gtktextiter.c 143 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/* GTK - The GIMP Toolkit
 * gtktextiter.c Copyright (C) 2000 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 13 14 15 16 17 18 19 20 21 22 23
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
24
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25 26
 */

27
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
28 29 30 31
#include "gtktextiter.h"
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"
#include "gtkdebug.h"
32
#include <string.h>
33

34 35
#define FIX_OVERFLOWS(varname) if ((varname) == G_MININT) (varname) = G_MININT + 1

36 37
typedef struct _GtkTextRealIter GtkTextRealIter;

38 39
struct _GtkTextRealIter
{
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
  /* Always-valid information */
  GtkTextBTree *tree;
  GtkTextLine *line;
  /* At least one of these is always valid;
     if invalid, they are -1.

     If the line byte offset is valid, so is the segment byte offset;
     and ditto for char offsets. */
  gint line_byte_offset;
  gint line_char_offset;
  /* These two are valid if >= 0 */
  gint cached_char_index;
  gint cached_line_number;
  /* Stamps to detect the buffer changing under us */
  gint chars_changed_stamp;
  gint segments_changed_stamp;
  /* Valid if the segments_changed_stamp is up-to-date */
  GtkTextLineSegment *segment;     /* indexable segment we index */
  GtkTextLineSegment *any_segment; /* first segment in our location,
59
                                      maybe same as "segment" */
60 61 62 63 64 65 66
  /* One of these will always be valid if segments_changed_stamp is
     up-to-date. If invalid, they are -1.

     If the line byte offset is valid, so is the segment byte offset;
     and ditto for char offsets. */
  gint segment_byte_offset;
  gint segment_char_offset;
67 68 69 70

  /* padding */
  gint pad1;
  gpointer pad2;
71 72 73 74 75 76
};

/* These "set" functions should not assume any fields
   other than the char stamp and the tree are valid.
*/
static void
77 78
iter_set_common (GtkTextRealIter *iter,
                 GtkTextLine *line)
79 80 81
{
  /* Update segments stamp */
  iter->segments_changed_stamp =
82
    _gtk_text_btree_get_segments_changed_stamp (iter->tree);
83

84 85 86 87 88 89 90 91 92 93 94
  iter->line = line;

  iter->line_byte_offset = -1;
  iter->line_char_offset = -1;
  iter->segment_byte_offset = -1;
  iter->segment_char_offset = -1;
  iter->cached_char_index = -1;
  iter->cached_line_number = -1;
}

static void
95 96 97
iter_set_from_byte_offset (GtkTextRealIter *iter,
                           GtkTextLine *line,
                           gint byte_offset)
98
{
99
  iter_set_common (iter, line);
100

101 102 103 104 105 106 107 108
  if (!_gtk_text_line_byte_locate (iter->line,
                                   byte_offset,
                                   &iter->segment,
                                   &iter->any_segment,
                                   &iter->segment_byte_offset,
                                   &iter->line_byte_offset))
    g_error ("Byte index %d is off the end of the line",
             byte_offset);
109 110 111
}

static void
112 113 114
iter_set_from_char_offset (GtkTextRealIter *iter,
                           GtkTextLine *line,
                           gint char_offset)
115
{
116
  iter_set_common (iter, line);
117

118 119 120 121 122 123 124 125
  if (!_gtk_text_line_char_locate (iter->line,
                                   char_offset,
                                   &iter->segment,
                                   &iter->any_segment,
                                   &iter->segment_char_offset,
                                   &iter->line_char_offset))
    g_error ("Char offset %d is off the end of the line",
             char_offset);
126 127 128
}

static void
129 130 131
iter_set_from_segment (GtkTextRealIter *iter,
                       GtkTextLine *line,
                       GtkTextLineSegment *segment)
132 133 134 135 136 137 138 139 140 141 142 143 144 145
{
  GtkTextLineSegment *seg;
  gint byte_offset;

  /* This could theoretically be optimized by computing all the iter
     fields in this same loop, but I'm skipping it for now. */
  byte_offset = 0;
  seg = line->segments;
  while (seg != segment)
    {
      byte_offset += seg->byte_count;
      seg = seg->next;
    }

146
  iter_set_from_byte_offset (iter, line, byte_offset);
147 148 149 150
}

/* This function ensures that the segment-dependent information is
   truly computed lazily; often we don't need to do the full make_real
151 152
   work. This ensures the btree and line are valid, but doesn't
   update the segments. */
153
static GtkTextRealIter*
154
gtk_text_iter_make_surreal (const GtkTextIter *_iter)
155 156
{
  GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
157

158
  if (iter->chars_changed_stamp !=
159
      _gtk_text_btree_get_chars_changed_stamp (iter->tree))
160 161 162 163 164 165 166 167 168 169 170
    {
      g_warning ("Invalid text buffer iterator: either the iterator "
                 "is uninitialized, or the characters/pixbufs/widgets "
                 "in the buffer have been modified since the iterator "
                 "was created.\nYou must use marks, character numbers, "
                 "or line numbers to preserve a position across buffer "
                 "modifications.\nYou can apply tags and insert marks "
                 "without invalidating your iterators,\n"
                 "but any mutation that affects 'indexable' buffer contents "
                 "(contents that can be referred to by character offset)\n"
                 "will invalidate all outstanding iterators");
171 172 173 174 175 176 177 178 179
      return NULL;
    }

  /* We don't update the segments information since we are becoming
     only surreal. However we do invalidate the segments information
     if appropriate, to be sure we segfault if we try to use it and we
     should have used make_real. */

  if (iter->segments_changed_stamp !=
180
      _gtk_text_btree_get_segments_changed_stamp (iter->tree))
181 182 183 184 185 186 187
    {
      iter->segment = NULL;
      iter->any_segment = NULL;
      /* set to segfault-causing values. */
      iter->segment_byte_offset = -10000;
      iter->segment_char_offset = -10000;
    }
188

189 190 191 192
  return iter;
}

