Mark McGranaghan 8d31ec147c move to vendor
2012-11-17 08:21:42 -08:00

1011 lines
34 KiB
Nim

import glib2, gtk2, gdk2, gtksourceview, dialogs, os, pango, osproc, strutils
import pegs, streams
import settings, types, cfg, search
{.push callConv:cdecl.}
const
NimrodProjectExt = ".nimprj"
var win: types.MainWin
win.Tabs = @[]
search.win = addr(win)
var lastSession: seq[string] = @[]
var confParseFail = False # This gets set to true
# When there is an error parsing the config
# Load the settings
try:
win.settings = cfg.load(lastSession)
except ECFGParse:
# TODO: Make the dialog show the exception
confParseFail = True
win.settings = cfg.defaultSettings()
except EIO:
win.settings = cfg.defaultSettings()
proc getProjectTab(): int =
for i in 0..high(win.tabs):
if win.tabs[i].filename.endswith(NimrodProjectExt): return i
proc saveTab(tabNr: int, startpath: string) =
if tabNr < 0: return
if win.Tabs[tabNr].saved: return
var path = ""
if win.Tabs[tabNr].filename == "":
path = ChooseFileToSave(win.w, startpath)
# dialogs.nim STOCK_OPEN instead of STOCK_SAVE
else:
path = win.Tabs[tabNr].filename
if path != "":
var buffer = PTextBuffer(win.Tabs[tabNr].buffer)
# Get the text from the TextView
var startIter: TTextIter
buffer.getStartIter(addr(startIter))
var endIter: TTextIter
buffer.getEndIter(addr(endIter))
var text = buffer.getText(addr(startIter), addr(endIter), False)
# Save it to a file
var f: TFile
if open(f, path, fmWrite):
f.write(text)
f.close()
win.tempStuff.lastSaveDir = splitFile(path).dir
# Change the tab name and .Tabs.filename etc.
win.Tabs[tabNr].filename = path
win.Tabs[tabNr].saved = True
var name = extractFilename(path)
var cTab = win.Tabs[tabNr]
cTab.label.setText(name)
else:
error(win.w, "Unable to write to file")
proc saveAllTabs() =
for i in 0..high(win.tabs):
saveTab(i, os.splitFile(win.tabs[i].filename).dir)
# GTK Events
# -- w(PWindow)
proc destroy(widget: PWidget, data: pgpointer) {.cdecl.} =
# gather some settings
win.settings.VPanedPos = PPaned(win.sourceViewTabs.getParent()).getPosition()
win.settings.winWidth = win.w.allocation.width
win.settings.winHeight = win.w.allocation.height
# save the settings
win.save()
# then quit
main_quit()
proc delete_event(widget: PWidget, event: PEvent, user_data: pgpointer): bool =
var quit = True
for i in low(win.Tabs)..len(win.Tabs)-1:
if not win.Tabs[i].saved:
var askSave = dialogNewWithButtons("", win.w, 0,
STOCK_SAVE, RESPONSE_ACCEPT, STOCK_CANCEL,
RESPONSE_CANCEL,
"Close without saving", RESPONSE_REJECT, nil)
askSave.setTransientFor(win.w)
# TODO: Make this dialog look better
var label = labelNew(win.Tabs[i].filename &
" is unsaved, would you like to save it ?")
PBox(askSave.vbox).pack_start(label, False, False, 0)
label.show()
var resp = askSave.run()
gtk2.destroy(PWidget(askSave))
case resp
of RESPONSE_ACCEPT:
saveTab(i, os.splitFile(win.tabs[i].filename).dir)
quit = True
of RESPONSE_CANCEL:
quit = False
break
of RESPONSE_REJECT:
quit = True
else:
quit = False
break
# If False is returned the window will close
return not quit
proc windowState_Changed(widget: PWidget, event: PEventWindowState,
user_data: pgpointer) =
win.settings.winMaximized = (event.newWindowState and
WINDOW_STATE_MAXIMIZED) != 0
# -- SourceView(PSourceView) & SourceBuffer
proc updateStatusBar(buffer: PTextBuffer){.cdecl.} =
# Incase this event gets fired before
# bottomBar is initialized
if win.bottomBar != nil and not win.tempStuff.stopSBUpdates:
var iter: TTextIter
win.bottomBar.pop(0)
buffer.getIterAtMark(addr(iter), buffer.getInsert())
var row = getLine(addr(iter)) + 1
var col = getLineOffset(addr(iter))
discard win.bottomBar.push(0, "Line: " & $row & " Column: " & $col)
proc cursorMoved(buffer: PTextBuffer, location: PTextIter,
mark: PTextMark, user_data: pgpointer){.cdecl.} =
updateStatusBar(buffer)
proc onCloseTab(btn: PButton, user_data: PWidget) =
if win.sourceViewTabs.getNPages() > 1:
var tab = win.sourceViewTabs.pageNum(user_data)
win.sourceViewTabs.removePage(tab)
win.Tabs.delete(tab)
proc onSwitchTab(notebook: PNotebook, page: PNotebookPage, pageNum: guint,
user_data: pgpointer) =
if win.Tabs.len()-1 >= pageNum:
win.w.setTitle("Aporia IDE - " & win.Tabs[pageNum].filename)
proc createTabLabel(name: string, t_child: PWidget): tuple[box: PWidget,
label: PLabel] =
var box = hboxNew(False, 0)
var label = labelNew(name)
var closebtn = buttonNew()
closeBtn.setLabel(nil)
var iconSize = iconSizeFromName("tabIconSize")
if iconSize == 0:
iconSize = iconSizeRegister("tabIconSize", 10, 10)
var image = imageNewFromStock(STOCK_CLOSE, iconSize)
discard gSignalConnect(closebtn, "clicked", G_Callback(onCloseTab), t_child)
closebtn.setImage(image)
gtk2.setRelief(closebtn, RELIEF_NONE)
box.packStart(label, True, True, 0)
box.packEnd(closebtn, False, False, 0)
box.showAll()
return (box, label)
proc changed(buffer: PTextBuffer, user_data: pgpointer) =
# Update the 'Line & Column'
#updateStatusBar(buffer)
# Change the tabs state to 'unsaved'
# and add '*' to the Tab Name
var current = win.SourceViewTabs.getCurrentPage()
var name = ""
if win.Tabs[current].filename == "":
win.Tabs[current].saved = False
name = "Untitled *"
else:
win.Tabs[current].saved = False
name = extractFilename(win.Tabs[current].filename) & " *"
var cTab = win.Tabs[current]
cTab.label.setText(name)
# Other(Helper) functions
proc initSourceView(SourceView: var PWidget, scrollWindow: var PScrolledWindow,
buffer: var PSourceBuffer) =
# This gets called by addTab
# Each tabs creates a new SourceView
# SourceScrolledWindow(ScrolledWindow)
scrollWindow = scrolledWindowNew(nil, nil)
scrollWindow.setPolicy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
scrollWindow.show()
# SourceView(gtkSourceView)
SourceView = sourceViewNew(buffer)
PSourceView(SourceView).setInsertSpacesInsteadOfTabs(True)
PSourceView(SourceView).setIndentWidth(win.settings.indentWidth)
PSourceView(SourceView).setShowLineNumbers(win.settings.showLineNumbers)
PSourceView(SourceView).setHighlightCurrentLine(
win.settings.highlightCurrentLine)
PSourceView(SourceView).setShowRightMargin(win.settings.rightMargin)
PSourceView(SourceView).setAutoIndent(win.settings.autoIndent)
var font = font_description_from_string(win.settings.font)
SourceView.modifyFont(font)
scrollWindow.add(SourceView)
SourceView.show()
buffer.setHighlightMatchingBrackets(
win.settings.highlightMatchingBrackets)
# UGLY workaround for yet another compiler bug:
discard gsignalConnect(buffer, "mark-set",
GCallback(aporia.cursorMoved), nil)
discard gsignalConnect(buffer, "changed", GCallback(aporia.changed), nil)
# -- Set the syntax highlighter scheme
buffer.setScheme(win.scheme)
proc addTab(name, filename: string) =
## Adds a tab, if filename is not "" reads the file. And sets
## the tabs SourceViews text to that files contents.
assert(win.nimLang != nil)
var buffer: PSourceBuffer = sourceBufferNew(win.nimLang)
if filename != nil and filename != "":
var lang = win.langMan.guessLanguage(filename, nil)
if lang != nil:
buffer.setLanguage(lang)
else:
buffer.setHighlightSyntax(False)
var nam = name
if nam == "": nam = "Untitled"
if filename == "": nam.add(" *")
elif filename != "" and name == "":
# Disable the undo/redo manager.
buffer.begin_not_undoable_action()
# Load the file.
var file: string = readFile(filename)
if file != nil:
buffer.set_text(file, len(file))
# Enable the undo/redo manager.
buffer.end_not_undoable_action()
# Get the name.ext of the filename, for the tabs title
nam = extractFilename(filename)
# Init the sourceview
var sourceView: PWidget
var scrollWindow: PScrolledWindow
initSourceView(sourceView, scrollWindow, buffer)
var (TabLabel, labelText) = createTabLabel(nam, scrollWindow)
# Add a tab
discard win.SourceViewTabs.appendPage(scrollWindow, TabLabel)
var nTab: Tab
nTab.buffer = buffer
nTab.sourceView = sourceView
nTab.label = labelText
nTab.saved = (filename != "")
nTab.filename = filename
win.Tabs.add(nTab)
PTextView(SourceView).setBuffer(nTab.buffer)
# GTK Events Contd.
# -- TopMenu & TopBar
proc newFile(menuItem: PMenuItem, user_data: pgpointer) =
addTab("", "")
win.sourceViewTabs.setCurrentPage(win.Tabs.len()-1)
proc openFile(menuItem: PMenuItem, user_data: pgpointer) =
var startpath = ""
var currPage = win.SourceViewTabs.getCurrentPage()
if currPage <% win.tabs.len:
startpath = os.splitFile(win.tabs[currPage].filename).dir
if startpath.len == 0:
# Use lastSavePath as the startpath
startpath = win.tempStuff.lastSaveDir
if startpath.len == 0:
startpath = os.getHomeDir()
var files = ChooseFilesToOpen(win.w, startpath)
if files.len() > 0:
for f in items(files):
try:
addTab("", f)
except EIO:
error(win.w, "Unable to read from file")
# Switch to the newly created tab
win.sourceViewTabs.setCurrentPage(win.Tabs.len()-1)
proc saveFile_Activate(menuItem: PMenuItem, user_data: pgpointer) =
var current = win.SourceViewTabs.getCurrentPage()
saveTab(current, os.splitFile(win.tabs[current].filename).dir)
proc saveFileAs_Activate(menuItem: PMenuItem, user_data: pgpointer) =
var current = win.SourceViewTabs.getCurrentPage()
var (filename, saved) = (win.Tabs[current].filename, win.Tabs[current].saved)
win.Tabs[current].saved = False
win.Tabs[current].filename = ""
saveTab(current, os.splitFile(filename).dir)
# If the user cancels the save file dialog. Restore the previous filename
# and saved state
if win.Tabs[current].filename == "":
win.Tabs[current].filename = filename
win.Tabs[current].saved = saved
proc undo(menuItem: PMenuItem, user_data: pgpointer) =
var current = win.SourceViewTabs.getCurrentPage()
if win.Tabs[current].buffer.canUndo():
win.Tabs[current].buffer.undo()
proc redo(menuItem: PMenuItem, user_data: pgpointer) =
var current = win.SourceViewTabs.getCurrentPage()
if win.Tabs[current].buffer.canRedo():
win.Tabs[current].buffer.redo()
proc find_Activate(menuItem: PMenuItem, user_data: pgpointer) =
# Get the selected text, and set the findEntry to it.
var currentTab = win.SourceViewTabs.getCurrentPage()
var insertIter: TTextIter
win.Tabs[currentTab].buffer.getIterAtMark(addr(insertIter),
win.Tabs[currentTab].buffer.getInsert())
var insertOffset = addr(insertIter).getOffset()
var selectIter: TTextIter
win.Tabs[currentTab].buffer.getIterAtMark(addr(selectIter),
win.Tabs[currentTab].buffer.getSelectionBound())
var selectOffset = addr(selectIter).getOffset()
if insertOffset != selectOffset:
var text = win.Tabs[currentTab].buffer.getText(addr(insertIter),
addr(selectIter), false)
win.findEntry.setText(text)
win.findBar.show()
win.findEntry.grabFocus()
win.replaceEntry.hide()
win.replaceLabel.hide()
win.replaceBtn.hide()
win.replaceAllBtn.hide()
proc replace_Activate(menuitem: PMenuItem, user_data: pgpointer) =
win.findBar.show()
win.findEntry.grabFocus()
win.replaceEntry.show()
win.replaceLabel.show()
win.replaceBtn.show()
win.replaceAllBtn.show()
proc settings_Activate(menuitem: PMenuItem, user_data: pgpointer) =
settings.showSettings(win)
proc viewBottomPanel_Toggled(menuitem: PCheckMenuItem, user_data: pgpointer) =
win.settings.bottomPanelVisible = menuitem.itemGetActive()
if win.settings.bottomPanelVisible:
win.bottomPanelTabs.show()
else:
win.bottomPanelTabs.hide()
var
pegLineError = peg"{[^(]*} '(' {\d+} ', ' \d+ ') Error:' \s* {.*}"
pegLineWarning = peg"{[^(]*} '(' {\d+} ', ' \d+ ') ' ('Warning:'/'Hint:') \s* {.*}"
pegOtherError = peg"'Error:' \s* {.*}"
pegSuccess = peg"'Hint: operation successful'.*"
proc addText(textView: PTextView, text: string, colorTag: PTextTag = nil) =
if text != nil:
var iter: TTextIter
textView.getBuffer().getEndIter(addr(iter))
if colorTag == nil:
textView.getBuffer().insert(addr(iter), text, len(text))
else:
textView.getBuffer().insertWithTags(addr(iter), text, len(text), colorTag,
nil)
proc createColor(textView: PTextView, name, color: string): PTextTag =
var tagTable = textView.getBuffer().getTagTable()
result = tagTable.tableLookup(name)
if result == nil:
result = textView.getBuffer().createTag(name, "foreground", color, nil)
when not defined(os.findExe):
proc findExe(exe: string): string =
## returns "" if the exe cannot be found
result = addFileExt(exe, os.exeExt)
if ExistsFile(result): return
var path = os.getEnv("PATH")
for candidate in split(path, pathSep):
var x = candidate / result
if ExistsFile(x): return x
result = ""
proc GetCmd(cmd, filename: string): string =
var f = quoteIfContainsWhite(filename)
if cmd =~ peg"\s* '$' y'findExe' '(' {[^)]+} ')' {.*}":
var exe = quoteIfContainsWhite(findExe(matches[0]))
if exe.len == 0: exe = matches[0]
result = exe & " " & matches[1] % f
else:
result = cmd % f
proc showBottomPanel() =
if not win.settings.bottomPanelVisible:
win.bottomPanelTabs.show()
win.settings.bottomPanelVisible = true
PCheckMenuItem(win.viewBottomPanelMenuItem).itemSetActive(true)
# Scroll to the end of the TextView
# This is stupid, it works sometimes... it's random
var endIter: TTextIter
win.outputTextView.getBuffer().getEndIter(addr(endIter))
discard win.outputTextView.scrollToIter(
addr(endIter), 0.25, False, 0.0, 0.0)
proc compileRun(currentTab: int, shouldRun: bool) =
if win.Tabs[currentTab].filename.len == 0: return
# Clear the outputTextView
win.outputTextView.getBuffer().setText("", 0)
var outp = osProc.execProcess(GetCmd(win.settings.nimrodCmd,
win.Tabs[currentTab].filename))
# Colors
var normalTag = createColor(win.outputTextView, "normalTag", "#3d3d3d")
var errorTag = createColor(win.outputTextView, "errorTag", "red")
var warningTag = createColor(win.outputTextView, "warningTag", "darkorange")
var successTag = createColor(win.outputTextView, "successTag", "darkgreen")
for x in outp.splitLines():
if x =~ pegLineError / pegOtherError:
win.outputTextView.addText("\n" & x, errorTag)
elif x=~ pegSuccess:
win.outputTextView.addText("\n" & x, successTag)
# Launch the process
if shouldRun:
var filename = changeFileExt(win.Tabs[currentTab].filename, os.ExeExt)
var output = "\n" & osProc.execProcess(filename)
win.outputTextView.addText(output)
elif x =~ pegLineWarning:
win.outputTextView.addText("\n" & x, warningTag)
else:
win.outputTextView.addText("\n" & x, normalTag)
showBottomPanel()
proc CompileCurrent_Activate(menuitem: PMenuItem, user_data: pgpointer) =
saveFile_Activate(nil, nil)
compileRun(win.SourceViewTabs.getCurrentPage(), false)
proc CompileRunCurrent_Activate(menuitem: PMenuItem, user_data: pgpointer) =
saveFile_Activate(nil, nil)
compileRun(win.SourceViewTabs.getCurrentPage(), true)
proc CompileProject_Activate(menuitem: PMenuItem, user_data: pgpointer) =
saveAllTabs()
compileRun(getProjectTab(), false)
proc CompileRunProject_Activate(menuitem: PMenuItem, user_data: pgpointer) =
saveAllTabs()
compileRun(getProjectTab(), true)
proc RunCustomCommand(cmd: string) =
saveFile_Activate(nil, nil)
var currentTab = win.SourceViewTabs.getCurrentPage()
if win.Tabs[currentTab].filename.len == 0 or cmd.len == 0: return
# Clear the outputTextView
win.outputTextView.getBuffer().setText("", 0)
var outp = osProc.execProcess(GetCmd(cmd, win.Tabs[currentTab].filename))
var normalTag = createColor(win.outputTextView, "normalTag", "#3d3d3d")
for x in outp.splitLines():
win.outputTextView.addText("\n" & x, normalTag)
showBottomPanel()
proc RunCustomCommand1(menuitem: PMenuItem, user_data: pgpointer) =
RunCustomCommand(win.settings.customCmd1)
proc RunCustomCommand2(menuitem: PMenuItem, user_data: pgpointer) =
RunCustomCommand(win.settings.customCmd2)
proc RunCustomCommand3(menuitem: PMenuItem, user_data: pgpointer) =
RunCustomCommand(win.settings.customCmd3)
# -- FindBar
proc nextBtn_Clicked(button: PButton, user_data: pgpointer) = findText(True)
proc prevBtn_Clicked(button: PButton, user_data: pgpointer) = findText(False)
proc replaceBtn_Clicked(button: PButton, user_data: pgpointer) =
var currentTab = win.SourceViewTabs.getCurrentPage()
var start, theEnd: TTextIter
if not win.Tabs[currentTab].buffer.getSelectionBounds(
addr(start), addr(theEnd)):
# If no text is selected, try finding a match.
findText(True)
if not win.Tabs[currentTab].buffer.getSelectionBounds(
addr(start), addr(theEnd)):
# No match
return
# Remove the text
win.Tabs[currentTab].buffer.delete(addr(start), addr(theEnd))
# Insert the replacement
var text = getText(win.replaceEntry)
win.Tabs[currentTab].buffer.insert(addr(start), text, len(text))
proc replaceAllBtn_Clicked(button: PButton, user_data: pgpointer) =
var find = getText(win.findEntry)
var replace = getText(win.replaceEntry)
discard replaceAll(find, replace)
proc closeBtn_Clicked(button: PButton, user_data: pgpointer) =
win.findBar.hide()
proc caseSens_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) =
win.settings.search = "casesens"
proc caseInSens_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) =
win.settings.search = "caseinsens"
proc style_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) =
win.settings.search = "style"
proc regex_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) =
win.settings.search = "regex"
proc peg_Changed(radiomenuitem: PRadioMenuitem, user_data: pgpointer) =
win.settings.search = "peg"
proc extraBtn_Clicked(button: PButton, user_data: pgpointer) =
var extraMenu = menuNew()
var group: PGSList
var caseSensMenuItem = radio_menu_item_new(group, "Case sensitive")
extraMenu.append(caseSensMenuItem)
discard signal_connect(caseSensMenuItem, "toggled",
SIGNAL_FUNC(caseSens_Changed), nil)
caseSensMenuItem.show()
group = caseSensMenuItem.ItemGetGroup()
var caseInSensMenuItem = radio_menu_item_new(group, "Case insensitive")
extraMenu.append(caseInSensMenuItem)
discard signal_connect(caseInSensMenuItem, "toggled",
SIGNAL_FUNC(caseInSens_Changed), nil)
caseInSensMenuItem.show()
group = caseInSensMenuItem.ItemGetGroup()
var styleMenuItem = radio_menu_item_new(group, "Style insensitive")
extraMenu.append(styleMenuItem)
discard signal_connect(styleMenuItem, "toggled",
SIGNAL_FUNC(style_Changed), nil)
styleMenuItem.show()
group = styleMenuItem.ItemGetGroup()
var regexMenuItem = radio_menu_item_new(group, "Regex")
extraMenu.append(regexMenuItem)
discard signal_connect(regexMenuItem, "toggled",
SIGNAL_FUNC(regex_Changed), nil)
regexMenuItem.show()
group = regexMenuItem.ItemGetGroup()
var pegMenuItem = radio_menu_item_new(group, "Pegs")
extraMenu.append(pegMenuItem)
discard signal_connect(pegMenuItem, "toggled",
SIGNAL_FUNC(peg_Changed), nil)
pegMenuItem.show()
# Make the correct radio button active
case win.settings.search
of "casesens":
PCheckMenuItem(caseSensMenuItem).ItemSetActive(True)
of "caseinsens":
PCheckMenuItem(caseInSensMenuItem).ItemSetActive(True)
of "style":
PCheckMenuItem(styleMenuItem).ItemSetActive(True)
of "regex":
PCheckMenuItem(regexMenuItem).ItemSetActive(True)
of "peg":
PCheckMenuItem(pegMenuItem).ItemSetActive(True)
extraMenu.popup(nil, nil, nil, nil, 0, get_current_event_time())
# GUI Initialization
proc createAccelMenuItem(toolsMenu: PMenu, accGroup: PAccelGroup,
label: string, acc: gint,
action: proc (i: PMenuItem, p: pgpointer)) =
var result = menu_item_new(label)
result.addAccelerator("activate", accGroup, acc, 0, ACCEL_VISIBLE)
ToolsMenu.append(result)
show(result)
discard signal_connect(result, "activate", SIGNAL_FUNC(action), nil)
proc createSeparator(menu: PMenu) =
var sep = separator_menu_item_new()
menu.append(sep)
sep.show()
proc initTopMenu(MainBox: PBox) =
# Create a accelerator group, used for shortcuts
# like CTRL + S in SaveMenuItem
var accGroup = accel_group_new()
add_accel_group(win.w, accGroup)
# TopMenu(MenuBar)
var TopMenu = menuBarNew()
# FileMenu
var FileMenu = menuNew()
var NewMenuItem = menu_item_new("New") # New
FileMenu.append(NewMenuItem)
show(NewMenuItem)
discard signal_connect(NewMenuItem, "activate",
SIGNAL_FUNC(newFile), nil)
createSeparator(FileMenu)
var OpenMenuItem = menu_item_new("Open...") # Open...
# CTRL + O
OpenMenuItem.add_accelerator("activate", accGroup,
KEY_o, CONTROL_MASK, ACCEL_VISIBLE)
FileMenu.append(OpenMenuItem)
show(OpenMenuItem)
discard signal_connect(OpenMenuItem, "activate",
SIGNAL_FUNC(aporia.openFile), nil)
var SaveMenuItem = menu_item_new("Save") # Save
# CTRL + S
SaveMenuItem.add_accelerator("activate", accGroup,
KEY_s, CONTROL_MASK, ACCEL_VISIBLE)
FileMenu.append(SaveMenuItem)
show(SaveMenuItem)
discard signal_connect(SaveMenuItem, "activate",
SIGNAL_FUNC(saveFile_activate), nil)
var SaveAsMenuItem = menu_item_new("Save As...") # Save as...
SaveAsMenuItem.add_accelerator("activate", accGroup,
KEY_s, CONTROL_MASK or gdk2.SHIFT_MASK, ACCEL_VISIBLE)
FileMenu.append(SaveAsMenuItem)
show(SaveAsMenuItem)
discard signal_connect(SaveAsMenuItem, "activate",
SIGNAL_FUNC(saveFileAs_Activate), nil)
var FileMenuItem = menuItemNewWithMnemonic("_File")
FileMenuItem.setSubMenu(FileMenu)
FileMenuItem.show()
TopMenu.append(FileMenuItem)
# Edit menu
var EditMenu = menuNew()
var UndoMenuItem = menu_item_new("Undo") # Undo
EditMenu.append(UndoMenuItem)
show(UndoMenuItem)
discard signal_connect(UndoMenuItem, "activate",
SIGNAL_FUNC(aporia.undo), nil)
var RedoMenuItem = menu_item_new("Redo") # Undo
EditMenu.append(RedoMenuItem)
show(RedoMenuItem)
discard signal_connect(RedoMenuItem, "activate",
SIGNAL_FUNC(aporia.redo), nil)
createSeparator(EditMenu)
var FindMenuItem = menu_item_new("Find") # Find
FindMenuItem.add_accelerator("activate", accGroup,
KEY_f, CONTROL_MASK, ACCEL_VISIBLE)
EditMenu.append(FindMenuItem)
show(FindMenuItem)
discard signal_connect(FindMenuItem, "activate",
SIGNAL_FUNC(aporia.find_Activate), nil)
var ReplaceMenuItem = menu_item_new("Replace") # Replace
ReplaceMenuItem.add_accelerator("activate", accGroup,
KEY_h, CONTROL_MASK, ACCEL_VISIBLE)
EditMenu.append(ReplaceMenuItem)
show(ReplaceMenuItem)
discard signal_connect(ReplaceMenuItem, "activate",
SIGNAL_FUNC(aporia.replace_Activate), nil)
createSeparator(EditMenu)
var SettingsMenuItem = menu_item_new("Settings...") # Settings
EditMenu.append(SettingsMenuItem)
show(SettingsMenuItem)
discard signal_connect(SettingsMenuItem, "activate",
SIGNAL_FUNC(aporia.Settings_Activate), nil)
var EditMenuItem = menuItemNewWithMnemonic("_Edit")
EditMenuItem.setSubMenu(EditMenu)
EditMenuItem.show()
TopMenu.append(EditMenuItem)
# View menu
var ViewMenu = menuNew()
win.viewBottomPanelMenuItem = check_menu_item_new("Bottom Panel")
PCheckMenuItem(win.viewBottomPanelMenuItem).itemSetActive(
win.settings.bottomPanelVisible)
win.viewBottomPanelMenuItem.add_accelerator("activate", accGroup,
KEY_f9, CONTROL_MASK, ACCEL_VISIBLE)
ViewMenu.append(win.viewBottomPanelMenuItem)
show(win.viewBottomPanelMenuItem)
discard signal_connect(win.viewBottomPanelMenuItem, "toggled",
SIGNAL_FUNC(aporia.viewBottomPanel_Toggled), nil)
var ViewMenuItem = menuItemNewWithMnemonic("_View")
ViewMenuItem.setSubMenu(ViewMenu)
ViewMenuItem.show()
TopMenu.append(ViewMenuItem)
# Tools menu
var ToolsMenu = menuNew()
createAccelMenuItem(ToolsMenu, accGroup, "Compile current file",
KEY_F4, aporia.CompileCurrent_Activate)
createAccelMenuItem(ToolsMenu, accGroup, "Compile & run current file",
KEY_F5, aporia.CompileRunCurrent_Activate)
createSeparator(ToolsMenu)
createAccelMenuItem(ToolsMenu, accGroup, "Compile project",
KEY_F8, aporia.CompileProject_Activate)
createAccelMenuItem(ToolsMenu, accGroup, "Compile & run project",
KEY_F9, aporia.CompileRunProject_Activate)
createSeparator(ToolsMenu)
createAccelMenuItem(ToolsMenu, accGroup, "Run custom command 1",
KEY_F1, aporia.RunCustomCommand1)
createAccelMenuItem(ToolsMenu, accGroup, "Run custom command 2",
KEY_F2, aporia.RunCustomCommand2)
createAccelMenuItem(ToolsMenu, accGroup, "Run custom command 3",
KEY_F3, aporia.RunCustomCommand3)
var ToolsMenuItem = menuItemNewWithMnemonic("_Tools")
ToolsMenuItem.setSubMenu(ToolsMenu)
ToolsMenuItem.show()
TopMenu.append(ToolsMenuItem)
# Help menu
MainBox.packStart(TopMenu, False, False, 0)
TopMenu.show()
proc initToolBar(MainBox: PBox) =
# TopBar(ToolBar)
var TopBar = toolbarNew()
TopBar.setStyle(TOOLBAR_ICONS)
var NewFileItem = TopBar.insertStock(STOCK_NEW, "New File",
"New File", SIGNAL_FUNC(aporia.newFile), nil, 0)
TopBar.appendSpace()
var OpenItem = TopBar.insertStock(STOCK_OPEN, "Open",
"Open", SIGNAL_FUNC(aporia.openFile), nil, -1)
var SaveItem = TopBar.insertStock(STOCK_SAVE, "Save",
"Save", SIGNAL_FUNC(saveFile_Activate), nil, -1)
TopBar.appendSpace()
var UndoItem = TopBar.insertStock(STOCK_UNDO, "Undo",
"Undo", SIGNAL_FUNC(aporia.undo), nil, -1)
var RedoItem = TopBar.insertStock(STOCK_REDO, "Redo",
"Redo", SIGNAL_FUNC(aporia.redo), nil, -1)
MainBox.packStart(TopBar, False, False, 0)
TopBar.show()
proc initSourceViewTabs() =
win.SourceViewTabs = notebookNew()
#win.sourceViewTabs.dragDestSet(DEST_DEFAULT_DROP, nil, 0, ACTION_MOVE)
discard win.SourceViewTabs.signalConnect(
"switch-page", SIGNAL_FUNC(onSwitchTab), nil)
#discard win.SourceViewTabs.signalConnect(
# "drag-drop", SIGNAL_FUNC(svTabs_DragDrop), nil)
#discard win.SourceViewTabs.signalConnect(
# "drag-data-received", SIGNAL_FUNC(svTabs_DragDataRecv), nil)
#discard win.SourceViewTabs.signalConnect(
# "drag-motion", SIGNAL_FUNC(svTabs_DragMotion), nil)
win.SourceViewTabs.set_scrollable(True)
win.SourceViewTabs.show()
if lastSession.len != 0:
for i in 0 .. len(lastSession)-1:
var splitUp = lastSession[i].split('|')
var (filename, offset) = (splitUp[0], splitUp[1])
addTab("", filename)
var iter: TTextIter
win.Tabs[i].buffer.getIterAtOffset(addr(iter), offset.parseInt())
win.Tabs[i].buffer.moveMarkByName("insert", addr(iter))
win.Tabs[i].buffer.moveMarkByName("selection_bound", addr(iter))
# TODO: Fix this..... :(
discard PTextView(win.Tabs[i].sourceView).
scrollToIter(addr(iter), 0.25, true, 0.0, 0.0)
else:
addTab("", "")
# This doesn't work :\
win.Tabs[0].sourceView.grabFocus()
proc initBottomTabs() =
win.bottomPanelTabs = notebookNew()
if win.settings.bottomPanelVisible:
win.bottomPanelTabs.show()
# output tab
var tabLabel = labelNew("Output")
var outputTab = vboxNew(False, 0)
discard win.bottomPanelTabs.appendPage(outputTab, tabLabel)
# Compiler tabs, gtktextview
var outputScrolledWindow = scrolledwindowNew(nil, nil)
outputScrolledWindow.setPolicy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
outputTab.packStart(outputScrolledWindow, true, true, 0)
outputScrolledWindow.show()
win.outputTextView = textviewNew()
outputScrolledWindow.add(win.outputTextView)
win.outputTextView.show()
outputTab.show()
proc initTAndBP(MainBox: PBox) =
# This init's the HPaned, which splits the sourceViewTabs
# and the BottomPanelTabs
initSourceViewTabs()
initBottomTabs()
var TAndBPVPaned = vpanedNew()
tandbpVPaned.pack1(win.sourceViewTabs, resize=True, shrink=False)
tandbpVPaned.pack2(win.bottomPanelTabs, resize=False, shrink=False)
MainBox.packStart(TAndBPVPaned, True, True, 0)
tandbpVPaned.setPosition(win.settings.VPanedPos)
TAndBPVPaned.show()
proc initFindBar(MainBox: PBox) =
# Create a fixed container
win.findBar = HBoxNew(False, 0)
win.findBar.setSpacing(4)
# Add a Label 'Find'
var findLabel = labelNew("Find:")
win.findBar.packStart(findLabel, False, False, 0)
findLabel.show()
# Add a (find) text entry
win.findEntry = entryNew()
win.findBar.packStart(win.findEntry, False, False, 0)
discard win.findEntry.signalConnect("activate", SIGNAL_FUNC(
aporia.nextBtn_Clicked), nil)
win.findEntry.show()
var rq: TRequisition
win.findEntry.sizeRequest(addr(rq))
# Make the (find) text entry longer
win.findEntry.set_size_request(190, rq.height)
# Add a Label 'Replace'
# - This Is only shown, when the 'Search & Replace'(CTRL + H) is shown
win.replaceLabel = labelNew("Replace:")
win.findBar.packStart(win.replaceLabel, False, False, 0)
#replaceLabel.show()
# Add a (replace) text entry
# - This Is only shown, when the 'Search & Replace'(CTRL + H) is shown
win.replaceEntry = entryNew()
win.findBar.packStart(win.replaceEntry, False, False, 0)
#win.replaceEntry.show()
var rq1: TRequisition
win.replaceEntry.sizeRequest(addr(rq1))
# Make the (replace) text entry longer
win.replaceEntry.set_size_request(100, rq1.height)
# Find next button
var nextBtn = buttonNew("Next")
win.findBar.packStart(nextBtn, false, false, 0)
discard nextBtn.signalConnect("clicked",
SIGNAL_FUNC(aporia.nextBtn_Clicked), nil)
nextBtn.show()
var nxtBtnRq: TRequisition
nextBtn.sizeRequest(addr(nxtBtnRq))
# Find previous button
var prevBtn = buttonNew("Previous")
win.findBar.packStart(prevBtn, false, false, 0)
discard prevBtn.signalConnect("clicked",
SIGNAL_FUNC(aporia.prevBtn_Clicked), nil)
prevBtn.show()
# Replace button
# - This Is only shown, when the 'Search & Replace'(CTRL + H) is shown
win.replaceBtn = buttonNew("Replace")
win.findBar.packStart(win.replaceBtn, false, false, 0)
discard win.replaceBtn.signalConnect("clicked",
SIGNAL_FUNC(aporia.replaceBtn_Clicked), nil)
#replaceBtn.show()
# Replace all button
# - this Is only shown, when the 'Search & Replace'(CTRL + H) is shown
win.replaceAllBtn = buttonNew("Replace All")
win.findBar.packStart(win.replaceAllBtn, false, false, 0)
discard win.replaceAllBtn.signalConnect("clicked",
SIGNAL_FUNC(aporia.replaceAllBtn_Clicked), nil)
#replaceAllBtn.show()
# Right side ...
# Close button - With a close stock image
var closeBtn = buttonNew()
var closeImage = imageNewFromStock(STOCK_CLOSE, ICON_SIZE_SMALL_TOOLBAR)
var closeBox = hboxNew(False, 0)
closeBtn.add(closeBox)
closeBox.show()
closeBox.add(closeImage)
closeImage.show()
discard closeBtn.signalConnect("clicked",
SIGNAL_FUNC(aporia.closeBtn_Clicked), nil)
win.findBar.packEnd(closeBtn, False, False, 2)
closeBtn.show()
# Extra button - When clicked shows a menu with options like 'Use regex'
var extraBtn = buttonNew()
var extraImage = imageNewFromStock(STOCK_PROPERTIES, ICON_SIZE_SMALL_TOOLBAR)
var extraBox = hboxNew(False, 0)
extraBtn.add(extraBox)
extraBox.show()
extraBox.add(extraImage)
extraImage.show()
discard extraBtn.signalConnect("clicked",
SIGNAL_FUNC(aporia.extraBtn_Clicked), nil)
win.findBar.packEnd(extraBtn, False, False, 0)
extraBtn.show()
MainBox.packStart(win.findBar, False, False, 0)
win.findBar.show()
proc initStatusBar(MainBox: PBox) =
win.bottomBar = statusbarNew()
MainBox.packStart(win.bottomBar, False, False, 0)
win.bottomBar.show()
discard win.bottomBar.push(0, "Line: 0 Column: 0")
proc initControls() =
# Load up the language style
win.langMan = languageManagerGetDefault()
var langpaths: array[0..1, cstring] =
[cstring(os.getApplicationDir() / langSpecs), nil]
win.langMan.setSearchPath(addr(langpaths))
var nimLang = win.langMan.getLanguage("nimrod")
win.nimLang = nimLang
# Load the scheme
var schemeMan = schemeManagerGetDefault()
var schemepaths: array[0..1, cstring] =
[cstring(os.getApplicationDir() / styles), nil]
schemeMan.setSearchPath(addr(schemepaths))
win.scheme = schemeMan.getScheme(win.settings.colorSchemeID)
# Window
win.w = windowNew(gtk2.WINDOW_TOPLEVEL)
win.w.setDefaultSize(win.settings.winWidth, win.settings.winHeight)
win.w.setTitle("Aporia IDE")
if win.settings.winMaximized: win.w.maximize()
win.w.show() # The window has to be shown before
# setting the position of the VPaned so that
# it gets set correctly, when the window is maximized.
discard win.w.signalConnect("destroy", SIGNAL_FUNC(aporia.destroy), nil)
discard win.w.signalConnect("delete_event",
SIGNAL_FUNC(aporia.delete_event), nil)
discard win.w.signalConnect("window-state-event",
SIGNAL_FUNC(aporia.windowState_Changed), nil)
# MainBox (vbox)
var MainBox = vboxNew(False, 0)
win.w.add(MainBox)
initTopMenu(MainBox)
initToolBar(MainBox)
initTAndBP(MainBox)
initFindBar(MainBox)
initStatusBar(MainBox)
MainBox.show()
if confParseFail:
dialogs.warning(win.w, "Error parsing config file, using default settings.")
nimrod_init()
initControls()
main()