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