sidebar.cpp 13.3 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

con's avatar
con committed
30
31
#include "sidebar.h"
#include "imode.h"
hjk's avatar
hjk committed
32

33
#include "actionmanager/actionmanager.h"
34
#include "actionmanager/command.h"
con's avatar
con committed
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

#include <QtCore/QEvent>
#include <QtCore/QSettings>
#include <QtGui/QLayout>
#include <QtGui/QToolBar>
#include <QtGui/QAction>
#include <QtGui/QToolButton>

using namespace Core;
using namespace Core::Internal;

SideBarItem::~SideBarItem()
{
    delete m_widget;
}

SideBar::SideBar(QList<SideBarItem*> itemList,
52
53
                 QList<SideBarItem*> defaultVisible) :
    m_closeWhenEmpty(false)
con's avatar
con committed
54
55
56
{
    setOrientation(Qt::Vertical);
    foreach (SideBarItem *item, itemList) {
57
58
59
        m_itemMap.insert(item->id(), item);
        m_availableItemIds.append(item->id());
        m_availableItemTitles.append(item->title());
60
    }
61

62
63
64
    foreach (SideBarItem *item, defaultVisible) {
        if (!itemList.contains(item))
            continue;
65
        m_defaultVisible.append(item->id());
con's avatar
con committed
66
67
68
69
70
    }
}

SideBar::~SideBar()
{
71
72
73
74
75
76
    QMutableMapIterator<QString, QWeakPointer<SideBarItem> > iter(m_itemMap);
    while(iter.hasNext()) {
        iter.next();
        if (!iter.value().isNull())
            delete iter.value().data();
    }
con's avatar
con committed
77
78
}

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
QString SideBar::idForTitle(const QString &title) const
{
    QMapIterator<QString, QWeakPointer<SideBarItem> > iter(m_itemMap);
    while(iter.hasNext()) {
        iter.next();
        if (iter.value().data()->title() == title)
            return iter.key();
    }
    return QString();
}

QStringList SideBar::availableItemIds() const
{
    return m_availableItemIds;
}

QStringList SideBar::availableItemTitles() const
con's avatar
con committed
96
{
97
    return m_availableItemTitles;
con's avatar
con committed
98
99
}

100
QStringList SideBar::unavailableItemIds() const
101
{
102
    return m_unavailableItemIds;
103
104
105
106
107
108
109
110
111
112
113
}

bool SideBar::closeWhenEmpty() const
{
    return m_closeWhenEmpty;
}
void SideBar::setCloseWhenEmpty(bool value)
{
    m_closeWhenEmpty = value;
}

con's avatar
con committed
114
115
void SideBar::makeItemAvailable(SideBarItem *item)
{
116
    QMap<QString, QWeakPointer<SideBarItem> >::const_iterator it = m_itemMap.constBegin();
con's avatar
con committed
117
    while (it != m_itemMap.constEnd()) {
118
        if (it.value().data() == item) {
119
120
121
122
            m_availableItemIds.append(it.key());
            m_availableItemTitles.append(it.value().data()->title());
            m_unavailableItemIds.removeAll(it.key());
            qSort(m_availableItemTitles);
123
124
            emit availableItemsChanged();
            //updateWidgets();
con's avatar
con committed
125
126
127
128
129
130
            break;
        }
        ++it;
    }
}

131
132
// sets a list of externally used, unavailable items. For example,
// another sidebar could set
133
void SideBar::setUnavailableItemIds(const QStringList &itemIds)
134
135
{
    // re-enable previous items
136
137
138
139
    foreach(const QString &id, m_unavailableItemIds) {
        m_availableItemIds.append(id);
        m_availableItemTitles.append(m_itemMap.value(id).data()->title());
    }
140

141
    m_unavailableItemIds.clear();
142

143
144
145
146
147
    foreach (const QString &id, itemIds) {
        if (!m_unavailableItemIds.contains(id))
            m_unavailableItemIds.append(id);
        m_availableItemIds.removeAll(id);
        m_availableItemTitles.removeAll(m_itemMap.value(id).data()->title());
148
    }
149
    qSort(m_availableItemTitles);
150
151
152
    updateWidgets();
}

