about summary refs log tree commit diff
path: root/saneterm/search.py
blob: 9b840ab44c5883e56603030006a4841e6d798bb2 (plain) (blame)
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
from gi.repository import Gtk

class SearchBar(Gtk.SearchBar):
    "SearchBar implements Gtk.SearchBar on a Gtk.TextBuffer."

    BG_COLOR = "yellow"
    FG_COLOR = "black"

    def __init__(self, view):
        Gtk.SearchBar.__init__(self)

        self.__view = view
        self.__buffer = view.get_buffer()

        self.__match = None
        self.__tag = self.__buffer.create_tag("search-match",
                background=self.BG_COLOR,
                foreground=self.FG_COLOR)

        hbox = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, 0)
        style_ctx = hbox.get_style_context()
        style_ctx.add_class("linked")
        style_ctx.add_class("raised")
        self.add(hbox)

        search_entry = Gtk.SearchEntry.new()
        search_entry.set_width_chars(32)
        search_entry.connect("search-changed", self.__search_changed)
        search_entry.connect("next-match", self.__next_match)
        search_entry.connect("previous-match", self.__prev_match)
        hbox.pack_start(search_entry, True, True, 0)

        self.__nextbtn, self.__prevbtn = self.__create_nav_buttons(search_entry, hbox)

        self.set_show_close_button(True)
        self.connect_entry(search_entry)

    def __create_nav_buttons(self, search_entry, box):
        def button_callback(button, dir):
            signal = "previous-match" if dir == "up" else "next-match"
            search_entry.emit(signal)

        buttons = []
        for dir in ["up", "down"]:
            button = Gtk.Button.new_from_icon_name(F"go-{dir}-symbolic", Gtk.IconSize.MENU)
            button.set_sensitive(False)
            button.connect("clicked", button_callback, dir)

            box.add(button)
            buttons.append(button)

        return tuple(buttons)

    def __find_match(self, entry, start, forward=True):
        buf = self.__buffer
        text = entry.get_text()

        # Remove old match.
        buf.remove_tag(self.__tag,
                buf.get_start_iter(),
                buf.get_end_iter())

        if forward:
            self.__match = start.forward_search(text, 0, None)
        else:
            self.__match = start.backward_search(text, 0, None)

        if self.__match:
            mstart, mend = self.__match
            buf.apply_tag(self.__tag, mstart, mend)
            self.__view.scroll_to_iter(mstart, 0.1, False, 0.0, 0.0)

        return self.__match

    def __search_changed(self, entry):
        found = self.__find_match(entry, self.__buffer.get_start_iter())

        self.__nextbtn.set_sensitive(not found is None)
        self.__prevbtn.set_sensitive(not found is None)

    def __next_match(self, entry):
        # Wrap around if no match was found previously.
        if self.__match is None:
            start = self.__buffer.get_start_iter()
        else:
            _, start = self.__match

        self.__find_match(entry, start)

    def __prev_match(self, entry):
        # Wrap around if no match was found previously.
        if self.__match is None:
            start = self.__buffer.get_end_iter()
        else:
            start, _ = self.__match

        self.__find_match(entry, start, forward=False)