OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / coreplugin / outputpanemanager.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
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.
18 **
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.
22 **
23 ** Other Usage
24 **
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.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "outputpanemanager.h"
34 #include "outputpane.h"
35 #include "coreconstants.h"
36 #include "findplaceholder.h"
37
38 #include "coreconstants.h"
39 #include "icore.h"
40 #include "ioutputpane.h"
41 #include "mainwindow.h"
42 #include "modemanager.h"
43
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>
51
52 #include <extensionsystem/pluginmanager.h>
53
54 #include <utils/styledbar.h>
55 #include <utils/qtcassert.h>
56
57 #include <QtCore/QDebug>
58
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>
69
70 namespace Core {
71 namespace Internal {
72
73 ////
74 // OutputPaneManager
75 ////
76
77 static OutputPaneManager *m_instance = 0;
78
79 void OutputPaneManager::create()
80 {
81    m_instance = new OutputPaneManager;
82 }
83
84 void OutputPaneManager::destroy()
85 {
86     delete m_instance;
87     m_instance = 0;
88 }
89
90 OutputPaneManager *OutputPaneManager::instance()
91 {
92     return m_instance;
93 }
94
95 void OutputPaneManager::updateStatusButtons(bool visible)
96 {
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());
102 }
103
104 OutputPaneManager::OutputPaneManager(QWidget *parent) :
105     QWidget(parent),
106     m_widgetComboBox(new QComboBox),
107     m_clearButton(new QToolButton),
108     m_closeButton(new QToolButton),
109     m_minMaxAction(0),
110     m_minMaxButton(new QToolButton),
111     m_nextAction(0),
112     m_prevAction(0),
113     m_lastIndex(-1),
114     m_outputWidgetPane(new QStackedWidget),
115     m_opToolBarWidgets(new QStackedWidget),
116     m_minimizeIcon(":/core/images/arrowdown.png"),
117     m_maximizeIcon(":/core/images/arrowup.png"),
118     m_maximised(false)
119 {
120     setWindowTitle(tr("Output"));
121     connect(m_widgetComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changePage()));
122
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()));
126
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()));
131
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()));
136
137     m_minMaxAction = new QAction(this);
138     m_minMaxAction->setIcon(m_maximizeIcon);
139     m_minMaxAction->setText(tr("Maximize Output Pane"));
140
141     m_closeButton->setIcon(QIcon(QLatin1String(Constants::ICON_CLOSE)));
142     connect(m_closeButton, SIGNAL(clicked()), this, SLOT(slotHide()));
143
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);
164
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);
169
170 }
171
172 OutputPaneManager::~OutputPaneManager()
173 {
174 }
175
176 QWidget *OutputPaneManager::buttonsWidget()
177 {
178     return m_buttonsWidget;
179 }
180
181 // Return shortcut as Ctrl+<number>
182 static inline int paneShortCut(int number)
183 {
184 #ifdef Q_WS_MAC
185     int modifier = Qt::CTRL;
186 #else
187     int modifier = Qt::ALT;
188 #endif
189     return modifier | (Qt::Key_0 + number);
190 }
191
192 void OutputPaneManager::init()
193 {
194     ActionManager *am = Core::ICore::instance()->actionManager();
195     ActionContainer *mwindow = am->actionContainer(Constants::M_WINDOW);
196     const Context globalcontext(Core::Constants::C_GLOBAL);
197
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");
204
205     Core::Command *cmd;
206
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");
211
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");
216
217     cmd = am->registerAction(m_minMaxAction, "Coreplugin.OutputPane.minmax", globalcontext);
218 #ifdef Q_WS_MAC
219     cmd->setDefaultKeySequence(QKeySequence("Ctrl+9"));
220 #else
221     cmd->setDefaultKeySequence(QKeySequence("Alt+9"));
222 #endif
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());
228
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");
233
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);
239
240     QMultiMap<int, IOutputPane*>::const_iterator it, begin;
241     begin = sorted.constBegin();
242     it = sorted.constEnd();
243     int shortcutNumber = 1;
244     while (it != begin) {
245         --it;
246         IOutputPane* outPane = it.value();
247         const int idx = m_outputWidgetPane->addWidget(outPane->outputWidget(this));
248
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()));
254
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);
263
264         m_opToolBarWidgets->addWidget(toolButtonsContainer);
265
266         QString actionId = QString("QtCreator.Pane.%1").arg(outPane->displayName().simplified());
267         actionId.remove(QLatin1Char(' '));
268         QAction *action = new QAction(outPane->displayName(), this);
269
270         Command *cmd = am->registerAction(action, actionId, Context(Constants::C_GLOBAL));
271
272         mpanes->addAction(cmd, "Coreplugin.OutputPane.PanesGroup");
273         m_actions.insert(cmd->action(), idx);
274
275         if (outPane->priorityInStatusBar() != -1) {
276             cmd->setDefaultKeySequence(QKeySequence(paneShortCut(shortcutNumber)));
277             QPushButton *button = new OutputPaneToggleButton(shortcutNumber, outPane->displayName(),
278                                                              cmd->action());
279             ++shortcutNumber;
280             m_buttonsWidget->layout()->addWidget(button);
281             connect(button, SIGNAL(clicked()), this, SLOT(buttonTriggered()));
282             m_buttons.insert(idx, button);
283         }
284
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);
287
288         connect(cmd->action(), SIGNAL(triggered()), this, SLOT(shortcutTriggered()));
289     }
290
291     changePage();
292 }
293
294 void OutputPaneManager::shortcutTriggered()
295 {
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();
309             else
310                 slotHide();
311         } else {
312             outputPane->popup(true);
313         }
314     }
315 }
316
317 bool OutputPaneManager::isMaximized()const
318 {
319     return m_maximised;
320 }
321
322 void OutputPaneManager::slotMinMax()
323 {
324     QTC_ASSERT(OutputPanePlaceHolder::getCurrent(), return);
325
326     if (!OutputPanePlaceHolder::getCurrent()->isVisible()) // easier than disabling/enabling the action
327         return;
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"));
333 }
334
335 void OutputPaneManager::buttonTriggered()
336 {
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)
342             break;
343     }
344     int idx = it.key();
345
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
350         slotHide();
351     } else {
352         showPage(idx, true);
353     }
354 }
355
356 void OutputPaneManager::slotNext()
357 {
358     int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt();
359     ensurePageVisible(idx);
360     IOutputPane *out = m_pageMap.value(idx);
361     if (out->canNext())
362         out->goToNext();
363 }
364
365 void OutputPaneManager::slotPrev()
366 {
367     int idx = m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt();
368     ensurePageVisible(idx);
369     IOutputPane *out = m_pageMap.value(idx);
370     if (out->canPrevious())
371         out->goToPrev();
372 }
373
374 void OutputPaneManager::slotHide()
375 {
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();
383     }
384 }
385
386 int OutputPaneManager::findIndexForPage(IOutputPane *out)
387 {
388     if (!out)
389         return -1;
390
391     int stackIndex = -1;
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();
396             break;
397         }
398         ++it;
399     }
400     if (stackIndex > -1)
401         return m_widgetComboBox->findData(stackIndex);
402     else
403         return -1;
404 }
405
406 void OutputPaneManager::ensurePageVisible(int idx)
407 {
408     if (m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() != idx) {
409         m_widgetComboBox->setCurrentIndex(m_widgetComboBox->findData(idx));
410     } else {
411         changePage();
412     }
413 }
414
415 void OutputPaneManager::updateNavigateState()
416 {
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());
422     }
423 }
424
425 // Slot connected to showPage signal of each page
426 void OutputPaneManager::showPage(bool focus)
427 {
428     int idx = findIndexForPage(qobject_cast<IOutputPane*>(sender()));
429     showPage(idx, focus);
430 }
431
432 void OutputPaneManager::showPage(int idx, bool focus)
433 {
434     IOutputPane *out = m_pageMap.value(idx);
435     if (idx > -1) {
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);
440         }
441         if (OutputPanePlaceHolder::getCurrent()) {
442             // make the page visible
443             OutputPanePlaceHolder::getCurrent()->setVisible(true);
444             ensurePageVisible(idx);
445             if (focus && out->canFocus())
446                 out->setFocus();
447         }
448     }
449 }
450
451 void OutputPaneManager::togglePage(bool focus)
452 {
453     int idx = findIndexForPage(qobject_cast<IOutputPane*>(sender()));
454     if (OutputPanePlaceHolder::isCurrentVisible()
455        && m_widgetComboBox->itemData(m_widgetComboBox->currentIndex()).toInt() == idx) {
456          slotHide();
457     } else {
458          showPage(idx, focus);
459     }
460 }
461
462 void OutputPaneManager::setCloseable(bool b)
463 {
464     m_closeButton->setVisible(b);
465 }
466
467 bool OutputPaneManager::closeable()
468 {
469     return m_closeButton->isVisibleTo(m_closeButton->parentWidget());
470 }
471
472 void OutputPaneManager::focusInEvent(QFocusEvent *e)
473 {
474     if (m_outputWidgetPane->currentWidget())
475         m_outputWidgetPane->currentWidget()->setFocus(e->reason());
476 }
477
478 void OutputPaneManager::changePage()
479 {
480     if (m_outputWidgetPane->count() <= 0)
481         return;
482
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());
488         }
489         m_lastIndex = idx;
490         return;
491     }
492
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);
499
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());
503     }
504
505     if (m_buttons.value(m_lastIndex))
506         m_buttons.value(m_lastIndex)->setChecked(false);
507
508     if (m_buttons.value(idx))
509         m_buttons.value(idx)->setChecked(OutputPanePlaceHolder::isCurrentVisible());
510
511     m_lastIndex = idx;
512 }
513
514 void OutputPaneManager::clearPage()
515 {
516     if (m_pageMap.contains(m_outputWidgetPane->currentIndex()))
517         m_pageMap.value(m_outputWidgetPane->currentIndex())->clearContents();
518 }
519
520
521 OutputPaneToggleButton::OutputPaneToggleButton(int number, const QString &text,
522                                                QAction *action, QWidget *parent)
523     : QPushButton(parent)
524     , m_number(QString::number(number))
525     , m_text(text)
526     , m_action(action)
527 {
528     setFocusPolicy(Qt::NoFocus);
529     setCheckable(true);
530     setStyleSheet(
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 } "
539 #endif
540             );
541     if (m_action)
542         connect(m_action, SIGNAL(changed()), this, SLOT(updateToolTip()));
543 }
544
545 void OutputPaneToggleButton::updateToolTip()
546 {
547     Q_ASSERT(m_action);
548     setToolTip(m_action->toolTip());
549 }
550
551 QSize OutputPaneToggleButton::sizeHint() const
552 {
553     ensurePolished();
554
555     QSize s = fontMetrics().size(Qt::TextSingleLine, m_text);
556
557     // Expand to account for border image set by stylesheet above
558     s.rwidth() += 19 + 5 + 2;
559     s.rheight() += 2 + 2;
560
561     return s.expandedTo(QApplication::globalStrut());
562 }
563
564 void OutputPaneToggleButton::paintEvent(QPaintEvent *event)
565 {
566     // For drawing the style sheet stuff
567     QPushButton::paintEvent(event);
568
569     const QFontMetrics fm = fontMetrics();
570     const int baseLine = (height() - fm.height() + 1) / 2 + fm.ascent();
571     const int numberWidth = fm.width(m_number);
572
573     QPainter p(this);
574     p.setFont(font());
575     p.setPen(Qt::white);
576     p.drawText((20 - numberWidth) / 2, baseLine, m_number);
577     if (!isChecked())
578         p.setPen(Qt::black);
579     int leftPart = 22;
580     p.drawText(leftPart, baseLine, fm.elidedText(m_text, Qt::ElideRight, width() - leftPart - 1));
581 }
582
583 } // namespace Internal
584 } // namespace Core
585
586