source: trunk/tools/designer/src/lib/shared/qdesigner_toolbar.cpp@ 769

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 16.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the Qt Designer of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qdesigner_toolbar_p.h"
43#include "qdesigner_command_p.h"
44#include "actionrepository_p.h"
45#include "actionprovider_p.h"
46#include "qdesigner_utils_p.h"
47#include "qdesigner_objectinspector_p.h"
48#include "promotiontaskmenu_p.h"
49
50#include <QtDesigner/QDesignerFormWindowInterface>
51#include <QtDesigner/QDesignerPropertyEditorInterface>
52#include <QtDesigner/QDesignerFormEditorInterface>
53#include <actionprovider_p.h>
54#include <QtDesigner/QExtensionManager>
55#include <QtDesigner/QDesignerWidgetFactoryInterface>
56
57#include <QtGui/QAction>
58#include <QtGui/QApplication>
59#include <QtGui/QToolButton>
60#include <QtGui/QToolBar>
61#include <QtGui/QMenu>
62#include <QtGui/qevent.h>
63#include <QtGui/QApplication>
64#include <QtCore/QDebug>
65
66Q_DECLARE_METATYPE(QAction*)
67
68QT_BEGIN_NAMESPACE
69
70typedef QList<QAction*> ActionList;
71
72namespace qdesigner_internal {
73// ------------------- ToolBarEventFilter
74void ToolBarEventFilter::install(QToolBar *tb)
75{
76 ToolBarEventFilter *tf = new ToolBarEventFilter(tb);
77 tb->installEventFilter(tf);
78 tb->setAcceptDrops(true); // ### fake
79}
80
81ToolBarEventFilter::ToolBarEventFilter(QToolBar *tb) :
82 QObject(tb),
83 m_toolBar(tb),
84 m_promotionTaskMenu(0)
85{
86}
87
88ToolBarEventFilter *ToolBarEventFilter::eventFilterOf(const QToolBar *tb)
89{
90 // Look for 1st order children only..otherwise, we might get filters of nested widgets
91 const QObjectList children = tb->children();
92 const QObjectList::const_iterator cend = children.constEnd();
93 for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) {
94 QObject *o = *it;
95 if (!o->isWidgetType())
96 if (ToolBarEventFilter *ef = qobject_cast<ToolBarEventFilter *>(o))
97 return ef;
98 }
99 return 0;
100}
101
102bool ToolBarEventFilter::eventFilter (QObject *watched, QEvent *event)
103{
104 if (watched != m_toolBar)
105 return QObject::eventFilter (watched, event);
106
107 switch (event->type()) {
108 case QEvent::ChildAdded: {
109 // Children should not interact with the mouse
110 const QChildEvent *ce = static_cast<const QChildEvent *>(event);
111 if (QWidget *w = qobject_cast<QWidget *>(ce->child())) {
112 w->setAttribute(Qt::WA_TransparentForMouseEvents, true);
113 w->setFocusPolicy(Qt::NoFocus);
114 }
115 }
116 break;
117 case QEvent::ContextMenu:
118 return handleContextMenuEvent(static_cast<QContextMenuEvent*>(event));
119#ifndef QT_NO_DRAGANDDROP
120 case QEvent::DragEnter:
121 case QEvent::DragMove:
122 return handleDragEnterMoveEvent(static_cast<QDragMoveEvent *>(event));
123 case QEvent::DragLeave:
124 return handleDragLeaveEvent(static_cast<QDragLeaveEvent *>(event));
125 case QEvent::Drop:
126 return handleDropEvent(static_cast<QDropEvent *>(event));
127#endif
128 case QEvent::MouseButtonPress:
129 return handleMousePressEvent(static_cast<QMouseEvent*>(event));
130 case QEvent::MouseButtonRelease:
131 return handleMouseReleaseEvent(static_cast<QMouseEvent*>(event));
132 case QEvent::MouseMove:
133 return handleMouseMoveEvent(static_cast<QMouseEvent*>(event));
134 default:
135 break;
136 }
137 return QObject::eventFilter (watched, event);
138}
139
140ActionList ToolBarEventFilter::contextMenuActions(const QPoint &globalPos)
141{
142 ActionList rc;
143 const int index = actionIndexAt(m_toolBar, m_toolBar->mapFromGlobal(globalPos), m_toolBar->orientation());
144 const ActionList actions = m_toolBar->actions();
145 QAction *action = index != -1 ?actions.at(index) : 0;
146 QVariant itemData;
147
148 // Insert before
149 if (action && index != 0 && !action->isSeparator()) {
150 QAction *newSeperatorAct = new QAction(tr("Insert Separator before '%1'").arg(action->objectName()), 0);
151 qVariantSetValue(itemData, action);
152 newSeperatorAct->setData(itemData);
153 connect(newSeperatorAct, SIGNAL(triggered()), this, SLOT(slotInsertSeparator()));
154 rc.push_back(newSeperatorAct);
155 }
156
157 // Append separator
158 if (actions.empty() || !actions.back()->isSeparator()) {
159 QAction *newSeperatorAct = new QAction(tr("Append Separator"), 0);
160 qVariantSetValue(itemData, static_cast<QAction*>(0));
161 newSeperatorAct->setData(itemData);
162 connect(newSeperatorAct, SIGNAL(triggered()), this, SLOT(slotInsertSeparator()));
163 rc.push_back(newSeperatorAct);
164 }
165 // Promotion
166 if (!m_promotionTaskMenu)
167 m_promotionTaskMenu = new PromotionTaskMenu(m_toolBar, PromotionTaskMenu::ModeSingleWidget, this);
168 m_promotionTaskMenu->addActions(formWindow(), PromotionTaskMenu::LeadingSeparator|PromotionTaskMenu::TrailingSeparator, rc);
169 // Remove
170 if (action) {
171 QAction *a = new QAction(tr("Remove action '%1'").arg(action->objectName()), 0);
172 qVariantSetValue(itemData, action);
173 a->setData(itemData);
174 connect(a, SIGNAL(triggered()), this, SLOT(slotRemoveSelectedAction()));
175 rc.push_back(a);
176 }
177
178 QAction *remove_toolbar = new QAction(tr("Remove Toolbar '%1'").arg(m_toolBar->objectName()), 0);
179 connect(remove_toolbar, SIGNAL(triggered()), this, SLOT(slotRemoveToolBar()));
180 rc.push_back(remove_toolbar);
181 return rc;
182}
183
184bool ToolBarEventFilter::handleContextMenuEvent(QContextMenuEvent * event )
185{
186 event->accept();
187
188 const QPoint globalPos = event->globalPos();
189 const ActionList al = contextMenuActions(event->globalPos());
190
191 QMenu menu(0);
192 const ActionList::const_iterator acend = al.constEnd();
193 for (ActionList::const_iterator it = al.constBegin(); it != acend; ++it)
194 menu.addAction(*it);
195 menu.exec(globalPos);
196 return true;
197}
198
199void ToolBarEventFilter::slotRemoveSelectedAction()
200{
201 QAction *action = qobject_cast<QAction*>(sender());
202 if (!action)
203 return;
204
205 QAction *a = qvariant_cast<QAction*>(action->data());
206 Q_ASSERT(a != 0);
207
208 QDesignerFormWindowInterface *fw = formWindow();
209 Q_ASSERT(fw);
210
211 const ActionList actions = m_toolBar->actions();
212 const int pos = actions.indexOf(a);
213 QAction *action_before = 0;
214 if (pos != -1 && actions.count() > pos + 1)
215 action_before = actions.at(pos + 1);
216
217 RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw);
218 cmd->init(m_toolBar, a, action_before);
219 fw->commandHistory()->push(cmd);
220}
221
222void ToolBarEventFilter::slotRemoveToolBar()
223{
224 QDesignerFormWindowInterface *fw = formWindow();
225 Q_ASSERT(fw);
226 DeleteToolBarCommand *cmd = new DeleteToolBarCommand(fw);
227 cmd->init(m_toolBar);
228 fw->commandHistory()->push(cmd);
229}
230
231void ToolBarEventFilter::slotInsertSeparator()
232{
233 QDesignerFormWindowInterface *fw = formWindow();
234 QAction *theSender = qobject_cast<QAction*>(sender());
235 QAction *previous = qvariant_cast<QAction *>(theSender->data());
236 fw->beginCommand(tr("Insert Separator"));
237 QAction *action = createAction(fw, QLatin1String("separator"), true);
238 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
239 cmd->init(m_toolBar, action, previous);
240 fw->commandHistory()->push(cmd);
241 fw->endCommand();
242}
243
244QDesignerFormWindowInterface *ToolBarEventFilter::formWindow() const
245{
246 return QDesignerFormWindowInterface::findFormWindow(m_toolBar);
247}
248
249QAction *ToolBarEventFilter::createAction(QDesignerFormWindowInterface *fw, const QString &objectName, bool separator)
250{
251 QAction *action = new QAction(fw);
252 fw->core()->widgetFactory()->initialize(action);
253 if (separator)
254 action->setSeparator(true);
255
256 action->setObjectName(objectName);
257 fw->ensureUniqueObjectName(action);
258
259 qdesigner_internal::AddActionCommand *cmd = new qdesigner_internal::AddActionCommand(fw);
260 cmd->init(action);
261 fw->commandHistory()->push(cmd);
262
263 return action;
264}
265
266void ToolBarEventFilter::adjustDragIndicator(const QPoint &pos)
267{
268 if (QDesignerFormWindowInterface *fw = formWindow()) {
269 QDesignerFormEditorInterface *core = fw->core();
270 if (QDesignerActionProviderExtension *a = qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(), m_toolBar))
271 a->adjustIndicator(pos);
272 }
273}
274
275void ToolBarEventFilter::hideDragIndicator()
276{
277 adjustDragIndicator(QPoint(-1, -1));
278}
279
280bool ToolBarEventFilter::handleMousePressEvent(QMouseEvent *event)
281{
282 if (event->button() != Qt::LeftButton || withinHandleArea(m_toolBar, event->pos()))
283 return false;
284
285 if (QDesignerFormWindowInterface *fw = formWindow()) {
286 QDesignerFormEditorInterface *core = fw->core();
287 // Keep selection in sync
288 fw->clearSelection(false);
289 if (QDesignerObjectInspector *oi = qobject_cast<QDesignerObjectInspector *>(core->objectInspector())) {
290 oi->clearSelection();
291 oi->selectObject(m_toolBar);
292 }
293 core->propertyEditor()->setObject(m_toolBar);
294 }
295 m_startPosition = m_toolBar->mapFromGlobal(event->globalPos());
296 event->accept();
297 return true;
298}
299
300bool ToolBarEventFilter::handleMouseReleaseEvent(QMouseEvent *event)
301{
302 if (event->button() != Qt::LeftButton || m_startPosition.isNull() || withinHandleArea(m_toolBar, event->pos()))
303 return false;
304
305 // Accept the event, otherwise, form window selection will trigger
306 m_startPosition = QPoint();
307 event->accept();
308 return true;
309}
310
311bool ToolBarEventFilter::handleMouseMoveEvent(QMouseEvent *event)
312{
313 if (m_startPosition.isNull() || withinHandleArea(m_toolBar, event->pos()))
314 return false;
315
316 const QPoint pos = m_toolBar->mapFromGlobal(event->globalPos());
317 if ((pos - m_startPosition).manhattanLength() > qApp->startDragDistance()) {
318 startDrag(m_startPosition, event->modifiers());
319 m_startPosition = QPoint();
320 event->accept();
321 return true;
322 }
323 return false;
324}
325
326#ifndef QT_NO_DRAGANDDROP
327bool ToolBarEventFilter::handleDragEnterMoveEvent(QDragMoveEvent *event)
328{
329 const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
330 if (!d)
331 return false;
332
333 if (d->actionList().isEmpty()) {
334 event->ignore();
335 hideDragIndicator();
336 return true;
337 }
338
339 QAction *action = d->actionList().first();
340 if (!action || action->menu() || m_toolBar->actions().contains(action) || !Utils::isObjectAncestorOf(formWindow()->mainContainer(), action)) {
341 event->ignore();
342 hideDragIndicator();
343 return true;
344 }
345
346 d->accept(event);
347 adjustDragIndicator(event->pos());
348 return true;
349}
350
351bool ToolBarEventFilter::handleDragLeaveEvent(QDragLeaveEvent *)
352{
353 hideDragIndicator();
354 return false;
355}
356
357bool ToolBarEventFilter::handleDropEvent(QDropEvent *event)
358{
359 const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData());
360 if (!d)
361 return false;
362
363 if (d->actionList().isEmpty()) {
364 event->ignore();
365 hideDragIndicator();
366 return true;
367 }
368
369 QAction *action = d->actionList().first();
370
371 const ActionList actions = m_toolBar->actions();
372 if (!action || actions.contains(action)) {
373 event->ignore();
374 hideDragIndicator();
375 return true;
376 }
377
378 // Try to find action to 'insert before'. Click on action or in free area, else ignore.
379 QAction *beforeAction = 0;
380 const QPoint pos = event->pos();
381 const int index = actionIndexAt(m_toolBar, pos, m_toolBar->orientation());
382 if (index != -1) {
383 beforeAction = actions.at(index);
384 } else {
385 if (!freeArea(m_toolBar).contains(pos)) {
386 event->ignore();
387 hideDragIndicator();
388 return true;
389 }
390 }
391
392 event->acceptProposedAction();
393 QDesignerFormWindowInterface *fw = formWindow();
394 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
395 cmd->init(m_toolBar, action, beforeAction);
396 fw->commandHistory()->push(cmd);
397 hideDragIndicator();
398 return true;
399}
400#endif
401
402void ToolBarEventFilter::startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers)
403{
404 const int index = actionIndexAt(m_toolBar, pos, m_toolBar->orientation());
405 if (index == - 1)
406 return;
407
408 const ActionList actions = m_toolBar->actions();
409 QAction *action = actions.at(index);
410 QDesignerFormWindowInterface *fw = formWindow();
411
412 const Qt::DropAction dropAction = (modifiers & Qt::ControlModifier) ? Qt::CopyAction : Qt::MoveAction;
413 if (dropAction == Qt::MoveAction) {
414 RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw);
415 const int nextIndex = index + 1;
416 QAction *nextAction = nextIndex < actions.size() ? actions.at(nextIndex) : 0;
417 cmd->init(m_toolBar, action, nextAction);
418 fw->commandHistory()->push(cmd);
419 }
420
421#ifndef QT_NO_DRAGANDDROP
422 QDrag *drag = new QDrag(m_toolBar);
423 drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap( action));
424 drag->setMimeData(new ActionRepositoryMimeData(action, dropAction));
425
426 if (drag->start(dropAction) == Qt::IgnoreAction) {
427#else
428 {
429#endif
430 hideDragIndicator();
431 if (dropAction == Qt::MoveAction) {
432 const ActionList currentActions = m_toolBar->actions();
433 QAction *previous = 0;
434 if (index >= 0 && index < currentActions.size())
435 previous = currentActions.at(index);
436 InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw);
437 cmd->init(m_toolBar, action, previous);
438 fw->commandHistory()->push(cmd);
439 }
440 }
441}
442
443QAction *ToolBarEventFilter::actionAt(const QToolBar *tb, const QPoint &pos)
444{
445 const int index = actionIndexAt(tb, pos, tb->orientation());
446 if (index == -1)
447 return 0;
448 return tb->actions().at(index);
449}
450
451//that's a trick to get access to the initStyleOption which is a protected member
452class FriendlyToolBar : public QToolBar {
453public:
454 friend class ToolBarEventFilter;
455};
456
457QRect ToolBarEventFilter::handleArea(const QToolBar *tb)
458{
459 QStyleOptionToolBar opt;
460 static_cast<const FriendlyToolBar*>(tb)->initStyleOption(&opt);
461 return tb->style()->subElementRect(QStyle::SE_ToolBarHandle, &opt, tb);
462}
463
464bool ToolBarEventFilter::withinHandleArea(const QToolBar *tb, const QPoint &pos)
465{
466 return handleArea(tb).contains(pos);
467}
468
469// Determine the free area behind the last action.
470QRect ToolBarEventFilter::freeArea(const QToolBar *tb)
471{
472 QRect rc = QRect(QPoint(0, 0), tb->size());
473 const ActionList actionList = tb->actions();
474 QRect exclusionRectangle = actionList.empty() ? handleArea(tb) : tb->actionGeometry(actionList.back());
475 switch (tb->orientation()) {
476 case Qt::Horizontal:
477 switch (tb->layoutDirection()) {
478 case Qt::LeftToRight:
479 rc.setX(exclusionRectangle.right() + 1);
480 break;
481 case Qt::RightToLeft:
482 rc.setRight(exclusionRectangle.x());
483 break;
484 }
485 break;
486 case Qt::Vertical:
487 rc.setY(exclusionRectangle.bottom() + 1);
488 break;
489 }
490 return rc;
491}
492
493}
494
495QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.