static GtkTextRealIter*
193
gtk_text_iter_make_real (const GtkTextIter *_iter)
194 195
{
  GtkTextRealIter *iter;
196 197 198

  iter = gtk_text_iter_make_surreal (_iter);

199
  if (iter->segments_changed_stamp !=
200
      _gtk_text_btree_get_segments_changed_stamp (iter->tree))
201 202 203
    {
      if (iter->line_byte_offset >= 0)
        {
204 205 206
          iter_set_from_byte_offset (iter,
                                     iter->line,
                                     iter->line_byte_offset);
207 208 209
        }
      else
        {
210 211 212 213 214
          g_assert (iter->line_char_offset >= 0);

          iter_set_from_char_offset (iter,
                                     iter->line,
                                     iter->line_char_offset);
215 216 217
        }
    }

218 219 220 221
  g_assert (iter->segment != NULL);
  g_assert (iter->any_segment != NULL);
  g_assert (iter->segment->char_count > 0);

222 223 224 225
  return iter;
}

static GtkTextRealIter*
226 227
iter_init_common (GtkTextIter *_iter,
                  GtkTextBTree *tree)
228 229 230
{
  GtkTextRealIter *iter = (GtkTextRealIter*)_iter;

231 232
  g_return_val_if_fail (iter != NULL, NULL);
  g_return_val_if_fail (tree != NULL, NULL);
233 234 235 236

  iter->tree = tree;

  iter->chars_changed_stamp =
237
    _gtk_text_btree_get_chars_changed_stamp (iter->tree);
238

239 240 241 242
  return iter;
}

static GtkTextRealIter*
243 244 245 246
iter_init_from_segment (GtkTextIter *iter,
                        GtkTextBTree *tree,
                        GtkTextLine *line,
                        GtkTextLineSegment *segment)
247 248 249
{
  GtkTextRealIter *real;

250 251 252 253 254 255
  g_return_val_if_fail (line != NULL, NULL);

  real = iter_init_common (iter, tree);

  iter_set_from_segment (real, line, segment);

256 257 258 259
  return real;
}

static GtkTextRealIter*
260 261 262 263
iter_init_from_byte_offset (GtkTextIter *iter,
                            GtkTextBTree *tree,
                            GtkTextLine *line,
                            gint line_byte_offset)
264 265 266
{
  GtkTextRealIter *real;

267 268 269 270 271 272
  g_return_val_if_fail (line != NULL, NULL);

  real = iter_init_common (iter, tree);

  iter_set_from_byte_offset (real, line, line_byte_offset);

273 274 275 276 277 278 279
  if (real->segment->type == &gtk_text_char_type &&
      (real->segment->body.chars[real->segment_byte_offset] & 0xc0) == 0x80)
    g_warning ("Incorrect line byte index %d falls in the middle of a UTF-8 "
               "character; this will crash the text buffer. "
               "Byte indexes must refer to the start of a character.",
               line_byte_offset);
  
280 281 282 283
  return real;
}

static GtkTextRealIter*
284 285 286 287
iter_init_from_char_offset (GtkTextIter *iter,
                            GtkTextBTree *tree,
                            GtkTextLine *line,
                            gint line_char_offset)
288 289 290
{
  GtkTextRealIter *real;

291 292 293 294 295 296
  g_return_val_if_fail (line != NULL, NULL);

  real = iter_init_common (iter, tree);

  iter_set_from_char_offset (real, line, line_char_offset);

297 298 299 300
  return real;
}

static inline void
301
invalidate_segment (GtkTextRealIter *iter)
302 303 304 305 306
{
  iter->segments_changed_stamp -= 1;
}

static inline void
307
invalidate_char_index (GtkTextRealIter *iter)
308 309 310 311 312
{
  iter->cached_char_index = -1;
}

static inline void
313
invalidate_line_number (GtkTextRealIter *iter)
314 315 316 317 318
{
  iter->cached_line_number = -1;
}

static inline void
319
adjust_char_index (GtkTextRealIter *iter, gint count)
320 321 322 323 324 325
{
  if (iter->cached_char_index >= 0)
    iter->cached_char_index += count;
}

static inline void
326
adjust_line_number (GtkTextRealIter *iter, gint count)
327 328 329 330 331 332
{
  if (iter->cached_line_number >= 0)
    iter->cached_line_number += count;
}

static inline void
333
adjust_char_offsets (GtkTextRealIter *iter, gint count)
334 335 336 337
{
  if (iter->line_char_offset >= 0)
    {
      iter->line_char_offset += count;
338
      g_assert (iter->segment_char_offset >= 0);
339 340 341 342 343
      iter->segment_char_offset += count;
    }
}

static inline void
344
adjust_byte_offsets (GtkTextRealIter *iter, gint count)
345 346 347 348
{
  if (iter->line_byte_offset >= 0)
    {
      iter->line_byte_offset += count;
349
      g_assert (iter->segment_byte_offset >= 0);
350 351 352 353 354
      iter->segment_byte_offset += count;
    }
}

static inline void
355
ensure_char_offsets (GtkTextRealIter *iter)
356 357 358
{
  if (iter->line_char_offset < 0)
    {
359
      g_assert (iter->line_byte_offset >= 0);
360

361
      _gtk_text_line_byte_to_char_offsets (iter->line,
362 363 364
                                          iter->line_byte_offset,
                                          &iter->line_char_offset,
                                          &iter->segment_char_offset);
365 366 367 368
    }
}

static inline void
369
ensure_byte_offsets (GtkTextRealIter *iter)
370 371 372
{
  if (iter->line_byte_offset < 0)
    {
373
      g_assert (iter->line_char_offset >= 0);
374

375
      _gtk_text_line_char_to_byte_offsets (iter->line,
376 377 378
                                          iter->line_char_offset,
                                          &iter->line_byte_offset,
                                          &iter->segment_byte_offset);
379 380 381
    }
}

