miniprojecttargetselector.cpp 25.1 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 7 8
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
con's avatar
con committed
9
** No Commercial Usage
Tobias Hunger's avatar
Tobias Hunger committed
10
**
con's avatar
con committed
11 12 13 14
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
Tobias Hunger's avatar
Tobias Hunger committed
15 16 17 18 19 20 21 22 23 24
**
** GNU Lesser General Public License Usage
**
** 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.
**
con's avatar
con committed
25 26 27 28 29 30
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** 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
31 32 33
**
**************************************************************************/

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

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

#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 403 404 405 406 407
    m_projectsBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);

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

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

Tobias Hunger's avatar
Tobias Hunger committed
411 412
    connect(m_projectsBox, SIGNAL(activated(int)),
            SLOT(emitStartupProjectChanged(int)));
413 414 415 416 417 418 419 420 421 422 423 424
}

void MiniProjectTargetSelector::setVisible(bool visible)
{
    if (visible) {
        resize(sizeHint());
        QStatusBar *statusBar = Core::ICore::instance()->statusBar();
        QPoint moveTo = statusBar->mapToGlobal(QPoint(0,0));
        moveTo -= QPoint(0, sizeHint().height());
        move(moveTo);
    }
    QWidget::setVisible(visible);
Tobias Hunger's avatar
Tobias Hunger committed
425 426
    if (m_widgetStack->currentWidget())
        m_widgetStack->currentWidget()->setFocus();
427 428

    m_projectAction->setChecked(visible);
429 430
}

431 432 433 434 435 436 437 438 439 440 441 442 443
// 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);
}

444 445 446 447
void MiniProjectTargetSelector::addProject(ProjectExplorer::Project* project)
{
    QTC_ASSERT(project, return);
    ProjectListWidget *targetList = new ProjectListWidget(project);
448
    targetList->setStyleSheet(QString::fromLatin1("QListWidget { background: %1; border: none; }")
449
                              .arg(QColor(70, 70, 70).name()));
450

dt's avatar
dt committed
451 452 453 454 455 456 457 458 459 460
    m_ignoreIndexChange = true;

    int pos = 0;
    for (int i=0; i < m_projectsBox->count(); ++i)
        if (m_projectsBox->itemText(i) > project->displayName())
            pos = i;

    m_widgetStack->insertWidget(pos, targetList);

    m_projectsBox->insertItem(pos, project->displayName(), QVariant::fromValue(project));
461

Tobias Hunger's avatar
Tobias Hunger committed
462 463
    connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
            SLOT(updateAction()));
464

Tobias Hunger's avatar
Tobias Hunger committed
465 466 467 468
    connect(project, SIGNAL(addedTarget(ProjectExplorer::Target*)),
            SLOT(addTarget(ProjectExplorer::Target*)));
    connect(project, SIGNAL(removedTarget(ProjectExplorer::Target*)),
            SLOT(removeTarget(ProjectExplorer::Target*)));
469 470
    connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
            SLOT(changeActiveTarget(ProjectExplorer::Target*)));
471 472 473 474 475

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

dt's avatar
dt committed
477 478
    m_ignoreIndexChange = false;

Tobias Hunger's avatar
Tobias Hunger committed
479 480
    foreach (Target *t, project->targets())
        addTarget(t, t == project->activeTarget());
481 482

    m_projectsBox->setEnabled(m_projectsBox->count() > 1);
483 484 485 486
}

void MiniProjectTargetSelector::removeProject(ProjectExplorer::Project* project)
{
Tobias Hunger's avatar
Tobias Hunger committed
487 488 489 490
    int index = indexFor(project);
    if (index < 0)
        return;
    ProjectListWidget *plw = qobject_cast<ProjectListWidget*>(m_widgetStack->widget(index));
491 492 493 494

    // 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
495
    m_projectsBox->removeItem(index);
496
    m_projectsBox->setEnabled(m_projectsBox->count() > 1);
Tobias Hunger's avatar
Tobias Hunger committed
497
    delete plw;
498
    m_ignoreIndexChange = false;
499 500
}

