kdgantt

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     tqWarning( "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     tqWarning( "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     //  tqDebug("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