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
**
hjk's avatar
hjk committed
5
** Copyright (c) 2010 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 146 147 148
    return m_project;
}

QSize ProjectListWidget::sizeHint() const
{
    int height = 0;
    for (int itemPos = 0; itemPos < count(); ++itemPos)
        height += item(itemPos)->sizeHint().height();

149 150 151 152 153 154 155 156 157 158 159
    // We try to keep the height of the popup equal to the actionbar
    QSize size(QListWidget::sizeHint().width(), height);
    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
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
}

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)
{
180 181 182 183
    MiniTargetWidget *mtw = qobject_cast<MiniTargetWidget *>(itemWidget(item(index)));
    if (!mtw)
        return;
    m_project->setActiveTarget(mtw->target());
Tobias Hunger's avatar
Tobias Hunger committed
184 185 186 187 188 189 190 191 192
}

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

    if (hasBuildConfiguration()) {
        m_buildComboBox = new QComboBox;
193 194
        m_buildComboBox->setProperty("alignarrow", true);
        m_buildComboBox->setProperty("hideborder", true);
Tobias Hunger's avatar
Tobias Hunger committed
195
        m_buildComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
196
        m_buildComboBox->setToolTip(tr("Select active build configuration"));
dt's avatar
dt committed
197
        m_buildComboBox->setModel(new BuildConfigurationModel(m_target, this));
Tobias Hunger's avatar
Tobias Hunger committed
198 199 200 201
    } else {
        m_buildComboBox = 0;
    }
 
202
    m_runComboBox = new QComboBox;
203 204
    m_runComboBox ->setProperty("alignarrow", true);
    m_runComboBox ->setProperty("hideborder", true);
205
    m_runComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
206
    m_runComboBox->setToolTip(tr("Select active run configuration"));
dt's avatar
dt committed
207 208
    RunConfigurationModel *model = new RunConfigurationModel(m_target, this);
    m_runComboBox->setModel(model);
209
    int fontSize = font().pointSize();
210 211 212 213 214
    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));
215 216 217

    QGridLayout *gridLayout = new QGridLayout(this);

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

dt's avatar
dt committed
226 227 228
        BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
        m_buildComboBox->setCurrentIndex(model->indexFor(m_target->activeBuildConfiguration()).row());

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

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

dt's avatar
dt committed
239 240
    m_runComboBox->setEnabled(m_target->runConfigurations().count() > 1);
    m_runComboBox->setCurrentIndex(model->indexFor(m_target->activeRunConfiguration()).row());
241

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

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

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

    QHBoxLayout *buildHelperLayout = 0;
    if (hasBuildConfiguration()) {
        buildHelperLayout= new QHBoxLayout;
        buildHelperLayout->setMargin(0);
        buildHelperLayout->setSpacing(0);
        buildHelperLayout->addWidget(m_buildComboBox);
    }
260 261 262 263 264 265 266

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

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

Tobias Hunger's avatar
Tobias Hunger committed
276 277
        formLayout->addRow(lbl, buildHelperLayout);
    }
278
    lbl = new QLabel(tr("Run:"));
279
    lbl->setObjectName(QString::fromUtf8("runLabel"));
280 281
    lbl->setMinimumWidth(lbl->fontMetrics().width(lbl->text()) + indent + 4);
    lbl->setIndent(indent);
282 283 284 285 286 287 288
    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
289 290
void MiniTargetWidget::updateIcon()
{
291
    m_targetIcon->setPixmap(createCenteredIcon(m_target->icon(), QIcon()).pixmap(Core::Constants::TARGET_ICON_SIZE));
con's avatar
con committed
292 293
}

Tobias Hunger's avatar
Tobias Hunger committed
294 295 296 297 298
ProjectExplorer::Target *MiniTargetWidget::target() const
{
    return m_target;
}

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

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

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

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

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

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

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

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

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

358
MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorAction, QWidget *parent) :
359
    QWidget(parent), m_projectAction(targetSelectorAction), m_ignoreIndexChange(false)
360
{
361 362
    setProperty("panelwidget", true);
    setContentsMargins(QMargins(0, 1, 1, 8));
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
    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);
384 385 386 387 388

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

389
    m_projectsBox = new QComboBox;
390
    m_projectsBox->setToolTip(tr("Select active project"));
391
    m_projectsBox->setFocusPolicy(Qt::TabFocus);
392 393 394 395 396 397
    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"));
398 399 400 401 402 403 404
    m_projectsBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);

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

    m_widgetStack = new QStackedWidget;
405
    m_widgetStack->setFocusPolicy(Qt::NoFocus);
406 407
    layout->addWidget(m_widgetStack);

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

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
422 423
    if (m_widgetStack->currentWidget())
        m_widgetStack->currentWidget()->setFocus();
424 425

    m_projectAction->setChecked(visible);
426 427
}

428 429 430 431 432 433 434 435 436 437 438 439 440
// 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);
}

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

dt's avatar
dt committed
448 449 450 451 452 453 454 455 456 457
    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));
458

Tobias Hunger's avatar
Tobias Hunger committed
459 460
    connect(project, SIGNAL(activeTargetChanged(ProjectExplorer::Target*)),
            SLOT(updateAction()));
461

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

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

dt's avatar
dt committed
474 475
    m_ignoreIndexChange = false;

Tobias Hunger's avatar
Tobias Hunger committed
476 477
    foreach (Target *t, project->targets())
        addTarget(t, t == project->activeTarget());
478 479

    m_projectsBox->setEnabled(m_projectsBox->count() > 1);
480 481 482 483
}

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

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

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

    // 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
525
    MiniTargetWidget *targetWidget = new MiniTargetWidget(target);
526
    connect(targetWidget, SIGNAL(changed()), this, SLOT(updateAction()));
Tobias Hunger's avatar
Tobias Hunger committed
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554
    targetWidget->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
    // width==0 size hint to avoid horizontal scrolling in list widget
    lwi->setSizeHint(QSize(0, targetWidget->sizeHint().height()));
    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;
    }
555 556 557
    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
558
}
559

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

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

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

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

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

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

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

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;
635 636 637 638
}

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

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