Mercurial > hg > xemacs-beta
diff src/specifier.c @ 3659:98af8a976fc3
[xemacs-hg @ 2006-11-05 22:31:31 by aidan]
Support specifying fonts for particular character sets in Mule; support
translation to ISO 10646-1 for Mule character sets without an otherwise
matching font; move to a vector of X11-charset-X11-registry instead of a
regex for the charset-registry property.
author | aidan |
---|---|
date | Sun, 05 Nov 2006 22:31:46 +0000 |
parents | d674024a8674 |
children | b880e45ea63b |
line wrap: on
line diff
--- a/src/specifier.c Sat Nov 04 22:51:03 2006 +0000 +++ b/src/specifier.c Sun Nov 05 22:31:46 2006 +0000 @@ -47,6 +47,7 @@ Lisp_Object Qconsole_type, Qdevice_class; static Lisp_Object Vuser_defined_tags; +static Lisp_Object Vcharset_tag_lists; typedef struct specifier_type_entry specifier_type_entry; struct specifier_type_entry @@ -428,9 +429,9 @@ }; static const struct memory_description specifier_empty_extra_description_1[] = -{ - { XD_END } -}; + { + { XD_END } + }; const struct sized_memory_description specifier_empty_extra_description = { 0, specifier_empty_extra_description_1 @@ -471,7 +472,7 @@ } maybe_invalid_argument ("Invalid specifier type", - type, Qspecifier, errb); + type, Qspecifier, errb); return 0; } @@ -683,7 +684,7 @@ instantiation will actually occur in the window the image instance itself is instantiated in. */ - (domain)) + (domain)) { /* This cannot GC. */ return ((DEVICEP (domain) && DEVICE_LIVE_P (XDEVICE (domain))) || @@ -694,14 +695,14 @@ ? Qt : Qnil; } -DEFUN ("valid-specifier-locale-type-p", Fvalid_specifier_locale_type_p, 1, 1, 0, - /* +DEFUN ("valid-specifier-locale-type-p", Fvalid_specifier_locale_type_p, 1, + 1, 0, /* Given a specifier LOCALE-TYPE, return non-nil if it is valid. Valid locale types are `global', `device', `frame', `window', and `buffer'. \(Note, however, that in functions that accept either a locale or a locale type, `global' is considered an individual locale.) */ - (locale_type)) + (locale_type)) { /* This cannot GC. */ return (EQ (locale_type, Qglobal) || @@ -731,7 +732,7 @@ /* This cannot GC. */ if (NILP (Fvalid_specifier_locale_p (locale))) invalid_argument ("Invalid specifier locale", - locale); + locale); if (DEVICEP (locale)) return Qdevice; if (FRAMEP (locale)) return Qframe; if (WINDOWP (locale)) return Qwindow; @@ -750,7 +751,7 @@ return locale; else invalid_argument ("Invalid specifier locale", - locale); + locale); return Qnil; } @@ -766,7 +767,7 @@ if (EQ (locale_type, Qbuffer)) return LOCALE_BUFFER; invalid_argument ("Invalid specifier locale type", - locale_type); + locale_type); RETURN_NOT_REACHED (LOCALE_GLOBAL); } @@ -803,7 +804,7 @@ { if (NILP (Fvalid_specifier_domain_p (domain))) invalid_argument ("Invalid specifier domain", - domain); + domain); } Lisp_Object @@ -834,10 +835,10 @@ DEFUN ("valid-specifier-tag-set-p", Fvalid_specifier_tag_set_p, 1, 1, 0, /* Return non-nil if TAG-SET is a valid specifier tag set. -A specifier tag set is an entity that is attached to an instantiator -and can be used to restrict the scope of that instantiator to a -particular device class or device type and/or to mark instantiators -added by a particular package so that they can be later removed. +A specifier tag set is an entity that is attached to an instantiator and can +be used to restrict the scope of that instantiator to a particular device +class, device type, or charset. It can also be used to mark instantiators +added by a particular package so that they can be later removed as a group. A specifier tag set consists of a list of zero of more specifier tags, each of which is a symbol that is recognized by XEmacs as a tag. @@ -846,18 +847,25 @@ \(as opposed to a list) because the order of the tags or the number of times a particular tag occurs does not matter. -Each tag has a predicate associated with it, which specifies whether -that tag applies to a particular device. The tags which are device types -and classes match devices of that type or class. User-defined tags can -have any predicate, or none (meaning that all devices match). When -attempting to instantiate a specifier, a particular instantiator is only -considered if the device of the domain being instantiated over matches -all tags in the tag set attached to that instantiator. +Each tag has two predicates associated with it, which specify, respectively, +whether that tag applies to a particular device and whether it applies to a +particular character set. The predefined tags which are device types and +classes match devices of that type or class. User-defined tags can have any +device predicate, or none (meaning that all devices match). When attempting +to instantiate a specifier, a particular instantiator is only considered if +the device of the domain being instantiated over matches all tags in the tag +set attached to that instantiator. + +If a charset is to be considered--which is only the case for face +instantiators--this consideration may be done twice. The first iteration +pays attention to the character set predicates; if no instantiator can be +found in that case, the search is repeated ignoring the character set +predicates. Most of the time, a tag set is not specified, and the instantiator gets a null tag set, which matches all devices. */ - (tag_set)) + (tag_set)) { Lisp_Object rest; @@ -880,7 +888,7 @@ return list1 (tag_set); if (NILP (Fvalid_specifier_tag_set_p (tag_set))) invalid_argument ("Invalid specifier tag-set", - tag_set); + tag_set); return tag_set; } @@ -973,6 +981,63 @@ return 1; } +static int +charset_matches_specifier_tag_set_p (Lisp_Object charset, + Lisp_Object tag_set, + enum font_specifier_matchspec_stages + stage) +{ + Lisp_Object rest; + int res = 0; + + assert(stage != impossible); + + LIST_LOOP (rest, tag_set) + { + Lisp_Object tag = XCAR (rest); + Lisp_Object assoc; + + /* This function will not ever be called with a charset for which the + relevant information hasn't been calculated (the information is + calculated with the creation of every charset). */ + assert (!NILP(XVECTOR_DATA + (Vcharset_tag_lists)[XCHARSET_LEADING_BYTE(charset) + - MIN_LEADING_BYTE])); + + /* Now, find out what the pre-calculated value is. */ + assoc = assq_no_quit(tag, + XVECTOR_DATA(Vcharset_tag_lists) + [XCHARSET_LEADING_BYTE(charset) + - MIN_LEADING_BYTE]); + + if (!(NILP(assoc)) && !(NILP(XCDR(assoc)))) + { + assert(VECTORP(XCDR(assoc))); + + /* In the event that a tag specifies a charset, then the specifier + must match for (this stage and this charset) for all + charset-specifying tags. */ + if (NILP(XVECTOR_DATA(XCDR(assoc))[stage])) + { + /* It doesn't match for this tag, even though the tag + specifies a charset. Return 0. */ + return 0; + } + + /* This tag specifies charset limitations, and this charset and + stage match those charset limitations. + + In the event that a later tag specifies charset limitations + that don't match, the return 0 above prevents us giving a + positive match. */ + res = 1; + } + } + + return res; +} + + DEFUN ("device-matches-specifier-tag-set-p", Fdevice_matches_specifier_tag_set_p, 2, 2, 0, /* Return non-nil if DEVICE matches specifier tag set TAG-SET. @@ -990,56 +1055,71 @@ return device_matches_specifier_tag_set_p (device, tag_set) ? Qt : Qnil; } -DEFUN ("define-specifier-tag", Fdefine_specifier_tag, 1, 2, 0, /* -Define a new specifier tag. -If PREDICATE is specified, it should be a function of one argument -\(a device) that specifies whether the tag matches that particular -device. If PREDICATE is omitted, the tag matches all devices. - -You can redefine an existing user-defined specifier tag. However, -you cannot redefine the built-in specifier tags (the device types -and classes) or the symbols nil, t, `all', or `global'. -*/ - (tag, predicate)) +Lisp_Object +define_specifier_tag(Lisp_Object tag, Lisp_Object device_predicate, + Lisp_Object charset_predicate) { - Lisp_Object assoc, devcons, concons; - int recompute = 0; - - CHECK_SYMBOL (tag); - if (valid_device_class_p (tag) || - valid_console_type_p (tag)) - invalid_change ("Cannot redefine built-in specifier tags", tag); - /* Try to prevent common instantiators and locales from being - redefined, to reduce ambiguity */ - if (NILP (tag) || EQ (tag, Qt) || EQ (tag, Qall) || EQ (tag, Qglobal)) - invalid_change ("Cannot define nil, t, `all', or `global'", tag); - assoc = assq_no_quit (tag, Vuser_defined_tags); + Lisp_Object assoc = assq_no_quit (tag, Vuser_defined_tags), + concons, devcons, charpres = Qnil; + int recompute_devices = 0, recompute_charsets = 0, i, max_args = -1; + if (NILP (assoc)) { - recompute = 1; - Vuser_defined_tags = Fcons (Fcons (tag, predicate), Vuser_defined_tags); + recompute_devices = recompute_charsets = 1; + Vuser_defined_tags = Fcons (list3 (tag, device_predicate, + charset_predicate), + Vuser_defined_tags); DEVICE_LOOP_NO_BREAK (devcons, concons) { struct device *d = XDEVICE (XCAR (devcons)); /* Initially set the value to t in case of error - in predicate */ + in device_predicate */ DEVICE_USER_DEFINED_TAGS (d) = Fcons (Fcons (tag, Qt), DEVICE_USER_DEFINED_TAGS (d)); } + + if (!NILP (charset_predicate)) + { + max_args = XINT(Ffunction_max_args(charset_predicate)); + if (max_args < 1) + { + invalid_argument + ("Charset predicate must be able to take an argument", tag); + } + } } - else if (!NILP (predicate) && !NILP (XCDR (assoc))) + else if (!NILP (device_predicate) && !NILP (XCADR (assoc))) { - recompute = 1; - XCDR (assoc) = predicate; + recompute_devices = 1; + XCDR (assoc) = list2(device_predicate, charset_predicate); } - - /* recompute the tag values for all devices. However, in the special - case where both the old and new predicates are nil, we know that - we don't have to do this. (It's probably common for people to - call (define-specifier-tag) more than once on the same tag, - and the most common case is where PREDICATE is not specified.) */ - - if (recompute) + else if (!NILP (charset_predicate) || !NILP(XCADDR (assoc))) + { + max_args = XINT(Ffunction_max_args(charset_predicate)); + if (max_args < 1) + { + invalid_argument + ("Charset predicate must be able to take an argument", tag); + } + + /* If there exists a charset_predicate for the tag currently (even if + the new charset_predicate is nil), or if we're adding one, we need + to recompute. This contrasts with the device predicates, where we + don't need to recompute if the old and new device predicates are + both nil. */ + + recompute_charsets = 1; + XCDR (assoc) = list2(device_predicate, charset_predicate); + } + + /* Recompute the tag values for all devices and charsets, if necessary. In + the special case where both the old and new device_predicates are nil, + we know that we don't have to do it for the device. (It's probably + common for people to call (define-specifier-tag) more than once on the + same tag, and the most common case is where DEVICE_PREDICATE is not + specified.) */ + + if (recompute_devices) { DEVICE_LOOP_NO_BREAK (devcons, concons) { @@ -1047,14 +1127,157 @@ assoc = assq_no_quit (tag, DEVICE_USER_DEFINED_TAGS (XDEVICE (device))); assert (CONSP (assoc)); - if (NILP (predicate)) + if (NILP (device_predicate)) XCDR (assoc) = Qt; else - XCDR (assoc) = !NILP (call1 (predicate, device)) ? Qt : Qnil; + XCDR (assoc) = !NILP (call1 (device_predicate, device)) ? Qt + : Qnil; } } - return Qnil; + if (recompute_charsets) + { + if (NILP(charset_predicate)) + { + charpres = Qnil; + } + + for (i = 0; i < NUM_LEADING_BYTES; ++i) + { + if (NILP(charset_by_leading_byte(MIN_LEADING_BYTE + i))) + { + continue; + } + + assoc = assq_no_quit (tag, + XVECTOR_DATA(Vcharset_tag_lists)[i]); + + if (!NILP(charset_predicate)) + { + static int line_1147_calls; + ++line_1147_calls; + charpres = make_vector(impossible, Qnil); + + /* If you want to extend the number of stages available, here + in setup_charset_initial_specifier_tags, and in specifier.h + is where you want to go. */ + +#define DEFINE_SPECIFIER_TAG_FROB(stage) do { \ + if (max_args > 1) \ + { \ + XVECTOR_DATA(charpres)[stage] = \ + call2_trapping_problems \ + ("Error during specifier tag charset predicate," \ + " stage " #stage, charset_predicate, \ + charset_by_leading_byte(MIN_LEADING_BYTE + i), \ + Q##stage, 0); \ + } \ + else \ + { \ + XVECTOR_DATA(charpres)[stage] = \ + call1_trapping_problems \ + ("Error during specifier tag charset predicate," \ + " stage " #stage, charset_predicate, \ + charset_by_leading_byte(MIN_LEADING_BYTE + i), \ + 0); \ + } \ + \ + if (UNBOUNDP(XVECTOR_DATA(charpres)[stage])) \ + { \ + XVECTOR_DATA(charpres)[stage] = Qnil; \ + } \ + else if (!NILP(XVECTOR_DATA(charpres)[stage])) \ + { \ + /* Don't want refs to random other objects. */ \ + XVECTOR_DATA(charpres)[stage] = Qt; \ + } \ + } while (0) + + DEFINE_SPECIFIER_TAG_FROB (initial); + DEFINE_SPECIFIER_TAG_FROB (final); + +#undef DEFINE_SPECIFIER_TAG_FROB + + } + + if (!NILP(assoc)) + { + assert(CONSP(assoc)); + XCDR (assoc) = charpres; + } + else + { + XVECTOR_DATA(Vcharset_tag_lists)[i] + = Fcons(Fcons(tag, charpres), + XVECTOR_DATA (Vcharset_tag_lists)[i]); + } + } + } + return Qt; +} + +DEFUN ("define-specifier-tag", Fdefine_specifier_tag, 1, 3, 0, /* +Define a new specifier tag. + +If DEVICE-PREDICATE is specified, it should be a function of one argument +\(a device) that specifies whether the tag matches that particular device. +If DEVICE-PREDICATE is omitted, the tag matches all devices. + +If CHARSET-PREDICATE is supplied, it should be a function taking a single +Lisp character set argument. A tag's charset predicate is primarily used to +determine what font to use for a given \(set of) charset\(s) when that tag +is used in a set-face-font call; a non-nil return value indicates that the +tag matches the charset. + +The font matching process also has a concept of stages; the defined stages +are currently `initial' and `final', and there exist specifier tags with +those names that correspond to those stages. On X11, 'initial is used when +the font matching process is looking for fonts that match the desired +registries of the charset--see the `charset-registries' function. If that +match process fails, then the 'final tag becomes relevant; this means that a +more general lookup is desired, and that a font doesn't necessarily have to +match the desired XLFD for the face, just the charset repertoire for this +charset. It also means that the charset registry and encoding used will be +`iso10646-1', and the characters will be converted to display using that +registry. + +If a tag set matches no character set; the two-stage match process will +ignore the tag on its first pass, but if no match is found, it will respect +it on the second pass, where character set information is ignored. + +You can redefine an existing user-defined specifier tag. However, you +cannot redefine most of the built-in specifier tags \(the device types and +classes, `initial', and `final') or the symbols nil, t, `all', or `global'. +Note that if a device type is not supported in this XEmacs, it will not be +available as a built-in specifier tag; this is probably something we should +change. +*/ + (tag, device_predicate, charset_predicate)) +{ + int max_args; + + CHECK_SYMBOL (tag); + if (valid_device_class_p (tag) || + valid_console_type_p (tag) || + EQ (tag, Qinitial) || EQ (tag, Qfinal)) + invalid_change ("Cannot redefine built-in specifier tags", tag); + /* Try to prevent common instantiators and locales from being + redefined, to reduce ambiguity */ + if (NILP (tag) || EQ (tag, Qt) || EQ (tag, Qall) || EQ (tag, Qglobal)) + invalid_change ("Cannot define nil, t, `all', or `global'", tag); + + if (!NILP (charset_predicate)) + { + max_args = XINT(Ffunction_max_args(charset_predicate)); + if (max_args != 1) + { + /* We only allow the stage argument to be specifed from C. */ + invalid_change ("Charset predicate must take one argument", + tag); + } + } + + return define_specifier_tag(tag, device_predicate, charset_predicate); } /* Called at device-creation time to initialize the user-defined @@ -1065,6 +1288,8 @@ { Lisp_Object rest, rest2; Lisp_Object device = wrap_device (d); + Lisp_Object device_predicate, charset_predicate; + int list_len; DEVICE_USER_DEFINED_TAGS (d) = Fcopy_alist (Vuser_defined_tags); @@ -1075,21 +1300,89 @@ for (rest = Vuser_defined_tags, rest2 = DEVICE_USER_DEFINED_TAGS (d); !NILP (rest); rest = XCDR (rest), rest2 = XCDR (rest2)) { - Lisp_Object predicate = XCDR (XCAR (rest)); - if (NILP (predicate)) - XCDR (XCAR (rest2)) = Qt; + GET_LIST_LENGTH(XCAR(rest), list_len); + + assert(3 == list_len); + + device_predicate = XCADR(XCAR (rest)); + charset_predicate = XCADDR(XCAR (rest)); + + if (NILP (device_predicate)) + { + XCDR (XCAR (rest2)) = list2(Qt, charset_predicate); + } else - XCDR (XCAR (rest2)) = - !NILP (call_critical_lisp_code (d, predicate, device)) ? Qt : Qnil; + { + device_predicate = !NILP (call_critical_lisp_code + (d, device_predicate, device)) + ? Qt : Qnil; + XCDR (XCAR (rest2)) = list2(device_predicate, charset_predicate); + } } } +void +setup_charset_initial_specifier_tags (Lisp_Object charset) +{ + Lisp_Object rest, charset_predicate, tag, new_value; + Lisp_Object charset_tag_list = Qnil; + + LIST_LOOP (rest, Vuser_defined_tags) + { + tag = XCAR(XCAR(rest)); + charset_predicate = XCADDR(XCAR (rest)); + + if (NILP(charset_predicate)) + { + continue; + } + + new_value = make_vector(impossible, Qnil); + +#define SETUP_CHARSET_TAGS_FROB(stage) do { \ + \ + XVECTOR_DATA(new_value)[stage] = call2_trapping_problems \ + ("Error during specifier tag charset predicate," \ + " stage " #stage, \ + charset_predicate, charset, Q##stage, 0); \ + \ + if (UNBOUNDP(XVECTOR_DATA(new_value)[stage])) \ + { \ + XVECTOR_DATA(new_value)[stage] = Qnil; \ + } \ + else if (!NILP(XVECTOR_DATA(new_value)[stage])) \ + { \ + /* Don't want random other objects hanging around. */ \ + XVECTOR_DATA(new_value)[stage] = Qt; \ + } \ + \ + } while (0) + + SETUP_CHARSET_TAGS_FROB (initial); + SETUP_CHARSET_TAGS_FROB (final); + /* More later? */ + +#undef SETUP_CHARSET_TAGS_FROB + + charset_tag_list = Fcons(Fcons(tag, new_value), charset_tag_list); + } + + XVECTOR_DATA + (Vcharset_tag_lists)[XCHARSET_LEADING_BYTE(charset) - MIN_LEADING_BYTE] + = charset_tag_list; +} + +#ifdef DEBUG_XEMACS + +/* Nothing's calling this, I see no reason to keep it in the production + builds. */ + DEFUN ("device-matching-specifier-tag-list", Fdevice_matching_specifier_tag_list, 0, 1, 0, /* -Return a list of all specifier tags matching DEVICE. -DEVICE defaults to the selected device if omitted. -*/ + Return a list of all specifier tags matching DEVICE. + DEVICE defaults to the selected device if omitted. + */ (device)) { struct device *d = decode_device (device); @@ -1100,7 +1393,7 @@ LIST_LOOP (rest, DEVICE_USER_DEFINED_TAGS (d)) { - if (!NILP (XCDR (XCAR (rest)))) + if (!NILP (XCADR (XCAR (rest)))) list = Fcons (XCAR (XCAR (rest)), list); } @@ -1111,6 +1404,8 @@ RETURN_UNGCPRO (list); } +#endif /* DEBUG_XEMACS */ + DEFUN ("specifier-tag-list", Fspecifier_tag_list, 0, 0, 0, /* Return a list of all currently-defined specifier tags. This includes the built-in ones (the device types and classes). @@ -1132,8 +1427,9 @@ RETURN_UNGCPRO (list); } -DEFUN ("specifier-tag-predicate", Fspecifier_tag_predicate, 1, 1, 0, /* -Return the predicate for the given specifier tag. +DEFUN ("specifier-tag-device-predicate", Fspecifier_tag_device_predicate, + 1, 1, 0, /* +Return the device predicate for the given specifier tag. */ (tag)) { @@ -1142,7 +1438,7 @@ if (NILP (Fvalid_specifier_tag_p (tag))) invalid_argument ("Invalid specifier tag", - tag); + tag); /* Make up some predicates for the built-in types */ @@ -1156,11 +1452,27 @@ list3 (Qeq, list2 (Qquote, tag), list2 (Qdevice_class, Qdevice))); - return XCDR (assq_no_quit (tag, Vuser_defined_tags)); + return XCADR (assq_no_quit (tag, Vuser_defined_tags)); +} + +DEFUN ("specifier-tag-charset-predicate", Fspecifier_tag_charset_predicate, + 1, 1, 0, /* + Return the charset predicate for the given specifier tag. + */ + (tag)) +{ + /* The return value of this function must be GCPRO'd. */ + CHECK_SYMBOL (tag); + + if (NILP (Fvalid_specifier_tag_p (tag))) + invalid_argument ("Invalid specifier tag", + tag); + + return XCADDR (assq_no_quit (tag, Vuser_defined_tags)); } /* Return true if A "matches" B. If EXACT_P is 0, A must be a subset of B. - Otherwise, A must be `equal' to B. The sets must be canonicalized. */ + Otherwise, A must be `equal' to B. The sets must be canonicalized. */ static int tag_sets_match_p (Lisp_Object a, Lisp_Object b, int exact_p) { @@ -1268,15 +1580,15 @@ if (!CONSP (inst_pair)) { maybe_sferror ( - "Invalid instantiator pair", inst_pair, - Qspecifier, errb); + "Invalid instantiator pair", inst_pair, + Qspecifier, errb); return Qnil; } if (NILP (Fvalid_specifier_tag_set_p (tag_set = XCAR (inst_pair)))) { maybe_invalid_argument ( - "Invalid specifier tag", tag_set, - Qspecifier, errb); + "Invalid specifier tag", tag_set, + Qspecifier, errb); return Qnil; } @@ -1317,15 +1629,15 @@ if (!CONSP (spec)) { maybe_sferror ( - "Invalid specification list", spec_list, - Qspecifier, errb); + "Invalid specification list", spec_list, + Qspecifier, errb); return Qnil; } if (NILP (Fvalid_specifier_locale_p (locale = XCAR (spec)))) { maybe_invalid_argument ( - "Invalid specifier locale", locale, - Qspecifier, errb); + "Invalid specifier locale", locale, + Qspecifier, errb); return Qnil; } @@ -1414,13 +1726,13 @@ out the frequency with which this is called with the various types and reorder the check accordingly. */ #define SPECIFIER_GET_SPEC_LIST(specifier, type) \ -(type == LOCALE_GLOBAL ? &(XSPECIFIER (specifier)->global_specs) : \ - type == LOCALE_DEVICE ? &(XSPECIFIER (specifier)->device_specs) : \ - type == LOCALE_FRAME ? &(XSPECIFIER (specifier)->frame_specs) : \ - type == LOCALE_WINDOW ? &(XWEAK_LIST_LIST \ - (XSPECIFIER (specifier)->window_specs)) : \ - type == LOCALE_BUFFER ? &(XSPECIFIER (specifier)->buffer_specs) : \ - 0) + (type == LOCALE_GLOBAL ? &(XSPECIFIER (specifier)->global_specs) : \ + type == LOCALE_DEVICE ? &(XSPECIFIER (specifier)->device_specs) : \ + type == LOCALE_FRAME ? &(XSPECIFIER (specifier)->frame_specs) : \ + type == LOCALE_WINDOW ? &(XWEAK_LIST_LIST \ + (XSPECIFIER (specifier)->window_specs)) : \ + type == LOCALE_BUFFER ? &(XSPECIFIER (specifier)->buffer_specs) : \ + 0) static Lisp_Object * specifier_get_inst_list (Lisp_Object specifier, Lisp_Object locale, @@ -1759,8 +2071,8 @@ validating LOCALE and INST-LIST, but the tag-sets in INST-LIST do not need to be canonicalized. */ - /* #### I really need to rethink the after-change - functions to make them easier to use and more efficient. */ +/* #### I really need to rethink the after-change + functions to make them easier to use and more efficient. */ static void specifier_add_spec (Lisp_Object specifier, Lisp_Object locale, @@ -1856,9 +2168,9 @@ /* map MAPFUN over the locales in SPECIFIER that are given in LOCALE. CLOSURE is passed unchanged to MAPFUN. LOCALE can be one of - -- nil (same as `all') - -- a single locale, locale type, or `all' - -- a list of locales, locale types, and/or `all' + -- nil (same as `all') + -- a single locale, locale type, or `all' + -- a list of locales, locale types, and/or `all' MAPFUN is called for each locale and locale type given; for `all', it is called for the locale `global' and for the four possible @@ -1868,7 +2180,7 @@ If MAPFUN ever returns non-zero, the mapping is halted and the value returned is returned from map_specifier(). Otherwise, the mapping proceeds to the end and map_specifier() returns 0. - */ +*/ static int map_specifier (Lisp_Object specifier, Lisp_Object locale, @@ -2148,7 +2460,7 @@ TAG-SET must be equal to an instantiator's tag set for the instantiator to be returned. */ - (specifier, locale, tag_set, exact_p)) + (specifier, locale, tag_set, exact_p)) { struct specifier_spec_list_closure cl; struct gcpro gcpro1, gcpro2; @@ -2347,7 +2659,7 @@ CHECK_SPECIFIER (dest); check_modifiable_specifier (dest); if (XSPECIFIER (dest)->methods != XSPECIFIER (specifier)->methods) - invalid_argument ("Specifiers not of same type", Qunbound); + invalid_argument ("Specifiers not of same type", Qunbound); } cl.dest = dest; @@ -2496,10 +2808,13 @@ { /* This function can GC */ Lisp_Specifier *sp; - Lisp_Object device; - Lisp_Object rest; - int count = specpdl_depth (); + Lisp_Object device, charset = Qnil, rest; + int count = specpdl_depth (), respected_charsets = 0; struct gcpro gcpro1, gcpro2; + enum font_specifier_matchspec_stages stage = initial; +#ifdef DEBUG_XEMACS + int non_ascii; +#endif GCPRO2 (specifier, inst_list); @@ -2507,37 +2822,125 @@ device = DOMAIN_DEVICE (domain); if (no_quit) - /* The instantiate method is allowed to call eval. Since it - is quite common for this function to get called from somewhere in - redisplay we need to make sure that quits are ignored. Otherwise - Fsignal will abort. */ + /* The instantiate method is allowed to call eval. Since it + is quite common for this function to get called from somewhere in + redisplay we need to make sure that quits are ignored. Otherwise + Fsignal will abort. */ specbind (Qinhibit_quit, Qt); +#ifdef MULE + if (CONSP(matchspec) && (CHARSETP(XCAR(matchspec)))) + { + charset = Ffind_charset(XCAR(matchspec)); + +#ifdef DEBUG_XEMACS + /* This is mostly to have somewhere to set debug breakpoints. */ + if (!EQ(charset, Vcharset_ascii)) + { + non_ascii = 1; + } +#endif /* DEBUG_XEMACS */ + + if (!NILP(XCDR(matchspec))) + { + +#define FROB(new_stage) if (EQ(Q##new_stage, XCDR(matchspec))) \ + { \ + stage = new_stage; \ + } + + FROB(initial) + else FROB(final) + else assert(0); +#undef FROB + + } + } +#endif /* MULE */ + + LIST_LOOP(rest, inst_list) + { + Lisp_Object tagged_inst = XCAR (rest); + Lisp_Object tag_set = XCAR (tagged_inst); + Lisp_Object val, the_instantiator; + + if (!device_matches_specifier_tag_set_p (device, tag_set)) + { + continue; + } + + val = XCDR (tagged_inst); + the_instantiator = val; + + if (!NILP(charset) && + !(charset_matches_specifier_tag_set_p (charset, tag_set, stage))) + { + ++respected_charsets; + continue; + } + + if (HAS_SPECMETH_P (sp, instantiate)) + val = call_with_suspended_errors + ((lisp_fn_t) RAW_SPECMETH (sp, instantiate), + Qunbound, Qspecifier, errb, 5, specifier, + matchspec, domain, val, depth); + + if (!UNBOUNDP (val)) + { + unbind_to (count); + UNGCPRO; + if (instantiator) + *instantiator = the_instantiator; + return val; + } + } + + /* We've checked all the tag sets, and checking the charset part of the + specifier never returned 0 (preventing the attempted instantiation), so + there's no need to loop for the second time to avoid checking the + charsets. */ + if (!respected_charsets) + { + unbind_to (count); + UNGCPRO; + return Qunbound; + } + + /* Right, didn't instantiate a specifier last time, perhaps because we + paid attention to the charset-specific aspects of the specifier. Try + again without checking the charset information. + + We can't emulate the approach for devices, defaulting to matching all + character sets for a given specifier, because $random font instantiator + cannot usefully show all character sets, and indeed having it try is a + failure on our part. */ LIST_LOOP (rest, inst_list) { Lisp_Object tagged_inst = XCAR (rest); Lisp_Object tag_set = XCAR (tagged_inst); - - if (device_matches_specifier_tag_set_p (device, tag_set)) + Lisp_Object val, the_instantiator; + + if (!device_matches_specifier_tag_set_p (device, tag_set)) { - Lisp_Object val = XCDR (tagged_inst); - Lisp_Object the_instantiator = val; - - - if (HAS_SPECMETH_P (sp, instantiate)) - val = call_with_suspended_errors - ((lisp_fn_t) RAW_SPECMETH (sp, instantiate), - Qunbound, Qspecifier, errb, 5, specifier, - matchspec, domain, val, depth); - - if (!UNBOUNDP (val)) - { - unbind_to (count); - UNGCPRO; - if (instantiator) - *instantiator = the_instantiator; - return val; - } + continue; + } + + val = XCDR (tagged_inst); + the_instantiator = val; + + if (HAS_SPECMETH_P (sp, instantiate)) + val = call_with_suspended_errors + ((lisp_fn_t) RAW_SPECMETH (sp, instantiate), + Qunbound, Qspecifier, errb, 5, specifier, + matchspec, domain, val, depth); + + if (!UNBOUNDP (val)) + { + unbind_to (count); + UNGCPRO; + if (instantiator) + *instantiator = the_instantiator; + return val; } } @@ -2552,19 +2955,19 @@ return it. Otherwise return Qunbound. */ #define CHECK_INSTANCE_ENTRY(key, matchspec, type) do { \ - Lisp_Object *CIE_inst_list = \ - specifier_get_inst_list (specifier, key, type); \ - if (CIE_inst_list) \ - { \ - Lisp_Object CIE_val = \ - specifier_instance_from_inst_list (specifier, matchspec, \ - domain, *CIE_inst_list, \ - errb, no_quit, depth, \ - instantiator); \ - if (!UNBOUNDP (CIE_val)) \ - return CIE_val; \ - } \ -} while (0) + Lisp_Object *CIE_inst_list = \ + specifier_get_inst_list (specifier, key, type); \ + if (CIE_inst_list) \ + { \ + Lisp_Object CIE_val = \ + specifier_instance_from_inst_list (specifier, matchspec, \ + domain, *CIE_inst_list, \ + errb, no_quit, depth, \ + instantiator); \ + if (!UNBOUNDP (CIE_val)) \ + return CIE_val; \ + } \ + } while (0) /* We accept any window, frame or device domain and do our checking starting from as specific a locale type as we can determine from the @@ -2919,8 +3322,8 @@ 0); } -DEFUN ("specifier-instantiator-from-inst-list", Fspecifier_instantiator_from_inst_list, - 3, 4, 0, /* +DEFUN ("specifier-instantiator-from-inst-list", + Fspecifier_instantiator_from_inst_list, 3, 4, 0, /* Attempt to convert an inst-list into an instance; return instantiator. This is identical to `specifier-instance-from-inst-list' but returns the instantiator used to generate the instance, rather than the instance @@ -2988,17 +3391,17 @@ If you create a built-in specifier, you should do the following: - Make sure the file you create the specifier in has a - specifier_vars_of_foo() function. If not, create it, declare it in - symsinit.h, and make sure it's called in the appropriate place in - emacs.c. + specifier_vars_of_foo() function. If not, create it, declare it in + symsinit.h, and make sure it's called in the appropriate place in + emacs.c. - In specifier_vars_of_foo(), do a DEFVAR_SPECIFIER(), followed by - initializing the specifier using Fmake_specifier(), followed by - set_specifier_fallback(), followed (optionally) by - set_specifier_caching(). + initializing the specifier using Fmake_specifier(), followed by + set_specifier_fallback(), followed (optionally) by + set_specifier_caching(). - If you used set_specifier_caching(), make sure to create the - appropriate value-changed functions. Also make sure to add the - appropriate slots where the values are cached to frameslots.h and - winslots.h. + appropriate value-changed functions. Also make sure to add the + appropriate slots where the values are cached to frameslots.h and + winslots.h. Do a grep for menubar_visible_p for an example. */ @@ -3025,7 +3428,7 @@ sp->caching = alloc_lrecord_type (struct specifier_caching, &lrecord_specifier_caching); #else /* not NEW_GC */ - sp->caching = xnew_and_zero (struct specifier_caching); + sp->caching = xnew_and_zero (struct specifier_caching); #endif /* not NEW_GC */ sp->caching->offset_into_struct_window = struct_window_offset; sp->caching->value_changed_in_window = value_changed_in_window; @@ -3326,10 +3729,10 @@ DEFINE_SPECIFIER_TYPE (display_table); -#define VALID_SINGLE_DISPTABLE_INSTANTIATOR_P(instantiator) \ - (VECTORP (instantiator) \ - || (CHAR_TABLEP (instantiator) \ - && (XCHAR_TABLE_TYPE (instantiator) == CHAR_TABLE_TYPE_CHAR \ +#define VALID_SINGLE_DISPTABLE_INSTANTIATOR_P(instantiator) \ + (VECTORP (instantiator) \ + || (CHAR_TABLEP (instantiator) \ + && (XCHAR_TABLE_TYPE (instantiator) == CHAR_TABLE_TYPE_CHAR \ || XCHAR_TABLE_TYPE (instantiator) == CHAR_TABLE_TYPE_GENERIC)) \ || RANGE_TABLEP (instantiator)) @@ -3354,7 +3757,7 @@ lose: dead_wrong_type_argument (display_table_specifier_methods->predicate_symbol, - instantiator); + instantiator); } } } @@ -3408,7 +3811,8 @@ DEFSUBR (Fdefine_specifier_tag); DEFSUBR (Fdevice_matching_specifier_tag_list); DEFSUBR (Fspecifier_tag_list); - DEFSUBR (Fspecifier_tag_predicate); + DEFSUBR (Fspecifier_tag_device_predicate); + DEFSUBR (Fspecifier_tag_charset_predicate); DEFSUBR (Fcheck_valid_instantiator); DEFSUBR (Fvalid_instantiator_p); @@ -3509,4 +3913,7 @@ Vunlock_ghost_specifiers = Qnil; staticpro (&Vunlock_ghost_specifiers); + + Vcharset_tag_lists = make_vector(NUM_LEADING_BYTES, Qnil); + staticpro (&Vcharset_tag_lists); }