155. How to highlight matching html tags in GtkTextView?

Problem

You want to highlight enclosing html tags in a GtkTextView as shown below:

How to highlight matching html tags in GtkTextView?


Solution


Sample Code

1   
2   
3   
4   
5   
6   
7   
8   
9   
10   
11   
12   
13   
14   
16   
18   
19   
20   
21   
22   
23   
24   
25   
26   
27   
28   
29   
30   
31   
32   
33   
40   
41   
42   
43   
44   
45   
46   
47   
48   
49   
50   
51   
52   
53   
54   
55   
56   
57   
59   
60   
61   
62   
63   
64   
66   
67   
68   
69   
70   
71   
74   
76   
77   
78   
79   
80   
81   
82   
83   
88   
89   
90   
91   
92   
93   
94   
95   
96   
97   
98   
99   
100   
101   
102   
103   
106   
108   
109   
110   
111   
112   
113   
114   
115   
120   
121   
122   
123   
124   
125   
126   
127   
129   
131   
132   
133   
134   
135   
136   
137   
138   
139   
140   
141   
142   
143   
144   
145   
146   
147   
149   
150   
151   
152   
153   
154   
155   
156   
157   
158   
160   
161   
162   
163   
164   
165   
166   
167   
168   
169   
170   
171   
172   
173   
<?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("Highlight matching HTML tags");
$title->modify_font(new PangoFontDescription("Times New Roman Italic 10"));
$title->modify_fg(Gtk::STATE_NORMAL, GdkColor::parse("#0000ff"));
$title->set_size_request(-1, 20);
$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('<p>This is a test</p>
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>');
$view = new GtkTextView();
$view->set_buffer($buffer);
$view->modify_font(new PangoFontDescription("Arial 10"));
$view->set_wrap_mode(Gtk::WRAP_WORD);
$view->connect('button-press-event', 'on_button_press_in_textview');
$view->connect('key-press-event', 'on_key_press_in_textview');

$hbox = new GtkHBox();
$hbox->pack_start($button = new GtkButton('Find enclosing tags'), 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_tag($iter, $buffer) {
    global $tag;

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

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

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

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

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

    $buffer->apply_tag($tag['highlight'], $match_start, $match_end2); // note 3
    return array($match_start, $match_end2);
}

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

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

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

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

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

    $buffer->apply_tag($tag['highlight'], $match_start, $match_end2); // note 6
    return array($match_start, $match_end2);
}

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

    $cursor_pos = $buffer->get_mark('insert');
    $iter = $buffer->get_iter_at_mark($cursor_pos);
    if ($iter==null) return;
    $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_tag($iter, $buffer);
    if ($left_start_iter==null) return;

    list($right_start_iter, $right_end_iter) =
        find_right_tag($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();
}

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

function remove_highlight() {
    global $buffer, $tag;
    global $left_start_iter, $left_end_iter;
    global $right_start_iter, $right_end_iter;
    if ($left_start_iter==null || $left_end_iter==null) return; // note 3
    if ($right_start_iter==null || $right_end_iter==null) return; // note 3
    $buffer->remove_tag($tag['highlight'], $left_start_iter, $left_end_iter); // note 4
    $buffer->remove_tag($tag['highlight'], $right_start_iter, $right_end_iter); // note 4
}

?>

Output

As shown above.
 

Explanation

  1. Search for start of left tag.
  2. Search for end of left tag.
  3. Highlight the left tag.
  4. Search for start of right tag.
  5. Search for end of right tag.
  6. Highlight the right tag.

Note

To make the code easier to understand, I did not put in any error checking here. In your actual implementation, you should check that the start and end tags are matching, otherwise the following might occur:

Related Links

Add comment


Security code
Refresh