diff --git a/assets/text/TODO b/assets/text/TODO index b279ef2..35b37b0 100644 --- a/assets/text/TODO +++ b/assets/text/TODO @@ -13,8 +13,7 @@ High-priority list: 1) geometric primitives: arrow, circle, cloud/speech bubble, curve, heart, hexagon, line, pentagon, polygon, rhombus, triangle, square, star 2) region filters: crop, duplicate, erase, fill, invert, measure, pick, rotate, scale, select, shift, slice, tile, translate 3) text tool: a) allow navigating w/ cursor keys b) Unicode set key & GUI w/ MRU -4) GUI: {de,in}crease cell size -5) GUI: MRU {directories,files} -6) cleanup & refactor +4) GUI: {de,in}crease cell size on +5) cleanup & refactor vim:ff=dos tw=0 diff --git a/libgui/GuiFrame.py b/libgui/GuiFrame.py index 63fb1bb..4758595 100644 --- a/libgui/GuiFrame.py +++ b/libgui/GuiFrame.py @@ -39,6 +39,17 @@ def GuiSelectDecorator(idx, caption, label, icon, accel, initialState): return targetObject return GuiSelectDecoratorOuter # }}} +# {{{ GuiSubMenuDecorator(targetObject) +def GuiSubMenuDecorator(caption, label, icon, accel, initialState): + def GuiSubMenuDecoratorOuter(targetObject): + if callable(targetObject): + if not hasattr(targetObject, "attrDict"): + setattr(targetObject, "attrDict", []) + setattr(targetObject, "isSubMenu", True) + targetObject.attrDict = {"caption": caption, "label": label, "icon": icon, "accel": accel, "initialState": initialState, "id": None, "menu":None} + return targetObject + return GuiSubMenuDecoratorOuter +# }}} # # Non-items (0xf000-0xffff) @@ -99,6 +110,9 @@ class GuiFrame(wx.Frame): self.itemsById[menuItem.attrDict["id"]] = menuItem if hasattr(menuItem, "isSelect"): menuItemWindow = menuWindow.AppendRadioItem(menuItem.attrDict["id"], menuItem.attrDict["label"], menuItem.attrDict["caption"]) + elif hasattr(menuItem, "isSubMenu"): + menuItem.attrDict["menu"] = wx.Menu() + menuItemWindow = menuWindow.AppendSubMenu(menuItem.attrDict["menu"], menuItem.attrDict["label"], menuItem.attrDict["caption"]) else: menuItemWindow = menuWindow.Append(menuItem.attrDict["id"], menuItem.attrDict["label"], menuItem.attrDict["caption"]) if menuItem.attrDict["accel"] != None: diff --git a/libroar/RoarCanvasCommandsFile.py b/libroar/RoarCanvasCommandsFile.py index cf15091..7bdb921 100644 --- a/libroar/RoarCanvasCommandsFile.py +++ b/libroar/RoarCanvasCommandsFile.py @@ -16,7 +16,8 @@ try: except ImportError: haveUrllib = False -from GuiFrame import GuiCommandDecorator, NID_MENU_SEP +from GuiFrame import GuiCommandDecorator, GuiSubMenuDecorator, NID_MENU_SEP +from RtlPlatform import getLocalConfPathName import io, os, wx class RoarCanvasCommandsFile(): @@ -39,7 +40,7 @@ class RoarCanvasCommandsFile(): with wx.MessageDialog(self.parentCanvas, "Error: {}".format(error), "", wx.OK | wx.OK_DEFAULT) as dialog: dialogChoice = dialog.ShowModal() self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor)) - return rc + return rc, newPathName # }}} # {{{ _importFile(self, f, newDirty, wildcard) def _importFile(self, f, newDirty, wildcard): @@ -47,11 +48,19 @@ class RoarCanvasCommandsFile(): if self.lastDir != None: dialog.SetDirectory(self.lastDir) if dialog.ShowModal() == wx.ID_CANCEL: - return False + return False, None elif self._promptSaveChanges(): pathName = dialog.GetPath(); self.lastDir = os.path.dirname(pathName); return self._import(f, newDirty, pathName) # }}} + # {{{ _loadRecent(self) + def _loadRecent(self): + localConfFileName = getLocalConfPathName("Recent.lst") + if os.path.exists(localConfFileName): + with open(localConfFileName, "r", encoding="utf-8") as inFile: + for lastFile in inFile.readlines(): + self._pushRecent(lastFile.rstrip("\r\n"), False) + # }}} # {{{ _promptSaveChanges(self) def _promptSaveChanges(self): if self.parentCanvas.dirty: @@ -69,6 +78,24 @@ class RoarCanvasCommandsFile(): else: return True # }}} + # {{{ _pushRecent(self, pathName, serialise=True) + def _pushRecent(self, pathName, serialise=True): + menuItemId = wx.NewId() + if not pathName in [l["pathName"] for l in self.lastFiles]: + numLastFiles = len(self.lastFiles) if self.lastFiles != None else 0 + if (numLastFiles + 1) > 8: + self.canvasOpenRecent.attrDict["menu"].Delete(self.lastFiles[0]["menuItemId"]) + del self.lastFiles[0] + menuItemWindow = self.canvasOpenRecent.attrDict["menu"].Append(menuItemId, "{}".format(pathName), pathName) + self.parentFrame.menuItemsById[self.canvasOpenRecent.attrDict["id"]].Enable(True) + self.parentFrame.Bind(wx.EVT_MENU, lambda event: self.canvasOpenRecent(event, pathName), menuItemWindow) + self.lastFiles += [{"menuItemId":menuItemId, "menuItemWindow":menuItemWindow, "pathName":pathName}] + if serialise: + localConfFileName = getLocalConfPathName("Recent.lst") + with open(localConfFileName, "w", encoding="utf-8") as outFile: + for lastFile in [l["pathName"] for l in self.lastFiles]: + print(lastFile, file=outFile) + # }}} # {{{ canvasExit(self, event) @GuiCommandDecorator("Exit", "E&xit", None, [wx.ACCEL_CTRL, ord("X")], None) @@ -197,7 +224,17 @@ class RoarCanvasCommandsFile(): def canvasImportmIRC(pathName): rc, error = self.parentCanvas.canvas.importStore.importTextFile(pathName) return (rc, error, self.parentCanvas.canvas.importStore.outMap, pathName, self.parentCanvas.canvas.importStore.inSize) - self._importFile(canvasImportmIRC, False, "mIRC art files (*.txt)|*.txt|All Files (*.*)|*.*") + rc, newPathName = self._importFile(canvasImportmIRC, False, "mIRC art files (*.txt)|*.txt|All Files (*.*)|*.*") + if rc: + self._pushRecent(newPathName) + # }}} + # {{{ canvasOpenRecent(self, event, pathName=None) + @GuiSubMenuDecorator("Open Recent", "Open &Recent", None, None, False) + def canvasOpenRecent(self, event, pathName=None): + def canvasImportmIRC(pathName): + rc, error = self.parentCanvas.canvas.importStore.importTextFile(pathName) + return (rc, error, self.parentCanvas.canvas.importStore.outMap, pathName, self.parentCanvas.canvas.importStore.inSize) + self._import(canvasImportmIRC, False, pathName) # }}} # {{{ canvasSave(self, event) @GuiCommandDecorator("Save", "&Save", ["", wx.ART_FILE_SAVE], [wx.ACCEL_CTRL, ord("S")], None) @@ -227,16 +264,17 @@ class RoarCanvasCommandsFile(): return False else: self.canvasPathName = dialog.GetPath(); self.lastDir = os.path.dirname(self.canvasPathName); - return self.canvasSave(event, newDirty=True) + if self.canvasSave(event, newDirty=True): + self._pushRecent(pathName) # }}} # # __init__(self) def __init__(self): - self.imgurApiKey, self.lastDir = ImgurApiKey.imgurApiKey if haveImgurApiKey else None, None + self.imgurApiKey, self.lastFiles, self.lastDir = ImgurApiKey.imgurApiKey if haveImgurApiKey else None, [], None self.menus = ( ("&File", - self.canvasNew, self.canvasOpen, self.canvasSave, self.canvasSaveAs, NID_MENU_SEP, + self.canvasNew, self.canvasOpen, self.canvasOpenRecent, self.canvasSave, self.canvasSaveAs, NID_MENU_SEP, self.canvasExportAsAnsi, self.canvasExportToClipboard, self.canvasExportImgur, self.canvasExportPastebin, self.canvasExportAsPng, NID_MENU_SEP, self.canvasImportAnsi, self.canvasImportFromClipboard, self.canvasImportSauce, NID_MENU_SEP, self.canvasExit, diff --git a/librtl/RtlPlatform.py b/librtl/RtlPlatform.py new file mode 100644 index 0000000..4da6389 --- /dev/null +++ b/librtl/RtlPlatform.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# +# RtlPlatform.py +# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz +# This project is licensed under the terms of the MIT licence. +# + +import os, platform + +# {{{ getLocalConfPathName(*args) +def getLocalConfPathName(*args): + vname = "LOCALAPPDATA" if platform.system() == "Windows" else "HOME" + return os.path.join(os.getenv(vname), "roar", *args) +# }}} + +# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120 diff --git a/roar.py b/roar.py index 8daedea..e886a69 100755 --- a/roar.py +++ b/roar.py @@ -9,13 +9,18 @@ import os, sys ["libcanvas", "libgui", "libroar", "librtl", "libtools"]] from RoarClient import RoarClient +from RtlPlatform import getLocalConfPathName import wx # # Entry point def main(*argv): + localConfDirName = getLocalConfPathName() + if not os.path.exists(localConfDirName): + os.makedirs(localConfirName) wxApp, roarClient = wx.App(False), RoarClient(None) argv0, argv = argv[0], argv[1:] + roarClient.canvasPanel.commands._loadRecent() if len(argv) >= 1: if (len(argv) >= 2) and (argv[1].endswith(".lst")): roarClient.assetsWindow._load_list(argv[1]) @@ -24,6 +29,7 @@ def main(*argv): if rc: roarClient.canvasPanel.update(roarClient.canvasPanel.canvas.importStore.inSize, False, roarClient.canvasPanel.canvas.importStore.outMap) roarClient.canvasPanel.commands.update(pathName=argv[0], undoLevel=-1) + roarClient.canvasPanel.commands._pushRecent(argv[0]) else: print("error: {}".format(error), file=sys.stderr) wxApp.MainLoop()