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
46 QtxWorkstack::QtxWorkstack( QWidget* parent )
53 myActionsMap.insert( SplitVertical, new QAction( tr( "Split vertically" ), 0, this ) );
54 myActionsMap.insert( SplitHorizontal, new QAction( tr( "Split horizontally" ), 0, this ) );
55 myActionsMap.insert( Close, new QAction( tr( "Close" ), 0, this ) );
56 myActionsMap.insert( Rename, new QAction( tr( "Rename" ), 0, this ) );
58 connect( myActionsMap[SplitVertical], SIGNAL( activated() ), this, SLOT( splitVertical() ) );
59 connect( myActionsMap[SplitHorizontal], SIGNAL( activated() ), this, SLOT( splitHorizontal() ) );
60 connect( myActionsMap[Close], SIGNAL( activated() ), this, SLOT( onCloseWindow() ) );
61 connect( myActionsMap[Rename], SIGNAL( activated() ), this, SLOT( onRename() ) );
63 QVBoxLayout* base = new QVBoxLayout( this );
64 mySplit = new QSplitter( this );
65 mySplit->setChildrenCollapsible( false );
66 base->addWidget( mySplit );
72 QtxWorkstack::~QtxWorkstack()
77 \return list of all widgets in all areas
79 QWidgetList QtxWorkstack::windowList() const
81 QPtrList<QtxWorkstackArea> lst;
82 areas( mySplit, lst, true );
85 for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current(); ++it )
87 QWidgetList wids = it.current()->widgetList();
88 for ( QWidgetListIt itr( wids ); itr.current(); ++itr )
89 widList.append( itr.current() );
96 \return list of all widgets in active area
98 QWidgetList QtxWorkstack::splitWindowList() const
100 return myArea ? myArea->widgetList() : QWidgetList();
104 \return active widget
106 QWidget* QtxWorkstack::activeWindow() const
113 \param o - orientation (Qt::Orientation)
115 void QtxWorkstack::split( const int o )
117 QtxWorkstackArea* area = myWorkArea;
123 if ( area->widgetList().count() < 2 )
126 QWidget* curWid = area->activeWidget();
130 QSplitter* s = splitter( area );
131 QPtrList<QtxWorkstackArea> areaList;
132 areas( s, areaList );
134 QPtrList<QSplitter> splitList;
135 splitters( s, splitList );
138 if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
142 trg = wrapSplitter( area );
147 trg->setOrientation( (Orientation)o );
149 QtxWorkstackArea* newArea = createArea( 0 );
150 insertWidget( newArea, trg, area );
152 area->removeWidget( curWid );
153 newArea->insertWidget( curWid );
155 distributeSpace( trg );
162 \brief Split workarea of the given widget on two parts.
163 \param wid - widget, belonging to this workstack
164 \param o - orientation of splitting (Qt::Horizontal or Qt::Vertical)
165 \param type - type of splitting, see <VAR>SplitType</VAR> enumeration
167 void QtxWorkstack::Split (QWidget* wid, const Qt::Orientation o, const SplitType type)
171 // find area of the given widget
172 QtxWorkstackArea* area = NULL;
173 QPtrList<QtxWorkstackArea> allAreas;
174 areas(mySplit, allAreas, true);
176 QPtrListIterator<QtxWorkstackArea> it (allAreas);
177 for (; it.current() && !area; ++it) {
178 if (it.current()->contains(wid))
183 QWidgetList wids = area->widgetList();
184 if (wids.count() < 2)
187 QSplitter* s = splitter(area);
188 QPtrList<QtxWorkstackArea> areaList;
191 QPtrList<QSplitter> splitList;
192 splitters(s, splitList);
195 if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
198 if (!trg) trg = wrapSplitter(area);
201 trg->setOrientation(o);
203 QtxWorkstackArea* newArea = createArea(0);
204 insertWidget(newArea, trg, area);
209 QWidgetListIt itr (wids);
210 for (; itr.current(); ++itr)
212 QWidget* wid_i = itr.current();
214 area->removeWidget(wid_i);
215 newArea->insertWidget(wid_i);
222 QWidgetListIt itr (wids);
223 for (; itr.current() && itr.current() != wid; ++itr) {
225 for (; itr.current(); ++itr) {
226 area->removeWidget(itr.current());
227 newArea->insertWidget(itr.current());
232 area->removeWidget(wid);
233 newArea->insertWidget(wid);
237 distributeSpace(trg);
241 \brief Put given widget on top of its workarea
242 \param wid - widget, belonging to this workstack
245 void QtxWorkstack::OnTop (QWidget* wid)
250 // find area of the given widget
251 QtxWorkstackArea* area = 0;
252 QPtrList<QtxWorkstackArea> allAreas;
253 areas( mySplit, allAreas, true );
254 for ( QPtrListIterator<QtxWorkstackArea> it( allAreas ); it.current() && !area; ++it )
256 if ( it.current()->contains( wid ) )
261 area->setActiveWidget( wid );
266 \brief Move widget(s) from source workarea into target workarea
267 or just reorder widgets inside one workarea.
268 \param wid1 - widget from target workarea
269 \param wid2 - widget from source workarea
270 \param all - if this parameter is TRUE, all widgets from source workarea will
271 be moved into the target one, else only the \a wid2 will be moved
273 Move \a wid2 in target workarea. Put it right after \a wid1.
274 If value of boolean argument is TRUE, all widgets from source workarea
275 will be moved together with \a wid2, source workarea will be deleted.
276 If \a wid1 and \a wid2 belongs to one workarea, simple reordering will take place.
278 void QtxWorkstack::Attract ( QWidget* wid1, QWidget* wid2, const bool all )
280 if ( !wid1 || !wid2 )
283 // find area of the widgets
284 QtxWorkstackArea *area1 = NULL, *area2 = NULL;
285 QPtrList<QtxWorkstackArea> allAreas;
286 areas(mySplit, allAreas, true);
287 QPtrListIterator<QtxWorkstackArea> it (allAreas);
288 for (; it.current() && !(area1 && area2); ++it) {
289 if (it.current()->contains(wid1))
290 area1 = it.current();
291 if (it.current()->contains(wid2))
292 area2 = it.current();
294 if (!area1 || !area2) return;
296 QWidget* curWid = area1->activeWidget();
299 if (area1 == area2) {
301 // Set wid1 at first position, wid2 at second
302 area1->insertWidget(wid1);
303 area1->insertWidget(wid2, 1);
305 // Set wid2 right after wid1
306 area1->removeWidget(wid2);
308 QWidgetList wids1 = area1->widgetList();
309 QWidgetListIt itr1 (wids1);
310 for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
311 area1->insertWidget(wid2, wid1_ind + 1);
315 QWidgetList wids1 = area1->widgetList();
316 QWidgetListIt itr1 (wids1);
317 for (; itr1.current() && itr1.current() != wid1; ++itr1, ++wid1_ind);
320 // Set wid2 right after wid1, other widgets from area2 right after wid2
321 QWidgetList wids2 = area2->widgetList();
322 QWidgetListIt itr2 (wids2);
323 for (int ind = wid1_ind + 1; itr2.current(); ++itr2, ++ind)
325 area2->removeWidget(itr2.current());
326 if (itr2.current() == wid2) {
327 area1->insertWidget(itr2.current(), wid1_ind + 1);
329 area1->insertWidget(itr2.current(), ind);
333 // Set wid2 right after wid1
334 area2->removeWidget(wid2);
335 area1->insertWidget(wid2, wid1_ind + 1);
339 area1->setActiveWidget( curWid );
342 static void setSizes (QIntList& szList, const int item_ind,
343 const int new_near, const int new_this, const int new_farr)
345 // set size to all items before an item # <item_ind>
347 QIntList::iterator its = szList.begin();
348 for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
351 if (its == szList.end()) return;
352 // set size to item # <item_ind>
355 // set size to all items after an item # <item_ind>
356 for (; its != szList.end(); ++its) {
362 * \brief Set position of the widget relatively its splitter.
363 * \param wid - widget to set position of
364 * \param pos - position relatively splitter. Value in range [0..1].
366 * Orientation of positioning will correspond to the splitter orientation.
368 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
370 if ( position < 0.0 || 1.0 < position)
376 // find area of the given widget
377 QtxWorkstackArea* area = NULL;
378 QPtrList<QtxWorkstackArea> allAreas;
379 areas(mySplit, allAreas, true);
380 for ( QPtrListIterator<QtxWorkstackArea> it( allAreas );
381 it.current() && !area;
384 if (it.current()->contains(wid))
391 QSplitter* split = splitter( area );
395 // find index of the area in its splitter
397 bool isFound = false;
398 const QObjectList* was = split->children();
399 for (QObjectListIt ito (*was); ito.current() && !isFound; ++ito, ++item_ind)
401 if (ito.current() == area)
404 if (!isFound || item_ind == 0)
407 QIntList szList = split->sizes();
408 int splitter_size = (split->orientation() == Horizontal ?
409 split->width() : split->height());
410 int nb = szList.count();
412 int new_prev = int(splitter_size * position / item_ind);
413 int new_next = int(splitter_size * (1.0 - position) / (nb - item_ind));
414 setSizes (szList, item_ind, new_prev, new_next, new_next);
415 split->setSizes(szList);
419 * \brief Set position of the widget relatively the entire workstack.
420 * \param wid - widget to set position of
421 * \param o - orientation of positioning (Qt::Horizontal or Qt::Vertical).
422 * If o = Qt::Horizontal, horizontal position of \a wid will be changed.
423 * If o = Qt::Vertical, vertical position of \a wid will be changed.
424 * \param pos - position relatively workstack. Value in range [0..1].
426 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
427 const double position )
429 if ( position < 0.0 || 1.0 < position )
435 int splitter_size = o == Horizontal ? mySplit->width() : mySplit->height();
436 int need_pos = int( position * splitter_size );
437 int splitter_pos = 0;
439 if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
441 // impossible to set required position
446 * \brief Sets the action's accelerator key to accel.
447 * \param id - the key of the action in the actions map.
448 * \param accel - action's accelerator key.
450 void QtxWorkstack::setAccel( const int id, const int accel )
452 if ( !myActionsMap.contains( id ) )
455 myActionsMap[id]->setAccel( accel );
459 * \brief Returns the action's accelerator key.
460 * \param id - the key of the action in the actions map.
461 * \retval int - action's accelerator key.
463 int QtxWorkstack::accel( const int id ) const
466 if ( myActionsMap.contains( id ) )
467 res = myActionsMap[id]->accel();
471 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
472 const int item_ind, const int item_rel_pos,
473 const int need_pos, const int splitter_pos)
475 if (item_ind == 0) { // cannot move in this splitter
476 return (need_pos - splitter_pos);
481 int new_this = szList[item_ind];
484 bool isToCheck = false;
486 if (need_pos < splitter_pos) {
487 // Set size of all previous workareas to zero <--
488 if (item_ind == nb - 1) {
489 // item iz last in the splitter, it will occupy all the splitter
490 new_this = splitter_size;
492 // recompute size of next items in splitter
493 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
495 delta = need_pos - splitter_pos;
497 } else if (need_pos > (splitter_pos + splitter_size)) {
498 // Set size of all next workareas to zero -->
499 // recompute size of previous items in splitter
501 new_prev = (splitter_size - new_this) / item_ind;
502 delta = need_pos - (splitter_pos + splitter_size - new_this);
504 } else { // required position lays inside this splitter
505 // Move workarea inside splitter into required position <->
506 int new_item_rel_pos = need_pos - splitter_pos;
507 new_prev = new_item_rel_pos / item_ind;
508 if (need_pos < (splitter_pos + item_rel_pos)) {
509 // Make previous workareas smaller, next - bigger
510 // No problem to keep old size of the widget
512 // Make previous workareas bigger, next - smaller
513 if (new_this > splitter_size - new_item_rel_pos) {
514 new_this = splitter_size - new_item_rel_pos;
516 // jfa to do: in this case fixed size of next widgets could prevent right resizing
519 if (item_ind == nb - 1) {
520 new_this = splitter_size - new_item_rel_pos;
522 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
527 setSizes (szList, item_ind, new_prev, new_this, new_next);
532 * \brief Set position of given widget.
533 * \param wid - widget to be moved
534 * \param split - currently processed splitter (goes from more common
535 * to more particular splitter in recursion calls)
536 * \param o - orientation of positioning
537 * \param need_pos - required position of the given widget in pixels
538 * (from top/left side of workstack area)
539 * \param splitter_pos - position of the splitter \a split
540 * (from top/left side of workstack area)
541 * \retval int - returns difference between a required and a distinguished position.
543 * Internal method. Recursively calls itself.
544 * Is called from <VAR>SetRelativePosition</VAR> public method.
546 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
547 const int need_pos, const int splitter_pos )
549 if ( !wid || !split )
550 return need_pos - splitter_pos;
552 // Find corresponding sub-splitter.
553 // Find also index of appropriate item in current splitter.
554 int cur_ind = 0, item_ind = 0;
555 bool isBottom = false, isFound = false;
556 QSplitter* sub_split = NULL;
557 const QObjectList* objs = split->children();
560 for (QObjectListIt it (*objs); it.current() && !isFound; ++it)
562 if (it.current()->inherits( "QtxWorkstackArea")) {
563 if (((QtxWorkstackArea*)it.current())->contains(wid)) {
569 } else if (it.current()->inherits("QSplitter")) {
570 QPtrList<QtxWorkstackArea> areaList;
571 areas((QSplitter*)it.current(), areaList, true);
572 for (QPtrListIterator<QtxWorkstackArea> ita (areaList);
573 ita.current() && !isFound;
576 if (ita.current()->contains(wid)) {
579 sub_split = (QSplitter*)it.current();
587 return (need_pos - splitter_pos);
589 if (split->orientation() == o) {
590 // Find coordinates of near and far sides of the appropriate item relatively current splitter
591 int splitter_size = (o == Horizontal ? split->width() : split->height());
592 QIntList szList = split->sizes();
593 int nb = szList.count();
594 int item_rel_pos = 0; // position of near side of item relatively this splitter
595 for (int i = 0; i < item_ind; i++) {
596 item_rel_pos += szList[i];
598 int item_size = szList[item_ind]; // size of item
599 int item_pos = splitter_pos + item_rel_pos;
601 // Resize splitter items to complete the conditions
603 // I. Bottom of splitters stack reached
605 int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
606 split->setSizes(szList);
607 // Recompute delta, as some windows can reject given size
608 int new_item_rel_pos = 0;
609 QIntList szList1 = split->sizes();
610 for (int i = 0; i < item_ind; i++) {
611 new_item_rel_pos += szList1[i];
613 delta = need_pos - (splitter_pos + new_item_rel_pos);
617 // II. Bottom of splitters stack is not yet reached
619 if (item_ind == 0) { // cannot move in this splitter
620 // Process in sub-splitter
621 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
625 int new_this = szList[item_ind];
628 if (need_pos < splitter_pos) {
629 // Set size of all previous workareas to zero <--
630 if (item_ind == nb - 1) {
631 new_this = splitter_size;
633 new_next = (splitter_size - new_this) / (nb - item_ind - 1);
635 setSizes (szList, item_ind, new_prev, new_this, new_next);
636 split->setSizes(szList);
637 // Recompute splitter_pos, as some windows can reject given size
638 int new_item_rel_pos = 0;
639 QIntList szList1 = split->sizes();
640 for (int i = 0; i < item_ind; i++) {
641 new_item_rel_pos += szList1[i];
643 // Process in sub-splitter
644 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
645 } else if (need_pos > (splitter_pos + splitter_size)) {
646 // Set size of all next workareas to zero -->
647 new_prev = (splitter_size - new_this) / item_ind;
648 setSizes (szList, item_ind, new_prev, new_this, new_next);
649 split->setSizes(szList);
650 // Recompute splitter_pos, as some windows can reject given size
651 int new_item_rel_pos = 0;
652 QIntList szList1 = split->sizes();
653 for (int i = 0; i < item_ind; i++) {
654 new_item_rel_pos += szList1[i];
656 // Process in sub-splitter
657 return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
659 // Set appropriate size of all previous/next items <->
660 int new_item_rel_pos = item_rel_pos;
661 if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
662 // Move item inside splitter into required position <->
663 int new_this = szList[item_ind];
665 new_item_rel_pos = need_pos - splitter_pos;
666 if ((item_pos + item_size) < need_pos) {
667 //new_item_rel_pos = need_pos - (item_pos + item_size);
668 new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
670 int new_prev = new_item_rel_pos / item_ind;
671 if (need_pos < (splitter_pos + item_rel_pos)) {
672 // Make previous workareas smaller, next - bigger
673 // No problem to keep old size of the widget
675 // Make previous workareas bigger, next - smaller
676 if (new_this > splitter_size - new_item_rel_pos) {
677 new_this = splitter_size - new_item_rel_pos;
680 if (item_ind == nb - 1) {
681 new_this = splitter_size - new_item_rel_pos;
683 new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
685 setSizes (szList, item_ind, new_prev, new_this, new_next);
686 split->setSizes(szList);
687 // Recompute new_item_rel_pos, as some windows can reject given size
688 new_item_rel_pos = 0;
689 QIntList szList1 = split->sizes();
690 for (int i = 0; i < item_ind; i++) {
691 new_item_rel_pos += szList1[i];
696 // Process in sub-splitter
697 int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
701 // this can be if corresponding workarea is first in sub-splitter
702 // or sub-splitter has another orientation
704 // Resize ones again to reach precize position <->
705 int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
707 // Move workarea inside splitter into required position <->
708 int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
709 new_item_rel_pos, need_pos_1, splitter_pos);
710 split->setSizes(szList);
711 // Recompute new_item_rel_pos, as some windows can reject given size
712 new_item_rel_pos = 0;
713 QIntList szList1 = split->sizes();
714 for (int i = 0; i < item_ind; i++) {
715 new_item_rel_pos += szList1[i];
717 delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
722 return setPosition(wid, sub_split, o, need_pos, splitter_pos);
729 Redistributes space among widgets equally
731 void QtxWorkstack::distributeSpace( QSplitter* split ) const
736 QIntList szList = split->sizes();
737 int size = ( split->orientation() == Horizontal ?
738 split->width() : split->height() ) / szList.count();
739 for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
741 split->setSizes( szList );
745 Splits widgets vertically
747 void QtxWorkstack::splitVertical()
749 split( Qt::Horizontal );
753 Splits widgets horizontally
755 void QtxWorkstack::splitHorizontal()
757 split( Qt::Vertical );
761 SLOT: called if action "Rename" is activated, changes caption of widget
763 void QtxWorkstack::onRename()
769 QString newName = QInputDialog::getText( tr( "Rename" ), tr( "Enter new name:" ), QLineEdit::Normal,
770 myWorkWin->caption(), &ok, topLevelWidget() );
771 if ( ok && !newName.isEmpty() )
772 myWorkWin->setCaption( newName );
776 Wraps area into new splitter
779 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
784 QSplitter* pSplit = splitter( area );
788 bool upd = pSplit->isUpdatesEnabled();
789 pSplit->setUpdatesEnabled( false );
791 QIntList szList = pSplit->sizes();
793 QSplitter* wrap = new QSplitter( 0 );
794 #if defined QT_VERSION && QT_VERSION >= 0x30200
795 wrap->setChildrenCollapsible( false );
797 insertWidget( wrap, pSplit, area );
798 area->reparent( wrap, QPoint( 0, 0 ), true );
800 pSplit->setSizes( szList );
802 pSplit->setUpdatesEnabled( upd );
808 Reparenst and adds widget
810 \param pWid - parent widget
811 \param after - after widget
813 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
818 QWidgetList moveList;
819 const QObjectList* lst = pWid->children();
823 for ( QObjectListIt it( *lst ); it.current(); ++it )
825 if ( found && ( it.current()->inherits( "QSplitter" ) ||
826 it.current()->inherits( "QtxWorkstackArea" ) ) )
827 moveList.append( (QWidget*)it.current() );
828 if ( it.current() == after )
833 QMap<QWidget*, bool> map;
834 for ( QWidgetListIt it( moveList ); it.current(); ++it )
836 map.insert( it.current(), it.current()->isVisibleTo( it.current()->parentWidget() ) );
837 it.current()->reparent( 0, QPoint( 0, 0 ), false );
840 wid->reparent( pWid, QPoint( 0, 0 ), true );
842 for ( QWidgetListIt itr( moveList ); itr.current(); ++itr )
843 itr.current()->reparent( pWid, QPoint( 0, 0 ), map.contains( itr.current() ) ? map[itr.current()] : false );
847 * \brief Closes the active window.
849 void QtxWorkstack::onCloseWindow()
853 else if( activeWindow() )
854 activeWindow()->close();
858 SLOT: called on area is destroyed
859 Sets focus to neighbour area
861 void QtxWorkstack::onDestroyed( QObject* obj )
863 QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
865 if ( area == myArea )
870 QtxWorkstackArea* cur = neighbourArea( area );
875 QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
879 SLOT: called on window activating
881 void QtxWorkstack::onWindowActivated( QWidget* wid )
883 const QObject* obj = sender();
884 if ( !obj->inherits( "QtxWorkstackArea" ) )
887 setActiveArea( (QtxWorkstackArea*)obj );
891 SLOT: called on window deactivating
893 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
895 if ( myArea != area )
898 QPtrList<QtxWorkstackArea> lst;
899 areas( mySplit, lst, true );
901 int idx = lst.find( area );
908 QtxWorkstackArea* newArea = neighbourArea( area );
909 if ( newArea && newArea->activeWidget() )
910 newArea->activeWidget()->setFocus();
912 QApplication::postEvent( this, new QCustomEvent( QEvent::User ) );
916 Creates and shows popup menu for area
918 \param p - popup position
920 void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
922 QtxWorkstackArea* anArea = dynamic_cast<QtxWorkstackArea*>( (QObject*)sender() );
924 anArea = activeArea();
929 QWidgetList lst = anArea->widgetList();
936 QPopupMenu* pm = new QPopupMenu();
938 if ( lst.count() > 1 )
940 myActionsMap[SplitVertical]->addTo( pm );
941 myActionsMap[SplitHorizontal]->addTo( pm );
942 pm->insertSeparator();
947 myActionsMap[Close]->addTo( pm );
948 myActionsMap[Rename]->addTo( pm );
961 Custom child event handler, inserts widget to active or current area
963 void QtxWorkstack::childEvent( QChildEvent* e )
965 if ( e->inserted() && e->child()->isWidgetType() )
967 QWidget* w = (QWidget*)e->child();
968 if ( w && w != mySplit )
970 targetArea()->insertWidget( w );
974 QWidget::childEvent( e );
978 Handler of custom events
980 void QtxWorkstack::customEvent( QCustomEvent* e )
986 \return splitter corresponding to area
989 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
994 QSplitter* split = 0;
996 QWidget* wid = area->parentWidget();
997 if ( wid && wid->inherits( "QSplitter" ) )
998 split = (QSplitter*)wid;
1004 Fills list with children splitters
1005 \param split - parent splitter
1006 \param splitList - list to be filled with
1007 \param rec - recursive search of children
1009 void QtxWorkstack::splitters( QSplitter* split, QPtrList<QSplitter>& splitList, const bool rec ) const
1014 const QObjectList* objs = split->children();
1017 for ( QObjectListIt it( *objs ); it.current(); ++it )
1020 splitters( (QSplitter*)it.current(), splitList, rec );
1021 if ( it.current()->inherits( "QSplitter" ) )
1022 splitList.append( (QSplitter*)it.current() );
1028 Fills list with children areas
1029 \param split - parent splitter
1030 \param areaList - list to be filled with
1031 \param rec - recursive search of children
1033 void QtxWorkstack::areas( QSplitter* split, QPtrList<QtxWorkstackArea>& areaList, const bool rec ) const
1038 const QObjectList* objs = split->children();
1041 for ( QObjectListIt it( *objs ); it.current(); ++it )
1043 if ( it.current()->inherits( "QtxWorkstackArea" ) )
1044 areaList.append( (QtxWorkstackArea*)it.current() );
1045 else if ( rec && it.current()->inherits( "QSplitter" ) )
1046 areas( (QSplitter*)it.current(), areaList, rec );
1054 QtxWorkstackArea* QtxWorkstack::activeArea() const
1060 \return active area or current area or create new area of there is no one
1062 QtxWorkstackArea* QtxWorkstack::targetArea()
1064 QtxWorkstackArea* area = activeArea();
1066 area = currentArea();
1069 QPtrList<QtxWorkstackArea> lst;
1070 areas( mySplit, lst );
1071 if ( !lst.isEmpty() )
1076 area = createArea( mySplit );
1082 \return current area (that has focus)
1084 QtxWorkstackArea* QtxWorkstack::currentArea() const
1086 QtxWorkstackArea* area = 0;
1087 QWidget* wid = focusWidget();
1088 while ( wid && !area )
1090 if ( wid->inherits( "QtxWorkstackArea" ) )
1091 area = (QtxWorkstackArea*)wid;
1092 wid = wid->parentWidget();
1101 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
1103 QtxWorkstackArea* area = new QtxWorkstackArea( parent );
1105 connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1106 connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
1107 connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
1108 this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
1109 connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
1118 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
1120 QWidget* oldCur = myWin;
1122 QtxWorkstackArea* oldArea = myArea;
1126 if ( myArea != oldArea )
1129 oldArea->updateActiveState();
1131 myArea->updateActiveState();
1135 myWin = myArea->activeWidget();
1137 if ( myWin && oldCur != myWin )
1138 emit windowActivated( myWin );
1142 \return neighbour area
1143 \param area - area to search neighbour
1145 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
1147 QPtrList<QtxWorkstackArea> lst;
1148 areas( mySplit, lst, true );
1149 int pos = lst.find( area );
1153 QtxWorkstackArea* na = 0;
1154 for ( int i = pos - 1; i >= 0 && !na; i-- )
1156 if ( !lst.at( i )->isEmpty() )
1160 for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
1162 if ( !lst.at( j )->isEmpty() )
1169 \return area covering point
1172 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
1174 QtxWorkstackArea* area = 0;
1175 QPtrList<QtxWorkstackArea> lst;
1176 areas( mySplit, lst, true );
1177 for ( QPtrListIterator<QtxWorkstackArea> it( lst ); it.current() && !area; ++it )
1179 QtxWorkstackArea* cur = it.current();
1180 QRect r = cur->geometry();
1181 if ( cur->parentWidget() )
1182 r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
1183 if ( r.contains( p ) )
1192 void QtxWorkstack::updateState()
1194 updateState( mySplit );
1200 void QtxWorkstack::updateState( QSplitter* split )
1202 QPtrList<QSplitter> recList;
1203 splitters( split, recList, false );
1204 for ( QPtrListIterator<QSplitter> itr( recList ); itr.current(); ++itr )
1205 updateState( itr.current() );
1207 QPtrList<QSplitter> splitList;
1208 splitters( split, splitList, false );
1210 QPtrList<QtxWorkstackArea> areaList;
1211 areas( split, areaList, false );
1214 for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
1216 if ( it.current()->isEmpty() )
1217 it.current()->hide();
1220 it.current()->show();
1225 if ( split == mySplit )
1228 for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current() && !vis; ++iter )
1229 vis = iter.current()->isVisibleTo( iter.current()->parentWidget() );
1231 if ( areaList.isEmpty() && splitList.isEmpty() )
1240 Gets splitter info for debug
1241 \param split - splitter
1242 \param info - string to be filled with info
1244 void QtxWorkstack::splitterInfo( QSplitter* split, QString& info ) const
1249 const QObjectList* objs = split->children();
1252 // make up a sizes string: integer values are separated by ':' char
1253 QValueList<int> sizes = split->sizes();
1255 for ( QValueList<int>::Iterator sIt = sizes.begin(); sIt != sizes.end(); ++sIt ) {
1256 if ( *sIt > 1 ) // size 1 pixel usually means empty Workstack area, which will NOT be re-created,
1257 sizesStr += QString( ":%1" ).arg( *sIt ); // so we don't need to store its size
1259 if ( !sizesStr.isEmpty() ) // cut the first ':'
1260 sizesStr = sizesStr.right( sizesStr.length()-1 );
1262 // count all QSplitter-s and QtxWorkstackArea-s
1263 // int nChilds( 0 );
1264 // QObjectListIt it( *objs );
1265 // for ( ; it.current(); ++it )
1267 // if ( it.current()->inherits( "QSplitter" ) ||
1268 // it.current()->inherits( "QtxWorkstackArea" ) )
1272 info += QString( "(splitter orientation=%1 sizes=%3 " ).arg( split->orientation() ).arg( sizesStr );
1274 for ( QObjectListIt it( *objs ); it.current(); ++it )
1276 if ( it.current()->inherits( "QSplitter" ) )
1277 splitterInfo( (QSplitter*)it.current(), info );
1278 else if ( it.current()->inherits( "QtxWorkstackArea" ) ) {
1279 QtxWorkstackArea* area = (QtxWorkstackArea*)it.current();
1280 if ( area->isEmpty() )
1282 info += QString( "(views active='%1'" ).arg( area->activeWidget()->name() );
1283 QWidgetList views = area->widgetList();
1284 for ( QWidgetListIt wIt( views ); wIt.current(); ++wIt )
1285 info += QString( " '%1'" ).arg( wIt.current()->name() );
1294 //Cuts starting '(' symbol and ending '(' symbol
1295 void cutBrackets( QString& parameters )
1297 if ( !parameters.isEmpty() && parameters[0] == '(' && parameters[parameters.length()-1] == ')' )
1298 parameters = parameters.mid( 1, parameters.length()-2 );
1302 for strings like "(splitter orientation=0 children=2 sizes=332:478" returns values of
1303 parameters. For example, getValue( example, "children" ) returns "2"
1304 getValue( example, "sizes" ) returns "332:478"
1306 QString getValue( const QString& str, const QString& valName )
1308 int i = str.find( valName );
1310 int equal_i = str.find( '=', i );
1311 if ( equal_i != -1 ) {
1312 int space_i = str.find( ' ', ++equal_i );
1313 if ( space_i != -1 )
1314 return str.mid( equal_i, space_i-equal_i );
1317 return QString( "" );
1321 checks format of splitter parameters string
1323 bool checkFormat( const QString& parameters )
1325 QString params( parameters );
1326 // 1. begins and ends with brackets
1327 bool ok = ( params[0] == '(' && params[params.length()-1] == ')' );
1328 if ( !ok ) return ok;
1329 ::cutBrackets( params );
1330 // 2. has splitter word
1331 ok = ( params.left( 8 ) == "splitter" );
1332 if ( !ok ) return ok;
1333 // 3. has children? = '(' is found
1334 int i = params.find( '(' );
1336 if ( !ok ) return ok;
1337 params = params.left( i ); // cut all children, they will be checked later
1338 // 4. has orientation word and correct value
1339 ::getValue( params, "orientation" ).toInt( &ok );
1340 if ( !ok ) return ok;
1341 // 5. has sizes word and values
1342 ok = ! ::getValue( params, "sizes" ).isEmpty();
1343 if ( !ok ) return ok;
1344 // 6. check children -> number of '(' == number of ')' in original string
1345 ok = ( parameters.contains( '(' ) == parameters.contains( ')' ) );
1350 Returns children of splitter in a list. Children are separated by '(' and ')' symbols
1352 QStringList getChildren( const QString& str )
1355 if ( !str.startsWith( "(" ) )
1359 nOpen = 1, // count brackets: '(' increments nOpen, ')' decrements
1361 while ( i < str.length() ) {
1362 if ( str[i] == '(' ) {
1367 else if ( str[i] == ')' ) {
1370 lst.append( str.mid( start, i-start+1 ) );
1378 // for a string like "views active='AnotherView' 'GLView' 'AnotherView'"
1379 // getViewName( example, 0 ) returns "GLView",
1380 // getViewName( example, 1 ) -> "AnotherView", etc.
1381 QString getViewName( const QString& str, int i )
1383 QRegExp exp( "\\s'\\w+'" );
1384 int start = 0; // start index of view name in the string
1385 int num = 0 ; // index of found match
1386 while ( ( start = exp.search( str, start ) ) != -1 && num < i ) {
1387 start += exp.matchedLength();
1390 if ( start != -1 ) // +2 and -3 avoid starting space and starting and ending ' symbols
1391 return str.mid( start+2, exp.matchedLength()-3 );
1393 return QString( "" );
1396 // returns widget with given name
1397 QWidget* getView( const QWidget* parent, const QString& aName )
1400 QObjectList *l = parent->topLevelWidget()->queryList( "QWidget", aName, false, true );
1401 if ( !l->isEmpty() )
1402 view = dynamic_cast<QWidget*>( l->first() );
1408 Installs a splitter described by given parameters string
1410 void QtxWorkstack::setSplitter( QSplitter* splitter, const QString& parameters, QMap< QSplitter*, QValueList<int> >& sMap )
1412 if ( !::checkFormat( parameters ) ) {
1413 printf( "\nInvalid format of workstack parameters. Positions of viewers can not be restored.\n" );
1417 QString params( parameters );
1418 ::cutBrackets( params );
1420 // get splitter sizes and store it in the map for future setting
1421 QValueList<int> sizes;
1422 QStringList sizesLst = QStringList::split( ':', ::getValue( params, "sizes" ) );
1423 for ( QStringList::Iterator it = sizesLst.begin(); it != sizesLst.end(); ++it )
1424 sizes.append( (*it).toInt() );
1425 sMap[ splitter ] = sizes;
1427 // set orientation of splitter
1428 int orient = ::getValue( params, "orientation" ).toInt();
1429 splitter->setOrientation( (Qt::Orientation)orient );
1432 QString options = params.left( params.find( '(' ) );
1433 QString childrenStr = params.right( params.length()-options.length() );
1434 QStringList children = ::getChildren( childrenStr );
1437 // printf (" splitter orient=%d, sizes_count=%d, children=%d\n", orient, sizes.count(), children.count() );
1438 // for ( QStringList::Iterator tit = children.begin(); tit != children.end(); ++tit )
1439 // printf (" |-> child = [%s]\n", (*tit).latin1() );
1441 for ( QStringList::Iterator it = children.begin(); it != children.end(); ++it ) {
1442 if ( (*it).startsWith( "(splitter" ) ) {
1443 QSplitter* newSplitter = new QSplitter( splitter );
1444 setSplitter( newSplitter, *it, sMap );
1446 else if ( (*it).startsWith( "(views" ) ) {
1447 QtxWorkstackArea* newArea = createArea( splitter );
1448 QString activeViewName = ::getValue( *it, "active" );
1449 QWidget* activeView( 0 );
1450 activeViewName = activeViewName.mid( 1, activeViewName.length()-2 ); // chop off ' symbols
1452 QString viewName = ::getViewName( *it, i );
1453 while ( !viewName.isEmpty() ) {
1454 if ( QWidget* view = ::getView( splitter, viewName ) ) {
1455 newArea->insertWidget( view );
1456 if ( activeViewName == view->name() )
1459 viewName = ::getViewName( *it, ++i );
1462 newArea->setActiveWidget( activeView );
1468 Restore workstack's configuration stored in 'parameters' string
1470 QtxWorkstack& QtxWorkstack::operator<<( const QString& parameters )
1472 // clear the main splitter - remove all child splitters and empty areas from it
1473 QPtrList<QSplitter> splitList;
1474 QPtrList<QtxWorkstackArea> areaList;
1475 splitters( mySplit, splitList, false );
1476 areas( mySplit, areaList, false );
1477 for ( QPtrListIterator<QSplitter> iter( splitList ); iter.current(); ++iter )
1478 delete iter.current();
1479 for ( QPtrListIterator<QtxWorkstackArea> it( areaList ); it.current(); ++it )
1480 if ( it.current()->isEmpty() )
1481 delete it.current();
1483 // restore splitter recursively
1484 QMap< QSplitter*, QValueList<int> > sMap;
1485 setSplitter( mySplit, parameters, sMap );
1487 // now mySplit may contains empty area (where all views were located before restoring)
1488 // in order setSize to work correctly we have to exclude this area
1490 areas( mySplit, areaList, false );
1491 for ( QPtrListIterator<QtxWorkstackArea> delIt( areaList ); delIt.current(); ++delIt )
1492 if ( delIt.current()->isEmpty() )
1493 delete delIt.current();
1495 qApp->processEvents();
1497 // restore splitters' sizes (map of sizes is filled in setSplitters)
1498 for ( QMap< QSplitter*, QValueList<int> >::Iterator it = sMap.begin(); it != sMap.end(); ++it )
1499 it.key()->setSizes( it.data() );
1505 Example of string produced by operator>> :
1506 "(splitter orientation=0 sizes=186:624 (views active='OCCViewer_0_0' 'OCCViewer_0_0')
1507 / (views active='VTKViewer_0_0' 'VTKViewer_0_0'))"
1509 QtxWorkstack& QtxWorkstack::operator>>( QString& outParameters )
1511 splitterInfo( mySplit, outParameters );
1519 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
1522 QVBoxLayout* base = new QVBoxLayout( this );
1524 QHBox* top = new QHBox( this );
1525 base->addWidget( top );
1527 myBar = new QtxWorkstackTabBar( top );
1529 QPushButton* close = new QPushButton( top );
1530 close->setPixmap( style().stylePixmap( QStyle::SP_TitleBarCloseButton ) );
1531 close->setAutoDefault( true );
1532 close->setFlat( true );
1535 top->setStretchFactor( myBar, 1 );
1537 myStack = new QWidgetStack( this );
1539 base->addWidget( myStack, 1 );
1541 connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
1542 connect( myBar, SIGNAL( selected( int ) ), this, SLOT( onSelected( int ) ) );
1543 connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
1544 connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
1548 updateActiveState();
1550 qApp->installEventFilter( this );
1556 QtxWorkstackArea::~QtxWorkstackArea()
1558 qApp->removeEventFilter( this );
1562 \return true if area is empty
1564 bool QtxWorkstackArea::isEmpty() const
1567 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !res; ++it )
1568 res = it.data().vis;
1577 void QtxWorkstackArea::insertWidget( QWidget* wid, const int idx )
1582 int pos = myList.find( wid );
1583 if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
1586 myList.removeRef( wid );
1587 pos = idx < 0 ? myList.count() : idx;
1588 myList.insert( QMIN( pos, (int)myList.count() ), wid );
1589 if ( !myInfo.contains( wid ) )
1591 QtxWorkstackChild* child = new QtxWorkstackChild( wid, myStack );
1592 myChild.insert( wid, child );
1593 myInfo.insert( wid, WidgetInfo() );
1594 myInfo[wid].id = generateId();
1595 myInfo[wid].vis = wid->isVisibleTo( wid->parentWidget() );
1597 connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
1598 connect( wid, SIGNAL( destroyed() ), this, SLOT( onWidgetDestroyed() ) );
1599 connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
1600 connect( child, SIGNAL( hided( QtxWorkstackChild* ) ), this, SLOT( onChildHided( QtxWorkstackChild* ) ) );
1601 connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
1602 connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
1607 setWidgetActive( wid );
1612 Creates and shows popup menu for area
1613 \param p - popup position
1615 void QtxWorkstackArea::onContextMenuRequested( QPoint p )
1617 const QtxWorkstackTabBar* bar = ::qt_cast<const QtxWorkstackTabBar*>( sender() );
1622 QTab* tab = myBar->tabAt( tabAt( p ) );
1624 wid = widget( tab->identifier() );
1626 emit contextMenuRequested( wid, p );
1630 SLOT: called when widget added to area is destroyed, removes widget from area
1632 void QtxWorkstackArea::onWidgetDestroyed()
1635 removeWidget( (QWidget*)sender(), false );
1639 Removes widget from area
1641 \param del - auto deleting
1643 void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
1645 if ( !myList.contains( wid ) )
1648 if ( myBar->tab( widgetId( wid ) ) )
1649 myBar->removeTab( myBar->tab( widgetId( wid ) ) );
1650 myStack->removeWidget( child( wid ) );
1652 myList.remove( wid );
1653 myInfo.remove( wid );
1654 myChild.remove( wid );
1658 delete child( wid );
1659 if( myList.isEmpty() )
1669 \return list of visible widgets
1671 QWidgetList QtxWorkstackArea::widgetList() const
1674 for ( QWidgetListIt it( myList ); it.current(); ++it )
1676 if ( widgetVisibility( it.current() ) )
1677 lst.append( it.current() );
1683 \return active widget
1685 QWidget* QtxWorkstackArea::activeWidget() const
1687 return widget( myBar->currentTab() );
1691 Sets widget as active
1694 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
1696 myBar->setCurrentTab( widgetId( wid ) );
1700 \return true if area contains widget
1703 bool QtxWorkstackArea::contains( QWidget* wid ) const
1705 return myList.contains( wid );
1711 void QtxWorkstackArea::show()
1713 QMap<QWidget*, bool> map;
1714 for ( QWidgetListIt it( myList ); it.current(); ++it )
1716 map.insert( it.current(), isBlocked( it.current() ) );
1717 setBlocked( it.current(), true );
1722 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1723 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1729 void QtxWorkstackArea::hide()
1731 QMap<QWidget*, bool> map;
1732 for ( QWidgetListIt it( myList ); it.current(); ++it )
1734 map.insert( it.current(), isBlocked( it.current() ) );
1735 setBlocked( it.current(), true );
1740 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1741 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1745 \return true if area is active
1747 bool QtxWorkstackArea::isActive() const
1749 QtxWorkstack* ws = workstack();
1753 return ws->activeArea() == this;
1757 Update active state of tab bar
1759 void QtxWorkstackArea::updateActiveState()
1761 myBar->setActive( isActive() );
1765 \return corresponding workstack
1767 QtxWorkstack* QtxWorkstackArea::workstack() const
1769 QtxWorkstack* ws = 0;
1770 QWidget* wid = parentWidget();
1771 while ( wid && !ws )
1773 if ( wid->inherits( "QtxWorkstack" ) )
1774 ws = (QtxWorkstack*)wid;
1775 wid = wid->parentWidget();
1783 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
1785 if ( o->isWidgetType() )
1787 QWidget* wid = (QWidget*)o;
1788 if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
1791 while ( !ok && wid && wid != myClose )
1794 wid = wid->parentWidget();
1797 QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
1804 \return rectangle of area in order to draw drop rectangle on area
1806 QRect QtxWorkstackArea::floatRect() const
1808 QRect r = myStack->geometry();
1809 return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
1813 \return rectangle of tab in order to draw drop rectangle on tab
1814 \param idx - tab index
1816 QRect QtxWorkstackArea::floatTab( const int idx ) const
1818 return myBar->tabRect( idx );
1822 \return tab covering point
1825 int QtxWorkstackArea::tabAt( const QPoint& p ) const
1828 for ( int i = 0; i < myBar->count() && idx == -1; i++ )
1830 QRect r = myBar->tabRect( i );
1831 if ( r.isValid() && r.contains( p ) )
1838 Event handler for custom events
1840 void QtxWorkstackArea::customEvent( QCustomEvent* e )
1842 switch ( e->type() )
1844 case ActivateWidget:
1845 emit activated( activeWidget() );
1848 if ( activeWidget() )
1850 if ( !activeWidget()->focusWidget() )
1851 activeWidget()->setFocus();
1853 if ( activeWidget()->focusWidget()->hasFocus()) {
1854 QFocusEvent in(QEvent::FocusIn);
1855 QApplication::sendEvent(this, &in);
1858 activeWidget()->focusWidget()->setFocus();
1863 removeWidget( (QWidget*)e->data() );
1869 Custom focus in event handler
1871 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
1873 QWidget::focusInEvent( e );
1875 emit activated( activeWidget() );
1879 Custom mouse press event handler
1881 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
1883 QWidget::mousePressEvent( e );
1885 emit activated( activeWidget() );
1889 SLOT: called if button close is pressed
1891 void QtxWorkstackArea::onClose()
1893 QWidget* wid = activeWidget();
1899 SLOT: called if tab page is selected
1901 void QtxWorkstackArea::onSelected( int id )
1905 emit activated( activeWidget() );
1909 SLOT: called if active tab page is dragged
1911 void QtxWorkstackArea::onDragActiveTab()
1913 QtxWorkstackChild* c = child( activeWidget() );
1917 new QtxWorkstackDrag( workstack(), c );
1921 SLOT: called on child is destroyed, removes from area
1923 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
1925 QtxWorkstackChild* child = (QtxWorkstackChild*)obj;
1926 myStack->removeWidget( child );
1929 for ( ChildMap::ConstIterator it = myChild.begin(); it != myChild.end() && !wid; ++it )
1931 if ( it.data() == child )
1935 myChild.remove( wid );
1937 QApplication::postEvent( this, new QCustomEvent( (QEvent::Type)RemoveWidget, wid ) );
1941 SLOT: called on child is shown
1943 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
1945 setWidgetShown( c->widget(), true );
1949 SLOT: called on child is hidden
1951 void QtxWorkstackArea::onChildHided( QtxWorkstackChild* c )
1953 setWidgetShown( c->widget(), false );
1957 SLOT: called on child is activated
1959 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
1961 setWidgetActive( c->widget() );
1965 SLOT: called on child caption is changed
1967 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
1969 updateTab( c->widget() );
1973 Raises widget when active tab is changed
1975 void QtxWorkstackArea::updateCurrent()
1977 QMap<QWidget*, bool> map;
1978 for ( QWidgetListIt it( myList ); it.current(); ++it )
1980 map.insert( it.current(), isBlocked( it.current() ) );
1981 setBlocked( it.current(), true );
1984 myStack->raiseWidget( myBar->currentTab() );
1986 for ( QWidgetListIt itr( myList ); itr.current(); ++itr )
1987 setBlocked( itr.current(), map.contains( itr.current() ) ? map[itr.current()] : false );
1992 \param wid - tab widget
1994 void QtxWorkstackArea::updateTab( QWidget* wid )
1996 QTab* tab = myBar->tab( widgetId( wid ) );
2003 QPixmap pix = *wid->icon();
2004 pix.convertFromImage( pix.convertToImage().smoothScale( pix.width(), 16, QImage::ScaleMin ) );
2005 icoSet = QIconSet( pix );
2008 tab->setIconSet( icoSet );
2009 tab->setText( wid->caption() );
2014 \param id - widget id
2016 QWidget* QtxWorkstackArea::widget( const int id ) const
2019 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end() && !wid; ++it )
2021 if ( it.data().id == id )
2031 int QtxWorkstackArea::widgetId( QWidget* wid ) const
2034 if ( myInfo.contains( wid ) )
2035 id = myInfo[wid].id;
2040 \return true if widget is visible
2043 bool QtxWorkstackArea::widgetVisibility( QWidget* wid ) const
2046 if ( myInfo.contains( wid ) )
2047 res = myInfo[wid].vis;
2052 Sets widget as active
2055 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
2057 int id = widgetId( wid );
2061 myBar->setCurrentTab( id );
2067 \param on - new shown state
2069 void QtxWorkstackArea::setWidgetShown( QWidget* wid, const bool on )
2071 if ( isBlocked( wid ) || !myInfo.contains( wid ) || myInfo[wid].vis == on )
2074 myInfo[wid].vis = on;
2081 void QtxWorkstackArea::updateState()
2083 bool updBar = myBar->isUpdatesEnabled();
2084 bool updStk = myStack->isUpdatesEnabled();
2085 myBar->setUpdatesEnabled( false );
2086 myStack->setUpdatesEnabled( false );
2088 bool block = myBar->signalsBlocked();
2089 myBar->blockSignals( true );
2091 QWidget* prev = activeWidget();
2094 for ( QWidgetListIt it( myList ); it.current(); ++it )
2096 QWidget* wid = it.current();
2097 int id = widgetId( wid );
2102 bool vis = widgetVisibility( wid );
2104 if ( myBar->tab( id ) && ( !vis || myBar->indexOf( id ) != idx ) )
2105 myBar->removeTab( myBar->tab( id ) );
2107 if ( !myBar->tab( id ) && vis )
2109 QTab* tab = new QTab( wid->caption() );
2110 myBar->insertTab( tab, idx );
2111 tab->setIdentifier( id );
2116 bool block = isBlocked( wid );
2117 setBlocked( wid, true );
2119 QtxWorkstackChild* cont = child( wid );
2122 myStack->removeWidget( cont );
2123 else if ( !myStack->widget( id ) )
2124 myStack->addWidget( cont, id );
2129 setBlocked( wid, block );
2132 int curId = widgetId( prev );
2133 if ( !myBar->tab( curId ) )
2136 int pos = myList.find( prev );
2137 for ( int i = pos - 1; i >= 0 && !wid; i-- )
2139 if ( widgetVisibility( myList.at( i ) ) )
2140 wid = myList.at( i );
2143 for ( int j = pos + 1; j < (int)myList.count() && !wid; j++ )
2145 if ( widgetVisibility( myList.at( j ) ) )
2146 wid = myList.at( j );
2150 curId = widgetId( wid );
2153 myBar->setCurrentTab( curId );
2155 myBar->blockSignals( block );
2159 myBar->setUpdatesEnabled( updBar );
2160 myStack->setUpdatesEnabled( updStk );
2166 QResizeEvent re( myBar->size(), myBar->size() );
2167 QApplication::sendEvent( myBar, &re );
2172 emit deactivated( this );
2177 if ( prev != activeWidget() )
2178 emit activated( activeWidget() );
2183 \return first unshared widget id
2185 int QtxWorkstackArea::generateId() const
2189 for ( WidgetInfoMap::ConstIterator it = myInfo.begin(); it != myInfo.end(); ++it )
2190 map.insert( it.data().id, 0 );
2193 while ( map.contains( id ) )
2200 \return true if widget is blocked
2203 bool QtxWorkstackArea::isBlocked( QWidget* wid ) const
2205 return myBlock.contains( wid );
2211 \param on - new blocked state
2213 void QtxWorkstackArea::setBlocked( QWidget* wid, const bool on )
2216 myBlock.insert( wid, 0 );
2218 myBlock.remove( wid );
2222 \return child corresponding to widget
2225 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
2227 QtxWorkstackChild* res = 0;
2228 if ( myChild.contains( wid ) )
2236 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent )
2240 myWidget->reparent( this, QPoint( 0, 0 ), myWidget->isVisibleTo( myWidget->parentWidget() ) );
2241 myWidget->installEventFilter( this );
2243 connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
2249 QtxWorkstackChild::~QtxWorkstackChild()
2251 qApp->removeEventFilter( this );
2256 widget()->removeEventFilter( this );
2257 widget()->reparent( 0, QPoint( 0, 0 ), false );
2258 disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
2262 \return corresponding widget
2264 QWidget* QtxWorkstackChild::widget() const
2272 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
2274 if ( o->isWidgetType() )
2276 if ( e->type() == QEvent::CaptionChange || e->type() == QEvent::IconChange )
2277 emit captionChanged( this );
2279 if ( !e->spontaneous() && ( e->type() == QEvent::Show || e->type() == QEvent::ShowToParent ) )
2282 if ( !e->spontaneous() && ( e->type() == QEvent::Hide || e->type() == QEvent::HideToParent ) )
2285 if ( e->type() == QEvent::FocusIn )
2286 emit activated( this );
2288 return QHBox::eventFilter( o, e );
2292 SLOT: called on object is destroyed
2294 void QtxWorkstackChild::onDestroyed( QObject* obj )
2296 if ( obj != widget() )
2304 Custom child event handler
2306 void QtxWorkstackChild::childEvent( QChildEvent* e )
2308 if ( e->type() == QEvent::ChildRemoved && e->child() == widget() )
2313 QHBox::childEvent( e );
2319 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
2320 : QTabBar( parent ),
2328 QtxWorkstackTabBar::~QtxWorkstackTabBar()
2333 Sets tab bar as active or inactive
2334 \param on - new active state
2336 void QtxWorkstackTabBar::setActive( const bool on )
2338 QFont aFont = font();
2339 aFont.setUnderline( on );
2340 QColorGroup* aColGrp = new QColorGroup();
2341 QPalette aPal = palette();
2343 aPal.setColor( QColorGroup::HighlightedText, aColGrp->foreground() );
2344 aPal.setColor( QColorGroup::Highlight, colorGroup().dark().light( DARK_COLOR_LIGHT ) );
2348 aPal.setColor( QColorGroup::HighlightedText, aColGrp->highlightedText() );
2349 aPal.setColor( QColorGroup::Highlight, aColGrp->highlight() );
2358 \return tab rectangle
2359 \param idx - tab index
2361 QRect QtxWorkstackTabBar::tabRect( const int idx ) const
2364 QTab* t = tabAt( idx );
2368 r.setLeft( QMAX( r.left(), 0 ) );
2370 int x1 = tabAt( 0 )->rect().left();
2371 int x2 = tabAt( count() - 1 )->rect().right();
2374 if ( QABS( x2 - x1 ) > width() )
2375 #if defined QT_VERSION && QT_VERSION >= 0x30300
2376 bw = 2 * style().pixelMetric( QStyle::PM_TabBarScrollButtonWidth, this );
2381 int limit = width() - bw;
2382 r.setRight( QMIN( r.right(), limit ) );
2384 r = QRect( mapToGlobal( r.topLeft() ), r.size() );
2390 Custom mouse move event handler
2392 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
2394 if ( myId != -1 && !tab( myId )->rect().contains( e->pos() ) )
2397 emit dragActiveTab();
2400 QTabBar::mouseMoveEvent( e );
2404 Custom mouse press event handler
2406 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
2408 QTabBar::mousePressEvent( e );
2410 if ( e->button() == LeftButton )
2411 myId = currentTab();
2415 Custom mouse release event handler
2417 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
2419 QTabBar::mouseReleaseEvent( e );
2423 if ( e->button() == RightButton )
2424 emit contextMenuRequested( e->globalPos() );
2428 Custom context menu event handler
2430 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
2432 if ( e->reason() != QContextMenuEvent::Mouse )
2433 emit contextMenuRequested( e->globalPos() );
2437 Draws label of tab bar
2439 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
2441 if ( currentTab() != t->identifier() )
2443 QFont fnt = p->font();
2444 fnt.setUnderline( false );
2447 QTabBar::paintLabel( p, br, t, has_focus );
2453 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
2461 qApp->installEventFilter( this );
2467 QtxWorkstackDrag::~QtxWorkstackDrag()
2469 qApp->removeEventFilter( this );
2477 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
2479 switch ( e->type() )
2481 case QEvent::MouseMove:
2482 updateTarget( ((QMouseEvent*)e)->globalPos() );
2484 case QEvent::MouseButtonRelease:
2497 Updates internal field with widget-target for dropping
2498 \param p - current point of dragging
2500 void QtxWorkstackDrag::updateTarget( const QPoint& p )
2503 QtxWorkstackArea* area = detectTarget( p, tab );
2504 setTarget( area, tab );
2508 \return target area for dropping by point
2509 \param p - current point of dragging
2510 \param tab - index of tab to dropping
2512 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
2517 QtxWorkstackArea* area = myWS->areaAt( p );
2519 tab = area->tabAt( p );
2524 Changes target area for dropping
2525 \param area - new target area
2526 \param tab - tab index
2528 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
2530 if ( !area || ( myArea == area && tab == myTab ) )
2546 Called on widget drop, inserts dropped widget to area
2548 void QtxWorkstackDrag::dropWidget()
2551 myArea->insertWidget( myChild->widget(), myTab );
2557 void QtxWorkstackDrag::drawRect()
2559 if ( !myPainter || !myArea )
2562 QRect r = myArea->floatRect();
2563 int m = myPainter->pen().width();
2565 r.setTop( r.top() + m + 2 );
2566 r.setLeft( r.left() + m + 2 );
2567 r.setRight( r.right() - m - 2 );
2568 r.setBottom( r.bottom() - m - 2 );
2570 myPainter->drawRect( r );
2572 QRect tr = myArea->floatTab( myTab );
2573 tr.setTop( tr.top() + m );
2574 tr.setLeft( tr.left() + m );
2575 tr.setRight( tr.right() - m );
2576 tr.setBottom( tr.bottom() - m );
2578 myPainter->drawRect( tr );
2582 Deletes internal painter
2584 void QtxWorkstackDrag::endDrawRect()
2591 Initialize internal painter
2593 void QtxWorkstackDrag::startDrawRect()
2598 int scr = QApplication::desktop()->screenNumber( (QWidget*)this );
2599 QWidget* paint_on = QApplication::desktop()->screen( scr );
2601 myPainter = new QPainter( paint_on, true );
2602 myPainter->setPen( QPen( gray, 3 ) );
2603 myPainter->setRasterOp( XorROP );