(run_mac_command): Update call to openp.
[bpt/emacs.git] / src / textprop.c
1 /* Interface code for dealing with text properties.
2 Copyright (C) 1993, 1994, 1995, 1997, 1999, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs; see the file COPYING. If not, write to
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22 #include <config.h>
23 #include "lisp.h"
24 #include "intervals.h"
25 #include "buffer.h"
26 #include "window.h"
27
28 #ifndef NULL
29 #define NULL (void *)0
30 #endif
31
32 /* Test for membership, allowing for t (actually any non-cons) to mean the
33 universal set. */
34
35 #define TMEM(sym, set) (CONSP (set) ? ! NILP (Fmemq (sym, set)) : ! NILP (set))
36 \f
37
38 /* NOTES: previous- and next- property change will have to skip
39 zero-length intervals if they are implemented. This could be done
40 inside next_interval and previous_interval.
41
42 set_properties needs to deal with the interval property cache.
43
44 It is assumed that for any interval plist, a property appears
45 only once on the list. Although some code i.e., remove_properties,
46 handles the more general case, the uniqueness of properties is
47 necessary for the system to remain consistent. This requirement
48 is enforced by the subrs installing properties onto the intervals. */
49
50 \f
51 /* Types of hooks. */
52 Lisp_Object Qmouse_left;
53 Lisp_Object Qmouse_entered;
54 Lisp_Object Qpoint_left;
55 Lisp_Object Qpoint_entered;
56 Lisp_Object Qcategory;
57 Lisp_Object Qlocal_map;
58
59 /* Visual properties text (including strings) may have. */
60 Lisp_Object Qforeground, Qbackground, Qfont, Qunderline, Qstipple;
61 Lisp_Object Qinvisible, Qread_only, Qintangible, Qmouse_face;
62
63 /* Sticky properties */
64 Lisp_Object Qfront_sticky, Qrear_nonsticky;
65
66 /* If o1 is a cons whose cdr is a cons, return non-zero and set o2 to
67 the o1's cdr. Otherwise, return zero. This is handy for
68 traversing plists. */
69 #define PLIST_ELT_P(o1, o2) (CONSP (o1) && ((o2)=XCDR (o1), CONSP (o2)))
70
71 Lisp_Object Vinhibit_point_motion_hooks;
72 Lisp_Object Vdefault_text_properties;
73 Lisp_Object Vtext_property_default_nonsticky;
74
75 /* verify_interval_modification saves insertion hooks here
76 to be run later by report_interval_modification. */
77 Lisp_Object interval_insert_behind_hooks;
78 Lisp_Object interval_insert_in_front_hooks;
79
80
81 /* Signal a `text-read-only' error. This function makes it easier
82 to capture that error in GDB by putting a breakpoint on it. */
83
84 static void
85 text_read_only ()
86 {
87 Fsignal (Qtext_read_only, Qnil);
88 }
89
90
91 \f
92 /* Extract the interval at the position pointed to by BEGIN from
93 OBJECT, a string or buffer. Additionally, check that the positions
94 pointed to by BEGIN and END are within the bounds of OBJECT, and
95 reverse them if *BEGIN is greater than *END. The objects pointed
96 to by BEGIN and END may be integers or markers; if the latter, they
97 are coerced to integers.
98
99 When OBJECT is a string, we increment *BEGIN and *END
100 to make them origin-one.
101
102 Note that buffer points don't correspond to interval indices.
103 For example, point-max is 1 greater than the index of the last
104 character. This difference is handled in the caller, which uses
105 the validated points to determine a length, and operates on that.
106 Exceptions are Ftext_properties_at, Fnext_property_change, and
107 Fprevious_property_change which call this function with BEGIN == END.
108 Handle this case specially.
109
110 If FORCE is soft (0), it's OK to return NULL_INTERVAL. Otherwise,
111 create an interval tree for OBJECT if one doesn't exist, provided
112 the object actually contains text. In the current design, if there
113 is no text, there can be no text properties. */
114
115 #define soft 0
116 #define hard 1
117
118 INTERVAL
119 validate_interval_range (object, begin, end, force)
120 Lisp_Object object, *begin, *end;
121 int force;
122 {
123 register INTERVAL i;
124 int searchpos;
125
126 CHECK_STRING_OR_BUFFER (object);
127 CHECK_NUMBER_COERCE_MARKER (*begin);
128 CHECK_NUMBER_COERCE_MARKER (*end);
129
130 /* If we are asked for a point, but from a subr which operates
131 on a range, then return nothing. */
132 if (EQ (*begin, *end) && begin != end)
133 return NULL_INTERVAL;
134
135 if (XINT (*begin) > XINT (*end))
136 {
137 Lisp_Object n;
138 n = *begin;
139 *begin = *end;
140 *end = n;
141 }
142
143 if (BUFFERP (object))
144 {
145 register struct buffer *b = XBUFFER (object);
146
147 if (!(BUF_BEGV (b) <= XINT (*begin) && XINT (*begin) <= XINT (*end)
148 && XINT (*end) <= BUF_ZV (b)))
149 args_out_of_range (*begin, *end);
150 i = BUF_INTERVALS (b);
151
152 /* If there's no text, there are no properties. */
153 if (BUF_BEGV (b) == BUF_ZV (b))
154 return NULL_INTERVAL;
155
156 searchpos = XINT (*begin);
157 }
158 else
159 {
160 register struct Lisp_String *s = XSTRING (object);
161
162 if (! (0 <= XINT (*begin) && XINT (*begin) <= XINT (*end)
163 && XINT (*end) <= s->size))
164 args_out_of_range (*begin, *end);
165 XSETFASTINT (*begin, XFASTINT (*begin));
166 if (begin != end)
167 XSETFASTINT (*end, XFASTINT (*end));
168 i = s->intervals;
169
170 if (s->size == 0)
171 return NULL_INTERVAL;
172
173 searchpos = XINT (*begin);
174 }
175
176 if (NULL_INTERVAL_P (i))
177 return (force ? create_root_interval (object) : i);
178
179 return find_interval (i, searchpos);
180 }
181
182 /* Validate LIST as a property list. If LIST is not a list, then
183 make one consisting of (LIST nil). Otherwise, verify that LIST
184 is even numbered and thus suitable as a plist. */
185
186 static Lisp_Object
187 validate_plist (list)
188 Lisp_Object list;
189 {
190 if (NILP (list))
191 return Qnil;
192
193 if (CONSP (list))
194 {
195 register int i;
196 register Lisp_Object tail;
197 for (i = 0, tail = list; !NILP (tail); i++)
198 {
199 tail = Fcdr (tail);
200 QUIT;
201 }
202 if (i & 1)
203 error ("Odd length text property list");
204 return list;
205 }
206
207 return Fcons (list, Fcons (Qnil, Qnil));
208 }
209
210 /* Return nonzero if interval I has all the properties,
211 with the same values, of list PLIST. */
212
213 static int
214 interval_has_all_properties (plist, i)
215 Lisp_Object plist;
216 INTERVAL i;
217 {
218 register Lisp_Object tail1, tail2, sym1;
219 register int found;
220
221 /* Go through each element of PLIST. */
222 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
223 {
224 sym1 = Fcar (tail1);
225 found = 0;
226
227 /* Go through I's plist, looking for sym1 */
228 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
229 if (EQ (sym1, Fcar (tail2)))
230 {
231 /* Found the same property on both lists. If the
232 values are unequal, return zero. */
233 if (! EQ (Fcar (Fcdr (tail1)), Fcar (Fcdr (tail2))))
234 return 0;
235
236 /* Property has same value on both lists; go to next one. */
237 found = 1;
238 break;
239 }
240
241 if (! found)
242 return 0;
243 }
244
245 return 1;
246 }
247
248 /* Return nonzero if the plist of interval I has any of the
249 properties of PLIST, regardless of their values. */
250
251 static INLINE int
252 interval_has_some_properties (plist, i)
253 Lisp_Object plist;
254 INTERVAL i;
255 {
256 register Lisp_Object tail1, tail2, sym;
257
258 /* Go through each element of PLIST. */
259 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
260 {
261 sym = Fcar (tail1);
262
263 /* Go through i's plist, looking for tail1 */
264 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
265 if (EQ (sym, Fcar (tail2)))
266 return 1;
267 }
268
269 return 0;
270 }
271
272 /* Return nonzero if the plist of interval I has any of the
273 property names in LIST, regardless of their values. */
274
275 static INLINE int
276 interval_has_some_properties_list (list, i)
277 Lisp_Object list;
278 INTERVAL i;
279 {
280 register Lisp_Object tail1, tail2, sym;
281
282 /* Go through each element of LIST. */
283 for (tail1 = list; ! NILP (tail1); tail1 = XCDR (tail1))
284 {
285 sym = Fcar (tail1);
286
287 /* Go through i's plist, looking for tail1 */
288 for (tail2 = i->plist; ! NILP (tail2); tail2 = XCDR (XCDR (tail2)))
289 if (EQ (sym, XCAR (tail2)))
290 return 1;
291 }
292
293 return 0;
294 }
295 \f
296 /* Changing the plists of individual intervals. */
297
298 /* Return the value of PROP in property-list PLIST, or Qunbound if it
299 has none. */
300 static Lisp_Object
301 property_value (plist, prop)
302 Lisp_Object plist, prop;
303 {
304 Lisp_Object value;
305
306 while (PLIST_ELT_P (plist, value))
307 if (EQ (XCAR (plist), prop))
308 return XCAR (value);
309 else
310 plist = XCDR (value);
311
312 return Qunbound;
313 }
314
315 /* Set the properties of INTERVAL to PROPERTIES,
316 and record undo info for the previous values.
317 OBJECT is the string or buffer that INTERVAL belongs to. */
318
319 static void
320 set_properties (properties, interval, object)
321 Lisp_Object properties, object;
322 INTERVAL interval;
323 {
324 Lisp_Object sym, value;
325
326 if (BUFFERP (object))
327 {
328 /* For each property in the old plist which is missing from PROPERTIES,
329 or has a different value in PROPERTIES, make an undo record. */
330 for (sym = interval->plist;
331 PLIST_ELT_P (sym, value);
332 sym = XCDR (value))
333 if (! EQ (property_value (properties, XCAR (sym)),
334 XCAR (value)))
335 {
336 record_property_change (interval->position, LENGTH (interval),
337 XCAR (sym), XCAR (value),
338 object);
339 }
340
341 /* For each new property that has no value at all in the old plist,
342 make an undo record binding it to nil, so it will be removed. */
343 for (sym = properties;
344 PLIST_ELT_P (sym, value);
345 sym = XCDR (value))
346 if (EQ (property_value (interval->plist, XCAR (sym)), Qunbound))
347 {
348 record_property_change (interval->position, LENGTH (interval),
349 XCAR (sym), Qnil,
350 object);
351 }
352 }
353
354 /* Store new properties. */
355 interval->plist = Fcopy_sequence (properties);
356 }
357
358 /* Add the properties of PLIST to the interval I, or set
359 the value of I's property to the value of the property on PLIST
360 if they are different.
361
362 OBJECT should be the string or buffer the interval is in.
363
364 Return nonzero if this changes I (i.e., if any members of PLIST
365 are actually added to I's plist) */
366
367 static int
368 add_properties (plist, i, object)
369 Lisp_Object plist;
370 INTERVAL i;
371 Lisp_Object object;
372 {
373 Lisp_Object tail1, tail2, sym1, val1;
374 register int changed = 0;
375 register int found;
376 struct gcpro gcpro1, gcpro2, gcpro3;
377
378 tail1 = plist;
379 sym1 = Qnil;
380 val1 = Qnil;
381 /* No need to protect OBJECT, because we can GC only in the case
382 where it is a buffer, and live buffers are always protected.
383 I and its plist are also protected, via OBJECT. */
384 GCPRO3 (tail1, sym1, val1);
385
386 /* Go through each element of PLIST. */
387 for (tail1 = plist; ! NILP (tail1); tail1 = Fcdr (Fcdr (tail1)))
388 {
389 sym1 = Fcar (tail1);
390 val1 = Fcar (Fcdr (tail1));
391 found = 0;
392
393 /* Go through I's plist, looking for sym1 */
394 for (tail2 = i->plist; ! NILP (tail2); tail2 = Fcdr (Fcdr (tail2)))
395 if (EQ (sym1, Fcar (tail2)))
396 {
397 /* No need to gcpro, because tail2 protects this
398 and it must be a cons cell (we get an error otherwise). */
399 register Lisp_Object this_cdr;
400
401 this_cdr = Fcdr (tail2);
402 /* Found the property. Now check its value. */
403 found = 1;
404
405 /* The properties have the same value on both lists.
406 Continue to the next property. */
407 if (EQ (val1, Fcar (this_cdr)))
408 break;
409
410 /* Record this change in the buffer, for undo purposes. */
411 if (BUFFERP (object))
412 {
413 record_property_change (i->position, LENGTH (i),
414 sym1, Fcar (this_cdr), object);
415 }
416
417 /* I's property has a different value -- change it */
418 Fsetcar (this_cdr, val1);
419 changed++;
420 break;
421 }
422
423 if (! found)
424 {
425 /* Record this change in the buffer, for undo purposes. */
426 if (BUFFERP (object))
427 {
428 record_property_change (i->position, LENGTH (i),
429 sym1, Qnil, object);
430 }
431 i->plist = Fcons (sym1, Fcons (val1, i->plist));
432 changed++;
433 }
434 }
435
436 UNGCPRO;
437
438 return changed;
439 }
440
441 /* For any members of PLIST, or LIST,
442 which are properties of I, remove them from I's plist.
443 (If PLIST is non-nil, use that, otherwise use LIST.)
444 OBJECT is the string or buffer containing I. */
445
446 static int
447 remove_properties (plist, list, i, object)
448 Lisp_Object plist, list;
449 INTERVAL i;
450 Lisp_Object object;
451 {
452 register Lisp_Object tail1, tail2, sym, current_plist;
453 register int changed = 0;
454
455 /* Nonzero means tail1 is a plist, otherwise it is a list. */
456 int use_plist;
457
458 current_plist = i->plist;
459
460 if (! NILP (plist))
461 tail1 = plist, use_plist = 1;
462 else
463 tail1 = list, use_plist = 0;
464
465 /* Go through each element of LIST or PLIST. */
466 while (CONSP (tail1))
467 {
468 sym = XCAR (tail1);
469
470 /* First, remove the symbol if it's at the head of the list */
471 while (CONSP (current_plist) && EQ (sym, XCAR (current_plist)))
472 {
473 if (BUFFERP (object))
474 record_property_change (i->position, LENGTH (i),
475 sym, XCAR (XCDR (current_plist)),
476 object);
477
478 current_plist = XCDR (XCDR (current_plist));
479 changed++;
480 }
481
482 /* Go through I's plist, looking for SYM. */
483 tail2 = current_plist;
484 while (! NILP (tail2))
485 {
486 register Lisp_Object this;
487 this = XCDR (XCDR (tail2));
488 if (CONSP (this) && EQ (sym, XCAR (this)))
489 {
490 if (BUFFERP (object))
491 record_property_change (i->position, LENGTH (i),
492 sym, XCAR (XCDR (this)), object);
493
494 Fsetcdr (XCDR (tail2), XCDR (XCDR (this)));
495 changed++;
496 }
497 tail2 = this;
498 }
499
500 /* Advance thru TAIL1 one way or the other. */
501 tail1 = XCDR (tail1);
502 if (use_plist && CONSP (tail1))
503 tail1 = XCDR (tail1);
504 }
505
506 if (changed)
507 i->plist = current_plist;
508 return changed;
509 }
510
511 #if 0
512 /* Remove all properties from interval I. Return non-zero
513 if this changes the interval. */
514
515 static INLINE int
516 erase_properties (i)
517 INTERVAL i;
518 {
519 if (NILP (i->plist))
520 return 0;
521
522 i->plist = Qnil;
523 return 1;
524 }
525 #endif
526 \f
527 /* Returns the interval of POSITION in OBJECT.
528 POSITION is BEG-based. */
529
530 INTERVAL
531 interval_of (position, object)
532 int position;
533 Lisp_Object object;
534 {
535 register INTERVAL i;
536 int beg, end;
537
538 if (NILP (object))
539 XSETBUFFER (object, current_buffer);
540 else if (EQ (object, Qt))
541 return NULL_INTERVAL;
542
543 CHECK_STRING_OR_BUFFER (object);
544
545 if (BUFFERP (object))
546 {
547 register struct buffer *b = XBUFFER (object);
548
549 beg = BUF_BEGV (b);
550 end = BUF_ZV (b);
551 i = BUF_INTERVALS (b);
552 }
553 else
554 {
555 register struct Lisp_String *s = XSTRING (object);
556
557 beg = 0;
558 end = s->size;
559 i = s->intervals;
560 }
561
562 if (!(beg <= position && position <= end))
563 args_out_of_range (make_number (position), make_number (position));
564 if (beg == end || NULL_INTERVAL_P (i))
565 return NULL_INTERVAL;
566
567 return find_interval (i, position);
568 }
569 \f
570 DEFUN ("text-properties-at", Ftext_properties_at,
571 Stext_properties_at, 1, 2, 0,
572 doc: /* Return the list of properties of the character at POSITION in OBJECT.
573 OBJECT is the string or buffer to look for the properties in;
574 nil means the current buffer.
575 If POSITION is at the end of OBJECT, the value is nil. */)
576 (position, object)
577 Lisp_Object position, object;
578 {
579 register INTERVAL i;
580
581 if (NILP (object))
582 XSETBUFFER (object, current_buffer);
583
584 i = validate_interval_range (object, &position, &position, soft);
585 if (NULL_INTERVAL_P (i))
586 return Qnil;
587 /* If POSITION is at the end of the interval,
588 it means it's the end of OBJECT.
589 There are no properties at the very end,
590 since no character follows. */
591 if (XINT (position) == LENGTH (i) + i->position)
592 return Qnil;
593
594 return i->plist;
595 }
596
597 DEFUN ("get-text-property", Fget_text_property, Sget_text_property, 2, 3, 0,
598 doc: /* Return the value of POSITION's property PROP, in OBJECT.
599 OBJECT is optional and defaults to the current buffer.
600 If POSITION is at the end of OBJECT, the value is nil. */)
601 (position, prop, object)
602 Lisp_Object position, object;
603 Lisp_Object prop;
604 {
605 return textget (Ftext_properties_at (position, object), prop);
606 }
607
608 /* Return the value of POSITION's property PROP, in OBJECT.
609 OBJECT is optional and defaults to the current buffer.
610 If OVERLAY is non-0, then in the case that the returned property is from
611 an overlay, the overlay found is returned in *OVERLAY, otherwise nil is
612 returned in *OVERLAY.
613 If POSITION is at the end of OBJECT, the value is nil.
614 If OBJECT is a buffer, then overlay properties are considered as well as
615 text properties.
616 If OBJECT is a window, then that window's buffer is used, but
617 window-specific overlays are considered only if they are associated
618 with OBJECT. */
619 Lisp_Object
620 get_char_property_and_overlay (position, prop, object, overlay)
621 Lisp_Object position, object;
622 register Lisp_Object prop;
623 Lisp_Object *overlay;
624 {
625 struct window *w = 0;
626
627 CHECK_NUMBER_COERCE_MARKER (position);
628
629 if (NILP (object))
630 XSETBUFFER (object, current_buffer);
631
632 if (WINDOWP (object))
633 {
634 w = XWINDOW (object);
635 object = w->buffer;
636 }
637 if (BUFFERP (object))
638 {
639 int posn = XINT (position);
640 int noverlays;
641 Lisp_Object *overlay_vec, tem;
642 int next_overlay;
643 int len;
644 struct buffer *obuf = current_buffer;
645
646 set_buffer_temp (XBUFFER (object));
647
648 /* First try with room for 40 overlays. */
649 len = 40;
650 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
651
652 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
653 &next_overlay, NULL, 0);
654
655 /* If there are more than 40,
656 make enough space for all, and try again. */
657 if (noverlays > len)
658 {
659 len = noverlays;
660 overlay_vec = (Lisp_Object *) alloca (len * sizeof (Lisp_Object));
661 noverlays = overlays_at (posn, 0, &overlay_vec, &len,
662 &next_overlay, NULL, 0);
663 }
664 noverlays = sort_overlays (overlay_vec, noverlays, w);
665
666 set_buffer_temp (obuf);
667
668 /* Now check the overlays in order of decreasing priority. */
669 while (--noverlays >= 0)
670 {
671 tem = Foverlay_get (overlay_vec[noverlays], prop);
672 if (!NILP (tem))
673 {
674 if (overlay)
675 /* Return the overlay we got the property from. */
676 *overlay = overlay_vec[noverlays];
677 return tem;
678 }
679 }
680 }
681
682 if (overlay)
683 /* Indicate that the return value is not from an overlay. */
684 *overlay = Qnil;
685
686 /* Not a buffer, or no appropriate overlay, so fall through to the
687 simpler case. */
688 return Fget_text_property (position, prop, object);
689 }
690
691 DEFUN ("get-char-property", Fget_char_property, Sget_char_property, 2, 3, 0,
692 doc: /* Return the value of POSITION's property PROP, in OBJECT.
693 Both overlay properties and text properties are checked.
694 OBJECT is optional and defaults to the current buffer.
695 If POSITION is at the end of OBJECT, the value is nil.
696 If OBJECT is a buffer, then overlay properties are considered as well as
697 text properties.
698 If OBJECT is a window, then that window's buffer is used, but window-specific
699 overlays are considered only if they are associated with OBJECT. */)
700 (position, prop, object)
701 Lisp_Object position, object;
702 register Lisp_Object prop;
703 {
704 return get_char_property_and_overlay (position, prop, object, 0);
705 }
706 \f
707 DEFUN ("next-char-property-change", Fnext_char_property_change,
708 Snext_char_property_change, 1, 2, 0,
709 doc: /* Return the position of next text property or overlay change.
710 This scans characters forward from POSITION till it finds a change in
711 some text property, or the beginning or end of an overlay, and returns
712 the position of that.
713 If none is found, the function returns (point-max).
714
715 If the optional third argument LIMIT is non-nil, don't search
716 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
717 (position, limit)
718 Lisp_Object position, limit;
719 {
720 Lisp_Object temp;
721
722 temp = Fnext_overlay_change (position);
723 if (! NILP (limit))
724 {
725 CHECK_NUMBER (limit);
726 if (XINT (limit) < XINT (temp))
727 temp = limit;
728 }
729 return Fnext_property_change (position, Qnil, temp);
730 }
731
732 DEFUN ("previous-char-property-change", Fprevious_char_property_change,
733 Sprevious_char_property_change, 1, 2, 0,
734 doc: /* Return the position of previous text property or overlay change.
735 Scans characters backward from POSITION till it finds a change in some
736 text property, or the beginning or end of an overlay, and returns the
737 position of that.
738 If none is found, the function returns (point-max).
739
740 If the optional third argument LIMIT is non-nil, don't search
741 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
742 (position, limit)
743 Lisp_Object position, limit;
744 {
745 Lisp_Object temp;
746
747 temp = Fprevious_overlay_change (position);
748 if (! NILP (limit))
749 {
750 CHECK_NUMBER (limit);
751 if (XINT (limit) > XINT (temp))
752 temp = limit;
753 }
754 return Fprevious_property_change (position, Qnil, temp);
755 }
756
757
758 DEFUN ("next-single-char-property-change", Fnext_single_char_property_change,
759 Snext_single_char_property_change, 2, 4, 0,
760 doc: /* Return the position of next text property or overlay change for a specific property.
761 Scans characters forward from POSITION till it finds
762 a change in the PROP property, then returns the position of the change.
763 The optional third argument OBJECT is the string or buffer to scan.
764 The property values are compared with `eq'.
765 If the property is constant all the way to the end of OBJECT, return the
766 last valid position in OBJECT.
767 If the optional fourth argument LIMIT is non-nil, don't search
768 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
769 (position, prop, object, limit)
770 Lisp_Object prop, position, object, limit;
771 {
772 if (STRINGP (object))
773 {
774 position = Fnext_single_property_change (position, prop, object, limit);
775 if (NILP (position))
776 {
777 if (NILP (limit))
778 position = make_number (XSTRING (object)->size);
779 else
780 position = limit;
781 }
782 }
783 else
784 {
785 Lisp_Object initial_value, value;
786 int count = specpdl_ptr - specpdl;
787
788 if (! NILP (object))
789 CHECK_BUFFER (object);
790
791 if (BUFFERP (object) && current_buffer != XBUFFER (object))
792 {
793 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
794 Fset_buffer (object);
795 }
796
797 initial_value = Fget_char_property (position, prop, object);
798
799 if (NILP (limit))
800 XSETFASTINT (limit, BUF_ZV (current_buffer));
801 else
802 CHECK_NUMBER_COERCE_MARKER (limit);
803
804 for (;;)
805 {
806 position = Fnext_char_property_change (position, limit);
807 if (XFASTINT (position) >= XFASTINT (limit)) {
808 position = limit;
809 break;
810 }
811
812 value = Fget_char_property (position, prop, object);
813 if (!EQ (value, initial_value))
814 break;
815 }
816
817 unbind_to (count, Qnil);
818 }
819
820 return position;
821 }
822
823 DEFUN ("previous-single-char-property-change",
824 Fprevious_single_char_property_change,
825 Sprevious_single_char_property_change, 2, 4, 0,
826 doc: /* Return the position of previous text property or overlay change for a specific property.
827 Scans characters backward from POSITION till it finds
828 a change in the PROP property, then returns the position of the change.
829 The optional third argument OBJECT is the string or buffer to scan.
830 The property values are compared with `eq'.
831 If the property is constant all the way to the start of OBJECT, return the
832 first valid position in OBJECT.
833 If the optional fourth argument LIMIT is non-nil, don't search
834 back past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
835 (position, prop, object, limit)
836 Lisp_Object prop, position, object, limit;
837 {
838 if (STRINGP (object))
839 {
840 position = Fprevious_single_property_change (position, prop, object, limit);
841 if (NILP (position))
842 {
843 if (NILP (limit))
844 position = make_number (XSTRING (object)->size);
845 else
846 position = limit;
847 }
848 }
849 else
850 {
851 int count = specpdl_ptr - specpdl;
852
853 if (! NILP (object))
854 CHECK_BUFFER (object);
855
856 if (BUFFERP (object) && current_buffer != XBUFFER (object))
857 {
858 record_unwind_protect (Fset_buffer, Fcurrent_buffer ());
859 Fset_buffer (object);
860 }
861
862 if (NILP (limit))
863 XSETFASTINT (limit, BUF_BEGV (current_buffer));
864 else
865 CHECK_NUMBER_COERCE_MARKER (limit);
866
867 if (XFASTINT (position) <= XFASTINT (limit))
868 position = limit;
869 else
870 {
871 Lisp_Object initial_value =
872 Fget_char_property (make_number (XFASTINT (position) - 1),
873 prop, object);
874
875 for (;;)
876 {
877 position = Fprevious_char_property_change (position, limit);
878
879 if (XFASTINT (position) <= XFASTINT (limit))
880 {
881 position = limit;
882 break;
883 }
884 else
885 {
886 Lisp_Object value =
887 Fget_char_property (make_number (XFASTINT (position) - 1),
888 prop, object);
889
890 if (!EQ (value, initial_value))
891 break;
892 }
893 }
894 }
895
896 unbind_to (count, Qnil);
897 }
898
899 return position;
900 }
901 \f
902 DEFUN ("next-property-change", Fnext_property_change,
903 Snext_property_change, 1, 3, 0,
904 doc: /* Return the position of next property change.
905 Scans characters forward from POSITION in OBJECT till it finds
906 a change in some text property, then returns the position of the change.
907 The optional second argument OBJECT is the string or buffer to scan.
908 Return nil if the property is constant all the way to the end of OBJECT.
909 If the value is non-nil, it is a position greater than POSITION, never equal.
910
911 If the optional third argument LIMIT is non-nil, don't search
912 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
913 (position, object, limit)
914 Lisp_Object position, object, limit;
915 {
916 register INTERVAL i, next;
917
918 if (NILP (object))
919 XSETBUFFER (object, current_buffer);
920
921 if (!NILP (limit) && !EQ (limit, Qt))
922 CHECK_NUMBER_COERCE_MARKER (limit);
923
924 i = validate_interval_range (object, &position, &position, soft);
925
926 /* If LIMIT is t, return start of next interval--don't
927 bother checking further intervals. */
928 if (EQ (limit, Qt))
929 {
930 if (NULL_INTERVAL_P (i))
931 next = i;
932 else
933 next = next_interval (i);
934
935 if (NULL_INTERVAL_P (next))
936 XSETFASTINT (position, (STRINGP (object)
937 ? XSTRING (object)->size
938 : BUF_ZV (XBUFFER (object))));
939 else
940 XSETFASTINT (position, next->position);
941 return position;
942 }
943
944 if (NULL_INTERVAL_P (i))
945 return limit;
946
947 next = next_interval (i);
948
949 while (!NULL_INTERVAL_P (next) && intervals_equal (i, next)
950 && (NILP (limit) || next->position < XFASTINT (limit)))
951 next = next_interval (next);
952
953 if (NULL_INTERVAL_P (next))
954 return limit;
955 if (NILP (limit))
956 XSETFASTINT (limit, (STRINGP (object)
957 ? XSTRING (object)->size
958 : BUF_ZV (XBUFFER (object))));
959 if (!(next->position < XFASTINT (limit)))
960 return limit;
961
962 XSETFASTINT (position, next->position);
963 return position;
964 }
965
966 /* Return 1 if there's a change in some property between BEG and END. */
967
968 int
969 property_change_between_p (beg, end)
970 int beg, end;
971 {
972 register INTERVAL i, next;
973 Lisp_Object object, pos;
974
975 XSETBUFFER (object, current_buffer);
976 XSETFASTINT (pos, beg);
977
978 i = validate_interval_range (object, &pos, &pos, soft);
979 if (NULL_INTERVAL_P (i))
980 return 0;
981
982 next = next_interval (i);
983 while (! NULL_INTERVAL_P (next) && intervals_equal (i, next))
984 {
985 next = next_interval (next);
986 if (NULL_INTERVAL_P (next))
987 return 0;
988 if (next->position >= end)
989 return 0;
990 }
991
992 if (NULL_INTERVAL_P (next))
993 return 0;
994
995 return 1;
996 }
997
998 DEFUN ("next-single-property-change", Fnext_single_property_change,
999 Snext_single_property_change, 2, 4, 0,
1000 doc: /* Return the position of next property change for a specific property.
1001 Scans characters forward from POSITION till it finds
1002 a change in the PROP property, then returns the position of the change.
1003 The optional third argument OBJECT is the string or buffer to scan.
1004 The property values are compared with `eq'.
1005 Return nil if the property is constant all the way to the end of OBJECT.
1006 If the value is non-nil, it is a position greater than POSITION, never equal.
1007
1008 If the optional fourth argument LIMIT is non-nil, don't search
1009 past position LIMIT; return LIMIT if nothing is found before LIMIT. */)
1010 (position, prop, object, limit)
1011 Lisp_Object position, prop, object, limit;
1012 {
1013 register INTERVAL i, next;
1014 register Lisp_Object here_val;
1015
1016 if (NILP (object))
1017 XSETBUFFER (object, current_buffer);
1018
1019 if (!NILP (limit))
1020 CHECK_NUMBER_COERCE_MARKER (limit);
1021
1022 i = validate_interval_range (object, &position, &position, soft);
1023 if (NULL_INTERVAL_P (i))
1024 return limit;
1025
1026 here_val = textget (i->plist, prop);
1027 next = next_interval (i);
1028 while (! NULL_INTERVAL_P (next)
1029 && EQ (here_val, textget (next->plist, prop))
1030 && (NILP (limit) || next->position < XFASTINT (limit)))
1031 next = next_interval (next);
1032
1033 if (NULL_INTERVAL_P (next))
1034 return limit;
1035 if (NILP (limit))
1036 XSETFASTINT (limit, (STRINGP (object)
1037 ? XSTRING (object)->size
1038 : BUF_ZV (XBUFFER (object))));
1039 if (!(next->position < XFASTINT (limit)))
1040 return limit;
1041
1042 return make_number (next->position);
1043 }
1044
1045 DEFUN ("previous-property-change", Fprevious_property_change,
1046 Sprevious_property_change, 1, 3, 0,
1047 doc: /* Return the position of previous property change.
1048 Scans characters backwards from POSITION in OBJECT till it finds
1049 a change in some text property, then returns the position of the change.
1050 The optional second argument OBJECT is the string or buffer to scan.
1051 Return nil if the property is constant all the way to the start of OBJECT.
1052 If the value is non-nil, it is a position less than POSITION, never equal.
1053
1054 If the optional third argument LIMIT is non-nil, don't search
1055 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1056 (position, object, limit)
1057 Lisp_Object position, object, limit;
1058 {
1059 register INTERVAL i, previous;
1060
1061 if (NILP (object))
1062 XSETBUFFER (object, current_buffer);
1063
1064 if (!NILP (limit))
1065 CHECK_NUMBER_COERCE_MARKER (limit);
1066
1067 i = validate_interval_range (object, &position, &position, soft);
1068 if (NULL_INTERVAL_P (i))
1069 return limit;
1070
1071 /* Start with the interval containing the char before point. */
1072 if (i->position == XFASTINT (position))
1073 i = previous_interval (i);
1074
1075 previous = previous_interval (i);
1076 while (!NULL_INTERVAL_P (previous) && intervals_equal (previous, i)
1077 && (NILP (limit)
1078 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1079 previous = previous_interval (previous);
1080 if (NULL_INTERVAL_P (previous))
1081 return limit;
1082 if (NILP (limit))
1083 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1084 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1085 return limit;
1086
1087 return make_number (previous->position + LENGTH (previous));
1088 }
1089
1090 DEFUN ("previous-single-property-change", Fprevious_single_property_change,
1091 Sprevious_single_property_change, 2, 4, 0,
1092 doc: /* Return the position of previous property change for a specific property.
1093 Scans characters backward from POSITION till it finds
1094 a change in the PROP property, then returns the position of the change.
1095 The optional third argument OBJECT is the string or buffer to scan.
1096 The property values are compared with `eq'.
1097 Return nil if the property is constant all the way to the start of OBJECT.
1098 If the value is non-nil, it is a position less than POSITION, never equal.
1099
1100 If the optional fourth argument LIMIT is non-nil, don't search
1101 back past position LIMIT; return LIMIT if nothing is found until LIMIT. */)
1102 (position, prop, object, limit)
1103 Lisp_Object position, prop, object, limit;
1104 {
1105 register INTERVAL i, previous;
1106 register Lisp_Object here_val;
1107
1108 if (NILP (object))
1109 XSETBUFFER (object, current_buffer);
1110
1111 if (!NILP (limit))
1112 CHECK_NUMBER_COERCE_MARKER (limit);
1113
1114 i = validate_interval_range (object, &position, &position, soft);
1115
1116 /* Start with the interval containing the char before point. */
1117 if (!NULL_INTERVAL_P (i) && i->position == XFASTINT (position))
1118 i = previous_interval (i);
1119
1120 if (NULL_INTERVAL_P (i))
1121 return limit;
1122
1123 here_val = textget (i->plist, prop);
1124 previous = previous_interval (i);
1125 while (!NULL_INTERVAL_P (previous)
1126 && EQ (here_val, textget (previous->plist, prop))
1127 && (NILP (limit)
1128 || (previous->position + LENGTH (previous) > XFASTINT (limit))))
1129 previous = previous_interval (previous);
1130 if (NULL_INTERVAL_P (previous))
1131 return limit;
1132 if (NILP (limit))
1133 XSETFASTINT (limit, (STRINGP (object) ? 0 : BUF_BEGV (XBUFFER (object))));
1134 if (!(previous->position + LENGTH (previous) > XFASTINT (limit)))
1135 return limit;
1136
1137 return make_number (previous->position + LENGTH (previous));
1138 }
1139 \f
1140 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1141
1142 DEFUN ("add-text-properties", Fadd_text_properties,
1143 Sadd_text_properties, 3, 4, 0,
1144 doc: /* Add properties to the text from START to END.
1145 The third argument PROPERTIES is a property list
1146 specifying the property values to add.
1147 The optional fourth argument, OBJECT,
1148 is the string or buffer containing the text.
1149 Return t if any property value actually changed, nil otherwise. */)
1150 (start, end, properties, object)
1151 Lisp_Object start, end, properties, object;
1152 {
1153 register INTERVAL i, unchanged;
1154 register int s, len, modified = 0;
1155 struct gcpro gcpro1;
1156
1157 properties = validate_plist (properties);
1158 if (NILP (properties))
1159 return Qnil;
1160
1161 if (NILP (object))
1162 XSETBUFFER (object, current_buffer);
1163
1164 i = validate_interval_range (object, &start, &end, hard);
1165 if (NULL_INTERVAL_P (i))
1166 return Qnil;
1167
1168 s = XINT (start);
1169 len = XINT (end) - s;
1170
1171 /* No need to protect OBJECT, because we GC only if it's a buffer,
1172 and live buffers are always protected. */
1173 GCPRO1 (properties);
1174
1175 /* If we're not starting on an interval boundary, we have to
1176 split this interval. */
1177 if (i->position != s)
1178 {
1179 /* If this interval already has the properties, we can
1180 skip it. */
1181 if (interval_has_all_properties (properties, i))
1182 {
1183 int got = (LENGTH (i) - (s - i->position));
1184 if (got >= len)
1185 RETURN_UNGCPRO (Qnil);
1186 len -= got;
1187 i = next_interval (i);
1188 }
1189 else
1190 {
1191 unchanged = i;
1192 i = split_interval_right (unchanged, s - unchanged->position);
1193 copy_properties (unchanged, i);
1194 }
1195 }
1196
1197 if (BUFFERP (object))
1198 modify_region (XBUFFER (object), XINT (start), XINT (end));
1199
1200 /* We are at the beginning of interval I, with LEN chars to scan. */
1201 for (;;)
1202 {
1203 if (i == 0)
1204 abort ();
1205
1206 if (LENGTH (i) >= len)
1207 {
1208 /* We can UNGCPRO safely here, because there will be just
1209 one more chance to gc, in the next call to add_properties,
1210 and after that we will not need PROPERTIES or OBJECT again. */
1211 UNGCPRO;
1212
1213 if (interval_has_all_properties (properties, i))
1214 {
1215 if (BUFFERP (object))
1216 signal_after_change (XINT (start), XINT (end) - XINT (start),
1217 XINT (end) - XINT (start));
1218
1219 return modified ? Qt : Qnil;
1220 }
1221
1222 if (LENGTH (i) == len)
1223 {
1224 add_properties (properties, i, object);
1225 if (BUFFERP (object))
1226 signal_after_change (XINT (start), XINT (end) - XINT (start),
1227 XINT (end) - XINT (start));
1228 return Qt;
1229 }
1230
1231 /* i doesn't have the properties, and goes past the change limit */
1232 unchanged = i;
1233 i = split_interval_left (unchanged, len);
1234 copy_properties (unchanged, i);
1235 add_properties (properties, i, object);
1236 if (BUFFERP (object))
1237 signal_after_change (XINT (start), XINT (end) - XINT (start),
1238 XINT (end) - XINT (start));
1239 return Qt;
1240 }
1241
1242 len -= LENGTH (i);
1243 modified += add_properties (properties, i, object);
1244 i = next_interval (i);
1245 }
1246 }
1247
1248 /* Callers note, this can GC when OBJECT is a buffer (or nil). */
1249
1250 DEFUN ("put-text-property", Fput_text_property,
1251 Sput_text_property, 4, 5, 0,
1252 doc: /* Set one property of the text from START to END.
1253 The third and fourth arguments PROPERTY and VALUE
1254 specify the property to add.
1255 The optional fifth argument, OBJECT,
1256 is the string or buffer containing the text. */)
1257 (start, end, property, value, object)
1258 Lisp_Object start, end, property, value, object;
1259 {
1260 Fadd_text_properties (start, end,
1261 Fcons (property, Fcons (value, Qnil)),
1262 object);
1263 return Qnil;
1264 }
1265
1266 DEFUN ("set-text-properties", Fset_text_properties,
1267 Sset_text_properties, 3, 4, 0,
1268 doc: /* Completely replace properties of text from START to END.
1269 The third argument PROPERTIES is the new property list.
1270 The optional fourth argument, OBJECT,
1271 is the string or buffer containing the text.
1272 If OBJECT is omitted or nil, it defaults to the current buffer.
1273 If PROPERTIES is nil, the effect is to remove all properties from
1274 the designated part of OBJECT. */)
1275 (start, end, properties, object)
1276 Lisp_Object start, end, properties, object;
1277 {
1278 return set_text_properties (start, end, properties, object, Qt);
1279 }
1280
1281
1282 /* Replace properties of text from START to END with new list of
1283 properties PROPERTIES. OBJECT is the buffer or string containing
1284 the text. OBJECT nil means use the current buffer.
1285 SIGNAL_AFTER_CHANGE_P nil means don't signal after changes. Value
1286 is non-nil if properties were replaced; it is nil if there weren't
1287 any properties to replace. */
1288
1289 Lisp_Object
1290 set_text_properties (start, end, properties, object, signal_after_change_p)
1291 Lisp_Object start, end, properties, object, signal_after_change_p;
1292 {
1293 register INTERVAL i;
1294 Lisp_Object ostart, oend;
1295
1296 ostart = start;
1297 oend = end;
1298
1299 properties = validate_plist (properties);
1300
1301 if (NILP (object))
1302 XSETBUFFER (object, current_buffer);
1303
1304 /* If we want no properties for a whole string,
1305 get rid of its intervals. */
1306 if (NILP (properties) && STRINGP (object)
1307 && XFASTINT (start) == 0
1308 && XFASTINT (end) == XSTRING (object)->size)
1309 {
1310 if (! XSTRING (object)->intervals)
1311 return Qt;
1312
1313 XSTRING (object)->intervals = 0;
1314 return Qt;
1315 }
1316
1317 i = validate_interval_range (object, &start, &end, soft);
1318
1319 if (NULL_INTERVAL_P (i))
1320 {
1321 /* If buffer has no properties, and we want none, return now. */
1322 if (NILP (properties))
1323 return Qnil;
1324
1325 /* Restore the original START and END values
1326 because validate_interval_range increments them for strings. */
1327 start = ostart;
1328 end = oend;
1329
1330 i = validate_interval_range (object, &start, &end, hard);
1331 /* This can return if start == end. */
1332 if (NULL_INTERVAL_P (i))
1333 return Qnil;
1334 }
1335
1336 if (BUFFERP (object))
1337 modify_region (XBUFFER (object), XINT (start), XINT (end));
1338
1339 set_text_properties_1 (start, end, properties, object, i);
1340
1341 if (BUFFERP (object) && !NILP (signal_after_change_p))
1342 signal_after_change (XINT (start), XINT (end) - XINT (start),
1343 XINT (end) - XINT (start));
1344 return Qt;
1345 }
1346
1347 /* Replace properties of text from START to END with new list of
1348 properties PROPERTIES. BUFFER is the buffer containing
1349 the text. This does not obey any hooks.
1350 You can provide the interval that START is located in as I,
1351 or pass NULL for I and this function will find it.
1352 START and END can be in any order. */
1353
1354 void
1355 set_text_properties_1 (start, end, properties, buffer, i)
1356 Lisp_Object start, end, properties, buffer;
1357 INTERVAL i;
1358 {
1359 register INTERVAL prev_changed = NULL_INTERVAL;
1360 register int s, len;
1361 INTERVAL unchanged;
1362
1363 s = XINT (start);
1364 len = XINT (end) - s;
1365 if (len == 0)
1366 return;
1367 if (len < 0)
1368 {
1369 s = s + len;
1370 len = - len;
1371 }
1372
1373 if (i == 0)
1374 i = find_interval (BUF_INTERVALS (XBUFFER (buffer)), s);
1375
1376 if (i->position != s)
1377 {
1378 unchanged = i;
1379 i = split_interval_right (unchanged, s - unchanged->position);
1380
1381 if (LENGTH (i) > len)
1382 {
1383 copy_properties (unchanged, i);
1384 i = split_interval_left (i, len);
1385 set_properties (properties, i, buffer);
1386 return;
1387 }
1388
1389 set_properties (properties, i, buffer);
1390
1391 if (LENGTH (i) == len)
1392 return;
1393
1394 prev_changed = i;
1395 len -= LENGTH (i);
1396 i = next_interval (i);
1397 }
1398
1399 /* We are starting at the beginning of an interval, I */
1400 while (len > 0)
1401 {
1402 if (i == 0)
1403 abort ();
1404
1405 if (LENGTH (i) >= len)
1406 {
1407 if (LENGTH (i) > len)
1408 i = split_interval_left (i, len);
1409
1410 /* We have to call set_properties even if we are going to
1411 merge the intervals, so as to make the undo records
1412 and cause redisplay to happen. */
1413 set_properties (properties, i, buffer);
1414 if (!NULL_INTERVAL_P (prev_changed))
1415 merge_interval_left (i);
1416 return;
1417 }
1418
1419 len -= LENGTH (i);
1420
1421 /* We have to call set_properties even if we are going to
1422 merge the intervals, so as to make the undo records
1423 and cause redisplay to happen. */
1424 set_properties (properties, i, buffer);
1425 if (NULL_INTERVAL_P (prev_changed))
1426 prev_changed = i;
1427 else
1428 prev_changed = i = merge_interval_left (i);
1429
1430 i = next_interval (i);
1431 }
1432 }
1433
1434 DEFUN ("remove-text-properties", Fremove_text_properties,
1435 Sremove_text_properties, 3, 4, 0,
1436 doc: /* Remove some properties from text from START to END.
1437 The third argument PROPERTIES is a property list
1438 whose property names specify the properties to remove.
1439 \(The values stored in PROPERTIES are ignored.)
1440 The optional fourth argument, OBJECT,
1441 is the string or buffer containing the text.
1442 Return t if any property was actually removed, nil otherwise. */)
1443 (start, end, properties, object)
1444 Lisp_Object start, end, properties, object;
1445 {
1446 register INTERVAL i, unchanged;
1447 register int s, len, modified = 0;
1448
1449 if (NILP (object))
1450 XSETBUFFER (object, current_buffer);
1451
1452 i = validate_interval_range (object, &start, &end, soft);
1453 if (NULL_INTERVAL_P (i))
1454 return Qnil;
1455
1456 s = XINT (start);
1457 len = XINT (end) - s;
1458
1459 if (i->position != s)
1460 {
1461 /* No properties on this first interval -- return if
1462 it covers the entire region. */
1463 if (! interval_has_some_properties (properties, i))
1464 {
1465 int got = (LENGTH (i) - (s - i->position));
1466 if (got >= len)
1467 return Qnil;
1468 len -= got;
1469 i = next_interval (i);
1470 }
1471 /* Split away the beginning of this interval; what we don't
1472 want to modify. */
1473 else
1474 {
1475 unchanged = i;
1476 i = split_interval_right (unchanged, s - unchanged->position);
1477 copy_properties (unchanged, i);
1478 }
1479 }
1480
1481 if (BUFFERP (object))
1482 modify_region (XBUFFER (object), XINT (start), XINT (end));
1483
1484 /* We are at the beginning of an interval, with len to scan */
1485 for (;;)
1486 {
1487 if (i == 0)
1488 abort ();
1489
1490 if (LENGTH (i) >= len)
1491 {
1492 if (! interval_has_some_properties (properties, i))
1493 return modified ? Qt : Qnil;
1494
1495 if (LENGTH (i) == len)
1496 {
1497 remove_properties (properties, Qnil, i, object);
1498 if (BUFFERP (object))
1499 signal_after_change (XINT (start), XINT (end) - XINT (start),
1500 XINT (end) - XINT (start));
1501 return Qt;
1502 }
1503
1504 /* i has the properties, and goes past the change limit */
1505 unchanged = i;
1506 i = split_interval_left (i, len);
1507 copy_properties (unchanged, i);
1508 remove_properties (properties, Qnil, i, object);
1509 if (BUFFERP (object))
1510 signal_after_change (XINT (start), XINT (end) - XINT (start),
1511 XINT (end) - XINT (start));
1512 return Qt;
1513 }
1514
1515 len -= LENGTH (i);
1516 modified += remove_properties (properties, Qnil, i, object);
1517 i = next_interval (i);
1518 }
1519 }
1520
1521 DEFUN ("remove-list-of-text-properties", Fremove_list_of_text_properties,
1522 Sremove_list_of_text_properties, 3, 4, 0,
1523 doc: /* Remove some properties from text from START to END.
1524 The third argument LIST-OF-PROPERTIES is a list of property names to remove.
1525 The optional fourth argument, OBJECT,
1526 is the string or buffer containing the text, defaulting to the current buffer.
1527 Return t if any property was actually removed, nil otherwise. */)
1528 (start, end, list_of_properties, object)
1529 Lisp_Object start, end, list_of_properties, object;
1530 {
1531 register INTERVAL i, unchanged;
1532 register int s, len, modified = 0;
1533 Lisp_Object properties;
1534 properties = list_of_properties;
1535
1536 if (NILP (object))
1537 XSETBUFFER (object, current_buffer);
1538
1539 i = validate_interval_range (object, &start, &end, soft);
1540 if (NULL_INTERVAL_P (i))
1541 return Qnil;
1542
1543 s = XINT (start);
1544 len = XINT (end) - s;
1545
1546 if (i->position != s)
1547 {
1548 /* No properties on this first interval -- return if
1549 it covers the entire region. */
1550 if (! interval_has_some_properties_list (properties, i))
1551 {
1552 int got = (LENGTH (i) - (s - i->position));
1553 if (got >= len)
1554 return Qnil;
1555 len -= got;
1556 i = next_interval (i);
1557 }
1558 /* Split away the beginning of this interval; what we don't
1559 want to modify. */
1560 else
1561 {
1562 unchanged = i;
1563 i = split_interval_right (unchanged, s - unchanged->position);
1564 copy_properties (unchanged, i);
1565 }
1566 }
1567
1568 if (BUFFERP (object))
1569 modify_region (XBUFFER (object), XINT (start), XINT (end));
1570
1571 /* We are at the beginning of an interval, with len to scan */
1572 for (;;)
1573 {
1574 if (i == 0)
1575 abort ();
1576
1577 if (LENGTH (i) >= len)
1578 {
1579 if (! interval_has_some_properties_list (properties, i))
1580 return modified ? Qt : Qnil;
1581
1582 if (LENGTH (i) == len)
1583 {
1584 remove_properties (Qnil, properties, i, object);
1585 if (BUFFERP (object))
1586 signal_after_change (XINT (start), XINT (end) - XINT (start),
1587 XINT (end) - XINT (start));
1588 return Qt;
1589 }
1590
1591 /* i has the properties, and goes past the change limit */
1592 unchanged = i;
1593 i = split_interval_left (i, len);
1594 copy_properties (unchanged, i);
1595 remove_properties (Qnil, properties, i, object);
1596 if (BUFFERP (object))
1597 signal_after_change (XINT (start), XINT (end) - XINT (start),
1598 XINT (end) - XINT (start));
1599 return Qt;
1600 }
1601
1602 len -= LENGTH (i);
1603 modified += remove_properties (Qnil, properties, i, object);
1604 i = next_interval (i);
1605 }
1606 }
1607 \f
1608 DEFUN ("text-property-any", Ftext_property_any,
1609 Stext_property_any, 4, 5, 0,
1610 doc: /* Check text from START to END for property PROPERTY equalling VALUE.
1611 If so, return the position of the first character whose property PROPERTY
1612 is `eq' to VALUE. Otherwise return nil.
1613 The optional fifth argument, OBJECT, is the string or buffer
1614 containing the text. */)
1615 (start, end, property, value, object)
1616 Lisp_Object start, end, property, value, object;
1617 {
1618 register INTERVAL i;
1619 register int e, pos;
1620
1621 if (NILP (object))
1622 XSETBUFFER (object, current_buffer);
1623 i = validate_interval_range (object, &start, &end, soft);
1624 if (NULL_INTERVAL_P (i))
1625 return (!NILP (value) || EQ (start, end) ? Qnil : start);
1626 e = XINT (end);
1627
1628 while (! NULL_INTERVAL_P (i))
1629 {
1630 if (i->position >= e)
1631 break;
1632 if (EQ (textget (i->plist, property), value))
1633 {
1634 pos = i->position;
1635 if (pos < XINT (start))
1636 pos = XINT (start);
1637 return make_number (pos);
1638 }
1639 i = next_interval (i);
1640 }
1641 return Qnil;
1642 }
1643
1644 DEFUN ("text-property-not-all", Ftext_property_not_all,
1645 Stext_property_not_all, 4, 5, 0,
1646 doc: /* Check text from START to END for property PROPERTY not equalling VALUE.
1647 If so, return the position of the first character whose property PROPERTY
1648 is not `eq' to VALUE. Otherwise, return nil.
1649 The optional fifth argument, OBJECT, is the string or buffer
1650 containing the text. */)
1651 (start, end, property, value, object)
1652 Lisp_Object start, end, property, value, object;
1653 {
1654 register INTERVAL i;
1655 register int s, e;
1656
1657 if (NILP (object))
1658 XSETBUFFER (object, current_buffer);
1659 i = validate_interval_range (object, &start, &end, soft);
1660 if (NULL_INTERVAL_P (i))
1661 return (NILP (value) || EQ (start, end)) ? Qnil : start;
1662 s = XINT (start);
1663 e = XINT (end);
1664
1665 while (! NULL_INTERVAL_P (i))
1666 {
1667 if (i->position >= e)
1668 break;
1669 if (! EQ (textget (i->plist, property), value))
1670 {
1671 if (i->position > s)
1672 s = i->position;
1673 return make_number (s);
1674 }
1675 i = next_interval (i);
1676 }
1677 return Qnil;
1678 }
1679
1680 \f
1681 /* Return the direction from which the text-property PROP would be
1682 inherited by any new text inserted at POS: 1 if it would be
1683 inherited from the char after POS, -1 if it would be inherited from
1684 the char before POS, and 0 if from neither. */
1685
1686 int
1687 text_property_stickiness (prop, pos)
1688 Lisp_Object prop;
1689 Lisp_Object pos;
1690 {
1691 Lisp_Object prev_pos, front_sticky;
1692 int is_rear_sticky = 1, is_front_sticky = 0; /* defaults */
1693
1694 if (XINT (pos) > BEGV)
1695 /* Consider previous character. */
1696 {
1697 Lisp_Object rear_non_sticky;
1698
1699 prev_pos = make_number (XINT (pos) - 1);
1700 rear_non_sticky = Fget_text_property (prev_pos, Qrear_nonsticky, Qnil);
1701
1702 if (!NILP (CONSP (rear_non_sticky)
1703 ? Fmemq (prop, rear_non_sticky)
1704 : rear_non_sticky))
1705 /* PROP is rear-non-sticky. */
1706 is_rear_sticky = 0;
1707 }
1708
1709 /* Consider following character. */
1710 front_sticky = Fget_text_property (pos, Qfront_sticky, Qnil);
1711
1712 if (EQ (front_sticky, Qt)
1713 || (CONSP (front_sticky)
1714 && !NILP (Fmemq (prop, front_sticky))))
1715 /* PROP is inherited from after. */
1716 is_front_sticky = 1;
1717
1718 /* Simple cases, where the properties are consistent. */
1719 if (is_rear_sticky && !is_front_sticky)
1720 return -1;
1721 else if (!is_rear_sticky && is_front_sticky)
1722 return 1;
1723 else if (!is_rear_sticky && !is_front_sticky)
1724 return 0;
1725
1726 /* The stickiness properties are inconsistent, so we have to
1727 disambiguate. Basically, rear-sticky wins, _except_ if the
1728 property that would be inherited has a value of nil, in which case
1729 front-sticky wins. */
1730 if (XINT (pos) == BEGV || NILP (Fget_text_property (prev_pos, prop, Qnil)))
1731 return 1;
1732 else
1733 return -1;
1734 }
1735
1736 \f
1737 /* I don't think this is the right interface to export; how often do you
1738 want to do something like this, other than when you're copying objects
1739 around?
1740
1741 I think it would be better to have a pair of functions, one which
1742 returns the text properties of a region as a list of ranges and
1743 plists, and another which applies such a list to another object. */
1744
1745 /* Add properties from SRC to SRC of SRC, starting at POS in DEST.
1746 SRC and DEST may each refer to strings or buffers.
1747 Optional sixth argument PROP causes only that property to be copied.
1748 Properties are copied to DEST as if by `add-text-properties'.
1749 Return t if any property value actually changed, nil otherwise. */
1750
1751 /* Note this can GC when DEST is a buffer. */
1752
1753 Lisp_Object
1754 copy_text_properties (start, end, src, pos, dest, prop)
1755 Lisp_Object start, end, src, pos, dest, prop;
1756 {
1757 INTERVAL i;
1758 Lisp_Object res;
1759 Lisp_Object stuff;
1760 Lisp_Object plist;
1761 int s, e, e2, p, len, modified = 0;
1762 struct gcpro gcpro1, gcpro2;
1763
1764 i = validate_interval_range (src, &start, &end, soft);
1765 if (NULL_INTERVAL_P (i))
1766 return Qnil;
1767
1768 CHECK_NUMBER_COERCE_MARKER (pos);
1769 {
1770 Lisp_Object dest_start, dest_end;
1771
1772 dest_start = pos;
1773 XSETFASTINT (dest_end, XINT (dest_start) + (XINT (end) - XINT (start)));
1774 /* Apply this to a copy of pos; it will try to increment its arguments,
1775 which we don't want. */
1776 validate_interval_range (dest, &dest_start, &dest_end, soft);
1777 }
1778
1779 s = XINT (start);
1780 e = XINT (end);
1781 p = XINT (pos);
1782
1783 stuff = Qnil;
1784
1785 while (s < e)
1786 {
1787 e2 = i->position + LENGTH (i);
1788 if (e2 > e)
1789 e2 = e;
1790 len = e2 - s;
1791
1792 plist = i->plist;
1793 if (! NILP (prop))
1794 while (! NILP (plist))
1795 {
1796 if (EQ (Fcar (plist), prop))
1797 {
1798 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1799 break;
1800 }
1801 plist = Fcdr (Fcdr (plist));
1802 }
1803 if (! NILP (plist))
1804 {
1805 /* Must defer modifications to the interval tree in case src
1806 and dest refer to the same string or buffer. */
1807 stuff = Fcons (Fcons (make_number (p),
1808 Fcons (make_number (p + len),
1809 Fcons (plist, Qnil))),
1810 stuff);
1811 }
1812
1813 i = next_interval (i);
1814 if (NULL_INTERVAL_P (i))
1815 break;
1816
1817 p += len;
1818 s = i->position;
1819 }
1820
1821 GCPRO2 (stuff, dest);
1822
1823 while (! NILP (stuff))
1824 {
1825 res = Fcar (stuff);
1826 res = Fadd_text_properties (Fcar (res), Fcar (Fcdr (res)),
1827 Fcar (Fcdr (Fcdr (res))), dest);
1828 if (! NILP (res))
1829 modified++;
1830 stuff = Fcdr (stuff);
1831 }
1832
1833 UNGCPRO;
1834
1835 return modified ? Qt : Qnil;
1836 }
1837
1838
1839 /* Return a list representing the text properties of OBJECT between
1840 START and END. if PROP is non-nil, report only on that property.
1841 Each result list element has the form (S E PLIST), where S and E
1842 are positions in OBJECT and PLIST is a property list containing the
1843 text properties of OBJECT between S and E. Value is nil if OBJECT
1844 doesn't contain text properties between START and END. */
1845
1846 Lisp_Object
1847 text_property_list (object, start, end, prop)
1848 Lisp_Object object, start, end, prop;
1849 {
1850 struct interval *i;
1851 Lisp_Object result;
1852
1853 result = Qnil;
1854
1855 i = validate_interval_range (object, &start, &end, soft);
1856 if (!NULL_INTERVAL_P (i))
1857 {
1858 int s = XINT (start);
1859 int e = XINT (end);
1860
1861 while (s < e)
1862 {
1863 int interval_end, len;
1864 Lisp_Object plist;
1865
1866 interval_end = i->position + LENGTH (i);
1867 if (interval_end > e)
1868 interval_end = e;
1869 len = interval_end - s;
1870
1871 plist = i->plist;
1872
1873 if (!NILP (prop))
1874 for (; !NILP (plist); plist = Fcdr (Fcdr (plist)))
1875 if (EQ (Fcar (plist), prop))
1876 {
1877 plist = Fcons (prop, Fcons (Fcar (Fcdr (plist)), Qnil));
1878 break;
1879 }
1880
1881 if (!NILP (plist))
1882 result = Fcons (Fcons (make_number (s),
1883 Fcons (make_number (s + len),
1884 Fcons (plist, Qnil))),
1885 result);
1886
1887 i = next_interval (i);
1888 if (NULL_INTERVAL_P (i))
1889 break;
1890 s = i->position;
1891 }
1892 }
1893
1894 return result;
1895 }
1896
1897
1898 /* Add text properties to OBJECT from LIST. LIST is a list of triples
1899 (START END PLIST), where START and END are positions and PLIST is a
1900 property list containing the text properties to add. Adjust START
1901 and END positions by DELTA before adding properties. Value is
1902 non-zero if OBJECT was modified. */
1903
1904 int
1905 add_text_properties_from_list (object, list, delta)
1906 Lisp_Object object, list, delta;
1907 {
1908 struct gcpro gcpro1, gcpro2;
1909 int modified_p = 0;
1910
1911 GCPRO2 (list, object);
1912
1913 for (; CONSP (list); list = XCDR (list))
1914 {
1915 Lisp_Object item, start, end, plist, tem;
1916
1917 item = XCAR (list);
1918 start = make_number (XINT (XCAR (item)) + XINT (delta));
1919 end = make_number (XINT (XCAR (XCDR (item))) + XINT (delta));
1920 plist = XCAR (XCDR (XCDR (item)));
1921
1922 tem = Fadd_text_properties (start, end, plist, object);
1923 if (!NILP (tem))
1924 modified_p = 1;
1925 }
1926
1927 UNGCPRO;
1928 return modified_p;
1929 }
1930
1931
1932
1933 /* Modify end-points of ranges in LIST destructively. LIST is a list
1934 as returned from text_property_list. Change end-points equal to
1935 OLD_END to NEW_END. */
1936
1937 void
1938 extend_property_ranges (list, old_end, new_end)
1939 Lisp_Object list, old_end, new_end;
1940 {
1941 for (; CONSP (list); list = XCDR (list))
1942 {
1943 Lisp_Object item, end;
1944
1945 item = XCAR (list);
1946 end = XCAR (XCDR (item));
1947
1948 if (EQ (end, old_end))
1949 XSETCAR (XCDR (item), new_end);
1950 }
1951 }
1952
1953
1954 \f
1955 /* Call the modification hook functions in LIST, each with START and END. */
1956
1957 static void
1958 call_mod_hooks (list, start, end)
1959 Lisp_Object list, start, end;
1960 {
1961 struct gcpro gcpro1;
1962 GCPRO1 (list);
1963 while (!NILP (list))
1964 {
1965 call2 (Fcar (list), start, end);
1966 list = Fcdr (list);
1967 }
1968 UNGCPRO;
1969 }
1970
1971 /* Check for read-only intervals between character positions START ... END,
1972 in BUF, and signal an error if we find one.
1973
1974 Then check for any modification hooks in the range.
1975 Create a list of all these hooks in lexicographic order,
1976 eliminating consecutive extra copies of the same hook. Then call
1977 those hooks in order, with START and END - 1 as arguments. */
1978
1979 void
1980 verify_interval_modification (buf, start, end)
1981 struct buffer *buf;
1982 int start, end;
1983 {
1984 register INTERVAL intervals = BUF_INTERVALS (buf);
1985 register INTERVAL i;
1986 Lisp_Object hooks;
1987 register Lisp_Object prev_mod_hooks;
1988 Lisp_Object mod_hooks;
1989 struct gcpro gcpro1;
1990
1991 hooks = Qnil;
1992 prev_mod_hooks = Qnil;
1993 mod_hooks = Qnil;
1994
1995 interval_insert_behind_hooks = Qnil;
1996 interval_insert_in_front_hooks = Qnil;
1997
1998 if (NULL_INTERVAL_P (intervals))
1999 return;
2000
2001 if (start > end)
2002 {
2003 int temp = start;
2004 start = end;
2005 end = temp;
2006 }
2007
2008 /* For an insert operation, check the two chars around the position. */
2009 if (start == end)
2010 {
2011 INTERVAL prev = NULL;
2012 Lisp_Object before, after;
2013
2014 /* Set I to the interval containing the char after START,
2015 and PREV to the interval containing the char before START.
2016 Either one may be null. They may be equal. */
2017 i = find_interval (intervals, start);
2018
2019 if (start == BUF_BEGV (buf))
2020 prev = 0;
2021 else if (i->position == start)
2022 prev = previous_interval (i);
2023 else if (i->position < start)
2024 prev = i;
2025 if (start == BUF_ZV (buf))
2026 i = 0;
2027
2028 /* If Vinhibit_read_only is set and is not a list, we can
2029 skip the read_only checks. */
2030 if (NILP (Vinhibit_read_only) || CONSP (Vinhibit_read_only))
2031 {
2032 /* If I and PREV differ we need to check for the read-only
2033 property together with its stickiness. If either I or
2034 PREV are 0, this check is all we need.
2035 We have to take special care, since read-only may be
2036 indirectly defined via the category property. */
2037 if (i != prev)
2038 {
2039 if (! NULL_INTERVAL_P (i))
2040 {
2041 after = textget (i->plist, Qread_only);
2042
2043 /* If interval I is read-only and read-only is
2044 front-sticky, inhibit insertion.
2045 Check for read-only as well as category. */
2046 if (! NILP (after)
2047 && NILP (Fmemq (after, Vinhibit_read_only)))
2048 {
2049 Lisp_Object tem;
2050
2051 tem = textget (i->plist, Qfront_sticky);
2052 if (TMEM (Qread_only, tem)
2053 || (NILP (Fplist_get (i->plist, Qread_only))
2054 && TMEM (Qcategory, tem)))
2055 text_read_only ();
2056 }
2057 }
2058
2059 if (! NULL_INTERVAL_P (prev))
2060 {
2061 before = textget (prev->plist, Qread_only);
2062
2063 /* If interval PREV is read-only and read-only isn't
2064 rear-nonsticky, inhibit insertion.
2065 Check for read-only as well as category. */
2066 if (! NILP (before)
2067 && NILP (Fmemq (before, Vinhibit_read_only)))
2068 {
2069 Lisp_Object tem;
2070
2071 tem = textget (prev->plist, Qrear_nonsticky);
2072 if (! TMEM (Qread_only, tem)
2073 && (! NILP (Fplist_get (prev->plist,Qread_only))
2074 || ! TMEM (Qcategory, tem)))
2075 text_read_only ();
2076 }
2077 }
2078 }
2079 else if (! NULL_INTERVAL_P (i))
2080 {
2081 after = textget (i->plist, Qread_only);
2082
2083 /* If interval I is read-only and read-only is
2084 front-sticky, inhibit insertion.
2085 Check for read-only as well as category. */
2086 if (! NILP (after) && NILP (Fmemq (after, Vinhibit_read_only)))
2087 {
2088 Lisp_Object tem;
2089
2090 tem = textget (i->plist, Qfront_sticky);
2091 if (TMEM (Qread_only, tem)
2092 || (NILP (Fplist_get (i->plist, Qread_only))
2093 && TMEM (Qcategory, tem)))
2094 text_read_only ();
2095
2096 tem = textget (prev->plist, Qrear_nonsticky);
2097 if (! TMEM (Qread_only, tem)
2098 && (! NILP (Fplist_get (prev->plist, Qread_only))
2099 || ! TMEM (Qcategory, tem)))
2100 text_read_only ();
2101 }
2102 }
2103 }
2104
2105 /* Run both insert hooks (just once if they're the same). */
2106 if (!NULL_INTERVAL_P (prev))
2107 interval_insert_behind_hooks
2108 = textget (prev->plist, Qinsert_behind_hooks);
2109 if (!NULL_INTERVAL_P (i))
2110 interval_insert_in_front_hooks
2111 = textget (i->plist, Qinsert_in_front_hooks);
2112 }
2113 else
2114 {
2115 /* Loop over intervals on or next to START...END,
2116 collecting their hooks. */
2117
2118 i = find_interval (intervals, start);
2119 do
2120 {
2121 if (! INTERVAL_WRITABLE_P (i))
2122 text_read_only ();
2123
2124 if (!inhibit_modification_hooks)
2125 {
2126 mod_hooks = textget (i->plist, Qmodification_hooks);
2127 if (! NILP (mod_hooks) && ! EQ (mod_hooks, prev_mod_hooks))
2128 {
2129 hooks = Fcons (mod_hooks, hooks);
2130 prev_mod_hooks = mod_hooks;
2131 }
2132 }
2133
2134 i = next_interval (i);
2135 }
2136 /* Keep going thru the interval containing the char before END. */
2137 while (! NULL_INTERVAL_P (i) && i->position < end);
2138
2139 if (!inhibit_modification_hooks)
2140 {
2141 GCPRO1 (hooks);
2142 hooks = Fnreverse (hooks);
2143 while (! EQ (hooks, Qnil))
2144 {
2145 call_mod_hooks (Fcar (hooks), make_number (start),
2146 make_number (end));
2147 hooks = Fcdr (hooks);
2148 }
2149 UNGCPRO;
2150 }
2151 }
2152 }
2153
2154 /* Run the interval hooks for an insertion on character range START ... END.
2155 verify_interval_modification chose which hooks to run;
2156 this function is called after the insertion happens
2157 so it can indicate the range of inserted text. */
2158
2159 void
2160 report_interval_modification (start, end)
2161 Lisp_Object start, end;
2162 {
2163 if (! NILP (interval_insert_behind_hooks))
2164 call_mod_hooks (interval_insert_behind_hooks, start, end);
2165 if (! NILP (interval_insert_in_front_hooks)
2166 && ! EQ (interval_insert_in_front_hooks,
2167 interval_insert_behind_hooks))
2168 call_mod_hooks (interval_insert_in_front_hooks, start, end);
2169 }
2170 \f
2171 void
2172 syms_of_textprop ()
2173 {
2174 DEFVAR_LISP ("default-text-properties", &Vdefault_text_properties,
2175 doc: /* Property-list used as default values.
2176 The value of a property in this list is seen as the value for every
2177 character that does not have its own value for that property. */);
2178 Vdefault_text_properties = Qnil;
2179
2180 DEFVAR_LISP ("inhibit-point-motion-hooks", &Vinhibit_point_motion_hooks,
2181 doc: /* If non-nil, don't run `point-left' and `point-entered' text properties.
2182 This also inhibits the use of the `intangible' text property. */);
2183 Vinhibit_point_motion_hooks = Qnil;
2184
2185 DEFVAR_LISP ("text-property-default-nonsticky",
2186 &Vtext_property_default_nonsticky,
2187 doc: /* Alist of properties vs the corresponding non-stickinesses.
2188 Each element has the form (PROPERTY . NONSTICKINESS).
2189
2190 If a character in a buffer has PROPERTY, new text inserted adjacent to
2191 the character doesn't inherit PROPERTY if NONSTICKINESS is non-nil,
2192 inherits it if NONSTICKINESS is nil. The front-sticky and
2193 rear-nonsticky properties of the character overrides NONSTICKINESS. */);
2194 Vtext_property_default_nonsticky = Qnil;
2195
2196 staticpro (&interval_insert_behind_hooks);
2197 staticpro (&interval_insert_in_front_hooks);
2198 interval_insert_behind_hooks = Qnil;
2199 interval_insert_in_front_hooks = Qnil;
2200
2201
2202 /* Common attributes one might give text */
2203
2204 staticpro (&Qforeground);
2205 Qforeground = intern ("foreground");
2206 staticpro (&Qbackground);
2207 Qbackground = intern ("background");
2208 staticpro (&Qfont);
2209 Qfont = intern ("font");
2210 staticpro (&Qstipple);
2211 Qstipple = intern ("stipple");
2212 staticpro (&Qunderline);
2213 Qunderline = intern ("underline");
2214 staticpro (&Qread_only);
2215 Qread_only = intern ("read-only");
2216 staticpro (&Qinvisible);
2217 Qinvisible = intern ("invisible");
2218 staticpro (&Qintangible);
2219 Qintangible = intern ("intangible");
2220 staticpro (&Qcategory);
2221 Qcategory = intern ("category");
2222 staticpro (&Qlocal_map);
2223 Qlocal_map = intern ("local-map");
2224 staticpro (&Qfront_sticky);
2225 Qfront_sticky = intern ("front-sticky");
2226 staticpro (&Qrear_nonsticky);
2227 Qrear_nonsticky = intern ("rear-nonsticky");
2228 staticpro (&Qmouse_face);
2229 Qmouse_face = intern ("mouse-face");
2230
2231 /* Properties that text might use to specify certain actions */
2232
2233 staticpro (&Qmouse_left);
2234 Qmouse_left = intern ("mouse-left");
2235 staticpro (&Qmouse_entered);
2236 Qmouse_entered = intern ("mouse-entered");
2237 staticpro (&Qpoint_left);
2238 Qpoint_left = intern ("point-left");
2239 staticpro (&Qpoint_entered);
2240 Qpoint_entered = intern ("point-entered");
2241
2242 defsubr (&Stext_properties_at);
2243 defsubr (&Sget_text_property);
2244 defsubr (&Sget_char_property);
2245 defsubr (&Snext_char_property_change);
2246 defsubr (&Sprevious_char_property_change);
2247 defsubr (&Snext_single_char_property_change);
2248 defsubr (&Sprevious_single_char_property_change);
2249 defsubr (&Snext_property_change);
2250 defsubr (&Snext_single_property_change);
2251 defsubr (&Sprevious_property_change);
2252 defsubr (&Sprevious_single_property_change);
2253 defsubr (&Sadd_text_properties);
2254 defsubr (&Sput_text_property);
2255 defsubr (&Sset_text_properties);
2256 defsubr (&Sremove_text_properties);
2257 defsubr (&Sremove_list_of_text_properties);
2258 defsubr (&Stext_property_any);
2259 defsubr (&Stext_property_not_all);
2260 /* defsubr (&Serase_text_properties); */
2261 /* defsubr (&Scopy_text_properties); */
2262 }
2263