Cleanup, bugfixes & C++ backend implementation.

1) {About,Melp?} window: switch to green on black.
2) Assets window: scroll assets list on selected item update w/ <Cursor> or on deletion.
3) Canvas window: change default brush colours to [3, -1].
4) Canvas window: copy canvas cells given transparent cells from tools.
5) Canvas window: don't disable {re,un}do during object tool usage.
6) Canvas window: don't hide cursor during {re,un}do.
7) Canvas window: draw new cells using current brush background colour on resize.
8) Canvas window: fix memory leak on cell size updating.
9) Text tool: process [\r\n] in text pasted from clipboard.

assets/audio/roar{vap0r[1-8],viking[1-5]}.wav: added.
assets/text/README.txt: updated.
assets/tools/AnsiToMiRCART.py: added (for spoke.)
assets/tools/deploy-python.sh: updated.
This commit is contained in:
Lucio Andrés Illanes Albornoz 2019-10-24 21:14:00 +02:00
parent 89c555e2fd
commit 52c5f679af
39 changed files with 1021 additions and 523 deletions

2
.vscode/tasks.json vendored
View File

@ -6,7 +6,7 @@
{
"label": "Build libgui/GuiCanvasWxBackendFast.pyd",
"type": "shell",
"command": "cd \"${workspaceFolder}/libgui\" && cmd /c cl /LD /IC:/Python37/include GuiCanvasWxBackendFast.c C:/Python37/libs/python37.lib /FeGuiCanvasWxBackendFast.pyd",
"command": "cd \"${workspaceFolder}/libgui\" && cmd /c cl /EHsc /LD /Ox /Wall /WX /IC:/Python37/include GuiCanvasWxBackendFast.cpp C:/Python37/libs/python37.lib /FeGuiCanvasWxBackendFast.pyd",
"problemMatcher": [
"$msCompile"
]

BIN
assets/audio/roarvap0r1.wav Normal file

Binary file not shown.

BIN
assets/audio/roarvap0r2.wav Normal file

Binary file not shown.

BIN
assets/audio/roarvap0r3.wav Normal file

Binary file not shown.

BIN
assets/audio/roarvap0r4.wav Normal file

Binary file not shown.

BIN
assets/audio/roarvap0r5.wav Normal file

Binary file not shown.

BIN
assets/audio/roarvap0r6.wav Normal file

Binary file not shown.

BIN
assets/audio/roarvap0r7.wav Normal file

Binary file not shown.

BIN
assets/audio/roarvap0r8.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,7 +1,7 @@
# roar.py -- mIRC art editor for Windows & Linux (WIP)
* Prerequisites on Windows: install Python v3.6.x[1] and script dependencies w/ the following elevated command prompt command line:
* Prerequisites on Windows: install Python v3.7.x[1] and script dependencies w/ the following elevated command prompt command line:
`pip install requests urllib3 wxPython`
* Prerequisites on Linux: python3 && python-wx{gtk2.8,tools} on Debian-family Linux distributions
* Prerequisites on Linux: python3 (v3.7.x) && python-wx{gtk2.8,tools} on Debian-family Linux distributions
* Screenshot:
![Screenshot](https://github.com/lalbornoz/roar/raw/master/assets/images/roar.png "Screenshot")

View File

@ -1,4 +1,4 @@
1) Backend: correctly refresh transparent cursor cells when drawing cursor patches
1) GUI: drag & drop file outside of canvas to open, into canvas as object w/ select tool
2) GUI: edit asset in new canvas, import from {canvas,object}
3) GUI: implement GuiCanvasWxBackendFast.c{,c}
4) GUI: select all
@ -6,8 +6,10 @@
6) Operators: copy, cut, delete, insert from, paste
7) Operators: crop, scale, shift, slice operators
8) Tools: measure, triangle, unicode block elements tool
9) Tools: object tool: reimplement cloning correctly outside of object tool
10) Tools: text tool: finish Arabic/RTL text implementation
11) Tools: text tool: implicitly draw (text) w/ bg -1, toggle drawing actual brushColours[1] mode w/ RMB
9) Tools: object tool: allow application of arbitrary tool to selection before setting
10) Tools: object tool: reimplement cloning correctly outside of object tool
11) Tools: reimplement in C++
12) Tools: text tool: finish Arabic/RTL text implementation
13) Tools: text tool: implicitly draw (text) w/ bg -1, toggle drawing actual brushColours[1] mode w/ RMB
vim:ff=dos tw=0

View File

@ -1,17 +1,17 @@
 3,6▟6,3▝
 3,6▟6,3▜▛3,6▟6,3▝
 3,6▟6,3▝▜3,6▟6,3▝▜▛3,6▟6,3▝
 3,6▟6,3▝3,8/\3,6▟6,3▜▛3,6▟6,3▝3,8/\3,6▟6,3▝
 9,1/\ 9,1/ 6,3▝▜▛3,6▟6,3▝8,8 3,6▟6,3▝▜▛3,6▟6,3▝
6,1(0o9 6) 6,1( 6,3▜▛3,6▟6,3▝8,8 3o _ o8 3,6▟6,3▝▜▛3,6▟
 9,1( \ 9,1) 6,3▝▜▛▜8,8 6(_3Y6_)6,3▛▝▛3,6▟6,3▝
 6,1|(__)/ 3,6▟6,3▝▜▛8,8 6\_/6,3▜▛3,6▟6,3▝
 1,3\\ 1,3.'0s3 6▜▛3,6▟6,3▝
 1,6\\ 1,6/6 0p6 1\ \ 6,3▜▛
 1,3\\/3 0o3 1\3 1\ \6▜
 1,6\6 0k6 1/)___|_|6,3▛
 1,3(_0e1__/__))) )))6▛
 12,1╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲
 12,1
 2,1
 2,1╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲
3,6▟6,3▝15
3,6▟6,3▜▛3,6▟6,3▝15
3,6▟6,3▝▜3,6▟6,3▝▜▛3,6▟6,3▝15
3,6▟6,3▝3,8/\3,6▟6,3▜▛3,6▟6,3▝3,8/\3,6▟6,3▝15
9,1/\15 9,1/15 6,3▝▜▛3,6▟6,3▝8,8 3,6▟6,3▝▜▛3,6▟6,3▝15
6,1(0o9 6)15 6,1(15 6,3▜▛3,6▟6,3▝8,8 3o _ o8 3,6▟6,3▝▜▛3,6▟
9,1( \15 9,1)15 6,3▝▜▛▜8,8 6(_3Y6_)6,3▛▝▛3,6▟6,3▝15
6,1|(__)/15 3,6▟6,3▝▜▛8,8 6\_/6,3▜▛3,6▟6,3▝15
1,3\\15 1,3.'0s3 6▜▛3,6▟6,3▝15
1,6\\15 1,6/6 0p6 1\ \ 6,3▜▛15
1,3\\/3 0o3 1\3 1\ \6▜15
1,6\6 0k6 1/)___|_|6,3▛15
1,3(_0e1__/__))) )))6▛15
12,1╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲15
12,115
2,115
2,1╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲15

