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 "taskwindow.h"
35 #include "itaskhandler.h"
36 #include "projectexplorerconstants.h"
40 #include <coreplugin/actionmanager/actionmanager.h>
41 #include <coreplugin/actionmanager/command.h>
42 #include <coreplugin/coreconstants.h>
43 #include <coreplugin/icontext.h>
44 #include <coreplugin/icore.h>
45 #include <extensionsystem/pluginmanager.h>
46 #include <qt4projectmanager/qt4projectmanagerconstants.h>
48 #include <QtCore/QDir>
49 #include <QtCore/QFileInfo>
50 #include <QtGui/QApplication>
51 #include <QtGui/QClipboard>
52 #include <QtGui/QKeyEvent>
53 #include <QtGui/QListView>
54 #include <QtGui/QPainter>
55 #include <QtGui/QStyledItemDelegate>
56 #include <QtGui/QSortFilterProxyModel>
57 #include <QtGui/QMenu>
58 #include <QtGui/QToolButton>
61 const int ELLIPSIS_GRADIENT_WIDTH = 16;
64 namespace ProjectExplorer {
67 class TaskView : public QListView
70 TaskView(QWidget *parent = 0);
72 void resizeEvent(QResizeEvent *e);
73 void keyPressEvent(QKeyEvent *e);
76 class TaskWindowContext : public Core::IContext
79 TaskWindowContext(QWidget *widget);
80 virtual Core::Context context() const;
81 virtual QWidget *widget();
84 const Core::Context m_context;
87 class TaskModel : public QAbstractItemModel
92 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
93 QModelIndex parent(const QModelIndex &child) const;
94 int rowCount(const QModelIndex &parent = QModelIndex()) const;
95 int columnCount(const QModelIndex &parent = QModelIndex()) const;
96 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
97 Task task(const QModelIndex &index) const;
99 QStringList categoryIds() const;
100 QString categoryDisplayName(const QString &categoryId) const;
101 void addCategory(const QString &categoryId, const QString &categoryName);
103 QList<Task> tasks(const QString &categoryId = QString()) const;
104 void addTask(const Task &task);
105 void removeTask(const Task &task);
106 void clearTasks(const QString &categoryId = QString());
108 int sizeOfFile(const QFont &font);
109 int sizeOfLineNumber(const QFont &font);
110 void setFileNotFound(const QModelIndex &index, bool b);
112 enum Roles { File = Qt::UserRole, Line, Description, FileNotFound, Type, Category, Icon, Task_t };
114 QIcon taskTypeIcon(Task::TaskType t) const;
117 int errorTaskCount();
119 bool hasFile(const QModelIndex &index) const;
122 QHash<QString,QString> m_categories; // category id -> display name
123 QList<Task> m_tasks; // all tasks (in order of insertion)
124 QMap<QString,QList<Task> > m_tasksInCategory; // categoryId->tasks
126 QHash<QString,bool> m_fileNotFound;
127 int m_maxSizeOfFileName;
128 QString m_fileMeasurementFont;
129 const QIcon m_errorIcon;
130 const QIcon m_warningIcon;
132 int m_errorTaskCount;
133 int m_sizeOfLineNumber;
134 QString m_lineMeasurementFont;
137 class TaskFilterModel : public QSortFilterProxyModel
140 TaskFilterModel(TaskModel *sourceModel, QObject *parent = 0);
142 TaskModel *taskModel() const;
144 bool filterIncludesUnknowns() const { return m_includeUnknowns; }
145 void setFilterIncludesUnknowns(bool b) { m_includeUnknowns = b; invalidateFilter(); }
147 bool filterIncludesWarnings() const { return m_includeWarnings; }
148 void setFilterIncludesWarnings(bool b) { m_includeWarnings = b; invalidateFilter(); }
150 bool filterIncludesErrors() const { return m_includeErrors; }
151 void setFilterIncludesErrors(bool b) { m_includeErrors = b; invalidateFilter(); }
153 QStringList filteredCategories() const { return m_categoryIds; }
154 void setFilteredCategories(const QStringList &categoryIds) { m_categoryIds = categoryIds; invalidateFilter(); }
156 Task task(const QModelIndex &index) const
157 { return static_cast<TaskModel *>(sourceModel())->task(mapToSource(index)); }
159 bool hasFile(const QModelIndex &index) const
160 { return static_cast<TaskModel *>(sourceModel())->hasFile(mapToSource(index)); }
163 bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
166 bool m_includeUnknowns;
167 bool m_includeWarnings;
168 bool m_includeErrors;
169 QStringList m_categoryIds;
172 class TaskDelegate : public QStyledItemDelegate
177 TaskDelegate(QObject * parent = 0);
179 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
180 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
182 // TaskView uses this method if the size of the taskview changes
183 void emitSizeHintChanged(const QModelIndex &index);
186 void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
189 void generateGradientPixmap(int width, int height, QColor color, bool selected) const;
193 +----------------------------------------------------------------------------------------------------+
194 | TASKICONAREA TEXTAREA FILEAREA LINEAREA |
195 +----------------------------------------------------------------------------------------------------+
198 +----------------------------------------------------------------------------------------------------+
199 | TASKICONICON TEXTAREA FILEAREA LINEAREA |
200 | more text -------------------------------------------------------------------------> |
201 +----------------------------------------------------------------------------------------------------+
206 Positions(const QStyleOptionViewItemV4 &options, TaskModel *model) :
207 m_totalWidth(options.rect.width()),
208 m_maxFileLength(model->sizeOfFile(options.font)),
209 m_maxLineLength(model->sizeOfLineNumber(options.font)),
210 m_realFileLength(m_maxFileLength),
211 m_top(options.rect.top()),
212 m_bottom(options.rect.bottom())
214 int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING;
215 if (m_maxFileLength > flexibleArea / 2)
216 m_realFileLength = flexibleArea / 2;
217 m_fontHeight = QFontMetrics(options.font).height();
220 int top() const { return m_top + ITEM_MARGIN; }
221 int left() const { return ITEM_MARGIN; }
222 int right() const { return m_totalWidth - ITEM_MARGIN; }
223 int bottom() const { return m_bottom; }
224 int firstLineHeight() const { return m_fontHeight + 1; }
225 int minimumHeight() const { return taskIconHeight() + 2 * ITEM_MARGIN; }
227 int taskIconLeft() const { return left(); }
228 int taskIconWidth() const { return TASK_ICON_SIZE; }
229 int taskIconHeight() const { return TASK_ICON_SIZE; }
230 int taskIconRight() const { return taskIconLeft() + taskIconWidth(); }
231 QRect taskIcon() const { return QRect(taskIconLeft(), top(), taskIconWidth(), taskIconHeight()); }
233 int textAreaLeft() const { return taskIconRight() + ITEM_SPACING; }
234 int textAreaWidth() const { return textAreaRight() - textAreaLeft(); }
235 int textAreaRight() const { return fileAreaLeft() - ITEM_SPACING; }
236 QRect textArea() const { return QRect(textAreaLeft(), top(), textAreaWidth(), firstLineHeight()); }
238 int fileAreaLeft() const { return fileAreaRight() - fileAreaWidth(); }
239 int fileAreaWidth() const { return m_realFileLength; }
240 int fileAreaRight() const { return lineAreaLeft() - ITEM_SPACING; }
241 QRect fileArea() const { return QRect(fileAreaLeft(), top(), fileAreaWidth(), firstLineHeight()); }
243 int lineAreaLeft() const { return lineAreaRight() - lineAreaWidth(); }
244 int lineAreaWidth() const { return m_maxLineLength; }
245 int lineAreaRight() const { return right(); }
246 QRect lineArea() const { return QRect(lineAreaLeft(), top(), lineAreaWidth(), firstLineHeight()); }
252 int m_realFileLength;
257 static const int TASK_ICON_SIZE = 16;
258 static const int ITEM_MARGIN = 2;
259 static const int ITEM_SPACING = 2 * ITEM_MARGIN;
263 TaskView::TaskView(QWidget *parent)
266 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
267 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
270 TaskView::~TaskView()
275 void TaskView::resizeEvent(QResizeEvent *e)
278 static_cast<TaskDelegate *>(itemDelegate())->emitSizeHintChanged(selectionModel()->currentIndex());
281 void TaskView::keyPressEvent(QKeyEvent *e)
283 if (!e->modifiers() && e->key() == Qt::Key_Return) {
284 emit activated(currentIndex());
288 QListView::keyPressEvent(e);
295 TaskModel::TaskModel() :
296 m_maxSizeOfFileName(0),
297 m_errorIcon(QLatin1String(":/projectexplorer/images/compile_error.png")),
298 m_warningIcon(QLatin1String(":/projectexplorer/images/compile_warning.png")),
301 m_sizeOfLineNumber(0)
305 int TaskModel::taskCount()
310 int TaskModel::errorTaskCount()
312 return m_errorTaskCount;
315 bool TaskModel::hasFile(const QModelIndex &index) const
317 int row = index.row();
318 if (!index.isValid() || row < 0 || row >= m_tasks.count())
320 return !m_tasks.at(row).file.isEmpty();
323 QIcon TaskModel::taskTypeIcon(Task::TaskType t) const
327 return m_warningIcon;
336 void TaskModel::addCategory(const QString &categoryId, const QString &categoryName)
338 Q_ASSERT(!categoryId.isEmpty());
339 m_categories.insert(categoryId, categoryName);
342 QList<Task> TaskModel::tasks(const QString &categoryId) const
344 if (categoryId.isEmpty()) {
347 return m_tasksInCategory.value(categoryId);
351 void TaskModel::addTask(const Task &task)
353 Q_ASSERT(m_categories.keys().contains(task.category));
355 if (m_tasksInCategory.contains(task.category)) {
356 m_tasksInCategory[task.category].append(task);
360 m_tasksInCategory.insert(task.category, temp);
363 beginInsertRows(QModelIndex(), m_tasks.size(), m_tasks.size());
364 m_tasks.append(task);
367 m_maxSizeOfFileName = 0;
369 if (task.type == Task::Error)
373 void TaskModel::removeTask(const Task &task)
375 if (m_tasks.contains(task)) {
376 int index = m_tasks.indexOf(task);
377 beginRemoveRows(QModelIndex(), index, index);
378 m_tasks.removeAt(index);
380 if (task.type == Task::Error)
386 void TaskModel::clearTasks(const QString &categoryId)
388 if (categoryId.isEmpty()) {
389 if (m_tasks.size() == 0)
391 beginRemoveRows(QModelIndex(), 0, m_tasks.size() -1);
393 m_tasksInCategory.clear();
395 m_errorTaskCount = 0;
397 m_maxSizeOfFileName = 0;
401 int subErrorTaskCount = 0;
402 while (index < m_tasks.size()) {
403 while (index < m_tasks.size() && m_tasks.at(index).category != categoryId) {
407 if (index == m_tasks.size())
409 while (index < m_tasks.size() && m_tasks.at(index).category == categoryId) {
410 if (m_tasks.at(index).type == Task::Error)
414 // Index is now on the first non category
415 beginRemoveRows(QModelIndex(), start, index - 1);
417 for (int i = start; i < index; ++i) {
418 m_tasksInCategory[categoryId].removeOne(m_tasks.at(i));
421 m_tasks.erase(m_tasks.begin() + start, m_tasks.begin() + index);
423 m_taskCount -= index - start;
424 m_errorTaskCount -= subErrorTaskCount;
429 // what to do with m_maxSizeOfFileName ?
434 QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const
436 if (parent.isValid())
437 return QModelIndex();
438 return createIndex(row, column, 0);
441 QModelIndex TaskModel::parent(const QModelIndex &child) const
444 return QModelIndex();
447 int TaskModel::rowCount(const QModelIndex &parent) const
449 return parent.isValid() ? 0 : m_tasks.count();
452 int TaskModel::columnCount(const QModelIndex &parent) const
454 return parent.isValid() ? 0 : 1;
457 QVariant TaskModel::data(const QModelIndex &index, int role) const
459 if (!index.isValid() || index.row() >= m_tasks.size() || index.column() != 0)
462 if (role == TaskModel::File) {
463 return m_tasks.at(index.row()).file;
464 } else if (role == TaskModel::Line) {
465 if (m_tasks.at(index.row()).line <= 0)
468 return m_tasks.at(index.row()).line;
469 } else if (role == TaskModel::Description) {
470 return m_tasks.at(index.row()).description;
471 } else if (role == TaskModel::FileNotFound) {
472 return m_fileNotFound.value(m_tasks.at(index.row()).file);
473 } else if (role == TaskModel::Type) {
474 return (int)m_tasks.at(index.row()).type;
475 } else if (role == TaskModel::Category) {
476 return m_tasks.at(index.row()).category;
477 } else if (role == TaskModel::Icon) {
478 return taskTypeIcon(m_tasks.at(index.row()).type);
479 } else if (role == TaskModel::Task_t) {
480 return QVariant::fromValue(task(index));
485 Task TaskModel::task(const QModelIndex &index) const
487 if (!index.isValid())
489 return m_tasks.at(index.row());
492 QStringList TaskModel::categoryIds() const
494 return m_categories.keys();
497 QString TaskModel::categoryDisplayName(const QString &categoryId) const
499 return m_categories.value(categoryId);
502 int TaskModel::sizeOfFile(const QFont &font)
504 QString fontKey = font.key();
505 if (m_maxSizeOfFileName > 0 && fontKey == m_fileMeasurementFont)
506 return m_maxSizeOfFileName;
508 QFontMetrics fm(font);
509 m_fileMeasurementFont = fontKey;
511 foreach (const Task & t, m_tasks) {
512 QString filename = t.file;
513 const int pos = filename.lastIndexOf(QLatin1Char('/'));
515 filename = filename.mid(pos +1);
517 m_maxSizeOfFileName = qMax(m_maxSizeOfFileName, fm.width(filename));
519 return m_maxSizeOfFileName;
522 int TaskModel::sizeOfLineNumber(const QFont &font)
524 QString fontKey = font.key();
525 if (m_sizeOfLineNumber == 0 || fontKey != m_lineMeasurementFont) {
526 QFontMetrics fm(font);
527 m_lineMeasurementFont = fontKey;
528 m_sizeOfLineNumber = fm.width("8888");
530 return m_sizeOfLineNumber;
533 void TaskModel::setFileNotFound(const QModelIndex &idx, bool b)
535 if (idx.isValid() && idx.row() < m_tasks.size()) {
536 m_fileNotFound.insert(m_tasks[idx.row()].file, b);
537 emit dataChanged(idx, idx);
545 TaskFilterModel::TaskFilterModel(TaskModel *sourceModel, QObject *parent)
546 : QSortFilterProxyModel(parent)
548 setSourceModel(sourceModel);
549 setDynamicSortFilter(true);
550 m_includeUnknowns = m_includeWarnings = m_includeErrors = true;
553 TaskModel *TaskFilterModel::taskModel() const
555 return static_cast<TaskModel*>(sourceModel());
558 bool TaskFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
562 QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
563 Task::TaskType type = Task::TaskType(index.data(TaskModel::Type).toInt());
566 accept = m_includeUnknowns;
569 accept = m_includeWarnings;
572 accept = m_includeErrors;
576 const QString &categoryId = index.data(TaskModel::Category).toString();
577 if (m_categoryIds.contains(categoryId))
587 class TaskWindowPrivate
590 Internal::TaskModel *m_model;
591 Internal::TaskFilterModel *m_filter;
592 Internal::TaskView *m_listview;
593 Internal::TaskWindowContext *m_taskWindowContext;
594 QMenu *m_contextMenu;
595 QModelIndex m_contextMenuIndex;
596 ITaskHandler *m_defaultHandler;
597 QToolButton *m_filterWarningsButton;
598 QToolButton *m_categoriesButton;
599 QMenu *m_categoriesMenu;
603 static QToolButton *createFilterButton(QIcon icon, const QString &toolTip,
604 QObject *receiver, const char *slot)
606 QToolButton *button = new QToolButton;
607 button->setIcon(icon);
608 button->setToolTip(toolTip);
609 button->setCheckable(true);
610 button->setChecked(true);
611 button->setAutoRaise(true);
612 button->setEnabled(true);
613 QObject::connect(button, SIGNAL(toggled(bool)), receiver, slot);
617 TaskWindow::TaskWindow(TaskHub *taskhub) : d(new TaskWindowPrivate)
619 d->m_defaultHandler = 0;
621 d->m_model = new Internal::TaskModel;
622 d->m_filter = new Internal::TaskFilterModel(d->m_model);
623 d->m_listview = new Internal::TaskView;
625 d->m_listview->setModel(d->m_filter);
626 d->m_listview->setFrameStyle(QFrame::NoFrame);
627 d->m_listview->setWindowTitle(tr("Build Issues"));
628 d->m_listview->setSelectionMode(QAbstractItemView::SingleSelection);
629 Internal::TaskDelegate *tld = new Internal::TaskDelegate(this);
630 d->m_listview->setItemDelegate(tld);
631 d->m_listview->setWindowIcon(QIcon(QLatin1String(Qt4ProjectManager::Constants::ICON_WINDOW)));
632 d->m_listview->setContextMenuPolicy(Qt::ActionsContextMenu);
633 d->m_listview->setAttribute(Qt::WA_MacShowFocusRect, false);
635 d->m_taskWindowContext = new Internal::TaskWindowContext(d->m_listview);
636 d->m_taskHub = taskhub;
638 Core::ICore::instance()->addContextObject(d->m_taskWindowContext);
640 connect(d->m_listview->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
641 tld, SLOT(currentChanged(QModelIndex,QModelIndex)));
643 connect(d->m_listview, SIGNAL(activated(QModelIndex)),
644 this, SLOT(triggerDefaultHandler(QModelIndex)));
646 d->m_contextMenu = new QMenu(d->m_listview);
647 connect(d->m_contextMenu, SIGNAL(triggered(QAction*)),
648 this, SLOT(contextMenuEntryTriggered(QAction*)));
650 d->m_listview->setContextMenuPolicy(Qt::CustomContextMenu);
652 connect(d->m_listview, SIGNAL(customContextMenuRequested(QPoint)),
653 this, SLOT(showContextMenu(QPoint)));
655 d->m_filterWarningsButton = createFilterButton(d->m_model->taskTypeIcon(Task::Warning),
657 this, SLOT(setShowWarnings(bool)));
659 d->m_categoriesButton = new QToolButton;
660 d->m_categoriesButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_FILTER)));
661 d->m_categoriesButton->setToolTip(tr("Filter by categories"));
662 d->m_categoriesButton->setAutoRaise(true);
663 d->m_categoriesButton->setPopupMode(QToolButton::InstantPopup);
665 d->m_categoriesMenu = new QMenu(d->m_categoriesButton);
666 connect(d->m_categoriesMenu, SIGNAL(aboutToShow()), this, SLOT(updateCategoriesMenu()));
667 connect(d->m_categoriesMenu, SIGNAL(triggered(QAction*)), this, SLOT(filterCategoryTriggered(QAction*)));
669 d->m_categoriesButton->setMenu(d->m_categoriesMenu);
671 connect(d->m_taskHub, SIGNAL(categoryAdded(QString, QString)),
672 this, SLOT(addCategory(QString, QString)));
673 connect(d->m_taskHub, SIGNAL(taskAdded(ProjectExplorer::Task)),
674 this, SLOT(addTask(ProjectExplorer::Task)));
675 connect(d->m_taskHub, SIGNAL(taskRemoved(ProjectExplorer::Task)),
676 this, SLOT(removeTask(ProjectExplorer::Task)));
677 connect(d->m_taskHub, SIGNAL(tasksCleared(QString)),
678 this, SLOT(clearTasks(QString)));
681 TaskWindow::~TaskWindow()
683 Core::ICore::instance()->removeContextObject(d->m_taskWindowContext);
685 delete d->m_filterWarningsButton;
686 delete d->m_listview;
692 QList<QWidget*> TaskWindow::toolBarWidgets() const
694 return QList<QWidget*>() << d->m_filterWarningsButton << d->m_categoriesButton;
697 QWidget *TaskWindow::outputWidget(QWidget *)
699 return d->m_listview;
702 void TaskWindow::clearTasks(const QString &categoryId)
704 d->m_model->clearTasks(categoryId);
708 navigateStateChanged();
711 void TaskWindow::visibilityChanged(bool /* b */)
715 void TaskWindow::addCategory(const QString &categoryId, const QString &displayName)
717 Q_ASSERT(!categoryId.isEmpty());
718 d->m_model->addCategory(categoryId, displayName);
721 void TaskWindow::addTask(const Task &task)
723 d->m_model->addTask(task);
726 navigateStateChanged();
729 void TaskWindow::removeTask(const Task &task)
731 d->m_model->removeTask(task);
734 navigateStateChanged();
737 void TaskWindow::triggerDefaultHandler(const QModelIndex &index)
739 if (!index.isValid())
742 // Find a default handler to use:
743 if (!d->m_defaultHandler) {
744 QList<ITaskHandler *> handlers = ExtensionSystem::PluginManager::instance()->getObjects<ITaskHandler>();
745 foreach(ITaskHandler *handler, handlers) {
746 if (handler->id() == QLatin1String(Constants::SHOW_TASK_IN_EDITOR)) {
747 d->m_defaultHandler = handler;
752 Q_ASSERT(d->m_defaultHandler);
753 Task task(d->m_filter->task(index));
757 if (d->m_defaultHandler->canHandle(task)) {
758 d->m_defaultHandler->handle(task);
760 if (!QFileInfo(task.file).exists())
761 d->m_model->setFileNotFound(index, true);
765 void TaskWindow::showContextMenu(const QPoint &position)
767 QModelIndex index = d->m_listview->indexAt(position);
768 if (!index.isValid())
770 d->m_contextMenuIndex = index;
773 Task task = d->m_filter->task(index);
777 QList<ITaskHandler *> handlers = ExtensionSystem::PluginManager::instance()->getObjects<ITaskHandler>();
778 foreach(ITaskHandler *handler, handlers) {
779 if (handler == d->m_defaultHandler)
781 QAction * action = handler->createAction(d->m_contextMenu);
782 action->setEnabled(handler->canHandle(task));
783 action->setData(qVariantFromValue(qobject_cast<QObject*>(handler)));
784 d->m_contextMenu->addAction(action);
786 d->m_contextMenu->popup(d->m_listview->mapToGlobal(position));
789 void TaskWindow::contextMenuEntryTriggered(QAction *action)
791 if (action->isEnabled()) {
792 Task task = d->m_filter->task(d->m_contextMenuIndex);
796 ITaskHandler *handler = qobject_cast<ITaskHandler*>(action->data().value<QObject*>());
799 handler->handle(task);
803 void TaskWindow::cleanContextMenu()
805 QList<QAction *> actions = d->m_contextMenu->actions();
807 d->m_contextMenu->clear();
810 void TaskWindow::setShowWarnings(bool show)
812 d->m_filter->setFilterIncludesWarnings(show);
813 d->m_filter->setFilterIncludesUnknowns(show); // "Unknowns" are often associated with warnings
816 void TaskWindow::updateCategoriesMenu()
818 d->m_categoriesMenu->clear();
820 const QStringList filteredCategories = d->m_filter->filteredCategories();
822 foreach (const QString &categoryId, d->m_model->categoryIds()) {
823 const QString categoryName = d->m_model->categoryDisplayName(categoryId);
825 QAction *action = new QAction(d->m_categoriesMenu);
826 action->setCheckable(true);
827 action->setText(categoryName);
828 action->setData(categoryId);
829 action->setChecked(!filteredCategories.contains(categoryId));
831 d->m_categoriesMenu->addAction(action);
835 void TaskWindow::filterCategoryTriggered(QAction *action)
837 QString categoryId = action->data().toString();
838 Q_ASSERT(!categoryId.isEmpty());
840 QStringList categories = d->m_filter->filteredCategories();
841 Q_ASSERT(d->m_filter->filteredCategories().contains(categoryId) == action->isChecked());
843 if (action->isChecked()) {
844 categories.removeOne(categoryId);
846 categories.append(categoryId);
849 d->m_filter->setFilteredCategories(categories);
852 int TaskWindow::taskCount() const
854 return d->m_model->taskCount();
857 int TaskWindow::errorTaskCount() const
859 return d->m_model->errorTaskCount();
862 int TaskWindow::priorityInStatusBar() const
867 void TaskWindow::clearContents()
869 // clear all tasks in all displays
870 // Yeah we are that special
871 d->m_taskHub->clearTasks(QString());
874 bool TaskWindow::hasFocus()
876 return d->m_listview->hasFocus();
879 bool TaskWindow::canFocus()
881 return d->m_filter->rowCount();
884 void TaskWindow::setFocus()
886 if (d->m_filter->rowCount()) {
887 d->m_listview->setFocus();
888 if (d->m_listview->currentIndex() == QModelIndex()) {
889 d->m_listview->setCurrentIndex(d->m_filter->index(0,0, QModelIndex()));
894 bool TaskWindow::canNext()
896 return d->m_filter->rowCount();
899 bool TaskWindow::canPrevious()
901 return d->m_filter->rowCount();
904 void TaskWindow::goToNext()
908 QModelIndex startIndex = d->m_listview->currentIndex();
909 QModelIndex currentIndex = startIndex;
911 if (startIndex.isValid()) {
913 int row = currentIndex.row() + 1;
914 if (row == d->m_filter->rowCount())
916 currentIndex = d->m_filter->index(row, 0);
917 if (d->m_filter->hasFile(currentIndex))
919 } while (startIndex != currentIndex);
921 currentIndex = d->m_filter->index(0, 0);
923 d->m_listview->setCurrentIndex(currentIndex);
924 triggerDefaultHandler(currentIndex);
927 void TaskWindow::goToPrev()
931 QModelIndex startIndex = d->m_listview->currentIndex();
932 QModelIndex currentIndex = startIndex;
934 if (startIndex.isValid()) {
936 int row = currentIndex.row() - 1;
938 row = d->m_filter->rowCount() - 1;
939 currentIndex = d->m_filter->index(row, 0);
940 if (d->m_filter->hasFile(currentIndex))
942 } while (startIndex != currentIndex);
944 currentIndex = d->m_filter->index(0, 0);
946 d->m_listview->setCurrentIndex(currentIndex);
947 triggerDefaultHandler(currentIndex);
950 bool TaskWindow::canNavigate()
959 TaskDelegate::TaskDelegate(QObject *parent)
960 : QStyledItemDelegate(parent)
964 TaskDelegate::~TaskDelegate()
968 QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
970 QStyleOptionViewItemV4 opt = option;
971 initStyleOption(&opt, index);
973 QFontMetrics fm(option.font);
974 int fontHeight = fm.height();
975 int fontLeading = fm.leading();
977 const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget);
978 TaskModel *model = static_cast<TaskFilterModel *>(view->model())->taskModel();
979 Positions positions(option, model);
982 s.setWidth(option.rect.width());
983 if (view->selectionModel()->currentIndex() == index) {
984 QString description = index.data(TaskModel::Description).toString();
985 // Layout the description
986 int leading = fontLeading;
988 description.replace('\n', QChar::LineSeparator);
989 QTextLayout tl(description);
992 QTextLine line = tl.createLine();
995 line.setLineWidth(positions.textAreaWidth());
997 line.setPosition(QPoint(0, height));
998 height += static_cast<int>(line.height());
1002 s.setHeight(height + leading + fontHeight + 3);
1004 s.setHeight(fontHeight + 3);
1006 if (s.height() < positions.minimumHeight())
1007 s.setHeight(positions.minimumHeight());
1011 void TaskDelegate::emitSizeHintChanged(const QModelIndex &index)
1013 emit sizeHintChanged(index);
1016 void TaskDelegate::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
1018 emit sizeHintChanged(current);
1019 emit sizeHintChanged(previous);
1022 void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
1024 QStyleOptionViewItemV4 opt = option;
1025 initStyleOption(&opt, index);
1028 QFontMetrics fm(opt.font);
1029 QColor backgroundColor;
1032 const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget);
1033 bool selected = view->selectionModel()->currentIndex() == index;
1036 painter->setBrush(opt.palette.highlight().color());
1037 backgroundColor = opt.palette.highlight().color();
1039 painter->setBrush(opt.palette.background().color());
1040 backgroundColor = opt.palette.background().color();
1042 painter->setPen(Qt::NoPen);
1043 painter->drawRect(opt.rect);
1047 textColor = opt.palette.highlightedText().color();
1049 textColor = opt.palette.text().color();
1051 painter->setPen(textColor);
1053 TaskModel *model = static_cast<TaskFilterModel *>(view->model())->taskModel();
1054 Positions positions(opt, model);
1056 // Paint TaskIconArea:
1057 QIcon icon = index.data(TaskModel::Icon).value<QIcon>();
1058 painter->drawPixmap(positions.left(), positions.top(),
1059 icon.pixmap(positions.taskIconWidth(), positions.taskIconHeight()));
1063 // in small mode we lay out differently
1064 QString bottom = index.data(TaskModel::Description).toString().split('\n').first();
1065 painter->setClipRect(positions.textArea());
1066 painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(), bottom);
1067 if (fm.width(bottom) > positions.textAreaWidth()) {
1068 // draw a gradient to mask the text
1069 int gradientStart = positions.textAreaRight() - ELLIPSIS_GRADIENT_WIDTH + 1;
1070 QLinearGradient lg(gradientStart, 0, gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0);
1071 lg.setColorAt(0, Qt::transparent);
1072 lg.setColorAt(1, backgroundColor);
1073 painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg);
1077 QString description = index.data(TaskModel::Description).toString();
1078 // Layout the description
1079 int leading = fm.leading();
1081 description.replace('\n', QChar::LineSeparator);
1082 QTextLayout tl(description);
1083 tl.setAdditionalFormats(index.data(TaskModel::Task_t).value<ProjectExplorer::Task>().formats);
1086 QTextLine line = tl.createLine();
1087 if (!line.isValid())
1089 line.setLineWidth(positions.textAreaWidth());
1091 line.setPosition(QPoint(0, height));
1092 height += static_cast<int>(line.height());
1095 tl.draw(painter, QPoint(positions.textAreaLeft(), positions.top()));
1098 mix.setRgb( static_cast<int>(0.7 * textColor.red() + 0.3 * backgroundColor.red()),
1099 static_cast<int>(0.7 * textColor.green() + 0.3 * backgroundColor.green()),
1100 static_cast<int>(0.7 * textColor.blue() + 0.3 * backgroundColor.blue()));
1101 painter->setPen(mix);
1103 const QString directory = QDir::toNativeSeparators(index.data(TaskModel::File).toString());
1104 int secondBaseLine = positions.top() + fm.ascent() + height + leading;
1105 if (index.data(TaskModel::FileNotFound).toBool()) {
1106 QString fileNotFound = tr("File not found: %1").arg(directory);
1107 painter->setPen(Qt::red);
1108 painter->drawText(positions.textAreaLeft(), secondBaseLine, fileNotFound);
1110 painter->drawText(positions.textAreaLeft(), secondBaseLine, directory);
1113 painter->setPen(textColor);
1116 QString file = index.data(TaskModel::File).toString();
1117 const int pos = file.lastIndexOf(QLatin1Char('/'));
1119 file = file.mid(pos +1);
1120 const int realFileWidth = fm.width(file);
1121 painter->setClipRect(positions.fileArea());
1122 painter->drawText(qMin(positions.fileAreaLeft(), positions.fileAreaRight() - realFileWidth),
1123 positions.top() + fm.ascent(), file);
1124 if (realFileWidth > positions.fileAreaWidth()) {
1125 // draw a gradient to mask the text
1126 int gradientStart = positions.fileAreaLeft() - 1;
1127 QLinearGradient lg(gradientStart + ELLIPSIS_GRADIENT_WIDTH, 0, gradientStart, 0);
1128 lg.setColorAt(0, Qt::transparent);
1129 lg.setColorAt(1, backgroundColor);
1130 painter->fillRect(gradientStart, positions.top(), ELLIPSIS_GRADIENT_WIDTH, positions.firstLineHeight(), lg);
1134 QString lineText = index.data(TaskModel::Line).toString();
1135 painter->setClipRect(positions.lineArea());
1136 const int realLineWidth = fm.width(lineText);
1137 painter->drawText(positions.lineAreaRight() - realLineWidth, positions.top() + fm.ascent(), lineText);
1138 painter->setClipRect(opt.rect);
1141 painter->setPen(QColor::fromRgb(150,150,150));
1142 painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
1146 TaskWindowContext::TaskWindowContext(QWidget *widget)
1147 : Core::IContext(widget),
1149 m_context(Core::Constants::C_PROBLEM_PANE)
1153 Core::Context TaskWindowContext::context() const
1158 QWidget *TaskWindowContext::widget()
1163 } // namespace Internal
1165 } // namespace ProjectExplorer
1167 #include "taskwindow.moc"