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 Qt, and distribute the resulting executable,
00030  ** without including the source code for Qt in the source distribution.
00031  **
00032  **********************************************************************/
00033 
00034 #include "KDGanttMinimizeSplitter.h"
00035 #ifndef QT_NO_SPLITTER
00036 
00037 #include "qpainter.h"
00038 #include "qdrawutil.h"
00039 #include "qbitmap.h"
00040 #if QT_VERSION >= 300
00041 #include "qptrlist.h"
00042 #include "qmemarray.h"
00043 #else
00044 #include <qlist.h>
00045 #include <qarray.h>
00046 #define QPtrList QList
00047 #define QMemArray QArray
00048 #endif
00049 #include "qlayoutengine_p.h"
00050 #include "qobjectlist.h"
00051 #include "qstyle.h"
00052 #include "qapplication.h" //sendPostedEvents
00053 #include <qvaluelist.h>
00054 #include <qcursor.h>
00055 #ifndef KDGANTT_MASTER_CVS
00056 #include "KDGanttMinimizeSplitter.moc"
00057 #endif
00058 
00059 
00060 #ifndef DOXYGEN_SKIP_INTERNAL
00061 
00062 #if QT_VERSION >= 300
00063 static int mouseOffset;
00064 static int opaqueOldPos = -1; //### there's only one mouse, but this is a bit risky
00065 
00066 
00067 KDGanttSplitterHandle::KDGanttSplitterHandle( Qt::Orientation o,
00068                   KDGanttMinimizeSplitter *parent, const char * name )
00069     : QWidget( parent, name ), _activeButton( 0 ), _collapsed( false )
00070 {
00071     s = parent;
00072     setOrientation(o);
00073     setMouseTracking( true );
00074 }
00075 
00076 QSize KDGanttSplitterHandle::sizeHint() const
00077 {
00078     return QSize(8,8);
00079 }
00080 
00081 void KDGanttSplitterHandle::setOrientation( Qt::Orientation o )
00082 {
00083     orient = o;
00084 #ifndef QT_NO_CURSOR
00085     if ( o == KDGanttMinimizeSplitter::Horizontal )
00086     setCursor( splitHCursor );
00087     else
00088     setCursor( splitVCursor );
00089 #endif
00090 }
00091 
00092 
00093 void KDGanttSplitterHandle::mouseMoveEvent( QMouseEvent *e )
00094 {
00095     updateCursor( e->pos() );
00096     if ( !(e->state()&LeftButton) )
00097     return;
00098 
00099     if ( _activeButton != 0)
00100         return;
00101 
00102     QCOORD pos = s->pick(parentWidget()->mapFromGlobal(e->globalPos()))
00103          - mouseOffset;
00104     if ( opaque() ) {
00105     s->moveSplitter( pos, id() );
00106     } else {
00107     int min = pos; int max = pos;
00108     s->getRange( id(), &min, &max );
00109     s->setRubberband( QMAX( min, QMIN(max, pos )));
00110     }
00111     _collapsed = false;
00112 }
00113 
00114 void KDGanttSplitterHandle::mousePressEvent( QMouseEvent *e )
00115 {
00116     if ( e->button() == LeftButton ) {
00117         _activeButton = onButton( e->pos() );
00118         mouseOffset = s->pick(e->pos());
00119         if ( _activeButton != 0)
00120             repaint();
00121         updateCursor( e->pos() );
00122     }
00123 }
00124 
00125 void KDGanttSplitterHandle::updateCursor( const QPoint& p)
00126 {
00127     if ( onButton( p ) != 0 ) {
00128         setCursor( arrowCursor );
00129     }
00130     else {
00131         if ( orient == KDGanttMinimizeSplitter::Horizontal )
00132             setCursor( splitHCursor );
00133         else
00134             setCursor( splitVCursor );
00135     }
00136 }
00137 
00138 
00139 void KDGanttSplitterHandle::mouseReleaseEvent( QMouseEvent *e )
00140 {
00141     if ( _activeButton != 0 ) {
00142         if ( onButton( e->pos() ) == _activeButton )
00143         {
00144             int pos;
00145             int min, max;
00146             if ( !_collapsed ) {
00147                 s->expandPos( id(), &min, &max );
00148                 if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Left
00149                      || s->minimizeDirection() == KDGanttMinimizeSplitter::Up ) {
00150                     pos = min;
00151                 }
00152                 else {
00153                     pos = max;
00154                 }
00155 
00156                 _origPos = s->pick(mapToParent( QPoint( 0,0 ) ));
00157                 s->moveSplitter( pos, id() );
00158                 _collapsed = true;
00159             }
00160             else {
00161                 s->moveSplitter( _origPos, id() );
00162                 _collapsed = false;
00163             }
00164 
00165         }
00166         _activeButton = 0;
00167         updateCursor( e->pos() );
00168     }
00169     else {
00170         if ( !opaque() && e->button() == LeftButton ) {
00171             QCOORD pos = s->pick(parentWidget()->mapFromGlobal(e->globalPos()))
00172                 - mouseOffset;
00173             s->setRubberband( -1 );
00174             s->moveSplitter( pos, id() );
00175         }
00176     }
00177     repaint();
00178 }
00179 
00180 int KDGanttSplitterHandle::onButton( const QPoint& p )
00181 {
00182     QValueList<QPointArray> list = buttonRegions();
00183     int index = 1;
00184     for( QValueList<QPointArray>::Iterator it = list.begin(); it != list.end(); ++it ) {
00185         QRect rect = (*it).boundingRect();
00186         rect.setLeft( rect.left()- 4 );
00187         rect.setRight( rect.right() + 4);
00188         rect.setTop( rect.top()- 4 );
00189         rect.setBottom( rect.bottom() + 4);
00190         if ( rect.contains( p ) ) {
00191             return index;
00192         }
00193         index++;
00194     }
00195     return 0;
00196 }
00197 
00198 
00199 QValueList<QPointArray> KDGanttSplitterHandle::buttonRegions()
00200 {
00201     QValueList<QPointArray> list;
00202 
00203     int sw = 8;
00204     int voffset[] = { (int) -sw*3, (int) sw*3 };
00205     for ( int i = 0; i < 2; i++ ) {
00206         QPointArray arr;
00207         if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Right ||
00208              _collapsed  && s->minimizeDirection() == KDGanttMinimizeSplitter::Left) {
00209             int mid = height()/2 + voffset[i];
00210             arr.setPoints( 3,
00211                            1, mid - sw + 4,
00212                            sw-3, mid,
00213                            1, mid + sw -4);
00214         }
00215         else if ( !_collapsed &&  s->minimizeDirection() == KDGanttMinimizeSplitter::Left ||
00216                   _collapsed  && s->minimizeDirection() == KDGanttMinimizeSplitter::Right ) {
00217             int mid = height()/2 + voffset[i];
00218             arr.setPoints( 3,
00219                            sw-4, mid - sw + 4,
00220                            0, mid,
00221                            sw-4, mid + sw - 4);
00222         }
00223         else if ( !_collapsed &&  s->minimizeDirection() == KDGanttMinimizeSplitter::Up ||
00224                   _collapsed  && s->minimizeDirection() == KDGanttMinimizeSplitter::Down) {
00225             int mid = width()/2 + voffset[i];
00226             arr.setPoints( 3,
00227                            mid - sw + 4, sw-4,
00228                            mid, 0,
00229                            mid + sw - 4, sw-4 );
00230         }
00231         else if ( !_collapsed && s->minimizeDirection() == KDGanttMinimizeSplitter::Down ||
00232                   _collapsed  && s->minimizeDirection() == KDGanttMinimizeSplitter::Up ) {
00233             int mid = width()/2 + voffset[i];
00234             arr.setPoints( 3,
00235                            mid - sw + 4, 1,
00236                            mid, sw-3,
00237                            mid + sw -4, 1);
00238         }
00239         list.append( arr );
00240     }
00241     return list;
00242 }
00243 
00244 void KDGanttSplitterHandle::paintEvent( QPaintEvent * )
00245 {
00246     QPixmap buffer( size() );
00247     QPainter p( &buffer );
00248 
00249     // Draw the splitter rectangle
00250     p.setBrush( colorGroup().background() );
00251     p.setPen( colorGroup().foreground() );
00252     p.drawRect( rect() );
00253     parentWidget()->style().drawPrimitive( QStyle::PE_Panel, &p, rect(),
00254                                            parentWidget()->colorGroup());
00255 
00256     int sw = 8; // Hardcoded, given I didn't use styles anymore, I didn't like to use their size
00257 
00258     // arrow color
00259     QColor col = colorGroup().background().dark( 200 );
00260     p.setBrush( col );
00261     p.setPen( col  );
00262 
00263     QValueList<QPointArray> list = buttonRegions();
00264     int index = 1;
00265     for ( QValueList<QPointArray>::Iterator it = list.begin(); it != list.end(); ++it ) {
00266         if ( index == _activeButton ) {
00267             p.save();
00268             p.translate( parentWidget()->style().pixelMetric( QStyle::PM_ButtonShiftHorizontal ),
00269                          parentWidget()->style().pixelMetric( QStyle::PM_ButtonShiftVertical ) );
00270             p.drawPolygon( *it, true );
00271             p.restore();
00272         }
00273         else {
00274             p.drawPolygon( *it, true );
00275         }
00276         index++;
00277     }
00278 
00279     // Draw the lines between the arrows
00280     if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Left ||
00281          s->minimizeDirection() == KDGanttMinimizeSplitter::Right ) {
00282         int mid = height()/2;
00283         p.drawLine ( 2, mid - sw, 2, mid + sw );
00284         p.drawLine ( 4, mid - sw, 4, mid + sw );
00285     }
00286     else if ( s->minimizeDirection() == KDGanttMinimizeSplitter::Up ||
00287               s->minimizeDirection() == KDGanttMinimizeSplitter::Down ) {
00288         int mid = width()/2;
00289         p.drawLine( mid -sw, 2, mid +sw, 2 );
00290         p.drawLine( mid -sw, 4, mid +sw, 4 );
00291     }
00292     bitBlt( this, 0, 0, &buffer );
00293 }
00294 #endif
00295 
00296 class QSplitterLayoutStruct
00297 {
00298 public:
00299     KDGanttMinimizeSplitter::ResizeMode mode;
00300     QCOORD sizer;
00301     bool isSplitter;
00302     QWidget *wid;
00303 };
00304 
00305 class QSplitterData
00306 {
00307 public:
00308     QSplitterData() : opaque( FALSE ), firstShow( TRUE ) {}
00309 
00310     QPtrList<QSplitterLayoutStruct> list;
00311     bool opaque;
00312     bool firstShow;
00313 };
00314 
00315 void kdganttGeomCalc( QMemArray<QLayoutStruct> &chain, int start, int count, int pos,
00316                  int space, int spacer );
00317 #endif // DOXYGEN_SKIP_INTERNAL
00318 
00319 
00369 static QSize minSize( const QWidget* /*w*/ )
00370 {
00371     return QSize(0,0);
00372 }
00373 
00374 // This is the original version of minSize
00375 static QSize minSizeHint( const QWidget* w )
00376 {
00377     QSize min = w->minimumSize();
00378     QSize s;
00379     if ( min.height() <= 0 || min.width() <= 0 )
00380     s = w->minimumSizeHint();
00381     if ( min.height() > 0 )
00382     s.setHeight( min.height() );
00383     if ( min.width() > 0 )
00384     s.setWidth( min.width() );
00385     return s.expandedTo(QSize(0,0));
00386 }
00387 
00388 
00389 
00394 KDGanttMinimizeSplitter::KDGanttMinimizeSplitter( QWidget *parent, const char *name )
00395     :QFrame(parent,name,WPaintUnclipped)
00396 {
00397 #if QT_VERSION >= 300
00398      orient = Horizontal;
00399      init();
00400 #endif
00401 }
00402 
00407 KDGanttMinimizeSplitter::KDGanttMinimizeSplitter( Orientation o, QWidget *parent, const char *name )
00408     :QFrame(parent,name,WPaintUnclipped)
00409 {
00410 #if QT_VERSION >= 300
00411      orient = o;
00412      init();
00413 #endif
00414 }
00415 
00419 KDGanttMinimizeSplitter::~KDGanttMinimizeSplitter()
00420 {
00421 #if QT_VERSION >= 300
00422     data->list.setAutoDelete( TRUE );
00423     delete data;
00424 #endif
00425 }
00426 
00427 
00428 #if QT_VERSION >= 300
00429 void KDGanttMinimizeSplitter::init()
00430 {
00431     data = new QSplitterData;
00432     if ( orient == Horizontal )
00433     setSizePolicy( QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum) );
00434     else
00435     setSizePolicy( QSizePolicy(QSizePolicy::Minimum,QSizePolicy::Expanding) );
00436 }
00437 #endif
00438 
00439 
00440 
00447 void KDGanttMinimizeSplitter::setOrientation( Orientation o )
00448 {
00449 #if QT_VERSION >= 300
00450     if ( orient == o )
00451     return;
00452     orient = o;
00453 
00454     if ( orient == Horizontal )
00455     setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ) );
00456     else
00457     setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding ) );
00458 
00459     QSplitterLayoutStruct *s = data->list.first();
00460     while ( s ) {
00461     if ( s->isSplitter )
00462         ((KDGanttSplitterHandle*)s->wid)->setOrientation( o );
00463     s = data->list.next();  // ### next at end of loop, no iterator
00464     }
00465     recalc( isVisible() );
00466 #endif
00467 }
00468 
00469 
00470 #if QT_VERSION >= 300
00471 
00474 void KDGanttMinimizeSplitter::resizeEvent( QResizeEvent * )
00475 {
00476     doResize();
00477 }
00478 
00479 
00480 /*
00481   Inserts the widget \a w at the end (or at the beginning if \a first
00482   is TRUE) of the splitter's list of widgets.
00483 
00484   It is the responsibility of the caller of this function to make sure
00485   that \a w is not already in the splitter and to call recalcId if
00486   needed.  (If \a first is TRUE, then recalcId is very probably
00487   needed.)
00488 */
00489 QSplitterLayoutStruct *KDGanttMinimizeSplitter::addWidget( QWidget *w, bool first )
00490 {
00491     QSplitterLayoutStruct *s;
00492     KDGanttSplitterHandle *newHandle = 0;
00493     if ( data->list.count() > 0 ) {
00494     s = new QSplitterLayoutStruct;
00495     s->mode = KeepSize;
00496     QString tmp = "qt_splithandle_";
00497     tmp += w->name();
00498     newHandle = new KDGanttSplitterHandle( orientation(), this, tmp.latin1() );
00499     s->wid = newHandle;
00500     newHandle->setId(data->list.count());
00501     s->isSplitter = TRUE;
00502     s->sizer = pick( newHandle->sizeHint() );
00503     if ( first )
00504         data->list.insert( 0, s );
00505     else
00506         data->list.append( s );
00507     }
00508     s = new QSplitterLayoutStruct;
00509     s->mode = Stretch;
00510     s->wid = w;
00511     if ( !testWState( WState_Resized ) && w->sizeHint().isValid() )
00512     s->sizer = pick( w->sizeHint() );
00513     else
00514     s->sizer = pick( w->size() );
00515     s->isSplitter = FALSE;
00516     if ( first )
00517     data->list.insert( 0, s );
00518     else
00519     data->list.append( s );
00520     if ( newHandle && isVisible() )
00521     newHandle->show(); //will trigger sending of post events
00522     return s;
00523 }
00524 
00525 
00530 void KDGanttMinimizeSplitter::childEvent( QChildEvent *c )
00531 {
00532     if ( c->type() == QEvent::ChildInserted ) {
00533     if ( !c->child()->isWidgetType() )
00534         return;
00535 
00536     if ( ((QWidget*)c->child())->testWFlags( WType_TopLevel ) )
00537         return;
00538 
00539     QSplitterLayoutStruct *s = data->list.first();
00540     while ( s ) {
00541         if ( s->wid == c->child() )
00542         return;
00543         s = data->list.next();
00544     }
00545     addWidget( (QWidget*)c->child() );
00546     recalc( isVisible() );
00547 
00548     } else if ( c->type() == QEvent::ChildRemoved ) {
00549     QSplitterLayoutStruct *p = 0;
00550     if ( data->list.count() > 1 )
00551         p = data->list.at(1); //remove handle _after_ first widget.
00552     QSplitterLayoutStruct *s = data->list.first();
00553     while ( s ) {
00554         if ( s->wid == c->child() ) {
00555         data->list.removeRef( s );
00556         delete s;
00557         if ( p && p->isSplitter ) {
00558             data->list.removeRef( p );
00559             delete p->wid; //will call childEvent
00560             delete p;
00561         }
00562         recalcId();
00563         doResize();
00564         return;
00565         }
00566         p = s;
00567         s = data->list.next();
00568     }
00569     }
00570 }
00571 
00572 
00577 void KDGanttMinimizeSplitter::setRubberband( int p )
00578 {
00579     QPainter paint( this );
00580     paint.setPen( gray );
00581     paint.setBrush( gray );
00582     paint.setRasterOp( XorROP );
00583     QRect r = contentsRect();
00584     const int rBord = 3; //Themable????
00585     int sw = style().pixelMetric(QStyle::PM_SplitterWidth, this);
00586     if ( orient == Horizontal ) {
00587     if ( opaqueOldPos >= 0 )
00588         paint.drawRect( opaqueOldPos + sw/2 - rBord , r.y(),
00589                 2*rBord, r.height() );
00590     if ( p >= 0 )
00591         paint.drawRect( p  + sw/2 - rBord, r.y(), 2*rBord, r.height() );
00592     } else {
00593     if ( opaqueOldPos >= 0 )
00594         paint.drawRect( r.x(), opaqueOldPos + sw/2 - rBord,
00595                 r.width(), 2*rBord );
00596     if ( p >= 0 )
00597         paint.drawRect( r.x(), p + sw/2 - rBord, r.width(), 2*rBord );
00598     }
00599     opaqueOldPos = p;
00600 }
00601 
00602 
00604 bool KDGanttMinimizeSplitter::event( QEvent *e )
00605 {
00606     if ( e->type() == QEvent::LayoutHint || ( e->type() == QEvent::Show && data->firstShow ) ) {
00607     recalc( isVisible() );
00608     if ( e->type() == QEvent::Show )
00609         data->firstShow = FALSE;
00610     }
00611     return QWidget::event( e );
00612 }
00613 
00614 
00622 void KDGanttMinimizeSplitter::drawSplitter( QPainter *p,
00623                   QCOORD x, QCOORD y, QCOORD w, QCOORD h )
00624 {
00625     style().drawPrimitive(QStyle::PE_Splitter, p, QRect(x, y, w, h), colorGroup(),
00626               (orientation() == Qt::Horizontal ?
00627                QStyle::Style_Horizontal : 0));
00628 }
00629 
00630 
00636 int KDGanttMinimizeSplitter::idAfter( QWidget* w ) const
00637 {
00638     QSplitterLayoutStruct *s = data->list.first();
00639     bool seen_w = FALSE;
00640     while ( s ) {
00641     if ( s->isSplitter && seen_w )
00642         return data->list.at();
00643     if ( !s->isSplitter && s->wid == w )
00644         seen_w = TRUE;
00645     s = data->list.next();
00646     }
00647     return 0;
00648 }
00649 
00650 
00663 void KDGanttMinimizeSplitter::moveSplitter( QCOORD p, int id )
00664 {
00665     p = adjustPos( p, id );
00666 
00667     QSplitterLayoutStruct *s = data->list.at(id);
00668     int oldP = orient == Horizontal ? s->wid->x() : s->wid->y();
00669     bool upLeft;
00670     if ( QApplication::reverseLayout() && orient == Horizontal ) {
00671     p += s->wid->width();
00672     upLeft = p > oldP;
00673     } else
00674     upLeft = p < oldP;
00675 
00676     moveAfter( p, id, upLeft );
00677     moveBefore( p-1, id-1, upLeft );
00678 
00679     storeSizes();
00680 }
00681 
00682 
00683 void KDGanttMinimizeSplitter::setG( QWidget *w, int p, int s, bool isSplitter )
00684 {
00685     if ( orient == Horizontal ) {
00686     if ( QApplication::reverseLayout() && orient == Horizontal && !isSplitter )
00687         p = contentsRect().width() - p - s;
00688     w->setGeometry( p, contentsRect().y(), s, contentsRect().height() );
00689     } else
00690     w->setGeometry( contentsRect().x(), p, contentsRect().width(), s );
00691 }
00692 
00693 
00694 /*
00695   Places the right/bottom edge of the widget at \a id at position \a pos.
00696 
00697   \sa idAfter()
00698 */
00699 void KDGanttMinimizeSplitter::moveBefore( int pos, int id, bool upLeft )
00700 {
00701     if( id < 0 )
00702     return;
00703     QSplitterLayoutStruct *s = data->list.at(id);
00704     if ( !s )
00705     return;
00706     QWidget *w = s->wid;
00707     if ( w->isHidden() ) {
00708     moveBefore( pos, id-1, upLeft );
00709     } else if ( s->isSplitter ) {
00710     int pos1, pos2;
00711     int dd = s->sizer;
00712     if( QApplication::reverseLayout() && orient == Horizontal ) {
00713         pos1 = pos;
00714         pos2 = pos + dd;
00715     } else {
00716         pos2 = pos - dd;
00717         pos1 = pos2 + 1;
00718     }
00719     if ( upLeft ) {
00720         setG( w, pos1, dd, TRUE );
00721         moveBefore( pos2, id-1, upLeft );
00722     } else {
00723         moveBefore( pos2, id-1, upLeft );
00724         setG( w, pos1, dd, TRUE );
00725     }
00726     } else {
00727     int dd, newLeft, nextPos;
00728     if( QApplication::reverseLayout() && orient == Horizontal ) {
00729         dd = w->geometry().right() - pos;
00730         dd = QMAX( pick(minSize(w)), QMIN(dd, pick(w->maximumSize())));
00731         newLeft = pos+1;
00732         nextPos = newLeft + dd;
00733     } else {
00734         dd = pos - pick( w->pos() ) + 1;
00735         dd = QMAX( pick(minSize(w)), QMIN(dd, pick(w->maximumSize())));
00736         newLeft = pos-dd+1;
00737         nextPos = newLeft - 1;
00738     }
00739     setG( w, newLeft, dd, TRUE );
00740     moveBefore( nextPos, id-1, upLeft );
00741     }
00742 }
00743 
00744 
00745 /*
00746   Places the left/top edge of the widget at \a id at position \a pos.
00747 
00748   \sa idAfter()
00749 */
00750 void KDGanttMinimizeSplitter::moveAfter( int pos, int id, bool upLeft )
00751 {
00752     QSplitterLayoutStruct *s = id < int(data->list.count()) ?
00753                    data->list.at(id) : 0;
00754     if ( !s )
00755     return;
00756     QWidget *w = s->wid;
00757     if ( w->isHidden() ) {
00758     moveAfter( pos, id+1, upLeft );
00759     } else if ( pick( w->pos() ) == pos ) {
00760     //No need to do anything if it's already there.
00761     return;
00762     } else if ( s->isSplitter ) {
00763     int dd = s->sizer;
00764     int pos1, pos2;
00765     if( QApplication::reverseLayout() && orient == Horizontal ) {
00766         pos2 = pos - dd;
00767         pos1 = pos2 + 1;
00768     } else {
00769         pos1 = pos;
00770         pos2 = pos + dd;
00771     }
00772     if ( upLeft ) {
00773         setG( w, pos1, dd, TRUE );
00774         moveAfter( pos2, id+1, upLeft );
00775     } else {
00776         moveAfter( pos2, id+1, upLeft );
00777         setG( w, pos1, dd, TRUE );
00778     }
00779     } else {
00780     int left = pick( w->pos() );
00781     int right, dd,/* newRight,*/ newLeft, nextPos;
00782     if ( QApplication::reverseLayout() && orient == Horizontal ) {
00783         dd = pos - left + 1;
00784         dd = QMAX( pick(minSize(w)), QMIN(dd, pick(w->maximumSize())));
00785         newLeft = pos-dd+1;
00786         nextPos = newLeft - 1;
00787     } else {
00788         right = pick( w->geometry().bottomRight() );
00789         dd = right - pos + 1;
00790         dd = QMAX( pick(minSize(w)), QMIN(dd, pick(w->maximumSize())));
00791         /*newRight = pos+dd-1;*/
00792         newLeft = pos;
00793         nextPos = newLeft + dd;
00794     }
00795     setG( w, newLeft, dd, TRUE );
00796     /*if( right != newRight )*/
00797     moveAfter( nextPos, id+1, upLeft );
00798     }
00799 }
00800 
00801 
00802 void KDGanttMinimizeSplitter::expandPos( int id, int*  min, int* max )
00803 {
00804     QSplitterLayoutStruct *s = data->list.at(id-1);
00805     QWidget* w = s->wid;
00806     *min = pick( w->mapToParent( QPoint(0,0) ) );
00807 
00808     if ( (uint) id == data->list.count() ) {
00809         pick( size() );
00810     }
00811     else {
00812         QSplitterLayoutStruct *s = data->list.at(id+1);
00813         QWidget* w = s->wid;
00814         *max = pick( w->mapToParent( QPoint( w->width(), w->height() ) ) ) -8;
00815     }
00816 }
00817 
00818 
00825 void KDGanttMinimizeSplitter::getRange( int id, int *min, int *max )
00826 {
00827     int minB = 0;   //before
00828     int maxB = 0;
00829     int minA = 0;
00830     int maxA = 0;   //after
00831     int n = data->list.count();
00832     if ( id < 0 || id >= n )
00833     return;
00834     int i;
00835     for ( i = 0; i < id; i++ ) {
00836     QSplitterLayoutStruct *s = data->list.at(i);
00837     if ( s->wid->isHidden() ) {
00838         //ignore
00839     } else if ( s->isSplitter ) {
00840         minB += s->sizer;
00841         maxB += s->sizer;
00842     } else {
00843         minB += pick( minSize(s->wid) );
00844         maxB += pick( s->wid->maximumSize() );
00845     }
00846     }
00847     for ( i = id; i < n; i++ ) {
00848     QSplitterLayoutStruct *s = data->list.at(i);
00849     if ( s->wid->isHidden() ) {
00850         //ignore
00851     } else if ( s->isSplitter ) {
00852         minA += s->sizer;
00853         maxA += s->sizer;
00854     } else {
00855         minA += pick( minSize(s->wid) );
00856         maxA += pick( s->wid->maximumSize() );
00857     }
00858     }
00859     QRect r = contentsRect();
00860     if ( orient == Horizontal && QApplication::reverseLayout() ) {
00861     int splitterWidth = style().pixelMetric(QStyle::PM_SplitterWidth, this);
00862     if ( min )
00863         *min = pick(r.topRight()) - QMIN( maxB, pick(r.size())-minA ) - splitterWidth;
00864     if ( max )
00865         *max = pick(r.topRight()) - QMAX( minB, pick(r.size())-maxA ) - splitterWidth;
00866     } else {
00867     if ( min )
00868         *min = pick(r.topLeft()) + QMAX( minB, pick(r.size())-maxA );
00869     if ( max )
00870         *max = pick(r.topLeft()) + QMIN( maxB, pick(r.size())-minA );
00871     }
00872 }
00873 
00874 
00881 int KDGanttMinimizeSplitter::adjustPos( int p, int id )
00882 {
00883     int min = 0;
00884     int max = 0;
00885     getRange( id, &min, &max );
00886     p = QMAX( min, QMIN( p, max ) );
00887 
00888     return p;
00889 }
00890 
00891 
00892 void KDGanttMinimizeSplitter::doResize()
00893 {
00894     QRect r = contentsRect();
00895     int i;
00896     int n = data->list.count();
00897     QMemArray<QLayoutStruct> a( n );
00898     for ( i = 0; i< n; i++ ) {
00899     a[i].init();
00900     QSplitterLayoutStruct *s = data->list.at(i);
00901     if ( s->wid->isHidden() ) {
00902         a[i].stretch = 0;
00903         a[i].sizeHint = a[i].minimumSize = 0;
00904         a[i].maximumSize = 0;
00905     } else if ( s->isSplitter ) {
00906         a[i].stretch = 0;
00907         a[i].sizeHint = a[i].minimumSize = a[i].maximumSize = s->sizer;
00908         a[i].empty = FALSE;
00909     } else if ( s->mode == KeepSize ) {
00910         a[i].stretch = 0;
00911         a[i].minimumSize = pick( minSize(s->wid) );
00912         a[i].sizeHint = s->sizer;
00913         a[i].maximumSize = pick( s->wid->maximumSize() );
00914         a[i].empty = FALSE;
00915     } else if ( s->mode == FollowSizeHint ) {
00916         a[i].stretch = 0;
00917         a[i].minimumSize = a[i].sizeHint = pick( s->wid->sizeHint() );
00918         a[i].maximumSize = pick( s->wid->maximumSize() );
00919         a[i].empty = FALSE;
00920     } else { //proportional
00921         a[i].stretch = s->sizer;
00922         a[i].maximumSize = pick( s->wid->maximumSize() );
00923         a[i].sizeHint = a[i].minimumSize = pick( minSize(s->wid) );
00924         a[i].empty = FALSE;
00925     }
00926     }
00927 
00928     kdganttGeomCalc( a, 0, n, pick( r.topLeft() ), pick( r.size() ), 0 );
00929 
00930     for ( i = 0; i< n; i++ ) {
00931     QSplitterLayoutStruct *s = data->list.at(i);
00932     setG( s->wid, a[i].pos, a[i].size );
00933     }
00934 
00935 }
00936 
00937 
00938 void KDGanttMinimizeSplitter::recalc( bool update )
00939 {
00940     int fi = 2*frameWidth();
00941     int maxl = fi;
00942     int minl = fi;
00943     int maxt = QWIDGETSIZE_MAX;
00944     int mint = fi;
00945     int n = data->list.count();
00946     bool first = TRUE;
00947     /*
00948       The splitter before a hidden widget is always hidden.
00949       The splitter before the first visible widget is hidden.
00950       The splitter before any other visible widget is visible.
00951     */
00952     for ( int i = 0; i< n; i++ ) {
00953     QSplitterLayoutStruct *s = data->list.at(i);
00954     if ( !s->isSplitter ) {
00955         QSplitterLayoutStruct *p = (i > 0) ? data->list.at( i-1 ) : 0;
00956         if ( p && p->isSplitter )
00957         if ( first || s->wid->isHidden() )
00958             p->wid->hide(); //may trigger new recalc
00959         else
00960             p->wid->show(); //may trigger new recalc
00961         if ( !s->wid->isHidden() )
00962         first = FALSE;
00963     }
00964     }
00965 
00966     bool empty=TRUE;
00967     for ( int j = 0; j< n; j++ ) {
00968     QSplitterLayoutStruct *s = data->list.at(j);
00969     if ( !s->wid->isHidden() ) {
00970         empty = FALSE;
00971         if ( s->isSplitter ) {
00972         minl += s->sizer;
00973         maxl += s->sizer;
00974         } else {
00975         QSize minS = minSize(s->wid);
00976         minl += pick( minS );
00977         maxl += pick( s->wid->maximumSize() );
00978         mint = QMAX( mint, trans( minS ));
00979         int tm = trans( s->wid->maximumSize() );
00980         if ( tm > 0 )
00981             maxt = QMIN( maxt, tm );
00982         }
00983     }
00984     }
00985     if ( empty ) {
00986         if ( parentWidget() != 0 && parentWidget()->inherits("KDGanttMinimizeSplitter") ) {
00987             // nested splitters; be nice
00988             maxl = maxt = 0;
00989         } else {
00990             // KDGanttMinimizeSplitter with no children yet
00991             maxl = QWIDGETSIZE_MAX;
00992         }
00993     } else {
00994         maxl = QMIN( maxl, QWIDGETSIZE_MAX );
00995     }
00996     if ( maxt < mint )
00997     maxt = mint;
00998 
00999     if ( orient == Horizontal ) {
01000     setMaximumSize( maxl, maxt );
01001     setMinimumSize( minl, mint );
01002     } else {
01003     setMaximumSize( maxt, maxl );
01004     setMinimumSize( mint, minl );
01005     }
01006     if ( update )
01007     doResize();
01008 }
01009 
01016 void KDGanttMinimizeSplitter::setResizeMode( QWidget *w, ResizeMode mode )
01017 {
01018     processChildEvents();
01019     QSplitterLayoutStruct *s = data->list.first();
01020     while ( s ) {
01021     if ( s->wid == w  ) {
01022         s->mode = mode;
01023         return;
01024     }
01025     s = data->list.next();
01026     }
01027     s = addWidget( w, TRUE );
01028     s->mode = mode;
01029 }
01030 
01031 
01038 bool KDGanttMinimizeSplitter::opaqueResize() const
01039 {
01040     return data->opaque;
01041 }
01042 
01043 
01052 void KDGanttMinimizeSplitter::setOpaqueResize( bool on )
01053 {
01054     data->opaque = on;
01055 }
01056 
01057 
01062 void KDGanttMinimizeSplitter::moveToFirst( QWidget *w )
01063 {
01064     processChildEvents();
01065     bool found = FALSE;
01066     QSplitterLayoutStruct *s = data->list.first();
01067     while ( s ) {
01068     if ( s->wid == w  ) {
01069         found = TRUE;
01070         QSplitterLayoutStruct *p = data->list.prev();
01071         if ( p ) { // not already at first place
01072         data->list.take(); //take p
01073         data->list.take(); // take s
01074         data->list.insert( 0, p );
01075         data->list.insert( 0, s );
01076         }
01077         break;
01078     }
01079     s = data->list.next();
01080     }
01081      if ( !found )
01082     addWidget( w, TRUE );
01083      recalcId();
01084 }
01085 
01086 
01091 void KDGanttMinimizeSplitter::moveToLast( QWidget *w )
01092 {
01093     processChildEvents();
01094     bool found = FALSE;
01095     QSplitterLayoutStruct *s = data->list.first();
01096     while ( s ) {
01097     if ( s->wid == w  ) {
01098         found = TRUE;
01099         data->list.take(); // take s
01100         QSplitterLayoutStruct *p = data->list.current();
01101         if ( p ) { // the splitter handle after s
01102         data->list.take(); //take p
01103         data->list.append( p );
01104         }
01105         data->list.append( s );
01106         break;
01107     }
01108     s = data->list.next();
01109     }
01110      if ( !found )
01111     addWidget( w);
01112      recalcId();
01113 }
01114 
01115 
01116 void KDGanttMinimizeSplitter::recalcId()
01117 {
01118     int n = data->list.count();
01119     for ( int i = 0; i < n; i++ ) {
01120     QSplitterLayoutStruct *s = data->list.at(i);
01121     if ( s->isSplitter )
01122         ((KDGanttSplitterHandle*)s->wid)->setId(i);
01123     }
01124 }
01125 
01126 
01129 QSize KDGanttMinimizeSplitter::sizeHint() const
01130 {
01131     constPolish();
01132     int l = 0;
01133     int t = 0;
01134     if ( children() ) {
01135     const QObjectList * c = children();
01136     QObjectListIt it( *c );
01137     QObject * o;
01138 
01139     while( (o=it.current()) != 0 ) {
01140         ++it;
01141         if ( o->isWidgetType() &&
01142          !((QWidget*)o)->isHidden() ) {
01143         QSize s = ((QWidget*)o)->sizeHint();
01144         if ( s.isValid() ) {
01145             l += pick( s );
01146             t = QMAX( t, trans( s ) );
01147         }
01148         }
01149     }
01150     }
01151     return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
01152 }
01153 
01154 
01159 QSize KDGanttMinimizeSplitter::minimumSizeHint() const
01160 {
01161     constPolish();
01162     int l = 0;
01163     int t = 0;
01164     if ( children() ) {
01165     const QObjectList * c = children();
01166     QObjectListIt it( *c );
01167     QObject * o;
01168 
01169     while( (o=it.current()) != 0 ) {
01170         ++it;
01171         if ( o->isWidgetType() &&
01172          !((QWidget*)o)->isHidden() ) {
01173         QSize s = minSizeHint((QWidget*)o);
01174         if ( s.isValid() ) {
01175             l += pick( s );
01176             t = QMAX( t, trans( s ) );
01177         }
01178         }
01179     }
01180     }
01181     return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l );
01182 }
01183 
01184 
01185 /*
01186   Calculates stretch parameters from current sizes
01187 */
01188 
01189 void KDGanttMinimizeSplitter::storeSizes()
01190 {
01191     QSplitterLayoutStruct *s = data->list.first();
01192     while ( s ) {
01193     if ( !s->isSplitter )
01194         s->sizer = pick( s->wid->size() );
01195     s = data->list.next();
01196     }
01197 }
01198 
01199 
01200 #if 0 // ### remove this code ASAP
01201 
01209 void KDGanttMinimizeSplitter::setHidden( QWidget *w, bool hide )
01210 {
01211     if ( w == w1 ) {
01212     w1show = !hide;
01213     } else if ( w == w2 ) {
01214     w2show = !hide;
01215     } else {
01216 #ifdef QT_CHECK_RANGE
01217     qWarning( "KDGanttMinimizeSplitter::setHidden(), unknown widget" );
01218 #endif
01219     return;
01220     }
01221     if ( hide )
01222     w->hide();
01223     else
01224     w->show();
01225     recalc( TRUE );
01226 }
01227 
01228 
01233 bool KDGanttMinimizeSplitter::isHidden( QWidget *w ) const
01234 {
01235     if ( w == w1 )
01236     return !w1show;
01237      else if ( w == w2 )
01238     return !w2show;
01239 #ifdef QT_CHECK_RANGE
01240     else
01241     qWarning( "KDGanttMinimizeSplitter::isHidden(), unknown widget" );
01242 #endif
01243     return FALSE;
01244 }
01245 #endif
01246 
01247 
01269 QValueList<int> KDGanttMinimizeSplitter::sizes() const
01270 {
01271     if ( !testWState(WState_Polished) ) {
01272     QWidget* that = (QWidget*) this;
01273     that->polish();
01274     }
01275     QValueList<int> list;
01276     QSplitterLayoutStruct *s = data->list.first();
01277     while ( s ) {
01278     if ( !s->isSplitter )
01279         list.append( s->sizer );
01280     s = data->list.next();
01281     }
01282     return list;
01283 }
01284 
01285 
01286 
01300 void KDGanttMinimizeSplitter::setSizes( QValueList<int> list )
01301 {
01302     processChildEvents();
01303     QValueList<int>::Iterator it = list.begin();
01304     QSplitterLayoutStruct *s = data->list.first();
01305     while ( s && it != list.end() ) {
01306     if ( !s->isSplitter ) {
01307         s->sizer = *it;
01308         ++it;
01309     }
01310     s = data->list.next();
01311     }
01312     doResize();
01313 }
01314 
01315 
01321 void KDGanttMinimizeSplitter::processChildEvents()
01322 {
01323     QApplication::sendPostedEvents( this, QEvent::ChildInserted );
01324 }
01325 
01326 
01331 void KDGanttMinimizeSplitter::styleChange( QStyle& old )
01332 {
01333     int sw = style().pixelMetric(QStyle::PM_SplitterWidth, this);
01334     QSplitterLayoutStruct *s = data->list.first();
01335     while ( s ) {
01336     if ( s->isSplitter )
01337         s->sizer = sw;
01338     s = data->list.next();
01339     }
01340     doResize();
01341     QFrame::styleChange( old );
01342 }
01343 
01344 #endif
01345 
01353 void KDGanttMinimizeSplitter::setMinimizeDirection( Direction direction )
01354 {
01355     _direction = direction;
01356 }
01357 
01361 KDGanttMinimizeSplitter::Direction KDGanttMinimizeSplitter::minimizeDirection() const
01362 {
01363     return _direction;
01364 }
01365 
01366 /*
01367   This is a copy of qGeomCalc() in qlayoutengine.cpp which
01368   unfortunately isn't exported.
01369 */
01370 static inline int toFixed( int i ) { return i * 256; }
01371 static inline int fRound( int i ) {
01372     return ( i % 256 < 128 ) ? i / 256 : 1 + i / 256;
01373 }
01374 void kdganttGeomCalc( QMemArray<QLayoutStruct> &chain, int start, int count, int pos,
01375         int space, int spacer )
01376 {
01377     typedef int fixed;
01378     int cHint = 0;
01379     int cMin = 0;
01380     int cMax = 0;
01381     int sumStretch = 0;
01382     int spacerCount = 0;
01383 
01384     bool wannaGrow = FALSE; // anyone who really wants to grow?
01385     //    bool canShrink = FALSE; // anyone who could be persuaded to shrink?
01386 
01387     int i;
01388     for ( i = start; i < start + count; i++ ) {
01389     chain[i].done = FALSE;
01390     cHint += chain[i].sizeHint;
01391     cMin += chain[i].minimumSize;
01392     cMax += chain[i].maximumSize;
01393     sumStretch += chain[i].stretch;
01394     if ( !chain[i].empty )
01395         spacerCount++;
01396     wannaGrow = wannaGrow || chain[i].expansive;
01397     }
01398 
01399     int extraspace = 0;
01400     if ( spacerCount )
01401     spacerCount--; // only spacers between things
01402     if ( space < cMin + spacerCount * spacer ) {
01403     //  qDebug("not enough space");
01404     for ( i = start; i < start+count; i++ ) {
01405         chain[i].size = chain[i].minimumSize;
01406         chain[i].done = TRUE;
01407     }
01408     } else if ( space < cHint + spacerCount*spacer ) {
01409     // Less space than sizeHint, but more than minimum.
01410     // Currently take space equally from each, like in Qt 2.x.
01411     // Commented-out lines will give more space to stretchier items.
01412     int n = count;
01413     int space_left = space - spacerCount*spacer;
01414     int overdraft = cHint - space_left;
01415     //first give to the fixed ones:
01416     for ( i = start; i < start+count; i++ ) {
01417         if ( !chain[i].done && chain[i].minimumSize >= chain[i].sizeHint) {
01418         chain[i].size = chain[i].sizeHint;
01419         chain[i].done = TRUE;
01420         space_left -= chain[i].sizeHint;
01421         // sumStretch -= chain[i].stretch;
01422         n--;
01423         }
01424     }
01425     bool finished = n == 0;
01426     while ( !finished ) {
01427         finished = TRUE;
01428         fixed fp_over = toFixed( overdraft );
01429         fixed fp_w = 0;
01430 
01431         for ( i = start; i < start+count; i++ ) {
01432         if ( chain[i].done )
01433             continue;
01434         // if ( sumStretch <= 0 )
01435         fp_w += fp_over / n;
01436         // else
01437         //    fp_w += (fp_over * chain[i].stretch) / sumStretch;
01438         int w = fRound( fp_w );
01439         chain[i].size = chain[i].sizeHint - w;
01440         fp_w -= toFixed( w ); //give the difference to the next
01441         if ( chain[i].size < chain[i].minimumSize ) {
01442             chain[i].done = TRUE;
01443             chain[i].size = chain[i].minimumSize;
01444             finished = FALSE;
01445             overdraft -= chain[i].sizeHint - chain[i].minimumSize;
01446             // sumStretch -= chain[i].stretch;
01447             n--;
01448             break;
01449         }
01450         }
01451     }
01452     } else { //extra space
01453     int n = count;
01454     int space_left = space - spacerCount*spacer;
01455     // first give to the fixed ones, and handle non-expansiveness
01456     for ( i = start; i < start + count; i++ ) {
01457         if ( !chain[i].done && (chain[i].maximumSize <= chain[i].sizeHint
01458                     || wannaGrow && !chain[i].expansive) ) {
01459         chain[i].size = chain[i].sizeHint;
01460         chain[i].done = TRUE;
01461         space_left -= chain[i].sizeHint;
01462         sumStretch -= chain[i].stretch;
01463         n--;
01464         }
01465     }
01466     extraspace = space_left;
01467     /*
01468       Do a trial distribution and calculate how much it is off.
01469       If there are more deficit pixels than surplus pixels, give
01470       the minimum size items what they need, and repeat.
01471       Otherwise give to the maximum size items, and repeat.
01472 
01473       I have a wonderful mathematical proof for the correctness
01474       of this principle, but unfortunately this comment is too
01475       small to contain it.
01476     */
01477     int surplus, deficit;
01478     do {
01479         surplus = deficit = 0;
01480         fixed fp_space = toFixed( space_left );
01481         fixed fp_w = 0;
01482         for ( i = start; i < start+count; i++ ) {
01483         if ( chain[i].done )
01484             continue;
01485         extraspace = 0;
01486         if ( sumStretch <= 0 )
01487             fp_w += fp_space / n;
01488         else
01489             fp_w += (fp_space * chain[i].stretch) / sumStretch;
01490         int w = fRound( fp_w );
01491         chain[i].size = w;
01492         fp_w -= toFixed( w ); // give the difference to the next
01493         if ( w < chain[i].sizeHint ) {
01494             deficit +=  chain[i].sizeHint - w;
01495         } else if ( w > chain[i].maximumSize ) {
01496             surplus += w - chain[i].maximumSize;
01497         }
01498         }
01499         if ( deficit > 0 && surplus <= deficit ) {
01500         // give to the ones that have too little
01501         for ( i = start; i < start+count; i++ ) {
01502             if ( !chain[i].done &&
01503              chain[i].size < chain[i].sizeHint ) {
01504             chain[i].size = chain[i].sizeHint;
01505             chain[i].done = TRUE;
01506             space_left -= chain[i].sizeHint;
01507             sumStretch -= chain[i].stretch;
01508             n--;
01509             }
01510         }
01511         }
01512         if ( surplus > 0 && surplus >= deficit ) {
01513         // take from the ones that have too much
01514         for ( i = start; i < start+count; i++ ) {
01515             if ( !chain[i].done &&
01516              chain[i].size > chain[i].maximumSize ) {
01517             chain[i].size = chain[i].maximumSize;
01518             chain[i].done = TRUE;
01519             space_left -= chain[i].maximumSize;
01520             sumStretch -= chain[i].stretch;
01521             n--;
01522             }
01523         }
01524         }
01525     } while ( n > 0 && surplus != deficit );
01526     if ( n == 0 )
01527         extraspace = space_left;
01528     }
01529 
01530     // as a last resort, we distribute the unwanted space equally
01531     // among the spacers (counting the start and end of the chain).
01532 
01533     //### should do a sub-pixel allocation of extra space
01534     int extra = extraspace / ( spacerCount + 2 );
01535     int p = pos + extra;
01536     for ( i = start; i < start+count; i++ ) {
01537     chain[i].pos = p;
01538     p = p + chain[i].size;
01539     if ( !chain[i].empty )
01540         p += spacer+extra;
01541     }
01542 }
01543 
01544 #endif
01545 
KDE Home | KDE Accessibility Home | Description of Access Keys