153. How to highlight matching brackets in GtkTextView - Part 2?

Problem

In Part 1, you have highlighted enclosing curly brackets in a GtkTextView.

However, pressing the button when the cursor is at "else" should not highlight anything. We'll fix this in this article as shown below:

How to highlight matching brackets in GtkTextView - Part 2?


Solution

  • On the left, we need to make sure that there's no "}" after the "{" by using the method.
  • Similarly, on the right, we need to make sure that there's no "{" before the "}".
  • GtkTextiter::compare() is the method that allows us to compare the position of two iters.

Sample Code

1   
2   
3   
4   
5   
6   
7   
8   
9   
10   
11   
12   
13   
14   
15   
16   
18   
20   
21   
22   
23   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
34   
43   
44   
45   
46   
47   
48   
49   
50   
51   
52   
53   
54   
55   
56   
57   
58   
60   
61   
62   
63   
64   
65   
66   
68   
69   
70   
71   
72   
73   
76   
78   
79   
80   
81   
82   
83   
84   
89   
90   
91   
92   
93   
94   
95   
96   
97   
98   
99   
100   
101   
102   
103   
104   
105   
108   
110   
111   
112   
113   
114   
115   
116   
121   
122   
123   
124   
125   
126   
127   
128   
129   
131   
133   
134   
135   
136   
137   
138   
139   
140   
141   
142   
143   
144   
145   
146   
147   
148   
150   
151   
152   
153   
154   
155   
156   
157   
158   
159   
<?php
$window = new GtkWindow();
$window->set_size_request(400, 240);
$window->connect_simple('destroy', array('Gtk','main_quit'));
$window->add($vbox = new GtkVBox());

// display title
$title = new GtkLabel("Find matching curly bracket - Part 2\n".
"Place the cursor on 'else' and press the button.\n".
"Nothing will be highlighted.");
$title->modify_font(new PangoFontDescription("Times New Roman Italic 10"));
$title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000ff"));
$title->set_size_request(-1, 60);
$title->set_justify(Gtk::JUSTIFY_CENTER);
$alignment = new GtkAlignment(0.5, 0.5, 0, 0);
$alignment->add($title);
$vbox->pack_start($alignment);

$left_start_iter = $left_end_iter = null;
$right_start_iter = $right_end_iter = null;

// Create a new buffer and a new view to show the buffer.
$buffer = new GtkTextBuffer();
$buffer->set_text('if ($i==1) {
    test();
} else {
    test2();
}');
$view = new GtkTextView();
$view->set_buffer($buffer);
$view->modify_font(new PangoFontDescription("Arial 10"));
$view->set_wrap_mode(Gtk::WRAP_WORD);

$hbox = new GtkHBox();
$hbox->pack_start($button = new GtkButton('Find enclosing bracket'), 0);
$vbox->pack_start($hbox, 0);
$button->connect('clicked', 'on_button', $buffer);

$scrolled_win = new GtkScrolledWindow();
$scrolled_win->set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
$vbox->pack_start($scrolled_win);
$scrolled_win->add($view);

$tag_table = $buffer->get_tag_table();
$tag['highlight'] = new GtkTextTag();
$tag['highlight']->set_property('background', "#ffff00");
$tag_table->add($tag['highlight']);

$window->show_all();
Gtk::main();

function find_left_bracket($iter, $buffer) {
    global $tag;
    $iter2 = $iter->copy();

    $match_start = $iter->copy();
    $match_end = $iter->copy();

    // check left { first
    $found = $iter->backward_search('{', 0, $match_start,
        $match_end, null);
    if (!$found) return null;

    // check if there's a } on the left
    $match_start2 = $iter->copy();
    $match_end2 = $iter->copy();

    $found2 = $iter2->backward_search('}', 0, $match_start2, // note 1
        $match_end2, null); 

    if ($found2 && $match_start->compare($match_start2)!=1) // note 2
        return null; 

    $buffer->apply_tag($tag['highlight'], $match_start, $match_end);
    return array($match_start, $match_end);
}

function find_right_bracket($iter, $buffer) {
    global $tag;
    $iter2 = $iter->copy();
    $match_start = $iter->copy();
    $match_end = $iter->copy();

    // check right } first
    $found = $iter->forward_search('}', 0, $match_start,
        $match_end, null);
    if (!$found) return null;

    // check if there's a { on the right
    $match_start2 = $iter->copy();
    $match_end2 = $iter->copy();

    $found2 = $iter2->forward_search('{', 0, $match_start2, // note 3
        $match_end2, null); 

    if ($found2 && $match_start->compare($match_start2)!=-1)  // note 4
        return null; 

    $buffer->apply_tag($tag['highlight'], $match_start, $match_end);
    return array($match_start, $match_end);
}

function on_button($button, $buffer) {
    global $view, $tag;

    $cursor_pos = $buffer->get_mark('insert');
    $iter = $buffer->get_iter_at_mark($cursor_pos);
    $last_pos = $buffer->create_mark('last_pos', $iter, false);

    global $left_start_iter, $left_end_iter;
    global $right_start_iter, $right_end_iter;

    list($left_start_iter, $left_end_iter) =
        find_left_bracket($iter, $buffer);
    if ($left_start_iter==null) return;

    list($right_start_iter, $right_end_iter) =
        find_right_bracket($iter, $buffer);
    if ($right_start_iter==null) return;


    $view->scroll_mark_onscreen($last_pos);
    $view->grab_focus();
}

function on_button_press_in_textview($widget, $event) {
    remove_highlight();
}

?>

Output

As shown above.
 

Explanation

  1. Check if there's a "}" on the left.
  2. If the } comes after the {, we do not highlight the curly bracket.
  3. Check if there's a "{" on the right.
  4. If the { comes after the }, we do not highlight the curly bracket.

Note

There is one more thing we need to improve:

  • When the user moves the cursor (by mouse or keyboard), the highlight should disappear.

We will fix this in the next article.

Related Links

Add comment


Security code
Refresh