153
SideBarItem *SideBar::item(const QString &id)
con's avatar
con committed
154
{
155
156
157
    if (m_itemMap.contains(id)) {
        m_availableItemIds.removeAll(id);
        m_availableItemTitles.removeAll(m_itemMap.value(id).data()->title());
158

159
160
        if (!m_unavailableItemIds.contains(id))
            m_unavailableItemIds.append(id);
161
162

        emit availableItemsChanged();
163
        return m_itemMap.value(id).data();
con's avatar
con committed
164
165
166
167
    }
    return 0;
}

168
SideBarWidget *SideBar::insertSideBarWidget(int position, const QString &id)
con's avatar
con committed
169
{
170
    SideBarWidget *item = new SideBarWidget(this, id);
171
172
    connect(item, SIGNAL(splitMe()), this, SLOT(splitSubWidget()));
    connect(item, SIGNAL(closeMe()), this, SLOT(closeSubWidget()));
con's avatar
con committed
173
174
175
176
177
178
179
    connect(item, SIGNAL(currentWidgetChanged()), this, SLOT(updateWidgets()));
    insertWidget(position, item);
    m_widgets.insert(position, item);
    updateWidgets();
    return item;
}

180
181
182
183
184
185
186
187
void SideBar::removeSideBarWidget(SideBarWidget *widget)
{
    widget->removeCurrentItem();
    m_widgets.removeOne(widget);
    widget->hide();
    widget->deleteLater();
}

188
void SideBar::splitSubWidget()
con's avatar
con committed
189
190
191
192
193
194
195
{
    SideBarWidget *original = qobject_cast<SideBarWidget*>(sender());
    int pos = indexOf(original) + 1;
    insertSideBarWidget(pos);
    updateWidgets();
}

196
void SideBar::closeSubWidget()
con's avatar
con committed
197
198
199
200
201
{
    if (m_widgets.count() != 1) {
        SideBarWidget *widget = qobject_cast<SideBarWidget*>(sender());
        if (!widget)
            return;
202
        removeSideBarWidget(widget);
con's avatar
con committed
203
        updateWidgets();
204
205
206
    } else {
        if (m_closeWhenEmpty)
            setVisible(false);
con's avatar
con committed
207
208
209
210
211
212
213
214
215
    }
}

void SideBar::updateWidgets()
{
    foreach (SideBarWidget *i, m_widgets)
        i->updateAvailableItems();
}

216
void SideBar::saveSettings(QSettings *settings, const QString &name)
con's avatar
con committed
217
{
218
219
    const QString prefix = name.isEmpty() ? name : (name + QLatin1Char('/'));

con's avatar
con committed
220
    QStringList views;
221
222
223
224
225
226
227
228
229
230
    for (int i = 0; i < m_widgets.count(); ++i) {
        QString currentItemId = m_widgets.at(i)->currentItemId();
        if (!currentItemId.isEmpty())
            views.append(currentItemId);
    }
    if (views.isEmpty() && m_itemMap.size()) {
        QMapIterator<QString, QWeakPointer<SideBarItem> > iter(m_itemMap);
        iter.next();
        views.append(iter.key());
    }
231

232
    settings->setValue(prefix + "Views", views);
233
    settings->setValue(prefix + "Visible", true);
234
235
    settings->setValue(prefix + "VerticalPosition", saveState());
    settings->setValue(prefix + "Width", width());
con's avatar
con committed
236
237
}

238
239
240
241
242
243
void SideBar::closeAllWidgets()
{
    foreach (SideBarWidget *widget, m_widgets)
        removeSideBarWidget(widget);
}

244
void SideBar::readSettings(QSettings *settings, const QString &name)
con's avatar
con committed
245
{
246
247
    const QString prefix = name.isEmpty() ? name : (name + QLatin1Char('/'));

248
    closeAllWidgets();
249

250
251
    if (settings->contains(prefix + "Views")) {
        QStringList views = settings->value(prefix + "Views").toStringList();
con's avatar
con committed
252
        if (views.count()) {
253
254
            foreach (const QString &id, views)
                insertSideBarWidget(m_widgets.count(), id);
255

con's avatar
con committed
256
257
258
259
        } else {
            insertSideBarWidget(0);
        }
    } else {
260
261
        foreach (const QString &id, m_defaultVisible)
            insertSideBarWidget(m_widgets.count(), id);
con's avatar
con committed
262
263
    }

264
265
    if (settings->contains(prefix + "Visible"))
        setVisible(settings->value(prefix + "Visible").toBool());
con's avatar
con committed
266

267
268
    if (settings->contains(prefix + "VerticalPosition"))
        restoreState(settings->value(prefix + "VerticalPosition").toByteArray());
con's avatar
con committed
269

270
    if (settings->contains(prefix + "Width")) {
con's avatar
con committed
271
        QSize s = size();
272
        s.setWidth(settings->value(prefix + "Width").toInt());
con's avatar
con committed
273
274
275
276
277
278
        resize(s);
    }
}

