CompilerOutputWidget.cpp

Go to the documentation of this file.
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         // Model stuff
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 //  TaskView
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 // TaskModel
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 // TaskWindow
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     //m_listview->setWindowIcon(QIcon(":/qt4projectmanager/images/window.png"));
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 // Delegate
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         // Layout the description
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 &current, 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     // Set Text Color
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         // in small mode we lay out differently
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             // draw a gradient to mask the text
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         // Description
00476         QString description = index.data(TaskModel::Description).toString();
00477         // Layout the description
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         //painter->drawText(22, 2 + opt.rect.top() + fm.ascent(), description);
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; //opt.rect.top() + fm.ascent() + fm.height() + 6;
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     // Assemble string for the right side
00514     // just filename + linenumer
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     // Separator lines
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 

Generated on Tue Sep 15 14:48:47 2009 for RoseQtWidgets by  doxygen 1.4.7