[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
src/klfbackend/klflatexpreviewthread.cpp
00001 /***************************************************************************
00002  *   file klflatexpreviewthread.cpp
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2011 by Philippe Faist
00005  *   philippe.faist at bluewin.ch
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 /* $Id$ */
00023 
00024 #include <QImage>
00025 #include <QThread>
00026 #include <QMutex>
00027 #include <QWaitCondition>
00028 #include <QQueue>
00029 
00030 #include <klfbackend.h>
00031 
00032 #include "klflatexpreviewthread.h"
00033 #include "klflatexpreviewthread_p.h"
00034 
00035 
00036 
00037 KLFLatexPreviewHandler::KLFLatexPreviewHandler(QObject * parent)
00038   : QObject(parent)
00039 {
00040 }
00041 KLFLatexPreviewHandler::~KLFLatexPreviewHandler()
00042 {
00043 }
00044 
00045 void KLFLatexPreviewHandler::latexPreviewReset()
00046 {
00047 }
00048 void KLFLatexPreviewHandler::latexOutputAvailable(const KLFBackend::klfOutput& output)
00049 {
00050   Q_UNUSED(output);
00051 }
00052 void KLFLatexPreviewHandler::latexPreviewAvailable(const QImage& preview, const QImage& largePreview,
00053                                                    const QImage& fullPreview)
00054 {
00055   Q_UNUSED(preview); Q_UNUSED(largePreview); Q_UNUSED(fullPreview);
00056 }
00057 void KLFLatexPreviewHandler::latexPreviewImageAvailable(const QImage& preview)
00058 {
00059   Q_UNUSED(preview);
00060 }
00061 void KLFLatexPreviewHandler::latexPreviewLargeImageAvailable(const QImage& largePreview)
00062 {
00063   Q_UNUSED(largePreview);
00064 }
00065 void KLFLatexPreviewHandler::latexPreviewFullImageAvailable(const QImage& fullPreview)
00066 {
00067   Q_UNUSED(fullPreview);
00068 }
00069 void KLFLatexPreviewHandler::latexPreviewError(const QString& errorString, int errorCode)
00070 {
00071   Q_UNUSED(errorString); Q_UNUSED(errorCode);
00072 }
00073 
00074 
00075 
00076 // ---
00077 
00078 
00079 KLFLatexPreviewThread::KLFLatexPreviewThread(QObject * parent)
00080   : QThread(parent)
00081 {
00082   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00083 
00084   KLF_INIT_PRIVATE( KLFLatexPreviewThread ) ;
00085 
00086   // we need to register meta-type KLFBackend::klfOutput/klfInput/klfSettings for our
00087   // signal/meta-object call, so register it if not yet done
00088   if (QMetaType::type("KLFBackend::klfOutput") == 0) {
00089     qRegisterMetaType<KLFBackend::klfOutput>("KLFBackend::klfOutput") ;
00090   }
00091   if (QMetaType::type("KLFBackend::klfInput") == 0) {
00092     qRegisterMetaType<KLFBackend::klfInput>("KLFBackend::klfInput") ;
00093   }
00094   if (QMetaType::type("KLFBackend::klfSettings") == 0) {
00095     qRegisterMetaType<KLFBackend::klfSettings>("KLFBackend::klfSettings") ;
00096   }
00097   if (QMetaType::type("KLFLatexPreviewThreadWorker::Task") == 0) {
00098     qRegisterMetaType<KLFLatexPreviewThreadWorker::Task>("KLFLatexPreviewThreadWorker::Task") ;
00099   }
00100   if (QMetaType::type("KLFLatexPreviewThread::TaskId") == 0) {
00101     qRegisterMetaType<KLFLatexPreviewThread::TaskId>("KLFLatexPreviewThread::TaskId") ;
00102   }
00103 
00104 
00105   //
00106   // create a worker that will do all the job for us
00107   //
00108 
00109   d->worker = new KLFLatexPreviewThreadWorker;
00110   d->worker->moveToThread(this);
00111 
00112   // create a direct-connection abort signal; this is fine because worker.abort() is thread-safe.
00113   connect(d, SIGNAL(internalRequestAbort()), d->worker, SLOT(abort()), Qt::DirectConnection);
00114   
00115   // connect the signal that submits a new job.
00116   connect(d, SIGNAL(internalRequestSubmitNewTask(KLFLatexPreviewThreadWorker::Task, bool,
00117                                                  KLFLatexPreviewThread::TaskId)),
00118           d->worker, SLOT(threadSubmitTask(KLFLatexPreviewThreadWorker::Task, bool,
00119                                            KLFLatexPreviewThread::TaskId)),
00120           Qt::QueuedConnection);
00121   // signal to clear all pending jobs
00122   connect(d, SIGNAL(internalRequestClearPendingTasks()), d->worker, SLOT(threadClearPendingTasks()),
00123           Qt::QueuedConnection);
00124   // signal to cancel a specific task
00125   connect(d, SIGNAL(internalRequestCancelTask(KLFLatexPreviewThread::TaskId)),
00126           d->worker, SLOT(threadCancelTask(KLFLatexPreviewThread::TaskId)),
00127           Qt::QueuedConnection);
00128 }
00129 
00130 KLFLatexPreviewThread::~KLFLatexPreviewThread()
00131 {
00132   stop();
00133 
00134   if (d->worker) {
00135     delete d->worker;
00136   }
00137 
00138   KLF_DELETE_PRIVATE ;
00139 }
00140 
00141 
00142 QSize KLFLatexPreviewThread::previewSize() const
00143 { return d->previewSize; }
00144 QSize KLFLatexPreviewThread::largePreviewSize() const
00145 { return d->largePreviewSize; }
00146 
00147 void KLFLatexPreviewThread::setPreviewSize(const QSize& previewSize)
00148 { d->previewSize = previewSize; }
00149 void KLFLatexPreviewThread::setLargePreviewSize(const QSize& largePreviewSize)
00150 { d->largePreviewSize = largePreviewSize; }
00151 
00152 
00153 void KLFLatexPreviewThread::start(Priority priority)
00154 {
00155   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00156 
00157   // fire up the thread
00158   QThread::start(priority);
00159 }
00160 
00161 void KLFLatexPreviewThread::stop()
00162 {
00163   // tell thread to stop, and wait for it
00164   emit d->internalRequestAbort();
00165   quit();
00166   wait();
00167 }
00168 
00169 
00170 void KLFLatexPreviewThread::run()
00171 {
00172   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00173 
00174   // fire up and enter the main loop.
00175   QThread::run();
00176 }
00177 
00178 KLFLatexPreviewThread::TaskId
00179 /* */  KLFLatexPreviewThread::submitPreviewTask(const KLFBackend::klfInput& input,
00180                                                 const KLFBackend::klfSettings& settings,
00181                                                 KLFLatexPreviewHandler * outputhandler,
00182                                                 const QSize& previewSize,
00183                                                 const QSize& largePreviewSize)
00184 {
00185   KLFLatexPreviewThreadWorker::Task t;
00186   t.input = input;
00187   t.settings = settings;
00188   t.handler = outputhandler;
00189   t.previewSize = previewSize;
00190   t.largePreviewSize = largePreviewSize;
00191 
00192   return d->submitTask(t, false, -1);
00193 }
00194 
00195 KLFLatexPreviewThread::TaskId
00196 /* */  KLFLatexPreviewThread::submitPreviewTask(const KLFBackend::klfInput& input,
00197                                                 const KLFBackend::klfSettings& settings,
00198                                                 KLFLatexPreviewHandler * outputhandler)
00199 {
00200   KLFLatexPreviewThreadWorker::Task t;
00201   t.input = input;
00202   t.settings = settings;
00203   t.handler = outputhandler;
00204   t.previewSize = d->previewSize;
00205   t.largePreviewSize = d->largePreviewSize;
00206 
00207   return d->submitTask(t, false, -1);
00208 }
00209 
00210 KLFLatexPreviewThread::TaskId
00211 /* */  KLFLatexPreviewThread::clearAndSubmitPreviewTask(const KLFBackend::klfInput& input,
00212                                                         const KLFBackend::klfSettings& settings,
00213                                                         KLFLatexPreviewHandler * outputhandler,
00214                                                         const QSize& previewSize,
00215                                                         const QSize& largePreviewSize)
00216 {
00217   KLFLatexPreviewThreadWorker::Task t;
00218   t.input = input;
00219   t.settings = settings;
00220   t.handler = outputhandler;
00221   t.previewSize = previewSize;
00222   t.largePreviewSize = largePreviewSize;
00223 
00224   return d->submitTask(t, true, -1);
00225 }
00226 
00227 KLFLatexPreviewThread::TaskId
00228 /* */  KLFLatexPreviewThread::clearAndSubmitPreviewTask(const KLFBackend::klfInput& input,
00229                                                         const KLFBackend::klfSettings& settings,
00230                                                         KLFLatexPreviewHandler * outputhandler)
00231 {
00232   KLFLatexPreviewThreadWorker::Task t;
00233   t.input = input;
00234   t.settings = settings;
00235   t.handler = outputhandler;
00236   t.previewSize = d->previewSize;
00237   t.largePreviewSize = d->largePreviewSize;
00238 
00239   return d->submitTask(t, true, -1);
00240 }
00241 
00242 KLFLatexPreviewThread::TaskId
00243 /* */  KLFLatexPreviewThread::replaceSubmitPreviewTask(KLFLatexPreviewThread::TaskId replaceId,
00244                                                        const KLFBackend::klfInput& input,
00245                                                        const KLFBackend::klfSettings& settings,
00246                                                        KLFLatexPreviewHandler * outputhandler,
00247                                                        const QSize& previewSize,
00248                                                        const QSize& largePreviewSize)
00249 {
00250   KLFLatexPreviewThreadWorker::Task t;
00251   t.input = input;
00252   t.settings = settings;
00253   t.handler = outputhandler;
00254   t.previewSize = previewSize;
00255   t.largePreviewSize = largePreviewSize;
00256 
00257   return d->submitTask(t, false, replaceId);
00258 }
00259 
00260 KLFLatexPreviewThread::TaskId
00261 /* */  KLFLatexPreviewThread::replaceSubmitPreviewTask(KLFLatexPreviewThread::TaskId replaceId,
00262                                                        const KLFBackend::klfInput& input,
00263                                                        const KLFBackend::klfSettings& settings,
00264                                                        KLFLatexPreviewHandler * outputhandler)
00265 {
00266   KLFLatexPreviewThreadWorker::Task t;
00267   t.input = input;
00268   t.settings = settings;
00269   t.handler = outputhandler;
00270   t.previewSize = d->previewSize;
00271   t.largePreviewSize = d->largePreviewSize;
00272 
00273   return d->submitTask(t, false, replaceId);
00274 }
00275 
00276 
00277 
00278 void KLFLatexPreviewThread::cancelTask(TaskId task)
00279 {
00280   emit d->internalRequestCancelTask(task);
00281 }
00282 void KLFLatexPreviewThread::clearPendingTasks()
00283 {
00284   emit d->internalRequestClearPendingTasks();
00285 }
00286 
00287 
00288 
00289 
00290 // -----
00291 
00292 
00293 
00294 void KLFLatexPreviewThreadWorker::threadSubmitTask(Task task, bool clearOtherJobs, TaskId replaceTaskId)
00295 {
00296   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00297 
00298   if (clearOtherJobs) {
00299     threadClearPendingTasks();
00300   }
00301   if (replaceTaskId) {
00302     threadCancelTask(replaceTaskId);
00303   }
00304 
00305   // enqueue the new task
00306   newTasks.enqueue(task);
00307 
00308   klfDbg("enqueued task id="<<task.taskid) ;
00309 
00310   // and notify ourself in the event loop that there are more jobs to process
00311   QMetaObject::invokeMethod(this, "threadProcessJobs", Qt::QueuedConnection);
00312 }
00313 
00314 bool KLFLatexPreviewThreadWorker::threadCancelTask(TaskId taskid)
00315 {
00316   int k;
00317   for (k = 0; k < newTasks.size(); ++k) {
00318     if (newTasks.at(k).taskid == taskid) {
00319       newTasks.removeAt(k);
00320       return true;
00321     }
00322   }
00323 
00324   // this might not be an error, it could be that the task completed before we had
00325   // a chance to cancel it
00326   klfDbg("No such task ID: "<<taskid) ;
00327   return false;
00328 }
00329 
00330 void KLFLatexPreviewThreadWorker::threadClearPendingTasks()
00331 {
00332   newTasks.clear();
00333 }
00334 
00335 void KLFLatexPreviewThreadWorker::threadProcessJobs()
00336 {
00337   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00338 
00339   Task task;
00340   KLFBackend::klfOutput ouroutput;
00341 
00342   if (!newTasks.size()) {
00343     return;
00344   }
00345 
00346   if (_abort) {
00347     return;
00348   }
00349 
00350   // fetch task info
00351   task = newTasks.dequeue();
00352 
00353   klfDbg("processing job ID="<<task.taskid) ;
00354 
00355   QImage img, prev, lprev;
00356   if ( task.input.latex.trimmed().isEmpty() ) {
00357     QMetaObject::invokeMethod(task.handler, "latexPreviewReset", Qt::QueuedConnection);
00358   } else {
00359     // and GO!
00360     klfDbg("worker: running KLFBackend::getLatexFormula()") ;
00361     ouroutput = KLFBackend::getLatexFormula(task.input, task.settings, false);
00362     img = ouroutput.result;
00363 
00364     klfDbg("got result: status="<<ouroutput.status) ;
00365 
00366     if (ouroutput.status != 0) {
00367       // error...
00368       QMetaObject::invokeMethod(task.handler, "latexPreviewError", Qt::QueuedConnection,
00369                                 Q_ARG(QString, ouroutput.errorstr),
00370                                 Q_ARG(int, ouroutput.status));
00371     } else {
00372       // this method must be called first (by API design)
00373       QMetaObject::invokeMethod(task.handler, "latexOutputAvailable", Qt::QueuedConnection,
00374                                 Q_ARG(KLFBackend::klfOutput, ouroutput));
00375       if (task.previewSize.isValid()) {
00376         prev = img;
00377         if (prev.width() > task.previewSize.width() || prev.height() > task.previewSize.height()) {
00378           prev = img.scaled(task.previewSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
00379         }
00380       }
00381       if (task.largePreviewSize.isValid()) {
00382         lprev = img;
00383         if (lprev.width() > task.largePreviewSize.width() || lprev.height() > task.largePreviewSize.height()) {
00384           lprev = img.scaled(task.largePreviewSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
00385         }
00386       }
00387       
00388       QMetaObject::invokeMethod(task.handler, "latexPreviewAvailable", Qt::QueuedConnection,
00389                                 Q_ARG(QImage, prev),
00390                                 Q_ARG(QImage, lprev),
00391                                 Q_ARG(QImage, img));
00392       if (task.previewSize.isValid()) {
00393         QMetaObject::invokeMethod(task.handler, "latexPreviewImageAvailable", Qt::QueuedConnection,
00394                                   Q_ARG(QImage, prev));
00395       }
00396       if (task.largePreviewSize.isValid()) {
00397         QMetaObject::invokeMethod(task.handler, "latexPreviewLargeImageAvailable", Qt::QueuedConnection,
00398                                   Q_ARG(QImage, lprev));
00399       }
00400       QMetaObject::invokeMethod(task.handler, "latexPreviewFullImageAvailable", Qt::QueuedConnection,
00401                                 Q_ARG(QImage, img));
00402     }
00403   }
00404 
00405   klfDbg("about to invoke delayed threadProcessJobs.") ;
00406 
00407   // continue processing jobs, but let the event loop have a chance to run a bit too.
00408   QMetaObject::invokeMethod(this, "threadProcessJobs", Qt::QueuedConnection);
00409 
00410   klfDbg("threadProcessJobs: end") ;
00411 }
00412 
00413 
00414 
00415 
00416 
00417 
00418 
00419 // ------------
00420 
00421 
00422 KLFContLatexPreview::KLFContLatexPreview(KLFLatexPreviewThread *thread)
00423   : QObject(thread)
00424 {
00425   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00426 
00427   KLF_INIT_PRIVATE( KLFContLatexPreview ) ;
00428 
00429   setThread(thread);
00430 }
00431 
00432 KLFContLatexPreview::~KLFContLatexPreview()
00433 {
00434   KLF_DELETE_PRIVATE ;
00435 }
00436 
00437 KLF_DEFINE_PROPERTY_GET(KLFContLatexPreview, QSize, previewSize) ;
00438 
00439 KLF_DEFINE_PROPERTY_GET(KLFContLatexPreview, QSize, largePreviewSize) ;
00440 
00441 
00442 
00443 bool KLFContLatexPreview::enabled() const
00444 {
00445   return d->enabled;
00446 }
00447 
00448 void KLFContLatexPreview::setEnabled(bool enabled)
00449 {
00450   d->enabled = enabled;
00451 }
00452 
00453 
00454 void KLFContLatexPreview::setThread(KLFLatexPreviewThread * thread)
00455 {
00456   d->thread = thread;
00457 }
00458 
00459 bool KLFContLatexPreview::setInput(const KLFBackend::klfInput& input)
00460 {
00461   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00462 
00463   if (d->input == input)
00464     return false;
00465 
00466   d->input = input;
00467   d->refreshPreview();
00468   return true;
00469 }
00470 bool KLFContLatexPreview::setSettings(const KLFBackend::klfSettings& settings, bool disableExtraFormats)
00471 {
00472   KLFBackend::klfSettings s = settings;
00473   if (disableExtraFormats) {
00474     s.wantRaw = false;
00475     s.wantPDF = false;
00476     s.wantSVG = false;
00477   }
00478 
00479   if (d->settings == s)
00480     return false;
00481 
00482   d->settings = s;
00483   d->refreshPreview();
00484   return true;
00485 }
00486 
00487 bool KLFContLatexPreview::setPreviewSize(const QSize& previewSize)
00488 {
00489   if (d->previewSize == previewSize)
00490     return false;
00491   d->previewSize = previewSize;
00492   d->refreshPreview();
00493   return true;
00494 }
00495 bool KLFContLatexPreview::setLargePreviewSize(const QSize& largePreviewSize)
00496 {
00497   if (d->largePreviewSize == largePreviewSize)
00498     return false;
00499   d->largePreviewSize = largePreviewSize;
00500   d->refreshPreview();
00501   return true;
00502 }
00503 
00504 
00505 

Generated by doxygen 1.7.6.1. The KLatexFormula website is hosted on sourceforge.net