31
assets/tools/AnsiToMiRCART.py Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env python3
#
# AnsiToMiRCART.py -- convert ANSI to mIRC art file (for spoke)
# Copyright (c) 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
# This project is licensed under the terms of the MIT licence.
#
import os, sys
[sys.path.append(os.path.join(os.getcwd(), "..", "..", path)) for path in ["libcanvas", "librtl"]]
from CanvasExportStore import CanvasExportStore
from CanvasImportStore import CanvasImportStore
#
# Entry point
def main(*argv):
if (len(sys.argv) - 1) != 2:
print("usage: {} <ANSI input file pathname> <mIRC art output file pathname>".format(sys.argv[0]), file=sys.stderr)
else:
canvasImportStore = CanvasImportStore()
rc, error = canvasImportStore.importAnsiFile(argv[1])
if rc:
canvasExportStore = CanvasExportStore()
with open(argv[2], "w", encoding="utf-8") as outFile:
canvasExportStore.exportTextFile(canvasImportStore.outMap, canvasImportStore.inSize, outFile)
else:
print("error: {}".format(error), file=sys.stderr)
if __name__ == "__main__":
main(*sys.argv)
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -32,6 +32,9 @@ deploy() {
-not -path '*/__pycache__/*' \
-not -path '*/__pycache__' \
-not -path './librtl/ImgurApiKey.py' \
-not -name '*.exp' \
-not -name '*.lib' \
-not -name '*.obj' \
-not -name '*.sw*' \
-not -name "${0##*/}" |\
cpio --quiet -dLmp "${_release_dname}";

View File

@ -6,7 +6,6 @@
from CanvasExportStore import CanvasExportStore
from CanvasImportStore import CanvasImportStore
from CanvasJournal import CanvasJournal
class Canvas():
def _commitPatch(self, patch):
@ -18,11 +17,51 @@ class Canvas():
else:
patchDeltaCell = self.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell];
if commitUndo:
self.journal.updateCurrentDeltas(patch, patchDelta)
self.updateCurrentDeltas(patch, patchDelta)
self._commitPatch(patch)
return True
def resize(self, newSize, commitUndo=True):
def begin(self):
deltaItem = [[], []]; self.patchesUndo.insert(self.patchesUndoLevel, deltaItem);
def end(self):
if self.patchesUndo[self.patchesUndoLevel] == [[], []]:
del self.patchesUndo[self.patchesUndoLevel]
else:
if self.patchesUndoLevel > 0:
del self.patchesUndo[:self.patchesUndoLevel]; self.patchesUndoLevel = 0;
def popCursor(self, reset=True):
patchesCursor = []
if len(self.patchesCursor):
patchesCursor = self.patchesCursor
if reset:
self.resetCursor()
return patchesCursor
def popUndo(self, redo=False):
patches = []
if not redo:
if self.patchesUndo[self.patchesUndoLevel] != None:
patches = self.patchesUndo[self.patchesUndoLevel][0]; self.patchesUndoLevel += 1;
else:
if self.patchesUndoLevel > 0:
self.patchesUndoLevel -= 1; patches = self.patchesUndo[self.patchesUndoLevel][1];
return patches
def pushCursor(self, patches):
self.patchesCursor = patches
def resetCursor(self):
self.patchesCursor = []
def resetUndo(self):
if self.patchesUndo != None:
self.patchesUndo.clear()
self.patchesUndo = [None]; self.patchesUndoLevel = 0;
def resize(self, brushColours, newSize, commitUndo=True):
newCells = []
if newSize != self.size:
if self.map == None:
self.map, oldSize = [], [0, 0]
@ -30,41 +69,43 @@ class Canvas():
oldSize = self.size
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
if commitUndo:
self.journal.begin()
self.begin()
undoPatches, redoPatches = ["resize", *oldSize], ["resize", *newSize]
self.journal.updateCurrentDeltas(redoPatches, undoPatches)
self.updateCurrentDeltas(redoPatches, undoPatches)
if deltaSize[0] < 0:
for numRow in range(oldSize[1]):
if commitUndo:
for numCol in range((oldSize[0] + deltaSize[0]), oldSize[0]):
self.journal.updateCurrentDeltas(None, [numCol, numRow, *self.map[numRow][numCol]])
self.updateCurrentDeltas(None, [numCol, numRow, *self.map[numRow][numCol]])
del self.map[numRow][-1:(deltaSize[0]-1):-1]
else:
for numRow in range(oldSize[1]):
self.map[numRow].extend([[1, 1, 0, " "]] * deltaSize[0])
self.map[numRow].extend([[*brushColours, 0, " "]] * deltaSize[0])
for numNewCol in range(oldSize[0], newSize[0]):
if commitUndo:
self.journal.updateCurrentDeltas([numNewCol, numRow, 1, 1, 0, " "], None)
self.applyPatch([numNewCol, numRow, 1, 1, 0, " "], False)
self.updateCurrentDeltas([numNewCol, numRow, *brushColours, 0, " "], None)
newCells += [[numNewCol, numRow, *brushColours, 0, " "]]
self.applyPatch([numNewCol, numRow, *brushColours, 0, " "], False)
if deltaSize[1] < 0:
if commitUndo:
for numRow in range((oldSize[1] + deltaSize[1]), oldSize[1]):
for numCol in range(oldSize[0] + deltaSize[0]):
self.journal.updateCurrentDeltas(None, [numCol, numRow, *self.map[numRow][numCol]])
self.updateCurrentDeltas(None, [numCol, numRow, *self.map[numRow][numCol]])
del self.map[-1:(deltaSize[1]-1):-1]
else:
for numNewRow in range(oldSize[1], newSize[1]):
self.map.extend([[[1, 1, 0, " "]] * newSize[0]])
self.map.extend([[[*brushColours, 0, " "]] * newSize[0]])
for numNewCol in range(newSize[0]):
if commitUndo:
self.journal.updateCurrentDeltas([numNewCol, numNewRow, 1, 1, 0, " "], None)
self.applyPatch([numNewCol, numNewRow, 1, 1, 0, " "], False)
self.updateCurrentDeltas([numNewCol, numNewRow, *brushColours, 0, " "], None)
newCells += [[numNewCol, numNewRow, *brushColours, 0, " "]]
self.applyPatch([numNewCol, numNewRow, *brushColours, 0, " "], False)
self.size = newSize
if commitUndo:
self.journal.end()
return True
self.end()
return True, newCells
else:
return False
return False, newCells
def update(self, newSize, newCanvas=None):
for numRow in range(self.size[1]):
@ -73,7 +114,15 @@ class Canvas():
and (numRow < len(newCanvas)) and (numCol < len(newCanvas[numRow])):
self._commitPatch([numCol, numRow, *newCanvas[numRow][numCol]])
def updateCurrentDeltas(self, redoPatches, undoPatches):
self.patchesUndo[self.patchesUndoLevel][0].append(undoPatches)
self.patchesUndo[self.patchesUndoLevel][1].append(redoPatches)
def __del__(self):
self.resetCursor(); self.resetUndo();
def __init__(self, size):
self.exportStore, self.importStore, self.journal, self.map, self.size = CanvasExportStore(), CanvasImportStore(), CanvasJournal(), None, size
self.exportStore, self.importStore, self.map, self.size = CanvasExportStore(), CanvasImportStore(), None, size
self.patchesCursor, self.patchesUndo, self.patchesUndoLevel = [], [None], 0
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -1,62 +0,0 @@
#!/usr/bin/env python3
#
# CanvasJournal.py
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
#
class CanvasJournal():
def begin(self):
deltaItem = [[], []]; self.patchesUndo.insert(self.patchesUndoLevel, deltaItem);
def end(self):
if self.patchesUndo[self.patchesUndoLevel] == [[], []]:
del self.patchesUndo[self.patchesUndoLevel]
else:
if self.patchesUndoLevel > 0:
del self.patchesUndo[:self.patchesUndoLevel]; self.patchesUndoLevel = 0;
def popCursor(self, reset=True):
if len(self.patchesCursor):
patchesCursor = self.patchesCursor
if reset:
self.resetCursor()
return patchesCursor
else:
return []
def popRedo(self):
if self.patchesUndoLevel > 0:
self.patchesUndoLevel -= 1; patches = self.patchesUndo[self.patchesUndoLevel];
return patches[1]
else:
return []
def popUndo(self):
if self.patchesUndo[self.patchesUndoLevel] != None:
patches = self.patchesUndo[self.patchesUndoLevel]; self.patchesUndoLevel += 1;
return patches[0]
else:
return []
def pushCursor(self, patches):
self.patchesCursor = patches
def resetCursor(self):
self.patchesCursor = []
def resetUndo(self):
if self.patchesUndo != None:
self.patchesUndo.clear()
self.patchesUndo = [None]; self.patchesUndoLevel = 0;
def updateCurrentDeltas(self, redoPatches, undoPatches):
self.patchesUndo[self.patchesUndoLevel][0].append(undoPatches)
self.patchesUndo[self.patchesUndoLevel][1].append(redoPatches)
def __del__(self):
self.resetCursor(); self.resetUndo();
def __init__(self):
self.patchesUndo = None; self.resetCursor(); self.resetUndo();
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -4,10 +4,14 @@
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
#
from ctypes import *
try:
import GuiCanvasWxBackendFast; haveGuiCanvasWxBackendFast = True;
except ImportError as e:
print("Failed to import GuiCanvasWxBackendFast: {}".format(e)); haveGuiCanvasWxBackendFast = False;
from ctypes import WinDLL
from GuiCanvasColours import Colours
import GuiCanvasWxBackendFast
import math, os, platform, wx
import math, os, platform, Rtl, wx
class GuiBufferedDC(wx.MemoryDC):
def __del__(self):
@ -143,20 +147,7 @@ class GuiCanvasWxBackend():
brush, pen = self._brushesBlend[bg][14], self._pensBlend[bg][14]
return brush, pen
def drawCursorMaskWithJournal(self, canvas, canvasJournal, eventDc, reset=True):
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
cursorPatches = canvasJournal.popCursor(reset=reset); patches = [];
for cursorCell in [p[:2] for p in cursorPatches]:
if (cursorCell[0] < canvas.size[0]) \
and (cursorCell[1] < canvas.size[1]):
patches += [[*cursorCell, *canvas.map[cursorCell[1]][cursorCell[0]]]]
if len(patches) > 0:
self.drawPatches(canvas, eventDc, patches, False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
return cursorPatches
def drawPatches(self, canvas, eventDc, patches, isCursor=False):
GuiCanvasWxBackendFast.drawPatches()
patchesRender = []
for patch in patches:
point = patch[:2]
@ -166,6 +157,8 @@ class GuiCanvasWxBackend():
patchesRender += [patchReshaped]
else:
patchesRender += [patch]
if haveGuiCanvasWxBackendFast:
GuiCanvasWxBackendFast.drawPatches(self.canvasBitmap, canvas.map, canvas.size, eventDc, isCursor, patchesRender); return;
numPatch, textBg = 0, wx.Colour(0, 0, 0, 0)
rectangles, pens, brushes = [None] * len(patchesRender), [None] * len(patchesRender), [None] * len(patchesRender)
textList, coords, foregrounds, backgrounds = [], [], [], []
@ -173,10 +166,10 @@ class GuiCanvasWxBackend():
for patchRender in patchesRender:
if (patchRender[5] == " ") and (patchRender[3] == -1):
text, textFg = "", wx.Colour(0, 0, 0, 255)
elif isCursor and (patchRender[5] == " ") and (canvas.map[patchRender[1]][patchRender[0]][3] != " "):
elif False and isCursor and (patchRender[5] == " ") and (canvas.map[patchRender[1]][patchRender[0]][3] != " "):
patchRender = [*patchRender[:-2], *canvas.map[patchRender[1]][patchRender[0]][2:]]
text, textFg = canvas.map[patchRender[1]][patchRender[0]][3], wx.Colour(self._blendColours(canvas.map[patchRender[1]][patchRender[0]][0], patchRender[3]))
elif isCursor and (patchRender[5] == " ") and (canvas.map[patchRender[1]][patchRender[0]][2] & self._CellState.CS_UNDERLINE):
elif False and isCursor and (patchRender[5] == " ") and (canvas.map[patchRender[1]][patchRender[0]][2] & self._CellState.CS_UNDERLINE):
patchRender = [*patchRender[:-2], *canvas.map[patchRender[1]][patchRender[0]][2:]]
text, textFg = "_", wx.Colour(self._blendColours(canvas.map[patchRender[1]][patchRender[0]][0], patchRender[3]))
elif patchRender[5] != " ":
@ -228,9 +221,11 @@ class GuiCanvasWxBackend():
oldDc = wx.MemoryDC(); oldDc.SelectObject(self.canvasBitmap);
newDc = wx.MemoryDC(); newBitmap = wx.Bitmap(winSize); newDc.SelectObject(newBitmap);
newDc.Blit(0, 0, *self.canvasBitmap.GetSize(), oldDc, 0, 0)
oldDc.SelectObject(wx.NullBitmap)
oldDc.SelectObject(wx.NullBitmap); newDc.SelectObject(wx.NullBitmap);
self.canvasBitmap.Destroy(); self.canvasBitmap = newBitmap;
self.canvasSize = canvasSize
self.canvasSize = canvasSize;
if haveGuiCanvasWxBackendFast:
GuiCanvasWxBackendFast.resize(tuple(self.cellSize), self._font, tuple(winSize))
def xlateEventPoint(self, event, eventDc, viewRect):
eventPoint = event.GetLogicalPosition(eventDc)
@ -244,6 +239,8 @@ class GuiCanvasWxBackend():
self._finiBrushesAndPens()
def __init__(self, canvasSize, fontName="Dejavu Sans Mono", fontPathName=os.path.join("assets", "fonts", "DejaVuSansMono.ttf"), fontSize=8):
if haveGuiCanvasWxBackendFast:
GuiCanvasWxBackendFast.init(wx)
self._brushes, self._font, self._lastBrush, self._lastPen, self._pens = None, None, None, None, None
self.canvasBitmap, self.cellSize, self.fontName, self.fontPathName, self.fontSize = None, None, fontName, fontPathName, fontSize
if platform.system() == "Windows":

View File

@ -1,25 +0,0 @@
#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
#include <Python.h>
static PyObject *
GuiCanvasWxBackendFast_drawPatches(PyObject *self, PyObject *args)
{
Py_RETURN_NONE;
}
static PyMethodDef
GuiCanvasWxBackendFast_methods[] = {
{"drawPatches", GuiCanvasWxBackendFast_drawPatches, METH_VARARGS, "XXX"},
{NULL, NULL, 0, NULL},
};
static struct PyModuleDef
GuiCanvasWxBackendFastmodule = {
PyModuleDef_HEAD_INIT, "GuiCanvasWxBackendFast", NULL, -1, GuiCanvasWxBackendFast_methods,
};
PyMODINIT_FUNC
PyInit_GuiCanvasWxBackendFast(void)
{
return PyModule_Create(&GuiCanvasWxBackendFastmodule);
}

View File

@ -0,0 +1,577 @@
/*
* GuiCanvasWxBackendFast.cpp
* Copyright (c) 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
*/
#ifdef _MSC_VER
#pragma warning(disable : 4514)
#pragma warning(disable : 4530)
#pragma warning(disable : 4577)
#pragma warning(disable : 4706)
#pragma warning(disable : 4710)
#pragma warning(disable : 4711)
#pragma warning(disable : 4820)
#pragma warning(disable : 5045)
#endif /* _MSC_VER_ */
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <chrono>
#include <cmath>
#include <map>
#include <vector>
#define PY_SSIZE_T_CLEAN /* Make "s#" use Py_ssize_t rather than int. */
#include <Python.h>
/*
* Private types
*/
typedef uint32_t COLOUR;
#define COLOUR_ALPHA(colour) (((colour) >> 24) & 0xff)
typedef uint64_t COORD;
typedef struct point_s {
COORD x, y;
} POINT;
#define POINT_EMPTY {0ULL, 0ULL}
typedef enum cell_attrs_e {
CATTR_NONE = 0x00,
CATTR_BOLD = 0x01,
CATTR_UNDERLINE = 0x02,
} CELL_ATTRS;
typedef struct cell_s {
CELL_ATTRS attrs;
COLOUR bg, fg;
POINT p;
wchar_t txt[8];
} CELL;
#define CELL_EMPTY {CATTR_NONE, 0UL, 0UL, POINT_EMPTY, {L'\0',}}
typedef struct rect_s {
POINT p0, p1;
} RECT;
#define RECT_EMPTY {POINT_EMPTY, POINT_EMPTY}
#define RECT_HEIGHT(r) ((r).p1.y - (r).p0.y)
#define RECT_WIDTH(r) ((r).p1.x - (r).p0.x)
typedef struct size_s {
uint64_t h, w;
} SIZE;
#define SIZE_EMPTY {0ULL, 0ULL}
typedef std::vector<std::vector<uint8_t>> COLOUR_LIST;
typedef std::vector<std::vector<COLOUR>> CHAR_MAP_ITEM;
typedef std::map<wchar_t, CHAR_MAP_ITEM> CHAR_MAP;
/*
* Private constants and variables
*/
#define BITMAP_BPS 24
#define BITMAP_BPS_BYTES 3
#define BLEND_ALPHA_COEFFICIENT 0.8
static PyObject *s_bitmap = NULL, *s_dc = NULL, *s_dc_tmp = NULL, *s_font = NULL, *s_wx = NULL, *s_wx_NullBitmap = NULL;
static uint8_t *s_bitmap_buffer = NULL;
static size_t s_bitmap_buffer_size = 0;
static SIZE s_bitmap_size = SIZE_EMPTY, s_cell_size = SIZE_EMPTY;
static CHAR_MAP s_char_map;
static PyObject *s_colour_black = NULL, *s_colour_white = NULL;
static PyObject *s_error = NULL;
static COLOUR_LIST s_colours = {
{255, 255, 255}, // Bright White
{0, 0, 0}, // Black
{0, 0, 187}, // Light Blue
{0, 187, 0}, // Green
{255, 85, 85}, // Red
{187, 0, 0}, // Light Red
{187, 0, 187}, // Pink
{187, 187, 0}, // Yellow
{255, 255, 85}, // Light Yellow
{85, 255, 85}, // Light Green
{0, 187, 187}, // Cyan
{85, 255, 255}, // Light Cyan
{85, 85, 255}, // Blue
{255, 85, 255}, // Light Pink
{85, 85, 85}, // Grey
{187, 187, 187}, // Light Grey
};
static COLOUR_LIST s_colours_bold = {
{255, 255, 255}, // Bright White
{85, 85, 85}, // Black
{85, 85, 255}, // Light Blue
{85, 255, 85}, // Green
{255, 85, 85}, // Red
{255, 85, 85}, // Light Red
{255, 85, 255}, // Pink
{255, 255, 85}, // Yellow
{255, 255, 85}, // Light Yellow
{85, 255, 85}, // Light Green
{85, 255, 255}, // Cyan
{85, 255, 255}, // Light Cyan
{85, 85, 255}, // Blue
{255, 85, 255}, // Light Pink
{85, 85, 85}, // Grey
{255, 255, 255}, // Light Grey
};
/*
* Private preprocessor macros
*/
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define PYTHON_TRY(expr, msg) \
[&](){bool rc = (bool)(expr); if (!rc) { PyErr_SetString(s_error, msg); }; return rc;}()
#define PYTHON_TRY_NOMEMORY(expr, msg) \
[&](){bool rc = (bool)(expr); if (!rc) { PyErr_SetString(PyExc_MemoryError, msg); }; return rc;}()
/*
* N.B. required due to absence of Python_CallMethodV()
*/
#define COMMA ,
#define PYTHON_WRAP_METHOD(fn, fmt, args1, args2) \
static bool \
python_##fn(PyObject *obj, const char *default_error, PyObject **presult, args1) \
{ \
bool rc = true; \
PyObject *result; \
\
if ((result = PyObject_CallMethod(obj, #fn, fmt, args2))) { \
if (!presult) { \
Py_XDECREF(result); \
} else { \
*presult = result; \
} \
} else { \
rc = false; \
setErrorFromLast(default_error ? default_error \
: "Failed to call " # fn "()"); \
} \
return rc; \
}
#define PYTHON_WRAP_METHOD0(fn) \
static bool \
python_##fn(PyObject *obj, const char *default_error, PyObject **presult) \
{ \
bool rc = true; \
PyObject *result; \
\
if ((result = PyObject_CallMethod(obj, #fn, ""))) { \
if (!presult) { \
Py_XDECREF(result); \
} else { \
*presult = result; \
} \
} else { \
rc = false; \
setErrorFromLast(default_error ? default_error \
: "Failed to call " # fn "()"); \
} \
return rc; \
}
/*
* Static private subroutine prototypes
*/
static COLOUR blendColours(COLOUR bg, COLOUR fg);
static void cellDraw(size_t bitmap_bps_bytes, uint8_t *bitmap_buffer, SIZE bitmap_size, CELL cell, SIZE cell_size, RECT *prect);
static bool cellDrawPixel(size_t bitmap_bps_bytes, uint8_t *bitmap_buffer, SIZE bitmap_size, CELL cell, SIZE cell_size, COLOUR colour, RECT *prect, COORD rx, COORD ry);
static bool cellDrawText(size_t bitmap_bps_bytes, uint8_t *bitmap_buffer, SIZE bitmap_size, CELL cell, SIZE cell_size, CHAR_MAP& char_map, RECT *prect);
static bool cellFetch(const COLOUR_LIST& colours, const COLOUR_LIST& colours_bold, PyObject *object, bool fromCanvas, POINT canvasPoint, CELL *pcell);
static void setErrorFromLast(const char *default_fmt, ...);
#ifdef TIMING
static std::chrono::system_clock::time_point timeBegin();
static double timeDelta(std::chrono::system_clock::time_point t0);
#endif /* TIMING */
static bool updateCharMap(SIZE cell_size, CHAR_MAP& char_map, wchar_t wch);
PYTHON_WRAP_METHOD(Bitmap, "lll", unsigned long long width COMMA unsigned long long height COMMA unsigned long long bits, width COMMA height COMMA bits);
PYTHON_WRAP_METHOD(Blit, "OllllOll", PyObject *self COMMA unsigned long long xdest COMMA unsigned long long ydest COMMA unsigned long long width COMMA unsigned long long height COMMA PyObject *source COMMA unsigned long long xsrc COMMA unsigned long long ysrc, self COMMA xdest COMMA ydest COMMA width COMMA height COMMA source COMMA xsrc COMMA ysrc);
PYTHON_WRAP_METHOD(Colour, "lll", unsigned long long red COMMA unsigned long long green COMMA unsigned long long blue, red COMMA green COMMA blue);
PYTHON_WRAP_METHOD(CopyFromBuffer, "Ol", PyObject *data COMMA unsigned long long format, data COMMA format);
PYTHON_WRAP_METHOD(CopyToBuffer, "Ol", PyObject *data COMMA unsigned long long format, data COMMA format);
PYTHON_WRAP_METHOD(DrawText, "u#ll", wchar_t *text COMMA size_t text_size COMMA unsigned long long x COMMA unsigned long long y, text COMMA text_size COMMA x COMMA y);
PYTHON_WRAP_METHOD0(MemoryDC);
PYTHON_WRAP_METHOD(SelectObject, "O", PyObject *bitmap, bitmap);
PYTHON_WRAP_METHOD(SetFont, "O", PyObject *font, font);
PYTHON_WRAP_METHOD(SetTextBackground, "O", PyObject *colour, colour);
PYTHON_WRAP_METHOD(SetTextForeground, "O", PyObject *colour, colour);
/*
* Static private subroutines
*/
static COLOUR
blendColours(COLOUR bg, COLOUR fg)
{
return (COLOUR)
(std::llround(((fg & 0xff) * BLEND_ALPHA_COEFFICIENT) + ((bg & 0xff) * (1.0 - BLEND_ALPHA_COEFFICIENT))) |
(std::llround((((fg >> 8) & 0xff) * BLEND_ALPHA_COEFFICIENT) + (((bg >> 8) & 0xff) * (1.0 - BLEND_ALPHA_COEFFICIENT))) << 8) |
(std::llround((((fg >> 16) & 0xff) * BLEND_ALPHA_COEFFICIENT) + (((bg >> 16) & 0xff) * (1.0 - BLEND_ALPHA_COEFFICIENT))) << 16));
}
static void
cellDraw(size_t bitmap_bps_bytes, uint8_t *bitmap_buffer, SIZE bitmap_size, CELL cell, SIZE cell_size, RECT *prect)
{
for (COORD ry = 0; ry < cell_size.h; ry++) {
for (COORD rx = 0; rx < cell_size.w; rx++)
cellDrawPixel(bitmap_bps_bytes, bitmap_buffer, bitmap_size, cell, cell_size, cell.bg, prect, rx, ry);
}
if (cell.attrs & CATTR_UNDERLINE) {
for (COORD rx = 0, ry = (cell_size.h - 1); rx < cell_size.w; rx++)
cellDrawPixel(bitmap_bps_bytes, bitmap_buffer, bitmap_size, cell, cell_size, cell.fg, prect, rx, ry);
}
}
static bool
cellDrawPixel(size_t bitmap_bps_bytes, uint8_t *bitmap_buffer, SIZE bitmap_size, CELL cell, SIZE cell_size, COLOUR colour, RECT *prect, COORD rx, COORD ry)
{
COORD offset, x_, y_;
bool rc = false;
x_ = (cell.p.x * cell_size.w) + rx, y_ = (cell.p.y * cell_size.h) + ry;
offset = ((y_ * bitmap_size.w) + x_) * bitmap_bps_bytes;
if ((x_ < bitmap_size.w) && (y_ < bitmap_size.h)) {
prect->p0.x = MIN(prect->p0.x > 0 ? prect->p0.x : x_, x_);
prect->p0.y = MIN(prect->p0.y > 0 ? prect->p0.y : y_, y_);
prect->p1.x = MAX(prect->p1.x, x_+ 1); prect->p1.y = MAX(prect->p1.y, y_+ 1);
bitmap_buffer[offset] = colour & 0xff;
bitmap_buffer[offset + 1] = (colour >> 8) & 0xff;
bitmap_buffer[offset + 2] = (colour >> 16) & 0xff;
rc = true;
}
return rc;
}
static bool
cellDrawText(size_t bitmap_bps_bytes, uint8_t *bitmap_buffer, SIZE bitmap_size, CELL cell, SIZE cell_size, CHAR_MAP& char_map, RECT *prect)
{
CHAR_MAP::iterator char_map_item;
COLOUR colour;
bool rc = true;
for (size_t nch = 0; (nch < (sizeof(cell.txt) / sizeof(cell.txt[0]))) && (cell.txt[nch]); nch++) {
if ((char_map_item = char_map.find(cell.txt[nch])) == char_map.end()) {
if (updateCharMap(cell_size, char_map, cell.txt[nch]))
char_map_item = char_map.find(cell.txt[nch]);
else {
rc = false; break;
}
}
for (COORD ry = 0; ry < cell_size.h; ry++) {
for (COORD rx = 0; rx < cell_size.w; rx++) {
if ((char_map_item != char_map.end())
&& (char_map_item->second[ry][rx] != (COLOUR)0x0L))
colour = cell.fg;
else
colour = cell.bg;
cellDrawPixel(bitmap_bps_bytes, bitmap_buffer, bitmap_size, cell, cell_size, colour, prect, rx, ry);
}
}
}
if (cell.attrs & CATTR_UNDERLINE) {
for (COORD rx = 0, ry = (cell_size.h - 1); rx < cell_size.w; rx++)
cellDrawPixel(bitmap_bps_bytes, bitmap_buffer, bitmap_size, cell, cell_size, cell.fg, prect, rx, ry);
}
return rc;
}
static bool
cellFetch(const COLOUR_LIST& colours, const COLOUR_LIST& colours_bold, PyObject *object, bool fromCanvas, POINT canvasPoint, CELL *pcell)
{
long bg, fg;
PyObject *canvasMapRow, *cellObject = NULL, *txt;
Py_ssize_t offset, txt_len;
const COLOUR_LIST *pcolours;
bool rc = false;
if (fromCanvas) {
offset = -2;
if ((canvasMapRow = PyList_GetItem(object, (Py_ssize_t)canvasPoint.y)))
cellObject = PyList_GetItem(canvasMapRow, (Py_ssize_t)canvasPoint.x);
} else
cellObject = object, offset = 0;
if (cellObject && PyList_Check(cellObject) && (PyList_Size(cellObject) == 6 + offset)) {
if (!fromCanvas) {
pcell->p.x = PyLong_AsUnsignedLongLong(PyList_GetItem(cellObject, 0));
pcell->p.y = PyLong_AsUnsignedLongLong(PyList_GetItem(cellObject, 1));
}
fg = PyLong_AsLong(PyList_GetItem(cellObject, 2 + offset));
bg = PyLong_AsLong(PyList_GetItem(cellObject, 3 + offset));
pcell->attrs = (CELL_ATTRS)PyLong_AsUnsignedLong(PyList_GetItem(cellObject, 4 + offset));
if (pcell->attrs & CATTR_BOLD)
pcolours = &colours_bold;
else
pcolours = &colours;
pcell->bg = (bg == -1) ? 0xff000000 : (COLOUR)((colours[(uint8_t)bg][0]) | ((colours[(uint8_t)bg][1] << 8) & 0xff00) | ((colours[(uint8_t)bg][2] << 16) & 0xff0000));
pcell->fg = (fg == -1) ? pcell->bg : (COLOUR)(((*pcolours)[(uint8_t)fg][0]) | (((*pcolours)[(uint8_t)fg][1] << 8) & 0xff00) | (((*pcolours)[(uint8_t)fg][2] << 16) & 0xff0000));
txt = PyList_GetItem(cellObject, 5 + offset);
if ((txt_len = PyUnicode_AsWideChar(txt, pcell->txt, sizeof(pcell->txt) / sizeof(pcell->txt[0]))) > 0) {
if (txt_len < (sizeof(pcell->txt) / sizeof(pcell->txt[0])))
pcell->txt[txt_len] = L'\0';
rc = true;
}
}
return rc;
}
static void
setErrorFromLast(const char *default_fmt, ...)
{
va_list ap;
static char default_buf[1024];
PyObject *exc_traceback, *exc_type, *exc_value = NULL;
PyErr_Fetch(&exc_type, &exc_value, &exc_traceback);
if (exc_value)
PyErr_SetObject(s_error, exc_value);
else {
va_start(ap, default_fmt);
vsnprintf_s(default_buf, sizeof(default_buf), default_fmt, ap);
va_end(ap);
PyErr_SetString(s_error, default_buf);
}
}
#ifdef TIMING
static std::chrono::system_clock::time_point
timeBegin()
{
return std::chrono::system_clock::now();
}
static double
timeDelta(std::chrono::system_clock::time_point t0)
{
return ((std::chrono::duration<double>)(std::chrono::system_clock::now() - t0)).count();
}
#endif /* TIMING */
static bool
updateCharMap(SIZE cell_size, CHAR_MAP& char_map, wchar_t wch)
{
PyObject *bitmap, *mv = NULL;
Py_buffer buffer;
uint8_t *char_buffer = NULL;
size_t char_buffer_size;
bool rc = false;
PyBuffer_FillInfo(&buffer, 0, NULL, 0, false, PyBUF_WRITABLE);
char_buffer_size = s_cell_size.w * s_cell_size.h * BITMAP_BPS_BYTES;
if (python_Bitmap(s_wx, NULL, &bitmap, s_cell_size.w, s_cell_size.h, BITMAP_BPS)
&& python_SelectObject(s_dc_tmp, NULL, NULL, bitmap)
&& python_SetFont(s_dc_tmp, NULL, NULL, s_font)
&& python_SetTextBackground(s_dc_tmp, NULL, NULL, s_colour_black)
&& python_SetTextForeground(s_dc_tmp, NULL, NULL, s_colour_white)
&& python_DrawText(s_dc_tmp, NULL, NULL, &wch, 1, 0, 0)
&& PYTHON_TRY_NOMEMORY((char_buffer = (uint8_t *)malloc(char_buffer_size)), "Failed to allocate character bitmap buffer")
&& PYTHON_TRY(PyBuffer_FillInfo(&buffer, 0, char_buffer, (Py_ssize_t)char_buffer_size, false, PyBUF_WRITABLE) == 0, "Failed to create Py_buffer")
&& PYTHON_TRY((mv = PyMemoryView_FromBuffer(&buffer)), "Failed to create Py_buffer memory view")
&& python_CopyToBuffer(bitmap, NULL, NULL, mv, 0)) {
char_map[wch] = CHAR_MAP_ITEM(cell_size.h);
for (COORD ry = 0; ry < cell_size.h; ry++) {
for (COORD rx = 0; rx < cell_size.w; rx++)
char_map[wch][ry].push_back(
(((COLOUR)char_buffer[(((ry * cell_size.w) + rx) * BITMAP_BPS_BYTES)]) & 0xff) |
(((COLOUR)char_buffer[(((ry * cell_size.w) + rx) * BITMAP_BPS_BYTES) + 1] << 8) & 0xff00) |
(((COLOUR)char_buffer[(((ry * cell_size.w) + rx) * BITMAP_BPS_BYTES) + 2] << 16) & 0xff0000));
}
rc = true;
}
if (s_dc_tmp)
python_SelectObject(s_dc_tmp, NULL, NULL, s_wx_NullBitmap);
Py_XDECREF(bitmap);
if (char_buffer) {
free(char_buffer);
}
Py_XDECREF(mv); PyBuffer_Release(&buffer);
return rc;
}
/*
* Private Python module subroutine prototypes
*/
static PyObject *GuiCanvasWxBackendFast_drawPatches(PyObject *self, PyObject *args);
static PyObject *GuiCanvasWxBackendFast_init(PyObject *self, PyObject *args);
static PyObject *GuiCanvasWxBackendFast_resize(PyObject *self, PyObject *args);
/*
* Private Python module subroutines
*/
static PyObject *
GuiCanvasWxBackendFast_drawPatches(PyObject *self, PyObject *args)
{
PyObject *bitmap, *canvas_map, *canvas_size_obj, *eventDc, *patches;
Py_buffer buffer;
SIZE canvas_size;
CELL cell, cell_canvas;
bool isCursor, skip, status = true;
PyObject *iter, *iter_cur, *mv = NULL, *rc = NULL;
RECT rect = RECT_EMPTY;
(void)self;
PyBuffer_FillInfo(&buffer, 0, NULL, 0, false, PyBUF_WRITABLE);
#ifdef TIMING
auto t0 = timeBegin();
#endif /* TIMING */
if (PYTHON_TRY(PyArg_ParseTuple(args, "OOOOpO", &bitmap, &canvas_map, &canvas_size_obj, &eventDc, &isCursor, &patches), "Invalid arguments")
&& PYTHON_TRY((iter = PyObject_GetIter(patches)), "Failed to get patches iterator object")) {
canvas_size.w = PyLong_AsUnsignedLong(PyList_GetItem(canvas_size_obj, 0));
canvas_size.h = PyLong_AsUnsignedLong(PyList_GetItem(canvas_size_obj, 1));
while (iter_cur = PyIter_Next(iter)) {
skip = false, status = true;
if (PYTHON_TRY(cellFetch(s_colours, s_colours_bold, iter_cur, false, POINT_EMPTY, &cell), "Failed to get patch cell")) {
if (isCursor) {
if (!(skip = !cellFetch(s_colours, s_colours_bold, canvas_map, true, cell.p, &cell_canvas))) {
cell.attrs = cell_canvas.attrs;
if (COLOUR_ALPHA(cell.bg) != 0xff) {
cell.fg = blendColours(cell_canvas.fg, cell.bg); cell.bg = blendColours(cell_canvas.bg, cell.bg);
if ((cell_canvas.txt[0] == L' ') && (COLOUR_ALPHA(cell_canvas.bg) == 0xff))
cell.txt[0] = L'\u2591', cell.txt[1] = L'\0';
else
memcpy(cell.txt, cell_canvas.txt, sizeof(cell.txt));
} else if (COLOUR_ALPHA(cell_canvas.bg) == 0xff) {
cell.fg = cell_canvas.fg, cell.bg = cell_canvas.bg;
if (cell_canvas.txt[0] == L' ')
cell.txt[0] = L'\u2591', cell.txt[1] = L'\0';
else
memcpy(cell.txt, cell_canvas.txt, sizeof(cell.txt));
} else {
cell.fg = cell_canvas.fg, cell.bg = cell_canvas.bg;
memcpy(cell.txt, cell_canvas.txt, sizeof(cell.txt));
}
}
} else if ((cell.txt[0] == L' ') && (COLOUR_ALPHA(cell.bg) == 0xff))
cell.bg = 0x00000000, cell.txt[0] = L'\u2591', cell.txt[1] = L'\0';
if (status && !skip) {
if (cell.txt[0] != L' ')
status = cellDrawText(BITMAP_BPS_BYTES, s_bitmap_buffer, s_bitmap_size, cell, s_cell_size, s_char_map, &rect);
else
cellDraw(BITMAP_BPS_BYTES, s_bitmap_buffer, s_bitmap_size, cell, s_cell_size, &rect);
}
}
Py_XDECREF(iter_cur);
}
Py_XDECREF(iter);
if (status
&& PYTHON_TRY(PyBuffer_FillInfo(&buffer, 0, s_bitmap_buffer, (Py_ssize_t)s_bitmap_buffer_size, false, PyBUF_WRITABLE) == 0, "Failed to create Py_buffer")
&& PYTHON_TRY((mv = PyMemoryView_FromBuffer(&buffer)), "Failed to create Py_buffer memory view")
&& python_CopyFromBuffer(s_bitmap, NULL, NULL, mv, 0)
&& python_Blit((PyObject *)eventDc->ob_type, NULL, NULL, eventDc, rect.p0.x, rect.p0.y, RECT_WIDTH(rect), RECT_HEIGHT(rect), s_dc, rect.p0.x, rect.p0.y)) {
Py_INCREF(Py_True), rc = Py_True;
}
}
#ifdef TIMING
printf("drawing took %.2f ms\n", timeDelta(t0) * 1000);
#endif /* TIMING */
Py_XDECREF(mv); PyBuffer_Release(&buffer);
return rc;
}
static PyObject *
GuiCanvasWxBackendFast_init(PyObject *self, PyObject *args)
{
PyObject *colour_black = NULL, *colour_white = NULL, *dc = NULL, *dc_tmp = NULL, *wx = NULL, *wx_NullBitmap = NULL;
PyObject *rc = NULL, *wx_dict;
(void)self;
if (PYTHON_TRY(PyArg_ParseTuple(args, "O", &wx), "Invalid arguments")
&& PYTHON_TRY((wx_dict = PyModule_GetDict(wx)), "Failed to get wx module dictionary")
&& python_Colour(wx, NULL, &colour_black, 0, 0, 0)
&& python_Colour(wx, NULL, &colour_white, 255, 255, 255)
&& python_MemoryDC(wx, NULL, &dc)
&& python_MemoryDC(wx, NULL, &dc_tmp)
&& PYTHON_TRY((wx_NullBitmap = PyObject_GetAttrString(wx, "NullBitmap")), "Failed to get wx.NullBitmap attribute")) {
s_colour_black = colour_black, s_colour_white = colour_white, s_dc = dc, s_dc_tmp = dc_tmp;
s_wx = wx; Py_INCREF(wx_NullBitmap), s_wx_NullBitmap = wx_NullBitmap;
Py_INCREF(Py_True), rc = Py_True;
}
if (!rc) {
Py_XDECREF(colour_black); Py_XDECREF(colour_white); Py_XDECREF(dc); Py_XDECREF(dc_tmp); Py_XDECREF(wx_NullBitmap);
}
return rc;
}
static PyObject *
GuiCanvasWxBackendFast_resize(PyObject *self, PyObject *args)
{
uint8_t *bitmap_buffer_new = NULL;
size_t bitmap_buffer_size_new;
PyObject *bitmap_new = NULL;
SIZE bitmap_size_new, cell_size_new;
PyObject *cellSize, *cellSizeHeightObj, *cellSizeWidthObj, *font, *winSize, *winSizeHeightObj, *winSizeWidthObj, *rc = NULL;
(void)self;
if (PYTHON_TRY(PyArg_ParseTuple(args, "OOO", &cellSize, &font, &winSize) && PyTuple_Check(cellSize) && PyTuple_Check(winSize), "Invalid arguments")) {
cellSizeWidthObj = PyTuple_GetItem(cellSize, 0); cellSizeHeightObj = PyTuple_GetItem(cellSize, 1);
cell_size_new.w = PyLong_AsUnsignedLong(cellSizeWidthObj); cell_size_new.h = PyLong_AsUnsignedLong(cellSizeHeightObj);
winSizeWidthObj = PyTuple_GetItem(winSize, 0); bitmap_size_new.w = PyLong_AsUnsignedLong(winSizeWidthObj);
winSizeHeightObj = PyTuple_GetItem(winSize, 1); bitmap_size_new.h = PyLong_AsUnsignedLong(winSizeHeightObj);
bitmap_buffer_size_new = bitmap_size_new.h * bitmap_size_new.w * BITMAP_BPS_BYTES;
if (python_Bitmap(s_wx, NULL, &bitmap_new, bitmap_size_new.w, bitmap_size_new.h, BITMAP_BPS)
&& (s_bitmap ? python_SelectObject(s_dc, NULL, NULL, s_wx_NullBitmap) : true)
&& python_SelectObject(s_dc, NULL, NULL, bitmap_new)
&& PYTHON_TRY_NOMEMORY((bitmap_buffer_new = (uint8_t *)malloc(bitmap_buffer_size_new)), "Failed to allocate bitmap buffer")) {
if (s_bitmap_buffer)
free(s_bitmap_buffer);
s_bitmap_buffer = bitmap_buffer_new;
Py_XDECREF(s_bitmap); s_bitmap = bitmap_new;
s_bitmap_buffer_size = bitmap_buffer_size_new, s_bitmap_size = bitmap_size_new;
if ((cell_size_new.h != s_cell_size.h) || (cell_size_new.w != s_cell_size.w))
s_char_map.clear();
s_cell_size = cell_size_new; Py_INCREF(font), s_font = font; Py_INCREF(Py_True), rc = Py_True;
}
}
if (!rc) {
if (bitmap_buffer_new)
free(bitmap_buffer_new);
Py_XDECREF(bitmap_new); Py_XDECREF(font);
}
return rc;
}
/*
* Python C/C++ extension footer
*/
static PyMethodDef
GuiCanvasWxBackendFast_methods[] = {
{"drawPatches", GuiCanvasWxBackendFast_drawPatches, METH_VARARGS, "drawPatches"},
{"init", GuiCanvasWxBackendFast_init, METH_VARARGS, "init"},
{"resize", GuiCanvasWxBackendFast_resize, METH_VARARGS, "resize"},
{NULL, NULL, 0, NULL},
};
static struct PyModuleDef
GuiCanvasWxBackendFastmodule = {
PyModuleDef_HEAD_INIT, "GuiCanvasWxBackendFast", NULL, -1, GuiCanvasWxBackendFast_methods,
};
PyMODINIT_FUNC
PyInit_GuiCanvasWxBackendFast(void)
{
PyObject *m = NULL;
m = PyModule_Create(&GuiCanvasWxBackendFastmodule);
s_error = PyErr_NewException("GuiCanvasWxBackendFast.error", NULL, NULL);
Py_XINCREF(s_error);
if (PyModule_AddObject(m, "error", s_error) < 0) {
Py_XDECREF(s_error); Py_CLEAR(s_error); Py_DECREF(m); m = NULL;
}
return m;
}

View File

@ -165,7 +165,7 @@ class RoarAssetsWindow(GuiMiniFrame):
def resize(self, canvas, newSize):
oldSize = [0, 0] if canvas.map == None else canvas.size
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
if canvas.resize(newSize, False):
if canvas.resize((1, 1,), newSize, False):
panelSize = [a * b for a, b in zip(canvas.size, self.backend.cellSize)]
self.panelCanvas.SetMinSize(panelSize); self.panelCanvas.SetSize(wx.DefaultCoord, wx.DefaultCoord, *panelSize);
curWindow = self.panelCanvas
@ -228,6 +228,7 @@ class RoarAssetsWindow(GuiMiniFrame):
id = +1 if keyCode == wx.WXK_DOWN else -1
self.currentIndex = (self.currentIndex + id) % len(self.canvasList)
self.listView.Select(self.currentIndex, on=1)
self.listView.EnsureVisible(self.currentIndex)
else:
index, rc = self.listView.GetFirstSelected(), False
if index != -1:
@ -304,6 +305,8 @@ class RoarAssetsWindow(GuiMiniFrame):
break
while len(items):
self._removeAsset(items[0]); del items[0]; items = [i - 1 for i in items];
if self.currentIndex != None:
self.listView.EnsureVisible(self.currentIndex)
def onSaveList(self, event):
rc = True

View File

@ -5,12 +5,13 @@
#
from GuiCanvasColours import Colours
from GuiFrame import NID_TOOLBAR_HSEP
from GuiFrame import NID_MENU_SEP, NID_TOOLBAR_HSEP
from RoarCanvasCommandsEdit import RoarCanvasCommandsEdit
from RoarCanvasCommandsFile import RoarCanvasCommandsFile
from RoarCanvasCommandsHelp import RoarCanvasCommandsHelp
from RoarCanvasCommandsOperators import RoarCanvasCommandsOperators
from RoarCanvasCommandsTools import RoarCanvasCommandsTools
from ToolObject import ToolObject
import os, wx
class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCanvasCommandsTools, RoarCanvasCommandsOperators, RoarCanvasCommandsHelp):
@ -40,6 +41,65 @@ class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCan
_initColourBitmaps_(RoarCanvasCommandsEdit.canvasColour, RoarCanvasCommandsEdit.canvasColourAlpha, 1.0)
_initColourBitmaps_(RoarCanvasCommandsEdit.canvasColourBackground, RoarCanvasCommandsEdit.canvasColourAlphaBackground, 1.5)
def _initInterface(self):
accels = ()
menus = (
("&File",
self.canvasNew, self.canvasOpen, self.canvasOpenRecent, self.canvasRestore, self.canvasSave, self.canvasSaveAs, NID_MENU_SEP,
("&Export...", self.canvasExportAsAnsi, self.canvasExportToClipboard, self.canvasExportImgur, self.canvasExportPastebin, self.canvasExportAsPng,),
("&Import...", self.canvasImportAnsi, self.canvasImportFromClipboard, self.canvasImportSauce,),
NID_MENU_SEP,
self.canvasExit,
),
("&Edit",
self.canvasUndo, self.canvasRedo, NID_MENU_SEP,
self.canvasCut, self.canvasCopy, self.canvasPaste,
self.canvasDelete, NID_MENU_SEP,
("Brush size", self.canvasBrushSize(self.canvasBrushSize, 0, True), self.canvasBrushSize(self.canvasBrushSize, 0, False), self.canvasBrushSize(self.canvasBrushSize, 1, True), self.canvasBrushSize(self.canvasBrushSize, 1, False), NID_MENU_SEP,
self.canvasBrushSize(self.canvasBrushSize, 2, True), self.canvasBrushSize(self.canvasBrushSize, 2, False),),
("Canvas size", self.canvasCanvasSize(self.canvasCanvasSize, 1, True), self.canvasCanvasSize(self.canvasCanvasSize, 1, False), self.canvasCanvasSize(self.canvasCanvasSize, 0, True), self.canvasCanvasSize(self.canvasCanvasSize, 0, False), NID_MENU_SEP,
self.canvasCanvasSize(self.canvasCanvasSize, 2, True), self.canvasCanvasSize(self.canvasCanvasSize, 2, False),),
self.canvasColoursFlip,
NID_MENU_SEP,
self.canvasBrush(self.canvasBrush, 0), NID_MENU_SEP,
self.canvasAssetsWindowHide, self.canvasAssetsWindowShow,
),
("&Tools",
self.canvasTool(self.canvasTool, 1), self.canvasTool(self.canvasTool, 7), self.canvasTool(self.canvasTool, 0), self.canvasTool(self.canvasTool, 3), self.canvasTool(self.canvasTool, 4), self.canvasTool(self.canvasTool, 8), self.canvasTool(self.canvasTool, 5), self.canvasTool(self.canvasTool, 2), self.canvasTool(self.canvasTool, 6),
),
("&Operators",
self.canvasOperator(self.canvasOperator, 0), self.canvasOperator(self.canvasOperator, 1), self.canvasOperator(self.canvasOperator, 2), self.canvasOperator(self.canvasOperator, 3), self.canvasOperator(self.canvasOperator, 4),
),
("&Help",
self.canvasMelp, NID_MENU_SEP, self.canvasNewIssueGitHub, self.canvasVisitGitHub, NID_MENU_SEP, self.canvasAbout,
),
)
toolBars = (
(self.canvasNew, self.canvasOpen, self.canvasSave, self.canvasSaveAs, NID_TOOLBAR_HSEP,
self.canvasUndo, self.canvasRedo, NID_TOOLBAR_HSEP,
self.canvasCut, self.canvasCopy, self.canvasPaste, self.canvasDelete, NID_TOOLBAR_HSEP,
self.canvasAssetsWindowHide, self.canvasAssetsWindowShow, NID_TOOLBAR_HSEP,
),
(self.canvasTool(self.canvasTool, 1), self.canvasTool(self.canvasTool, 7), self.canvasTool(self.canvasTool, 0), self.canvasTool(self.canvasTool, 3), self.canvasTool(self.canvasTool, 4), self.canvasTool(self.canvasTool, 8), self.canvasTool(self.canvasTool, 5), self.canvasTool(self.canvasTool, 2), self.canvasTool(self.canvasTool, 6),),
(self.canvasColour(self.canvasColour, 0), self.canvasColour(self.canvasColour, 1), self.canvasColour(self.canvasColour, 2), self.canvasColour(self.canvasColour, 3),
self.canvasColour(self.canvasColour, 4), self.canvasColour(self.canvasColour, 5), self.canvasColour(self.canvasColour, 6), self.canvasColour(self.canvasColour, 7),
self.canvasColour(self.canvasColour, 8), self.canvasColour(self.canvasColour, 9), self.canvasColour(self.canvasColour, 10), self.canvasColour(self.canvasColour, 11),
self.canvasColour(self.canvasColour, 12), self.canvasColour(self.canvasColour, 13), self.canvasColour(self.canvasColour, 14), self.canvasColour(self.canvasColour, 15),
self.canvasColourAlpha(self.canvasColourAlpha, 0), self.canvasColoursFlip, NID_TOOLBAR_HSEP,
self.canvasBrushSize(self.canvasBrushSize, 1, True), self.canvasBrushSize(self.canvasBrushSize, 1, False), self.canvasBrushSize(self.canvasBrushSize, 0, True), self.canvasBrushSize(self.canvasBrushSize, 0, False), NID_TOOLBAR_HSEP,
self.canvasBrushSize(self.canvasBrushSize, 2, True), self.canvasBrushSize(self.canvasBrushSize, 2, False),
),
(self.canvasColourBackground(self.canvasColourBackground, 0), self.canvasColourBackground(self.canvasColourBackground, 1), self.canvasColourBackground(self.canvasColourBackground, 2), self.canvasColourBackground(self.canvasColourBackground, 3),
self.canvasColourBackground(self.canvasColourBackground, 4), self.canvasColourBackground(self.canvasColourBackground, 5), self.canvasColourBackground(self.canvasColourBackground, 6), self.canvasColourBackground(self.canvasColourBackground, 7),
self.canvasColourBackground(self.canvasColourBackground, 8), self.canvasColourBackground(self.canvasColourBackground, 9), self.canvasColourBackground(self.canvasColourBackground, 10), self.canvasColourBackground(self.canvasColourBackground, 11),
self.canvasColourBackground(self.canvasColourBackground, 12), self.canvasColourBackground(self.canvasColourBackground, 13), self.canvasColourBackground(self.canvasColourBackground, 14), self.canvasColourBackground(self.canvasColourBackground, 15),
self.canvasColourAlphaBackground(self.canvasColourAlphaBackground, 0), self.canvasColoursFlip, NID_TOOLBAR_HSEP,
self.canvasCanvasSize(self.canvasCanvasSize, 1, True), self.canvasCanvasSize(self.canvasCanvasSize, 1, False), self.canvasCanvasSize(self.canvasCanvasSize, 0, True), self.canvasCanvasSize(self.canvasCanvasSize, 0, False), NID_TOOLBAR_HSEP,
self.canvasCanvasSize(self.canvasCanvasSize, 2, True), self.canvasCanvasSize(self.canvasCanvasSize, 2, False),
),
)
return accels, menus, toolBars
def update(self, **kwargs):
self.lastPanelState.update(kwargs); textItems = [];
if "cellPos" in self.lastPanelState:
@ -53,17 +113,13 @@ class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCan
toolBar = self.parentFrame.toolBarItemsById[self.canvasColour(self.canvasColour, self.lastPanelState["colours"][0]).attrDict["id"]][0]
toolBarBg = self.parentFrame.toolBarItemsById[self.canvasColourBackground(self.canvasColourBackground, self.lastPanelState["colours"][1]).attrDict["id"]][0]
if self.lastPanelState["colours"][0] != -1:
toolBar.ToggleTool(self.canvasColour(self.canvasColour, self.lastPanelState["colours"][0]).attrDict["id"], True)
toolBar.Refresh()
toolBar.ToggleTool(self.canvasColour(self.canvasColour, self.lastPanelState["colours"][0]).attrDict["id"], True); toolBar.Refresh()
else:
toolBar.ToggleTool(self.canvasColourAlpha(self.canvasColourAlpha, 0).attrDict["id"], True)
toolBar.Refresh()
toolBar.ToggleTool(self.canvasColourAlpha(self.canvasColourAlpha, 0).attrDict["id"], True); toolBar.Refresh()
if self.lastPanelState["colours"][1] != -1:
toolBarBg.ToggleTool(self.canvasColourBackground(self.canvasColourBackground, self.lastPanelState["colours"][1]).attrDict["id"], True)
toolBarBg.Refresh()
toolBarBg.ToggleTool(self.canvasColourBackground(self.canvasColourBackground, self.lastPanelState["colours"][1]).attrDict["id"], True); toolBarBg.Refresh()
else:
toolBarBg.ToggleTool(self.canvasColourAlphaBackground(self.canvasColourAlphaBackground, 0).attrDict["id"], True)
toolBarBg.Refresh()
toolBarBg.ToggleTool(self.canvasColourAlphaBackground(self.canvasColourAlphaBackground, 0).attrDict["id"], True); toolBarBg.Refresh()
if "pathName" in self.lastPanelState:
if self.lastPanelState["pathName"] != None:
basePathName = os.path.basename(self.lastPanelState["pathName"])
@ -71,27 +127,25 @@ class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCan
self.parentFrame.SetTitle("{} - roar".format(basePathName))
else:
self.parentFrame.SetTitle("roar")
if "toolName" in self.lastPanelState:
textItems.append("T: {}".format(self.lastPanelState["toolName"]))
if ("operator" in self.lastPanelState) \
and (self.lastPanelState["operator"] != None):
if "currentTool" in self.lastPanelState:
self.parentFrame.menuItemsById[self.canvasTool.attrList[self.lastPanelState["currentToolIdx"]]["id"]].Check(True)
toolBar = self.parentFrame.toolBarItemsById[self.canvasTool.attrList[self.lastPanelState["currentToolIdx"]]["id"]][0]
toolBar.ToggleTool(self.canvasTool.attrList[self.lastPanelState["currentToolIdx"]]["id"], True); toolBar.Refresh();
if (self.lastPanelState["currentTool"] != None) and (self.lastPanelState["currentTool"].__class__ == ToolObject):
self.parentFrame.menuItemsById[self.canvasOperator.attrList[4]["id"]].Enable(True)
else:
self.parentFrame.menuItemsById[self.canvasOperator.attrList[4]["id"]].Enable(False)
textItems.append("T: {}".format(self.lastPanelState["currentTool"].name if (self.lastPanelState["currentTool"] != None) else "Cursor"))
if ("operator" in self.lastPanelState) and (self.lastPanelState["operator"] != None):
textItems.append("O: {}".format(self.lastPanelState["operator"]))
if "dirty" in self.lastPanelState \
and self.lastPanelState["dirty"]:
if ("dirty" in self.lastPanelState) and self.lastPanelState["dirty"]:
textItems.append("*")
if "backupStatus" in self.lastPanelState \
and self.lastPanelState["backupStatus"] == True:
if ("backupStatus" in self.lastPanelState) and (self.lastPanelState["backupStatus"] == True):
textItems.append("Saving backup...")
self.parentFrame.statusBar.SetStatusText(" | ".join(textItems))
if ("undoInhibit" in self.lastPanelState) \
and (self.lastPanelState["undoInhibit"]):
for item in (self.canvasRedo, self.canvasUndo):
self.parentFrame.menuItemsById[item.attrDict["id"]].Enable(False)
toolBar = self.parentFrame.toolBarItemsById[item.attrDict["id"]][0]
toolBar.EnableTool(item.attrDict["id"], False); toolBar.Refresh();
elif "undoLevel" in self.lastPanelState:
if "undoLevel" in self.lastPanelState:
if (self.lastPanelState["undoLevel"] >= 0) \
and (self.lastPanelState["undoLevel"] < (len(self.parentCanvas.canvas.journal.patchesUndo) - 1)):
and (self.lastPanelState["undoLevel"] < (len(self.parentCanvas.canvas.patchesUndo) - 1)):
self.parentFrame.menuItemsById[self.canvasUndo.attrDict["id"]].Enable(True)
toolBar = self.parentFrame.toolBarItemsById[self.canvasUndo.attrDict["id"]][0]
toolBar.EnableTool(self.canvasUndo.attrDict["id"], True); toolBar.Refresh();
@ -109,45 +163,8 @@ class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCan
toolBar.EnableTool(self.canvasRedo.attrDict["id"], False); toolBar.Refresh();
def __init__(self, parentCanvas, parentFrame):
accels, menus, toolBars = [], [], []
[classObject.__init__(self) for classObject in self.__class__.__bases__]
self._initColourBitmaps(); self.accels, self.menus, self.toolBars = self._initInterface();
self.canvasPathName, self.lastPanelState, self.parentCanvas, self.parentFrame = None, {}, parentCanvas, parentFrame
for classObject in self.__class__.__bases__:
classObject.__init__(self)
if len(self.accels):
accels += self.accels
if len(self.menus):
menus += self.menus
if len(self.toolBars):
toolBars += self.toolBars
self._initColourBitmaps()
toolBars.append(
[self.canvasNew, self.canvasOpen, self.canvasSave, self.canvasSaveAs, NID_TOOLBAR_HSEP,
self.canvasUndo, self.canvasRedo, NID_TOOLBAR_HSEP,
self.canvasCut, self.canvasCopy, self.canvasPaste, self.canvasDelete, NID_TOOLBAR_HSEP,
self.canvasAssetsWindowHide, self.canvasAssetsWindowShow, NID_TOOLBAR_HSEP,
])
toolBars.append(
[self.canvasTool(self.canvasTool, 1), self.canvasTool(self.canvasTool, 7), self.canvasTool(self.canvasTool, 0), self.canvasTool(self.canvasTool, 3), self.canvasTool(self.canvasTool, 4), self.canvasTool(self.canvasTool, 8), self.canvasTool(self.canvasTool, 5), self.canvasTool(self.canvasTool, 2), self.canvasTool(self.canvasTool, 6),
])
toolBars.append(
[self.canvasColour(self.canvasColour, 0), self.canvasColour(self.canvasColour, 1), self.canvasColour(self.canvasColour, 2), self.canvasColour(self.canvasColour, 3),
self.canvasColour(self.canvasColour, 4), self.canvasColour(self.canvasColour, 5), self.canvasColour(self.canvasColour, 6), self.canvasColour(self.canvasColour, 7),
self.canvasColour(self.canvasColour, 8), self.canvasColour(self.canvasColour, 9), self.canvasColour(self.canvasColour, 10), self.canvasColour(self.canvasColour, 11),
self.canvasColour(self.canvasColour, 12), self.canvasColour(self.canvasColour, 13), self.canvasColour(self.canvasColour, 14), self.canvasColour(self.canvasColour, 15),
self.canvasColourAlpha(self.canvasColourAlpha, 0), self.canvasColoursFlip, NID_TOOLBAR_HSEP,
self.canvasBrushSize(self.canvasBrushSize, 1, True), self.canvasBrushSize(self.canvasBrushSize, 1, False), self.canvasBrushSize(self.canvasBrushSize, 0, True), self.canvasBrushSize(self.canvasBrushSize, 0, False), NID_TOOLBAR_HSEP,
self.canvasBrushSize(self.canvasBrushSize, 2, True), self.canvasBrushSize(self.canvasBrushSize, 2, False),
])
toolBars.append(
[self.canvasColourBackground(self.canvasColourBackground, 0), self.canvasColourBackground(self.canvasColourBackground, 1), self.canvasColourBackground(self.canvasColourBackground, 2), self.canvasColourBackground(self.canvasColourBackground, 3),
self.canvasColourBackground(self.canvasColourBackground, 4), self.canvasColourBackground(self.canvasColourBackground, 5), self.canvasColourBackground(self.canvasColourBackground, 6), self.canvasColourBackground(self.canvasColourBackground, 7),
self.canvasColourBackground(self.canvasColourBackground, 8), self.canvasColourBackground(self.canvasColourBackground, 9), self.canvasColourBackground(self.canvasColourBackground, 10), self.canvasColourBackground(self.canvasColourBackground, 11),
self.canvasColourBackground(self.canvasColourBackground, 12), self.canvasColourBackground(self.canvasColourBackground, 13), self.canvasColourBackground(self.canvasColourBackground, 14), self.canvasColourBackground(self.canvasColourBackground, 15),
self.canvasColourAlphaBackground(self.canvasColourAlphaBackground, 0), self.canvasColoursFlip, NID_TOOLBAR_HSEP,
self.canvasCanvasSize(self.canvasCanvasSize, 1, True), self.canvasCanvasSize(self.canvasCanvasSize, 1, False), self.canvasCanvasSize(self.canvasCanvasSize, 0, True), self.canvasCanvasSize(self.canvasCanvasSize, 0, False), NID_TOOLBAR_HSEP,
self.canvasCanvasSize(self.canvasCanvasSize, 2, True), self.canvasCanvasSize(self.canvasCanvasSize, 2, False),
])
self.accels, self.menus, self.toolBars = accels, menus, toolBars
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0

View File

@ -4,7 +4,7 @@
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
#
from GuiFrame import GuiCommandDecorator, GuiCommandListDecorator, GuiSelectDecorator, NID_MENU_SEP
from GuiFrame import GuiCommandDecorator, GuiCommandListDecorator, GuiSelectDecorator
import wx
class RoarCanvasCommandsEdit():
@ -194,31 +194,10 @@ class RoarCanvasCommandsEdit():
@GuiCommandDecorator("Redo", "&Redo", ["", wx.ART_REDO], [wx.ACCEL_CTRL, ord("Y")], False)
def canvasRedo(self, event):
self.parentCanvas.undo(redo=True); self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel);
self.parentCanvas.undo(redo=True); self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.patchesUndoLevel);
@GuiCommandDecorator("Undo", "&Undo", ["", wx.ART_UNDO], [wx.ACCEL_CTRL, ord("Z")], False)
def canvasUndo(self, event):
self.parentCanvas.undo(); self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel);
def __init__(self):
self.accels = (
(self.canvasColourBackground(self.canvasColourBackground, 0), self.canvasColourBackground(self.canvasColourBackground, 1), self.canvasColourBackground(self.canvasColourBackground, 2), self.canvasColourBackground(self.canvasColourBackground, 3), self.canvasColourBackground(self.canvasColourBackground, 4), self.canvasColourBackground(self.canvasColourBackground, 5), self.canvasColourBackground(self.canvasColourBackground, 6), self.canvasColourBackground(self.canvasColourBackground, 7), self.canvasColourBackground(self.canvasColourBackground, 8), self.canvasColourBackground(self.canvasColourBackground, 9), self.canvasColourBackground(self.canvasColourBackground, 10), self.canvasColourBackground(self.canvasColourBackground, 11), self.canvasColourBackground(self.canvasColourBackground, 12), self.canvasColourBackground(self.canvasColourBackground, 13), self.canvasColourBackground(self.canvasColourBackground, 14), self.canvasColourBackground(self.canvasColourBackground, 15), self.canvasColourAlphaBackground(self.canvasColourAlphaBackground, 0),),
)
self.menus = (
("&Edit",
self.canvasUndo, self.canvasRedo, NID_MENU_SEP,
self.canvasCut, self.canvasCopy, self.canvasPaste,
self.canvasDelete, NID_MENU_SEP,
("Brush size", self.canvasBrushSize(self.canvasBrushSize, 0, True), self.canvasBrushSize(self.canvasBrushSize, 0, False), self.canvasBrushSize(self.canvasBrushSize, 1, True), self.canvasBrushSize(self.canvasBrushSize, 1, False), NID_MENU_SEP,
self.canvasBrushSize(self.canvasBrushSize, 2, True), self.canvasBrushSize(self.canvasBrushSize, 2, False),),
("Canvas size", self.canvasCanvasSize(self.canvasCanvasSize, 1, True), self.canvasCanvasSize(self.canvasCanvasSize, 1, False), self.canvasCanvasSize(self.canvasCanvasSize, 0, True), self.canvasCanvasSize(self.canvasCanvasSize, 0, False), NID_MENU_SEP,
self.canvasCanvasSize(self.canvasCanvasSize, 2, True), self.canvasCanvasSize(self.canvasCanvasSize, 2, False),),
self.canvasColoursFlip,
NID_MENU_SEP,
self.canvasBrush(self.canvasBrush, 0), NID_MENU_SEP,
self.canvasAssetsWindowHide, self.canvasAssetsWindowShow,
),
)
self.toolBars = ()
self.parentCanvas.undo(); self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.patchesUndoLevel);
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0

View File

@ -5,14 +5,12 @@
#
try:
from ImgurApiKey import ImgurApiKey
haveImgurApiKey = True
from ImgurApiKey import ImgurApiKey; haveImgurApiKey = True;
except ImportError:
haveImgurApiKey = False
try:
import base64, json, requests, urllib.request
haveUrllib = True
import base64, json, requests, urllib.request; haveUrllib = True;
except ImportError:
haveUrllib = False
@ -35,8 +33,7 @@ class RoarCanvasCommandsFile():
self.canvasPathName = None
self.parentCanvas._snapshotsReset()
self.update(dirty=self.parentCanvas.dirty, pathName=self.canvasPathName, undoLevel=-1)
self.parentCanvas.canvas.journal.resetCursor()
self.parentCanvas.canvas.journal.resetUndo()
self.parentCanvas.canvas.resetCursor(); self.parentCanvas.canvas.resetUndo();
except FileNotFoundError as e:
rc, error, newMap, newPathName, newSize = False, str(e), None, None, None
if not rc:
@ -52,28 +49,10 @@ class RoarCanvasCommandsFile():
if dialog.ShowModal() == wx.ID_CANCEL:
return False, None
elif self._promptSaveChanges():
pathName = dialog.GetPath(); self.lastDir = os.path.dirname(pathName); self._storeLastDir(self.lastDir);
pathName = dialog.GetPath(); self.lastDir = os.path.dirname(pathName); self._recentDirSave(self.lastDir);
return self._import(f, newDirty, pathName, emptyPathName=emptyPathName)
return False, None
def _loadLastDir(self):
localConfFileName = getLocalConfPathName("RecentDir.txt")
if os.path.exists(localConfFileName):
with open(localConfFileName, "r", encoding="utf-8") as inFile:
self.lastDir = inFile.read().rstrip("\r\n")
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)
def _popSnapshot(self, pathName):
if pathName in self.snapshots.keys():
self.canvasRestore.attrDict["menu"].Delete(self.snapshots[pathName]["menuItemId"])
del self.snapshots[pathName]
def _promptSaveChanges(self):
if self.parentCanvas.dirty:
message = "Do you want to save changes to {}?".format(self.canvasPathName if self.canvasPathName != None else "(Untitled)")
@ -90,7 +69,25 @@ class RoarCanvasCommandsFile():
else:
return True
def _pushRecent(self, pathName, serialise=True):
def _recentDirLoad(self):
localConfFileName = getLocalConfPathName("RecentDir.txt")
if os.path.exists(localConfFileName):
with open(localConfFileName, "r", encoding="utf-8") as inFile:
self.lastDir = inFile.read().rstrip("\r\n")
def _recentDirSave(self, pathName):
localConfFileName = getLocalConfPathName("RecentDir.txt")
with open(localConfFileName, "w", encoding="utf-8") as outFile:
print(pathName, file=outFile)
def _recentLoad(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._recentPush(lastFile.rstrip("\r\n"), False)
def _recentPush(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
@ -102,9 +99,20 @@ class RoarCanvasCommandsFile():
self.parentFrame.Bind(wx.EVT_MENU, lambda event: self.canvasOpenRecent(event, pathName), menuItemWindow)
self.lastFiles += [{"menuItemId":menuItemId, "menuItemWindow":menuItemWindow, "pathName":pathName}]
if serialise:
self._saveRecent()
self._recentSave()
def _pushSnapshot(self, pathName):
def _recentSave(self):
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)
def _snapshotsPop(self, pathName):
if pathName in self.snapshots.keys():
self.canvasRestore.attrDict["menu"].Delete(self.snapshots[pathName]["menuItemId"])
del self.snapshots[pathName]
def _snapshotsPush(self, pathName):
menuItemId = wx.NewId()
if not (pathName in self.snapshots.keys()):
label = datetime.datetime.fromtimestamp(os.stat(pathName)[stat.ST_MTIME]).strftime("%c")
@ -113,23 +121,12 @@ class RoarCanvasCommandsFile():
self.parentFrame.Bind(wx.EVT_MENU, lambda event: self.canvasRestore(event, pathName), menuItemWindow)
self.snapshots[pathName] = {"menuItemId":menuItemId, "menuItemWindow":menuItemWindow}
def _resetSnapshots(self):
def _snapshotsReset(self):
for pathName in list(self.snapshots.keys()):
self._popSnapshot(pathName)
self._snapshotsPop(pathName)
if self.canvasRestore.attrDict["id"] in self.parentFrame.menuItemsById:
self.parentFrame.menuItemsById[self.canvasRestore.attrDict["id"]].Enable(False)
def _saveRecent(self):
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)
def _storeLastDir(self, pathName):
localConfFileName = getLocalConfPathName("RecentDir.txt")
with open(localConfFileName, "w", encoding="utf-8") as outFile:
print(pathName, file=outFile)
@GuiCommandDecorator("Clear list", "&Clear list", None, None, False)
def canvasClearRecent(self, event):
if self.lastFiles != None:
@ -155,7 +152,7 @@ class RoarCanvasCommandsFile():
if dialog.ShowModal() == wx.ID_CANCEL:
return False
else:
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName); self._storeLastDir(self.lastDir);
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName); self._recentDirSave(self.lastDir);
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
with open(outPathName, "w", encoding="utf-8") as outFile:
self.parentCanvas.canvas.exportStore.exportAnsiFile(self.parentCanvas.canvas.map, self.parentCanvas.canvas.size, outFile)
@ -170,30 +167,20 @@ class RoarCanvasCommandsFile():
if dialog.ShowModal() == wx.ID_CANCEL:
return False
else:
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName); self._storeLastDir(self.lastDir);
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName); self._recentDirSave(self.lastDir);
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas)
self.parentCanvas.backend.drawCursorMaskWithJournal(self.parentCanvas.canvas, self.parentCanvas.canvas.journal, eventDc, reset=False)
self.parentCanvas.cursorHide()
self.parentCanvas.canvas.exportStore.exportBitmapToPngFile(self.parentCanvas.backend.canvasBitmap, outPathName, wx.BITMAP_TYPE_PNG)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
cursorPatches = self.parentCanvas.canvas.journal.popCursor(reset=False)
if len(cursorPatches) > 0:
self.parentCanvas.backend.drawPatches(self.parentCanvas.canvas, eventDc, cursorPatches, isCursor=True)
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.parentCanvas.cursorShow()
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
return True
@GuiCommandDecorator("Export to Imgur...", "Export to I&mgur...", None, None, haveImgurApiKey and haveUrllib)
def canvasExportImgur(self, event):
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas)
self.parentCanvas.backend.drawCursorMaskWithJournal(self.parentCanvas.canvas, self.parentCanvas.canvas.journal, eventDc, reset=False)
self.parentCanvas.cursorHide()
rc, status, result = self.parentCanvas.canvas.exportStore.exportBitmapToImgur(self.imgurApiKey, self.parentCanvas.backend.canvasBitmap, "", "", wx.BITMAP_TYPE_PNG)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
cursorPatches = self.parentCanvas.canvas.journal.popCursor(reset=False)
if len(cursorPatches) > 0:
self.parentCanvas.backend.drawPatches(self.parentCanvas.canvas, eventDc, cursorPatches, isCursor=True)
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.parentCanvas.cursorShow()
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
if rc:
if not wx.TheClipboard.IsOpened():
@ -261,7 +248,7 @@ class RoarCanvasCommandsFile():
nonlocal newCanvasSize
if newCanvasSize == None:
newCanvasSize = list(self.parentCanvas.canvas.size)
newMap = [[[-1, -1, 0, " "] for x in range(newCanvasSize[0])] for y in range(newCanvasSize[1])]
newMap = [[[*self.parentCanvas.brushColours, 0, " "] for x in range(newCanvasSize[0])] for y in range(newCanvasSize[1])]
return (True, "", newMap, None, newCanvasSize)
if self._promptSaveChanges():
self._import(canvasImportEmpty, False, None)
@ -273,7 +260,7 @@ class RoarCanvasCommandsFile():
return (rc, error, self.parentCanvas.canvas.importStore.outMap, pathName, self.parentCanvas.canvas.importStore.inSize)
rc, newPathName = self._importFile(canvasImportmIRC, False, "mIRC art files (*.txt)|*.txt|All Files (*.*)|*.*")
if rc:
self._pushRecent(newPathName)
self._recentPush(newPathName)
@GuiSubMenuDecorator("Open Recent", "Open &Recent", None, None, False)
def canvasOpenRecent(self, event, pathName=None):
@ -285,7 +272,7 @@ class RoarCanvasCommandsFile():
if not rc:
numLastFile = [i for i in range(len(self.lastFiles)) if self.lastFiles[i]["pathName"] == pathName][0]
self.canvasOpenRecent.attrDict["menu"].Delete(self.lastFiles[numLastFile]["menuItemId"]); del self.lastFiles[numLastFile];
self._saveRecent()
self._recentSave()
@GuiSubMenuDecorator("Restore Snapshot", "Res&tore Snapshot", None, None, False)
def canvasRestore(self, event, pathName=None):
@ -328,27 +315,16 @@ class RoarCanvasCommandsFile():
if dialog.ShowModal() == wx.ID_CANCEL:
return False
else:
self.canvasPathName = dialog.GetPath(); self.lastDir = os.path.dirname(self.canvasPathName); self._storeLastDir(self.lastDir);
self.canvasPathName = dialog.GetPath(); self.lastDir = os.path.dirname(self.canvasPathName); self._recentDirSave(self.lastDir);
if self.canvasSave(event, newDirty=False):
self.update(pathName=self.canvasPathName)
self._pushRecent(self.canvasPathName)
self._recentPush(self.canvasPathName)
return True
else:
return False
def __init__(self):
self.imgurApiKey, self.lastFiles, self.lastDir, self.snapshots = ImgurApiKey.imgurApiKey if haveImgurApiKey else None, [], None, {}
self.accels = ()
self.menus = (
("&File",
self.canvasNew, self.canvasOpen, self.canvasOpenRecent, self.canvasRestore, self.canvasSave, self.canvasSaveAs, NID_MENU_SEP,
("&Export...", self.canvasExportAsAnsi, self.canvasExportToClipboard, self.canvasExportImgur, self.canvasExportPastebin, self.canvasExportAsPng,),
("&Import...", self.canvasImportAnsi, self.canvasImportFromClipboard, self.canvasImportSauce,),
NID_MENU_SEP,
self.canvasExit,
),
)
self.toolBars = ()
self.exiting = False
self.exiting, self.lastFiles, self.lastDir, self.snapshots = False, [], None, {}
self.imgurApiKey = ImgurApiKey.imgurApiKey if haveImgurApiKey else None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0

