KDGanttMinimizeSplitter.cpp
00001 /* -*- Mode: C++ -*- 00002 $Id$ 00003 */ 00004 00005 /**************************************************************************** 00006 ** Copyright (C) 2002-2004 Klar�lvdalens Datakonsult AB. All rights reserved. 00007 ** 00008 ** This file is part of the KDGantt library. 00009 ** 00010 ** This file may be distributed and/or modified under the terms of the 00011 ** GNU General Public License version 2 as published by the Free Software 00012 ** Foundation and appearing in the file LICENSE.GPL included in the 00013 ** packaging of this file. 00014 ** 00015 ** Licensees holding valid commercial KDGantt licenses may use this file in 00016 ** accordance with the KDGantt Commercial License Agreement provided with 00017 ** the Software. 00018 ** 00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 00021 ** 00022 ** See http://www.klaralvdalens-datakonsult.se/Public/products/ for 00023 ** information about KDGantt Commercial License Agreements. 00024 ** 00025 ** Contact info@klaralvdalens-datakonsult.se if any conditions of this 00026 ** licensing are not clear to you. 00027 ** 00028 ** As a special exception, permission is given to link this program 00029 ** with any edition of TQt, and distribute the resulting executable, 00030 ** without including the source code for TQt in the source distribution. 00031 ** 00032 **********************************************************************/ 00033 00034 #include "KDGanttMinimizeSplitter.h" 00035 #ifndef TQT_NO_SPLITTER 00036 00037 #include "tqpainter.h" 00038 #include "tqdrawutil.h" 00039 #include "tqbitmap.h" 00040 #include "tqptrlist.h" 00041 #include "tqmemarray.h" 00042 #include "tqlayout.h" 00043 #include "tqlayoutengine_p.h" 00044 #include "tqobjectlist.h" 00045 #include "tqstyle.h" 00046 #include "tqapplication.h" //sendPostedEvents 00047 #include <tqvaluelist.h> 00048 #include <tqcursor.h> 00049 00050 #include "KDGanttMinimizeSplitter.moc" 00051 00052 #ifndef DOXYGEN_SKIP_INTERNAL 00053 00054 static int mouseOffset; 00055 static int opaqueOldPos = -1; //### there's only one mouse, but this is a bit risky 00056 00057 00058 KDGanttSplitterHandle::KDGanttSplitterHandle( Qt::Orientation o, 00059 KDGanttMinimizeSplitter *parent, const char * name ) 00060 : TQWidget( parent, name ), _activeButton( 0 ), _collapsed( false ) 00061 { 00062 s = parent; 00063 setOrientation(o); 00064 setMouseTracking( true ); 00065 } 00066 00067 TQSize KDGanttSplitterHandle::sizeHint() const 00068 { 00069 return TQSize(8,8); 00070 } 00071 00072 void KDGanttSplitterHandle::setOrientation( Qt::Orientation o ) 00073 { 00074 orient = o; 00075 #ifndef TQT_NO_CURSOR 00076 if ( o == Qt::Horizontal ) 00077 setCursor( splitHCursor ); 00078 else 00079 setCursor( splitVCursor ); 00080 #endif 00081 } 00082 00083 00084 void KDGanttSplitterHandle::mouseMoveEvent( TQMouseEvent *e ) 00085 { 00086 updateCursor( e->pos() ); 00087 if ( !(e->state()&Qt::LeftButton) ) 00088 return; 00089 00090 if ( _activeButton != 0) 00091 return; 00092 00093 TQCOORD pos = s->pick(parentWidget()->mapFromGlobal(e->globalPos())) 00094 - mouseOffset; 00095 if ( opaque() ) { 00096 s->moveSplitter( pos, id() ); 00097 } else { 00098 int min = pos; int max = pos; 00099 s->getRange( id(), &min, &max ); 00100 s->setRubberband( TQMAX( min, TQMIN(max, pos ))); 00101 } 00102 _collapsed = false; 00103 } 00104 00105 void KDGanttSplitterHandle::mousePressEvent( TQMouseEvent *e ) 00106 { 00107 if ( e->button() == Qt::LeftButton ) { 00108 _activeButton = onButton( e->pos() ); 00109 mouseOffset = s->pick(e->pos()); 00110 if ( _activeButton != 0) 00111 repaint(); 00112 updateCursor( e->pos() ); 00113 } 00114 } 00115 00116 void KDGanttSplitterHandle::updateCursor( const TQPoint& p) 00117 { 00118 if ( onButton( p ) != 0 ) { 00119 setCursor( arrowCursor ); 00120 } 00121 else { 00122 if ( orient == Qt::Horizontal ) 00123 setCursor( splitHCursor ); 00124 else 00125 setCursor( splitVCursor ); 00126 } 00127 } 00128 00129 00130 void KDGanttSplitterHandle::mouseReleaseEvent( TQMouseEvent *e ) 00131 { 00132 if ( _activeButton != 0 ) { 00133 if ( onButton( e->pos() ) == _activeButton ) 00134 { 00135 int pos; 00136 int min, max; 00137 if ( !_collapsed ) { 00138 s->expandPos( id(), &min, &max ); 00139 if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Left 00140 || s->minimizeDirection() == KDGanttMinimizeSplitter::Up ) { 00141 pos = min; 00142 } 00143 else { 00144 pos = max; 00145 } 00146 00147 _origPos = s->pick(mapToParent( TQPoint( 0,0 ) )); 00148 s->moveSplitter( pos, id() ); 00149 _collapsed = true; 00150 } 00151 else { 00152 s->moveSplitter( _origPos, id() ); 00153 _collapsed = false; 00154 } 00155 00156 } 00157 _activeButton = 0; 00158 updateCursor( e->pos() ); 00159 } 00160 else { 00161 if ( !opaque() && e->button() == Qt::LeftButton ) { 00162 TQCOORD pos = s->pick(parentWidget()->mapFromGlobal(e->globalPos())) 00163 - mouseOffset; 00164 s->setRubberband( -1 ); 00165 s->moveSplitter( pos, id() ); 00166 } 00167 } 00168 repaint(); 00169 } 00170 00171 int KDGanttSplitterHandle::onButton( const TQPoint& p ) 00172 { 00173 TQValueList<TQPointArray> list = buttonRegions(); 00174 int index = 1; 00175 for( TQValueList<TQPointArray>::Iterator it = list.begin(); it != list.end(); ++it ) { 00176 TQRect rect = (*it).boundingRect(); 00177 rect.setLeft( rect.left()- 4 ); 00178 rect.setRight( rect.right() + 4); 00179 rect.setTop( rect.top()- 4 ); 00180 rect.setBottom( rect.bottom() + 4); 00181 if ( rect.contains( p ) ) { 00182 return index; 00183 } 00184 index++; 00185 } 00186 return 0; 00187 } 00188 00189 00190 TQValueList<TQPointArray> KDGanttSplitterHandle::buttonRegions() 00191 { 00192 TQValueList<TQPointArray> list; 00193 00194 int sw = 8; 00195 int voffset[] = { (int) -sw*3, (int) sw*3 }; 00196 for ( int i = 0; i < 2; i++ ) { 00197 TQPointArray arr; 00198 if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Right || 00199 _collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Left) { 00200 int mid = height()/2 + voffset[i]; 00201 arr.setPoints( 3, 00202 1, mid - sw + 4, 00203 sw-3, mid, 00204 1, mid + sw -4); 00205 } 00206 else if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Left || 00207 _collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Right ) { 00208 int mid = height()/2 + voffset[i]; 00209 arr.setPoints( 3, 00210 sw-4, mid - sw + 4, 00211 0, mid, 00212 sw-4, mid + sw - 4); 00213 } 00214 else if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Up || 00215 _collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Down) { 00216 int mid = width()/2 + voffset[i]; 00217 arr.setPoints( 3, 00218 mid - sw + 4, sw-4, 00219 mid, 0, 00220 mid + sw - 4, sw-4 ); 00221 } 00222 else if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Down || 00223 _collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Up ) { 00224 int mid = width()/2 + voffset[i]; 00225 arr.setPoints( 3, 00226 mid - sw + 4, 1, 00227 mid, sw-3, 00228 mid + sw -4, 1); 00229 } 00230 list.append( arr ); 00231 } 00232 return list; 00233 } 00234 00235 void KDGanttSplitterHandle::paintEvent( TQPaintEvent * ) 00236 { 00237 TQPixmap buffer( size() ); 00238 TQPainter p( &buffer ); 00239 00240 // Draw the splitter rectangle 00241 p.setBrush( colorGroup().background() ); 00242 p.setPen( colorGroup().foreground() ); 00243 p.drawRect( rect() ); 00244 parentWidget()->style().tqdrawPrimitive( TQStyle::PE_Panel, &p, rect(), 00245 parentWidget()->colorGroup()); 00246 00247 int sw = 8; // Hardcoded, given I didn't use styles anymore, I didn't like to use their size 00248 00249 // arrow color 00250 TQColor col = colorGroup().background().dark( 200 ); 00251 p.setBrush( col ); 00252 p.setPen( col ); 00253 00254 TQValueList<TQPointArray> list = buttonRegions(); 00255 int index = 1; 00256 for ( TQValueList<TQPointArray>::Iterator it = list.begin(); it != list.end(); ++it ) { 00257 if ( index == _activeButton ) { 00258 p.save(); 00259 p.translate( parentWidget()->style().pixelMetric( TQStyle::PM_ButtonShiftHorizontal ), 00260 parentWidget()->style().pixelMetric( TQStyle::PM_ButtonShiftVertical ) ); 00261 p.drawPolygon( *it, true ); 00262 p.restore(); 00263 } 00264 else { 00265 p.drawPolygon( *it, true ); 00266 } 00267 index++; 00268 } 00269 00270 // Draw the lines between the arrows 00271 if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Left || 00272 s->minimizeDirection() == KDGanttMinimizeSplitter::Right ) { 00273 int mid = height()/2; 00274 p.drawLine ( 2, mid - sw, 2, mid + sw ); 00275 p.drawLine ( 4, mid - sw, 4, mid + sw ); 00276 } 00277 else if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Up || 00278 s->minimizeDirection() == KDGanttMinimizeSplitter::Down ) { 00279 int mid = width()/2; 00280 p.drawLine( mid -sw, 2, mid +sw, 2 ); 00281 p.drawLine( mid -sw, 4, mid +sw, 4 ); 00282 } 00283 bitBlt( this, 0, 0, &buffer ); 00284 } 00285 00286 class TQSplitterLayoutStruct 00287 { 00288 public: 00289 KDGanttMinimizeSplitter::ResizeMode mode; 00290 TQCOORD sizer; 00291 bool isSplitter; 00292 TQWidget *wid; 00293 }; 00294 00295 class TQSplitterData 00296 { 00297 public: 00298 TQSplitterData() : opaque( FALSE ), firstShow( TRUE ) {} 00299 00300 TQPtrList<TQSplitterLayoutStruct> list; 00301 bool opaque; 00302 bool firstShow; 00303 }; 00304 00305 void kdganttGeomCalc( TQMemArray<TQLayoutStruct> &chain, int start, int count, int pos, 00306 int space, int spacer ); 00307 #endif // DOXYGEN_SKIP_INTERNAL 00308 00309 00359 static TQSize minSize( const TQWidget* /*w*/ ) 00360 { 00361 return TQSize(0,0); 00362 } 00363 00364 // This is the original version of minSize 00365 static TQSize minSizeHint( const TQWidget* w ) 00366 { 00367 TQSize min = w->minimumSize(); 00368 TQSize s; 00369 if ( min.height() <= 0 || min.width() <= 0 ) 00370 s = w->minimumSizeHint(); 00371 if ( min.height() > 0 ) 00372 s.setHeight( min.height() ); 00373 if ( min.width() > 0 ) 00374 s.setWidth( min.width() ); 00375 return s.expandedTo(TQSize(0,0)); 00376 } 00377 00378 00379 00384 KDGanttMinimizeSplitter::KDGanttMinimizeSplitter( TQWidget *parent, const char *name ) 00385 :TQFrame(parent,name,WPaintUnclipped) 00386 { 00387 orient = Qt::Horizontal; 00388 init(); 00389 } 00390 00395 KDGanttMinimizeSplitter::KDGanttMinimizeSplitter( Qt::Orientation o, TQWidget *parent, const char *name ) 00396 :TQFrame(parent,name,WPaintUnclipped) 00397 { 00398 orient = o; 00399 init(); 00400 } 00401 00405 KDGanttMinimizeSplitter::~KDGanttMinimizeSplitter() 00406 { 00407 data->list.setAutoDelete( TRUE ); 00408 delete data; 00409 } 00410 00411 00412 void KDGanttMinimizeSplitter::init() 00413 { 00414 data = new TQSplitterData; 00415 if ( orient == Qt::Horizontal ) 00416 setSizePolicy( TQSizePolicy(TQSizePolicy::Expanding,TQSizePolicy::Minimum) ); 00417 else 00418 setSizePolicy( TQSizePolicy(TQSizePolicy::Minimum,TQSizePolicy::Expanding) ); 00419 } 00420 00421 00422 00429 void KDGanttMinimizeSplitter::setOrientation( Qt::Orientation o ) 00430 { 00431 if ( orient == o ) 00432 return; 00433 orient = o; 00434 00435 if ( orient == Qt::Horizontal ) 00436 setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum ) ); 00437 else 00438 setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Expanding ) ); 00439 00440 TQSplitterLayoutStruct *s = data->list.first(); 00441 while ( s ) { 00442 if ( s->isSplitter ) 00443 ((KDGanttSplitterHandle*)s->wid)->setOrientation( o ); 00444 s = data->list.next(); // ### next at end of loop, no iterator 00445 } 00446 recalc( isVisible() ); 00447 } 00448 00449 00453 void KDGanttMinimizeSplitter::resizeEvent( TQResizeEvent * ) 00454 { 00455 doResize(); 00456 } 00457 00458 00459 /* 00460 Inserts the widget \a w at the end (or at the beginning if \a first 00461 is TRUE) of the splitter's list of widgets. 00462 00463 It is the responsibility of the caller of this function to make sure 00464 that \a w is not already in the splitter and to call recalcId if 00465 needed. (If \a first is TRUE, then recalcId is very probably 00466 needed.) 00467 */ 00468 TQSplitterLayoutStruct *KDGanttMinimizeSplitter::addWidget( TQWidget *w, bool first ) 00469 { 00470 TQSplitterLayoutStruct *s; 00471 KDGanttSplitterHandle *newHandle = 0; 00472 if ( data->list.count() > 0 ) { 00473 s = new TQSplitterLayoutStruct; 00474 s->mode = KeepSize; 00475 TQString tmp = "qt_splithandle_"; 00476 tmp += w->name(); 00477 newHandle = new KDGanttSplitterHandle( orientation(), this, tmp.latin1() ); 00478 s->wid = newHandle; 00479 newHandle->setId(data->list.count()); 00480 s->isSplitter = TRUE; 00481 s->sizer = pick( newHandle->sizeHint() ); 00482 if ( first ) 00483 data->list.insert( 0, s ); 00484 else 00485 data->list.append( s ); 00486 } 00487 s = new TQSplitterLayoutStruct; 00488 s->mode = Stretch; 00489 s->wid = w; 00490 if ( !testWState( WState_Resized ) && w->sizeHint().isValid() ) 00491 s->sizer = pick( w->sizeHint() ); 00492 else 00493 s->sizer = pick( w->size() ); 00494 s->isSplitter = FALSE; 00495 if ( first ) 00496 data->list.insert( 0, s ); 00497 else 00498 data->list.append( s ); 00499 if ( newHandle && isVisible() ) 00500 newHandle->show(); //will trigger sending of post events 00501 return s; 00502 } 00503 00504 00509 void KDGanttMinimizeSplitter::childEvent( TQChildEvent *c ) 00510 { 00511 if ( c->type() == TQEvent::ChildInserted ) { 00512 if ( !c->child()->isWidgetType() ) 00513 return; 00514 00515 if ( ((TQWidget*)c->child())->testWFlags( WType_TopLevel ) ) 00516 return; 00517 00518 TQSplitterLayoutStruct *s = data->list.first(); 00519 while ( s ) { 00520 if ( s->wid == c->child() ) 00521 return; 00522 s = data->list.next(); 00523 } 00524 addWidget( (TQWidget*)c->child() ); 00525 recalc( isVisible() ); 00526 00527 } else if ( c->type() == TQEvent::ChildRemoved ) { 00528 TQSplitterLayoutStruct *p = 0; 00529 if ( data->list.count() > 1 ) 00530 p = data->list.at(1); //remove handle _after_ first widget. 00531 TQSplitterLayoutStruct *s = data->list.first(); 00532 while ( s ) { 00533 if ( s->wid == c->child() ) { 00534 data->list.removeRef( s ); 00535 delete s; 00536 if ( p && p->isSplitter ) { 00537 data->list.removeRef( p ); 00538 delete p->wid; //will call childEvent 00539 delete p; 00540 } 00541 recalcId(); 00542 doResize(); 00543 return; 00544 } 00545 p = s; 00546 s = data->list.next(); 00547 } 00548 } 00549 } 00550 00551 00556 void KDGanttMinimizeSplitter::setRubberband( int p ) 00557 { 00558 TQPainter paint( this ); 00559 paint.setPen( gray ); 00560 paint.setBrush( gray ); 00561 paint.setRasterOp( XorROP ); 00562 TQRect r = contentsRect(); 00563 const int rBord = 3; //Themable???? 00564 int sw = style().pixelMetric(TQStyle::PM_SplitterWidth, this); 00565 if ( orient == Qt::Horizontal ) { 00566 if ( opaqueOldPos >= 0 ) 00567 paint.drawRect( opaqueOldPos + sw/2 - rBord , r.y(), 00568 2*rBord, r.height() ); 00569 if ( p >= 0 ) 00570 paint.drawRect( p + sw/2 - rBord, r.y(), 2*rBord, r.height() ); 00571 } else { 00572 if ( opaqueOldPos >= 0 ) 00573 paint.drawRect( r.x(), opaqueOldPos + sw/2 - rBord, 00574 r.width(), 2*rBord ); 00575 if ( p >= 0 ) 00576 paint.drawRect( r.x(), p + sw/2 - rBord, r.width(), 2*rBord ); 00577 } 00578 opaqueOldPos = p; 00579 } 00580 00581 00583 bool KDGanttMinimizeSplitter::event( TQEvent *e ) 00584 { 00585 if ( e->type() == TQEvent::LayoutHint || ( e->type() == TQEvent::Show && data->firstShow ) ) { 00586 recalc( isVisible() ); 00587 if ( e->type() == TQEvent::Show ) 00588 data->firstShow = FALSE; 00589 } 00590 return TQWidget::event( e ); 00591 } 00592 00593 00601 void KDGanttMinimizeSplitter::drawSplitter( TQPainter *p, 00602 TQCOORD x, TQCOORD y, TQCOORD w, TQCOORD h ) 00603 { 00604 style().tqdrawPrimitive(TQStyle::PE_Splitter, p, TQRect(x, y, w, h), colorGroup(), 00605 (orientation() == Qt::Horizontal ? 00606 TQStyle::Style_Horizontal : 0)); 00607 } 00608 00609 00615 int KDGanttMinimizeSplitter::idAfter( TQWidget* w ) const 00616 { 00617 TQSplitterLayoutStruct *s = data->list.first(); 00618 bool seen_w = FALSE; 00619 while ( s ) { 00620 if ( s->isSplitter && seen_w ) 00621 return data->list.at(); 00622 if ( !s->isSplitter && s->wid == w ) 00623 seen_w = TRUE; 00624 s = data->list.next(); 00625 } 00626 return 0; 00627 } 00628 00629 00642 void KDGanttMinimizeSplitter::moveSplitter( TQCOORD p, int id ) 00643 { 00644 p = adjustPos( p, id ); 00645 00646 TQSplitterLayoutStruct *s = data->list.at(id); 00647 int oldP = orient == Qt::Horizontal ? s->wid->x() : s->wid->y(); 00648 bool upLeft; 00649 if ( TQApplication::reverseLayout() && orient == Qt::Horizontal ) { 00650 p += s->wid->width(); 00651 upLeft = p > oldP; 00652 } else 00653 upLeft = p < oldP; 00654 00655 moveAfter( p, id, upLeft ); 00656 moveBefore( p-1, id-1, upLeft ); 00657 00658 storeSizes(); 00659 } 00660 00661 00662 void KDGanttMinimizeSplitter::setG( TQWidget *w, int p, int s, bool isSplitter ) 00663 { 00664 if ( orient == Qt::Horizontal ) { 00665 if ( TQApplication::reverseLayout() && orient == Qt::Horizontal && !isSplitter ) 00666 p = contentsRect().width() - p - s; 00667 w->setGeometry( p, contentsRect().y(), s, contentsRect().height() ); 00668 } else 00669 w->setGeometry( contentsRect().x(), p, contentsRect().width(), s ); 00670 } 00671 00672 00673 /* 00674 Places the right/bottom edge of the widget at \a id at position \a pos. 00675 00676 \sa idAfter() 00677 */ 00678 void KDGanttMinimizeSplitter::moveBefore( int pos, int id, bool upLeft ) 00679 { 00680 if( id < 0 ) 00681 return; 00682 TQSplitterLayoutStruct *s = data->list.at(id); 00683 if ( !s ) 00684 return; 00685 TQWidget *w = s->wid; 00686 if ( w->isHidden() ) { 00687 moveBefore( pos, id-1, upLeft ); 00688 } else if ( s->isSplitter ) { 00689 int pos1, pos2; 00690 int dd = s->sizer; 00691 if( TQApplication::reverseLayout() && orient == Qt::Horizontal ) { 00692 pos1 = pos; 00693 pos2 = pos + dd; 00694 } else { 00695 pos2 = pos - dd; 00696 pos1 = pos2 + 1; 00697 } 00698 if ( upLeft ) { 00699 setG( w, pos1, dd, TRUE ); 00700 moveBefore( pos2, id-1, upLeft ); 00701 } else { 00702 moveBefore( pos2, id-1, upLeft ); 00703 setG( w, pos1, dd, TRUE ); 00704 } 00705 } else { 00706 int dd, newLeft, nextPos; 00707 if( TQApplication::reverseLayout() && orient == Qt::Horizontal ) { 00708 dd = w->geometry().right() - pos; 00709 dd = TQMAX( pick(minSize(w)), TQMIN(dd, pick(w->maximumSize()))); 00710 newLeft = pos+1; 00711 nextPos = newLeft + dd; 00712 } else { 00713 dd = pos - pick( w->pos() ) + 1; 00714 dd = TQMAX( pick(minSize(w)), TQMIN(dd, pick(w->maximumSize()))); 00715 newLeft = pos-dd+1; 00716 nextPos = newLeft - 1; 00717 } 00718 setG( w, newLeft, dd, TRUE ); 00719 moveBefore( nextPos, id-1, upLeft ); 00720 } 00721 } 00722 00723 00724 /* 00725 Places the left/top edge of the widget at \a id at position \a pos. 00726 00727 \sa idAfter() 00728 */ 00729 void KDGanttMinimizeSplitter::moveAfter( int pos, int id, bool upLeft ) 00730 { 00731 TQSplitterLayoutStruct *s = id < int(data->list.count()) ? 00732 data->list.at(id) : 0; 00733 if ( !s ) 00734 return; 00735 TQWidget *w = s->wid; 00736 if ( w->isHidden() ) { 00737 moveAfter( pos, id+1, upLeft ); 00738 } else if ( pick( w->pos() ) == pos ) { 00739 //No need to do anything if it's already there. 00740 return; 00741 } else if ( s->isSplitter ) { 00742 int dd = s->sizer; 00743 int pos1, pos2; 00744 if( TQApplication::reverseLayout() && orient == Qt::Horizontal ) { 00745 pos2 = pos - dd; 00746 pos1 = pos2 + 1; 00747 } else { 00748 pos1 = pos; 00749 pos2 = pos + dd; 00750 } 00751 if ( upLeft ) { 00752 setG( w, pos1, dd, TRUE ); 00753 moveAfter( pos2, id+1, upLeft ); 00754 } else { 00755 moveAfter( pos2, id+1, upLeft ); 00756 setG( w, pos1, dd, TRUE ); 00757 } 00758 } else { 00759 int left = pick( w->pos() ); 00760 int right, dd,/* newRight,*/ newLeft, nextPos; 00761 if ( TQApplication::reverseLayout() && orient == Qt::Horizontal ) { 00762 dd = pos - left + 1; 00763 dd = TQMAX( pick(minSize(w)), TQMIN(dd, pick(w->maximumSize()))); 00764 newLeft = pos-dd+1; 00765 nextPos = newLeft - 1; 00766 } else { 00767 right = pick( w->geometry().bottomRight() ); 00768 dd = right - pos + 1; 00769 dd = TQMAX( pick(minSize(w)), TQMIN(dd, pick(w->maximumSize()))); 00770 /*newRight = pos+dd-1;*/ 00771 newLeft = pos; 00772 nextPos = newLeft + dd; 00773 } 00774 setG( w, newLeft, dd, TRUE ); 00775 /*if( right != newRight )*/ 00776 moveAfter( nextPos, id+1, upLeft ); 00777 } 00778 } 00779 00780 00781 void KDGanttMinimizeSplitter::expandPos( int id, int* min, int* max ) 00782 { 00783 TQSplitterLayoutStruct *s = data->list.at(id-1); 00784 TQWidget* w = s->wid; 00785 *min = pick( w->mapToParent( TQPoint(0,0) ) ); 00786 00787 if ( (uint) id == data->list.count() ) { 00788 pick( size() ); 00789 } 00790 else { 00791 TQSplitterLayoutStruct *s = data->list.at(id+1); 00792 TQWidget* w = s->wid; 00793 *max = pick( w->mapToParent( TQPoint( w->width(), w->height() ) ) ) -8; 00794 } 00795 } 00796 00797 00804 void KDGanttMinimizeSplitter::getRange( int id, int *min, int *max ) 00805 { 00806 int minB = 0; //before 00807 int maxB = 0; 00808 int minA = 0; 00809 int maxA = 0; //after 00810 int n = data->list.count(); 00811 if ( id < 0 || id >= n ) 00812 return; 00813 int i; 00814 for ( i = 0; i < id; i++ ) { 00815 TQSplitterLayoutStruct *s = data->list.at(i); 00816 if ( s->wid->isHidden() ) { 00817 //ignore 00818 } else if ( s->isSplitter ) { 00819 minB += s->sizer; 00820 maxB += s->sizer; 00821 } else { 00822 minB += pick( minSize(s->wid) ); 00823 maxB += pick( s->wid->maximumSize() ); 00824 } 00825 } 00826 for ( i = id; i < n; i++ ) { 00827 TQSplitterLayoutStruct *s = data->list.at(i); 00828 if ( s->wid->isHidden() ) { 00829 //ignore 00830 } else if ( s->isSplitter ) { 00831 minA += s->sizer; 00832 maxA += s->sizer; 00833 } else { 00834 minA += pick( minSize(s->wid) ); 00835 maxA += pick( s->wid->maximumSize() ); 00836 } 00837 } 00838 TQRect r = contentsRect(); 00839 if ( orient == Qt::Horizontal && TQApplication::reverseLayout() ) { 00840 int splitterWidth = style().pixelMetric(TQStyle::PM_SplitterWidth, this); 00841 if ( min ) 00842 *min = pick(r.topRight()) - TQMIN( maxB, pick(r.size())-minA ) - splitterWidth; 00843 if ( max ) 00844 *max = pick(r.topRight()) - TQMAX( minB, pick(r.size())-maxA ) - splitterWidth; 00845 } else { 00846 if ( min ) 00847 *min = pick(r.topLeft()) + TQMAX( minB, pick(r.size())-maxA ); 00848 if ( max ) 00849 *max = pick(r.topLeft()) + TQMIN( maxB, pick(r.size())-minA ); 00850 } 00851 } 00852 00853 00860 int KDGanttMinimizeSplitter::adjustPos( int p, int id ) 00861 { 00862 int min = 0; 00863 int max = 0; 00864 getRange( id, &min, &max ); 00865 p = TQMAX( min, TQMIN( p, max ) ); 00866 00867 return p; 00868 } 00869 00870 00871 void KDGanttMinimizeSplitter::doResize() 00872 { 00873 TQRect r = contentsRect(); 00874 int i; 00875 int n = data->list.count(); 00876 TQMemArray<TQLayoutStruct> a( n ); 00877 for ( i = 0; i< n; i++ ) { 00878 a[i].init(); 00879 TQSplitterLayoutStruct *s = data->list.at(i); 00880 if ( s->wid->isHidden() ) { 00881 a[i].stretch = 0; 00882 a[i].sizeHint = a[i].minimumSize = 0; 00883 a[i].maximumSize = 0; 00884 } else if ( s->isSplitter ) { 00885 a[i].stretch = 0; 00886 a[i].sizeHint = a[i].minimumSize = a[i].maximumSize = s->sizer; 00887 a[i].empty = FALSE; 00888 } else if ( s->mode == KeepSize ) { 00889 a[i].stretch = 0; 00890 a[i].minimumSize = pick( minSize(s->wid) ); 00891 a[i].sizeHint = s->sizer; 00892 a[i].maximumSize = pick( s->wid->maximumSize() ); 00893 a[i].empty = FALSE; 00894 } else if ( s->mode == FollowSizeHint ) { 00895 a[i].stretch = 0; 00896 a[i].minimumSize = a[i].sizeHint = pick( s->wid->sizeHint() ); 00897 a[i].maximumSize = pick( s->wid->maximumSize() ); 00898 a[i].empty = FALSE; 00899 } else { //proportional 00900 a[i].stretch = s->sizer; 00901 a[i].maximumSize = pick( s->wid->maximumSize() ); 00902 a[i].sizeHint = a[i].minimumSize = pick( minSize(s->wid) ); 00903 a[i].empty = FALSE; 00904 } 00905 } 00906 00907 kdganttGeomCalc( a, 0, n, pick( r.topLeft() ), pick( r.size() ), 0 ); 00908 00909 for ( i = 0; i< n; i++ ) { 00910 TQSplitterLayoutStruct *s = data->list.at(i); 00911 setG( s->wid, a[i].pos, a[i].size ); 00912 } 00913 00914 } 00915 00916 00917 void KDGanttMinimizeSplitter::recalc( bool update ) 00918 { 00919 int fi = 2*frameWidth(); 00920 int maxl = fi; 00921 int minl = fi; 00922 int maxt = TQWIDGETSIZE_MAX; 00923 int mint = fi; 00924 int n = data->list.count(); 00925 bool first = TRUE; 00926 /* 00927 The splitter before a hidden widget is always hidden. 00928 The splitter before the first visible widget is hidden. 00929 The splitter before any other visible widget is visible. 00930 */ 00931 for ( int i = 0; i< n; i++ ) { 00932 TQSplitterLayoutStruct *s = data->list.at(i); 00933 if ( !s->isSplitter ) { 00934 TQSplitterLayoutStruct *p = (i > 0) ? data->list.at( i-1 ) : 0; 00935 if ( p && p->isSplitter ) 00936 if ( first || s->wid->isHidden() ) 00937 p->wid->hide(); //may trigger new recalc 00938 else 00939 p->wid->show(); //may trigger new recalc 00940 if ( !s->wid->isHidden() ) 00941 first = FALSE; 00942 } 00943 } 00944 00945 bool empty=TRUE; 00946 for ( int j = 0; j< n; j++ ) { 00947 TQSplitterLayoutStruct *s = data->list.at(j); 00948 if ( !s->wid->isHidden() ) { 00949 empty = FALSE; 00950 if ( s->isSplitter ) { 00951 minl += s->sizer; 00952 maxl += s->sizer; 00953 } else { 00954 TQSize minS = minSize(s->wid); 00955 minl += pick( minS ); 00956 maxl += pick( s->wid->maximumSize() ); 00957 mint = TQMAX( mint, trans( minS )); 00958 int tm = trans( s->wid->maximumSize() ); 00959 if ( tm > 0 ) 00960 maxt = TQMIN( maxt, tm ); 00961 } 00962 } 00963 } 00964 if ( empty ) { 00965 if ( parentWidget() != 0 && parentWidget()->inherits("KDGanttMinimizeSplitter") ) { 00966 // nested splitters; be nice 00967 maxl = maxt = 0; 00968 } else { 00969 // KDGanttMinimizeSplitter with no children yet 00970 maxl = TQWIDGETSIZE_MAX; 00971 } 00972 } else { 00973 maxl = TQMIN( maxl, TQWIDGETSIZE_MAX ); 00974 } 00975 if ( maxt < mint ) 00976 maxt = mint; 00977 00978 if ( orient == Qt::Horizontal ) { 00979 setMaximumSize( maxl, maxt ); 00980 setMinimumSize( minl, mint ); 00981 } else { 00982 setMaximumSize( maxt, maxl ); 00983 setMinimumSize( mint, minl ); 00984 } 00985 if ( update ) 00986 doResize(); 00987 } 00988 00995 void KDGanttMinimizeSplitter::setResizeMode( TQWidget *w, ResizeMode mode ) 00996 { 00997 processChildEvents(); 00998 TQSplitterLayoutStruct *s = data->list.first(); 00999 while ( s ) { 01000 if ( s->wid == w ) { 01001 s->mode = mode; 01002 return; 01003 } 01004 s = data->list.next(); 01005 } 01006 s = addWidget( w, TRUE ); 01007 s->mode = mode; 01008 } 01009 01010 01017 bool KDGanttMinimizeSplitter::opaqueResize() const 01018 { 01019 return data->opaque; 01020 } 01021 01022 01031 void KDGanttMinimizeSplitter::setOpaqueResize( bool on ) 01032 { 01033 data->opaque = on; 01034 } 01035 01036 01041 void KDGanttMinimizeSplitter::moveToFirst( TQWidget *w ) 01042 { 01043 processChildEvents(); 01044 bool found = FALSE; 01045 TQSplitterLayoutStruct *s = data->list.first(); 01046 while ( s ) { 01047 if ( s->wid == w ) { 01048 found = TRUE; 01049 TQSplitterLayoutStruct *p = data->list.prev(); 01050 if ( p ) { // not already at first place 01051 data->list.take(); //take p 01052 data->list.take(); // take s 01053 data->list.insert( 0, p ); 01054 data->list.insert( 0, s ); 01055 } 01056 break; 01057 } 01058 s = data->list.next(); 01059 } 01060 if ( !found ) 01061 addWidget( w, TRUE ); 01062 recalcId(); 01063 } 01064 01065 01070 void KDGanttMinimizeSplitter::moveToLast( TQWidget *w ) 01071 { 01072 processChildEvents(); 01073 bool found = FALSE; 01074 TQSplitterLayoutStruct *s = data->list.first(); 01075 while ( s ) { 01076 if ( s->wid == w ) { 01077 found = TRUE; 01078 data->list.take(); // take s 01079 TQSplitterLayoutStruct *p = data->list.current(); 01080 if ( p ) { // the splitter handle after s 01081 data->list.take(); //take p 01082 data->list.append( p ); 01083 } 01084 data->list.append( s ); 01085 break; 01086 } 01087 s = data->list.next(); 01088 } 01089 if ( !found ) 01090 addWidget( w); 01091 recalcId(); 01092 } 01093 01094 01095 void KDGanttMinimizeSplitter::recalcId() 01096 { 01097 int n = data->list.count(); 01098 for ( int i = 0; i < n; i++ ) { 01099 TQSplitterLayoutStruct *s = data->list.at(i); 01100 if ( s->isSplitter ) 01101 ((KDGanttSplitterHandle*)s->wid)->setId(i); 01102 } 01103 } 01104 01105 01108 TQSize KDGanttMinimizeSplitter::sizeHint() const 01109 { 01110 constPolish(); 01111 int l = 0; 01112 int t = 0; 01113 if ( !childrenListObject().isEmpty() ) { 01114 const TQObjectList c = childrenListObject(); 01115 TQObjectListIt it( c ); 01116 TQObject * o; 01117 01118 while( (o=it.current()) != 0 ) { 01119 ++it; 01120 if ( o->isWidgetType() && 01121 !((TQWidget*)o)->isHidden() ) { 01122 TQSize s = ((TQWidget*)o)->sizeHint(); 01123 if ( s.isValid() ) { 01124 l += pick( s ); 01125 t = TQMAX( t, trans( s ) ); 01126 } 01127 } 01128 } 01129 } 01130 return orientation() == Qt::Horizontal ? TQSize( l, t ) : TQSize( t, l ); 01131 } 01132 01133 01138 TQSize KDGanttMinimizeSplitter::minimumSizeHint() const 01139 { 01140 constPolish(); 01141 int l = 0; 01142 int t = 0; 01143 if ( !childrenListObject().isEmpty() ) { 01144 const TQObjectList c = childrenListObject(); 01145 TQObjectListIt it( c ); 01146 TQObject * o; 01147 01148 while( (o=it.current()) != 0 ) { 01149 ++it; 01150 if ( o->isWidgetType() && 01151 !((TQWidget*)o)->isHidden() ) { 01152 TQSize s = minSizeHint((TQWidget*)o); 01153 if ( s.isValid() ) { 01154 l += pick( s ); 01155 t = TQMAX( t, trans( s ) ); 01156 } 01157 } 01158 } 01159 } 01160 return orientation() == Qt::Horizontal ? TQSize( l, t ) : TQSize( t, l ); 01161 } 01162 01163 01164 /* 01165 Calculates stretch parameters from current sizes 01166 */ 01167 01168 void KDGanttMinimizeSplitter::storeSizes() 01169 { 01170 TQSplitterLayoutStruct *s = data->list.first(); 01171 while ( s ) { 01172 if ( !s->isSplitter ) 01173 s->sizer = pick( s->wid->size() ); 01174 s = data->list.next(); 01175 } 01176 } 01177 01178 01179 #if 0 // ### remove this code ASAP 01180 01188 void KDGanttMinimizeSplitter::setHidden( TQWidget *w, bool hide ) 01189 { 01190 if ( w == w1 ) { 01191 w1show = !hide; 01192 } else if ( w == w2 ) { 01193 w2show = !hide; 01194 } else { 01195 #ifdef TQT_CHECK_RANGE 01196 qWarning( "KDGanttMinimizeSplitter::setHidden(), unknown widget" ); 01197 #endif 01198 return; 01199 } 01200 if ( hide ) 01201 w->hide(); 01202 else 01203 w->show(); 01204 recalc( TRUE ); 01205 } 01206 01207 01212 bool KDGanttMinimizeSplitter::isHidden( TQWidget *w ) const 01213 { 01214 if ( w == w1 ) 01215 return !w1show; 01216 else if ( w == w2 ) 01217 return !w2show; 01218 #ifdef TQT_CHECK_RANGE 01219 else 01220 qWarning( "KDGanttMinimizeSplitter::isHidden(), unknown widget" ); 01221 #endif 01222 return FALSE; 01223 } 01224 #endif 01225 01226 01248 TQValueList<int> KDGanttMinimizeSplitter::sizes() const 01249 { 01250 if ( !testWState(WState_Polished) ) { 01251 TQWidget* that = (TQWidget*) this; 01252 that->polish(); 01253 } 01254 TQValueList<int> list; 01255 TQSplitterLayoutStruct *s = data->list.first(); 01256 while ( s ) { 01257 if ( !s->isSplitter ) 01258 list.append( s->sizer ); 01259 s = data->list.next(); 01260 } 01261 return list; 01262 } 01263 01264 01265 01279 void KDGanttMinimizeSplitter::setSizes( TQValueList<int> list ) 01280 { 01281 processChildEvents(); 01282 TQValueList<int>::Iterator it = list.begin(); 01283 TQSplitterLayoutStruct *s = data->list.first(); 01284 while ( s && it != list.end() ) { 01285 if ( !s->isSplitter ) { 01286 s->sizer = *it; 01287 ++it; 01288 } 01289 s = data->list.next(); 01290 } 01291 doResize(); 01292 } 01293 01294 01300 void KDGanttMinimizeSplitter::processChildEvents() 01301 { 01302 TQApplication::sendPostedEvents( this, TQEvent::ChildInserted ); 01303 } 01304 01305 01310 void KDGanttMinimizeSplitter::styleChange( TQStyle& old ) 01311 { 01312 int sw = style().pixelMetric(TQStyle::PM_SplitterWidth, this); 01313 TQSplitterLayoutStruct *s = data->list.first(); 01314 while ( s ) { 01315 if ( s->isSplitter ) 01316 s->sizer = sw; 01317 s = data->list.next(); 01318 } 01319 doResize(); 01320 TQFrame::styleChange( old ); 01321 } 01322 01330 void KDGanttMinimizeSplitter::setMinimizeDirection( Direction direction ) 01331 { 01332 _direction = direction; 01333 } 01334 01338 KDGanttMinimizeSplitter::Direction KDGanttMinimizeSplitter::minimizeDirection() const 01339 { 01340 return _direction; 01341 } 01342 01343 /* 01344 This is a copy of qGeomCalc() in qlayoutengine.cpp which 01345 unfortunately isn't exported. 01346 */ 01347 static inline int toFixed( int i ) { return i * 256; } 01348 static inline int fRound( int i ) { 01349 return ( i % 256 < 128 ) ? i / 256 : 1 + i / 256; 01350 } 01351 void kdganttGeomCalc( TQMemArray<TQLayoutStruct> &chain, int start, int count, int pos, 01352 int space, int spacer ) 01353 { 01354 typedef int fixed; 01355 int cHint = 0; 01356 int cMin = 0; 01357 int cMax = 0; 01358 int sumStretch = 0; 01359 int spacerCount = 0; 01360 01361 bool wannaGrow = FALSE; // anyone who really wants to grow? 01362 // bool canShrink = FALSE; // anyone who could be persuaded to shrink? 01363 01364 int i; 01365 for ( i = start; i < start + count; i++ ) { 01366 chain[i].done = FALSE; 01367 cHint += chain[i].sizeHint; 01368 cMin += chain[i].minimumSize; 01369 cMax += chain[i].maximumSize; 01370 sumStretch += chain[i].stretch; 01371 if ( !chain[i].empty ) 01372 spacerCount++; 01373 wannaGrow = wannaGrow || chain[i].expansive; 01374 } 01375 01376 int extraspace = 0; 01377 if ( spacerCount ) 01378 spacerCount--; // only spacers between things 01379 if ( space < cMin + spacerCount * spacer ) { 01380 // qDebug("not enough space"); 01381 for ( i = start; i < start+count; i++ ) { 01382 chain[i].size = chain[i].minimumSize; 01383 chain[i].done = TRUE; 01384 } 01385 } else if ( space < cHint + spacerCount*spacer ) { 01386 // Less space than sizeHint, but more than minimum. 01387 // Currently take space equally from each, like in TQt 2.x. 01388 // Commented-out lines will give more space to stretchier items. 01389 int n = count; 01390 int space_left = space - spacerCount*spacer; 01391 int overdraft = cHint - space_left; 01392 //first give to the fixed ones: 01393 for ( i = start; i < start+count; i++ ) { 01394 if ( !chain[i].done && chain[i].minimumSize >= chain[i].sizeHint) { 01395 chain[i].size = chain[i].sizeHint; 01396 chain[i].done = TRUE; 01397 space_left -= chain[i].sizeHint; 01398 // sumStretch -= chain[i].stretch; 01399 n--; 01400 } 01401 } 01402 bool finished = n == 0; 01403 while ( !finished ) { 01404 finished = TRUE; 01405 fixed fp_over = toFixed( overdraft ); 01406 fixed fp_w = 0; 01407 01408 for ( i = start; i < start+count; i++ ) { 01409 if ( chain[i].done ) 01410 continue; 01411 // if ( sumStretch <= 0 ) 01412 fp_w += fp_over / n; 01413 // else 01414 // fp_w += (fp_over * chain[i].stretch) / sumStretch; 01415 int w = fRound( fp_w ); 01416 chain[i].size = chain[i].sizeHint - w; 01417 fp_w -= toFixed( w ); //give the difference to the next 01418 if ( chain[i].size < chain[i].minimumSize ) { 01419 chain[i].done = TRUE; 01420 chain[i].size = chain[i].minimumSize; 01421 finished = FALSE; 01422 overdraft -= chain[i].sizeHint - chain[i].minimumSize; 01423 // sumStretch -= chain[i].stretch; 01424 n--; 01425 break; 01426 } 01427 } 01428 } 01429 } else { //extra space 01430 int n = count; 01431 int space_left = space - spacerCount*spacer; 01432 // first give to the fixed ones, and handle non-expansiveness 01433 for ( i = start; i < start + count; i++ ) { 01434 if ( !chain[i].done && (chain[i].maximumSize <= chain[i].sizeHint 01435 || wannaGrow && !chain[i].expansive) ) { 01436 chain[i].size = chain[i].sizeHint; 01437 chain[i].done = TRUE; 01438 space_left -= chain[i].sizeHint; 01439 sumStretch -= chain[i].stretch; 01440 n--; 01441 } 01442 } 01443 extraspace = space_left; 01444 /* 01445 Do a trial distribution and calculate how much it is off. 01446 If there are more deficit pixels than surplus pixels, give 01447 the minimum size items what they need, and repeat. 01448 Otherwise give to the maximum size items, and repeat. 01449 01450 I have a wonderful mathematical proof for the correctness 01451 of this principle, but unfortunately this comment is too 01452 small to contain it. 01453 */ 01454 int surplus, deficit; 01455 do { 01456 surplus = deficit = 0; 01457 fixed fp_space = toFixed( space_left ); 01458 fixed fp_w = 0; 01459 for ( i = start; i < start+count; i++ ) { 01460 if ( chain[i].done ) 01461 continue; 01462 extraspace = 0; 01463 if ( sumStretch <= 0 ) 01464 fp_w += fp_space / n; 01465 else 01466 fp_w += (fp_space * chain[i].stretch) / sumStretch; 01467 int w = fRound( fp_w ); 01468 chain[i].size = w; 01469 fp_w -= toFixed( w ); // give the difference to the next 01470 if ( w < chain[i].sizeHint ) { 01471 deficit += chain[i].sizeHint - w; 01472 } else if ( w > chain[i].maximumSize ) { 01473 surplus += w - chain[i].maximumSize; 01474 } 01475 } 01476 if ( deficit > 0 && surplus <= deficit ) { 01477 // give to the ones that have too little 01478 for ( i = start; i < start+count; i++ ) { 01479 if ( !chain[i].done && 01480 chain[i].size < chain[i].sizeHint ) { 01481 chain[i].size = chain[i].sizeHint; 01482 chain[i].done = TRUE; 01483 space_left -= chain[i].sizeHint; 01484 sumStretch -= chain[i].stretch; 01485 n--; 01486 } 01487 } 01488 } 01489 if ( surplus > 0 && surplus >= deficit ) { 01490 // take from the ones that have too much 01491 for ( i = start; i < start+count; i++ ) { 01492 if ( !chain[i].done && 01493 chain[i].size > chain[i].maximumSize ) { 01494 chain[i].size = chain[i].maximumSize; 01495 chain[i].done = TRUE; 01496 space_left -= chain[i].maximumSize; 01497 sumStretch -= chain[i].stretch; 01498 n--; 01499 } 01500 } 01501 } 01502 } while ( n > 0 && surplus != deficit ); 01503 if ( n == 0 ) 01504 extraspace = space_left; 01505 } 01506 01507 // as a last resort, we distribute the unwanted space equally 01508 // among the spacers (counting the start and end of the chain). 01509 01510 //### should do a sub-pixel allocation of extra space 01511 int extra = extraspace / ( spacerCount + 2 ); 01512 int p = pos + extra; 01513 for ( i = start; i < start+count; i++ ) { 01514 chain[i].pos = p; 01515 p = p + chain[i].size; 01516 if ( !chain[i].empty ) 01517 p += spacer+extra; 01518 } 01519 } 01520 01521 #endif 01522