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