File indexing completed on 2024-04-14 14:20:15
0001 /* This file is part of the KDE project 0002 Copyright (C) 2000 Keunwoo Lee <klee@cs.washington.edu> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 GNU Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #ifndef KACCELGEN_H 0021 #define KACCELGEN_H 0022 0023 #include <QMap> 0024 #include <QString> 0025 #include <QStringList> 0026 0027 /** 0028 * Provides functions that, given a collection of QStrings, will 0029 * automatically and intelligently assign menu accelerators to the 0030 * QStrings in the collection. 0031 * 0032 * NOTE: When this file speaks of "accelerators", we really mean 0033 * accelerators as defined by the KDE User Interface Guidelines. We 0034 * do NOT mean "shortcuts", which are what's handled by most other KDE 0035 * libraries with "accel" in the name. 0036 * 0037 * In the Qt library, the mechanism for adding a keyboard accelerator 0038 * to a menu item is to insert an '&' before the letter. Since we 0039 * usually don't want to disturb the original collection, the idiom in 0040 * these functions is to populate a "target" QStringList parameter 0041 * with the input collectin's QStrings, plus possibly some added '&' 0042 * characters. 0043 * 0044 * That is the mechanism. Here is the policy, in order of decreasing 0045 * importance (it may seem like these are implementation details, but 0046 * IMHO the policy is an important part of the interface): 0047 * 0048 * 1. If the string already contains an '&' character, skip this 0049 * string, because we consider such strings to be "user-specified" 0050 * accelerators. 0051 * 0052 * 2. No accelerator may clash with a previously defined accelerator, 0053 * including any legal (alphanumeric) user-specified accelerator 0054 * anywhere in the collection 0055 * 0056 * 3. Prefer alphanumerics at the start of the string. 0057 * 0058 * 4. Otherwise, prefer alphanumerics at the start of a word. 0059 * 0060 * 5. Otherwise, choose any alphanumeric character not already 0061 * taken. If no such character is available, give up & skip this 0062 * string. 0063 * 0064 * A typical use of these functions would be to automatically assign 0065 * accelerators to a dynamically populated popup menu. For example, 0066 * the core code was written to automatically set accelerators for the 0067 * "Load View Profile" popup menu for Konqueror. We quickly realized 0068 * that it would be useful to make this facility more generally 0069 * available, so I abstracted it out into a set of templates. 0070 * 0071 * TODO: 0072 * 0073 * + Add sugar functions for more collections. 0074 * 0075 * + Add more Deref classes so that we can access a wider variety of 0076 * collections. 0077 * 0078 * @deprecated 0079 * */ 0080 namespace KAccelGen 0081 { 0082 0083 // HELPERS 0084 0085 /** 0086 * Static dereference class, for use as a template parameter. 0087 */ 0088 template <class Iter> 0089 class Deref 0090 { 0091 public: 0092 static QString deref(Iter i) 0093 { 0094 return *i; 0095 } 0096 }; 0097 0098 /** 0099 * Static dereference class that calls the key() method on its 0100 * target; for use as a template parameter. 0101 */ 0102 template <class Iter> 0103 class Deref_Key 0104 { 0105 public: 0106 static QString deref(Iter i) 0107 { 0108 return i.key(); 0109 } 0110 }; 0111 0112 /** 0113 * Helper to determine if the given offset in the string could be a 0114 * legal alphanumeric accelerator. 0115 * 0116 * @param str base string 0117 * @param index offset to check 0118 */ 0119 inline bool 0120 isLegalAccelerator(const QString &str, int index) 0121 { 0122 return index >= 0 && index < str.length() 0123 && str[index].isLetterOrNumber(); 0124 } 0125 0126 /** 0127 * Loads all legal predefined accelerators in the (implicitly 0128 * specified) collection into the given QMap. 0129 * 0130 * @param begin start iterator 0131 * @param end (last+1) iterator 0132 * @param keys map to store output 0133 */ 0134 template <class Iter, class Deref> 0135 inline void 0136 loadPredefined(Iter begin, Iter end, QMap<QChar, bool> &keys) 0137 { 0138 for (Iter i = begin; i != end; ++i) { 0139 QString item = Deref::deref(i); 0140 int user_ampersand = item.indexOf(QLatin1Char('&')); 0141 if (user_ampersand >= 0) { 0142 // Sanity check. Note that we don't try to find an 0143 // accelerator if the user shoots him/herself in the foot 0144 // by adding a bad '&'. 0145 if (isLegalAccelerator(item, user_ampersand + 1)) { 0146 keys.insert(item[user_ampersand + 1], true); 0147 } 0148 } 0149 } 0150 } 0151 0152 // /////////////////////////////////////////////////////////////////// 0153 // MAIN USER FUNCTIONS 0154 0155 /** 0156 * Main, maximally flexible template function that assigns 0157 * accelerators to the elements of a collection of QStrings. Clients 0158 * will seldom use this directly, as it's usually easier to use one of 0159 * the wrapper functions that simply takes a collection (see below). 0160 * 0161 * The Deref template parameter is a class containing a static 0162 * dereferencing function, modeled after the comparison class C in 0163 * Stroustrup 13.4. 0164 * 0165 * @param begin (you know) 0166 * @param end (you know) 0167 * @param target collection to store generated strings 0168 */ 0169 template <class Iter, class Iter_Deref > 0170 void 0171 generate(Iter begin, Iter end, QStringList &target) 0172 { 0173 // Will keep track of used accelerator chars 0174 QMap<QChar, bool> used_accels; 0175 0176 // Prepass to detect manually user-coded accelerators 0177 loadPredefined<Iter, Iter_Deref>(begin, end, used_accels); 0178 0179 // Main pass 0180 for (Iter i = begin; i != end; ++i) { 0181 QString item = Iter_Deref::deref(i); 0182 0183 // Attempt to find a good accelerator, but only if the user 0184 // has not manually hardcoded one. 0185 int user_ampersand = item.indexOf(QLatin1Char('&')); 0186 if (user_ampersand < 0 || item[user_ampersand + 1] == QLatin1Char('&')) { 0187 bool found = false; 0188 int j; 0189 0190 // Check word-starting letters first. 0191 for (j = 0; j < item.length(); ++j) { 0192 if (isLegalAccelerator(item, j) 0193 && !used_accels.contains(item[j]) 0194 && (0 == j || (j > 0 && item[j - 1].isSpace()))) { 0195 found = true; 0196 break; 0197 } 0198 } 0199 0200 if (!found) { 0201 // No word-starting letter; search for any letter. 0202 for (j = 0; j < item.length(); ++j) { 0203 if (isLegalAccelerator(item, j) 0204 && !used_accels.contains(item[j])) { 0205 found = true; 0206 break; 0207 } 0208 } 0209 } 0210 0211 if (found) { 0212 // Both upper and lower case marked as used 0213 used_accels.insert(item[j].toUpper(), true); 0214 used_accels.insert(item[j].toLower(), true); 0215 item.insert(j, QLatin1Char('&')); 0216 } 0217 } 0218 0219 target.append(item); 0220 } 0221 } 0222 0223 /** 0224 * Another convenience function; looks up the key instead of 0225 * dereferencing directly for the given iterator. 0226 * 0227 * @param begin 0228 * @param end 0229 * @param target 0230 */ 0231 template <class Iter> 0232 inline void 0233 generateFromKeys(Iter begin, Iter end, QStringList &target) 0234 { 0235 generate< Iter, Deref_Key<Iter> >(begin, end, target); 0236 } 0237 0238 /** 0239 * Convenience function; generates accelerators for all the items in 0240 * a QStringList. 0241 * 0242 * @param source Strings for which to generate accelerators 0243 * @param target Output for accelerator-added strings */ 0244 inline void 0245 generate(const QStringList &source, QStringList &target) 0246 { 0247 generate<QStringList::ConstIterator, Deref<QStringList::ConstIterator> >(source.begin(), source.end(), target); 0248 } 0249 0250 /** 0251 * Convenience function; generates accelerators for all the values in 0252 * a QMap<T,QString>. 0253 * 0254 * @param source Map with input strings as VALUES. 0255 * @param target Output for accelerator-added strings */ 0256 template <class Key> 0257 inline void 0258 generateFromValues(const QMap<Key, QString> &source, QStringList &target) 0259 { 0260 generate<typename QMap<Key, QString>::ConstIterator, Deref_Key<typename QMap<Key, QString>::ConstIterator> >(source.begin(), source.end(), target); 0261 } 0262 0263 /** 0264 * Convenience function; generates an accelerator mapping from all the 0265 * keys in a QMap<QString,T> 0266 * 0267 * @param source Map with input strings as KEYS. 0268 * @param target Output for accelerator-added strings */ 0269 template <class Data> 0270 inline void 0271 generateFromKeys(const QMap<QString, Data> &source, QStringList &target) 0272 { 0273 generateFromKeys(source.begin(), source.end(), target); 0274 } 0275 0276 } // end namespace KAccelGen 0277 0278 #endif 0279