root/branch/max/pysourceview/pysourceview.py @ 929

Revision 929, 26.9 kB (checked in by maxim-gavrilov, 6 years ago)

unindent

Line 
1import pygtk
2pygtk.require('2.0')
3
4import gettext
5gettext.install('pysourceview')
6
7import gtk
8from gtk import TextView
9from gtk import gdk
10import pango
11import gobject
12import pango
13
14# defines
15GUTTER_PIXMAP = 16
16
17TARGET_COLOR = 200 # DnD color
18
19MIN_NUMBER_WINDOW_WIDTH = 20
20
21DEFAULT_TAB_WIDTH = 8
22MAX_TAB_WIDTH = 32
23
24DEFAULT_MARGIN = 80
25MAX_MARGIN = 200
26
27# main view class
28class SourceView(TextView):
29    __gproperties__ = {
30        "show_line_numbers" : (gobject.TYPE_BOOLEAN,
31                               _("Show Line Numbers"),
32                               _("Whether to display line numbers"),
33                               False,
34                               gobject.PARAM_READWRITE),
35       
36        "show_line_markers" : (gobject.TYPE_BOOLEAN,
37                               _("Show Line Markers"),
38                               _("Whether to display line marker pixbufs"),
39                               False,
40                               gobject.PARAM_READWRITE),
41
42        "tabs_width" : (gobject.TYPE_UINT,
43                        _("Tabs Width"),
44                        _("Tabs Width"),
45                        1,
46                        MAX_TAB_WIDTH,
47                        DEFAULT_TAB_WIDTH,
48                        gobject.PARAM_READWRITE),
49       
50        "auto_indent" : (gobject.TYPE_BOOLEAN,
51                         _("Auto Indentation"),
52                         _("Whether to enable auto indentation"),
53                         False,
54                         gobject.PARAM_READWRITE),
55
56        "insert_spaces_instead_of_tabs": (gobject.TYPE_BOOLEAN,
57                                          _("Insert Spaces Instead of Tabs"),
58                                          _("Whether to insert spaces instead of tabs"),
59                                          False,
60                                          gobject.PARAM_READWRITE),
61
62        "show_margin" : (gobject.TYPE_BOOLEAN,
63                         _("Show Right Margin"),
64                         _("Whether to display the right margin"),
65                         False,
66                         gobject.PARAM_READWRITE),
67
68        "margin" : (gobject.TYPE_UINT,
69                    _("Margin position"),
70                    _("Position of the right margin"),
71                    1,
72                    MAX_MARGIN,
73                    DEFAULT_MARGIN,
74                    gobject.PARAM_READWRITE),
75
76        "smart_home_end" : (gobject.TYPE_BOOLEAN,
77                            _("Smart Home/End"),
78                            _("HOME and END keys move to first/last "
79                              "non whitespace chapters on line before going "
80                              "to the start/end of the line"),
81                            False,
82                            gobject.PARAM_READWRITE),
83
84        "highlight_current_line" : (gobject.TYPE_BOOLEAN,
85                                    _("Highlight current line"),
86                                    _("Whether to highlight the current line"),
87                                    False,
88                                    gobject.PARAM_READWRITE),
89
90        "indent_on_tab" : (gobject.TYPE_BOOLEAN,
91                           _("Indent on tab"),
92                           _("Whether to indent the selected text when the tab key is pressed"),
93                           True,
94                           gobject.PARAM_READWRITE)
95    }
96
97   
98    def __init__(self, buffer=None):
99        TextView.__init__(self, buffer)
100       
101        self.tabs_width = DEFAULT_TAB_WIDTH
102        self.margin = DEFAULT_MARGIN;
103        self.cached_margin_width = -1
104        self.indent_on_tab = True
105        self.smart_home_end = False
106        self.set_left_margin(2)
107        self.set_right_margin(2)
108
109        # create members
110        self.show_line_numbers = False
111        self.show_line_markers = False
112        self.auto_indent = False
113        self.insert_spaces = False
114        self.show_margin = False
115        self.highlight_current_line = False
116        self.source_buffer = None
117        self.source_buffer_handler_id = []
118        self.old_lines = 0 # number of lines
119        self.current_line_gc = None
120
121        self._set_source_buffer(self.get_buffer())
122
123        # TODO: not yet implemented
124        #self.pixmap_cache = dict()
125        #self.style_scheme_applied = False
126        #self.style_scheme = None
127
128        # DnD init
129        #tl = self.drag_dest_get_target_list()
130        #if tl:
131        #    tl.append(("application/x-color", 0, TARGET_COLOR))
132        #    self.connect("drag_data_received", self.view_dnd_drop)
133        #    self.connect("notify::buffer", self.notify_buffer0
134       
135    def set_show_line_numbers(self, show):
136        if show and not self.show_line_numbers:
137            if not self.show_line_markers:
138                self.set_border_window_size(gtk.TEXT_WINDOW_LEFT, MIN_NUMBER_WINDOW_WIDTH)
139            else:
140                self.queue_draw()
141            self.show_line_numbers = show
142            self.notify("show_line_numbers")
143        if not show and self.show_line_numbers:
144            self.queue_draw()
145            self.show_line_numbers = show
146            self.notify("show_line_numbers")
147
148    def get_show_line_numbers(self):
149        return self.show_line_numbers
150
151    def set_show_line_markers(self, show):
152        if show and not self.show_line_markers:
153            if not self.show_line_numbers:
154                self.set_border_window_size(gtk.TEXT_WINDOW_LEFT, MIN_NUMBER_WINDOW_WIDTH)
155            else:
156                self.queue_draw()
157            self.show_line_markers = show
158            self.notify("show_line_markers")
159        if not show and self.show_line_markers:
160            self.queue_draw()
161            self.show_line_markers = show
162            self.notify("show_line_markers")
163
164    def get_show_line_markers(self):
165        return self.show_line_markers
166
167    def set_tabs_width(self, width):
168        if width <= 0 or width > MAX_TAB_WIDTH or self.tabs_width == width:
169            return
170        save_width = self.tabs_width
171        self.tabs_width = width
172        if self._set_tab_stops_internal():
173            self.notify("tabs_width")
174        else:
175            self.tabs_width = save_width
176
177    def get_tabs_width(self):
178        return self.tabs_width
179
180    def set_auto_indent(self, enable):
181        if self.auto_indent != enable:
182            self.auto_indent = enable
183            self.notify("auto_indent")
184           
185    def get_auto_indent(self):
186        return self.auto_indent
187
188    def set_insert_spaces_instead_of_tabs(self, enable):
189        if self.insert_spaces != enable:
190            self.insert_spaces = enable
191            self.notify("insert_spaces_instead_of_tabs")
192
193    def get_insert_spaces_instead_of_tabs(self):
194        return self.insert_spaces
195
196    def set_show_margin(self, show):
197        if self.show_margin != show:
198            self.show_margin = show
199            self.queue_draw()
200            self.notify("show_margin")
201
202    def get_show_margin(self):
203        return self.show_margin
204
205    def set_margin(self, margin):
206        if margin < 1 or margin > MAX_MARGIN or self.margin == margin:
207            return
208        self.margin = margin
209        self.cached_margin_width = -1
210        self.queue_draw()
211        self.notify("margin")
212
213    def get_margin(self):
214        return self.margin
215
216    def set_smart_home_end(self, enable):
217        if not self.smart_home_end == enable:
218            self.smart_home_end = enable
219            self.notify("smart_home_end")
220
221    def get_smart_home_end(self):
222        return self.smart_home_end
223
224    def set_highlight_current_line(self, enable):
225        if not self.highlight_current_line == enable:
226            self.highlight_current_line = enable
227            self.queue_draw()
228            self.notify("highlight_current_line")
229   
230    def get_highlight_current_line(self):
231        return self.highlight_current_line
232
233    property_map = {
234        'show-line-numbers': (set_show_line_numbers, get_show_line_numbers),
235        'show-line-marker': (set_show_line_markers, get_show_line_markers),
236        'tabs-width': (set_tabs_width, get_tabs_width),
237        'auto-indent': (set_auto_indent, get_auto_indent),
238        'insert-spaces-instead-of-tabs': (set_insert_spaces_instead_of_tabs, get_insert_spaces_instead_of_tabs),
239        'show-margin': (set_show_margin, get_show_margin),
240        'margin': (set_margin, get_margin),
241        'smart-home-end': (set_smart_home_end, get_smart_home_end),
242        'highlight-current-line': (set_highlight_current_line, get_highlight_current_line)
243        }
244   
245    def do_get_property(self, property):
246        if property.name in self._property_map:
247            return self._property_map[property.name]
248        raise AttributeError, 'unknown property %s' % property.name
249       
250    def do_set_property(self, property, value):
251        if property.name in self._property_map:
252            return self._property_map[property.name]
253        raise AttributeError, 'unknown property %s' % property.name
254       
255    # aux
256    def _set_tab_stops_internal(self):
257        real_tab_width = self._calculate_real_tab_width(self.tabs_width, ' ')
258        if real_tab_width < 0:
259            return False
260        tab_array = pango.TabArray(1, True)
261        tab_array.set_tab(0, pango.TAB_LEFT, real_tab_width)
262        self.set_tabs(tab_array)
263        return True
264
265    def _calculate_real_tab_width(self, tab_size, c):
266        if tab_size == 0:
267            return -1
268        tab_string = c * tab_size
269        layout = self.create_pango_layout(tab_string)
270        if layout:
271            return layout.get_pixel_size()[0]
272        return -1
273
274    def _set_source_buffer(self, buffer):
275        if self.source_buffer == buffer:
276            return
277        if self.source_buffer:
278            for handler_id in self.source_buffer_handler_id:
279                self.source_buffer.disconnect(handler_id)
280        if buffer and type(buffer) == SourceBuffer:
281            self.source_buffer_handler_id = [
282                self.connect("highlight_updated", self._highlight_updated_cb),
283                self.connect("marker-updated", self._marker_updated_cb),
284                self.connect("notify::style_scheme", self._buffer_style_scheme_changed_cb),
285            ]
286        else:
287            self.source_buffer = None
288            self.source_buffer_handler_id = []
289
290        if buffer:
291            self._update_style_scheme()
292
293    def _highlight_updated_cb(self, buffer, start, end):
294        visible_rect = self.get_visible_rect()
295        (y, height) = self.get_line_yrange(start)
296        updated_rect.y = y
297        (y, height) = self.get_line_yrange(end)
298        updated_rect.height = y + height - updated_rect.y
299        updated_rect.x = visible_rect.x
300        updated_rect.width = visible_rect.width
301
302        redraw_rect = updated_rect.intersect(visible_rect)
303        if not tuple(redraw_rect) == (0, 0, 0, 0):
304            (x, y) = self.buffer_to_window_coord(gtk.TEXT_WINDOW_WIDGET, redraw_rect.x, redraw_rect.y)
305            widget_rect = gdk.Rectangle(0, 0, redraw_rect.width, redraw_rect.height)
306            self.query_draw_area(widget_rect.x, widget_rect.y, widget_rect.width, widget_rect.height)
307
308    def _marker_updated_cb(self, buffer, where):
309        if not self.show_line_markers:
310            return
311        visible_rect = self.get_visible_rect()
312        (y, height) = self.get_line_yrange(where)
313        updated_rect = gdk.Rectangle(visible_rect.x, y, visible_rect.width, height)
314        redraw_rect = updated_rect.intersect(visible_rect)
315        if not tuple(redraw_rect) == (0, 0, 0, 0):
316            y_win = self.buffer_to_window_coord(gtk.TEXT_WINDOW_WIDGET, 0, redraw_rect.y)[1]
317            width = self.get_border_window_size(gtk.TEXT_WINDOW_LEFT)
318            self.query_draw_area(0, y_win, width, height)
319       
320    def _buffer_style_scheme_changed_cb(self, buffer, pspec):
321        self._update_style_scheme()
322
323    def _update_style_scheme(self):
324        # TODO: not yet implemented
325        pass
326        #buffer = self.get_buffer()
327        #if type(buffer) == SourceBuffer:
328        #    new_scheme = buffer.get_style_scheme()
329        #else:
330        #    new_scheme = None
331
332    def do_key_press_event(self, event):
333        buf = self.get_buffer()
334        modifiers = gtk.accelerator_get_default_mod_mask()
335        key = event.keyval
336        mark = buf.get_insert()
337        cur = buf.get_iter_at_mark(mark)
338
339        if key in [gdk.keyval_from_name(name) for name in ['Enter', 'Return']] and \
340           not event.state & gdk.SHIFT_MASK and \
341           self.auto_indent:
342            indent = self._compute_indentation(cur)
343            if indent:
344                # TODO: find im_context
345                #if self.im_context.filter_keypress(event):
346                #    return True
347                cur = buf.get_iter_at_mark(mark)
348                buf.begin_user_action()
349                buf.insert(cur, "\n")
350                buf.insert(cur, indent)
351                buf.end_user_action()
352                self.scroll_mark_onscreen(mark)
353                return True
354
355        if key in [gdk.keyval_from_name(name) for name in ['Tab', 'KP_Tab', 'ISO_Left_Tab']] and \
356           (event.state & modifiers) in (0, gdk.SHIFT_MASK):
357            selection = buf.get_selection_bounds()
358            has_selection = bool(selection)
359            if not has_selection:
360                it = buf.get_iter_at_mark(buf.get_insert())
361                selection = (it, it.copy())
362           
363            if self.indent_on_tab:
364                if event.state & gdk.SHIFT_MASK:
365                    self._unindent_lines(selection)
366                    return True
367                if has_selection and \
368                   ((selection[0].starts_line() and selection[1].ends_line()) or \
369                    (selection[0].get_line() != selection[1].get_line())):
370                    self._indent_lines(selection)
371                    return True
372            self._insert_tab_or_spaces(selection)
373            return True
374        TextView.do_key_press_event(self, event)
375
376    def _unindent_lines(self, (start, end)):
377        buf = self.get_buffer()
378        start_line = start.get_line()
379        end_line = end.get_line()
380
381        if end.get_visible_line_offset() == 0 and end_line > start_line:
382            end_line -= 1
383
384        buf.begin_user_action()
385        for i in range(start_line, end_line + 1):
386            it = buf.get_iter_at_line(i)
387            if it.get_char == '\t':
388                it2 = it.copy()
389                it2.forward_char()
390                buf.delete(it, it2)
391            elif it.get_char() == ' ':
392                spaces = 0
393                it2 = it.copy()
394                while not it2.ends_line():
395                    if it2.get_char() == ' ':
396                        spaces += 1
397                    else:
398                        break
399                    it2.forward_char()
400                if spaces > 0:
401                    spaces = spaces % self.tabs_width
402                    if spaces == 0:
403                        spaces = self.tabs_width
404                    it2 = it.copy()
405                    it2.forward_chars(spaces)
406                    buf.delete(it, it2)
407        buf.end_user_action()
408        self.scroll_mark_onscreen(buf.get_insert())
409
410
411    def _compute_indentation(self, cur):
412        line = cur.get_line()
413        buf = self.get_buffer()
414        start = buf.get_iter_at_line(line)
415        end = start.copy()
416        ch = end.get_char()
417
418        while ch.isspace() and not ch in ('\n', '\r') and end.compare(cur) < 0:
419            if not end.forward_char():
420                break
421            ch = end.get_char()
422
423        if start.equal(end):
424            return None
425
426        res = start.get_slice(end)
427        return res
428
429    def _indent_lines(self, (start, end)):
430        buf = self.get_buffer()
431
432        start_line = start.get_line()
433        end_line = end.get_line()
434
435        if end.get_visible_line_offset() == 0 and end_line > start_line:
436            end_line -= 1
437
438        if self.get_insert_spaces_instead_of_tabs():
439            tabs_size = self.tabs_width
440            tab_buffer = ' ' * tabs_size
441        else:
442            tab_buffer = "\t"
443
444        buf.begin_user_action()
445        for i in xrange(start_line, end_line + 1):
446            cur = buf.get_iter_at_line(i)
447            if cur.ends_line():
448                continue
449            buf.insert(cur, tab_buffer, -1)
450        buf.end_user_action()
451
452        self.scroll_mark_onscreen(buf.get_insert())
453       
454    def _insert_tab_or_spaces(self, (start, end)):
455        if self.insert_spaces:
456            tabs_size = self.tabs_width
457            cur = start.copy()
458            cur_pos = cur.get_line_offset()
459            tab_pos = cur_pos
460            while tab_pos > 0:
461                cur.backward_char()
462                c = cur.get_char()
463                if c == '\t':
464                    break
465                tab_pos -= 1
466            num_of_equivalent_spaces = tabs_size - (cur_pos - tab_pos) % tabs_size
467            tab_buf = ' ' * num_of_equivalent_spaces
468        else:
469            tab_buf = '\t'
470
471        buf = self.get_buffer()
472        buf.begin_user_action()
473        buf.delete(start, end)
474        buf.insert(start, tab_buf, -1)
475        buf.end_user_action()
476
477    def do_move_cursor(self, step, count, extend_selection):
478        buffer = self.get_buffer()
479        mark = buffer.get_insert()
480        cur = buffer.get_iter_at_mark(mark)
481        it = cur.copy()
482
483        if self.smart_home_end and  step == gtk.MOVEMENT_DISPLAY_LINE_ENDS and count == -1:
484            #move_to_first_char
485            it.set_line_offset(0)
486            while not it.ends_line():
487                if it.get_char().isspace():
488                    it.forward_char()
489                else:
490                    break
491            self._do_cursor_move(cur, it, extend_selection)
492            return
493        elif self.smart_home_end and step == gtk.MOVEMENT_DISPLAY_LINE_ENDS and count == 1:
494            #move_to_last_char
495            if not it.ends_line():
496                it.forward_to_line_end()
497            while not it.starts_line():
498                it.backward_char()
499                if not it.get_char().isspace():
500                    it.forward_char()
501                    break
502            self._do_cursor_move(cur, it, extend_selection)
503            return
504        TextView.do_move_cursor(self, step, count, extend_selection)
505               
506    def _do_cursor_move(self, cur, it, extend_selection):
507        buffer = self.get_buffer()
508        if not cur.equal(it) or not extend_selection:
509            # move_cursor
510            if extend_selection:
511                buffer.move_mark_by_name("insert", it)
512            else:
513                buffer.place_cursor(it)
514            self.scroll_mark_onscreen(buffer.get_insert())
515
516    def do_expose_event(self, event):
517        event_handled = False
518        # update highlight
519        if event.window == self.get_window(gtk.TEXT_WINDOW_TEXT) and not self.source_buffer is None:
520            visible_rect = self.get_visible_rect()
521            iter1 = self.get_line_at_y(visible_rect.y)[0]
522            iter1.backward_line()
523            iter2 = self.get_line_at_y(visible_rect.y + visible_rect.height)[0]
524            iter2.forward_line()
525            self.source_buffer.update_highlight(iter1, iter2, False)
526
527        if event.window == self.get_window(gtk.TEXT_WINDOW_LEFT):
528            self._paint_margin(event)
529            event_handled = True
530        else:
531            buffer =self.get_buffer()
532            lines = buffer.get_line_count()
533            if self.old_lines != lines:
534                self.old_lines = lines
535                w = self.get_window(gtk.TEXT_WINDOW_LEFT)
536                if not w is None:
537                    w.invalidate_rect(None, False)
538
539            if self.highlight_current_line and event.window == self.get_window(gtk.TEXT_WINDOW_TEXT):
540                cur = buffer.get_iter_at_mark(buffer.get_insert())
541                (y, height) = self.get_line_yrange(cur)
542                visible_rect = self.get_visible_rect()
543                (redraw_x, redraw_y) = self.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT,
544                                                                              visible_rect.x, visible_rect.y)
545                (win_x, win_y) = self.buffer_to_window_coords(gtk.TEXT_WINDOW_TEXT, 0, y)
546                redraw_rect = gdk.Rectangle(redraw_x, redraw_y, visible_rect.width, visible_rect.height)
547
548                if self.current_line_gc:
549                    gc = self.current_line_gc
550                else:
551                    gc = self.style.bg_gc[self.state]
552
553                if self.get_focus_hadjustment():
554                    margin = self.get_left_margin() - int(self.get_focus_hadjustment())
555                else:
556                    margin = self.get_left_margin()
557
558                event.window.draw_rectangle(gc, True, redraw_rect.x + max(0, margin - 1),
559                                   win_y, redraw_rect.width, height)
560
561            event_handled = TextView.do_expose_event(self, event)
562
563            if self.show_margin and event.window == self.get_window(gtk.TEXT_WINDOW_TEXT):
564                if self.cached_margin_width < 0:
565                    self.cached_margin_width = self._calculate_real_tab_width(self.margin, '_')
566                visible_rect = self.get_visible_rect()
567                redraw_x, redraw_y = self.buffer_to_window_coords(gtk.TEXT_WINDOW_LEFT,
568                                                                  visible_rect.x, visible_rect.y)
569                redraw_rect = gdk.Rectangle(redraw_x, redraw_y, visible_rect.width, visible_rect.height)
570                cr = self.get_window(gtk.TEXT_WINDOW_TEXT).cairo_create()
571                cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height)
572                cr.clip()
573                x = self.cached_margin_width - \
574                    visible_rect.x + redraw_rect.x + 0.5 + \
575                    self.get_left_margin()
576                cr.set_line_width(1.0)
577                cr.move_to(x, redraw_rect.y)
578                cr.line_to(x, redraw_rect.y + redraw_rect.height)
579                # TODO: get style properties
580                alpha = 40
581                line_color = 0
582                toggle = False
583
584                if not line_color:
585                    line_color = self.style.text[gtk.STATE_NORMAL]
586                cr.set_source_rgba(line_color.red / 65535.,
587                                   line_color.green / 65535.,
588                                   line_color.blue / 65535.,
589                                   alpha / 255.)
590                cr.stroke()
591                if toggle:
592                    # TODO: overlay draw
593                    pass
594
595       
596        return event_handled
597
598    def do_button_press_event(self, event):
599        buf = self.get_buffer()
600        if self.show_line_numbers and event.window == self.get_window(gtk.TEXT_WINDOW_LEFT):
601            (x_buf, y_buf) = self.window_to_buffer_coord(gtk.TEXT_WINDOW_LEFT, event.x, event.y)
602            line_start = self.get_line_at_y(y_buf)[0]
603            if event.type == gdk.BUTTON_PRESS and event.button == 1:
604                if event.state & gdk.CONTROL_MASK:
605                    self._select_line(buf, line_start)
606                elif event.state & gdk.SHIFT_MASK:
607                    self._extend_selection_to_line(buf, line_start)
608                else:
609                    buf.place_cursor(line_start)
610            elif event.type == gdk._2BUTTON_PRESS and event.button == 1:
611                self._select_line(buf, line_start)
612            return True
613        return TextView.do_button_press_event(self, event)
614
615    def _paint_margin(self, event):
616        if not self.show_line_numbers and not self.show_line_markers:
617            self.set_border_window_size(gtk.TEXT_WINDOW_LEFT, 0)
618            return
619        win = self.get_window(gtk.TEXT_WINDOW_LEFT)
620        buf = self.get_buffer()
621        y1 = event.area.y
622        y2 = y1 + event.area.height
623        (x1, y1) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_LEFT, 0, y1)
624        (x2, y2) = self.window_to_buffer_coords(gtk.TEXT_WINDOW_LEFT, 0, y2)
625
626        (numbers, pixels) = self._get_lines(y1, y2)
627
628        if len(numbers) == 0:
629            numbers.append(0)
630            pixels.append(0)
631
632        tmp = str(max(99, buf.get_line_count()))
633        layout = self.create_pango_layout(tmp)
634        text_width = layout.get_pixel_size()[0]
635        layout.set_width(text_width)
636        layout.set_alignment(pango.ALIGN_RIGHT)
637
638        if self.show_line_numbers:
639            margin_width = text_width + 4
640        else:
641            margin_width = 0
642        x_pixmap = margin_width
643        if self.show_line_markers:
644            margin_width += GUTTER_PIXMAP
645        if margin_width == 0:
646            return
647
648        self.set_border_window_size(gtk.TEXT_WINDOW_LEFT, margin_width)
649        markers = None
650        if self.source_buffer and self.show_line_markers:
651            # TODO: not yet implemented
652            pass
653
654        #current_marker = markers
655        #if current_marker:
656        #    marker_line = current_marker.data.get_line()
657
658        cur = buf.get_iter_at_mark(buf.get_insert())
659        cur_line = cur.get_line() + 1
660        for number, pixel in zip(numbers, pixels):
661            pos = self.buffer_to_window_coords(gtk.TEXT_WINDOW_LEFT, 0, pixel)[1]
662            if self.show_line_numbers:
663                line_to_paint = number + 1
664                if line_to_paint == cur_line:
665                    layout.set_markup("<b>%d</b>" % line_to_paint)
666                else:
667                    layout.set_markup("%d" % line_to_paint)
668                self.style.paint_layout(win, self.state, False, None, self, None, text_width + 2, pos, layout)
669
670            if self.show_line_markers and current_marker and marker_line == number:
671                # TODO: _draw_line_markers
672                pass
673       
674
675    def _get_lines(self, first_y, last_y):
676        buffer_coords = []
677        numbers = []
678        last_line_num = -1
679        it = self.get_line_at_y(first_y)[0]
680
681        while not it.is_end():
682            (y, height) = self.get_line_yrange(it)
683            buffer_coords.append(y)
684            last_line_num = it.get_line()
685            numbers.append(last_line_num)
686            if y + height >= last_y:
687                break
688            it.forward_line()
689
690        if it.is_end():
691            (y, height) = self.get_line_yrange(it)
692            line_num = it.get_line()
693            if line_num != last_line_num:
694                buffer_coords.append(y)
695                numbers.append(line_num)
696
697        return (numbers, buffer_coords)
698           
699           
700
701class SourceBuffer(gtk.TextBuffer):
702    pass
703
704gobject.type_register(SourceView)
705
706if __name__ == "__main__":
707    import gtk
708    class TestApp:
709        def destroy(self, widget, data=None):
710            gtk.main_quit()
711
712        def __init__(self):
713            self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
714            self.window.set_size_request(400, 400)
715            self.window.connect("destroy", self.destroy)
716            self.textview = SourceView()
717            self.textview.set_tabs_width(10)
718            self.textview.set_auto_indent(True)
719            self.textview.set_insert_spaces_instead_of_tabs(True)
720            self.textview.set_smart_home_end(True)
721            self.textview.set_highlight_current_line(True)
722            self.textview.set_show_line_numbers(True)
723            self.textview.set_margin(80)
724            self.textview.set_show_margin(True)
725            self.window.add(self.textview)
726            self.window.show_all()
727
728    app = TestApp()
729    gtk.main()
730
Note: See TracBrowser for help on using the browser.