Compare commits
385 Commits
roar-pytho
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
22db646b91 | ||
![]() |
b77db5eb4b | ||
![]() |
8fabe15792 | ||
![]() |
d545a7447e | ||
![]() |
c8a048403a | ||
![]() |
0c66f94797 | ||
![]() |
90840bd0a0 | ||
![]() |
bc969295dd | ||
![]() |
e1bf3a3ad4 | ||
![]() |
1fd0768912 | ||
![]() |
19957a2006 | ||
![]() |
451a708d7a | ||
![]() |
de96a7cdaa | ||
![]() |
4b98d1cdf1 | ||
![]() |
2592fa9ad8 | ||
![]() |
fe6b79a2ab | ||
![]() |
50edb55e0b | ||
![]() |
76e57bd081 | ||
![]() |
86e2c9e904 | ||
![]() |
0750bc2261 | ||
![]() |
c45a616442 | ||
![]() |
e2183acf46 | ||
![]() |
279cca3313 | ||
![]() |
286df901a7 | ||
![]() |
50c9d69309 | ||
![]() |
fdcf242884 | ||
![]() |
b479e7ddbb | ||
![]() |
9eec4f58ab | ||
![]() |
a3d1ed3d96 | ||
![]() |
84d20a4398 | ||
![]() |
6ab7c443eb | ||
![]() |
c083800b3b | ||
![]() |
35f6910427 | ||
![]() |
c7dd80327d | ||
![]() |
a2295d1cfc | ||
![]() |
e68cae09b5 | ||
![]() |
3eb4eaeea5 | ||
![]() |
dc8b96146e | ||
![]() |
e0a9c97343 | ||
![]() |
c355e848b2 | ||
![]() |
cefb982c1d | ||
![]() |
1d650bd64b | ||
![]() |
600cecfad1 | ||
![]() |
b1de2eda79 | ||
![]() |
9c03910420 | ||
![]() |
e6e0951029 | ||
![]() |
a48d7f3d32 | ||
![]() |
bff70b409b | ||
![]() |
0843b7d330 | ||
![]() |
724b460be1 | ||
![]() |
6fcac2b3b9 | ||
![]() |
f504fa2b76 | ||
![]() |
eb3795a98e | ||
![]() |
62e7edc852 | ||
![]() |
5320e45651 | ||
![]() |
74a0ef6df8 | ||
![]() |
25c42023f2 | ||
![]() |
1a0e1bf2de | ||
![]() |
93c05d6b09 | ||
![]() |
d01cf7d167 | ||
![]() |
1c82e723c1 | ||
![]() |
3dae6dadde | ||
![]() |
187dfa6355 | ||
![]() |
deba33deba | ||
![]() |
366a958fdb | ||
![]() |
632e3a8ac1 | ||
![]() |
c93c8dd8b0 | ||
![]() |
5ad678bf4c | ||
![]() |
9752a8e6ce | ||
![]() |
99369626c4 | ||
![]() |
299a045aa9 | ||
![]() |
72b96b7c77 | ||
![]() |
70d7995b20 | ||
![]() |
3c0606a390 | ||
![]() |
aef7f629d0 | ||
![]() |
df8ee6fedf | ||
![]() |
aed3f7157c | ||
![]() |
af5363cf04 | ||
![]() |
e71f11ddad | ||
![]() |
78b567d42b | ||
![]() |
9f72bd5a4c | ||
![]() |
1487f2eb9a | ||
![]() |
d7ebed77f7 | ||
![]() |
a40017b607 | ||
![]() |
1c7524ba6f | ||
![]() |
866ef19966 | ||
![]() |
245925406e | ||
![]() |
7226639977 | ||
![]() |
e2f413e4ba | ||
![]() |
d67dd317a1 | ||
![]() |
9cc6364a08 | ||
![]() |
ddec3cefeb | ||
![]() |
25c9e88484 | ||
![]() |
0e4915351e | ||
![]() |
bdc8bcd494 | ||
![]() |
075bff0644 | ||
![]() |
c049181ceb | ||
![]() |
67f2d11240 | ||
![]() |
9e2d9d1419 | ||
![]() |
6d12c04349 | ||
![]() |
cab0478c39 | ||
![]() |
b1cc38a8e7 | ||
![]() |
5123a016e8 | ||
![]() |
243311c091 | ||
![]() |
f65fe72cea | ||
![]() |
74e2bf7f49 | ||
![]() |
2fa06043c6 | ||
![]() |
2c8fa89eda | ||
![]() |
6e470c93d0 | ||
![]() |
a4a9c9a2d5 | ||
![]() |
ec515d4ed4 | ||
![]() |
3e4644122b | ||
![]() |
31b45d1076 | ||
![]() |
6e05238efa | ||
![]() |
b6c063c5d3 | ||
![]() |
99e746f090 | ||
![]() |
6dad3c7798 | ||
![]() |
3f992ec44f | ||
![]() |
9de3372a80 | ||
![]() |
8485ab568d | ||
![]() |
7a4e1b23e9 | ||
![]() |
0f88a78639 | ||
![]() |
418d16a81a | ||
![]() |
3be34bc657 | ||
![]() |
e30628f28d | ||
![]() |
6109e9b38c | ||
![]() |
968e7d45c5 | ||
![]() |
af1ef072ab | ||
![]() |
5f7f2b0f9c | ||
![]() |
1156f8b5c3 | ||
![]() |
bb23efac56 | ||
![]() |
309e8cf089 | ||
![]() |
fb274c0d66 | ||
![]() |
f23101f511 | ||
![]() |
01ca10be21 | ||
![]() |
8fd0294ef8 | ||
![]() |
116e5915dc | ||
![]() |
c1ce141700 | ||
![]() |
5f00b60cdd | ||
![]() |
649f118b20 | ||
![]() |
b50879db5f | ||
![]() |
a4ee9e9083 | ||
![]() |
bfd8e17b2b | ||
![]() |
c2f8fb272e | ||
![]() |
0fe6899d05 | ||
![]() |
b3f587fc73 | ||
![]() |
1fe5db9fa6 | ||
![]() |
c2657eeefd | ||
![]() |
04430e7c0c | ||
![]() |
56a3b748cc | ||
![]() |
88d69cb31e | ||
![]() |
e54429e29e | ||
![]() |
52672796c6 | ||
![]() |
ff05b829b8 | ||
![]() |
629e03928e | ||
![]() |
fcd5173329 | ||
![]() |
b83684073e | ||
![]() |
f6e2dbd64b | ||
![]() |
c77813380a | ||
![]() |
6da84c05c5 | ||
![]() |
d410ef9321 | ||
![]() |
a628ed6471 | ||
![]() |
8367c166f3 | ||
![]() |
f3140d4b3d | ||
![]() |
c28c2f87ab | ||
![]() |
34d9df756b | ||
![]() |
bb4ba3ae46 | ||
![]() |
045b7a24ed | ||
![]() |
da8384f3bb | ||
![]() |
752af56774 | ||
![]() |
bd83513689 | ||
![]() |
b9f271297c | ||
![]() |
e3d23200b3 | ||
![]() |
e407ab2d81 | ||
![]() |
84126f5d4e | ||
![]() |
e03cf65028 | ||
![]() |
74da2e43c9 | ||
![]() |
f0cc23fcaa | ||
![]() |
9c528e8a6f | ||
![]() |
71fd619709 | ||
![]() |
cf2c5a7042 | ||
![]() |
08b453b33f | ||
![]() |
9420392b83 | ||
![]() |
01394e6137 | ||
![]() |
6453c1ff03 | ||
![]() |
6e371adbbe | ||
![]() |
035ab03e3c | ||
![]() |
80df97f972 | ||
![]() |
c15a5b7a21 | ||
![]() |
0d4a6432b2 | ||
![]() |
6639336a25 | ||
![]() |
2578c80819 | ||
![]() |
66fb77b9b5 | ||
![]() |
50b7e9c279 | ||
![]() |
6200254152 | ||
![]() |
ecaaf75b6b | ||
![]() |
b4e2286ecc | ||
![]() |
fd71785b3b | ||
![]() |
7d80e5331f | ||
![]() |
375324be9b | ||
![]() |
a4fa91f894 | ||
![]() |
efd8b20671 | ||
![]() |
61f7f75755 | ||
![]() |
c4e58ee096 | ||
![]() |
8f65e1723e | ||
![]() |
c53d1172a2 | ||
![]() |
1218de3bdf | ||
![]() |
3b981c3a34 | ||
![]() |
2d4d14266e | ||
![]() |
bffd7711a1 | ||
![]() |
35f3323be1 | ||
![]() |
a39c103065 | ||
![]() |
47604d6bde | ||
![]() |
8ef33b1683 | ||
![]() |
b031c994b5 | ||
![]() |
61c40b59c3 | ||
![]() |
b6e0a93029 | ||
![]() |
f35850051c | ||
![]() |
bd46fb99c5 | ||
![]() |
fad1dab3c6 | ||
![]() |
ad87bc8b2f | ||
![]() |
0ad72d7fb0 | ||
![]() |
3903f61815 | ||
![]() |
6cae432f46 | ||
![]() |
f012c628f2 | ||
![]() |
6d4db48ef0 | ||
![]() |
1d224b2bb4 | ||
![]() |
f9100dacb0 | ||
![]() |
8ec55357e6 | ||
![]() |
85cad2879a | ||
![]() |
a02ebdd039 | ||
![]() |
cc7d8b1b17 | ||
![]() |
2e682e0af1 | ||
![]() |
baacc19a67 | ||
![]() |
06fa555306 | ||
![]() |
0f2a234ec4 | ||
![]() |
301a003549 | ||
![]() |
48ce316417 | ||
![]() |
114ec3fb19 | ||
![]() |
048b3677ba | ||
![]() |
74952c6367 | ||
![]() |
18342308b2 | ||
![]() |
e1d338d6c3 | ||
![]() |
dc45d81361 | ||
![]() |
4b8c0f3469 | ||
![]() |
f273a3c6b1 | ||
![]() |
7afc5ea271 | ||
![]() |
401b7a408c | ||
![]() |
2287a8368b | ||
![]() |
0f5c8eae4a | ||
![]() |
9bacadcb70 | ||
![]() |
8bb9982b3b | ||
![]() |
d7e7519592 | ||
![]() |
233abaedef | ||
![]() |
9d4f1bf30a | ||
![]() |
07e693cc85 | ||
![]() |
eb0bbac893 | ||
![]() |
80cacedd89 | ||
![]() |
b853878b4b | ||
![]() |
9f0357605b | ||
![]() |
b51f315bb7 | ||
![]() |
5424b2ba1e | ||
![]() |
b0f140d657 | ||
![]() |
19200b8249 | ||
![]() |
d597715422 | ||
![]() |
f68fd2bcb9 | ||
![]() |
0f727db699 | ||
![]() |
1aeca12d04 | ||
![]() |
2da7965c99 | ||
![]() |
0f8b211517 | ||
![]() |
90192929f6 | ||
![]() |
948147124f | ||
![]() |
3607fd59e8 | ||
![]() |
6a347c4786 | ||
![]() |
fcea7308f2 | ||
![]() |
e5e254fb9b | ||
![]() |
5d045d4de5 | ||
![]() |
ac8fb74dc4 | ||
![]() |
fea5e2a4c9 | ||
![]() |
60cb67e7a6 | ||
![]() |
042cb2a57b | ||
![]() |
6180783d2d | ||
![]() |
8e34feff55 | ||
![]() |
4c629197a7 | ||
![]() |
3c79ca0675 | ||
![]() |
72aa916dc8 | ||
![]() |
88f8efe56c | ||
![]() |
735c016f94 | ||
![]() |
6be9cbd9b0 | ||
![]() |
b10c6fb6f7 | ||
![]() |
9fbfe7c26f | ||
![]() |
af683e4f26 | ||
![]() |
6b5fc121c8 | ||
![]() |
f7db3b30f4 | ||
![]() |
f1fd806573 | ||
![]() |
fc652958e8 | ||
![]() |
63531adce7 | ||
![]() |
44e4b02068 | ||
![]() |
21526e2421 | ||
![]() |
4a132b50df | ||
![]() |
86e849b122 | ||
![]() |
7e264f8a8b | ||
![]() |
2106b77586 | ||
![]() |
0eb96f9266 | ||
![]() |
08e5856db9 | ||
![]() |
54522ac329 | ||
![]() |
455a3fdcba | ||
![]() |
3c7560f6d7 | ||
![]() |
624ab9ca71 | ||
![]() |
05d3d28f1d | ||
![]() |
7a1ec135b2 | ||
![]() |
8b483e262e | ||
![]() |
8d6f6ef56e | ||
![]() |
9a9d5d858d | ||
![]() |
95f7dd113c | ||
![]() |
99c0c14993 | ||
![]() |
3b70d42784 | ||
![]() |
8e91d1269e | ||
![]() |
6d5e081dfb | ||
![]() |
1a503979d1 | ||
![]() |
c6ab27c08c | ||
![]() |
579dd858ba | ||
![]() |
92124dc34f | ||
![]() |
f77e6d5bf8 | ||
![]() |
b385909b45 | ||
![]() |
0cf1040614 | ||
![]() |
802de6fa6e | ||
![]() |
4f4ecb4331 | ||
![]() |
1511002798 | ||
![]() |
d79ea00164 | ||
![]() |
9eee59b501 | ||
![]() |
5380e899e4 | ||
![]() |
4e19f91eb0 | ||
![]() |
a33ff6b5df | ||
![]() |
46258cde32 | ||
![]() |
508a3cd489 | ||
![]() |
44490912b2 | ||
![]() |
2f85fa8cd1 | ||
![]() |
bb243c7556 | ||
![]() |
d1936486ff | ||
![]() |
cf04a12690 | ||
![]() |
02d3c7f6f4 | ||
![]() |
ebdf7f410b | ||
![]() |
4bc9b61f2c | ||
![]() |
f65603dd0c | ||
![]() |
d26a322612 | ||
![]() |
499296ba1d | ||
![]() |
e53e8bf8e7 | ||
![]() |
33df272b39 | ||
![]() |
8c0de9fbd3 | ||
![]() |
ad761d658e | ||
![]() |
c9d6b90e68 | ||
![]() |
49ef54ef76 | ||
![]() |
2254a0638b | ||
![]() |
402e542137 | ||
![]() |
a320c904fe | ||
![]() |
3fa377791a | ||
![]() |
e157f479ce | ||
![]() |
12027bc6b5 | ||
![]() |
f9f910160f | ||
![]() |
3e03126006 | ||
![]() |
398ee3af3d | ||
![]() |
cd5ea1fab5 | ||
![]() |
ed8ccab26f | ||
![]() |
2e014cd7ec | ||
![]() |
dbf6c76930 | ||
![]() |
363a86fe81 | ||
![]() |
dec1e4c0d4 | ||
![]() |
38e90f96ca | ||
![]() |
2a7f281d29 | ||
![]() |
f431adb381 | ||
![]() |
195ddd9af8 | ||
![]() |
9b5c9be88b | ||
![]() |
1099341b5d | ||
![]() |
e7b7e05234 | ||
![]() |
30f3688a2c | ||
![]() |
da82a543b5 | ||
![]() |
54adf3a95c | ||
![]() |
6a4356568b | ||
![]() |
39babc71a4 | ||
![]() |
84ab4eee5a | ||
![]() |
f6911293c3 | ||
![]() |
1c5f869c40 | ||
![]() |
3c28fa0071 | ||
![]() |
115991736a |
1
.env
Normal file
@ -0,0 +1 @@
|
||||
PYTHONPATH="E:\Documents - Repositories\roar\libcanvas;E:\Documents - Repositories\roar\libgui;E:\Documents - Repositories\roar\liboperators;E:\Documents - Repositories\roar\libroar;E:\Documents - Repositories\roar\librtl;E:\Documents - Repositories\roar\libtools"
|
4
.gitignore
vendored
@ -1,5 +1,9 @@
|
||||
*.sw[op]
|
||||
__pycache__/
|
||||
build/
|
||||
libgui/GuiCanvasWxBackendFast.exp
|
||||
libgui/GuiCanvasWxBackendFast.lib
|
||||
libgui/GuiCanvasWxBackendFast.obj
|
||||
libgui/GuiCanvasWxBackendFast.pyd
|
||||
librtl/ImgurApiKey.py
|
||||
releases/
|
||||
|
23
.vscode/c_cpp_properties.json
vendored
Executable file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Win32",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/**",
|
||||
"C:/Python37/include/**"
|
||||
],
|
||||
"defines": [
|
||||
"_DEBUG",
|
||||
"UNICODE",
|
||||
"_UNICODE"
|
||||
],
|
||||
"windowsSdkVersion": "10.0.18362.0",
|
||||
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.23.28105/bin/Hostx64/x64/cl.exe",
|
||||
"cStandard": "c11",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "msvc-x64",
|
||||
"compilerArgs": []
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
70
.vscode/launch.json
vendored
Executable file
@ -0,0 +1,70 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Current File (Integrated Terminal)",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Python: Remote Attach",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"port": 5678,
|
||||
"host": "localhost",
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Python: Module",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "enter-your-module-name-here",
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Python: Django",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/manage.py",
|
||||
"console": "integratedTerminal",
|
||||
"args": [
|
||||
"runserver",
|
||||
"--noreload",
|
||||
"--nothreading"
|
||||
],
|
||||
"django": true
|
||||
},
|
||||
{
|
||||
"name": "Python: Flask",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "flask",
|
||||
"env": {
|
||||
"FLASK_APP": "app.py"
|
||||
},
|
||||
"args": [
|
||||
"run",
|
||||
"--no-debugger",
|
||||
"--no-reload"
|
||||
],
|
||||
"jinja": true
|
||||
},
|
||||
{
|
||||
"name": "Python: Current File (External Terminal)",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "externalTerminal"
|
||||
}
|
||||
]
|
||||
}
|
8
.vscode/roar.code-workspace
vendored
Executable file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "E:\\Documents - Repositories\\roar"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
5
.vscode/settings.json
vendored
Executable file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"python.analysis.disabled": ["unresolved-import"],
|
||||
"python.pythonPath": "C:\\Python37\\python.exe",
|
||||
"python.linting.enabled": false
|
||||
}
|
15
.vscode/tasks.json
vendored
Executable file
@ -0,0 +1,15 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Build libgui/GuiCanvasWxBackendFast.pyd",
|
||||
"type": "shell",
|
||||
"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/roararab6.wav
Normal file
BIN
assets/audio/roararab7.wav
Normal file
BIN
assets/audio/roararab8.wav
Normal file
BIN
assets/audio/roardaemon1.wav
Normal file
BIN
assets/audio/roarspoke10.wav
Normal file
BIN
assets/audio/roarspoke11.wav
Normal file
BIN
assets/audio/roarspoke12.wav
Normal file
BIN
assets/audio/roarspoke13.wav
Normal file
BIN
assets/audio/roarspoke14.wav
Normal file
BIN
assets/audio/roarspoke15.wav
Normal file
BIN
assets/audio/roarspoke9.wav
Normal file
BIN
assets/audio/roarvap0r1.wav
Normal file
BIN
assets/audio/roarvap0r2.wav
Normal file
BIN
assets/audio/roarvap0r3.wav
Normal file
BIN
assets/audio/roarvap0r4.wav
Normal file
BIN
assets/audio/roarvap0r5.wav
Normal file
BIN
assets/audio/roarvap0r6.wav
Normal file
BIN
assets/audio/roarvap0r7.wav
Normal file
BIN
assets/audio/roarvap0r8.wav
Normal file
BIN
assets/audio/roarviking1.wav
Normal file
BIN
assets/audio/roarviking2.wav
Normal file
BIN
assets/audio/roarviking3.wav
Normal file
BIN
assets/audio/roarviking4.wav
Normal file
BIN
assets/audio/roarviking5.wav
Normal file
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 45 KiB |
BIN
assets/images/toolColoursFlip.png
Normal file
After Width: | Height: | Size: 221 B |
0
assets/images/toolCursor.png
Executable file → Normal file
Before Width: | Height: | Size: 203 B After Width: | Height: | Size: 203 B |
BIN
assets/images/toolErase.png
Normal file
After Width: | Height: | Size: 253 B |
BIN
assets/images/toolHideAssetsWindow.png
Normal file
After Width: | Height: | Size: 274 B |
BIN
assets/images/toolPickColour.png
Normal file
After Width: | Height: | Size: 279 B |
BIN
assets/images/toolShowAssetsWindow.png
Normal file
After Width: | Height: | Size: 268 B |
@ -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:
|
||||
# roar.py -- mIRC art editor for Windows & Linux (unmaintained reference implementation, do not use)
|
||||
* 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:
|
||||

