1 // File: QtxListAction.cxx
2 // Author: Sergey TELKOV (Based on code by Eugene AKSENOV)
4 #include "QtxListAction.h"
13 #include <qpopupmenu.h>
14 #include <qtoolbutton.h>
15 #include <qobjectlist.h>
16 #include <qapplication.h>
18 static const char* list_arrow_icon[] = {
31 Class: QtxListAction::ToolButton
35 class QtxListAction::ToolButton : public QToolButton
38 ToolButton( QtxListAction*, QWidget* = 0, const char* = 0 );
39 virtual ~ToolButton();
41 virtual QSize sizeHint() const;
44 QtxListAction* myAction;
47 QtxListAction::ToolButton::ToolButton( QtxListAction* a, QWidget* parent, const char* name )
48 : QToolButton( parent, name ),
51 setIconSet( QPixmap( list_arrow_icon ) );
54 QtxListAction::ToolButton::~ToolButton()
57 myAction->controlDeleted( this );
60 QSize QtxListAction::ToolButton::sizeHint() const
62 QSize sz = iconSet().pixmap().size();
63 return QSize( sz.width() + 2, sz.height() + 2 );
72 Name: QtxListAction [public]
73 Desc: Constructs an list action with given parent and name. If toggle is true the
74 action will be a toggle action, otherwise it will be a command action.
77 QtxListAction::QtxListAction( QObject* parent, const char* name, bool toggle )
78 : QtxAction( parent, name, toggle ),
87 Name: QtxListAction [public]
88 Desc: This constructor creates an action with the following properties: the
89 description text, the icon or iconset icon, the menu text and keyboard
90 accelerator. It is a child of given parent and named specified name.
91 If toggle is true the action will be a toggle action, otherwise it will
95 QtxListAction::QtxListAction( const QString& text, const QIconSet& icon,
96 const QString& menuText, int accel,
97 QObject* parent, const char* name, bool toggle )
98 : QtxAction( text, icon, menuText, accel, parent, name, toggle ),
107 Name: QtxListAction [public]
108 Desc: This constructor creates an action with the following properties: the
109 description text, the menu text and keyboard accelerator. It is a child
110 of given parent and named specified name. If toggle is true the action
111 will be a toggle action, otherwise it will be a command action.
114 QtxListAction::QtxListAction( const QString& text, const QString& menuText,
115 int accel, QObject* parent, const char* name, bool toggle )
116 : QtxAction( text, menuText, accel, parent, name, toggle ),
125 Name: ~QtxListAction [virtual public]
129 QtxListAction::~QtxListAction()
134 Name: popupMode [public]
135 Desc: Returns popup mode. If popup mode "Item" (default) then action will
136 be added into popup menu as menu item. If popup mode "SubMenu" then
137 action will be added into popup menu as sub menu with list of items.
140 int QtxListAction::popupMode() const
146 Name: setPopupMode [public]
147 Desc: Set the popup mode. Popup mode define way in this action will be
148 added into popup menu. This function should be used before addTo.
151 void QtxListAction::setPopupMode( const int mode )
157 Name: addNames [public]
158 Desc: Fills the list of actions. Removes the old contents from
159 the list if 'clear' is true.
162 void QtxListAction::addNames( const QStringList& names, bool clear )
170 myFrame->addNames( names );
172 QStringList lst = myFrame->names();
173 for ( PopupsMap::Iterator pit = myPopups.begin(); pit != myPopups.end(); ++pit )
176 QPopupMenu* pm = (QPopupMenu*)pit.key();
177 for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it )
178 pit.data().popup->insertItem( *it, i++ );
179 pm->setItemEnabled( pit.data().id, isEnabled() && pit.data().popup->count() );
182 for ( ButtonsMap::Iterator bit = myButtons.begin(); bit != myButtons.end(); ++bit )
184 bit.data().drop->setEnabled( isEnabled() && !lst.isEmpty() );
185 bit.data().main->setEnabled( isEnabled() && !lst.isEmpty() );
190 Name: addTo [virtual public]
191 Desc: Adds this control to 'popup' or 'toolbar'.
194 bool QtxListAction::addTo( QWidget* w )
196 if ( myButtons.contains( w ) || myPopups.contains( w ) )
199 if ( !w->inherits( "QPopupMenu" ) || popupMode() != SubMenu )
200 if ( !QtxAction::addTo( w ) )
205 addedTo( (QWidget*)w->children()->getLast(), w );
208 if ( w->inherits( "QToolBar" ) )
210 Buttons& entry = myButtons[w];
211 QHBox* dropWrap = new QHBox( w );
212 entry.drop = new ToolButton( this, dropWrap, "qt_dockwidget_internal" );
214 entry.drop->setTextLabel( text() );
215 entry.drop->setToggleButton( true );
216 entry.drop->setAutoRaise( entry.main->autoRaise() );
218 entry.main->setEnabled( isEnabled() && !myFrame->names().isEmpty() );
219 entry.drop->setEnabled( isEnabled() && !myFrame->names().isEmpty() );
221 entry.main->installEventFilter( this );
222 entry.drop->installEventFilter( this );
224 QToolTip::add( entry.drop, toolTip(), myTipGroup, statusTip() );
226 connect( entry.drop, SIGNAL( toggled( bool ) ), this, SLOT( onExpand( bool ) ) );
228 else if ( w->inherits( "QPopupMenu" ) && popupMode() == SubMenu )
231 QPopupMenu* pm = (QPopupMenu*)w;
233 entry.popup = new QPopupMenu( pm );
234 entry.id = pm->insertItem( text(), entry.popup );
237 QStringList lst = myFrame->names();
238 for ( QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it )
240 int id = entry.popup->insertItem( *it );
241 entry.popup->setItemParameter( id, i++ );
243 pm->setItemEnabled( entry.id, isEnabled() && entry.popup->count() );
244 myPopups.insert( w, entry );
246 connect( entry.popup, SIGNAL( activated( int ) ), this, SLOT( onActivated( int ) ) );
250 connect( w, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
257 Name: addTo [virtual public]
258 Desc: Adds this control to 'popup' or 'toolbar'. Allow to specify index
259 for adding into 'popup'.
262 bool QtxListAction::addTo( QWidget* w, const int idx )
264 return QtxAction::addTo( w, idx );
268 Name: removeFrom [virtual public]
269 Desc: Removes this control from 'popup' or 'toolbar'.
272 bool QtxListAction::removeFrom( QWidget* w )
274 if ( !QtxAction::removeFrom( w ) )
277 if ( w->inherits( "QToolBar" ) )
282 if ( myButtons.contains( w ) )
284 Buttons& entry = myButtons[w];
286 if ( entry.drop->parent() && entry.drop->parent()->parent() == w )
287 delete entry.drop->parent();
291 myButtons.remove( w );
293 else if ( w->inherits( "QPopupMenu" ) )
294 myPopups.remove( w );
300 Name: setEnabled [virtual public slot]
301 Desc: Enables/disables this control.
304 void QtxListAction::setEnabled( bool enable )
306 QtxAction::setEnabled( enable );
308 bool isOn = enable && !myFrame->names().isEmpty();
310 for ( ButtonsMap::Iterator bit = myButtons.begin(); bit != myButtons.end(); ++bit )
312 bit.data().drop->setEnabled( isOn );
313 bit.data().main->setEnabled( isOn );
316 for ( PopupsMap::Iterator pit = myPopups.begin(); pit != myPopups.end(); ++pit )
318 QPopupMenu* cont = (QPopupMenu*)pit.key();
319 cont->setItemEnabled( pit.data().id, isOn );
324 Name: setMaxLines [public]
325 Desc: Sets max number of lines that list frame shows
326 without vertical scroll bar. Default value is 5.
329 void QtxListAction::setMaxLines( int nlines )
331 myFrame->setMaxLines( nlines );
335 Name: setMaxLineChars [public]
336 Desc: Sets max number of characters in a line which list frame shows
337 without truncation. Default value is 12 (the widest char size is used).
340 void QtxListAction::setMaxLineChars( int nchars )
342 myFrame->setMaxLineChars( nchars );
346 Name: setComment [public]
347 Desc: Sets the format Qt string for comments displayed under the list
348 of actions for one action and for several actions.
349 Ex. "Undo %1 actions" format string will work as "Undo 3 actions"
350 when 3 actions are selected. The default format string is "%1".
353 void QtxListAction::setComment( const QString& c, const QString& sc )
358 myFrame->setSingleComment( sc.isEmpty() ? c : sc );
359 myFrame->setMultipleComment( c );
363 Name: eventFilter [virtual public]
364 Desc: Reimplemented to paint the tool buttons in 2D/3D.
367 bool QtxListAction::eventFilter( QObject* o, QEvent* e )
369 if ( !myRaise && ( e->type() == QEvent::Enter || e->type() == QEvent::Leave ) )
372 QWidget* wid = widget( (QWidget*)o );
373 if ( o == mainButton( wid ) )
374 obj = dropButton( wid );
375 else if ( o == dropButton( wid ) )
376 obj = mainButton( wid );
381 QApplication::sendEvent( obj, e );
386 return QObject::eventFilter( o, e );
390 Name: addedTo [protected]
391 Desc: Reimplemented for internal reasons.
394 void QtxListAction::addedTo( QWidget* actionWidget, QWidget* container )
397 QtxAction::addedTo( actionWidget, container );
400 if ( !container->inherits( "QToolBar" ) )
404 entry.main = (QToolButton*)actionWidget;
406 myButtons.insert( container, entry );
410 Name: initialize [private]
411 Desc: Initialization of object QtxListAction.
414 void QtxListAction::initialize()
416 myTipGroup = new QToolTipGroup( this );
418 myFrame = new QtxListFrame( qApp->mainWidget() );
419 myFrame->setMaxLines( 5 );
420 myFrame->setMaxLineChars( 7 );
424 connect( myFrame, SIGNAL( hided() ), this, SLOT( onHided() ) );
425 connect( this, SIGNAL( activated() ), this, SLOT( onSingle() ) );
426 connect( myFrame, SIGNAL( selected( int ) ), this, SLOT( onMultiple( int ) ) );
428 connect( myTipGroup, SIGNAL( removeTip() ), this, SLOT( clearStatusText() ) );
429 connect( myTipGroup, SIGNAL( showTip( const QString& ) ), this, SLOT( showStatusText( const QString& ) ) );
433 Name: onSingle [private slot]
434 Desc: Called when a single action is selected.
437 void QtxListAction::onSingle()
443 Name: onMultiple [private slot]
444 Desc: Called when multiple actions are selected.
447 void QtxListAction::onMultiple( int numActions )
452 if ( numActions > 0 )
453 emit activated( numActions );
457 Name: onExpand [private slot]
458 Desc: Activates the list of actions.
461 void QtxListAction::onExpand( bool on )
463 const QObject* obj = sender();
466 QWidget* wid = widget( (QToolButton*)obj );
467 QToolButton* main = mainButton( wid );
468 myFrame->setOwner( main );
476 void QtxListAction::onHided()
478 for ( ButtonsMap::Iterator bit = myButtons.begin(); bit != myButtons.end(); ++bit )
480 bool block = bit.data().drop->signalsBlocked();
481 bit.data().drop->blockSignals( true );
482 bit.data().drop->setOn( false );
483 bit.data().drop->blockSignals( block );
488 Name: onActivated [private slot]
489 Desc: Called when a sub menu item is activated.
492 void QtxListAction::onActivated( int id )
494 QPopupMenu* pm = (QPopupMenu*)sender();
495 int num = pm->itemParameter( id );
497 emit activated( num );
501 Name: onDestroyed [private slot]
502 Desc: Called when a container widget is destroyed.
505 void QtxListAction::onDestroyed( QObject* obj )
507 if ( !obj->isWidgetType() )
510 myPopups.remove( (QWidget*)obj );
511 myButtons.remove( (QWidget*)obj );
515 Name: widget [private]
516 Desc: Returns container widget for specified control.
519 QWidget* QtxListAction::widget( QWidget* obj ) const
522 for ( PopupsMap::ConstIterator pit = myPopups.begin(); pit != myPopups.end() && !wid; ++pit )
523 if ( pit.data().popup == obj )
526 for ( ButtonsMap::ConstIterator bit = myButtons.begin(); bit != myButtons.end() && !wid; ++bit )
527 if ( bit.data().main == obj || bit.data().drop == obj )
534 Name: listPopup [private]
535 Desc: Returns sub popup menu widget for specified container.
538 QPopupMenu* QtxListAction::listPopup( QWidget* wid ) const
541 if ( myPopups.contains( wid ) )
542 p = myPopups[wid].popup;
547 Name: mainButton [private]
548 Desc: Returns main tool button for specified container.
551 QToolButton* QtxListAction::mainButton( QWidget* wid ) const
554 if ( myButtons.contains( wid ) )
555 mb = myButtons[wid].main;
560 Name: dropButton [private]
561 Desc: Returns drop tool button for specified container.
564 QToolButton* QtxListAction::dropButton( QWidget* wid ) const
567 if ( myButtons.contains( wid ) )
568 db = myButtons[wid].drop;
573 Name: controlDeleted [private]
574 Desc: Called when action child controls deleted.
577 void QtxListAction::controlDeleted( QWidget* wid )
580 for ( ButtonsMap::Iterator it = myButtons.begin(); it != myButtons.end() && !w; ++it )
582 if ( it.data().main == wid || it.data().drop == wid )
591 myButtons.remove( w );
595 /**********************************************************************
596 ** Class: QtxListFrame
597 ** Descr: Frame for the list of actions
599 ***********************************************************************/
601 class QtxListFrame::ScrollEvent : public QCustomEvent
604 enum { Scroll = User + 1 };
606 ScrollEvent( bool down ) : QCustomEvent( Scroll ), myDown( down ) {};
607 virtual ~ScrollEvent() {};
609 bool isDown() const { return myDown; };
623 QtxListFrame::QtxListFrame( QWidget* parent, WFlags f )
624 : QFrame( parent, 0, WStyle_Customize | WStyle_NoBorderEx | WType_Popup | WStyle_Tool | WStyle_StaysOnTop ),
629 myMaxLineChars( 10 ),
631 myScrollBlock( false )
633 QVBoxLayout* theLayout = new QVBoxLayout( this, 3 );
634 theLayout->setResizeMode( QLayout::FreeResize );
636 myList = new QListBox( this );
637 myList->setSelectionMode( QListBox::Multi );
638 myList->setHScrollBarMode( QScrollView::AlwaysOff );
639 myList->setFocusPolicy( NoFocus );
641 QPalette p = myList->palette();
642 p.setColor( QPalette::Inactive, QColorGroup::Highlight,
643 p.color( QPalette::Active, QColorGroup::Highlight ) );
644 p.setColor( QPalette::Inactive, QColorGroup::HighlightedText,
645 p.color( QPalette::Active, QColorGroup::HighlightedText ) );
646 myList->setPalette( p );
648 /* We'll have the vertical scroll bar only and
649 truncate the names which are too wide */
650 connect( myList, SIGNAL( contentsMoving( int, int ) ), this, SLOT( onScroll( int, int ) ) );
652 myComment = new QLabel( this );
653 myComment->setFrameStyle( Panel | Sunken );
654 myComment->setAlignment( AlignCenter );
655 myMultipleComment = "%1";
657 theLayout->addWidget( myList );
658 theLayout->addWidget( myComment );
660 setFrameStyle( Panel | Raised );
663 QtxListFrame::~QtxListFrame()
668 Clears list of names [ public ]
671 void QtxListFrame::clear()
677 void QtxListFrame::addNames( const QStringList& names )
679 for ( QStringList::ConstIterator it = names.begin(); it != names.end(); ++it )
680 myNames.append( *it );
685 Sets a names to the list. Truncates the name to fit to the frame width.
686 Use QtxListAction::setMaxLineChar( int ) to set the width in characters. [ public ]
689 void QtxListFrame::setNames( const QStringList& names )
696 for ( QStringList::ConstIterator it = names.begin(); it != names.end(); ++it )
699 QFontMetrics fm = myList->fontMetrics();
700 int maxW = myMaxLineChars * fm.maxWidth();
701 int w = fm.width( s );
704 QString extra( "..." );
705 int len = s.length();
706 int extraLen = fm.width( extra ) + 1;
709 w = fm.width( s, --len );
710 if ( w + extraLen < maxW )
718 myList->insertItem( s );
722 const QStringList QtxListFrame::names() const
728 Sets max number of lines shown without activation of vertical scroll bar. [ public ]
731 void QtxListFrame::setMaxLines( int maxLines )
733 myMaxLines = maxLines;
737 Sets max number of chars in line ( the rest will be truncated ). [ public ]
740 void QtxListFrame::setMaxLineChars( int maxChars )
742 if ( myMaxLineChars == maxChars )
745 myMaxLineChars = maxChars;
750 Sets the format of single comment. [ public ]
753 void QtxListFrame::setSingleComment( const QString& comment )
755 mySingleComment = comment;
761 Sets the format of multiple comment. [ public ]
764 void QtxListFrame::setMultipleComment( const QString& comment )
766 myMultipleComment = comment;
772 Updates comment display. [ public ]
775 void QtxListFrame::updateComment()
778 int selNum = selected();
780 com = myMultipleComment;
781 else if ( selNum > 0 && !mySingleComment.isEmpty() )
782 com = mySingleComment;
784 if ( !com.isEmpty() )
785 com = com.arg( selNum );
787 myComment->setText( com );
790 void QtxListFrame::setOwner( QWidget* wo )
796 if ( myOwner->parentWidget() && myOwner->parentWidget()->inherits( "QToolBar" ) &&
797 ((QToolBar*)myOwner->parentWidget())->orientation() == Qt::Vertical )
798 lpos = QPoint( myOwner->x() + myOwner->width() + 2, myOwner->y() );
800 lpos = QPoint( myOwner->x(), myOwner->y() + myOwner->height() + 2 );
801 QPoint gpos = myOwner->parentWidget() ? myOwner->parentWidget()->mapToGlobal( lpos )
802 : myOwner->mapToGlobal( lpos );
803 if ( parentWidget() )
804 move( parentWidget()->mapFromGlobal( gpos ) );
811 Validates the action. [ private slot ]
814 void QtxListFrame::accept()
816 emit selected( selected() );
820 Cancels the action. [ private slot ]
823 void QtxListFrame::reject()
829 Initializes / shows the frame. [ virtual public slot ]
832 void QtxListFrame::show()
834 int cnt = (int)myList->count();
838 myList->setTopItem( 0 );
839 myList->clearSelection();
840 myList->setMinimumSize( 0, ( QMIN( cnt + 1, myMaxLines ) ) * myList->itemHeight() + 1 );
843 int linstep = myList->itemHeight();
844 myList->verticalScrollBar()->setLineStep( linstep );
845 myList->verticalScrollBar()->setPageStep( myMaxLines * linstep );
847 QFontMetrics fm = myList->fontMetrics();
848 layout()->invalidate();
849 int maxHeight = layout()->minimumSize().height() + layout()->margin();
850 int maxWidth = myMaxLineChars * fm.maxWidth();
851 for ( uint i = 0; i <= myList->count(); i++ )
852 maxWidth = QMAX( maxWidth, fm.width( myList->text( i ) ) );
854 resize( width(), maxHeight );
856 myList->updateGeometry();
858 QApplication::sendPostedEvents();
860 myList->resizeContents( myList->contentsWidth(),
861 myList->itemHeight() * cnt );
862 if ( myList->contentsHeight() > myList->visibleHeight() )
863 maxWidth += myList->verticalScrollBar()->width();
865 QString single = mySingleComment.arg( cnt );
866 QString multi = myMultipleComment.arg( cnt );
867 int comWidth = QMAX( myComment->fontMetrics().width( single ), myComment->fontMetrics().width( multi ) );
868 if ( myComment->frameWidth() )
869 comWidth += myComment->fontMetrics().width( "x" );
871 maxWidth = QMAX( maxWidth, comWidth );
873 resize( maxWidth, maxHeight );
876 qApp->installEventFilter( this );
883 Cleanup. [ virtual public slot ]
886 void QtxListFrame::hide()
888 qApp->removeEventFilter( this );
894 Processes KeyUp/KeyDown, PageUp/PageDown, CR and Esc keys.
895 Returns 'true' if event is eaten, 'false' otherwise. [ private ]
898 bool QtxListFrame::handleKeyEvent( QObject* , QKeyEvent* e )
900 if ( e->type() == QEvent::KeyRelease )
903 int selNum = selected();
907 setSelected( QMAX( 1, selNum - 1 ) );
910 setSelected( QMAX( 1, selNum + 1 ) );
913 setSelected( QMAX( 1, selNum - myMaxLines ) );
916 setSelected( selNum += myMaxLines );
922 setSelected( myList->count() );
935 Selects items on move, validates on button release. If object 'o' is not our name list,
936 we close the frame. Returns 'true' if event is eaten, 'false' otherwise. [ private ]
939 bool QtxListFrame::handleMouseEvent( QObject* o, QMouseEvent* e )
943 case QEvent::MouseButtonPress:
945 if ( o != myList->viewport() && !isPopup() )
949 case QEvent::MouseMove:
951 if ( o == myList->viewport() )
953 QListBoxItem* lbi = myList->itemAt( e->pos() );
955 setSelected( myList->index( lbi ) + 1 );
959 case QEvent::MouseButtonRelease:
961 if ( o == myList->viewport() )
973 bool QtxListFrame::event( QEvent* e )
975 if ( e->type() != (int)ScrollEvent::Scroll )
976 return QFrame::event( e );
978 ScrollEvent* se = (ScrollEvent*)e;
980 setSelected( myList->topItem() + myList->numItemsVisible() );
982 setSelected( myList->topItem() + 1 );
988 Watches mouse events on the viewport of the list. [ virtual public ]
991 bool QtxListFrame::eventFilter( QObject* o, QEvent* e )
993 bool isKeyEvent = ( e->type() == QEvent::KeyPress ||
994 e->type() == QEvent::KeyRelease );
995 bool isMouseEvent = ( e->type() == QEvent::MouseMove ||
996 e->type() == QEvent::MouseButtonPress ||
997 e->type() == QEvent::MouseButtonRelease ||
998 e->type() == QEvent::MouseButtonDblClick );
1002 if ( handleKeyEvent( o, ( QKeyEvent* )e ) )
1005 else if ( isMouseEvent && o != myList->verticalScrollBar() )
1007 if ( handleMouseEvent( o, ( QMouseEvent*)e ) )
1011 if ( o != this && ( e->type() == QEvent::Resize || e->type() == QEvent::Move ) )
1012 setOwner( myOwner );
1014 return QFrame::eventFilter( o, e );
1018 Selects operations while scrolling the list. [ private slot ]
1021 void QtxListFrame::onScroll( int x, int y )
1023 int dx = y - myScrollVal;
1024 if ( !myScrollBlock )
1025 QApplication::postEvent( this, new ScrollEvent( dx > 0 ) );
1030 Selects the actions [ 0 - lastSel ]. [ public ]
1033 void QtxListFrame::setSelected( const int lastSel )
1035 int last = QMIN( lastSel, (int)myList->count() );
1037 for ( int i = 0; i < (int)myList->count(); i++ )
1038 myList->setSelected( i, i < last );
1040 int item = last - 1;
1042 myScrollBlock = true;
1044 if ( item < myList->topItem() )
1045 myList->setTopItem( item );
1047 if ( item >= myList->topItem() + myList->numItemsVisible() )
1048 myList->setTopItem( item - myList->numItemsVisible() + 1 );
1050 myScrollBlock = false;
1052 myList->clearFocus();
1057 int QtxListFrame::selected() const
1060 while ( sel < myList->count() && myList->isSelected( sel ) )