Salome HOME
Moved some functionality to VTKViewer_Utilities.h
[modules/kernel.git] / src / PatchQt / qsplitterP.cxx
1 /////////////////////////////////////////////////////////////////////////////
2 // Module      : PatchQt
3 // File        : qsplitterP.cxx
4 // Description : the patch for Qt's QSplitter class (qsplitter.cpp)
5 /////////////////////////////////////////////////////////////////////////////
6
7 /****************************************************************************
8 ** $Id$
9 **
10 **  Splitter widget
11 **
12 **  Created:  980105
13 **
14 ** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
15 **
16 ** This file is part of the widgets module of the Qt GUI Toolkit.
17 **
18 ** This file may be distributed under the terms of the Q Public License
19 ** as defined by Trolltech AS of Norway and appearing in the file
20 ** LICENSE.QPL included in the packaging of this file.
21 **
22 ** This file may be distributed and/or modified under the terms of the
23 ** GNU General Public License version 2 as published by the Free Software
24 ** Foundation and appearing in the file LICENSE.GPL included in the
25 ** packaging of this file.
26 **
27 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
28 ** licenses may use this file in accordance with the Qt Commercial License
29 ** Agreement provided with the Software.
30 **
31 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
32 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
33 **
34 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
35 **   information about Qt Commercial License Agreements.
36 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
37 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
38 **
39 ** Contact info@trolltech.com if any conditions of this licensing are
40 ** not clear to you.
41 **
42 **********************************************************************/
43
44 #include "qsplitterP.h"
45 #ifndef QT_NO_COMPLEXWIDGETS
46
47 #include <qglobal.h>
48 #include <qpainter.h>
49 #include <qdrawutil.h>
50 #include <qbitmap.h>
51 #include <private/qlayoutengine_p.h>
52 #if QT_VERSION < 300
53   #include <qlist.h>
54   #include <qarray.h>
55 #else
56   #include <qptrlist.h>  //qt3.x
57   #include <qmemarray.h> //qt3.x
58   #include <qstyle.h> //qt3.x
59 #endif
60
61 #include <qobjectlist.h>
62 #include <qapplication.h> //sendPostedEvents
63 using namespace std;
64
65 #define SPLITTER_ICON_WIDTH 10
66 #define SPLITTER_ICON_HEIGHT 12
67
68 static int mouseOffset;
69 static int opaqueOldPos = -1; //### there's only one mouse, but this is a bit risky
70
71 static const char* splitter_left_xpm[] = {
72   "5 5 2 1 0 0",
73   "     s none          m none  c none",
74   ".    s iconColor1    m black c black",
75   "   ..",
76   " ....",
77   ".....",
78   " ....",
79   "   .."
80 };
81
82 static const char* splitter_right_xpm[] = {
83   "5 5 2 1 0 0",
84   "     s iconColor1    m black c black",
85   ".    s none          m none  c none",
86   "  ...",
87   "    .",
88   "     ",
89   "    .",
90   "  ..."
91 };
92
93 static const char* splitter_up_xpm[] = {
94   "5 5 2 1 0 0",
95   "     s none          m none  c none",
96   ".    s iconColor1    m black c black",
97   "  .  ",
98   " ... ",
99   " ... ",
100   ".....",
101   "....."
102 };
103
104 static const char* splitter_down_xpm[] = {
105   "5 5 2 1 0 0",
106   "     s iconColor1    m black c black",
107   ".    s none          m none  c none",
108   "     ",
109   "     ",
110   ".   .",
111   ".   .",
112   ".. .."
113 };
114
115 QSplitterPHandle::QSplitterPHandle( Qt::Orientation o,
116                                   QSplitterP *parent, const char * name )
117     : QWidget( parent, name )
118 {
119   s = parent;
120   
121   left  = new QPushButton(this);
122   right = new QPushButton(this);
123   unleft  = new QPushButton(this);
124   unright = new QPushButton(this);
125   
126   setOrientation(o);
127   
128   left->setAutoDefault(false); unleft->setAutoDefault(false);   
129   right->setAutoDefault(false); unright->setAutoDefault(false);
130   left->setFlat(true); right->setFlat(true); unleft->setFlat(true); unright->setFlat(true);
131   left->setCursor( arrowCursor );       right->setCursor( arrowCursor );
132   unleft->setCursor( arrowCursor ); unright->setCursor( arrowCursor );
133
134   connect(left,    SIGNAL(clicked()), this, SLOT(onBeforeCompress()));
135   connect(right,   SIGNAL(clicked()), this, SLOT(onAfterCompress()));
136   connect(unleft,  SIGNAL(clicked()), this, SLOT(onBeforeUnCompress()));
137   connect(unright, SIGNAL(clicked()), this, SLOT(onAfterUnCompress()));
138 }
139
140 void QSplitterPHandle::onBeforeCompress() {
141   oldpos = s->pick(pos());
142   int pos = s->pick(s->widgetBefore(id())->pos());
143   s->compress(s->widgetBefore(id()));
144   s->moveSplitter(pos, id());
145   s->updateSplitterHandles();
146 }
147
148 void QSplitterPHandle::onBeforeUnCompress() {
149   s->unCompress(s->widgetBefore(id()));
150   s->moveSplitter(oldpos, id());
151   s->updateSplitterHandles();
152 }
153
154 void QSplitterPHandle::onAfterCompress() {
155   oldpos = s->pick(pos());
156   int pos = s->pick(s->widgetAfter(id())->pos()) + s->pick(s->widgetAfter(id())->size()) - s->pick(size());
157   s->compress(s->widgetAfter(id()));
158   s->moveSplitter(pos, id());
159   s->updateSplitterHandles();
160 }
161
162 void QSplitterPHandle::onAfterUnCompress() {
163   s->unCompress(s->widgetAfter(id()));
164   s->moveSplitter(oldpos, id());
165   s->updateSplitterHandles();
166 }
167
168 // VSR 21/11/02 -> ================================
169 void QSplitterPHandle::compressAfter() 
170 {
171   onAfterCompress();
172 }
173
174 void QSplitterPHandle::unCompressAfter() 
175 {
176   onAfterUnCompress();
177 }
178
179 void QSplitterPHandle::compressBefore() 
180 {
181   onBeforeCompress();
182 }
183
184 void QSplitterPHandle::unCompressBefore() 
185 {
186   onBeforeUnCompress();
187 }
188 // VSR 21/11/02 <- ================================
189
190 QSizePolicy QSplitterPHandle::sizePolicy() const
191 {
192     //### removeme 3.0
193     return QWidget::sizePolicy();
194 }
195
196 QSize QSplitterPHandle::sizeHint() const
197 {
198 #if QT_VERSION < 300
199   int sw = style().splitterWidth();
200   return QSize(sw,sw).expandedTo( QApplication::globalStrut() );
201 #else
202   int sw = style().pixelMetric(QStyle::PM_SplitterWidth, this);
203   return (style().sizeFromContents(QStyle::CT_Splitter, s, QSize(sw, sw)).
204           expandedTo(QApplication::globalStrut()));
205 #endif
206 }
207
208 void QSplitterPHandle::setOrientation( Qt::Orientation o )
209 {
210     orient = o;
211 #ifndef QT_NO_CURSOR
212     if ( o == QSplitterP::Horizontal )
213         setCursor( splitHCursor );
214     else
215         setCursor( splitVCursor );
216 #endif
217
218         QPixmap lp, rp;
219         if ( orient == QSplitterP::Horizontal )
220         {
221                 lp = QPixmap(splitter_left_xpm);
222                 rp = QPixmap(splitter_right_xpm);
223         }
224         else
225         {
226                 lp = QPixmap(splitter_up_xpm);
227                 rp = QPixmap(splitter_down_xpm);
228         }
229         left->setPixmap(lp);
230         right->setPixmap(rp);
231         unleft->setPixmap(rp);
232         unright->setPixmap(lp);
233 }
234
235 void QSplitterPHandle::mouseMoveEvent( QMouseEvent *e )
236 {
237     if ( !(e->state()&LeftButton) )
238         return;
239     QCOORD pos = s->pick(parentWidget()->mapFromGlobal(e->globalPos()))
240                  - mouseOffset;
241     if ( opaque() ) {
242         s->moveSplitter( pos, id() );
243     } else {
244         int min = pos; int max = pos;
245         s->getRange( id(), &min, &max );
246         s->setRubberband( QMAX( min, QMIN(max, pos )));
247     }
248 }
249
250 void QSplitterPHandle::mousePressEvent( QMouseEvent *e )
251 {
252     if ( e->button() == LeftButton )
253         mouseOffset = s->pick(e->pos());
254 }
255
256 void QSplitterPHandle::mouseReleaseEvent( QMouseEvent *e )
257 {
258     if ( !opaque() && e->button() == LeftButton ) {
259         QCOORD pos = s->pick(parentWidget()->mapFromGlobal(e->globalPos()));
260         s->setRubberband( -1 );
261         s->moveSplitter( pos, id() );
262     }
263 }
264
265 void QSplitterPHandle::paintEvent( QPaintEvent * )
266 {
267         updateButtonState();
268     QPainter p( this );
269     s->drawSplitter( &p, 0, 0, width(), height() );
270 }
271
272 void QSplitterPHandle::updateButtonState() {
273         if (!s->isCompressEnabled()) {
274                 left->hide();
275                 right->hide();
276                 unleft->hide();
277                 unright->hide();
278         }
279         else {
280                 if ( orient == QSplitterP::Horizontal )
281                 {
282                         left->setGeometry( QRect( QPoint( (width() - SPLITTER_ICON_WIDTH)/2, 0 ),
283                                                                           QSize ( SPLITTER_ICON_WIDTH, SPLITTER_ICON_HEIGHT ) ) );
284                         right->setGeometry( QRect( QPoint( (width() - SPLITTER_ICON_WIDTH)/2, SPLITTER_ICON_HEIGHT ),
285                                                            QSize ( SPLITTER_ICON_WIDTH, SPLITTER_ICON_HEIGHT ) ) );
286                         unleft->setGeometry( left->geometry() );
287                         unright->setGeometry( right->geometry() );
288                 }
289                 else
290                 {
291                         left->setGeometry( QRect( QPoint( 0, (height() - SPLITTER_ICON_WIDTH)/2 ),
292                                                                           QSize ( SPLITTER_ICON_HEIGHT, SPLITTER_ICON_WIDTH ) ) );
293                         right->setGeometry( QRect( QPoint( SPLITTER_ICON_HEIGHT, (height() - SPLITTER_ICON_WIDTH)/2),
294                                                                            QSize ( SPLITTER_ICON_HEIGHT, SPLITTER_ICON_WIDTH ) ) );
295                         unleft->setGeometry( left->geometry() );
296                         unright->setGeometry( right->geometry() );
297                 }
298                 if ( s->isCompressed( s->widgetBefore( id() ) ) ) {
299                         left->hide();
300                         unleft->show();
301                         right->setEnabled(false);
302                 }
303                 else {
304                         unleft->hide();
305                         left->show();
306                         right->setEnabled(true);
307                 }
308                 if ( s->isCompressed( s->widgetAfter( id() ) ) ) {
309                         right->hide();
310                         unright->show();
311                         left->setEnabled(false);
312                 }
313                 else {
314                         unright->hide();
315                         right->show();
316                         left->setEnabled(true);
317                 }
318         }
319 }
320
321 class QSplitterPLayoutStruct
322 {
323 public:
324     QSplitterP::ResizeMode mode;
325     QCOORD sizer;
326     bool isSplitter;
327     QWidget *wid;
328 };
329
330 class QSplitterPData
331 {
332 public:
333     QSplitterPData() : opaque( FALSE ), firstShow( TRUE ) {}
334
335     QList<QSplitterPLayoutStruct> list;
336     bool opaque;
337     bool firstShow;
338 };
339
340
341 // NOT REVISED
342 /*!
343   \class QSplitter qsplitter.h
344   \brief The QSplitter class implements a splitter widget.
345
346   \ingroup organizers
347
348   A splitter lets the user control the size of child widgets by
349   dragging the boundary between the children. Any number of widgets
350   may be controlled.
351
352   To show a QListBox, a QListView and a QMultiLineEdit side by side:
353
354   \code
355     QSplitter *split = new QSplitter( parent );
356     QListBox *lb = new QListBox( split );
357     QListView *lv = new QListView( split );
358     QMultiLineEdit *ed = new QMultiLineEdit( split );
359   \endcode
360
361   In QSplitter the boundary can be either horizontal or vertical.  The
362   default is horizontal (the children are side by side) and you
363   can use setOrientation( QSplitter::Vertical ) to set it to vertical.
364
365   By default, all widgets can be as large or as small as the user
366   wishes, down to \link QWidget::minimumSizeHint() minimumSizeHint()\endlink.
367   You can naturally use setMinimumSize() and/or
368   setMaximumSize() on the children. Use setResizeMode() to specify that
369   a widget should keep its size when the splitter is resized.
370
371   QSplitter normally resizes the children only at the end of a
372   resize operation, but if you call setOpaqueResize( TRUE ), the
373   widgets are resized as often as possible.
374
375   The initial distribution of size between the widgets is determined
376   by the initial size of each widget. You can also use setSizes() to
377   set the sizes of all the widgets. The function sizes() returns the
378   sizes set by the user.
379
380   If you hide() a child, its space will be distributed among the other
381   children. When you show() it again, it will be reinstated.
382
383   <img src=qsplitter-m.png> <img src=qsplitter-w.png>
384
385   \sa QTabBar
386 */
387
388
389
390 QSize QSplitterP::minSize( const QWidget *w ) const
391 {
392  if ( isCompressed(w) && (data->list.count() > 1 ) ) return QSize(0,0);
393  QSize min = w->minimumSize();
394  QSize s;
395  if ( min.height() <= 0 || min.width() <= 0 )
396     s = w->minimumSizeHint();
397  if ( min.height() > 0 )
398     s.setHeight( min.height() );
399  if ( min.width() > 0 )
400     s.setWidth( min.width() );
401  return s.expandedTo(QSize(0,0));
402 }
403
404 QSize QSplitterP::maxSize( const QWidget* w ) const
405 {
406   if ( isCompressed(w) && (data->list.count() > 1 ) ) return QSize(0,0);
407   else return w->maximumSize();
408 }
409
410 /*!
411   Constructs a horizontal splitter.
412 */
413
414 QSplitterP::QSplitterP( QWidget *parent, const char *name )
415      :QFrame(parent,name,WPaintUnclipped)
416 {
417   orient = Horizontal;
418   init();
419 }
420
421
422 /*!
423   Constructs splitter with orientation \a o.
424 */
425
426 QSplitterP::QSplitterP( Orientation o, QWidget *parent, const char *name )
427     :QFrame(parent,name,WPaintUnclipped)
428 {
429   orient = o;
430   init();
431 }
432
433
434 /*!
435   Destructs the splitter.
436 */
437
438 QSplitterP::~QSplitterP()
439 {
440     data->list.setAutoDelete( TRUE );
441     delete data;
442 }
443
444
445 void QSplitterP::init()
446 {
447     data = new QSplitterPData;
448     if ( orient == Horizontal )
449         setSizePolicy( QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Minimum) );
450     else
451         setSizePolicy( QSizePolicy(QSizePolicy::Minimum,QSizePolicy::Fixed) );
452         compressed_widgets.clear();
453         compress_flag = false;
454 }
455
456
457 /*!
458   \fn void QSplitter::refresh()
459
460   Updates the splitter state. You should not need to call this
461   function during normal use of the splitter.
462 */
463
464
465 /*!  Sets the orientation to \a o.  By default the orientation is
466   horizontal (the widgets are side by side).
467
468   \sa orientation()
469 */
470
471 void QSplitterP::setOrientation( Orientation o )
472 {
473     if ( orient == o )
474         return;
475     orient = o;
476
477     if ( orient == Horizontal )
478         setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ) );
479     else
480         setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) );
481
482     QSplitterPLayoutStruct *s = data->list.first();
483     while ( s ) {
484         if ( s->isSplitter )
485             ((QSplitterPHandle*)s->wid)->setOrientation( o );
486         s = data->list.next();  // ### next at end of loop, no iterator
487     }
488     recalc( isVisible() );
489 }
490
491
492 /*!
493    \fn Orientation QSplitter::orientation() const
494
495    Returns the orientation (\c Horizontal or \c Vertical) of the splitter.
496    \sa setOrientation()
497 */
498
499 /*!
500   \reimp
501 */
502 void QSplitterP::resizeEvent( QResizeEvent * )
503 {
504     doResize();
505 }
506
507
508 /*!
509   Inserts the widget \a w at the end, or at the beginning if \a first is TRUE
510
511   It is the responsibility of the caller of this function to make sure
512   that \a w is not already in the splitter, and to call recalcId if
513   needed.  (If \a first is TRUE, then recalcId is very probably
514   needed.)
515 */
516
517 QSplitterPLayoutStruct *QSplitterP::addWidget( QWidget *w, bool first )
518 {
519     QSplitterPLayoutStruct *s;
520     QSplitterPHandle *newHandle = 0;
521     if ( data->list.count() > 0 ) {
522         s = new QSplitterPLayoutStruct;
523         s->mode = KeepSize;
524         newHandle = new QSplitterPHandle( orientation(), this );
525         s->wid = newHandle;
526         newHandle->setId(data->list.count());
527         s->isSplitter = TRUE;
528         s->sizer = pick( newHandle->sizeHint() );
529         if ( first )
530             data->list.insert( 0, s );
531         else
532             data->list.append( s );
533     }
534     s = new QSplitterPLayoutStruct;
535     s->mode = Stretch;
536     s->wid = w;
537     if ( !testWState( WState_Resized ) && w->sizeHint().isValid() )
538         s->sizer = pick( w->sizeHint() );
539     else
540         s->sizer = pick( w->size() );
541     s->isSplitter = FALSE;
542     if ( first )
543         data->list.insert( 0, s );
544     else
545         data->list.append( s );
546     if ( newHandle && isVisible() )
547         newHandle->show(); //will trigger sending of post events
548     return s;
549 }
550
551
552 /*!
553   Tells the splitter that a child widget has been inserted/removed.
554 */
555
556 void QSplitterP::childEvent( QChildEvent *c )
557 {
558     if ( c->type() == QEvent::ChildInserted ) {
559         if ( !c->child()->isWidgetType() )
560             return;
561
562         if ( ((QWidget*)c->child())->testWFlags( WType_TopLevel ) )
563             return;
564
565         QSplitterPLayoutStruct *s = data->list.first();
566         while ( s ) {
567             if ( s->wid == c->child() )
568                 return;
569             s = data->list.next();
570         }
571         addWidget( (QWidget*)c->child() );
572         recalc( isVisible() );
573
574     } else if ( c->type() == QEvent::ChildRemoved ) {
575         QSplitterPLayoutStruct *p = 0;
576         if ( data->list.count() > 1 )
577             p = data->list.at(1); //remove handle _after_ first widget.
578         QSplitterPLayoutStruct *s = data->list.first();
579         while ( s ) {
580             if ( s->wid == c->child() ) {
581                 data->list.removeRef( s );
582                 delete s;
583                 if ( p && p->isSplitter ) {
584                     data->list.removeRef( p );
585                     delete p->wid; //will call childEvent
586                     delete p;
587                 }
588                 recalcId();
589                 doResize();
590                 return;
591             }
592             p = s;
593             s = data->list.next();
594         }
595     }
596 }
597
598
599 /*!
600   Shows a rubber band at position \a p. If \a p is negative, the
601   rubber band is removed.
602 */
603
604 void QSplitterP::setRubberband( int p )
605 {
606     QPainter paint( this );
607     paint.setPen( gray );
608     paint.setBrush( gray );
609     paint.setRasterOp( XorROP );
610     QRect r = contentsRect();
611     const int rBord = 3; //Themable????
612 #if QT_VERSION < 300
613     const int sw = style().splitterWidth();
614 #else
615     int sw = style().pixelMetric(QStyle::PM_SplitterWidth, this); //qt3.x
616 #endif
617     if ( orient == Horizontal ) {
618         if ( opaqueOldPos >= 0 )
619             paint.drawRect( opaqueOldPos + sw/2 - rBord , r.y(),
620                             2*rBord, r.height() );
621         if ( p >= 0 )
622             paint.drawRect( p  + sw/2 - rBord, r.y(), 2*rBord, r.height() );
623     } else {
624         if ( opaqueOldPos >= 0 )
625             paint.drawRect( r.x(), opaqueOldPos + sw/2 - rBord,
626                             r.width(), 2*rBord );
627         if ( p >= 0 )
628             paint.drawRect( r.x(), p + sw/2 - rBord, r.width(), 2*rBord );
629     }
630     opaqueOldPos = p;
631 }
632
633
634 /*! \reimp */
635
636 bool QSplitterP::event( QEvent *e )
637 {
638     if ( e->type() == QEvent::LayoutHint || ( e->type() == QEvent::Show && data->firstShow ) ) {
639         recalc( isVisible() );
640         if ( e->type() == QEvent::Show )
641             data->firstShow = FALSE;
642     }
643     return QWidget::event( e );
644 }
645
646
647 /*!
648   Draws the splitter handle in the rectangle described by \a x, \a y,
649   \a w, \a h using painter \a p.
650   \sa QStyle::drawSplitter
651 */
652
653 void QSplitterP::drawSplitter( QPainter *p,
654                               QCOORD x, QCOORD y, QCOORD w, QCOORD h )
655 {
656 #if QT_VERSION < 300
657     style().drawSplitter( p, x, y, w, h, colorGroup(), orient );
658 #else
659     style().drawPrimitive(QStyle::PE_Splitter, p, QRect(x, y, w, h), colorGroup(),
660                           (orientation() == Qt::Horizontal ?
661                            QStyle::Style_Horizontal : 0));
662 #endif
663 }
664
665
666 /*!
667   Returns the id of the splitter to the right of or below the widget \a w,
668   or 0 if there is no such splitter.
669   (ie. it is either not in this QSplitter, or it is at the end).
670 */
671
672 int QSplitterP::idAfter( QWidget* w ) const
673 {
674     QSplitterPLayoutStruct *s = data->list.first();
675     bool seen_w = FALSE;
676     while ( s ) {
677         if ( s->isSplitter && seen_w )
678             return data->list.at();
679         if ( !s->isSplitter && s->wid == w )
680             seen_w = TRUE;
681         s = data->list.next();
682     }
683     return 0;
684 }
685
686 // VSR 21/11/02 -> ================================
687 QSplitterPHandle* QSplitterP::getHandleAfter(QWidget* w)
688 {
689   QSplitterPLayoutStruct *s = data->list.first();
690   bool seen_w = FALSE;
691   while ( s ) {
692     if ( s->isSplitter && seen_w )
693       return (QSplitterPHandle*)s->wid;
694     if ( !s->isSplitter && s->wid == w )
695       seen_w = TRUE;
696     s = data->list.next();
697   }
698   return 0;
699 }
700
701 QSplitterPHandle* QSplitterP::getHandleBefore(QWidget* w)
702 {
703   QSplitterPLayoutStruct *s = data->list.first();
704   QSplitterPHandle* h = 0;
705   while ( s ) {
706     if ( s->isSplitter )
707       h = (QSplitterPHandle*)s;
708     if ( !s->isSplitter && s->wid == w )
709       return h;
710     s = data->list.next();
711   }
712   return 0;
713 }
714 // VSR 21/11/02 <- ================================
715
716 QWidget* QSplitterP::widgetBefore( int id ) const
717 {
718     QSplitterPLayoutStruct *s = data->list.first();
719         QWidget* w;
720     while ( s ) {
721                 if ( !s->isSplitter ) {
722                         w = s->wid;
723                         if (idAfter(w) == id) return w;
724                 }
725                 s = data->list.next();
726     }
727     return 0;
728 }
729
730 QWidget* QSplitterP::widgetAfter( int id ) const
731 {
732     QSplitterPLayoutStruct *s = data->list.first();
733         bool seen_s = FALSE;
734     while ( s ) {
735         if ( !s->isSplitter && seen_s )
736             return s->wid;
737         if ( s->isSplitter && data->list.at() == id )
738             seen_s = TRUE;
739         s = data->list.next();
740     }
741     return 0;
742 }
743
744 void QSplitterP::unCompress(QWidget* w) {
745    compressed_widgets.remove(w);
746 }
747
748 void QSplitterP::compress(QWidget* w)
749 {
750   if (!isCompressed(w)) compressed_widgets.append(w);
751 }
752
753 bool QSplitterP::isCompressed(const QWidget* w) const {
754   QWidget* t = (QWidget*)w;
755   if (compressed_widgets.containsRef(t)==0) return false;
756   else return true;
757 }
758
759 /*!
760   Moves the left/top edge of the splitter handle with id \a id as
761   close as possible to \a p which is the distance from the left (or
762   top) edge of the widget.
763
764   \sa idAfter()
765 */
766 void QSplitterP::moveSplitter( QCOORD p, int id )
767 {
768     p = adjustPos( p, id );
769
770     QSplitterPLayoutStruct *s = data->list.at(id);
771     int oldP = orient == Horizontal? s->wid->x() : s->wid->y();
772     bool upLeft = p < oldP;
773
774     moveAfter( p, id, upLeft );
775     moveBefore( p-1, id-1, upLeft );
776
777     storeSizes();
778 }
779
780
781 void QSplitterP::setG( QWidget *w, int p, int s )
782 {
783     if ( orient == Horizontal )
784         w->setGeometry( p, contentsRect().y(), s, contentsRect().height() );
785     else
786         w->setGeometry( contentsRect().x(), p, contentsRect().width(), s );
787 }
788
789
790 /*!
791   Places the right/bottom edge of the widget at \a id at position \a pos.
792
793   \sa idAfter()
794 */
795
796 void QSplitterP::moveBefore( int pos, int id, bool upLeft )
797 {
798     QSplitterPLayoutStruct *s = data->list.at(id);
799     if ( !s )
800         return;
801     QWidget *w = s->wid;
802     if ( w->isHidden() ) {
803         moveBefore( pos, id-1, upLeft );
804     } else if ( s->isSplitter ) {
805         int dd = s->sizer;
806         if ( upLeft ) {
807             setG( w, pos-dd+1, dd );
808             moveBefore( pos-dd, id-1, upLeft );
809         } else {
810             moveBefore( pos-dd, id-1, upLeft );
811             setG( w, pos-dd+1, dd );
812         }
813     } else {
814         int left = pick( w->pos() );
815         int dd = pos - left + 1;
816         dd = QMAX( pick(minSize(w)), QMIN(dd, pick(maxSize(w))));
817         int newLeft = pos-dd+1;
818         setG( w, newLeft, dd );
819         if ( left != newLeft )
820             moveBefore( newLeft-1, id-1, upLeft );
821     }
822 }
823
824
825 /*!
826   Places the left/top edge of the widget at \a id at position \a pos.
827
828   \sa idAfter()
829 */
830
831 void QSplitterP::moveAfter( int pos, int id, bool upLeft )
832 {
833     QSplitterPLayoutStruct *s = id < int(data->list.count()) ?
834                                data->list.at(id) : 0;
835     if ( !s )
836         return;
837     QWidget *w = s->wid;
838     if ( w->isHidden() ) {
839         moveAfter( pos, id+1, upLeft );
840     } else if ( pick( w->pos() ) == pos ) {
841         //No need to do anything if it's already there.
842         return;
843     } else if ( s->isSplitter ) {
844         int dd = s->sizer;
845         if ( upLeft ) {
846             setG( w, pos, dd );
847             moveAfter( pos+dd, id+1, upLeft );
848         } else {
849             moveAfter( pos+dd, id+1, upLeft );
850             setG( w, pos, dd );
851         }
852     } else {
853         int right = pick( w->geometry().bottomRight() );
854         int ddd = pick( QSize(contentsRect().width(), contentsRect().height() ) ) - pos;
855         int dd = QMIN(right - pos + 1, ddd);
856         dd = QMAX( pick(minSize(w)), QMIN(dd, pick(maxSize(w))));
857         int newRight = pos+dd-1;
858         setG( w, pos, dd );
859         moveAfter( newRight+1, id+1, upLeft );
860     }
861 }
862
863
864 /*!
865   Returns the valid range of the splitter with id \a id in \a min and \a max.
866
867   \sa idAfter()
868 */
869
870 void QSplitterP::getRange( int id, int *min, int *max )
871 {
872     int minB = 0;       //before
873     int maxB = 0;
874     int minA = 0;
875     int maxA = 0;       //after
876     int n = data->list.count();
877     if ( id < 0 || id >= n )
878         return;
879     int i;
880     for ( i = 0; i < id; i++ ) {
881         QSplitterPLayoutStruct *s = data->list.at(i);
882         if ( s->isSplitter ) {
883             minB += s->sizer;
884             maxB += s->sizer;
885         } else {
886             minB += pick( minSize(s->wid) );
887             maxB += pick( maxSize(s->wid) );
888         }
889     }
890     for ( i = id; i < n; i++ ) {
891         QSplitterPLayoutStruct *s = data->list.at(i);
892         if ( s->isSplitter ) {
893             minA += s->sizer;
894             maxA += s->sizer;
895         } else {
896             minA += pick( minSize(s->wid) );
897             maxA += pick( maxSize(s->wid) );
898         }
899     }
900     QRect r = contentsRect();
901     if ( min )
902         *min = pick(r.topLeft()) + QMAX( minB, pick(r.size())-maxA );
903     if ( max )
904         *max = pick(r.topLeft()) + QMIN( maxB, pick(r.size())-minA );
905
906 }
907
908
909 /*!
910   Returns the legal position closest to \a p of the splitter with id \a id.
911
912   \sa idAfter()
913 */
914
915 int QSplitterP::adjustPos( int p, int id )
916 {
917     int min = 0;
918     int max = 0;
919     getRange( id, &min, &max );
920     p = QMAX( min, QMIN( p, max ) );
921
922     return p;
923 }
924
925
926 void QSplitterP::doResize()
927 {
928     QRect r = contentsRect();
929     int i;
930     int n = data->list.count();
931     QArray<QLayoutStruct> a( n );
932     for ( i = 0; i< n; i++ ) {
933         a[i].init();
934         QSplitterPLayoutStruct *s = data->list.at(i);
935         if ( s->wid->isHidden() ) {
936             a[i].stretch = 0;
937             a[i].sizeHint = a[i].minimumSize = 0;
938             a[i].maximumSize = 0;
939         } else if ( s->isSplitter ) {
940             a[i].stretch = 0;
941             a[i].sizeHint = a[i].minimumSize = a[i].maximumSize = s->sizer;
942             a[i].empty = FALSE;
943         } else if ( s->mode == KeepSize ) {
944             a[i].stretch = 0;
945             a[i].minimumSize = pick( minSize(s->wid) );
946             a[i].sizeHint = s->sizer;
947             a[i].maximumSize = pick( maxSize(s->wid) );
948             a[i].empty = FALSE;
949         } else if ( s->mode == FollowSizeHint ) {
950             a[i].stretch = 0;
951             a[i].minimumSize = a[i].sizeHint = pick( s->wid->sizeHint() );
952             a[i].maximumSize = pick( maxSize(s->wid) );
953             a[i].empty = FALSE;
954         } else { //proportional
955             a[i].stretch = s->sizer;
956             a[i].maximumSize = pick( maxSize(s->wid) );
957             a[i].sizeHint = a[i].minimumSize = pick( minSize(s->wid) );
958             a[i].empty = FALSE;
959         }
960     }
961
962     qGeomCalc( a, 0, n, pick( r.topLeft() ), pick( r.size() ), 0 );
963     for ( i = 0; i< n; i++ ) {
964         QSplitterPLayoutStruct *s = data->list.at(i);
965         if ( orient == Horizontal )
966             s->wid->setGeometry( a[i].pos, r.top(), a[i].size, r.height() );
967         else
968             s->wid->setGeometry( r.left(), a[i].pos, r.width(), a[i].size );
969     }
970
971 }
972
973
974 void QSplitterP::recalc( bool update )
975 {
976     int fi = 2*frameWidth();
977     int maxl = fi;
978     int minl = fi;
979     int maxt = QWIDGETSIZE_MAX;
980     int mint = fi;
981     int n = data->list.count();
982     bool first = TRUE;
983     /*
984       The splitter before a hidden widget is always hidden.
985       The splitter before the first visible widget is hidden.
986       The splitter before any other visible widget is visible.
987     */
988     for ( int i = 0; i< n; i++ ) {
989         QSplitterPLayoutStruct *s = data->list.at(i);
990         if ( !s->isSplitter ) {
991             QSplitterPLayoutStruct *p = (i > 0) ? p = data->list.at( i-1 ) : 0;
992             if ( p && p->isSplitter )
993                 if ( first || s->wid->isHidden() )
994                     p->wid->hide(); //may trigger new recalc
995                 else
996                     p->wid->show(); //may trigger new recalc
997             if ( !s->wid->isHidden() )
998                 first = FALSE;
999         }
1000     }
1001
1002     bool empty=TRUE;
1003     for ( int j = 0; j< n; j++ ) {
1004         QSplitterPLayoutStruct *s = data->list.at(j);
1005         if ( !s->wid->isHidden() ) {
1006             empty = FALSE;
1007             if ( s->isSplitter ) {
1008                 minl += s->sizer;
1009                 maxl += s->sizer;
1010             } else {
1011                 QSize minS = minSize(s->wid);
1012                 minl += pick( minS );
1013                 maxl += pick( maxSize(s->wid) );
1014                 mint = QMAX( mint, trans( minS ));
1015                 int tm = trans( maxSize(s->wid) );
1016                 if ( tm > 0 )
1017                     maxt = QMIN( maxt, tm );
1018             }
1019         }
1020     }
1021     if ( empty )
1022         maxl = maxt = 0;
1023     else
1024         maxl = QMIN( maxl, QWIDGETSIZE_MAX );
1025     if ( maxt < mint )
1026         maxt = mint;
1027
1028     if ( orient == Horizontal ) {
1029         setMaximumSize( maxl, maxt );
1030         setMinimumSize( minl, mint );
1031     } else {
1032         setMaximumSize( maxt, maxl );
1033         setMinimumSize( mint, minl );
1034     }
1035     if ( update )
1036         doResize();
1037 }
1038
1039 /*! \enum QSplitter::ResizeMode
1040
1041   This enum type describes how QSplitter will resize each of its child widgets.  The currently defined values are: <ul>
1042
1043   <li> \c Stretch - the widget will be resized when the splitter
1044   itself is resized.
1045
1046   <li> \c KeepSize - QSplitter will try to keep this widget's size
1047   unchanged.
1048
1049   <li> \c FollowSizeHint - QSplitter will resize the widget when its
1050   size hint changes.
1051
1052   </ul>
1053
1054 */
1055
1056 /*!
1057   Sets resize mode of \a w to \a mode.
1058
1059   \sa ResizeMode
1060 */
1061
1062 void QSplitterP::setResizeMode( QWidget *w, ResizeMode mode )
1063 {
1064     processChildEvents();
1065     QSplitterPLayoutStruct *s = data->list.first();
1066     while ( s ) {
1067         if ( s->wid == w  ) {
1068             s->mode = mode;
1069             return;
1070         }
1071         s = data->list.next();
1072     }
1073     s = addWidget( w, TRUE );
1074     s->mode = mode;
1075 }
1076
1077
1078 /*!
1079   Returns TRUE if opaque resize is on, FALSE otherwise.
1080
1081   \sa setOpaqueResize()
1082 */
1083
1084 bool QSplitterP::opaqueResize() const
1085 {
1086     return data->opaque;
1087 }
1088
1089
1090 /*!
1091   Sets opaque resize to \a on. Opaque resize is initially turned off.
1092
1093   \sa opaqueResize()
1094 */
1095
1096 void QSplitterP::setOpaqueResize( bool on )
1097 {
1098     data->opaque = on;
1099 }
1100
1101
1102 /*!
1103   Moves \a w to the leftmost/top position.
1104 */
1105
1106 void QSplitterP::moveToFirst( QWidget *w )
1107 {
1108     processChildEvents();
1109     bool found = FALSE;
1110     QSplitterPLayoutStruct *s = data->list.first();
1111     while ( s ) {
1112         if ( s->wid == w  ) {
1113             found = TRUE;
1114             QSplitterPLayoutStruct *p = data->list.prev();
1115             if ( p ) { // not already at first place
1116                 data->list.take(); //take p
1117                 data->list.take(); // take s
1118                 data->list.insert( 0, p );
1119                 data->list.insert( 0, s );
1120             }
1121             break;
1122         }
1123         s = data->list.next();
1124     }
1125      if ( !found )
1126         addWidget( w, TRUE );
1127      recalcId();
1128 }
1129
1130
1131 /*!
1132   Moves \a w to the rightmost/bottom position.
1133 */
1134
1135 void QSplitterP::moveToLast( QWidget *w )
1136 {
1137     processChildEvents();
1138     bool found = FALSE;
1139     QSplitterPLayoutStruct *s = data->list.first();
1140     while ( s ) {
1141         if ( s->wid == w  ) {
1142             found = TRUE;
1143             data->list.take(); // take s
1144             QSplitterPLayoutStruct *p = data->list.current();
1145             if ( p ) { // the splitter handle after s
1146                 data->list.take(); //take p
1147                 data->list.append( p );
1148             }
1149             data->list.append( s );
1150             break;
1151         }
1152         s = data->list.next();
1153     }
1154      if ( !found )
1155         addWidget( w);
1156      recalcId();
1157 }
1158
1159
1160 void QSplitterP::recalcId()
1161 {
1162     int n = data->list.count();
1163     for ( int i = 0; i < n; i++ ) {
1164         QSplitterPLayoutStruct *s = data->list.at(i);
1165         if ( s->isSplitter )
1166             ((QSplitterPHandle*)s->wid)->setId(i);
1167     }
1168 }
1169
1170
1171 /*!\reimp
1172 */
1173 QSize QSplitterP::sizeHint() const
1174 {
1175     constPolish();
1176     int l = 0;
1177     int t = 0;
1178     if ( children() ) {
1179         const QObjectList * c = children();
1180         QObjectListIt it( *c );
1181         QObject * o;
1182
1183         while( (o=it.current()) != 0 ) {
1184             ++it;
1185             if ( o->isWidgetType() &&
1186                  !((QWidget*)o)->isHidden() ) {
1187                 QSize s = ((QWidget*)o)->sizeHint();
1188                 if ( s.isValid() ) {
1189                     l += pick( s );
1190                     t = QMAX( t, trans( s ) );
1191                 }
1192             }
1193         }
1194     }
1195     return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
1196 }
1197
1198
1199 /*!
1200 \reimp
1201 */
1202
1203 QSize QSplitterP::minimumSizeHint() const
1204 {
1205     constPolish();
1206     int l = 0;
1207     int t = 0;
1208     if ( children() ) {
1209         const QObjectList * c = children();
1210         QObjectListIt it( *c );
1211         QObject * o;
1212
1213         while( (o=it.current()) != 0 ) {
1214             ++it;
1215             if ( o->isWidgetType() &&
1216                  !((QWidget*)o)->isHidden() ) {
1217                 QSize s = minSize((QWidget*)o);
1218                 if ( s.isValid() ) {
1219                     l += pick( s );
1220                     t = QMAX( t, trans( s ) );
1221                 }
1222             }
1223         }
1224     }
1225     return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
1226 }
1227
1228
1229
1230 /*!\reimp
1231 */
1232 QSizePolicy QSplitterP::sizePolicy() const
1233 {
1234     return QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
1235 }
1236
1237
1238 /*!
1239   Calculates stretch parameters from current sizes
1240 */
1241
1242 void QSplitterP::storeSizes()
1243 {
1244     QSplitterPLayoutStruct *s = data->list.first();
1245     while ( s ) {
1246         if ( !s->isSplitter )
1247             s->sizer = pick( s->wid->size() );
1248         s = data->list.next();
1249     }
1250 }
1251
1252
1253 #if 0 // ### remove this code ASAP
1254
1255 /*!
1256   Hides \a w if \a hide is TRUE, and updates the splitter.
1257
1258   \warning Due to a limitation in the current implementation,
1259   calling QWidget::hide() will not work.
1260 */
1261
1262 void QSplitterP::setHidden( QWidget *w, bool hide )
1263 {
1264     if ( w == w1 ) {
1265         w1show = !hide;
1266     } else if ( w == w2 ) {
1267         w2show = !hide;
1268     } else {
1269 #ifdef CHECK_RANGE
1270         qWarning( "QSplitterP::setHidden(), unknown widget" );
1271 #endif
1272         return;
1273     }
1274     if ( hide )
1275         w->hide();
1276     else
1277         w->show();
1278     recalc( TRUE );
1279 }
1280
1281
1282 /*!
1283   Returns the hidden status of \a w
1284 */
1285
1286 bool QSplitterP::isHidden( QWidget *w ) const
1287 {
1288     if ( w == w1 )
1289         return !w1show;
1290      else if ( w == w2 )
1291         return !w2show;
1292 #ifdef CHECK_RANGE
1293     else
1294         qWarning( "QSplitterP::isHidden(), unknown widget" );
1295 #endif
1296     return FALSE;
1297 }
1298 #endif
1299
1300
1301 /*!
1302   Returns a list of the size parameters of all the widgets in this
1303   splitter.
1304
1305   Giving the values to setSizes() will give a splitter with the same
1306   layout as this one.
1307 */
1308
1309 QValueList<int> QSplitterP::sizes() const
1310 {
1311     if ( !testWState(WState_Polished) ) {
1312         QWidget* that = (QWidget*) this;
1313         that->polish();
1314     }
1315     QValueList<int> list;
1316     QSplitterPLayoutStruct *s = data->list.first();
1317     while ( s ) {
1318         if ( !s->isSplitter )
1319             list.append( s->sizer );
1320         s = data->list.next();
1321     }
1322     return list;
1323 }
1324
1325
1326
1327 /*!
1328   Sets the size parameters to the values given in \a list.
1329   If the splitterP is horizontal, the values set the sizes from
1330   left to right. If it is vertical, the sizes are applied from
1331   top to bottom.
1332   Extra values in \a list are ignored.
1333
1334   If \a list contains too few values, the result is undefined
1335   but the program will still be well-behaved.
1336 */
1337
1338 void QSplitterP::setSizes( QValueList<int> list )
1339 {
1340     processChildEvents();
1341     QValueList<int>::Iterator it = list.begin();
1342     QSplitterPLayoutStruct *s = data->list.first();
1343     while ( s && it != list.end() ) {
1344         if ( !s->isSplitter ) {
1345             s->sizer = *it;
1346             ++it;
1347         }
1348         s = data->list.next();
1349     }
1350     doResize();
1351 }
1352
1353
1354 /*!
1355   Gets all posted child events, ensuring that the internal state of
1356   the splitter is consistent with the programmer's idea.
1357 */
1358
1359 void QSplitterP::processChildEvents()
1360 {
1361     QApplication::sendPostedEvents( this, QEvent::ChildInserted );
1362 }
1363
1364
1365 /*!
1366   \reimp
1367 */
1368
1369 void QSplitterP::styleChange( QStyle& old )
1370 {
1371 #if QT_VERSION < 300
1372     int sw = style().splitterWidth();
1373 #else
1374     int sw = style().pixelMetric(QStyle::PM_SplitterWidth, this); //qt3.x
1375 #endif
1376     QSplitterPLayoutStruct *s = data->list.first();
1377     while ( s ) {
1378         if ( s->isSplitter )
1379             s->sizer = sw;
1380         s = data->list.next();
1381     }
1382     doResize();
1383     QFrame::styleChange( old );
1384 }
1385 #endif
1386
1387 bool QSplitterP::isCompressEnabled() const
1388
1389         return compress_flag; 
1390 }
1391
1392 void QSplitterP::setCompressEnabled(bool on) {
1393         compress_flag = on;
1394 }
1395
1396 void QSplitterP::updateSplitterHandles() const {
1397     QSplitterPLayoutStruct *s = data->list.first();
1398     while ( s ) {
1399         if ( s->isSplitter )
1400             ((QSplitterPHandle*)s->wid)->updateButtonState();
1401         s = data->list.next();
1402     }
1403 }