~rick-rickspencer3/quidgets/natty-trunk

« back to all changes in this revision

Viewing changes to quickly/widgets/text_editor.py

  • Committer: Rick Spencer
  • Date: 2010-11-22 15:21:28 UTC
  • Revision ID: rick.spencer@canonical.com-20101122152128-gm0x0l3ar31rgmzo
removing a couple of widgets that aren't ready for primetime

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
### BEGIN LICENSE
2
 
# Copyright (C) 2010 Rick Spencer rick.spencer@canonical.com
3
 
#This program is free software: you can redistribute it and/or modify it 
4
 
#under the terms of the GNU General Public License version 3, as published 
5
 
#by the Free Software Foundation.
6
 
#
7
 
#This program is distributed in the hope that it will be useful, but 
8
 
#WITHOUT ANY WARRANTY; without even the implied warranties of 
9
 
#MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
10
 
#PURPOSE.  See the GNU General Public License for more details.
11
 
#
12
 
#You should have received a copy of the GNU General Public License along 
13
 
#with this program.  If not, see <http://www.gnu.org/licenses/>.
14
 
### END LICENSE
15
 
"""Module for the TextView widgth wich encapsulates management of TextBuffer
16
 
and TextIter for common functionality, such as cut, copy, paste, undo, redo, 
17
 
and highlighting of text.
18
 
 
19
 
Using
20
 
#create the TextEditor and set the text
21
 
editor = TextEditor()
22
 
editor.text = "Text to add to the editor"
23
 
 
24
 
#use cut, works the same for copy, paste, undo, and redo
25
 
def __handle_on_cut(self, widget, data=None):
26
 
    self.editor.cut()
27
 
 
28
 
#add string to highlight
29
 
self.editor.add_highlight("Ubuntu")
30
 
self.editor.add_highlight("Quickly")
31
 
 
32
 
#remove highlights
33
 
self.editor.clear_highlight("Ubuntu")
34
 
self.editor.clear_all_highlight()
35
 
 
36
 
Configuring
37
 
#Configure as a TextView
38
 
self.editor.set_wrap_mode(gtk.WRAP_CHAR)
39
 
 
40
 
#Access the gtk.TextBuffer if needed
41
 
buffer = self.editor.get_buffer()
42
 
 
43
 
Extending
44
 
A TextEditor is gtk.TextView
45
 
 
46
 
"""
47
 
 
48
 
try:
49
 
    import pygtk
50
 
    pygtk.require("2.0")
51
 
    import gtk
52
 
    import re
53
 
except:
54
 
    print "couldn't load depencies"
55
 
 
56
 
class TextEditor( gtk.TextView ):
57
 
    """TextEditor encapsulates management of TextBuffer and TextIter for
58
 
    common functionality, such as cut, copy, paste, undo, redo, and 
59
 
    highlighting of text.
60
 
 
61
 
    """
62
 
 
63
 
    def __init__(self):
64
 
        """Create a TextEditor
65
 
 
66
 
        """
67
 
 
68
 
        gtk.TextView.__init__( self)
69
 
        self.undo_max = None
70
 
        self._highlight_strings = []
71
 
        found_tag = gtk.TextTag("highlight")
72
 
        found_tag.set_property("background","yellow")
73
 
        self.get_buffer().get_tag_table().add(found_tag)
74
 
 
75
 
        self.insert_event = self.get_buffer().connect("insert-text",self._on_insert)
76
 
        self.delete_event = self.get_buffer().connect("delete-range",self._on_delete)
77
 
        self.change_event = self.get_buffer().connect("changed",self._on_text_changed)
78
 
        self._auto_bullet = None
79
 
        self.auto_bullets = False
80
 
        self.clipboard = gtk.Clipboard()
81
 
 
82
 
        self.undos = []
83
 
        self.redos = []
84
 
 
85
 
    @property
86
 
    def text(self):
87
 
        """text - a string specifying all the text currently 
88
 
        in the TextEditor's buffer.
89
 
 
90
 
        This property is read/write.
91
 
 
92
 
        """
93
 
        start_iter = self.get_buffer().get_iter_at_offset(0)
94
 
        end_iter =  self.get_buffer().get_iter_at_offset(-1)