382 383 384 385 386 387
static inline gboolean
is_segment_start (GtkTextRealIter *real)
{
  return real->segment_byte_offset == 0 || real->segment_char_offset == 0;
}

388 389
#if 1
static void
390
check_invariants (const GtkTextIter *iter)
391 392
{
  if (gtk_debug_flags & GTK_DEBUG_TEXT)
393
    _gtk_text_iter_check (iter);
394 395
}
#else
396
#define check_invariants (x)
397 398
#endif

399 400 401
/**
 * gtk_text_iter_get_buffer:
 * @iter: an iterator
402
 *
403
 * Returns the #GtkTextBuffer this iterator is associated with.
404
 *
405 406
 * Return value: the buffer
 **/
407
GtkTextBuffer*
408
gtk_text_iter_get_buffer (const GtkTextIter *iter)
409 410 411
{
  GtkTextRealIter *real;

412 413 414
  g_return_val_if_fail (iter != NULL, NULL);

  real = gtk_text_iter_make_surreal (iter);
415 416 417 418

  if (real == NULL)
    return NULL;

419 420
  check_invariants (iter);

421
  return _gtk_text_btree_get_buffer (real->tree);
422 423
}

424 425 426
/**
 * gtk_text_iter_copy:
 * @iter: an iterator
427
 *
428
 * Creates a dynamically-allocated copy of an iterator. This function
429 430 431
 * is not useful in applications, because iterators can be copied with a
 * simple assignment (<literal>GtkTextIter i = j;</literal>). The
 * function is used by language bindings.
432 433
 *
 * Return value: a copy of the @iter, free with gtk_text_iter_free ()
434
 **/
435
GtkTextIter*
436
gtk_text_iter_copy (const GtkTextIter *iter)
437 438 439
{
  GtkTextIter *new_iter;

440 441 442
  g_return_val_if_fail (iter != NULL, NULL);

  new_iter = g_new (GtkTextIter, 1);
443 444

  *new_iter = *iter;
445

446 447 448
  return new_iter;
}

449 450 451 452 453 454 455 456 457
/**
 * gtk_text_iter_free:
 * @iter: a dynamically-allocated iterator
 *
 * Free an iterator allocated on the heap. This function
 * is intended for use in language bindings, and is not
 * especially useful for applications, because iterators can
 * simply be allocated on the stack.
 **/
458
void
459
gtk_text_iter_free (GtkTextIter *iter)
460
{
461
  g_return_if_fail (iter != NULL);
462

463
  g_free (iter);
464 465
}

466 467 468 469 470 471
GType
gtk_text_iter_get_type (void)
{
  static GType our_type = 0;
  
  if (our_type == 0)
472
    our_type = g_boxed_type_register_static ("GtkTextIter",
473 474 475 476 477 478
					     (GBoxedCopyFunc) gtk_text_iter_copy,
					     (GBoxedFreeFunc) gtk_text_iter_free);

  return our_type;
}

479
GtkTextLineSegment*
480
_gtk_text_iter_get_indexable_segment (const GtkTextIter *iter)
481 482 483
{
  GtkTextRealIter *real;

484
  g_return_val_if_fail (iter != NULL, NULL);
485 486

  real = gtk_text_iter_make_real (iter);
487 488 489 490

  if (real == NULL)
    return NULL;

491 492 493 494
  check_invariants (iter);

  g_assert (real->segment != NULL);

495 496 497 498
  return real->segment;
}

GtkTextLineSegment*
499
_gtk_text_iter_get_any_segment (const GtkTextIter *iter)
500 501 502
{
  GtkTextRealIter *real;

503
  g_return_val_if_fail (iter != NULL, NULL);
504 505

  real = gtk_text_iter_make_real (iter);
506 507 508 509

  if (real == NULL)
    return NULL;

510 511 512 513
  check_invariants (iter);

  g_assert (real->any_segment != NULL);

514 515 516 517
  return real->any_segment;
}

gint
518
_gtk_text_iter_get_segment_byte (const GtkTextIter *iter)
519 520 521
{
  GtkTextRealIter *real;

522
  g_return_val_if_fail (iter != NULL, 0);
523 524

  real = gtk_text_iter_make_real (iter);
525 526 527 528

  if (real == NULL)
    return 0;

529 530 531
  ensure_byte_offsets (real);

  check_invariants (iter);
532 533 534 535 536

  return real->segment_byte_offset;
}

gint
537
_gtk_text_iter_get_segment_char (const GtkTextIter *iter)
538 539 540
{
  GtkTextRealIter *real;

541
  g_return_val_if_fail (iter != NULL, 0);
542 543

  real = gtk_text_iter_make_real (iter);
544 545 546 547

  if (real == NULL)
    return 0;

548 549 550
  ensure_char_offsets (real);

  check_invariants (iter);
551 552 553 554 555 556 557

  return real->segment_char_offset;
}

/* This function does not require a still-valid
   iterator */
GtkTextLine*
558
_gtk_text_iter_get_text_line (const GtkTextIter *iter)
559 560 561
{
  const GtkTextRealIter *real;

562
  g_return_val_if_fail (iter != NULL, NULL);
563

564 565 566 567 568 569 570 571
  real = (const GtkTextRealIter*)iter;

  return real->line;
}

/* This function does not require a still-valid
   iterator */
GtkTextBTree*
572
_gtk_text_iter_get_btree (const GtkTextIter *iter)
573 574 575
{
  const GtkTextRealIter *real;

576
  g_return_val_if_fail (iter != NULL, NULL);
577

578 579 580 581 582 583 584 585 586
  real = (const GtkTextRealIter*)iter;

  return real->tree;
}

/*
 * Conversions
 */