void SideBar::activateItem(SideBarItem *item)
{
279
    QMap<QString, QWeakPointer<SideBarItem> >::const_iterator it = m_itemMap.constBegin();
280
    QString id;
con's avatar
con committed
281
    while (it != m_itemMap.constEnd()) {
282
        if (it.value().data() == item) {
283
            id = it.key();
con's avatar
con committed
284
285
286
287
288
            break;
        }
        ++it;
    }

289
    if (id.isEmpty())
con's avatar
con committed
290
291
292
        return;

    for (int i = 0; i < m_widgets.count(); ++i) {
293
        if (m_widgets.at(i)->currentItemId() == id) {
con's avatar
con committed
294
295
296
297
298
299
            item->widget()->setFocus();
            return;
        }
    }

    SideBarWidget *widget = m_widgets.first();
300
    widget->setCurrentItem(id);
con's avatar
con committed
301
302
303
304
    updateWidgets();
    item->widget()->setFocus();
}

con's avatar
con committed
305
void SideBar::setShortcutMap(const QMap<QString, Core::Command*> &shortcutMap)
con's avatar
con committed
306
307
308
309
{
    m_shortcutMap = shortcutMap;
}

con's avatar
con committed
310
QMap<QString, Core::Command*> SideBar::shortcutMap() const
con's avatar
con committed
311
312
313
314
{
    return m_shortcutMap;
}

315
SideBarWidget::SideBarWidget(SideBar *sideBar, const QString &id)
con's avatar
con committed
316
317
318
319
320
321
322
323
324
325
326
    : m_currentItem(0)
    , m_sideBar(sideBar)
{
    m_comboBox = new ComboBox(this);
    m_comboBox->setMinimumContentsLength(15);

    m_toolbar = new QToolBar(this);
    m_toolbar->setContentsMargins(0, 0, 0, 0);
    m_toolbar->addWidget(m_comboBox);

    m_splitButton = new QToolButton;
327
    m_splitButton->setIcon(QIcon(":/core/images/splitbutton_horizontal.png"));
con's avatar
con committed
328
    m_splitButton->setToolTip(tr("Split"));
329
    connect(m_splitButton, SIGNAL(clicked(bool)), this, SIGNAL(splitMe()));
con's avatar
con committed
330
331

    m_closeButton = new QToolButton;
332
    m_closeButton->setIcon(QIcon(":/core/images/closebutton.png"));
con's avatar
con committed
333
334
    m_closeButton->setToolTip(tr("Close"));

335
    connect(m_closeButton, SIGNAL(clicked(bool)), this, SIGNAL(closeMe()));
con's avatar
con committed
336
337
338
339
340
341
342
343
344
345
346
347
348

    QWidget *spacerItem = new QWidget(this);
    spacerItem->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
    m_toolbar->addWidget(spacerItem);
    m_splitAction = m_toolbar->addWidget(m_splitButton);
    m_toolbar->addWidget(m_closeButton);

    QVBoxLayout *lay = new QVBoxLayout();
    lay->setMargin(0);
    lay->setSpacing(0);
    setLayout(lay);
    lay->addWidget(m_toolbar);

349
350
351
352
353
354
355
    QStringList titleList = m_sideBar->availableItemTitles();
    qSort(titleList);
    QString t = id;
    if (titleList.count()) {
        foreach(const QString &itemTitle, titleList)
            m_comboBox->addItem(itemTitle, m_sideBar->idForTitle(itemTitle));

con's avatar
con committed
356
357
        m_comboBox->setCurrentIndex(0);
        if (t.isEmpty())
358
            t = m_comboBox->itemData(0, ComboBox::IdRole).toString();
con's avatar
con committed
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    }
    setCurrentItem(t);

    connect(m_comboBox, SIGNAL(currentIndexChanged(int)),
            this, SLOT(setCurrentIndex(int)));
}

