/* This file is part of the KDE project Copyright (C) 2000 Keunwoo Lee 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 TDEACCELGEN_H #define TDEACCELGEN_H #include #include #include #include /** * 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" TQStringList 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 TDEAccelGen { // HELPERS /** * Static dereference class, for use as a template parameter. */ template class Deref { public: static TQString deref(Iter i) { return *i; } }; /** * Static dereference class that calls the key() method on its * target; for use as a template parameter. */ template class Deref_Key { public: static TQString 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 TQString& str, uint index) { return index < str.length() && str[index].isLetterOrNumber(); } /** * Loads all legal predefined accelerators in the (implicitly * specified) collection into the given TQMap. * * @param begin start iterator * @param end (last+1) iterator * @param keys map to store output */ template inline void loadPredefined(Iter begin, Iter end, TQMap& keys) { for (Iter i = begin; i != end; ++i) { TQString item = Deref::deref(i); int user_ampersand = item.find(TQChar('&')); 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 void generate(Iter begin, Iter end, TQStringList& target) { // Will keep track of used accelerator chars TQMap used_accels; // Prepass to detect manually user-coded accelerators loadPredefined(begin, end, used_accels); // Main pass for (Iter i = begin; i != end; ++i) { TQString 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(TQChar('&')); 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,TQChar('&')); } } 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 inline void generateFromKeys(Iter begin, Iter end, TQStringList& target) { generate< Iter, Deref_Key >(begin, end, target); } /** * Convenience function; generates accelerators for all the items in * a TQStringList. * * @param source Strings for which to generate accelerators * @param target Output for accelerator-added strings */ inline void generate(const TQStringList& source, TQStringList& target) { generate >(source.begin(), source.end(), target); } /** * Convenience function; generates accelerators for all the values in * a TQMap. * * @param source Map with input strings as VALUES. * @param target Output for accelerator-added strings */ template inline void generateFromValues(const TQMap& source, TQStringList& target) { generate, Deref_Key > >(source.begin(), source.end(), target); } /** * Convenience function; generates an accelerator mapping from all the * keys in a TQMap * * @param source Map with input strings as KEYS. * @param target Output for accelerator-added strings */ template inline void generateFromKeys(const TQMap& source, TQStringList& target) { generateFromKeys(source.begin(), source.end(), target); } } // end namespace TDEAccelGen #endif