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/opieui | |
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/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 |
10 files changed, 5656 insertions, 0 deletions
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 |