587 588 589
/**
 * gtk_text_iter_get_offset:
 * @iter: an iterator
590
 *
591 592 593
 * Returns the character offset of an iterator.
 * Each character in a #GtkTextBuffer has an offset,
 * starting with 0 for the first character in the buffer.
594
 * Use gtk_text_buffer_get_iter_at_offset () to convert an
595
 * offset back into an iterator.
596
 *
597 598
 * Return value: a character offset
 **/
599
gint
600
gtk_text_iter_get_offset (const GtkTextIter *iter)
601 602 603
{
  GtkTextRealIter *real;

604 605 606
  g_return_val_if_fail (iter != NULL, 0);

  real = gtk_text_iter_make_surreal (iter);
607 608 609 610

  if (real == NULL)
    return 0;

611 612
  check_invariants (iter);
  
613 614
  if (real->cached_char_index < 0)
    {
615 616
      ensure_char_offsets (real);
      
617
      real->cached_char_index =
618
        _gtk_text_line_char_index (real->line);
619 620 621
      real->cached_char_index += real->line_char_offset;
    }

622 623
  check_invariants (iter);

624 625 626
  return real->cached_char_index;
}

627 628 629
/**
 * gtk_text_iter_get_line:
 * @iter: an iterator
630
 *
631 632 633
 * Returns the line number containing the iterator. Lines in
 * a #GtkTextBuffer are numbered beginning with 0 for the first
 * line in the buffer.
634
 *
635 636
 * Return value: a line number
 **/
637
gint
638
gtk_text_iter_get_line (const GtkTextIter *iter)
639 640 641
{
  GtkTextRealIter *real;

642 643 644
  g_return_val_if_fail (iter != NULL, 0);

  real = gtk_text_iter_make_surreal (iter);
645 646 647 648 649 650

  if (real == NULL)
    return 0;

  if (real->cached_line_number < 0)
    real->cached_line_number =
651
      _gtk_text_line_get_number (real->line);
652 653

  check_invariants (iter);
654 655 656 657

  return real->cached_line_number;
}

658 659 660
/**
 * gtk_text_iter_get_line_offset:
 * @iter: an iterator
661
 *
662 663 664
 * Returns the character offset of the iterator,
 * counting from the start of a newline-terminated line.
 * The first character on the line has offset 0.
665
 *
666 667
 * Return value: offset from start of line
 **/
668
gint
669
gtk_text_iter_get_line_offset (const GtkTextIter *iter)
670 671 672
{
  GtkTextRealIter *real;

673 674 675
  g_return_val_if_fail (iter != NULL, 0);

  real = gtk_text_iter_make_surreal (iter);
676 677 678 679

  if (real == NULL)
    return 0;

680 681 682
  ensure_char_offsets (real);

  check_invariants (iter);
683 684 685 686

  return real->line_char_offset;
}

687 688 689
/**
 * gtk_text_iter_get_line_index:
 * @iter: an iterator
690
 *
691 692 693 694 695
 * Returns the byte index of the iterator, counting
 * from the start of a newline-terminated line.
 * Remember that #GtkTextBuffer encodes text in
 * UTF-8, and that characters can require a variable
 * number of bytes to represent.
696
 *
697 698
 * Return value: distance from start of line, in bytes
 **/
699
gint
700
gtk_text_iter_get_line_index (const GtkTextIter *iter)
701 702
{
  GtkTextRealIter *real;
703
  
704 705 706
  g_return_val_if_fail (iter != NULL, 0);

  real = gtk_text_iter_make_surreal (iter);
707 708 709 710

  if (real == NULL)
    return 0;

711 712 713
  ensure_byte_offsets (real);

  check_invariants (iter);
714 715 716 717

  return real->line_byte_offset;
}

718 719 720 721 722 723 724 725 726 727 728
/**
 * gtk_text_iter_get_visible_line_offset:
 * @iter: a #GtkTextIter
 * 
 * Returns the offset in characters from the start of the
 * line to the given @iter, not counting characters that
 * are invisible due to tags with the "invisible" flag
 * toggled on.
 * 
 * Return value: offset in visible characters from the start of the line 
 **/
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
gint
gtk_text_iter_get_visible_line_offset (const GtkTextIter *iter)
{
  GtkTextRealIter *real;
  gint vis_offset;
  GtkTextLineSegment *seg;
  GtkTextIter pos;
  
  g_return_val_if_fail (iter != NULL, 0);

  real = gtk_text_iter_make_real (iter);

  if (real == NULL)
    return 0;

  ensure_char_offsets (real);

  check_invariants (iter);
747
  
748 749
  vis_offset = real->line_char_offset;

750 751
  g_assert (vis_offset >= 0);
  
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
  _gtk_text_btree_get_iter_at_line (real->tree,
                                    &pos,
                                    real->line,
                                    0);

  seg = _gtk_text_iter_get_indexable_segment (&pos);

  while (seg != real->segment)
    {
      /* This is a pretty expensive call, making the
       * whole function pretty lame; we could keep track
       * of current invisibility state by looking at toggle
       * segments as we loop, and then call this function
       * only once per line, in order to speed up the loop
       * quite a lot.
       */
      if (_gtk_text_btree_char_is_invisible (&pos))
        vis_offset -= seg->char_count;

      _gtk_text_iter_forward_indexable_segment (&pos);

      seg = _gtk_text_iter_get_indexable_segment (&pos);
    }

  if (_gtk_text_btree_char_is_invisible (&pos))
    vis_offset -= real->segment_char_offset;
  
  return vis_offset;
}

782 783 784 785 786 787 788 789 790 791 792 793

/**
 * gtk_text_iter_get_visible_line_index:
 * @iter: a #GtkTextIter
 * 
 * Returns the number of bytes from the start of the
 * line to the given @iter, not counting bytes that
 * are invisible due to tags with the "invisible" flag
 * toggled on.
 * 
 * Return value: byte index of @iter with respect to the start of the line
 **/
