00001
00002 #include "CompilerOutputWidget.h"
00003
00004 #include <QDir>
00005 #include <QKeyEvent>
00006 #include <QHeaderView>
00007 #include <QListView>
00008 #include <QPainter>
00009 #include <QAbstractItemModel>
00010 #include <QApplication>
00011 #include <QClipboard>
00012 #include <QFont>
00013 #include <QFontMetrics>
00014 #include <QTextLayout>
00015
00016
00017
00018 class TaskModel;
00019
00020
00021 class TaskView : public QListView
00022 {
00023 public:
00024 TaskView(QWidget *parent = 0);
00025 ~TaskView();
00026 void resizeEvent(QResizeEvent *e);
00027 void keyPressEvent(QKeyEvent *e);
00028 };
00029
00030
00031
00032 class TaskModel : public QAbstractItemModel
00033 {
00034 public:
00035
00036 typedef CompilerOutputWidget::PatternType PatternType;
00037 struct TaskItem
00038 {
00039 QString description;
00040 QString file;
00041 int line;
00042 bool fileNotFound;
00043 PatternType type;
00044 };
00045
00046
00047
00048 TaskModel();
00049 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
00050 QModelIndex parent(const QModelIndex &child) const;
00051 int rowCount(const QModelIndex &parent = QModelIndex()) const;
00052 int columnCount(const QModelIndex &parent = QModelIndex()) const;
00053 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
00054 void clear();
00055 void addTask(PatternType type, const QString &description, const QString &file, int line);
00056 int sizeOfFile();
00057 int sizeOfLineNumber();
00058 QModelIndex firstError() const;
00059 void setFileNotFound(const QModelIndex &index, bool b);
00060
00061 enum Roles { File = Qt::UserRole, Line, Description, FileNotFound, Type };
00062
00063 QIcon iconFor(PatternType type);
00064
00065 private:
00066 QList<TaskItem> m_items;
00067 int m_maxSizeOfFileName;
00068 QIcon m_errorIcon;
00069 QIcon m_warningIcon;
00070 QIcon m_unspecifiedIcon;
00071 };
00072
00073
00074
00075
00076
00078
00080
00081 TaskView::TaskView(QWidget *parent)
00082 : QListView(parent)
00083 {
00084 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00085 }
00086
00087 TaskView::~TaskView()
00088 {
00089
00090 }
00091
00092 void TaskView::resizeEvent(QResizeEvent *e)
00093 {
00094 Q_UNUSED(e);
00095 static_cast<TaskDelegate *>(itemDelegate())->emitSizeHintChanged(selectionModel()->currentIndex());
00096 }
00097
00098 void TaskView::keyPressEvent(QKeyEvent *e)
00099 {
00100 if (!e->modifiers() && e->key() == Qt::Key_Return) {
00101 emit activated(currentIndex());
00102 e->accept();
00103 return;
00104 }
00105 QListView::keyPressEvent(e);
00106 }
00107
00109
00111
00112 TaskModel::TaskModel()
00113 {
00114 m_maxSizeOfFileName = 0;
00115 m_errorIcon = QIcon(":/images/compile_error.png");
00116 m_warningIcon = QIcon(":/images/compile_warning.png");
00117 m_unspecifiedIcon = QIcon(":/images/compile_unspecified.png");
00118 }
00119
00120 void TaskModel::addTask(PatternType type, const QString &description, const QString &file, int line)
00121 {
00122 TaskItem task;
00123 task.description = description;
00124 task.file = file;
00125 task.line = line;
00126 task.type = type;
00127 task.fileNotFound = false;
00128
00129 beginInsertRows(QModelIndex(), m_items.size(), m_items.size());
00130 m_items.append(task);
00131 endInsertRows();
00132
00133 QFont font;
00134 QFontMetrics fm(font);
00135 QString filename = task.file;
00136 int pos = filename.lastIndexOf("/");
00137 if (pos != -1)
00138 filename = file.mid(pos +1);
00139 m_maxSizeOfFileName = qMax(m_maxSizeOfFileName, fm.width(filename));
00140 }
00141
00142 void TaskModel::clear()
00143 {
00144 if (m_items.isEmpty())
00145 return;
00146 beginRemoveRows(QModelIndex(), 0, m_items.size() -1);
00147 m_items.clear();
00148 endRemoveRows();
00149 m_maxSizeOfFileName = 0;
00150 }
00151
00152
00153 QModelIndex TaskModel::index(int row, int column, const QModelIndex &parent) const
00154 {
00155 if (parent.isValid())
00156 return QModelIndex();
00157 return createIndex(row, column, 0);
00158 }
00159
00160 QModelIndex TaskModel::parent(const QModelIndex &child) const
00161 {
00162 Q_UNUSED(child);
00163 return QModelIndex();
00164 }
00165
00166 int TaskModel::rowCount(const QModelIndex &parent) const
00167 {
00168 return parent.isValid() ? 0 : m_items.count();
00169 }
00170
00171 int TaskModel::columnCount(const QModelIndex &parent) const
00172 {
00173 return parent.isValid() ? 0 : 1;
00174 }
00175
00176 QVariant TaskModel::data(const QModelIndex &index, int role) const
00177 {
00178 if (!index.isValid() || index.row() >= m_items.size() || index.column() != 0)
00179 return QVariant();
00180
00181 if (role == TaskModel::File)
00182 return m_items.at(index.row()).file;
00183 else if (role == TaskModel::Line)
00184 return m_items.at(index.row()).line;
00185 else if (role == TaskModel::Description)
00186 return m_items.at(index.row()).description;
00187 else if (role == TaskModel::FileNotFound)
00188 return m_items.at(index.row()).fileNotFound;
00189 else if (role == TaskModel::Type)
00190 return (int)m_items.at(index.row()).type;
00191 return QVariant();
00192 }
00193
00194 QIcon TaskModel::iconFor(PatternType type)
00195 {
00196 if (type == CompilerOutputWidget::Error)
00197 return m_errorIcon;
00198 else if (type == CompilerOutputWidget::Warning)
00199 return m_warningIcon;
00200 else
00201 return m_unspecifiedIcon;
00202 }
00203
00204 int TaskModel::sizeOfFile()
00205 {
00206 return m_maxSizeOfFileName;
00207 }
00208
00209 int TaskModel::sizeOfLineNumber()
00210 {
00211 QFont font;
00212 QFontMetrics fm(font);
00213 return fm.width("8888");
00214 }
00215
00216 QModelIndex TaskModel::firstError() const
00217 {
00218 int size = m_items.size();
00219 for (int i=0; i<size; ++i) {
00220 if (m_items.at(i).type == CompilerOutputWidget::Error) {
00221 return index(i, 0);
00222 }
00223 }
00224 return QModelIndex();
00225 }
00226
00227 void TaskModel::setFileNotFound(const QModelIndex &idx, bool b)
00228 {
00229 if (idx.isValid() && idx.row() < m_items.size()) {
00230 m_items[idx.row()].fileNotFound = b;
00231 emit dataChanged(idx, idx);
00232 }
00233 }
00234
00236
00238
00239 #include <QVBoxLayout>
00240
00241 CompilerOutputWidget::CompilerOutputWidget(QWidget * par)
00242 : QWidget(par)
00243 {
00244 m_model = new TaskModel;
00245 m_listview = new TaskView(this);
00246
00247
00248 QVBoxLayout * layout = new QVBoxLayout(this);
00249 layout->addWidget(m_listview);
00250
00251 m_listview->setModel(m_model);
00252 m_listview->setWindowTitle(tr("Compiler Output"));
00253 m_listview->setSelectionMode(QAbstractItemView::SingleSelection);
00254 TaskDelegate *tld = new TaskDelegate(this);
00255 m_listview->setItemDelegate(tld);
00256
00257 m_listview->setContextMenuPolicy(Qt::ActionsContextMenu);
00258 m_listview->setAttribute(Qt::WA_MacShowFocusRect, false);
00259
00260
00261 connect(m_listview->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
00262 tld, SLOT(currentChanged(const QModelIndex &, const QModelIndex &)));
00263
00264 connect(m_listview, SIGNAL(activated(const QModelIndex &)),
00265 this, SLOT(showTaskInFile(const QModelIndex &)));
00266 connect(m_listview, SIGNAL(clicked(const QModelIndex &)),
00267 this, SLOT(showTaskInFile(const QModelIndex &)));
00268
00269
00270 m_errorCount = 0;
00271 m_currentTask = -1;
00272 }
00273
00274 CompilerOutputWidget::~CompilerOutputWidget()
00275 {
00276 delete m_model;
00277 }
00278
00279
00280 void CompilerOutputWidget::clearContents()
00281 {
00282 m_errorCount = 0;
00283 m_currentTask = -1;
00284 m_model->clear();
00285 emit tasksChanged();
00286 }
00287
00288
00289
00290 void CompilerOutputWidget::addItem(CompilerOutputWidget::PatternType type,
00291 const QString &description, const QString &file, int line)
00292 {
00293 m_model->addTask(type, description, file, line);
00294 if (type == CompilerOutputWidget::Error)
00295 ++m_errorCount;
00296 emit tasksChanged();
00297 }
00298
00299 void CompilerOutputWidget::showTaskInFile(const QModelIndex &index)
00300 {
00301 if (!index.isValid())
00302 return;
00303 QString file = index.data(TaskModel::File).toString();
00304 int line = index.data(TaskModel::Line).toInt();
00305 if (file.isEmpty() || line == -1)
00306 return;
00307
00308 QFileInfo fi(file);
00309 if (fi.exists()) {
00310 emit taskClicked(file,line);
00311 }
00312 else
00313 m_model->setFileNotFound(index, true);
00314
00315 m_listview->selectionModel()->setCurrentIndex(index, QItemSelectionModel::Select);
00316 m_listview->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
00317 }
00318
00319 void CompilerOutputWidget::copy()
00320 {
00321 QModelIndex index = m_listview->selectionModel()->currentIndex();
00322 QString file = index.data(TaskModel::File).toString();
00323 QString line = index.data(TaskModel::Line).toString();
00324 QString description = index.data(TaskModel::Description).toString();
00325 QString type;
00326 switch (index.data(TaskModel::Type).toInt()) {
00327 case CompilerOutputWidget::Error:
00328 type = "error: ";
00329 break;
00330 case CompilerOutputWidget::Warning:
00331 type = "warning: ";
00332 break;
00333 }
00334
00335 QApplication::clipboard()->setText(file + ':' + line + ": " + type + description);
00336 }
00337
00338 int CompilerOutputWidget::numberOfTasks() const
00339 {
00340 return m_model->rowCount(QModelIndex());
00341 }
00342
00343 int CompilerOutputWidget::numberOfErrors() const
00344 {
00345 return m_errorCount;
00346 }
00347
00348 void CompilerOutputWidget::gotoFirstError()
00349 {
00350 QModelIndex idx = m_model->firstError();
00351 if (idx.isValid())
00352 showTaskInFile(idx);
00353 }
00354
00355
00357
00359
00360 TaskDelegate::TaskDelegate(QObject *parent)
00361 : QStyledItemDelegate(parent)
00362 {
00363 }
00364
00365 TaskDelegate::~TaskDelegate()
00366 {
00367 }
00368
00369 QSize TaskDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
00370 {
00371 QStyleOptionViewItemV4 opt = option;
00372 initStyleOption(&opt, index);
00373
00374 QFontMetrics fm(option.font);
00375 int fontHeight = fm.height();
00376 int fontLeading = fm.leading();
00377
00378 QSize s;
00379 s.setWidth(option.rect.width());
00380 const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget);
00381 TaskModel *model = static_cast<TaskModel *>(view->model());
00382 int width = opt.rect.width() - model->sizeOfFile() - model->sizeOfLineNumber() - 12 - 22;
00383 if (view->selectionModel()->currentIndex() == index) {
00384 QString description = index.data(TaskModel::Description).toString();
00385
00386 int leading = fontLeading;
00387 int height = 0;
00388 QTextLayout tl(description);
00389 tl.beginLayout();
00390 while (true) {
00391 QTextLine line = tl.createLine();
00392 if (!line.isValid())
00393 break;
00394 line.setLineWidth(width);
00395 height += leading;
00396 line.setPosition(QPoint(0, height));
00397 height += static_cast<int>(line.height());
00398 }
00399 tl.endLayout();
00400
00401 s.setHeight(height + leading + fontHeight + 3);
00402 } else {
00403 s.setHeight(fontHeight + 3);
00404 }
00405 return s;
00406 }
00407
00408 void TaskDelegate::emitSizeHintChanged(const QModelIndex &index)
00409 {
00410 emit sizeHintChanged(index);
00411 }
00412
00413 void TaskDelegate::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
00414 {
00415 emit sizeHintChanged(current);
00416 emit sizeHintChanged(previous);
00417 }
00418
00419 void TaskDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
00420 {
00421 QStyleOptionViewItemV4 opt = option;
00422 initStyleOption(&opt, index);
00423 painter->save();
00424
00425 painter->setRenderHint(QPainter::Antialiasing,true);
00426 painter->setRenderHint(QPainter::TextAntialiasing,true);
00427
00428 QFontMetrics fm(opt.font);
00429 QColor backgroundColor;
00430 QColor textColor;
00431
00432
00433 const QAbstractItemView * view = qobject_cast<const QAbstractItemView *>(opt.widget);
00434 bool selected = view->selectionModel()->currentIndex() == index;
00435
00436 if (selected) {
00437 painter->setBrush(opt.palette.highlight().color());
00438 backgroundColor = opt.palette.highlight().color();
00439 } else {
00440 painter->setBrush(opt.palette.background().color());
00441 backgroundColor = opt.palette.background().color();
00442 }
00443 painter->setPen(Qt::NoPen);
00444 painter->drawRect(opt.rect);
00445
00446
00447 if (selected)
00448 textColor = opt.palette.highlightedText().color();
00449 else
00450 textColor = opt.palette.text().color();
00451
00452 painter->setPen(textColor);
00453
00454 TaskModel *model = static_cast<TaskModel *>(view->model());
00455 CompilerOutputWidget::PatternType type = CompilerOutputWidget::PatternType(index.data(TaskModel::Type).toInt());
00456 QIcon icon = model->iconFor(type);
00457 painter->drawPixmap(2, opt.rect.top() + 2, icon.pixmap(16, 16));
00458
00459 int width = opt.rect.width() - model->sizeOfFile() - model->sizeOfLineNumber() - 12 - 22;
00460 if (!selected) {
00461
00462 QString bottom = index.data(TaskModel::Description).toString();
00463 painter->drawText(22, 2 + opt.rect.top() + fm.ascent(), bottom);
00464 if (fm.width(bottom) > width) {
00465
00466 int gwidth = opt.rect.right() - width;
00467 QLinearGradient lg(QPoint(width, 0), QPoint(width+gwidth, 0));
00468 QColor c = backgroundColor;
00469 c.setAlpha(0);
00470 lg.setColorAt(0, c);
00471 lg.setColorAt(20.0/gwidth, backgroundColor);
00472 painter->fillRect(width, 2 + opt.rect.top(), gwidth, fm.height() + 1, lg);
00473 }
00474 } else {
00475
00476 QString description = index.data(TaskModel::Description).toString();
00477
00478 int leading = fm.leading();
00479 int height = 0;
00480 QTextLayout tl(description);
00481 tl.beginLayout();
00482 while (true) {
00483 QTextLine line = tl.createLine();
00484 if (!line.isValid())
00485 break;
00486 line.setLineWidth(width);
00487 height += leading;
00488 line.setPosition(QPoint(0, height));
00489 height += static_cast<int>(line.height());
00490 }
00491 tl.endLayout();
00492 tl.draw(painter, QPoint(22, 2 + opt.rect.top()));
00493
00494
00495 QColor mix;
00496 mix.setRgb( static_cast<int>(0.7 * textColor.red() + 0.3 * backgroundColor.red()),
00497 static_cast<int>(0.7 * textColor.green() + 0.3 * backgroundColor.green()),
00498 static_cast<int>(0.7 * textColor.blue() + 0.3 * backgroundColor.blue()));
00499 painter->setPen(mix);
00500
00501 QString directory = index.data(TaskModel::File).toString();
00502 int secondBaseLine = 2 + fm.ascent() + opt.rect.top() + height + leading;
00503 if (index.data(TaskModel::FileNotFound).toBool()) {
00504 QString fileNotFound = tr("File not found: %1").arg(directory);
00505 painter->setPen(Qt::red);
00506 painter->drawText(22, secondBaseLine, fileNotFound);
00507 } else {
00508 painter->drawText(22, secondBaseLine, directory);
00509 }
00510 }
00511
00512 painter->setPen(textColor);
00513
00514
00515 QString file = index.data(TaskModel::File).toString();
00516 int pos = file.lastIndexOf("/");
00517 if (pos != -1)
00518 file = file.mid(pos +1);
00519 painter->drawText(width + 22 + 4, 2 + opt.rect.top() + fm.ascent(), file);
00520
00521 QString topRight = index.data(TaskModel::Line).toString();
00522 painter->drawText(opt.rect.right() - fm.width(topRight) - 6 , 2 + opt.rect.top() + fm.ascent(), topRight);
00523
00524 painter->setPen(QColor::fromRgb(150,150,150));
00525 painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
00526 painter->restore();
00527 }
00528
00529
00530