diff options
Diffstat (limited to 'kdeui/kaccelgen.h')
-rw-r--r-- | kdeui/kaccelgen.h | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/kdeui/kaccelgen.h b/kdeui/kaccelgen.h new file mode 100644 index 000000000..2d5d69549 --- /dev/null +++ b/kdeui/kaccelgen.h @@ -0,0 +1,280 @@ +/* This file is part of the KDE project + Copyright (C) 2000 Keunwoo Lee <klee@cs.washington.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KACCELGEN_H +#define KACCELGEN_H + +#include <qmap.h> +#include <qstring.h> +#include <qstringlist.h> + +#include <kdelibs_export.h> + +/** + * Provides functions that, given a collection of QStrings, will + * automatically and intelligently assign menu accelerators to the + * QStrings in the collection. + * + * NOTE: When this file speaks of "accelerators", we really mean + * accelerators as defined by the KDE User Interface Guidelines. We + * do NOT mean "shortcuts", which are what's handled by most other KDE + * libraries with "accel" in the name. + * + * In the Qt library, the mechanism for adding a keyboard accelerator + * to a menu item is to insert an '&' before the letter. Since we + * usually don't want to disturb the original collection, the idiom in + * these functions is to populate a "target" QStringList parameter + * with the input collectin's QStrings, plus possibly some added '&' + * characters. + * + * That is the mechanism. Here is the policy, in order of decreasing + * importance (it may seem like these are implementation details, but + * IMHO the policy is an important part of the interface): + * + * 1. If the string already contains an '&' character, skip this + * string, because we consider such strings to be "user-specified" + * accelerators. + * + * 2. No accelerator may clash with a previously defined accelerator, + * including any legal (alphanumeric) user-specified accelerator + * anywhere in the collection + * + * 3. Prefer alphanumerics at the start of the string. + * + * 4. Otherwise, prefer alphanumerics at the start of a word. + * + * 5. Otherwise, choose any alphanumeric character not already + * taken. If no such character is available, give up & skip this + * string. + * + * A typical use of these functions would be to automatically assign + * accelerators to a dynamically populated popup menu. For example, + * the core code was written to automatically set accelerators for the + * "Load View Profile" popup menu for Konqueror. We quickly realized + * that it would be useful to make this facility more generally + * available, so I abstracted it out into a set of templates. + * + * TODO: + * + * + Add sugar functions for more collections. + * + * + Add more Deref classes so that we can access a wider variety of + * collections. + * */ +namespace KAccelGen +{ + +// HELPERS + +/** + * Static dereference class, for use as a template parameter. + */ +template <class Iter> +class Deref +{ +public: + static QString deref(Iter i) { return *i; } +}; + +/** + * Static dereference class that calls the key() method on its + * target; for use as a template parameter. + */ +template <class Iter> +class Deref_Key +{ +public: + static QString deref(Iter i) { return i.key(); } +}; + +/** + * Helper to determine if the given offset in the string could be a + * legal alphanumeric accelerator. + * + * @param str base string + * @param index offset to check + */ +inline bool +isLegalAccelerator(const QString& str, uint index) +{ + return index < str.length() + && str[index].isLetterOrNumber(); +} + +/** + * Loads all legal predefined accelerators in the (implicitly + * specified) collection into the given QMap. + * + * @param begin start iterator + * @param end (last+1) iterator + * @param keys map to store output + */ +template <class Iter, class Deref> +inline void +loadPredefined(Iter begin, Iter end, QMap<QChar,bool>& keys) +{ + for (Iter i = begin; i != end; ++i) { + QString item = Deref::deref(i); + int user_ampersand = item.find(QChar('&')); + if( user_ampersand >= 0 ) { + // Sanity check. Note that we don't try to find an + // accelerator if the user shoots him/herself in the foot + // by adding a bad '&'. + if( isLegalAccelerator(item, user_ampersand+1) ) { + keys.insert(item[user_ampersand+1], true); + } + } + } +} + + +// /////////////////////////////////////////////////////////////////// +// MAIN USER FUNCTIONS + + +/** + * Main, maximally flexible template function that assigns + * accelerators to the elements of a collection of QStrings. Clients + * will seldom use this directly, as it's usually easier to use one of + * the wrapper functions that simply takes a collection (see below). + * + * The Deref template parameter is a class containing a static + * dereferencing function, modeled after the comparison class C in + * Stroustrup 13.4. + * + * @param begin (you know) + * @param end (you know) + * @param target collection to store generated strings + */ +template <class Iter, class Iter_Deref > +void +generate(Iter begin, Iter end, QStringList& target) +{ + // Will keep track of used accelerator chars + QMap<QChar,bool> used_accels; + + // Prepass to detect manually user-coded accelerators + loadPredefined<Iter,Iter_Deref>(begin, end, used_accels); + + // Main pass + for (Iter i = begin; i != end; ++i) { + QString item = Iter_Deref::deref(i); + + // Attempt to find a good accelerator, but only if the user + // has not manually hardcoded one. + int user_ampersand = item.find(QChar('&')); + if( user_ampersand < 0 || item[user_ampersand+1] == '&') { + bool found = false; + uint found_idx; + uint j; + + // Check word-starting letters first. + for( j=0; j < item.length(); ++j ) { + if( isLegalAccelerator(item, j) + && !used_accels.contains(item[j]) + && (0 == j || (j > 0 && item[j-1].isSpace())) ) { + found = true; + found_idx = j; + break; + } + } + + if( !found ) { + // No word-starting letter; search for any letter. + for( j=0; j < item.length(); ++j ) { + if( isLegalAccelerator(item, j) + && !used_accels.contains(item[j]) ) { + found = true; + found_idx = j; + break; + } + } + } + + if( found ) { + // Both upper and lower case marked as used + used_accels.insert(item[j].upper(),true); + used_accels.insert(item[j].lower(),true); + item.insert(j,QChar('&')); + } + } + + target.append( item ); + } +} + +/** + * Another convenience function; looks up the key instead of + * dereferencing directly for the given iterator. + * + * @param begin + * @param end + * @param target + */ +template <class Iter> +inline void +generateFromKeys(Iter begin, Iter end, QStringList& target) +{ + generate< Iter, Deref_Key<Iter> >(begin, end, target); +} + + +/** + * Convenience function; generates accelerators for all the items in + * a QStringList. + * + * @param source Strings for which to generate accelerators + * @param target Output for accelerator-added strings */ +inline void +generate(const QStringList& source, QStringList& target) +{ + generate<QStringList::ConstIterator, Deref<QStringList::ConstIterator> >(source.begin(), source.end(), target); +} + +/** + * Convenience function; generates accelerators for all the values in + * a QMap<T,QString>. + * + * @param source Map with input strings as VALUES. + * @param target Output for accelerator-added strings */ +template <class Key> +inline void +generateFromValues(const QMap<Key,QString>& source, QStringList& target) +{ + generate<QMapConstIterator<Key,QString>, Deref_Key<QMapConstIterator<Key,QString> > >(source.begin(), source.end(), target); +} + +/** + * Convenience function; generates an accelerator mapping from all the + * keys in a QMap<QString,T> + * + * @param source Map with input strings as KEYS. + * @param target Output for accelerator-added strings */ +template <class Data> +inline void +generateFromKeys(const QMap<QString,Data>& source, QStringList& target) +{ + generateFromKeys(source.begin(), source.end(), target); +} + + +} // end namespace KAccelGen + +#endif + |