You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/kexi/widget/kexiscrollview.cpp

408 lines
11 KiB

/* This file is part of the KDE project
Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
Copyright (C) 2004 Jaroslaw Staniek <js@iidea.pl>
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 <qcursor.h>
#include <qobjectlist.h>
#include <qpainter.h>
#include <qpixmap.h>
#include <kdebug.h>
#include <kstaticdeleter.h>
#include <klocale.h>
#include <utils/kexirecordnavigator.h>
#include <core/kexi.h>
#include <kexiutils/utils.h>
//! @internal
class KexiScrollViewData
{
public:
QPixmap horizontalOuterAreaPixmapBuffer;
QPixmap verticalOuterAreaPixmapBuffer;
};
// @todo warning: not reentrant!
static KStaticDeleter<KexiScrollViewData> 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"<<endl;
if (m_scrollViewNavPanel && m_scrollViewNavPanel->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"