1 // Copyright (C) 2007-2015 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, or (at your option) any later version.
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
23 // File: QtxWorkstack.cxx
24 // Author: Sergey TELKOV
26 #include "QtxWorkstack.h"
28 #include "QtxAction.h"
35 #include <QDataStream>
36 #include <QFocusEvent>
37 #include <QMouseEvent>
38 #include <QRubberBand>
39 #include <QApplication>
40 #include <QStyleOption>
41 #include <QInputDialog>
42 #include <QStackedWidget>
43 #include <QAbstractButton>
46 \class QtxWorkstackArea::WidgetEvent
48 \brief Internal class used to forward child widgets events to the workarea
51 class QtxWorkstackArea::WidgetEvent : public QEvent
54 WidgetEvent( Type t, QtxWorkstackChild* w = 0 ) : QEvent( t ), myChild( w ) {};
56 QtxWorkstackChild* child() const { return myChild; }
59 QtxWorkstackChild* myChild; // event receiver widget
63 \class QtxWorkstackArea::RestoreEvent
65 \brief Internal class used to forward restore info events to the workarea
68 class QtxWorkstackArea::RestoreEvent : public QtxWorkstackArea::WidgetEvent
71 RestoreEvent( Type t, int id, int f, QtxWorkstackChild* w )
72 : WidgetEvent( t, w ), myId( id ), myFlags( f ) {};
74 int id() const { return myId; }
75 int flags() const { return myFlags; }
83 \class QtxWorkstackDrag
85 \brief Workstack drag object
90 \param ws parent workstack
91 \param child child widget container
93 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
102 QApplication::instance()->installEventFilter( this );
108 QtxWorkstackDrag::~QtxWorkstackDrag()
110 QApplication::instance()->removeEventFilter( this );
116 \brief Custom event filter.
117 \param o event receiver widget
119 \return \c true if event should be filtered (stop further processing)
121 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
125 case QEvent::MouseMove:
126 updateTarget( ((QMouseEvent*)e)->globalPos() );
128 case QEvent::MouseButtonRelease:
141 \brief Detect and set dropping target widget.
142 \param p current dragging position
144 void QtxWorkstackDrag::updateTarget( const QPoint& p )
147 QtxWorkstackArea* area = detectTarget( p, tab );
148 setTarget( area, tab );
152 \brief Detect dropping target.
153 \param p current dragging position
154 \param tab resulting target tab page index
155 \return target workarea or 0 if there is no correct drop target
157 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
162 QtxWorkstackArea* area = myWS->areaAt( p );
164 tab = area->tabAt( p );
169 \brief Set dropping target.
170 \param area new target workarea
171 \param tab target workarea's tab page index
173 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
175 if ( !area || ( myArea == area && tab == myTab ) )
191 \brief Called when drop operation is finished.
193 Inserts dropped widget to the target workarea.
195 void QtxWorkstackDrag::dropWidget()
198 myArea->insertChild( myChild, myTab );
202 \brief Draw floating rectangle.
204 void QtxWorkstackDrag::drawRect()
209 QRect r = myArea->floatRect();
212 r.setTop( r.top() + m + 2 );
213 r.setLeft( r.left() + m + 2 );
214 r.setRight( r.right() - m - 2 );
215 r.setBottom( r.bottom() - m - 2 );
219 myAreaRect->setGeometry( r );
220 myAreaRect->setVisible( r.isValid() );
223 QRect tr = myArea->floatTab( myTab );
225 tr.setTop( tr.top() + m );
226 tr.setLeft( tr.left() + m );
227 tr.setRight( tr.right() - m );
228 tr.setBottom( tr.bottom() - m );
232 myTabRect->setGeometry( tr );
233 myTabRect->setVisible( tr.isValid() );
238 \brief Delete rubber band on the end on the dragging operation.
240 void QtxWorkstackDrag::endDrawRect()
250 \brief Create rubber band to be drawn on the dragging operation.
252 void QtxWorkstackDrag::startDrawRect()
255 myTabRect = new QRubberBand( QRubberBand::Rectangle );
260 myAreaRect = new QRubberBand( QRubberBand::Rectangle );
268 \brief Workstack area close button.
271 class CloseButton : public QAbstractButton
274 CloseButton( QWidget* );
276 QSize sizeHint() const;
277 QSize minimumSizeHint() const;
279 void enterEvent( QEvent* );
280 void leaveEvent( QEvent* );
281 void paintEvent( QPaintEvent* );
287 \param parent parent widget
289 CloseButton::CloseButton( QWidget* parent )
290 : QAbstractButton( parent )
292 setFocusPolicy( Qt::NoFocus );
296 \brief Get appropriate size for the button.
300 QSize CloseButton::sizeHint() const
304 if( !icon().isNull() )
306 const QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ),
308 dim = qMax( pm.width(), pm.height() );
310 return QSize( dim + 4, dim + 4 );
314 \brief Get minimum appropriate size for the button.
316 \return minimum size value
318 QSize CloseButton::minimumSizeHint() const
324 \brief Process mouse enter event.
326 \param event mouse enter event
328 void CloseButton::enterEvent( QEvent *event )
332 QAbstractButton::enterEvent( event );
336 \brief Process mouse leave event.
338 \param event mouse leave event
340 void CloseButton::leaveEvent( QEvent *event )
344 QAbstractButton::leaveEvent( event );
348 \brief Process paint event.
350 \param event paint event
352 void CloseButton::paintEvent( QPaintEvent* )
359 opt.state |= QStyle::State_AutoRaise;
360 if ( isEnabled() && underMouse() && !isChecked() && !isDown() )
361 opt.state |= QStyle::State_Raised;
363 opt.state |= QStyle::State_On;
365 opt.state |= QStyle::State_Sunken;
366 style()->drawPrimitive( QStyle::PE_PanelButtonTool, &opt, &p, this );
368 int shiftHorizontal = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftHorizontal, &opt, this ) : 0;
369 int shiftVertical = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftVertical, &opt, this ) : 0;
371 r.adjust( 2, 2, -2, -2 );
372 r.translate( shiftHorizontal, shiftVertical );
374 QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ), isEnabled() ?
375 underMouse() ? QIcon::Active : QIcon::Normal
377 isDown() ? QIcon::On : QIcon::Off );
378 style()->drawItemPixmap( &p, r, Qt::AlignCenter, pm );
383 \class QtxWorkstackSplitter
385 \brief Workstack splitter.
390 \param parent parent widget
392 QtxWorkstackSplitter::QtxWorkstackSplitter( QWidget* parent )
393 : QSplitter( parent )
395 setChildrenCollapsible( false );
401 QtxWorkstackSplitter::~QtxWorkstackSplitter()
406 \brief Get parent workstack
407 \return workstack owning this workarea
409 QtxWorkstack* QtxWorkstackSplitter::workstack() const
411 QtxWorkstack* ws = 0;
412 QWidget* wid = parentWidget();
415 ws = ::qobject_cast<QtxWorkstack*>( wid );
416 wid = wid->parentWidget();
422 \brief Save the widget area configuration into data stream.
424 void QtxWorkstackSplitter::saveState( QDataStream& stream ) const
426 stream << QtxWorkstack::SplitMarker;
429 if ( orientation() == Qt::Horizontal )
430 flags |= QtxWorkstack::Horizontal;
435 QList<int> sz = sizes();
436 for ( QList<int>::const_iterator it = sz.begin(); it != sz.end(); ++it )
439 for ( int i = 0; i < count(); i++ )
441 QWidget* wid = widget( i );
442 QtxWorkstackSplitter* split = ::qobject_cast<QtxWorkstackSplitter*>( wid );
444 split->saveState( stream );
447 QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( wid );
449 area->saveState( stream );
455 \brief Restore the widget area configuration from data stream info.
456 \return \c true in successful case.
458 bool QtxWorkstackSplitter::restoreState( QDataStream& stream, QMap<QString, QtxWorkstackChild*>& map )
466 setOrientation( flags & QtxWorkstack::Horizontal ? Qt::Horizontal : Qt::Vertical );
469 for ( int s = 0; s < num; s++ )
477 for ( int i = 0; i < num && ok; i++ )
482 if ( stream.status() != QDataStream::Ok )
485 if ( marker == QtxWorkstack::SplitMarker )
487 QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
489 split->setVisible( true );
491 ok = split->restoreState( stream, map );
493 else if ( marker == QtxWorkstack::AreaMarker )
495 QtxWorkstack* ws = workstack();
496 QtxWorkstackArea* area = ws->createArea( this );
498 area->setVisible( true );
500 ok = area->restoreState( stream, map );
513 \class QtxWorkstackArea
515 \brief Workstack widget workarea.
520 \param parent parent widget
522 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
525 setFrameStyle( QFrame::Panel | QFrame::Sunken );
527 QVBoxLayout* base = new QVBoxLayout( this );
528 base->setMargin( frameWidth() );
529 base->setSpacing( 0 );
531 QWidget* top = new QWidget( this );
532 base->addWidget( top );
534 QHBoxLayout* tl = new QHBoxLayout( top );
537 myBar = new QtxWorkstackTabBar( top );
538 tl->addWidget( myBar, 1 );
540 CloseButton* close = new CloseButton( top );
541 close->setIcon( style()->standardIcon( QStyle::SP_TitleBarCloseButton ) );
543 tl->addWidget( myClose );
545 myStack = new QStackedWidget( this );
547 base->addWidget( myStack, 1 );
549 connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
550 connect( myBar, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
551 connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
552 connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
558 QApplication::instance()->installEventFilter( this );
564 QtxWorkstackArea::~QtxWorkstackArea()
566 QApplication::instance()->removeEventFilter( this );
570 \brief Check if workarea contains any widgets.
571 \return \c true if area is null (havn't any child widgets)
573 bool QtxWorkstackArea::isNull() const
575 return myList.isEmpty();
579 \brief Check if workarea contains visible widgets.
580 \return \c true if area is empty (all child widgets are removed or now shown)
582 bool QtxWorkstackArea::isEmpty() const
585 for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !res; ++it )
586 res = (*it)->visibility();
591 \brief Add widget to the workarea.
592 \param wid widget to be added
593 \param idx position in the area widget to be added to
594 \param f widget flags
595 \return child widget container object (or 0 if index is invalid)
597 QtxWorkstackChild* QtxWorkstackArea::insertWidget( QWidget* wid, const int idx, Qt::WindowFlags f )
602 QtxWorkstackChild* c = child( wid );
604 c = new QtxWorkstackChild( wid, myStack, f );
606 insertChild( c, idx );
611 void QtxWorkstackArea::insertChild( QtxWorkstackChild* child, const int idx )
616 QtxWorkstackArea* a = child->area();
617 if ( a && a != this )
618 a->removeChild( child, false );
620 int pos = myList.indexOf( child );
621 if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
624 bool found = myList.contains( child );
626 myList.removeAll( child );
627 pos = idx < 0 ? myList.count() : idx;
628 myList.insert( qMin( pos, (int)myList.count() ), child );
633 for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !hasId; ++it )
634 hasId = (*it)->id() == child->id();
636 if ( hasId || child->id() < 0 )
637 child->setId( generateId() );
639 connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
640 connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
641 connect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
642 connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
643 connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
648 setWidgetActive( child->widget() );
649 child->widget()->setFocus();
653 \brief Create and show popup menu for the area.
654 \param p mouse pointer position at which popup menu should be shown
656 void QtxWorkstackArea::onContextMenuRequested( QPoint p )
658 const QtxWorkstackTabBar* bar = ::qobject_cast<const QtxWorkstackTabBar*>( sender() );
663 int idx = tabAt( p );
665 wid = widget( myBar->tabId( idx ) );
667 emit contextMenuRequested( wid, p );
671 \brief Remove widget from workarea.
672 \param wid widget to be removed
673 \param del if \c true the widget should be also deleted
675 void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
677 removeChild( child( wid ), del );
681 \brief Remove child from workarea.
682 \param child child to be removed
683 \param del if \c true the widget should be also deleted
685 void QtxWorkstackArea::removeChild( QtxWorkstackChild* child, const bool del )
687 if ( !myList.contains( child ) )
690 myStack->removeWidget( child );
692 if ( myBar->indexOf( child->id() ) != -1 )
693 myBar->removeTab( myBar->indexOf( child->id() ) );
695 myList.removeAll( child );
699 else if ( child->widget() )
701 disconnect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
702 disconnect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
703 disconnect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
704 disconnect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
705 disconnect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
714 QList<QtxWorkstackChild*> QtxWorkstackArea::childList() const
720 \brief Get all visible child widgets.
721 \return list of visible child widgets
723 QWidgetList QtxWorkstackArea::widgetList() const
726 for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
728 QtxWorkstackChild* c = *it;
729 if ( c->visibility() )
730 lst.append( c->widget() );
736 \brief Get active child widget.
737 \return active widget
739 QWidget* QtxWorkstackArea::activeWidget() const
741 return widget( myBar->tabId( myBar->currentIndex() ) );
745 \brief Set active widget.
746 \param wid widget to be made active
748 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
750 myBar->setCurrentIndex( myBar->indexOf( widgetId( wid ) ) );
754 \brief Check if area owns the specified widget.
755 \param wid widget to be checked
756 \return \c true if area contains widget
758 bool QtxWorkstackArea::contains( QWidget* wid ) const
764 \brief Check if workarea is active.
765 \return \c true if area is active
767 bool QtxWorkstackArea::isActive() const
769 QtxWorkstack* ws = workstack();
773 return ws->activeArea() == this;
777 \brief Update active tab bar state (active/inactive).
779 void QtxWorkstackArea::updateActiveState()
781 myBar->setActive( isActive() );
785 \brief Get parent workstack
786 \return workstack owning this workarea
788 QtxWorkstack* QtxWorkstackArea::workstack() const
790 QtxWorkstack* ws = 0;
791 QWidget* wid = parentWidget();
794 ws = ::qobject_cast<QtxWorkstack*>( wid );
795 wid = wid->parentWidget();
801 \brief Custom event filter.
803 Process events from child widgets.
805 \param o event receiver widget
807 \return \c true if event should be filtered (stop further processing)
809 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
811 if ( o->isWidgetType() )
813 QWidget* wid = (QWidget*)o;
814 if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
817 while ( !ok && wid && wid != myClose )
820 wid = wid->parentWidget();
823 QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
830 \brief Save the own widgets configuration into data stream.
832 void QtxWorkstackArea::saveState( QDataStream& stream ) const
834 stream << QtxWorkstack::AreaMarker;
835 stream << myList.count();
836 stream << myBar->tabId( myBar->currentIndex() );
837 for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
839 QtxWorkstackChild* c = *it;
841 stream << QtxWorkstack::WidgetMarker;
843 stream << c->widget()->objectName();
847 if ( c->visibility() )
848 flags |= QtxWorkstack::Visible;
855 \brief Restore the widgets configuration from data stream info.
856 \return \c true in successful case.
858 bool QtxWorkstackArea::restoreState( QDataStream& stream, QMap<QString, QtxWorkstackChild*>& map )
866 QtxWorkstackChild* curChild = 0;
867 for ( int i = 0; i < num; i++ )
872 if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::WidgetMarker )
884 QtxWorkstackChild* c = map.contains( name ) ? map[name] : 0;
887 qWarning( "QtxWorkstack: Restored child widget \"%s\" not found.", (const char*)name.toLatin1() );
896 QApplication::postEvent( this, new RestoreEvent( (QEvent::Type)RestoreWidget, id, flags, c ) );
900 QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)MakeCurrent, curChild ) );
906 \brief Get rectangle to be drawn when highlighting drop area.
907 \return area drop rectangle
909 QRect QtxWorkstackArea::floatRect() const
911 QRect r = myStack->geometry();
912 return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
916 \brief Get rectangle to be drawn when highlighting drop area on tab bar.
918 \return tab bar drop rectrangle
920 QRect QtxWorkstackArea::floatTab( const int idx ) const
922 QRect r = myBar->tabRect( idx );
923 return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
927 \brief Get tab index by point.
929 \return tab covering point or -1 if there is no tab covering point
931 int QtxWorkstackArea::tabAt( const QPoint& pnt ) const
934 QPoint p = myBar->mapFromGlobal( pnt );
935 for ( int i = 0; i < myBar->count() && idx == -1; i++ )
937 QRect r = myBar->tabRect( i );
938 if ( r.isValid() && r.contains( p ) )
945 \brief Event handler for custom events.
946 \param e custom event
948 void QtxWorkstackArea::customEvent( QEvent* e )
950 WidgetEvent* we = (WidgetEvent*)e;
952 switch ( we->type() )
955 myBar->updateActiveState();
956 // IMN 27/03/2015: This workaround caused by the bug INT PAL 0052623: OCC view blinking when
957 // using polyline sketcher which is reproduced on Unix systems with qt-4.8.4.
958 myStack->setUpdatesEnabled( false );
960 myStack->setUpdatesEnabled( true );
961 emit activated( activeWidget() );
964 if ( activeWidget() )
966 if ( !activeWidget()->focusWidget() )
967 activeWidget()->setFocus();
970 if ( activeWidget()->focusWidget()->hasFocus() )
972 QFocusEvent in( QEvent::FocusIn );
973 QApplication::sendEvent( this, &in );
977 activeWidget()->focusWidget()->setFocus();
978 myBar->updateActiveState();
984 if ( we->child()->widget() )
985 setActiveWidget( we->child()->widget() );
990 QtxWorkstackChild* c = we->child();
991 RestoreEvent* re = (RestoreEvent*)we;
993 c->widget()->setVisible( re->flags() & QtxWorkstack::Visible );
994 c->setId( re->id() );
1004 \brief Customize focus in event handler.
1005 \param e focus in event
1007 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
1009 QFrame::focusInEvent( e );
1011 myBar->updateActiveState();
1013 emit activated( activeWidget() );
1017 \brief Customize mouse press event handler.
1018 \param e mouse press event
1020 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
1022 QFrame::mousePressEvent( e );
1024 emit activated( activeWidget() );
1028 \brief Called when user presses "Close" button.
1030 void QtxWorkstackArea::onClose()
1032 QWidget* wid = activeWidget();
1038 \brief Called when user selects any tab page.
1039 \param idx tab page index (not used)
1041 void QtxWorkstackArea::onCurrentChanged( int /*idx*/ )
1045 emit activated( activeWidget() );
1049 \brief Called when user starts tab page dragging.
1051 void QtxWorkstackArea::onDragActiveTab()
1053 QtxWorkstackChild* c = child( activeWidget() );
1057 new QtxWorkstackDrag( workstack(), c );
1061 \brief Called when area's child widget container is destroyed.
1062 \param obj widget container being destroyed
1064 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
1066 removeChild( (QtxWorkstackChild*)obj, false );
1070 \brief Called when child widget container is shown.
1071 \param c child widget container being shown
1073 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
1079 \brief Called when child widget container is hidden.
1080 \param c child widget container being hidden
1082 void QtxWorkstackArea::onChildHidden( QtxWorkstackChild* c )
1088 \brief Called when child widget container is activated.
1089 \param c child widget container being activated
1091 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
1093 setWidgetActive( c->widget() );
1097 \brief Called when child widget container's title is changed.
1098 \param c child widget container which title is changed
1100 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
1102 updateTab( c->widget() );
1106 \brief Update current child widget container.
1108 Raises widget when active tab page is changed.
1110 void QtxWorkstackArea::updateCurrent()
1112 QWidget* cur = child( myBar->tabId( myBar->currentIndex() ) );
1114 myStack->setCurrentWidget( cur );
1118 \brief Update tab bar.
1119 \param wid tab page widget
1121 void QtxWorkstackArea::updateTab( QWidget* wid )
1123 int idx = myBar->indexOf( widgetId( wid ) );
1127 myBar->setTabIcon( idx, wid->windowIcon() );
1128 myBar->setTabText( idx, wid->windowTitle() );
1132 \brief Get child widget by specified identifier.
1134 \return widget or 0, if identifier in invalid
1136 QWidget* QtxWorkstackArea::widget( const int id ) const
1138 QtxWorkstackChild* c = child( id );
1140 return c ? c->widget() : 0;
1144 \brief Get child widget identifier.
1146 \return widget ID or -1 if widget is not found
1148 int QtxWorkstackArea::widgetId( QWidget* wid ) const
1150 QtxWorkstackChild* c = child( wid );
1152 return c ? c->id() : -1;
1156 \brief Set active child widget.
1157 \param wid widget to be set active
1159 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
1161 int id = widgetId( wid );
1165 myBar->setCurrentIndex( myBar->indexOf( id ) );
1169 \brief Update internal state.
1171 void QtxWorkstackArea::updateState()
1173 bool updBar = myBar->updatesEnabled();
1174 bool updStk = myStack->updatesEnabled();
1175 myBar->setUpdatesEnabled( false );
1176 myStack->setUpdatesEnabled( false );
1178 bool block = myBar->signalsBlocked();
1179 myBar->blockSignals( true );
1181 QWidget* prev = activeWidget();
1184 for ( ChildList::iterator it = myList.begin(); it != myList.end(); ++it )
1186 QtxWorkstackChild* cont = *it;
1187 QWidget* wid = cont->widget();;
1188 int id = cont->id();
1193 bool vis = cont->visibility();
1195 int cIdx = myBar->indexOf( id );
1196 if ( cIdx != -1 && ( !vis || myBar->indexOf( id ) != idx ) )
1197 myBar->removeTab( cIdx );
1199 if ( myBar->indexOf( id ) == -1 && vis )
1200 myBar->setTabId( myBar->insertTab( idx, wid->windowTitle() ), id );
1205 myStack->removeWidget( cont );
1206 else if ( myStack->indexOf( cont ) < 0 )
1207 myStack->addWidget( cont );
1213 int curId = widgetId( prev );
1214 if ( myBar->indexOf( curId ) < 0 )
1216 QtxWorkstackChild* c = 0;
1217 int pos = myList.indexOf( child( prev ) );
1218 for ( int i = pos - 1; i >= 0 && !c; i-- )
1220 if ( myList.at( i )->visibility() )
1224 for ( int j = pos + 1; j < (int)myList.count() && !c; j++ )
1226 if ( myList.at( j )->visibility() )
1234 myBar->setCurrentIndex( myBar->indexOf( curId ) );
1236 myBar->blockSignals( block );
1240 myBar->updateActiveState();
1242 myBar->setUpdatesEnabled( updBar );
1243 myStack->setUpdatesEnabled( updStk );
1249 QResizeEvent re( myBar->size(), myBar->size() );
1250 QApplication::sendEvent( myBar, &re );
1252 myBar->updateGeometry();
1257 emit deactivated( this );
1262 if ( prev != activeWidget() )
1263 emit activated( activeWidget() );
1268 \brief Generate unique widget identifier.
1269 \return first non shared widget ID
1271 int QtxWorkstackArea::generateId() const
1275 for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
1276 map.insert( (*it)->id(), 0 );
1279 while ( map.contains( id ) )
1286 \brief Get child widget container.
1287 \param wid child widget
1288 \return child widget container corresponding to the \a wid
1290 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1292 QtxWorkstackChild* res = 0;
1293 for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !res; ++it )
1295 if ( (*it)->widget() == wid )
1301 QtxWorkstackChild* QtxWorkstackArea::child( const int id ) const
1303 QtxWorkstackChild* c = 0;
1304 for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !c; ++it )
1306 if ( (*it)->id() == id )
1313 \fn void QtxWorkstackArea::activated( QWidget* w )
1314 \brief Emitted when child widget is activated.
1315 \param w child widget being activated
1319 \fn void QtxWorkstackArea::contextMenuRequested( QWidget* w, QPoint p )
1320 \brief Emitted when context popup menu is requested.
1321 \param w child widget popup menu requested for
1322 \param p point popup menu to be shown at
1326 \fn void QtxWorkstackArea::deactivated( QtxWorkstackArea* wa )
1327 \brief Emitted when workarea is deactivated.
1328 \param wa workarea being deactivated
1332 \class QtxWorkstackChild
1334 \brief Workarea child widget container.
1339 \param wid child widget
1340 \param parent parent widget
1341 \param f widget flags
1343 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent, Qt::WindowFlags f )
1344 : QWidget( parent ),
1350 myWidget->setParent( this, f );
1351 myWidget->installEventFilter( this );
1352 if ( myWidget->focusProxy() )
1353 myWidget->focusProxy()->installEventFilter( this );
1354 myWidget->setVisible( myWidget->isVisibleTo( myWidget->parentWidget() ) );
1356 QVBoxLayout* base = new QVBoxLayout( this );
1357 base->setMargin( 0 );
1358 base->addWidget( myWidget );
1360 connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1367 QtxWorkstackChild::~QtxWorkstackChild()
1369 QApplication::instance()->removeEventFilter( this );
1374 disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1377 widget()->removeEventFilter( this );
1378 if ( widget()->focusProxy() )
1379 widget()->focusProxy()->removeEventFilter( this );
1381 widget()->setParent( 0 );
1385 \brief Get child widget.
1386 \return child widget
1388 QWidget* QtxWorkstackChild::widget() const
1394 \brief Returns the id.
1396 int QtxWorkstackChild::id() const
1404 void QtxWorkstackChild::setId( const int id )
1410 \brief Returns true if this child window should be visible.
1412 bool QtxWorkstackChild::visibility()
1414 return myWidget ? myWidget->isVisibleTo( this ) : false;
1417 QtxWorkstackArea* QtxWorkstackChild::area() const
1419 QtxWorkstackArea* a = 0;
1420 QWidget* w = parentWidget();
1423 a = ::qobject_cast<QtxWorkstackArea*>( w );
1424 w = w->parentWidget();
1431 \brief Custom event filter.
1433 Process events from child widgets.
1435 \param o event receiver widget
1437 \return \c true if event should be filtered (stop further processing)
1439 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1441 if ( o->isWidgetType() )
1443 if ( e->type() == QEvent::WindowTitleChange || e->type() == QEvent::WindowIconChange )
1444 emit captionChanged( this );
1446 if ( !e->spontaneous() && e->type() == QEvent::ShowToParent )
1449 if ( !e->spontaneous() && e->type() == QEvent::HideToParent )
1450 emit hidden( this );
1452 if ( e->type() == QEvent::FocusIn )
1453 emit activated( this );
1455 return QWidget::eventFilter( o, e );
1459 \brief Called when child widget is destroyed.
1460 \param obj child widget being destroyed
1462 void QtxWorkstackChild::onDestroyed( QObject* obj )
1468 \brief Customize child event handler.
1469 \param e child event
1471 void QtxWorkstackChild::childEvent( QChildEvent* e )
1473 if ( e->removed() && e->child() == widget() )
1478 QWidget::childEvent( e );
1482 \fn void QtxWorkstackChild::shown( QtxWorkstackChild* w )
1483 \brief Emitted when child widget is shown.
1484 \param w child widget container
1488 \fn void QtxWorkstackChild::hidden( QtxWorkstackChild* w )
1489 \brief Emitted when child widget is hidden.
1490 \param w child widget container
1494 \fn void QtxWorkstackChild::activated( QtxWorkstackChild* w )
1495 \brief Emitted when child widget is activated.
1496 \param w child widget container
1500 \fn void QtxWorkstackChild::captionChanged( QtxWorkstackChild* w )
1501 \brief Emitted when child widget's title is changed.
1502 \param w child widget container
1506 \class QtxWorkstackTabBar
1508 \brief Workstack tab bar widget
1513 \param parent parent widget
1515 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1516 : QTabBar( parent ),
1517 myId( -1 ),myActive(false)
1519 setDrawBase( true );
1520 setElideMode( Qt::ElideNone );
1522 connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
1528 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1533 \brief Get tab page identifier.
1534 \param index tab page index
1535 \return tab page ID or -1 if \a index is out of range
1537 int QtxWorkstackTabBar::tabId( const int index ) const
1539 QVariant v = tabData( index );
1540 if ( !v.canConvert( QVariant::Int ) )
1546 \brief Set tab page identifier.
1547 \param index tab page index
1548 \param id tab page ID
1550 void QtxWorkstackTabBar::setTabId( const int index, const int id )
1552 setTabData( index, id );
1556 \brief Get tab page index by specified identifier.
1557 \param id tab page ID
1558 \return tab page index or -1 if not found
1560 int QtxWorkstackTabBar::indexOf( const int id ) const
1563 for ( int i = 0; i < (int)count() && index < 0; i++ )
1565 if ( tabId( i ) == id )
1572 \brief Check if the tab bar is active.
1573 \return \c true if tab bar is active
1575 bool QtxWorkstackTabBar::isActive() const
1581 \brief Set tab bar active/inactive.
1582 \param on new active state
1584 void QtxWorkstackTabBar::setActive( const bool on )
1586 if ( myActive == on )
1590 updateActiveState();
1594 \brief Update tab bar according to the 'active' state.
1596 void QtxWorkstackTabBar::updateActiveState()
1598 QColor bc = palette().color( QPalette::Text );
1599 QColor ac = isActive() ? palette().color( QPalette::Highlight ) : bc;
1600 for ( int i = 0; i < (int)count(); i++ )
1601 setTabTextColor( i, currentIndex() == i ? ac : bc );
1605 \brief Called when current tab page is changed.
1606 \param idx tab page index (not used)
1608 void QtxWorkstackTabBar::onCurrentChanged( int /*index*/ )
1610 updateActiveState();
1614 \brief Customize mouse move event handler.
1615 \param e mouse event
1617 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1619 if ( myId != -1 && !tabRect( indexOf( myId ) ).contains( e->pos() ) )
1622 emit dragActiveTab();
1625 QTabBar::mouseMoveEvent( e );
1629 \brief Customize mouse press event handler.
1630 \param e mouse event
1632 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1634 QTabBar::mousePressEvent( e );
1636 if ( e->button() == Qt::LeftButton )
1637 myId = tabId( currentIndex() );
1641 \brief Customize mouse release event handler.
1642 \param e mouse event
1644 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1646 QTabBar::mouseReleaseEvent( e );
1650 if ( e->button() == Qt::RightButton )
1651 emit contextMenuRequested( e->globalPos() );
1655 \brief Customize context menu event handler.
1656 \param e context menu event
1658 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1660 if ( e->reason() != QContextMenuEvent::Mouse )
1661 emit contextMenuRequested( e->globalPos() );
1665 \brief Process widget change state events (style, palette, enable state changing, etc).
1666 \param e change event (not used)
1668 void QtxWorkstackTabBar::changeEvent( QEvent* /*e*/ )
1670 updateActiveState();
1674 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1676 if ( currentTab() != t->identifier() )
1678 QFont fnt = p->font();
1679 fnt.setUnderline( false );
1682 QTabBar::paintLabel( p, br, t, has_focus );
1687 \fn void QtxWorkstackTabBar::dragActiveTab()
1688 \brief Emitted when dragging operation is started.
1692 \fn void QtxWorkstackTabBar::contextMenuRequested( QPoint p )
1693 \brief Emitted when context popup menu is requested.
1694 \param p point popup menu to be shown at
1699 \brief Workstack widget.
1701 Organizes the child widgets in the tabbed space.
1702 Allows splitting the working area to arrange the child widgets in
1703 arbitrary way. Any widgets can be moved to another working area with
1704 drag-n-drop operation.
1706 This widget can be used as workspace of the application main window,
1707 for example, as kind of implementation of multi-document interface.
1712 \param parent parent widget
1714 QtxWorkstack::QtxWorkstack( QWidget* parent )
1715 : QWidget( parent ),
1721 myActionsMap.insert( SplitVertical, new QtxAction( QString(), tr( "Split vertically" ), 0, this ) );
1722 myActionsMap.insert( SplitHorizontal, new QtxAction( QString(), tr( "Split horizontally" ), 0, this ) );
1723 myActionsMap.insert( Close, new QtxAction( QString(), tr( "Close" ), 0, this ) );
1724 myActionsMap.insert( Rename, new QtxAction( QString(), tr( "Rename" ), 0, this ) );
1726 connect( myActionsMap[SplitVertical], SIGNAL( triggered( bool ) ), this, SLOT( splitVertical() ) );
1727 connect( myActionsMap[SplitHorizontal], SIGNAL( triggered( bool ) ), this, SLOT( splitHorizontal() ) );
1728 connect( myActionsMap[Close], SIGNAL( triggered( bool ) ), this, SLOT( onCloseWindow() ) );
1729 connect( myActionsMap[Rename], SIGNAL( triggered( bool ) ), this, SLOT( onRename() ) );
1731 // Action shortcut will work when action added in any widget.
1732 for ( QMap<int, QAction*>::iterator it = myActionsMap.begin(); it != myActionsMap.end(); ++it )
1734 addAction( it.value() );
1735 it.value()->setShortcutContext( Qt::ApplicationShortcut );
1738 QVBoxLayout* base = new QVBoxLayout( this );
1739 base->setMargin( 0 );
1741 mySplit = new QtxWorkstackSplitter( this );
1742 base->addWidget( mySplit );
1748 QtxWorkstack::~QtxWorkstack()
1753 \brief Get list of all widgets in all areas or in specified area which given
1755 \param wid widget specifying area if it is equal to null when widgets of all
1757 \return list of widgets
1759 QWidgetList QtxWorkstack::windowList( QWidget* wid ) const
1761 QList<QtxWorkstackArea*> lst;
1764 areas( mySplit, lst, true );
1768 QtxWorkstackArea* area = wgArea( wid );
1773 QWidgetList widList;
1774 for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end(); ++it )
1776 QWidgetList wids = (*it)->widgetList();
1777 for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1778 widList.append( *itr );
1785 \brief Get all child widgets in the active workarea.
1786 \return list of widgets in active workarea
1788 QWidgetList QtxWorkstack::splitWindowList() const
1790 return myArea ? myArea->widgetList() : QWidgetList();
1794 \brief Get active widget.
1795 \return active widget
1797 QWidget* QtxWorkstack::activeWindow() const
1803 \brief Set active widget
1804 \param wid widget to activate
1806 void QtxWorkstack::setActiveWindow( QWidget* wid )
1809 activeArea()->setActiveWidget( wid );
1813 \brief Split workstack.
1815 Splitting is possible only if there are two or more widgets in the workarea.
1816 This function splits current workarea to two new ones.
1818 \param o splitting orientation (Qt::Orientation)
1820 void QtxWorkstack::split( const int o )
1822 QtxWorkstackArea* area = myWorkArea;
1824 area = activeArea();
1828 if ( area->widgetList().count() < 2 )
1831 QWidget* curWid = area->activeWidget();
1835 QSplitter* s = splitter( area );
1836 QList<QtxWorkstackArea*> areaList;
1837 areas( s, areaList );
1839 QList<QSplitter*> splitList;
1840 splitters( s, splitList );
1843 if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
1847 trg = wrapSplitter( area );
1852 trg->setOrientation( (Qt::Orientation)o );
1854 QtxWorkstackArea* newArea = createArea( 0 );
1855 trg->insertWidget( trg->indexOf( area ) + 1, newArea );
1857 area->removeWidget( curWid );
1858 newArea->insertWidget( curWid );
1860 distributeSpace( trg );
1867 \brief Split workarea of the given widget on two parts.
1869 Splitting is possible only if there are two or more widgets in the workarea.
1870 This function splits current workarea to two new ones.
1872 \param wid widget belonging to the workstack
1873 \param o splitting orientation type (Qt::Orientation)
1874 \param type splitting type (QtxWorkstack::SplitType)
1876 void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
1880 // find area of the given widget
1881 QtxWorkstackArea* area = NULL;
1882 QList<QtxWorkstackArea*> allAreas;
1883 areas(mySplit, allAreas, true);
1885 for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
1887 if ( (*it)->contains( wid ) )
1894 QWidget* curWid = area->activeWidget();
1898 QWidgetList wids = area->widgetList();
1899 if ( wids.count() < 2 )
1902 QSplitter* s = splitter( area );
1903 QList<QtxWorkstackArea*> areaList;
1904 areas( s, areaList );
1906 QList<QSplitter*> splitList;
1907 splitters(s, splitList);
1910 if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
1913 if (!trg) trg = wrapSplitter(area);
1916 trg->setOrientation(o);
1918 QtxWorkstackArea* newArea = createArea(0);
1919 insertWidget(newArea, trg, area);
1924 for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1926 QWidget* wid_i = *itr;
1929 area->removeWidget( wid_i );
1930 newArea->insertWidget( wid_i );
1931 wid_i->showMaximized();
1937 QWidgetList::iterator itr = wids.begin();
1938 for ( ; itr != wids.end() && *itr != wid; ++itr )
1941 for ( ; itr != wids.end(); ++itr )
1943 area->removeWidget( *itr );
1944 newArea->insertWidget( *itr );
1945 (*itr)->showMaximized();
1950 area->removeWidget( wid );
1951 newArea->insertWidget( wid );
1952 wid->showMaximized();
1956 distributeSpace( trg );
1963 \brief Move widget(s) from the source workarea into the target workarea
1964 or reorder widgets inside one workarea.
1966 Move \a wid2 in target workarea. Put it right after \a wid1.
1967 If \a all parameter is \c true, all widgets from source workarea
1968 will be moved including \a wid2 and source workarea will be deleted then.
1970 If \a wid1 and \a wid2 belongs to one workarea, widgets will be just reordered
1973 \param wid1 widget from target workarea
1974 \param wid2 widget from source workarea
1975 \param all if \c true, all widgets from source workarea will
1976 be moved into the target one, else only the \a wid2 will be moved
1978 void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
1980 if ( !wid1 || !wid2 )
1983 // find area of the widgets
1984 QtxWorkstackArea *area1 = 0, *area2 = 0;
1985 QList<QtxWorkstackArea*> allAreas;
1986 areas( mySplit, allAreas, true );
1987 for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !( area1 && area2 ); ++it )
1989 if ( (*it)->contains( wid1 ) )
1992 if ( (*it)->contains( wid2 ) )
1996 if ( !area1 || !area2 )
1999 QSplitter* s1 = splitter( area1 );
2001 QWidget* curWid = area1->activeWidget();
2005 if ( area1 == area2 )
2009 // Set wid1 at first position, wid2 at second
2010 area1->insertWidget( wid1 );
2011 area1->insertWidget( wid2, 1 );
2012 wid1->showMaximized();
2013 wid2->showMaximized();
2017 // Set wid2 right after wid1
2018 area1->removeWidget( wid2 );
2020 QWidgetList wids1 = area1->widgetList();
2021 for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
2022 area1->insertWidget( wid2, wid1_ind + 1 );
2023 wid2->showMaximized();
2029 QWidgetList wids1 = area1->widgetList();
2030 for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
2033 // Set wid2 right after wid1, other widgets from area2 right after wid2
2034 QWidgetList wids2 = area2->widgetList();
2035 QWidgetList::iterator itr2 = wids2.begin();
2036 for ( int ind = wid1_ind + 1; itr2 != wids2.end(); ++itr2, ++ind )
2038 area2->removeWidget( *itr2 );
2039 if ( *itr2 == wid2 )
2040 area1->insertWidget( *itr2, wid1_ind + 1 );
2042 area1->insertWidget( *itr2, ind );
2043 (*itr2)->showMaximized();
2048 // Set wid2 right after wid1
2049 area2->removeWidget( wid2 );
2050 area1->insertWidget( wid2, wid1_ind + 1 );
2051 wid2->showMaximized();
2055 distributeSpace( s1 );
2057 area1->setActiveWidget( curWid );
2066 \brief Calculate sizes of the splitter widget for the workarea.
2069 static void setSizes (QIntList& szList, const int item_ind,
2070 const int new_near, const int new_this, const int new_farr)
2072 // set size to all items before an item # <item_ind>
2074 QIntList::iterator its = szList.begin();
2075 for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
2078 if (its == szList.end()) return;
2079 // set size to item # <item_ind>
2082 // set size to all items after an item # <item_ind>
2083 for (; its != szList.end(); ++its) {
2089 \brief Set position of the widget relatively to its parent splitter.
2091 Orientation of positioning will correspond to the splitter orientation.
2094 \param pos position relatively to the splitter; value in the range [0..1]
2096 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
2098 if ( position < 0.0 || 1.0 < position)
2104 // find area of the given widget
2105 QtxWorkstackArea* area = NULL;
2106 QList<QtxWorkstackArea*> allAreas;
2107 areas( mySplit, allAreas, true );
2108 for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
2110 if ( (*it)->contains( wid ) )
2117 QSplitter* split = splitter( area );
2121 // find index of the area in its splitter
2123 bool isFound = false;
2124 const QObjectList& was = split->children();
2125 for ( QObjectList::const_iterator ito = was.begin(); ito != was.end() && !isFound; ++ito, ++item_ind )
2131 if ( !isFound || item_ind == 0 )
2134 QIntList szList = split->sizes();
2135 int splitter_size = ( split->orientation() == Qt::Horizontal ? split->width() : split->height());
2136 int nb = szList.count();
2138 int new_prev = int( splitter_size * position / item_ind );
2139 if (nb == item_ind) return;
2140 int new_next = int( splitter_size * ( 1.0 - position ) / ( nb - item_ind ) );
2141 setSizes( szList, item_ind, new_prev, new_next, new_next );
2142 split->setSizes( szList );
2146 \brief Set position of the widget relatively to the entire workstack.
2148 If \a o is \c Qt::Horizontal, the horizontal position of \a wid will be changed.
2149 If \a o is \c Qt::Vertical, the vertical position of \a wid will be changed.
2152 \param o orientation of positioning (\c Qt::Horizontal or \c Qt::Vertical)
2153 \param pos position relatively to the workstack; value in range [0..1]
2155 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
2156 const double position )
2158 if ( position < 0.0 || 1.0 < position )
2164 int splitter_size = o == Qt::Horizontal ? mySplit->width() : mySplit->height();
2165 int need_pos = int( position * splitter_size );
2166 int splitter_pos = 0;
2168 if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
2170 // impossible to set required position
2175 \brief Set accelerator key-combination for the action with specified \a id.
2177 \param accel action accelerator
2179 void QtxWorkstack::setAccel( const int id, const int accel )
2181 if ( !myActionsMap.contains( id ) )
2184 myActionsMap[id]->setShortcut( accel );
2188 \brief Get the action's accelerator key-combination.
2190 \return action accelerator
2192 int QtxWorkstack::accel( const int id ) const
2195 if ( myActionsMap.contains( id ) )
2196 res = myActionsMap[id]->shortcut();
2201 \brief Get icon for the specified action.
2203 If \a id is invalid, null icon is returned.
2205 \param id menu action ID
2206 \return menu item icon
2208 QIcon QtxWorkstack::icon( const int id ) const
2211 if ( myActionsMap.contains( id ) )
2212 ico = myActionsMap[id]->icon();
2217 \brief Set menu item icon for the specified action.
2218 \param id menu action ID
2219 \param ico new menu item icon
2221 void QtxWorkstack::setIcon( const int id, const QIcon& icon )
2223 if ( !myActionsMap.contains( id ) )
2226 myActionsMap[id]->setIcon( icon );
2230 \brief Set actions to be visible in the context popup menu.
2232 Actions, which IDs are set in \a flags parameter, will be shown in the
2233 context popup menu. Other actions will not be shown.
2235 \param flags ORed together actions flags
2237 void QtxWorkstack::setMenuActions( const int flags )
2239 myActionsMap[SplitVertical]->setVisible( flags & SplitVertical );
2240 myActionsMap[SplitHorizontal]->setVisible( flags & SplitHorizontal );
2241 myActionsMap[Close]->setVisible( flags & Close );
2242 myActionsMap[Rename]->setVisible( flags & Rename );
2246 \brief Set actions to be visible in the context popup menu.
2248 Actions, which IDs are set in \a flags parameter, will be shown in the
2249 context popup menu. Other actions will not be shown.
2251 \param flags ORed together actions flags
2253 int QtxWorkstack::menuActions() const
2256 ret = ret | ( myActionsMap[SplitVertical]->isVisible() ? SplitVertical : 0 );
2257 ret = ret | ( myActionsMap[SplitHorizontal]->isVisible() ? SplitHorizontal : 0 );
2258 ret = ret | ( myActionsMap[Close]->isVisible() ? Close : 0 );
2259 ret = ret | ( myActionsMap[Rename]->isVisible() ? Rename : 0 );
2264 \brief Calculate sizes of the splitter widget for the workarea.
2267 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
2268 const int item_ind, const int item_rel_pos,
2269 const int need_pos, const int splitter_pos)
2271 if (item_ind == 0) { // cannot move in this splitter
2272 return (need_pos - splitter_pos);
2277 int new_this = szList[item_ind];
2280 bool isToCheck = false;
2282 if (need_pos < splitter_pos) {
2283 // Set size of all previous workareas to zero <--
2284 if (item_ind == nb - 1) {
2285 // item iz last in the splitter, it will occupy all the splitter
2286 new_this = splitter_size;
2288 // recompute size of next items in splitter
2289 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2291 delta = need_pos - splitter_pos;
2293 } else if (need_pos > (splitter_pos + splitter_size)) {
2294 // Set size of all next workareas to zero -->
2295 // recompute size of previous items in splitter
2297 new_prev = (splitter_size - new_this) / item_ind;
2298 delta = need_pos - (splitter_pos + splitter_size - new_this);
2300 } else { // required position lays inside this splitter
2301 // Move workarea inside splitter into required position <->
2302 int new_item_rel_pos = need_pos - splitter_pos;
2303 new_prev = new_item_rel_pos / item_ind;
2304 if (need_pos < (splitter_pos + item_rel_pos)) {
2305 // Make previous workareas smaller, next - bigger
2306 // No problem to keep old size of the widget
2308 // Make previous workareas bigger, next - smaller
2309 if (new_this > splitter_size - new_item_rel_pos) {
2310 new_this = splitter_size - new_item_rel_pos;
2312 // jfa to do: in this case fixed size of next widgets could prevent right resizing
2315 if (item_ind == nb - 1) {
2316 new_this = splitter_size - new_item_rel_pos;
2318 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2323 setSizes (szList, item_ind, new_prev, new_this, new_next);
2328 \brief Set position of the widget.
2330 Called from SetRelativePosition() public method.
2332 \param wid widget to be moved
2333 \param split currently processed splitter (goes from more common
2334 to more particular splitter in recursion calls)
2335 \param o orientation of positioning
2336 \param need_pos required position of the given widget in pixels
2337 (from top/left side of workstack area)
2338 \param splitter_pos position of the splitter \a split
2339 (from top/left side of workstack area)
2340 \return difference between a required and a distinguished position
2342 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
2343 const int need_pos, const int splitter_pos )
2345 if ( !wid || !split )
2346 return need_pos - splitter_pos;
2348 // Find corresponding sub-splitter.
2349 // Find also index of appropriate item in current splitter.
2350 int cur_ind = 0, item_ind = 0;
2351 bool isBottom = false, isFound = false;
2352 QSplitter* sub_split = NULL;
2353 const QObjectList& objs = split->children();
2354 for ( QObjectList::const_iterator it = objs.begin(); it != objs.end() && !isFound; ++it )
2356 QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
2359 if ( area->contains( wid ) )
2367 else if ( (*it)->inherits( "QSplitter" ) )
2369 QList<QtxWorkstackArea*> areaList;
2370 areas( (QSplitter*)(*it), areaList, true );
2371 for ( QList<QtxWorkstackArea*>::iterator ita = areaList.begin(); ita != areaList.end() && !isFound; ++ita )
2373 if ( (*ita)->contains( wid ) )
2377 sub_split = (QSplitter*)*it;
2385 return ( need_pos - splitter_pos );
2387 if ( split->orientation() == o )
2389 // Find coordinates of near and far sides of the appropriate item relatively current splitter
2390 int splitter_size = ( o == Qt::Horizontal ? split->width() : split->height() );
2391 QIntList szList = split->sizes();
2392 int nb = szList.count();
2393 int item_rel_pos = 0; // position of near side of item relatively this splitter
2394 for (int i = 0; i < item_ind; i++) {
2395 item_rel_pos += szList[i];
2397 int item_size = szList[item_ind]; // size of item
2398 int item_pos = splitter_pos + item_rel_pos;
2400 // Resize splitter items to complete the conditions
2402 // I. Bottom of splitters stack reached
2404 int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
2405 split->setSizes(szList);
2406 // Recompute delta, as some windows can reject given size
2407 int new_item_rel_pos = 0;
2408 QIntList szList1 = split->sizes();
2409 for (int i = 0; i < item_ind; i++) {
2410 new_item_rel_pos += szList1[i];
2412 delta = need_pos - (splitter_pos + new_item_rel_pos);
2416 // II. Bottom of splitters stack is not yet reached
2418 if (item_ind == 0) { // cannot move in this splitter
2419 // Process in sub-splitter
2420 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2424 int new_this = szList[item_ind];
2427 if (need_pos < splitter_pos) {
2428 // Set size of all previous workareas to zero <--
2429 if (item_ind == nb - 1) {
2430 new_this = splitter_size;
2432 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2434 setSizes (szList, item_ind, new_prev, new_this, new_next);
2435 split->setSizes(szList);
2436 // Recompute splitter_pos, as some windows can reject given size
2437 int new_item_rel_pos = 0;
2438 QIntList szList1 = split->sizes();
2439 for (int i = 0; i < item_ind; i++) {
2440 new_item_rel_pos += szList1[i];
2442 // Process in sub-splitter
2443 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2444 } else if (need_pos > (splitter_pos + splitter_size)) {
2445 // Set size of all next workareas to zero -->
2446 new_prev = (splitter_size - new_this) / item_ind;
2447 setSizes (szList, item_ind, new_prev, new_this, new_next);
2448 split->setSizes(szList);
2449 // Recompute splitter_pos, as some windows can reject given size
2450 int new_item_rel_pos = 0;
2451 QIntList szList1 = split->sizes();
2452 for (int i = 0; i < item_ind; i++) {
2453 new_item_rel_pos += szList1[i];
2455 // Process in sub-splitter
2456 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2458 // Set appropriate size of all previous/next items <->
2459 int new_item_rel_pos = item_rel_pos;
2460 if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
2461 // Move item inside splitter into required position <->
2462 int new_this = szList[item_ind];
2464 new_item_rel_pos = need_pos - splitter_pos;
2465 if ((item_pos + item_size) < need_pos) {
2466 //new_item_rel_pos = need_pos - (item_pos + item_size);
2467 new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
2469 int new_prev = new_item_rel_pos / item_ind;
2470 if (need_pos < (splitter_pos + item_rel_pos)) {
2471 // Make previous workareas smaller, next - bigger
2472 // No problem to keep old size of the widget
2474 // Make previous workareas bigger, next - smaller
2475 if (new_this > splitter_size - new_item_rel_pos) {
2476 new_this = splitter_size - new_item_rel_pos;
2479 if (item_ind == nb - 1) {
2480 new_this = splitter_size - new_item_rel_pos;
2482 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2484 setSizes (szList, item_ind, new_prev, new_this, new_next);
2485 split->setSizes(szList);
2486 // Recompute new_item_rel_pos, as some windows can reject given size
2487 new_item_rel_pos = 0;
2488 QIntList szList1 = split->sizes();
2489 for (int i = 0; i < item_ind; i++) {
2490 new_item_rel_pos += szList1[i];
2495 // Process in sub-splitter
2496 int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2500 // this can be if corresponding workarea is first in sub-splitter
2501 // or sub-splitter has another orientation
2503 // Resize ones again to reach precize position <->
2504 int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
2506 // Move workarea inside splitter into required position <->
2507 int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
2508 new_item_rel_pos, need_pos_1, splitter_pos);
2509 split->setSizes(szList);
2510 // Recompute new_item_rel_pos, as some windows can reject given size
2511 new_item_rel_pos = 0;
2512 QIntList szList1 = split->sizes();
2513 for (int i = 0; i < item_ind; i++) {
2514 new_item_rel_pos += szList1[i];
2516 delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
2521 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2528 \brief Redistribute space among widgets equally.
2529 \param split splitter
2531 void QtxWorkstack::distributeSpace( QSplitter* split ) const
2536 QIntList szList = split->sizes();
2537 int size = ( split->orientation() == Qt::Horizontal ?
2538 split->width() : split->height() ) / szList.count();
2539 for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
2541 split->setSizes( szList );
2545 \brief Split widgets vertically.
2547 void QtxWorkstack::splitVertical()
2549 split( Qt::Horizontal );
2553 \brief Split widgets horizontally.
2555 void QtxWorkstack::splitHorizontal()
2557 split( Qt::Vertical );
2561 \brief Called when user activates "Rename" menu item.
2563 Changes widget title.
2565 void QtxWorkstack::onRename()
2571 QString newName = QInputDialog::getText( topLevelWidget(), tr( "Rename" ), tr( "Enter new name:" ),
2572 QLineEdit::Normal, myWorkWin->windowTitle(), &ok );
2573 if ( ok && !newName.isEmpty() )
2574 myWorkWin->setWindowTitle( newName );
2578 \brief Wrap area into the new splitter.
2580 \return new splitter
2582 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
2587 QSplitter* pSplit = splitter( area );
2591 bool upd = pSplit->updatesEnabled();
2592 pSplit->setUpdatesEnabled( false );
2594 QIntList szList = pSplit->sizes();
2596 QSplitter* wrap = new QtxWorkstackSplitter( 0 );
2597 pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
2598 wrap->setVisible( true );
2599 wrap->addWidget( area );
2601 pSplit->setSizes( szList );
2603 pSplit->setUpdatesEnabled( upd );
2609 \brief Reparent and add widget.
2611 \param pWid parent widget
2612 \param after widget after which \a wid should be added
2614 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
2616 if ( !wid || !pWid )
2619 QWidgetList moveList;
2620 const QObjectList& lst = pWid->children();
2622 for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
2624 if ( found && ( (*it)->inherits( "QSplitter" ) ||
2625 (*it)->inherits( "QtxWorkstackArea" ) ) )
2626 moveList.append( (QWidget*)(*it) );
2631 QMap<QWidget*, bool> map;
2632 for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
2634 map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
2635 (*it)->setParent( 0 );
2639 wid->setParent( pWid );
2641 for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
2643 (*itr)->setParent( pWid );
2644 (*itr)->setShown( map.contains( *itr ) ? map[*itr] : false );
2649 \brief Close active window.
2651 void QtxWorkstack::onCloseWindow()
2655 else if( activeWindow() )
2656 activeWindow()->close();
2660 \brief Called when workarea is destroyed.
2662 Set input focus to the neighbour area.
2664 \param obj workarea being destroyed
2666 void QtxWorkstack::onDestroyed( QObject* obj )
2668 QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
2670 if ( area == myArea )
2675 QtxWorkstackArea* cur = neighbourArea( area );
2680 QApplication::postEvent( this, new QEvent( QEvent::User ) );
2684 \brief Called on window activating.
2685 \param area workarea being activated (not used)
2687 void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
2689 const QObject* obj = sender();
2690 if ( !obj->inherits( "QtxWorkstackArea" ) )
2693 setActiveArea( (QtxWorkstackArea*)obj );
2697 \brief Called on window deactivating.
2698 \param area workarea being deactivated
2700 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
2702 if ( myArea != area )
2705 QList<QtxWorkstackArea*> lst;
2706 areas( mySplit, lst, true );
2708 int idx = lst.indexOf( area );
2715 QtxWorkstackArea* newArea = neighbourArea( area );
2716 if ( newArea && newArea->activeWidget() )
2717 newArea->activeWidget()->setFocus();
2719 QApplication::postEvent( this, new QEvent( QEvent::User ) );
2723 \brief Create and show popup menu for workarea.
2725 \param p popup position
2727 void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
2729 QtxWorkstackArea* anArea = ::qobject_cast<QtxWorkstackArea*>( (QObject*)sender() );
2731 anArea = activeArea();
2736 QWidgetList lst = anArea->widgetList();
2737 if ( lst.isEmpty() )
2741 myWorkArea = anArea;
2743 QMenu* pm = new QMenu();
2745 if ( lst.count() > 1 )
2747 if ( !myActionsMap[SplitVertical]->isEnabled() )
2748 myActionsMap[SplitVertical]->setEnabled(true);
2749 pm->addAction( myActionsMap[SplitVertical] );
2750 if ( !myActionsMap[SplitHorizontal]->isEnabled() )
2751 myActionsMap[SplitHorizontal]->setEnabled(true);
2752 pm->addAction( myActionsMap[SplitHorizontal] );
2758 if ( myActionsMap[Close]->isEnabled() )
2759 pm->addAction( myActionsMap[Close] );
2760 if ( myActionsMap[Rename]->isEnabled() )
2761 pm->addAction( myActionsMap[Rename] );
2764 Qtx::simplifySeparators( pm );
2766 if ( !pm->actions().isEmpty() )
2776 \brief Add child widget.
2778 \param f widget flags
2779 \return child widget container
2781 QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
2786 return targetArea()->insertWidget( w, -1, f );
2790 \brief Handle custom events.
2791 \param e custom event (not used)
2793 void QtxWorkstack::customEvent( QEvent* /*e*/ )
2799 \brief Get splitter corresponding to the workarea.
2801 \return splitter corresponding to the workarea
2803 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
2808 QSplitter* split = 0;
2810 QWidget* wid = area->parentWidget();
2811 if ( wid && wid->inherits( "QSplitter" ) )
2812 split = (QSplitter*)wid;
2818 \brief Get list of child splitters.
2819 \param split parent splitter
2820 \param splitList list to be filled with child splitters
2821 \param rec if \c true, perform recursive search of children
2823 void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
2828 const QObjectList& objs = split->children();
2829 for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2832 splitters( (QSplitter*)*it, splitList, rec );
2833 if ( (*it)->inherits( "QSplitter" ) )
2834 splitList.append( (QSplitter*)*it );
2839 \brief Get list of child workareas.
2840 \param split parent splitter
2841 \param areaList list to be filled with child workareas
2842 \param rec if \c true, perform recursive search of children
2844 void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
2849 const QObjectList& objs = split->children();
2850 for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2852 if ( (*it)->inherits( "QtxWorkstackArea" ) )
2853 areaList.append( (QtxWorkstackArea*)*it );
2854 else if ( rec && (*it)->inherits( "QSplitter" ) )
2855 areas( (QSplitter*)*it, areaList, rec );
2860 \brief Get active workarea.
2861 \return active workarea
2863 QtxWorkstackArea* QtxWorkstack::activeArea() const
2869 \brief Get target area (for which the current operation should be done).
2871 Returns active workarea or current area (if there is no active workarea).
2872 If there are no workareas, create new workarea and return it.
2876 QtxWorkstackArea* QtxWorkstack::targetArea()
2878 QtxWorkstackArea* area = activeArea();
2880 area = currentArea();
2883 QList<QtxWorkstackArea*> lst;
2884 areas( mySplit, lst );
2885 if ( !lst.isEmpty() )
2890 area = createArea( mySplit );
2896 \brief Get current workarea.
2898 Current workarea is that one which has input focus.
2900 \return current area
2902 QtxWorkstackArea* QtxWorkstack::currentArea() const
2904 QtxWorkstackArea* area = 0;
2905 QWidget* wid = focusWidget();
2906 while ( wid && !area )
2908 if ( wid->inherits( "QtxWorkstackArea" ) )
2909 area = (QtxWorkstackArea*)wid;
2910 wid = wid->parentWidget();
2917 \brief Create new workarea.
2918 \param parent parent widget
2919 \return created workarea
2921 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
2923 QtxWorkstackArea* area = new QtxWorkstackArea( parent );
2925 connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
2926 connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
2927 connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
2928 this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
2929 connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
2935 \brief Set active workarea.
2938 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
2940 QWidget* oldCur = myWin;
2942 QtxWorkstackArea* oldArea = myArea;
2946 if ( myArea != oldArea )
2949 oldArea->updateActiveState();
2951 myArea->updateActiveState();
2955 myWin = myArea->activeWidget();
2957 if ( myWin && oldCur != myWin )
2958 emit windowActivated( myWin );
2962 \brief Get workarea which is nearest to \a area.
2963 \param area area for which neighbour is searched
2964 \return neighbour area (or 0 if not found)
2966 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
2968 QList<QtxWorkstackArea*> lst;
2969 areas( mySplit, lst, true );
2970 int pos = lst.indexOf( area );
2974 QtxWorkstackArea* na = 0;
2975 for ( int i = pos - 1; i >= 0 && !na; i-- )
2977 if ( !lst.at( i )->isEmpty() )
2981 for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
2983 if ( !lst.at( j )->isEmpty() )
2990 \brief Get workarea covering point.
2994 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
2996 QtxWorkstackArea* area = 0;
2997 QList<QtxWorkstackArea*> lst;
2998 areas( mySplit, lst, true );
2999 for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end() && !area; ++it )
3001 QtxWorkstackArea* cur = *it;
3002 QRect r = cur->geometry();
3003 if ( cur->parentWidget() )
3004 r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
3005 if ( r.contains( p ) )
3012 \brief Update internal state.
3014 void QtxWorkstack::updateState()
3016 updateState( mySplit );
3020 \brief Update splitter state.
3021 \param split splitter to be updated
3023 void QtxWorkstack::updateState( QSplitter* split )
3025 QList<QSplitter*> recList;
3026 splitters( split, recList, false );
3027 for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
3028 updateState( *itr );
3030 QList<QSplitter*> splitList;
3031 splitters( split, splitList, false );
3033 QList<QtxWorkstackArea*> areaList;
3034 areas( split, areaList, false );
3037 for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
3039 if ( (*it)->isEmpty() )
3048 if ( split == mySplit )
3051 for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
3052 vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
3054 if ( areaList.isEmpty() && splitList.isEmpty() )
3057 split->setVisible( vis );
3061 \brief Dump workstack configuration to the state description array.
3062 \param version number
3063 \return state byte array.
3065 QByteArray QtxWorkstack::saveState( int version ) const
3069 QDataStream stream( &data, QIODevice::WriteOnly );
3070 stream << QtxWorkstack::VersionMarker;
3072 saveState( stream );
3078 \brief Restore workstack configuration from the state description array.
3079 \param version number
3080 \return restore performing state
3082 bool QtxWorkstack::restoreState( const QByteArray& state, int version )
3084 if ( state.isEmpty() )
3087 QByteArray sd = state;
3088 QDataStream stream( &sd, QIODevice::ReadOnly );
3092 if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::VersionMarker || ver != version )
3095 return restoreState( stream );
3098 void QtxWorkstack::saveState( QDataStream& stream ) const
3100 mySplit->saveState( stream );
3103 bool QtxWorkstack::restoreState( QDataStream& stream )
3105 QMap<QString, QtxWorkstackChild*> map;
3106 QList<QtxWorkstackArea*> areaList;
3107 areas( mySplit, areaList, true );
3108 for ( QList<QtxWorkstackArea*>::const_iterator it = areaList.begin(); it != areaList.end(); ++it )
3110 QtxWorkstackArea* area = *it;
3111 QList<QtxWorkstackChild*> childList = area->childList();
3112 for ( QList<QtxWorkstackChild*>::iterator itr = childList.begin(); itr != childList.end(); ++itr )
3114 QtxWorkstackChild* c = *itr;
3118 map.insert( c->widget()->objectName(), c );
3120 qDebug( "QtxWorkstack::restoreState: found widget \"%s\"", (const char*)c->widget()->objectName().toLatin1() );
3126 if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::SplitMarker )
3129 QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
3131 layout()->addWidget( split );
3133 bool ok = split->restoreState( stream, map );
3138 mySplit->deleteLater();
3141 QList<QtxWorkstackArea*> aList;
3142 areas( mySplit, aList, true );
3144 QtxWorkstackArea* a = !aList.isEmpty() ? aList.first() : 0;
3145 for ( QMap<QString, QtxWorkstackChild*>::const_iterator it = map.begin(); it != map.end(); ++it )
3147 QtxWorkstackChild* c = it.value();
3149 c->widget()->setVisible( false );
3151 a->insertChild( c );
3153 c->setVisible( false );
3161 \brief Set resize mode of all splitters opaque or transparent.
3162 \param opaque opaque mode
3164 void QtxWorkstack::setOpaqueResize( bool opaque )
3166 QList<QSplitter*> splitList;
3167 splitters( mySplit, splitList, true );
3168 splitList << mySplit;
3169 foreach( QSplitter* split, splitList )
3170 split->setOpaqueResize( opaque );
3174 \brief Get resize mode of all splitters: opaque (\c true) or transparent (\c false).
3175 \return current opaque mode
3177 bool QtxWorkstack::opaqueResize() const
3179 return mySplit->opaqueResize();
3184 \fn void QtxWorkstack::windowActivated( QWidget* w )
3185 \brief Emitted when the workstack's child widget \w is activated.
3186 \param w widget being activated
3190 \brief Gets area containing given widget
3192 \return pointer to QtxWorkstackArea* object
3194 QtxWorkstackArea* QtxWorkstack::wgArea( QWidget* wid ) const
3196 QtxWorkstackArea* resArea = 0;
3198 QList<QtxWorkstackArea*> areaList;
3199 areas( mySplit, areaList, true );
3201 QList<QtxWorkstackArea*>::ConstIterator it;
3202 for ( it = areaList.begin(); it != areaList.end() && !resArea; ++it )
3204 if ( (*it)->contains( wid ) )
3212 \brief Moves the first widget to the same area which the second widget belongs to
3213 \param wid widget to be moved
3214 \param wid_to widget specified the destination area
3215 \param before specifies whether the first widget has to be moved before or after
3217 \return TRUE if operation is completed successfully, FALSE otherwise
3219 bool QtxWorkstack::move( QWidget* wid, QWidget* wid_to, const bool before )
3221 if ( wid && wid_to )
3223 QtxWorkstackArea* area_src = wgArea( wid );
3224 QtxWorkstackArea* area_to = wgArea( wid_to );
3225 if ( area_src && area_to )
3227 // find index of the second widget
3228 QWidgetList wgList = area_to->widgetList();
3229 QWidgetList::ConstIterator it;
3231 for ( it = wgList.begin(); it != wgList.begin(); ++it, idx++ )
3233 if ( *it == wid_to )
3237 if ( idx < wgList.count() ) // paranoidal check
3241 area_src->removeWidget( wid, true );
3242 area_to->insertWidget( wid, idx );
3243 wid->showMaximized();
3252 \brief Group all windows in one area
3253 \return TRUE if operation is completed successfully, FALSE otherwise
3255 void QtxWorkstack::stack()
3257 QWidgetList wgList = windowList();
3258 if ( !wgList.count() )
3259 return; // nothing to do
3261 QtxWorkstackArea* area_to = 0;
3262 QWidgetList::ConstIterator it;
3263 for ( it = wgList.begin(); it != wgList.end(); ++it )
3265 QtxWorkstackArea* area_src = 0;
3268 area_to = wgArea( *it );
3272 area_src = wgArea( *it );
3274 if ( area_src != area_to )
3276 area_src->removeWidget( *it, true );
3277 area_to->insertWidget( *it, -1 );
3278 (*it)->showMaximized();
3283 QAction* QtxWorkstack::action( const int id ) const
3285 return myActionsMap.contains( id ) ? myActionsMap[id] : 0;