subtraction of already painted area: be fool and
[kdelibs.git] / kdeui / kaccelgen.h
blobaea71431f8601dd14ecdb4a3e52572f35d065f8d
1 /* This file is part of the KDE project
2 Copyright (C) 2000 Keunwoo Lee <klee@cs.washington.edu>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.
18 // -*- mode: c++; c-basic-offset: 2 -*-
20 #ifndef KACCELGEN_H
21 #define KACCELGEN_H
23 #include <qmap.h>
24 #include <qstring.h>
25 #include <qstringlist.h>
27 #include <kdelibs_export.h>
29 /**
30 * Provides functions that, given a collection of QStrings, will
31 * automatically and intelligently assign menu accelerators to the
32 * QStrings in the collection.
34 * NOTE: When this file speaks of "accelerators", we really mean
35 * accelerators as defined by the KDE User Interface Guidelines. We
36 * do NOT mean "shortcuts", which are what's handled by most other KDE
37 * libraries with "accel" in the name.
39 * In the Qt library, the mechanism for adding a keyboard accelerator
40 * to a menu item is to insert an '&' before the letter. Since we
41 * usually don't want to disturb the original collection, the idiom in
42 * these functions is to populate a "target" QStringList parameter
43 * with the input collectin's QStrings, plus possibly some added '&'
44 * characters.
46 * That is the mechanism. Here is the policy, in order of decreasing
47 * importance (it may seem like these are implementation details, but
48 * IMHO the policy is an important part of the interface):
50 * 1. If the string already contains an '&' character, skip this
51 * string, because we consider such strings to be "user-specified"
52 * accelerators.
54 * 2. No accelerator may clash with a previously defined accelerator,
55 * including any legal (alphanumeric) user-specified accelerator
56 * anywhere in the collection
58 * 3. Prefer alphanumerics at the start of the string.
60 * 4. Otherwise, prefer alphanumerics at the start of a word.
62 * 5. Otherwise, choose any alphanumeric character not already
63 * taken. If no such character is available, give up & skip this
64 * string.
66 * A typical use of these functions would be to automatically assign
67 * accelerators to a dynamically populated popup menu. For example,
68 * the core code was written to automatically set accelerators for the
69 * "Load View Profile" popup menu for Konqueror. We quickly realized
70 * that it would be useful to make this facility more generally
71 * available, so I abstracted it out into a set of templates.
73 * TODO:
75 * + Add sugar functions for more collections.
77 * + Add more Deref classes so that we can access a wider variety of
78 * collections.
79 * */
80 namespace KAccelGen
83 // HELPERS
85 /**
86 * Static dereference class, for use as a template parameter.
88 template <class Iter>
89 class Deref
91 public:
92 static QString deref(Iter i) { return *i; }
95 /**
96 * Static dereference class that calls the key() method on its
97 * target; for use as a template parameter.
99 template <class Iter>
100 class Deref_Key
102 public:
103 static QString deref(Iter i) { return i.key(); }
107 * Helper to determine if the given offset in the string could be a
108 * legal alphanumeric accelerator.
110 * @param str base string
111 * @param index offset to check
113 inline bool
114 isLegalAccelerator(const QString& str, uint index)
116 return index < str.length()
117 && str[index].isLetterOrNumber();
121 * Loads all legal predefined accelerators in the (implicitly
122 * specified) collection into the given QMap.
124 * @param begin start iterator
125 * @param end (last+1) iterator
126 * @param keys map to store output
128 template <class Iter, class Deref>
129 inline void
130 loadPredefined(Iter begin, Iter end, QMap<QChar,bool>& keys)
132 for (Iter i = begin; i != end; ++i) {
133 QString item = Deref::deref(i);
134 int user_ampersand = item.find(QChar('&'));
135 if( user_ampersand >= 0 ) {
136 // Sanity check. Note that we don't try to find an
137 // accelerator if the user shoots him/herself in the foot
138 // by adding a bad '&'.
139 if( isLegalAccelerator(item, user_ampersand+1) ) {
140 keys.insert(item[user_ampersand+1], true);
147 // ///////////////////////////////////////////////////////////////////
148 // MAIN USER FUNCTIONS
152 * Main, maximally flexible template function that assigns
153 * accelerators to the elements of a collection of QStrings. Clients
154 * will seldom use this directly, as it's usually easier to use one of
155 * the wrapper functions that simply takes a collection (see below).
157 * The Deref template parameter is a class containing a static
158 * dereferencing function, modeled after the comparison class C in
159 * Stroustrup 13.4.
161 * @param begin (you know)
162 * @param end (you know)
163 * @param target collection to store generated strings
165 template <class Iter, class Iter_Deref >
166 void
167 generate(Iter begin, Iter end, QStringList& target)
169 // Will keep track of used accelerator chars
170 QMap<QChar,bool> used_accels;
172 // Prepass to detect manually user-coded accelerators
173 loadPredefined<Iter,Iter_Deref>(begin, end, used_accels);
175 // Main pass
176 for (Iter i = begin; i != end; ++i) {
177 QString item = Iter_Deref::deref(i);
179 // Attempt to find a good accelerator, but only if the user
180 // has not manually hardcoded one.
181 int user_ampersand = item.find(QChar('&'));
182 if( user_ampersand < 0 || item[user_ampersand+1] == '&') {
183 bool found = false;
184 uint found_idx;
185 uint j;
187 // Check word-starting letters first.
188 for( j=0; j < item.length(); ++j ) {
189 if( isLegalAccelerator(item, j)
190 && !used_accels.contains(item[j])
191 && (0 == j || j > 0 && item[j-1].isSpace()) ) {
192 found = true;
193 found_idx = j;
194 break;
198 if( !found ) {
199 // No word-starting letter; search for any letter.
200 for( j=0; j < item.length(); ++j ) {
201 if( isLegalAccelerator(item, j)
202 && !used_accels.contains(item[j]) ) {
203 found = true;
204 found_idx = j;
205 break;
210 if( found ) {
211 // Both upper and lower case marked as used
212 used_accels.insert(item[j].upper(),true);
213 used_accels.insert(item[j].lower(),true);
214 item.insert(j,QChar('&'));
218 target.append( item );
223 * Another convenience function; looks up the key instead of
224 * dereferencing directly for the given iterator.
226 * @param begin
227 * @param end
228 * @param target
230 template <class Iter>
231 inline void
232 generateFromKeys(Iter begin, Iter end, QStringList& target)
234 generate< Iter, Deref_Key<Iter> >(begin, end, target);
239 * Convenience function; generates accelerators for all the items in
240 * a QStringList.
242 * @param source Strings for which to generate accelerators
243 * @param target Output for accelerator-added strings */
244 inline void
245 generate(const QStringList& source, QStringList& target)
247 generate<QStringList::ConstIterator, Deref<QStringList::ConstIterator> >(source.begin(), source.end(), target);
251 * Convenience function; generates accelerators for all the values in
252 * a QMap<T,QString>.
254 * @param source Map with input strings as VALUES.
255 * @param target Output for accelerator-added strings */
256 template <class Key>
257 inline void
258 generateFromValues(const QMap<Key,QString>& source, QStringList& target)
260 generate<QMapConstIterator<Key,QString>, Deref_Key<QMapConstIterator<Key,QString> > >(source.begin(), source.end(), target);
264 * Convenience function; generates an accelerator mapping from all the
265 * keys in a QMap<QString,T>
267 * @param source Map with input strings as KEYS.
268 * @param target Output for accelerator-added strings */
269 template <class Data>
270 inline void
271 generateFromKeys(const QMap<QString,Data>& source, QStringList& target)
273 generateFromKeys(source.begin(), source.end(), target);
277 } // end namespace KAccelGen
279 #endif