Ejemplo n.º 1
0
    def update_search(self):
        self._search_timeout = 0
        from translate.tools.pogrep import GrepFilter
        self.filter = GrepFilter(
            searchstring=unicode(self.ent_search.get_text()),
            searchparts=('source', 'target'),
            ignorecase=not self.chk_casesensitive.get_active(),
            useregexp=self.chk_regex.get_active(),
            max_matches=self.MAX_RESULTS)
        store_units = self.storecursor.model.get_units()
        self.matches, indexes = self.filter.getmatches(store_units)
        self.matchcursor = Cursor(self.matches, range(len(self.matches)))

        logging.debug('Search text: %s (%d matches)' %
                      (self.ent_search.get_text(), len(indexes)))

        if indexes:
            self.ent_search.modify_base(gtk.STATE_NORMAL, self.default_base)
            self.ent_search.modify_text(gtk.STATE_NORMAL, self.default_text)

            self.storecursor.indices = indexes
            # Select initial match for in the current unit.
            match_index = 0
            selected_unit = self.storecursor.model[self.storecursor.index]
            for match in self.matches:
                if match.unit is selected_unit:
                    break
                match_index += 1
            self.matchcursor.index = match_index
        else:
            if self.ent_search.get_text():
                self.ent_search.modify_base(
                    gtk.STATE_NORMAL,
                    gtk.gdk.color_parse(current_theme['warning_bg']))
                self.ent_search.modify_text(gtk.STATE_NORMAL,
                                            gtk.gdk.color_parse('#fff'))
            else:
                self.ent_search.modify_base(gtk.STATE_NORMAL,
                                            self.default_base)
                self.ent_search.modify_text(gtk.STATE_NORMAL,
                                            self.default_text)

            self.filter.re_search = None
            # Act like the "Default" mode...
            self.storecursor.indices = self.storecursor.model.stats['total']
        self._highlight_matches()

        def grabfocus():
            curpos = self.ent_search.props.cursor_position
            self.ent_search.grab_focus()
            # that will select all text, so reset the cursor position
            self.ent_search.set_position(curpos)
            return False

        gobject.idle_add(grabfocus)
Ejemplo n.º 2
0
    def update_search(self):
        from translate.tools.pogrep import GrepFilter

        self.filter = GrepFilter(
            searchstring=unicode(self.ent_search.get_text()),
            searchparts=("source", "target"),
            ignorecase=not self.chk_casesensitive.get_active(),
            useregexp=self.chk_regex.get_active(),
            max_matches=self.MAX_RESULTS,
        )
        store_units = self.storecursor.model.get_units()
        self.matches, indexes = self.filter.getmatches(store_units)
        self.matchcursor = Cursor(self.matches, range(len(self.matches)))

        logging.debug("Search text: %s (%d matches)" % (self.ent_search.get_text(), len(indexes)))

        if indexes:
            self.ent_search.modify_base(gtk.STATE_NORMAL, self.default_base)
            self.ent_search.modify_text(gtk.STATE_NORMAL, self.default_text)

            self.storecursor.indices = indexes
            # Select initial match for in the current unit.
            match_index = 0
            selected_unit = self.storecursor.model[self.storecursor.index]
            for match in self.matches:
                if match.unit is selected_unit:
                    break
                match_index += 1
            self.matchcursor.index = match_index
        else:
            if self.ent_search.get_text():
                self.ent_search.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(current_theme["warning_bg"]))
                self.ent_search.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse("#fff"))
            else:
                self.ent_search.modify_base(gtk.STATE_NORMAL, self.default_base)
                self.ent_search.modify_text(gtk.STATE_NORMAL, self.default_text)

            self.filter.re_search = None
            # Act like the "Default" mode...
            self.storecursor.indices = self.storecursor.model.stats["total"]
        self._highlight_matches()

        curpos = self.ent_search.props.cursor_position

        def grabfocus():
            self.ent_search.grab_focus()
            self.ent_search.set_position(curpos)
            return False

        gobject.idle_add(grabfocus)
