Commit c4f4129e authored by Joris Steyn's avatar Joris Steyn
Browse files

Show only menu actions applicable for selection

When multiple widgets are selected, a context menu is shown containing only the
actions that work on the whole selection.

The old implementation had a few features like changing the "show" options
on a classifier for multiple selected widgets and bulk-change classifiers to
interfaces. That code is removed now since it wasn't used in the first place
and it's problematic in both implementation and user interface.

Additional improvements:
 * fix bug where fonts and colors sometimes wont apply
 * fix bug where right-click on an unselected widget does not select the widget
 * allow cloning multiple widgets
 * remove double keyboard shortcuts for delete action

Also, checkable menu items should not be used in the context menu of multiple
widgets. There's no sane initial value if the widgets are set to a different
value. The only nice way to handle this is have one menu item for setting the option
and one for unsetting. This is done for the "Use fill color" checkbox.

BUG: 329070
parent 40979a85
......@@ -277,55 +277,48 @@ ListPopupMenu::ListPopupMenu(QWidget *parent, UMLListViewItem::ListViewType type
* @param parent The parent to ListPopupMenu.
* @param object The WidgetBase to represent a menu for.
* @param multi True if multiple items are selected.
* @param unique True if multiple selected items all have
* the same type (e.g. Class, Interface)
*/
ListPopupMenu::ListPopupMenu(QWidget * parent, WidgetBase * object,
bool multi, bool unique)
ListPopupMenu::ListPopupMenu(QWidget * parent, WidgetBase * object, bool multi)
: KMenu(parent)
{
m_TriggerObject.m_Widget = object;
m_TriggerObjectType = tot_Widget;
//make the right menu for the type
//make menu for logical view
if (!object)
return;
WidgetBase::WidgetType type = object->baseType();
// uDebug() << "ListPopupMenu created with multi=" << multi << ", unique="
// << unique << " for WidgetType=" << WidgetBase::toString(type);
if (multi) {
ClassifierWidget *c = NULL;
if (unique && (type == WidgetBase::wt_Class || type == WidgetBase::wt_Interface)) {
c = static_cast<ClassifierWidget *>(object);
makeMultiClassifierPopup(c);
}
insertSubMenuColor(object->useFillColor());
addSeparator();
insert(mt_Cut);
insert(mt_Copy);
insert(mt_Paste);
addSeparator();
insert(mt_Change_Font_Selection, Icon_Utils::SmallIcon(Icon_Utils::it_Change_Font), i18n("Change Font..."));
insert(mt_Delete_Selection, Icon_Utils::SmallIcon(Icon_Utils::it_Delete), i18n("Delete Selected Items"));
// add this here and not above with the other stuff of the interface
// user might expect it at this position of the context menu
if (unique) {
if (type == WidgetBase::wt_Interface) {
insert(mt_DrawAsCircle_Selection, i18n("Draw as Circle"), CHECKABLE);
setActionChecked(mt_DrawAsCircle_Selection, c->visualProperty(ClassifierWidget::DrawAsCircle));
insert(mt_ChangeToClass_Selection, i18n("Change into Class"));
} else if (type == WidgetBase::wt_Class) {
UMLClassifier *umlc = c->classifier();
if (umlc->isAbstract() && umlc->attributes() == 0)
insert(mt_ChangeToInterface_Selection, i18n("Change into Interface"));
}
}
insertMultiSelectionMenu();
} else {
insertSingleSelectionMenu(object);
}
return;
bool bCutState = UMLApp::app()->isCutCopyState();
setActionEnabled(mt_Cut, bCutState);
setActionEnabled(mt_Copy, bCutState);
setActionEnabled(mt_Paste, false);
setupActionsData();
}
/**
* Standard destructor.
*/
ListPopupMenu::~ListPopupMenu()
{
foreach (QAction* action, m_actions) {
delete action;
}
m_actions.clear();
}
/**
* Inserts the menu actions for a widget
*
* @param WidgetBase* object
*/
void ListPopupMenu::insertSingleSelectionMenu(WidgetBase* object)
{
WidgetBase::WidgetType type = object->baseType();
switch (type) {
case WidgetBase::wt_Actor:
case WidgetBase::wt_UseCase:
......@@ -589,25 +582,35 @@ ListPopupMenu::ListPopupMenu(QWidget * parent, WidgetBase * object,
uWarning() << "unhandled WidgetType " << WidgetBase::toString(type);
break;
}//end switch
bool bCutState = UMLApp::app()->isCutCopyState();
setActionEnabled(mt_Cut, bCutState);
setActionEnabled(mt_Copy, bCutState);
setActionEnabled(mt_Paste, false);
setupActionsData();
}
/**
* Standard destructor.
* Inserts the menu actions that work on the whole selection of widgets
*/
ListPopupMenu::~ListPopupMenu()
void ListPopupMenu::insertMultiSelectionMenu()
{
foreach (QAction* action, m_actions) {
delete action;
}
m_actions.clear();
KMenu* color = new KMenu(i18nc("color menu", "Color"), this);
insert(mt_Line_Color, color, Icon_Utils::SmallIcon(Icon_Utils::it_Color_Line), i18n("Line Color..."));
insert(mt_Fill_Color, color, Icon_Utils::SmallIcon(Icon_Utils::it_Color_Fill), i18n("Fill Color..."));
insert(mt_Set_Use_Fill_Color_Selection, color, i18n("Use Fill Color"));
insert(mt_Unset_Use_Fill_Color_Selection, color, i18n("No Fill Color"));
addMenu(color);
addSeparator();
insert(mt_Cut);
insert(mt_Copy);
addSeparator();
insert(mt_Clone);
insert(mt_Delete);
insert(mt_Resize, i18n("Resize"));
addSeparator();
insert(mt_Change_Font, Icon_Utils::SmallIcon(Icon_Utils::it_Change_Font), i18n("Change Font..."));
}
/**
* Shortcut for the frequently used addAction() calls.
*
......@@ -1040,10 +1043,9 @@ void ListPopupMenu::insertLayoutItems(UMLView *view)
}
/**
* Creates a popup menu for a multiple selection of class and
* interface widgets.
* Creates the "Show" submenu for classifier widgets context menu
*/
void ListPopupMenu::makeMultiClassifierPopup(ClassifierWidget *c)
void ListPopupMenu::makeClassifierVisibilityPopup(ClassifierWidget *c)
{
WidgetBase::WidgetType type = c->baseType();
ClassifierWidget *cls = NULL;
......@@ -1094,7 +1096,7 @@ void ListPopupMenu::makeClassifierPopup(ClassifierWidget *c)
insert(mt_Template, menu, Icon_Utils::SmallIcon(Icon_Utils::it_Template_New), i18n("Template..."));
addMenu(menu);
makeMultiClassifierPopup(c);
makeClassifierVisibilityPopup(c);
insertSubMenuColor(c->useFillColor());
insertStdItems(true, type);
......
......@@ -15,6 +15,7 @@
#include "widgetbase.h"
#include "umllistviewitem.h"
#include "umlobject.h"
#include "widgetbase.h"
#include <kmenu.h>
......@@ -179,11 +180,12 @@ public:
mt_AttributeAssociation, // Rendering of an attribute as an association
mt_RoleNameA,
mt_RoleNameB,
mt_Delete_Selection,
mt_Reset_Label_Positions,
mt_Line_Color,
mt_Fill_Color,
mt_Use_Fill_Color,
mt_Set_Use_Fill_Color_Selection,
mt_Unset_Use_Fill_Color_Selection,
mt_Default_Properties,
mt_Rename_MultiA,
mt_Rename_MultiB,
......@@ -191,7 +193,6 @@ public:
mt_Rename_RoleAName,
mt_Rename_RoleBName,
mt_Change_Font,
mt_Change_Font_Selection,
mt_SnapToGrid,
mt_ShowDocumentationIndicator,
mt_ShowSnapGrid,
......@@ -250,7 +251,7 @@ public:
explicit ListPopupMenu(QWidget* parent, MenuType type = mt_Undefined, UMLView* view = 0);
ListPopupMenu(QWidget* parent, MenuType type, WidgetBase *widget);
ListPopupMenu(QWidget* parent, UMLListViewItem::ListViewType type, UMLObject* object);
ListPopupMenu(QWidget* parent, WidgetBase* object, bool multi = false, bool unique = false);
ListPopupMenu(QWidget* parent, WidgetBase* object, bool multi = false);
virtual ~ListPopupMenu();
......@@ -269,6 +270,9 @@ public:
private:
void insertSingleSelectionMenu(WidgetBase* object);
void insertMultiSelectionMenu();
void insert(MenuType m);
void insert(const MenuType m, KMenu* menu);
void insert(const MenuType m, const QIcon & icon, const QString & text);
......@@ -285,8 +289,8 @@ private:
void insertSubmodelAction();
void insertLayoutItems(UMLView *view);
void makeMultiClassifierPopup(ClassifierWidget *c);
void makeClassifierPopup(ClassifierWidget *c);
void makeClassifierVisibilityPopup(ClassifierWidget *c);
KMenu* makeCategoryTypeMenu(UMLCategory* category);
void insertSubMenuNew(MenuType type);
......
......@@ -1638,6 +1638,14 @@ void UMLScene::selectWidgets(UMLWidgetList &widgets)
makeSelected(widget);
}
/**
* Add one widget to the selected widgets list
*/
void UMLScene::selectWidget(UMLWidget* widget)
{
makeSelected(widget);
}
/**
* Returns the PNG picture of the paste operation.
* @param diagram the class to store PNG picture of the paste operation.
......@@ -3426,32 +3434,6 @@ void UMLScene::checkSelections()
}//end foreach
}
/**
* This function checks if the currently selected items have all the same
* type (class, interface, ...). If true, the selection is unique and true
* will be returned.
* If there are no items selected, the function will return always true.
*/
bool UMLScene::checkUniqueSelection()
{
// if there are no selected items, we return true
if (selectedWidgets().isEmpty())
return true;
// get the first item and its base type
UMLWidget * pTemp = (UMLWidget *) selectedWidgets().first();
WidgetBase::WidgetType tmpType = pTemp->baseType();
// check all selected items, if they have the same BaseType
foreach(pTemp, selectedWidgets()) {
if (pTemp->baseType() != tmpType) {
return false; // the base types are different, the list is not unique
}
} // for (through all selected items)
return true; // selected items are unique
}
/**
* Asks for confirmation and clears everything on the diagram.
* Called from menus.
......
......@@ -258,8 +258,6 @@ public:
void checkSelections();
bool checkUniqueSelection();
void clearDiagram();
void applyLayout(const QString &actionText);
......@@ -284,6 +282,7 @@ public:
void selectWidgets(qreal px, qreal py, qreal qx, qreal qy);
void selectWidgets(UMLWidgetList &widgets);
void selectWidget(UMLWidget* widget);
void selectWidgetsOfAssoc(AssociationWidget *a);
ObjectWidget * onWidgetLine(const QPointF &point) const;
......
......@@ -3040,16 +3040,6 @@ void AssociationWidget::slotMenuSelection(QAction* action)
}
break;
case ListPopupMenu::mt_Change_Font_Selection:
{
QFont fnt = font();
if (KFontDialog::getFont( fnt, KFontChooser::NoDisplayFlags, m_scene->activeView())) {
m_scene->selectionSetFont( fnt );
umlDoc()->setModified(true);
}
}
break;
case ListPopupMenu::mt_Line_Color:
{
QColor newColor;
......
......@@ -710,10 +710,6 @@ void UMLWidget::slotMenuSelection(QAction *trigger)
ListPopupMenu::MenuType sel = ListPopupMenu::typeFromAction(trigger);
switch (sel) {
case ListPopupMenu::mt_Delete:
umlScene()->removeWidget(this);
break;
case ListPopupMenu::mt_Resize:
umlScene()->resizeSelection();
break;
......
......@@ -667,15 +667,37 @@ void WidgetBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
uDebug() << "widget = " << name() << " / type = " << baseTypeStr();
UMLScene *scene = umlScene();
if (!isSelected() && scene && !scene->selectedItems().isEmpty()) {
// If right-click was done on a widget that was not selected, clear the
// current selection and select the new widget. The context menu is shown
// with actions for that single widget.
// If a keyboard modifier was used, add the widget to the current selection
// and show the menu with actions for the whole selection.
if (!isSelected()) {
Qt::KeyboardModifiers forSelection = (Qt::ControlModifier | Qt::ShiftModifier);
if ((event->modifiers() & forSelection) == 0) {
scene->clearSelection();
scene->clearSelected();
}
if (umlObject() != 0) {
scene->selectWidget(dynamic_cast<UMLWidget*>(this));
} else {
setSelected(true);
}
}
setSelected(true);
ListPopupMenu popup(0, this, false, false);
setupContextMenuActions(popup);
int count = scene->selectedCount(true);
// Determine multi state
bool multi = (isSelected() && count > 1);
ListPopupMenu popup(0, this, multi);
// Disable the "view code" menu for simple code generators
if (UMLApp::app()->isSimpleCodeGeneratorActive()) {
popup.setActionEnabled(ListPopupMenu::mt_ViewCode, false);
}
QAction *triggered = popup.exec(event->screenPos());
ListPopupMenu *parentMenu = ListPopupMenu::menuFromAction(triggered);
......@@ -694,38 +716,6 @@ void WidgetBase::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
ownerWidget->slotMenuSelection(triggered);
}
/**
*
*/
void WidgetBase::setupContextMenuActions(ListPopupMenu &menu)
{
/* the following was code from UMLWidget::setupPopupMenu():
//if in a multi- selection to a specific m_pMenu for that
// NEW: ask UMLView to count ONLY the widgets and not their floatingtextwidgets
int count = m_scene->selectedCount(true);
//a MessageWidget when selected will select its text widget and vice versa
//so take that into account for popup menu.
// determine multi state
bool multi = (isSelected() && count > 1);
// if multiple selected items have the same type
bool unique = false;
// if multiple items are selected, we have to check if they all have the same
// base type
if (multi == true)
unique = m_scene->checkUniqueSelection();
// create the right click context menu
m_pMenu = new ListPopupMenu(m_scene->activeView(), this, multi, unique);
*/
// disable the "view code" menu for simple code generators
if (UMLApp::app()->isSimpleCodeGeneratorActive())
menu.setActionEnabled(ListPopupMenu::mt_ViewCode, false);
}
/**
* This is usually called synchronously after menu.exec() and \a
* trigger's parent is always the ListPopupMenu which can be used to
......@@ -739,8 +729,7 @@ void WidgetBase::slotMenuSelection(QAction *trigger)
if (!trigger) {
return;
}
QColor newColour;
WidgetBase* widget = 0; // use for select the first object properties (fill, line color)
QColor newColor;
const WidgetType wt = m_baseType; // short hand name
......@@ -750,10 +739,6 @@ void WidgetBase::slotMenuSelection(QAction *trigger)
umlDoc()->renameUMLObject(umlObject());
break;
//case ListPopupMenu::mt_Delete: // is done in UMLWidget
// umlScene()->removeWidget(this);
// break;
case ListPopupMenu::mt_Properties:
if (wt == WidgetBase::wt_Actor || wt == WidgetBase::wt_UseCase ||
wt == WidgetBase::wt_Package || wt == WidgetBase::wt_Interface ||
......@@ -772,30 +757,31 @@ void WidgetBase::slotMenuSelection(QAction *trigger)
break;
case ListPopupMenu::mt_Line_Color:
widget = umlScene()->getFirstMultiSelectedWidget();
if (widget) {
newColour = widget->lineColor();
}
if (KColorDialog::getColor(newColour)) {
umlScene()->selectionSetLineColor(newColour);
newColor = this->lineColor();
if (KColorDialog::getColor(newColor)) {
umlScene()->selectionSetLineColor(newColor);
umlDoc()->setModified(true);
}
break;
case ListPopupMenu::mt_Fill_Color:
widget = umlScene()->getFirstMultiSelectedWidget();
if (widget) {
//:TODO: newColour = widget->brush().color();
newColour = widget->fillColor();
}
if (KColorDialog::getColor(newColour)) {
umlScene()->selectionSetFillColor(newColour);
newColor = this->fillColor();
if (KColorDialog::getColor(newColor)) {
umlScene()->selectionSetFillColor(newColor);
umlDoc()->setModified(true);
}
break;
case ListPopupMenu::mt_Use_Fill_Color:
umlScene()->selectionUseFillColor(!m_useFillColor);
this->setUseFillColor(!m_useFillColor);
break;
case ListPopupMenu::mt_Set_Use_Fill_Color_Selection:
umlScene()->selectionUseFillColor(true);
break;
case ListPopupMenu::mt_Unset_Use_Fill_Color_Selection:
umlScene()->selectionUseFillColor(false);
break;
case ListPopupMenu::mt_Show_Attributes_Selection:
......@@ -819,16 +805,14 @@ void WidgetBase::slotMenuSelection(QAction *trigger)
break;
}
case ListPopupMenu::mt_Delete_Selection:
case ListPopupMenu::mt_Delete:
umlScene()->deleteSelection();
break;
case ListPopupMenu::mt_Change_Font:
case ListPopupMenu::mt_Change_Font_Selection: {
case ListPopupMenu::mt_Change_Font: {
QFont newFont = font();
if (KFontDialog::getFont(newFont, KFontChooser::NoDisplayFlags, 0) == KFontDialog::Accepted) {
setFont(newFont);
//UMLApp::app()->executeCommand(new CmdChangeFontSelection(m_doc, m_pView, font));
m_scene->selectionSetFont(newFont);
}
}
break;
......@@ -854,10 +838,13 @@ void WidgetBase::slotMenuSelection(QAction *trigger)
break;
case ListPopupMenu::mt_Clone:
// In principle we clone all the uml objects.
{
UMLObject *pClone = umlObject()->clone();
umlScene()->addObject(pClone);
foreach (UMLWidget* widget, umlScene()->selectedWidgets()) {
if (Model_Utils::isCloneable(widget->baseType())) {
UMLObject *clone = widget->umlObject()->clone();
umlScene()->addObject(clone);
}
}
}
break;
......
......@@ -150,7 +150,6 @@ public Q_SLOTS:
protected:
virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event);
virtual void setupContextMenuActions(ListPopupMenu &menu);
WidgetType m_baseType; ///< Type of widget.
UMLScene *m_scene;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment