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