miniprojecttargetselector.cpp 26.4 KB
Newer Older
Tobias Hunger's avatar
Tobias Hunger committed
1 2 3 4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
Tobias Hunger's avatar
Tobias Hunger committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
Tobias Hunger's avatar
Tobias Hunger committed
8 9 10 11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12 13 14 15 16 17
** 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.
Tobias Hunger's avatar
Tobias Hunger committed
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21 22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23 24 25 26 27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28 29
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
Tobias Hunger's avatar
Tobias Hunger committed
30 31 32
**
**************************************************************************/

33
#include "miniprojecttargetselector.h"
dt's avatar
dt committed
34 35
#include "buildconfigurationmodel.h"
#include "runconfigurationmodel.h"
36
#include "target.h"
37 38 39 40 41

#include <utils/qtcassert.h>
#include <utils/styledbar.h>
#include <utils/stylehelper.h>

42
#include <coreplugin/ifile.h>
43
#include <coreplugin/icore.h>
con's avatar
con committed
44
#include <coreplugin/coreconstants.h>
45 46 47 48 49 50 51 52 53 54 55 56 57 58

#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/buildconfiguration.h>

#include <QtGui/QLayout>
#include <QtGui/QFormLayout>
#include <QtGui/QLabel>
#include <QtGui/QComboBox>
#include <QtGui/QListWidget>
#include <QtGui/QStatusBar>
#include <QtGui/QStackedWidget>
Tobias Hunger's avatar
Tobias Hunger committed
59
#include <QtGui/QKeyEvent>
con's avatar
con committed
60
#include <QtGui/QPainter>
61
#include <QtGui/QAction>
62
#include <QtGui/QItemDelegate>
63
#include <QtGui/QMainWindow>
64

Tobias Hunger's avatar
Tobias Hunger committed
65
#include <QtGui/QApplication>
66

67 68 69 70 71 72 73
static QIcon createCenteredIcon(const QIcon &icon, const QIcon &overlay)
{
    QPixmap targetPixmap;
    targetPixmap = QPixmap(Core::Constants::TARGET_ICON_SIZE, Core::Constants::TARGET_ICON_SIZE);
    targetPixmap.fill(Qt::transparent);
    QPainter painter(&targetPixmap);

74 75 76
    QPixmap pixmap = icon.pixmap(Core::Constants::TARGET_ICON_SIZE);
    painter.drawPixmap((Core::Constants::TARGET_ICON_SIZE - pixmap.width())/2,
                       (Core::Constants::TARGET_ICON_SIZE - pixmap.height())/2, pixmap);
77
    if (!overlay.isNull()) {
78 79 80
        pixmap = overlay.pixmap(Core::Constants::TARGET_ICON_SIZE);
        painter.drawPixmap((Core::Constants::TARGET_ICON_SIZE - pixmap.width())/2,
                           (Core::Constants::TARGET_ICON_SIZE - pixmap.height())/2, pixmap);
81 82 83 84
    }
    return QIcon(targetPixmap);
}

85 86 87
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
class TargetSelectorDelegate : public QItemDelegate
{
public:
    TargetSelectorDelegate(QObject *parent) : QItemDelegate(parent) { }
private:
    void paint(QPainter *painter,
               const QStyleOptionViewItem &option,
               const QModelIndex &index) const;
    mutable QImage selectionGradient;
};

void TargetSelectorDelegate::paint(QPainter *painter,
                                   const QStyleOptionViewItem &option,
                                   const QModelIndex &) const
{
    painter->save();
    painter->setClipping(false);

    if (selectionGradient.isNull())
        selectionGradient.load(QLatin1String(":/projectexplorer/images/targetpanel_gradient.png"));

    if (option.state & QStyle::State_Selected) {
110 111 112 113
        QColor color =(option.state & QStyle::State_HasFocus) ?
                      option.palette.highlight().color() :
                      option.palette.dark().color();
        painter->fillRect(option.rect, color.darker(140));
114 115 116 117 118 119 120 121 122 123 124
        Utils::StyleHelper::drawCornerImage(selectionGradient, painter, option.rect.adjusted(0, 0, 0, -1), 5, 5, 5, 5);
        painter->setPen(QColor(255, 255, 255, 60));
        painter->drawLine(option.rect.topLeft(), option.rect.topRight());
        painter->setPen(QColor(255, 255, 255, 30));
        painter->drawLine(option.rect.bottomLeft() - QPoint(0,1), option.rect.bottomRight() -  QPoint(0,1));
        painter->setPen(QColor(0, 0, 0, 80));
        painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
    }
    painter->restore();
}

