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