(construct_menu_click, construct_mouse_click):
[bpt/emacs.git] / src / textprop.c
CommitLineData
d418ef42 1/* Interface code for dealing with text properties.
23ad4658 2 Copyright (C) 1993 Free Software Foundation, Inc.
d418ef42
JA
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
71dfa9f4 8the Free Software Foundation; either version 2, or (at your option)
d418ef42
JA
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
18160b98 20#include <config.h>
d418ef42
JA
21#include "lisp.h"
22#include "intervals.h"
23#include "buffer.h"
f5957179 24#include "window.h"
d418ef42
JA
25\f
26
27/* NOTES: previous- and next- property change will have to skip
28 zero-length intervals if they are implemented. This could be done
29 inside next_interval and previous_interval.
30
9c79dd1b
JA
31 set_properties needs to deal with the interval property cache.
32
d418ef42 33 It is assumed that for any interval plist, a property appears
d4b530ad 34 only once on the list. Although some code i.e., remove_properties,
d418ef42 35 handles the more general case, the uniqueness of properties is
eb8c3be9 36 necessary for the system to remain consistent. This requirement
d418ef42
JA
37 is enforced by the subrs installing properties onto the intervals. */
38
25013c26
JA
39/* The rest of the file is within this conditional */
40#ifdef USE_TEXT_PROPERTIES
d418ef42
JA
41\f
42/* Types of hooks. */
43Lisp_Object Qmouse_left;
44Lisp_Object Qmouse_entered;
45Lisp_Object Qpoint_left;
46Lisp_Object Qpoint_entered;
dc70cea7
RS
47Lisp_Object Qcategory;
48Lisp_Object Qlocal_map;
d418ef42
JA
49
50/* Visual properties text (including strings) may have. */
51Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
19e1c426
RS
52Lisp_Object Qinvisible, Qread_only, Qhidden;
53
54/* Sticky properties */
55Lisp_Object Qfront_sticky, Qrear_nonsticky;
d7b4e137
JB
56
57/* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
58 the o1's cdr. Otherwise, return zero. This is handy for
59 traversing plists. */
60#define PLIST_ELT_P(o1, o2) (CONSP (o1) && CONSP ((o2) = XCONS (o1)->cdr))
61
688a5a0f
RS
62Lisp_Object Vinhibit_point_motion_hooks;
63
d418ef42 64\f
ac876a79
JA
65/* Extract the interval at the position pointed to by BEGIN from
66 OBJECT, a string or buffer. Additionally, check that the positions
67 pointed to by BEGIN and END are within the bounds of OBJECT, and
68 reverse them if *BEGIN is greater than *END. The objects pointed
69 to by BEGIN and END may be integers or markers; if the latter, they
70 are coerced to integers.
d418ef42 71
d4b530ad
RS
72 When OBJECT is a string, we increment *BEGIN and *END
73 to make them origin-one.
74
d418ef42
JA
75 Note that buffer points don't correspond to interval indices.
76 For example, point-max is 1 greater than the index of the last
77 character. This difference is handled in the caller, which uses
78 the validated points to determine a length, and operates on that.
79 Exceptions are Ftext_properties_at, Fnext_property_change, and
80 Fprevious_property_change which call this function with BEGIN == END.
81 Handle this case specially.
82
83 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
ac876a79
JA
84 create an interval tree for OBJECT if one doesn't exist, provided
85 the object actually contains text. In the current design, if there
d4b530ad 86 is no text, there can be no text properties. */
d418ef42
JA
87
88#define soft 0
89#define hard 1
90
91static INTERVAL
92validate_interval_range (object, begin, end, force)
93 Lisp_Object object, *begin, *end;
94 int force;
95{
96 register INTERVAL i;
d4b530ad
RS
97 int searchpos;
98
d418ef42
JA
99 CHECK_STRING_OR_BUFFER (object, 0);
100 CHECK_NUMBER_COERCE_MARKER (*begin, 0);
101 CHECK_NUMBER_COERCE_MARKER (*end, 0);
102
103 /* If we are asked for a point, but from a subr which operates
104 on a range, then return nothing. */
105 if (*begin == *end && begin != end)
106 return NULL_INTERVAL;
107
108 if (XINT (*begin) > XINT (*end))
109 {
d4b530ad
RS
110 Lisp_Object n;
111 n = *begin;
d418ef42 112 *begin = *end;
d4b530ad 113 *end = n;
d418ef42
JA
114 }
115
116 if (XTYPE (object) == Lisp_Buffer)
117 {
118 register struct buffer *b = XBUFFER (object);
119
d418ef42
JA
120 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
121 && XINT (*end) <= BUF_ZV (b)))
122 args_out_of_range (*begin, *end);
123 i = b->intervals;
124
d4b530ad
RS
125 /* If there's no text, there are no properties. */
126 if (BUF_BEGV (b) == BUF_ZV (b))
127 return NULL_INTERVAL;
128
129 searchpos = XINT (*begin);
d418ef42
JA
130 }
131 else
132 {
133 register struct Lisp_String *s = XSTRING (object);
134
d4b530ad 135 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
d418ef42
JA
136 && XINT (*end) <= s->size))
137 args_out_of_range (*begin, *end);
d4b530ad
RS
138 /* User-level Positions in strings start with 0,
139 but the interval code always wants positions starting with 1. */
140 XFASTINT (*begin) += 1;
b1e94638
JB
141 if (begin != end)
142 XFASTINT (*end) += 1;
d418ef42 143 i = s->intervals;
d4b530ad
RS
144
145 if (s->size == 0)
146 return NULL_INTERVAL;
147
148 searchpos = XINT (*begin);
d418ef42
JA
149 }
150
151 if (NULL_INTERVAL_P (i))
152 return (force ? create_root_interval (object) : i);
153
d4b530ad 154 return find_interval (i, searchpos);
d418ef42
JA
155}
156
157/* Validate LIST as a property list. If LIST is not a list, then
158 make one consisting of (LIST nil). Otherwise, verify that LIST
159 is even numbered and thus suitable as a plist. */
160
161static Lisp_Object
162validate_plist (list)
4d780c76 163 Lisp_Object list;
d418ef42
JA
164{
165 if (NILP (list))
166 return Qnil;
167
168 if (CONSP (list))
169 {
170 register int i;
171 register Lisp_Object tail;
172 for (i = 0, tail = list; !NILP (tail); i++)
b1e94638
JB
173 {
174 tail = Fcdr (tail);
175 QUIT;
176 }
d418ef42
JA
177 if (i & 1)
178 error ("Odd length text property list");
179 return list;
180 }
181
182 return Fcons (list, Fcons (Qnil, Qnil));
183}
184
d418ef42
JA
185/* Return nonzero if interval I has all the properties,
186 with the same values, of list PLIST. */
187
188static int
189interval_has_all_properties (plist, i)
190 Lisp_Object plist;
191 INTERVAL i;
192{
193 register Lisp_Object tail1, tail2, sym1, sym2;
194 register int found;
195
196 /* Go through each element of PLIST. */
197 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
198 {
199 sym1 = Fcar (tail1);
200 found = 0;
201
202 /* Go through I's plist, looking for sym1 */
203 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
204 if (EQ (sym1, Fcar (tail2)))
205 {
206 /* Found the same property on both lists. If the
207 values are unequal, return zero. */
734c51b2 208 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
d418ef42
JA
209 return 0;
210
211 /* Property has same value on both lists; go to next one. */
212 found = 1;
213 break;
214 }
215
216 if (! found)
217 return 0;
218 }
219
220 return 1;
221}
222
223/* Return nonzero if the plist of interval I has any of the
224 properties of PLIST, regardless of their values. */
225
226static INLINE int
227interval_has_some_properties (plist, i)
228 Lisp_Object plist;
229 INTERVAL i;
230{
231 register Lisp_Object tail1, tail2, sym;
232
233 /* Go through each element of PLIST. */
234 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
235 {
236 sym = Fcar (tail1);
237
238 /* Go through i's plist, looking for tail1 */
239 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
240 if (EQ (sym, Fcar (tail2)))
241 return 1;
242 }
243
244 return 0;
245}
d4b530ad 246\f
d7b4e137
JB
247/* Changing the plists of individual intervals. */
248
249/* Return the value of PROP in property-list PLIST, or Qunbound if it
250 has none. */
251static int
252property_value (plist, prop)
253{
254 Lisp_Object value;
255
256 while (PLIST_ELT_P (plist, value))
257 if (EQ (XCONS (plist)->car, prop))
258 return XCONS (value)->car;
259 else
260 plist = XCONS (value)->cdr;
261
262 return Qunbound;
263}
264
d4b530ad
RS
265/* Set the properties of INTERVAL to PROPERTIES,
266 and record undo info for the previous values.
267 OBJECT is the string or buffer that INTERVAL belongs to. */
268
269static void
270set_properties (properties, interval, object)
271 Lisp_Object properties, object;
272 INTERVAL interval;
273{
d7b4e137 274 Lisp_Object sym, value;
d4b530ad 275
d7b4e137 276 if (BUFFERP (object))
d4b530ad 277 {
d7b4e137
JB
278 /* For each property in the old plist which is missing from PROPERTIES,
279 or has a different value in PROPERTIES, make an undo record. */
280 for (sym = interval->plist;
281 PLIST_ELT_P (sym, value);
282 sym = XCONS (value)->cdr)
283 if (! EQ (property_value (properties, XCONS (sym)->car),
284 XCONS (value)->car))
f7a9275a
RS
285 {
286 modify_region (XBUFFER (object),
287 make_number (interval->position),
288 make_number (interval->position + LENGTH (interval)));
289 record_property_change (interval->position, LENGTH (interval),
290 XCONS (sym)->car, XCONS (value)->car,
291 object);
292 }
d7b4e137
JB
293
294 /* For each new property that has no value at all in the old plist,
295 make an undo record binding it to nil, so it will be removed. */
296 for (sym = properties;
297 PLIST_ELT_P (sym, value);
298 sym = XCONS (value)->cdr)
299 if (EQ (property_value (interval->plist, XCONS (sym)->car), Qunbound))
f7a9275a
RS
300 {
301 modify_region (XBUFFER (object),
302 make_number (interval->position),
303 make_number (interval->position + LENGTH (interval)));
304 record_property_change (interval->position, LENGTH (interval),
305 XCONS (sym)->car, Qnil,
306 object);
307 }
d4b530ad
RS
308 }
309
310 /* Store new properties. */
311 interval->plist = Fcopy_sequence (properties);
312}
d418ef42
JA
313
314/* Add the properties of PLIST to the interval I, or set
315 the value of I's property to the value of the property on PLIST
316 if they are different.
317
d4b530ad
RS
318 OBJECT should be the string or buffer the interval is in.
319
d418ef42
JA
320 Return nonzero if this changes I (i.e., if any members of PLIST
321 are actually added to I's plist) */
322
d4b530ad
RS
323static int
324add_properties (plist, i, object)
d418ef42
JA
325 Lisp_Object plist;
326 INTERVAL i;
d4b530ad 327 Lisp_Object object;
d418ef42
JA
328{
329 register Lisp_Object tail1, tail2, sym1, val1;
330 register int changed = 0;
331 register int found;
332
333 /* Go through each element of PLIST. */
334 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
335 {
336 sym1 = Fcar (tail1);
337 val1 = Fcar (Fcdr (tail1));
338 found = 0;
339
340 /* Go through I's plist, looking for sym1 */
341 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
342 if (EQ (sym1, Fcar (tail2)))
343 {
3814ccf5 344 register Lisp_Object this_cdr;
d418ef42 345
3814ccf5 346 this_cdr = Fcdr (tail2);
d418ef42
JA
347 /* Found the property. Now check its value. */
348 found = 1;
349
350 /* The properties have the same value on both lists.
351 Continue to the next property. */
734c51b2 352 if (EQ (val1, Fcar (this_cdr)))
d418ef42
JA
353 break;
354
d4b530ad
RS
355 /* Record this change in the buffer, for undo purposes. */
356 if (XTYPE (object) == Lisp_Buffer)
357 {
04a759c8
JB
358 modify_region (XBUFFER (object),
359 make_number (i->position),
d4b530ad 360 make_number (i->position + LENGTH (i)));
f7a9275a
RS
361 record_property_change (i->position, LENGTH (i),
362 sym1, Fcar (this_cdr), object);
d4b530ad
RS
363 }
364
d418ef42
JA
365 /* I's property has a different value -- change it */
366 Fsetcar (this_cdr, val1);
367 changed++;
368 break;
369 }
370
371 if (! found)
372 {
d4b530ad
RS
373 /* Record this change in the buffer, for undo purposes. */
374 if (XTYPE (object) == Lisp_Buffer)
375 {
04a759c8
JB
376 modify_region (XBUFFER (object),
377 make_number (i->position),
d4b530ad 378 make_number (i->position + LENGTH (i)));
f7a9275a
RS
379 record_property_change (i->position, LENGTH (i),
380 sym1, Qnil, object);
d4b530ad 381 }
d418ef42
JA
382 i->plist = Fcons (sym1, Fcons (val1, i->plist));
383 changed++;
384 }
385 }
386
387 return changed;
388}
389
390/* For any members of PLIST which are properties of I, remove them
d4b530ad
RS
391 from I's plist.
392 OBJECT is the string or buffer containing I. */
d418ef42 393
d4b530ad
RS
394static int
395remove_properties (plist, i, object)
d418ef42
JA
396 Lisp_Object plist;
397 INTERVAL i;
d4b530ad 398 Lisp_Object object;
d418ef42 399{
3814ccf5 400 register Lisp_Object tail1, tail2, sym, current_plist;
d418ef42
JA
401 register int changed = 0;
402
3814ccf5 403 current_plist = i->plist;
d418ef42
JA
404 /* Go through each element of plist. */
405 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
406 {
407 sym = Fcar (tail1);
408
409 /* First, remove the symbol if its at the head of the list */
410 while (! NILP (current_plist) && EQ (sym, Fcar (current_plist)))
411 {
d4b530ad
RS
412 if (XTYPE (object) == Lisp_Buffer)
413 {
04a759c8
JB
414 modify_region (XBUFFER (object),
415 make_number (i->position),
d4b530ad 416 make_number (i->position + LENGTH (i)));
f7a9275a
RS
417 record_property_change (i->position, LENGTH (i),
418 sym, Fcar (Fcdr (current_plist)),
419 object);
d4b530ad
RS
420 }
421
d418ef42
JA
422 current_plist = Fcdr (Fcdr (current_plist));
423 changed++;
424 }
425
426 /* Go through i's plist, looking for sym */
427 tail2 = current_plist;
428 while (! NILP (tail2))
429 {
3814ccf5
KH
430 register Lisp_Object this;
431 this = Fcdr (Fcdr (tail2));
d418ef42
JA
432 if (EQ (sym, Fcar (this)))
433 {
d4b530ad
RS
434 if (XTYPE (object) == Lisp_Buffer)
435 {
04a759c8
JB
436 modify_region (XBUFFER (object),
437 make_number (i->position),
d4b530ad 438 make_number (i->position + LENGTH (i)));
f7a9275a
RS
439 record_property_change (i->position, LENGTH (i),
440 sym, Fcar (Fcdr (this)), object);
d4b530ad
RS
441 }
442
d418ef42
JA
443 Fsetcdr (Fcdr (tail2), Fcdr (Fcdr (this)));
444 changed++;
445 }
446 tail2 = this;
447 }
448 }
449
450 if (changed)
451 i->plist = current_plist;
452 return changed;
453}
454
d4b530ad 455#if 0
d418ef42
JA
456/* Remove all properties from interval I. Return non-zero
457 if this changes the interval. */
458
459static INLINE int
460erase_properties (i)
461 INTERVAL i;
462{
463 if (NILP (i->plist))
464 return 0;
465
466 i->plist = Qnil;
467 return 1;
468}
d4b530ad 469#endif
d418ef42 470\f
d418ef42
JA
471DEFUN ("text-properties-at", Ftext_properties_at,
472 Stext_properties_at, 1, 2, 0,
473 "Return the list of properties held by the character at POSITION\n\
474in optional argument OBJECT, a string or buffer. If nil, OBJECT\n\
d4b530ad
RS
475defaults to the current buffer.\n\
476If POSITION is at the end of OBJECT, the value is nil.")
d418ef42
JA
477 (pos, object)
478 Lisp_Object pos, object;
479{
480 register INTERVAL i;
d418ef42
JA
481
482 if (NILP (object))
483 XSET (object, Lisp_Buffer, current_buffer);
484
485 i = validate_interval_range (object, &pos, &pos, soft);
486 if (NULL_INTERVAL_P (i))
487 return Qnil;
d4b530ad
RS
488 /* If POS is at the end of the interval,
489 it means it's the end of OBJECT.
490 There are no properties at the very end,
491 since no character follows. */
492 if (XINT (pos) == LENGTH (i) + i->position)
493 return Qnil;
d418ef42
JA
494
495 return i->plist;
496}
497
5fbe2a44 498DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
0df58c91 499 "Return the value of position POS's property PROP, in OBJECT.\n\
d4b530ad
RS
500OBJECT is optional and defaults to the current buffer.\n\
501If POSITION is at the end of OBJECT, the value is nil.")
5fbe2a44 502 (pos, prop, object)
0df58c91 503 Lisp_Object pos, object;
5fbe2a44
RS
504 register Lisp_Object prop;
505{
506 register INTERVAL i;
507 register Lisp_Object tail;
508
509 if (NILP (object))
510 XSET (object, Lisp_Buffer, current_buffer);
5fbe2a44
RS
511 i = validate_interval_range (object, &pos, &pos, soft);
512 if (NULL_INTERVAL_P (i))
513 return Qnil;
514
d4b530ad
RS
515 /* If POS is at the end of the interval,
516 it means it's the end of OBJECT.
517 There are no properties at the very end,
518 since no character follows. */
519 if (XINT (pos) == LENGTH (i) + i->position)
520 return Qnil;
521
dc70cea7 522 return textget (i->plist, prop);
5fbe2a44
RS
523}
524
f5957179
KH
525DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
526 "Return the value of position POS's property PROP, in OBJECT.\n\
527OBJECT is optional and defaults to the current buffer.\n\
528If POSITION is at the end of OBJECT, the value is nil.\n\
529If OBJECT is a buffer, then overlay properties are considered as well as\n\
99830d63
KH
530text properties.\n\
531If OBJECT is a window, then that window's buffer is used, but window-specific\n\
f5957179
KH
532overlays are considered only if they are associated with OBJECT.")
533 (pos, prop, object)
534 Lisp_Object pos, object;
535 register Lisp_Object prop;
536{
537 struct window *w = 0;
538
539 CHECK_NUMBER_COERCE_MARKER (pos, 0);
540
541 if (NILP (object))
542 XSET (object, Lisp_Buffer, current_buffer);
543
544 if (WINDOWP (object))
545 {
546 w = XWINDOW (object);
547 XSET (object, Lisp_Buffer, w->buffer);
548 }
549 if (BUFFERP (object))
550 {
551 int posn = XINT (pos);
552 int noverlays;
553 Lisp_Object *overlay_vec, tem;
554 int next_overlay;
555 int len;
556
557 /* First try with room for 40 overlays. */
558 len = 40;
559 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
560
561 noverlays = overlays_at (posn, 0, &overlay_vec, &len, &next_overlay);
562
563 /* If there are more than 40,
564 make enough space for all, and try again. */
565 if (noverlays > len)
566 {
567 len = noverlays;
568 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
569 noverlays = overlays_at (posn, 0, &overlay_vec, &len, &next_overlay);
570 }
571 noverlays = sort_overlays (overlay_vec, noverlays, w);
572
573 /* Now check the overlays in order of decreasing priority. */
574 while (--noverlays >= 0)
575 {
576 tem = Foverlay_get (overlay_vec[noverlays], prop);
577 if (!NILP (tem))
578 return (tem);
579 }
580 }
581 /* Not a buffer, or no appropriate overlay, so fall through to the
582 simpler case. */
583 return (Fget_text_property (pos, prop, object));
584}
585
d418ef42 586DEFUN ("next-property-change", Fnext_property_change,
111b637d 587 Snext_property_change, 1, 3, 0,
5fbe2a44
RS
588 "Return the position of next property change.\n\
589Scans characters forward from POS in OBJECT till it finds\n\
590a change in some text property, then returns the position of the change.\n\
591The optional second argument OBJECT is the string or buffer to scan.\n\
592Return nil if the property is constant all the way to the end of OBJECT.\n\
111b637d
RS
593If the value is non-nil, it is a position greater than POS, never equal.\n\n\
594If the optional third argument LIMIT is non-nil, don't search\n\
595past position LIMIT; return LIMIT if nothing is found before LIMIT.")
596 (pos, object, limit)
597 Lisp_Object pos, object, limit;
d418ef42
JA
598{
599 register INTERVAL i, next;
600
5fbe2a44
RS
601 if (NILP (object))
602 XSET (object, Lisp_Buffer, current_buffer);
603
d418ef42
JA
604 i = validate_interval_range (object, &pos, &pos, soft);
605 if (NULL_INTERVAL_P (i))
111b637d 606 return limit;
d418ef42
JA
607
608 next = next_interval (i);
111b637d
RS
609 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next)
610 && (NILP (limit) || next->position < XFASTINT (limit)))
d418ef42
JA
611 next = next_interval (next);
612
613 if (NULL_INTERVAL_P (next))
111b637d
RS
614 return limit;
615 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
616 return limit;
d418ef42 617
d4b530ad 618 return next->position - (XTYPE (object) == Lisp_String);
19e1c426
RS
619}
620
621/* Return 1 if there's a change in some property between BEG and END. */
622
623int
624property_change_between_p (beg, end)
625 int beg, end;
626{
627 register INTERVAL i, next;
628 Lisp_Object object, pos;
629
630 XSET (object, Lisp_Buffer, current_buffer);
631 XFASTINT (pos) = beg;
632
633 i = validate_interval_range (object, &pos, &pos, soft);
634 if (NULL_INTERVAL_P (i))
635 return 0;
636
637 next = next_interval (i);
638 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
639 {
640 next = next_interval (next);
e050ef74
RS
641 if (NULL_INTERVAL_P (next))
642 return 0;
19e1c426
RS
643 if (next->position >= end)
644 return 0;
645 }
646
647 if (NULL_INTERVAL_P (next))
648 return 0;
649
650 return 1;
d418ef42
JA
651}
652
9c79dd1b 653DEFUN ("next-single-property-change", Fnext_single_property_change,
111b637d 654 Snext_single_property_change, 2, 4, 0,
5fbe2a44
RS
655 "Return the position of next property change for a specific property.\n\
656Scans characters forward from POS till it finds\n\
657a change in the PROP property, then returns the position of the change.\n\
658The optional third argument OBJECT is the string or buffer to scan.\n\
da625a3c 659The property values are compared with `eq'.\n\
5fbe2a44 660Return nil if the property is constant all the way to the end of OBJECT.\n\
111b637d
RS
661If the value is non-nil, it is a position greater than POS, never equal.\n\n\
662If the optional fourth argument LIMIT is non-nil, don't search\n\
5abb9556 663past position LIMIT; return LIMIT if nothing is found before LIMIT.")
111b637d
RS
664 (pos, prop, object, limit)
665 Lisp_Object pos, prop, object, limit;
9c79dd1b
JA
666{
667 register INTERVAL i, next;
668 register Lisp_Object here_val;
669
5fbe2a44
RS
670 if (NILP (object))
671 XSET (object, Lisp_Buffer, current_buffer);
672
9c79dd1b
JA
673 i = validate_interval_range (object, &pos, &pos, soft);
674 if (NULL_INTERVAL_P (i))
111b637d 675 return limit;
9c79dd1b 676
6a0486dd 677 here_val = textget (i->plist, prop);
9c79dd1b 678 next = next_interval (i);
6a0486dd 679 while (! NULL_INTERVAL_P (next)
111b637d
RS
680 && EQ (here_val, textget (next->plist, prop))
681 && (NILP (limit) || next->position < XFASTINT (limit)))
9c79dd1b
JA
682 next = next_interval (next);
683
684 if (NULL_INTERVAL_P (next))
111b637d
RS
685 return limit;
686 if (! NILP (limit) && !(next->position < XFASTINT (limit)))
687 return limit;
9c79dd1b 688
d4b530ad 689 return next->position - (XTYPE (object) == Lisp_String);
9c79dd1b
JA
690}
691
d418ef42 692DEFUN ("previous-property-change", Fprevious_property_change,
111b637d 693 Sprevious_property_change, 1, 3, 0,
5fbe2a44
RS
694 "Return the position of previous property change.\n\
695Scans characters backwards from POS in OBJECT till it finds\n\
696a change in some text property, then returns the position of the change.\n\
697The optional second argument OBJECT is the string or buffer to scan.\n\
698Return nil if the property is constant all the way to the start of OBJECT.\n\
111b637d
RS
699If the value is non-nil, it is a position less than POS, never equal.\n\n\
700If the optional third argument LIMIT is non-nil, don't search\n\
5abb9556 701back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
111b637d
RS
702 (pos, object, limit)
703 Lisp_Object pos, object, limit;
d418ef42
JA
704{
705 register INTERVAL i, previous;
706
5fbe2a44
RS
707 if (NILP (object))
708 XSET (object, Lisp_Buffer, current_buffer);
709
d418ef42
JA
710 i = validate_interval_range (object, &pos, &pos, soft);
711 if (NULL_INTERVAL_P (i))
111b637d 712 return limit;
d418ef42 713
53b7feec
RS
714 /* Start with the interval containing the char before point. */
715 if (i->position == XFASTINT (pos))
716 i = previous_interval (i);
717
d418ef42 718 previous = previous_interval (i);
111b637d
RS
719 while (! NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
720 && (NILP (limit)
721 || previous->position + LENGTH (previous) > XFASTINT (limit)))
d418ef42
JA
722 previous = previous_interval (previous);
723 if (NULL_INTERVAL_P (previous))
111b637d
RS
724 return limit;
725 if (!NILP (limit)
726 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
727 return limit;
d418ef42 728
111b637d 729 return (previous->position + LENGTH (previous)
d4b530ad 730 - (XTYPE (object) == Lisp_String));
d418ef42
JA
731}
732
9c79dd1b 733DEFUN ("previous-single-property-change", Fprevious_single_property_change,
111b637d 734 Sprevious_single_property_change, 2, 4, 0,
5fbe2a44
RS
735 "Return the position of previous property change for a specific property.\n\
736Scans characters backward from POS till it finds\n\
737a change in the PROP property, then returns the position of the change.\n\
738The optional third argument OBJECT is the string or buffer to scan.\n\
93fda178 739The property values are compared with `eq'.\n\
5fbe2a44 740Return nil if the property is constant all the way to the start of OBJECT.\n\
111b637d
RS
741If the value is non-nil, it is a position less than POS, never equal.\n\n\
742If the optional fourth argument LIMIT is non-nil, don't search\n\
5abb9556 743back past position LIMIT; return LIMIT if nothing is found until LIMIT.")
111b637d
RS
744 (pos, prop, object, limit)
745 Lisp_Object pos, prop, object, limit;
9c79dd1b
JA
746{
747 register INTERVAL i, previous;
748 register Lisp_Object here_val;
749
5fbe2a44
RS
750 if (NILP (object))
751 XSET (object, Lisp_Buffer, current_buffer);
752
9c79dd1b
JA
753 i = validate_interval_range (object, &pos, &pos, soft);
754 if (NULL_INTERVAL_P (i))
111b637d 755 return limit;
9c79dd1b 756
53b7feec
RS
757 /* Start with the interval containing the char before point. */
758 if (i->position == XFASTINT (pos))
759 i = previous_interval (i);
760
6a0486dd 761 here_val = textget (i->plist, prop);
9c79dd1b
JA
762 previous = previous_interval (i);
763 while (! NULL_INTERVAL_P (previous)
111b637d
RS
764 && EQ (here_val, textget (previous->plist, prop))
765 && (NILP (limit)
766 || previous->position + LENGTH (previous) > XFASTINT (limit)))
9c79dd1b
JA
767 previous = previous_interval (previous);
768 if (NULL_INTERVAL_P (previous))
111b637d
RS
769 return limit;
770 if (!NILP (limit)
771 && !(previous->position + LENGTH (previous) > XFASTINT (limit)))
772 return limit;
9c79dd1b 773
111b637d 774 return (previous->position + LENGTH (previous)
d4b530ad 775 - (XTYPE (object) == Lisp_String));
9c79dd1b
JA
776}
777
d418ef42 778DEFUN ("add-text-properties", Fadd_text_properties,
5fbe2a44
RS
779 Sadd_text_properties, 3, 4, 0,
780 "Add properties to the text from START to END.\n\
781The third argument PROPS is a property list\n\
782specifying the property values to add.\n\
783The optional fourth argument, OBJECT,\n\
784is the string or buffer containing the text.\n\
785Return t if any property value actually changed, nil otherwise.")
786 (start, end, properties, object)
787 Lisp_Object start, end, properties, object;
d418ef42
JA
788{
789 register INTERVAL i, unchanged;
caa31568 790 register int s, len, modified = 0;
d418ef42
JA
791
792 properties = validate_plist (properties);
793 if (NILP (properties))
794 return Qnil;
795
5fbe2a44
RS
796 if (NILP (object))
797 XSET (object, Lisp_Buffer, current_buffer);
798
d418ef42
JA
799 i = validate_interval_range (object, &start, &end, hard);
800 if (NULL_INTERVAL_P (i))
801 return Qnil;
802
803 s = XINT (start);
804 len = XINT (end) - s;
805
806 /* If we're not starting on an interval boundary, we have to
807 split this interval. */
808 if (i->position != s)
809 {
810 /* If this interval already has the properties, we can
811 skip it. */
812 if (interval_has_all_properties (properties, i))
813 {
814 int got = (LENGTH (i) - (s - i->position));
815 if (got >= len)
816 return Qnil;
817 len -= got;
05d5b93e 818 i = next_interval (i);
d418ef42
JA
819 }
820 else
821 {
822 unchanged = i;
ad9c1940 823 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 824 copy_properties (unchanged, i);
d418ef42
JA
825 }
826 }
827
daa5e28f 828 /* We are at the beginning of interval I, with LEN chars to scan. */
caa31568 829 for (;;)
d418ef42 830 {
d4b530ad
RS
831 if (i == 0)
832 abort ();
833
d418ef42
JA
834 if (LENGTH (i) >= len)
835 {
836 if (interval_has_all_properties (properties, i))
837 return modified ? Qt : Qnil;
838
839 if (LENGTH (i) == len)
840 {
d4b530ad 841 add_properties (properties, i, object);
d418ef42
JA
842 return Qt;
843 }
844
845 /* i doesn't have the properties, and goes past the change limit */
846 unchanged = i;
ad9c1940 847 i = split_interval_left (unchanged, len);
d418ef42 848 copy_properties (unchanged, i);
d4b530ad 849 add_properties (properties, i, object);
d418ef42
JA
850 return Qt;
851 }
852
853 len -= LENGTH (i);
d4b530ad 854 modified += add_properties (properties, i, object);
d418ef42
JA
855 i = next_interval (i);
856 }
857}
858
d4b530ad
RS
859DEFUN ("put-text-property", Fput_text_property,
860 Sput_text_property, 4, 5, 0,
861 "Set one property of the text from START to END.\n\
862The third and fourth arguments PROP and VALUE\n\
863specify the property to add.\n\
864The optional fifth argument, OBJECT,\n\
865is the string or buffer containing the text.")
866 (start, end, prop, value, object)
867 Lisp_Object start, end, prop, value, object;
868{
869 Fadd_text_properties (start, end,
870 Fcons (prop, Fcons (value, Qnil)),
871 object);
872 return Qnil;
873}
874
d418ef42 875DEFUN ("set-text-properties", Fset_text_properties,
5fbe2a44
RS
876 Sset_text_properties, 3, 4, 0,
877 "Completely replace properties of text from START to END.\n\
878The third argument PROPS is the new property list.\n\
879The optional fourth argument, OBJECT,\n\
880is the string or buffer containing the text.")
881 (start, end, props, object)
882 Lisp_Object start, end, props, object;
d418ef42
JA
883{
884 register INTERVAL i, unchanged;
9c79dd1b 885 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42
JA
886 register int s, len;
887
5fbe2a44 888 props = validate_plist (props);
d418ef42 889
5fbe2a44
RS
890 if (NILP (object))
891 XSET (object, Lisp_Buffer, current_buffer);
892
d418ef42
JA
893 i = validate_interval_range (object, &start, &end, hard);
894 if (NULL_INTERVAL_P (i))
895 return Qnil;
896
897 s = XINT (start);
898 len = XINT (end) - s;
899
900 if (i->position != s)
901 {
902 unchanged = i;
ad9c1940 903 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 904
d418ef42
JA
905 if (LENGTH (i) > len)
906 {
9c79dd1b 907 copy_properties (unchanged, i);
ad9c1940 908 i = split_interval_left (i, len);
daa5e28f 909 set_properties (props, i, object);
d418ef42
JA
910 return Qt;
911 }
912
daa5e28f
RS
913 set_properties (props, i, object);
914
9c79dd1b
JA
915 if (LENGTH (i) == len)
916 return Qt;
917
918 prev_changed = i;
d418ef42
JA
919 len -= LENGTH (i);
920 i = next_interval (i);
921 }
922
cd7d971d 923 /* We are starting at the beginning of an interval, I */
7855e674 924 while (len > 0)
d418ef42 925 {
d4b530ad
RS
926 if (i == 0)
927 abort ();
928
d418ef42
JA
929 if (LENGTH (i) >= len)
930 {
cd7d971d 931 if (LENGTH (i) > len)
ad9c1940 932 i = split_interval_left (i, len);
d418ef42 933
9c79dd1b 934 if (NULL_INTERVAL_P (prev_changed))
d4b530ad 935 set_properties (props, i, object);
9c79dd1b
JA
936 else
937 merge_interval_left (i);
d418ef42
JA
938 return Qt;
939 }
940
941 len -= LENGTH (i);
9c79dd1b
JA
942 if (NULL_INTERVAL_P (prev_changed))
943 {
d4b530ad 944 set_properties (props, i, object);
9c79dd1b
JA
945 prev_changed = i;
946 }
947 else
948 prev_changed = i = merge_interval_left (i);
949
d418ef42
JA
950 i = next_interval (i);
951 }
952
953 return Qt;
954}
955
956DEFUN ("remove-text-properties", Fremove_text_properties,
5fbe2a44
RS
957 Sremove_text_properties, 3, 4, 0,
958 "Remove some properties from text from START to END.\n\
959The third argument PROPS is a property list\n\
960whose property names specify the properties to remove.\n\
961\(The values stored in PROPS are ignored.)\n\
962The optional fourth argument, OBJECT,\n\
963is the string or buffer containing the text.\n\
964Return t if any property was actually removed, nil otherwise.")
965 (start, end, props, object)
966 Lisp_Object start, end, props, object;
d418ef42
JA
967{
968 register INTERVAL i, unchanged;
caa31568 969 register int s, len, modified = 0;
d418ef42 970
5fbe2a44
RS
971 if (NILP (object))
972 XSET (object, Lisp_Buffer, current_buffer);
973
d418ef42
JA
974 i = validate_interval_range (object, &start, &end, soft);
975 if (NULL_INTERVAL_P (i))
976 return Qnil;
977
978 s = XINT (start);
979 len = XINT (end) - s;
9c79dd1b 980
d418ef42
JA
981 if (i->position != s)
982 {
983 /* No properties on this first interval -- return if
984 it covers the entire region. */
5fbe2a44 985 if (! interval_has_some_properties (props, i))
d418ef42
JA
986 {
987 int got = (LENGTH (i) - (s - i->position));
988 if (got >= len)
989 return Qnil;
990 len -= got;
05d5b93e 991 i = next_interval (i);
d418ef42 992 }
daa5e28f
RS
993 /* Split away the beginning of this interval; what we don't
994 want to modify. */
d418ef42
JA
995 else
996 {
997 unchanged = i;
ad9c1940 998 i = split_interval_right (unchanged, s - unchanged->position);
d418ef42 999 copy_properties (unchanged, i);
d418ef42
JA
1000 }
1001 }
1002
1003 /* We are at the beginning of an interval, with len to scan */
caa31568 1004 for (;;)
d418ef42 1005 {
d4b530ad
RS
1006 if (i == 0)
1007 abort ();
1008
d418ef42
JA
1009 if (LENGTH (i) >= len)
1010 {
5fbe2a44 1011 if (! interval_has_some_properties (props, i))
d418ef42
JA
1012 return modified ? Qt : Qnil;
1013
1014 if (LENGTH (i) == len)
1015 {
d4b530ad 1016 remove_properties (props, i, object);
d418ef42
JA
1017 return Qt;
1018 }
1019
1020 /* i has the properties, and goes past the change limit */
daa5e28f 1021 unchanged = i;
ad9c1940 1022 i = split_interval_left (i, len);
d418ef42 1023 copy_properties (unchanged, i);
d4b530ad 1024 remove_properties (props, i, object);
d418ef42
JA
1025 return Qt;
1026 }
1027
1028 len -= LENGTH (i);
d4b530ad 1029 modified += remove_properties (props, i, object);
d418ef42
JA
1030 i = next_interval (i);
1031 }
1032}
1033
ad9c1940
JB
1034DEFUN ("text-property-any", Ftext_property_any,
1035 Stext_property_any, 4, 5, 0,
1036 "Check text from START to END to see if PROP is ever `eq' to VALUE.\n\
1037If so, return the position of the first character whose PROP is `eq'\n\
1038to VALUE. Otherwise return nil.\n\
1039The optional fifth argument, OBJECT, is the string or buffer\n\
1040containing the text.")
1041 (start, end, prop, value, object)
1042 Lisp_Object start, end, prop, value, object;
1043{
1044 register INTERVAL i;
1045 register int e, pos;
1046
1047 if (NILP (object))
1048 XSET (object, Lisp_Buffer, current_buffer);
1049 i = validate_interval_range (object, &start, &end, soft);
1050 e = XINT (end);
1051
1052 while (! NULL_INTERVAL_P (i))
1053 {
1054 if (i->position >= e)
1055 break;
1056 if (EQ (textget (i->plist, prop), value))
1057 {
1058 pos = i->position;
1059 if (pos < XINT (start))
1060 pos = XINT (start);
1061 return make_number (pos - (XTYPE (object) == Lisp_String));
1062 }
1063 i = next_interval (i);
1064 }
1065 return Qnil;
1066}
1067
1068DEFUN ("text-property-not-all", Ftext_property_not_all,
1069 Stext_property_not_all, 4, 5, 0,
1070 "Check text from START to END to see if PROP is ever not `eq' to VALUE.\n\
1071If so, return the position of the first character whose PROP is not\n\
1072`eq' to VALUE. Otherwise, return nil.\n\
1073The optional fifth argument, OBJECT, is the string or buffer\n\
1074containing the text.")
1075 (start, end, prop, value, object)
1076 Lisp_Object start, end, prop, value, object;
1077{
1078 register INTERVAL i;
1079 register int s, e;
1080
1081 if (NILP (object))
1082 XSET (object, Lisp_Buffer, current_buffer);
1083 i = validate_interval_range (object, &start, &end, soft);
1084 if (NULL_INTERVAL_P (i))
916a3119 1085 return (NILP (value) || EQ (start, end)) ? Qnil : start;
ad9c1940
JB
1086 s = XINT (start);
1087 e = XINT (end);
1088
1089 while (! NULL_INTERVAL_P (i))
1090 {
1091 if (i->position >= e)
1092 break;
1093 if (! EQ (textget (i->plist, prop), value))
1094 {
1095 if (i->position > s)
1096 s = i->position;
1097 return make_number (s - (XTYPE (object) == Lisp_String));
1098 }
1099 i = next_interval (i);
1100 }
1101 return Qnil;
1102}
1103
5fbe2a44
RS
1104#if 0 /* You can use set-text-properties for this. */
1105
d418ef42 1106DEFUN ("erase-text-properties", Ferase_text_properties,
5fbe2a44
RS
1107 Serase_text_properties, 2, 3, 0,
1108 "Remove all properties from the text from START to END.\n\
1109The optional third argument, OBJECT,\n\
1110is the string or buffer containing the text.")
1111 (start, end, object)
1112 Lisp_Object start, end, object;
d418ef42 1113{
cd7d971d 1114 register INTERVAL i;
03ad6beb 1115 register INTERVAL prev_changed = NULL_INTERVAL;
d418ef42
JA
1116 register int s, len, modified;
1117
5fbe2a44
RS
1118 if (NILP (object))
1119 XSET (object, Lisp_Buffer, current_buffer);
1120
d418ef42
JA
1121 i = validate_interval_range (object, &start, &end, soft);
1122 if (NULL_INTERVAL_P (i))
1123 return Qnil;
1124
1125 s = XINT (start);
1126 len = XINT (end) - s;
7855e674 1127
d418ef42
JA
1128 if (i->position != s)
1129 {
7855e674 1130 register int got;
cd7d971d 1131 register INTERVAL unchanged = i;
d418ef42 1132
7855e674 1133 /* If there are properties here, then this text will be modified. */
cd7d971d 1134 if (! NILP (i->plist))
d418ef42 1135 {
ad9c1940 1136 i = split_interval_right (unchanged, s - unchanged->position);
7855e674 1137 i->plist = Qnil;
d418ef42 1138 modified++;
7855e674
JA
1139
1140 if (LENGTH (i) > len)
1141 {
ad9c1940 1142 i = split_interval_right (i, len);
7855e674
JA
1143 copy_properties (unchanged, i);
1144 return Qt;
1145 }
1146
1147 if (LENGTH (i) == len)
1148 return Qt;
1149
1150 got = LENGTH (i);
d418ef42 1151 }
cd7d971d
JA
1152 /* If the text of I is without any properties, and contains
1153 LEN or more characters, then we may return without changing
1154 anything.*/
7855e674
JA
1155 else if (LENGTH (i) - (s - i->position) <= len)
1156 return Qnil;
cd7d971d
JA
1157 /* The amount of text to change extends past I, so just note
1158 how much we've gotten. */
7855e674
JA
1159 else
1160 got = LENGTH (i) - (s - i->position);
d418ef42
JA
1161
1162 len -= got;
7855e674 1163 prev_changed = i;
d418ef42
JA
1164 i = next_interval (i);
1165 }
1166
7855e674 1167 /* We are starting at the beginning of an interval, I. */
d418ef42
JA
1168 while (len > 0)
1169 {
7855e674 1170 if (LENGTH (i) >= len)
d418ef42 1171 {
cd7d971d
JA
1172 /* If I has no properties, simply merge it if possible. */
1173 if (NILP (i->plist))
7855e674
JA
1174 {
1175 if (! NULL_INTERVAL_P (prev_changed))
1176 merge_interval_left (i);
d418ef42 1177
7855e674
JA
1178 return modified ? Qt : Qnil;
1179 }
1180
cd7d971d 1181 if (LENGTH (i) > len)
ad9c1940 1182 i = split_interval_left (i, len);
7855e674
JA
1183 if (! NULL_INTERVAL_P (prev_changed))
1184 merge_interval_left (i);
cd7d971d
JA
1185 else
1186 i->plist = Qnil;
7855e674 1187
cd7d971d 1188 return Qt;
d418ef42
JA
1189 }
1190
cd7d971d 1191 /* Here if we still need to erase past the end of I */
d418ef42 1192 len -= LENGTH (i);
7855e674
JA
1193 if (NULL_INTERVAL_P (prev_changed))
1194 {
1195 modified += erase_properties (i);
1196 prev_changed = i;
1197 }
1198 else
1199 {
cd7d971d
JA
1200 modified += ! NILP (i->plist);
1201 /* Merging I will give it the properties of PREV_CHANGED. */
7855e674
JA
1202 prev_changed = i = merge_interval_left (i);
1203 }
1204
d418ef42
JA
1205 i = next_interval (i);
1206 }
1207
1208 return modified ? Qt : Qnil;
1209}
5fbe2a44 1210#endif /* 0 */
d418ef42 1211
15e4954b
JB
1212/* I don't think this is the right interface to export; how often do you
1213 want to do something like this, other than when you're copying objects
1214 around?
1215
1216 I think it would be better to have a pair of functions, one which
1217 returns the text properties of a region as a list of ranges and
1218 plists, and another which applies such a list to another object. */
1219
1220/* DEFUN ("copy-text-properties", Fcopy_text_properties,
1221 Scopy_text_properties, 5, 6, 0,
1222 "Add properties from SRC-START to SRC-END of SRC at DEST-POS of DEST.\n\
1223SRC and DEST may each refer to strings or buffers.\n\
1224Optional sixth argument PROP causes only that property to be copied.\n\
1225Properties are copied to DEST as if by `add-text-properties'.\n\
1226Return t if any property value actually changed, nil otherwise.") */
1227
1228Lisp_Object
1229copy_text_properties (start, end, src, pos, dest, prop)
1230 Lisp_Object start, end, src, pos, dest, prop;
1231{
1232 INTERVAL i;
1233 Lisp_Object res;
1234 Lisp_Object stuff;
1235 Lisp_Object plist;
1236 int s, e, e2, p, len, modified = 0;
1237
1238 i = validate_interval_range (src, &start, &end, soft);
1239 if (NULL_INTERVAL_P (i))
1240 return Qnil;
1241
1242 CHECK_NUMBER_COERCE_MARKER (pos, 0);
1243 {
1244 Lisp_Object dest_start, dest_end;
1245
1246 dest_start = pos;
1247 XFASTINT (dest_end) = XINT (dest_start) + (XINT (end) - XINT (start));
1248 /* Apply this to a copy of pos; it will try to increment its arguments,
1249 which we don't want. */
1250 validate_interval_range (dest, &dest_start, &dest_end, soft);
1251 }
1252
1253 s = XINT (start);
1254 e = XINT (end);
1255 p = XINT (pos);
1256
1257 stuff = Qnil;
1258
1259 while (s < e)
1260 {
1261 e2 = i->position + LENGTH (i);
1262 if (e2 > e)
1263 e2 = e;
1264 len = e2 - s;
1265
1266 plist = i->plist;
1267 if (! NILP (prop))
1268 while (! NILP (plist))
1269 {
1270 if (EQ (Fcar (plist), prop))
1271 {
1272 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1273 break;
1274 }
1275 plist = Fcdr (Fcdr (plist));
1276 }
1277 if (! NILP (plist))
1278 {
1279 /* Must defer modifications to the interval tree in case src
1280 and dest refer to the same string or buffer. */
1281 stuff = Fcons (Fcons (make_number (p),
1282 Fcons (make_number (p + len),
1283 Fcons (plist, Qnil))),
1284 stuff);
1285 }
1286
1287 i = next_interval (i);
1288 if (NULL_INTERVAL_P (i))
1289 break;
1290
1291 p += len;
1292 s = i->position;
1293 }
1294
1295 while (! NILP (stuff))
1296 {
1297 res = Fcar (stuff);
1298 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1299 Fcar (Fcdr (Fcdr (res))), dest);
1300 if (! NILP (res))
1301 modified++;
1302 stuff = Fcdr (stuff);
1303 }
1304
1305 return modified ? Qt : Qnil;
1306}
1307
d418ef42
JA
1308void
1309syms_of_textprop ()
1310{
1311 DEFVAR_INT ("interval-balance-threshold", &interval_balance_threshold,
c2e42adb 1312 "Threshold for rebalancing interval trees, expressed as the\n\
d418ef42
JA
1313percentage by which the left interval tree should not differ from the right.");
1314 interval_balance_threshold = 8;
1315
688a5a0f 1316 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
364a6ae3 1317 "If non-nil, don't call the text property values of\n\
688a5a0f
RS
1318`point-left' and `point-entered'.");
1319 Vinhibit_point_motion_hooks = Qnil;
1320
d418ef42
JA
1321 /* Common attributes one might give text */
1322
1323 staticpro (&Qforeground);
1324 Qforeground = intern ("foreground");
1325 staticpro (&Qbackground);
1326 Qbackground = intern ("background");
1327 staticpro (&Qfont);
1328 Qfont = intern ("font");
1329 staticpro (&Qstipple);
1330 Qstipple = intern ("stipple");
1331 staticpro (&Qunderline);
1332 Qunderline = intern ("underline");
1333 staticpro (&Qread_only);
1334 Qread_only = intern ("read-only");
1335 staticpro (&Qinvisible);
1336 Qinvisible = intern ("invisible");
19e1c426
RS
1337 staticpro (&Qhidden);
1338 Qhidden = intern ("hidden");
dc70cea7
RS
1339 staticpro (&Qcategory);
1340 Qcategory = intern ("category");
1341 staticpro (&Qlocal_map);
1342 Qlocal_map = intern ("local-map");
19e1c426
RS
1343 staticpro (&Qfront_sticky);
1344 Qfront_sticky = intern ("front-sticky");
1345 staticpro (&Qrear_nonsticky);
1346 Qrear_nonsticky = intern ("rear-nonsticky");
d418ef42
JA
1347
1348 /* Properties that text might use to specify certain actions */
1349
1350 staticpro (&Qmouse_left);
1351 Qmouse_left = intern ("mouse-left");
1352 staticpro (&Qmouse_entered);
1353 Qmouse_entered = intern ("mouse-entered");
1354 staticpro (&Qpoint_left);
1355 Qpoint_left = intern ("point-left");
1356 staticpro (&Qpoint_entered);
1357 Qpoint_entered = intern ("point-entered");
d418ef42
JA
1358
1359 defsubr (&Stext_properties_at);
5fbe2a44 1360 defsubr (&Sget_text_property);
d418ef42 1361 defsubr (&Snext_property_change);
9c79dd1b 1362 defsubr (&Snext_single_property_change);
d418ef42 1363 defsubr (&Sprevious_property_change);
9c79dd1b 1364 defsubr (&Sprevious_single_property_change);
d418ef42 1365 defsubr (&Sadd_text_properties);
d4b530ad 1366 defsubr (&Sput_text_property);
d418ef42
JA
1367 defsubr (&Sset_text_properties);
1368 defsubr (&Sremove_text_properties);
ad9c1940
JB
1369 defsubr (&Stext_property_any);
1370 defsubr (&Stext_property_not_all);
5fbe2a44 1371/* defsubr (&Serase_text_properties); */
15e4954b 1372/* defsubr (&Scopy_text_properties); */
d418ef42 1373}
25013c26
JA
1374
1375#else
1376
1377lose -- this shouldn't be compiled if USE_TEXT_PROPERTIES isn't defined
1378
1379#endif /* USE_TEXT_PROPERTIES */