794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
gint
gtk_text_iter_get_visible_line_index (const GtkTextIter *iter)
{
  GtkTextRealIter *real;
  gint vis_offset;
  GtkTextLineSegment *seg;
  GtkTextIter pos;
  
  g_return_val_if_fail (iter != NULL, 0);

  real = gtk_text_iter_make_real (iter);

  if (real == NULL)
    return 0;

809
  ensure_byte_offsets (real);
810 811 812 813 814

  check_invariants (iter);

  vis_offset = real->line_byte_offset;

815 816
  g_assert (vis_offset >= 0);
  
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846
  _gtk_text_btree_get_iter_at_line (real->tree,
                                    &pos,
                                    real->line,
                                    0);

  seg = _gtk_text_iter_get_indexable_segment (&pos);

  while (seg != real->segment)
    {
      /* This is a pretty expensive call, making the
       * whole function pretty lame; we could keep track
       * of current invisibility state by looking at toggle
       * segments as we loop, and then call this function
       * only once per line, in order to speed up the loop
       * quite a lot.
       */
      if (_gtk_text_btree_char_is_invisible (&pos))
        vis_offset -= seg->byte_count;

      _gtk_text_iter_forward_indexable_segment (&pos);

      seg = _gtk_text_iter_get_indexable_segment (&pos);
    }

  if (_gtk_text_btree_char_is_invisible (&pos))
    vis_offset -= real->segment_byte_offset;
  
  return vis_offset;
}

847 848 849 850
/*
 * Dereferencing
 */

851 852 853
/**
 * gtk_text_iter_get_char:
 * @iter: an iterator
854
 *
855
 * Returns the Unicode character at this iterator.  (Equivalent to
856
 * operator* on a C++ iterator.)  If the element at this iterator is a
857
 * non-character element, such as an image embedded in the buffer, the
858
 * Unicode "unknown" character 0xFFFC is returned. If invoked on
859
 * the end iterator, zero is returned; zero is not a valid Unicode character.
860
 * So you can write a loop which ends when gtk_text_iter_get_char ()
861
 * returns 0.
862
 *
863
 * Return value: a Unicode character, or 0 if @iter is not dereferenceable
864
 **/
865
gunichar
866
gtk_text_iter_get_char (const GtkTextIter *iter)
867 868 869
{
  GtkTextRealIter *real;

870 871 872
  g_return_val_if_fail (iter != NULL, 0);

  real = gtk_text_iter_make_real (iter);
873 874 875 876

  if (real == NULL)
    return 0;

877
  check_invariants (iter);
878

879
  if (gtk_text_iter_is_end (iter))
880 881
    return 0;
  else if (real->segment->type == &gtk_text_char_type)
882
    {
883
      ensure_byte_offsets (real);
884
      
885 886
      return g_utf8_get_char (real->segment->body.chars +
                              real->segment_byte_offset);
887 888 889
    }
  else
    {
890 891
      /* Unicode "unknown character" 0xFFFC */
      return GTK_TEXT_UNKNOWN_CHAR;
892 893 894
    }
}

895 896 897 898
/**
 * gtk_text_iter_get_slice:
 * @start: iterator at start of a range
 * @end: iterator at end of a range
899
 *
900 901
 * Returns the text in the given range. A "slice" is an array of
 * characters encoded in UTF-8 format, including the Unicode "unknown"
902
 * character 0xFFFC for iterable non-character elements in the buffer,
903 904
 * such as images.  Because images are encoded in the slice, byte and
 * character offsets in the returned array will correspond to byte
905
 * offsets in the text buffer. Note that 0xFFFC can occur in normal
906 907
 * text as well, so it is not a reliable indicator that a pixbuf or
 * widget is in the buffer.
908
 *
909 910
 * Return value: slice of text from the buffer
 **/
911 912
gchar*
gtk_text_iter_get_slice       (const GtkTextIter *start,
913
                               const GtkTextIter *end)
914
{
915 916 917 918 919
  g_return_val_if_fail (start != NULL, NULL);
  g_return_val_if_fail (end != NULL, NULL);

  check_invariants (start);
  check_invariants (end);
920

921
  return _gtk_text_btree_get_text (start, end, TRUE, TRUE);
922 923
}

924 925 926 927
/**
 * gtk_text_iter_get_text:
 * @start: iterator at start of a range
 * @end: iterator at end of a range
928
 *
929 930 931 932
 * Returns <emphasis>text</emphasis> in the given range.  If the range
 * contains non-text elements such as images, the character and byte
 * offsets in the returned string will not correspond to character and
 * byte offsets in the buffer. If you want offsets to correspond, see
933 934
 * gtk_text_iter_get_slice ().
 *
935 936
 * Return value: array of characters from the buffer
 **/
937 938
gchar*
gtk_text_iter_get_text       (const GtkTextIter *start,
939
                              const GtkTextIter *end)
940
{
941 942 943 944 945
  g_return_val_if_fail (start != NULL, NULL);
  g_return_val_if_fail (end != NULL, NULL);

  check_invariants (start);
  check_invariants (end);
946

947
  return _gtk_text_btree_get_text (start, end, TRUE, FALSE);
948 949
}

950 951 952 953
/**
 * gtk_text_iter_get_visible_slice:
 * @start: iterator at start of range
 * @end: iterator at end of range
954 955
 *
 * Like gtk_text_iter_get_slice (), but invisible text is not included.
956 957
 * Invisible text is usually invisible because a #GtkTextTag with the
 * "invisible" attribute turned on has been applied to it.
958
 *
959 960
 * Return value: slice of text from the buffer
 **/
961 962
gchar*
gtk_text_iter_get_visible_slice (const GtkTextIter  *start,
963
                                 const GtkTextIter  *end)
