You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
8.6 KiB
281 lines
8.6 KiB
/* 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
|
|
|