findbar.py 6.32 KB
Newer Older
1
### Copyright (C) 2002-2009 Stephen Kennedy <stevek@gnome.org>
2
### Copyright (C) 2012 Kai Willadsen <kai.willadsen@gmail.com>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation; either version 2 of the License, or
### (at your option) any later version.

### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU General Public License for more details.

### You should have received a copy of the GNU General Public License
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import gnomeglade
19 20
from meld import paths
from meld import misc
21 22
import gtk
import re
23
from gettext import gettext as _
24 25

class FindBar(gnomeglade.Component):
26
    def __init__(self, parent):
27 28
        gnomeglade.Component.__init__(self, paths.ui_dir("findbar.ui"),
                                      "findbar", ["arrow_left", "arrow_right"])
29 30
        gnomeglade.connect_signal_handlers(self)
        self.textview = None
31
        self.orig_base_color = self.find_entry.get_style().base[0]
32 33
        self.arrow_left.show()
        self.arrow_right.show()
34 35 36 37 38 39 40
        parent.connect('set-focus-child', self.on_focus_child)

    def on_focus_child(self, container, widget):
        if widget is not None:
            if widget is not self.widget and self.widget.get_visible():
                self.hide()
        return False
41 42 43 44 45

    def hide(self):
        self.textview = None
        self.widget.hide()

46
    def start_find(self, textview, text=None):
47 48 49
        self.textview = textview
        self.replace_label.hide()
        self.replace_entry.hide()
50
        self.hbuttonbox2.hide()
51 52
        if text:
            self.find_entry.set_text(text)
53
        self.widget.set_row_spacings(0)
54 55 56 57 58 59
        self.widget.show()
        self.find_entry.grab_focus()

    def start_find_next(self, textview):
        self.textview = textview
        if self.find_entry.get_text():
60
            self.find_next_button.activate()
61 62 63
        else:
            self.start_find(self.textview)

64
    def start_find_previous(self, textview, text=None):
65 66 67 68 69 70
        self.textview = textview
        if self.find_entry.get_text():
            self.find_previous_button.activate()
        else:
            self.start_find(self.textview)

71
    def start_replace(self, textview, text=None):
72
        self.textview = textview
73 74
        if text:
            self.find_entry.set_text(text)
75
        self.widget.set_row_spacings(6)
76 77 78 79
        self.widget.show_all()
        self.find_entry.grab_focus()

    def on_find_entry__activate(self, entry):
80
        self.find_next_button.activate()
81 82 83 84

    def on_replace_entry__activate(self, entry):
        self.replace_button.activate()

85
    def on_find_next_button__clicked(self, button):
86 87
        self._find_text()

88 89 90
    def on_find_previous_button__clicked(self, button):
        self._find_text(backwards=True)

91
    def on_replace_button__clicked(self, entry):
92
        buf = self.textview.get_buffer()
93 94 95 96 97
        oldsel = buf.get_selection_bounds()
        match = self._find_text(0)
        newsel = buf.get_selection_bounds()
        # only replace if there is a match at the cursor and it was already selected
        if match and oldsel and oldsel[0].equal(newsel[0]) and oldsel[1].equal(newsel[1]):
98 99 100 101 102 103
            buf.begin_user_action()
            buf.delete_selection(False,False)
            buf.insert_at_cursor( self.replace_entry.get_text() )
            self._find_text( 0 )
            buf.end_user_action()

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    def on_replace_all_button__clicked(self, entry):
        buf = self.textview.get_buffer()
        saved_insert = buf.create_mark(None, buf.get_iter_at_mark(buf.get_insert()), True)
        buf.begin_user_action()
        while self._find_text(0):
            buf.delete_selection(False,False)
            buf.insert_at_cursor( self.replace_entry.get_text() )
        buf.end_user_action()
        if not saved_insert.get_deleted():
            buf.place_cursor( buf.get_iter_at_mark(saved_insert) )
            self.textview.scroll_to_mark(buf.get_insert(), 0.25)

    def on_find_entry__changed(self, entry):
        entry.modify_base( gtk.STATE_NORMAL, self.orig_base_color )

119 120 121
        #
        # find/replace buffer
        #
122
    def _find_text(self, start_offset=1, backwards=False, wrap=True):
123 124 125 126 127 128 129 130
        match_case = self.match_case.get_active()
        whole_word = self.whole_word.get_active()
        regex = self.regex.get_active()
        assert self.textview
        buf = self.textview.get_buffer()
        insert = buf.get_iter_at_mark( buf.get_insert() )
        tofind_utf8 = self.find_entry.get_text()
        tofind = tofind_utf8.decode("utf-8") # tofind is utf-8 encoded
131 132
        start, end = buf.get_bounds()
        text = buf.get_text(start, end, False).decode("utf-8") # as is buffer
133 134 135 136 137 138 139 140 141
        if not regex:
            tofind = re.escape(tofind)
        if whole_word:
            tofind = r'\b' + tofind + r'\b'
        try:
            pattern = re.compile( tofind, (match_case and re.M or (re.M|re.I)) )
        except re.error, e:
            misc.run_dialog( _("Regular expression error\n'%s'") % e, self, messagetype=gtk.MESSAGE_ERROR)
        else:
142 143
            if backwards == False:
                match = pattern.search(text, insert.get_offset() + start_offset)
144
                if match is None and wrap:
145 146 147 148 149
                    match = pattern.search(text, 0)
            else:
                match = None
                for m in pattern.finditer(text, 0, insert.get_offset()):
                    match = m
150
                if match is None and wrap:
151 152
                    for m in pattern.finditer(text, insert.get_offset()):
                        match = m
153 154 155 156 157 158
            if match:
                it = buf.get_iter_at_offset( match.start() )
                buf.place_cursor( it )
                it.forward_chars( match.end() - match.start() )
                buf.move_mark( buf.get_selection_bound(), it )
                self.textview.scroll_to_mark(buf.get_insert(), 0.25)
159
                return True
160
            else:
161 162
                buf.place_cursor( buf.get_iter_at_mark(buf.get_insert()) )
                self.find_entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffdddd"))