summaryrefslogtreecommitdiffstats
path: root/kdeui/kaccelgen.h
diff options
context:
space:
mode:
Diffstat (limited to 'kdeui/kaccelgen.h')
-rw-r--r--kdeui/kaccelgen.h280
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
+