95
 
        return self.get_buffer().get_text(start_iter,end_iter)
96
 
 
97
 
    @text.setter
98
 
    def text(self, text):
99
 
        self.get_buffer().set_text(text)
100
 
 
101
 
    def append(self, text):
102
 
        """append: appends text to the end of the textbuffer.
103
 
 
104
 
        arguments:
105
 
        text - a string to add to the buffer. The text will be the
106
 
        last text in the buffer. The insertion cursor will not be moved.
107
 
 
108
 
        """
109
 
 
110
 
        end_iter =  self.get_buffer().get_iter_at_offset(-1)
111
 
        self.get_buffer().insert(end_iter, text)
112
 
 
113
 
    def prepend(self, text):
114
 
        """prepend: appends text to the start of the textbuffer.
115
 
 
116
 
        arguments:
117
 
        text - a string to add to the buffer. The text will be the
118
 
        first text in the buffer. The insertion cursor will not be moved.
119
 
 
120
 
        """
121
 
 
122
 
        start_iter =  self.get_buffer().get_iter_at_offset(0)
123
 
        self.get_buffer().insert(start_iter, text)
124
 
        insert_iter = self.get_buffer().get_iter_at_offset(len(text)-1)
125
 
        self.get_buffer().place_cursor(insert_iter)
126
 
 
127
 
    def cursor_to_end(self):
128
 
        """cursor_to_end: moves the insertion curson to the last position
129
 
        in the buffer.
130
 
 
131
 
        """
132
 
 
133
 
        end_iter = self.get_buffer().get_iter_at_offset(-1)
134
 
        self.get_buffer().place_cursor(end_iter)
135
 
 
136
 
    def cursor_to_start(self):
137
 
        """cursor_to_start: moves the insertion curson to the first position
138
 
        in the buffer.
139
 
 
140
 
        """
141
 
 
142
 
        start_iter = self.get_buffer().get_iter_at_offset(0)
143
 
        self.get_buffer().place_cursor(start_iter)
144
 
 
145
 
    def add_highlight(self, text):
146
 
        """add_highlight: add string to be highlighted when entered in the
147
 
        buffer.
148
 
 
149
 
        arguments:
150
 
        text - a string to be highlighted
151
 
 
152
 
        """ 
153
 
 
154
 
        if text not in self._highlight_strings:
155
 
            self._highlight_strings.append(text)
156
 
        self._highlight()
157
 
 
158
 
    def clear_highlight(self, text):
159
 
        """clear_highlight: stops a string from being highlighted in the 
160
 
        buffer.
161
 
 
162
 
        arguments:
163
 
        text - the string to stop highlighting. If the string is not currently
164
 
        being highlighted, the function will be ignored.
165
 
 
166
 
        """
167
 
 
168
 
        if text in self._highlight_strings:
169
 
            del(self._highlight_strings[text])
170
 
        self._highlight()
171
 
 
172
 
    def clear_all_highlights(self):
173
 
        """clear_all_highlight: stops highlighting all strings in the buffer.
174
 
        The TextEditor will forget about all strings specified for highlighting.
175
 
        If no strings are currently set for highlighting, the function will be
176
 
        ignored.
177
 
 
178
 
        """
179
 
 
180
 
 
181
 
        self._highlight_strings = []
182
 
        self._highlight()
183
 
 
184
 
    def _highlight(self):
185
 
        """_highlight: internal method to trigger highlighting.
186
 
        Do not call directly.
187
 
 
188
 
        """
189
 
 
190
 
        start_iter = self.get_buffer().get_iter_at_offset(0)
191
 
        end_iter =  self.get_buffer().get_iter_at_offset(-1)
192
 
        text = self.get_buffer().get_text(start_iter,end_iter)
193
 
        self.get_buffer().remove_all_tags(start_iter, end_iter)
194
 
        for s in self._highlight_strings:
195
 
            hits = [match.start() for match in re.finditer(re.escape(s), text)]
196
 
            for hit in hits:
197
 
                start_iter = self.get_buffer().get_iter_at_offset(hit)
198
 
                end_iter = self.get_buffer().get_iter_at_offset(hit + len(s))
199
 
                self.get_buffer().apply_tag_by_name("highlight",start_iter,end_iter)                 
