00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "pageitem_latexframe.h"
00025
00026 #include <QDebug>
00027 #include <QTemporaryFile>
00028 #include <QGridLayout>
00029 #include <QLabel>
00030 #include <QImage>
00031 #include <QPoint>
00032
00033 #include "prefsmanager.h"
00034 #include "scpainter.h"
00035 #include "scraction.h"
00036 #include "scribus.h"
00037 #include "scribusdoc.h"
00038 #include "undomanager.h"
00039 #include "undostate.h"
00040 #include "filewatcher.h"
00041 #include "latexeditor.h"
00042 #include "latexhelpers.h"
00043
00044
00045 PageItem_LatexFrame::PageItem_LatexFrame(ScribusDoc *pa, double x, double y, double w, double h, double w2, QString fill, QString outline)
00046 : PageItem_ImageFrame(pa, x, y, w, h, w2, fill, outline)
00047 {
00048 setUPixmap(Um::ILatexFrame);
00049 AnName = tr("Latex") + QString::number(m_Doc->TotalItems);
00050 setUName(AnName);
00051
00052 imgValid = false;
00053 m_usePreamble = true;
00054 err = 0;
00055 internalEditor = 0;
00056 killed = false;
00057
00058 config = 0;
00059 setConfigFile(PrefsManager::instance()->latexConfigs()[0]);
00060
00061
00062 latex = new QProcess();
00063 connect(latex, SIGNAL(finished(int, QProcess::ExitStatus)),
00064 this, SLOT(updateImage(int, QProcess::ExitStatus)));
00065 connect(latex, SIGNAL(error(QProcess::ProcessError)),
00066 this, SLOT(latexError(QProcess::ProcessError)));
00067 latex->setProcessChannelMode(QProcess::MergedChannels);
00068
00069
00070
00071 QTemporaryFile *tempfile = new QTemporaryFile(QDir::tempPath() + "/scribus_temp_latex_XXXXXX");
00072 tempfile->open();
00073 tempFileBase = tempfile->fileName();
00074 tempfile->close();
00075 delete tempfile;
00076 Q_ASSERT(!tempFileBase.isEmpty());
00077 editor = new QProcess();
00078 connect(editor, SIGNAL(finished(int, QProcess::ExitStatus)),
00079 this, SLOT(editorFinished(int, QProcess::ExitStatus)));
00080 connect(editor, SIGNAL(error(QProcess::ProcessError)),
00081 this, SLOT(editorError(QProcess::ProcessError)));
00082 editor->setProcessChannelMode(QProcess::MergedChannels);
00083
00084 fileWatcher = new FileWatcher(this);
00085 fileWatcher->stop();
00086 fileWatcher->setTimeOut(1500);
00087
00088 m_dpi = 0;
00089 pixm.imgInfo.lowResType = m_Doc->toolSettings.lowResType;;
00090 }
00091
00092 PageItem_LatexFrame::~PageItem_LatexFrame()
00093 {
00094 if (internalEditor) delete internalEditor;
00095
00096
00097
00098
00099 fileWatcher->disconnect();
00100 delete fileWatcher;
00101
00102 editor->disconnect();
00103
00104 delete editor;
00105
00106
00107 QDir dir;
00108 if (!editorFile.isEmpty() && !dir.remove(editorFile)) {
00109 qDebug() << "LATEX: Failed to remove editorfile" << qPrintable(editorFile);
00110 }
00111
00112
00113 latex->disconnect();
00114 if (latex->state() != QProcess::NotRunning) {
00115 qDebug() << "LATEX: Destructor: Killing running process from latexFrame";
00116 killed = true;
00117 latex->terminate();
00118 latex->waitForFinished(500);
00119 if (latex->state() != QProcess::NotRunning) {
00120 latex->kill();
00121 latex->waitForFinished(500);
00122 }
00123 }
00124 deleteImageFile();
00125 delete latex;
00126 }
00127
00128 void PageItem_LatexFrame::clearContents()
00129 {
00130 PageItem_ImageFrame::clearContents();
00131 formulaText = "";
00132 appStdout = "";
00133 err = 0;
00134 imgValid = false;
00135 }
00136
00137 void PageItem_LatexFrame::deleteImageFile()
00138 {
00139 QFileInfo fi(tempFileBase);
00140 QDir dir = fi.absoluteDir();
00141 QStringList filter;
00142
00143 filter << fi.fileName() + "*";
00144 Q_ASSERT(!fi.fileName().isEmpty());
00145 Q_ASSERT(!fi.fileName().contains("/"));
00146 Q_ASSERT(!fi.fileName().contains("\\"));
00147 QStringList files;
00148 files = dir.entryList(filter);
00149 foreach (QString file, files) {
00150 Q_ASSERT(file.startsWith("scribus_temp"));
00151
00152 dir.remove(file);
00153 }
00154 imgValid = false;
00155 }
00156
00157 void PageItem_LatexFrame::DrawObj_Item(ScPainter *p, QRectF e, double sc)
00158 {
00159 layout();
00160 if (!imgValid && !err) {
00161
00162 p->setPen(Qt::green, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
00163 p->drawLine(FPoint(0, 0), FPoint(Width, Height));
00164 } else if (err) {
00165
00166 p->setPen(Qt::blue, 1, Qt::SolidLine, Qt::FlatCap, Qt::MiterJoin);
00167 p->drawLine(FPoint(0, 0), FPoint(Width, Height));
00168 p->drawLine(FPoint(0, Height), FPoint(Width, 0));
00169 } else {
00170
00171 PageItem_ImageFrame::DrawObj_Item(p, e, sc);
00172 }
00173 }
00174
00175 void PageItem_LatexFrame::updateImage(int exitCode, QProcess::ExitStatus exitStatus)
00176 {
00177 appStdout = latex->readAllStandardOutput();
00178 err = exitCode;
00179
00180 emit latexFinished();
00181 emit stateChanged(latex->state());
00182
00183 static bool firstWarning = true;
00184 if (exitCode) {
00185 imgValid = false;
00186 if (firstWarning && !killed)
00187 {
00188 QMessageBox::critical(0, tr("Error"), "<qt>" +
00189 tr("Running the external application failed!")
00190 + "</qt>", 1, 0, 0);
00191 firstWarning = false;
00192 }
00193 qDebug() << "LATEX: updateImage():" << tr("Running the external application failed!");
00194 killed = false;
00195 update();
00196 return;
00197 }
00198 else
00199 {
00200 firstWarning = true;
00201 }
00202 imgValid = true;
00203
00204
00205 bool do_update = PicAvail;
00206 double scaleX = 72.0, scaleY = 72.0, offX = 0.0, offY = 0.0;
00207 if (do_update) {
00208 scaleX = LocalScX * pixm.imgInfo.xres;
00209 scaleY = LocalScY * pixm.imgInfo.yres;
00210 offX = LocalX / pixm.imgInfo.xres;
00211 offY = LocalY / pixm.imgInfo.yres;
00212 }
00213 PageItem_ImageFrame::loadImage(imageFile, true, realDpi());
00214 if (PrefsManager::instance()->latexForceDpi())
00215 {
00216 pixm.imgInfo.xres = pixm.imgInfo.yres = realDpi();
00217 }
00218
00219 if (do_update)
00220 {
00221
00222 LocalScX = scaleX / pixm.imgInfo.xres;
00223 LocalScY = scaleY / pixm.imgInfo.yres;
00224 LocalX = offX * pixm.imgInfo.xres;
00225 LocalY = offY * pixm.imgInfo.yres;
00226 }
00227 else
00228 {
00229
00230 LocalX = LocalY = 0;
00231 LocalScX = 72.0/pixm.imgInfo.xres;
00232 LocalScY = 72.0/pixm.imgInfo.yres;
00233 }
00234 emit imageOffsetScale(LocalScX, LocalScY, LocalX, LocalY );
00235
00236 update();
00237 }
00238
00239
00240 void PageItem_LatexFrame::runApplication()
00241 {
00242 imgValid = false;
00243 err = 0;
00244 killed = false;
00245
00246 static bool firstWarningTmpfile = true;
00247 static bool firstWarningLatexMissing = true;
00248
00249 QFile tempfile(tempFileBase);
00250 if (!tempfile.open(QIODevice::Truncate|QIODevice::WriteOnly)) {
00251 err = 0xffff;
00252 if (firstWarningTmpfile)
00253 {
00254 QMessageBox::critical(0, tr("Error"), "<qt>" +
00255 tr("Could not create a temporary file to run the application!")
00256 + "</qt>", 1, 0, 0);
00257 firstWarningTmpfile = false;
00258 }
00259 qDebug() << "LATEX:" << tr("Could not create a temporary file to run the application!");
00260
00261 return;
00262 }
00263 else
00264 {
00265 firstWarningTmpfile = true;
00266 }
00267
00268 QString full_command = config->executable().replace(
00269 "%dpi", QString::number(realDpi()));
00270
00271 if (full_command.isEmpty()) {
00272 if (firstWarningLatexMissing)
00273 {
00274 QMessageBox::information(0, tr("Information"),
00275 "<qt>" + tr("Please specify a latex executable in the preferences!") +
00276 "</qt>",1, 0, 0);
00277 firstWarningLatexMissing = false;
00278 }
00279 qDebug() << "LATEX:" << tr("Please specify a latex executable in the preferences!");
00280 return;
00281 }
00282 else
00283 {
00284 firstWarningLatexMissing = true;
00285 }
00286
00287 if (full_command.contains("%file")) {
00288 full_command.replace("%file", tempFileBase);
00289 } else {
00290 full_command += " " + tempFileBase;
00291 }
00292 if (full_command.contains("%dir")) {
00293 full_command.replace("%dir", QDir::tempPath());
00294 }
00295 latex->setWorkingDirectory(QDir::tempPath());
00296
00297 #ifdef _WIN32
00298 full_command.replace("\"", "\"\"\"");
00299 #endif
00300 qDebug() << "Full command" << full_command;
00301
00302
00303
00304
00305
00306
00307 imageFile = tempFileBase + config->imageExtension();
00308
00309 writeFileContents(&tempfile);
00310 tempfile.close();
00311
00312 latex->start(full_command);
00313 emit stateChanged(QProcess::Starting);
00314 }
00315
00316 void PageItem_LatexFrame::runEditor()
00317 {
00318 if (PrefsManager::instance()->latexUseEmbeddedEditor()) {
00319 if (!internalEditor) {
00320 internalEditor = new LatexEditor(this);
00321 }
00322 internalEditor->startEditor();
00323 return;
00324 }
00325
00326 if (editor->state() != QProcess::NotRunning) {
00327 QMessageBox::information(0, tr("Information"),
00328 "<qt>" + tr("An editor for this frame is already running!") +
00329 "</qt>", 1, 0, 0);
00330 return;
00331 }
00332
00333 QString full_command = PrefsManager::instance()->latexEditorExecutable();
00334 if (full_command.isEmpty()) {
00335 QMessageBox::information(0, tr("Information"),
00336 "<qt>" + tr("Please specify an editor in the preferences!") +
00337 "</qt>",1, 0, 0);
00338 return;
00339 }
00340
00341 writeEditorFile();
00342
00343 if (full_command.contains("%file")) {
00344 full_command.replace("%file", editorFile);
00345 } else {
00346 full_command += " " + editorFile;
00347 }
00348 full_command.replace("%dir", QDir::tempPath());
00349 editor->setWorkingDirectory(QDir::tempPath());
00350
00351 #ifdef _WIN32
00352 full_command.replace("\"", "\"\"\"");
00353 #endif
00354
00355 qDebug() << "LATEX: Starting editor" << full_command;
00356 editor->start(full_command);
00357 }
00358
00359 void PageItem_LatexFrame::rerunApplication(bool updateDisplay)
00360 {
00361 qDebug() << "LATEX: rerunApplication()";
00362 if (latex->state() != QProcess::NotRunning) {
00363 qDebug() << "LATEX: rerunApplication(): Killing running process from latexFrame";
00364 killed = true;
00365 latex->terminate();
00366 latex->waitForFinished(500);
00367 if (latex->state() != QProcess::NotRunning) {
00368
00369 latex->kill();
00370 latex->waitForFinished(500);
00371 }
00372 }
00373 runApplication();
00374 if (updateDisplay) this->update();
00375 }
00376
00377
00378 void PageItem_LatexFrame::writeFileContents(QFile *tempfile)
00379 {
00380 QString tmp(formulaText);
00381 double scaleX, scaleY, realW, realH, offsetX, offsetY;
00382 double lDpi = realDpi()/72.0;
00383 scaleX = LocalScX*lDpi;
00384 scaleY = LocalScY*lDpi;
00385 offsetX = LocalX*LocalScX;
00386 offsetY = LocalY*LocalScY;
00387 realW = Width/scaleX - LocalX/lDpi;
00388 realH = Height/scaleY - LocalY/lDpi;
00389 if (!tmp.contains("$scribus_noprepost$") && m_usePreamble) {
00390 tmp = config->preamble() + tmp + config->postamble();
00391 }
00392 tmp.replace(QString("$scribus_width$"), QString::number(Width));
00393 tmp.replace(QString("$scribus_width_px$"), QString::number(qRound(Width*lDpi)));
00394 tmp.replace(QString("$scribus_width_inch$"), QString::number(Width/72.0));
00395 tmp.replace(QString("$scribus_height$"), QString::number(Height));
00396 tmp.replace(QString("$scribus_height_px$"), QString::number(qRound(Height*lDpi)));
00397 tmp.replace(QString("$scribus_height_inch$"), QString::number(Height/72.0));
00398 tmp.replace(QString("$scribus_realwidth$"), QString::number(realW));
00399 tmp.replace(QString("$scribus_realwidth_px$"), QString::number(qRound(realW*lDpi)));
00400 tmp.replace(QString("$scribus_realheight$"), QString::number(realH));
00401 tmp.replace(QString("$scribus_realheight_px$"), QString::number(qRound(realH*lDpi)));
00402 tmp.replace(QString("$scribus_offsetX$"), QString::number(offsetX));
00403 tmp.replace(QString("$scribus_offsetX_px$"), QString::number(qRound(offsetX*lDpi)));
00404 tmp.replace(QString("$scribus_offsetY$"), QString::number(offsetY));
00405 tmp.replace(QString("$scribus_offsetY$"), QString::number(qRound(offsetY*lDpi)));
00406 tmp.replace(QString("$scribus_scaleX$"), QString::number(scaleX));
00407 tmp.replace(QString("$scribus_scaleY$"), QString::number(scaleY));
00408 tmp.replace(QString("$scribus_dpi$"), QString::number(realDpi()));
00409 tmp.replace(QString("$scribus_file$"), tempFileBase);
00410 tmp.replace(QString("$scribus_dir$"), QDir::tempPath());
00411 QMapIterator<QString, QString> i(editorProperties);
00412 while (i.hasNext()) {
00413 i.next();
00414 tmp.replace("$scribus_"+i.key()+"$", i.value());
00415 }
00416 tempfile->write(tmp.toUtf8());
00417 }
00418
00419 void PageItem_LatexFrame::writeEditorFile()
00420 {
00421 fileWatcher->stop();
00422 fileWatcher->disconnect();
00423
00424 if (editorFile.isEmpty()) {
00425 QTemporaryFile *editortempfile = new QTemporaryFile(QDir::tempPath() + "/scribus_temp_editor_XXXXXX");
00426 if (!editortempfile->open()) {
00427 QMessageBox::critical(0, tr("Error"), "<qt>" +
00428 tr("Could not create a temporary file to run the external editor!")
00429 + "</qt>", 1, 0, 0);
00430 }
00431 editorFile = editortempfile->fileName();
00432 editortempfile->setAutoRemove(false);
00433 editortempfile->close();
00434 delete editortempfile;
00435 fileWatcher->addFile(editorFile);
00436 }
00437 QFile f(editorFile);
00438 f.open(QIODevice::WriteOnly);
00439 f.write(formulaText.toUtf8());
00440 f.close();
00441 fileWatcher->forceScan();
00442 connect(fileWatcher, SIGNAL(fileChanged(QString)),
00443 this, SLOT(editorFileChanged(QString)));
00444 fileWatcher->start();
00445 }
00446
00447 bool PageItem_LatexFrame::setFormula(QString formula, bool undoable)
00448 {
00449 if (formula == formulaText) {
00450
00451 return false;
00452 }
00453 imgValid = false;
00454 err = 0;
00455 if (UndoManager::undoEnabled() && undoable)
00456 {
00457 SimpleState *ss = new SimpleState(Um::ChangeFormula, "", Um::IChangeFormula);
00458 ss->set("CHANGE_FORMULA", "change_formula");
00459 ss->set("OLD_FORMULA", formulaText);
00460 ss->set("NEW_FORMULA", formula);
00461 undoManager->action(this, ss);
00462 } else {
00463 emit formulaAutoUpdate(formulaText, formula);
00464 }
00465 formulaText = formula;
00466 return true;
00467 }
00468
00469
00470 void PageItem_LatexFrame::loadEditorFile()
00471 {
00472 QString new_formula;
00473 QFile f(editorFile);
00474 f.open(QIODevice::ReadOnly);
00475 new_formula = QString::fromUtf8(f.readAll());
00476 f.close();
00477 if (!new_formula.isEmpty()) {
00478 setFormula(new_formula);
00479 }
00480 this->update();
00481 }
00482
00483
00484 void PageItem_LatexFrame::editorFinished(int exitCode, QProcess::ExitStatus exitStatus)
00485 {
00486 Q_ASSERT(editor);
00487
00488 if (exitCode) {
00489 qDebug() << "LATEX: Editor's output was: " <<
00490 qPrintable(QString(editor->readAllStandardOutput()));
00491 QMessageBox::critical(0, tr("Error"), "<qt>" +
00492 tr("Running the editor failed with exitcode %d!").arg(exitCode) +
00493 "</qt>", 1, 0, 0);
00494 return;
00495 }
00496 }
00497
00498 void PageItem_LatexFrame::editorFileChanged(QString filename)
00499 {
00500 loadEditorFile();
00501 rerunApplication();
00502 }
00503
00504 void PageItem_LatexFrame::editorError(QProcess::ProcessError error)
00505 {
00506 QMessageBox::critical(0, tr("Error"), "<qt>" +
00507 tr("Running the editor \"%1\" failed!").
00508 arg(PrefsManager::instance()->latexEditorExecutable()) +
00509 "</qt>", 1, 0, 0);
00510 }
00511
00512 void PageItem_LatexFrame::latexError(QProcess::ProcessError error)
00513 {
00514 static bool firstWarning = true;
00515 if (killed) {
00516 killed = false;
00517 update();
00518
00519 return;
00520 }
00521 if (firstWarning)
00522 {
00523 if (latex->error() == QProcess::FailedToStart) {
00524 QMessageBox::critical(0, tr("Error"), "<qt>" +
00525 tr("The application \"%1\" failed to start!").
00526 arg(config->executable())
00527 + "</qt>", 1, 0, 0);
00528 } else {
00529 QMessageBox::critical(0, tr("Error"), "<qt>" +
00530 tr("The application \"%1\" crashed!").arg(config->executable())
00531 + "</qt>", 1, 0, 0);
00532 }
00533 firstWarning = false;
00534 }
00535 qDebug() << "LATEX: latexError():" <<
00536 tr("Running the application \"%1\" failed!").arg(config->executable()) << latex->error();
00537 }
00538
00539
00540 QString PageItem_LatexFrame::application() const
00541 {
00542 return config->executable();
00543 }
00544
00545 QString PageItem_LatexFrame::configFile() const
00546 {
00547 return configFilename;
00548 }
00549
00550 int PageItem_LatexFrame::realDpi() const
00551 {
00552 if (m_dpi) {
00553 return m_dpi;
00554 } else {
00555 return PrefsManager::instance()->latexResolution();
00556 }
00557 }
00558
00559 void PageItem_LatexFrame::setDpi(int newDpi)
00560 {
00561 m_dpi = newDpi;
00562 }
00563
00564 void PageItem_LatexFrame::setConfigFile(QString newConfig)
00565 {
00566 qDebug() << "CHANGING CONFIG FILE: " << configFilename << newConfig;
00567 if (configFilename == newConfig) return;
00568
00569 bool unchanged = false;
00570 if (formulaText.isEmpty() || formulaText == config->emptyFrameText()) {
00571 unchanged = true;
00572 }
00573
00574 configFilename = newConfig;
00575 config = LatexConfigCache::instance()->parser(configFilename);
00576
00577
00578 QString key;
00579 QMapIterator<QString, QString> i(config->properties);
00580 while (i.hasNext()) {
00581 i.next();
00582 key = i.key();
00583 if (!editorProperties.contains(key)) {
00584 editorProperties[key] = i.value();
00585 }
00586 }
00587 QString newFormula;
00588 if (unchanged) {
00589 if (PrefsManager::instance()->latexStartWithEmptyFrames()) {
00590 newFormula = "";
00591 } else {
00592 newFormula = config->emptyFrameText();
00593 }
00594 emit formulaAutoUpdate(formulaText, newFormula);
00595 formulaText = newFormula;
00596 }
00597
00598 emit applicationChanged();
00599 }
00600
00601 void PageItem_LatexFrame::killProcess()
00602 {
00603 killed = true;
00604 latex->kill();
00605 }
00606
00607 void PageItem_LatexFrame::restore(UndoState *state, bool isUndo)
00608 {
00609 SimpleState *ss = dynamic_cast<SimpleState*>(state);
00610 if (!ss) {
00611 PageItem_ImageFrame::restore(state, isUndo);
00612 return;
00613 }
00614 if (ss->contains("CHANGE_FORMULA")) {
00615 if (isUndo) {
00616 setFormula(ss->get("OLD_FORMULA"), false);
00617 } else {
00618 setFormula(ss->get("NEW_FORMULA"), false);
00619 }
00620 } else {
00621 PageItem_ImageFrame::restore(state, isUndo);
00622 }
00623 }
00624
00625
00626 void PageItem_LatexFrame::setUsePreamble(bool useP)
00627 {
00628 m_usePreamble = useP;
00629 }
00630
00631 void PageItem_LatexFrame::layout()
00632 {
00633 if (!invalid) return;
00634 invalid = false;
00635
00636 if (Width == lastWidth && Height == lastHeight) return;
00637 lastWidth = Width;
00638 lastHeight = Height;
00639
00640 rerunApplication(false);
00641 }
00642
00643 void PageItem_LatexFrame::applicableActions(QStringList & actionList)
00644 {
00645 actionList << "itemPreviewFull";
00646 actionList << "itemPreviewLow";
00647 actionList << "itemPreviewNormal";
00648 actionList << "itemUpdateImage";
00649 actionList << "editEditWithLatexEditor";
00650 if (PicAvail)
00651 {
00652
00653
00654 actionList << "editClearContents";
00655 actionList << "editCopyContents";
00656 }
00657
00658 qDebug() << "Buffertype" << doc()->scMW()->contentsBuffer.sourceType;
00659 if (doc()->scMW()->contentsBuffer.sourceType==PageItem::LatexFrame)
00660 {
00661 actionList << "editPasteContents";
00662 actionList << "editPasteContentsAbs";
00663 }
00664 }
00665
00666 QString PageItem_LatexFrame::infoDescription()
00667 {
00668 QString htmlText;
00669 htmlText.append("<h2>"+tr("Latex") + "</h2><table>");
00670 htmlText.append("<tr><th align=\"right\">" + tr("Application") + ": </th><td>" + application());
00671 htmlText.append("</td></tr><tr><th align=\"right\">" + tr("DPI") + ": </th><td>" +
00672 QString::number(realDpi()));
00673 htmlText.append("</td></tr><tr><th align=\"right\">" + tr("State") + ": </th><td>");
00674 if (latex->state() == QProcess::NotRunning) {
00675 if (error()) {
00676 htmlText.append(tr("Error"));
00677 } else {
00678 htmlText.append(tr("Finished"));
00679 }
00680 } else {
00681 htmlText.append(tr("Running"));
00682 }
00683 htmlText.append("</td></tr></table><hr>");
00684 htmlText.append(PageItem::infoDescription());
00685 return htmlText;
00686 }
00687