[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 
Specific input to KLFBackend::getLatexFormula()
Definition: klfbackend.h:306
bool setSettings(const KLFBackend::klfSettings &settings, bool disableExtraFormats=true)
#define klfDbg(streamableItems)
#define KLF_DEBUG_BLOCK(msg)
QThread * thread() const
static klfOutput getLatexFormula(const klfInput &in, const klfSettings &settings, bool isMainThread=true)
The function that processes everything.
Definition: klfbackend.cpp:488
int status
A code describing the status of the request.
Definition: klfbackend.h:381
int type(const char *typeName)
void start(Priority priority)
#define KLF_DELETE_PRIVATE
Priority priority() const
virtual void latexPreviewError(const QString &errorString, int errorCode)
int width() const
virtual void latexPreviewImageAvailable(const QImage &preview)
#define KLF_DEBUG_TIME_BLOCK(msg)
virtual void run()
#define KLF_FUNC_NAME
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)
Definition of class KLFBackend.
bool wait(unsigned long time)
QImage result
The actual resulting image.
Definition: klfbackend.h:393
virtual void latexPreviewFullImageAvailable(const QImage &fullPreview)
QString errorstr
An explicit error string.
Definition: klfbackend.h:390
KLFBackend::getLatexFormula() result.
Definition: klfbackend.h:370
virtual void latexPreviewLargeImageAvailable(const QImage &largePreview)
int height() const
bool setPreviewSize(const QSize &previewSize)
virtual void latexOutputAvailable(const KLFBackend::klfOutput &output)
bool setLargePreviewSize(const QSize &largePreviewSize)
#define KLF_INIT_PRIVATE(ClassName)
bool setInput(const KLFBackend::klfInput &input)
void quit()
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
virtual void latexPreviewAvailable(const QImage &preview, const QImage &largePreview, const QImage &fullPreview)
QImage scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:218

Generated by doxygen 1.8.14