/* This file is part of the KDE project Copyright (C) 2004 Cedric Pasteur Copyright (C) 2004 Jaroslaw Staniek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kexiscrollview.h" #include #include #include #include #include #include #include #include #include #include //! @internal class KexiScrollViewData { public: QPixmap horizontalOuterAreaPixmapBuffer; QPixmap verticalOuterAreaPixmapBuffer; }; // @todo warning: not reentrant! static KStaticDeleter KexiScrollView_data_deleter; KexiScrollViewData* KexiScrollView_data = 0; KexiScrollView::KexiScrollView(QWidget *parent, bool preview) : QScrollView(parent, "kexiscrollview", WStaticContents) , m_widget(0) , m_helpFont(font()) , m_preview(preview) , m_scrollViewNavPanel(0) { setFrameStyle(QFrame::WinPanel | QFrame::Sunken); viewport()->setPaletteBackgroundColor(colorGroup().mid()); QColor fc = palette().active().foreground(), bc = viewport()->paletteBackgroundColor(); m_helpColor = KexiUtils::blendedColors(fc, bc, 1, 2); // m_helpColor = QColor((fc.red()+bc.red()*2)/3, (fc.green()+bc.green()*2)/3, // (fc.blue()+bc.blue()*2)/3); m_helpFont.setPointSize( m_helpFont.pointSize() * 3 ); setFocusPolicy(WheelFocus); //initial resize mode is always manual; //will be changed on show(), if needed setResizePolicy(Manual); viewport()->setMouseTracking(true); m_resizing = false; m_enableResizing = true; m_snapToGrid = false; m_gridSize = 0; m_outerAreaVisible = true; connect(&m_delayedResize, SIGNAL(timeout()), this, SLOT(refreshContentsSize())); m_smodeSet = false; if (m_preview) { refreshContentsSizeLater(true, true); //! @todo allow to hide navigator updateScrollBars(); m_scrollViewNavPanel = new KexiRecordNavigator(this, leftMargin(), "nav"); m_scrollViewNavPanel->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Preferred); } } KexiScrollView::~KexiScrollView() { } void KexiScrollView::setWidget(QWidget *w) { addChild(w); m_widget = w; } void KexiScrollView::setRecordNavigatorVisible(bool visible) { if(/*m_scrollViewNavPanel->isVisible() &&*/ !visible) m_scrollViewNavPanel->hide(); else if(visible) { m_scrollViewNavPanel->show(); updateNavPanelGeometry(); } } void KexiScrollView::setSnapToGrid(bool enable, int gridSize) { m_snapToGrid = enable; if(enable) { m_gridSize = gridSize; } } void KexiScrollView::refreshContentsSizeLater(bool horizontal, bool vertical) { Q_UNUSED( horizontal ); Q_UNUSED( vertical ); if (!m_smodeSet) { m_smodeSet = true; m_vsmode = vScrollBarMode(); m_hsmode = hScrollBarMode(); } // if (vertical) setVScrollBarMode(QScrollView::AlwaysOff); //if (horizontal) setHScrollBarMode(QScrollView::AlwaysOff); updateScrollBars(); m_delayedResize.start( 100, true ); } void KexiScrollView::refreshContentsSize() { if(!m_widget) return; if (m_preview) { resizeContents(m_widget->width(), m_widget->height()); // kdDebug() << "KexiScrollView::refreshContentsSize(): ( " // << m_widget->width() <<", "<< m_widget->height() << endl; setVScrollBarMode(m_vsmode); setHScrollBarMode(m_hsmode); m_smodeSet = false; updateScrollBars(); } else { // Ensure there is always space to resize Form int w = contentsWidth(), h = contentsHeight(); bool change = false; const int delta_x = QMAX( (KexiScrollView_data ? KexiScrollView_data->verticalOuterAreaPixmapBuffer.width() : 0), 300); const int delta_y = QMAX( (KexiScrollView_data ? KexiScrollView_data->horizontalOuterAreaPixmapBuffer.height() : 0), 300); if((m_widget->width() + delta_x * 2 / 3) > w) { w = m_widget->width() + delta_x; change = true; } else if((w - m_widget->width()) > delta_x) { w = m_widget->width() + delta_x; change = true; } if((m_widget->height() + delta_y * 2 / 3) > h) { h = m_widget->height() + delta_y; change = true; } else if((h - m_widget->height()) > delta_y) { h = m_widget->height() + delta_y; change = true; } if (change) { repaint(); viewport()->repaint(); repaintContents(); updateContents(0, 0, 2000,2000); clipper()->repaint(); resizeContents(w, h); } // kdDebug() << "KexiScrollView::refreshContentsSize(): ( " // << contentsWidth() <<", "<< contentsHeight() << endl; updateScrollBars(); setVScrollBarMode(Auto); setHScrollBarMode(Auto); } updateContents(); updateScrollBars(); } void KexiScrollView::updateNavPanelGeometry() { if (m_scrollViewNavPanel) m_scrollViewNavPanel->updateGeometry(leftMargin()); } void KexiScrollView::contentsMousePressEvent(QMouseEvent *ev) { if(!m_widget) return; QRect r3(0, 0, m_widget->width() + 4, m_widget->height() + 4); if(!r3.contains(ev->pos())) // clicked outside form //m_form->resetSelection(); emit outerAreaClicked(); if(!m_enableResizing) return; QRect r(m_widget->width(), 0, 4, m_widget->height() + 4); // right limit QRect r2(0, m_widget->height(), m_widget->width() + 4, 4); // bottom limit if(r.contains(ev->pos()) || r2.contains(ev->pos())) { m_resizing = true; emit resizingStarted(); } } void KexiScrollView::contentsMouseReleaseEvent(QMouseEvent *) { if(m_resizing) { m_resizing = false; emit resizingEnded(); } unsetCursor(); } void KexiScrollView::contentsMouseMoveEvent(QMouseEvent *ev) { if(!m_widget || !m_enableResizing) return; if(m_resizing) // resize widget { int tmpx = ev->x(), tmpy = ev->y(); const int exceeds_x = (tmpx - contentsX() + 5) - clipper()->width(); const int exceeds_y = (tmpy - contentsY() + 5) - clipper()->height(); if (exceeds_x > 0) tmpx -= exceeds_x; if (exceeds_y > 0) tmpy -= exceeds_y; if ((tmpx - contentsX()) < 0) tmpx = contentsX(); if ((tmpy - contentsY()) < 0) tmpy = contentsY(); // we look for the max widget right() (or bottom()), which would be the limit for form resizing (not to hide widgets) QObjectList *list = m_widget->queryList("QWidget", 0, true, false /* not recursive*/); for(QObject *o = list->first(); o; o = list->next()) { QWidget *w = (QWidget*)o; tmpx = QMAX(tmpx, (w->geometry().right() + 10)); tmpy = QMAX(tmpy, (w->geometry().bottom() + 10)); } delete list; int neww = -1, newh; if(cursor().shape() == QCursor::SizeHorCursor) { if(m_snapToGrid) neww = int( float(tmpx) / float(m_gridSize) + 0.5 ) * m_gridSize; else neww = tmpx; newh = m_widget->height(); } else if(cursor().shape() == QCursor::SizeVerCursor) { neww = m_widget->width(); if(m_snapToGrid) newh = int( float(tmpy) / float(m_gridSize) + 0.5 ) * m_gridSize; else newh = tmpy; } else if(cursor().shape() == QCursor::SizeFDiagCursor) { if(m_snapToGrid) { neww = int( float(tmpx) / float(m_gridSize) + 0.5 ) * m_gridSize; newh = int( float(tmpy) / float(m_gridSize) + 0.5 ) * m_gridSize; } else { neww = tmpx; newh = tmpy; } } //needs update? if (neww!=-1 && m_widget->size() != QSize(neww, newh)) { m_widget->resize( neww, newh ); refreshContentsSize(); updateContents(); } } else // update mouse cursor { QPoint p = ev->pos(); QRect r(m_widget->width(), 0, 4, m_widget->height()); // right QRect r2(0, m_widget->height(), m_widget->width(), 4); // bottom QRect r3(m_widget->width(), m_widget->height(), 4, 4); // bottom-right corner if(r.contains(p)) setCursor(QCursor::SizeHorCursor); else if(r2.contains(p)) setCursor(QCursor::SizeVerCursor); else if(r3.contains(p)) setCursor(QCursor::SizeFDiagCursor); else unsetCursor(); } } void KexiScrollView::setupPixmapBuffer(QPixmap& pixmap, const QString& text, int lines) { Q_UNUSED( lines ); QFontMetrics fm(m_helpFont); const int flags = Qt::AlignCenter|Qt::AlignTop; QRect rect(fm.boundingRect(0,0,1000,1000,flags,text)); const int txtw = rect.width(), txth = rect.height();//fm.width(text), txth = fm.height()*lines; pixmap = QPixmap(txtw, txth); if (!pixmap.isNull()) { //create pixmap once pixmap.fill( viewport()->paletteBackgroundColor() ); QPainter pb(&pixmap, this); pb.setPen(m_helpColor); pb.setFont(m_helpFont); pb.drawText(0, 0, txtw, txth, Qt::AlignCenter|Qt::AlignTop, text); } } void KexiScrollView::drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph ) { QScrollView::drawContents(p, clipx, clipy, clipw, cliph); if (m_widget) { if(m_preview || !m_outerAreaVisible) return; //draw right and bottom borders const int wx = childX(m_widget); const int wy = childY(m_widget); p->setPen(palette().active().foreground()); p->drawLine(wx+m_widget->width(), wy, wx+m_widget->width(), wy+m_widget->height()); p->drawLine(wx, wy+m_widget->height(), wx+m_widget->width(), wy+m_widget->height()); //kdDebug() << "KexiScrollView::drawContents() " << wy+m_widget->height() << endl; if (!KexiScrollView_data) { KexiScrollView_data_deleter.setObject( KexiScrollView_data, new KexiScrollViewData() ); //create flicker-less buffer setupPixmapBuffer( KexiScrollView_data->horizontalOuterAreaPixmapBuffer, i18n("Outer Area"), 1 ); setupPixmapBuffer( KexiScrollView_data->verticalOuterAreaPixmapBuffer, i18n("Outer\nArea"), 2 ); } if (!KexiScrollView_data->horizontalOuterAreaPixmapBuffer.isNull() && !KexiScrollView_data->verticalOuterAreaPixmapBuffer.isNull() && !m_delayedResize.isActive() /* only draw text if there's not pending delayed resize*/) { if (m_widget->height()>(KexiScrollView_data->verticalOuterAreaPixmapBuffer.height()+20)) { p->drawPixmap( QMAX( m_widget->width(), KexiScrollView_data->verticalOuterAreaPixmapBuffer.width() + 20 ) + 20, QMAX( (m_widget->height() - KexiScrollView_data->verticalOuterAreaPixmapBuffer.height())/2, 20 ), KexiScrollView_data->verticalOuterAreaPixmapBuffer ); } p->drawPixmap( QMAX( (m_widget->width() - KexiScrollView_data->horizontalOuterAreaPixmapBuffer.width())/2, 20 ), QMAX( m_widget->height(), KexiScrollView_data->horizontalOuterAreaPixmapBuffer.height() + 20 ) + 20, KexiScrollView_data->horizontalOuterAreaPixmapBuffer ); } } } void KexiScrollView::leaveEvent( QEvent *e ) { QWidget::leaveEvent(e); m_widget->update(); //update form elements on too fast mouse move } void KexiScrollView::setHBarGeometry( QScrollBar & hbar, int x, int y, int w, int h ) { /*todo*/ // kdDebug(44021)<<"KexiScrollView::setHBarGeometry"<isVisible()) { m_scrollViewNavPanel->setHBarGeometry( hbar, x, y, w, h ); } else { hbar.setGeometry( x, y, w, h ); } } KexiRecordNavigator* KexiScrollView::recordNavigator() const { return m_scrollViewNavPanel; } #include "kexiscrollview.moc"