kexi

kexiscrollview.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
00003    Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019 */
00020 #include "kexiscrollview.h"
00021 
00022 #include <qcursor.h>
00023 #include <qobjectlist.h>
00024 #include <qpainter.h>
00025 #include <qpixmap.h>
00026 
00027 #include <kdebug.h>
00028 #include <kstaticdeleter.h>
00029 #include <klocale.h>
00030 
00031 #include <utils/kexirecordnavigator.h>
00032 #include <core/kexi.h>
00033 #include <kexiutils/utils.h>
00034 
00036 class KexiScrollViewData
00037 {
00038     public:
00039         QPixmap horizontalOuterAreaPixmapBuffer;
00040         QPixmap verticalOuterAreaPixmapBuffer;
00041 };
00042 
00043 // @todo warning: not reentrant!
00044 static KStaticDeleter<KexiScrollViewData> KexiScrollView_data_deleter;
00045 KexiScrollViewData* KexiScrollView_data = 0;
00046 
00047 KexiScrollView::KexiScrollView(QWidget *parent, bool preview)
00048  : QScrollView(parent, "kexiscrollview", WStaticContents)
00049  , m_widget(0)
00050  , m_helpFont(font())
00051  , m_preview(preview)
00052  , m_scrollViewNavPanel(0)
00053 {
00054     setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
00055     viewport()->setPaletteBackgroundColor(colorGroup().mid());
00056     QColor fc = palette().active().foreground(),
00057         bc = viewport()->paletteBackgroundColor();
00058     m_helpColor = KexiUtils::blendedColors(fc, bc, 1, 2);
00059 //  m_helpColor = QColor((fc.red()+bc.red()*2)/3, (fc.green()+bc.green()*2)/3, 
00060 //      (fc.blue()+bc.blue()*2)/3);
00061     m_helpFont.setPointSize( m_helpFont.pointSize() * 3 );
00062 
00063     setFocusPolicy(WheelFocus);
00064 
00065     //initial resize mode is always manual;
00066     //will be changed on show(), if needed
00067     setResizePolicy(Manual);
00068 
00069     viewport()->setMouseTracking(true);
00070     m_resizing = false;
00071     m_enableResizing = true;
00072     m_snapToGrid = false;
00073     m_gridSize = 0;
00074     m_outerAreaVisible = true;
00075 
00076     connect(&m_delayedResize, SIGNAL(timeout()), this, SLOT(refreshContentsSize()));
00077     m_smodeSet = false;
00078     if (m_preview) {
00079         refreshContentsSizeLater(true, true);
00081         updateScrollBars();
00082         m_scrollViewNavPanel = new KexiRecordNavigator(this, leftMargin(), "nav");
00083         m_scrollViewNavPanel->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Preferred);
00084     }
00085 }
00086 
00087 KexiScrollView::~KexiScrollView()
00088 {
00089 }
00090 
00091 void
00092 KexiScrollView::setWidget(QWidget *w)
00093 {
00094     addChild(w);
00095     m_widget = w;
00096 }
00097 
00098 void
00099 KexiScrollView::setRecordNavigatorVisible(bool visible)
00100 {
00101     if(/*m_scrollViewNavPanel->isVisible() &&*/ !visible)
00102         m_scrollViewNavPanel->hide();
00103     else if(visible)  {
00104         m_scrollViewNavPanel->show();
00105         updateNavPanelGeometry();
00106     }
00107 }
00108 
00109 void
00110 KexiScrollView::setSnapToGrid(bool enable, int gridSize)
00111 {
00112     m_snapToGrid = enable;
00113     if(enable) {
00114         m_gridSize = gridSize;
00115     }
00116 }
00117 
00118 void
00119 KexiScrollView::refreshContentsSizeLater(bool horizontal, bool vertical)
00120 {
00121     Q_UNUSED( horizontal );
00122     Q_UNUSED( vertical );
00123 
00124     if (!m_smodeSet) {
00125         m_smodeSet = true;
00126         m_vsmode = vScrollBarMode();
00127         m_hsmode = hScrollBarMode();
00128     }
00129 //  if (vertical)
00130         setVScrollBarMode(QScrollView::AlwaysOff);
00131     //if (horizontal)
00132         setHScrollBarMode(QScrollView::AlwaysOff);
00133     updateScrollBars();
00134     m_delayedResize.start( 100, true );
00135 }
00136 
00137 void
00138 KexiScrollView::refreshContentsSize()
00139 {
00140     if(!m_widget)
00141         return;
00142     if (m_preview) {
00143         resizeContents(m_widget->width(), m_widget->height());
00144 //      kdDebug() << "KexiScrollView::refreshContentsSize(): ( " 
00145     //      << m_widget->width() <<", "<< m_widget->height() << endl;
00146         setVScrollBarMode(m_vsmode);
00147         setHScrollBarMode(m_hsmode);
00148         m_smodeSet = false;
00149         updateScrollBars();
00150     }
00151     else {
00152         // Ensure there is always space to resize Form
00153         int w = contentsWidth(), h = contentsHeight();
00154         bool change = false;
00155         const int delta_x = QMAX( (KexiScrollView_data ? 
00156             KexiScrollView_data->verticalOuterAreaPixmapBuffer.width() : 0), 300);
00157         const int delta_y = QMAX( (KexiScrollView_data ? 
00158             KexiScrollView_data->horizontalOuterAreaPixmapBuffer.height() : 0), 300);
00159         if((m_widget->width() + delta_x * 2 / 3) > w) {
00160             w = m_widget->width() + delta_x;
00161             change = true;
00162         }
00163         else if((w - m_widget->width()) > delta_x) {
00164             w = m_widget->width() + delta_x;
00165             change = true;
00166         }
00167         if((m_widget->height() + delta_y * 2 / 3) > h) {
00168             h = m_widget->height() + delta_y;
00169             change = true;
00170         }
00171         else if((h - m_widget->height()) > delta_y) {
00172             h = m_widget->height() + delta_y;
00173             change = true;
00174         }
00175         if (change) {
00176     repaint();
00177     viewport()->repaint();
00178     repaintContents();
00179     updateContents(0, 0, 2000,2000);
00180     clipper()->repaint();
00181 
00182             resizeContents(w, h);
00183         }
00184 //      kdDebug() << "KexiScrollView::refreshContentsSize(): ( " 
00185     //      << contentsWidth() <<", "<< contentsHeight() << endl;
00186         updateScrollBars();
00187         setVScrollBarMode(Auto);
00188         setHScrollBarMode(Auto);
00189     }
00190     updateContents();
00191     updateScrollBars();
00192 }
00193 
00194 void
00195 KexiScrollView::updateNavPanelGeometry()
00196 {
00197     if (m_scrollViewNavPanel)
00198         m_scrollViewNavPanel->updateGeometry(leftMargin());
00199 }
00200 
00201 void
00202 KexiScrollView::contentsMousePressEvent(QMouseEvent *ev)
00203 {
00204     if(!m_widget)
00205         return;
00206 
00207     QRect r3(0, 0, m_widget->width() + 4, m_widget->height() + 4);
00208     if(!r3.contains(ev->pos())) // clicked outside form
00209         //m_form->resetSelection();
00210         emit outerAreaClicked();
00211 
00212     if(!m_enableResizing)
00213         return;
00214 
00215     QRect r(m_widget->width(),  0, 4, m_widget->height() + 4); // right limit
00216     QRect r2(0, m_widget->height(), m_widget->width() + 4, 4); // bottom limit
00217     if(r.contains(ev->pos()) || r2.contains(ev->pos()))
00218     {
00219         m_resizing = true;
00220         emit resizingStarted();
00221     }
00222 }
00223 
00224 void
00225 KexiScrollView::contentsMouseReleaseEvent(QMouseEvent *)
00226 {
00227     if(m_resizing) {
00228         m_resizing = false;
00229         emit resizingEnded();
00230     }
00231 
00232     unsetCursor();
00233 }
00234 
00235 void
00236 KexiScrollView::contentsMouseMoveEvent(QMouseEvent *ev)
00237 {
00238     if(!m_widget || !m_enableResizing)
00239         return;
00240 
00241     if(m_resizing) // resize widget
00242     {
00243         int tmpx = ev->x(), tmpy = ev->y();
00244         const int exceeds_x = (tmpx - contentsX() + 5) - clipper()->width();
00245         const int exceeds_y = (tmpy - contentsY() + 5) - clipper()->height();
00246         if (exceeds_x > 0)
00247             tmpx -= exceeds_x;
00248         if (exceeds_y > 0)
00249             tmpy -= exceeds_y;
00250         if ((tmpx - contentsX()) < 0)
00251             tmpx = contentsX();
00252         if ((tmpy - contentsY()) < 0)
00253             tmpy = contentsY();
00254 
00255         // we look for the max widget right() (or bottom()), which would be the limit for form resizing (not to hide widgets)
00256         QObjectList *list = m_widget->queryList("QWidget", 0, true, false /* not recursive*/);
00257         for(QObject *o = list->first(); o; o = list->next())
00258         {
00259             QWidget *w = (QWidget*)o;
00260             tmpx = QMAX(tmpx, (w->geometry().right() + 10));
00261             tmpy = QMAX(tmpy, (w->geometry().bottom() + 10));
00262         }
00263         delete list;
00264 
00265         int neww = -1, newh;
00266         if(cursor().shape() == QCursor::SizeHorCursor)
00267         {
00268             if(m_snapToGrid)
00269                 neww = int( float(tmpx) / float(m_gridSize) + 0.5 ) * m_gridSize;
00270             else
00271                 neww = tmpx;
00272             newh = m_widget->height();
00273         }
00274         else if(cursor().shape() == QCursor::SizeVerCursor)
00275         {
00276             neww = m_widget->width();
00277             if(m_snapToGrid)
00278                 newh = int( float(tmpy) / float(m_gridSize) + 0.5 ) * m_gridSize;
00279             else
00280                 newh = tmpy;
00281         }
00282         else if(cursor().shape() == QCursor::SizeFDiagCursor)
00283         {
00284             if(m_snapToGrid) {
00285                 neww = int( float(tmpx) / float(m_gridSize) + 0.5 ) * m_gridSize;
00286                 newh = int( float(tmpy) / float(m_gridSize) + 0.5 ) * m_gridSize;
00287             } else {
00288                 neww = tmpx;
00289                 newh = tmpy;
00290             }
00291         }
00292         //needs update?
00293         if (neww!=-1 && m_widget->size() != QSize(neww, newh)) {
00294             m_widget->resize( neww, newh );
00295             refreshContentsSize();
00296             updateContents();
00297         }
00298     }
00299     else // update mouse cursor
00300     {
00301         QPoint p = ev->pos();
00302         QRect r(m_widget->width(),  0, 4, m_widget->height()); // right
00303         QRect r2(0, m_widget->height(), m_widget->width(), 4); // bottom
00304         QRect r3(m_widget->width(), m_widget->height(), 4, 4); // bottom-right corner
00305 
00306         if(r.contains(p))
00307             setCursor(QCursor::SizeHorCursor);
00308         else if(r2.contains(p))
00309             setCursor(QCursor::SizeVerCursor);
00310         else if(r3.contains(p))
00311             setCursor(QCursor::SizeFDiagCursor);
00312         else
00313             unsetCursor();
00314     }
00315 }
00316 
00317 void 
00318 KexiScrollView::setupPixmapBuffer(QPixmap& pixmap, const QString& text, int lines)
00319 {
00320     Q_UNUSED( lines );
00321 
00322     QFontMetrics fm(m_helpFont);
00323     const int flags = Qt::AlignCenter|Qt::AlignTop;
00324     QRect rect(fm.boundingRect(0,0,1000,1000,flags,text));
00325     const int txtw = rect.width(), txth = rect.height();//fm.width(text), txth = fm.height()*lines;
00326     pixmap = QPixmap(txtw, txth);
00327     if (!pixmap.isNull()) {
00328         //create pixmap once
00329         pixmap.fill( viewport()->paletteBackgroundColor() );
00330         QPainter pb(&pixmap, this);
00331         pb.setPen(m_helpColor);
00332         pb.setFont(m_helpFont);
00333         pb.drawText(0, 0, txtw, txth, Qt::AlignCenter|Qt::AlignTop, text);
00334     }
00335 }
00336 
00337 void
00338 KexiScrollView::drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph ) 
00339 {
00340     QScrollView::drawContents(p, clipx, clipy, clipw, cliph);
00341     if (m_widget) {
00342         if(m_preview || !m_outerAreaVisible)
00343             return;
00344 
00345         //draw right and bottom borders
00346         const int wx = childX(m_widget);
00347         const int wy = childY(m_widget);
00348         p->setPen(palette().active().foreground());
00349         p->drawLine(wx+m_widget->width(), wy, wx+m_widget->width(), wy+m_widget->height());
00350         p->drawLine(wx, wy+m_widget->height(), wx+m_widget->width(), wy+m_widget->height());
00351 //kdDebug() << "KexiScrollView::drawContents() " << wy+m_widget->height() << endl;
00352 
00353         if (!KexiScrollView_data) {
00354             KexiScrollView_data_deleter.setObject( KexiScrollView_data, new KexiScrollViewData() );
00355 
00356             //create flicker-less buffer
00357             setupPixmapBuffer( KexiScrollView_data->horizontalOuterAreaPixmapBuffer, i18n("Outer Area"), 1 );
00358             setupPixmapBuffer( KexiScrollView_data->verticalOuterAreaPixmapBuffer, i18n("Outer\nArea"), 2 );
00359         }
00360         if (!KexiScrollView_data->horizontalOuterAreaPixmapBuffer.isNull() 
00361             && !KexiScrollView_data->verticalOuterAreaPixmapBuffer.isNull() 
00362             && !m_delayedResize.isActive() /* only draw text if there's not pending delayed resize*/)
00363         {
00364             if (m_widget->height()>(KexiScrollView_data->verticalOuterAreaPixmapBuffer.height()+20)) {
00365                 p->drawPixmap( 
00366                     QMAX( m_widget->width(), KexiScrollView_data->verticalOuterAreaPixmapBuffer.width() + 20 ) + 20,
00367                     QMAX( (m_widget->height() - KexiScrollView_data->verticalOuterAreaPixmapBuffer.height())/2, 20 ),
00368                     KexiScrollView_data->verticalOuterAreaPixmapBuffer
00369                 );
00370             }
00371             p->drawPixmap( 
00372                 QMAX( (m_widget->width() - KexiScrollView_data->horizontalOuterAreaPixmapBuffer.width())/2, 20 ),
00373                 QMAX( m_widget->height(), KexiScrollView_data->horizontalOuterAreaPixmapBuffer.height() + 20 ) + 20,
00374                 KexiScrollView_data->horizontalOuterAreaPixmapBuffer
00375             );
00376         }
00377     }
00378 }
00379 
00380 void
00381 KexiScrollView::leaveEvent( QEvent *e )
00382 {
00383     QWidget::leaveEvent(e);
00384     m_widget->update(); //update form elements on too fast mouse move
00385 }
00386 
00387 void
00388 KexiScrollView::setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h )
00389 {
00390 /*todo*/
00391 //  kdDebug(44021)<<"KexiScrollView::setHBarGeometry"<<endl;
00392     if (m_scrollViewNavPanel && m_scrollViewNavPanel->isVisible()) {
00393         m_scrollViewNavPanel->setHBarGeometry( hbar, x, y, w, h );
00394     }
00395     else {
00396         hbar.setGeometry( x, y, w, h );
00397     }
00398 }
00399 
00400 KexiRecordNavigator*
00401 KexiScrollView::recordNavigator() const
00402 {
00403     return m_scrollViewNavPanel;
00404 }
00405 
00406 #include "kexiscrollview.moc"
00407 
KDE Home | KDE Accessibility Home | Description of Access Keys