From edb3094fa33a20d2f83e517fffdfa3889c18bc07 Mon Sep 17 00:00:00 2001 From: Vasiliy Sorokin <sorokin.vasiliy@gmail.com> Date: Tue, 14 Feb 2012 22:43:26 +0400 Subject: [PATCH] Initial commit for TODO Plugin Change-Id: I8f12017be5dc65b1244df369880e3b4851af4b2e Reviewed-by: Eike Ziller <eike.ziller@nokia.com> --- src/plugins/todo/INSTALL | 7 + src/plugins/todo/LICENSE.BSD | 20 ++ src/plugins/todo/addkeyworddialog.cpp | 42 +++ src/plugins/todo/addkeyworddialog.h | 28 ++ src/plugins/todo/addkeyworddialog.ui | 123 +++++++++ src/plugins/todo/icons.qrc | 8 + src/plugins/todo/images/error.png | Bin 0 -> 640 bytes src/plugins/todo/images/info.png | Bin 0 -> 856 bytes src/plugins/todo/images/todo.png | Bin 0 -> 11012 bytes src/plugins/todo/images/warning.png | Bin 0 -> 511 bytes src/plugins/todo/keyword.cpp | 26 ++ src/plugins/todo/keyword.h | 29 ++ src/plugins/todo/settingsdialog.cpp | 138 ++++++++++ src/plugins/todo/settingsdialog.h | 45 +++ src/plugins/todo/settingsdialog.ui | 143 ++++++++++ src/plugins/todo/settingspage.cpp | 118 ++++++++ src/plugins/todo/settingspage.h | 39 +++ src/plugins/todo/todooutputpane.cpp | 174 ++++++++++++ src/plugins/todo/todooutputpane.h | 76 ++++++ src/plugins/todo/todoplugin.cpp | 376 ++++++++++++++++++++++++++ src/plugins/todo/todoplugin.h | 91 +++++++ src/plugins/todo/todoplugin.pro | 46 ++++ 22 files changed, 1529 insertions(+) create mode 100644 src/plugins/todo/INSTALL create mode 100644 src/plugins/todo/LICENSE.BSD create mode 100644 src/plugins/todo/addkeyworddialog.cpp create mode 100644 src/plugins/todo/addkeyworddialog.h create mode 100644 src/plugins/todo/addkeyworddialog.ui create mode 100755 src/plugins/todo/icons.qrc create mode 100755 src/plugins/todo/images/error.png create mode 100755 src/plugins/todo/images/info.png create mode 100644 src/plugins/todo/images/todo.png create mode 100755 src/plugins/todo/images/warning.png create mode 100644 src/plugins/todo/keyword.cpp create mode 100644 src/plugins/todo/keyword.h create mode 100644 src/plugins/todo/settingsdialog.cpp create mode 100644 src/plugins/todo/settingsdialog.h create mode 100644 src/plugins/todo/settingsdialog.ui create mode 100644 src/plugins/todo/settingspage.cpp create mode 100644 src/plugins/todo/settingspage.h create mode 100755 src/plugins/todo/todooutputpane.cpp create mode 100755 src/plugins/todo/todooutputpane.h create mode 100755 src/plugins/todo/todoplugin.cpp create mode 100755 src/plugins/todo/todoplugin.h create mode 100755 src/plugins/todo/todoplugin.pro diff --git a/src/plugins/todo/INSTALL b/src/plugins/todo/INSTALL new file mode 100644 index 00000000000..e992d0a0271 --- /dev/null +++ b/src/plugins/todo/INSTALL @@ -0,0 +1,7 @@ +Installation. +1. Download QtCreator sources. +2. Build QtCreator from sources or download and install binary. +3. Change todoplugin.pro, You need QTC_BUILD_DIR set to your path to qtcreator binary and set QTC_SOURCE_DIR your path to qtcreator source +4. Build plugin: + qmake + make diff --git a/src/plugins/todo/LICENSE.BSD b/src/plugins/todo/LICENSE.BSD new file mode 100644 index 00000000000..a3c72c27b54 --- /dev/null +++ b/src/plugins/todo/LICENSE.BSD @@ -0,0 +1,20 @@ +Copyright (c) 2010, Vasiliy Sorokin +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of the vsorokin nor the names of its contributors may be used to endorse or +promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/plugins/todo/addkeyworddialog.cpp b/src/plugins/todo/addkeyworddialog.cpp new file mode 100644 index 00000000000..436a185627a --- /dev/null +++ b/src/plugins/todo/addkeyworddialog.cpp @@ -0,0 +1,42 @@ +#include "addkeyworddialog.h" +#include "ui_addkeyworddialog.h" +#include <QColorDialog> + + +AddKeywordDialog::AddKeywordDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AddKeywordDialog) +{ + ui->setupUi(this); + this->ui->listWidget->setViewMode(QListWidget::IconMode); + this->ui->listWidget->addItem(new QListWidgetItem(QIcon(":/info"), "information")); + this->ui->listWidget->addItem(new QListWidgetItem(QIcon(":/warning"), "warning")); + this->ui->listWidget->addItem(new QListWidgetItem(QIcon(":/error"), "error")); + connect(this->ui->pushButton, SIGNAL(clicked()), this, SLOT(chooseColor())); +} + +AddKeywordDialog::~AddKeywordDialog() +{ + delete ui; +} + +QString AddKeywordDialog::keywordName() +{ + return this->ui->lineEdit->text(); +} + +QColor AddKeywordDialog::keywordColor() +{ + return QColor(this->ui->colorEdit->text()); +} + +QIcon AddKeywordDialog::keywordIcon() +{ + return this->ui->listWidget->currentItem()->icon(); +} + +void AddKeywordDialog::chooseColor() +{ + QColorDialog *dialog = new QColorDialog(QColor(this->ui->colorEdit->text()),this); + this->ui->colorEdit->setText(dialog->getColor().name()); +} diff --git a/src/plugins/todo/addkeyworddialog.h b/src/plugins/todo/addkeyworddialog.h new file mode 100644 index 00000000000..39c8e0d5721 --- /dev/null +++ b/src/plugins/todo/addkeyworddialog.h @@ -0,0 +1,28 @@ +#ifndef ADDKEYWORDDIALOG_H +#define ADDKEYWORDDIALOG_H + +#include <QDialog> + +namespace Ui { + class AddKeywordDialog; +} + +class AddKeywordDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddKeywordDialog(QWidget *parent = 0); + ~AddKeywordDialog(); + QString keywordName(); + QColor keywordColor(); + QIcon keywordIcon(); + +public slots: + void chooseColor(); + +private: + Ui::AddKeywordDialog *ui; +}; + +#endif // ADDKEYWORDDIALOG_H diff --git a/src/plugins/todo/addkeyworddialog.ui b/src/plugins/todo/addkeyworddialog.ui new file mode 100644 index 00000000000..3d7c0269870 --- /dev/null +++ b/src/plugins/todo/addkeyworddialog.ui @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AddKeywordDialog</class> + <widget class="QDialog" name="AddKeywordDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>379</width> + <height>225</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Icons</string> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="listWidget"/> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Color</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="2" column="0"> + <widget class="QLineEdit" name="colorEdit"> + <property name="inputMask"> + <string>\#HHHHHH; </string> + </property> + <property name="text"> + <string>#000000</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QPushButton" name="pushButton"> + <property name="text"> + <string>Choose</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Keyword</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLineEdit" name="lineEdit"/> + </item> + </layout> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>AddKeywordDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>AddKeywordDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/todo/icons.qrc b/src/plugins/todo/icons.qrc new file mode 100755 index 00000000000..045f39daa08 --- /dev/null +++ b/src/plugins/todo/icons.qrc @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="/"> + <file alias="error">images/error.png</file> + <file alias="info">images/info.png</file> + <file alias="warning">images/warning.png</file> + <file alias="todo">images/todo.png</file> + </qresource> +</RCC> diff --git a/src/plugins/todo/images/error.png b/src/plugins/todo/images/error.png new file mode 100755 index 0000000000000000000000000000000000000000..87cd0b0125d6193c16c59f25b88396493e885f6b GIT binary patch literal 640 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJbFq_W2nPqp?T7vkfLzW3kH}&M z2FBeW%xLxI@gtz1WQl7;NpOBzNqJ&XDnogBxn5>oc5!lIL8@MUQTpt6Hc~*<;sHJ( zuK)l4Z(v|p!N9PVfuWs&p^JfGA_Kz=28KBd467IzAX4jDSb*GiKE6H%hCBv_Tn2_L z28Klp4AZo=>*d6nRHQFelpgl-Jed$XS6*hniPlkf`z7+iOXNkDC`v6+mRl+(v`kK5 zwTk$9b?ME9YMZs?_j%bL_HsDk<9sU2r$Je<QCV@JoyjI=t1M;tEM<ji8>2`?nP^4n zy$lTd7#Q|j8XpwoI%I5o$k*ermDN#8qhmrm$Ax%K+S#3Uu|MtSb;ix@0t3SZA)d=& zVVA>0E~h43Ns7Cc5PLm2@wTwQZ4try!u<C|gzje~K4xHes;~9bK<BBE?o%VZr^SWO za&w=RmA>rld@myOUR3yfXZwfVuCELX$$gHBz;HAv3GxeOFfuao00U1?PcLu(fWV*& z*YDnY^6b^8FJFJbz;6)v0|bBn{{8pwYTmshp#IsOE{-7*lD!9A#hMrxSP$@P+)yej zFHd4mDu4d>{K5|<=XpdH-eIeeoFkI5<onEvOuxRIc6Um;8@En#@y>#Mch*eE5E3q& zsDJ)QWe~HZu=JPqwHHm!J!tPMoAuVM<=A|yGuL0$MD)r}t=B23K4h_=$wxfq{j{s6 zvbJ;1RM`0b{&%I*Q{~<_^9mg*KC_8ox^*ST366Hw_&MDTLh{a=g|okJNlO7blfl!~ K&t;ucLK6VLNBNWh literal 0 HcmV?d00001 diff --git a/src/plugins/todo/images/info.png b/src/plugins/todo/images/info.png new file mode 100755 index 0000000000000000000000000000000000000000..f6af9f83f4d5e2354fc41251f23f0e06084954b9 GIT binary patch literal 856 zcmV-e1E>6nP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004b3#c}2nYxW zd<bNS00009a7bBm0005I0005I0XppC;s5{u8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10@X=GK~#9!ZIj(=Ttyg%pPBQuIlI}-HoGCArKX^Q4QNrsFRWj& zgo;>1A|eXC@DC6XFN)V9=(TvI2;M|1nu}OL6hmpKMT+$UZ$u4kv^AS-&hDP?Gp~CR zSgZ75VCG_;cNqBnl!$OWYH`uxmuY|cmIDh@t>y`>6xTYv<&CZN6(+BQvb0oi9isti z2oJneKYZ(v<!2wi>*(BUo13QUG^-w+Ud#`_cX)UC{J9I4&L3y>oj`_+29&b*=<CPs zxNY{6dv3qkw`-GRSQJ_#25e=RsAy^`;hT%UMynToetPivsV`)RP08KQH<}am)756% zznoRcIw_LHJaj<ViBr}#eFR0_vuW&`9Gg8jbNZ)yPwof0cKK)J9$FgYsyeX8>2Ief zCEV9ecy;j+N}hRRh4r*Xzb}lr7O7Nxr*`P0p}S!7*FImgJztw>VG09zgcxDFU(gMD z1cQifRFJ0;CA~-}9FN@g9^+cuw@Jq4@lcgYg)E8@5fI_xW#PHEz9vWv-B=++AW|+= zQSMri*5++2wSWi;h{6ytU=Um~9zN1yC(QWpy8&WKsXzeNwLuE7Sg;jnF**G(#j-TU z2!(byet4YMUOYz0+Nsa@v7<3@Odf{}wn8X!Fa|9}v4Y8mVcFr&PM^&fdg9Tj*VtwE zMTgB$vz2IqOjDF{o<N?S(Rj(1fKbF>yA;|Z!6jD~Bb*9Us^E_8oiUKbQo5Syt<cu2 z5o~}~<&fP=i8v!avxlPV@5vh2UPnv5TDp5ly#HiF(Yz{-@3H?*7;iIGcX;!ONrFUp z`75*gf4Up+$G507h=uKGIrrYqzfpehl_#^@d~5`VnK@`4gvJb54)m|W-@n0??@@|O zTl%Tu?AvE#$bZUIM%r^*XOlwDaRXdSE~Tb>l(qLqWx8RV$Jc8UmfLtCM^1<mV+o&X id+<TG74-gJ=YIe_9&h^M40neB0000<MNUMnLSTZkNs?Fq literal 0 HcmV?d00001 diff --git a/src/plugins/todo/images/todo.png b/src/plugins/todo/images/todo.png new file mode 100644 index 0000000000000000000000000000000000000000..78e6b5840db9886b1a0b7d044a2f63ee4efdb829 GIT binary patch literal 11012 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 literal 0 HcmV?d00001 diff --git a/src/plugins/todo/images/warning.png b/src/plugins/todo/images/warning.png new file mode 100755 index 0000000000000000000000000000000000000000..0bc86a3d12b368c056828620796c5b392f410b7f GIT binary patch literal 511 zcmV<b0RaAqP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00009a7bBm000XT z000XT0n*)m`~Uy|1ZP1_K>z@;j(q!3lK=n!AY({UO#lFTB>(_`g8%^e{{R4h=>PzA zFaQARU;qF*m;eA5Z<1fdMgRZ-bxA})RCwB@lRHWSQ5c242`VNLbCoE*(1C~&-ywpP z5Nzy3w6mJdQe8oG1#t(u0Ux-6U?FN_Pzwu%S%8`mA%Z4e^D|*^bWC9m9PZ=)|8oxq zuA#N25g7b|!&P#?CN6eY4%nhD{KJ_lwR5|vix)Fm_Hsu6LIh(kxoYuP*yZW2z{tFN z_qTxm_3cQvX~ueNu8-jQ1UmX5+>R-=dIB<R0vl6n%QT1bP(w>AOe}!Zq^F*PTzovE zdPk1o6A!W<NJ3{HN_qkXY9TGBEfdN%Xz-w2k@O-+&-Xw>KZA=zkD<aZ2g0@(jLs=v zprP-<B{DWt9BB@8ARHcZ2rK0UYScoxO0QdLaC#r!fe)7tryy<;Ddg<YzJaSV93UkQ zvJ%|jY+hdQSbB#0^C~Z)RK!;s8ivj|N@hjCN-il0?w3Hxsq#fuzks1(avEf|332tN zD3|;;qcIqgrV6MoD8KpZuMP-|S{PaWhn<GM^B15~lFj@;8E*gp002ovPDHLkV1naX B$;$u$ literal 0 HcmV?d00001 diff --git a/src/plugins/todo/keyword.cpp b/src/plugins/todo/keyword.cpp new file mode 100644 index 00000000000..a1d549c34a4 --- /dev/null +++ b/src/plugins/todo/keyword.cpp @@ -0,0 +1,26 @@ +#include "keyword.h" + +Keyword::Keyword() +{ +} + +Keyword::Keyword(QString name_, QIcon icon_, QColor warningColor_) : + name(name_), icon(icon_), warningColor(warningColor_) +{ +} + +QDataStream &operator<<(QDataStream &out, const Keyword &myObj) +{ + out << myObj.name; + out << myObj.icon; + out << myObj.warningColor; + return out; +} + +QDataStream &operator>>(QDataStream &in, Keyword &myObj) +{ + in >> myObj.name; + in >> myObj.icon; + in >> myObj.warningColor; + return in; +} diff --git a/src/plugins/todo/keyword.h b/src/plugins/todo/keyword.h new file mode 100644 index 00000000000..41a59dc3b2d --- /dev/null +++ b/src/plugins/todo/keyword.h @@ -0,0 +1,29 @@ +#ifndef KEYWORD_H +#define KEYWORD_H + +#include <QIcon> +#include <QColor> +#include <QString> +#include <QList> +#include <QMetaType> + +class Keyword +{ +public: + Keyword(); + Keyword(QString name_, QIcon icon_, QColor warningColor_); + QString name; + QIcon icon; + QColor warningColor; +}; + +typedef QList<Keyword> KeywordsList; + +QDataStream &operator<<(QDataStream &out, const Keyword &myObj); +QDataStream &operator>>(QDataStream &in, Keyword &myObj); + + +Q_DECLARE_METATYPE(KeywordsList) +Q_DECLARE_METATYPE(Keyword) + +#endif // KEYWORD_H diff --git a/src/plugins/todo/settingsdialog.cpp b/src/plugins/todo/settingsdialog.cpp new file mode 100644 index 00000000000..0efd8dc86aa --- /dev/null +++ b/src/plugins/todo/settingsdialog.cpp @@ -0,0 +1,138 @@ +#include "settingsdialog.h" +#include "ui_settingsdialog.h" +#include "addkeyworddialog.h" + +SettingsDialog::SettingsDialog(QWidget *parent) : + QWidget(parent), + ui(new Ui::SettingsDialog) +{ + ui->setupUi(this); + connect(this->ui->addButton, SIGNAL(clicked()), this, SLOT(addButtonClicked())); + connect(this->ui->removeButton, SIGNAL(clicked()), this, SLOT(removeButtonClicked())); + connect(this->ui->resetButton, SIGNAL(clicked()), this, SLOT(resetButtonClicked())); + + connect(this->ui->buildIssuesRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(settingsChanged())); + connect(this->ui->todoOutputRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(settingsChanged())); + connect(this->ui->projectRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(settingsChanged())); + connect(this->ui->currentFileRadioButton, SIGNAL(toggled(bool)), this, SIGNAL(settingsChanged())); + +} + +SettingsDialog::~SettingsDialog() +{ + delete ui; +} + +void SettingsDialog::setProjectRadioButtonEnabled(bool what) +{ + this->ui->projectRadioButton->setChecked(what); +} + +void SettingsDialog::setCurrentFileRadioButtonEnabled(bool what) +{ + this->ui->currentFileRadioButton->setChecked(what); +} + +void SettingsDialog::setBuildIssuesRadioButtonEnabled(bool what) +{ + this->ui->buildIssuesRadioButton->setChecked(what); +} + +void SettingsDialog::setTodoOutputRadioButtonEnabled(bool what) +{ + this->ui->todoOutputRadioButton->setChecked(what); +} + + +void SettingsDialog::addToKeywordsList(Keyword keyword) +{ + QListWidgetItem *item = new QListWidgetItem(keyword.icon, keyword.name); + item->setBackgroundColor(keyword.warningColor); + this->ui->keywordsList->addItem(item); +} + +void SettingsDialog::setKeywordsList(KeywordsList list) +{ + if (!list.count()) + { + resetButtonClicked(); + } + else + { + for (int i = 0; i < list.count(); ++i) + { + addToKeywordsList(list.at(i)); + } + } +} + +bool SettingsDialog::projectRadioButtonEnabled() +{ + return this->ui->projectRadioButton->isChecked(); +} + +bool SettingsDialog::currentFileRadioButtonEnabled() +{ + return this->ui->currentFileRadioButton->isChecked(); +} + +bool SettingsDialog::buildIssuesRadioButtonEnabled() +{ + return this->ui->buildIssuesRadioButton->isChecked(); +} + +bool SettingsDialog::todoOutputRadioButtonEnabled() +{ + return this->ui->todoOutputRadioButton->isChecked(); +} + +KeywordsList SettingsDialog::keywordsList() +{ + KeywordsList list; + for (int i = 0; i < this->ui->keywordsList->count(); ++i) + { + Keyword keyword; + keyword.name = this->ui->keywordsList->item(i)->text(); + keyword.icon = this->ui->keywordsList->item(i)->icon(); + keyword.warningColor = this->ui->keywordsList->item(i)->backgroundColor(); + list.append(keyword); + } + return list; +} + +void SettingsDialog::clearKeywordsList() +{ + this->ui->keywordsList->clear(); +} + +void SettingsDialog::addButtonClicked() +{ + Keyword keyword; + AddKeywordDialog *addKeywordDialog = new AddKeywordDialog(this); + if (addKeywordDialog->exec() == QDialog::Accepted) + { + keyword.name = addKeywordDialog->keywordName(); + keyword.icon = addKeywordDialog->keywordIcon(); + keyword.warningColor = addKeywordDialog->keywordColor(); + addToKeywordsList(keyword); + emit settingsChanged(); + } +} + +void SettingsDialog::removeButtonClicked() +{ + this->ui->keywordsList->takeItem(this->ui->keywordsList->currentRow()); + emit settingsChanged(); +} + +void SettingsDialog::resetButtonClicked() +{ + clearKeywordsList(); + addToKeywordsList(Keyword("TODO", QIcon(":/warning"), QColor("#BFFFC8"))); + addToKeywordsList(Keyword("NOTE", QIcon(":/info"), QColor("#E2DFFF"))); + addToKeywordsList(Keyword("FIXME", QIcon(":/error"), QColor("#FFBFBF"))); + addToKeywordsList(Keyword("BUG", QIcon(":/error"), QColor("#FFDFDF"))); + addToKeywordsList(Keyword("HACK", QIcon(":/info"), QColor("#FFFFAA"))); + + emit settingsChanged(); +} diff --git a/src/plugins/todo/settingsdialog.h b/src/plugins/todo/settingsdialog.h new file mode 100644 index 00000000000..316137d3477 --- /dev/null +++ b/src/plugins/todo/settingsdialog.h @@ -0,0 +1,45 @@ +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include <QWidget> +#include "keyword.h" + +namespace Ui { + class SettingsDialog; +} + +class SettingsDialog : public QWidget +{ + Q_OBJECT + +public: + explicit SettingsDialog(QWidget *parent = 0); + ~SettingsDialog(); + void setProjectRadioButtonEnabled(bool what); + void setCurrentFileRadioButtonEnabled(bool what); + void setBuildIssuesRadioButtonEnabled(bool what); + void setTodoOutputRadioButtonEnabled(bool what); + + void addToKeywordsList(Keyword); + void setKeywordsList(KeywordsList); + + bool projectRadioButtonEnabled(); + bool currentFileRadioButtonEnabled(); + bool buildIssuesRadioButtonEnabled(); + bool todoOutputRadioButtonEnabled(); + KeywordsList keywordsList(); + +signals: + void settingsChanged(); + +private slots: + void clearKeywordsList(); + void addButtonClicked(); + void removeButtonClicked(); + void resetButtonClicked(); + +private: + Ui::SettingsDialog *ui; +}; + +#endif // SETTINGSDIALOG_H diff --git a/src/plugins/todo/settingsdialog.ui b/src/plugins/todo/settingsdialog.ui new file mode 100644 index 00000000000..521a24c1ccb --- /dev/null +++ b/src/plugins/todo/settingsdialog.ui @@ -0,0 +1,143 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SettingsDialog</class> + <widget class="QWidget" name="SettingsDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>740</width> + <height>423</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_6"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_5"> + <item> + <widget class="QListWidget" name="keywordsList"/> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QPushButton" name="addButton"> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removeButton"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="resetButton"> + <property name="text"> + <string>Reset</string> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="projectGroupBox"> + <property name="title"> + <string>View options</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QRadioButton" name="currentFileRadioButton"> + <property name="text"> + <string>From current opened file</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="projectRadioButton"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>From projects</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="paneGroupBox"> + <property name="title"> + <string>Pane options</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QRadioButton" name="todoOutputRadioButton"> + <property name="text"> + <string>Show in "TODO Output"</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="buildIssuesRadioButton"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Show in "Build Issues"</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Icons and Colors show on +TODO Output pane only</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/todo/settingspage.cpp b/src/plugins/todo/settingspage.cpp new file mode 100644 index 00000000000..ef55cd1b1e9 --- /dev/null +++ b/src/plugins/todo/settingspage.cpp @@ -0,0 +1,118 @@ +#include "settingspage.h" +#include <coreplugin/icore.h> +#include <QList> +#include <QMessageBox> + +SettingsPage::SettingsPage(KeywordsList keywords, int projectOptions, int paneOptions, QObject *parent) : IOptionsPage(parent) +{ + this->keywords = keywords; + this->projectOptions = projectOptions; + this->paneOptions = paneOptions; +} + +SettingsPage::~SettingsPage() +{ + delete dialog; +} + +QString SettingsPage::id() const +{ + return "TodoSettings"; +} + +QString SettingsPage::trName() const +{ + return tr("TODO Plugin"); +} + +QString SettingsPage::category() const +{ + return "TODO"; +} + +QString SettingsPage::trCategory() const +{ + return tr("TODO"); +} + +QString SettingsPage::displayName() const +{ + return trName(); +} + +QString SettingsPage::displayCategory() const +{ + return trCategory(); +} + +QIcon SettingsPage::categoryIcon() const +{ + return QIcon(":/todo"); +} + + +QWidget *SettingsPage::createPage(QWidget *parent) +{ + settingsStatus = false; + + dialog = new SettingsDialog(parent); + dialog->setKeywordsList(keywords); + switch (projectOptions) + { + case 0: + dialog->setProjectRadioButtonEnabled(false); + dialog->setCurrentFileRadioButtonEnabled(true); + break; + case 1: + default: + dialog->setProjectRadioButtonEnabled(true); + dialog->setCurrentFileRadioButtonEnabled(false); + break; + } + + switch (paneOptions) + { + case 0: + dialog->setBuildIssuesRadioButtonEnabled(false); + dialog->setTodoOutputRadioButtonEnabled(true); + break; + case 1: + default: + dialog->setBuildIssuesRadioButtonEnabled(true); + dialog->setTodoOutputRadioButtonEnabled(false); + break; + } + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); + return dialog; +} + +void SettingsPage::apply() +{ + if (settingsStatus) + { + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("TODOPlugin"); + projectOptions = dialog->currentFileRadioButtonEnabled() ? 0 : 1; + paneOptions = dialog->todoOutputRadioButtonEnabled() ? 0 : 1; + keywords = dialog->keywordsList(); + settings->setValue("project_options", projectOptions); + settings->setValue("pane_options", paneOptions); + settings->setValue("keywords", qVariantFromValue(keywords)); + + settings->endGroup(); + settings->sync(); + + QMessageBox::information(dialog, tr("Information"), tr("The TODO plugin settings change will take effect after a restart of Qt Creator.")); + settingsStatus = false; + } +} + +void SettingsPage::finish() +{ + //apply(); +} + +void SettingsPage::settingsChanged() +{ + settingsStatus = true; +} diff --git a/src/plugins/todo/settingspage.h b/src/plugins/todo/settingspage.h new file mode 100644 index 00000000000..4f43f8afa88 --- /dev/null +++ b/src/plugins/todo/settingspage.h @@ -0,0 +1,39 @@ +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H + +#include <QIcon> +#include <coreplugin/dialogs/ioptionspage.h> +#include "settingsdialog.h" + + +class SettingsPage : public Core::IOptionsPage +{ + Q_OBJECT +public: + + SettingsPage(KeywordsList keywords = KeywordsList(), int projectOptions = 0, int paneOptions = 0, QObject *parent = 0); + ~SettingsPage(); + + QString id() const; + QString trName() const; + QString category() const; + QString trCategory() const; + QString displayName() const; + QIcon categoryIcon() const; + QString displayCategory() const; + QWidget *createPage(QWidget *parent); + void apply(); + void finish(); + +public slots: + void settingsChanged(); + +private: + SettingsDialog *dialog; + bool settingsStatus; + int projectOptions; + int paneOptions; + KeywordsList keywords; +}; + +#endif // SETTINGSPAGE_H diff --git a/src/plugins/todo/todooutputpane.cpp b/src/plugins/todo/todooutputpane.cpp new file mode 100755 index 00000000000..57fe83fba50 --- /dev/null +++ b/src/plugins/todo/todooutputpane.cpp @@ -0,0 +1,174 @@ +/* + * + * TODO plugin - Add pane with list all TODO, FIXME, etc. comments. + * + * Copyright (C) 2010 VasiliySorokin + * + * Authors: Vasiliy Sorokin <sorokin.vasiliy@gmail.com> + * + * This file is part of TODO plugin for QtCreator. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of the vsorokin nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ +#include "todooutputpane.h" +#include <QListWidgetItem> +#include <QRegExp> +#include <QIcon> + +// TODO: make fix +// NOTE: make note +// HACK: make hack +// BUG: make bug + +TodoOutputPane::TodoOutputPane(QObject *parent) : IOutputPane(parent) +{ + todoList = new QListWidget(); + todoList->setFlow(QListView::TopToBottom); + todoList->setFrameStyle(QFrame::NoFrame); + lastCurrentRow = 0; +} + +TodoOutputPane::~TodoOutputPane() +{ + delete todoList; +} + +void TodoOutputPane::addItem(const QString &text, const QString &file, const int rowNumber, const QIcon &icon, const QColor &color) +{ + QListWidgetItem *newItem = new QListWidgetItem(); + newItem->setBackgroundColor(color); + newItem->setIcon(icon); + newItem->setData(Qt::UserRole + 1, file); + newItem->setData(Qt::UserRole + 2, rowNumber); + newItem->setToolTip(file + ":" + QString::number(rowNumber)); + + newItem->setText(file.right(file.size() - file.lastIndexOf("/") - 1) + ":" + QString::number(rowNumber) + ": " + text); + + todoList->addItem(newItem); +} + +QListWidget *TodoOutputPane::getTodoList() const +{ + return todoList; +} + + +QWidget *TodoOutputPane::outputWidget(QWidget */*parent*/) +{ + return todoList; +} + +QList<QWidget*> TodoOutputPane::toolBarWidgets() const +{ + return QList<QWidget*>(); +} + +QString TodoOutputPane::name() const +{ + return tr("TODO Output"); +} + +QString TodoOutputPane::displayName() const +{ + return name(); +} + +int TodoOutputPane::priorityInStatusBar() const +{ + return 1; +} + +void TodoOutputPane::clearContents() +{ + todoList->clear(); +} + + +void TodoOutputPane::clearContents(QString filename) +{ + int i = 0; + lastCurrentRow = 0; + while (i < todoList->count()) + { + if (!filename.compare(todoList->item(i)->data(Qt::UserRole + 1).toString())) + { + if (lastCurrentRow == 0) + lastCurrentRow = todoList->currentRow(); + todoList->takeItem(i); + } + else + { + ++i; + } + } +} + + +void TodoOutputPane::visibilityChanged(bool visible) +{ + todoList->setVisible(visible); +} + +void TodoOutputPane::setFocus() +{ + todoList->setFocus(); +} + +bool TodoOutputPane::hasFocus() +{ + return todoList->hasFocus(); +} + +bool TodoOutputPane::canFocus() +{ + return true; +} + +bool TodoOutputPane::canNavigate() +{ + return todoList->count() > 1; +} + +bool TodoOutputPane::canNext() +{ + return todoList->currentRow() < todoList->count() && todoList->count() > 1; +} + +bool TodoOutputPane::canPrevious() +{ + return todoList->currentRow() > 0 && todoList->count() > 1; +} + +void TodoOutputPane::goToNext() +{ + todoList->setCurrentRow(todoList->currentRow() + 1); +} + +void TodoOutputPane::goToPrev() +{ + todoList->setCurrentRow(todoList->currentRow() - 1); +} + +void TodoOutputPane::sort() +{ + todoList->sortItems(Qt::AscendingOrder); + if (todoList->count() > 0) + todoList->setCurrentRow(lastCurrentRow < todoList->count() ? lastCurrentRow : todoList->count() - 1); +} diff --git a/src/plugins/todo/todooutputpane.h b/src/plugins/todo/todooutputpane.h new file mode 100755 index 00000000000..2192eecc83a --- /dev/null +++ b/src/plugins/todo/todooutputpane.h @@ -0,0 +1,76 @@ +/* + * + * TODO plugin - Add pane with list all TODO, FIXME, etc. comments. + * + * Copyright (C) 2010 VasiliySorokin + * + * Authors: Vasiliy Sorokin <sorokin.vasiliy@gmail.com> + * + * This file is part of TODO plugin for QtCreator. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of the vsorokin nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ + +#ifndef TODOOUTPUTPANE_H +#define TODOOUTPUTPANE_H + +#include "keyword.h" +#include <coreplugin/ioutputpane.h> +#include <QObject> +#include <QListWidget> + +class TodoOutputPane : public Core::IOutputPane +{ +public: + TodoOutputPane(QObject *parent); + ~TodoOutputPane(); + + QWidget *outputWidget(QWidget *parent); + QList<QWidget*> toolBarWidgets() const; + QString name() const; + QString displayName() const; + + int priorityInStatusBar() const; + + void clearContents(); + void clearContents(QString filename); + void visibilityChanged(bool visible); + + void setFocus(); + bool hasFocus(); + bool canFocus(); + + bool canNavigate(); + bool canNext(); + bool canPrevious(); + void goToNext(); + void goToPrev(); + + void sort(); + + void addItem(const QString &text, const QString &file, const int rowNumber, const QIcon &icon, const QColor &color); + QListWidget *getTodoList() const; + +private: + QListWidget *todoList; + int lastCurrentRow; +}; + +#endif // TODOOUTPUTPANE_H diff --git a/src/plugins/todo/todoplugin.cpp b/src/plugins/todo/todoplugin.cpp new file mode 100755 index 00000000000..79c8df7fe29 --- /dev/null +++ b/src/plugins/todo/todoplugin.cpp @@ -0,0 +1,376 @@ +/* + * + * TODO plugin - Add pane with list all TODO, FIXME, etc. comments. + * + * Copyright (C) 2010 VasiliySorokin + * + * Authors: Vasiliy Sorokin <sorokin.vasiliy@gmail.com> + * + * This file is part of TODO plugin for QtCreator. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of the vsorokin nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ + +#include "todoplugin.h" +#include <QtPlugin> +#include <QStringList> +#include <QtConcurrentRun> + +#include <coreplugin/icore.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> +#include <qtconcurrent/runextensions.h> +#include <texteditor/basetexteditor.h> +#include <texteditor/itexteditor.h> +#include <coreplugin/ifile.h> +#include <extensionsystem/pluginmanager.h> +#include <QKeySequence> +#include <QAction> +#include <QFile> +#include <QFileInfo> +#include <QDataStream> +#include <QSettings> +#include <QMessageBox> +#include <QTextCodec> +#include <coreplugin/progressmanager/progressmanager.h> +#include <coreplugin/progressmanager/futureprogress.h> + +TodoPlugin::TodoPlugin() +{ + qRegisterMetaTypeStreamOperators<Keyword>("Keyword"); + qRegisterMetaTypeStreamOperators<KeywordsList>("KeywordsList"); + currentProject = 0; + inReading = false; + readSettings(); +} + +TodoPlugin::~TodoPlugin() +{ +// Do notning +} + +void TodoPlugin::readSettings() +{ + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("TODOPlugin"); + projectOptions = settings->value("project_options", 0).toInt(); + paneOptions = settings->value("pane_options", 0).toInt(); + KeywordsList defaultKeywords; + defaultKeywords.append(Keyword("TODO", QIcon(":/warning"), QColor("#BFFFC8"))); + defaultKeywords.append(Keyword("NOTE", QIcon(":/info"), QColor("#E2DFFF"))); + defaultKeywords.append(Keyword("FIXME", QIcon(":/error"), QColor("#FFBFBF"))); + defaultKeywords.append(Keyword("BUG", QIcon(":/error"), QColor("#FFDFDF"))); + defaultKeywords.append(Keyword("HACK", QIcon(":/info"), QColor("#FFFFAA"))); + + keywords = settings->value("keywords", qVariantFromValue(defaultKeywords)).value<KeywordsList>(); + settings->endGroup(); +} + +bool TodoPlugin::initialize(const QStringList& args, QString *errMsg) +{ + Q_UNUSED(args); + Q_UNUSED(errMsg); + patternString = generatePatternString(); + settingsPage = new SettingsPage(keywords, projectOptions, paneOptions, this); + if (paneOptions == 0) + { + outPane = new TodoOutputPane(this); + addAutoReleasedObject(outPane); + connect(outPane->getTodoList(), SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(gotoToRowInFile(QListWidgetItem*))); + connect(outPane->getTodoList(), SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(gotoToRowInFile(QListWidgetItem*))); + } + else + { + ExtensionSystem::PluginManager* pluginManager = ExtensionSystem::PluginManager::instance(); + taskHub = pluginManager->getObject<ProjectExplorer::TaskHub>(); + if (!taskHub) + { + paneOptions = 1; + outPane = new TodoOutputPane(this); + addAutoReleasedObject(outPane); + connect(outPane->getTodoList(), SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(gotoToRowInFile(QListWidgetItem*))); + connect(outPane->getTodoList(), SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(gotoToRowInFile(QListWidgetItem*))); + QMessageBox::warning((QWidget *)Core::ICore::instance()->mainWindow(), tr("TODO plugin"), tr("Task window object not found.\nWork in TODO Output pane.")); + } + else + { + taskHub->addCategory("todoplugin", tr("TODOs comments")); + } + + } + addAutoReleasedObject(settingsPage); + + connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)), this, SLOT(currentEditorChanged(Core::IEditor*))); + if (projectOptions != 0) + { + connect(ProjectExplorer::ProjectExplorerPlugin::instance(), SIGNAL(currentProjectChanged(ProjectExplorer::Project*)), this, SLOT(projectChanged(ProjectExplorer::Project *))); + } + return true; +} + +void TodoPlugin::extensionsInitialized() +{ +// Do nothing +} + +void TodoPlugin::shutdown() +{ +// Do nothing +} + +void TodoPlugin::showPane() +{ + if (paneOptions == 0) + { + outPane->visibilityChanged(true); + outPane->setFocus(); + } +} + +void TodoPlugin::gotoToRowInFile(QListWidgetItem *item) +{ + int row = item->data(Qt::UserRole + 2).toInt(); + QString file = item->data(Qt::UserRole + 1).toString(); + + if (QFileInfo(file).exists()) + { + Core::IEditor *editor = Core::EditorManager::instance()->openEditor(file); + editor->gotoLine(row); + } +} + +void TodoPlugin::currentEditorChanged(Core::IEditor *editor) +{ + if (inReading) + return; + if (projectOptions == 0) + { + if (paneOptions == 0) + { + outPane->clearContents(); + } + else + { + taskHub->clearTasks("todoplugin"); + } + } + + if (!editor) + { + return; + } + connect(editor->file(), SIGNAL(changed()), this, SLOT(fileChanged())); + QString fileName = editor->file()->fileName(); + if (projectOptions == 0) + readFile(fileName); + +} + +void TodoPlugin::removeFromLocalTasks(QString filename) +{ + for (int i = 0; i < tasks.count(); ++i) + { + if (!tasks.at(i).file.compare(filename)) + { + tasks.removeAt(i); + } + } +} + +void TodoPlugin::addLocalTaskToTaskWindow() +{ + for (int i = 0; i < tasks.count(); ++i) + { + taskHub->addTask(tasks.at(i)); + } +} + +void TodoPlugin::fileChanged() +{ + Core::IFile *file = (Core::IFile *)sender(); + if (file) + { + if (projectOptions == 0) + { + if (paneOptions == 0) + { + outPane->clearContents(); + } + else + { + taskHub->clearTasks("todoplugin"); + } + } + else + { + if (paneOptions == 0) + { + outPane->clearContents(file->fileName()); + } + else + { + taskHub->clearTasks("todoplugin"); + removeFromLocalTasks(file->fileName()); + } + } + readFile(file->fileName()); + } +} + +Keyword TodoPlugin::prepareOutputString(QString &text) +{ + Keyword keyword; + for(int i = 0; i < keywords.count(); ++i) + { + QRegExp keywordExp("//\\s*" + keywords.at(i).name + "(:|\\s)", Qt::CaseInsensitive); + if (text.contains(keywordExp)) + { + text = text.replace("\n", ""); + text = text.replace("\r", ""); + text = text.replace(keywordExp, keywords.at(i).name + ": "); + text = text.trimmed(); + keyword = keywords.at(i); + break; + } + } + return keyword; +} + +void TodoPlugin::readFile(QString fileName) +{ + QFile file(fileName); + if (!file.open(QFile::ReadOnly | QFile::Text)) + return; + int i = 1; + while (!file.atEnd()) + { + QString currentLine = file.readLine(); + if (currentLine.contains(QRegExp(patternString, Qt::CaseInsensitive))) + { + Keyword resultKeyword = prepareOutputString(currentLine); + QTextCodec *unicodeCodec = QTextCodec::codecForLocale(); + currentLine = unicodeCodec->toUnicode(currentLine.toAscii()); + if (paneOptions == 0) + { + outPane->addItem(currentLine, fileName, i, resultKeyword.icon, resultKeyword.warningColor); + if (!inReading) + outPane->sort(); + } + else + { + ProjectExplorer::Task task(ProjectExplorer::Task::Unknown, currentLine, fileName, i, "todoplugin"); + tasks.append(task); + } + } + ++i; + } + + if (paneOptions != 0 && !inReading) + { + qSort(tasks.begin(), tasks.end(), TodoPlugin::taskLessThan); + addLocalTaskToTaskWindow(); + } +} + +QString TodoPlugin::generatePatternString() +{ + QString result = ""; + + if (keywords.count()) + { + for (int i = 0; i < keywords.count() - 1; ++i) + { + result += "//\\s*" + keywords.at(i).name + "(:|\\s)|"; + } + result += "//\\s*" + keywords.at(keywords.count() - 1).name + "(:|\\s)"; + } + return result; +} + +void TodoPlugin::projectChanged(ProjectExplorer::Project *project) +{ + if (!project) + return; + if (inReading) + return; + currentProject = project; + if (paneOptions == 0) + { + outPane->clearContents(); + } + else + { + taskHub->clearTasks("todoplugin"); + } + inReading = true; + + QFuture<void> result = QtConcurrent::run(&TodoPlugin::readCurrentProject, this); + Core::ICore::instance()->progressManager()->addTask(result, tr("Todoscan"), "Todo.Plugin.Scanning"); + +} + +void TodoPlugin::readCurrentProject(QFutureInterface<void> &future, TodoPlugin *instance) +{ + QStringList filesList = instance->currentProject->files(ProjectExplorer::Project::ExcludeGeneratedFiles); + future.setProgressRange(0, filesList.count()-1); + for (int i = 0; i < filesList.count(); ++i) + { + instance->readFile(filesList.at(i)); + future.setProgressValue(i); + } + + if (instance->paneOptions == 0) + { + instance->outPane->sort(); + } + else + { + qSort(instance->tasks.begin(), instance->tasks.end(), TodoPlugin::taskLessThan); + instance->addLocalTaskToTaskWindow(); + } + + instance->inReading = false; + future.reportFinished(); +} + +bool TodoPlugin::taskLessThan(const ProjectExplorer::Task &t1, const ProjectExplorer::Task &t2) +{ + if (!t1.file.right(t1.file.size() - t1.file.lastIndexOf("/") - 1).compare(t2.file.right(t2.file.size() - t2.file.lastIndexOf("/") - 1))) + { + if (t1.line < t2.line) + { + return true; + } + else + { + return false; + } + } + else + { + return t1.file.right(t1.file.size() - t1.file.lastIndexOf("/") - 1).compare(t2.file.right(t2.file.size() - t2.file.lastIndexOf("/") - 1)) < 0; + } +} + + + +Q_EXPORT_PLUGIN(TodoPlugin) + + diff --git a/src/plugins/todo/todoplugin.h b/src/plugins/todo/todoplugin.h new file mode 100755 index 00000000000..1623ac83f34 --- /dev/null +++ b/src/plugins/todo/todoplugin.h @@ -0,0 +1,91 @@ +/* + * + * TODO plugin - Add pane with list all TODO, FIXME, etc. comments. + * + * Copyright (C) 2010 VasiliySorokin + * + * Authors: Vasiliy Sorokin <sorokin.vasiliy@gmail.com> + * + * This file is part of TODO plugin for QtCreator. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of the vsorokin nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * +*/ + +#ifndef TODOPLUGIN_H +#define TODOPLUGIN_H +#include <QFuture> +#include <QList> +#include <extensionsystem/iplugin.h> +#include <coreplugin/editormanager/editormanager.h> +#include <projectexplorer/task.h> +#include <projectexplorer/taskhub.h> +#include <projectexplorer/projectexplorer.h> +#include <projectexplorer/project.h> +#include "todooutputpane.h" +#include "settingspage.h" +#include "keyword.h" + + +class TodoPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT +public: + TodoPlugin(); + ~TodoPlugin(); + void extensionsInitialized(); + bool initialize(const QStringList & arguments, QString * errorString); + void shutdown(); + +public slots: + void showPane(); + void gotoToRowInFile(QListWidgetItem *); + void currentEditorChanged(Core::IEditor *editor); + void fileChanged(); + void projectChanged(ProjectExplorer::Project *); + +signals: + void updateFutureValue(int value); + void setFutureRange(int, int); + +private: + void readFile(QString); + static void readCurrentProject(QFutureInterface<void> &future, TodoPlugin* instance); + void removeFromLocalTasks(QString filename); + void addLocalTaskToTaskWindow(); + + static bool taskLessThan(const ProjectExplorer::Task &t1, const ProjectExplorer::Task &t2); + + Keyword prepareOutputString(QString &text); + QString generatePatternString(); + void readSettings(); + TodoOutputPane *outPane; + ProjectExplorer::TaskHub *taskHub; + SettingsPage *settingsPage; + QString patternString; + KeywordsList keywords; + int projectOptions; + int paneOptions; + ProjectExplorer::Project *currentProject; + QList<ProjectExplorer::Task> tasks; + bool inReading; + QFutureInterface<void> *progressObject; +}; +#endif // TODOPLUGIN_H + diff --git a/src/plugins/todo/todoplugin.pro b/src/plugins/todo/todoplugin.pro new file mode 100755 index 00000000000..83ce31cda10 --- /dev/null +++ b/src/plugins/todo/todoplugin.pro @@ -0,0 +1,46 @@ +CONFIG += release +TEMPLATE = lib +TARGET = todo +PROVIDER = vsorokin + +QTC_SOURCE_DIR = /home/vass/qt-creator +IDE_SOURCE_TREE = $$QTC_SOURCE_DIR +QTC_BUILD_DIR = /opt/qtcreator-2.1.81 + +DESTDIR = $$QTC_BUILD_DIR/lib/qtcreator/plugins/$$(PROVIDER) +IDE_BUILD_TREE = $$QTC_BUILD_DIR + +LIBS += -L$$IDE_BUILD_TREE/lib/qtcreator/ \ + -L$$IDE_BUILD_TREE/lib/qtcreator/plugins/Nokia + +include( $$IDE_SOURCE_TREE/src/qtcreatorplugin.pri ) +include( $$IDE_SOURCE_TREE/src/plugins/coreplugin/coreplugin.pri ) +include( $$IDE_SOURCE_TREE/src/plugins/projectexplorer/projectexplorer.pri ) +include( $$IDE_SOURCE_TREE/src/plugins/texteditor/texteditor.pri ) + +INCLUDEPATH += $$IDE_SOURCE_TREE/src \ + $$IDE_SOURCE_TREE/src/plugins \ + $$IDE_SOURCE_TREE/src/libs \ + $$IDE_SOURCE_TREE/src/libs/extensionsystem + + +HEADERS += todoplugin.h \ + todooutputpane.h \ + settingsdialog.h \ + settingspage.h \ + addkeyworddialog.h \ + keyword.h +SOURCES += todoplugin.cpp \ + todooutputpane.cpp \ + settingsdialog.cpp \ + settingspage.cpp \ + addkeyworddialog.cpp \ + keyword.cpp +OTHER_FILES += todo.pluginspec + +RESOURCES += \ + icons.qrc + +FORMS += \ + settingsdialog.ui \ + addkeyworddialog.ui -- GitLab