|
||||
|
||||
|
@ -1,22 +1,20 @@
|
||||
1) Implement ANSI CSI CU[BDPU] sequences & italic
|
||||
2) Layers & layout (e.g. for comics, zines, etc.)
|
||||
3) Implement instrumentation & unit tests, document
|
||||
4) Open and toggle a reference image in the background
|
||||
5) Client-Server or Peer-to-Peer realtime collaboration
|
||||
6) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.)
|
||||
7) Incremental auto{load,save} & {backup,restore} (needs Settings window)
|
||||
8) Composition, parametrisation & keying of tools from higher-order operators (brushes, functions, filters, outlines, patterns & shaders) and unit tools
|
||||
9) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...)
|
||||
|
||||
High-priority list:
|
||||
1) unit tools: arrow, {cloud,speech bubble}, curve, measure, pick, polygon, triangle, unicode
|
||||
2) text tool: a) honour RTL text flow b) navigating w/ cursor keys c) pasting text
|
||||
3) operators: rotate, scale, shift, slice, tile
|
||||
4) cleanup & refactor, switch to GTK
|
||||
5) GUI:
|
||||
a) replace logo w/ canvas panel in About dialogue
|
||||
b) Settings/Settings window (e.g. autosave, hide cursor on leaving window, ...)
|
||||
c) replace resize buttons w/ {-,edit box,+} buttons & lock button re: ratio (ty lol3)
|
||||
d) {copy,cut,delete,insert from,paste}, {edit asset in new canvas,import from {canvas,object}}
|
||||
1) ANSI CSI CU[BDPU] sequences
|
||||
3) Documentation, instrumentation & unit tests
|
||||
4) Layers & layout (e.g. for comics, zines, etc.)
|
||||
5) Open and toggle a reference image in the background
|
||||
6) Client-Server or Peer-to-Peer realtime collaboration
|
||||
7) Arbitrary {format,palette}s ({4,8} bit ANSI/mIRC, etc.)
|
||||
8) Unit tools: arrow, {cloud,speech bubble}, curve, polygon
|
||||
9) {record,replay} {keyboard,mouse,...} events in debugging builds
|
||||
10) Integrate ENNTool code in the form of OpenGL-based animation window (see 13) and 14))
|
||||
11) Composition, parametrisation & keying of tools from higher-order operators (brushes, functions, filters, outlines, patterns & shaders) and unit tools
|
||||
12) Sprites & scripted (Python?) animation on the basis of asset traits and {composable,parametrised} patterns (metric flow, particle system, rigging, ...)
|
||||
13) GUI TODO list:
|
||||
a) switch to Gtk
|
||||
b) canvas preview in Open dialogue(s)
|
||||
c) https://material.io/resources/icons/?style=baseline
|
||||
d) replace logo w/ canvas panel in About dialogue, revisit melp? dialogue
|
||||
e) replace resize buttons w/ {-,edit box,+} buttons & lock button re: ratio (ty lol3)
|
||||
f) Settings window (e.g. autosave parameters, cursor opacity, default colours, hide cursor on leaving window, keyboard/mouse map, show cell position tooltip on mouse hover, ...)
|
||||
|
||||
vim:ff=dos tw=0
|
||||
|
41
assets/text/melp.txt
Normal file
@ -0,0 +1,41 @@
|
||||
Keys or mouse actions separated by forward slashes (`/') indicate alternatives.
|
||||
Keys or mouse actions separated by commas indicate separate commands.
|
||||
|
||||
Global commands:
|
||||
<Ctrl> -, +/<Mouse wheel> Decrease/increase brush height and width
|
||||
<Ctrl> <Mouse wheel> Decrease/increase canvas height and width
|
||||
<Ctrl> <Up>, <Down> Decrease/increase canvas height
|
||||
<Ctrl> <Left>, <Right> Decrease/increase canvas width
|
||||
<Ctrl> <Alt> <Mouse wheel> Decrease/increase cell size
|
||||
|
||||
<Ctrl> 0-9 Set foreground colour to #0-9
|
||||
<Ctrl> <Shift> 0-5, 6 Set foreground colour to #10-15 or transparent colour, resp.
|
||||
<Ctrl> <Alt> 0-9 Set background colour to #0-9
|
||||
<Ctrl> <Alt> <Shift> 0-5, 6 Set background colour to #10-15 or transparent colour, resp.
|
||||
<Ctrl> I Flip colours
|
||||
<F1> View melp?
|
||||
<F2-F10> Switch to cursor, rectangle, circle, fill, line, text, object, erase, pick colour tool
|
||||
|
||||
<Ctrl> N New canvas
|
||||
<Ctrl> O Open mIRC art file
|
||||
<Ctrl> S Save canvas as mIRC art file
|
||||
<Ctrl> X Exit
|
||||
<Ctrl> Y, Z Redo, undo last action
|
||||
<Shift> <Pause> Break into Python debugger
|
||||
|
||||
Canvas commands:
|
||||
<Down>, <Left>, <Right>, <Up> Move canvas cursor
|
||||
<LMB>/<Space> Apply current tool with foreground colour (with exceptions)
|
||||
<RMB> Apply current tool with background colour (with exceptions)
|
||||
|
||||
Tool-specific commands:
|
||||
(Circle, rectangle) <Ctrl> <LMB>, <RMB> Initiate circle/rectangle dragging irrespective of brush size
|
||||
(Erase) <RMB> Erase background colour with foreground colour
|
||||
(Fill) <Ctrl> <LMB>, <Space>/<RMB> Fill entire region with foreground/background colour ignoring character cells
|
||||
(Line, object) <LMB>/<Space> Initiate line drawing/selection
|
||||
(Object) <Ctrl> <LMB> Move selection instead of cloning
|
||||
(Pick colour) <LMB>/<Space> Pick current cell's foreground colour
|
||||
(Pick colour) <RMB> Pick current cell's background colour
|
||||
(Text) <Backspace> Erase last cell and move backwards w/ wraparound
|
||||
(Text) <Ctrl> V Paste text from clipboard
|
||||
(Text) <Enter> Move to leftmost cell on next row w/ wraparound
|
1
assets/text/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
.
|
15
assets/text/roadmap.txt
Normal file
@ -0,0 +1,15 @@
|
||||
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
|
||||
5) GUI: show line numbers w/ tooltip on accelerator
|
||||
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: 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
|
@ -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,1╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱15
|
||||
2,1╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱╱15
|
||||
2,1╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲╲15
|
||||
|
31
assets/tools/AnsiToMiRCART.py
Executable 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
|
@ -19,17 +19,14 @@ class IrcMiRCARTBot(IrcClient):
|
||||
"""IRC<->MiRC2png bot"""
|
||||
imgurApiKey = ImgurApiKey.imgurApiKey
|
||||
|
||||
# {{{ ContentTooLargeException(Exception): Raised by _urlretrieveReportHook() given download size > 1 MB
|
||||
class ContentTooLargeException(Exception):
|
||||
pass
|
||||
# }}}
|
||||
# {{{ _dispatch001(self, message): Dispatch single 001 (RPL_WELCOME)
|
||||
|
||||
def _dispatch001(self, message):
|
||||
self._log("Registered on {}:{} as {}, {}, {}.".format(self.serverHname, self.serverPort, self.clientNick, self.clientIdent, self.clientGecos))
|
||||
self._log("Attempting to join {} on {}:{}...".format(self.clientChannel, self.serverHname, self.serverPort))
|
||||
self.queue("JOIN", self.clientChannel)
|
||||
# }}}
|
||||
# {{{ _dispatch353(self, message): Dispatch single 353 (RPL_NAMREPLY)
|
||||
|
||||
def _dispatch353(self, message):
|
||||
if message[4].lower() == self.clientChannel.lower():
|
||||
for channelNickSpec in message[5].split(" "):
|
||||
@ -38,20 +35,17 @@ class IrcMiRCARTBot(IrcClient):
|
||||
and len(channelNickSpec[1:]):
|
||||
self.clientChannelOps.append(channelNickSpec[1:].lower())
|
||||
self._log("Authorising {} on {}".format(channelNickSpec[1:].lower(), message[4].lower()))
|
||||
# }}}
|
||||
# {{{ _dispatchJoin(self, message): Dispatch single JOIN message from server
|
||||
|
||||
def _dispatchJoin(self, message):
|
||||
self._log("Joined {} on {}:{}.".format(message[2].lower(), self.serverHname, self.serverPort))
|
||||
self.clientNextTimeout = None; self.clientChannelRejoin = False;
|
||||
# }}}
|
||||
# {{{ _dispatchKick(self, message): Dispatch single KICK message from server
|
||||
|
||||
def _dispatchKick(self, message):
|
||||
if message[2].lower() == self.clientChannel.lower() \
|
||||
and message[3].lower() == self.clientNick.lower():
|
||||
self._log("Kicked from {} by {}, rejoining in 15 seconds".format(message[2].lower(), message[0]))
|
||||
self.clientNextTimeout = time.time() + 15; self.clientChannelRejoin = True;
|
||||
# }}}
|
||||
# {{{ _dispatchMode(self, message): Dispatch single MODE message from server
|
||||
|
||||
def _dispatchMode(self, message):
|
||||
if message[2].lower() == self.clientChannel.lower():
|
||||
channelModeType = "+"; channelModeArg = 4;
|
||||
@ -78,17 +72,14 @@ class IrcMiRCARTBot(IrcClient):
|
||||
channelAuthDel = channelAuthDel.lower()
|
||||
self._log("Deauthorising {} on {}".format(channelAuthDel, message[2].lower()))
|
||||
self.clientChannelOps.remove(channelAuthDel)
|
||||
# }}}
|
||||
# {{{ _dispatchNone(self): Dispatch None message from server
|
||||
|
||||
def _dispatchNone(self):
|
||||
self._log("Disconnected from {}:{}.".format(self.serverHname, self.serverPort))
|
||||
self.close()
|
||||
# }}}
|
||||
# {{{ _dispatchPing(self, message): Dispatch single PING message from server
|
||||
|
||||
def _dispatchPing(self, message):
|
||||
self.queue("PONG", message[2])
|
||||
# }}}
|
||||
# {{{ _dispatchPrivmsg(self, message): Dispatch single PRIVMSG message from server
|
||||
|
||||
def _dispatchPrivmsg(self, message):
|
||||
if message[2].lower() == self.clientChannel.lower() \
|
||||
and message[3].startswith("!pngbot "):
|
||||
@ -155,19 +146,16 @@ class IrcMiRCARTBot(IrcClient):
|
||||
os.remove(asciiTmpFilePath)
|
||||
if os.path.isfile(imgTmpFilePath):
|
||||
os.remove(imgTmpFilePath)
|
||||
# }}}
|
||||
# {{{ _dispatchTimer(self): Dispatch single client timer expiration
|
||||
|
||||
def _dispatchTimer(self):
|
||||
if self.clientChannelRejoin:
|
||||
self._log("Attempting to join {} on {}:{}...".format(self.clientChannel, self.serverHname, self.serverPort))
|
||||
self.queue("JOIN", self.clientChannel)
|
||||
self.clientNextTimeout = time.time() + 15; self.clientChannelRejoin = True;
|
||||
# }}}
|
||||
# {{{ _log(self, msg): Log single message to stdout w/ timestamp
|
||||
|
||||
def _log(self, msg):
|
||||
print(time.strftime("%Y/%m/%d %H:%M:%S") + " " + msg)
|
||||
# }}}
|
||||
# {{{ _uploadToImgur(self, imgFilePath, imgName, imgTitle, apiKey): Upload single file to Imgur
|
||||
|
||||
def _uploadToImgur(self, imgFilePath, imgName, imgTitle, apiKey):
|
||||
with open(imgFilePath, "rb") as requestImage:
|
||||
requestImageData = requestImage.read()
|
||||
@ -188,13 +176,11 @@ class IrcMiRCARTBot(IrcClient):
|
||||
return [200, responseDict.get("data").get("link")]
|
||||
else:
|
||||
return [responseHttp.status_code, responseHttp.text]
|
||||
# }}}
|
||||
# {{{ _urlretrieveReportHook(count, blockSize, totalSize): Limit downloads to 1 MB
|
||||
|
||||
def _urlretrieveReportHook(count, blockSize, totalSize):
|
||||
if (totalSize > pow(2,20)):
|
||||
raise IrcMiRCARTBot.ContentTooLargeException
|
||||
# }}}
|
||||
# {{{ connect(self, localAddr=None, preferFamily=0, timeout=None): Connect to server and (re)initialise w/ optional timeout
|
||||
|
||||
def connect(self, localAddr=None, preferFamily=0, timeout=None):
|
||||
self._log("Connecting to {}:{}...".format(self.serverHname, self.serverPort))
|
||||
if super().connect(localAddr=localAddr, preferFamily=preferFamily, timeout=timeout):
|
||||
@ -206,8 +192,7 @@ class IrcMiRCARTBot(IrcClient):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
# }}}
|
||||
# {{{ dispatch(self): Read, parse, and dispatch single line from server
|
||||
|
||||
def dispatch(self):
|
||||
while True:
|
||||
if self.clientNextTimeout:
|
||||
@ -245,10 +230,7 @@ class IrcMiRCARTBot(IrcClient):
|
||||
self.clientHasPing = False
|
||||
elif serverMessage[1] == "PRIVMSG":
|
||||
self._dispatchPrivmsg(serverMessage)
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, serverHname, serverPort="6667", clientNick="pngbot", clientIdent="pngbot", clientGecos="pngbot", clientChannel="#MiRCART"): initialisation method
|
||||
def __init__(self, serverHname, serverPort="6667", clientNick="pngbot", clientIdent="pngbot", clientGecos="pngbot", clientChannel="#MiRCART"):
|
||||
super().__init__(serverHname, serverPort, clientNick, clientIdent, clientGecos)
|
||||
self.clientChannel = clientChannel
|
||||
@ -277,12 +259,12 @@ if __name__ == "__main__":
|
||||
optlist, argv = getopt(sys.argv[1:], "46l:")
|
||||
optdict = dict(optlist)
|
||||
if len(argv) < 1 or len(argv) > 6:
|
||||
print("usage: {} [-4|-6] [-l <local hostname>] " \
|
||||
"<IRC server hostname> " \
|
||||
"[<IRC server port; defaults to 6667>] " \
|
||||
"[<IRC bot nick name; defaults to pngbot>] " \
|
||||
"[<IRC bot user name; defaults to pngbot>] " \
|
||||
"[<IRC bot real name; defaults to pngbot>] " \
|
||||
print("usage: {} [-4|-6] [-l <local hostname>] " \
|
||||
"<IRC server hostname> " \
|
||||
"[<IRC server port; defaults to 6667>] " \
|
||||
"[<IRC bot nick name; defaults to pngbot>] " \
|
||||
"[<IRC bot user name; defaults to pngbot>] " \
|
||||
"[<IRC bot real name; defaults to pngbot>] " \
|
||||
"[<IRC bot channel name; defaults to #MiRCART>] ".format(sys.argv[0]), file=sys.stderr)
|
||||
else:
|
||||
main(optdict, *argv)
|
||||
|
@ -27,13 +27,15 @@ deploy() {
|
||||
-mindepth 1 \
|
||||
-not -path "./${RELEASES_DNAME}/*" \
|
||||
-not -path "./${RELEASES_DNAME}" \
|
||||
-not -path "./.git/*" \
|
||||
-not -path "./.git" \
|
||||
-not -path './.*/*' \
|
||||
-not -path './.*' \
|
||||
-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 '.gitignore' \
|
||||
-not -name "${0##*/}" |\
|
||||
cpio --quiet -dLmp "${_release_dname}";
|
||||
sed -i"" "s/__ROAR_RELEASE_VERSION__/${_release_version_long}/" "${_release_dname}/libroar/RoarWindowAbout.py";
|
||||
|
@ -6,102 +6,123 @@
|
||||
|
||||
from CanvasExportStore import CanvasExportStore
|
||||
from CanvasImportStore import CanvasImportStore
|
||||
from CanvasJournal import CanvasJournal
|
||||
|
||||
class Canvas():
|
||||
# {{{ _commitPatch(self, patch)
|
||||
def _commitPatch(self, patch):
|
||||
self.map[patch[1]][patch[0]] = patch[2:]
|
||||
# }}}
|
||||
|
||||
# {{{ dispatchPatch(self, isCursor, patch, commitUndo=True)
|
||||
def dispatchPatch(self, isCursor, patch, commitUndo=True):
|
||||
def applyPatch(self, patch, commitUndo=True):
|
||||
if (patch[0] >= self.size[0]) or (patch[1] >= self.size[1]):
|
||||
return False
|
||||
else:
|
||||
patchDeltaCell = self.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell];
|
||||
if isCursor:
|
||||
self.journal.pushCursor(patchDelta)
|
||||
else:
|
||||
if commitUndo:
|
||||
self.journal.begin(); self.journal.updateCurrentDeltas(patch, patchDelta); self.journal.end();
|
||||
self._commitPatch(patch)
|
||||
if commitUndo:
|
||||
self.updateCurrentDeltas(patch, patchDelta)
|
||||
self._commitPatch(patch)
|
||||
return True
|
||||
# }}}
|
||||
# {{{ dispatchPatchSingle(self, isCursor, patch, commitUndo=True)
|
||||
def dispatchPatchSingle(self, isCursor, patch, commitUndo=True):
|
||||
if (patch[0] >= self.size[0]) or (patch[1] >= self.size[1]):
|
||||
return False
|
||||
|
||||
def begin(self):
|
||||
deltaItem = [[], []]; self.patchesUndo.insert(self.patchesUndoLevel, deltaItem);
|
||||
|
||||
def end(self):
|
||||
if self.patchesUndo[self.patchesUndoLevel] == [[], []]:
|
||||
del self.patchesUndo[self.patchesUndoLevel]
|
||||
else:
|
||||
patchDeltaCell = self.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell];
|
||||
if isCursor:
|
||||
self.journal.pushCursor(patchDelta)
|
||||
else:
|
||||
if commitUndo:
|
||||
self.journal.updateCurrentDeltas(patch, patchDelta)
|
||||
self._commitPatch(patch)
|
||||
return True
|
||||
# }}}
|
||||
# {{{ resize(self, newSize, commitUndo=True)
|
||||
def resize(self, newSize, commitUndo=True):
|
||||
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]
|
||||
else:
|
||||
oldSize = self.size
|
||||
deltaSize = [b - a for a, b in zip(oldSize, newSize)]
|
||||
self.journal.resetCursor()
|
||||
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.dispatchPatch(False, [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.dispatchPatch(False, [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
|
||||
# }}}
|
||||
# {{{ update(self, newSize, newCanvas=None)
|
||||
return False, newCells
|
||||
|
||||
def update(self, newSize, newCanvas=None):
|
||||
for numRow in range(self.size[1]):
|
||||
for numCol in range(self.size[0]):
|
||||
if (newCanvas != None) \
|
||||
and (numRow < len(newCanvas)) \
|
||||
and (numCol < len(newCanvas[numRow])):
|
||||
if (newCanvas != None) \
|
||||
and (numRow < len(newCanvas)) and (numCol < len(newCanvas[numRow])):
|
||||
self._commitPatch([numCol, numRow, *newCanvas[numRow][numCol]])
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, size): initialisation method
|
||||
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.dirtyCursor, self.map, self.size = False, None, size
|
||||
self.exportStore, self.importStore, self.journal = CanvasExportStore(), CanvasImportStore(), CanvasJournal()
|
||||
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
|
||||
|
@ -4,11 +4,10 @@
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
# {{{ AnsiBgToMiRCARTColours
|
||||
AnsiBgToMiRCARTColours = {
|
||||
107: 0, # Bright White
|
||||
40: 1, # Black
|
||||
104: 2, # Blue
|
||||
44: 2, # Blue
|
||||
42: 3, # Green
|
||||
101: 4, # Red
|
||||
41: 5, # Light Red
|
||||
@ -18,13 +17,12 @@ AnsiBgToMiRCARTColours = {
|
||||
102: 9, # Light Green
|
||||
46: 10, # Cyan
|
||||
106: 11, # Light Cyan
|
||||
44: 12, # Light Blue
|
||||
104: 12, # Light Blue
|
||||
105: 13, # Light Pink
|
||||
100: 14, # Grey
|
||||
47: 15, # Light Grey
|
||||
};
|
||||
# }}}
|
||||
# {{{ AnsiFgBoldToMiRCARTColours
|
||||
|
||||
AnsiFgBoldToMiRCARTColours = {
|
||||
97: 0, # Bright White
|
||||
30: 14, # Grey
|
||||
@ -38,17 +36,16 @@ AnsiFgBoldToMiRCARTColours = {
|
||||
92: 9, # Light Green
|
||||
36: 11, # Light Cyan
|
||||
96: 11, # Light Cyan
|
||||
34: 12, # Light Blue
|
||||
94: 12, # Light Blue
|
||||
95: 13, # Light Pink
|
||||
90: 14, # Grey
|
||||
37: 0, # Bright White
|
||||
};
|
||||
# }}}
|
||||
# {{{ AnsiFgToMiRCARTColours
|
||||
|
||||
AnsiFgToMiRCARTColours = {
|
||||
97: 0, # Bright White
|
||||
30: 1, # Black
|
||||
94: 2, # Blue
|
||||
34: 2, # Blue
|
||||
32: 3, # Green
|
||||
91: 4, # Red
|
||||
31: 5, # Light Red
|
||||
@ -58,13 +55,12 @@ AnsiFgToMiRCARTColours = {
|
||||
92: 9, # Light Green
|
||||
36: 10, # Cyan
|
||||
96: 11, # Light Cyan
|
||||
34: 12, # Light Blue
|
||||
94: 12, # Light Blue
|
||||
95: 13, # Light Pink
|
||||
90: 14, # Grey
|
||||
37: 15, # Light Grey
|
||||
};
|
||||
# }}}
|
||||
# {{{ ColourMapBold: mIRC colour number to RGBA map given ^B (bold)
|
||||
|
||||
ColourMapBold = [
|
||||
[255, 255, 255], # Bright White
|
||||
[85, 85, 85], # Black
|
||||
@ -83,8 +79,7 @@ ColourMapBold = [
|
||||
[85, 85, 85], # Grey
|
||||
[255, 255, 255], # Light Grey
|
||||
]
|
||||
# }}}
|
||||
# {{{ ColourMapNormal: mIRC colour number to RGBA map given none of ^[BFV_] (bold, italic, reverse, underline)
|
||||
|
||||
ColourMapNormal = [
|
||||
[255, 255, 255], # Bright White
|
||||
[0, 0, 0], # Black
|
||||
@ -103,12 +98,11 @@ ColourMapNormal = [
|
||||
[85, 85, 85], # Grey
|
||||
[187, 187, 187], # Light Grey
|
||||
]
|
||||
# }}}
|
||||
# {{{ MiRCARTToAnsiColours
|
||||
|
||||
MiRCARTToAnsiColours = [
|
||||
97, # Bright White
|
||||
30, # Black
|
||||
94, # Light Blue
|
||||
34, # Blue
|
||||
32, # Green
|
||||
91, # Red
|
||||
31, # Light Red
|
||||
@ -118,11 +112,10 @@ MiRCARTToAnsiColours = [
|
||||
92, # Light Green
|
||||
36, # Cyan
|
||||
96, # Light Cyan
|
||||
34, # Blue
|
||||
94, # Light Blue
|
||||
95, # Light Pink
|
||||
90, # Grey
|
||||
37, # Light Grey
|
||||
];
|
||||
# }}}
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -20,25 +20,20 @@ except ImportError:
|
||||
haveUrllib = False
|
||||
|
||||
class CanvasExportStore():
|
||||
# {{{ _CellState(): Cell state
|
||||
class _CellState():
|
||||
CS_NONE = 0x00
|
||||
CS_BOLD = 0x01
|
||||
CS_ITALIC = 0x02
|
||||
CS_UNDERLINE = 0x04
|
||||
# }}}
|
||||
CS_UNDERLINE = 0x02
|
||||
|
||||
ImgurUploadUrl = "https://api.imgur.com/3/upload.json"
|
||||
PastebinPostUrl = "https://pastebin.com/api/api_post.php"
|
||||
|
||||
# {{{ _drawUnderline(self, curPos, fillColour, fontSize, imgDraw)
|
||||
def _drawUnderLine(self, curPos, fillColour, fontSize, imgDraw):
|
||||
imgDraw.line( \
|
||||
xy=(curPos[0], curPos[1] + (fontSize[1] - 2), \
|
||||
curPos[0] + fontSize[0], curPos[1] + (fontSize[1] - 2)), \
|
||||
fill=fillColour)
|
||||
# }}}
|
||||
|
||||
# {{{ exportAnsiFile(self, canvasMap, canvasSize, outFile)
|
||||
def exportAnsiFile(self, canvasMap, canvasSize, outFile):
|
||||
outBuffer = ""
|
||||
for inCurRow in range(len(canvasMap)):
|
||||
@ -50,9 +45,12 @@ class CanvasExportStore():
|
||||
outBuffer += "\u001b[1m"
|
||||
if inCurCell[2] & self._CellState.CS_UNDERLINE:
|
||||
outBuffer += "\u001b[4m"
|
||||
elif (lastAttribs & self._CellState.CS_UNDERLINE) \
|
||||
and ((inCurCell[2] & self._CellState.CS_UNDERLINE) == 0):
|
||||
outBuffer += "\u001b[24m"
|
||||
lastAttribs = inCurCell[2]
|
||||
if lastColours == None or lastColours != inCurCell[:2]:
|
||||
if (inCurCell[0] == -1) \
|
||||
if (inCurCell[0] == -1) \
|
||||
and (inCurCell[1] == -1):
|
||||
outBuffer += "\u001b[39;49m{}".format(" ")
|
||||
elif inCurCell[1] == -1:
|
||||
@ -71,8 +69,7 @@ class CanvasExportStore():
|
||||
return (True, None)
|
||||
else:
|
||||
return (False, "empty buffer generated")
|
||||
# }}}
|
||||
# {{{ exportBitmapToImgur(self, apiKey, canvasBitmap, imgName, imgTitle, imgType)
|
||||
|
||||
def exportBitmapToImgur(self, apiKey, canvasBitmap, imgName, imgTitle, imgType):
|
||||
tmpPathName = tempfile.mkstemp()
|
||||
os.close(tmpPathName[0])
|
||||
@ -94,12 +91,10 @@ class CanvasExportStore():
|
||||
imgurResult = (False, responseHttp.status_code, responseDict.get("data"))
|
||||
os.remove(tmpPathName[1])
|
||||
return imgurResult
|
||||
# }}}
|
||||
# {{{ exportBitmapToPngFile(self, canvasBitmap, outPathName, outType)
|
||||
|
||||
def exportBitmapToPngFile(self, canvasBitmap, outPathName, outType):
|
||||
return canvasBitmap.ConvertToImage().SaveFile(outPathName, outType)
|
||||
# }}}
|
||||
# {{{ exportPastebin(self, apiDevKey, canvasMap, canvasSize, pasteName="", pastePrivate=0)
|
||||
|
||||
def exportPastebin(self, apiDevKey, canvasMap, canvasSize, pasteName="", pastePrivate=0):
|
||||
if haveUrllib:
|
||||
outFile = io.StringIO()
|
||||
@ -117,8 +112,7 @@ class CanvasExportStore():
|
||||
return (False, str(responseHttp.status_code))
|
||||
else:
|
||||
return (False, "missing requests and/or urllib3 module(s)")
|
||||
# }}}
|
||||
# {{{ exportPngFile(self, canvasMap, fontFilePath, fontSize, outPathName)
|
||||
|
||||
def exportPngFile(self, canvasMap, fontFilePath, fontSize, outPathName):
|
||||
if havePIL:
|
||||
inSize = (len(canvasMap[0]), len(canvasMap))
|
||||
@ -161,20 +155,32 @@ class CanvasExportStore():
|
||||
return (True, None)
|
||||
else:
|
||||
return (False, "missing PIL modules")
|
||||
# }}}
|
||||
# {{{ exportTextBuffer(self, canvasMap, canvasSize)
|
||||
|
||||
def exportTextBuffer(self, canvasMap, canvasSize):
|
||||
outBuffer = ""
|
||||
for canvasRow in range(canvasSize[1]):
|
||||
canvasLastColours = [15, -1]
|
||||
canvasLastAttrs, canvasLastColours = self._CellState.CS_NONE, [15, -1]
|
||||
for canvasCol in range(canvasSize[0]):
|
||||
canvasColAttrs = canvasMap[canvasRow][canvasCol][2]
|
||||
canvasColColours = canvasMap[canvasRow][canvasCol][0:2]
|
||||
canvasColText = canvasMap[canvasRow][canvasCol][3]
|
||||
if (canvasColAttrs & self._CellState.CS_BOLD) \
|
||||
and (not (canvasLastAttrs & self._CellState.CS_BOLD)):
|
||||
outBuffer += "\u0002"; canvasLastAttrs = canvasLastAttrs | self._CellState.CS_BOLD;
|
||||
if (not (canvasColAttrs & self._CellState.CS_BOLD)) \
|
||||
and (canvasLastAttrs & self._CellState.CS_BOLD):
|
||||
outBuffer += "\u0002"; canvasLastAttrs = canvasLastAttrs & ~self._CellState.CS_BOLD;
|
||||
if (canvasColAttrs & self._CellState.CS_UNDERLINE) \
|
||||
and (not (canvasLastAttrs & self._CellState.CS_UNDERLINE)):
|
||||
outBuffer += "\u001f"; canvasLastAttrs = canvasLastAttrs | self._CellState.CS_UNDERLINE;
|
||||
if (not (canvasColAttrs & self._CellState.CS_UNDERLINE)) \
|
||||
and (canvasLastAttrs & self._CellState.CS_UNDERLINE):
|
||||
outBuffer += "\u001f"; canvasLastAttrs = canvasLastAttrs & ~self._CellState.CS_UNDERLINE;
|
||||
if canvasColColours[0] == -1:
|
||||
canvasColColours[0] = canvasColColours[1]
|
||||
if (canvasColColours[0] != canvasLastColours[0]) \
|
||||
if (canvasColColours[0] != canvasLastColours[0]) \
|
||||
and (canvasColColours[1] != canvasLastColours[1]):
|
||||
if (canvasColColours[0] == -1) \
|
||||
if (canvasColColours[0] == -1) \
|
||||
and (canvasColColours[1] == -1):
|
||||
outBuffer += "\u000f"
|
||||
elif canvasColColours[1] == -1:
|
||||
@ -200,11 +206,9 @@ class CanvasExportStore():
|
||||
return (True, outBuffer)
|
||||
else:
|
||||
return (False, "empty buffer generated")
|
||||
# }}}
|
||||
# {{{ exportTextFile(self, canvasMap, canvasSize, outFile)
|
||||
|
||||
def exportTextFile(self, canvasMap, canvasSize, outFile):
|
||||
rc, outBuffer = self.exportTextBuffer(canvasMap, canvasSize)
|
||||
return outFile.write(outBuffer) if rc else (rc, outBuffer)
|
||||
# }}}
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -9,20 +9,14 @@ from CanvasColours import AnsiBgToMiRCARTColours, AnsiFgToMiRCARTColours, AnsiFg
|
||||
import io, os, re, struct, sys
|
||||
|
||||
class CanvasImportStore():
|
||||
# {{{ _CellState(): Cell state
|
||||
class _CellState():
|
||||
CS_NONE = 0x00
|
||||
CS_BOLD = 0x01
|
||||
CS_ITALIC = 0x02
|
||||
CS_UNDERLINE = 0x04
|
||||
# }}}
|
||||
CS_UNDERLINE = 0x02
|
||||
|
||||
# {{{ _flipCellStateBit(self, bit, cellState)
|
||||
def _flipCellStateBit(self, bit, cellState):
|
||||
return cellState & ~bit if cellState & bit else cellState | bit
|
||||
# }}}
|
||||
|
||||
# {{{ importAnsiBuffer(self, inBuffer, encoding="cp437", width=None)
|
||||
def importAnsiBuffer(self, inBuffer, encoding="cp437", width=None):
|
||||
curBg, curBgAnsi, curBoldAnsi, curFg, curFgAnsi = -1, 30, False, 15, 37
|
||||
done, outMap, outMaxCols = False, [[]], 0
|
||||
@ -74,12 +68,10 @@ class CanvasImportStore():
|
||||
return (True, None)
|
||||
else:
|
||||
return (False, "empty output map")
|
||||
# }}}
|
||||
# {{{ importAnsiFile(self, inPathName, encoding="cp437")
|
||||
|
||||
def importAnsiFile(self, inPathName, encoding="cp437"):
|
||||
return self.importAnsiBuffer(open(inPathName, "rb").read(), encoding)
|
||||
# }}}
|
||||
# {{{ importSauceFile(self, inPathName, encoding="cp437")
|
||||
|
||||
def importSauceFile(self, inPathName, encoding="cp437"):
|
||||
with open(inPathName, "rb") as inFile:
|
||||
inFileStat = os.stat(inPathName)
|
||||
@ -90,8 +82,7 @@ class CanvasImportStore():
|
||||
return self.importAnsiBuffer(inFileData, encoding, width)
|
||||
else:
|
||||
return (False, "only character based ANSi SAUCE files are supported")
|
||||
# }}}
|
||||
# {{{ importTextBuffer(self, inFile)
|
||||
|
||||
def importTextBuffer(self, inFile):
|
||||
try:
|
||||
inLine, outMap, outMaxCols = inFile.readline(), [], 0
|
||||
@ -110,19 +101,25 @@ class CanvasImportStore():
|
||||
inCurColours = (int(m[2]), int(m[3]))
|
||||
elif (m[2] != None) and (m[3] == None):
|
||||
inCurColours = (int(m[2]), int(inCurColours[1]))
|
||||
elif (m[2] == None) and (m[3] != None):
|
||||
inCurColours = (int(inCurColours[0]), int(m[3]))
|
||||
else:
|
||||
inCurColours = (15, -1)
|
||||
inCurCol += len(m[0])
|
||||
else:
|
||||
inCurColours = (15, -1); inCurCol += 1;
|
||||
elif inChar == "\u0006":
|
||||
inCellState = self._flipCellStateBit(self._CellState.CS_ITALIC, inCellState); inCurCol += 1;
|
||||
inCurCol += 1
|
||||
elif inChar == "\u000f":
|
||||
inCellState |= self._CellState.CS_NONE; inCurColours = (15, -1); inCurCol += 1;
|
||||
elif inChar == "\u0016":
|
||||
inCurColours = (inCurColours[1], inCurColours[0]); inCurCol += 1;
|
||||
elif inChar == "\u001f":
|
||||
inCellState = self._flipCellStateBit(self._CellState.CS_UNDERLINE, inCellState); inCurCol += 1;
|
||||
elif inChar == "\t":
|
||||
for tabChar in range(8 - len(outMap[-1]) % 8):
|
||||
outMap[-1].append([*inCurColours, inCellState, inChar])
|
||||
inCurCol += 1
|
||||
else:
|
||||
outMap[-1].append([*inCurColours, inCellState, inChar]); inCurCol += 1;
|
||||
inLine, outMaxCols = inFile.readline(), max(outMaxCols, len(outMap[-1]))
|
||||
@ -137,15 +134,11 @@ class CanvasImportStore():
|
||||
return (False, "empty output map")
|
||||
except:
|
||||
return (False, sys.exc_info()[1])
|
||||
# }}}
|
||||
# {{{ importTextFile(self, pathName)
|
||||
|
||||
def importTextFile(self, pathName):
|
||||
with open(pathName, "r", encoding="utf-8-sig") as inFile:
|
||||
return self.importTextBuffer(inFile)
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, inFile=None): initialisation method
|
||||
def __init__(self, inFile=None):
|
||||
self.inSize, self.outMap = None, None
|
||||
if inFile != None:
|
||||
|
@ -1,77 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# CanvasJournal.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
class CanvasJournal():
|
||||
# {{{ begin(self)
|
||||
def begin(self):
|
||||
deltaItem = [[], []]; self.patchesUndo.insert(self.patchesUndoLevel, deltaItem);
|
||||
# }}}
|
||||
# {{{ end(self)
|
||||
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;
|
||||
# }}}
|
||||
# {{{ popCursor(self)
|
||||
def popCursor(self):
|
||||
if len(self.patchesCursor):
|
||||
patchesCursor = self.patchesCursor; self.patchesCursor = [];
|
||||
return patchesCursor
|
||||
else:
|
||||
return []
|
||||
# }}}
|
||||
# {{{ popRedo(self)
|
||||
def popRedo(self):
|
||||
if self.patchesUndoLevel > 0:
|
||||
self.patchesUndoLevel -= 1; patches = self.patchesUndo[self.patchesUndoLevel];
|
||||
return patches[1]
|
||||
else:
|
||||
return []
|
||||
# }}}
|
||||
# {{{ popUndo(self)
|
||||
def popUndo(self):
|
||||
if self.patchesUndo[self.patchesUndoLevel] != None:
|
||||
patches = self.patchesUndo[self.patchesUndoLevel]; self.patchesUndoLevel += 1;
|
||||
return patches[0]
|
||||
else:
|
||||
return []
|
||||
# }}}
|
||||
# {{{ pushCursor(self, patches)
|
||||
def pushCursor(self, patches):
|
||||
self.patchesCursor.append(patches)
|
||||
# }}}
|
||||
# {{{ resetCursor(self)
|
||||
def resetCursor(self):
|
||||
if self.patchesCursor != None:
|
||||
self.patchesCursor.clear()
|
||||
self.patchesCursor = []
|
||||
# }}}
|
||||
# {{{ resetUndo(self)
|
||||
def resetUndo(self):
|
||||
if self.patchesUndo != None:
|
||||
self.patchesUndo.clear()
|
||||
self.patchesUndo = [None]; self.patchesUndoLevel = 0;
|
||||
# }}}
|
||||
# {{{ updateCurrentDeltas(self, redoPatches, undoPatches)
|
||||
def updateCurrentDeltas(self, redoPatches, undoPatches):
|
||||
self.patchesUndo[self.patchesUndoLevel][0].append(undoPatches)
|
||||
self.patchesUndo[self.patchesUndoLevel][1].append(redoPatches)
|
||||
# }}}
|
||||
|
||||
# {{{ __del__(self): destructor method
|
||||
def __del__(self):
|
||||
self.resetCursor(); self.resetUndo();
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self): initialisation method
|
||||
def __init__(self):
|
||||
self.patchesCursor, self.patchesUndo, self. patchesUndoLevel = None, None, None
|
||||
self.resetCursor(); self.resetUndo();
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
@ -5,7 +5,7 @@
|
||||
#
|
||||
|
||||
#
|
||||
# Colours: mIRC colour number to RGBA map given none of ^[BFV_] (bold, italic, reverse, underline)
|
||||
# Colours: mIRC colour number to RGBA map given none of ^[BV_] (bold, reverse, underline)
|
||||
#
|
||||
Colours = [
|
||||
[255, 255, 255, 255, "White"],
|
||||
|
@ -4,17 +4,19 @@
|
||||
# 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 GuiCanvasColours import Colours
|
||||
import math, os, platform, wx
|
||||
import math, os, platform, Rtl, wx
|
||||
|
||||
class GuiBufferedDC(wx.MemoryDC):
|
||||
# {{{ __del__(self)
|
||||
def __del__(self):
|
||||
self.dc.Blit(0, 0, *self.viewSize, self, 0, 0)
|
||||
self.SelectObject(wx.NullBitmap)
|
||||
# }}}
|
||||
# {{{ __init__(self, backend, buffer, clientSize, dc, viewRect)
|
||||
|
||||
def __init__(self, backend, buffer, clientSize, dc, viewRect):
|
||||
super().__init__()
|
||||
canvasSize = [a - b for a, b in zip(backend.canvasSize, viewRect)]
|
||||
@ -24,161 +26,228 @@ class GuiBufferedDC(wx.MemoryDC):
|
||||
viewSize = [m * n for m, n in zip(backend.cellSize, viewSize)]
|
||||
self.SelectObject(buffer); self.SetDeviceOrigin(*viewRect);
|
||||
self.dc, self.viewRect, self.viewSize = dc, viewRect, viewSize
|
||||
# }}}
|
||||
|
||||
class GuiCanvasWxBackend():
|
||||
# {{{ _drawBrushPatch(self, eventDc, patch, point)
|
||||
def _drawBrushPatch(self, eventDc, patch, point):
|
||||
absPoint = self._xlatePoint(point)
|
||||
brushBg, brushFg, pen = self._getBrushPatchColours(patch)
|
||||
self._setBrushDc(brushBg, brushFg, eventDc, pen)
|
||||
eventDc.DrawRectangle(*absPoint, *self.cellSize)
|
||||
# }}}
|
||||
# {{{ _drawCharPatch(self, eventDc, patch, point)
|
||||
def _drawCharPatch(self, eventDc, patch, point):
|
||||
absPoint, fontBitmap = self._xlatePoint(point), wx.Bitmap(*self.cellSize)
|
||||
brushBg, brushFg, pen = self._getCharPatchColours(patch)
|
||||
fontDc = wx.MemoryDC(); fontDc.SelectObject(fontBitmap); fontDc.SetFont(self._font);
|
||||
fontDc.SetBackground(brushBg); fontDc.SetBrush(brushFg); fontDc.SetPen(pen);
|
||||
fontDc.SetTextForeground(wx.Colour(Colours[patch[0]][:4]))
|
||||
fontDc.SetTextBackground(wx.Colour(Colours[patch[1]][:4]))
|
||||
fontDc.DrawRectangle(0, 0, *self.cellSize)
|
||||
if patch[3] == "_":
|
||||
fontDc.SetPen(self._pens[patch[0]])
|
||||
fontDc.DrawLine(0, self.cellSize[1] - 1, self.cellSize[0], self.cellSize[1] - 1)
|
||||
else:
|
||||
fontDc.DrawText(patch[3], 0, 0)
|
||||
eventDc.Blit(*absPoint, *self.cellSize, fontDc, 0, 0)
|
||||
# }}}
|
||||
# {{{ _finiBrushesAndPens(self)
|
||||
def _finiBrushesAndPens(self):
|
||||
[brush.Destroy() for brush in self._brushes or []]
|
||||
[pen.Destroy() for pen in self._pens or []]
|
||||
self._brushes, self._lastBrushBg, self._lastBrushFg, self._lastPen, self._pens = None, None, None, None, None
|
||||
# }}}
|
||||
# {{{ _getBrushPatchColours(self, patch)
|
||||
def _getBrushPatchColours(self, patch):
|
||||
if (patch[0] != -1) and (patch[1] != -1):
|
||||
brushBg, brushFg, pen = self._brushes[patch[1]], self._brushes[patch[1]], self._pens[patch[1]]
|
||||
elif (patch[0] == -1) and (patch[1] == -1):
|
||||
brushBg, brushFg, pen = self._brushAlpha, self._brushAlpha, self._penAlpha
|
||||
elif patch[0] == -1:
|
||||
brushBg, brushFg, pen = self._brushes[patch[1]], self._brushes[patch[1]], self._pens[patch[1]]
|
||||
elif patch[1] == -1:
|
||||
brushBg, brushFg, pen = self._brushAlpha, self._brushAlpha, self._penAlpha
|
||||
return (brushBg, brushFg, pen)
|
||||
# }}}
|
||||
# {{{ _getCharPatchColours(self, patch)
|
||||
def _getCharPatchColours(self, patch):
|
||||
if (patch[0] != -1) and (patch[1] != -1):
|
||||
brushBg, brushFg, pen = self._brushes[patch[1]], self._brushes[patch[1]], self._pens[patch[1]]
|
||||
elif (patch[0] == -1) and (patch[1] == -1):
|
||||
brushBg, brushFg, pen = self._brushAlpha, self._brushAlpha, self._penAlpha
|
||||
elif patch[0] == -1:
|
||||
brushBg, brushFg, pen = self._brushes[patch[1]], self._brushes[patch[1]], self._pens[patch[1]]
|
||||
elif patch[1] == -1:
|
||||
brushBg, brushFg, pen = self._brushAlpha, self._brushAlpha, self._penAlpha
|
||||
return (brushBg, brushFg, pen)
|
||||
# }}}
|
||||
# {{{ _initBrushesAndPens(self)
|
||||
def _initBrushesAndPens(self):
|
||||
self._brushes, self._pens = [None for x in range(len(Colours))], [None for x in range(len(Colours))]
|
||||
for mircColour in range(len(Colours)):
|
||||
self._brushes[mircColour] = wx.Brush(wx.Colour(Colours[mircColour][:4]), wx.BRUSHSTYLE_SOLID)
|
||||
self._pens[mircColour] = wx.Pen(wx.Colour(Colours[mircColour][:4]), 1)
|
||||
self._brushAlpha = wx.Brush(wx.Colour(Colours[14][:4]), wx.BRUSHSTYLE_SOLID)
|
||||
self._penAlpha = wx.Pen(wx.Colour(Colours[14][:4]), 1)
|
||||
self._lastBrushBg, self._lastBrushFg, self._lastPen = None, None, None
|
||||
# }}}
|
||||
# {{{ _setBrushDc(self, brushBg, brushFg, dc, pen)
|
||||
def _setBrushDc(self, brushBg, brushFg, dc, pen):
|
||||
if self._lastBrushBg != brushBg:
|
||||
dc.SetBackground(brushBg); self._lastBrushBg = brushBg;
|
||||
if self._lastBrushFg != brushFg:
|
||||
dc.SetBrush(brushFg); self._lastBrushFg = brushFg;
|
||||
if self._lastPen != pen:
|
||||
dc.SetPen(pen); self._lastPen = pen;
|
||||
# }}}
|
||||
# {{{ _xlatePoint(self, point)
|
||||
def _xlatePoint(self, point):
|
||||
return [a * b for a, b in zip(point, self.cellSize)]
|
||||
# }}}
|
||||
arabicShapes = {
|
||||
u'\u0621': (u'\uFE80'),
|
||||
u'\u0622': (u'\uFE81', None, None, u'\uFE82'),
|
||||
u'\u0623': (u'\uFE83', None, None, u'\uFE84'),
|
||||
u'\u0624': (u'\uFE85', None, None, u'\uFE86'),
|
||||
u'\u0625': (u'\uFE87', None, None, u'\uFE88'),
|
||||
u'\u0626': (u'\uFE89', u'\uFE8B', u'\uFE8C', u'\uFE8A'),
|
||||
u'\u0627': (u'\uFE8D', None, None, u'\uFE8E'),
|
||||
u'\u0628': (u'\uFE8F', u'\uFE91', u'\uFE92', u'\uFE90'),
|
||||
u'\u0629': (u'\uFE93', None, None, u'\uFE94'),
|
||||
u'\u062A': (u'\uFE95', u'\uFE97', u'\uFE98', u'\uFE96'),
|
||||
u'\u062B': (u'\uFE99', u'\uFE9B', u'\uFE9C', u'\uFE9A'),
|
||||
u'\u062C': (u'\uFE9D', u'\uFE9F', u'\uFEA0', u'\uFE9E'),
|
||||
u'\u062D': (u'\uFEA1', u'\uFEA3', u'\uFEA4', u'\uFEA2'),
|
||||
u'\u062E': (u'\uFEA5', u'\uFEA7', u'\uFEA8', u'\uFEA6'),
|
||||
u'\u062F': (u'\uFEA9', None, None, u'\uFEAA'),
|
||||
u'\u0630': (u'\uFEAB', None, None, u'\uFEAC'),
|
||||
u'\u0631': (u'\uFEAD', None, None, u'\uFEAE'),
|
||||
u'\u0632': (u'\uFEAF', None, None, u'\uFEB0'),
|
||||
u'\u0633': (u'\uFEB1', u'\uFEB3', u'\uFEB4', u'\uFEB2'),
|
||||
u'\u0634': (u'\uFEB5', u'\uFEB7', u'\uFEB8', u'\uFEB6'),
|
||||
u'\u0635': (u'\uFEB9', u'\uFEBB', u'\uFEBC', u'\uFEBA'),
|
||||
u'\u0636': (u'\uFEBD', u'\uFEBF', u'\uFEC0', u'\uFEBE'),
|
||||
u'\u0637': (u'\uFEC1', u'\uFEC3', u'\uFEC4', u'\uFEC2'),
|
||||
u'\u0638': (u'\uFEC5', u'\uFEC7', u'\uFEC8', u'\uFEC6'),
|
||||
u'\u0639': (u'\uFEC9', u'\uFECB', u'\uFECC', u'\uFECA'),
|
||||
u'\u063A': (u'\uFECD', u'\uFECF', u'\uFED0', u'\uFECE'),
|
||||
u'\u0640': (u'\u0640', None, None, None),
|
||||
u'\u0641': (u'\uFED1', u'\uFED3', u'\uFED4', u'\uFED2'),
|
||||
u'\u0642': (u'\uFED5', u'\uFED7', u'\uFED8', u'\uFED6'),
|
||||
u'\u0643': (u'\uFED9', u'\uFEDB', u'\uFEDC', u'\uFEDA'),
|
||||
u'\u0644': (u'\uFEDD', u'\uFEDF', u'\uFEE0', u'\uFEDE'),
|
||||
u'\u0645': (u'\uFEE1', u'\uFEE3', u'\uFEE4', u'\uFEE2'),
|
||||
u'\u0646': (u'\uFEE5', u'\uFEE7', u'\uFEE8', u'\uFEE6'),
|
||||
u'\u0647': (u'\uFEE9', u'\uFEEB', u'\uFEEC', u'\uFEEA'),
|
||||
u'\u0648': (u'\uFEED', None, None, u'\uFEEE'),
|
||||
u'\u0649': (u'\uFEEF', None, None, u'\uFEF0'),
|
||||
u'\u064A': (u'\uFEF1', u'\uFEF3', u'\uFEF4', u'\uFEF2'),
|
||||
}
|
||||
|
||||
# {{{ drawCursorMaskWithJournal(self, canvasJournal, eventDc, viewRect)
|
||||
def drawCursorMaskWithJournal(self, canvasJournal, eventDc, viewRect):
|
||||
[self.drawPatch(eventDc, patch, viewRect) for patch in canvasJournal.popCursor()]
|
||||
# }}}
|
||||
# {{{ drawPatch(self, eventDc, patch, viewRect)
|
||||
def drawPatch(self, eventDc, patch, viewRect):
|
||||
point = [m - n for m, n in zip(patch[:2], viewRect)]
|
||||
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
|
||||
if patch[5] == " ":
|
||||
if patch[3] == -1:
|
||||
self._drawCharPatch(eventDc, [*patch[2:-1], "░"], point)
|
||||
else:
|
||||
self._drawBrushPatch(eventDc, patch[2:], point)
|
||||
class _CellState():
|
||||
CS_NONE = 0x00
|
||||
CS_BOLD = 0x01
|
||||
CS_UNDERLINE = 0x02
|
||||
|
||||
def _blendColours(self, bg, fg):
|
||||
return [int((fg * 0.8) + (bg * (1.0 - 0.8))) for bg, fg in zip(Colours[bg][:3], Colours[fg][:3])]
|
||||
|
||||
def _finiBrushesAndPens(self):
|
||||
for wxObject in Rtl.flatten([
|
||||
(self._brushAlpha,), (*(self._brushes or ()),), (self._penAlpha,), (*(self._pens or ()),),
|
||||
*[[self._brushesBlend[bg][fg] for fg in self._brushesBlend[bg].keys()] for bg in self._brushesBlend.keys()],
|
||||
*[[self._pensBlend[bg][fg] for fg in self._pensBlend[bg].keys()] for bg in self._pensBlend.keys()]]):
|
||||
if wxObject != None:
|
||||
wxObject.Destroy()
|
||||
self._brushAlpha, self._brushes, self._brushesBlend, self._lastBrush, self._lastPen, self._penAlpha, self._pens, self._pensBlend = None, [], {}, None, None, None, [], {}
|
||||
|
||||
def _initBrushesAndPens(self):
|
||||
self._brushes, self._brushesBlend, self._lastBrush, self._lastPen, self._pens, self._pensBlend = [], {}, None, None, [], {}
|
||||
self._brushAlpha, self._penAlpha = wx.Brush(wx.Colour(48, 48, 48, 255), wx.BRUSHSTYLE_SOLID), wx.Pen(wx.Colour(48, 48, 48, 255), 1)
|
||||
for mircColour in range(len(Colours)):
|
||||
self._brushes += [wx.Brush(wx.Colour(Colours[mircColour][:4]), wx.BRUSHSTYLE_SOLID)]; self._brushesBlend[mircColour] = {};
|
||||
self._pens += [wx.Pen(wx.Colour(Colours[mircColour][:4]), 1)]; self._pensBlend[mircColour] = {};
|
||||
for mircColourFg in range(len(Colours)):
|
||||
colourBlend = self._blendColours(mircColour, mircColourFg)
|
||||
self._brushesBlend[mircColour][mircColourFg] = wx.Brush(wx.Colour(colourBlend), wx.BRUSHSTYLE_SOLID)
|
||||
self._pensBlend[mircColour][mircColourFg] = wx.Pen(wx.Colour(colourBlend), 1)
|
||||
|
||||
def _reshapeArabic(self, canvas, eventDc, isCursor, patch, point):
|
||||
lastCell, patches = point[0], []
|
||||
while True:
|
||||
if ((lastCell + 1) >= (canvas.size[0] - 1)) \
|
||||
or (not canvas.map[point[1]][lastCell + 1][3] in self.arabicShapes):
|
||||
break
|
||||
else:
|
||||
self._drawCharPatch(eventDc, patch[2:], point)
|
||||
return True
|
||||
lastCell += 1
|
||||
connect = False
|
||||
for runX in range(lastCell, point[0], -1):
|
||||
runCell = list(canvas.map[point[1]][runX])
|
||||
if runX == lastCell:
|
||||
if self.arabicShapes[runCell[3]][1] != None:
|
||||
runCell[3] = self.arabicShapes[runCell[3]][1]; connect = True;
|
||||
else:
|
||||
runCell[3] = self.arabicShapes[runCell[3]][0]; connect = False;
|
||||
else:
|
||||
if connect and (self.arabicShapes[runCell[3]][2] != None):
|
||||
runCell[3] = self.arabicShapes[runCell[3]][2]; connect = True;
|
||||
elif connect and (self.arabicShapes[runCell[3]][3] != None):
|
||||
runCell[3] = self.arabicShapes[runCell[3]][3]; connect = False;
|
||||
elif not connect and (self.arabicShapes[runCell[3]][1] != None):
|
||||
runCell[3] = self.arabicShapes[runCell[3]][1]; connect = True;
|
||||
else:
|
||||
runCell[3] = self.arabicShapes[runCell[3]][0]; connect = False;
|
||||
patches += [[runX, point[1], *runCell]]
|
||||
runCell = list(patch[2:])
|
||||
if connect and (self.arabicShapes[patch[5]][3] != None):
|
||||
runCell[3] = self.arabicShapes[patch[5]][3]
|
||||
else:
|
||||
return False
|
||||
# }}}
|
||||
# {{{ getDeviceContext(self, clientSize, parentWindow, viewRect)
|
||||
def getDeviceContext(self, clientSize, parentWindow, viewRect):
|
||||
runCell[3] = self.arabicShapes[patch[5]][0]
|
||||
patches += [[*point, *runCell]]
|
||||
return patches
|
||||
|
||||
def _setBrushColours(self, dc, isCursor, patch, patchBg):
|
||||
if ((patch[0] != -1) and (patch[1] != -1)) \
|
||||
or ((patch[0] == -1) and (patch[1] != -1)):
|
||||
if not isCursor:
|
||||
brush, pen = self._brushes[patch[1]], self._pens[patch[1]]
|
||||
else:
|
||||
bg = patchBg[1] if patchBg[1] != -1 else 14
|
||||
brush, pen = self._brushesBlend[bg][patch[1]], self._pensBlend[bg][patch[1]]
|
||||
else:
|
||||
if not isCursor:
|
||||
brush, pen = self._brushAlpha, self._penAlpha
|
||||
else:
|
||||
bg = patchBg[1] if patchBg[1] != -1 else 14
|
||||
brush, pen = self._brushesBlend[bg][14], self._pensBlend[bg][14]
|
||||
return brush, pen
|
||||
|
||||
def drawPatches(self, canvas, eventDc, patches, isCursor=False):
|
||||
patchesRender = []
|
||||
for patch in patches:
|
||||
point = patch[:2]
|
||||
if [(c >= 0) and (c < s) for c, s in zip(point, self.canvasSize)] == [True, True]:
|
||||
if patch[5] in self.arabicShapes:
|
||||
for patchReshaped in self._reshapeArabic(canvas, eventDc, isCursor, patch, point):
|
||||
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 = [], [], [], []
|
||||
eventDc.SetFont(self._font)
|
||||
for patchRender in patchesRender:
|
||||
if (patchRender[5] == " ") and (patchRender[3] == -1):
|
||||
text, textFg = "░", wx.Colour(0, 0, 0, 255)
|
||||
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 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] != " ":
|
||||
text, textFg = patchRender[5], wx.Colour(Colours[patchRender[2]][:4])
|
||||
elif patchRender[4] & self._CellState.CS_UNDERLINE:
|
||||
text, textFg = "_", wx.Colour(Colours[patchRender[2]][:4])
|
||||
else:
|
||||
text = None
|
||||
brush, pen = self._setBrushColours(eventDc, isCursor, patchRender[2:], canvas.map[patchRender[1]][patchRender[0]])
|
||||
rectangles[numPatch] = [patchRender[:2][0] * self.cellSize[0], patchRender[:2][1] * self.cellSize[1], self.cellSize[0], self.cellSize[1]];
|
||||
pens[numPatch] = pen; brushes[numPatch] = brush;
|
||||
if text != None:
|
||||
textList += [text]; coords += [[patchRender[:2][0] * self.cellSize[0], patchRender[:2][1] * self.cellSize[1]]]; foregrounds += [textFg]; backgrounds += [textBg];
|
||||
numPatch += 1
|
||||
eventDc.DrawRectangleList(rectangles, pens, brushes)
|
||||
eventDc.DrawTextList(textList, coords, foregrounds, backgrounds)
|
||||
|
||||
def getDeviceContext(self, clientSize, parentWindow, viewRect=None):
|
||||
if viewRect == None:
|
||||
viewRect = parentWindow.GetViewStart()
|
||||
if viewRect == (0, 0):
|
||||
eventDc = wx.BufferedDC(wx.ClientDC(parentWindow), self.canvasBitmap)
|
||||
else:
|
||||
eventDc = GuiBufferedDC(self, self.canvasBitmap, clientSize, wx.ClientDC(parentWindow), viewRect)
|
||||
self._lastBrushBg, self._lastBrushFg, self._lastPen = None, None, None
|
||||
self._lastBrush, self._lastPen = None, None
|
||||
return eventDc
|
||||
# }}}
|
||||
# {{{ onPaint(self, clientSize, panelWindow, viewRect)
|
||||
|
||||
def onPaint(self, clientSize, panelWindow, viewRect):
|
||||
if self.canvasBitmap != None:
|
||||
if viewRect == (0, 0):
|
||||
eventDc = wx.BufferedPaintDC(panelWindow, self.canvasBitmap)
|
||||
else:
|
||||
eventDc = GuiBufferedDC(self, self.canvasBitmap, clientSize, wx.PaintDC(panelWindow), viewRect)
|
||||
# }}}
|
||||
# {{{ resize(self, canvasSize, cellSize):
|
||||
def resize(self, canvasSize, cellSize):
|
||||
winSize = [a * b for a, b in zip(canvasSize, cellSize)]
|
||||
|
||||
def resize(self, canvasSize):
|
||||
if platform.system() == "Windows":
|
||||
self._font = wx.TheFontList.FindOrCreateFont(self.fontSize, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, self.fontName)
|
||||
fontInfoDesc = self._font.GetNativeFontInfoDesc().split(";"); fontInfoDesc[12] = "3";
|
||||
self._font.SetNativeFontInfo(";".join(fontInfoDesc))
|
||||
dc = wx.MemoryDC()
|
||||
dc.SetFont(self._font); self.cellSize = dc.GetTextExtent("_");
|
||||
dc.Destroy()
|
||||
else:
|
||||
self._font = wx.Font(self.fontSize, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
|
||||
dc = wx.MemoryDC()
|
||||
dc.SetFont(self._font); self.cellSize = dc.GetTextExtent("_");
|
||||
dc.Destroy()
|
||||
winSize = [a * b for a, b in zip(canvasSize, self.cellSize)]
|
||||
if self.canvasBitmap == None:
|
||||
self.canvasBitmap = wx.Bitmap(winSize)
|
||||
else:
|
||||
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, self.cellSize = canvasSize, cellSize
|
||||
if platform.system() == "Windows":
|
||||
self._font = wx.TheFontList.FindOrCreateFont(cellSize[0] + 1, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, self.fontName)
|
||||
else:
|
||||
self._font = wx.Font(cellSize[0] + 1, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
|
||||
# }}}
|
||||
# {{{ xlateEventPoint(self, event, eventDc, viewRect)
|
||||
self.canvasSize = canvasSize;
|
||||
if haveGuiCanvasWxBackendFast:
|
||||
GuiCanvasWxBackendFast.resize(tuple(self.cellSize), self._font, tuple(winSize))
|
||||
|
||||
def xlateEventPoint(self, event, eventDc, viewRect):
|
||||
eventPoint = event.GetLogicalPosition(eventDc)
|
||||
rectX, rectY = eventPoint.x - (eventPoint.x % self.cellSize[0]), eventPoint.y - (eventPoint.y % self.cellSize[1])
|
||||
mapX, mapY = int(rectX / self.cellSize[0] if rectX else 0), int(rectY / self.cellSize[1] if rectY else 0)
|
||||
return [m + n for m, n in zip((mapX, mapY), viewRect)]
|
||||
# }}}
|
||||
|
||||
# {{{ __del__(self): destructor method
|
||||
def __del__(self):
|
||||
if self.canvasBitmap != None:
|
||||
self.canvasBitmap.Destroy(); self.canvasBitmap = None;
|
||||
self._finiBrushesAndPens()
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, canvasSize, cellSize, fontName="Dejavu Sans Mono", fontPathName=os.path.join("assets", "fonts", "DejaVuSansMono.ttf")): initialisation method
|
||||
def __init__(self, canvasSize, cellSize, fontName="Dejavu Sans Mono", fontPathName=os.path.join("assets", "fonts", "DejaVuSansMono.ttf")):
|
||||
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 = None, None, fontName, fontPathName
|
||||
self.canvasBitmap, self.cellSize, self.fontName, self.fontPathName, self.fontSize = None, None, fontName, fontPathName, fontSize
|
||||
if platform.system() == "Windows":
|
||||
from ctypes import WinDLL
|
||||
WinDLL("gdi32.dll").AddFontResourceW(self.fontPathName.encode("utf16"))
|
||||
self._initBrushesAndPens(); self.resize(canvasSize, cellSize);
|
||||
self._initBrushesAndPens(); self.resize(canvasSize);
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
577
libgui/GuiCanvasWxBackendFast.cpp
Normal 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;
|
||||
}
|
@ -4,11 +4,10 @@
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
import os, sys, wx
|
||||
import os, sys, wx, wx.lib.agw.aui
|
||||
|
||||
#
|
||||
# Decorators
|
||||
# {{{ GuiCommandDecorator(targetObject)
|
||||
def GuiCommandDecorator(caption, label, icon, accel, initialState):
|
||||
def GuiCommandDecoratorOuter(targetObject):
|
||||
if callable(targetObject):
|
||||
@ -17,8 +16,7 @@ def GuiCommandDecorator(caption, label, icon, accel, initialState):
|
||||
targetObject.attrDict = {"caption": caption, "label": label, "icon": icon, "accel": accel, "initialState": initialState, "id": None}
|
||||
return targetObject
|
||||
return GuiCommandDecoratorOuter
|
||||
# }}}
|
||||
# {{{ GuiCommandListDecorator(targetObject)
|
||||
|
||||
def GuiCommandListDecorator(idx, caption, label, icon, accel, initialState):
|
||||
def GuiCommandListDecoratorOuter(targetObject):
|
||||
if callable(targetObject):
|
||||
@ -27,8 +25,7 @@ def GuiCommandListDecorator(idx, caption, label, icon, accel, initialState):
|
||||
targetObject.attrList.insert(0, {"caption": caption, "label": label, "icon": icon, "accel": accel, "initialState": initialState, "id": None, "idx": idx})
|
||||
return targetObject
|
||||
return GuiCommandListDecoratorOuter
|
||||
# }}}
|
||||
# {{{ GuiSelectDecorator(targetObject)
|
||||
|
||||
def GuiSelectDecorator(idx, caption, label, icon, accel, initialState):
|
||||
def GuiSelectDecoratorOuter(targetObject):
|
||||
if callable(targetObject):
|
||||
@ -38,8 +35,7 @@ def GuiSelectDecorator(idx, caption, label, icon, accel, initialState):
|
||||
targetObject.attrList.insert(0, {"caption": caption, "label": label, "icon": icon, "accel": accel, "initialState": initialState, "id": None, "idx": idx})
|
||||
return targetObject
|
||||
return GuiSelectDecoratorOuter
|
||||
# }}}
|
||||
# {{{ GuiSubMenuDecorator(targetObject)
|
||||
|
||||
def GuiSubMenuDecorator(caption, label, icon, accel, initialState):
|
||||
def GuiSubMenuDecoratorOuter(targetObject):
|
||||
if callable(targetObject):
|
||||
@ -49,7 +45,18 @@ def GuiSubMenuDecorator(caption, label, icon, accel, initialState):
|
||||
targetObject.attrDict = {"caption": caption, "label": label, "icon": icon, "accel": accel, "initialState": initialState, "id": None, "menu":None}
|
||||
return targetObject
|
||||
return GuiSubMenuDecoratorOuter
|
||||
# }}}
|
||||
|
||||
class GuiToolBarArtProvider(wx.lib.agw.aui.AuiDefaultToolBarArt):
|
||||
def DrawBackground(self, dc, wnd, _rect, horizontal=True):
|
||||
dc.SetBrush(wx.Brush(wx.Colour(240, 240, 240, 0), wx.BRUSHSTYLE_SOLID)); dc.SetPen(wx.Pen(wx.Colour(240, 240, 240, 0), 1));
|
||||
dc.DrawRectangle(*_rect)
|
||||
dc.SetPen(wx.Pen(wx.Colour(180, 180, 180, 0), 1))
|
||||
dc.DrawLine(0, _rect[3]-1, _rect[2], _rect[3]-1); dc.DrawLine(_rect[2]-1, 0, _rect[2]-1, _rect[3]-1);
|
||||
dc.SetPen(wx.Pen(wx.Colour(255, 255, 255, 0), 1))
|
||||
dc.DrawLine(0, 0, _rect[2]-1, 0); dc.DrawLine(0, 0, 0, _rect[3]-1);
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
#
|
||||
# Non-items (0xf000-0xffff)
|
||||
@ -57,13 +64,11 @@ NID_MENU_SEP = 0xf000
|
||||
NID_TOOLBAR_HSEP = 0xf001
|
||||
|
||||
class GuiFrame(wx.Frame):
|
||||
# {{{ _initIcon(self, iconPathName)
|
||||
def _initIcon(self, iconPathName):
|
||||
icon = wx.Icon()
|
||||
icon.CopyFromBitmap(wx.Bitmap(iconPathName, wx.BITMAP_TYPE_ANY))
|
||||
self.SetIcon(icon)
|
||||
# }}}
|
||||
# {{{ _initMenu(self, menuItem, menuWindow)
|
||||
|
||||
def _initMenu(self, menuItem, menuWindow):
|
||||
if menuItem == NID_MENU_SEP:
|
||||
menuWindow.AppendSeparator()
|
||||
@ -87,10 +92,8 @@ class GuiFrame(wx.Frame):
|
||||
menuItemWindow.Check(menuItem.attrDict["initialState"])
|
||||
else:
|
||||
menuItemWindow.Enable(menuItem.attrDict["initialState"])
|
||||
# }}}
|
||||
|
||||
# {{{ loadAccels(self, menus, toolBars)
|
||||
def loadAccels(self, menus, toolBars):
|
||||
def loadAccels(self, accelsIn, menus, toolBars):
|
||||
def loadAccels_(accels):
|
||||
nonlocal accelTableEntries
|
||||
accels_ = []
|
||||
@ -110,10 +113,9 @@ class GuiFrame(wx.Frame):
|
||||
self.itemsById[accel.attrDict["id"]] = accel
|
||||
self.Bind(wx.EVT_MENU, self.onMenu, id=accel.attrDict["id"])
|
||||
accelTableEntries = []
|
||||
[loadAccels_(menu[1:]) for menu in menus]; [loadAccels_(toolBar) for toolBar in toolBars];
|
||||
[loadAccels_(accel) for accel in accelsIn]; [loadAccels_(menu[1:]) for menu in menus]; [loadAccels_(toolBar) for toolBar in toolBars];
|
||||
self.SetAcceleratorTable(wx.AcceleratorTable(accelTableEntries))
|
||||
# }}}
|
||||
# {{{ loadBitmap(self, basePathName, descr, size=(16, 16))
|
||||
|
||||
def loadBitmap(self, basePathName, descr, size=(16, 16)):
|
||||
if descr == None:
|
||||
descr = ["", None, wx.ArtProvider.GetBitmap(wx.ART_HELP, wx.ART_TOOLBAR, size)]
|
||||
@ -126,8 +128,7 @@ class GuiFrame(wx.Frame):
|
||||
elif len(descr) == 3:
|
||||
descr = ("", None, descr[2])
|
||||
return descr
|
||||
# }}}
|
||||
# {{{ loadMenus(self, menus)
|
||||
|
||||
def loadMenus(self, menus):
|
||||
self.menuBar = wx.MenuBar()
|
||||
for menu in menus:
|
||||
@ -142,11 +143,11 @@ class GuiFrame(wx.Frame):
|
||||
self._initMenu(menuItem, menuWindow)
|
||||
self.menuBar.Append(menuWindow, menu[0])
|
||||
self.SetMenuBar(self.menuBar)
|
||||
# }}}
|
||||
# {{{ loadToolBars(self, toolBars)
|
||||
|
||||
def loadToolBars(self, toolBars):
|
||||
for toolBar in toolBars:
|
||||
self.toolBars.append(wx.ToolBar(self.panelSkin, -1, style=wx.TB_FLAT | wx.HORIZONTAL | wx.TB_NODIVIDER))
|
||||
self.toolBars.append(wx.lib.agw.aui.AuiToolBar(self, -1))
|
||||
self.toolBars[-1].SetArtProvider(GuiToolBarArtProvider())
|
||||
self.toolBars[-1].SetToolBitmapSize((16, 16))
|
||||
for toolBarItem in toolBar:
|
||||
if toolBarItem == NID_TOOLBAR_HSEP:
|
||||
@ -156,58 +157,53 @@ class GuiFrame(wx.Frame):
|
||||
toolBarItem.attrDict["id"] = wx.NewId()
|
||||
self.itemsById[toolBarItem.attrDict["id"]] = toolBarItem
|
||||
if hasattr(toolBarItem, "isSelect"):
|
||||
toolBarItemWindow = self.toolBars[-1].AddRadioTool(toolBarItem.attrDict["id"], toolBarItem.attrDict["caption"], toolBarItem.attrDict["icon"][2], shortHelp=toolBarItem.attrDict["label"])
|
||||
toolBarItemWindow = self.toolBars[-1].AddRadioTool(toolBarItem.attrDict["id"], toolBarItem.attrDict["caption"], toolBarItem.attrDict["icon"][2], wx.NullBitmap, short_help_string=toolBarItem.attrDict["caption"])
|
||||
else:
|
||||
toolBarItemWindow = self.toolBars[-1].AddTool(toolBarItem.attrDict["id"], toolBarItem.attrDict["caption"], toolBarItem.attrDict["icon"][2], shortHelp=toolBarItem.attrDict["label"])
|
||||
self.toolBarItemsById[toolBarItem.attrDict["id"]] = toolBarItemWindow
|
||||
toolBarItemWindow = self.toolBars[-1].AddTool(toolBarItem.attrDict["id"], toolBarItem.attrDict["caption"], toolBarItem.attrDict["icon"][2], wx.NullBitmap, wx.ITEM_NORMAL, short_help_string=toolBarItem.attrDict["caption"])
|
||||
self.toolBarItemsById[toolBarItem.attrDict["id"]] = (self.toolBars[-1], toolBarItemWindow,)
|
||||
self.Bind(wx.EVT_TOOL, self.onMenu, toolBarItemWindow)
|
||||
self.Bind(wx.EVT_TOOL_RCLICKED, self.onMenu, toolBarItemWindow)
|
||||
if toolBarItem.attrDict["initialState"] != None:
|
||||
if hasattr(toolBarItem, "isSelect"):
|
||||
toolBarItemWindow.Toggle(toolBarItem.attrDict["initialState"])
|
||||
if toolBarItem.attrDict["initialState"]:
|
||||
self.toolBars[-1].ToggleTool(toolBarItemWindow, True)
|
||||
else:
|
||||
toolBarItemWindow.Enable(toolBarItem.attrDict["initialState"])
|
||||
self.toolBars[-1].EnableTool(toolBarItem.attrDict["id"], toolBarItem.attrDict["initialState"])
|
||||
self.toolBars[-1].Refresh()
|
||||
self.toolBarPanes, row = [], 0
|
||||
for toolBar in self.toolBars:
|
||||
self.sizerSkin.Add(toolBar, 0, wx.ALIGN_LEFT | wx.ALL, 3)
|
||||
toolBar.Realize(); toolBar.Fit();
|
||||
# }}}
|
||||
# {{{ addWindow(self, window, border=14, expand=False)
|
||||
def addWindow(self, window, border=14, expand=False):
|
||||
flags = wx.ALL; flags = flags | wx.EXPAND if expand else flags;
|
||||
self.sizerSkin.Add(window, 0, flags, border); self.sizerSkin.Fit(self.panelSkin);
|
||||
# }}}
|
||||
# {{{ onChar(self, event)
|
||||
self.toolBarPanes += [wx.lib.agw.aui.AuiPaneInfo().ToolbarPane().CaptionVisible(False).CloseButton(False).Dockable(True).Floatable(True).Gripper(True).Row(row).Top()]
|
||||
self.auiManager.AddPane(toolBar, self.toolBarPanes[-1]); row += 1;
|
||||
self.auiManager.Update()
|
||||
|
||||
def addWindow(self, window):
|
||||
self.auiManager.AddPane(window, wx.lib.agw.aui.AuiPaneInfo().CaptionVisible(False).Centre().CloseButton(False).Dockable(False).Floatable(False).Gripper(False))
|
||||
self.auiManager.Update()
|
||||
|
||||
def onChar(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onMenu(self, event)
|
||||
|
||||
def onMenu(self, event):
|
||||
eventId = event.GetId()
|
||||
if eventId in self.itemsById:
|
||||
self.itemsById[eventId](event); wx.SafeYield();
|
||||
self.itemsById[eventId](event)
|
||||
else:
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onMouseWheel(self, event)
|
||||
|
||||
def onMouseWheel(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, iconPathName, size, parent=None, title=""): initialisation method
|
||||
def __init__(self, iconPathName, size, parent=None, title=""):
|
||||
super().__init__(parent, wx.ID_ANY, title, size=size)
|
||||
self.itemsById, self.menuItemsById, self.toolBarItemsById = {}, {}, {}
|
||||
self.panelSkin, self.sizerSkin, self.toolBars = wx.Panel(self, wx.ID_ANY), wx.BoxSizer(wx.VERTICAL), []
|
||||
self.sizerSkin.AddSpacer(5); self.panelSkin.SetSizer(self.sizerSkin); self.panelSkin.SetAutoLayout(1);
|
||||
self.auiManager = wx.lib.agw.aui.AuiManager(); self.auiManager.SetManagedWindow(self);
|
||||
self.itemsById, self.menuItemsById, self.toolBarItemsById, self.toolBars = {}, {}, {}, []
|
||||
self._initIcon(iconPathName); self.statusBar = self.CreateStatusBar();
|
||||
self.sizerSkin.Fit(self.panelSkin); self.SetFocus(); self.Show(True);
|
||||
self.SetFocus(); self.Show(True);
|
||||
for event, f in ((wx.EVT_CHAR, self.onChar), (wx.EVT_MENU, self.onMenu), (wx.EVT_MOUSEWHEEL, self.onMouseWheel)):
|
||||
self.Bind(event, f)
|
||||
|
||||
class GuiMiniFrame(wx.MiniFrame):
|
||||
#
|
||||
# __init__(self, parent, size, title, pos=wx.DefaultPosition): initialisation method
|
||||
def __init__(self, parent, size, title, pos=wx.DefaultPosition):
|
||||
super().__init__(parent, id=wx.ID_ANY, pos=pos, size=size, title=title)
|
||||
|
||||
|
@ -7,69 +7,57 @@
|
||||
import wx
|
||||
|
||||
class GuiWindow(wx.ScrolledWindow):
|
||||
# {{{ _updateScrollBars(self)
|
||||
def _updateScrollBars(self):
|
||||
if self.size != None:
|
||||
clientSize = self.GetClientSize()
|
||||
if (self.scrollStep != None) and (self.size != None):
|
||||
self.SetScrollRate(*self.scrollStep); clientSize = self.GetClientSize();
|
||||
if (self.size[0] > clientSize[0]) or (self.size[1] > clientSize[1]):
|
||||
self.scrollFlag = True; super().SetVirtualSize(self.size);
|
||||
elif self.scrollFlag \
|
||||
and ((self.size[0] <= clientSize[0]) or (self.size[1] <= clientSize[1])):
|
||||
self.scrollFlag = False; super().SetVirtualSize((0, 0));
|
||||
# }}}
|
||||
|
||||
# {{{ onClose(self, event)
|
||||
def onClose(self, event):
|
||||
self.Destroy()
|
||||
# }}}
|
||||
# {{{ onEnterWindow(self, event)
|
||||
|
||||
def onEnterWindow(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onKeyboardInput(self, event)
|
||||
|
||||
def onKeyboardInput(self, event):
|
||||
return False
|
||||
# }}}
|
||||
# {{{ onLeaveWindow(self, event)
|
||||
|
||||
def onLeaveWindow(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onMouseInput(self, event)
|
||||
|
||||
def onMouseInput(self, event):
|
||||
return False
|
||||
# }}}
|
||||
# {{{ onPaint(self, event)
|
||||
|
||||
def onPaint(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onScroll(self, event)
|
||||
|
||||
def onScroll(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onSize(self, event)
|
||||
|
||||
def onSize(self, event):
|
||||
self._updateScrollBars(); event.Skip();
|
||||
# }}}
|
||||
# {{{ resize(self, newSize)
|
||||
|
||||
def resize(self, newSize):
|
||||
self.size = newSize; self._updateScrollBars();
|
||||
self.SetMinSize(self.size); self.SetSize(wx.DefaultCoord, wx.DefaultCoord, *self.size);
|
||||
self.SetMinSize(self.parent.GetSize()); self.SetSize(wx.DefaultCoord, wx.DefaultCoord, *self.parent.GetSize())
|
||||
curWindow = self
|
||||
while curWindow != None:
|
||||
curWindow.Layout(); curWindow = curWindow.GetParent();
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, parent, pos, scrollStep, style=0): initialisation method
|
||||
def __init__(self, parent, pos, scrollStep, style=0):
|
||||
def __init__(self, parent, pos, style=0):
|
||||
super().__init__(parent, pos=pos, style=style) if style != 0 else super().__init__(parent, pos=pos)
|
||||
self.pos, self.scrollFlag, self.scrollStep, self.size = pos, False, scrollStep, None
|
||||
self.parent = parent
|
||||
self.pos, self.scrollFlag, self.scrollStep, self.size = pos, False, None, None
|
||||
for eventType, f in (
|
||||
(wx.EVT_CHAR, self.onKeyboardInput), (wx.EVT_CLOSE, self.onClose), (wx.EVT_ENTER_WINDOW, self.onEnterWindow),
|
||||
(wx.EVT_LEAVE_WINDOW, self.onLeaveWindow), (wx.EVT_LEFT_DOWN, self.onMouseInput), (wx.EVT_MOTION, self.onMouseInput),
|
||||
(wx.EVT_PAINT, self.onPaint), (wx.EVT_RIGHT_DOWN, self.onMouseInput), (wx.EVT_SCROLLWIN_LINEDOWN, self.onScroll),
|
||||
(wx.EVT_SCROLLWIN_LINEUP, self.onScroll), (wx.EVT_SIZE, self.onSize)):
|
||||
self.Bind(eventType, f)
|
||||
self.SetScrollRate(*self.scrollStep); self._updateScrollBars();
|
||||
self._updateScrollBars()
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -5,12 +5,9 @@
|
||||
#
|
||||
|
||||
class Operator(object):
|
||||
#
|
||||
# apply(self, region)
|
||||
def apply(self, region):
|
||||
pass
|
||||
|
||||
# __init__(self, *args): initialisation method
|
||||
def __init__(self, *args):
|
||||
pass
|
||||
|
||||
|
@ -6,16 +6,26 @@
|
||||
|
||||
from Operator import Operator
|
||||
|
||||
# TODO <https://en.wikipedia.org/wiki/Box_Drawing_(Unicode_block)>
|
||||
|
||||
class OperatorFlipHorizontal(Operator):
|
||||
name = "Flip horizontally"
|
||||
flipPairs = {
|
||||
"/":"\\", "╱":"╲",
|
||||
"▀":"▄", "▁":"▔", "▖":"▘", "▗":"▝",
|
||||
"▙":"▛", "▚":"▞", "▜":"▟",
|
||||
}
|
||||
|
||||
#
|
||||
# apply(self, region)
|
||||
def apply(self, region):
|
||||
region.reverse(); return region;
|
||||
region.reverse()
|
||||
for numRow in range(len(region)):
|
||||
for numCol in range(len(region[numRow])):
|
||||
if region[numRow][numCol][3] in self.flipPairs:
|
||||
region[numRow][numCol][3] = self.flipPairs[region[numRow][numCol][3]]
|
||||
return region
|
||||
|
||||
# __init__(self, *args): initialisation method
|
||||
def __init__(self, *args):
|
||||
pass
|
||||
for flipPairKey in list(self.flipPairs.keys()):
|
||||
self.flipPairs[self.flipPairs[flipPairKey]] = flipPairKey
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -6,18 +6,27 @@
|
||||
|
||||
from Operator import Operator
|
||||
|
||||
# TODO <https://en.wikipedia.org/wiki/Box_Drawing_(Unicode_block)>
|
||||
|
||||
class OperatorFlipVertical(Operator):
|
||||
name = "Flip"
|
||||
flipPairs = {
|
||||
"(":")", "/":"\\", "╱":"╲", "[":"]", "{":"}", "<":">", "`":"'",
|
||||
"▌":"▐", "▏":"▕",
|
||||
"▖":"▗", "▘":"▝",
|
||||
"▟":"▙", "▛":"▜", "▚":"▞",
|
||||
}
|
||||
|
||||
#
|
||||
# apply(self, region)
|
||||
def apply(self, region):
|
||||
for numRow in range(len(region)):
|
||||
region[numRow].reverse()
|
||||
for numCol in range(len(region[numRow])):
|
||||
if region[numRow][numCol][3] in self.flipPairs:
|
||||
region[numRow][numCol][3] = self.flipPairs[region[numRow][numCol][3]]
|
||||
return region
|
||||
|
||||
# __init__(self, *args): initialisation method
|
||||
def __init__(self, *args):
|
||||
pass
|
||||
for flipPairKey in list(self.flipPairs.keys()):
|
||||
self.flipPairs[self.flipPairs[flipPairKey]] = flipPairKey
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -9,15 +9,12 @@ from Operator import Operator
|
||||
class OperatorInvert(Operator):
|
||||
name = "Invert colours"
|
||||
|
||||
#
|
||||
# apply(self, region)
|
||||
def apply(self, region):
|
||||
for numRow in range(len(region)):
|
||||
for numCol in range(len(region[numRow])):
|
||||
region[numRow][numCol][0:2] = [(~r & (16 - 1) if r > 0 else r) for r in region[numRow][numCol][0:2]]
|
||||
return region
|
||||
|
||||
# __init__(self, *args): initialisation method
|
||||
def __init__(self, *args):
|
||||
pass
|
||||
|
||||
|
35
liboperators/OperatorRotate.py
Normal file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# OperatorRotate.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
from Operator import Operator
|
||||
import math
|
||||
|
||||
class OperatorRotate(Operator):
|
||||
name = "Rotate"
|
||||
|
||||
def apply2(self, mapPoint, mousePoint, regionOld, region):
|
||||
if self.originPoint == None:
|
||||
self.originPoint = list(mousePoint)
|
||||
delta = [b - a for a, b in zip(self.originPoint, mousePoint)]
|
||||
radius = math.sqrt(math.pow(delta[0], 2) + math.pow(delta[1], 2))
|
||||
if radius >= 10:
|
||||
regionSize = (len(region[0]), len(region))
|
||||
theta = math.atan2(-delta[1], delta[0]); cos, sin = math.cos(theta), math.sin(theta);
|
||||
for numCol in range(regionSize[0]):
|
||||
for numRow in range(regionSize[1]):
|
||||
numRow_, numCol_ = (numRow / regionSize[1]) * 2 - 1, (numCol / regionSize[0]) * 2 - 1
|
||||
b, a = (numCol_ * sin) + (numRow_ * cos), (numCol_ * cos) - (numRow_ * sin)
|
||||
numRow_, numCol_ = int((b + 1) / 2 * regionSize[1]), int((a + 1) / 2 * regionSize[0])
|
||||
if (numRow_ < regionSize[1]) and (numCol_ < regionSize[0]):
|
||||
region[numRow][numCol] = list(regionOld[numRow_][numCol_])
|
||||
return region
|
||||
else:
|
||||
return region
|
||||
|
||||
def __init__(self, *args):
|
||||
self.originPoint = None
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
36
liboperators/OperatorTile.py
Normal file
@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# OperatorTile.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
from Operator import Operator
|
||||
import copy
|
||||
|
||||
class OperatorTile(Operator):
|
||||
name = "Tile"
|
||||
|
||||
def apply2(self, mapPoint, mousePoint, regionOld, region):
|
||||
if self.lastPoint == None:
|
||||
self.lastPoint = list(mapPoint)
|
||||
if self.tileObject == None:
|
||||
self.tileObject = copy.deepcopy(region)
|
||||
delta = [b - a for a, b in zip(self.lastPoint, mapPoint)]
|
||||
if delta[1] > 0:
|
||||
for numNewRow in range(delta[1]):
|
||||
newRow = copy.deepcopy(self.tileObject[len(region) % len(self.tileObject)])
|
||||
if len(newRow) < len(region[0]):
|
||||
for numNewCol in range(len(newRow), len(region[0])):
|
||||
newRow += [list(self.tileObject[len(region) % len(self.tileObject)][numNewCol % len(self.tileObject[len(region) % len(self.tileObject)])])]
|
||||
region += [newRow]
|
||||
if delta[0] > 0:
|
||||
for numRow in range(len(region)):
|
||||
for numNewCol in range(len(region[numRow]), len(region[numRow]) + delta[0]):
|
||||
region[numRow] += [list(self.tileObject[numRow % len(self.tileObject)][numNewCol % len(self.tileObject[numRow % len(self.tileObject)])])]
|
||||
self.lastPoint = list(mapPoint)
|
||||
return region
|
||||
|
||||
def __init__(self, *args):
|
||||
self.lastPoint, self.tileObject = None, None
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
@ -7,15 +7,10 @@
|
||||
from Canvas import Canvas
|
||||
from GuiFrame import GuiMiniFrame
|
||||
from GuiWindow import GuiWindow
|
||||
from RtlPlatform import getLocalConfPathName
|
||||
import json, os, sys, wx
|
||||
|
||||
class RoarAssetsWindow(GuiMiniFrame):
|
||||
# {{{ _drawPatch(self, eventDc, isCursor, patch, viewRect)
|
||||
def _drawPatch(self, eventDc, isCursor, patch, viewRect):
|
||||
if not isCursor:
|
||||
self.backend.drawPatch(eventDc, patch, viewRect)
|
||||
# }}}
|
||||
# {{{ _import(self, f, pathName)
|
||||
def _import(self, f, pathName):
|
||||
rc = False
|
||||
self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
@ -28,22 +23,22 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
rc, error, newMap, newPathName, newSize = False, str(e), None, None, None
|
||||
self.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
return rc, error, canvas, newMap, newPathName, newSize
|
||||
# }}}
|
||||
# {{{ _importFiles(self, f, wildcard)
|
||||
|
||||
def _importFiles(self, f, wildcard):
|
||||
resultList = []
|
||||
with wx.FileDialog(self, "Load...", os.getcwd(), "", wildcard, wx.FD_MULTIPLE | wx.FD_OPEN) as dialog:
|
||||
if self.lastDir != None:
|
||||
dialog.SetDirectory(self.lastDir)
|
||||
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||
resultList += [[False, "(cancelled)", None, None, None, None]]
|
||||
resultList += [[None, "(cancelled)", None, None, None, None]]
|
||||
else:
|
||||
for pathName in dialog.GetPaths():
|
||||
resultList += [self._import(f, pathName)]
|
||||
self.lastDir = os.path.dirname(pathName)
|
||||
lastDir = os.path.dirname(pathName)
|
||||
if self.lastDir != lastDir:
|
||||
self.lastDir = lastDir; self._storeLastDir(self.lastDir);
|
||||
return resultList
|
||||
# }}}
|
||||
# {{{ _load_list(self, pathName)
|
||||
|
||||
def _load_list(self, pathName):
|
||||
try:
|
||||
with open(pathName, "r") as fileObject:
|
||||
@ -84,8 +79,40 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
self.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
with wx.MessageDialog(self, "Error: {}".format(str(e)), "", wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
# }}}
|
||||
# {{{ _updateScrollBars(self)
|
||||
|
||||
def _loadLastDir(self):
|
||||
localConfFileName = getLocalConfPathName("RecentAssetsDir.txt")
|
||||
if os.path.exists(localConfFileName):
|
||||
with open(localConfFileName, "r", encoding="utf-8") as inFile:
|
||||
self.lastDir = inFile.read().rstrip("\r\n")
|
||||
|
||||
def _removeAsset(self, idx):
|
||||
del self.canvasList[idx]; self.listView.DeleteItem(idx);
|
||||
itemCount = self.listView.GetItemCount()
|
||||
if itemCount > 0:
|
||||
self.listView.Select(self.currentIndex, on=0)
|
||||
for numCanvas in [n for n in sorted(self.canvasList.keys()) if n >= idx]:
|
||||
self.canvasList[numCanvas - 1] = self.canvasList[numCanvas]; del self.canvasList[numCanvas];
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE) for col in (0, 1)]
|
||||
if (idx == 0) or (idx >= itemCount):
|
||||
idx = 0 if itemCount > 0 else None
|
||||
else:
|
||||
idx = idx if idx < itemCount else None
|
||||
self.currentIndex = idx
|
||||
if self.currentIndex != None:
|
||||
self.listView.Select(self.currentIndex, on=1)
|
||||
self.drawCanvas(self.canvasList[self.currentIndex][0])
|
||||
else:
|
||||
self.currentIndex = None
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE_USEHEADER) for col in (0, 1)]
|
||||
self.drawCanvas(Canvas((0, 0)))
|
||||
return self.currentIndex
|
||||
|
||||
def _storeLastDir(self, pathName):
|
||||
localConfFileName = getLocalConfPathName("RecentAssetsDir.txt")
|
||||
with open(localConfFileName, "w", encoding="utf-8") as outFile:
|
||||
print(pathName, file=outFile)
|
||||
|
||||
def _updateScrollBars(self):
|
||||
clientSize = self.panelCanvas.GetClientSize()
|
||||
if self.currentIndex != None:
|
||||
@ -99,27 +126,26 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
elif self.scrollFlag \
|
||||
and ((panelSize[0] <= clientSize[0]) or (panelSize[1] <= clientSize[1])):
|
||||
self.scrollFlag = False; super(wx.ScrolledWindow, self.panelCanvas).SetVirtualSize((0, 0));
|
||||
# }}}
|
||||
|
||||
# {{{ drawCanvas(self, canvas)
|
||||
def drawCanvas(self, canvas):
|
||||
panelSize = [a * b for a, b in zip(canvas.size, self.cellSize)]
|
||||
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
|
||||
while curWindow != None:
|
||||
curWindow.Layout(); curWindow = curWindow.GetParent();
|
||||
self.backend.resize(canvas.size, self.cellSize)
|
||||
viewRect = self.panelCanvas.GetViewStart();
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas, viewRect)
|
||||
self.backend.resize(canvas.size)
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas)
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
patches = []
|
||||
for numRow in range(canvas.size[1]):
|
||||
for numCol in range(canvas.size[0]):
|
||||
self.backend.drawPatch(eventDc, [numCol, numRow, *canvas.map[numRow][numCol]], viewRect)
|
||||
# }}}
|
||||
# {{{ onPaint(self, event)
|
||||
patches += [[numCol, numRow, *canvas.map[numRow][numCol]]]
|
||||
self.backend.drawPatches(canvas, eventDc, patches, isCursor=False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
|
||||
def onPaint(self, event):
|
||||
self.backend.onPaint(self.panelCanvas.GetClientSize(), self.panelCanvas, self.panelCanvas.GetViewStart())
|
||||
# }}}
|
||||
# {{{ onPanelLeftDown(self, event)
|
||||
|
||||
def onPanelLeftDown(self, event):
|
||||
self.panelCanvas.SetFocus()
|
||||
if (self.currentIndex != None):
|
||||
@ -129,60 +155,58 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
dropSource.SetData(textDataObject)
|
||||
result = dropSource.DoDragDrop(True)
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onPanelPaint(self, event)
|
||||
|
||||
def onPanelPaint(self, event):
|
||||
self.backend.onPaint(self.panelCanvas.GetClientSize(), self.panelCanvas, self.panelCanvas.GetViewStart())
|
||||
# }}}
|
||||
# {{{ onPanelSize(self, event)
|
||||
|
||||
def onPanelSize(self, event):
|
||||
self._updateScrollBars(); event.Skip();
|
||||
# }}}
|
||||
# {{{ resize(self, canvas, newSize)
|
||||
|
||||
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):
|
||||
panelSize = [a * b for a, b in zip(canvas.size, self.cellSize)]
|
||||
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
|
||||
while curWindow != None:
|
||||
curWindow.Layout(); curWindow = curWindow.GetParent();
|
||||
self.backend.resize(newSize, self.cellSize)
|
||||
viewRect = self.panelCanvas.GetViewStart(); eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas, viewRect);
|
||||
self.backend.resize(newSize)
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas)
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
patches = []
|
||||
if deltaSize[0] > 0:
|
||||
for numRow in range(oldSize[1]):
|
||||
for numNewCol in range(oldSize[0], newSize[0]):
|
||||
self._drawPatch(eventDc, False, [numNewCol, numRow, 1, 1, 0, " "], viewRect)
|
||||
patches += [[numNewCol, numRow, 1, 1, 0, " "]]
|
||||
if deltaSize[1] > 1:
|
||||
for numNewRow in range(oldSize[1], newSize[1]):
|
||||
for numNewCol in range(newSize[0]):
|
||||
self._drawPatch(eventDc, False, [numNewCol, numNewRow, 1, 1, 0, " "], viewRect)
|
||||
# }}}
|
||||
# {{{ update(self, canvas, newSize, newCanvas=None)
|
||||
patches += [[numNewCol, numNewRow, 1, 1, 0, " "]]
|
||||
self.backend.drawPatches(canvas, eventDc, patches, isCursor=False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
|
||||
def update(self, canvas, newSize, newCanvas=None):
|
||||
self.resize(canvas, newSize);
|
||||
canvas.update(newSize, newCanvas); viewRect = self.panelCanvas.GetViewStart();
|
||||
viewRect = self.panelCanvas.GetViewStart();
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas, viewRect)
|
||||
canvas.update(newSize, newCanvas);
|
||||
eventDc = self.backend.getDeviceContext(self.panelCanvas.GetClientSize(), self.panelCanvas)
|
||||
eventDcOrigin = eventDc.GetDeviceOrigin(); eventDc.SetDeviceOrigin(0, 0);
|
||||
patches = []
|
||||
for numRow in range(canvas.size[1]):
|
||||
for numCol in range(canvas.size[0]):
|
||||
self.backend.drawPatch(eventDc, [numCol, numRow, *canvas.map[numRow][numCol]], viewRect)
|
||||
# }}}
|
||||
patches += [[numCol, numRow, *canvas.map[numRow][numCol]]]
|
||||
self.backend.drawPatches(canvas, eventDc, patches, isCursor=False)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
|
||||
# {{{ onImportAnsi(self, event)
|
||||
def onImportAnsi(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onImportFromClipboard(self, event)
|
||||
|
||||
def onImportFromClipboard(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onImportSauce(self, event)
|
||||
|
||||
def onImportSauce(self, event):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onChar(self, event)
|
||||
|
||||
def onChar(self, event):
|
||||
if (event.GetModifiers() == wx.MOD_NONE) \
|
||||
and (event.GetKeyCode() in (wx.WXK_DOWN, wx.WXK_UP)):
|
||||
@ -190,24 +214,35 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
return wx.PostEvent(self.listView, event)
|
||||
else:
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onListViewChar(self, event)
|
||||
|
||||
def onClearList(self, event):
|
||||
while len(self.canvasList):
|
||||
self._removeAsset(list(self.canvasList.keys())[0])
|
||||
|
||||
def onListViewChar(self, event):
|
||||
index, rc = self.listView.GetFirstSelected(), False
|
||||
if index != -1:
|
||||
keyChar, keyModifiers = event.GetKeyCode(), event.GetModifiers()
|
||||
if (keyChar, keyModifiers) == (wx.WXK_DELETE, wx.MOD_NONE):
|
||||
self.currentIndex, rc = index, True; self.onRemove(None);
|
||||
if not rc:
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onListViewItemSelected(self, event)
|
||||
keyCode = event.GetKeyCode()
|
||||
if (event.GetModifiers() == wx.MOD_NONE) \
|
||||
and (keyCode in (wx.WXK_DOWN, wx.WXK_UP)) \
|
||||
and (self.currentIndex != None):
|
||||
self.listView.Select(self.currentIndex, on=0)
|
||||
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:
|
||||
keyChar, keyModifiers = event.GetKeyCode(), event.GetModifiers()
|
||||
if (keyChar, keyModifiers) == (wx.WXK_DELETE, wx.MOD_NONE):
|
||||
self.currentIndex, rc = index, True; self.onRemove(None);
|
||||
if not rc:
|
||||
event.Skip()
|
||||
|
||||
def onListViewItemSelected(self, event):
|
||||
self.currentIndex = event.GetItem().GetId()
|
||||
item = [self.listView.GetItem(self.currentIndex, col).GetText() for col in (0, 1)]
|
||||
self.drawCanvas(self.canvasList[self.currentIndex][0])
|
||||
# }}}
|
||||
# {{{ onListViewRightDown(self, event)
|
||||
|
||||
def onListViewRightDown(self, event):
|
||||
eventPoint = event.GetPosition()
|
||||
if self.currentIndex == None:
|
||||
@ -218,15 +253,18 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
self.contextMenuItems[4].Enable(False)
|
||||
else:
|
||||
self.contextMenuItems[4].Enable(True)
|
||||
if len(self.canvasList) == 0:
|
||||
self.contextMenuItems[7].Enable(False)
|
||||
else:
|
||||
self.contextMenuItems[7].Enable(True)
|
||||
self.PopupMenu(self.contextMenu, eventPoint)
|
||||
# }}}
|
||||
# {{{ onLoad(self, event)
|
||||
|
||||
def onLoad(self, event):
|
||||
def importmIRC(canvas, pathName):
|
||||
rc, error = canvas.importStore.importTextFile(pathName)
|
||||
return (rc, error, canvas.importStore.outMap, pathName, canvas.importStore.inSize)
|
||||
for rc, error, canvas, newMap, newPathName, newSize in self._importFiles(importmIRC, "mIRC art files (*.txt)|*.txt|All Files (*.*)|*.*"):
|
||||
if rc:
|
||||
if rc == True:
|
||||
self.currentIndex = self.listView.GetItemCount()
|
||||
self.canvasList[self.currentIndex] = [canvas, newPathName]
|
||||
self.listView.InsertItem(self.currentIndex, "")
|
||||
@ -241,43 +279,35 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
self.listView.SetFocus()
|
||||
[self.listView.SetItem(self.currentIndex, col, label) for col, label in zip((0, 1), (os.path.basename(newPathName), "{}x{}".format(*newSize)))]
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE) for col in (0, 1)]
|
||||
else:
|
||||
elif rc == False:
|
||||
with wx.MessageDialog(self, "Error: {}".format(error), "", wx.CANCEL | wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
if dialogChoice == wx.ID_CANCEL:
|
||||
break
|
||||
# }}}
|
||||
# {{{ onLoadList(self, event)
|
||||
|
||||
def onLoadList(self, event):
|
||||
rc = True
|
||||
with wx.FileDialog(self, "Load from list...", os.getcwd(), "", "List files (*.lst)|*.lst|Text files (*.txt)|*.txt|All Files (*.*)|*.*", wx.FD_OPEN) as dialog:
|
||||
if self.lastDir != None:
|
||||
dialog.SetDirectory(self.lastDir)
|
||||
if dialog.ShowModal() != wx.ID_CANCEL:
|
||||
pathName = dialog.GetPath(); self.lastDir = os.path.dirname(pathName);
|
||||
pathName = dialog.GetPath()
|
||||
self.lastDir = os.path.dirname(pathName); self._storeLastDir(self.lastDir);
|
||||
self._load_list(pathName)
|
||||
# }}}
|
||||
# {{{ onRemove(self, event)
|
||||
|
||||
def onRemove(self, event):
|
||||
del self.canvasList[self.currentIndex]; self.listView.DeleteItem(self.currentIndex);
|
||||
itemCount = self.listView.GetItemCount()
|
||||
if itemCount > 0:
|
||||
for numCanvas in [n for n in sorted(self.canvasList.keys()) if n >= self.currentIndex]:
|
||||
self.canvasList[numCanvas - 1] = self.canvasList[numCanvas]; del self.canvasList[numCanvas];
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE) for col in (0, 1)]
|
||||
if (self.currentIndex == 0) or (self.currentIndex >= itemCount):
|
||||
self.currentIndex = 0 if itemCount > 0 else None
|
||||
items = [self.listView.GetFirstSelected()]
|
||||
while True:
|
||||
item = self.listView.GetNextSelected(items[-1])
|
||||
if item != -1:
|
||||
items += [item]
|
||||
else:
|
||||
self.currentIndex = self.currentIndex if self.currentIndex < itemCount else None
|
||||
if self.currentIndex != None:
|
||||
self.listView.Select(self.currentIndex, on=1)
|
||||
self.drawCanvas(self.canvasList[self.currentIndex][0])
|
||||
else:
|
||||
self.currentIndex = None
|
||||
[self.listView.SetColumnWidth(col, wx.LIST_AUTOSIZE_USEHEADER) for col in (0, 1)]
|
||||
self.drawCanvas(Canvas((0, 0)))
|
||||
# }}}
|
||||
# {{{ onSaveList(self, event)
|
||||
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
|
||||
if len(self.canvasList):
|
||||
@ -285,7 +315,8 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
if self.lastDir != None:
|
||||
dialog.SetDirectory(self.lastDir)
|
||||
if dialog.ShowModal() != wx.ID_CANCEL:
|
||||
pathName = dialog.GetPath(); self.lastDir = os.path.dirname(pathName);
|
||||
pathName = dialog.GetPath()
|
||||
self.lastDir = os.path.dirname(pathName); self._storeLastDir(self.lastDir);
|
||||
with open(pathName, "w") as fileObject:
|
||||
for pathName in [self.canvasList[k][1] for k in self.canvasList.keys()]:
|
||||
print(pathName, file=fileObject)
|
||||
@ -295,17 +326,15 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
if not rc:
|
||||
with wx.MessageDialog(self, "Error: {}".format(error), "", wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, backend, cellSize, parent, pos=None, size=(400, 400), title="Assets"): initialisation method
|
||||
def __init__(self, backend, cellSize, parent, pos=None, size=(400, 400), title="Assets"):
|
||||
def __init__(self, backend, parent, pos=None, size=(400, 400), title="Assets"):
|
||||
if pos == None:
|
||||
parentRect = parent.GetScreenRect(); pos = (parentRect.x + parentRect.width, parentRect.y);
|
||||
super().__init__(parent, size, title, pos=pos)
|
||||
self.backend, self.canvasList, self.lastDir = backend((0, 0), cellSize), {}, None
|
||||
self.cellSize, self.currentIndex, self.leftDown, self.parent, self.scrollFlag = cellSize, None, False, parent, False
|
||||
self.backend, self.canvasList, self.lastDir = backend((0, 0)), {}, None
|
||||
self.currentIndex, self.leftDown, self.parent, self.scrollFlag = None, False, parent, False
|
||||
self.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self._loadLastDir()
|
||||
|
||||
self.contextMenu, self.contextMenuItems = wx.Menu(), []
|
||||
for text, f in (
|
||||
@ -316,7 +345,9 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
("&Remove", self.onRemove),
|
||||
(None, None),
|
||||
("Load from l&ist...", self.onLoadList),
|
||||
("Sa&ve as list...", self.onSaveList),):
|
||||
("Sa&ve as list...", self.onSaveList),
|
||||
(None, None),
|
||||
("Cl&ear list", self.onClearList),):
|
||||
if (text, f) == (None, None):
|
||||
self.contextMenu.AppendSeparator()
|
||||
else:
|
||||
@ -330,7 +361,8 @@ class RoarAssetsWindow(GuiMiniFrame):
|
||||
self.listView.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onListViewItemSelected)
|
||||
self.listView.Bind(wx.EVT_RIGHT_DOWN, self.onListViewRightDown)
|
||||
|
||||
self.panelCanvas = GuiWindow(self, (0, 0), cellSize, wx.BORDER_SUNKEN)
|
||||
self.panelCanvas = GuiWindow(self, (0, 0), wx.BORDER_SUNKEN)
|
||||
self.panelCanvas.Bind(wx.EVT_CHAR, self.onChar)
|
||||
self.panelCanvas.Bind(wx.EVT_LEFT_DOWN, self.onPanelLeftDown)
|
||||
self.panelCanvas.Bind(wx.EVT_PAINT, self.onPanelPaint)
|
||||
self.panelCanvas.Bind(wx.EVT_SIZE, self.onPanelSize)
|
||||
|
@ -5,41 +5,101 @@
|
||||
#
|
||||
|
||||
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):
|
||||
# {{{ _initColourBitmaps(self)
|
||||
def _initColourBitmaps(self):
|
||||
for numColour in range(len(RoarCanvasCommandsEdit.canvasColour.attrList)):
|
||||
if numColour < len(Colours):
|
||||
toolBitmapColour = Colours[numColour][0:4]
|
||||
toolBitmap = wx.Bitmap((16, 16))
|
||||
toolBitmapDc = wx.MemoryDC(); toolBitmapDc.SelectObject(toolBitmap);
|
||||
toolBitmapBrush = wx.Brush(wx.Colour(toolBitmapColour), wx.BRUSHSTYLE_SOLID)
|
||||
toolBitmapDc.SetBrush(toolBitmapBrush)
|
||||
toolBitmapDc.SetBackground(toolBitmapBrush)
|
||||
toolBitmapDc.SetPen(wx.Pen(wx.Colour(toolBitmapColour), 1))
|
||||
toolBitmapDc.DrawRectangle(0, 0, 16, 16)
|
||||
RoarCanvasCommandsEdit.canvasColour.attrList[numColour]["icon"] = ["", None, toolBitmap]
|
||||
toolBitmapColours = ((0, 0, 0, 255), (255, 255, 255, 255))
|
||||
toolBitmap = wx.Bitmap((16, 16))
|
||||
toolBitmapDc = wx.MemoryDC(); toolBitmapDc.SelectObject(toolBitmap);
|
||||
toolBitmapBrush = [wx.Brush(wx.Colour(c), wx.BRUSHSTYLE_SOLID) for c in toolBitmapColours]
|
||||
toolBitmapDc.SetBrush(toolBitmapBrush[1])
|
||||
toolBitmapDc.SetBackground(toolBitmapBrush[1])
|
||||
toolBitmapDc.SetPen(wx.Pen(wx.Colour(toolBitmapColours[1]), 1))
|
||||
toolBitmapDc.DrawRectangle(0, 0, 8, 8)
|
||||
toolBitmapDc.DrawRectangle(8, 8, 16, 16)
|
||||
RoarCanvasCommandsEdit.canvasColourAlpha.attrList[0]["icon"] = ["", None, toolBitmap]
|
||||
# }}}
|
||||
def _initColourBitmaps_(cmd, cmdAlpha, div):
|
||||
for numColour in range(len(cmd.attrList)):
|
||||
if numColour < len(Colours):
|
||||
toolBitmapColour = Colours[numColour][0:4]
|
||||
toolBitmap = wx.Bitmap((16, 16))
|
||||
toolBitmapDc = wx.MemoryDC(); toolBitmapDc.SelectObject(toolBitmap);
|
||||
toolBitmapBrush = wx.Brush(wx.Colour([*[int(c / div) for c in toolBitmapColour[:3]], 255]), wx.BRUSHSTYLE_SOLID)
|
||||
toolBitmapDc.SetBrush(toolBitmapBrush)
|
||||
toolBitmapDc.SetBackground(toolBitmapBrush)
|
||||
toolBitmapDc.SetPen(wx.Pen(toolBitmapColour, 1))
|
||||
toolBitmapDc.DrawRectangle(0, 0, 16, 16)
|
||||
cmd.attrList[numColour]["icon"] = ["", None, toolBitmap]
|
||||
toolBitmapColours = ((0, 0, 0, 255), (255, 255, 255, 255))
|
||||
toolBitmap = wx.Bitmap((16, 16))
|
||||
toolBitmapDc = wx.MemoryDC(); toolBitmapDc.SelectObject(toolBitmap);
|
||||
toolBitmapBrush = [wx.Brush(wx.Colour(c), wx.BRUSHSTYLE_SOLID) for c in toolBitmapColours]
|
||||
toolBitmapDc.SetBrush(toolBitmapBrush[1])
|
||||
toolBitmapDc.SetBackground(toolBitmapBrush[1])
|
||||
toolBitmapDc.SetPen(wx.Pen(wx.Colour(toolBitmapColours[1]), 1))
|
||||
toolBitmapDc.DrawRectangle(0, 0, 8, 8)
|
||||
toolBitmapDc.DrawRectangle(8, 8, 16, 16)
|
||||
cmdAlpha.attrList[0]["icon"] = ["", None, toolBitmap]
|
||||
_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
|
||||
|
||||
# {{{ update(self, **kwargs)
|
||||
def update(self, **kwargs):
|
||||
self.lastPanelState.update(kwargs); textItems = [];
|
||||
if "cellPos" in self.lastPanelState:
|
||||
@ -47,83 +107,64 @@ class RoarCanvasCommands(RoarCanvasCommandsFile, RoarCanvasCommandsEdit, RoarCan
|
||||
if "size" in self.lastPanelState:
|
||||
textItems.append("W: {:03d} H: {:03d}".format(*self.lastPanelState["size"]))
|
||||
if "brushSize" in self.lastPanelState:
|
||||
textItems.append("Brush: {:02d}x{:02d}".format(*self.lastPanelState["brushSize"]))
|
||||
textItems.append("B: {:02d}x{:02d}".format(*self.lastPanelState["brushSize"]))
|
||||
if "colours" in self.lastPanelState:
|
||||
textItems.append("FG: {:02d}, BG: {:02d}".format(*self.lastPanelState["colours"]))
|
||||
textItems.append("{} on {}".format(
|
||||
Colours[self.lastPanelState["colours"][0]][4] if self.lastPanelState["colours"][0] != -1 else "Transparent",
|
||||
Colours[self.lastPanelState["colours"][1]][4] if self.lastPanelState["colours"][1] != -1 else "Transparent"))
|
||||
textItems.append("FG: {:02d} ({}), BG: {:02d} ({})".format(self.lastPanelState["colours"][0], Colours[self.lastPanelState["colours"][0]][4] if self.lastPanelState["colours"][0] != -1 else "Transparent", self.lastPanelState["colours"][1], Colours[self.lastPanelState["colours"][1]][4] if self.lastPanelState["colours"][1] != -1 else "Transparent"))
|
||||
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()
|
||||
else:
|
||||
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()
|
||||
else:
|
||||
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"])
|
||||
textItems.append("Current file: {}".format(basePathName))
|
||||
textItems.append("F: {}".format(basePathName))
|
||||
self.parentFrame.SetTitle("{} - roar".format(basePathName))
|
||||
else:
|
||||
self.parentFrame.SetTitle("roar")
|
||||
if "toolName" in self.lastPanelState:
|
||||
textItems.append("Current tool: {}".format(self.lastPanelState["toolName"]))
|
||||
if "dirty" in self.lastPanelState \
|
||||
and self.lastPanelState["dirty"]:
|
||||
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"]:
|
||||
textItems.append("*")
|
||||
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"]].GetToolBar()
|
||||
toolBar.EnableTool(item.attrDict["id"], False)
|
||||
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"]].GetToolBar()
|
||||
toolBar.EnableTool(self.canvasUndo.attrDict["id"], True)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasUndo.attrDict["id"]][0]
|
||||
toolBar.EnableTool(self.canvasUndo.attrDict["id"], True); toolBar.Refresh();
|
||||
else:
|
||||
self.parentFrame.menuItemsById[self.canvasUndo.attrDict["id"]].Enable(False)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasUndo.attrDict["id"]].GetToolBar()
|
||||
toolBar.EnableTool(self.canvasUndo.attrDict["id"], False)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasUndo.attrDict["id"]][0]
|
||||
toolBar.EnableTool(self.canvasUndo.attrDict["id"], False); toolBar.Refresh();
|
||||
if self.lastPanelState["undoLevel"] > 0:
|
||||
self.parentFrame.menuItemsById[self.canvasRedo.attrDict["id"]].Enable(True)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasRedo.attrDict["id"]].GetToolBar()
|
||||
toolBar.EnableTool(self.canvasRedo.attrDict["id"], True)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasRedo.attrDict["id"]][0]
|
||||
toolBar.EnableTool(self.canvasRedo.attrDict["id"], True); toolBar.Refresh();
|
||||
else:
|
||||
self.parentFrame.menuItemsById[self.canvasRedo.attrDict["id"]].Enable(False)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasRedo.attrDict["id"]].GetToolBar()
|
||||
toolBar.EnableTool(self.canvasRedo.attrDict["id"], False)
|
||||
# }}}
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasRedo.attrDict["id"]][0]
|
||||
toolBar.EnableTool(self.canvasRedo.attrDict["id"], False); toolBar.Refresh();
|
||||
|
||||
#
|
||||
# __init__(self, parentCanvas, parentFrame):
|
||||
def __init__(self, parentCanvas, parentFrame):
|
||||
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.menus):
|
||||
menus += self.menus
|
||||
if len(self.toolBars):
|
||||
toolBars += self.toolBars
|
||||
self._initColourBitmaps()
|
||||
|
||||
# XXX
|
||||
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.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), NID_TOOLBAR_HSEP,
|
||||
self.canvasTool(self.canvasTool, 1), self.canvasTool(self.canvasTool, 5), self.canvasTool(self.canvasTool, 0), self.canvasTool(self.canvasTool, 2), self.canvasTool(self.canvasTool, 3), self.canvasTool(self.canvasTool, 6), self.canvasTool(self.canvasTool, 4),
|
||||
])
|
||||
# XXX
|
||||
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), NID_TOOLBAR_HSEP,
|
||||
self.canvasBrushSize(self.canvasBrushSize, 1, True), self.canvasBrushSize(self.canvasBrushSize, 0, False), self.canvasBrushSize(self.canvasBrushSize, 1, True), self.canvasBrushSize(self.canvasBrushSize, 1, False), NID_TOOLBAR_HSEP,
|
||||
self.canvasBrushSize(self.canvasBrushSize, 2, True), self.canvasBrushSize(self.canvasBrushSize, 2, False),
|
||||
])
|
||||
self.menus, self.toolBars = menus, toolBars
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0
|
||||
|
@ -4,25 +4,30 @@
|
||||
# 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():
|
||||
# {{{ canvasAssetsWindowHide(self, event)
|
||||
@GuiCommandDecorator("Hide assets window", "Hide assets window", None, None, False)
|
||||
@GuiCommandDecorator("Hide assets window", "Hide assets window", ["toolHideAssetsWindow.png"], None, False)
|
||||
def canvasAssetsWindowHide(self, event):
|
||||
self.parentFrame.assetsWindow.Show(False)
|
||||
self.parentFrame.menuItemsById[self.canvasAssetsWindowHide.attrDict["id"]].Enable(False)
|
||||
self.parentFrame.menuItemsById[self.canvasAssetsWindowShow.attrDict["id"]].Enable(True)
|
||||
# }}}
|
||||
# {{{ canvasAssetsWindowShow(self, event)
|
||||
@GuiCommandDecorator("Show assets window", "Show assets window", None, None, False)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasAssetsWindowHide.attrDict["id"]][0]
|
||||
toolBar.EnableTool(self.canvasAssetsWindowHide.attrDict["id"], False)
|
||||
toolBar.EnableTool(self.canvasAssetsWindowShow.attrDict["id"], True)
|
||||
toolBar.Refresh()
|
||||
|
||||
@GuiCommandDecorator("Show assets window", "Show assets window", ["toolShowAssetsWindow.png"], None, False)
|
||||
def canvasAssetsWindowShow(self, event):
|
||||
self.parentFrame.assetsWindow.Show(True)
|
||||
self.parentFrame.menuItemsById[self.canvasAssetsWindowHide.attrDict["id"]].Enable(True)
|
||||
self.parentFrame.menuItemsById[self.canvasAssetsWindowShow.attrDict["id"]].Enable(False)
|
||||
# }}}
|
||||
# {{{ canvasBrush(self, f, idx)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasAssetsWindowHide.attrDict["id"]][0]
|
||||
toolBar.EnableTool(self.canvasAssetsWindowHide.attrDict["id"], True)
|
||||
toolBar.EnableTool(self.canvasAssetsWindowShow.attrDict["id"], False)
|
||||
toolBar.Refresh()
|
||||
|
||||
@GuiSelectDecorator(0, "Solid brush", "Solid brush", None, None, True)
|
||||
def canvasBrush(self, f, idx):
|
||||
def canvasBrush_(self, event):
|
||||
@ -30,14 +35,13 @@ class RoarCanvasCommandsEdit():
|
||||
setattr(canvasBrush_, "attrDict", f.attrList[idx])
|
||||
setattr(canvasBrush_, "isSelect", True)
|
||||
return canvasBrush_
|
||||
# }}}
|
||||
# {{{ canvasBrushSize(self, f, dimension, incrFlag)
|
||||
|
||||
@GuiCommandListDecorator(0, "Decrease brush width", "Decrease brush width", ["toolDecrBrushW.png"], None, None)
|
||||
@GuiCommandListDecorator(1, "Decrease brush height", "Decrease brush height", ["toolDecrBrushH.png"], None, None)
|
||||
@GuiCommandListDecorator(2, "Decrease brush size", "Decrease brush size", ["toolDecrBrushHW.png"], None, None)
|
||||
@GuiCommandListDecorator(2, "Decrease brush size", "Decrease brush size", ["toolDecrBrushHW.png"], [wx.ACCEL_CTRL, ord("-")], None)
|
||||
@GuiCommandListDecorator(3, "Increase brush width", "Increase brush width", ["toolIncrBrushW.png"], None, None)
|
||||
@GuiCommandListDecorator(4, "Increase brush height", "Increase brush height", ["toolIncrBrushH.png"], None, None)
|
||||
@GuiCommandListDecorator(5, "Increase brush size", "Increase brush size", ["toolIncrBrushHW.png"], None, None)
|
||||
@GuiCommandListDecorator(5, "Increase brush size", "Increase brush size", ["toolIncrBrushHW.png"], [wx.ACCEL_CTRL, ord("+")], None)
|
||||
def canvasBrushSize(self, f, dimension, incrFlag):
|
||||
def canvasBrushSize_(event):
|
||||
if (dimension < 2) and not incrFlag:
|
||||
@ -49,15 +53,17 @@ class RoarCanvasCommandsEdit():
|
||||
self.update(brushSize=self.parentCanvas.brushSize)
|
||||
elif dimension == 2:
|
||||
[self.canvasBrushSize(f, dimension_, incrFlag)(None) for dimension_ in [0, 1]]
|
||||
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, *self.parentCanvas.lastMouseState, self.currentTool, viewRect, force=True)
|
||||
setattr(canvasBrushSize_, "attrDict", f.attrList[dimension + (0 if not incrFlag else 3)])
|
||||
return canvasBrushSize_
|
||||
# }}}
|
||||
# {{{ canvasCanvasSize(self, f, dimension, incrFlag)
|
||||
@GuiCommandListDecorator(0, "Decrease canvas height", "Decrease canvas height", ["toolDecrCanvasH.png"], None, None)
|
||||
@GuiCommandListDecorator(1, "Decrease canvas width", "Decrease canvas width", ["toolDecrCanvasW.png"], None, None)
|
||||
|
||||
@GuiCommandListDecorator(0, "Decrease canvas height", "Decrease canvas height", ["toolDecrCanvasH.png"], [wx.ACCEL_CTRL, wx.WXK_UP], None)
|
||||
@GuiCommandListDecorator(1, "Decrease canvas width", "Decrease canvas width", ["toolDecrCanvasW.png"], [wx.ACCEL_CTRL, wx.WXK_LEFT], None)
|
||||
@GuiCommandListDecorator(2, "Decrease canvas size", "Decrease canvas size", ["toolDecrCanvasHW.png"], None, None)
|
||||
@GuiCommandListDecorator(3, "Increase canvas height", "Increase canvas height", ["toolIncrCanvasH.png"], None, None)
|
||||
@GuiCommandListDecorator(4, "Increase canvas width", "Increase canvas width", ["toolIncrCanvasW.png"], None, None)
|
||||
@GuiCommandListDecorator(3, "Increase canvas height", "Increase canvas height", ["toolIncrCanvasH.png"], [wx.ACCEL_CTRL, wx.WXK_DOWN], None)
|
||||
@GuiCommandListDecorator(4, "Increase canvas width", "Increase canvas width", ["toolIncrCanvasW.png"], [wx.ACCEL_CTRL, wx.WXK_RIGHT], None)
|
||||
@GuiCommandListDecorator(5, "Increase canvas size", "Increase canvas size", ["toolIncrCanvasHW.png"], None, None)
|
||||
def canvasCanvasSize(self, f, dimension, incrFlag):
|
||||
def canvasCanvasSize_(event):
|
||||
@ -77,24 +83,23 @@ class RoarCanvasCommandsEdit():
|
||||
[self.canvasCanvasSize(f, dimension_, incrFlag)(None) for dimension_ in [0, 1]]
|
||||
setattr(canvasCanvasSize_, "attrDict", f.attrList[dimension + (0 if not incrFlag else 3)])
|
||||
return canvasCanvasSize_
|
||||
# }}}
|
||||
# {{{ canvasColour(self, f, idx)
|
||||
@GuiSelectDecorator(0, "Colour #00", "Colour #00 (Bright White)", None, None, False)
|
||||
@GuiSelectDecorator(1, "Colour #01", "Colour #01 (Black)", None, None, False)
|
||||
@GuiSelectDecorator(2, "Colour #02", "Colour #02 (Blue)", None, None, False)
|
||||
@GuiSelectDecorator(3, "Colour #03", "Colour #03 (Green)", None, None, False)
|
||||
@GuiSelectDecorator(4, "Colour #04", "Colour #04 (Red)", None, None, False)
|
||||
@GuiSelectDecorator(5, "Colour #05", "Colour #05 (Light Red)", None, None, False)
|
||||
@GuiSelectDecorator(6, "Colour #06", "Colour #06 (Pink)", None, None, False)
|
||||
@GuiSelectDecorator(7, "Colour #07", "Colour #07 (Yellow)", None, None, False)
|
||||
@GuiSelectDecorator(8, "Colour #08", "Colour #08 (Light Yellow)", None, None, False)
|
||||
@GuiSelectDecorator(9, "Colour #09", "Colour #09 (Light Green)", None, None, False)
|
||||
@GuiSelectDecorator(10, "Colour #10", "Colour #10 (Cyan)", None, None, False)
|
||||
@GuiSelectDecorator(11, "Colour #11", "Colour #11 (Light Cyan)", None, None, False)
|
||||
@GuiSelectDecorator(12, "Colour #12", "Colour #12 (Light Blue)", None, None, False)
|
||||
@GuiSelectDecorator(13, "Colour #13", "Colour #13 (Light Pink)", None, None, False)
|
||||
@GuiSelectDecorator(14, "Colour #14", "Colour #14 (Grey)", None, None, False)
|
||||
@GuiSelectDecorator(15, "Colour #15", "Colour #15 (Light Grey)", None, None, False)
|
||||
|
||||
@GuiSelectDecorator(0, "Colour #00", "Colour #00 (Bright White)", None, [wx.ACCEL_CTRL, ord("0")], False)
|
||||
@GuiSelectDecorator(1, "Colour #01", "Colour #01 (Black)", None, [wx.ACCEL_CTRL, ord("1")], False)
|
||||
@GuiSelectDecorator(2, "Colour #02", "Colour #02 (Blue)", None, [wx.ACCEL_CTRL, ord("2")], False)
|
||||
@GuiSelectDecorator(3, "Colour #03", "Colour #03 (Green)", None, [wx.ACCEL_CTRL, ord("3")], False)
|
||||
@GuiSelectDecorator(4, "Colour #04", "Colour #04 (Red)", None, [wx.ACCEL_CTRL, ord("4")], False)
|
||||
@GuiSelectDecorator(5, "Colour #05", "Colour #05 (Light Red)", None, [wx.ACCEL_CTRL, ord("5")], False)
|
||||
@GuiSelectDecorator(6, "Colour #06", "Colour #06 (Pink)", None, [wx.ACCEL_CTRL, ord("6")], False)
|
||||
@GuiSelectDecorator(7, "Colour #07", "Colour #07 (Yellow)", None, [wx.ACCEL_CTRL, ord("7")], False)
|
||||
@GuiSelectDecorator(8, "Colour #08", "Colour #08 (Light Yellow)", None, [wx.ACCEL_CTRL, ord("8")], False)
|
||||
@GuiSelectDecorator(9, "Colour #09", "Colour #09 (Light Green)", None, [wx.ACCEL_CTRL, ord("9")], False)
|
||||
@GuiSelectDecorator(10, "Colour #10", "Colour #10 (Cyan)", None, [wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord("0")], False)
|
||||
@GuiSelectDecorator(11, "Colour #11", "Colour #11 (Light Cyan)", None, [wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord("1")], False)
|
||||
@GuiSelectDecorator(12, "Colour #12", "Colour #12 (Light Blue)", None, [wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord("2")], False)
|
||||
@GuiSelectDecorator(13, "Colour #13", "Colour #13 (Light Pink)", None, [wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord("3")], False)
|
||||
@GuiSelectDecorator(14, "Colour #14", "Colour #14 (Grey)", None, [wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord("4")], False)
|
||||
@GuiSelectDecorator(15, "Colour #15", "Colour #15 (Light Grey)", None, [wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord("5")], False)
|
||||
def canvasColour(self, f, idx):
|
||||
def canvasColour_(event):
|
||||
if event.GetEventType() == wx.wxEVT_TOOL:
|
||||
@ -102,12 +107,14 @@ class RoarCanvasCommandsEdit():
|
||||
elif event.GetEventType() == wx.wxEVT_TOOL_RCLICKED:
|
||||
self.parentCanvas.brushColours[1] = idx
|
||||
self.update(colours=self.parentCanvas.brushColours)
|
||||
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, *self.parentCanvas.lastMouseState, self.currentTool, viewRect, force=True)
|
||||
setattr(canvasColour_, "attrDict", f.attrList[idx])
|
||||
setattr(canvasColour_, "isSelect", True)
|
||||
return canvasColour_
|
||||
# }}}
|
||||
# {{{ canvasColourAlpha(self, f, idx)
|
||||
@GuiSelectDecorator(0, "Transparent colour", "Transparent colour", None, None, False)
|
||||
|
||||
@GuiSelectDecorator(0, "Transparent colour", "Transparent colour", None, [wx.ACCEL_CTRL | wx.ACCEL_SHIFT, ord("6")], False)
|
||||
def canvasColourAlpha(self, f, idx):
|
||||
def canvasColourAlpha_(event):
|
||||
if event.GetEventType() == wx.wxEVT_TOOL:
|
||||
@ -115,60 +122,82 @@ class RoarCanvasCommandsEdit():
|
||||
elif event.GetEventType() == wx.wxEVT_TOOL_RCLICKED:
|
||||
self.parentCanvas.brushColours[1] = -1
|
||||
self.update(colours=self.parentCanvas.brushColours)
|
||||
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, *self.parentCanvas.lastMouseState, self.currentTool, viewRect, force=True)
|
||||
setattr(canvasColourAlpha_, "attrDict", f.attrList[idx])
|
||||
setattr(canvasColourAlpha_, "isSelect", True)
|
||||
return canvasColourAlpha_
|
||||
# }}}
|
||||
# {{{ canvasCopy(self, event)
|
||||
|
||||
@GuiSelectDecorator(0, "Transparent colour", "Transparent colour", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT | wx.ACCEL_SHIFT, ord("6")], False)
|
||||
def canvasColourAlphaBackground(self, f, idx):
|
||||
def canvasColourAlphaBackground_(event):
|
||||
self.parentCanvas.brushColours[1] = -1
|
||||
self.update(colours=self.parentCanvas.brushColours)
|
||||
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, *self.parentCanvas.lastMouseState, self.currentTool, viewRect, force=True)
|
||||
setattr(canvasColourAlphaBackground_, "attrDict", f.attrList[idx])
|
||||
setattr(canvasColourAlphaBackground_, "isSelect", True)
|
||||
return canvasColourAlphaBackground_
|
||||
|
||||
@GuiSelectDecorator(0, "Colour #00", "Colour #00 (Bright White)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("0")], False)
|
||||
@GuiSelectDecorator(1, "Colour #01", "Colour #01 (Black)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("1")], False)
|
||||
@GuiSelectDecorator(2, "Colour #02", "Colour #02 (Blue)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("2")], False)
|
||||
@GuiSelectDecorator(3, "Colour #03", "Colour #03 (Green)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("3")], False)
|
||||
@GuiSelectDecorator(4, "Colour #04", "Colour #04 (Red)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("4")], False)
|
||||
@GuiSelectDecorator(5, "Colour #05", "Colour #05 (Light Red)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("5")], False)
|
||||
@GuiSelectDecorator(6, "Colour #06", "Colour #06 (Pink)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("6")], False)
|
||||
@GuiSelectDecorator(7, "Colour #07", "Colour #07 (Yellow)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("7")], False)
|
||||
@GuiSelectDecorator(8, "Colour #08", "Colour #08 (Light Yellow)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("8")], False)
|
||||
@GuiSelectDecorator(9, "Colour #09", "Colour #09 (Light Green)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT, ord("9")], False)
|
||||
@GuiSelectDecorator(10, "Colour #10", "Colour #10 (Cyan)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT | wx.ACCEL_SHIFT, ord("0")], False)
|
||||
@GuiSelectDecorator(11, "Colour #11", "Colour #11 (Light Cyan)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT | wx.ACCEL_SHIFT, ord("1")], False)
|
||||
@GuiSelectDecorator(12, "Colour #12", "Colour #12 (Light Blue)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT | wx.ACCEL_SHIFT, ord("2")], False)
|
||||
@GuiSelectDecorator(13, "Colour #13", "Colour #13 (Light Pink)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT | wx.ACCEL_SHIFT, ord("3")], False)
|
||||
@GuiSelectDecorator(14, "Colour #14", "Colour #14 (Grey)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT | wx.ACCEL_SHIFT, ord("4")], False)
|
||||
@GuiSelectDecorator(15, "Colour #15", "Colour #15 (Light Grey)", None, [wx.ACCEL_CTRL | wx.ACCEL_ALT | wx.ACCEL_SHIFT, ord("5")], False)
|
||||
def canvasColourBackground(self, f, idx):
|
||||
def canvasColourBackground_(event):
|
||||
self.parentCanvas.brushColours[1] = idx
|
||||
self.update(colours=self.parentCanvas.brushColours)
|
||||
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, *self.parentCanvas.lastMouseState, self.currentTool, viewRect, force=True)
|
||||
setattr(canvasColourBackground_, "attrDict", f.attrList[idx])
|
||||
setattr(canvasColourBackground_, "isSelect", True)
|
||||
return canvasColourBackground_
|
||||
|
||||
@GuiCommandDecorator("Flip colours", "Flip colours", ["toolColoursFlip.png"], [wx.ACCEL_CTRL, ord("I")], True)
|
||||
def canvasColoursFlip(self, event):
|
||||
self.parentCanvas.brushColours = [self.parentCanvas.brushColours[1], self.parentCanvas.brushColours[0]]
|
||||
self.update(colours=self.parentCanvas.brushColours)
|
||||
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, *self.parentCanvas.lastMouseState, self.currentTool, viewRect, force=True)
|
||||
|
||||
@GuiCommandDecorator("Copy", "&Copy", ["", wx.ART_COPY], None, False)
|
||||
def canvasCopy(self, event):
|
||||
pass
|
||||
# }}}
|
||||
# {{{ canvasCut(self, event)
|
||||
|
||||
@GuiCommandDecorator("Cut", "Cu&t", ["", wx.ART_CUT], None, False)
|
||||
def canvasCut(self, event):
|
||||
pass
|
||||
# }}}
|
||||
# {{{ canvasDelete(self, event)
|
||||
|
||||
@GuiCommandDecorator("Delete", "De&lete", ["", wx.ART_DELETE], None, False)
|
||||
def canvasDelete(self, event):
|
||||
pass
|
||||
# }}}
|
||||
# {{{ canvasPaste(self, event)
|
||||
|
||||
@GuiCommandDecorator("Paste", "&Paste", ["", wx.ART_PASTE], None, False)
|
||||
def canvasPaste(self, event):
|
||||
pass
|
||||
# }}}
|
||||
# {{{ canvasRedo(self, event)
|
||||
|
||||
@GuiCommandDecorator("Redo", "&Redo", ["", wx.ART_REDO], [wx.ACCEL_CTRL, ord("Y")], False)
|
||||
def canvasRedo(self, event):
|
||||
self.parentCanvas.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popRedo())
|
||||
self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel)
|
||||
# }}}
|
||||
# {{{ canvasUndo(self, event)
|
||||
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.dispatchDeltaPatches(self.parentCanvas.canvas.journal.popUndo())
|
||||
self.update(size=self.parentCanvas.canvas.size, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel)
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self)
|
||||
def __init__(self):
|
||||
self.menus = (
|
||||
("&Edit",
|
||||
self.canvasUndo, self.canvasRedo, NID_MENU_SEP,
|
||||
self.canvasCut, self.canvasCopy, self.canvasPaste,
|
||||
self.canvasDelete, NID_MENU_SEP,
|
||||
("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),),
|
||||
("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),),
|
||||
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
|
||||
|
@ -5,35 +5,35 @@
|
||||
#
|
||||
|
||||
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
|
||||
|
||||
from GuiFrame import GuiCommandDecorator, GuiSubMenuDecorator, NID_MENU_SEP
|
||||
from RtlPlatform import getLocalConfPathName
|
||||
import io, os, wx
|
||||
import datetime, io, os, stat, wx
|
||||
|
||||
class RoarCanvasCommandsFile():
|
||||
# {{{ _import(self, f, newDirty, pathName)
|
||||
def _import(self, f, newDirty, pathName):
|
||||
def _import(self, f, newDirty, pathName, emptyPathName=False):
|
||||
rc = False
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
try:
|
||||
rc, error, newMap, newPathName, newSize = f(pathName)
|
||||
if rc:
|
||||
self.parentCanvas.update(newSize, False, newMap, dirty=newDirty)
|
||||
self.parentCanvas.dirty = newDirty
|
||||
self.parentCanvas.update(newSize, False, newMap)
|
||||
self.canvasPathName = newPathName
|
||||
if not emptyPathName:
|
||||
self.canvasPathName = newPathName
|
||||
else:
|
||||
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:
|
||||
@ -41,27 +41,18 @@ class RoarCanvasCommandsFile():
|
||||
dialogChoice = dialog.ShowModal()
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
return rc, newPathName
|
||||
# }}}
|
||||
# {{{ _importFile(self, f, newDirty, wildcard)
|
||||
def _importFile(self, f, newDirty, wildcard):
|
||||
with wx.FileDialog(self.parentCanvas, "Open", os.getcwd(), "", wildcard, wx.FD_OPEN) as dialog:
|
||||
|
||||
def _importFile(self, f, newDirty, wildcard, emptyPathName=False):
|
||||
with wx.FileDialog(self.parentCanvas, "Open...", os.getcwd(), "", wildcard, wx.FD_OPEN) as dialog:
|
||||
if self.lastDir != None:
|
||||
dialog.SetDirectory(self.lastDir)
|
||||
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||
return False, None
|
||||
elif self._promptSaveChanges():
|
||||
pathName = dialog.GetPath(); self.lastDir = os.path.dirname(pathName);
|
||||
return self._import(f, newDirty, pathName)
|
||||
# }}}
|
||||
# {{{ _loadRecent(self)
|
||||
def _loadRecent(self):
|
||||
localConfFileName = getLocalConfPathName("Recent.lst")
|
||||
if os.path.exists(localConfFileName):
|
||||
with open(localConfFileName, "r", encoding="utf-8") as inFile:
|
||||
for lastFile in inFile.readlines():
|
||||
self._pushRecent(lastFile.rstrip("\r\n"), False)
|
||||
# }}}
|
||||
# {{{ _promptSaveChanges(self)
|
||||
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 _promptSaveChanges(self):
|
||||
if self.parentCanvas.dirty:
|
||||
message = "Do you want to save changes to {}?".format(self.canvasPathName if self.canvasPathName != None else "(Untitled)")
|
||||
@ -77,34 +68,82 @@ class RoarCanvasCommandsFile():
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
# }}}
|
||||
# {{{ _pushRecent(self, pathName, serialise=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
|
||||
if (numLastFiles + 1) > 8:
|
||||
self.canvasOpenRecent.attrDict["menu"].Delete(self.lastFiles[0]["menuItemId"])
|
||||
del self.lastFiles[0]
|
||||
menuItemWindow = self.canvasOpenRecent.attrDict["menu"].Append(menuItemId, "{}".format(pathName), pathName)
|
||||
menuItemWindow = self.canvasOpenRecent.attrDict["menu"].Insert(self.canvasOpenRecent.attrDict["menu"].GetMenuItemCount() - 2, menuItemId, pathName, pathName)
|
||||
self.parentFrame.menuItemsById[self.canvasOpenRecent.attrDict["id"]].Enable(True)
|
||||
self.parentFrame.Bind(wx.EVT_MENU, lambda event: self.canvasOpenRecent(event, pathName), menuItemWindow)
|
||||
self.lastFiles += [{"menuItemId":menuItemId, "menuItemWindow":menuItemWindow, "pathName":pathName}]
|
||||
if serialise:
|
||||
localConfFileName = getLocalConfPathName("Recent.lst")
|
||||
with open(localConfFileName, "w", encoding="utf-8") as outFile:
|
||||
for lastFile in [l["pathName"] for l in self.lastFiles]:
|
||||
print(lastFile, file=outFile)
|
||||
# }}}
|
||||
self._recentSave()
|
||||
|
||||
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")
|
||||
menuItemWindow = self.canvasRestore.attrDict["menu"].Insert(self.canvasRestore.attrDict["menu"].GetMenuItemCount() - 2, menuItemId, label)
|
||||
self.parentFrame.menuItemsById[self.canvasRestore.attrDict["id"]].Enable(True)
|
||||
self.parentFrame.Bind(wx.EVT_MENU, lambda event: self.canvasRestore(event, pathName), menuItemWindow)
|
||||
self.snapshots[pathName] = {"menuItemId":menuItemId, "menuItemWindow":menuItemWindow}
|
||||
|
||||
def _snapshotsReset(self):
|
||||
for pathName in list(self.snapshots.keys()):
|
||||
self._snapshotsPop(pathName)
|
||||
if self.canvasRestore.attrDict["id"] in self.parentFrame.menuItemsById:
|
||||
self.parentFrame.menuItemsById[self.canvasRestore.attrDict["id"]].Enable(False)
|
||||
|
||||
@GuiCommandDecorator("Clear list", "&Clear list", None, None, False)
|
||||
def canvasClearRecent(self, event):
|
||||
if self.lastFiles != None:
|
||||
for lastFile in self.lastFiles:
|
||||
self.canvasOpenRecent.attrDict["menu"].Delete(lastFile["menuItemId"])
|
||||
self.lastFiles = []
|
||||
localConfFileName = getLocalConfPathName("Recent.lst")
|
||||
if os.path.exists(localConfFileName):
|
||||
os.unlink(localConfFileName)
|
||||
self.parentFrame.menuItemsById[self.canvasOpenRecent.attrDict["id"]].Enable(False)
|
||||
|
||||
# {{{ canvasExit(self, event)
|
||||
@GuiCommandDecorator("Exit", "E&xit", None, [wx.ACCEL_CTRL, ord("X")], None)
|
||||
def canvasExit(self, event):
|
||||
if self._promptSaveChanges():
|
||||
self.parentFrame.Close(True)
|
||||
# }}}
|
||||
if not self.exiting:
|
||||
if self._promptSaveChanges():
|
||||
self.exiting = True; self.parentFrame.Close(True);
|
||||
|
||||
# {{{ canvasExportAsAnsi(self, event)
|
||||
@GuiCommandDecorator("Export as ANSI...", "Export as &ANSI...", None, None, None)
|
||||
def canvasExportAsAnsi(self, event):
|
||||
with wx.FileDialog(self.parentFrame, "Save As...", os.getcwd(), "", "ANSI files (*.ans;*.txt)|*.ans;*.txt|All Files (*.*)|*.*", wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dialog:
|
||||
@ -113,14 +152,13 @@ class RoarCanvasCommandsFile():
|
||||
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||
return False
|
||||
else:
|
||||
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName);
|
||||
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)
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
return True
|
||||
# }}}
|
||||
# {{{ canvasExportAsPng(self, event)
|
||||
|
||||
@GuiCommandDecorator("Export as PNG...", "Export as PN&G...", None, None, None)
|
||||
def canvasExportAsPng(self, event):
|
||||
with wx.FileDialog(self.parentFrame, "Save As...", os.getcwd(), "", "PNG (*.png)|*.png|All Files (*.*)|*.*", wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dialog:
|
||||
@ -129,17 +167,20 @@ class RoarCanvasCommandsFile():
|
||||
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||
return False
|
||||
else:
|
||||
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName);
|
||||
outPathName = dialog.GetPath(); self.lastDir = os.path.dirname(outPathName); self._recentDirSave(self.lastDir);
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
self.parentCanvas.cursorHide()
|
||||
self.parentCanvas.canvas.exportStore.exportBitmapToPngFile(self.parentCanvas.backend.canvasBitmap, outPathName, wx.BITMAP_TYPE_PNG)
|
||||
self.parentCanvas.cursorShow()
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
return True
|
||||
# }}}
|
||||
# {{{ canvasExportImgur(self, event)
|
||||
|
||||
@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))
|
||||
self.parentCanvas.cursorHide()
|
||||
rc, status, result = self.parentCanvas.canvas.exportStore.exportBitmapToImgur(self.imgurApiKey, self.parentCanvas.backend.canvasBitmap, "", "", wx.BITMAP_TYPE_PNG)
|
||||
self.parentCanvas.cursorShow()
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
if rc:
|
||||
if not wx.TheClipboard.IsOpened():
|
||||
@ -147,12 +188,11 @@ class RoarCanvasCommandsFile():
|
||||
wx.MessageBox("Exported to Imgur: {}".format(result), "Export to Imgur", wx.ICON_INFORMATION | wx.OK)
|
||||
else:
|
||||
wx.MessageBox("Failed to export to Imgur: {}".format(result), "Export to Imgur", wx.ICON_EXCLAMATION | wx.OK)
|
||||
# }}}
|
||||
# {{{ canvasExportPastebin(self, event)
|
||||
|
||||
@GuiCommandDecorator("Export to Pastebin...", "Export to Pasteb&in...", None, None, haveUrllib)
|
||||
def canvasExportPastebin(self, event):
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
pasteStatus, pasteResult = self.parentCanvas.canvas.exportStore.exportPastebin("253ce2f0a45140ee0a44ca99aa49260", self.parentCanvas.canvas.map, self.parentCanvas.canvas.size)
|
||||
pasteStatus, pasteResult = self.parentCanvas.canvas.exportStore.exportPastebin("", self.parentCanvas.canvas.map, self.parentCanvas.canvas.size)
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
if pasteStatus:
|
||||
if not wx.TheClipboard.IsOpened():
|
||||
@ -162,8 +202,7 @@ class RoarCanvasCommandsFile():
|
||||
wx.MessageBox("Exported to Pastebin: " + pasteResult, "Export to Pastebin", wx.OK|wx.ICON_INFORMATION)
|
||||
else:
|
||||
wx.MessageBox("Failed to export to Pastebin: " + pasteResult, "Export to Pastebin", wx.OK|wx.ICON_EXCLAMATION)
|
||||
# }}}
|
||||
# {{{ canvasExportToClipboard(self, event)
|
||||
|
||||
@GuiCommandDecorator("Export to clipboard", "Export to &clipboard", None, None, None)
|
||||
def canvasExportToClipboard(self, event):
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
@ -173,17 +212,14 @@ class RoarCanvasCommandsFile():
|
||||
wx.TheClipboard.Close()
|
||||
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
return True
|
||||
# }}}
|
||||
|
||||
# {{{ canvasImportAnsi(self, event)
|
||||
@GuiCommandDecorator("Import ANSI...", "Import &ANSI...", None, None, None)
|
||||
def canvasImportAnsi(self, event):
|
||||
def canvasImportAnsi_(pathName):
|
||||
rc, error = self.parentCanvas.canvas.importStore.importAnsiFile(pathName)
|
||||
return (rc, error, self.parentCanvas.canvas.importStore.outMap, pathName, self.parentCanvas.canvas.importStore.inSize)
|
||||
self._importFile(canvasImportAnsi_, True, "ANSI files (*.ans;*.txt)|*.ans;*.txt|All Files (*.*)|*.*")
|
||||
# }}}
|
||||
# {{{ canvasImportFromClipboard(self, event)
|
||||
self._importFile(canvasImportAnsi_, True, "ANSI files (*.ans;*.txt)|*.ans;*.txt|All Files (*.*)|*.*", emptyPathName=True)
|
||||
|
||||
@GuiCommandDecorator("Import from clipboard", "Import from &clipboard", None, None, None)
|
||||
def canvasImportFromClipboard(self, event):
|
||||
def canvasImportFromClipboard_(pathName):
|
||||
@ -197,31 +233,26 @@ class RoarCanvasCommandsFile():
|
||||
else:
|
||||
rc, error = False, "Clipboard does not contain text data and/or cannot be opened"
|
||||
return (rc, error, self.parentCanvas.canvas.importStore.outMap, None, self.parentCanvas.canvas.importStore.inSize)
|
||||
if self._promptSaveChanges():
|
||||
self._import(canvasImportFromClipboard_, True, None)
|
||||
# }}}
|
||||
# {{{ canvasImportSauce(self, event)
|
||||
self._import(canvasImportFromClipboard_, True, None, emptyPathName=True)
|
||||
|
||||
@GuiCommandDecorator("Import SAUCE...", "Import &SAUCE...", None, None, None)
|
||||
def canvasImportSauce(self, event):
|
||||
def canvasImportSauce_(pathName):
|
||||
rc, error = self.parentCanvas.canvas.importStore.importSauceFile(pathName)
|
||||
return (rc, error, self.parentCanvas.canvas.importStore.outMap, pathName, self.parentCanvas.canvas.importStore.inSize)
|
||||
self._importFile(canvasImportSauce_, True, "SAUCE files (*.ans;*.txt)|*.ans;*.txt|All Files (*.*)|*.*")
|
||||
# }}}
|
||||
self._importFile(canvasImportSauce_, True, "SAUCE files (*.ans;*.txt)|*.ans;*.txt|All Files (*.*)|*.*", emptyPathName=True)
|
||||
|
||||
# {{{ canvasNew(self, event, newCanvasSize=None)
|
||||
@GuiCommandDecorator("New", "&New", ["", wx.ART_NEW], [wx.ACCEL_CTRL, ord("N")], None)
|
||||
def canvasNew(self, event, newCanvasSize=None):
|
||||
def canvasImportEmpty(pathName):
|
||||
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])]
|
||||
newCanvasSize = [100, 30]
|
||||
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)
|
||||
# }}}
|
||||
# {{{ canvasOpen(self, event)
|
||||
|
||||
@GuiCommandDecorator("Open", "&Open", ["", wx.ART_FILE_OPEN], [wx.ACCEL_CTRL, ord("O")], None)
|
||||
def canvasOpen(self, event):
|
||||
def canvasImportmIRC(pathName):
|
||||
@ -229,17 +260,36 @@ 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)
|
||||
# }}}
|
||||
# {{{ canvasOpenRecent(self, event, pathName=None)
|
||||
self._recentPush(newPathName)
|
||||
|
||||
@GuiSubMenuDecorator("Open Recent", "Open &Recent", None, None, False)
|
||||
def canvasOpenRecent(self, event, pathName=None):
|
||||
def canvasImportmIRC(pathName_):
|
||||
rc, error = self.parentCanvas.canvas.importStore.importTextFile(pathName_)
|
||||
return (rc, error, self.parentCanvas.canvas.importStore.outMap, pathName_, self.parentCanvas.canvas.importStore.inSize)
|
||||
if self._promptSaveChanges():
|
||||
rc, newPathName = self._import(canvasImportmIRC, False, pathName)
|
||||
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._recentSave()
|
||||
|
||||
@GuiSubMenuDecorator("Restore Snapshot", "Res&tore Snapshot", None, None, False)
|
||||
def canvasRestore(self, event, pathName=None):
|
||||
def canvasImportmIRC(pathName_):
|
||||
rc, error = self.parentCanvas.canvas.importStore.importTextFile(pathName)
|
||||
return (rc, error, self.parentCanvas.canvas.importStore.outMap, self.canvasPathName, self.parentCanvas.canvas.importStore.inSize)
|
||||
if self._promptSaveChanges():
|
||||
rc, newPathName = self._import(canvasImportmIRC, False, self.canvasPathName)
|
||||
|
||||
@GuiCommandDecorator("Restore from file", "Restore from &file", None, None, False)
|
||||
def canvasRestoreFile(self, event):
|
||||
def canvasImportmIRC(pathName):
|
||||
rc, error = self.parentCanvas.canvas.importStore.importTextFile(pathName)
|
||||
return (rc, error, self.parentCanvas.canvas.importStore.outMap, pathName, self.parentCanvas.canvas.importStore.inSize)
|
||||
self._import(canvasImportmIRC, False, pathName)
|
||||
# }}}
|
||||
# {{{ canvasSave(self, event)
|
||||
if self._promptSaveChanges():
|
||||
rc, newPathName = self._import(canvasImportmIRC, False, self.canvasPathName)
|
||||
|
||||
@GuiCommandDecorator("Save", "&Save", ["", wx.ART_FILE_SAVE], [wx.ACCEL_CTRL, ord("S")], None)
|
||||
def canvasSave(self, event, newDirty=False):
|
||||
if self.canvasPathName == None:
|
||||
@ -256,8 +306,7 @@ class RoarCanvasCommandsFile():
|
||||
return True
|
||||
except IOError as error:
|
||||
return False
|
||||
# }}}
|
||||
# {{{ canvasSaveAs(self, event)
|
||||
|
||||
@GuiCommandDecorator("Save As...", "Save &As...", ["", wx.ART_FILE_SAVE_AS], None, None)
|
||||
def canvasSaveAs(self, event):
|
||||
with wx.FileDialog(self.parentCanvas, "Save As", os.getcwd(), "", "mIRC art files (*.txt)|*.txt|All Files (*.*)|*.*", wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dialog:
|
||||
@ -266,24 +315,16 @@ class RoarCanvasCommandsFile():
|
||||
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||
return False
|
||||
else:
|
||||
self.canvasPathName = dialog.GetPath(); self.lastDir = os.path.dirname(self.canvasPathName);
|
||||
if self.canvasSave(event, newDirty=True):
|
||||
self._pushRecent(pathName)
|
||||
# }}}
|
||||
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._recentPush(self.canvasPathName)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
#
|
||||
# __init__(self)
|
||||
def __init__(self):
|
||||
self.imgurApiKey, self.lastFiles, self.lastDir = ImgurApiKey.imgurApiKey if haveImgurApiKey else None, [], None
|
||||
self.menus = (
|
||||
("&File",
|
||||
self.canvasNew, self.canvasOpen, self.canvasOpenRecent, 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, 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
|
||||
|
@ -6,17 +6,24 @@
|
||||
|
||||
from GuiFrame import GuiCommandDecorator
|
||||
from RoarWindowAbout import RoarWindowAbout
|
||||
from RoarWindowMelp import RoarWindowMelp
|
||||
import webbrowser, wx
|
||||
|
||||
class RoarCanvasCommandsHelp():
|
||||
# {{{ canvasAbout(self, event)
|
||||
@GuiCommandDecorator("About", "&About", None, None, True)
|
||||
@GuiCommandDecorator("About roar", "&About roar", None, None, True)
|
||||
def canvasAbout(self, event):
|
||||
RoarWindowAbout(self.parentFrame)
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self)
|
||||
def __init__(self):
|
||||
self.menus, self.toolBars = (("&Help", self.canvasAbout,),), ()
|
||||
@GuiCommandDecorator("View melp?", "View &melp?", None, [wx.MOD_NONE, wx.WXK_F1], True)
|
||||
def canvasMelp(self, event):
|
||||
RoarWindowMelp(self.parentFrame)
|
||||
|
||||
@GuiCommandDecorator("Open &issue on GitHub", "Open &issue on GitHub", None, None, True)
|
||||
def canvasNewIssueGitHub(self, event):
|
||||
webbrowser.open("https://github.com/lalbornoz/roar/issues/new")
|
||||
|
||||
@GuiCommandDecorator("Visit GitHub website", "Visit &GitHub website", None, None, True)
|
||||
def canvasVisitGitHub(self, event):
|
||||
webbrowser.open("https://www.github.com/lalbornoz/roar")
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0
|
||||
|
@ -4,63 +4,29 @@
|
||||
# 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 GuiFrame import GuiCommandListDecorator
|
||||
from OperatorRotate import OperatorRotate
|
||||
from OperatorTile import OperatorTile
|
||||
from ToolObject import ToolObject
|
||||
import copy, wx
|
||||
|
||||
class RoarCanvasCommandsOperators():
|
||||
# {{{ canvasOperator(self, f, idx)
|
||||
@GuiCommandListDecorator(0, "Flip", "&Flip", None, None, None)
|
||||
@GuiCommandListDecorator(1, "Flip horizontally", "Flip &horizontally", None, None, None)
|
||||
@GuiCommandListDecorator(2, "Invert", "&Invert", None, None, None)
|
||||
@GuiCommandListDecorator(2, "Invert colours", "&Invert colours", None, None, None)
|
||||
@GuiCommandListDecorator(3, "Rotate", "&Rotate", None, None, None)
|
||||
@GuiCommandListDecorator(4, "Tile", "&Tile", None, None, False)
|
||||
def canvasOperator(self, f, idx):
|
||||
def canvasOperator_(event):
|
||||
applyOperator = [OperatorFlipVertical, OperatorFlipHorizontal, OperatorInvert][idx]()
|
||||
if (self.currentTool.__class__ == ToolObject) \
|
||||
and (self.currentTool.toolState >= self.currentTool.TS_SELECT):
|
||||
region = self.currentTool.getRegion(self.parentCanvas.canvas)
|
||||
else:
|
||||
region = self.parentCanvas.canvas.map
|
||||
region = applyOperator.apply(copy.deepcopy(region))
|
||||
if (self.currentTool.__class__ == ToolObject) \
|
||||
and (self.currentTool.toolState >= self.currentTool.TS_SELECT):
|
||||
viewRect = self.parentCanvas.GetViewStart()
|
||||
if self.parentCanvas.popupEventDc == None:
|
||||
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas, viewRect)
|
||||
else:
|
||||
eventDc = self.parentCanvas.popupEventDc
|
||||
self.currentTool.setRegion(self.parentCanvas.canvas, None, region, [len(region[0]), len(region)], self.currentTool.external)
|
||||
self.currentTool.onSelectEvent(self.parentCanvas.canvas, (0, 0), self.parentCanvas.dispatchPatchSingle, eventDc, True, wx.MOD_NONE, None, self.currentTool.targetRect, viewRect)
|
||||
else:
|
||||
viewRect = self.parentCanvas.GetViewStart()
|
||||
if self.parentCanvas.popupEventDc == None:
|
||||
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas, viewRect)
|
||||
else:
|
||||
eventDc = self.parentCanvas.popupEventDc
|
||||
self.parentCanvas.canvas.journal.begin()
|
||||
dirty = False
|
||||
for numRow in range(len(region)):
|
||||
for numCol in range(len(region[numRow])):
|
||||
if not dirty:
|
||||
dirty = True
|
||||
self.parentCanvas.dispatchPatchSingle(eventDc, False, [numCol, numRow, *region[numRow][numCol]], viewRect)
|
||||
self.parentCanvas.canvas.journal.end()
|
||||
self.parentCanvas.commands.update(dirty=dirty, undoLevel=self.parentCanvas.canvas.journal.patchesUndoLevel)
|
||||
self.currentOperator = [OperatorFlipVertical, OperatorFlipHorizontal, OperatorInvert, OperatorRotate, OperatorTile][idx]()
|
||||
self.operatorState = None
|
||||
self.parentCanvas.applyOperator(self.currentTool, self.parentCanvas.brushPos, None, False, self.currentOperator, self.parentCanvas.GetViewStart())
|
||||
setattr(canvasOperator_, "attrDict", f.attrList[idx])
|
||||
return canvasOperator_
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self)
|
||||
def __init__(self):
|
||||
self.menus = (
|
||||
("&Operators",
|
||||
self.canvasOperator(self.canvasOperator, 0), self.canvasOperator(self.canvasOperator, 1), self.canvasOperator(self.canvasOperator, 2),
|
||||
),
|
||||
)
|
||||
self.toolBars = ()
|
||||
self.currentOperator, self.operatorState = None, None
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0
|
||||
|
@ -6,51 +6,42 @@
|
||||
|
||||
from GuiFrame import GuiSelectDecorator
|
||||
from ToolCircle import ToolCircle
|
||||
from ToolErase import ToolErase
|
||||
from ToolFill import ToolFill
|
||||
from ToolLine import ToolLine
|
||||
from ToolObject import ToolObject
|
||||
from ToolPickColour import ToolPickColour
|
||||
from ToolRect import ToolRect
|
||||
from ToolText import ToolText
|
||||
import wx
|
||||
|
||||
class RoarCanvasCommandsTools():
|
||||
# {{{ canvasTool(self, f, idx)
|
||||
@GuiSelectDecorator(0, "Circle", "&Circle", ["toolCircle.png"], [wx.ACCEL_CTRL, ord("C")], False)
|
||||
@GuiSelectDecorator(1, "Cursor", "C&ursor", ["toolCursor.png"], [wx.ACCEL_CTRL, ord("U")], False)
|
||||
@GuiSelectDecorator(2, "Fill", "&Fill", ["toolFill.png"], [wx.ACCEL_CTRL, ord("F")], False)
|
||||
@GuiSelectDecorator(3, "Line", "&Line", ["toolLine.png"], [wx.ACCEL_CTRL, ord("L")], False)
|
||||
@GuiSelectDecorator(4, "Object", "&Object", ["toolObject.png"], [wx.ACCEL_CTRL, ord("E")], False)
|
||||
@GuiSelectDecorator(5, "Rectangle", "&Rectangle", ["toolRect.png"], [wx.ACCEL_CTRL, ord("R")], True)
|
||||
@GuiSelectDecorator(6, "Text", "&Text", ["toolText.png"], [wx.ACCEL_CTRL, ord("T")], False)
|
||||
@GuiSelectDecorator(0, "Circle", "&Circle", ["toolCircle.png"], [wx.MOD_NONE, wx.WXK_F4], False)
|
||||
@GuiSelectDecorator(1, "Cursor", "C&ursor", ["toolCursor.png"], [wx.MOD_NONE, wx.WXK_F2], False)
|
||||
@GuiSelectDecorator(2, "Erase", "&Erase", ["toolErase.png"], [wx.MOD_NONE, wx.WXK_F9], False)
|
||||
@GuiSelectDecorator(3, "Fill", "&Fill", ["toolFill.png"], [wx.MOD_NONE, wx.WXK_F5], False)
|
||||
@GuiSelectDecorator(4, "Line", "&Line", ["toolLine.png"], [wx.MOD_NONE, wx.WXK_F6], False)
|
||||
@GuiSelectDecorator(5, "Object", "&Object", ["toolObject.png"], [wx.MOD_NONE, wx.WXK_F8], False)
|
||||
@GuiSelectDecorator(6, "Pick colour", "&Pick colour", ["toolPickColour.png"], [wx.MOD_NONE, wx.WXK_F10], False)
|
||||
@GuiSelectDecorator(7, "Rectangle", "&Rectangle", ["toolRect.png"], [wx.MOD_NONE, wx.WXK_F3], True)
|
||||
@GuiSelectDecorator(8, "Text", "&Text", ["toolText.png"], [wx.MOD_NONE, wx.WXK_F7], False)
|
||||
def canvasTool(self, f, idx):
|
||||
def canvasTool_(event):
|
||||
self.lastTool, self.currentTool = self.currentTool, [ToolCircle, None, ToolFill, ToolLine, ToolObject, ToolRect, ToolText][idx]
|
||||
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.parentFrame.menuItemsById[self.canvasTool.attrList[idx]["id"]].Check(True)
|
||||
toolBar = self.parentFrame.toolBarItemsById[self.canvasTool.attrList[idx]["id"]].GetToolBar()
|
||||
toolBar.ToggleTool(self.canvasTool.attrList[idx]["id"], True)
|
||||
if self.currentTool != None:
|
||||
self.update(toolName=self.currentTool.name)
|
||||
viewRect = self.parentCanvas.GetViewStart()
|
||||
eventDc = self.parentCanvas.backend.getDeviceContext(self.parentCanvas.GetClientSize(), self.parentCanvas, viewRect)
|
||||
self.parentCanvas.applyTool(eventDc, True, None, None, self.parentCanvas.brushPos, False, False, False, self.currentTool, viewRect)
|
||||
else:
|
||||
self.update(toolName="Cursor")
|
||||
self.currentOperator, self.operatorState = None, None
|
||||
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_
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self)
|
||||
def __init__(self):
|
||||
self.menus = (
|
||||
("&Tools",
|
||||
self.canvasTool(self.canvasTool, 1), self.canvasTool(self.canvasTool, 5), self.canvasTool(self.canvasTool, 0), self.canvasTool(self.canvasTool, 2), self.canvasTool(self.canvasTool, 3), self.canvasTool(self.canvasTool, 6), self.canvasTool(self.canvasTool, 4),
|
||||
),
|
||||
)
|
||||
self.toolBars = ()
|
||||
self.currentTool, self.lastTool = None, None
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=0
|
||||
|
@ -5,16 +5,16 @@
|
||||
#
|
||||
|
||||
from GuiWindow import GuiWindow
|
||||
from Rtl import natural_sort
|
||||
from RtlPlatform import getLocalConfPathName
|
||||
from ToolObject import ToolObject
|
||||
import json, wx, sys
|
||||
import time
|
||||
from ToolText import ToolText
|
||||
import copy, hashlib, json, os, pdb, re, time, wx, sys
|
||||
|
||||
class RoarCanvasWindowDropTarget(wx.TextDropTarget):
|
||||
# {{{ done(self)
|
||||
def done(self):
|
||||
self.inProgress = False
|
||||
# }}}
|
||||
# {{{ OnDropText(self, x, y, data)
|
||||
|
||||
def OnDropText(self, x, y, data):
|
||||
rc = False
|
||||
if ((self.parent.commands.currentTool.__class__ != ToolObject) \
|
||||
@ -22,209 +22,340 @@ class RoarCanvasWindowDropTarget(wx.TextDropTarget):
|
||||
and (not self.inProgress):
|
||||
try:
|
||||
dropMap, dropSize = json.loads(data)
|
||||
viewRect = self.parent.GetViewStart()
|
||||
rectX, rectY = x - (x % self.parent.backend.cellSize[0]), y - (y % self.parent.backend.cellSize[1])
|
||||
mapX, mapY = int(rectX / self.parent.backend.cellSize[0] if rectX else 0), int(rectY / self.parent.backend.cellSize[1] if rectY else 0)
|
||||
mapPoint = [m + n for m, n in zip((mapX, mapY), viewRect)]
|
||||
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.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)
|
||||
self.parent.applyTool(eventDc, True, None, None, self.parent.brushPos, False, False, False, self.parent.commands.currentTool, 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)
|
||||
eventDc.SetDeviceOrigin(*eventDcOrigin)
|
||||
rc = True; self.inProgress = True;
|
||||
except:
|
||||
with wx.MessageDialog(self.parent, "Error: {}".format(sys.exc_info()[1]), "", wx.OK | wx.OK_DEFAULT) as dialog:
|
||||
dialogChoice = dialog.ShowModal()
|
||||
return rc
|
||||
# }}}
|
||||
# {{{ __init__(self, parent)
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__(); self.inProgress, self.parent = False, parent;
|
||||
# }}}
|
||||
|
||||
class RoarCanvasWindow(GuiWindow):
|
||||
# {{{ _drawPatch(self, eventDc, isCursor, patch, viewRect)
|
||||
def _drawPatch(self, eventDc, isCursor, patch, viewRect):
|
||||
if not self.canvas.dirtyCursor:
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, viewRect)
|
||||
self.canvas.dirtyCursor = True
|
||||
if self.backend.drawPatch(eventDc, patch, viewRect) and isCursor:
|
||||
patchDeltaCell = self.canvas.map[patch[1]][patch[0]]; patchDelta = [*patch[0:2], *patchDeltaCell];
|
||||
self.canvas.journal.pushCursor(patchDelta)
|
||||
# }}}
|
||||
def _applyPatches(self, eventDc, patches, patchesCursor, rc, commitUndo=True, dirty=True, eventDcResetOrigin=True, hideCursor=True):
|
||||
if rc:
|
||||
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)
|
||||
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=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
|
||||
|
||||
# {{{ applyTool(self, eventDc, eventMouse, keyChar, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, tool, viewRect)
|
||||
def applyTool(self, eventDc, eventMouse, keyChar, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, tool, viewRect):
|
||||
if mapPoint != None:
|
||||
mapPoint = [a + b for a, b in zip(mapPoint, viewRect)]
|
||||
dirty, self.canvas.dirtyCursor, rc = False, False, False
|
||||
self.canvas.journal.begin()
|
||||
def _snapshotsReset(self):
|
||||
self._snapshotFiles, self._snapshotsUpdateLast = [], time.time()
|
||||
self.commands._snapshotsReset()
|
||||
if self.commands.canvasPathName != None:
|
||||
canvasPathName = os.path.abspath(self.commands.canvasPathName)
|
||||
canvasFileName = os.path.basename(canvasPathName)
|
||||
canvasPathNameHash = hashlib.sha1(canvasPathName.encode()).hexdigest()
|
||||
self._snapshotsDirName = os.path.join(getLocalConfPathName(), "{}_{}".format(canvasFileName, canvasPathNameHash))
|
||||
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._snapshotsPush(os.path.join(self._snapshotsDirName, snapshotFile))
|
||||
else:
|
||||
self._snapshotsDirName = None
|
||||
|
||||
def _snapshotsUpdate(self):
|
||||
if self._snapshotsDirName != None:
|
||||
t = time.time()
|
||||
if (t > self._snapshotsUpdateLast) and ((t - self._snapshotsUpdateLast) >= (5 * 60)):
|
||||
try:
|
||||
if not os.path.exists(self._snapshotsDirName):
|
||||
os.makedirs(self._snapshotsDirName)
|
||||
self._snapshotFiles = 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))])
|
||||
if self._snapshotFiles != []:
|
||||
snapshotsCount, snapshotIndex = len(self._snapshotFiles), abs(int(re.match(r'snapshot(\d+)\.txt$', self._snapshotFiles[-1])[1])) + 1
|
||||
else:
|
||||
snapshotsCount, snapshotIndex = 0, 1
|
||||
snapshotPathName = os.path.join(self._snapshotsDirName, "snapshot{}.txt".format(snapshotIndex));
|
||||
self.commands.update(snapshotStatus=True)
|
||||
with open(snapshotPathName, "w", encoding="utf-8") as outFile:
|
||||
self.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||
self.canvas.exportStore.exportTextFile(self.canvas.map, self.canvas.size, outFile)
|
||||
self.SetCursor(wx.Cursor(wx.NullCursor))
|
||||
self.commands.update(snapshotStatus=False); self._snapshotsUpdateLast = time.time();
|
||||
self._snapshotFiles += [os.path.basename(snapshotPathName)];
|
||||
self.commands._snapshotsPush(snapshotPathName)
|
||||
if len(self._snapshotFiles) > 72:
|
||||
for snapshotFile in self._snapshotFiles[:len(self._snapshotFiles) - 8]:
|
||||
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):
|
||||
region = currentTool.getRegion(self.canvas)
|
||||
else:
|
||||
region = self.canvas.map
|
||||
if hasattr(operator, "apply2"):
|
||||
self.commands.update(operator=self.commands.currentOperator.name)
|
||||
if mouseLeftDown:
|
||||
self.commands.operatorState = True if self.commands.operatorState == None else self.commands.operatorState
|
||||
region = operator.apply2(mapPoint, mousePoint, region, copy.deepcopy(region))
|
||||
elif self.commands.operatorState != None:
|
||||
self.commands.currentOperator = None; self.commands.update(operator=None); rc = False;
|
||||
else:
|
||||
region = operator.apply(copy.deepcopy(region)); self.commands.currentOperator = None;
|
||||
if rc:
|
||||
if (currentTool.__class__ == ToolObject) and (currentTool.toolState >= currentTool.TS_SELECT):
|
||||
currentTool.setRegion(self.canvas, None, region, [len(region[0]), len(region)], currentTool.external)
|
||||
rc, patches, patchesCursor = currentTool.onSelectEvent(self.canvas, (0, 0), True, wx.MOD_NONE, None, currentTool.targetRect)
|
||||
patchesCursor = [] if patchesCursor == None else patchesCursor
|
||||
patchesCursor += currentTool._drawSelectRect(currentTool.targetRect)
|
||||
self._applyPatches(eventDc, patches, patchesCursor, rc)
|
||||
else:
|
||||
patches = []
|
||||
for numRow in range(len(region)):
|
||||
for numCol in range(len(region[numRow])):
|
||||
patches += [[numCol, numRow, *region[numRow][numCol]]]
|
||||
self._applyPatches(eventDc, patches, patchesCursor, rc)
|
||||
if (patches != None) and (len(patches) > 0):
|
||||
self._snapshotsUpdate()
|
||||
return rc
|
||||
|
||||
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:
|
||||
if ((mapPoint[0] < self.canvas.size[0]) \
|
||||
and (mapPoint[1] < self.canvas.size[1])) \
|
||||
and ((self.lastCellState == None) \
|
||||
or (self.lastCellState != [list(mapPoint), mouseDragging, mouseLeftDown, mouseRightDown, list(viewRect)])):
|
||||
self.brushPos = list(mapPoint)
|
||||
self.lastCellState = [list(mapPoint), mouseDragging, mouseLeftDown, mouseRightDown, list(viewRect)]
|
||||
self.lastCellState = None if force else self.lastCellState
|
||||
if ((mapPoint[0] < self.canvas.size[0]) and (mapPoint[1] < self.canvas.size[1])) \
|
||||
and ((self.lastCellState == None) or (self.lastCellState != [list(mapPoint), mouseDragging, mouseLeftDown, mouseRightDown, list(viewRect)])):
|
||||
self.brushPos = list(mapPoint) if tool.__class__ != ToolText else self.brushPos
|
||||
if tool != None:
|
||||
rc, dirty = tool.onMouseEvent(self.brushColours, self.brushSize, self.canvas, self.dispatchPatchSingle, eventDc, keyModifiers, self.brushPos, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
rc, patches, patchesCursor = tool.onMouseEvent(mapPoint, self.brushColours, self.brushPos, self.brushSize, self.canvas, keyModifiers, self.brushPos, mouseDragging, mouseLeftDown, mouseRightDown)
|
||||
else:
|
||||
self.dispatchPatchSingle(eventDc, True, [*mapPoint, self.brushColours[0], self.brushColours[0], 0, " "] , viewRect)
|
||||
rc, patches, patchesCursor = True, None, [[*mapPoint, self.brushColours[0], self.brushColours[0], 0, " "]]
|
||||
self.lastCellState = [list(mapPoint), mouseDragging, mouseLeftDown, mouseRightDown, list(viewRect)]
|
||||
else:
|
||||
if tool != None:
|
||||
rc, dirty = tool.onKeyboardEvent(self.brushColours, self.brushSize, self.canvas, self.dispatchPatchSingle, eventDc, keyChar, keyModifiers, self.brushPos, viewRect)
|
||||
else:
|
||||
self.dispatchPatchSingle(eventDc, True, [*mapPoint, self.brushColours[0], self.brushColours[0], 0, " "] , viewRect)
|
||||
if dirty:
|
||||
self.dirty = True
|
||||
self.commands.update(dirty=self.dirty, cellPos=self.brushPos, undoLevel=self.canvas.journal.patchesUndoLevel)
|
||||
else:
|
||||
self.commands.update(cellPos=mapPoint if mapPoint else self.brushPos)
|
||||
self.canvas.journal.end()
|
||||
if rc and (tool.__class__ == ToolObject):
|
||||
if tool.toolState > tool.TS_NONE:
|
||||
self.commands.update(undoInhibit=True)
|
||||
elif tool.toolState == tool.TS_NONE:
|
||||
if tool.external:
|
||||
self.commands.currentTool, self.commands.lastTool = self.commands.lastTool, self.commands.currentTool
|
||||
self.commands.update(toolName=self.commands.currentTool.name, undoInhibit=False)
|
||||
self.dropTarget.done()
|
||||
else:
|
||||
self.commands.update(undoInhibit=False)
|
||||
return rc
|
||||
# }}}
|
||||
# {{{ dispatchDeltaPatches(self, deltaPatches)
|
||||
def dispatchDeltaPatches(self, deltaPatches):
|
||||
viewRect = self.GetViewStart()
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect)
|
||||
if self.canvas.dirtyCursor:
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, viewRect)
|
||||
self.canvas.dirtyCursor = False
|
||||
for patch in deltaPatches:
|
||||
if patch == None:
|
||||
continue
|
||||
elif patch[0] == "resize":
|
||||
del eventDc; self.resize(patch[1:], False); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart());
|
||||
else:
|
||||
self.canvas._commitPatch(patch); self.backend.drawPatch(eventDc, patch, self.GetViewStart());
|
||||
# }}}
|
||||
# {{{ dispatchPatch(self, eventDc, isCursor, patch, viewRect)
|
||||
def dispatchPatch(self, eventDc, isCursor, patch, viewRect):
|
||||
if self.canvas.dispatchPatch(isCursor, patch, False if isCursor else True):
|
||||
self._drawPatch(eventDc, isCursor, patch, viewRect)
|
||||
# }}}
|
||||
# {{{ dispatchPatchSingle(self, eventDc, isCursor, patch, viewRect)
|
||||
def dispatchPatchSingle(self, eventDc, isCursor, patch, viewRect):
|
||||
if self.canvas.dispatchPatchSingle(isCursor, patch, False if isCursor else True):
|
||||
self._drawPatch(eventDc, isCursor, patch, viewRect)
|
||||
# }}}
|
||||
# {{{ resize(self, newSize, commitUndo=True)
|
||||
def resize(self, newSize, commitUndo=True):
|
||||
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):
|
||||
super().resize([a * b for a, b in zip(newSize, self.backend.cellSize)])
|
||||
self.backend.resize(newSize, self.backend.cellSize)
|
||||
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
|
||||
if deltaSize[0] > 0:
|
||||
for numRow in range(oldSize[1]):
|
||||
for numNewCol in range(oldSize[0], newSize[0]):
|
||||
self._drawPatch(eventDc, False, [numNewCol, numRow, 1, 1, 0, " "], viewRect)
|
||||
if deltaSize[1] > 1:
|
||||
for numNewRow in range(oldSize[1], newSize[1]):
|
||||
for numNewCol in range(newSize[0]):
|
||||
self._drawPatch(eventDc, False, [numNewCol, numNewRow, 1, 1, 0, " "], viewRect)
|
||||
self.commands.update(size=newSize, undoLevel=self.canvas.journal.patchesUndoLevel)
|
||||
# }}}
|
||||
# {{{ update(self, newSize, commitUndo=True, newCanvas=None)
|
||||
def update(self, newSize, commitUndo=True, newCanvas=None):
|
||||
self.resize(newSize, commitUndo)
|
||||
self.canvas.update(newSize, newCanvas)
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, self.GetViewStart())
|
||||
for numRow in range(newSize[1]):
|
||||
for numCol in range(newSize[0]):
|
||||
self.backend.drawPatch(eventDc, [numCol, numRow, *self.canvas.map[numRow][numCol]], self.GetViewStart())
|
||||
# }}}
|
||||
rc, patches, patchesCursor = tool.onKeyboardEvent(mapPoint, self.brushColours, self.brushPos, self.brushSize, self.canvas, keyChar, keyCode, keyModifiers, self.brushPos)
|
||||
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) 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 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
|
||||
|
||||
# {{{ onKeyboardInput(self, event)
|
||||
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()
|
||||
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) and (self.brushPos[1] < (self.canvas.size[1] - 1)):
|
||||
self.brushPos = [self.brushPos[0], self.brushPos[1] + 1]
|
||||
elif (keyCode == wx.WXK_LEFT) and (self.brushPos[0] > 0):
|
||||
self.brushPos = [self.brushPos[0] - 1, self.brushPos[1]]
|
||||
elif (keyCode == wx.WXK_RIGHT) and (self.brushPos[0] < (self.canvas.size[0] - 1)):
|
||||
self.brushPos = [self.brushPos[0] + 1, self.brushPos[1]]
|
||||
elif (keyCode == wx.WXK_UP) and (self.brushPos[1] > 0):
|
||||
self.brushPos = [self.brushPos[0], self.brushPos[1] - 1]
|
||||
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, self.brushPos, False, False, False, self.commands.currentTool, viewRect)
|
||||
else:
|
||||
if not self.applyTool(eventDc, False, chr(event.GetUnicodeKey()), keyModifiers, None, None, None, None, self.commands.currentTool, viewRect):
|
||||
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()
|
||||
# }}}
|
||||
# {{{ onEnterWindow(self, event)
|
||||
def onEnterWindow(self, event):
|
||||
self.lastCellState = None
|
||||
# }}}
|
||||
# {{{ onLeaveWindow(self, event)
|
||||
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.journal, eventDc, self.GetViewStart())
|
||||
self.cursorHide()
|
||||
self.lastCellState = None
|
||||
# }}}
|
||||
# {{{ onMouseInput(self, event)
|
||||
|
||||
def onMouseInput(self, event):
|
||||
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
|
||||
mouseDragging, mouseLeftDown, mouseRightDown = event.Dragging(), event.LeftIsDown(), event.RightIsDown()
|
||||
self.lastMouseState = [mouseDragging, mouseLeftDown, mouseRightDown]
|
||||
mapPoint = self.backend.xlateEventPoint(event, eventDc, viewRect)
|
||||
if mouseRightDown \
|
||||
if viewRect != (0, 0):
|
||||
mapPoint = [a + b for a, b in zip(mapPoint, viewRect)]
|
||||
if self.commands.currentOperator != None:
|
||||
self.applyOperator(self.commands.currentTool, mapPoint, mouseLeftDown, event.GetLogicalPosition(eventDc), self.commands.currentOperator, viewRect)
|
||||
elif mouseRightDown \
|
||||
and (self.commands.currentTool.__class__ == ToolObject) \
|
||||
and (self.commands.currentTool.toolState >= self.commands.currentTool.TS_SELECT):
|
||||
self.popupEventDc = eventDc; self.PopupMenu(self.operatorsMenu); self.popupEventDc = None;
|
||||
elif not self.applyTool(eventDc, True, None, event.GetModifiers(), mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, self.commands.currentTool, viewRect):
|
||||
elif not self.applyTool(eventDc, True, None, None, event.GetModifiers(), mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, self.commands.currentTool, viewRect):
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onMouseWheel(self, event)
|
||||
|
||||
def onMouseWheel(self, event):
|
||||
if event.GetModifiers() == wx.MOD_CONTROL:
|
||||
cd = +1 if event.GetWheelRotation() >= event.GetWheelDelta() else -1
|
||||
newCellSize = [cs + cd for cs in self.backend.cellSize]
|
||||
if (newCellSize[0] > 0) and (newCellSize[1] > 0):
|
||||
self.backend.cellSize = newCellSize
|
||||
delta, modifiers = +1 if event.GetWheelRotation() >= event.GetWheelDelta() else -1, event.GetModifiers()
|
||||
if modifiers == (wx.MOD_CONTROL | wx.MOD_ALT):
|
||||
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;
|
||||
super().resize([a * b for a, b in zip(self.canvas.size, self.backend.cellSize)])
|
||||
self.backend.resize(self.canvas.size, self.backend.cellSize)
|
||||
viewRect = self.GetViewStart(); eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect);
|
||||
patches = []
|
||||
for numRow in range(self.canvas.size[1]):
|
||||
for numCol in range(len(self.canvas.map[numRow])):
|
||||
self._drawPatch(eventDc, False, [numCol, numRow, *self.canvas.map[numRow][numCol]], viewRect)
|
||||
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(); 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:
|
||||
self.commands.canvasBrushSize(self.commands.canvasBrushSize, 2, 1 if delta > 0 else 0)(None)
|
||||
else:
|
||||
event.Skip()
|
||||
# }}}
|
||||
# {{{ onPaint(self, event)
|
||||
|
||||
def onPaint(self, event):
|
||||
viewRect = self.GetViewStart()
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self, viewRect)
|
||||
self.backend.drawCursorMaskWithJournal(self.canvas.journal, eventDc, viewRect)
|
||||
self.backend.onPaint(self.GetClientSize(), self, self.GetViewStart())
|
||||
# }}}
|
||||
eventDc = self.backend.getDeviceContext(self.GetClientSize(), self)
|
||||
self.backend.onPaint(self.GetClientSize(), self, viewRect)
|
||||
del eventDc; self._windowEraseBackground(wx.PaintDC(self));
|
||||
|
||||
#
|
||||
# __init__(self, backend, canvas, cellSize, commands, parent, parentFrame, pos, scrollStep, size): initialisation method
|
||||
def __init__(self, backend, canvas, cellSize, commands, parent, parentFrame, pos, scrollStep, size):
|
||||
super().__init__(parent, pos, scrollStep)
|
||||
self.size = size
|
||||
self.backend, self.canvas, self.cellSize, self.commands, self.parentFrame = backend(self.size, cellSize), canvas, cellSize, commands(self, parentFrame), parentFrame
|
||||
self.brushColours, self.brushPos, self.brushSize, self.dirty, self.lastCellState = [4, 1], [0, 0], [1, 1], False, None
|
||||
self.popupEventDc = None
|
||||
self.dropTarget = RoarCanvasWindowDropTarget(self)
|
||||
def resize(self, newSize, commitUndo=True, dirty=True, freeze=True):
|
||||
if freeze:
|
||||
self.Freeze()
|
||||
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)]
|
||||
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.patchesUndoLevel)
|
||||
if commitUndo:
|
||||
self._snapshotsUpdate()
|
||||
if freeze:
|
||||
self.cursorShow(); self.Thaw(); self._windowEraseBackground(wx.ClientDC(self));
|
||||
|
||||
def undo(self, redo=False):
|
||||
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;
|
||||
self.resize(patch[1:], False, freeze=False)
|
||||
else:
|
||||
patches += [patch]
|
||||
eventDc = self._applyPatches(None, patches, None, True, commitUndo=False, hideCursor=False)
|
||||
self.cursorShow(eventDc, True, None)
|
||||
if freezeFlag:
|
||||
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);
|
||||
patches = []
|
||||
for numRow in range(newSize[1]):
|
||||
for numCol in range(newSize[0]):
|
||||
patches += [[numCol, numRow, *self.canvas.map[numRow][numCol]]]
|
||||
self._applyPatches(None, patches, None, True, dirty=False)
|
||||
|
||||
def __init__(self, backend, canvas, commands, parent, pos, size):
|
||||
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.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.Bind(wx.EVT_MOUSEWHEEL, self.onMouseWheel)
|
||||
self._snapshotsReset()
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -15,50 +15,61 @@ from glob import glob
|
||||
import os, random, sys, wx
|
||||
|
||||
class RoarClient(GuiFrame):
|
||||
# {{{ _getIconPathName(self)
|
||||
def _getIconPathName(self):
|
||||
iconPathNames = glob(os.path.join("assets", "images", "logo*.bmp"))
|
||||
return iconPathNames[random.randint(0, len(iconPathNames) - 1)]
|
||||
# }}}
|
||||
# {{{ _initToolBitmaps(self, toolBars)
|
||||
|
||||
def _initToolBitmaps(self, toolBars):
|
||||
basePathName = os.path.join(os.path.dirname(sys.argv[0]), "assets", "images")
|
||||
for toolBar in toolBars:
|
||||
for toolBarItem in [i for i in toolBar if i != NID_TOOLBAR_HSEP]:
|
||||
toolBarItem.attrDict["icon"] = self.loadBitmap(basePathName, toolBarItem.attrDict["icon"])
|
||||
# }}}
|
||||
|
||||
# {{{ onChar(self, event)
|
||||
def onChar(self, event):
|
||||
self.canvasPanel.onKeyboardInput(event)
|
||||
# }}}
|
||||
# {{{ onMouseWheel(self, event)
|
||||
|
||||
def onClose(self, event):
|
||||
if not self.canvasPanel.commands.exiting:
|
||||
closeFlag = self.canvasPanel.commands._promptSaveChanges()
|
||||
else:
|
||||
closeFlag = True
|
||||
if closeFlag:
|
||||
event.Skip();
|
||||
|
||||
def onMouseWheel(self, event):
|
||||
self.canvasPanel.GetEventHandler().ProcessEvent(event)
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, parent, defaultCanvasPos=(0, 75), defaultCanvasSize=(100, 30), defaultCellSize=(7, 14), size=(840, 630), title=""): initialisation method
|
||||
def __init__(self, parent, defaultCanvasPos=(0, 75), defaultCanvasSize=(100, 30), defaultCellSize=(7, 14), size=(840, 630), title=""):
|
||||
def onSize(self, event):
|
||||
pass
|
||||
|
||||
def __init__(self, parent, defaultCanvasPos=(0, 75), defaultCanvasSize=(100, 30), size=(840, 640), title=""):
|
||||
super().__init__(self._getIconPathName(), size, parent, title)
|
||||
self.canvas = Canvas(defaultCanvasSize)
|
||||
self.canvasPanel = RoarCanvasWindow(GuiCanvasWxBackend, self.canvas, defaultCellSize, RoarCanvasCommands, self.panelSkin, self, defaultCanvasPos, defaultCellSize, defaultCanvasSize)
|
||||
self.loadAccels(self.canvasPanel.commands.menus, self.canvasPanel.commands.toolBars)
|
||||
self.canvasPanel = RoarCanvasWindow(GuiCanvasWxBackend, self.canvas, RoarCanvasCommands, self, defaultCanvasPos, defaultCanvasSize)
|
||||
self.loadAccels(self.canvasPanel.commands.accels, self.canvasPanel.commands.menus, self.canvasPanel.commands.toolBars)
|
||||
self.loadMenus(self.canvasPanel.commands.menus)
|
||||
self._initToolBitmaps(self.canvasPanel.commands.toolBars)
|
||||
self.loadToolBars(self.canvasPanel.commands.toolBars)
|
||||
|
||||
self.canvasPanel.commands.canvasNew(None)
|
||||
self.canvasPanel.commands.canvasTool(self.canvasPanel.commands.canvasTool, 1)(None)
|
||||
self.canvasPanel.commands.update(brushSize=self.canvasPanel.brushSize, colours=self.canvasPanel.brushColours)
|
||||
self.addWindow(self.canvasPanel, expand=True)
|
||||
self.assetsWindow = RoarAssetsWindow(GuiCanvasWxBackend, defaultCellSize, self)
|
||||
self.addWindow(self.canvasPanel)
|
||||
self.assetsWindow = RoarAssetsWindow(GuiCanvasWxBackend, self)
|
||||
self.canvasPanel.commands.canvasAssetsWindowShow(None)
|
||||
|
||||
# XXX
|
||||
self.canvasPanel.operatorsMenu = wx.Menu()
|
||||
for menuItem in self.canvasPanel.commands.menus[3][1:]:
|
||||
menuItemWindow = self.canvasPanel.operatorsMenu.Append(menuItem.attrDict["id"], menuItem.attrDict["label"], menuItem.attrDict["caption"])
|
||||
self.Bind(wx.EVT_MENU, self.onMenu, menuItemWindow)
|
||||
|
||||
self.canvasPanel.commands.canvasClearRecent.attrDict["id"] = wx.NewId()
|
||||
self.canvasPanel.commands.canvasOpenRecent.attrDict["menu"].AppendSeparator()
|
||||
self.canvasPanel.commands.canvasRestore.attrDict["menu"].AppendSeparator()
|
||||
self.canvasPanel.commands.canvasRestoreFile.attrDict["id"] = wx.NewId()
|
||||
menuItemWindow = self.canvasPanel.commands.canvasOpenRecent.attrDict["menu"].Append(self.canvasPanel.commands.canvasClearRecent.attrDict["id"], self.canvasPanel.commands.canvasClearRecent.attrDict["label"], self.canvasPanel.commands.canvasClearRecent.attrDict["caption"])
|
||||
menuItemWindow = self.canvasPanel.commands.canvasRestore.attrDict["menu"].Append(self.canvasPanel.commands.canvasRestoreFile.attrDict["id"], self.canvasPanel.commands.canvasRestoreFile.attrDict["label"], self.canvasPanel.commands.canvasRestoreFile.attrDict["caption"])
|
||||
self.canvasPanel.commands.canvasOpenRecent.attrDict["menu"].Bind(wx.EVT_MENU, self.canvasPanel.commands.canvasClearRecent, menuItemWindow)
|
||||
self.canvasPanel.commands.canvasRestore.attrDict["menu"].Bind(wx.EVT_MENU, self.canvasPanel.commands.canvasRestoreFile, menuItemWindow)
|
||||
self.Bind(wx.EVT_CLOSE, self.onClose); self.Bind(wx.EVT_SIZE, self.onSize);
|
||||
self.toolBarPanes[0].BestSize(0, 0).Right(); self.toolBarPanes[1].BestSize(0, 0).Right(); self.auiManager.Update();
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -8,22 +8,19 @@ from glob import glob
|
||||
import os, random, wx, wx.adv
|
||||
|
||||
class RoarWindowAbout(wx.Dialog):
|
||||
# {{{ onButtonRoar(self, event)
|
||||
def onButtonRoar(self, event):
|
||||
self.Destroy()
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, parent, minSize=(320, 300), title="About roar")
|
||||
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)]
|
||||
self.logo = wx.StaticBitmap(self.panel, -1, wx.Bitmap(logoPathName))
|
||||
|
||||
self.title = wx.StaticText(self.panel, label="roar -- mIRC art editor for Windows && Linux\n__ROAR_RELEASE_VERSION__\nhttps://www.github.com/lalbornoz/roar/\nCopyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>", style=wx.ALIGN_CENTER)
|
||||
self.title = wx.StaticText(self.panel, label="roar -- mIRC art editor for Windows && Linux\n__ROAR_RELEASE_VERSION__\nhttps://www.github.com/lalbornoz/roar/\nCopyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>\nhttps://www.lucioillanes.de\n", style=wx.ALIGN_CENTER)
|
||||
self.title.SetFont(wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=False))
|
||||
labelsText = ["&roar!", "&ROAR!", "&roaaaaaaar!", "&ROAROARAOR", "_&ROAR_"]
|
||||
labelText = labelsText[random.randint(0, len(labelsText) - 1)]
|
||||
@ -39,7 +36,7 @@ class RoarWindowAbout(wx.Dialog):
|
||||
self.SetTitle(title)
|
||||
|
||||
soundBitePathNames = glob(os.path.join("assets", "audio", "roar*.wav"))
|
||||
soundBitePathName = soundBitePathNames[random.randint(0, len(logoPathNames) - 1)]
|
||||
soundBitePathName = soundBitePathNames[random.randint(0, len(soundBitePathNames) - 1)]
|
||||
self.soundBite = wx.adv.Sound(soundBitePathName)
|
||||
if self.soundBite.IsOk():
|
||||
self.soundBite.Play(wx.adv.SOUND_ASYNC)
|
||||
|
32
libroar/RoarWindowMelp.py
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# RoarWindowMelp.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
import os, wx
|
||||
|
||||
class RoarWindowMelp(wx.Dialog):
|
||||
def onButtonRoar(self, event):
|
||||
self.Destroy()
|
||||
|
||||
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())
|
||||
self.title = wx.StaticText(self.panel, label=helpLabel, style=wx.ALIGN_LEFT)
|
||||
self.title.SetFont(wx.Font(8, wx.MODERN, wx.NORMAL, wx.NORMAL, underline=False))
|
||||
self.buttonRoar = wx.Button(self.panel, label="&explodes.")
|
||||
self.buttonRoar.Bind(wx.EVT_BUTTON, self.onButtonRoar)
|
||||
self.sizer.AddMany(((self.title, 1, wx.ALL | wx.CENTER | wx.EXPAND, 4), (self.buttonRoar, 0, wx.ALL | wx.CENTER, 4),))
|
||||
self.panel.SetSizerAndFit(self.sizer)
|
||||
newSize = self.sizer.ComputeFittingWindowSize(self)
|
||||
self.SetSize((newSize[0] + 128, newSize[1],)); self.Center();
|
||||
self.SetTitle(title)
|
||||
|
||||
self.ShowModal()
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
@ -11,13 +11,11 @@ import select, socket, time
|
||||
class IrcClient:
|
||||
"""Non-blocking abstraction over the IRC protocol"""
|
||||
|
||||
# {{{ close(self): Close connection to server
|
||||
def close(self):
|
||||
if self.clientSocket != None:
|
||||
self.clientSocket.close()
|
||||
self.clientSocket = self.clientSocketFile = None;
|
||||
# }}}
|
||||
# {{{ connect(self, localAddr=None, preferFamily=socket.AF_INET, timeout=None): Connect to server and register w/ optional timeout
|
||||
|
||||
def connect(self, localAddr=None, preferFamily=socket.AF_INET, timeout=None):
|
||||
gaiInfo = socket.getaddrinfo(self.serverHname, self.serverPort,
|
||||
preferFamily, socket.SOCK_STREAM, socket.IPPROTO_TCP)
|
||||
@ -41,8 +39,7 @@ class IrcClient:
|
||||
self.queue("NICK", self.clientNick)
|
||||
self.queue("USER", self.clientIdent, "0", "0", self.clientGecos)
|
||||
return True
|
||||
# }}}
|
||||
# {{{ queue(self, *args): Parse and queue single line to server from list
|
||||
|
||||
def queue(self, *args):
|
||||
msg = ""; argNumMax = len(args);
|
||||
for argNum in range(argNumMax):
|
||||
@ -51,8 +48,7 @@ class IrcClient:
|
||||
else:
|
||||
msg += args[argNum] + " "
|
||||
self.clientQueue.append((msg + "\r\n").encode())
|
||||
# }}}
|
||||
# {{{ readline(self, timeout=30): Read and parse single line from server into canonicalised list, honouring timers
|
||||
|
||||
def readline(self, timeout=30):
|
||||
if self.clientNextTimeout:
|
||||
timeNow = time.time()
|
||||
@ -85,8 +81,7 @@ class IrcClient:
|
||||
else:
|
||||
msg = [""] + msg[0:]
|
||||
return msg
|
||||
# }}}
|
||||
# {{{ unqueue(self, timeout=15): Send all queued lines to server, honouring timers
|
||||
|
||||
def unqueue(self, timeout=15):
|
||||
while self.clientQueue:
|
||||
msg = self.clientQueue[0]; msgLen = len(msg); msgBytesSent = 0;
|
||||
@ -111,10 +106,7 @@ class IrcClient:
|
||||
msg = msg[msgBytesSent:]; msgLen -= msgBytesSent;
|
||||
del self.clientQueue[0]
|
||||
return True
|
||||
# }}}
|
||||
|
||||
#
|
||||
# __init__(self, serverHname, serverPort, clientNick, clientIdent, clientGecos): initialisation method
|
||||
def __init__(self, serverHname, serverPort, clientNick, clientIdent, clientGecos):
|
||||
self.clientGecos, self.clientIdent, self.clientNick = clientGecos, clientIdent, clientNick
|
||||
self.clientNextTimeout, self.clientQueue, self.clientSocket, self.clientSocketFile = None, None, None, None
|
||||
|
18
librtl/Rtl.py
Normal file
@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Rtl.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
# This project is licensed under the terms of the MIT licence.
|
||||
#
|
||||
|
||||
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)]
|
||||
return sorted(l, key=alphanum_key)
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
@ -7,10 +7,8 @@
|
||||
|
||||
import os, platform
|
||||
|
||||
# {{{ getLocalConfPathName(*args)
|
||||
def getLocalConfPathName(*args):
|
||||
vname = "LOCALAPPDATA" if platform.system() == "Windows" else "HOME"
|
||||
return os.path.join(os.getenv(vname), "roar", *args)
|
||||
# }}}
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -5,13 +5,10 @@
|
||||
#
|
||||
|
||||
class Tool(object):
|
||||
# {{{ onKeyboardEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyChar, keyModifiers, mapPoint, viewRect)
|
||||
def onKeyboardEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyChar, keyModifiers, mapPoint, viewRect):
|
||||
return False, False
|
||||
# }}}
|
||||
# {{{ onMouseEvent(self, brushColours, brushSize, dispatchFn, eventDc, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
return False, False
|
||||
# }}}
|
||||
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyChar, keyCode, keyModifiers, mapPoint):
|
||||
return False, None, None
|
||||
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
return False, None, None
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -5,35 +5,60 @@
|
||||
#
|
||||
|
||||
from Tool import Tool
|
||||
import wx
|
||||
|
||||
class ToolCircle(Tool):
|
||||
name = "Circle"
|
||||
TS_NONE = 0
|
||||
TS_ORIGIN = 1
|
||||
|
||||
#
|
||||
# onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
brushColours, dirty = brushColours.copy(), False
|
||||
if mouseLeftDown:
|
||||
brushColours[1] = brushColours[0]
|
||||
elif mouseRightDown:
|
||||
brushColours[0] = brushColours[1]
|
||||
else:
|
||||
brushColours[1] = brushColours[0]
|
||||
_brushSize = brushSize[0] * 2
|
||||
originPoint, radius = (_brushSize / 2, _brushSize / 2), _brushSize
|
||||
def _drawCircle(self, brushColours, canvas, mapPoint, originPoint, radius):
|
||||
cells, patches = [], []
|
||||
for brushY in range(-radius, radius + 1):
|
||||
cells += [[]]
|
||||
for brushX in range(-radius, radius + 1):
|
||||
if ((brushX ** 2) + (brushY ** 2) < (((radius ** 2) + radius) * 0.8)):
|
||||
patch = [ \
|
||||
mapPoint[0] + int(originPoint[0] + brushX), \
|
||||
mapPoint[1] + int(originPoint[1] + brushY), \
|
||||
*brushColours, 0, " "]
|
||||
if mouseLeftDown or mouseRightDown:
|
||||
if not dirty:
|
||||
dirty = True
|
||||
dispatchFn(eventDc, False, patch, viewRect); dispatchFn(eventDc, True, patch, viewRect);
|
||||
cells[-1] += [[mapPoint[i] + int(originPoint[i] + o) for i, o in zip((0, 1,), (brushX, brushY,))]]
|
||||
if cells[-1] == []:
|
||||
del cells[-1]
|
||||
for numRow in range(len(cells)):
|
||||
for numCol in range(len(cells[numRow])):
|
||||
point = cells[numRow][numCol]
|
||||
if (point[0] >= 0) and (point[1] >= 0):
|
||||
if ((numRow == 0) or (numRow == (len(cells) - 1))) \
|
||||
or ((numCol == 0) or (numCol == (len(cells[numRow]) - 1))):
|
||||
patch = [*cells[numRow][numCol], brushColours[0], brushColours[0], 0, " "]
|
||||
elif ((numRow > 0) and (cells[numRow][numCol][0] < cells[numRow - 1][0][0])) \
|
||||
or ((numRow < len(cells)) and (cells[numRow][numCol][0] < cells[numRow + 1][0][0])):
|
||||
patch = [*cells[numRow][numCol], brushColours[0], brushColours[0], 0, " "]
|
||||
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, " "]
|
||||
else:
|
||||
dispatchFn(eventDc, True, patch, viewRect)
|
||||
return True, dirty
|
||||
patch = [*cells[numRow][numCol], brushColours[1], brushColours[1], 0, " "]
|
||||
patches += [patch]
|
||||
return patches
|
||||
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
brushColours, brushSize = [brushColours[1], brushColours[0]] if mouseRightDown else brushColours, brushSize[0] * 2
|
||||
if self.toolState == self.TS_NONE:
|
||||
originPoint, radius, targetPoint = list(mapPoint), brushSize, (brushSize / 2,) * 2
|
||||
if (keyModifiers == wx.MOD_CONTROL) and (mouseLeftDown or mouseRightDown):
|
||||
self.brushColours, isCursor, self.originPoint, self.toolState = brushColours, True, originPoint, self.TS_ORIGIN
|
||||
else:
|
||||
isCursor = not (mouseLeftDown or mouseRightDown)
|
||||
elif self.toolState == self.TS_ORIGIN:
|
||||
if mapPoint[0] > self.originPoint[0]:
|
||||
brushSize += (mapPoint[0] - self.originPoint[0]); brushSize = brushSize + (brushSize % 2);
|
||||
brushColours, originPoint, radius, targetPoint = self.brushColours, self.originPoint, brushSize, (brushSize / 2,) * 2
|
||||
if not (mouseLeftDown or mouseRightDown):
|
||||
self.brushColours, isCursor, self.originPoint, self.toolState = None, False, None, self.TS_NONE
|
||||
else:
|
||||
isCursor = True
|
||||
patches = self._drawCircle(brushColours, canvas, originPoint, targetPoint, radius)
|
||||
return True, patches if not isCursor else None, patches if isCursor else None
|
||||
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args); self.brushColours, self.originPoint, self.toolState = None, None, self.TS_NONE;
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
32
libtools/ToolErase.py
Normal file
@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# ToolErase.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
from Tool import Tool
|
||||
|
||||
class ToolErase(Tool):
|
||||
name = "Erase"
|
||||
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
brushColours, brushSize, isCursor, patches, patchesCursor = list(brushColours), list(brushSize), not (mouseLeftDown or mouseRightDown), [], []
|
||||
if brushSize[0] > 1:
|
||||
brushSize[0] *= 2
|
||||
for brushRow in range(brushSize[1]):
|
||||
for brushCol in range(brushSize[0]):
|
||||
if mouseLeftDown:
|
||||
patches += [[mapPoint[0] + brushCol, mapPoint[1] + brushRow, brushColours[1], brushColours[1], 0, " "]]
|
||||
elif mouseRightDown \
|
||||
and ((mapPoint[0] + brushCol) < canvas.size[0]) \
|
||||
and ((mapPoint[1] + brushRow) < canvas.size[1]) \
|
||||
and (canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][1] == brushColours[1]):
|
||||
if canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][3] == " ":
|
||||
patches += [[mapPoint[0] + brushCol, mapPoint[1] + brushRow, brushColours[0], brushColours[0], *canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][2:]]]
|
||||
else:
|
||||
patches += [[mapPoint[0] + brushCol, mapPoint[1] + brushRow, canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][0], brushColours[0], *canvas.map[mapPoint[1] + brushRow][mapPoint[0] + brushCol][2:]]]
|
||||
else:
|
||||
patchesCursor += [[mapPoint[0] + brushCol, mapPoint[1] + brushRow, brushColours[1], brushColours[1], 0, " "]]
|
||||
return True, patches if not isCursor else None, patchesCursor
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
@ -5,26 +5,22 @@
|
||||
#
|
||||
|
||||
from Tool import Tool
|
||||
import wx
|
||||
|
||||
class ToolFill(Tool):
|
||||
name = "Fill"
|
||||
|
||||
#
|
||||
# onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
dirty, pointsDone, pointStack, testColour, = False, [], [list(mapPoint)], canvas.map[mapPoint[1]][mapPoint[0]][0:2]
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
isCursor, patches, pointsDone, pointStack, testChar, testColour = not (mouseLeftDown or mouseRightDown), [], [], [list(mapPoint)], canvas.map[mapPoint[1]][mapPoint[0]][3], canvas.map[mapPoint[1]][mapPoint[0]][0:2]
|
||||
if mouseLeftDown or mouseRightDown:
|
||||
if mouseRightDown:
|
||||
brushColours = [brushColours[1], brushColours[0]]
|
||||
fillColour = brushColours[0] if mouseLeftDown else brushColours[1]
|
||||
while len(pointStack) > 0:
|
||||
point = pointStack.pop()
|
||||
pointCell = canvas.map[point[1]][point[0]]
|
||||
if (pointCell[0:2] == testColour) \
|
||||
if ((pointCell[1] == testColour[1]) and ((pointCell[3] == testChar) or (keyModifiers == wx.MOD_CONTROL))) \
|
||||
or ((pointCell[3] == " ") and (pointCell[1] == testColour[1])):
|
||||
if not point in pointsDone:
|
||||
if not dirty:
|
||||
dirty = True
|
||||
dispatchFn(eventDc, False, [*point, brushColours[0], brushColours[0], 0, " "], viewRect)
|
||||
patches += [[*point, fillColour, fillColour, 0, " "]]
|
||||
if point[0] > 0:
|
||||
pointStack.append([point[0] - 1, point[1]])
|
||||
if point[0] < (canvas.size[0] - 1):
|
||||
@ -35,8 +31,7 @@ class ToolFill(Tool):
|
||||
pointStack.append([point[0], point[1] + 1])
|
||||
pointsDone += [point]
|
||||
else:
|
||||
patch = [mapPoint[0], mapPoint[1], brushColours[0], brushColours[0], 0, " "]
|
||||
dispatchFn(eventDc, True, patch, viewRect)
|
||||
return True, dirty
|
||||
patches = [[mapPoint[0], mapPoint[1], brushColours[0], brushColours[0], 0, " "]]
|
||||
return True, patches if not isCursor else None, patches if isCursor else None
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -11,10 +11,8 @@ class ToolLine(Tool):
|
||||
TS_NONE = 0
|
||||
TS_ORIGIN = 1
|
||||
|
||||
# {{{ _getLine(self, brushColours, brushSize, dispatchFn, eventDc, isCursor, originPoint, targetPoint, viewRect)
|
||||
def _getLine(self, brushColours, brushSize, dispatchFn, eventDc, isCursor, originPoint, targetPoint, viewRect):
|
||||
dirty = False
|
||||
originPoint, targetPoint = originPoint.copy(), targetPoint.copy()
|
||||
def _getLine(self, brushColours, brushSize, isCursor, originPoint, targetPoint):
|
||||
originPoint, patches, targetPoint = originPoint.copy(), [], targetPoint.copy()
|
||||
pointDelta = self._pointDelta(originPoint, targetPoint)
|
||||
lineXSign = 1 if pointDelta[0] > 0 else -1; lineYSign = 1 if pointDelta[1] > 0 else -1;
|
||||
pointDelta = [abs(a) for a in pointDelta]
|
||||
@ -24,36 +22,28 @@ class ToolLine(Tool):
|
||||
lineXX, lineXY, lineYX, lineYY = 0, lineYSign, lineXSign, 0
|
||||
pointDelta = [pointDelta[1], pointDelta[0]]
|
||||
lineD = 2 * pointDelta[1] - pointDelta[0]; lineY = 0;
|
||||
pointsDone = []
|
||||
for lineX in range(pointDelta[0] + 1):
|
||||
for brushStep in range(brushSize[0]):
|
||||
patch = [ \
|
||||
originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, \
|
||||
originPoint[1] + lineX * lineXY + lineY * lineYY, \
|
||||
*brushColours, 0, " "]
|
||||
if isCursor:
|
||||
dispatchFn(eventDc, False, patch, viewRect); dispatchFn(eventDc, True, patch, viewRect);
|
||||
else:
|
||||
if not dirty:
|
||||
dirty = True
|
||||
dispatchFn(eventDc, True, patch, viewRect)
|
||||
if not ([originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, originPoint[1] + lineX * lineXY + lineY * lineYY] in pointsDone):
|
||||
patches += [[ \
|
||||
originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, \
|
||||
originPoint[1] + lineX * lineXY + lineY * lineYY, \
|
||||
*brushColours, 0, " "]]
|
||||
pointsDone += [[originPoint[0] + lineX * lineXX + lineY * lineYX + brushStep, originPoint[1] + lineX * lineXY + lineY * lineYY]]
|
||||
if lineD > 0:
|
||||
lineD -= pointDelta[0]; lineY += 1;
|
||||
lineD += pointDelta[1]
|
||||
return dirty
|
||||
# }}}
|
||||
# {{{ _pointDelta(self, a, b)
|
||||
return patches
|
||||
|
||||
def _pointDelta(self, a, b):
|
||||
return [a2 - a1 for a1, a2 in zip(a, b)]
|
||||
# }}}
|
||||
# {{{ _pointSwap(self, a, b)
|
||||
|
||||
def _pointSwap(self, a, b):
|
||||
return [b, a]
|
||||
# }}}
|
||||
|
||||
#
|
||||
# onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
brushColours, dirty = brushColours.copy(), False
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
brushColours, isCursor, patches, rc = brushColours.copy(), not (mouseLeftDown or mouseRightDown), [], False
|
||||
if mouseLeftDown:
|
||||
brushColours[1] = brushColours[0]
|
||||
elif mouseRightDown:
|
||||
@ -62,20 +52,24 @@ class ToolLine(Tool):
|
||||
brushColours[1] = brushColours[0]
|
||||
if self.toolState == self.TS_NONE:
|
||||
if mouseLeftDown or mouseRightDown:
|
||||
self.toolColours, self.toolOriginPoint, self.toolState = brushColours, list(mapPoint), self.TS_ORIGIN
|
||||
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, " "], viewRect)
|
||||
self.toolOriginPoint, self.toolState = list(mapPoint), self.TS_ORIGIN
|
||||
isCursor, patches, rc = True, [], True
|
||||
for brushCol in range(brushSize[0]):
|
||||
if ((mapPoint[0] + brushCol) < canvas.size[0]) \
|
||||
and (mapPoint[1] < canvas.size[1]):
|
||||
patches += [[mapPoint[0] + brushCol, mapPoint[1], *brushColours, 0, " "]]
|
||||
elif self.toolState == self.TS_ORIGIN:
|
||||
originPoint, targetPoint = self.toolOriginPoint, list(mapPoint)
|
||||
dirty = self._getLine(self.toolColours, brushSize, dispatchFn, eventDc, mouseLeftDown or mouseRightDown, originPoint, targetPoint, viewRect)
|
||||
if mouseLeftDown or mouseRightDown:
|
||||
self.toolColours, self.toolOriginPoint, self.toolState = None, None, self.TS_NONE
|
||||
else:
|
||||
return False, dirty
|
||||
return True, dirty
|
||||
patches = self._getLine(brushColours, brushSize, False, originPoint, targetPoint)
|
||||
self.toolOriginPoint, self.toolState = None, self.TS_NONE
|
||||
else:
|
||||
patches = self._getLine(brushColours, brushSize, True, originPoint, targetPoint)
|
||||
rc = True
|
||||
return rc, patches if not isCursor else None, patches if isCursor else None
|
||||
|
||||
# __init__(self, *args): initialisation method
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
self.toolColours, self.toolOriginPoint, self.toolState = None, None, self.TS_NONE
|
||||
self.toolOriginPoint, self.toolState = None, self.TS_NONE
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -14,21 +14,21 @@ class ToolObject(Tool):
|
||||
TS_SELECT = 2
|
||||
TS_TARGET = 3
|
||||
|
||||
# {{{ _dispatchSelectEvent(self, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseLeftDown, selectRect, viewRect)
|
||||
def _dispatchSelectEvent(self, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseLeftDown, selectRect, viewRect):
|
||||
def _dispatchSelectEvent(self, canvas, keyModifiers, mapPoint, mouseLeftDown, selectRect):
|
||||
if mouseLeftDown:
|
||||
disp, isCursor = [mapPoint[m] - self.lastAtPoint[m] for m in [0, 1]], True
|
||||
newTargetRect = [[selectRect[n][m] + disp[m] for m in [0, 1]] for n in [0, 1]]
|
||||
self.lastAtPoint = list(mapPoint)
|
||||
else:
|
||||
disp, isCursor, newTargetRect = [0, 0], True, selectRect.copy()
|
||||
dirty = self.onSelectEvent(canvas, disp, dispatchFn, eventDc, isCursor, keyModifiers, newTargetRect, selectRect, viewRect)
|
||||
self._drawSelectRect(newTargetRect, dispatchFn, eventDc, viewRect)
|
||||
rc, patches, patchesCursor = self.onSelectEvent(canvas, disp, isCursor, keyModifiers, newTargetRect, selectRect)
|
||||
patchesCursor = [] if patchesCursor == None else patchesCursor
|
||||
patchesCursor += self._drawSelectRect(newTargetRect)
|
||||
self.targetRect = newTargetRect
|
||||
return dirty
|
||||
# }}}
|
||||
# {{{ _drawSelectRect(self, rect, dispatchFn, eventDc, viewRect)
|
||||
def _drawSelectRect(self, rect, dispatchFn, eventDc, viewRect):
|
||||
return rc, patches, patchesCursor
|
||||
|
||||
def _drawSelectRect(self, rect):
|
||||
patches = []
|
||||
rectFrame = [[rect[m][n] for n in [0, 1]] for m in (0, 1)]
|
||||
if rectFrame[0][0] > rectFrame[1][0]:
|
||||
rectFrame[0][0], rectFrame[1][0] = rectFrame[1][0], rectFrame[0][0]
|
||||
@ -37,29 +37,21 @@ class ToolObject(Tool):
|
||||
curColours, rectFrame = [0, 0], [[rectFrame[m[0]][n] + m[1] for n in [0, 1]] for m in [[0, -1], [1, +1]]]
|
||||
for rectX in range(rectFrame[0][0], rectFrame[1][0] + 1):
|
||||
curColours = [1, 1] if curColours == [0, 0] else [0, 0]
|
||||
dispatchFn(eventDc, True, [rectX, rectFrame[0][1], *curColours, 0, " "], viewRect)
|
||||
dispatchFn(eventDc, True, [rectX, rectFrame[1][1], *curColours, 0, " "], viewRect)
|
||||
patches += [[rectX, rectFrame[0][1], *curColours, 0, " "], [rectX, rectFrame[1][1], *curColours, 0, " "]]
|
||||
for rectY in range(rectFrame[0][1], rectFrame[1][1] + 1):
|
||||
curColours = [1, 1] if curColours == [0, 0] else [0, 0]
|
||||
dispatchFn(eventDc, True, [rectFrame[0][0], rectY, *curColours, 0, " "], viewRect)
|
||||
dispatchFn(eventDc, True, [rectFrame[1][0], rectY, *curColours, 0, " "], viewRect)
|
||||
# }}}
|
||||
# {{{ _mouseEventTsNone(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect)
|
||||
def _mouseEventTsNone(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect):
|
||||
self.substract = False
|
||||
if self.external:
|
||||
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, " "], viewRect)
|
||||
else:
|
||||
if mouseLeftDown:
|
||||
self.targetRect, self.toolState = [list(mapPoint), []], self.TS_ORIGIN
|
||||
else:
|
||||
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, " "], viewRect)
|
||||
return False
|
||||
# }}}
|
||||
# {{{ _mouseEventTsOrigin(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect)
|
||||
def _mouseEventTsOrigin(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect):
|
||||
patches += [[rectFrame[0][0], rectY, *curColours, 0, " "], [rectFrame[1][0], rectY, *curColours, 0, " "]]
|
||||
return patches
|
||||
|
||||
def _mouseEventTsNone(self, brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
|
||||
patchesCursor = [[*mapPoint, brushColours[0], brushColours[0], 0, " "]]; self.substract = False;
|
||||
if (not self.external) and mouseLeftDown:
|
||||
self.targetRect, self.toolState = [list(mapPoint), []], self.TS_ORIGIN
|
||||
return True, None, patchesCursor
|
||||
|
||||
def _mouseEventTsOrigin(self, brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
|
||||
self.targetRect[1] = list(mapPoint)
|
||||
if mouseLeftDown:
|
||||
if not mouseLeftDown:
|
||||
if self.targetRect[0][0] > self.targetRect[1][0]:
|
||||
self.targetRect[0][0], self.targetRect[1][0] = self.targetRect[1][0], self.targetRect[0][0]
|
||||
if self.targetRect[0][1] > self.targetRect[1][1]:
|
||||
@ -70,14 +62,10 @@ class ToolObject(Tool):
|
||||
for numCol in range((self.targetRect[1][0] - self.targetRect[0][0]) + 1):
|
||||
rectX, rectY = self.targetRect[0][0] + numCol, self.targetRect[0][1] + numRow
|
||||
self.objectMap[numRow].append(canvas.map[rectY][rectX])
|
||||
self._drawSelectRect(self.targetRect, dispatchFn, eventDc, viewRect)
|
||||
else:
|
||||
self._drawSelectRect(self.targetRect, dispatchFn, eventDc, viewRect)
|
||||
return False
|
||||
# }}}
|
||||
# {{{ _mouseEventTsSelect(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect)
|
||||
def _mouseEventTsSelect(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect):
|
||||
dirty = False
|
||||
return True, None, self._drawSelectRect(self.targetRect)
|
||||
|
||||
def _mouseEventTsSelect(self, brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
|
||||
rc, patches, patchesCursor = False, None, None
|
||||
if mouseLeftDown:
|
||||
if (mapPoint[0] >= (self.targetRect[0][0] - 1)) \
|
||||
and (mapPoint[0] <= (self.targetRect[1][0] + 1)) \
|
||||
@ -85,74 +73,79 @@ class ToolObject(Tool):
|
||||
and (mapPoint[1] <= (self.targetRect[1][1] + 1)):
|
||||
self.lastAtPoint, self.toolState = list(mapPoint), self.TS_TARGET
|
||||
else:
|
||||
dirty = self.onSelectEvent(canvas, (0, 0), dispatchFn, eventDc, False, keyModifiers, self.targetRect.copy(), self.targetRect, viewRect)
|
||||
self._drawSelectRect(self.targetRect, dispatchFn, eventDc, viewRect)
|
||||
rc, patches, patchesCursor = self.onSelectEvent(canvas, (0, 0), False, keyModifiers, self.targetRect.copy(), self.targetRect)
|
||||
patchesCursor = [] if patchesCursor == None else patchesCursor
|
||||
patchesCursor += self._drawSelectRect(self.targetRect)
|
||||
self.objectMap, self.objectSize, self.targetRect, self.toolState = None, None, None, self.TS_NONE
|
||||
else:
|
||||
dirty = self._dispatchSelectEvent(canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseLeftDown, self.targetRect, viewRect)
|
||||
return dirty
|
||||
# }}}
|
||||
# {{{ _mouseEventTsTarget(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect)
|
||||
def _mouseEventTsTarget(self, brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect):
|
||||
rc, patches, patchesCursor = self._dispatchSelectEvent(canvas, keyModifiers, mapPoint, mouseLeftDown, self.targetRect)
|
||||
return rc, patches, patchesCursor
|
||||
|
||||
def _mouseEventTsTarget(self, brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown):
|
||||
rc, patches, patchesCursor = False, None, None
|
||||
if (keyModifiers == wx.MOD_CONTROL) and (self.srcRect == self.targetRect):
|
||||
self.substract = True
|
||||
dirty = False
|
||||
if mouseLeftDown:
|
||||
dirty = self._dispatchSelectEvent(canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseLeftDown, self.targetRect, viewRect)
|
||||
rc, patches, patchesCursor = self._dispatchSelectEvent(canvas, keyModifiers, mapPoint, mouseLeftDown, self.targetRect)
|
||||
else:
|
||||
self.toolState = self.TS_SELECT
|
||||
return True, dirty
|
||||
# }}}
|
||||
return rc, patches, patchesCursor
|
||||
|
||||
# {{{ getRegion(self, canvas)
|
||||
def getRegion(self, canvas):
|
||||
return self.objectMap
|
||||
# }}}
|
||||
# {{{ onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
dirty = False
|
||||
if self.toolState == self.TS_NONE:
|
||||
dirty = self._mouseEventTsNone(brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect)
|
||||
elif self.toolState == self.TS_SELECT:
|
||||
dirty = self._mouseEventTsSelect(brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect)
|
||||
elif self.toolState == self.TS_ORIGIN:
|
||||
dirty = self._mouseEventTsOrigin(brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect)
|
||||
elif self.toolState == self.TS_TARGET:
|
||||
dirty = self._mouseEventTsTarget(brushColours, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, viewRect)
|
||||
|
||||
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyChar, keyCode, keyModifiers, mapPoint):
|
||||
if (ord(keyChar) == wx.WXK_ESCAPE) and (self.toolState >= self.TS_SELECT):
|
||||
rc, patches, patchesCursor = self.onSelectEvent(canvas, (0, 0), False, keyModifiers, self.targetRect.copy(), self.targetRect)
|
||||
patchesCursor = [] if patchesCursor == None else patchesCursor
|
||||
patchesCursor += self._drawSelectRect(self.targetRect)
|
||||
self.objectMap, self.objectSize, self.targetRect, self.toolState = None, None, None, self.TS_NONE
|
||||
else:
|
||||
return False, dirty
|
||||
return True, dirty
|
||||
# }}}
|
||||
# {{{ onSelectEvent(self, canvas, disp, dispatchFn, eventDc, isCursor, keyModifiers, newTargetRect, selectRect, viewRect)
|
||||
def onSelectEvent(self, canvas, disp, dispatchFn, eventDc, isCursor, keyModifiers, newTargetRect, selectRect, viewRect):
|
||||
dirty = False
|
||||
rc, patches, patchesCursor = False, None, None
|
||||
return rc, patches, patchesCursor
|
||||
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
if self.toolState == self.TS_NONE:
|
||||
rc, patches, patchesCursor = self._mouseEventTsNone(brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
|
||||
elif self.toolState == self.TS_SELECT:
|
||||
rc, patches, patchesCursor = self._mouseEventTsSelect(brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
|
||||
elif self.toolState == self.TS_ORIGIN:
|
||||
rc, patches, patchesCursor = self._mouseEventTsOrigin(brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
|
||||
elif self.toolState == self.TS_TARGET:
|
||||
rc, patches, patchesCursor = self._mouseEventTsTarget(brushColours, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown)
|
||||
else:
|
||||
rc, patches, patchesCursor = False, None, None
|
||||
return rc, patches, patchesCursor
|
||||
|
||||
def onSelectEvent(self, canvas, disp, isCursor, keyModifiers, newTargetRect, selectRect):
|
||||
patches = []
|
||||
if self.external:
|
||||
for numRow in range(len(self.objectMap)):
|
||||
for numCol in range(len(self.objectMap[numRow])):
|
||||
rectX, rectY = selectRect[0][0] + numCol, selectRect[0][1] + numRow
|
||||
dirty = False if isCursor else True
|
||||
cellNew = self.objectMap[numRow][numCol]
|
||||
if (cellNew[1] == -1) and (cellNew[3] == " "):
|
||||
if ((rectY + disp[1]) < canvas.size[1]) and ((rectX + disp[0]) < canvas.size[0]):
|
||||
cellNew = canvas.map[rectY + disp[1]][rectX + disp[0]]
|
||||
dispatchFn(eventDc, isCursor, [rectX + disp[0], rectY + disp[1], *cellNew], viewRect)
|
||||
patches += [[rectX + disp[0], rectY + disp[1], *cellNew]]
|
||||
else:
|
||||
if self.substract:
|
||||
for numRow in range(self.srcRect[0][1], self.srcRect[1][1]):
|
||||
for numCol in range(self.srcRect[0][0], self.srcRect[1][0]):
|
||||
if ((numCol < selectRect[0][0]) or (numCol > selectRect[1][0])) \
|
||||
or ((numRow < selectRect[0][1]) or (numRow > selectRect[1][1])):
|
||||
dirty = False if isCursor else True
|
||||
dispatchFn(eventDc, isCursor, [numCol, numRow, 1, 1, 0, " "], viewRect)
|
||||
patches += [[numCol, numRow, 1, 1, 0, " "]]
|
||||
for numRow in range(len(self.objectMap)):
|
||||
for numCol in range(len(self.objectMap[numRow])):
|
||||
cellOld = self.objectMap[numRow][numCol]
|
||||
rectX, rectY = selectRect[0][0] + numCol, selectRect[0][1] + numRow
|
||||
dirty = False if isCursor else True
|
||||
dispatchFn(eventDc, isCursor, [rectX + disp[0], rectY + disp[1], *cellOld], viewRect)
|
||||
return dirty
|
||||
# }}}
|
||||
# {{{ setRegion(self, canvas, mapPoint, objectMap, objectSize, external=True)
|
||||
cellNew = self.objectMap[numRow][numCol]
|
||||
if (cellNew[1] == -1) and (cellNew[3] == " "):
|
||||
if ((rectY + disp[1]) < canvas.size[1]) and ((rectX + disp[0]) < canvas.size[0]):
|
||||
cellNew = canvas.map[rectY + disp[1]][rectX + disp[0]]
|
||||
patches += [[rectX + disp[0], rectY + disp[1], *cellNew]]
|
||||
return True, patches if not isCursor else None, patches if isCursor else None
|
||||
|
||||
def setRegion(self, canvas, mapPoint, objectMap, objectSize, external=True):
|
||||
self.external, self.toolState = external, self.TS_SELECT
|
||||
if mapPoint != None:
|
||||
@ -162,13 +155,11 @@ class ToolObject(Tool):
|
||||
elif self.objectSize != objectSize:
|
||||
if self.objectSize == None:
|
||||
self.objectSize = objectSize
|
||||
self.targetRect[1] = [t + d for t, d in zip(self.targetRect[1], (a - b for a, b in zip(self.objectSize, objectSize)))]
|
||||
self.targetRect[1] = [t + d for t, d in zip(self.targetRect[1], (b - a for a, b in zip(self.objectSize, objectSize)))]
|
||||
if self.srcRect == None:
|
||||
self.srcRect = self.targetRect
|
||||
self.objectMap, self.objectSize = objectMap, objectSize
|
||||
# }}}
|
||||
|
||||
# __init__(self, *args): initialisation method
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
self.external, self.lastAtPoint, self.srcRect, self.substract, \
|
||||
|
23
libtools/ToolPickColour.py
Normal file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# ToolPickColour.py
|
||||
# Copyright (c) 2018, 2019 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||
#
|
||||
|
||||
from Tool import Tool
|
||||
|
||||
class ToolPickColour(Tool):
|
||||
name = "Pick colour"
|
||||
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
if (mapPoint[0] < canvas.size[0]) and (mapPoint[1] < canvas.size[1]):
|
||||
if mouseLeftDown:
|
||||
if canvas.map[mapPoint[1]][mapPoint[0]][3] == " ":
|
||||
brushColours[0] = canvas.map[mapPoint[1]][mapPoint[0]][1]
|
||||
else:
|
||||
brushColours[0] = canvas.map[mapPoint[1]][mapPoint[0]][0]
|
||||
elif mouseRightDown:
|
||||
brushColours[1] = canvas.map[mapPoint[1]][mapPoint[0]][1]
|
||||
return True, None, [[*mapPoint, *brushColours, 0, "░"]]
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
@ -5,32 +5,43 @@
|
||||
#
|
||||
|
||||
from Tool import Tool
|
||||
import wx
|
||||
|
||||
class ToolRect(Tool):
|
||||
name = "Rectangle"
|
||||
TS_NONE = 0
|
||||
TS_ORIGIN = 1
|
||||
|
||||
#
|
||||
# onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
brushColours, dirty = brushColours.copy(), False
|
||||
if mouseLeftDown:
|
||||
brushColours[1] = brushColours[0]
|
||||
elif mouseRightDown:
|
||||
brushColours[0] = brushColours[1]
|
||||
else:
|
||||
brushColours[1] = brushColours[0]
|
||||
brushSize = brushSize.copy()
|
||||
if brushSize[0] > 1:
|
||||
brushSize[0] *= 2
|
||||
for brushRow in range(brushSize[1]):
|
||||
for brushCol in range(brushSize[0]):
|
||||
patch = [mapPoint[0] + brushCol, mapPoint[1] + brushRow, *brushColours, 0, " "]
|
||||
if mouseLeftDown or mouseRightDown:
|
||||
if not dirty:
|
||||
dirty = True
|
||||
dispatchFn(eventDc, False, patch, viewRect); dispatchFn(eventDc, True, patch, viewRect);
|
||||
def _drawRect(self, brushColours, canvas, rect):
|
||||
patches = []
|
||||
for brushRow in range((rect[3] - rect[1]) + 1):
|
||||
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
|
||||
else:
|
||||
dispatchFn(eventDc, True, patch, viewRect)
|
||||
return True, dirty
|
||||
patchColours = [brushColours[1]] * 2
|
||||
patches += [[rect[0] + brushCol, rect[1] + brushRow, *patchColours, 0, " "]]
|
||||
return patches
|
||||
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
brushColours = [brushColours[1], brushColours[0]] if mouseRightDown else brushColours
|
||||
brushSize, patches = list(brushSize), []; brushSize[0] *= 2 if brushSize[0] > 1 else brushSize[0];
|
||||
if self.toolState == self.TS_NONE:
|
||||
if (keyModifiers == wx.MOD_CONTROL) and (mouseLeftDown or mouseRightDown):
|
||||
self.brushColours, isCursor, self.originPoint, self.toolState = list(brushColours), True, list(mapPoint), self.TS_ORIGIN
|
||||
else:
|
||||
isCursor = not (mouseLeftDown or mouseRightDown)
|
||||
rect = [*mapPoint, *([a + b for a, b in zip(brushSize, mapPoint)] if brushSize[0] > 1 else mapPoint)]
|
||||
elif self.toolState == self.TS_ORIGIN:
|
||||
rect = [*self.originPoint, *mapPoint]
|
||||
if not (mouseLeftDown or mouseRightDown):
|
||||
brushColours, isCursor, self.brushColours, self.originPoint, self.toolState = self.brushColours, False, None, None, self.TS_NONE
|
||||
else:
|
||||
isCursor = True
|
||||
patches = self._drawRect(brushColours, canvas, rect)
|
||||
return True, patches if not isCursor else None, patches if isCursor else None
|
||||
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args); self.brushColours, self.originPoint, self.toolState = None, None, self.TS_NONE;
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -5,43 +5,127 @@
|
||||
#
|
||||
|
||||
from Tool import Tool
|
||||
import string, wx
|
||||
import re, string, wx
|
||||
|
||||
class ToolText(Tool):
|
||||
name = "Text"
|
||||
arabicCombiningRegEx = r'^[\u064B-\u065F\uFE70-\uFE72\uFE74\uFE76-\uFE7F]+$'
|
||||
arabicRegEx = r'^[\u0621-\u063A\u0640-\u064A]+$'
|
||||
rtlRegEx = r'^[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]+$'
|
||||
|
||||
#
|
||||
# onKeyboardEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyChar, keyModifiers, mapPoint, viewRect)
|
||||
def onKeyboardEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyChar, keyModifiers, mapPoint, viewRect):
|
||||
if (ord(keyChar) != wx.WXK_NONE) \
|
||||
and (not keyChar in set("\t\n\v\f\r")) \
|
||||
and ((ord(keyChar) >= 32) if ord(keyChar) < 127 else True) \
|
||||
and (keyModifiers in (wx.MOD_NONE, wx.MOD_SHIFT)):
|
||||
rc, dirty = True, True
|
||||
if self.textPos == None:
|
||||
self.textPos = list(mapPoint)
|
||||
dispatchFn(eventDc, False, [*self.textPos, *brushColours, 0, keyChar], viewRect)
|
||||
if self.textPos[0] < (canvas.size[0] - 1):
|
||||
self.textPos[0] += 1
|
||||
elif self.textPos[1] < (canvas.size[1] - 1):
|
||||
self.textPos[0] = 0; self.textPos[1] += 1;
|
||||
else:
|
||||
self.textPos = [0, 0]
|
||||
def _checkRtl(self, canvas, brushPos, keyChar):
|
||||
rtlFlag = False
|
||||
if (keyChar != None) and re.match(self.rtlRegEx, keyChar):
|
||||
rtlFlag = True
|
||||
else:
|
||||
rc, dirty = False, False
|
||||
return rc, dirty
|
||||
lastX, lastY = brushPos[0], brushPos[1]
|
||||
while True:
|
||||
if canvas.map[lastY][lastX][3] == " ":
|
||||
if (lastX + 1) >= canvas.size[0]:
|
||||
if lastY == 0:
|
||||
break
|
||||
else:
|
||||
lastX, lastY = 0, lastY - 1
|
||||
else:
|
||||
lastX += 1
|
||||
elif re.match(self.arabicRegEx, canvas.map[lastY][lastX][3]):
|
||||
rtlFlag = True
|
||||
if (lastX + 1) >= canvas.size[0]:
|
||||
if lastY == 0:
|
||||
break
|
||||
else:
|
||||
lastX, lastY = 0, lastY - 1
|
||||
else:
|
||||
lastX += 1
|
||||
else:
|
||||
break
|
||||
return rtlFlag
|
||||
|
||||
#
|
||||
# onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect)
|
||||
def onMouseEvent(self, brushColours, brushSize, canvas, dispatchFn, eventDc, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown, viewRect):
|
||||
def _processKeyChar(self, brushColours, brushPos, canvas, keyChar, keyModifiers):
|
||||
patches, rc = [], False
|
||||
if (ord(keyChar) != wx.WXK_NONE) \
|
||||
and (not keyChar in set("\t\n\v\f\r")) \
|
||||
and ((ord(keyChar) >= 32) if ord(keyChar) < 127 else True):
|
||||
patches += [[*brushPos, *brushColours, 0, keyChar]]
|
||||
if not self._checkRtl(canvas, brushPos, keyChar):
|
||||
if brushPos[0] < (canvas.size[0] - 1):
|
||||
brushPos[0] += 1
|
||||
elif brushPos[1] < (canvas.size[1] - 1):
|
||||
brushPos[0], brushPos[1] = 0, brushPos[1] + 1
|
||||
else:
|
||||
brushPos[0], brushPos[1] = 0, 0
|
||||
else:
|
||||
if brushPos[0] > 0:
|
||||
brushPos[0] -= 1
|
||||
elif brushPos[1] > 0:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, brushPos[1] - 1
|
||||
else:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, canvas.size[1] - 1
|
||||
rc = True
|
||||
return rc, patches
|
||||
|
||||
def onKeyboardEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyChar, keyCode, keyModifiers, mapPoint):
|
||||
patches, patchesCursor, rc = [], [], False
|
||||
if re.match(self.arabicCombiningRegEx, keyChar):
|
||||
rc = True
|
||||
elif keyCode == wx.WXK_CONTROL_V:
|
||||
rc = True
|
||||
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 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
|
||||
if rc:
|
||||
patchesCursor += [[*brushPos, *brushColours, 0, "_"]]
|
||||
wx.TheClipboard.Close()
|
||||
else:
|
||||
rc, error = False, "Clipboard does not contain text data and/or cannot be opened"
|
||||
elif keyCode == wx.WXK_BACK:
|
||||
if ((brushPos[0] + 1) >= canvas.size[0]):
|
||||
lastBrushPos = [0, brushPos[1] - 1] if brushPos[1] > 0 else [0, 0]
|
||||
else:
|
||||
lastBrushPos = [brushPos[0] + 1, brushPos[1]]
|
||||
if not self._checkRtl(canvas, lastBrushPos, None):
|
||||
patches += [[*brushPos, *brushColours, 0, " "]]
|
||||
if brushPos[0] > 0:
|
||||
brushPos[0] -= 1
|
||||
elif brushPos[1] > 0:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, brushPos[1] - 1
|
||||
else:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, canvas.size[1] - 1
|
||||
else:
|
||||
if brushPos[0] < (canvas.size[0] - 1):
|
||||
brushPos[0] += 1
|
||||
elif brushPos[1] > 0:
|
||||
brushPos[0], brushPos[1] = 0, brushPos[1] - 1
|
||||
else:
|
||||
brushPos[0], brushPos[1] = canvas.size[0] - 1, 0
|
||||
rc = True; patchesCursor += [[*brushPos, *brushColours, 0, "_"]];
|
||||
elif keyCode == wx.WXK_RETURN:
|
||||
if brushPos[1] < (canvas.size[1] - 1):
|
||||
brushPos[0], brushPos[1] = 0, brushPos[1] + 1
|
||||
else:
|
||||
brushPos[0], brushPos[1] = 0, 0
|
||||
rc = True; patchesCursor += [[*brushPos, *brushColours, 0, "_"]];
|
||||
elif not (keyModifiers in (wx.MOD_ALT, wx.MOD_ALTGR, wx.MOD_CONTROL)):
|
||||
rc, patches_ = self._processKeyChar(brushColours, brushPos, canvas, keyChar, keyModifiers)
|
||||
patches += patches_
|
||||
if rc:
|
||||
patchesCursor += [[*brushPos, *brushColours, 0, "_"]]
|
||||
return rc, patches, patchesCursor
|
||||
|
||||
def onMouseEvent(self, atPoint, brushColours, brushPos, brushSize, canvas, keyModifiers, mapPoint, mouseDragging, mouseLeftDown, mouseRightDown):
|
||||
if mouseLeftDown or mouseRightDown:
|
||||
self.textPos = list(mapPoint)
|
||||
dispatchFn(eventDc, True, [*mapPoint, *brushColours, 0, "_"], viewRect)
|
||||
return True, False
|
||||
|
||||
# __init__(self, *args): initialisation method
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
self.textColours = self.textPos = None
|
||||
brushPos[0], brushPos[1] = atPoint[0], atPoint[1]
|
||||
return True, None, [[*brushPos, *brushColours, 0, "_"]]
|
||||
|
||||
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
||||
|
@ -1 +0,0 @@
|
||||
.
|
1
requirements.txt
Symbolic link
@ -0,0 +1 @@
|
||||
assets/text/requirements.txt
|
13
roar.py
@ -17,21 +17,26 @@ import wx
|
||||
def main(*argv):
|
||||
localConfDirName = getLocalConfPathName()
|
||||
if not os.path.exists(localConfDirName):
|
||||
os.makedirs(localConfirName)
|
||||
os.makedirs(localConfDirName)
|
||||
wxApp, roarClient = wx.App(False), RoarClient(None)
|
||||
argv0, argv = argv[0], argv[1:]
|
||||
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])
|
||||
roarClient.canvasPanel.commands.canvasPathName = argv[0]
|
||||
roarClient.canvasPanel._snapshotsReset()
|
||||
rc, error = roarClient.canvasPanel.canvas.importStore.importTextFile(argv[0])
|
||||
if rc:
|
||||
roarClient.canvasPanel.update(roarClient.canvasPanel.canvas.importStore.inSize, False, roarClient.canvasPanel.canvas.importStore.outMap)
|
||||
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)
|
||||
else:
|
||||
roarClient.canvasPanel.commands.canvasNew(None)
|
||||
roarClient.canvasPanel.commands.canvasTool(roarClient.canvasPanel.commands.canvasTool, 1)(None)
|
||||
wxApp.MainLoop()
|
||||
if __name__ == "__main__":
|
||||
main(*sys.argv)
|
||||
|