Tobias Hunger's avatar
Tobias Hunger committed
501 502 503 504 505 506 507 508
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
509 510
    connect(target, SIGNAL(toolTipChanged()), this, SLOT(updateAction()));
    connect(target, SIGNAL(iconChanged()), this, SLOT(updateAction()));
511
    connect(target, SIGNAL(overlayIconChanged()), this, SLOT(updateAction()));
Tobias Hunger's avatar
Tobias Hunger committed
512 513
    ProjectListWidget *plw = qobject_cast<ProjectListWidget*>(m_widgetStack->widget(index));
    QListWidgetItem *lwi = new QListWidgetItem();
514 515 516 517 518 519 520 521 522 523 524 525 526 527

    // 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
528
    MiniTargetWidget *targetWidget = new MiniTargetWidget(target);
529
    connect(targetWidget, SIGNAL(changed()), this, SLOT(updateAction()));
Tobias Hunger's avatar
Tobias Hunger committed
530
    targetWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
531
    lwi->setSizeHint(targetWidget->sizeHint());
Tobias Hunger's avatar
Tobias Hunger committed
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
    plw->setItemWidget(lwi, targetWidget);

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

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;
    }
557 558 559
    disconnect(target, SIGNAL(toolTipChanged()), this, SLOT(updateAction()));
    disconnect(target, SIGNAL(iconChanged()), this, SLOT(updateAction()));
    disconnect(target, SIGNAL(overlayIconChanged()), this, SLOT(updateAction()));
Tobias Hunger's avatar
Tobias Hunger committed
560
}
561

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
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;
        }
    }
}

579 580 581 582
void MiniProjectTargetSelector::updateAction()
{
    Project *project = ProjectExplorerPlugin::instance()->startupProject();

583
    QString projectName;;
584
    QString targetName;
con's avatar
con committed
585
    QString targetToolTipText;
586 587
    QString buildConfig;
    QString runConfig;
588
    QIcon targetIcon = style()->standardIcon(QStyle::SP_ComputerIcon);
589

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

594 595
    if (project) {
        projectName = project->displayName();
Tobias Hunger's avatar
Tobias Hunger committed
596

597 598 599 600 601
        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
602
                buildConfig = bc->displayName();
603 604
                int minimumWidth = fontMetrics().width(bc->displayName() + tr("Build:")) + extrawidth;
                m_widgetStack->setMinimumWidth(qMin(maxLength, qMax(minimumWidth, m_widgetStack->minimumWidth())));
605 606 607 608
            }

            if (RunConfiguration *rc = target->activeRunConfiguration()) {
                runConfig = rc->displayName();
609 610
                int minimumWidth = fontMetrics().width(rc->displayName() + tr("Run:")) + extrawidth;
                m_widgetStack->setMinimumWidth(qMin(maxLength, qMax(minimumWidth, m_widgetStack->minimumWidth())));
611
            }
con's avatar
con committed
612
            targetToolTipText = target->toolTip();
613
            targetIcon = createCenteredIcon(target->icon(), target->overlayIcon());
Tobias Hunger's avatar
Tobias Hunger committed
614
        }
615 616 617
    }
    m_projectAction->setProperty("heading", projectName);
    m_projectAction->setProperty("subtitle", buildConfig);
con's avatar
con committed
618
    m_projectAction->setIcon(targetIcon);
619
    QString toolTip = tr("<html><nobr><b>Project:</b> %1<br/>%2%3<b>Run:</b> %4%5</html>");
620 621 622 623
    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
624 625 626
    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
627 628 629 630 631 632 633 634 635 636
}

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;
637 638 639 640
}

void MiniProjectTargetSelector::emitStartupProjectChanged(int index)
{
641 642
    if (m_ignoreIndexChange)
        return;
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
    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
659

660 661 662 663 664 665 666 667 668 669 670
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);
}