964
{
965 966
  g_return_val_if_fail (start != NULL, NULL);
  g_return_val_if_fail (end != NULL, NULL);
967

968 969 970
  check_invariants (start);
  check_invariants (end);

971
  return _gtk_text_btree_get_text (start, end, FALSE, TRUE);
972 973
}

974 975 976 977
/**
 * gtk_text_iter_get_visible_text:
 * @start: iterator at start of range
 * @end: iterator at end of range
978 979
 *
 * Like gtk_text_iter_get_text (), but invisible text is not included.
980 981
 * Invisible text is usually invisible because a #GtkTextTag with the
 * "invisible" attribute turned on has been applied to it.
982
 *
983 984
 * Return value: string containing visible text in the range
 **/
985 986
gchar*
gtk_text_iter_get_visible_text (const GtkTextIter  *start,
987
                                const GtkTextIter  *end)
988
{
989 990 991 992 993 994
  g_return_val_if_fail (start != NULL, NULL);
  g_return_val_if_fail (end != NULL, NULL);

  check_invariants (start);
  check_invariants (end);

995
  return _gtk_text_btree_get_text (start, end, FALSE, FALSE);
996 997
}

998
/**
Havoc Pennington's avatar
Havoc Pennington committed
999
 * gtk_text_iter_get_pixbuf:
1000
 * @iter: an iterator
1001
 *
1002 1003
 * If the element at @iter is a pixbuf, the pixbuf is returned
 * (with no new reference count added). Otherwise,
1004
 * %NULL is returned.
1005
 *
Havoc Pennington's avatar
Havoc Pennington committed
1006
 * Return value: the pixbuf at @iter
1007
 **/
Havoc Pennington's avatar
Havoc Pennington committed
1008 1009
GdkPixbuf*
gtk_text_iter_get_pixbuf (const GtkTextIter *iter)
1010 1011 1012
{
  GtkTextRealIter *real;

1013
  g_return_val_if_fail (iter != NULL, NULL);
1014 1015

  real = gtk_text_iter_make_real (iter);
1016 1017

  if (real == NULL)
1018
    return NULL;
1019 1020 1021

  check_invariants (iter);

Havoc Pennington's avatar
Havoc Pennington committed
1022
  if (real->segment->type != &gtk_text_pixbuf_type)
1023
    return NULL;
1024
  else
Havoc Pennington's avatar
Havoc Pennington committed
1025
    return real->segment->body.pixbuf.pixbuf;
1026 1027
}

1028 1029 1030 1031
/**
 * gtk_text_iter_get_child_anchor:
 * @iter: an iterator
 *
1032
 * If the location at @iter contains a child anchor, the
1033
 * anchor is returned (with no new reference count added). Otherwise,
1034
 * %NULL is returned.
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
 *
 * Return value: the anchor at @iter
 **/
GtkTextChildAnchor*
gtk_text_iter_get_child_anchor (const GtkTextIter *iter)
{
  GtkTextRealIter *real;

  g_return_val_if_fail (iter != NULL, NULL);

  real = gtk_text_iter_make_real (iter);

  if (real == NULL)
    return NULL;

  check_invariants (iter);

  if (real->segment->type != &gtk_text_child_type)
    return NULL;
  else
    return real->segment->body.child.obj;
}

1058 1059 1060
/**
 * gtk_text_iter_get_marks:
 * @iter: an iterator
1061
 *
1062 1063 1064 1065 1066
 * Returns a list of all #GtkTextMark at this location. Because marks
 * are not iterable (they don't take up any "space" in the buffer,
 * they are just marks in between iterable locations), multiple marks
 * can exist in the same place. The returned list is not in any
 * meaningful order.
1067
 *
1068 1069
 * Return value: list of #GtkTextMark
 **/
1070 1071 1072 1073 1074 1075
GSList*
gtk_text_iter_get_marks (const GtkTextIter *iter)
{
  GtkTextRealIter *real;
  GtkTextLineSegment *seg;
  GSList *retval;
1076 1077 1078 1079

  g_return_val_if_fail (iter != NULL, NULL);

  real = gtk_text_iter_make_real (iter);
1080 1081 1082 1083

  if (real == NULL)
    return NULL;

1084 1085
  check_invariants (iter);

1086 1087 1088 1089 1090 1091
  retval = NULL;
  seg = real->any_segment;
  while (seg != real->segment)
    {
      if (seg->type == &gtk_text_left_mark_type ||
          seg->type == &gtk_text_right_mark_type)
1092
        retval = g_slist_prepend (retval, seg->body.mark.obj);
1093

1094 1095 1096 1097 1098 1099 1100 1101
      seg = seg->next;
    }

  /* The returned list isn't guaranteed to be in any special order,
     and it isn't. */
  return retval;
}

1102 1103 1104
/**
 * gtk_text_iter_get_toggled_tags:
 * @iter: an iterator
1105
 * @toggled_on: %TRUE to get toggled-on tags
1106
 *
1107
 * Returns a list of #GtkTextTag that are toggled on or off at this
1108
 * point.  (If @toggled_on is %TRUE, the list contains tags that are
1109 1110 1111 1112
 * toggled on.) If a tag is toggled on at @iter, then some non-empty
 * range of characters following @iter has that tag applied to it.  If
 * a tag is toggled off, then some non-empty range following @iter
 * does <emphasis>not</emphasis> have the tag applied to it.
1113
 *
1114 1115
 * Return value: tags toggled at this point
 **/
1116 1117
GSList*
gtk_text_iter_get_toggled_tags  (const GtkTextIter  *iter,
1118
                                 gboolean            toggled_on)
