home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / SLAX 6.0.8 / slax-6.0.8.iso / slax / base / 006-devel.lzm / usr / include / kaccelgen.h < prev    next >
Encoding:
C/C++ Source or Header  |  2007-05-14  |  8.6 KB  |  281 lines

  1. /*  This file is part of the KDE project
  2.     Copyright (C) 2000 Keunwoo Lee <klee@cs.washington.edu>
  3.  
  4.     This program is free software; you can redistribute it and/or
  5.     modify it under the terms of the GNU Library General Public
  6.     License as published by the Free Software Foundation; either
  7.     version 2 of the License, or (at your option) any later version.
  8.  
  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 Library General Public License for more details.
  13.  
  14.     You should have received a copy of the GNU Library General Public License
  15.     along with this library; see the file COPYING.LIB. If not, write to
  16.     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17.     Boston, MA  02110-1301, USA.
  18. */
  19.  
  20. #ifndef KACCELGEN_H
  21. #define KACCELGEN_H
  22.  
  23. #include <qmap.h>
  24. #include <qstring.h>
  25. #include <qstringlist.h>
  26.  
  27. #include <kdelibs_export.h>
  28.  
  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.
  33.  *
  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.
  38.  *
  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.
  45.  *
  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):
  49.  *
  50.  * 1. If the string already contains an '&' character, skip this
  51.  * string, because we consider such strings to be "user-specified"
  52.  * accelerators.
  53.  *
  54.  * 2. No accelerator may clash with a previously defined accelerator,
  55.  * including any legal (alphanumeric) user-specified accelerator
  56.  * anywhere in the collection
  57.  *
  58.  * 3. Prefer alphanumerics at the start of the string.
  59.  *
  60.  * 4. Otherwise, prefer alphanumerics at the start of a word.
  61.  *
  62.  * 5. Otherwise, choose any alphanumeric character not already
  63.  * taken. If no such character is available, give up & skip this
  64.  * string.
  65.  *
  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.
  72.  *
  73.  * TODO:
  74.  *
  75.  * + Add sugar functions for more collections.
  76.  *
  77.  * + Add more Deref classes so that we can access a wider variety of
  78.  * collections.
  79.  * */
  80. namespace KAccelGen
  81. {
  82.  
  83. // HELPERS
  84.  
  85. /**
  86.  * Static dereference class, for use as a template parameter.
  87.  */
  88. template <class Iter>
  89. class Deref
  90. {
  91. public:
  92.     static QString deref(Iter i) { return *i; }
  93. };
  94.  
  95. /**
  96.  * Static dereference class that calls the key() method on its
  97.  * target; for use as a template parameter.
  98.  */
  99. template <class Iter>
  100. class Deref_Key
  101. {
  102. public:
  103.     static QString deref(Iter i) { return i.key(); }
  104. };
  105.  
  106. /**
  107.  * Helper to determine if the given offset in the string could be a
  108.  * legal alphanumeric accelerator.
  109.  *
  110.  * @param str   base string
  111.  * @param index offset to check
  112.  */
  113. inline bool
  114. isLegalAccelerator(const QString& str, uint index)
  115. {
  116.     return index < str.length()
  117.         && str[index].isLetterOrNumber();
  118. }
  119.  
  120. /**
  121.  * Loads all legal predefined accelerators in the (implicitly
  122.  * specified) collection into the given QMap.
  123.  *
  124.  * @param begin start iterator
  125.  * @param end   (last+1) iterator
  126.  * @param keys map to store output
  127.  */
  128. template <class Iter, class Deref>
  129. inline void
  130. loadPredefined(Iter begin, Iter end, QMap<QChar,bool>& keys)
  131. {
  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);
  141.             }
  142.         }
  143.     }
  144. }
  145.  
  146.  
  147. // ///////////////////////////////////////////////////////////////////
  148. // MAIN USER FUNCTIONS
  149.  
  150.  
  151. /**
  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).
  156.  *
  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.
  160.  *
  161.  * @param begin  (you know)
  162.  * @param end    (you know)
  163.  * @param target collection to store generated strings
  164.  */
  165. template <class Iter, class Iter_Deref >
  166. void
  167. generate(Iter begin, Iter end, QStringList& target)
  168. {
  169.     // Will keep track of used accelerator chars
  170.     QMap<QChar,bool> used_accels;
  171.  
  172.     // Prepass to detect manually user-coded accelerators
  173.     loadPredefined<Iter,Iter_Deref>(begin, end, used_accels);
  174.  
  175.     // Main pass
  176.     for (Iter i = begin; i != end; ++i) {
  177.         QString item = Iter_Deref::deref(i);
  178.  
  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;
  186.  
  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;
  195.                 }
  196.             }
  197.  
  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;
  206.                     }
  207.                 }
  208.             }
  209.  
  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('&'));
  215.             }
  216.         }
  217.  
  218.         target.append( item );
  219.     }
  220. }
  221.  
  222. /**
  223.  * Another convenience function; looks up the key instead of
  224.  * dereferencing directly for the given iterator.
  225.  *
  226.  * @param begin
  227.  * @param end
  228.  * @param target
  229.  */
  230. template <class Iter>
  231. inline void
  232. generateFromKeys(Iter begin, Iter end, QStringList& target)
  233. {
  234.     generate< Iter, Deref_Key<Iter> >(begin, end, target);
  235. }
  236.  
  237.  
  238. /**
  239.  * Convenience function; generates accelerators for all the items in
  240.  * a QStringList.
  241.  *
  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)
  246. {
  247.     generate<QStringList::ConstIterator, Deref<QStringList::ConstIterator> >(source.begin(), source.end(), target);
  248. }
  249.  
  250. /**
  251.  * Convenience function; generates accelerators for all the values in
  252.  * a QMap<T,QString>.
  253.  *
  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)
  259. {
  260.     generate<QMapConstIterator<Key,QString>, Deref_Key<QMapConstIterator<Key,QString> > >(source.begin(), source.end(), target);
  261. }
  262.  
  263. /**
  264.  * Convenience function; generates an accelerator mapping from all the
  265.  * keys in a QMap<QString,T>
  266.  *
  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)
  272. {
  273.     generateFromKeys(source.begin(), source.end(), target);
  274. }
  275.  
  276.  
  277. } // end namespace KAccelGen
  278.  
  279. #endif
  280.  
  281.