200
 
 
201
 
    def cut(self, widget=None, data=None):
202
 
        """cut: cut currently selected text and put it on the clipboard.
203
 
        This function can be called as a function, or assigned as a signal
204
 
        handler.
205
 
 
206
 
        """
207
 
 
208
 
        self.get_buffer().cut_clipboard(self.clipboard, True)
209
 
 
210
 
    def copy(self, widget=None, data=None):
211
 
        """copy: copy currently selected text to the clipboard.
212
 
        This function can be called as a function, or assigned as a signal
213
 
        handler.
214
 
 
215
 
        """
216
 
 
217
 
        self.get_buffer().copy_clipboard(self.clipboard)            
218
 
 
219
 
    def paste(self, widget=None, data=None):
220
 
        """paste: Insert any text currently on the clipboard into the
221
 
        buffer.
222
 
        This function can be called as a function, or assigned as a signal
223
 
        handler.
224
 
 
225
 
        """
226
 
 
227
 
        self.get_buffer().paste_clipboard(self.clipboard,None,True)
228
 
 
229
 
    def undo(self, widget=None, data=None):
230
 
        """undo: revert (undo) the last action.
231
 
        This function can be called as a function, or assigned as a signal
232
 
        handler.
233
 
 
234
 
        """
235
 
 
236
 
        if len(self.undos) == 0:
237
 
            return
238
 
 
239
 
        self.get_buffer().disconnect(self.delete_event)
240
 
        self.get_buffer().disconnect(self.insert_event)
241
 
 
242
 
        undo = self.undos[-1]
243
 
        redo = self._do_action(undo)
244
 
        self.redos.append(redo)
245
 
        del(self.undos[-1])
246
 
 
247
 
        self.insert_event = self.get_buffer().connect("insert-text",self._on_insert)
248
 
        self.delete_event = self.get_buffer().connect("delete-range",self._on_delete)
249
 
        self._highlight()
250
 
 
251
 
    def _do_action(self, action):
252
 
        if action["action"] == "delete":
253
 
            start_iter = self.get_buffer().get_iter_at_offset(action["offset"])
254
 
            end_iter =  self.get_buffer().get_iter_at_offset(action["offset"] + len(action["text"]))
255
 
            self.get_buffer().delete(start_iter, end_iter)
256
 
            action["action"] = "insert"
257
 
 
258
 
        elif action["action"] == "insert":
259
 
            start_iter = self.get_buffer().get_iter_at_offset(action["offset"])
260
 
            self.get_buffer().insert(start_iter, action["text"])
261
 
            action["action"] = "delete"
262
 
 
263
 
        return action
264
 
 
265
 
    def redo(self, widget=None, data=None):
266
 
        """redo: revert (undo) the last revert (undo) action.
267
 
        This function can be called as a function, or assigned as a signal
268
 
        handler.
269
 
 
270
 
        """
271
 
 
272
 
        if len(self.redos) == 0:
273
 
            return
274
 
 
275
 
        self.get_buffer().disconnect(self.delete_event)
276
 
        self.get_buffer().disconnect(self.insert_event)
277
 
 
278
 
        redo = self.redos[-1]
279
 
        undo = self._do_action(redo)
280
 
        self.undos.append(undo)
281
 
        del(self.redos[-1])
282
 
        
283
 
        self.insert_event = self.get_buffer().connect("insert-text",self._on_insert)
284
 
        self.delete_event = self.get_buffer().connect("delete-range",self._on_delete)
285
 
 
286
 
        self._highlight()
287
 
 
288
 
    def _on_text_changed(self, buff):
289
 
        if self._auto_bullet is not None:
290
 
            self.get_buffer().disconnect(self.change_event)
291
 
            buff.insert_at_cursor(self._auto_bullet)
292
 
            self._auto_bullet = None
293
 
            self.change_event = self.get_buffer().connect("changed",self._on_text_changed)
294
 
 
295
 
    def _on_insert(self, text_buffer, iter, text, length, data=None):
296
 
        """_on_insert: internal function to handle programatically inserted
297
 
        text. Do not call directly.
298
 
 
299
 
        """
300
 
        #TODO: add auto-whatever functionality by connecting to an event such
301
 
        #as text-changed, etc...
302
 
        self._highlight()
