actioncontainer.cpp 13.2 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
con's avatar
con committed
8
**
9
** Commercial Usage
10
**
11
12
13
14
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
15
**
16
** GNU Lesser General Public License Usage
17
**
18
19
20
21
22
23
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24
**
25
** If you are unsure which license is appropriate for your use, please
hjk's avatar
hjk committed
26
** contact the sales department at http://qt.nokia.com/contact.
con's avatar
con committed
27
**
28
**************************************************************************/
hjk's avatar
hjk committed
29

30
#include "actioncontainer_p.h"
31
32
#include "actionmanager_p.h"

con's avatar
con committed
33
#include "command_p.h"
con's avatar
con committed
34
35
36
37
38

#include "coreconstants.h"
#include "uniqueidmanager.h"

#include <QtCore/QDebug>
con's avatar
con committed
39
#include <QtCore/QTimer>
con's avatar
con committed
40
41
42
43
44
45
46
47
48
#include <QtGui/QAction>
#include <QtGui/QMenuBar>

Q_DECLARE_METATYPE(Core::Internal::MenuActionContainer*)

using namespace Core;
using namespace Core::Internal;

/*!
49
    \class ActionContainer
con's avatar
con committed
50
51
    \mainclass

52
    \brief The ActionContainer class represents a menu or menu bar in Qt Creator.
con's avatar
con committed
53

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
    You don't create instances of this class directly, but instead use the
    \l{ActionManager::createMenu()}
    and \l{ActionManager::createMenuBar()} methods.
    Retrieve existing action containers for an ID with
    \l{ActionManager::actionContainer()}.

    Within a menu or menu bar you can group menus and items together by defining groups
    (the order of the groups is defined by the order of the \l{ActionContainer::appendGroup()} calls), and
    adding menus/actions to these groups. If no custom groups are defined, an action container
    has three default groups \c{Core::Constants::G_DEFAULT_ONE}, \c{Core::Constants::G_DEFAULT_TWO}
    and \c{Core::Constants::G_DEFAULT_THREE}.

    You can define if the menu represented by this action container should automatically disable
    or hide whenever it only contains disabled items and submenus by setting the corresponding
    \l{ActionContainer::setEmptyAction()}{EmptyAction}.
con's avatar
con committed
69
70
71
*/

/*!
72
    \enum ActionContainer::EmptyAction
73
74
75
76
77
78
79
80
    Defines what happens when the represented menu is empty or contains only disabled/invisible items.
    \omitvalue EA_Mask
    \value EA_None
        The menu will still be visible and active.
    \value EA_Disable
        The menu will be visible but disabled.
    \value EA_Hide
        The menu will not be visible until the state of the subitems change.
con's avatar
con committed
81
82
83
*/

/*!
84
85
86
87
    \fn ActionContainer::setEmptyAction(EmptyAction disableOrHide)
    Defines if the menu represented by this action container should automatically \a disableOrHide
    whenever it only contains disabled items and submenus.
    \sa ActionContainer::EmptyAction
con's avatar
con committed
88
89
90
*/

/*!
91
92
    \fn int ActionContainer::id() const
    \internal
con's avatar
con committed
93
94
95
*/

/*!
96
97
98
    \fn QMenu *ActionContainer::menu() const
    Returns the QMenu instance that is represented by this action container, or
    0 if this action container represents a menu bar.
con's avatar
con committed
99
100
101
*/

/*!
102
103
104
    \fn QMenuBar *ActionContainer::menuBar() const
    Returns the QMenuBar instance that is represented by this action container, or
    0 if this action container represents a menu.
con's avatar
con committed
105
106
107
*/

/*!
108
109
110
    \fn QAction *ActionContainer::insertLocation(const QString &group) const
    Returns an action representing the \a group,
    that could be used with \c{QWidget::insertAction}.
con's avatar
con committed
111
112
113
*/

/*!
114
115
116
117
118
119
    \fn void ActionContainer::appendGroup(const QString &identifier)
    Adds a group with the given \a identifier to the action container. Using groups
    you can segment your action container into logical parts and add actions and
    menus directly to these parts.
    \sa addAction()
    \sa addMenu()
con's avatar
con committed
120
121
122
*/

