1 /*
2 	Actiona
3 	Copyright (C) 2005 Jonathan Mercier-Ganady
4 
5 	Actiona is free software: you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation, either version 3 of the License, or
8 	(at your option) any later version.
9 
10 	Actiona is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18 	Contact : jmgr@jmgr.info
19 */
20 
21 #include "consolewidget.h"
22 #include "ui_consolewidget.h"
23 
24 #include <QStandardItemModel>
25 
26 namespace ActionTools
27 {
ConsoleWidget(QWidget * parent)28 	ConsoleWidget::ConsoleWidget(QWidget *parent)
29 		: QWidget(parent),
30 		ui(new Ui::ConsoleWidget)
31 
32 	{
33 		ui->setupUi(this);
34 
35         ui->console->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
36         ui->console->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
37 		ui->clearPushButton->setEnabled(false);
38 	}
39 
~ConsoleWidget()40 	ConsoleWidget::~ConsoleWidget()
41 	{
42 		delete ui;
43 	}
44 
setup(QStandardItemModel * model)45 	void ConsoleWidget::setup(QStandardItemModel *model)
46 	{
47 		mModel = (model ? model : new QStandardItemModel(0, 1, this));
48 
49 		QItemSelectionModel *oldModel = ui->console->selectionModel();
50 		ui->console->setModel(mModel);
51 		delete oldModel;
52 
53         // Note: this connection is still string based because it uses a private signal
54         connect(mModel, SIGNAL(rowsInserted(QModelIndex,int,int)), ui->console, SLOT(scrollToBottom()));
55 	}
56 
addScriptParameterLine(const QString & message,int parameter,int line,int column,Type type)57 	void ConsoleWidget::addScriptParameterLine(const QString &message, int parameter, int line, int column, Type type)
58 	{
59 		auto item = new QStandardItem();
60 
61 		item->setData(parameter, ParameterRole);
62 		item->setData(line, LineRole);
63 		item->setData(column, ColumnRole);
64 
65         addLine(message, item, Parameters, type);
66     }
67 
addResourceLine(const QString & message,const QString & resourceKey,ConsoleWidget::Type type)68     void ConsoleWidget::addResourceLine(const QString &message, const QString &resourceKey, ConsoleWidget::Type type)
69     {
70         auto item = new QStandardItem();
71 
72         item->setData(resourceKey, ResourceRole);
73 
74         addLine(message, item, Resources, type);
75     }
76 
addActionLine(const QString & message,qint64 actionRuntimeId,const QString & field,const QString & subField,int line,int column,Type type)77 	void ConsoleWidget::addActionLine(const QString &message, qint64 actionRuntimeId, const QString &field, const QString &subField, int line, int column, Type type)
78 	{
79 		auto item = new QStandardItem();
80 
81 		item->setData(actionRuntimeId, ActionRole);
82 		item->setData(field, FieldRole);
83 		item->setData(subField, SubFieldRole);
84 		item->setData(line, LineRole);
85 		item->setData(column, ColumnRole);
86 
87 		addLine(message, item, Action, type);
88 	}
89 
addUserLine(const QString & message,qint64 actionRuntimeId,const QString & field,const QString & subField,int line,int column,const QStringList & backtrace,Type type)90 	void ConsoleWidget::addUserLine(const QString &message, qint64 actionRuntimeId, const QString &field, const QString &subField, int line, int column, const QStringList &backtrace, Type type)
91 	{
92 		auto item = new QStandardItem();
93 
94 		item->setData(actionRuntimeId, ActionRole);
95 		item->setData(field, FieldRole);
96 		item->setData(subField, SubFieldRole);
97 		item->setData(line, LineRole);
98 		item->setData(column, ColumnRole);
99 		item->setData(backtrace, BacktraceRole);
100 
101 		addLine(message, item, User, type);
102 	}
103 
addExceptionLine(const QString & message,qint64 actionRuntimeId,int exception,Type type)104 	void ConsoleWidget::addExceptionLine(const QString &message, qint64 actionRuntimeId, int exception, Type type)
105 	{
106 		auto item = new QStandardItem();
107 
108 		item->setData(actionRuntimeId, ActionRole);
109 		item->setData(exception, ExceptionRole);
110 
111 		addLine(message, item, Exception, type);
112 	}
113 
addDesignErrorLine(const QString & message,Type type)114 	void ConsoleWidget::addDesignErrorLine(const QString &message, Type type)
115 	{
116 		auto item = new QStandardItem();
117 
118 		addLine(message, item, DesignError, type);
119 	}
120 
addStartSeparator()121 	void ConsoleWidget::addStartSeparator()
122 	{
123 		mStartTime = QDateTime::currentDateTime();
124 		QStandardItem *item = new QStandardItem(tr("Execution started at %1").arg(mStartTime.toString(QStringLiteral("dd/MM/yyyy hh:mm:ss:zzz"))));
125 		item->setTextAlignment(Qt::AlignCenter);
126 		addSeparator(item);
127 	}
128 
addEndSeparator()129 	void ConsoleWidget::addEndSeparator()
130 	{
131 		const QDateTime &currentDateTime = QDateTime::currentDateTime();
132 		int days = mStartTime.daysTo(currentDateTime);
133 
134 		QString durationString;
135 		if(days > 0)
136 			durationString += tr("%n day(s) ", "", days);
137         mStartTime = mStartTime.addDays(-days);
138 
139 		int seconds = mStartTime.secsTo(currentDateTime);
140 		int hours = seconds / 3600;
141 		seconds = seconds % 3600;
142 		int minutes = seconds / 60;
143 		seconds = seconds % 60;
144 
145 		if(hours > 0)
146 			durationString += tr("%n hour(s) ", "", hours);
147 		if(minutes > 0)
148 			durationString += tr("%n minute(s) ", "", minutes);
149 		if(seconds > 0)
150 			durationString += tr("%n second(s) ", "", seconds);
151 		int startMSec = mStartTime.toString(QStringLiteral("z")).toInt();
152 		int endMSec = currentDateTime.toString(QStringLiteral("z")).toInt();
153 		int msec = (endMSec > startMSec) ? (endMSec - startMSec) : (1000 - (startMSec - endMSec));
154 
155 		durationString += tr("%n millisecond(s)", "", msec);
156 
157 		QStandardItem *item = new QStandardItem(tr("Execution ended at %1\n(%2)").arg(currentDateTime.toString(QStringLiteral("dd/MM/yyyy hh:mm:ss:zzz"))).arg(durationString));
158 		item->setTextAlignment(Qt::AlignCenter);
159 		addSeparator(item);
160 	}
161 
clear()162 	void ConsoleWidget::clear()
163 	{
164 		mModel->removeRows(0, mModel->rowCount());
165 
166         ui->clearPushButton->setEnabled(false);
167     }
168 
clearExceptSeparators()169     void ConsoleWidget::clearExceptSeparators()
170     {
171         int rowCount = mModel->rowCount();
172 
173         for(int index = rowCount - 1; index >= 0; --index)
174         {
175             QStandardItem *item = mModel->item(index);
176 
177             Type type = item->data(TypeRole).value<Type>();
178 
179             if(type != Separator)
180                 mModel->removeRow(index);
181         }
182 
183         if(mModel->rowCount() == 0)
184             ui->clearPushButton->setEnabled(false);
185     }
186 
updateClearButton()187 	void ConsoleWidget::updateClearButton()
188 	{
189 		ui->clearPushButton->setEnabled(mModel->rowCount() > 0);
190 	}
191 
on_clearPushButton_clicked()192 	void ConsoleWidget::on_clearPushButton_clicked()
193 	{
194 		clear();
195 	}
196 
on_console_doubleClicked(const QModelIndex & index)197 	void ConsoleWidget::on_console_doubleClicked(const QModelIndex &index)
198 	{
199 		emit itemDoubleClicked(index.row());
200 	}
201 
on_console_clicked(const QModelIndex & index)202 	void ActionTools::ConsoleWidget::on_console_clicked(const QModelIndex &index)
203 	{
204 		emit itemClicked(index.row());
205 	}
206 
addLine(const QString & message,QStandardItem * item,Source source,Type type)207 	void ConsoleWidget::addLine(const QString &message, QStandardItem *item, Source source, Type type)
208 	{
209 		QIcon icon;
210 
211 		switch(type)
212 		{
213 		case Information:
214 			icon = QIcon(QStringLiteral(":/images/information.png"));
215 			break;
216 		case Warning:
217 			icon = QIcon(QStringLiteral(":/images/warning.png"));
218 			break;
219 		case Error:
220 			icon = QIcon(QStringLiteral(":/images/error.png"));
221 			break;
222         case Separator:
223             Q_ASSERT(false && "Should use addSeparator instead");
224             break;
225 		}
226 
227 		item->setText(message);
228 		if(source == DesignError)
229 			item->setToolTip(message);
230 		else
231 			item->setToolTip(message + tr("\nDouble-click to show"));
232 		item->setIcon(icon);
233 		item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
234 		item->setData(QVariant::fromValue<Source>(source), SourceRole);
235 		item->setData(QVariant::fromValue<Type>(type), TypeRole);
236 
237 		mModel->appendRow(item);
238 
239         qApp->processEvents(); // This is needed so that the console output gets displayed before a blocking call (such as sleep)
240         // It would be better not to have any blocking code calls, but then this would cause some bugs when the user cancels the execution during a non-blocking sleep
241         // Pausing the execution and then resuming it after some time seems to be the best way to do this, but would require important changes in the code
242 
243 		ui->clearPushButton->setEnabled(true);
244 	}
245 
addSeparator(QStandardItem * item)246 	void ConsoleWidget::addSeparator(QStandardItem *item)
247 	{
248 		item->setFlags(nullptr);
249 		item->setBackground(QBrush(Qt::lightGray));
250                 item->setForeground(QBrush(Qt::white));
251 
252 		QFont appFont = QApplication::font();
253 		appFont.setPointSize(7);
254 
255 		item->setFont(appFont);
256         item->setData(QVariant::fromValue<Type>(Separator), TypeRole);
257 
258 		mModel->appendRow(item);
259 	}
260 }
261