[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
klflatexpreviewthread.cpp
1/***************************************************************************
2 * file klflatexpreviewthread.cpp
3 * This file is part of the KLatexFormula Project.
4 * Copyright (C) 2011 by Philippe Faist
5 * philippe.faist at bluewin.ch
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21 ***************************************************************************/
22/* $Id$ */
23
24#include <QImage>
25#include <QThread>
26#include <QMutex>
27#include <QWaitCondition>
28#include <QQueue>
29
30#include <klfbackend.h>
31
32#include "klflatexpreviewthread.h"
33#include "klflatexpreviewthread_p.h"
34
35
36
37KLFLatexPreviewHandler::KLFLatexPreviewHandler(QObject * parent)
38 : QObject(parent)
39{
40}
41KLFLatexPreviewHandler::~KLFLatexPreviewHandler()
42{
43}
44
49{
50 Q_UNUSED(output);
51}
52void KLFLatexPreviewHandler::latexPreviewAvailable(const QImage& preview, const QImage& largePreview,
53 const QImage& fullPreview)
54{
55 Q_UNUSED(preview); Q_UNUSED(largePreview); Q_UNUSED(fullPreview);
56}
58{
59 Q_UNUSED(preview);
60}
62{
63 Q_UNUSED(largePreview);
64}
66{
67 Q_UNUSED(fullPreview);
68}
69void KLFLatexPreviewHandler::latexPreviewError(const QString& errorString, int errorCode)
70{
71 Q_UNUSED(errorString); Q_UNUSED(errorCode);
72}
73
74
75
76// ---
77
78
79KLFLatexPreviewThread::KLFLatexPreviewThread(QObject * parent)
80 : QThread(parent)
81{
82 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
83
85
86 // we need to register meta-type KLFBackend::klfOutput/klfInput/klfSettings for our
87 // signal/meta-object call, so register it if not yet done
88 if (QMetaType::type("KLFBackend::klfOutput") == 0) {
89 qRegisterMetaType<KLFBackend::klfOutput>("KLFBackend::klfOutput") ;
90 }
91 if (QMetaType::type("KLFBackend::klfInput") == 0) {
92 qRegisterMetaType<KLFBackend::klfInput>("KLFBackend::klfInput") ;
93 }
94 if (QMetaType::type("KLFBackend::klfSettings") == 0) {
95 qRegisterMetaType<KLFBackend::klfSettings>("KLFBackend::klfSettings") ;
96 }
97 if (QMetaType::type("KLFLatexPreviewThreadWorker::Task") == 0) {
98 qRegisterMetaType<KLFLatexPreviewThreadWorker::Task>("KLFLatexPreviewThreadWorker::Task") ;
99 }
100 if (QMetaType::type("KLFLatexPreviewThread::TaskId") == 0) {
101 qRegisterMetaType<KLFLatexPreviewThread::TaskId>("KLFLatexPreviewThread::TaskId") ;
102 }
103
104
105 //
106 // create a worker that will do all the job for us
107 //
108
109 d->worker = new KLFLatexPreviewThreadWorker;
110 d->worker->moveToThread(this);
111
112 // create a direct-connection abort signal; this is fine because worker.abort() is thread-safe.
113 connect(d, SIGNAL(internalRequestAbort()), d->worker, SLOT(abort()), Qt::DirectConnection);
114
115 // connect the signal that submits a new job.
116 connect(d, SIGNAL(internalRequestSubmitNewTask(KLFLatexPreviewThreadWorker::Task, bool,
117 KLFLatexPreviewThread::TaskId)),
118 d->worker, SLOT(threadSubmitTask(KLFLatexPreviewThreadWorker::Task, bool,
119 KLFLatexPreviewThread::TaskId)),
120 Qt::QueuedConnection);
121 // signal to clear all pending jobs
122 connect(d, SIGNAL(internalRequestClearPendingTasks()), d->worker, SLOT(threadClearPendingTasks()),
123 Qt::QueuedConnection);
124 // signal to cancel a specific task
125 connect(d, SIGNAL(internalRequestCancelTask(KLFLatexPreviewThread::TaskId)),
126 d->worker, SLOT(threadCancelTask(KLFLatexPreviewThread::TaskId)),
127 Qt::QueuedConnection);
128}
129
130KLFLatexPreviewThread::~KLFLatexPreviewThread()
131{
132 stop();
133
134 if (d->worker) {
135 delete d->worker;
136 }
137
139}
140
141
142QSize KLFLatexPreviewThread::previewSize() const
143{ return d->previewSize; }
144QSize KLFLatexPreviewThread::largePreviewSize() const
145{ return d->largePreviewSize; }
146
147void KLFLatexPreviewThread::setPreviewSize(const QSize& previewSize)
148{ d->previewSize = previewSize; }
149void KLFLatexPreviewThread::setLargePreviewSize(const QSize& largePreviewSize)
150{ d->largePreviewSize = largePreviewSize; }
151
152
153void KLFLatexPreviewThread::start(Priority priority)
154{
155 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
156
157 // fire up the thread
159}
160
161void KLFLatexPreviewThread::stop()
162{
163 // tell thread to stop, and wait for it
164 emit d->internalRequestAbort();
165 quit();
166 wait();
167}
168
169
170void KLFLatexPreviewThread::run()
171{
172 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
173
174 // fire up and enter the main loop.
175 QThread::run();
176}
177
178KLFLatexPreviewThread::TaskId
179/* */ KLFLatexPreviewThread::submitPreviewTask(const KLFBackend::klfInput& input,
180 const KLFBackend::klfSettings& settings,
181 KLFLatexPreviewHandler * outputhandler,
182 const QSize& previewSize,
183 const QSize& largePreviewSize)
184{
185 KLFLatexPreviewThreadWorker::Task t;
186 t.input = input;
187 t.settings = settings;
188 t.handler = outputhandler;
189 t.previewSize = previewSize;
190 t.largePreviewSize = largePreviewSize;
191
192 return d->submitTask(t, false, -1);
193}
194
195KLFLatexPreviewThread::TaskId
196/* */ KLFLatexPreviewThread::submitPreviewTask(const KLFBackend::klfInput& input,
197 const KLFBackend::klfSettings& settings,
198 KLFLatexPreviewHandler * outputhandler)
199{
200 KLFLatexPreviewThreadWorker::Task t;
201 t.input = input;
202 t.settings = settings;
203 t.handler = outputhandler;
204 t.previewSize = d->previewSize;
205 t.largePreviewSize = d->largePreviewSize;
206
207 return d->submitTask(t, false, -1);
208}
209
210KLFLatexPreviewThread::TaskId
211/* */ KLFLatexPreviewThread::clearAndSubmitPreviewTask(const KLFBackend::klfInput& input,
212 const KLFBackend::klfSettings& settings,
213 KLFLatexPreviewHandler * outputhandler,
214 const QSize& previewSize,
215 const QSize& largePreviewSize)
216{
217 KLFLatexPreviewThreadWorker::Task t;
218 t.input = input;
219 t.settings = settings;
220 t.handler = outputhandler;
221 t.previewSize = previewSize;
222 t.largePreviewSize = largePreviewSize;
223
224 return d->submitTask(t, true, -1);
225}
226
227KLFLatexPreviewThread::TaskId
228/* */ KLFLatexPreviewThread::clearAndSubmitPreviewTask(const KLFBackend::klfInput& input,
229 const KLFBackend::klfSettings& settings,
230 KLFLatexPreviewHandler * outputhandler)
231{
232 KLFLatexPreviewThreadWorker::Task t;
233 t.input = input;
234 t.settings = settings;
235 t.handler = outputhandler;
236 t.previewSize = d->previewSize;
237 t.largePreviewSize = d->largePreviewSize;
238
239 return d->submitTask(t, true, -1);
240}
241
242KLFLatexPreviewThread::TaskId
243/* */ KLFLatexPreviewThread::replaceSubmitPreviewTask(KLFLatexPreviewThread::TaskId replaceId,
244 const KLFBackend::klfInput& input,
245 const KLFBackend::klfSettings& settings,
246 KLFLatexPreviewHandler * outputhandler,
247 const QSize& previewSize,
248 const QSize& largePreviewSize)
249{
250 KLFLatexPreviewThreadWorker::Task t;
251 t.input = input;
252 t.settings = settings;
253 t.handler = outputhandler;
254 t.previewSize = previewSize;
255 t.largePreviewSize = largePreviewSize;
256
257 return d->submitTask(t, false, replaceId);
258}
259
260KLFLatexPreviewThread::TaskId
261/* */ KLFLatexPreviewThread::replaceSubmitPreviewTask(KLFLatexPreviewThread::TaskId replaceId,
262 const KLFBackend::klfInput& input,
263 const KLFBackend::klfSettings& settings,
264 KLFLatexPreviewHandler * outputhandler)
265{
266 KLFLatexPreviewThreadWorker::Task t;
267 t.input = input;
268 t.settings = settings;
269 t.handler = outputhandler;
270 t.previewSize = d->previewSize;
271 t.largePreviewSize = d->largePreviewSize;
272
273 return d->submitTask(t, false, replaceId);
274}
275
276
277
278void KLFLatexPreviewThread::cancelTask(TaskId task)
279{
280 emit d->internalRequestCancelTask(task);
281}
282void KLFLatexPreviewThread::clearPendingTasks()
283{
284 emit d->internalRequestClearPendingTasks();
285}
286
287
288
289
290// -----
291
292
293
294void KLFLatexPreviewThreadWorker::threadSubmitTask(Task task, bool clearOtherJobs, TaskId replaceTaskId)
295{
296 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
297
298 if (clearOtherJobs) {
299 threadClearPendingTasks();
300 }
301 if (replaceTaskId) {
302 threadCancelTask(replaceTaskId);
303 }
304
305 // enqueue the new task
306 newTasks.enqueue(task);
307
308 klfDbg("enqueued task id="<<task.taskid) ;
309
310 // and notify ourself in the event loop that there are more jobs to process
311 QMetaObject::invokeMethod(this, "threadProcessJobs", Qt::QueuedConnection);
312}
313
314bool KLFLatexPreviewThreadWorker::threadCancelTask(TaskId taskid)
315{
316 int k;
317 for (k = 0; k < newTasks.size(); ++k) {
318 if (newTasks.at(k).taskid == taskid) {
319 newTasks.removeAt(k);
320 return true;
321 }
322 }
323
324 // this might not be an error, it could be that the task completed before we had
325 // a chance to cancel it
326 klfDbg("No such task ID: "<<taskid) ;
327 return false;
328}
329
330void KLFLatexPreviewThreadWorker::threadClearPendingTasks()
331{
332 newTasks.clear();
333}
334
335void KLFLatexPreviewThreadWorker::threadProcessJobs()
336{
337 KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
338
339 Task task;
340 KLFBackend::klfOutput ouroutput;
341
342 if (!newTasks.size()) {
343 return;
344 }
345
346 if (_abort) {
347 return;
348 }
349
350 // fetch task info
351 task = newTasks.dequeue();
352
353 klfDbg("processing job ID="<<task.taskid) ;
354
355 QImage img, prev, lprev;
356 if ( task.input.latex.trimmed().isEmpty() ) {
357 QMetaObject::invokeMethod(task.handler, "latexPreviewReset", Qt::QueuedConnection);
358 } else {
359 // and GO!
360 klfDbg("worker: running KLFBackend::getLatexFormula()") ;
361 ouroutput = KLFBackend::getLatexFormula(task.input, task.settings, false);
362 img = ouroutput.result;
363
364 klfDbg("got result: status="<<ouroutput.status) ;
365
366 if (ouroutput.status != 0) {
367 // error...
368 QMetaObject::invokeMethod(task.handler, "latexPreviewError", Qt::QueuedConnection,
369 Q_ARG(QString, ouroutput.errorstr),
370 Q_ARG(int, ouroutput.status));
371 } else {
372 // this method must be called first (by API design)
373 QMetaObject::invokeMethod(task.handler, "latexOutputAvailable", Qt::QueuedConnection,
374 Q_ARG(KLFBackend::klfOutput, ouroutput));
375 if (task.previewSize.isValid()) {
376 prev = img;
377 if (prev.width() > task.previewSize.width() || prev.height() > task.previewSize.height()) {
378 prev = img.scaled(task.previewSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
379 }
380 }
381 if (task.largePreviewSize.isValid()) {
382 lprev = img;
383 if (lprev.width() > task.largePreviewSize.width() || lprev.height() > task.largePreviewSize.height()) {
384 lprev = img.scaled(task.largePreviewSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
385 }
386 }
387
388 QMetaObject::invokeMethod(task.handler, "latexPreviewAvailable", Qt::QueuedConnection,
389 Q_ARG(QImage, prev),
390 Q_ARG(QImage, lprev),
391 Q_ARG(QImage, img));
392 if (task.previewSize.isValid()) {
393 QMetaObject::invokeMethod(task.handler, "latexPreviewImageAvailable", Qt::QueuedConnection,
394 Q_ARG(QImage, prev));
395 }
396 if (task.largePreviewSize.isValid()) {
397 QMetaObject::invokeMethod(task.handler, "latexPreviewLargeImageAvailable", Qt::QueuedConnection,
398 Q_ARG(QImage, lprev));
399 }
400 QMetaObject::invokeMethod(task.handler, "latexPreviewFullImageAvailable", Qt::QueuedConnection,
401 Q_ARG(QImage, img));
402 }
403 }
404
405 klfDbg("about to invoke delayed threadProcessJobs.") ;
406
407 // continue processing jobs, but let the event loop have a chance to run a bit too.
408 QMetaObject::invokeMethod(this, "threadProcessJobs", Qt::QueuedConnection);
409
410 klfDbg("threadProcessJobs: end") ;
411}
412
413
414
415
416
417
418
419// ------------
420
421
422KLFContLatexPreview::KLFContLatexPreview(KLFLatexPreviewThread *thread)
423 : QObject(thread)
424{
425 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
426
427 KLF_INIT_PRIVATE( KLFContLatexPreview ) ;
428
429 setThread(thread);
430}
431
432KLFContLatexPreview::~KLFContLatexPreview()
433{
435}
436
437KLF_DEFINE_PROPERTY_GET(KLFContLatexPreview, QSize, previewSize) ;
438
439KLF_DEFINE_PROPERTY_GET(KLFContLatexPreview, QSize, largePreviewSize) ;
440
441
442
443bool KLFContLatexPreview::enabled() const
444{
445 return d->enabled;
446}
447
448void KLFContLatexPreview::setEnabled(bool enabled)
449{
450 d->enabled = enabled;
451}
452
453
454void KLFContLatexPreview::setThread(KLFLatexPreviewThread * thread)
455{
456 d->thread = thread;
457}
458
460{
461 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
462
463 if (d->input == input)
464 return false;
465
466 d->input = input;
467 d->refreshPreview();
468 return true;
469}
470bool KLFContLatexPreview::setSettings(const KLFBackend::klfSettings& settings, bool disableExtraFormats)
471{
472 KLFBackend::klfSettings s = settings;
473 if (disableExtraFormats) {
474 s.wantRaw = false;
475 s.wantPDF = false;
476 s.wantSVG = false;
477 }
478
479 if (d->settings == s)
480 return false;
481
482 d->settings = s;
483 d->refreshPreview();
484 return true;
485}
486
488{
489 if (d->previewSize == previewSize)
490 return false;
491 d->previewSize = previewSize;
492 d->refreshPreview();
493 return true;
494}
496{
497 if (d->largePreviewSize == largePreviewSize)
498 return false;
499 d->largePreviewSize = largePreviewSize;
500 d->refreshPreview();
501 return true;
502}
503
504
505
static klfOutput getLatexFormula(const klfInput &in, const klfSettings &settings, bool isMainThread=true)
The function that processes everything.
bool setLargePreviewSize(const QSize &largePreviewSize)
bool setSettings(const KLFBackend::klfSettings &settings, bool disableExtraFormats=true)
bool setInput(const KLFBackend::klfInput &input)
bool setPreviewSize(const QSize &previewSize)
virtual void latexPreviewImageAvailable(const QImage &preview)
virtual void latexPreviewLargeImageAvailable(const QImage &largePreview)
virtual void latexPreviewAvailable(const QImage &preview, const QImage &largePreview, const QImage &fullPreview)
virtual void latexPreviewError(const QString &errorString, int errorCode)
virtual void latexPreviewFullImageAvailable(const QImage &fullPreview)
virtual void latexOutputAvailable(const KLFBackend::klfOutput &output)
Definition of class KLFBackend.
#define KLF_DEBUG_TIME_BLOCK(msg)
#define KLF_DEBUG_BLOCK(msg)
#define klfDbg(streamableItems)
#define KLF_DELETE_PRIVATE
#define KLF_INIT_PRIVATE(ClassName)
int height() const
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
int width() const
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
int type(const char *typeName)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QThread * thread() const
Priority priority() const
void quit()
virtual void run()
void start(Priority priority)
bool wait(unsigned long time)
Specific input to KLFBackend::getLatexFormula()
Definition klfbackend.h:307
KLFBackend::getLatexFormula() result.
Definition klfbackend.h:371
int status
A code describing the status of the request.
Definition klfbackend.h:381
QImage result
The actual resulting image.
Definition klfbackend.h:393
QString errorstr
An explicit error string.
Definition klfbackend.h:390
General settings for KLFBackend::getLatexFormula()
Definition klfbackend.h:219

Generated by doxygen 1.13.2