1119 1120 1121 1122
{
  GtkTextRealIter *real;
  GtkTextLineSegment *seg;
  GSList *retval;
1123 1124 1125 1126

  g_return_val_if_fail (iter != NULL, NULL);

  real = gtk_text_iter_make_real (iter);
1127 1128 1129 1130

  if (real == NULL)
    return NULL;

1131 1132
  check_invariants (iter);

1133 1134 1135 1136 1137 1138 1139 1140
  retval = NULL;
  seg = real->any_segment;
  while (seg != real->segment)
    {
      if (toggled_on)
        {
          if (seg->type == &gtk_text_toggle_on_type)
            {
1141
              retval = g_slist_prepend (retval, seg->body.toggle.info->tag);
1142 1143 1144 1145 1146 1147
            }
        }
      else
        {
          if (seg->type == &gtk_text_toggle_off_type)
            {
1148
              retval = g_slist_prepend (retval, seg->body.toggle.info->tag);
1149 1150
            }
        }
1151

1152 1153 1154 1155 1156 1157 1158 1159
      seg = seg->next;
    }

  /* The returned list isn't guaranteed to be in any special order,
     and it isn't. */
  return retval;
}

1160 1161 1162
/**
 * gtk_text_iter_begins_tag:
 * @iter: an iterator
1163
 * @tag: a #GtkTextTag, or %NULL
1164
 *
1165 1166 1167
 * Returns %TRUE if @tag is toggled on at exactly this point. If @tag
 * is %NULL, returns %TRUE if any tag is toggled on at this point. Note
 * that the gtk_text_iter_begins_tag () returns %TRUE if @iter is the
1168
 * <emphasis>start</emphasis> of the tagged range;
1169
 * gtk_text_iter_has_tag () tells you whether an iterator is
1170
 * <emphasis>within</emphasis> a tagged range.
1171
 *
1172 1173
 * Return value: whether @iter is the start of a range tagged with @tag
 **/
1174 1175
gboolean
gtk_text_iter_begins_tag    (const GtkTextIter  *iter,
1176
                             GtkTextTag         *tag)
1177 1178 1179
{
  GtkTextRealIter *real;
  GtkTextLineSegment *seg;
1180 1181 1182 1183

  g_return_val_if_fail (iter != NULL, FALSE);

  real = gtk_text_iter_make_real (iter);
1184 1185 1186 1187

  if (real == NULL)
    return FALSE;

1188 1189
  check_invariants (iter);

1190 1191 1192 1193 1194 1195 1196 1197 1198
  seg = real->any_segment;
  while (seg != real->segment)
    {
      if (seg->type == &gtk_text_toggle_on_type)
        {
          if (tag == NULL ||
              seg->body.toggle.info->tag == tag)
            return TRUE;
        }
1199

1200 1201 1202 1203 1204 1205
      seg = seg->next;
    }

  return FALSE;
}

1206 1207 1208
/**
 * gtk_text_iter_ends_tag:
 * @iter: an iterator
1209
 * @tag: a #GtkTextTag, or %NULL
1210
 *
1211 1212 1213
 * Returns %TRUE if @tag is toggled off at exactly this point. If @tag
 * is %NULL, returns %TRUE if any tag is toggled off at this point. Note
 * that the gtk_text_iter_ends_tag () returns %TRUE if @iter is the
1214
 * <emphasis>end</emphasis> of the tagged range;
1215
 * gtk_text_iter_has_tag () tells you whether an iterator is
1216
 * <emphasis>within</emphasis> a tagged range.
1217
 *
1218
 * Return value: whether @iter is the end of a range tagged with @tag
1219
 *
1220
 **/
1221 1222
gboolean
gtk_text_iter_ends_tag   (const GtkTextIter  *iter,
1223
                          GtkTextTag         *tag)
1224 1225 1226
{
  GtkTextRealIter *real;
  GtkTextLineSegment *seg;
1227 1228 1229 1230

  g_return_val_if_fail (iter != NULL, FALSE);

  real = gtk_text_iter_make_real (iter);
1231 1232 1233 1234

  if (real == NULL)
    return FALSE;

1235 1236
  check_invariants (iter);

1237 1238 1239 1240 1241 1242 1243 1244 1245
  seg = real->any_segment;
  while (seg != real->segment)
    {
      if (seg->type == &gtk_text_toggle_off_type)
        {
          if (tag == NULL ||
              seg->body.toggle.info->tag == tag)
            return TRUE;
        }
1246

1247 1248 1249 1250 1251 1252
      seg = seg->next;
    }

  return FALSE;
}

1253 1254 1255
/**
 * gtk_text_iter_toggles_tag:
 * @iter: an iterator
1256
 * @tag: a #GtkTextTag, or %NULL
1257 1258 1259
 *
 * This is equivalent to (gtk_text_iter_begins_tag () ||
 * gtk_text_iter_ends_tag ()), i.e. it tells you whether a range with
1260
 * @tag applied to it begins <emphasis>or</emphasis> ends at @iter.
1261
 *
1262 1263
 * Return value: whether @tag is toggled on or off at @iter
 **/
1264
gboolean
1265 1266
gtk_text_iter_toggles_tag (const GtkTextIter  *iter,
                           GtkTextTag         *tag)
1267 1268 1269
{
  GtkTextRealIter *real;
  GtkTextLineSegment *seg;
1270 1271 1272 1273

  g_return_val_if_fail (iter != NULL, FALSE);

  real = gtk_text_iter_make_real (iter);
1274 1275 1276 1277

  if (real == NULL)
    return FALSE;

1278 1279
  check_invariants (iter);

1280 1281 1282 1283 1284 1285 1286 1287
  seg = real->any_segment;
  while (seg != real->segment)
    {
      if ( (seg->type == &gtk_text_toggle_off_type ||
            seg->type == &gtk_text_toggle_on_type) &&
           (tag == NULL ||
            seg->body.toggle.info->tag == tag) )
        return TRUE;
1288

1289 1290 1291 1292 1293 1294
      seg = seg->next;
    }

  return FALSE;
}

1295 1296 1297 1298
/**
 * gtk_text_iter_has_tag:
 * @iter: an iterator
 * @tag: a #GtkTextTag
1299
 *
1300
 * Returns %TRUE if @iter is within a range tagged with @tag.
1301
 *
1302 1303
 * Return value: whether @iter is tagged with @tag
 **/
