1 // File: QtxWorkstack.cxx
2 // Author: Sergey TELKOV
4 #include "QtxWorkstack.h"
12 #include <qsplitter.h>
13 #include <qpopupmenu.h>
14 #include <qobjectlist.h>
15 #include <qpushbutton.h>
16 #include <qwidgetstack.h>
17 #include <qapplication.h>
20 Class: QtxWorkstack [Public]
24 QtxWorkstack::QtxWorkstack( QWidget* parent )
29 myActionsMap.insert( SplitVertical, new QAction( tr( "Split vertically" ), 0, this ));
30 myActionsMap.insert( SplitHorizontal, new QAction( tr( "Split horizontally" ), 0, this ));
31 myActionsMap.insert( Close, new QAction( tr( "Close" ), 0, this ));
33 connect( myActionsMap[SplitVertical], SIGNAL( activated() ), this, SLOT( splitVertical() ) );
34 connect( myActionsMap[SplitHorizontal], SIGNAL( activated() ), this, SLOT( splitHorizontal() ) );
35 connect( myActionsMap[Close], SIGNAL( activated() ), this, SLOT( onCloseWindow() ) );
37 QVBoxLayout* base = new QVBoxLayout( this );
38 mySplit = new QSplitter( this );
39 mySplit->setChildrenCollapsible( false );
40 base->addWidget( mySplit );
43 QtxWorkstack::~QtxWorkstack()
47 QWidgetList QtxWorkstack::windowList() const
49 QPtrList<QtxWorkstackArea> lst;
50 areas( mySplit, lst, true );
53 for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current(); ++it )
55 QWidgetList wids = it.current()->widgetList();
56 for ( QWidgetListIt itr( wids ); itr.current(); ++itr )
57 widList.append( itr.current() );
63 QWidgetList QtxWorkstack::splitWindowList() const
65 return myArea ? myArea->widgetList() : QWidgetList();
68 QWidget* QtxWorkstack::activeWindow() const
73 void QtxWorkstack::split( const int o )
75 QtxWorkstackArea* area = activeArea();
79 if ( area->widgetList().count() < 2 )
82 QWidget* curWid = area->activeWidget();
86 QSplitter* s = splitter( area );
87 QPtrList<QtxWorkstackArea> areaList;
90 QPtrList<QSplitter> splitList;
91 splitters( s, splitList );
94 if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
98 trg = wrapSplitter( area );
103 trg->setOrientation( (Orientation)o );
105 QtxWorkstackArea* newArea = createArea( 0 );
106 insertWidget( newArea, trg, area );
108 area->removeWidget( curWid );
109 newArea->insertWidget( curWid );
111 distributeSpace( trg );
118 * \brief Split workarea of the given widget on two parts.
119 * \param wid - widget, belonging to this workstack
120 * \param o - orientation of splitting (Qt::Horizontal or Qt::Vertical)
121 * \param type - type of splitting, see <VAR>SplitType</VAR> enumeration
123 void QtxWorkstack::Split (QWidget* wid, const Qt::Orientation o, const SplitType type)
127 // find area of the given widget
128 QtxWorkstackArea* area = NULL;
129 QPtrList<QtxWorkstackArea> allAreas;
130 areas(mySplit, allAreas, true);
132 QPtrListIterator<QtxWorkstackArea> it (allAreas);
133 for (; it.current() && !area; ++it) {
134 if (it.current()->contains(wid))
139 QWidgetList wids = area->widgetList();
140 if (wids.count() < 2)
143 QSplitter* s = splitter(area);
144 QPtrList<QtxWorkstackArea> areaList;
147 QPtrList<QSplitter> splitList;
148 splitters(s, splitList);
151 if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
154 if (!trg) trg = wrapSplitter(area);
157 trg->setOrientation(o);
159 QtxWorkstackArea* newArea = createArea(0);
160 insertWidget(newArea, trg, area);
165 QWidgetListIt itr (wids);
166 for (; itr.current(); ++itr)
168 QWidget* wid_i = itr.current();
170 area->removeWidget(wid_i);
171 newArea->insertWidget(wid_i);
178 QWidgetListIt itr (wids);
179 for (; itr.current() && itr.current() != wid; ++itr) {
181 for (; itr.current(); ++itr) {
182 area->removeWidget(itr.current());
183 newArea->insertWidget(itr.current());
188 area->removeWidget(wid);
189 newArea->insertWidget(wid);
193 distributeSpace(trg);
197 * \brief Put given widget on top of its workarea
198 * \param wid - widget, belonging to this workstack
201 void QtxWorkstack::OnTop (QWidget* wid)
206 // find area of the given widget
207 QtxWorkstackArea* area = 0;
208 QPtrList<QtxWorkstackArea> allAreas;
209 areas( mySplit, allAreas, true );
210 for ( QPtrListIterator<QtxWorkstackArea> it( allAreas ); it.current() && !area; ++it )
212 if ( it.current()->contains( wid ) )
217 area->setActiveWidget( wid );
222 * \brief Move widget(s) from source workarea into target workarea
223 * or just reorder widgets inside one workarea.
224 * \param wid1 - widget from target workarea
225 * \param wid2 - widget from source workarea
226 * \param all - if this parameter is TRUE, all widgets from source workarea will
227 * be moved into the target one, else only the \a wid2 will be moved
229 * Move \a wid2 in target workarea. Put it right after \a wid1.
230 * If value of boolean argument is TRUE, all widgets from source workarea
231 * will be moved together with \a wid2, source workarea will be deleted.
232 * If \a wid1 and \a wid2 belongs to one workarea, simple reordering will take place.
234 void QtxWorkstack::Attract ( QWidget* wid1, QWidget* wid2, const bool all )
236 if ( !wid1 || !wid2 )
239 // find area of the widgets
240 QtxWorkstackArea *area1 = NULL, *area2 = NULL;
241 QPtrList<QtxWorkstackArea> allAreas;
242 areas(mySplit, allAreas, true);
243 QPtrListIterator<QtxWorkstackArea> it (allAreas);
244 for (; it.current() && !(area1 && area2); ++it) {
245 if (it.current()->contains(wid1))
246 area1 = it.current();
247 if (it.current()->contains(wid2))
248 area2 = it.current();
250 if (!area1 || !area2) return;
252 QWidget* curWid = area1->activeWidget();
255 if (area1 == area2) {
257 // Set wid1 at first position, wid2 at second
258 area1->insertWidget(wid1);
259 area1->insertWidget(wid2, 1);
261 // Set wid2 right after wid1
262 area1->removeWidget(wid2);
264 QWidgetList wids1 = area1->widgetList();
265 QWidgetListIt itr1 (wids1);
266 for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
267 area1->insertWidget(wid2, wid1_ind + 1);
271 QWidgetList wids1 = area1->widgetList();
272 QWidgetListIt itr1 (wids1);
273 for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
276 // Set wid2 right after wid1, other widgets from area2 right after wid2
277 QWidgetList wids2 = area2->widgetList();
278 QWidgetListIt itr2 (wids2);
279 for (int ind = wid1_ind + 1; itr2.current(); ++itr2, ++ind)
281 area2->removeWidget(itr2.current());
282 if (itr2.current() == wid2) {
283 area1->insertWidget(itr2.current(), wid1_ind + 1);
285 area1->insertWidget(itr2.current(), ind);
289 // Set wid2 right after wid1
290 area2->removeWidget(wid2);
291 area1->insertWidget(wid2, wid1_ind + 1);
295 area1->setActiveWidget( curWid );
298 static void setSizes (QIntList& szList, const int item_ind,
299 const int new_near, const int new_this, const int new_farr)
301 // set size to all items before an item # <item_ind>
303 QIntList::iterator its = szList.begin();
304 for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
307 if (its == szList.end()) return;
308 // set size to item # <item_ind>
311 // set size to all items after an item # <item_ind>
312 for (; its != szList.end(); ++its) {
318 * \brief Set position of the widget relatively its splitter.
319 * \param wid - widget to set position of
320 * \param pos - position relatively splitter. Value in range [0..1].
322 * Orientation of positioning will correspond to the splitter orientation.
324 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
326 if ( position < 0.0 || 1.0 < position)
332 // find area of the given widget
333 QtxWorkstackArea* area = NULL;
334 QPtrList<QtxWorkstackArea> allAreas;
335 areas(mySplit, allAreas, true);
336 for ( QPtrListIterator<QtxWorkstackArea> it( allAreas );
337 it.current() && !area;
340 if (it.current()->contains(wid))
347 QSplitter* split = splitter( area );
351 // find index of the area in its splitter
353 bool isFound = false;
354 const QObjectList* was = split->children();
355 for (QObjectListIt ito (*was); ito.current() && !isFound; ++ito, ++item_ind)
357 if (ito.current() == area)
360 if (!isFound || item_ind == 0)
363 QIntList szList = split->sizes();
364 int splitter_size = (split->orientation() == Horizontal ?
365 split->width() : split->height());
366 int nb = szList.count();
368 int new_prev = int(splitter_size * position / item_ind);
369 int new_next = int(splitter_size * (1.0 - position) / (nb - item_ind));
370 setSizes (szList, item_ind, new_prev, new_next, new_next);
371 split->setSizes(szList);
375 * \brief Set position of the widget relatively the entire workstack.
376 * \param wid - widget to set position of
377 * \param o - orientation of positioning (Qt::Horizontal or Qt::Vertical).
378 * If o = Qt::Horizontal, horizontal position of \a wid will be changed.
379 * If o = Qt::Vertical, vertical position of \a wid will be changed.
380 * \param pos - position relatively workstack. Value in range [0..1].
382 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
383 const double position )
385 if ( position < 0.0 || 1.0 < position )
391 int splitter_size = o == Horizontal ? mySplit->width() : mySplit->height();
392 int need_pos = int( position * splitter_size );
393 int splitter_pos = 0;
395 if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
397 // impossible to set required position
402 * \brief Sets the action's accelerator key to accel.
403 * \param id - the key of the action in the actions map.
404 * \param accel - action's accelerator key.
406 void QtxWorkstack::setAccel( const int id, const int accel )
408 if ( !myActionsMap.contains( id ) )
411 myActionsMap[id]->setAccel( accel );
415 * \brief Returns the action's accelerator key.
416 * \param id - the key of the action in the actions map.
417 * \retval int - action's accelerator key.
419 int QtxWorkstack::accel( const int id ) const
422 if ( myActionsMap.contains( id ) )
423 res = myActionsMap[id]->accel();
427 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
428 const int item_ind, const int item_rel_pos,
429 const int need_pos, const int splitter_pos)
431 if (item_ind == 0) { // cannot move in this splitter
432 return (need_pos - splitter_pos);
437 int new_this = szList[item_ind];
440 bool isToCheck = false;
442 if (need_pos < splitter_pos) {
443 // Set size of all previous workareas to zero <--
444 if (item_ind == nb - 1) {
445 // item iz last in the splitter, it will occupy all the splitter
446 new_this = splitter_size;
448 // recompute size of next items in splitter
449 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
451 delta = need_pos - splitter_pos;
453 } else if (need_pos > (splitter_pos + splitter_size)) {
454 // Set size of all next workareas to zero -->
455 // recompute size of previous items in splitter
457 new_prev = (splitter_size - new_this) / item_ind;
458 delta = need_pos - (splitter_pos + splitter_size - new_this);
460 } else { // required position lays inside this splitter
461 // Move workarea inside splitter into required position <->
462 int new_item_rel_pos = need_pos - splitter_pos;
463 new_prev = new_item_rel_pos / item_ind;
464 if (need_pos < (splitter_pos + item_rel_pos)) {
465 // Make previous workareas smaller, next - bigger
466 // No problem to keep old size of the widget
468 // Make previous workareas bigger, next - smaller
469 if (new_this > splitter_size - new_item_rel_pos) {
470 new_this = splitter_size - new_item_rel_pos;
472 // jfa to do: in this case fixed size of next widgets could prevent right resizing
475 if (item_ind == nb - 1) {
476 new_this = splitter_size - new_item_rel_pos;
478 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
483 setSizes (szList, item_ind, new_prev, new_this, new_next);
488 * \brief Set position of given widget.
489 * \param wid - widget to be moved
490 * \param split - currently processed splitter (goes from more common
491 * to more particular splitter in recursion calls)
492 * \param o - orientation of positioning
493 * \param need_pos - required position of the given widget in pixels
494 * (from top/left side of workstack area)
495 * \param splitter_pos - position of the splitter \a split
496 * (from top/left side of workstack area)
497 * \retval int - returns difference between a required and a distinguished position.
499 * Internal method. Recursively calls itself.
500 * Is called from <VAR>SetRelativePosition</VAR> public method.
502 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
503 const int need_pos, const int splitter_pos )
505 if ( !wid || !split )
506 return need_pos - splitter_pos;
508 // Find corresponding sub-splitter.
509 // Find also index of appropriate item in current splitter.
510 int cur_ind = 0, item_ind = 0;
511 bool isBottom = false, isFound = false;
512 QSplitter* sub_split = NULL;
513 const QObjectList* objs = split->children();
516 for (QObjectListIt it (*objs); it.current() && !isFound; ++it)
518 if (it.current()->inherits( "QtxWorkstackArea")) {
519 if (((QtxWorkstackArea*)it.current())->contains(wid)) {
525 } else if (it.current()->inherits("QSplitter")) {
526 QPtrList<QtxWorkstackArea> areaList;
527 areas((QSplitter*)it.current(), areaList, true);
528 for (QPtrListIterator<QtxWorkstackArea> ita (areaList);
529 ita.current() && !isFound;
532 if (ita.current()->contains(wid)) {
535 sub_split = (QSplitter*)it.current();
543 return (need_pos - splitter_pos);
545 if (split->orientation() == o) {
546 // Find coordinates of near and far sides of the appropriate item relatively current splitter
547 int splitter_size = (o == Horizontal ? split->width() : split->height());
548 QIntList szList = split->sizes();
549 int nb = szList.count();
550 int item_rel_pos = 0; // position of near side of item relatively this splitter
551 for (int i = 0; i < item_ind; i++) {
552 item_rel_pos += szList[i];
554 int item_size = szList[item_ind]; // size of item
555 int item_pos = splitter_pos + item_rel_pos;
557 // Resize splitter items to complete the conditions
559 // I. Bottom of splitters stack reached
561 int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
562 split->setSizes(szList);
563 // Recompute delta, as some windows can reject given size
564 int new_item_rel_pos = 0;
565 QIntList szList1 = split->sizes();
566 for (int i = 0; i < item_ind; i++) {
567 new_item_rel_pos += szList1[i];
569 delta = need_pos - (splitter_pos + new_item_rel_pos);
573 // II. Bottom of splitters stack is not yet reached
575 if (item_ind == 0) { // cannot move in this splitter
576 // Process in sub-splitter
577 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
581 int new_this = szList[item_ind];
584 if (need_pos < splitter_pos) {
585 // Set size of all previous workareas to zero <--
586 if (item_ind == nb - 1) {
587 new_this = splitter_size;
589 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
591 setSizes (szList, item_ind, new_prev, new_this, new_next);
592 split->setSizes(szList);
593 // Recompute splitter_pos, as some windows can reject given size
594 int new_item_rel_pos = 0;
595 QIntList szList1 = split->sizes();
596 for (int i = 0; i < item_ind; i++) {
597 new_item_rel_pos += szList1[i];
599 // Process in sub-splitter
600 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
601 } else if (need_pos > (splitter_pos + splitter_size)) {
602 // Set size of all next workareas to zero -->
603 new_prev = (splitter_size - new_this) / item_ind;
604 setSizes (szList, item_ind, new_prev, new_this, new_next);
605 split->setSizes(szList);
606 // Recompute splitter_pos, as some windows can reject given size
607 int new_item_rel_pos = 0;
608 QIntList szList1 = split->sizes();
609 for (int i = 0; i < item_ind; i++) {
610 new_item_rel_pos += szList1[i];
612 // Process in sub-splitter
613 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
615 // Set appropriate size of all previous/next items <->
616 int new_item_rel_pos = item_rel_pos;
617 if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
618 // Move item inside splitter into required position <->
619 int new_this = szList[item_ind];
621 new_item_rel_pos = need_pos - splitter_pos;
622 if ((item_pos + item_size) < need_pos) {
623 //new_item_rel_pos = need_pos - (item_pos + item_size);
624 new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
626 int new_prev = new_item_rel_pos / item_ind;
627 if (need_pos < (splitter_pos + item_rel_pos)) {
628 // Make previous workareas smaller, next - bigger
629 // No problem to keep old size of the widget
631 // Make previous workareas bigger, next - smaller
632 if (new_this > splitter_size - new_item_rel_pos) {
633 new_this = splitter_size - new_item_rel_pos;
636 if (item_ind == nb - 1) {
637 new_this = splitter_size - new_item_rel_pos;
639 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
641 setSizes (szList, item_ind, new_prev, new_this, new_next);
642 split->setSizes(szList);
643 // Recompute new_item_rel_pos, as some windows can reject given size
644 new_item_rel_pos = 0;
645 QIntList szList1 = split->sizes();
646 for (int i = 0; i < item_ind; i++) {
647 new_item_rel_pos += szList1[i];
652 // Process in sub-splitter
653 int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
657 // this can be if corresponding workarea is first in sub-splitter
658 // or sub-splitter has another orientation
660 // Resize ones again to reach precize position <->
661 int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
663 // Move workarea inside splitter into required position <->
664 int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
665 new_item_rel_pos, need_pos_1, splitter_pos);
666 split->setSizes(szList);
667 // Recompute new_item_rel_pos, as some windows can reject given size
668 new_item_rel_pos = 0;
669 QIntList szList1 = split->sizes();
670 for (int i = 0; i < item_ind; i++) {
671 new_item_rel_pos += szList1[i];
673 delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
678 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
684 void QtxWorkstack::distributeSpace( QSplitter* split ) const
689 QIntList szList = split->sizes();
690 int size = ( split->orientation() == Horizontal ?
691 split->width() : split->height() ) / szList.count();
692 for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
694 split->setSizes( szList );
697 void QtxWorkstack::splitVertical()
699 split( Qt::Horizontal );
702 void QtxWorkstack::splitHorizontal()
704 split( Qt::Vertical );
707 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
712 QSplitter* pSplit = splitter( area );
716 bool upd = pSplit->isUpdatesEnabled();
717 pSplit->setUpdatesEnabled( false );
719 QIntList szList = pSplit->sizes();
721 QSplitter* wrap = new QSplitter( 0 );
722 #if defined QT_VERSION && QT_VERSION >= 0x30200
723 wrap->setChildrenCollapsible( false );
725 insertWidget( wrap, pSplit, area );
726 area->reparent( wrap, QPoint( 0, 0 ), true );
728 pSplit->setSizes( szList );
730 pSplit->setUpdatesEnabled( upd );
735 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
740 QWidgetList moveList;
741 const QObjectList* lst = pWid->children();
745 for ( QObjectListIt it( *lst ); it.current(); ++it )
747 if ( found && ( it.current()->inherits( "QSplitter" ) ||
748 it.current()->inherits( "QtxWorkstackArea" ) ) )
749 moveList.append( (QWidget*)it.current() );
750 if ( it.current() == after )
755 QMap<QWidget*, bool> map;
756 for ( QWidgetListIt it( moveList ); it.current(); ++it )
758 map.insert( it.current(), it.current()->isVisibleTo( it.current()->parentWidget() ) );
759 it.current()->reparent( 0, QPoint( 0, 0 ), false );
762 wid->reparent( pWid, QPoint( 0, 0 ), true );
764 for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
765 itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
769 * \brief Closes the active window.
771 void QtxWorkstack::onCloseWindow()
773 if ( activeWindow() )
774 activeWindow()->close();
777 void QtxWorkstack::onDestroyed( QObject* obj )
779 QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
781 if ( area == myArea )
786 QtxWorkstackArea* cur = neighbourArea( area );
791 QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
794 void QtxWorkstack::onWindowActivated( QWidget* wid )
796 const QObject* obj = sender();
797 if ( !obj->inherits( "QtxWorkstackArea" ) )
800 setActiveArea( (QtxWorkstackArea*)obj );
803 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
805 if ( myArea != area )
808 QPtrList<QtxWorkstackArea> lst;
809 areas( mySplit, lst, true );
811 int idx = lst.find( area );
818 QtxWorkstackArea* newArea = neighbourArea( area );
819 if ( newArea && newArea->activeWidget() )
820 newArea->activeWidget()->setFocus();
822 QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
825 void QtxWorkstack::onContextMenuRequested( QPoint p )
830 QWidgetList lst = activeArea()->widgetList();
834 QPopupMenu* pm = new QPopupMenu();
836 if ( lst.count() > 1 )
838 myActionsMap[SplitVertical]->addTo( pm );
839 myActionsMap[SplitHorizontal]->addTo( pm );
840 pm->insertSeparator();
842 myActionsMap[Close]->addTo( pm );
849 void QtxWorkstack::childEvent( QChildEvent* e )
851 if ( e->inserted() && e->child()->isWidgetType() )
853 QWidget* w = (QWidget*)e->child();
854 if ( w && w != mySplit )
856 targetArea()->insertWidget( w );
860 QWidget::childEvent( e );
863 void QtxWorkstack::customEvent( QCustomEvent* e )
868 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
873 QSplitter* split = 0;
875 QWidget* wid = area->parentWidget();
876 if ( wid && wid->inherits( "QSplitter" ) )
877 split = (QSplitter*)wid;
882 void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
887 const QObjectList* objs = split->children();
890 for ( QObjectListIt it( *objs ); it.current(); ++it )
893 splitters( (QSplitter*)it.current(), splitList, rec );
894 if ( it.current()->inherits( "QSplitter" ) )
895 splitList.append( (QSplitter*)it.current() );
900 void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
905 const QObjectList* objs = split->children();
908 for ( QObjectListIt it( *objs ); it.current(); ++it )
910 if ( it.current()->inherits( "QtxWorkstackArea" ) )
911 areaList.append( (QtxWorkstackArea*)it.current() );
912 else if ( rec && it.current()->inherits( "QSplitter" ) )
913 areas( (QSplitter*)it.current(), areaList, rec );
918 QtxWorkstackArea* QtxWorkstack::activeArea() const
923 QtxWorkstackArea* QtxWorkstack::targetArea()
925 QtxWorkstackArea* area = activeArea();
927 area = currentArea();
930 QPtrList<QtxWorkstackArea> lst;
931 areas( mySplit, lst );
932 if ( !lst.isEmpty() )
937 area = createArea( mySplit );
942 QtxWorkstackArea* QtxWorkstack::currentArea() const
944 QtxWorkstackArea* area = 0;
945 QWidget* wid = focusWidget();
946 while ( wid && !area )
948 if ( wid->inherits( "QtxWorkstackArea" ) )
949 area = (QtxWorkstackArea*)wid;
950 wid = wid->parentWidget();
956 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
958 QtxWorkstackArea* area = new QtxWorkstackArea( parent );
960 connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
961 connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
962 connect( area, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
963 connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
968 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
970 QWidget* oldCur = myWin;
972 QtxWorkstackArea* oldArea = myArea;
976 if ( myArea != oldArea )
979 oldArea->updateActiveState();
981 myArea->updateActiveState();
985 myWin = myArea->activeWidget();
987 if ( myWin && oldCur != myWin )
988 emit windowActivated( myWin );
991 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
993 QPtrList<QtxWorkstackArea> lst;
994 areas( mySplit, lst, true );
995 int pos = lst.find( area );
999 QtxWorkstackArea* na = 0;
1000 for ( int i = pos - 1; i >= 0 && !na; i-- )
1002 if ( !lst.at( i )->isEmpty() )
1006 for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
1008 if ( !lst.at( j )->isEmpty() )
1014 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
1016 QtxWorkstackArea* area = 0;
1017 QPtrList<QtxWorkstackArea> lst;
1018 areas( mySplit, lst, true );
1019 for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current() && !area; ++it )
1021 QtxWorkstackArea* cur = it.current();
1022 QRect r = cur->geometry();
1023 if ( cur->parentWidget() )
1024 r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
1025 if ( r.contains( p ) )
1031 void QtxWorkstack::updateState()
1033 updateState( mySplit );
1036 void QtxWorkstack::updateState( QSplitter* split )
1038 QPtrList<QSplitter> recList;
1039 splitters( split, recList, false );
1040 for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
1041 updateState( itr.current() );
1043 QPtrList<QSplitter> splitList;
1044 splitters( split, splitList, false );
1046 QPtrList<QtxWorkstackArea> areaList;
1047 areas( split, areaList, false );
1050 for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
1052 if ( it.current()->isEmpty() )
1053 it.current()->hide();
1056 it.current()->show();
1061 if ( split == mySplit )
1064 for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current() && !vis; ++iter )
1065 vis = iter.current()->isVisibleTo( iter.current()->parentWidget() );
1067 if ( areaList.isEmpty() && splitList.isEmpty() )
1076 Class: QtxWorkstackArea [Internal]
1080 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
1083 QVBoxLayout* base = new QVBoxLayout( this );
1085 QHBox* top = new QHBox( this );
1086 base->addWidget( top );
1088 myBar = new QtxWorkstackTabBar( top );
1090 QPushButton* close = new QPushButton( top );
1091 close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
1092 close->setAutoDefault( true );
1093 close->setFlat( true );
1096 top->setStretchFactor( myBar, 1 );
1098 myStack = new QWidgetStack( this );
1100 base->addWidget( myStack, 1 );
1102 connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
1103 connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
1104 connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
1105 connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SIGNAL( contextMenuRequested( QPoint ) ) );
1109 updateActiveState();
1111 qApp->installEventFilter( this );
1114 QtxWorkstackArea::~QtxWorkstackArea()
1116 qApp->removeEventFilter( this );
1119 bool QtxWorkstackArea::isEmpty() const
1122 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
1123 res = it.data().vis;
1127 void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
1132 int pos = myList.find( wid );
1133 if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
1136 myList.removeRef( wid );
1137 pos = idx < 0 ? myList.count() : idx;
1138 myList.insert( QMIN( pos, (int)myList.count() ), wid );
1139 if ( !myInfo.contains( wid ) )
1141 QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
1142 myChild.insert( wid, child );
1143 myInfo.insert( wid, WidgetInfo() );
1144 myInfo[wid].id = generateId();
1145 myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
1147 connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
1148 connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
1149 connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
1150 connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
1151 connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
1152 connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
1157 setWidgetActive( wid );
1160 void QtxWorkstackArea::onWidgetDestroyed()
1163 removeWidget( (QWidget*)sender(), false );
1166 void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
1168 if ( !myList.contains( wid ) )
1171 if ( myBar->tab( widgetId( wid ) ) )
1172 myBar->removeTab( myBar->tab( widgetId( wid ) ) );
1173 myStack->removeWidget( child( wid ) );
1175 myList.remove( wid );
1176 myInfo.remove( wid );
1177 myChild.remove( wid );
1181 delete child( wid );
1182 if( myList.isEmpty() )
1191 QWidgetList QtxWorkstackArea::widgetList() const
1194 for ( QWidgetListIt it( myList ); it.current(); ++it )
1196 if ( widgetVisibility( it.current() ) )
1197 lst.append( it.current() );
1202 QWidget* QtxWorkstackArea::activeWidget() const
1204 return widget( myBar->currentTab() );
1207 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
1209 myBar->setCurrentTab( widgetId( wid ) );
1212 bool QtxWorkstackArea::contains( QWidget* wid ) const
1214 return myList.contains( wid );
1217 void QtxWorkstackArea::show()
1219 QMap<QWidget*, bool> map;
1220 for ( QWidgetListIt it( myList ); it.current(); ++it )
1222 map.insert( it.current(), isBlocked( it.current() ) );
1223 setBlocked( it.current(), true );
1228 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1229 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1232 void QtxWorkstackArea::hide()
1234 QMap<QWidget*, bool> map;
1235 for ( QWidgetListIt it( myList ); it.current(); ++it )
1237 map.insert( it.current(), isBlocked( it.current() ) );
1238 setBlocked( it.current(), true );
1243 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1244 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1247 bool QtxWorkstackArea::isActive() const
1249 QtxWorkstack* ws = workstack();
1253 return ws->activeArea() == this;
1256 void QtxWorkstackArea::updateActiveState()
1258 myBar->setActive( isActive() );
1261 QtxWorkstack* QtxWorkstackArea::workstack() const
1263 QtxWorkstack* ws = 0;
1264 QWidget* wid = parentWidget();
1265 while ( wid && !ws )
1267 if ( wid->inherits( "QtxWorkstack" ) )
1268 ws = (QtxWorkstack*)wid;
1269 wid = wid->parentWidget();
1274 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
1276 if ( o->isWidgetType() )
1278 QWidget* wid = (QWidget*)o;
1279 if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
1282 while ( !ok && wid && wid != myClose )
1285 wid = wid->parentWidget();
1288 QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
1294 QRect QtxWorkstackArea::floatRect() const
1296 QRect r = myStack->geometry();
1297 return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
1300 QRect QtxWorkstackArea::floatTab( const int idx ) const
1302 return myBar->tabRect( idx );
1305 int QtxWorkstackArea::tabAt( const QPoint& p ) const
1308 for ( int i = 0; i < myBar->count() && idx == -1; i++ )
1310 QRect r = myBar->tabRect( i );
1311 if ( r.isValid() && r.contains( p ) )
1317 void QtxWorkstackArea::customEvent( QCustomEvent* e )
1319 switch ( e->type() )
1321 case ActivateWidget:
1322 emit activated( activeWidget() );
1325 if ( activeWidget() && !activeWidget()->focusWidget() )
1326 activeWidget()->setFocus();
1329 removeWidget( (QWidget*)e->data() );
1334 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
1336 QWidget::focusInEvent( e );
1338 emit activated( activeWidget() );
1341 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
1343 QWidget::mousePressEvent( e );
1345 emit activated( activeWidget() );
1348 void QtxWorkstackArea::onClose()
1350 QWidget* wid = activeWidget();
1355 void QtxWorkstackArea::onSelected( int id )
1359 emit activated( activeWidget() );
1362 void QtxWorkstackArea::onDragActiveTab()
1364 QtxWorkstackChild* c = child( activeWidget() );
1368 new QtxWorkstackDrag( workstack(), c );
1371 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
1373 QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
1374 myStack->removeWidget( child );
1377 for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
1379 if ( it.data() == child )
1383 myChild.remove( wid );
1385 QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveWidget, wid ) );
1388 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
1390 setWidgetShown( c->widget(), true );
1393 void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
1395 setWidgetShown( c->widget(), false );
1398 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
1400 setWidgetActive( c->widget() );
1403 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
1405 updateTab( c->widget() );
1408 void QtxWorkstackArea::updateCurrent()
1410 QMap<QWidget*, bool> map;
1411 for ( QWidgetListIt it( myList ); it.current(); ++it )
1413 map.insert( it.current(), isBlocked( it.current() ) );
1414 setBlocked( it.current(), true );
1417 myStack->raiseWidget( myBar->currentTab() );
1419 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1420 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1423 void QtxWorkstackArea::updateTab( QWidget* wid )
1425 QTab* tab = myBar->tab( widgetId( wid ) );
1431 icoSet = QIconSet( *wid->icon() );
1433 tab->setIconSet( icoSet );
1434 tab->setText( wid->caption() );
1437 QWidget* QtxWorkstackArea::widget( const int id ) const
1440 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
1442 if ( it.data().id == id )
1448 int QtxWorkstackArea::widgetId( QWidget* wid ) const
1451 if ( myInfo.contains( wid ) )
1452 id = myInfo[wid].id;
1456 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
1459 if ( myInfo.contains( wid ) )
1460 res = myInfo[wid].vis;
1464 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
1466 int id = widgetId( wid );
1470 myBar->setCurrentTab( id );
1473 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
1475 if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
1478 myInfo[wid].vis = on;
1482 void QtxWorkstackArea::updateState()
1484 bool updBar = myBar->isUpdatesEnabled();
1485 bool updStk = myStack->isUpdatesEnabled();
1486 myBar->setUpdatesEnabled( false );
1487 myStack->setUpdatesEnabled( false );
1489 bool block = myBar->signalsBlocked();
1490 myBar->blockSignals( true );
1492 QWidget* prev = activeWidget();
1495 for ( QWidgetListIt it( myList ); it.current(); ++it )
1497 QWidget* wid = it.current();
1498 int id = widgetId( wid );
1503 bool vis = widgetVisibility( wid );
1505 if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
1506 myBar->removeTab( myBar->tab( id ) );
1508 if ( !myBar->tab( id ) && vis )
1510 QTab* tab = new QTab( wid->caption() );
1511 myBar->insertTab( tab, idx );
1512 tab->setIdentifier( id );
1517 bool block = isBlocked( wid );
1518 setBlocked( wid, true );
1520 QtxWorkstackChild* cont = child( wid );
1523 myStack->removeWidget( cont );
1524 else if ( !myStack->widget( id ) )
1525 myStack->addWidget( cont, id );
1530 setBlocked( wid, block );
1533 int curId = widgetId( prev );
1534 if ( !myBar->tab( curId ) )
1537 int pos = myList.find( prev );
1538 for ( int i = pos - 1; i >= 0 && !wid; i-- )
1540 if ( widgetVisibility( myList.at( i ) ) )
1541 wid = myList.at( i );
1544 for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
1546 if ( widgetVisibility( myList.at( j ) ) )
1547 wid = myList.at( j );
1551 curId = widgetId( wid );
1554 myBar->setCurrentTab( curId );
1556 myBar->blockSignals( block );
1560 myBar->setUpdatesEnabled( updBar );
1561 myStack->setUpdatesEnabled( updStk );
1567 QResizeEvent re( myBar->size(), myBar->size() );
1568 QApplication::sendEvent( myBar, &re );
1573 emit deactivated( this );
1578 if ( prev != activeWidget() )
1579 emit activated( activeWidget() );
1583 int QtxWorkstackArea::generateId() const
1587 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
1588 map.insert( it.data().id, 0 );
1591 while ( map.contains( id ) )
1597 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
1599 return myBlock.contains( wid );
1602 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
1605 myBlock.insert( wid, 0 );
1607 myBlock.remove( wid );
1610 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1612 QtxWorkstackChild* res = 0;
1613 if ( myChild.contains( wid ) )
1619 Class: QtxWorkstackChild [Internal]
1623 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
1627 myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
1628 myWidget->installEventFilter( this );
1630 connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1633 QtxWorkstackChild::~QtxWorkstackChild()
1635 qApp->removeEventFilter( this );
1640 widget()->removeEventFilter( this );
1641 widget()->reparent( 0, QPoint( 0, 0 ), false );
1642 disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1645 QWidget* QtxWorkstackChild::widget() const
1650 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1652 if ( o->isWidgetType() )
1654 if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
1655 emit captionChanged( this );
1657 if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
1660 if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
1663 if ( e->type() == QEvent::FocusIn )
1664 emit activated( this );
1666 return QHBox::eventFilter( o, e );
1669 void QtxWorkstackChild::onDestroyed( QObject* obj )
1671 if ( obj != widget() )
1678 void QtxWorkstackChild::childEvent( QChildEvent* e )
1680 if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
1685 QHBox::childEvent( e );
1689 Class: QtxWorkstackTabBar [Internal]
1693 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1694 : QTabBar( parent ),
1699 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1703 void QtxWorkstackTabBar::setActive( const bool on )
1705 QFont aFont = font();
1706 aFont.setUnderline( on );
1712 QRect QtxWorkstackTabBar::tabRect( const int idx ) const
1715 QTab* t = tabAt( idx );
1719 r.setLeft( QMAX( r.left(), 0 ) );
1721 int x1 = tabAt( 0 )->rect().left();
1722 int x2 = tabAt( count() - 1 )->rect().right();
1725 if ( QABS( x2 - x1 ) > width() )
1726 #if defined QT_VERSION && QT_VERSION >= 0x30300
1727 bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
1732 int limit = width() - bw;
1733 r.setRight( QMIN( r.right(), limit ) );
1735 r = QRect( mapToGlobal( r.topLeft() ), r.size() );
1740 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1742 if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
1745 emit dragActiveTab();
1748 QTabBar::mouseMoveEvent( e );
1751 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1753 QTabBar::mousePressEvent( e );
1755 if ( e->button() == LeftButton )
1756 myId = currentTab();
1759 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1761 QTabBar::mouseReleaseEvent( e );
1765 if ( e->button() == RightButton )
1766 emit contextMenuRequested( e->globalPos() );
1769 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1771 if ( e->reason() != QContextMenuEvent::Mouse )
1772 emit contextMenuRequested( e->globalPos() );
1775 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1777 if ( currentTab() != t->identifier() )
1779 QFont fnt = p->font();
1780 fnt.setUnderline( false );
1783 QTabBar::paintLabel( p, br, t, has_focus );
1787 Class: QtxWorkstackDrag [Internal]
1791 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
1799 qApp->installEventFilter( this );
1802 QtxWorkstackDrag::~QtxWorkstackDrag()
1804 qApp->removeEventFilter( this );
1809 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
1811 switch ( e->type() )
1813 case QEvent::MouseMove:
1814 updateTarget( ((QMouseEvent*)e)->globalPos() );
1816 case QEvent::MouseButtonRelease:
1828 void QtxWorkstackDrag::updateTarget( const QPoint& p )
1831 QtxWorkstackArea* area = detectTarget( p, tab );
1832 setTarget( area, tab );
1835 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
1840 QtxWorkstackArea* area = myWS->areaAt( p );
1842 tab = area->tabAt( p );
1846 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
1848 if ( !area || ( myArea == area && tab == myTab ) )
1863 void QtxWorkstackDrag::dropWidget()
1866 myArea->insertWidget( myChild->widget(), myTab );
1869 void QtxWorkstackDrag::drawRect()
1871 if ( !myPainter || !myArea )
1874 QRect r = myArea->floatRect();
1875 int m = myPainter->pen().width();
1877 r.setTop( r.top() + m + 2 );
1878 r.setLeft( r.left() + m + 2 );
1879 r.setRight( r.right() - m - 2 );
1880 r.setBottom( r.bottom() - m - 2 );
1882 myPainter->drawRect( r );
1884 QRect tr = myArea->floatTab( myTab );
1885 tr.setTop( tr.top() + m );
1886 tr.setLeft( tr.left() + m );
1887 tr.setRight( tr.right() - m );
1888 tr.setBottom( tr.bottom() - m );
1890 myPainter->drawRect( tr );
1893 void QtxWorkstackDrag::endDrawRect()
1899 void QtxWorkstackDrag::startDrawRect()
1904 int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
1905 QWidget* paint_on = QApplication::desktop()->screen( scr );
1907 myPainter = new QPainter( paint_on, true );
1908 myPainter->setPen( QPen( gray, 3 ) );
1909 myPainter->setRasterOp( XorROP );