]> git.lyx.org Git - features.git/blob - src/frontends/qt/GuiLog.cpp
Regenerate previews after zoom (#11919)
[features.git] / src / frontends / qt / GuiLog.cpp
1 /**
2  * \file GuiLog.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Angus Leeming
8  * \author Jürgen Spitzmüller
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "GuiLog.h"
16
17 #include "GuiApplication.h"
18 #include "qt_helpers.h"
19 #include "Lexer.h"
20
21 #include "frontends/Clipboard.h"
22
23 #include "support/docstring.h"
24 #include "support/gettext.h"
25 #include "support/lstrings.h"
26
27 #include <QTextBrowser>
28 #include <QSyntaxHighlighter>
29 #include <QClipboard>
30
31 #include <fstream>
32 #include <sstream>
33
34 using namespace std;
35 using namespace lyx::support;
36
37 namespace lyx {
38 namespace frontend {
39
40
41 // Regular expressions needed at several places
42 // FIXME: These regexes are incomplete. It would be good if we could collect those used in LaTeX::scanLogFile
43 //        and LaTeX::scanBlgFile and re-use them here!(spitz, 2013-05-27)
44 #if QT_VERSION < 0x060000
45 // Information
46 QRegExp exprInfo("^(Document Class:|LaTeX Font Info:|File:|Package:|Language:|.*> INFO - |\\(|\\\\).*$");
47 // Warnings
48 QRegExp exprWarning("^(## Warning|LaTeX Warning|LaTeX Font Warning|Package [\\w\\.]+ Warning|Class \\w+ Warning|Warning--|Underfull|Overfull|.*> WARN - ).*$");
49 // Errors
50 QRegExp exprError("^(ERROR: |!|.*---line [0-9]+ of file|.*> FATAL - |.*> ERROR - |Missing character: There is no ).*$");
51 #else
52 // Information
53 QRegularExpression exprInfo("^(Document Class:|LaTeX Font Info:|File:|Package:|Language:|.*> INFO - |\\(|\\\\).*$");
54 // Warnings
55 QRegularExpression exprWarning("^(## Warning|LaTeX Warning|LaTeX Font Warning|Package [\\w\\-\\.]+ Warning|Class \\w+ Warning|Warning--|Underfull|Overfull|.*> WARN - ).*$");
56 // Errors
57 QRegularExpression exprError("^(ERROR: |!|.*---line [0-9]+ of file|.*> FATAL - |.*> ERROR - |Missing character: There is no ).*$");
58 #endif
59
60
61 /////////////////////////////////////////////////////////////////////
62 //
63 // LogHighlighter
64 //
65 ////////////////////////////////////////////////////////////////////
66
67 class LogHighlighter : public QSyntaxHighlighter
68 {
69 public:
70         LogHighlighter(QTextDocument * parent);
71
72 private:
73         void highlightBlock(QString const & text) override;
74
75 private:
76         QTextCharFormat infoFormat;
77         QTextCharFormat warningFormat;
78         QTextCharFormat errorFormat;
79 };
80
81
82
83 LogHighlighter::LogHighlighter(QTextDocument * parent)
84         : QSyntaxHighlighter(parent)
85 {
86         infoFormat.setForeground(Qt::darkGray);
87         warningFormat.setForeground(Qt::darkBlue);
88         errorFormat.setForeground(Qt::red);
89 }
90
91
92 void LogHighlighter::highlightBlock(QString const & text)
93 {
94 #if QT_VERSION < 0x060000
95         // Info
96         int index = exprInfo.indexIn(text);
97         while (index >= 0) {
98                 int length = exprInfo.matchedLength();
99                 setFormat(index, length, infoFormat);
100                 index = exprInfo.indexIn(text, index + length);
101         }
102         // LaTeX Warning:
103         index = exprWarning.indexIn(text);
104         while (index >= 0) {
105                 int length = exprWarning.matchedLength();
106                 setFormat(index, length, warningFormat);
107                 index = exprWarning.indexIn(text, index + length);
108         }
109         // ! error
110         index = exprError.indexIn(text);
111         while (index >= 0) {
112                 int length = exprError.matchedLength();
113                 setFormat(index, length, errorFormat);
114                 index = exprError.indexIn(text, index + length);
115         }
116 #else
117         // Info
118         QRegularExpressionMatch match = exprInfo.match(text);
119         int index = match.capturedStart(1);
120         while (index >= 0) {
121                 int length = match.capturedEnd(1) - index;
122                 setFormat(index, length, infoFormat);
123                 match = exprInfo.match(text, index + length);
124                 index = match.capturedStart(1);
125         }
126         // LaTeX Warning:
127         match = exprWarning.match(text);
128         index = match.capturedStart(1);
129         while (index >= 0) {
130                 int length = match.capturedEnd(1) - index;
131                 setFormat(index, length, warningFormat);
132                 match = exprWarning.match(text, index + length);
133                 index = match.capturedStart(1);
134         }
135         // ! error
136         match = exprError.match(text);
137         index = match.capturedStart(1);
138         while (index >= 0) {
139                 int length = match.capturedEnd(1) - index;
140                 setFormat(index, length, errorFormat);
141                 match = exprError.match(text, index + length);
142                 index = match.capturedStart(1);
143         }
144 #endif
145 }
146
147
148 /////////////////////////////////////////////////////////////////////
149 //
150 // GuiLog
151 //
152 /////////////////////////////////////////////////////////////////////
153
154 GuiLog::GuiLog(GuiView & lv)
155         : GuiDialog(lv, "log", qt_("LaTeX Log")), type_(LatexLog)
156 {
157         setupUi(this);
158
159         connect(buttonBox, SIGNAL(clicked(QAbstractButton *)),
160                 this, SLOT(slotButtonBox(QAbstractButton *)));
161         connect(updatePB, SIGNAL(clicked()), this, SLOT(updateContents()));
162         connect(findPB, SIGNAL(clicked()), this, SLOT(find()));
163         connect(findLE, SIGNAL(returnPressed()), this, SLOT(find()));
164         connect(logTypeCO, SIGNAL(activated(int)),
165                 this, SLOT(typeChanged(int)));
166
167         bc().setPolicy(ButtonPolicy::OkCancelPolicy);
168
169         // set syntax highlighting
170         highlighter = new LogHighlighter(logTB->document());
171
172         logTB->setReadOnly(true);
173         logTB->setFont(guiApp->typewriterSystemFont());
174
175         QPushButton * closePB = buttonBox->button(QDialogButtonBox::Close);
176         closePB->setAutoDefault(false);
177 }
178
179
180 void GuiLog::updateContents()
181 {
182         setTitle(toqstr(title()));
183
184         ostringstream ss;
185         getContents(ss);
186
187         logTB->setPlainText(toqstr(ss.str()));
188
189         nextErrorPB->setEnabled(contains(exprError));
190         nextWarningPB->setEnabled(contains(exprWarning));
191 }
192
193
194 void GuiLog::typeChanged(int i)
195 {
196         string const type =
197                 fromqstr(logTypeCO->itemData(i).toString());
198         string ext;
199         if (type == "latex")
200                 ext = "log";
201         else if (type == "bibtex")
202                 ext = "blg";
203         else if (type == "index")
204                 ext = "ilg";
205
206         if (!ext.empty())
207                 logfile_.changeExtension(ext);
208
209         updateContents();
210 }
211
212
213 void GuiLog::find()
214 {
215         logTB->find(findLE->text());
216 }
217
218
219 void GuiLog::on_nextErrorPB_clicked()
220 {
221         goTo(exprError);
222 }
223
224
225 void GuiLog::on_nextWarningPB_clicked()
226 {
227         goTo(exprWarning);
228 }
229
230
231 void GuiLog::on_openDirPB_clicked()
232 {       
233         showDirectory(logfile_.onlyPath());
234 }
235
236
237 #if QT_VERSION < 0x060000
238 void GuiLog::goTo(QRegExp const & exp) const
239 #else
240 void GuiLog::goTo(QRegularExpression const & exp) const
241 #endif
242 {
243         QTextCursor const newc =
244                 logTB->document()->find(exp, logTB->textCursor());
245         logTB->setTextCursor(newc);
246 }
247
248
249 #if QT_VERSION < 0x060000
250 bool GuiLog::contains(QRegExp const & exp) const
251 #else
252 bool GuiLog::contains(QRegularExpression const & exp) const
253 #endif
254 {
255         return !logTB->document()->find(exp, logTB->textCursor()).isNull();
256 }
257
258
259 bool GuiLog::initialiseParams(string const & sdata)
260 {
261         istringstream is(sdata);
262         Lexer lex;
263         lex.setStream(is);
264
265         string logtype, logfile;
266         lex >> logtype;
267         if (lex) {
268                 lex.next(true);
269                 logfile = lex.getString();
270         }
271         if (!lex)
272                 // Parsing of the data failed.
273                 return false;
274
275         logTypeCO->setEnabled(logtype == "latex");
276         logTypeCO->clear();
277
278         FileName log(logfile);
279
280         if (logtype == "latex") {
281                 type_ = LatexLog;
282                 logTypeCO->addItem(qt_("LaTeX"), toqstr(logtype));
283                 FileName tmp = log;
284                 tmp.changeExtension("blg");
285                 if (tmp.exists()) {
286                         if (support::contains(tmp.fileContents("UTF-8"), from_ascii("This is Biber")))
287                                 logTypeCO->addItem(qt_("Biber"), QString("bibtex"));
288                         else
289                                 logTypeCO->addItem(qt_("BibTeX"), QString("bibtex"));
290                 }
291                 tmp.changeExtension("ilg");
292                 if (tmp.exists())
293                         logTypeCO->addItem(qt_("Index"), QString("index"));
294         // FIXME: not sure "literate" still works.
295         } else if (logtype == "literate") {
296                 type_ = LiterateLog;
297                 logTypeCO->addItem(qt_("Literate"), toqstr(logtype));
298         } else if (logtype == "lyx2lyx") {
299                 type_ = Lyx2lyxLog;
300                 logTypeCO->addItem(qt_("LyX2LyX"), toqstr(logtype));
301         } else if (logtype == "vc") {
302                 type_ = VCLog;
303                 logTypeCO->addItem(qt_("Version Control"), toqstr(logtype));
304         } else
305                 return false;
306
307         logfile_ = log;
308
309         updateContents();
310
311         return true;
312 }
313
314
315 void GuiLog::clearParams()
316 {
317         logfile_.erase();
318 }
319
320
321 docstring GuiLog::title() const
322 {
323         switch (type_) {
324         case LatexLog:
325                 return _("LaTeX Log");
326         case LiterateLog:
327                 return _("Literate Programming Build Log");
328         case Lyx2lyxLog:
329                 return _("lyx2lyx Error Log");
330         case VCLog:
331                 return _("Version Control Log");
332         default:
333                 return docstring();
334         }
335 }
336
337
338 void GuiLog::getContents(ostream & ss) const
339 {
340         ifstream in(logfile_.toFilesystemEncoding().c_str());
341
342         bool success = false;
343
344         // FIXME UNICODE
345         // Our caller interprets the file contents as UTF8, but is that
346         // correct?
347         // spitz: No it isn't (generally). The log file encoding depends on the TeX
348         // _output_ encoding (T1 etc.). We should account for that. See #10728.
349         if (in) {
350                 ss << in.rdbuf();
351                 success = ss.good();
352         }
353
354         if (success)
355                 return;
356
357         switch (type_) {
358         case LatexLog:
359                 ss << to_utf8(_("Log file not found."));
360                 break;
361         case LiterateLog:
362                 ss << to_utf8(_("No literate programming build log file found."));
363                 break;
364         case Lyx2lyxLog:
365                 ss << to_utf8(_("No lyx2lyx error log file found."));
366                 break;
367         case VCLog:
368                 ss << to_utf8(_("No version control log file found."));
369                 break;
370         }
371 }
372
373
374 } // namespace frontend
375 } // namespace lyx
376
377 #include "moc_GuiLog.cpp"