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