# The beginning of an IDE for Nimrod # (c) 2008 Andreas Rumpf import os, glib2, gdk2, gtk2, libglade2, pango, dialogs, parseopt const GuiTemplate = "nimide.glade" dummyConst = "" type TTab {.pure, final.} = object textview: PGtkTextView filename: string untitled: bool hbox: PGtkHBox e: PEditor PTab = ptr TTab TEditor = object of TObject window: PGtkWindow statusbar: PGtkStatusBar menu: PGtkMenuBar notebook: PGtkNotebook tabname: int # used for generating tab names tabs: seq[PTab] currTab: int PEditor = ptr TEditor proc onWindowDestroy(obj: PGtkObject, event: PGdkEvent, data: pointer): gboolean {.cdecl.} = gtkMainQuit() proc onAboutMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} = gtkShowAboutDialog(e.window, "comments", "A fast and leight-weight IDE for Nimrod", "copyright", "Copyright \xc2\xa9 2009 Andreas Rumpf", "version", "0.1", "website", "http://nimrod.ethexor.com", "program-name", "Nimrod IDE", nil) proc getTabIndex(e: PEditor, tab: PTab): int = var i = 0 while true: var w = gtkNotebookGetNthPage(e.notebook, i) if w == nil: return -1 var v = gtkNotebookGetTabLabel(e.notebook, w) if tab.hbox == v: return i inc(i) proc getActiveTab(e: PEditor): PTab = nil type TAnswer = enum answerYes, answerNo, answerCancel proc askWhetherToSave(e: PEditor): TAnswer = var dialog = gtkDialogNewWithButtons("Should the changes be saved?", e.window, GTK_DIALOG_MODAL, GTK_STOCK_SAVE, 1, "gtk-discard", 2, GTK_STOCK_CANCEL, 3, nil) result = TAnswer(gtkDialogRun(dialog)+1) gtkWidgetDestroy(dialog) proc saveTab(tab: PTab) = if tab.untitled: tab.filename = ChooseFileToSave(tab.e.window, getRoot(tab)) tab.untitled = false XXX proc OnCloseTab(button: PGtkButton, tab: PTab) {.cdecl.} = var idx = -1 for i in 0..high(tab.e.tabs): if tab.e.tabs[i] == tab: idx = i break if idx >= 0: if gtkTextBufferGetModified(gtkTextViewGetBuffer(tab.textView)): case askWhetherToSave(tab.e) of answerCancel: return of answerYes: saveTab(tab) of answerNo: nil gtkNotebookRemovePage(tab.e.notebook, idx) if idx < high(tab.e.tabs): for i in idx..high(tab.e.tabs)-1: tab.e.tabs[i] = tab.e.tabs[i+1] else: dec currTab GC_unref(tab.filename) dealloc(tab) #var idx = getTabIndex(tab.e, tab) #if idx >= 0: gtk_notebook_remove_page(tab.e.notebook, idx) proc createTab(e: PEditor, filename: string, untitled: bool) = var t = cast[PTab](alloc0(sizeof(TTab))) t.textview = gtkTextViewNewWithBuffer(gtkTextBufferNew(nil)) var fontDesc = pangoFontDescriptionFromString("monospace 10") gtkWidgetModifyFont(t.textview, fontDesc) pangoFontDescriptionFree(fontDesc) t.filename = filename t.untitled = untitled gtkWidgetShow(t.textview) var scroll = gtkScrolledWindowNew(nil, nil) gtkContainerAdd(scroll, t.textview) gtkWidgetShow(scroll) t.e = e t.hbox = gtkHboxNew(false, 0) var image = gtkImageNewFromStock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU) var button = gtkButtonNew() var lab = gtkLabelNew(filename) gtkButtonSetImage(button, image) gtkButtonSetRelief(button, GTK_RELIEF_NONE) gtkBoxPackStart(t.hbox, lab, false, false, 2) gtkBoxPackEnd(t.hbox, button, false, false, 0) discard gSignalConnect(button, "clicked", G_Callback(onCloseTab), t) gtkWidgetShow(button) gtkWidgetShow(lab) var idx = gtkNotebookAppendPage(e.notebook, scroll, t.hbox) e.currTab = idx add(e.tabs, t) gtkNotebookSetCurrentPage(e.notebook, idx) proc onOpenMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} = var files = ChooseFilesToOpen(e.window, getRoot(getActiveTab(e))) for f in items(files): createTab(e, f, untitled=false) proc onSaveMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} = var cp = gtkNotebookGetCurrentPage(e.notebook) proc onSaveAsMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} = nil proc onNewMenuItemActivate(menuItem: PGtkMenuItem, e: PEditor) {.cdecl.} = inc(e.tabname) createTab(e, "untitled-" & $e.tabname, true) proc main(e: PEditor) = var builder = gladeXmlNew(getApplicationDir() / GuiTemplate, nil, nil) if builder == nil: quit("cannot open: " & GuiTemplate) # get the components: e.window = GTK_WINDOW(gladeXmlGetWidget(builder, "window")) e.statusbar = GTK_STATUSBAR(gladeXmlGetWidget(builder, "statusbar")) e.notebook = GTK_NOTEBOOK(gladeXmlGetWidget(builder, "notebook")) e.tabs = @[] e.currTab = -1 setHomogeneous(e.notebook^, 1) # connect the signal handlers: gladeXmlSignalConnect(builder, "on_window_destroy", GCallback(onWindowDestroy)) var about = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "about_menu_item")) discard gSignalConnect(about, "activate", G_CALLBACK(onAboutMenuItemActivate), e) var newItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "new_menu_item")) discard gSignalConnect(newItem, "activate", G_CALLBACK(onNewMenuItemActivate), e) var quitItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "quit_menu_item")) discard gSignalConnect(quitItem, "activate", G_CALLBACK(onWindowDestroy), e) var openItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "open_menu_item")) discard gSignalConnect(openItem, "activate", G_CALLBACK(onOpenMenuItemActivate), e) var saveItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "save_menu_item")) discard gSignalConnect(saveItem, "activate", G_CALLBACK(onSaveMenuItemActivate), e) var saveAsItem = GTK_MENU_ITEM(gladeXmlGetWidget(builder, "save_as_menu_item")) discard gSignalConnect(saveAsItem, "activate", G_CALLBACK(onSaveAsMenuItemActivate), e) gtkWindowSetDefaultIconName(GTK_STOCK_EDIT) gtkWidgetShow(e.window) gtkMain() gtkNimrodInit() var e = cast[PEditor](alloc0(sizeof(TEditor))) main(e)