Ejemplo n.º 3
0
class SearchMode(BaseMode):
    """Search mode - Includes only units matching the given search term."""

    display_name = _("Search")
    name = 'Search'
    widgets = []

    MAX_RESULTS = 100000
    SEARCH_DELAY = 500

    # INITIALIZERS #
    def __init__(self, controller):
        """Constructor.
            @type  controller: virtaal.controllers.ModeController
            @param controller: The ModeController that managing program modes."""
        self.controller = controller
        self.unitview = controller.main_controller.unit_controller.view

        self._create_widgets()
        self._setup_key_bindings()
        # We alter the colours of ent_search, so let's listen for changes on
        # ent_replace to ensure we are always compliant to the style.
        self.ent_replace.connect('style-set', self._on_style_set)

        self.matches = []
        self.select_first_match = True
        self._search_timeout = 0
        self._unit_modified_id = 0

    def _create_widgets(self):
        # Widgets for search functionality (in first row)
        self.ent_search = gtk.Entry()
        self.ent_search.connect('changed', self._on_search_text_changed)
        self.ent_search.connect('activate', self._on_entry_activate)
        self.btn_search = gtk.Button(_('Search'))
        self.btn_search.connect('clicked', self._on_search_clicked)
        self.chk_casesensitive = gtk.CheckButton(_('_Case sensitive'))
        self.chk_casesensitive.connect('toggled', self._refresh_proxy)
        # l10n: To read about what regular expressions are, see
        # http://en.wikipedia.org/wiki/Regular_expression
        self.chk_regex = gtk.CheckButton(_("_Regular expression"))
        self.chk_regex.connect('toggled', self._refresh_proxy)

        # Widgets for replace (second row)
        # l10n: This text label shows in front of the text box where the replacement
        # text is typed. Keep in mind that the text box will appear after this text.
        # If this sentence construction is hard to use, consdider translating this as
        # "Replacement"
        self.lbl_replace = gtk.Label(_('Replace with'))
        self.ent_replace = gtk.Entry()
        # l10n: Button text
        self.btn_replace = gtk.Button(_('Replace'))
        self.btn_replace.connect('clicked', self._on_replace_clicked)
        # l10n: Check box
        self.chk_replace_all = gtk.CheckButton(_('Replace _All'))

        self.widgets = [
            self.ent_search, self.btn_search, self.chk_casesensitive, self.chk_regex,
            self.lbl_replace, self.ent_replace, self.btn_replace, self.chk_replace_all
        ]

    def _setup_key_bindings(self):
        gtk.accel_map_add_entry("<Virtaal>/Edit/Search", gtk.keysyms.F3, 0)
        gtk.accel_map_add_entry("<Virtaal>/Edit/Search Ctrl+F", gtk.keysyms.F, gtk.gdk.CONTROL_MASK)
        gtk.accel_map_add_entry("<Virtaal>/Edit/Search: Next", gtk.keysyms.G, gtk.gdk.CONTROL_MASK)
        gtk.accel_map_add_entry("<Virtaal>/Edit/Search: Previous", gtk.keysyms.G, gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK)

        self.accel_group = gtk.AccelGroup()
        self.accel_group.connect_by_path("<Virtaal>/Edit/Search", self._on_start_search)
        self.accel_group.connect_by_path("<Virtaal>/Edit/Search Ctrl+F", self._on_start_search)
        self.accel_group.connect_by_path("<Virtaal>/Edit/Search: Next", self._on_search_next)
        self.accel_group.connect_by_path("<Virtaal>/Edit/Search: Previous", self._on_search_prev)

        self.controller.main_controller.view.add_accel_group(self.accel_group)


    # METHODS #
    def selected(self):
        # XXX: Assumption: This method is called when a new file is loaded and that is
        #      why we keep a reference to the store's cursor.
        self.storecursor = self.controller.main_controller.store_controller.cursor
        if not self.storecursor or not self.storecursor.model:
            return

        self._add_widgets()
        self._connect_highlighting()
        self._connect_textboxes()
        if not self.ent_search.get_text():
            self.storecursor.indices = self.storecursor.model.stats['total']
        else:
            self.update_search()

        curpos = self.ent_search.props.cursor_position
        def grab_focus():
            self.ent_search.grab_focus()
            self.ent_search.set_position(curpos)
            return False

        # FIXME: The following line is a VERY UGLY HACK, but at least it works.
        gobject.timeout_add(100, grab_focus)

    def select_match(self, match):
        """Select the specified match in the GUI."""
        main_controller = self.controller.main_controller
        main_controller.select_unit(match.unit)
        view = main_controller.unit_controller.view

        if match.part == 'target':
            textbox = view.targets[match.part_n]
        elif match.part == 'source':
            textbox = view.sources[match.part_n]

        if not textbox:
            return False

        # Wait for SearchMode to finish with its highlighting and stuff, and then we do...
        def select_match_text():
            textbox.grab_focus()
            buff = textbox.buffer
            buffstr = textbox.get_text()
            unescaped = markup.unescape(buffstr)

            start, end = self._escaped_indexes(unescaped, match.start, match.end)
            if hasattr(textbox.elem, 'gui_info'):
                start = textbox.elem.gui_info.tree_to_gui_index(start)
                end = textbox.elem.gui_info.tree_to_gui_index(end)
            start_iter = buff.get_iter_at_offset(start)
            end_iter = buff.get_iter_at_offset(end)

            buff.select_range(end_iter, start_iter)
            return False

        # TODO: Implement for 'notes' and 'locations' parts
        gobject.idle_add(select_match_text)

    def replace_match(self, match, replace_str):
        main_controller = self.controller.main_controller
        unit_controller = main_controller.unit_controller
        # Using unit_controller directly is a hack to make sure that the replacement changes are immediately displayed.
        if match.part != 'target':
            return

        if unit_controller is None:
            if match.unit.hasplural():
                string_n = match.unit.target.strings[match.part_n]
                strings[match.part_n] = string_n[:match.start] + replace_str + string_n[match.end:]
                match.unit.target = strings
            else:
                rstring = match.unit.target
                rstring = rstring[:match.start] + replace_str + rstring[match.end:]
                match.unit.target = rstring
        else:
            main_controller.select_unit(match.unit)
            rstring = unit_controller.get_unit_target(match.part_n)
            unit_controller.set_unit_target(match.part_n, rstring[:match.start] + replace_str + rstring[match.end:])

    def update_search(self):
        self.filter = GrepFilter(
            searchstring=unicode(self.ent_search.get_text()),
            searchparts=('source', 'target'),
            ignorecase=not self.chk_casesensitive.get_active(),
            useregexp=self.chk_regex.get_active(),
            max_matches=self.MAX_RESULTS
        )
        store_units = self.storecursor.model.get_units()
        self.matches, indexes = self.filter.getmatches(store_units)
        self.matchcursor = Cursor(self.matches, range(len(self.matches)))

        logging.debug('Search text: %s (%d matches)' % (self.ent_search.get_text(), len(indexes)))

        if indexes:
            self.ent_search.modify_base(gtk.STATE_NORMAL, self.default_base)
            self.ent_search.modify_text(gtk.STATE_NORMAL, self.default_text)

            self.storecursor.indices = indexes
            # Select initial match for in the current unit.
            match_index = 0
            selected_unit = self.storecursor.model[self.storecursor.index]
            for match in self.matches:
                if match.unit is selected_unit:
                    break
                match_index += 1
            self.matchcursor.index = match_index
        else:
            if self.ent_search.get_text():
                self.ent_search.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(current_theme['warning_bg']))
                self.ent_search.modify_text(gtk.STATE_NORMAL, gtk.gdk.color_parse('#fff'))
            else:
                self.ent_search.modify_base(gtk.STATE_NORMAL, self.default_base)
                self.ent_search.modify_text(gtk.STATE_NORMAL, self.default_text)

            self.filter.re_search = None
            # Act like the "Default" mode...
            self.storecursor.indices = self.storecursor.model.stats['total']
        self._highlight_matches()

        curpos = self.ent_search.props.cursor_position
        def grabfocus():
            self.ent_search.grab_focus()
            self.ent_search.set_position(curpos)
            return False
        gobject.idle_add(grabfocus)

    def unselected(self):
        # TODO: Unhightlight the previously selected unit
        if hasattr(self, '_signalid_cursor_changed'):
            self.storecursor.disconnect(self._signalid_cursor_changed)

        if hasattr(self, '_textbox_signals'):
            for textbox, signal_id in self._textbox_signals.items():
                textbox.disconnect(signal_id)

        if self._unit_modified_id:
            self.controller.main_controller.unit_controller.disconnect(self._unit_modified_id)
            self._unit_modified_id = 0

        self.matches = []

    def _add_widgets(self):
        table = self.controller.view.mode_box

        xoptions = gtk.FILL
        table.attach(self.ent_search, 2, 3, 0, 1, xoptions=xoptions)
        table.attach(self.btn_search, 3, 4, 0, 1, xoptions=xoptions)
        table.attach(self.chk_casesensitive, 4, 5, 0, 1, xoptions=xoptions)
        table.attach(self.chk_regex, 5, 6, 0, 1, xoptions=xoptions)

        table.attach(self.lbl_replace, 1, 2, 1, 2, xoptions=xoptions)
        table.attach(self.ent_replace, 2, 3, 1, 2, xoptions=xoptions)
        table.attach(self.btn_replace, 3, 4, 1, 2, xoptions=xoptions)
        table.attach(self.chk_replace_all, 4, 5, 1, 2, xoptions=xoptions)

        table.show_all()

    def _connect_highlighting(self):
        self._signalid_cursor_changed = self.storecursor.connect('cursor-changed', self._on_cursor_changed)

    def _connect_textboxes(self):
        self._textbox_signals = {}
        for textbox in self.unitview.sources + self.unitview.targets:
            self._textbox_signals[textbox] = textbox.connect(
                'refreshed', self._on_textbox_refreshed
            )

    def _get_matches_for_unit(self, unit):
        return [match for match in self.matches if match.unit is unit]

    def _get_unit_matches_dict(self):
        d = {}
        for match in self.matches:
            if match.unit not in d:
                d[match.unit] = []
            d[match.unit].append(match)
        return d

    def _escaped_indexes(self, unescaped, start, end):
        """Returns the indexes of start and end in the escaped version of the
        given unescaped string."""
        # Escaping might mean that the indexes should be offset, so we
        # test to see if escaping comes into play. The unescaped version
        # will help us calculate how much we need to adjust.
        leading_segment = unescaped[:end]
        lines = leading_segment.count(u'\n') + leading_segment.count(u'\t')
        start = start + lines * 2
        end = end + lines * 2
        return (start, end)

    def _highlight_matches(self):
        if not hasattr(self, 'filter') or not hasattr(self.filter, 're_search') or self.filter.re_search is None:
            return

        for textbox in self.unitview.sources + self.unitview.targets:
            self._highlight_textbox_matches(textbox)

    def _get_matches_for_textbox(self, textbox):
        if textbox.role == 'source':
            textbox_n = self.unitview.sources.index(textbox)
        elif textbox.role == 'target':
            textbox_n = self.unitview.targets.index(textbox)
        else:
            raise ValueError('Could not find text box in sources or targets: %s' % (textbox))
        return [
            m for m in self.matches
            if m.unit is self.unitview.unit and \
                m.part == textbox.role and \
                m.part_n == textbox_n
        ]

    def _highlight_textbox_matches(self, textbox, select_match=True):
        buff = textbox.buffer
        buffstr = textbox.get_text()
        unescaped = markup.unescape(buffstr)

        # Make sure the 'search_highlight' tag in the textbox's tag table
        # is "fresh".
        try:
            tagtable = buff.get_tag_table()
            tag = tagtable.lookup('search_highlight')
            if tag:
                tagtable.remove(tag)
            tagtable.add(self._make_highlight_tag())
        except ValueError, ve:
            logging.exception("(Re-)adding search highlighting tag exception:")

        select_iters = []
        for match in self._get_matches_for_textbox(textbox):
            start, end = self._escaped_indexes(unescaped, match.start, match.end)
            if hasattr(textbox.elem, 'gui_info'):
                start = textbox.elem.gui_info.tree_to_gui_index(start)
                end   = textbox.elem.gui_info.tree_to_gui_index(end)
            start_iter, end_iter = buff.get_iter_at_offset(start), buff.get_iter_at_offset(end)
            buff.apply_tag_by_name('search_highlight', start_iter, end_iter)

            if select_match and textbox.role == 'target' and not select_iters and self.select_first_match:
                select_iters = [start_iter, end_iter]

        if select_iters:
            buff.select_range(select_iters[1], select_iters[0])
