1 // File: QtxWorkstack.cxx
2 // Author: Sergey TELKOV
4 #include "QtxWorkstack.h"
11 #include <qsplitter.h>
12 #include <qpopupmenu.h>
13 #include <qobjectlist.h>
14 #include <qpushbutton.h>
15 #include <qwidgetstack.h>
16 #include <qapplication.h>
21 Class: QtxWorkstack [Public]
25 QtxWorkstack::QtxWorkstack( QWidget* parent )
30 QVBoxLayout* base = new QVBoxLayout( this );
31 mySplit = new QSplitter( this );
32 mySplit->setChildrenCollapsible( false );
33 base->addWidget( mySplit );
36 QtxWorkstack::~QtxWorkstack()
40 QWidgetList QtxWorkstack::windowList() const
42 QPtrList<QtxWorkstackArea> lst;
43 areas( mySplit, lst, true );
46 for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current(); ++it )
48 QWidgetList wids = it.current()->widgetList();
49 for ( QWidgetListIt itr( wids ); itr.current(); ++itr )
50 widList.append( itr.current() );
56 QWidgetList QtxWorkstack::splitWindowList() const
58 return myArea ? myArea->widgetList() : QWidgetList();
61 QWidget* QtxWorkstack::activeWindow() const
66 void QtxWorkstack::split( const int o )
68 QtxWorkstackArea* area = activeArea();
72 if ( area->widgetList().count() < 2 )
75 QWidget* curWid = area->activeWidget();
79 QSplitter* s = splitter( area );
80 QPtrList<QtxWorkstackArea> areaList;
83 QPtrList<QSplitter> splitList;
84 splitters( s, splitList );
87 if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
91 trg = wrapSplitter( area );
96 trg->setOrientation( (Orientation)o );
98 QtxWorkstackArea* newArea = createArea( 0 );
99 insertWidget( newArea, trg, area );
101 area->removeWidget( curWid );
102 newArea->insertWidget( curWid );
104 distributeSpace( trg );
110 // begin: jfa 06.07.2005
111 void QtxWorkstack::Split (QWidget* wid, const Qt::Orientation o, const SplitType type)
115 // find area of the given widget
116 QtxWorkstackArea* area = NULL;
117 QPtrList<QtxWorkstackArea> allAreas;
118 areas(mySplit, allAreas, true);
120 QPtrListIterator<QtxWorkstackArea> it (allAreas);
121 for (; it.current() && !area; ++it) {
122 if (it.current()->contains(wid))
127 QWidgetList wids = area->widgetList();
128 if (wids.count() < 2)
131 QSplitter* s = splitter(area);
132 QPtrList<QtxWorkstackArea> areaList;
135 QPtrList<QSplitter> splitList;
136 splitters(s, splitList);
139 if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
142 if (!trg) trg = wrapSplitter(area);
145 trg->setOrientation(o);
147 QtxWorkstackArea* newArea = createArea(0);
148 insertWidget(newArea, trg, area);
153 QWidgetListIt itr (wids);
154 for (; itr.current(); ++itr)
156 QWidget* wid_i = itr.current();
158 area->removeWidget(wid_i);
159 newArea->insertWidget(wid_i);
166 QWidgetListIt itr (wids);
167 for (; itr.current() && itr.current() != wid; ++itr) {
169 for (; itr.current(); ++itr) {
170 area->removeWidget(itr.current());
171 newArea->insertWidget(itr.current());
176 area->removeWidget(wid);
177 newArea->insertWidget(wid);
181 distributeSpace(trg);
184 void QtxWorkstack::OnTop (QWidget* wid)
188 // find area of the given widget
189 QtxWorkstackArea* area = NULL;
190 QPtrList<QtxWorkstackArea> allAreas;
191 areas(mySplit, allAreas, true);
192 QPtrListIterator<QtxWorkstackArea> it (allAreas);
193 for (; it.current() && !area; ++it) {
194 if (it.current()->contains(wid))
199 area->setActiveWidget(wid);
202 void QtxWorkstack::Attract (QWidget* wid1, QWidget* wid2, const bool all)
204 if (!wid1 || !wid2) return;
206 // find area of the widgets
207 QtxWorkstackArea *area1 = NULL, *area2 = NULL;
208 QPtrList<QtxWorkstackArea> allAreas;
209 areas(mySplit, allAreas, true);
210 QPtrListIterator<QtxWorkstackArea> it (allAreas);
211 for (; it.current() && !(area1 && area2); ++it) {
212 if (it.current()->contains(wid1))
213 area1 = it.current();
214 if (it.current()->contains(wid2))
215 area2 = it.current();
217 if (!area1 || !area2) return;
219 QWidget* curWid = area1->activeWidget();
222 if (area1 == area2) {
224 // Set wid1 at first position, wid2 at second
225 area1->insertWidget(wid1);
226 area1->insertWidget(wid2, 1);
228 // Set wid2 right after wid1
229 area1->removeWidget(wid2);
231 QWidgetList wids1 = area1->widgetList();
232 QWidgetListIt itr1 (wids1);
233 for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
234 area1->insertWidget(wid2, wid1_ind + 1);
238 QWidgetList wids1 = area1->widgetList();
239 QWidgetListIt itr1 (wids1);
240 for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
243 // Set wid2 right after wid1, other widgets from area2 right after wid2
244 QWidgetList wids2 = area2->widgetList();
245 QWidgetListIt itr2 (wids2);
246 for (int ind = wid1_ind + 1; itr2.current(); ++itr2, ++ind)
248 area2->removeWidget(itr2.current());
249 if (itr2.current() == wid2) {
250 area1->insertWidget(itr2.current(), wid1_ind + 1);
252 area1->insertWidget(itr2.current(), ind);
256 // Set wid2 right after wid1
257 area2->removeWidget(wid2);
258 area1->insertWidget(wid2, wid1_ind + 1);
262 /*area2->removeWidget(wid2);
263 area1->insertWidget(wid2);
266 QWidgetListIt itr( wids1 );
267 for ( ; itr.current() && itr.current() != wid1; ++itr )
271 for ( ; itr.current(); ++itr )
273 if (itr.current() != wid2) {
274 area1->removeWidget( itr.current() );
275 area1->insertWidget( itr.current() );
279 area1->setActiveWidget(curWid);
282 static void setSizes (QIntList& szList, const int item_ind,
283 const int new_near, const int new_this, const int new_farr)
285 // set size to all items before an item # <item_ind>
287 QIntList::iterator its = szList.begin();
288 for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
291 if (its == szList.end()) return;
292 // set size to item # <item_ind>
295 // set size to all items after an item # <item_ind>
296 for (; its != szList.end(); ++its) {
301 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
303 if ( position < 0.0 || 1.0 < position)
308 // find area of the given widget
309 QtxWorkstackArea* area = NULL;
310 QPtrList<QtxWorkstackArea> allAreas;
311 areas(mySplit, allAreas, true);
312 for ( QPtrListIterator<QtxWorkstackArea> it( allAreas );
313 it.current() && !area;
316 if (it.current()->contains(wid))
323 QSplitter* split = splitter( area );
327 // find index of the area in its splitter
329 bool isFound = false;
330 const QObjectList* was = split->children();
331 for (QObjectListIt ito (*was); ito.current() && !isFound; ++ito, ++item_ind)
333 if (ito.current() == area)
336 if (!isFound || item_ind == 0)
339 QIntList szList = split->sizes();
340 int splitter_size = (split->orientation() == Horizontal ?
341 split->width() : split->height());
342 int nb = szList.count();
344 int new_prev = int(splitter_size * position / item_ind);
345 int new_next = int(splitter_size * (1.0 - position) / (nb - item_ind));
346 setSizes (szList, item_ind, new_prev, new_next, new_next);
347 split->setSizes(szList);
350 void QtxWorkstack::SetRelativePosition (QWidget* wid,
351 const Qt::Orientation o,
352 const double position)
354 if (position < 0.0 || 1.0 < position)
359 int splitter_size = (o == Horizontal ? mySplit->width() : mySplit->height());
360 int need_pos = int(position * splitter_size);
361 int splitter_pos = 0;
363 if (setPosition(wid, mySplit, o, need_pos, splitter_pos) != 0) {
364 // impossible to set required position
368 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
369 const int item_ind, const int item_rel_pos,
370 const int need_pos, const int splitter_pos)
372 if (item_ind == 0) { // cannot move in this splitter
373 return (need_pos - splitter_pos);
378 int new_this = szList[item_ind];
381 bool isToCheck = false;
383 if (need_pos < splitter_pos) {
384 // Set size of all previous workareas to zero <--
385 if (item_ind == nb - 1) {
386 // item iz last in the splitter, it will occupy all the splitter
387 new_this = splitter_size;
389 // recompute size of next items in splitter
390 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
392 delta = need_pos - splitter_pos;
394 } else if (need_pos > (splitter_pos + splitter_size)) {
395 // Set size of all next workareas to zero -->
396 // recompute size of previous items in splitter
398 new_prev = (splitter_size - new_this) / item_ind;
399 delta = need_pos - (splitter_pos + splitter_size - new_this);
401 } else { // required position lays inside this splitter
402 // Move workarea inside splitter into required position <->
403 int new_item_rel_pos = need_pos - splitter_pos;
404 new_prev = new_item_rel_pos / item_ind;
405 if (need_pos < (splitter_pos + item_rel_pos)) {
406 // Make previous workareas smaller, next - bigger
407 // No problem to keep old size of the widget
409 // Make previous workareas bigger, next - smaller
410 if (new_this > splitter_size - new_item_rel_pos) {
411 new_this = splitter_size - new_item_rel_pos;
413 // jfa to do: in this case fixed size of next widgets could prevent right resizing
416 if (item_ind == nb - 1) {
417 new_this = splitter_size - new_item_rel_pos;
419 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
424 setSizes (szList, item_ind, new_prev, new_this, new_next);
428 int QtxWorkstack::setPosition (QWidget* wid, QSplitter* split,
429 const Qt::Orientation o, const int need_pos,
430 const int splitter_pos)
433 return need_pos - splitter_pos;
435 // Find corresponding sub-splitter.
436 // Find also index of appropriate item in current splitter.
437 int cur_ind = 0, item_ind = 0;
438 bool isBottom = false, isFound = false;
439 QSplitter* sub_split = NULL;
440 const QObjectList* objs = split->children();
443 for (QObjectListIt it (*objs); it.current() && !isFound; ++it)
445 if (it.current()->inherits( "QtxWorkstackArea")) {
446 if (((QtxWorkstackArea*)it.current())->contains(wid)) {
452 } else if (it.current()->inherits("QSplitter")) {
453 QPtrList<QtxWorkstackArea> areaList;
454 areas((QSplitter*)it.current(), areaList, true);
455 for (QPtrListIterator<QtxWorkstackArea> ita (areaList);
456 ita.current() && !isFound;
459 if (ita.current()->contains(wid)) {
462 sub_split = (QSplitter*)it.current();
470 return (need_pos - splitter_pos);
472 if (split->orientation() == o) {
473 // Find coordinates of near and far sides of the appropriate item relatively current splitter
474 int splitter_size = (o == Horizontal ? split->width() : split->height());
475 QIntList szList = split->sizes();
476 int nb = szList.count();
477 int item_rel_pos = 0; // position of near side of item relatively this splitter
478 for (int i = 0; i < item_ind; i++) {
479 item_rel_pos += szList[i];
481 int item_size = szList[item_ind]; // size of item
482 int item_pos = splitter_pos + item_rel_pos;
484 // Resize splitter items to complete the conditions
486 // I. Bottom of splitters stack reached
488 int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
489 split->setSizes(szList);
490 // Recompute delta, as some windows can reject given size
491 int new_item_rel_pos = 0;
492 QIntList szList1 = split->sizes();
493 for (int i = 0; i < item_ind; i++) {
494 new_item_rel_pos += szList1[i];
496 delta = need_pos - (splitter_pos + new_item_rel_pos);
500 // II. Bottom of splitters stack is not yet reached
502 if (item_ind == 0) { // cannot move in this splitter
503 // Process in sub-splitter
504 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
508 int new_this = szList[item_ind];
511 if (need_pos < splitter_pos) {
512 // Set size of all previous workareas to zero <--
513 if (item_ind == nb - 1) {
514 new_this = splitter_size;
516 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
518 setSizes (szList, item_ind, new_prev, new_this, new_next);
519 split->setSizes(szList);
520 // Recompute splitter_pos, as some windows can reject given size
521 int new_item_rel_pos = 0;
522 QIntList szList1 = split->sizes();
523 for (int i = 0; i < item_ind; i++) {
524 new_item_rel_pos += szList1[i];
526 // Process in sub-splitter
527 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
528 } else if (need_pos > (splitter_pos + splitter_size)) {
529 // Set size of all next workareas to zero -->
530 new_prev = (splitter_size - new_this) / item_ind;
531 setSizes (szList, item_ind, new_prev, new_this, new_next);
532 split->setSizes(szList);
533 // Recompute splitter_pos, as some windows can reject given size
534 int new_item_rel_pos = 0;
535 QIntList szList1 = split->sizes();
536 for (int i = 0; i < item_ind; i++) {
537 new_item_rel_pos += szList1[i];
539 // Process in sub-splitter
540 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
542 // Set appropriate size of all previous/next items <->
543 int new_item_rel_pos = item_rel_pos;
544 if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
545 // Move item inside splitter into required position <->
546 int new_this = szList[item_ind];
548 new_item_rel_pos = need_pos - splitter_pos;
549 if ((item_pos + item_size) < need_pos) {
550 //new_item_rel_pos = need_pos - (item_pos + item_size);
551 new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
553 int new_prev = new_item_rel_pos / item_ind;
554 if (need_pos < (splitter_pos + item_rel_pos)) {
555 // Make previous workareas smaller, next - bigger
556 // No problem to keep old size of the widget
558 // Make previous workareas bigger, next - smaller
559 if (new_this > splitter_size - new_item_rel_pos) {
560 new_this = splitter_size - new_item_rel_pos;
563 if (item_ind == nb - 1) {
564 new_this = splitter_size - new_item_rel_pos;
566 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
568 setSizes (szList, item_ind, new_prev, new_this, new_next);
569 split->setSizes(szList);
570 // Recompute new_item_rel_pos, as some windows can reject given size
571 new_item_rel_pos = 0;
572 QIntList szList1 = split->sizes();
573 for (int i = 0; i < item_ind; i++) {
574 new_item_rel_pos += szList1[i];
579 // Process in sub-splitter
580 int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
584 // this can be if corresponding workarea is first in sub-splitter
585 // or sub-splitter has another orientation
587 // Resize ones again to reach precize position <->
588 int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
590 // Move workarea inside splitter into required position <->
591 int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
592 new_item_rel_pos, need_pos_1, splitter_pos);
593 split->setSizes(szList);
594 // Recompute new_item_rel_pos, as some windows can reject given size
595 new_item_rel_pos = 0;
596 QIntList szList1 = split->sizes();
597 for (int i = 0; i < item_ind; i++) {
598 new_item_rel_pos += szList1[i];
600 delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
605 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
610 // end: jfa 06.07.2005
612 void QtxWorkstack::distributeSpace( QSplitter* split ) const
617 QIntList szList = split->sizes();
618 int size = ( split->orientation() == Horizontal ?
619 split->width() : split->height() ) / szList.count();
620 for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
622 split->setSizes( szList );
625 void QtxWorkstack::splitVertical()
627 split( Qt::Vertical );
630 void QtxWorkstack::splitHorizontal()
632 split( Qt::Horizontal );
635 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
640 QSplitter* pSplit = splitter( area );
644 bool upd = pSplit->isUpdatesEnabled();
645 pSplit->setUpdatesEnabled( false );
647 QIntList szList = pSplit->sizes();
649 QSplitter* wrap = new QSplitter( 0 );
650 #if defined QT_VERSION && QT_VERSION >= 0x30200
651 wrap->setChildrenCollapsible( false );
653 insertWidget( wrap, pSplit, area );
654 area->reparent( wrap, QPoint( 0, 0 ), true );
656 pSplit->setSizes( szList );
658 pSplit->setUpdatesEnabled( upd );
663 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
668 QWidgetList moveList;
669 const QObjectList* lst = pWid->children();
673 for ( QObjectListIt it( *lst ); it.current(); ++it )
675 if ( found && ( it.current()->inherits( "QSplitter" ) ||
676 it.current()->inherits( "QtxWorkstackArea" ) ) )
677 moveList.append( (QWidget*)it.current() );
678 if ( it.current() == after )
683 QMap<QWidget*, bool> map;
684 for ( QWidgetListIt it( moveList ); it.current(); ++it )
686 map.insert( it.current(), it.current()->isVisibleTo( it.current()->parentWidget() ) );
687 it.current()->reparent( 0, QPoint( 0, 0 ), false );
690 wid->reparent( pWid, QPoint( 0, 0 ), true );
692 for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
693 itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
696 void QtxWorkstack::onPopupActivated( int id )
703 case SplitHorizontal:
707 if ( activeWindow() )
708 activeWindow()->close();
713 void QtxWorkstack::onDestroyed( QObject* obj )
715 QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
717 if ( area == myArea )
722 QtxWorkstackArea* cur = neighbourArea( area );
727 QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
730 void QtxWorkstack::onWindowActivated( QWidget* wid )
732 const QObject* obj = sender();
733 if ( !obj->inherits( "QtxWorkstackArea" ) )
736 setActiveArea( (QtxWorkstackArea*)obj );
739 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
741 if ( myArea != area )
744 QPtrList<QtxWorkstackArea> lst;
745 areas( mySplit, lst, true );
747 int idx = lst.find( area );
754 QtxWorkstackArea* newArea = neighbourArea( area );
755 if ( newArea && newArea->activeWidget() )
756 newArea->activeWidget()->setFocus();
758 QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
761 void QtxWorkstack::onContextMenuRequested( QPoint p )
766 QWidgetList lst = activeArea()->widgetList();
770 QPopupMenu* pm = new QPopupMenu();
771 connect( pm, SIGNAL( activated( int ) ), this, SLOT( onPopupActivated( int ) ) );
773 if ( lst.count() > 1 )
775 pm->insertItem( tr( "Split vertically" ), SplitVertical );
776 pm->insertItem( tr( "Split horizontally" ), SplitHorizontal );
777 pm->insertSeparator();
779 pm->insertItem( tr( "Close" ), Close );
786 void QtxWorkstack::childEvent( QChildEvent* e )
788 if ( e->inserted() && e->child()->isWidgetType() )
790 QWidget* w = (QWidget*)e->child();
791 if ( w && w != mySplit )
793 targetArea()->insertWidget( w );
797 QWidget::childEvent( e );
800 void QtxWorkstack::customEvent( QCustomEvent* e )
805 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
810 QSplitter* split = 0;
812 QWidget* wid = area->parentWidget();
813 if ( wid && wid->inherits( "QSplitter" ) )
814 split = (QSplitter*)wid;
819 void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
824 const QObjectList* objs = split->children();
827 for ( QObjectListIt it( *objs ); it.current(); ++it )
830 splitters( (QSplitter*)it.current(), splitList, rec );
831 if ( it.current()->inherits( "QSplitter" ) )
832 splitList.append( (QSplitter*)it.current() );
837 void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
842 const QObjectList* objs = split->children();
845 for ( QObjectListIt it( *objs ); it.current(); ++it )
847 if ( it.current()->inherits( "QtxWorkstackArea" ) )
848 areaList.append( (QtxWorkstackArea*)it.current() );
849 else if ( rec && it.current()->inherits( "QSplitter" ) )
850 areas( (QSplitter*)it.current(), areaList, rec );
855 QtxWorkstackArea* QtxWorkstack::activeArea() const
860 QtxWorkstackArea* QtxWorkstack::targetArea()
862 QtxWorkstackArea* area = activeArea();
864 area = currentArea();
867 QPtrList<QtxWorkstackArea> lst;
868 areas( mySplit, lst );
869 if ( !lst.isEmpty() )
874 area = createArea( mySplit );
879 QtxWorkstackArea* QtxWorkstack::currentArea() const
881 QtxWorkstackArea* area = 0;
882 QWidget* wid = focusWidget();
883 while ( wid && !area )
885 if ( wid->inherits( "QtxWorkstackArea" ) )
886 area = (QtxWorkstackArea*)wid;
887 wid = wid->parentWidget();
893 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
895 QtxWorkstackArea* area = new QtxWorkstackArea( parent );
897 connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
898 connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
899 connect( area, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
900 connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
905 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
907 QWidget* oldCur = myWin;
909 QtxWorkstackArea* oldArea = myArea;
913 if ( myArea != oldArea )
916 oldArea->updateActiveState();
918 myArea->updateActiveState();
922 myWin = myArea->activeWidget();
924 if ( myWin && oldCur != myWin )
925 emit windowActivated( myWin );
928 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
930 QPtrList<QtxWorkstackArea> lst;
931 areas( mySplit, lst, true );
932 int pos = lst.find( area );
936 QtxWorkstackArea* na = 0;
937 for ( int i = pos - 1; i >= 0 && !na; i-- )
939 if ( !lst.at( i )->isEmpty() )
943 for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
945 if ( !lst.at( j )->isEmpty() )
951 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
953 QtxWorkstackArea* area = 0;
954 QPtrList<QtxWorkstackArea> lst;
955 areas( mySplit, lst, true );
956 for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current() && !area; ++it )
958 QtxWorkstackArea* cur = it.current();
959 QRect r = cur->geometry();
960 if ( cur->parentWidget() )
961 r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
962 if ( r.contains( p ) )
968 void QtxWorkstack::updateState()
970 updateState( mySplit );
973 void QtxWorkstack::updateState( QSplitter* split )
975 QPtrList<QSplitter> recList;
976 splitters( split, recList, false );
977 for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
978 updateState( itr.current() );
980 QPtrList<QSplitter> splitList;
981 splitters( split, splitList, false );
983 QPtrList<QtxWorkstackArea> areaList;
984 areas( split, areaList, false );
987 for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
989 if ( it.current()->isEmpty() )
990 it.current()->hide();
993 it.current()->show();
998 if ( split == mySplit )
1001 for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current() && !vis; ++iter )
1002 vis = iter.current()->isVisibleTo( iter.current()->parentWidget() );
1004 if ( areaList.isEmpty() && splitList.isEmpty() )
1013 Class: QtxWorkstackArea [Internal]
1017 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
1020 QVBoxLayout* base = new QVBoxLayout( this );
1022 QHBox* top = new QHBox( this );
1023 base->addWidget( top );
1025 myBar = new QtxWorkstackTabBar( top );
1027 QPushButton* close = new QPushButton( top );
1028 close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
1029 close->setAutoDefault( true );
1030 close->setFlat( true );
1033 top->setStretchFactor( myBar, 1 );
1035 myStack = new QWidgetStack( this );
1037 base->addWidget( myStack, 1 );
1039 connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
1040 connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
1041 connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
1042 connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SIGNAL( contextMenuRequested( QPoint ) ) );
1046 updateActiveState();
1048 qApp->installEventFilter( this );
1051 QtxWorkstackArea::~QtxWorkstackArea()
1053 qApp->removeEventFilter( this );
1056 bool QtxWorkstackArea::isEmpty() const
1059 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
1060 res = it.data().vis;
1064 void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
1069 int pos = myList.find( wid );
1070 if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
1073 myList.removeRef( wid );
1074 pos = idx < 0 ? myList.count() : idx;
1075 myList.insert( QMIN( pos, (int)myList.count() ), wid );
1076 if ( !myInfo.contains( wid ) )
1078 QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
1079 myChild.insert( wid, child );
1080 myInfo.insert( wid, WidgetInfo() );
1081 myInfo[wid].id = generateId();
1082 myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
1084 connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
1085 connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
1086 connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
1087 connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
1088 connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
1093 setWidgetActive( wid );
1096 void QtxWorkstackArea::removeWidget( QWidget* wid )
1098 if ( !myList.contains( wid ) )
1101 if ( myBar->tab( widgetId( wid ) ) )
1102 myBar->removeTab( myBar->tab( widgetId( wid ) ) );
1103 myStack->removeWidget( child( wid ) );
1105 myList.remove( wid );
1106 myInfo.remove( wid );
1107 myChild.remove( wid );
1109 delete child( wid );
1111 if ( myList.isEmpty() )
1117 QWidgetList QtxWorkstackArea::widgetList() const
1120 for ( QWidgetListIt it( myList ); it.current(); ++it )
1122 if ( widgetVisibility( it.current() ) )
1123 lst.append( it.current() );
1128 QWidget* QtxWorkstackArea::activeWidget() const
1130 return widget( myBar->currentTab() );
1133 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
1135 myBar->setCurrentTab( widgetId( wid ) );
1138 bool QtxWorkstackArea::contains( QWidget* wid ) const
1140 return myList.contains( wid );
1143 void QtxWorkstackArea::show()
1145 QMap<QWidget*, bool> map;
1146 for ( QWidgetListIt it( myList ); it.current(); ++it )
1148 map.insert( it.current(), isBlocked( it.current() ) );
1149 setBlocked( it.current(), true );
1154 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1155 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1158 void QtxWorkstackArea::hide()
1160 QMap<QWidget*, bool> map;
1161 for ( QWidgetListIt it( myList ); it.current(); ++it )
1163 map.insert( it.current(), isBlocked( it.current() ) );
1164 setBlocked( it.current(), true );
1169 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1170 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1173 bool QtxWorkstackArea::isActive() const
1175 QtxWorkstack* ws = workstack();
1179 return ws->activeArea() == this;
1182 void QtxWorkstackArea::updateActiveState()
1184 myBar->setActive( isActive() );
1187 QtxWorkstack* QtxWorkstackArea::workstack() const
1189 QtxWorkstack* ws = 0;
1190 QWidget* wid = parentWidget();
1191 while ( wid && !ws )
1193 if ( wid->inherits( "QtxWorkstack" ) )
1194 ws = (QtxWorkstack*)wid;
1195 wid = wid->parentWidget();
1200 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
1202 if ( o->isWidgetType() )
1204 QWidget* wid = (QWidget*)o;
1205 if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
1208 while ( !ok && wid && wid != myClose )
1211 wid = wid->parentWidget();
1214 QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
1220 QRect QtxWorkstackArea::floatRect() const
1222 QRect r = myStack->geometry();
1223 return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
1226 QRect QtxWorkstackArea::floatTab( const int idx ) const
1228 return myBar->tabRect( idx );
1231 int QtxWorkstackArea::tabAt( const QPoint& p ) const
1234 for ( int i = 0; i < myBar->count() && idx == -1; i++ )
1236 QRect r = myBar->tabRect( i );
1237 if ( r.isValid() && r.contains( p ) )
1243 void QtxWorkstackArea::customEvent( QCustomEvent* e )
1245 switch ( e->type() )
1247 case ActivateWidget:
1248 emit activated( activeWidget() );
1251 if ( activeWidget() && !activeWidget()->focusWidget() )
1252 activeWidget()->setFocus();
1255 removeWidget( (QWidget*)e->data() );
1260 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
1262 QWidget::focusInEvent( e );
1264 emit activated( activeWidget() );
1267 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
1269 QWidget::mousePressEvent( e );
1271 emit activated( activeWidget() );
1274 void QtxWorkstackArea::onClose()
1276 QWidget* wid = activeWidget();
1281 void QtxWorkstackArea::onSelected( int id )
1285 emit activated( activeWidget() );
1288 void QtxWorkstackArea::onDragActiveTab()
1290 QtxWorkstackChild* c = child( activeWidget() );
1294 new QtxWorkstackDrag( workstack(), c );
1297 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
1299 QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
1300 myStack->removeWidget( child );
1303 for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
1305 if ( it.data() == child )
1309 myChild.remove( wid );
1311 QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveWidget, wid ) );
1314 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
1316 setWidgetShown( c->widget(), true );
1319 void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
1321 setWidgetShown( c->widget(), false );
1324 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
1326 setWidgetActive( c->widget() );
1329 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
1331 updateTab( c->widget() );
1334 void QtxWorkstackArea::updateCurrent()
1336 QMap<QWidget*, bool> map;
1337 for ( QWidgetListIt it( myList ); it.current(); ++it )
1339 map.insert( it.current(), isBlocked( it.current() ) );
1340 setBlocked( it.current(), true );
1343 myStack->raiseWidget( myBar->currentTab() );
1345 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1346 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1349 void QtxWorkstackArea::updateTab( QWidget* wid )
1351 QTab* tab = myBar->tab( widgetId( wid ) );
1357 icoSet = QIconSet( *wid->icon() );
1359 tab->setIconSet( icoSet );
1360 tab->setText( wid->caption() );
1363 QWidget* QtxWorkstackArea::widget( const int id ) const
1366 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
1368 if ( it.data().id == id )
1374 int QtxWorkstackArea::widgetId( QWidget* wid ) const
1377 if ( myInfo.contains( wid ) )
1378 id = myInfo[wid].id;
1382 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
1385 if ( myInfo.contains( wid ) )
1386 res = myInfo[wid].vis;
1390 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
1392 int id = widgetId( wid );
1396 myBar->setCurrentTab( id );
1399 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
1401 if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
1404 myInfo[wid].vis = on;
1408 void QtxWorkstackArea::updateState()
1410 bool updBar = myBar->isUpdatesEnabled();
1411 bool updStk = myStack->isUpdatesEnabled();
1412 myBar->setUpdatesEnabled( false );
1413 myStack->setUpdatesEnabled( false );
1415 bool block = myBar->signalsBlocked();
1416 myBar->blockSignals( true );
1418 QWidget* prev = activeWidget();
1421 for ( QWidgetListIt it( myList ); it.current(); ++it )
1423 QWidget* wid = it.current();
1424 int id = widgetId( wid );
1429 bool vis = widgetVisibility( wid );
1431 if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
1432 myBar->removeTab( myBar->tab( id ) );
1434 if ( !myBar->tab( id ) && vis )
1436 QTab* tab = new QTab( wid->caption() );
1437 myBar->insertTab( tab, idx );
1438 tab->setIdentifier( id );
1443 bool block = isBlocked( wid );
1444 setBlocked( wid, true );
1446 QtxWorkstackChild* cont = child( wid );
1449 myStack->removeWidget( cont );
1450 else if ( !myStack->widget( id ) )
1451 myStack->addWidget( cont, id );
1456 setBlocked( wid, block );
1459 int curId = widgetId( prev );
1460 if ( !myBar->tab( curId ) )
1463 int pos = myList.find( prev );
1464 for ( int i = pos - 1; i >= 0 && !wid; i-- )
1466 if ( widgetVisibility( myList.at( i ) ) )
1467 wid = myList.at( i );
1470 for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
1472 if ( widgetVisibility( myList.at( j ) ) )
1473 wid = myList.at( j );
1477 curId = widgetId( wid );
1480 myBar->setCurrentTab( curId );
1482 myBar->blockSignals( block );
1486 myBar->setUpdatesEnabled( updBar );
1487 myStack->setUpdatesEnabled( updStk );
1493 QResizeEvent re( myBar->size(), myBar->size() );
1494 QApplication::sendEvent( myBar, &re );
1499 emit deactivated( this );
1504 if ( prev != activeWidget() )
1505 emit activated( activeWidget() );
1509 int QtxWorkstackArea::generateId() const
1513 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
1514 map.insert( it.data().id, 0 );
1517 while ( map.contains( id ) )
1523 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
1525 return myBlock.contains( wid );
1528 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
1531 myBlock.insert( wid, 0 );
1533 myBlock.remove( wid );
1536 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1538 QtxWorkstackChild* res = 0;
1539 if ( myChild.contains( wid ) )
1545 Class: QtxWorkstackChild [Internal]
1549 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
1553 myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
1554 myWidget->installEventFilter( this );
1556 connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1559 QtxWorkstackChild::~QtxWorkstackChild()
1561 qApp->removeEventFilter( this );
1566 widget()->removeEventFilter( this );
1567 widget()->reparent( 0, QPoint( 0, 0 ), false );
1568 disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1571 QWidget* QtxWorkstackChild::widget() const
1576 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1578 if ( o->isWidgetType() )
1580 if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
1581 emit captionChanged( this );
1583 if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
1586 if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
1589 if ( e->type() == QEvent::FocusIn )
1590 emit activated( this );
1592 return QHBox::eventFilter( o, e );
1595 void QtxWorkstackChild::onDestroyed( QObject* obj )
1597 if ( obj != widget() )
1604 void QtxWorkstackChild::childEvent( QChildEvent* e )
1606 if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
1611 QHBox::childEvent( e );
1615 Class: QtxWorkstackTabBar [Internal]
1619 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1620 : QTabBar( parent ),
1625 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1629 void QtxWorkstackTabBar::setActive( const bool on )
1631 QFont aFont = font();
1632 aFont.setUnderline( on );
1638 QRect QtxWorkstackTabBar::tabRect( const int idx ) const
1641 QTab* t = tabAt( idx );
1645 r.setLeft( QMAX( r.left(), 0 ) );
1647 int x1 = tabAt( 0 )->rect().left();
1648 int x2 = tabAt( count() - 1 )->rect().right();
1651 if ( QABS( x2 - x1 ) > width() )
1652 #if defined QT_VERSION && QT_VERSION >= 0x30300
1653 bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
1658 int limit = width() - bw;
1659 r.setRight( QMIN( r.right(), limit ) );
1661 r = QRect( mapToGlobal( r.topLeft() ), r.size() );
1666 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1668 if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
1671 emit dragActiveTab();
1674 QTabBar::mouseMoveEvent( e );
1677 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1679 QTabBar::mousePressEvent( e );
1681 if ( e->button() == LeftButton )
1682 myId = currentTab();
1685 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1687 QTabBar::mouseReleaseEvent( e );
1691 if ( e->button() == RightButton )
1692 emit contextMenuRequested( e->globalPos() );
1695 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1697 if ( e->reason() != QContextMenuEvent::Mouse )
1698 emit contextMenuRequested( e->globalPos() );
1701 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1703 if ( currentTab() != t->identifier() )
1705 QFont fnt = p->font();
1706 fnt.setUnderline( false );
1709 QTabBar::paintLabel( p, br, t, has_focus );
1713 Class: QtxWorkstackDrag [Internal]
1717 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
1725 qApp->installEventFilter( this );
1728 QtxWorkstackDrag::~QtxWorkstackDrag()
1730 qApp->removeEventFilter( this );
1735 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
1737 switch ( e->type() )
1739 case QEvent::MouseMove:
1740 updateTarget( ((QMouseEvent*)e)->globalPos() );
1742 case QEvent::MouseButtonRelease:
1754 void QtxWorkstackDrag::updateTarget( const QPoint& p )
1757 QtxWorkstackArea* area = detectTarget( p, tab );
1758 setTarget( area, tab );
1761 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
1766 QtxWorkstackArea* area = myWS->areaAt( p );
1768 tab = area->tabAt( p );
1772 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
1774 if ( !area || ( myArea == area && tab == myTab ) )
1789 void QtxWorkstackDrag::dropWidget()
1792 myArea->insertWidget( myChild->widget(), myTab );
1795 void QtxWorkstackDrag::drawRect()
1797 if ( !myPainter || !myArea )
1800 QRect r = myArea->floatRect();
1801 int m = myPainter->pen().width();
1803 r.setTop( r.top() + m + 2 );
1804 r.setLeft( r.left() + m + 2 );
1805 r.setRight( r.right() - m - 2 );
1806 r.setBottom( r.bottom() - m - 2 );
1808 myPainter->drawRect( r );
1810 QRect tr = myArea->floatTab( myTab );
1811 tr.setTop( tr.top() + m );
1812 tr.setLeft( tr.left() + m );
1813 tr.setRight( tr.right() - m );
1814 tr.setBottom( tr.bottom() - m );
1816 myPainter->drawRect( tr );
1819 void QtxWorkstackDrag::endDrawRect()
1825 void QtxWorkstackDrag::startDrawRect()
1830 int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
1831 QWidget* paint_on = QApplication::desktop()->screen( scr );
1833 myPainter = new QPainter( paint_on, true );
1834 myPainter->setPen( QPen( gray, 3 ) );
1835 myPainter->setRasterOp( XorROP );