303
 
        cmd = {"action":"delete","offset":iter.get_offset(),"text":text}
304
 
        self._add_undo(cmd)
305
 
        self.redos = []
306
 
        if text == "\n" and self.auto_bullets:
307
 
            cur_line = iter.get_line()
308
 
            prev_line_iter = self.get_buffer().get_iter_at_line(cur_line)
309
 
            pl_offset = prev_line_iter.get_offset()
310
 
            pl_text = self.get_buffer().get_text(prev_line_iter, iter)
311
 
            if pl_text.strip().find("*") == 0:
312
 
                ws = ""
313
 
                if not pl_text.startswith("*"):
314
 
                    ws = (pl_text.split("*")[0])
315
 
                self._auto_bullet = ws + "* "
316
 
                
317
 
    def _on_delete(self, text_buffer, start_iter, end_iter, data=None):
318
 
        """_on_insert: internal function to handle delete
319
 
        text. Do not call directly.
320
 
 
321
 
        """
322
 
 
323
 
        self._highlight()
324
 
        text = self.get_buffer().get_text(start_iter,end_iter)        
325
 
        cmd = {"action":"insert","offset":start_iter.get_offset(),"text":text}
326
 
        self._add_undo(cmd)
327
 
 
328
 
    def _add_undo(self, cmd):
329
 
        """internal function to capture current state to add to undo stack.
330
 
        Do not call directly.
331
 
 
332
 
        """
333
 
 
334
 
        #delete the oldest undo if undo maximum is in effect
335
 
        if self.undo_max is not None and len(self.undos) >= self.undo_max:
336
 
            del(self.undos[0])
337
 
        self.undos.append(cmd)
338
 
 
339
 
class TestWindow(gtk.Window):
340
 
    """For testing and demonstrating AsycnTaskProgressBox.
341
 
 
342
 
    """
343
 
    def __init__(self):
344
 
        #create a window a VBox to hold the controls
345
 
        gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
346
 
        self.set_title("TextEditor Test Window")
347
 
        windowbox = gtk.VBox(False, 2)
348
 
        windowbox.show()
349
 
        self.add(windowbox)
350
 
        self.editor = TextEditor()
351
 
        self.editor.show()
352
 
        windowbox.pack_end(self.editor)
353
 
        self.set_size_request(200,200)
354
 
        self.show()
355
 
        self.maximize()
356
 
  
357
 
        self.connect("destroy", gtk.main_quit)
358
 
        self.editor.text = "this is some inserted text"
359
 
        self.editor.append("\nLine 3")
360
 
        self.editor.prepend("Line1\n")
361
 
        self.editor.cursor_to_end()
362
 
        self.editor.cursor_to_start()
363
 
        self.editor.add_highlight("his")
364
 
        self.editor.clear_all_highlights()
365
 
        self.editor.add_highlight("some")
366
 
        self.editor.undo_max = 100
367
 
        self.editor.auto_bullets = True
368
 
        cut_button = gtk.Button("Cut")
369
 
        cut_button.connect("clicked",self.editor.cut)
370
 
        cut_button.show()
371
 
        windowbox.pack_start(cut_button, False)
372
 
 
373
 
        copy_button = gtk.Button("Copy")
374
 
        copy_button.connect("clicked",self.editor.copy)
375
 
        copy_button.show()
376
 
        windowbox.pack_start(copy_button, False)
377
 
 
378
 
        paste_button = gtk.Button("Paste")
379
 
        paste_button.connect("clicked",self.editor.paste)
380
 
        paste_button.show()
381
 
        windowbox.pack_start(paste_button, False)
382
 
 
383
 
        undo_button = gtk.Button("Undo")
384
 
        undo_button.connect("clicked",self.editor.undo)
385
 
        undo_button.show()
386
 
        windowbox.pack_start(undo_button, False)
387
 
 
388
 
        redo_button = gtk.Button("Redo")
389
 
        redo_button.connect("clicked",self.editor.redo)
390
 
        redo_button.show()
391
 
        windowbox.pack_start(redo_button, False)
392
 
 
393
 
        print self.editor.text
394
 
 
395
 
 
396
 
if __name__== "__main__":
397
 
    test = TestWindow()
398
 
    gtk.main()
399