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