f54501f18857eedf9c502da3e7bd565b69c83f0d
[platform/upstream/atk.git] / atk / atktext.c
1 /* ATK - The Accessibility Toolkit for GTK+
2  * Copyright 2001 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "atktext.h"
21 #include "atkmarshal.h"
22
23 enum {
24   TEXT_CHANGED,
25   CARET_MOVED,
26   LAST_SIGNAL
27 };
28
29 struct _AtkTextIfaceClass
30 {
31   GObjectClass parent;
32 };
33
34 typedef struct _AtkTextIfaceClass AtkTextIfaceClass;
35
36 static void atk_text_base_init (gpointer *g_class);
37
38 static guint atk_text_signals[LAST_SIGNAL] = { 0 };
39
40 GType
41 atk_text_get_type ()
42 {
43   static GType type = 0;
44
45   if (!type) 
46     {
47       static const GTypeInfo tinfo =
48       {
49         sizeof (AtkTextIface),
50         (GBaseInitFunc) atk_text_base_init,
51         (GBaseFinalizeFunc) NULL,
52         (GClassInitFunc) NULL /* atk_text_interface_init */ ,
53         (GClassFinalizeFunc) NULL,
54
55       };
56
57       type = g_type_register_static (G_TYPE_INTERFACE, "AtkText", &tinfo, 0);
58     }
59
60   return type;
61 }
62
63 static void
64 atk_text_base_init (gpointer *g_class)
65 {
66   static gboolean initialized = FALSE;
67   
68   if (! initialized)
69     {
70       /* 
71        * Note that text_changed signal supports details "insert", "delete", 
72        * possibly "replace". 
73        */
74       
75       atk_text_signals[TEXT_CHANGED] =
76         g_signal_new ("text_changed",
77                       ATK_TYPE_TEXT,
78                       G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
79                       G_STRUCT_OFFSET (AtkTextIface, text_changed), 
80                       (GSignalAccumulator) NULL, NULL,
81                       atk_marshal_VOID__INT_INT,
82                       G_TYPE_NONE,
83                       2, G_TYPE_INT, G_TYPE_INT);
84       
85       atk_text_signals[CARET_MOVED] =
86         g_signal_new ("text_caret_moved",
87                       ATK_TYPE_TEXT,
88                       G_SIGNAL_RUN_LAST,
89                       G_STRUCT_OFFSET (AtkTextIface, caret_changed),
90                       (GSignalAccumulator) NULL, NULL,
91                       g_cclosure_marshal_VOID__INT,
92                       G_TYPE_NONE,
93                       1, G_TYPE_INT);
94       
95       initialized = TRUE;
96     }
97 }
98
99 /**
100  * atk_text_get_text:
101  * @text: an #AtkText
102  * @start_offset: start position
103  * @end_offset: end position
104  *
105  * Gets the specified text.
106  *
107  * Returns: the text from @start_offset up to, but not including @end_offset.
108  **/
109 gchar*
110 atk_text_get_text (AtkText      *text,
111                    gint         start_offset,
112                    gint         end_offset)
113 {
114   AtkTextIface *iface;
115   
116   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
117
118   iface = ATK_TEXT_GET_IFACE (text);
119
120   if (iface->get_text)
121     return (*(iface->get_text)) (text, start_offset, end_offset);
122   else
123     return NULL;
124 }
125
126 /**
127  * atk_text_get_character_at_offset:
128  * @text: an #AtkText
129  * @offset: position
130  *
131  * Gets the specified text.
132  *
133  * Returns: the character at @offset.
134  **/
135 gunichar
136 atk_text_get_character_at_offset (AtkText      *text,
137                                   gint         offset)
138 {
139   AtkTextIface *iface;
140
141   g_return_val_if_fail (ATK_IS_TEXT (text), (gunichar) 0);
142
143   iface = ATK_TEXT_GET_IFACE (text);
144
145   if (iface->get_character_at_offset)
146     return (*(iface->get_character_at_offset)) (text, offset);
147   else
148     return (gunichar) 0;
149 }
150
151 /**
152  * atk_text_get_text_after_offset:
153  * @text: an #AtkText
154  * @offset: position
155  * @boundary_type: An #AtkTextBoundary
156  * @start_offset: the start offset of the returned string.
157  * @end_offset: the end offset of the returned string.
158  *
159  * Gets the specified text.
160  * If the boundary type is ATK_TEXT_BOUNDARY_WORD_START or
161  * ATK_TEXT_BOUNDARY_WORD_END part of a word may be returned.
162  * If the boundary type is ATK_TEXT_BOUNDARY_SENTENCE_START the start point 
163  * will be the offset and will continue to the start of the next sentence. 
164  * The first word may not be a complete word. Similarly for 
165  * ATK_TEXT_BOUNDARY_SENTENCE_END, ATK_TEXT_BOUNDARY_LINE_START and
166  * ATK_TEXT_BOUNDARY_LINE_END
167  *
168  * Returns: the text after @offset up to the specified @boundary_type.
169  **/
170 gchar*
171 atk_text_get_text_after_offset (AtkText          *text,
172                                 gint             offset,
173                                 AtkTextBoundary  boundary_type,
174                                 gint             *start_offset,
175                                 gint             *end_offset)
176 {
177   AtkTextIface *iface;
178   gint local_start_offset, local_end_offset;
179   gint *real_start_offset, *real_end_offset;
180
181   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
182
183   if (start_offset)
184     real_start_offset = start_offset;
185   else
186     real_start_offset = &local_start_offset;
187   if (end_offset)
188     real_end_offset = end_offset;
189   else
190     real_end_offset = &local_end_offset;
191
192   iface = ATK_TEXT_GET_IFACE (text);
193
194   if (iface->get_text_after_offset)
195     return (*(iface->get_text_after_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
196   else
197     return NULL;
198 }
199
200 /**
201  * atk_text_get_text_at_offset:
202  * @text: an #AtkText
203  * @offset: position
204  * @boundary_type: An #AtkTextBoundary
205  * @start_offset: the start offset of the returned string.
206  * @end_offset: the end offset of the returned string.
207  *
208  * Gets the specified text.
209  * If the boundary_type is ATK_TEXT_BOUNDARY_WORD_START or 
210  * ATK_TEXT_BOUNDARY_WORD_END a complete word is returned; 
211  * if the boundary type is  ATK_TEXT_BOUNDARY_SENTENCE_START or 
212  * ATK_TEXT_BOUNDARY_SENTENCE_END a complete sentence
213  * is returned; if the boundary type is ATK_TEXT_BOUNDARY_LINE_START or
214  * ATK_TEXT_BOUNDARY_LINE_END a complete line is returned.
215  *
216  * Returns: the text at @offset up to the specified @boundary_type.
217  **/
218 gchar*
219 atk_text_get_text_at_offset (AtkText          *text,
220                              gint             offset,
221                              AtkTextBoundary  boundary_type,
222                              gint             *start_offset,
223                              gint             *end_offset)
224 {
225   AtkTextIface *iface;
226   gint local_start_offset, local_end_offset;
227   gint *real_start_offset, *real_end_offset;
228
229   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
230
231   if (start_offset)
232     real_start_offset = start_offset;
233   else
234     real_start_offset = &local_start_offset;
235   if (end_offset)
236     real_end_offset = end_offset;
237   else
238     real_end_offset = &local_end_offset;
239
240   iface = ATK_TEXT_GET_IFACE (text);
241
242   if (iface->get_text_at_offset)
243     return (*(iface->get_text_at_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
244   else
245     return NULL;
246 }
247
248 /**
249  * atk_text_get_text_before_offset:
250  * @text: an #AtkText
251  * @offset: position
252  * @boundary_type: An #AtkTextBoundary
253  * @start_offset: the start offset of the returned string.
254  * @end_offset: the end offset of the returned string.
255  *
256  * Gets the specified text.
257  * If the boundary type is ATK_TEXT_BOUNDARY_WORD_START or
258  * ATK_TEXT_BOUNDARY_WORD_END part of a word may be returned.
259  * If the boundary type is ATK_TEXT_BOUNDARY_SENTENCE_START the start point 
260  * will be at the start of the sentence, and will continue to the offset. 
261  * The last word may not be a complete word. Similarly for 
262  * ATK_TEXT_BOUNDARY_SENTENCE_END, ATK_TEXT_BOUNDARY_LINE_START and
263  * ATK_TEXT_BOUNDARY_LINE_END
264  *
265  * Returns: the text before @offset starting from the specified @boundary_type.
266  **/
267 gchar*
268 atk_text_get_text_before_offset (AtkText          *text,
269                                  gint             offset,
270                                  AtkTextBoundary  boundary_type,
271                                  gint             *start_offset,
272                                  gint             *end_offset)
273 {
274   AtkTextIface *iface;
275   gint local_start_offset, local_end_offset;
276   gint *real_start_offset, *real_end_offset;
277
278   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
279
280   if (start_offset)
281     real_start_offset = start_offset;
282   else
283     real_start_offset = &local_start_offset;
284   if (end_offset)
285     real_end_offset = end_offset;
286   else
287     real_end_offset = &local_end_offset;
288
289   iface = ATK_TEXT_GET_IFACE (text);
290
291   if (iface->get_text_before_offset)
292     return (*(iface->get_text_before_offset)) (text, offset, boundary_type, real_start_offset, real_end_offset);
293   else
294     return NULL;
295 }
296
297 /**
298  * atk_text_get_caret_offset:
299  * @text: an #AtkText
300  *
301  * Gets the offset position of the caret (cursor).
302  *
303  * Returns: the offset position of the caret (cursor).
304  **/
305 gint
306 atk_text_get_caret_offset (AtkText *text)
307 {
308   AtkTextIface *iface;
309
310   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
311
312   iface = ATK_TEXT_GET_IFACE (text);
313
314   if (iface->get_caret_offset)
315     return (*(iface->get_caret_offset)) (text);
316   else
317     return -1;
318 }
319
320 /**
321  * atk_text_get_character_extents:
322  * @text: an #AtkText
323  * @offset: position
324  * @x: x-position of character
325  * @y: y-position of character
326  * @width: width of character
327  * @height: height of character
328  * @coords: specify whether coordinates are relative to the screen or widget window 
329  *
330  * Given an @offset, the @x, @y, @width, and @height values are filled
331  * appropriately. 
332  **/
333 void
334 atk_text_get_character_extents (AtkText *text,
335                                 gint offset,
336                                 gint *x,
337                                 gint *y,
338                                 gint *width,
339                                 gint *height,
340                                 AtkCoordType coords)
341 {
342   AtkTextIface *iface;
343   gint local_x, local_y, local_width, local_height;
344   gint *real_x, *real_y, *real_width, *real_height;
345
346   g_return_if_fail (ATK_IS_TEXT (text));
347
348   if (x)
349     real_x = x;
350   else
351     real_x = &local_x;
352   if (y)
353     real_y = y;
354   else
355     real_y = &local_y;
356   if (width)
357     real_width = width;
358   else
359     real_width = &local_width;
360   if (height)
361     real_height = height;
362   else
363     real_height = &local_height;
364
365   iface = ATK_TEXT_GET_IFACE (text);
366
367   if (iface->get_character_extents)
368     (*(iface->get_character_extents)) (text, offset, real_x, real_y, real_width, real_height, coords);
369   else
370     {
371       *real_x = 0;
372       *real_y = 0;
373       *real_width = 0;
374       *real_height = 0;
375     }
376 }
377
378 /**
379  *atk_text_ref_run_attributes:
380  *@text: an #AtkText
381  *@offset: the offset at which to get the attributes
382  *@start_offset: the address to put the start offset of the range
383  *@end_offset: the address to put the end offset of the range
384  *
385  *Creates an #AtkAttributeSet which consists of the attributes explicitly
386  *set at the position @offset in the text. @start_offset and @end_offset are
387  *set to the start and end of the range around @offset where the attributes are
388  *invariant. See the ATK_ATTRIBUTE macros, such as #ATK_ATTRIBUTE_LEFT_MARGIN
389  *for types of text attributes that can be returned. Note that other 
390  *attributes that do not have corresponding macros may also be returned.
391  *
392  *Returns: an #AtkAttributeSet which contains the attributes explicitly set
393  *at @offset
394  **/
395 AtkAttributeSet* atk_text_ref_run_attributes              (AtkText          *text,
396                                                            gint             offset,
397                                                            gint             *start_offset,
398                                                            gint             *end_offset)
399 {
400   AtkTextIface *iface;
401   gint local_start_offset, local_end_offset;
402   gint *real_start_offset, *real_end_offset;
403
404   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
405
406   if (start_offset)
407     real_start_offset = start_offset;
408   else
409     real_start_offset = &local_start_offset;
410   if (end_offset)
411     real_end_offset = end_offset;
412   else
413     real_start_offset = &local_end_offset;
414
415   iface = ATK_TEXT_GET_IFACE (text);
416
417   if (iface->ref_run_attributes)
418     return (*(iface->ref_run_attributes)) (text, offset, real_start_offset, real_end_offset);
419   else
420     return NULL;
421 }
422
423 /**
424  * atk_text_get_character_count:
425  * @text: an #AtkText
426  *
427  * Gets the character count.
428  *
429  * Returns: the number of characters.
430  **/
431 gint
432 atk_text_get_character_count (AtkText *text)
433 {
434   AtkTextIface *iface;
435
436   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
437
438   iface = ATK_TEXT_GET_IFACE (text);
439
440   if (iface->get_character_count)
441     return (*(iface->get_character_count)) (text);
442   else
443     return -1;
444 }
445
446 /**
447  * atk_text_get_offset_at_point:
448  * @text: an #AtkText
449  * @x: screen x-position of character
450  * @y: screen y-position of character
451  * @coords: specify whether coordinates are relative to the screen or
452  * widget window 
453  *
454  * Gets the offset of the character located at coordinates @x and @y. @x and @y
455  * are interpreted as being relative to the screen or this widget's window
456  * depending on @coords.
457  *
458  * Returns: the offset to the character which is located at
459  * the specified @x and @y coordinates.
460  **/
461 gint
462 atk_text_get_offset_at_point (AtkText *text,
463                               gint x,
464                               gint y,
465                               AtkCoordType coords)
466 {
467   AtkTextIface *iface;
468
469   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
470
471   iface = ATK_TEXT_GET_IFACE (text);
472
473   if (iface->get_offset_at_point)
474     return (*(iface->get_offset_at_point)) (text, x, y, coords);
475   else
476     return -1;
477 }
478
479 /**
480  * atk_text_get_n_selections:
481  * @text: an #AtkText
482  *
483  * Gets the number of selected regions.
484  *
485  * Returns: The number of selected regions, or -1 if a failure
486  *   occurred.
487  **/
488 gint
489 atk_text_get_n_selections (AtkText *text)
490 {
491   AtkTextIface *iface;
492
493   g_return_val_if_fail (ATK_IS_TEXT (text), -1);
494
495   iface = ATK_TEXT_GET_IFACE (text);
496
497   if (iface->get_n_selections)
498     return (*(iface->get_n_selections)) (text);
499   else
500     return -1;
501 }
502
503 /**
504  * atk_text_get_selection:
505  * @text: an #AtkText
506  * @selection_num: The selection number.  The selected regions are
507  * assigned numbers that correspond to how far the region is from the
508  * start of the text.  The selected region closest to the beginning
509  * of the text region is assigned the number 0, etc.  Note that adding,
510  * moving or deleting a selected region can change the numbering.
511  * @start_offset: passes back the start position of the selected region
512  * @end_offset: passes back the end position of the selected region
513  *
514  * Gets the text from the specified selection.
515  *
516  * Returns: the selected text.
517  **/
518 gchar*
519 atk_text_get_selection (AtkText *text, 
520                         gint    selection_num,
521                         gint    *start_offset,
522                         gint    *end_offset)
523 {
524   AtkTextIface *iface;
525   gint local_start_offset, local_end_offset;
526   gint *real_start_offset, *real_end_offset;
527
528   g_return_val_if_fail (ATK_IS_TEXT (text), NULL);
529
530   if (start_offset)
531     real_start_offset = start_offset;
532   else
533     real_start_offset = &local_start_offset;
534   if (end_offset)
535     real_end_offset = end_offset;
536   else
537     real_start_offset = &local_end_offset;
538
539   iface = ATK_TEXT_GET_IFACE (text);
540
541   if (iface->get_selection)
542   {
543     return (*(iface->get_selection)) (text, selection_num,
544        real_start_offset, real_end_offset);
545   }
546   else
547     return NULL;
548 }
549
550 /**
551  * atk_text_add_selection:
552  * @text: an #AtkText
553  * @start_offset: the start position of the selected region
554  * @end_offset: the end position of the selected region
555  *
556  * Adds a selection bounded by the specified offsets.
557  *
558  * Returns: %TRUE if success, %FALSE otherwise
559  **/
560 gboolean
561 atk_text_add_selection (AtkText *text, 
562                         gint    start_offset,
563                         gint    end_offset)
564 {
565   AtkTextIface *iface;
566
567   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
568
569   iface = ATK_TEXT_GET_IFACE (text);
570
571   if (iface->add_selection)
572     return (*(iface->add_selection)) (text, start_offset, end_offset);
573   else
574     return FALSE;
575 }
576
577 /**
578  * atk_text_remove_selection:
579  * @text: an #AtkText
580  * @selection_num: The selection number.  The selected regions are
581  * assigned numbers that correspond to how far the region is from the
582  * start of the text.  The selected region closest to the beginning
583  * of the text region is assigned the number 0, etc.  Note that adding,
584  * moving or deleting a selected region can change the numbering.
585  *
586  * Removes the specified selection.
587  *
588  * Returns: %TRUE if success, %FALSE otherwise
589  **/
590 gboolean
591 atk_text_remove_selection (AtkText *text, 
592                            gint    selection_num)
593 {
594   AtkTextIface *iface;
595
596   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
597
598   iface = ATK_TEXT_GET_IFACE (text);
599
600   if (iface->remove_selection)
601     return (*(iface->remove_selection)) (text, selection_num);
602   else
603     return FALSE;
604 }
605
606 /**
607  * atk_text_set_selection:
608  * @text: an #AtkText
609  * @selection_num: The selection number.  The selected regions are
610  * assigned numbers that correspond to how far the region is from the
611  * start of the text.  The selected region closest to the beginning
612  * of the text region is assigned the number 0, etc.  Note that adding,
613  * moving or deleting a selected region can change the numbering.
614  * @start_offset: the new start position of the selection
615  * @end_offset: the new end position of the selection
616  *
617  * Changes the start and end offset of the specified selection.
618  *
619  * Returns: %TRUE if success, %FALSE otherwise
620  **/
621 gboolean
622 atk_text_set_selection (AtkText *text, 
623                         gint    selection_num,
624                         gint    start_offset, 
625                         gint    end_offset)
626 {
627   AtkTextIface *iface;
628
629   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
630
631   iface = ATK_TEXT_GET_IFACE (text);
632
633   if (iface->set_selection)
634   {
635     return (*(iface->set_selection)) (text, selection_num,
636        start_offset, end_offset);
637   }
638   else
639     return FALSE;
640 }
641
642 /**
643  * atk_text_set_caret_offset:
644  * @text: an #AtkText
645  * @offset: position
646  *
647  * Sets the caret (cursor) position to the specified @offset.
648  *
649  * Returns: %TRUE if success, %FALSE otherwise.
650  **/
651 gboolean
652 atk_text_set_caret_offset (AtkText *text,
653                            gint    offset)
654 {
655   AtkTextIface *iface;
656
657   g_return_val_if_fail (ATK_IS_TEXT (text), FALSE);
658
659   iface = ATK_TEXT_GET_IFACE (text);
660
661   if (iface->set_caret_offset)
662     {
663       return (*(iface->set_caret_offset)) (text, offset);
664     }
665   else
666     {
667       return FALSE;
668     }
669 }
670
671 /**
672  * atk_attribute_set_free:
673  * @attrib_set: The #AtkAttributeSet to free
674  *
675  * Frees the memory used by an #AtkAttributeSet, including all its
676  * #AtkAttributes.
677  **/
678 void
679 atk_attribute_set_free(AtkAttributeSet *attrib_set)
680 {
681   GSList *temp;
682
683   temp = attrib_set;
684
685   while (temp != NULL)
686     {
687       AtkAttribute *att;
688
689       att = temp->data;
690
691       g_free (att->name);
692       g_free (att->value);
693       g_free (att);
694       temp = temp->next;
695     }
696   g_slist_free (attrib_set);
697 }