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 
54 static int mouseOffset;
55 static int opaqueOldPos = -1; //### there's only one mouse, but this is a bit risky
56 
57 
58 KDGanttSplitterHandle::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 
67 TQSize KDGanttSplitterHandle::sizeHint() const
68 {
69  return TQSize(8,8);
70 }
71 
72 void 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 
84 void 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 
105 void 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 
116 void 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 
130 void 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 
171 int 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 
190 TQValueList<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 
235 void 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 
286 class TQSplitterLayoutStruct
287 {
288 public:
290  TQCOORD sizer;
291  bool isSplitter;
292  TQWidget *wid;
293 };
294 
295 class TQSplitterData
296 {
297 public:
298  TQSplitterData() : opaque( FALSE ), firstShow( TRUE ) {}
299 
300  TQPtrList<TQSplitterLayoutStruct> list;
301  bool opaque;
302  bool firstShow;
303 };
304 
305 void kdganttGeomCalc( TQMemArray<TQLayoutStruct> &chain, int start, int count, int pos,
306  int space, int spacer );
307 #endif // DOXYGEN_SKIP_INTERNAL
308 
309 
359 static TQSize minSize( const TQWidget* /*w*/ )
360 {
361  return TQSize(0,0);
362 }
363 
364 // This is the original version of minSize
365 static 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 
384 KDGanttMinimizeSplitter::KDGanttMinimizeSplitter( TQWidget *parent, const char *name )
385  :TQFrame(parent,name,WPaintUnclipped)
386 {
387  orient = TQt::Horizontal;
388  init();
389 }
390 
395 KDGanttMinimizeSplitter::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 
412 void 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 
429 void KDGanttMinimizeSplitter::setOrientation( TQt::Orientation o )
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 */
468 TQSplitterLayoutStruct *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 
509 void KDGanttMinimizeSplitter::childEvent( TQChildEvent *c )
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 
615 int 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 
642 void KDGanttMinimizeSplitter::moveSplitter( TQCOORD p, int id )
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 
662 void 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 */
678 void 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 */
729 void 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 
781 void 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 
804 void 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 
871 void 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 
917 void 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 
1095 void 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 
1168 void 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 
1188 void 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 
1212 bool 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 
1248 TQValueList<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 
1279 void 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 
1300 void 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 */
1347 static inline int toFixed( int i ) { return i * 256; }
1348 static inline int fRound( int i ) {
1349  return ( i % 256 < 128 ) ? i / 256 : 1 + i / 256;
1350 }
1351 void 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