View File

@ -4,7 +4,7 @@
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
#
from GuiFrame import GuiCommandDecorator, NID_MENU_SEP
from GuiFrame import GuiCommandDecorator
from RoarWindowAbout import RoarWindowAbout
from RoarWindowMelp import RoarWindowMelp
import webbrowser, wx
@ -26,8 +26,4 @@ class RoarCanvasCommandsHelp():
def canvasVisitGitHub(self, event):
webbrowser.open("https://www.github.com/lalbornoz/roar")
def __init__(self):
self.accels = ()
self.menus, self.toolBars = (("&Help", self.canvasMelp, NID_MENU_SEP, self.canvasNewIssueGitHub, self.canvasVisitGitHub, NID_MENU_SEP, self.canvasAbout,),), ()
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0

View File

@ -4,14 +4,13 @@
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
#
from GuiFrame import GuiCommandListDecorator
from OperatorFlipHorizontal import OperatorFlipHorizontal
from OperatorFlipVertical import OperatorFlipVertical
from OperatorInvert import OperatorInvert
from OperatorRotate import OperatorRotate
from OperatorTile import OperatorTile
from GuiFrame import GuiCommandListDecorator
from ToolObject import ToolObject
import copy, wx
class RoarCanvasCommandsOperators():
@GuiCommandListDecorator(0, "Flip", "&Flip", None, None, None)
@ -28,13 +27,6 @@ class RoarCanvasCommandsOperators():
return canvasOperator_
def __init__(self):
self.accels = ()
self.menus = (
("&Operators",
self.canvasOperator(self.canvasOperator, 0), self.canvasOperator(self.canvasOperator, 1), self.canvasOperator(self.canvasOperator, 2), self.canvasOperator(self.canvasOperator, 3), self.canvasOperator(self.canvasOperator, 4),
),
)
self.toolBars = ()
self.currentOperator, self.operatorState = None, None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0