1304
gboolean
1305 1306
gtk_text_iter_has_tag (const GtkTextIter   *iter,
                       GtkTextTag          *tag)
1307 1308
{
  GtkTextRealIter *real;
1309 1310 1311 1312 1313

  g_return_val_if_fail (iter != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_TEXT_TAG (tag), FALSE);

  real = gtk_text_iter_make_surreal (iter);
1314 1315

  if (real == NULL)
1316 1317 1318
    return FALSE;

  check_invariants (iter);
1319 1320 1321

  if (real->line_byte_offset >= 0)
    {
1322
      return _gtk_text_line_byte_has_tag (real->line, real->tree,
1323
                                          real->line_byte_offset, tag);
1324 1325 1326
    }
  else
    {
1327
      g_assert (real->line_char_offset >= 0);
1328
      return _gtk_text_line_char_has_tag (real->line, real->tree,
1329
                                          real->line_char_offset, tag);
1330 1331 1332
    }
}

1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354
/**
 * gtk_text_iter_get_tags:
 * @iter: a #GtkTextIter
 * 
 * Returns a list of tags that apply to @iter, in ascending order of
 * priority (highest-priority tags are last). The #GtkTextTag in the
 * list don't have a reference added, but you have to free the list
 * itself.
 * 
 * Return value: list of #GtkTextTag
 **/
GSList*
gtk_text_iter_get_tags (const GtkTextIter *iter)
{
  GtkTextTag** tags;
  gint tag_count = 0;
  gint i;
  GSList *retval;
  
  g_return_val_if_fail (iter != NULL, NULL);
  
  /* Get the tags at this spot */
1355
  tags = _gtk_text_btree_get_tags (iter, &tag_count);
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366

  /* No tags, use default style */
  if (tags == NULL || tag_count == 0)
    {
      if (tags)
        g_free (tags);

      return NULL;
    }

  /* Sort tags in ascending order of priority */
1367
  _gtk_text_tag_array_sort (tags, tag_count);
1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382

  retval = NULL;
  i = 0;
  while (i < tag_count)
    {
      retval = g_slist_prepend (retval, tags[i]);
      ++i;
    }
  
  g_free (tags);

  /* Return tags in ascending order of priority */
  return g_slist_reverse (retval);
}

1383 1384 1385
/**
 * gtk_text_iter_editable:
 * @iter: an iterator
1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400
 * @default_setting: %TRUE if text is editable by default
 *
 * Returns whether the character at @iter is within an editable region
 * of text.  Non-editable text is "locked" and can't be changed by the
 * user via #GtkTextView. This function is simply a convenience
 * wrapper around gtk_text_iter_get_attributes (). If no tags applied
 * to this text affect editability, @default_setting will be returned.
 *
 * You don't want to use this function to decide whether text can be
 * inserted at @iter, because for insertion you don't want to know
 * whether the char at @iter is inside an editable range, you want to
 * know whether a new character inserted at @iter would be inside an
 * editable range. Use gtk_text_iter_can_insert() to handle this
 * case.
 * 
1401 1402
 * Return value: whether @iter is inside an editable range
 **/
1403 1404 1405 1406
gboolean
gtk_text_iter_editable (const GtkTextIter *iter,
                        gboolean           default_setting)
{
1407
  GtkTextAttributes *values;
1408
  gboolean retval;
1409

1410 1411
  g_return_val_if_fail (iter != NULL, FALSE);
  
1412
  values = gtk_text_attributes_new ();
1413 1414 1415

  values->editable = default_setting;

1416
  gtk_text_iter_get_attributes (iter, values);
1417 1418

  retval = values->editable;
1419

1420
  gtk_text_attributes_unref (values);
1421 1422 1423 1424

  return retval;
}

1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464
/**
 * gtk_text_iter_can_insert:
 * @iter: an iterator
 * @default_editability: %TRUE if text is editable by default
 * 
 * Considering the default editability of the buffer, and tags that
 * affect editability, determines whether text inserted at @iter would
 * be editable. If text inserted at @iter would be editable then the
 * user should be allowed to insert text at @iter.
 * gtk_text_buffer_insert_interactive() uses this function to decide
 * whether insertions are allowed at a given position.
 * 
 * Return value: whether text inserted at @iter would be editable
 **/
gboolean
gtk_text_iter_can_insert (const GtkTextIter *iter,
                          gboolean           default_editability)
{
  g_return_val_if_fail (iter != NULL, FALSE);
  
  if (gtk_text_iter_editable (iter, default_editability))
    return TRUE;
  /* If at start/end of buffer, default editability is used */
  else if ((gtk_text_iter_is_start (iter) ||
            gtk_text_iter_is_end (iter)) &&
           default_editability)
    return TRUE;
  else
    {
      /* if iter isn't editable, and the char before iter is,
       * then iter is the first char in an editable region
       * and thus insertion at iter results in editable text.
       */
      GtkTextIter prev = *iter;
      gtk_text_iter_backward_char (&prev);
      return gtk_text_iter_editable (&prev, default_editability);
    }
}


1465 1466 1467
/**
 * gtk_text_iter_get_language:
 * @iter: an iterator
1468 1469
 *
 * A convenience wrapper around gtk_text_iter_get_attributes (),
1470
 * which returns the language in effect at @iter. If no tags affecting
1471
 * language apply to @iter, the return value is identical to that of
1472 1473
 * gtk_get_default_language ().
 *
1474 1475
 * Return value: language in effect at @iter
 **/
1476
PangoLanguage *
1477 1478
gtk_text_iter_get_language (const GtkTextIter *iter)
{
1479
  GtkTextAttributes *values;
1480 1481
  PangoLanguage *retval;
  
1482
  values = gtk_text_attributes_new ();
1483