/*!
123
124
125
126
127
    \fn void ActionContainer::addAction(Core::Command *action, const QString &group)
    Add the \a action as a menu item to this action container. The action is added as the
    last item of the specified \a group.
    \sa appendGroup()
    \sa addMenu()
con's avatar
con committed
128
129
130
*/

/*!
131
132
133
134
135
    \fn void ActionContainer::addMenu(Core::ActionContainer *menu, const QString &group)
    Add the \a menu as a submenu to this action container. The menu is added as the
    last item of the specified \a group.
    \sa appendGroup()
    \sa addAction()
con's avatar
con committed
136
137
138
*/

/*!
139
140
    \fn bool ActionContainer::update()
    \internal
con's avatar
con committed
141
142
143
*/

/*!
144
145
    \fn ActionContainer::~ActionContainer()
    \internal
con's avatar
con committed
146
147
*/

148
// ---------- ActionContainerPrivate ------------
con's avatar
con committed
149
150

/*!
151
    \class Core::Internal::ActionContainerPrivate
152
    \internal
con's avatar
con committed
153
154
*/

155
ActionContainerPrivate::ActionContainerPrivate(int id)
con's avatar
con committed
156
    : m_data(0), m_id(id), m_updateRequested(false)
con's avatar
con committed
157
158
159
160
{

}

161
void ActionContainerPrivate::setEmptyAction(EmptyAction ea)
con's avatar
con committed
162
163
164
165
{
    m_data = ((m_data & ~EA_Mask) | ea);
}

166
bool ActionContainerPrivate::hasEmptyAction(EmptyAction ea) const
con's avatar
con committed
167
168
169
170
{
    return (m_data & EA_Mask) == ea;
}

171
void ActionContainerPrivate::appendGroup(const QString &group)
con's avatar
con committed
172
{
173
    int gid = UniqueIDManager::instance()->uniqueIdentifier(group);
con's avatar
con committed
174
175
176
    m_groups << gid;
}

177
QAction *ActionContainerPrivate::insertLocation(const QString &group) const
con's avatar
con committed
178
{
179
    int grpid = UniqueIDManager::instance()->uniqueIdentifier(group);
con's avatar
con committed
180
181
182
183
184
    int prevKey = 0;
    int pos = ((grpid << 16) | 0xFFFF);
    return beforeAction(pos, &prevKey);
}

con's avatar
con committed
185
void ActionContainerPrivate::addAction(Command *action, const QString &group)
con's avatar
con committed
186
187
188
189
{
    if (!canAddAction(action))
        return;

190
    ActionManagerPrivate *am = ActionManagerPrivate::instance();
con's avatar
con committed
191
192
193
194
195
196
197
198
    UniqueIDManager *idmanager = UniqueIDManager::instance();
    int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO);
    if (!group.isEmpty())
        grpid = idmanager->uniqueIdentifier(group);
    if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid))
        qWarning() << "*** addAction(): Unknown group: " << group;
    int pos = ((grpid << 16) | 0xFFFF);
    addAction(action, pos, true);
con's avatar
con committed
199
200
}

201
void ActionContainerPrivate::addMenu(ActionContainer *menu, const QString &group)
con's avatar
con committed
202
{
203
    ActionContainerPrivate *container = static_cast<ActionContainerPrivate *>(menu);
204
    if (!container->canBeAddedToMenu())
con's avatar
con committed
205
206
        return;

207
    ActionManagerPrivate *am = ActionManagerPrivate::instance();
con's avatar
con committed
208
209
210
211
212
213
214
215
    UniqueIDManager *idmanager = UniqueIDManager::instance();
    int grpid = idmanager->uniqueIdentifier(Constants::G_DEFAULT_TWO);
    if (!group.isEmpty())
        grpid = idmanager->uniqueIdentifier(group);
    if (!m_groups.contains(grpid) && !am->defaultGroups().contains(grpid))
        qWarning() << "*** addMenu(): Unknown group: " << group;
    int pos = ((grpid << 16) | 0xFFFF);
    addMenu(menu, pos, true);
con's avatar
con committed
216
217
}

218
int ActionContainerPrivate::id() const
con's avatar
con committed
219
220
221
222
{
    return m_id;
}

