5 #include <useful_functions.h>
7 #include "my-atk-text.h"
8 //*************************implementation***********************
12 * Auxiliary functions create/copy/print/free structures
13 * Use the same naming principe, as atk, but without 'atk' prefix
16 AtkAttribute* attribute_new(const gchar* name, const gchar* value)
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);
24 AtkAttribute* attribute_copy(AtkAttribute* attr)
26 return attribute_new(attr->name, attr->value);
28 void attribute_print(AtkAttribute* attr)
30 TRACE("name=%s, value=%s", attr->name, attr->value);
34 AtkAttributeSet* attribute_set_copy(AtkAttributeSet* attr)
37 AtkAttributeSet* result = g_slist_copy(attr);
38 for(tmp = result; tmp != NULL; tmp = tmp->next)
39 tmp->data = attribute_copy((AtkAttribute*)tmp->data);
42 void attribute_set_print(AtkAttributeSet *set)
47 g_slist_foreach(set, (GFunc)attribute_print, NULL);
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,
62 table_sentence_symbols,
63 table_sentence_symbols,
68 static gboolean current_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
69 const gboolean table_token_symbols[TABLE_SIZE])
71 const gchar *current = str + offset;
72 if(!table_token_symbols[(guchar)*current])
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;
83 static gboolean next_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
84 const gboolean table_token_symbols[TABLE_SIZE])
86 const gchar *current = str + offset;
87 for( ; (*current != 0) && table_token_symbols[(guchar)*current]; ++current);
90 for(++current ; (*current != 0) && !table_token_symbols[(guchar)*current]; ++current);
91 if(!table_token_symbols[(guchar)*current])
93 return current_token(str, current - str, token_start, token_end, table_token_symbols);
95 static gboolean previous_token(const gchar* str, gint offset, gint *token_start, gint *token_end,
96 const gboolean table_token_symbols[TABLE_SIZE])
98 const gchar *current = str + offset;
99 for( ; (current > str) && table_token_symbols[(guchar)*current]; --current);
102 for( ; (current > str) && !table_token_symbols[(guchar)*current]; --current);
103 if(!table_token_symbols[(guchar)*current])
105 return current_token(str, current - str, token_start, token_end, table_token_symbols);
109 //Range: type of data, containing in list of attributes
112 gint start,end;//range, containing this attributes
113 AtkAttributeSet* attributeSet;
115 //auxiliary functions for ranges
116 Range* range_new(gint start, gint end)
118 Range *range = g_malloc(sizeof(Range));
119 range->start = start;
121 range->attributeSet = NULL;
125 void range_free(Range* range)
127 atk_attribute_set_free(range->attributeSet);
130 void range_print(const Range*range)
132 TRACE("[%d,%d):", range->start, range->end);
133 attribute_set_print(range->attributeSet);
135 //only for correct list of ranges - ranges shouldn't intersect
136 gint range_compare(const Range* range1, const Range* range2)
138 return range1->start - range2->start;//never equal
141 void text_bounds_init(TextBounds *bounds)
145 bounds->pixels_above_line = 2;
146 bounds->pixels_below_line = 3;
148 bounds->pixels_between_characters = 1;
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)
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);
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)
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++]])
175 if(state_FSM)continue;
179 if(relative_offset != NULL) *relative_offset = offset - last_line_start;
182 // Compute extent of character,
183 // as it was at line 'line' and at offset 'relative_offset' in that line
185 void get_extents(MyAtkText *text, gint line, gint relative_offset, AtkTextRectangle *rect)
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;
194 //return line, corresponding to given y-coordinate
195 gint get_point_line(MyAtkText *text, gint y)
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);
202 // Returns offset from left boundary of text, correspondind to x-coordinate
203 gint get_point_relative_offset(MyAtkText *text, gint x)
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);
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)
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++]])
223 if(state_FSM || ++current_line != line)continue;
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)
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]])
242 return offset_at_line_start + relative_offset;
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.
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)
253 guchar *str = (guchar*)text->str;
254 gint len = my_strlen(text->str);
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 ++)
261 if(!table_line_symbols[str[current_line_start]])
263 current_line_start++;
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);
270 if(end_offset - current_line_start > rel_start_offset)
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;
279 ranges[number_of_ranges] = text_range_new((AtkText*)text,
280 range_start_offset, range_end_offset, coord_type);
284 current_line_start = end_offset + 1;
286 if(ranges != NULL) ranges[number_of_ranges] = NULL;
287 return number_of_ranges;
290 //"free"-functions(for internal using, because them don't emit signals)
291 void my_atk_text_free_run_attributes(MyAtkText *text)
293 g_list_foreach(text->attributes, (GFunc)range_free, NULL);
294 g_list_free(text->attributes);
295 text->attributes = NULL;
297 void my_atk_text_free_default_attributes(MyAtkText *text)
299 atk_attribute_set_free(text->default_attributes);
300 text->default_attributes = NULL;
302 void my_atk_text_clear_selections(MyAtkText *text)
304 if(text->selections->len != 0)
305 g_array_remove_range(text->selections, 0, text->selections->len);
307 void table_symbols_init()
311 for(i = TABLE_SIZE - 1;i > 0 ; --i)
312 table_word_symbols[i] = g_ascii_isalnum(i);
313 table_word_symbols['\0'] = FALSE;
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;
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;
329 void correct_selections_after_insert(MyAtkText *text, gint position, gint length)
332 GArray* selections = text->selections;
333 for(i = selections->len - 1; i >=0; i--)
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;
341 void correct_selections_after_delete(MyAtkText *text, gint position, gint length)
344 GArray* selections = text->selections;
345 for(i = selections->len - 1; i >=0; i--)
347 TextSelection* sel = &g_array_index(selections, TextSelection, i);
348 if(sel->start_offset >= position)
350 if(sel->start_offset >= position + length)
352 sel->start_offset-= length;
353 sel->end_offset-= length;
355 //position <= sel->start_offset < position + length
356 else if(sel->end_offset > position + length)
358 sel->start_offset = position;
359 sel->end_offset -= length;
363 g_array_remove_index(selections, i);
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;
373 void correct_attributes_after_insert(MyAtkText* text, gint position, gint length)
375 GList *attributes = text->attributes;
377 //before inserted position
378 for(tmp = attributes; tmp != NULL; tmp = tmp -> next)
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
387 //after inserted position
388 for(; tmp != NULL; tmp = tmp -> next)
390 Range* range = (Range*)tmp->data;
391 range->end += length;
392 range->start += length;
395 void correct_attributes_after_delete(MyAtkText* text, gint position, gint length)
397 GList *attributes = text->attributes;
398 GList *tmp = attributes;
399 //before deleting range
400 for(tmp = attributes; tmp != NULL; tmp = tmp->next)
402 Range* range = (Range*)tmp->data;
403 if(range->end <= position) continue;
404 if(range->start < position)
406 if(range->end > position + length) range->end -= length;
407 else range->end = position;
415 Range* range = (Range*)tmp->data;
416 if(range->start >= position + length) break;
417 if(range->end <= position + length)
419 GList *tmp1 = tmp->next;
421 attributes = g_list_remove_link(attributes, tmp);
425 //range->end > position + length
426 //range->start < position + length
427 range->start = position + length;//will be restored in next loop
430 //after deleting range
431 for(;tmp != NULL; tmp = tmp->next)
433 Range* range = (Range*)tmp->data;
434 range->end -= length;
435 range->start -= length;
437 text->attributes = attributes;
439 void correct_caret_after_insert(MyAtkText* text, gint position, gint length)
441 if(text->caret_offset > position)text->caret_offset += length;
443 void correct_caret_after_delete(MyAtkText* text, gint position, gint length)
445 if(text->caret_offset >= position + length)text->caret_offset -= length;
446 else if(text->caret_offset > position) text->caret_offset = position;
449 // Implementation of virtual functions
450 //******************************my_atk_text_get_character_count*************************
451 static gint my_atk_text_get_character_count(AtkText *text)
453 MyAtkText *self = (MyAtkText*)text;
454 return my_strlen(self->str);
456 //**************************************my_atk_text_get_text*****************************
457 static gchar* my_atk_text_get_text(AtkText *text, gint start_offset, gint end_offset)
459 gchar *str = ((MyAtkText*)text)->str;
460 if((start_offset < 0) || (end_offset > my_strlen(str)) || (end_offset <= start_offset))
465 return g_strndup(str + start_offset, end_offset - start_offset);
468 //*******************************my_atk_text_get_character_at_offset************************
469 static gunichar my_atk_text_get_character_at_offset(AtkText *text, gint offset)
471 gchar *str = ((MyAtkText*)text)->str;
472 if(offset < 0 || offset >= my_strlen(str))
476 return (gunichar)str[offset];
478 // In the next 3 functions some code is commented for verify tests themselves on 'mutants'
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)
484 gchar *str = ((MyAtkText*)text)->str;
485 gint len = my_strlen(str);
486 if((offset < 0) || (offset >= len))
488 return NULL;//incorrect offset
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;
498 switch(boundary_type)
500 case ATK_TEXT_BOUNDARY_CHAR:
501 if(offset + 1 == len)
503 is_successed = FALSE;
506 *start_offset = offset + 1;
507 *end_offset = offset + 2;
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]))
516 // if(current_token(str, offset, start_offset, end_offset, tables[boundary_type]))
518 // is_successed = TRUE;
521 is_successed = FALSE;
524 if(!next_token(str, end_tmp, end_offset, &end_tmp, tables[boundary_type]))
530 // (*start_offset)++;
531 // if(*start_offset > 10) ++(*start_offset);
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]))
538 if(!next_token(str, offset, &start_tmp, start_offset, tables[boundary_type]))
540 is_successed = FALSE;
545 // else if(*start_offset > strlen(str) - 7)
547 // *end_offset = *start_offset + 3;
548 // is_successed = TRUE;
551 if(!next_token(str, *start_offset, &start_tmp, end_offset, tables[boundary_type]))
553 is_successed = FALSE;
557 // --(*start_offset);
562 is_successed = FALSE;
567 return my_atk_text_get_text(text, *start_offset, *end_offset);
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)
578 gchar *str = ((MyAtkText*)text)->str;
579 gint len = my_strlen(str);
580 if((offset < 0) || (offset >= len))
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;
592 switch(boundary_type)
594 case ATK_TEXT_BOUNDARY_CHAR:
595 *start_offset = offset;
596 *end_offset = offset + 1;
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]))
604 if(!previous_token(str, offset, start_offset, &end_tmp, tables[boundary_type]))
606 is_successed = FALSE;
610 if(!next_token(str, offset, end_offset, &end_tmp, tables[boundary_type]))
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]))
621 if(!next_token(str, offset, &start_tmp, end_offset, tables[boundary_type]))
623 is_successed = FALSE;
627 if(!previous_token(str, start_tmp, &start_tmp, start_offset, tables[boundary_type]))
633 // ++(*start_offset);
636 is_successed = FALSE;
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);
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)
655 gchar *str = ((MyAtkText*)text)->str;
656 gint len = my_strlen(str);
657 if((offset < 0) || (offset >= len))
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;
669 switch(boundary_type)
671 case ATK_TEXT_BOUNDARY_CHAR:
674 is_successed = FALSE;
677 *start_offset = offset - 1;
678 *end_offset = offset;
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]))
686 if(!previous_token(str, offset, end_offset, &end_tmp, tables[boundary_type]))
688 is_successed = FALSE;
692 if(!previous_token(str, *end_offset, start_offset, &end_tmp, tables[boundary_type]))
694 is_successed = FALSE;
699 // ++(*start_offset);
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]))
706 is_successed = FALSE;
709 if(!previous_token(str, start_tmp, &start_tmp, start_offset, tables[boundary_type]))
716 is_successed = FALSE;
721 return my_atk_text_get_text(text, *start_offset, *end_offset);
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)
733 GList *attributes = ((MyAtkText*)text)->attributes;
734 if(offset < 0 || offset >= my_atk_text_get_character_count(text))
736 TRACE0("Incorrect value of offset.");
739 gint start = -1, end = -1;
740 for(tmp = attributes; tmp != NULL; tmp = tmp->next)
742 Range* range = (Range*)(tmp->data);
743 if(range->end <= offset)
748 if(range->start > offset)
754 *start_offset = range->start;
755 *end_offset = range->end;
756 return attribute_set_copy(range->attributeSet);
758 *start_offset = (start == -1) ? 0 : start;
759 *end_offset = (end == -1) ? my_atk_text_get_character_count(text) : end;
762 //*********************************my_atk_text_get_default_attributes*****************
763 AtkAttributeSet* my_atk_text_get_default_attributes(AtkText* text)
765 return attribute_set_copy(((MyAtkText*)text)->default_attributes);
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)
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);
777 *width = result.width;
778 *height = result.height;
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)
784 //simple - union of extents of the characters, contained in this range
785 AtkTextRectangle result, bounds_tmp;
788 atk_text_get_character_extents (text, start_offset,
789 &result.x, &result.y,
790 &result.width, &result.height,
793 for (i = start_offset + 1; i < end_offset; i++)
795 my_atk_text_get_character_extents (text, i,&bounds_tmp.x, &bounds_tmp.y,
796 &bounds_tmp.width, &bounds_tmp.height, coord_type);
798 if(bounds_tmp.x < result.x)
800 //corrects left boundary
801 result.width += result.x - bounds_tmp.x;
802 result.x = bounds_tmp.x;
804 if(bounds_tmp.x + bounds_tmp.width > result.x + result.width)
806 //corrects right boundary
807 result.width = bounds_tmp.x + bounds_tmp.width - result.x;
809 if(bounds_tmp.y < result.y)
811 //corrects top boundary
812 result.height += result.y - bounds_tmp.y;
813 result.y = bounds_tmp.y;
815 if(bounds_tmp.y + bounds_tmp.height > result.y + result.height)
817 //corrects buttom boundary
818 result.height = bounds_tmp.y + bounds_tmp.height - result.y;
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)
826 gint line, relative_offset;
828 line = get_point_line((MyAtkText*)text, y);
829 relative_offset = get_point_relative_offset((MyAtkText*)text, x);
831 return get_offset_at_line((MyAtkText*)text, line, relative_offset);
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)
837 MyAtkText *self = (MyAtkText*)text;
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;}
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;
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;
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));
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
876 //********************************my_atk_text_get_n_selections*******************************
877 gint my_atk_text_get_n_selections(AtkText *text)
879 MyAtkText *self = (MyAtkText*)text;
880 return self->selections->len;
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)
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);
894 //********************************my_atk_text_remove_selection*******************************
895 gboolean my_atk_text_remove_selection(AtkText *text, gint selection_num)
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);
902 g_signal_emit_by_name(text, "text-selection-changed");
905 //********************************my_atk_text_add_selection*******************************
906 gboolean my_atk_text_add_selection(AtkText *text, gint start_offset, gint end_offset)
908 if(start_offset < 0 || end_offset > my_atk_text_get_character_count(text)
909 || start_offset >= end_offset) return FALSE;
911 MyAtkText *self = (MyAtkText*)text;
912 GArray *selections = self->selections;
914 for(i = 0; i < selections->len; i++)
916 if(g_array_index(selections, TextSelection, i).start_offset >= start_offset)
918 if(g_array_index(selections, TextSelection, i).start_offset < end_offset)
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);
928 g_signal_emit_by_name(text, "text-selection-changed");
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)
935 MyAtkText *self = (MyAtkText*)text;
936 GArray *selections = self->selections;
937 if(selection_num < 0 || selection_num >= selections->len) return NULL;
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)
945 //Arrange of selections won't change
946 g_array_index(selections, TextSelection, selection_num).start_offset =
948 g_array_index(selections, TextSelection, selection_num).end_offset =
950 g_signal_emit_by_name(text, "text-selection-changed");
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;
958 my_atk_text_remove_selection(text, selection_num);
959 if(!my_atk_text_add_selection(text, start_offset, end_offset))
961 //fail when adding selection. Restore initial state.
962 my_atk_text_add_selection(text, start_offset_old, end_offset_old);
965 g_signal_emit_by_name(text, "text-selection-changed");
970 //************************************my_atk_text_get_caret_offset******************
971 gint my_atk_text_get_caret_offset(AtkText *text)
973 MyAtkText *self = (MyAtkText*)text;
974 return self->caret_offset;
976 //************************************my_atk_text_set_caret_offset******************
977 gboolean my_atk_text_set_caret_offset(AtkText *text, gint offset)
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);
987 //***********************my_atk_text_insert_text*******************************
988 void my_atk_text_insert_text(AtkEditableText* text, const gchar* string,
989 gint length, gint *position)
992 MyAtkText* myAtkText = (MyAtkText*)text;
993 gchar *str = myAtkText->str;
994 gint strlen_old = my_strlen(str);
996 if(string == NULL) return;
998 for(i = 0; i < length; i ++)
1000 if(string[i] == '\0') {length = i; break;}
1003 if(*position < 0 || *position > strlen_old || length <= 0 )return;
1006 gchar *new_str = g_malloc(strlen_old + length + 1);
1007 if(new_str == NULL)return;
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';
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);
1027 (*position) += length;
1029 //*************************my_atk_text_delete_text*******************
1030 void my_atk_text_delete_text(AtkEditableText* text, gint start_pos, gint end_pos)
1032 MyAtkText* myAtkText = (MyAtkText*)text;
1033 gchar *str = myAtkText->str;
1034 gint strlen_old = my_strlen(str);
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';
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);
1049 //***********************my_atk_text_set_text_contents*************************
1050 void my_atk_text_set_text_contents(AtkEditableText* text, const gchar* string)
1052 my_atk_text_delete_text(text, 0, my_atk_text_get_character_count((AtkText*)text));
1054 my_atk_text_insert_text(text, string, my_strlen(string), &position);
1056 //**********************my_atk_text_copy_text***************************
1057 void my_atk_text_copy_text(AtkEditableText* text, gint start_pos, gint end_pos)
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;
1064 MyAtkTextClass *parent = MY_ATK_TEXT_GET_CLASS(text);
1065 g_free(parent->clipboard);
1066 /*parent->clipboard = g_malloc(end_pos - start_pos + 1);
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);
1072 //**********************my_atk_text_paste_text***************************
1073 void my_atk_text_paste_text(AtkEditableText *text, gint position)
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);
1079 //**********************my_atk_text_cut_text***************************
1080 void my_atk_text_cut_text(AtkEditableText* text, gint start_pos, gint end_pos)
1082 my_atk_text_copy_text(text, start_pos, end_pos);
1083 my_atk_text_delete_text(text, start_pos, end_pos);
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)
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)
1093 GList *attributes = self->attributes;
1094 GList *tmp = attributes;
1098 Range *range = (Range*)tmp->data;
1099 if(range->start < start_offset)
1101 if(range->end <= end_offset)
1103 if(range->end > start_offset) range->end = start_offset;
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);
1115 else/*range->start >= start_offset*/
1117 if(range->end <= end_offset)
1119 GList *tmp1 = tmp->next;
1120 attributes = g_list_remove_link(attributes, tmp);
1124 /*range->end > end_offset*/
1125 if(range->start < end_offset) range->start = end_offset;
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);
1134 self->attributes = attributes;
1135 g_signal_emit_by_name(self, "text_attributes_changed");
1140 //sets default attributes
1141 void my_atk_text_set_default_attributes(MyAtkText* text, AtkAttributeSet *set)
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");
1148 void my_atk_text_print_run_attributes(MyAtkText *text)
1150 g_list_foreach(text->attributes, (GFunc)range_print, NULL);
1152 void my_atk_text_print_default_attributes(MyAtkText *text)
1154 attribute_set_print(text->default_attributes);
1156 //need for separate testing interfaces
1157 void auxiliary_atk_text_set_text_contents(MyAtkText* text, const gchar* string)
1159 my_atk_text_set_text_contents((AtkEditableText*)text, string);
1161 void auxiliary_atk_text_set_run_attributes(MyAtkText* text, AtkAttributeSet* attrib_set,
1162 gint start_offset, gint end_offset)
1164 my_atk_text_set_run_attributes((AtkEditableText*)text, attrib_set, start_offset, end_offset);
1167 //initialize/finalize functions
1168 static void my_atk_text_instance_init(GTypeInstance *obj, gpointer g_class)
1170 MyAtkText *self = (MyAtkText*)obj;
1173 self->attributes = NULL;
1174 self->default_attributes = NULL;
1175 text_bounds_init(&self->bounds);
1177 self->selections = g_array_new(FALSE, FALSE, sizeof(TextSelection));
1179 self->caret_offset = 0;
1181 static void my_atk_text_instance_finalize(GObject* obj)
1183 MyAtkText *self = (MyAtkText*)obj;
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);
1190 static void my_atk_text_class_init(gpointer g_class, gpointer class_data)
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;
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)
1204 MyAtkTextClass *self = (MyAtkTextClass*)g_class;
1206 g_free(self->clipboard);
1207 self->clipboard = NULL;
1209 void my_atk_text_interface_init(gpointer g_iface, gpointer iface_data)
1211 AtkTextIface *klass = (AtkTextIface*)g_iface;
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;
1220 klass->get_run_attributes = my_atk_text_get_run_attributes;
1221 klass->get_default_attributes = my_atk_text_get_default_attributes;
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;
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;
1234 klass->get_caret_offset = my_atk_text_get_caret_offset;
1235 klass->set_caret_offset = my_atk_text_set_caret_offset;
1238 static void my_atk_editable_text_interface_init(gpointer g_iface, gpointer iface_data)
1240 AtkEditableTextIface *klass = (AtkEditableTextIface*)g_iface;
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;
1250 GType my_atk_text_get_type()
1252 static GType type = 0;
1255 static const GTypeInfo typeInfo =
1257 sizeof(MyAtkTextClass),
1259 NULL, //base_finalize
1260 my_atk_text_class_init, //class_init
1261 NULL, //class_finalize
1265 my_atk_text_instance_init //instance_init
1268 static const GInterfaceInfo AtkTextIface_info =
1270 my_atk_text_interface_init, /* interface_init*/
1271 NULL, /* interface_finalize*/
1272 NULL /* interface_data */
1274 static const GInterfaceInfo AtkEditableTextIface_info =
1276 my_atk_editable_text_interface_init,/* interface_init*/
1277 NULL, /* interface_finalize*/
1278 NULL /* interface_data */
1280 type = g_type_register_static(MY_TYPE_ATK_OBJECT, "MyAtkText", &typeInfo, 0);
1281 g_type_add_interface_static(type,
1283 &AtkTextIface_info);
1285 g_type_add_interface_static(type,
1286 ATK_TYPE_EDITABLE_TEXT,
1287 &AtkEditableTextIface_info);