[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 
37 KLFLatexPreviewHandler::KLFLatexPreviewHandler(QObject * parent)
38  : QObject(parent)
39 {
40 }
41 KLFLatexPreviewHandler::~KLFLatexPreviewHandler()
42 {
43 }
44 
46 {
47 }
49 {
50  Q_UNUSED(output);
51 }
52 void 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 }
69 void KLFLatexPreviewHandler::latexPreviewError(const QString& errorString, int errorCode)
70 {
71  Q_UNUSED(errorString); Q_UNUSED(errorCode);
72 }
73 
74 
75 
76 // ---
77 
78 
79 KLFLatexPreviewThread::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 
130 KLFLatexPreviewThread::~KLFLatexPreviewThread()
131 {
132  stop();
133 
134  if (d->worker) {
135  delete d->worker;
136  }
137 
139 }
140 
141 
142 QSize KLFLatexPreviewThread::previewSize() const
143 { return d->previewSize; }
144 QSize KLFLatexPreviewThread::largePreviewSize() const
145 { return d->largePreviewSize; }
146 
147 void KLFLatexPreviewThread::setPreviewSize(const QSize& previewSize)
148 { d->previewSize = previewSize; }
149 void KLFLatexPreviewThread::setLargePreviewSize(const QSize& largePreviewSize)
150 { d->largePreviewSize = largePreviewSize; }
151 
152 
153 void KLFLatexPreviewThread::start(Priority priority)
154 {
155  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
156 
157  // fire up the thread
159 }
160 
161 void KLFLatexPreviewThread::stop()
162 {
163  // tell thread to stop, and wait for it
164  emit d->internalRequestAbort();
165  quit();
166  wait();
167 }
168 
169 
170 void KLFLatexPreviewThread::run()
171 {
172  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
173 
174  // fire up and enter the main loop.
175  QThread::run();
176 }
177 
178 KLFLatexPreviewThread::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 
195 KLFLatexPreviewThread::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 
210 KLFLatexPreviewThread::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 
227 KLFLatexPreviewThread::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 
242 KLFLatexPreviewThread::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 
260 KLFLatexPreviewThread::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 
278 void KLFLatexPreviewThread::cancelTask(TaskId task)
279 {
280  emit d->internalRequestCancelTask(task);
281 }
282 void KLFLatexPreviewThread::clearPendingTasks()
283 {
284  emit d->internalRequestClearPendingTasks();
285 }
286 
287 
288 
289 
290 // -----
291 
292 
293 
294 void 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 
314 bool 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 
330 void KLFLatexPreviewThreadWorker::threadClearPendingTasks()
331 {
332  newTasks.clear();
333 }
334 
335 void 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 
422 KLFContLatexPreview::KLFContLatexPreview(KLFLatexPreviewThread *thread)
423  : QObject(thread)
424 {
425  KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
426 
428 
429  setThread(thread);
430 }
431 
432 KLFContLatexPreview::~KLFContLatexPreview()
433 {
435 }
436 
437 KLF_DEFINE_PROPERTY_GET(KLFContLatexPreview, QSize, previewSize) ;
438 
439 KLF_DEFINE_PROPERTY_GET(KLFContLatexPreview, QSize, largePreviewSize) ;
440 
441 
442 
443 bool KLFContLatexPreview::enabled() const
444 {
445  return d->enabled;
446 }
447 
448 void KLFContLatexPreview::setEnabled(bool enabled)
449 {
450  d->enabled = enabled;
451 }
452 
453 
454 void KLFContLatexPreview::setThread(KLFLatexPreviewThread * thread)
455 {
456  d->thread = thread;
457 }
458 
460 {
462 
463  if (d->input == input)
464  return false;
465 
466  d->input = input;
467  d->refreshPreview();
468  return true;
469 }
470 bool 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 }
495 bool KLFContLatexPreview::setLargePreviewSize(const QSize& largePreviewSize)
496 {
497  if (d->largePreviewSize == largePreviewSize)
498  return false;
499  d->largePreviewSize = largePreviewSize;
500  d->refreshPreview();
501  return true;
502 }
503 
504 
505 
KLFBackend::klfSettings::wantSVG
bool wantSVG
Definition: klfbackend.h:285
KLFLatexPreviewHandler::latexOutputAvailable
virtual void latexOutputAvailable(const KLFBackend::klfOutput &output)
Definition: klflatexpreviewthread.cpp:48
QImage::height
int height() const
KLFBackend::klfSettings::wantRaw
bool wantRaw
Definition: klfbackend.h:277
QThread
QMetaType::type
int type(const char *typeName)
KLFContLatexPreview::setInput
bool setInput(const KLFBackend::klfInput &input)
Definition: klflatexpreviewthread.cpp:459
QThread::wait
bool wait(unsigned long time)
KLFBackend::klfSettings::wantPDF
bool wantPDF
Definition: klfbackend.h:281
QImage::scaled
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
QSize
KLFLatexPreviewHandler
Definition: klflatexpreviewthread.h:38
KLFBackend::klfInput
Specific input to KLFBackend::getLatexFormula()
Definition: klfbackend.h:307
KLF_FUNC_NAME
#define KLF_FUNC_NAME
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::thread
QThread * thread() const
QThread::quit
void quit()
KLFBackend::klfOutput::status
int status
A code describing the status of the request.
Definition: klfbackend.h:381
KLFLatexPreviewThread
Definition: klflatexpreviewthread.h:74
klfDbg
#define klfDbg(streamableItems)
KLFBackend::getLatexFormula
static klfOutput getLatexFormula(const klfInput &in, const klfSettings &settings, bool isMainThread=true)
The function that processes everything.
Definition: klfbackend.cpp:488
QObject
QThread::run
virtual void run()
KLF_DEBUG_BLOCK
#define KLF_DEBUG_BLOCK(msg)
QString
QThread::start
void start(Priority priority)
KLF_DELETE_PRIVATE
#define KLF_DELETE_PRIVATE
KLFLatexPreviewHandler::latexPreviewError
virtual void latexPreviewError(const QString &errorString, int errorCode)
Definition: klflatexpreviewthread.cpp:69
KLFContLatexPreview::setPreviewSize
bool setPreviewSize(const QSize &previewSize)
Definition: klflatexpreviewthread.cpp:487
KLFLatexPreviewHandler::latexPreviewLargeImageAvailable
virtual void latexPreviewLargeImageAvailable(const QImage &largePreview)
Definition: klflatexpreviewthread.cpp:61
klfbackend.h
Definition of class KLFBackend.
KLFLatexPreviewHandler::latexPreviewImageAvailable
virtual void latexPreviewImageAvailable(const QImage &preview)
Definition: klflatexpreviewthread.cpp:57
QMetaObject::invokeMethod
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)
KLFLatexPreviewHandler::latexPreviewReset
virtual void latexPreviewReset()
Definition: klflatexpreviewthread.cpp:45
KLFBackend::klfOutput
KLFBackend::getLatexFormula() result.
Definition: klfbackend.h:371
KLFBackend::klfSettings
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:219
KLFContLatexPreview::setLargePreviewSize
bool setLargePreviewSize(const QSize &largePreviewSize)
Definition: klflatexpreviewthread.cpp:495
KLFBackend::klfOutput::result
QImage result
The actual resulting image.
Definition: klfbackend.h:393
KLF_INIT_PRIVATE
#define KLF_INIT_PRIVATE(ClassName)
KLFLatexPreviewHandler::latexPreviewAvailable
virtual void latexPreviewAvailable(const QImage &preview, const QImage &largePreview, const QImage &fullPreview)
Definition: klflatexpreviewthread.cpp:52
KLFBackend::klfOutput::errorstr
QString errorstr
An explicit error string.
Definition: klfbackend.h:390
KLFContLatexPreview
Definition: klflatexpreviewthread.h:153
QThread::priority
Priority priority() const
KLF_DEBUG_TIME_BLOCK
#define KLF_DEBUG_TIME_BLOCK(msg)
QImage
KLFLatexPreviewHandler::latexPreviewFullImageAvailable
virtual void latexPreviewFullImageAvailable(const QImage &fullPreview)
Definition: klflatexpreviewthread.cpp:65
KLFContLatexPreview::setSettings
bool setSettings(const KLFBackend::klfSettings &settings, bool disableExtraFormats=true)
Definition: klflatexpreviewthread.cpp:470
QImage::width
int width() const

Generated by doxygen 1.8.20