View File

@ -27,40 +27,21 @@ class RoarCanvasCommandsTools():
@GuiSelectDecorator(8, "Text", "&Text", ["toolText.png"], [wx.MOD_NONE, wx.WXK_F7], False)
def canvasTool(self, f, idx):
def canvasTool_(event):
if (self.currentTool.__class__ == ToolObject) \
and (self.currentTool.toolState > self.currentTool.TS_NONE) \
if (self.currentTool.__class__ == ToolObject) \
and (self.currentTool.toolState > self.currentTool.TS_NONE) \
and self.currentTool.external:
self.parentCanvas.dropTarget.done()
self.lastTool, self.currentTool = self.currentTool, [ToolCircle, None, ToolErase, ToolFill, ToolLine, ToolObject, ToolPickColour, ToolRect, ToolText][idx]
if self.currentTool != None:
self.currentTool = self.currentTool()
self.currentOperator, self.operatorState = None, None
self.parentFrame.menuItemsById[self.canvasTool.attrList[idx]["id"]].Check(True)
if self.currentTool.__class__ == ToolObject:
self.parentFrame.menuItemsById[self.canvasOperator.attrList[4]["id"]].Enable(True)
else:
self.parentFrame.menuItemsById[self.canvasOperator.attrList[4]["id"]].Enable(False)
toolBar = self.parentFrame.toolBarItemsById[self.canvasTool.attrList[idx]["id"]][0]
toolBar.ToggleTool(self.canvasTool.attrList[idx]["id"], True); toolBar.Refresh();
if self.currentTool != None:
self.update(operator=None, toolName=self.currentTool.name)
else:
self.update(operator=None, toolName="Cursor")
viewRect = self.parentCanvas.GetViewStart()
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas, viewRect)
self.parentCanvas.applyTool(eventDc, True, None, None, None, self.parentCanvas.brushPos, False, False, False, self.currentTool, viewRect, force=True)
self.update(currentTool=self.currentTool, currentToolIdx=idx, operator=None)
self.parentCanvas.applyTool(None, True, None, None, None, self.parentCanvas.brushPos, False, False, False, self.currentTool, None, force=True)
setattr(canvasTool_, "attrDict", f.attrList[idx])
setattr(canvasTool_, "isSelect", True)
return canvasTool_
def __init__(self):
self.accels = ()
self.menus = (
("&Tools",
self.canvasTool(self.canvasTool, 1), self.canvasTool(self.canvasTool, 7), self.canvasTool(self.canvasTool, 0), self.canvasTool(self.canvasTool, 3), self.canvasTool(self.canvasTool, 4), self.canvasTool(self.canvasTool, 8), self.canvasTool(self.canvasTool, 5), self.canvasTool(self.canvasTool, 2), self.canvasTool(self.canvasTool, 6),
),
)
self.toolBars = ()
self.currentTool, self.lastTool = None, None
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0

