* window.c (window_internal_width): New function, which accounts
[bpt/emacs.git] / src / textprop.c
CommitLineData
d418ef42
JA
1/* Interface code for dealing with text properties.
2 Copyright (C) 1992 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 1, or (at your option)
9any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs; see the file COPYING. If not, write to
18the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20#include "config.h"
21#include "lisp.h"
22#include "intervals.h"
23#include "buffer.h"
24\f
25
26/* NOTES: previous- and next- property change will have to skip
27 zero-length intervals if they are implemented. This could be done
28 inside next_interval and previous_interval.
29
9c79dd1b
JA
30 set_properties needs to deal with the interval property cache.
31
d418ef42
JA
32 It is assumed that for any interval plist, a property appears
33 only once on the list. Although some code i.e., remove_properties (),
34 handles the more general case, the uniqueness of properties is
35 neccessary for the system to remain consistent. This requirement
36 is enforced by the subrs installing properties onto the intervals. */
37
25013c26
JA
38/* The rest of the file is within this conditional */
39#ifdef USE_TEXT_PROPERTIES
d418ef42
JA
40\f
41/* Types of hooks. */
42Lisp_Object Qmouse_left;
43Lisp_Object Qmouse_entered;
44Lisp_Object Qpoint_left;
45Lisp_Object Qpoint_entered;
46Lisp_Object Qmodification;
47
48/* Visual properties text (including strings) may have. */
49Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
50Lisp_Object Qinvisible, Qread_only;
51\f
ac876a79
JA
52/* Extract the interval at the position pointed to by BEGIN from
53 OBJECT, a string or buffer. Additionally, check that the positions
54 pointed to by BEGIN and END are within the bounds of OBJECT, and
55 reverse them if *BEGIN is greater than *END. The objects pointed
56 to by BEGIN and END may be integers or markers; if the latter, they
57 are coerced to integers.
d418ef42
JA
58
59 Note that buffer points don't correspond to interval indices.
60 For example, point-max is 1 greater than the index of the last
61 character. This difference is handled in the caller, which uses
62 the validated points to determine a length, and operates on that.
63 Exceptions are Ftext_properties_at, Fnext_property_change, and
64 Fprevious_property_change which call this function with BEGIN == END.
65 Handle this case specially.
66
67 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
ac876a79
JA
68 create an interval tree for OBJECT if one doesn't exist, provided
69 the object actually contains text. In the current design, if there
70 is no text, there can be no text properties. */
d418ef42
JA
71
72#define soft 0
73#define hard 1
74
75static INTERVAL
76validate_interval_range (object, begin, end, force)
77 Lisp_Object object, *begin, *end;
78 int force;
79{
80 register INTERVAL i;
81 CHECK_STRING_OR_BUFFER (object, 0);
82 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
83 CHECK_NUMBER_COERCE_MARKER (*end, 0);
84
85 /* If we are asked for a point, but from a subr which operates
86 on a range, then return nothing. */
87 if (*begin == *end && begin != end)
88 return NULL_INTERVAL;
89
90 if (XINT (*begin) > XINT (*end))
91 {
92 register int n;
93 n = XFASTINT (*begin); /* This is legit even if *begin is < 0 */
94 *begin = *end;
95 XFASTINT (*end) = n; /* because this is all we do with n. */
96 }
97
98 if (XTYPE (object) == Lisp_Buffer)
99 {
100 register struct buffer *b = XBUFFER (object);
101
102 /* If there's no text, there are no properties. */
103 if (BUF_BEGV (b) == BUF_ZV (b))
104 return NULL_INTERVAL;
105
106 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
107 && XINT (*end) <= BUF_ZV (b)))
108 args_out_of_range (*begin, *end);
109 i = b->intervals;
110
111 /* Special case for point-max: return the interval for the
112 last character. */
113 if (*begin == *end && *begin == BUF_Z (b))
114 *begin -= 1;
115 }
116 else
117 {
118 register struct Lisp_String *s = XSTRING (object);
119
120 if (! (1 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
121 && XINT (*end) <= s->size))
122 args_out_of_range (*begin, *end);
123 i = s->intervals;
124 }
125
126 if (NULL_INTERVAL_P (i))
127 return (force ? create_root_interval (object) : i);
128
129 return find_interval (i, XINT (*begin));
130}
131
132/* Validate LIST as a property list. If LIST is not a list, then
133 make one consisting of (LIST nil). Otherwise, verify that LIST
134 is even numbered and thus suitable as a plist. */
135
136static Lisp_Object
137validate_plist (list)
138{
139 if (NILP (list))
140 return Qnil;
141
142 if (CONSP (list))
143 {
144 register int i;
145 register Lisp_Object tail;
146 for (i = 0, tail = list; !NILP (tail); i++)
147 tail = Fcdr (tail);
148 if (i & 1)
149 error ("Odd length text property list");
150 return list;
151 }
152
153 return Fcons (list, Fcons (Qnil, Qnil));
154}
155
156#define set_properties(list,i) (i->plist = Fcopy_sequence (list))
157
158/* Return nonzero if interval I has all the properties,
159 with the same values, of list PLIST. */
160
161static int
162interval_has_all_properties (plist, i)
163 Lisp_Object plist;
164 INTERVAL i;
165{
166 register Lisp_Object tail1, tail2, sym1, sym2;
167 register int found;
168
169 /* Go through each element of PLIST. */
170 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
171 {
172 sym1 = Fcar (tail1);
173 found = 0;
174
175 /* Go through I's plist, looking for sym1 */
176 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
177 if (EQ (sym1, Fcar (tail2)))
178 {
179 /* Found the same property on both lists. If the
180 values are unequal, return zero. */
181 if (! EQ (Fequal (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))),
182 Qt))
183 return 0;
184
185 /* Property has same value on both lists; go to next one. */
186 found = 1;
187 break;
188 }
189
190 if (! found)
191 return 0;
192 }
193
194 return 1;
195}
196
197/* Return nonzero if the plist of interval I has any of the
198 properties of PLIST, regardless of their values. */
199
200static INLINE int
201interval_has_some_properties (plist, i)
202 Lisp_Object plist;
203 INTERVAL i;
204{
205 register Lisp_Object tail1, tail2, sym;
206
207 /* Go through each element of PLIST. */
208 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
209 {
210 sym = Fcar (tail1);
211
212 /* Go through i's plist, looking for tail1 */
213 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
214 if (EQ (sym, Fcar (tail2)))
215 return 1;
216 }
217
218 return 0;
219}
220
221/* Add the properties of PLIST to the interval I, or set
222 the value of I's property to the value of the property on PLIST
223 if they are different.
224
225 Return nonzero if this changes I (i.e., if any members of PLIST
226 are actually added to I's plist) */
227
228static INLINE int
229add_properties (plist, i)
230 Lisp_Object plist;
231 INTERVAL i;
232{
233 register Lisp_Object tail1, tail2, sym1, val1;
234 register int changed = 0;
235 register int found;
236
237 /* Go through each element of PLIST. */
238 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
239 {
240 sym1 = Fcar (tail1);
241 val1 = Fcar (Fcdr (tail1));
242 found = 0;
243
244 /* Go through I's plist, looking for sym1 */
245 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
246 if (EQ (sym1, Fcar (tail2)))
247 {
248 register Lisp_Object this_cdr = Fcdr (tail2);
249
250 /* Found the property. Now check its value. */
251 found = 1;
252
253 /* The properties have the same value on both lists.
254 Continue to the next property. */
255 if (Fequal (val1, Fcar (this_cdr)))
256 break;
257
258 /* I's property has a different value -- change it */
259 Fsetcar (this_cdr, val1);
260 changed++;
261 break;
262 }
263
264 if (! found)
265 {
266 i->plist = Fcons (sym1, Fcons (val1, i->plist));
267 changed++;
268 }
269 }
270
271 return changed;
272}
273
274/* For any members of PLIST which are properties of I, remove them
275 from I's plist. */
276
277static INLINE int
278remove_properties (plist, i)
279 Lisp_Object plist;
280 INTERVAL i;
281{
282 register Lisp_Object tail1, tail2, sym;
283 register Lisp_Object current_plist = i->plist;
284 register int changed = 0;
285
286 /* Go through each element of plist. */
287 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
288 {
289 sym = Fcar (tail1);
290
291 /* First, remove the symbol if its at the head of the list */
292 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
293 {
294 current_plist = Fcdr (Fcdr (current_plist));
295 changed++;
296 }
297
298 /* Go through i's plist, looking for sym */
299 tail2 = current_plist;
300 while (! NILP (tail2))
301 {
302 register Lisp_Object this = Fcdr (Fcdr (tail2));
303 if (EQ (sym, Fcar (this)))
304 {
305 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
306 changed++;
307 }
308 tail2 = this;
309 }
310 }
311
312 if (changed)
313 i->plist = current_plist;
314 return changed;
315}
316
317/* Remove all properties from interval I. Return non-zero
318 if this changes the interval. */
319
320static INLINE int
321erase_properties (i)
322 INTERVAL i;
323{
324 if (NILP (i->plist))
325 return 0;
326
327 i->plist = Qnil;
328 return 1;
329}
330\f
d418ef42
JA
331DEFUN ("text-properties-at", Ftext_properties_at,
332 Stext_properties_at, 1, 2, 0,
333 "Return the list of properties held by the character at POSITION\n\
334in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
335defaults to the current buffer.")
336 (pos, object)
337 Lisp_Object pos, object;
338{
339 register INTERVAL i;
340 register int p;
341
342 if (NILP (object))
343 XSET (object, Lisp_Buffer, current_buffer);
344
345 i = validate_interval_range (object, &pos, &pos, soft);
346 if (NULL_INTERVAL_P (i))
347 return Qnil;
348
349 return i->plist;
350}
351
352DEFUN ("next-property-change", Fnext_property_change,
353 Snext_property_change, 2, 2, 0,
354 "Return the position after POSITION in OBJECT which has properties\n\
355different from those at POSITION. OBJECT may be a string or buffer.\n\
356Returns nil if unsuccessful.")
357 (pos, object)
358 Lisp_Object pos, object;
359{
360 register INTERVAL i, next;
361
362 i = validate_interval_range (object, &pos, &pos, soft);
363 if (NULL_INTERVAL_P (i))
364 return Qnil;
365
366 next = next_interval (i);
367 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
368 next = next_interval (next);
369
370 if (NULL_INTERVAL_P (next))
371 return Qnil;
372
373 return next->position;
374}
375
9c79dd1b
JA
376DEFUN ("next-single-property-change", Fnext_single_property_change,
377 Snext_single_property_change, 3, 3, 0,
378 "Return the position after POSITION in OBJECT which has a different\n\
379value for PROPERTY than the text at POSITION. OBJECT may be a string or\n\
380buffer. Returns nil if unsuccessful.")
381 (pos, object, prop)
382{
383 register INTERVAL i, next;
384 register Lisp_Object here_val;
385
386 i = validate_interval_range (object, &pos, &pos, soft);
387 if (NULL_INTERVAL_P (i))
388 return Qnil;
389
390 here_val = Fget (prop, i->plist);
391 next = next_interval (i);
392 while (! NULL_INTERVAL_P (next) && EQ (here_val, Fget (prop, next->plist)))
393 next = next_interval (next);
394
395 if (NULL_INTERVAL_P (next))
396 return Qnil;
397
398 return next->position;
399}
400
d418ef42
JA
401DEFUN ("previous-property-change", Fprevious_property_change,
402 Sprevious_property_change, 2, 2, 0,
9c79dd1b 403 "Return the position preceding POSITION in OBJECT which has properties\n\
d418ef42
JA
404different from those at POSITION. OBJECT may be a string or buffer.\n\
405Returns nil if unsuccessful.")
406 (pos, object)
407 Lisp_Object pos, object;
408{
409 register INTERVAL i, previous;
410
411 i = validate_interval_range (object, &pos, &pos, soft);
412 if (NULL_INTERVAL_P (i))
413 return Qnil;
414
415 previous = previous_interval (i);
416 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i))
417 previous = previous_interval (previous);
418 if (NULL_INTERVAL_P (previous))
419 return Qnil;
420
421 return previous->position + LENGTH (previous) - 1;
422}
423
9c79dd1b
JA
424DEFUN ("previous-single-property-change", Fprevious_single_property_change,
425 Sprevious_single_property_change, 3, 3, 0,
426 "Return the position preceding POSITION in OBJECT which has a\n\
c2e42adb 427different value for PROPERTY than the text at POSITION. OBJECT may be\n\
9c79dd1b
JA
428a string or buffer. Returns nil if unsuccessful.")
429 (pos, object, prop)
430{
431 register INTERVAL i, previous;
432 register Lisp_Object here_val;
433
434 i = validate_interval_range (object, &pos, &pos, soft);
435 if (NULL_INTERVAL_P (i))
436 return Qnil;
437
438 here_val = Fget (prop, i->plist);
439 previous = previous_interval (i);
440 while (! NULL_INTERVAL_P (previous)
441 && EQ (here_val, Fget (prop, previous->plist)))
442 previous = previous_interval (previous);
443 if (NULL_INTERVAL_P (previous))
444 return Qnil;
445
446 return previous->position + LENGTH (previous) - 1;
447}
448
d418ef42
JA
449DEFUN ("add-text-properties", Fadd_text_properties,
450 Sadd_text_properties, 4, 4, 0,
cd7d971d
JA
451 "Add the PROPERTIES, a property list, to the text of OBJECT,\n\
452a string or buffer, in the range START to END. Returns t if any change\n\
d418ef42
JA
453was made, nil otherwise.")
454 (object, start, end, properties)
455 Lisp_Object object, start, end, properties;
456{
457 register INTERVAL i, unchanged;
458 register int s, len, modified;
459
460 properties = validate_plist (properties);
461 if (NILP (properties))
462 return Qnil;
463
464 i = validate_interval_range (object, &start, &end, hard);
465 if (NULL_INTERVAL_P (i))
466 return Qnil;
467
468 s = XINT (start);
469 len = XINT (end) - s;
470
471 /* If we're not starting on an interval boundary, we have to
472 split this interval. */
473 if (i->position != s)
474 {
475 /* If this interval already has the properties, we can
476 skip it. */
477 if (interval_has_all_properties (properties, i))
478 {
479 int got = (LENGTH (i) - (s - i->position));
480 if (got >= len)
481 return Qnil;
482 len -= got;
483 }
484 else
485 {
486 unchanged = i;
487 i = split_interval_right (unchanged, s - unchanged->position + 1);
488 copy_properties (unchanged, i);
489 if (LENGTH (i) > len)
490 {
491 i = split_interval_left (i, len + 1);
492 copy_properties (unchanged, i);
493 add_properties (properties, i);
494 return Qt;
495 }
496
497 add_properties (properties, i);
498 modified = 1;
499 len -= LENGTH (i);
500 i = next_interval (i);
501 }
502 }
503
504 /* We are at the beginning of an interval, with len to scan */
505 while (1)
506 {
507 if (LENGTH (i) >= len)
508 {
509 if (interval_has_all_properties (properties, i))
510 return modified ? Qt : Qnil;
511
512 if (LENGTH (i) == len)
513 {
514 add_properties (properties, i);
515 return Qt;
516 }
517
518 /* i doesn't have the properties, and goes past the change limit */
519 unchanged = i;
520 i = split_interval_left (unchanged, len + 1);
521 copy_properties (unchanged, i);
522 add_properties (properties, i);
523 return Qt;
524 }
525
526 len -= LENGTH (i);
527 modified += add_properties (properties, i);
528 i = next_interval (i);
529 }
530}
531
532DEFUN ("set-text-properties", Fset_text_properties,
533 Sset_text_properties, 4, 4, 0,
cd7d971d
JA
534 "Make the text of OBJECT, a string or buffer, have precisely\n\
535PROPERTIES, a list of properties, in the range START to END.\n\
d418ef42
JA
536\n\
537If called with a valid property list, return t (text was changed).\n\
538Otherwise return nil.")
539 (object, start, end, properties)
540 Lisp_Object object, start, end, properties;
541{
542 register INTERVAL i, unchanged;
9c79dd1b 543 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42
JA
544 register int s, len;
545
546 properties = validate_plist (properties);
547 if (NILP (properties))
548 return Qnil;
549
550 i = validate_interval_range (object, &start, &end, hard);
551 if (NULL_INTERVAL_P (i))
552 return Qnil;
553
554 s = XINT (start);
555 len = XINT (end) - s;
556
557 if (i->position != s)
558 {
559 unchanged = i;
560 i = split_interval_right (unchanged, s - unchanged->position + 1);
9c79dd1b 561 set_properties (properties, i);
7855e674 562
d418ef42
JA
563 if (LENGTH (i) > len)
564 {
9c79dd1b
JA
565 i = split_interval_right (i, len);
566 copy_properties (unchanged, i);
d418ef42
JA
567 return Qt;
568 }
569
9c79dd1b
JA
570 if (LENGTH (i) == len)
571 return Qt;
572
573 prev_changed = i;
d418ef42
JA
574 len -= LENGTH (i);
575 i = next_interval (i);
576 }
577
cd7d971d 578 /* We are starting at the beginning of an interval, I */
7855e674 579 while (len > 0)
d418ef42
JA
580 {
581 if (LENGTH (i) >= len)
582 {
cd7d971d
JA
583 if (LENGTH (i) > len)
584 i = split_interval_left (i, len + 1);
d418ef42 585
9c79dd1b
JA
586 if (NULL_INTERVAL_P (prev_changed))
587 set_properties (properties, i);
588 else
589 merge_interval_left (i);
d418ef42
JA
590 return Qt;
591 }
592
593 len -= LENGTH (i);
9c79dd1b
JA
594 if (NULL_INTERVAL_P (prev_changed))
595 {
596 set_properties (properties, i);
597 prev_changed = i;
598 }
599 else
600 prev_changed = i = merge_interval_left (i);
601
d418ef42
JA
602 i = next_interval (i);
603 }
604
605 return Qt;
606}
607
608DEFUN ("remove-text-properties", Fremove_text_properties,
609 Sremove_text_properties, 4, 4, 0,
cd7d971d
JA
610 "Remove the PROPERTIES, a property list, from the text of OBJECT,\n\
611a string or buffer, in the range START to END. Returns t if any change\n\
d418ef42
JA
612was made, nil otherwise.")
613 (object, start, end, properties)
614 Lisp_Object object, start, end, properties;
615{
616 register INTERVAL i, unchanged;
617 register int s, len, modified;
618
619 i = validate_interval_range (object, &start, &end, soft);
620 if (NULL_INTERVAL_P (i))
621 return Qnil;
622
623 s = XINT (start);
624 len = XINT (end) - s;
9c79dd1b 625
d418ef42
JA
626 if (i->position != s)
627 {
628 /* No properties on this first interval -- return if
629 it covers the entire region. */
630 if (! interval_has_some_properties (properties, i))
631 {
632 int got = (LENGTH (i) - (s - i->position));
633 if (got >= len)
634 return Qnil;
635 len -= got;
636 }
637 /* Remove the properties from this interval. If it's short
638 enough, return, splitting it if it's too short. */
639 else
640 {
641 unchanged = i;
642 i = split_interval_right (unchanged, s - unchanged->position + 1);
643 copy_properties (unchanged, i);
644 if (LENGTH (i) > len)
645 {
646 i = split_interval_left (i, len + 1);
647 copy_properties (unchanged, i);
648 remove_properties (properties, i);
649 return Qt;
650 }
651
652 remove_properties (properties, i);
653 modified = 1;
654 len -= LENGTH (i);
655 i = next_interval (i);
656 }
657 }
658
659 /* We are at the beginning of an interval, with len to scan */
660 while (1)
661 {
662 if (LENGTH (i) >= len)
663 {
664 if (! interval_has_some_properties (properties, i))
665 return modified ? Qt : Qnil;
666
667 if (LENGTH (i) == len)
668 {
669 remove_properties (properties, i);
670 return Qt;
671 }
672
673 /* i has the properties, and goes past the change limit */
674 unchanged = split_interval_right (i, len + 1);
675 copy_properties (unchanged, i);
676 remove_properties (properties, i);
677 return Qt;
678 }
679
680 len -= LENGTH (i);
681 modified += remove_properties (properties, i);
682 i = next_interval (i);
683 }
684}
685
686DEFUN ("erase-text-properties", Ferase_text_properties,
687 Serase_text_properties, 3, 3, 0,
688 "Remove all text properties from OBJECT (a string or buffer), in the\n\
689range START to END. Returns t if any change was made, nil otherwise.")
690 (object, start, end)
691 Lisp_Object object, start, end;
692{
cd7d971d 693 register INTERVAL i;
03ad6beb 694 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42
JA
695 register int s, len, modified;
696
697 i = validate_interval_range (object, &start, &end, soft);
698 if (NULL_INTERVAL_P (i))
699 return Qnil;
700
701 s = XINT (start);
702 len = XINT (end) - s;
7855e674 703
d418ef42
JA
704 if (i->position != s)
705 {
7855e674 706 register int got;
cd7d971d 707 register INTERVAL unchanged = i;
d418ef42 708
7855e674 709 /* If there are properties here, then this text will be modified. */
cd7d971d 710 if (! NILP (i->plist))
d418ef42 711 {
d418ef42 712 i = split_interval_right (unchanged, s - unchanged->position + 1);
7855e674 713 i->plist = Qnil;
d418ef42 714 modified++;
7855e674
JA
715
716 if (LENGTH (i) > len)
717 {
718 i = split_interval_right (i, len + 1);
719 copy_properties (unchanged, i);
720 return Qt;
721 }
722
723 if (LENGTH (i) == len)
724 return Qt;
725
726 got = LENGTH (i);
d418ef42 727 }
cd7d971d
JA
728 /* If the text of I is without any properties, and contains
729 LEN or more characters, then we may return without changing
730 anything.*/
7855e674
JA
731 else if (LENGTH (i) - (s - i->position) <= len)
732 return Qnil;
cd7d971d
JA
733 /* The amount of text to change extends past I, so just note
734 how much we've gotten. */
7855e674
JA
735 else
736 got = LENGTH (i) - (s - i->position);
d418ef42
JA
737
738 len -= got;
7855e674 739 prev_changed = i;
d418ef42
JA
740 i = next_interval (i);
741 }
742
7855e674 743 /* We are starting at the beginning of an interval, I. */
d418ef42
JA
744 while (len > 0)
745 {
7855e674 746 if (LENGTH (i) >= len)
d418ef42 747 {
cd7d971d
JA
748 /* If I has no properties, simply merge it if possible. */
749 if (NILP (i->plist))
7855e674
JA
750 {
751 if (! NULL_INTERVAL_P (prev_changed))
752 merge_interval_left (i);
d418ef42 753
7855e674
JA
754 return modified ? Qt : Qnil;
755 }
756
cd7d971d
JA
757 if (LENGTH (i) > len)
758 i = split_interval_left (i, len + 1);
7855e674
JA
759 if (! NULL_INTERVAL_P (prev_changed))
760 merge_interval_left (i);
cd7d971d
JA
761 else
762 i->plist = Qnil;
7855e674 763
cd7d971d 764 return Qt;
d418ef42
JA
765 }
766
cd7d971d 767 /* Here if we still need to erase past the end of I */
d418ef42 768 len -= LENGTH (i);
7855e674
JA
769 if (NULL_INTERVAL_P (prev_changed))
770 {
771 modified += erase_properties (i);
772 prev_changed = i;
773 }
774 else
775 {
cd7d971d
JA
776 modified += ! NILP (i->plist);
777 /* Merging I will give it the properties of PREV_CHANGED. */
7855e674
JA
778 prev_changed = i = merge_interval_left (i);
779 }
780
d418ef42
JA
781 i = next_interval (i);
782 }
783
784 return modified ? Qt : Qnil;
785}
786
787void
788syms_of_textprop ()
789{
790 DEFVAR_INT ("interval-balance-threshold", &interval_balance_threshold,
c2e42adb 791 "Threshold for rebalancing interval trees, expressed as the\n\
d418ef42
JA
792percentage by which the left interval tree should not differ from the right.");
793 interval_balance_threshold = 8;
794
795 /* Common attributes one might give text */
796
797 staticpro (&Qforeground);
798 Qforeground = intern ("foreground");
799 staticpro (&Qbackground);
800 Qbackground = intern ("background");
801 staticpro (&Qfont);
802 Qfont = intern ("font");
803 staticpro (&Qstipple);
804 Qstipple = intern ("stipple");
805 staticpro (&Qunderline);
806 Qunderline = intern ("underline");
807 staticpro (&Qread_only);
808 Qread_only = intern ("read-only");
809 staticpro (&Qinvisible);
810 Qinvisible = intern ("invisible");
811
812 /* Properties that text might use to specify certain actions */
813
814 staticpro (&Qmouse_left);
815 Qmouse_left = intern ("mouse-left");
816 staticpro (&Qmouse_entered);
817 Qmouse_entered = intern ("mouse-entered");
818 staticpro (&Qpoint_left);
819 Qpoint_left = intern ("point-left");
820 staticpro (&Qpoint_entered);
821 Qpoint_entered = intern ("point-entered");
822 staticpro (&Qmodification);
823 Qmodification = intern ("modification");
824
825 defsubr (&Stext_properties_at);
826 defsubr (&Snext_property_change);
9c79dd1b 827 defsubr (&Snext_single_property_change);
d418ef42 828 defsubr (&Sprevious_property_change);
9c79dd1b 829 defsubr (&Sprevious_single_property_change);
d418ef42
JA
830 defsubr (&Sadd_text_properties);
831 defsubr (&Sset_text_properties);
832 defsubr (&Sremove_text_properties);
833 defsubr (&Serase_text_properties);
834}
25013c26
JA
835
836#else
837
838lose -- this shouldn't be compiled if USE_TEXT_PROPERTIES isn't defined
839
840#endif /* USE_TEXT_PROPERTIES */