1import glib2, gtk2, gdk2, gtksourceview, dialogs, os, pango, osproc, strutils 2import pegs, streams 3import settings, types, cfg, search 4 5{.push callConv:cdecl.} 6 7const 8 NimrodProjectExt = ".nimprj" 9 10var win: types.MainWin 11win.Tabs = @[] 12 13search.win = addr(win) 14 15var lastSession: seq[string] = @[] 16 17var confParseFail = False # This gets set to true 18 # When there is an error parsing the config 19 20# Load the settings 21try: 22 win.settings = cfg.load(lastSession) 23except ECFGParse: 24 # TODO: Make the dialog show the exception 25 confParseFail = True 26 win.settings = cfg.defaultSettings() 27except EIO: 28 win.settings = cfg.defaultSettings() 29 30proc getProjectTab(): int = 31 for i in 0..high(win.tabs): 32 if win.tabs[i].filename.endswith(NimrodProjectExt): return i 33 34proc saveTab(tabNr: int, startpath: string) = 35 if tabNr < 0: return 36 if win.Tabs[tabNr].saved: return 37 var path = "" 38 if win.Tabs[tabNr].filename == "": 39 path = ChooseFileToSave(win.w, startpath) 40 # dialogs.nim STOCK_OPEN instead of STOCK_SAVE 41 else: 42 path = win.Tabs[tabNr].filename 43 44 if path != "": 45 var buffer = PTextBuffer(win.Tabs[tabNr].buffer) 46 # Get the text from the TextView 47 var startIter: TTextIter 48 buffer.getStartIter(addr(startIter)) 49 50 var endIter: TTextIter 51 buffer.getEndIter(addr(endIter)) 52 53 var text = buffer.getText(addr(startIter), addr(endIter), False) 54 # Save it to a file 55 var f: TFile 56 if open(f, path, fmWrite): 57 f.write(text) 58 f.close() 59 60 win.tempStuff.lastSaveDir = splitFile(path).dir 61 62 # Change the tab name and .Tabs.filename etc. 63 win.Tabs[tabNr].filename = path 64 win.Tabs[tabNr].saved = True 65 var name = extractFilename(path) 66 67 var cTab = win.Tabs[tabNr] 68 cTab.label.setText(name) 69 else: 70 error(win.w, "Unable to write to file") 71 72proc saveAllTabs() = 73 for i in 0..high(win.tabs): 74 saveTab(i, os.splitFile(win.tabs[i].filename).dir) 75 76# GTK Events 77# -- w(PWindow) 78proc destroy(widget: PWidget, data: pgpointer) {.cdecl.} = 79 # gather some settings 80 win.settings.VPanedPos = PPaned(win.sourceViewTabs.getParent()).getPosition() 81 win.settings.winWidth = win.w.allocation.width 82 win.settings.winHeight = win.w.allocation.height 83 84 # save the settings 85 win.save() 86 # then quit 87 main_quit() 88 89proc delete_event(widget: PWidget, event: PEvent, user_data: pgpointer): bool = 90 var quit = True 91 for i in low(win.Tabs)..len(win.Tabs)-1: 92 if not win.Tabs[i].saved: 93 var askSave = dialogNewWithButtons("", win.w, 0, 94 STOCK_SAVE, RESPONSE_ACCEPT, STOCK_CANCEL, 95 RESPONSE_CANCEL, 96 "Close without saving", RESPONSE_REJECT, nil) 97 askSave.setTransientFor(win.w) 98 # TODO: Make this dialog look better 99 var label = labelNew(win.Tabs[i].filename & 100 " is unsaved, would you like to save it ?") 101 PBox(askSave.vbox).pack_start(label, False, False, 0) 102 label.show() 103 104 var resp = askSave.run() 105 gtk2.destroy(PWidget(askSave)) 106 case resp 107 of RESPONSE_ACCEPT: 108 saveTab(i, os.splitFile(win.tabs[i].filename).dir) 109 quit = True 110 of RESPONSE_CANCEL: 111 quit = False 112 break 113 of RESPONSE_REJECT: 114 quit = True 115 else: 116 quit = False 117 break 118 119 # If False is returned the window will close 120 return not quit 121 122proc windowState_Changed(widget: PWidget, event: PEventWindowState, 123 user_data: pgpointer) = 124 win.settings.winMaximized = (event.newWindowState and 125 WINDOW_STATE_MAXIMIZED) != 0 126 127# -- SourceView(PSourceView) & SourceBuffer 128proc updateStatusBar(buffer: PTextBuffer){.cdecl.} = 129 # Incase this event gets fired before 130 # bottomBar is initialized 131 if win.bottomBar != nil and not win.tempStuff.stopSBUpdates: 132 var iter: TTextIter 133 134 win.bottomBar.pop(0) 135 buffer.getIterAtMark(addr(iter), buffer.getInsert()) 136 var row = getLine(addr(iter)) + 1 137 var col = getLineOffset(addr(iter)) 138 discard win.bottomBar.push(0, "Line: " & $row & " Column: " & $col) 139 140proc cursorMoved(buffer: PTextBuffer, location: PTextIter, 141 mark: PTextMark, user_data: pgpointer){.cdecl.} = 142 updateStatusBar(buffer) 143 144proc onCloseTab(btn: PButton, user_data: PWidget) = 145 if win.sourceViewTabs.getNPages() > 1: 146 var tab = win.sourceViewTabs.pageNum(user_data) 147 win.sourceViewTabs.removePage(tab) 148 149 win.Tabs.delete(tab) 150 151proc onSwitchTab(notebook: PNotebook, page: PNotebookPage, pageNum: guint, 152 user_data: pgpointer) = 153 if win.Tabs.len()-1 >= pageNum: 154 win.w.setTitle("Aporia IDE - " & win.Tabs[pageNum].filename) 155 156proc createTabLabel(name: string, t_child: PWidget): tuple[box: PWidget, 157 label: PLabel] = 158 var box = hboxNew(False, 0) 159 var label = labelNew(name) 160 var closebtn = buttonNew() 161 closeBtn.setLabel(nil) 162 var iconSize = iconSizeFromName("tabIconSize") 163 if iconSize == 0: 164 iconSize = iconSizeRegister("tabIconSize", 10, 10) 165 var image = imageNewFromStock(STOCK_CLOSE, iconSize) 166 discard gSignalConnect(closebtn, "clicked", G_Callback(onCloseTab), t_child) 167 closebtn.setImage(image) 168 gtk2.setRelief(closebtn, RELIEF_NONE) 169 box.packStart(label, True, True, 0) 170 box.packEnd(closebtn, False, False, 0) 171 box.showAll() 172 return (box, label) 173 174proc changed(buffer: PTextBuffer, user_data: pgpointer) = 175 # Update the 'Line & Column' 176 #updateStatusBar(buffer) 177 178 # Change the tabs state to 'unsaved' 179 # and add '*' to the Tab Name 180 var current = win.SourceViewTabs.getCurrentPage() 181 var name = "" 182 if win.Tabs[current].filename == "": 183 win.Tabs[current].saved = False 184 name = "Untitled *" 185 else: 186 win.Tabs[current].saved = False 187 name = extractFilename(win.Tabs[current].filename) & " *" 188 189 var cTab = win.Tabs[current] 190 cTab.label.setText(name) 191 192# Other(Helper) functions 193 194proc initSourceView(SourceView: var PWidget, scrollWindow: var PScrolledWindow, 195 buffer: var PSourceBuffer) = 196 # This gets called by addTab 197 # Each tabs creates a new SourceView 198 # SourceScrolledWindow(ScrolledWindow) 199 scrollWindow = scrolledWindowNew(nil, nil) 200 scrollWindow.setPolicy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) 201 scrollWindow.show() 202 203 # SourceView(gtkSourceView) 204 SourceView = sourceViewNew(buffer) 205 PSourceView(SourceView).setInsertSpacesInsteadOfTabs(True) 206 PSourceView(SourceView).setIndentWidth(win.settings.indentWidth) 207 PSourceView(SourceView).setShowLineNumbers(win.settings.showLineNumbers) 208 PSourceView(SourceView).setHighlightCurrentLine( 209 win.settings.highlightCurrentLine) 210 PSourceView(SourceView).setShowRightMargin(win.settings.rightMargin) 211 PSourceView(SourceView).setAutoIndent(win.settings.autoIndent) 212 213 var font = font_description_from_string(win.settings.font) 214 SourceView.modifyFont(font) 215 216 scrollWindow.add(SourceView) 217 SourceView.show() 218 219 buffer.setHighlightMatchingBrackets( 220 win.settings.highlightMatchingBrackets) 221 222 # UGLY workaround for yet another compiler bug: 223 discard gsignalConnect(buffer, "mark-set", 224 GCallback(aporia.cursorMoved), nil) 225 discard gsignalConnect(buffer, "changed", GCallback(aporia.changed), nil) 226 227 # -- Set the syntax highlighter scheme 228 buffer.setScheme(win.scheme) 229 230proc addTab(name, filename: string) = 231 ## Adds a tab, if filename is not "" reads the file. And sets 232 ## the tabs SourceViews text to that files contents. 233 assert(win.nimLang != nil) 234 var buffer: PSourceBuffer = sourceBufferNew(win.nimLang) 235 236 if filename != nil and filename != "": 237 var lang = win.langMan.guessLanguage(filename, nil) 238 if lang != nil: 239 buffer.setLanguage(lang) 240 else: 241 buffer.setHighlightSyntax(False) 242 243 var nam = name 244 if nam == "": nam = "Untitled" 245 if filename == "": nam.add(" *") 246 elif filename != "" and name == "": 247 # Disable the undo/redo manager. 248 buffer.begin_not_undoable_action() 249 250 # Load the file. 251 var file: string = readFile(filename) 252 if file != nil: 253 buffer.set_text(file, len(file)) 254 255 # Enable the undo/redo manager. 256 buffer.end_not_undoable_action() 257 258 # Get the name.ext of the filename, for the tabs title 259 nam = extractFilename(filename) 260 261 # Init the sourceview 262 var sourceView: PWidget 263 var scrollWindow: PScrolledWindow 264 initSourceView(sourceView, scrollWindow, buffer) 265 266 var (TabLabel, labelText) = createTabLabel(nam, scrollWindow) 267 # Add a tab 268 discard win.SourceViewTabs.appendPage(scrollWindow, TabLabel) 269 270 var nTab: Tab 271 nTab.buffer = buffer 272 nTab.sourceView = sourceView 273 nTab.label = labelText 274 nTab.saved = (filename != "") 275 nTab.filename = filename 276 win.Tabs.add(nTab) 277 278 PTextView(SourceView).setBuffer(nTab.buffer) 279 280# GTK Events Contd. 281# -- TopMenu & TopBar 282 283proc newFile(menuItem: PMenuItem, user_data: pgpointer) = 284 addTab("", "") 285 win.sourceViewTabs.setCurrentPage(win.Tabs.len()-1) 286 287proc openFile(menuItem: PMenuItem, user_data: pgpointer) = 288 var startpath = "" 289 var currPage = win.SourceViewTabs.getCurrentPage() 290 if currPage <% win.tabs.len: 291 startpath = os.splitFile(win.tabs[currPage].filename).dir 292 293 if startpath.len == 0: 294 # Use lastSavePath as the startpath 295 startpath = win.tempStuff.lastSaveDir 296 if startpath.len == 0: 297 startpath = os.getHomeDir() 298 299 var files = ChooseFilesToOpen(win.w, startpath) 300 if files.len() > 0: 301 for f in items(files): 302 try: 303 addTab("", f) 304 except EIO: 305 error(win.w, "Unable to read from file") 306 # Switch to the newly created tab 307 win.sourceViewTabs.setCurrentPage(win.Tabs.len()-1) 308 309proc saveFile_Activate(menuItem: PMenuItem, user_data: pgpointer) = 310 var current = win.SourceViewTabs.getCurrentPage() 311 saveTab(current, os.splitFile(win.tabs[current].filename).dir) 312 313proc saveFileAs_Activate(menuItem: PMenuItem, user_data: pgpointer) = 314 var current = win.SourceViewTabs.getCurrentPage() 315 var (filename, saved) = (win.Tabs[current].filename, win.Tabs[current].saved) 316 317 win.Tabs[current].saved = False 318 win.Tabs[current].filename = "" 319 saveTab(current, os.splitFile(filename).dir) 320 # If the user cancels the save file dialog. Restore the previous filename 321 # and saved state 322 if win.Tabs[current].filename == "": 323 win.Tabs[current].filename = filename 324 win.Tabs[current].saved = saved 325 326proc undo(menuItem: PMenuItem, user_data: pgpointer) = 327 var current = win.SourceViewTabs.getCurrentPage() 328 if win.Tabs[current].buffer.canUndo(): 329 win.Tabs[current].buffer.undo() 330 331proc redo(menuItem: PMenuItem, user_data: pgpointer) = 332 var current = win.SourceViewTabs.getCurrentPage() 333 if win.Tabs[current].buffer.canRedo(): 334 win.Tabs[current].buffer.redo() 335 336proc find_Activate(menuItem: PMenuItem, user_data: pgpointer) = 337 # Get the selected text, and set the findEntry to it. 338 var currentTab = win.SourceViewTabs.getCurrentPage() 339 var insertIter: TTextIter 340 win.Tabs[currentTab].buffer.getIterAtMark(addr(insertIter), 341 win.Tabs[currentTab].buffer.getInsert()) 342 var insertOffset = addr(insertIter).getOffset() 343 344 var selectIter: TTextIter 345 win.Tabs[currentTab].buffer.getIterAtMark(addr(selectIter), 346 win.Tabs[currentTab].buffer.getSelectionBound()) 347 var selectOffset = addr(selectIter).getOffset() 348 349 if insertOffset != selectOffset: 350 var text = win.Tabs[currentTab].buffer.getText(addr(insertIter), 351 addr(selectIter), false) 352 win.findEntry.setText(text) 353 354 win.findBar.show() 355 win.findEntry.grabFocus() 356 win.replaceEntry.hide() 357 win.replaceLabel.hide() 358 win.replaceBtn.hide() 359 win.replaceAllBtn.hide() 360 361proc replace_Activate(menuitem: PMenuItem, user_data: pgpointer) = 362 win.findBar.show() 363 win.findEntry.grabFocus() 364 win.replaceEntry.show() 365 win.replaceLabel.show() 366 win.replaceBtn.show() 367 win.replaceAllBtn.show() 368 369proc settings_Activate(menuitem: PMenuItem, user_data: pgpointer) = 370 settings.showSettings(win) 371 372proc viewBottomPanel_Toggled(menuitem: PCheckMenuItem, user_data: pgpointer) = 373 win.settings.bottomPanelVisible = menuitem.itemGetActive() 374 if win.settings.bottomPanelVisible: 375 win.bottomPanelTabs.show() 376 else: 377 win.bottomPanelTabs.hide() 378 379var 380 pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') Error:' \s* {.*}" 381 pegLineWarning = peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Warning:'/'Hint:') \s* {.*}" 382 pegOtherError = peg"'Error:' \s* {.*}" 383 pegSuccess = peg"'Hint: operation successful'.*" 384 385proc addText(textView: PTextView, text: string, colorTag: PTextTag = nil) = 386 if text != nil: 387 var iter: TTextIter 388 textView.getBuffer().getEndIter(addr(iter)) 389 390 if colorTag == nil: 391 textView.getBuffer().insert(addr(iter), text, len(text)) 392 else: 393 textView.getBuffer().insertWithTags(addr(iter), text, len(text), colorTag, 394 nil) 395 396proc createColor(textView: PTextView, name, color: string): PTextTag = 397 var tagTable = textView.getBuffer().getTagTable() 398 result = tagTable.tableLookup(name) 399 if result == nil: 400 result = textView.getBuffer().createTag(name, "foreground", color, nil) 401 402when not defined(os.findExe): 403 proc findExe(exe: string): string = 404 ## returns "" if the exe cannot be found 405 result = addFileExt(exe, os.exeExt) 406 if ExistsFile(result): return 407 var path = os.getEnv("PATH") 408 for candidate in split(path, pathSep): 409 var x = candidate / result 410 if ExistsFile(x): return x 411 result = "" 412 413proc GetCmd(cmd, filename: string): string = 414 var f = quoteIfContainsWhite(filename) 415 if cmd =~ peg"\s* '$' y'findExe' '(' {[^)]+} ')' {.*}": 416 var exe = quoteIfContainsWhite(findExe(matches[0])) 417 if exe.len == 0: exe = matches[0] 418 result = exe & " " & matches[1] % f 419 else: 420 result = cmd % f 421 422proc showBottomPanel() = 423 if not win.settings.bottomPanelVisible: 424 win.bottomPanelTabs.show() 425 win.settings.bottomPanelVisible = true 426 PCheckMenuItem(win.viewBottomPanelMenuItem).itemSetActive(true) 427 # Scroll to the end of the TextView 428 # This is stupid, it works sometimes... it's random 429 var endIter: TTextIter 430 win.outputTextView.getBuffer().getEndIter(addr(endIter)) 431 discard win.outputTextView.scrollToIter( 432 addr(endIter), 0.25, False, 0.0, 0.0) 433 434proc compileRun(currentTab: int, shouldRun: bool) = 435 if win.Tabs[currentTab].filename.len == 0: return 436 # Clear the outputTextView 437 win.outputTextView.getBuffer().setText("", 0) 438 439 var outp = osProc.execProcess(GetCmd(win.settings.nimrodCmd, 440 win.Tabs[currentTab].filename)) 441 # Colors 442 var normalTag = createColor(win.outputTextView, "normalTag", "#3d3d3d") 443 var errorTag = createColor(win.outputTextView, "errorTag", "red") 444 var warningTag = createColor(win.outputTextView, "warningTag", "darkorange") 445 var successTag = createColor(win.outputTextView, "successTag", "darkgreen") 446 for x in outp.splitLines(): 447 if x =~ pegLineError / pegOtherError: 448 win.outputTextView.addText("\n" & x, errorTag) 449 elif x=~ pegSuccess: 450 win.outputTextView.addText("\n" & x, successTag) 451 452 # Launch the process 453 if shouldRun: 454 var filename = changeFileExt(win.Tabs[currentTab].filename, os.ExeExt) 455 var output = "\n" & osProc.execProcess(filename) 456 win.outputTextView.addText(output) 457 elif x =~ pegLineWarning: 458 win.outputTextView.addText("\n" & x, warningTag) 459 else: 460 win.outputTextView.addText("\n" & x, normalTag) 461 showBottomPanel() 462 463proc CompileCurrent_Activate(menuitem: PMenuItem, user_data: pgpointer) = 464 saveFile_Activate(nil, nil) 465 compileRun(win.SourceViewTabs.getCurrentPage(), false) 466 467proc CompileRunCurrent_Activate(menuitem: PMenuItem, user_data: pgpointer) = 468 saveFile_Activate(nil, nil) 469 compileRun(win.SourceViewTabs.getCurrentPage(), true) 470 471proc CompileProject_Activate(menuitem: PMenuItem, user_data: pgpointer) = 472 saveAllTabs() 473 compileRun(getProjectTab(), false) 474 475proc CompileRunProject_Activate(menuitem: PMenuItem, user_data: pgpointer) = 476 saveAllTabs() 477 compileRun(getProjectTab(), true) 478 479proc RunCustomCommand(cmd: string) = 480 saveFile_Activate(nil, nil) 481 var currentTab = win.SourceViewTabs.getCurrentPage() 482 if win.Tabs[currentTab].filename.len == 0 or cmd.len == 0: return 483 # Clear the outputTextView 484 win.outputTextView.getBuffer().setText("", 0) 485 var outp = osProc.execProcess(GetCmd(cmd, win.Tabs[currentTab].filename)) 486 var normalTag = createColor(win.outputTextView, "normalTag", "#3d3d3d") 487 for x in outp.splitLines(): 488 win.outputTextView.addText("\n" & x, normalTag) 489 showBottomPanel() 490 491proc RunCustomCommand1(menuitem: PMenuItem, user_data: pgpointer) = 492 RunCustomCommand(win.settings.customCmd1) 493 494proc RunCustomCommand2(menuitem: PMenuItem, user_data: pgpointer) = 495 RunCustomCommand(win.settings.customCmd2) 496 497proc RunCustomCommand3(menuitem: PMenuItem, user_data: pgpointer) = 498 RunCustomCommand(win.settings.customCmd3) 499 500# -- FindBar 501 502proc nextBtn_Clicked(button: PButton, user_data: pgpointer) = findText(True) 503proc prevBtn_Clicked(button: PButton, user_data: pgpointer) = findText(False) 504 505proc replaceBtn_Clicked(button: PButton, user_data: pgpointer) = 506 var currentTab = win.SourceViewTabs.getCurrentPage() 507 var start, theEnd: TTextIter 508 if not win.Tabs[currentTab].buffer.getSelectionBounds( 509 addr(start), addr(theEnd)): 510 # If no text is selected, try finding a match. 511 findText(True) 512 if not win.Tabs[currentTab].buffer.getSelectionBounds( 513 addr(start), addr(theEnd)): 514 # No match 515 return 516 517 # Remove the text 518 win.Tabs[currentTab].buffer.delete(addr(start), addr(theEnd)) 519 # Insert the replacement 520 var text = getText(win.replaceEntry) 521 win.Tabs[currentTab].buffer.insert(addr(start), text, len(text)) 522 523proc replaceAllBtn_Clicked(button: PButton, user_data: pgpointer) = 524 var find = getText(win.findEntry) 525 var replace = getText(win.replaceEntry) 526 discard replaceAll(find, replace) 527 528proc closeBtn_Clicked(button: PButton, user_data: pgpointer) = 529 win.findBar.hide() 530 531proc caseSens_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) = 532 win.settings.search = "casesens" 533proc caseInSens_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) = 534 win.settings.search = "caseinsens" 535proc style_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) = 536 win.settings.search = "style" 537proc regex_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) = 538 win.settings.search = "regex" 539proc peg_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) = 540 win.settings.search = "peg" 541 542proc extraBtn_Clicked(button: PButton, user_data: pgpointer) = 543 var extraMenu = menuNew() 544 var group: PGSList 545 546 var caseSensMenuItem = radio_menu_item_new(group, "Case sensitive") 547 extraMenu.append(caseSensMenuItem) 548 discard signal_connect(caseSensMenuItem, "toggled", 549 SIGNAL_FUNC(caseSens_Changed), nil) 550 caseSensMenuItem.show() 551 group = caseSensMenuItem.ItemGetGroup() 552 553 var caseInSensMenuItem = radio_menu_item_new(group, "Case insensitive") 554 extraMenu.append(caseInSensMenuItem) 555 discard signal_connect(caseInSensMenuItem, "toggled", 556 SIGNAL_FUNC(caseInSens_Changed), nil) 557 caseInSensMenuItem.show() 558 group = caseInSensMenuItem.ItemGetGroup() 559 560 var styleMenuItem = radio_menu_item_new(group, "Style insensitive") 561 extraMenu.append(styleMenuItem) 562 discard signal_connect(styleMenuItem, "toggled", 563 SIGNAL_FUNC(style_Changed), nil) 564 styleMenuItem.show() 565 group = styleMenuItem.ItemGetGroup() 566 567 var regexMenuItem = radio_menu_item_new(group, "Regex") 568 extraMenu.append(regexMenuItem) 569 discard signal_connect(regexMenuItem, "toggled", 570 SIGNAL_FUNC(regex_Changed), nil) 571 regexMenuItem.show() 572 group = regexMenuItem.ItemGetGroup() 573 574 var pegMenuItem = radio_menu_item_new(group, "Pegs") 575 extraMenu.append(pegMenuItem) 576 discard signal_connect(pegMenuItem, "toggled", 577 SIGNAL_FUNC(peg_Changed), nil) 578 pegMenuItem.show() 579 580 # Make the correct radio button active 581 case win.settings.search 582 of "casesens": 583 PCheckMenuItem(caseSensMenuItem).ItemSetActive(True) 584 of "caseinsens": 585 PCheckMenuItem(caseInSensMenuItem).ItemSetActive(True) 586 of "style": 587 PCheckMenuItem(styleMenuItem).ItemSetActive(True) 588 of "regex": 589 PCheckMenuItem(regexMenuItem).ItemSetActive(True) 590 of "peg": 591 PCheckMenuItem(pegMenuItem).ItemSetActive(True) 592 593 extraMenu.popup(nil, nil, nil, nil, 0, get_current_event_time()) 594 595# GUI Initialization 596 597proc createAccelMenuItem(toolsMenu: PMenu, accGroup: PAccelGroup, 598 label: string, acc: gint, 599 action: proc (i: PMenuItem, p: pgpointer)) = 600 var result = menu_item_new(label) 601 result.addAccelerator("activate", accGroup, acc, 0, ACCEL_VISIBLE) 602 ToolsMenu.append(result) 603 show(result) 604 discard signal_connect(result, "activate", SIGNAL_FUNC(action), nil) 605 606proc createSeparator(menu: PMenu) = 607 var sep = separator_menu_item_new() 608 menu.append(sep) 609 sep.show() 610 611proc initTopMenu(MainBox: PBox) = 612 # Create a accelerator group, used for shortcuts 613 # like CTRL + S in SaveMenuItem 614 var accGroup = accel_group_new() 615 add_accel_group(win.w, accGroup) 616 617 # TopMenu(MenuBar) 618 var TopMenu = menuBarNew() 619 620 # FileMenu 621 var FileMenu = menuNew() 622 623 var NewMenuItem = menu_item_new("New") # New 624 FileMenu.append(NewMenuItem) 625 show(NewMenuItem) 626 discard signal_connect(NewMenuItem, "activate", 627 SIGNAL_FUNC(newFile), nil) 628 629 createSeparator(FileMenu) 630 631 var OpenMenuItem = menu_item_new("Open...") # Open... 632 # CTRL + O 633 OpenMenuItem.add_accelerator("activate", accGroup, 634 KEY_o, CONTROL_MASK, ACCEL_VISIBLE) 635 FileMenu.append(OpenMenuItem) 636 show(OpenMenuItem) 637 discard signal_connect(OpenMenuItem, "activate", 638 SIGNAL_FUNC(aporia.openFile), nil) 639 640 var SaveMenuItem = menu_item_new("Save") # Save 641 # CTRL + S 642 SaveMenuItem.add_accelerator("activate", accGroup, 643 KEY_s, CONTROL_MASK, ACCEL_VISIBLE) 644 FileMenu.append(SaveMenuItem) 645 show(SaveMenuItem) 646 discard signal_connect(SaveMenuItem, "activate", 647 SIGNAL_FUNC(saveFile_activate), nil) 648 649 var SaveAsMenuItem = menu_item_new("Save As...") # Save as... 650 651 SaveAsMenuItem.add_accelerator("activate", accGroup, 652 KEY_s, CONTROL_MASK or gdk2.SHIFT_MASK, ACCEL_VISIBLE) 653 FileMenu.append(SaveAsMenuItem) 654 show(SaveAsMenuItem) 655 discard signal_connect(SaveAsMenuItem, "activate", 656 SIGNAL_FUNC(saveFileAs_Activate), nil) 657 658 var FileMenuItem = menuItemNewWithMnemonic("_File") 659 660 FileMenuItem.setSubMenu(FileMenu) 661 FileMenuItem.show() 662 TopMenu.append(FileMenuItem) 663 664 # Edit menu 665 var EditMenu = menuNew() 666 667 var UndoMenuItem = menu_item_new("Undo") # Undo 668 EditMenu.append(UndoMenuItem) 669 show(UndoMenuItem) 670 discard signal_connect(UndoMenuItem, "activate", 671 SIGNAL_FUNC(aporia.undo), nil) 672 673 var RedoMenuItem = menu_item_new("Redo") # Undo 674 EditMenu.append(RedoMenuItem) 675 show(RedoMenuItem) 676 discard signal_connect(RedoMenuItem, "activate", 677 SIGNAL_FUNC(aporia.redo), nil) 678 679 createSeparator(EditMenu) 680 681 var FindMenuItem = menu_item_new("Find") # Find 682 FindMenuItem.add_accelerator("activate", accGroup, 683 KEY_f, CONTROL_MASK, ACCEL_VISIBLE) 684 EditMenu.append(FindMenuItem) 685 show(FindMenuItem) 686 discard signal_connect(FindMenuItem, "activate", 687 SIGNAL_FUNC(aporia.find_Activate), nil) 688 689 var ReplaceMenuItem = menu_item_new("Replace") # Replace 690 ReplaceMenuItem.add_accelerator("activate", accGroup, 691 KEY_h, CONTROL_MASK, ACCEL_VISIBLE) 692 EditMenu.append(ReplaceMenuItem) 693 show(ReplaceMenuItem) 694 discard signal_connect(ReplaceMenuItem, "activate", 695 SIGNAL_FUNC(aporia.replace_Activate), nil) 696 697 createSeparator(EditMenu) 698 699 var SettingsMenuItem = menu_item_new("Settings...") # Settings 700 EditMenu.append(SettingsMenuItem) 701 show(SettingsMenuItem) 702 discard signal_connect(SettingsMenuItem, "activate", 703 SIGNAL_FUNC(aporia.Settings_Activate), nil) 704 705 var EditMenuItem = menuItemNewWithMnemonic("_Edit") 706 707 EditMenuItem.setSubMenu(EditMenu) 708 EditMenuItem.show() 709 TopMenu.append(EditMenuItem) 710 711 # View menu 712 var ViewMenu = menuNew() 713 714 win.viewBottomPanelMenuItem = check_menu_item_new("Bottom Panel") 715 PCheckMenuItem(win.viewBottomPanelMenuItem).itemSetActive( 716 win.settings.bottomPanelVisible) 717 win.viewBottomPanelMenuItem.add_accelerator("activate", accGroup, 718 KEY_f9, CONTROL_MASK, ACCEL_VISIBLE) 719 ViewMenu.append(win.viewBottomPanelMenuItem) 720 show(win.viewBottomPanelMenuItem) 721 discard signal_connect(win.viewBottomPanelMenuItem, "toggled", 722 SIGNAL_FUNC(aporia.viewBottomPanel_Toggled), nil) 723 724 var ViewMenuItem = menuItemNewWithMnemonic("_View") 725 726 ViewMenuItem.setSubMenu(ViewMenu) 727 ViewMenuItem.show() 728 TopMenu.append(ViewMenuItem) 729 730 731 # Tools menu 732 var ToolsMenu = menuNew() 733 734 createAccelMenuItem(ToolsMenu, accGroup, "Compile current file", 735 KEY_F4, aporia.CompileCurrent_Activate) 736 createAccelMenuItem(ToolsMenu, accGroup, "Compile & run current file", 737 KEY_F5, aporia.CompileRunCurrent_Activate) 738 createSeparator(ToolsMenu) 739 createAccelMenuItem(ToolsMenu, accGroup, "Compile project", 740 KEY_F8, aporia.CompileProject_Activate) 741 createAccelMenuItem(ToolsMenu, accGroup, "Compile & run project", 742 KEY_F9, aporia.CompileRunProject_Activate) 743 createSeparator(ToolsMenu) 744 createAccelMenuItem(ToolsMenu, accGroup, "Run custom command 1", 745 KEY_F1, aporia.RunCustomCommand1) 746 createAccelMenuItem(ToolsMenu, accGroup, "Run custom command 2", 747 KEY_F2, aporia.RunCustomCommand2) 748 createAccelMenuItem(ToolsMenu, accGroup, "Run custom command 3", 749 KEY_F3, aporia.RunCustomCommand3) 750 751 var ToolsMenuItem = menuItemNewWithMnemonic("_Tools") 752 753 ToolsMenuItem.setSubMenu(ToolsMenu) 754 ToolsMenuItem.show() 755 TopMenu.append(ToolsMenuItem) 756 757 # Help menu 758 MainBox.packStart(TopMenu, False, False, 0) 759 TopMenu.show() 760 761proc initToolBar(MainBox: PBox) = 762 # TopBar(ToolBar) 763 var TopBar = toolbarNew() 764 TopBar.setStyle(TOOLBAR_ICONS) 765 766 var NewFileItem = TopBar.insertStock(STOCK_NEW, "New File", 767 "New File", SIGNAL_FUNC(aporia.newFile), nil, 0) 768 TopBar.appendSpace() 769 var OpenItem = TopBar.insertStock(STOCK_OPEN, "Open", 770 "Open", SIGNAL_FUNC(aporia.openFile), nil, -1) 771 var SaveItem = TopBar.insertStock(STOCK_SAVE, "Save", 772 "Save", SIGNAL_FUNC(saveFile_Activate), nil, -1) 773 TopBar.appendSpace() 774 var UndoItem = TopBar.insertStock(STOCK_UNDO, "Undo", 775 "Undo", SIGNAL_FUNC(aporia.undo), nil, -1) 776 var RedoItem = TopBar.insertStock(STOCK_REDO, "Redo", 777 "Redo", SIGNAL_FUNC(aporia.redo), nil, -1) 778 779 MainBox.packStart(TopBar, False, False, 0) 780 TopBar.show() 781 782proc initSourceViewTabs() = 783 win.SourceViewTabs = notebookNew() 784 #win.sourceViewTabs.dragDestSet(DEST_DEFAULT_DROP, nil, 0, ACTION_MOVE) 785 discard win.SourceViewTabs.signalConnect( 786 "switch-page", SIGNAL_FUNC(onSwitchTab), nil) 787 #discard win.SourceViewTabs.signalConnect( 788 # "drag-drop", SIGNAL_FUNC(svTabs_DragDrop), nil) 789 #discard win.SourceViewTabs.signalConnect( 790 # "drag-data-received", SIGNAL_FUNC(svTabs_DragDataRecv), nil) 791 #discard win.SourceViewTabs.signalConnect( 792 # "drag-motion", SIGNAL_FUNC(svTabs_DragMotion), nil) 793 win.SourceViewTabs.set_scrollable(True) 794 795 win.SourceViewTabs.show() 796 if lastSession.len != 0: 797 for i in 0 .. len(lastSession)-1: 798 var splitUp = lastSession[i].split('|') 799 var (filename, offset) = (splitUp[0], splitUp[1]) 800 addTab("", filename) 801 802 var iter: TTextIter 803 win.Tabs[i].buffer.getIterAtOffset(addr(iter), offset.parseInt()) 804 win.Tabs[i].buffer.moveMarkByName("insert", addr(iter)) 805 win.Tabs[i].buffer.moveMarkByName("selection_bound", addr(iter)) 806 807 # TODO: Fix this..... :( 808 discard PTextView(win.Tabs[i].sourceView). 809 scrollToIter(addr(iter), 0.25, true, 0.0, 0.0) 810 else: 811 addTab("", "") 812 813 # This doesn't work :\ 814 win.Tabs[0].sourceView.grabFocus() 815 816 817proc initBottomTabs() = 818 win.bottomPanelTabs = notebookNew() 819 if win.settings.bottomPanelVisible: 820 win.bottomPanelTabs.show() 821 822 # output tab 823 var tabLabel = labelNew("Output") 824 var outputTab = vboxNew(False, 0) 825 discard win.bottomPanelTabs.appendPage(outputTab, tabLabel) 826 # Compiler tabs, gtktextview 827 var outputScrolledWindow = scrolledwindowNew(nil, nil) 828 outputScrolledWindow.setPolicy(POLICY_AUTOMATIC, POLICY_AUTOMATIC) 829 outputTab.packStart(outputScrolledWindow, true, true, 0) 830 outputScrolledWindow.show() 831 832 win.outputTextView = textviewNew() 833 outputScrolledWindow.add(win.outputTextView) 834 win.outputTextView.show() 835 836 outputTab.show() 837 838proc initTAndBP(MainBox: PBox) = 839 # This init's the HPaned, which splits the sourceViewTabs 840 # and the BottomPanelTabs 841 initSourceViewTabs() 842 initBottomTabs() 843 844 var TAndBPVPaned = vpanedNew() 845 tandbpVPaned.pack1(win.sourceViewTabs, resize=True, shrink=False) 846 tandbpVPaned.pack2(win.bottomPanelTabs, resize=False, shrink=False) 847 MainBox.packStart(TAndBPVPaned, True, True, 0) 848 tandbpVPaned.setPosition(win.settings.VPanedPos) 849 TAndBPVPaned.show() 850 851proc initFindBar(MainBox: PBox) = 852 # Create a fixed container 853 win.findBar = HBoxNew(False, 0) 854 win.findBar.setSpacing(4) 855 856 # Add a Label 'Find' 857 var findLabel = labelNew("Find:") 858 win.findBar.packStart(findLabel, False, False, 0) 859 findLabel.show() 860 861 # Add a (find) text entry 862 win.findEntry = entryNew() 863 win.findBar.packStart(win.findEntry, False, False, 0) 864 discard win.findEntry.signalConnect("activate", SIGNAL_FUNC( 865 aporia.nextBtn_Clicked), nil) 866 win.findEntry.show() 867 var rq: TRequisition 868 win.findEntry.sizeRequest(addr(rq)) 869 870 # Make the (find) text entry longer 871 win.findEntry.set_size_request(190, rq.height) 872 873 # Add a Label 'Replace' 874 # - This Is only shown, when the 'Search & Replace'(CTRL + H) is shown 875 win.replaceLabel = labelNew("Replace:") 876 win.findBar.packStart(win.replaceLabel, False, False, 0) 877 #replaceLabel.show() 878 879 # Add a (replace) text entry 880 # - This Is only shown, when the 'Search & Replace'(CTRL + H) is shown 881 win.replaceEntry = entryNew() 882 win.findBar.packStart(win.replaceEntry, False, False, 0) 883 #win.replaceEntry.show() 884 var rq1: TRequisition 885 win.replaceEntry.sizeRequest(addr(rq1)) 886 887 # Make the (replace) text entry longer 888 win.replaceEntry.set_size_request(100, rq1.height) 889 890 # Find next button 891 var nextBtn = buttonNew("Next") 892 win.findBar.packStart(nextBtn, false, false, 0) 893 discard nextBtn.signalConnect("clicked", 894 SIGNAL_FUNC(aporia.nextBtn_Clicked), nil) 895 nextBtn.show() 896 var nxtBtnRq: TRequisition 897 nextBtn.sizeRequest(addr(nxtBtnRq)) 898 899 # Find previous button 900 var prevBtn = buttonNew("Previous") 901 win.findBar.packStart(prevBtn, false, false, 0) 902 discard prevBtn.signalConnect("clicked", 903 SIGNAL_FUNC(aporia.prevBtn_Clicked), nil) 904 prevBtn.show() 905 906 # Replace button 907 # - This Is only shown, when the 'Search & Replace'(CTRL + H) is shown 908 win.replaceBtn = buttonNew("Replace") 909 win.findBar.packStart(win.replaceBtn, false, false, 0) 910 discard win.replaceBtn.signalConnect("clicked", 911 SIGNAL_FUNC(aporia.replaceBtn_Clicked), nil) 912 #replaceBtn.show() 913 914 # Replace all button 915 # - this Is only shown, when the 'Search & Replace'(CTRL + H) is shown 916 win.replaceAllBtn = buttonNew("Replace All") 917 win.findBar.packStart(win.replaceAllBtn, false, false, 0) 918 discard win.replaceAllBtn.signalConnect("clicked", 919 SIGNAL_FUNC(aporia.replaceAllBtn_Clicked), nil) 920 #replaceAllBtn.show() 921 922 # Right side ... 923 924 # Close button - With a close stock image 925 var closeBtn = buttonNew() 926 var closeImage = imageNewFromStock(STOCK_CLOSE, ICON_SIZE_SMALL_TOOLBAR) 927 var closeBox = hboxNew(False, 0) 928 closeBtn.add(closeBox) 929 closeBox.show() 930 closeBox.add(closeImage) 931 closeImage.show() 932 discard closeBtn.signalConnect("clicked", 933 SIGNAL_FUNC(aporia.closeBtn_Clicked), nil) 934 win.findBar.packEnd(closeBtn, False, False, 2) 935 closeBtn.show() 936 937 # Extra button - When clicked shows a menu with options like 'Use regex' 938 var extraBtn = buttonNew() 939 var extraImage = imageNewFromStock(STOCK_PROPERTIES, ICON_SIZE_SMALL_TOOLBAR) 940 941 var extraBox = hboxNew(False, 0) 942 extraBtn.add(extraBox) 943 extraBox.show() 944 extraBox.add(extraImage) 945 extraImage.show() 946 discard extraBtn.signalConnect("clicked", 947 SIGNAL_FUNC(aporia.extraBtn_Clicked), nil) 948 win.findBar.packEnd(extraBtn, False, False, 0) 949 extraBtn.show() 950 951 MainBox.packStart(win.findBar, False, False, 0) 952 win.findBar.show() 953 954proc initStatusBar(MainBox: PBox) = 955 win.bottomBar = statusbarNew() 956 MainBox.packStart(win.bottomBar, False, False, 0) 957 win.bottomBar.show() 958 959 discard win.bottomBar.push(0, "Line: 0 Column: 0") 960 961proc initControls() = 962 # Load up the language style 963 win.langMan = languageManagerGetDefault() 964 var langpaths: array[0..1, cstring] = 965 [cstring(os.getApplicationDir() / langSpecs), nil] 966 win.langMan.setSearchPath(addr(langpaths)) 967 var nimLang = win.langMan.getLanguage("nimrod") 968 win.nimLang = nimLang 969 970 # Load the scheme 971 var schemeMan = schemeManagerGetDefault() 972 var schemepaths: array[0..1, cstring] = 973 [cstring(os.getApplicationDir() / styles), nil] 974 schemeMan.setSearchPath(addr(schemepaths)) 975 win.scheme = schemeMan.getScheme(win.settings.colorSchemeID) 976 977 # Window 978 win.w = windowNew(gtk2.WINDOW_TOPLEVEL) 979 win.w.setDefaultSize(win.settings.winWidth, win.settings.winHeight) 980 win.w.setTitle("Aporia IDE") 981 if win.settings.winMaximized: win.w.maximize() 982 983 win.w.show() # The window has to be shown before 984 # setting the position of the VPaned so that 985 # it gets set correctly, when the window is maximized. 986 987 discard win.w.signalConnect("destroy", SIGNAL_FUNC(aporia.destroy), nil) 988 discard win.w.signalConnect("delete_event", 989 SIGNAL_FUNC(aporia.delete_event), nil) 990 discard win.w.signalConnect("window-state-event", 991 SIGNAL_FUNC(aporia.windowState_Changed), nil) 992 993 # MainBox (vbox) 994 var MainBox = vboxNew(False, 0) 995 win.w.add(MainBox) 996 997 initTopMenu(MainBox) 998 initToolBar(MainBox) 999 initTAndBP(MainBox) 1000 initFindBar(MainBox) 1001 initStatusBar(MainBox) 1002 1003 MainBox.show() 1004 if confParseFail: 1005 dialogs.warning(win.w, "Error parsing config file, using default settings.") 1006 1007nimrod_init() 1008initControls() 1009main() 1010 1011