View File

@ -9,7 +9,7 @@ from Rtl import natural_sort
from RtlPlatform import getLocalConfPathName
from ToolObject import ToolObject
from ToolText import ToolText
import copy, hashlib, json, os, re, time, wx, sys
import copy, hashlib, json, os, pdb, re, time, wx, sys
class RoarCanvasWindowDropTarget(wx.TextDropTarget):
def done(self):
@ -27,8 +27,8 @@ class RoarCanvasWindowDropTarget(wx.TextDropTarget):
viewRect = self.parent.GetViewStart(); mapPoint = [m + n for m, n in zip((mapX, mapY), viewRect)];
self.parent.commands.lastTool, self.parent.commands.currentTool = self.parent.commands.currentTool, ToolObject()
self.parent.commands.currentTool.setRegion(self.parent.canvas, mapPoint, dropMap, dropSize, external=True)
self.parent.parentFrame.menuItemsById[self.parent.commands.canvasOperator.attrList[4]["id"]].Enable(True)
self.parent.commands.update(toolName=self.parent.commands.currentTool.name)
self.parent.parent.menuItemsById[self.parent.commands.canvasOperator.attrList[4]["id"]].Enable(True)
self.parent.commands.update(currentTool=self.parent.commands.currentTool, currentToolIdx=5)
eventDc = self.parent.backend.getDeviceContext(self.parent.GetClientSize(), self.parent, viewRect)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
self.parent.applyTool(eventDc, True, None, None, None, self.parent.brushPos, False, False, False, self.parent.commands.currentTool, viewRect)
@ -43,45 +43,34 @@ class RoarCanvasWindowDropTarget(wx.TextDropTarget):
super().__init__(); self.inProgress, self.parent = False, parent;
class RoarCanvasWindow(GuiWindow):
def _applyPatches(self, eventDc, patches, patchesCursor, rc):
def _applyPatches(self, eventDc, patches, patchesCursor, rc, commitUndo=True, dirty=True, eventDcResetOrigin=True, hideCursor=True):
if rc:
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
if ((patches != None) and (len(patches) > 0)) \
or ((patchesCursor != None) and (len(patchesCursor) > 0)):
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc)
if eventDc == None:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
if eventDcResetOrigin:
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
if hideCursor and (((patches != None) and (len(patches) > 0)) or ((patchesCursor != None) and (len(patchesCursor) > 0))):
self.cursorHide(eventDc, False, True)
if (patches != None) and (len(patches) > 0):
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
self.dirty = True if not self.dirty else self.dirty;
self.canvas.journal.begin()
if dirty and not self.dirty:
self.dirty = True
if commitUndo:
self.canvas.begin()
for patch in patches if patches != None else []:
self.canvas.applyPatch(patch, commitUndo=True)
self.canvas.journal.end()
if patchesCursor != None:
self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
if len(patchesCursor) > 0:
self.canvas.journal.pushCursor(patchesCursor)
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.commands.update(dirty=self.dirty, cellPos=self.brushPos, undoLevel=self.canvas.journal.patchesUndoLevel)
def _eraseBackground(self, eventDc):
viewRect = self.GetViewStart()
canvasSize, panelSize = [a * b for a, b in zip(self.backend.canvasSize, self.backend.cellSize)], self.GetSize()
if viewRect != (0, 0):
viewRect = [a * b for a, b in zip(self.backend.cellSize, viewRect)]
canvasSize = [a - b for a, b in zip(canvasSize, viewRect)]
rectangles, pens, brushes = [], [], []
if panelSize[0] > canvasSize[0]:
brushes += [self.bgBrush]; pens += [self.bgPen];
rectangles += [[canvasSize[0], 0, panelSize[0] - canvasSize[0], panelSize[1]]]
if panelSize[1] > canvasSize[1]:
brushes += [self.bgBrush]; pens += [self.bgPen];
rectangles += [[0, canvasSize[1], panelSize[0], panelSize[1] - canvasSize[1]]]
if len(rectangles) > 0:
eventDc.DrawRectangleList(rectangles, pens, brushes)
self.canvas.applyPatch(patch, commitUndo=commitUndo)
if commitUndo:
self.canvas.end()
if hideCursor and (patchesCursor != None):
self.cursorShow(eventDc, False, patchesCursor=patchesCursor)
if eventDcResetOrigin:
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.commands.update(cellPos=self.brushPos, dirty=self.dirty, undoLevel=self.canvas.patchesUndoLevel)
return eventDc
def _snapshotsReset(self):
self._snapshotFiles, self._snapshotsUpdateLast = [], time.time()
self.commands._resetSnapshots()
self.commands._snapshotsReset()
if self.commands.canvasPathName != None:
canvasPathName = os.path.abspath(self.commands.canvasPathName)
canvasFileName = os.path.basename(canvasPathName)
@ -90,7 +79,7 @@ class RoarCanvasWindow(GuiWindow):
if os.path.exists(self._snapshotsDirName):
for snapshotFile in natural_sort([f for f in os.listdir(self._snapshotsDirName) \
if (re.match(r'snapshot\d+\.txt$', f)) and os.path.isfile(os.path.join(self._snapshotsDirName, f))]):
self.commands._pushSnapshot(os.path.join(self._snapshotsDirName, snapshotFile))
self.commands._snapshotsPush(os.path.join(self._snapshotsDirName, snapshotFile))
else:
self._snapshotsDirName = None
@ -115,14 +104,30 @@ class RoarCanvasWindow(GuiWindow):
self.SetCursor(wx.Cursor(wx.NullCursor))
self.commands.update(snapshotStatus=False); self._snapshotsUpdateLast = time.time();
self._snapshotFiles += [os.path.basename(snapshotPathName)];
self.commands._pushSnapshot(snapshotPathName)
self.commands._snapshotsPush(snapshotPathName)
if len(self._snapshotFiles) > 72:
for snapshotFile in self._snapshotFiles[:len(self._snapshotFiles) - 8]:
self.commands._popSnapshot(os.path.join(self._snapshotsDirName, snapshotFile))
self.commands._snapshotsPop(os.path.join(self._snapshotsDirName, snapshotFile))
os.remove(os.path.join(self._snapshotsDirName, snapshotFile)); snapshotsCount -= 1;
except:
print("Exception during _snapshotsUpdate(): {}".format(sys.exc_info()[1]))
def _windowEraseBackground(self, eventDc):
viewRect = self.GetViewStart()
canvasSize, panelSize = [a * b for a, b in zip(self.backend.canvasSize, self.backend.cellSize)], self.GetSize()
if viewRect != (0, 0):
viewRect = [a * b for a, b in zip(self.backend.cellSize, viewRect)]
canvasSize = [a - b for a, b in zip(canvasSize, viewRect)]
rectangles, pens, brushes = [], [], []
if panelSize[0] > canvasSize[0]:
brushes += [self.bgBrush]; pens += [self.bgPen];
rectangles += [[canvasSize[0], 0, panelSize[0] - canvasSize[0], panelSize[1]]]
if panelSize[1] > canvasSize[1]:
brushes += [self.bgBrush]; pens += [self.bgPen];
rectangles += [[0, canvasSize[1], panelSize[0], panelSize[1] - canvasSize[1]]]
if len(rectangles) > 0:
eventDc.DrawRectangleList(rectangles, pens, brushes)
def applyOperator(self, currentTool, mapPoint, mouseLeftDown, mousePoint, operator, viewRect):
eventDc, patches, patchesCursor, rc = self.backend.getDeviceContext(self.GetClientSize(), self), None, None, True
if (currentTool.__class__ == ToolObject) and (currentTool.toolState >= currentTool.TS_SELECT):
@ -157,6 +162,10 @@ class RoarCanvasWindow(GuiWindow):
def applyTool(self, eventDc, eventMouse, keyChar, keyCode, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, tool, viewRect, force=False):
patches, patchesCursor, rc = None, None, False
if viewRect == None:
viewRect = self.GetViewStart()
if eventDc == None:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect)
if eventMouse:
self.lastCellState = None if force else self.lastCellState
if ((mapPoint[0] < self.canvas.size[0]) and (mapPoint[1] < self.canvas.size[1])) \
@ -173,75 +182,80 @@ class RoarCanvasWindow(GuiWindow):
elif mapPoint != None:
rc, patches, patchesCursor = True, None, [[*mapPoint, self.brushColours[0], self.brushColours[0], 0, " "]]
if rc:
for patch in patches if patches != None else []:
if ((patch[2] == -1) and (patch[3] == -1)) \
and (patch[0] < self.canvas.size[0]) \
and (patch[1] < self.canvas.size[1]):
patch[2:] = self.canvas.map[patch[1]][patch[0]]
self._applyPatches(eventDc, patches, patchesCursor, rc)
if tool.__class__ == ToolObject:
if tool.toolState > tool.TS_NONE:
self.commands.update(undoInhibit=True)
elif tool.toolState == tool.TS_NONE:
if tool.external:
self.dropTarget.done(); self.commands.currentTool, self.commands.lastTool = self.commands.lastTool, self.commands.currentTool;
newToolName = "Cursor" if self.commands.currentTool == None else self.commands.currentTool.name
self.commands.update(toolName=newToolName, undoInhibit=False)
else:
self.commands.update(undoInhibit=False)
if (tool.__class__ == ToolObject) and (tool.external, tool.toolState) == (True, tool.TS_NONE):
self.dropTarget.done(); self.commands.currentTool, self.commands.lastTool = self.commands.lastTool, self.commands.currentTool;
self.commands.update(currentTool=self.commands.currentTool)
if (patches != None) and (len(patches) > 0):
self._snapshotsUpdate()
return rc
def onKeyboardInput(self, event):
keyCode, keyModifiers = event.GetKeyCode(), event.GetModifiers()
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
if (keyCode == wx.WXK_PAUSE) \
and (keyModifiers == wx.MOD_SHIFT):
import pdb; pdb.set_trace()
elif keyCode in (wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_UP):
if keyCode == wx.WXK_DOWN:
if self.brushPos[1] < (self.canvas.size[1] - 1):
self.brushPos = [self.brushPos[0], self.brushPos[1] + 1]
else:
self.brushPos = [self.brushPos[0], 0]
elif keyCode == wx.WXK_LEFT:
if self.brushPos[0] > 0:
self.brushPos = [self.brushPos[0] - 1, self.brushPos[1]]
else:
self.brushPos = [self.canvas.size[0] - 1, self.brushPos[1]]
elif keyCode == wx.WXK_RIGHT:
if self.brushPos[0] < (self.canvas.size[0] - 1):
self.brushPos = [self.brushPos[0] + 1, self.brushPos[1]]
else:
self.brushPos = [0, self.brushPos[1]]
elif keyCode == wx.WXK_UP:
if self.brushPos[1] > 0:
self.brushPos = [self.brushPos[0], self.brushPos[1] - 1]
else:
self.brushPos = [self.brushPos[0], self.canvas.size[1] - 1]
self.commands.update(cellPos=self.brushPos)
self.applyTool(eventDc, True, None, None, None, self.brushPos, False, False, False, self.commands.currentTool, viewRect)
elif (chr(event.GetUnicodeKey()) == " ") \
and (self.commands.currentTool.__class__ != ToolText):
if not self.applyTool(eventDc, True, None, None, event.GetModifiers(), self.brushPos, False, True, False, self.commands.currentTool, viewRect):
event.Skip()
else:
if self.brushPos[0] < (self.canvas.size[0] - 1):
self.brushPos = [self.brushPos[0] + 1, self.brushPos[1]]
else:
self.brushPos = [0, self.brushPos[1]]
self.commands.update(cellPos=self.brushPos)
self.applyTool(eventDc, True, None, None, None, self.brushPos, False, False, False, self.commands.currentTool, viewRect)
else:
if not self.applyTool(eventDc, False, chr(event.GetUnicodeKey()), keyCode, keyModifiers, None, None, None, None, self.commands.currentTool, viewRect):
event.Skip()
def cursorHide(self, eventDc=None, eventDcResetOrigin=True, reset=False):
if eventDc == None:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
if eventDcResetOrigin:
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
patchesCursor = self.canvas.popCursor(reset=reset); patchesCursor_ = [];
for cursorCell in [p[:2] for p in patchesCursor]:
if (cursorCell[0] < self.canvas.size[0]) and (cursorCell[1] < self.canvas.size[1]):
patchesCursor_ += [[*cursorCell, *self.canvas.map[cursorCell[1]][cursorCell[0]]]]
if len(patchesCursor_) > 0:
self.backend.drawPatches(self.canvas, eventDc, patchesCursor_, False)
if eventDcResetOrigin:
eventDc.SetDeviceOrigin(*eventDcOrigin)
return eventDc
def cursorShow(self, eventDc=None, eventDcResetOrigin=True, patchesCursor=None):
if eventDc == None:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
if eventDcResetOrigin:
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
if patchesCursor == None:
patchesCursor = self.canvas.popCursor(reset=False)
elif len(patchesCursor) > 0:
self.canvas.pushCursor(patchesCursor)
if (patchesCursor != None) and (len(patchesCursor) > 0):
self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
if eventDcResetOrigin:
eventDc.SetDeviceOrigin(*eventDcOrigin)
def onEnterWindow(self, event):
self.lastCellState = None
def onErase(self, event):
pass
def onKeyboardInput(self, event):
keyCode, keyModifiers = event.GetKeyCode(), event.GetModifiers()
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
if (keyCode, keyModifiers,) == (wx.WXK_PAUSE, wx.MOD_SHIFT,):
pdb.set_trace()
elif keyCode in (wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_UP):
if keyCode == wx.WXK_DOWN:
self.brushPos[1] = (self.brushPos[1] + 1) % self.canvas.size[1]
elif keyCode == wx.WXK_LEFT:
self.brushPos[0] = (self.brushPos[0] - 1) if (self.brushPos[0] > 0) else (self.canvas.size[0] - 1)
elif keyCode == wx.WXK_RIGHT:
self.brushPos[0] = (self.brushPos[0] + 1) % self.canvas.size[0]
elif keyCode == wx.WXK_UP:
self.brushPos[1] = (self.brushPos[1] - 1) if (self.brushPos[1] > 0) else (self.canvas.size[1] - 1)
self.commands.update(cellPos=self.brushPos)
self.applyTool(eventDc, True, None, None, None, self.brushPos, False, False, False, self.commands.currentTool, viewRect)
elif (chr(event.GetUnicodeKey()) == " ") and (self.commands.currentTool.__class__ != ToolText):
if not self.applyTool(eventDc, True, None, None, event.GetModifiers(), self.brushPos, False, True, False, self.commands.currentTool, viewRect):
event.Skip()
else:
self.brushPos[0] = (self.brushPos[0] + 1) if (self.brushPos[0] < (self.canvas.size[0] - 1)) else 0
self.commands.update(cellPos=self.brushPos)
self.applyTool(eventDc, True, None, None, None, self.brushPos, False, False, False, self.commands.currentTool, viewRect)
elif not self.applyTool(eventDc, False, chr(event.GetUnicodeKey()), keyCode, keyModifiers, None, None, None, None, self.commands.currentTool, viewRect):
event.Skip()
def onLeaveWindow(self, event):
if False:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc)
self.cursorHide()
self.lastCellState = None
def onMouseInput(self, event):
@ -266,18 +280,19 @@ class RoarCanvasWindow(GuiWindow):
newFontSize = self.backend.fontSize + delta
if newFontSize > 0:
self.Freeze()
self.backend.fontSize = newFontSize
self.backend.resize(self.canvas.size); self.scrollStep = self.backend.cellSize;
self.backend.fontSize = newFontSize; self.backend.resize(self.canvas.size); self.scrollStep = self.backend.cellSize;
super().resize([a * b for a, b in zip(self.canvas.size, self.backend.cellSize)])
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
patches = []
for numRow in range(self.canvas.size[1]):
for numCol in range(len(self.canvas.map[numRow])):
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
self.cursorHide(eventDc, False, False)
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
self.cursorShow(eventDc, False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
self.Thaw(); del eventDc; self._eraseBackground(wx.ClientDC(self));
self.Thaw(); self._windowEraseBackground(wx.ClientDC(self));
elif modifiers == (wx.MOD_CONTROL | wx.MOD_SHIFT):
self.commands.canvasCanvasSize(self.commands.canvasCanvasSize, 2, 1 if delta > 0 else 0)(None)
elif modifiers == wx.MOD_CONTROL:
@ -289,7 +304,7 @@ class RoarCanvasWindow(GuiWindow):
viewRect = self.GetViewStart()
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
self.backend.onPaint(self.GetClientSize(), self, viewRect)
del eventDc; self._eraseBackground(wx.PaintDC(self));
del eventDc; self._windowEraseBackground(wx.PaintDC(self));
def resize(self, newSize, commitUndo=True, dirty=True, freeze=True):
if freeze:
@ -297,76 +312,50 @@ class RoarCanvasWindow(GuiWindow):
viewRect = self.GetViewStart()
oldSize = [0, 0] if self.canvas.map == None else self.canvas.size
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
if self.canvas.resize(newSize, commitUndo):
rc, newCells = self.canvas.resize(self.brushColours, newSize, commitUndo)
if rc:
self.backend.resize(newSize); self.scrollStep = self.backend.cellSize;
super().resize([a * b for a, b in zip(newSize, self.backend.cellSize)])
self._applyPatches(None, newCells, None, True, commitUndo=False, dirty=True, hideCursor=False)
self.Scroll(*viewRect); self.dirty = dirty;
self.commands.update(dirty=self.dirty, size=newSize, undoLevel=self.canvas.journal.patchesUndoLevel)
self.commands.update(dirty=self.dirty, size=newSize, undoLevel=self.canvas.patchesUndoLevel)
if commitUndo:
self._snapshotsUpdate()
if freeze:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
cursorPatches = self.canvas.journal.popCursor(reset=False)
if len(cursorPatches) > 0:
self.backend.drawPatches(self.canvas, eventDc, cursorPatches, isCursor=True)
eventDc.SetDeviceOrigin(*eventDcOrigin)
del eventDc; self.Thaw(); self._eraseBackground(wx.ClientDC(self));
self.cursorShow(); self.Thaw(); self._windowEraseBackground(wx.ClientDC(self));
def undo(self, redo=False):
freezeFlag = False
deltaPatches = self.canvas.journal.popUndo() if not redo else self.canvas.journal.popRedo()
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
patchesCursor = self.backend.drawCursorMaskWithJournal(self.canvas, self.canvas.journal, eventDc, reset=False)
patches = []
for patch in deltaPatches:
if patch == None:
continue
elif patch[0] == "resize":
freezeFlag, patches, patchesDelta = False, [], self.canvas.popUndo(redo)
for patch in [p for p in patchesDelta if p != None]:
if patch[0] == "resize":
if not freezeFlag:
self.Freeze(); freezeFlag = True;
eventDc = None; self.resize(patch[1:], False, freeze=False);
self.resize(patch[1:], False, freeze=False)
else:
self.canvas._commitPatch(patch); patches += [patch];
if eventDc == None:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
if len(patchesCursor):
self.backend.drawPatches(self.canvas, eventDc, patchesCursor, isCursor=True)
eventDc.SetDeviceOrigin(*eventDcOrigin)
patches += [patch]
eventDc = self._applyPatches(None, patches, None, True, commitUndo=False, hideCursor=False)
self.cursorShow(eventDc, True, None)
if freezeFlag:
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
cursorPatches = self.canvas.journal.popCursor(reset=False)
if len(cursorPatches) > 0:
self.backend.drawPatches(self.canvas, eventDc, cursorPatches, isCursor=True)
eventDc.SetDeviceOrigin(*eventDcOrigin)
del eventDc; self.Thaw(); self._eraseBackground(wx.ClientDC(self));
self.Thaw(); self._windowEraseBackground(wx.ClientDC(self));
def update(self, newSize, commitUndo=True, newCanvas=None, dirty=True):
self.resize(newSize, commitUndo, dirty)
self.canvas.update(newSize, newCanvas)
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0); patches = [];
self.resize(newSize, commitUndo, dirty); self.canvas.update(newSize, newCanvas);
patches = []
for numRow in range(newSize[1]):
for numCol in range(newSize[0]):
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
self.backend.drawPatches(self.canvas, eventDc, patches, isCursor=False)
eventDc.SetDeviceOrigin(*eventDcOrigin)
self._applyPatches(None, patches, None, True, dirty=False)
def __init__(self, backend, canvas, commands, parent, pos, size):
super().__init__(parent, pos)
self.lastMouseState, self.size = [False, False, False], size
self.backend, self.canvas, self.commands, self.parentFrame = backend(self.size), canvas, commands(self, parent), parent
self.brushColours, self.brushPos, self.brushSize, self.dirty, self.lastCellState = [3, 9], [0, 0], [1, 1], False, None
self.popupEventDc = None
self.dropTarget = RoarCanvasWindowDropTarget(self)
self.SetDropTarget(self.dropTarget)
super().__init__(parent, pos); self.parent, self.size = parent, size;
self.backend, self.canvas, self.commands = backend(self.size), canvas, commands(self, parent)
self.bgBrush, self.bgPen = wx.Brush(self.GetBackgroundColour(), wx.BRUSHSTYLE_SOLID), wx.Pen(self.GetBackgroundColour(), 1)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.onErase)
self.Bind(wx.EVT_MOUSEWHEEL, self.onMouseWheel)
self.brushColours, self.brushPos, self.brushSize, = [3, -1], [0, 0], [1, 1]
self.dirty, self.lastCellState, self.lastMouseState = False, None, [False, False, False]
self.dropTarget, self.popupEventDc = RoarCanvasWindowDropTarget(self), None
for event, handler in ((wx.EVT_ERASE_BACKGROUND, lambda event: None,), (wx.EVT_MOUSEWHEEL, self.onMouseWheel,),):
self.Bind(event, handler)
self.SetDropTarget(self.dropTarget)
self._snapshotsReset()
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -14,6 +14,7 @@ class RoarWindowAbout(wx.Dialog):
def __init__(self, parent, minSize=(320, 300), title="About roar"):
super().__init__(parent, size=minSize, title=title)
self.panel, self.sizer, self.sizerV = wx.Panel(self), wx.FlexGridSizer(2, 2, 4, 4), wx.BoxSizer(wx.VERTICAL)
self.panel.SetBackgroundColour(wx.Colour(0, 0, 0)); self.panel.SetForegroundColour(wx.Colour(0, 187, 0));
logoPathNames = glob(os.path.join("assets", "images", "logo*.bmp"))
logoPathName = logoPathNames[random.randint(0, len(logoPathNames) - 1)]

