1 // Copyright (C) 2005 OPEN CASCADE, CEA/DEN, EDF R&D, PRINCIPIA R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
8 // This library is distributed in the hope that it will be useful
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/
19 // File: QtxWorkstack.cxx
20 // Author: Sergey TELKOV
22 #include "QtxWorkstack.h"
31 #include <qsplitter.h>
32 #include <qpopupmenu.h>
33 #include <qobjectlist.h>
34 #include <qpushbutton.h>
35 #include <qwidgetstack.h>
36 #include <qapplication.h>
37 #include <qinputdialog.h>
41 #define DARK_COLOR_LIGHT 250
44 Class: QtxWorkstack [Public]
48 QtxWorkstack::QtxWorkstack( QWidget* parent )
55 myActionsMap.insert( SplitVertical, new QAction( tr( "Split vertically" ), 0, this ) );
56 myActionsMap.insert( SplitHorizontal, new QAction( tr( "Split horizontally" ), 0, this ) );
57 myActionsMap.insert( Close, new QAction( tr( "Close" ), 0, this ) );
58 myActionsMap.insert( Rename, new QAction( tr( "Rename" ), 0, this ) );
60 connect( myActionsMap[SplitVertical], SIGNAL( activated() ), this, SLOT( splitVertical() ) );
61 connect( myActionsMap[SplitHorizontal], SIGNAL( activated() ), this, SLOT( splitHorizontal() ) );
62 connect( myActionsMap[Close], SIGNAL( activated() ), this, SLOT( onCloseWindow() ) );
63 connect( myActionsMap[Rename], SIGNAL( activated() ), this, SLOT( onRename() ) );
65 QVBoxLayout* base = new QVBoxLayout( this );
66 mySplit = new QSplitter( this );
67 mySplit->setChildrenCollapsible( false );
68 base->addWidget( mySplit );
71 QtxWorkstack::~QtxWorkstack()
75 QWidgetList QtxWorkstack::windowList() const
77 QPtrList<QtxWorkstackArea> lst;
78 areas( mySplit, lst, true );
81 for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current(); ++it )
83 QWidgetList wids = it.current()->widgetList();
84 for ( QWidgetListIt itr( wids ); itr.current(); ++itr )
85 widList.append( itr.current() );
91 QWidgetList QtxWorkstack::splitWindowList() const
93 return myArea ? myArea->widgetList() : QWidgetList();
96 QWidget* QtxWorkstack::activeWindow() const
101 void QtxWorkstack::split( const int o )
103 QtxWorkstackArea* area = myWorkArea;
109 if ( area->widgetList().count() < 2 )
112 QWidget* curWid = area->activeWidget();
116 QSplitter* s = splitter( area );
117 QPtrList<QtxWorkstackArea> areaList;
118 areas( s, areaList );
120 QPtrList<QSplitter> splitList;
121 splitters( s, splitList );
124 if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
128 trg = wrapSplitter( area );
133 trg->setOrientation( (Orientation)o );
135 QtxWorkstackArea* newArea = createArea( 0 );
136 insertWidget( newArea, trg, area );
138 area->removeWidget( curWid );
139 newArea->insertWidget( curWid );
141 distributeSpace( trg );
148 * \brief Split workarea of the given widget on two parts.
149 * \param wid - widget, belonging to this workstack
150 * \param o - orientation of splitting (Qt::Horizontal or Qt::Vertical)
151 * \param type - type of splitting, see <VAR>SplitType</VAR> enumeration
153 void QtxWorkstack::Split (QWidget* wid, const Qt::Orientation o, const SplitType type)
157 // find area of the given widget
158 QtxWorkstackArea* area = NULL;
159 QPtrList<QtxWorkstackArea> allAreas;
160 areas(mySplit, allAreas, true);
162 QPtrListIterator<QtxWorkstackArea> it (allAreas);
163 for (; it.current() && !area; ++it) {
164 if (it.current()->contains(wid))
169 QWidgetList wids = area->widgetList();
170 if (wids.count() < 2)
173 QSplitter* s = splitter(area);
174 QPtrList<QtxWorkstackArea> areaList;
177 QPtrList<QSplitter> splitList;
178 splitters(s, splitList);
181 if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
184 if (!trg) trg = wrapSplitter(area);
187 trg->setOrientation(o);
189 QtxWorkstackArea* newArea = createArea(0);
190 insertWidget(newArea, trg, area);
195 QWidgetListIt itr (wids);
196 for (; itr.current(); ++itr)
198 QWidget* wid_i = itr.current();
200 area->removeWidget(wid_i);
201 newArea->insertWidget(wid_i);
208 QWidgetListIt itr (wids);
209 for (; itr.current() && itr.current() != wid; ++itr) {
211 for (; itr.current(); ++itr) {
212 area->removeWidget(itr.current());
213 newArea->insertWidget(itr.current());
218 area->removeWidget(wid);
219 newArea->insertWidget(wid);
223 distributeSpace(trg);
227 * \brief Put given widget on top of its workarea
228 * \param wid - widget, belonging to this workstack
231 void QtxWorkstack::OnTop (QWidget* wid)
236 // find area of the given widget
237 QtxWorkstackArea* area = 0;
238 QPtrList<QtxWorkstackArea> allAreas;
239 areas( mySplit, allAreas, true );
240 for ( QPtrListIterator<QtxWorkstackArea> it( allAreas ); it.current() && !area; ++it )
242 if ( it.current()->contains( wid ) )
247 area->setActiveWidget( wid );
252 * \brief Move widget(s) from source workarea into target workarea
253 * or just reorder widgets inside one workarea.
254 * \param wid1 - widget from target workarea
255 * \param wid2 - widget from source workarea
256 * \param all - if this parameter is TRUE, all widgets from source workarea will
257 * be moved into the target one, else only the \a wid2 will be moved
259 * Move \a wid2 in target workarea. Put it right after \a wid1.
260 * If value of boolean argument is TRUE, all widgets from source workarea
261 * will be moved together with \a wid2, source workarea will be deleted.
262 * If \a wid1 and \a wid2 belongs to one workarea, simple reordering will take place.
264 void QtxWorkstack::Attract ( QWidget* wid1, QWidget* wid2, const bool all )
266 if ( !wid1 || !wid2 )
269 // find area of the widgets
270 QtxWorkstackArea *area1 = NULL, *area2 = NULL;
271 QPtrList<QtxWorkstackArea> allAreas;
272 areas(mySplit, allAreas, true);
273 QPtrListIterator<QtxWorkstackArea> it (allAreas);
274 for (; it.current() && !(area1 && area2); ++it) {
275 if (it.current()->contains(wid1))
276 area1 = it.current();
277 if (it.current()->contains(wid2))
278 area2 = it.current();
280 if (!area1 || !area2) return;
282 QWidget* curWid = area1->activeWidget();
285 if (area1 == area2) {
287 // Set wid1 at first position, wid2 at second
288 area1->insertWidget(wid1);
289 area1->insertWidget(wid2, 1);
291 // Set wid2 right after wid1
292 area1->removeWidget(wid2);
294 QWidgetList wids1 = area1->widgetList();
295 QWidgetListIt itr1 (wids1);
296 for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
297 area1->insertWidget(wid2, wid1_ind + 1);
301 QWidgetList wids1 = area1->widgetList();
302 QWidgetListIt itr1 (wids1);
303 for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
306 // Set wid2 right after wid1, other widgets from area2 right after wid2
307 QWidgetList wids2 = area2->widgetList();
308 QWidgetListIt itr2 (wids2);
309 for (int ind = wid1_ind + 1; itr2.current(); ++itr2, ++ind)
311 area2->removeWidget(itr2.current());
312 if (itr2.current() == wid2) {
313 area1->insertWidget(itr2.current(), wid1_ind + 1);
315 area1->insertWidget(itr2.current(), ind);
319 // Set wid2 right after wid1
320 area2->removeWidget(wid2);
321 area1->insertWidget(wid2, wid1_ind + 1);
325 area1->setActiveWidget( curWid );
328 static void setSizes (QIntList& szList, const int item_ind,
329 const int new_near, const int new_this, const int new_farr)
331 // set size to all items before an item # <item_ind>
333 QIntList::iterator its = szList.begin();
334 for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
337 if (its == szList.end()) return;
338 // set size to item # <item_ind>
341 // set size to all items after an item # <item_ind>
342 for (; its != szList.end(); ++its) {
348 * \brief Set position of the widget relatively its splitter.
349 * \param wid - widget to set position of
350 * \param pos - position relatively splitter. Value in range [0..1].
352 * Orientation of positioning will correspond to the splitter orientation.
354 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
356 if ( position < 0.0 || 1.0 < position)
362 // find area of the given widget
363 QtxWorkstackArea* area = NULL;
364 QPtrList<QtxWorkstackArea> allAreas;
365 areas(mySplit, allAreas, true);
366 for ( QPtrListIterator<QtxWorkstackArea> it( allAreas );
367 it.current() && !area;
370 if (it.current()->contains(wid))
377 QSplitter* split = splitter( area );
381 // find index of the area in its splitter
383 bool isFound = false;
384 const QObjectList* was = split->children();
385 for (QObjectListIt ito (*was); ito.current() && !isFound; ++ito, ++item_ind)
387 if (ito.current() == area)
390 if (!isFound || item_ind == 0)
393 QIntList szList = split->sizes();
394 int splitter_size = (split->orientation() == Horizontal ?
395 split->width() : split->height());
396 int nb = szList.count();
398 int new_prev = int(splitter_size * position / item_ind);
399 int new_next = int(splitter_size * (1.0 - position) / (nb - item_ind));
400 setSizes (szList, item_ind, new_prev, new_next, new_next);
401 split->setSizes(szList);
405 * \brief Set position of the widget relatively the entire workstack.
406 * \param wid - widget to set position of
407 * \param o - orientation of positioning (Qt::Horizontal or Qt::Vertical).
408 * If o = Qt::Horizontal, horizontal position of \a wid will be changed.
409 * If o = Qt::Vertical, vertical position of \a wid will be changed.
410 * \param pos - position relatively workstack. Value in range [0..1].
412 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
413 const double position )
415 if ( position < 0.0 || 1.0 < position )
421 int splitter_size = o == Horizontal ? mySplit->width() : mySplit->height();
422 int need_pos = int( position * splitter_size );
423 int splitter_pos = 0;
425 if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
427 // impossible to set required position
432 * \brief Sets the action's accelerator key to accel.
433 * \param id - the key of the action in the actions map.
434 * \param accel - action's accelerator key.
436 void QtxWorkstack::setAccel( const int id, const int accel )
438 if ( !myActionsMap.contains( id ) )
441 myActionsMap[id]->setAccel( accel );
445 * \brief Returns the action's accelerator key.
446 * \param id - the key of the action in the actions map.
447 * \retval int - action's accelerator key.
449 int QtxWorkstack::accel( const int id ) const
452 if ( myActionsMap.contains( id ) )
453 res = myActionsMap[id]->accel();
457 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
458 const int item_ind, const int item_rel_pos,
459 const int need_pos, const int splitter_pos)
461 if (item_ind == 0) { // cannot move in this splitter
462 return (need_pos - splitter_pos);
467 int new_this = szList[item_ind];
470 bool isToCheck = false;
472 if (need_pos < splitter_pos) {
473 // Set size of all previous workareas to zero <--
474 if (item_ind == nb - 1) {
475 // item iz last in the splitter, it will occupy all the splitter
476 new_this = splitter_size;
478 // recompute size of next items in splitter
479 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
481 delta = need_pos - splitter_pos;
483 } else if (need_pos > (splitter_pos + splitter_size)) {
484 // Set size of all next workareas to zero -->
485 // recompute size of previous items in splitter
487 new_prev = (splitter_size - new_this) / item_ind;
488 delta = need_pos - (splitter_pos + splitter_size - new_this);
490 } else { // required position lays inside this splitter
491 // Move workarea inside splitter into required position <->
492 int new_item_rel_pos = need_pos - splitter_pos;
493 new_prev = new_item_rel_pos / item_ind;
494 if (need_pos < (splitter_pos + item_rel_pos)) {
495 // Make previous workareas smaller, next - bigger
496 // No problem to keep old size of the widget
498 // Make previous workareas bigger, next - smaller
499 if (new_this > splitter_size - new_item_rel_pos) {
500 new_this = splitter_size - new_item_rel_pos;
502 // jfa to do: in this case fixed size of next widgets could prevent right resizing
505 if (item_ind == nb - 1) {
506 new_this = splitter_size - new_item_rel_pos;
508 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
513 setSizes (szList, item_ind, new_prev, new_this, new_next);
518 * \brief Set position of given widget.
519 * \param wid - widget to be moved
520 * \param split - currently processed splitter (goes from more common
521 * to more particular splitter in recursion calls)
522 * \param o - orientation of positioning
523 * \param need_pos - required position of the given widget in pixels
524 * (from top/left side of workstack area)
525 * \param splitter_pos - position of the splitter \a split
526 * (from top/left side of workstack area)
527 * \retval int - returns difference between a required and a distinguished position.
529 * Internal method. Recursively calls itself.
530 * Is called from <VAR>SetRelativePosition</VAR> public method.
532 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
533 const int need_pos, const int splitter_pos )
535 if ( !wid || !split )
536 return need_pos - splitter_pos;
538 // Find corresponding sub-splitter.
539 // Find also index of appropriate item in current splitter.
540 int cur_ind = 0, item_ind = 0;
541 bool isBottom = false, isFound = false;
542 QSplitter* sub_split = NULL;
543 const QObjectList* objs = split->children();
546 for (QObjectListIt it (*objs); it.current() && !isFound; ++it)
548 if (it.current()->inherits( "QtxWorkstackArea")) {
549 if (((QtxWorkstackArea*)it.current())->contains(wid)) {
555 } else if (it.current()->inherits("QSplitter")) {
556 QPtrList<QtxWorkstackArea> areaList;
557 areas((QSplitter*)it.current(), areaList, true);
558 for (QPtrListIterator<QtxWorkstackArea> ita (areaList);
559 ita.current() && !isFound;
562 if (ita.current()->contains(wid)) {
565 sub_split = (QSplitter*)it.current();
573 return (need_pos - splitter_pos);
575 if (split->orientation() == o) {
576 // Find coordinates of near and far sides of the appropriate item relatively current splitter
577 int splitter_size = (o == Horizontal ? split->width() : split->height());
578 QIntList szList = split->sizes();
579 int nb = szList.count();
580 int item_rel_pos = 0; // position of near side of item relatively this splitter
581 for (int i = 0; i < item_ind; i++) {
582 item_rel_pos += szList[i];
584 int item_size = szList[item_ind]; // size of item
585 int item_pos = splitter_pos + item_rel_pos;
587 // Resize splitter items to complete the conditions
589 // I. Bottom of splitters stack reached
591 int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
592 split->setSizes(szList);
593 // Recompute delta, 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 delta = need_pos - (splitter_pos + new_item_rel_pos);
603 // II. Bottom of splitters stack is not yet reached
605 if (item_ind == 0) { // cannot move in this splitter
606 // Process in sub-splitter
607 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
611 int new_this = szList[item_ind];
614 if (need_pos < splitter_pos) {
615 // Set size of all previous workareas to zero <--
616 if (item_ind == nb - 1) {
617 new_this = splitter_size;
619 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
621 setSizes (szList, item_ind, new_prev, new_this, new_next);
622 split->setSizes(szList);
623 // Recompute splitter_pos, as some windows can reject given size
624 int new_item_rel_pos = 0;
625 QIntList szList1 = split->sizes();
626 for (int i = 0; i < item_ind; i++) {
627 new_item_rel_pos += szList1[i];
629 // Process in sub-splitter
630 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
631 } else if (need_pos > (splitter_pos + splitter_size)) {
632 // Set size of all next workareas to zero -->
633 new_prev = (splitter_size - new_this) / item_ind;
634 setSizes (szList, item_ind, new_prev, new_this, new_next);
635 split->setSizes(szList);
636 // Recompute splitter_pos, as some windows can reject given size
637 int new_item_rel_pos = 0;
638 QIntList szList1 = split->sizes();
639 for (int i = 0; i < item_ind; i++) {
640 new_item_rel_pos += szList1[i];
642 // Process in sub-splitter
643 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
645 // Set appropriate size of all previous/next items <->
646 int new_item_rel_pos = item_rel_pos;
647 if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
648 // Move item inside splitter into required position <->
649 int new_this = szList[item_ind];
651 new_item_rel_pos = need_pos - splitter_pos;
652 if ((item_pos + item_size) < need_pos) {
653 //new_item_rel_pos = need_pos - (item_pos + item_size);
654 new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
656 int new_prev = new_item_rel_pos / item_ind;
657 if (need_pos < (splitter_pos + item_rel_pos)) {
658 // Make previous workareas smaller, next - bigger
659 // No problem to keep old size of the widget
661 // Make previous workareas bigger, next - smaller
662 if (new_this > splitter_size - new_item_rel_pos) {
663 new_this = splitter_size - new_item_rel_pos;
666 if (item_ind == nb - 1) {
667 new_this = splitter_size - new_item_rel_pos;
669 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
671 setSizes (szList, item_ind, new_prev, new_this, new_next);
672 split->setSizes(szList);
673 // Recompute new_item_rel_pos, as some windows can reject given size
674 new_item_rel_pos = 0;
675 QIntList szList1 = split->sizes();
676 for (int i = 0; i < item_ind; i++) {
677 new_item_rel_pos += szList1[i];
682 // Process in sub-splitter
683 int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
687 // this can be if corresponding workarea is first in sub-splitter
688 // or sub-splitter has another orientation
690 // Resize ones again to reach precize position <->
691 int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
693 // Move workarea inside splitter into required position <->
694 int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
695 new_item_rel_pos, need_pos_1, splitter_pos);
696 split->setSizes(szList);
697 // Recompute new_item_rel_pos, as some windows can reject given size
698 new_item_rel_pos = 0;
699 QIntList szList1 = split->sizes();
700 for (int i = 0; i < item_ind; i++) {
701 new_item_rel_pos += szList1[i];
703 delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
708 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
714 void QtxWorkstack::distributeSpace( QSplitter* split ) const
719 QIntList szList = split->sizes();
720 int size = ( split->orientation() == Horizontal ?
721 split->width() : split->height() ) / szList.count();
722 for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
724 split->setSizes( szList );
727 void QtxWorkstack::splitVertical()
729 split( Qt::Horizontal );
732 void QtxWorkstack::splitHorizontal()
734 split( Qt::Vertical );
737 void QtxWorkstack::onRename()
743 QString newName = QInputDialog::getText( tr( "Rename" ), tr( "Enter new name:" ), QLineEdit::Normal,
744 myWorkWin->caption(), &ok, topLevelWidget() );
745 if ( ok && !newName.isEmpty() )
746 myWorkWin->setCaption( newName );
749 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
754 QSplitter* pSplit = splitter( area );
758 bool upd = pSplit->isUpdatesEnabled();
759 pSplit->setUpdatesEnabled( false );
761 QIntList szList = pSplit->sizes();
763 QSplitter* wrap = new QSplitter( 0 );
764 #if defined QT_VERSION && QT_VERSION >= 0x30200
765 wrap->setChildrenCollapsible( false );
767 insertWidget( wrap, pSplit, area );
768 area->reparent( wrap, QPoint( 0, 0 ), true );
770 pSplit->setSizes( szList );
772 pSplit->setUpdatesEnabled( upd );
777 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
782 QWidgetList moveList;
783 const QObjectList* lst = pWid->children();
787 for ( QObjectListIt it( *lst ); it.current(); ++it )
789 if ( found && ( it.current()->inherits( "QSplitter" ) ||
790 it.current()->inherits( "QtxWorkstackArea" ) ) )
791 moveList.append( (QWidget*)it.current() );
792 if ( it.current() == after )
797 QMap<QWidget*, bool> map;
798 for ( QWidgetListIt it( moveList ); it.current(); ++it )
800 map.insert( it.current(), it.current()->isVisibleTo( it.current()->parentWidget() ) );
801 it.current()->reparent( 0, QPoint( 0, 0 ), false );
804 wid->reparent( pWid, QPoint( 0, 0 ), true );
806 for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
807 itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
811 * \brief Closes the active window.
813 void QtxWorkstack::onCloseWindow()
817 else if( activeWindow() )
818 activeWindow()->close();
821 void QtxWorkstack::onDestroyed( QObject* obj )
823 QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
825 if ( area == myArea )
830 QtxWorkstackArea* cur = neighbourArea( area );
835 QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
838 void QtxWorkstack::onWindowActivated( QWidget* wid )
840 const QObject* obj = sender();
841 if ( !obj->inherits( "QtxWorkstackArea" ) )
844 setActiveArea( (QtxWorkstackArea*)obj );
847 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
849 if ( myArea != area )
852 QPtrList<QtxWorkstackArea> lst;
853 areas( mySplit, lst, true );
855 int idx = lst.find( area );
862 QtxWorkstackArea* newArea = neighbourArea( area );
863 if ( newArea && newArea->activeWidget() )
864 newArea->activeWidget()->setFocus();
866 QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
869 void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
871 QtxWorkstackArea* anArea = dynamic_cast<QtxWorkstackArea*>( (QObject*)sender() );
873 anArea = activeArea();
878 QWidgetList lst = anArea->widgetList();
885 QPopupMenu* pm = new QPopupMenu();
887 if ( lst.count() > 1 )
889 myActionsMap[SplitVertical]->addTo( pm );
890 myActionsMap[SplitHorizontal]->addTo( pm );
891 pm->insertSeparator();
896 myActionsMap[Close]->addTo( pm );
897 myActionsMap[Rename]->addTo( pm );
909 void QtxWorkstack::childEvent( QChildEvent* e )
911 if ( e->inserted() && e->child()->isWidgetType() )
913 QWidget* w = (QWidget*)e->child();
914 if ( w && w != mySplit )
916 targetArea()->insertWidget( w );
920 QWidget::childEvent( e );
923 void QtxWorkstack::customEvent( QCustomEvent* e )
928 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
933 QSplitter* split = 0;
935 QWidget* wid = area->parentWidget();
936 if ( wid && wid->inherits( "QSplitter" ) )
937 split = (QSplitter*)wid;
942 void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
947 const QObjectList* objs = split->children();
950 for ( QObjectListIt it( *objs ); it.current(); ++it )
953 splitters( (QSplitter*)it.current(), splitList, rec );
954 if ( it.current()->inherits( "QSplitter" ) )
955 splitList.append( (QSplitter*)it.current() );
960 void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
965 const QObjectList* objs = split->children();
968 for ( QObjectListIt it( *objs ); it.current(); ++it )
970 if ( it.current()->inherits( "QtxWorkstackArea" ) )
971 areaList.append( (QtxWorkstackArea*)it.current() );
972 else if ( rec && it.current()->inherits( "QSplitter" ) )
973 areas( (QSplitter*)it.current(), areaList, rec );
978 QtxWorkstackArea* QtxWorkstack::activeArea() const
983 QtxWorkstackArea* QtxWorkstack::targetArea()
985 QtxWorkstackArea* area = activeArea();
987 area = currentArea();
990 QPtrList<QtxWorkstackArea> lst;
991 areas( mySplit, lst );
992 if ( !lst.isEmpty() )
997 area = createArea( mySplit );
1002 QtxWorkstackArea* QtxWorkstack::currentArea() const
1004 QtxWorkstackArea* area = 0;
1005 QWidget* wid = focusWidget();
1006 while ( wid && !area )
1008 if ( wid->inherits( "QtxWorkstackArea" ) )
1009 area = (QtxWorkstackArea*)wid;
1010 wid = wid->parentWidget();
1016 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
1018 QtxWorkstackArea* area = new QtxWorkstackArea( parent );
1020 connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1021 connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
1022 connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
1023 this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
1024 connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
1029 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
1031 QWidget* oldCur = myWin;
1033 QtxWorkstackArea* oldArea = myArea;
1037 if ( myArea != oldArea )
1040 oldArea->updateActiveState();
1042 myArea->updateActiveState();
1046 myWin = myArea->activeWidget();
1048 if ( myWin && oldCur != myWin )
1049 emit windowActivated( myWin );
1052 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
1054 QPtrList<QtxWorkstackArea> lst;
1055 areas( mySplit, lst, true );
1056 int pos = lst.find( area );
1060 QtxWorkstackArea* na = 0;
1061 for ( int i = pos - 1; i >= 0 && !na; i-- )
1063 if ( !lst.at( i )->isEmpty() )
1067 for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
1069 if ( !lst.at( j )->isEmpty() )
1075 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
1077 QtxWorkstackArea* area = 0;
1078 QPtrList<QtxWorkstackArea> lst;
1079 areas( mySplit, lst, true );
1080 for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current() && !area; ++it )
1082 QtxWorkstackArea* cur = it.current();
1083 QRect r = cur->geometry();
1084 if ( cur->parentWidget() )
1085 r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
1086 if ( r.contains( p ) )
1092 void QtxWorkstack::updateState()
1094 updateState( mySplit );
1097 void QtxWorkstack::updateState( QSplitter* split )
1099 QPtrList<QSplitter> recList;
1100 splitters( split, recList, false );
1101 for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
1102 updateState( itr.current() );
1104 QPtrList<QSplitter> splitList;
1105 splitters( split, splitList, false );
1107 QPtrList<QtxWorkstackArea> areaList;
1108 areas( split, areaList, false );
1111 for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
1113 if ( it.current()->isEmpty() )
1114 it.current()->hide();
1117 it.current()->show();
1122 if ( split == mySplit )
1125 for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current() && !vis; ++iter )
1126 vis = iter.current()->isVisibleTo( iter.current()->parentWidget() );
1128 if ( areaList.isEmpty() && splitList.isEmpty() )
1136 void QtxWorkstack::splitterInfo( QSplitter* split, QString& info ) const
1141 const QObjectList* objs = split->children();
1144 // make up a sizes string: integer values are separated by ':' char
1145 QValueList<int> sizes = split->sizes();
1147 for ( QValueList<int>::Iterator sIt = sizes.begin(); sIt != sizes.end(); ++sIt ) {
1148 if ( *sIt > 1 ) // size 1 pixel usually means empty Workstack area, which will NOT be re-created,
1149 sizesStr += QString( ":%1" ).arg( *sIt ); // so we don't need to store its size
1151 if ( !sizesStr.isEmpty() ) // cut the first ':'
1152 sizesStr = sizesStr.right( sizesStr.length()-1 );
1154 // count all QSplitter-s and QtxWorkstackArea-s
1155 // int nChilds( 0 );
1156 // QObjectListIt it( *objs );
1157 // for ( ; it.current(); ++it )
1159 // if ( it.current()->inherits( "QSplitter" ) ||
1160 // it.current()->inherits( "QtxWorkstackArea" ) )
1164 info += QString( "(splitter orientation=%1 sizes=%3 " ).arg( split->orientation() ).arg( sizesStr );
1166 for ( QObjectListIt it( *objs ); it.current(); ++it )
1168 if ( it.current()->inherits( "QSplitter" ) )
1169 splitterInfo( (QSplitter*)it.current(), info );
1170 else if ( it.current()->inherits( "QtxWorkstackArea" ) ) {
1171 QtxWorkstackArea* area = (QtxWorkstackArea*)it.current();
1172 if ( area->isEmpty() )
1174 info += QString( "(views active='%1'" ).arg( area->activeWidget()->name() );
1175 QWidgetList views = area->widgetList();
1176 for ( QWidgetListIt wIt( views ); wIt.current(); ++wIt )
1177 info += QString( " '%1'" ).arg( wIt.current()->name() );
1186 // cuts starting '(' symbol and ending '(' symbol
1187 void cutBrackets( QString& parameters )
1189 if ( !parameters.isEmpty() && parameters[0] == '(' && parameters[parameters.length()-1] == ')' )
1190 parameters = parameters.mid( 1, parameters.length()-2 );
1193 // for strings like "(splitter orientation=0 children=2 sizes=332:478" returns values of
1194 // parameters. For example, getValue( example, "children" ) returns "2"
1195 // getValue( example, "sizes" ) returns "332:478"
1196 QString getValue( const QString& str, const QString& valName )
1198 int i = str.find( valName );
1200 int equal_i = str.find( '=', i );
1201 if ( equal_i != -1 ) {
1202 int space_i = str.find( ' ', ++equal_i );
1203 if ( space_i != -1 )
1204 return str.mid( equal_i, space_i-equal_i );
1207 return QString( "" );
1210 // checks format of splitter parameters string
1211 bool checkFormat( const QString& parameters )
1213 QString params( parameters );
1214 // 1. begins and ends with brackets
1215 bool ok = ( params[0] == '(' && params[params.length()-1] == ')' );
1216 if ( !ok ) return ok;
1217 ::cutBrackets( params );
1218 // 2. has splitter word
1219 ok = ( params.left( 8 ) == "splitter" );
1220 if ( !ok ) return ok;
1221 // 3. has children? = '(' is found
1222 int i = params.find( '(' );
1224 if ( !ok ) return ok;
1225 params = params.left( i ); // cut all children, they will be checked later
1226 // 4. has orientation word and correct value
1227 ::getValue( params, "orientation" ).toInt( &ok );
1228 if ( !ok ) return ok;
1229 // 5. has sizes word and values
1230 ok = ! ::getValue( params, "sizes" ).isEmpty();
1231 if ( !ok ) return ok;
1232 // 6. check children -> number of '(' == number of ')' in original string
1233 ok = ( parameters.contains( '(' ) == parameters.contains( ')' ) );
1237 // returns children of splitter in a list. Children are separated by '(' and ')' symbols
1238 QStringList getChildren( const QString& str )
1241 if ( !str.startsWith( "(" ) )
1245 nOpen = 1, // count brackets: '(' increments nOpen, ')' decrements
1247 while ( i < str.length() ) {
1248 if ( str[i] == '(' ) {
1253 else if ( str[i] == ')' ) {
1256 lst.append( str.mid( start, i-start+1 ) );
1264 // for a string like "views active='AnotherView' 'GLView' 'AnotherView'"
1265 // getViewName( example, 0 ) returns "GLView",
1266 // getViewName( example, 1 ) -> "AnotherView", etc.
1267 QString getViewName( const QString& str, int i )
1269 QRegExp exp( "\\s'\\w+'" );
1270 int start = 0; // start index of view name in the string
1271 int num = 0 ; // index of found match
1272 while ( ( start = exp.search( str, start ) ) != -1 && num < i ) {
1273 start += exp.matchedLength();
1276 if ( start != -1 ) // +2 and -3 avoid starting space and starting and ending ' symbols
1277 return str.mid( start+2, exp.matchedLength()-3 );
1279 return QString( "" );
1282 // returns widget with given name
1283 QWidget* getView( const QWidget* parent, const QString& aName )
1286 QObjectList *l = parent->topLevelWidget()->queryList( "QWidget", aName, false, true );
1287 if ( !l->isEmpty() )
1288 view = dynamic_cast<QWidget*>( l->first() );
1293 // installs a splitter described by given parameters string
1294 void QtxWorkstack::setSplitter( QSplitter* splitter, const QString& parameters, QMap< QSplitter*, QValueList<int> >& sMap )
1296 if ( !::checkFormat( parameters ) ) {
1297 printf( "\nInvalid format of workstack parameters. Positions of viewers can not be restored.\n" );
1301 QString params( parameters );
1302 ::cutBrackets( params );
1304 // get splitter sizes and store it in the map for future setting
1305 QValueList<int> sizes;
1306 QStringList sizesLst = QStringList::split( ':', ::getValue( params, "sizes" ) );
1307 for ( QStringList::Iterator it = sizesLst.begin(); it != sizesLst.end(); ++it )
1308 sizes.append( (*it).toInt() );
1309 sMap[ splitter ] = sizes;
1311 // set orientation of splitter
1312 int orient = ::getValue( params, "orientation" ).toInt();
1313 splitter->setOrientation( (Qt::Orientation)orient );
1316 QString options = params.left( params.find( '(' ) );
1317 QString childrenStr = params.right( params.length()-options.length() );
1318 QStringList children = ::getChildren( childrenStr );
1321 // printf (" splitter orient=%d, sizes_count=%d, children=%d\n", orient, sizes.count(), children.count() );
1322 // for ( QStringList::Iterator tit = children.begin(); tit != children.end(); ++tit )
1323 // printf (" |-> child = [%s]\n", (*tit).latin1() );
1325 for ( QStringList::Iterator it = children.begin(); it != children.end(); ++it ) {
1326 if ( (*it).startsWith( "(splitter" ) ) {
1327 QSplitter* newSplitter = new QSplitter( splitter );
1328 setSplitter( newSplitter, *it, sMap );
1330 else if ( (*it).startsWith( "(views" ) ) {
1331 QtxWorkstackArea* newArea = createArea( splitter );
1332 QString activeViewName = ::getValue( *it, "active" );
1333 QWidget* activeView( 0 );
1334 activeViewName = activeViewName.mid( 1, activeViewName.length()-2 ); // chop off ' symbols
1336 QString viewName = ::getViewName( *it, i );
1337 while ( !viewName.isEmpty() ) {
1338 if ( QWidget* view = ::getView( splitter, viewName ) ) {
1339 newArea->insertWidget( view );
1340 if ( activeViewName == view->name() )
1343 viewName = ::getViewName( *it, ++i );
1346 newArea->setActiveWidget( activeView );
1351 // Restore workstack's configuration stored in 'parameters' string
1352 QtxWorkstack& QtxWorkstack::operator<<( const QString& parameters )
1354 // clear the main splitter - remove all child splitters and empty areas from it
1355 QPtrList<QSplitter> splitList;
1356 QPtrList<QtxWorkstackArea> areaList;
1357 splitters( mySplit, splitList, false );
1358 areas( mySplit, areaList, false );
1359 for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current(); ++iter )
1360 delete iter.current();
1361 for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
1362 if ( it.current()->isEmpty() )
1363 delete it.current();
1365 // restore splitter recursively
1366 QMap< QSplitter*, QValueList<int> > sMap;
1367 setSplitter( mySplit, parameters, sMap );
1369 // now mySplit may contains empty area (where all views were located before restoring)
1370 // in order setSize to work correctly we have to exclude this area
1372 areas( mySplit, areaList, false );
1373 for ( QPtrListIterator<QtxWorkstackArea> delIt( areaList ); delIt.current(); ++delIt )
1374 if ( delIt.current()->isEmpty() )
1375 delete delIt.current();
1377 qApp->processEvents();
1379 // restore splitters' sizes (map of sizes is filled in setSplitters)
1380 for ( QMap< QSplitter*, QValueList<int> >::Iterator it = sMap.begin(); it != sMap.end(); ++it )
1381 it.key()->setSizes( it.data() );
1386 // Example of string produced by operator>> :
1387 // "(splitter orientation=0 sizes=186:624 (views active='OCCViewer_0_0' 'OCCViewer_0_0')
1388 // (views active='VTKViewer_0_0' 'VTKViewer_0_0'))"
1389 QtxWorkstack& QtxWorkstack::operator>>( QString& outParameters )
1391 splitterInfo( mySplit, outParameters );
1397 Class: QtxWorkstackArea [Internal]
1401 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
1404 QVBoxLayout* base = new QVBoxLayout( this );
1406 QHBox* top = new QHBox( this );
1407 base->addWidget( top );
1409 myBar = new QtxWorkstackTabBar( top );
1411 QPushButton* close = new QPushButton( top );
1412 close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
1413 close->setAutoDefault( true );
1414 close->setFlat( true );
1417 top->setStretchFactor( myBar, 1 );
1419 myStack = new QWidgetStack( this );
1421 base->addWidget( myStack, 1 );
1423 connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
1424 connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
1425 connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
1426 connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
1430 updateActiveState();
1432 qApp->installEventFilter( this );
1435 QtxWorkstackArea::~QtxWorkstackArea()
1437 qApp->removeEventFilter( this );
1440 bool QtxWorkstackArea::isEmpty() const
1443 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
1444 res = it.data().vis;
1448 void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
1453 int pos = myList.find( wid );
1454 if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
1457 myList.removeRef( wid );
1458 pos = idx < 0 ? myList.count() : idx;
1459 myList.insert( QMIN( pos, (int)myList.count() ), wid );
1460 if ( !myInfo.contains( wid ) )
1462 QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
1463 myChild.insert( wid, child );
1464 myInfo.insert( wid, WidgetInfo() );
1465 myInfo[wid].id = generateId();
1466 myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
1468 connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
1469 connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
1470 connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
1471 connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
1472 connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
1473 connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
1478 setWidgetActive( wid );
1482 void QtxWorkstackArea::onContextMenuRequested( QPoint p )
1484 const QtxWorkstackTabBar* bar = ::qt_cast<const QtxWorkstackTabBar*>( sender() );
1489 QTab* tab = myBar->tabAt( tabAt( p ) );
1491 wid = widget( tab->identifier() );
1493 emit contextMenuRequested( wid, p );
1496 void QtxWorkstackArea::onWidgetDestroyed()
1499 removeWidget( (QWidget*)sender(), false );
1502 void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
1504 if ( !myList.contains( wid ) )
1507 if ( myBar->tab( widgetId( wid ) ) )
1508 myBar->removeTab( myBar->tab( widgetId( wid ) ) );
1509 myStack->removeWidget( child( wid ) );
1511 myList.remove( wid );
1512 myInfo.remove( wid );
1513 myChild.remove( wid );
1517 delete child( wid );
1518 if( myList.isEmpty() )
1527 QWidgetList QtxWorkstackArea::widgetList() const
1530 for ( QWidgetListIt it( myList ); it.current(); ++it )
1532 if ( widgetVisibility( it.current() ) )
1533 lst.append( it.current() );
1538 QWidget* QtxWorkstackArea::activeWidget() const
1540 return widget( myBar->currentTab() );
1543 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
1545 myBar->setCurrentTab( widgetId( wid ) );
1548 bool QtxWorkstackArea::contains( QWidget* wid ) const
1550 return myList.contains( wid );
1553 void QtxWorkstackArea::show()
1555 QMap<QWidget*, bool> map;
1556 for ( QWidgetListIt it( myList ); it.current(); ++it )
1558 map.insert( it.current(), isBlocked( it.current() ) );
1559 setBlocked( it.current(), true );
1564 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1565 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1568 void QtxWorkstackArea::hide()
1570 QMap<QWidget*, bool> map;
1571 for ( QWidgetListIt it( myList ); it.current(); ++it )
1573 map.insert( it.current(), isBlocked( it.current() ) );
1574 setBlocked( it.current(), true );
1579 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1580 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1583 bool QtxWorkstackArea::isActive() const
1585 QtxWorkstack* ws = workstack();
1589 return ws->activeArea() == this;
1592 void QtxWorkstackArea::updateActiveState()
1594 myBar->setActive( isActive() );
1597 QtxWorkstack* QtxWorkstackArea::workstack() const
1599 QtxWorkstack* ws = 0;
1600 QWidget* wid = parentWidget();
1601 while ( wid && !ws )
1603 if ( wid->inherits( "QtxWorkstack" ) )
1604 ws = (QtxWorkstack*)wid;
1605 wid = wid->parentWidget();
1610 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
1612 if ( o->isWidgetType() )
1614 QWidget* wid = (QWidget*)o;
1615 if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
1618 while ( !ok && wid && wid != myClose )
1621 wid = wid->parentWidget();
1624 QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
1630 QRect QtxWorkstackArea::floatRect() const
1632 QRect r = myStack->geometry();
1633 return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
1636 QRect QtxWorkstackArea::floatTab( const int idx ) const
1638 return myBar->tabRect( idx );
1641 int QtxWorkstackArea::tabAt( const QPoint& p ) const
1644 for ( int i = 0; i < myBar->count() && idx == -1; i++ )
1646 QRect r = myBar->tabRect( i );
1647 if ( r.isValid() && r.contains( p ) )
1653 void QtxWorkstackArea::customEvent( QCustomEvent* e )
1655 switch ( e->type() )
1657 case ActivateWidget:
1658 emit activated( activeWidget() );
1661 if ( activeWidget() )
1663 if ( !activeWidget()->focusWidget() )
1664 activeWidget()->setFocus();
1666 if ( activeWidget()->focusWidget()->hasFocus()) {
1667 QFocusEvent in(QEvent::FocusIn);
1668 QApplication::sendEvent(this, &in);
1671 activeWidget()->focusWidget()->setFocus();
1676 removeWidget( (QWidget*)e->data() );
1681 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
1683 QWidget::focusInEvent( e );
1685 emit activated( activeWidget() );
1688 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
1690 QWidget::mousePressEvent( e );
1692 emit activated( activeWidget() );
1695 void QtxWorkstackArea::onClose()
1697 QWidget* wid = activeWidget();
1702 void QtxWorkstackArea::onSelected( int id )
1706 emit activated( activeWidget() );
1709 void QtxWorkstackArea::onDragActiveTab()
1711 QtxWorkstackChild* c = child( activeWidget() );
1715 new QtxWorkstackDrag( workstack(), c );
1718 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
1720 QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
1721 myStack->removeWidget( child );
1724 for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
1726 if ( it.data() == child )
1730 myChild.remove( wid );
1732 QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveWidget, wid ) );
1735 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
1737 setWidgetShown( c->widget(), true );
1740 void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
1742 setWidgetShown( c->widget(), false );
1745 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
1747 setWidgetActive( c->widget() );
1750 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
1752 updateTab( c->widget() );
1755 void QtxWorkstackArea::updateCurrent()
1757 QMap<QWidget*, bool> map;
1758 for ( QWidgetListIt it( myList ); it.current(); ++it )
1760 map.insert( it.current(), isBlocked( it.current() ) );
1761 setBlocked( it.current(), true );
1764 myStack->raiseWidget( myBar->currentTab() );
1766 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1767 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1770 void QtxWorkstackArea::updateTab( QWidget* wid )
1772 QTab* tab = myBar->tab( widgetId( wid ) );
1779 QPixmap pix = *wid->icon();
1780 pix.convertFromImage( pix.convertToImage().smoothScale( pix.width(), 16, QImage::ScaleMin ) );
1781 icoSet = QIconSet( pix );
1784 tab->setIconSet( icoSet );
1785 tab->setText( wid->caption() );
1788 QWidget* QtxWorkstackArea::widget( const int id ) const
1791 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
1793 if ( it.data().id == id )
1799 int QtxWorkstackArea::widgetId( QWidget* wid ) const
1802 if ( myInfo.contains( wid ) )
1803 id = myInfo[wid].id;
1807 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
1810 if ( myInfo.contains( wid ) )
1811 res = myInfo[wid].vis;
1815 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
1817 int id = widgetId( wid );
1821 myBar->setCurrentTab( id );
1824 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
1826 if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
1829 myInfo[wid].vis = on;
1833 void QtxWorkstackArea::updateState()
1835 bool updBar = myBar->isUpdatesEnabled();
1836 bool updStk = myStack->isUpdatesEnabled();
1837 myBar->setUpdatesEnabled( false );
1838 myStack->setUpdatesEnabled( false );
1840 bool block = myBar->signalsBlocked();
1841 myBar->blockSignals( true );
1843 QWidget* prev = activeWidget();
1846 for ( QWidgetListIt it( myList ); it.current(); ++it )
1848 QWidget* wid = it.current();
1849 int id = widgetId( wid );
1854 bool vis = widgetVisibility( wid );
1856 if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
1857 myBar->removeTab( myBar->tab( id ) );
1859 if ( !myBar->tab( id ) && vis )
1861 QTab* tab = new QTab( wid->caption() );
1862 myBar->insertTab( tab, idx );
1863 tab->setIdentifier( id );
1868 bool block = isBlocked( wid );
1869 setBlocked( wid, true );
1871 QtxWorkstackChild* cont = child( wid );
1874 myStack->removeWidget( cont );
1875 else if ( !myStack->widget( id ) )
1876 myStack->addWidget( cont, id );
1881 setBlocked( wid, block );
1884 int curId = widgetId( prev );
1885 if ( !myBar->tab( curId ) )
1888 int pos = myList.find( prev );
1889 for ( int i = pos - 1; i >= 0 && !wid; i-- )
1891 if ( widgetVisibility( myList.at( i ) ) )
1892 wid = myList.at( i );
1895 for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
1897 if ( widgetVisibility( myList.at( j ) ) )
1898 wid = myList.at( j );
1902 curId = widgetId( wid );
1905 myBar->setCurrentTab( curId );
1907 myBar->blockSignals( block );
1911 myBar->setUpdatesEnabled( updBar );
1912 myStack->setUpdatesEnabled( updStk );
1918 QResizeEvent re( myBar->size(), myBar->size() );
1919 QApplication::sendEvent( myBar, &re );
1924 emit deactivated( this );
1929 if ( prev != activeWidget() )
1930 emit activated( activeWidget() );
1934 int QtxWorkstackArea::generateId() const
1938 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
1939 map.insert( it.data().id, 0 );
1942 while ( map.contains( id ) )
1948 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
1950 return myBlock.contains( wid );
1953 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
1956 myBlock.insert( wid, 0 );
1958 myBlock.remove( wid );
1961 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1963 QtxWorkstackChild* res = 0;
1964 if ( myChild.contains( wid ) )
1970 Class: QtxWorkstackChild [Internal]
1974 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
1978 myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
1979 myWidget->installEventFilter( this );
1981 connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1984 QtxWorkstackChild::~QtxWorkstackChild()
1986 qApp->removeEventFilter( this );
1991 widget()->removeEventFilter( this );
1992 widget()->reparent( 0, QPoint( 0, 0 ), false );
1993 disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1996 QWidget* QtxWorkstackChild::widget() const
2001 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
2003 if ( o->isWidgetType() )
2005 if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
2006 emit captionChanged( this );
2008 if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
2011 if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
2014 if ( e->type() == QEvent::FocusIn )
2015 emit activated( this );
2017 return QHBox::eventFilter( o, e );
2020 void QtxWorkstackChild::onDestroyed( QObject* obj )
2022 if ( obj != widget() )
2029 void QtxWorkstackChild::childEvent( QChildEvent* e )
2031 if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
2036 QHBox::childEvent( e );
2040 Class: QtxWorkstackTabBar [Internal]
2044 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
2045 : QTabBar( parent ),
2050 QtxWorkstackTabBar::~QtxWorkstackTabBar()
2054 void QtxWorkstackTabBar::setActive( const bool on )
2056 QFont aFont = font();
2057 aFont.setUnderline( on );
2058 QColorGroup* aColGrp = new QColorGroup();
2059 QPalette aPal = palette();
2061 aPal.setColor( QColorGroup::HighlightedText, aColGrp->foreground() );
2062 aPal.setColor( QColorGroup::Highlight, colorGroup().dark().light( DARK_COLOR_LIGHT ) );
2066 aPal.setColor( QColorGroup::HighlightedText, aColGrp->highlightedText() );
2067 aPal.setColor( QColorGroup::Highlight, aColGrp->highlight() );
2075 QRect QtxWorkstackTabBar::tabRect( const int idx ) const
2078 QTab* t = tabAt( idx );
2082 r.setLeft( QMAX( r.left(), 0 ) );
2084 int x1 = tabAt( 0 )->rect().left();
2085 int x2 = tabAt( count() - 1 )->rect().right();
2088 if ( QABS( x2 - x1 ) > width() )
2089 #if defined QT_VERSION && QT_VERSION >= 0x30300
2090 bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
2095 int limit = width() - bw;
2096 r.setRight( QMIN( r.right(), limit ) );
2098 r = QRect( mapToGlobal( r.topLeft() ), r.size() );
2103 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
2105 if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
2108 emit dragActiveTab();
2111 QTabBar::mouseMoveEvent( e );
2114 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
2116 QTabBar::mousePressEvent( e );
2118 if ( e->button() == LeftButton )
2119 myId = currentTab();
2122 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
2124 QTabBar::mouseReleaseEvent( e );
2128 if ( e->button() == RightButton )
2129 emit contextMenuRequested( e->globalPos() );
2132 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
2134 if ( e->reason() != QContextMenuEvent::Mouse )
2135 emit contextMenuRequested( e->globalPos() );
2138 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
2140 if ( currentTab() != t->identifier() )
2142 QFont fnt = p->font();
2143 fnt.setUnderline( false );
2146 QTabBar::paintLabel( p, br, t, has_focus );
2150 Class: QtxWorkstackDrag [Internal]
2154 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
2162 qApp->installEventFilter( this );
2165 QtxWorkstackDrag::~QtxWorkstackDrag()
2167 qApp->removeEventFilter( this );
2172 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
2174 switch ( e->type() )
2176 case QEvent::MouseMove:
2177 updateTarget( ((QMouseEvent*)e)->globalPos() );
2179 case QEvent::MouseButtonRelease:
2191 void QtxWorkstackDrag::updateTarget( const QPoint& p )
2194 QtxWorkstackArea* area = detectTarget( p, tab );
2195 setTarget( area, tab );
2198 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
2203 QtxWorkstackArea* area = myWS->areaAt( p );
2205 tab = area->tabAt( p );
2209 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
2211 if ( !area || ( myArea == area && tab == myTab ) )
2226 void QtxWorkstackDrag::dropWidget()
2229 myArea->insertWidget( myChild->widget(), myTab );
2232 void QtxWorkstackDrag::drawRect()
2234 if ( !myPainter || !myArea )
2237 QRect r = myArea->floatRect();
2238 int m = myPainter->pen().width();
2240 r.setTop( r.top() + m + 2 );
2241 r.setLeft( r.left() + m + 2 );
2242 r.setRight( r.right() - m - 2 );
2243 r.setBottom( r.bottom() - m - 2 );
2245 myPainter->drawRect( r );
2247 QRect tr = myArea->floatTab( myTab );
2248 tr.setTop( tr.top() + m );
2249 tr.setLeft( tr.left() + m );
2250 tr.setRight( tr.right() - m );
2251 tr.setBottom( tr.bottom() - m );
2253 myPainter->drawRect( tr );
2256 void QtxWorkstackDrag::endDrawRect()
2262 void QtxWorkstackDrag::startDrawRect()
2267 int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
2268 QWidget* paint_on = QApplication::desktop()->screen( scr );
2270 myPainter = new QPainter( paint_on, true );
2271 myPainter->setPen( QPen( gray, 3 ) );
2272 myPainter->setRasterOp( XorROP );