kspread

Canvas.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright 2009 Thomas Zander <zander@kde.org>
00003    Copyright 2006-2007 Stefan Nikolaus <stefan.nikolaus@kdemail.net>
00004    Copyright 2006 Robert Knight <robertknight@gmail.com>
00005    Copyright 2006 Inge Wallin <inge@lysator.liu.se>
00006    Copyright 1999-2002,2004 Laurent Montel <montel@kde.org>
00007    Copyright 2002-2005 Ariya Hidayat <ariya@kde.org>
00008    Copyright 1999-2004 David Faure <faure@kde.org>
00009    Copyright 2004-2005 Meni Livne <livne@kde.org>
00010    Copyright 2001-2003 Philipp Mueller <philipp.mueller@gmx.de>
00011    Copyright 2002-2003 Norbert Andres <nandres@web.de>
00012    Copyright 2003 Hamish Rodda <rodda@kde.org>
00013    Copyright 2003 Joseph Wenninger <jowenn@kde.org>
00014    Copyright 2003 Lukas Tinkl <lukas@kde.org>
00015    Copyright 2000-2002 Werner Trobin <trobin@kde.org>
00016    Copyright 2002 Harri Porten <porten@kde.org>
00017    Copyright 2002 John Dailey <dailey@vt.edu>
00018    Copyright 2002 Daniel Naber <daniel.naber@t-online.de>
00019    Copyright 1999-2000 Torben Weis <weis@kde.org>
00020    Copyright 1999-2000 Stephan Kulow <coolo@kde.org>
00021    Copyright 2000 Bernd Wuebben <wuebben@kde.org>
00022    Copyright 2000 Wilco Greven <greven@kde.org>
00023    Copyright 2000 Simon Hausmann <hausmann@kde.org
00024    Copyright 1999 Michael Reiher <michael.reiher@gmx.de>
00025    Copyright 1999 Boris Wedl <boris.wedl@kfunigraz.ac.at>
00026    Copyright 1999 Reginald Stadlbauer <reggie@kde.org>
00027 
00028    This library is free software; you can redistribute it and/or
00029    modify it under the terms of the GNU Library General Public
00030    License as published by the Free Software Foundation; either
00031    version 2 of the License, or (at your option) any later version.
00032 
00033    This library is distributed in the hope that it will be useful,
00034    but WITHOUT ANY WARRANTY; without even the implied warranty of
00035    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00036    Library General Public License for more details.
00037 
00038    You should have received a copy of the GNU Library General Public License
00039    along with this library; see the file COPYING.LIB.  If not, write to
00040    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00041    Boston, MA 02110-1301, USA.
00042 */
00043 
00044 // Local
00045 #include "Canvas.h"
00046 #include "Canvas_p.h"
00047 
00048 // std
00049 #include <assert.h>
00050 #include <float.h>
00051 #include <stdlib.h>
00052 
00053 // Qt
00054 #include <QApplication>
00055 #include <QBuffer>
00056 #include <QByteArray>
00057 #include <QClipboard>
00058 #include <QDragLeaveEvent>
00059 #include <QDragMoveEvent>
00060 #include <QDropEvent>
00061 #include <QEvent>
00062 #include <QFocusEvent>
00063 #include <QKeyEvent>
00064 #include <QLabel>
00065 #include <QList>
00066 #include <QMenu>
00067 #include <QMouseEvent>
00068 #include <QPainter>
00069 #include <QPaintEvent>
00070 #include <QPixmap>
00071 #include <QPoint>
00072 #include <QScrollBar>
00073 #include <QTextStream>
00074 #include <QToolTip>
00075 #include <QWidget>
00076 
00077 // KDE
00078 #include <kcursor.h>
00079 #include <kdebug.h>
00080 #include <kmessagebox.h>
00081 #include <krun.h>
00082 #include <kmimetype.h>
00083 #include <ksharedptr.h>
00084 #include <kwordwrap.h>
00085 #include <kxmlguifactory.h>
00086 
00087 // KOffice
00088 #include <KoCanvasController.h>
00089 #include <KoShapeManager.h>
00090 #include <KoStore.h>
00091 #include <KoToolManager.h>
00092 #include <KoToolProxy.h>
00093 #include <KoXmlWriter.h>
00094 #include <KoZoomHandler.h>
00095 
00096 // KSpread
00097 #include "CellStorage.h"
00098 #include "CellView.h"
00099 #include "Doc.h"
00100 #include "Editors.h"
00101 #include "Global.h"
00102 #include "Headers.h"
00103 #include "Localization.h"
00104 #include "Map.h"
00105 #include "RowColumnFormat.h"
00106 #include "Selection.h"
00107 #include "Sheet.h"
00108 #include "SheetView.h"
00109 #include "Util.h"
00110 #include "Validity.h"
00111 #include "View.h"
00112 
00113 // commands
00114 #include "commands/DeleteCommand.h"
00115 #include "commands/StyleCommand.h"
00116 #include "commands/Undo.h"
00117 
00118 #define MIN_SIZE 10
00119 
00120 using namespace KSpread;
00121 
00122 /****************************************************************
00123  *
00124  * Canvas
00125  *
00126  ****************************************************************/
00127 
00128 Canvas::Canvas(View *view)
00129     : QWidget( view )
00130     , KoCanvasBase(0)
00131     , d( new Private )
00132 {
00133   setAttribute( Qt::WA_OpaquePaintEvent );
00134   setAttribute( Qt::WA_StaticContents );
00135   setBackgroundRole(QPalette::Base);
00136 
00137   d->validationInfo = 0;
00138 
00139   QWidget::setFocusPolicy( Qt::StrongFocus );
00140 
00141   d->offset = QPointF( 0.0, 0.0 );
00142 
00143   d->view = view;
00144 
00145   setMouseTracking( true );
00146   d->mousePressed = false;
00147   d->dragging = false;
00148 
00149     connect( d->view, SIGNAL( autoScroll( const QPoint & )),
00150              this, SLOT( slotAutoScroll( const QPoint &)));
00151 
00152   installEventFilter( this ); // for TAB key processing, otherwise focus change
00153   setAcceptDrops( true );
00154   setAttribute(Qt::WA_InputMethodEnabled, true); // ensure using the InputMethod
00155 
00156     // flake
00157     d->shapeManager = new KoShapeManager( this );
00158     d->toolProxy = new KoToolProxy( this );
00159 }
00160 
00161 Canvas::~Canvas()
00162 {
00163     delete d->shapeManager;
00164     delete d->toolProxy;
00165     delete d->validationInfo;
00166     delete d;
00167 }
00168 
00169 View* Canvas::view() const
00170 {
00171   return d->view;
00172 }
00173 
00174 Doc* Canvas::doc() const
00175 {
00176   return d->view->doc();
00177 }
00178 
00179 void Canvas::gridSize( qreal* horizontal, qreal* vertical ) const
00180 {
00181     *horizontal = doc()->map()->defaultColumnFormat()->width();
00182     *vertical = doc()->map()->defaultRowFormat()->height();
00183 }
00184 
00185 bool Canvas::snapToGrid() const
00186 {
00187     return false; // FIXME
00188 }
00189 
00190 void Canvas::addCommand( QUndoCommand* command )
00191 {
00192     doc()->addCommand( command );
00193 }
00194 
00195 KoShapeManager* Canvas::shapeManager() const
00196 {
00197     return d->shapeManager;
00198 }
00199 
00200 void Canvas::updateCanvas( const QRectF& rc )
00201 {
00202     QRect clipRect( viewConverter()->documentToView( rc.translated( -offset() ) ).toRect() );
00203     clipRect.adjust( -2, -2, 2, 2 ); // Resize to fit anti-aliasing
00204     update( clipRect );
00205 }
00206 
00207 const KoViewConverter* Canvas::viewConverter() const
00208 {
00209     return view()->zoomHandler();
00210 }
00211 
00212 KoUnit Canvas::unit() const
00213 {
00214     return doc()->unit();
00215 }
00216 
00217 KoToolProxy* Canvas::toolProxy() const
00218 {
00219     return d->toolProxy;
00220 }
00221 
00222 QPointF Canvas::offset() const
00223 {
00224     return d->offset;
00225 }
00226 
00227 double Canvas::xOffset() const
00228 {
00229     return d->offset.x();
00230 }
00231 
00232 double Canvas::yOffset() const
00233 {
00234     return d->offset.y();
00235 }
00236 
00237 bool Canvas::eventFilter( QObject *o, QEvent *e )
00238 {
00239   /* this canvas event filter acts on events sent to the line edit as well
00240      as events to this filter itself.
00241   */
00242   if ( !o || !e )
00243     return true;
00244   switch ( e->type() )
00245   {
00246   case QEvent::KeyPress:
00247   {
00248     QKeyEvent * keyev = static_cast<QKeyEvent *>(e);
00249     if ((keyev->key()==Qt::Key_Tab) || (keyev->key()==Qt::Key_Backtab))
00250     {
00251       keyPressEvent ( keyev );
00252       return true;
00253     }
00254     break;
00255   }
00256   case QEvent::InputMethod:
00257   {
00258       //QIMEvent * imev = static_cast<QIMEvent *>(e);
00259       //processIMEvent( imev );
00260       //break;
00261   }
00262   case QEvent::ToolTip:
00263   {
00264     QHelpEvent* helpEvent = static_cast<QHelpEvent*>( e );
00265     showToolTip( helpEvent->pos() );
00266   }
00267   default:
00268     break;
00269   }
00270   return false;
00271 }
00272 
00273 Selection* Canvas::selection() const
00274 {
00275   return d->view->selection();
00276 }
00277 
00278 ColumnHeader* Canvas::columnHeader() const
00279 {
00280   return d->view->columnHeader();
00281 }
00282 
00283 RowHeader* Canvas::rowHeader() const
00284 {
00285   return d->view->rowHeader();
00286 }
00287 
00288 QScrollBar* Canvas::horzScrollBar() const
00289 {
00290   return d->view->horzScrollBar();
00291 }
00292 
00293 QScrollBar* Canvas::vertScrollBar() const
00294 {
00295   return d->view->vertScrollBar();
00296 }
00297 
00298 Sheet* Canvas::activeSheet() const
00299 {
00300   return d->view->activeSheet();
00301 }
00302 
00303 void Canvas::validateSelection()
00304 {
00305   register Sheet * const sheet = activeSheet();
00306   if (!sheet)
00307     return;
00308 
00309     if ( selection()->isSingular() )
00310     {
00311         int col = selection()->marker().x();
00312         int row = selection()->marker().y();
00313         Cell cell( sheet, col,row );
00314         Validity validity = cell.validity();
00315         if ( validity.displayValidationInformation() )
00316         {
00317             QString title = validity.titleInfo();
00318             QString message = validity.messageInfo();
00319             if ( title.isEmpty() && message.isEmpty() )
00320                 return;
00321 
00322             if ( !d->validationInfo )
00323                 d->validationInfo = new QLabel(  this );
00324             kDebug(36001)<<" display info validation";
00325             double u = cell.width();
00326             double v = cell.height();
00327             double xpos = sheet->columnPosition( selection()->marker().x() ) - xOffset();
00328             double ypos = sheet->rowPosition( selection()->marker().y() ) - yOffset();
00329             // Special treatment for obscured cells.
00330             if ( cell.isPartOfMerged() )
00331             {
00332                 cell = cell.masterCell();
00333                 int moveX = cell.column();
00334                 int moveY = cell.row();
00335 
00336                 // Use the obscuring cells dimensions
00337                 u = cell.width();
00338                 v = cell.height();
00339                 xpos = sheet->columnPosition( moveX );
00340                 ypos = sheet->rowPosition( moveY );
00341             }
00342             //d->validationInfo->setGeometry( 3, y + 3, len + 2, hei + 2 );
00343             d->validationInfo->setAlignment( Qt::AlignVCenter );
00344             QPainter painter;
00345             painter.begin( this );
00346             int len = 0;
00347             int hei = 0;
00348             QString resultText;
00349             if ( !title.isEmpty() )
00350             {
00351                 len = painter.fontMetrics().width( title );
00352                 hei = painter.fontMetrics().height();
00353                 resultText = title + '\n';
00354             }
00355             if ( !message.isEmpty() )
00356             {
00357                 int i = 0;
00358                 int pos = 0;
00359                 QString t;
00360                 do
00361                 {
00362                     i = message.indexOf( "\n", pos );
00363                     if ( i == -1 )
00364                         t = message.mid( pos, message.length() - pos );
00365                     else
00366                     {
00367                         t = message.mid( pos, i - pos );
00368                         pos = i + 1;
00369                     }
00370                     hei += painter.fontMetrics().height();
00371                     len = qMax( len, painter.fontMetrics().width( t ) );
00372                 }
00373                 while ( i != -1 );
00374                 resultText += message;
00375             }
00376             painter.end();
00377             d->validationInfo->setText( resultText );
00378 
00379             QRectF unzoomedMarker( xpos - xOffset()+u,
00380                                    ypos - yOffset()+v,
00381                                    len,
00382                                    hei );
00383             QRectF marker( viewConverter()->documentToView( unzoomedMarker ) );
00384 
00385             d->validationInfo->setGeometry( marker.toRect() );
00386             d->validationInfo->show();
00387         }
00388         else
00389         {
00390             delete d->validationInfo;
00391             d->validationInfo = 0;
00392         }
00393     }
00394     else
00395     {
00396         delete d->validationInfo;
00397         d->validationInfo = 0;
00398     }
00399 }
00400 
00401 
00402 void Canvas::scrollToCell(const QPoint& location) const
00403 {
00404     register Sheet * const sheet = activeSheet();
00405     if (!sheet)
00406         return;
00407     if (d->view->isLoading())
00408         return;
00409 
00410     // Adjust the maximum accessed column and row for the scrollbars.
00411     view()->sheetView(sheet)->updateAccessedCellRange(location);
00412 
00413     // The cell geometry expanded by the size of one column or one row, resp., in each direction.
00414     const Cell cell = Cell(sheet, location).masterCell();
00415     const double xpos = sheet->columnPosition(cell.cellPosition().x());
00416     const double ypos = sheet->rowPosition(cell.cellPosition().y());
00417     const double width = sheet->map()->defaultColumnFormat()->width();
00418     const double height = sheet->map()->defaultRowFormat()->height();
00419     QRectF rect(xpos, ypos, cell.width(), cell.height());
00420     rect.adjust(-width-2, -height-2, width+2, height+2);
00421     rect = rect & QRectF(QPointF(0.0, 0.0), sheet->documentSize());
00422 
00423     d->view->canvasController()->ensureVisible(rect, true);
00424 }
00425 
00426 void Canvas::setDocumentOffset( const QPoint& offset )
00427 {
00428     const QPoint delta = offset - viewConverter()->documentToView( d->offset ).toPoint();
00429     d->offset = viewConverter()->viewToDocument( offset );
00430 
00431     columnHeader()->scroll(delta.x(), 0);
00432     rowHeader()->scroll(0, delta.y());
00433 }
00434 
00435 void Canvas::setDocumentSize( const QSizeF& size )
00436 {
00437     const QSize s = viewConverter()->documentToView( size ).toSize();
00438     emit documentSizeChanged( s );
00439 }
00440 
00441 #if 0
00442 void Canvas::slotScrollHorz( int _value )
00443 {
00444     register Sheet * const sheet = activeSheet();
00445     if (!sheet)
00446         return;
00447 
00448     kDebug(36005) <<"slotScrollHorz: value =" << _value;
00449     //kDebug(36005) << kBacktrace();
00450 
00451     if ( sheet->layoutDirection() == Qt::RightToLeft )
00452         _value = horzScrollBar()->maximum() - _value;
00453 
00454     if ( _value < 0 ) {
00455         kDebug (36001)
00456                 << "Canvas::slotScrollHorz: value out of range (_value: "
00457                 << _value << ')' << endl;
00458         _value = 0;
00459     }
00460 
00461     double xpos = sheet->columnPosition( qMin( KS_colMax, sheet->maxColumn()+10 ) ) - d->xOffset;
00462     if ( _value > ( xpos + d->xOffset ) )
00463         _value = (int) ( xpos + d->xOffset );
00464 
00465     // Relative movement
00466     // NOTE Stefan: Always scroll by whole pixels, otherwise we'll get offsets.
00467     int dx = qRound( viewConverter()->documentToViewX( d->xOffset - _value ) );
00468 
00469     // New absolute position
00470     // NOTE Stefan: Always store whole pixels, otherwise we'll get offsets.
00471     d->xOffset -=  viewConverter()->viewToDocumentX( dx );
00472     if ( d->xOffset < 0.05 )
00473         d->xOffset = 0.0;
00474 
00475     // scrolling the widgets in the right direction
00476     if ( sheet->layoutDirection() == Qt::RightToLeft )
00477         dx = -dx;
00478     scroll( dx, 0 );
00479     columnHeader()->scroll( dx, 0 );
00480 }
00481 
00482 void Canvas::slotScrollVert( int _value )
00483 {
00484     register Sheet * const sheet = activeSheet();
00485     if (!sheet)
00486         return;
00487 
00488     if ( _value < 0 )
00489     {
00490         _value = 0;
00491         kDebug (36001) <<"Canvas::slotScrollVert: value out of range (_value:" <<
00492                 _value << ')' << endl;
00493     }
00494 
00495     double ypos = sheet->rowPosition( qMin( KS_rowMax, sheet->maxRow()+10 ) );
00496     if ( _value > ypos )
00497         _value = (int) ypos;
00498 
00499     // Relative movement
00500     // NOTE Stefan: Always scroll by whole pixels, otherwise we'll get offsets.
00501     int dy = qRound( viewConverter()->documentToViewY( d->yOffset - _value ) );
00502     scroll( 0, dy );
00503     rowHeader()->scroll( 0, dy );
00504 
00505     // New absolute position
00506     // NOTE Stefan: Always store whole pixels, otherwise we'll get offsets.
00507     d->yOffset -= viewConverter()->viewToDocumentY( dy );
00508     if ( d->yOffset < 0.05 )
00509         d->yOffset = 0.0;
00510 }
00511 
00512 void Canvas::slotMaxColumn( int _max_column )
00513 {
00514   register Sheet * const sheet = activeSheet();
00515   if (!sheet)
00516     return;
00517 
00518   int oldValue = horzScrollBar()->maximum() - horzScrollBar()->value();
00519   double xpos = sheet->columnPosition( qMin( KS_colMax, _max_column + 10 ) ) - xOffset();
00520   double unzoomWidth = viewConverter()->viewToDocumentX( width() );
00521 
00522   //Don't go beyond the maximum column range (KS_colMax)
00523   double sizeMaxX = sheet->documentSize().width();
00524   if ( xpos > sizeMaxX - xOffset() - unzoomWidth )
00525     xpos = sizeMaxX - xOffset() - unzoomWidth;
00526 
00527   horzScrollBar()->setRange( 0, (int) ( xpos + xOffset() ) );
00528 
00529   if ( sheet->layoutDirection() == Qt::RightToLeft )
00530     horzScrollBar()->setValue( horzScrollBar()->maximum() - oldValue );
00531 }
00532 
00533 void Canvas::slotMaxRow( int _max_row )
00534 {
00535   register Sheet * const sheet = activeSheet();
00536   if (!sheet)
00537     return;
00538 
00539   double ypos = sheet->rowPosition( qMin( KS_rowMax, _max_row + 10 ) ) - yOffset();
00540   double unzoomHeight = viewConverter()->viewToDocumentY( height() );
00541 
00542   //Don't go beyond the maximum row range (KS_rowMax)
00543   double sizeMaxY = sheet->documentSize().height();
00544   if ( ypos > sizeMaxY - yOffset() - unzoomHeight )
00545     ypos = sizeMaxY - yOffset() - unzoomHeight;
00546 
00547   vertScrollBar()->setRange( 0, (int) ( ypos + yOffset() ) );
00548 }
00549 #endif
00550 
00551 void Canvas::mousePressEvent( QMouseEvent* event )
00552 {
00553     // flake
00554     d->toolProxy->mousePressEvent( event, viewConverter()->viewToDocument( event->pos() ) + offset() );
00555 
00556     if (!event->isAccepted() && event->button() == Qt::RightButton)
00557     {
00558         d->view->unplugActionList("toolproxy_action_list");
00559         d->view->plugActionList("toolproxy_action_list", toolProxy()->popupActionList());
00560         QMenu* menu = dynamic_cast<QMenu*>(d->view->factory()->container("default_canvas_popup", d->view));
00561         // Only show the menu, if there are items. The plugged action list counts as one action.
00562         if (menu && menu->actions().count() > 1) {
00563             menu->exec(event->globalPos());
00564         }
00565         event->setAccepted(true);
00566     }
00567 }
00568 
00569 void Canvas::mouseReleaseEvent( QMouseEvent* event )
00570 {
00571     // flake
00572     d->toolProxy->mouseReleaseEvent( event, viewConverter()->viewToDocument( event->pos() ) + offset() );
00573 }
00574 
00575 void Canvas::mouseMoveEvent( QMouseEvent* event )
00576 {
00577     // flake
00578     d->toolProxy->mouseMoveEvent( event, viewConverter()->viewToDocument( event->pos() ) + offset() );
00579 }
00580 
00581 void Canvas::mouseDoubleClickEvent( QMouseEvent* event )
00582 {
00583     // flake
00584     d->toolProxy->mouseDoubleClickEvent( event, viewConverter()->viewToDocument( event->pos() ) + offset() );
00585 }
00586 
00587 void Canvas::keyPressEvent ( QKeyEvent* event )
00588 {
00589     // flake
00590     d->toolProxy->keyPressEvent( event );
00591 }
00592 
00593 void Canvas::tabletEvent(QTabletEvent *e)
00594 {
00595     // flake
00596     d->toolProxy->tabletEvent(e, viewConverter()->viewToDocument(e->pos() + offset()));
00597 }
00598 
00599 QVariant Canvas::inputMethodQuery(Qt::InputMethodQuery query) const
00600 {
00601     // flake
00602     return d->toolProxy->inputMethodQuery(query, *(viewConverter()));
00603 }
00604 
00605 void Canvas::inputMethodEvent(QInputMethodEvent *event)
00606 {
00607     // flake
00608     d->toolProxy->inputMethodEvent(event);
00609 }
00610 
00611 bool Canvas::highlightRangeSizeGripAt(double x, double y)
00612 {
00613   if ( !selection()->referenceSelectionMode() )
00614     return false;
00615 
00616   Region::ConstIterator end = selection()->constEnd();
00617   for (Region::ConstIterator it = selection()->constBegin(); it != end; ++it)
00618   {
00619     // TODO Stefan: adapt to Selection::selectionHandleArea
00620     QRectF visibleRect = activeSheet()->cellCoordinatesToDocument( (*it)->rect() );
00621 
00622     QPoint bottomRight((int) visibleRect.right(), (int) visibleRect.bottom());
00623     QRect handle( ( (int) bottomRight.x() - 6 ),
00624                   ( (int) bottomRight.y() - 6 ),
00625                   ( 6 ),
00626                   ( 6 ) );
00627 
00628     if (handle.contains(QPoint((int) x,(int) y)))
00629             {
00630                 return true;
00631             }
00632     }
00633 
00634     return false;
00635 }
00636 
00637 void Canvas::startTheDrag()
00638 {
00639   register Sheet * const sheet = activeSheet();
00640   if (!sheet)
00641     return;
00642 
00643   // right area for start dragging
00644   setCursor( Qt::PointingHandCursor );
00645 
00646   QDomDocument doc = sheet->saveCellRegion(*selection(), true);
00647 
00648   // Save to buffer
00649   QBuffer buffer;
00650   buffer.open( QIODevice::WriteOnly );
00651   QTextStream str( &buffer );
00652   str.setCodec( "UTF-8" );
00653   str << doc;
00654   buffer.close();
00655 
00656   QMimeData* mimeData = new QMimeData();
00657   mimeData->setText( sheet->copyAsText( selection() ) );
00658   mimeData->setData( "application/x-kspread-snippet", buffer.buffer() );
00659 
00660   QDrag *drag = new QDrag(this);
00661   drag->setMimeData( mimeData );
00662   drag->start();
00663 
00664   setCursor( Qt::ArrowCursor );
00665 }
00666 
00667 void Canvas::paintEvent( QPaintEvent* event )
00668 {
00669     if ( d->view->doc()->isLoading() || d->view->isLoading() )
00670         return;
00671 
00672     register Sheet * const sheet = activeSheet();
00673     if (!sheet)
00674         return;
00675 
00676     ElapsedTime et( "Painting cells", ElapsedTime::PrintOnlyTime );
00677 
00678     QPainter painter(this);
00679     const QPointF offset = viewConverter()->documentToView(this->offset());
00680     painter.translate(-offset);
00681     painter.setClipRegion(event->region().translated(offset.x(), offset.y()));
00682     painter.setRenderHints( QPainter::Antialiasing | QPainter::TextAntialiasing );
00683     painter.save();
00684 
00685     qreal zoomX, zoomY;
00686     viewConverter()->zoom(&zoomX, &zoomY);
00687     painter.scale(zoomX, zoomY);
00688 
00689     // erase background
00690     const QRectF paintRect = viewConverter()->viewToDocument(rect()).translated(this->offset());
00691     painter.fillRect(paintRect, painter.background());
00692 
00693     // paint visible cells
00694     const QRect visibleRect = visibleCells();
00695     const QPointF topLeft(sheet->columnPosition(visibleRect.left()), sheet->rowPosition(visibleRect.top()));
00696     view()->sheetView( sheet )->setPaintCellRange( visibleRect );
00697     view()->sheetView( sheet )->paintCells( this, painter, paintRect, topLeft );
00698 
00699     // flake
00700     painter.restore();
00701     d->shapeManager->paint( painter, *viewConverter(), false );
00702     painter.setRenderHint( QPainter::Antialiasing, false );
00703     d->toolProxy->paint( painter, *viewConverter() );
00704 
00705     event->accept();
00706 }
00707 
00708 void Canvas::focusInEvent( QFocusEvent* )
00709 {
00710     // If we are in editing mode, we redirect the
00711     // focus to the CellEditor or ExternalEditor.
00712     // This screws up <Tab> though (David)
00713     selection()->emitRequestFocusEditor();
00714 }
00715 
00716 void Canvas::focusOutEvent( QFocusEvent* )
00717 {
00718     d->mousePressed = false;
00719     d->view->disableAutoScroll();
00720 }
00721 
00722 void Canvas::dragEnterEvent( QDragEnterEvent* event )
00723 {
00724   const QMimeData* mimeData = event->mimeData();
00725   if ( mimeData->hasText() ||
00726        mimeData->hasFormat( "application/x-kspread-snippet" ) )
00727   {
00728     event->acceptProposedAction();
00729   }
00730 }
00731 
00732 void Canvas::dragMoveEvent( QDragMoveEvent* event )
00733 {
00734   register Sheet * const sheet = activeSheet();
00735   if (!sheet)
00736   {
00737     event->ignore();
00738     return;
00739   }
00740 
00741   const QMimeData* mimeData = event->mimeData();
00742   if ( mimeData->hasText() || mimeData->hasFormat( "application/x-kspread-snippet" ) )
00743   {
00744     event->acceptProposedAction();
00745   }
00746   else
00747   {
00748     event->ignore();
00749     return;
00750   }
00751 #if 0 // TODO Stefan: implement drag marking rectangle
00752   QRect dragMarkingRect;
00753   if ( mimeData->hasFormat( "application/x-kspread-snippet" ) )
00754   {
00755     if ( event->source() == this  )
00756     {
00757       kDebug(36005) <<"source == this";
00758       dragMarkingRect = selection()->boundingRect();
00759     }
00760     else
00761     {
00762       kDebug(36005) <<"source != this";
00763       QByteArray data = mimeData->data( "application/x-kspread-snippet" );
00764       QString errorMsg;
00765       int errorLine;
00766       int errorColumn;
00767       QDomDocument doc;
00768       if ( !doc.setContent( data, false, &errorMsg, &errorLine, &errorColumn ) )
00769       {
00770         // an error occurred
00771         kDebug(36005) <<"Canvas::daragMoveEvent: an error occurred" << endl
00772                  << "line: " << errorLine << " col: " << errorColumn
00773                  << ' ' << errorMsg << endl;
00774         dragMarkingRect = QRect(1,1,1,1);
00775       }
00776       else
00777       {
00778         QDomElement root = doc.documentElement(); // "spreadsheet-snippet"
00779         dragMarkingRect = QRect(1,1,
00780                                 root.attribute( "columns" ).toInt(),
00781                                 root.attribute( "rows" ).toInt());
00782       }
00783     }
00784   }
00785   else // if ( mimeData->hasText() )
00786   {
00787     kDebug(36005) <<"has text";
00788     dragMarkingRect = QRect(1,1,1,1);
00789   }
00790 #endif
00791   const QPoint dragAnchor = selection()->boundingRect().topLeft();
00792   double xpos = sheet->columnPosition( dragAnchor.x() );
00793   double ypos = sheet->rowPosition( dragAnchor.y() );
00794   double width  = sheet->columnFormat( dragAnchor.x() )->width();
00795   double height = sheet->rowFormat( dragAnchor.y() )->height();
00796 
00797   // consider also the selection rectangle
00798   const QRectF noGoArea( xpos - 1, ypos - 1, width + 3, height + 3 );
00799 
00800   // determine the current position
00801   double eventPosX;
00802   if (sheet->layoutDirection() == Qt::RightToLeft)
00803   {
00804     eventPosX = this->width() - viewConverter()->viewToDocumentX( event->pos().x() ) + xOffset();
00805   }
00806   else
00807   {
00808     eventPosX = viewConverter()->viewToDocumentX( event->pos().x() ) + xOffset();
00809   }
00810   double eventPosY = viewConverter()->viewToDocumentY( event->pos().y() ) + yOffset();
00811 
00812   if ( noGoArea.contains( QPointF( eventPosX, eventPosY ) ) )
00813   {
00814     event->ignore( noGoArea.toRect() );
00815     return;
00816   }
00817 
00818 #if 0 // TODO Stefan: implement drag marking rectangle
00819   // determine the cell position under the mouse
00820   double tmp;
00821   const int col = sheet->leftColumn( eventPosX, tmp );
00822   const int row = sheet->topRow( eventPosY, tmp );
00823   dragMarkingRect.moveTo( QPoint( col, row ) );
00824   kDebug(36005) <<"MARKING RECT =" << dragMarkingRect;
00825 #endif
00826 }
00827 
00828 void Canvas::dragLeaveEvent( QDragLeaveEvent * )
00829 {
00830 }
00831 
00832 void Canvas::dropEvent( QDropEvent * _ev )
00833 {
00834   d->dragging = false;
00835   d->view->disableAutoScroll();
00836   register Sheet * const sheet = activeSheet();
00837   if ( !sheet || sheet->isProtected() )
00838   {
00839     _ev->ignore();
00840     return;
00841   }
00842 
00843   double xpos = sheet->columnPosition( selection()->lastRange().left() );
00844   double ypos = sheet->rowPosition( selection()->lastRange().top() );
00845   double width  = sheet->columnFormat( selection()->lastRange().left() )->width();
00846   double height = sheet->rowFormat( selection()->lastRange().top() )->height();
00847 
00848   const QRectF noGoArea( xpos - 1, ypos - 1, width + 3, height + 3 );
00849 
00850   double ev_PosX;
00851   if ( sheet->layoutDirection() == Qt::RightToLeft )
00852     ev_PosX = this->width() - viewConverter()->viewToDocumentX( _ev->pos().x() ) + xOffset();
00853   else
00854     ev_PosX = viewConverter()->viewToDocumentX( _ev->pos().x() ) + xOffset();
00855 
00856   double ev_PosY = viewConverter()->viewToDocumentY( _ev->pos().y() ) + yOffset();
00857 
00858   if ( noGoArea.contains( QPointF( ev_PosX, ev_PosY ) ) )
00859   {
00860     _ev->ignore();
00861     return;
00862   }
00863   else
00864     _ev->setAccepted(true);
00865 
00866   double tmp;
00867   int col = sheet->leftColumn( ev_PosX, tmp );
00868   int row = sheet->topRow( ev_PosY, tmp );
00869 
00870   const QMimeData* mimeData = _ev->mimeData();
00871   if ( !mimeData->hasText() && !mimeData->hasFormat( "application/x-kspread-snippet" ) )
00872   {
00873     _ev->ignore();
00874     return;
00875   }
00876 
00877   QByteArray b;
00878 
00879   bool makeUndo = true;
00880 
00881   if ( mimeData->hasFormat( "application/x-kspread-snippet" ) )
00882   {
00883     if ( _ev->source() == this  )
00884     {
00885         UndoDragDrop * undo
00886           = new UndoDragDrop(sheet, *selection(),
00887                              Region(QRect(col, row,
00888                                    selection()->boundingRect().width(),
00889                                    selection()->boundingRect().height())),
00890                              selection());
00891         d->view->doc()->addCommand( undo );
00892         makeUndo = false;
00893 
00894       DeleteCommand* command = new DeleteCommand();
00895       command->setSheet( activeSheet() );
00896       command->add( *selection() );
00897       command->setRegisterUndo( false );
00898       command->execute();
00899     }
00900 
00901 
00902     b = mimeData->data( "application/x-kspread-snippet" );
00903     sheet->paste( b, QRect( col, row, 1, 1 ), makeUndo );
00904 
00905     //Select the pasted cells
00906     selection()->initialize( QRect( col, row,  
00907                                     selection()->boundingRect().width(),
00908                                     selection()->boundingRect().height() ), sheet);
00909 
00910     _ev->setAccepted(true);
00911   }
00912   else
00913   {
00914     QString text = mimeData->text();
00915     sheet->pasteTextPlain( text, QRect( col, row, 1, 1 ) );
00916     _ev->setAccepted(true);
00917     return;
00918   }
00919 }
00920 
00921 void Canvas::slotAutoScroll(const QPoint &scrollDistance)
00922 {
00923   // NOTE Stefan: This slot is triggered by the same signal as
00924   //              ColumnHeader::slotAutoScroll and RowHeader::slotAutoScroll.
00925   //              Therefore, nothing has to be done except the scrolling was
00926   //              initiated in the canvas.
00927   if (!d->mousePressed)
00928     return;
00929   d->view->canvasController()->scrollContentsBy(scrollDistance.x(), scrollDistance.y());
00930 }
00931 
00932 QRect Canvas::viewToCellCoordinates( const QRectF& viewRect ) const
00933 {
00934   register Sheet * const sheet = activeSheet();
00935   if (!sheet)
00936     return QRect();
00937 
00938   const QRectF rect = d->view->zoomHandler()->viewToDocument( viewRect ).translated( offset() );
00939 
00940   double tmp;
00941   const int left = sheet->leftColumn( rect.left(), tmp );
00942   const int right = sheet->rightColumn( rect.right() );
00943   const int top = sheet->topRow( rect.top(), tmp );
00944   const int bottom = sheet->bottomRow( rect.bottom() );
00945 
00946   return QRect( left, top, right - left + 1, bottom - top + 1 );
00947 }
00948 
00949 QRect Canvas::visibleCells() const
00950 {
00951   return viewToCellCoordinates( rect() );
00952 }
00953 
00954 //---------------------------------------------
00955 //
00956 // Drawing Engine
00957 //
00958 //---------------------------------------------
00959 
00960 QRectF Canvas::cellCoordinatesToView( const QRect& cellRange ) const
00961 {
00962     register Sheet * const sheet = activeSheet();
00963     if (!sheet)
00964         return QRectF();
00965 
00966     QRectF rect = sheet->cellCoordinatesToDocument( cellRange );
00967     // apply scrolling offset
00968     rect.translate( -xOffset(), -yOffset() );
00969     // convert it to view coordinates
00970     rect = d->view->zoomHandler()->documentToView( rect );
00971     // apply layout direction
00972     if ( sheet->layoutDirection() == Qt::RightToLeft )
00973     {
00974         const double left = rect.left();
00975         const double right = rect.right();
00976         rect.setLeft( width() - right );
00977         rect.setRight( width() - left );
00978     }
00979     return rect;
00980 }
00981 
00982 void Canvas::showToolTip( const QPoint& p )
00983 {
00984     register Sheet * const sheet = activeSheet();
00985     if (!sheet)
00986         return;
00987 
00988     // Over which cell is the mouse ?
00989     double ypos, xpos;
00990     double dwidth = d->view->zoomHandler()->viewToDocumentX( width() );
00991     int col;
00992     if ( sheet->layoutDirection() == Qt::RightToLeft )
00993       col = sheet->leftColumn( (dwidth - d->view->zoomHandler()->viewToDocumentX( p.x() ) +
00994                                               xOffset()), xpos );
00995     else
00996       col = sheet->leftColumn( (d->view->zoomHandler()->viewToDocumentX( p.x() ) +
00997                                      xOffset()), xpos );
00998 
00999 
01000     int row = sheet->topRow( (d->view->zoomHandler()->viewToDocumentY( p.y() ) +
01001                                    yOffset()), ypos );
01002 
01003     Cell cell = Cell(sheet, col, row).masterCell();
01004     CellView cellView = view()->sheetView(sheet)->cellView(cell.column(), cell.row());
01005     if (cellView.isObscured())
01006     {
01007         cell = Cell(sheet, cellView.obscuringCell());
01008         cellView = view()->sheetView(sheet)->cellView(cellView.obscuringCell().x(), cellView.obscuringCell().y());
01009     }
01010 
01011     // displayed tool tip, which has the following priorities:
01012     //  - cell content if the cell dimension is too small
01013     //  - cell comment
01014     //  - hyperlink
01015     // Ensure that it is plain text.
01016     // Not funny if (intentional or not) <a> appears as hyperlink.
01017     QString tipText;
01018     // If cell is too small, show the content
01019     if (!cellView.dimensionFits())
01020         tipText = cell.displayText().replace('<', "&lt;");
01021 
01022     // Show hyperlink, if any
01023     if ( tipText.isEmpty() )
01024         tipText = cell.link().replace('<', "&lt;");
01025 
01026     // Nothing to display, bail out
01027     if (tipText.isEmpty() && cell.comment().isEmpty())
01028       return;
01029 
01030     // Cut if the tip is ridiculously long
01031     const int maxLen = 256;
01032     if ( tipText.length() > maxLen )
01033         tipText = tipText.left(maxLen).append("...");
01034 
01035     // Determine position and width of the current cell.
01036     const double cellWidth = cellView.cellWidth();
01037     const double cellHeight = cellView.cellHeight();
01038 
01039     // Get the cell dimensions
01040     QRect cellRect;
01041     bool insideCellRect = false;
01042     if ( sheet->layoutDirection() == Qt::RightToLeft )
01043     {
01044         const QRectF rect(dwidth - cellWidth - xpos + xOffset(), ypos - yOffset(), cellWidth, cellHeight);
01045         cellRect = viewConverter()->documentToView(rect).toRect();
01046         insideCellRect = cellRect.contains( p );
01047     }
01048     else
01049     {
01050         QRectF rect(xpos - xOffset(), ypos - yOffset(), cellWidth, cellHeight);
01051         cellRect = viewConverter()->documentToView(rect).toRect();
01052         insideCellRect = cellRect.contains( p );
01053     }
01054 
01055     // No use if mouse is somewhere else
01056     if ( !insideCellRect )
01057         return;
01058 
01059     // Show comment, if any.
01060     if (tipText.isEmpty())
01061         tipText = cell.comment().replace('<', "&lt;");
01062     else if (!cell.comment().isEmpty())
01063         tipText += "</p><h4>" + i18n("Comment:") + "</h4><p>" + cell.comment().replace('<', "&lt;");
01064 
01065     // Now we show the tip
01066     QToolTip::showText(mapToGlobal(cellRect.bottomRight()),
01067                        "<p>" + tipText.replace('\n', "<br>") + "</p>",
01068                        this, cellRect.translated(-mapToGlobal(cellRect.topLeft())));
01069 }
01070 
01071 void Canvas::updateInputMethodInfo() {
01072     updateMicroFocus();
01073 }
01074 
01075 #include "Canvas.moc"