223
QMenu *ActionContainerPrivate::menu() const
con's avatar
con committed
224
225
226
227
{
    return 0;
}

228
QMenuBar *ActionContainerPrivate::menuBar() const
con's avatar
con committed
229
230
231
232
{
    return 0;
}

con's avatar
con committed
233
bool ActionContainerPrivate::canAddAction(Command *action) const
con's avatar
con committed
234
{
con's avatar
con committed
235
    return (action->action() != 0);
con's avatar
con committed
236
237
}

con's avatar
con committed
238
void ActionContainerPrivate::addAction(Command *action, int pos, bool setpos)
con's avatar
con committed
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
{
    Action *a = static_cast<Action *>(action);

    int prevKey = 0;
    QAction *ba = beforeAction(pos, &prevKey);

    if (setpos) {
        pos = calcPosition(pos, prevKey);
        CommandLocation loc;
        loc.m_container = m_id;
        loc.m_position = pos;
        QList<CommandLocation> locs = a->locations();
        locs.append(loc);
        a->setLocations(locs);
    }

    m_commands.append(action);
    m_posmap.insert(pos, action->id());
con's avatar
con committed
257
    connect(action, SIGNAL(activeStateChanged()), this, SLOT(scheduleUpdate()));
con's avatar
con committed
258
259
260
    insertAction(ba, a->action());
}

261
void ActionContainerPrivate::addMenu(ActionContainer *menu, int pos, bool setpos)
con's avatar
con committed
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
{
    MenuActionContainer *mc = static_cast<MenuActionContainer *>(menu);

    int prevKey = 0;
    QAction *ba = beforeAction(pos, &prevKey);

    if (setpos) {
        pos = calcPosition(pos, prevKey);
        CommandLocation loc;
        loc.m_container = m_id;
        loc.m_position = pos;
        mc->setLocation(loc);
    }

    m_subContainers.append(menu);
    m_posmap.insert(pos, menu->id());
    insertMenu(ba, mc->menu());
}

281
QAction *ActionContainerPrivate::beforeAction(int pos, int *prevKey) const
con's avatar
con committed
282
{
283
    ActionManagerPrivate *am = ActionManagerPrivate::instance();
con's avatar
con committed
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

    int baId = -1;

    (*prevKey) = -1;

    QMap<int, int>::const_iterator i = m_posmap.constBegin();
    while (i != m_posmap.constEnd()) {
        if (i.key() > pos) {
            baId = i.value();
            break;
        }
        (*prevKey) = i.key();
        ++i;
    }

    if (baId == -1)
        return 0;

con's avatar
con committed
302
    if (Command *cmd = am->command(baId))
con's avatar
con committed
303
        return cmd->action();
304
    if (ActionContainer *container = am->actionContainer(baId))
con's avatar
con committed
305
306
307
308
309
310
        if (QMenu *menu = container->menu())
            return menu->menuAction();

    return 0;
}

311
int ActionContainerPrivate::calcPosition(int pos, int prevKey) const
con's avatar
con committed
312
313
314
315
316
317
318
319
320
321
322
323
324
{
    int grp = (pos & 0xFFFF0000);
    if (prevKey == -1)
        return grp;

    int prevgrp = (prevKey & 0xFFFF0000);

    if (grp != prevgrp)
        return grp;

    return grp + (prevKey & 0xFFFF) + 10;
}

con's avatar
con committed
325
326
327
328
329
330
331
332
333
334
335
336
337
338
void ActionContainerPrivate::scheduleUpdate()
{
    if (m_updateRequested)
        return;
    m_updateRequested = true;
    QTimer::singleShot(0, this, SLOT(update()));
}

void ActionContainerPrivate::update()
{
    updateInternal();
    m_updateRequested = false;
}

con's avatar
con committed
339
340
341
// ---------- MenuActionContainer ------------

/*!
342
343
    \class Core::Internal::MenuActionContainer
    \internal
con's avatar
con committed
344
345
346
*/

MenuActionContainer::MenuActionContainer(int id)
347
    : ActionContainerPrivate(id), m_menu(0)
con's avatar
con committed
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
{
    setEmptyAction(EA_Disable);
}

