Skip to content

Commit 092c8e9

Browse files
committed
Insert Symbol based alternative implementation
1 parent 1362913 commit 092c8e9

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed

inserttext.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
2+
# Copyright 2010 Jaap Karssenberg <jaap.karssenberg@gmail.com>
3+
# Copyright 2022 introt <introt@koti.fimnet.fi>
4+
5+
from gi.repository import Gtk
6+
import logging
7+
8+
from zim.plugins import PluginClass
9+
from zim.actions import action
10+
from zim.config import ConfigManager
11+
12+
from zim.gui.pageview import PageViewExtension
13+
from zim.gui.widgets import Dialog, InputEntry, ScrolledWindow
14+
from zim.gui.applications import edit_config_file
15+
16+
logger = logging.getLogger('zim.plugins.inserttext')
17+
18+
19+
VERBATIM = 'code'
20+
VERBATIM_BLOCK = 'pre'
21+
22+
23+
class InsertTextPlugin(PluginClass):
24+
25+
plugin_info = {
26+
'name': _('Insert Text'), # T: plugin name
27+
'description': _('''\
28+
This plugin adds the 'Insert Text' dialog and allows
29+
auto-replacing text snippets.
30+
31+
This is not a core plugin shipping with zim.
32+
'''), # T: plugin description
33+
'author': 'Jaap Karssenberg & introt',
34+
}
35+
36+
def __init__(self):
37+
PluginClass.__init__(self)
38+
self.texts = {}
39+
self.text_order = []
40+
41+
def load_file(self):
42+
self.texts = {}
43+
self.text_order = []
44+
file = ConfigManager.get_config_file('texts.list')
45+
for line in file.readlines():
46+
line = line.strip()
47+
if not line or line.startswith('#'):
48+
continue
49+
try:
50+
if '#' in line:
51+
line, _ = line.split('#', 1)
52+
line = line.strip()
53+
shortcut, code = line.split(maxsplit=1)
54+
#text = chr(int(code))
55+
if not shortcut in self.texts:
56+
self.texts[shortcut] = code.replace(r'\n', '\n') #text
57+
self.text_order.append(shortcut)
58+
else:
59+
logger.exception('Shortcut defined twice: %s', shortcut)
60+
except:
61+
logger.exception('Could not parse text: %s', line)
62+
63+
def get_texts(self):
64+
for shortcut in self.text_order:
65+
text = self.texts[shortcut]
66+
yield text, shortcut
67+
68+
69+
class InsertTextPageViewExtension(PageViewExtension):
70+
71+
def __init__(self, plugin, pageview):
72+
PageViewExtension.__init__(self, plugin, pageview)
73+
self.connectto(pageview.textview, 'end-of-word')
74+
if not plugin.texts:
75+
plugin.load_file()
76+
77+
@action(_('Text...'), menuhints='insert') # T: menu item
78+
def insert_text(self):
79+
'''Run the InsertTextDialog'''
80+
InsertTextDialog(self.pageview, self.plugin, self.pageview).run()
81+
82+
def on_end_of_word(self, textview, start, end, word, char, editmode):
83+
'''Handler for the end-of-word signal from the textview'''
84+
# We check for non-space char because e.g. typing "-->" will
85+
# emit end-of-word with "--" as word and ">" as character.
86+
# This should be distinguished from the case when e.g. typing
87+
# "-- " emits end-of-word with "--" as word and " " (space) as
88+
# the char.
89+
if VERBATIM in editmode \
90+
or VERBATIM_BLOCK in editmode \
91+
or not (char.isspace() or char == ';'):
92+
return
93+
94+
text = self.plugin.texts.get(word)
95+
if not text and word.count('\\') == 1:
96+
# do this after testing the whole word, we have e.g. "=\="
97+
# also avoid replacing end of e.g. "C:\foo\bar\left",
98+
# so match exactly one "\"
99+
prefix, key = word.split('\\', 1)
100+
text = self.plugin.texts.get('\\' + key)
101+
if text:
102+
start.forward_chars(len(prefix))
103+
104+
if not text:
105+
return
106+
107+
# replace word with text
108+
buffer = textview.get_buffer()
109+
mark = buffer.create_mark(None, end, left_gravity=False)
110+
if char == ';':
111+
end = end.copy()
112+
end.forward_char() # include the ';' in the delete
113+
buffer.delete(start, end)
114+
else:
115+
buffer.delete(start, end)
116+
iter = buffer.get_iter_at_mark(mark)
117+
buffer.insert(iter, text)
118+
buffer.delete_mark(mark)
119+
120+
# block other handlers
121+
textview.stop_emission('end-of-word')
122+
123+
124+
class InsertTextDialog(Dialog):
125+
126+
def __init__(self, parent, plugin, pageview):
127+
Dialog.__init__(
128+
self,
129+
parent,
130+
_('Insert Text'), # T: Dialog title
131+
button=_('_Insert'), # T: Button label
132+
defaultwindowsize=(350, 400)
133+
)
134+
self.plugin = plugin
135+
self.pageview = pageview
136+
if not plugin.texts:
137+
plugin.load_file()
138+
139+
self.textentry = InputEntry()
140+
self.vbox.pack_start(self.textentry, False, True, 0)
141+
142+
model = Gtk.ListStore(str, str) # text, shortcut
143+
self.iconview = Gtk.IconView(model)
144+
self.iconview.set_text_column(0)
145+
self.iconview.set_column_spacing(0)
146+
self.iconview.set_row_spacing(0)
147+
self.iconview.set_property('has-tooltip', True)
148+
self.iconview.set_property('activate-on-single-click', True)
149+
self.iconview.connect('query-tooltip', self.on_query_tooltip)
150+
self.iconview.connect('item-activated', self.on_activated)
151+
152+
swindow = ScrolledWindow(self.iconview)
153+
self.vbox.pack_start(swindow, True, True, 0)
154+
155+
button = Gtk.Button.new_with_mnemonic(_('_Edit')) # T: Button label
156+
button.connect('clicked', self.on_edit)
157+
self.action_area.add(button)
158+
self.action_area.reorder_child(button, 0)
159+
160+
self.load_texts()
161+
162+
def load_texts(self):
163+
model = self.iconview.get_model()
164+
model.clear()
165+
for text, shortcut in self.plugin.get_texts():
166+
model.append((text, shortcut))
167+
168+
def on_query_tooltip(self, iconview, x, y, keyboard, tooltip):
169+
if keyboard:
170+
return False
171+
172+
x, y = iconview.convert_widget_to_bin_window_coords(x, y)
173+
path = iconview.get_path_at_pos(x, y)
174+
if path is None:
175+
return False
176+
177+
model = iconview.get_model()
178+
iter = model.get_iter(path)
179+
text = model.get_value(iter, 1)
180+
if not text:
181+
return False
182+
183+
tooltip.set_text(text)
184+
return True
185+
186+
def on_activated(self, iconview, path):
187+
model = iconview.get_model()
188+
iter = model.get_iter(path)
189+
text = model.get_value(iter, 0)
190+
pos = self.textentry.get_position()
191+
self.textentry.insert_text(text, pos)
192+
self.textentry.set_position(pos + len(text))
193+
194+
def on_edit(self, button):
195+
file = ConfigManager.get_config_file('texts.list')
196+
if edit_config_file(self, file):
197+
self.plugin.load_file()
198+
self.load_texts()
199+
200+
def run(self):
201+
self.iconview.grab_focus()
202+
Dialog.run(self)
203+
204+
def do_response_ok(self):
205+
text = self.textentry.get_text()
206+
textview = self.pageview.textview
207+
buffer = textview.get_buffer()
208+
buffer.insert_at_cursor(text)
209+
return True

0 commit comments

Comments
 (0)