OSDN Git Service

Merge remote branch 'origin/1.3'
[qt-creator-jp/qt-creator-jp.git] / src / plugins / projectexplorer / outputwindow.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "outputwindow.h"
31 #include "projectexplorerconstants.h"
32 #include "runconfiguration.h"
33
34 #include <coreplugin/actionmanager/actionmanager.h>
35 #include <coreplugin/coreconstants.h>
36 #include <coreplugin/icore.h>
37 #include <coreplugin/uniqueidmanager.h>
38 #include <find/basetextfind.h>
39 #include <aggregation/aggregate.h>
40
41 #include <QtGui/QIcon>
42 #include <QtGui/QScrollBar>
43 #include <QtGui/QTextLayout>
44 #include <QtGui/QPainter>
45 #include <QtGui/QApplication>
46 #include <QtGui/QClipboard>
47 #include <QtGui/QMenu>
48 #include <QtGui/QMessageBox>
49 #include <QtGui/QVBoxLayout>
50 #include <QtGui/QTabWidget>
51
52 #include <QtGui/QApplication>
53
54 using namespace ProjectExplorer::Internal;
55 using namespace ProjectExplorer;
56
57 static const int MaxBlockCount = 100000;
58
59 OutputPane::OutputPane()
60     : m_mainWidget(new QWidget)
61 {
62 //     m_insertLineButton = new QToolButton;
63 //     m_insertLineButton->setIcon(QIcon(ProjectExplorer::Constants::ICON_INSERT_LINE));
64 //     m_insertLineButton->setText(tr("Insert line"));
65 //     m_insertLineButton->setToolTip(tr("Insert line"));
66 //     m_insertLineButton->setAutoRaise(true);
67 //     connect(m_insertLineButton, SIGNAL(clicked()), this, SLOT(insertLine()));
68
69     QIcon runIcon(Constants::ICON_RUN);
70     runIcon.addFile(Constants::ICON_RUN_SMALL);
71
72     // Rerun
73     m_reRunButton = new QToolButton;
74     m_reRunButton->setIcon(runIcon);
75     m_reRunButton->setToolTip(tr("Re-run this run-configuration"));
76     m_reRunButton->setAutoRaise(true);
77     m_reRunButton->setEnabled(false);
78     connect(m_reRunButton, SIGNAL(clicked()),
79             this, SLOT(reRunRunControl()));
80
81     // Stop
82     Core::ActionManager *am = Core::ICore::instance()->actionManager();
83     QList<int> globalcontext;
84     globalcontext.append(Core::Constants::C_GLOBAL_ID);
85
86     m_stopAction = new QAction(QIcon(Constants::ICON_STOP), tr("Stop"), this);
87     m_stopAction->setToolTip(tr("Stop"));
88     m_stopAction->setEnabled(false);
89
90     Core::Command *cmd = am->registerAction(m_stopAction, Constants::STOP, globalcontext);
91     cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+R")));
92
93     m_stopButton = new QToolButton;
94     m_stopButton->setDefaultAction(cmd->action());
95     m_stopButton->setAutoRaise(true);
96
97     connect(m_stopAction, SIGNAL(triggered()),
98             this, SLOT(stopRunControl()));
99
100     // Spacer (?)
101
102     QVBoxLayout *layout = new QVBoxLayout;
103     layout->setMargin(0);
104     m_tabWidget = new QTabWidget;
105     m_tabWidget->setDocumentMode(true);
106     m_tabWidget->setTabsClosable(true);
107     m_tabWidget->setMovable(true);
108     connect(m_tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
109     layout->addWidget(m_tabWidget);
110
111     connect(m_tabWidget, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
112
113     m_mainWidget->setLayout(layout);
114
115     connect(Core::ICore::instance(), SIGNAL(coreAboutToClose()),
116             this, SLOT(coreAboutToClose()));
117 }
118
119 void OutputPane::coreAboutToClose()
120 {
121     while (m_tabWidget->count()) {
122         RunControl *rc = runControlForTab(0);
123         if (rc->isRunning())
124             rc->stop();
125         closeTab(0);
126     }
127 }
128
129 OutputPane::~OutputPane()
130 {
131     delete m_mainWidget;
132 }
133
134 QWidget *OutputPane::outputWidget(QWidget *)
135 {
136     return m_mainWidget;
137 }
138
139 QList<QWidget*> OutputPane::toolBarWidgets() const
140 {
141     return QList<QWidget*>() << m_reRunButton << m_stopButton
142             ; // << m_insertLineButton;
143 }
144
145 QString OutputPane::name() const
146 {
147     return tr("Application Output");
148 }
149
150 int OutputPane::priorityInStatusBar() const
151 {
152     return 60;
153 }
154
155 void OutputPane::clearContents()
156 {
157     OutputWindow *currentWindow = qobject_cast<OutputWindow *>(m_tabWidget->currentWidget());
158     if (currentWindow)
159         currentWindow->clear();
160 }
161
162 void OutputPane::visibilityChanged(bool /* b */)
163 {
164 }
165
166 bool OutputPane::hasFocus()
167 {
168     return m_tabWidget->currentWidget() && m_tabWidget->currentWidget()->hasFocus();
169 }
170
171 bool OutputPane::canFocus()
172 {
173     return m_tabWidget->currentWidget();
174 }
175
176 void OutputPane::setFocus()
177 {
178     if (m_tabWidget->currentWidget())
179         m_tabWidget->currentWidget()->setFocus();
180 }
181
182 void OutputPane::appendOutput(const QString &/*out*/)
183 {
184     // This function is in the interface, since we can't do anything sensible here, we don't do anything here.
185 }
186
187 void OutputPane::createNewOutputWindow(RunControl *rc)
188 {
189     connect(rc, SIGNAL(started()),
190             this, SLOT(runControlStarted()));
191     connect(rc, SIGNAL(finished()),
192             this, SLOT(runControlFinished()));
193
194     // First look if we can reuse a tab
195     bool found = false;
196     for (int i = 0; i < m_tabWidget->count(); ++i) {
197         RunControl *old = runControlForTab(i);
198         if (old->sameRunConfiguration(rc) && !old->isRunning()) {
199             // Reuse this tab
200             delete old;
201             m_outputWindows.remove(old);
202             OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(i));
203             ow->grayOutOldContent();
204             ow->verticalScrollBar()->setValue(ow->verticalScrollBar()->maximum());
205             m_outputWindows.insert(rc, ow);
206             found = true;
207             break;
208         }
209     }
210     if (!found) {
211         OutputWindow *ow = new OutputWindow(m_tabWidget);
212         Aggregation::Aggregate *agg = new Aggregation::Aggregate;
213         agg->add(ow);
214         agg->add(new Find::BaseTextFind(ow));
215         m_outputWindows.insert(rc, ow);
216         m_tabWidget->addTab(ow, rc->displayName());
217     }
218 }
219
220 void OutputPane::appendOutput(RunControl *rc, const QString &out)
221 {
222     OutputWindow *ow = m_outputWindows.value(rc);
223     ow->appendOutput(out);
224 }
225
226 void OutputPane::appendOutputInline(RunControl *rc, const QString &out)
227 {
228     OutputWindow *ow = m_outputWindows.value(rc);
229     ow->appendOutputInline(out);
230 }
231
232 void OutputPane::showTabFor(RunControl *rc)
233 {
234     OutputWindow *ow = m_outputWindows.value(rc);
235     m_tabWidget->setCurrentWidget(ow);
236 }
237
238 void OutputPane::insertLine()
239 {
240     OutputWindow *currentWindow = qobject_cast<OutputWindow *>(m_tabWidget->currentWidget());
241     if (currentWindow)
242         currentWindow->clear();
243 }
244
245 void OutputPane::reRunRunControl()
246 {
247     int index = m_tabWidget->currentIndex();
248     RunControl *rc = runControlForTab(index);
249     OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(index));
250     ow->grayOutOldContent();
251     ow->verticalScrollBar()->setValue(ow->verticalScrollBar()->maximum());
252     rc->start();
253 }
254
255 void OutputPane::stopRunControl()
256 {
257     RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
258     rc->stop();
259 }
260
261 void OutputPane::closeTab(int index)
262 {
263     OutputWindow *ow = static_cast<OutputWindow *>(m_tabWidget->widget(index));
264     RunControl *rc = m_outputWindows.key(ow);
265
266     if (rc->isRunning()) {
267         QString msg = tr("The application is still running. Close it first.");
268         QMessageBox::critical(0, tr("Unable to close"), msg);
269         return;
270     }
271
272     m_tabWidget->removeTab(index);
273     delete ow;
274     delete rc;
275 }
276
277 void OutputPane::projectRemoved()
278 {
279     tabChanged(m_tabWidget->currentIndex());
280 }
281
282 void OutputPane::tabChanged(int i)
283 {
284     if (i == -1) {
285         m_stopAction->setEnabled(false);
286         m_reRunButton->setEnabled(false);
287     } else {
288         RunControl *rc = runControlForTab(i);
289         m_stopAction->setEnabled(rc->isRunning());
290         m_reRunButton->setEnabled(!rc->isRunning());
291     }
292 }
293
294 void OutputPane::runControlStarted()
295 {
296     RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
297     if (rc == qobject_cast<RunControl *>(sender())) {
298         m_reRunButton->setEnabled(false);
299         m_stopAction->setEnabled(true);
300     }
301 }
302
303 void OutputPane::runControlFinished()
304 {
305     RunControl *rc = runControlForTab(m_tabWidget->currentIndex());
306     if (rc == qobject_cast<RunControl *>(sender())) {
307         m_reRunButton->setEnabled(rc);
308         m_stopAction->setEnabled(false);
309     }
310 }
311
312 RunControl* OutputPane::runControlForTab(int index) const
313 {
314     return m_outputWindows.key(qobject_cast<OutputWindow *>(m_tabWidget->widget(index)));
315 }
316
317 bool OutputPane::canNext()
318 {
319     return false;
320 }
321
322 bool OutputPane::canPrevious()
323 {
324     return false;
325 }
326
327 void OutputPane::goToNext()
328 {
329
330 }
331
332 void OutputPane::goToPrev()
333 {
334
335 }
336
337 bool OutputPane::canNavigate()
338 {
339     return false;
340 }
341
342 /*******************/
343
344 OutputWindow::OutputWindow(QWidget *parent)
345     : QPlainTextEdit(parent)
346 {
347     m_enforceNewline = false;
348     m_scrollToBottom = false;
349
350     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
351     //setCenterOnScroll(false);
352     setWindowTitle(tr("Application Output Window"));
353     setWindowIcon(QIcon(":/qt4projectmanager/images/window.png"));
354     setFrameShape(QFrame::NoFrame);
355
356     static uint usedIds = 0;
357     Core::ICore *core = Core::ICore::instance();
358     QList<int> context;
359     context << core->uniqueIDManager()->uniqueIdentifier(QString(Constants::C_APP_OUTPUT) + QString().setNum(usedIds++));
360     m_outputWindowContext = new Core::BaseContext(this, context);
361     core->addContextObject(m_outputWindowContext);
362
363     QAction *undoAction = new QAction(this);
364     QAction *redoAction = new QAction(this);
365     QAction *cutAction = new QAction(this);
366     QAction *copyAction = new QAction(this);
367     QAction *pasteAction = new QAction(this);
368     QAction *selectAllAction = new QAction(this);
369
370     core->actionManager()->registerAction(undoAction, Core::Constants::UNDO, context);
371     core->actionManager()->registerAction(redoAction, Core::Constants::REDO, context);
372     core->actionManager()->registerAction(cutAction, Core::Constants::CUT, context);
373     core->actionManager()->registerAction(copyAction, Core::Constants::COPY, context);
374     core->actionManager()->registerAction(pasteAction, Core::Constants::PASTE, context);
375     core->actionManager()->registerAction(selectAllAction, Core::Constants::SELECTALL, context);
376
377     connect(undoAction, SIGNAL(triggered()), this, SLOT(undo()));
378     connect(redoAction, SIGNAL(triggered()), this, SLOT(redo()));
379     connect(cutAction, SIGNAL(triggered()), this, SLOT(cut()));
380     connect(copyAction, SIGNAL(triggered()), this, SLOT(copy()));
381     connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
382     connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll()));
383
384     connect(this, SIGNAL(undoAvailable(bool)), undoAction, SLOT(setEnabled(bool)));
385     connect(this, SIGNAL(redoAvailable(bool)), redoAction, SLOT(setEnabled(bool)));
386     connect(this, SIGNAL(copyAvailable(bool)), cutAction, SLOT(setEnabled(bool)));  // OutputWindow never read-only
387     connect(this, SIGNAL(copyAvailable(bool)), copyAction, SLOT(setEnabled(bool)));
388
389     undoAction->setEnabled(false);
390     redoAction->setEnabled(false);
391     cutAction->setEnabled(false);
392     copyAction->setEnabled(false);
393 }
394
395 OutputWindow::~OutputWindow()
396 {
397     Core::ICore::instance()->removeContextObject(m_outputWindowContext);
398     delete m_outputWindowContext;
399 }
400
401 void OutputWindow::showEvent(QShowEvent *e)
402 {
403     QPlainTextEdit::showEvent(e);
404     if (m_scrollToBottom) {
405         verticalScrollBar()->setValue(verticalScrollBar()->maximum());
406     }
407     m_scrollToBottom = false;
408 }
409
410 void OutputWindow::appendOutput(const QString &out)
411 {
412     m_scrollToBottom = true;
413     QString s = out;
414     m_enforceNewline = true; // make appendOutputInline put in a newline next time
415     if (s.endsWith(QLatin1Char('\n'))) {
416         s.chop(1);
417     }
418     setMaximumBlockCount(MaxBlockCount);
419     appendPlainText(out);
420     enableUndoRedo();
421 }
422
423
424 void OutputWindow::appendOutputInline(const QString &out)
425 {
426     m_scrollToBottom = true;
427     setMaximumBlockCount(MaxBlockCount);
428
429     int newline = -1;
430     bool enforceNewline = m_enforceNewline;
431     m_enforceNewline = false;
432
433     if (!enforceNewline) {
434         newline = out.indexOf(QLatin1Char('\n'));
435         moveCursor(QTextCursor::End);
436         bool atBottom = (blockBoundingRect(document()->lastBlock()).bottom() + contentOffset().y()
437                          <= viewport()->rect().bottom());
438         insertPlainText(newline < 0 ? out : out.left(newline)); // doesn't enforce new paragraph like appendPlainText
439         if (atBottom)
440             verticalScrollBar()->setValue(verticalScrollBar()->maximum());
441     }
442
443     QString s = out.mid(newline+1);
444     if (s.isEmpty()) {
445         m_enforceNewline = true;
446     } else {
447         if (s.endsWith(QLatin1Char('\n'))) {
448             m_enforceNewline = true;
449             s.chop(1);
450         }
451         appendPlainText(s);
452     }
453
454     enableUndoRedo();
455 }
456
457 void OutputWindow::insertLine()
458 {
459     m_scrollToBottom = true;
460     setMaximumBlockCount(MaxBlockCount);
461     appendPlainText(QString());
462     enableUndoRedo();
463 }
464
465 void OutputWindow::grayOutOldContent()
466 {
467     QTextCursor cursor = textCursor();
468     cursor.select(QTextCursor::Document);
469     QTextBlockFormat tbf;
470     const QColor bkgColor = palette().window().color();
471     const QColor fgdColor = palette().windowText().color();
472     tbf.setBackground(QColor((0.5 * bkgColor.red() + 0.5* fgdColor.red()),\
473                              (0.5 * bkgColor.green() + 0.5* fgdColor.green()),\
474                              (0.5 * bkgColor.blue() + 0.5* fgdColor.blue()) ));
475     cursor.mergeBlockFormat(tbf);
476
477     cursor.movePosition(QTextCursor::End);
478     cursor.insertBlock(QTextBlockFormat());
479 }
480
481 void OutputWindow::enableUndoRedo()
482 {
483     setMaximumBlockCount(0);
484     setUndoRedoEnabled(true);
485 }