Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

ocompletion.cpp

Go to the documentation of this file.
00001 /*
00002                              This file is part of the Opie Project
00003                              Originally part of the KDE Project
00004                              Copyright (C) 1999,2000,2001 Carsten Pfeiffer <pfeiffer@kde.org>
00005               =.
00006             .=l.
00007            .>+-=
00008  _;:,     .>    :=|.         This program is free software; you can
00009 .> <`_,   >  .   <=          redistribute it and/or  modify it under
00010 :`=1 )Y*s>-.--   :           the terms of the GNU Library General Public
00011 .="- .-=="i,     .._         License as published by the Free Software
00012  - .   .-<_>     .<>         Foundation; either version 2 of the License,
00013      ._= =}       :          or (at your option) any later version.
00014     .%`+i>       _;_.
00015     .i_,=:_.      -<s.       This program is distributed in the hope that
00016      +  .  -:.       =       it will be useful,  but WITHOUT ANY WARRANTY;
00017     : ..    .:,     . . .    without even the implied warranty of
00018     =_        +     =;=|`    MERCHANTABILITY or FITNESS FOR A
00019   _.=:.       :    :=>`:     PARTICULAR PURPOSE. See the GNU
00020 ..}^=.=       =       ;      Library General Public License for more
00021 ++=   -.     .`     .:       details.
00022  :     =  ...= . :.=-
00023  -.   .:....=;==+<;          You should have received a copy of the GNU
00024   -_. . .   )=.  =           Library General Public License along with
00025     --        :-=`           this library; see the file COPYING.LIB.
00026                              If not, write to the Free Software Foundation,
00027                              Inc., 59 Temple Place - Suite 330,
00028                              Boston, MA 02111-1307, USA.
00029 */
00030 
00031 #include <opie2/ocompletion.h>
00032 
00033 class OCompTreeNode;
00034 
00035 /**************************************************************************************************/
00036 /* OCompTreeNodeList
00037 /**************************************************************************************************/
00038 
00039 class OCompTreeNodeList
00040 {
00041 public:
00042     OCompTreeNodeList() : first(0), last(0), m_count(0) {}
00043     OCompTreeNode *begin() const { return first; }
00044     OCompTreeNode *end() const { return last; }
00045 
00046     OCompTreeNode *at(uint index) const;
00047     void append(OCompTreeNode *item);
00048     void prepend(OCompTreeNode *item);
00049     void insert(OCompTreeNode *after, OCompTreeNode *item);
00050     OCompTreeNode *remove(OCompTreeNode *item);
00051     uint count() const { return m_count; }
00052 
00053 private:
00054     OCompTreeNode *first, *last;
00055     uint m_count;
00056 };
00057 
00058 typedef OCompTreeNodeList OCompTreeChildren;
00059 typedef OSortableValueList<QString> OCompletionMatchesList;
00060 
00091 /**************************************************************************************************/
00092 /* OCompTreeNode
00093 /**************************************************************************************************/
00094 
00095 class OCompTreeNode : public QChar
00096 {
00097 public:
00098     OCompTreeNode():QChar(), myWeight(0) {}
00099     OCompTreeNode( const QChar& ch, uint weight = 0 ):QChar( ch ), myWeight( weight ) {}
00100     ~OCompTreeNode();
00101 
00102     // FIXME: Do we need this for Opie? [see also the static ZoneAllocater below]
00103     //void * operator new( size_t s ) {
00104     //  return alloc.allocate( s );
00105     //}
00106     //void operator delete( void * s ) {
00107     //  alloc.deallocate( s );
00108     //}
00109 
00110     // Returns a child of this node matching ch, if available.
00111     // Otherwise, returns 0L
00112     inline OCompTreeNode * find( const QChar& ch ) const {
00113       OCompTreeNode * cur = myChildren.begin();
00114       while (cur && (*cur != ch)) cur = cur->next;
00115       return cur;
00116     }
00117 
00118     OCompTreeNode * insert( const QChar&, bool sorted );
00119     void remove( const QString& );
00120 
00121     inline int childrenCount() const { return myChildren.count(); };
00122 
00123     // weighting
00124     inline void confirm() { myWeight++; };
00125     inline void confirm(uint w) { myWeight += w; };
00126     inline void decline() { myWeight--; };
00127     inline uint weight() const { return myWeight; };
00128 
00129     inline const OCompTreeChildren * children() const { return &myChildren; };
00130     inline const OCompTreeNode * childAt(int index) const { return myChildren.at(index); };
00131     inline const OCompTreeNode * firstChild() const { return myChildren.begin(); };
00132     inline const OCompTreeNode * lastChild()  const { return myChildren.end(); };
00133 
00134     /* We want to handle a list of OCompTreeNodes on our own, to not
00135        need to use QValueList<>.  And to make it even more fast we don't
00136        use an accessor, but just a public member.  */
00137     OCompTreeNode *next;
00138 
00139 private:
00140     uint myWeight;
00141     OCompTreeNodeList   myChildren;
00142     //static OZoneAllocator alloc; // FIXME: Do we need this for Opie?
00143 };
00144 
00145 /**************************************************************************************************/
00146 /* OCompletionMatchesWrapper
00147 /**************************************************************************************************/
00148 
00149 class OCompletionMatchesWrapper
00150 {
00151 public:
00152     OCompletionMatchesWrapper( bool sort = false )
00153         : sortedList( sort ? new OCompletionMatchesList : 0L ),
00154           dirty( false )
00155     {}
00156     ~OCompletionMatchesWrapper() {
00157         delete sortedList;
00158     }
00159 
00160     void setSorting( bool sort ) {
00161         if ( sort && !sortedList )
00162             sortedList = new OCompletionMatchesList;
00163         else if ( !sort ) {
00164             delete sortedList;
00165             sortedList = 0L;
00166         }
00167         stringList.clear();
00168         dirty = false;
00169     }
00170 
00171     bool sorting() const {
00172         return sortedList != 0L;
00173     }
00174 
00175     void append( int i, const QString& string ) {
00176         if ( sortedList )
00177             sortedList->insert( i, string );
00178         else
00179             stringList.append( string );
00180         dirty = true;
00181     }
00182 
00183     void clear() {
00184         if ( sortedList )
00185             sortedList->clear();
00186         stringList.clear();
00187         dirty = false;
00188     }
00189 
00190     uint count() const {
00191         if ( sortedList )
00192             return sortedList->count();
00193         return stringList.count();
00194     }
00195 
00196     bool isEmpty() const {
00197         return count() == 0;
00198     }
00199 
00200     QString first() const {
00201         return list().first();
00202     }
00203 
00204     QString last() const {
00205         return list().last();
00206     }
00207 
00208     QStringList list() const;
00209 
00210     mutable QStringList stringList;
00211     OCompletionMatchesList *sortedList;
00212     mutable bool dirty;
00213 };
00214 
00215 /**************************************************************************************************/
00216 /* OCompletionPrivate
00217 /**************************************************************************************************/
00218 
00219 class OCompletionPrivate
00220 {
00221 public:
00222     // not a member to avoid #including kcompletion_private.h from kcompletion.h
00223     // list used for nextMatch() and previousMatch()
00224     OCompletionMatchesWrapper matches;
00225 };
00226 
00227 /**************************************************************************************************/
00228 /* OCompletion
00229 /**************************************************************************************************/
00230 
00231 OCompletion::OCompletion()
00232 {
00233     d = new OCompletionPrivate;
00234 
00235     myCompletionMode = OGlobalSettings::completionMode();
00236     myTreeRoot = new OCompTreeNode;
00237     myBeep = true;
00238     myIgnoreCase = false;
00239     myHasMultipleMatches = false;
00240     myRotationIndex = 0;
00241     setOrder( Insertion );
00242 }
00243 
00244 
00245 OCompletion::~OCompletion()
00246 {
00247     delete d;
00248     delete myTreeRoot;
00249 }
00250 
00251 
00252 void OCompletion::setOrder( CompOrder order )
00253 {
00254     myOrder = order;
00255     d->matches.setSorting( order == Weighted );
00256 }
00257 
00258 
00259 void OCompletion::setIgnoreCase( bool ignoreCase )
00260 {
00261     myIgnoreCase = ignoreCase;
00262 }
00263 
00264 
00265 void OCompletion::setItems( const QStringList& items )
00266 {
00267     clear();
00268     insertItems( items );
00269 }
00270 
00271 
00272 void OCompletion::insertItems( const QStringList& items )
00273 {
00274     bool weighted = (myOrder == Weighted);
00275     QStringList::ConstIterator it;
00276     if ( weighted ) { // determine weight
00277         for ( it = items.begin(); it != items.end(); ++it ) addWeightedItem( *it );
00278     }
00279     else {
00280         for ( it = items.begin(); it != items.end(); ++it ) addItem( *it, 0 );
00281     }
00282 }
00283 
00284 
00285 QStringList OCompletion::items() const
00286 {
00287     OCompletionMatchesWrapper list; // unsorted
00288     bool addWeight = (myOrder == Weighted);
00289     extractStringsFromNode( myTreeRoot, QString::null, &list, addWeight );
00290 
00291     return list.list();
00292 }
00293 
00294 
00295 void OCompletion::addItem( const QString& item )
00296 {
00297     d->matches.clear();
00298     myRotationIndex = 0;
00299     myLastString = QString::null;
00300 
00301     addItem( item, 0 );
00302 }
00303 
00304 
00305 void OCompletion::addItem( const QString& item, uint weight )
00306 {
00307     if ( item.isEmpty() ) return;
00308 
00309     OCompTreeNode *node = myTreeRoot;
00310     uint len = item.length();
00311 
00312     bool sorted = (myOrder == Sorted);
00313     bool weighted = ((myOrder == Weighted) && weight > 1);
00314 
00315     // knowing the weight of an item, we simply add this weight to all of its
00316     // nodes.
00317 
00318     for ( uint i = 0; i < len; i++ ) {
00319         node = node->insert( item.at(i), sorted );
00320         if ( weighted ) node->confirm( weight -1 ); // node->insert() sets weighting to 1
00321     }
00322 
00323     // add 0x0-item as delimiter with evtl. weight
00324     node = node->insert( 0x0, true );
00325     if ( weighted )
00326         node->confirm( weight -1 );
00327     //qDebug( "OCompletion: added: %s (%i)", item.latin1(), node->weight());
00328 }
00329 
00330 
00331 void OCompletion::addWeightedItem( const QString& item )
00332 {
00333     if ( myOrder != Weighted ) {
00334         addItem( item, 0 );
00335         return;
00336     }
00337 
00338     uint len = item.length();
00339     uint weight = 0;
00340 
00341     // find out the weighting of this item (appended to the string as ":num")
00342     int index = item.findRev(':');
00343     if ( index > 0 ) {
00344         bool ok;
00345         weight = item.mid( index + 1 ).toUInt( &ok );
00346         if ( !ok )
00347             weight = 0;
00348 
00349         len = index; // only insert until the ':'
00350     }
00351 
00352     addItem( item.left( len ), weight );
00353     return;
00354 }
00355 
00356 
00357 void OCompletion::removeItem( const QString& item )
00358 {
00359     d->matches.clear();
00360     myRotationIndex = 0;
00361     myLastString = QString::null;
00362 
00363     myTreeRoot->remove( item );
00364 }
00365 
00366 
00367 void OCompletion::clear()
00368 {
00369     d->matches.clear();
00370     myRotationIndex = 0;
00371     myLastString = QString::null;
00372 
00373     delete myTreeRoot;
00374     myTreeRoot = new OCompTreeNode;
00375 }
00376 
00377 
00378 QString OCompletion::makeCompletion( const QString& string )
00379 {
00380     if ( myCompletionMode == OGlobalSettings::CompletionNone )
00381         return QString::null;
00382 
00383     //qDebug( "OCompletion: completing: %s", string );
00384 
00385     d->matches.clear();
00386     myRotationIndex = 0;
00387     myHasMultipleMatches = false;
00388     myLastMatch = myCurrentMatch;
00389 
00390     // in Shell-completion-mode, emit all matches when we get the same
00391     // complete-string twice
00392     if ( myCompletionMode == OGlobalSettings::CompletionShell &&
00393         string == myLastString ) {
00394         // Don't use d->matches since calling postProcessMatches()
00395         // on d->matches here would interfere with call to
00396         // postProcessMatch() during rotation
00397 
00398         findAllCompletions( string, &d->matches, myHasMultipleMatches );
00399             QStringList l = d->matches.list();
00400         postProcessMatches( &l );
00401         emit matches( l );
00402 
00403         if ( l.isEmpty() )
00404             doBeep( NoMatch );
00405 
00406         return QString::null;
00407     }
00408 
00409     QString completion;
00410     // in case-insensitive popup mode, we search all completions at once
00411     if ( myCompletionMode == OGlobalSettings::CompletionPopup ||
00412          myCompletionMode == OGlobalSettings::CompletionPopupAuto ) {
00413         findAllCompletions( string, &d->matches, myHasMultipleMatches );
00414         if ( !d->matches.isEmpty() )
00415             completion = d->matches.first();
00416     }
00417     else
00418         completion = findCompletion( string );
00419 
00420     if ( myHasMultipleMatches )
00421         emit multipleMatches();
00422 
00423     myLastString = string;
00424     myCurrentMatch = completion;
00425 
00426     postProcessMatch( &completion );
00427 
00428     if ( !string.isEmpty() ) { // only emit match when string != ""
00429         //qDebug( "OCompletion: Match: %s", completion );
00430         emit match( completion );
00431     }
00432 
00433     if ( completion.isNull() )
00434         doBeep( NoMatch );
00435 
00436     return completion;
00437 }
00438 
00439 QStringList OCompletion::substringCompletion( const QString& string ) const
00440 {
00441     // get all items in the tree, eventually in sorted order
00442     bool sorted = (myOrder == Weighted);
00443     OCompletionMatchesWrapper allItems( sorted );
00444     extractStringsFromNode( myTreeRoot, QString::null, &allItems, false );
00445 
00446     QStringList list = allItems.list();
00447 
00448     // subStringMatches is invoked manually, via a shortcut, so we should
00449     // beep here, if necessary.
00450     if ( list.isEmpty() ) {
00451         doBeep( NoMatch );
00452         return list;
00453     }
00454 
00455     if ( string.isEmpty() ) { // shortcut
00456         postProcessMatches( &list );
00457         return list;
00458     }
00459 
00460     QStringList matches;
00461     QStringList::ConstIterator it = list.begin();
00462 
00463     for( ; it != list.end(); ++it ) {
00464         QString item = *it;
00465         if ( item.find( string, 0, false ) != -1 ) { // always case insensitive
00466             postProcessMatch( &item );
00467             matches.append( item );
00468         }
00469     }
00470 
00471     if ( matches.isEmpty() )
00472         doBeep( NoMatch );
00473 
00474     return matches;
00475 }
00476 
00477 
00478 void OCompletion::setCompletionMode( OGlobalSettings::Completion mode )
00479 {
00480     myCompletionMode = mode;
00481 }
00482 
00483 
00484 QStringList OCompletion::allMatches()
00485 {
00486     // Don't use d->matches since calling postProcessMatches()
00487     // on d->matches here would interfere with call to
00488     // postProcessMatch() during rotation
00489     OCompletionMatchesWrapper matches( myOrder == Weighted );
00490     bool dummy;
00491     findAllCompletions( myLastString, &matches, dummy );
00492     QStringList l = matches.list();
00493     postProcessMatches( &l );
00494     return l;
00495 }
00496 
00497 
00498 OCompletionMatches OCompletion::allWeightedMatches()
00499 {
00500     // Don't use d->matches since calling postProcessMatches()
00501     // on d->matches here would interfere with call to
00502     // postProcessMatch() during rotation
00503     OCompletionMatchesWrapper matches( myOrder == Weighted );
00504     bool dummy;
00505     findAllCompletions( myLastString, &matches, dummy );
00506     OCompletionMatches ret( matches );
00507     postProcessMatches( &ret );
00508     return ret;
00509 }
00510 
00511 QStringList OCompletion::allMatches( const QString &string )
00512 {
00513     OCompletionMatchesWrapper matches( myOrder == Weighted );
00514     bool dummy;
00515     findAllCompletions( string, &matches, dummy );
00516     QStringList l = matches.list();
00517     postProcessMatches( &l );
00518     return l;
00519 }
00520 
00521 OCompletionMatches OCompletion::allWeightedMatches( const QString &string )
00522 {
00523     OCompletionMatchesWrapper matches( myOrder == Weighted );
00524     bool dummy;
00525     findAllCompletions( string, &matches, dummy );
00526     OCompletionMatches ret( matches );
00527     postProcessMatches( &ret );
00528     return ret;
00529 }
00530 
00533 
00534 
00535 QString OCompletion::nextMatch()
00536 {
00537     QString completion;
00538     myLastMatch = myCurrentMatch;
00539 
00540     if ( d->matches.isEmpty() ) {
00541         findAllCompletions( myLastString, &d->matches, myHasMultipleMatches );
00542         completion = d->matches.first();
00543         myCurrentMatch = completion;
00544             myRotationIndex = 0;
00545         postProcessMatch( &completion );
00546         emit match( completion );
00547         return completion;
00548     }
00549 
00550     QStringList matches = d->matches.list();
00551     myLastMatch = matches[ myRotationIndex++ ];
00552 
00553     if ( myRotationIndex == matches.count() -1 )
00554         doBeep( Rotation ); // indicate last matching item -> rotating
00555 
00556     else if ( myRotationIndex == matches.count() )
00557         myRotationIndex = 0;
00558 
00559     completion = matches[ myRotationIndex ];
00560     myCurrentMatch = completion;
00561     postProcessMatch( &completion );
00562     emit match( completion );
00563     return completion;
00564 }
00565 
00566 
00567 
00568 QString OCompletion::previousMatch()
00569 {
00570     QString completion;
00571     myLastMatch = myCurrentMatch;
00572 
00573     if ( d->matches.isEmpty() ) {
00574         findAllCompletions( myLastString, &d->matches, myHasMultipleMatches );
00575         completion = d->matches.last();
00576         myCurrentMatch = completion;
00577             myRotationIndex = 0;
00578         postProcessMatch( &completion );
00579         emit match( completion );
00580         return completion;
00581     }
00582 
00583     QStringList matches = d->matches.list();
00584     myLastMatch = matches[ myRotationIndex ];
00585     if ( myRotationIndex == 1 )
00586         doBeep( Rotation ); // indicate first item -> rotating
00587 
00588     else if ( myRotationIndex == 0 )
00589         myRotationIndex = matches.count();
00590 
00591     myRotationIndex--;
00592 
00593     completion = matches[ myRotationIndex ];
00594     myCurrentMatch = completion;
00595     postProcessMatch( &completion );
00596     emit match( completion );
00597     return completion;
00598 }
00599 
00600 
00601 
00602 // tries to complete "string" from the tree-root
00603 QString OCompletion::findCompletion( const QString& string )
00604 {
00605     QChar ch;
00606     QString completion;
00607     const OCompTreeNode *node = myTreeRoot;
00608 
00609     // start at the tree-root and try to find the search-string
00610     for( uint i = 0; i < string.length(); i++ ) {
00611         ch = string.at( i );
00612         node = node->find( ch );
00613 
00614         if ( node )
00615             completion += ch;
00616         else
00617             return QString::null; // no completion
00618     }
00619 
00620     // Now we have the last node of the to be completed string.
00621     // Follow it as long as it has exactly one child (= longest possible
00622     // completion)
00623 
00624     while ( node->childrenCount() == 1 ) {
00625         node = node->firstChild();
00626         if ( !node->isNull() )
00627             completion += *node;
00628     }
00629     // if multiple matches and auto-completion mode
00630     // -> find the first complete match
00631     if ( node && node->childrenCount() > 1 ) {
00632         myHasMultipleMatches = true;
00633 
00634         if ( myCompletionMode == OGlobalSettings::CompletionAuto ) {
00635             myRotationIndex = 1;
00636             if (myOrder != Weighted) {
00637                 while ( (node = node->firstChild()) ) {
00638                     if ( !node->isNull() )
00639                     completion += *node;
00640                         else
00641                     break;
00642                     }
00643                 }
00644                 else {
00645                 // don't just find the "first" match, but the one with the
00646                 // highest priority
00647 
00648                 const OCompTreeNode* temp_node = 0L;
00649                 while(1) {
00650                     int count = node->childrenCount();
00651                     temp_node = node->firstChild();
00652                     uint weight = temp_node->weight();
00653                     const OCompTreeNode* hit = temp_node;
00654                     for( int i = 1; i < count; i++ ) {
00655                     temp_node = node->childAt(i);
00656                     if( temp_node->weight() > weight ) {
00657                         hit = temp_node;
00658                         weight = hit->weight();
00659                     }
00660                     }
00661                     // 0x0 has the highest priority -> we have the best match
00662                     if ( hit->isNull() )
00663                     break;
00664 
00665                     node = hit;
00666                     completion += *node;
00667                     }
00668                 }
00669             }
00670 
00671         else
00672             doBeep( PartialMatch ); // partial match -> beep
00673     }
00674 
00675     return completion;
00676 }
00677 
00678 
00679 void OCompletion::findAllCompletions(const QString& string,
00680                                      OCompletionMatchesWrapper *matches,
00681                                      bool& hasMultipleMatches) const
00682 {
00683     //qDebug( "OCompletion: finding all completions for %s", (const char*) string );
00684 
00685     if ( string.isEmpty() )
00686         return;
00687 
00688     if ( myIgnoreCase ) { // case insensitive completion
00689         extractStringsFromNodeCI( myTreeRoot, QString::null, string, matches );
00690         hasMultipleMatches = (matches->count() > 1);
00691         return;
00692     }
00693 
00694     QChar ch;
00695     QString completion;
00696     const OCompTreeNode *node = myTreeRoot;
00697 
00698     // start at the tree-root and try to find the search-string
00699     for( uint i = 0; i < string.length(); i++ ) {
00700         ch = string.at( i );
00701         node = node->find( ch );
00702 
00703         if ( node )
00704             completion += ch;
00705         else
00706             return; // no completion -> return empty list
00707     }
00708 
00709     // Now we have the last node of the to be completed string.
00710     // Follow it as long as it has exactly one child (= longest possible
00711     // completion)
00712 
00713     while ( node->childrenCount() == 1 ) {
00714         node = node->firstChild();
00715         if ( !node->isNull() )
00716             completion += *node;
00717         // kdDebug() << completion << node->latin1();
00718     }
00719 
00720 
00721     // there is just one single match)
00722     if ( node->childrenCount() == 0 )
00723         matches->append( node->weight(), completion );
00724 
00725     else {
00726         // node has more than one child
00727         // -> recursively find all remaining completions
00728         hasMultipleMatches = true;
00729         extractStringsFromNode( node, completion, matches );
00730     }
00731 }
00732 
00733 
00734 void OCompletion::extractStringsFromNode( const OCompTreeNode *node,
00735                         const QString& beginning,
00736                         OCompletionMatchesWrapper *matches,
00737                         bool addWeight ) const
00738 {
00739     if ( !node || !matches ) return;
00740 
00741     // kDebug() << "Beginning: " << beginning << endl;
00742     const OCompTreeChildren *list = node->children();
00743     QString string;
00744     QString w;
00745 
00746     // loop thru all children
00747     for ( OCompTreeNode *cur = list->begin(); cur ; cur = cur->next) {
00748         string = beginning;
00749         node = cur;
00750         if ( !node->isNull() )
00751             string += *node;
00752 
00753         while ( node && node->childrenCount() == 1 ) {
00754             node = node->firstChild();
00755             if ( node->isNull() ) break;
00756             string += *node;
00757         }
00758 
00759         if ( node && node->isNull() ) { // we found a leaf
00760             if ( addWeight ) {
00761                 // add ":num" to the string to store the weighting
00762                 string += ':';
00763                 w.setNum( node->weight() );
00764                 string.append( w );
00765                 }
00766             matches->append( node->weight(), string );
00767         }
00768 
00769         // recursively find all other strings.
00770         if ( node && node->childrenCount() > 1 )
00771             extractStringsFromNode( node, string, matches, addWeight );
00772     }
00773 }
00774 
00775 void OCompletion::extractStringsFromNodeCI( const OCompTreeNode *node,
00776                                             const QString& beginning,
00777                                             const QString& restString,
00778                                       OCompletionMatchesWrapper *matches ) const
00779 {
00780     if ( restString.isEmpty() ) {
00781         extractStringsFromNode( node, beginning, matches, false /*noweight*/ );
00782         return;
00783     }
00784 
00785     QChar ch1 = restString.at(0);
00786     QString newRest = restString.mid(1);
00787     OCompTreeNode *child1, *child2;
00788 
00789     child1 = node->find( ch1 ); // the correct match
00790     if ( child1 )
00791         extractStringsFromNodeCI( child1, beginning + *child1, newRest,
00792                                   matches );
00793 
00794     // append the case insensitive matches, if available
00795     if ( ch1.isLetter() ) {
00796         // find out if we have to lower or upper it. Is there a better way?
00797         QChar ch2 = ch1.lower();
00798         if ( ch1 == ch2 )
00799             ch2 = ch1.upper();
00800         if ( ch1 != ch2 ) {
00801             child2 = node->find( ch2 );
00802             if ( child2 )
00803                 extractStringsFromNodeCI( child2, beginning + *child2, newRest,
00804                                           matches );
00805         }
00806     }
00807 }
00808 
00809 // FIXME: Revise this for Opie?
00810 
00811 void OCompletion::doBeep( BeepMode mode ) const
00812 {
00813     if ( !myBeep ) return;
00814 
00815     QString text, event;
00816 
00817     switch ( mode ) {
00818     case Rotation:
00819         event = QString::fromLatin1("Textcompletion: rotation");
00820         text = tr("You reached the end of the list\nof matching items.\n");
00821         break;
00822     case PartialMatch:
00823         if ( myCompletionMode == OGlobalSettings::CompletionShell ||
00824             myCompletionMode == OGlobalSettings::CompletionMan ) {
00825             event = QString::fromLatin1("Textcompletion: partial match");
00826             text = tr("The completion is ambiguous, more than one\nmatch is available.\n");
00827         }
00828         break;
00829     case NoMatch:
00830         if ( myCompletionMode == OGlobalSettings::CompletionShell ) {
00831             event = QString::fromLatin1("Textcompletion: no match");
00832             text = tr("There is no matching item available.\n");
00833         }
00834         break;
00835     }
00836 
00837     //if ( !text.isEmpty() )
00838         //ONotifyClient::event( event, text ); // FIXME: Revise for Opie?
00839 }
00840 
00841 // Implements the tree. Every node is a QChar and has a list of children, which
00842 // are Nodes as well.
00843 // QChar( 0x0 ) is used as the delimiter of a string; the last child of each
00844 // inserted string is 0x0.
00845 
00846 OCompTreeNode::~OCompTreeNode()
00847 {
00848     // delete all children
00849     OCompTreeNode *cur = myChildren.begin();
00850     while (cur) {
00851         OCompTreeNode * next = cur->next;
00852         delete myChildren.remove(cur);
00853         cur = next;
00854     }
00855 }
00856 
00857 
00858 // Adds a child-node "ch" to this node. If such a node is already existant,
00859 // it will not be created. Returns the new/existing node.
00860 OCompTreeNode * OCompTreeNode::insert( const QChar& ch, bool sorted )
00861 {
00862     OCompTreeNode *child = find( ch );
00863     if ( !child ) {
00864         child = new OCompTreeNode( ch );
00865 
00866         // FIXME, first (slow) sorted insertion implementation
00867         if ( sorted ) {
00868             OCompTreeNode * prev = 0;
00869             OCompTreeNode * cur = myChildren.begin();
00870             while ( cur ) {
00871                 if ( ch > *cur ) {
00872                     prev = cur;
00873                     cur = cur->next;
00874                 } else
00875                     break;
00876             }
00877             if (prev)
00878                 myChildren.insert( prev, child );
00879             else
00880                 myChildren.prepend(child);
00881         }
00882 
00883         else
00884             myChildren.append( child );
00885     }
00886 
00887     // implicit weighting: the more often an item is inserted, the higher
00888     // priority it gets.
00889     child->confirm();
00890 
00891     return child;
00892 }
00893 
00894 
00895 // Recursively removes a string from the tree (untested :-)
00896 void OCompTreeNode::remove( const QString& string )
00897 {
00898     OCompTreeNode *child = 0L;
00899 
00900     if ( string.isEmpty() ) {
00901         child = find( 0x0 );
00902         delete myChildren.remove( child );
00903         return;
00904     }
00905 
00906     QChar ch = string.at(0);
00907     child = find( ch );
00908     if ( child ) {
00909         child->remove( string.right( string.length() -1 ) );
00910         if ( child->myChildren.count() == 0 ) {
00911             delete myChildren.remove( child );
00912         }
00913     }
00914 }
00915 
00916 QStringList OCompletionMatchesWrapper::list() const {
00917     if ( sortedList && dirty ) {
00918         sortedList->sort();
00919         dirty = false;
00920 
00921         stringList.clear();
00922 
00923         // high weight == sorted last -> reverse the sorting here
00924         QValueListConstIterator<OSortableItem<QString> > it;
00925         for ( it = sortedList->begin(); it != sortedList->end(); ++it )
00926             stringList.prepend( (*it).value() );
00927     }
00928 
00929     return stringList;
00930 }
00931 
00932 OCompletionMatches::OCompletionMatches( bool sort_P )
00933     : _sorting( sort_P )
00934 {
00935 }
00936 
00937 OCompletionMatches::OCompletionMatches( const OCompletionMatchesWrapper& matches )
00938     : _sorting( matches.sorting())
00939 {
00940     if( matches.sortedList != 0L )
00941         OCompletionMatchesList::operator=( *matches.sortedList );
00942     else {
00943         QStringList l = matches.list();
00944         for( QStringList::ConstIterator it = l.begin();
00945              it != l.end();
00946              ++it )
00947             prepend( OSortableItem<QString, int>( 1, *it ) );
00948     }
00949 }
00950 
00951 OCompletionMatches::~OCompletionMatches()
00952 {
00953 }
00954 
00955 QStringList OCompletionMatches::list( bool sort_P ) const
00956 {
00957     if( _sorting && sort_P )
00958         const_cast< OCompletionMatches* >( this )->sort();
00959     QStringList stringList;
00960     // high weight == sorted last -> reverse the sorting here
00961     for ( ConstIterator it = begin(); it != end(); ++it )
00962         stringList.prepend( (*it).value() );
00963     return stringList;
00964 }
00965 
00966 void OCompletionMatches::removeDuplicates()
00967 {
00968     Iterator it1, it2;
00969     for ( it1 = begin(); it1 != end(); ++it1 ) {
00970         for ( (it2 = it1), ++it2; it2 != end();) {
00971             if( (*it1).value() == (*it2).value()) {
00972                 // use the max height
00973                 //(*it1).first = kMax( (*it1).index(), (*it2).index());
00974                 (*it1).first = (*it2).index() < (*it1).index() ? (*it1).index() : (*it2).index();
00975                 it2 = remove( it2 );
00976                 continue;
00977             }
00978             ++it2;
00979         }
00980     }
00981 }
00982 
00983 void OCompTreeNodeList::append(OCompTreeNode *item)
00984 {
00985     m_count++;
00986     if (!last) {
00987         last = item;
00988         last->next = 0;
00989         first = item;
00990         return;
00991     }
00992     last->next = item;
00993     item->next = 0;
00994     last = item;
00995 }
00996 
00997 void OCompTreeNodeList::prepend(OCompTreeNode *item)
00998 {
00999     m_count++;
01000     if (!last) {
01001         last = item;
01002         last->next = 0;
01003         first = item;
01004         return;
01005     }
01006     item->next = first;
01007     first = item;
01008 }
01009 
01010 void OCompTreeNodeList::insert(OCompTreeNode *after, OCompTreeNode *item)
01011 {
01012     if (!after) {
01013         append(item);
01014         return;
01015     }
01016 
01017     m_count++;
01018 
01019     item->next = after->next;
01020     after->next = item;
01021 
01022     if (after == last)
01023         last = item;
01024 }
01025 
01026 OCompTreeNode *OCompTreeNodeList::remove(OCompTreeNode *item)
01027 {
01028     if (!first || !item)
01029         return 0;
01030     OCompTreeNode *cur = 0;
01031 
01032     if (item == first)
01033         first = first->next;
01034     else {
01035         cur = first;
01036         while (cur && cur->next != item) cur = cur->next;
01037         if (!cur)
01038             return 0;
01039         cur->next = item->next;
01040     }
01041     if (item == last)
01042         last = cur;
01043     m_count--;
01044     return item;
01045 }
01046 
01047 OCompTreeNode *OCompTreeNodeList::at(uint index) const
01048 {
01049     OCompTreeNode *cur = first;
01050     while (index-- && cur) cur = cur->next;
01051     return cur;
01052 }
01053 
01054 // FIXME: Revise for Opie?
01055 //OZoneAllocator OCompTreeNode::alloc(8192);
01056 
01057 //void OCompletion::virtual_hook( int, void* )
01058 //{ /*BASE::virtual_hook( id, data );*/ }
01059 
01060 //void OCompletionBase::virtual_hook( int, void* )
01061 //{ /*BASE::virtual_hook( id, data );*/ }

Generated on Sat Nov 5 16:16:28 2005 for OPIE by  doxygen 1.4.2