SideBarWidget::~SideBarWidget()
{
}

QString SideBarWidget::currentItemTitle() const
{
    return m_comboBox->currentText();
}

375
376
QString SideBarWidget::currentItemId() const
{
377
378
379
    if (m_currentItem)
        return m_currentItem->id();
    return QString();
380
381
382
}

void SideBarWidget::setCurrentItem(const QString &id)
con's avatar
con committed
383
{
384
385
386
    if (!id.isEmpty()) {
        int idx = m_comboBox->findData(QVariant(id), ComboBox::IdRole);

con's avatar
con committed
387
388
        if (idx < 0)
            idx = 0;
389

390
        bool blocked = m_comboBox->blockSignals(true);
con's avatar
con committed
391
        m_comboBox->setCurrentIndex(idx);
392
        m_comboBox->blockSignals(blocked);
con's avatar
con committed
393
394
    }

395
    SideBarItem *item = m_sideBar->item(id);
con's avatar
con committed
396
397
398
399
    if (!item)
        return;
    removeCurrentItem();
    m_currentItem = item;
400

con's avatar
con committed
401
402
403
404
405
406
407
408
409
    layout()->addWidget(m_currentItem->widget());

    // Add buttons and remember their actions for later removal
    foreach (QToolButton *b, m_currentItem->createToolBarWidgets())
        m_addedToolBarActions.append(m_toolbar->insertWidget(m_splitAction, b));
}

void SideBarWidget::updateAvailableItems()
{
410
    bool blocked = m_comboBox->blockSignals(true);
411
    QString currentTitle = m_comboBox->currentText();
con's avatar
con committed
412
    m_comboBox->clear();
413
414
415
416
417
418
419
420
421
422
    QStringList titleList = m_sideBar->availableItemTitles();
    if (!currentTitle.isEmpty() && !titleList.contains(currentTitle))
        titleList.append(currentTitle);
    qSort(titleList);

    foreach(const QString &itemTitle, titleList)
        m_comboBox->addItem(itemTitle, m_sideBar->idForTitle(itemTitle));

    int idx = m_comboBox->findText(currentTitle);

con's avatar
con committed
423
424
    if (idx < 0)
        idx = 0;
425

con's avatar
con committed
426
    m_comboBox->setCurrentIndex(idx);
427
    m_splitButton->setEnabled(titleList.count() > 1);
428
    m_comboBox->blockSignals(blocked);
con's avatar
con committed
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
}

void SideBarWidget::removeCurrentItem()
{
    if (!m_currentItem)
        return;

    QWidget *w = m_currentItem->widget();
    layout()->removeWidget(w);
    w->setParent(0);
    m_sideBar->makeItemAvailable(m_currentItem);

    // Delete custom toolbar widgets
    qDeleteAll(m_addedToolBarActions);
    m_addedToolBarActions.clear();

    m_currentItem = 0;
}

void SideBarWidget::setCurrentIndex(int)
{
450
451
    setCurrentItem(m_comboBox->itemData(m_comboBox->currentIndex(),
                                        ComboBox::IdRole).toString());
con's avatar
con committed
452
453
454
    emit currentWidgetChanged();
}

455
Core::Command *SideBarWidget::command(const QString &id) const
con's avatar
con committed
456
{
con's avatar
con committed
457
    const QMap<QString, Core::Command*> shortcutMap = m_sideBar->shortcutMap();
458
    QMap<QString, Core::Command*>::const_iterator r = shortcutMap.find(id);
con's avatar
con committed
459
460
461
462
463
464
465
466
467
468
469
470
471
472
    if (r != shortcutMap.end())
        return r.value();
    return 0;
}

ComboBox::ComboBox(SideBarWidget *sideBarWidget)
    : m_sideBarWidget(sideBarWidget)
{
}

bool ComboBox::event(QEvent *e)
{
    if (e->type() == QEvent::ToolTip) {
        QString txt = currentText();
con's avatar
con committed
473
        Core::Command *cmd = m_sideBarWidget->command(txt);
con's avatar
con committed
474
475
476
477
478
479
480
481
482
        if (cmd) {
            txt = tr("Activate %1").arg(txt);
            setToolTip(cmd->stringWithAppendedShortcut(txt));
        } else {
            setToolTip(txt);
        }
    }
    return QComboBox::event(e);
}