1 /*
2  * Copyright 2008 Codethink Ltd.
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., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 
21 #include <atk/atk.h>
22 #include <string.h>
23 #include <limits.h>
24 #include <useful_functions.h>
25 
26 #include "my-atk-text.h"
27 //*************************implementation***********************
28 
29 //Attributes
30 /*
31  * Auxiliary functions create/copy/print/free structures
32  * Use the same naming principe, as atk, but without 'atk' prefix
33  */
34  //AtkAttribute
attribute_new(const gchar * name,const gchar * value)35 AtkAttribute* attribute_new(const gchar* name, const gchar* value)
36 {
37     AtkAttribute* attr = g_malloc(sizeof(AtkAttribute));
38     if(attr == NULL) return NULL;
39     attr->name = g_strdup(name);
40     attr->value = g_strdup(value);
41     return attr;
42 }
attribute_copy(AtkAttribute * attr)43 AtkAttribute* attribute_copy(AtkAttribute* attr)
44 {
45     return attribute_new(attr->name, attr->value);
46 }
attribute_print(AtkAttribute * attr)47 void attribute_print(AtkAttribute* attr)
48 {
49     TRACE("name=%s, value=%s", attr->name, attr->value);
50 }
51 
52 //AtkAttributeSet
attribute_set_copy(AtkAttributeSet * attr)53 AtkAttributeSet* attribute_set_copy(AtkAttributeSet* attr)
54 {
55     GSList *tmp;
56     AtkAttributeSet* result = g_slist_copy(attr);
57     for(tmp = result; tmp != NULL; tmp = tmp->next)
58         tmp->data = attribute_copy((AtkAttribute*)tmp->data);
59     return result;
60 }
attribute_set_print(AtkAttributeSet * set)61 void attribute_set_print(AtkAttributeSet *set)
62 {
63     if(set == NULL)
64        TRACE0("(empty)");
65     else
66        g_slist_foreach(set, (GFunc)attribute_print, NULL);
67 }
68 
69 // STATIC FUNCTIONS
70 //
71 //auxiliary functions for search tokens
72 //Number of different characters
73 #define TABLE_SIZE 256
74 //modificator static isn't used because this tables will be use in tests
75 /*static*/ gboolean  table_word_symbols[TABLE_SIZE],
76                      table_sentence_symbols[TABLE_SIZE],
77                      table_line_symbols[TABLE_SIZE];
78 static gboolean *tables[7]={NULL,
79     table_word_symbols,
80     table_word_symbols,
81     table_sentence_symbols,
82     table_sentence_symbols,
83     table_line_symbols,
84     table_line_symbols
85     };
86 
current_token(const gchar * str,gint offset,gint * token_start,gint * token_end,const gboolean table_token_symbols[TABLE_SIZE])87 static gboolean current_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
88     const gboolean table_token_symbols[TABLE_SIZE])
89 {
90     const gchar *current = str + offset;
91     if(!table_token_symbols[(guchar)*current])
92     {
93         return FALSE;
94     }
95     for( --current; (current >= str) && table_token_symbols[(guchar)*current]; --current);
96     *token_start = current - str + 1;
97     for(current = str + offset + 1;
98         (*current != 0) && table_token_symbols[(guchar)*current]; ++current);
99     *token_end = current - str;
100     return TRUE;
101 }
next_token(const gchar * str,gint offset,gint * token_start,gint * token_end,const gboolean table_token_symbols[TABLE_SIZE])102 static gboolean next_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
103     const gboolean table_token_symbols[TABLE_SIZE])
104 {
105     const gchar *current = str + offset;
106     for( ; (*current != 0) && table_token_symbols[(guchar)*current]; ++current);
107     if(*current == 0)
108         return FALSE;
109     for(++current ; (*current != 0) && !table_token_symbols[(guchar)*current]; ++current);
110     if(!table_token_symbols[(guchar)*current])
111         return FALSE;
112     return current_token(str, current - str, token_start, token_end, table_token_symbols);
113 }
previous_token(const gchar * str,gint offset,gint * token_start,gint * token_end,const gboolean table_token_symbols[TABLE_SIZE])114 static gboolean previous_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
115     const gboolean table_token_symbols[TABLE_SIZE])
116 {
117     const gchar *current = str + offset;
118     for( ; (current > str) && table_token_symbols[(guchar)*current]; --current);
119     if(current == str)
120         return FALSE;
121     for( ; (current > str) && !table_token_symbols[(guchar)*current]; --current);
122     if(!table_token_symbols[(guchar)*current])
123         return FALSE;
124     return current_token(str, current - str, token_start, token_end, table_token_symbols);
125 }
126 
127 
128 //Range: type of data, containing in list of attributes
129 typedef struct
130 {
131     gint start,end;//range, containing this attributes
132     AtkAttributeSet* attributeSet;
133 } Range;
134 //auxiliary functions for ranges
range_new(gint start,gint end)135 Range* range_new(gint start, gint end)
136 {
137     Range *range = g_malloc(sizeof(Range));
138     range->start = start;
139     range->end = end;
140     range->attributeSet = NULL;
141     return range;
142 }
143 
range_free(Range * range)144 void range_free(Range* range)
145 {
146     atk_attribute_set_free(range->attributeSet);
147     g_free(range);
148 }
range_print(const Range * range)149 void range_print(const Range*range)
150 {
151     TRACE("[%d,%d):", range->start, range->end);
152     attribute_set_print(range->attributeSet);
153 }
154 //only for correct list of ranges - ranges shouldn't intersect
range_compare(const Range * range1,const Range * range2)155 gint range_compare(const Range* range1, const Range* range2)
156 {
157     return range1->start - range2->start;//never equal
158 }
159 //Bounds of text
text_bounds_init(TextBounds * bounds)160 void text_bounds_init(TextBounds *bounds)
161 {
162     bounds->base_x = 0;
163     bounds->base_y = 0;
164     bounds->pixels_above_line = 2;
165     bounds->pixels_below_line = 3;
166     bounds->size = 8;
167     bounds->pixels_between_characters = 1;
168     bounds->width = 3;
169 }
170 
171 //auxiliary function - create new range according to start_offset and end_offset
text_range_new(AtkText * text,gint start_offset,gint end_offset,AtkCoordType coord_type)172 AtkTextRange* text_range_new(AtkText* text,
173     gint start_offset, gint end_offset, AtkCoordType coord_type)
174 {
175     AtkTextRange* range = g_malloc(sizeof(AtkTextRange));
176     if(range == NULL) return NULL;
177     range->start_offset = start_offset;
178     range->end_offset = end_offset;
179     range->content = atk_text_get_text(text, start_offset, end_offset);
180     atk_text_get_range_extents(text, start_offset, end_offset, coord_type, &range->bounds);
181     return range;
182 }
183 // Returns number of line, which contain given character.
184 // Also return relative offset - offset of this character from start of the line
get_character_line(MyAtkText * text,gint offset,gint * relative_offset)185 gint get_character_line(MyAtkText *text, gint offset, gint *relative_offset)
186 {
187     gint result = 0;
188     //simple realization - counts lines from start of the text, until reaches offset
189     const guchar *text_str = (guchar*)text->str;
190     gboolean state_FSM = table_line_symbols[text_str[0]];
191     gint i, last_line_start = 0;
192     for(i = 1; i <= offset; state_FSM = table_line_symbols[text_str[i++]])
193     {
194         if(state_FSM)continue;
195         result++;
196         last_line_start = i;
197     }
198     if(relative_offset != NULL) *relative_offset = offset - last_line_start;
199     return result;
200 }
201 // Compute extent of character,
202 // as it was at line 'line' and at offset 'relative_offset' in that line
203 //(geometry)
get_extents(MyAtkText * text,gint line,gint relative_offset,AtkTextRectangle * rect)204 void get_extents(MyAtkText *text, gint line, gint relative_offset, AtkTextRectangle *rect)
205 {
206     rect->x = text->bounds.base_x + relative_offset *
207         (text->bounds.width + text->bounds.pixels_between_characters);
208     rect->y = text->bounds.base_y + text->bounds.pixels_above_line + line *
209         (text->bounds.size + text->bounds.pixels_below_line + text->bounds.pixels_above_line);
210     rect->width = text->bounds.width;
211     rect->height = text->bounds.size;
212 }
213 //return line, corresponding to given y-coordinate
get_point_line(MyAtkText * text,gint y)214 gint get_point_line(MyAtkText *text, gint y)
215 {
216     //slightly differ from invers operation
217     if(y - text->bounds.base_y < 0)return -1;
218     return  (y - text->bounds.base_y)
219         /(text->bounds.size + text->bounds.pixels_below_line + text->bounds.pixels_above_line);
220 }
221 // Returns offset from left boundary of text, correspondind to x-coordinate
get_point_relative_offset(MyAtkText * text,gint x)222 gint get_point_relative_offset(MyAtkText *text, gint x)
223 {
224     //slightly differ from invers operation
225     if(x - text->bounds.base_x < 0)return -1;
226     return (x - text->bounds.base_x)
227         /(text->bounds.width + text->bounds.pixels_between_characters);
228 }
229 // Returns offset where given line start(even if this line is empty)
230 // If line number too small(<0)return -1, if too big - return length of the text
get_offset_at_line_start(MyAtkText * text,gint line)231 gint get_offset_at_line_start(MyAtkText *text, gint line)
232 {
233     gint i;
234     if(line < 0)return -1;
235     if(line == 0)return 0;
236     gint len = my_strlen(text->str);
237     guchar *str = (guchar*)text->str;
238     gint current_line = 0;
239     gboolean state_FSM = table_line_symbols[str[0]];
240     for(i = 1; i < len; state_FSM = table_line_symbols[str[i++]])
241     {
242         if(state_FSM || ++current_line != line)continue;
243         return i;
244     }
245     return len;
246 
247 }
248 // Return offset of character at the given line and at the given offset at this line
249 // If such character doesn't exist, return -1
get_offset_at_line(MyAtkText * text,gint line,gint relative_offset)250 gint get_offset_at_line(MyAtkText *text, gint line, gint relative_offset)
251 {
252     gint j;
253     if(line < 0 || relative_offset < 0)return -1;
254     const guchar* str = (guchar*)text->str;
255     gint len = my_strlen(text->str);
256     gint offset_at_line_start = get_offset_at_line_start(text, line);
257     if(offset_at_line_start + relative_offset >= len)return -1;
258     for(j = 0; j <= relative_offset; j++)
259         if(!table_line_symbols[str[offset_at_line_start + j]])
260             return -1;
261     return offset_at_line_start + relative_offset;
262 }
263 /*
264  * Count ranges of text, which clipping by rel_start_offset and relative_end_offset.
265  * 'offset' stands start of search(start of first line),
266  * number_of_lines - maximum number of lines for search.
267  * If 'ranges' not NULL, writes ranges to it. 'coord_type' used only in this case.
268  */
count_ranges(MyAtkText * text,gint offset,gint rel_start_offset,gint rel_end_offset,gint number_of_lines,AtkTextRange ** ranges,AtkCoordType coord_type)269 gint count_ranges(MyAtkText *text, gint offset, gint rel_start_offset, gint rel_end_offset,
270                  gint number_of_lines, AtkTextRange** ranges, AtkCoordType coord_type)
271 {
272     guchar *str = (guchar*)text->str;
273     gint len = my_strlen(text->str);
274 
275     gint number_of_ranges = 0;
276     gint current_line = 0;
277     gint current_line_start = offset;
278     for(;(current_line < number_of_lines) && (current_line_start < len); current_line ++)
279     {
280         if(!table_line_symbols[str[current_line_start]])
281         {
282             current_line_start++;
283             continue;
284         }
285         gint start_offset,end_offset;
286         gchar *tmp_str = atk_text_get_text_at_offset((AtkText*)text, current_line_start,
287             ATK_TEXT_BOUNDARY_LINE_END, &start_offset, &end_offset);
288         g_free(tmp_str);
289         if(end_offset - current_line_start > rel_start_offset)
290         {
291             if(ranges != NULL)
292             {
293                 gint range_start_offset = current_line_start + rel_start_offset;
294                 gint range_end_offset =  current_line_start + rel_end_offset + 1;
295                 if(range_end_offset > end_offset)
296                     range_end_offset = end_offset;
297                 //add element
298                 ranges[number_of_ranges] = text_range_new((AtkText*)text,
299                     range_start_offset, range_end_offset, coord_type);
300             }
301             number_of_ranges++;
302         }
303         current_line_start = end_offset + 1;
304     }
305     if(ranges != NULL) ranges[number_of_ranges] = NULL;
306     return number_of_ranges;
307 }
308 
309 //"free"-functions(for internal using, because them don't emit signals)
my_atk_text_free_run_attributes(MyAtkText * text)310 void my_atk_text_free_run_attributes(MyAtkText *text)
311 {
312     g_list_foreach(text->attributes, (GFunc)range_free, NULL);
313     g_list_free(text->attributes);
314     text->attributes = NULL;
315 }
my_atk_text_free_default_attributes(MyAtkText * text)316 void my_atk_text_free_default_attributes(MyAtkText *text)
317 {
318     atk_attribute_set_free(text->default_attributes);
319     text->default_attributes = NULL;
320 }
my_atk_text_clear_selections(MyAtkText * text)321 void my_atk_text_clear_selections(MyAtkText *text)
322 {
323     if(text->selections->len != 0)
324         g_array_remove_range(text->selections, 0, text->selections->len);
325 }
table_symbols_init()326 void table_symbols_init()
327 {
328     //word
329     gint i;
330     for(i = TABLE_SIZE - 1;i > 0 ; --i)
331         table_word_symbols[i] = g_ascii_isalnum(i);
332     table_word_symbols['\0'] = FALSE;
333     //sentence
334     for(i = TABLE_SIZE - 1;i >= 0x20; --i)
335         table_sentence_symbols[i] = TRUE;
336     table_sentence_symbols['.'] = FALSE;
337     table_sentence_symbols['!'] = FALSE;
338     table_sentence_symbols['?'] = FALSE;
339     for(i = 0x1f;i > 0; --i)
340         table_sentence_symbols[i] = FALSE;
341     table_sentence_symbols['\0'] = FALSE;
342     //line
343     for(i = TABLE_SIZE - 1;i > 0 ; --i)
344         table_line_symbols[i] = TRUE;
345     table_line_symbols['\n'] = FALSE;
346     table_line_symbols['\0'] = FALSE;
347 }
correct_selections_after_insert(MyAtkText * text,gint position,gint length)348 void correct_selections_after_insert(MyAtkText *text, gint position, gint length)
349 {
350     gint i;
351     GArray* selections = text->selections;
352     for(i = selections->len - 1; i >=0; i--)
353     {
354         TextSelection* sel  = &g_array_index(selections, TextSelection, i);
355         if(sel->end_offset >= position) sel->end_offset+= length;
356         if(sel->start_offset >= position) sel->start_offset+= length;
357         else break;
358     }
359 }
correct_selections_after_delete(MyAtkText * text,gint position,gint length)360 void correct_selections_after_delete(MyAtkText *text, gint position, gint length)
361 {
362     gint i;
363     GArray* selections = text->selections;
364     for(i = selections->len - 1; i >=0; i--)
365     {
366         TextSelection* sel  = &g_array_index(selections, TextSelection, i);
367         if(sel->start_offset >= position)
368         {
369              if(sel->start_offset >= position + length)
370              {
371                 sel->start_offset-= length;
372                 sel->end_offset-= length;
373              }
374              //position <= sel->start_offset < position + length
375              else if(sel->end_offset > position + length)
376              {
377                 sel->start_offset = position;
378                 sel->end_offset -= length;
379              }
380              else
381              {
382                 g_array_remove_index(selections, i);
383              }
384              continue;
385         }
386         /*sel->start_offset < position*/
387         if(sel->end_offset > position + length) sel->end_offset-= length;
388         else if(sel->end_offset > position) sel->end_offset = position;
389         break;
390     }
391 }
correct_attributes_after_insert(MyAtkText * text,gint position,gint length)392 void correct_attributes_after_insert(MyAtkText* text, gint position, gint length)
393 {
394     GList *attributes = text->attributes;
395     GList *tmp;
396     //before inserted position
397     for(tmp = attributes; tmp != NULL; tmp = tmp -> next)
398     {
399         Range* range = (Range*)tmp->data;
400         if(range->end <= position)continue;
401         //range->end > position
402         if(range->start < position)
403             range->start -= length;//will be restore in the next loop
404         break;
405     }
406     //after inserted position
407     for(; tmp != NULL; tmp = tmp -> next)
408     {
409         Range* range = (Range*)tmp->data;
410         range->end += length;
411         range->start += length;
412     }
413 }
correct_attributes_after_delete(MyAtkText * text,gint position,gint length)414 void correct_attributes_after_delete(MyAtkText* text, gint position, gint length)
415 {
416     GList *attributes = text->attributes;
417     GList *tmp = attributes;
418     //before deleting range
419     for(tmp = attributes; tmp != NULL; tmp = tmp->next)
420     {
421         Range* range  = (Range*)tmp->data;
422         if(range->end <= position) continue;
423         if(range->start < position)
424         {
425              if(range->end > position + length) range->end -= length;
426              else range->end = position;
427              tmp = tmp->next;
428         }
429         break;
430     }
431     //at deleting range
432     while(tmp != NULL)
433     {
434         Range* range = (Range*)tmp->data;
435         if(range->start >= position + length) break;
436         if(range->end <= position + length)
437         {
438             GList *tmp1 = tmp->next;
439             range_free(range);
440             attributes = g_list_remove_link(attributes, tmp);
441             tmp = tmp1;
442             continue;
443         }
444         //range->end > position + length
445         //range->start < position + length
446         range->start = position + length;//will be restored in next loop
447         break;
448     }
449     //after deleting range
450     for(;tmp != NULL; tmp = tmp->next)
451     {
452         Range* range = (Range*)tmp->data;
453         range->end -= length;
454         range->start -= length;
455     }
456     text->attributes = attributes;
457 }
correct_caret_after_insert(MyAtkText * text,gint position,gint length)458 void correct_caret_after_insert(MyAtkText* text, gint position, gint length)
459 {
460     if(text->caret_offset > position)text->caret_offset += length;
461 }
correct_caret_after_delete(MyAtkText * text,gint position,gint length)462 void correct_caret_after_delete(MyAtkText* text, gint position, gint length)
463 {
464     if(text->caret_offset >= position + length)text->caret_offset -= length;
465     else if(text->caret_offset > position) text->caret_offset = position;
466 }
467 
468 // Implementation of virtual functions
469 //******************************my_atk_text_get_character_count*************************
my_atk_text_get_character_count(AtkText * text)470 static gint my_atk_text_get_character_count(AtkText *text)
471 {
472     MyAtkText *self = (MyAtkText*)text;
473     return my_strlen(self->str);
474 }
475 //**************************************my_atk_text_get_text*****************************
my_atk_text_get_text(AtkText * text,gint start_offset,gint end_offset)476 static gchar* my_atk_text_get_text(AtkText *text, gint start_offset, gint end_offset)
477 {
478     gchar *str = ((MyAtkText*)text)->str;
479     if((start_offset < 0) || (end_offset > my_strlen(str)) || (end_offset <= start_offset))
480     {
481         //incorrect bounds
482         return NULL;
483     }
484     return g_strndup(str + start_offset, end_offset - start_offset);
485 
486 }
487 //*******************************my_atk_text_get_character_at_offset************************
my_atk_text_get_character_at_offset(AtkText * text,gint offset)488 static gunichar my_atk_text_get_character_at_offset(AtkText *text, gint offset)
489 {
490     gchar *str = ((MyAtkText*)text)->str;
491     if(offset < 0 || offset >= my_strlen(str))
492     {
493         return 0;
494     }
495     return (gunichar)str[offset];
496 }
497 // In the next 3 functions some code is commented for verify tests themselves on 'mutants'
498 // in realization.
499 //******************************my_atk_text_get_text_after_offset***************************
my_atk_text_get_text_after_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)500 static gchar* my_atk_text_get_text_after_offset(AtkText *text, gint offset,
501     AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset)
502 {
503     gchar *str = ((MyAtkText*)text)->str;
504     gint len = my_strlen(str);
505     if((offset < 0) || (offset >= len))
506     {
507         return NULL;//incorrect offset
508     }
509 
510     // This variable is set in switch statement. If after that statement variable is TRUE,
511     // then return text from 'strat_offset' to 'end_offset'. Otherwise NULL will be returned.
512     gboolean is_successed = TRUE;
513 
514     gint start_tmp;
515     gint end_tmp;
516 
517     switch(boundary_type)
518     {
519     case ATK_TEXT_BOUNDARY_CHAR:
520         if(offset + 1 == len)
521         {
522             is_successed = FALSE;
523             break;
524         }
525         *start_offset = offset + 1;
526         *end_offset = offset + 2;
527         is_successed = TRUE;
528         break;
529     case ATK_TEXT_BOUNDARY_WORD_START:
530     case ATK_TEXT_BOUNDARY_SENTENCE_START:
531     case ATK_TEXT_BOUNDARY_LINE_START:
532         if(!next_token(str, offset, start_offset, &end_tmp, tables[boundary_type]))
533         {
534             //debug
535 //            if(current_token(str, offset, start_offset, end_offset, tables[boundary_type]))
536 //            {
537 //                is_successed = TRUE;
538 //                break;
539 //            }
540             is_successed = FALSE;
541             break;
542         }
543         if(!next_token(str, end_tmp, end_offset, &end_tmp, tables[boundary_type]))
544         {
545             *end_offset = len;
546         }
547         is_successed = TRUE;
548         //debug
549 //        (*start_offset)++;
550 //        if(*start_offset > 10) ++(*start_offset);
551         break;
552     case ATK_TEXT_BOUNDARY_WORD_END:
553     case ATK_TEXT_BOUNDARY_SENTENCE_END:
554     case ATK_TEXT_BOUNDARY_LINE_END:
555         if(!current_token(str, offset, &start_tmp, start_offset, tables[boundary_type]))
556         {
557             if(!next_token(str, offset, &start_tmp, start_offset, tables[boundary_type]))
558             {
559                 is_successed = FALSE;
560                 break;
561             }
562         }
563         //debug
564 //        else if(*start_offset > strlen(str) - 7)
565 //        {
566 //           *end_offset = *start_offset + 3;
567 //            is_successed = TRUE;
568 //           break;
569 //        }
570         if(!next_token(str, *start_offset, &start_tmp, end_offset, tables[boundary_type]))
571         {
572             is_successed = FALSE;
573             break;
574         }
575         //debug
576 //        --(*start_offset);
577         is_successed = TRUE;
578 
579         break;
580     default:
581         is_successed = FALSE;
582     }
583 
584     if(is_successed)
585     {
586         return my_atk_text_get_text(text, *start_offset, *end_offset);
587     }
588     else
589     {
590         return NULL;
591     }
592 }
593 //*******************************my_atk_text_get_text_at_offset*******************************
my_atk_text_get_text_at_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)594 static gchar* my_atk_text_get_text_at_offset(AtkText *text, gint offset,
595     AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset)
596 {
597     gchar *str = ((MyAtkText*)text)->str;
598     gint len = my_strlen(str);
599     if((offset < 0) || (offset >= len))
600     {
601         return NULL;
602     }
603 
604     // This variable is set in switch statement. If after that statement variable is TRUE,
605     // then return text from 'strat_offset' to 'end_offset'. Otherwise NULL will be returned.
606     gboolean is_successed = TRUE;
607 
608     gint start_tmp;
609     gint end_tmp;
610 
611     switch(boundary_type)
612     {
613     case ATK_TEXT_BOUNDARY_CHAR:
614         *start_offset = offset;
615         *end_offset = offset + 1;
616         is_successed = TRUE;
617         break;
618     case ATK_TEXT_BOUNDARY_WORD_START:
619     case ATK_TEXT_BOUNDARY_SENTENCE_START:
620     case ATK_TEXT_BOUNDARY_LINE_START:
621         if(!current_token(str, offset, start_offset, &end_tmp, tables[boundary_type]))
622         {
623             if(!previous_token(str, offset, start_offset, &end_tmp, tables[boundary_type]))
624             {
625                 is_successed = FALSE;
626                 break;
627             }
628         }
629         if(!next_token(str, offset, end_offset, &end_tmp, tables[boundary_type]))
630         {
631             *end_offset = len;
632         }
633         is_successed = TRUE;
634         break;
635     case ATK_TEXT_BOUNDARY_WORD_END:
636     case ATK_TEXT_BOUNDARY_SENTENCE_END:
637     case ATK_TEXT_BOUNDARY_LINE_END:
638         if(!current_token(str, offset, &start_tmp, end_offset, tables[boundary_type]))
639         {
640             if(!next_token(str, offset, &start_tmp, end_offset, tables[boundary_type]))
641             {
642                 is_successed = FALSE;
643                 break;
644             }
645         }
646         if(!previous_token(str, start_tmp, &start_tmp, start_offset, tables[boundary_type]))
647         {
648             *start_offset = 0;
649         }
650         is_successed = TRUE;
651         //debug
652 //        ++(*start_offset);
653         break;
654     default:
655         is_successed = FALSE;
656     }
657 
658     if(is_successed)
659     {
660         //debug
661 //        if(boundary_type == ATK_TEXT_BOUNDARY_LINE_START)
662 //            return my_atk_text_get_text(text, ++(*start_offset), *end_offset);
663         return my_atk_text_get_text(text, *start_offset, *end_offset);
664     }
665     else
666     {
667         return NULL;
668     }
669 }
670 //***********************************my_atk_text_get_text_before_offset******************
my_atk_text_get_text_before_offset(AtkText * text,gint offset,AtkTextBoundary boundary_type,gint * start_offset,gint * end_offset)671 static gchar* my_atk_text_get_text_before_offset(AtkText *text, gint offset,
672     AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset)
673 {
674     gchar *str = ((MyAtkText*)text)->str;
675     gint len = my_strlen(str);
676     if((offset < 0) || (offset >= len))
677     {
678         return NULL;
679     }
680 
681     // This variable is set in switch statement. If after that statement variable is TRUE,
682     // then return text from 'strat_offset' to 'end_offset'. Otherwise NULL will be returned.
683     gboolean is_successed = TRUE;
684 
685     gint start_tmp;
686     gint end_tmp;
687 
688     switch(boundary_type)
689     {
690     case ATK_TEXT_BOUNDARY_CHAR:
691         if(offset == 0)
692         {
693             is_successed = FALSE;
694             break;
695         }
696         *start_offset = offset - 1;
697         *end_offset = offset;
698         is_successed = TRUE;
699         break;
700     case ATK_TEXT_BOUNDARY_WORD_START:
701     case ATK_TEXT_BOUNDARY_SENTENCE_START:
702     case ATK_TEXT_BOUNDARY_LINE_START:
703         if(!current_token(str, offset, end_offset, &end_tmp, tables[boundary_type]))
704         {
705             if(!previous_token(str, offset, end_offset, &end_tmp, tables[boundary_type]))
706             {
707                 is_successed = FALSE;
708                 break;
709             }
710         }
711         if(!previous_token(str, *end_offset, start_offset, &end_tmp, tables[boundary_type]))
712         {
713             is_successed = FALSE;
714             break;
715         }
716         is_successed = TRUE;
717         //debug
718 //        ++(*start_offset);
719         break;
720     case ATK_TEXT_BOUNDARY_WORD_END:
721     case ATK_TEXT_BOUNDARY_SENTENCE_END:
722     case ATK_TEXT_BOUNDARY_LINE_END:
723         if(!previous_token(str, offset, &start_tmp, end_offset, tables[boundary_type]))
724         {
725             is_successed = FALSE;
726             break;
727         }
728         if(!previous_token(str, start_tmp, &start_tmp, start_offset, tables[boundary_type]))
729         {
730             *start_offset = 0;
731         }
732         is_successed = TRUE;
733         break;
734     default:
735         is_successed = FALSE;
736     }
737 
738     if(is_successed)
739     {
740         return my_atk_text_get_text(text, *start_offset, *end_offset);
741     }
742     else
743     {
744         return NULL;
745     }
746 }
747 //*********************************my_atk_text_get_run_attributes*****************
my_atk_text_get_run_attributes(AtkText * text,gint offset,gint * start_offset,gint * end_offset)748 AtkAttributeSet* my_atk_text_get_run_attributes(AtkText* text, gint offset,
749     gint *start_offset, gint *end_offset)
750 {
751     GList *tmp;
752     GList *attributes = ((MyAtkText*)text)->attributes;
753     if(offset < 0 || offset >= my_atk_text_get_character_count(text))
754     {
755         TRACE0("Incorrect value of offset.");
756         return NULL;
757     }
758     gint start = -1, end = -1;
759     for(tmp = attributes; tmp != NULL; tmp = tmp->next)
760     {
761         Range* range = (Range*)(tmp->data);
762         if(range->end <= offset)
763         {
764              start = range->end;
765              continue;
766         }
767         if(range->start > offset)
768         {
769              end = range->start;
770              break;
771         }
772 
773         *start_offset = range->start;
774         *end_offset = range->end;
775         return attribute_set_copy(range->attributeSet);
776     }
777     *start_offset = (start == -1) ? 0 : start;
778     *end_offset = (end == -1) ? my_atk_text_get_character_count(text) : end;
779     return NULL;
780 }
781 //*********************************my_atk_text_get_default_attributes*****************
my_atk_text_get_default_attributes(AtkText * text)782 AtkAttributeSet* my_atk_text_get_default_attributes(AtkText* text)
783 {
784     return attribute_set_copy(((MyAtkText*)text)->default_attributes);
785 }
786 //*********************************my_atk_text_get_character_extents*****************
my_atk_text_get_character_extents(AtkText * text,gint offset,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)787 void my_atk_text_get_character_extents(AtkText* text, gint offset, gint *x, gint *y,
788     gint *width, gint *height, AtkCoordType coord_type)
789 {
790     AtkTextRectangle result;
791     gint relative_offset, line;
792     line = get_character_line((MyAtkText*)text, offset, &relative_offset);
793     get_extents((MyAtkText*)text, line, relative_offset, &result);
794     *x = result.x;
795     *y = result.y;
796     *width = result.width;
797     *height = result.height;
798 }
799 //*******************************my_atk_text_get_range_extents************************
my_atk_text_get_range_extents(AtkText * text,gint start_offset,gint end_offset,AtkCoordType coord_type,AtkTextRectangle * rect)800 void my_atk_text_get_range_extents(AtkText *text, gint start_offset, gint end_offset,
801     AtkCoordType coord_type, AtkTextRectangle *rect)
802 {
803     //simple - union of extents of the characters, contained in this range
804     AtkTextRectangle result, bounds_tmp;
805     gint i;
806 
807     atk_text_get_character_extents (text, start_offset,
808                                   &result.x, &result.y,
809                                   &result.width, &result.height,
810                                   coord_type);
811 
812     for (i = start_offset + 1; i < end_offset; i++)
813     {
814         my_atk_text_get_character_extents (text, i,&bounds_tmp.x, &bounds_tmp.y,
815             &bounds_tmp.width, &bounds_tmp.height, coord_type);
816 
817         if(bounds_tmp.x < result.x)
818         {
819             //corrects left boundary
820             result.width += result.x - bounds_tmp.x;
821             result.x = bounds_tmp.x;
822         }
823         if(bounds_tmp.x + bounds_tmp.width > result.x + result.width)
824         {
825             //corrects right boundary
826             result.width = bounds_tmp.x + bounds_tmp.width - result.x;
827         }
828         if(bounds_tmp.y < result.y)
829         {
830             //corrects top boundary
831             result.height += result.y - bounds_tmp.y;
832             result.y = bounds_tmp.y;
833         }
834         if(bounds_tmp.y + bounds_tmp.height > result.y + result.height)
835         {
836             //corrects buttom boundary
837             result.height = bounds_tmp.y + bounds_tmp.height - result.y;
838         }
839     }
840     *rect = result;
841 }
842 //**********************************my_atk_text_get_offset_at_point*********************
my_atk_text_get_offset_at_point(AtkText * text,gint x,gint y,AtkCoordType coord_type)843 gint my_atk_text_get_offset_at_point(AtkText* text, gint x, gint y, AtkCoordType coord_type)
844 {
845     gint line, relative_offset;
846 
847     line = get_point_line((MyAtkText*)text, y);
848     relative_offset = get_point_relative_offset((MyAtkText*)text, x);
849 
850     return get_offset_at_line((MyAtkText*)text, line, relative_offset);
851 }
852 //*****************************my_atk_text_get_bounded_ranges******************************
my_atk_text_get_bounded_ranges(AtkText * text,AtkTextRectangle * rect,AtkCoordType coord_type,AtkTextClipType x_clip_type,AtkTextClipType y_clip_type)853 AtkTextRange** my_atk_text_get_bounded_ranges(AtkText *text, AtkTextRectangle *rect,
854     AtkCoordType coord_type, AtkTextClipType x_clip_type, AtkTextClipType y_clip_type)
855 {
856     MyAtkText *self = (MyAtkText*)text;
857 
858     gint start_line, end_line, start_rel_offset, end_rel_offset;
859     AtkTextRange** result;
860     gint len = my_strlen(self->str);
861 //macro for simplify return empty ranges when fail to do smth
862 #define RETURN_EMTPY_RANGES {result = g_malloc(sizeof(AtkTextRange*));result[0] = NULL;return result;}
863     //start line
864     start_line = (y_clip_type == ATK_TEXT_CLIP_NONE) || (y_clip_type == ATK_TEXT_CLIP_MAX)
865         ? 0 : get_point_line(self, rect->y);
866     if(start_line < 0) start_line = 0;
867     //end line
868     end_line = (y_clip_type == ATK_TEXT_CLIP_NONE) || (y_clip_type == ATK_TEXT_CLIP_MIN)
869         ? G_MAXINT/2 : get_point_line(self, rect->y  + rect->height);
870     if(end_line < 0) RETURN_EMTPY_RANGES;
871     //start relative offset
872     start_rel_offset = (x_clip_type == ATK_TEXT_CLIP_NONE) || (x_clip_type == ATK_TEXT_CLIP_MAX)
873         ? 0 : get_point_relative_offset(self, rect->x);
874     if(start_rel_offset < 0) start_rel_offset = 0;
875     //end relative offset
876     end_rel_offset = (x_clip_type == ATK_TEXT_CLIP_NONE) || (x_clip_type == ATK_TEXT_CLIP_MIN)
877         ? G_MAXINT/2 : get_point_relative_offset(self, rect->x + rect->width);
878     if(end_rel_offset < 0) RETURN_EMTPY_RANGES;
879     //start offset(at the start of 'start_line')
880     gint start_offset = get_offset_at_line_start(self, start_line);
881     if(start_offset + start_rel_offset >= len) RETURN_EMTPY_RANGES;
882 
883     //count ranges
884     gint number_of_ranges = count_ranges(self, start_offset,
885         start_rel_offset, end_rel_offset, end_line - start_line + 1, NULL, coord_type);
886     //create array(with just getting size)
887     result = g_malloc(sizeof(AtkTextRange*) * (number_of_ranges + 1));
888     //write ranges
889     count_ranges(self, start_offset,
890         start_rel_offset, end_rel_offset, end_line - start_line + 1, result, coord_type);
891 #undef RETURN_EMPTY_RANGES
892     return result;
893 }
894 
895 //********************************my_atk_text_get_n_selections*******************************
my_atk_text_get_n_selections(AtkText * text)896 gint my_atk_text_get_n_selections(AtkText *text)
897 {
898     MyAtkText *self = (MyAtkText*)text;
899     return self->selections->len;
900 }
901 
902 //********************************my_atk_text_get_selection*******************************
my_atk_text_get_selection(AtkText * text,gint selection_num,gint * start_offset,gint * end_offset)903 gchar* my_atk_text_get_selection(AtkText *text,
904     gint selection_num, gint *start_offset, gint *end_offset)
905 {
906     MyAtkText *self = (MyAtkText*)text;
907     GArray *selections = self->selections;
908     if(selection_num < 0 || selection_num >= selections->len) return NULL;
909     *start_offset = g_array_index(selections, TextSelection, selection_num).start_offset;
910     *end_offset = g_array_index(selections, TextSelection, selection_num).end_offset;
911     return my_atk_text_get_text(text, *start_offset, *end_offset);
912 }
913 //********************************my_atk_text_remove_selection*******************************
my_atk_text_remove_selection(AtkText * text,gint selection_num)914 gboolean my_atk_text_remove_selection(AtkText *text, gint selection_num)
915 {
916     MyAtkText *self = (MyAtkText*)text;
917     GArray *selections = self->selections;
918     if(selection_num < 0 || selection_num >= selections->len) return FALSE;
919     g_array_remove_index(selections, selection_num);
920 
921     g_signal_emit_by_name(text, "text-selection-changed");
922     return TRUE;
923 }
924 //********************************my_atk_text_add_selection*******************************
my_atk_text_add_selection(AtkText * text,gint start_offset,gint end_offset)925 gboolean my_atk_text_add_selection(AtkText *text, gint start_offset, gint end_offset)
926 {
927     if(start_offset < 0 || end_offset > my_atk_text_get_character_count(text)
928         || start_offset >= end_offset) return FALSE;
929 
930     MyAtkText *self = (MyAtkText*)text;
931     GArray *selections = self->selections;
932     gint i;
933     for(i = 0; i < selections->len; i++)
934     {
935         if(g_array_index(selections, TextSelection, i).start_offset >= start_offset)
936         {
937             if(g_array_index(selections, TextSelection, i).start_offset < end_offset)
938                 return FALSE;
939             break;
940         }
941     }
942     TextSelection new_selection;
943     new_selection.start_offset = start_offset;
944     new_selection.end_offset = end_offset;
945     g_array_insert_val(selections, i, new_selection);
946 
947     g_signal_emit_by_name(text, "text-selection-changed");
948     return TRUE;
949 }
950 //********************************my_atk_text_set_selection*******************************
my_atk_text_set_selection(AtkText * text,gint selection_num,gint start_offset,gint end_offset)951 gboolean my_atk_text_set_selection(AtkText *text,
952     gint selection_num, gint start_offset, gint end_offset)
953 {
954     MyAtkText *self = (MyAtkText*)text;
955     GArray *selections = self->selections;
956     if(selection_num < 0 || selection_num >= selections->len) return FALSE;
957 
958     if((selection_num == 0
959         || g_array_index(selections, TextSelection, selection_num - 1).end_offset <= start_offset)
960         && (selection_num == selections->len - 1
961         || g_array_index(selections, TextSelection, selection_num + 1).start_offset >= end_offset)
962         )
963     {
964         //Arrange of selections won't change
965         g_array_index(selections, TextSelection, selection_num).start_offset =
966             start_offset;
967         g_array_index(selections, TextSelection, selection_num).end_offset =
968             end_offset;
969         g_signal_emit_by_name(text, "text-selection-changed");
970         return TRUE;
971     }
972     gint start_offset_old =
973         g_array_index(selections, TextSelection, selection_num).start_offset;
974     gint end_offset_old =
975         g_array_index(selections, TextSelection, selection_num).end_offset;
976 
977     my_atk_text_remove_selection(text, selection_num);
978     if(!my_atk_text_add_selection(text, start_offset, end_offset))
979     {
980         //fail when adding selection. Restore initial state.
981         my_atk_text_add_selection(text, start_offset_old, end_offset_old);
982         return FALSE;
983     }
984     g_signal_emit_by_name(text, "text-selection-changed");
985     return TRUE;
986 
987 }
988 
989 //************************************my_atk_text_get_caret_offset******************
my_atk_text_get_caret_offset(AtkText * text)990 gint my_atk_text_get_caret_offset(AtkText *text)
991 {
992     MyAtkText *self = (MyAtkText*)text;
993     return self->caret_offset;
994 }
995 //************************************my_atk_text_set_caret_offset******************
my_atk_text_set_caret_offset(AtkText * text,gint offset)996 gboolean my_atk_text_set_caret_offset(AtkText *text, gint offset)
997 {
998     MyAtkText *self = (MyAtkText*)text;
999     //caret may be set just after the last character
1000     if(offset < 0 || offset > my_atk_text_get_character_count(text))return FALSE;
1001     self->caret_offset = offset;
1002     g_signal_emit_by_name(self, "text-caret-moved", offset);
1003     return TRUE;
1004 }
1005 
1006 //***********************my_atk_text_insert_text*******************************
my_atk_text_insert_text(AtkEditableText * text,const gchar * string,gint length,gint * position)1007 void my_atk_text_insert_text(AtkEditableText* text, const gchar* string,
1008     gint length, gint *position)
1009 {
1010     gint i;
1011     MyAtkText* myAtkText = (MyAtkText*)text;
1012     gchar *str = myAtkText->str;
1013     gint strlen_old = my_strlen(str);
1014 
1015     if(string == NULL) return;
1016     //correct length
1017     for(i = 0; i < length; i ++)
1018     {
1019         if(string[i] == '\0') {length = i; break;}
1020     }
1021 
1022     if(*position < 0 || *position > strlen_old || length <= 0 )return;
1023 
1024 
1025     gchar *new_str = g_malloc(strlen_old + length + 1);
1026     if(new_str == NULL)return;
1027 
1028     if(*position != 0)
1029         memcpy(new_str, str, (size_t)*position);
1030     memcpy(new_str + *position, string, (size_t)length);
1031     if(strlen_old != *position)
1032         memcpy(new_str + *position + length, str + *position,
1033             (size_t)(strlen_old - *position));
1034     new_str[strlen_old + length] = '\0';
1035 
1036     g_free(str);
1037     myAtkText->str = new_str;
1038     correct_selections_after_insert(myAtkText, *position, length);
1039     correct_attributes_after_insert(myAtkText, *position, length);
1040     correct_caret_after_insert(myAtkText, *position, length);
1041     g_signal_emit_by_name(text, "text-changed::insert", *position, length);
1042     g_signal_emit_by_name(text, "text-selection-changed");
1043     g_signal_emit_by_name(text, "text-attributes-changed");
1044     g_signal_emit_by_name(text, "text-caret-moved", myAtkText->caret_offset);
1045 
1046     (*position) += length;
1047 }
1048 //*************************my_atk_text_delete_text*******************
my_atk_text_delete_text(AtkEditableText * text,gint start_pos,gint end_pos)1049 void my_atk_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
1050 {
1051     MyAtkText* myAtkText = (MyAtkText*)text;
1052     gchar *str = myAtkText->str;
1053     gint strlen_old = my_strlen(str);
1054 
1055     if(start_pos < 0 || end_pos > strlen_old || start_pos >= end_pos )return;
1056     if(strlen_old != end_pos)
1057         memmove(str + start_pos, str + end_pos, strlen_old - end_pos);
1058     str[start_pos - end_pos + strlen_old] = '\0';
1059 
1060     correct_selections_after_delete(myAtkText, start_pos, end_pos - start_pos);
1061     correct_attributes_after_delete(myAtkText, start_pos, end_pos - start_pos);
1062     correct_caret_after_delete(myAtkText, start_pos, end_pos - start_pos);
1063     g_signal_emit_by_name(text, "text-changed::delete", start_pos, end_pos - start_pos);
1064     g_signal_emit_by_name(text, "text-selection-changed");
1065     g_signal_emit_by_name(text, "text-attributes-changed");
1066     g_signal_emit_by_name(text, "text-caret-moved", myAtkText->caret_offset);
1067 }
1068 //***********************my_atk_text_set_text_contents*************************
my_atk_text_set_text_contents(AtkEditableText * text,const gchar * string)1069 void my_atk_text_set_text_contents(AtkEditableText* text, const gchar* string)
1070 {
1071     my_atk_text_delete_text(text, 0, my_atk_text_get_character_count((AtkText*)text));
1072     gint position = 0;
1073     my_atk_text_insert_text(text, string, my_strlen(string), &position);
1074 }
1075 //**********************my_atk_text_copy_text***************************
my_atk_text_copy_text(AtkEditableText * text,gint start_pos,gint end_pos)1076 void my_atk_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
1077 {
1078     MyAtkText* myAtkText = (MyAtkText*)text;
1079     const gchar *str = myAtkText->str;
1080     gint strlen_old = my_strlen(str);
1081     if(start_pos < 0 || end_pos > strlen_old || start_pos >= end_pos )return;
1082 
1083     MyAtkTextClass *parent = MY_ATK_TEXT_GET_CLASS(text);
1084     g_free(parent->clipboard);
1085     /*parent->clipboard = g_malloc(end_pos - start_pos + 1);
1086 
1087     strncpy(parent->clipboard, str + start_pos, end_pos - start_pos);
1088     parent->clipboard[end_pos - start_pos] = '\0';*/
1089     parent->clipboard = g_strndup(str + start_pos, end_pos - start_pos);
1090 }
1091 //**********************my_atk_text_paste_text***************************
my_atk_text_paste_text(AtkEditableText * text,gint position)1092 void my_atk_text_paste_text(AtkEditableText *text, gint position)
1093 {
1094     //NULL-clipboard process corretly
1095     MyAtkTextClass* parent = MY_ATK_TEXT_GET_CLASS(text);
1096     my_atk_text_insert_text(text, parent->clipboard, my_strlen(parent->clipboard), &position);
1097 }
1098 //**********************my_atk_text_cut_text***************************
my_atk_text_cut_text(AtkEditableText * text,gint start_pos,gint end_pos)1099 void my_atk_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
1100 {
1101     my_atk_text_copy_text(text, start_pos, end_pos);
1102     my_atk_text_delete_text(text, start_pos, end_pos);
1103 }
1104 //*********************my_atk_text_set_run_attributes************************
my_atk_text_set_run_attributes(AtkEditableText * text,AtkAttributeSet * attrib_set,gint start_offset,gint end_offset)1105 gboolean my_atk_text_set_run_attributes(AtkEditableText* text, AtkAttributeSet* attrib_set,
1106     gint start_offset, gint end_offset)
1107 {
1108     MyAtkText* self = (MyAtkText*)text;
1109     gint len = atk_text_get_character_count((AtkText*)text);
1110     if(start_offset < 0 || start_offset >= end_offset || end_offset > len)
1111         return FALSE;
1112     GList *attributes = self->attributes;
1113     GList *tmp = attributes;
1114 
1115     while(tmp != NULL)
1116     {
1117         Range *range = (Range*)tmp->data;
1118         if(range->start < start_offset)
1119         {
1120             if(range->end <= end_offset)
1121             {
1122                 if(range->end > start_offset) range->end = start_offset;
1123                 tmp = tmp->next;
1124                 continue;
1125             }
1126             /*range->end > end_offset*/
1127             Range* additional_range = range_new(end_offset, range->end);
1128             additional_range->attributeSet = attribute_set_copy(range->attributeSet);
1129             range->end = start_offset;
1130             attributes = g_list_insert_before(attributes, tmp->next, additional_range);
1131             tmp = tmp->next;
1132             break;
1133         }
1134         else/*range->start >= start_offset*/
1135         {
1136             if(range->end <= end_offset)
1137             {
1138                 GList *tmp1 = tmp->next;
1139                 attributes = g_list_remove_link(attributes, tmp);
1140                 tmp = tmp1;
1141                 continue;
1142             }
1143             /*range->end > end_offset*/
1144             if(range->start < end_offset) range->start = end_offset;
1145             break;
1146         }
1147     }
1148     Range *new_range = range_new(start_offset, end_offset);
1149     new_range->attributeSet = attribute_set_copy(attrib_set);
1150     if(tmp == NULL)attributes = g_list_append(attributes, new_range);
1151     else attributes = g_list_insert_before(attributes, tmp, new_range);
1152 
1153     self->attributes = attributes;
1154     g_signal_emit_by_name(self, "text_attributes_changed");
1155     return TRUE;
1156 }
1157 
1158 //others functions
1159 //sets default attributes
my_atk_text_set_default_attributes(MyAtkText * text,AtkAttributeSet * set)1160 void my_atk_text_set_default_attributes(MyAtkText* text, AtkAttributeSet *set)
1161 {
1162     atk_attribute_set_free(text->default_attributes);
1163     text->default_attributes = attribute_set_copy(set);
1164     g_signal_emit_by_name(text, "text-attributes-changed");
1165 }
1166 
my_atk_text_print_run_attributes(MyAtkText * text)1167 void my_atk_text_print_run_attributes(MyAtkText *text)
1168 {
1169     g_list_foreach(text->attributes, (GFunc)range_print, NULL);
1170 }
my_atk_text_print_default_attributes(MyAtkText * text)1171 void my_atk_text_print_default_attributes(MyAtkText *text)
1172 {
1173     attribute_set_print(text->default_attributes);
1174 }
1175 //need for separate testing interfaces
auxiliary_atk_text_set_text_contents(MyAtkText * text,const gchar * string)1176 void auxiliary_atk_text_set_text_contents(MyAtkText* text, const gchar* string)
1177 {
1178     my_atk_text_set_text_contents((AtkEditableText*)text, string);
1179 }
auxiliary_atk_text_set_run_attributes(MyAtkText * text,AtkAttributeSet * attrib_set,gint start_offset,gint end_offset)1180 void auxiliary_atk_text_set_run_attributes(MyAtkText* text, AtkAttributeSet* attrib_set,
1181     gint start_offset, gint end_offset)
1182 {
1183     my_atk_text_set_run_attributes((AtkEditableText*)text, attrib_set, start_offset, end_offset);
1184 }
1185 
1186 //initialize/finalize functions
my_atk_text_instance_init(GTypeInstance * obj,gpointer g_class)1187 static void my_atk_text_instance_init(GTypeInstance *obj, gpointer g_class)
1188 {
1189     MyAtkText *self = (MyAtkText*)obj;
1190 
1191     self->str = NULL;
1192     self->attributes = NULL;
1193     self->default_attributes = NULL;
1194     text_bounds_init(&self->bounds);
1195 
1196     self->selections = g_array_new(FALSE, FALSE, sizeof(TextSelection));
1197 
1198     self->caret_offset = 0;
1199 }
my_atk_text_instance_finalize(GObject * obj)1200 static void my_atk_text_instance_finalize(GObject* obj)
1201 {
1202     MyAtkText *self = (MyAtkText*)obj;
1203     g_free(self->str);
1204     my_atk_text_free_run_attributes(self);
1205     my_atk_text_free_default_attributes(self);
1206     if(self->selections != NULL)g_array_free(self->selections, FALSE);
1207 }
1208 
my_atk_text_class_init(gpointer g_class,gpointer class_data)1209 static void my_atk_text_class_init(gpointer g_class, gpointer class_data)
1210 {
1211     GObjectClass* g_object_class = (GObjectClass*)g_class;
1212     //GObject virtual table
1213     g_object_class->finalize = my_atk_text_instance_finalize;
1214     //Fills tables of symbols
1215     table_symbols_init();
1216     //initialize clipboard
1217     ((MyAtkTextClass*)g_class)->clipboard = NULL;
1218 }
1219 //Because of static registration of type, finalization function will never been called
1220 //And glib prints warning if use it in registration.
1221 /*static void my_atk_text_class_finalize(gpointer g_class, gpointer class_data)
1222 {
1223     MyAtkTextClass *self = (MyAtkTextClass*)g_class;
1224 
1225     g_free(self->clipboard);
1226     self->clipboard = NULL;
1227 }*/
my_atk_text_interface_init(gpointer g_iface,gpointer iface_data)1228 void my_atk_text_interface_init(gpointer g_iface, gpointer iface_data)
1229 {
1230     AtkTextIface *klass = (AtkTextIface*)g_iface;
1231     //"get_text"
1232     klass->get_character_count = my_atk_text_get_character_count;
1233     klass->get_text = my_atk_text_get_text;
1234     klass->get_character_at_offset = my_atk_text_get_character_at_offset;
1235     klass->get_text_after_offset = my_atk_text_get_text_after_offset;
1236     klass->get_text_at_offset = my_atk_text_get_text_at_offset;
1237     klass->get_text_before_offset = my_atk_text_get_text_before_offset;
1238     //"attributes"
1239     klass->get_run_attributes = my_atk_text_get_run_attributes;
1240     klass->get_default_attributes = my_atk_text_get_default_attributes;
1241     //"bounds"
1242     klass->get_character_extents = my_atk_text_get_character_extents;
1243     klass->get_range_extents = my_atk_text_get_range_extents;
1244     klass->get_offset_at_point = my_atk_text_get_offset_at_point;
1245     klass->get_bounded_ranges = my_atk_text_get_bounded_ranges;
1246     //"selection"
1247     klass->get_n_selections = my_atk_text_get_n_selections;
1248     klass->get_selection = my_atk_text_get_selection;
1249     klass->remove_selection = my_atk_text_remove_selection;
1250     klass->add_selection = my_atk_text_add_selection;
1251     klass->set_selection = my_atk_text_set_selection;
1252     //"caret"
1253     klass->get_caret_offset = my_atk_text_get_caret_offset;
1254     klass->set_caret_offset = my_atk_text_set_caret_offset;
1255 }
1256 
my_atk_editable_text_interface_init(gpointer g_iface,gpointer iface_data)1257 static void my_atk_editable_text_interface_init(gpointer g_iface, gpointer iface_data)
1258 {
1259     AtkEditableTextIface *klass = (AtkEditableTextIface*)g_iface;
1260 
1261     klass->set_text_contents = my_atk_text_set_text_contents;
1262     klass->set_run_attributes = my_atk_text_set_run_attributes;
1263     klass->copy_text =  my_atk_text_copy_text;
1264     klass->insert_text =  my_atk_text_insert_text;
1265     klass->paste_text = my_atk_text_paste_text;
1266     klass->cut_text = my_atk_text_cut_text;
1267     klass->delete_text = my_atk_text_delete_text;
1268 }
my_atk_text_get_type()1269 GType my_atk_text_get_type()
1270 {
1271     static GType type = 0;
1272     if(type == 0)
1273     {
1274         static const GTypeInfo typeInfo =
1275         {
1276             sizeof(MyAtkTextClass),
1277             NULL,                       //base_init
1278             NULL,                       //base_finalize
1279             my_atk_text_class_init,     //class_init
1280             NULL,                       //class_finalize
1281             NULL,                       //class_data
1282             sizeof(MyAtkText),
1283             0,                          //n_preallocs
1284             my_atk_text_instance_init   //instance_init
1285         };
1286 
1287         static const GInterfaceInfo AtkTextIface_info =
1288         {
1289             my_atk_text_interface_init,         /* interface_init*/
1290             NULL,                               /* interface_finalize*/
1291             NULL                                /* interface_data */
1292         };
1293         static const GInterfaceInfo AtkEditableTextIface_info =
1294         {
1295             my_atk_editable_text_interface_init,/* interface_init*/
1296             NULL,                               /* interface_finalize*/
1297             NULL                                /* interface_data */
1298         };
1299         type = g_type_register_static(MY_TYPE_ATK_OBJECT, "MyAtkText", &typeInfo, 0);
1300         g_type_add_interface_static(type,
1301             ATK_TYPE_TEXT,
1302             &AtkTextIface_info);
1303 
1304         g_type_add_interface_static(type,
1305             ATK_TYPE_EDITABLE_TEXT,
1306             &AtkEditableTextIface_info);
1307     }
1308     return type;
1309 }
1310