54d7a2ff373b61ab588bc8cf38f1f3f16e7f127b
[platform/upstream/at-spi2-atk.git] / tests / dummyatk / my-atk-text.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; https://wiki.gnome.org/Accessibility)
4  *
5  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <stdio.h>
24 #include <glib.h>
25 #include <string.h>
26 #include <atk/atk.h>
27
28 #include "my-atk-object.h"
29 #include "my-atk-text.h"
30
31 typedef struct _MyAtkTextInfo MyAtkTextInfo;
32
33 static void atk_text_interface_init (AtkTextIface *iface);
34
35 typedef struct _MyAtkTextSelection MyAtkTextSelection;
36
37 struct _MyAtkTextSelection {
38   gint start;
39   gint end;
40 };
41
42 G_DEFINE_TYPE_WITH_CODE (MyAtkText,
43                          my_atk_text,
44                          MY_TYPE_ATK_OBJECT,
45                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT,
46                              atk_text_interface_init));
47
48 guint
49 my_atk_set_text (AtkText *obj,
50                  const gchar *text,
51                  const gint x,
52                  const gint y,
53                  const gint width,
54                  const gint height,
55                  AtkAttributeSet *attrSet)
56 {
57   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), -1);
58
59   MyAtkText *self = MY_ATK_TEXT (obj);
60   self->text = g_strdup (text);
61   self->x = x;
62   self->y = y;
63   self->width = width;
64   self->height = height;
65   self->attributes = g_slist_copy (attrSet);
66
67   return 0;
68 }
69
70 MyAtkText *
71 my_atk_text_new (void)
72 {
73   return g_object_new (MY_TYPE_ATK_TEXT, NULL);
74 }
75
76 static gchar *
77 my_atk_text_get_text (AtkText *obj, gint start_offset, gint end_offset)
78 {
79   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), NULL);
80   gchar *str = MY_ATK_TEXT (obj)->text;
81
82   if ((end_offset < start_offset) || start_offset < 0 || !str)
83     return NULL;
84   if (strlen (str) < end_offset)
85     return NULL;
86
87   return g_strndup (str + start_offset, end_offset - start_offset);
88 }
89
90 static gint
91 my_atk_text_get_character_count (AtkText *obj)
92 {
93   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), -1);
94   gchar *str = MY_ATK_TEXT (obj)->text;
95   if (!str) return 0;
96   return (gint) strlen (str);
97 }
98
99 static int
100 my_atk_text_get_caret_offset (AtkText *obj)
101 {
102   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), -1);
103   return MY_ATK_TEXT (obj)->caret_offset;
104 }
105
106 static gboolean
107 my_atk_text_set_caret_offset (AtkText *obj, gint offset)
108 {
109   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), FALSE);
110   MyAtkText *self = MY_ATK_TEXT (obj);
111   if (offset < 0 && strlen (self->text) <= offset)
112     return FALSE;
113   self->caret_offset = offset;
114   return TRUE;
115 }
116
117 static gunichar
118 my_atk_text_get_character_at_offset (AtkText *obj, gint offset)
119 {
120   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), 255);
121   return MY_ATK_TEXT (obj)->text[offset];
122 }
123
124 static void
125 my_atk_text_get_character_extents (AtkText *obj, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords)
126 {
127   g_return_if_fail (MY_IS_ATK_TEXT (obj));
128   MyAtkText *self = MY_ATK_TEXT (obj);
129   *x = self->x;
130   *y = self->y;
131   *width = self->width;
132   *height = self->height;
133 }
134
135 static void
136 my_atk_text_get_range_extents (AtkText *obj, gint start_offset, gint stop_offset, AtkCoordType coords, AtkTextRectangle *rect)
137 {
138   g_return_if_fail (MY_IS_ATK_TEXT (obj));
139   MyAtkText *self = MY_ATK_TEXT (obj);
140   rect->x = self->x;
141   rect->y = self->y;
142   rect->width = self->width;
143   rect->height = self->height;
144 }
145
146 static gint
147 my_atk_text_get_n_selections (AtkText *obj)
148 {
149   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), -1);
150   return g_list_length (MY_ATK_TEXT (obj)->selection);
151 }
152
153 static gboolean
154 my_atk_text_add_selection (AtkText *obj, gint start_offset, gint end_offset)
155 {
156   MyAtkText *self = MY_ATK_TEXT (obj);
157   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), FALSE);
158
159   MyAtkTextSelection *node = g_malloc (sizeof (MyAtkTextSelection));
160
161   node->start = start_offset;
162   node->end = end_offset;
163
164   self->selection = g_list_append (self->selection, node);
165
166   return TRUE;
167 }
168
169 static gchar *
170 my_atk_text_get_selection (AtkText *obj, gint selection_num, gint *start_offset, gint *end_offset)
171 {
172   MyAtkText *self = MY_ATK_TEXT (obj);
173   gchar *str = NULL;
174   GList *it;
175   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), NULL);
176
177   if (selection_num < 0)
178     return NULL;
179
180   it = g_list_nth (self->selection, selection_num);
181   if (!it)
182     return NULL;
183
184   str = my_atk_text_get_text (obj, ((MyAtkTextSelection *)it->data)->start, ((MyAtkTextSelection *)it->data)->end);
185   if (!str)
186     return NULL;
187   *start_offset = ((MyAtkTextSelection *)it->data)->start;
188   *end_offset = ((MyAtkTextSelection *)it->data)->end;
189
190   return str;
191 }
192
193 static gboolean
194 my_atk_text_set_selection (AtkText *obj, gint selection_num, gint start_offset, gint end_offset)
195 {
196   MyAtkText *self = MY_ATK_TEXT (obj);
197   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), FALSE);
198
199   GList *it;
200
201   if (selection_num < 0)
202     return FALSE;
203
204   it = g_list_nth (self->selection, selection_num);
205   if (!it)
206     return FALSE;
207
208   ((MyAtkTextSelection *)it->data)->start = start_offset;
209   ((MyAtkTextSelection *)it->data)->end = end_offset;
210
211   return TRUE;
212 }
213
214 static gboolean
215 my_atk_text_remove_selection (AtkText *obj, gint selection_num)
216 {
217   MyAtkText *self = MY_ATK_TEXT (obj);
218   GList *it;
219   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), FALSE);
220
221   if (selection_num < 0)
222     return FALSE;
223
224   it = g_list_nth (self->selection, selection_num);
225   if (!it)
226     return FALSE;
227
228   self->selection = g_list_delete_link (self->selection, it);
229   return TRUE;
230 }
231
232 static gint
233 my_atk_text_get_offset_at_point (AtkText *obj, gint x, gint y, AtkCoordType coords)
234 {
235   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), -1);
236   return 5;
237 }
238
239 static AtkAttributeSet *
240 my_atk_text_get_default_attributes (AtkText *obj)
241 {
242   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), NULL);
243   return MY_ATK_TEXT (obj)->attributes;
244 }
245
246 static AtkAttributeSet *
247 my_atk_text_get_run_attributes (AtkText *obj, gint offset, gint *start_offset, gint *end_offset)
248 {
249   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), NULL);
250   AtkAttributeSet *attributes;
251   AtkAttribute *attr;
252
253   attr = g_malloc (sizeof (AtkAttribute));
254   attr->name = g_strdup ("text_test_attr1");
255   attr->value = g_strdup ("on");
256   attributes = g_slist_append (NULL, attr);
257
258   attr = g_malloc (sizeof (AtkAttribute));
259   attr->name = g_strdup ("text_test_attr2");
260   attr->value = g_strdup ("off");
261   attributes = g_slist_append (attributes, attr);
262
263   *start_offset = 5;
264   *end_offset = 10;
265
266   return attributes;
267 }
268
269 static void setSentenceStartEnd (MyAtkText *self,gint *_offset, gint *start_offset, gint*end_offset, const gchar *fstr)
270 {
271   gchar *p_str_begin = NULL;
272   gchar *p_str_end = NULL;
273   const gint length = strlen (self->text);
274   gint offset = *_offset;
275   gint star_correction = 1;
276   /*
277    * In case if offset is in the middle of the word rewind to 1 character before.
278    */
279   for (; g_ascii_isalpha (self->text[offset]) && 0 < offset; offset--);
280   /*
281    * if [char]  rewind to word after by passing none alpha
282    * else  try to find last [string] in range [0,offset]
283    *   if  found then correct position
284    *   else not found so this is first sentence find first word
285    */
286   if (self->text[offset] == fstr[0]) {
287     for (; !g_ascii_isalpha (self->text[offset]) && offset < length; offset++);
288     p_str_begin = self->text + offset;
289   } else {
290     p_str_begin = g_strrstr_len (self->text, offset, fstr);
291     if (p_str_begin) {
292       for (; !g_ascii_isalpha (self->text[offset]) && length < offset; offset++);
293     } else {
294       for (offset = 0; !g_ascii_isalpha (self->text[offset]) && length < offset; offset++);
295       star_correction = 0;
296     }
297     p_str_begin = self->text + offset;
298   }
299   /*
300    * try find ending
301    * if not found set ending at text end.
302    * */
303   p_str_end = g_strstr_len (self->text + offset, length - offset, fstr);
304   if (!p_str_end) {
305     p_str_end = self->text + (length -1);
306   }
307   if (p_str_begin  && p_str_end) {
308     *start_offset = p_str_begin - self->text + star_correction;
309     *end_offset = p_str_end - self->text + 1;
310     *_offset = offset;
311   }
312 }
313
314 static gchar *
315 my_atk_text_get_string_at_offset (AtkText *obj, gint offset, AtkTextGranularity granularity, gint *start_offset, gint *end_offset)
316 {
317   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), NULL);
318   MyAtkText *self = MY_ATK_TEXT (obj);
319   gint cnt;
320   gint length;
321   gint myoffset = 0;
322   *start_offset = -1;
323   *end_offset = -1;
324
325   switch (granularity) {
326   case ATK_TEXT_GRANULARITY_CHAR:
327     *start_offset = offset;
328     *end_offset = *start_offset + 1;
329     break;
330   case ATK_TEXT_GRANULARITY_WORD:
331     length = strlen (self->text);
332     for (; !g_ascii_isalpha (self->text[offset]) && offset < length ; offset++);
333     for (cnt = offset; cnt < length; cnt++) {
334       if (!g_ascii_isalpha (self->text[cnt])) {
335         *start_offset = offset;
336         *end_offset = cnt - 1;
337         myoffset = 1;
338         break;
339       }
340     }
341     for (cnt = offset; 0 < cnt; cnt--) {
342       if (!g_ascii_isalpha (self->text[cnt])) {
343         *start_offset = cnt + 1;
344         break;
345       }
346     }
347     break;
348   case ATK_TEXT_GRANULARITY_SENTENCE:
349     setSentenceStartEnd (self, &offset, start_offset, end_offset, ".");
350     break;
351   case ATK_TEXT_GRANULARITY_LINE:
352     setSentenceStartEnd (self, &offset, start_offset, end_offset, "/n");
353     break;
354   case ATK_TEXT_GRANULARITY_PARAGRAPH:
355     /* Not implemented */
356     *start_offset = 0;
357     *end_offset = 0;
358     break;
359   default:
360     break;
361   }
362   return my_atk_text_get_text (obj, *start_offset, *end_offset + myoffset);
363 }
364
365 AtkTextRange **
366 my_atk_get_bounded_ranges (AtkText *obj, AtkTextRectangle *rect, AtkCoordType ctype, AtkTextClipType xclip, AtkTextClipType yclip)
367 {
368   g_return_val_if_fail (MY_IS_ATK_TEXT (obj), NULL);
369   AtkTextRange **range = g_new (AtkTextRange *, 3);
370
371   *range = g_new (AtkTextRange, 1);
372   (*range)->start_offset = 0;
373   (*range)->end_offset = 5;
374   (*range)->content = my_atk_text_get_text (obj, (*range)->start_offset, (*range)->end_offset);
375
376   *(range+1) = g_new (AtkTextRange, 1);
377   (*(range+1))->start_offset = 6;
378   (*(range+1))->end_offset = 10;
379   (*(range+1))->content = my_atk_text_get_text (obj, (*(range+1))->start_offset, (*(range+1))->end_offset);
380
381   *(range+2) = NULL;
382
383   return range;
384 }
385
386 static void
387 atk_text_interface_init (AtkTextIface *iface)
388 {
389   if (!iface) return;
390
391   iface->get_text = my_atk_text_get_text;
392   iface->get_character_count = my_atk_text_get_character_count;
393   iface->get_caret_offset = my_atk_text_get_caret_offset;
394   iface->set_caret_offset = my_atk_text_set_caret_offset;
395   iface->get_character_at_offset = my_atk_text_get_character_at_offset;
396   iface->get_character_extents = my_atk_text_get_character_extents;
397   iface->get_range_extents = my_atk_text_get_range_extents;
398   iface->get_n_selections =  my_atk_text_get_n_selections;
399   iface->add_selection = my_atk_text_add_selection;
400   iface->get_selection = my_atk_text_get_selection;
401   iface->set_selection = my_atk_text_set_selection;
402   iface->remove_selection = my_atk_text_remove_selection;
403   iface->get_offset_at_point = my_atk_text_get_offset_at_point;
404   iface->get_default_attributes = my_atk_text_get_default_attributes;
405   iface->get_string_at_offset = my_atk_text_get_string_at_offset;
406   iface->get_bounded_ranges = my_atk_get_bounded_ranges;
407   iface->get_run_attributes = my_atk_text_get_run_attributes;
408 }
409
410 static void
411 my_atk_text_init (MyAtkText *self)
412 {
413   self->text = NULL;
414   self->caret_offset = -1;
415   self->x =-1;
416   self->y = -1;
417   self->width = -1;
418   self->height = -1;
419   self->selection = NULL;
420   self->attributes = NULL;
421 }
422
423 static void
424 my_atk_text_class_initialize (AtkObject *obj, gpointer data)
425 {
426 }
427
428 static void
429 my_atk_text_class_finalize (GObject *obj)
430 {
431 }
432
433 static void
434 my_atk_text_class_init (MyAtkTextClass *my_class)
435 {
436   AtkObjectClass *atk_class = ATK_OBJECT_CLASS (my_class);
437   GObjectClass *gobject_class = G_OBJECT_CLASS (my_class);
438
439   gobject_class->finalize = my_atk_text_class_finalize;
440   atk_class->initialize = my_atk_text_class_initialize;
441 }