void MenuActionContainer::setMenu(QMenu *menu)
{
    m_menu = menu;

    QVariant v;
    qVariantSetValue<MenuActionContainer*>(v, this);

    m_menu->menuAction()->setData(v);
}

QMenu *MenuActionContainer::menu() const
{
    return m_menu;
}

void MenuActionContainer::insertAction(QAction *before, QAction *action)
{
    m_menu->insertAction(before, action);
}

void MenuActionContainer::insertMenu(QAction *before, QMenu *menu)
{
    m_menu->insertMenu(before, menu);
}

void MenuActionContainer::setLocation(const CommandLocation &location)
{
    m_location = location;
}

CommandLocation MenuActionContainer::location() const
{
    return m_location;
}

con's avatar
con committed
387
bool MenuActionContainer::updateInternal()
con's avatar
con committed
388
389
390
391
392
{
    if (hasEmptyAction(EA_None))
        return true;

    bool hasitems = false;
393
    QList<QAction *> actions = m_menu->actions();
con's avatar
con committed
394

395
    foreach (ActionContainer *container, subContainers()) {
396
        actions.removeAll(container->menu()->menuAction());
con's avatar
con committed
397
398
399
400
        if (container == this) {
            qWarning() << Q_FUNC_INFO << "container" << (this->menu() ? this->menu()->title() : "") <<  "contains itself as subcontainer";
            continue;
        }
con's avatar
con committed
401
        if (qobject_cast<ActionContainerPrivate*>(container)->updateInternal()) {
con's avatar
con committed
402
403
404
405
406
            hasitems = true;
            break;
        }
    }
    if (!hasitems) {
con's avatar
con committed
407
        foreach (Command *command, commands()) {
408
            actions.removeAll(command->action());
con's avatar
con committed
409
410
411
412
413
414
            if (command->isActive()) {
                hasitems = true;
                break;
            }
        }
    }
415
416
417
418
419
420
421
422
423
    if (!hasitems) {
        // look if there were actions added that we don't control and check if they are enabled
        foreach (const QAction *action, actions) {
            if (!action->isSeparator() && action->isEnabled()) {
                hasitems = true;
                break;
            }
        }
    }
con's avatar
con committed
424
425
426
427
428
429
430
431
432

    if (hasEmptyAction(EA_Hide))
        m_menu->setVisible(hasitems);
    else if (hasEmptyAction(EA_Disable))
        m_menu->setEnabled(hasitems);

    return hasitems;
}

433
bool MenuActionContainer::canBeAddedToMenu() const
con's avatar
con committed
434
{
435
    return true;
con's avatar
con committed
436
437
438
439
440
441
}


// ---------- MenuBarActionContainer ------------

/*!
442
443
    \class Core::Internal::MenuBarActionContainer
    \internal
con's avatar
con committed
444
445
446
*/

MenuBarActionContainer::MenuBarActionContainer(int id)
447
    : ActionContainerPrivate(id), m_menuBar(0)
con's avatar
con committed
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
{
    setEmptyAction(EA_None);
}

void MenuBarActionContainer::setMenuBar(QMenuBar *menuBar)
{
    m_menuBar = menuBar;
}

QMenuBar *MenuBarActionContainer::menuBar() const
{
    return m_menuBar;
}

void MenuBarActionContainer::insertAction(QAction *before, QAction *action)
{
    m_menuBar->insertAction(before, action);
}

void MenuBarActionContainer::insertMenu(QAction *before, QMenu *menu)
{
    m_menuBar->insertMenu(before, menu);
}

con's avatar
con committed
472
bool MenuBarActionContainer::updateInternal()
con's avatar
con committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
{
    if (hasEmptyAction(EA_None))
        return true;

    bool hasitems = false;
    QList<QAction *> actions = m_menuBar->actions();
    for (int i=0; i<actions.size(); ++i) {
        if (actions.at(i)->isVisible()) {
            hasitems = true;
            break;
        }
    }

    if (hasEmptyAction(EA_Hide))
        m_menuBar->setVisible(hasitems);
    else if (hasEmptyAction(EA_Disable))
        m_menuBar->setEnabled(hasitems);

    return hasitems;
}
493
494
495
496
497
498

bool MenuBarActionContainer::canBeAddedToMenu() const
{
    return false;
}