kdgantt

KDGanttMinimizeSplitter.cpp
1/*
2 $Id$
3*/
4
5/****************************************************************************
6 ** Copyright (C) 2002-2004 Klarälvdalens Datakonsult AB. All rights reserved.
7 **
8 ** This file is part of the KDGantt library.
9 **
10 ** This file may be distributed and/or modified under the terms of the
11 ** GNU General Public License version 2 as published by the Free Software
12 ** Foundation and appearing in the file LICENSE.GPL included in the
13 ** packaging of this file.
14 **
15 ** Licensees holding valid commercial KDGantt licenses may use this file in
16 ** accordance with the KDGantt Commercial License Agreement provided with
17 ** the Software.
18 **
19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21 **
22 ** See http://www.klaralvdalens-datakonsult.se/Public/products/ for
23 ** information about KDGantt Commercial License Agreements.
24 **
25 ** Contact info@klaralvdalens-datakonsult.se if any conditions of this
26 ** licensing are not clear to you.
27 **
28 ** As a special exception, permission is given to link this program
29 ** with any edition of TQt, and distribute the resulting executable,
30 ** without including the source code for TQt in the source distribution.
31 **
32 **********************************************************************/
33
34#include "KDGanttMinimizeSplitter.h"
35#ifndef TQT_NO_SPLITTER
36
37#include "tqpainter.h"
38#include "tqdrawutil.h"
39#include "tqbitmap.h"
40#include "tqptrlist.h"
41#include "tqmemarray.h"
42#include "tqlayout.h"
43#include "private/tqlayoutengine_p.h"
44#include "tqobjectlist.h"
45#include "tqstyle.h"
46#include "tqapplication.h" //sendPostedEvents
47#include <tqvaluelist.h>
48#include <tqcursor.h>
49
50#include "KDGanttMinimizeSplitter.moc"
51
52#ifndef DOXYGEN_SKIP_INTERNAL
53
54static int mouseOffset;
55static int opaqueOldPos = -1; //### there's only one mouse, but this is a bit risky
56
57
58KDGanttSplitterHandle::KDGanttSplitterHandle( TQt::Orientation o,
59 KDGanttMinimizeSplitter *parent, const char * name )
60 : TQWidget( parent, name ), _activeButton( 0 ), _collapsed( false )
61{
62 s = parent;
63 setOrientation(o);
64 setMouseTracking( true );
65}
66
67TQSize KDGanttSplitterHandle::sizeHint() const
68{
69 return TQSize(8,8);
70}
71
72void KDGanttSplitterHandle::setOrientation( TQt::Orientation o )
73{
74 orient = o;
75#ifndef TQT_NO_CURSOR
76 if ( o == TQt::Horizontal )
77 setCursor( splitHCursor );
78 else
79 setCursor( splitVCursor );
80#endif
81}
82
83
84void KDGanttSplitterHandle::mouseMoveEvent( TQMouseEvent *e )
85{
86 updateCursor( e->pos() );
87 if ( !(e->state()&TQt::LeftButton) )
88 return;
89
90 if ( _activeButton != 0)
91 return;
92
93 TQCOORD pos = s->pick(parentWidget()->mapFromGlobal(e->globalPos()))
94 - mouseOffset;
95 if ( opaque() ) {
96 s->moveSplitter( pos, id() );
97 } else {
98 int min = pos; int max = pos;
99 s->getRange( id(), &min, &max );
100 s->setRubberband( TQMAX( min, TQMIN(max, pos )));
101 }
102 _collapsed = false;
103}
104
105void KDGanttSplitterHandle::mousePressEvent( TQMouseEvent *e )
106{
107 if ( e->button() == TQt::LeftButton ) {
108 _activeButton = onButton( e->pos() );
109 mouseOffset = s->pick(e->pos());
110 if ( _activeButton != 0)
111 repaint();
112 updateCursor( e->pos() );
113 }
114}
115
116void KDGanttSplitterHandle::updateCursor( const TQPoint& p)
117{
118 if ( onButton( p ) != 0 ) {
119 setCursor( arrowCursor );
120 }
121 else {
122 if ( orient == TQt::Horizontal )
123 setCursor( splitHCursor );
124 else
125 setCursor( splitVCursor );
126 }
127}
128
129
130void KDGanttSplitterHandle::mouseReleaseEvent( TQMouseEvent *e )
131{
132 if ( _activeButton != 0 ) {
133 if ( onButton( e->pos() ) == _activeButton )
134 {
135 int pos;
136 int min, max;
137 if ( !_collapsed ) {
138 s->expandPos( id(), &min, &max );
139 if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Left
140 || s->minimizeDirection() == KDGanttMinimizeSplitter::Up ) {
141 pos = min;
142 }
143 else {
144 pos = max;
145 }
146
147 _origPos = s->pick(mapToParent( TQPoint( 0,0 ) ));
148 s->moveSplitter( pos, id() );
149 _collapsed = true;
150 }
151 else {
152 s->moveSplitter( _origPos, id() );
153 _collapsed = false;
154 }
155
156 }
157 _activeButton = 0;
158 updateCursor( e->pos() );
159 }
160 else {
161 if ( !opaque() && e->button() == TQt::LeftButton ) {
162 TQCOORD pos = s->pick(parentWidget()->mapFromGlobal(e->globalPos()))
163 - mouseOffset;
164 s->setRubberband( -1 );
165 s->moveSplitter( pos, id() );
166 }
167 }
168 repaint();
169}
170
171int KDGanttSplitterHandle::onButton( const TQPoint& p )
172{
173 TQValueList<TQPointArray> list = buttonRegions();
174 int index = 1;
175 for( TQValueList<TQPointArray>::Iterator it = list.begin(); it != list.end(); ++it ) {
176 TQRect rect = (*it).boundingRect();
177 rect.setLeft( rect.left()- 4 );
178 rect.setRight( rect.right() + 4);
179 rect.setTop( rect.top()- 4 );
180 rect.setBottom( rect.bottom() + 4);
181 if ( rect.contains( p ) ) {
182 return index;
183 }
184 index++;
185 }
186 return 0;
187}
188
189
190TQValueList<TQPointArray> KDGanttSplitterHandle::buttonRegions()
191{
192 TQValueList<TQPointArray> list;
193
194 int sw = 8;
195 int voffset[] = { (int) -sw*3, (int) sw*3 };
196 for ( int i = 0; i < 2; i++ ) {
197 TQPointArray arr;
198 if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Right ||
199 _collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Left) {
200 int mid = height()/2 + voffset[i];
201 arr.setPoints( 3,
202 1, mid - sw + 4,
203 sw-3, mid,
204 1, mid + sw -4);
205 }
206 else if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Left ||
207 _collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Right ) {
208 int mid = height()/2 + voffset[i];
209 arr.setPoints( 3,
210 sw-4, mid - sw + 4,
211 0, mid,
212 sw-4, mid + sw - 4);
213 }
214 else if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Up ||
215 _collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Down) {
216 int mid = width()/2 + voffset[i];
217 arr.setPoints( 3,
218 mid - sw + 4, sw-4,
219 mid, 0,
220 mid + sw - 4, sw-4 );
221 }
222 else if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Down ||
223 _collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Up ) {
224 int mid = width()/2 + voffset[i];
225 arr.setPoints( 3,
226 mid - sw + 4, 1,
227 mid, sw-3,
228 mid + sw -4, 1);
229 }
230 list.append( arr );
231 }
232 return list;
233}
234
235void KDGanttSplitterHandle::paintEvent( TQPaintEvent * )
236{
237 TQPixmap buffer( size() );
238 TQPainter p( &buffer );
239
240 // Draw the splitter rectangle
241 p.setBrush( colorGroup().background() );
242 p.setPen( colorGroup().foreground() );
243 p.drawRect( rect() );
244 parentWidget()->style().drawPrimitive( TQStyle::PE_Panel, &p, rect(),
245 parentWidget()->colorGroup());
246
247 int sw = 8; // Hardcoded, given I didn't use styles anymore, I didn't like to use their size
248
249 // arrow color
250 TQColor col = colorGroup().background().dark( 200 );
251 p.setBrush( col );
252 p.setPen( col );
253
254 TQValueList<TQPointArray> list = buttonRegions();
255 int index = 1;
256 for ( TQValueList<TQPointArray>::Iterator it = list.begin(); it != list.end(); ++it ) {
257 if ( index == _activeButton ) {
258 p.save();
259 p.translate( parentWidget()->style().pixelMetric( TQStyle::PM_ButtonShiftHorizontal ),
260 parentWidget()->style().pixelMetric( TQStyle::PM_ButtonShiftVertical ) );
261 p.drawPolygon( *it, true );
262 p.restore();
263 }
264 else {
265 p.drawPolygon( *it, true );
266 }
267 index++;
268 }
269
270 // Draw the lines between the arrows
271 if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Left ||
272 s->minimizeDirection() == KDGanttMinimizeSplitter::Right ) {
273 int mid = height()/2;
274 p.drawLine ( 2, mid - sw, 2, mid + sw );
275 p.drawLine ( 4, mid - sw, 4, mid + sw );
276 }
277 else if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Up ||
278 s->minimizeDirection() == KDGanttMinimizeSplitter::Down ) {
279 int mid = width()/2;
280 p.drawLine( mid -sw, 2, mid +sw, 2 );
281 p.drawLine( mid -sw, 4, mid +sw, 4 );
282 }
283 bitBlt( this, 0, 0, &buffer );
284}
285
286class TQSplitterLayoutStruct
287{
288public:
290 TQCOORD sizer;
291 bool isSplitter;
292 TQWidget *wid;
293};
294
295class TQSplitterData
296{
297public:
298 TQSplitterData() : opaque( FALSE ), firstShow( TRUE ) {}
299
300 TQPtrList<TQSplitterLayoutStruct> list;
301 bool opaque;
302 bool firstShow;
303};
304
305void kdganttGeomCalc( TQMemArray<TQLayoutStruct> &chain, int start, int count, int pos,
306 int space, int spacer );
307#endif // DOXYGEN_SKIP_INTERNAL
308
309
359static TQSize minSize( const TQWidget* /*w*/ )
360{
361 return TQSize(0,0);
362}
363
364// This is the original version of minSize
365static TQSize minSizeHint( const TQWidget* w )
366{
367 TQSize min = w->minimumSize();
368 TQSize s;
369 if ( min.height() <= 0 || min.width() <= 0 )
370 s = w->minimumSizeHint();
371 if ( min.height() > 0 )
372 s.setHeight( min.height() );
373 if ( min.width() > 0 )
374 s.setWidth( min.width() );
375 return s.expandedTo(TQSize(0,0));
376}
377
378
379
384KDGanttMinimizeSplitter::KDGanttMinimizeSplitter( TQWidget *parent, const char *name )
385 :TQFrame(parent,name,WPaintUnclipped)
386{
387 orient = TQt::Horizontal;
388 init();
389}
390
395KDGanttMinimizeSplitter::KDGanttMinimizeSplitter( TQt::Orientation o, TQWidget *parent, const char *name )
396 :TQFrame(parent,name,WPaintUnclipped)
397{
398 orient = o;
399 init();
400}
401
406{
407 data->list.setAutoDelete( TRUE );
408 delete data;
409}
410
411
412void KDGanttMinimizeSplitter::init()
413{
414 data = new TQSplitterData;
415 if ( orient == TQt::Horizontal )
416 setSizePolicy( TQSizePolicy(TQSizePolicy::Expanding,TQSizePolicy::Minimum) );
417 else
418 setSizePolicy( TQSizePolicy(TQSizePolicy::Minimum,TQSizePolicy::Expanding) );
419}
420
421
422
430{
431 if ( orient == o )
432 return;
433 orient = o;
434
435 if ( orient == TQt::Horizontal )
436 setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum ) );
437 else
438 setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Expanding ) );
439
440 TQSplitterLayoutStruct *s = data->list.first();
441 while ( s ) {
442 if ( s->isSplitter )
443 ((KDGanttSplitterHandle*)s->wid)->setOrientation( o );
444 s = data->list.next(); // ### next at end of loop, no iterator
445 }
446 recalc( isVisible() );
447}
448
449
454{
455 doResize();
456}
457
458
459/*
460 Inserts the widget \a w at the end (or at the beginning if \a first
461 is TRUE) of the splitter's list of widgets.
462
463 It is the responsibility of the caller of this function to make sure
464 that \a w is not already in the splitter and to call recalcId if
465 needed. (If \a first is TRUE, then recalcId is very probably
466 needed.)
467*/
468TQSplitterLayoutStruct *KDGanttMinimizeSplitter::addWidget( TQWidget *w, bool first )
469{
470 TQSplitterLayoutStruct *s;
471 KDGanttSplitterHandle *newHandle = 0;
472 if ( data->list.count() > 0 ) {
473 s = new TQSplitterLayoutStruct;
474 s->mode = KeepSize;
475 TQString tmp = "qt_splithandle_";
476 tmp += w->name();
477 newHandle = new KDGanttSplitterHandle( orientation(), this, tmp.latin1() );
478 s->wid = newHandle;
479 newHandle->setId(data->list.count());
480 s->isSplitter = TRUE;
481 s->sizer = pick( newHandle->sizeHint() );
482 if ( first )
483 data->list.insert( 0, s );
484 else
485 data->list.append( s );
486 }
487 s = new TQSplitterLayoutStruct;
488 s->mode = Stretch;
489 s->wid = w;
490 if ( !testWState( WState_Resized ) && w->sizeHint().isValid() )
491 s->sizer = pick( w->sizeHint() );
492 else
493 s->sizer = pick( w->size() );
494 s->isSplitter = FALSE;
495 if ( first )
496 data->list.insert( 0, s );
497 else
498 data->list.append( s );
499 if ( newHandle && isVisible() )
500 newHandle->show(); //will trigger sending of post events
501 return s;
502}
503
504
510{
511 if ( c->type() == TQEvent::ChildInserted ) {
512 if ( !c->child()->isWidgetType() )
513 return;
514
515 if ( ((TQWidget*)c->child())->testWFlags( WType_TopLevel ) )
516 return;
517
518 TQSplitterLayoutStruct *s = data->list.first();
519 while ( s ) {
520 if ( s->wid == c->child() )
521 return;
522 s = data->list.next();
523 }
524 addWidget( (TQWidget*)c->child() );
525 recalc( isVisible() );
526
527 } else if ( c->type() == TQEvent::ChildRemoved ) {
528 TQSplitterLayoutStruct *p = 0;
529 if ( data->list.count() > 1 )
530 p = data->list.at(1); //remove handle _after_ first widget.
531 TQSplitterLayoutStruct *s = data->list.first();
532 while ( s ) {
533 if ( s->wid == c->child() ) {
534 data->list.removeRef( s );
535 delete s;
536 if ( p && p->isSplitter ) {
537 data->list.removeRef( p );
538 delete p->wid; //will call childEvent
539 delete p;
540 }
541 recalcId();
542 doResize();
543 return;
544 }
545 p = s;
546 s = data->list.next();
547 }
548 }
549}
550
551
557{
558 TQPainter paint( this );
559 paint.setPen( gray );
560 paint.setBrush( gray );
561 paint.setRasterOp( XorROP );
562 TQRect r = contentsRect();
563 const int rBord = 3; //Themable????
564 int sw = style().pixelMetric(TQStyle::PM_SplitterWidth, this);
565 if ( orient == TQt::Horizontal ) {
566 if ( opaqueOldPos >= 0 )
567 paint.drawRect( opaqueOldPos + sw/2 - rBord , r.y(),
568 2*rBord, r.height() );
569 if ( p >= 0 )
570 paint.drawRect( p + sw/2 - rBord, r.y(), 2*rBord, r.height() );
571 } else {
572 if ( opaqueOldPos >= 0 )
573 paint.drawRect( r.x(), opaqueOldPos + sw/2 - rBord,
574 r.width(), 2*rBord );
575 if ( p >= 0 )
576 paint.drawRect( r.x(), p + sw/2 - rBord, r.width(), 2*rBord );
577 }
578 opaqueOldPos = p;
579}
580
581
584{
585 if ( e->type() == TQEvent::LayoutHint || ( e->type() == TQEvent::Show && data->firstShow ) ) {
586 recalc( isVisible() );
587 if ( e->type() == TQEvent::Show )
588 data->firstShow = FALSE;
589 }
590 return TQWidget::event( e );
591}
592
593
602 TQCOORD x, TQCOORD y, TQCOORD w, TQCOORD h )
603{
604 style().drawPrimitive(TQStyle::PE_Splitter, p, TQRect(x, y, w, h), colorGroup(),
605 (orientation() == TQt::Horizontal ?
606 TQStyle::Style_Horizontal : 0));
607}
608
609
615int KDGanttMinimizeSplitter::idAfter( TQWidget* w ) const
616{
617 TQSplitterLayoutStruct *s = data->list.first();
618 bool seen_w = FALSE;
619 while ( s ) {
620 if ( s->isSplitter && seen_w )
621 return data->list.at();
622 if ( !s->isSplitter && s->wid == w )
623 seen_w = TRUE;
624 s = data->list.next();
625 }
626 return 0;
627}
628
629
643{
644 p = adjustPos( p, id );
645
646 TQSplitterLayoutStruct *s = data->list.at(id);
647 int oldP = orient == TQt::Horizontal ? s->wid->x() : s->wid->y();
648 bool upLeft;
649 if ( TQApplication::reverseLayout() && orient == TQt::Horizontal ) {
650 p += s->wid->width();
651 upLeft = p > oldP;
652 } else
653 upLeft = p < oldP;
654
655 moveAfter( p, id, upLeft );
656 moveBefore( p-1, id-1, upLeft );
657
658 storeSizes();
659}
660
661
662void KDGanttMinimizeSplitter::setG( TQWidget *w, int p, int s, bool isSplitter )
663{
664 if ( orient == TQt::Horizontal ) {
665 if ( TQApplication::reverseLayout() && orient == TQt::Horizontal && !isSplitter )
666 p = contentsRect().width() - p - s;
667 w->setGeometry( p, contentsRect().y(), s, contentsRect().height() );
668 } else
669 w->setGeometry( contentsRect().x(), p, contentsRect().width(), s );
670}
671
672
673/*
674 Places the right/bottom edge of the widget at \a id at position \a pos.
675
676 \sa idAfter()
677*/
678void KDGanttMinimizeSplitter::moveBefore( int pos, int id, bool upLeft )
679{
680 if( id < 0 )
681 return;
682 TQSplitterLayoutStruct *s = data->list.at(id);
683 if ( !s )
684 return;
685 TQWidget *w = s->wid;
686 if ( w->isHidden() ) {
687 moveBefore( pos, id-1, upLeft );
688 } else if ( s->isSplitter ) {
689 int pos1, pos2;
690 int dd = s->sizer;
691 if( TQApplication::reverseLayout() && orient == TQt::Horizontal ) {
692 pos1 = pos;
693 pos2 = pos + dd;
694 } else {
695 pos2 = pos - dd;
696 pos1 = pos2 + 1;
697 }
698 if ( upLeft ) {
699 setG( w, pos1, dd, TRUE );
700 moveBefore( pos2, id-1, upLeft );
701 } else {
702 moveBefore( pos2, id-1, upLeft );
703 setG( w, pos1, dd, TRUE );
704 }
705 } else {
706 int dd, newLeft, nextPos;
707 if( TQApplication::reverseLayout() && orient == TQt::Horizontal ) {
708 dd = w->geometry().right() - pos;
709 dd = TQMAX( pick(minSize(w)), TQMIN(dd, pick(w->maximumSize())));
710 newLeft = pos+1;
711 nextPos = newLeft + dd;
712 } else {
713 dd = pos - pick( w->pos() ) + 1;
714 dd = TQMAX( pick(minSize(w)), TQMIN(dd, pick(w->maximumSize())));
715 newLeft = pos-dd+1;
716 nextPos = newLeft - 1;
717 }
718 setG( w, newLeft, dd, TRUE );
719 moveBefore( nextPos, id-1, upLeft );
720 }
721}
722
723
724/*
725 Places the left/top edge of the widget at \a id at position \a pos.
726
727 \sa idAfter()
728*/
729void KDGanttMinimizeSplitter::moveAfter( int pos, int id, bool upLeft )
730{
731 TQSplitterLayoutStruct *s = id < int(data->list.count()) ?
732 data->list.at(id) : 0;
733 if ( !s )
734 return;
735 TQWidget *w = s->wid;
736 if ( w->isHidden() ) {
737 moveAfter( pos, id+1, upLeft );
738 } else if ( pick( w->pos() ) == pos ) {
739 //No need to do anything if it's already there.
740 return;
741 } else if ( s->isSplitter ) {
742 int dd = s->sizer;
743 int pos1, pos2;
744 if( TQApplication::reverseLayout() && orient == TQt::Horizontal ) {
745 pos2 = pos - dd;
746 pos1 = pos2 + 1;
747 } else {
748 pos1 = pos;
749 pos2 = pos + dd;
750 }
751 if ( upLeft ) {
752 setG( w, pos1, dd, TRUE );
753 moveAfter( pos2, id+1, upLeft );
754 } else {
755 moveAfter( pos2, id+1, upLeft );
756 setG( w, pos1, dd, TRUE );
757 }
758 } else {
759 int left = pick( w->pos() );
760 int right, dd,/* newRight,*/ newLeft, nextPos;
761 if ( TQApplication::reverseLayout() && orient == TQt::Horizontal ) {
762 dd = pos - left + 1;
763 dd = TQMAX( pick(minSize(w)), TQMIN(dd, pick(w->maximumSize())));
764 newLeft = pos-dd+1;
765 nextPos = newLeft - 1;
766 } else {
767 right = pick( w->geometry().bottomRight() );
768 dd = right - pos + 1;
769 dd = TQMAX( pick(minSize(w)), TQMIN(dd, pick(w->maximumSize())));
770 /*newRight = pos+dd-1;*/
771 newLeft = pos;
772 nextPos = newLeft + dd;
773 }
774 setG( w, newLeft, dd, TRUE );
775 /*if( right != newRight )*/
776 moveAfter( nextPos, id+1, upLeft );
777 }
778}
779
780
781void KDGanttMinimizeSplitter::expandPos( int id, int* min, int* max )
782{
783 TQSplitterLayoutStruct *s = data->list.at(id-1);
784 TQWidget* w = s->wid;
785 *min = pick( w->mapToParent( TQPoint(0,0) ) );
786
787 if ( (uint) id == data->list.count() ) {
788 pick( size() );
789 }
790 else {
791 TQSplitterLayoutStruct *s = data->list.at(id+1);
792 TQWidget* w = s->wid;
793 *max = pick( w->mapToParent( TQPoint( w->width(), w->height() ) ) ) -8;
794 }
795}
796
797
804void KDGanttMinimizeSplitter::getRange( int id, int *min, int *max )
805{
806 int minB = 0; //before
807 int maxB = 0;
808 int minA = 0;
809 int maxA = 0; //after
810 int n = data->list.count();
811 if ( id < 0 || id >= n )
812 return;
813 int i;
814 for ( i = 0; i < id; i++ ) {
815 TQSplitterLayoutStruct *s = data->list.at(i);
816 if ( s->wid->isHidden() ) {
817 //ignore
818 } else if ( s->isSplitter ) {
819 minB += s->sizer;
820 maxB += s->sizer;
821 } else {
822 minB += pick( minSize(s->wid) );
823 maxB += pick( s->wid->maximumSize() );
824 }
825 }
826 for ( i = id; i < n; i++ ) {
827 TQSplitterLayoutStruct *s = data->list.at(i);
828 if ( s->wid->isHidden() ) {
829 //ignore
830 } else if ( s->isSplitter ) {
831 minA += s->sizer;
832 maxA += s->sizer;
833 } else {
834 minA += pick( minSize(s->wid) );
835 maxA += pick( s->wid->maximumSize() );
836 }
837 }
838 TQRect r = contentsRect();
839 if ( orient == TQt::Horizontal && TQApplication::reverseLayout() ) {
840 int splitterWidth = style().pixelMetric(TQStyle::PM_SplitterWidth, this);
841 if ( min )
842 *min = pick(r.topRight()) - TQMIN( maxB, pick(r.size())-minA ) - splitterWidth;
843 if ( max )
844 *max = pick(r.topRight()) - TQMAX( minB, pick(r.size())-maxA ) - splitterWidth;
845 } else {
846 if ( min )
847 *min = pick(r.topLeft()) + TQMAX( minB, pick(r.size())-maxA );
848 if ( max )
849 *max = pick(r.topLeft()) + TQMIN( maxB, pick(r.size())-minA );
850 }
851}
852
853
861{
862 int min = 0;
863 int max = 0;
864 getRange( id, &min, &max );
865 p = TQMAX( min, TQMIN( p, max ) );
866
867 return p;
868}
869
870
871void KDGanttMinimizeSplitter::doResize()
872{
873 TQRect r = contentsRect();
874 int i;
875 int n = data->list.count();
876 TQMemArray<TQLayoutStruct> a( n );
877 for ( i = 0; i< n; i++ ) {
878 a[i].init();
879 TQSplitterLayoutStruct *s = data->list.at(i);
880 if ( s->wid->isHidden() ) {
881 a[i].stretch = 0;
882 a[i].sizeHint = a[i].minimumSize = 0;
883 a[i].maximumSize = 0;
884 } else if ( s->isSplitter ) {
885 a[i].stretch = 0;
886 a[i].sizeHint = a[i].minimumSize = a[i].maximumSize = s->sizer;
887 a[i].empty = FALSE;
888 } else if ( s->mode == KeepSize ) {
889 a[i].stretch = 0;
890 a[i].minimumSize = pick( minSize(s->wid) );
891 a[i].sizeHint = s->sizer;
892 a[i].maximumSize = pick( s->wid->maximumSize() );
893 a[i].empty = FALSE;
894 } else if ( s->mode == FollowSizeHint ) {
895 a[i].stretch = 0;
896 a[i].minimumSize = a[i].sizeHint = pick( s->wid->sizeHint() );
897 a[i].maximumSize = pick( s->wid->maximumSize() );
898 a[i].empty = FALSE;
899 } else { //proportional
900 a[i].stretch = s->sizer;
901 a[i].maximumSize = pick( s->wid->maximumSize() );
902 a[i].sizeHint = a[i].minimumSize = pick( minSize(s->wid) );
903 a[i].empty = FALSE;
904 }
905 }
906
907 kdganttGeomCalc( a, 0, n, pick( r.topLeft() ), pick( r.size() ), 0 );
908
909 for ( i = 0; i< n; i++ ) {
910 TQSplitterLayoutStruct *s = data->list.at(i);
911 setG( s->wid, a[i].pos, a[i].size );
912 }
913
914}
915
916
917void KDGanttMinimizeSplitter::recalc( bool update )
918{
919 int fi = 2*frameWidth();
920 int maxl = fi;
921 int minl = fi;
922 int maxt = TQWIDGETSIZE_MAX;
923 int mint = fi;
924 int n = data->list.count();
925 bool first = TRUE;
926 /*
927 The splitter before a hidden widget is always hidden.
928 The splitter before the first visible widget is hidden.
929 The splitter before any other visible widget is visible.
930 */
931 for ( int i = 0; i< n; i++ ) {
932 TQSplitterLayoutStruct *s = data->list.at(i);
933 if ( !s->isSplitter ) {
934 TQSplitterLayoutStruct *p = (i > 0) ? data->list.at( i-1 ) : 0;
935 if ( p && p->isSplitter )
936 if ( first || s->wid->isHidden() )
937 p->wid->hide(); //may trigger new recalc
938 else
939 p->wid->show(); //may trigger new recalc
940 if ( !s->wid->isHidden() )
941 first = FALSE;
942 }
943 }
944
945 bool empty=TRUE;
946 for ( int j = 0; j< n; j++ ) {
947 TQSplitterLayoutStruct *s = data->list.at(j);
948 if ( !s->wid->isHidden() ) {
949 empty = FALSE;
950 if ( s->isSplitter ) {
951 minl += s->sizer;
952 maxl += s->sizer;
953 } else {
954 TQSize minS = minSize(s->wid);
955 minl += pick( minS );
956 maxl += pick( s->wid->maximumSize() );
957 mint = TQMAX( mint, trans( minS ));
958 int tm = trans( s->wid->maximumSize() );
959 if ( tm > 0 )
960 maxt = TQMIN( maxt, tm );
961 }
962 }
963 }
964 if ( empty ) {
965 if ( parentWidget() != 0 && parentWidget()->inherits("KDGanttMinimizeSplitter") ) {
966 // nested splitters; be nice
967 maxl = maxt = 0;
968 } else {
969 // KDGanttMinimizeSplitter with no children yet
970 maxl = TQWIDGETSIZE_MAX;
971 }
972 } else {
973 maxl = TQMIN( maxl, TQWIDGETSIZE_MAX );
974 }
975 if ( maxt < mint )
976 maxt = mint;
977
978 if ( orient == TQt::Horizontal ) {
979 setMaximumSize( maxl, maxt );
980 setMinimumSize( minl, mint );
981 } else {
982 setMaximumSize( maxt, maxl );
983 setMinimumSize( mint, minl );
984 }
985 if ( update )
986 doResize();
987}
988
996{
997 processChildEvents();
998 TQSplitterLayoutStruct *s = data->list.first();
999 while ( s ) {
1000 if ( s->wid == w ) {
1001 s->mode = mode;
1002 return;
1003 }
1004 s = data->list.next();
1005 }
1006 s = addWidget( w, TRUE );
1007 s->mode = mode;
1008}
1009
1010
1018{
1019 return data->opaque;
1020}
1021
1022
1032{
1033 data->opaque = on;
1034}
1035
1036
1042{
1043 processChildEvents();
1044 bool found = FALSE;
1045 TQSplitterLayoutStruct *s = data->list.first();
1046 while ( s ) {
1047 if ( s->wid == w ) {
1048 found = TRUE;
1049 TQSplitterLayoutStruct *p = data->list.prev();
1050 if ( p ) { // not already at first place
1051 data->list.take(); //take p
1052 data->list.take(); // take s
1053 data->list.insert( 0, p );
1054 data->list.insert( 0, s );
1055 }
1056 break;
1057 }
1058 s = data->list.next();
1059 }
1060 if ( !found )
1061 addWidget( w, TRUE );
1062 recalcId();
1063}
1064
1065
1071{
1072 processChildEvents();
1073 bool found = FALSE;
1074 TQSplitterLayoutStruct *s = data->list.first();
1075 while ( s ) {
1076 if ( s->wid == w ) {
1077 found = TRUE;
1078 data->list.take(); // take s
1079 TQSplitterLayoutStruct *p = data->list.current();
1080 if ( p ) { // the splitter handle after s
1081 data->list.take(); //take p
1082 data->list.append( p );
1083 }
1084 data->list.append( s );
1085 break;
1086 }
1087 s = data->list.next();
1088 }
1089 if ( !found )
1090 addWidget( w);
1091 recalcId();
1092}
1093
1094
1095void KDGanttMinimizeSplitter::recalcId()
1096{
1097 int n = data->list.count();
1098 for ( int i = 0; i < n; i++ ) {
1099 TQSplitterLayoutStruct *s = data->list.at(i);
1100 if ( s->isSplitter )
1101 ((KDGanttSplitterHandle*)s->wid)->setId(i);
1102 }
1103}
1104
1105
1109{
1110 constPolish();
1111 int l = 0;
1112 int t = 0;
1113 if ( !childrenListObject().isEmpty() ) {
1114 const TQObjectList c = childrenListObject();
1115 TQObjectListIt it( c );
1116 TQObject * o;
1117
1118 while( (o=it.current()) != 0 ) {
1119 ++it;
1120 if ( o->isWidgetType() &&
1121 !((TQWidget*)o)->isHidden() ) {
1122 TQSize s = ((TQWidget*)o)->sizeHint();
1123 if ( s.isValid() ) {
1124 l += pick( s );
1125 t = TQMAX( t, trans( s ) );
1126 }
1127 }
1128 }
1129 }
1130 return orientation() == TQt::Horizontal ? TQSize( l, t ) : TQSize( t, l );
1131}
1132
1133
1139{
1140 constPolish();
1141 int l = 0;
1142 int t = 0;
1143 if ( !childrenListObject().isEmpty() ) {
1144 const TQObjectList c = childrenListObject();
1145 TQObjectListIt it( c );
1146 TQObject * o;
1147
1148 while( (o=it.current()) != 0 ) {
1149 ++it;
1150 if ( o->isWidgetType() &&
1151 !((TQWidget*)o)->isHidden() ) {
1152 TQSize s = minSizeHint((TQWidget*)o);
1153 if ( s.isValid() ) {
1154 l += pick( s );
1155 t = TQMAX( t, trans( s ) );
1156 }
1157 }
1158 }
1159 }
1160 return orientation() == TQt::Horizontal ? TQSize( l, t ) : TQSize( t, l );
1161}
1162
1163
1164/*
1165 Calculates stretch parameters from current sizes
1166*/
1167
1168void KDGanttMinimizeSplitter::storeSizes()
1169{
1170 TQSplitterLayoutStruct *s = data->list.first();
1171 while ( s ) {
1172 if ( !s->isSplitter )
1173 s->sizer = pick( s->wid->size() );
1174 s = data->list.next();
1175 }
1176}
1177
1178
1179#if 0 // ### remove this code ASAP
1180
1188void KDGanttMinimizeSplitter::setHidden( TQWidget *w, bool hide )
1189{
1190 if ( w == w1 ) {
1191 w1show = !hide;
1192 } else if ( w == w2 ) {
1193 w2show = !hide;
1194 } else {
1195#ifdef TQT_CHECK_RANGE
1196 tqWarning( "KDGanttMinimizeSplitter::setHidden(), unknown widget" );
1197#endif
1198 return;
1199 }
1200 if ( hide )
1201 w->hide();
1202 else
1203 w->show();
1204 recalc( TRUE );
1205}
1206
1207
1212bool KDGanttMinimizeSplitter::isHidden( TQWidget *w ) const
1213{
1214 if ( w == w1 )
1215 return !w1show;
1216 else if ( w == w2 )
1217 return !w2show;
1218#ifdef TQT_CHECK_RANGE
1219 else
1220 tqWarning( "KDGanttMinimizeSplitter::isHidden(), unknown widget" );
1221#endif
1222 return FALSE;
1223}
1224#endif
1225
1226
1248TQValueList<int> KDGanttMinimizeSplitter::sizes() const
1249{
1250 if ( !testWState(WState_Polished) ) {
1251 TQWidget* that = (TQWidget*) this;
1252 that->polish();
1253 }
1254 TQValueList<int> list;
1255 TQSplitterLayoutStruct *s = data->list.first();
1256 while ( s ) {
1257 if ( !s->isSplitter )
1258 list.append( s->sizer );
1259 s = data->list.next();
1260 }
1261 return list;
1262}
1263
1264
1265
1279void KDGanttMinimizeSplitter::setSizes( TQValueList<int> list )
1280{
1281 processChildEvents();
1282 TQValueList<int>::Iterator it = list.begin();
1283 TQSplitterLayoutStruct *s = data->list.first();
1284 while ( s && it != list.end() ) {
1285 if ( !s->isSplitter ) {
1286 s->sizer = *it;
1287 ++it;
1288 }
1289 s = data->list.next();
1290 }
1291 doResize();
1292}
1293
1294
1300void KDGanttMinimizeSplitter::processChildEvents()
1301{
1302 TQApplication::sendPostedEvents( this, TQEvent::ChildInserted );
1303}
1304
1305
1311{
1312 int sw = style().pixelMetric(TQStyle::PM_SplitterWidth, this);
1313 TQSplitterLayoutStruct *s = data->list.first();
1314 while ( s ) {
1315 if ( s->isSplitter )
1316 s->sizer = sw;
1317 s = data->list.next();
1318 }
1319 doResize();
1320 TQFrame::styleChange( old );
1321}
1322
1331{
1332 _direction = direction;
1333}
1334
1339{
1340 return _direction;
1341}
1342
1343/*
1344 This is a copy of qGeomCalc() in qlayoutengine.cpp which
1345 unfortunately isn't exported.
1346*/
1347static inline int toFixed( int i ) { return i * 256; }
1348static inline int fRound( int i ) {
1349 return ( i % 256 < 128 ) ? i / 256 : 1 + i / 256;
1350}
1351void kdganttGeomCalc( TQMemArray<TQLayoutStruct> &chain, int start, int count, int pos,
1352 int space, int spacer )
1353{
1354 typedef int fixed;
1355 int cHint = 0;
1356 int cMin = 0;
1357 int cMax = 0;
1358 int sumStretch = 0;
1359 int spacerCount = 0;
1360
1361 bool wannaGrow = FALSE; // anyone who really wants to grow?
1362 // bool canShrink = FALSE; // anyone who could be persuaded to shrink?
1363
1364 int i;
1365 for ( i = start; i < start + count; i++ ) {
1366 chain[i].done = FALSE;
1367 cHint += chain[i].sizeHint;
1368 cMin += chain[i].minimumSize;
1369 cMax += chain[i].maximumSize;
1370 sumStretch += chain[i].stretch;
1371 if ( !chain[i].empty )
1372 spacerCount++;
1373 wannaGrow = wannaGrow || chain[i].expansive;
1374 }
1375
1376 int extraspace = 0;
1377 if ( spacerCount )
1378 spacerCount--; // only spacers between things
1379 if ( space < cMin + spacerCount * spacer ) {
1380 // tqDebug("not enough space");
1381 for ( i = start; i < start+count; i++ ) {
1382 chain[i].size = chain[i].minimumSize;
1383 chain[i].done = TRUE;
1384 }
1385 } else if ( space < cHint + spacerCount*spacer ) {
1386 // Less space than sizeHint, but more than minimum.
1387 // Currently take space equally from each, like in TQt 2.x.
1388 // Commented-out lines will give more space to stretchier items.
1389 int n = count;
1390 int space_left = space - spacerCount*spacer;
1391 int overdraft = cHint - space_left;
1392 //first give to the fixed ones:
1393 for ( i = start; i < start+count; i++ ) {
1394 if ( !chain[i].done && chain[i].minimumSize >= chain[i].sizeHint) {
1395 chain[i].size = chain[i].sizeHint;
1396 chain[i].done = TRUE;
1397 space_left -= chain[i].sizeHint;
1398 // sumStretch -= chain[i].stretch;
1399 n--;
1400 }
1401 }
1402 bool finished = n == 0;
1403 while ( !finished ) {
1404 finished = TRUE;
1405 fixed fp_over = toFixed( overdraft );
1406 fixed fp_w = 0;
1407
1408 for ( i = start; i < start+count; i++ ) {
1409 if ( chain[i].done )
1410 continue;
1411 // if ( sumStretch <= 0 )
1412 fp_w += fp_over / n;
1413 // else
1414 // fp_w += (fp_over * chain[i].stretch) / sumStretch;
1415 int w = fRound( fp_w );
1416 chain[i].size = chain[i].sizeHint - w;
1417 fp_w -= toFixed( w ); //give the difference to the next
1418 if ( chain[i].size < chain[i].minimumSize ) {
1419 chain[i].done = TRUE;
1420 chain[i].size = chain[i].minimumSize;
1421 finished = FALSE;
1422 overdraft -= chain[i].sizeHint - chain[i].minimumSize;
1423 // sumStretch -= chain[i].stretch;
1424 n--;
1425 break;
1426 }
1427 }
1428 }
1429 } else { //extra space
1430 int n = count;
1431 int space_left = space - spacerCount*spacer;
1432 // first give to the fixed ones, and handle non-expansiveness
1433 for ( i = start; i < start + count; i++ ) {
1434 if ( !chain[i].done && (chain[i].maximumSize <= chain[i].sizeHint
1435 || wannaGrow && !chain[i].expansive) ) {
1436 chain[i].size = chain[i].sizeHint;
1437 chain[i].done = TRUE;
1438 space_left -= chain[i].sizeHint;
1439 sumStretch -= chain[i].stretch;
1440 n--;
1441 }
1442 }
1443 extraspace = space_left;
1444 /*
1445 Do a trial distribution and calculate how much it is off.
1446 If there are more deficit pixels than surplus pixels, give
1447 the minimum size items what they need, and repeat.
1448 Otherwise give to the maximum size items, and repeat.
1449
1450 I have a wonderful mathematical proof for the correctness
1451 of this principle, but unfortunately this comment is too
1452 small to contain it.
1453 */
1454 int surplus, deficit;
1455 do {
1456 surplus = deficit = 0;
1457 fixed fp_space = toFixed( space_left );
1458 fixed fp_w = 0;
1459 for ( i = start; i < start+count; i++ ) {
1460 if ( chain[i].done )
1461 continue;
1462 extraspace = 0;
1463 if ( sumStretch <= 0 )
1464 fp_w += fp_space / n;
1465 else
1466 fp_w += (fp_space * chain[i].stretch) / sumStretch;
1467 int w = fRound( fp_w );
1468 chain[i].size = w;
1469 fp_w -= toFixed( w ); // give the difference to the next
1470 if ( w < chain[i].sizeHint ) {
1471 deficit += chain[i].sizeHint - w;
1472 } else if ( w > chain[i].maximumSize ) {
1473 surplus += w - chain[i].maximumSize;
1474 }
1475 }
1476 if ( deficit > 0 && surplus <= deficit ) {
1477 // give to the ones that have too little
1478 for ( i = start; i < start+count; i++ ) {
1479 if ( !chain[i].done &&
1480 chain[i].size < chain[i].sizeHint ) {
1481 chain[i].size = chain[i].sizeHint;
1482 chain[i].done = TRUE;
1483 space_left -= chain[i].sizeHint;
1484 sumStretch -= chain[i].stretch;
1485 n--;
1486 }
1487 }
1488 }
1489 if ( surplus > 0 && surplus >= deficit ) {
1490 // take from the ones that have too much
1491 for ( i = start; i < start+count; i++ ) {
1492 if ( !chain[i].done &&
1493 chain[i].size > chain[i].maximumSize ) {
1494 chain[i].size = chain[i].maximumSize;
1495 chain[i].done = TRUE;
1496 space_left -= chain[i].maximumSize;
1497 sumStretch -= chain[i].stretch;
1498 n--;
1499 }
1500 }
1501 }
1502 } while ( n > 0 && surplus != deficit );
1503 if ( n == 0 )
1504 extraspace = space_left;
1505 }
1506
1507 // as a last resort, we distribute the unwanted space equally
1508 // among the spacers (counting the start and end of the chain).
1509
1510 //### should do a sub-pixel allocation of extra space
1511 int extra = extraspace / ( spacerCount + 2 );
1512 int p = pos + extra;
1513 for ( i = start; i < start+count; i++ ) {
1514 chain[i].pos = p;
1515 p = p + chain[i].size;
1516 if ( !chain[i].empty )
1517 p += spacer+extra;
1518 }
1519}
1520
1521#endif
1522
The KDGanttMinimizeSplitter class implements a splitter widget with minimize buttons.
KDGanttMinimizeSplitter(TQWidget *parent=0, const char *name=0)
void setSizes(TQValueList< int >)
TQValueList< int > sizes() const
virtual void setOrientation(TQt::Orientation)
the orientation of the splitter
virtual void setOpaqueResize(bool=TRUE)
virtual void setResizeMode(TQWidget *w, ResizeMode)
void moveSplitter(TQCOORD pos, int id)
void resizeEvent(TQResizeEvent *)
virtual TQSize minimumSizeHint() const
void getRange(int id, int *, int *)
virtual void drawSplitter(TQPainter *, TQCOORD x, TQCOORD y, TQCOORD w, TQCOORD h)
TQt::Orientation orientation() const