Tobias Hunger's avatar
Tobias Hunger committed
125 126 127 128 129 130 131
ProjectListWidget::ProjectListWidget(ProjectExplorer::Project *project, QWidget *parent)
    : QListWidget(parent), m_project(project)
{
    setFocusPolicy(Qt::NoFocus);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setAlternatingRowColors(false);
    setFocusPolicy(Qt::WheelFocus);
132
    setItemDelegate(new TargetSelectorDelegate(this));
133
    setAttribute(Qt::WA_MacShowFocusRect, false);
Tobias Hunger's avatar
Tobias Hunger committed
134 135 136 137 138

    connect(this, SIGNAL(currentRowChanged(int)), SLOT(setTarget(int)));
}

ProjectExplorer::Project *ProjectListWidget::project() const
139
{
Tobias Hunger's avatar
Tobias Hunger committed
140 141 142 143 144 145
    return m_project;
}

QSize ProjectListWidget::sizeHint() const
{
    int height = 0;
146 147
    int width = 0;
    for (int itemPos = 0; itemPos < count(); ++itemPos) {
Tobias Hunger's avatar
Tobias Hunger committed
148
        height += item(itemPos)->sizeHint().height();
149 150
        width = qMax(width, item(itemPos)->sizeHint().width());
    }
Tobias Hunger's avatar
Tobias Hunger committed
151

152
    // We try to keep the height of the popup equal to the actionbar
153
    QSize size(width, height);
154 155 156 157 158 159 160 161 162
    static QStatusBar *statusBar = Core::ICore::instance()->statusBar();
    static QWidget *actionBar = Core::ICore::instance()->mainWindow()->findChild<QWidget*>("actionbar");
    Q_ASSERT(actionBar);

    QMargins popupMargins = window()->contentsMargins();
    if (actionBar)
        size.setHeight(qMax(actionBar->height() - statusBar->height() -
                            (popupMargins.top() + popupMargins.bottom()), height));
    return size;
Tobias Hunger's avatar
Tobias Hunger committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
}

void ProjectListWidget::setRunComboPopup()
{
    QWidget *w = itemWidget(currentItem());
    MiniTargetWidget *mtw = qobject_cast<MiniTargetWidget*>(w);
    if (mtw->runSettingsComboBox())
        mtw->runSettingsComboBox()->showPopup();
}

void ProjectListWidget::setBuildComboPopup()
{
    QWidget *w = itemWidget(currentItem());
    MiniTargetWidget *mtw = qobject_cast<MiniTargetWidget*>(w);
    if (mtw->buildSettingsComboBox())
        mtw->buildSettingsComboBox()->showPopup();
}

void ProjectListWidget::setTarget(int index)
{
183 184 185 186
    MiniTargetWidget *mtw = qobject_cast<MiniTargetWidget *>(itemWidget(item(index)));
    if (!mtw)
        return;
    m_project->setActiveTarget(mtw->target());
Tobias Hunger's avatar
Tobias Hunger committed
187 188 189 190 191 192 193 194 195
}

