1 // Copyright (C) 2007-2008 CEA/DEN, EDF R&D, OPEN CASCADE
3 // Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 // File: QtxWorkstack.cxx
23 // Author: Sergey TELKOV
25 #include "QtxWorkstack.h"
27 #include "QtxAction.h"
35 #include <QFocusEvent>
36 #include <QMouseEvent>
37 #include <QRubberBand>
38 #include <QApplication>
39 #include <QStyleOption>
40 #include <QInputDialog>
41 #include <QStackedWidget>
42 #include <QAbstractButton>
45 \class QtxWorkstackArea::WidgetEvent
47 \brief Internal class used to forward child widgets events to the workarea
50 class QtxWorkstackArea::WidgetEvent : public QEvent
53 WidgetEvent( Type t, QWidget* w = 0 ) : QEvent( t ), myWidget( w ) {};
55 QWidget* widget() const { return myWidget; }
58 QWidget* myWidget; // event receiver widget
62 \class QtxWorkstackDrag
64 \brief Workstack drag object
69 \param ws parent workstack
70 \param child child widget container
72 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
81 QApplication::instance()->installEventFilter( this );
87 QtxWorkstackDrag::~QtxWorkstackDrag()
89 QApplication::instance()->removeEventFilter( this );
95 \brief Custom event filter.
96 \param o event receiver widget
98 \return \c true if event should be filtered (stop further processing)
100 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
104 case QEvent::MouseMove:
105 updateTarget( ((QMouseEvent*)e)->globalPos() );
107 case QEvent::MouseButtonRelease:
120 \brief Detect and set dropping target widget.
121 \param p current dragging position
123 void QtxWorkstackDrag::updateTarget( const QPoint& p )
126 QtxWorkstackArea* area = detectTarget( p, tab );
127 setTarget( area, tab );
131 \brief Detect dropping target.
132 \param p current dragging position
133 \param tab resulting target tab page index
134 \return target workarea or 0 if there is no correct drop target
136 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
141 QtxWorkstackArea* area = myWS->areaAt( p );
143 tab = area->tabAt( p );
148 \brief Set dropping target.
149 \param area new target workarea
150 \param tab target workarea's tab page index
152 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
154 if ( !area || ( myArea == area && tab == myTab ) )
170 \brief Called when drop operation is finished.
172 Inserts dropped widget to the target workarea.
174 void QtxWorkstackDrag::dropWidget()
177 myArea->insertWidget( myChild->widget(), myTab );
181 \brief Draw floating rectangle.
183 void QtxWorkstackDrag::drawRect()
188 QRect r = myArea->floatRect();
191 r.setTop( r.top() + m + 2 );
192 r.setLeft( r.left() + m + 2 );
193 r.setRight( r.right() - m - 2 );
194 r.setBottom( r.bottom() - m - 2 );
198 myAreaRect->setGeometry( r );
199 myAreaRect->setVisible( r.isValid() );
202 QRect tr = myArea->floatTab( myTab );
204 tr.setTop( tr.top() + m );
205 tr.setLeft( tr.left() + m );
206 tr.setRight( tr.right() - m );
207 tr.setBottom( tr.bottom() - m );
211 myTabRect->setGeometry( tr );
212 myTabRect->setVisible( tr.isValid() );
217 \brief Delete rubber band on the end on the dragging operation.
219 void QtxWorkstackDrag::endDrawRect()
229 \brief Create rubber band to be drawn on the dragging operation.
231 void QtxWorkstackDrag::startDrawRect()
234 myTabRect = new QRubberBand( QRubberBand::Rectangle );
239 myAreaRect = new QRubberBand( QRubberBand::Rectangle );
247 \brief Workstack area close button.
250 class CloseButton : public QAbstractButton
253 CloseButton( QWidget* );
255 QSize sizeHint() const;
256 QSize minimumSizeHint() const;
258 void enterEvent( QEvent* );
259 void leaveEvent( QEvent* );
260 void paintEvent( QPaintEvent* );
266 \param parent parent widget
268 CloseButton::CloseButton( QWidget* parent )
269 : QAbstractButton( parent )
271 setFocusPolicy( Qt::NoFocus );
275 \brief Get appropriate size for the button.
279 QSize CloseButton::sizeHint() const
283 if( !icon().isNull() )
285 const QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ),
287 dim = qMax( pm.width(), pm.height() );
289 return QSize( dim + 4, dim + 4 );
293 \brief Get minimum appropriate size for the button.
295 \return minimum size value
297 QSize CloseButton::minimumSizeHint() const
303 \brief Process mouse enter event.
305 \param event mouse enter event
307 void CloseButton::enterEvent( QEvent *event )
311 QAbstractButton::enterEvent( event );
315 \brief Process mouse leave event.
317 \param event mouse leave event
319 void CloseButton::leaveEvent( QEvent *event )
323 QAbstractButton::leaveEvent( event );
327 \brief Process paint event.
329 \param event paint event
331 void CloseButton::paintEvent( QPaintEvent* )
338 opt.state |= QStyle::State_AutoRaise;
339 if ( isEnabled() && underMouse() && !isChecked() && !isDown() )
340 opt.state |= QStyle::State_Raised;
342 opt.state |= QStyle::State_On;
344 opt.state |= QStyle::State_Sunken;
345 style()->drawPrimitive( QStyle::PE_PanelButtonTool, &opt, &p, this );
347 int shiftHorizontal = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftHorizontal, &opt, this ) : 0;
348 int shiftVertical = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftVertical, &opt, this ) : 0;
350 r.adjust( 2, 2, -2, -2 );
351 r.translate( shiftHorizontal, shiftVertical );
353 QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ), isEnabled() ?
354 underMouse() ? QIcon::Active : QIcon::Normal
356 isDown() ? QIcon::On : QIcon::Off );
357 style()->drawItemPixmap( &p, r, Qt::AlignCenter, pm );
361 \class QtxWorkstackArea
363 \brief Workstack widget workarea.
368 \param parent parent widget
370 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
373 setFrameStyle( QFrame::Panel | QFrame::Sunken );
375 QVBoxLayout* base = new QVBoxLayout( this );
376 base->setMargin( frameWidth() );
377 base->setSpacing( 0 );
379 QWidget* top = new QWidget( this );
380 base->addWidget( top );
382 QHBoxLayout* tl = new QHBoxLayout( top );
385 myBar = new QtxWorkstackTabBar( top );
386 tl->addWidget( myBar, 1 );
388 CloseButton* close = new CloseButton( top );
389 close->setIcon( style()->standardIcon( QStyle::SP_TitleBarCloseButton ) );
391 tl->addWidget( myClose );
393 myStack = new QStackedWidget( this );
395 base->addWidget( myStack, 1 );
397 connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
398 connect( myBar, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
399 connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
400 connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
406 QApplication::instance()->installEventFilter( this );
412 QtxWorkstackArea::~QtxWorkstackArea()
414 QApplication::instance()->removeEventFilter( this );
418 \brief Check if workarea contains any widgets.
419 \return \c true if area is null (havn't any child widgets)
421 bool QtxWorkstackArea::isNull() const
423 return myList.isEmpty();
427 \brief Check if workarea contains visible widgets.
428 \return \c true if area is empty (all child widgets are removed or now shown)
430 bool QtxWorkstackArea::isEmpty() const
433 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
434 res = it.value().vis;
439 \brief Add widget to the workarea.
440 \param wid widget to be added
441 \param idx position in the area widget to be added to
442 \param f widget flags
443 \return child widget container object (or 0 if index is invalid)
445 QWidget* QtxWorkstackArea::insertWidget( QWidget* wid, const int idx, Qt::WindowFlags f )
450 int pos = myList.indexOf( wid );
451 if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
454 myList.removeAll( wid );
455 pos = idx < 0 ? myList.count() : idx;
456 myList.insert( qMin( pos, (int)myList.count() ), wid );
457 if ( !myInfo.contains( wid ) )
459 QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack, f );
460 myChild.insert( wid, child );
461 myInfo.insert( wid, WidgetInfo() );
462 myInfo[wid].id = generateId();
463 myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
465 connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
466 connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
467 connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
468 connect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
469 connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
470 connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
475 setWidgetActive( wid );
482 \brief Create and show popup menu for the area.
483 \param p mouse pointer position at which popup menu should be shown
485 void QtxWorkstackArea::onContextMenuRequested( QPoint p )
487 const QtxWorkstackTabBar* bar = ::qobject_cast<const QtxWorkstackTabBar*>( sender() );
492 int idx = tabAt( p );
494 wid = widget( myBar->tabId( idx ) );
496 emit contextMenuRequested( wid, p );
500 \brief Called when area's child widget is destroyed.
502 Removes child widget from the area.
504 void QtxWorkstackArea::onWidgetDestroyed()
507 removeWidget( (QWidget*)sender(), false );
511 \brief Remove widget from workarea.
512 \param wid widget to be removed
513 \param del if \c true the widget should be also deleted
515 void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
517 if ( !myList.contains( wid ) )
520 if ( myBar->indexOf( widgetId( wid ) ) != -1 )
521 myBar->removeTab( myBar->indexOf( widgetId( wid ) ) );
523 myStack->removeWidget( child( wid ) );
525 myList.removeAll( wid );
526 myInfo.remove( wid );
527 myChild.remove( wid );
539 \brief Get all visible child widgets.
540 \return list of visible child widgets
542 QWidgetList QtxWorkstackArea::widgetList() const
545 for ( QWidgetList::const_iterator it = myList.begin(); it != myList.end(); ++it )
547 if ( widgetVisibility( *it ) )
554 \brief Get active child widget.
555 \return active widget
557 QWidget* QtxWorkstackArea::activeWidget() const
559 return widget( myBar->tabId( myBar->currentIndex() ) );
563 \brief Set active widget.
564 \param wid widget to be made active
566 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
568 myBar->setCurrentIndex( myBar->indexOf( widgetId( wid ) ) );
572 \brief Check if area owns the specified widget.
573 \param wid widget to be checked
574 \return \c true if area contains widget
576 bool QtxWorkstackArea::contains( QWidget* wid ) const
578 return myList.contains( wid );
582 \brief Show/hide workarea.
583 \param on new visibility state
585 void QtxWorkstackArea::setVisible( bool on )
587 QMap<QWidget*, bool> map;
588 for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
590 map.insert( *it, isBlocked( *it ) );
591 setBlocked( *it, true );
594 QFrame::setVisible( on );
596 for ( QWidgetList::iterator itr = myList.begin(); itr != myList.end(); ++itr )
597 setBlocked( *itr, map.contains( *itr ) ? map[*itr] : false );
601 \brief Check if workarea is active.
602 \return \c true if area is active
604 bool QtxWorkstackArea::isActive() const
606 QtxWorkstack* ws = workstack();
610 return ws->activeArea() == this;
614 \brief Update active tab bar state (active/inactive).
616 void QtxWorkstackArea::updateActiveState()
618 myBar->setActive( isActive() );
622 \brief Get parent workstack
623 \return workstack owning this workarea
625 QtxWorkstack* QtxWorkstackArea::workstack() const
627 QtxWorkstack* ws = 0;
628 QWidget* wid = parentWidget();
631 if ( wid->inherits( "QtxWorkstack" ) )
632 ws = (QtxWorkstack*)wid;
633 wid = wid->parentWidget();
639 \brief Custom event filter.
641 Process events from child widgets.
643 \param o event receiver widget
645 \return \c true if event should be filtered (stop further processing)
647 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
649 if ( o->isWidgetType() )
651 QWidget* wid = (QWidget*)o;
652 if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
655 while ( !ok && wid && wid != myClose )
658 wid = wid->parentWidget();
661 QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
668 \brief Get rectangle to be drawn when highlighting drop area.
669 \return area drop rectangle
671 QRect QtxWorkstackArea::floatRect() const
673 QRect r = myStack->geometry();
674 return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
678 \brief Get rectangle to be drawn when highlighting drop area on tab bar.
680 \return tab bar drop rectrangle
682 QRect QtxWorkstackArea::floatTab( const int idx ) const
684 QRect r = myBar->tabRect( idx );
685 return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
689 \brief Get tab index by point.
691 \return tab covering point or -1 if there is no tab covering point
693 int QtxWorkstackArea::tabAt( const QPoint& pnt ) const
696 QPoint p = myBar->mapFromGlobal( pnt );
697 for ( int i = 0; i < myBar->count() && idx == -1; i++ )
699 QRect r = myBar->tabRect( i );
700 if ( r.isValid() && r.contains( p ) )
707 \brief Event handler for custom events.
708 \param e custom event
710 void QtxWorkstackArea::customEvent( QEvent* e )
712 WidgetEvent* we = (WidgetEvent*)e;
714 switch ( we->type() )
717 myBar->updateActiveState();
718 emit activated( activeWidget() );
721 if ( activeWidget() )
723 if ( !activeWidget()->focusWidget() )
724 activeWidget()->setFocus();
727 if ( activeWidget()->focusWidget()->hasFocus() )
729 QFocusEvent in( QEvent::FocusIn );
730 QApplication::sendEvent( this, &in );
733 activeWidget()->focusWidget()->setFocus();
734 myBar->updateActiveState();
740 removeWidget( we->widget() );
748 \brief Customize focus in event handler.
749 \param e focus in event
751 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
753 QFrame::focusInEvent( e );
755 myBar->updateActiveState();
757 emit activated( activeWidget() );
761 \brief Customize mouse press event handler.
762 \param e mouse press event
764 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
766 QFrame::mousePressEvent( e );
768 emit activated( activeWidget() );
772 \brief Called when user presses "Close" button.
774 void QtxWorkstackArea::onClose()
776 QWidget* wid = activeWidget();
782 \brief Called when user selects any tab page.
783 \param idx tab page index (not used)
785 void QtxWorkstackArea::onCurrentChanged( int /*idx*/ )
789 emit activated( activeWidget() );
793 \brief Called when user starts tab page dragging.
795 void QtxWorkstackArea::onDragActiveTab()
797 QtxWorkstackChild* c = child( activeWidget() );
801 new QtxWorkstackDrag( workstack(), c );
805 \brief Called when area's child widget container is destroyed.
806 \param obj widget container being destroyed
808 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
810 QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
811 myStack->removeWidget( child );
814 for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
816 if ( it.value() == child )
820 myChild.remove( wid );
822 QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)RemoveWidget, wid ) );
826 \brief Called when child widget container is shown.
827 \param c child widget container being shown
829 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
831 setWidgetShown( c->widget(), true );
835 \brief Called when child widget container is hidden.
836 \param c child widget container being hidden
838 void QtxWorkstackArea::onChildHidden( QtxWorkstackChild* c )
840 setWidgetShown( c->widget(), false );
844 \brief Called when child widget container is activated.
845 \param c child widget container being activated
847 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
849 setWidgetActive( c->widget() );
853 \brief Called when child widget container's title is changed.
854 \param c child widget container which title is changed
856 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
858 updateTab( c->widget() );
862 \brief Update current child widget container.
864 Raises widget when active tab page is changed.
866 void QtxWorkstackArea::updateCurrent()
868 QMap<QWidget*, bool> map;
869 for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
871 map.insert( *it, isBlocked( *it ) );
872 setBlocked( *it, true );
875 QWidget* cur = child( widget( myBar->tabId( myBar->currentIndex() ) ) );
877 myStack->setCurrentWidget( cur );
879 for ( QWidgetList::iterator itr = myList.begin(); itr != myList.end(); ++itr )
880 setBlocked( *itr, map.contains( *itr ) ? map[*itr] : false );
884 \brief Update tab bar.
885 \param wid tab page widget
887 void QtxWorkstackArea::updateTab( QWidget* wid )
889 int idx = myBar->indexOf( widgetId( wid ) );
893 myBar->setTabIcon( idx, wid->windowIcon() );
894 myBar->setTabText( idx, wid->windowTitle() );
898 \brief Get child widget by specified identifier.
900 \return widget or 0, if identifier in invalid
902 QWidget* QtxWorkstackArea::widget( const int id ) const
905 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
907 if ( it.value().id == id )
914 \brief Get child widget identifier.
916 \return widget ID or -1 if widget is not found
918 int QtxWorkstackArea::widgetId( QWidget* wid ) const
921 if ( myInfo.contains( wid ) )
927 \brief Get child widget's visibility.
929 \return \c true if widget is visible
931 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
934 if ( myInfo.contains( wid ) )
935 res = myInfo[wid].vis;
940 \brief Set active child widget.
941 \param wid widget to be set active
943 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
945 int id = widgetId( wid );
949 myBar->setCurrentIndex( myBar->indexOf( id ) );
953 \brief Show/hide child widget.
955 \param on new visibility state
957 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
959 if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
962 myInfo[wid].vis = on;
967 \brief Update internal state.
969 void QtxWorkstackArea::updateState()
971 bool updBar = myBar->updatesEnabled();
972 bool updStk = myStack->updatesEnabled();
973 myBar->setUpdatesEnabled( false );
974 myStack->setUpdatesEnabled( false );
976 bool block = myBar->signalsBlocked();
977 myBar->blockSignals( true );
979 QWidget* prev = activeWidget();
982 for ( QWidgetList::iterator it = myList.begin(); it != myList.end(); ++it )
985 int id = widgetId( wid );
990 bool vis = widgetVisibility( wid );
992 int cIdx = myBar->indexOf( id );
993 if ( cIdx != -1 && ( !vis || myBar->indexOf( id ) != idx ) )
994 myBar->removeTab( cIdx );
996 if ( myBar->indexOf( id ) == -1 && vis )
997 myBar->setTabId( myBar->insertTab( idx, wid->windowTitle() ), id );
1001 bool block = isBlocked( wid );
1002 setBlocked( wid, true );
1004 QtxWorkstackChild* cont = child( wid );
1007 myStack->removeWidget( cont );
1008 else if ( myStack->indexOf( cont ) < 0 )
1009 myStack->addWidget( cont );
1014 setBlocked( wid, block );
1017 int curId = widgetId( prev );
1018 if ( myBar->indexOf( curId ) < 0 )
1021 int pos = myList.indexOf( prev );
1022 for ( int i = pos - 1; i >= 0 && !wid; i-- )
1024 if ( widgetVisibility( myList.at( i ) ) )
1025 wid = myList.at( i );
1028 for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
1030 if ( widgetVisibility( myList.at( j ) ) )
1031 wid = myList.at( j );
1035 curId = widgetId( wid );
1038 myBar->setCurrentIndex( myBar->indexOf( curId ) );
1040 myBar->blockSignals( block );
1044 myBar->updateActiveState();
1046 myBar->setUpdatesEnabled( updBar );
1047 myStack->setUpdatesEnabled( updStk );
1053 QResizeEvent re( myBar->size(), myBar->size() );
1054 QApplication::sendEvent( myBar, &re );
1056 myBar->updateGeometry();
1061 emit deactivated( this );
1066 if ( prev != activeWidget() )
1067 emit activated( activeWidget() );
1072 \brief Generate unique widget identifier.
1073 \return first non shared widget ID
1075 int QtxWorkstackArea::generateId() const
1079 for ( WidgetInfoMap::const_iterator it = myInfo.begin(); it != myInfo.end(); ++it )
1080 map.insert( it.value().id, 0 );
1083 while ( map.contains( id ) )
1090 \brief Check if the child wiget is blocked.
1092 \return \c true if the widget is blocked
1094 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
1096 return myBlock.contains( wid );
1100 \brief Block widget.
1102 \param on new blocked state
1104 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
1107 myBlock.insert( wid, 0 );
1109 myBlock.remove( wid );
1113 \brief Get child widget container.
1114 \param wid child widget
1115 \return child widget container corresponding to the \a wid
1117 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1119 QtxWorkstackChild* res = 0;
1120 if ( myChild.contains( wid ) )
1126 \fn void QtxWorkstackArea::activated( QWidget* w )
1127 \brief Emitted when child widget is activated.
1128 \param w child widget being activated
1132 \fn void QtxWorkstackArea::contextMenuRequested( QWidget* w, QPoint p )
1133 \brief Emitted when context popup menu is requested.
1134 \param w child widget popup menu requested for
1135 \param p point popup menu to be shown at
1139 \fn void QtxWorkstackArea::deactivated( QtxWorkstackArea* wa )
1140 \brief Emitted when workarea is deactivated.
1141 \param wa workarea being deactivated
1145 \class QtxWorkstackChild
1147 \brief Workarea child widget container.
1152 \param wid child widget
1153 \param parent parent widget
1154 \param f widget flags
1156 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent, Qt::WindowFlags f )
1157 : QWidget( parent ),
1160 myWidget->setParent( this, f );
1161 myWidget->installEventFilter( this );
1162 QVBoxLayout* base = new QVBoxLayout( this );
1163 base->setMargin( 0 );
1164 base->addWidget( myWidget );
1166 connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1172 QtxWorkstackChild::~QtxWorkstackChild()
1174 QApplication::instance()->removeEventFilter( this );
1179 disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1182 widget()->removeEventFilter( this );
1184 widget()->setParent( 0 );
1188 \brief Get child widget.
1189 \return child widget
1191 QWidget* QtxWorkstackChild::widget() const
1197 \brief Custom event filter.
1199 Process events from child widgets.
1201 \param o event receiver widget
1203 \return \c true if event should be filtered (stop further processing)
1205 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1207 if ( o->isWidgetType() )
1209 if ( e->type() == QEvent::WindowTitleChange || e->type() == QEvent::WindowIconChange )
1210 emit captionChanged( this );
1212 if ( !e->spontaneous() && e->type() == QEvent::ShowToParent )
1215 if ( !e->spontaneous() && e->type() == QEvent::HideToParent )
1216 emit hidden( this );
1218 if ( e->type() == QEvent::FocusIn )
1219 emit activated( this );
1221 return QWidget::eventFilter( o, e );
1225 \brief Called when child widget is destroyed.
1226 \param obj child widget being destroyed
1228 void QtxWorkstackChild::onDestroyed( QObject* obj )
1230 if ( obj != widget() )
1238 \brief Customize child event handler.
1239 \param e child event
1241 void QtxWorkstackChild::childEvent( QChildEvent* e )
1243 if ( e->removed() && e->child() == widget() )
1248 QWidget::childEvent( e );
1252 \fn void QtxWorkstackChild::shown( QtxWorkstackChild* w )
1253 \brief Emitted when child widget is shown.
1254 \param w child widget container
1258 \fn void QtxWorkstackChild::hidden( QtxWorkstackChild* w )
1259 \brief Emitted when child widget is hidden.
1260 \param w child widget container
1264 \fn void QtxWorkstackChild::activated( QtxWorkstackChild* w )
1265 \brief Emitted when child widget is activated.
1266 \param w child widget container
1270 \fn void QtxWorkstackChild::captionChanged( QtxWorkstackChild* w )
1271 \brief Emitted when child widget's title is changed.
1272 \param w child widget container
1276 \class QtxWorkstackTabBar
1278 \brief Workstack tab bar widget
1283 \param parent parent widget
1285 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1286 : QTabBar( parent ),
1289 setDrawBase( true );
1290 setElideMode( Qt::ElideNone );
1292 connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
1298 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1303 \brief Get tab page identifier.
1304 \param index tab page index
1305 \return tab page ID or -1 if \a index is out of range
1307 int QtxWorkstackTabBar::tabId( const int index ) const
1309 QVariant v = tabData( index );
1310 if ( !v.canConvert( QVariant::Int ) )
1316 \brief Set tab page identifier.
1317 \param index tab page index
1318 \param id tab page ID
1320 void QtxWorkstackTabBar::setTabId( const int index, const int id )
1322 setTabData( index, id );
1326 \brief Get tab page index by specified identifier.
1327 \param id tab page ID
1328 \return tab page index or -1 if not found
1330 int QtxWorkstackTabBar::indexOf( const int id ) const
1333 for ( int i = 0; i < (int)count() && index < 0; i++ )
1335 if ( tabId( i ) == id )
1342 \brief Check if the tab bar is active.
1343 \return \c true if tab bar is active
1345 bool QtxWorkstackTabBar::isActive() const
1351 \brief Set tab bar active/inactive.
1352 \param on new active state
1354 void QtxWorkstackTabBar::setActive( const bool on )
1356 if ( myActive == on )
1360 updateActiveState();
1364 \brief Update tab bar according to the 'active' state.
1366 void QtxWorkstackTabBar::updateActiveState()
1368 QColor bc = palette().color( QPalette::Text );
1369 QColor ac = isActive() ? palette().color( QPalette::Highlight ) : bc;
1370 for ( int i = 0; i < (int)count(); i++ )
1371 setTabTextColor( i, currentIndex() == i ? ac : bc );
1375 \brief Called when current tab page is changed.
1376 \param idx tab page index (not used)
1378 void QtxWorkstackTabBar::onCurrentChanged( int /*index*/ )
1380 updateActiveState();
1384 \brief Customize mouse move event handler.
1385 \param e mouse event
1387 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1389 if ( myId != -1 && !tabRect( indexOf( myId ) ).contains( e->pos() ) )
1392 emit dragActiveTab();
1395 QTabBar::mouseMoveEvent( e );
1399 \brief Customize mouse press event handler.
1400 \param e mouse event
1402 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1404 QTabBar::mousePressEvent( e );
1406 if ( e->button() == Qt::LeftButton )
1407 myId = tabId( currentIndex() );
1411 \brief Customize mouse release event handler.
1412 \param e mouse event
1414 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1416 QTabBar::mouseReleaseEvent( e );
1420 if ( e->button() == Qt::RightButton )
1421 emit contextMenuRequested( e->globalPos() );
1425 \brief Customize context menu event handler.
1426 \param e context menu event
1428 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1430 if ( e->reason() != QContextMenuEvent::Mouse )
1431 emit contextMenuRequested( e->globalPos() );
1435 \brief Process widget change state events (style, palette, enable state changing, etc).
1436 \param e change event (not used)
1438 void QtxWorkstackTabBar::changeEvent( QEvent* /*e*/ )
1440 updateActiveState();
1444 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1446 if ( currentTab() != t->identifier() )
1448 QFont fnt = p->font();
1449 fnt.setUnderline( false );
1452 QTabBar::paintLabel( p, br, t, has_focus );
1457 \fn void QtxWorkstackTabBar::dragActiveTab()
1458 \brief Emitted when dragging operation is started.
1462 \fn void QtxWorkstackTabBar::contextMenuRequested( QPoint p )
1463 \brief Emitted when context popup menu is requested.
1464 \param p point popup menu to be shown at
1469 \brief Workstack widget.
1471 Organizes the child widgets in the tabbed space.
1472 Allows splitting the working area to arrange the child widgets in
1473 arbitrary way. Any widgets can be moved to another working area with
1474 drag-n-drop operation.
1476 This widget can be used as workspace of the application main window,
1477 for example, as kind of implementation of multi-document interface.
1482 \param parent parent widget
1484 QtxWorkstack::QtxWorkstack( QWidget* parent )
1485 : QWidget( parent ),
1491 myActionsMap.insert( SplitVertical, new QtxAction( QString(), tr( "Split vertically" ), 0, this ) );
1492 myActionsMap.insert( SplitHorizontal, new QtxAction( QString(), tr( "Split horizontally" ), 0, this ) );
1493 myActionsMap.insert( Close, new QtxAction( QString(), tr( "Close" ), 0, this ) );
1494 myActionsMap.insert( Rename, new QtxAction( QString(), tr( "Rename" ), 0, this ) );
1496 connect( myActionsMap[SplitVertical], SIGNAL( triggered( bool ) ), this, SLOT( splitVertical() ) );
1497 connect( myActionsMap[SplitHorizontal], SIGNAL( triggered( bool ) ), this, SLOT( splitHorizontal() ) );
1498 connect( myActionsMap[Close], SIGNAL( triggered( bool ) ), this, SLOT( onCloseWindow() ) );
1499 connect( myActionsMap[Rename], SIGNAL( triggered( bool ) ), this, SLOT( onRename() ) );
1501 // Action shortcut will work when action added in any widget.
1502 for ( QMap<int, QAction*>::iterator it = myActionsMap.begin(); it != myActionsMap.end(); ++it )
1504 addAction( it.value() );
1505 it.value()->setShortcutContext( Qt::ApplicationShortcut );
1508 QVBoxLayout* base = new QVBoxLayout( this );
1509 base->setMargin( 0 );
1511 mySplit = new QSplitter( this );
1512 mySplit->setChildrenCollapsible( false );
1513 base->addWidget( mySplit );
1519 QtxWorkstack::~QtxWorkstack()
1524 \brief Get list of all widgets in all areas or in specified area which given
1526 \param wid widget specifying area if it is equal to null when widgets of all
1528 \return list of widgets
1530 QWidgetList QtxWorkstack::windowList( QWidget* wid ) const
1532 QList<QtxWorkstackArea*> lst;
1535 areas( mySplit, lst, true );
1539 QtxWorkstackArea* area = wgArea( wid );
1544 QWidgetList widList;
1545 for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end(); ++it )
1547 QWidgetList wids = (*it)->widgetList();
1548 for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1549 widList.append( *itr );
1556 \brief Get all child widgets in the active workarea.
1557 \return list of widgets in active workarea
1559 QWidgetList QtxWorkstack::splitWindowList() const
1561 return myArea ? myArea->widgetList() : QWidgetList();
1565 \brief Get active widget.
1566 \return active widget
1568 QWidget* QtxWorkstack::activeWindow() const
1574 \brief Split workstack.
1576 Splitting is possible only if there are two or more widgets in the workarea.
1577 This function splits current workarea to two new ones.
1579 \param o splitting orientation (Qt::Orientation)
1581 void QtxWorkstack::split( const int o )
1583 QtxWorkstackArea* area = myWorkArea;
1585 area = activeArea();
1589 if ( area->widgetList().count() < 2 )
1592 QWidget* curWid = area->activeWidget();
1596 QSplitter* s = splitter( area );
1597 QList<QtxWorkstackArea*> areaList;
1598 areas( s, areaList );
1600 QList<QSplitter*> splitList;
1601 splitters( s, splitList );
1604 if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
1608 trg = wrapSplitter( area );
1613 trg->setOrientation( (Qt::Orientation)o );
1615 QtxWorkstackArea* newArea = createArea( 0 );
1616 trg->insertWidget( trg->indexOf( area ) + 1, newArea );
1618 area->removeWidget( curWid );
1619 newArea->insertWidget( curWid );
1621 distributeSpace( trg );
1628 \brief Split workarea of the given widget on two parts.
1630 Splitting is possible only if there are two or more widgets in the workarea.
1631 This function splits current workarea to two new ones.
1633 \param wid widget belonging to the workstack
1634 \param o splitting orientation type (Qt::Orientation)
1635 \param type splitting type (QtxWorkstack::SplitType)
1637 void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
1641 // find area of the given widget
1642 QtxWorkstackArea* area = NULL;
1643 QList<QtxWorkstackArea*> allAreas;
1644 areas(mySplit, allAreas, true);
1647 for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
1649 if ( (*it)->contains( wid ) )
1656 QWidgetList wids = area->widgetList();
1657 if ( wids.count() < 2 )
1660 QSplitter* s = splitter( area );
1661 QList<QtxWorkstackArea*> areaList;
1662 areas( s, areaList );
1664 QList<QSplitter*> splitList;
1665 splitters(s, splitList);
1668 if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
1671 if (!trg) trg = wrapSplitter(area);
1674 trg->setOrientation(o);
1676 QtxWorkstackArea* newArea = createArea(0);
1677 insertWidget(newArea, trg, area);
1682 for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1684 QWidget* wid_i = *itr;
1687 area->removeWidget( wid_i );
1688 newArea->insertWidget( wid_i );
1694 QWidgetList::iterator itr = wids.begin();
1695 for ( ; itr != wids.end() && *itr != wid; ++itr )
1698 for ( ; itr != wids.end(); ++itr )
1700 area->removeWidget( *itr );
1701 newArea->insertWidget( *itr );
1706 area->removeWidget( wid );
1707 newArea->insertWidget( wid );
1711 distributeSpace( trg );
1715 \brief Move widget(s) from the source workarea into the target workarea
1716 or reorder widgets inside one workarea.
1718 Move \a wid2 in target workarea. Put it right after \a wid1.
1719 If \a all parameter is \c true, all widgets from source workarea
1720 will be moved including \a wid2 and source workarea will be deleted then.
1722 If \a wid1 and \a wid2 belongs to one workarea, widgets will be just reordered
1725 \param wid1 widget from target workarea
1726 \param wid2 widget from source workarea
1727 \param all if \c true, all widgets from source workarea will
1728 be moved into the target one, else only the \a wid2 will be moved
1730 void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
1732 if ( !wid1 || !wid2 )
1735 // find area of the widgets
1736 QtxWorkstackArea *area1 = 0, *area2 = 0;
1737 QList<QtxWorkstackArea*> allAreas;
1738 areas( mySplit, allAreas, true );
1739 for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !( area1 && area2 ); ++it )
1741 if ( (*it)->contains( wid1 ) )
1744 if ( (*it)->contains( wid2 ) )
1748 if ( !area1 || !area2 )
1751 QWidget* curWid = area1->activeWidget();
1755 if ( area1 == area2 )
1759 // Set wid1 at first position, wid2 at second
1760 area1->insertWidget( wid1 );
1761 area1->insertWidget( wid2, 1 );
1765 // Set wid2 right after wid1
1766 area1->removeWidget( wid2 );
1768 QWidgetList wids1 = area1->widgetList();
1769 for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
1770 area1->insertWidget( wid2, wid1_ind + 1 );
1776 QWidgetList wids1 = area1->widgetList();
1777 for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
1780 // Set wid2 right after wid1, other widgets from area2 right after wid2
1781 QWidgetList wids2 = area2->widgetList();
1782 QWidgetList::iterator itr2 = wids2.begin();
1783 for ( int ind = wid1_ind + 1; itr2 != wids2.end(); ++itr2, ++ind )
1785 area2->removeWidget( *itr2 );
1786 if ( *itr2 == wid2 )
1787 area1->insertWidget( *itr2, wid1_ind + 1 );
1789 area1->insertWidget( *itr2, ind );
1794 // Set wid2 right after wid1
1795 area2->removeWidget( wid2 );
1796 area1->insertWidget( wid2, wid1_ind + 1 );
1800 area1->setActiveWidget( curWid );
1804 \brief Calculate sizes of the splitter widget for the workarea.
1807 static void setSizes (QIntList& szList, const int item_ind,
1808 const int new_near, const int new_this, const int new_farr)
1810 // set size to all items before an item # <item_ind>
1812 QIntList::iterator its = szList.begin();
1813 for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
1816 if (its == szList.end()) return;
1817 // set size to item # <item_ind>
1820 // set size to all items after an item # <item_ind>
1821 for (; its != szList.end(); ++its) {
1827 \brief Set position of the widget relatively to its parent splitter.
1829 Orientation of positioning will correspond to the splitter orientation.
1832 \param pos position relatively to the splitter; value in the range [0..1]
1834 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
1836 if ( position < 0.0 || 1.0 < position)
1842 // find area of the given widget
1843 QtxWorkstackArea* area = NULL;
1844 QList<QtxWorkstackArea*> allAreas;
1845 areas( mySplit, allAreas, true );
1846 for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
1848 if ( (*it)->contains( wid ) )
1855 QSplitter* split = splitter( area );
1859 // find index of the area in its splitter
1861 bool isFound = false;
1862 const QObjectList& was = split->children();
1863 for ( QObjectList::const_iterator ito = was.begin(); ito != was.end() && !isFound; ++ito, ++item_ind )
1869 if ( !isFound || item_ind == 0 )
1872 QIntList szList = split->sizes();
1873 int splitter_size = ( split->orientation() == Qt::Horizontal ? split->width() : split->height());
1874 int nb = szList.count();
1876 int new_prev = int( splitter_size * position / item_ind );
1877 if (nb == item_ind) return;
1878 int new_next = int( splitter_size * ( 1.0 - position ) / ( nb - item_ind ) );
1879 setSizes( szList, item_ind, new_prev, new_next, new_next );
1880 split->setSizes( szList );
1884 \brief Set position of the widget relatively to the entire workstack.
1886 If \a o is \c Qt::Horizontal, the horizontal position of \a wid will be changed.
1887 If \a o is \c Qt::Vertical, the vertical position of \a wid will be changed.
1890 \param o orientation of positioning (\c Qt::Horizontal or \c Qt::Vertical)
1891 \param pos position relatively to the workstack; value in range [0..1]
1893 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
1894 const double position )
1896 if ( position < 0.0 || 1.0 < position )
1902 int splitter_size = o == Qt::Horizontal ? mySplit->width() : mySplit->height();
1903 int need_pos = int( position * splitter_size );
1904 int splitter_pos = 0;
1906 if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
1908 // impossible to set required position
1913 \brief Set accelerator key-combination for the action with specified \a id.
1915 \param accel action accelerator
1917 void QtxWorkstack::setAccel( const int id, const int accel )
1919 if ( !myActionsMap.contains( id ) )
1922 myActionsMap[id]->setShortcut( accel );
1926 \brief Get the action's accelerator key-combination.
1928 \return action accelerator
1930 int QtxWorkstack::accel( const int id ) const
1933 if ( myActionsMap.contains( id ) )
1934 res = myActionsMap[id]->shortcut();
1939 \brief Get icon for the specified action.
1941 If \a id is invalid, null icon is returned.
1943 \param id menu action ID
1944 \return menu item icon
1946 QIcon QtxWorkstack::icon( const int id ) const
1949 if ( myActionsMap.contains( id ) )
1950 ico = myActionsMap[id]->icon();
1955 \brief Set menu item icon for the specified action.
1956 \param id menu action ID
1957 \param ico new menu item icon
1959 void QtxWorkstack::setIcon( const int id, const QIcon& icon )
1961 if ( !myActionsMap.contains( id ) )
1964 myActionsMap[id]->setIcon( icon );
1968 \brief Set actions to be visible in the context popup menu.
1970 Actions, which IDs are set in \a flags parameter, will be shown in the
1971 context popup menu. Other actions will not be shown.
1973 \param flags ORed together actions flags
1975 void QtxWorkstack::setMenuActions( const int flags )
1977 myActionsMap[SplitVertical]->setVisible( flags & SplitVertical );
1978 myActionsMap[SplitHorizontal]->setVisible( flags & SplitHorizontal );
1979 myActionsMap[Close]->setVisible( flags & Close );
1980 myActionsMap[Rename]->setVisible( flags & Rename );
1984 \brief Set actions to be visible in the context popup menu.
1986 Actions, which IDs are set in \a flags parameter, will be shown in the
1987 context popup menu. Other actions will not be shown.
1989 \param flags ORed together actions flags
1991 int QtxWorkstack::menuActions() const
1994 ret = ret | ( myActionsMap[SplitVertical]->isVisible() ? SplitVertical : 0 );
1995 ret = ret | ( myActionsMap[SplitHorizontal]->isVisible() ? SplitHorizontal : 0 );
1996 ret = ret | ( myActionsMap[Close]->isVisible() ? Close : 0 );
1997 ret = ret | ( myActionsMap[Rename]->isVisible() ? Rename : 0 );
2002 \brief Calculate sizes of the splitter widget for the workarea.
2005 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
2006 const int item_ind, const int item_rel_pos,
2007 const int need_pos, const int splitter_pos)
2009 if (item_ind == 0) { // cannot move in this splitter
2010 return (need_pos - splitter_pos);
2015 int new_this = szList[item_ind];
2018 bool isToCheck = false;
2020 if (need_pos < splitter_pos) {
2021 // Set size of all previous workareas to zero <--
2022 if (item_ind == nb - 1) {
2023 // item iz last in the splitter, it will occupy all the splitter
2024 new_this = splitter_size;
2026 // recompute size of next items in splitter
2027 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2029 delta = need_pos - splitter_pos;
2031 } else if (need_pos > (splitter_pos + splitter_size)) {
2032 // Set size of all next workareas to zero -->
2033 // recompute size of previous items in splitter
2035 new_prev = (splitter_size - new_this) / item_ind;
2036 delta = need_pos - (splitter_pos + splitter_size - new_this);
2038 } else { // required position lays inside this splitter
2039 // Move workarea inside splitter into required position <->
2040 int new_item_rel_pos = need_pos - splitter_pos;
2041 new_prev = new_item_rel_pos / item_ind;
2042 if (need_pos < (splitter_pos + item_rel_pos)) {
2043 // Make previous workareas smaller, next - bigger
2044 // No problem to keep old size of the widget
2046 // Make previous workareas bigger, next - smaller
2047 if (new_this > splitter_size - new_item_rel_pos) {
2048 new_this = splitter_size - new_item_rel_pos;
2050 // jfa to do: in this case fixed size of next widgets could prevent right resizing
2053 if (item_ind == nb - 1) {
2054 new_this = splitter_size - new_item_rel_pos;
2056 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2061 setSizes (szList, item_ind, new_prev, new_this, new_next);
2066 \brief Set position of the widget.
2068 Called from SetRelativePosition() public method.
2070 \param wid widget to be moved
2071 \param split currently processed splitter (goes from more common
2072 to more particular splitter in recursion calls)
2073 \param o orientation of positioning
2074 \param need_pos required position of the given widget in pixels
2075 (from top/left side of workstack area)
2076 \param splitter_pos position of the splitter \a split
2077 (from top/left side of workstack area)
2078 \return difference between a required and a distinguished position
2080 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
2081 const int need_pos, const int splitter_pos )
2083 if ( !wid || !split )
2084 return need_pos - splitter_pos;
2086 // Find corresponding sub-splitter.
2087 // Find also index of appropriate item in current splitter.
2088 int cur_ind = 0, item_ind = 0;
2089 bool isBottom = false, isFound = false;
2090 QSplitter* sub_split = NULL;
2091 const QObjectList& objs = split->children();
2092 for ( QObjectList::const_iterator it = objs.begin(); it != objs.end() && !isFound; ++it )
2094 QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
2097 if ( area->contains( wid ) )
2105 else if ( (*it)->inherits( "QSplitter" ) )
2107 QList<QtxWorkstackArea*> areaList;
2108 areas( (QSplitter*)(*it), areaList, true );
2109 for ( QList<QtxWorkstackArea*>::iterator ita = areaList.begin(); ita != areaList.end() && !isFound; ++ita )
2111 if ( (*ita)->contains( wid ) )
2115 sub_split = (QSplitter*)*it;
2123 return ( need_pos - splitter_pos );
2125 if ( split->orientation() == o )
2127 // Find coordinates of near and far sides of the appropriate item relatively current splitter
2128 int splitter_size = ( o == Qt::Horizontal ? split->width() : split->height() );
2129 QIntList szList = split->sizes();
2130 int nb = szList.count();
2131 int item_rel_pos = 0; // position of near side of item relatively this splitter
2132 for (int i = 0; i < item_ind; i++) {
2133 item_rel_pos += szList[i];
2135 int item_size = szList[item_ind]; // size of item
2136 int item_pos = splitter_pos + item_rel_pos;
2138 // Resize splitter items to complete the conditions
2140 // I. Bottom of splitters stack reached
2142 int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
2143 split->setSizes(szList);
2144 // Recompute delta, as some windows can reject given size
2145 int new_item_rel_pos = 0;
2146 QIntList szList1 = split->sizes();
2147 for (int i = 0; i < item_ind; i++) {
2148 new_item_rel_pos += szList1[i];
2150 delta = need_pos - (splitter_pos + new_item_rel_pos);
2154 // II. Bottom of splitters stack is not yet reached
2156 if (item_ind == 0) { // cannot move in this splitter
2157 // Process in sub-splitter
2158 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2162 int new_this = szList[item_ind];
2165 if (need_pos < splitter_pos) {
2166 // Set size of all previous workareas to zero <--
2167 if (item_ind == nb - 1) {
2168 new_this = splitter_size;
2170 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2172 setSizes (szList, item_ind, new_prev, new_this, new_next);
2173 split->setSizes(szList);
2174 // Recompute splitter_pos, as some windows can reject given size
2175 int new_item_rel_pos = 0;
2176 QIntList szList1 = split->sizes();
2177 for (int i = 0; i < item_ind; i++) {
2178 new_item_rel_pos += szList1[i];
2180 // Process in sub-splitter
2181 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2182 } else if (need_pos > (splitter_pos + splitter_size)) {
2183 // Set size of all next workareas to zero -->
2184 new_prev = (splitter_size - new_this) / item_ind;
2185 setSizes (szList, item_ind, new_prev, new_this, new_next);
2186 split->setSizes(szList);
2187 // Recompute splitter_pos, as some windows can reject given size
2188 int new_item_rel_pos = 0;
2189 QIntList szList1 = split->sizes();
2190 for (int i = 0; i < item_ind; i++) {
2191 new_item_rel_pos += szList1[i];
2193 // Process in sub-splitter
2194 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2196 // Set appropriate size of all previous/next items <->
2197 int new_item_rel_pos = item_rel_pos;
2198 if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
2199 // Move item inside splitter into required position <->
2200 int new_this = szList[item_ind];
2202 new_item_rel_pos = need_pos - splitter_pos;
2203 if ((item_pos + item_size) < need_pos) {
2204 //new_item_rel_pos = need_pos - (item_pos + item_size);
2205 new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
2207 int new_prev = new_item_rel_pos / item_ind;
2208 if (need_pos < (splitter_pos + item_rel_pos)) {
2209 // Make previous workareas smaller, next - bigger
2210 // No problem to keep old size of the widget
2212 // Make previous workareas bigger, next - smaller
2213 if (new_this > splitter_size - new_item_rel_pos) {
2214 new_this = splitter_size - new_item_rel_pos;
2217 if (item_ind == nb - 1) {
2218 new_this = splitter_size - new_item_rel_pos;
2220 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2222 setSizes (szList, item_ind, new_prev, new_this, new_next);
2223 split->setSizes(szList);
2224 // Recompute new_item_rel_pos, as some windows can reject given size
2225 new_item_rel_pos = 0;
2226 QIntList szList1 = split->sizes();
2227 for (int i = 0; i < item_ind; i++) {
2228 new_item_rel_pos += szList1[i];
2233 // Process in sub-splitter
2234 int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2238 // this can be if corresponding workarea is first in sub-splitter
2239 // or sub-splitter has another orientation
2241 // Resize ones again to reach precize position <->
2242 int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
2244 // Move workarea inside splitter into required position <->
2245 int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
2246 new_item_rel_pos, need_pos_1, splitter_pos);
2247 split->setSizes(szList);
2248 // Recompute new_item_rel_pos, as some windows can reject given size
2249 new_item_rel_pos = 0;
2250 QIntList szList1 = split->sizes();
2251 for (int i = 0; i < item_ind; i++) {
2252 new_item_rel_pos += szList1[i];
2254 delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
2259 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2266 \brief Redistribute space among widgets equally.
2267 \param split splitter
2269 void QtxWorkstack::distributeSpace( QSplitter* split ) const
2274 QIntList szList = split->sizes();
2275 int size = ( split->orientation() == Qt::Horizontal ?
2276 split->width() : split->height() ) / szList.count();
2277 for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
2279 split->setSizes( szList );
2283 \brief Split widgets vertically.
2285 void QtxWorkstack::splitVertical()
2287 split( Qt::Horizontal );
2291 \brief Split widgets horizontally.
2293 void QtxWorkstack::splitHorizontal()
2295 split( Qt::Vertical );
2299 \brief Called when user activates "Rename" menu item.
2301 Changes widget title.
2303 void QtxWorkstack::onRename()
2309 QString newName = QInputDialog::getText( topLevelWidget(), tr( "Rename" ), tr( "Enter new name:" ),
2310 QLineEdit::Normal, myWorkWin->windowTitle(), &ok );
2311 if ( ok && !newName.isEmpty() )
2312 myWorkWin->setWindowTitle( newName );
2316 \brief Wrap area into the new splitter.
2318 \return new splitter
2320 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
2325 QSplitter* pSplit = splitter( area );
2329 bool upd = pSplit->updatesEnabled();
2330 pSplit->setUpdatesEnabled( false );
2332 QIntList szList = pSplit->sizes();
2334 QSplitter* wrap = new QSplitter( 0 );
2335 wrap->setChildrenCollapsible( false );
2336 pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
2337 wrap->setVisible( true );
2338 wrap->addWidget( area );
2340 pSplit->setSizes( szList );
2342 pSplit->setUpdatesEnabled( upd );
2348 \brief Reparent and add widget.
2350 \param pWid parent widget
2351 \param after widget after which \a wid should be added
2353 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
2355 if ( !wid || !pWid )
2358 QWidgetList moveList;
2359 const QObjectList& lst = pWid->children();
2361 for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
2363 if ( found && ( (*it)->inherits( "QSplitter" ) ||
2364 (*it)->inherits( "QtxWorkstackArea" ) ) )
2365 moveList.append( (QWidget*)(*it) );
2370 QMap<QWidget*, bool> map;
2371 for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
2373 map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
2374 (*it)->setParent( 0 );
2378 wid->setParent( pWid );
2380 for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
2382 (*itr)->setParent( pWid );
2383 (*itr)->setShown( map.contains( *itr ) ? map[*itr] : false );
2388 \brief Close active window.
2390 void QtxWorkstack::onCloseWindow()
2394 else if( activeWindow() )
2395 activeWindow()->close();
2399 \brief Called when workarea is destroyed.
2401 Set input focus to the neighbour area.
2403 \param obj workarea being destroyed
2405 void QtxWorkstack::onDestroyed( QObject* obj )
2407 QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
2409 if ( area == myArea )
2414 QtxWorkstackArea* cur = neighbourArea( area );
2419 QApplication::postEvent( this, new QEvent( QEvent::User ) );
2423 \brief Called on window activating.
2424 \param area workarea being activated (not used)
2426 void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
2428 const QObject* obj = sender();
2429 if ( !obj->inherits( "QtxWorkstackArea" ) )
2432 setActiveArea( (QtxWorkstackArea*)obj );
2436 \brief Called on window deactivating.
2437 \param area workarea being deactivated
2439 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
2441 if ( myArea != area )
2444 QList<QtxWorkstackArea*> lst;
2445 areas( mySplit, lst, true );
2447 int idx = lst.indexOf( area );
2454 QtxWorkstackArea* newArea = neighbourArea( area );
2455 if ( newArea && newArea->activeWidget() )
2456 newArea->activeWidget()->setFocus();
2458 QApplication::postEvent( this, new QEvent( QEvent::User ) );
2462 \brief Create and show popup menu for workarea.
2464 \param p popup position
2466 void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
2468 QtxWorkstackArea* anArea = ::qobject_cast<QtxWorkstackArea*>( (QObject*)sender() );
2470 anArea = activeArea();
2475 QWidgetList lst = anArea->widgetList();
2476 if ( lst.isEmpty() )
2480 myWorkArea = anArea;
2482 QMenu* pm = new QMenu();
2484 if ( lst.count() > 1 )
2486 if ( myActionsMap[SplitVertical]->isEnabled() )
2487 pm->addAction( myActionsMap[SplitVertical] );
2488 if ( myActionsMap[SplitHorizontal]->isEnabled() )
2489 pm->addAction( myActionsMap[SplitHorizontal] );
2495 if ( myActionsMap[Close]->isEnabled() )
2496 pm->addAction( myActionsMap[Close] );
2497 if ( myActionsMap[Rename]->isEnabled() )
2498 pm->addAction( myActionsMap[Rename] );
2501 Qtx::simplifySeparators( pm );
2503 if ( !pm->actions().isEmpty() )
2513 \brief Add child widget.
2515 \param f widget flags
2516 \return child widget container
2518 QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
2523 return targetArea()->insertWidget( w, -1, f );
2527 \brief Handle custom events.
2528 \param e custom event (not used)
2530 void QtxWorkstack::customEvent( QEvent* /*e*/ )
2536 \brief Get splitter corresponding to the workarea.
2538 \return splitter corresponding to the workarea
2540 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
2545 QSplitter* split = 0;
2547 QWidget* wid = area->parentWidget();
2548 if ( wid && wid->inherits( "QSplitter" ) )
2549 split = (QSplitter*)wid;
2555 \brief Get list of child splitters.
2556 \param split parent splitter
2557 \param splitList list to be filled with child splitters
2558 \param rec if \c true, perform recursive search of children
2560 void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
2565 const QObjectList& objs = split->children();
2566 for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2569 splitters( (QSplitter*)*it, splitList, rec );
2570 if ( (*it)->inherits( "QSplitter" ) )
2571 splitList.append( (QSplitter*)*it );
2576 \brief Get list of child workareas.
2577 \param split parent splitter
2578 \param areaList list to be filled with child workareas
2579 \param rec if \c true, perform recursive search of children
2581 void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
2586 const QObjectList& objs = split->children();
2587 for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2589 if ( (*it)->inherits( "QtxWorkstackArea" ) )
2590 areaList.append( (QtxWorkstackArea*)*it );
2591 else if ( rec && (*it)->inherits( "QSplitter" ) )
2592 areas( (QSplitter*)*it, areaList, rec );
2597 \brief Get active workarea.
2598 \return active workarea
2600 QtxWorkstackArea* QtxWorkstack::activeArea() const
2606 \brief Get target area (for which the current operation should be done).
2608 Returns active workarea or current area (if there is no active workarea).
2609 If there are no workareas, create new workarea and return it.
2613 QtxWorkstackArea* QtxWorkstack::targetArea()
2615 QtxWorkstackArea* area = activeArea();
2617 area = currentArea();
2620 QList<QtxWorkstackArea*> lst;
2621 areas( mySplit, lst );
2622 if ( !lst.isEmpty() )
2627 area = createArea( mySplit );
2633 \brief Get current workarea.
2635 Current workarea is that one which has input focus.
2637 \return current area
2639 QtxWorkstackArea* QtxWorkstack::currentArea() const
2641 QtxWorkstackArea* area = 0;
2642 QWidget* wid = focusWidget();
2643 while ( wid && !area )
2645 if ( wid->inherits( "QtxWorkstackArea" ) )
2646 area = (QtxWorkstackArea*)wid;
2647 wid = wid->parentWidget();
2654 \brief Create new workarea.
2655 \param parent parent widget
2656 \return created workarea
2658 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
2660 QtxWorkstackArea* area = new QtxWorkstackArea( parent );
2662 connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
2663 connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
2664 connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
2665 this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
2666 connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
2672 \brief Set active workarea.
2675 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
2677 QWidget* oldCur = myWin;
2679 QtxWorkstackArea* oldArea = myArea;
2683 if ( myArea != oldArea )
2686 oldArea->updateActiveState();
2688 myArea->updateActiveState();
2692 myWin = myArea->activeWidget();
2694 if ( myWin && oldCur != myWin )
2695 emit windowActivated( myWin );
2699 \brief Get workarea which is nearest to \a area.
2700 \param area area for which neighbour is searched
2701 \return neighbour area (or 0 if not found)
2703 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
2705 QList<QtxWorkstackArea*> lst;
2706 areas( mySplit, lst, true );
2707 int pos = lst.indexOf( area );
2711 QtxWorkstackArea* na = 0;
2712 for ( int i = pos - 1; i >= 0 && !na; i-- )
2714 if ( !lst.at( i )->isEmpty() )
2718 for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
2720 if ( !lst.at( j )->isEmpty() )
2727 \brief Get workarea covering point.
2731 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
2733 QtxWorkstackArea* area = 0;
2734 QList<QtxWorkstackArea*> lst;
2735 areas( mySplit, lst, true );
2736 for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end() && !area; ++it )
2738 QtxWorkstackArea* cur = *it;
2739 QRect r = cur->geometry();
2740 if ( cur->parentWidget() )
2741 r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
2742 if ( r.contains( p ) )
2749 \brief Update internal state.
2751 void QtxWorkstack::updateState()
2753 updateState( mySplit );
2757 \brief Update splitter state.
2758 \param split splitter to be updated
2760 void QtxWorkstack::updateState( QSplitter* split )
2762 QList<QSplitter*> recList;
2763 splitters( split, recList, false );
2764 for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
2765 updateState( *itr );
2767 QList<QSplitter*> splitList;
2768 splitters( split, splitList, false );
2770 QList<QtxWorkstackArea*> areaList;
2771 areas( split, areaList, false );
2774 for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
2776 if ( (*it)->isEmpty() )
2785 if ( split == mySplit )
2788 for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
2789 vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
2791 if ( areaList.isEmpty() && splitList.isEmpty() )
2800 \brief Get splitter info (for debug purposes)
2801 \param split splitter
2802 \param info string to be filled with splitter data.
2804 void QtxWorkstack::splitterInfo( QSplitter* split, QString& info ) const
2809 /*const QObjectList& objs = */split->children(); // VSR: is it needed ???
2812 QList<int> sizes = split->sizes();
2813 for ( QList<int>::iterator sIt = sizes.begin(); sIt != sizes.end(); ++sIt )
2815 if ( *sIt > 1 ) // size 1 pixel usually means empty Workstack area, which will NOT be re-created,
2816 sizesStr += QString( ":%1" ).arg( *sIt ); // so we don't need to store its size
2819 if ( !sizesStr.isEmpty() ) // cut the first ':'
2820 sizesStr = sizesStr.right( sizesStr.length() - 1 );
2822 info += QString( "(splitter orientation=%1 sizes=%3 " ).arg( split->orientation() ).arg( sizesStr );
2824 for( int index = 0, count = split->count(); index < count; index++ )
2826 QObject* obj = split->widget( index );
2827 if ( obj->inherits( "QSplitter" ) )
2828 splitterInfo( (QSplitter*)obj, info );
2829 else if ( obj->inherits( "QtxWorkstackArea" ) )
2831 QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
2832 if ( area->isEmpty() )
2834 info += QString( "(views active='%1'" ).arg( area->activeWidget()->objectName() );
2835 QWidgetList views = area->widgetList();
2836 for ( QWidgetList::iterator wIt = views.begin(); wIt != views.end(); ++wIt )
2837 info += QString( " '%1'" ).arg( (*wIt)->objectName() );
2843 printf( (const char*)QString( info + '\n' ).toLatin1() );
2847 \brief Remove round brackets symbols from the string.
2849 \param parameters string to be processed
2851 static void cutBrackets( QString& parameters )
2853 QChar c1 = parameters[0];
2854 QChar c2 = parameters[int(parameters.length()-1)];
2855 if ( !parameters.isEmpty() && c1 == '(' && c2 == ')' )
2856 parameters = parameters.mid( 1, parameters.length()-2 );
2860 \brief Parse string to get some parameter value.
2863 String \a str can contain the parameters description of kind "<param>=<value> ...".
2866 QString s = "splitter orientation=0 children=2 sizes=332:478";
2867 QString orient_val = getValue( s, "children" ); // orient_val contains "2"
2868 QString size_val = getValue( s, "sizes" ); // val contains "332:478"
2871 \param str string to be processed
2872 \param valName parameter name
2873 \return parameter value (or null QStrinhg if parameter is not found)
2875 static QString getValue( const QString& str, const QString& valName )
2877 int i = str.indexOf( valName );
2880 int equal_i = str.indexOf( '=', i );
2881 if ( equal_i != -1 )
2883 int space_i = str.indexOf( ' ', ++equal_i );
2884 if ( space_i != -1 )
2885 return str.mid( equal_i, space_i - equal_i );
2892 \brief Check format of splitter parameters string.
2894 \param parameters splitter parameters description
2895 \return \c true on success and \c false on error
2897 static bool checkFormat( const QString& parameters )
2899 QString params( parameters );
2900 // 1. begins and ends with brackets
2901 QChar c1 = params[0];
2902 QChar c2 = params[int(params.length()-1)];
2903 bool ok = ( c1 == '(' && c2 == ')' );
2904 if ( !ok ) return ok;
2905 ::cutBrackets( params );
2906 // 2. has splitter word
2907 ok = ( params.left( 8 ) == "splitter" );
2908 if ( !ok ) return ok;
2909 // 3. has children? = '(' is found
2910 int i = params.indexOf( '(' );
2912 if ( !ok ) return ok;
2913 params = params.left( i ); // cut all children, they will be checked later
2914 // 4. has orientation word and correct value
2915 ::getValue( params, "orientation" ).toInt( &ok );
2916 if ( !ok ) return ok;
2917 // 5. has sizes word and values
2918 ok = ! ::getValue( params, "sizes" ).isEmpty();
2919 if ( !ok ) return ok;
2920 // 6. check children -> number of '(' == number of ')' in original string
2921 ok = ( parameters.contains( '(' ) == parameters.contains( ')' ) );
2926 \brief Get splitter's children descriptions from the string.
2929 Child widgets descriptions are separated by '(' and ')' symbols.
2931 \param str string to be processed
2932 \return child widgets descriptions
2934 static QStringList getChildren( const QString& str )
2937 if ( !str.startsWith( "(" ) )
2941 nOpen = 1, // count brackets: '(' increments nOpen, ')' decrements
2943 while ( i < (int)str.length() )
2945 if ( str[i] == '(' )
2951 else if ( str[i] == ')' )
2955 lst.append( str.mid( start, i-start+1 ) );
2964 \brief Get view name by index.
2969 QString s = "views active='AnotherView' 'GLView' 'AnotherView'";
2970 QString a0 = getViewName( s, 0 ); // --> a0 contains "GLView"
2971 QString a1 = getViewName( s, 1 ); // --> a1 contains "AnotherView"
2974 \param str string to be processed
2978 static QString getViewName( const QString& str, int i )
2980 QRegExp exp( "\\s'\\w+'" );
2981 int start = 0; // start index of view name in the string
2982 int num = 0 ; // index of found match
2983 while ( ( start = exp.indexIn( str, start ) ) != -1 && num < i )
2985 start += exp.matchedLength();
2988 if ( start != -1 ) // +2 and -3 avoid starting space and starting and ending ' symbols
2989 return str.mid( start + 2, exp.matchedLength() - 3 );
2995 \brief Get child widget with specified name.
2997 \param parent parent widget
2998 \param aName child widget name
2999 \return child widget or 0 if not found
3001 static QWidget* getView( const QWidget* parent, const QString& aName )
3004 QList<QWidget*> l = qFindChildren<QWidget*>( parent->topLevelWidget(), aName );
3006 view = ::qobject_cast<QWidget*>( l.first() );
3011 \brief Setup splitter according to the specified parameters string.
3012 \param splitter splitter to be set up
3013 \param parameters splitter parameters description
3014 \param sMap map containing resulting child splitters sizes
3016 void QtxWorkstack::setSplitter( QSplitter* splitter, const QString& parameters, QMap<QSplitter*, QList<int> >& sMap )
3018 printf( QString( parameters + '\n' ).toLatin1() );
3019 if ( !::checkFormat( parameters ) ) {
3020 printf( "\nInvalid format of workstack parameters. Positions of viewers can not be restored.\n" );
3024 QString params( parameters );
3025 ::cutBrackets( params );
3027 // get splitter sizes and store it in the map for future setting
3029 QStringList sizesLst = ::getValue( params, "sizes" ).split( ':', QString::SkipEmptyParts );
3030 QStringList::Iterator it;
3031 for ( it = sizesLst.begin(); it != sizesLst.end(); ++it )
3032 sizes.append( (*it).toInt() );
3033 sMap[ splitter ] = sizes;
3035 // set orientation of splitter
3036 int orient = ::getValue( params, "orientation" ).toInt();
3037 splitter->setOrientation( (Qt::Orientation)orient );
3040 QString options = params.left( params.indexOf( '(' ) );
3041 QString childrenStr = params.right( params.length()-options.length() );
3042 QStringList children = ::getChildren( childrenStr );
3045 // printf (" splitter orient=%d, sizes_count=%d, children=%d\n", orient, sizes.count(), children.count() );
3046 // for ( QStringList::Iterator tit = children.begin(); tit != children.end(); ++tit )
3047 // printf (" |-> child = [%s]\n", (*tit).latin1() );
3049 for ( it = children.begin(); it != children.end(); ++it )
3051 if ( (*it).startsWith( "(splitter" ) )
3053 QSplitter* newSplitter = new QSplitter( splitter );
3054 setSplitter( newSplitter, *it, sMap );
3056 else if ( (*it).startsWith( "(views" ) )
3058 QtxWorkstackArea* newArea = createArea( splitter );
3059 QString activeViewName = ::getValue( *it, "active" );
3060 QWidget* activeView = 0;
3061 activeViewName = activeViewName.mid( 1, activeViewName.length()-2 ); // chop off ' symbols
3063 QString viewName = ::getViewName( *it, i );
3064 while ( !viewName.isEmpty() )
3066 if ( QWidget* view = ::getView( splitter, viewName ) )
3068 newArea->insertWidget( view );
3069 if ( activeViewName == view->objectName() )
3072 viewName = ::getViewName( *it, ++i );
3075 newArea->setActiveWidget( activeView );
3081 \brief Restore workstack configuration from the state description string.
3082 \param parameters workstack state description
3083 \return reference to this workstack
3085 QtxWorkstack& QtxWorkstack::operator<<( const QString& parameters )
3087 // clear the main splitter - remove all child splitters and empty areas from it
3088 QList<QSplitter*> splitList;
3089 QList<QtxWorkstackArea*> areaList;
3090 splitters( mySplit, splitList, false );
3091 areas( mySplit, areaList, false );
3092 for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end(); ++iter )
3095 for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
3097 if ( (*it)->isEmpty() )
3101 // restore splitter recursively
3102 QMap< QSplitter*, QList<int> > sMap;
3103 setSplitter( mySplit, parameters, sMap );
3105 // now mySplit may contains empty area (where all views were located before restoring)
3106 // in order setSize to work correctly we have to exclude this area
3108 areas( mySplit, areaList, false );
3109 for ( QList<QtxWorkstackArea*>::iterator delIt = areaList.begin(); delIt != areaList.end(); ++delIt )
3111 if ( (*delIt)->isEmpty() )
3115 QApplication::instance()->processEvents();
3117 // restore splitters' sizes (map of sizes is filled in setSplitters)
3118 for ( QMap< QSplitter*, QList<int> >::iterator itm = sMap.begin(); itm != sMap.end(); ++itm )
3119 itm.key()->setSizes( itm.value() );
3125 \brief Dump workstack configuration to the state description string.
3126 \param parameters resulting workstack state description
3127 \return reference to this workstack
3129 QtxWorkstack& QtxWorkstack::operator>>( QString& outParameters )
3131 splitterInfo( mySplit, outParameters );
3136 \fn void QtxWorkstack::windowActivated( QWidget* w )
3137 \brief Emitted when the workstack's child widget \w is activated.
3138 \param w widget being activated
3142 \brief Gets area containing given widget
3144 \return pointer to QtxWorkstackArea* object
3146 QtxWorkstackArea* QtxWorkstack::wgArea( QWidget* wid ) const
3148 QtxWorkstackArea* resArea = 0;
3150 QList<QtxWorkstackArea*> areaList;
3151 areas( mySplit, areaList, true );
3153 QList<QtxWorkstackArea*>::ConstIterator it;
3154 for ( it = areaList.begin(); it != areaList.end() && !resArea; ++it )
3156 if ( (*it)->contains( wid ) )
3164 \brief Moves the first widget to the same area which the second widget belongs to
3165 \param wid widget to be moved
3166 \param wid_to widget specified the destination area
3167 \param before specifies whether the first widget has to be moved before or after
3169 \return TRUE if operation is completed successfully, FALSE otherwise
3171 bool QtxWorkstack::move( QWidget* wid, QWidget* wid_to, const bool before )
3173 if ( wid && wid_to )
3175 QtxWorkstackArea* area_src = wgArea( wid );
3176 QtxWorkstackArea* area_to = wgArea( wid_to );
3177 if ( area_src && area_to )
3179 // find index of the second widget
3180 QWidgetList wgList = area_to->widgetList();
3181 QWidgetList::ConstIterator it;
3183 for ( it = wgList.begin(); it != wgList.begin(); ++it, idx++ )
3185 if ( *it == wid_to )
3189 if ( idx < wgList.count() ) // paranoidal check
3193 area_src->removeWidget( wid, true );
3194 area_to->insertWidget( wid, idx );
3203 \brief Group all windows in one area
3204 \return TRUE if operation is completed successfully, FALSE otherwise
3206 void QtxWorkstack::stack()
3208 QWidgetList wgList = windowList();
3209 if ( !wgList.count() )
3210 return; // nothing to do
3212 QtxWorkstackArea* area_to = 0;
3213 QWidgetList::ConstIterator it;
3214 for ( it = wgList.begin(); it != wgList.end(); ++it )
3216 QtxWorkstackArea* area_src = 0;
3219 area_to = wgArea( *it );
3223 area_src = wgArea( *it );
3225 if ( area_src != area_to )
3227 area_src->removeWidget( *it, true );
3228 area_to->insertWidget( *it, -1 );