12.7. Example: Cursors and Gtk2::TextView content

As you may by now have gathered, the Gtk2::TextView can be used in quite complex scenarios. Rather than having one large demo program, we will be going through a few basic ones, that will use available methods to accomplish what we want.

Objective

This lesson will look at the three items that can fit into a Gtk2::TextView. They are text, pictures and widgets. You will also see how to change the cursor when the mouse pointer is at certain places.

Table 12-4. Gtk2 object classes used

Gtk2 Class Name
Gtk2::Pango
Gtk2::Window
Gtk2::VBox
Gtk2::Button
Gtk2::CheckButton
Gtk2::Frame
Gtk2::ScrolledWindow
Gtk2::Gdk::Cursor
Gtk2::Gdk::Pixbuf
Gtk2::TextView
Gtk2::TextBuffer
Gtk2::TextTag

The screenshot

Figure 12-2. Cursors and Gtk2::TextViews

The Code

The program can be found here: 'Gtk2::TextView - Basic '

  1 #! /usr/bin/perl -w
  2 use strict;
  3 
  4 use Gtk2 '-init';
  5 use Glib qw/TRUE FALSE/;
  6 use Gtk2::Pango; 
  7 
  8 #anchor for a checkbutton
  9 my $check_anchor;
 10 #anchor for a normal button
 11 my $submit_anchor;
 12 
 13 my $hovering_over_link = FALSE;
 14 #the two cursors that we want to use
 15 my $pencil_cursor = Gtk2::Gdk::Cursor->new ('pencil');
 16 my $point_cursor = Gtk2::Gdk::Cursor->new ('shuttle');
 17  
 18 #standard window creation, placement, and signal connecting
 19 my $window = Gtk2::Window->new('toplevel');
 20 $window->signal_connect('delete_event' => sub { Gtk2->main_quit; });
 21 $window->set_border_width(5);
 22 #$window->set_position('center_always');
 23 
 24 	$window->set_property ('window-position' => 'center_always');
 25 
 26 #this vbox will geturn the bulk of the gui
 27 my $vbox = &ret_vbox();
 28 
 29 #add and show the vbox
 30 $window->add($vbox);
 31 $window->show();
 32 	
 33 #our main event-loop
 34 Gtk2->main();
 35 
 36 sub ret_vbox {
 37 
 38 my $vbox = Gtk2::VBox->new(FALSE,5);
 39 
 40 	my $frame = Gtk2::Frame->new("Gtk2::TextView - Gentle Introduction");
 41 		
 42 	#method of Gtk2::Container
 43 	$frame->set_border_width(5);
 44 		#create a scrolled window to put the textview in
 45 		my $sw = Gtk2::ScrolledWindow->new (undef, undef);
 46     		$sw->set_shadow_type ('etched-out');
 47 		$sw->set_policy ('automatic', 'automatic');
 48 		#This is a method of the Gtk2::Widget class,it will force a minimum 
 49 		#size on the widget. Handy to give intitial size to a 
 50 		#Gtk2::ScrolledWindow class object
 51 		$sw->set_size_request (500, 200);
 52 		#method of Gtk2::Container
 53 		$sw->set_border_width(5);
 54 			
 55 			#we create and build the buffer
 56 			my $buffer = &create_buffer;
 57 			#we add this buffer to a new textview
 58 			my $tview = Gtk2::TextView->new_with_buffer($buffer);
 59 			#if we want to change the cursors, we have to connect the
 60 			#motion_notify_event to a sub that will handle it
 61 			$tview->signal_connect (motion_notify_event => \&motion_notify_event);
 62 				#create a widget to add to the textview's anchor		
 63 				my $chk_button = Gtk2::CheckButton->new();
 64 			#attach this widget to the textview's anchor
 65 			$tview->add_child_at_anchor ($chk_button, $check_anchor);
 66 				#create a widget to add to the textview's anchor
 67 				my $btn_submit = Gtk2::Button->new("_Submit");
 68 			#attach this widget to the textview's anchor
 69 			$tview->add_child_at_anchor ($btn_submit, $submit_anchor);
 70 			#we do not want to edit anything else than specified text.
 71 			$tview->set_editable(FALSE);			
 72 		#add the textview to the scrolled window		
 73   		$sw->add($tview);
 74 	#add the scrolledwindow to the frame
 75 	$frame->add($sw);
 76 				
 77 #add the frame to the vbox	
 78 $vbox->pack_start($frame,TRUE,TRUE,4);
 79 #make them all visable				
 80 $vbox->show_all();	
 81 return $vbox;
 82 }
 83 
 84 sub create_buffer {
 85 	#-----------------------------------------------
 86 	#a standard buffer will typically be:
 87 	#1.) created
 88 	#2.) tags added to;
 89 	#3.) anchors, text, marks, and pixbuffs added to
 90 	#-----------------------------------------------
 91 	#create a net textbuffer
 92 	my $buffer = Gtk2::TextBuffer->new();
 93 	#create a bunch of standard tags
 94 	$buffer->create_tag ("bold", weight => PANGO_WEIGHT_BOLD);
 95 	$buffer->create_tag ("big", size => 20 * PANGO_SCALE);
 96 	$buffer->create_tag ("italic", style => 'italic');
 97 	$buffer->create_tag ("grey_foreground", foreground => "grey");
 98 	#create a tag for the editable text (editable => TRUE)		
 99 	my $tag = $buffer->create_tag ("editable",
100 					style =>'italic',
101 					weight => PANGO_WEIGHT_ULTRALIGHT,
102 					foreground => "blue",
103 					editable => TRUE,
104 					);
105 	#a created tag is for all practical purposes a hash with keys and values.
106 	#the "create_tag" method does not allow any additional keys, other than a standard set.
107 	#here we add a key and value to the $tag hash JUST after creation. This will later be used
108 	#when we check if the cursor must change.
109 	$tag->{pointer} = "edit";
110 	
111 	#we get the pointer to the start of the buffer, and add some content.
112 	#after every addition, this pointer ($iter) will be pointing to
113 	#a new place in the buffer. This works fine for sequencial additions
114 	my $iter = $buffer->get_start_iter;
115 	#tags is a list, thus they can be stacked to get desired results.
116 	$buffer->insert_with_tags_by_name ($iter, "Question:", "bold","big","grey_foreground");
117 	#pixbuffs can also be inserted into the buffer
118 	$buffer->insert_pixbuf ($iter,  Gtk2::Gdk::Pixbuf->new_from_file ("./pix/tux.png"));
119 	$buffer->insert_with_tags_by_name($iter, "\nDo you like Gtk2-Perl?\n\n","grey_foreground");
120 	#anchors are places where widgets can attach to
121 	$check_anchor = $buffer->create_child_anchor ($iter); 
122 	#our editable text
123 	$buffer->insert_with_tags_by_name ($iter, "\"Oh, yes my dear, its quite alright, I hear the tinkle of a bell!\"", "editable");
124 	$buffer->insert_with_tags_by_name($iter, "\n\n","grey_foreground");
125 	$submit_anchor = $buffer->create_child_anchor ($iter); 
126 	
127 	#return the finished buffer
128 	return $buffer;
129 
130 }
131 
132 sub motion_notify_event{
133 	
134 	my ($text_view, $event) = @_;
135 	
136 	#we have to discover where in the buffer the mouse currently
137 	#roams
138 	my ($x, $y) = $text_view->window_to_buffer_coords ( 
139                                          'widget', #GTK_TEXT_WINDOW_WIDGET,
140                                          $event->x, $event->y);
141 	#when we have the pointer co-ordinates within the buffer, 
142 	#we can do extra processing
143 	&determine_pointer($text_view,$x,$y);
144 	
145 	#this is a must to ensure the continuaty of 
146 	# recording the pointer position
147 	$text_view->window->get_pointer;
148 	return FALSE;	
149 }
150 
151 
152   sub determine_pointer {
153   
154   #this sub will determine the iter where the mouse points at
155   #in the textbuffer. Then it will check the iter's tags and 
156   # if it finds the interesting one, change the cursor
157   my ($text_view, $x, $y) = @_;
158   
159   my $hovering = FALSE;
160   
161   #get the textbuffer
162   my $buffer = $text_view->get_buffer;
163   	#determine which iter is at the x and y points where the mouse
164 	#currently sits at
165 	my $iter = $text_view->get_iter_at_location ($x, $y);
166 	#get all the tags associated with this iter
167 	#step througk them to check if one is a key 
168 	#with value "pointer" 	
169 	foreach my $tag ($iter->get_tags) {
170       if ($tag->{pointer}) {
171           $hovering = TRUE;
172           last;
173       }
174   }
175 	#check if we need to change the cursor.
176 	#this will only execute when there is a change
177 	if ($hovering != $hovering_over_link){
178       		$hovering_over_link = $hovering;
179 
180       			$text_view->get_window ('text')->set_cursor
181 			
182 		#compact conditional statement
183       		($hovering_over_link ? $pencil_cursor : $point_cursor);
184     	}
185   }

From the code - Important pieces