Ejemplo n.º 4
0
class SearchMode(BaseMode):
    """Search mode - Includes only units matching the given search string."""

    display_name = _("Search")
    name = 'Search'
    widgets = []

    MAX_RESULTS = 200000
    SEARCH_DELAY = 500

    # INITIALIZERS #
    def __init__(self, controller):
        """Constructor.
            @type  controller: virtaal.controllers.ModeController
            @param controller: The ModeController managing navigation modes."""
        self.controller = controller
        self.unitview = controller.main_controller.unit_controller.view

        self._create_widgets()
        self._setup_key_bindings()
        # We alter the colours of ent_search, so let's listen for changes on
        # ent_replace to ensure we are always compliant to the style.
        self.ent_replace.connect('style-set', self._on_style_set)

        self.filter = None
        self.matches = []
        self.select_first_match = True
        self._search_timeout = 0
        self._unit_modified_id = 0

    def _create_widgets(self):
        # Widgets for search functionality (in first row)
        self.ent_search = gtk.Entry()
        self.ent_search.connect('changed', self._on_search_text_changed)
        self.ent_search.connect('activate', self._on_entry_activate)
        self.btn_search = gtk.Button(_('Search'))
        self.btn_search.connect('clicked', self._on_search_clicked)
        self.chk_casesensitive = gtk.CheckButton(_('_Case sensitive'))
        self.chk_casesensitive.connect('toggled', self._refresh_proxy)
        # l10n: To read about what regular expressions are, see
        # http://en.wikipedia.org/wiki/Regular_expression
        self.chk_regex = gtk.CheckButton(_("_Regular expression"))
        self.chk_regex.connect('toggled', self._refresh_proxy)

        # Widgets for replace (second row)
        # l10n: This text label shows in front of the text box where the replacement
        # text is typed. Keep in mind that the text box will appear after this text.
        # If this sentence construction is hard to use, consdider translating this as
        # "Replacement"
        self.lbl_replace = gtk.Label(_('Replace with'))
        self.ent_replace = gtk.Entry()
        # l10n: Button text
        self.btn_replace = gtk.Button(_('Replace'))
        self.btn_replace.connect('clicked', self._on_replace_clicked)
        # l10n: Check box
        self.chk_replace_all = gtk.CheckButton(_('Replace _All'))

        self.widgets = [
            self.ent_search, self.btn_search, self.chk_casesensitive,
            self.chk_regex, self.lbl_replace, self.ent_replace,
            self.btn_replace, self.chk_replace_all
        ]

    def _setup_key_bindings(self):
        gtk.accel_map_add_entry("<Virtaal>/Edit/Search", gtk.keysyms.F3, 0)
        gtk.accel_map_add_entry("<Virtaal>/Edit/Search Ctrl+F", gtk.keysyms.F,
                                gtk.gdk.CONTROL_MASK)
        gtk.accel_map_add_entry("<Virtaal>/Edit/Search: Next", gtk.keysyms.G,
                                gtk.gdk.CONTROL_MASK)
        gtk.accel_map_add_entry("<Virtaal>/Edit/Search: Previous",
                                gtk.keysyms.G,
                                gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK)

        self.accel_group = gtk.AccelGroup()
        self.accel_group.connect_by_path("<Virtaal>/Edit/Search",
                                         self._on_start_search)
        self.accel_group.connect_by_path("<Virtaal>/Edit/Search Ctrl+F",
                                         self._on_start_search)
        self.accel_group.connect_by_path("<Virtaal>/Edit/Search: Next",
                                         self._on_search_next)
        self.accel_group.connect_by_path("<Virtaal>/Edit/Search: Previous",
                                         self._on_search_prev)

        self.controller.main_controller.view.add_accel_group(self.accel_group)

    # METHODS #
    def selected(self):
        # XXX: Assumption: This method is called when a new file is loaded and that is
        #      why we keep a reference to the store's cursor.
        self.storecursor = self.controller.main_controller.store_controller.cursor
        if not self.storecursor or not self.storecursor.model:
            return

        self._add_widgets()
        self._connect_highlighting()
        self._connect_textboxes()
        unitcont = self.controller.main_controller.unit_controller
        if self._unit_modified_id:
            unitcont.disconnect(self._unit_modified_id)
        self._unit_modified_id = unitcont.connect('unit-modified',
                                                  self._on_unit_modified)
        if not self.ent_search.get_text():
            self.storecursor.indices = self.storecursor.model.stats['total']
        else:
            self.update_search()

        def grab_focus():
            curpos = self.ent_search.props.cursor_position
            self.ent_search.grab_focus()
            # that will select all text, so reset the cursor position
            self.ent_search.set_position(curpos)
            return False

        # FIXME: The following line is a VERY UGLY HACK, but at least it works.
        gobject.timeout_add(100, grab_focus)

    def select_match(self, match):
        """Select the specified match in the GUI."""
        main_controller = self.controller.main_controller
        main_controller.select_unit(match.unit)
        view = main_controller.unit_controller.view

        if match.part == 'target':
            textbox = view.targets[match.part_n]
        elif match.part == 'source':
            textbox = view.sources[match.part_n]

        if not textbox:
            return False

        # Wait for SearchMode to finish with its highlighting and stuff, and then we do...
        def select_match_text():
            textbox.grab_focus()
            buff = textbox.buffer
            buffstr = textbox.get_text()

            start, end = match.start, match.end
            if hasattr(textbox.elem, 'gui_info'):
                start_iter = textbox.elem.gui_info.treeindex_to_iter(start)
                end_iter = textbox.elem.gui_info.treeindex_to_iter(
                    end, start_at=(start, start_iter))
            else:
                start_iter = buff.get_iter_at_offset(start)
                end_iter = buff.get_iter_at_offset(end)

            buff.select_range(end_iter, start_iter)
            return False

        # TODO: Implement for 'notes' and 'locations' parts
        gobject.idle_add(select_match_text)

    def replace_match(self, match, replace_str):
        main_controller = self.controller.main_controller
        unit_controller = main_controller.unit_controller
        # Using unit_controller directly is a hack to make sure that the replacement changes are immediately displayed.
        if match.part != 'target':
            return

        if unit_controller is None:
            if match.unit.hasplural():
                string_n = match.unit.target.strings[match.part_n]
                strings[
                    match.
                    part_n] = string_n[:match.start] + replace_str + string_n[
                        match.end:]
                match.unit.target = strings
            else:
                rstring = match.unit.target
                rstring = rstring[:match.start] + replace_str + rstring[match.
                                                                        end:]
                match.unit.target = rstring
        else:
            main_controller.select_unit(match.unit)
            rstring = unit_controller.get_unit_target(match.part_n)
            unit_controller.set_unit_target(
                match.part_n,
                rstring[:match.start] + replace_str + rstring[match.end:])

    def update_search(self):
        self._search_timeout = 0
        from translate.tools.pogrep import GrepFilter
        self.filter = GrepFilter(
            searchstring=unicode(self.ent_search.get_text()),
            searchparts=('source', 'target'),
            ignorecase=not self.chk_casesensitive.get_active(),
            useregexp=self.chk_regex.get_active(),
            max_matches=self.MAX_RESULTS)
        store_units = self.storecursor.model.get_units()
        self.matches, indexes = self.filter.getmatches(store_units)
        self.matchcursor = Cursor(self.matches, range(len(self.matches)))

        logging.debug('Search text: %s (%d matches)' %
                      (self.ent_search.get_text(), len(indexes)))

        if indexes:
            self.ent_search.modify_base(gtk.STATE_NORMAL, self.default_base)
            self.ent_search.modify_text(gtk.STATE_NORMAL, self.default_text)

            self.storecursor.indices = indexes
            # Select initial match for in the current unit.
            match_index = 0
            selected_unit = self.storecursor.model[self.storecursor.index]
            for match in self.matches:
                if match.unit is selected_unit:
                    break
                match_index += 1
            self.matchcursor.index = match_index
        else:
            if self.ent_search.get_text():
                self.ent_search.modify_base(
                    gtk.STATE_NORMAL,
                    gtk.gdk.color_parse(current_theme['warning_bg']))
                self.ent_search.modify_text(gtk.STATE_NORMAL,
                                            gtk.gdk.color_parse('#fff'))
            else:
                self.ent_search.modify_base(gtk.STATE_NORMAL,
                                            self.default_base)
                self.ent_search.modify_text(gtk.STATE_NORMAL,
                                            self.default_text)

            self.filter.re_search = None
            # Act like the "Default" mode...
            self.storecursor.indices = self.storecursor.model.stats['total']
        self._highlight_matches()

        def grabfocus():
            curpos = self.ent_search.props.cursor_position
            self.ent_search.grab_focus()
            # that will select all text, so reset the cursor position
            self.ent_search.set_position(curpos)
            return False

        gobject.idle_add(grabfocus)

    def unselected(self):
        # TODO: Unhightlight the previously selected unit
        if hasattr(self, '_signalid_cursor_changed'):
            self.storecursor.disconnect(self._signalid_cursor_changed)

        if hasattr(self, '_textbox_signals'):
            for textbox, signal_id in self._textbox_signals.items():
                textbox.disconnect(signal_id)

        if self._unit_modified_id:
            self.controller.main_controller.unit_controller.disconnect(
                self._unit_modified_id)
            self._unit_modified_id = 0

        self.matches = []

    def _add_widgets(self):
        table = self.controller.view.mode_box

        xoptions = gtk.FILL
        table.attach(self.ent_search, 2, 3, 0, 1, xoptions=xoptions)
        table.attach(self.btn_search, 3, 4, 0, 1, xoptions=xoptions)
        table.attach(self.chk_casesensitive, 4, 5, 0, 1, xoptions=xoptions)
        table.attach(self.chk_regex, 5, 6, 0, 1, xoptions=xoptions)

        table.attach(self.lbl_replace, 1, 2, 1, 2, xoptions=xoptions)
        table.attach(self.ent_replace, 2, 3, 1, 2, xoptions=xoptions)
        table.attach(self.btn_replace, 3, 4, 1, 2, xoptions=xoptions)
        table.attach(self.chk_replace_all, 4, 5, 1, 2, xoptions=xoptions)

        table.show_all()

    def _connect_highlighting(self):
        self._signalid_cursor_changed = self.storecursor.connect(
            'cursor-changed', self._on_cursor_changed)

    def _connect_textboxes(self):
        self._textbox_signals = {}
        for textbox in self.unitview.sources + self.unitview.targets:
            self._textbox_signals[textbox] = textbox.connect(
                'refreshed', self._on_textbox_refreshed)

    def _get_matches_for_unit(self, unit):
        return [match for match in self.matches if match.unit is unit]

    def _get_unit_matches_dict(self):
        d = {}
        for match in self.matches:
            if match.unit not in d:
                d[match.unit] = []
            d[match.unit].append(match)
        return d

    def _highlight_matches(self):
        if getattr(self.filter, 're_search', None) is None:
            return

        for textbox in self.unitview.sources + self.unitview.targets:
            if textbox.props.visible:
                self._highlight_textbox_matches(textbox)

    def _get_matches_for_textbox(self, textbox):
        role = textbox.role
        unit = self.unitview.unit
        if role == 'source':
            textbox_n = self.unitview.sources.index(textbox)
        elif role == 'target':
            textbox_n = self.unitview.targets.index(textbox)
        else:
            raise ValueError(
                'Could not find text box in sources or targets: %s' %
                (textbox))
        return [
            m for m in self.matches
            if m.unit is unit and \
                m.part == role and \
                m.part_n == textbox_n
        ]

    def _highlight_textbox_matches(self, textbox, select_match=True):
        buff = textbox.buffer
        buffstr = textbox.get_text()

        # Make sure the 'search_highlight' tag in the textbox's tag table
        # is "fresh".
        try:
            tagtable = buff.get_tag_table()
            tag = tagtable.lookup('search_highlight')
            if tag:
                tagtable.remove(tag)
            tagtable.add(self._make_highlight_tag())
        except ValueError, ve:
            logging.exception("(Re-)adding search highlighting tag exception:")

        select_iters = []

        # We keep the iterator and index pointing to the end of the previous
        # match so that we continue searching from there for the next match.
        end_iter = None
        old_end = -1

        for match in self._get_matches_for_textbox(textbox):
            start, end = match.start, match.end
            if hasattr(textbox.elem, 'gui_info'):
                if end_iter:
                    start_iter = textbox.elem.gui_info.treeindex_to_iter(
                        start, start_at=(old_end, end_iter))
                else:
                    start_iter = textbox.elem.gui_info.treeindex_to_iter(start)
                end_iter = textbox.elem.gui_info.treeindex_to_iter(
                    end, start_at=(start, start_iter))
                old_end = end
            else:
                start_iter, end_iter = buff.get_iter_at_offset(
                    start), buff.get_iter_at_offset(end)
                old_end = end
            buff.apply_tag_by_name('search_highlight', start_iter, end_iter)

            if select_match and textbox.role == 'target' and not select_iters and self.select_first_match:
                select_iters = [start_iter, end_iter]

        if select_iters:
            buff.select_range(select_iters[1], select_iters[0])