Merge remote-tracking branch 'MiRCARTools/master'
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
MiRCARTImgurApiKey.py
|
||||||
|
__pycache__/
|
||||||
|
*.sw[op]
|
BIN
DejaVuSansMono.ttf
Normal file
128
IrcClient.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# mirc2png -- convert ASCII w/ mIRC control codes to monospaced PNG (for EFnet #MiRCART)
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from itertools import chain
|
||||||
|
import select, socket, time
|
||||||
|
|
||||||
|
class IrcClient:
|
||||||
|
"""Non-blocking abstraction over the IRC protocol"""
|
||||||
|
serverHname = serverPort = None;
|
||||||
|
clientNick = clientIdent = clientGecos = None;
|
||||||
|
clientSocket = clientSocketFile = None;
|
||||||
|
clientNextTimeout = None; clientQueue = None;
|
||||||
|
|
||||||
|
# {{{ close(self): Close connection to server
|
||||||
|
def close(self):
|
||||||
|
if self.clientSocket != None:
|
||||||
|
self.clientSocket.close()
|
||||||
|
self.clientSocket = self.clientSocketFile = None;
|
||||||
|
# }}}
|
||||||
|
# {{{ connect(self, preferFamily=socket.AF_INET, timeout=None): Connect to server and register w/ optional timeout
|
||||||
|
def connect(self, preferFamily=socket.AF_INET, timeout=None):
|
||||||
|
gaiInfo = socket.getaddrinfo(self.serverHname, self.serverPort,
|
||||||
|
preferFamily, socket.SOCK_STREAM, socket.IPPROTO_TCP)
|
||||||
|
self.clientSocket = socket.socket(*gaiInfo[0][:3])
|
||||||
|
self.clientSocket.setblocking(0)
|
||||||
|
try:
|
||||||
|
self.clientSocket.connect(gaiInfo[0][4])
|
||||||
|
except BlockingIOError:
|
||||||
|
pass
|
||||||
|
if timeout:
|
||||||
|
readySet = select.select([], [self.clientSocket.fileno()], [], timeout)
|
||||||
|
if len(readySet[1]) == 0:
|
||||||
|
self.close(); return False;
|
||||||
|
else:
|
||||||
|
select.select([], [self.clientSocket.fileno()], [])
|
||||||
|
self.clientSocketFile = self.clientSocket.makefile(encoding="utf-8", errors="replace")
|
||||||
|
self.clientQueue = []
|
||||||
|
self.queue("NICK", self.clientNick)
|
||||||
|
self.queue("USER", self.clientIdent, "0", "0", self.clientGecos)
|
||||||
|
return True
|
||||||
|
# }}}
|
||||||
|
# {{{ readline(self): Read and parse single line from server into canonicalised list, honouring timers
|
||||||
|
def readline(self):
|
||||||
|
if self.clientNextTimeout:
|
||||||
|
timeNow = time.time()
|
||||||
|
if self.clientNextTimeout <= timeNow:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
readySet = select.select([self.clientSocket.fileno()], [], [], self.clientNextTimeout - timeNow)
|
||||||
|
else:
|
||||||
|
readySet = select.select([self.clientSocket.fileno()], [], [])
|
||||||
|
msg = self.clientSocketFile.readline()
|
||||||
|
if len(msg):
|
||||||
|
msg = msg.rstrip("\r\n")
|
||||||
|
else:
|
||||||
|
if len(readySet[0]) == 0:
|
||||||
|
return ""
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
msg = msg.split(" :", 1)
|
||||||
|
if len(msg) == 1:
|
||||||
|
msg = list(chain.from_iterable(m.split(" ") for m in msg))
|
||||||
|
elif len(msg) == 2:
|
||||||
|
msg = msg[0].split(" ") + [msg[1]]
|
||||||
|
if msg[0][0] == ':':
|
||||||
|
msg = [msg[0][1:]] + msg[1:]
|
||||||
|
else:
|
||||||
|
msg = [""] + msg[0:]
|
||||||
|
return msg
|
||||||
|
# }}}
|
||||||
|
# {{{ 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):
|
||||||
|
if argNum == (argNumMax - 1):
|
||||||
|
msg += ":" + args[argNum]
|
||||||
|
else:
|
||||||
|
msg += args[argNum] + " "
|
||||||
|
self.clientQueue.append((msg + "\r\n").encode())
|
||||||
|
# }}}
|
||||||
|
# {{{ unqueue(self): Send all queued lines to server, honouring timers
|
||||||
|
def unqueue(self):
|
||||||
|
while self.clientQueue:
|
||||||
|
msg = self.clientQueue[0]; msgLen = len(msg); msgBytesSent = 0;
|
||||||
|
while msgBytesSent < msgLen:
|
||||||
|
if self.clientNextTimeout:
|
||||||
|
timeNow = time.time()
|
||||||
|
if self.clientNextTimeout <= timeNow:
|
||||||
|
self.clientQueue[0] = msg; return;
|
||||||
|
else:
|
||||||
|
readySet = select.select([], [self.clientSocket.fileno()], [], self.clientNextTimeout - timeNow)
|
||||||
|
if len(readySet[1]) == 0:
|
||||||
|
self.clientQueue[0] = msg; return;
|
||||||
|
else:
|
||||||
|
readySet = select.select([], [self.clientSocket.fileno()], [])
|
||||||
|
msgBytesSent = self.clientSocket.send(msg)
|
||||||
|
msg = msg[msgBytesSent:]; msgLen -= msgBytesSent;
|
||||||
|
del self.clientQueue[0]
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, serverHname, serverPort, clientNick, clientIdent, clientGecos): initialisation method
|
||||||
|
def __init__(self, serverHname, serverPort, clientNick, clientIdent, clientGecos):
|
||||||
|
self.serverHname = serverHname; self.serverPort = serverPort;
|
||||||
|
self.clientNick = clientNick; self.clientIdent = clientIdent; self.clientGecos = clientGecos;
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
287
IrcMiRCARTBot.py
Executable file
@ -0,0 +1,287 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# IrcMiRCARTBot.py -- IRC<->MiRC2png bot (for EFnet #MiRCART)
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from getopt import getopt, GetoptError
|
||||||
|
import base64
|
||||||
|
import os, socket, sys, time
|
||||||
|
import json
|
||||||
|
import IrcClient
|
||||||
|
import requests, urllib.request
|
||||||
|
from MiRCARTCanvasImportStore import MiRCARTCanvasImportStore
|
||||||
|
from MiRCARTImgurApiKey import MiRCARTImgurApiKey
|
||||||
|
from MiRCARTToPngFile import MiRCARTToPngFile
|
||||||
|
|
||||||
|
class IrcMiRCARTBot(IrcClient.IrcClient):
|
||||||
|
"""IRC<->MiRC2png bot"""
|
||||||
|
clientChannelLastMessage = clientChannelOps = clientChannel = None
|
||||||
|
clientChannelRejoin = None
|
||||||
|
imgurApiKey = MiRCARTImgurApiKey.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(" "):
|
||||||
|
if len(channelNickSpec) \
|
||||||
|
and channelNickSpec[0] == "@" \
|
||||||
|
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;
|
||||||
|
channelAuthAdd = ""; channelAuthDel = "";
|
||||||
|
for channelModeChar in message[3]:
|
||||||
|
if channelModeChar[0] == "-":
|
||||||
|
channelModeType = "-"
|
||||||
|
elif channelModeChar[0] == "+":
|
||||||
|
channelModeType = "+"
|
||||||
|
elif channelModeChar[0].isalpha():
|
||||||
|
if channelModeChar[0] == "o":
|
||||||
|
if channelModeType == "+":
|
||||||
|
channelAuthAdd = message[channelModeArg]; channelAuthDel = "";
|
||||||
|
elif channelModeType == "-":
|
||||||
|
channelAuthAdd = ""; channelAuthDel = message[channelModeArg];
|
||||||
|
channelModeArg += 1
|
||||||
|
if len(channelAuthAdd) \
|
||||||
|
and channelAuthAdd not in self.clientChannelOps:
|
||||||
|
channelAuthAdd = channelAuthAdd.lower()
|
||||||
|
self._log("Authorising {} on {}".format(channelAuthAdd, message[2].lower()))
|
||||||
|
self.clientChannelOps.append(channelAuthAdd)
|
||||||
|
elif len(channelAuthDel) \
|
||||||
|
and channelAuthDel in self.clientChannelOps:
|
||||||
|
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 "):
|
||||||
|
if (int(time.time()) - self.clientLastMessage) < 5:
|
||||||
|
self._log("Ignoring request on {} from {} due to rate limit: {}".format(message[2].lower(), message[0], message[3]))
|
||||||
|
return
|
||||||
|
elif message[0].split("!")[0].lower() not in self.clientChannelOps:
|
||||||
|
self._log("Ignoring request on {} from {} due to lack of authorisation: {}".format(message[2].lower(), message[0], message[3]))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self._log("Processing request on {} from {}: {}".format(message[2].lower(), message[0], message[3]))
|
||||||
|
asciiUrl = message[3].split(" ")[1]
|
||||||
|
asciiTmpFilePath = "tmp.txt"; imgTmpFilePath = "tmp.png";
|
||||||
|
if os.path.isfile(asciiTmpFilePath):
|
||||||
|
os.remove(asciiTmpFilePath)
|
||||||
|
if os.path.isfile(imgTmpFilePath):
|
||||||
|
os.remove(imgTmpFilePath)
|
||||||
|
try:
|
||||||
|
urllib.request.urlretrieve(asciiUrl, asciiTmpFilePath, IrcMiRCARTBot._urlretrieveReportHook)
|
||||||
|
except IrcMiRCARTBot.ContentTooLargeException:
|
||||||
|
self._log("Download size exceeds quota of 1 MB!")
|
||||||
|
self.queue("PRIVMSG", message[2], "4/!\\ Download size exceeds quota of 1 MB!")
|
||||||
|
return
|
||||||
|
except urllib.error.HTTPError as err:
|
||||||
|
self._log("Download failed with HTTP status code {}".format(err.code))
|
||||||
|
self.queue("PRIVMSG", message[2], "4/!\\ Download failed with HTTP status code {}!".format(err.code))
|
||||||
|
return
|
||||||
|
except urllib.error.URLError as err:
|
||||||
|
self._log("Invalid URL specified!")
|
||||||
|
self.queue("PRIVMSG", message[2], "4/!\\ Invalid URL specified!")
|
||||||
|
return
|
||||||
|
except ValueError as err:
|
||||||
|
self._log("Unknown URL type specified!")
|
||||||
|
self.queue("PRIVMSG", message[2], "4/!\\ Unknown URL type specified!")
|
||||||
|
return
|
||||||
|
|
||||||
|
canvasStore = MiRCARTCanvasImportStore(inFile=asciiTmpFilePath)
|
||||||
|
numRowCols = 0
|
||||||
|
for numRow in range(len(canvasStore.outMap)):
|
||||||
|
numRowCols = max(numRowCols, len(canvasStore.outMap[numRow]))
|
||||||
|
for numRow in range(len(canvasStore.outMap)):
|
||||||
|
if len(canvasStore.outMap[numRow]) != numRowCols:
|
||||||
|
for numColOff in range(numRowCols - len(canvasStore.outMap[numRow])):
|
||||||
|
canvasStore.outMap[numRow].append([1, 1, 0, " "])
|
||||||
|
canvasStore.outMap[numRow].insert(0, [1, 1, 0, " "])
|
||||||
|
canvasStore.outMap[numRow].append([1, 1, 0, " "])
|
||||||
|
canvasStore.outMap.insert(0, [[1, 1, 0, " "]] * len(canvasStore.outMap[0]))
|
||||||
|
canvasStore.outMap.append([[1, 1, 0, " "]] * len(canvasStore.outMap[0]))
|
||||||
|
MiRCARTToPngFile(canvasStore.outMap, "DejaVuSansMono.ttf", 11).export(imgTmpFilePath)
|
||||||
|
imgurResponse = self._uploadToImgur(imgTmpFilePath, "MiRCART image", "MiRCART image", self.imgurApiKey)
|
||||||
|
if imgurResponse[0] == 200:
|
||||||
|
self._log("Uploaded as: {}".format(imgurResponse[1]))
|
||||||
|
self.queue("PRIVMSG", message[2], "8/!\\ Uploaded as: {}".format(imgurResponse[1]))
|
||||||
|
self.clientLastMessage = int(time.time())
|
||||||
|
else:
|
||||||
|
self._log("Upload failed with HTTP status code {}".format(imgurResponse[0]))
|
||||||
|
self._log("Message from website: {}".format(imgurResponse[1]))
|
||||||
|
self.queue("PRIVMSG", message[2], "4/!\\ Upload failed with HTTP status code {}!".format(imgurResponse[0]))
|
||||||
|
self.queue("PRIVMSG", message[2], "4/!\\ Message from website: {}".format(imgurResponse[1]))
|
||||||
|
if os.path.isfile(asciiTmpFilePath):
|
||||||
|
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()
|
||||||
|
requestData = { \
|
||||||
|
"image": base64.b64encode(requestImageData), \
|
||||||
|
"key": apiKey, \
|
||||||
|
"name": imgName, \
|
||||||
|
"title": imgTitle, \
|
||||||
|
"type": "base64"}
|
||||||
|
requestHeaders = { \
|
||||||
|
"Authorization": "Client-ID " + apiKey}
|
||||||
|
responseHttp = requests.post("https://api.imgur.com/3/upload.json", data=requestData, headers=requestHeaders)
|
||||||
|
responseDict = json.loads(responseHttp.text)
|
||||||
|
if responseHttp.status_code == 200:
|
||||||
|
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, preferFamily=0, timeout=None): Connect to server and (re)initialise w/ optional timeout
|
||||||
|
def connect(self, preferFamily=0, timeout=None):
|
||||||
|
self._log("Connecting to {}:{}...".format(self.serverHname, self.serverPort))
|
||||||
|
if super().connect(preferFamily=preferFamily, timeout=timeout):
|
||||||
|
self._log("Connected to {}:{}.".format(self.serverHname, self.serverPort))
|
||||||
|
self._log("Registering on {}:{} as {}, {}, {}...".format(self.serverHname, self.serverPort, self.clientNick, self.clientIdent, self.clientGecos))
|
||||||
|
self.clientLastMessage = 0; self.clientChannelOps = [];
|
||||||
|
self.clientChannelRejoin = False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
# }}}
|
||||||
|
# {{{ dispatch(self): Read, parse, and dispatch single line from server
|
||||||
|
def dispatch(self):
|
||||||
|
while True:
|
||||||
|
if self.clientNextTimeout:
|
||||||
|
timeNow = time.time()
|
||||||
|
if self.clientNextTimeout <= timeNow:
|
||||||
|
self._dispatchTimer()
|
||||||
|
self.unqueue()
|
||||||
|
serverMessage = self.readline()
|
||||||
|
if serverMessage == None:
|
||||||
|
self._dispatchNone(); break;
|
||||||
|
elif serverMessage == "":
|
||||||
|
continue
|
||||||
|
elif serverMessage[1] == "001":
|
||||||
|
self._dispatch001(serverMessage)
|
||||||
|
elif serverMessage[1] == "353":
|
||||||
|
self._dispatch353(serverMessage)
|
||||||
|
elif serverMessage[1] == "JOIN":
|
||||||
|
self._dispatchJoin(serverMessage)
|
||||||
|
elif serverMessage[1] == "KICK":
|
||||||
|
self._dispatchKick(serverMessage)
|
||||||
|
elif serverMessage[1] == "MODE":
|
||||||
|
self._dispatchMode(serverMessage)
|
||||||
|
elif serverMessage[1] == "PING":
|
||||||
|
self._dispatchPing(serverMessage)
|
||||||
|
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
|
||||||
|
|
||||||
|
#
|
||||||
|
# Entry point
|
||||||
|
def main(optdict, *argv):
|
||||||
|
_IrcMiRCARTBot = IrcMiRCARTBot(*argv)
|
||||||
|
while True:
|
||||||
|
if "-4" in optdict:
|
||||||
|
preferFamily = socket.AF_INET
|
||||||
|
elif "-6" in optdict:
|
||||||
|
preferFamily = socket.AF_INET6
|
||||||
|
else:
|
||||||
|
preferFamily = 0
|
||||||
|
if _IrcMiRCARTBot.connect(preferFamily=preferFamily, timeout=15):
|
||||||
|
_IrcMiRCARTBot.dispatch()
|
||||||
|
_IrcMiRCARTBot.close()
|
||||||
|
time.sleep(15)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
optlist, argv = getopt(sys.argv[1:], "46")
|
||||||
|
optdict = dict(optlist)
|
||||||
|
if len(argv) < 1 or len(argv) > 4:
|
||||||
|
print("usage: {} [-4|-6] " \
|
||||||
|
"<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)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
BIN
MiRCART.png
Normal file
After Width: | Height: | Size: 51 KiB |
43
MiRCART.py
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCART.py -- mIRC art editor for Windows & Linux
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTFrame import MiRCARTFrame
|
||||||
|
import sys, wx
|
||||||
|
|
||||||
|
#
|
||||||
|
# Entry point
|
||||||
|
def main(*argv):
|
||||||
|
wxApp = wx.App(False)
|
||||||
|
appFrame = MiRCARTFrame(None)
|
||||||
|
if len(argv) > 1 \
|
||||||
|
and len(argv[1]) > 0:
|
||||||
|
appFrame.panelCanvas.canvasInterface.canvasPathName = argv[1]
|
||||||
|
appFrame.panelCanvas.canvasImportStore.importTextFile(argv[1])
|
||||||
|
appFrame.panelCanvas.canvasImportStore.importIntoPanel()
|
||||||
|
appFrame.onCanvasUpdate(pathName=argv[1], undoLevel=-1)
|
||||||
|
wxApp.MainLoop()
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main(*sys.argv)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
60
MiRCARTCanonicalise.py
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTCanonicalise.py -- canonicalise mIRC art {from,to} file (for munki)
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTCanvasImportStore import MiRCARTCanvasImportStore
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def canonicalise(inPathName):
|
||||||
|
canvasStore = MiRCARTCanvasImportStore(inPathName)
|
||||||
|
inMap = canvasStore.outMap.copy(); del canvasStore;
|
||||||
|
with open(inPathName, "w+") as outFile:
|
||||||
|
for inCurRow in range(len(inMap)):
|
||||||
|
lastAttribs = MiRCARTCanvasImportStore._CellState.CS_NONE
|
||||||
|
lastColours = None
|
||||||
|
for inCurCol in range(len(inMap[inCurRow])):
|
||||||
|
inCurCell = inMap[inCurRow][inCurCol]
|
||||||
|
if lastAttribs != inCurCell[2]:
|
||||||
|
if inCurCell[2] & MiRCARTCanvasImportStore._CellState.CS_BOLD:
|
||||||
|
print("\u0002", end="", file=outFile)
|
||||||
|
if inCurCell[2] & MiRCARTCanvasImportStore._CellState.CS_UNDERLINE:
|
||||||
|
print("\u001f", end="", file=outFile)
|
||||||
|
lastAttribs = inCurCell[2]
|
||||||
|
if lastColours == None or lastColours != inCurCell[:2]:
|
||||||
|
print("\u0003{:02d},{:02d}{}".format(*inCurCell[:2], inCurCell[3]), end="", file=outFile)
|
||||||
|
lastColours = inCurCell[:2]
|
||||||
|
else:
|
||||||
|
print(inCurCell[3], end="", file=outFile)
|
||||||
|
print("\n", end="", file=outFile)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Entry point
|
||||||
|
def main(*argv):
|
||||||
|
canonicalise(argv[1])
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if (len(sys.argv) - 1) != 1:
|
||||||
|
print("usage: {} <MiRCART input file pathname>".format(sys.argv[0]), file=sys.stderr)
|
||||||
|
else:
|
||||||
|
main(*sys.argv)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
221
MiRCARTCanvas.py
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTCanvas.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTCanvasBackend import MiRCARTCanvasBackend
|
||||||
|
from MiRCARTCanvasJournal import MiRCARTCanvasJournal
|
||||||
|
from MiRCARTCanvasExportStore import MiRCARTCanvasExportStore, haveMiRCARTToPngFile, haveUrllib
|
||||||
|
from MiRCARTCanvasImportStore import MiRCARTCanvasImportStore
|
||||||
|
from MiRCARTCanvasInterface import MiRCARTCanvasInterface
|
||||||
|
from MiRCARTImgurApiKey import MiRCARTImgurApiKey
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class MiRCARTCanvas(wx.Panel):
|
||||||
|
"""XXX"""
|
||||||
|
parentFrame = None
|
||||||
|
defaultCanvasPos = defaultCanvasSize = defaultCellSize = None
|
||||||
|
canvasMap = canvasPos = canvasSize = None
|
||||||
|
brushColours = brushPos = brushSize = None
|
||||||
|
canvasBackend = canvasJournal = None
|
||||||
|
canvasExportStore = canvasImportStore = None
|
||||||
|
canvasInterface = None
|
||||||
|
imgurApiKey = MiRCARTImgurApiKey.imgurApiKey
|
||||||
|
|
||||||
|
# {{{ _commitPatch(self, patch): XXX
|
||||||
|
def _commitPatch(self, patch):
|
||||||
|
self.canvasMap[patch[1]][patch[0]] = patch[2:]
|
||||||
|
# }}}
|
||||||
|
# {{{ _dispatchDeltaPatches(self, deltaPatches): XXX
|
||||||
|
def _dispatchDeltaPatches(self, deltaPatches):
|
||||||
|
eventDc = self.canvasBackend.getDeviceContext(self)
|
||||||
|
for patch in deltaPatches:
|
||||||
|
if self.canvasBackend.drawPatch(eventDc, patch):
|
||||||
|
self._commitPatch(patch)
|
||||||
|
self.parentFrame.onCanvasUpdate(undoLevel=self.canvasJournal.patchesUndoLevel)
|
||||||
|
# }}}
|
||||||
|
# {{{ _dispatchPatch(self, eventDc, isCursor, patch): XXX
|
||||||
|
def _dispatchPatch(self, eventDc, isCursor, patch):
|
||||||
|
if not self._canvasDirtyCursor:
|
||||||
|
self.canvasBackend.drawCursorMaskWithJournal( \
|
||||||
|
self.canvasJournal, eventDc)
|
||||||
|
self._canvasDirtyCursor = True
|
||||||
|
if self.canvasBackend.drawPatch(eventDc, patch):
|
||||||
|
patchDeltaCell = self.canvasMap[patch[1]][patch[0]]
|
||||||
|
patchDelta = [*patch[0:2], *patchDeltaCell]
|
||||||
|
if isCursor:
|
||||||
|
self.canvasJournal.pushCursor(patchDelta)
|
||||||
|
else:
|
||||||
|
if not self._canvasDirty:
|
||||||
|
self.canvasJournal.pushDeltas([], [])
|
||||||
|
self._canvasDirty = True
|
||||||
|
self.canvasJournal.updateCurrentDeltas(patchDelta, patch)
|
||||||
|
self._commitPatch(patch)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ onPanelClose(self, event): XXX
|
||||||
|
def onPanelClose(self, event):
|
||||||
|
self.Destroy()
|
||||||
|
# }}}
|
||||||
|
# {{{ onPanelEnterWindow(self, event): XXX
|
||||||
|
def onPanelEnterWindow(self, event):
|
||||||
|
self.parentFrame.SetFocus()
|
||||||
|
# }}}
|
||||||
|
# {{{ onPanelInput(self, event): XXX
|
||||||
|
def onPanelInput(self, event):
|
||||||
|
eventDc = self.canvasBackend.getDeviceContext(self)
|
||||||
|
eventType = event.GetEventType()
|
||||||
|
self._canvasDirty = self._canvasDirtyCursor = False
|
||||||
|
tool = self.canvasInterface.canvasTool
|
||||||
|
if eventType == wx.wxEVT_CHAR:
|
||||||
|
mapPoint = self.brushPos
|
||||||
|
doSkip = tool.onKeyboardEvent( \
|
||||||
|
event, mapPoint, self.brushColours, self.brushSize, \
|
||||||
|
chr(event.GetUnicodeKey()), self._dispatchPatch, eventDc)
|
||||||
|
if doSkip:
|
||||||
|
event.Skip(); return;
|
||||||
|
else:
|
||||||
|
mapPoint = self.canvasBackend.xlateEventPoint(event, eventDc)
|
||||||
|
if mapPoint[0] >= self.canvasSize[0] \
|
||||||
|
or mapPoint[1] >= self.canvasSize[1]:
|
||||||
|
return
|
||||||
|
self.brushPos = mapPoint
|
||||||
|
tool.onMouseEvent( \
|
||||||
|
event, mapPoint, self.brushColours, self.brushSize, \
|
||||||
|
event.Dragging(), event.LeftIsDown(), event.RightIsDown(), \
|
||||||
|
self._dispatchPatch, eventDc)
|
||||||
|
if self._canvasDirty:
|
||||||
|
self.parentFrame.onCanvasUpdate(cellPos=self.brushPos, \
|
||||||
|
undoLevel=self.canvasJournal.patchesUndoLevel)
|
||||||
|
if eventType == wx.wxEVT_MOTION:
|
||||||
|
self.parentFrame.onCanvasUpdate(cellPos=mapPoint)
|
||||||
|
# }}}
|
||||||
|
# {{{ onPanelLeaveWindow(self, event): XXX
|
||||||
|
def onPanelLeaveWindow(self, event):
|
||||||
|
eventDc = self.canvasBackend.getDeviceContext(self)
|
||||||
|
self.canvasBackend.drawCursorMaskWithJournal( \
|
||||||
|
self.canvasJournal, eventDc)
|
||||||
|
# }}}
|
||||||
|
# {{{ onPanelPaint(self, event): XXX
|
||||||
|
def onPanelPaint(self, event):
|
||||||
|
self.canvasBackend.onPanelPaintEvent(self, event)
|
||||||
|
# }}}
|
||||||
|
# {{{ onStoreUpdate(self, newCanvasSize, newCanvas=None): XXX
|
||||||
|
def onStoreUpdate(self, newCanvasSize, newCanvas=None):
|
||||||
|
self.resize(newCanvasSize=newCanvasSize)
|
||||||
|
eventDc = self.canvasBackend.getDeviceContext(self)
|
||||||
|
for numRow in range(self.canvasSize[1]):
|
||||||
|
for numCol in range(self.canvasSize[0]):
|
||||||
|
if newCanvas != None \
|
||||||
|
and numRow < len(newCanvas) \
|
||||||
|
and numCol < len(newCanvas[numRow]):
|
||||||
|
self._commitPatch([ \
|
||||||
|
numCol, numRow, *newCanvas[numRow][numCol]])
|
||||||
|
self.canvasBackend.drawPatch(eventDc, \
|
||||||
|
[numCol, numRow, \
|
||||||
|
*self.canvasMap[numRow][numCol]])
|
||||||
|
wx.SafeYield()
|
||||||
|
# }}}
|
||||||
|
# {{{ resize(self, newCanvasSize): XXX
|
||||||
|
def resize(self, newCanvasSize):
|
||||||
|
if newCanvasSize != self.canvasSize:
|
||||||
|
if self.canvasMap == None:
|
||||||
|
self.canvasMap = []; oldCanvasSize = [0, 0];
|
||||||
|
else:
|
||||||
|
oldCanvasSize = self.canvasSize
|
||||||
|
deltaCanvasSize = [b-a for a,b in zip(oldCanvasSize, newCanvasSize)]
|
||||||
|
|
||||||
|
newWinSize = [a*b for a,b in zip(newCanvasSize, self.canvasBackend.cellSize)]
|
||||||
|
self.SetMinSize(newWinSize)
|
||||||
|
self.SetSize(wx.DefaultCoord, wx.DefaultCoord, *newWinSize)
|
||||||
|
curWindow = self
|
||||||
|
while curWindow != None:
|
||||||
|
curWindow.Layout()
|
||||||
|
curWindow = curWindow.GetParent()
|
||||||
|
|
||||||
|
self.canvasBackend.resize(newCanvasSize, self.canvasBackend.cellSize)
|
||||||
|
eventDc = self.canvasBackend.getDeviceContext(self)
|
||||||
|
self.canvasJournal.resetCursor(); self.canvasJournal.resetUndo();
|
||||||
|
|
||||||
|
if deltaCanvasSize[0] < 0:
|
||||||
|
for numRow in range(oldCanvasSize[1]):
|
||||||
|
del self.canvasMap[numRow][-1:(deltaCanvasSize[0]-1):-1]
|
||||||
|
else:
|
||||||
|
for numRow in range(oldCanvasSize[1]):
|
||||||
|
self.canvasMap[numRow].extend( \
|
||||||
|
[[1, 1, 0, " "]] * deltaCanvasSize[0])
|
||||||
|
for numNewCol in range(oldCanvasSize[0], newCanvasSize[0]):
|
||||||
|
self.canvasBackend.drawPatch( \
|
||||||
|
eventDc, [numNewCol, numRow, \
|
||||||
|
*self.canvasMap[numRow][-1]])
|
||||||
|
if deltaCanvasSize[1] < 0:
|
||||||
|
del self.canvasMap[-1:(deltaCanvasSize[1]-1):-1]
|
||||||
|
else:
|
||||||
|
for numNewRow in range(oldCanvasSize[1], newCanvasSize[1]):
|
||||||
|
self.canvasMap.extend( \
|
||||||
|
[[[1, 1, 0, " "]] * newCanvasSize[0]])
|
||||||
|
for numNewCol in range(newCanvasSize[0]):
|
||||||
|
self.canvasBackend.drawPatch( \
|
||||||
|
eventDc, [numNewCol, numNewRow, \
|
||||||
|
*self.canvasMap[-1][-1]])
|
||||||
|
|
||||||
|
self.canvasSize = newCanvasSize
|
||||||
|
wx.SafeYield()
|
||||||
|
self.parentFrame.onCanvasUpdate(size=newCanvasSize, undoLevel=-1)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ __del__(self): destructor method
|
||||||
|
def __del__(self):
|
||||||
|
if self.canvasMap != None:
|
||||||
|
self.canvasMap.clear(); self.canvasMap = None;
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, parent, parentFrame, defaultCanvasPos, defaultCanvasSize, defaultCellSize): initialisation method
|
||||||
|
def __init__(self, parent, parentFrame, defaultCanvasPos, defaultCanvasSize, defaultCellSize):
|
||||||
|
super().__init__(parent, pos=defaultCanvasPos, \
|
||||||
|
size=[w*h for w,h in zip(defaultCanvasSize, defaultCellSize)])
|
||||||
|
|
||||||
|
self.parentFrame = parentFrame
|
||||||
|
self.canvasMap = None
|
||||||
|
self.canvasPos = defaultCanvasPos; self.canvasSize = defaultCanvasSize;
|
||||||
|
self.defaultCanvasPos = defaultCanvasPos; self.defaultCanvasSize = defaultCanvasSize;
|
||||||
|
self.brushColours = [4, 1]; self.brushPos = [0, 0]; self.brushSize = [1, 1];
|
||||||
|
self.parentFrame.onCanvasUpdate( \
|
||||||
|
brushSize=self.brushSize, colours=self.brushColours)
|
||||||
|
self.canvasBackend = MiRCARTCanvasBackend(defaultCanvasSize, defaultCellSize)
|
||||||
|
self.canvasJournal = MiRCARTCanvasJournal()
|
||||||
|
self.canvasExportStore = MiRCARTCanvasExportStore(parentCanvas=self)
|
||||||
|
self.canvasImportStore = MiRCARTCanvasImportStore(parentCanvas=self)
|
||||||
|
self.canvasInterface = MiRCARTCanvasInterface(self, parentFrame)
|
||||||
|
|
||||||
|
# Bind event handlers
|
||||||
|
self.Bind(wx.EVT_CLOSE, self.onPanelClose)
|
||||||
|
self.Bind(wx.EVT_ENTER_WINDOW, self.onPanelEnterWindow)
|
||||||
|
self.Bind(wx.EVT_LEAVE_WINDOW, self.onPanelLeaveWindow)
|
||||||
|
self.parentFrame.Bind(wx.EVT_CHAR, self.onPanelInput)
|
||||||
|
for eventType in( \
|
||||||
|
wx.EVT_LEFT_DOWN, wx.EVT_MOTION, wx.EVT_RIGHT_DOWN):
|
||||||
|
self.Bind(eventType, self.onPanelInput)
|
||||||
|
self.Bind(wx.EVT_PAINT, self.onPanelPaint)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
175
MiRCARTCanvasBackend.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTCanvasBackend.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTColours import MiRCARTColours
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class MiRCARTCanvasBackend():
|
||||||
|
"""XXX"""
|
||||||
|
_font = _brushes = _pens = None
|
||||||
|
_lastBrush = _lastPen = None
|
||||||
|
canvasBitmap = cellSize = None
|
||||||
|
|
||||||
|
# {{{ _drawBrushPatch(self, eventDc, patch): XXX
|
||||||
|
def _drawBrushPatch(self, eventDc, patch):
|
||||||
|
absPoint = self._xlatePoint(patch)
|
||||||
|
brushFg = self._brushes[patch[3]]
|
||||||
|
brushBg = self._brushes[patch[2]]
|
||||||
|
pen = self._pens[patch[3]]
|
||||||
|
self._setBrushDc(brushBg, brushFg, eventDc, pen)
|
||||||
|
eventDc.DrawRectangle(*absPoint, *self.cellSize)
|
||||||
|
# }}}
|
||||||
|
# {{{ _drawCharPatch(self, eventDc, patch): XXX
|
||||||
|
def _drawCharPatch(self, eventDc, patch):
|
||||||
|
absPoint = self._xlatePoint(patch)
|
||||||
|
brushFg = self._brushes[patch[2]]
|
||||||
|
brushBg = self._brushes[patch[3]]
|
||||||
|
pen = self._pens[patch[3]]
|
||||||
|
fontBitmap = wx.Bitmap(*self.cellSize)
|
||||||
|
fontDc = wx.MemoryDC(); fontDc.SelectObject(fontBitmap);
|
||||||
|
fontDc.SetTextForeground(wx.Colour(MiRCARTColours[patch[2]][0:4]))
|
||||||
|
fontDc.SetTextBackground(wx.Colour(MiRCARTColours[patch[3]][0:4]))
|
||||||
|
fontDc.SetBrush(brushBg); fontDc.SetBackground(brushBg); fontDc.SetPen(pen);
|
||||||
|
fontDc.SetFont(self._font)
|
||||||
|
fontDc.DrawRectangle(0, 0, *self.cellSize)
|
||||||
|
fontDc.DrawText(patch[5], 0, 0)
|
||||||
|
eventDc.Blit(*absPoint, *self.cellSize, fontDc, 0, 0)
|
||||||
|
# }}}
|
||||||
|
# {{{ _finiBrushesAndPens(self): XXX
|
||||||
|
def _finiBrushesAndPens(self):
|
||||||
|
for brush in self._brushes or []:
|
||||||
|
brush.Destroy()
|
||||||
|
self._brushes = None
|
||||||
|
for pen in self._pens or []:
|
||||||
|
pen.Destroy()
|
||||||
|
self._pens = None
|
||||||
|
self._lastBrushBg = self._lastBrushFg = self._lastPen = None;
|
||||||
|
# }}}
|
||||||
|
# {{{ _initBrushesAndPens(self): XXX
|
||||||
|
def _initBrushesAndPens(self):
|
||||||
|
self._brushes = [None for x in range(len(MiRCARTColours))]
|
||||||
|
self._pens = [None for x in range(len(MiRCARTColours))]
|
||||||
|
for mircColour in range(len(MiRCARTColours)):
|
||||||
|
self._brushes[mircColour] = wx.Brush( \
|
||||||
|
wx.Colour(MiRCARTColours[mircColour][0:4]), wx.BRUSHSTYLE_SOLID)
|
||||||
|
self._pens[mircColour] = wx.Pen( \
|
||||||
|
wx.Colour(MiRCARTColours[mircColour][0:4]), 1)
|
||||||
|
self._lastBrushBg = self._lastBrushFg = self._lastPen = None;
|
||||||
|
# }}}
|
||||||
|
# {{{ _setBrushDc(self, brushBg, brushFg, dc, pen): XXX
|
||||||
|
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, patch): XXX
|
||||||
|
def _xlatePoint(self, patch):
|
||||||
|
return [a*b for a,b in zip(patch[0:2], self.cellSize)]
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ drawPatch(self, eventDc, patch): XXX
|
||||||
|
def drawPatch(self, eventDc, patch):
|
||||||
|
if patch[0] < self.canvasSize[0] \
|
||||||
|
and patch[0] >= 0 \
|
||||||
|
and patch[1] < self.canvasSize[1] \
|
||||||
|
and patch[1] >= 0:
|
||||||
|
if patch[5] == " ":
|
||||||
|
self._drawBrushPatch(eventDc, patch)
|
||||||
|
else:
|
||||||
|
self._drawCharPatch(eventDc, patch)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
# }}}
|
||||||
|
# {{{ drawCursorMaskWithJournal(self, canvasJournal, eventDc): XXX
|
||||||
|
def drawCursorMaskWithJournal(self, canvasJournal, eventDc):
|
||||||
|
for patch in canvasJournal.popCursor():
|
||||||
|
self.drawPatch(eventDc, patch)
|
||||||
|
# }}}
|
||||||
|
# {{{ getDeviceContext(self, parentWindow): XXX
|
||||||
|
def getDeviceContext(self, parentWindow):
|
||||||
|
eventDc = wx.BufferedDC( \
|
||||||
|
wx.ClientDC(parentWindow), self.canvasBitmap)
|
||||||
|
self._lastBrushBg = self._lastBrushFg = self._lastPen = None;
|
||||||
|
return eventDc
|
||||||
|
# }}}
|
||||||
|
# {{{ onPanelPaintEvent(self, panelWindow, panelEvent): XXX
|
||||||
|
def onPanelPaintEvent(self, panelWindow, panelEvent):
|
||||||
|
if self.canvasBitmap != None:
|
||||||
|
eventDc = wx.BufferedPaintDC(panelWindow, self.canvasBitmap)
|
||||||
|
# }}}
|
||||||
|
# {{{ reset(self, canvasSize, cellSize):
|
||||||
|
def reset(self, canvasSize, cellSize):
|
||||||
|
self.resize(canvasSize, cellSize)
|
||||||
|
# }}}
|
||||||
|
# {{{ resize(self, canvasSize, cellSize):
|
||||||
|
def resize(self, canvasSize, cellSize):
|
||||||
|
winSize = [a*b for a,b in zip(canvasSize, 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)
|
||||||
|
self.canvasBitmap.Destroy(); self.canvasBitmap = newBitmap;
|
||||||
|
self.canvasSize = canvasSize; self.cellSize = cellSize;
|
||||||
|
self._font = wx.Font( \
|
||||||
|
8, \
|
||||||
|
wx.FONTFAMILY_TELETYPE, \
|
||||||
|
wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
|
||||||
|
# }}}
|
||||||
|
# {{{ xlateEventPoint(self, event, eventDc): XXX
|
||||||
|
def xlateEventPoint(self, event, eventDc):
|
||||||
|
eventPoint = event.GetLogicalPosition(eventDc)
|
||||||
|
rectX = eventPoint.x - (eventPoint.x % self.cellSize[0])
|
||||||
|
rectY = eventPoint.y - (eventPoint.y % self.cellSize[1])
|
||||||
|
mapX = int(rectX / self.cellSize[0] if rectX else 0)
|
||||||
|
mapY = int(rectY / self.cellSize[1] if rectY else 0)
|
||||||
|
return (mapX, mapY)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ __del__(self): destructor method
|
||||||
|
def __del__(self):
|
||||||
|
if self.canvasBitmap != None:
|
||||||
|
self.canvasBitmap.Destroy(); self.canvasBitmap = None;
|
||||||
|
self._finiBrushesAndPens()
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, canvasSize, cellSize): initialisation method
|
||||||
|
def __init__(self, canvasSize, cellSize):
|
||||||
|
self._initBrushesAndPens()
|
||||||
|
self.reset(canvasSize, cellSize)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
129
MiRCARTCanvasExportStore.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTCanvasExportStore.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
import io, os, tempfile
|
||||||
|
|
||||||
|
try:
|
||||||
|
from MiRCARTToPngFile import MiRCARTToPngFile
|
||||||
|
haveMiRCARTToPngFile = True
|
||||||
|
except ImportError:
|
||||||
|
haveMiRCARTToPngFile = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
import base64, json, requests, urllib.request
|
||||||
|
haveUrllib = True
|
||||||
|
except ImportError:
|
||||||
|
haveUrllib = False
|
||||||
|
|
||||||
|
class MiRCARTCanvasExportStore():
|
||||||
|
"""XXX"""
|
||||||
|
parentCanvas = None
|
||||||
|
|
||||||
|
# {{{ _exportFileToImgur(self, apiKey, imgName, imgTitle, pathName): upload single PNG file to Imgur
|
||||||
|
def _exportFileToImgur(self, apiKey, imgName, imgTitle, pathName):
|
||||||
|
with open(pathName, "rb") as requestImage:
|
||||||
|
requestImageData = requestImage.read()
|
||||||
|
requestData = { \
|
||||||
|
"image": base64.b64encode(requestImageData), \
|
||||||
|
"key": apiKey, \
|
||||||
|
"name": imgName, \
|
||||||
|
"title": imgTitle, \
|
||||||
|
"type": "base64"}
|
||||||
|
requestHeaders = {"Authorization": "Client-ID " + apiKey}
|
||||||
|
responseHttp = requests.post( \
|
||||||
|
"https://api.imgur.com/3/upload.json", \
|
||||||
|
data=requestData, headers=requestHeaders)
|
||||||
|
responseDict = json.loads(responseHttp.text)
|
||||||
|
if responseHttp.status_code == 200:
|
||||||
|
return [200, responseDict.get("data").get("link")]
|
||||||
|
else:
|
||||||
|
return [responseHttp.status_code, ""]
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ exportBitmapToPngFile(self, canvasBitmap, outPathName, outType): XXX
|
||||||
|
def exportBitmapToPngFile(self, canvasBitmap, outPathName, outType):
|
||||||
|
return canvasBitmap.ConvertToImage().SaveFile(outPathName, outType)
|
||||||
|
# }}}
|
||||||
|
# {{{ exportBitmapToImgur(self, apiKey, canvasBitmap, imgName, imgTitle, imgType): XXX
|
||||||
|
def exportBitmapToImgur(self, apiKey, canvasBitmap, imgName, imgTitle, imgType):
|
||||||
|
tmpPathName = tempfile.mkstemp()
|
||||||
|
os.close(tmpPathName[0])
|
||||||
|
canvasBitmap.ConvertToImage().SaveFile(tmpPathName[1], imgType)
|
||||||
|
imgurResult = self._exportFileToImgur(apiKey, imgName, imgTitle, tmpPathName[1])
|
||||||
|
os.remove(tmpPathName[1])
|
||||||
|
return imgurResult
|
||||||
|
# }}}
|
||||||
|
# {{{ exportPastebin(self, apiDevKey, canvasMap, canvasSize, pasteName="", pastePrivate=0): XXX
|
||||||
|
def exportPastebin(self, apiDevKey, canvasMap, canvasSize, pasteName="", pastePrivate=0):
|
||||||
|
if haveUrllib:
|
||||||
|
outFile = io.StringIO()
|
||||||
|
self.exportTextFile(canvasMap, canvasSize, outFile)
|
||||||
|
requestData = { \
|
||||||
|
"api_dev_key": apiDevKey, \
|
||||||
|
"api_option": "paste", \
|
||||||
|
"api_paste_code": outFile.getvalue().encode(), \
|
||||||
|
"api_paste_name": pasteName, \
|
||||||
|
"api_paste_private": pastePrivate}
|
||||||
|
responseHttp = requests.post("https://pastebin.com/api/api_post.php", \
|
||||||
|
data=requestData)
|
||||||
|
if responseHttp.status_code == 200:
|
||||||
|
if responseHttp.text.startswith("http"):
|
||||||
|
return (True, responseHttp.text)
|
||||||
|
else:
|
||||||
|
return (False, responseHttp.text)
|
||||||
|
else:
|
||||||
|
return (False, str(responseHttp.status_code))
|
||||||
|
else:
|
||||||
|
return (False, "missing requests and/or urllib3 module(s)")
|
||||||
|
# }}}
|
||||||
|
# {{{ exportPngFile(self, canvasMap, outPathName): XXX
|
||||||
|
def exportPngFile(self, canvasMap, outPathName):
|
||||||
|
if haveMiRCARTToPngFile:
|
||||||
|
MiRCARTToPngFile(canvasMap).export(outPathName)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
# }}}
|
||||||
|
# {{{ exportTextFile(self, canvasMap, canvasSize, outFile): XXX
|
||||||
|
def exportTextFile(self, canvasMap, canvasSize, outFile):
|
||||||
|
for canvasRow in range(canvasSize[1]):
|
||||||
|
canvasLastColours = []
|
||||||
|
for canvasCol in range(canvasSize[0]):
|
||||||
|
canvasColColours = canvasMap[canvasRow][canvasCol][0]
|
||||||
|
canvasColText = canvasMap[canvasRow][canvasCol][2]
|
||||||
|
if canvasColColours != canvasLastColours:
|
||||||
|
canvasLastColours = canvasColColours
|
||||||
|
outFile.write("\x03" + \
|
||||||
|
str(canvasColColours[0]) + \
|
||||||
|
"," + str(canvasColColours[1]))
|
||||||
|
outFile.write(canvasColText)
|
||||||
|
outFile.write("\n")
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, parentCanvas): initialisation method
|
||||||
|
def __init__(self, parentCanvas):
|
||||||
|
self.parentCanvas = parentCanvas
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
163
MiRCARTCanvasImportStore.py
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTCanvasImportStore.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
class MiRCARTCanvasImportStore():
|
||||||
|
"""XXX"""
|
||||||
|
inFile = inSize = outMap = None
|
||||||
|
parentCanvas = None
|
||||||
|
|
||||||
|
#
|
||||||
|
# _CellState(): Cell state
|
||||||
|
class _CellState():
|
||||||
|
CS_NONE = 0x00
|
||||||
|
CS_BOLD = 0x01
|
||||||
|
CS_ITALIC = 0x02
|
||||||
|
CS_UNDERLINE = 0x04
|
||||||
|
|
||||||
|
#
|
||||||
|
# _ParseState(): Parsing loop state
|
||||||
|
class _ParseState():
|
||||||
|
PS_CHAR = 1
|
||||||
|
PS_COLOUR_DIGIT0 = 2
|
||||||
|
PS_COLOUR_DIGIT1 = 3
|
||||||
|
|
||||||
|
# {{{ _flipCellStateBit(self, cellState, bit): XXX
|
||||||
|
def _flipCellStateBit(self, cellState, bit):
|
||||||
|
if cellState & bit:
|
||||||
|
return cellState & ~bit
|
||||||
|
else:
|
||||||
|
return cellState | bit
|
||||||
|
# }}}
|
||||||
|
# {{{ _parseCharAsColourSpec(self, colourSpec, curColours): XXX
|
||||||
|
def _parseCharAsColourSpec(self, colourSpec, curColours):
|
||||||
|
if len(colourSpec) > 0:
|
||||||
|
colourSpec = colourSpec.split(",")
|
||||||
|
if len(colourSpec) == 2 \
|
||||||
|
and len(colourSpec[1]) > 0:
|
||||||
|
return (int(colourSpec[0] or curColours[0]), \
|
||||||
|
int(colourSpec[1]))
|
||||||
|
elif len(colourSpec) == 1 \
|
||||||
|
or len(colourSpec[1]) == 0:
|
||||||
|
return (int(colourSpec[0]), curColours[1])
|
||||||
|
else:
|
||||||
|
return (15, 1)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ importIntoPanel(self): XXX
|
||||||
|
def importIntoPanel(self):
|
||||||
|
self.parentCanvas.onStoreUpdate(self.inSize, self.outMap)
|
||||||
|
# }}}
|
||||||
|
# {{{ importTextFile(self, pathName): XXX
|
||||||
|
def importTextFile(self, pathName):
|
||||||
|
self.inFile = open(pathName, "r", encoding="utf-8")
|
||||||
|
self.inSize = self.outMap = None;
|
||||||
|
inCurColourSpec = ""; inCurRow = -1;
|
||||||
|
inLine = self.inFile.readline()
|
||||||
|
inSize = [0, 0]; outMap = []; inMaxCols = 0;
|
||||||
|
while inLine:
|
||||||
|
inCellState = self._CellState.CS_NONE
|
||||||
|
inParseState = self._ParseState.PS_CHAR
|
||||||
|
inCurCol = 0; inMaxCol = len(inLine);
|
||||||
|
inCurColourDigits = 0; inCurColours = (15, 1); inCurColourSpec = "";
|
||||||
|
inCurRow += 1; outMap.append([]); inRowCols = 0; inSize[1] += 1;
|
||||||
|
while inCurCol < inMaxCol:
|
||||||
|
inChar = inLine[inCurCol]
|
||||||
|
if inChar in set("\r\n"): \
|
||||||
|
inCurCol += 1
|
||||||
|
elif inParseState == self._ParseState.PS_CHAR:
|
||||||
|
inCurCol += 1
|
||||||
|
if inChar == "":
|
||||||
|
inCellState = self._flipCellStateBit( \
|
||||||
|
inCellState, self._CellState.CS_BOLD)
|
||||||
|
elif inChar == "":
|
||||||
|
inParseState = self._ParseState.PS_COLOUR_DIGIT0
|
||||||
|
elif inChar == "":
|
||||||
|
inCellState = self._flipCellStateBit( \
|
||||||
|
inCellState, self._CellState.CS_ITALIC)
|
||||||
|
elif inChar == "":
|
||||||
|
inCellState |= self._CellState.CS_NONE
|
||||||
|
inCurColours = (15, 1)
|
||||||
|
elif inChar == "":
|
||||||
|
inCurColours = (inCurColours[1], inCurColours[0])
|
||||||
|
elif inChar == "":
|
||||||
|
inCellState = self._flipCellStateBit( \
|
||||||
|
inCellState, self._CellState.CS_UNDERLINE)
|
||||||
|
else:
|
||||||
|
inRowCols += 1
|
||||||
|
outMap[inCurRow].append([*inCurColours, inCellState, inChar])
|
||||||
|
elif inParseState == self._ParseState.PS_COLOUR_DIGIT0 \
|
||||||
|
or inParseState == self._ParseState.PS_COLOUR_DIGIT1:
|
||||||
|
if inChar == "," \
|
||||||
|
and inParseState == self._ParseState.PS_COLOUR_DIGIT0:
|
||||||
|
if (inCurCol + 1) < inMaxCol \
|
||||||
|
and not inLine[inCurCol + 1] in set("0123456789"):
|
||||||
|
inCurColours = self._parseCharAsColourSpec( \
|
||||||
|
inCurColourSpec, inCurColours)
|
||||||
|
inCurColourDigits = 0; inCurColourSpec = "";
|
||||||
|
inParseState = self._ParseState.PS_CHAR
|
||||||
|
else:
|
||||||
|
inCurCol += 1
|
||||||
|
inCurColourDigits = 0; inCurColourSpec += inChar;
|
||||||
|
inParseState = self._ParseState.PS_COLOUR_DIGIT1
|
||||||
|
elif inChar in set("0123456789") \
|
||||||
|
and inCurColourDigits == 0:
|
||||||
|
inCurCol += 1
|
||||||
|
inCurColourDigits += 1; inCurColourSpec += inChar;
|
||||||
|
elif inChar in set("0123456789") \
|
||||||
|
and inCurColourDigits == 1 \
|
||||||
|
and inCurColourSpec[-1] == "0":
|
||||||
|
inCurCol += 1
|
||||||
|
inCurColourDigits += 1; inCurColourSpec += inChar;
|
||||||
|
elif inChar in set("012345") \
|
||||||
|
and inCurColourDigits == 1 \
|
||||||
|
and inCurColourSpec[-1] == "1":
|
||||||
|
inCurCol += 1
|
||||||
|
inCurColourDigits += 1; inCurColourSpec += inChar;
|
||||||
|
else:
|
||||||
|
inCurColours = self._parseCharAsColourSpec( \
|
||||||
|
inCurColourSpec, inCurColours)
|
||||||
|
inCurColourDigits = 0; inCurColourSpec = "";
|
||||||
|
inParseState = self._ParseState.PS_CHAR
|
||||||
|
inMaxCols = max(inMaxCols, inRowCols)
|
||||||
|
inLine = self.inFile.readline()
|
||||||
|
inSize[0] = inMaxCols; self.inSize = inSize; self.outMap = outMap;
|
||||||
|
self.inFile.close()
|
||||||
|
# }}}
|
||||||
|
# {{{ importNew(self, newCanvasSize=None): XXX
|
||||||
|
def importNew(self, newCanvasSize=None):
|
||||||
|
newMap = [[[1, 1, 0, " "] \
|
||||||
|
for x in range(newCanvasSize[0])] \
|
||||||
|
for y in range(newCanvasSize[1])]
|
||||||
|
self.parentCanvas.onStoreUpdate(newCanvasSize, newMap)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, inFile=None, parentCanvas=None): initialisation method
|
||||||
|
def __init__(self, inFile=None, parentCanvas=None):
|
||||||
|
self.inFile = inFile; self.inSize = self.outMap = None;
|
||||||
|
self.parentCanvas = parentCanvas
|
||||||
|
if inFile != None:
|
||||||
|
self.importTextFile(inFile)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
353
MiRCARTCanvasInterface.py
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTCanvasInterface.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTToolCircle import MiRCARTToolCircle
|
||||||
|
from MiRCARTToolFill import MiRCARTToolFill
|
||||||
|
from MiRCARTToolLine import MiRCARTToolLine
|
||||||
|
from MiRCARTToolSelectClone import MiRCARTToolSelectClone
|
||||||
|
from MiRCARTToolSelectMove import MiRCARTToolSelectMove
|
||||||
|
from MiRCARTToolRect import MiRCARTToolRect
|
||||||
|
from MiRCARTToolText import MiRCARTToolText
|
||||||
|
|
||||||
|
import os, wx
|
||||||
|
|
||||||
|
class MiRCARTCanvasInterface():
|
||||||
|
"""XXX"""
|
||||||
|
imgurApiKey = None
|
||||||
|
parentCanvas = parentFrame = canvasPathName = canvasTool = None
|
||||||
|
|
||||||
|
# {{{ _dialogSaveChanges(self)
|
||||||
|
def _dialogSaveChanges(self):
|
||||||
|
with wx.MessageDialog(self.parentCanvas, \
|
||||||
|
"Do you want to save changes to {}?".format( \
|
||||||
|
self.canvasPathName), "MiRCART", \
|
||||||
|
wx.CANCEL|wx.CANCEL_DEFAULT|wx.ICON_QUESTION|wx.YES_NO) as dialog:
|
||||||
|
dialogChoice = dialog.ShowModal()
|
||||||
|
return dialogChoice
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ canvasBrushSolid(self, event): XXX
|
||||||
|
def canvasBrushSolid(self, event):
|
||||||
|
pass
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasColour(self, event, numColour): XXX
|
||||||
|
def canvasColour(self, event, numColour):
|
||||||
|
if event.GetEventType() == wx.wxEVT_TOOL:
|
||||||
|
self.parentCanvas.brushColours[0] = numColour
|
||||||
|
elif event.GetEventType() == wx.wxEVT_TOOL_RCLICKED:
|
||||||
|
self.parentCanvas.brushColours[1] = numColour
|
||||||
|
self.parentFrame.onCanvasUpdate(colours=self.parentCanvas.brushColours)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasCopy(self, event): XXX
|
||||||
|
def canvasCopy(self, event):
|
||||||
|
pass
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasCut(self, event): XXX
|
||||||
|
def canvasCut(self, event):
|
||||||
|
pass
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasDecrBrushHeight(self, event): XXX
|
||||||
|
def canvasDecrBrushHeight(self, event):
|
||||||
|
if self.parentCanvas.brushSize[1] > 1:
|
||||||
|
self.parentCanvas.brushSize[1] -= 1
|
||||||
|
self.parentFrame.onCanvasUpdate(brushSize=self.parentCanvas.brushSize)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasDecrBrushHeightWidth(self, event): XXX
|
||||||
|
def canvasDecrBrushHeightWidth(self, event):
|
||||||
|
self.canvasDecrBrushHeight(event)
|
||||||
|
self.canvasDecrBrushWidth(event)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasDecrBrushWidth(self, event): XXX
|
||||||
|
def canvasDecrBrushWidth(self, event):
|
||||||
|
if self.parentCanvas.brushSize[0] > 1:
|
||||||
|
self.parentCanvas.brushSize[0] -= 1
|
||||||
|
self.parentFrame.onCanvasUpdate(brushSize=self.parentCanvas.brushSize)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasDecrCanvasHeight(self, event): XXX
|
||||||
|
def canvasDecrCanvasHeight(self, event):
|
||||||
|
if self.parentCanvas.canvasSize[1] > 1:
|
||||||
|
self.parentCanvas.resize([ \
|
||||||
|
self.parentCanvas.canvasSize[0], \
|
||||||
|
self.parentCanvas.canvasSize[1]-1])
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasDecrCanvasHeightWidth(self, event): XXX
|
||||||
|
def canvasDecrCanvasHeightWidth(self, event):
|
||||||
|
self.canvasDecrCanvasHeight(event)
|
||||||
|
self.canvasDecrCanvasWidth(event)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasDecrCanvasWidth(self, event): XXX
|
||||||
|
def canvasDecrCanvasWidth(self, event):
|
||||||
|
if self.parentCanvas.canvasSize[0] > 1:
|
||||||
|
self.parentCanvas.resize([ \
|
||||||
|
self.parentCanvas.canvasSize[0]-1, \
|
||||||
|
self.parentCanvas.canvasSize[1]])
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasDelete(self, event): XXX
|
||||||
|
def canvasDelete(self, event):
|
||||||
|
pass
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasExit(self, event): XXX
|
||||||
|
def canvasExit(self, event):
|
||||||
|
if self.canvasPathName != None:
|
||||||
|
saveChanges = self._dialogSaveChanges()
|
||||||
|
if saveChanges == wx.ID_CANCEL:
|
||||||
|
return
|
||||||
|
elif saveChanges == wx.ID_NO:
|
||||||
|
pass
|
||||||
|
elif saveChanges == wx.ID_YES:
|
||||||
|
self.canvasSave(event)
|
||||||
|
self.parentFrame.Close(True)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasExportAsPng(self, event): XXX
|
||||||
|
def canvasExportAsPng(self, event):
|
||||||
|
with wx.FileDialog(self, "Save As...", os.getcwd(), "", \
|
||||||
|
"*.png", wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dialog:
|
||||||
|
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
outPathName = dialog.GetPath()
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||||
|
self.parentCanvas.canvasExportStore.exportBitmapToPngFile( \
|
||||||
|
self.parentCanvas.canvasBackend.canvasBitmap, outPathName, \
|
||||||
|
wx.BITMAP_TYPE_PNG)
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||||
|
return True
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasExportImgur(self, event): XXX
|
||||||
|
def canvasExportImgur(self, event):
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||||
|
imgurResult = self.parentCanvas.canvasExportStore.exportBitmapToImgur( \
|
||||||
|
self.imgurApiKey, self.parentCanvas.canvasBackend.canvasBitmap, \
|
||||||
|
"", "", wx.BITMAP_TYPE_PNG)
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||||
|
if imgurResult[0] == 200:
|
||||||
|
if not wx.TheClipboard.IsOpened():
|
||||||
|
wx.TheClipboard.Open()
|
||||||
|
wx.TheClipboard.SetData(wx.TextDataObject(imgurResult[1]))
|
||||||
|
wx.TheClipboard.Close()
|
||||||
|
wx.MessageBox("Exported to Imgur: " + imgurResult[1], \
|
||||||
|
"Export to Imgur", wx.OK|wx.ICON_INFORMATION)
|
||||||
|
else:
|
||||||
|
wx.MessageBox("Failed to export to Imgur: " + imgurResult[1], \
|
||||||
|
"Export to Imgur", wx.OK|wx.ICON_EXCLAMATION)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasExportPastebin(self, event): XXX
|
||||||
|
def canvasExportPastebin(self, event):
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||||
|
pasteStatus, pasteResult = \
|
||||||
|
self.parentCanvas.canvasExportStore.exportPastebin( \
|
||||||
|
"", \
|
||||||
|
self.parentCanvas.canvasMap, \
|
||||||
|
self.parentCanvas.canvasSize)
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||||
|
if pasteStatus:
|
||||||
|
if not wx.TheClipboard.IsOpened():
|
||||||
|
wx.TheClipboard.Open()
|
||||||
|
wx.TheClipboard.SetData(wx.TextDataObject(pasteResult))
|
||||||
|
wx.TheClipboard.Close()
|
||||||
|
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)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasIncrBrushHeight(self, event): XXX
|
||||||
|
def canvasIncrBrushHeight(self, event):
|
||||||
|
self.parentCanvas.brushSize[1] += 1
|
||||||
|
self.parentFrame.onCanvasUpdate(brushSize=self.parentCanvas.brushSize)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasIncrBrushHeightWidth(self, event): XXX
|
||||||
|
def canvasIncrBrushHeightWidth(self, event):
|
||||||
|
self.canvasIncrBrushHeight(event)
|
||||||
|
self.canvasIncrBrushWidth(event)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasIncrBrushWidth(self, event): XXX
|
||||||
|
def canvasIncrBrushWidth(self, event):
|
||||||
|
self.parentCanvas.brushSize[0] += 1
|
||||||
|
self.parentFrame.onCanvasUpdate(brushSize=self.parentCanvas.brushSize)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasIncrCanvasHeight(self, event): XXX
|
||||||
|
def canvasIncrCanvasHeight(self, event):
|
||||||
|
self.parentCanvas.resize([ \
|
||||||
|
self.parentCanvas.canvasSize[0], \
|
||||||
|
self.parentCanvas.canvasSize[1]+1])
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasIncrCanvasHeightWidth(self, event): XXX
|
||||||
|
def canvasIncrCanvasHeightWidth(self, event):
|
||||||
|
self.canvasIncrCanvasHeight(event)
|
||||||
|
self.canvasIncrCanvasWidth(event)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasIncrCanvasWidth(self, event): XXX
|
||||||
|
def canvasIncrCanvasWidth(self, event):
|
||||||
|
self.parentCanvas.resize([ \
|
||||||
|
self.parentCanvas.canvasSize[0]+1, \
|
||||||
|
self.parentCanvas.canvasSize[1]])
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasNew(self, event, newCanvasSize=None): XXX
|
||||||
|
def canvasNew(self, event, newCanvasSize=None):
|
||||||
|
if self.canvasPathName != None:
|
||||||
|
saveChanges = self._dialogSaveChanges()
|
||||||
|
if saveChanges == wx.ID_CANCEL:
|
||||||
|
return
|
||||||
|
elif saveChanges == wx.ID_NO:
|
||||||
|
pass
|
||||||
|
elif saveChanges == wx.ID_YES:
|
||||||
|
self.canvasSave(event)
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||||
|
if newCanvasSize == None:
|
||||||
|
newCanvasSize = list(self.parentCanvas.defaultCanvasSize)
|
||||||
|
self.parentCanvas.canvasImportStore.importNew(newCanvasSize)
|
||||||
|
self.canvasPathName = None
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||||
|
self.parentFrame.onCanvasUpdate(pathName="", undoLevel=-1)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasOpen(self, event): XXX
|
||||||
|
def canvasOpen(self, event):
|
||||||
|
if self.canvasPathName != None:
|
||||||
|
saveChanges = self._dialogSaveChanges()
|
||||||
|
if saveChanges == wx.ID_CANCEL:
|
||||||
|
return
|
||||||
|
elif saveChanges == wx.ID_NO:
|
||||||
|
pass
|
||||||
|
elif saveChanges == wx.ID_YES:
|
||||||
|
self.canvasSave(event)
|
||||||
|
with wx.FileDialog(self.parentCanvas, "Open", os.getcwd(), "", \
|
||||||
|
"*.txt", wx.FD_OPEN) as dialog:
|
||||||
|
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.canvasPathName = dialog.GetPath()
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||||
|
self.parentCanvas.canvasImportStore.importTextFile(self.canvasPathName)
|
||||||
|
self.parentCanvas.canvasImportStore.importIntoPanel()
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||||
|
self.parentFrame.onCanvasUpdate( \
|
||||||
|
pathName=self.canvasPathName, undoLevel=-1)
|
||||||
|
return True
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasPaste(self, event): XXX
|
||||||
|
def canvasPaste(self, event):
|
||||||
|
pass
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasRedo(self, event): XXX
|
||||||
|
def canvasRedo(self, event):
|
||||||
|
self.parentCanvas._dispatchDeltaPatches( \
|
||||||
|
self.parentCanvas.canvasJournal.popRedo())
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasSave(self, event): XXX
|
||||||
|
def canvasSave(self, event):
|
||||||
|
if self.canvasPathName == None:
|
||||||
|
if self.canvasSaveAs(event) == False:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with open(self.canvasPathName, "w") as outFile:
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.CURSOR_WAIT))
|
||||||
|
self.parentCanvas.canvasExportStore.exportTextFile( \
|
||||||
|
self.parentCanvas.canvasMap, \
|
||||||
|
self.parentCanvas.canvasSize, outFile)
|
||||||
|
self.parentCanvas.SetCursor(wx.Cursor(wx.NullCursor))
|
||||||
|
return True
|
||||||
|
except IOError as error:
|
||||||
|
return False
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasSaveAs(self, event): XXX
|
||||||
|
def canvasSaveAs(self, event):
|
||||||
|
with wx.FileDialog(self.parentCanvas, "Save As", os.getcwd(), "", \
|
||||||
|
"*.txt", wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dialog:
|
||||||
|
if dialog.ShowModal() == wx.ID_CANCEL:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.canvasPathName = dialog.GetPath()
|
||||||
|
return self.canvasSave(event)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasToolCircle(self, event): XXX
|
||||||
|
def canvasToolCircle(self, event):
|
||||||
|
self.canvasTool = MiRCARTToolCircle(self.parentCanvas)
|
||||||
|
self.parentFrame.menuItemsById[self.parentFrame.CID_CIRCLE[0]].Check(True)
|
||||||
|
toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_CIRCLE[0]].GetToolBar()
|
||||||
|
toolBar.ToggleTool(self.parentFrame.CID_CIRCLE[0], True)
|
||||||
|
self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasToolFill(self, event): XXX
|
||||||
|
def canvasToolFill(self, event):
|
||||||
|
self.canvasTool = MiRCARTToolFill(self.parentCanvas)
|
||||||
|
self.parentFrame.menuItemsById[self.parentFrame.CID_FILL[0]].Check(True)
|
||||||
|
toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_FILL[0]].GetToolBar()
|
||||||
|
toolBar.ToggleTool(self.parentFrame.CID_FILL[0], True)
|
||||||
|
self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasToolLine(self, event): XXX
|
||||||
|
def canvasToolLine(self, event):
|
||||||
|
self.canvasTool = MiRCARTToolLine(self.parentCanvas)
|
||||||
|
self.parentFrame.menuItemsById[self.parentFrame.CID_LINE[0]].Check(True)
|
||||||
|
toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_LINE[0]].GetToolBar()
|
||||||
|
toolBar.ToggleTool(self.parentFrame.CID_LINE[0], True)
|
||||||
|
self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasToolSelectClone(self, event): XXX
|
||||||
|
def canvasToolSelectClone(self, event):
|
||||||
|
self.canvasTool = MiRCARTToolSelectClone(self.parentCanvas)
|
||||||
|
self.parentFrame.menuItemsById[self.parentFrame.CID_CLONE_SELECT[0]].Check(True)
|
||||||
|
toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_CLONE_SELECT[0]].GetToolBar()
|
||||||
|
toolBar.ToggleTool(self.parentFrame.CID_CLONE_SELECT[0], True)
|
||||||
|
self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasToolSelectMove(self, event): XXX
|
||||||
|
def canvasToolSelectMove(self, event):
|
||||||
|
self.canvasTool = MiRCARTToolSelectMove(self.parentCanvas)
|
||||||
|
self.parentFrame.menuItemsById[self.parentFrame.CID_MOVE_SELECT[0]].Check(True)
|
||||||
|
toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_MOVE_SELECT[0]].GetToolBar()
|
||||||
|
toolBar.ToggleTool(self.parentFrame.CID_MOVE_SELECT[0], True)
|
||||||
|
self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasToolRect(self, event): XXX
|
||||||
|
def canvasToolRect(self, event):
|
||||||
|
self.canvasTool = MiRCARTToolRect(self.parentCanvas)
|
||||||
|
self.parentFrame.menuItemsById[self.parentFrame.CID_RECT[0]].Check(True)
|
||||||
|
toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_RECT[0]].GetToolBar()
|
||||||
|
toolBar.ToggleTool(self.parentFrame.CID_RECT[0], True)
|
||||||
|
self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasToolText(self, event): XXX
|
||||||
|
def canvasToolText(self, event):
|
||||||
|
self.canvasTool = MiRCARTToolText(self.parentCanvas)
|
||||||
|
self.parentFrame.menuItemsById[self.parentFrame.CID_TEXT[0]].Check(True)
|
||||||
|
toolBar = self.parentFrame.toolBarItemsById[self.parentFrame.CID_TEXT[0]].GetToolBar()
|
||||||
|
toolBar.ToggleTool(self.parentFrame.CID_TEXT[0], True)
|
||||||
|
self.parentFrame.onCanvasUpdate(toolName=self.canvasTool.name)
|
||||||
|
# }}}
|
||||||
|
# {{{ canvasUndo(self, event): XXX
|
||||||
|
def canvasUndo(self, event):
|
||||||
|
self.parentCanvas._dispatchDeltaPatches( \
|
||||||
|
self.parentCanvas.canvasJournal.popUndo())
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, parentCanvas, parentFrame):
|
||||||
|
def __init__(self, parentCanvas, parentFrame):
|
||||||
|
self.parentCanvas = parentCanvas; self.parentFrame = parentFrame;
|
||||||
|
self.canvasPathName = None
|
||||||
|
self.canvasToolRect(None)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
97
MiRCARTCanvasJournal.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTCanvasJournal.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
class MiRCARTCanvasJournal():
|
||||||
|
"""XXX"""
|
||||||
|
patchesCursor = patchesUndo = patchesUndoLevel = None
|
||||||
|
|
||||||
|
# {{{ popCursor(self): XXX
|
||||||
|
def popCursor(self):
|
||||||
|
if len(self.patchesCursor):
|
||||||
|
patchesCursor = self.patchesCursor
|
||||||
|
self.patchesCursor = []
|
||||||
|
return patchesCursor
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
# }}}
|
||||||
|
# {{{ popRedo(self): XXX
|
||||||
|
def popRedo(self):
|
||||||
|
if self.patchesUndoLevel > 0:
|
||||||
|
self.patchesUndoLevel -= 1
|
||||||
|
patches = self.patchesUndo[self.patchesUndoLevel]
|
||||||
|
return patches[1]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
# }}}
|
||||||
|
# {{{ popUndo(self): XXX
|
||||||
|
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): XXX
|
||||||
|
def pushCursor(self, patches):
|
||||||
|
self.patchesCursor.append(patches)
|
||||||
|
# }}}
|
||||||
|
# {{{ pushDeltas(self, undoPatches, redoPatches): XXX
|
||||||
|
def pushDeltas(self, undoPatches, redoPatches):
|
||||||
|
if self.patchesUndoLevel > 0:
|
||||||
|
del self.patchesUndo[0:self.patchesUndoLevel]
|
||||||
|
self.patchesUndoLevel = 0
|
||||||
|
deltaItem = [undoPatches, redoPatches]
|
||||||
|
self.patchesUndo.insert(0, deltaItem)
|
||||||
|
return deltaItem
|
||||||
|
# }}}
|
||||||
|
# {{{ resetCursor(self): XXX
|
||||||
|
def resetCursor(self):
|
||||||
|
if self.patchesCursor != None:
|
||||||
|
self.patchesCursor.clear()
|
||||||
|
self.patchesCursor = []
|
||||||
|
# }}}
|
||||||
|
# {{{ resetUndo(self): XXX
|
||||||
|
def resetUndo(self):
|
||||||
|
if self.patchesUndo != None:
|
||||||
|
self.patchesUndo.clear()
|
||||||
|
self.patchesUndo = [None]; self.patchesUndoLevel = 0;
|
||||||
|
# }}}
|
||||||
|
# {{{ updateCurrentDeltas(self, undoPatches, redoPatches): XXX
|
||||||
|
def updateCurrentDeltas(self, undoPatches, redoPatches):
|
||||||
|
self.patchesUndo[0][0].append(undoPatches)
|
||||||
|
self.patchesUndo[0][1].append(redoPatches)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ __del__(self): destructor method
|
||||||
|
def __del__(self):
|
||||||
|
self.resetCursor(); self.resetUndo();
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self): initialisation method
|
||||||
|
def __init__(self):
|
||||||
|
self.resetCursor(); self.resetUndo();
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
33
MiRCARTCheckLineLengths.sh
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# MiRCARTCheckLineLengths.py -- check mIRC art line lengths
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
for FNAME in "${@}"; do
|
||||||
|
FNAME_LINES="$(wc -l "${FNAME}" | awk '{print $1}')";
|
||||||
|
for FNAME_LINE in $(seq "${FNAME_LINES}"); do
|
||||||
|
printf "%-5d %-5d %s\n" \
|
||||||
|
"$(sed -n "${FNAME_LINE}p" "${FNAME}" | wc -c)" \
|
||||||
|
"${FNAME_LINE}" "${FNAME}";
|
||||||
|
done;
|
||||||
|
done | sort -nk1;
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
47
MiRCARTColours.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTColours.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# MiRCARTColours: mIRC colour number to RGBA map given none of ^[BFV_] (bold, italic, reverse, underline],
|
||||||
|
#
|
||||||
|
MiRCARTColours = [
|
||||||
|
[255, 255, 255, 255, "White"],
|
||||||
|
[0, 0, 0, 255, "Black"],
|
||||||
|
[0, 0, 187, 255, "Blue"],
|
||||||
|
[0, 187, 0, 255, "Green"],
|
||||||
|
[255, 85, 85, 255, "Light Red"],
|
||||||
|
[187, 0, 0, 255, "Red"],
|
||||||
|
[187, 0, 187, 255, "Purple"],
|
||||||
|
[187, 187, 0, 255, "Yellow"],
|
||||||
|
[255, 255, 85, 255, "Light Yellow"],
|
||||||
|
[85, 255, 85, 255, "Light Green"],
|
||||||
|
[0, 187, 187, 255, "Cyan"],
|
||||||
|
[85, 255, 255, 255, "Light Cyan"],
|
||||||
|
[85, 85, 255, 255, "Light Blue"],
|
||||||
|
[255, 85, 255, 255, "Pink"],
|
||||||
|
[85, 85, 85, 255, "Grey"],
|
||||||
|
[187, 187, 187, 255, "Light Grey"],
|
||||||
|
]
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
252
MiRCARTFrame.py
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTFrame.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTCanvas import MiRCARTCanvas, haveUrllib
|
||||||
|
from MiRCARTCanvasInterface import MiRCARTCanvasInterface
|
||||||
|
from MiRCARTColours import MiRCARTColours
|
||||||
|
from MiRCARTGeneralFrame import MiRCARTGeneralFrame, \
|
||||||
|
TID_ACCELS, TID_COMMAND, TID_LIST, TID_MENU, TID_NOTHING, TID_SELECT, TID_TOOLBAR, \
|
||||||
|
NID_MENU_SEP, NID_TOOLBAR_HSEP, NID_TOOLBAR_VSEP
|
||||||
|
|
||||||
|
import os, wx
|
||||||
|
|
||||||
|
class MiRCARTFrame(MiRCARTGeneralFrame):
|
||||||
|
"""XXX"""
|
||||||
|
panelCanvas = None; lastPanelState = {};
|
||||||
|
|
||||||
|
# {{{ Commands
|
||||||
|
# Id Type Id Labels Icon bitmap Accelerator [Initial state]
|
||||||
|
CID_NEW = [0x100, TID_COMMAND, "New", "&New", ["", wx.ART_NEW], [wx.ACCEL_CTRL, ord("N")], None, MiRCARTCanvasInterface.canvasNew]
|
||||||
|
CID_OPEN = [0x101, TID_COMMAND, "Open", "&Open", ["", wx.ART_FILE_OPEN], [wx.ACCEL_CTRL, ord("O")], None, MiRCARTCanvasInterface.canvasOpen]
|
||||||
|
CID_SAVE = [0x102, TID_COMMAND, "Save", "&Save", ["", wx.ART_FILE_SAVE], [wx.ACCEL_CTRL, ord("S")], None, MiRCARTCanvasInterface.canvasSave]
|
||||||
|
CID_SAVEAS = [0x103, TID_COMMAND, "Save As...", "Save &As...", ["", wx.ART_FILE_SAVE_AS], None, None, MiRCARTCanvasInterface.canvasSaveAs]
|
||||||
|
CID_EXPORT_AS_PNG = [0x104, TID_COMMAND, "Export as PNG...", \
|
||||||
|
"Export as PN&G...", None, None, None, MiRCARTCanvasInterface.canvasExportAsPng]
|
||||||
|
CID_EXPORT_IMGUR = [0x105, TID_COMMAND, "Export to Imgur...", \
|
||||||
|
"Export to I&mgur...", None, None, haveUrllib, MiRCARTCanvasInterface.canvasExportImgur]
|
||||||
|
CID_EXPORT_PASTEBIN = [0x106, TID_COMMAND, "Export to Pastebin...", \
|
||||||
|
"Export to Pasteb&in...", None, None, haveUrllib, MiRCARTCanvasInterface.canvasExportPastebin]
|
||||||
|
CID_EXIT = [0x107, TID_COMMAND, "Exit", "E&xit", None, [wx.ACCEL_CTRL, ord("X")], None, MiRCARTCanvasInterface.canvasExit]
|
||||||
|
CID_UNDO = [0x108, TID_COMMAND, "Undo", "&Undo", ["", wx.ART_UNDO], [wx.ACCEL_CTRL, ord("Z")], False, MiRCARTCanvasInterface.canvasUndo]
|
||||||
|
CID_REDO = [0x109, TID_COMMAND, "Redo", "&Redo", ["", wx.ART_REDO], [wx.ACCEL_CTRL, ord("Y")], False, MiRCARTCanvasInterface.canvasRedo]
|
||||||
|
CID_CUT = [0x10a, TID_COMMAND, "Cut", "Cu&t", ["", wx.ART_CUT], None, False, MiRCARTCanvasInterface.canvasCut]
|
||||||
|
CID_COPY = [0x10b, TID_COMMAND, "Copy", "&Copy", ["", wx.ART_COPY], None, False, MiRCARTCanvasInterface.canvasCopy]
|
||||||
|
CID_PASTE = [0x10c, TID_COMMAND, "Paste", "&Paste", ["", wx.ART_PASTE], None, False, MiRCARTCanvasInterface.canvasPaste]
|
||||||
|
CID_DELETE = [0x10d, TID_COMMAND, "Delete", "De&lete", ["", wx.ART_DELETE], None, False, MiRCARTCanvasInterface.canvasDelete]
|
||||||
|
CID_INCRW_CANVAS = [0x10e, TID_COMMAND, "Increase canvas width", \
|
||||||
|
"Increase canvas width", ["toolIncrCanvasW.png"], None, None, MiRCARTCanvasInterface.canvasIncrCanvasWidth]
|
||||||
|
CID_DECRW_CANVAS = [0x10f, TID_COMMAND, "Decrease canvas width", \
|
||||||
|
"Decrease canvas width", ["toolDecrCanvasW.png"], None, None, MiRCARTCanvasInterface.canvasDecrCanvasWidth]
|
||||||
|
CID_INCRH_CANVAS = [0x110, TID_COMMAND, "Increase canvas height", \
|
||||||
|
"Increase canvas height", ["toolIncrCanvasH.png"], None, None, MiRCARTCanvasInterface.canvasIncrCanvasHeight]
|
||||||
|
CID_DECRH_CANVAS = [0x111, TID_COMMAND, "Decrease canvas height", \
|
||||||
|
"Decrease canvas height", ["toolDecrCanvasH.png"], None, None, MiRCARTCanvasInterface.canvasDecrCanvasHeight]
|
||||||
|
CID_INCRHW_CANVAS = [0x112, TID_COMMAND, "Increase canvas size", \
|
||||||
|
"Increase canvas size", ["toolIncrCanvasHW.png"], None, None, MiRCARTCanvasInterface.canvasIncrCanvasHeightWidth]
|
||||||
|
CID_DECRHW_CANVAS = [0x113, TID_COMMAND, "Decrease canvas size", \
|
||||||
|
"Decrease canvas size", ["toolDecrCanvasHW.png"], None, None, MiRCARTCanvasInterface.canvasDecrCanvasHeightWidth]
|
||||||
|
CID_INCRW_BRUSH = [0x114, TID_COMMAND, "Increase brush width", \
|
||||||
|
"Increase brush width", ["toolIncrBrushW.png"], None, None, MiRCARTCanvasInterface.canvasIncrBrushWidth]
|
||||||
|
CID_DECRW_BRUSH = [0x115, TID_COMMAND, "Decrease brush width", \
|
||||||
|
"Decrease brush width", ["toolDecrBrushW.png"], None, None, MiRCARTCanvasInterface.canvasDecrBrushWidth]
|
||||||
|
CID_INCRH_BRUSH = [0x116, TID_COMMAND, "Increase brush height", \
|
||||||
|
"Increase brush height", ["toolIncrBrushH.png"], None, None, MiRCARTCanvasInterface.canvasIncrBrushHeight]
|
||||||
|
CID_DECRH_BRUSH = [0x117, TID_COMMAND, "Decrease brush height", \
|
||||||
|
"Decrease brush height", ["toolDecrBrushH.png"], None, None, MiRCARTCanvasInterface.canvasDecrBrushHeight]
|
||||||
|
CID_INCRHW_BRUSH = [0x118, TID_COMMAND, "Increase brush size", \
|
||||||
|
"Increase brush size", ["toolIncrBrushHW.png"], None, None, MiRCARTCanvasInterface.canvasIncrBrushHeightWidth]
|
||||||
|
CID_DECRHW_BRUSH = [0x119, TID_COMMAND, "Decrease brush size", \
|
||||||
|
"Decrease brush size", ["toolDecrBrushHW.png"], None, None, MiRCARTCanvasInterface.canvasDecrBrushHeightWidth]
|
||||||
|
CID_SOLID_BRUSH = [0x11a, TID_SELECT, "Solid brush", "Solid brush", None, None, True, MiRCARTCanvasInterface.canvasBrushSolid]
|
||||||
|
|
||||||
|
CID_RECT = [0x150, TID_SELECT, "Rectangle", "&Rectangle", ["toolRect.png"], [wx.ACCEL_CTRL, ord("R")], True, MiRCARTCanvasInterface.canvasToolRect]
|
||||||
|
CID_CIRCLE = [0x151, TID_SELECT, "Circle", "&Circle", ["toolCircle.png"], [wx.ACCEL_CTRL, ord("C")], False, MiRCARTCanvasInterface.canvasToolCircle]
|
||||||
|
CID_FILL = [0x152, TID_SELECT, "Fill", "&Fill", ["toolFill.png"], [wx.ACCEL_CTRL, ord("F")], False, MiRCARTCanvasInterface.canvasToolFill]
|
||||||
|
CID_LINE = [0x153, TID_SELECT, "Line", "&Line", ["toolLine.png"], [wx.ACCEL_CTRL, ord("L")], False, MiRCARTCanvasInterface.canvasToolLine]
|
||||||
|
CID_TEXT = [0x154, TID_SELECT, "Text", "&Text", ["toolText.png"], [wx.ACCEL_CTRL, ord("T")], False, MiRCARTCanvasInterface.canvasToolText]
|
||||||
|
CID_CLONE_SELECT = [0x155, TID_SELECT, "Clone", "Cl&one", ["toolClone.png"], [wx.ACCEL_CTRL, ord("E")], False, MiRCARTCanvasInterface.canvasToolSelectClone]
|
||||||
|
CID_MOVE_SELECT = [0x156, TID_SELECT, "Move", "&Move", ["toolMove.png"], [wx.ACCEL_CTRL, ord("M")], False, MiRCARTCanvasInterface.canvasToolSelectMove]
|
||||||
|
|
||||||
|
CID_COLOUR00 = [0x1a0, TID_SELECT, "Colour #00", "Colour #00", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR01 = [0x1a1, TID_SELECT, "Colour #01", "Colour #01", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR02 = [0x1a2, TID_SELECT, "Colour #02", "Colour #02", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR03 = [0x1a3, TID_SELECT, "Colour #03", "Colour #03", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR04 = [0x1a4, TID_SELECT, "Colour #04", "Colour #04", None, None, True, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR05 = [0x1a5, TID_SELECT, "Colour #05", "Colour #05", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR06 = [0x1a6, TID_SELECT, "Colour #06", "Colour #06", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR07 = [0x1a7, TID_SELECT, "Colour #07", "Colour #07", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR08 = [0x1a8, TID_SELECT, "Colour #08", "Colour #08", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR09 = [0x1a9, TID_SELECT, "Colour #09", "Colour #09", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR10 = [0x1aa, TID_SELECT, "Colour #10", "Colour #10", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR11 = [0x1ab, TID_SELECT, "Colour #11", "Colour #11", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR12 = [0x1ac, TID_SELECT, "Colour #12", "Colour #12", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR13 = [0x1ad, TID_SELECT, "Colour #13", "Colour #13", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR14 = [0x1ae, TID_SELECT, "Colour #14", "Colour #14", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
CID_COLOUR15 = [0x1af, TID_SELECT, "Colour #15", "Colour #15", None, None, False, MiRCARTCanvasInterface.canvasColour]
|
||||||
|
# }}}
|
||||||
|
# {{{ Menus
|
||||||
|
MID_FILE = (0x300, TID_MENU, "File", "&File", ( \
|
||||||
|
CID_NEW, CID_OPEN, CID_SAVE, CID_SAVEAS, NID_MENU_SEP, \
|
||||||
|
CID_EXPORT_AS_PNG, CID_EXPORT_IMGUR, CID_EXPORT_PASTEBIN, NID_MENU_SEP, \
|
||||||
|
CID_EXIT))
|
||||||
|
MID_EDIT = (0x301, TID_MENU, "Edit", "&Edit", ( \
|
||||||
|
CID_UNDO, CID_REDO, NID_MENU_SEP, \
|
||||||
|
CID_CUT, CID_COPY, CID_PASTE, CID_DELETE, NID_MENU_SEP, \
|
||||||
|
CID_INCRW_CANVAS, CID_DECRW_CANVAS, CID_INCRH_CANVAS, CID_DECRH_CANVAS, NID_MENU_SEP, \
|
||||||
|
CID_INCRHW_CANVAS, CID_DECRHW_CANVAS, NID_MENU_SEP, \
|
||||||
|
CID_INCRW_BRUSH, CID_DECRW_BRUSH, CID_INCRH_BRUSH, CID_DECRH_BRUSH, NID_MENU_SEP, \
|
||||||
|
CID_INCRHW_BRUSH, CID_DECRHW_BRUSH, NID_MENU_SEP, \
|
||||||
|
CID_SOLID_BRUSH))
|
||||||
|
MID_TOOLS = (0x302, TID_MENU, "Tools", "&Tools", ( \
|
||||||
|
CID_RECT, CID_CIRCLE, CID_FILL, CID_LINE, CID_TEXT, CID_CLONE_SELECT, CID_MOVE_SELECT))
|
||||||
|
# }}}
|
||||||
|
# {{{ Toolbars
|
||||||
|
BID_TOOLBAR = (0x400, TID_TOOLBAR, ( \
|
||||||
|
CID_NEW, CID_OPEN, CID_SAVE, CID_SAVEAS, NID_TOOLBAR_HSEP, \
|
||||||
|
CID_UNDO, CID_REDO, NID_TOOLBAR_HSEP, \
|
||||||
|
CID_CUT, CID_COPY, CID_PASTE, CID_DELETE, NID_TOOLBAR_HSEP, \
|
||||||
|
CID_INCRW_CANVAS, CID_DECRW_CANVAS, CID_INCRH_CANVAS, CID_DECRH_CANVAS, NID_TOOLBAR_HSEP, \
|
||||||
|
CID_INCRHW_CANVAS, CID_DECRHW_CANVAS, NID_TOOLBAR_HSEP, \
|
||||||
|
CID_RECT, CID_CIRCLE, CID_FILL, CID_LINE, CID_TEXT, CID_CLONE_SELECT, CID_MOVE_SELECT, \
|
||||||
|
NID_TOOLBAR_VSEP, \
|
||||||
|
CID_COLOUR00, CID_COLOUR01, CID_COLOUR02, CID_COLOUR03, CID_COLOUR04, \
|
||||||
|
CID_COLOUR05, CID_COLOUR06, CID_COLOUR07, CID_COLOUR08, CID_COLOUR09, \
|
||||||
|
CID_COLOUR10, CID_COLOUR11, CID_COLOUR12, CID_COLOUR13, CID_COLOUR14, \
|
||||||
|
CID_COLOUR15, NID_TOOLBAR_HSEP, \
|
||||||
|
CID_INCRW_BRUSH, CID_DECRW_BRUSH, CID_INCRH_BRUSH, CID_DECRH_BRUSH, NID_TOOLBAR_HSEP, \
|
||||||
|
CID_INCRHW_BRUSH, CID_DECRHW_BRUSH))
|
||||||
|
# }}}
|
||||||
|
# {{{ Accelerators (hotkeys)
|
||||||
|
AID_EDIT = (0x500, TID_ACCELS, ( \
|
||||||
|
CID_NEW, CID_OPEN, CID_SAVE, CID_EXIT, CID_UNDO, CID_REDO, \
|
||||||
|
CID_RECT, CID_CIRCLE, CID_FILL, CID_LINE, CID_TEXT, CID_CLONE_SELECT, CID_MOVE_SELECT))
|
||||||
|
# }}}
|
||||||
|
# {{{ Lists
|
||||||
|
LID_ACCELS = (0x600, TID_LIST, (AID_EDIT))
|
||||||
|
LID_MENUS = (0x601, TID_LIST, (MID_FILE, MID_EDIT, MID_TOOLS))
|
||||||
|
LID_TOOLBARS = (0x602, TID_LIST, (BID_TOOLBAR))
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ _initPaletteToolBitmaps(self): XXX
|
||||||
|
def _initPaletteToolBitmaps(self):
|
||||||
|
paletteDescr = ( \
|
||||||
|
self.CID_COLOUR00, self.CID_COLOUR01, self.CID_COLOUR02, self.CID_COLOUR03, self.CID_COLOUR04, \
|
||||||
|
self.CID_COLOUR05, self.CID_COLOUR06, self.CID_COLOUR07, self.CID_COLOUR08, self.CID_COLOUR09, \
|
||||||
|
self.CID_COLOUR10, self.CID_COLOUR11, self.CID_COLOUR12, self.CID_COLOUR13, self.CID_COLOUR14, \
|
||||||
|
self.CID_COLOUR15)
|
||||||
|
for numColour in range(len(paletteDescr)):
|
||||||
|
toolBitmapColour = MiRCARTColours[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)
|
||||||
|
paletteDescr[numColour][4] = ["", None, toolBitmap]
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ onInput(self, event): XXX
|
||||||
|
def onInput(self, event):
|
||||||
|
eventId = event.GetId()
|
||||||
|
if eventId >= self.CID_COLOUR00[0] \
|
||||||
|
and eventId <= self.CID_COLOUR15[0]:
|
||||||
|
numColour = eventId - self.CID_COLOUR00[0]
|
||||||
|
self.itemsById[eventId][7](self.panelCanvas.canvasInterface, event, numColour)
|
||||||
|
else:
|
||||||
|
self.itemsById[eventId][7](self.panelCanvas.canvasInterface, event)
|
||||||
|
# }}}
|
||||||
|
# {{{ onCanvasUpdate(self, newBrushSize=None, newCellPos=None, newColours=None, newPathName=None, newSize=None, newToolName=None, newUndoLevel=None): XXX
|
||||||
|
def onCanvasUpdate(self, **kwargs):
|
||||||
|
self.lastPanelState.update(kwargs)
|
||||||
|
textItems = []
|
||||||
|
if "cellPos" in self.lastPanelState:
|
||||||
|
textItems.append("X: {:03d} Y: {:03d}".format( \
|
||||||
|
*self.lastPanelState["cellPos"]))
|
||||||
|
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"]))
|
||||||
|
if "colours" in self.lastPanelState:
|
||||||
|
textItems.append("FG: {:02d}, BG: {:02d}".format( \
|
||||||
|
*self.lastPanelState["colours"]))
|
||||||
|
textItems.append("{} on {}".format( \
|
||||||
|
MiRCARTColours[self.lastPanelState["colours"][0]][4], \
|
||||||
|
MiRCARTColours[self.lastPanelState["colours"][1]][4]))
|
||||||
|
if "pathName" in self.lastPanelState:
|
||||||
|
if self.lastPanelState["pathName"] != "":
|
||||||
|
basePathName = os.path.basename(self.lastPanelState["pathName"])
|
||||||
|
textItems.append("Current file: {}".format(basePathName))
|
||||||
|
self.SetTitle("{} - MiRCART".format(basePathName))
|
||||||
|
else:
|
||||||
|
self.SetTitle("MiRCART")
|
||||||
|
if "toolName" in self.lastPanelState:
|
||||||
|
textItems.append("Current tool: {}".format( \
|
||||||
|
self.lastPanelState["toolName"]))
|
||||||
|
self.statusBar.SetStatusText(" | ".join(textItems))
|
||||||
|
if "undoLevel" in self.lastPanelState:
|
||||||
|
if self.lastPanelState["undoLevel"] >= 0:
|
||||||
|
self.menuItemsById[self.CID_UNDO[0]].Enable(True)
|
||||||
|
toolBar = self.toolBarItemsById[self.CID_UNDO[0]].GetToolBar()
|
||||||
|
toolBar.EnableTool(self.CID_UNDO[0], True)
|
||||||
|
else:
|
||||||
|
self.menuItemsById[self.CID_UNDO[0]].Enable(False)
|
||||||
|
toolBar = self.toolBarItemsById[self.CID_UNDO[0]].GetToolBar()
|
||||||
|
toolBar.EnableTool(self.CID_UNDO[0], False)
|
||||||
|
if self.lastPanelState["undoLevel"] > 0:
|
||||||
|
self.menuItemsById[self.CID_REDO[0]].Enable(True)
|
||||||
|
toolBar = self.toolBarItemsById[self.CID_REDO[0]].GetToolBar()
|
||||||
|
toolBar.EnableTool(self.CID_REDO[0], True)
|
||||||
|
else:
|
||||||
|
self.menuItemsById[self.CID_REDO[0]].Enable(False)
|
||||||
|
toolBar = self.toolBarItemsById[self.CID_REDO[0]].GetToolBar()
|
||||||
|
toolBar.EnableTool(self.CID_REDO[0], False)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ __del__(self): destructor method
|
||||||
|
def __del__(self):
|
||||||
|
if self.panelCanvas != None:
|
||||||
|
del self.panelCanvas; self.panelCanvas = None;
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, parent, appSize=(840, 630), defaultCanvasPos=(0, 75), defaultCanvasSize=(100, 30), defaultCellSize=(7, 14)): initialisation method
|
||||||
|
def __init__(self, parent, appSize=(840, 630), defaultCanvasPos=(0, 75), defaultCanvasSize=(100, 30), defaultCellSize=(7, 14)):
|
||||||
|
self._initPaletteToolBitmaps()
|
||||||
|
self.panelSkin = super().__init__(parent, wx.ID_ANY, "MiRCART", size=appSize)
|
||||||
|
self.panelCanvas = MiRCARTCanvas(self.panelSkin, parentFrame=self, \
|
||||||
|
defaultCanvasPos=defaultCanvasPos, \
|
||||||
|
defaultCanvasSize=defaultCanvasSize, \
|
||||||
|
defaultCellSize=defaultCellSize)
|
||||||
|
self.panelCanvas.canvasInterface.canvasNew(None)
|
||||||
|
self.sizerSkin.AddSpacer(5)
|
||||||
|
self.sizerSkin.Add(self.panelCanvas, 0, wx.ALL|wx.EXPAND, 14)
|
||||||
|
self.panelSkin.SetSizer(self.sizerSkin)
|
||||||
|
self.panelSkin.SetAutoLayout(1)
|
||||||
|
self.sizerSkin.Fit(self.panelSkin)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
187
MiRCARTGeneralFrame.py
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTGeneralFrame.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os, sys, wx
|
||||||
|
|
||||||
|
#
|
||||||
|
# Types
|
||||||
|
TID_ACCELS = (0x001)
|
||||||
|
TID_COMMAND = (0x002)
|
||||||
|
TID_LIST = (0x003)
|
||||||
|
TID_MENU = (0x004)
|
||||||
|
TID_NOTHING = (0x005)
|
||||||
|
TID_SELECT = (0x006)
|
||||||
|
TID_TOOLBAR = (0x007)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Non-items
|
||||||
|
NID_MENU_SEP = (0x200, TID_NOTHING)
|
||||||
|
NID_TOOLBAR_HSEP = (0x201, TID_NOTHING)
|
||||||
|
NID_TOOLBAR_VSEP = (0x202, TID_NOTHING)
|
||||||
|
|
||||||
|
class MiRCARTGeneralFrame(wx.Frame):
|
||||||
|
"""XXX"""
|
||||||
|
accelItemsById = itemsById = menuItemsById = toolBarItemsById = None
|
||||||
|
statusBar = toolBars = None
|
||||||
|
panelSkin = sizerSkin = None
|
||||||
|
|
||||||
|
# {{{ _initAccelTable(self, accelsDescr): XXX
|
||||||
|
def _initAccelTable(self, accelsDescr):
|
||||||
|
accelTableEntries = [wx.AcceleratorEntry() for n in range(len(accelsDescr[2]))]
|
||||||
|
self.accelItemsById = {}
|
||||||
|
for numAccel in range(len(accelsDescr[2])):
|
||||||
|
accelDescr = accelsDescr[2][numAccel]
|
||||||
|
if accelDescr[5] != None:
|
||||||
|
self.itemsById[accelDescr[0]] = accelDescr
|
||||||
|
accelTableEntries[numAccel].Set(*accelDescr[5], accelDescr[0])
|
||||||
|
self.accelItemsById[accelDescr[0]] = accelTableEntries[numAccel]
|
||||||
|
self.Bind(wx.EVT_MENU, self.onInput, id=accelDescr[0])
|
||||||
|
return accelTableEntries
|
||||||
|
# }}}
|
||||||
|
# {{{ _initMenus(self, menusDescr): XXX
|
||||||
|
def _initMenus(self, menusDescr):
|
||||||
|
self.menuItemsById = {}; menuBar = wx.MenuBar();
|
||||||
|
for menuDescr in menusDescr:
|
||||||
|
menuWindow = wx.Menu()
|
||||||
|
for menuItem in menuDescr[4]:
|
||||||
|
if menuItem == NID_MENU_SEP:
|
||||||
|
menuWindow.AppendSeparator()
|
||||||
|
elif menuItem[1] == TID_SELECT:
|
||||||
|
self.itemsById[menuItem[0]] = menuItem
|
||||||
|
menuItemWindow = menuWindow.AppendRadioItem(menuItem[0], menuItem[3], menuItem[2])
|
||||||
|
if menuItem[5] != None:
|
||||||
|
menuItemWindow.SetAccel(self.accelItemsById[menuItem[0]])
|
||||||
|
self.menuItemsById[menuItem[0]] = menuItemWindow
|
||||||
|
self.Bind(wx.EVT_MENU, self.onInput, menuItemWindow)
|
||||||
|
if menuItem[6] != None:
|
||||||
|
menuItemWindow.Check(menuItem[6])
|
||||||
|
else:
|
||||||
|
self.itemsById[menuItem[0]] = menuItem
|
||||||
|
menuItemWindow = menuWindow.Append(menuItem[0], menuItem[3], menuItem[2])
|
||||||
|
if menuItem[5] != None:
|
||||||
|
menuItemWindow.SetAccel(self.accelItemsById[menuItem[0]])
|
||||||
|
self.menuItemsById[menuItem[0]] = menuItemWindow
|
||||||
|
self.Bind(wx.EVT_MENU, self.onInput, menuItemWindow)
|
||||||
|
if menuItem[6] != None:
|
||||||
|
menuItemWindow.Enable(menuItem[6])
|
||||||
|
menuBar.Append(menuWindow, menuDescr[3])
|
||||||
|
return menuBar
|
||||||
|
# }}}
|
||||||
|
# {{{ _initToolBars(self, toolBarsDescr, panelSkin): XXX
|
||||||
|
def _initToolBars(self, toolBarsDescr, panelSkin):
|
||||||
|
self.toolBarItemsById = {}
|
||||||
|
self.sizerSkin = wx.BoxSizer(wx.VERTICAL)
|
||||||
|
self.toolBars = [None]; numToolBar = 0;
|
||||||
|
for toolBarItem in toolBarsDescr[2]:
|
||||||
|
if self.toolBars[numToolBar] == None:
|
||||||
|
self.toolBars[numToolBar] = \
|
||||||
|
wx.ToolBar(panelSkin, -1, \
|
||||||
|
style=wx.HORIZONTAL|wx.TB_FLAT|wx.TB_NODIVIDER)
|
||||||
|
self.toolBars[numToolBar].SetToolBitmapSize((16,16))
|
||||||
|
if toolBarItem == NID_TOOLBAR_HSEP:
|
||||||
|
self.toolBars[numToolBar].AddSeparator()
|
||||||
|
elif toolBarItem == NID_TOOLBAR_VSEP:
|
||||||
|
numToolBar += 1; self.toolBars.append(None);
|
||||||
|
elif toolBarItem[1] == TID_SELECT:
|
||||||
|
self.itemsById[toolBarItem[0]] = toolBarItem
|
||||||
|
toolBarItemWindow = \
|
||||||
|
self.toolBars[numToolBar].AddRadioTool( \
|
||||||
|
toolBarItem[0], toolBarItem[2], toolBarItem[4][2])
|
||||||
|
self.toolBarItemsById[toolBarItem[0]] = toolBarItemWindow
|
||||||
|
if toolBarItem[6] != None:
|
||||||
|
toolBarItemWindow.Toggle(toolBarItem[6])
|
||||||
|
self.Bind(wx.EVT_TOOL, self.onInput, toolBarItemWindow)
|
||||||
|
self.Bind(wx.EVT_TOOL_RCLICKED, self.onInput, toolBarItemWindow)
|
||||||
|
else:
|
||||||
|
self.itemsById[toolBarItem[0]] = toolBarItem
|
||||||
|
toolBarItemWindow = \
|
||||||
|
self.toolBars[numToolBar].AddTool( \
|
||||||
|
toolBarItem[0], toolBarItem[2], toolBarItem[4][2])
|
||||||
|
self.toolBarItemsById[toolBarItem[0]] = toolBarItemWindow
|
||||||
|
if toolBarItem[6] != None:
|
||||||
|
toolBarItemWindow.Enable(toolBarItem[6])
|
||||||
|
self.Bind(wx.EVT_TOOL, self.onInput, toolBarItemWindow)
|
||||||
|
self.Bind(wx.EVT_TOOL_RCLICKED, self.onInput, toolBarItemWindow)
|
||||||
|
for numToolBar in range(len(self.toolBars)):
|
||||||
|
self.sizerSkin.Add( \
|
||||||
|
self.toolBars[numToolBar], 0, wx.ALL|wx.ALIGN_LEFT, 3)
|
||||||
|
self.toolBars[numToolBar].Realize()
|
||||||
|
self.toolBars[numToolBar].Fit()
|
||||||
|
# }}}
|
||||||
|
# {{{ _initToolBitmaps(self, toolBarsDescr): XXX
|
||||||
|
def _initToolBitmaps(self, toolBarsDescr):
|
||||||
|
for toolBarItem in toolBarsDescr[2]:
|
||||||
|
if toolBarItem == NID_TOOLBAR_HSEP \
|
||||||
|
or toolBarItem == NID_TOOLBAR_VSEP:
|
||||||
|
continue
|
||||||
|
elif toolBarItem[4] == None:
|
||||||
|
toolBarItem[4] = ["", None, wx.ArtProvider.GetBitmap( \
|
||||||
|
wx.ART_HELP, wx.ART_TOOLBAR, (16,16))]
|
||||||
|
elif toolBarItem[4][0] == "" \
|
||||||
|
and toolBarItem[4][1] != None:
|
||||||
|
toolBarItem[4] = ["", None, wx.ArtProvider.GetBitmap( \
|
||||||
|
toolBarItem[4][1], wx.ART_TOOLBAR, (16,16))]
|
||||||
|
elif toolBarItem[4][0] == "" \
|
||||||
|
and toolBarItem[4][1] == None:
|
||||||
|
toolBarItem[4] = ["", None, toolBarItem[4][2]]
|
||||||
|
elif toolBarItem[4][0] != "":
|
||||||
|
toolBitmapPathName = os.path.dirname(sys.argv[0])
|
||||||
|
toolBitmapPathName = os.path.join(toolBitmapPathName, \
|
||||||
|
"assets", toolBarItem[4][0])
|
||||||
|
toolBitmap = wx.Bitmap((16,16))
|
||||||
|
toolBitmap.LoadFile(toolBitmapPathName, wx.BITMAP_TYPE_ANY)
|
||||||
|
toolBarItem[4] = ["", None, toolBitmap]
|
||||||
|
# }}}
|
||||||
|
# {{{ onInput(self, event): XXX
|
||||||
|
def onInput(self, event):
|
||||||
|
pass
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, *args, **kwargs): initialisation method
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs); self.itemsById = {};
|
||||||
|
panelSkin = wx.Panel(self, wx.ID_ANY)
|
||||||
|
|
||||||
|
# Initialise accelerators (hotkeys)
|
||||||
|
accelTable = wx.AcceleratorTable( \
|
||||||
|
self._initAccelTable(self.LID_ACCELS[2]))
|
||||||
|
self.SetAcceleratorTable(accelTable)
|
||||||
|
|
||||||
|
# Initialise menu bar, menus & menu items
|
||||||
|
# Initialise toolbar & toolbar items
|
||||||
|
menuBar = self._initMenus(self.LID_MENUS[2])
|
||||||
|
self.SetMenuBar(menuBar)
|
||||||
|
self._initToolBitmaps(self.LID_TOOLBARS[2])
|
||||||
|
toolBar = self._initToolBars(self.LID_TOOLBARS[2], panelSkin)
|
||||||
|
|
||||||
|
# Initialise status bar
|
||||||
|
self.statusBar = self.CreateStatusBar()
|
||||||
|
|
||||||
|
# Set focus on & show window
|
||||||
|
self.SetFocus(); self.Show(True);
|
||||||
|
|
||||||
|
return panelSkin
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
28
MiRCARTImgurApiKey.py.template
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTImgurApiKey.py -- melp?
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
class MiRCARTImgurApiKey(object):
|
||||||
|
imgurApiKey = None
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
156
MiRCARTToPngFile.py
Executable file
@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToPngFile.py -- convert ASCII w/ mIRC control codes to monospaced PNG (for EFnet #MiRCART)
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
import MiRCARTCanvasImportStore
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
import sys
|
||||||
|
|
||||||
|
class MiRCARTToPngFile:
|
||||||
|
"""XXX"""
|
||||||
|
inFile = inFromTextFile = None
|
||||||
|
outFontFilePath = outFontSize = None
|
||||||
|
|
||||||
|
# {{{ _ColourMapBold: mIRC colour number to RGBA map given ^B (bold)
|
||||||
|
_ColourMapBold = [
|
||||||
|
[255, 255, 255], # White
|
||||||
|
[85, 85, 85], # Grey
|
||||||
|
[85, 85, 255], # Light Blue
|
||||||
|
[85, 255, 85], # Light Green
|
||||||
|
[255, 85, 85], # Light Red
|
||||||
|
[255, 85, 85], # Light Red
|
||||||
|
[255, 85, 255], # Pink
|
||||||
|
[255, 255, 85], # Light Yellow
|
||||||
|
[255, 255, 85], # Light Yellow
|
||||||
|
[85, 255, 85], # Light Green
|
||||||
|
[85, 255, 255], # Light Cyan
|
||||||
|
[85, 255, 255], # Light Cyan
|
||||||
|
[85, 85, 255], # Light Blue
|
||||||
|
[255, 85, 255], # Pink
|
||||||
|
[85, 85, 85], # Grey
|
||||||
|
[255, 255, 255], # White
|
||||||
|
]
|
||||||
|
# }}}
|
||||||
|
# {{{ _ColourMapNormal: mIRC colour number to RGBA map given none of ^[BFV_] (bold, italic, reverse, underline)
|
||||||
|
_ColourMapNormal = [
|
||||||
|
[255, 255, 255], # White
|
||||||
|
[0, 0, 0], # Black
|
||||||
|
[0, 0, 187], # Blue
|
||||||
|
[0, 187, 0], # Green
|
||||||
|
[255, 85, 85], # Light Red
|
||||||
|
[187, 0, 0], # Red
|
||||||
|
[187, 0, 187], # Purple
|
||||||
|
[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], # Light Blue
|
||||||
|
[255, 85, 255], # Pink
|
||||||
|
[85, 85, 85], # Grey
|
||||||
|
[187, 187, 187], # Light Grey
|
||||||
|
]
|
||||||
|
# }}}
|
||||||
|
# {{{ _drawUnderline(self, curPos, fontSize, imgDraw, fillColour): XXX
|
||||||
|
def _drawUnderLine(self, curPos, fontSize, imgDraw, fillColour):
|
||||||
|
imgDraw.line( \
|
||||||
|
xy=(curPos[0], curPos[1] + (fontSize[1] - 2), \
|
||||||
|
curPos[0] + fontSize[0], curPos[1] + (fontSize[1] - 2)), \
|
||||||
|
fill=fillColour)
|
||||||
|
# }}}
|
||||||
|
# {{{ export(self, outFilePath): XXX
|
||||||
|
def export(self, outFilePath):
|
||||||
|
inSize = (len(self.inCanvasMap[0]), len(self.inCanvasMap))
|
||||||
|
outSize = [a*b for a,b in zip(inSize, self.outImgFontSize)]
|
||||||
|
outCurPos = [0, 0]
|
||||||
|
outImg = Image.new("RGBA", outSize, (*self._ColourMapNormal[1], 255))
|
||||||
|
outImgDraw = ImageDraw.Draw(outImg)
|
||||||
|
for inCurRow in range(len(self.inCanvasMap)):
|
||||||
|
for inCurCol in range(len(self.inCanvasMap[inCurRow])):
|
||||||
|
inCurCell = self.inCanvasMap[inCurRow][inCurCol]
|
||||||
|
outColours = [0, 0]
|
||||||
|
if inCurCell[2] & MiRCARTCanvasImportStore.MiRCARTCanvasImportStore._CellState.CS_BOLD:
|
||||||
|
if inCurCell[3] != " ":
|
||||||
|
if inCurCell[3] == "█":
|
||||||
|
outColours[1] = self._ColourMapNormal[inCurCell[0]]
|
||||||
|
else:
|
||||||
|
outColours[0] = self._ColourMapBold[inCurCell[0]]
|
||||||
|
outColours[1] = self._ColourMapNormal[inCurCell[1]]
|
||||||
|
else:
|
||||||
|
outColours[1] = self._ColourMapNormal[inCurCell[1]]
|
||||||
|
else:
|
||||||
|
if inCurCell[3] != " ":
|
||||||
|
if inCurCell[3] == "█":
|
||||||
|
outColours[1] = self._ColourMapNormal[inCurCell[0]]
|
||||||
|
else:
|
||||||
|
outColours[0] = self._ColourMapNormal[inCurCell[0]]
|
||||||
|
outColours[1] = self._ColourMapNormal[inCurCell[1]]
|
||||||
|
else:
|
||||||
|
outColours[1] = self._ColourMapNormal[inCurCell[1]]
|
||||||
|
outImgDraw.rectangle((*outCurPos, \
|
||||||
|
outCurPos[0] + self.outImgFontSize[0], \
|
||||||
|
outCurPos[1] + self.outImgFontSize[1]), \
|
||||||
|
fill=(*outColours[1], 255))
|
||||||
|
if not inCurCell[3] in " █" \
|
||||||
|
and outColours[0] != outColours[1]:
|
||||||
|
# XXX implement italic
|
||||||
|
outImgDraw.text(outCurPos, \
|
||||||
|
inCurCell[3], (*outColours[0], 255), self.outImgFont)
|
||||||
|
if inCurCell[2] & MiRCARTCanvasImportStore.MiRCARTCanvasImportStore._CellState.CS_UNDERLINE:
|
||||||
|
outColours[0] = self._ColourMapNormal[inCurCell[0]]
|
||||||
|
self._drawUnderLine(outCurPos, \
|
||||||
|
self.outImgFontSize, \
|
||||||
|
outImgDraw, (*outColours[0], 255))
|
||||||
|
outCurPos[0] += self.outImgFontSize[0];
|
||||||
|
outCurPos[0] = 0
|
||||||
|
outCurPos[1] += self.outImgFontSize[1]
|
||||||
|
outImg.save(outFilePath);
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, inCanvasMap, fontFilePath="DejaVuSansMono.ttf", fontSize=11): initialisation method
|
||||||
|
def __init__(self, inCanvasMap, fontFilePath="DejaVuSansMono.ttf", fontSize=11):
|
||||||
|
self.inCanvasMap = inCanvasMap
|
||||||
|
self.outFontFilePath = fontFilePath; self.outFontSize = int(fontSize);
|
||||||
|
self.outImgFont = ImageFont.truetype( \
|
||||||
|
self.outFontFilePath, self.outFontSize)
|
||||||
|
self.outImgFontSize = [*self.outImgFont.getsize(" ")]
|
||||||
|
self.outImgFontSize[1] += 3
|
||||||
|
|
||||||
|
#
|
||||||
|
# Entry point
|
||||||
|
def main(*argv):
|
||||||
|
canvasStore = MiRCARTCanvasImportStore.MiRCARTCanvasImportStore(inFile=argv[1])
|
||||||
|
MiRCARTToPngFile(canvasStore.outMap, *argv[3:]).export(argv[2])
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if ((len(sys.argv) - 1) < 2)\
|
||||||
|
or ((len(sys.argv) - 1) > 4):
|
||||||
|
print("usage: {} " \
|
||||||
|
"<MiRCART input file pathname> " \
|
||||||
|
"<PNG image output file pathname> " \
|
||||||
|
"[<Font file pathname; defaults to DejaVuSansMono.ttf>] " \
|
||||||
|
"[<Font size; defaults to 11>]".format(sys.argv[0]), file=sys.stderr)
|
||||||
|
else:
|
||||||
|
main(*sys.argv)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
28
MiRCARTToPngFiles.sh
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
#
|
||||||
|
# MiRCARTToPngFiles.sh -- convert ASCII(s) w/ mIRC control codes to monospaced PNG(s) (for EFnet #MiRCART)
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
for FNAME in "${@}"; do
|
||||||
|
./MiRCARTToPngFile.py "${FNAME}" "${FNAME%.txt}.png";
|
||||||
|
done;
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
43
MiRCARTTool.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTTool.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
class MiRCARTTool():
|
||||||
|
"""XXX"""
|
||||||
|
parentCanvas = None
|
||||||
|
|
||||||
|
# {{{ onKeyboardEvent(self, event, atPoint, brushColours, brushSize, keyChar, dispatchFn, eventDc):
|
||||||
|
def onKeyboardEvent(self, event, atPoint, brushColours, brushSize, keyChar, dispatchFn, eventDc):
|
||||||
|
return True
|
||||||
|
# }}}
|
||||||
|
# {{{ onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
return ()
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# __init__(self, parentCanvas): initialisation method
|
||||||
|
def __init__(self, parentCanvas):
|
||||||
|
self.parentCanvas = parentCanvas
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
56
MiRCARTToolCircle.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToolCircle.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTTool import MiRCARTTool
|
||||||
|
|
||||||
|
class MiRCARTToolCircle(MiRCARTTool):
|
||||||
|
"""XXX"""
|
||||||
|
name = "Circle"
|
||||||
|
|
||||||
|
#
|
||||||
|
# onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
brushColours = brushColours.copy()
|
||||||
|
if isLeftDown:
|
||||||
|
brushColours[1] = brushColours[0]
|
||||||
|
elif isRightDown:
|
||||||
|
brushColours[0] = brushColours[1]
|
||||||
|
else:
|
||||||
|
brushColours[1] = brushColours[0]
|
||||||
|
_brushSize = brushSize[0]*2
|
||||||
|
originPoint = (_brushSize/2, _brushSize/2)
|
||||||
|
radius = _brushSize
|
||||||
|
for brushY in range(-radius, radius + 1):
|
||||||
|
for brushX in range(-radius, radius + 1):
|
||||||
|
if ((brushX**2)+(brushY**2) < (((radius**2)+radius)*0.8)):
|
||||||
|
patch = [ \
|
||||||
|
atPoint[0] + int(originPoint[0]+brushX), \
|
||||||
|
atPoint[1] + int(originPoint[1]+brushY), \
|
||||||
|
*brushColours, 0, " "]
|
||||||
|
if isLeftDown or isRightDown:
|
||||||
|
dispatchFn(eventDc, False, patch); dispatchFn(eventDc, True, patch);
|
||||||
|
else:
|
||||||
|
dispatchFn(eventDc, True, patch)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
56
MiRCARTToolFill.py
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToolFill.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTTool import MiRCARTTool
|
||||||
|
|
||||||
|
class MiRCARTToolFill(MiRCARTTool):
|
||||||
|
"""XXX"""
|
||||||
|
name = "Fill"
|
||||||
|
|
||||||
|
#
|
||||||
|
# onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
pointStack = [list(atPoint)]; pointsDone = [];
|
||||||
|
testColour = self.parentCanvas.canvasMap[atPoint[1]][atPoint[0]][0:2]
|
||||||
|
if isLeftDown or isRightDown:
|
||||||
|
if isRightDown:
|
||||||
|
brushColours = [brushColours[1], brushColours[0]]
|
||||||
|
while len(pointStack) > 0:
|
||||||
|
point = pointStack.pop()
|
||||||
|
pointCell = self.parentCanvas.canvasMap[point[1]][point[0]]
|
||||||
|
if pointCell[0:2] == testColour:
|
||||||
|
if not point in pointsDone:
|
||||||
|
dispatchFn(eventDc, False, [*point, \
|
||||||
|
brushColours[0], brushColours[0], 0, " "])
|
||||||
|
if point[0] > 0:
|
||||||
|
pointStack.append([point[0] - 1, point[1]])
|
||||||
|
if point[0] < (self.parentCanvas.canvasSize[0] - 1):
|
||||||
|
pointStack.append([point[0] + 1, point[1]])
|
||||||
|
if point[1] > 0:
|
||||||
|
pointStack.append([point[0], point[1] - 1])
|
||||||
|
if point[1] < (self.parentCanvas.canvasSize[1] - 1):
|
||||||
|
pointStack.append([point[0], point[1] + 1])
|
||||||
|
pointsDone += [point]
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
105
MiRCARTToolLine.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToolLine.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTTool import MiRCARTTool
|
||||||
|
|
||||||
|
class MiRCARTToolLine(MiRCARTTool):
|
||||||
|
"""XXX"""
|
||||||
|
name = "Line"
|
||||||
|
toolColours = toolOriginPoint = toolState = None
|
||||||
|
|
||||||
|
TS_NONE = 0
|
||||||
|
TS_ORIGIN = 1
|
||||||
|
|
||||||
|
# {{{ _pointDelta(self, a, b): XXX
|
||||||
|
def _pointDelta(self, a, b):
|
||||||
|
return [a2-a1 for a1, a2 in zip(a, b)]
|
||||||
|
# }}}
|
||||||
|
# {{{ _pointSwap(self, a, b): XXX
|
||||||
|
def _pointSwap(self, a, b):
|
||||||
|
return [b, a]
|
||||||
|
# }}}
|
||||||
|
# {{{ _getLine(self, brushColours, brushSize, eventDc, isCursor, originPoint, targetPoint, dispatchFn): XXX
|
||||||
|
def _getLine(self, brushColours, brushSize, eventDc, isCursor, originPoint, targetPoint, dispatchFn):
|
||||||
|
originPoint = originPoint.copy(); targetPoint = 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]
|
||||||
|
if pointDelta[0] > pointDelta[1]:
|
||||||
|
lineXX, lineXY, lineYX, lineYY = lineXSign, 0, 0, lineYSign
|
||||||
|
else:
|
||||||
|
pointDelta = [pointDelta[1], pointDelta[0]]
|
||||||
|
lineXX, lineXY, lineYX, lineYY = 0, lineYSign, lineXSign, 0
|
||||||
|
lineD = 2 * pointDelta[1] - pointDelta[0]; lineY = 0;
|
||||||
|
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); dispatchFn(eventDc, True, patch);
|
||||||
|
else:
|
||||||
|
dispatchFn(eventDc, True, patch)
|
||||||
|
if lineD > 0:
|
||||||
|
lineD -= pointDelta[0]; lineY += 1;
|
||||||
|
lineD += pointDelta[1]
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
brushColours = brushColours.copy()
|
||||||
|
if isLeftDown:
|
||||||
|
brushColours[1] = brushColours[0]
|
||||||
|
elif isRightDown:
|
||||||
|
brushColours[0] = brushColours[1]
|
||||||
|
else:
|
||||||
|
brushColours[1] = brushColours[0]
|
||||||
|
if self.toolState == self.TS_NONE:
|
||||||
|
if isLeftDown or isRightDown:
|
||||||
|
self.toolColours = brushColours
|
||||||
|
self.toolOriginPoint = list(atPoint)
|
||||||
|
self.toolState = self.TS_ORIGIN
|
||||||
|
dispatchFn(eventDc, True, [*atPoint, *brushColours, 0, " "])
|
||||||
|
elif self.toolState == self.TS_ORIGIN:
|
||||||
|
targetPoint = list(atPoint)
|
||||||
|
originPoint = self.toolOriginPoint
|
||||||
|
self._getLine(self.toolColours, brushSize, \
|
||||||
|
eventDc, isLeftDown or isRightDown, \
|
||||||
|
originPoint, targetPoint, dispatchFn)
|
||||||
|
if isLeftDown or isRightDown:
|
||||||
|
self.toolColours = None
|
||||||
|
self.toolOriginPoint = None
|
||||||
|
self.toolState = self.TS_NONE
|
||||||
|
|
||||||
|
# __init__(self, *args): initialisation method
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.toolColours = None
|
||||||
|
self.toolOriginPoint = None
|
||||||
|
self.toolState = self.TS_NONE
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
52
MiRCARTToolRect.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToolRect.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTTool import MiRCARTTool
|
||||||
|
|
||||||
|
class MiRCARTToolRect(MiRCARTTool):
|
||||||
|
"""XXX"""
|
||||||
|
name = "Rectangle"
|
||||||
|
|
||||||
|
#
|
||||||
|
# onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
brushColours = brushColours.copy()
|
||||||
|
if isLeftDown:
|
||||||
|
brushColours[1] = brushColours[0]
|
||||||
|
elif isRightDown:
|
||||||
|
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 = [atPoint[0] + brushCol, atPoint[1] + brushRow, *brushColours, 0, " "]
|
||||||
|
if isLeftDown or isRightDown:
|
||||||
|
dispatchFn(eventDc, False, patch); dispatchFn(eventDc, True, patch);
|
||||||
|
else:
|
||||||
|
dispatchFn(eventDc, True, patch)
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
129
MiRCARTToolSelect.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToolSelect.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTTool import MiRCARTTool
|
||||||
|
|
||||||
|
class MiRCARTToolSelect(MiRCARTTool):
|
||||||
|
"""XXX"""
|
||||||
|
toolColours = toolRect = toolState = None
|
||||||
|
toolLastAtPoint = None
|
||||||
|
toolSelectMap = None
|
||||||
|
srcRect = None
|
||||||
|
|
||||||
|
TS_NONE = 0
|
||||||
|
TS_ORIGIN = 1
|
||||||
|
TS_TARGET = 2
|
||||||
|
|
||||||
|
# {{{ _drawSelectRect(self, rect, dispatchFn, eventDc): XXX
|
||||||
|
def _drawSelectRect(self, rect, dispatchFn, eventDc):
|
||||||
|
rectFrame = [ \
|
||||||
|
[rect[0][0]-1, rect[0][1]-1], \
|
||||||
|
[rect[1][0]+1, rect[1][1]+1]]
|
||||||
|
if rectFrame[0][0] > rectFrame[1][0]:
|
||||||
|
rectFrame[0][0], rectFrame[1][0] = \
|
||||||
|
rectFrame[1][0], rectFrame[0][0]
|
||||||
|
if rectFrame[0][1] > rectFrame[1][1]:
|
||||||
|
rectFrame[0][1], rectFrame[1][1] = \
|
||||||
|
rectFrame[1][1], rectFrame[0][1]
|
||||||
|
curColours = [0, 0]
|
||||||
|
for rectX in range(rectFrame[0][0], rectFrame[1][0]+1):
|
||||||
|
if curColours == [0, 0]:
|
||||||
|
curColours = [1, 1]
|
||||||
|
else:
|
||||||
|
curColours = [0, 0]
|
||||||
|
dispatchFn(eventDc, True, \
|
||||||
|
[rectX, rectFrame[0][1], *curColours, 0, " "])
|
||||||
|
dispatchFn(eventDc, True, \
|
||||||
|
[rectX, rectFrame[1][1], *curColours, 0, " "])
|
||||||
|
for rectY in range(rectFrame[0][1], rectFrame[1][1]+1):
|
||||||
|
if curColours == [0, 0]:
|
||||||
|
curColours = [1, 1]
|
||||||
|
else:
|
||||||
|
curColours = [0, 0]
|
||||||
|
dispatchFn(eventDc, True, \
|
||||||
|
[rectFrame[0][0], rectY, *curColours, 0, " "])
|
||||||
|
dispatchFn(eventDc, True, \
|
||||||
|
[rectFrame[1][0], rectY, *curColours, 0, " "])
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
#
|
||||||
|
# onSelectEvent(self, event, atPoint, selectRect, brushColours, brushSize, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onSelectEvent(self, event, atPoint, selectRect, brushColours, brushSize, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
#
|
||||||
|
# onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
if self.toolState == self.TS_NONE:
|
||||||
|
if isLeftDown or isRightDown:
|
||||||
|
self.toolColours = [0, 1]
|
||||||
|
self.toolRect = [list(atPoint), []]
|
||||||
|
self.toolState = self.TS_ORIGIN
|
||||||
|
else:
|
||||||
|
dispatchFn(eventDc, True, \
|
||||||
|
[*atPoint, *brushColours, 0, " "])
|
||||||
|
elif self.toolState == self.TS_ORIGIN:
|
||||||
|
self.toolRect[1] = list(atPoint)
|
||||||
|
if isLeftDown or isRightDown:
|
||||||
|
if self.toolRect[0][0] > self.toolRect[1][0]:
|
||||||
|
self.toolRect[0][0], self.toolRect[1][0] = \
|
||||||
|
self.toolRect[1][0], self.toolRect[0][0]
|
||||||
|
if self.toolRect[0][1] > self.toolRect[1][1]:
|
||||||
|
self.toolRect[0][1], self.toolRect[1][1] = \
|
||||||
|
self.toolRect[1][1], self.toolRect[0][1]
|
||||||
|
self.srcRect = self.toolRect[0]
|
||||||
|
self.toolLastAtPoint = list(atPoint)
|
||||||
|
self.toolState = self.TS_TARGET
|
||||||
|
self.toolSelectMap = []
|
||||||
|
for numRow in range((self.toolRect[1][1] - self.toolRect[0][1]) + 1):
|
||||||
|
self.toolSelectMap.append([])
|
||||||
|
for numCol in range((self.toolRect[1][0] - self.toolRect[0][0]) + 1):
|
||||||
|
rectY = self.toolRect[0][1] + numRow
|
||||||
|
rectX = self.toolRect[0][0] + numCol
|
||||||
|
self.toolSelectMap[numRow].append( \
|
||||||
|
self.parentCanvas.canvasMap[rectY][rectX])
|
||||||
|
self._drawSelectRect(self.toolRect, dispatchFn, eventDc)
|
||||||
|
elif self.toolState == self.TS_TARGET:
|
||||||
|
if isRightDown:
|
||||||
|
self.onSelectEvent(event, atPoint, self.toolRect, \
|
||||||
|
brushColours, brushSize, isLeftDown, isRightDown, \
|
||||||
|
dispatchFn, eventDc)
|
||||||
|
self.toolColours = None
|
||||||
|
self.toolRect = None
|
||||||
|
self.toolState = self.TS_NONE
|
||||||
|
else:
|
||||||
|
self.onSelectEvent(event, atPoint, self.toolRect, \
|
||||||
|
brushColours, brushSize, isLeftDown, isRightDown, \
|
||||||
|
dispatchFn, eventDc)
|
||||||
|
|
||||||
|
# __init__(self, *args): initialisation method
|
||||||
|
def __init__(self, *args):
|
||||||
|
super().__init__(*args)
|
||||||
|
self.toolColours = None
|
||||||
|
self.toolRect = None
|
||||||
|
self.toolState = self.TS_NONE
|
||||||
|
self.toolLastAtPoint = None
|
||||||
|
self.toolSelectMap = None
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
60
MiRCARTToolSelectClone.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToolSelectClone.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTToolSelect import MiRCARTToolSelect
|
||||||
|
|
||||||
|
class MiRCARTToolSelectClone(MiRCARTToolSelect):
|
||||||
|
"""XXX"""
|
||||||
|
name = "Clone selection"
|
||||||
|
|
||||||
|
#
|
||||||
|
# onSelectEvent(self, event, atPoint, selectRect, brushColours, brushSize, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onSelectEvent(self, event, atPoint, selectRect, brushColours, brushSize, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
if isLeftDown:
|
||||||
|
atPoint = list(atPoint)
|
||||||
|
disp = [atPoint[0]-self.toolLastAtPoint[0], \
|
||||||
|
atPoint[1]-self.toolLastAtPoint[1]]
|
||||||
|
self.toolLastAtPoint = atPoint
|
||||||
|
newToolRect = [ \
|
||||||
|
[selectRect[0][0]+disp[0], selectRect[0][1]+disp[1]], \
|
||||||
|
[selectRect[1][0]+disp[0], selectRect[1][1]+disp[1]]]
|
||||||
|
isCursor = True
|
||||||
|
elif isRightDown:
|
||||||
|
disp = [0, 0]
|
||||||
|
newToolRect = selectRect.copy()
|
||||||
|
isCursor = False
|
||||||
|
else:
|
||||||
|
disp = [0, 0]
|
||||||
|
newToolRect = selectRect.copy()
|
||||||
|
isCursor = True
|
||||||
|
for numRow in range(len(self.toolSelectMap)):
|
||||||
|
for numCol in range(len(self.toolSelectMap[numRow])):
|
||||||
|
cellOld = self.toolSelectMap[numRow][numCol]
|
||||||
|
rectY = selectRect[0][1] + numRow
|
||||||
|
rectX = selectRect[0][0] + numCol
|
||||||
|
dispatchFn(eventDc, isCursor, [rectX+disp[0], rectY+disp[1], *cellOld])
|
||||||
|
self._drawSelectRect(newToolRect, dispatchFn, eventDc)
|
||||||
|
self.toolRect = newToolRect
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
64
MiRCARTToolSelectMove.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToolSelectMove.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTToolSelect import MiRCARTToolSelect
|
||||||
|
|
||||||
|
class MiRCARTToolSelectMove(MiRCARTToolSelect):
|
||||||
|
"""XXX"""
|
||||||
|
name = "Move selection"
|
||||||
|
|
||||||
|
#
|
||||||
|
# onSelectEvent(self, event, atPoint, selectRect, brushColours, brushSize, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onSelectEvent(self, event, atPoint, selectRect, brushColours, brushSize, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
if isLeftDown:
|
||||||
|
atPoint = list(atPoint)
|
||||||
|
disp = [atPoint[0]-self.toolLastAtPoint[0], \
|
||||||
|
atPoint[1]-self.toolLastAtPoint[1]]
|
||||||
|
self.toolLastAtPoint = atPoint
|
||||||
|
newToolRect = [ \
|
||||||
|
[selectRect[0][0]+disp[0], selectRect[0][1]+disp[1]], \
|
||||||
|
[selectRect[1][0]+disp[0], selectRect[1][1]+disp[1]]]
|
||||||
|
isCursor = True
|
||||||
|
elif isRightDown:
|
||||||
|
disp = [0, 0]
|
||||||
|
newToolRect = selectRect.copy()
|
||||||
|
isCursor = False
|
||||||
|
else:
|
||||||
|
disp = [0, 0]
|
||||||
|
newToolRect = selectRect.copy()
|
||||||
|
isCursor = True
|
||||||
|
for numRow in range(len(self.toolSelectMap)):
|
||||||
|
for numCol in range(len(self.toolSelectMap[numRow])):
|
||||||
|
dispatchFn(eventDc, isCursor, [self.srcRect[0] + numCol, \
|
||||||
|
self.srcRect[1] + numRow, 1, 1, 0, " "])
|
||||||
|
for numRow in range(len(self.toolSelectMap)):
|
||||||
|
for numCol in range(len(self.toolSelectMap[numRow])):
|
||||||
|
cellOld = self.toolSelectMap[numRow][numCol]
|
||||||
|
rectY = selectRect[0][1] + numRow
|
||||||
|
rectX = selectRect[0][0] + numCol
|
||||||
|
dispatchFn(eventDc, isCursor, [rectX+disp[0], rectY+disp[1], *cellOld])
|
||||||
|
self._drawSelectRect(newToolRect, dispatchFn, eventDc)
|
||||||
|
self.toolRect = newToolRect
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
70
MiRCARTToolText.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# MiRCARTToolText.py -- XXX
|
||||||
|
# Copyright (c) 2018 Lucio Andrés Illanes Albornoz <lucio@lucioillanes.de>
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
from MiRCARTTool import MiRCARTTool
|
||||||
|
import wx
|
||||||
|
|
||||||
|
class MiRCARTToolText(MiRCARTTool):
|
||||||
|
"""XXX"""
|
||||||
|
name = "Text"
|
||||||
|
textColours = textPos = None
|
||||||
|
|
||||||
|
#
|
||||||
|
# onKeyboardEvent(self, event, atPoint, brushColours, brushSize, keyChar, dispatchFn, eventDc): XXX
|
||||||
|
def onKeyboardEvent(self, event, atPoint, brushColours, brushSize, keyChar, dispatchFn, eventDc):
|
||||||
|
keyModifiers = event.GetModifiers()
|
||||||
|
if keyModifiers != wx.MOD_NONE \
|
||||||
|
and keyModifiers != wx.MOD_SHIFT:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
if self.textColours == None:
|
||||||
|
self.textColours = brushColours.copy()
|
||||||
|
if self.textPos == None:
|
||||||
|
self.textPos = list(atPoint)
|
||||||
|
dispatchFn(eventDc, False, [*self.textPos, *self.textColours, 0, keyChar])
|
||||||
|
if self.textPos[0] < (self.parentCanvas.canvasSize[0] - 1):
|
||||||
|
self.textPos[0] += 1
|
||||||
|
elif self.textPos[1] < (self.parentCanvas.canvasSize[1] - 1):
|
||||||
|
self.textPos[0] = 0
|
||||||
|
self.textPos[1] += 1
|
||||||
|
else:
|
||||||
|
self.textPos = [0, 0]
|
||||||
|
return False
|
||||||
|
|
||||||
|
#
|
||||||
|
# onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc): XXX
|
||||||
|
def onMouseEvent(self, event, atPoint, brushColours, brushSize, isDragging, isLeftDown, isRightDown, dispatchFn, eventDc):
|
||||||
|
if isLeftDown:
|
||||||
|
self.textColours = brushColours.copy()
|
||||||
|
self.textPos = list(atPoint)
|
||||||
|
elif isRightDown:
|
||||||
|
self.textColours = [brushColours[1], brushColours[0]]
|
||||||
|
self.textPos = list(atPoint)
|
||||||
|
else:
|
||||||
|
if self.textColours == None:
|
||||||
|
self.textColours = brushColours.copy()
|
||||||
|
self.textPos = list(atPoint)
|
||||||
|
dispatchFn(eventDc, True, [*self.textPos, *self.textColours, 0, "_"])
|
||||||
|
|
||||||
|
# vim:expandtab foldmethod=marker sw=4 ts=4 tw=120
|
17
README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# MiRCART.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:
|
||||||
|
`pip install requests urllib3 wxPython`
|
||||||
|
* Prerequisites on Linux: python3 && python-wx{gtk2.8,tools} on Debian-family Linux distributions
|
||||||
|
* Screenshot:
|
||||||
|
![Screenshot](https://github.com/lalbornoz/MiRCARTools/raw/master/MiRCART.png "Screenshot")
|
||||||
|
|
||||||
|
# IrcMiRCARTBot.py -- IRC<->MiRC2png bot (for EFnet #MiRCART) (pending cleanup)
|
||||||
|
* Prerequisites: python3 && python3-{json,requests,urllib3} on Debian-family Linux distributions
|
||||||
|
* IrcMiRCARTBot.py usage: IrcMiRCARTBot.py `<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>`]
|
||||||
|
|
||||||
|
# MiRCARTToPngFile.py -- convert ASCII w/ mIRC control codes to monospaced PNG (pending cleanup)
|
||||||
|
* Prerequisites: python3 && python3-pil on Debian-family Linux distributions
|
||||||
|
* MiRC2png.py usage: MiRC2png.py `<MiRCART input file pathname>` `<PNG image output file pathname>` [`<Font file pathname; defaults to DejaVuSansMono.ttf>`] [`<Font size; defaults to 11>`]
|
||||||
|
|
||||||
|
References:
|
||||||
|
Fri, 05 Jan 2018 17:01:47 +0100 [1] Python Releases for Windows | Python.org <https://www.python.org/downloads/windows/>
|
BIN
assets/toolCircle.png
Normal file
After Width: | Height: | Size: 360 B |
BIN
assets/toolClone.png
Normal file
After Width: | Height: | Size: 227 B |
BIN
assets/toolDecrBrushH.png
Normal file
After Width: | Height: | Size: 271 B |
BIN
assets/toolDecrBrushHW.png
Normal file
After Width: | Height: | Size: 277 B |
BIN
assets/toolDecrBrushW.png
Normal file
After Width: | Height: | Size: 250 B |
BIN
assets/toolDecrCanvasH.png
Normal file
After Width: | Height: | Size: 265 B |
BIN
assets/toolDecrCanvasHW.png
Normal file
After Width: | Height: | Size: 289 B |
BIN
assets/toolDecrCanvasW.png
Normal file
After Width: | Height: | Size: 249 B |
BIN
assets/toolFill.png
Normal file
After Width: | Height: | Size: 527 B |
BIN
assets/toolIncrBrushH.png
Normal file
After Width: | Height: | Size: 236 B |
BIN
assets/toolIncrBrushHW.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
assets/toolIncrBrushW.png
Normal file
After Width: | Height: | Size: 232 B |
BIN
assets/toolIncrCanvasH.png
Normal file
After Width: | Height: | Size: 246 B |
BIN
assets/toolIncrCanvasHW.png
Normal file
After Width: | Height: | Size: 282 B |
BIN
assets/toolIncrCanvasW.png
Normal file
After Width: | Height: | Size: 236 B |
BIN
assets/toolLine.png
Normal file
After Width: | Height: | Size: 371 B |
BIN
assets/toolMove.png
Normal file
After Width: | Height: | Size: 213 B |
BIN
assets/toolRect.png
Normal file
After Width: | Height: | Size: 220 B |
BIN
assets/toolText.png
Normal file
After Width: | Height: | Size: 306 B |