Hana 9e216da9ef go.mod: add go.mod and move pygments to third_party
After go1.16, go will use module mode by default,
even when the repository is checked out under GOPATH
or in a one-off directory. Add go.mod, go.sum to keep
this repo buildable without opting out of the module
mode.

> go mod init github.com/mmcgrana/gobyexample
> go mod tidy
> go mod vendor

In module mode, the 'vendor' directory is special
and its contents will be actively maintained by the
go command. pygments aren't the dependency the go will
know about, so it will delete the contents from vendor
directory. Move it to `third_party` directory now.

And, vendor the blackfriday package.

Note: the tutorial contents are not affected by the
change in go1.16 because all the examples in this
tutorial ask users to run the go command with the
explicit list of files to be compiled (e.g.
`go run hello-world.go` or `go build command-line-arguments.go`).
When the source list is provided, the go command does
not have to compute the build list and whether it's
running in GOPATH mode or module mode becomes irrelevant.
2021-02-15 16:45:26 -05: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()