1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001 Sun Microsystems Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include "evolution-config.h"
18 
19 #include <gtk/gtk.h>
20 #include <libgnomecanvas/libgnomecanvas.h>
21 #include "gailcanvasitem.h"
22 #include "gailcanvastext.h"
23 #include <libgail-util/gail-util.h>
24 
25 struct _GailCanvasText
26 {
27   GailCanvasItem parent;
28   GailTextUtil *textutil;
29 };
30 
31 static void           gail_canvas_text_text_interface_init (AtkTextIface        *iface);
32 static gchar *         gail_canvas_text_get_text            (AtkText             *text,
33                                                             gint                start_offset,
34                                                             gint                end_offset);
35 static gchar *         gail_canvas_text_get_text_after_offset
36                                                            (AtkText             *text,
37                                                             gint                offset,
38                                                             AtkTextBoundary     boundary_type,
39                                                             gint                *start_offset,
40                                                             gint                *end_offset);
41 static gchar *         gail_canvas_text_get_text_at_offset  (AtkText             *text,
42                                                             gint                offset,
43                                                             AtkTextBoundary     boundary_type,
44                                                             gint                *start_offset,
45                                                             gint                *end_offset);
46 static gchar *         gail_canvas_text_get_text_before_offset
47                                                            (AtkText             *text,
48                                                             gint                offset,
49                                                             AtkTextBoundary     boundary_type,
50                                                             gint                *start_offset,
51                                                             gint                *end_offset);
52 static gunichar       gail_canvas_text_get_character_at_offset
53                                                             (AtkText            *text,
54                                                              gint               offset);
55 static gint           gail_canvas_text_get_character_count  (AtkText            *text);
56 static gint           gail_canvas_text_get_caret_offset     (AtkText            *text);
57 static gboolean       gail_canvas_text_set_caret_offset     (AtkText            *text,
58                                                              gint               offset);
59 static gint           gail_canvas_text_get_offset_at_point  (AtkText            *text,
60                                                              gint               x,
61                                                              gint               y,
62                                                              AtkCoordType       coords);
63 static void           gail_canvas_text_get_character_extents (AtkText           *text,
64                                                               gint              offset,
65                                                               gint              *x,
66                                                               gint              *y,
67                                                               gint              *width,
68                                                               gint              *height,
69                                                               AtkCoordType      coords);
70 static AtkAttributeSet *
71                       gail_canvas_text_get_run_attributes    (AtkText           *text,
72                                                               gint              offset,
73                                                               gint              *start_offset,
74                                                               gint              *end_offset);
75 static AtkAttributeSet *
76                       gail_canvas_text_get_default_attributes (AtkText          *text);
77 static gint           gail_canvas_text_get_n_selections      (AtkText           *text);
78 static gchar *         gail_canvas_text_get_selection         (AtkText           *text,
79                                                               gint              selection_num,
80                                                               gint              *start_pos,
81                                                               gint              *end_pos);
82 static gboolean       gail_canvas_text_add_selection         (AtkText           *text,
83                                                               gint              start_pos,
84                                                               gint              end_pos);
85 static gboolean       gail_canvas_text_remove_selection      (AtkText           *text,
86                                                               gint              selection_num);
87 static gboolean       gail_canvas_text_set_selection         (AtkText           *text,
88                                                               gint              selection_num,
89                                                               gint              start_pos,
90                                                               gint              end_pos);
91 static gchar *         get_text_near_offset                   (AtkText           *text,
92                                                               GailOffsetType    function,
93                                                               AtkTextBoundary   boundary_type,
94                                                               gint              offset,
95                                                               gint              *start_offset,
96                                                               gint              *end_offset);
97 
G_DEFINE_TYPE_WITH_CODE(GailCanvasText,gail_canvas_text,GAIL_TYPE_CANVAS_ITEM,G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,gail_canvas_text_text_interface_init);)98 G_DEFINE_TYPE_WITH_CODE (GailCanvasText,
99 			gail_canvas_text,
100 			GAIL_TYPE_CANVAS_ITEM,
101 			G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,
102 					       gail_canvas_text_text_interface_init);)
103 
104 static void
105 gail_canvas_text_init (GailCanvasText *foo)
106 {
107   ;
108 }
109 
110 AtkObject *
gail_canvas_text_new(GObject * obj)111 gail_canvas_text_new (GObject *obj)
112 {
113   gpointer object;
114   AtkObject *atk_object;
115   GailCanvasText *gail_text;
116 
117   g_return_val_if_fail (GNOME_IS_CANVAS_ITEM (obj), NULL);
118   object = g_object_new (GAIL_TYPE_CANVAS_TEXT, NULL);
119   atk_object = ATK_OBJECT (object);
120   gail_text = GAIL_CANVAS_TEXT (object);
121 
122   atk_object_initialize (atk_object, obj);
123   gail_text->textutil = gail_text_util_new ();
124 
125   if (GNOME_IS_CANVAS_TEXT (obj))
126     {
127       gail_text_util_text_setup (gail_text->textutil,
128 				   GNOME_CANVAS_TEXT (obj)->text);
129     }
130 
131   atk_object->role = ATK_ROLE_TEXT;
132   return atk_object;
133 }
134 
135 static void
gail_canvas_text_class_init(GailCanvasTextClass * klass)136 gail_canvas_text_class_init (GailCanvasTextClass *klass)
137 {
138 }
139 
140 static void
gail_canvas_text_text_interface_init(AtkTextIface * iface)141 gail_canvas_text_text_interface_init (AtkTextIface *iface)
142 {
143   g_return_if_fail (iface != NULL);
144 
145   iface->get_text = gail_canvas_text_get_text;
146   iface->get_text_after_offset = gail_canvas_text_get_text_after_offset;
147   iface->get_text_at_offset = gail_canvas_text_get_text_at_offset;
148   iface->get_text_before_offset = gail_canvas_text_get_text_before_offset;
149   iface->get_character_at_offset = gail_canvas_text_get_character_at_offset;
150   iface->get_character_count = gail_canvas_text_get_character_count;
151   iface->get_caret_offset = gail_canvas_text_get_caret_offset;
152   iface->set_caret_offset = gail_canvas_text_set_caret_offset;
153   iface->get_offset_at_point = gail_canvas_text_get_offset_at_point;
154   iface->get_character_extents = gail_canvas_text_get_character_extents;
155   iface->get_n_selections = gail_canvas_text_get_n_selections;
156   iface->get_selection = gail_canvas_text_get_selection;
157   iface->add_selection = gail_canvas_text_add_selection;
158   iface->remove_selection = gail_canvas_text_remove_selection;
159   iface->set_selection = gail_canvas_text_set_selection;
160   iface->get_run_attributes = gail_canvas_text_get_run_attributes;
161   iface->get_default_attributes = gail_canvas_text_get_default_attributes;
162 }
163 
164 static gchar *
gail_canvas_text_get_text(AtkText * text,gint start_offset,gint end_offset)165 gail_canvas_text_get_text (AtkText *text,
166                            gint start_offset,
167                            gint end_offset)
168 {
169   GailCanvasText *gail_text;
170   GtkTextBuffer *buffer;
171   GtkTextIter start, end;
172 
173   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
174   gail_text = GAIL_CANVAS_TEXT (text);
175   g_return_val_if_fail (gail_text->textutil, NULL);
176 
177   buffer = gail_text->textutil->buffer;
178   gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
179   gtk_text_buffer_get_iter_at_offset (buffer, &end, end_offset);
180 
181   return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
182 }
183 
184 static gchar *
gail_canvas_text_get_text_after_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)185 gail_canvas_text_get_text_after_offset (AtkText *text,
186                                         gint offset,
187                                         AtkTextBoundary boundary_type,
188                                         gint *start_offset,
189                                         gint *end_offset)
190 {
191   return get_text_near_offset (text, GAIL_AFTER_OFFSET,
192 			       boundary_type, offset,
193 			       start_offset, end_offset);
194 }
195 
196 static gchar *
gail_canvas_text_get_text_at_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)197 gail_canvas_text_get_text_at_offset (AtkText *text,
198                                      gint offset,
199                                      AtkTextBoundary boundary_type,
200                                      gint *start_offset,
201                                      gint *end_offset)
202 {
203   return get_text_near_offset (text, GAIL_AT_OFFSET,
204 			       boundary_type, offset,
205 			       start_offset, end_offset);
206 }
207 
208 static gchar *
gail_canvas_text_get_text_before_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)209 gail_canvas_text_get_text_before_offset (AtkText *text,
210                                          gint offset,
211                                          AtkTextBoundary boundary_type,
212                                          gint *start_offset,
213                                          gint *end_offset)
214 {
215   return get_text_near_offset (text, GAIL_BEFORE_OFFSET,
216 			       boundary_type, offset,
217 			       start_offset, end_offset);
218 }
219 
220 static gunichar
gail_canvas_text_get_character_at_offset(AtkText * text,gint offset)221 gail_canvas_text_get_character_at_offset (AtkText *text,
222                                           gint offset)
223 {
224   GailCanvasText *gail_item;
225   GtkTextIter start, end;
226   GtkTextBuffer *buffer;
227   gchar *string;
228   gchar *index;
229   gunichar unichar;
230 
231   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), '\0');
232   gail_item = GAIL_CANVAS_TEXT (text);
233   buffer = gail_item->textutil->buffer;
234   if (offset >= gtk_text_buffer_get_char_count (buffer))
235     return '\0';
236 
237   gtk_text_buffer_get_start_iter (buffer, &start);
238   gtk_text_buffer_get_end_iter (buffer, &end);
239   string = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
240   index = g_utf8_offset_to_pointer (string, offset);
241 
242   unichar = g_utf8_get_char (index);
243   g_free (string);
244   return unichar;
245 }
246 
247 static gint
gail_canvas_text_get_character_count(AtkText * text)248 gail_canvas_text_get_character_count (AtkText *text)
249 {
250   GtkTextBuffer *buffer;
251   GailCanvasText *gail_text;
252 
253   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), 0);
254   gail_text = GAIL_CANVAS_TEXT (text);
255   g_return_val_if_fail (gail_text->textutil, 0);
256   buffer = gail_text->textutil->buffer;
257   return gtk_text_buffer_get_char_count (buffer);
258 }
259 
260 static gint
gail_canvas_text_get_caret_offset(AtkText * text)261 gail_canvas_text_get_caret_offset (AtkText *text)
262 {
263   GailCanvasText *gail_text;
264   GtkTextBuffer *buffer;
265   GtkTextMark *cursor_mark;
266   GtkTextIter cursor_itr;
267 
268   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), 0);
269   gail_text = GAIL_CANVAS_TEXT (text);
270   g_return_val_if_fail (gail_text->textutil, 0);
271   buffer = gail_text->textutil->buffer;
272   cursor_mark = gtk_text_buffer_get_insert (buffer);
273   gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark);
274   return gtk_text_iter_get_offset (&cursor_itr);
275 }
276 
277 static gboolean
gail_canvas_text_set_caret_offset(AtkText * text,gint offset)278 gail_canvas_text_set_caret_offset (AtkText *text,
279                                    gint offset)
280 {
281   GailCanvasText *gail_text;
282   GtkTextBuffer *buffer;
283   GtkTextIter pos_itr;
284 
285   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
286   gail_text = GAIL_CANVAS_TEXT (text);
287   g_return_val_if_fail (gail_text->textutil, FALSE);
288   buffer = gail_text->textutil->buffer;
289   gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, offset);
290   gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
291   return TRUE;
292 }
293 
294 static gint
gail_canvas_text_get_offset_at_point(AtkText * text,gint x,gint y,AtkCoordType coords)295 gail_canvas_text_get_offset_at_point (AtkText *text,
296                                       gint x,
297                                       gint y,
298                                       AtkCoordType coords)
299 {
300   return -1;
301 }
302 
303 static void
gail_canvas_text_get_character_extents(AtkText * text,gint offset,gint * x,gint * y,gint * width,gint * height,AtkCoordType coords)304 gail_canvas_text_get_character_extents (AtkText *text,
305                                         gint offset,
306                                         gint *x,
307                                         gint *y,
308                                         gint *width,
309                                         gint *height,
310                                         AtkCoordType coords)
311 {
312   return;
313 }
314 
315 static AtkAttributeSet *
gail_canvas_text_get_run_attributes(AtkText * text,gint offset,gint * start_offset,gint * end_offset)316 gail_canvas_text_get_run_attributes (AtkText *text,
317                                      gint offset,
318                                      gint *start_offset,
319                                      gint *end_offset)
320 {
321   GailCanvasText *gail_text;
322 
323   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
324   gail_text = GAIL_CANVAS_TEXT (text);
325   g_return_val_if_fail (gail_text->textutil, NULL);
326 
327   return gail_misc_buffer_get_run_attributes (gail_text->textutil->buffer,
328 					      offset, start_offset, end_offset);
329 }
330 
331 static AtkAttributeSet *
gail_canvas_text_get_default_attributes(AtkText * text)332 gail_canvas_text_get_default_attributes (AtkText *text)
333 {
334   return NULL;
335 }
336 
337 static gint
gail_canvas_text_get_n_selections(AtkText * text)338 gail_canvas_text_get_n_selections (AtkText *text)
339 {
340   GailCanvasText *gail_text;
341   GtkTextBuffer *buffer;
342   GtkTextIter start, end;
343   gint select_start, select_end;
344 
345   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), -1);
346   gail_text = GAIL_CANVAS_TEXT (text);
347   g_return_val_if_fail (gail_text->textutil, -1);
348   buffer = gail_text->textutil->buffer;
349 
350   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
351   select_start = gtk_text_iter_get_offset (&start);
352   select_end = gtk_text_iter_get_offset (&end);
353 
354   if (select_start != select_end)
355      return 1;
356   else
357      return 0;
358 }
359 
360 static gchar *
gail_canvas_text_get_selection(AtkText * text,gint selection_num,gint * start_pos,gint * end_pos)361 gail_canvas_text_get_selection (AtkText *text,
362                                 gint selection_num,
363                                 gint *start_pos,
364                                 gint *end_pos)
365 {
366   GailCanvasText *gail_text;
367   GtkTextBuffer *buffer;
368   GtkTextIter start, end;
369 
370  /* Only let the user get the selection if one is set, and if the
371   * selection_num is 0.
372   */
373   if (selection_num != 0)
374      return NULL;
375 
376   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), NULL);
377   gail_text = GAIL_CANVAS_TEXT (text);
378   g_return_val_if_fail (gail_text->textutil, NULL);
379   buffer = gail_text->textutil->buffer;
380 
381   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
382   *start_pos = gtk_text_iter_get_offset (&start);
383   *end_pos = gtk_text_iter_get_offset (&end);
384 
385   if (*start_pos != *end_pos)
386     return gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
387   else
388     return NULL;
389 }
390 
391 static gboolean
gail_canvas_text_add_selection(AtkText * text,gint start_pos,gint end_pos)392 gail_canvas_text_add_selection (AtkText *text,
393                                 gint start_pos,
394                                 gint end_pos)
395 {
396   GailCanvasText *gail_text;
397   GtkTextBuffer *buffer;
398   GtkTextIter pos_itr;
399   GtkTextIter start, end;
400   gint select_start, select_end;
401 
402   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
403   gail_text = GAIL_CANVAS_TEXT (text);
404   g_return_val_if_fail (gail_text->textutil, FALSE);
405   buffer = gail_text->textutil->buffer;
406 
407   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
408   select_start = gtk_text_iter_get_offset (&start);
409   select_end = gtk_text_iter_get_offset (&end);
410 
411  /* If there is already a selection, then don't allow another to be added,
412   * since GtkTextView only supports one selected region.
413   */
414   if (select_start == select_end)
415     {
416       gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, start_pos);
417       gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
418       gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, end_pos);
419       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr);
420       return TRUE;
421     }
422   else
423     return FALSE;
424 }
425 
426 static gboolean
gail_canvas_text_remove_selection(AtkText * text,gint selection_num)427 gail_canvas_text_remove_selection (AtkText *text,
428                                  gint selection_num)
429 {
430   GailCanvasText *gail_text;
431   GtkTextBuffer *buffer;
432   GtkTextMark *cursor_mark;
433   GtkTextIter cursor_itr;
434   GtkTextIter start, end;
435   gint select_start, select_end;
436 
437   if (selection_num != 0)
438      return FALSE;
439 
440   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
441   gail_text = GAIL_CANVAS_TEXT (text);
442   g_return_val_if_fail (gail_text->textutil, FALSE);
443   buffer = gail_text->textutil->buffer;
444 
445   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
446   select_start = gtk_text_iter_get_offset (&start);
447   select_end = gtk_text_iter_get_offset (&end);
448 
449   if (select_start != select_end)
450     {
451      /* Setting the start & end of the selected region to the caret position
452       * turns off the selection.
453       */
454       cursor_mark = gtk_text_buffer_get_insert (buffer);
455       gtk_text_buffer_get_iter_at_mark (buffer, &cursor_itr, cursor_mark);
456       gtk_text_buffer_move_mark_by_name (buffer, "insert", &cursor_itr);
457       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &cursor_itr);
458       return TRUE;
459     }
460   else
461     return FALSE;
462 }
463 
464 static gboolean
gail_canvas_text_set_selection(AtkText * text,gint selection_num,gint start_pos,gint end_pos)465 gail_canvas_text_set_selection (AtkText *text,
466                               gint selection_num,
467                               gint start_pos,
468                               gint end_pos)
469 {
470   GailCanvasText *gail_text;
471   GtkTextBuffer *buffer;
472   GtkTextIter pos_itr;
473   GtkTextIter start, end;
474   gint select_start, select_end;
475 
476  /* Only let the user move the selection if one is set, and if the
477   * selection_num is 0
478   */
479   if (selection_num != 0)
480      return FALSE;
481 
482   g_return_val_if_fail (GAIL_IS_CANVAS_TEXT (text), FALSE);
483   gail_text = GAIL_CANVAS_TEXT (text);
484   g_return_val_if_fail (gail_text->textutil, FALSE);
485   buffer = gail_text->textutil->buffer;
486 
487   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
488   select_start = gtk_text_iter_get_offset (&start);
489   select_end = gtk_text_iter_get_offset (&end);
490 
491   if (select_start != select_end)
492     {
493       gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, start_pos);
494       gtk_text_buffer_move_mark_by_name (buffer, "insert", &pos_itr);
495       gtk_text_buffer_get_iter_at_offset (buffer,  &pos_itr, end_pos);
496       gtk_text_buffer_move_mark_by_name (buffer, "selection_bound", &pos_itr);
497       return TRUE;
498     }
499   else
500     return FALSE;
501 }
502 
503 static gchar *
get_text_near_offset(AtkText * text,GailOffsetType function,AtkTextBoundary boundary_type,gint offset,gint * start_offset,gint * end_offset)504 get_text_near_offset (AtkText *text,
505                       GailOffsetType function,
506                       AtkTextBoundary boundary_type,
507                       gint offset,
508                       gint *start_offset,
509                       gint *end_offset)
510 {
511   return gail_text_util_get_text (GAIL_CANVAS_TEXT (text)->textutil, NULL,
512 				  function, boundary_type, offset,
513 				  start_offset, end_offset);
514 }
515