MiniTargetWidget::MiniTargetWidget(Target *target, QWidget *parent) :
    QWidget(parent), m_target(target)
{
    Q_ASSERT(m_target);

    if (hasBuildConfiguration()) {
        m_buildComboBox = new QComboBox;
196 197
        m_buildComboBox->setProperty("alignarrow", true);
        m_buildComboBox->setProperty("hideborder", true);
Tobias Hunger's avatar
Tobias Hunger committed
198
        m_buildComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
199
        m_buildComboBox->setToolTip(tr("Select active build configuration"));
dt's avatar
dt committed
200
        m_buildComboBox->setModel(new BuildConfigurationModel(m_target, this));
Tobias Hunger's avatar
Tobias Hunger committed
201 202 203 204
    } else {
        m_buildComboBox = 0;
    }
 
205
    m_runComboBox = new QComboBox;
206 207
    m_runComboBox ->setProperty("alignarrow", true);
    m_runComboBox ->setProperty("hideborder", true);
208
    m_runComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
209
    m_runComboBox->setToolTip(tr("Select active run configuration"));
dt's avatar
dt committed
210 211
    RunConfigurationModel *model = new RunConfigurationModel(m_target, this);
    m_runComboBox->setModel(model);
212
    int fontSize = font().pointSize();
213 214 215 216 217
    setStyleSheet(QString::fromLatin1("QLabel { font-size: %2pt; color: white; } "
                                      "#target { font: bold %1pt;} "
                                      "#buildLabel{ font: bold; color: rgba(255, 255, 255, 160)} "
                                      "#runLabel { font: bold ; color: rgba(255, 255, 255, 160)} "
                                      ).arg(fontSize).arg(fontSize - 2));
218 219 220

    QGridLayout *gridLayout = new QGridLayout(this);

Tobias Hunger's avatar
Tobias Hunger committed
221
    m_targetName = new QLabel(m_target->displayName());
222
    m_targetName->setObjectName(QString::fromUtf8("target"));
223
    m_targetIcon = new QLabel();
con's avatar
con committed
224
    updateIcon();
Tobias Hunger's avatar
Tobias Hunger committed
225 226 227
    if (hasBuildConfiguration()) {
        Q_FOREACH(BuildConfiguration* bc, m_target->buildConfigurations())
                addBuildConfiguration(bc);
228

dt's avatar
dt committed
229 230 231
        BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
        m_buildComboBox->setCurrentIndex(model->indexFor(m_target->activeBuildConfiguration()).row());

Tobias Hunger's avatar
Tobias Hunger committed
232 233 234
        connect(m_target, SIGNAL(addedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
                SLOT(addBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
        connect(m_target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
235
                SLOT(removeBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
Tobias Hunger's avatar
Tobias Hunger committed
236 237 238 239 240

        connect(m_target, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
                SLOT(setActiveBuildConfiguration()));
        connect(m_buildComboBox, SIGNAL(currentIndexChanged(int)), SLOT(setActiveBuildConfiguration(int)));
    }
241

dt's avatar
dt committed
242 243
    m_runComboBox->setEnabled(m_target->runConfigurations().count() > 1);
    m_runComboBox->setCurrentIndex(model->indexFor(m_target->activeRunConfiguration()).row());
244

Tobias Hunger's avatar
Tobias Hunger committed
245
    connect(m_target, SIGNAL(addedRunConfiguration(ProjectExplorer::RunConfiguration*)),
246
            SLOT(addRunConfiguration(ProjectExplorer::RunConfiguration*)));
Tobias Hunger's avatar
Tobias Hunger committed
247
    connect(m_target, SIGNAL(removedRunConfiguration(ProjectExplorer::RunConfiguration*)),
248 249 250 251
            SLOT(removeRunConfiguration(ProjectExplorer::RunConfiguration*)));

    connect(m_runComboBox, SIGNAL(currentIndexChanged(int)), SLOT(setActiveRunConfiguration(int)));

Tobias Hunger's avatar
Tobias Hunger committed
252 253
    connect(m_target, SIGNAL(activeRunConfigurationChanged(ProjectExplorer::RunConfiguration*)),
            SLOT(setActiveRunConfiguration()));
con's avatar
con committed
254
    connect(m_target, SIGNAL(iconChanged()), this, SLOT(updateIcon()));
Tobias Hunger's avatar
Tobias Hunger committed
255 256 257 258 259 260 261 262

    QHBoxLayout *buildHelperLayout = 0;
    if (hasBuildConfiguration()) {
        buildHelperLayout= new QHBoxLayout;
        buildHelperLayout->setMargin(0);
        buildHelperLayout->setSpacing(0);
        buildHelperLayout->addWidget(m_buildComboBox);
    }
263 264 265 266 267 268 269

    QHBoxLayout *runHelperLayout = new QHBoxLayout;
    runHelperLayout->setMargin(0);
    runHelperLayout->setSpacing(0);
    runHelperLayout->addWidget(m_runComboBox);

    QFormLayout *formLayout = new QFormLayout;
270
    formLayout->setLabelAlignment(Qt::AlignRight);
Tobias Hunger's avatar
Tobias Hunger committed
271
    QLabel *lbl;
272
    int indent = 10;
Tobias Hunger's avatar
Tobias Hunger committed
273 274
    if (hasBuildConfiguration()) {
        lbl = new QLabel(tr("Build:"));
275
        lbl->setObjectName(QString::fromUtf8("buildLabel"));
276 277 278
        lbl->setMinimumWidth(lbl->fontMetrics().width(lbl->text()) + indent + 4);
        lbl->setIndent(indent);

Tobias Hunger's avatar
Tobias Hunger committed
279 280
        formLayout->addRow(lbl, buildHelperLayout);
    }
281
    lbl = new QLabel(tr("Run:"));
282
    lbl->setObjectName(QString::fromUtf8("runLabel"));
283 284
    lbl->setMinimumWidth(lbl->fontMetrics().width(lbl->text()) + indent + 4);
    lbl->setIndent(indent);
285 286 287 288 289 290 291
    formLayout->addRow(lbl, runHelperLayout);

    gridLayout->addWidget(m_targetName, 0, 0);
    gridLayout->addWidget(m_targetIcon, 0, 1, 2, 1, Qt::AlignTop|Qt::AlignHCenter);
    gridLayout->addLayout(formLayout, 1, 0);
}

con's avatar
con committed
292 293
void MiniTargetWidget::updateIcon()
{
294
    m_targetIcon->setPixmap(createCenteredIcon(m_target->icon(), QIcon()).pixmap(Core::Constants::TARGET_ICON_SIZE));
con's avatar
con committed
295 296
}

Tobias Hunger's avatar
Tobias Hunger committed
297 298 299 300 301
ProjectExplorer::Target *MiniTargetWidget::target() const
{
    return m_target;
}

302 303
void MiniTargetWidget::setActiveBuildConfiguration(int index)
{
dt's avatar
dt committed
304 305
    BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
    m_target->setActiveBuildConfiguration(model->buildConfigurationAt(index));
306
    emit changed();
307 308 309 310
}

void MiniTargetWidget::setActiveRunConfiguration(int index)
{
dt's avatar
dt committed
311 312
    RunConfigurationModel *model = static_cast<RunConfigurationModel *>(m_runComboBox->model());
    m_target->setActiveRunConfiguration(model->runConfigurationAt(index));
con's avatar
con committed
313
    updateIcon();
314
    emit changed();
315
}
dt's avatar
dt committed
316

317 318
void MiniTargetWidget::setActiveBuildConfiguration()
{
dt's avatar
dt committed
319 320 321
    QTC_ASSERT(m_buildComboBox, return);
    BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
    m_buildComboBox->setCurrentIndex(model->indexFor(m_target->activeBuildConfiguration()).row());
322 323 324 325
}

void MiniTargetWidget::setActiveRunConfiguration()
{
dt's avatar
dt committed
326 327
    RunConfigurationModel *model = static_cast<RunConfigurationModel *>(m_runComboBox->model());
    m_runComboBox->setCurrentIndex(model->indexFor(m_target->activeRunConfiguration()).row());
328 329
}

Tobias Hunger's avatar
Tobias Hunger committed
330
void MiniTargetWidget::addRunConfiguration(ProjectExplorer::RunConfiguration* rc)
331
{
hjk's avatar
hjk committed
332 333
    Q_UNUSED(rc);
    m_runComboBox->setEnabled(m_target->runConfigurations().count()>1);
334 335
}

Tobias Hunger's avatar
Tobias Hunger committed
336
void MiniTargetWidget::removeRunConfiguration(ProjectExplorer::RunConfiguration* rc)
337
{
dt's avatar
dt committed
338 339
    Q_UNUSED(rc);
    m_runComboBox->setEnabled(m_target->runConfigurations().count()>1);
340 341
}

Tobias Hunger's avatar
Tobias Hunger committed
342
void MiniTargetWidget::addBuildConfiguration(ProjectExplorer::BuildConfiguration* bc)
343
{
dt's avatar
dt committed
344
    Q_UNUSED(bc);
hjk's avatar
hjk committed
345 346
    connect(bc, SIGNAL(displayNameChanged()), SIGNAL(changed()), Qt::UniqueConnection);
    m_buildComboBox->setEnabled(m_target->buildConfigurations().count() > 1);
347 348
}

Tobias Hunger's avatar
Tobias Hunger committed
349
void MiniTargetWidget::removeBuildConfiguration(ProjectExplorer::BuildConfiguration* bc)
350
{
dt's avatar
dt committed
351
    Q_UNUSED(bc);
Tobias Hunger's avatar
Tobias Hunger committed
352
    QTC_ASSERT(m_buildComboBox, return);
dt's avatar
dt committed
353
    m_buildComboBox->setEnabled(m_target->buildConfigurations().count() > 1);
354 355
}

Tobias Hunger's avatar
Tobias Hunger committed
356 357 358 359 360
bool MiniTargetWidget::hasBuildConfiguration() const
{
    return (m_target->buildConfigurationFactory() != 0);
}

361
MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorAction, QWidget *parent) :
362
    QWidget(parent), m_projectAction(targetSelectorAction), m_ignoreIndexChange(false)
363
{
364 365
    setProperty("panelwidget", true);
    setContentsMargins(QMargins(0, 1, 1, 8));
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
    setWindowFlags(Qt::Popup);

    targetSelectorAction->setIcon(style()->standardIcon(QStyle::SP_ComputerIcon));
    targetSelectorAction->setProperty("titledAction", true);

    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->setMargin(0);
    layout->setSpacing(0);

    Utils::StyledBar *bar = new Utils::StyledBar;
    bar->setSingleRow(true);
    layout->addWidget(bar);
    QHBoxLayout *toolLayout = new QHBoxLayout(bar);
    toolLayout->setMargin(0);
    toolLayout->setSpacing(0);

    QLabel *lbl = new QLabel(tr("Project"));
    lbl->setIndent(6);
    QFont f = lbl->font();
    f.setBold(true);
    lbl->setFont(f);
387 388 389 390 391

    int panelHeight = lbl->fontMetrics().height() + 12;
    bar->ensurePolished(); // Required since manhattanstyle overrides height
    bar->setFixedHeight(panelHeight);

392
    m_projectsBox = new QComboBox;
393
    m_projectsBox->setToolTip(tr("Select active project"));
394
    m_projectsBox->setFocusPolicy(Qt::TabFocus);
395 396 397 398 399 400
    f.setBold(false);
    m_projectsBox->setFont(f);
    m_projectsBox->ensurePolished();
    m_projectsBox->setFixedHeight(panelHeight);
    m_projectsBox->setProperty("hideborder", true);
    m_projectsBox->setObjectName(QString::fromUtf8("ProjectsBox"));
401 402
    m_projectsBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    m_projectsBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
403 404 405 406 407 408

    toolLayout->addWidget(lbl);
    toolLayout->addWidget(new Utils::StyledSeparator);
    toolLayout->addWidget(m_projectsBox);

    m_widgetStack = new QStackedWidget;
409
    m_widgetStack->setFocusPolicy(Qt::NoFocus);
410 411
    layout->addWidget(m_widgetStack);

Tobias Hunger's avatar
Tobias Hunger committed
412 413
    connect(m_projectsBox, SIGNAL(activated(int)),
            SLOT(emitStartupProjectChanged(int)));
414 415 416 417 418
}

void MiniProjectTargetSelector::setVisible(bool visible)
{
    if (visible) {
419 420
        QSize sh = sizeHint();
        resize(sh);
421 422
        QStatusBar *statusBar = Core::ICore::instance()->statusBar();
        QPoint moveTo = statusBar->mapToGlobal(QPoint(0,0));
423
        moveTo -= QPoint(0, sh.height());
424 425
        move(moveTo);
    }
426

427
    QWidget::setVisible(visible);
428

Tobias Hunger's avatar
Tobias Hunger committed
429 430
    if (m_widgetStack->currentWidget())
        m_widgetStack->currentWidget()->setFocus();
431 432

    m_projectAction->setChecked(visible);
433 434
}

435 436 437 438 439 440 441 442 443 444 445 446 447
// This is a workaround for the problem that Windows
// will let the mouse events through when you click
// outside a popup to close it. This causes the popup
// to open on mouse release if you hit the button, which
//
//
// A similar case can be found in QComboBox
void MiniProjectTargetSelector::mousePressEvent(QMouseEvent *e)
{
    setAttribute(Qt::WA_NoMouseReplay);
    QWidget::mousePressEvent(e);
}

448 449 450 451 452 453
QString MiniProjectTargetSelector::fullName(ProjectExplorer::Project *project)
{
    return project->displayName() + " (" + project->file()->fileName() + ")";
}

void MiniProjectTargetSelector::addProject(ProjectExplorer::Project *project)
454 455 456
{
    QTC_ASSERT(project, return);
    ProjectListWidget *targetList = new ProjectListWidget(project);
457
    targetList->setStyleSheet(QString::fromLatin1("QListWidget { background: %1; border: none; }")
458
                              .arg(QColor(70, 70, 70).name()));
459

dt's avatar
dt committed
460 461
    m_ignoreIndexChange = true;

462
    QString sortName = fullName(project);
dt's avatar
dt committed
463
    int pos = 0;
464 465 466 467
    for (int i=0; i < m_projectsBox->count(); ++i) {
        Project *p = m_projectsBox->itemData(i).value<Project*>();
        QString itemSortName = fullName(p);
        if (itemSortName > sortName)
dt's avatar
dt committed
468
            pos = i;
469
    }
dt's avatar
dt committed
470 471 472

    m_widgetStack->insertWidget(pos, targetList);

473 474 475 476 477 478 479 480 481 482 483
    bool useFullName = false;
    for (int i = 0; i < m_projectsBox->count(); ++i) {
        Project *p = m_projectsBox->itemData(i).value<Project*>();
        if (p->displayName() == project->displayName()) {
            useFullName = true;
            m_projectsBox->setItemText(i, fullName(p));
        }
    }

    QString displayName = useFullName ? fullName(project) : project->displayName();
    m_projectsBox->insertItem(pos, displayName, QVariant::fromValue(project));
484

Tobias Hunger's avatar
Tobias Hunger committed
485 486
    connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
            SLOT(updateAction()));
487

Tobias Hunger's avatar
Tobias Hunger committed
488 489 490 491
    connect(project, SIGNAL(addedTarget(ProjectExplorer::Target*)),
            SLOT(addTarget(ProjectExplorer::Target*)));
    connect(project, SIGNAL(removedTarget(ProjectExplorer::Target*)),
            SLOT(removeTarget(ProjectExplorer::Target*)));
492 493
    connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
            SLOT(changeActiveTarget(ProjectExplorer::Target*)));
494 495 496 497 498

    if (project == ProjectExplorerPlugin::instance()->startupProject()) {
        m_projectsBox->setCurrentIndex(pos);
        m_widgetStack->setCurrentIndex(pos);
    }
Tobias Hunger's avatar
Tobias Hunger committed
499

dt's avatar
dt committed
500 501
    m_ignoreIndexChange = false;

Tobias Hunger's avatar
Tobias Hunger committed
502 503
    foreach (Target *t, project->targets())
        addTarget(t, t == project->activeTarget());
504 505

    m_projectsBox->setEnabled(m_projectsBox->count() > 1);
506
    m_projectsBox->parentWidget()->layout()->activate();
507 508 509 510
}

void MiniProjectTargetSelector::removeProject(ProjectExplorer::Project* project)
{
Tobias Hunger's avatar
Tobias Hunger committed
511 512 513 514
    int index = indexFor(project);
    if (index < 0)
        return;
    ProjectListWidget *plw = qobject_cast<ProjectListWidget*>(m_widgetStack->widget(index));
515 516 517 518

    // We don't want to emit a startUpProject changed, even if we remove the current startup project
    // The ProjectExplorer takes care of setting a new startup project.
    m_ignoreIndexChange = true;
Tobias Hunger's avatar
Tobias Hunger committed
519
    m_projectsBox->removeItem(index);
520
    m_projectsBox->setEnabled(m_projectsBox->count() > 1);
Tobias Hunger's avatar
Tobias Hunger committed
521
    delete plw;
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538

    // Update display names
    QString name = project->displayName();
    int count = 0;
    int otherIndex;
    for (int i = 0; i < m_projectsBox->count(); ++i) {
        Project *p = m_projectsBox->itemData(i).value<Project*>();
        if (p->displayName() == name) {
            ++count;
            otherIndex = i;
        }
    }
    if (count == 1) {
        Project *p = m_projectsBox->itemData(otherIndex).value<Project*>();
        m_projectsBox->setItemText(otherIndex, p->displayName());
    }

539
    m_ignoreIndexChange = false;
540
    m_projectsBox->parentWidget()->layout()->activate();
541 542
}

Tobias Hunger's avatar
Tobias Hunger committed
543 544 545 546 547 548 549 550
void MiniProjectTargetSelector::addTarget(ProjectExplorer::Target *target, bool activeTarget)
{
    QTC_ASSERT(target, return);

    int index = indexFor(target->project());
    if (index < 0)
        return;

con's avatar
con committed
551 552
    connect(target, SIGNAL(toolTipChanged()), this, SLOT(updateAction()));
    connect(target, SIGNAL(iconChanged()), this, SLOT(updateAction()));
553
    connect(target, SIGNAL(overlayIconChanged()), this, SLOT(updateAction()));
Tobias Hunger's avatar
Tobias Hunger committed
554 555
    ProjectListWidget *plw = qobject_cast<ProjectListWidget*>(m_widgetStack->widget(index));
    QListWidgetItem *lwi = new QListWidgetItem();
556 557 558 559 560 561 562 563 564 565 566 567 568 569

    // Sort on insert:
    for (int idx = 0; idx <= plw->count(); ++idx) {
        QListWidgetItem *itm(plw->item(idx));
        MiniTargetWidget *mtw(qobject_cast<MiniTargetWidget *>(plw->itemWidget(itm)));
        if (!mtw && idx < plw->count())
            continue;
        if (idx == plw->count() ||
            mtw->target()->displayName() > target->displayName()) {
            plw->insertItem(idx, lwi);
            break;
        }
    }

Tobias Hunger's avatar
Tobias Hunger committed
570
    MiniTargetWidget *targetWidget = new MiniTargetWidget(target);
571
    connect(targetWidget, SIGNAL(changed()), this, SLOT(updateAction()));
Tobias Hunger's avatar
Tobias Hunger committed
572
    targetWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
573
    lwi->setSizeHint(targetWidget->sizeHint());
Tobias Hunger's avatar
Tobias Hunger committed
574 575 576 577
    plw->setItemWidget(lwi, targetWidget);

    if (activeTarget)
        plw->setCurrentItem(lwi, QItemSelectionModel::SelectCurrent);
578 579

    m_widgetStack->updateGeometry();
Tobias Hunger's avatar
Tobias Hunger committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
}

void MiniProjectTargetSelector::removeTarget(ProjectExplorer::Target *target)
{
    QTC_ASSERT(target, return);

    int index = indexFor(target->project());
    if (index < 0)
        return;

    ProjectListWidget *plw = qobject_cast<ProjectListWidget*>(m_widgetStack->widget(index));
    for (int i = 0; i < plw->count(); ++i) {
        QListWidgetItem *itm(plw->item(i));
        MiniTargetWidget *mtw(qobject_cast<MiniTargetWidget *>(plw->itemWidget(itm)));
        if (!mtw)
            continue;
        if (target != mtw->target())
            continue;
        delete plw->takeItem(i);
        delete mtw;
    }
601

602 603 604
    disconnect(target, SIGNAL(toolTipChanged()), this, SLOT(updateAction()));
    disconnect(target, SIGNAL(iconChanged()), this, SLOT(updateAction()));
    disconnect(target, SIGNAL(overlayIconChanged()), this, SLOT(updateAction()));
605 606

    m_widgetStack->updateGeometry();
Tobias Hunger's avatar
Tobias Hunger committed
607
}
608

609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
void MiniProjectTargetSelector::changeActiveTarget(ProjectExplorer::Target *target)
{
    int index = indexFor(target->project());
    if (index < 0)
        return;
    ProjectListWidget *plw = qobject_cast<ProjectListWidget*>(m_widgetStack->widget(index));

    for (int i = 0; i < plw->count(); ++i) {
        QListWidgetItem *itm = plw->item(i);
        MiniTargetWidget *mtw = qobject_cast<MiniTargetWidget*>(plw->itemWidget(itm));
        if (mtw->target() == target) {
            plw->setCurrentItem(itm);
            break;
        }
    }
}

626 627 628 629
void MiniProjectTargetSelector::updateAction()
{
    Project *project = ProjectExplorerPlugin::instance()->startupProject();

630
    QString projectName;;
631
    QString targetName;
con's avatar
con committed
632
    QString targetToolTipText;
633 634
    QString buildConfig;
    QString runConfig;
635
    QIcon targetIcon = style()->standardIcon(QStyle::SP_ComputerIcon);
636

637
    const int extrawidth = 110; // Size of margins + icon width
Tobias Hunger's avatar
Tobias Hunger committed
638
    // Some fudge numbers to ensure the menu does not grow unbounded
639 640
    int maxLength = fontMetrics().averageCharWidth() * 140;

641 642
    if (project) {
        projectName = project->displayName();
Tobias Hunger's avatar
Tobias Hunger committed
643

644 645 646 647 648
        if (Target *target = project->activeTarget()) {
            if (project->targets().count() > 1) {
                targetName = project->activeTarget()->displayName();
            }
            if (BuildConfiguration *bc = target->activeBuildConfiguration()) {
Tobias Hunger's avatar
Tobias Hunger committed
649
                buildConfig = bc->displayName();
650 651
                int minimumWidth = fontMetrics().width(bc->displayName() + tr("Build:")) + extrawidth;
                m_widgetStack->setMinimumWidth(qMin(maxLength, qMax(minimumWidth, m_widgetStack->minimumWidth())));
652 653 654 655
            }

            if (RunConfiguration *rc = target->activeRunConfiguration()) {
                runConfig = rc->displayName();
656 657
                int minimumWidth = fontMetrics().width(rc->displayName() + tr("Run:")) + extrawidth;
                m_widgetStack->setMinimumWidth(qMin(maxLength, qMax(minimumWidth, m_widgetStack->minimumWidth())));
658
            }
con's avatar
con committed
659
            targetToolTipText = target->toolTip();
660
            targetIcon = createCenteredIcon(target->icon(), target->overlayIcon());
Tobias Hunger's avatar
Tobias Hunger committed
661
        }
662 663 664
    }
    m_projectAction->setProperty("heading", projectName);
    m_projectAction->setProperty("subtitle", buildConfig);
con's avatar
con committed
665
    m_projectAction->setIcon(targetIcon);
666
    QString toolTip = tr("<html><nobr><b>Project:</b> %1<br/>%2%3<b>Run:</b> %4%5</html>");
667 668 669 670
    QString targetTip = targetName.isEmpty() ? QLatin1String("")
        : tr("<b>Target:</b> %1<br/>").arg(targetName);
    QString buildTip = buildConfig.isEmpty() ? QLatin1String("")
        : tr("<b>Build:</b> %2<br/>").arg(buildConfig);
con's avatar
con committed
671 672 673
    QString targetToolTip = targetToolTipText.isEmpty() ? QLatin1String("")
        : tr("<br/>%1").arg(targetToolTipText);
    m_projectAction->setToolTip(toolTip.arg(projectName, targetTip, buildTip, runConfig, targetToolTip));
Tobias Hunger's avatar
Tobias Hunger committed
674 675 676 677 678 679 680 681 682 683
}

int MiniProjectTargetSelector::indexFor(ProjectExplorer::Project *project) const
{
    for (int i = 0; i < m_widgetStack->count(); ++i) {
        ProjectListWidget *plw = qobject_cast<ProjectListWidget*>(m_widgetStack->widget(i));
        if (plw && plw->project() == project)
            return i;
    }
    return -1;
684 685 686 687
}

void MiniProjectTargetSelector::emitStartupProjectChanged(int index)
{
688 689
    if (m_ignoreIndexChange)
        return;
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
    Project *project = m_projectsBox->itemData(index).value<Project*>();
    QTC_ASSERT(project, return;)
    emit startupProjectChanged(project);
}

void MiniProjectTargetSelector::changeStartupProject(ProjectExplorer::Project *project)
{
    for (int i = 0; i < m_widgetStack->count(); ++i) {
        ProjectListWidget *plw = qobject_cast<ProjectListWidget*>(m_widgetStack->widget(i));
        if (plw && plw->project() == project) {
            m_projectsBox->setCurrentIndex(i);
            m_widgetStack->setCurrentIndex(i);
        }
    }
    updateAction();
}
Tobias Hunger's avatar
Tobias Hunger committed
706

707 708 709 710 711 712 713 714 715 716 717
void MiniProjectTargetSelector::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setPen(Utils::StyleHelper::borderColor());
    painter.drawLine(rect().topLeft(), rect().topRight());
    painter.drawLine(rect().topRight(), rect().bottomRight());

    QRect bottomRect(0, rect().height() - 8, rect().width(), 8);
    static QImage image(QLatin1String(":/projectexplorer/images/targetpanel_bottom.png"));
    Utils::StyleHelper::drawCornerImage(image, &painter, bottomRect, 1, 1, 1, 1);
}