author | mickeyl <mickeyl> | 2003-03-28 15:11:52 (UTC) |
---|---|---|
committer | mickeyl <mickeyl> | 2003-03-28 15:11:52 (UTC) |
commit | 11304d02942e9fa493e4e80943a828f9c65f6772 (patch) (side-by-side diff) | |
tree | a0223c10c067e1afc70d15c2b82be3f3c15e41ae /libopie2/qt3 | |
parent | b271d575fa05cf570a1a829136517761bd47e69b (diff) | |
download | opie-11304d02942e9fa493e4e80943a828f9c65f6772.zip opie-11304d02942e9fa493e4e80943a828f9c65f6772.tar.gz opie-11304d02942e9fa493e4e80943a828f9c65f6772.tar.bz2 |
skeleton and the start of libopie2, please read README, ROADMAP and STATUS and comment...
-rw-r--r-- | libopie2/qt3/opiecore/ocompletion.cpp | 1061 | ||||
-rw-r--r-- | libopie2/qt3/opiecore/ocompletion.h | 603 | ||||
-rw-r--r-- | libopie2/qt3/opiecore/ocompletionbase.cpp | 171 | ||||
-rw-r--r-- | libopie2/qt3/opiecore/ocompletionbase.h | 403 | ||||
-rw-r--r-- | libopie2/qt3/opiecore/opair.h | 99 | ||||
-rw-r--r-- | libopie2/qt3/opiecore/osortablevaluelist.h | 117 | ||||
-rw-r--r-- | libopie2/qt3/opiecore/otl.h | 325 | ||||
-rw-r--r-- | libopie2/qt3/opieui/ocombobox.cpp | 666 | ||||
-rw-r--r-- | libopie2/qt3/opieui/ocombobox.h | 790 | ||||
-rw-r--r-- | libopie2/qt3/opieui/ocompletionbox.cpp | 408 | ||||
-rw-r--r-- | libopie2/qt3/opieui/ocompletionbox.h | 232 | ||||
-rw-r--r-- | libopie2/qt3/opieui/oeditlistbox.cpp | 416 | ||||
-rw-r--r-- | libopie2/qt3/opieui/oeditlistbox.h | 250 | ||||
-rw-r--r-- | libopie2/qt3/opieui/ojanuswidget.cpp | 1116 | ||||
-rw-r--r-- | libopie2/qt3/opieui/ojanuswidget.h | 551 | ||||
-rw-r--r-- | libopie2/qt3/opieui/olineedit.cpp | 729 | ||||
-rw-r--r-- | libopie2/qt3/opieui/olineedit.h | 498 |
17 files changed, 8435 insertions, 0 deletions
diff --git a/libopie2/qt3/opiecore/ocompletion.cpp b/libopie2/qt3/opiecore/ocompletion.cpp new file mode 100644 index 0000000..7b263ab --- a/dev/null +++ b/libopie2/qt3/opiecore/ocompletion.cpp @@ -0,0 +1,1061 @@ +/* + This file is part of the Opie Project + Originally part of the KDE Project + Copyright (C) 1999,2000,2001 Carsten Pfeiffer <pfeiffer@kde.org> + =. + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <opie2/ocompletion.h> + +class OCompTreeNode; + +/**************************************************************************************************/ +/* OCompTreeNodeList +/**************************************************************************************************/ + +class OCompTreeNodeList +{ +public: + OCompTreeNodeList() : first(0), last(0), m_count(0) {} + OCompTreeNode *begin() const { return first; } + OCompTreeNode *end() const { return last; } + + OCompTreeNode *at(uint index) const; + void append(OCompTreeNode *item); + void prepend(OCompTreeNode *item); + void insert(OCompTreeNode *after, OCompTreeNode *item); + OCompTreeNode *remove(OCompTreeNode *item); + uint count() const { return m_count; } + +private: + OCompTreeNode *first, *last; + uint m_count; +}; + +typedef OCompTreeNodeList OCompTreeChildren; +typedef OSortableValueList<QString> OCompletionMatchesList; + +/** + * A helper class for OCompletion. Implements a tree of QChar. + * + * The tree looks like this (containing the items "kde", "kde-ui", + * "kde-core" and "pfeiffer". Every item is delimited with QChar( 0x0 ) + * + * some_root_node + * / \ + * k p + * | | + * d f + * | | + * e e + * /| | + * 0x0 - i + * / \ | + * u c f + * | | | + * i o f + * | | | + * 0x0 r e + * | | + * e r + * | | + * 0x0 0x0 + * + * @author Carsten Pfeiffer <pfeiffer@kde.org> + * @internal + */ + +/**************************************************************************************************/ +/* OCompTreeNode +/**************************************************************************************************/ + +class OCompTreeNode : public QChar +{ +public: + OCompTreeNode():QChar(), myWeight(0) {} + OCompTreeNode( const QChar& ch, uint weight = 0 ):QChar( ch ), myWeight( weight ) {} + ~OCompTreeNode(); + + // FIXME: Do we need this for Opie? [see also the static ZoneAllocater below] + //void * operator new( size_t s ) { + // return alloc.allocate( s ); + //} + //void operator delete( void * s ) { + // alloc.deallocate( s ); + //} + + // Returns a child of this node matching ch, if available. + // Otherwise, returns 0L + inline OCompTreeNode * find( const QChar& ch ) const { + OCompTreeNode * cur = myChildren.begin(); + while (cur && (*cur != ch)) cur = cur->next; + return cur; + } + + OCompTreeNode * insert( const QChar&, bool sorted ); + void remove( const QString& ); + + inline int childrenCount() const { return myChildren.count(); }; + + // weighting + inline void confirm() { myWeight++; }; + inline void confirm(uint w) { myWeight += w; }; + inline void decline() { myWeight--; }; + inline uint weight() const { return myWeight; }; + + inline const OCompTreeChildren * children() const { return &myChildren; }; + inline const OCompTreeNode * childAt(int index) const { return myChildren.at(index); }; + inline const OCompTreeNode * firstChild() const { return myChildren.begin(); }; + inline const OCompTreeNode * lastChild() const { return myChildren.end(); }; + + /* We want to handle a list of OCompTreeNodes on our own, to not + need to use QValueList<>. And to make it even more fast we don't + use an accessor, but just a public member. */ + OCompTreeNode *next; + +private: + uint myWeight; + OCompTreeNodeList myChildren; + //static OZoneAllocator alloc; // FIXME: Do we need this for Opie? +}; + +/**************************************************************************************************/ +/* OCompletionMatchesWrapper +/**************************************************************************************************/ + +class OCompletionMatchesWrapper +{ +public: + OCompletionMatchesWrapper( bool sort = false ) + : sortedList( sort ? new OCompletionMatchesList : 0L ), + dirty( false ) + {} + ~OCompletionMatchesWrapper() { + delete sortedList; + } + + void setSorting( bool sort ) { + if ( sort && !sortedList ) + sortedList = new OCompletionMatchesList; + else if ( !sort ) { + delete sortedList; + sortedList = 0L; + } + stringList.clear(); + dirty = false; + } + + bool sorting() const { + return sortedList != 0L; + } + + void append( int i, const QString& string ) { + if ( sortedList ) + sortedList->insert( i, string ); + else + stringList.append( string ); + dirty = true; + } + + void clear() { + if ( sortedList ) + sortedList->clear(); + stringList.clear(); + dirty = false; + } + + uint count() const { + if ( sortedList ) + return sortedList->count(); + return stringList.count(); + } + + bool isEmpty() const { + return count() == 0; + } + + QString first() const { + return list().first(); + } + + QString last() const { + return list().last(); + } + + QStringList list() const; + + mutable QStringList stringList; + OCompletionMatchesList *sortedList; + mutable bool dirty; +}; + +/**************************************************************************************************/ +/* OCompletionPrivate +/**************************************************************************************************/ + +class OCompletionPrivate +{ +public: + // not a member to avoid #including kcompletion_private.h from kcompletion.h + // list used for nextMatch() and previousMatch() + OCompletionMatchesWrapper matches; +}; + +/**************************************************************************************************/ +/* OCompletion +/**************************************************************************************************/ + +OCompletion::OCompletion() +{ + d = new OCompletionPrivate; + + myCompletionMode = OGlobalSettings::completionMode(); + myTreeRoot = new OCompTreeNode; + myBeep = true; + myIgnoreCase = false; + myHasMultipleMatches = false; + myRotationIndex = 0; + setOrder( Insertion ); +} + + +OCompletion::~OCompletion() +{ + delete d; + delete myTreeRoot; +} + + +void OCompletion::setOrder( CompOrder order ) +{ + myOrder = order; + d->matches.setSorting( order == Weighted ); +} + + +void OCompletion::setIgnoreCase( bool ignoreCase ) +{ + myIgnoreCase = ignoreCase; +} + + +void OCompletion::setItems( const QStringList& items ) +{ + clear(); + insertItems( items ); +} + + +void OCompletion::insertItems( const QStringList& items ) +{ + bool weighted = (myOrder == Weighted); + QStringList::ConstIterator it; + if ( weighted ) { // determine weight + for ( it = items.begin(); it != items.end(); ++it ) addWeightedItem( *it ); + } + else { + for ( it = items.begin(); it != items.end(); ++it ) addItem( *it, 0 ); + } +} + + +QStringList OCompletion::items() const +{ + OCompletionMatchesWrapper list; // unsorted + bool addWeight = (myOrder == Weighted); + extractStringsFromNode( myTreeRoot, QString::null, &list, addWeight ); + + return list.list(); +} + + +void OCompletion::addItem( const QString& item ) +{ + d->matches.clear(); + myRotationIndex = 0; + myLastString = QString::null; + + addItem( item, 0 ); +} + + +void OCompletion::addItem( const QString& item, uint weight ) +{ + if ( item.isEmpty() ) return; + + OCompTreeNode *node = myTreeRoot; + uint len = item.length(); + + bool sorted = (myOrder == Sorted); + bool weighted = ((myOrder == Weighted) && weight > 1); + + // knowing the weight of an item, we simply add this weight to all of its + // nodes. + + for ( uint i = 0; i < len; i++ ) { + node = node->insert( item.at(i), sorted ); + if ( weighted ) node->confirm( weight -1 ); // node->insert() sets weighting to 1 + } + + // add 0x0-item as delimiter with evtl. weight + node = node->insert( 0x0, true ); + if ( weighted ) + node->confirm( weight -1 ); + //qDebug( "OCompletion: added: %s (%i)", item.latin1(), node->weight()); +} + + +void OCompletion::addWeightedItem( const QString& item ) +{ + if ( myOrder != Weighted ) { + addItem( item, 0 ); + return; + } + + uint len = item.length(); + uint weight = 0; + + // find out the weighting of this item (appended to the string as ":num") + int index = item.findRev(':'); + if ( index > 0 ) { + bool ok; + weight = item.mid( index + 1 ).toUInt( &ok ); + if ( !ok ) + weight = 0; + + len = index; // only insert until the ':' + } + + addItem( item.left( len ), weight ); + return; +} + + +void OCompletion::removeItem( const QString& item ) +{ + d->matches.clear(); + myRotationIndex = 0; + myLastString = QString::null; + + myTreeRoot->remove( item ); +} + + +void OCompletion::clear() +{ + d->matches.clear(); + myRotationIndex = 0; + myLastString = QString::null; + + delete myTreeRoot; + myTreeRoot = new OCompTreeNode; +} + + +QString OCompletion::makeCompletion( const QString& string ) +{ + if ( myCompletionMode == OGlobalSettings::CompletionNone ) + return QString::null; + + //qDebug( "OCompletion: completing: %s", string ); + + d->matches.clear(); + myRotationIndex = 0; + myHasMultipleMatches = false; + myLastMatch = myCurrentMatch; + + // in Shell-completion-mode, emit all matches when we get the same + // complete-string twice + if ( myCompletionMode == OGlobalSettings::CompletionShell && + string == myLastString ) { + // Don't use d->matches since calling postProcessMatches() + // on d->matches here would interfere with call to + // postProcessMatch() during rotation + + findAllCompletions( string, &d->matches, myHasMultipleMatches ); + QStringList l = d->matches.list(); + postProcessMatches( &l ); + emit matches( l ); + + if ( l.isEmpty() ) + doBeep( NoMatch ); + + return QString::null; + } + + QString completion; + // in case-insensitive popup mode, we search all completions at once + if ( myCompletionMode == OGlobalSettings::CompletionPopup || + myCompletionMode == OGlobalSettings::CompletionPopupAuto ) { + findAllCompletions( string, &d->matches, myHasMultipleMatches ); + if ( !d->matches.isEmpty() ) + completion = d->matches.first(); + } + else + completion = findCompletion( string ); + + if ( myHasMultipleMatches ) + emit multipleMatches(); + + myLastString = string; + myCurrentMatch = completion; + + postProcessMatch( &completion ); + + if ( !string.isEmpty() ) { // only emit match when string != "" + //qDebug( "OCompletion: Match: %s", completion ); + emit match( completion ); + } + + if ( completion.isNull() ) + doBeep( NoMatch ); + + return completion; +} + +QStringList OCompletion::substringCompletion( const QString& string ) const +{ + // get all items in the tree, eventually in sorted order + bool sorted = (myOrder == Weighted); + OCompletionMatchesWrapper allItems( sorted ); + extractStringsFromNode( myTreeRoot, QString::null, &allItems, false ); + + QStringList list = allItems.list(); + + // subStringMatches is invoked manually, via a shortcut, so we should + // beep here, if necessary. + if ( list.isEmpty() ) { + doBeep( NoMatch ); + return list; + } + + if ( string.isEmpty() ) { // shortcut + postProcessMatches( &list ); + return list; + } + + QStringList matches; + QStringList::ConstIterator it = list.begin(); + + for( ; it != list.end(); ++it ) { + QString item = *it; + if ( item.find( string, 0, false ) != -1 ) { // always case insensitive + postProcessMatch( &item ); + matches.append( item ); + } + } + + if ( matches.isEmpty() ) + doBeep( NoMatch ); + + return matches; +} + + +void OCompletion::setCompletionMode( OGlobalSettings::Completion mode ) +{ + myCompletionMode = mode; +} + + +QStringList OCompletion::allMatches() +{ + // Don't use d->matches since calling postProcessMatches() + // on d->matches here would interfere with call to + // postProcessMatch() during rotation + OCompletionMatchesWrapper matches( myOrder == Weighted ); + bool dummy; + findAllCompletions( myLastString, &matches, dummy ); + QStringList l = matches.list(); + postProcessMatches( &l ); + return l; +} + + +OCompletionMatches OCompletion::allWeightedMatches() +{ + // Don't use d->matches since calling postProcessMatches() + // on d->matches here would interfere with call to + // postProcessMatch() during rotation + OCompletionMatchesWrapper matches( myOrder == Weighted ); + bool dummy; + findAllCompletions( myLastString, &matches, dummy ); + OCompletionMatches ret( matches ); + postProcessMatches( &ret ); + return ret; +} + +QStringList OCompletion::allMatches( const QString &string ) +{ + OCompletionMatchesWrapper matches( myOrder == Weighted ); + bool dummy; + findAllCompletions( string, &matches, dummy ); + QStringList l = matches.list(); + postProcessMatches( &l ); + return l; +} + +OCompletionMatches OCompletion::allWeightedMatches( const QString &string ) +{ + OCompletionMatchesWrapper matches( myOrder == Weighted ); + bool dummy; + findAllCompletions( string, &matches, dummy ); + OCompletionMatches ret( matches ); + postProcessMatches( &ret ); + return ret; +} + +///////////////////////////////////////////////////// +///////////////// tree operations /////////////////// + + +QString OCompletion::nextMatch() +{ + QString completion; + myLastMatch = myCurrentMatch; + + if ( d->matches.isEmpty() ) { + findAllCompletions( myLastString, &d->matches, myHasMultipleMatches ); + completion = d->matches.first(); + myCurrentMatch = completion; + myRotationIndex = 0; + postProcessMatch( &completion ); + emit match( completion ); + return completion; + } + + QStringList matches = d->matches.list(); + myLastMatch = matches[ myRotationIndex++ ]; + + if ( myRotationIndex == matches.count() -1 ) + doBeep( Rotation ); // indicate last matching item -> rotating + + else if ( myRotationIndex == matches.count() ) + myRotationIndex = 0; + + completion = matches[ myRotationIndex ]; + myCurrentMatch = completion; + postProcessMatch( &completion ); + emit match( completion ); + return completion; +} + + + +QString OCompletion::previousMatch() +{ + QString completion; + myLastMatch = myCurrentMatch; + + if ( d->matches.isEmpty() ) { + findAllCompletions( myLastString, &d->matches, myHasMultipleMatches ); + completion = d->matches.last(); + myCurrentMatch = completion; + myRotationIndex = 0; + postProcessMatch( &completion ); + emit match( completion ); + return completion; + } + + QStringList matches = d->matches.list(); + myLastMatch = matches[ myRotationIndex ]; + if ( myRotationIndex == 1 ) + doBeep( Rotation ); // indicate first item -> rotating + + else if ( myRotationIndex == 0 ) + myRotationIndex = matches.count(); + + myRotationIndex--; + + completion = matches[ myRotationIndex ]; + myCurrentMatch = completion; + postProcessMatch( &completion ); + emit match( completion ); + return completion; +} + + + +// tries to complete "string" from the tree-root +QString OCompletion::findCompletion( const QString& string ) +{ + QChar ch; + QString completion; + const OCompTreeNode *node = myTreeRoot; + + // start at the tree-root and try to find the search-string + for( uint i = 0; i < string.length(); i++ ) { + ch = string.at( i ); + node = node->find( ch ); + + if ( node ) + completion += ch; + else + return QString::null; // no completion + } + + // Now we have the last node of the to be completed string. + // Follow it as long as it has exactly one child (= longest possible + // completion) + + while ( node->childrenCount() == 1 ) { + node = node->firstChild(); + if ( !node->isNull() ) + completion += *node; + } + // if multiple matches and auto-completion mode + // -> find the first complete match + if ( node && node->childrenCount() > 1 ) { + myHasMultipleMatches = true; + + if ( myCompletionMode == OGlobalSettings::CompletionAuto ) { + myRotationIndex = 1; + if (myOrder != Weighted) { + while ( (node = node->firstChild()) ) { + if ( !node->isNull() ) + completion += *node; + else + break; + } + } + else { + // don't just find the "first" match, but the one with the + // highest priority + + const OCompTreeNode* temp_node = 0L; + while(1) { + int count = node->childrenCount(); + temp_node = node->firstChild(); + uint weight = temp_node->weight(); + const OCompTreeNode* hit = temp_node; + for( int i = 1; i < count; i++ ) { + temp_node = node->childAt(i); + if( temp_node->weight() > weight ) { + hit = temp_node; + weight = hit->weight(); + } + } + // 0x0 has the highest priority -> we have the best match + if ( hit->isNull() ) + break; + + node = hit; + completion += *node; + } + } + } + + else + doBeep( PartialMatch ); // partial match -> beep + } + + return completion; +} + + +void OCompletion::findAllCompletions(const QString& string, + OCompletionMatchesWrapper *matches, + bool& hasMultipleMatches) const +{ + //qDebug( "OCompletion: finding all completions for %s", (const char*) string ); + + if ( string.isEmpty() ) + return; + + if ( myIgnoreCase ) { // case insensitive completion + extractStringsFromNodeCI( myTreeRoot, QString::null, string, matches ); + hasMultipleMatches = (matches->count() > 1); + return; + } + + QChar ch; + QString completion; + const OCompTreeNode *node = myTreeRoot; + + // start at the tree-root and try to find the search-string + for( uint i = 0; i < string.length(); i++ ) { + ch = string.at( i ); + node = node->find( ch ); + + if ( node ) + completion += ch; + else + return; // no completion -> return empty list + } + + // Now we have the last node of the to be completed string. + // Follow it as long as it has exactly one child (= longest possible + // completion) + + while ( node->childrenCount() == 1 ) { + node = node->firstChild(); + if ( !node->isNull() ) + completion += *node; + // kdDebug() << completion << node->latin1(); + } + + + // there is just one single match) + if ( node->childrenCount() == 0 ) + matches->append( node->weight(), completion ); + + else { + // node has more than one child + // -> recursively find all remaining completions + hasMultipleMatches = true; + extractStringsFromNode( node, completion, matches ); + } +} + + +void OCompletion::extractStringsFromNode( const OCompTreeNode *node, + const QString& beginning, + OCompletionMatchesWrapper *matches, + bool addWeight ) const +{ + if ( !node || !matches ) return; + + // kDebug() << "Beginning: " << beginning << endl; + const OCompTreeChildren *list = node->children(); + QString string; + QString w; + + // loop thru all children + for ( OCompTreeNode *cur = list->begin(); cur ; cur = cur->next) { + string = beginning; + node = cur; + if ( !node->isNull() ) + string += *node; + + while ( node && node->childrenCount() == 1 ) { + node = node->firstChild(); + if ( node->isNull() ) break; + string += *node; + } + + if ( node && node->isNull() ) { // we found a leaf + if ( addWeight ) { + // add ":num" to the string to store the weighting + string += ':'; + w.setNum( node->weight() ); + string.append( w ); + } + matches->append( node->weight(), string ); + } + + // recursively find all other strings. + if ( node && node->childrenCount() > 1 ) + extractStringsFromNode( node, string, matches, addWeight ); + } +} + +void OCompletion::extractStringsFromNodeCI( const OCompTreeNode *node, + const QString& beginning, + const QString& restString, + OCompletionMatchesWrapper *matches ) const +{ + if ( restString.isEmpty() ) { + extractStringsFromNode( node, beginning, matches, false /*noweight*/ ); + return; + } + + QChar ch1 = restString.at(0); + QString newRest = restString.mid(1); + OCompTreeNode *child1, *child2; + + child1 = node->find( ch1 ); // the correct match + if ( child1 ) + extractStringsFromNodeCI( child1, beginning + *child1, newRest, + matches ); + + // append the case insensitive matches, if available + if ( ch1.isLetter() ) { + // find out if we have to lower or upper it. Is there a better way? + QChar ch2 = ch1.lower(); + if ( ch1 == ch2 ) + ch2 = ch1.upper(); + if ( ch1 != ch2 ) { + child2 = node->find( ch2 ); + if ( child2 ) + extractStringsFromNodeCI( child2, beginning + *child2, newRest, + matches ); + } + } +} + +// FIXME: Revise this for Opie? + +void OCompletion::doBeep( BeepMode mode ) const +{ + if ( !myBeep ) return; + + QString text, event; + + switch ( mode ) { + case Rotation: + event = QString::fromLatin1("Textcompletion: rotation"); + text = tr("You reached the end of the list\nof matching items.\n"); + break; + case PartialMatch: + if ( myCompletionMode == OGlobalSettings::CompletionShell || + myCompletionMode == OGlobalSettings::CompletionMan ) { + event = QString::fromLatin1("Textcompletion: partial match"); + text = tr("The completion is ambiguous, more than one\nmatch is available.\n"); + } + break; + case NoMatch: + if ( myCompletionMode == OGlobalSettings::CompletionShell ) { + event = QString::fromLatin1("Textcompletion: no match"); + text = tr("There is no matching item available.\n"); + } + break; + } + + //if ( !text.isEmpty() ) + //ONotifyClient::event( event, text ); // FIXME: Revise for Opie? +} + +// Implements the tree. Every node is a QChar and has a list of children, which +// are Nodes as well. +// QChar( 0x0 ) is used as the delimiter of a string; the last child of each +// inserted string is 0x0. + +OCompTreeNode::~OCompTreeNode() +{ + // delete all children + OCompTreeNode *cur = myChildren.begin(); + while (cur) { + OCompTreeNode * next = cur->next; + delete myChildren.remove(cur); + cur = next; + } +} + + +// Adds a child-node "ch" to this node. If such a node is already existant, +// it will not be created. Returns the new/existing node. +OCompTreeNode * OCompTreeNode::insert( const QChar& ch, bool sorted ) +{ + OCompTreeNode *child = find( ch ); + if ( !child ) { + child = new OCompTreeNode( ch ); + + // FIXME, first (slow) sorted insertion implementation + if ( sorted ) { + OCompTreeNode * prev = 0; + OCompTreeNode * cur = myChildren.begin(); + while ( cur ) { + if ( ch > *cur ) { + prev = cur; + cur = cur->next; + } else + break; + } + if (prev) + myChildren.insert( prev, child ); + else + myChildren.prepend(child); + } + + else + myChildren.append( child ); + } + + // implicit weighting: the more often an item is inserted, the higher + // priority it gets. + child->confirm(); + + return child; +} + + +// Recursively removes a string from the tree (untested :-) +void OCompTreeNode::remove( const QString& string ) +{ + OCompTreeNode *child = 0L; + + if ( string.isEmpty() ) { + child = find( 0x0 ); + delete myChildren.remove( child ); + return; + } + + QChar ch = string.at(0); + child = find( ch ); + if ( child ) { + child->remove( string.right( string.length() -1 ) ); + if ( child->myChildren.count() == 0 ) { + delete myChildren.remove( child ); + } + } +} + +QStringList OCompletionMatchesWrapper::list() const { + if ( sortedList && dirty ) { + sortedList->sort(); + dirty = false; + + stringList.clear(); + + // high weight == sorted last -> reverse the sorting here + QValueListConstIterator<OSortableItem<QString> > it; + for ( it = sortedList->begin(); it != sortedList->end(); ++it ) + stringList.prepend( (*it).value() ); + } + + return stringList; +} + +OCompletionMatches::OCompletionMatches( bool sort_P ) + : _sorting( sort_P ) +{ +} + +OCompletionMatches::OCompletionMatches( const OCompletionMatchesWrapper& matches ) + : _sorting( matches.sorting()) +{ + if( matches.sortedList != 0L ) + OCompletionMatchesList::operator=( *matches.sortedList ); + else { + QStringList l = matches.list(); + for( QStringList::ConstIterator it = l.begin(); + it != l.end(); + ++it ) + prepend( OSortableItem<QString, int>( 1, *it ) ); + } +} + +OCompletionMatches::~OCompletionMatches() +{ +} + +QStringList OCompletionMatches::list( bool sort_P ) const +{ + if( _sorting && sort_P ) + const_cast< OCompletionMatches* >( this )->sort(); + QStringList stringList; + // high weight == sorted last -> reverse the sorting here + for ( ConstIterator it = begin(); it != end(); ++it ) + stringList.prepend( (*it).value() ); + return stringList; +} + +void OCompletionMatches::removeDuplicates() +{ + Iterator it1, it2; + for ( it1 = begin(); it1 != end(); ++it1 ) { + for ( (it2 = it1), ++it2; it2 != end();) { + if( (*it1).value() == (*it2).value()) { + // use the max height + //(*it1).first = kMax( (*it1).index(), (*it2).index()); + (*it1).first = (*it2).index() < (*it1).index() ? (*it1).index() : (*it2).index(); + it2 = remove( it2 ); + continue; + } + ++it2; + } + } +} + +void OCompTreeNodeList::append(OCompTreeNode *item) +{ + m_count++; + if (!last) { + last = item; + last->next = 0; + first = item; + return; + } + last->next = item; + item->next = 0; + last = item; +} + +void OCompTreeNodeList::prepend(OCompTreeNode *item) +{ + m_count++; + if (!last) { + last = item; + last->next = 0; + first = item; + return; + } + item->next = first; + first = item; +} + +void OCompTreeNodeList::insert(OCompTreeNode *after, OCompTreeNode *item) +{ + if (!after) { + append(item); + return; + } + + m_count++; + + item->next = after->next; + after->next = item; + + if (after == last) + last = item; +} + +OCompTreeNode *OCompTreeNodeList::remove(OCompTreeNode *item) +{ + if (!first || !item) + return 0; + OCompTreeNode *cur = 0; + + if (item == first) + first = first->next; + else { + cur = first; + while (cur && cur->next != item) cur = cur->next; + if (!cur) + return 0; + cur->next = item->next; + } + if (item == last) + last = cur; + m_count--; + return item; +} + +OCompTreeNode *OCompTreeNodeList::at(uint index) const +{ + OCompTreeNode *cur = first; + while (index-- && cur) cur = cur->next; + return cur; +} + +// FIXME: Revise for Opie? +//OZoneAllocator OCompTreeNode::alloc(8192); + +//void OCompletion::virtual_hook( int, void* ) +//{ /*BASE::virtual_hook( id, data );*/ } + +//void OCompletionBase::virtual_hook( int, void* ) +//{ /*BASE::virtual_hook( id, data );*/ } diff --git a/libopie2/qt3/opiecore/ocompletion.h b/libopie2/qt3/opiecore/ocompletion.h new file mode 100644 index 0000000..0317c1b --- a/dev/null +++ b/libopie2/qt3/opiecore/ocompletion.h @@ -0,0 +1,603 @@ +/* + This file is part of the Opie Project + Originally part of the KDE Project + Copyright (C) 1999,2000 Carsten Pfeiffer <pfeiffer@kde.org> + =. + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef OCOMPLETION_H +#define OCOMPLETION_H + +/* QT */ + +#include <qmap.h> +#include <qlist.h> +#include <qobject.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qguardedptr.h> + +/* OPIE */ + +#include <opie2/oglobalsettings.h> +#include <opie2/osortablevaluelist.h> + +/* FORWARDS */ + +class OCompTreeNode; +class OCompletionPrivate; +class OCompletionBasePrivate; +class OCompletionMatchesWrapper; +class OCompletionMatches; +class QPopupMenu; + +// FIXME: Do we need special ShortCut handling in Opie? If so, revise this. +class OShortcut +{ +public: + bool isNull() const { return true; }; + bool operator == ( const OShortcut& bla ) const { return false; }; +}; + + +/** + * This class offers easy use of "auto-completion", "manual-completion" or + * "shell completion" on QString objects. A common use is completing filenames + * or URLs (see @ref OURLCompletion()). + * But it is not limited to URL-completion -- everything should be completable! + * The user should be able to complete email-addresses, telephone-numbers, + * commands, SQL queries, ... + * Every time your program knows what the user can type into an edit-field, you + * should offer completion. With OCompletion, this is very easy, and if you are + * using a line edit widget (@ref OLineEdit), it is even more easy. + * Basically, you tell a OCompletion object what strings should be completable + * and whenever completion should be invoked, you call @ref makeCompletion(). + * OLineEdit and (an editable) OComboBox even do this automatically for you. + * + * OCompletion offers the completed string via the signal @ref match() and + * all matching strings (when the result is ambiguous) via the method + * @ref allMatches(). + * + * Notice: auto-completion, shell completion and manual completion work + * slightly differently: + * + * @li auto-completion always returns a complete item as match. + * When more than one matching items are available, it will deliver just + * the first (depending on sorting order) item. Iterating over all matches + * is possible via @ref nextMatch() and @ref previousMatch(). + * + * @li popup-completion works in the same way, the only difference being that + * the completed items are not put into the edit-widget, but into a + * separate popup-box. + * + * @li manual completion works the same way as auto-completion, the + * subtle difference is, that it isn't invoked automatically while the user + * is typing, but only when the user presses a special key. The difference + * of manual and auto-completion is therefore only visible in UI classes, + * OCompletion needs to know whether to deliver partial matches + * (shell completion) or whole matches (auto/manual completion), therefore + * @ref OGlobalSettings::CompletionMan and + * @ref OGlobalSettings::CompletionAuto have the exact same effect in + * OCompletion. + * + * @li shell completion works like how shells complete filenames: + * when multiple matches are available, the longest possible string of all + * matches is returned (i.e. only a partial item). + * Iterating over all matching items (complete, not partial) is possible + * via @ref nextMatch() and @ref previousMatch(). + * + * You don't have to worry much about that though, OCompletion handles + * that for you, according to the setting @ref setCompletionMode(). + * The default setting is globally configured by the user and read + * from @ref OGlobalSettings::completionMode(). + * + * A short example: + * <pre> + * OCompletion completion; + * completion.setOrder( OCompletion::Sorted ); + * completion.addItem( "pfeiffer@kde.org" ); + * completion.addItem( "coolo@kde.org" ); + * completion.addItem( "carpdjih@sp.zrz.tu-berlin.de" ); + * completion.addItem( "carp@cs.tu-berlin.de" ); + * + * cout << completion.makeCompletion( "ca" ).latin1() << endl; + * </pre> + * In shell-completion-mode, this will be "carp"; in auto-completion- + * mode it will be "carp@cs.tu-berlin.de", as that is alphabetically + * smaller. + * If setOrder was set to Insertion, "carpdjih@sp.zrz.tu-berlin.de" + * would be completed in auto-completion-mode, as that was inserted before + * "carp@cs.tu-berlin.de". + * + * You can dynamically update the completable items by removing and adding them + * whenever you want. + * For advanced usage, you could even use multiple OCompletion objects. E.g. + * imagine an editor like kwrite with multiple open files. You could store + * items of each file in a different OCompletion object, so that you know (and + * tell the user) where a completion comes from. + * + * Note: OCompletion does not work with strings that contain 0x0 characters + * (unicode nul), as this is used internally as a delimiter. + * + * You may inherit from OCompletion and override @ref makeCompletion() in + * special cases (like reading directories/urls and then supplying the + * contents to OCompletion, as OURLCompletion does), but generally, this is + * not necessary. + * + * + * @short A generic class for completing QStrings + * @author Carsten Pfeiffer <pfeiffer@kde.org> + * @version $Id$ + */ + +class OCompletion : public QObject +{ + Q_ENUMS( CompOrder ) + Q_PROPERTY( CompOrder order READ order WRITE setOrder ) + Q_PROPERTY( bool ignoreCase READ ignoreCase WRITE setIgnoreCase ) + Q_PROPERTY( QStringList items READ items WRITE setItems ) + Q_OBJECT + + public: + /** + * Constants that represent the order in which OCompletion performs + * completion-lookups. + */ + enum CompOrder { Sorted, Insertion, Weighted }; + + /** + * Constructor, nothing special here :) + */ + OCompletion(); + + // FIXME: copy constructor, assignment constructor... + + /** + * Destructor, nothing special here, either. + */ + virtual ~OCompletion(); + + /** + * Attempts to find an item in the list of available completions, + * that begins with @p string. Will either return the first matching item + * (if there is more than one match) or QString::null, if no match was + * found. + * + * In the latter case, a sound will be issued, depending on + * @ref isSoundsEnabled(). + * If a match was found, it will also be emitted via the signal + * @ref match(). + * + * If this is called twice or more often with the same string while no + * items were added or removed in the meantime, all available completions + * will be emitted via the signal @ref matches(). + * This happens only in shell-completion-mode. + * + * @returns the matching item, or QString::null if there is no matching + * item. + * @see #slotMakeCompletion + * @see #substringCompletion + */ + virtual QString makeCompletion( const QString& string ); + + /** + * @returns a list of items which all contain @p text as a substring, + * i.e. not necessarily at the beginning. + * + * @see #makeCompletion + */ + QStringList substringCompletion( const QString& string ) const; + + /** + * @returns the next item from the matching-items-list. + * When reaching the beginning, the list is rotated so it will return the + * last match and a sound is issued (depending on @ref isSoundsEnabled()). + * When there is no match, QString::null is returned and + * a sound is be issued. + * @see #slotPreviousMatch + */ + QString previousMatch(); + + /** + * @returns the previous item from the matching-items-list + * When reaching the last item, the list is rotated, so it will return + * the first match and a sound is issued (depending on + * @ref isSoundsEnabled()). When there is no match, QString::null is + * returned and a sound is issued. + * @see #slotNextMatch + */ + QString nextMatch(); + + /** + * @returns the last match. Might be useful if you need to check whether + * a completion is different from the last one. + * QString::null is returned when there is no last match. + */ + virtual const QString& lastMatch() const { return myLastMatch; } + + /** + * Returns a list of all items inserted into OCompletion. This is useful + * if you need to save the state of a OCompletion object and restore it + * later. + * + * Important note: when @ref order() == Weighted, then every item in the + * stringlist has its weight appended, delimited by a colon. E.g. an item + * "www.kde.org" might look like "www.kde.org:4", where 4 is the weight. + * + * This is necessary so that you can save the items along with its + * weighting on disk and load them back with @ref setItems(), restoring its + * weight as well. If you really don't want the appended weightings, call + * @ref setOrder( OCompletion::Insertion ) + * before calling items(). + * + * @returns a list of all items + * @see #setItems + */ + QStringList items() const; + + /** + * Sets the completion mode to Auto/Manual, Shell or None. + * If you don't set the mode explicitly, the global default value + * OGlobalSettings::completionMode() is used. + * @ref OGlobalSettings::CompletionNone disables completion. + * @see #completionMode + * @see #OGlobalSettings::completionMode + */ + virtual void setCompletionMode( OGlobalSettings::Completion mode ); + + /** + * @returns the current completion mode. + * May be different from @ref OGlobalSettings::completionMode(), if you + * explicitly called @ref setCompletionMode(). + * @see #setCompletionMode + */ + OGlobalSettings::Completion completionMode() const { return myCompletionMode; }; + + /** + * OCompletion offers three different ways in which it offers its items: + * @li in the order of insertion + * @li sorted alphabetically + * @li weighted + * + * Choosing weighted makes OCompletion perform an implicit weighting based + * on how often an item is inserted. Imagine a web browser with a location + * bar, where the user enters URLs. The more often a URL is entered, the + * higher priority it gets. + * + * Note: Setting the order to sorted only affects new inserted items, + * already existing items will stay in the current order. So you probably + * want to call setOrder( Sorted ) before inserting items, when you want + * everything sorted. + * + * Default is insertion order + * @see #order + */ + virtual void setOrder( CompOrder order ); + + /** + * @returns the current completion order. + * @see #setOrder + */ + CompOrder order() const { return myOrder; } + + /** + * Setting this to true makes OCompletion behave case insensitively. + * E.g. makeCompletion( "CA" ); might return "carp@cs.tu-berlin.de". + * Default is false (case sensitive). + * @see #ignoreCase + */ + virtual void setIgnoreCase( bool ignoreCase ); + + /** + * @returns whether OCompletion acts case insensitively or not. + * Default is false (case sensitive). + * @see #setIgnoreCase + */ + bool ignoreCase() const { return myIgnoreCase; }; + + /** + * @returns a list of all items matching the last completed string. + * Might take some time, when you have LOTS of items. + * + * @see #substringCompletion + */ + QStringList allMatches(); + + /** + * @returns a list of all items matching @p string. + */ + QStringList allMatches( const QString& string ); + + /** + * @returns a list of all items matching the last completed string. + * Might take some time, when you have LOTS of items. + * The matches are returned as OCompletionMatches, which also + * keeps the weight of the matches, allowing + * you to modify some matches or merge them with matches + * from another call to allWeightedMatches(), and sort the matches + * after that in order to have the matches ordered correctly + * + * @see #substringCompletion + */ + OCompletionMatches allWeightedMatches(); + + /** + * @returns a list of all items matching @p string. + */ + OCompletionMatches allWeightedMatches( const QString& string ); + + /** + * Enables/disables playing a sound when + * @li @ref makeCompletion() can't find a match + * @li there is a partial completion (= multiple matches in + * Shell-completion mode) + * @li @ref nextMatch() or @ref previousMatch() hit the last possible + * match -> rotation + * + * For playing the sounds, @ref ONotifyClient() is used. // FIXME: Revise this for Opie + * + * @see #isSoundsEnabled + */ + virtual void setEnableSounds( bool enable ) { myBeep = enable; } + + /** + * Tells you whether OCompletion will play sounds on certain occasions. + * Default is enabled + * @see #enableSounds + * @see #disableSounds + */ + bool isSoundsEnabled() const { return myBeep; }; + + /** + * @returns true when more than one match is found + * @see #multipleMatches + */ + bool hasMultipleMatches() const { return myHasMultipleMatches; }; + + public slots: + /** + * Attempts to complete "string" and emits the completion via @ref match(). + * Same as @ref makeCompletion() (just as a slot). + * @see #makeCompletion + */ + void slotMakeCompletion( const QString& string ) { (void) makeCompletion( string ); }; + + /** + * Searches the previous matching item and emits it via @ref match() + * Same as @ref previousMatch() (just as a slot). + * @see #previousMatch + */ + void slotPreviousMatch() { (void) previousMatch(); }; + + /** + * Searches the next matching item and emits it via @ref match() + * Same as @ref nextMatch() (just as a slot). + * @see #nextMatch + */ + void slotNextMatch() { (void) nextMatch(); }; + + /** + * Inserts @p items into the list of possible completions. + * Does the same as @ref setItems(), but does not call @ref clear() before. + */ + void insertItems( const QStringList& items ); + + /** + * Sets the list of items available for completion. Removes all previous + * items. + * + * Notice: when order() == Weighted, then the weighting is looked up for + * every item in the stringlist. Every item should have ":number" appended, + * where number is an unsigned integer, specifying the weighting. + * + * If you don't like this, call + * setOrder( OCompletion::Insertion ) + * before calling setItems(). + * + * @see #items + */ + virtual void setItems( const QStringList& ); + + /** + * Adds an item to the list of available completions. + * Resets the current item-state (@ref previousMatch() and @ref nextMatch() + * won't work anymore). + */ + void addItem( const QString& ); + + /** + * Adds an item to the list of available completions. + * Resets the current item-state (@ref previousMatch() and @ref nextMatch() + * won't work anymore). + * + * Sets the weighting of the item to @p weight or adds it to the current + * weighting if the item is already available. The weight has to be greater + * than 1 to take effect (default weight is 1). + */ + void addItem( const QString&, uint weight ); + + /** + * Removes an item from the list of available completions. + * Resets the current item-state (@ref previousMatch() and @ref nextMatch() + * won't work anymore). + */ + void removeItem( const QString& ); + + /** + * Removes all inserted items. + */ + virtual void clear(); + + signals: + /** + * The matching item. Will be emitted by @ref makeCompletion(), + * @ref previousMatch() or @ref nextMatch(). May be QString::null if there + * is no matching item. + */ + void match( const QString& ); + + /** + * All matching items. Will be emitted by @ref makeCompletion() in shell- + * completion-mode, when the same string is passed to makeCompletion twice + * or more often. + */ + void matches( const QStringList& ); + + /** + * This signal is emitted, when calling @ref makeCompletion() and more than + * one matching item is found. + * @see #hasMultipleMatches + */ + void multipleMatches(); + + protected: + /** + * This method is called after a completion is found and before the + * matching string is emitted. You can override this method to modify the + * string that will be emitted. + * This is necessary e.g. in @ref OURLCompletion(), where files with spaces + * in their names are shown escaped ("filename\ with\ spaces"), but stored + * unescaped inside OCompletion. + * Never delete that pointer! + * + * Default implementation does nothing. + * @see #postProcessMatches + */ + virtual void postProcessMatch( QString * /*match*/ ) const {} + + /** + * This method is called before a list of all available completions is + * emitted via @ref matches. You can override this method to modify the + * found items before @ref match() or @ref matches() are emitted. + * Never delete that pointer! + * + * Default implementation does nothing. + * @see #postProcessMatch + */ + virtual void postProcessMatches( QStringList * /*matches*/ ) const {} + + /** + * This method is called before a list of all available completions is + * emitted via @ref matches. You can override this method to modify the + * found items before @ref match() or @ref matches() are emitted. + * Never delete that pointer! + * + * Default implementation does nothing. + * @see #postProcessMatch + */ + virtual void postProcessMatches( OCompletionMatches * /*matches*/ ) const {} + +private: + void addWeightedItem( const QString& ); + QString findCompletion( const QString& string ); + void findAllCompletions( const QString&, OCompletionMatchesWrapper *matches, bool& hasMultipleMatches ) const; + + void extractStringsFromNode( const OCompTreeNode *, + const QString& beginning, + OCompletionMatchesWrapper *matches, + bool addWeight = false ) const; + void extractStringsFromNodeCI( const OCompTreeNode *, + const QString& beginning, + const QString& restString, + OCompletionMatchesWrapper *matches) const; + + enum BeepMode { NoMatch, PartialMatch, Rotation }; + void doBeep( BeepMode ) const; + + OGlobalSettings::Completion myCompletionMode; + + CompOrder myOrder; + QString myLastString; + QString myLastMatch; + QString myCurrentMatch; + OCompTreeNode * myTreeRoot; + QStringList myRotations; + bool myBeep; + bool myIgnoreCase; + bool myHasMultipleMatches; + uint myRotationIndex; + + private: + OCompletionPrivate *d; +}; + +// some more helper stuff +typedef OSortableValueList<QString> OCompletionMatchesList; +class OCompletionMatchesPrivate; + +/** + * This structure is returned by @ref OCompletion::allWeightedMatches . + * It also keeps the weight of the matches, allowing + * you to modify some matches or merge them with matches + * from another call to allWeightedMatches(), and sort the matches + * after that in order to have the matches ordered correctly + * + * Example (a simplified example of what Oonqueror's completion does): + * <pre> + * OCompletionMatches matches = completion->allWeightedMatches( location ); + * if( !location.startsWith( "www." )) + matches += completion->allWeightedmatches( "www." + location" ); + * matches.removeDuplicates(); + * QStringList list = matches.list(); + * </pre> + * + * @short List for keeping matches returned from OCompletion + */ + +class OCompletionMatches + : public OCompletionMatchesList +{ + public: + OCompletionMatches( bool sort ); + /** + * @internal + */ + OCompletionMatches( const OCompletionMatchesWrapper& matches ); + ~OCompletionMatches(); + /** + * Removes duplicate matches. Needed only when you merged several matches + * results and there's a possibility of duplicates. + */ + void removeDuplicates(); + /** + * Returns the matches as a QStringList. + * @param sort if false, the matches won't be sorted before the conversion, + * use only if you're sure the sorting is not needed + */ + QStringList list( bool sort = true ) const; + /** + * If sorting() returns false, the matches aren't sorted by their weight, + * even if true is passed to list(). + */ + bool sorting() const { + return _sorting; + } +private: + bool _sorting; + OCompletionMatchesPrivate* d; +}; + +#endif // OCOMPLETION_H diff --git a/libopie2/qt3/opiecore/ocompletionbase.cpp b/libopie2/qt3/opiecore/ocompletionbase.cpp new file mode 100644 index 0000000..6ff129a --- a/dev/null +++ b/libopie2/qt3/opiecore/ocompletionbase.cpp @@ -0,0 +1,171 @@ +/* + This file is part of the Opie Project + + Copyright (C) 2003 Michael Lauer <mickey@tm.informatik.uni-frankfurt.de> + Inspired by the KDE completion classes which are + Copyright (C) 2000 Dawit Alemayehu <adawit@kde.org> + =. + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include <opie2/ocompletion.h> +#include <opie2/ocompletionbase.h> + +OCompletionBase::OCompletionBase() +{ + m_delegate = 0L; + // Assign the default completion type to use. + m_iCompletionMode = OGlobalSettings::completionMode(); + + // Initialize all key-bindings to 0 by default so that + // the event filter will use the global settings. + useGlobalKeyBindings(); + + // By default we initialize everything to false. + // All the variables would be setup properly when + // the appropriate member functions are called. + setup( false, false, false ); +} + +OCompletionBase::~OCompletionBase() +{ + if( m_bAutoDelCompObj && m_pCompObj ) + { + delete m_pCompObj; + } +} + +void OCompletionBase::setDelegate( OCompletionBase *delegate ) +{ + m_delegate = delegate; + + if ( m_delegate ) { + m_delegate->m_bAutoDelCompObj = m_bAutoDelCompObj; + m_delegate->m_bHandleSignals = m_bHandleSignals; + m_delegate->m_bEmitSignals = m_bEmitSignals; + m_delegate->m_iCompletionMode = m_iCompletionMode; + m_delegate->m_keyMap = m_keyMap; + } +} + +OCompletion* OCompletionBase::completionObject( bool hsig ) +{ + if ( m_delegate ) + return m_delegate->completionObject( hsig ); + + if ( !m_pCompObj ) + { + setCompletionObject( new OCompletion(), hsig ); + m_bAutoDelCompObj = true; + } + return m_pCompObj; +} + +void OCompletionBase::setCompletionObject( OCompletion* compObj, bool hsig ) +{ + if ( m_delegate ) { + m_delegate->setCompletionObject( compObj, hsig ); + return; + } + + if ( m_bAutoDelCompObj && compObj != m_pCompObj ) + delete m_pCompObj; + + m_pCompObj = compObj; + + // We emit rotation and completion signals + // if completion object is not NULL. + setup( false, hsig, !m_pCompObj.isNull() ); +} + +// BC: Inline this function and possibly rename it to setHandleEvents??? (DA) +void OCompletionBase::setHandleSignals( bool handle ) +{ + if ( m_delegate ) + m_delegate->setHandleSignals( handle ); + else + m_bHandleSignals = handle; +} + +void OCompletionBase::setCompletionMode( OGlobalSettings::Completion mode ) +{ + if ( m_delegate ) { + m_delegate->setCompletionMode( mode ); + return; + } + + m_iCompletionMode = mode; + // Always sync up OCompletion mode with ours as long as we + // are performing completions. + if( m_pCompObj && m_iCompletionMode != OGlobalSettings::CompletionNone ) + m_pCompObj->setCompletionMode( m_iCompletionMode ); +} + +bool OCompletionBase::setKeyBinding( KeyBindingType item, const OShortcut& cut ) +{ + if ( m_delegate ) + return m_delegate->setKeyBinding( item, cut ); + + + if( !cut.isNull() ) + { + for( KeyBindingMap::Iterator it = m_keyMap.begin(); it != m_keyMap.end(); ++it ) + if( it.data() == cut ) return false; + } + m_keyMap.replace( item, cut ); + return true; +} + +void OCompletionBase::useGlobalKeyBindings() +{ + +/* + + if ( m_delegate ) { + m_delegate->useGlobalKeyBindings(); + return; + } + + m_keyMap.clear(); + m_keyMap.insert( TextCompletion, 0 ); + m_keyMap.insert( PrevCompletionMatch, 0 ); + m_keyMap.insert( NextCompletionMatch, 0 ); + m_keyMap.insert( SubstringCompletion, 0 ); + +*/ + +} + +void OCompletionBase::setup( bool autodel, bool hsig, bool esig ) +{ + if ( m_delegate ) { + m_delegate->setup( autodel, hsig, esig ); + return; + } + + m_bAutoDelCompObj = autodel; + m_bHandleSignals = hsig; + m_bEmitSignals = esig; +} diff --git a/libopie2/qt3/opiecore/ocompletionbase.h b/libopie2/qt3/opiecore/ocompletionbase.h new file mode 100644 index 0000000..517667e --- a/dev/null +++ b/libopie2/qt3/opiecore/ocompletionbase.h @@ -0,0 +1,403 @@ +/* + This file is part of the Opie Project + + Copyright (C) 2003 Michael Lauer <mickey@tm.informatik.uni-frankfurt.de> + Inspired by the KDE completion classes which are + Copyright (C) 2000 Dawit Alemayehu <adawit@kde.org> + =. + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef OCOMPLETIONBASE_H +#define OCOMPLETIONBASE_H + +/** + * An abstract base class for adding a completion feature + * into widgets. + * + * This is a convenience class that provides the basic functions + * needed to add text completion support into widgets. All that + * is required is an implementation for the pure virtual function + * @ref setCompletedText. Refer to @ref OLineEdit or @ref OComboBox + * to see how easily such support can be added using this as a base + * class. + * + * @short An abstract class for adding text completion support to widgets. + * @author Dawit Alemayehu <adawit@kde.org> + */ + +class OCompletionBase +{ + + public: + + /** + * Constants that represent the items whose short-cut + * key-binding is programmable. The default key-bindings + * for these items are defined in @ref OStdAccel. + */ + enum KeyBindingType { + /** + * Text completion (by default Ctrl-E). + */ + TextCompletion, + /** + * Switch to previous completion (by default Ctrl-Up). + */ + PrevCompletionMatch, + /** + * Switch to next completion (by default Ctrl-Down). + */ + NextCompletionMatch, + /** + * Substring completion (by default Ctrl-T). + */ + SubstringCompletion + }; + + + // Map for the key binding types mentioned above. + typedef QMap<KeyBindingType, OShortcut> KeyBindingMap; + + /** + * Default constructor. + */ + OCompletionBase(); + + /** + * Destructor. + */ + virtual ~OCompletionBase(); + + /** + * Returns a pointer to the current completion object. + * + * If the object does not exist, it is automatically + * created. Note that the completion object created + * here is used by default to handle the signals + * internally. It is also deleted when this object's + * destructor is invoked. If you do not want these + * default settings, use @ref setAutoDeleteCompletionObject + * and @ref setHandleSignals to change the behavior. + * Alternatively, you can set the boolean parameter to + * false to disable the automatic handling of the signals + * by this object. Note that the boolean argument will be + * ignored if there already exists a completion object since + * no new object needs to be created. You need to use either + * @ref setHandleSignals or @ref setCompletionObject for + * such cases depending on your requirement. + * + * @param hsig if true, handles signals internally. + * @return a pointer the completion object. + */ + OCompletion* completionObject( bool hsig = true ); + + /** + * Sets up the completion object to be used. + * + * This method assigns the completion object and sets it + * up to automatically handle the completion and rotation + * signals internally. You should use this function if + * you want to share one completion object among you widgets + * or need to use a customized completion object. + * + * The object assigned through this method is not deleted + * when this object's destructor is invoked unless you + * explicitly call @ref setAutoDeleteCompletionObject after + * calling this method. Also if you do not want the signals + * to be handled by an internal implementation, be sure to + * set the bool argument to false. + * + * This method is also called when a completion-object is created + * automatically, when completionObject() is called the first time. + * + * @param compObj a @ref OCompletion() or a derived child object. + * @param hsig if true, handles signals internally. + */ + virtual void setCompletionObject( OCompletion* /*compObj*/, bool hsig = true ); + + /** + * Enables this object to handle completion and rotation + * events internally. + * + * This function simply assigns a boolean value that + * indicates whether it should handle rotation and + * completion events or not. Note that this does not + * stop the object from emitting signals when these + * events occur. + * + * @param handle if true, handle completion & rotation internally. + */ + virtual void setHandleSignals( bool /*handle*/ ); + + /** + * Returns true if the completion object is deleted + * upon this widget's destruction. + * + * See @ref setCompletionObject() and @ref enableCompletion() + * for details. + * + * @return true if the completion object + */ + bool isCompletionObjectAutoDeleted() const { + return m_delegate ? m_delegate->isCompletionObjectAutoDeleted() : m_bAutoDelCompObj; + } + + /** + * Sets the completion object when this widget's destructor + * is called. + * + * If the argument is set to true, the completion object + * is deleted when this widget's destructor is called. + * + * @param autoDelete if true, delete completion object on destruction. + */ + void setAutoDeleteCompletionObject( bool autoDelete ) { + if ( m_delegate ) + m_delegate->setAutoDeleteCompletionObject( autoDelete ); + else + m_bAutoDelCompObj = autoDelete; + } + + /** + * Sets the widget's ability to emit text completion and + * rotation signals. + * + * Invoking this function with @p enable set to @p false will + * cause the completion & rotation signals not to be emitted. + * However, unlike setting the completion object to @p NULL + * using @ref setCompletionObject, disabling the emition of + * the signals through this method does not affect the current + * completion object. + * + * There is no need to invoke this function by default. When a + * completion object is created through @ref completionObject or + * @ref setCompletionObject, these signals are set to emit + * automatically. Also note that disabling this signals will not + * necessarily interfere with the objects ability to handle these + * events internally. See @ref setHandleSignals. + * + * @param enable if false, disables the emition of completion & rotation signals. + */ + void setEnableSignals( bool enable ) { + if ( m_delegate ) + m_delegate->setEnableSignals( enable ); + else + m_bEmitSignals = enable; + } + + /** + * Returns true if the object handles the signals + * + * @return true if this signals are handled internally. + */ + bool handleSignals() const { return m_delegate ? m_delegate->handleSignals() : m_bHandleSignals; } + + /** + * Returns true if the object emits the signals + * + * @return true if signals are emitted + */ + bool emitSignals() const { return m_delegate ? m_delegate->emitSignals() : m_bEmitSignals; } + + /** + * Sets the type of completion to be used. + * + * The completion modes supported are those defined in + * @ref OGlobalSettings(). See below. + * + * @param mode Completion type: + * @li CompletionNone: Disables completion feature. + * @li CompletionAuto: Attempts to find a match & + * fills-in the remaining text. + * @li CompletionMan: Acts the same as the above + * except the action has to be + * manually triggered through + * pre-defined completion key. + * @li CompletionShell: Mimics the completion feature + * found in typical *nix shell + * environments. + * @li CompletionPopup: Shows all available completions at once, + * in a listbox popping up. + */ + virtual void setCompletionMode( OGlobalSettings::Completion mode ); + + /** + * Returns the current completion mode. + * + * The return values are of type @ref OGlobalSettings::Completion. + * See @ref setCompletionMode() for details. + * + * @return the completion mode. + */ + OGlobalSettings::Completion completionMode() const { + return m_delegate ? m_delegate->completionMode() : m_iCompletionMode; + } + + /** + * Sets the key-binding to be used for manual text + * completion, text rotation in a history list as + * well as a completion list. + * + * + * When the keys set by this function are pressed, a + * signal defined by the inheriting widget will be activated. + * If the default value or 0 is specified by the second + * parameter, then the key-binding as defined in the global + * setting should be used. This method returns false value + * for @p key is negative or the supplied key-binding conflicts + * with the ones set for one of the other features. + * + * NOTE: To use a modifier key (Shift, Ctrl, Alt) as part of + * the key-binding simply simply @p sum up the values of the + * modifier and the actual key. For example, to use CTRL+E as + * a key binding for one of the items, you would simply supply + * @p "Qt::CtrlButton + Qt::Key_E" as the second argument to this + * function. + * + * @param item the feature whose key-binding needs to be set: + * + * @li TextCompletion the manual completion key-binding. + * @li PrevCompletionMatch the previous match key for multiple completion. + * @li NextCompletionMatch the next match key for for multiple completion. + * @li SubstringCompletion the key for substring completion + * + * @param key key-binding used to rotate down in a list. + * + * @return true if key-binding can successfully be set. + * @see #getKeyBinding + */ + bool setKeyBinding( KeyBindingType /*item*/ , const OShortcut& cut ); + + /** + * Returns the key-binding used for the specified item. + * + * This methods returns the key-binding used to activate + * the feature feature given by @p item. If the binding + * contains modifier key(s), the SUM of the modifier key + * and the actual key code are returned. + * + * @return the key-binding used for the feature given by @p item. + * @see #setKeyBinding + */ + const OShortcut& getKeyBinding( KeyBindingType item ) const { + return m_delegate ? m_delegate->getKeyBinding( item ) : m_keyMap[ item ]; + } + + /** + * Sets this object to use global values for key-bindings. + * + * This method changes the values of the key bindings for + * rotation and completion features to the default values + * provided in OGlobalSettings. + * + * NOTE: By default inheriting widgets should uses the + * global key-bindings so that there will be no need to + * call this method. + */ + void useGlobalKeyBindings(); + + /** + * A pure virtual function that must be implemented by + * all inheriting classes. + * + * This function is intended to allow external completion + * implementations to set completed text appropriately. It + * is mostly relevant when the completion mode is set to + * CompletionAuto and CompletionManual modes. See + * @ref OCompletionBase::setCompletedText. + * Does nothing in CompletionPopup mode, as all available + * matches will be shown in the popup. + * + * @param text the completed text to be set in the widget. + */ + virtual void setCompletedText( const QString& text ) = 0; + + /** + * A pure virtual function that must be implemented by + * all inheriting classes. + * + */ + virtual void setCompletedItems( const QStringList& items ) = 0; + + /** + * Returns a pointer to the completion object. + * + * This method is only different from @ref completionObject() + * in that it does not create a new OCompletion object even if + * the internal pointer is @p NULL. Use this method to get the + * pointer to a completion object when inheriting so that you + * won't inadvertently create it!! + * + * @returns the completion object or NULL if one does not exist. + */ + OCompletion* compObj() const { return m_delegate ? m_delegate->compObj() : (OCompletion*) m_pCompObj; } + +protected: + /** + * Returns a key-binding map + * + * This method is the same as @ref getKeyBinding() except it + * returns the whole keymap containing the key-bindings. + * + * @return the key-binding used for the feature given by @p item. + */ + KeyBindingMap getKeyBindings() const { return m_delegate ? m_delegate->getKeyBindings() : m_keyMap; } + + void setDelegate( OCompletionBase *delegate ); + OCompletionBase *delegate() const { return m_delegate; } + +private: + // This method simply sets the autodelete boolean for + // the completion object, the emit signals and handle + // signals internally flags to the provided values. + void setup( bool, bool, bool ); + + // Flag that determined whether the completion object + // should be deleted when this object is destroyed. + bool m_bAutoDelCompObj; + // Determines whether this widget handles completion signals + // internally or not + bool m_bHandleSignals; + // Determines whether this widget fires rotation signals + bool m_bEmitSignals; + // Stores the completion mode locally. + OGlobalSettings::Completion m_iCompletionMode; + // Pointer to Completion object. + QGuardedPtr<OCompletion> m_pCompObj; + // Keybindings + KeyBindingMap m_keyMap; + // we may act as a proxy to another OCompletionBase object + OCompletionBase *m_delegate; + + // FIXME: Revise this for Opie? + //protected: + // virtual void virtual_hook( int id, void* data ); + private: + OCompletionBasePrivate *d; +}; + +#endif // OCOMPLETIONBASE_H + diff --git a/libopie2/qt3/opiecore/opair.h b/libopie2/qt3/opiecore/opair.h new file mode 100644 index 0000000..26f617d --- a/dev/null +++ b/libopie2/qt3/opiecore/opair.h @@ -0,0 +1,99 @@ +// QPair minus QT_INLINE_TEMPLATE (instead directly using 'inline' directive) +//FIXME: remove and use qpair.h as soon as we're on Qt3 + +/**************************************************************************** +** +** Definition of QPair class +** +** +** Copyright (C) 1992-2001 Trolltech AS. All rights reserved. +** +** This file is part of the tools module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QPAIR_H +#define QPAIR_H + +#ifndef QT_H +#include "qglobal.h" +#include "qdatastream.h" +#endif // QT_H + +template <class T1, class T2> +struct QPair +{ + typedef T1 first_type; + typedef T2 second_type; + + QPair() + : first( T1() ), second( T2() ) + {} + QPair( const T1& t1, const T2& t2 ) + : first( t1 ), second( t2 ) + {} + + T1 first; + T2 second; +}; + +template <class T1, class T2> +inline bool operator==( const QPair<T1, T2>& x, const QPair<T1, T2>& y ) +{ + return x.first == y.first && x.second == y.second; +} + +template <class T1, class T2> +inline bool operator<( const QPair<T1, T2>& x, const QPair<T1, T2>& y ) +{ + return x.first < y.first || + ( !( y.first < x.first ) && x.second < y.second ); +} + +template <class T1, class T2> +inline QPair<T1, T2> qMakePair( const T1& x, const T2& y ) +{ + return QPair<T1, T2>( x, y ); +} + +#ifndef QT_NO_DATASTREAM +template <class T1, class T2> +inline QDataStream& operator>>( QDataStream& s, QPair<T1, T2>& p ) +{ + s >> p.first >> p.second; + return s; +} + +template <class T1, class T2> +inline QDataStream& operator<<( QDataStream& s, const QPair<T1, T2>& p ) +{ + s << p.first << p.second; + return s; +} +#endif + +#endif diff --git a/libopie2/qt3/opiecore/osortablevaluelist.h b/libopie2/qt3/opiecore/osortablevaluelist.h new file mode 100644 index 0000000..f66cf25 --- a/dev/null +++ b/libopie2/qt3/opiecore/osortablevaluelist.h @@ -0,0 +1,117 @@ +/* + This file is part of the Opie Project + Originally a part of the KDE Project + (C) 2001 Carsten Pfeiffer <pfeiffer@kde.org> + =. + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef OSORTABLEVALUELIST_H +#define OSORTABLEVALUELIST_H + +#if QT_VERSION > 290 +#include <qtl.h> +#include <qpair.h> +#else +#include <opie2/otl.h> +#include <opie2/opair.h> +#endif +#include <qvaluelist.h> + +template<class T, class Key = int> class OSortableItem : public QPair<Key,T> +{ +public: + OSortableItem( Key i, const T& t ) : QPair<Key, T>( i, t ) {} + OSortableItem( const OSortableItem<T, Key> &rhs ) + : QPair<Key,T>( rhs.first, rhs.second ) {} + + OSortableItem() {} + + OSortableItem<T, Key> &operator=( const OSortableItem<T, Key>& i ) { + first = i.first; + second = i.second; + return *this; + } + + // operators for sorting + bool operator> ( const OSortableItem<T, Key>& i2 ) const { + return (i2.first < first); + } + bool operator< ( const OSortableItem<T, Key>& i2 ) const { + return (first < i2.first); + } + bool operator>= ( const OSortableItem<T, Key>& i2 ) const { + return (first >= i2.first); + } + bool operator<= ( const OSortableItem<T, Key>& i2 ) const { + return !(i2.first < first); + } + bool operator== ( const OSortableItem<T, Key>& i2 ) const { + return (first == i2.first); + } + bool operator!= ( const OSortableItem<T, Key>& i2 ) const { + return (first != i2.first); + } + + T& value() { + return second; + } + const T& value() const { + return second; + } + + Key index() const { + return first; + } +}; + + +// convenience +template <class T, class Key = int> +class OSortableValueList : public QValueList<OSortableItem<T, Key> > +{ +public: + void insert( Key i, const T& t ) { + QValueList<OSortableItem<T, Key> >::append( OSortableItem<T, Key>( i, t ) ); + } + // add more as you please... + + T& operator[]( Key i ) { + return QValueList<OSortableItem<T, Key> >::operator[]( i ).value(); + } + const T& operator[]( Key i ) const { + return QValueList<OSortableItem<T, Key> >::operator[]( i ).value(); + } + + void sort() { + qHeapSort( *this ); + } +}; + +// template <class T> class OSortableValueListIterator : public QValueListIterator<OSortableItem<T> > +// { +// }; + +#endif // OSORTABLEVALUELIST_H diff --git a/libopie2/qt3/opiecore/otl.h b/libopie2/qt3/opiecore/otl.h new file mode 100644 index 0000000..ee2a28e --- a/dev/null +++ b/libopie2/qt3/opiecore/otl.h @@ -0,0 +1,325 @@ +// qtl minus QT_INLINE_TEMPLATE and QT_EXPLICIT (instead directly using 'inline' directive) +//FIXME: remove and use qtl.h as soon as we're on Qt3 + +/**************************************************************************** +** $Id$ +** +** Definition of Qt template library classes +** +** Created : 990128 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file is part of the tools module of the Qt GUI Toolkit. +** +** This file may be distributed under the terms of the Q Public License +** as defined by Trolltech AS of Norway and appearing in the file +** LICENSE.QPL included in the packaging of this file. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. +** See http://www.trolltech.com/qpl/ for QPL licensing information. +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** +** Contact info@trolltech.com if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#ifndef QTL_H +#define QTL_H + +#ifndef QT_H +#include "qglobal.h" +#include "qtextstream.h" +#include "qstring.h" +#endif // QT_H + +#ifndef QT_NO_TEXTSTREAM +template <class T> +class QTextOStreamIterator +{ +protected: + QTextOStream& stream; + QString separator; + +public: + QTextOStreamIterator( QTextOStream& s) : stream( s ) {} + QTextOStreamIterator( QTextOStream& s, const QString& sep ) + : stream( s ), separator( sep ) {} + QTextOStreamIterator<T>& operator= ( const T& x ) { + stream << x; + if ( !separator.isEmpty() ) + stream << separator; + return *this; + } + QTextOStreamIterator<T>& operator*() { return *this; } + QTextOStreamIterator<T>& operator++() { return *this; } + QTextOStreamIterator<T>& operator++(int) { return *this; } +}; +#endif //QT_NO_TEXTSTREAM + +template <class InputIterator, class OutputIterator> +inline OutputIterator qCopy( InputIterator _begin, InputIterator _end, + OutputIterator _dest ) +{ + while( _begin != _end ) + *_dest++ = *_begin++; + return _dest; +} + +template <class BiIterator, class BiOutputIterator> +inline BiOutputIterator qCopyBackward( BiIterator _begin, BiIterator _end, + BiOutputIterator _dest ) +{ + while ( _begin != _end ) + *--_dest = *--_end; + return _dest; +} + +template <class InputIterator1, class InputIterator2> +inline bool qEqual( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2 ) +{ + // ### compare using !(*first1 == *first2) in Qt 4.0 + for ( ; first1 != last1; ++first1, ++first2 ) + if ( *first1 != *first2 ) + return FALSE; + return TRUE; +} + +template <class ForwardIterator, class T> +inline void qFill( ForwardIterator first, ForwardIterator last, const T& val ) +{ + for ( ; first != last; ++first ) + *first = val; +} + +#if 0 +template <class BiIterator, class OutputIterator> +inline OutputIterator qReverseCopy( BiIterator _begin, BiIterator _end, + OutputIterator _dest ) +{ + while ( _begin != _end ) { + --_end; + *_dest = *_end; + ++_dest; + } + return _dest; +} +#endif + + +template <class InputIterator, class T> +inline InputIterator qFind( InputIterator first, InputIterator last, + const T& val ) +{ + while ( first != last && *first != val ) + ++first; + return first; +} + +template <class InputIterator, class T, class Size> +inline void qCount( InputIterator first, InputIterator last, const T& value, + Size& n ) +{ + for ( ; first != last; ++first ) + if ( *first == value ) + ++n; +} + +template <class T> +inline void qSwap( T& _value1, T& _value2 ) +{ + T tmp = _value1; + _value1 = _value2; + _value2 = tmp; +} + + +template <class InputIterator> +inline void qBubbleSort( InputIterator b, InputIterator e ) +{ + // Goto last element; + InputIterator last = e; + --last; + // only one element or no elements ? + if ( last == b ) + return; + + // So we have at least two elements in here + while( b != last ) { + bool swapped = FALSE; + InputIterator swap_pos = b; + InputIterator x = e; + InputIterator y = x; + y--; + do { + --x; + --y; + if ( *x < *y ) { + swapped = TRUE; + qSwap( *x, *y ); + swap_pos = y; + } + } while( y != b ); + if ( !swapped ) + return; + b = swap_pos; + b++; + } +} + + +template <class Container> +inline void qBubbleSort( Container &c ) +{ + qBubbleSort( c.begin(), c.end() ); +} + + +template <class Value> +inline void qHeapSortPushDown( Value* heap, int first, int last ) +{ + int r = first; + while ( r <= last / 2 ) { + if ( last == 2 * r ) { + // node r has only one child + if ( heap[2 * r] < heap[r] ) + qSwap( heap[r], heap[2 * r] ); + r = last; + } else { + // node r has two children + if ( heap[2 * r] < heap[r] && !(heap[2 * r + 1] < heap[2 * r]) ) { + // swap with left child + qSwap( heap[r], heap[2 * r] ); + r *= 2; + } else if ( heap[2 * r + 1] < heap[r] + && heap[2 * r + 1] < heap[2 * r] ) { + // swap with right child + qSwap( heap[r], heap[2 * r + 1] ); + r = 2 * r + 1; + } else { + r = last; + } + } + } +} + + +template <class InputIterator, class Value> +inline void qHeapSortHelper( InputIterator b, InputIterator e, Value, uint n ) +{ + // Create the heap + InputIterator insert = b; + Value* realheap = new Value[n]; + // Wow, what a fake. But I want the heap to be indexed as 1...n + Value* heap = realheap - 1; + int size = 0; + for( ; insert != e; ++insert ) { + heap[++size] = *insert; + int i = size; + while( i > 1 && heap[i] < heap[i / 2] ) { + qSwap( heap[i], heap[i / 2] ); + i /= 2; + } + } + + // Now do the sorting + for( uint i = n; i > 0; i-- ) { + *b++ = heap[1]; + if ( i > 1 ) { + heap[1] = heap[i]; + qHeapSortPushDown( heap, 1, (int)i - 1 ); + } + } + + delete[] realheap; +} + + +template <class InputIterator> +inline void qHeapSort( InputIterator b, InputIterator e ) +{ + // Empty ? + if ( b == e ) + return; + + // How many entries have to be sorted ? + InputIterator it = b; + uint n = 0; + while ( it != e ) { + ++n; + ++it; + } + + // The second last parameter is a hack to retrieve the value type + // Do the real sorting here + qHeapSortHelper( b, e, *b, n ); +} + + +template <class Container> +inline void qHeapSort( Container &c ) +{ + if ( c.begin() == c.end() ) + return; + + // The second last parameter is a hack to retrieve the value type + // Do the real sorting here + qHeapSortHelper( c.begin(), c.end(), *(c.begin()), (uint)c.count() ); +} + +template <class Container> +class QBackInsertIterator +{ +public: + QBackInsertIterator( Container &c ) + : container( &c ) + { + } + + QBackInsertIterator<Container>& + operator=( const typename Container::value_type &value ) + { + container->push_back( value ); + return *this; + } + + QBackInsertIterator<Container>& operator*() + { + return *this; + } + + QBackInsertIterator<Container>& operator++() + { + return *this; + } + + QBackInsertIterator<Container>& operator++(int) + { + return *this; + } + +protected: + Container *container; +}; + +template <class Container> +inline QBackInsertIterator<Container> qBackInserter( Container &c ) +{ + return QBackInsertIterator<Container>( c ); +} + +#endif diff --git a/libopie2/qt3/opieui/ocombobox.cpp b/libopie2/qt3/opieui/ocombobox.cpp new file mode 100644 index 0000000..a1dd5f5 --- a/dev/null +++ b/libopie2/qt3/opieui/ocombobox.cpp @@ -0,0 +1,666 @@ +/* + This file Copyright (C) 2003 Michael 'Mickey' Lauer <mickey@tm.informatik.uni-frankfurt.de> + is part of the Copyright (C) 2000 Carsten Pfeiffer <pfeiffer@kde.org> + Opie Project Copyright (C) 2000 Dawit Alemayehu <adawit@kde.org> + + =. Originally part of the KDE Project + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +/* QT */ + +#include <qclipboard.h> +#include <qlistbox.h> +#include <qpopupmenu.h> + +/* OPIE */ + +#include <opie2/ocompletionbox.h> +#include <opie2/olineedit.h> +#include <opie2/opixmapprovider.h> +#include <opie2/ocombobox.h> + +/*====================================================================================== + * OComboBoxPrivate + *======================================================================================*/ + +class OComboBox::OComboBoxPrivate +{ +public: + OComboBoxPrivate() + { + olineEdit = 0L; + } + ~OComboBoxPrivate() + { + } + + OLineEdit *olineEdit; +}; + +/*====================================================================================== + * OComboBox + *======================================================================================*/ + +OComboBox::OComboBox( QWidget *parent, const char *name ) + : QComboBox( parent, name ) +{ + init(); +} + +OComboBox::OComboBox( bool rw, QWidget *parent, const char *name ) + : QComboBox( rw, parent, name ) +{ + init(); + + if ( rw ) + { + OLineEdit *edit = new OLineEdit( this, "combo lineedit" ); + setLineEdit( edit ); + } +} + +OComboBox::~OComboBox() +{ + delete d; +} + +void OComboBox::init() +{ + d = new OComboBoxPrivate; + + // Permanently set some parameters in the parent object. + QComboBox::setAutoCompletion( false ); + + // Initialize enable popup menu to false. + // Below it will be enabled if the widget + // is editable. + m_bEnableMenu = false; + + m_trapReturnKey = false; + + // Enable context menu by default if widget + // is editable. + setContextMenuEnabled( true ); + + // for wheelscrolling + installEventFilter( this ); + if ( lineEdit() ) + lineEdit()->installEventFilter( this ); +} + + +bool OComboBox::contains( const QString& _text ) const +{ + if ( _text.isEmpty() ) + return false; + + for (int i = 0; i < count(); i++ ) { + if ( text(i) == _text ) + return true; + } + return false; +} + +void OComboBox::setAutoCompletion( bool autocomplete ) +{ + if ( d->olineEdit ) + { + if ( autocomplete ) + { + d->olineEdit->setCompletionMode( OGlobalSettings::CompletionAuto ); + setCompletionMode( OGlobalSettings::CompletionAuto ); + } + else + { + d->olineEdit->setCompletionMode( OGlobalSettings::completionMode() ); + setCompletionMode( OGlobalSettings::completionMode() ); + } + } +} + +void OComboBox::setContextMenuEnabled( bool showMenu ) +{ + if( d->olineEdit ) + { + d->olineEdit->setContextMenuEnabled( showMenu ); + m_bEnableMenu = showMenu; + } +} + +/* +void OComboBox::setURLDropsEnabled( bool enable ) +{ + if ( d->olineEdit ) + d->olineEdit->setURLDropsEnabled( enable ); +} + +bool OComboBox::isURLDropsEnabled() const +{ + return d->olineEdit && d->olineEdit->isURLDropsEnabled(); +} +*/ + +void OComboBox::setCompletedText( const QString& text, bool marked ) +{ + if ( d->olineEdit ) + d->olineEdit->setCompletedText( text, marked ); +} + +void OComboBox::setCompletedText( const QString& text ) +{ + if ( d->olineEdit ) + d->olineEdit->setCompletedText( text ); +} + +void OComboBox::makeCompletion( const QString& text ) +{ + if( d->olineEdit ) + d->olineEdit->makeCompletion( text ); + + else // read-only combo completion + { + if( text.isNull() || !listBox() ) + return; + + int index = listBox()->index( listBox()->findItem( text ) ); + if( index >= 0 ) { + setCurrentItem( index ); + } + } +} + +void OComboBox::rotateText( OCompletionBase::KeyBindingType type ) +{ + if ( d->olineEdit ) + d->olineEdit->rotateText( type ); +} + +bool OComboBox::eventFilter( QObject* o, QEvent* ev ) +{ + QLineEdit *edit = lineEdit(); + + int type = ev->type(); + + if ( o == edit ) + { + //OCursor::autoHideEventFilter( edit, ev ); + + if ( type == QEvent::KeyPress ) + { + QKeyEvent *e = static_cast<QKeyEvent *>( ev ); + + if ( e->key() == Key_Return || e->key() == Key_Enter) + { + // On Return pressed event, emit both + // returnPressed(const QString&) and returnPressed() signals + emit returnPressed(); + emit returnPressed( currentText() ); + if ( d->olineEdit && d->olineEdit->completionBox(false) && + d->olineEdit->completionBox()->isVisible() ) + d->olineEdit->completionBox()->hide(); + + return m_trapReturnKey; + } + } + } + + + // wheel-scrolling changes the current item + if ( type == QEvent::Wheel ) { + if ( !listBox() || listBox()->isHidden() ) { + QWheelEvent *e = static_cast<QWheelEvent*>( ev ); + static const int WHEEL_DELTA = 120; + int skipItems = e->delta() / WHEEL_DELTA; + if ( e->state() & ControlButton ) // fast skipping + skipItems *= 10; + + int newItem = currentItem() - skipItems; + + if ( newItem < 0 ) + newItem = 0; + else if ( newItem >= count() ) + newItem = count() -1; + + setCurrentItem( newItem ); + if ( !text( newItem ).isNull() ) + emit activated( text( newItem ) ); + emit activated( newItem ); + e->accept(); + return true; + } + } + + return QComboBox::eventFilter( o, ev ); +} + +void OComboBox::setTrapReturnKey( bool grab ) +{ + m_trapReturnKey = grab; +} + +bool OComboBox::trapReturnKey() const +{ + return m_trapReturnKey; +} + +/* +void OComboBox::setEditURL( const OURL& url ) +{ + QComboBox::setEditText( url.prettyURL() ); +} + +void OComboBox::insertURL( const OURL& url, int index ) +{ + QComboBox::insertItem( url.prettyURL(), index ); +} + +void OComboBox::insertURL( const QPixmap& pixmap, const OURL& url, int index ) +{ + QComboBox::insertItem( pixmap, url.prettyURL(), index ); +} + +void OComboBox::changeURL( const OURL& url, int index ) +{ + QComboBox::changeItem( url.prettyURL(), index ); +} + +void OComboBox::changeURL( const QPixmap& pixmap, const OURL& url, int index ) +{ + QComboBox::changeItem( pixmap, url.prettyURL(), index ); +} +*/ + + +void OComboBox::setCompletedItems( const QStringList& items ) +{ + if ( d->olineEdit ) + d->olineEdit->setCompletedItems( items ); +} + + +OCompletionBox * OComboBox::completionBox( bool create ) +{ + if ( d->olineEdit ) + return d->olineEdit->completionBox( create ); + return 0; +} + +// QWidget::create() turns off mouse-Tracking which would break auto-hiding +void OComboBox::create( WId id, bool initializeWindow, bool destroyOldWindow ) +{ + QComboBox::create( id, initializeWindow, destroyOldWindow ); + //OCursor::setAutoHideCursor( lineEdit(), true, true ); +} + +void OComboBox::setLineEdit( OLineEdit *edit ) +{ + #if QT_VERSION > 290 + QComboBox::setLineEdit( edit ); + d->olineEdit = dynamic_cast<OLineEdit*>( edit ); + setDelegate( d->olineEdit ); + + // forward some signals. We only emit returnPressed() ourselves. + if ( d->olineEdit ) { + connect( d->olineEdit, SIGNAL( completion( const QString& )), + SIGNAL( completion( const QString& )) ); + connect( d->olineEdit, SIGNAL( substringCompletion( const QString& )), + SIGNAL( substringCompletion( const QString& )) ); + connect( d->olineEdit, + SIGNAL( textRotation( OCompletionBase::KeyBindingType )), + SIGNAL( textRotation( OCompletionBase::KeyBindingType )) ); + connect( d->olineEdit, + SIGNAL( completionModeChanged( OGlobalSettings::Completion )), + SIGNAL( completionModeChanged( OGlobalSettings::Completion))); + + connect( d->olineEdit, + SIGNAL( aboutToShowContextMenu( QPopupMenu * )), + SIGNAL( aboutToShowContextMenu( QPopupMenu * )) ); + } + #else + #warning OComboBox is not fully functional with Qt2 + #endif +} + +// Temporary functions until QT3 appears. - Seth Chaiklin 20 may 2001 +void OComboBox::deleteWordForward() +{ + lineEdit()->cursorWordForward(TRUE); + #if QT_VERSION > 290 + if ( lineEdit()->hasSelectedText() ) + #else + if ( lineEdit()->hasMarkedText() ) + #endif + { + lineEdit()->del(); + } +} + +void OComboBox::deleteWordBack() +{ + lineEdit()->cursorWordBackward(TRUE); + #if QT_VERSION > 290 + if ( lineEdit()->hasSelectedText() ) + #else + if ( lineEdit()->hasMarkedText() ) + #endif + { + lineEdit()->del(); + } +} + +void OComboBox::setCurrentItem( const QString& item, bool insert, int index ) +{ + int sel = -1; + for (int i = 0; i < count(); ++i) + if (text(i) == item) + { + sel = i; + break; + } + if (sel == -1 && insert) + { + insertItem(item, index); + if (index >= 0) + sel = index; + else + sel = count() - 1; + } + setCurrentItem(sel); +} + +void OComboBox::setCurrentItem(int index) +{ + QComboBox::setCurrentItem(index); +} + + +/*====================================================================================== + * OHistoryCombo + *======================================================================================*/ + +// we are always read-write +OHistoryCombo::OHistoryCombo( QWidget *parent, const char *name ) + : OComboBox( true, parent, name ) +{ + init( true ); // using completion +} + +// we are always read-write +OHistoryCombo::OHistoryCombo( bool useCompletion, + QWidget *parent, const char *name ) + : OComboBox( true, parent, name ) +{ + init( useCompletion ); +} + +void OHistoryCombo::init( bool useCompletion ) +{ + if ( useCompletion ) + completionObject()->setOrder( OCompletion::Weighted ); + + setInsertionPolicy( NoInsertion ); + myIterateIndex = -1; + myRotated = false; + myPixProvider = 0L; + + connect( this, SIGNAL(aboutToShowContextMenu(QPopupMenu*)), + SLOT(addContextMenuItems(QPopupMenu*)) ); + connect( this, SIGNAL( activated(int) ), SLOT( slotReset() )); + connect( this, SIGNAL( returnPressed(const QString&) ), SLOT(slotReset())); +} + +OHistoryCombo::~OHistoryCombo() +{ + delete myPixProvider; +} + +void OHistoryCombo::setHistoryItems( QStringList items, + bool setCompletionList ) +{ + OComboBox::clear(); + + // limit to maxCount() + while ( (int) items.count() > maxCount() && !items.isEmpty() ) + items.remove( items.begin() ); + + insertItems( items ); + + if ( setCompletionList && useCompletion() ) { + // we don't have any weighting information here ;( + OCompletion *comp = completionObject(); + comp->setOrder( OCompletion::Insertion ); + comp->setItems( items ); + comp->setOrder( OCompletion::Weighted ); + } + + clearEdit(); +} + +QStringList OHistoryCombo::historyItems() const +{ + QStringList list; + for ( int i = 0; i < count(); i++ ) + list.append( text( i ) ); + + return list; +} + +void OHistoryCombo::clearHistory() +{ + OComboBox::clear(); + if ( useCompletion() ) + completionObject()->clear(); +} + +void OHistoryCombo::addContextMenuItems( QPopupMenu* menu ) +{ + if ( menu &&!lineEdit()->text().isEmpty()) + { + menu->insertSeparator(); + menu->insertItem( tr("Empty Contents"), this, SLOT( slotClear())); + } +} + +void OHistoryCombo::addToHistory( const QString& item ) +{ + if ( item.isEmpty() || (count() > 0 && item == text(0) )) + return; + + // remove all existing items before adding + if ( !duplicatesEnabled() ) { + for ( int i = 0; i < count(); i++ ) { + if ( text( i ) == item ) + removeItem( i ); + } + } + + // now add the item + if ( myPixProvider ) + //insertItem( myPixProvider->pixmapFor(item, KIcon::SizeSmall), item, 0); + insertItem( myPixProvider->pixmapFor(item, 16), item, 0); + else + insertItem( item, 0 ); + + int last; + QString rmItem; + + bool useComp = useCompletion(); + while ( count() > maxCount() && count() > 0 ) { + // remove the last item, as long as we are longer than maxCount() + // remove the removed item from the completionObject if it isn't + // anymore available at all in the combobox. + last = count() - 1; + rmItem = text( last ); + removeItem( last ); + if ( useComp && !contains( rmItem ) ) + completionObject()->removeItem( rmItem ); + } + + if ( useComp ) + completionObject()->addItem( item ); +} + +bool OHistoryCombo::removeFromHistory( const QString& item ) +{ + if ( item.isEmpty() ) + return false; + + bool removed = false; + QString temp = currentText(); + for ( int i = 0; i < count(); i++ ) { + while ( item == text( i ) ) { + removed = true; + removeItem( i ); + } + } + + if ( removed && useCompletion() ) + completionObject()->removeItem( item ); + + setEditText( temp ); + return removed; +} + +void OHistoryCombo::keyPressEvent( QKeyEvent *e ) +{ + // save the current text in the lineedit + if ( myIterateIndex == -1 ) + myText = currentText(); + + // going up in the history, rotating when reaching QListBox::count() + //if ( OStdAccel::isEqual( e, OStdAccel::rotateUp() ) ) { + if ( e->key() == Qt::Key_Up ) { + myIterateIndex++; + + // skip duplicates/empty items + while ( myIterateIndex < count()-1 && + (currentText() == text( myIterateIndex ) || + text( myIterateIndex ).isEmpty()) ) + myIterateIndex++; + + if ( myIterateIndex >= count() ) { + myRotated = true; + myIterateIndex = -1; + + // if the typed text is the same as the first item, skip the first + if ( myText == text(0) ) + myIterateIndex = 0; + + setEditText( myText ); + } + else + setEditText( text( myIterateIndex )); + } + + + // going down in the history, no rotation possible. Last item will be + // the text that was in the lineedit before Up was called. + //else if ( OStdAccel::isEqual( e, OStdAccel::rotateDown() ) ) { + else if ( e->key() == Qt::Key_Down ) { + myIterateIndex--; + + // skip duplicates/empty items + while ( myIterateIndex >= 0 && + (currentText() == text( myIterateIndex ) || + text( myIterateIndex ).isEmpty()) ) + myIterateIndex--; + + + if ( myIterateIndex < 0 ) { + if ( myRotated && myIterateIndex == -2 ) { + myRotated = false; + myIterateIndex = count() - 1; + setEditText( text(myIterateIndex) ); + } + else { // bottom of history + if ( myIterateIndex == -2 ) { + qDebug( "ONotifyClient is not implemented yet." ); + //ONotifyClient::event( ONotifyClient::notification, + // i18n("No further item in the history.")); + } + + myIterateIndex = -1; + if ( currentText() != myText ) + setEditText( myText ); + } + } + else + setEditText( text( myIterateIndex )); + } + + else + OComboBox::keyPressEvent( e ); +} + +void OHistoryCombo::slotReset() +{ + myIterateIndex = -1; + myRotated = false; +} + + +void OHistoryCombo::setPixmapProvider( OPixmapProvider *prov ) +{ + if ( myPixProvider == prov ) + return; + + delete myPixProvider; + myPixProvider = prov; + + // re-insert all the items with/without pixmap + // I would prefer to use changeItem(), but that doesn't honour the pixmap + // when using an editable combobox (what we do) + if ( count() > 0 ) { + QStringList items( historyItems() ); + clear(); + insertItems( items ); + } +} + +void OHistoryCombo::insertItems( const QStringList& items ) +{ + QStringList::ConstIterator it = items.begin(); + QString item; + while ( it != items.end() ) { + item = *it; + if ( !item.isEmpty() ) { // only insert non-empty items + if ( myPixProvider ) + // insertItem( myPixProvider->pixmapFor(item, OIcon::SizeSmall), item ); + insertItem( myPixProvider->pixmapFor(item, 16), item ); + else + insertItem( item ); + } + ++it; + } +} + +void OHistoryCombo::slotClear() +{ + clearHistory(); + emit cleared(); +} + diff --git a/libopie2/qt3/opieui/ocombobox.h b/libopie2/qt3/opieui/ocombobox.h new file mode 100644 index 0000000..4e35b61 --- a/dev/null +++ b/libopie2/qt3/opieui/ocombobox.h @@ -0,0 +1,790 @@ +/* + This file Copyright (C) 2003 Michael 'Mickey' Lauer <mickey@tm.informatik.uni-frankfurt.de> + is part of the Copyright (C) 2000 Carsten Pfeiffer <pfeiffer@kde.org> + Opie Project Copyright (C) 2000 Dawit Alemayehu <adawit@kde.org> + + =. Originally part of the KDE projects + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef OCOMBOBOX_H +#define OCOMBOBOX_H + +/* QT */ + +#include <qcombobox.h> + +/* OPIE */ + +#include <opie2/olineedit.h> +#include <opie2/ocompletion.h> +#include <opie2/ocompletionbase.h> + +/* FORWARDS */ + +class QListBoxItem; +class QPopupMenu; +class OCompletionBox; +typedef QString OURL; + +/** + * A combined button, line-edit and a popup list widget. + * + * @sect Detail + * + * This widget inherits from @ref QComboBox and implements + * the following additional functionalities: a completion + * object that provides both automatic and manual text + * completion as well as text rotation features, configurable + * key-bindings to activate these features, and a popup-menu + * item that can be used to allow the user to set text completion + * modes on the fly based on their preference. + * + * To support these new features OComboBox also emits a few + * more additional signals as well. The main ones are the + * @ref completion( const QString& ) and @ref textRotation( KeyBindingType ) + * signals. The completion signal is intended to be connected to a slot + * that will assist the user in filling out the remaining text while + * the rotation signals is intended to be used to traverse through all + * possible matches whenever text completion results in multiple matches. + * The @ref returnPressed() and @ref returnPressed( const QString& ) + * signal is emitted when the user presses the Enter/Return key. + * + * This widget by default creates a completion object when you invoke + * the @ref completionObject( bool ) member function for the first time + * or use @ref setCompletionObject( OCompletion*, bool ) to assign your + * own completion object. Additionally, to make this widget more functional, + * OComboBox will by default handle the text rotation and completion + * events internally whenever a completion object is created through either + * one of the methods mentioned above. If you do not need this functionality, + * simply use @ref OCompletionBase::setHandleSignals( bool ) or alternatively + * set the boolean parameter in the above methods to FALSE. + * + * The default key-bindings for completion and rotation is determined + * from the global settings in @ref OStdAccel. These values, however, + * can be overriden locally by invoking @ref OCompletionBase::setKeyBinding(). + * The values can easily be reverted back to the default setting, by simply + * calling @ref useGlobalSettings(). An alternate method would be to default + * individual key-bindings by usning @ref setKeyBinding() with the default + * second argument. + * + * Note that if this widget is not editable ( i.e. select-only ), then only + * one completion mode, @p CompletionAuto, will work. All the other modes are + * simply ignored. The @p CompletionAuto mode in this case allows you to + * automatically select an item from the list by trying to match the pressed + * keycode with the first letter of the enteries in the combo box. + * + * @sect Useage + * + * To enable the basic completion feature: + * + * <pre> + * OComboBox *combo = new OComboBox( true, this, "mywidget" ); + * OCompletion *comp = combo->completionObject(); + * // Connect to the return pressed signal - optional + * connect(combo,SIGNAL(returnPressed(const QString&)),comp,SLOT(addItem(const QString&)); + * </pre> + * + * To use your own completion object: + * + * <pre> + * OComboBox *combo = new OComboBox( this,"mywidget" ); + * OURLCompletion *comp = new OURLCompletion(); + * combo->setCompletionObject( comp ); + * // Connect to the return pressed signal - optional + * connect(combo,SIGNAL(returnPressed(const QString&)),comp,SLOT(addItem(const QString&)); + * </pre> + * + * Note that you have to either delete the allocated completion object + * when you don't need it anymore, or call + * setAutoDeleteCompletionObject( true ); + * + * Miscellaneous function calls: + * + * <pre> + * // Tell the widget not to handle completion and rotation + * combo->setHandleSignals( false ); + * // Set your own completion key for manual completions. + * combo->setKeyBinding( OCompletionBase::TextCompletion, Qt::End ); + * // Hide the context (popup) menu + * combo->setContextMenuEnabled( false ); + * // Temporarly disable signal emition + * combo->disableSignals(); + * // Default the all key-bindings to their system-wide settings. + * combo->useGlobalKeyBindings(); + * </pre> + * + * @short An enhanced combo box. + * @author Dawit Alemayehu <adawit@kde.org> + */ +class OComboBox : public QComboBox, public OCompletionBase +{ + Q_OBJECT + + //Q_PROPERTY( bool autoCompletion READ autoCompletion WRITE setAutoCompletion ) + //Q_PROPERTY( bool contextMenuEnabled READ isContextMenuEnabled WRITE setContextMenuEnabled ) + //Q_PROPERTY( bool urlDropsEnabled READ isURLDropsEnabled WRITE setURLDropsEnabled ) + +public: + + /** + * Constructs a read-only or rather select-only combo box with a + * parent object and a name. + * + * @param parent The parent object of this widget + * @param name The name of this widget + */ + OComboBox( QWidget *parent=0, const char *name=0 ); + + /** + * Constructs a "read-write" or "read-only" combo box depending on + * the value of the first argument( @p rw ) with a parent, a + * name. + * + * @param rw When @p true, widget will be editable. + * @param parent The parent object of this widget. + * @param name The name of this widget. + */ + OComboBox( bool rw, QWidget *parent=0, const char *name=0 ); + + /** + * Destructor. + */ + virtual ~OComboBox(); + + /** + * Sets @p url into the edit field of the combobox. It uses + * @ref OURL::prettyURL() so that the url is properly decoded for + * displaying. + */ + //void setEditURL( const OURL& url ); + + /** + * Inserts @p url at position @p index into the combobox. The item will + * be appended if @p index is negative. @ref OURL::prettyURL() is used + * so that the url is properly decoded for displaying. + */ + //void insertURL( const OURL& url, int index = -1 ); + + /** + * Inserts @p url with the pixmap &p pixmap at position @p index into + * the combobox. The item will be appended if @p index is negative. + * @ref OURL::prettyURL() is used so that the url is properly decoded + * for displaying. + */ + //void insertURL( const QPixmap& pixmap, const OURL& url, int index = -1 ); + + /** + * Replaces the item at position @p index with @p url. + * @ref OURL::prettyURL() is used so that the url is properly decoded + * for displaying. + */ + //void changeURL( const OURL& url, int index ); + + /** + * Replaces the item at position @p index with @p url and pixmap @p pixmap. + * @ref OURL::prettyURL() is used so that the url is properly decoded + * for displaying. + */ + //void changeURL( const QPixmap& pixmap, const OURL& url, int index ); + + /** + * Returns the current cursor position. + * + * This method always returns a -1 if the combo-box is @em not + * editable (read-write). + * + * @return Current cursor position. + */ + int cursorPosition() const { return ( lineEdit() ) ? lineEdit()->cursorPosition() : -1; } + + /** + * Re-implemented from @ref QComboBox. + * + * If @p true, the completion mode will be set to automatic. + * Otherwise, it is defaulted to the global setting. This + * method has been replaced by the more comprehensive + * @ref setCompletionMode(). + * + * @param autocomplete Flag to enable/disable automatic completion mode. + */ + virtual void setAutoCompletion( bool autocomplete ); + + /** + * Re-implemented from QComboBox. + * + * Returns @p true if the current completion mode is set + * to automatic. See its more comprehensive replacement + * @ref completionMode(). + * + * @return @p true when completion mode is automatic. + */ + bool autoCompletion() const { + return completionMode() == OGlobalSettings::CompletionAuto; + } + + /** + * Enables or disable the popup (context) menu. + * + * This method only works if this widget is editable, i.e. + * read-write and allows you to enable/disable the context + * menu. It does nothing if invoked for a none-editable + * combo-box. Note that by default the mode changer item + * is made visiable whenever the context menu is enabled. + * Use @ref hideModechanger() if you want to hide this + * item. Also by default, the context menu is created if + * this widget is editable. Call this function with the + * argument set to false to disable the popup menu. + * + * @param showMenu If @p true, show the context menu. + * @param showMode If @p true, show the mode changer. + */ + virtual void setContextMenuEnabled( bool showMenu ); + + /** + * Returns @p true when the context menu is enabled. + */ + bool isContextMenuEnabled() const { return m_bEnableMenu; } + + /** + * Enables/Disables handling of URL drops. If enabled and the user + * drops an URL, the decoded URL will be inserted. Otherwise the default + * behaviour of QComboBox is used, which inserts the encoded URL. + * + * @param enable If @p true, insert decoded URLs + */ + //void setURLDropsEnabled( bool enable ); + + /** + * Returns @p true when decoded URL drops are enabled + */ + //bool isURLDropsEnabled() const; + + /** + * Convenience method which iterates over all items and checks if + * any of them is equal to @p text. + * + * If @p text is an empty string, @p false + * is returned. + * + * @return @p true if an item with the string @p text is in the combobox. + */ + bool contains( const QString& text ) const; + + /** + * By default, OComboBox recognizes Key_Return and Key_Enter + * and emits + * the @ref returnPressed() signals, but it also lets the event pass, + * for example causing a dialog's default-button to be called. + * + * Call this method with @p trap equal to true to make OComboBox + * stop these + * events. The signals will still be emitted of course. + * + * Only affects read-writable comboboxes. + * + * @see setTrapReturnKey() + */ + void setTrapReturnKey( bool trap ); + + /** + * @return @p true if keyevents of Key_Return or Key_Enter will + * be stopped or if they will be propagated. + * + * @see setTrapReturnKey () + */ + bool trapReturnKey() const; + + /** + * Re-implemented for internal reasons. API not affected. + * + * @reimplemented + */ + virtual bool eventFilter( QObject *, QEvent * ); + + /** + * @returns the completion-box, that is used in completion mode + * @ref OGlobalSettings::CompletionPopup and @ref OGlobalSettings::CompletionPopupAuto. + * This method will create a completion-box by calling + * @ref OLineEdit::completionBox, if none is there, yet. + * + * @param create Set this to false if you don't want the box to be created + * i.e. to test if it is available. + */ + OCompletionBox * completionBox( bool create = true ); + + virtual void setLineEdit( OLineEdit * ); + +signals: + /** + * Emitted when the user presses the Enter key. + * + * Note that this signal is only + * emitted if this widget is editable. + */ + void returnPressed(); + + /** + * Emitted when the user presses + * the Enter key. + * + * The argument is the current + * text being edited. This signal is just like + * @ref returnPressed() except it contains the + * current text as its argument. + * + * Note that this signal is only emitted if this + * widget is editable. + */ + void returnPressed( const QString& ); + + /** + * This signal is emitted when the completion key + * is pressed. + * + * The argument is the current text + * being edited. + * + * Note that this signal is @em not available if this + * widget is non-editable or the completion mode is + * set to @p OGlobalSettings::CompletionNone. + */ + void completion( const QString& ); + + /** + * Emitted when the shortcut for substring completion is pressed. + */ + void substringCompletion( const QString& ); + + /** + * Emitted when the text rotation key-bindings are pressed. + * + * The argument indicates which key-binding was pressed. + * In this case this can be either one of four values: + * @p PrevCompletionMatch, @p NextCompletionMatch, @p RotateUp or + * @p RotateDown. See @ref OCompletionBase::setKeyBinding() for + * details. + * + * Note that this signal is @em NOT emitted if the completion + * mode is set to CompletionNone. + */ + void textRotation( OCompletionBase::KeyBindingType ); + + /** + * Emitted when the user changed the completion mode by using the + * popupmenu. + */ + void completionModeChanged( OGlobalSettings::Completion ); + + /** + * Emitted before the context menu is displayed. + * + * The signal allows you to add your own entries into the + * the context menu that is created on demand. + * + * NOTE: Do not store the pointer to the QPopupMenu + * provided through since it is created and deleted + * on demand. + * + * @param the context menu about to be displayed + */ + void aboutToShowContextMenu( QPopupMenu * ); + +public slots: + + /** + * Iterates through all possible matches of the completed text + * or the history list. + * + * Depending on the value of the argument, this function either + * iterates through the history list of this widget or the all + * possible matches in whenever multiple matches result from a + * text completion request. Note that the all-possible-match + * iteration will not work if there are no previous matches, i.e. + * no text has been completed and the *nix shell history list + * rotation is only available if the insertion policy for this + * widget is set either @p QComobBox::AtTop or @p QComboBox::AtBottom. + * For other insertion modes whatever has been typed by the user + * when the rotation event was initiated will be lost. + * + * @param type The key-binding invoked. + */ + void rotateText( OCompletionBase::KeyBindingType /* type */ ); + + /** + * Sets the completed text in the line-edit appropriately. + * + * This function is an implementation for + * @ref OCompletionBase::setCompletedText. + */ + virtual void setCompletedText( const QString& ); + + /** + * Sets @p items into the completion-box if @ref completionMode() is + * CompletionPopup. The popup will be shown immediately. + */ + void setCompletedItems( const QStringList& items ); + + public: + /** + * Selects the first item that matches @p item. If there is no such item, + * it is inserted at position @p index if @p insert is true. Otherwise, + * no item is selected. + */ + void setCurrentItem( const QString& item, bool insert = false, int index = -1 ); + void setCurrentItem(int index); + +protected slots: + + /** + * @deprecated. + */ + virtual void itemSelected( QListBoxItem* ) {}; + + /** + * Completes text according to the completion mode. + * + * Note: this method is @p not invoked if the completion mode is + * set to CompletionNone. Also if the mode is set to @p CompletionShell + * and multiple matches are found, this method will complete the + * text to the first match with a beep to inidicate that there are + * more matches. Then any successive completion key event iterates + * through the remaining matches. This way the rotation functionality + * is left to iterate through the list as usual. + */ + virtual void makeCompletion( const QString& ); + +protected: + /* + * This function simply sets the lineedit text and + * highlights the text appropriately if the boolean + * value is set to true. + * + * @param + * @param + */ + virtual void setCompletedText( const QString& /* */, bool /*marked*/ ); + + /** + * Reimplemented for internal reasons, the API is not affected. + */ + virtual void create( WId = 0, bool initializeWindow = true, + bool destroyOldWindow = true ); + +private: + // Constants that represent the ID's of the popup menu. + // TODO: See if we can replace this mess with OActionMenu + // in the future though this is working lovely. + enum MenuID { + Default=0, + Cut, + Copy, + Paste, + Clear, + Unselect, + SelectAll, + NoCompletion, + AutoCompletion, + ShellCompletion, + PopupCompletion, + SemiAutoCompletion + }; + + /** + * Initializes the variables upon construction. + */ + void init(); + /** + * Temporary functions to delete words back and foward until + * alternatives are available in QT3 (Seth Chaiklin, 21 may 2001) + */ + void deleteWordBack(); + void deleteWordForward(); + + bool m_bEnableMenu; + + // indicating if we should stop return-key events from propagating + bool m_trapReturnKey; + +//protected: +// virtual void virtual_hook( int id, void* data ); +private: + class OComboBoxPrivate; + OComboBoxPrivate *d; +}; + + +class OPixmapProvider; + +/** + * A combobox which implements a history like a unix shell. You can navigate + * through all the items by using the Up or Down arrows (configurable of + * course). Additionally, weighted completion is available. So you should + * load and save the completion list to preserve the weighting between + * sessions. + * + * @author Carsten Pfeiffer <pfeiffer@kde.org> + * @short A combobox for offering a history and completion + */ +class OHistoryCombo : public OComboBox +{ + Q_OBJECT + Q_PROPERTY( QStringList historyItems READ historyItems WRITE setHistoryItems ) + +public: + /** + * Constructs a "read-write" combobox. A read-only history combobox + * doesn't make much sense, so it is only available as read-write. + * Completion will be used automatically for the items in the combo. + * + * The insertion-policy is set to NoInsertion, you have to add the items + * yourself via the slot @ref addToHistory. If you want every item added, + * use + * + * <pre> + * connect( combo, SIGNAL( activated( const QString& )), + * combo, SLOT( addToHistory( const QString& ))); + * </pre> + * + * Use @ref QComboBox::setMaxCount() to limit the history. + * + * @p parent the parent object of this widget. + * @p name the name of this widget. + */ + OHistoryCombo( QWidget *parent = 0L, const char *name = 0L ); + + // ### merge these two constructors + /** + * Same as the previous constructor, but additionally has the option + * to specify whether you want to let OHistoryCombo handle completion + * or not. If set to @p true, OHistoryCombo will sync the completion to the + * contents of the combobox. + */ + OHistoryCombo( bool useCompletion, + QWidget *parent = 0L, const char *name = 0L ); + + /** + * Destructs the combo, the completion-object and the pixmap-provider + */ + ~OHistoryCombo(); + + /** + * Inserts @p items into the combobox. @p items might get + * truncated if it is longer than @ref maxCount() + * + * @see #historyItems + */ + inline void setHistoryItems( QStringList items ) { + setHistoryItems(items, false); + } + + /** + * Inserts @p items into the combobox. @p items might get + * truncated if it is longer than @ref maxCount() + * + * Set @p setCompletionList to true, if you don't have a list of + * completions. This tells OHistoryCombo to use all the items for the + * completion object as well. + * You won't have the benefit of weighted completion though, so normally + * you should do something like + * <pre> + * OConfig *config = kapp->config(); + * QStringList list; + * + * // load the history and completion list after creating the history combo + * list = config->readListEntry( "Completion list" ); + * combo->completionObject()->setItems( list ); + * list = config->readListEntry( "History list" ); + * combo->setHistoryItems( list ); + * + * [...] + * + * // save the history and completion list when the history combo is + * // destroyed + * list = combo->completionObject()->items() + * config->writeEntry( "Completion list", list ); + * list = combo->historyItems(); + * config->writeEntry( "History list", list ); + * </pre> + * + * Be sure to use different names for saving with OConfig if you have more + * than one OHistoryCombo. + * + * Note: When @p setCompletionList is true, the items are inserted into the + * OCompletion object with mode OCompletion::Insertion and the mode is set + * to OCompletion::Weighted afterwards. + * + * @see #historyItems + * @see OComboBox::completionObject + * @see OCompletion::setItems + * @see OCompletion::items + */ + void setHistoryItems( QStringList items, bool setCompletionList ); + + /** + * Returns the list of history items. Empty, when this is not a read-write + * combobox. + * + * @see #setHistoryItems + */ + QStringList historyItems() const; + + /** + * Removes all items named @p item. + * + * @return @p true if at least one item was removed. + * + * @see #addToHistory + */ + bool removeFromHistory( const QString& item ); + + /** + * Sets a pixmap provider, so that items in the combobox can have a pixmap. + * @ref OPixmapProvider is just an abstract class with the one pure virtual + * method @ref OPixmapProvider::pixmapFor(). This method is called whenever + * an item is added to the OHistoryComboBox. Implement it to return your + * own custom pixmaps, or use the @ref OURLPixmapProvider from libkio, + * which uses @ref OMimeType::pixmapForURL to resolve icons. + * + * Set @p prov to 0L if you want to disable pixmaps. Default no pixmaps. + * + * @see #pixmapProvider + */ + void setPixmapProvider( OPixmapProvider *prov ); + + /** + * @returns the current pixmap provider. + * @see #setPixmapProvider + * @see OPixmapProvider + */ + OPixmapProvider * pixmapProvider() const { return myPixProvider; } + + /** + * Resets the current position of the up/down history. Call this + * when you manually call @ref setCurrentItem() or @ref clearEdit(). + */ + void reset() { slotReset(); } + +public slots: + /** + * Adds an item to the end of the history list and to the completion list. + * If @ref maxCount() is reached, the first item of the list will be + * removed. + * + * If the last inserted item is the same as @p item, it will not be + * inserted again. + * + * If @ref duplicatesEnabled() is false, any equal existing item will be + * removed before @p item is added. + * + * Note: By using this method and not the Q and OComboBox insertItem() + * methods, you make sure that the combobox stays in sync with the + * completion. It would be annoying if completion would give an item + * not in the combobox, and vice versa. + * + * @see #removeFromHistory + * @see QComboBox::setDuplicatesEnabled + */ + void addToHistory( const QString& item ); + + /** + * Clears the history and the completion list. + */ + void clearHistory(); + +signals: + /** + * Emitted when the history was cleared by the entry in the popup menu. + */ + void cleared(); + +protected: + /** + * Handling key-events, the shortcuts to rotate the items. + */ + virtual void keyPressEvent( QKeyEvent * ); + + + /** + * Inserts @p items into the combo, honouring @ref pixmapProvider() + * Does not update the completionObject. + * + * Note: @ref duplicatesEnabled() is not honored here. + * + * Called from @ref setHistoryItems() and @ref setPixmapProvider() + */ + void insertItems( const QStringList& items ); + + /** + * @returns if we can modify the completion object or not. + */ + bool useCompletion() const { return compObj() != 0L; } + +private slots: + /** + * Resets the iterate index to -1 + */ + void slotReset(); + + /** + * Called from the popupmenu, + * calls clearHistory() and emits cleared() + */ + void slotClear(); + + /** + * Appends our own context menu entry. + */ + void addContextMenuItems( QPopupMenu* ); + +private: + void init( bool useCompletion ); + + /** + * The current position (index) in the combobox, used for Up and Down + */ + int myIterateIndex; + + /** + * The text typed before Up or Down was pressed. + */ + QString myText; + + /** + * Indicates that the user at least once rotated Up through the entire list + * Needed to allow going back after rotation. + */ + bool myRotated; + OPixmapProvider *myPixProvider; + +private: + class OHistoryComboPrivate; + OHistoryComboPrivate *d; +}; + + +#endif + diff --git a/libopie2/qt3/opieui/ocompletionbox.cpp b/libopie2/qt3/opieui/ocompletionbox.cpp new file mode 100644 index 0000000..b594b8e --- a/dev/null +++ b/libopie2/qt3/opieui/ocompletionbox.cpp @@ -0,0 +1,408 @@ +/* + This file Copyright (C) 2003 Michael 'Mickey' Lauer <mickey@tm.informatik.uni-frankfurt.de> + is part of the Copyright (C) 2000,2001 Carsten Pfeiffer <pfeiffer@kde.org> + Opie Project Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + Copyright (C) 2000,2001 Dawit Alemayehu <adawit@kde.org> + =. + .=l. Originally part of the KDE Project + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#include <qapplication.h> +#include <qevent.h> +#include <qstyle.h> + +#include <opie2/ocompletionbox.h> + +#define OListBox QListBox + +class OCompletionBox::OCompletionBoxPrivate +{ +public: + QWidget *m_parent; // necessary to set the focus back + QString cancelText; + bool tabHandling; + bool down_workaround; +}; + +OCompletionBox::OCompletionBox( QWidget *parent, const char *name ) + :OListBox( parent, name, WType_Popup ) +{ + d = new OCompletionBoxPrivate; + d->m_parent = parent; + d->tabHandling = true; + d->down_workaround = false; + + setColumnMode( 1 ); + setLineWidth( 1 ); + setFrameStyle( QFrame::Box | QFrame::Plain ); + + if ( parent ) + setFocusProxy( parent ); + else + setFocusPolicy( NoFocus ); + + setVScrollBarMode( Auto ); + setHScrollBarMode( AlwaysOff ); + + connect( this, SIGNAL( doubleClicked( QListBoxItem * )), + SLOT( slotActivated( QListBoxItem * )) ); + + // grmbl, just QListBox workarounds :[ Thanks Volker. + connect( this, SIGNAL( currentChanged( QListBoxItem * )), + SLOT( slotCurrentChanged() )); + connect( this, SIGNAL( clicked( QListBoxItem * )), + SLOT( slotItemClicked( QListBoxItem * )) ); +} + +OCompletionBox::~OCompletionBox() +{ + d->m_parent = 0L; + delete d; +} + +QStringList OCompletionBox::items() const +{ + QStringList list; + for ( uint i = 0; i < count(); i++ ) { + list.append( text( i ) ); + } + return list; +} + +void OCompletionBox::slotActivated( QListBoxItem *item ) +{ + if ( !item ) + return; + + hide(); + emit activated( item->text() ); +} + +bool OCompletionBox::eventFilter( QObject *o, QEvent *e ) +{ + int type = e->type(); + + if ( o == d->m_parent ) { + if ( isVisible() ) { + if ( type == QEvent::KeyPress ) { + QKeyEvent *ev = static_cast<QKeyEvent *>( e ); + switch ( ev->key() ) { + case Key_BackTab: + if ( d->tabHandling ) { + up(); + ev->accept(); + return true; + } + break; + case Key_Tab: + if ( d->tabHandling ) { + down(); // Only on TAB!! + ev->accept(); + return true; + } + break; + case Key_Down: + down(); + ev->accept(); + return true; + case Key_Up: + up(); + ev->accept(); + return true; + case Key_Prior: + pageUp(); + ev->accept(); + return true; + case Key_Next: + pageDown(); + ev->accept(); + return true; + case Key_Escape: + cancelled(); + ev->accept(); + return true; + case Key_Enter: + case Key_Return: + if ( ev->state() & ShiftButton ) { + hide(); + ev->accept(); // Consume the Enter event + return true; + } + break; + default: + break; + } + } + else if ( type == QEvent::AccelOverride ) { + // Override any acceleartors that match + // the key sequences we use here... + QKeyEvent *ev = static_cast<QKeyEvent *>( e ); + switch ( ev->key() ) { + case Key_Tab: + case Key_BackTab: + case Key_Down: + case Key_Up: + case Key_Prior: + case Key_Next: + case Key_Escape: + case Key_Enter: + case Key_Return: + ev->accept(); + return true; + break; + default: + break; + } + } + + // parent loses focus or gets a click -> we hide + else if ( type == QEvent::FocusOut || type == QEvent::Resize || + type == QEvent::Close || type == QEvent::Hide || + type == QEvent::Move ) { + hide(); + } + else if ( type == QEvent::Move ) + move( d->m_parent->mapToGlobal(QPoint(0, d->m_parent->height()))); + else if ( type == QEvent::Resize ) + resize( sizeHint() ); + } + } + + // any mouse-click on something else than "this" makes us hide + else if ( type == QEvent::MouseButtonPress ) { + QMouseEvent *ev = static_cast<QMouseEvent *>( e ); + if ( !rect().contains( ev->pos() )) // this widget + hide(); + } + + return OListBox::eventFilter( o, e ); +} + + +void OCompletionBox::popup() +{ + if ( count() == 0 ) + hide(); + else { + ensureCurrentVisible(); + bool block = signalsBlocked(); + blockSignals( true ); + setCurrentItem( 0 ); + blockSignals( block ); + clearSelection(); + if ( !isVisible() ) + show(); + else if ( size().height() < sizeHint().height() ) + resize( sizeHint() ); + } +} + +void OCompletionBox::show() +{ + resize( sizeHint() ); + + if ( d->m_parent ) + { + //QDesktopWidget *screen = QApplication::desktop(); + QWidget *screen = QApplication::desktop(); + + QPoint orig = d->m_parent->mapToGlobal( QPoint(0, d->m_parent->height()) ); + int x = orig.x(); + int y = orig.y(); + + if ( x + width() > screen->width() ) + x = screen->width() - width(); + if (y + height() > screen->height() ) + y = y - height() - d->m_parent->height(); + + move( x, y); + qApp->installEventFilter( this ); + } + + // ### we shouldn't need to call this, but without this, the scrollbars + // are pretty b0rked. + //triggerUpdate( true ); + + OListBox::show(); +} + +void OCompletionBox::hide() +{ + if ( d->m_parent ) + qApp->removeEventFilter( this ); + d->cancelText = QString::null; + OListBox::hide(); +} + +QSize OCompletionBox::sizeHint() const +{ + int ih = itemHeight(); + int h = QMIN( 15 * ih, (int) count() * ih ) +1; + h = QMAX( h, OListBox::minimumSizeHint().height() ); + + int w = (d->m_parent) ? d->m_parent->width() : OListBox::minimumSizeHint().width(); + w = QMAX( OListBox::minimumSizeHint().width(), w ); + return QSize( w, h ); +} + +void OCompletionBox::down() +{ + int i = currentItem(); + + if ( i == 0 && d->down_workaround ) { + d->down_workaround = false; + setCurrentItem( 0 ); + setSelected( 0, true ); + emit highlighted( currentText() ); + } + + else if ( i < (int) count() - 1 ) + setCurrentItem( i + 1 ); +} + +void OCompletionBox::up() +{ + if ( currentItem() > 0 ) + setCurrentItem( currentItem() - 1 ); +} + +void OCompletionBox::pageDown() +{ + int i = currentItem() + numItemsVisible(); + i = i > (int)count() - 1 ? (int)count() - 1 : i; + setCurrentItem( i ); +} + +void OCompletionBox::pageUp() +{ + int i = currentItem() - numItemsVisible(); + i = i < 0 ? 0 : i; + setCurrentItem( i ); +} + +void OCompletionBox::home() +{ + setCurrentItem( 0 ); +} + +void OCompletionBox::end() +{ + setCurrentItem( count() -1 ); +} + +void OCompletionBox::setTabHandling( bool enable ) +{ + d->tabHandling = enable; +} + +bool OCompletionBox::isTabHandling() const +{ + return d->tabHandling; +} + +void OCompletionBox::setCancelledText( const QString& text ) +{ + d->cancelText = text; +} + +QString OCompletionBox::cancelledText() const +{ + return d->cancelText; +} + +void OCompletionBox::cancelled() +{ + if ( !d->cancelText.isNull() ) + emit userCancelled( d->cancelText ); + if ( isVisible() ) + hide(); +} + +class OCompletionBoxItem : public QListBoxItem +{ +public: + void reuse( const QString &text ) { setText( text ); } +}; + + +void OCompletionBox::insertItems( const QStringList& items, int index ) +{ + bool block = signalsBlocked(); + blockSignals( true ); + insertStringList( items, index ); + blockSignals( block ); + d->down_workaround = true; +} + +void OCompletionBox::setItems( const QStringList& items ) +{ + bool block = signalsBlocked(); + blockSignals( true ); + + QListBoxItem* item = firstItem(); + if ( !item ) { + insertStringList( items ); + } + else { + for ( QStringList::ConstIterator it = items.begin(); it != items.end(); it++) { + if ( item ) { + ((OCompletionBoxItem*)item)->reuse( *it ); + item = item->next(); + } + else { + insertItem( new QListBoxText( *it ) ); + } + } + QListBoxItem* tmp = item; + while ( (item = tmp ) ) { + tmp = item->next(); + delete item; + } + triggerUpdate( false ); + } + + blockSignals( block ); + d->down_workaround = true; +} + +void OCompletionBox::slotCurrentChanged() +{ + d->down_workaround = false; +} + +void OCompletionBox::slotItemClicked( QListBoxItem *item ) +{ + if ( item ) + { + if ( d->down_workaround ) { + d->down_workaround = false; + emit highlighted( item->text() ); + } + + hide(); + emit activated( item->text() ); + } +} diff --git a/libopie2/qt3/opieui/ocompletionbox.h b/libopie2/qt3/opieui/ocompletionbox.h new file mode 100644 index 0000000..54d9ef5 --- a/dev/null +++ b/libopie2/qt3/opieui/ocompletionbox.h @@ -0,0 +1,232 @@ +/* + This file Copyright (C) 2003 Michael 'Mickey' Lauer <mickey@tm.informatik.uni-frankfurt.de> + is part of the Copyright (C) 2000 Carsten Pfeiffer <pfeiffer@kde.org> + Opie Project Copyright (C) 2000 Stefan Schimanski <1Stein@gmx.de> + + =. Originally part of the KDE Project + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef OCOMPLETIONBOX_H +#define OCOMPLETIONBOX_H + +class QEvent; +#include <qstringlist.h> +#include <qlistbox.h> + +// ML: Until we don't have an own OListBox, we use the QListBox +#define OListBox QListBox + +/** + * A little utility class for "completion-widgets", like @ref OLineEdit or + * @ref OComboBox. OCompletionBox is a listbox, displayed as a rectangle without + * any window decoration, usually directly under the lineedit or combobox. + * It is filled with all possible matches for a completion, so the user + * can select the one he wants. + * + * It is used when OGlobalSettings::Completion == CompletionPopup or CompletionPopupAuto. + * + * @short A helper widget for "completion-widgets" (OLineEdit, OComboBox)) + * @short Adapted for the Opie project by Michael Lauer <mickey@tm.informatik.uni-frankfurt.de> + * @author Carsten Pfeiffer <pfeiffer@kde.org> + * + */ +class OCompletionBox : public OListBox +{ + Q_OBJECT + Q_PROPERTY( bool isTabHandling READ isTabHandling WRITE setTabHandling ) + Q_PROPERTY(QString cancelledText READ cancelledText WRITE setCancelledText) + +public: + /** + * Constructs a OCompletionBox. + * + * Notice: the parent needs to be always 0L, + * so you can't specify it in the constructor. Because of that, Qt's + * auto-deletion does not take place, so you have to explicitly delete + * this widget when you don't need it anymore. + * + * The parent widget is used to give the focus back when pressing the + * up-button on the very first item. + */ + OCompletionBox( QWidget *parent, const char *name = 0 ); + + /** + * Destroys the box + */ + ~OCompletionBox(); + + virtual QSize sizeHint() const; + +public slots: + /** + * Returns a list of all items currently in the box. + */ + QStringList items() const; + + /** + * Inserts @p items into the box. Does not clear the items before. + * @p index determines at which position @p items will be inserted. + * (defaults to appending them at the end) + */ + void insertItems( const QStringList& items, int index = -1 ); + + /** + * Clears the box and inserts @p items. + */ + void setItems( const QStringList& items ); + + /** + * Adjusts the size of the box to fit the width of the parent given in the + * constructor and pops it up at the most appropriate place, relative to + * the parent. + * + * Depending on the screensize and the position of the parent, this may + * be a different place, however the default is to pop it up and the + * lower left corner of the parent. + * + * Make sure to hide() the box when appropriate. + */ + virtual void popup(); + + /** + * Makes this widget (when visible) capture Tab-key events to traverse the + * items in the dropdown list. + * + * Default off, as it conflicts with the usual behavior of Tab to traverse + * widgets. It is useful for cases like Konqueror's Location Bar, though. + * + * @see #isTabHandling + */ + void setTabHandling( bool enable ); + + /** + * @returns true if this widget is handling Tab-key events to traverse the + * items in the dropdown list, otherwise false. + * + * Default is false. + * + * @see #setTabHandling + */ + bool isTabHandling() const; + + /** + * Sets the text to be emitted if the user chooses not to + * pick from the available matches. + * + * If the cancelled text is not set through this function, the + * @ref userCancelled signal will not be emitted. + * + * @see userCancelled( const QString& ) + * @param txt the text to be emitted if the user cancels this box + */ + void setCancelledText( const QString& ); + + /** + * @returns the text set via @ref setCancelledText() or QString::null. + */ + QString cancelledText() const; + + /** + * Moves the selection one line down or select the first item if nothing is selected yet. + */ + void down(); + + /** + * Moves the selection one line up or select the first item if nothing is selected yet. + */ + void up(); + + /** + * Moves the selection one page down. + */ + void pageDown(); + + /** + * Moves the selection one page up. + */ + void pageUp(); + + /** + * Moves the selection up to the first item. + */ + void home(); + + /** + * Moves the selection down to the last item. + */ + void end(); + + /** + * Re-implemented for internal reasons. API is unaffected. + */ + virtual void show(); + + /** + * Re-implemented for internal reasons. API is unaffected. + */ + virtual void hide(); + +signals: + /** + * Emitted when an item was selected, contains the text of + * the selected item. + */ + void activated( const QString& ); + + /** + * Emitted whenever the user chooses to ignore the available + * selections and close the this box. + */ + void userCancelled( const QString& ); + +protected: + /** + * Reimplemented from OListBox to get events from the viewport (to hide + * this widget on mouse-click, Escape-presses, etc. + */ + virtual bool eventFilter( QObject *, QEvent * ); + +protected slots: + /** + * Called when an item was activated. Emits + * @ref activated() with the item. + */ + virtual void slotActivated( QListBoxItem * ); + +private slots: + void slotSetCurrentItem( QListBoxItem *i ) { setCurrentItem( i ); } // grrr + void slotCurrentChanged(); + void cancelled(); + void slotItemClicked( QListBoxItem * ); + +private: + class OCompletionBoxPrivate; + OCompletionBoxPrivate* d; +}; + + +#endif // OCOMPLETIONBOX_H diff --git a/libopie2/qt3/opieui/oeditlistbox.cpp b/libopie2/qt3/opieui/oeditlistbox.cpp new file mode 100644 index 0000000..3c53552 --- a/dev/null +++ b/libopie2/qt3/opieui/oeditlistbox.cpp @@ -0,0 +1,416 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <faure@kde.org>, Alexander Neundorf <neundorf@kde.org> + 2000, 2002 Carsten Pfeiffer <pfeiffer@kde.org> + + This library 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 library 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/* QT */ + +#include <qstringlist.h> +#include <qpushbutton.h> +#include <qlayout.h> +#include <qgroupbox.h> +#include <qlistbox.h> +#include <qwhatsthis.h> +#include <qlabel.h> + +/* OPIE */ + +#include <opie2/ocombobox.h> +#include <opie2/odialog.h> +#include <opie2/olineedit.h> +#include <opie2/oeditlistbox.h> + +/* UNIX */ + +#include <assert.h> + +/*====================================================================================== + * OEditListBoxPrivate + *======================================================================================*/ + +class OEditListBoxPrivate +{ +public: + bool m_checkAtEntering; + int buttons; +}; + +/*====================================================================================== + * OEditListBox + *======================================================================================*/ + +OEditListBox::OEditListBox(QWidget *parent, const char *name, + bool checkAtEntering, int buttons ) + :QGroupBox(parent, name ) +{ + init( checkAtEntering, buttons ); +} + +OEditListBox::OEditListBox(const QString& title, QWidget *parent, + const char *name, bool checkAtEntering, int buttons) + :QGroupBox(title, parent, name ) +{ + init( checkAtEntering, buttons ); +} + +OEditListBox::OEditListBox(const QString& title, const CustomEditor& custom, + QWidget *parent, const char *name, + bool checkAtEntering, int buttons) + :QGroupBox(title, parent, name ) +{ + m_lineEdit = custom.lineEdit(); + init( checkAtEntering, buttons, custom.representationWidget() ); +} + +OEditListBox::~OEditListBox() +{ + delete d; + d=0; +} + +void OEditListBox::init( bool checkAtEntering, int buttons, + QWidget *representationWidget ) +{ + d=new OEditListBoxPrivate; + d->m_checkAtEntering=checkAtEntering; + d->buttons = buttons; + + int lostButtons = 0; + if ( (buttons & Add) == 0 ) + lostButtons++; + if ( (buttons & Remove) == 0 ) + lostButtons++; + if ( (buttons & UpDown) == 0 ) + lostButtons += 2; + + + servNewButton = servRemoveButton = servUpButton = servDownButton = 0L; + setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding)); + + QWidget * gb = this; + QGridLayout * grid = new QGridLayout(gb, 7 - lostButtons, 2, + ODialog::marginHint(), + ODialog::spacingHint()); + grid->addRowSpacing(0, fontMetrics().lineSpacing()); + for ( int i = 1; i < 7 - lostButtons; i++ ) + grid->setRowStretch(i, 1); + + grid->setMargin(15); + + if ( representationWidget ) + representationWidget->reparent( gb, QPoint(0,0) ); + else + m_lineEdit=new OLineEdit(gb); + + m_listBox = new QListBox(gb); + + QWidget *editingWidget = representationWidget ? + representationWidget : m_lineEdit; + grid->addMultiCellWidget(editingWidget,1,1,0,1); + grid->addMultiCellWidget(m_listBox, 2, 6 - lostButtons, 0, 0); + int row = 2; + if ( buttons & Add ) { + servNewButton = new QPushButton(tr("&Add"), gb); + servNewButton->setEnabled(false); + connect(servNewButton, SIGNAL(clicked()), SLOT(addItem())); + + grid->addWidget(servNewButton, row++, 1); + } + + if ( buttons & Remove ) { + servRemoveButton = new QPushButton(tr("&Remove"), gb); + servRemoveButton->setEnabled(false); + connect(servRemoveButton, SIGNAL(clicked()), SLOT(removeItem())); + + grid->addWidget(servRemoveButton, row++, 1); + } + + if ( buttons & UpDown ) { + servUpButton = new QPushButton(tr("Move &Up"), gb); + servUpButton->setEnabled(false); + connect(servUpButton, SIGNAL(clicked()), SLOT(moveItemUp())); + + servDownButton = new QPushButton(tr("Move &Down"), gb); + servDownButton->setEnabled(false); + connect(servDownButton, SIGNAL(clicked()), SLOT(moveItemDown())); + + grid->addWidget(servUpButton, row++, 1); + grid->addWidget(servDownButton, row++, 1); + } + + connect(m_lineEdit,SIGNAL(textChanged(const QString&)),this,SLOT(typedSomething(const QString&))); + m_lineEdit->setTrapReturnKey(true); + connect(m_lineEdit,SIGNAL(returnPressed()),this,SLOT(addItem())); + connect(m_listBox, SIGNAL(highlighted(int)), SLOT(enableMoveButtons(int))); + + // maybe supplied lineedit has some text already + typedSomething( m_lineEdit->text() ); +} + +void OEditListBox::typedSomething(const QString& text) +{ + if(currentItem() >= 0) { + if(currentText() != m_lineEdit->text()) + { + // IMHO changeItem() shouldn't do anything with the value + // of currentItem() ... like changing it or emitting signals ... + // but TT disagree with me on this one (it's been that way since ages ... grrr) + bool block = m_listBox->signalsBlocked(); + m_listBox->blockSignals( true ); + m_listBox->changeItem(text, currentItem()); + m_listBox->blockSignals( block ); + emit changed(); + } + } + + if ( !servNewButton ) + return; + + if (!d->m_checkAtEntering) + servNewButton->setEnabled(!text.isEmpty()); + else + { + if (text.isEmpty()) + { + servNewButton->setEnabled(false); + } + else + { + #if QT_VERSION > 290 + StringComparisonMode mode = (StringComparisonMode) (ExactMatch | CaseSensitive ); + bool enable = (m_listBox->findItem( text, mode ) == 0L); + #else + bool enable = (m_listBox->findItem( text ) == 0L); + #endif + servNewButton->setEnabled( enable ); + } + } +} + +void OEditListBox::moveItemUp() +{ + if (!m_listBox->isEnabled()) + { + //ONotifyClient::beep(); + return; + } + + unsigned int selIndex = m_listBox->currentItem(); + if (selIndex == 0) + { + //ONotifyClient::beep(); + return; + } + + QListBoxItem *selItem = m_listBox->item(selIndex); + m_listBox->takeItem(selItem); + m_listBox->insertItem(selItem, selIndex-1); + m_listBox->setCurrentItem(selIndex - 1); + + emit changed(); +} + +void OEditListBox::moveItemDown() +{ + if (!m_listBox->isEnabled()) + { + //ONotifyClient::beep(); + return; + } + + unsigned int selIndex = m_listBox->currentItem(); + if (selIndex == m_listBox->count() - 1) + { + //ONotifyClient::beep(); + return; + } + + QListBoxItem *selItem = m_listBox->item(selIndex); + m_listBox->takeItem(selItem); + m_listBox->insertItem(selItem, selIndex+1); + m_listBox->setCurrentItem(selIndex + 1); + + emit changed(); +} + +void OEditListBox::addItem() +{ + // when m_checkAtEntering is true, the add-button is disabled, but this + // slot can still be called through Key_Return/Key_Enter. So we guard + // against this. + if ( !servNewButton || !servNewButton->isEnabled() ) + return; + + const QString& currentTextLE=m_lineEdit->text(); + bool alreadyInList(false); + //if we didn't check for dupes at the inserting we have to do it now + if (!d->m_checkAtEntering) + { + // first check current item instead of dumb iterating the entire list + if ( m_listBox->currentText() == currentTextLE ) + alreadyInList = true; + else + { + #if QT_VERSION > 290 + StringComparisonMode mode = (StringComparisonMode) (ExactMatch | CaseSensitive ); + alreadyInList =(m_listBox->findItem(currentTextLE, mode) != 0); + #else + alreadyInList =(m_listBox->findItem(currentTextLE) != 0); + #endif + } + } + + if ( servNewButton ) + servNewButton->setEnabled(false); + + bool block = m_lineEdit->signalsBlocked(); + m_lineEdit->blockSignals(true); + m_lineEdit->clear(); + m_lineEdit->blockSignals(block); + + m_listBox->setSelected(currentItem(), false); + + if (!alreadyInList) + { + block = m_listBox->signalsBlocked(); + m_listBox->blockSignals( true ); + m_listBox->insertItem(currentTextLE); + m_listBox->blockSignals( block ); + emit changed(); + emit added( currentTextLE ); + } +} + +int OEditListBox::currentItem() const +{ + int nr = m_listBox->currentItem(); + #if QT_VERSION > 290 + if(nr >= 0 && !m_listBox->item(nr)->isSelected()) return -1; + #else + if(nr >= 0 && !m_listBox->isSelected(m_listBox->item(nr))) return -1; + #endif + return nr; +} + +void OEditListBox::removeItem() +{ + int selected = m_listBox->currentItem(); + + if ( selected >= 0 ) + { + QString removedText = m_listBox->currentText(); + + m_listBox->removeItem( selected ); + if ( count() > 0 ) + m_listBox->setSelected( QMIN( selected, count() - 1 ), true ); + + emit changed(); + emit removed( removedText ); + } + + if ( servRemoveButton && m_listBox->currentItem() == -1 ) + servRemoveButton->setEnabled(false); +} + +void OEditListBox::enableMoveButtons(int index) +{ + // Update the lineEdit when we select a different line. + if(currentText() != m_lineEdit->text()) + m_lineEdit->setText(currentText()); + + bool moveEnabled = servUpButton && servDownButton; + + if (moveEnabled ) + { + if (m_listBox->count() <= 1) + { + servUpButton->setEnabled(false); + servDownButton->setEnabled(false); + } + else if ((uint) index == (m_listBox->count() - 1)) + { + servUpButton->setEnabled(true); + servDownButton->setEnabled(false); + } + else if (index == 0) + { + servUpButton->setEnabled(false); + servDownButton->setEnabled(true); + } + else + { + servUpButton->setEnabled(true); + servDownButton->setEnabled(true); + } + } + + if ( servRemoveButton ) + servRemoveButton->setEnabled(true); +} + +void OEditListBox::clear() +{ + m_lineEdit->clear(); + m_listBox->clear(); + emit changed(); +} + +void OEditListBox::insertStringList(const QStringList& list, int index) +{ + m_listBox->insertStringList(list,index); +} + +void OEditListBox::insertStrList(const QStrList* list, int index) +{ + m_listBox->insertStrList(list,index); +} + +void OEditListBox::insertStrList(const QStrList& list, int index) +{ + m_listBox->insertStrList(list,index); +} + +void OEditListBox::insertStrList(const char ** list, int numStrings, int index) +{ + m_listBox->insertStrList(list,numStrings,index); +} + +QStringList OEditListBox::items() const +{ + QStringList list; + for ( uint i = 0; i < m_listBox->count(); i++ ) + list.append( m_listBox->text( i )); + + return list; +} + +void OEditListBox::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + + +/*====================================================================================== + * CustomEditor + *======================================================================================*/ + +OEditListBox::CustomEditor::CustomEditor( OComboBox *combo ) +{ + m_representationWidget = combo; + m_lineEdit = dynamic_cast<OLineEdit*>( combo->lineEdit() ); + assert( m_lineEdit ); +} diff --git a/libopie2/qt3/opieui/oeditlistbox.h b/libopie2/qt3/opieui/oeditlistbox.h new file mode 100644 index 0000000..63fab11 --- a/dev/null +++ b/libopie2/qt3/opieui/oeditlistbox.h @@ -0,0 +1,250 @@ +/* This file is part of the KDE libraries + Copyright (C) 2000 David Faure <faure@kde.org>, Alexander Neundorf <neundorf@kde.org> + + This library 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 library 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#ifndef OEDITLISTBOX_H +#define OEDITLISTBOX_H + +#include <qgroupbox.h> +#include <qlistbox.h> + +class OLineEdit; +class OComboBox; +class QPushButton; + +#if QT_VERSION < 300 + enum StringComparisonMode { + CaseSensitive = 0x00001, // 0 0001 + BeginsWith = 0x00002, // 0 0010 + EndsWith = 0x00004, // 0 0100 + Contains = 0x00008, // 0 1000 + ExactMatch = 0x00010 // 1 0000 + }; +#endif + +class OEditListBoxPrivate; +/** + * An editable listbox + * + * This class provides a editable listbox ;-), this means + * a listbox which is accompanied by a line edit to enter new + * items into the listbox and pushbuttons to add and remove + * items from the listbox and two buttons to move items up and down. + */ +class OEditListBox : public QGroupBox +{ + Q_OBJECT + +public: + /// @since 3.1 + class CustomEditor + { + public: + CustomEditor() + : m_representationWidget( 0L ), + m_lineEdit( 0L ) {} + CustomEditor( QWidget *repWidget, OLineEdit *edit ) + : m_representationWidget( repWidget ), + m_lineEdit( edit ) {} + CustomEditor( OComboBox *combo ); + + void setRepresentationWidget( QWidget *repWidget ) { + m_representationWidget = repWidget; + } + void setLineEdit( OLineEdit *edit ) { + m_lineEdit = edit; + } + + virtual QWidget *representationWidget() const { + return m_representationWidget; + } + virtual OLineEdit *lineEdit() const { + return m_lineEdit; + } + + protected: + QWidget *m_representationWidget; + OLineEdit *m_lineEdit; + }; + + public: + + /** + * Enumeration of the buttons, the listbox offers. Specify them in the + * constructor in the buttons parameter. + */ + enum Button { Add = 1, Remove = 2, UpDown = 4, All = Add|Remove|UpDown }; + + /** + * Create an editable listbox. + * + * If @p checkAtEntering is true, after every character you type + * in the line edit OEditListBox will enable or disable + * the Add-button, depending whether the current content of the + * line edit is already in the listbox. Maybe this can become a + * performance hit with large lists on slow machines. + * If @p checkAtEntering is false, + * it will be checked if you press the Add-button. It is not + * possible to enter items twice into the listbox. + */ + OEditListBox(QWidget *parent = 0, const char *name = 0, + bool checkAtEntering=false, int buttons = All ); + /** + * Create an editable listbox. + * + * The same as the other constructor, additionally it takes + * @title, which will be the title of the frame around the listbox. + */ + OEditListBox(const QString& title, QWidget *parent = 0, + const char *name = 0, bool checkAtEntering=false, + int buttons = All ); + + /** + * Another constructor, which allows to use a custom editing widget + * instead of the standard OLineEdit widget. E.g. you can use a + * @ref OURLRequester or a @ref OComboBox as input widget. The custom + * editor must consist of a lineedit and optionally another widget that + * is used as representation. A OComboBox or a OURLRequester have a + * OLineEdit as child-widget for example, so the OComboBox is used as + * the representation widget. + * + * @see OURLRequester::customEditor() + * @since 3.1 + */ + OEditListBox( const QString& title, + const CustomEditor &customEditor, + QWidget *parent = 0, const char *name = 0, + bool checkAtEntering = false, int buttons = All ); + + virtual ~OEditListBox(); + + /** + * Return a pointer to the embedded QListBox. + */ + QListBox* listBox() const { return m_listBox; } + /** + * Return a pointer to the embedded QLineEdit. + */ + OLineEdit* lineEdit() const { return m_lineEdit; } + /** + * Return a pointer to the Add button + */ + QPushButton* addButton() const { return servNewButton; } + /** + * Return a pointer to the Remove button + */ + QPushButton* removeButton() const { return servRemoveButton; } + /** + * Return a pointer to the Up button + */ + QPushButton* upButton() const { return servUpButton; } + /** + * Return a pointer to the Down button + */ + QPushButton* downButton() const { return servDownButton; } + + /** + * See @ref QListBox::count() + */ + int count() const { return int(m_listBox->count()); } + /** + * See @ref QListBox::insertStringList() + */ + void insertStringList(const QStringList& list, int index=-1); + /** + * See @ref QListBox::insertStringList() + */ + void insertStrList(const QStrList* list, int index=-1); + /** + * See @ref QListBox::insertStrList() + */ + void insertStrList(const QStrList& list, int index=-1); + /** + * See @ref QListBox::insertStrList() + */ + void insertStrList(const char ** list, int numStrings=-1, int index=-1); + /** + * See @ref QListBox::insertItem() + */ + void insertItem(const QString& text, int index=-1) {m_listBox->insertItem(text,index);} + /** + * Clears both the listbox and the line edit. + */ + void clear(); + /** + * See @ref QListBox::text() + */ + QString text(int index) const { return m_listBox->text(index); } + /** + * See @ref QListBox::currentItem() + */ + int currentItem() const; + /** + * See @ref QListBox::currentText() + */ + QString currentText() const { return m_listBox->currentText(); } + + /** + * @returns a stringlist of all items in the listbox + */ + QStringList items() const; + + signals: + void changed(); + + /** + * This signal is emitted when the user adds a new string to the list, + * the parameter is the added string. + * @since 3.2 + */ + void added( const QString & text ); + + /** + * This signal is emitted when the user removes a string from the list, + * the parameter is the removed string. + * @since 3.2 + */ + void removed( const QString & text ); + + protected slots: + //the names should be self-explaining + void moveItemUp(); + void moveItemDown(); + void addItem(); + void removeItem(); + void enableMoveButtons(int index); + void typedSomething(const QString& text); + + private: + QListBox *m_listBox; + QPushButton *servUpButton, *servDownButton; + QPushButton *servNewButton, *servRemoveButton; + OLineEdit *m_lineEdit; + + //this is called in both ctors, to avoid code duplication + void init( bool checkAtEntering, int buttons, + QWidget *representationWidget = 0L ); + + protected: + virtual void virtual_hook( int id, void* data ); + private: + //our lovely private d-pointer + OEditListBoxPrivate *d; +}; + +#endif // OEDITLISTBOX diff --git a/libopie2/qt3/opieui/ojanuswidget.cpp b/libopie2/qt3/opieui/ojanuswidget.cpp new file mode 100644 index 0000000..0a037ff --- a/dev/null +++ b/libopie2/qt3/opieui/ojanuswidget.cpp @@ -0,0 +1,1116 @@ +/* + This file is part of the Opie Project + + Originally part of the KDE project + (C) 1999-2000 Espen Sand (espensa@online.no) + =. + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +/* QT */ + +#include <qbitmap.h> +#include <qgrid.h> +#include <qhbox.h> +#include <qheader.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qobjectlist.h> +#include <qpixmap.h> +#include <qlistview.h> +#include <qsplitter.h> +#include <qtabwidget.h> +#include <qvbox.h> +#include <qwidgetstack.h> +#include <qpainter.h> +#include <qtimer.h> +#include <qstyle.h> + +/* OPIE */ + +#include <opie2/odialog.h> +#include <opie2/oseparator.h> +#include <opie2/ojanuswidget.h> + +/*====================================================================================== + * IconListItem + *======================================================================================*/ + +class OJanusWidget::IconListItem : public QListBoxItem +{ + public: + IconListItem( QListBox *listbox, const QPixmap &pixmap, + const QString &text ); + virtual int height( const QListBox *lb ) const; + virtual int width( const QListBox *lb ) const; + int expandMinimumWidth( int width ); + + protected: + const QPixmap &defaultPixmap(); + void paint( QPainter *painter ); + + private: + QPixmap mPixmap; + int mMinimumWidth; +}; + +template class QPtrList<QListViewItem>; + +/*====================================================================================== + * OJanusWidget + *======================================================================================*/ + +OJanusWidget::OJanusWidget( QWidget *parent, const char *name, int face ) + : QWidget( parent, name, 0 ), + mValid(false), mPageList(0), + mTitleList(0), mFace(face), mTitleLabel(0), mActivePageWidget(0), + mShowIconsInTreeList(false), d(0) +{ + QVBoxLayout *topLayout = new QVBoxLayout( this ); + + if( mFace == TreeList || mFace == IconList ) + { + mPageList = new QPtrList<QWidget>; + mTitleList = new QStringList(); + + QFrame *page; + if( mFace == TreeList ) + { + QSplitter *splitter = new QSplitter( this ); + topLayout->addWidget( splitter, 10 ); + mTreeListResizeMode = QSplitter::KeepSize; + + mTreeList = new QListView( splitter ); + mTreeList->addColumn( QString::fromLatin1("") ); + mTreeList->header()->hide(); + mTreeList->setRootIsDecorated(true); + mTreeList->setSorting( -1 ); + connect( mTreeList, SIGNAL(selectionChanged()), SLOT(slotShowPage()) ); + connect( mTreeList, SIGNAL(clicked(QListViewItem *)), SLOT(slotItemClicked(QListViewItem *))); + + // + // Page area. Title at top with a separator below and a pagestack using + // all available space at bottom. + // + QFrame *p = new QFrame( splitter ); + + QHBoxLayout *hbox = new QHBoxLayout( p, 0, 0 ); + hbox->addSpacing( ODialog::spacingHint() ); + + page = new QFrame( p ); + hbox->addWidget( page, 10 ); + } + else + { + QHBoxLayout *hbox = new QHBoxLayout( topLayout ); + mIconList = new IconListBox( this ); + + QFont listFont( mIconList->font() ); + listFont.setBold( true ); + mIconList->setFont( listFont ); + + mIconList->verticalScrollBar()->installEventFilter( this ); + hbox->addWidget( mIconList ); + connect( mIconList, SIGNAL(selectionChanged()), SLOT(slotShowPage())); + hbox->addSpacing( ODialog::spacingHint() ); + page = new QFrame( this ); + hbox->addWidget( page, 10 ); + } + + // + // Rest of page area. Title at top with a separator below and a + // pagestack using all available space at bottom. + // + + QVBoxLayout *vbox = new QVBoxLayout( page, 0, ODialog::spacingHint() ); + + mTitleLabel = new QLabel( QString::fromLatin1("Empty page"), page, "OJanusWidgetTitleLabel" ); + vbox->addWidget( mTitleLabel ); + + QFont titleFont( mTitleLabel->font() ); + titleFont.setBold( true ); + mTitleLabel->setFont( titleFont ); + + mTitleSep = new OSeparator( page ); + mTitleSep->setFrameStyle( QFrame::HLine|QFrame::Plain ); + vbox->addWidget( mTitleSep ); + + mPageStack = new QWidgetStack( page ); + connect(mPageStack, SIGNAL(aboutToShow(QWidget *)), + this, SIGNAL(aboutToShowPage(QWidget *))); + vbox->addWidget( mPageStack, 10 ); + } + else if( mFace == Tabbed ) + { + mPageList = new QPtrList<QWidget>; + + mTabControl = new QTabWidget( this ); + mTabControl->setMargin (ODialog::marginHint()); + topLayout->addWidget( mTabControl, 10 ); + } + else if( mFace == Swallow ) + { + mSwallowPage = new QWidget( this ); + topLayout->addWidget( mSwallowPage, 10 ); + } + else + { + mFace = Plain; + mPlainPage = new QFrame( this ); + topLayout->addWidget( mPlainPage, 10 ); + } + + /* FIXME: Revise for Opie + if ( kapp ) + connect(kapp,SIGNAL(kdisplayFontChanged()),SLOT(slotFontChanged())); + */ + + mValid = true; + + setSwallowedWidget(0); // Set default size if 'mFace' is Swallow. +} + + +OJanusWidget::~OJanusWidget() +{ + delete mPageList; + mPageList = 0; + delete mTitleList; + mTitleList = 0; +} + + +bool OJanusWidget::isValid() const +{ + return( mValid ); +} + + +QFrame *OJanusWidget::plainPage() +{ + return( mPlainPage ); +} + + +int OJanusWidget::face() const +{ + return( mFace ); +} + +QWidget *OJanusWidget::FindParent() +{ + if( mFace == Tabbed ) { + return mTabControl; + } + else { + return this; + } +} + +QFrame *OJanusWidget::addPage( const QStringList &items, const QString &header, + const QPixmap &pixmap ) +{ + if( mValid == false ) + { + qDebug( "addPage: Invalid object" ); + return( 0 ); + } + + QFrame *page = new QFrame( FindParent(), "page" ); + addPageWidget( page, items, header, pixmap ); + + return page; +} + +void OJanusWidget::pageGone( QObject *obj ) +{ + removePage( static_cast<QWidget*>( obj ) ); +} + +void OJanusWidget::slotReopen( QListViewItem * item ) +{ + if( item ) + item->setOpen( true ); +} + +QFrame *OJanusWidget::addPage( const QString &itemName, const QString &header, + const QPixmap &pixmap ) +{ + QStringList items; + items << itemName; + return addPage(items, header, pixmap); +} + + + +QVBox *OJanusWidget::addVBoxPage( const QStringList &items, + const QString &header, + const QPixmap &pixmap ) +{ + if( mValid == false ) + { + qDebug( "addPage: Invalid object" ); + return( 0 ); + } + + QVBox *page = new QVBox(FindParent() , "page" ); + page->setSpacing( ODialog::spacingHint() ); + addPageWidget( page, items, header, pixmap ); + + return page; +} + +QVBox *OJanusWidget::addVBoxPage( const QString &itemName, + const QString &header, + const QPixmap &pixmap ) +{ + QStringList items; + items << itemName; + return addVBoxPage(items, header, pixmap); +} + +QHBox *OJanusWidget::addHBoxPage( const QStringList &items, + const QString &header, + const QPixmap &pixmap ) +{ + if( mValid == false ) { + qDebug( "addPage: Invalid object" ); + return( 0 ); + } + + QHBox *page = new QHBox(FindParent(), "page"); + page->setSpacing( ODialog::spacingHint() ); + addPageWidget( page, items, header, pixmap ); + + return page; +} + +QHBox *OJanusWidget::addHBoxPage( const QString &itemName, + const QString &header, + const QPixmap &pixmap ) +{ + QStringList items; + items << itemName; + return addHBoxPage(items, header, pixmap); +} + +QGrid *OJanusWidget::addGridPage( int n, Orientation dir, + const QStringList &items, + const QString &header, + const QPixmap &pixmap ) +{ + if( mValid == false ) + { + qDebug( "addPage: Invalid object" ); + return( 0 ); + } + + QGrid *page = new QGrid( n, dir, FindParent(), "page" ); + page->setSpacing( ODialog::spacingHint() ); + addPageWidget( page, items, header, pixmap ); + + return page; +} + + +QGrid *OJanusWidget::addGridPage( int n, Orientation dir, + const QString &itemName, + const QString &header, + const QPixmap &pixmap ) +{ + QStringList items; + items << itemName; + return addGridPage(n, dir, items, header, pixmap); +} + +void OJanusWidget::InsertTreeListItem(const QStringList &items, const QPixmap &pixmap, QFrame *page) +{ + bool isTop = true; + QListViewItem *curTop = 0, *child, *last, *newChild; + unsigned int index = 1; + QStringList curPath; + + for ( QStringList::ConstIterator it = items.begin(); it != items.end(); ++it, index++ ) { + QString name = (*it); + bool isPath = ( index != items.count() ); + + // Find the first child. + if (isTop) { + child = mTreeList->firstChild(); + } + else { + child = curTop->firstChild(); + } + + // Now search for a child with the current Name, and if it we doesn't + // find it, then remember the location of the last child. + for (last = 0; child && child->text(0) != name ; last = child, child = child->nextSibling()); + + if (last == 0 && child == 0) { + // This node didn't have any children at all, lets just insert the + // new child. + if (isTop) + newChild = new QListViewItem(mTreeList, name); + else + newChild = new QListViewItem(curTop, name); + + } + else if (child != 0) { + // we found the given name in this child. + if (!isPath) { + qDebug( "The element inserted was already in the TreeList box!" ); + return; + } + else { + // Ok we found the folder + newChild = child; + } + } + else { + // the node had some children, but we didn't find the given name + if (isTop) + newChild = new QListViewItem(mTreeList, last, name); + else + newChild = new QListViewItem(curTop, last, name); + } + + // Now make the element expandable if it is a path component, and make + // ready for next loop + if (isPath) { + newChild->setExpandable(true); + curTop = newChild; + isTop = false; + curPath << name; + + QString key = curPath.join("_/_"); + if (mFolderIconMap.contains(key)) { + QPixmap p = mFolderIconMap[key]; + newChild->setPixmap(0,p); + } + } + else { + if (mShowIconsInTreeList) { + newChild->setPixmap(0, pixmap); + } + mTreeListToPageStack.insert(newChild, page); + } + } +} + +void OJanusWidget::addPageWidget( QFrame *page, const QStringList &items, + const QString &header,const QPixmap &pixmap ) +{ + connect(page, SIGNAL(destroyed(QObject*)), SLOT(pageGone(QObject*))); + + if( mFace == Tabbed ) + { + mTabControl->addTab (page, items.last()); + mPageList->append (page); + } + else if( mFace == TreeList || mFace == IconList ) + { + mPageList->append( page ); + mPageStack->addWidget( page, 0 ); + + if (items.count() == 0) { + qDebug( "Invalid QStringList, with zero items" ); + return; + } + + if( mFace == TreeList ) + { + InsertTreeListItem(items, pixmap, page); + } + else // mFace == IconList + { + QString itemName = items.last(); + IconListItem *item = new IconListItem( mIconList, pixmap, itemName ); + // + // 2000-06-01 Espen Sand: If I do this with Qt 2.1.1 all sorts of + // strange things happen. With Qt <= 2.1 it worked but now I must + // either specify the listbox in the constructor on the item + // or as below, not both. + // mIconList->insertItem( item ); + // + mIconListToPageStack.insert(item, page); + mIconList->invalidateHeight(); + mIconList->invalidateWidth(); + + if (mIconList->isVisible()) + mIconList->updateWidth(); + } + + // + // Make sure the title label is sufficiently wide + // + QString lastName = items.last(); + const QString &title = (header != QString::null ? header : lastName); + QRect r = mTitleLabel->fontMetrics().boundingRect( title ); + if( mTitleLabel->minimumWidth() < r.width() ) + { + mTitleLabel->setMinimumWidth( r.width() ); + } + mTitleList->append( title ); + + if( mTitleList->count() == 1 ) + { + showPage(0); + } + } + else + { + qDebug( "OJanusWidget::addPageWidget: can only add a page in Tabbed, TreeList or IconList modes" ); + } + +} + +void OJanusWidget::setFolderIcon(const QStringList &path, const QPixmap &pixmap) +{ + QString key = path.join("_/_"); + mFolderIconMap.insert(key,pixmap); +} + + + +bool OJanusWidget::setSwallowedWidget( QWidget *widget ) +{ + if( mFace != Swallow || mValid == false ) + { + return( false ); + } + + // + // Remove current layout and make a new. + // + if( mSwallowPage->layout() != 0 ) + { + delete mSwallowPage->layout(); + } + QGridLayout *gbox = new QGridLayout( mSwallowPage, 1, 1, 0 ); + + // + // Hide old children + // + QObjectList *l = (QObjectList*)mSwallowPage->children(); // silence please + for( uint i=0; i < l->count(); i++ ) + { + QObject *o = l->at(i); + if( o->isWidgetType() ) + { + ((QWidget*)o)->hide(); + } + } + + // + // Add new child or make default size + // + if( widget == 0 ) + { + gbox->addRowSpacing(0,100); + gbox->addColSpacing(0,100); + mSwallowPage->setMinimumSize(100,100); + } + else + { + if( widget->parent() != mSwallowPage ) + { + widget->reparent( mSwallowPage, 0, QPoint(0,0) ); + } + gbox->addWidget(widget, 0, 0 ); + gbox->activate(); + mSwallowPage->setMinimumSize( widget->minimumSize() ); + } + + return( true ); +} + +bool OJanusWidget::slotShowPage() +{ + if( mValid == false ) + { + return( false ); + } + + if( mFace == TreeList ) + { + QListViewItem *node = mTreeList->selectedItem(); + if( node == 0 ) { return( false ); } + + QWidget *stackItem = mTreeListToPageStack[node]; + return showPage(stackItem); + } + else if( mFace == IconList ) + { + QListBoxItem *node = mIconList->item( mIconList->currentItem() ); + if( node == 0 ) { return( false ); } + QWidget *stackItem = mIconListToPageStack[node]; + return showPage(stackItem); + } + + return( false ); +} + + +bool OJanusWidget::showPage( int index ) +{ + if( mPageList == 0 || mValid == false ) + { + return( false ); + } + else + { + return showPage(mPageList->at(index)); + } +} + + +bool OJanusWidget::showPage( QWidget *w ) +{ + if( w == 0 || mValid == false ) + { + return( false ); + } + + if( mFace == TreeList || mFace == IconList ) + { + mPageStack->raiseWidget( w ); + mActivePageWidget = w; + + int index = mPageList->findRef( w ); + mTitleLabel->setText( *mTitleList->at(index) ); + if( mFace == TreeList ) + { + QMap<QListViewItem *, QWidget *>::Iterator it; + for (it = mTreeListToPageStack.begin(); it != mTreeListToPageStack.end(); ++it){ + QListViewItem *key = it.key(); + QWidget *val = it.data(); + if (val == w) { + mTreeList->setSelected(key, true ); + break; + } + } + } + else + { + QMap<QListBoxItem *, QWidget *>::Iterator it; + for (it = mIconListToPageStack.begin(); it != mIconListToPageStack.end(); ++it){ + QListBoxItem *key = it.key(); + QWidget *val = it.data(); + if (val == w) { + mIconList->setSelected( key, true ); + break; + } + } + + // + // 2000-02-13 Espen Sand + // Don't ask me why (because I don't know). If I select a page + // with the mouse the page is not updated until it receives an + // event. It seems this event get lost if the mouse is not moved + // when released. The timer ensures the update + // + QTimer::singleShot( 0, mActivePageWidget, SLOT(update()) ); + } + } + else if( mFace == Tabbed ) + { + mTabControl->showPage(w); + mActivePageWidget = w; + } + else + { + return( false ); + } + + return( true ); +} + + +int OJanusWidget::activePageIndex() const +{ + if( mFace == TreeList) { + QListViewItem *node = mTreeList->selectedItem(); + if( node == 0 ) { return -1; } + QWidget *stackItem = mTreeListToPageStack[node]; + return mPageList->findRef(stackItem); + } + else if (mFace == IconList) { + QListBoxItem *node = mIconList->item( mIconList->currentItem() ); + if( node == 0 ) { return( false ); } + QWidget *stackItem = mIconListToPageStack[node]; + return mPageList->findRef(stackItem); + } + else if( mFace == Tabbed ) { + QWidget *widget = mTabControl->currentPage(); + return( widget == 0 ? -1 : mPageList->findRef( widget ) ); + } + else { + return( -1 ); + } +} + + +int OJanusWidget::pageIndex( QWidget *widget ) const +{ + if( widget == 0 ) + { + return( -1 ); + } + else if( mFace == TreeList || mFace == IconList ) + { + return( mPageList->findRef( widget ) ); + } + else if( mFace == Tabbed ) + { + // + // The user gets the real page widget with addVBoxPage(), addHBoxPage() + // and addGridPage() but not with addPage() which returns a child of + // the toplevel page. addPage() returns a QFrame so I check for that. + // + if( widget->isA("QFrame") ) + { + return( mPageList->findRef( widget->parentWidget() ) ); + } + else + { + return( mPageList->findRef( widget ) ); + } + } + else + { + return( -1 ); + } +} + +void OJanusWidget::slotFontChanged() +{ +#ifdef FIXME + + if ( mTitleLabel != 0 ) + { + mTitleLabel->setFont( KGlobalSettings::generalFont() ); + QFont titleFont( mTitleLabel->font() ); + titleFont.setBold( true ); + mTitleLabel->setFont( titleFont ); + } +#endif + + if( mFace == IconList ) + { + QFont listFont( mIconList->font() ); + listFont.setBold( true ); + mIconList->setFont( listFont ); + mIconList->invalidateHeight(); + mIconList->invalidateWidth(); + } +} + +// makes the treelist behave like the list of kcontrol +void OJanusWidget::slotItemClicked(QListViewItem *it) +{ + if(it && (it->childCount()>0)) + it->setOpen(!it->isOpen()); +} + +void OJanusWidget::setFocus() +{ + if( mValid == false ) { return; } + if( mFace == TreeList ) + { + mTreeList->setFocus(); + } + if( mFace == IconList ) + { + mIconList->setFocus(); + } + else if( mFace == Tabbed ) + { + mTabControl->setFocus(); + } + else if( mFace == Swallow ) + { + mSwallowPage->setFocus(); + } + else if( mFace == Plain ) + { + mPlainPage->setFocus(); + } +} + + +QSize OJanusWidget::minimumSizeHint() const +{ + if( mFace == TreeList || mFace == IconList ) + { + QSize s1( ODialog::spacingHint(), ODialog::spacingHint()*2 ); + QSize s2(0,0); + QSize s3(0,0); + QSize s4( mPageStack->sizeHint() ); + + if( mFace == TreeList ) + { +#if QT_VERSION < 300 + s1.rwidth() += style().splitterWidth(); +#else + s1.rwidth() += style().pixelMetric( QStyle::PM_SplitterWidth ); +#endif + s2 = mTreeList->minimumSize(); + } + else + { + mIconList->updateMinimumHeight(); + mIconList->updateWidth(); + s2 = mIconList->minimumSize(); + } + + if( mTitleLabel->isVisible() == true ) + { + s3 += mTitleLabel->sizeHint(); + s3.rheight() += mTitleSep->minimumSize().height(); + } + + // + // Select the tallest item. It has only effect in IconList mode + // + int h1 = s1.rheight() + s3.rheight() + s4.height(); + int h2 = QMAX( h1, s2.rheight() ); + + return( QSize( s1.width()+s2.width()+QMAX(s3.width(),s4.width()), h2 ) ); + } + else if( mFace == Tabbed ) + { + return( mTabControl->sizeHint() ); + } + else if( mFace == Swallow ) + { + return( mSwallowPage->minimumSize() ); + } + else if( mFace == Plain ) + { + return( mPlainPage->sizeHint() ); + } + else + { + return( QSize( 100, 100 ) ); // Should never happen though. + } + +} + + +QSize OJanusWidget::sizeHint() const +{ + return( minimumSizeHint() ); +} + + +void OJanusWidget::setTreeListAutoResize( bool state ) +{ + if( mFace == TreeList ) + { + mTreeListResizeMode = state == false ? + QSplitter::KeepSize : QSplitter::Stretch; + QSplitter *splitter = (QSplitter*)(mTreeList->parentWidget()); + splitter->setResizeMode( mTreeList, mTreeListResizeMode ); + } +} + + +void OJanusWidget::setIconListAllVisible( bool state ) +{ + if( mFace == IconList ) + { + mIconList->setShowAll( state ); + } +} + +void OJanusWidget::setShowIconsInTreeList( bool state ) +{ + mShowIconsInTreeList = state; +} + +void OJanusWidget::setRootIsDecorated( bool state ) +{ + if( mFace == TreeList ) { + mTreeList->setRootIsDecorated(state); + } +} + +void OJanusWidget::unfoldTreeList( bool persist ) +{ + if( mFace == TreeList ) + { + if( persist ) + connect( mTreeList, SIGNAL( collapsed( QListViewItem * ) ), this, SLOT( slotReopen( QListViewItem * ) ) ); + else + disconnect( mTreeList, SIGNAL( collapsed( QListViewItem * ) ), this, SLOT( slotReopen( QListViewItem * ) ) ); + + for( QListViewItem * item = mTreeList->firstChild(); item; item = item->itemBelow() ) + item->setOpen( true ); + } +} + +void OJanusWidget::showEvent( QShowEvent * ) +{ + if( mFace == TreeList ) + { + QSplitter *splitter = (QSplitter*)(mTreeList->parentWidget()); + splitter->setResizeMode( mTreeList, mTreeListResizeMode ); + } +} + + +// +// 2000-13-02 Espen Sand +// It should be obvious that this eventfilter must only be +// be installed on the vertical scrollbar of the mIconList. +// +bool OJanusWidget::eventFilter( QObject *o, QEvent *e ) +{ + if( e->type() == QEvent::Show ) + { + IconListItem *item = (IconListItem*)mIconList->item(0); + if( item != 0 ) + { + int lw = item->width( mIconList ); + int sw = mIconList->verticalScrollBar()->sizeHint().width(); + mIconList->setFixedWidth( lw+sw+mIconList->frameWidth()*2 ); + } + } + else if( e->type() == QEvent::Hide ) + { + IconListItem *item = (IconListItem*)mIconList->item(0); + if( item != 0 ) + { + int lw = item->width( mIconList ); + mIconList->setFixedWidth( lw+mIconList->frameWidth()*2 ); + } + } + return QWidget::eventFilter( o, e ); +} + + + +// +// Code for the icon list box +// + + +OJanusWidget::IconListBox::IconListBox( QWidget *parent, const char *name, + WFlags f ) + :QListBox( parent, name, f ), mShowAll(false), mHeightValid(false), + mWidthValid(false) +{ +} + + +void OJanusWidget::IconListBox::updateMinimumHeight() +{ + if( mShowAll == true && mHeightValid == false ) + { + int h = frameWidth()*2; + for( QListBoxItem *i = item(0); i != 0; i = i->next() ) + { + h += i->height( this ); + } + setMinimumHeight( h ); + mHeightValid = true; + } +} + + +void OJanusWidget::IconListBox::updateWidth() +{ + if( mWidthValid == false ) + { + int maxWidth = 10; + for( QListBoxItem *i = item(0); i != 0; i = i->next() ) + { + int w = ((IconListItem *)i)->width(this); + maxWidth = QMAX( w, maxWidth ); + } + + for( QListBoxItem *i = item(0); i != 0; i = i->next() ) + { + ((IconListItem *)i)->expandMinimumWidth( maxWidth ); + } + + if( verticalScrollBar()->isVisible() ) + { + maxWidth += verticalScrollBar()->sizeHint().width(); + } + + setFixedWidth( maxWidth + frameWidth()*2 ); + mWidthValid = true; + } +} + + +void OJanusWidget::IconListBox::invalidateHeight() +{ + mHeightValid = false; +} + + +void OJanusWidget::IconListBox::invalidateWidth() +{ + mWidthValid = false; +} + + +void OJanusWidget::IconListBox::setShowAll( bool showAll ) +{ + mShowAll = showAll; + mHeightValid = false; +} + + + +OJanusWidget::IconListItem::IconListItem( QListBox *listbox, const QPixmap &pixmap, + const QString &text ) + : QListBoxItem( listbox ) +{ + mPixmap = pixmap; + if( mPixmap.isNull() == true ) + { + mPixmap = defaultPixmap(); + } + setText( text ); + mMinimumWidth = 0; +} + + +int OJanusWidget::IconListItem::expandMinimumWidth( int width ) +{ + mMinimumWidth = QMAX( mMinimumWidth, width ); + return( mMinimumWidth ); +} + + +const QPixmap &OJanusWidget::IconListItem::defaultPixmap() +{ + static QPixmap *pix=0; + if( pix == 0 ) + { + pix = new QPixmap( 32, 32 ); + QPainter p( pix ); + p.eraseRect( 0, 0, pix->width(), pix->height() ); + p.setPen( Qt::red ); + p.drawRect ( 0, 0, pix->width(), pix->height() ); + p.end(); + + QBitmap mask( pix->width(), pix->height(), true ); + mask.fill( Qt::black ); + p.begin( &mask ); + p.setPen( Qt::white ); + p.drawRect ( 0, 0, pix->width(), pix->height() ); + p.end(); + + pix->setMask( mask ); + } + return( *pix ); +} + + +void OJanusWidget::IconListItem::paint( QPainter *painter ) +{ + QFontMetrics fm = painter->fontMetrics(); + //int wt = fm.boundingRect(text()).width(); + int wp = mPixmap.width(); + int ht = fm.lineSpacing(); + int hp = mPixmap.height(); + + painter->drawPixmap( (mMinimumWidth-wp)/2, 5, mPixmap ); + if( text().isEmpty() == false ) + { + painter->drawText( 0, hp+7, mMinimumWidth, ht, Qt::AlignCenter, text() ); + } +} + +int OJanusWidget::IconListItem::height( const QListBox *lb ) const +{ + if( text().isEmpty() == true ) + { + return( mPixmap.height() ); + } + else + { + return( mPixmap.height() + lb->fontMetrics().lineSpacing()+10 ); + } +} + + +int OJanusWidget::IconListItem::width( const QListBox *lb ) const +{ + int wt = lb->fontMetrics().boundingRect(text()).width()+10; + int wp = mPixmap.width() + 10; + int w = QMAX( wt, wp ); + return( QMAX( w, mMinimumWidth ) ); +} + +// Just remove the page from our stack of widgets. Do not modify the given widget in +// any way. No memory leak occurs as parent is not changed. +// Make this virtual in KDE 4.0. +// Ravikiran Rajagopal <ravi@ee.eng.ohio-state.edu> +void OJanusWidget::removePage( QWidget *page ) +{ + if (!mPageList || !mPageList->containsRef(page)) + return; + + int index = mPageList->findRef( page ); + if ( mTitleList ) + mTitleList->remove(mTitleList->at(index)); + + mPageList->removeRef(page); + + if ( mFace == TreeList ) + { + QMap<QListViewItem*, QWidget *>::Iterator i; + for( i = mTreeListToPageStack.begin(); i != mTreeListToPageStack.end(); ++i ) + if (i.data()==page) + { + delete i.key(); + mPageStack->removeWidget(page); + mTreeListToPageStack.remove(i); + break; + } + } + else if ( mFace == IconList ) + { + QMap<QListBoxItem*, QWidget *>::Iterator i; + for( i = mIconListToPageStack.begin(); i != mIconListToPageStack.end(); ++i ) + if (i.data()==page) + { + delete i.key(); + mPageStack->removeWidget(page); + mIconListToPageStack.remove(i); + break; + } + } + else // Tabbed + { + mTabControl->removePage(page); + } +} diff --git a/libopie2/qt3/opieui/ojanuswidget.h b/libopie2/qt3/opieui/ojanuswidget.h new file mode 100644 index 0000000..b601b8c --- a/dev/null +++ b/libopie2/qt3/opieui/ojanuswidget.h @@ -0,0 +1,551 @@ +/* + This file is part of the Opie Project + + Copyright (C) 2003 Michael 'Mickey' Lauer <mickey@tm.informatik.uni-frankfurt.de> + Copyright (C) 1999-2000 Espen Sand (espen@kde.org) + =. + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef OJANUSWIDGET_H +#define OJANUSWIDGET_H + +#include <qptrlist.h> +#include <qpixmap.h> +#include <qlistbox.h> +#include <qmap.h> +#include <qsplitter.h> +#include <qstringlist.h> + +class QGrid; +class QHBox; +class QLabel; +class QTabWidget; +class QVBox; +class QWidgetStack; +class OSeparator; +class QListView; +class QListViewItem; + +/** + * Provides a number of ready to use layouts (faces). It is used + * as an internal widget in @ref KDialogBase, but can also used as a + * widget of its own. + * + * It provides TreeList, IconList, Tabbed, Plain and Swallow layouts. + * + * The TreeList face provides a list in the left area and pages in the + * right. The area are separated by a movable splitter. The style is somewhat + * similar to the layout in the Control Center. A page is raised by + * selecting the corresponding tree list item. + * + * The IconList face provides an icon list in the left area and pages in the + * right. For each entry the Icon is on top with the text below. The style + * is somewhat similar to the layout of the Eudora configuation dialog box. + * A page is raised by selecting the corresponding icon list item. The + * preferred icon size is 32x32 pixels. + * + * The Tabbed face is a common tabbed widget. The procedure for creating a + * page is similar for creating a TreeList. This has the advantage that if + * your widget contain too many pages it is trivial to convert it into a + * TreeList. Just change the face in the KJanusWidget constructor to + * KJanusWidget::TreeList and you have a tree list layout instead. + * + * The Plain face provides an empty widget (QFrame) where you can place your + * widgets. The KJanusWidget makes no assumptions regarding the contents so + * you are free to add whatever you want. + * + * The Swallow face is provided in order to simplify the usage of existing + * widgets and to allow changing the visible widget. You specify the widget + * to be displayed by @ref #setSwallowedWidget(). Your widget will be + * reparented inside the widget. You can specify a Null (0) widget. A empty + * space is then displayed. + * + * For all modes it is important that you specify the @ref QWidget::minimumSize() + * on the page, plain widget or the swallowed widget. If you use a QLayout + * on the page, plain widget or the swallowed widget this will be taken care + * of automatically. The size is used when the KJanusWidget determines its + * own minimum size. You get the minimum size by using the + * @ref #minimumSizeHint() or @ref #sizeHint() methods. + * + * Pages that have been added in TreeList, IconList or Tabbed mode can be + * removed by simply deleting the page. + * + * @short Easy to use widget with many layouts + * @author Espen Sand (espen@kde.org) + */ +class OJanusWidget : public QWidget +{ + Q_OBJECT + + private: + + class IconListBox : public QListBox + { + public: + IconListBox( QWidget *parent=0, const char *name=0, WFlags f=0 ); + void updateMinimumHeight(); + void updateWidth(); + void invalidateHeight(); + void invalidateWidth(); + void setShowAll( bool showAll ); + + private: + bool mShowAll; + bool mHeightValid; + bool mWidthValid; + }; + + public: + + enum Face + { + TreeList = 0, + Tabbed, + Plain, + Swallow, + IconList + }; + + public: + + /** + * Constructor where you specify the face. + * + * @param parent Parent of the widget. + * @param name Widget name. + * @param int face The kind of dialog, Use TreeList, Tabbed, Plain or + * Swallow. + */ + OJanusWidget( QWidget *parent=0, const char *name=0, int face=Plain ); + + /** + * Destructor. + */ + ~OJanusWidget(); + + /** + * Raises the page which was added by @ref addPage(). + * + * @param index The index of the page you want to raise. + */ + virtual bool showPage( int index ); + + /** + * Returns the index of the page that are currently displayed. + * + * @return The index or -1 if the face is not Tabbed, TreeList or + * IconList. + */ + virtual int activePageIndex() const; + + /** + * Use this to verify + * that no memory allocation failed. + * + * @return true if the widget was properly created. + */ + virtual bool isValid() const; + + /** + * Returns the face type. + * + * @return The face type. + */ + virtual int face() const; + + /** + * Returns the minimum size that must be made available for the widget + * so that UIs can be displayed properly + * + * @return The minimum size. + */ + virtual QSize minimumSizeHint() const; + + /** + * Returns the recommended size for the widget in order to be displayed + * properly. + * + * @return The recommended size. + */ + virtual QSize sizeHint() const; + + /** + * Returns the empty widget that is available in Plain mode. + * + * @return The widget or 0 if the face in not Plain. + */ + virtual QFrame *plainPage(); + + /** + * Add a new page when the class is used in TreeList, IconList or Tabbed + * mode. The returned widget is empty and you must add your widgets + * as children to this widget. In most cases you must create a layout + * manager and associate it with this widget as well. + * + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + * + * @param item String used in the list or Tab item. + * @param header A longer string used in TreeList and IconList mode to + * describe the contents of a page. If empty, the item string + * will be used instead. + * @param pixmap Used in IconList mode or in TreeList mode. You should + * prefer a pixmap with size 32x32 pixels. + * + * @return The empty page or 0 if the face is not TreeList, IconList or + * Tabbed. + */ + virtual QFrame *addPage(const QString &item,const QString &header=QString::null, + const QPixmap &pixmap=QPixmap() ); + + /** + * This is like addPage just above, with the difference that the first + * element is a list of strings. These strings are used to form a path + * of folders down to the given page. The initial elements are names + * for the folders, while the last element is the name of the page. + * Note: This does yet only work for the TreeList face. Later this may + * be added for the IconList face too. In other faces than the + * TreeList, all the strings except the last one is ignored. + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + * + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + **/ + virtual QFrame *addPage(const QStringList &items, const QString &header=QString::null, + const QPixmap &pixmap=QPixmap() ); + + /** + * Add a new page when the class is used in TreeList, IconList or Tabbed + * mode. The returned widget is empty and you must add your widgets + * as children to this widget. The returned widget is a @ref QVBox + * so it contains a QVBoxLayout layout that lines up the child widgets + * are vertically. + * + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + * + * @param item String used in the list or Tab item. + * @param header A longer string used in TreeList and IconList mode to + * describe the contents of a page. If empty, the item string + * will be used instead. + * @param pixmap Used in IconList mode or in TreeList mode. You should + * prefer a pixmap with size 32x32 pixels. + * + * @return The empty page or 0 if the face is not TreeList, IconList or + * Tabbed. */ + virtual QVBox *addVBoxPage( const QString &item, + const QString &header=QString::null, + const QPixmap &pixmap=QPixmap() ); + + /** + * This is like addVBoxPage just above, with the difference that the first + * element is a list of strings. These strings are used to form a path + * of folders down to the given page. The initial elements are names + * for the folders, while the last element is the name of the page. + * Note: This does yet only work for the TreeList face. Later this may + * be added for the IconList face too. In other faces than the + * TreeList, all the strings except the last one is ignored. + * + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + **/ + virtual QVBox *addVBoxPage( const QStringList &items, + const QString &header=QString::null, + const QPixmap &pixmap=QPixmap() ); + + /** + * Add a new page when the class is used in TreeList, IconList or Tabbed + * mode. The returned widget is empty and you must add your widgets + * as children to this widget. The returned widget is a @ref QHBox + * so it contains a QHBoxLayout layout that lines up the child widgets + * are horizontally. + * + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + * + * @param item String used in the list or Tab item. + * @param header A longer string used in TreeList and IconList mode to + * describe the contents of a page. If empty, the item string + * will be used instead. + * @param pixmap Used in IconList mode or in TreeList mode. You should + * prefer a pixmap with size 32x32 pixels. + * + * @return The empty page or 0 if the face is not TreeList, IconList or + * Tabbed. + */ + virtual QHBox *addHBoxPage( const QString &itemName, + const QString &header=QString::null, + const QPixmap &pixmap=QPixmap() ); + + /** + * This is like addHBoxPage just above, with the difference that the first + * element is a list of strings. These strings are used to form a path + * of folders down to the given page. The initial elements are names + * for the folders, while the last element is the name of the page. + * Note: This does yet only work for the TreeList face. Later this may + * be added for the IconList face too. In other faces than the + * TreeList, all the strings except the last one is ignored. + * + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + **/ + virtual QHBox *addHBoxPage( const QStringList &items, + const QString &header=QString::null, + const QPixmap &pixmap=QPixmap() ); + + /** + * Add a new page when the class is used in either TreeList or Tabbed + * mode. The returned widget is empty and you must add your widgets + * as children to this widget. The returned widget is a @ref QGrid + * so it contains a QGridLayout layout that places up the child widgets + * in a grid. + * + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + * + * @param n Specifies the number of columns if 'dir' is QGrid::Horizontal + * or the number of rows if 'dir' is QGrid::Vertical. + * @param dir Can be QGrid::Horizontal or QGrid::Vertical. + * @param item String used in the list or Tab item. + * @param header A longer string used in TreeList and IconList mode to + * describe the contents of a page. If empty, the item string + * will be used instead. + * @param pixmap Used in IconList mode or in TreeList mode. You should + * prefer a pixmap with size 32x32 pixels. + * + * @return The empty page or 0 if the face is not TreeList, IconList or + * Tabbed. + */ + virtual QGrid *addGridPage( int n, Orientation dir, + const QString &itemName, + const QString &header=QString::null, + const QPixmap &pixmap=QPixmap() ); + + /** + * This is like addGridPage just above, with the difference that the first + * element is a list of strings. These strings are used to form a path + * of folders down to the given page. The initial elements are names + * for the folders, while the last element is the name of the page. + * Note: This does yet only work for the TreeList face. Later this may + * be added for the IconList face too. In other faces than the + * TreeList, all the strings except the last one is ignored. + * + * Deleting the returned frame will cause the listitem or tab to be + * removed (you can re-add a page with the same name later. + **/ + virtual QGrid *addGridPage( int n, Orientation dir, + const QStringList &items, + const QString &header=QString::null, + const QPixmap &pixmap=QPixmap() ); + + /** + * @short Removes a page created with @ref addPage, @ref addVBoxPage, + * @ref addHBoxPage or @ref addGridPage. If the page has already + * been deleted or has already been removed, nothing happens. The widget + * itself is not deleted. + * + * @param page The widget returned by @ref addPage , @ref addVBoxPage , + * @ref addHBoxPage or @ref addGridPage . + */ + void removePage( QWidget *page ); + + + /** + * Returns the index of a page created with @ref addPage , + * @ref addVBoxPage , @ref addHBoxPage or @ref addGridPage . + * You can can compare this index with the value returned from + * @ref activePageIndex if you need to do some page specific actions + * in your code. + * + * The returned index will never change so you can safely use this + * function once and save the value. + * + * @param widget The widget returned by @ref addPage , @ref addVBoxPage , + * @ref addHBoxPage or @ref addGridPage . + * + * @return The index or -1 if the face is not Tabbed, TreeList or + * IconList + */ + virtual int pageIndex( QWidget *widget ) const; + + /** + * Defines the widget to be swallowed. + * + * This method can be used several + * times. Only the latest defined widget will be shown. + * + * @param widget The widget to be swallowed. If 0, then an empty rectangle + * is displayed. + */ + virtual bool setSwallowedWidget( QWidget *widget ); + + /** + * This function has only effect in TreeList mode. + * + * Defines how the tree list is resized when the widget is resized + * horizontally. By default the tree list keeps its width when the + * widget becomes wider. + * + * @param state The resize mode. If false (default) the TreeList keeps + * its current width when the widget becomes wider. + */ + virtual void setTreeListAutoResize( bool state ); + + /** + * This function has only effect in TreeList mode. + * + * This tells the widgets whether the icons given in the @ref addPage, + * @ref addVBoxPage, @ref addHBoxPage, or @ref addGridPage methods should + * be shown in the TreeList. + * + * Note: This method must be called before calling any of the methods + * which add icons to the page. + * + * @param state If true the icons are shown. + **/ + virtual void setShowIconsInTreeList(bool state); + + /** + * This function has only effect in TreeList mode. + * + * This tells the widgets whether the root should be decorated. + * For details see @ref QListView::setRootIsDecorated + * + * @param state Root will be decorated if true. + **/ + virtual void setRootIsDecorated( bool state ); + + /** + * This function has only effect in TreeList mode. + * + * This tells the TreeList to unfold the whole tree so that all entries + * are visible. + * + * If the list is empty when you call this method newly created entries + * will not automatically be opened. If the @p persist flag is set opened + * entries cannot be closed again, though. + * + * @param persist If true the tree always stays unfolded. + * @since 3.2 + */ + /*virtual*/ void unfoldTreeList( bool persist = false ); //### KDE4 BIC add virtual + + /** + * This function has only effect in IconList mode. + * + * Defines how the icon list widget is displayed. By default it is + * the widgets in the pages that decide the minimum height + * of the toplevel widget. A vertical scrollbar can be used in + * the icon list area. + * + * @param state The visibility mode. If true, the minimum height is + * adjusted so that every icon in the list is visible at the + * same time. The vertical scrollbar will never be visible. + */ + virtual void setIconListAllVisible( bool state ); + + /** + * Sets the icon used in TreeList Mode for the given path. + * @param path The path for which this icon should be shown. + * @param pixmap The icon used. + **/ + virtual void setFolderIcon(const QStringList &path, const QPixmap &pixmap); + + signals: + void aboutToShowPage(QWidget *page); + + public slots: + /** + * Give the keyboard input focus to the widget. + */ + virtual void setFocus(); + + protected: + /** + * Reimplemented to handle the splitter width when the the face + * is TreeList + */ + virtual void showEvent( QShowEvent * ); + + /** + * This function is used internally when in IconList mode. If you + * reimplement this class a make your own event filter, make sure to + * call this function from your filter. + * + * @param o Object that has received an event. + * @param e The event. + */ + virtual bool eventFilter( QObject *o, QEvent *e ); + + private slots: + bool slotShowPage(); + void slotFontChanged(); + void slotItemClicked(QListViewItem *it); + void pageGone(QObject *obj); // signal from the added page's "destroyed" signal + void slotReopen(QListViewItem *item); + + protected: + bool showPage( QWidget *w ); + void addPageWidget( QFrame *page, const QStringList &items, + const QString &header, const QPixmap &pixmap ); + void InsertTreeListItem(const QStringList &items, const QPixmap &pixmap, QFrame *page); + QWidget *FindParent(); + + private: + bool mValid; + + QPtrList<QWidget> *mPageList; + QStringList *mTitleList; + + int mFace; + QListView *mTreeList; + IconListBox *mIconList; + QWidgetStack *mPageStack; + QLabel *mTitleLabel; + QTabWidget *mTabControl; + QFrame *mPlainPage; + QWidget *mSwallowPage; + QWidget *mActivePageWidget; + OSeparator *mTitleSep; + QSplitter::ResizeMode mTreeListResizeMode; + bool mShowIconsInTreeList; + QMap<QListViewItem *, QWidget *> mTreeListToPageStack; + QMap<QListBoxItem *, QWidget *> mIconListToPageStack; + QMap<QString, QPixmap> mFolderIconMap; + QMap<QString, QStringList> mChildrenNames; + QMap<QString, QWidget *> mChildPages; + + public: + class IconListItem; + + private: + class OJanusWidgetPrivate; + OJanusWidgetPrivate *d; +}; + +#endif diff --git a/libopie2/qt3/opieui/olineedit.cpp b/libopie2/qt3/opieui/olineedit.cpp new file mode 100644 index 0000000..9cb0cff --- a/dev/null +++ b/libopie2/qt3/opieui/olineedit.cpp @@ -0,0 +1,729 @@ +/* + This file Copyright (C) 2003 Michael 'Mickey' Lauer <mickey@tm.informatik.uni-frankfurt.de> + is part of the Copyright (C) 2001 Carsten Pfeiffer <pfeiffer@kde.org>, Dawit Alemayehu <adawit@kde.org> + Opie Project Copyright (C) 1999 Preston Brown <pbrown@kde.org>, Patrick Ward <PAT_WARD@HP-USA-om5.om.hp.com> + Copyright (C) 1997 Sven Radej (sven.radej@iname.com) + =. + .=l. Originally part of the KDE Project + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +/* QT */ + +#include <qapplication.h> +#include <qclipboard.h> +#include <qtimer.h> +#include <qpopupmenu.h> + +/* OPIE */ + +#include <opie2/ocompletionbox.h> +#include <opie2/olineedit.h> +#include <opie2/oglobalsettings.h> + +typedef QString KURL; //FIXME: Revise for Opie + +/*====================================================================================== + * OLineEditPrivate + *======================================================================================*/ + +class OLineEdit::OLineEditPrivate +{ +public: + OLineEditPrivate() + { + grabReturnKeyEvents = false; + handleURLDrops = true; + completionBox = 0L; + } + ~OLineEditPrivate() + { + delete completionBox; + } + + bool grabReturnKeyEvents; + bool handleURLDrops; + OCompletionBox *completionBox; +}; + + +/*====================================================================================== + * OLineEdit + *======================================================================================*/ + +OLineEdit::OLineEdit( const QString &string, QWidget *parent, const char *name ) + : QLineEdit( string, parent, name ) +{ + init(); +} + +OLineEdit::OLineEdit( QWidget *parent, const char *name ) + : QLineEdit( parent, name ) +{ + init(); +} + +OLineEdit::~OLineEdit () +{ + delete d; +} + +void OLineEdit::init() +{ + d = new OLineEditPrivate; + possibleTripleClick = false; + // Enable the context menu by default. + setContextMenuEnabled( true ); + //OCursor::setAutoHideCursor( this, true, true ); + installEventFilter( this ); +} + +void OLineEdit::setCompletionMode( OGlobalSettings::Completion mode ) +{ + OGlobalSettings::Completion oldMode = completionMode(); + if ( oldMode != mode && oldMode == OGlobalSettings::CompletionPopup && + d->completionBox && d->completionBox->isVisible() ) + d->completionBox->hide(); + + // If the widgets echo mode is not Normal, no completion + // feature will be enabled even if one is requested. + if ( echoMode() != QLineEdit::Normal ) + mode = OGlobalSettings::CompletionNone; // Override the request. + + OCompletionBase::setCompletionMode( mode ); +} + +void OLineEdit::setCompletedText( const QString& t, bool marked ) +{ + QString txt = text(); + if ( t != txt ) + { + int curpos = marked ? txt.length() : t.length(); + validateAndSet( t, curpos, curpos, t.length() ); + } +} + +void OLineEdit::setCompletedText( const QString& text ) +{ + OGlobalSettings::Completion mode = completionMode(); + bool marked = ( mode == OGlobalSettings::CompletionAuto || + mode == OGlobalSettings::CompletionMan || + mode == OGlobalSettings::CompletionPopup ); + setCompletedText( text, marked ); +} + +void OLineEdit::rotateText( OCompletionBase::KeyBindingType type ) +{ + OCompletion* comp = compObj(); + if ( comp && + (type == OCompletionBase::PrevCompletionMatch || + type == OCompletionBase::NextCompletionMatch ) ) + { + QString input = (type == OCompletionBase::PrevCompletionMatch) ? comp->previousMatch() : comp->nextMatch(); + // Skip rotation if previous/next match is null or the same text + if ( input.isNull() || input == displayText() ) + return; + #if QT_VERSION > 290 + setCompletedText( input, hasSelectedText() ); + #else + setCompletedText( input, hasMarkedText() ); + #endif + } +} + +void OLineEdit::makeCompletion( const QString& text ) +{ + OCompletion *comp = compObj(); + if ( !comp ) + return; // No completion object... + + QString match = comp->makeCompletion( text ); + OGlobalSettings::Completion mode = completionMode(); + if ( mode == OGlobalSettings::CompletionPopup ) + { + if ( match.isNull() ) + { + if ( d->completionBox ) { + d->completionBox->hide(); + d->completionBox->clear(); + } + } + else + setCompletedItems( comp->allMatches() ); + } + else + { + // all other completion modes + // If no match or the same match, simply return without completing. + if ( match.isNull() || match == text ) + return; + + setCompletedText( match ); + } +} + +void OLineEdit::setReadOnly(bool readOnly) +{ + QPalette p = palette(); + if (readOnly) + { + QColor color = p.color(QPalette::Disabled, QColorGroup::Background); + p.setColor(QColorGroup::Base, color); + p.setColor(QColorGroup::Background, color); + } + else + { + QColor color = p.color(QPalette::Normal, QColorGroup::Base); + p.setColor(QColorGroup::Base, color); + p.setColor(QColorGroup::Background, color); + } + setPalette(p); + + QLineEdit::setReadOnly (readOnly); +} + +void OLineEdit::keyPressEvent( QKeyEvent *e ) +{ + qDebug( "OLineEdit::keyPressEvent()" ); + + /* + + KKey key( e ); + + if ( KStdAccel::copy().contains( key ) ) { + copy(); + return; + } + else if ( KStdAccel::paste().contains( key ) ) { + paste(); + return; + } + else if ( KStdAccel::cut().contains( key ) ) { + cut(); + return; + } + else if ( KStdAccel::undo().contains( key ) ) { + undo(); + return; + } + else if ( KStdAccel::redo().contains( key ) ) { + redo(); + return; + } + else if ( KStdAccel::deleteWordBack().contains( key ) ) + { + cursorWordBackward(TRUE); + if ( hasSelectedText() ) + del(); + + e->accept(); + return; + } + else if ( KStdAccel::deleteWordForward().contains( key ) ) + { + // Workaround for QT bug where + cursorWordForward(TRUE); + if ( hasSelectedText() ) + del(); + + e->accept(); + return; + } + */ + + // Filter key-events if EchoMode is normal & + // completion mode is not set to CompletionNone + if ( echoMode() == QLineEdit::Normal && + completionMode() != OGlobalSettings::CompletionNone ) + { + KeyBindingMap keys = getKeyBindings(); + OGlobalSettings::Completion mode = completionMode(); + bool noModifier = (e->state() == NoButton || e->state()== ShiftButton); + + if ( (mode == OGlobalSettings::CompletionAuto || + mode == OGlobalSettings::CompletionMan) && noModifier ) + { + QString keycode = e->text(); + if ( !keycode.isNull() && keycode.unicode()->isPrint() ) + { + QLineEdit::keyPressEvent ( e ); + QString txt = text(); + int len = txt.length(); + #if QT_VERSION > 290 + if ( !hasSelectedText() && len && cursorPosition() == len ) + #else + if ( !hasMarkedText() && len && cursorPosition() == len ) + #endif + { + if ( emitSignals() ) + emit completion( txt ); + if ( handleSignals() ) + makeCompletion( txt ); + e->accept(); + } + return; + } + } + + else if ( mode == OGlobalSettings::CompletionPopup && noModifier ) + { + qDebug( "OLineEdit::keyPressEvent() - global settings = CompletionPopup & noModifier" ); + + QString old_txt = text(); + QLineEdit::keyPressEvent ( e ); + QString txt = text(); + int len = txt.length(); + QString keycode = e->text(); + + + if ( txt != old_txt && len && cursorPosition() == len && + ( (!keycode.isNull() && keycode.unicode()->isPrint()) || + e->key() == Key_Backspace ) ) + { + if ( emitSignals() ) + emit completion( txt ); // emit when requested... + if ( handleSignals() ) + makeCompletion( txt ); // handle when requested... + e->accept(); + } + else if (!len && d->completionBox && d->completionBox->isVisible()) + d->completionBox->hide(); + + return; + } + + /*else if ( mode == OGlobalSettings::CompletionShell ) + { + // Handles completion. + KShortcut cut; + if ( keys[TextCompletion].isNull() ) + cut = KStdAccel::shortcut(KStdAccel::TextCompletion); + else + cut = keys[TextCompletion]; + + if ( cut.contains( key ) ) + { + // Emit completion if the completion mode is CompletionShell + // and the cursor is at the end of the string. + QString txt = text(); + int len = txt.length(); + if ( cursorPosition() == len && len != 0 ) + { + if ( emitSignals() ) + emit completion( txt ); + if ( handleSignals() ) + makeCompletion( txt ); + return; + } + } + else if ( d->completionBox ) + d->completionBox->hide(); + } + + // handle rotation + if ( mode != OGlobalSettings::CompletionNone ) + { + // Handles previous match + KShortcut cut; + if ( keys[PrevCompletionMatch].isNull() ) + cut = KStdAccel::shortcut(KStdAccel::PrevCompletion); + else + cut = keys[PrevCompletionMatch]; + + if ( cut.contains( key ) ) + { + if ( emitSignals() ) + emit textRotation( OCompletionBase::PrevCompletionMatch ); + if ( handleSignals() ) + rotateText( OCompletionBase::PrevCompletionMatch ); + return; + } + + // Handles next match + if ( keys[NextCompletionMatch].isNull() ) + cut = KStdAccel::key(KStdAccel::NextCompletion); + else + cut = keys[NextCompletionMatch]; + + if ( cut.contains( key ) ) + { + if ( emitSignals() ) + emit textRotation( OCompletionBase::NextCompletionMatch ); + if ( handleSignals() ) + rotateText( OCompletionBase::NextCompletionMatch ); + return; + } + } + + // substring completion + if ( compObj() ) + { + KShortcut cut; + if ( keys[SubstringCompletion].isNull() ) + cut = KStdAccel::shortcut(KStdAccel::SubstringCompletion); + else + cut = keys[SubstringCompletion]; + + if ( cut.contains( key ) ) + { + if ( emitSignals() ) + emit substringCompletion( text() ); + if ( handleSignals() ) + { + setCompletedItems( compObj()->substringCompletion(text())); + e->accept(); + } + return; + } + } */ + } + + // Let QLineEdit handle any other keys events. + QLineEdit::keyPressEvent ( e ); +} + +void OLineEdit::mouseDoubleClickEvent( QMouseEvent* e ) +{ + if ( e->button() == Qt::LeftButton ) + { + possibleTripleClick=true; + QTimer::singleShot( QApplication::doubleClickInterval(),this, + SLOT(tripleClickTimeout()) ); + } + QLineEdit::mouseDoubleClickEvent( e ); +} + +void OLineEdit::mousePressEvent( QMouseEvent* e ) +{ + if ( possibleTripleClick && e->button() == Qt::LeftButton ) + { + selectAll(); + return; + } + QLineEdit::mousePressEvent( e ); +} + +void OLineEdit::tripleClickTimeout() +{ + possibleTripleClick=false; +} + +QPopupMenu *OLineEdit::createPopupMenu() +{ + // Return if popup menu is not enabled !! + if ( !m_bEnableMenu ) + return 0; + + #if QT_VERSION > 290 + QPopupMenu *popup = QLineEdit::createPopupMenu(); + #else + QPopupMenu *popup = new QPopupMenu(); + #warning OLineEdit is not fully functional on Qt2 + #endif + + // completion object is present. + if ( compObj() ) + { + QPopupMenu *subMenu = new QPopupMenu( popup ); + connect( subMenu, SIGNAL( activated( int ) ), + this, SLOT( completionMenuActivated( int ) ) ); + + popup->insertSeparator(); + //popup->insertItem( SmallIconSet("completion"), i18n("Text Completion"), + // subMenu ); + + popup->insertItem( tr("Text Completion"), subMenu ); + + subMenu->insertItem( tr("None"), NoCompletion ); + subMenu->insertItem( tr("Manual"), ShellCompletion ); + subMenu->insertItem( tr("Automatic"), AutoCompletion ); + subMenu->insertItem( tr("Dropdown List"), PopupCompletion ); + subMenu->insertItem( tr("Short Automatic"), SemiAutoCompletion ); + + //subMenu->setAccel( KStdAccel::completion(), ShellCompletion ); + subMenu->setAccel( Key_Tab, ShellCompletion ); + + OGlobalSettings::Completion mode = completionMode(); + subMenu->setItemChecked( NoCompletion, + mode == OGlobalSettings::CompletionNone ); + subMenu->setItemChecked( ShellCompletion, + mode == OGlobalSettings::CompletionShell ); + subMenu->setItemChecked( PopupCompletion, + mode == OGlobalSettings::CompletionPopup ); + subMenu->setItemChecked( AutoCompletion, + mode == OGlobalSettings::CompletionAuto ); + subMenu->setItemChecked( SemiAutoCompletion, + mode == OGlobalSettings::CompletionMan ); + if ( mode != OGlobalSettings::completionMode() ) + { + subMenu->insertSeparator(); + subMenu->insertItem( tr("Default"), Default ); + } + } + // ### do we really need this? Yes, Please do not remove! This + // allows applications to extend the popup menu without having to + // inherit from this class! (DA) + emit aboutToShowContextMenu( popup ); + + return popup; +} + +void OLineEdit::completionMenuActivated( int id ) +{ + OGlobalSettings::Completion oldMode = completionMode(); + + switch ( id ) + { + case Default: + setCompletionMode( OGlobalSettings::completionMode() ); break; + case NoCompletion: + setCompletionMode( OGlobalSettings::CompletionNone ); break; + case AutoCompletion: + setCompletionMode( OGlobalSettings::CompletionAuto ); break; + case SemiAutoCompletion: + setCompletionMode( OGlobalSettings::CompletionMan ); break; + case ShellCompletion: + setCompletionMode( OGlobalSettings::CompletionShell ); break; + case PopupCompletion: + setCompletionMode( OGlobalSettings::CompletionPopup ); break; + default: return; + } + + if ( oldMode != completionMode() ) + { + if ( oldMode == OGlobalSettings::CompletionPopup && + d->completionBox && d->completionBox->isVisible() ) + d->completionBox->hide(); + emit completionModeChanged( completionMode() ); + } +} + +/*void OLineEdit::dropEvent(QDropEvent *e) +{ + KURL::List urlList; + if( d->handleURLDrops && KURLDrag::decode( e, urlList ) ) + { + QString dropText = text(); + KURL::List::ConstIterator it; + for( it = urlList.begin() ; it != urlList.end() ; ++it ) + { + if(!dropText.isEmpty()) + dropText+=' '; + + dropText += (*it).prettyURL(); + } + + validateAndSet( dropText, dropText.length(), 0, 0); + + e->accept(); + } + else + QLineEdit::dropEvent(e); +}*/ + +bool OLineEdit::eventFilter( QObject* o, QEvent* ev ) +{ + if( o == this ) + { + //OCursor::autoHideEventFilter( this, ev ); + if ( ev->type() == QEvent::AccelOverride ) + { + QKeyEvent *e = static_cast<QKeyEvent *>( ev ); + // if (overrideAccel (e)) + // { + // e->accept(); + // return true; + // } + } + else if( ev->type() == QEvent::KeyPress ) + { + QKeyEvent *e = static_cast<QKeyEvent *>( ev ); + + if( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter ) + { + bool trap = d->completionBox && d->completionBox->isVisible(); + + // Qt will emit returnPressed() itself if we return false + if ( d->grabReturnKeyEvents || trap ) + emit QLineEdit::returnPressed(); + + emit returnPressed( displayText() ); + + if ( trap ) + d->completionBox->hide(); + + // Eat the event if the user asked for it, or if a completionbox was visible + return d->grabReturnKeyEvents || trap; + } + } + } + return QLineEdit::eventFilter( o, ev ); +} + + +void OLineEdit::setURLDropsEnabled(bool enable) +{ + d->handleURLDrops=enable; +} + +bool OLineEdit::isURLDropsEnabled() const +{ + return d->handleURLDrops; +} + +void OLineEdit::setTrapReturnKey( bool grab ) +{ + d->grabReturnKeyEvents = grab; +} + +bool OLineEdit::trapReturnKey() const +{ + return d->grabReturnKeyEvents; +} + +/*void OLineEdit::setURL( const KURL& url ) +{ + QLineEdit::setText( url.prettyURL() ); +}*/ + +void OLineEdit::makeCompletionBox() +{ + if ( d->completionBox ) + return; + + d->completionBox = new OCompletionBox( this, "completion box" ); + if ( handleSignals() ) + { + connect( d->completionBox, SIGNAL(highlighted( const QString& )), + SLOT(setText( const QString& )) ); + connect( d->completionBox, SIGNAL(userCancelled( const QString& )), + SLOT(setText( const QString& )) ); + + // Nice lil' hacklet ;) KComboBox doesn't know when the completionbox + // is created (childEvent() is even more hacky, IMHO), so we simply + // forward the completionbox' activated signal from here. + if ( parentWidget() && parentWidget()->inherits("KComboBox") ) + connect( d->completionBox, SIGNAL( activated( const QString& )), + parentWidget(), SIGNAL( activated( const QString & ))); + } +} + +/*bool OLineEdit::overrideAccel (const QKeyEvent* e) +{ + KShortcut scKey; + + KKey key( e ); + KeyBindingMap keys = getKeyBindings(); + + if (keys[TextCompletion].isNull()) + scKey = KStdAccel::shortcut(KStdAccel::TextCompletion); + else + scKey = keys[TextCompletion]; + + if (scKey.contains( key )) + return true; + + if (keys[NextCompletionMatch].isNull()) + scKey = KStdAccel::shortcut(KStdAccel::NextCompletion); + else + scKey = keys[NextCompletionMatch]; + + if (scKey.contains( key )) + return true; + + if (keys[PrevCompletionMatch].isNull()) + scKey = KStdAccel::shortcut(KStdAccel::PrevCompletion); + else + scKey = keys[PrevCompletionMatch]; + + if (scKey.contains( key )) + return true; + + if (KStdAccel::deleteWordBack().contains( key )) + return true; + if (KStdAccel::deleteWordForward().contains( key )) + return true; + + if (d->completionBox && d->completionBox->isVisible ()) + if (e->key () == Key_Backtab) + return true; + + return false; +}*/ + +void OLineEdit::setCompletedItems( const QStringList& items ) +{ + QString txt = text(); + if ( !items.isEmpty() && + !(items.count() == 1 && txt == items.first()) ) + { + if ( !d->completionBox ) + makeCompletionBox(); + + if ( !txt.isEmpty() ) + d->completionBox->setCancelledText( txt ); + d->completionBox->setItems( items ); + d->completionBox->popup(); + } + else + { + if ( d->completionBox && d->completionBox->isVisible() ) + d->completionBox->hide(); + } +} + +OCompletionBox * OLineEdit::completionBox( bool create ) +{ + if ( create ) + makeCompletionBox(); + + return d->completionBox; +} + +void OLineEdit::setCompletionObject( OCompletion* comp, bool hsig ) +{ + OCompletion *oldComp = compObj(); + if ( oldComp && handleSignals() ) + disconnect( oldComp, SIGNAL( matches( const QStringList& )), + this, SLOT( setCompletedItems( const QStringList& ))); + + if ( comp && hsig ) + connect( comp, SIGNAL( matches( const QStringList& )), + this, SLOT( setCompletedItems( const QStringList& ))); + + OCompletionBase::setCompletionObject( comp, hsig ); +} + +// QWidget::create() turns off mouse-Tracking which would break auto-hiding +void OLineEdit::create( WId id, bool initializeWindow, bool destroyOldWindow ) +{ + QLineEdit::create( id, initializeWindow, destroyOldWindow ); + //OCursor::setAutoHideCursor( this, true, true ); +} + +void OLineEdit::clear() +{ + setText( QString::null ); +} diff --git a/libopie2/qt3/opieui/olineedit.h b/libopie2/qt3/opieui/olineedit.h new file mode 100644 index 0000000..ecfca27 --- a/dev/null +++ b/libopie2/qt3/opieui/olineedit.h @@ -0,0 +1,498 @@ +/* + This file Copyright (C) 2003 Michael 'Mickey' Lauer <mickey@tm.informatik.uni-frankfurt.de> + is part of the Copyright (C) 2001 Carsten Pfeiffer <pfeiffer@kde.org>, Dawit Alemayehu <adawit@kde.org> + Opie Project Copyright (C) 1999 Preston Brown <pbrown@kde.org>, Patrick Ward <PAT_WARD@HP-USA-om5.om.hp.com> + Copyright (C) 1997 Sven Radej (sven.radej@iname.com) + =. + .=l. + .>+-= + _;:, .> :=|. This program is free software; you can +.> <`_, > . <= redistribute it and/or modify it under +:`=1 )Y*s>-.-- : the terms of the GNU Library General Public +.="- .-=="i, .._ License as published by the Free Software + - . .-<_> .<> Foundation; either version 2 of the License, + ._= =} : or (at your option) any later version. + .%`+i> _;_. + .i_,=:_. -<s. 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + +*/ + +#ifndef OLINEEDIT_H +#define OLINEEDIT_H + +/* QT */ + +#include <qlineedit.h> + +/* OPIE */ + +#include <opie2/ocompletion.h> +#include <opie2/ocompletionbase.h> + +class QPopupMenu; + +class OCompletionBox; +typedef QString KURL; //class KURL; + +/** + * An enhanced QLineEdit widget for inputting text. + * + * @sect Detail + * + * This widget inherits from @ref QLineEdit and implements the following + * additional functionalities: q completion object that provides both + * automatic and manual text completion as well as multiple match iteration + * features, configurable key-bindings to activate these features and a + * popup-menu item that can be used to allow the user to set text completion + * modes on the fly based on their preference. + * + * To support these new features OLineEdit also emits a few more + * additional signals. These are: @ref completion( const QString& ), + * textRotation( KeyBindingType ), and @ref returnPressed( const QString& ). + * The completion signal can be connected to a slot that will assist the + * user in filling out the remaining text. The text rotation signal is + * intended to be used to iterate through the list of all possible matches + * whenever there is more than one match for the entered text. The + * @p returnPressed( const QString& ) signals are the same as QLineEdit's + * except it provides the current text in the widget as its argument whenever + * appropriate. + * + * This widget by default creates a completion object when you invoke + * the @ref completionObject( bool ) member function for the first time or + * use @ref setCompletionObject( OCompletion*, bool ) to assign your own + * completion object. Additionally, to make this widget more functional, + * OLineEdit will by default handle the text rotation and completion + * events internally when a completion object is created through either one + * of the methods mentioned above. If you do not need this functionality, + * simply use @ref OCompletionBase::setHandleSignals( bool ) or set the + * boolean parameter in the above functions to FALSE. + * + * The default key-bindings for completion and rotation is determined + * from the global settings in @ref OStdAccel. These values, however, + * can be overriden locally by invoking @ref OCompletionBase::setKeyBinding(). + * The values can easily be reverted back to the default setting, by simply + * calling @ref useGlobalSettings(). An alternate method would be to default + * individual key-bindings by usning @ref setKeyBinding() with the default + * second argument. + * + * NOTE that if the @p EchoMode for this widget is set to something other + * than @p QLineEdit::Normal, the completion mode will always be defaulted + * to @ref PGlobalSettings::CompletionNone. This is done purposefully to guard + * against protected entries such as passwords being cached in @ref OCompletion's + * list. Hence, if the @p EchoMode is not @ref QLineEdit::Normal, the completion + * mode is automatically disabled. + * + * @sect Useage + * + * To enable the basic completion feature : + * + * <pre> + * OLineEdit *edit = new OLineEdit( this, "mywidget" ); + * OCompletion *comp = edit->completionObject(); + * // Fill the completion object with a list of possible matches + * QStringList list; + * list << "mickeyl@handhelds.org" << "mickey@tm.informatik.uni-frankfurt.de>" << "mickey@Vanille.de"; + * comp->setItems( list ); + * // Connect to the return pressed signal (optional) + * connect(edit,SIGNAL(returnPressed(const QString&)),comp,SLOT(addItem(const QString&)); + * </pre> + * + * To use a customized completion objects or your + * own completion object : + * + * <pre> + * OLineEdit *edit = new OLineEdit( this,"mywidget" ); + * KURLCompletion *comp = new KURLCompletion(); + * edit->setCompletionObject( comp ); + * // Connect to the return pressed signal - optional + * connect(edit,SIGNAL(returnPressed(const QString&)),comp,SLOT(addItem(const QString&)); + * </pre> + * + * Note that you have to either delete the allocated completion object + * when you don't need it anymore, or call + * setAutoDeleteCompletionObject( true ); + * + * @sect Miscellaneous function calls : + * + * <pre> + * // Tell the widget not to handle completion and + * // iteration internally. + * edit->setHandleSignals( false ); + * // Set your own completion key for manual completions. + * edit->setKeyBinding( OCompletionBase::TextCompletion, Qt::End ); + * // Hide the context (popup) menu + * edit->setContextMenuEnabled( false ); + * // Temporarly disable signal emitions + * // (both completion & iteration signals) + * edit->disableSignals(); + * // Default the key-bindings to system settings. + * edit->useGlobalKeyBindings(); + * </pre> + * + * @short An enhanced single line input widget. + * @author Dawit Alemayehu <adawit@kde.org> + * @author Opie adaption by Michael Lauer <mickey@tm.informatik.uni-frankfurt.de> + */ + +class OLineEdit : public QLineEdit, public OCompletionBase +{ + friend class OComboBox; + + Q_OBJECT + Q_PROPERTY( bool contextMenuEnabled READ isContextMenuEnabled WRITE setContextMenuEnabled ) + Q_PROPERTY( bool urlDropsEnabled READ isURLDropsEnabled WRITE setURLDropsEnabled ) + +public: + + /** + * Constructs a OLineEdit object with a default text, a parent, + * and a name. + * + * @param string Text to be shown in the edit widget. + * @param parent The parent object of this widget. + * @param name the name of this widget + */ + OLineEdit( const QString &string, QWidget *parent, const char *name = 0 ); + + /** + * Constructs a OLineEdit object with a parent and a name. + * + * @param string Text to be shown in the edit widget. + * @param parent The parent object of this widget. + * @param name The name of this widget. + */ + OLineEdit ( QWidget *parent=0, const char *name=0 ); + + /** + * Destructor. + */ + virtual ~OLineEdit (); + + /** + * Sets @p url into the lineedit. It uses @ref KURL::prettyURL() so + * that the url is properly decoded for displaying. + */ + void setURL( const KURL& url ); + + /** + * Puts the text cursor at the end of the string. + * + * This method is deprecated. Use @ref QLineEdit::end() + * instead. + * + * @deprecated + * @ref QLineEdit::end() + */ + void cursorAtEnd() { end( false ); } + + /** + * Re-implemented from @ref OCompletionBase for internal reasons. + * + * This function is re-implemented in order to make sure that + * the EchoMode is acceptable before we set the completion mode. + * + * See @ref OCompletionBase::setCompletionMode + */ + virtual void setCompletionMode( OGlobalSettings::Completion mode ); + + /** + * Enables/disables the popup (context) menu. + * + * Note that when this function is invoked with its argument + * set to @p true, then both the context menu and the completion + * menu item are enabled. If you do not want to the completion + * item to be visible simply invoke @ref hideModechanger() right + * after calling this method. Also by default, the context + * menu is automatically created if this widget is editable. Thus + * you need to call this function with the argument set to false + * if you do not want this behaviour. + * + * @param showMenu If @p true, show the context menu. + */ + virtual void setContextMenuEnabled( bool showMenu ) { m_bEnableMenu = showMenu; } + + /** + * Returns @p true when the context menu is enabled. + */ + bool isContextMenuEnabled() const { return m_bEnableMenu; } + + /** + * Enables/Disables handling of URL drops. If enabled and the user + * drops an URL, the decoded URL will be inserted. Otherwise the default + * behaviour of QLineEdit is used, which inserts the encoded URL. + * + * @param enable If @p true, insert decoded URLs + */ + void setURLDropsEnabled( bool enable ); + + /** + * Returns @p true when decoded URL drops are enabled + */ + bool isURLDropsEnabled() const; + + /** + * By default, OLineEdit recognizes @p Key_Return and @p Key_Enter and emits + * the @ref returnPressed() signals, but it also lets the event pass, + * for example causing a dialog's default-button to be called. + * + * Call this method with @p trap = @p true to make @p OLineEdit stop these + * events. The signals will still be emitted of course. + * + * @see trapReturnKey() + */ + void setTrapReturnKey( bool trap ); + + /** + * @returns @p true if keyevents of @p Key_Return or + * @p Key_Enter will be stopped or if they will be propagated. + * + * @see setTrapReturnKey () + */ + bool trapReturnKey() const; + + /** + * Re-implemented for internal reasons. API not affected. + * + * @reimplemented + */ + virtual bool eventFilter( QObject *, QEvent * ); + + /** + * @returns the completion-box, that is used in completion mode + * @ref KGlobalSettings::CompletionPopup. + * This method will create a completion-box if none is there, yet. + * + * @param create Set this to false if you don't want the box to be created + * i.e. to test if it is available. + */ + OCompletionBox * completionBox( bool create = true ); + + /** + * Reimplemented for internal reasons, the API is not affected. + */ + virtual void setCompletionObject( OCompletion *, bool hsig = true ); + + +signals: + + /** + * Emitted when the user presses the return key. + * + * The argument is the current text. Note that this + * signal is @em not emitted if the widget's @p EchoMode is set to + * @ref QLineEdit::EchoMode. + */ + void returnPressed( const QString& ); + + /** + * Emitted when the completion key is pressed. + * + * Please note that this signal is @em not emitted if the + * completion mode is set to @p CompletionNone or @p EchoMode is + * @em normal. + */ + void completion( const QString& ); + + /** + * Emitted when the shortcut for substring completion is pressed. + */ + void substringCompletion( const QString& ); + + /** + * Emitted when the text rotation key-bindings are pressed. + * + * The argument indicates which key-binding was pressed. + * In OLineEdit's case this can be either one of two values: + * @ref PrevCompletionMatch or @ref NextCompletionMatch. See + * @ref OCompletionBase::setKeyBinding for details. + * + * Note that this signal is @em not emitted if the completion + * mode is set to @p KGlobalSettings::CompletionNone or @p echoMode() is @em not normal. + */ + void textRotation( OCompletionBase::KeyBindingType ); + + /** + * Emitted when the user changed the completion mode by using the + * popupmenu. + */ + void completionModeChanged( OGlobalSettings::Completion ); + + /** + * Emitted before the context menu is displayed. + * + * The signal allows you to add your own entries into the + * the context menu that is created on demand. + * + * NOTE: Do not store the pointer to the QPopupMenu + * provided through since it is created and deleted + * on demand. + * + * @param the context menu about to be displayed + */ + void aboutToShowContextMenu( QPopupMenu* ); + +public slots: + + /** + * Re-implemented for internal reasons. API not changed. + */ + virtual void setReadOnly(bool); + + /** + * Iterates through all possible matches of the completed text or + * the history list. + * + * This function simply iterates over all possible matches in case + * multimple matches are found as a result of a text completion request. + * It will have no effect if only a single match is found. + * + * @param type The key-binding invoked. + */ + void rotateText( OCompletionBase::KeyBindingType /* type */ ); + + /** + * See @ref OCompletionBase::setCompletedText. + */ + virtual void setCompletedText( const QString& ); + + /** + * Sets @p items into the completion-box if @ref completionMode() is + * CompletionPopup. The popup will be shown immediately. + */ + void setCompletedItems( const QStringList& items ); + + /** + * Reimplemented to workaround a buggy QLineEdit::clear() + * (changing the clipboard to the text we just had in the lineedit) + */ + virtual void clear(); + +protected slots: + + /** + * Completes the remaining text with a matching one from + * a given list. + */ + virtual void makeCompletion( const QString& ); + + /** + * @deprecated. Will be removed in the next major release! + */ + void slotAboutToShow() {} + + /** + * @deprecated. Will be removed in the next major release! + */ + void slotCancelled() {} + +protected: + + /** + * Re-implemented for internal reasons. API not affected. + * + * See @ref QLineEdit::keyPressEvent(). + */ + virtual void keyPressEvent( QKeyEvent * ); + + /** + * Re-implemented for internal reasons. API not affected. + * + * See @ref QLineEdit::mousePressEvent(). + */ + virtual void mousePressEvent( QMouseEvent * ); + + /** + * Re-implemented for internal reasons. API not affected. + * + * See @ref QWidget::mouseDoubleClickEvent(). + */ + virtual void mouseDoubleClickEvent( QMouseEvent * ); + + /** + * Re-implemented for internal reasons. API not affected. + * + * See @ref QLineEdit::createPopupMenu(). + */ + virtual QPopupMenu *createPopupMenu(); + + /** + * Re-implemented to handle URI drops. + * + * See @ref QLineEdit::dropEvent(). + */ + //virtual void dropEvent( QDropEvent * ); + + /* + * This function simply sets the lineedit text and + * highlights the text appropriately if the boolean + * value is set to true. + * + * @param text + * @param marked + */ + virtual void setCompletedText( const QString& /*text*/, bool /*marked*/ ); + + /** + * Reimplemented for internal reasons, the API is not affected. + */ + virtual void create( WId = 0, bool initializeWindow = true, + bool destroyOldWindow = true ); + +private slots: + void completionMenuActivated( int id ); + void tripleClickTimeout(); // resets possibleTripleClick + +private: + // Constants that represent the ID's of the popup menu. + // TODO: See if we can replace this mess with KActionMenu + // in the future though it's working lovely. + enum MenuID { + Default = 42, + NoCompletion, + AutoCompletion, + ShellCompletion, + PopupCompletion, + SemiAutoCompletion + }; + + /** + * Initializes variables. Called from the constructors. + */ + void init(); + + /** + * Creates the completion box + */ + void makeCompletionBox(); + + /** + * Checks whether we should/should not consume a key used as + * an accelerator. + */ + //bool overrideAccel (const QKeyEvent* e); + + bool m_bEnableMenu; + + bool possibleTripleClick; // set in mousePressEvent, deleted in tripleClickTimeout + +protected: + //virtual void virtual_hook( int id, void* data ); +private: + class OLineEditPrivate; + OLineEditPrivate *d; +}; + +#endif |