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

src/klflatexsymbols.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   file klflatexsymbols.cpp
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2007 by Philippe Faist
00005  *   philippe.faist@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: klflatexsymbols.cpp 567 2010-11-26 23:03:06Z philippe $ */
00023 
00024 #include <stdio.h>
00025 
00026 #include <QFile>
00027 #include <QDir>
00028 #include <QTextStream>
00029 #include <QScrollArea>
00030 #include <QList>
00031 #include <QStringList>
00032 #include <QProgressDialog>
00033 #include <QGridLayout>
00034 #include <QPushButton>
00035 #include <QMap>
00036 #include <QStackedWidget>
00037 #include <QLineEdit>
00038 #include <QMessageBox>
00039 #include <QScrollBar>
00040 #include <QApplication>
00041 #include <QCloseEvent>
00042 #include <QDebug>
00043 #include <QPainter>
00044 #include <QPlastiqueStyle>
00045 
00046 #include <QDomDocument>
00047 #include <QDomElement>
00048 
00049 #include <klfbackend.h>
00050 
00051 #include <ui_klflatexsymbols.h>
00052 
00053 #include <klfpixmapbutton.h>
00054 #include "klfmain.h"
00055 #include "klfconfig.h"
00056 #include "klflatexsymbols.h"
00057 
00058 
00059 
00060 // ------------------
00061 
00062 
00063 KLFLatexSymbol::KLFLatexSymbol(const QDomElement& e)
00064   : symbol(), preamble(), textmode(false), bbexpand(), hidden(false)
00065 {
00066   // preamble
00067   QDomNodeList usepackagelist = e.elementsByTagName("usepackage");
00068   int k;
00069   for (k = 0; k < usepackagelist.size(); ++k) {
00070     QString package = usepackagelist.at(k).toElement().attribute("name");
00071     if (package[0] == '[' || package[0] == '{')
00072       preamble.append(QString::fromAscii("\\usepackage%1").arg(package));
00073     else
00074       preamble.append(QString::fromAscii("\\usepackage{%1}").arg(package));
00075   }
00076   QDomNodeList preamblelinelist = e.elementsByTagName("preambleline");
00077   for (k = 0; k < preamblelinelist.size(); ++k) {
00078     preamble.append(preamblelinelist.at(k).toElement().text());
00079   }
00080   // textmode
00081   if (e.attribute("textmode") == "true")
00082     textmode = true;
00083   else
00084     textmode = false;
00085 
00086   if (e.elementsByTagName("hidden").size() > 0)
00087     hidden = true;
00088 
00089   // bb offset
00090   QDomNodeList bblist = e.elementsByTagName("bb");
00091   if (bblist.size() > 1) {
00092     fprintf(stderr, "WARNING: Expected at most single <bb expand=\"..\"/> item!\n");
00093   }
00094   if (bblist.size()) {
00095     sscanf(bblist.at(0).toElement().attribute("expand").toLatin1().constData(), "%d,%d,%d,%d",
00096            &bbexpand.t, &bbexpand.r, &bbexpand.b, &bbexpand.l);
00097   }
00098 
00099   // latex code
00100   QDomNodeList latexlist = e.elementsByTagName("latex");
00101   if (latexlist.size() != 1) {
00102     fprintf(stderr, "WARNING: Expected single <latex>...</latex> in symbol entry!\n");
00103   }
00104   if (latexlist.size() == 0)
00105     return;
00106   symbol = latexlist.at(0).toElement().text();
00107 
00108   klfDbg("read symbol "<<symbol<<" hidden="<<hidden);
00109 }
00110 
00111 KLF_EXPORT bool operator==(const KLFLatexSymbol& a, const KLFLatexSymbol& b)
00112 {
00113   return a.symbol == b.symbol &&
00114     a.textmode == b.textmode &&
00115     a.preamble == b.preamble &&
00116     a.bbexpand.t == b.bbexpand.t &&
00117     a.bbexpand.r == b.bbexpand.r &&
00118     a.bbexpand.b == b.bbexpand.b &&
00119     a.bbexpand.l == b.bbexpand.l &&
00120     a.hidden == b.hidden;
00121 }
00122 
00123 KLF_EXPORT bool operator<(const KLFLatexSymbol& a, const KLFLatexSymbol& b)
00124 {
00125   if (a.symbol != b.symbol)
00126     return a.symbol < b.symbol;
00127   if (a.textmode != b.textmode)
00128     return a.textmode < b.textmode;
00129   if (a.preamble.size() != b.preamble.size())
00130     return a.preamble.size() < b.preamble.size();
00131   int k;
00132   for (k = 0; k < a.preamble.size(); ++k)
00133     if (a.preamble[k] != b.preamble[k])
00134       return a.preamble[k] < b.preamble[k];
00135   // a and b seem to be equal
00136   return false;
00137 }
00138 
00139 QDataStream& operator<<(QDataStream& stream, const KLFLatexSymbol& s)
00140 {
00141   return stream << s.symbol << s.preamble << (quint8)s.textmode
00142                 << (qint16)s.bbexpand.t << (qint16)s.bbexpand.r
00143                 << (qint16)s.bbexpand.b << (qint16)s.bbexpand.l << (quint8)s.hidden;
00144 }
00145 
00146 QDataStream& operator>>(QDataStream& stream, KLFLatexSymbol& s)
00147 {
00148   quint8 textmode, hidden;
00149   struct { qint16 t, r, b, l; } readbbexpand;
00150   stream >> s.symbol >> s.preamble >> textmode >> readbbexpand.t >> readbbexpand.r
00151          >> readbbexpand.b >> readbbexpand.l >> hidden;
00152   s.bbexpand.t = readbbexpand.t;
00153   s.bbexpand.r = readbbexpand.r;
00154   s.bbexpand.b = readbbexpand.b;
00155   s.bbexpand.l = readbbexpand.l;
00156   s.textmode = textmode;
00157   s.hidden = hidden;
00158   return stream;
00159 }
00160 
00161 
00162 
00163 // -----------------------------------------------------------
00164 
00165 
00166 KLFLatexSymbolsCache * KLFLatexSymbolsCache::staticCache = NULL;
00167 
00168 static QString __rel_cache_file = QString();
00169 static QString relcachefile()
00170 {
00171   if (__rel_cache_file.isEmpty())
00172     __rel_cache_file =
00173       QString("/symbolspixmapcache-klf%1").arg(KLF_DATA_STREAM_APP_VERSION);
00174   return __rel_cache_file;
00175 }
00176 
00177 KLFLatexSymbolsCache::KLFLatexSymbolsCache()
00178 {
00179   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00180   // load the cache
00181 
00182   QStringList cachefiles;
00183   cachefiles
00184     << klfconfig.homeConfigDir+relcachefile()
00185     << ":/data/symbolspixmapcache_base" ;
00186   int k;
00187   bool ok = false;
00188   for (k = 0; !ok && k < cachefiles.size(); ++k) {
00189     // ??: do the two attempts here apply a datastream version to the header only, or to
00190     //     the data too?
00191     klfDbg("trying to load from "<<cachefiles[k]) ;
00192     ok = (  loadCacheFrom(cachefiles[k], QDataStream::Qt_4_4)
00193             == KLFLatexSymbolsCache::Ok  ) ;
00194     if (!ok) {
00195       klfDbg("trying to load from "<<cachefiles[k]<<" with default header datastream version") ;
00196       ok = (  loadCacheFrom(cachefiles[k], -1)
00197               == KLFLatexSymbolsCache::Ok  ) ;
00198     }
00199   }
00200   if ( ! ok ) {
00201     qWarning() << KLF_FUNC_NAME << ": error finding and reading cache file!";
00202   }
00203 
00204   flag_modified = false;
00205 }
00206 
00207 int KLFLatexSymbolsCache::loadCacheStream(QDataStream& stream)
00208 {
00209   QString readHeader;
00210   QString readCompatKLFVersion;
00211   bool r = klfDataStreamReadHeader(stream, QStringList()<<"KLATEXFORMULA_SYMBOLS_PIXMAP_CACHE",
00212                                    &readHeader, &readCompatKLFVersion);
00213   if (!r) {
00214     klfDbg("failed to read symbolscache data header. readHeader="<<readHeader
00215            <<", readcompatklfver="<<readCompatKLFVersion) ;
00216     if (readHeader.isEmpty() || readCompatKLFVersion.isEmpty())
00217       return BadHeader;
00218     // otherwise, it's a bad version error
00219     return BadVersion;
00220   }
00221 
00222   // stream is now ready to read
00223 
00224   stream >> cache;
00225 
00226   flag_modified = false;
00227   return 0;
00228 }
00229 
00230 int KLFLatexSymbolsCache::saveCacheStream(QDataStream& stream)
00231 {
00232   klfDataStreamWriteHeader(stream, "KLATEXFORMULA_SYMBOLS_PIXMAP_CACHE");
00233   // stream is now ready to be written
00234   stream << cache;
00235   flag_modified = false;
00236   return 0;
00237 }
00238 
00239 QPixmap KLFLatexSymbolsCache::getPixmap(const KLFLatexSymbol& sym, bool fromcacheonly)
00240 {
00241   klfDbg("sym.symbol="<<sym.symbol<<" fromCacheOnly="<<fromcacheonly) ;
00242 
00243   if (cache.contains(sym)) {
00244     klfDbg("Found symbol in cache! pixmap is null="<<cache[sym].isNull()<<"; sym.preamble="<<sym.preamble.join(";"));
00245     return cache[sym];
00246   }
00247 
00248   if (fromcacheonly) {
00249     // if we weren't able to load it from cache, show failed icon
00250     return QPixmap(":/pics/badsym.png");
00251   }
00252 
00253   {
00254     KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME+"/clean cache duplicate test") ;
00255     // clean cache: make sure there are no two duplicate symbols (this is for the popup hint parser,
00256     // so that it doesn't detect old symbols in the cache)
00257     // This is done only if fromcache is false, so as to perform the check only on first pass
00258     // when generating the symbol cache.
00259     QMap<KLFLatexSymbol,QPixmap>::iterator it = cache.begin();
00260     while (it != cache.end()) {
00261       klfDbg("Testing symbol "<<it.key().symbol<<",preamble="<<it.key().preamble.join(",")
00262              << "for being a duplicate of "<<sym.symbol);
00263       if (it.key().symbol == sym.symbol) {
00264         klfDbg("erasing duplicate.");
00265         it = cache.erase(it); // erase old symbol entry
00266       } else {
00267         ++it;
00268       }
00269     }
00270   }
00271 
00272   if (sym.hidden) {
00273     // special treatment for hidden symbols
00274     // insert a QPixmap() into cache and return it
00275     klfDbg("symbol is hidden. Assigning NULL pixmap.") ;
00276     cache[sym] = QPixmap();
00277     return QPixmap();
00278   }
00279 
00280   const float mag = 4.0;
00281 
00282   KLFBackend::klfInput in;
00283   in.latex = sym.symbol;
00284   in.mathmode = sym.textmode ? "..." : "\\[ ... \\]";
00285   in.preamble = sym.preamble.join("\n")+"\n";
00286   in.fg_color = qRgb(0,0,0);
00287   in.bg_color = qRgba(255,255,255,0); // transparent Bg
00288   in.dpi = (int)(mag * 150);
00289 
00290   backendsettings.epstopdfexec = ""; // don't waste time making PDF, we don't need it
00291   backendsettings.tborderoffset = sym.bbexpand.t;
00292   backendsettings.rborderoffset = sym.bbexpand.r;
00293   backendsettings.bborderoffset = sym.bbexpand.b;
00294   backendsettings.lborderoffset = sym.bbexpand.l;
00295 
00296   KLFBackend::klfOutput out = KLFBackend::getLatexFormula(in, backendsettings);
00297 
00298   if (out.status != 0) {
00299     qWarning()
00300       <<KLF_FUNC_NAME
00301       <<QString(":ERROR: Can't generate preview for symbol %1 : status %2 !\n\tError: %3\n")
00302       .arg(sym.symbol).arg(out.status).arg(out.errorstr);
00303     return QPixmap(":/pics/badsym.png");
00304   }
00305 
00306   flag_modified = true;
00307 
00308   QImage scaled = out.result.scaled((int)(out.result.width() / mag),
00309                                     (int)(out.result.height() / mag),
00310                                     Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
00311   QPixmap pix = QPixmap::fromImage(scaled);
00312   cache[sym] = pix;
00313 
00314   klfDbg("Ran getLatexFormula(), got the pixmap. Returning.") ;
00315 
00316   return pix;
00317 }
00318 
00319 int KLFLatexSymbolsCache::precacheList(const QList<KLFLatexSymbol>& list, bool userfeedback,
00320                                        QWidget *parent)
00321 {
00322   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00323 
00324   QProgressDialog *pdlg = NULL;
00325 
00326   if (userfeedback) {
00327     pdlg = new QProgressDialog(QObject::tr("Please wait while generating symbol previews ... "),
00328                                QObject::tr("Skip"), 0, list.size()-1, parent);
00331     //  pdlg->setMinimumDuration(15000);
00332     pdlg->setWindowModality(Qt::WindowModal);
00333     pdlg->setModal(true);
00334     pdlg->setValue(0);
00335   }
00336 
00337   for (int i = 0; i < list.size(); ++i) {
00338     if (userfeedback) {
00339       // get events for cancel button (for example)
00340       qApp->processEvents();
00341       if (pdlg->wasCanceled()) {
00342         delete pdlg;
00343         return 1;
00344       }
00345       pdlg->setValue(i);
00346     }
00347     getPixmap(list[i], false);
00348   }
00349 
00350   if (userfeedback) {
00351     delete pdlg;
00352   }
00353 
00354   return 0;
00355 };
00356 
00357 
00358 
00359 void KLFLatexSymbolsCache::setBackendSettings(const KLFBackend::klfSettings& settings)
00360 {
00361   backendsettings = settings;
00362 }
00363 
00364 KLFLatexSymbol KLFLatexSymbolsCache::findSymbol(const QString& symbolCode)
00365 {
00366   for (QMap<KLFLatexSymbol,QPixmap>::const_iterator it = cache.begin();
00367        it != cache.end(); ++it) {
00368     if (it.key().symbol == symbolCode)
00369       return it.key();
00370   }
00371   return KLFLatexSymbol();
00372 }
00373 
00374 QStringList KLFLatexSymbolsCache::symbolCodeList()
00375 {
00376   QStringList l;
00377   for (QMap<KLFLatexSymbol,QPixmap>::const_iterator it = cache.begin();
00378        it != cache.end(); ++it)
00379     l << it.key().symbol;
00380   return l;
00381 }
00382 
00383 QPixmap KLFLatexSymbolsCache::findSymbolPixmap(const QString& symbolCode)
00384 {
00385   KLFLatexSymbol sym = findSymbol(symbolCode);
00386   if (sym.symbol.isEmpty()) {
00387     // invalid symbol
00388     qWarning()<<KLF_FUNC_NAME<<": Can't find symbol "<<symbolCode<<".";
00389     return QPixmap();
00390   }
00391   // return the pixmap from cache
00392   return cache[sym];
00393 }
00394 
00395 
00396 
00397 
00398 
00399 
00400 
00401 // private
00402 int KLFLatexSymbolsCache::loadCacheFrom(const QString& fname, int version)
00403 {
00404   QFile f(fname);
00405   if ( ! f.open(QIODevice::ReadOnly) ) {
00406     klfDbg("Failed to open "<<fname) ;
00407     return -1;
00408   }
00409   QDataStream ds(&f);
00410   if (version >= 0)
00411     ds.setVersion(version);
00412   int r = loadCacheStream(ds);
00413   return r;
00414 }
00415 
00416 
00417 // static
00418 KLFLatexSymbolsCache * KLFLatexSymbolsCache::theCache()
00419 {
00420   if (staticCache == NULL) {
00421     staticCache = new KLFLatexSymbolsCache;
00422   }
00423   return staticCache;
00424 }
00425 // static
00426 void KLFLatexSymbolsCache::saveTheCache()
00427 {
00428   if (staticCache->cacheNeedsSave()) {
00429     QString s = klfconfig.homeConfigDir + relcachefile();
00430     QFile f(s);
00431     if ( ! f.open(QIODevice::WriteOnly) ) {
00432       qWarning() << KLF_FUNC_NAME<< "Can't save cache to file "<< s << "!";
00433       return;
00434     }
00435     QDataStream ds(&f);
00436     ds.setVersion(QDataStream::Qt_4_4);
00437     staticCache->saveCacheStream(ds);
00438     klfDbg("Saved cache to file "<<s);
00439   }
00440 }
00441 
00442 
00443 
00444 
00445 // -----------------------------------------------------------
00446 
00447 
00448 
00449 
00450 
00451 KLFLatexSymbolsView::KLFLatexSymbolsView(const QString& category, QWidget *parent)
00452   : QScrollArea(parent), _category(category)
00453 {
00454   mFrame = new QWidget(this);
00455 
00456   setWidgetResizable(true);
00457 
00458   //  mFrame->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
00459   //  mFrame->setFrameShadow(QFrame::Sunken);
00460   //  mFrame->setFrameShape(QFrame::Box);
00461   mFrame->setObjectName("frmSymbolList");
00462   //   mFrame->setFrameShadow(QFrame::Plain);
00463   //   mFrame->setFrameShape(QFrame::NoFrame);
00464   //   mFrame->setMidLineWidth(0);
00465   //   mFrame->setLineWidth(0);
00466 
00467   mLayout = 0;
00468   mSpacerItem = 0;
00469 
00470   setWidget(mFrame);
00471 }
00472 
00473 void KLFLatexSymbolsView::setSymbolList(const QList<KLFLatexSymbol>& symbols)
00474 {
00475   _symbols.clear();
00476   appendSymbolList(symbols);
00477 }
00478 void KLFLatexSymbolsView::appendSymbolList(const QList<KLFLatexSymbol>& symbols)
00479 {
00480   // filter out hidden symbols
00481   int k;
00482   for (k = 0; k < symbols.size(); ++k)
00483     if ( ! symbols[k].hidden )
00484       _symbols.append(symbols[k]);
00485 }
00486 
00487 void KLFLatexSymbolsView::buildDisplay()
00488 {
00489   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00490 #ifdef Q_WS_MAC
00491   QStyle *myStyle = new QPlastiqueStyle;
00492   QPalette pal = palette();
00493   pal.setColor(QPalette::Window, QColor(206,207,233));
00494   pal.setColor(QPalette::Base, QColor(206,207,233));
00495   pal.setColor(QPalette::Button, QColor(206,207,233));
00496 #endif
00497   mLayout = new QGridLayout(mFrame);
00498   int i;
00499   for (i = 0; i < _symbols.size(); ++i) {
00500     QPixmap p = KLFLatexSymbolsCache::theCache()->getPixmap(_symbols[i]);
00501     KLFPixmapButton *btn = new KLFPixmapButton(p, mFrame);
00502 #ifdef Q_WS_MAC
00503     btn->setStyle(myStyle);
00504     btn->setPalette(pal);
00505 #endif
00506     btn->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
00507     btn->setProperty("symbol", QVariant::fromValue<int>(i));
00508     btn->setProperty("gridpos", QPoint(-1,-1));
00509     btn->setProperty("gridcolspan", -1);
00510     btn->setProperty("myWidth", p.width() + 4);
00511     QString tooltiptext =
00512       "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\""
00513       " \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
00514       "<html><head><meta name=\"qrichtext\" content=\"1\" />"
00515       "<style type=\"text/css\">\n"
00516       "p { white-space: pre-wrap; padding: 0px; margin: 0px 0px 2px 0px; }\n"
00517       "pre { padding: 0px; margin: 0px 0px 2px 0px; }\n"
00518       "</style>"
00519       "</head>"
00520       "<body>\n"
00521       "<p style=\"white-space: pre\">"+tr("LaTeX code:")+" <b><tt>"+_symbols[i].symbol+"</tt></b>"+
00522       (_symbols[i].textmode?tr(" [in text mode]"):QString(""))+
00523       +"</p>";
00524     if (_symbols[i].preamble.size())
00525       tooltiptext += "<p>"+tr("Requires:")+"<b><pre>" +
00526         _symbols[i].preamble.join("\n")+"</pre></b></p>";
00527     tooltiptext += "</body></html>";
00528     btn->setToolTip(tooltiptext);
00529     //klfDbg("tooltip text is "<<tooltiptext);
00530     connect(btn, SIGNAL(clicked()), this, SLOT(slotSymbolActivated()));
00531     mSymbols.append(btn);
00532   }
00533   mSpacerItem = new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding);
00534   recalcLayout();
00535 }
00536 
00537 void KLFLatexSymbolsView::recalcLayout()
00538 {
00539   int row = 0, col = 0, colspan;
00540   int n = klfconfig.UI.symbolsPerLine;
00541   int quantawidth = 55; // hard-coded here
00542   //  printf("DEBUG: n=%d, quantawidth=%d\n", n, quantawidth);
00543   int i;
00544   // now add them all again as needed
00545   for (i = 0; i < mSymbols.size(); ++i) {
00546     colspan = 1 + mSymbols[i]->property("myWidth").toInt() / quantawidth;
00547     if (colspan < 1)
00548       colspan = 1;
00549 
00550     if (colspan > n)
00551       colspan = n;
00552     if (col + colspan > n) {
00553       row++;
00554       col = 0;
00555     }
00556     if (mSymbols[i]->property("gridpos") != QPoint(row, col) ||
00557         mSymbols[i]->property("gridcolspan") != colspan) {
00558       //      printf("DEBUG: %d: setting to (%d,%d)+(1,%d)\n", i, row, col, colspan);
00559       mSymbols[i]->setProperty("gridpos", QPoint(row, col));
00560       mSymbols[i]->setProperty("gridcolspan", colspan);
00561       mLayout->removeWidget(mSymbols[i]);
00562       mLayout->addWidget(mSymbols[i], row, col, 1, colspan);
00563     }
00564     col += colspan;
00565     if (col >= n) {
00566       row++;
00567       col = 0;
00568     }
00569   }
00570   // remove spacer and add it again
00571   mLayout->removeItem(mSpacerItem);
00572   mLayout->addItem(mSpacerItem, row+1, 0);
00573 
00574   setMinimumWidth(mFrame->sizeHint().width() + verticalScrollBar()->width() + 2);
00575 }
00576 
00577 
00578 void KLFLatexSymbolsView::slotSymbolActivated()
00579 {
00580   QObject *s = sender();
00581   int i = s->property("symbol").toInt();
00582   if (i < 0 || i >= _symbols.size())
00583     qWarning()<<KLF_FUNC_NAME<<": Inavlid symbol index "<<i;
00584   else
00585     emit symbolActivated(_symbols[i]);
00586 }
00587 
00588 
00589 bool KLFLatexSymbolsView::searchIterMatches(const SearchIterator& pos, const QString& queryString)
00590 {
00591   // remember:  SearchIterator==int
00592   if (pos < 0 || pos >= mSymbols.size())
00593     return false;
00594 
00595   int symIndex = mSymbols[pos]->property("symbol").toInt();
00596   if (symIndex < 0 || symIndex >= _symbols.size()) {
00597     qWarning()<<KLF_FUNC_NAME<<": Inavlid symbol index "<<symIndex;
00598     return false;
00599   }
00600 
00601   // (X)Emacs-style: presence of capital letter triggers case sensitive search
00602   Qt::CaseSensitivity cs = (queryString.contains(QRegExp("[A-Z]")) ? Qt::CaseSensitive : Qt::CaseInsensitive) ;
00603 
00604   if ( _symbols[symIndex].symbol.contains(queryString, cs) ||
00605        _symbols[symIndex].preamble.contains(queryString, cs) ) {
00606     klfDbg("found match at "<<symIndex<<": "<<_symbols[symIndex].symbol) ;
00607     return true;
00608   }
00609   return false;
00610 }
00611 
00612 void KLFLatexSymbolsView::searchPerformed(const SearchIterator& result)
00613 {
00614   klfDbg("result is "<<result<<" valid="<<(result<mSymbols.size())) ;
00615 
00616   highlightSearchMatches(result);
00617 }
00618 void KLFLatexSymbolsView::searchAbort()
00619 {
00620   KLFIteratorSearchable<int>::searchAbort();
00621   highlightSearchMatches(-1);
00622   //  setFocus();
00623 }
00624 
00625 void KLFLatexSymbolsView::highlightSearchMatches(int currentMatch)
00626 {
00627   QString stylesheets[] = {
00628     // don't affect tooltips: give KLFPixmapButton { } scopes
00629     "",
00630     "KLFPixmapButton { background-color: rgb(180,180,255); }",
00631     "KLFPixmapButton { background-color: rgb(0,0,255); }"
00632   };
00633 
00634   if (currentMatch == -1) {
00635     // abort search
00636     stylesheets[0] = stylesheets[1] = stylesheets[2] = QString();
00637   }
00638   int k;
00639   for (k = 0; k < mSymbols.size(); ++k) {
00640     int which = 0;
00641     if (k == currentMatch)
00642       which = 2;
00643     else if (searchIterMatches(k, searchQueryString()))
00644       which = 1;
00645     mSymbols[k]->setStyleSheet(stylesheets[which]);
00646   }  
00647   if (currentMatch >= 0 && currentMatch < mSymbols.size())
00648     ensureWidgetVisible(mSymbols[currentMatch]);
00649 }
00650 
00651 
00652 
00653 
00654 
00655 KLFLatexSymbols::KLFLatexSymbols(QWidget *parent, const KLFBackend::klfSettings& baseSettings)
00656   : QWidget(
00657 #if defined(Q_OS_WIN32)
00658             0 /* parent */
00659 #else
00660             parent /* 0 */
00661 #endif
00662             , /*Qt::Tool*/ Qt::Window /*0*/)
00663 {
00664   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00665 
00666   u = new Ui::KLFLatexSymbols;
00667   u->setupUi(this);
00668   setAttribute(Qt::WA_StyledBackground);
00669 
00670   // add our search bar
00671   pSearchBar = new KLFSearchBar(this);
00672   KLF_DEBUG_ASSIGN_REF_INSTANCE(pSearchBar, "latexsymbols-searchbar") ;
00673   pSearchBar->setShowOverlayMode(true);
00674   pSearchBar->registerShortcuts(this);
00675   pSearchBar->setSearchText("");
00676   pSearchBar->setShowHideButton(true);
00677   connect(pSearchBar, SIGNAL(escapePressed()), pSearchBar, SLOT(hide()));
00678 
00679   klfDbg("prepared search bar.") ;
00680 
00681   KLFLatexSymbolsCache::theCache()->setBackendSettings(baseSettings);
00682 
00683   // read our config and create the UI
00684   read_symbols_create_ui();
00685 
00686   slotShowCategory(0);
00687 
00688   QFont f = u->cbxCategory->font();
00689   int ps = f.pointSize();
00690   if (ps < 8)
00691     ps = QFontInfo(f).pointSize();
00692   f.setPointSize(ps+1);
00693   u->cbxCategory->setFont(f);
00694 
00695   connect(u->cbxCategory, SIGNAL(highlighted(int)), this, SLOT(slotShowCategory(int)));
00696   connect(u->cbxCategory, SIGNAL(activated(int)), this, SLOT(slotShowCategory(int)));
00697   connect(u->btnClose, SIGNAL(clicked()), this, SLOT(close()));
00698 }
00699 
00700 void KLFLatexSymbols::retranslateUi(bool alsoBaseUi)
00701 {
00702   if (alsoBaseUi)
00703     u->retranslateUi(this);
00704 }
00705 
00706 KLFLatexSymbols::~KLFLatexSymbols()
00707 {
00708   KLFLatexSymbolsCache::saveTheCache();
00709 }
00710 
00711 void KLFLatexSymbols::read_symbols_create_ui()
00712 {
00713   klfDbgT("called.") ;
00714 
00715   // create our UI
00716   u->cbxCategory->clear();
00717   QGridLayout *lytstk = new QGridLayout(u->frmStackContainer);
00718   stkViews = new QStackedWidget(u->frmStackContainer);
00719   lytstk->addWidget(stkViews, 0, 0);
00720 
00721   mViews.clear();
00722 
00723   // find collection of XML files
00724   QStringList fxmllist;
00725   // in the following directories
00726   QStringList fxmldirs;
00727   fxmldirs << klfconfig.homeConfigDir + "/conf/latexsymbols.d/"
00728            << klfconfig.globalShareDir + "/conf/latexsymbols.d/"
00729            << ":/conf/latexsymbols.d/";
00730 
00731   klfDbgT("starting to collect XML files from dirs "<<fxmldirs) ;
00732 
00733   // collect all XML files
00734   int k, j;
00735   for (k = 0; k < fxmldirs.size(); ++k) {
00736     QDir fxmldir(fxmldirs[k]);
00737     QStringList xmllist = fxmldir.entryList(QStringList()<<"*.xml", QDir::Files);
00738     for (j = 0; j < xmllist.size(); ++j)
00739       fxmllist << fxmldir.absoluteFilePath(xmllist[j]);
00740   }
00741   klfDbgT("files collected: "<<fxmllist) ;
00742   if (fxmllist.isEmpty()) {
00743     // copy legacy XML file into the home latexsymbols.d directory
00744     QDir("/").mkpath(klfconfig.homeConfigDir+"/conf/latexsymbols.d");
00745     if (QFile::exists(klfconfig.homeConfigDir+"/latexsymbols.xml")) {
00746       QFile::copy(klfconfig.homeConfigDir+"/latexsymbols.xml",
00747                   klfconfig.homeConfigDir+"/conf/latexsymbols.d/mylatexsymbols.xml");
00748       fxmllist << klfconfig.homeConfigDir+"/conf/latexsymbols.d/mylatexsymbols.xml";
00749     } else {
00750       QFile::copy(":/data/latexsymbols.xml",
00751                   klfconfig.homeConfigDir+"/conf/latexsymbols.d/defaultlatexsymbols.xml");
00752       fxmllist << klfconfig.homeConfigDir+"/conf/latexsymbols.d/defaultlatexsymbols.xml";
00753     }
00754   }
00755 
00756   klfDbgT("got xml files, ensured not empty; fxmllist="<<fxmllist) ;
00757 
00758   // this will be a full list of symbols to feed to the cache
00759   QList<KLFLatexSymbol> allsymbols;
00760 
00761   // same indexes as in mViews[]
00762   QStringList categoryTitleLangs;
00763 
00764   // now read the file list
00765   for (k = 0; k < fxmllist.size(); ++k) {
00766     KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME+"/fxmllist["+('0'+k)+"]");
00767     klfDbg("reading XML file="<<fxmllist[k]);
00768 
00769     QString fn = fxmllist[k];
00770     QFile file(fn);
00771     if ( ! file.open(QIODevice::ReadOnly) ) {
00772       qWarning()<<KLF_FUNC_NAME<<": Error: Can't open latex symbols XML file "<<fn<<": "<<file.errorString()<<"!";
00773       continue;
00774     }
00775 
00776     QDomDocument doc("latexsymbols");
00777     QString errMsg; int errLine, errCol;
00778     bool r = doc.setContent(&file, false, &errMsg, &errLine, &errCol);
00779     if (!r) {
00780       qWarning()<<KLF_FUNC_NAME<<": Error parsing file "<<fn<<": "<<errMsg<<" at line "<<errLine<<", col "<<errCol;
00781       continue;
00782     }
00783     file.close();
00784     
00785     QDomElement root = doc.documentElement();
00786     if (root.nodeName() != "latexsymbollist") {
00787       qWarning("%s: Error parsing XML for latex symbols from file `%s': unexpected root tag `%s'.\n", KLF_FUNC_NAME,
00788                qPrintable(fn), qPrintable(root.nodeName()));
00789       continue;
00790     }
00791 
00792     QDomNode n;
00793     for (n = root.firstChild(); ! n.isNull(); n = n.nextSibling()) {
00794       QDomElement e = n.toElement(); // try to convert the node to an element.
00795       if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
00796         continue;
00797       if ( e.nodeName() != "category" ) {
00798         qWarning("WARNING in parsing XML : ignoring unexpected tag `%s'!\n",
00799                  qPrintable(e.nodeName()));
00800         continue;
00801       }
00802       // read category
00803       QString heading = e.attribute("name");
00804       QString categoryTitle;
00805       // xml:lang attribute for category title is obsolete, we use now Qt-linguist translated value...
00806       QString curCategoryTitleLang;
00807       QList<KLFLatexSymbol> l;
00808       QDomNode esym;
00809       for (esym = e.firstChild(); ! esym.isNull(); esym = esym.nextSibling() ) {
00810         if ( esym.isNull() || esym.nodeType() != QDomNode::ElementNode )
00811           continue;
00812         QDomElement eesym = esym.toElement();
00813         klfDbg("read element "<<esym.nodeName());
00814         if ( eesym.nodeName() == "category-title" ) {
00815           // xml:lang attribute for category title is obsolete, we use now Qt-linguist translated value...
00816           QString lang = eesym.hasAttribute("xml:lang") ? eesym.attribute("xml:lang") : QString() ;
00817           klfDbg("<category-title>: lang="<<lang<<"; hasAttribute(xml:lang)="<<eesym.hasAttribute("xml:lang")
00818                  <<"; current category-title="<<categoryTitle<<",lang="<<curCategoryTitleLang) ;
00819           if (categoryTitle.isEmpty()) {
00820             // no category title yet
00821             if (lang.isEmpty() || lang.startsWith(klfconfig.UI.locale) || klfconfig.UI.locale.startsWith(lang)) {
00822               // correct locale
00823               categoryTitle = qApp->translate("xmltr_latexsymbols", eesym.text().toUtf8().constData(),
00824                                               "[[tag: <category-title>]]", QCoreApplication::UnicodeUTF8);
00825               curCategoryTitleLang = lang;
00826             }
00827             // otherwise skip this tag
00828           } else {
00829             // see if this locale is correct and more specific
00830             if ( (lang.startsWith(klfconfig.UI.locale) || klfconfig.UI.locale.startsWith(lang)) &&
00831                  (curCategoryTitleLang.isEmpty() || lang.startsWith(curCategoryTitleLang) ) ) {
00832               // then keep it and replace the other
00833               categoryTitle = eesym.text();
00834               curCategoryTitleLang = lang;
00835             }
00836             // otherwise skip this tag
00837           }
00838           continue;
00839         }
00840         if ( esym.nodeName() != "sym" ) {
00841           qWarning("%s: WARNING in parsing XML : ignoring unexpected tag `%s' in category `%s'!\n",
00842                    KLF_FUNC_NAME, qPrintable(esym.nodeName()), qPrintable(heading));
00843           continue;
00844         }
00845         // read symbol
00846         KLFLatexSymbol sym(eesym);
00847         l.append(sym);
00848         allsymbols.append(sym);
00849       }
00850       // and add this category, or append to existing category
00851       KLFLatexSymbolsView * view = NULL;
00852       for (j = 0; j < mViews.size(); ++j) {
00853         if (mViews[j]->category() == heading) {
00854           view = mViews[j];
00855           break;
00856         }
00857       }
00858       if (view == NULL) {
00859         // category does not yet exist
00860         view = new KLFLatexSymbolsView(heading, stkViews);
00861         connect(view, SIGNAL(symbolActivated(const KLFLatexSymbol&)),
00862                 this, SIGNAL(insertSymbol(const KLFLatexSymbol&)));
00863         mViews.append(view);
00864         stkViews->addWidget(view);
00865         if (categoryTitle.isEmpty())
00866           categoryTitle = heading;
00867         u->cbxCategory->addItem(categoryTitle, heading);
00868         categoryTitleLangs << curCategoryTitleLang;
00869       } else {
00870         // possibly update the title if a better translation is available
00871         if (!categoryTitle.isEmpty() &&
00872             (categoryTitleLangs[j].isEmpty() || curCategoryTitleLang.startsWith(categoryTitleLangs[j]))) {
00873           // update the title
00874           u->cbxCategory->setItemText(j, categoryTitle);
00875         } else {
00876           // keep old title
00877         }
00878       }
00879 
00880       view->appendSymbolList(l);
00881     } // iterate over categories in XML file
00882   } // iterate over XML files
00883 
00884   // pre-cache all our symbols
00885   KLFLatexSymbolsCache::theCache()->precacheList(allsymbols, true, this);
00886 
00887   int i;
00888   for (i = 0; i < mViews.size(); ++i) {
00889     mViews[i]->buildDisplay();
00890   }
00891 
00892 }
00893 
00894 void KLFLatexSymbols::slotShowCategory(int c)
00895 {
00896   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00897 
00898   // called by combobox
00899   stkViews->setCurrentIndex(c);
00900 
00901   klfDbg("current index="<<c) ;
00902 
00903   QWidget * w = stkViews->currentWidget();
00904   KLFSearchable * target = NULL;
00905   if (w != NULL) {
00906     KLFLatexSymbolsView *view = qobject_cast<KLFLatexSymbolsView*>(w);
00907     if (view != NULL)
00908       target = view;
00909   }
00910   pSearchBar->setSearchTarget(target);
00911 }
00912 
00913 void KLFLatexSymbols::closeEvent(QCloseEvent *e)
00914 {
00915   e->accept();
00916 }
00917 
00918 void KLFLatexSymbols::showEvent(QShowEvent *e)
00919 {
00920   QWidget::showEvent(e);
00921 }
00922 
00923 
00924 bool KLFLatexSymbols::event(QEvent *e)
00925 {
00926   if (e->type() == QEvent::Polish) {
00927     u->cbxCategory->setMinimumHeight(u->cbxCategory->sizeHint().height()+5);
00928   }
00929   if (e->type() == QEvent::KeyPress) {
00930     QKeyEvent *ke = (QKeyEvent*)e;
00931     if (ke->key() == Qt::Key_F7 && ke->modifiers() == 0) {
00932       hide();
00933       e->accept();
00934       return true;
00935     }
00936   }
00937   return QWidget::event(e);
00938 }
00939 

Generated by doxygen 1.7.3