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.
tdesdk/umbrello/umbrello/umlwidgetcontroller.h

477 lines
18 KiB

/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* copyright (C) 2006 *
* Umbrello UML Modeller Authors <uml-devel@uml.sf.net> *
***************************************************************************/
#ifndef UMLWIDGETCONTROLLER_H
#define UMLWIDGETCONTROLLER_H
// qt includes
#include <tqdatetime.h>
// app includes
#include "umlwidgetlist.h"
class TQCursor;
class TQMouseEvent;
class TQMoveEvent;
class TQPoint;
class UMLWidget;
/**
* Controller for UMLWidget
* This class takes care of user interaction with UMLWidgets: select, deselect,
* move, resize...
* Those actions are done using events. There are handlers for mousePressEvent,
* mouseMoveEvent, mouseReleaseEvent and mouseDoubleClickEvent. There's more
* information about each of them in their respective documentation.
*
* Behaviour of this class can be customized overriding the handlers themselves
* (which isn't recommended) or, better, overriding the virtual protected
* methods.
*
* The area that is considered as "resize area" can be customized with
* isInResizeArea, so when there's a pressed button in this area, mouseMoveEvent
* will resize the widget.
* Also, if the resize area doesn't need to be modified, but the cursor to be
* used when the mouse is in that area can be done with getResizeCursor.
* When a widget is being resized, it's done using resizeWidget, so overriding
* it makes possible to, for example, constrain the resizing only in one axis
* no matter how the mouse was moved.
*
* Widget move can also be customized. The widgets are moved in mouseMoveEvent
* using the moveWidgetBy method of the controller of each selected widget.
* Overriding this method widget movement can be, for example, constrained to
* an axis, no matter if the widget is being moved explicitly or as part of a
* selection.
* On the other hand, the move of all the selected widgets can be constrained
* with constrainMovementForAllWidgets. This method, called in the controller
* that is handling the mouseMoveEvent, modifies the difference between the
* current position of the widgets and the new position to be moved to. For
* example, if a widget shouldn't be moved in X axis, it's receiving the
* mouseMoveEvents and there are other widgets selected, those other widgets
* shouldn't be allowed either to be moved in X axis.
*
* The behaviour when double clicking on the widget after it's selected can be
* customized with doMouseDoubleClick.
*
* @author Umbrello UML Modeller Authors <uml-devel@lists.sourceforge.net>
*/
class UMLWidgetController {
public:
/**
* Constructor for UMLWidgetController.
*
* @param widget The widget which uses the controller.
*/
UMLWidgetController(UMLWidget *widget);
/**
* Destructor for UMLWidgetController.
*/
virtual ~UMLWidgetController();
/**
* Handles a mouse press event.
* It'll select the widget (or mark it to be deselected) and prepare it to
* be moved or resized. Go on reading for more info about this.
*
* Widget values and message bar status are saved.
*
* If shift or control buttons are pressed, we're in move area no matter
* where the button was pressed in the widget. Moreover, if the widget
* wasn't already selected, it's added to the selection. If already selected,
* it's marked to be deselected when releasing the button (provided it isn't
* moved).
* Also, if the widget is already selected with other widgets but shift nor
* control buttons are pressed, we're in move area. If finally we don't move
* the widget, it's selected and the other widgets deselected when releasing
* the left button.
*
* If shift nor control buttons are pressed, we're facing a single selection.
* Depending on the position of the cursor, we're in move or in resize area.
* If the widget wasn't selected (both when there are no widgets selected, or
* when there're other widgets selected but not the one receiving the press
* event) it's selected and the others deselected, if any. If already selected,
* it's marked to be deselected when releasing the button (provided it wasn't
* moved or resized).
*
* @param me The TQMouseEvent event.
*/
virtual void mousePressEvent(TQMouseEvent *me);
/**
* Handles a mouse move event.
* It resizes or moves the widget, depending on where the cursor is pressed
* on the widget. Go on reading for more info about this.
*
* If resizing, the widget is resized using resizeWidget (where specific
* widget resize constrain can be applied), and then the associations are
* adjusted.
* The resizing can be constrained also to an specific axis using control
* and shift buttons. If on or another is pressed, it's constrained to X axis.
* If both are pressed, it's constrained to Y axis.
*
* If not resizing, the widget is being moved. If the move is being started,
* the selection bounds are set (which includes updating the list of selected
* widgets).
* The difference between the previous position of the selection and the new
* one is got (taking in account the selection bounds so widgets don't go
* beyond the canvas limits). Then, it's constrained to X or Y axis depending
* on shift and control buttons.
* A further constrain is made using constrainMovementForAllWidgets (for example,
* if the widget that receives the event can only be moved in Y axis, with this
* method the movement of all the widgets in the selection can be constrained to
* be moved only in Y axis).
* Then, all the selected widgets are moved using moveWidgetBy (where specific
* widget movement constrain can be applied) and, if an specific amount of time
* passed from the last move event, the associations are also updated (they're
* not updated always to be easy on the CPU). Finally, the canvas is resized,
* and selection bounds updated.
*
* @param me The TQMouseEvent event.
*/
virtual void mouseMoveEvent(TQMouseEvent* me);
/**
* Handles a mouse release event.
* It selects or deselects the widget and cancels or confirms the move or
* resize. Go on reading for more info about this.
* No matter which tool is selected, Z position of widget is updated.
*
* Middle button release resets the selection.
* Left button release, if it wasn't moved nor resized, selects the widget
* and deselect the others if it wasn't selected and there were other widgets
* selected. If the widget was marked to be deselected, deselects it.
* If it was moved or resized, the document is set to modified if position
* or size changed. Also, if moved, all the associations are adjusted because
* the timer could have prevented the adjustment in the last move event before
* the release.
* If mouse was pressed in resize area, cursor is set again to normal cursor
* Right button release if right button was pressed shows the pop up menu for
* the widget.
* If left button was pressed, it cancels the move or resize with a mouse move
* event at the same position than the cursor was when pressed. Another left
* button release is also sent.
*
* @param me The TQMouseEvent event.
*/
virtual void mouseReleaseEvent(TQMouseEvent * me);
/**
* Handles a mouse double click event.
* If the button wasn't left button it does nothing. Otherwise, it selects
* the widget (deselecting other selected widgets, if any) and executes
* doMouseDoubleClick.
* @see doMouseDoubleClick
*
* @param me The TQMouseEvent event.
*/
virtual void mouseDoubleClickEvent(TQMouseEvent *me);
protected:
/**
* Saves the values of the widget needed for move/resize.
* The values saved are: the offset from the cursor respect to the upper left
* corner of the widget in m_pressOffsetX/Y, the position in m_oldX/Y and the
* size in m_oldW/H.
*
* It can be overridden to save subclass specific values whenever a move or
* resize begins. However, parent method (that is, this method) must be
* called in the overridden method.
*
* @param me The TQMouseEvent to get the offset from.
*/
virtual void saveWidgetValues(TQMouseEvent *me);
/**
* Checks if the mouse is in resize area (right bottom corner), and sets
* the cursor depending on that.
* The cursor used when resizing is gotten from getResizeCursor().
*
* @param me The QMouseEVent to check.
* @return true if the mouse is in resize area, false otherwise.
*/
virtual bool isInResizeArea(TQMouseEvent *me);
/**
* Returns the cursor to be shown when resizing the widget.
*
* Default cursor is KCursor::sizeFDiagCursor().
*
* @return The cursor to be shown when resizing the widget.
*/
virtual TQCursor getResizeCursor();
/**
* Resizes the widget.
* It's called from resize, after the values are constrained and before
* the associations are adjusted.
*
* Default behaviour is resize the widget using the new size values.
* @see resize
*
* @param newW The new width for the widget.
* @param newH The new height for the widget.
*/
virtual void resizeWidget(int newW, int newH);
/**
* Moves the widget to a new position using the difference between the
* current position and the new position.
* This method doesn't adjust associations. It only moves the widget.
*
* It can be overridden to constrain movement of m_widget only in one axis even when
* the user isn't constraining the movement with shift or control buttons, for example.
* The movement policy set here is applied whenever the widget is moved, being it
* moving it explicitly, or as a part of a selection but not receiving directly the
* mouse events.
*
* Default behaviour is move the widget to the new position using the diffs.
* @see constrainMovementForAllWidgets
*
* @param diffX The difference between current X position and new X position.
* @param diffY The difference between current Y position and new Y position.
*/
virtual void moveWidgetBy(int diffX, int diffY);
/**
* Modifies the value of the diffX and diffY variables used to move the widgets.
*
* It can be overridden to constrain movement of all the selected widgets only in one
* axis even when the user isn't constraining the movement with shift or control
* buttons, for example.
* The difference with moveWidgetBy is that the diff positions used here are
* applied to all the selected widgets instead of only to m_widget, and that
* moveWidgetBy, in fact, moves the widget, and here simply the diff positions
* are modified.
*
* Default behaviour is do nothing.
* @see moveWidgetBy
*
* @param diffX The difference between current X position and new X position.
* @param diffY The difference between current Y position and new Y position.
*/
virtual void constrainMovementForAllWidgets(int &diffX, int &diffY);
/**
* Executes the action for double click in the widget.
* It's called only if the button used was left button.
* Before calling this method, the widget is selected.
*
* Default behaviour is show the properties dialog for the widget using
* m_widget->slotMenuSelection(ListPopupMenu::mt_Properties);
* If the widget doesn't have a property dialog (from the Widget_Type enum, those that
* don't have an UMLObject representation) there's no need to override
* the method, it simply does nothing.
*
* @param me The TQMouseEvent which triggered the double click event.
*/
virtual void doMouseDoubleClick(TQMouseEvent *me);
/**
* Clears the selection, resets the toolbar and deselects the widget.
*/
void resetSelection();
/**
* Selects the widget and clears the other selected widgets, if any.
*
* @param me The TQMouseEvent which made the selection.
*/
void selectSingle(TQMouseEvent *me);
/**
* Selects the widget and adds it to the list of selected widgets.
*
* @param me The TQMouseEvent which made the selection.
*/
void selectMultiple(TQMouseEvent *me);
/**
* Deselects the widget and removes it from the list of selected widgets.
*
* @param me The TQMouseEvent which made the selection.
*/
void deselect(TQMouseEvent *me);
/**
* Fills m_selectedWidgetsList and sets the selection bounds ((m_min/m_max)X/Y attributes).
*/
void setSelectionBounds();
/**
* Updates the selection bounds based on the movement made.
* If it was only a vertical movement, there's no need to update horizontal bounds,
* and vice versa.
*
* @param diffX The difference between current X position and new X position.
* @param diffY The difference between current Y position and new Y position.
*/
void updateSelectionBounds(int diffX, int diffY);
/**
* Resizes the widget and adjusts the associations.
* It's called when a mouse move event happens and the cursor was
* in resize area when pressed.
* Resizing can be constrained to an specific axis using control and shift buttons.
*
* @param me The TQMouseEvent to get the values from.
*/
void resize(TQMouseEvent *me);
/**
* Returns the smallest X position of all the widgets in the list.
*
* @param widgetList A list with UMLWidgets.
* @return The smallest X position.
*/
int getSmallestX(const UMLWidgetList &widgetList);
/**
* Returns the smallest Y position of all the widgets in the list.
*
* @param widgetList A list with UMLWidgets.
* @return The smallest Y position.
*/
int getSmallestY(const UMLWidgetList &widgetList);
/**
* Returns the biggest X position of all the widgets in the list.
*
* @param widgetList A list with UMLWidgets.
* @return The biggest X position.
*/
int getBiggestX(const UMLWidgetList &widgetList);
/**
* Returns the biggest Y position of all the widgets in the list.
*
* @param widgetList A list with UMLWidgets.
* @return The biggest Y position.
*/
int getBiggestY(const UMLWidgetList &widgetList);
/**
* Returns the adjusted position for the given mouse event.
* The adjusted position is computed using the current widget position
* m_widget->get{X,Y}(), the previous position m_old{X,Y}, and the
* mouse press offset m_pressOffset{X,Y}.
*
* @param me The TQMouseEvent for which to get the adjusted position.
* @return A TQPoint with the adjusted position.
*/
TQPoint getPosition(TQMouseEvent *me);
/**
* Returns a TQPoint with the new X and Y position difference of the mouse event
* respect to the position of the widget.
*
* @param me The TQMouseEvent to get the position to compare.
* @return A TQPoint with the position difference.
*/
TQPoint getPositionDifference(TQMouseEvent *me);
/**
* Shows the widget popup menu where the mouse event points to.
*
* @param me The TQMouseEvent which triggered the showing.
*/
void showPopupMenu(TQMouseEvent *me);
/**
* Checks if the size of the widget changed respect to the size that
* it had when press event was fired.
*
* @return true if was resized, false otherwise.
*/
bool wasSizeChanged();
/**
* Checks if the position of the widget changed respect to the position that
* it had when press event was fired.
*
* @return true if was moved, false otherwise.
*/
bool wasPositionChanged();
/**
* The widget which uses the controller.
*/
UMLWidget *m_widget;
/**
* Timer that prevents excessive updates (be easy on the CPU).
*/
TQTime lastUpdate;
/**
* A list containing the selected widgets.
* It's filled by setSelectionBounds method. It must be filled again if
* selected widgets changed. It is cleared only in setSelectionBounds, just
* before filling it.
* Select, deselect and so on methods DON'T modify this list.
*/
UMLWidgetList m_selectedWidgetsList;
/**
* The text in the status bar when the cursor was pressed.
*/
TQString m_oldStatusBarMsg;
/**
* The X/Y offset from the position of the cursor when it was pressed to the
* upper left corner of the widget.
*/
int m_pressOffsetX, m_pressOffsetY;
/**
* The X/Y position the widget had when the movement started.
*/
int m_oldX, m_oldY;
/**
* The width/height the widget had when the resize started.
*/
int m_oldW, m_oldH;
/**
* The minimum/maximum X/Y position of all the selected widgets.
*/
int m_minSelectedX, m_minSelectedY, m_maxSelectedX, m_maxSelectedY;
/**
* If shift or control button were pressed in mouse press event.
*/
bool m_shiftPressed;
/**
* If the left/middle/right button is pressed.
*/
bool m_leftButtonDown, m_middleButtonDown, m_rightButtonDown;
/**
* If cursor was in move/resize area when left button was pressed (and no
* other widgets were selected).
*/
bool m_inMoveArea, m_inResizeArea;
/**
* If the widget was selected/moved/resized in the press and release cycle.
* Moved/resized is true if the widget was moved/resized even if the final
* position/size is the same as the starting one.
*/
bool m_wasSelected, m_moved, m_resized;
};
#endif