View File

@ -13,6 +13,7 @@ class RoarWindowMelp(wx.Dialog):
def __init__(self, parent, minSize=(320, 300), title="melp?"):
super().__init__(parent, size=minSize, title=title)
self.panel, self.sizer = wx.Panel(self), wx.BoxSizer(wx.VERTICAL)
self.panel.SetBackgroundColour(wx.Colour(0, 0, 0)); self.panel.SetForegroundColour(wx.Colour(0, 187, 0));
with open(os.path.join("assets", "text", "melp.txt"), "r") as fileObject:
helpLabel = "".join(fileObject.readlines())

View File

@ -10,9 +10,9 @@ import re
def flatten(l):
return [li for l_ in l for li in l_]
def natural_sort(l):
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
def natural_sort(l):
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
return sorted(l, key=alphanum_key)
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120

View File

@ -34,12 +34,6 @@ class ToolCircle(Tool):
elif ((numRow > 0) and (cells[numRow][numCol][0] > cells[numRow - 1][-1][0])) \
or ((numRow < len(cells)) and (cells[numRow][numCol][0] > cells[numRow + 1][-1][0])):
patch = [*cells[numRow][numCol], brushColours[0], brushColours[0], 0, " "]
elif brushColours[1] == -1:
if (cells[numRow][numCol][0] < canvas.size[0]) \
and (cells[numRow][numCol][1] < canvas.size[1]):
patch = [cells[numRow][numCol][0], cells[numRow][numCol][1], *canvas.map[cells[numRow][numCol][1]][cells[numRow][numCol][0]]]
else:
patch = [*cells[numRow][numCol], brushColours[1], brushColours[1], 0, " "]
else:
patch = [*cells[numRow][numCol], brushColours[1], brushColours[1], 0, " "]
patches += [patch]

