/* This file is part of the KDE project Copyright 2006 Stefan Nikolaus Copyright 1999-2002,2004 Laurent Montel Copyright 2002-2005 Ariya Hidayat Copyright 1999-2001,2003 David Faure Copyright 2001-2003 Philipp Mueller Copyright 2002-2003 Norbert Andres Copyright 2000-2001 Werner Trobin Copyright 2002 Harri Porten Copyright 2002 John Dailey Copyright 1999-2000 Torben Weis Copyright 2000 Wilco Greven Copyright 1999 Boris Wedl Copyright 1999 Reginald Stadlbauer 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. */ #ifndef KSPREAD_CANVAS #define KSPREAD_CANVAS #include #include #include #include #include #include #include "kspread_util.h" #define YBORDER_WIDTH 50 #define XBORDER_HEIGHT 20 class QWidget; class QTimer; class QButton; class QPainter; class QLabel; class QScrollBar; class KoRect; class KoPoint; namespace KSpread { class Cell; class EditWidget; class Canvas; class HBorder; class VBorder; class Sheet; class Doc; class Point; class Range; class Region; class View; class Selection; class CellEditor; class LocationEditWidget; class ComboboxLocationEditWidget; class EmbeddedObject; /** * The canvas builds a part of the GUI of KSpread. * It contains the borders, scrollbars, * editwidget and of course it displays the sheet. * Especially most of the user interface logic is implemented here. * That means that this class knows what to do when a key is pressed * or if the mouse button was clicked. */ class KSPREAD_EXPORT Canvas : public QWidget { friend class HBorder; friend class VBorder; friend class View; Q_OBJECT public: /** * The current action associated with the mouse. * Default is 'NoAction'. */ enum MouseActions { /** No mouse action (default) */ NoAction, /** Marking action */ Mark, /** Merging cell */ ResizeCell, /** Autofilling */ AutoFill, /** Resizing the selection */ ResizeSelection }; enum EditorType { CellEditor, EditWidget }; Canvas (View *_view); ~Canvas( ); View* view() const; Doc* doc() const; KSpread::EditWidget* editWidget() const; KSpread::CellEditor* editor() const; /** * @return the usual selection of cells */ Selection* selectionInfo() const; /** * @return a selection of cells used in formulas */ Selection* choice() const; /** * @deprecated use selectionInfo() * Selections are no longer contiguous in general. * @see Selection::lastRange() * @see Selection::activeElement() */ QRect selection() const; /** * convenience function. * @return selection's marker * @see Selection::marker() */ QPoint marker() const; /** * convenience function. * @return selection's marker's column * @see Selection::marker() */ int markerColumn() const; /** * convenience function. * @return selection's marker's row * @see Selection::marker() */ int markerRow() const; /** * @return the pen, the default grid is painted with (light gray) */ const QPen& defaultGridPen() const; /** * convenience function * @see View::zoom() */ double zoom() const; /** * @return the width of the columns before the current screen */ double xOffset() const; /** * @return the height of the rows before the current screen */ double yOffset() const; /** * Sets the width of the columns before the current screen */ void setXOffset( double _xOffset ); /** * Sets the height of the rows before the current screen */ void setYOffset( double _yOffset ); /** * @return a rect indicating which cell range is currently visible onscreen */ QRect visibleCells() const; /** * @return a pointer to the active sheet */ Sheet* activeSheet() const; /** * convenience function * @see Map::findSheet() */ Sheet* findSheet( const QString& _name ) const; /** * Validates the selected cell. */ void validateSelection(); /** * Paint all visible cells that have a paint dirty flag set */ void paintUpdates(); /** * Makes sure a cell is visible onscreen by scrolling up/down and left/right * * @param location the cell coordinates to scroll to */ void scrollToCell(QPoint location) const; /** * Chooses the correct EditorType by looking at * the current cells value. By default CellEditor is chosen. */ void createEditor( bool captureArrowKeys=false ); bool createEditor( EditorType type, bool addFocus = true, bool captureArrowKeys=false ); /** * Deletes the current cell editor. * * @see #createEditor * @see #editor * @param saveChanges if true, the edited text is stored in the cell. * if false, the changes are discarded. * @param array if true, array formula was entered */ void deleteEditor(bool saveChanges, bool array = false); /** * Called from @ref EditWidget and CellEditor * if they loose the focus because the user started a "choose selection". * This is done because the editor wants to get its focus back afterwards. * But somehow Canvas must know whether the EditWidget or the CellEditor * lost the focus when the user clicked on the canvas. */ void setLastEditorWithFocus( EditorType type ); /** * Switches to choose mode and sets the initial selection to the * position returned by marker(). * Clears the choice. */ void startChoose(); /** * Switches to choose mode and sets the initial @p selection. */ void startChoose( const QRect& selection ); /** * Switches to selection mode. * Clear the choice. */ void endChoose(); /** * Switches the choose mode on and off. * Does not clear the choice. */ void setChooseMode(bool state); /** * @return @c true if choose mode is enabled, @c false otherwise */ bool chooseMode() const; void equalizeRow(); void equalizeColumn(); /** * Updates the position widget. */ void updatePosWidget(); /** * Close the cell editor and saves changes. * @see deleteEditor() */ void closeEditor(); // Created by the view since it's layout is managed there, // but is in fact a sibling of the canvas, which needs to know about it. void setEditWidget( KSpread::EditWidget * ew ); virtual bool focusNextPrevChild( bool ); /** * Depending on the offset in "zoomed" screen pixels * for the horizontal direction, * the function returns the steps in unzoomed points * for the autoscroll acceleration */ double autoScrollAccelerationX( int offset ); /** * Depending on the offset in "zoomed" screen pixels * for the vertical direction, * the function returns the steps in unzoomed points * for the autoscroll acceleration */ double autoScrollAccelerationY( int offset ); //TODO: These embedded-object related methods need API documentation! EmbeddedObject* getObject( const QPoint &pos, Sheet *_sheet ); void selectAllObjects(); void deselectAllObjects(); void selectObject( EmbeddedObject* ); void deselectObject( EmbeddedObject* ); void setMouseSelectedObject(bool b); bool isObjectSelected(); /** * @brief Move object by mouse * * @param pos The position of the mouse * @param keepXorYunchanged if true keep x or y position unchanged */ void moveObjectsByMouse( KoPoint &pos, bool keepXorYunchanged ); //---- stuff needed for resizing ---- /// resize the m_resizeObject void resizeObject( ModifyType _modType, const KoPoint & point, bool keepRatio ); /// create KPrResizeCmd void finishResizeObject( const QString &name, bool layout = true ); /** * @brief Display object above the other objects in editiong mode * * This is used to bring a single slected object to front, so it is easier * to modify. * * @param object which should be displayed above the other objects */ void raiseObject( EmbeddedObject *object ); /** * @brief Don't display an object above the others */ void lowerObject(); /** * @brief Get the list of objects in the order they should be displayed. * * This takes into acount the object set in raiseObject so that it is * the last one in the list returned (the one that is displayed above all * the others). * * @return List of objects */ void displayObjectList( QPtrList &list ); KoRect objectRect( bool all ) const; void repaintObject( EmbeddedObject *obj ); /** * This is intended to copy the selected objects to the clipboard so that they can be pasted into other * applications. However, until at least KWord, KSpread, KPresenter, KChart and KFormula have consistant * support for copying and pasting of OASIS objects the selected objects will just be copied in the form * of raster graphics */ void copyOasisObjects(); //void insertOasisData(); public slots: void slotScrollVert( int _value ); void slotScrollHorz( int _value ); void slotMaxColumn( int _max_column ); void slotMaxRow( int _max_row ); signals: void objectSelectedChanged(); void objectSizeChanged(); protected: virtual void keyPressEvent ( QKeyEvent* _ev ); virtual void paintEvent ( QPaintEvent* _ev ); virtual void mousePressEvent( QMouseEvent* _ev ); virtual void mouseReleaseEvent( QMouseEvent* _ev ); virtual void mouseMoveEvent( QMouseEvent* _ev ); virtual void mouseDoubleClickEvent( QMouseEvent* ); virtual void wheelEvent( QWheelEvent* ); virtual void focusInEvent( QFocusEvent* ); virtual void focusOutEvent( QFocusEvent* ); virtual void resizeEvent( QResizeEvent * _ev ); virtual void dragMoveEvent(QDragMoveEvent * _ev); virtual void dropEvent(QDropEvent * _ev); virtual void dragLeaveEvent(QDragLeaveEvent * _ev); /** * Checks to see if there is a size grip for a highlight range at a given position. * Note that both X and Y coordinates are UNZOOMED. To translate from a zoomed coordinate (eg. position of a mouse event) to * an unzoomed coordinate, use Doc::unzoomItX and Doc::unzoomItY. The document object * can be accessed via view()->doc() * @param x Unzoomed x coordinate to check * @param y Unzoomed y coordinate to check * @return @c true if there is a size grip at the specified position, @c false otherwise. */ bool highlightRangeSizeGripAt(double x, double y); private slots: /** * Scroll canvas when receiving this signal */ void slotAutoScroll(const QPoint &scrollDist); void doAutoScroll(); void speakCell(QWidget* w, const QPoint& p, uint flags); private: virtual bool eventFilter( QObject *o, QEvent *e ); HBorder* hBorderWidget() const; VBorder* vBorderWidget() const; QScrollBar* horzScrollBar() const; QScrollBar* vertScrollBar() const; /** * Returns the area of the document currently visible in a painter's * window, calculated by taking the painter's window() property and * translating it by the current x and y offset of the Canvas (taking * the zoom level into account) */ QRect painterWindowGeometry( const QPainter& painter ) const; /** * Enables clipping and removes the areas on the canvas widget occupied by embedded objects from * the clip region. This ensures that subsequent drawing operations using the given painter * don't paint over the area occupied by embedded objects */ void clipoutChildren( QPainter& painter ) const; /** * Returns the range of cells which appear in the specified area of the Canvas widget * For example, cellsInArea( QRect(0,0,width(),height()) ) returns a range containing all visible cells * * @param area The area (in pixels) on the Canvas widget */ QRect cellsInArea( const QRect area ) const; /** * Paints the children */ void paintChildren( QPainter& painter, QWMatrix& matrix ); /** * @see #setLastEditorWithFocus */ EditorType lastEditorWithFocus() const; private: void moveObject( int x, int y, bool key ); void startTheDrag(); /* helpers for the paintUpdates function */ void paintNormalMarker(QPainter& painter, const KoRect &viewRect); /** * Paint the highlighted ranges of cells. When the user is editing a formula in a text box, cells and ranges referenced * in the formula are highlighted on the canvas. * @param painter The painter on which to draw the highlighted ranges * @param viewRect The area currently visible on the canvas */ void paintHighlightedRanges(QPainter& painter, const KoRect& viewRect); /** * Calculates the visible region on the canvas occupied by a range of cells on the currently active sheet. * This is used for drawing the thick border around the current selection or highlights around cell range * references. * The results do not take into account the current zoom factor of the sheet, * use Doc::zoomRect on @p visibleRect after calling this function to get a new rectangle taking * the zoom level into account. * @param sheetArea The range of cells on the current sheet * @param visibleRect This is set to the visible region occupied by the given range of cells * */ void sheetAreaToVisibleRect( const QRect& sheetArea, KoRect& visibleRect ); /** * Calculates the physical region on the canvas widget occupied by a range of cells on * the currently active sheet. * Unlike @see sheetAreaToVisibleRect , scrolling the view does not affect sheetAreaToRect. * * @param sheetArea The range of cells on the current sheet * @param visibleRect This is set to the physical region occupied by the given range of cells */ void sheetAreaToRect( const QRect& sheetArea, KoRect& rect ); /** * helper function in drawing the marker and choose marker. * @param marker the rectangle that represents the marker being drawn * (cell coordinates) * @param viewRect the visible area on the canvas * @param positions output parameter where the viewable left, top, right, and * bottom of the marker will be. They are stored in the array * in that order, and take into account cropping due to part * of the marker being off screen. This array should have * at least a size of 4 pre-allocated. * @param paintSides booleans indicating whether a particular side is visible. * Again, these are in the order left, top, right, bottom. * This should be preallocated with a size of at least 4. */ void retrieveMarkerInfo( const QRect &marker, const KoRect &viewRect, double positions[], bool paintSides[] ); bool formatKeyPress( QKeyEvent * _ev ); /** helper method for formatKeyPress */ bool formatCellByKey(Cell *cell, int key, const QRect &rect); void processClickSelectionHandle(QMouseEvent *event); void processLeftClickAnchor(); /** current cursor position, be it marker or choose marker */ QPoint cursorPos(); /** * returns the rect that needs to be redrawn */ QRect moveDirection(KSpread::MoveTo direction, bool extendSelection); void processEnterKey(QKeyEvent *event); void processArrowKey(QKeyEvent *event); void processEscapeKey(QKeyEvent *event); bool processHomeKey(QKeyEvent *event); bool processEndKey(QKeyEvent *event); bool processPriorKey(QKeyEvent *event); bool processNextKey(QKeyEvent *event); void processDeleteKey(QKeyEvent *event); void processF2Key(QKeyEvent *event); void processF4Key(QKeyEvent *event); void processOtherKey(QKeyEvent *event); bool processControlArrowKey(QKeyEvent *event); void processIMEvent( QIMEvent * event ); /** * Used in choose mode. Shows/hides the editor depending on the selected * sheet. Triggers an update of the regions shown in the CellEditor. * @see CellEditor::updateChoice() */ void updateEditor(); /** * This function sets the paint dirty flag for a @p changedRegion in a * @p sheet . * The calculation which cells really should look different with the new * selection rather than repainting the entire area has to be done before. * @param sheet the sheet, which contains the cells * @param changedRegion the cell region to be set as dirty */ void setSelectionChangePaintDirty(Sheet* sheet, const Region& changedRegion); private: class Private; Private* d; }; /** * HBorder */ class HBorder : public QWidget { Q_OBJECT public: HBorder( QWidget *_parent, Canvas *_canvas, View *_view ); ~HBorder(); int markerColumn() const { return m_iSelectionAnchor; } void equalizeColumn( double resize ); void updateColumns( int from, int to ); QSize sizeHint() const; private slots: void doAutoScroll(); protected: virtual void paintEvent ( QPaintEvent* _ev ); virtual void mousePressEvent( QMouseEvent* _ev ); virtual void mouseReleaseEvent( QMouseEvent* _ev ); virtual void mouseDoubleClickEvent( QMouseEvent* _ev ); virtual void mouseMoveEvent( QMouseEvent* _ev ); virtual void wheelEvent( QWheelEvent* ); virtual void focusOutEvent( QFocusEvent* ev ); virtual void resizeEvent( QResizeEvent * _ev ); void paintSizeIndicator( int mouseX, bool firstTime ); private: Canvas *m_pCanvas; View *m_pView; QTimer * m_scrollTimer; /** * Flag that inidicates whether the user wants to mark columns. * The user may mark columns by dragging the mouse around in th XBorder widget. * If he is doing that right now, this flag is true. Mention that the user may * also resize columns by dragging the mouse. This case is not covered by this flag. */ bool m_bSelection; /** * The column over which the user pressed the mouse button. * If the user marks columns in the XBorder widget, then this is the initial * column on which he pressed the mouse button. */ int m_iSelectionAnchor; /** * Flag that indicates whether the user resizes a column * The user may resize columns by dragging the mouse around in the HBorder widget. * If he is doing that right now, this flag is true. */ bool m_bResize; /** * The column over which the user pressed the mouse button. * The user may resize columns by dragging the mouse around the XBorder widget. * This is the column over which he pressed the mouse button. This column is going * to be resized. */ int m_iResizedColumn; /** * Last position of the mouse, when resizing. */ int m_iResizePos; /** * The label used for showing the current size, when resizing */ QLabel *m_lSize; /** * True when the mouse button is pressed */ bool m_bMousePressed; private: }; /** * VBorder */ class VBorder : public QWidget { Q_OBJECT public: VBorder( QWidget *_parent, Canvas *_canvas, View *_view ); ~VBorder(); int markerRow() const { return m_iSelectionAnchor; } void equalizeRow( double resize ); void updateRows( int from, int to ); QSize sizeHint() const; private slots: void doAutoScroll(); protected: virtual void paintEvent ( QPaintEvent* _ev ); virtual void mousePressEvent( QMouseEvent* _ev ); virtual void mouseReleaseEvent( QMouseEvent* _ev ); virtual void mouseMoveEvent( QMouseEvent* _ev ); virtual void mouseDoubleClickEvent( QMouseEvent* _ev ); virtual void wheelEvent( QWheelEvent* ); virtual void focusOutEvent( QFocusEvent* ev ); void paintSizeIndicator( int mouseY, bool firstTime ); private: Canvas *m_pCanvas; View *m_pView; QTimer * m_scrollTimer; bool m_bSelection; int m_iSelectionAnchor; bool m_bResize; int m_iResizedRow; int m_iResizePos; /** * The label used for showing the current size, when resizing */ QLabel *m_lSize; /** * True when the mouse button is pressed */ bool m_bMousePressed; }; /** * Tooltip, which displays the comment and cell content, when it's too short */ class ToolTip : public QToolTip { public: ToolTip( Canvas* canvas ); protected: /** * @reimp */ void maybeTip( const QPoint& p ); private: Canvas* m_canvas; }; } // namespace KSpread #endif // KSPREAD_CANVAS