1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "outputpanemanager.h"
34 #include "outputpane.h"
35 #include "coreconstants.h"
36 #include "findplaceholder.h"
38 #include "coreconstants.h"
40 #include "ioutputpane.h"
41 #include "mainwindow.h"
42 #include "modemanager.h"
44 #include <coreplugin/actionmanager/actionmanager.h>
45 #include <coreplugin/actionmanager/actioncontainer.h>
46 #include <coreplugin/actionmanager/command.h>
47 #include <coreplugin/editormanager/editormanager.h>
48 #include <coreplugin/findplaceholder.h>
49 #include <coreplugin/editormanager/ieditor.h>
50 #include <coreplugin/uniqueidmanager.h>
52 #include <extensionsystem/pluginmanager.h>
54 #include <utils/styledbar.h>
55 #include <utils/qtcassert.h>
57 #include <QtCore/QDebug>
59 #include <QtGui/QAction>
60 #include <QtGui/QApplication>
61 #include <QtGui/QComboBox>
62 #include <QtGui/QFocusEvent>
63 #include <QtGui/QHBoxLayout>
64 #include <QtGui/QSplitter>
65 #include <QtGui/QPainter>
66 #include <QtGui/QToolButton>
67 #include <QtGui/QStackedWidget>
68 #include <QtGui/QMenu>
77 static OutputPaneManager *m_instance = 0;
79 void OutputPaneManager::create()
81 m_instance = new OutputPaneManager;
84 void OutputPaneManager::destroy()
90 OutputPaneManager *OutputPaneManager::instance()
95 void OutputPaneManager::updateStatusButtons(bool visible)
97 int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt();
98 if (m_buttons.value(idx))
99 m_buttons.value(idx)->setChecked(visible);
100 m_minMaxAction->setVisible(OutputPanePlaceHolder::getCurrent()
101 && OutputPanePlaceHolder::getCurrent()->canMaximizeOrMinimize());
104 OutputPaneManager::OutputPaneManager(QWidget *parent) :
106 m_widgetComboBox(new QComboBox),
107 m_clearButton(new QToolButton),
108 m_closeButton(new QToolButton),
110 m_minMaxButton(new QToolButton),
114 m_outputWidgetPane(new QStackedWidget),
115 m_opToolBarWidgets(new QStackedWidget),
116 m_minimizeIcon(":/core/images/arrowdown.png"),
117 m_maximizeIcon(":/core/images/arrowup.png"),
120 setWindowTitle(tr("Output"));
121 connect(m_widgetComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changePage()));
123 m_clearButton->setIcon(QIcon(QLatin1String(Constants::ICON_CLEAN_PANE)));
124 m_clearButton->setToolTip(tr("Clear"));
125 connect(m_clearButton, SIGNAL(clicked()), this, SLOT(clearPage()));
127 m_nextAction = new QAction(this);
128 m_nextAction->setIcon(QIcon(QLatin1String(Constants::ICON_NEXT)));
129 m_nextAction->setText(tr("Next Item"));
130 connect(m_nextAction, SIGNAL(triggered()), this, SLOT(slotNext()));
132 m_prevAction = new QAction(this);
133 m_prevAction->setIcon(QIcon(QLatin1String(Constants::ICON_PREV)));
134 m_prevAction->setText(tr("Previous Item"));
135 connect(m_prevAction, SIGNAL(triggered()), this, SLOT(slotPrev()));
137 m_minMaxAction = new QAction(this);
138 m_minMaxAction->setIcon(m_maximizeIcon);
139 m_minMaxAction->setText(tr("Maximize Output Pane"));
141 m_closeButton->setIcon(QIcon(QLatin1String(Constants::ICON_CLOSE)));
142 connect(m_closeButton, SIGNAL(clicked()), this, SLOT(slotHide()));
144 QVBoxLayout *mainlayout = new QVBoxLayout;
145 mainlayout->setSpacing(0);
146 mainlayout->setMargin(0);
147 m_toolBar = new Utils::StyledBar;
148 QHBoxLayout *toolLayout = new QHBoxLayout(m_toolBar);
149 toolLayout->setMargin(0);
150 toolLayout->setSpacing(0);
151 toolLayout->addWidget(m_widgetComboBox);
152 toolLayout->addWidget(m_clearButton);
153 m_prevToolButton = new QToolButton;
154 toolLayout->addWidget(m_prevToolButton);
155 m_nextToolButton = new QToolButton;
156 toolLayout->addWidget(m_nextToolButton);
157 toolLayout->addWidget(m_opToolBarWidgets);
158 toolLayout->addWidget(m_minMaxButton);
159 toolLayout->addWidget(m_closeButton);
160 mainlayout->addWidget(m_toolBar);
161 mainlayout->addWidget(m_outputWidgetPane, 10);
162 mainlayout->addWidget(new Core::FindToolBarPlaceHolder(this));
163 setLayout(mainlayout);
165 m_buttonsWidget = new QWidget;
166 m_buttonsWidget->setLayout(new QHBoxLayout);
167 m_buttonsWidget->layout()->setContentsMargins(5,0,0,0);
168 m_buttonsWidget->layout()->setSpacing(4);
172 OutputPaneManager::~OutputPaneManager()
176 QWidget *OutputPaneManager::buttonsWidget()
178 return m_buttonsWidget;
181 // Return shortcut as Ctrl+<number>
182 static inline int paneShortCut(int number)
185 int modifier = Qt::CTRL;
187 int modifier = Qt::ALT;
189 return modifier | (Qt::Key_0 + number);
192 void OutputPaneManager::init()
194 ActionManager *am = Core::ICore::instance()->actionManager();
195 ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
196 const Context globalcontext(Core::Constants::C_GLOBAL);
198 // Window->Output Panes
199 ActionContainer *mpanes = am->createMenu(Constants::M_WINDOW_PANES);
200 mwindow->addMenu(mpanes, Constants::G_WINDOW_PANES);
201 mpanes->menu()->setTitle(tr("Output &Panes"));
202 mpanes->appendGroup("Coreplugin.OutputPane.ActionsGroup");
203 mpanes->appendGroup("Coreplugin.OutputPane.PanesGroup");
207 cmd = am->registerAction(m_prevAction, "Coreplugin.OutputPane.previtem", globalcontext);
208 cmd->setDefaultKeySequence(QKeySequence("Shift+F6"));
209 m_prevToolButton->setDefaultAction(cmd->action());
210 mpanes->addAction(cmd, "Coreplugin.OutputPane.ActionsGroup");
212 cmd = am->registerAction(m_nextAction, "Coreplugin.OutputPane.nextitem", globalcontext);
213 m_nextToolButton->setDefaultAction(cmd->action());
214 cmd->setDefaultKeySequence(QKeySequence("F6"));
215 mpanes->addAction(cmd, "Coreplugin.OutputPane.ActionsGroup");
217 cmd = am->registerAction(m_minMaxAction, "Coreplugin.OutputPane.minmax", globalcontext);
219 cmd->setDefaultKeySequence(QKeySequence("Ctrl+9"));
221 cmd->setDefaultKeySequence(QKeySequence("Alt+9"));
223 cmd->setAttribute(Command::CA_UpdateText);
224 cmd->setAttribute(Command::CA_UpdateIcon);
225 mpanes->addAction(cmd, "Coreplugin.OutputPane.ActionsGroup");
226 connect(m_minMaxAction, SIGNAL(triggered()), this, SLOT(slotMinMax()));
227 m_minMaxButton->setDefaultAction(cmd->action());
229 QAction *sep = new QAction(this);
230 sep->setSeparator(true);
231 cmd = am->registerAction(sep, "Coreplugin.OutputPane.Sep", globalcontext);
232 mpanes->addAction(cmd, "Coreplugin.OutputPane.ActionsGroup");
234 QList<IOutputPane*> panes = ExtensionSystem::PluginManager::instance()
235 ->getObjects<IOutputPane>();
236 QMultiMap<int, IOutputPane*> sorted;
237 foreach (IOutputPane* outPane, panes)
238 sorted.insertMulti(outPane->priorityInStatusBar(), outPane);
240 QMultiMap<int, IOutputPane*>::const_iterator it, begin;
241 begin = sorted.constBegin();
242 it = sorted.constEnd();
243 int shortcutNumber = 1;
244 while (it != begin) {
246 IOutputPane* outPane = it.value();
247 const int idx = m_outputWidgetPane->addWidget(outPane->outputWidget(this));
249 m_pageMap.insert(idx, outPane);
250 connect(outPane, SIGNAL(showPage(bool)), this, SLOT(showPage(bool)));
251 connect(outPane, SIGNAL(hidePage()), this, SLOT(slotHide()));
252 connect(outPane, SIGNAL(togglePage(bool)), this, SLOT(togglePage(bool)));
253 connect(outPane, SIGNAL(navigateStateUpdate()), this, SLOT(updateNavigateState()));
255 QWidget *toolButtonsContainer = new QWidget(m_opToolBarWidgets);
256 QHBoxLayout *toolButtonsLayout = new QHBoxLayout;
257 toolButtonsLayout->setMargin(0);
258 toolButtonsLayout->setSpacing(0);
259 foreach (QWidget *toolButton, outPane->toolBarWidgets())
260 toolButtonsLayout->addWidget(toolButton);
261 toolButtonsLayout->addStretch(5);
262 toolButtonsContainer->setLayout(toolButtonsLayout);
264 m_opToolBarWidgets->addWidget(toolButtonsContainer);
266 QString actionId = QString("QtCreator.Pane.%1").arg(outPane->displayName().simplified());
267 actionId.remove(QLatin1Char(' '));
268 QAction *action = new QAction(outPane->displayName(), this);
270 Command *cmd = am->registerAction(action, actionId, Context(Constants::C_GLOBAL));
272 mpanes->addAction(cmd, "Coreplugin.OutputPane.PanesGroup");
273 m_actions.insert(cmd->action(), idx);
275 if (outPane->priorityInStatusBar() != -1) {
276 cmd->setDefaultKeySequence(QKeySequence(paneShortCut(shortcutNumber)));
277 QPushButton *button = new OutputPaneToggleButton(shortcutNumber, outPane->displayName(),
280 m_buttonsWidget->layout()->addWidget(button);
281 connect(button, SIGNAL(clicked()), this, SLOT(buttonTriggered()));
282 m_buttons.insert(idx, button);
285 // Now add the entry to the combobox, since the first item we add sets the currentIndex, thus we need to be set up for that
286 m_widgetComboBox->addItem(outPane->displayName(), idx);
288 connect(cmd->action(), SIGNAL(triggered()), this, SLOT(shortcutTriggered()));
294 void OutputPaneManager::shortcutTriggered()
296 QAction *action = qobject_cast<QAction*>(sender());
297 if (action && m_actions.contains(action)) {
298 int idx = m_actions.value(action);
299 Core::IOutputPane *outputPane = m_pageMap.value(idx);
300 // Now check the special case, the output window is already visible,
301 // we are already on that page
302 // but the outputpane doesn't have focus
303 // then just give it focus
304 // else do the same as clicking on the button does
305 if (OutputPanePlaceHolder::isCurrentVisible()
306 && m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() == idx) {
307 if (!outputPane->hasFocus() && outputPane->canFocus())
308 outputPane->setFocus();
312 outputPane->popup(true);
317 bool OutputPaneManager::isMaximized()const
322 void OutputPaneManager::slotMinMax()
324 QTC_ASSERT(OutputPanePlaceHolder::getCurrent(), return);
326 if (!OutputPanePlaceHolder::getCurrent()->isVisible()) // easier than disabling/enabling the action
328 m_maximised = !m_maximised;
329 OutputPanePlaceHolder::getCurrent()->maximizeOrMinimize(m_maximised);
330 m_minMaxAction->setIcon(m_maximised ? m_minimizeIcon : m_maximizeIcon);
331 m_minMaxAction->setText(m_maximised ? tr("Minimize Output Pane")
332 : tr("Maximize Output Pane"));
335 void OutputPaneManager::buttonTriggered()
337 QPushButton *button = qobject_cast<QPushButton *>(sender());
338 QMap<int, QPushButton *>::const_iterator it, end;
339 end = m_buttons.constEnd();
340 for (it = m_buttons.begin(); it != end; ++it) {
341 if (it.value() == button)
346 if (m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() == idx &&
347 OutputPanePlaceHolder::isCurrentVisible()
348 && OutputPanePlaceHolder::getCurrent()->closeable()) {
349 // we should toggle and the page is already visible and we are actually closeable
356 void OutputPaneManager::slotNext()
358 int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt();
359 ensurePageVisible(idx);
360 IOutputPane *out = m_pageMap.value(idx);
365 void OutputPaneManager::slotPrev()
367 int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt();
368 ensurePageVisible(idx);
369 IOutputPane *out = m_pageMap.value(idx);
370 if (out->canPrevious())
374 void OutputPaneManager::slotHide()
376 if (OutputPanePlaceHolder::getCurrent()) {
377 OutputPanePlaceHolder::getCurrent()->setVisible(false);
378 int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt();
379 if (m_buttons.value(idx))
380 m_buttons.value(idx)->setChecked(false);
381 if (IEditor *editor = Core::EditorManager::instance()->currentEditor())
382 editor->widget()->setFocus();
386 int OutputPaneManager::findIndexForPage(IOutputPane *out)
392 QMap<int, IOutputPane*>::const_iterator it = m_pageMap.constBegin();
393 while (it != m_pageMap.constEnd()) {
394 if (it.value() == out) {
395 stackIndex = it.key();
401 return m_widgetComboBox->findData(stackIndex);
406 void OutputPaneManager::ensurePageVisible(int idx)
408 if (m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() != idx) {
409 m_widgetComboBox->setCurrentIndex(m_widgetComboBox->findData(idx));
415 void OutputPaneManager::updateNavigateState()
417 IOutputPane* pane = qobject_cast<IOutputPane*>(sender());
418 int idx = findIndexForPage(pane);
419 if (m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() == idx) {
420 m_prevAction->setEnabled(pane->canNavigate() && pane->canPrevious());
421 m_nextAction->setEnabled(pane->canNavigate() && pane->canNext());
425 // Slot connected to showPage signal of each page
426 void OutputPaneManager::showPage(bool focus)
428 int idx = findIndexForPage(qobject_cast<IOutputPane*>(sender()));
429 showPage(idx, focus);
432 void OutputPaneManager::showPage(int idx, bool focus)
434 IOutputPane *out = m_pageMap.value(idx);
436 if (!OutputPanePlaceHolder::getCurrent()) {
437 // In this mode we don't have a placeholder
438 // switch to the output mode and switch the page
439 ICore::instance()->modeManager()->activateMode(Constants::MODE_EDIT);
441 if (OutputPanePlaceHolder::getCurrent()) {
442 // make the page visible
443 OutputPanePlaceHolder::getCurrent()->setVisible(true);
444 ensurePageVisible(idx);
445 if (focus && out->canFocus())
451 void OutputPaneManager::togglePage(bool focus)
453 int idx = findIndexForPage(qobject_cast<IOutputPane*>(sender()));
454 if (OutputPanePlaceHolder::isCurrentVisible()
455 && m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() == idx) {
458 showPage(idx, focus);
462 void OutputPaneManager::setCloseable(bool b)
464 m_closeButton->setVisible(b);
467 bool OutputPaneManager::closeable()
469 return m_closeButton->isVisibleTo(m_closeButton->parentWidget());
472 void OutputPaneManager::focusInEvent(QFocusEvent *e)
474 if (m_outputWidgetPane->currentWidget())
475 m_outputWidgetPane->currentWidget()->setFocus(e->reason());
478 void OutputPaneManager::changePage()
480 if (m_outputWidgetPane->count() <= 0)
483 if (!m_pageMap.contains(m_lastIndex)) {
484 int idx = m_outputWidgetPane->currentIndex();
485 m_pageMap.value(idx)->visibilityChanged(true);
486 if (m_buttons.value(idx)) {
487 m_buttons.value(idx)->setChecked(OutputPanePlaceHolder::isCurrentVisible());
493 int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt();
494 if (m_lastIndex != idx) {
495 m_outputWidgetPane->setCurrentIndex(idx);
496 m_opToolBarWidgets->setCurrentIndex(idx);
497 m_pageMap.value(idx)->visibilityChanged(true);
498 m_pageMap.value(m_lastIndex)->visibilityChanged(false);
500 bool canNavigate = m_pageMap.value(idx)->canNavigate();
501 m_prevAction->setEnabled(canNavigate && m_pageMap.value(idx)->canPrevious());
502 m_nextAction->setEnabled(canNavigate && m_pageMap.value(idx)->canNext());
505 if (m_buttons.value(m_lastIndex))
506 m_buttons.value(m_lastIndex)->setChecked(false);
508 if (m_buttons.value(idx))
509 m_buttons.value(idx)->setChecked(OutputPanePlaceHolder::isCurrentVisible());
514 void OutputPaneManager::clearPage()
516 if (m_pageMap.contains(m_outputWidgetPane->currentIndex()))
517 m_pageMap.value(m_outputWidgetPane->currentIndex())->clearContents();
521 OutputPaneToggleButton::OutputPaneToggleButton(int number, const QString &text,
522 QAction *action, QWidget *parent)
523 : QPushButton(parent)
524 , m_number(QString::number(number))
528 setFocusPolicy(Qt::NoFocus);
531 "QPushButton { border-image: url(:/core/images/panel_button.png) 2 2 2 19;"
532 " border-width: 2px 2px 2px 19px; padding-left: -17; padding-right: 4 } "
533 "QPushButton:checked { border-image: url(:/core/images/panel_button_checked.png) 2 2 2 19 } "
534 "QPushButton::menu-indicator { width:0; height:0 }"
535 #ifndef Q_WS_MAC // Mac UIs usually don't hover
536 "QPushButton:checked:hover { border-image: url(:/core/images/panel_button_checked_hover.png) 2 2 2 19 } "
537 "QPushButton:pressed:hover { border-image: url(:/core/images/panel_button_pressed.png) 2 2 2 19 } "
538 "QPushButton:hover { border-image: url(:/core/images/panel_button_hover.png) 2 2 2 19 } "
542 connect(m_action, SIGNAL(changed()), this, SLOT(updateToolTip()));
545 void OutputPaneToggleButton::updateToolTip()
548 setToolTip(m_action->toolTip());
551 QSize OutputPaneToggleButton::sizeHint() const
555 QSize s = fontMetrics().size(Qt::TextSingleLine, m_text);
557 // Expand to account for border image set by stylesheet above
558 s.rwidth() += 19 + 5 + 2;
559 s.rheight() += 2 + 2;
561 return s.expandedTo(QApplication::globalStrut());
564 void OutputPaneToggleButton::paintEvent(QPaintEvent *event)
566 // For drawing the style sheet stuff
567 QPushButton::paintEvent(event);
569 const QFontMetrics fm = fontMetrics();
570 const int baseLine = (height() - fm.height() + 1) / 2 + fm.ascent();
571 const int numberWidth = fm.width(m_number);
576 p.drawText((20 - numberWidth) / 2, baseLine, m_number);
580 p.drawText(leftPart, baseLine, fm.elidedText(m_text, Qt::ElideRight, width() - leftPart - 1));
583 } // namespace Internal