View File

@ -18,17 +18,9 @@ class ToolRect(Tool):
for brushCol in range((rect[2] - rect[0]) + 1):
if (brushCol in [0, (rect[2] - rect[0])]) or (brushRow in [0, (rect[3] - rect[1])]):
patchColours = [brushColours[0]] * 2
patch = [rect[0] + brushCol, rect[1] + brushRow, *patchColours, 0, " "]
elif brushColours[1] == -1:
if ((rect[0] + brushCol) < canvas.size[0]) \
and ((rect[1] + brushRow) < canvas.size[1]):
patch = [rect[0] + brushCol, rect[1] + brushRow, *canvas.map[rect[1] + brushRow][rect[0] + brushCol]]
else:
patch = [rect[0] + brushCol, rect[1] + brushRow, -1, -1, 0, " "]
else:
patchColours = [brushColours[1]] * 2
patch = [rect[0] + brushCol, rect[1] + brushRow, *patchColours, 0, " "]
patches += [patch]
patches += [[rect[0] + brushCol, rect[1] + brushRow, *patchColours, 0, " "]]
return patches
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):

View File

@ -70,12 +70,17 @@ class ToolText(Tool):
rc = True
elif keyCode == wx.WXK_CONTROL_V:
rc = True
if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)) \
and wx.TheClipboard.Open():
if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)) and wx.TheClipboard.Open():
inBuffer = wx.TextDataObject()
if wx.TheClipboard.GetData(inBuffer):
brushPosOriginX = brushPos[0]
for inBufferChar in list(inBuffer.GetText()):
if not re.match(self.arabicCombiningRegEx, inBufferChar):
if inBufferChar in set("\r\n"):
if brushPos[1] < (canvas.size[1] - 1):
brushPos[0], brushPos[1] = brushPosOriginX, brushPos[1] + 1
else:
brushPos[0], brushPos[1] = brushPosOriginX, 0
elif not re.match(self.arabicCombiningRegEx, inBufferChar):
rc_, patches_ = self._processKeyChar(brushColours, brushPos, canvas, inBufferChar, 0)
patches += patches_
rc = True if rc_ else rc

View File

@ -20,7 +20,7 @@ def main(*argv):
os.makedirs(localConfirName)
wxApp, roarClient = wx.App(False), RoarClient(None)
argv0, argv = argv[0], argv[1:]
roarClient.canvasPanel.commands._loadLastDir(); roarClient.canvasPanel.commands._loadRecent();
roarClient.canvasPanel.commands._recentDirLoad(); roarClient.canvasPanel.commands._recentLoad();
if len(argv) >= 1:
if (len(argv) >= 2) and (argv[1].endswith(".lst")):
roarClient.assetsWindow._load_list(argv[1])
@ -30,7 +30,7 @@ def main(*argv):
if rc:
roarClient.canvasPanel.update(roarClient.canvasPanel.canvas.importStore.inSize, False, roarClient.canvasPanel.canvas.importStore.outMap, dirty=False)
roarClient.canvasPanel.commands.update(pathName=argv[0], undoLevel=-1)
roarClient.canvasPanel.commands._pushRecent(argv[0])
roarClient.canvasPanel.commands._recentPush(argv[0])
roarClient.canvasPanel.commands.canvasTool(roarClient.canvasPanel.commands.canvasTool, 1)(None)
else:
print("error: {}".format(error), file=sys.stderr)