1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the Qt Designer of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | #include "button_taskmenu.h" |
30 | #include "inplace_editor.h" |
31 | #include <qdesigner_formwindowcommand_p.h> |
32 | #include <formwindowbase_p.h> |
33 | |
34 | #include <QtDesigner/abstractformwindow.h> |
35 | #include <QtDesigner/abstractformwindowcursor.h> |
36 | #include <QtDesigner/abstractformeditor.h> |
37 | #include <QtDesigner/abstractmetadatabase.h> |
38 | #include <QtDesigner/abstractobjectinspector.h> |
39 | #include <QtDesigner/abstractpropertyeditor.h> |
40 | |
41 | #include <QtWidgets/qaction.h> |
42 | #include <QtWidgets/qactiongroup.h> |
43 | #include <QtWidgets/qmenu.h> |
44 | #include <QtWidgets/qstyle.h> |
45 | #include <QtWidgets/qstyleoption.h> |
46 | #include <QtWidgets/qabstractbutton.h> |
47 | #include <QtWidgets/qbuttongroup.h> |
48 | #include <QtWidgets/qapplication.h> |
49 | #include <QtCore/qdebug.h> |
50 | |
51 | Q_DECLARE_METATYPE(QButtonGroup*) |
52 | |
53 | QT_BEGIN_NAMESPACE |
54 | |
55 | namespace qdesigner_internal { |
56 | |
57 | enum { = 0 }; |
58 | |
59 | using ButtonList = QList<QAbstractButton *>; |
60 | using ButtonGroupList = QList<QButtonGroup *>; |
61 | |
62 | // ButtonGroupCommand: Base for commands handling button groups and button lists |
63 | // addButtonsToGroup() and removeButtonsFromGroup() are low-level helpers for |
64 | // adding/removing members to/from existing groups. |
65 | // |
66 | // createButtonGroup()/breakButtonGroup() create and remove the groups from scratch. |
67 | // When using them in a command, the command must be executed within |
68 | // a macro since it makes the form emit objectRemoved() which might cause other components |
69 | // to add commands (for example, removal of signals and slots) |
70 | class ButtonGroupCommand : public QDesignerFormWindowCommand { |
71 | |
72 | protected: |
73 | ButtonGroupCommand(const QString &description, QDesignerFormWindowInterface *formWindow); |
74 | |
75 | void initialize(const ButtonList &bl, QButtonGroup *buttonGroup); |
76 | |
77 | // Helper: Add the buttons to the group |
78 | void addButtonsToGroup(); |
79 | // Helper; Remove the buttons |
80 | void removeButtonsFromGroup(); |
81 | |
82 | // Create the button group in Designer |
83 | void createButtonGroup(); |
84 | // Remove the button group from Designer |
85 | void breakButtonGroup(); |
86 | |
87 | public: |
88 | static QString nameList(const ButtonList& bl); |
89 | static ButtonGroupList managedButtonGroups(const QDesignerFormWindowInterface *formWindow); |
90 | |
91 | private: |
92 | ButtonList m_buttonList; |
93 | QButtonGroup *m_buttonGroup; |
94 | }; |
95 | |
96 | ButtonGroupCommand::ButtonGroupCommand(const QString &description, QDesignerFormWindowInterface *formWindow) : |
97 | QDesignerFormWindowCommand(description, formWindow), |
98 | m_buttonGroup(nullptr) |
99 | { |
100 | } |
101 | |
102 | void ButtonGroupCommand::initialize(const ButtonList &bl, QButtonGroup *buttonGroup) |
103 | { |
104 | m_buttonList = bl; |
105 | m_buttonGroup = buttonGroup; |
106 | } |
107 | |
108 | void ButtonGroupCommand::addButtonsToGroup() |
109 | { |
110 | if (debugButtonMenu) |
111 | qDebug() << "Adding " << m_buttonList << " to " << m_buttonGroup; |
112 | const ButtonList::const_iterator cend = m_buttonList.constEnd(); |
113 | for (ButtonList::const_iterator it = m_buttonList.constBegin(); it != cend; ++it) |
114 | m_buttonGroup->addButton(*it); |
115 | } |
116 | |
117 | void ButtonGroupCommand::removeButtonsFromGroup() |
118 | { |
119 | if (debugButtonMenu) |
120 | qDebug() << "Removing " << m_buttonList << " from " << m_buttonGroup; |
121 | const ButtonList::const_iterator cend = m_buttonList.constEnd(); |
122 | for (ButtonList::const_iterator it = m_buttonList.constBegin(); it != cend; ++it) |
123 | m_buttonGroup->removeButton(*it); |
124 | } |
125 | |
126 | void ButtonGroupCommand::createButtonGroup() |
127 | { |
128 | if (debugButtonMenu) |
129 | qDebug() << "Creating " << m_buttonGroup << " from " << m_buttonList; |
130 | |
131 | QDesignerFormWindowInterface *fw = formWindow(); |
132 | QDesignerFormEditorInterface *core = fw->core(); |
133 | core->metaDataBase()->add(object: m_buttonGroup); |
134 | addButtonsToGroup(); |
135 | // Make button group visible |
136 | core->objectInspector()->setFormWindow(fw); |
137 | } |
138 | |
139 | void ButtonGroupCommand::breakButtonGroup() |
140 | { |
141 | if (debugButtonMenu) |
142 | qDebug() << "Removing " << m_buttonGroup << " consisting of " << m_buttonList; |
143 | |
144 | QDesignerFormWindowInterface *fw = formWindow(); |
145 | QDesignerFormEditorInterface *core = fw->core(); |
146 | // Button group was selected, that is, break was invoked via its context menu. Remove it from property editor, select the buttons |
147 | if (core->propertyEditor()->object() == m_buttonGroup) { |
148 | fw->clearSelection(changePropertyDisplay: false); |
149 | const ButtonList::const_iterator cend = m_buttonList.constEnd(); |
150 | for (ButtonList::const_iterator it = m_buttonList.constBegin(); it != cend; ++it) |
151 | fw->selectWidget(w: *it, select: true); |
152 | } |
153 | // Now remove and refresh object inspector |
154 | removeButtonsFromGroup(); |
155 | // Notify components (for example, signal slot editor) |
156 | if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase *>(object: fw)) |
157 | fwb->emitObjectRemoved(o: m_buttonGroup); |
158 | core->metaDataBase()->remove(object: m_buttonGroup); |
159 | core->objectInspector()->setFormWindow(fw); |
160 | } |
161 | |
162 | QString ButtonGroupCommand::nameList(const ButtonList& bl) |
163 | { |
164 | QString rc; |
165 | const QChar quote = QLatin1Char('\''); |
166 | const QString separator = QStringLiteral(", " ); |
167 | const int size = bl.size(); |
168 | for (int i = 0; i < size; i++) { |
169 | if (i) |
170 | rc += separator; |
171 | rc += quote; |
172 | rc += bl[i]->objectName(); |
173 | rc += quote; |
174 | } |
175 | return rc; |
176 | |
177 | } |
178 | |
179 | ButtonGroupList ButtonGroupCommand::managedButtonGroups(const QDesignerFormWindowInterface *formWindow) |
180 | { |
181 | const QDesignerMetaDataBaseInterface *mdb = formWindow->core()->metaDataBase(); |
182 | ButtonGroupList bl; |
183 | // Check 1st order children for managed button groups |
184 | const QObjectList children = formWindow->mainContainer()->children(); |
185 | const QObjectList::const_iterator cend = children.constEnd(); |
186 | for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { |
187 | if (!(*it)->isWidgetType()) |
188 | if (QButtonGroup *bg = qobject_cast<QButtonGroup *>(object: *it)) |
189 | if (mdb->item(object: bg)) |
190 | bl.push_back(t: bg); |
191 | } |
192 | return bl; |
193 | } |
194 | |
195 | // --------------- CreateButtonGroupCommand |
196 | // This command might be executed in a macro with a remove |
197 | // command to move buttons from one group to a new one. |
198 | class CreateButtonGroupCommand : public ButtonGroupCommand { |
199 | public: |
200 | CreateButtonGroupCommand(QDesignerFormWindowInterface *formWindow); |
201 | bool init(const ButtonList &bl); |
202 | |
203 | void undo() override { breakButtonGroup(); } |
204 | void redo() override { createButtonGroup(); } |
205 | }; |
206 | |
207 | CreateButtonGroupCommand::CreateButtonGroupCommand(QDesignerFormWindowInterface *formWindow) : |
208 | ButtonGroupCommand(QApplication::translate(context: "Command" , key: "Create button group" ), formWindow) |
209 | { |
210 | } |
211 | |
212 | bool CreateButtonGroupCommand::init(const ButtonList &bl) |
213 | { |
214 | if (bl.isEmpty()) |
215 | return false; |
216 | QDesignerFormWindowInterface *fw = formWindow(); |
217 | QButtonGroup *buttonGroup = new QButtonGroup(fw->mainContainer()); |
218 | buttonGroup->setObjectName(QStringLiteral("buttonGroup" )); |
219 | fw->ensureUniqueObjectName(object: buttonGroup); |
220 | initialize(bl, buttonGroup); |
221 | return true; |
222 | } |
223 | |
224 | // --------------- BreakButtonGroupCommand |
225 | class BreakButtonGroupCommand : public ButtonGroupCommand { |
226 | public: |
227 | BreakButtonGroupCommand(QDesignerFormWindowInterface *formWindow); |
228 | bool init(QButtonGroup *group); |
229 | |
230 | void undo() override { createButtonGroup(); } |
231 | void redo() override { breakButtonGroup(); } |
232 | }; |
233 | |
234 | BreakButtonGroupCommand::BreakButtonGroupCommand(QDesignerFormWindowInterface *formWindow) : |
235 | ButtonGroupCommand(QApplication::translate(context: "Command" , key: "Break button group" ), formWindow) |
236 | { |
237 | } |
238 | |
239 | bool BreakButtonGroupCommand::init(QButtonGroup *group) |
240 | { |
241 | if (!group) |
242 | return false; |
243 | initialize(bl: group->buttons(), buttonGroup: group); |
244 | setText(QApplication::translate(context: "Command" , key: "Break button group '%1'" ).arg(a: group->objectName())); |
245 | return true; |
246 | } |
247 | |
248 | // --------------- AddButtonsToGroupCommand |
249 | // This command might be executed in a macro with a remove |
250 | // command to move buttons from one group to a new one. |
251 | class AddButtonsToGroupCommand : public ButtonGroupCommand { |
252 | public: |
253 | AddButtonsToGroupCommand(QDesignerFormWindowInterface *formWindow); |
254 | void init(const ButtonList &bl, QButtonGroup *group); |
255 | |
256 | void undo() override { removeButtonsFromGroup(); } |
257 | void redo() override { addButtonsToGroup(); } |
258 | }; |
259 | |
260 | AddButtonsToGroupCommand::AddButtonsToGroupCommand(QDesignerFormWindowInterface *formWindow) : |
261 | ButtonGroupCommand(QApplication::translate(context: "Command" , key: "Add buttons to group" ), formWindow) |
262 | { |
263 | } |
264 | |
265 | void AddButtonsToGroupCommand::init(const ButtonList &bl, QButtonGroup *group) |
266 | { |
267 | initialize(bl, buttonGroup: group); |
268 | //: Command description for adding buttons to a QButtonGroup |
269 | setText(QApplication::translate(context: "Command" , key: "Add '%1' to '%2'" ).arg(args: nameList(bl), args: group->objectName())); |
270 | } |
271 | |
272 | //-------------------- RemoveButtonsFromGroupCommand |
273 | class RemoveButtonsFromGroupCommand : public ButtonGroupCommand { |
274 | public: |
275 | RemoveButtonsFromGroupCommand(QDesignerFormWindowInterface *formWindow); |
276 | bool init(const ButtonList &bl); |
277 | |
278 | void undo() override { addButtonsToGroup(); } |
279 | void redo() override { removeButtonsFromGroup(); } |
280 | }; |
281 | |
282 | RemoveButtonsFromGroupCommand::RemoveButtonsFromGroupCommand(QDesignerFormWindowInterface *formWindow) : |
283 | ButtonGroupCommand(QApplication::translate(context: "Command" , key: "Remove buttons from group" ), formWindow) |
284 | { |
285 | } |
286 | |
287 | bool RemoveButtonsFromGroupCommand::init(const ButtonList &bl) |
288 | { |
289 | if (bl.isEmpty()) |
290 | return false; |
291 | QButtonGroup *group = bl.constFirst()->group(); |
292 | if (!group) |
293 | return false; |
294 | if (bl.size() >= group->buttons().size()) |
295 | return false; |
296 | initialize(bl, buttonGroup: group); |
297 | //: Command description for removing buttons from a QButtonGroup |
298 | setText(QApplication::translate(context: "Command" , key: "Remove '%1' from '%2'" ).arg(args: nameList(bl), args: group->objectName())); |
299 | return true; |
300 | } |
301 | |
302 | // -------- ButtonGroupMenu |
303 | ButtonGroupMenu::(QObject *parent) : |
304 | QObject(parent), |
305 | m_selectGroupAction(new QAction(tr(s: "Select members" ), this)), |
306 | m_breakGroupAction(new QAction(tr(s: "Break" ), this)) |
307 | { |
308 | connect(sender: m_breakGroupAction, signal: &QAction::triggered, receiver: this, slot: &ButtonGroupMenu::breakGroup); |
309 | connect(sender: m_selectGroupAction, signal: &QAction::triggered, receiver: this, slot: &ButtonGroupMenu::selectGroup); |
310 | } |
311 | |
312 | void ButtonGroupMenu::(QDesignerFormWindowInterface *formWindow, QButtonGroup *buttonGroup, QAbstractButton *currentButton) |
313 | { |
314 | m_buttonGroup = buttonGroup; |
315 | m_currentButton = currentButton; |
316 | m_formWindow = formWindow; |
317 | Q_ASSERT(m_formWindow); |
318 | |
319 | const bool canBreak = buttonGroup != nullptr; |
320 | m_breakGroupAction->setEnabled(canBreak); |
321 | m_selectGroupAction->setEnabled(canBreak); |
322 | } |
323 | |
324 | void ButtonGroupMenu::() |
325 | { |
326 | // Select and make current button "current" again by selecting it last (if there is any) |
327 | const ButtonList buttons = m_buttonGroup->buttons(); |
328 | m_formWindow->clearSelection(changePropertyDisplay: false); |
329 | const ButtonList::const_iterator cend = buttons.constEnd(); |
330 | for (ButtonList::const_iterator it = buttons.constBegin(); it != cend; ++it) |
331 | if (*it != m_currentButton) |
332 | m_formWindow->selectWidget(w: *it, select: true); |
333 | if (m_currentButton) |
334 | m_formWindow->selectWidget(w: m_currentButton, select: true); |
335 | } |
336 | |
337 | void ButtonGroupMenu::() |
338 | { |
339 | BreakButtonGroupCommand *cmd = new BreakButtonGroupCommand(m_formWindow); |
340 | if (cmd->init(group: m_buttonGroup)) { |
341 | // Need a macro since the command might trigger additional commands |
342 | QUndoStack *history = m_formWindow->commandHistory(); |
343 | history->beginMacro(text: cmd->text()); |
344 | history->push(cmd); |
345 | history->endMacro(); |
346 | } else { |
347 | qWarning(msg: "** WARNING Failed to initialize BreakButtonGroupCommand!" ); |
348 | delete cmd; |
349 | } |
350 | } |
351 | |
352 | // ButtonGroupTaskMenu |
353 | ButtonGroupTaskMenu::(QButtonGroup *buttonGroup, QObject *parent) : |
354 | QObject(parent), |
355 | m_buttonGroup(buttonGroup) |
356 | { |
357 | m_taskActions.push_back(t: m_menu.breakGroupAction()); |
358 | m_taskActions.push_back(t: m_menu.selectGroupAction()); |
359 | } |
360 | |
361 | QAction *ButtonGroupTaskMenu::() const |
362 | { |
363 | return m_menu.selectGroupAction(); |
364 | } |
365 | |
366 | QList<QAction*> ButtonGroupTaskMenu::() const |
367 | { |
368 | m_menu.initialize(formWindow: QDesignerFormWindowInterface::findFormWindow(obj: m_buttonGroup), buttonGroup: m_buttonGroup); |
369 | return m_taskActions; |
370 | } |
371 | |
372 | // -------- Text area editor |
373 | class : public TaskMenuInlineEditor |
374 | { |
375 | public: |
376 | ButtonTextTaskMenuInlineEditor(QAbstractButton *button, QObject *parent); |
377 | |
378 | protected: |
379 | QRect editRectangle() const override; |
380 | }; |
381 | |
382 | ButtonTextTaskMenuInlineEditor::(QAbstractButton *button, QObject *parent) : |
383 | TaskMenuInlineEditor(button, ValidationMultiLine, QStringLiteral("text" ), parent) |
384 | { |
385 | } |
386 | |
387 | QRect ButtonTextTaskMenuInlineEditor::() const |
388 | { |
389 | QWidget *w = widget(); |
390 | QStyleOptionButton opt; |
391 | opt.init(w); |
392 | return w->style()->subElementRect(subElement: QStyle::SE_PushButtonContents, option: &opt, widget: w); |
393 | } |
394 | |
395 | // -------- Command link button description editor |
396 | class : public TaskMenuInlineEditor |
397 | { |
398 | public: |
399 | LinkDescriptionTaskMenuInlineEditor(QAbstractButton *button, QObject *parent); |
400 | |
401 | protected: |
402 | QRect editRectangle() const override; |
403 | }; |
404 | |
405 | LinkDescriptionTaskMenuInlineEditor::(QAbstractButton *button, QObject *parent) : |
406 | TaskMenuInlineEditor(button, ValidationMultiLine, QStringLiteral("description" ), parent) |
407 | { |
408 | } |
409 | |
410 | QRect LinkDescriptionTaskMenuInlineEditor::() const |
411 | { |
412 | QWidget *w = widget(); // TODO: What is the exact description area? |
413 | QStyleOptionButton opt; |
414 | opt.init(w); |
415 | return w->style()->subElementRect(subElement: QStyle::SE_PushButtonContents, option: &opt, widget: w); |
416 | } |
417 | |
418 | // ----------- ButtonTaskMenu: |
419 | |
420 | ButtonTaskMenu::(QAbstractButton *button, QObject *parent) : |
421 | QDesignerTaskMenu(button, parent), |
422 | m_assignGroupSubMenu(new QMenu), |
423 | m_assignActionGroup(nullptr), |
424 | m_assignToGroupSubMenuAction(new QAction(tr(s: "Assign to button group" ), this)), |
425 | m_currentGroupSubMenu(new QMenu), |
426 | m_currentGroupSubMenuAction(new QAction(tr(s: "Button group" ), this)), |
427 | m_createGroupAction(new QAction(tr(s: "New button group" ), this)), |
428 | m_preferredEditAction(new QAction(tr(s: "Change text..." ), this)), |
429 | m_removeFromGroupAction(new QAction(tr(s: "None" ), this)) |
430 | { |
431 | connect(sender: m_createGroupAction, signal: &QAction::triggered, receiver: this, slot: &ButtonTaskMenu::createGroup); |
432 | TaskMenuInlineEditor *textEditor = new ButtonTextTaskMenuInlineEditor(button, this); |
433 | connect(sender: m_preferredEditAction, signal: &QAction::triggered, receiver: textEditor, slot: &TaskMenuInlineEditor::editText); |
434 | connect(sender: m_removeFromGroupAction, signal: &QAction::triggered, receiver: this, slot: &ButtonTaskMenu::removeFromGroup); |
435 | |
436 | m_assignToGroupSubMenuAction->setMenu(m_assignGroupSubMenu); |
437 | |
438 | m_currentGroupSubMenu->addAction(action: m_groupMenu.breakGroupAction()); |
439 | m_currentGroupSubMenu->addAction(action: m_groupMenu.selectGroupAction()); |
440 | m_currentGroupSubMenuAction->setMenu(m_currentGroupSubMenu); |
441 | |
442 | |
443 | m_taskActions.append(t: m_preferredEditAction); |
444 | m_taskActions.append(t: m_assignToGroupSubMenuAction); |
445 | m_taskActions.append(t: m_currentGroupSubMenuAction); |
446 | m_taskActions.append(t: createSeparator()); |
447 | } |
448 | |
449 | ButtonTaskMenu::() |
450 | { |
451 | delete m_assignGroupSubMenu; |
452 | delete m_currentGroupSubMenu; |
453 | } |
454 | |
455 | QAction *ButtonTaskMenu::() const |
456 | { |
457 | return m_preferredEditAction; |
458 | } |
459 | |
460 | bool ButtonTaskMenu::(const QDesignerFormWindowInterface *fw, int buttonCount, SelectionType st, QButtonGroup *currentGroup) |
461 | { |
462 | // clear |
463 | if (m_assignActionGroup) { |
464 | delete m_assignActionGroup; |
465 | m_assignActionGroup = nullptr; |
466 | } |
467 | m_assignGroupSubMenu->clear(); |
468 | if (st == OtherSelection) |
469 | return false; |
470 | |
471 | |
472 | // Assign to new: Need several |
473 | const bool canAssignToNewGroup = buttonCount > 1; |
474 | m_createGroupAction->setEnabled(canAssignToNewGroup); |
475 | if (canAssignToNewGroup) |
476 | m_assignGroupSubMenu->addAction(action: m_createGroupAction); |
477 | |
478 | // Assign to other |
479 | const ButtonGroupList bl = ButtonGroupCommand::managedButtonGroups(formWindow: fw); |
480 | // Groups: Any groups to add to except the current? |
481 | const int groupCount = bl.size(); |
482 | const bool hasAddGroups = groupCount > 1 || (groupCount == 1 && !bl.contains(t: currentGroup)); |
483 | if (hasAddGroups) { |
484 | if (!m_assignGroupSubMenu->isEmpty()) |
485 | m_assignGroupSubMenu->addSeparator(); |
486 | // Create a new action group |
487 | m_assignActionGroup = new QActionGroup(this); |
488 | connect(sender: m_assignActionGroup, signal: &QActionGroup::triggered, receiver: this, slot: &ButtonTaskMenu::addToGroup); |
489 | |
490 | const ButtonGroupList::const_iterator cend = bl.constEnd(); |
491 | for (ButtonGroupList::const_iterator it = bl.constBegin(); it != cend; ++it) { |
492 | QButtonGroup *bg = *it; |
493 | if (*it != currentGroup) { |
494 | QAction *a = new QAction(bg->objectName(), m_assignGroupSubMenu); |
495 | a->setData(QVariant::fromValue(value: bg)); |
496 | m_assignActionGroup->addAction(a); |
497 | m_assignGroupSubMenu->addAction(action: a); |
498 | } |
499 | } |
500 | } |
501 | // Can remove: A homogenous selection of another group that does not completely break it. |
502 | const bool canRemoveFromGroup = st == GroupedButtonSelection; |
503 | m_removeFromGroupAction->setEnabled(canRemoveFromGroup); |
504 | if (canRemoveFromGroup) { |
505 | if (!m_assignGroupSubMenu->isEmpty()) |
506 | m_assignGroupSubMenu->addSeparator(); |
507 | m_assignGroupSubMenu->addAction(action: m_removeFromGroupAction); |
508 | } |
509 | return !m_assignGroupSubMenu->isEmpty(); |
510 | } |
511 | |
512 | QList<QAction*> ButtonTaskMenu::() const |
513 | { |
514 | ButtonTaskMenu *ncThis = const_cast<ButtonTaskMenu*>(this); |
515 | QButtonGroup *buttonGroup = nullptr; |
516 | |
517 | QDesignerFormWindowInterface *fw = formWindow(); |
518 | const SelectionType st = selectionType(cursor: fw->cursor(), ptrToGroup: &buttonGroup); |
519 | |
520 | m_groupMenu.initialize(formWindow: fw, buttonGroup, currentButton: button()); |
521 | const bool hasAssignOptions = ncThis->refreshAssignMenu(fw, buttonCount: fw->cursor()->selectedWidgetCount(), st, currentGroup: buttonGroup); |
522 | m_assignToGroupSubMenuAction->setVisible(hasAssignOptions); |
523 | // add/remove |
524 | switch (st) { |
525 | case UngroupedButtonSelection: |
526 | case OtherSelection: |
527 | m_currentGroupSubMenuAction->setVisible(false); |
528 | break; |
529 | case GroupedButtonSelection: |
530 | m_currentGroupSubMenuAction->setText(tr(s: "Button group '%1'" ).arg(a: buttonGroup->objectName())); |
531 | m_currentGroupSubMenuAction->setVisible(true); |
532 | break; |
533 | } |
534 | |
535 | return m_taskActions + QDesignerTaskMenu::taskActions(); |
536 | } |
537 | |
538 | |
539 | void ButtonTaskMenu::(int index, QAction *a) |
540 | { |
541 | m_taskActions.insert(i: index, t: a); |
542 | } |
543 | |
544 | /* Create a button list from the cursor selection */ |
545 | static ButtonList buttonList(const QDesignerFormWindowCursorInterface *cursor) |
546 | { |
547 | ButtonList rc; |
548 | const int selectionCount = cursor->selectedWidgetCount(); |
549 | for (int i = 0; i < selectionCount; i++) { |
550 | QAbstractButton *ab = qobject_cast<QAbstractButton *>(object: cursor->selectedWidget(index: i)); |
551 | Q_ASSERT(ab); |
552 | rc += ab; |
553 | } |
554 | return rc; |
555 | } |
556 | |
557 | // Create a command to remove the buttons from their group |
558 | // If it would leave an empty or 1-member group behind, create a break command instead |
559 | |
560 | static QUndoCommand *createRemoveButtonsCommand(QDesignerFormWindowInterface *fw, const ButtonList &bl) |
561 | { |
562 | |
563 | QButtonGroup *bg = bl.constFirst()->group(); |
564 | // Complete group or 1-member group? |
565 | if (bl.size() >= bg->buttons().size() - 1) { |
566 | BreakButtonGroupCommand *breakCmd = new BreakButtonGroupCommand(fw); |
567 | if (!breakCmd->init(group: bg)) { |
568 | qWarning(msg: "** WARNING Failed to initialize BreakButtonGroupCommand!" ); |
569 | delete breakCmd; |
570 | return nullptr; |
571 | } |
572 | return breakCmd; |
573 | } |
574 | // Just remove the buttons |
575 | |
576 | RemoveButtonsFromGroupCommand *removeCmd = new RemoveButtonsFromGroupCommand(fw); |
577 | if (!removeCmd->init(bl)) { |
578 | qWarning(msg: "** WARNING Failed to initialize RemoveButtonsFromGroupCommand!" ); |
579 | delete removeCmd; |
580 | return nullptr; |
581 | } |
582 | return removeCmd; |
583 | } |
584 | |
585 | void ButtonTaskMenu::() |
586 | { |
587 | QDesignerFormWindowInterface *fw = formWindow(); |
588 | const ButtonList bl = buttonList(cursor: fw->cursor()); |
589 | // Do we need to remove the buttons from an existing group? |
590 | QUndoCommand *removeCmd = nullptr; |
591 | if (bl.constFirst()->group()) { |
592 | removeCmd = createRemoveButtonsCommand(fw, bl); |
593 | if (!removeCmd) |
594 | return; |
595 | } |
596 | // Add cmd |
597 | CreateButtonGroupCommand *addCmd = new CreateButtonGroupCommand(fw); |
598 | if (!addCmd->init(bl)) { |
599 | qWarning(msg: "** WARNING Failed to initialize CreateButtonGroupCommand!" ); |
600 | delete addCmd; |
601 | return; |
602 | } |
603 | // Need a macro [even if we only have the add command] since the command might trigger additional commands |
604 | QUndoStack *history = fw->commandHistory(); |
605 | history->beginMacro(text: addCmd->text()); |
606 | if (removeCmd) |
607 | history->push(cmd: removeCmd); |
608 | history->push(cmd: addCmd); |
609 | history->endMacro(); |
610 | } |
611 | |
612 | QAbstractButton *ButtonTaskMenu::() const |
613 | { |
614 | return qobject_cast<QAbstractButton *>(object: widget()); |
615 | } |
616 | |
617 | // Figure out if we have a homogenous selections (buttons of the same group or no group) |
618 | ButtonTaskMenu::SelectionType ButtonTaskMenu::(const QDesignerFormWindowCursorInterface *cursor, QButtonGroup **ptrToGroup) const |
619 | { |
620 | const int selectionCount = cursor->selectedWidgetCount(); |
621 | if (!selectionCount) |
622 | return OtherSelection; |
623 | |
624 | QButtonGroup *commonGroup = nullptr; |
625 | for (int i = 0; i < selectionCount; i++) { |
626 | if (const QAbstractButton *ab = qobject_cast<const QAbstractButton *>(object: cursor->selectedWidget(index: i))) { |
627 | QButtonGroup *buttonGroup = ab->group(); |
628 | if (i) { |
629 | if (buttonGroup != commonGroup) |
630 | return OtherSelection; |
631 | } else { |
632 | commonGroup = buttonGroup; |
633 | } |
634 | } else { |
635 | return OtherSelection; |
636 | } |
637 | } |
638 | |
639 | if (ptrToGroup) |
640 | *ptrToGroup = commonGroup; |
641 | |
642 | return commonGroup ? GroupedButtonSelection : UngroupedButtonSelection; |
643 | } |
644 | |
645 | void ButtonTaskMenu::(QAction *a) |
646 | { |
647 | QButtonGroup *bg = qvariant_cast<QButtonGroup *>(v: a->data()); |
648 | Q_ASSERT(bg); |
649 | |
650 | QDesignerFormWindowInterface *fw = formWindow(); |
651 | const ButtonList bl = buttonList(cursor: fw->cursor()); |
652 | // Do we need to remove the buttons from an existing group? |
653 | QUndoCommand *removeCmd = nullptr; |
654 | if (bl.constFirst()->group()) { |
655 | removeCmd = createRemoveButtonsCommand(fw, bl); |
656 | if (!removeCmd) |
657 | return; |
658 | } |
659 | AddButtonsToGroupCommand *addCmd = new AddButtonsToGroupCommand(fw); |
660 | addCmd->init(bl, group: bg); |
661 | |
662 | QUndoStack *history = fw->commandHistory(); |
663 | if (removeCmd) { |
664 | history->beginMacro(text: addCmd->text()); |
665 | history->push(cmd: removeCmd); |
666 | history->push(cmd: addCmd); |
667 | history->endMacro(); |
668 | } else { |
669 | history->push(cmd: addCmd); |
670 | } |
671 | } |
672 | |
673 | void ButtonTaskMenu::() |
674 | { |
675 | QDesignerFormWindowInterface *fw = formWindow(); |
676 | if (QUndoCommand *cmd = createRemoveButtonsCommand(fw, bl: buttonList(cursor: fw->cursor()))) |
677 | fw->commandHistory()->push(cmd); |
678 | } |
679 | |
680 | // -------------- CommandLinkButtonTaskMenu |
681 | |
682 | CommandLinkButtonTaskMenu::CommandLinkButtonTaskMenu(QCommandLinkButton *button, QObject *parent) : |
683 | ButtonTaskMenu(button, parent) |
684 | { |
685 | TaskMenuInlineEditor *descriptonEditor = new LinkDescriptionTaskMenuInlineEditor(button, this); |
686 | QAction *descriptionAction = new QAction(tr(s: "Change description..." ), this); |
687 | connect(sender: descriptionAction, signal: &QAction::triggered, receiver: descriptonEditor, slot: &TaskMenuInlineEditor::editText); |
688 | insertAction(index: 1, a: descriptionAction); |
689 | } |
690 | |
691 | } |
692 | |
693 | QT_END_NAMESPACE |
694 | |