Salome HOME
Unicode support: correct handling of unicode on GUI level
[modules/gui.git] / src / Qtx / QtxWorkstack.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 // File:      QtxWorkstack.cxx
24 // Author:    Sergey TELKOV
25 //
26 #include "QtxWorkstack.h"
27
28 #include "QtxAction.h"
29
30 #include <QMenu>
31 #include <QStyle>
32 #include <QRegExp>
33 #include <QLayout>
34 #include <QPainter>
35 #include <QDataStream>
36 #include <QFocusEvent>
37 #include <QMouseEvent>
38 #include <QRubberBand>
39 #include <QApplication>
40 #include <QStyleOption>
41 #include <QInputDialog>
42 #include <QStackedWidget>
43 #include <QAbstractButton>
44
45 /*!
46   \class QtxWorkstackArea::WidgetEvent
47   \internal
48   \brief Internal class used to forward child widgets events to the workarea
49 */
50
51 class QtxWorkstackArea::WidgetEvent : public QEvent
52 {
53 public:
54   WidgetEvent( Type t, QtxWorkstackChild* w = 0 ) : QEvent( t ), myChild( w ) {};
55
56   QtxWorkstackChild* child() const { return myChild; }
57
58 private:
59   QtxWorkstackChild* myChild;   // event receiver widget
60 };
61
62 /*!
63   \class QtxWorkstackArea::RestoreEvent
64   \internal
65   \brief Internal class used to forward restore info events to the workarea
66 */
67
68 class QtxWorkstackArea::RestoreEvent : public QtxWorkstackArea::WidgetEvent
69 {
70 public:
71   RestoreEvent( Type t, int id, int f, QtxWorkstackChild* w )
72   : WidgetEvent( t, w ), myId( id ), myFlags( f ) {};
73
74   int                id() const { return myId; }
75   int                flags() const { return myFlags; }
76
77 private:
78   int                myId;
79   int                myFlags;
80 };
81
82 /*!
83   \class QtxWorkstackDrag
84   \internal
85   \brief Workstack drag object
86 */
87
88 /*!
89   \brief Constructor.
90   \param ws parent workstack
91   \param child child widget container
92 */
93 QtxWorkstackDrag::QtxWorkstackDrag( QtxWorkstack* ws, QtxWorkstackChild* child )
94 : QObject( 0 ),
95   myWS( ws ),
96   myChild( child ),
97   myTab( -1 ),
98   myArea( 0 ),
99   myTabRect( 0 ),
100   myAreaRect( 0 )
101 {
102   QApplication::instance()->installEventFilter( this );
103 }
104
105 /*!
106   \brief Destructor.
107 */
108 QtxWorkstackDrag::~QtxWorkstackDrag()
109 {
110   QApplication::instance()->removeEventFilter( this );
111
112   endDrawRect();
113 }
114
115 /*!
116   \brief Custom event filter.
117   \param o event receiver widget
118   \param e event
119   \return \c true if event should be filtered (stop further processing)
120 */
121 bool QtxWorkstackDrag::eventFilter( QObject*, QEvent* e )
122 {
123   switch ( e->type() )
124   {
125   case QEvent::MouseMove:
126     updateTarget( ((QMouseEvent*)e)->globalPos() );
127     break;
128   case QEvent::MouseButtonRelease:
129     drawRect();
130     endDrawRect();
131     dropWidget();
132     deleteLater();
133     break;
134   default:
135     return false;
136   }
137   return true;
138 }
139
140 /*!
141   \brief Detect and set dropping target widget.
142   \param p current dragging position
143 */
144 void QtxWorkstackDrag::updateTarget( const QPoint& p )
145 {
146   int tab = -1;
147   QtxWorkstackArea* area = detectTarget( p, tab );
148   setTarget( area, tab );
149 }
150
151 /*!
152   \brief Detect dropping target.
153   \param p current dragging position
154   \param tab resulting target tab page index
155   \return target workarea or 0 if there is no correct drop target
156 */
157 QtxWorkstackArea* QtxWorkstackDrag::detectTarget( const QPoint& p, int& tab ) const
158 {
159   if ( p.isNull() )
160     return 0;
161
162   QtxWorkstackArea* area = myWS->areaAt( p );
163   if ( area )
164     tab = area->tabAt( p );
165   return area;
166 }
167
168 /*!
169   \brief Set dropping target.
170   \param area new target workarea
171   \param tab target workarea's tab page index
172 */
173 void QtxWorkstackDrag::setTarget( QtxWorkstackArea* area, const int tab )
174 {
175   if ( !area || ( myArea == area && tab == myTab ) )
176     return;
177
178   startDrawRect();
179
180   if ( myArea )
181     drawRect();
182
183   myTab = tab;
184   myArea = area;
185
186   if ( myArea )
187     drawRect();
188 }
189
190 /*!
191   \brief Called when drop operation is finished.
192
193   Inserts dropped widget to the target workarea.
194 */
195 void QtxWorkstackDrag::dropWidget()
196 {
197   if ( myArea )
198     myArea->insertChild( myChild, myTab );
199 }
200
201 /*!
202   \brief Draw floating rectangle.
203 */
204 void QtxWorkstackDrag::drawRect()
205 {
206   if ( !myArea )
207     return;
208
209   QRect r = myArea->floatRect();
210   int m = 2;
211
212   r.setTop( r.top() + m + 2 );
213   r.setLeft( r.left() + m + 2 );
214   r.setRight( r.right() - m - 2 );
215   r.setBottom( r.bottom() - m - 2 );
216
217   if ( myAreaRect )
218   {
219     myAreaRect->setGeometry( r );
220     myAreaRect->setVisible( r.isValid() );
221   }
222
223   QRect tr = myArea->floatTab( myTab );
224
225   tr.setTop( tr.top() + m );
226   tr.setLeft( tr.left() + m );
227   tr.setRight( tr.right() - m );
228   tr.setBottom( tr.bottom() - m );
229
230   if ( myTabRect )
231   {
232     myTabRect->setGeometry( tr );
233     myTabRect->setVisible( tr.isValid() );
234   }
235 }
236
237 /*!
238   \brief Delete rubber band on the end on the dragging operation.
239 */
240 void QtxWorkstackDrag::endDrawRect()
241 {
242   delete myAreaRect;
243   myAreaRect = 0;
244
245   delete myTabRect;
246   myTabRect = 0;
247 }
248
249 /*!
250   \brief Create rubber band to be drawn on the dragging operation.
251 */
252 void QtxWorkstackDrag::startDrawRect()
253 {
254   if ( !myTabRect )
255     myTabRect = new QRubberBand( QRubberBand::Rectangle );
256
257   myTabRect->hide();
258
259   if ( !myAreaRect )
260     myAreaRect = new QRubberBand( QRubberBand::Rectangle );
261
262   myAreaRect->hide();
263 }
264
265
266 /*
267   \class CloseButton
268   \brief Workstack area close button.
269   \internal
270 */
271 class CloseButton : public QAbstractButton
272 {
273 public:
274   CloseButton( QWidget* );
275
276   QSize        sizeHint() const;
277   QSize        minimumSizeHint() const;
278
279   void enterEvent( QEvent* );
280   void leaveEvent( QEvent* );
281   void paintEvent( QPaintEvent* );
282 };
283
284 /*!
285   \brief Constructor
286   \internal
287   \param parent parent widget
288 */
289 CloseButton::CloseButton( QWidget* parent )
290 : QAbstractButton( parent )
291 {
292  setFocusPolicy( Qt::NoFocus );
293 }
294
295 /*!
296   \brief Get appropriate size for the button.
297   \internal
298   \return size value
299 */
300 QSize CloseButton::sizeHint() const
301 {
302   ensurePolished();
303   int dim = 0;
304   if( !icon().isNull() )
305   {
306     const QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ),
307                                       QIcon::Normal );
308     dim = qMax( pm.width(), pm.height() );
309   }
310   return QSize( dim + 4, dim + 4 );
311 }
312
313 /*!
314   \brief Get minimum appropriate size for the button.
315   \internal
316   \return minimum size value
317 */
318 QSize CloseButton::minimumSizeHint() const
319 {
320   return sizeHint();
321 }
322
323 /*!
324   \brief Process mouse enter event.
325   \internal
326   \param event mouse enter event
327 */
328 void CloseButton::enterEvent( QEvent *event )
329 {
330   if ( isEnabled() )
331     update();
332   QAbstractButton::enterEvent( event );
333 }
334
335 /*!
336   \brief Process mouse leave event.
337   \internal
338   \param event mouse leave event
339 */
340 void CloseButton::leaveEvent( QEvent *event )
341 {
342   if( isEnabled() )
343     update();
344   QAbstractButton::leaveEvent( event );
345 }
346
347 /*!
348   \brief Process paint event.
349   \internal
350   \param event paint event
351 */
352 void CloseButton::paintEvent( QPaintEvent* )
353 {
354   QPainter p( this );
355
356   QRect r = rect();
357   QStyleOption opt;
358   opt.init( this );
359   opt.state |= QStyle::State_AutoRaise;
360   if ( isEnabled() && underMouse() && !isChecked() && !isDown() )
361     opt.state |= QStyle::State_Raised;
362   if ( isChecked() )
363     opt.state |= QStyle::State_On;
364   if ( isDown() )
365     opt.state |= QStyle::State_Sunken;
366   style()->drawPrimitive( QStyle::PE_PanelButtonTool, &opt, &p, this );
367
368   int shiftHorizontal = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftHorizontal, &opt, this ) : 0;
369   int shiftVertical = opt.state & QStyle::State_Sunken ? style()->pixelMetric( QStyle::PM_ButtonShiftVertical, &opt, this ) : 0;
370
371   r.adjust( 2, 2, -2, -2 );
372   r.translate( shiftHorizontal, shiftVertical );
373
374   QPixmap pm = icon().pixmap( style()->pixelMetric( QStyle::PM_SmallIconSize ), isEnabled() ?
375                               underMouse() ? QIcon::Active : QIcon::Normal
376                               : QIcon::Disabled,
377                               isDown() ? QIcon::On : QIcon::Off );
378   style()->drawItemPixmap( &p, r, Qt::AlignCenter, pm );
379 }
380
381
382 /*!
383   \class QtxWorkstackSplitter
384   \internal
385   \brief Workstack splitter.
386 */
387
388 /*!
389   \brief Constructor.
390   \param parent parent widget
391 */
392 QtxWorkstackSplitter::QtxWorkstackSplitter( QWidget* parent )
393 : QSplitter( parent )
394 {
395   setChildrenCollapsible( false );
396 }
397
398 /*!
399   \brief Destructor.
400 */
401 QtxWorkstackSplitter::~QtxWorkstackSplitter()
402 {
403 }
404
405 /*!
406   \brief Get parent workstack
407   \return workstack owning this workarea
408 */
409 QtxWorkstack* QtxWorkstackSplitter::workstack() const
410 {
411   QtxWorkstack* ws = 0;
412   QWidget* wid = parentWidget();
413   while ( wid && !ws )
414   {
415     ws = ::qobject_cast<QtxWorkstack*>( wid );
416     wid = wid->parentWidget();
417   }
418   return ws;
419 }
420
421 /*!
422   \brief Save the widget area configuration into data stream.
423 */
424 void QtxWorkstackSplitter::saveState( QDataStream& stream ) const
425 {
426   stream << QtxWorkstack::SplitMarker;
427
428   uchar flags = 0;
429   if ( orientation() == Qt::Horizontal )
430     flags |= QtxWorkstack::Horizontal;
431
432   stream << flags;
433   stream << count();
434
435   QList<int> sz = sizes();
436   for ( QList<int>::const_iterator it = sz.begin(); it != sz.end(); ++it )
437     stream << *it;
438
439   for ( int i = 0; i < count(); i++ )
440   {
441     QWidget* wid = widget( i );
442     QtxWorkstackSplitter* split = ::qobject_cast<QtxWorkstackSplitter*>( wid );
443     if ( split )
444       split->saveState( stream );
445     else
446     {
447       QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( wid );
448       if ( area )
449               area->saveState( stream );
450     }
451   }
452 }
453
454 /*!
455   \brief Restore the widget area configuration from data stream info.
456   \return \c true in successful case.
457 */
458 bool QtxWorkstackSplitter::restoreState( QDataStream& stream, QMap<QString, QtxWorkstackChild*>& map )
459 {
460   int num = 0;
461   uchar flags = 0;
462
463   stream >> flags;
464   stream >> num;
465
466   setOrientation( flags & QtxWorkstack::Horizontal ? Qt::Horizontal : Qt::Vertical );
467
468   QList<int> sz;
469   for ( int s = 0; s < num; s++ )
470   {
471     int sn = 0;
472     stream >> sn;
473     sz.append( sn );
474   }
475
476   bool ok = true;
477   for ( int i = 0; i < num && ok; i++ )
478   {
479     int marker;
480     stream >> marker;
481
482     if ( stream.status() != QDataStream::Ok )
483       return false;
484
485     if ( marker == QtxWorkstack::SplitMarker )
486     {
487       QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
488       addWidget( split );
489       split->setVisible( true );
490
491       ok = split->restoreState( stream, map );
492     }
493     else if ( marker == QtxWorkstack::AreaMarker )
494     {
495       QtxWorkstack* ws = workstack();
496       QtxWorkstackArea* area = ws->createArea( this );
497       addWidget( area );
498       area->setVisible( true );
499
500       ok = area->restoreState( stream, map );
501     }
502     else
503       return false;
504   }
505
506   if ( ok )
507     setSizes( sz );
508
509   return ok;
510 }
511
512 /*!
513   \class QtxWorkstackArea
514   \internal
515   \brief Workstack widget workarea.
516 */
517
518 /*!
519   \brief Constructor.
520   \param parent parent widget
521 */
522 QtxWorkstackArea::QtxWorkstackArea( QWidget* parent )
523 : QFrame( parent )
524 {
525   setFrameStyle( QFrame::Panel | QFrame::Sunken );
526
527   QVBoxLayout* base = new QVBoxLayout( this );
528   base->setMargin( frameWidth() );
529   base->setSpacing( 0 );
530
531   myTop = new QWidget( this );
532   base->addWidget( myTop );
533
534   QHBoxLayout* tl = new QHBoxLayout( myTop );
535   tl->setMargin( 0 );
536
537   myBar = new QtxWorkstackTabBar( myTop );
538   tl->addWidget( myBar, 1 );
539
540   CloseButton* close = new CloseButton( myTop );
541   close->setIcon( style()->standardIcon( QStyle::SP_TitleBarCloseButton ) );
542   myClose = close;
543   tl->addWidget( myClose );
544
545   myStack = new QStackedWidget( this );
546
547   base->addWidget( myStack, 1 );
548
549   connect( myClose, SIGNAL( clicked() ), this, SLOT( onClose() ) );
550   connect( myBar, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
551   connect( myBar, SIGNAL( dragActiveTab() ), this, SLOT( onDragActiveTab() ) );
552   connect( myBar, SIGNAL( contextMenuRequested( QPoint ) ), this, SLOT( onContextMenuRequested( QPoint ) ) );
553
554   updateState();
555
556   updateActiveState();
557
558   QApplication::instance()->installEventFilter( this );
559 }
560
561 /*!
562   \brief Destructor.
563 */
564 QtxWorkstackArea::~QtxWorkstackArea()
565 {
566   QApplication::instance()->removeEventFilter( this );
567 }
568
569 /*!
570   \brief Check if workarea contains any widgets.
571   \return \c true if area is null (havn't any child widgets)
572 */
573 bool QtxWorkstackArea::isNull() const
574 {
575   return myList.isEmpty();
576 }
577
578 /*!
579   \brief Check if workarea contains visible widgets.
580   \return \c true if area is empty (all child widgets are removed or now shown)
581 */
582 bool QtxWorkstackArea::isEmpty() const
583 {
584   bool res = false;
585   for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !res; ++it )
586     res = (*it)->visibility();
587   return !res;
588 }
589
590 /*!
591   \brief Add widget to the workarea.
592   \param wid widget to be added
593   \param idx position in the area widget to be added to
594   \param f widget flags
595   \return child widget container object (or 0 if index is invalid)
596 */
597 QtxWorkstackChild* QtxWorkstackArea::insertWidget( QWidget* wid, const int idx, Qt::WindowFlags f )
598 {
599   if ( !wid )
600     return 0;
601
602   QtxWorkstackChild* c = child( wid );
603   if ( !c )
604     c = new QtxWorkstackChild( wid, myStack, f );
605
606   insertChild( c, idx );
607
608   return c;
609 }
610
611 void QtxWorkstackArea::insertChild( QtxWorkstackChild* child, const int idx )
612 {
613   if ( !child )
614     return;
615
616   QtxWorkstackArea* a = child->area();
617   if ( a && a != this )
618     a->removeChild( child, false );
619
620   int pos = myList.indexOf( child );
621   if ( pos != -1 && ( pos == idx || ( idx < 0 && pos == (int)myList.count() - 1 ) ) )
622     return;
623
624   bool found = myList.contains( child );
625
626   myList.removeAll( child );
627   pos = idx < 0 ? myList.count() : idx;
628   myList.insert( qMin( pos, (int)myList.count() ), child );
629
630   if ( !found )
631   {
632     bool hasId = false;
633     for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !hasId; ++it )
634       hasId = (*it)->id() == child->id();
635
636     if ( hasId || child->id() < 0 )
637       child->setId( generateId() );
638
639     connect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
640     connect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
641     connect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
642     connect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
643     connect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
644   }
645
646   updateState();
647
648   setWidgetActive( child->widget() );
649   child->widget()->setFocus();
650 }
651
652 /*!
653   \brief Create and show popup menu for the area.
654   \param p mouse pointer position at which popup menu should be shown
655 */
656 void QtxWorkstackArea::onContextMenuRequested( QPoint p )
657 {
658   const QtxWorkstackTabBar* bar = ::qobject_cast<const QtxWorkstackTabBar*>( sender() );
659   if ( !bar )
660     return;
661
662   QWidget* wid = 0;
663   int idx = tabAt( p );
664   if ( idx != -1 )
665     wid = widget( myBar->tabId( idx ) );
666
667   emit contextMenuRequested( wid, p );
668 }
669
670 /*!
671   \brief Remove widget from workarea.
672   \param wid widget to be removed
673   \param del if \c true the widget should be also deleted
674 */
675 void QtxWorkstackArea::removeWidget( QWidget* wid, const bool del )
676 {
677   removeChild( child( wid ), del );
678 }
679
680 /*!
681   \brief Remove child from workarea.
682   \param child child to be removed
683   \param del if \c true the widget should be also deleted
684 */
685 void QtxWorkstackArea::removeChild( QtxWorkstackChild* child, const bool del )
686 {
687   if ( !myList.contains( child ) )
688     return;
689
690   myStack->removeWidget( child );
691
692   if ( myBar->indexOf( child->id() ) != -1 )
693     myBar->removeTab( myBar->indexOf( child->id() ) );
694
695   myList.removeAll( child );
696
697   if ( del )
698     delete child;
699   else if ( child->widget() )
700   {
701     disconnect( child, SIGNAL( destroyed( QObject* ) ), this, SLOT( onChildDestroyed( QObject* ) ) );
702     disconnect( child, SIGNAL( shown( QtxWorkstackChild* ) ), this, SLOT( onChildShown( QtxWorkstackChild* ) ) );
703     disconnect( child, SIGNAL( hidden( QtxWorkstackChild* ) ), this, SLOT( onChildHidden( QtxWorkstackChild* ) ) );
704     disconnect( child, SIGNAL( activated( QtxWorkstackChild* ) ), this, SLOT( onChildActivated( QtxWorkstackChild* ) ) );
705     disconnect( child, SIGNAL( captionChanged( QtxWorkstackChild* ) ), this, SLOT( onChildCaptionChanged( QtxWorkstackChild* ) ) );
706   }
707
708   if ( isNull() )
709     deleteLater();
710   else
711     updateState();
712 }
713
714 QList<QtxWorkstackChild*> QtxWorkstackArea::childList() const
715 {
716   return myList;
717 }
718
719 /*!
720   \brief Get all visible child widgets.
721   \return list of visible child widgets
722 */
723 QWidgetList QtxWorkstackArea::widgetList() const
724 {
725   QWidgetList lst;
726   for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
727   {
728     QtxWorkstackChild* c = *it;
729     if ( c->visibility() )
730       lst.append( c->widget() );
731   }
732   return lst;
733 }
734
735 /*!
736   \brief Get active child widget.
737   \return active widget
738 */
739 QWidget* QtxWorkstackArea::activeWidget() const
740 {
741   return widget( myBar->tabId( myBar->currentIndex() ) );
742 }
743
744 /*!
745   \brief Set active widget.
746   \param wid widget to be made active
747 */
748 void QtxWorkstackArea::setActiveWidget( QWidget* wid )
749 {
750   myBar->setCurrentIndex( myBar->indexOf( widgetId( wid ) ) );
751 }
752
753 /*!
754   \brief Check if area owns the specified widget.
755   \param wid widget to be checked
756   \return \c true if area contains widget
757 */
758 bool QtxWorkstackArea::contains( QWidget* wid ) const
759 {
760   return child( wid );
761 }
762
763 /*!
764   \brief Check if workarea is active.
765   \return \c true if area is active
766 */
767 bool QtxWorkstackArea::isActive() const
768 {
769   QtxWorkstack* ws = workstack();
770   if ( !ws )
771     return false;
772
773   return ws->activeArea() == this;
774 }
775
776 /*!
777   \brief Update active tab bar state (active/inactive).
778 */
779 void QtxWorkstackArea::updateActiveState()
780 {
781   myBar->setActive( isActive() );
782 }
783
784 /*!
785   \brief Get parent workstack
786   \return workstack owning this workarea
787 */
788 QtxWorkstack* QtxWorkstackArea::workstack() const
789 {
790   QtxWorkstack* ws = 0;
791   QWidget* wid = parentWidget();
792   while ( wid && !ws )
793   {
794     ws = ::qobject_cast<QtxWorkstack*>( wid );
795     wid = wid->parentWidget();
796   }
797   return ws;
798 }
799
800 /*!
801   \brief Custom event filter.
802
803   Process events from child widgets.
804
805   \param o event receiver widget
806   \param e event
807   \return \c true if event should be filtered (stop further processing)
808 */
809 bool QtxWorkstackArea::eventFilter( QObject* o, QEvent* e )
810 {
811   if ( o->isWidgetType() )
812   {
813     QWidget* wid = (QWidget*)o;
814     if ( e->type() == QEvent::FocusIn || e->type() == QEvent::MouseButtonPress )
815     {
816       bool ok = false;
817       while ( !ok && wid && wid != myClose )
818       {
819         ok = wid == this;
820         wid = wid->parentWidget();
821       }
822       if ( ok )
823         QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)( e->type() == QEvent::FocusIn ? ActivateWidget : FocusWidget ) ) );
824     }
825   }
826   return false;
827 }
828
829 /*!
830   \brief Save the own widgets configuration into data stream.
831 */
832 void QtxWorkstackArea::saveState( QDataStream& stream ) const
833 {
834   stream << QtxWorkstack::AreaMarker;
835   stream << myList.count();
836   stream << myBar->tabId( myBar->currentIndex() );
837   for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
838   {
839     QtxWorkstackChild* c = *it;
840
841     stream << QtxWorkstack::WidgetMarker;
842
843     stream << c->widget()->objectName();
844     stream << c->id();
845
846     uchar flags = 0;
847     if ( c->visibility() )
848       flags |= QtxWorkstack::Visible;
849
850     stream << flags;
851   }
852 }
853
854 /*!
855   \brief Restore the widgets configuration from data stream info.
856   \return \c true in successful case.
857 */
858 bool QtxWorkstackArea::restoreState( QDataStream& stream, QMap<QString, QtxWorkstackChild*>& map )
859 {
860   int num = 0;
861   int cur = -1;
862
863   stream >> num;
864   stream >> cur;
865
866   QtxWorkstackChild* curChild = 0;
867   for ( int i = 0; i < num; i++ )
868   {
869     int marker;
870     stream >> marker;
871
872     if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::WidgetMarker )
873       return false;
874
875     QString name;
876     stream >> name;
877
878     int id = -1;
879     stream >> id;
880
881     uchar flags = 0;
882     stream >> flags;
883
884     QtxWorkstackChild* c = map.contains( name ) ? map[name] : 0;
885     if ( !c )
886     {
887       qWarning( "QtxWorkstack: Restored child widget \"%s\" not found.", (const char*)name.toUtf8() );
888       return false;
889     }
890
891     map.remove( name );
892
893     if ( id == cur )
894       curChild = c;
895
896     QApplication::postEvent( this, new RestoreEvent( (QEvent::Type)RestoreWidget, id, flags, c ) );
897   }
898
899   if ( curChild )
900     QApplication::postEvent( this, new WidgetEvent( (QEvent::Type)MakeCurrent, curChild ) );
901
902   return true;
903 }
904
905 /*!
906   \brief Show/Hide tab bar.
907 */
908 void QtxWorkstackArea::showTabBar( bool visible)
909 {
910   myTop->setVisible(visible);
911   myBar->setVisible(visible);
912 }
913
914
915 /*!
916   \brief Get rectangle to be drawn when highlighting drop area.
917   \return area drop rectangle
918 */
919 QRect QtxWorkstackArea::floatRect() const
920 {
921   QRect r = myStack->geometry();
922   return QRect( mapToGlobal( r.topLeft() ), mapToGlobal( r.bottomRight() ) );
923 }
924
925 /*!
926   \brief Get rectangle to be drawn when highlighting drop area on tab bar.
927   \param idx tab index
928   \return tab bar drop rectrangle
929 */
930 QRect QtxWorkstackArea::floatTab( const int idx ) const
931 {
932   QRect r = myBar->tabRect( idx );
933   return QRect( myBar->mapToGlobal( r.topLeft() ), r.size() );
934 }
935
936 /*!
937   \brief Get tab index by point.
938   \param p point
939   \return tab covering point or -1 if there is no tab covering point
940 */
941 int QtxWorkstackArea::tabAt( const QPoint& pnt ) const
942 {
943   int idx = -1;
944   QPoint p = myBar->mapFromGlobal( pnt );
945   for ( int i = 0; i < myBar->count() && idx == -1; i++ )
946   {
947     QRect r = myBar->tabRect( i );
948     if ( r.isValid() && r.contains( p ) )
949       idx = i;
950   }
951   return idx;
952 }
953
954 /*!
955   \brief Event handler for custom events.
956   \param e custom event
957 */
958 void QtxWorkstackArea::customEvent( QEvent* e )
959 {
960   WidgetEvent* we = (WidgetEvent*)e;
961
962   switch ( we->type() )
963   {
964   case ActivateWidget:
965     myBar->updateActiveState();
966     // IMN 27/03/2015: This workaround caused by the bug INT PAL 0052623: OCC view blinking when
967     // using polyline sketcher which is reproduced on Unix systems with qt-4.8.4.
968     myStack->setUpdatesEnabled( false );
969     updateCurrent();
970     myStack->setUpdatesEnabled( true );
971     emit activated( activeWidget() );
972     break;
973   case FocusWidget:
974     if ( activeWidget() )
975     {
976       if ( !activeWidget()->focusWidget() )
977         activeWidget()->setFocus();
978       else
979       {
980         if ( activeWidget()->focusWidget()->hasFocus() )
981         {
982           QFocusEvent in( QEvent::FocusIn );
983                 QApplication::sendEvent( this, &in );
984               }
985         else
986         {
987           activeWidget()->focusWidget()->setFocus();
988                 myBar->updateActiveState();
989               }
990       }
991     }
992     break;
993   case MakeCurrent:
994     if ( we->child()->widget() )
995       setActiveWidget( we->child()->widget() );
996     break;
997   case RestoreWidget:
998     if ( we->child() )
999     {
1000       QtxWorkstackChild* c = we->child();
1001       RestoreEvent* re = (RestoreEvent*)we;
1002       if ( c->widget() )
1003         c->widget()->setVisible( re->flags() & QtxWorkstack::Visible );
1004       c->setId( re->id() );
1005       insertChild( c );
1006     }
1007     break;
1008   default:
1009     break;
1010   }
1011 }
1012
1013 /*!
1014   \brief Customize focus in event handler.
1015   \param e focus in event
1016 */
1017 void QtxWorkstackArea::focusInEvent( QFocusEvent* e )
1018 {
1019   QFrame::focusInEvent( e );
1020
1021   myBar->updateActiveState();
1022
1023   emit activated( activeWidget() );
1024 }
1025
1026 /*!
1027   \brief Customize mouse press event handler.
1028   \param e mouse press event
1029 */
1030 void QtxWorkstackArea::mousePressEvent( QMouseEvent* e )
1031 {
1032   QFrame::mousePressEvent( e );
1033
1034   emit activated( activeWidget() );
1035 }
1036
1037 /*!
1038   \brief Called when user presses "Close" button.
1039 */
1040 void QtxWorkstackArea::onClose()
1041 {
1042   QWidget* wid = activeWidget();
1043   if ( wid )
1044     wid->close();
1045 }
1046
1047 /*!
1048   \brief Called when user selects any tab page.
1049   \param idx tab page index (not used)
1050 */
1051 void QtxWorkstackArea::onCurrentChanged( int /*idx*/ )
1052 {
1053   updateCurrent();
1054
1055   emit activated( activeWidget() );
1056 }
1057
1058 /*!
1059   \brief Called when user starts tab page dragging.
1060 */
1061 void QtxWorkstackArea::onDragActiveTab()
1062 {
1063   QtxWorkstackChild* c = child( activeWidget() );
1064   if ( !c )
1065     return;
1066
1067   new QtxWorkstackDrag( workstack(), c );
1068 }
1069
1070 /*!
1071   \brief Called when area's child widget container is destroyed.
1072   \param obj widget container being destroyed
1073 */
1074 void QtxWorkstackArea::onChildDestroyed( QObject* obj )
1075 {
1076   removeChild( (QtxWorkstackChild*)obj, false );
1077 }
1078
1079 /*!
1080   \brief Called when child widget container is shown.
1081   \param c child widget container being shown
1082 */
1083 void QtxWorkstackArea::onChildShown( QtxWorkstackChild* c )
1084 {
1085   updateState();
1086 }
1087
1088 /*!
1089   \brief Called when child widget container is hidden.
1090   \param c child widget container being hidden
1091 */
1092 void QtxWorkstackArea::onChildHidden( QtxWorkstackChild* c )
1093 {
1094   updateState();
1095 }
1096
1097 /*!
1098   \brief Called when child widget container is activated.
1099   \param c child widget container being activated
1100 */
1101 void QtxWorkstackArea::onChildActivated( QtxWorkstackChild* c )
1102 {
1103   setWidgetActive( c->widget() );
1104 }
1105
1106 /*!
1107   \brief Called when child widget container's title is changed.
1108   \param c child widget container which title is changed
1109 */
1110 void QtxWorkstackArea::onChildCaptionChanged( QtxWorkstackChild* c )
1111 {
1112   updateTab( c->widget() );
1113 }
1114
1115 /*!
1116   \brief Update current child widget container.
1117
1118   Raises widget when active tab page is changed.
1119 */
1120 void QtxWorkstackArea::updateCurrent()
1121 {
1122   QWidget* cur = child( myBar->tabId( myBar->currentIndex() ) );
1123   if ( cur )
1124     myStack->setCurrentWidget( cur );
1125 }
1126
1127 /*!
1128   \brief Update tab bar.
1129   \param wid tab page widget
1130 */
1131 void QtxWorkstackArea::updateTab( QWidget* wid )
1132 {
1133   int idx = myBar->indexOf( widgetId( wid ) );
1134   if ( idx < 0 )
1135     return;
1136
1137   myBar->setTabIcon( idx, wid->windowIcon() );
1138   myBar->setTabText( idx, wid->windowTitle() );
1139 }
1140
1141 /*!
1142   \brief Get child widget by specified identifier.
1143   \param id widget ID
1144   \return widget or 0, if identifier in invalid
1145 */
1146 QWidget* QtxWorkstackArea::widget( const int id ) const
1147 {
1148   QtxWorkstackChild* c = child( id );
1149
1150   return c ? c->widget() : 0;
1151 }
1152
1153 /*!
1154   \brief Get child widget identifier.
1155   \param wid widget
1156   \return widget ID or -1 if widget is not found
1157 */
1158 int QtxWorkstackArea::widgetId( QWidget* wid ) const
1159 {
1160   QtxWorkstackChild* c = child( wid );
1161
1162   return c ? c->id() : -1;
1163 }
1164
1165 /*!
1166   \brief Set active child widget.
1167   \param wid widget to be set active
1168 */
1169 void QtxWorkstackArea::setWidgetActive( QWidget* wid )
1170 {
1171   int id = widgetId( wid );
1172   if ( id < 0 )
1173     return;
1174
1175   myBar->setCurrentIndex( myBar->indexOf( id ) );
1176 }
1177
1178 /*!
1179   \brief Update internal state.
1180 */
1181 void QtxWorkstackArea::updateState()
1182 {
1183   bool updBar = myBar->updatesEnabled();
1184   bool updStk = myStack->updatesEnabled();
1185   myBar->setUpdatesEnabled( false );
1186   myStack->setUpdatesEnabled( false );
1187
1188   bool block = myBar->signalsBlocked();
1189   myBar->blockSignals( true );
1190
1191   QWidget* prev = activeWidget();
1192
1193   int idx = 0;
1194   for ( ChildList::iterator it = myList.begin(); it != myList.end(); ++it )
1195   {
1196     QtxWorkstackChild* cont = *it;
1197     QWidget* wid = cont->widget();;
1198     int id = cont->id();
1199
1200     if ( id < 0 )
1201       continue;
1202
1203     bool vis = cont->visibility();
1204
1205     int cIdx = myBar->indexOf( id );
1206     if ( cIdx != -1 && ( !vis || myBar->indexOf( id ) != idx ) )
1207       myBar->removeTab( cIdx );
1208
1209     if ( myBar->indexOf( id ) == -1 && vis )
1210       myBar->setTabId( myBar->insertTab( idx, wid->windowTitle() ), id );
1211
1212     updateTab( wid );
1213
1214     if ( !vis )
1215       myStack->removeWidget( cont );
1216     else if ( myStack->indexOf( cont ) < 0 )
1217       myStack->addWidget( cont );
1218
1219     if ( vis )
1220       idx++;
1221   }
1222
1223   int curId = widgetId( prev );
1224   if ( myBar->indexOf( curId ) < 0 )
1225   {
1226     QtxWorkstackChild* c = 0;
1227     int pos = myList.indexOf( child( prev ) );
1228     for ( int i = pos - 1; i >= 0 && !c; i-- )
1229     {
1230       if ( myList.at( i )->visibility() )
1231         c = myList.at( i );
1232     }
1233
1234     for ( int j = pos + 1; j < (int)myList.count() && !c; j++ )
1235     {
1236       if ( myList.at( j )->visibility() )
1237         c = myList.at( j );
1238     }
1239
1240     if ( c )
1241       curId = c->id();
1242   }
1243
1244   myBar->setCurrentIndex( myBar->indexOf( curId ) );
1245
1246   myBar->blockSignals( block );
1247
1248   updateCurrent();
1249
1250   myBar->updateActiveState();
1251
1252   myBar->setUpdatesEnabled( updBar );
1253   myStack->setUpdatesEnabled( updStk );
1254   if ( updBar )
1255     myBar->update();
1256   if ( updStk )
1257     myStack->update();
1258
1259   QResizeEvent re( myBar->size(), myBar->size() );
1260   QApplication::sendEvent( myBar, &re );
1261
1262   myBar->updateGeometry();
1263
1264   if ( isEmpty() )
1265   {
1266     hide();
1267     emit deactivated( this );
1268   }
1269   else
1270   {
1271     show();
1272     if ( prev != activeWidget() )
1273       emit activated( activeWidget() );
1274   }
1275 }
1276
1277 /*!
1278   \brief Generate unique widget identifier.
1279   \return first non shared widget ID
1280 */
1281 int QtxWorkstackArea::generateId() const
1282 {
1283   QMap<int, int> map;
1284
1285   for ( ChildList::const_iterator it = myList.begin(); it != myList.end(); ++it )
1286     map.insert( (*it)->id(), 0 );
1287
1288   int id = 0;
1289   while ( map.contains( id ) )
1290     id++;
1291
1292   return id;
1293 }
1294
1295 /*!
1296   \brief Get child widget container.
1297   \param wid child widget
1298   \return child widget container corresponding to the \a wid
1299 */
1300 QtxWorkstackChild* QtxWorkstackArea::child( QWidget* wid ) const
1301 {
1302   QtxWorkstackChild* res = 0;
1303   for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !res; ++it )
1304   {
1305     if ( (*it)->widget() == wid )
1306       res = *it;
1307   }
1308   return res;
1309 }
1310
1311 QtxWorkstackChild* QtxWorkstackArea::child( const int id ) const
1312 {
1313   QtxWorkstackChild* c = 0;
1314   for ( ChildList::const_iterator it = myList.begin(); it != myList.end() && !c; ++it )
1315   {
1316     if ( (*it)->id() == id )
1317       c = *it;
1318   }
1319   return c;
1320 }
1321
1322 /*!
1323   \fn void QtxWorkstackArea::activated( QWidget* w )
1324   \brief Emitted when child widget is activated.
1325   \param w child widget being activated
1326 */
1327
1328 /*!
1329   \fn void QtxWorkstackArea::contextMenuRequested( QWidget* w, QPoint p )
1330   \brief Emitted when context popup menu is requested.
1331   \param w child widget popup menu requested for
1332   \param p point popup menu to be shown at
1333 */
1334
1335 /*!
1336   \fn void QtxWorkstackArea::deactivated( QtxWorkstackArea* wa )
1337   \brief Emitted when workarea is deactivated.
1338   \param wa workarea being deactivated
1339 */
1340
1341 /*!
1342   \class QtxWorkstackChild
1343   \internal
1344   \brief Workarea child widget container.
1345 */
1346
1347 /*!
1348   \brief Constructor.
1349   \param wid child widget
1350   \param parent parent widget
1351   \param f widget flags
1352 */
1353 QtxWorkstackChild::QtxWorkstackChild( QWidget* wid, QWidget* parent, Qt::WindowFlags f )
1354 : QWidget( parent ),
1355   myId( 0 ),
1356   myWidget( wid )
1357 {
1358   if ( myWidget )
1359   {
1360     myWidget->setParent( this, f );
1361     myWidget->installEventFilter( this );
1362     if ( myWidget->focusProxy() )
1363       myWidget->focusProxy()->installEventFilter( this );
1364     myWidget->setVisible( myWidget->isVisibleTo( myWidget->parentWidget() ) );
1365
1366     QVBoxLayout* base = new QVBoxLayout( this );
1367     base->setMargin( 0 );
1368     base->addWidget( myWidget );
1369
1370     connect( myWidget, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1371   }
1372 }
1373
1374 /*!
1375   \brief Destructor.
1376 */
1377 QtxWorkstackChild::~QtxWorkstackChild()
1378 {
1379   QApplication::instance()->removeEventFilter( this );
1380
1381   if ( !widget() )
1382     return;
1383
1384   disconnect( widget(), SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
1385
1386   widget()->hide();
1387   widget()->removeEventFilter( this );
1388   if ( widget()->focusProxy() )
1389     widget()->focusProxy()->removeEventFilter( this );
1390
1391   widget()->setParent( 0 );
1392 }
1393
1394 /*!
1395   \brief Get child widget.
1396   \return child widget
1397 */
1398 QWidget* QtxWorkstackChild::widget() const
1399 {
1400   return myWidget;
1401 }
1402
1403 /*!
1404   \brief Returns the id.
1405 */
1406 int QtxWorkstackChild::id() const
1407 {
1408   return myId;
1409 }
1410
1411 /*!
1412   \brief Sets the id.
1413 */
1414 void QtxWorkstackChild::setId( const int id )
1415 {
1416   myId = id;
1417 }
1418
1419 /*!
1420   \brief Returns true if this child window should be visible.
1421 */
1422 bool QtxWorkstackChild::visibility()
1423 {
1424   return myWidget ? myWidget->isVisibleTo( this ) : false;
1425 }
1426
1427 QtxWorkstackArea* QtxWorkstackChild::area() const
1428 {
1429   QtxWorkstackArea* a = 0;
1430   QWidget* w = parentWidget();
1431   while ( !a && w )
1432   {
1433     a = ::qobject_cast<QtxWorkstackArea*>( w );
1434     w = w->parentWidget();
1435   }
1436
1437   return a;
1438 }
1439
1440 /*!
1441   \brief Custom event filter.
1442
1443   Process events from child widgets.
1444
1445   \param o event receiver widget
1446   \param e event
1447   \return \c true if event should be filtered (stop further processing)
1448 */
1449 bool QtxWorkstackChild::eventFilter( QObject* o, QEvent* e )
1450 {
1451   if ( o->isWidgetType() )
1452   {
1453     if ( e->type() == QEvent::WindowTitleChange || e->type() == QEvent::WindowIconChange )
1454       emit captionChanged( this );
1455
1456     if ( !e->spontaneous() && e->type() == QEvent::ShowToParent )
1457       emit shown( this );
1458
1459     if ( !e->spontaneous() && e->type() == QEvent::HideToParent )
1460       emit hidden( this );
1461
1462     if ( e->type() == QEvent::FocusIn )
1463       emit activated( this );
1464   }
1465   return QWidget::eventFilter( o, e );
1466 }
1467
1468 /*!
1469   \brief Called when child widget is destroyed.
1470   \param obj child widget being destroyed
1471 */
1472 void QtxWorkstackChild::onDestroyed( QObject* obj )
1473 {
1474   deleteLater();
1475 }
1476
1477 /*!
1478   \brief Customize child event handler.
1479   \param e child event
1480 */
1481 void QtxWorkstackChild::childEvent( QChildEvent* e )
1482 {
1483   if ( e->removed() && e->child() == widget() )
1484   {
1485     myWidget = 0;
1486     deleteLater();
1487   }
1488   QWidget::childEvent( e );
1489 }
1490
1491 /*!
1492   \fn void QtxWorkstackChild::shown( QtxWorkstackChild* w )
1493   \brief Emitted when child widget is shown.
1494   \param w child widget container
1495 */
1496
1497 /*!
1498   \fn void QtxWorkstackChild::hidden( QtxWorkstackChild* w )
1499   \brief Emitted when child widget is hidden.
1500   \param w child widget container
1501 */
1502
1503 /*!
1504   \fn void QtxWorkstackChild::activated( QtxWorkstackChild* w )
1505   \brief Emitted when child widget is activated.
1506   \param w child widget container
1507 */
1508
1509 /*!
1510   \fn void QtxWorkstackChild::captionChanged( QtxWorkstackChild* w )
1511   \brief Emitted when child widget's title is changed.
1512   \param w child widget container
1513 */
1514
1515 /*!
1516   \class QtxWorkstackTabBar
1517   \internal
1518   \brief Workstack tab bar widget
1519 */
1520
1521 /*!
1522   \brief Constructor.
1523   \param parent parent widget
1524 */
1525 QtxWorkstackTabBar::QtxWorkstackTabBar( QWidget* parent )
1526 : QTabBar( parent ),
1527   myId( -1 ),myActive(false)
1528 {
1529   setDrawBase( true );
1530   setElideMode( Qt::ElideNone );
1531
1532   connect( this, SIGNAL( currentChanged( int ) ), this, SLOT( onCurrentChanged( int ) ) );
1533 }
1534
1535 /*!
1536   \brief Destructor.
1537 */
1538 QtxWorkstackTabBar::~QtxWorkstackTabBar()
1539 {
1540 }
1541
1542 /*!
1543   \brief Get tab page identifier.
1544   \param index tab page index
1545   \return tab page ID or -1 if \a index is out of range
1546 */
1547 int QtxWorkstackTabBar::tabId( const int index ) const
1548 {
1549   QVariant v = tabData( index );
1550   if ( !v.canConvert( QVariant::Int ) )
1551     return -1;
1552   return v.toInt();
1553 }
1554
1555 /*!
1556   \brief Set tab page identifier.
1557   \param index tab page index
1558   \param id tab page ID
1559 */
1560 void QtxWorkstackTabBar::setTabId( const int index, const int id )
1561 {
1562   setTabData( index, id );
1563 }
1564
1565 /*!
1566   \brief Get tab page index by specified identifier.
1567   \param id tab page ID
1568   \return tab page index or -1 if not found
1569 */
1570 int QtxWorkstackTabBar::indexOf( const int id ) const
1571 {
1572   int index = -1;
1573   for ( int i = 0; i < (int)count() && index < 0; i++ )
1574   {
1575     if ( tabId( i ) == id )
1576       index = i;
1577   }
1578   return index;
1579 }
1580
1581 /*!
1582   \brief Check if the tab bar is active.
1583   \return \c true if tab bar is active
1584 */
1585 bool QtxWorkstackTabBar::isActive() const
1586 {
1587   return myActive;
1588 }
1589
1590 /*!
1591   \brief Set tab bar active/inactive.
1592   \param on new active state
1593 */
1594 void QtxWorkstackTabBar::setActive( const bool on )
1595 {
1596   if ( myActive == on )
1597     return;
1598
1599   myActive = on;
1600   updateActiveState();
1601 }
1602
1603 /*!
1604   \brief Update tab bar according to the 'active' state.
1605 */
1606 void QtxWorkstackTabBar::updateActiveState()
1607 {
1608   QColor bc = palette().color( QPalette::Text );
1609   QColor ac = isActive() ? palette().color( QPalette::Highlight ) : bc;
1610   for ( int i = 0; i < (int)count(); i++ )
1611     setTabTextColor( i, currentIndex() == i ? ac : bc );
1612 }
1613
1614 /*!
1615   \brief Called when current tab page is changed.
1616   \param idx tab page index (not used)
1617 */
1618 void QtxWorkstackTabBar::onCurrentChanged( int /*index*/ )
1619 {
1620   updateActiveState();
1621 }
1622
1623 /*!
1624   \brief Customize mouse move event handler.
1625   \param e mouse event
1626 */
1627 void QtxWorkstackTabBar::mouseMoveEvent( QMouseEvent* e )
1628 {
1629   if ( myId != -1 && !tabRect( indexOf( myId ) ).contains( e->pos() ) )
1630   {
1631     myId = -1;
1632     emit dragActiveTab();
1633   }
1634
1635   QTabBar::mouseMoveEvent( e );
1636 }
1637
1638 /*!
1639   \brief Customize mouse press event handler.
1640   \param e mouse event
1641 */
1642 void QtxWorkstackTabBar::mousePressEvent( QMouseEvent* e )
1643 {
1644   QTabBar::mousePressEvent( e );
1645
1646   if ( e->button() == Qt::LeftButton )
1647     myId = tabId( currentIndex() );
1648 }
1649
1650 /*!
1651   \brief Customize mouse release event handler.
1652   \param e mouse event
1653 */
1654 void QtxWorkstackTabBar::mouseReleaseEvent( QMouseEvent* e )
1655 {
1656   QTabBar::mouseReleaseEvent( e );
1657
1658   myId = -1;
1659
1660   if ( e->button() == Qt::RightButton )
1661     emit contextMenuRequested( e->globalPos() );
1662 }
1663
1664 /*!
1665   \brief Customize context menu event handler.
1666   \param e context menu event
1667 */
1668 void QtxWorkstackTabBar::contextMenuEvent( QContextMenuEvent* e )
1669 {
1670   if ( e->reason() != QContextMenuEvent::Mouse )
1671     emit contextMenuRequested( e->globalPos() );
1672 }
1673
1674 /*!
1675   \brief Process widget change state events (style, palette, enable state changing, etc).
1676   \param e change event (not used)
1677 */
1678 void QtxWorkstackTabBar::changeEvent( QEvent* /*e*/ )
1679 {
1680   updateActiveState();
1681 }
1682
1683 /*
1684 void QtxWorkstackTabBar::paintLabel( QPainter* p, const QRect& br, QTab* t, bool has_focus ) const
1685 {
1686   if ( currentTab() != t->identifier() )
1687   {
1688     QFont fnt = p->font();
1689     fnt.setUnderline( false );
1690     p->setFont( fnt );
1691   }
1692   QTabBar::paintLabel( p, br, t, has_focus );
1693 }
1694 */
1695
1696 /*!
1697   \fn void QtxWorkstackTabBar::dragActiveTab()
1698   \brief Emitted when dragging operation is started.
1699 */
1700
1701 /*!
1702   \fn void QtxWorkstackTabBar::contextMenuRequested( QPoint p )
1703   \brief Emitted when context popup menu is requested.
1704   \param p point popup menu to be shown at
1705 */
1706
1707 /*!
1708   \class QtxWorkstack
1709   \brief Workstack widget.
1710
1711   Organizes the child widgets in the tabbed space.
1712   Allows splitting the working area to arrange the child widgets in
1713   arbitrary way. Any widgets can be moved to another working area with
1714   drag-n-drop operation.
1715
1716   This widget can be used as workspace of the application main window,
1717   for example, as kind of implementation of multi-document interface.
1718 */
1719
1720 /*!
1721   \brief Constructor.
1722   \param parent parent widget
1723 */
1724 QtxWorkstack::QtxWorkstack( QWidget* parent )
1725 : QWidget( parent ),
1726   myWin( 0 ),
1727   myArea( 0 ),
1728   myWorkWin( 0 ),
1729   myWorkArea( 0 )
1730 {
1731   myActionsMap.insert( SplitVertical,   new QtxAction( QString(), tr( "Split vertically" ), 0, this ) );
1732   myActionsMap.insert( SplitHorizontal, new QtxAction( QString(), tr( "Split horizontally" ), 0, this ) );
1733   myActionsMap.insert( Close,           new QtxAction( QString(), tr( "Close" ), 0, this ) );
1734   myActionsMap.insert( Rename,          new QtxAction( QString(), tr( "Rename" ), 0, this ) );
1735
1736   connect( myActionsMap[SplitVertical], SIGNAL( triggered( bool ) ), this, SLOT( splitVertical() ) );
1737   connect( myActionsMap[SplitHorizontal], SIGNAL( triggered( bool ) ), this, SLOT( splitHorizontal() ) );
1738   connect( myActionsMap[Close], SIGNAL( triggered( bool ) ), this, SLOT( onCloseWindow() ) );
1739   connect( myActionsMap[Rename], SIGNAL( triggered( bool ) ), this, SLOT( onRename() ) );
1740
1741   // Action shortcut will work when action added in any widget.
1742   for ( QMap<int, QAction*>::iterator it = myActionsMap.begin(); it != myActionsMap.end(); ++it )
1743   {
1744     addAction( it.value() );
1745     it.value()->setShortcutContext( Qt::ApplicationShortcut );
1746   }
1747
1748   QVBoxLayout* base = new QVBoxLayout( this );
1749   base->setMargin( 0 );
1750
1751   mySplit = new QtxWorkstackSplitter( this );
1752   base->addWidget( mySplit );
1753 }
1754
1755 /*!
1756   \brief Destructor.
1757 */
1758 QtxWorkstack::~QtxWorkstack()
1759 {
1760 }
1761
1762 /*!
1763   \brief Get list of all widgets in all areas or in specified area which given
1764          widget belongs to
1765   \param wid widget specifying area if it is equal to null when widgets of all
1766          areas are retuned
1767   \return list of widgets
1768 */
1769 QWidgetList QtxWorkstack::windowList( QWidget* wid ) const
1770 {
1771   QList<QtxWorkstackArea*> lst;
1772   if ( !wid )
1773   {
1774     areas( mySplit, lst, true );
1775   }
1776   else
1777   {
1778     QtxWorkstackArea* area = wgArea( wid );
1779     if ( area )
1780       lst.append( area );
1781   }
1782
1783   QWidgetList widList;
1784   for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end(); ++it )
1785   {
1786     QWidgetList wids = (*it)->widgetList();
1787     for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1788       widList.append( *itr );
1789   }
1790
1791   return widList;
1792 }
1793
1794 /*!
1795   \brief Get all child widgets in the active workarea.
1796   \return list of widgets in active workarea
1797 */
1798 QWidgetList QtxWorkstack::splitWindowList() const
1799 {
1800   return myArea ? myArea->widgetList() : QWidgetList();
1801 }
1802
1803 /*!
1804   \brief Get active widget.
1805   \return active widget
1806 */
1807 QWidget* QtxWorkstack::activeWindow() const
1808 {
1809   return myWin;
1810 }
1811
1812 /*!
1813   \brief Set active widget
1814   \param wid widget to activate
1815 */
1816 void QtxWorkstack::setActiveWindow( QWidget* wid )
1817 {
1818   if ( activeArea() )
1819     activeArea()->setActiveWidget( wid );
1820 }
1821
1822 /*!
1823   \brief Split workstack.
1824
1825   Splitting is possible only if there are two or more widgets in the workarea.
1826   This function splits current workarea to two new ones.
1827
1828   \param o splitting orientation (Qt::Orientation)
1829 */
1830 void QtxWorkstack::split( const int o )
1831 {
1832   QtxWorkstackArea* area = myWorkArea;
1833   if ( !area )
1834     area = activeArea();
1835   if ( !area )
1836     return;
1837
1838   if ( area->widgetList().count() < 2 )
1839     return;
1840
1841   QWidget* curWid = area->activeWidget();
1842   if ( !curWid )
1843     return;
1844
1845   QSplitter* s = splitter( area );
1846   QList<QtxWorkstackArea*> areaList;
1847   areas( s, areaList );
1848
1849   QList<QSplitter*> splitList;
1850   splitters( s, splitList );
1851
1852   QSplitter* trg = 0;
1853   if ( areaList.count() + splitList.count() < 2 || s->orientation() == o )
1854     trg = s;
1855
1856   if ( !trg )
1857     trg = wrapSplitter( area );
1858
1859   if ( !trg )
1860     return;
1861
1862   trg->setOrientation( (Qt::Orientation)o );
1863
1864   QtxWorkstackArea* newArea = createArea( 0 );
1865   trg->insertWidget( trg->indexOf( area ) + 1, newArea );
1866
1867   area->removeWidget( curWid );
1868   newArea->insertWidget( curWid );
1869
1870   distributeSpace( trg );
1871
1872   curWid->show();
1873   curWid->setFocus();
1874 }
1875
1876 /*!
1877   \brief Split workarea of the given widget on two parts.
1878
1879   Splitting is possible only if there are two or more widgets in the workarea.
1880   This function splits current workarea to two new ones.
1881
1882   \param wid widget belonging to the workstack
1883   \param o splitting orientation type (Qt::Orientation)
1884   \param type splitting type (QtxWorkstack::SplitType)
1885 */
1886 void QtxWorkstack::Split( QWidget* wid, const Qt::Orientation o, const SplitType type )
1887 {
1888   if (!wid) return;
1889
1890   // find area of the given widget
1891   QtxWorkstackArea* area = NULL;
1892   QList<QtxWorkstackArea*> allAreas;
1893   areas(mySplit, allAreas, true);
1894
1895   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
1896   {
1897     if ( (*it)->contains( wid ) )
1898       area = *it;
1899   }
1900
1901   if ( !area )
1902     return;
1903
1904   QWidget* curWid = area->activeWidget();
1905   if ( !curWid )
1906     return;
1907
1908   QWidgetList wids = area->widgetList();
1909   if ( wids.count() < 2 )
1910     return;
1911
1912   QSplitter* s = splitter( area );
1913   QList<QtxWorkstackArea*> areaList;
1914   areas( s, areaList );
1915
1916   QList<QSplitter*> splitList;
1917   splitters(s, splitList);
1918
1919   QSplitter* trg = 0;
1920   if (areaList.count() + splitList.count() < 2 || s->orientation() == o)
1921     trg = s;
1922
1923   if (!trg) trg = wrapSplitter(area);
1924   if (!trg) return;
1925
1926   trg->setOrientation(o);
1927
1928   QtxWorkstackArea* newArea = createArea(0);
1929   insertWidget(newArea, trg, area);
1930
1931   switch ( type )
1932   {
1933   case SplitStay:
1934     for ( QWidgetList::iterator itr = wids.begin(); itr != wids.end(); ++itr )
1935     {
1936       QWidget* wid_i = *itr;
1937       if ( wid_i != wid )
1938       {
1939         area->removeWidget( wid_i );
1940         newArea->insertWidget( wid_i );
1941         wid_i->showMaximized();
1942       }
1943     }
1944     break;
1945   case SplitAt:
1946     {
1947       QWidgetList::iterator itr = wids.begin();
1948       for ( ; itr != wids.end() && *itr != wid; ++itr )
1949       {
1950       }
1951       for ( ; itr != wids.end(); ++itr )
1952       {
1953         area->removeWidget( *itr );
1954         newArea->insertWidget( *itr );
1955         (*itr)->showMaximized();
1956       }
1957     }
1958     break;
1959   case SplitMove:
1960     area->removeWidget( wid );
1961     newArea->insertWidget( wid );
1962     wid->showMaximized();
1963     break;
1964   }
1965
1966   distributeSpace( trg );
1967
1968   curWid->show();
1969   curWid->setFocus();
1970 }
1971
1972 /*!
1973  \brief Move widget(s) from the source workarea into the target workarea
1974         or reorder widgets inside one workarea.
1975
1976  Move \a wid2 in target workarea. Put it right after \a wid1.
1977  If \a all parameter is \c true, all widgets from source workarea
1978  will be moved including \a wid2 and source workarea will be deleted then.
1979
1980  If \a wid1 and \a wid2 belongs to one workarea, widgets will be just reordered
1981  in that workarea.
1982
1983  \param wid1 widget from target workarea
1984  \param wid2 widget from source workarea
1985  \param all  if \c true, all widgets from source workarea will
1986              be moved into the target one, else only the \a wid2 will be moved
1987 */
1988 void QtxWorkstack::Attract( QWidget* wid1, QWidget* wid2, const bool all )
1989 {
1990   if ( !wid1 || !wid2 )
1991     return;
1992
1993   // find area of the widgets
1994   QtxWorkstackArea *area1 = 0, *area2 = 0;
1995   QList<QtxWorkstackArea*> allAreas;
1996   areas( mySplit, allAreas, true );
1997   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !( area1 && area2 ); ++it )
1998   {
1999     if ( (*it)->contains( wid1 ) )
2000       area1 = *it;
2001
2002     if ( (*it)->contains( wid2 ) )
2003       area2 = *it;
2004   }
2005
2006   if ( !area1 || !area2 )
2007     return;
2008
2009   QSplitter* s1 = splitter( area1 );
2010
2011   QWidget* curWid = area1->activeWidget();
2012   if ( !curWid )
2013     return;
2014
2015   if ( area1 == area2 )
2016   {
2017     if ( all )
2018     {
2019       // Set wid1 at first position, wid2 at second
2020       area1->insertWidget( wid1 );
2021       area1->insertWidget( wid2, 1 );
2022       wid1->showMaximized();
2023       wid2->showMaximized();
2024     }
2025     else
2026     {
2027       // Set wid2 right after wid1
2028       area1->removeWidget( wid2 );
2029       int wid1_ind = 0;
2030       QWidgetList wids1 = area1->widgetList();
2031       for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
2032       area1->insertWidget( wid2, wid1_ind + 1 );
2033       wid2->showMaximized();
2034     }
2035   }
2036   else
2037   {
2038     int wid1_ind = 0;
2039     QWidgetList wids1 = area1->widgetList();
2040     for ( QWidgetList::iterator itr1 = wids1.begin(); itr1 != wids1.end() && *itr1 != wid1; ++itr1, ++wid1_ind );
2041     if ( all )
2042     {
2043       // Set wid2 right after wid1, other widgets from area2 right after wid2
2044       QWidgetList wids2 = area2->widgetList();
2045       QWidgetList::iterator itr2 = wids2.begin();
2046       for ( int ind = wid1_ind + 1; itr2 != wids2.end(); ++itr2, ++ind )
2047       {
2048         area2->removeWidget( *itr2 );
2049         if ( *itr2 == wid2 )
2050           area1->insertWidget( *itr2, wid1_ind + 1 );
2051         else
2052           area1->insertWidget( *itr2, ind );
2053         (*itr2)->showMaximized();
2054       }
2055     }
2056     else
2057     {
2058       // Set wid2 right after wid1
2059       area2->removeWidget( wid2 );
2060       area1->insertWidget( wid2, wid1_ind + 1 );
2061       wid2->showMaximized();
2062     }
2063   }
2064
2065   distributeSpace( s1 );
2066
2067   area1->setActiveWidget( curWid );
2068
2069   wid2->show();
2070   wid1->setFocus();
2071   curWid->show();
2072   curWid->setFocus();
2073 }
2074
2075 /*!
2076   \brief Calculate sizes of the splitter widget for the workarea.
2077   \internal
2078 */
2079 static void setSizes (QIntList& szList, const int item_ind,
2080                       const int new_near, const int new_this, const int new_farr)
2081 {
2082   // set size to all items before an item # <item_ind>
2083   int cur_pos = 0;
2084   QIntList::iterator its = szList.begin();
2085   for (; its != szList.end() && cur_pos < item_ind; ++its, ++cur_pos) {
2086     *its = new_near;
2087   }
2088   if (its == szList.end()) return;
2089   // set size to item # <item_ind>
2090   *its = new_this;
2091   ++its;
2092   // set size to all items after an item # <item_ind>
2093   for (; its != szList.end(); ++its) {
2094     *its = new_farr;
2095   }
2096 }
2097
2098 /*!
2099   \brief Set position of the widget relatively to its parent splitter.
2100
2101   Orientation of positioning will correspond to the splitter orientation.
2102
2103   \param wid widget
2104   \param pos position relatively to the splitter; value in the range [0..1]
2105 */
2106 void QtxWorkstack::SetRelativePositionInSplitter( QWidget* wid, const double position )
2107 {
2108   if ( position < 0.0 || 1.0 < position)
2109     return;
2110
2111   if ( !wid )
2112     return;
2113
2114   // find area of the given widget
2115   QtxWorkstackArea* area = NULL;
2116   QList<QtxWorkstackArea*> allAreas;
2117   areas( mySplit, allAreas, true );
2118   for ( QList<QtxWorkstackArea*>::iterator it = allAreas.begin(); it != allAreas.end() && !area; ++it )
2119   {
2120     if ( (*it)->contains( wid ) )
2121       area = *it;
2122   }
2123
2124   if ( !area )
2125     return;
2126
2127   QSplitter* split = splitter( area );
2128   if ( !split )
2129     return;
2130
2131   // find index of the area in its splitter
2132   int item_ind = -1;
2133   bool isFound = false;
2134   const QObjectList& was = split->children();
2135   for ( QObjectList::const_iterator ito = was.begin(); ito != was.end() && !isFound; ++ito, ++item_ind )
2136   {
2137     if ( *ito == area )
2138       isFound = true;
2139   }
2140
2141   if ( !isFound || item_ind == 0 )
2142     return;
2143
2144   QIntList szList = split->sizes();
2145   int splitter_size = ( split->orientation() == Qt::Horizontal ? split->width() : split->height());
2146   int nb = szList.count();
2147
2148   int new_prev = int( splitter_size * position / item_ind );
2149   if (nb == item_ind) return;
2150   int new_next = int( splitter_size * ( 1.0 - position ) / ( nb - item_ind ) );
2151   setSizes( szList, item_ind, new_prev, new_next, new_next );
2152   split->setSizes( szList );
2153 }
2154
2155 /*!
2156   \brief Set position of the widget relatively to the entire workstack.
2157
2158   If \a o is \c Qt::Horizontal, the horizontal position of \a wid will be changed.
2159   If \a o is \c Qt::Vertical, the vertical position of \a wid will be changed.
2160
2161   \param wid widget
2162   \param o   orientation of positioning (\c Qt::Horizontal or \c Qt::Vertical)
2163   \param pos position relatively to the workstack; value in range [0..1]
2164 */
2165 void QtxWorkstack::SetRelativePosition( QWidget* wid, const Qt::Orientation o,
2166                                         const double position )
2167 {
2168   if ( position < 0.0 || 1.0 < position )
2169     return;
2170
2171   if ( !wid )
2172     return;
2173
2174   int splitter_size = o == Qt::Horizontal ? mySplit->width() : mySplit->height();
2175   int need_pos = int( position * splitter_size );
2176   int splitter_pos = 0;
2177
2178   if ( setPosition( wid, mySplit, o, need_pos, splitter_pos ) != 0 )
2179   {
2180     // impossible to set required position
2181   }
2182 }
2183
2184 /*!
2185   \brief Set accelerator key-combination for the action with specified \a id.
2186   \param id action ID
2187   \param accel action accelerator
2188 */
2189 void QtxWorkstack::setAccel( const int id, const int accel )
2190 {
2191   if ( !myActionsMap.contains( id ) )
2192     return;
2193
2194   myActionsMap[id]->setShortcut( accel );
2195 }
2196
2197 /*!
2198   \brief Get the action's accelerator key-combination.
2199   \param id action ID
2200   \return action accelerator
2201 */
2202 int QtxWorkstack::accel( const int id ) const
2203 {
2204   int res = 0;
2205   if ( myActionsMap.contains( id ) )
2206     res = myActionsMap[id]->shortcut()[0];
2207   return res;
2208 }
2209
2210 /*!
2211   \brief Get icon for the specified action.
2212
2213   If \a id is invalid, null icon is returned.
2214
2215   \param id menu action ID
2216   \return menu item icon
2217 */
2218 QIcon QtxWorkstack::icon( const int id ) const
2219 {
2220   QIcon ico;
2221   if ( myActionsMap.contains( id ) )
2222     ico = myActionsMap[id]->icon();
2223   return ico;
2224 }
2225
2226 /*!
2227   \brief Set menu item icon for the specified action.
2228   \param id menu action ID
2229   \param ico new menu item icon
2230 */
2231 void QtxWorkstack::setIcon( const int id, const QIcon& icon )
2232 {
2233   if ( !myActionsMap.contains( id ) )
2234     return;
2235
2236   myActionsMap[id]->setIcon( icon );
2237 }
2238
2239 /*!
2240   \brief Set actions to be visible in the context popup menu.
2241
2242   Actions, which IDs are set in \a flags parameter, will be shown in the
2243   context popup menu. Other actions will not be shown.
2244
2245   \param flags ORed together actions flags
2246 */
2247 void QtxWorkstack::setMenuActions( const int flags )
2248 {
2249   myActionsMap[SplitVertical]->setVisible( flags & SplitVertical );
2250   myActionsMap[SplitHorizontal]->setVisible( flags & SplitHorizontal );
2251   myActionsMap[Close]->setVisible( flags & Close );
2252   myActionsMap[Rename]->setVisible( flags & Rename );
2253 }
2254
2255 /*!
2256   \brief Set actions to be visible in the context popup menu.
2257
2258   Actions, which IDs are set in \a flags parameter, will be shown in the
2259   context popup menu. Other actions will not be shown.
2260
2261   \param flags ORed together actions flags
2262 */
2263 int QtxWorkstack::menuActions() const
2264 {
2265   int ret = 0;
2266   ret = ret | ( myActionsMap[SplitVertical]->isVisible() ? SplitVertical : 0 );
2267   ret = ret | ( myActionsMap[SplitHorizontal]->isVisible() ? SplitHorizontal : 0 );
2268   ret = ret | ( myActionsMap[Close]->isVisible() ? Close : 0 );
2269   ret = ret | ( myActionsMap[Rename]->isVisible() ? Rename : 0 );
2270   return ret;
2271 }
2272
2273 /*!
2274   \brief Calculate sizes of the splitter widget for the workarea.
2275   \internal
2276 */
2277 static int positionSimple (QIntList& szList, const int nb, const int splitter_size,
2278                            const int item_ind, const int item_rel_pos,
2279                            const int need_pos, const int splitter_pos)
2280 {
2281   if (item_ind == 0) { // cannot move in this splitter
2282     return (need_pos - splitter_pos);
2283   }
2284
2285   int delta = 0;
2286   int new_prev = 0;
2287   int new_this = szList[item_ind];
2288   int new_next = 0;
2289
2290   bool isToCheck = false;
2291
2292   if (need_pos < splitter_pos) {
2293     // Set size of all previous workareas to zero <--
2294     if (item_ind == nb - 1) {
2295       // item iz last in the splitter, it will occupy all the splitter
2296       new_this = splitter_size;
2297     } else {
2298       // recompute size of next items in splitter
2299       new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2300     }
2301     delta = need_pos - splitter_pos;
2302
2303   } else if (need_pos > (splitter_pos + splitter_size)) {
2304     // Set size of all next workareas to zero -->
2305     // recompute size of previous items in splitter
2306     new_this = 0;
2307     new_prev = (splitter_size - new_this) / item_ind;
2308     delta = need_pos - (splitter_pos + splitter_size - new_this);
2309
2310   } else { // required position lays inside this splitter
2311     // Move workarea inside splitter into required position <->
2312     int new_item_rel_pos = need_pos - splitter_pos;
2313     new_prev = new_item_rel_pos / item_ind;
2314     if (need_pos < (splitter_pos + item_rel_pos)) {
2315       // Make previous workareas smaller, next - bigger
2316       // No problem to keep old size of the widget
2317     } else {
2318       // Make previous workareas bigger, next - smaller
2319       if (new_this > splitter_size - new_item_rel_pos) {
2320         new_this = splitter_size - new_item_rel_pos;
2321       }
2322       // jfa to do: in this case fixed size of next widgets could prevent right resizing
2323       isToCheck = true;
2324     }
2325     if (item_ind == nb - 1) {
2326       new_this = splitter_size - new_item_rel_pos;
2327     } else {
2328       new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2329     }
2330     delta = 0;
2331   }
2332
2333   setSizes (szList, item_ind, new_prev, new_this, new_next);
2334   return delta;
2335 }
2336
2337 /*!
2338   \brief Set position of the widget.
2339
2340   Called from SetRelativePosition() public method.
2341
2342   \param wid   widget to be moved
2343   \param split currently processed splitter (goes from more common
2344                to more particular splitter in recursion calls)
2345   \param o     orientation of positioning
2346   \param need_pos required position of the given widget in pixels
2347                (from top/left side of workstack area)
2348   \param splitter_pos position of the splitter \a split
2349                (from top/left side of workstack area)
2350   \return difference between a required and a distinguished position
2351 */
2352 int QtxWorkstack::setPosition( QWidget* wid, QSplitter* split, const Qt::Orientation o,
2353                                const int need_pos, const int splitter_pos )
2354 {
2355   if ( !wid || !split )
2356     return need_pos - splitter_pos;
2357
2358   // Find corresponding sub-splitter.
2359   // Find also index of appropriate item in current splitter.
2360   int cur_ind = 0, item_ind = 0;
2361   bool isBottom = false, isFound = false;
2362   QSplitter* sub_split = NULL;
2363   const QObjectList& objs = split->children();
2364   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end() && !isFound; ++it )
2365   {
2366     QtxWorkstackArea* area = ::qobject_cast<QtxWorkstackArea*>( *it );
2367     if ( area )
2368     {
2369       if ( area->contains( wid ) )
2370       {
2371         item_ind = cur_ind;
2372         isBottom = true;
2373         isFound = true;
2374       }
2375       cur_ind++;
2376     }
2377     else if ( (*it)->inherits( "QSplitter" ) )
2378     {
2379       QList<QtxWorkstackArea*> areaList;
2380       areas( (QSplitter*)(*it), areaList, true );
2381       for ( QList<QtxWorkstackArea*>::iterator ita = areaList.begin(); ita != areaList.end() && !isFound; ++ita )
2382       {
2383         if ( (*ita)->contains( wid ) )
2384         {
2385           item_ind = cur_ind;
2386           isFound = true;
2387           sub_split = (QSplitter*)*it;
2388         }
2389       }
2390       cur_ind++;
2391     }
2392   }
2393
2394   if ( !isFound )
2395     return ( need_pos - splitter_pos );
2396
2397   if ( split->orientation() == o )
2398   {
2399     // Find coordinates of near and far sides of the appropriate item relatively current splitter
2400     int splitter_size = ( o == Qt::Horizontal ? split->width() : split->height() );
2401     QIntList szList = split->sizes();
2402     int nb = szList.count();
2403     int item_rel_pos = 0; // position of near side of item relatively this splitter
2404     for (int i = 0; i < item_ind; i++) {
2405       item_rel_pos += szList[i];
2406     }
2407     int item_size = szList[item_ind]; // size of item
2408     int item_pos = splitter_pos + item_rel_pos;
2409
2410     // Resize splitter items to complete the conditions
2411     if (isBottom) {
2412       // I. Bottom of splitters stack reached
2413
2414       int delta = positionSimple(szList, nb, splitter_size, item_ind, item_rel_pos, need_pos, splitter_pos);
2415       split->setSizes(szList);
2416       // Recompute delta, as some windows can reject given size
2417       int new_item_rel_pos = 0;
2418       QIntList szList1 = split->sizes();
2419       for (int i = 0; i < item_ind; i++) {
2420         new_item_rel_pos += szList1[i];
2421       }
2422       delta = need_pos - (splitter_pos + new_item_rel_pos);
2423       return delta;
2424
2425     } else {
2426       // II. Bottom of splitters stack is not yet reached
2427
2428       if (item_ind == 0) { // cannot move in this splitter
2429         // Process in sub-splitter
2430         return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2431       }
2432
2433       int new_prev = 0;
2434       int new_this = szList[item_ind];
2435       int new_next = 0;
2436
2437       if (need_pos < splitter_pos) {
2438         // Set size of all previous workareas to zero <--
2439         if (item_ind == nb - 1) {
2440           new_this = splitter_size;
2441         } else {
2442           new_next = (splitter_size - new_this) / (nb - item_ind - 1);
2443         }
2444         setSizes (szList, item_ind, new_prev, new_this, new_next);
2445         split->setSizes(szList);
2446         // Recompute splitter_pos, as some windows can reject given size
2447         int new_item_rel_pos = 0;
2448         QIntList szList1 = split->sizes();
2449         for (int i = 0; i < item_ind; i++) {
2450           new_item_rel_pos += szList1[i];
2451         }
2452         // Process in sub-splitter
2453         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2454       } else if (need_pos > (splitter_pos + splitter_size)) {
2455         // Set size of all next workareas to zero -->
2456         new_prev = (splitter_size - new_this) / item_ind;
2457         setSizes (szList, item_ind, new_prev, new_this, new_next);
2458         split->setSizes(szList);
2459         // Recompute splitter_pos, as some windows can reject given size
2460         int new_item_rel_pos = 0;
2461         QIntList szList1 = split->sizes();
2462         for (int i = 0; i < item_ind; i++) {
2463           new_item_rel_pos += szList1[i];
2464         }
2465         // Process in sub-splitter
2466         return setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2467       } else {
2468         // Set appropriate size of all previous/next items <->
2469         int new_item_rel_pos = item_rel_pos;
2470         if (need_pos < item_pos || (item_pos + item_size) < need_pos) {
2471           // Move item inside splitter into required position <->
2472           int new_this = szList[item_ind];
2473           int new_next = 0;
2474           new_item_rel_pos = need_pos - splitter_pos;
2475           if ((item_pos + item_size) < need_pos) {
2476             //new_item_rel_pos = need_pos - (item_pos + item_size);
2477             new_item_rel_pos = item_rel_pos + (need_pos - (item_pos + item_size));
2478           }
2479           int new_prev = new_item_rel_pos / item_ind;
2480           if (need_pos < (splitter_pos + item_rel_pos)) {
2481             // Make previous workareas smaller, next - bigger
2482             // No problem to keep old size of the widget
2483           } else {
2484             // Make previous workareas bigger, next - smaller
2485             if (new_this > splitter_size - new_item_rel_pos) {
2486               new_this = splitter_size - new_item_rel_pos;
2487             }
2488           }
2489           if (item_ind == nb - 1) {
2490             new_this = splitter_size - new_item_rel_pos;
2491           } else {
2492             new_next = (splitter_size - new_item_rel_pos - new_this) / (nb - item_ind - 1);
2493           }
2494           setSizes (szList, item_ind, new_prev, new_this, new_next);
2495           split->setSizes(szList);
2496           // Recompute new_item_rel_pos, as some windows can reject given size
2497           new_item_rel_pos = 0;
2498           QIntList szList1 = split->sizes();
2499           for (int i = 0; i < item_ind; i++) {
2500             new_item_rel_pos += szList1[i];
2501           }
2502         } else {
2503           // Do nothing
2504         }
2505         // Process in sub-splitter
2506         int add_pos = setPosition(wid, sub_split, o, need_pos, splitter_pos + new_item_rel_pos);
2507         if (add_pos == 0)
2508           return 0;
2509
2510         // this can be if corresponding workarea is first in sub-splitter
2511         // or sub-splitter has another orientation
2512
2513         // Resize ones again to reach precize position <->
2514         int need_pos_1 = splitter_pos + new_item_rel_pos + add_pos;
2515
2516         // Move workarea inside splitter into required position <->
2517         int delta_1 = positionSimple(szList, nb, splitter_size, item_ind,
2518                                      new_item_rel_pos, need_pos_1, splitter_pos);
2519         split->setSizes(szList);
2520         // Recompute new_item_rel_pos, as some windows can reject given size
2521         new_item_rel_pos = 0;
2522         QIntList szList1 = split->sizes();
2523         for (int i = 0; i < item_ind; i++) {
2524           new_item_rel_pos += szList1[i];
2525         }
2526         delta_1 = need_pos_1 - (splitter_pos + new_item_rel_pos);
2527         return delta_1;
2528       }
2529     }
2530   } else {
2531     return setPosition(wid, sub_split, o, need_pos, splitter_pos);
2532   }
2533
2534   return 0;
2535 }
2536
2537 /*!
2538   \brief Redistribute space among widgets equally.
2539   \param split splitter
2540 */
2541 void QtxWorkstack::distributeSpace( QSplitter* split ) const
2542 {
2543   if ( !split )
2544     return;
2545
2546   QIntList szList = split->sizes();
2547   int size = ( split->orientation() == Qt::Horizontal ?
2548                split->width() : split->height() ) / szList.count();
2549   for ( QIntList::iterator it = szList.begin(); it != szList.end(); ++it )
2550     *it = size;
2551   split->setSizes( szList );
2552 }
2553
2554 /*!
2555   \brief Split widgets vertically.
2556 */
2557 void QtxWorkstack::splitVertical()
2558 {
2559   split( Qt::Horizontal );
2560 }
2561
2562 /*!
2563   \brief Split widgets horizontally.
2564 */
2565 void QtxWorkstack::splitHorizontal()
2566 {
2567   split( Qt::Vertical );
2568 }
2569
2570 /*!
2571   \brief Called when user activates "Rename" menu item.
2572
2573   Changes widget title.
2574 */
2575 void QtxWorkstack::onRename()
2576 {
2577   if ( !myWorkWin )
2578     return;
2579
2580   bool ok = false;
2581   QString newName = QInputDialog::getText( topLevelWidget(),  tr( "Rename" ), tr( "Enter new name:" ),
2582                                            QLineEdit::Normal, myWorkWin->windowTitle(), &ok );
2583   if ( ok && !newName.isEmpty() )
2584     myWorkWin->setWindowTitle( newName );
2585 }
2586
2587 /*!
2588   \brief Wrap area into the new splitter.
2589   \param workarea
2590   \return new splitter
2591 */
2592 QSplitter* QtxWorkstack::wrapSplitter( QtxWorkstackArea* area )
2593 {
2594   if ( !area )
2595     return 0;
2596
2597   QSplitter* pSplit = splitter( area );
2598   if ( !pSplit )
2599     return 0;
2600
2601   bool upd = pSplit->updatesEnabled();
2602   pSplit->setUpdatesEnabled( false );
2603
2604   QIntList szList = pSplit->sizes();
2605
2606   QSplitter* wrap = new QtxWorkstackSplitter( 0 );
2607   pSplit->insertWidget( pSplit->indexOf( area ) + 1, wrap );
2608   wrap->setVisible( true );
2609   wrap->addWidget( area );
2610
2611   pSplit->setSizes( szList );
2612
2613   pSplit->setUpdatesEnabled( upd );
2614
2615   return wrap;
2616 }
2617
2618 /*!
2619   \brief Reparent and add widget.
2620   \param wid widget
2621   \param pWid parent widget
2622   \param after widget after which \a wid should be added
2623 */
2624 void QtxWorkstack::insertWidget( QWidget* wid, QWidget* pWid, QWidget* after )
2625 {
2626   if ( !wid || !pWid )
2627     return;
2628
2629   QWidgetList moveList;
2630   const QObjectList& lst = pWid->children();
2631   bool found = false;
2632   for ( QObjectList::const_iterator it = lst.begin(); it != lst.end(); ++it )
2633   {
2634     if ( found && ( (*it)->inherits( "QSplitter" ) ||
2635                     (*it)->inherits( "QtxWorkstackArea" ) ) )
2636       moveList.append( (QWidget*)(*it) );
2637     if ( *it == after )
2638       found = true;
2639   }
2640
2641   QMap<QWidget*, bool> map;
2642   for ( QWidgetList::iterator it = moveList.begin(); it != moveList.end(); ++it )
2643   {
2644     map.insert( *it, (*it)->isVisibleTo( (*it)->parentWidget() ) );
2645     (*it)->setParent( 0 );
2646     (*it)->hide();
2647   }
2648
2649   wid->setParent( pWid );
2650
2651   for ( QWidgetList::iterator itr = moveList.begin(); itr != moveList.end(); ++itr )
2652   {
2653     (*itr)->setParent( pWid );
2654     (*itr)->setVisible( map.contains( *itr ) ? map[*itr] : false );
2655   }
2656 }
2657
2658 /*!
2659   \brief Close active window.
2660 */
2661 void QtxWorkstack::onCloseWindow()
2662 {
2663   if ( myWorkWin )
2664     myWorkWin->close();
2665   else if( activeWindow() )
2666     activeWindow()->close();
2667 }
2668
2669 /*!
2670   \brief Called when workarea is destroyed.
2671
2672   Set input focus to the neighbour area.
2673
2674   \param obj workarea being destroyed
2675 */
2676 void QtxWorkstack::onDestroyed( QObject* obj )
2677 {
2678   QtxWorkstackArea* area = (QtxWorkstackArea*)obj;
2679
2680   if ( area == myArea )
2681     myArea = 0;
2682
2683   if ( !myArea )
2684   {
2685     QtxWorkstackArea* cur = neighbourArea( area );
2686     if ( cur )
2687       cur->setFocus();
2688   }
2689
2690   QApplication::postEvent( this, new QEvent( QEvent::User ) );
2691 }
2692
2693 /*!
2694   \brief Called on window activating.
2695   \param area workarea being activated (not used)
2696 */
2697 void QtxWorkstack::onWindowActivated( QWidget* /*area*/ )
2698 {
2699   const QObject* obj = sender();
2700   if ( !obj->inherits( "QtxWorkstackArea" ) )
2701     return;
2702
2703   setActiveArea( (QtxWorkstackArea*)obj );
2704 }
2705
2706 /*!
2707   \brief Called on window deactivating.
2708   \param area workarea being deactivated
2709 */
2710 void QtxWorkstack::onDeactivated( QtxWorkstackArea* area )
2711 {
2712   if ( myArea != area )
2713     return;
2714
2715   QList<QtxWorkstackArea*> lst;
2716   areas( mySplit, lst, true );
2717
2718   int idx = lst.indexOf( area );
2719   if ( idx == -1 )
2720     return;
2721
2722   myWin = 0;
2723   myArea = 0;
2724
2725   QtxWorkstackArea* newArea = neighbourArea( area );
2726   if ( newArea && newArea->activeWidget() )
2727     newArea->activeWidget()->setFocus();
2728
2729   QApplication::postEvent( this, new QEvent( QEvent::User ) );
2730 }
2731
2732 /*!
2733   \brief Create and show popup menu for workarea.
2734   \param w workarea
2735   \param p popup position
2736 */
2737 void QtxWorkstack::onContextMenuRequested( QWidget* w, QPoint p )
2738 {
2739   QtxWorkstackArea* anArea = ::qobject_cast<QtxWorkstackArea*>( (QObject*)sender() );
2740   if ( !anArea )
2741     anArea = activeArea();
2742
2743   if ( !anArea )
2744     return;
2745
2746   QWidgetList lst = anArea->widgetList();
2747   if ( lst.isEmpty() )
2748     return;
2749
2750   myWorkWin = w;
2751   myWorkArea = anArea;
2752
2753   QMenu* pm = new QMenu();
2754
2755   if ( lst.count() > 1 )
2756   {
2757     if ( !myActionsMap[SplitVertical]->isEnabled() )
2758       myActionsMap[SplitVertical]->setEnabled(true);
2759     pm->addAction( myActionsMap[SplitVertical] );
2760     if ( !myActionsMap[SplitHorizontal]->isEnabled() )
2761       myActionsMap[SplitHorizontal]->setEnabled(true);
2762     pm->addAction( myActionsMap[SplitHorizontal] );
2763     pm->addSeparator();
2764   }
2765
2766   if ( w )
2767   {
2768     if ( myActionsMap[Close]->isEnabled() )
2769       pm->addAction( myActionsMap[Close] );
2770     if ( myActionsMap[Rename]->isEnabled() )
2771       pm->addAction( myActionsMap[Rename] );
2772   }
2773
2774   Qtx::simplifySeparators( pm );
2775
2776   if ( !pm->actions().isEmpty() )
2777     pm->exec( p );
2778
2779   delete pm;
2780
2781   myWorkWin = 0;
2782   myWorkArea = 0;
2783 }
2784
2785 /*!
2786   \brief Add child widget.
2787   \param w widget
2788   \param f widget flags
2789   \return child widget container
2790 */
2791 QWidget* QtxWorkstack::addWindow( QWidget* w, Qt::WindowFlags f )
2792 {
2793   if ( !w )
2794     return 0;
2795
2796   return targetArea()->insertWidget( w, -1, f );
2797 }
2798
2799 /*!
2800   \brief Handle custom events.
2801   \param e custom event (not used)
2802 */
2803 void QtxWorkstack::customEvent( QEvent* /*e*/ )
2804 {
2805   updateState();
2806 }
2807
2808 /*!
2809   \brief Get splitter corresponding to the workarea.
2810   \param workarea
2811   \return splitter corresponding to the workarea
2812 */
2813 QSplitter* QtxWorkstack::splitter( QtxWorkstackArea* area ) const
2814 {
2815   if ( !area )
2816     return 0;
2817
2818   QSplitter* split = 0;
2819
2820   QWidget* wid = area->parentWidget();
2821   if ( wid && wid->inherits( "QSplitter" ) )
2822     split = (QSplitter*)wid;
2823
2824   return split;
2825 }
2826
2827 /*!
2828   \brief Get list of child splitters.
2829   \param split parent splitter
2830   \param splitList list to be filled with child splitters
2831   \param rec if \c true, perform recursive search of children
2832 */
2833 void QtxWorkstack::splitters( QSplitter* split, QList<QSplitter*>& splitList, const bool rec ) const
2834 {
2835   if ( !split )
2836     return;
2837
2838   const QObjectList& objs = split->children();
2839   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2840   {
2841     if ( rec )
2842       splitters( (QSplitter*)*it, splitList, rec );
2843     if ( (*it)->inherits( "QSplitter" ) )
2844       splitList.append( (QSplitter*)*it );
2845   }
2846 }
2847
2848 /*!
2849   \brief Get list of child workareas.
2850   \param split parent splitter
2851   \param areaList list to be filled with child workareas
2852   \param rec if \c true, perform recursive search of children
2853 */
2854 void QtxWorkstack::areas( QSplitter* split, QList<QtxWorkstackArea*>& areaList, const bool rec ) const
2855 {
2856   if ( !split )
2857     return;
2858
2859   const QObjectList& objs = split->children();
2860   for ( QObjectList::const_iterator it = objs.begin(); it != objs.end(); ++it )
2861   {
2862     if ( (*it)->inherits( "QtxWorkstackArea" ) )
2863       areaList.append( (QtxWorkstackArea*)*it );
2864     else if ( rec && (*it)->inherits( "QSplitter" ) )
2865       areas( (QSplitter*)*it, areaList, rec );
2866   }
2867 }
2868
2869 /*!
2870   \brief Get active workarea.
2871   \return active workarea
2872 */
2873 QtxWorkstackArea* QtxWorkstack::activeArea() const
2874 {
2875   return myArea;
2876 }
2877
2878 /*!
2879   \brief Get target area (for which the current operation should be done).
2880
2881   Returns active workarea or current area (if there is no active workarea).
2882   If there are no workareas, create new workarea and return it.
2883
2884   \return workarea
2885 */
2886 QtxWorkstackArea* QtxWorkstack::targetArea()
2887 {
2888   QtxWorkstackArea* area = activeArea();
2889   if ( !area )
2890     area = currentArea();
2891   if ( !area )
2892   {
2893     QList<QtxWorkstackArea*> lst;
2894     areas( mySplit, lst );
2895     if ( !lst.isEmpty() )
2896       area = lst.first();
2897   }
2898
2899   if ( !area )
2900     area = createArea( mySplit );
2901
2902   return area;
2903 }
2904
2905 /*!
2906   \brief Get current workarea.
2907
2908   Current workarea is that one which has input focus.
2909
2910   \return current area
2911 */
2912 QtxWorkstackArea* QtxWorkstack::currentArea() const
2913 {
2914   QtxWorkstackArea* area = 0;
2915   QWidget* wid = focusWidget();
2916   while ( wid && !area )
2917   {
2918     if ( wid->inherits( "QtxWorkstackArea" ) )
2919       area = (QtxWorkstackArea*)wid;
2920     wid = wid->parentWidget();
2921   }
2922
2923   return area;
2924 }
2925
2926 /*!
2927   \brief Create new workarea.
2928   \param parent parent widget
2929   \return created workarea
2930 */
2931 QtxWorkstackArea* QtxWorkstack::createArea( QWidget* parent ) const
2932 {
2933   QtxWorkstackArea* area = new QtxWorkstackArea( parent );
2934
2935   connect( area, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) );
2936   connect( area, SIGNAL( activated( QWidget* ) ), this, SLOT( onWindowActivated( QWidget* ) ) );
2937   connect( area, SIGNAL( contextMenuRequested( QWidget*, QPoint ) ),
2938            this, SLOT( onContextMenuRequested( QWidget*, QPoint ) ) );
2939   connect( area, SIGNAL( deactivated( QtxWorkstackArea* ) ), this, SLOT( onDeactivated( QtxWorkstackArea* ) ) );
2940
2941   return area;
2942 }
2943
2944 /*!
2945   \brief Set active workarea.
2946   \param workarea
2947 */
2948 void QtxWorkstack::setActiveArea( QtxWorkstackArea* area )
2949 {
2950   QWidget* oldCur = myWin;
2951
2952   QtxWorkstackArea* oldArea = myArea;
2953
2954   myArea = area;
2955
2956   if ( myArea != oldArea )
2957   {
2958     if ( oldArea )
2959       oldArea->updateActiveState();
2960     if ( myArea )
2961       myArea->updateActiveState();
2962   }
2963
2964   if ( myArea )
2965     myWin = myArea->activeWidget();
2966
2967   if ( myWin && oldCur != myWin )
2968     emit windowActivated( myWin );
2969 }
2970
2971 /*!
2972   \brief Get workarea which is nearest to \a area.
2973   \param area area for which neighbour is searched
2974   \return neighbour area (or 0 if not found)
2975 */
2976 QtxWorkstackArea* QtxWorkstack::neighbourArea( QtxWorkstackArea* area ) const
2977 {
2978   QList<QtxWorkstackArea*> lst;
2979   areas( mySplit, lst, true );
2980   int pos = lst.indexOf( area );
2981   if ( pos < 0 )
2982     return 0;
2983
2984   QtxWorkstackArea* na = 0;
2985   for ( int i = pos - 1; i >= 0 && !na; i-- )
2986   {
2987     if ( !lst.at( i )->isEmpty() )
2988       na = lst.at( i );
2989   }
2990
2991   for ( int j = pos + 1; j < (int)lst.count() && !na; j++ )
2992   {
2993     if ( !lst.at( j )->isEmpty() )
2994         na = lst.at( j );
2995   }
2996   return na;
2997 }
2998
2999 /*!
3000   \brief Get workarea covering point.
3001   \return workarea
3002   \param p point
3003 */
3004 QtxWorkstackArea* QtxWorkstack::areaAt( const QPoint& p ) const
3005 {
3006   QtxWorkstackArea* area = 0;
3007   QList<QtxWorkstackArea*> lst;
3008   areas( mySplit, lst, true );
3009   for ( QList<QtxWorkstackArea*>::iterator it = lst.begin(); it != lst.end() && !area; ++it )
3010   {
3011     QtxWorkstackArea* cur = *it;
3012     QRect r = cur->geometry();
3013     if ( cur->parentWidget() )
3014       r = QRect( cur->parentWidget()->mapToGlobal( r.topLeft() ), r.size() );
3015     if ( r.contains( p ) )
3016       area = cur;
3017   }
3018   return area;
3019 }
3020
3021 /*!
3022   \brief Update internal state.
3023 */
3024 void QtxWorkstack::updateState()
3025 {
3026   updateState( mySplit );
3027 }
3028
3029 /*!
3030   \brief Update splitter state.
3031   \param split splitter to be updated
3032 */
3033 void QtxWorkstack::updateState( QSplitter* split )
3034 {
3035   QList<QSplitter*> recList;
3036   splitters( split, recList, false );
3037   for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr )
3038     updateState( *itr );
3039
3040   QList<QSplitter*> splitList;
3041   splitters( split, splitList, false );
3042
3043   QList<QtxWorkstackArea*> areaList;
3044   areas( split, areaList, false );
3045
3046   bool vis = false;
3047   for ( QList<QtxWorkstackArea*>::iterator it = areaList.begin(); it != areaList.end(); ++it )
3048   {
3049     if ( (*it)->isEmpty() )
3050       (*it)->hide();
3051     else
3052     {
3053       (*it)->show();
3054       vis = true;
3055     }
3056   }
3057
3058   if ( split == mySplit )
3059     return;
3060
3061   for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end() && !vis; ++iter )
3062     vis = (*iter)->isVisibleTo( (*iter)->parentWidget() );
3063
3064   if ( areaList.isEmpty() && splitList.isEmpty() )
3065     delete split;
3066   else
3067     split->setVisible( vis );
3068 }
3069
3070 /*!
3071   \brief Dump workstack configuration to the state description array.
3072   \param version number
3073   \return state byte array.
3074 */
3075 QByteArray QtxWorkstack::saveState( int version ) const
3076 {
3077   QByteArray data;
3078
3079   QDataStream stream( &data, QIODevice::WriteOnly );
3080   stream << QtxWorkstack::VersionMarker;
3081   stream << version;
3082   saveState( stream );
3083
3084   return data;
3085 }
3086
3087 /*!
3088   \brief Restore workstack configuration from the state description array.
3089   \param version number
3090   \return restore performing state
3091 */
3092 bool QtxWorkstack::restoreState( const QByteArray& state, int version )
3093 {
3094   if ( state.isEmpty() )
3095     return false;
3096
3097   QByteArray sd = state;
3098   QDataStream stream( &sd, QIODevice::ReadOnly );
3099   int marker, ver;
3100   stream >> marker;
3101   stream >> ver;
3102   if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::VersionMarker || ver != version )
3103     return false;
3104
3105   return restoreState( stream );
3106 }
3107
3108 void QtxWorkstack::saveState( QDataStream& stream ) const
3109 {
3110   mySplit->saveState( stream );
3111 }
3112
3113 bool QtxWorkstack::restoreState( QDataStream& stream )
3114 {
3115   QMap<QString, QtxWorkstackChild*> map;
3116   QList<QtxWorkstackArea*> areaList;
3117   areas( mySplit, areaList, true );
3118   for ( QList<QtxWorkstackArea*>::const_iterator it = areaList.begin(); it != areaList.end(); ++it )
3119   {
3120     QtxWorkstackArea* area = *it;
3121     QList<QtxWorkstackChild*> childList = area->childList();
3122     for ( QList<QtxWorkstackChild*>::iterator itr = childList.begin(); itr != childList.end(); ++itr )
3123     {
3124       QtxWorkstackChild* c = *itr;
3125       if ( !c->widget() )
3126         continue;
3127
3128       map.insert( c->widget()->objectName(), c );
3129
3130       qDebug( "QtxWorkstack::restoreState: found widget \"%s\"", (const char*)c->widget()->objectName().toUtf8() );
3131     }
3132   }
3133
3134   int marker;
3135   stream >> marker;
3136   if ( stream.status() != QDataStream::Ok || marker != QtxWorkstack::SplitMarker )
3137     return false;
3138
3139   QtxWorkstackSplitter* split = new QtxWorkstackSplitter( this );
3140   if ( layout() )
3141     layout()->addWidget( split );
3142
3143   bool ok = split->restoreState( stream, map );
3144   if ( !ok )
3145     delete split;
3146   else
3147   {
3148     mySplit->deleteLater();
3149     mySplit = split;
3150
3151     QList<QtxWorkstackArea*> aList;
3152     areas( mySplit, aList, true );
3153
3154     QtxWorkstackArea* a = !aList.isEmpty() ? aList.first() : 0;
3155     for ( QMap<QString, QtxWorkstackChild*>::const_iterator it = map.begin(); it != map.end(); ++it )
3156     {
3157       QtxWorkstackChild* c = it.value();
3158       if ( c->widget() )
3159         c->widget()->setVisible( false );
3160       if ( a )
3161         a->insertChild( c );
3162       else
3163         c->setVisible( false );
3164     }
3165   }
3166
3167   return ok;
3168 }
3169
3170 /*!
3171   \brief Set resize mode of all splitters opaque or transparent.
3172   \param opaque opaque mode
3173 */
3174 void QtxWorkstack::setOpaqueResize( bool opaque )
3175 {
3176   QList<QSplitter*> splitList;
3177   splitters( mySplit, splitList, true );
3178   splitList << mySplit;
3179   foreach( QSplitter* split, splitList )
3180     split->setOpaqueResize( opaque );
3181 }
3182
3183 /*!
3184   \brief Get resize mode of all splitters: opaque (\c true) or transparent (\c false).
3185   \return current opaque mode
3186 */
3187 bool QtxWorkstack::opaqueResize() const
3188 {
3189   return mySplit->opaqueResize();
3190 }
3191
3192 /*!
3193   \brief Show/hide splitter state and area.
3194   \param wid widget (and parent area) will be shown/hidden
3195   \param parent_list parent splitters list
3196   \param split splitter will be shown/hidden
3197   \param visible splitter
3198 */
3199 void QtxWorkstack::splitterVisible(QWidget* wid, QList<QSplitter*>& parent_list, QSplitter* split, bool visible)
3200 {
3201   QList<QSplitter*> recList;
3202   splitters( split, recList, false );
3203   for ( QList<QSplitter*>::iterator itr = recList.begin(); itr != recList.end(); ++itr ) {
3204     parent_list.prepend( *itr );
3205     splitterVisible( wid, parent_list, *itr, visible );
3206   }
3207
3208   QList<QtxWorkstackArea*> areaList;
3209   areas( split, areaList, false );
3210   for ( QList<QtxWorkstackArea*>::const_iterator it = areaList.begin(); it != areaList.end(); ++it ) {
3211     QtxWorkstackArea* area = *it;
3212     bool isCurrentWidget = false;
3213
3214     area->showTabBar(visible);
3215
3216     // 1. Looking for the selected widget at the lowest level among all splitted areas.
3217     QList<QtxWorkstackChild*> childList = area->childList();
3218     for ( QList<QtxWorkstackChild*>::iterator itr = childList.begin(); itr != childList.end(); ++itr ) {
3219       QWidget* aCurWid = (*itr)->widget();
3220       if ( aCurWid == wid ) {
3221         isCurrentWidget = true;
3222         aCurWid->setVisible( true );
3223       }
3224       else
3225         aCurWid->setVisible( visible );
3226     }
3227
3228     // 2. Show/Hide other areas and widgets that don't contain the desired widget
3229     if ( !isCurrentWidget || visible )
3230       area->setVisible( visible );
3231
3232     if ( !isCurrentWidget && !visible )
3233       continue;
3234
3235     // 3. Show/hide all parent widgets
3236     QSplitter* pSplit = splitter( area );
3237     int count = pSplit->count();
3238     for ( int i = 0; i < count; i++ ) {
3239       if ( pSplit->indexOf( area ) == i && !visible )
3240         continue;
3241       pSplit->widget(i)->setVisible( visible );
3242     }
3243
3244     // 4. Show/hide all parent splitters don't contain the selected widget
3245     if ( visible )
3246       pSplit->setVisible( true );
3247
3248     if ( isCurrentWidget && !visible ) {
3249       for ( QList<QSplitter*>::iterator itr = parent_list.begin(); itr != parent_list.end() && pSplit != mySplit; ++itr ) {
3250         if ( pSplit == *itr )
3251           continue;
3252         QList<QSplitter*> splitList;
3253         splitters( *itr, splitList, false );
3254         for ( QList<QSplitter*>::iterator iter = splitList.begin(); iter != splitList.end(); ++iter ) {
3255           if ( pSplit == (*iter) ) {
3256             pSplit = *itr;
3257             continue;
3258           }
3259           (*iter)->setVisible( false );
3260         }
3261       }
3262     }
3263   }
3264 }
3265
3266 /*!
3267   \brief Show/hide splitters state and area.
3268   \param wid widget (and parent area) will be shown/hidden
3269   \param visible splitters
3270 */
3271 void QtxWorkstack::splittersVisible( QWidget* wid, bool visible )
3272 {
3273   QList<QSplitter*> parent_list;
3274   parent_list.append( mySplit );
3275   splitterVisible( wid, parent_list, mySplit, visible );
3276 }
3277
3278 /*!
3279   \fn void QtxWorkstack::windowActivated( QWidget* w )
3280   \brief Emitted when the workstack's child widget \w is activated.
3281   \param w widget being activated
3282 */
3283
3284 /*!
3285   \brief Gets area containing given widget
3286   \param wid widget
3287   \return pointer to QtxWorkstackArea* object
3288 */
3289 QtxWorkstackArea* QtxWorkstack::wgArea( QWidget* wid ) const
3290 {
3291   QtxWorkstackArea* resArea = 0;
3292
3293   QList<QtxWorkstackArea*> areaList;
3294   areas( mySplit, areaList, true );
3295
3296   QList<QtxWorkstackArea*>::ConstIterator it;
3297   for ( it = areaList.begin(); it != areaList.end() && !resArea; ++it )
3298   {
3299     if ( (*it)->contains( wid ) )
3300       resArea = *it;
3301   }
3302
3303   return resArea;
3304 }
3305
3306 /*!
3307   \brief Moves the first widget to the same area which the second widget belongs to
3308   \param wid widget to be moved
3309   \param wid_to widget specified the destination area
3310   \param before specifies whether the first widget has to be moved before or after
3311          the second widget
3312   \return \c true if operation is completed successfully, \c false otherwise
3313 */
3314 bool QtxWorkstack::move( QWidget* wid, QWidget* wid_to, const bool before )
3315 {
3316   if ( wid && wid_to )
3317   {
3318     QtxWorkstackArea* area_src = wgArea( wid );
3319     QtxWorkstackArea* area_to = wgArea( wid_to );
3320     if ( area_src && area_to )
3321     {
3322       // find index of the second widget
3323       QWidgetList wgList = area_to->widgetList();
3324       QWidgetList::ConstIterator it;
3325       int idx = 0;
3326       for ( it = wgList.begin(); it != wgList.begin(); ++it, idx++ )
3327       {
3328         if ( *it == wid_to )
3329           break;
3330       }
3331
3332       if ( idx < wgList.count() ) // paranoidal check
3333       {
3334         if ( !before )
3335           idx++;
3336         area_src->removeWidget( wid, true );
3337         area_to->insertWidget( wid, idx );
3338         wid->showMaximized();
3339         return true;
3340       }
3341     }
3342   }
3343   return false;
3344 }
3345
3346 /*!
3347   \brief Group all windows in one area
3348   \return \c true if operation is completed successfully, \c false otherwise
3349 */
3350 void QtxWorkstack::stack()
3351 {
3352   QWidgetList wgList = windowList();
3353   if ( !wgList.count() )
3354     return; // nothing to do
3355
3356   QtxWorkstackArea* area_to = 0;
3357   QWidgetList::ConstIterator it;
3358   for ( it = wgList.begin(); it != wgList.end(); ++it )
3359   {
3360     QtxWorkstackArea* area_src = 0;
3361     if ( !area_to )
3362     {
3363       area_to = wgArea( *it );
3364       area_src = area_to;
3365     }
3366     else
3367       area_src = wgArea( *it );
3368
3369     if ( area_src != area_to )
3370     {
3371       area_src->removeWidget( *it, true );
3372       area_to->insertWidget( *it, -1 );
3373       (*it)->showMaximized();
3374     }
3375   }
3376 }
3377
3378 QAction* QtxWorkstack::action( const int id ) const
3379 {
3380   return myActionsMap.contains( id ) ? myActionsMap[id] : 0;
3381 }