From 5264244664a1250a08f09477b56bce82aab87d01 Mon Sep 17 00:00:00 2001 From: m32 Date: Wed, 25 Dec 2024 23:28:35 +0100 Subject: [PATCH 1/2] show all characters --- python/configs/plug/plugins/ucharmap.py | 136 ++++++++++++++++-------- 1 file changed, 89 insertions(+), 47 deletions(-) diff --git a/python/configs/plug/plugins/ucharmap.py b/python/configs/plug/plugins/ucharmap.py index 23c83c7e1..ac2b65c27 100644 --- a/python/configs/plug/plugins/ucharmap.py +++ b/python/configs/plug/plugins/ucharmap.py @@ -2,6 +2,7 @@ from far2l.plugin import PluginBase from far2l.fardialogbuilder import ( TEXT, + EDIT, BUTTON, USERCONTROL, HLine, @@ -38,34 +39,24 @@ def _OpenPlugin(self, OpenFrom): import debugpy debugpy.breakpoint() - self.max_col = 32 - symbols = ''.join([chr(i) for i in range(256)])+( - 'ЂЃ‚ѓ„…†‡€‰Љ‹ЊЌЋЏ' - 'ђ‘’“”•–—˜™љ›њќћџ' - ' ЎўЈ¤Ґ¦§Ё©Є«¬"®Ї' - '°±Ііґµ¶·ё№є»јЅѕї' - 'АБВГДЕЖЗИЙКЛМНОП' - 'РСТУФХЦЧШЩЪЫЬЭЮЯ' - 'абвгдежзийклмноп' - 'рстуфхцчшщъыьэюя' - '░▒▓│┤╡╢╖╕╣║╗╝╜╛┐' - '└┴┬├─┼╞╟╚╔╩╦╠═╬╧' - '╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀' - '∙√■⌠≈≤≥⌡²÷ąćęłńó' - 'śżźĄĆĘŁŃÓŚŻŹ' - ) - n = len(symbols) % self.max_col - if n: - symbols += ' '*(self.max_col-n) - self.max_row = len(symbols) // self.max_col + self.offset = 0 + self.max_col = 64 + self.max_row = 20 self.text = '' - self.charbuf = self.ffi.new("CHAR_INFO []", len(symbols)) + self.charbuf = self.ffi.new("CHAR_INFO []", self.max_col*self.max_row) attrNormal = 0x170 attrSelected = 0x1c - for i in range(len(symbols)): - cb = self.charbuf[i] - cb.Attributes = attrNormal - cb.Char.UnicodeChar = ord(symbols[i]) + + def setVBuf(hDlg): + self.info.SendDlgMessage(hDlg, self.ffic.DM_ENABLEREDRAW, 0, 0) + for i in range(self.max_col*self.max_row): + cb = self.charbuf[i] + cb.Attributes = attrNormal + if self.offset+i <= 0xffff: + cb.Char.UnicodeChar = self.offset+i + else: + cb.Char.UnicodeChar = 0x20 + self.info.SendDlgMessage(hDlg, self.ffic.DM_ENABLEREDRAW, 1, 0) def xy2lin(col, row): return row * self.max_col + col @@ -74,31 +65,68 @@ def setColor(col, row, attr): self.charbuf[xy2lin(col, row)].Attributes = attr def updateOffset(offset): - char = symbols[offset] - dlg.SetText(dlg.ID_voffset, f"{offset}") - dlg.SetText(dlg.ID_vchar, f'{char}/{ord(char):4x}') + dlg.SetText(dlg.ID_voffset, f"{offset:5d}/0x{offset:04x}") - @self.ffi.callback("FARWINDOWPROC") - def DialogProc(hDlg, Msg, Param1, Param2): + def _DialogProc(hDlg, Msg, Param1, Param2): if Msg == self.ffic.DN_INITDIALOG: + setVBuf(hDlg) self.SetCursorPos(hDlg, dlg.ID_hex, 0, 0) setColor(0, 0, attrSelected) updateOffset(0) self.info.SendDlgMessage(hDlg, self.ffic.DM_SETCURSORSIZE, dlg.ID_hex, 1|(100<<32)) + dlg.SetFocus(dlg.ID_hex) return self.info.DefDlgProc(hDlg, Msg, Param1, Param2) elif Msg == self.ffic.DN_BTNCLICK: #log.debug(f"btn DialogProc({Param1}, {Param2})") - col, row = self.GetCursorPos(hDlg, dlg.ID_hex) - offset = xy2lin(col, row) - self.text = symbols[offset] - updateOffset(offset) - #log.debug(f"enter:{offset} row:{row} col:{col}, ch:{self.text} cb={self.charbuf[offset].Attributes:x}") - return self.info.DefDlgProc(hDlg, Msg, Param1, Param2) + if Param1 == dlg.ID_vgoto: + v = dlg.GetText(dlg.ID_vgotovalue).strip() + if not v: + v = '0' + try: + offset = int(v, 16 if v[:2] == '0x' else 10) + self.offset = offset + setVBuf(hDlg) + except: + if len(v) == 1: + self.offset = ord(v) + setVBuf(hDlg) + else: + log.exception('goto') + dlg.SetFocus(dlg.ID_hex) + self.SetCursorPos(hDlg, dlg.ID_hex, 0, 0) + updateOffset(self.offset) + return 1 + elif Param1 == dlg.ID_vok: + col, row = self.GetCursorPos(hDlg, dlg.ID_hex) + offset = self.offset + xy2lin(col, row) + if offset <= 0xffff: + try: + self.text = chr(offset) + except: + log.exception(f'OK click {offset}') + return 0 elif Msg == self.ffic.DN_KEY and Param1 == dlg.ID_hex: col, row = self.GetCursorPos(hDlg, dlg.ID_hex) setColor(col, row, attrNormal) #log.debug(f"key DialogProc({Param1}, {Param2:x}), col={col} row={row})") - if Param2 == self.ffic.KEY_LEFT: + if Param2 == self.ffic.KEY_PGUP: + self.offset -= self.max_row * self.max_col + if self.offset < 0: + self.offset = 0 + setVBuf(hDlg) + offset = self.offset + xy2lin(col, row) + updateOffset(offset) + elif Param2 == self.ffic.KEY_HOME: + row = 0 + col = 0 + elif Param2 == self.ffic.KEY_PGDN: + self.offset += self.max_row * self.max_col + if self.offset > 0xffff: + self.offset = 0x10000 - self.max_row * self.max_col + setVBuf(hDlg) + offset = self.offset + xy2lin(col, row) + updateOffset(offset) + elif Param2 == self.ffic.KEY_LEFT: col -= 1 elif Param2 == self.ffic.KEY_UP: row -= 1 @@ -107,9 +135,13 @@ def DialogProc(hDlg, Msg, Param1, Param2): elif Param2 == self.ffic.KEY_DOWN: row += 1 elif Param2 == self.ffic.KEY_ENTER: - offset = xy2lin(col, row) - self.text = symbols[offset] - #log.debug(f"enter:{offset} row:{row} col:{col}, ch:{self.text} cb={self.charbuf[offset].Attributes:x}") + #log.debug(f"enter at offset:{self.offset} row:{row} col:{col}") + offset = self.offset + xy2lin(col, row) + if offset <= 0xffff: + try: + self.text = chr(offset) + except: + log.exception(f'enter at {offset}') return 0 elif Param2 == self.ffic.KEY_ESC: return 0 @@ -124,7 +156,7 @@ def DialogProc(hDlg, Msg, Param1, Param2): elif row == -1: row = self.max_row - 1 setColor(col, row, attrSelected) - offset = xy2lin(col, row) + offset = self.offset + xy2lin(col, row) updateOffset(offset) #log.debug(f"col={col}/{self.max_col} row={row}/{self.max_row}") self.SetCursorPos(hDlg, dlg.ID_hex, col, row) @@ -138,11 +170,19 @@ def DialogProc(hDlg, Msg, Param1, Param2): setColor(col, row, attrSelected) self.SetCursorPos(hDlg, dlg.ID_hex, col, row) #log.debug(f"mou DialogProc(col={col} row={row})") - offset = xy2lin(col, row) - self.text = self.symbols[offset] + offset = self.offset + xy2lin(col, row) + #self.text = self.symbols[offset] updateOffset(offset) return self.info.DefDlgProc(hDlg, Msg, Param1, Param2) + @self.ffi.callback("FARWINDOWPROC") + def DialogProc(hDlg, Msg, Param1, Param2): + try: + return _DialogProc(hDlg, Msg, Param1, Param2) + except: + log.exception('dialogproc') + return 1 + b = DialogBuilder( self, DialogProc, @@ -152,9 +192,10 @@ def DialogProc(hDlg, Msg, Param1, Param2): VSizer( HSizer( TEXT(None, "Offset:"), - TEXT("voffset", " "*8), - TEXT(None, "Char:"), - TEXT("vchar", " "*6), + TEXT("voffset", " "*12), + TEXT(None, "&Goto:"), + EDIT("vgotovalue", width=7), + BUTTON("vgoto", "GOTO"), ), HLine(), USERCONTROL('hex', self.max_col, self.max_row, param={'VBuf':self.ffi.cast("CHAR_INFO *", self.ffi.addressof(self.charbuf))}), @@ -168,6 +209,7 @@ def DialogProc(hDlg, Msg, Param1, Param2): dlg = b.build(-1, -1) res = self.info.DialogRun(dlg.hDlg) - if res in (1, dlg.ID_vok): + #log.debug(f'res={res} text=***{self.text}***') + if res in (dlg.ID_hex, dlg.ID_vok): self.info.FSF.CopyToClipboard(self.s2f(self.text)) self.info.DialogFree(dlg.hDlg) From 993515c59715506cc17d88be0954b27436fdd0f0 Mon Sep 17 00:00:00 2001 From: m32 Date: Fri, 27 Dec 2024 21:13:21 +0100 Subject: [PATCH 2/2] copying files via clipboard - gnome files/nautilus is supported --- .../plug/plugins/tool-show-clipboard.py | 20 ++++++ python/configs/plug/plugins/uclipget.py | 65 +++++++++++++++++ python/configs/plug/plugins/uclipset.py | 71 +++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 python/configs/plug/plugins/tool-show-clipboard.py create mode 100644 python/configs/plug/plugins/uclipget.py create mode 100644 python/configs/plug/plugins/uclipset.py diff --git a/python/configs/plug/plugins/tool-show-clipboard.py b/python/configs/plug/plugins/tool-show-clipboard.py new file mode 100644 index 000000000..9031e0f37 --- /dev/null +++ b/python/configs/plug/plugins/tool-show-clipboard.py @@ -0,0 +1,20 @@ +import gi + +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk, Gdk + +clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) +for mime in ( + "text/plain", + "text/plain;charset=utf-8", + "text/html", + "text/uri-list", + "image/png", + 'x-special/gnome-copied-files', + 'SAVE_TARGETS', + 'TIMESTAMP', + 'TARGETS', +): + text = clipboard.wait_for_contents(Gdk.Atom.intern(mime, True)) + if text: + print(mime, text.get_data()) diff --git a/python/configs/plug/plugins/uclipget.py b/python/configs/plug/plugins/uclipget.py new file mode 100644 index 000000000..c0d8b453d --- /dev/null +++ b/python/configs/plug/plugins/uclipget.py @@ -0,0 +1,65 @@ +import os +import sys +import logging + +import ctypes as ct +import far2lc +from far2l.plugin import PluginBase +from yfar import FarPlugin + +log = logging.getLogger(__name__) + +class Plugin(FarPlugin): + label = "Python Clip GET" + openFrom = ["PLUGINSMENU", 'FILEPANEL'] + + def CopyFiles(self, data): + pwd = self.get_panel().directory + data = data.decode('utf8') + log.debug(f'copyfiles: {data}') + prefix = 'file://' + for uri in data.split('\n'): + if uri[:len(prefix)] != prefix: + continue + fqname = uri[len(prefix):] + fname = fqname.split('/')[-1] + dqname = os.path.join(pwd, fname) + log.debug(f'CopyFile: {fqname} -> {dqname}') + with open(dqname, 'wb') as fo: + with open(fqname, 'rb') as fi: + rec = fi.read(4096) + if not rec: + break + fo.write(rec) + + def OpenPlugin(self, OpenFrom): + winport = self.ffi.cast("struct WINPORTDECL *", far2lc.WINPORT()) + clipurifmt = winport.RegisterClipboardFormat("text/uri-list") + if not clipurifmt: + log.error('uclipset.ClipboardRegisterFormat.1') + return + clipgnofmt = winport.RegisterClipboardFormat("x-special/gnome-copied-files") + if not clipgnofmt: + log.error('uclipset.ClipboardRegisterFormat.2') + return + if not winport.OpenClipboard(self.ffi.NULL): + log.error('uclipset.OpenClipboard') + return + try: + data = winport.GetClipboardData(clipurifmt) + if data is not None: + nb = winport.ClipboardSize(data) + result = self.ffi.buffer(data, nb-1) + self.CopyFiles(bytes(result)) + else: + data = winport.GetClipboardData(clipgnofmt) + if data is not None: + nb = winport.ClipboardSize(data) + result = self.ffi.buffer(data, nb-1) + self.CopyFiles(bytes(result)) + except: + log.exception('uclipset.GetClipboardData') + finally: + if not winport.CloseClipboard(): + log.error('uclipset.CloseClipboard') + return diff --git a/python/configs/plug/plugins/uclipset.py b/python/configs/plug/plugins/uclipset.py new file mode 100644 index 000000000..17293603c --- /dev/null +++ b/python/configs/plug/plugins/uclipset.py @@ -0,0 +1,71 @@ +import sys +import logging + +import ctypes as ct +import far2lc +from far2l.plugin import PluginBase +from yfar import FarPlugin + +log = logging.getLogger(__name__) + +CAH_ALLOCATED_MAGIC = 0x0610ba10A110CED0 +CAH_FREED_MAGIC = 0x0610ba10F4EED000 +class ClipboardAllocHeader(ct.Structure): + _fields_ = [ + ('size', ct.c_uint32), + ('padding', ct.c_uint32), + ('magic', ct.c_uint64), + ] + +class Plugin(FarPlugin): + label = "Python Clip SET" + openFrom = ["PLUGINSMENU", 'FILEPANEL'] + + def SetClipboard(self, winport, fmt, fqname): + fqname = fqname.encode('utf8') + #log.debug(f'clip.{fmt} set: {fqname} {len(fqname)}') + ptr = winport.ClipboardAlloc(len(fqname)) + if ptr is None: + log.debug('clipboard alloc failed') + else: + if 0: + m = int(self.ffi.cast('DWORD64', ptr)) + log.debug(f'ptr={ptr} m={m:x}') + m -= ct.sizeof(ClipboardAllocHeader) + log.debug(f'm={m:x}') + mp = ClipboardAllocHeader.from_address(m) + log.debug(f'mp.size={mp.size} mp.padding={mp.padding} mp.magic={mp.magic:x} CAH_ALLOCATED_MAGIC={CAH_ALLOCATED_MAGIC:x}') + self.ffi.memmove(ptr, fqname, len(fqname)) + res = winport.SetClipboardData(fmt, ptr) + + def OpenPlugin(self, OpenFrom): + winport = self.ffi.cast("struct WINPORTDECL *", far2lc.WINPORT()) + clipurifmt = winport.RegisterClipboardFormat("text/uri-list") + if not clipurifmt: + log.error('uclipset.ClipboardRegisterFormat.1') + return + clipgnofmt = winport.RegisterClipboardFormat("x-special/gnome-copied-files") + if not clipgnofmt: + log.error('uclipset.ClipboardRegisterFormat.2') + return + if not winport.OpenClipboard(self.ffi.NULL): + log.error('uclipset.OpenClipboard') + return + try: + files = [] + panel = self.get_panel() + for f in panel.selected: + fqname = f.full_file_name + files.append("file://"+fqname) + #self.SetClipboard(winport, 1, fqname) + self.SetClipboard(winport, clipurifmt, "\n".join(files)) + self.SetClipboard(winport, clipgnofmt, "copy\n"+"\n".join(files)) + except: + log.exception('uclipset.SetClipboard') + finally: + if not winport.CloseClipboard(): + log.error('uclipset.CloseClipboard') + return + # typedef BOOL (*WINPORT_IsClipboardFormatAvailable) (UINT format); + # typedef PVOID (*WINPORT_GetClipboardData) (UINT format); + # typedef SIZE_T (*WINPORT_ClipboardSize) (PVOID mem);