[KLF Application][KLF Tools][KLF Backend][KLF Home]
KLatexFormula Project

src/klfmainwin_p.h

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   file klfmainwin_p.h
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2010 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: klfmainwin_p.h 570 2010-11-27 00:59:33Z philippe $ */
00023 
00024 #ifndef KLFMAINWIN_P_H
00025 #define KLFMAINWIN_P_H
00026 
00027 
00033 #include <QDialog>
00034 #include <QFile>
00035 #include <QString>
00036 #include <QByteArray>
00037 #include <QLabel>
00038 #include <QImageReader>
00039 #include <QBuffer>
00040 #include <QFileInfo>
00041 #include <QMessageBox>
00042 
00043 #include <klfutil.h>
00044 #include "klflibview.h"
00045 #include "klfmain.h"
00046 #include "klfsettings.h"
00047 
00048 #include "klfmainwin.h"
00049 
00050 #include <ui_klfaboutdialog.h>
00051 #include <ui_klfwhatsnewdialog.h>
00052 
00054 class KLFHelpDialogCommon
00055 {
00056 public:
00057   KLFHelpDialogCommon(const QString& baseFName) : pBaseFName(baseFName) { }
00058   virtual ~KLFHelpDialogCommon() { }
00059 
00060   virtual void addExtraText(const QString& htmlSnipplet)
00061   {
00062     pHtmlExtraSnipplets << htmlSnipplet;
00063   }
00064 
00065   virtual QString getFullHtml()
00066   {
00067     QString html;
00068     QString fn = klfFindTranslatedDataFile(":/data/"+pBaseFName, ".html");
00069 
00070     QFile f(fn);
00071     f.open(QIODevice::ReadOnly);
00072     html = QString::fromUtf8(f.readAll());
00073 
00074     klfDbg( "read from file="<<fn<<" the HTML code=\n"<<html ) ;
00075 
00076     const QString marker = QLatin1String("<!--KLF_MARKER_INSERT_SPOT-->");
00077     int k;
00078     for (k = 0; k < pHtmlExtraSnipplets.size(); ++k)
00079       html.replace(marker, pHtmlExtraSnipplets[k]+"\n"+marker);
00080 
00081     klfDbg( "new HTML is:\n"<<html ) ;
00082     
00083     // replace some general recognized "macros"
00084     html.replace("<!--KLF_VERSION-->", KLF_VERSION_STRING);
00085 
00086     return html;
00087   }
00088 
00089 protected:
00090   QString pBaseFName;
00091   QStringList pHtmlExtraSnipplets;
00092 };
00093 
00094 
00096 class KLFAboutDialog : public QDialog, public KLFHelpDialogCommon
00097 {
00098   Q_OBJECT
00099 signals:
00100   void linkActivated(const QUrl&);
00101 public:
00102   KLFAboutDialog(QWidget *parent) : QDialog(parent), KLFHelpDialogCommon("about")
00103   {
00104     u = new Ui::KLFAboutDialog;
00105     u->setupUi(this);
00106 
00107     connect(u->txtDisplay, SIGNAL(anchorClicked(const QUrl&)),
00108             this, SIGNAL(linkActivated(const QUrl&)));
00109   }
00110   virtual ~KLFAboutDialog() { }
00111 
00112   virtual void show()
00113   {
00114     u->txtDisplay->setHtml(getFullHtml());
00115     QDialog::show();
00116     // refresh our style sheet
00117     setStyleSheet(styleSheet());
00118   }
00119 
00120 private:
00121   Ui::KLFAboutDialog *u;
00122 };
00123 
00124 
00126 class KLFWhatsNewDialog : public QDialog, public KLFHelpDialogCommon
00127 {
00128   Q_OBJECT
00129 signals:
00130   void linkActivated(const QUrl&);
00131 public:
00132   KLFWhatsNewDialog(QWidget *parent)
00133     : QDialog(parent),
00134       KLFHelpDialogCommon(QString("whats-new-%1.%2").arg(klfVersionMaj()).arg(klfVersionMin()))
00135   {
00136     u = new Ui::KLFWhatsNewDialog;
00137     u->setupUi(this);
00138 
00139     connect(u->txtDisplay, SIGNAL(anchorClicked(const QUrl&)),
00140             this, SIGNAL(linkActivated(const QUrl&)));
00141   }
00142   virtual ~KLFWhatsNewDialog() { }
00143 
00144   virtual void show()
00145   {
00146     u->txtDisplay->setHtml(getFullHtml());
00147     QDialog::show();
00148     // refresh our style sheet
00149     setStyleSheet(styleSheet());
00150   }
00151 
00152 private:
00153   Ui::KLFWhatsNewDialog *u;
00154 };
00155 
00156 
00157 
00159 class KLFMainWinPopup : public QLabel
00160 {
00161   Q_OBJECT
00162 public:
00163   KLFMainWinPopup(KLFMainWin *mainwin)
00164     : QLabel(mainwin, Qt::Window|Qt::FramelessWindowHint|Qt::CustomizeWindowHint|
00165              Qt::WindowStaysOnTopHint|Qt::X11BypassWindowManagerHint)
00166   {
00167     setObjectName("KLFMainWinPopup");
00168     setFocusPolicy(Qt::NoFocus);
00169     QWidget *frmMain = mainwin->findChild<QWidget*>("frmMain");
00170     if (frmMain)
00171       setGeometry(QRect(mainwin->geometry().topLeft()+QPoint(25,mainwin->height()*2/3),
00172                         QSize(frmMain->width()+15, 0)));
00173     setAttribute(Qt::WA_ShowWithoutActivating, true);
00174     setProperty("klfTopLevelWidget", QVariant(true));
00175     setAttribute(Qt::WA_StyledBackground, true);
00176     setStyleSheet(mainwin->window()->styleSheet());
00177     setMargin(0);
00178     QFont f = font();
00179     f.setPointSize(QFontInfo(f).pointSize() - 1);
00180     setFont(f);
00181     setWordWrap(true);
00182 
00183     connect(this, SIGNAL(linkActivated(const QString&)),
00184             this, SLOT(internalLinkActivated(const QString&)));
00185     connect(this, SIGNAL(linkActivated(const QUrl&)),
00186             mainwin, SLOT(helpLinkAction(const QUrl&)));
00187   }
00188 
00189   virtual ~KLFMainWinPopup() { }
00190 
00191   bool hasMessages() { return (bool)msgKeys.size(); }
00192 
00193   QStringList messageKeys() { return msgKeys; }
00194 
00195 signals:
00196   void linkActivated(const QUrl& url);
00197 
00198 public slots:
00199 
00203   void addMessage(const QString& msgKey, const QString& msgText)
00204   {
00205     klfDbg("adding message "<<msgKey<<" -> "<<msgText);
00206     msgKeys << msgKey;
00207     messages << ("<p>"+msgText+"</p>");
00208     updateText();
00209   }
00210 
00211   void removeMessage(const QString& msgKey)
00212   {
00213     int i = msgKeys.indexOf(msgKey);
00214     if (i < 0)
00215       return;
00216     msgKeys.removeAt(i);
00217     messages.removeAt(i);
00218     updateText();
00219   }
00220 
00221   void show()
00222   {
00223     QLabel::show();
00224     setStyleSheet(styleSheet());
00225   }
00226 
00227 private slots:
00228   void internalLinkActivated(const QString& url)
00229   {
00230     emit linkActivated(QUrl::fromEncoded(url.toLatin1()));
00231   }
00232 
00233   void updateText()
00234   {
00235     setText("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\""
00236             " \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
00237             "<html><head><meta name=\"qrichtext\" content=\"1\" />"
00238             "<style type=\"text/css\">\n"
00239             "body { margin: 2px 10px; }\n"
00240             "p { white-space: pre-wrap; padding: 0px; margin: 0px 0px 2px 0px; }\n"
00241             "a, a:link { text-decoration: underline; color: #0000af }\n"
00242             "a:hover { color: #0000ff }\n"
00243             "a:active { color: #ff0000 }\n"
00244             "}\n"
00245             "</style>"
00246             "</head>"
00247             "<body>\n"
00248             + messages.join("\n") +
00249             "<p style=\"margin-top: 5px\">"
00250             "<a href=\"klfaction:/popup?acceptAll\">"+tr("Accept [<b>Alt-Enter</b>]")+"</a> | "+
00251             " <a href=\"klfaction:/popup_close\">"+tr("Close [<b>Esc</b>]")+"</a> | " +
00252             " <a href=\"klfaction:/popup?configDontShow\">"+tr("Don't Show Again")+"</a></p>"+
00253             "</body></html>" );
00254     resize(width(), sizeHint().height());
00255   }
00256 
00257 private:
00258   QStringList msgKeys;
00259   QStringList messages;
00260 };
00261 
00262 
00263 
00264 
00265 
00266 // -------------------------------------------------
00267 
00268 
00269 class KLFBasicDataOpener : public QObject, public KLFAbstractDataOpener
00270 {
00271   Q_OBJECT
00272 public:
00273   KLFBasicDataOpener(KLFMainWin *mainwin) : QObject(mainwin), KLFAbstractDataOpener(mainwin) { }
00274   virtual ~KLFBasicDataOpener() { }
00275 
00276   virtual QStringList supportedMimeTypes()
00277   {
00278     QStringList types;
00279     types << "image/png"
00280           << "image/jpeg"
00281           << "text/uri-list"
00282           << "application/x-klf-libentries"
00283           << "application/x-klatexformula"
00284           << "application/x-klatexformula-db" ;
00285     int k;
00286     QList<QByteArray> fmts = QImageReader::supportedImageFormats();
00287     for (k = 0; k < fmts.size(); ++k) {
00288       types << "image/"+fmts[k].toLower();
00289       types << "image/x-"+fmts[k].toLower();
00290     }
00291     klfDbg("mime types: "<<types) ;
00292     return types;
00293   }
00294 
00295   virtual bool canOpenFile(const QString& file)
00296   {
00297     KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00298     klfDbg("file="<<file) ;
00299 
00300     QFile f(file);
00301     bool r = f.open(QIODevice::ReadOnly);
00302     if (!r) {
00303       klfDbg(" ... file cannot be accessed.") ;
00304       return false;
00305     }
00306     bool isimage;
00307     if (isKlfImage(&f, &isimage)) {
00308       klfDbg(" ... is KLF-saved image.") ;
00309       return true;
00310     }
00311     if (isimage) { // no try going further, we can't otherwise read image ...
00312       klfDbg(" ... is a non-KLF image.") ;
00313       return false;
00314     }
00315 
00316     QString libscheme = KLFLibBasicWidgetFactory::guessLocalFileScheme(file);
00317     if (!libscheme.isEmpty()) {
00318       klfDbg(" ... libscheme="<<libscheme) ;
00319       return true; // it is a library file
00320     }
00321     // by default, fail
00322     klfDbg(" ... not recognized.") ;
00323     return false;
00324   }
00325 
00326   virtual bool canOpenData(const QByteArray& data)
00327   {
00328     QBuffer buf;
00329     buf.setData(data);
00330     buf.open(QIODevice::ReadOnly);
00331     bool isimg = false;
00332     if (isKlfImage(&buf, &isimg))
00333       return true;
00334     if (isimg) // no try going further, we can't otherwise read image ...
00335       return false;
00336 
00337     // try to read beginning with a QDataStream to look for a known x-klf-libentries header
00338     QDataStream stream(&buf);
00339     stream.setVersion(QDataStream::Qt_4_4);
00340     QString headerstr;
00341     stream >> headerstr;
00342     if (headerstr == QLatin1String("KLF_LIBENTRIES"))
00343       return true;
00344     // *** see note in openData() for library formats. ***
00345     // don't try to open .klf or .klf.db formats.
00346 
00347     // otherwise, we can't recognize data, fail
00348     return false;
00349   }
00350 
00351   virtual bool openFile(const QString& file)
00352   {
00353     KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00354     klfDbg("file="<<file);
00355     QFileInfo fi(file);
00356     if (!fi.exists() || !fi.isReadable()) {
00357       klfDbg(" ... file is not readable") ;
00358       return false;
00359     }
00360     QString ext = fi.suffix().trimmed().toLower();
00361 
00362     { // try to open image
00363       QFile f(file);
00364       bool r = f.open(QIODevice::ReadOnly);
00365       if (!r) {
00366         klfDbg(" ... file cannot be opened") ;
00367         return false;
00368       }
00369       bool isimage = false;
00370       if (tryOpenImageData(&f, &isimage)) {
00371         klfDbg(" ... loaded image data!") ;
00372         return true;
00373       }
00374       if (isimage) { // no try going further, we can't otherwise read image ...
00375         klfDbg(" ... is non-KLF image...") ;
00376         return false;
00377       }
00378     }
00379 
00380     // open image failed, try the other formats...
00381 
00382     // try to load library file
00383     bool result = mainWin()->openLibFile(file);
00384 
00385     klfDbg("mainWin()->openLibFile("<<file<<") returned "<<result) ;
00386     if (result)
00387       return true;
00388 
00389     // and otherwise, fail
00390     return false;
00391   }
00392 
00393   virtual bool openData(const QByteArray& data, const QString& mimetype)
00394   {
00395     KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00396     klfDbg("mimetype is "<<mimetype) ;
00397 
00398     QBuffer buf;
00399     buf.setData(data);
00400     buf.open(QIODevice::ReadOnly);
00401 
00402     if (mimetype == "text/uri-list") {
00403       klfDbg("Opening uri-list") ;
00404       QByteArray line;
00405       QStringList flist;
00406       char sbuf[1024];
00407       qint64 len;
00408       while ((len = buf.readLine(sbuf, sizeof(sbuf))) > 0) {
00409         line = QByteArray(sbuf, len);
00410         line = line.trimmed(); // gnome uses \r\n, and URLs don't have whitespace.
00411         QUrl url = QUrl::fromEncoded(line);
00412         if (url.scheme() != "file") {
00413           qWarning()<<KLF_FUNC_NAME<<": can't open URL "<<url.scheme()<<" (can only open local files)";
00414           continue;
00415         }
00416         flist << klfUrlLocalFilePath(url);
00417         klfDbg("... added file "<<flist.last()) ;
00418       }
00419       if (flist.isEmpty()) {
00420         klfDbg("file list is empty.") ;
00421         return true;
00422       }
00423       return  mainWin()->openFiles(flist);
00424     }
00425 
00426     bool isimage = false;
00427     if (tryOpenImageData(&buf, &isimage))
00428       return true;
00429     if (isimage) // no try going further, we can't otherwise read image ...
00430       return false;
00431 
00432     // drop application/x-klf-libentries
00433     if (tryOpenKlfLibEntries(&buf))
00434       return true;
00435 
00436     // otherwise if we paste/drop library entries, the case is more delicate, as it can't be
00437     // just opened in the library browser, as they are not in a file. So for now, fail to open the data
00438     // Possibly, todo: suggest user to create a resource/sub-resource to drop the data into
00439     return false;
00440   }
00441 
00442 private:
00443   bool prepareImageReader(QImageReader *imagereader, QImage *img, bool *isImage)
00444   {
00445     *isImage = false;
00446     if (!imagereader->canRead()) {
00447       klfDbg("format "<<imagereader->format()<<": canRead() returned FALSE!") ;
00448       return false;
00449     }
00450     *img = imagereader->read();
00451     if (img->isNull()) {
00452       klfDbg("read() returned a null qimage!") ;
00453       return false;
00454     }
00455     *isImage = true;
00456     klfDbg("format: '"<<imagereader->format().constData()<<"': text keys set: "<<img->textKeys()
00457            <<"; error="<<imagereader->error()
00458            <<"; quality="<<imagereader->quality()
00459            <<"; supportsOption(description)="<<imagereader->supportsOption(QImageIOHandler::Description)
00460            <<"; text('AppVersion')="<<img->text("AppVersion")
00461            ) ;
00462     if (!img->text("AppVersion").startsWith("KLatexFormula ") && !img->text("AppVersion").startsWith("KLF ")) {
00463       klfDbg("AppVersion is not set to 'KL[atex]F[ormula] [...]' in image metadata.") ;
00464       return false;
00465     }
00466     // it is a klf-saved image
00467     return true;
00468   }
00469   bool isKlfImage(QIODevice *device, bool *isImage)
00470   {
00471     QList<QByteArray> formatlist = QImageReader::supportedImageFormats();
00472     int k;
00473     QImage img;
00474     QImageReader imagereader;
00475     for (k = 0; k < formatlist.size(); ++k) {
00476       device->seek(0);
00477       imagereader.setFormat(formatlist[k]);
00478       imagereader.setDevice(device);
00479       if (prepareImageReader(&imagereader, &img, isImage))
00480         return true;
00481     }
00482     return false;
00483   }
00484   bool tryOpenImageData(QIODevice *device, bool *isimage)
00485   {
00486     QList<QByteArray> formatlist = QImageReader::supportedImageFormats();
00487     int k;
00488     QImageReader imagereader;
00489     QImage img;
00490     for (k = 0; k < formatlist.size(); ++k) {
00491       device->seek(0);
00492       imagereader.setFormat(formatlist[k]);
00493       imagereader.setDevice(device);
00494       if (!prepareImageReader(&imagereader, &img, isimage))
00495         continue;
00496 
00497       // read meta-information
00498       QString latex = img.text("InputLatex");
00499       KLFStyle style;
00500       style.fg_color = read_color(img.text("InputFgColor"));
00501       style.bg_color = read_color(img.text("InputBgColor"));
00502       style.mathmode = img.text("InputMathMode");
00503       style.preamble = img.text("InputPreamble");
00504       style.dpi = img.text("InputDPI").toInt();
00505 
00506       mainWin()->slotLoadStyle(style);
00507       mainWin()->slotSetLatex(latex);
00508       return true;
00509     }
00510     return false;
00511   }
00512   unsigned long read_color(const QString& text)
00513   {
00514     //                               1          2          3     4         5
00515     QRegExp rx1("\\s*rgba?\\s*\\(\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)(\\s*,\\s*(\\d+))?\\s*\\)\\s*");
00516     if (rx1.exactMatch(text)) {
00517       if (rx1.cap(4).isEmpty())
00518         return qRgb(rx1.cap(1).toInt(), rx1.cap(2).toInt(), rx1.cap(3).toInt());
00519       else
00520         return qRgba(rx1.cap(1).toInt(), rx1.cap(2).toInt(), rx1.cap(3).toInt(), rx1.cap(5).toInt());
00521     }
00522     // try named color format
00523     QColor c(text);
00524     return c.rgba();
00525   }
00526 
00527   bool tryOpenKlfLibEntries(QIODevice *dev)
00528   {
00529     // if ONE entry -> load that latex and style
00530     // if more entries -> warn user, and fail
00531     QDataStream stream(dev);
00532     stream.setVersion(QDataStream::Qt_4_4);
00533     QString headerstr;
00534     stream >> headerstr;
00535     if (headerstr != QLatin1String("KLF_LIBENTRIES"))
00536       return false;
00537     QVariantMap properties;
00538     KLFLibEntryList entries;
00539     stream >> properties >> entries;
00540     if (entries.size() > 1) {
00541       QMessageBox::critical(mainWin(), tr("Error"),
00542                             tr("The data you have request to open contains multiple formulas.\n"
00543                                "You may only open one formula into the LaTeX code editor."));
00544       return false;
00545     }
00546     if (entries.size() == 0) {
00547       QMessageBox::critical(mainWin(), tr("Error"),
00548                             tr("The data you have request to open contains no formulas."));
00549       return false;
00550     }
00551     mainWin()->restoreFromLibrary(entries[0], KLFLib::RestoreAll);
00552     return true;
00553   }
00554 };
00555 
00556 
00557 
00558 
00559 
00560 
00561 class KLFAddOnDataOpener : public QObject, public KLFAbstractDataOpener
00562 {
00563   Q_OBJECT
00564 public:
00565   KLFAddOnDataOpener(KLFMainWin *mainwin) : QObject(mainwin), KLFAbstractDataOpener(mainwin) { }
00566   virtual ~KLFAddOnDataOpener() { }
00567 
00568   virtual QStringList supportedMimeTypes()
00569   {
00570     return QStringList();
00571   }
00572 
00573   virtual bool canOpenFile(const QString& file)
00574   {
00575     if (QFileInfo(file).suffix() == "rcc")
00576       return true;
00577     QFile f(file);
00578     bool r = f.open(QIODevice::ReadOnly);
00579     if (!r) { // can't open file
00580       return false;
00581     }
00582     // check if file is RCC file (begins with 'qres')
00583     if (f.read(4) == "qres")
00584       return true;
00585     // not a Qt RCC file
00586     return false;
00587   }
00588 
00589   virtual bool canOpenData(const QByteArray& data)
00590   {
00591     // Dropped files are opened by the basic data opener, which handles "text/uri-list"
00592     // by calling the main window's openFiles()
00593     return false;
00594   }
00595 
00596   virtual bool openFile(const QString& file)
00597   {
00598     KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00599     klfDbg("file="<<file);
00600 
00601     if (!canOpenFile(file)) {
00602       klfDbg("file is not openable by us: "<<file);
00603       return false;
00604     }
00605 
00606     mainWin()->settingsDialog()->show();
00607     mainWin()->settingsDialog()->showControl(KLFSettings::ManageAddOns);
00608     mainWin()->settingsDialog()->importAddOn(file, true);
00609 
00610     return true;
00611   }
00612 
00613   virtual bool openData(const QByteArray& /*data*/, const QString& /*mimetype*/)
00614   {
00615     return false;
00616   }
00617 };
00618 
00619 
00620 
00621 
00622 #endif

Generated by doxygen 1.7.3