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

src/klflibview.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   file klflibview.cpp
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: klflibview.cpp 580 2010-12-02 18:55:28Z philippe $ */
00023 
00024 
00025 #include <QApplication>
00026 #include <QDebug>
00027 #include <QImage>
00028 #include <QString>
00029 #include <QDataStream>
00030 #include <QMessageBox>
00031 #include <QAbstractItemModel>
00032 #include <QModelIndex>
00033 #include <QPainter>
00034 #include <QStyle>
00035 #include <QVBoxLayout>
00036 #include <QStackedWidget>
00037 #include <QComboBox>
00038 #include <QHeaderView>
00039 #include <QTextDocument>
00040 #include <QTextCursor>
00041 #include <QTextCharFormat>
00042 #include <QListView>
00043 #include <QMenu>
00044 #include <QAction>
00045 #include <QEvent>
00046 #include <QDropEvent>
00047 #include <QDragEnterEvent>
00048 #include <QDragMoveEvent>
00049 #include <QStandardItemModel>
00050 #include <QItemDelegate>
00051 #include <QShortcut>
00052 
00053 #include <ui_klflibopenresourcedlg.h>
00054 #include <ui_klflibrespropeditor.h>
00055 #include <ui_klflibnewsubresdlg.h>
00056 
00057 #include <klfguiutil.h>
00058 #include "klfconfig.h"
00059 #include "klflibview.h"
00060 
00061 #include "klflibview_p.h"
00062 
00063 
00064 
00065 // ---------------------------------------------------
00066 
00068 static QImage transparentify_image(const QImage& img, qreal factor)
00069 {
00070   // set the image opacity to factor (by multiplying each alpha value by factor)
00071   QImage img2 = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
00072   int k, j;
00073   for (k = 0; k < img.height(); ++k) {
00074     for (j = 0; j < img.width(); ++j) {
00075       QRgb c = img2.pixel(j, k);
00076       img2.setPixel(j, k, qRgba(qRed(c),qGreen(c),qBlue(c),(int)(factor*qAlpha(c))));
00077     }
00078   }
00079   return img2;
00080 }
00081 
00082 
00084 static QImage autocrop_image(const QImage& img, int alpha_threshold = 0)
00085 {
00086   // crop transparent borders
00087   int x, y;
00088   int min_x = -1, max_x = -1, min_y = -1, max_y = -1;
00089   // so first find borders
00090   for (x = 0; x < img.width(); ++x) {
00091     for (y = 0; y < img.height(); ++y) {
00092       if (qAlpha(img.pixel(x,y)) - alpha_threshold > 0) {
00093         // opaque pixel: include it.
00094         if (min_x == -1 || min_x > x) min_x = x;
00095         if (max_x == -1 || max_x < x) max_x = x;
00096         if (min_y == -1 || min_y > y) min_y = y;
00097         if (max_y == -1 || max_y < y) max_y = y;
00098       }
00099     }
00100   }
00101   return img.copy(QRect(QPoint(min_x, min_y), QPoint(max_x, max_y)));
00102 }
00103 
00105 static float color_distinguishable_distance(QRgb a, QRgb b, bool aPremultiplied = false) {
00106   static const float C_r = 11.f,   C_g = 16.f,   C_b = 5.f;
00107   static const float C_avg = (C_r + C_g + C_b) / 3.f;
00108 
00109   // (?) really-> NO?  ON SECOND THOUGHT, REMOVE THIS FACTOR
00110   //  // * drkfactor reduces distances for dark colors, accounting for the fact that the eye
00111   //  //   distinguishes less dark colors than light colors
00112   //  // * 0 <= qGray(...) <= 255
00113   //  // * drkfactor <= 1 -> reducing factor
00114   //  float drkfactor = 1 - (qGray(b)/1000.f);
00115   static const float drkfactor = 1;
00116 
00117   float alpha = qAlpha(a)/255.f;
00118   QRgb m;
00119   if (aPremultiplied)
00120     m = qRgb((int)(qRed(a)+(1-alpha)*qRed(b)),
00121              (int)(qGreen(a)+(1-alpha)*qGreen(b)),
00122              (int)(qBlue(a)+(1-alpha)*qBlue(b)));
00123   else
00124     m = qRgb((int)(alpha*qRed(a)+(1-alpha)*qRed(b)),
00125              (int)(alpha*qGreen(a)+(1-alpha)*qGreen(b)),
00126              (int)(alpha*qBlue(a)+(1-alpha)*qBlue(b)));
00127 
00128   float dst = qMax( qMax(C_r*abs(qRed(m) - qRed(b)), C_g*abs(qGreen(m) - qGreen(b))),
00129                     C_b*abs(qBlue(m) - qBlue(b)) ) * drkfactor / C_avg;
00130   // klfDbg("a="<<klfFmt("%#010x", a).data()<<" qRed(a)="<<qRed(a)<<" b="<<klfFmt("%#010x", b).data()
00131   //     <<" m="<<klfFmt("%#010x", m)<<"drkfactor="<<drkfactor<<" a/alpha="<<alpha<<" => distance="<<dst) ;
00132   return dst;
00133 }
00134 
00135 
00137 static bool image_is_distinguishable(const QImage& imgsrc, QColor background, float threshold)
00138 {
00139   int fmt = imgsrc.format();
00140   bool apremultiplied;
00141   QImage img;
00142   switch (fmt) {
00143   case QImage::Format_ARGB32: img = imgsrc; apremultiplied = false; break;
00144   case QImage::Format_ARGB32_Premultiplied: img = imgsrc; apremultiplied = true; break;
00145   default:
00146    img = imgsrc.convertToFormat(QImage::Format_ARGB32);
00147    apremultiplied = false;
00148    break;
00149   }
00150   QRgb bg = background.rgb();
00151   // crop transparent borders
00152   int x, y;
00153   for (x = 0; x < img.width(); ++x) {
00154     for (y = 0; y < img.height(); ++y) {
00155       //      klfDbg("src/format="<<imgsrc.format()<<"; thisformat="<<img.format()
00156       //             <<" Testing pixel at ("<<x<<","<<y<<") pixel="<<klfFmt("%#010x", img.pixel(x,y))) ;
00157       float dist = color_distinguishable_distance(img.pixel(x,y), bg, apremultiplied);
00158       if (dist > threshold) {
00159         // ok we have one pixel at least we can distinguish.
00160         return true;
00161       }
00162     }
00163   }
00164   return false;
00165 }
00166 
00167 
00168 
00169 // -------------------------------------------------------
00170 
00171 KLFAbstractLibView::KLFAbstractLibView(QWidget *parent)
00172   : QWidget(parent), pResourceEngine(NULL)
00173 {
00174 }
00175 
00176 void KLFAbstractLibView::setResourceEngine(KLFLibResourceEngine *resource)
00177 {
00178   if (pResourceEngine)
00179     disconnect(pResourceEngine, 0, this, 0);
00180   pResourceEngine = resource;
00181   connect(pResourceEngine, SIGNAL(dataChanged(const QString&, int, const QList<KLFLib::entryId>&)),
00182           this, SLOT(updateResourceData(const QString&, int, const QList<KLFLib::entryId>&)));
00183   connect(pResourceEngine, SIGNAL(resourcePropertyChanged(int)),
00184           this, SLOT(updateResourceProp(int)));
00185   connect(pResourceEngine, SIGNAL(defaultSubResourceChanged(const QString&)),
00186           this, SLOT(updateResourceDefaultSubResourceChanged(const QString&)));
00187   updateResourceEngine();
00188 }
00189 
00190 void KLFAbstractLibView::updateResourceDefaultSubResourceChanged(const QString& /*subResource*/)
00191 {
00192   updateResourceEngine();
00193 }
00194 
00195 void KLFAbstractLibView::wantMoreCategorySuggestions()
00196 {
00197   emit moreCategorySuggestions(getCategorySuggestions());
00198 }
00199 
00200 QList<QAction*> KLFAbstractLibView::addContextMenuActions(const QPoint&)
00201 {
00202   return QList<QAction*>();
00203 }
00204 
00205 
00206 
00207 // -------------------------------------------------------
00208 
00209 QList<KLFLibViewFactory*> KLFLibViewFactory::pRegisteredFactories =
00210          QList<KLFLibViewFactory*>();
00211 
00212 
00213 KLFLibViewFactory::KLFLibViewFactory(const QStringList& viewTypeIdentifiers,
00214                                                      QObject *parent)
00215   : QObject(parent), pViewTypeIdentifiers(viewTypeIdentifiers)
00216 {
00217   registerFactory(this);
00218 }
00219 KLFLibViewFactory::~KLFLibViewFactory()
00220 {
00221   unRegisterFactory(this);
00222 }
00223 
00224 
00225 QString KLFLibViewFactory::defaultViewTypeIdentifier()
00226 {
00227   if (pRegisteredFactories.size() > 0)
00228     return pRegisteredFactories[0]->pViewTypeIdentifiers.first();
00229   return QString();
00230 }
00231 
00232 KLFLibViewFactory *KLFLibViewFactory::findFactoryFor(const QString& viewTypeIdentifier)
00233 {
00234   if (viewTypeIdentifier.isEmpty()) {
00235     if (pRegisteredFactories.size() > 0)
00236       return pRegisteredFactories[0]; // first registered factory is default
00237     return NULL;
00238   }
00239   int k;
00240   // walk registered factories, and return the first that supports this scheme.
00241   for (k = 0; k < pRegisteredFactories.size(); ++k) {
00242     if (pRegisteredFactories[k]->viewTypeIdentifiers().contains(viewTypeIdentifier))
00243       return pRegisteredFactories[k];
00244   }
00245   // no factory found
00246   return NULL;
00247 }
00248 
00249 QStringList KLFLibViewFactory::allSupportedViewTypeIdentifiers()
00250 {
00251   QStringList list;
00252   int k;
00253   for (k = 0; k < pRegisteredFactories.size(); ++k)
00254     list << pRegisteredFactories[k]->viewTypeIdentifiers();
00255   return list;
00256 }
00257 
00258 
00259 void KLFLibViewFactory::registerFactory(KLFLibViewFactory *factory)
00260 {
00261   KLF_ASSERT_NOT_NULL( factory, "Attempt to register NULL factory!", return )
00262     ;
00263   // WARNING: THIS FUNCTION IS CALLED FROM CONSTRUCTOR. NO VIRTUAL METHOD CALLS!
00264   if (factory->pViewTypeIdentifiers.size() == 0) {
00265     qWarning("KLFLibViewFactory::registerFactory: factory must provide at least one view type!");
00266     return;
00267   }
00268   if (pRegisteredFactories.indexOf(factory) != -1) // already registered
00269     return;
00270   pRegisteredFactories.append(factory);
00271 }
00272 
00273 void KLFLibViewFactory::unRegisterFactory(KLFLibViewFactory *factory)
00274 {
00275   if (pRegisteredFactories.indexOf(factory) == -1)
00276     return;
00277   pRegisteredFactories.removeAll(factory);
00278 }
00279 
00280 
00281 
00282 // ---------------------------------------------------
00283 
00284 
00285 // static
00286 KLFFactoryManager KLFLibWidgetFactory::pFactoryManager;
00287 
00288 KLFLibWidgetFactory::KLFLibWidgetFactory(QObject *parent)
00289   : QObject(parent), KLFFactoryBase(&pFactoryManager)
00290 {
00291 }
00292 
00293 // static
00294 KLFLibWidgetFactory *KLFLibWidgetFactory::findFactoryFor(const QString& wtype)
00295 {
00296   return dynamic_cast<KLFLibWidgetFactory*>(pFactoryManager.findFactoryFor(wtype));
00297 }
00298 
00299 // static
00300 QStringList KLFLibWidgetFactory::allSupportedWTypes()
00301 {
00302   return pFactoryManager.allSupportedTypes();
00303 }
00304 
00305 
00306 bool KLFLibWidgetFactory::hasCreateWidget(const QString& /*scheme*/) const
00307 {
00308   return false;
00309 }
00310 
00311 QWidget * KLFLibWidgetFactory::createPromptCreateParametersWidget(QWidget */*parent*/,
00312                                                                           const QString& /*scheme*/,
00313                                                                           const Parameters& /*par*/)
00314 {
00315   return NULL;
00316 }
00317 KLFLibWidgetFactory::Parameters
00318 /* */ KLFLibWidgetFactory::retrieveCreateParametersFromWidget(const QString& /*scheme*/,
00319                                                                       QWidget */*parent*/)
00320 {
00321   return Parameters();
00322 }
00323 
00324 bool KLFLibWidgetFactory::hasSaveToWidget(const QString& /*scheme*/) const
00325 {
00326   return false;
00327 }
00328 QWidget *KLFLibWidgetFactory::createPromptSaveToWidget(QWidget */*parent*/,
00329                                                        const QString& /*scheme*/,
00330                                                        KLFLibResourceEngine* /*resource*/,
00331                                                        const QUrl& /*defaultUrl*/)
00332 {
00333   return NULL;
00334 }
00335 QUrl KLFLibWidgetFactory::retrieveSaveToUrlFromWidget(const QString& /*scheme*/,
00336                                                       QWidget */*widget*/)
00337 {
00338   return QUrl();
00339 }
00340 
00341 
00342 
00343 // --------------------------------------------
00344 
00345 
00346 KLF_EXPORT  QDebug& operator<<(QDebug& dbg, const KLFLibModelCache::NodeId& n)
00347 {
00348   const char * skind =
00349     ( (n.kind == KLFLibModelCache::EntryKind) ? "EntryKind" :
00350       ((n.kind == KLFLibModelCache::CategoryLabelKind) ? "CategoryLabelKind" :
00351        "*UnknownKind*") );
00352   return dbg.nospace() << "NodeId("<<skind<<"+"<<n.index<<")";
00353 }
00354 KLF_EXPORT  QDebug& operator<<(QDebug& dbg, const KLFLibModelCache::Node& n)
00355 {
00356   return dbg << "[kind="<<n.kind<<", children/sz="<<n.children.size()
00357              <<",allfetched="<<n.allChildrenFetched<<"]";
00358 }
00359 KLF_EXPORT  QDebug& operator<<(QDebug& dbg, const KLFLibModelCache::EntryNode& en)
00360 {
00361   return dbg << "EntryNode(entryid="<<en.entryid<<"; entry/latex="<<en.entry.latex()<<"; parent="
00362              <<en.parent<<")";
00363 }
00364 KLF_EXPORT  QDebug& operator<<(QDebug& dbg, const KLFLibModelCache::CategoryLabelNode& cn)
00365 {
00366   return dbg << "CategoryLabelNode(label="<<cn.categoryLabel<<";fullCategoryPath="<<cn.fullCategoryPath
00367              << "; parent="<<cn.parent<<";"<<(const KLFLibModelCache::Node&)cn<<")";
00368 }
00369 KLF_EXPORT  QDebug& operator<<(QDebug& dbg, const KLFLibModel::PersistentId& n)
00370 {
00371   return dbg << "PersistentId("<<n.kind<<", entry_id="<<n.entry_id<<", cat...path="<<n.categorylabel_fullpath<<")";
00372 }
00373 KLF_EXPORT QDebug& operator<<(QDebug& d, const KLFLibViewDelegate::ColorRegion& c)
00374 {
00375   return d << "ColorRegion["<<c.start<<"->+"<<c.len<<"]";
00376 }
00377 
00378 
00379 
00380 // -------------------------------------------------------
00381 
00382 //   MODEL CACHE OBJECT
00383 
00384 
00385 
00386 
00387 bool KLFLibModelCache::KLFLibModelSorter::operator()(const NodeId& a, const NodeId& b)
00388 {
00389   if ( ! a.valid() || ! b.valid() )
00390     return false;
00391 
00392   if ( groupCategories ) {
00393     // category kind always smaller than entry kind in category grouping mode
00394     if ( a.kind != EntryKind && b.kind == EntryKind ) {
00395       return true;
00396     } else if ( a.kind == EntryKind && b.kind != EntryKind ) {
00397       return false;
00398     }
00399     if ( a.kind != EntryKind && b.kind != EntryKind ) {
00400       if ( ! (a.kind == CategoryLabelKind && b.kind == CategoryLabelKind) ) {
00401         qWarning("KLFLibModelSorter::operator(): Bad node kinds!! a: %d and b: %d",
00402                  a.kind, b.kind);
00403         return false;
00404       }
00405       // when grouping sub-categories, always sort the categories *ascending*
00406       return QString::localeAwareCompare(cache->getCategoryLabelNodeRef(a).categoryLabel,
00407                                          cache->getCategoryLabelNodeRef(b).categoryLabel)  < 0;
00408     }
00409     // both are entrykind
00410     return entrysorter->operator()(cache->getEntryNodeRef(a).entry, cache->getEntryNodeRef(b).entry);
00411   }
00412 
00413   int entryProp = entrysorter->propId();
00414   int sortorderfactor = (entrysorter->order() == Qt::AscendingOrder) ? 1 : -1;
00415   QString nodevaluea = cache->nodeValue(a, entryProp);
00416   QString nodevalueb = cache->nodeValue(b, entryProp);
00417   return sortorderfactor*QString::localeAwareCompare(nodevaluea, nodevalueb) < 0;
00418 }
00419 
00420 
00421 
00422 // ---
00423 
00424 void KLFLibModelCache::rebuildCache()
00425 {
00426   uint flavorFlags = pModel->pFlavorFlags;
00427 
00428   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00429   klfDbg(klfFmtCC("flavorFlags=%#010x", flavorFlags));
00430   int k;
00431 
00432   // report progress
00433 #ifndef Q_WS_MAC
00434   KLFProgressReporter progressReporter(0, 100, NULL);
00435   QString msg = QObject::tr("Updating View...", "[[KLFLibModelCache, progress text]]");
00436   emit pModel->operationStartReportingProgress(&progressReporter, msg);
00437   progressReporter.doReportProgress(0);
00438 #endif
00439 
00440   klfDbgT("saving persistent indexes ...");
00441   QModelIndexList persistentIndexes = pModel->persistentIndexList();
00442   QList<KLFLibModel::PersistentId> persistentIndexIds = pModel->persistentIdList(persistentIndexes);
00443   klfDbgT("... done saving persistent indexes.");
00444 
00445   // clear cache first
00446   pEntryCache.clear();
00447   pCategoryLabelCache.clear();
00448   // root category label MUST ALWAYS (in every display flavor) occupy index 0 in category label cache
00449   pCategoryLabelCache.append(CategoryLabelNode());
00450   CategoryLabelNode& root = pCategoryLabelCache[0];
00451   root.fullCategoryPath = "/";
00452   root.categoryLabel = "/";
00453   root.allChildrenFetched = false;
00454 
00455   QList<int> wantedProps = minimalistEntryPropIds();
00456   KLFLibResourceEngine::Query q;
00457   q.orderPropId = pLastSortPropId;
00458   q.orderDirection = pLastSortOrder;
00459   if (pModel->pFlavorFlags & KLFLibModel::CategoryTree) {
00460     // fetch only elements in root category
00461     KLFLib::PropertyMatch pmatch(KLFLibEntry::Category, KLFLib::StringMatch(QString("")));
00462     q.matchCondition = KLFLib::EntryMatchCondition::mkPropertyMatch(pmatch);
00463   }
00464   q.wantedEntryProperties = minimalistEntryPropIds();
00465   q.limit = pModel->pFetchBatchCount; // limit number of results
00466   KLFLibResourceEngine::QueryResult qr(KLFLibResourceEngine::QueryResult::FillEntryWithIdList);
00467   // query the resource
00468   klfDbgT("about to query resource...");
00469   int count = pModel->pResource->query(pModel->pResource->defaultSubResource(), q, &qr);
00470   klfDbgT("resource returned "<<count<<" entries.");
00471   if (count < 0) {
00472     qWarning()<<KLF_FUNC_NAME<<": query() returned an error.";
00473     // don't return, continue with empty list
00474   }
00475   if (count < pModel->pFetchBatchCount) {
00476     // we have fetched all children
00477     klfDbg("all children have been fetched.") ;
00478     root.allChildrenFetched = true;
00479   }
00480   const QList<KLFLibResourceEngine::KLFLibEntryWithId>& everything = qr.entryWithIdList;
00481   QList<KLFLibResourceEngine::KLFLibEntryWithId>::const_iterator it;
00482 
00483   k = 0; // used for progress reporting
00484   for (it = everything.begin(); it != everything.end(); ++it) {
00485     const KLFLibResourceEngine::KLFLibEntryWithId& ewid = *it;
00486     klfDbgT( "Adding entry id="<<ewid.id<<"; entry="<<ewid.entry ) ;
00487     EntryNode e;
00488     e.entryid = ewid.id;
00489     e.minimalist = true;
00490     e.entry = ewid.entry;
00491     treeInsertEntry(e, true); // rebuildingCache=TRUE
00492 
00493 #ifndef Q_WS_MAC
00494     if (k % 10 == 0)
00495       progressReporter.doReportProgress((++k) * 100 / everything.size());
00496 #endif
00497   }
00498 
00499   if (pModel->pFlavorFlags & KLFLibModel::CategoryTree) {
00500     // now fetch all categories, and insert them
00501     klfDbgT("About to query categories...");
00502     QVariantList vcatlist = pModel->pResource->queryValues(pModel->pResource->defaultSubResource(),
00503                                                            KLFLibEntry::Category);
00504     klfDbgT("... got categories. inserting them ...");
00505     for (QVariantList::const_iterator vcit = vcatlist.begin(); vcit != vcatlist.end(); ++vcit) {
00506       QString cat = (*vcit).toString();
00507       if (cat.isEmpty() || cat == "/")
00508         continue;
00509       // cacheFindCategoryLabel(categoryelements, createIfNotExists, notifyQtApi, newCreatedAreChildrenFetched)
00510       QStringList catelements = cat.split('/', QString::SkipEmptyParts);
00511       int i = cacheFindCategoryLabel(catelements, true, false, false);
00512       if (i < 0) {
00513         qWarning()<<KLF_FUNC_NAME<<": Failed to create category node for category "<<cat;
00514       }
00515       // remember this category as suggestion
00516       insertCategoryStringInSuggestionCache(catelements);
00517     }
00518     klfDbgT("... ins catnodes done.") ;
00519   }
00520 
00521   fullDump(); // DEBUG
00522 
00523   emit pModel->reset();
00524 
00525   klfDbg("restoring persistent indexes ...");
00526   QModelIndexList newPersistentIndexes = pModel->newPersistentIndexList(persistentIndexIds);
00527   // and refresh all persistent indexes
00528   pModel->changePersistentIndexList(persistentIndexes, newPersistentIndexes);
00529   klfDbg("... done restoring persistent indexes.");
00530 
00531   klfDbgT( " end of func" ) ;
00532 }
00533 
00534 
00535 QModelIndex KLFLibModelCache::createIndexFromId(NodeId nodeid, int row, int column)
00536 {
00537   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00538 
00539   if ( ! nodeid.valid() || nodeid == NodeId::rootNode())
00540     return QModelIndex();
00541 
00542   // if row is -1, then we need to find the row of the item
00543   if ( row < 0 ) {
00544     row = getNodeRow(nodeid);
00545   }
00546 
00547   // make sure all elements have been "fetched" up to this row
00548 
00549   klfDbg( ": nodeid="<<nodeid<<"; row="<<row<<"; col="<<column ) ;
00550 
00551   // get the parent node
00552   Node node = getNode(nodeid);
00553   NodeId parentid = node.parent;
00554   if (!parentid.valid())
00555     parentid = NodeId::rootNode();
00556 
00557   // if we cache getNode(parentid) make sure to keep a reference! it changes!
00558   klfDbg("row="<<row) ;
00559   while ( getNode(parentid).children.size() <= row && canFetchMore(parentid) ) {
00560     klfDbgT(": need to fetch more children/size="
00561             <<getNode(parentid).children.size()<<"<row="<<row<<" !");
00562     fetchMore(parentid);
00563   }
00564 
00565   // create & return the index
00566   return pModel->createIndex(row, column, nodeid.universalId());
00567 }
00568 
00569 
00570 KLFLibModelCache::NodeId KLFLibModelCache::getNodeForIndex(const QModelIndex& index)
00571 {
00572   if ( ! index.isValid() )
00573     return NodeId();
00574   NodeId n = NodeId::fromUID(index.internalId());
00575   // perform validity check on 'n'
00576   if (n.kind == EntryKind) {
00577     if (n.index < 0 || n.index >= pEntryCache.size()) {
00578       qWarning()<<KLF_FUNC_NAME<<": Invalid entry node reference: "<<n;
00579       return NodeId();
00580     }
00581   } else if (n.kind == CategoryLabelKind) {
00582     if (n.index < 0 || n.index >= pCategoryLabelCache.size()) {
00583       qWarning()<<KLF_FUNC_NAME<<": Invalid category label node reference: "<<n;
00584       return NodeId();
00585     }
00586   } else {
00587     qWarning()<<KLF_FUNC_NAME<<": Invalid node kind: "<<n;
00588     return NodeId();
00589   }
00590   return n;
00591 }
00592 
00593 KLFLibModelCache::Node KLFLibModelCache::getNode(NodeId nodeid)
00594 {
00595   if (!nodeid.valid())
00596     return CategoryLabelNode();
00597   Node &n = getNodeRef(nodeid);
00598   return n;
00599 }
00600 KLFLibModelCache::Node& KLFLibModelCache::getNodeRef(NodeId nodeid)
00601 {
00602   if (!nodeid.valid()) {
00603     qWarning()<<"KLFLibModelCache::getNodeRef: Invalid Node Id: "<<nodeid;
00604     return pInvalidEntryNode;
00605   }
00606   if (nodeid.kind == EntryKind) {
00607     if (nodeid.index < 0 || nodeid.index >= pEntryCache.size()) {
00608       qWarning()<<"KLFLibModelCache::getNodeRef: Invalid Entry Node Id: "<<nodeid<<" : index out of range!";
00609       return pInvalidEntryNode;
00610     }
00611     return pEntryCache[nodeid.index];
00612   } else if (nodeid.kind == CategoryLabelKind) {
00613     if (nodeid.index < 0 || nodeid.index >= pCategoryLabelCache.size()) {
00614       qWarning()<<"KLFLibModelCache::getNodeRef: Invalid Category Label Node Id: "<<nodeid
00615                 <<" : index out of range!";
00616       return pInvalidEntryNode;
00617     }
00618     return pCategoryLabelCache[nodeid.index];
00619   }
00620   qWarning("KLFLibModelCache::getNodeRef(): Invalid kind: %d (index=%d)\n", nodeid.kind, nodeid.index);
00621   return pInvalidEntryNode;
00622 }
00623 KLFLibModelCache::EntryNode& KLFLibModelCache::getEntryNodeRef(NodeId nodeid, bool enforceNotMinimalist)
00624 {
00625   static EntryNode dummyerrornode;
00626   if (!nodeid.valid() || nodeid.kind != EntryKind ||
00627       nodeid.index < 0 || nodeid.index >= pEntryCache.size()) {
00628     qWarning()<<"KLFLibModelCache::getEntryNodeRef: Invalid Entry Node "<<nodeid<<"!";
00629     return dummyerrornode;
00630   }
00631   if (enforceNotMinimalist && pEntryCache[nodeid.index].minimalist)
00632     ensureNotMinimalist(nodeid);
00633 
00634   return pEntryCache[nodeid.index];
00635 }
00636 
00637 KLFLibModelCache::CategoryLabelNode& KLFLibModelCache::getCategoryLabelNodeRef(NodeId nodeid)
00638 {
00639   if (!nodeid.valid() || nodeid.kind != CategoryLabelKind ||
00640       nodeid.index < 0 || nodeid.index >= pCategoryLabelCache.size()) {
00641     qWarning()<<"KLFLibModelCache::getCat.LabelNode: Invalid Category Label Node "<<nodeid<<"!";
00642     return pCategoryLabelCache[0];
00643   }
00644   return pCategoryLabelCache[nodeid.index];
00645 }
00646 
00647 int KLFLibModelCache::getNodeRow(NodeId node)
00648 {
00649   if ( ! node.valid() )
00650     return -1;
00651   if ( node == NodeId::rootNode() )
00652     return 0;
00653 
00654   Node n = getNode(node);
00655 
00656   NodeId pparentid = n.parent;
00657   if ( !pparentid.valid() ) {
00658     // shouldn't happen (!?!), only parentless item should be root node !
00659     qWarning()<<KLF_FUNC_NAME<<": Found parentless non-root node: "<<node;
00660     return 0;
00661   }
00662   Node pparent = getNode(pparentid);
00663   // find child in parent
00664   int k;
00665   for (k = 0; k < pparent.children.size(); ++k)
00666     if (pparent.children[k] == node)
00667       return k;
00668 
00669   // search failed
00670   qWarning()<<KLF_FUNC_NAME<<": Unable to get node row: parent-child one-way broken!! node="<<node;
00671   return -1;
00672 }
00673 
00674 void KLFLibModelCache::ensureNotMinimalist(NodeId p, int countdown)
00675 {
00676   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00677   // fetch & complete some minimalist entry(ies)
00678   NodeId n;
00679   // prepare some entry IDs to fetch
00680   QMap<KLFLib::entryId, NodeId> wantedIds;
00681   if (countdown < 0)
00682     countdown = pModel->pFetchBatchCount;
00683   //  n = prevNode(p);
00684   //  if (!n.valid())
00685   n = p;
00686   for (; n.valid() && countdown-- > 0; n = nextNode(n)) {
00687     if (n.kind == CategoryLabelKind) {
00688       ++countdown; // don't count category labels
00689       continue;
00690     }
00691     if (n.kind == EntryKind) {
00692       EntryNode en = getEntryNodeRef(n);
00693       if (en.minimalist) // if this entry is "minimalist", update it
00694         wantedIds[en.entryid] = n;
00695       continue;
00696     }
00697     qWarning()<<KLF_FUNC_NAME<<": Got unknown kind="<<n.kind;
00698   }
00699   // fetch the required entries
00700   QList<KLFLibResourceEngine::KLFLibEntryWithId> updatedentries =
00701     pModel->pResource->entries(wantedIds.keys(), QList<int>()); // fetch all properties
00702   int k;
00703   for (k = 0; k < updatedentries.size(); ++k) {
00704     KLFLib::entryId eid = updatedentries[k].id;
00705     if ( ! wantedIds.contains(eid) ) {
00706       qWarning()<<KLF_FUNC_NAME<<" got unrequested updated entry ID ?! id="<<eid;
00707       continue;
00708     }
00709     NodeId nid = wantedIds[eid];
00710     pEntryCache[nid.index].entry = updatedentries[k].entry;
00711     pEntryCache[nid.index].minimalist = false;
00712   }
00713   klfDbg( ": updated entries "<<wantedIds.keys() ) ;
00714 }
00715 
00716 bool KLFLibModelCache::canFetchMore(NodeId parentId)
00717 {
00718   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00719   if (pIsFetchingMore)
00720     return false;
00721 
00722   if (!parentId.valid())
00723     parentId = NodeId::rootNode();
00724 
00725   klfDbg("parentId="<<parentId) ;
00726 
00727   const Node& node = getNodeRef(parentId);
00728 
00729   klfDbg("node="<<node) ;
00730 
00731   if (!node.allChildrenFetched)
00732     return true;
00733 
00734   klfDbg("cannot fetchmore.") ;
00735   return false;
00736 }
00737 void KLFLibModelCache::fetchMore(NodeId n, int fetchBatchCount)
00738 {
00739   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME);
00740   klfDbg( "\t parentId: n="<<n<<"; valid="<<n.valid() <<"; url="<<pModel->url() ) ;
00741 
00742   if (fetchBatchCount < 0) // set default value
00743     fetchBatchCount = pModel->pFetchBatchCount;
00744 
00745   if (pIsFetchingMore)
00746     return;
00747   pIsFetchingMore = true;
00748 
00749   // see function doxygen doc for nIndex param info.
00750 
00751   if (!n.valid())
00752     n = NodeId::rootNode();
00753 
00754   if (n.kind != CategoryLabelKind) {
00755     klfDbg("can't fetch more in this node kind. n="<<n);
00756     qWarning()<<KLF_FUNC_NAME<<": Can't fetch more children of a non-category-label node.";
00757     return;
00758   }
00759 
00760   CategoryLabelNode& noderef = getCategoryLabelNodeRef(n);
00761   klfDbg( "\t -> n="<<n<<"; noderef: kind="<<noderef.kind<<", allChildrenFetched="<<noderef.allChildrenFetched ) ;
00762 
00763   if (noderef.allChildrenFetched) {
00764     // all children have been fetched, cannot do anything more.
00765     klfDbg("can't fetch more: all children are fetched! noderef="<<noderef<<"; n (the id)="<<n) ;
00766     //    qWarning()<<KLF_FUNC_NAME<<": can't fetch any more items!";
00767     pIsFetchingMore = false;
00768     return;
00769   }
00770 
00771   // fetch more items, using query().
00772   KLFLibResourceEngine::Query q;
00773   if (pModel->pFlavorFlags & KLFLibModel::CategoryTree) {
00774     QString c = KLFLibEntry::normalizeCategoryPath(noderef.fullCategoryPath);
00775     KLFLib::PropertyMatch pmatch(KLFLibEntry::Category, KLFLib::StringMatch(c));
00776     q.matchCondition = KLFLib::EntryMatchCondition::mkPropertyMatch(pmatch);
00777   }
00778   q.orderPropId = pLastSortPropId;
00779   q.orderDirection = pLastSortOrder;
00780   q.limit = pModel->pFetchBatchCount;
00781   q.skip = noderef.children.size();
00782   q.wantedEntryProperties = minimalistEntryPropIds();
00783   KLFLibResourceEngine::QueryResult qr(KLFLibResourceEngine::QueryResult::FillEntryWithIdList);
00784   // _query()_ the resource
00785   int count = pModel->pResource->query(pModel->pResource->defaultSubResource(), q, &qr);
00786   if (count < 0) {
00787     qWarning()<<KLF_FUNC_NAME<<": error fetching more results: count is "<<count;
00788     pIsFetchingMore = false;
00789     return;
00790   }
00791 
00798   // append all results into category-label-noderef 'noderef'
00799 
00800   // notify any views
00801   pModel->startLayoutChange(false);
00802   pModel->beginInsertRows(createIndexFromId(n, -1, 0), noderef.children.size(),
00803                           noderef.children.size() + qr.entryWithIdList.size()-1);
00804 
00805   // if we fetched all the remaining entries, then set allChildrenFetched to TRUE
00806   if (count < q.limit) {
00807     noderef.allChildrenFetched = true;
00808   }
00809 
00810   int k;
00811   for (k = 0; k < qr.entryWithIdList.size(); ++k) {
00812     const KLFLibResourceEngine::KLFLibEntryWithId& ewid = qr.entryWithIdList[k];
00813     EntryNode e;
00814     e.entryid = ewid.id;
00815     e.minimalist = true;
00816     e.entry = ewid.entry;
00817     e.parent = n;
00818     pEntryCache.append(e);
00819     NodeId entryindex;
00820     entryindex.kind = EntryKind;
00821     entryindex.index = pEntryCache.size()-1;
00822 
00823     klfDbg("appending "<<e<<" in category node.") ;
00824 
00825     noderef.children.append(entryindex);
00826   }
00827 
00828   klfDbg("Fetched more. About to notify view of end of rows inserted ... meanwile the dump:") ;
00829   fullDump();
00830 
00831   pModel->endInsertRows();
00832   klfDbg("signal emitted. restore persistent indexes...") ;
00833   pModel->endLayoutChange(false);
00834 
00835   klfDbg("views notified, persistent indexes restored.") ;
00836 
00837   pIsFetchingMore = false;
00838 }
00839 
00840 
00841 
00842 void KLFLibModelCache::updateData(const QList<KLFLib::entryId>& entryIdList, int modifyType)
00843 {
00844   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00845   klfDbg( "modifyType="<<modifyType<<" entryIdList="<<entryIdList ) ;
00846 
00847   if (modifyType == KLFLibResourceEngine::UnknownModification) {
00848     klfDbg("Performing full refresh.") ;
00849     rebuildCache();
00850     return;
00851   }
00852 
00853   if (entryIdList.size() > 10 &&
00854       (entryIdList.size() > pEntryCache.size()/3 || entryIdList.size() > 100)) {
00855     // too big a modification, just rebuild the cache
00856     klfDbg("Performing full refresh.") ;
00857     rebuildCache();
00858     return;
00859   }
00860 
00861 #ifndef Q_WS_MAC
00862   // progress reporting [here, not above, because rebuildCache() has its own progress reporting]
00863   KLFProgressReporter progressReporter(0, entryIdList.size(), NULL);
00864   emit pModel->operationStartReportingProgress(&progressReporter,
00865                                                QObject::tr("Updating View...", "[[KLFLibModelCache, progress text]]"));
00866   progressReporter.doReportProgress(0);
00867 #endif
00868 
00869   switch (modifyType) {
00870   case KLFLibResourceEngine::InsertData:
00871     {   // entries inserted
00872       QList<KLFLibResourceEngine::KLFLibEntryWithId> entryList = pModel->pResource->entries(entryIdList);
00873       int k;
00874       for (k = 0; k < entryList.size(); ++k) {
00875         EntryNode en;
00876         en.entryid = entryList[k].id;
00877         en.minimalist = false;
00878         en.entry = entryList[k].entry;
00881         treeInsertEntry(en);
00882         qDebug("%s: entry ID %d inserted", KLF_FUNC_NAME, entryIdList[k]);
00883 #ifndef Q_WS_MAC
00884         if (k % 20 == 0)
00885           progressReporter.doReportProgress(k+1);
00886 #endif
00887       }
00888       break;
00889     }
00890   case KLFLibResourceEngine::ChangeData:
00891     { // entry moved in category tree or just changed
00892       QList<KLFLibResourceEngine::KLFLibEntryWithId> entryList = pModel->pResource->entries(entryIdList);
00893       int k;
00894       for (k = 0; k < entryIdList.size(); ++k) {
00895         klfDbg("modifying entry ID="<<entryIdList[k]<<", modif."<<k) ;
00896         NodeId n = findEntryId(entryIdList[k]);
00897         if (!n.valid()) {
00898           qWarning()<<KLF_FUNC_NAME<<": n is invalid! (kind="<<n.kind<<", index="<<n.index<<")";
00899           continue;
00900         }
00901         KLFLibEntry oldentry = pEntryCache[n.index].entry;
00902         KLFLibEntry newentry = entryList[k].entry;
00903         klfDbg("entry change: old="<<oldentry<<"; new="<<newentry) ;
00904         // the modified entry may have a different category, move it if needed
00905         if (newentry.category() != oldentry.category() && (pModel->pFlavorFlags & KLFLibModel::CategoryTree)) {
00906           pModel->startLayoutChange(false);
00907           EntryNode entrynode = treeTakeEntry(n, true); // remove it from its position in tree
00908           // klfDbg( "\tremoved entry. dump:\n"
00909           //         <<"\t Entry Cache="<<pEntryCache<<"\n\t CategoryLabelCache = "<<pCategoryLabelCache ) ;
00910           // dumpNodeTree(NodeId::rootNode());
00911           // revalidate the removed entry
00912           entrynode.entryid = entryIdList[k];
00913           entrynode.entry = newentry;
00914           // and insert it at the (new) correct position (automatically positioned!)
00915           treeInsertEntry(entrynode);
00916           pModel->endLayoutChange(false);
00917           QModelIndex idx = createIndexFromId(n, -1, 0);
00918           emit pModel->dataChanged(idx, idx);
00919         } else {
00920           // just some data change
00921           pEntryCache[n.index].entry = newentry;
00922           QModelIndex idx = createIndexFromId(n, -1, 0);
00923           emit pModel->dataChanged(idx, idx);
00924         }
00925 #ifndef Q_WS_MAC
00926         if (k % 20 == 0)
00927           progressReporter.doReportProgress(k+1);
00928 #endif
00929       }
00930       break;
00931     }
00932   case KLFLibResourceEngine::DeleteData:
00933     { // entry removed
00934       int k;
00935       for (k = 0; k < entryIdList.size(); ++k) {
00936         qDebug("%s: deleting entry ID=%d.", KLF_FUNC_NAME, entryIdList[k]);
00937         // entry deleted
00938         NodeId n = findEntryId(entryIdList[k]);
00939         if (!n.valid()) {
00940           qWarning()<<KLF_FUNC_NAME<<": n not valid! n=(kind="<<n.kind<<", index="<<n.index<<")";
00941           continue;
00942         }
00943         (void) treeTakeEntry(n);
00944 #ifndef Q_WS_MAC
00945         if (k % 20 == 0)
00946           progressReporter.doReportProgress(k+1);
00947 #endif
00948       }
00949       break;
00950     }
00951   default:
00952     {
00953       qWarning()<<KLF_FUNC_NAME<<": Bad modify-type parameter: "<<modifyType;
00954       rebuildCache();
00955       return;
00956     }
00957   }
00958 
00959   //  updateCacheSetupModel();
00960   klfDbg( ": udpated; full tree dump:" ) ;
00961   fullDump();
00962 }
00963 
00964 
00965 void KLFLibModelCache::treeInsertEntry(const EntryNode& entrynode, bool rebuildingcache)
00966 {
00967   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
00968 
00969   bool notifyQtApi = !rebuildingcache;
00970   klfDbg( "entrynode="<<entrynode<<",notifyQtApi="<<notifyQtApi<<"" ) ;
00971 
00972   // first find the appropriate category label parent
00973 
00974   // start by looking at category
00975 
00976   QString category = entrynode.entry.category();
00977   QStringList catelements = category.split('/', QString::SkipEmptyParts);
00978   insertCategoryStringInSuggestionCache(catelements);
00979 
00980   // and find the category parent
00981 
00982   IndexType catindex;
00983   if (pModel->displayType() == KLFLibModel::LinearList) {
00984     // add as child of root element
00985     catindex = 0;
00986   } else if (pModel->displayType() == KLFLibModel::CategoryTree) {
00987     // find/create category label node (creating the full tree if needed)
00988     catindex = cacheFindCategoryLabel(catelements, true, notifyQtApi, rebuildingcache?false:true);
00989   } else {
00990     qWarning("Flavor Flags have unknown display type! flavorFlags=%#010x", pModel->pFlavorFlags);
00991     catindex = 0;
00992   }
00993 
00994   NodeId parentid = NodeId(CategoryLabelKind, catindex);
00995 
00996   // now actually create the entry cache node
00997   int index = pEntryCache.insertNewNode(entrynode);
00998   NodeId n = NodeId(EntryKind, index);
00999   // invalidate the node until the insert process is finished.
01000   // the (invalid but with correct info) entry needs to reside in the cache for qLowerBound() below.
01001   pEntryCache[index].parent = NodeId();
01002 
01003   // now we determined the parent of the new entry in the category tree, we will actually
01004   // insert the item according to current sort instructions.
01005   Node& parentref = getNodeRef(parentid);
01006   QList<NodeId> & childlistref = parentref.children;
01007   int insertPos;
01008   if (rebuildingcache || pLastSortPropId < 0) {
01009     insertPos = childlistref.size(); // no sorting, just append the item
01010   } else {
01011     KLFLibModelSorter srt =
01012       KLFLibModelSorter(this, pModel->pEntrySorter, pModel->pFlavorFlags & KLFLibModel::GroupSubCategories);
01013     // Note: as long as we are instructed to insert the new item at the end, we need to fetchMore() to ensure
01014     // that all elements logically appearing before the new element are fetched.
01015     bool retry;
01016     do {
01017       retry = false;
01018       // qLowerBound returns an iterator. subtract begin() to get absolute index
01019       insertPos = qLowerBound(childlistref.begin(), childlistref.end(), n, srt) - childlistref.begin();
01020       if (insertPos > childlistref.size()-10 && canFetchMore(parentid)) {
01021         fetchMore(parentid);
01022         retry = true;
01023       }
01024     } while (retry);
01025     // by fetching more, we may possibly have actually fetched the entry that we were instructed to insert
01026     // in the first place. Check.
01027     if (insertPos < childlistref.size() && childlistref[insertPos] == n)
01028       return; // job already done.
01029   }
01030 
01031   CategoryLabelNode &catLabelNodeRef = getCategoryLabelNodeRef(parentid);
01032 
01033   // and insert it again at the required spot
01034   if (notifyQtApi) {
01035     QModelIndex parentidx = createIndexFromId(parentid, -1, 0);
01036     pModel->beginInsertRows(parentidx, insertPos, insertPos);
01037   }
01038 
01039   qDebug("\tinserting (%d,%d) at pos %d in category '%s'", n.kind, n.index, insertPos,
01040          qPrintable(catLabelNodeRef.fullCategoryPath));
01041 
01042   pEntryCache[n.index].parent = parentid; // set the parent, thus validating the node
01043 
01044   childlistref.insert(insertPos, n); // insert into list of children
01045 
01046   if (notifyQtApi)
01047     pModel->endInsertRows();
01048 }
01049 
01050 KLFLibModelCache::EntryNode KLFLibModelCache::treeTakeEntry(const NodeId& nodeid, bool notifyQtApi)
01051 {
01052   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01053 
01054   klfDbg( "("<<nodeid<<","<<notifyQtApi<<")" ) ;
01055   NodeId n = nodeid;
01056   // keep it in cache to keep indexes valid, but invalidate the entry so findEntryIdList() doesn't find it
01057   // then remove all empty categories above the removed item.
01058   if (nodeid.kind != EntryKind) {
01059     qWarning()<<KLF_FUNC_NAME<<": nodeid="<<nodeid<<" does not reference an entry node!";
01060     return EntryNode();
01061   }
01062   EntryNode entrynode = getEntryNodeRef(nodeid);
01063 
01064   klfDbg("The entrynode in question is "<<entrynode) ;
01065 
01066   NodeId parentid;
01067   bool willRemoveParent;
01068   do {
01069     if (!n.valid())
01070       break; // what's going on?
01071     parentid = getNode(n).parent;
01072     if (n == NodeId::rootNode())
01073       break; // stop before removing root node (!)
01074     klfDbg("Getting interested to remove entry ID="<<n<<", from its parent of id="<<parentid) ;
01075     if (parentid.kind != CategoryLabelKind) {
01076       qWarning()<<KLF_FUNC_NAME<<"("<<n<<"): Bad parent node kind: "<<parentid.kind<<"!";
01077       return entrynode;
01078     }
01079     QModelIndex parent = createIndexFromId(parentid, -1, 0);
01080     int childIndex = pCategoryLabelCache[parentid.index].children.indexOf(n);
01081     if (childIndex < 0) {
01082       qWarning()<<KLF_FUNC_NAME<<"("<<n<<"): !!?! bad child-parent relation, can't find "<<n
01083                 <<" in child list "<<pCategoryLabelCache[parentid.index].children<<"; full dump:\n"
01084                 <<"\tEntryCache = "<<pEntryCache<<"\n"
01085                 <<"\tCat.Lbl.Cache = "<<pCategoryLabelCache;
01086       return entrynode;
01087     }
01088     if (notifyQtApi)
01089       pModel->beginRemoveRows(parent, childIndex, childIndex);
01090     // will-Remove-Parent if node 'n' is the sole remaining child of node 'parentid'
01091     willRemoveParent = parentid.valid() && getNode(parentid).children.size() <= 1;
01092     // remove 'n'
01093     if (n.kind == EntryKind) {
01094       klfDbg("unlinking entry node "<<n);
01095       pEntryCache.unlinkNode(n);
01096     } else if (n.kind == CategoryLabelKind) {
01097       klfDbg("unlinking category label node "<<n);
01098       pCategoryLabelCache.unlinkNode(n);
01099     } else {
01100       qWarning()<<KLF_FUNC_NAME<<": unlinking elements: unknown node kind in id="<<n<<"!";
01101     }
01102     // remove n from parents
01103     Node & parentref = getNodeRef(parentid);
01104     klfDbg("removing child #"<<childIndex<<" from parent id="<<parentid<<"; parent itself is "<<parentref) ;
01105     parentref.children.removeAt(childIndex);
01106     if (notifyQtApi)
01107       pModel->endRemoveRows();
01108     n = parentid;
01109   } while (willRemoveParent);
01110 
01111   return entrynode;
01112 }
01113 
01114 
01115 
01116 
01117 KLFLibModelCache::IndexType
01118 /* */ KLFLibModelCache::cacheFindCategoryLabel(QStringList catelements, bool createIfNotExists,
01119                                                bool notifyQtApi, bool newlyCreatedAreChildrenFetched)
01120 {
01121   klfDbg( "catelmnts="<<catelements<<", createIfNotExists="<<createIfNotExists<<", notifyQtApi="<<notifyQtApi ) ;
01122 
01123   QString catelpath = catelements.join("/");
01124 
01125   int i;
01126   for (i = 0; i < pCategoryLabelCache.size(); ++i) {
01127     if (pCategoryLabelCache.isAllocated(i) &&
01128         pCategoryLabelCache[i].parent.valid() &&
01129         pCategoryLabelCache[i].fullCategoryPath == catelpath) {
01130       // found the valid category label
01131       return i;
01132     }
01133   }
01134   if (catelements.isEmpty())
01135     return 0; // index of root category label
01136 
01137   // if we haven't found the correct category, we may need to create it if requested by
01138   // caller. If not, return failure immediately
01139   if ( ! createIfNotExists )
01140     return -1;
01141 
01142   if (pModel->displayType() != KLFLibModel::CategoryTree) {
01143     qWarning("cacheFindCategoryLabel: but not in a category tree display type (flavor flags=%#010x)",
01144              pModel->pFlavorFlags);
01145     return 0;
01146   }
01147 
01148   QStringList catelementsparent = catelements.mid(0, catelements.size()-1);
01149   IndexType parent_index = cacheFindCategoryLabel(catelementsparent, true, notifyQtApi);
01150   
01151   KLFLibModelCache::KLFLibModelSorter srt = 
01152     KLFLibModelCache::KLFLibModelSorter(this, pModel->pEntrySorter,
01153                                         pModel->pFlavorFlags & KLFLibModel::GroupSubCategories);
01154 
01155   // the category label node to add
01156   CategoryLabelNode c;
01157   c.allChildrenFetched = newlyCreatedAreChildrenFetched;
01158   c.fullCategoryPath = catelpath;
01159   c.categoryLabel = catelements.last(); // catelements is non-empty, see above
01160   // now create this last category label
01161   IndexType this_index = pCategoryLabelCache.insertNewNode(c);
01162   int insertPos;
01163   CategoryLabelNode & parentCatLabelNodeRef = pCategoryLabelCache[parent_index];
01164   QList<NodeId> & childlistref = parentCatLabelNodeRef.children;
01165   if (pLastSortPropId < 0) {
01166     insertPos = childlistref.size(); // no sorting, just append the item
01167   } else {
01168     // qLowerBound returns an iterator. subtract begin() to get absolute index
01169     insertPos = qLowerBound(childlistref.begin(), childlistref.end(),
01170                             NodeId(CategoryLabelKind, this_index), srt) - childlistref.begin();
01171   }
01172   klfDbg( ": About to insert rows in "<<NodeId(CategoryLabelKind, parent_index)
01173           <<" at position "<<insertPos ) ;
01174   if (notifyQtApi) {
01175     QModelIndex parentidx = createIndexFromId(NodeId(CategoryLabelKind, parent_index), -1, 0);
01176     pModel->beginInsertRows(parentidx, insertPos, insertPos);
01177   }
01178   qDebug("%s: Inserting this_index=%d in parent_index=%d 's children", KLF_FUNC_NAME, this_index,
01179          parent_index);
01180 
01181   childlistref.insert(insertPos, NodeId(CategoryLabelKind, this_index));
01182   pCategoryLabelCache[this_index].parent = NodeId(CategoryLabelKind, parent_index);
01183 
01184   if (notifyQtApi)
01185     pModel->endInsertRows();
01186 
01187   // and return the created category label index
01188   return this_index;
01189 }
01190 
01191 QString KLFLibModelCache::nodeValue(NodeId n, int entryProperty)
01192 {
01193   // return an internal string representation of the value of the property 'entryProperty' in node ptr.
01194   // or the category title if node is a category
01195 
01196   if (!n.valid() || n.isRoot())
01197     return QString();
01198   if (entryProperty < 0) {
01199     qWarning()<<KLF_FUNC_NAME<<": invalid entry property ID : "<<entryProperty;
01200     return QString();
01201   }
01202   if (n.kind == EntryKind) {
01203     EntryNode en = getEntryNodeRef(n);
01204     // don't update minimalist nodes (assume minimalist info is enough)
01205     return pModel->entrySorter()->entryValue(en.entry, entryProperty);
01206   }
01207   if (n.kind == CategoryLabelKind)
01208     return getCategoryLabelNodeRef(n).categoryLabel;
01209 
01210   qWarning()<<KLF_FUNC_NAME<<": Bad Item Kind: "<<n;
01211   return QString();
01212 }
01213 
01214 // private
01215 void KLFLibModelCache::sortCategory(NodeId category, KLFLibModelSorter *sorter, bool rootCall)
01216 {
01217   bool requireSimpleReverse = false;
01218    
01219   // some optimizations based on the current sorting
01220   if (sorter->entrySorter()->propId() == pLastSortPropId) {
01221     // already sorting according to that column
01222     if (sorter->entrySorter()->order() == pLastSortOrder) {
01223       // exact same sorting required, already done.
01224       return;
01225     } else {
01226       // opposite sorting (eg. Ascending instead of Descending)
01227       // -> reverse child list
01228       requireSimpleReverse = true;
01229     }
01230   }
01231    
01232   int k;
01233   // sort this category's children
01234    
01235   // This works both in LinearList and in CategoryTree display types. (In LinearList
01236   // display type, call this function on root category node)
01237   
01238   if (category.kind != CategoryLabelKind)
01239     return;
01240   if (category.index < 0 || category.index >= pCategoryLabelCache.size())
01241     return;
01242    
01243   if (sorter->entrySorter()->propId() < 0)
01244     return; // no sorting required
01245    
01246   QList<NodeId>& childlistref = pCategoryLabelCache[category.index].children;
01247   if (requireSimpleReverse) {
01248     // reverse the child list (but not category list if flavor is to group them)
01249     int N = childlistref.size();
01250     int firstEntryInd = 0;
01251     if (pModel->pFlavorFlags & KLFLibModel::GroupSubCategories) {
01252       for (firstEntryInd = 0; firstEntryInd < N &&
01253              childlistref[firstEntryInd].kind != EntryKind; ++firstEntryInd)
01254         ;
01255       // in case the GroupSubCategories flag is false, firstEntryInd is zero -> reverse all
01256     }
01257     // swap all entries, skipping the grouped sub-categories
01258     for (k = 0; k < (N-firstEntryInd)/2; ++k)
01259       qSwap(childlistref[firstEntryInd+k], childlistref[N-k-1]);
01260   } else {
01261     qSort(childlistref.begin(), childlistref.end(), *sorter); // normal sort
01262   }
01263    
01264   // and sort all children's children
01265   for (k = 0; k < childlistref.size(); ++k)
01266     if (childlistref[k].kind == CategoryLabelKind)
01267       sortCategory(childlistref[k], sorter, false /* not root call */);
01268 
01269   if (rootCall) {
01270     pLastSortPropId = sorter->entrySorter()->propId();
01271     pLastSortOrder = sorter->entrySorter()->order();
01272   }
01273 }
01274 
01275 
01276 KLFLibModelCache::NodeId KLFLibModelCache::nextNode(NodeId n)
01277 {
01278   //  klfDbg( "KLFLibModelCache::nextNode("<<n<<"): full tree dump:" ) ;
01279   //  fullDump();
01280     
01281   if (!n.valid()) {
01282     // start with root category (but don't return the root category itself)
01283     n = NodeId::rootNode();
01284   }
01285   // walk children, if any
01286   Node nn = getNode(n);
01287   if (nn.children.size() > 0) {
01288     return nn.children[0];
01289   }
01290   if (!nn.allChildrenFetched && canFetchMore(n)) {
01291     // we may have children, so try to fetch children
01292     fetchMore(n);
01293     // and recurse, now that more children have possibly been fetched.
01294     return nextNode(n);
01295   }
01296   // no children, find next sibling.
01297   NodeId parentid;
01298   while ( (parentid = nn.parent).valid() ) {
01299     Node parent = getNode(parentid);
01300     int row = getNodeRow(n);
01301     if (row+1 < parent.children.size()) {
01302       // there is a next sibling: return it
01303       return parent.children[row+1];
01304     }
01305     if (!parent.allChildrenFetched && canFetchMore(parentid)) {
01306       // there is possibly a next sibling, it just possibly hasn't been fetched.
01307       fetchMore(parentid);
01308       // now that the more children has been fetched for this parent, try again: (by recursing)
01309       return nextNode(n);
01310     }
01311     // no next sibling, so go up the tree
01312     n = parentid;
01313     nn = parent;
01314   }
01315   // last entry passed
01316   return NodeId();
01317 }
01318 
01319 KLFLibModelCache::NodeId KLFLibModelCache::prevNode(NodeId n)
01320 {
01321   if (!n.valid() || n.isRoot()) {
01322     // look for last node in tree.
01323     return lastNode(NodeId()); // NodeId() is accepted by lastNode.
01324   }
01325   // find previous sibling
01326   NodeId parentId = getNode(n).parent;
01327   Node parent = getNode(parentId);
01328   int row = getNodeRow(n);
01329   if (row > 0) {
01330     // there is a previous sibling: find its last node
01331     return lastNode(parent.children[row-1]);
01332   }
01333   // there is no previous sibling: so we can return the parent
01334   // except if parent is the root node, in which case we return an invalid NodeId.
01335   if (parentId == NodeId::rootNode())
01336     return NodeId();
01337 
01338   return parentId;
01339 }
01340 
01341 KLFLibModelCache::NodeId KLFLibModelCache::lastNode(NodeId n)
01342 {
01343   if (!n.valid())
01344     n = NodeId::rootNode(); // root category
01345 
01346   Node nn = getNode(n);
01347 
01348   // get the last child of node 'n'
01349   // this includes fetching _all_ its children if applicable
01350   while (!nn.allChildrenFetched) {
01351     if (!canFetchMore(n)) {
01352       qWarning()<<KLF_FUNC_NAME<<": internal error: node "<<n<<", node="<<nn
01353                 <<" has allChildrenFetched=false, but can't fetch more!";
01354       break;
01355     }
01356     fetchMore(n);
01357   }
01358 
01359   if (nn.children.size() == 0)
01360     return n; // no children: n is itself the "last node"
01361 
01362   // children: return last node of last child.
01363   return lastNode(nn.children[nn.children.size()-1]);
01364 }
01365 
01366 
01367 QList<KLFLib::entryId> KLFLibModelCache::entryIdList(const QModelIndexList& indexlist)
01368 {
01369   // ORDER OF RESULT IS NOT GARANTEED != entryIdForIndexList()
01370 
01371   QList<KLFLib::entryId> idList;
01372   int k;
01373   QList<NodeId> nodeIds;
01374   // walk all indexes and get their IDs
01375   for (k = 0; k < indexlist.size(); ++k) {
01376     NodeId n = getNodeForIndex(indexlist[k]);
01377     if ( !n.valid() || n.kind != EntryKind)
01378       continue;
01379     if ( nodeIds.contains(n) ) // duplicate, probably for another column
01380       continue;
01381     nodeIds << n;
01382     idList << getEntryNodeRef(n).entryid;
01383   }
01384   return idList;
01385 }
01386 
01387 
01388 
01389 QList<KLFLib::entryId> KLFLibModelCache::entryIdForIndexList(const QModelIndexList& indexlist)
01390 {
01391   // ORDER OF RESULT GARANTEED  != entryIdList()
01392 
01393   QList<KLFLib::entryId> eidlist;
01394   int k;
01395   for (k = 0; k < indexlist.size(); ++k) {
01396     NodeId node = getNodeForIndex(indexlist[k]);
01397     if ( !node.valid() || node.kind != EntryKind ) {
01398       eidlist << (KLFLib::entryId) -1;
01399       continue;
01400     }
01401     eidlist << getEntryNodeRef(node).entryid;
01402   }
01403   return eidlist;
01404 }
01405 QModelIndexList KLFLibModelCache::findEntryIdList(const QList<KLFLib::entryId>& eidlist)
01406 {
01407   klfDbg( ": eidlist="<<eidlist ) ;
01408   int k;
01409   int count = 0;
01410   QModelIndexList indexlist;
01411   // pre-fill index list
01412   for (k = 0; k < eidlist.size(); ++k)
01413     indexlist << QModelIndex();
01414 
01415   // walk entry list
01416   for (k = 0; k < pEntryCache.size(); ++k) {
01417     if (!pEntryCache[k].entryIsValid())
01418       continue;
01419     int i = eidlist.indexOf(pEntryCache[k].entryid);
01420     if (i >= 0) {
01421       indexlist[i] = createIndexFromId(NodeId(EntryKind, k), -1, 0);
01422       if (++count == eidlist.size())
01423         break; // found 'em all
01424     }
01425   }
01426   return indexlist;
01427 }
01428 
01429 KLFLibModelCache::NodeId KLFLibModelCache::findEntryId(KLFLib::entryId eId)
01430 {
01431   klfDbg("eId="<<eId) ;
01432   int k;
01433   for (k = 0; k < pEntryCache.size(); ++k)
01434     if (pEntryCache[k].entryid == eId && pEntryCache[k].entryIsValid())
01435       return NodeId(EntryKind, k);
01436 
01437   klfDbg("...not found.") ;
01438   return NodeId();
01439 }
01440 
01441 
01442 void KLFLibModelCache::fullDump()
01443 {
01444 #ifdef KLF_DEBUG
01445   int k;
01446   qDebug("---------------------------------------------------------");
01447   qDebug("------------- FULL CACHE DUMP ---------------------------");
01448   qDebug("---------------------------------------------------------");
01449   qDebug(" ");
01450   qDebug("Entry cache dump:");
01451   for (k = 0; k < pEntryCache.size(); ++k)
01452     qDebug()<<"#"<<k<<": "<<pEntryCache[k];
01453   qDebug(" ");
01454   qDebug("Category Label cache dump: ");
01455   for (k = 0; k < pCategoryLabelCache.size(); ++k)
01456     qDebug()<<"#"<<k<<": "<<pCategoryLabelCache[k];
01457   qDebug(" ");
01458   dumpNodeTree(NodeId::rootNode());
01459   qDebug(" ");
01460   qDebug("---------------------------------------------------------");
01461   qDebug("---------------------------------------------------------");
01462   qDebug("---------------------------------------------------------");
01463 #endif
01464 }
01465 
01466 void KLFLibModelCache::dumpNodeTree(NodeId node, int indent)
01467 {
01468 #ifdef KLF_DEBUG
01469 
01470   if (indent == 0) {
01471     qDebug(" ");
01472     qDebug("---------------- NODE TREE DUMP -------------------------");
01473   }
01474   char sindent[] = "                                                                                + ";
01475   if (indent < (signed)strlen(sindent))
01476     sindent[indent] = '\0';
01477 
01478   if (!node.valid())
01479     qDebug() << sindent << "(Invalid Node)";
01480 
01481   EntryNode en;
01482   CategoryLabelNode cn;
01483   switch (node.kind) {
01484   case EntryKind:
01485     en = getEntryNodeRef(node);
01486     qDebug() << sindent << node <<"\n"<<sindent<<"\t\t"<<en;
01487     break;
01488   case CategoryLabelKind:
01489     cn = getCategoryLabelNodeRef(node);
01490     qDebug() << sindent << node << "\n"<<sindent<<"\t\t"<<cn;
01491     break;
01492   default:
01493     qDebug() << sindent << node << "\n"<<sindent<<"\t\t*InvalidNodeKind*(kind="<<node.kind<<")";
01494   }
01495 
01496   if (node.valid()) {
01497     Node n = getNode(node);
01498     int k;
01499     for (k = 0; k < n.children.size(); ++k) {
01500       dumpNodeTree(getNode(node).children[k], indent+4);
01501     }
01502   }
01503 
01504   if (indent == 0) {
01505     qDebug("---------------------------------------------------------");
01506     qDebug("---------------------------------------------------------");
01507   }
01508 
01509 #endif
01510 }
01511 
01512 
01513 
01514 
01515 // -------------------------------------------------------
01516 
01517 //    MODEL ITSELF
01518 
01519 
01520 
01521 KLFLibModel::KLFLibModel(KLFLibResourceEngine *engine, uint flavorFlags, QObject *parent)
01522   : QAbstractItemModel(parent), pFlavorFlags(flavorFlags)
01523 {
01524   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01525 
01526   // set the default value of N items per batch
01527   // the initial default value is ridicuously small because fetchMore() is called during startup
01528   // sequence too many times (in my opinion). the batch count is increased once the widget is
01529   // shown, see KLFLibDefaultView::showEvent().
01530   // EDIT: the fetchMore() seems to be called when applying skins
01531   // EDIT(2): with new optimizations, the above not may not apply any more. needs testing.
01532   setFetchBatchCount(30);
01533 
01534   // by default, sort according to DateTime, recent first
01535   pEntrySorter = new KLFLibEntrySorter(KLFLibEntry::DateTime, Qt::DescendingOrder);
01536 
01537   pCache = new KLFLibModelCache(this);
01538   // assign KLF_DEBUG ref-instance later, as we don't have one yet!
01539 
01540   setResource(engine);
01541   // DON'T CONNECT SIGNALs FROM RESOURCE ENGINE HERE, we are informed from
01542   // the view. This is because of the KLFAbstractLibView API.
01543   //  connect(engine, SIGNAL(dataChanged(...)), this, SLOT(updateData(...)));
01544 
01545 }
01546 
01547 KLFLibModel::~KLFLibModel()
01548 {
01549   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01550   delete pCache;
01551   if (pEntrySorter)
01552     delete pEntrySorter;
01553 }
01554 
01555 void KLFLibModel::setResource(KLFLibResourceEngine *resource)
01556 {
01557   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01558 
01559   KLF_DEBUG_ASSIGN_SAME_REF_INSTANCE(pCache) ;
01560 
01561   pResource = resource;
01562   updateCacheSetupModel();
01563 }
01564 
01565 QUrl KLFLibModel::url() const
01566 {
01567   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01568   if (pResource == NULL) {
01569     qWarning()<<KLF_FUNC_NAME<<": resource is NULL!";
01570     return QUrl();
01571   }
01572   if ((pResource->supportedFeatureFlags() & KLFLibResourceEngine::FeatureSubResources)  ==  0)
01573     return pResource->url();
01574 
01575   // return URL with the default sub-resource (as we're displaying that one only)
01576   return pResource->url(KLFLibResourceEngine::WantUrlDefaultSubResource);
01577 }
01578 
01579 
01580 
01581 void KLFLibModel::setFlavorFlags(uint flags, uint modify_mask)
01582 {
01583   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01584   if ( (flags & modify_mask) == (pFlavorFlags & modify_mask) )
01585     return; // no change
01586   uint other_flags = pFlavorFlags & ~modify_mask;
01587   pFlavorFlags = flags | other_flags;
01588 
01589   // stuff that needs update
01590   if (modify_mask & DisplayTypeMask) {
01591     updateCacheSetupModel();
01592   }
01593   if (modify_mask & GroupSubCategories) {
01594     int col = columnForEntryPropertyId(pEntrySorter->propId());
01595     Qt::SortOrder ord = pEntrySorter->order();
01596     // force full re-sort
01597     sort(-1, ord); // by first setting to unsorted
01598     sort(col, ord); // and then re-sorting correctly
01599   }
01600 }
01601 uint KLFLibModel::flavorFlags() const
01602 {
01603   return pFlavorFlags;
01604 }
01605 
01606 void KLFLibModel::prefetch(const QModelIndexList& indexes) const
01607 {
01608   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01609   int k;
01610   for (k = 0; k < indexes.size(); ++k) {
01611     if (!indexes[k].isValid())
01612       continue;
01613     KLFLibModelCache::NodeId p = pCache->getNodeForIndex(indexes[k]);
01614     if (!p.valid() || p.isRoot()) {
01615       klfDbg("Invalid index: indexes[k].row="<<indexes[k].row());
01616       continue;
01617     }
01618     pCache->ensureNotMinimalist(p);
01619   }
01620 }
01621 
01622 QVariant KLFLibModel::data(const QModelIndex& index, int role) const
01623 {
01624   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01625   //  KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01626   //  klfDbg( "\tindex="<<index<<"; role="<<role ) ;
01627 
01628   KLFLibModelCache::NodeId p = pCache->getNodeForIndex(index);
01629   if (!p.valid() || p.isRoot())
01630     return QVariant();
01631 
01632   // if this node is not yet visible, hide it...
01633   KLFLibModelCache::Node thisNode = pCache->getNode(p);
01634   KLFLibModelCache::NodeId parent = thisNode.parent;
01635   KLFLibModelCache::Node parentNode = pCache->getNode(parent);
01636 
01637   if (role == ItemKindItemRole)
01638     return QVariant::fromValue<int>(p.kind);
01639 
01640   if (ItemKind(p.kind) == EntryKind) {
01641     // --- GET ENTRY DATA ---
01642     const KLFLibModelCache::EntryNode& ep = pCache->getEntryNodeRef(p);
01643 
01644     if (role == EntryContentsTypeItemRole)
01645       return entryColumnContentsPropertyId(index.column());
01646     if (role == EntryIdItemRole)
01647       return QVariant::fromValue<KLFLib::entryId>(ep.entryid);
01648 
01649     if (role == Qt::ToolTipRole || role == Qt::DisplayRole) { // current contents
01650       int propId = entryColumnContentsPropertyId(index.column());
01651       if (propId == KLFLibEntry::Preview)
01652         propId = KLFLibEntry::Tags;
01653       role = entryItemRole(propId);
01654       // role readjusted, continue below to return correct data
01655     }
01656 
01657     // now, only custom roles are recognized.
01658     if (role < Qt::UserRole)
01659       return QVariant();
01660 
01661     //    klfDbg( "(): role="<<role ) ;
01662 
01663     KLFLibEntry entry = ep.entry;
01664 
01665     if ( ! pCache->minimalistEntryPropIds().contains(entryPropIdForItemRole(role)) &&
01666          ep.minimalist) {
01667       // we are requesting for a property which is not included in minimalist property set
01668       pCache->ensureNotMinimalist(p);
01669     }
01670 
01671     if (role == entryItemRole(KLFLibEntry::Latex))
01672       return entry.latex();
01673     if (role == entryItemRole(KLFLibEntry::DateTime))
01674       return entry.dateTime();
01675     if (role == entryItemRole(KLFLibEntry::Category))
01676       return entry.category();
01677     if (role == entryItemRole(KLFLibEntry::Tags))
01678       //      return KLF_DEBUG_TEE( entry.tags() );
01679       return entry.tags();
01680     if (role == entryItemRole(KLFLibEntry::PreviewSize))
01681       return entry.previewSize();
01682 
01683     entry = ep.entry; // now the full entry
01684 
01685     if (role == FullEntryItemRole)
01686       return QVariant::fromValue(entry);
01687 
01688     if (role == entryItemRole(KLFLibEntry::Preview))
01689       return QVariant::fromValue<QImage>(entry.preview());
01690     if (role == entryItemRole(KLFLibEntry::Style))
01691       return QVariant::fromValue(entry.style());
01692     // by default
01693     return QVariant();
01694   }
01695   else if (ItemKind(p.kind) == CategoryLabelKind) {
01696     // --- GET CATEGORY LABEL DATA ---
01697     const KLFLibModelCache::CategoryLabelNode& cp = pCache->getCategoryLabelNodeRef(p);
01698     if (role == Qt::ToolTipRole)
01699       return cp.fullCategoryPath;
01700     if (role == CategoryLabelItemRole)
01701       return cp.categoryLabel;
01702     if (role == FullCategoryPathItemRole)
01703       return cp.fullCategoryPath;
01704     // by default
01705     return QVariant();
01706   }
01707 
01708   // default
01709   return QVariant();
01710 }
01711 Qt::ItemFlags KLFLibModel::flags(const QModelIndex& index) const
01712 {
01713   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01714   Qt::ItemFlags flagdropenabled = 0;
01715   if ( index.column() == 0 &&
01716        (pResource->canModifyData(KLFLibResourceEngine::InsertData) ||
01717         pResource->canModifyData(KLFLibResourceEngine::ChangeData)) )
01718     flagdropenabled = Qt::ItemIsDropEnabled;
01719 
01720   if (!index.isValid())
01721     return flagdropenabled;
01722 
01723   KLFLibModelCache::NodeId p = pCache->getNodeForIndex(index);
01724   if (!p.valid())
01725     return 0; // ?!!?
01726   if (p.kind == KLFLibModelCache::EntryKind)
01727     return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
01728   if (p.kind == KLFLibModelCache::CategoryLabelKind)
01729     return Qt::ItemIsEnabled | flagdropenabled;
01730 
01731   qWarning()<<KLF_FUNC_NAME<<": bad item kind! index-row="<<index.row()<<"; p="<<p;
01732 
01733   // by default (should never happen)
01734   return 0;
01735 }
01736 bool KLFLibModel::hasChildren(const QModelIndex &parent) const
01737 {
01738   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01739   if (parent.column() > 0)
01740     return false;
01741 
01742   KLFLibModelCache::NodeId p = pCache->getNodeForIndex(parent);
01743   if (!p.valid() || p.isRoot()) {
01744     // invalid index -> interpret as root node
01745     p = KLFLibModelCache::NodeId::rootNode();
01746   }
01747 
01748   return !pCache->getNode(p).allChildrenFetched  ||  pCache->getNode(p).children.size() > 0;
01749 }
01750 QVariant KLFLibModel::headerData(int section, Qt::Orientation orientation, int role) const
01751 {
01752   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01753   if (role == Qt::FontRole) {
01754     return qApp->font();
01755   }
01756   if (role == Qt::SizeHintRole && orientation == Qt::Horizontal) {
01757     switch (entryColumnContentsPropertyId(section)) {
01758     case KLFLibEntry::Preview:
01759       return QSize(280,30);
01760     case KLFLibEntry::Latex:
01761       return QSize(350,30);
01762     case KLFLibEntry::Category:
01763     case KLFLibEntry::Tags:
01764       return QSize(200,30);
01765     default:
01766       return QVariant();
01767     }
01768   }
01769   if (role == Qt::DisplayRole) {
01770     if (orientation == Qt::Horizontal)
01771       return KLFLibEntry().propertyNameForId(entryColumnContentsPropertyId(section));
01772     return QString("-");
01773   }
01774   return QVariant();
01775 }
01776 
01777 bool KLFLibModel::hasIndex(int row, int column, const QModelIndex &parent) const
01778 {
01779   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01780   // better implementation in the future
01781   return index(row, column, parent).isValid();
01782 }
01783 
01784 QModelIndex KLFLibModel::index(int row, int column, const QModelIndex &parent) const
01785 {
01786   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01787 
01788   const KLFLibModelCache::CategoryLabelNode& cat_p =
01789     pCache->getCategoryLabelNodeRef(KLFLibModelCache::NodeId::rootNode()); // root node
01790   KLFLibModelCache::CategoryLabelNode p = cat_p;
01791 
01792   if (parent.isValid()) {
01793     KLFLibModelCache::NodeId pp = pCache->getNodeForIndex(parent);
01794     if (pp.kind != KLFLibModelCache::CategoryLabelKind)
01795       return QModelIndex();
01796     if (pp.valid())
01797       p = pCache->getCategoryLabelNodeRef(pp);
01798   }
01799   if (row < 0 || row >= p.children.size() || column < 0 || column >= columnCount(parent))
01800     return QModelIndex();
01801   klfDbgT(": row="<<row<<"; column="<<column<<"; parent="<<parent);
01802   return pCache->createIndexFromId(p.children[row], row, column);
01803 }
01804 QModelIndex KLFLibModel::parent(const QModelIndex &index) const
01805 {
01806   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01807   klfDbgT(": requesting parent of index "<<index);
01808   KLFLibModelCache::NodeId p = pCache->getNodeForIndex(index);
01809   if ( !p.valid() ) // invalid index
01810     return QModelIndex();
01811   KLFLibModelCache::Node n = pCache->getNode(p);
01812   if ( ! n.parent.valid() ) // invalid parent (should never happen!)
01813     return QModelIndex();
01814   return KLF_DEBUG_TEE( pCache->createIndexFromId(n.parent, -1 /* figure out row */, 0) ) ;
01815 }
01816 int KLFLibModel::rowCount(const QModelIndex &parent) const
01817 {
01818   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01819 
01820   if (parent.column() > 0)
01821     return 0;
01822 
01823   KLFLibModelCache::NodeId p = pCache->getNodeForIndex(parent);
01824   if (!p.valid())
01825     p = KLFLibModelCache::NodeId::rootNode();
01826 
01827   KLFLibModelCache::Node n = pCache->getNode(p);
01828   klfDbg( " parent="<<parent<<"; numchildren="<<n.children.size() ) ;
01829 
01830   return n.children.size();
01831 }
01832 
01833 int KLFLibModel::columnCount(const QModelIndex & /*parent*/) const
01834 {
01835   return 5;
01836 }
01837 int KLFLibModel::entryColumnContentsPropertyId(int column) const
01838 {
01839   // WARNING: ANY CHANGES MADE HERE MUST BE REPEATED IN THE NEXT FUNCTION
01840   switch (column) {
01841   case 0:
01842     return KLFLibEntry::Preview;
01843   case 1:
01844     return KLFLibEntry::Tags;
01845   case 2:
01846     return KLFLibEntry::Category;
01847   case 3:
01848     return KLFLibEntry::Latex;
01849   case 4:
01850     return KLFLibEntry::DateTime;
01851   default:
01852     return -1;
01853   }
01854 }
01855 int KLFLibModel::columnForEntryPropertyId(int entryPropertyId) const
01856 {
01857   // WARNING: ANY CHANGES MADE HERE MUST BE REPEATED IN THE PREVIOUS FUNCTION
01858   switch (entryPropertyId) {
01859   case KLFLibEntry::Preview:
01860     return 0;
01861   case KLFLibEntry::Tags:
01862     return 1;
01863   case KLFLibEntry::Category:
01864     return 2;
01865   case KLFLibEntry::Latex:
01866     return 3;
01867   case KLFLibEntry::DateTime:
01868     return 4;
01869   default:
01870     return -1;
01871   }
01872 }
01873 
01874 
01875 bool KLFLibModel::canFetchMore(const QModelIndex& parent) const
01876 {
01877   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01878   //  KLF_DEBUG_BLOCK(KLF_FUNC_NAME); klfDbg( "\t parent="<<parent ) ;
01879 
01880   KLFLibModelCache::NodeId n = pCache->getNodeForIndex(parent);
01881   if (!n.valid())
01882     n = KLFLibModelCache::NodeId::rootNode();
01883 
01884   return pCache->canFetchMore(pCache->getNodeForIndex(parent));
01885 }
01886 void KLFLibModel::fetchMore(const QModelIndex& parent)
01887 {
01888   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01889   pCache->fetchMore(pCache->getNodeForIndex(parent), pFetchBatchCount);
01890 }
01891 
01892 
01893 Qt::DropActions KLFLibModel::supportedDropActions() const
01894 {
01895   return Qt::CopyAction|Qt::MoveAction;
01896 }
01897 
01898 QStringList KLFLibModel::mimeTypes() const
01899 {
01900   return QStringList() << "application/x-klf-internal-lib-move-entries"
01901                        << KLFAbstractLibEntryMimeEncoder::allEncodingMimeTypes();
01902 }
01903 QMimeData *KLFLibModel::mimeData(const QModelIndexList& indlist) const
01904 {
01905   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01906   // in the future, this will serve when dragging a category label, to redifine the index
01907   // list as containing all the child entries.
01908   QModelIndexList indexes = indlist;
01909 
01910   // get all entries
01911   KLFLibEntryList entries;
01912   QList<KLFLib::entryId> entryids;
01913   QList<KLFLibModelCache::NodeId> entriesnodeids;
01914   int k;
01915   for (k = 0; k < indexes.size(); ++k) {
01916     KLFLibModelCache::NodeId n = pCache->getNodeForIndex(indexes[k]);
01917     if (!n.valid() || n.isRoot())
01918       continue;
01919     if (n.kind != KLFLibModelCache::EntryKind)
01920       continue;
01921     if (entriesnodeids.contains(n))
01922       continue; // skip duplicates (for ex. for other model column indexes)
01923     const KLFLibModelCache::EntryNode& en = pCache->getEntryNodeRef(n);
01924     entries << pResource->entry(en.entryid);
01925     entryids << en.entryid;
01926     entriesnodeids << n;
01927   }
01928 
01929   //  klfDbg( "KLFLibModel::mimeData: Encoding entries "<<entries ) ;
01930 
01931   // some meta-data properties
01932   QVariantMap vprop;
01933   QUrl myurl = url();
01934   vprop["Url"] = myurl; // originating URL
01935 
01936   QMimeData *mimedata = KLFAbstractLibEntryMimeEncoder::createMimeData(entries, vprop);
01937 
01938   QByteArray internalmovedata;
01939 
01940   // application/x-klf-internal-lib-move-entries
01941   { QDataStream imstr(&internalmovedata, QIODevice::WriteOnly);
01942     imstr.setVersion(QDataStream::Qt_4_4);
01943     imstr << vprop << entryids;
01944   }
01945   mimedata->setData("application/x-klf-internal-lib-move-entries", internalmovedata);
01946 
01947   return mimedata;
01948 }
01949 
01950 // private
01951 bool KLFLibModel::dropCanInternal(const QMimeData *mimedata)
01952 {
01953   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01954   if ( ! mimedata->hasFormat("application/x-klf-internal-lib-move-entries") ||
01955        ! pResource->canModifyData(KLFLibResourceEngine::ChangeData))
01956     return false;
01957 
01958   QByteArray imdata = mimedata->data("application/x-klf-internal-lib-move-entries");
01959   QDataStream imstr(imdata);
01960   imstr.setVersion(QDataStream::Qt_4_4);
01961   QVariantMap vprop;
01962   imstr >> vprop;
01963   QUrl theirurl = vprop.value("Url").toUrl();
01964   QUrl oururl = url();
01965   bool ok = (oururl.toEncoded() == theirurl.toEncoded());
01966   klfDbg( "drag originated from "<<theirurl<<"; we are "<<oururl<<"; OK="<<ok ) ;
01967   return ok;
01968 }
01969 
01970 bool KLFLibModel::dropMimeData(const QMimeData *mimedata, Qt::DropAction action, int row,
01971                                int column, const QModelIndex& parent)
01972 {
01973   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
01974   klfDbg(  "Drop data: action="<<action<<" row="<<row<<" col="<<column
01975            << " parent="<<parent ) ;
01976 
01977   if (action == Qt::IgnoreAction)
01978     return true;
01979   if (action != Qt::CopyAction)
01980     return false;
01981 
01982   if ( ! (mimedata->hasFormat("application/x-klf-internal-lib-move-entries") &&
01983           pResource->canModifyData(KLFLibResourceEngine::ChangeData)) &&
01984        ! (KLFAbstractLibEntryMimeEncoder::canDecodeMimeData(mimedata) &&
01985           pResource->canModifyData(KLFLibResourceEngine::InsertData)) ) {
01986     // cannot (change items with application/x-klf-internal-lib-move-entries) AND
01987     // cannot (insert items with any supported format, eg. application/x-klf-libentries)
01988     return false;
01989   }
01990 
01991   if (column > 0)
01992     return false;
01993 
01994   // imdata, imstr : "*i*nternal *m*ove" ; ldata, lstr : "entry *l*ist"
01995   bool useinternalmove = dropCanInternal(mimedata);
01996   if (useinternalmove) {
01997     klfDbg(  "Dropping application/x-klf-internal-lib-move-entries" ) ;
01998     if ( !(pFlavorFlags & CategoryTree) )
01999       return false;
02000 
02001     QByteArray imdata = mimedata->data("application/x-klf-internal-lib-move-entries");
02002     QDataStream imstr(imdata);
02003     imstr.setVersion(QDataStream::Qt_4_4);
02004     QList<KLFLib::entryId> idlist;
02005     QVariantMap vprop;
02006     imstr >> vprop >> idlist;
02007     // get the category we moved to
02008     KLFLibModelCache::NodeId pn = pCache->getNodeForIndex(parent);
02009     if (!pn.valid()) {
02010       pn = KLFLibModelCache::NodeId::rootNode();
02011     }
02012     if (ItemKind(pn.kind) != CategoryLabelKind) {
02013       qWarning()<<"Dropped in a non-category index! (kind="<<pn.kind<<")";
02014       return false;
02015     }
02016     const KLFLibModelCache::CategoryLabelNode& cn = pCache->getCategoryLabelNodeRef(pn);
02017     // move, not copy: change the selected entries to the new category.
02018     QString newcategory = cn.fullCategoryPath;
02019     if (newcategory.endsWith("/"))
02020       newcategory.chop(1);
02021 
02022     bool r = pResource->changeEntries(idlist, QList<int>() << KLFLibEntry::Category,
02023                                       QList<QVariant>() << QVariant(newcategory));
02024     klfDbg( "Accepted drop of type application/x-klf-internal-lib-move-entries. Res="<<r<<"\n"
02025             <<"ID list is "<<idlist<<" new category is "<<newcategory ) ;
02026     if (!r) {
02027       return false;
02028     }
02029     // dataChanged() is emitted in changeEntries()
02030     return true;
02031   }
02032 
02033   klfDbg( "Dropping entry list." ) ;
02034 
02035   KLFLibEntryList elist;
02036   QVariantMap vprop;
02037   bool res = KLFAbstractLibEntryMimeEncoder::decodeMimeData(mimedata, &elist, &vprop);
02038   if ( ! res ) {
02039     qWarning()<<KLF_FUNC_NAME<<": Drop: Can't decode mime data! provided types="
02040               <<mimedata->formats();
02041     QMessageBox::warning(NULL, tr("Drop Error", "[[message box title]]"),
02042                          tr("Error dropping data."));
02043     return false;
02044   }
02045 
02046   if ( elist.isEmpty() )
02047     return true; // nothing to drop
02048 
02049   // insert list, regardless of parent (no category change)
02050   QList<KLFLib::entryId> inserted = pResource->insertEntries(elist);
02051   res = (inserted.size() && !inserted.contains(-1));
02052   klfDbg( "Dropped entry list "<<elist<<". Originating URL="
02053           <<(vprop.contains("Url")?vprop["Url"]:"(none)")<<". result="<<res ) ;
02054   if (!res)
02055     return false;
02056 
02057   // dataChanged()/rowsInserted()/... are emitted in insertEntries()
02058   return true;
02059 }
02060 
02061 uint KLFLibModel::dropFlags(QDragMoveEvent *event, QAbstractItemView *view)
02062 {
02063   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02064   const QMimeData *mdata = event->mimeData();
02065   if (dropCanInternal(mdata)) {
02066     if ( !(pFlavorFlags & CategoryTree) ) {
02067       return 0; // will NOT accept an internal move not in a category tree.
02068       //           (note: icon moves not handled in KLFLibModel; intercepted in view.)
02069     }
02070     // we're doing an internal drop in category tree:
02071     QModelIndex dropOnIndex = view->indexAt(event->pos());
02072     if (pResource->canModifyData(KLFLibResourceEngine::ChangeData) &&
02073         (!dropOnIndex.isValid() || dropOnIndex.column() == 0) )
02074       return DropWillAccept|DropWillMove|DropWillCategorize;
02075     return 0;
02076   }
02077   if (KLFAbstractLibEntryMimeEncoder::canDecodeMimeData(mdata) &&
02078       pResource->canModifyData(KLFLibResourceEngine::InsertData))
02079     return DropWillAccept;
02080   return 0;
02081 }
02082 
02083 QImage KLFLibModel::dragImage(const QModelIndexList& indexes)
02084 {
02085   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02086   if (indexes.isEmpty())
02087     return QImage();
02088 
02089   const int MAX = 5;
02090   const QSize s1 = 0.8*QSize(250,75); //klfconfig.UI.labelOutputFixedSize;
02091   const QPointF delta(18.0, 20.0);
02092   QList<QImage> previewlist;
02093   QList<KLFLibModelCache::NodeId> alreadydone;
02094   int k, j;
02095   int iS = indexes.size();
02096   int n = qMin(1+MAX, iS);
02097   for (j = 0, k = 0; k < iS && j < n; ++k) {
02098     KLFLibModelCache::NodeId n = pCache->getNodeForIndex(indexes[iS-k-1]); // reverse order
02099     if (!n.valid() || n.kind != KLFLibModelCache::EntryKind)
02100       continue;
02101     if (alreadydone.contains(n))
02102       continue;
02103     const KLFLibModelCache::EntryNode& en = pCache->getEntryNodeRef(n, true);
02104     alreadydone << n;
02105     previewlist << en.entry.preview().scaled(s1, Qt::KeepAspectRatio, Qt::SmoothTransformation);
02106     ++j;
02107   }
02108   if (previewlist.isEmpty())
02109     return QImage();
02110 
02111   int N = qMin(MAX, previewlist.size());
02112   QSize s2 = (s1 + (N-1)*pointToSizeF(delta)).toSize();
02113   QImage image(s2, QImage::Format_ARGB32_Premultiplied);
02114   image.fill(qRgba(0,0,0,0));
02115   {
02116     QPainter p(&image);
02117     QPointF P(0,0);
02118     QPointF lastimgbr;
02119     for (k = 0; k < N; ++k) {
02120       // and add this image
02121       QImage i = transparentify_image(previewlist[k], 0.7);
02122       p.drawImage(P, i);
02123       // p.drawRect(QRectF(P, s1));
02124       lastimgbr = P+sizeToPointF(i.size());
02125       P += delta;
02126     }
02127     if (k == MAX) {
02128       p.setCompositionMode(QPainter::CompositionMode_DestinationOver);
02129       p.setPen(QPen(QColor(0,0,0),2.0));
02130       QPointF br = lastimgbr;
02131       p.drawPoint(br - QPointF(5,5));
02132       p.drawPoint(br - QPointF(10,5));
02133       p.drawPoint(br - QPointF(15,5));
02134     }
02135   }
02136 
02137   return autocrop_image(image);
02138 }
02139 
02140 
02141 bool KLFLibModel::isDesendantOf(const QModelIndex& child, const QModelIndex& ancestor)
02142 {
02143   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02144   if (!child.isValid())
02145     return false;
02146 
02147   return child.parent() == ancestor || isDesendantOf(child.parent(), ancestor);
02148 }
02149 
02150 QStringList KLFLibModel::categoryList() const
02151 {
02152   return pCache->categoryListCache();
02153 }
02154 
02155 void KLFLibModel::updateData(const QList<KLFLib::entryId>& entryIdList, int modifyType)
02156 {
02157   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02158   pCache->updateData(entryIdList, modifyType);
02159 }
02160 
02161 QModelIndex KLFLibModel::walkNextIndex(const QModelIndex& cur)
02162 {
02163   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02164   KLFLibModelCache::NodeId nextnode = pCache->nextNode(pCache->getNodeForIndex(cur));
02165   //  klfDbg( "KLFLibModel::walkNextIndex: got next node=NodeId("<<nextnode.kind<<","<<nextnode.index<<")" ) ;
02166   return pCache->createIndexFromId(nextnode, -1, 0);
02167 }
02168 QModelIndex KLFLibModel::walkPrevIndex(const QModelIndex& cur)
02169 {
02170   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02171   KLFLibModelCache::NodeId prevnode = pCache->prevNode(pCache->getNodeForIndex(cur));
02172   return pCache->createIndexFromId(prevnode, -1, 0);
02173 }
02174 
02175 KLFLib::entryId KLFLibModel::entryIdForIndex(const QModelIndex& index) const
02176 {
02177   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02178   return entryIdForIndexList(QModelIndexList() << index) [0];
02179 }
02180 
02181 QModelIndex KLFLibModel::findEntryId(KLFLib::entryId eid) const
02182 {
02183   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02184   return findEntryIdList(QList<KLFLib::entryId>() << eid) [0];
02185 }
02186 
02187 QList<KLFLib::entryId> KLFLibModel::entryIdForIndexList(const QModelIndexList& indexlist) const
02188 {
02189   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02190   return pCache->entryIdForIndexList(indexlist);
02191 }
02192 QModelIndexList KLFLibModel::findEntryIdList(const QList<KLFLib::entryId>& eidlist) const
02193 {
02194   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02195   return pCache->findEntryIdList(eidlist);
02196 }
02197 
02198 
02199 void KLFLibModel::setEntrySorter(KLFLibEntrySorter *entrySorter)
02200 {
02201   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02202   if (pEntrySorter == entrySorter)
02203     return;
02204   if (entrySorter == NULL) {
02205     qWarning()<<KLF_FUNC_NAME<<": NULL entrySorter given!";
02206   }
02207 
02208   if (pEntrySorter)
02209     delete pEntrySorter;
02210   pEntrySorter = entrySorter;
02211 }
02212 
02213 
02214 QModelIndex KLFLibModel::searchFind(const QString& queryString, const QModelIndex& fromIndex,
02215                                     bool forward)
02216 {
02217   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02218   klfDbg( " s="<<queryString<<" from "<<fromIndex<<" forward="<<forward ) ;
02219   pSearchAborted = false;
02220   pSearchString = queryString;
02221   pSearchCurNode = fromIndex;
02222   return searchFindNext(forward);
02223 }
02224 QModelIndex KLFLibModel::searchFindNext(bool forward)
02225 {
02226   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02227   pSearchAborted = false;
02228   if (pSearchString.isEmpty())
02229     return QModelIndex();
02230 
02231   QTime t;
02232 
02233   KLFLibModelCache::NodeId curNode = pCache->getNodeForIndex(pSearchCurNode);
02234 
02235   // search nodes
02236   KLFLibModelCache::NodeId (KLFLibModelCache::*stepfunc)(KLFLibModelCache::NodeId) =
02237     forward
02238     ? &KLFLibModelCache::nextNode
02239     : &KLFLibModelCache::prevNode;
02240 
02241   // presence of capital letter switches case sensitivity on (like (X)Emacs)
02242   Qt::CaseSensitivity cs = Qt::CaseInsensitive;
02243   if (pSearchString.contains(QRegExp("[A-Z]")))
02244     cs = Qt::CaseSensitive;
02245 
02246   bool found = false;
02247   while ( ! found &&
02248           (curNode = (pCache->*stepfunc)(curNode)).valid() ) {
02249     if ( pCache->searchNodeMatches(curNode, pSearchString, cs) ) {
02250       found = true;
02251     }
02252     if (t.elapsed() > 150) {
02253       pSearchCurNode = pCache->createIndexFromId(curNode, -1, 0);
02254       qApp->processEvents();
02255       if (pSearchAborted)
02256         break;
02257       t.restart();
02258     }
02259   }
02260   pSearchCurNode = pCache->createIndexFromId(curNode, -1, 0);
02261   if (found) {
02262     klfDbg( "found "<<pSearchString<<" at "<<pSearchCurNode ) ;
02263     return pSearchCurNode;
02264   }
02265   return QModelIndex();
02266 }
02267 
02268 void KLFLibModel::searchAbort()
02269 {
02270   pSearchAborted = true;
02271 }
02272 
02273 bool KLFLibModelCache::searchNodeMatches(const NodeId& nodeId, const QString& searchString,
02274                                          Qt::CaseSensitivity cs)
02275 {
02276   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02277   if (nodeId.kind == CategoryLabelKind) {
02278     if (nodeValue(nodeId).contains(searchString, cs))
02279       return true;
02280     return false;
02281   }
02282   if (nodeValue(nodeId, KLFLibEntry::Latex).contains(searchString, cs) ||
02283       nodeValue(nodeId, KLFLibEntry::Tags).contains(searchString, cs))
02284     return true;
02285   // let an item match its category only in NON-category tree mode (user friendlyness: category matching
02286   // will match the category title, after that, don't walk all the children...)
02287   if ((pModel->pFlavorFlags & KLFLibModel::CategoryTree) == 0 &&
02288       nodeValue(nodeId, KLFLibEntry::Category).contains(searchString, cs)) {
02289     return true;
02290   }
02291 
02292   return false;
02293 }
02294 
02295 
02296 // bool KLFLibModel::changeEntries(const QModelIndexList& indexlist, int propId, const QVariant& value)
02297 // {
02298 //   if ( indexlist.size() == 0 )
02299 //     return true; // no change...
02300 
02301 //   QList<KLFLib::entryId> idList = pCache->entryIdList(indexlist);
02302 //   klfDbg( ": Changing entries "<<idList<<"; indexlist="<<indexlist ) ;
02303 //   //  klfDbg( "full dump:" ) ;
02304 //   //  pCache->fullDump();
02305 //   bool r = pResource->changeEntries(idList, QList<int>() << propId, QList<QVariant>() << value);
02306 //   //  klfDbg( ": entries changed, full dump:" ) ;
02307 //   //  pCache->fullDump();
02308 
02309 //   // the resource will emit dataChanged() to the view's updateResourceData() for the model update
02310 
02311 //   return r;
02312 // }
02313 
02314 // bool KLFLibModel::insertEntries(const KLFLibEntryList& entries)
02315 // {
02316 //   if (entries.size() == 0)
02317 //     return true; // nothing to do...
02318 
02319 //   KLFLibEntryList elist = entries;
02320 
02321 //   QList<KLFLib::entryId> list = pResource->insertEntries(elist);
02322 
02323 //   if ( list.size() == 0 || list.contains(-1) )
02324 //     return false; // error for at least one entry
02325 //   return true;
02326 // }
02327 
02328 // bool KLFLibModel::deleteEntries(const QModelIndexList& indexlist)
02329 // {
02330 //   if ( indexlist.size() == 0 )
02331 //     return true; // no change...
02332 
02333 //   QList<KLFLib::entryId> idList = pCache->entryIdList(indexlist);
02334 //   if ( pResource->deleteEntries(idList) ) {
02335 //     // will be automatically called via the call to resoruce->(modify-data)()!
02336 //     //    updateCacheSetupModel();
02337 //     return true;
02338 //   }
02339 //   return false;
02340 // }
02341 
02342 void KLFLibModel::completeRefresh()
02343 {
02344   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02345   updateCacheSetupModel();
02346 }
02347 
02348 
02349 void KLFLibModel::redoSort()
02350 {
02351   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02352 
02353   // truly need to re-query the resource with our partial querying and fetching more system...
02354   updateCacheSetupModel();
02355   return;
02356   /*
02357    // this does not work with partial querying and fetching more, we truly need to re-query the resource
02358    startLayoutChange();
02359    
02360    KLFLibModelCache::KLFLibModelSorter srt = 
02361    KLFLibModelCache::KLFLibModelSorter(pCache, pEntrySorter, pFlavorFlags & KLFLibModel::GroupSubCategories);
02362    
02363    pCache->sortCategory(KLFLibModelCache::NodeId::rootNode(), &srt);
02364   
02365    endLayoutChange();
02366   */
02367 }
02368 
02369 void KLFLibModel::sort(int column, Qt::SortOrder order)
02370 {
02371   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02372 
02373   // select right property ID
02374   int propId = entryColumnContentsPropertyId(column);
02375   // if property ID is preview, then request sort by DateTime (user friendliness)
02376   if (propId == KLFLibEntry::Preview) {
02377     propId = KLFLibEntry::DateTime;
02378   }
02379 
02380   pEntrySorter->setPropId(propId);
02381   pEntrySorter->setOrder(order);
02382 
02383   pCache->setSortingBy(propId, order);
02384 
02385   redoSort();
02386 }
02387 
02388 
02389 
02390 QList<KLFLibModel::PersistentId> KLFLibModel::persistentIdList(const QModelIndexList& persistentIndexes)
02391 {
02392   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02393   // save persistent indexes
02394   QList<PersistentId> persistentIndexIds;
02395   int k;
02396   for (k = 0; k < persistentIndexes.size(); ++k) {
02397     PersistentId id;
02398     KLFLibModelCache::NodeId n = pCache->getNodeForIndex(persistentIndexes[k]);
02399     if (!n.valid()) {
02400       id.kind = (ItemKind)-1;
02401     } else {
02402       id.kind = n.kind;
02403       if (ItemKind(id.kind) == EntryKind)
02404         id.entry_id = pCache->getEntryNodeRef(n).entryid;
02405       else if (ItemKind(id.kind) == CategoryLabelKind)
02406         id.categorylabel_fullpath = pCache->getCategoryLabelNodeRef(n).fullCategoryPath;
02407       else
02408         qWarning("KLFLibModel::persistentIdList: Bad Node kind: %d!!", id.kind);
02409     }
02410     id.column = persistentIndexes[k].column();
02411     persistentIndexIds << id;
02412     klfDbg("saved persistent id "<<id) ;
02413   }
02414   return persistentIndexIds;
02415 }
02416 QModelIndexList KLFLibModel::newPersistentIndexList(const QList<PersistentId>& persistentIndexIds)
02417 {
02418   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02419   QModelIndexList newPersistentIndexes;
02420   int k;
02421   for (k = 0; k < persistentIndexIds.size(); ++k) {
02422     // find node in new layout
02423     PersistentId id = persistentIndexIds[k];
02424     QModelIndex index;
02425     if (ItemKind(id.kind) == EntryKind) {
02426       QModelIndexList ilist = pCache->findEntryIdList(QList<KLFLib::entryId>() << id.entry_id);
02427       index = ilist[0];
02428     } else if (id.kind == CategoryLabelKind) {
02429       int idx = pCache->cacheFindCategoryLabel(id.categorylabel_fullpath.split('/'));
02430       KLFLibModelCache::NodeId nodeId(KLFLibModelCache::CategoryLabelKind, idx);
02431       index = pCache->createIndexFromId(nodeId, -1, 0);
02432     } else {
02433       qWarning("%s: bad persistent id node kind! :%d", KLF_FUNC_NAME, id.kind);
02434     }
02435 
02436     newPersistentIndexes << index;
02437     klfDbg("restoring persistent id "<<id<<" as index "<<index) ;
02438   }
02439   return newPersistentIndexes;
02440 }
02441 
02442 
02443 void KLFLibModel::startLayoutChange(bool withQtLayoutChangedSignal)
02444 {
02445   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02446   // first notify anyone who may be interested
02447   if (withQtLayoutChangedSignal)
02448     emit layoutAboutToBeChanged();
02449   // save persistent indexes
02450   pLytChgIndexes = persistentIndexList();
02451   pLytChgIds = persistentIdList(pLytChgIndexes);
02452 }
02453 void KLFLibModel::endLayoutChange(bool withQtLayoutChangedSignal)
02454 {
02455   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
02456 
02457   QModelIndexList newpindexes = newPersistentIndexList(pLytChgIds);
02458   changePersistentIndexList(pLytChgIndexes, newpindexes);
02459   // and notify of layout change
02460   if (withQtLayoutChangedSignal)
02461     emit layoutChanged();
02462 }
02463 
02464 
02465 void KLFLibModel::updateCacheSetupModel()
02466 {
02467   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02468   pCache->rebuildCache();
02469 }
02470 
02471 
02472 // --------------------------------------------------------
02473 
02474 
02475 
02476 KLFLibViewDelegate::KLFLibViewDelegate(QObject *parent)
02477   : QAbstractItemDelegate(parent), pSelModel(NULL), pTheTreeView(NULL),
02478     pPreviewSize(klfconfig.UI.labelOutputFixedSize)
02479 {
02480   pAutoBackgroundItems = true;
02481 }
02482 KLFLibViewDelegate::~KLFLibViewDelegate()
02483 {
02484 }
02485 
02486 QWidget * KLFLibViewDelegate::createEditor(QWidget */*parent*/,
02487                                            const QStyleOptionViewItem& /*option*/,
02488                                            const QModelIndex& /*index*/) const
02489 {
02490   return 0;
02491 }
02492 bool KLFLibViewDelegate::editorEvent(QEvent */*event*/, QAbstractItemModel */*model*/,
02493                                      const QStyleOptionViewItem& /*option*/,
02494                                      const QModelIndex& /*index*/)
02495 {
02496   return false;
02497 }
02498 
02499 
02501 class _klf_block_progress_blocker
02502 {
02503   KLFLibResourceEngine *res;
02504 public:
02505   _klf_block_progress_blocker(KLFLibResourceEngine *r) : res(r)
02506   {
02507     if (res != NULL)
02508       res->blockProgressReporting(true);
02509   }
02510   ~_klf_block_progress_blocker()
02511   {
02512     if (res != NULL)
02513       res->blockProgressReporting(false);
02514   }
02515 };
02516 
02517 
02518 void KLFLibViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem& option,
02519                                const QModelIndex& index) const
02520 {
02521   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02522   klfDbg( "\tindex="<<index<<"; rect="<<option.rect ) ;
02523   PaintPrivate pp;
02524   pp.p = painter;
02525   pp.option = &option;
02526   pp.innerRectImage = QRect(option.rect.topLeft()+QPoint(2,2), option.rect.size()-QSize(4,4));
02527   pp.innerRectText = QRect(option.rect.topLeft()+QPoint(4,2), option.rect.size()-QSize(8,3));
02528 
02529 #ifdef Q_WS_MAC
02530   // block progress reporting on MAC to avoid repaint recursions
02531   KLFLibResourceEngine *rres = NULL;
02532   KLFLibModel *rmodel = qobject_cast<KLFLibModel*>(const_cast<QAbstractItemModel*>(index.model()));
02533   if (rmodel != NULL) rres = rmodel->resource();
02534   _klf_block_progress_blocker blocker(rres);
02535 #endif
02536 
02537   painter->save();
02538 
02539   QPen pen = painter->pen();
02540   pp.isselected = (option.state & QStyle::State_Selected);
02541   QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
02542     ? QPalette::Normal : QPalette::Disabled;
02543   if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
02544     cg = QPalette::Inactive;
02545   if (pp.isselected) {
02546     pp.background = option.palette.brush(cg, QPalette::Highlight);
02547     painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
02548     painter->fillRect(option.rect, pp.background);
02549   } else {
02550     pp.background = pAutoBackgroundColor.isValid()
02551       ? pAutoBackgroundColor
02552       : option.palette.brush(cg, QPalette::Base);
02553     painter->setPen(option.palette.color(cg, QPalette::Text));
02554   }
02555 
02556   int kind = index.data(KLFLibModel::ItemKindItemRole).toInt();
02557   if (kind == KLFLibModel::EntryKind)
02558     paintEntry(&pp, index);
02559   else if (kind == KLFLibModel::CategoryLabelKind)
02560     paintCategoryLabel(&pp, index);
02561 
02562   if (option.state & QStyle::State_HasFocus) {
02563     QStyleOptionFocusRect o;
02564     o.QStyleOption::operator=(option);
02565     o.rect = option.rect;
02566     o.state |= QStyle::State_KeyboardFocusChange;
02567     o.state |= QStyle::State_Item;
02568     QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
02569                               ? QPalette::Normal : QPalette::Disabled;
02570     o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
02571                                              ? QPalette::Highlight : QPalette::Window);
02572     const QWidget *w = 0;
02573     if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3 *>(&option))
02574       w = v3->widget;
02575     QStyle *style = w ? w->style() : QApplication::style();
02576     style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, w);
02577   }
02578 
02579   painter->restore();
02580 }
02581 
02582 void KLFLibViewDelegate::paintEntry(PaintPrivate *p, const QModelIndex& index) const
02583 {
02584   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02585   uint fl = PTF_HighlightSearch;
02586   if (index.parent() == pSearchIndex.parent() && index.row() == pSearchIndex.row())
02587     fl |= PTF_HighlightSearchCurrent;
02588 
02589   switch (index.data(KLFLibModel::EntryContentsTypeItemRole).toInt()) {
02590   case KLFLibEntry::Latex:
02591     // paint Latex String
02592     fl |= PTF_FontTT;
02593     paintText(p, index.data(KLFLibModel::entryItemRole(KLFLibEntry::Latex)).toString(), fl);
02594     break;
02595   case KLFLibEntry::Preview:
02596     // paint Latex Equation
02597     {
02598       QImage img = index.data(KLFLibModel::entryItemRole(KLFLibEntry::Preview)).value<QImage>();
02599       QImage img2 = img.scaled(p->innerRectImage.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
02600       if (p->isselected)
02601         img2 = transparentify_image(img2, 0.85);
02602       QPoint pos = p->innerRectImage.topLeft()
02603         + QPoint(0, (p->innerRectImage.height()-img2.height()) / 2);
02604       if (pAutoBackgroundItems) {
02605         // draw image on different background if it can't be "distinguished" from default background
02606         // (eg. a transparent white formula)
02607         klfDbg( " BG Brush is "<<p->background ) ;
02608         QColor bgcolor = p->background.color();
02609         QList<QColor> bglista, bglistb, bglist;
02610         //      bglist << bgcolor
02611         //             << QColor(255,255,255)
02612         //             << QColor(220,220,220)
02613         //             << QColor(180,190,190)
02614         //             << QColor(200,200,200)
02615         //             << QColor(150,150,150)
02616         //             << QColor(100,100,100)
02617         //             << QColor(50,50,50)
02618         //             << QColor(0,0,0);
02619         bglist << bgcolor; // first try: default color (!)
02620         bglista = bglistb = bglist;
02621         int count;
02622         for (count = 0; count < 5; ++count) // suggest N (ever) darker colors
02623           bglista << bglista.last().darker(105+count*2);
02624         for (count = 0; count < 5; ++count) // and N (ever) lighter colors
02625           bglistb << bglistb.last().lighter(105+count*2);
02626         // build the full list, and always provide white, and black to be sure
02627         bglist << bglista.mid(1) << bglistb.mid(1) << QColor(255,255,255) << QColor(0,0,0);
02628         klfDbg( "alt. bg list is "<<bglist );
02629         int k;
02630         for (k = 0; k < bglist.size(); ++k) {
02631           //      klfDbg( "calling image_is_distinguishable on entry with latex="
02632           //              <<index.data(KLFLibModel::entryItemRole(KLFLibEntry::Latex)).toString() );
02633           bool distinguishable = image_is_distinguishable(img2, bglist[k], 20); // 30
02634           if ( distinguishable )
02635             break; // got distinguishable color
02636         }
02637         // if the background color is not the default one, fill the background with that color
02638         if (k > 0 && k < bglist.size())
02639           p->p->fillRect(QRect(pos, img2.size()), QBrush(bglist[k]));
02640       }
02641       // and draw the equation
02642       p->p->save();
02643       p->p->translate(pos);
02644       if (klfconfig.UI.glowEffect)
02645         klfDrawGlowedImage(p->p, img2, klfconfig.UI.glowEffectColor, klfconfig.UI.glowEffectRadius, false);
02646       p->p->drawImage(QPoint(0,0), img2);
02647       p->p->restore();
02648       break;
02649     }
02650   case KLFLibEntry::Category:
02651     // paint Category String
02652     paintText(p, index.data(KLFLibModel::entryItemRole(KLFLibEntry::Category)).toString(), fl);
02653     break;
02654   case KLFLibEntry::Tags:
02655     // paint Tags String
02656     fl |= PTF_FontLarge;
02657     paintText(p, index.data(KLFLibModel::entryItemRole(KLFLibEntry::Tags)).toString(), fl);
02658     break;
02659   case KLFLibEntry::DateTime:
02660     // paint DateTime String
02661     { QLocale loc; // use the default locale, which was set to the value of klfconfig.UI.locale in main app.
02662       paintText(p, loc.toString(index.data(KLFLibModel::entryItemRole(KLFLibEntry::DateTime))
02663                                 .toDateTime(), QLocale::LongFormat), fl);
02664       break;
02665     }
02666   default:
02667     qDebug("KLFLibViewDelegate::paintEntry(): Got bad contents type %d !",
02668            index.data(KLFLibModel::EntryContentsTypeItemRole).toInt());
02669     // nothing to show !
02670     //    painter->fillRect(option.rect, QColor(128,0,0));
02671   }
02672 }
02673 
02674 void KLFLibViewDelegate::paintCategoryLabel(PaintPrivate *p, const QModelIndex& index) const
02675 {
02676   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02677   //  klfDbg( "index="<<index<<"; pTheTreeView="<<pTheTreeView<<"; treeview->isexpanded="
02678   //      <<(pTheTreeView?(pTheTreeView->isExpanded(index)?"true":"false"):"(N/A)") );
02679   //  klfDbg( "index has selected desc. ?="<<indexHasSelectedDescendant(index) );
02680 
02681   if (index.column() > 0)  // paint only on first column
02682     return;
02683 
02684   //  klfDbg( "index="<<index<<"; pSearchIndex="<<pSearchIndex ) ;
02685 
02686   uint fl = PTF_HighlightSearch;
02687   if (index.parent() == pSearchIndex.parent() && index.row() == pSearchIndex.row())
02688     fl |= PTF_HighlightSearchCurrent;
02689   if ( pTheTreeView != NULL && !pTheTreeView->isExpanded(index) &&
02690        indexHasSelectedDescendant(index) ) { // not expanded, and has selected child
02691     fl |= PTF_SelUnderline;
02692   }
02693   //   if ( sizeHint(*p->option, index).width() > p->option->rect.width() ) {
02694   //     // force rich text edit to handle too-wide texts
02695   //     fl |= PTF_ForceRichTextRender;
02696   //   }
02697 
02698   if (fl & PTF_HighlightSearchCurrent) {
02699     // paint line as if it were selected.
02700     QPen pen = p->p->pen();
02701     QPalette::ColorGroup cg = p->option->state & QStyle::State_Enabled
02702       ? QPalette::Normal : QPalette::Disabled;
02703     if (cg == QPalette::Normal && !(p->option->state & QStyle::State_Active))
02704       cg = QPalette::Inactive;
02705     p->p->fillRect(p->option->rect, p->option->palette.brush(cg, QPalette::Highlight));
02706     p->p->setPen(p->option->palette.color(cg, QPalette::HighlightedText));
02707   }
02708 
02709   // paint Category Label
02710   paintText(p, index.data(KLFLibModel::CategoryLabelItemRole).toString(), fl);
02711 }
02712 
02713 void KLFLibViewDelegate::paintText(PaintPrivate *p, const QString& text, uint flags) const
02714 {
02715   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02716 
02717   QFont font = p->option->font;
02718 
02719   if (flags & PTF_FontTT)
02720     font = klfconfig.UI.preambleEditFont; // use preamble font
02721   if (flags & PTF_FontLarge) {
02722     font.setPointSize(QFontInfo(font).pointSize()+1);
02723     //    font.setStyle(QFont::StyleOblique);
02724   }
02725 
02726   QColor textcol = p->option->palette.color(QPalette::Text);
02727   QColor textcoltransp = textcol; textcoltransp.setAlpha(0);
02728   QGradientStops borderfadelingrstops;
02729   borderfadelingrstops << QGradientStop(0.0f,  textcoltransp)
02730                        << QGradientStop(0.1f, textcol)
02731                        << QGradientStop(0.9f, textcol)
02732                        << QGradientStop(1.0f,  textcoltransp);
02733   QLinearGradient borderfadelingr(p->innerRectText.topLeft(), p->innerRectText.bottomLeft());
02734   borderfadelingr.setStops(borderfadelingrstops);
02735   borderfadelingr.setCoordinateMode(QGradient::LogicalMode);
02736   QPen borderfadepen = QPen(QBrush(borderfadelingr), 1.0f);
02737   QPen normalpen = QPen(textcol, 1.0f);
02738 
02739   int drawnTextWidth;
02740   int drawnBaseLineY;
02741   if ( (pSearchString.isEmpty() || !(flags&PTF_HighlightSearch) ||
02742         text.indexOf(pSearchString, 0, Qt::CaseInsensitive) == -1) &&
02743        !(flags & PTF_SelUnderline) &&
02744        !(flags & PTF_ForceRichTextRender) ) {
02745     // no formatting required, use fast method.
02746     QSize s = QFontMetrics(font).size(0, text);
02747     drawnTextWidth = s.width();
02748     drawnBaseLineY = (int)(p->innerRectText.bottom() - 0.5f*(p->innerRectText.height()-s.height()));
02749     p->p->setFont(font);
02750     if (s.height() > p->innerRectText.height()) { // contents height exceeds available height
02751       klfDbg("Need border fade pen for text "<<text) ;
02752       p->p->setPen(borderfadepen);
02753     } else {
02754       klfDbg("Don't need border fade pen for text "<<text) ;
02755       p->p->setPen(normalpen);
02756     }
02757     p->p->drawText(p->innerRectText, Qt::AlignLeft|Qt::AlignVCenter, text);
02758   } else {
02759     // formatting required.
02760     // build a list of regions to highlight
02761     QList<ColorRegion> c;
02762     QTextCharFormat f_highlight;
02763     if (flags & PTF_HighlightSearchCurrent)
02764       f_highlight.setBackground(QColor(0,255,0,80));
02765     f_highlight.setForeground(QColor(128,0,0));
02766     f_highlight.setFontItalic(true);
02767     f_highlight.setFontWeight(QFont::Bold);
02768     int i = -1, ci, j;
02769     if (!pSearchString.isEmpty()) {
02770       while ((i = text.indexOf(pSearchString, i+1, Qt::CaseInsensitive)) != -1)
02771         c << ColorRegion(f_highlight, i, pSearchString.length());
02772     }
02773     qSort(c);
02774     //    klfDbg( "searchstr="<<pSearchString<<"; label "<<text<<": c is "<<c ) ;
02775     QTextDocument textDocument;
02776     textDocument.setDefaultFont(font);
02777     QTextCursor cur(&textDocument);
02778     QList<ColorRegion> appliedfmts;
02779     for (i = ci = 0; i < text.length(); ++i) {
02780       //      klfDbg( "Processing char "<<text[i]<<"; i="<<i<<"; ci="<<ci ) ;
02781       if (ci >= c.size() && appliedfmts.size() == 0) {
02782         // insert all remaining text (no more formatting needed)
02783         cur.insertText(Qt::escape(text.mid(i)), QTextCharFormat());
02784         break;
02785       }
02786       while (ci < c.size() && c[ci].start == i) {
02787         appliedfmts.append(c[ci]);
02788         ++ci;
02789       }
02790       QTextCharFormat f;
02791       for (j = 0; j < appliedfmts.size(); ++j) {
02792         if (i >= appliedfmts[j].start + appliedfmts[j].len) {
02793           // this format no longer applies
02794           appliedfmts.removeAt(j);
02795           --j;
02796           continue;
02797         }
02798         f.merge(appliedfmts[j].fmt);
02799       }
02800       cur.insertText(Qt::escape(text[i]), f);
02801     }
02802 
02803     QSizeF s = textDocument.size();
02804     QRectF textRect = p->innerRectText;
02805     // note: setLeft changes width, not right.
02806     textRect.setLeft(textRect.left()-4); // some offset because of qt's rich text drawing ..?
02807     // textRect is now correct
02808     // draw a "hidden children selection" marker, if needed
02809     if (flags & PTF_SelUnderline) {
02810        QColor h1 = p->option->palette.color(QPalette::Highlight);
02811        QColor h2 = h1;
02812        h1.setAlpha(0);
02813        h2.setAlpha(220);
02814        QLinearGradient gr(0.f, 0.f, 0.f, 1.f);
02815        gr.setCoordinateMode(QGradient::ObjectBoundingMode);
02816        gr.setColorAt(0.f, h1);
02817        gr.setColorAt(1.f, h2);
02818        QBrush selbrush(gr);
02819        p->p->save();
02820        //       p->p->setClipRect(p->option->rect);
02821        p->p->fillRect(QRectF(textRect.left(), textRect.bottom()-10,
02822                              textRect.width(), 10), selbrush);
02823        p->p->restore();
02824     }
02825     // check the text size
02826     drawnTextWidth = (int)s.width();
02827     if (s.width() > textRect.width()) { // sorry, too large
02828       s.setWidth(textRect.width());
02829     }
02830     p->p->save();
02831     p->p->setClipRect(textRect);
02832     if (s.height() > textRect.height()) { // contents height exceeds available height
02833       klfDbg("Need borderfadepen for (rich) text "<<textDocument.toHtml());
02834       p->p->setPen(borderfadepen);
02835     } else {
02836       p->p->setPen(normalpen);
02837     }
02838     p->p->translate(textRect.topLeft());
02839     p->p->translate( QPointF( 0, //textRect.width() - s.width(),
02840                               textRect.height() - s.height()) / 2.f );
02841     textDocument.drawContents(p->p);
02842     p->p->restore();
02843     drawnBaseLineY = (int)(textRect.bottom() - (textRect.height() - s.height()) / 2.f);
02844   }
02845 
02846   // draw arrow if text too large
02847 
02848   if (drawnTextWidth > p->innerRectText.width()) {
02849     // draw small arrow indicating more text
02850     //      c.setAlpha(80);
02851     p->p->save();
02852     p->p->translate(p->option->rect.right()-2, drawnBaseLineY-2);
02853     p->p->setPen(textcol);
02854     p->p->drawLine(0, 0, -16, 0);
02855     p->p->drawLine(0, 0, -2, +2);
02856     p->p->restore();
02857   }
02858 }
02859 
02860 void KLFLibViewDelegate::setEditorData(QWidget */*editor*/, const QModelIndex& /*index*/) const
02861 {
02862 }
02863 void KLFLibViewDelegate::setModelData(QWidget */*editor*/, QAbstractItemModel */*model*/,
02864                                       const QModelIndex& /*index*/) const
02865 {
02866 }
02867 QSize KLFLibViewDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
02868 {
02869   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02870   klfDbg( "\tindex="<<index ) ;
02871   int kind = index.data(KLFLibModel::ItemKindItemRole).toInt();
02872   if (kind == KLFLibModel::EntryKind) {
02873     int prop = -1;
02874     switch (index.data(KLFLibModel::EntryContentsTypeItemRole).toInt()) {
02875     case KLFLibEntry::Latex: prop = KLFLibEntry::Latex;
02876     case KLFLibEntry::Category: prop = (prop > 0) ? prop : KLFLibEntry::Category;
02877     case KLFLibEntry::Tags: prop = (prop > 0) ? prop : KLFLibEntry::Tags;
02878       return QFontMetrics(option.font)
02879         .size(0, index.data(KLFLibModel::entryItemRole(prop)).toString())+QSize(4,4);
02880     case KLFLibEntry::DateTime:
02881       { QLocale loc; // use default locale, which was set to the value of klfconfig.UI.locale;
02882         return QFontMetrics(option.font)
02883           .size(0, loc.toString(index.data(KLFLibModel::entryItemRole(KLFLibEntry::DateTime))
02884                                 .toDateTime(), QLocale::LongFormat) )+QSize(4,2);
02885       }
02886     case KLFLibEntry::Preview:
02887       {
02888         QSize s = index.data(KLFLibModel::entryItemRole(KLFLibEntry::PreviewSize)).value<QSize>() + QSize(4,3);
02889         if (s.width() > pPreviewSize.width() || s.height() > pPreviewSize.height()) {
02890           // shrink to the requested display size, keeping aspect ratio
02891           s.scale(pPreviewSize, Qt::KeepAspectRatio);
02892         }
02893         return s+QSize(2,2); // scaling margin etc.
02894       }
02895     default:
02896       return QSize();
02897     }
02898   } else if (kind == KLFLibModel::CategoryLabelKind) {
02899     return QFontMetrics(option.font)
02900       .size(0, index.data(KLFLibModel::CategoryLabelItemRole).toString())+QSize(4,4);
02901   } else {
02902     qWarning("KLFLibItemViewDelegate::sizeHint(): Bad Item kind: %d\n", kind);
02903     return QSize();
02904   }
02905 }
02906 void KLFLibViewDelegate::updateEditorGeometry(QWidget */*editor*/,
02907                                               const QStyleOptionViewItem& /*option*/,
02908                                               const QModelIndex& /*index*/) const
02909 {
02910 }
02911 
02912 
02913 bool KLFLibViewDelegate::indexHasSelectedDescendant(const QModelIndex& parent) const
02914 {
02915   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02916   klfDbg( "\t parent="<<parent ) ;
02917 
02918   if (!parent.isValid())
02919     return false;
02920 
02921   QTime tm; tm.start();
02922   return func_indexHasSelectedDescendant(parent, tm, 200);
02923 }
02924 
02925 bool KLFLibViewDelegate::selectionIntersectsIndexChildren(const QItemSelection& selection,
02926                                                           const QModelIndex& parent) const
02927 {
02928   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02929   klfDbg( "selection size is "<<selection.size() ) ;
02930   int k;
02931   for (k = 0; k < selection.size(); ++k) {
02932     if (selection[k].parent() != parent)
02933       continue;
02934     if (selection[k].isValid()) // selection range not empty, with same parent
02935       return true;
02936   }
02937   return false;
02938 }
02939 
02940 bool KLFLibViewDelegate::func_indexHasSelectedDescendant(const QModelIndex& parent, const QTime& timer,
02941                                                          int timeLimitMs) const
02942 {
02943   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02944   if (!parent.isValid()) {
02945     qWarning()<<KLF_FUNC_NAME<<": parent is invalid!";
02946     return false;
02947   }
02948   if (selectionIntersectsIndexChildren(pSelModel->selection(), parent)) {
02949     klfDbg( "selection under index parent="<<parent ) ;
02950     return true;
02951   }
02952   const QAbstractItemModel *model = parent.model();
02953   if (model == NULL) {
02954     qWarning()<<KLF_FUNC_NAME<<": parent has NULL model!";
02955     return false;
02956   }
02957 
02958   int k;
02959   QModelIndex idx;
02960   for (k = 0; k < model->rowCount(parent); ++k) {
02961     idx = model->index(k, 0, parent);
02962 
02963     if (!idx.isValid() || !model->hasChildren(idx))
02964       continue;
02965     if (func_indexHasSelectedDescendant(idx, timer, timeLimitMs))
02966       return true;
02967     // stop this function if it is taking too long.
02968     if (timer.elapsed() > timeLimitMs)
02969       return false;
02970 
02971   }
02972   return false;
02973 }
02974 
02975 
02976 
02977 
02978 // -------------------------------------------------------
02979 
02980 
02981 
02982 KLFLibDefaultView::KLFLibDefaultView(QWidget *parent, ViewType view)
02983   : KLFAbstractLibView(parent), pViewType(view)
02984 {
02985   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
02986 
02987   pModel = NULL;
02988 
02989   pPreviewSizeMenu = NULL;
02990 
02991   pEventFilterNoRecurse = false;
02992 
02993   QVBoxLayout *lyt = new QVBoxLayout(this);
02994   lyt->setMargin(0);
02995   lyt->setSpacing(0);
02996   pDelegate = new KLFLibViewDelegate(this);
02997 
02998   // set icon size
02999   switch (pViewType) {
03000   case CategoryTreeView:
03001     pDelegate->setPreviewSize(klfconfig.UI.labelOutputFixedSize*klfconfig.LibraryBrowser.treePreviewSizePercent/100.0);
03002     break;
03003   case ListTreeView:
03004     pDelegate->setPreviewSize(klfconfig.UI.labelOutputFixedSize*klfconfig.LibraryBrowser.listPreviewSizePercent/100.0);
03005     break;
03006   case IconView:
03007     pDelegate->setPreviewSize(klfconfig.UI.labelOutputFixedSize*klfconfig.LibraryBrowser.iconPreviewSizePercent/100.0);
03008     break;
03009   default:
03010     break;
03011   }
03012 
03013   setFocusPolicy(Qt::NoFocus);
03014 
03015   KLFLibDefTreeView *treeView = NULL;
03016   KLFLibDefListView *listView = NULL;
03017   switch (pViewType) {
03018   case IconView:
03019     listView = new KLFLibDefListView(this);
03020     klfDbg( "Created list view." ) ;
03021     listView->setViewMode(QListView::IconMode);
03022     listView->setSpacing(15);
03023     listView->setMovement(QListView::Free);
03024     // icon view flow is set later with setIconViewFlow()
03025     listView->setResizeMode(QListView::Adjust);
03026     klfDbg( "prepared list view." ) ;
03027     pView = listView;
03028     break;
03029   case CategoryTreeView:
03030   case ListTreeView:
03031   default:
03032     treeView = new KLFLibDefTreeView(this);
03033     treeView->setSortingEnabled(true);
03034     treeView->setIndentation(16);
03035     treeView->setAllColumnsShowFocus(true);
03036     //    treeView->setUniformRowHeights(true);   // optimization, but is ugly...
03037     pDelegate->setTheTreeView(treeView);
03038     pView = treeView;
03039     break;
03040   };
03041 
03042   lyt->addWidget(pView);
03043 
03044 
03045   pView->setSelectionBehavior(QAbstractItemView::SelectRows);
03046   pView->setSelectionMode(QAbstractItemView::ExtendedSelection);
03047   pView->setDragEnabled(true);
03048   pView->setDragDropMode(QAbstractItemView::DragDrop);
03049   pView->setDragDropOverwriteMode(false);
03050   pView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
03051   pView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
03052   pView->setItemDelegate(pDelegate);
03053   pView->viewport()->installEventFilter(this);
03054   pView->installEventFilter(this);
03055   installEventFilter(this);
03056 
03057   connect(pView, SIGNAL(clicked(const QModelIndex&)),
03058           this, SLOT(slotViewItemClicked(const QModelIndex&)));
03059   connect(pView, SIGNAL(doubleClicked(const QModelIndex&)),
03060           this, SLOT(slotEntryDoubleClicked(const QModelIndex&)));
03061 }
03062 KLFLibDefaultView::~KLFLibDefaultView()
03063 {
03064 }
03065 
03066 QUrl KLFLibDefaultView::url() const
03067 {
03068   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
03069 
03070   if (pModel == NULL)
03071     return QUrl();
03072   return pModel->url();
03073 }
03074 uint KLFLibDefaultView::compareUrlTo(const QUrl& other, uint interestFlags) const
03075 {
03076   bool baseequal = false;
03077   uint resultFlags = 0x0;
03078 
03079   // see if base resources are equal
03080   baseequal = resourceEngine()->compareUrlTo(other, KlfUrlCompareBaseEqual);
03081   if (baseequal)
03082     resultFlags |= KlfUrlCompareBaseEqual;
03083 
03084   // perform inclusion checks
03085   if (interestFlags & KlfUrlCompareLessSpecific) {
03086     if (!baseequal) {
03087       // aren't less specific since not base equal.
03088     } else {
03089       // we can only "see" sub-resources, compare for that only
03090       // "less specific" -> our sub-resource (if we have one) must equal theirs; if we don't have one we're fine.
03091       // --> they must support sub-resources if we do, and display one
03092       if ( ! (resourceEngine()->supportedFeatureFlags() & KLFLibResourceEngine::FeatureSubResources) ) {
03093         resultFlags |= KlfUrlCompareLessSpecific;
03094       } else if (other.hasQueryItem("klfDefaultSubResource")) {
03095         if (resourceEngine()->compareDefaultSubResourceEquals(other.queryItemValue("klfDefaultSubResource")))
03096           resultFlags |= KlfUrlCompareLessSpecific;
03097       }
03098     }
03099   }
03100   if (interestFlags & KlfUrlCompareMoreSpecific) {
03101     if (!baseequal) {
03102       // aren't more specific since not base equal.
03103     } else {
03104       // we can only "see" sub-resources, compare for that only
03105       // more spec.
03106       // -> if other doesn't have sub-resource, we're fine
03107       // -> if does, if (we not) { fail; } else { compare equality }
03108       if (!other.hasQueryItem("klfDefaultSubResource")) {
03109         resultFlags |= KlfUrlCompareMoreSpecific;
03110       } else if (! (resourceEngine()->supportedFeatureFlags() & KLFLibResourceEngine::FeatureSubResources)) {
03111         // fail
03112       } else {
03113         // both support sub-resources, compare equality
03114         if (resourceEngine()->compareDefaultSubResourceEquals(other.queryItemValue("klfDefaultSubResource")))
03115           resultFlags |= KlfUrlCompareMoreSpecific;
03116       }
03117     } 
03118   }
03119   if (interestFlags & KlfUrlCompareEqual) {
03120     bool wesupportsubres = (resourceEngine()->supportedFeatureFlags() & KLFLibResourceEngine::FeatureSubResources);
03121     bool hesupportssubres = other.hasQueryItem("klfDefaultSubResource");
03122     if ( wesupportsubres && hesupportssubres ) {
03123       // both have sub-resources
03124       if (baseequal && resourceEngine()->compareDefaultSubResourceEquals(other.queryItemValue("klfDefaultSubResource")))
03125         resultFlags |= KlfUrlCompareEqual;
03126     } else if ( !wesupportsubres && ! hesupportssubres ) {
03127       // both don't have sub-resources, so we're "equal"
03128       resultFlags |= KlfUrlCompareEqual;
03129     } else {
03130       // otherwise, not equal
03131     }
03132   }
03133 
03134   klfDbg( "and the final resultFlags are"<<klfFmtCC("%#010x",resultFlags) ) ;
03135 
03136   return resultFlags;
03137 }
03138 
03139 class __klf_guarded_bool {
03140   bool *x;
03141 public:
03142   __klf_guarded_bool(bool *var) : x(var) { *x = true; }
03143   ~__klf_guarded_bool() { *x = false; }
03144 };
03145 
03146 bool KLFLibDefaultView::event(QEvent *event)
03147 {
03148   return KLFAbstractLibView::event(event);
03149 }
03150 bool KLFLibDefaultView::eventFilter(QObject *object, QEvent *event)
03151 {
03152   if (pEventFilterNoRecurse) {
03153     klfDbg("Avoiding recursion") ;
03154     return KLFAbstractLibView::eventFilter(object, event);
03155   }
03156   __klf_guarded_bool guard_object(&pEventFilterNoRecurse);
03157 
03158   if (object == pView && event->type() == QEvent::KeyPress) {
03159     QKeyEvent *ke = (QKeyEvent*)event;
03160     QKeySequence thisKey = QKeySequence(ke->key() | ke->modifiers());
03161     int k;
03162     for (k = 0; k < pViewActionsWithShortcut.size(); ++k) {
03163       QAction *a = pViewActionsWithShortcut[k];
03164       if (a->shortcut() == thisKey) {
03165         klfDbg("Activating view action "<<a->text()<<" for shortcut key "<<thisKey<<".") ;
03166         a->trigger();
03167         return true;
03168       }
03169     }
03170   }
03171   return KLFAbstractLibView::eventFilter(object, event);
03172 }
03173 
03174 QList<KLFLib::entryId> KLFLibDefaultView::selectedEntryIds() const
03175 {
03176   QList<KLFLib::entryId> idListWithDupl = pModel->entryIdForIndexList(pView->selectionModel()->selectedIndexes());
03177   // remove duplicates
03178   QList<KLFLib::entryId> idList;
03179   int k;
03180   for (k = 0; k < idListWithDupl.size(); ++k) {
03181     if (!idList.contains(idListWithDupl[k]))
03182       idList << idListWithDupl[k];
03183   }
03184   return idList;
03185 }
03186 
03187 KLFLibEntryList KLFLibDefaultView::selectedEntries() const
03188 {
03189   QModelIndexList selectedindexes = selectedEntryIndexes();
03190   KLFLibEntryList elist;
03191   // fill the entry list with our selected entries
03192   int k;
03193   for (k = 0; k < selectedindexes.size(); ++k) {
03194     if ( selectedindexes[k].data(KLFLibModel::ItemKindItemRole) != KLFLibModel::EntryKind )
03195       continue;
03196     KLFLibEntry entry = selectedindexes[k].data(KLFLibModel::FullEntryItemRole).value<KLFLibEntry>();
03197     klfDbg(  "selection list: adding item [latex="<<entry.latex()<<"; tags="<<entry.tags()<<"]" ) ;
03198     elist << entry;
03199   }
03200   return elist;
03201 }
03202 
03203 QList<QAction*> KLFLibDefaultView::addContextMenuActions(const QPoint& /*pos*/)
03204 {
03205   QList<QAction*> actionList = QList<QAction*>() << pCommonActions;
03206   if (pViewType == IconView) {
03207     actionList << pIconViewActions;
03208   }
03209   if (pViewType == CategoryTreeView || pViewType == ListTreeView) {
03210     actionList << pShowColumnActions;
03211   }
03212   return actionList;
03213 }
03214 
03215 
03216 QVariantMap KLFLibDefaultView::saveGuiState() const
03217 {
03218   QVariantMap vst;
03219   if (pViewType == CategoryTreeView || pViewType == ListTreeView) {
03220     QTreeView *tv = qobject_cast<QTreeView*>(pView);
03221     KLF_ASSERT_NOT_NULL( tv, "Tree View is NULL in view type "<<pViewType<<" !!", return QVariantMap() )
03222       ;
03223     vst["ColumnsState"] = QVariant::fromValue<QByteArray>(tv->header()->saveState());
03224   }
03225   if (pViewType == IconView) {
03226     // nothing to be done
03227   }
03228   vst["IconPreviewSize"] = QVariant::fromValue<QSize>(previewSize());
03229   return vst;
03230 }
03231 bool KLFLibDefaultView::restoreGuiState(const QVariantMap& vstate)
03232 {
03233   if (pViewType == CategoryTreeView || pViewType == ListTreeView) {
03234     QByteArray colstate = vstate["ColumnsState"].toByteArray();
03235     QTreeView *tv = qobject_cast<QTreeView*>(pView);
03236     KLF_ASSERT_NOT_NULL( tv, "Tree View is NULL in view type"<<pViewType<<"!!", return false )
03237       ;
03238     tv->header()->restoreState(colstate);
03239   }
03240   if (pViewType == IconView) {
03241     // nothing to be done
03242   }
03243   setPreviewSize(vstate["IconPreviewSize"].value<QSize>());
03244   return true;
03245 }
03246 
03247 QModelIndex KLFLibDefaultView::currentVisibleIndex() const
03248 {
03249   QModelIndex index;
03250   if (pViewType == IconView) {
03251     KLFLibDefListView *lv = qobject_cast<KLFLibDefListView*>(pView);
03252     KLF_ASSERT_NOT_NULL( lv, "KLFLibDefListView List View is NULL in view type "<<pViewType<<" !!",
03253                          return QModelIndex() )
03254       ;
03255     index = lv->curVisibleIndex();
03256   } else if (pViewType == CategoryTreeView || pViewType == ListTreeView) {
03257     KLFLibDefTreeView *tv = qobject_cast<KLFLibDefTreeView*>(pView);
03258     KLF_ASSERT_NOT_NULL( tv, "KLFLibDefTreeView List View is NULL in view type "<<pViewType<<" !!",
03259                          return QModelIndex() )
03260       ;
03261     index = tv->curVisibleIndex();
03262   } else {
03263     index = QModelIndex();
03264   }
03265   return index;
03266 }
03267 
03268 
03269 #ifdef KLF_DEBUG_USE_MODELTEST
03270 #include <modeltest.h>
03271 #endif
03272 
03273 void KLFLibDefaultView::updateResourceEngine()
03274 {
03275   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
03276 
03277   int k;
03278   KLFLibResourceEngine *resource = resourceEngine();
03279   if (resource == NULL) {
03280     pModel = NULL;
03281     pView->setModel(NULL);
03282     for (k = 0; k < pShowColumnActions.size(); ++k)
03283       delete pShowColumnActions[k];
03284     pShowColumnActions.clear();
03285   }
03286 
03287   //  klfDbg(  "KLFLibDefaultView::updateResourceEngine: All items:\n"<<resource->allEntries() ) ;
03288   uint model_flavor = 0;
03289   switch (pViewType) {
03290   case IconView:
03291   case ListTreeView:
03292     model_flavor = KLFLibModel::LinearList;
03293     break;
03294   case CategoryTreeView:
03295   default:
03296     model_flavor = KLFLibModel::CategoryTree;
03297     break;
03298   };
03299 
03300   // refresh this setting from klfconfig
03301   setGroupSubCategories(klfconfig.LibraryBrowser.groupSubCategories);
03302 
03303   if (pGroupSubCategories)
03304     model_flavor |= KLFLibModel::GroupSubCategories;
03305 
03306   pModel = new KLFLibModel(resource, model_flavor, this);
03307   pView->setModel(pModel);
03308 
03309   KLF_DEBUG_ASSIGN_SAME_REF_INSTANCE(pModel) ;
03310 
03311   klfDbg("created model. pModel="<<klfFmtCC("%p", (void*)pModel)<<"; view type="<<pViewType);
03312 
03313 #ifdef KLF_DEBUG_USE_MODELTEST
03314   new ModelTest(pModel, this);
03315 #endif
03316 
03317   if (pViewType == IconView) {
03318     qobject_cast<KLFLibDefListView*>(pView)->modelInitialized();
03319   } else {
03320     qobject_cast<KLFLibDefTreeView*>(pView)->modelInitialized();
03321   }
03322 
03323   // get informed about selections
03324   QItemSelectionModel *s = pView->selectionModel();
03325   connect(s, SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
03326           this, SLOT(slotViewSelectionChanged(const QItemSelection&, const QItemSelection&)));
03327 
03328   connect(pModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
03329           this, SLOT(slotResourceDataChanged(const QModelIndex&, const QModelIndex&)));
03330 
03331   // reflect pModel's request for progress reporting
03332   connect(pModel, SIGNAL(operationStartReportingProgress(KLFProgressReporter *, const QString&)),
03333           this, SIGNAL(operationStartReportingProgress(KLFProgressReporter *, const QString&)));
03334 
03335 
03336   // delegate wants to know more about selections...
03337   pDelegate->setSelectionModel(s);
03338 
03339   QKeySequence selectAllKey = QKeySequence::SelectAll;
03340   QKeySequence refreshKey = QKeySequence::Refresh;
03341   QAction *selectAllAction = new QAction(tr("Select All", "[[menu action]]"), pView);
03342   selectAllAction->setShortcut(selectAllKey);
03343   connect(selectAllAction, SIGNAL(triggered()), this, SLOT(slotSelectAll()));
03344   QAction *refreshAction = new QAction(tr("Refresh", "[[menu action]]"), pView);
03345   refreshAction->setShortcut(refreshKey);
03346   connect(refreshAction, SIGNAL(triggered()), this, SLOT(slotRefresh()));
03347 
03348   QActionGroup * ag = new QActionGroup(pView);
03349   ag->setExclusive(true);
03350   QAction *aPreviewSizeLarge = new QAction(tr("Large", "[[icon preview size menu item]]"), ag);
03351   aPreviewSizeLarge->setCheckable(true);
03352   aPreviewSizeLarge->setData(100);
03353   connect(aPreviewSizeLarge, SIGNAL(triggered()), this, SLOT(slotPreviewSizeFromActionSender()));
03354   QAction *aPreviewSizeMedium = new QAction(tr("Medium", "[[icon preview size menu item]]"), ag);
03355   aPreviewSizeMedium->setCheckable(true);
03356   aPreviewSizeMedium->setData(75);
03357   connect(aPreviewSizeMedium, SIGNAL(triggered()), this, SLOT(slotPreviewSizeFromActionSender()));
03358   QAction *aPreviewSizeSmall = new QAction(tr("Small", "[[icon preview size menu item]]"), ag);
03359   aPreviewSizeSmall->setCheckable(true);
03360   aPreviewSizeSmall->setData(50);
03361   connect(aPreviewSizeSmall, SIGNAL(triggered()), this, SLOT(slotPreviewSizeFromActionSender()));
03362 
03363   pPreviewSizeMenu = new QMenu(this);
03364   pPreviewSizeMenu->addAction(aPreviewSizeLarge);
03365   pPreviewSizeMenu->addAction(aPreviewSizeMedium);
03366   pPreviewSizeMenu->addAction(aPreviewSizeSmall);
03367 
03368   QAction *aPreviewSize = new QAction(tr("Icon Size", "[[icon preview size option menu]]"), pView);
03369   aPreviewSize->setMenu(pPreviewSizeMenu);
03370 
03371   slotPreviewSizeActionsRefreshChecked();
03372 
03373   pCommonActions = QList<QAction*>() << selectAllAction << refreshAction << aPreviewSize;
03374   pViewActionsWithShortcut << selectAllAction << refreshAction;
03375 
03376   if (pViewType == IconView) {
03377     klfDbg( "About to prepare iconview." ) ;
03378     QAction * iconViewRelayoutAction = new QAction(tr("Relayout All Icons", "[[menu action]]"), this);
03379     connect(iconViewRelayoutAction, SIGNAL(triggered()), this, SLOT(slotRelayoutIcons()));
03380     pIconViewActions = QList<QAction*>() << iconViewRelayoutAction ;
03381   }
03382   if (pViewType == CategoryTreeView || pViewType == ListTreeView) {
03383     QTreeView *treeView = qobject_cast<QTreeView*>(pView);
03384     // select some columns to show
03385     treeView->setColumnHidden(pModel->columnForEntryPropertyId(KLFLibEntry::Preview), false);
03386     treeView->setColumnHidden(pModel->columnForEntryPropertyId(KLFLibEntry::Latex), true);
03387     treeView->setColumnHidden(pModel->columnForEntryPropertyId(KLFLibEntry::Tags), false);
03388     treeView->setColumnHidden(pModel->columnForEntryPropertyId(KLFLibEntry::Category), true);
03389     treeView->setColumnHidden(pModel->columnForEntryPropertyId(KLFLibEntry::DateTime), true);
03390     // optimize column sizes
03391     for (k = 0; k < pModel->columnCount(); ++k)
03392       treeView->resizeColumnToContents(k);
03393     treeView->setColumnWidth(0, 35+treeView->columnWidth(0));
03394     // and provide a menu to show/hide these columns
03395     int col;
03396     QMenu *colMenu = new QMenu(this);
03397     for (col = 0; col < pModel->columnCount(); ++col) {
03398       QString title = pModel->headerData(col, Qt::Horizontal, Qt::DisplayRole).toString();
03399       QAction *a;
03400       a = new QAction(title, this);
03401       a->setProperty("klfModelColumn", col);
03402       a->setCheckable(true);
03403       a->setChecked(!treeView->isColumnHidden(col));
03404       connect(a, SIGNAL(toggled(bool)), this, SLOT(slotShowColumnSenderAction(bool)));
03405       colMenu->addAction(a);
03406     }
03407     QAction *menuAction = new QAction(tr("Show/Hide Columns", "[[menu with sub-menu]]"), this);
03408     menuAction->setMenu(colMenu);
03409     pShowColumnActions = QList<QAction*>() << menuAction;
03410     // expand root items if they contain little number of children
03411     // or if there are little number of root items
03412     int numRootItems = pModel->rowCount(QModelIndex());
03413     for (k = 0; k < pModel->rowCount(QModelIndex()); ++k) {
03414       QModelIndex i = pModel->index(k, 0, QModelIndex());
03415       if (pModel->rowCount(i) < 6 || numRootItems < 6)
03416         treeView->expand(i);
03417     }
03418   }
03419 
03420   updateResourceProp(-1); // update all with respect to resource changes..
03421   updateResourceOwnData(QList<KLFLib::entryId>());
03422 
03423   wantMoreCategorySuggestions();
03424 }
03425 
03426 void KLFLibDefaultView::updateResourceData(const QString& subRes, int modifyType,
03427                                            const QList<KLFLib::entryId>& entryIdList)
03428 {
03429   KLF_ASSERT_NOT_NULL( pModel , "Model is NULL!" , return )
03430     ;
03431   klfDbg("The resource modified its data [type="<<modifyType<<"] in subres="<<subRes<<". Our subres="<<resourceEngine()->defaultSubResource()<<"; matches?="<<resourceEngine()->compareDefaultSubResourceEquals(subRes));
03432   if (!resourceEngine()->compareDefaultSubResourceEquals(subRes))
03433     return;
03434   pModel->updateData(entryIdList, modifyType);
03435   // update our own data (icon positions)
03436   updateResourceOwnData(entryIdList);
03437 }
03438 void KLFLibDefaultView::updateResourceOwnData(const QList<KLFLib::entryId>& /*entryIdList*/)
03439 {
03441   klfDbg( KLF_FUNC_NAME ) ;
03442   if (pViewType == IconView) {
03443     // nothing to do
03444   }
03445 }
03446 void KLFLibDefaultView::updateResourceProp(int propId)
03447 {
03448   klfDbg( "propId="<<propId ) ;
03449 
03450   KLF_ASSERT_NOT_NULL( resourceEngine() , "Resource Engine is NULL, skipping !" , return ) ;
03451 
03452   // ... nothing to be done...
03453 }
03454 
03455 void KLFLibDefaultView::showEvent(QShowEvent *event)
03456 {
03457   if (pModel)
03458     pModel->setFetchBatchCount(80);
03459 }
03460 
03461 
03462 QListView::Flow KLFLibDefaultView::iconViewFlow() const
03463 {
03464   if (pViewType == IconView) {
03465     KLFLibDefListView *lv = qobject_cast<KLFLibDefListView*>(pView);
03466     KLF_ASSERT_NOT_NULL( lv, "KLFLibDefListView List View is NULL in view type "<<pViewType<<" !!",
03467                          return QListView::TopToBottom)
03468       ;
03469     return lv->flow();
03470   }
03471   qWarning()<<KLF_FUNC_NAME<<": requesting icon view flow in the wrong mode `"<<pViewType
03472             <<"'. Should only be called for icon view modes.";
03473   return QListView::TopToBottom; // whatever
03474 }
03475 
03476 
03477 QStringList KLFLibDefaultView::getCategorySuggestions()
03478 {
03479   return pModel->categoryList();
03480 }
03481 
03482 // bool KLFLibDefaultView::writeEntryProperty(int property, const QVariant& value)
03483 // {
03484 //   return pModel->changeEntries(selectedEntryIndexes(), property, value);
03485 // }
03486 // bool KLFLibDefaultView::deleteSelected(bool requireConfirm)
03487 // {
03488 //   QModelIndexList sel = selectedEntryIndexes();
03489 //   if (sel.size() == 0)
03490 //     return true;
03491 //   if (requireConfirm) {
03492 //     QMessageBox::StandardButton res
03493 //       = QMessageBox::question(this, tr("Delete?"),
03494 //                            tr("Delete %n selected item(s) from resource \"%1\"?", "", sel.size())
03495 //                            .arg(pModel->resource()->title()),
03496 //                            QMessageBox::Yes|QMessageBox::Cancel, QMessageBox::Cancel);
03497 //     if (res != QMessageBox::Yes)
03498 //       return false; // abort action
03499 //   }
03500 //   return pModel->deleteEntries(sel);
03501 // }
03502 // bool KLFLibDefaultView::insertEntries(const KLFLibEntryList& entries)
03503 // {
03504 //   return pModel->insertEntries(entries);
03505 // }
03506 bool KLFLibDefaultView::selectEntries(const QList<KLFLib::entryId>& idList)
03507 {
03508   klfDbg("selecting entries: "<<idList) ;
03509 
03510   QModelIndexList mil = pModel->findEntryIdList(idList);
03511   QItemSelection sel;
03512   int k;
03513   for (k = 0; k < mil.size(); ++k)
03514     sel << QItemSelectionRange(mil[k]);
03515 
03516   pView->selectionModel()->select(sel, QItemSelectionModel::ClearAndSelect);
03517 
03518   if (pViewType == CategoryTreeView && pView->inherits("KLFLibDefTreeView")) {
03519     KLFLibDefTreeView *v = qobject_cast<KLFLibDefTreeView*>(pView);
03520     v->ensureExpandedTo(mil);
03521   }
03522   return true;
03523 }
03524 
03525 
03526 
03527 void KLFLibDefaultView::restore(uint restoreflags)
03528 {
03529   QModelIndexList sel = selectedEntryIndexes();
03530   if (sel.size() != 1) {
03531     qWarning("KLFLibDefaultView::restoreWithStyle(): Cannot restore: %d items selected.", sel.size());
03532     return;
03533   }
03534 
03535   KLFLibEntry e = sel[0].data(KLFLibModel::FullEntryItemRole).value<KLFLibEntry>();
03536   
03537   emit requestRestore(e, restoreflags);
03538 }
03539 
03540 void KLFLibDefaultView::showColumns(int propIdColumn, bool show)
03541 {
03542   if ( ! pView->inherits("QTreeView") ) {
03543     qWarning("KLFLibDefaultView::showColumns(%d,%s): Resource view for %s: view does not inherit QTreeView!",
03544              propIdColumn, show?"[show]":"[hide]", qPrintable(resourceEngine()->url().toString()));
03545     return;
03546   }
03547   int colNo = pModel->columnForEntryPropertyId(propIdColumn);
03548   qobject_cast<QTreeView*>(pView)->setColumnHidden(colNo, show);
03549 }
03550 
03551 void KLFLibDefaultView::sortBy(int propIdColumn, Qt::SortOrder sortorder)
03552 {
03553   if ( ! pView->inherits("QTreeView") ) {
03554     qWarning("KLFLibDefaultView::showBy(%d,%s): Resource view for %s: view does not inherit QTreeView!",
03555              propIdColumn, (sortorder == Qt::AscendingOrder)?"[Ascending]":"[Descending]" ,
03556              qPrintable(resourceEngine()->url().toString()));
03557     return;
03558   }
03559   QTreeView * tree = qobject_cast<QTreeView*>(pView);
03560   int colNo = pModel->columnForEntryPropertyId(propIdColumn);
03561   if (colNo < 0 || colNo >= pModel->columnCount()) {
03562     qWarning("KLFLibDefaultView::showBy(%d,%s): column number %d is not valid or hidden!",
03563              propIdColumn, (sortorder == Qt::AscendingOrder)?"[Ascending]":"[Descending]", colNo);
03564     return;
03565   }
03566   tree->sortByColumn(colNo, sortorder);
03567 }
03568 
03569 
03570 void KLFLibDefaultView::slotSelectAll(bool expandItems)
03571 {
03572   slotSelectAll( QModelIndex(), NoSignals | (expandItems?ExpandItems:0) );
03573   updateDisplay();
03574 }
03575 void KLFLibDefaultView::slotSelectAll(const QModelIndex& parent, uint selectAllFlags)
03576 {
03577   KLFDelayedPleaseWaitPopup pleaseWait(tr("Fetching and selecting all, please wait ..."), this);
03578   pleaseWait.setDisableUi(true);
03579   pleaseWait.setDelay(500);
03580 
03581   if (selectAllFlags & NoSignals)
03582     pView->selectionModel()->blockSignals(true);
03583 
03584   QTime tm;
03585   tm.start();
03586   func_selectAll(parent, selectAllFlags, &tm, &pleaseWait);
03587 
03588   if (selectAllFlags & NoSignals)
03589     pView->selectionModel()->blockSignals(false);
03590 
03591   emit entriesSelected(selectedEntries());
03592   updateDisplay();
03593 }
03594 
03595 // If this returns FALSE, then propagate the cancel/error/stop further up in recursion
03596 bool KLFLibDefaultView::func_selectAll(const QModelIndex& parent, uint selectAllFlags, QTime *tm,
03597                                        KLFDelayedPleaseWaitPopup *pleaseWait)
03598 {
03599   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
03600   // this function requires to fetch all items in parent!
03601 
03602   while (pModel->canFetchMore(parent)) {
03603     pModel->fetchMore(parent);
03604     pleaseWait->process();
03605     if (pleaseWait->wasUserDiscarded())
03606       return false;
03607     if (tm->elapsed() > 300) {
03608       // process a few events..
03609       qApp->processEvents();
03610       tm->restart();
03611     }
03612   }
03613 
03614   int k;
03615   QModelIndex topLeft = pModel->index(0, 0, parent);
03616   QModelIndex bottomRight = pModel->index(pModel->rowCount(parent)-1,
03617                                           pModel->columnCount(parent)-1, parent);
03618   pView->selectionModel()->select(QItemSelection(topLeft, bottomRight), QItemSelectionModel::Select);
03619   if ( ! pModel->hasChildren(parent) )
03620     return true;
03621 
03622   if (selectAllFlags & ExpandItems) {
03623     QTreeView *treeView = pView->inherits("QTreeView") ? qobject_cast<QTreeView*>(pView) : NULL;
03624     if (treeView != NULL)
03625       treeView->expand(parent);
03626   }
03627 
03628   for (k = 0; k < pModel->rowCount(parent); ++k) {
03629     QModelIndex child = pModel->index(k, 0, parent);
03630     if ( ! func_selectAll(child, selectAllFlags, tm, pleaseWait) )
03631       return false; // cancel requested eg. by user clicking on the popup
03632     if (tm->elapsed() > 300) {
03633       qApp->processEvents();
03634       pleaseWait->process();
03635       if (pleaseWait->wasUserDiscarded())
03636         return false;
03637       tm->restart();
03638     }
03639   }
03640 
03641   return true;
03642 }
03643 
03644 void KLFLibDefaultView::slotRefresh()
03645 {
03646   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
03647   pModel->completeRefresh();
03648 }
03649 
03650 void KLFLibDefaultView::slotPreviewSizeFromActionSender()
03651 {
03652   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
03653   QAction *a = qobject_cast<QAction*>(sender());
03654   KLF_ASSERT_NOT_NULL(a, "action sender is NULL!", return ; ) ;
03655 
03656   pDelegate->setPreviewSize(klfconfig.UI.labelOutputFixedSize * a->data().toInt() / 100.0);
03657   pView->reset();
03658 
03659   slotPreviewSizeActionsRefreshChecked();
03660 }
03661 
03662 void KLFLibDefaultView::slotPreviewSizeActionsRefreshChecked()
03663 {
03664   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
03665 
03666   KLF_ASSERT_NOT_NULL(pPreviewSizeMenu, "pPreviewSizeMenu is NULL!", return ) ;
03667 
03668   int curPreviewSizePercent
03669     = pDelegate->previewSize().width() * 100 / klfconfig.UI.labelOutputFixedSize.width();
03670   // round off to 5 units (for comparision with some threshold)
03671   curPreviewSizePercent = (int)(curPreviewSizePercent/5 +0.5)*5;
03672 
03673   QList<QAction*> alist = pPreviewSizeMenu->actions();
03674   klfDbg("There are "<<alist.size()<<" actions...") ;
03675   for (QList<QAction*>::iterator it = alist.begin(); it != alist.end(); ++it) {
03676     QAction *a = (*it);
03677     int a_sz = (int)(a->data().toInt()/5 +0.5)*5;
03678     klfDbg("Processing action "<< a->text() << " (data="<<a->data().toInt()<<" ~= "<<a_sz
03679            <<") curPreviewSizePercent="<<curPreviewSizePercent) ;
03680 
03681     if ( a_sz == curPreviewSizePercent )
03682       a->setChecked(true);
03683     else
03684       a->setChecked(false);
03685   }
03686 }
03687 
03688 
03689 void KLFLibDefaultView::slotRelayoutIcons()
03690 {
03691   if (pViewType != IconView || !pView->inherits("KLFLibDefListView")) {
03692     return;
03693   }
03694   KLFLibDefListView *lv = qobject_cast<KLFLibDefListView*>(pView);
03695   // force a re-layout
03696   lv->forceRelayout();
03697 }
03698 
03699 void KLFLibDefaultView::setIconViewFlow(QListView::Flow flow)
03700 {
03701   if (pViewType == IconView) {
03702     KLFLibDefListView *lv = qobject_cast<KLFLibDefListView*>(pView);
03703     KLF_ASSERT_NOT_NULL( lv, "KLFLibDefListView List View is NULL in view type "<<pViewType<<" !!",
03704                          return )
03705       ;
03706     // set the flow
03707     lv->setFlow(flow);
03708   }
03709 }
03710 
03711 void KLFLibDefaultView::updateDisplay()
03712 {
03713   KLF_ASSERT_NOT_NULL(pView, "view is NULL!", return ) ;
03714   pView->viewport()->update();
03715 }
03716 
03717 bool KLFLibDefaultView::searchFind(const QString& queryString, bool forward)
03718 {
03719   QModelIndex fromIndex = currentVisibleIndex();
03720   // take one index above in case it's partially shown
03721   fromIndex = pModel->walkPrevIndex(fromIndex);
03722   QModelIndex i = pModel->searchFind(queryString, fromIndex, forward);
03723   pDelegate->setSearchString(queryString);
03724   //  for (int col = 0; i.isValid() && col < pModel->columnCount(); ++col)
03725   //    pView->update(pModel->index(i.row(), col, i.parent())); // searchFound will call updateDisplay()
03726   searchFound(i);
03727   return i.isValid();
03728 }
03729 
03730 bool KLFLibDefaultView::searchFindNext(bool forward)
03731 {
03732   QModelIndex i = pModel->searchFindNext(forward);
03733   searchFound(i);
03734   return i.isValid();
03735 }
03736 
03737 void KLFLibDefaultView::searchAbort()
03738 {
03739   pModel->searchAbort();
03740   pDelegate->setSearchString(QString());
03741   pDelegate->setSearchIndex(QModelIndex());
03742   updateDisplay(); // repaint widget to update search underline
03743 
03744   // don't un-select the found index...
03745   //  searchFound(QModelIndex());
03746 }
03747 
03748 // private
03749 void KLFLibDefaultView::searchFound(const QModelIndex& i)
03750 {
03751   pDelegate->setSearchIndex(i);
03752   if ( ! i.isValid() ) {
03753     pView->scrollToTop();
03754     // unselect all
03755     pView->selectionModel()->setCurrentIndex(QModelIndex(), QItemSelectionModel::Clear);
03756     return;
03757   } else {
03758     pView->scrollTo(i, QAbstractItemView::EnsureVisible);
03759   }
03760   if (pViewType == CategoryTreeView) {
03761     // if tree view, expand item
03762     qobject_cast<QTreeView*>(pView)->expand(i);
03763   }
03764   // select the item
03765   pView->selectionModel()->setCurrentIndex(i,
03766                                            QItemSelectionModel::ClearAndSelect|
03767                                            QItemSelectionModel::Rows);
03768   updateDisplay();
03769 }
03770 
03771 
03772 void KLFLibDefaultView::slotViewSelectionChanged(const QItemSelection& /*selected*/,
03773                                                  const QItemSelection& /*deselected*/)
03774 {
03775 #ifndef Q_WS_WIN
03776   // This line generates QPaint* warnings on Win32
03777   // This is needed to update the parent items selection indicator
03778   updateDisplay();
03779 #endif
03780   
03781   emit entriesSelected(selectedEntries());
03782 }
03783 
03784 void KLFLibDefaultView::slotResourceDataChanged(const QModelIndex& topLeft,
03785                                                 const QModelIndex& bottomRight)
03786 {
03787   QItemSelectionRange rng = QItemSelectionRange(topLeft, bottomRight);
03788   QModelIndexList indexes = rng.indexes();
03789   QList<KLFLib::entryId> eids = pModel->entryIdForIndexList(indexes);
03790   emit resourceDataChanged(eids);
03791 }
03792 
03793 /*
03794 QItemSelection KLFLibDefaultView::fixSelection(const QModelIndexList& selidx)
03795 {
03796   QModelIndexList moreselidx;
03797   QItemSelection moresel;
03798 
03799   int k, j;
03800   for (k = 0; k < selidx.size(); ++k) {
03801     if (selidx[k].isValid() &&
03802         selidx[k].data(KLFLibModel::ItemKindItemRole).toInt() == KLFLibModel::CategoryLabelKind &&
03803         selidx[k].column() == 0) {
03804       // a category is selected -> select all its children
03805       const QAbstractItemModel *model = selidx[k].model();
03806       int nrows = model->rowCount(selidx[k]);
03807       int ncols = model->columnCount(selidx[k]);
03808       if (pViewType == CategoryTreeView) {
03809         //      // if tree view, expand item
03810         //      // do this delayed, since this function can be called from within a slot
03811         //      QMetaObject::invokeMethod(pView, "expand", Qt::QueuedConnection, Q_ARG(QModelIndex, selidx[k]));
03812       }
03813       for (j = 0; j < nrows; ++j) {
03814         switch (selidx[k].child(j,0).data(KLFLibModel::ItemKindItemRole).toInt()) {
03815         case KLFLibModel::CategoryLabelKind:
03816           moreselidx.append(selidx[k].child(j,0));
03817           // no break; proceed to entrykind->
03818         case KLFLibModel::EntryKind:
03819           moresel.append(QItemSelectionRange(selidx[k].child(j, 0),
03820                                              selidx[k].child(j, ncols-1)));
03821           break;
03822         default: ;
03823         }
03824         
03825       }
03826     }
03827   }
03828 
03829   if (moresel.isEmpty()) {
03830     return QItemSelection();
03831   }
03832 
03833   QItemSelection s = moresel;
03834   QItemSelection morefixed = fixSelection(moreselidx);
03835   s.merge(morefixed, QItemSelectionModel::Select);
03836   return s;
03837 
03838 }
03839 */
03840 
03841 
03842 void KLFLibDefaultView::slotViewItemClicked(const QModelIndex& index)
03843 {
03844   KLF_DEBUG_TIME_BLOCK(KLF_FUNC_NAME) ;
03845 
03846   if (index.column() != 0)
03847     return;
03848 
03849   slotSelectAll(index, ExpandItems);
03850   /*
03851     QItemSelection fixed = fixSelection( QModelIndexList() << index);
03852     // do this delayed, since this function can be called from within a slot
03853     bool result =
03854     QMetaObject::invokeMethod(pView->selectionModel(), "select", Qt::QueuedConnection,
03855     Q_ARG(QItemSelection, fixed),
03856     Q_ARG(QItemSelectionModel::SelectionFlags, QItemSelectionModel::Select));
03857     klfDbg("result of invoked method is "<<result) ;
03858   */
03859 }
03860 void KLFLibDefaultView::slotEntryDoubleClicked(const QModelIndex& index)
03861 {
03862   if (index.data(KLFLibModel::ItemKindItemRole).toInt() != KLFLibModel::EntryKind)
03863     return;
03864   emit requestRestore(index.data(KLFLibModel::FullEntryItemRole).value<KLFLibEntry>(),
03865                       KLFLib::RestoreLatex|KLFLib::RestoreStyle);
03866 }
03867 
03868 // void KLFLibDefaultView::slotExpanded(const QModelIndex& index)
03869 // {
03870 //   pDelegate->setIndexExpanded(index, true);
03871 // }
03872 // void KLFLibDefaultView::slotCollapsed(const QModelIndex& index)
03873 // {
03874 //   pDelegate->setIndexExpanded(index, false);
03875 // }
03876 
03877 void KLFLibDefaultView::slotShowColumnSenderAction(bool showCol)
03878 {
03879   QObject *a = sender();
03880   if (a == NULL)
03881     return;
03882 
03883   if ( ! pView->inherits("QTreeView") )
03884     return;
03885   int colNo = a->property("klfModelColumn").toInt();
03886   qobject_cast<QTreeView*>(pView)->setColumnHidden(colNo, !showCol);
03887 }
03888 
03889 
03890 
03891 
03892 QModelIndexList KLFLibDefaultView::selectedEntryIndexes() const
03893 {
03896   QModelIndexList selection = pView->selectionModel()->selectedIndexes();
03897   QModelIndexList entryindexes;
03898   int k;
03899   QList<KLFLib::entryId> doneEntryIds;
03900   // walk selection and strip all non-zero column number indexes
03901   for (k = 0; k < selection.size(); ++k) {
03902     KLFLib::entryId eid = selection[k].data(KLFLibModel::EntryIdItemRole).toInt();
03903     int iPos = qLowerBound(doneEntryIds.begin(), doneEntryIds.end(), eid) - doneEntryIds.begin();
03904     if ( iPos < doneEntryIds.size() && doneEntryIds[iPos] == eid ) // already in list
03905       continue;
03906     doneEntryIds.insert(iPos, eid);
03907     entryindexes << selection[k];
03908   }
03909   return entryindexes;
03910 }
03911 
03912 
03913 
03914 // ------------
03915 
03916 static QStringList defaultViewTypeIds = QStringList()<<"default"<<"default+list"<<"default+icons";
03917 
03918 
03919 KLFLibDefaultViewFactory::KLFLibDefaultViewFactory(QObject *parent)
03920   : KLFLibViewFactory(defaultViewTypeIds, parent)
03921 {
03922 }
03923 
03924 
03925 QString KLFLibDefaultViewFactory::viewTypeTitle(const QString& viewTypeIdent) const
03926 {
03927   if (viewTypeIdent == "default")
03928     return tr("Category Tree View");
03929   if (viewTypeIdent == "default+list")
03930     return tr("List View");
03931   if (viewTypeIdent == "default+icons")
03932     return tr("Icon View");
03933 
03934   return QString();
03935 }
03936 
03937 
03938 KLFAbstractLibView * KLFLibDefaultViewFactory::createLibView(const QString& viewTypeIdent,
03939                                                              QWidget *parent,
03940                                                              KLFLibResourceEngine *resourceEngine)
03941 {
03942   KLFLibDefaultView::ViewType v = KLFLibDefaultView::CategoryTreeView;
03943   if (viewTypeIdent == "default")
03944     v = KLFLibDefaultView::CategoryTreeView;
03945   else if (viewTypeIdent == "default+list")
03946     v = KLFLibDefaultView::ListTreeView;
03947   else if (viewTypeIdent == "default+icons")
03948     v = KLFLibDefaultView::IconView;
03949 
03950   KLFLibDefaultView *view = new KLFLibDefaultView(parent, v);
03951   view->setResourceEngine(resourceEngine);
03952   return view;
03953 }
03954 
03955 
03956 
03957 // ------------
03958 
03959 
03960 KLFLibOpenResourceDlg::KLFLibOpenResourceDlg(const QUrl& defaultlocation, QWidget *parent)
03961   : QDialog(parent)
03962 {
03963   pUi = new Ui::KLFLibOpenResourceDlg;
03964   pUi->setupUi(this);
03965   setWindowIcon(QPixmap(":/pics/klatexformula-64.png"));
03966 
03967   // check to see which is the default widget to show according to defaultlocation
03968   KLFLibEngineFactory *efactory = NULL;
03969   if (!defaultlocation.isEmpty())
03970     KLFLibEngineFactory::findFactoryFor(defaultlocation.scheme());
03971   QString defaultwtype;
03972   if (efactory == NULL) {
03973     if (!defaultlocation.isEmpty())
03974       qWarning()<<"No Factory for URL "<<defaultlocation<<"'s scheme!";
03975   } else {
03976     defaultwtype = efactory->correspondingWidgetType(defaultlocation.scheme());
03977   }
03978 
03979   // add a widget for all supported widgets
03980   QStringList wtypeList = KLFLibWidgetFactory::allSupportedWTypes();
03981   int k;
03982   for (k = 0; k < wtypeList.size(); ++k) {
03983     qDebug("KLFLibOpenRes.Dlg::[constr.]() Adding widget for wtype %s", qPrintable(wtypeList[k]));
03984     KLFLibWidgetFactory *factory = KLFLibWidgetFactory::findFactoryFor(wtypeList[k]);
03985     QUrl thisdefaultlocation;
03986     if (wtypeList[k] == defaultwtype)
03987       thisdefaultlocation = defaultlocation;
03988     QWidget *openWidget = factory->createPromptUrlWidget(pUi->stkOpenWidgets, wtypeList[k],
03989                                                          thisdefaultlocation);
03990     pUi->stkOpenWidgets->insertWidget(k, openWidget);
03991     pUi->cbxType->insertItem(k, factory->widgetTypeTitle(wtypeList[k]),
03992                              QVariant::fromValue(wtypeList[k]));
03993 
03994     connect(openWidget, SIGNAL(readyToOpen(bool)), this, SLOT(updateReadyToOpenFromSender(bool)));
03995   }
03996 
03997   if (defaultwtype.isEmpty()) {
03998     pUi->cbxType->setCurrentIndex(0);
03999     pUi->stkOpenWidgets->setCurrentIndex(0);
04000   } else {
04001     pUi->cbxType->setCurrentIndex(k = wtypeList.indexOf(defaultwtype));
04002     pUi->stkOpenWidgets->setCurrentIndex(k);
04003   }
04004 
04005   btnGo = pUi->btnBar->button(QDialogButtonBox::Open);
04006 
04007   connect(pUi->cbxType, SIGNAL(activated(int)), this, SLOT(updateReadyToOpen()));
04008   updateReadyToOpen();
04009 }
04010 
04011 KLFLibOpenResourceDlg::~KLFLibOpenResourceDlg()
04012 {
04013   delete pUi;
04014 }
04015 
04016 QUrl KLFLibOpenResourceDlg::retrieveRawUrl() const
04017 {
04018   int k = pUi->cbxType->currentIndex();
04019   QString wtype = pUi->cbxType->itemData(k).toString();
04020   KLFLibWidgetFactory *factory
04021     = KLFLibWidgetFactory::findFactoryFor(wtype);
04022   return factory->retrieveUrlFromWidget(wtype, pUi->stkOpenWidgets->widget(k));
04023 }
04024 
04025 QUrl KLFLibOpenResourceDlg::url() const
04026 {
04027   QUrl url = retrieveRawUrl();
04028   if (url.isEmpty()) {
04029     // empty url means cancel open
04030     return QUrl();
04031   }
04032   if (pUi->chkReadOnly->isChecked())
04033     url.addQueryItem("klfReadOnly", "true");
04034   if (pUi->cbxSubResource->count())
04035     url.addQueryItem("klfDefaultSubResource",
04036                      pUi->cbxSubResource->itemData(pUi->cbxSubResource->currentIndex()).toString());
04037   klfDbg( "Got URL: "<<url ) ;
04038   return url;
04039 }
04040 
04041 void KLFLibOpenResourceDlg::updateReadyToOpenFromSender(bool isready)
04042 {
04043   QObject *w = sender();
04044   // use the widget itself to store the value
04045   w->setProperty("__klflibopenresourcedlg_readyToOpen", isready);
04046   updateReadyToOpen();
04047 }
04048 void KLFLibOpenResourceDlg::updateReadyToOpen()
04049 {
04050   QWidget *w = pUi->stkOpenWidgets->currentWidget();
04051   KLF_ASSERT_NOT_NULL( w, "widget is NULL!",  return ) ;
04052 
04053   bool w_is_ready = w->property("readyToOpen").toBool();
04054   if (!w_is_ready) {
04055     // possibly the widget did not set the 'readyToOpen' property, but emitted its signal
04056     // that must have reached us in the secondary property
04057     QVariant v = w->property("__klflibopenresourcedlg_readyToOpen");
04058     if (v.isValid())
04059       w_is_ready = v.toBool();
04060   }
04061   btnGo->setEnabled(w_is_ready);
04062   // and propose choice of sub-resources
04063   pUi->frmSubResource->show();
04064   pUi->cbxSubResource->clear();
04065   if (!w_is_ready) {
04066     pUi->frmSubResource->hide();
04067   } else {
04068     // we're ready to open, read sub-resource list
04069     QMap<QString,QString> subResMap = KLFLibEngineFactory::listSubResourcesWithTitles(retrieveRawUrl());
04070     if (subResMap.isEmpty()) {
04071       pUi->frmSubResource->hide();
04072     } else {
04073       for (QMap<QString,QString>::const_iterator it = subResMap.begin(); it != subResMap.end(); ++it) {
04074         QString subres = it.key();
04075         QString subrestitle = it.value();
04076         if (subrestitle.isEmpty())
04077           subrestitle = subres;
04078         pUi->cbxSubResource->addItem(subrestitle, QVariant(subres));
04079       }
04080     }
04081   }
04082 }
04083 
04084 // static
04085 QUrl KLFLibOpenResourceDlg::queryOpenResource(const QUrl& defaultlocation, QWidget *parent)
04086 {
04087   KLFLibOpenResourceDlg dlg(defaultlocation, parent);
04088   int result = dlg.exec();
04089   if (result != QDialog::Accepted)
04090     return QUrl();
04091   QUrl url = dlg.url();
04092   return url;
04093 }
04094 
04095 
04096 
04097 // ---------------------
04098 
04099 
04100 KLFLibCreateResourceDlg::KLFLibCreateResourceDlg(const QString& defaultWtype, QWidget *parent)
04101   : QDialog(parent)
04102 {
04103   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
04104 
04105   pUi = new Ui::KLFLibOpenResourceDlg;
04106   pUi->setupUi(this);
04107   setWindowIcon(QPixmap(":/pics/klatexformula-64.png"));
04108 
04109   pUi->lblMain->setText(tr("Create New Library Resource", "[[dialog label title]]"));
04110   setWindowTitle(tr("Create New Library Resource", "[[dialog window title]]"));
04111   pUi->chkReadOnly->hide();
04112 
04113   pUi->cbxSubResource->setEnabled(true);
04114   pUi->cbxSubResource->setEditable(true);
04115   pUi->cbxSubResource->setEditText(tr("SubResource1"));
04116 
04117   pUi->btnBar->setStandardButtons(QDialogButtonBox::Save|QDialogButtonBox::Cancel);
04118   btnGo = pUi->btnBar->button(QDialogButtonBox::Save);
04119 
04120   int defaultIndex = 0;
04121   // add a widget for all supported widget-types
04122   QStringList wtypeList = KLFLibWidgetFactory::allSupportedWTypes();
04123   int k;
04124   for (k = 0; k < wtypeList.size(); ++k) {
04125     KLFLibWidgetFactory *factory
04126       = KLFLibWidgetFactory::findFactoryFor(wtypeList[k]);
04127     QWidget *createResWidget =
04128       factory->createPromptCreateParametersWidget(pUi->stkOpenWidgets, wtypeList[k],
04129                                                   Parameters());
04130     pUi->stkOpenWidgets->insertWidget(k, createResWidget);
04131     pUi->cbxType->insertItem(k, factory->widgetTypeTitle(wtypeList[k]),
04132                              QVariant::fromValue(wtypeList[k]));
04133 
04134     if (wtypeList[k] == defaultWtype)
04135       defaultIndex = k;
04136 
04137     connect(createResWidget, SIGNAL(readyToCreate(bool)),
04138             this, SLOT(updateReadyToCreateFromSender(bool)));
04139     klfDbg("Added create-res-widget of type "<<wtypeList[k]) ;
04140   }
04141 
04142   pUi->cbxType->setCurrentIndex(defaultIndex);
04143   pUi->stkOpenWidgets->setCurrentIndex(defaultIndex);
04144 
04145   connect(pUi->cbxType, SIGNAL(activated(int)), this, SLOT(updateReadyToCreate()));
04146   updateReadyToCreate();
04147 }
04148 KLFLibCreateResourceDlg::~KLFLibCreateResourceDlg()
04149 {
04150   delete pUi;
04151 }
04152 
04153 KLFLibEngineFactory::Parameters KLFLibCreateResourceDlg::getCreateParameters() const
04154 {
04155   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
04156 
04157   int k = pUi->cbxType->currentIndex();
04158   QString wtype = pUi->cbxType->itemData(k).toString();
04159   KLFLibWidgetFactory *factory
04160     = KLFLibWidgetFactory::findFactoryFor(wtype);
04161   Parameters p = factory->retrieveCreateParametersFromWidget(wtype, pUi->stkOpenWidgets->widget(k));
04162   p["klfWidgetType"] = wtype;
04163   p["klfDefaultSubResource"] = pUi->cbxSubResource->currentText();
04164   p["klfDefaultSubResourceTitle"] = pUi->cbxSubResource->currentText();
04165 
04166   klfDbg("Create parameters are: "<<p) ;
04167 
04168   return p;
04169 }
04170 
04171 
04172 void KLFLibCreateResourceDlg::accept()
04173 {
04174   const Parameters p = getCreateParameters();
04175   if (p == Parameters() || p["klfCancel"].toBool() == true) {
04176     QDialog::reject();
04177     return;
04178   }
04179   if (p["klfRetry"].toBool() == true)
04180     return; // ignore accept, reprompt user
04181 
04182   // then by default accept the event
04183   // set internal parameter cache to the given parameters, they
04184   // will be recycled by createResource() instead of being re-queried
04185   pParam = p;
04186   QDialog::accept();
04187 }
04188 void KLFLibCreateResourceDlg::reject()
04189 {
04190   QDialog::reject();
04191 }
04192  
04193 void KLFLibCreateResourceDlg::updateReadyToCreateFromSender(bool isready)
04194 {
04195   QObject *w = sender();
04196   w->setProperty("__klflibcreateresourcedlg_readyToCreate", isready);
04197   updateReadyToCreate();
04198 }
04199 void KLFLibCreateResourceDlg::updateReadyToCreate()
04200 {
04201   QWidget *w = pUi->stkOpenWidgets->currentWidget();
04202   if (w == NULL) return;
04203   QVariant v = w->property("__klflibcreateresourcedlg_readyToCreate");
04204   // and update button enabled. If the widget emitted no signal, then it is
04205   // assumed to be ready, as we have no way of knowing
04206   btnGo->setEnabled(!v.isValid() || v.toBool());
04207 }
04208 
04209 // static
04210 KLFLibResourceEngine *KLFLibCreateResourceDlg::createResource(const QString& defaultWtype,
04211                                                               QObject *resourceParent, QWidget *parent)
04212 {
04213   KLFLibCreateResourceDlg dlg(defaultWtype, parent);
04214   int result = dlg.exec();
04215   if (result != QDialog::Accepted)
04216     return NULL;
04217 
04218   Parameters p = dlg.pParam; // we have access to this private member
04219   QString scheme = p["klfScheme"].toString();
04220 
04221   if (scheme.isEmpty()) {
04222     qWarning()<<"KLFLibCr.Res.Dlg::createRes.(): Widget Type "<<p["klfWidgetType"]
04223               <<" failed to announce what scheme it wanted in p[\"klfScheme\"]!";
04224     return NULL;
04225   }
04226 
04227   KLFLibEngineFactory * factory = KLFLibEngineFactory::findFactoryFor(scheme);
04228   KLF_ASSERT_NOT_NULL( factory ,
04229                        qPrintable(QString("Couldn't find factory for scheme %1 ?!?").arg(scheme)),
04230                        return NULL )
04231     ;
04232 
04233   KLFLibResourceEngine *resource = factory->createResource(scheme, p, resourceParent);
04234   return resource;
04235 }
04236 
04237 
04238 // ---------------------
04239 
04240 
04241 KLFLibResPropEditor::KLFLibResPropEditor(KLFLibResourceEngine *res, QWidget *parent)
04242   : QWidget(parent)
04243 {
04244   U = new Ui::KLFLibResPropEditor;
04245   U->setupUi(this);
04246   setWindowIcon(QPixmap(":/pics/klatexformula-64.png"));
04247 
04248   QPalette pal = U->txtUrl->palette();
04249   pal.setColor(QPalette::Base, pal.color(QPalette::Window));
04250   pal.setColor(QPalette::Background, pal.color(QPalette::Window));
04251   U->txtUrl->setPalette(pal);
04252 
04253   if (res == NULL)
04254     qWarning("KLFLibResPropEditor: NULL Resource! Expect CRASH!");
04255 
04256   pResource = res;
04257 
04258   pSuppSubRes =
04259     (pResource->supportedFeatureFlags() & KLFLibResourceEngine::FeatureSubResources);
04260   pSuppSubResProps =
04261     (pResource->supportedFeatureFlags() & KLFLibResourceEngine::FeatureSubResourceProps) ;
04262 
04263   connect(pResource, SIGNAL(resourcePropertyChanged(int)),
04264           this, SLOT(slotResourcePropertyChanged(int)));
04265   connect(pResource, SIGNAL(subResourcePropertyChanged(const QString&, int)),
04266           this, SLOT(slotSubResourcePropertyChanged(const QString&, int)));
04267 
04268   pPropModel = new QStandardItemModel(this);
04269   pPropModel->setColumnCount(2);
04270   pPropModel->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Value"));
04271   U->tblProperties->setModel(pPropModel);
04272   U->tblProperties->setItemDelegate(new QItemDelegate(this));
04273 
04274   connect(pPropModel, SIGNAL(itemChanged(QStandardItem *)),
04275           this, SLOT(advPropEdited(QStandardItem *)));
04276 
04277   pSubResPropModel = new QStandardItemModel(this);
04278   pSubResPropModel->setColumnCount(2);
04279   pSubResPropModel->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Value"));
04280   U->tblSubResProperties->setModel(pSubResPropModel);
04281   U->tblSubResProperties->setItemDelegate(new QItemDelegate(this));
04282 
04283   connect(pSubResPropModel, SIGNAL(itemChanged(QStandardItem *)),
04284           this, SLOT(advSubResPropEdited(QStandardItem *)));
04285 
04286   U->frmAdvanced->setShown(U->btnAdvanced->isChecked());
04287 
04288   // perform full refresh (resource properties)
04289   updateResourceProperties();
04290   // preform full refresh (sub-resources)
04291   updateSubResources(pResource->defaultSubResource());
04292   // perform full refresh (sub-resource properties)
04293   updateSubResourceProperties();
04294 
04295   connect(U->btnApply, SIGNAL(clicked()), this, SLOT(apply()));
04296 }
04297 
04298 KLFLibResPropEditor::~KLFLibResPropEditor()
04299 {
04300   delete U;
04301 }
04302 
04303 bool KLFLibResPropEditor::apply()
04304 {
04305   bool res = true;
04306 
04307   // set the default sub-resource
04308   if (pResource->supportedFeatureFlags() & KLFLibResourceEngine::FeatureSubResources)
04309     pResource->setDefaultSubResource(curSubResource());
04310 
04311   bool srislocked = false;
04312   if (pSuppSubRes && pSuppSubResProps)
04313     srislocked = pResource->subResourceProperty(curSubResource(),
04314                                                 KLFLibResourceEngine::SubResPropLocked).toBool();
04315 
04316   bool lockmodified = (pResource->locked() != U->chkLocked->isChecked());
04317   bool srlockmodified = false;
04318   if (pSuppSubRes && pSuppSubResProps && srislocked != U->chkSubResLocked->isChecked()) {
04319     srlockmodified = true;
04320   }
04321   bool wantunlock = lockmodified && !U->chkLocked->isChecked();
04322   bool srwantunlock = srlockmodified && !U->chkSubResLocked->isChecked();
04323   bool wantlock = lockmodified && !wantunlock;
04324   bool srwantlock = srlockmodified && !srwantunlock;
04325   bool titlemodified = (pResource->title() != U->txtTitle->text());
04326   bool subrestitlemodified = false;
04327   if (pSuppSubRes && pSuppSubResProps &&
04328       pResource->subResourceProperty(curSubResource(), KLFLibResourceEngine::SubResPropTitle).toString()
04329       != U->txtSubResTitle->text()) {
04330     subrestitlemodified = true;
04331   }
04332 
04333   klfDbg( ": lockmodif="<<lockmodified<<"; srlockmodified="<<srlockmodified
04334           <<"; wantunlock="<<wantunlock<<"; srwantunlock="<<srwantunlock<<"; wantlock="<<wantlock
04335           <<"; srwantlock="<<srwantlock<<"; titlemodif="<<titlemodified
04336           <<"; srtitlemodif="<<subrestitlemodified ) ;
04337 
04338   if ( (pResource->locked() && !lockmodified) ||
04339        (srislocked && !srlockmodified) ) {
04340     // no intent to modify locked state of locked resource
04341     if (titlemodified || subrestitlemodified) {
04342       QMessageBox::critical(this, tr("Error"), tr("Can't rename a locked resource!"));
04343       return false;
04344     } else {
04345       return true; // nothing to apply
04346     }
04347   }
04348   if (wantunlock) {
04349     if ( ! pResource->setLocked(false) ) { // unlock resource before other modifications
04350       res = false;
04351       QMessageBox::critical(this, tr("Error"), tr("Failed to unlock resource."));
04352     }
04353   }
04354   if (srwantunlock) {
04355     if ( ! pResource->setSubResourceProperty(curSubResource(), KLFLibResourceEngine::SubResPropLocked,
04356                                              false) ) { // unlock sub-resource before other modifications
04357       res = false;
04358       QMessageBox::critical(this, tr("Error"), tr("Failed to unlock sub-resource \"%1\".")
04359                             .arg(curSubResource()));
04360     }
04361   }
04362 
04363   QString newTitle = U->txtTitle->text();
04364   QString newSubResTitle = U->txtSubResTitle->text();
04365 
04366   if ( titlemodified && ! pResource->setTitle(newTitle) ) {
04367     res = false;
04368     QMessageBox::critical(this, tr("Error"), tr("Failed to rename resource."));
04369   }
04370 
04371   if ( subrestitlemodified &&
04372        ! pResource->setSubResourceProperty(curSubResource(),
04373                                            KLFLibResourceEngine::SubResPropTitle,
04374                                            newSubResTitle) ) {
04375     res = false;
04376     QMessageBox::critical(this, tr("Error"), tr("Failed to rename sub-resource \"%1\".")
04377                           .arg(curSubResource()));
04378   }
04379 
04380   if (wantlock) {
04381     if ( ! pResource->setLocked(true) ) { // lock resource after other modifications
04382       res = false;
04383       QMessageBox::critical(this, tr("Error"), tr("Failed to lock resource."));
04384     }
04385   }
04386   if (srwantlock) {
04387     if ( ! pResource->setSubResourceProperty(curSubResource(), KLFLibResourceEngine::SubResPropLocked,
04388                                              true) ) { // lock resource after other modifications
04389       res = false;
04390       QMessageBox::critical(this, tr("Error"), tr("Failed to lock sub-resource \"%1\".")
04391                             .arg(curSubResource()));
04392     }
04393   }
04394 
04395   return res;
04396 }
04397 
04398 void KLFLibResPropEditor::on_btnAdvanced_toggled(bool on)
04399 {
04400   // show/hide advanced controls
04401   U->frmAdvanced->setShown(on);
04402   if (U->frmAdvanced->isVisible()) {
04403     // adjust size of columns
04404     int w = width() / 3;
04405     U->tblProperties->setColumnWidth(0, w);
04406     U->tblProperties->setColumnWidth(1, w);
04407     U->tblSubResProperties->setColumnWidth(0, w);
04408     U->tblSubResProperties->setColumnWidth(1, w);
04409   }
04410   update();
04411   adjustSize();
04412   if (parentWidget()) {
04413     klfDbg( "Parent widget is "<<parentWidget() ) ;
04414     parentWidget()->update();
04415     parentWidget()->adjustSize();
04416   }
04417 }
04418 
04419 void KLFLibResPropEditor::updateSubResources(const QString& curSubRes)
04420 {
04421   klfDbg( "KLFLibResPropEditor::updateSubResources("<<curSubRes<<")" ) ;
04422   if ( pSuppSubRes ) {
04423     U->cbxSubResource->blockSignals(true);
04424     U->cbxSubResource->clear();
04425     QStringList subResList = pResource->subResourceList();
04426     int k;
04427     int curSubResIndex = 0;
04428     for (k = 0; k < subResList.size(); ++k) {
04429       QString title;
04430       QString thissrtitle
04431         = pResource->subResourceProperty(subResList[k], KLFLibResourceEngine::SubResPropTitle).toString();
04432       if (!thissrtitle.isEmpty())
04433         title = QString("%1 (%2)").arg(thissrtitle, subResList[k]);
04434       else
04435         title = subResList[k];
04436       U->cbxSubResource->addItem(title, subResList[k]);
04437       if (subResList[k] == curSubRes)
04438         curSubResIndex = k;
04439     }
04440     klfDbg( "KLFLibResPropEditor::updateSubResources("<<curSubRes<<") : setting cur index="<<curSubResIndex ) ;
04441     U->cbxSubResource->setCurrentIndex(curSubResIndex);
04442     U->cbxSubResource->blockSignals(false);
04443     if ( pSuppSubResProps ) {
04444       U->wSubResProps->show();
04445       U->txtSubResTitle->setEnabled(true);
04446       U->chkSubResLocked->setEnabled(true);
04447       slotSubResourcePropertyChanged(curSubResource(), -2);
04448       U->txtSubResTitle->setEnabled(pResource
04449                                     ->canModifySubResourceProperty(curSubResource(),
04450                                                                    KLFLibResourceEngine::SubResPropTitle));
04451       U->txtSubResTitle->setText(pResource->subResourceProperty(curSubResource(),
04452                                                                 KLFLibResourceEngine::SubResPropTitle)
04453                                  .toString());
04454       U->chkSubResLocked->setEnabled(pResource
04455                                      ->canModifySubResourceProperty(curSubResource(),
04456                                                                     KLFLibResourceEngine::SubResPropLocked));
04457       U->chkSubResLocked->setChecked(pResource->subResourceProperty(curSubResource(),
04458                                                                     KLFLibResourceEngine::SubResPropLocked)
04459                                      .toBool());
04460     } else {
04461       U->wSubResProps->hide();
04462       U->chkSubResLocked->setEnabled(false);
04463       U->txtSubResTitle->setText("");
04464       U->txtSubResTitle->setEnabled(false);
04465     }
04466   } else {
04467     U->cbxSubResource->clear();
04468     U->cbxSubResource->setEnabled(false);
04469     U->wSubResProps->hide();
04470     U->chkSubResLocked->setEnabled(false);
04471     U->txtSubResTitle->setText("");
04472     U->txtSubResTitle->setEnabled(false);
04473   }
04474 }
04475 
04476 void KLFLibResPropEditor::advPropEdited(QStandardItem *item)
04477 {
04478   klfDbg( "advPropEdited("<<item<<")" ) ;
04479   QVariant value = item->data(Qt::EditRole);
04480   int propId = item->data(Qt::UserRole).toInt();
04481   bool r = pResource->setResourceProperty(propId, value);
04482   if ( ! r ) {
04483     QMessageBox::critical(this, tr("Error"),
04484                           tr("Failed to set resource property \"%1\".")
04485                           .arg(pResource->propertyNameForId(propId)));
04486   }
04487   // slotResourcePropertyChanged will be called.
04488 }
04489 
04490 void KLFLibResPropEditor::slotResourcePropertyChanged(int /*propId*/)
04491 {
04492   // perform full update
04493   updateResourceProperties();
04494   updateSubResources();
04495 }
04496 void KLFLibResPropEditor::updateResourceProperties()
04497 {
04498   pPropModel->setRowCount(0);
04499   int k;
04500   QStringList props = pResource->registeredPropertyNameList();
04501   QPalette pal = U->tblSubResProperties->palette();
04502   for (k = 0; k < props.size(); ++k) {
04503     QString prop = props[k];
04504     int propId = pResource->propertyIdForName(prop);
04505     QVariant val = pResource->resourceProperty(prop);
04506     QStandardItem *i1 = new QStandardItem(prop);
04507     i1->setEditable(false);
04508     QStandardItem *i2 = new QStandardItem(val.toString());
04509     bool editable = pResource->canModifyProp(propId);
04510     i2->setEditable(editable);
04511     QPalette::ColorGroup cg = editable ? QPalette::Active : QPalette::Disabled;
04512     i2->setForeground(pal.brush(cg, QPalette::Text));
04513     i2->setBackground(pal.brush(cg, QPalette::Base));
04514     i2->setData(val, Qt::EditRole);
04515     i2->setData(propId, Qt::UserRole); // user data is property ID
04516     pPropModel->appendRow(QList<QStandardItem*>() << i1 << i2);
04517   }
04518 
04519   U->txtTitle->setText(pResource->title());
04520   U->txtTitle->setEnabled(pResource->canModifyProp(KLFLibResourceEngine::PropTitle));
04521   QString surl = pResource->url().toString();
04522   if (surl.endsWith("?"))
04523     surl.chop(1);
04524   U->txtUrl->setText(surl);
04525   U->chkLocked->setChecked(pResource->locked());
04526   U->chkLocked->setEnabled(pResource->canModifyProp(KLFLibResourceEngine::PropLocked));
04527 }
04528 
04529 QString KLFLibResPropEditor::curSubResource() const
04530 {
04531   return U->cbxSubResource->itemData(U->cbxSubResource->currentIndex()).toString();
04532 }
04533 
04534 void KLFLibResPropEditor::advSubResPropEdited(QStandardItem *item)
04535 {
04536   klfDbg( "item="<<item<<"" ) ;
04537   QVariant value = item->data(Qt::EditRole);
04538   int propId = item->data(Qt::UserRole).toInt();
04539   bool r = pResource->setSubResourceProperty(curSubResource(), propId, value);
04540   if ( ! r ) {
04541     QMessageBox::critical(this, tr("Error"),
04542                           tr("Failed to set sub-resource \"%1\"'s property \"%2\".")
04543                           .arg(curSubResource(), propId));
04544   }
04545   // slotSubResourcePropertyChanged will be called.
04546 }
04547 void KLFLibResPropEditor::on_cbxSubResource_currentIndexChanged(int newSubResItemIndex)
04548 {
04549   slotSubResourcePropertyChanged(curSubResource(), -2);
04550   U->txtSubResTitle->setText(pResource->subResourceProperty(curSubResource(),
04551                                                             KLFLibResourceEngine::SubResPropTitle)
04552                              .toString());
04553   U->chkSubResLocked->setChecked(pResource->subResourceProperty(curSubResource(),
04554                                                                 KLFLibResourceEngine::SubResPropLocked)
04555                                  .toBool());
04556 }
04557 
04558 void KLFLibResPropEditor::slotSubResourcePropertyChanged(const QString& subResource, int subResPropId)
04559 {
04560   if (subResource != curSubResource())
04561     return;
04562 
04563   if (subResPropId != -2) {
04564     // REALLY full refresh
04565     updateSubResources(curSubResource());
04566     // will call this function again, with subResPropId==-2
04567     // so return here.
04568     return;
04569   }
04570 
04571   if ( ! pSuppSubResProps )
04572     return;
04573 
04574   // perform full update of sub-res prop list
04575   updateSubResourceProperties();
04576 }
04577 void KLFLibResPropEditor::updateSubResourceProperties()
04578 {
04579   // full update of model data
04580   pSubResPropModel->setRowCount(0);
04581   QPalette pal = U->tblSubResProperties->palette();
04582   int k;
04583   QList<int> props = pResource->subResourcePropertyIdList();
04584   for (k = 0; k < props.size(); ++k) {
04585     int propId = props[k];
04586     QVariant val = pResource->subResourceProperty(curSubResource(), propId);
04587     QStandardItem *i1 = new QStandardItem(pResource->subResourcePropertyName(propId));
04588     i1->setEditable(false);
04589     QStandardItem *i2 = new QStandardItem(val.toString());
04590     bool editable = pResource->canModifySubResourceProperty(curSubResource(), propId);
04591     i2->setEditable(editable);
04592     QPalette::ColorGroup cg = editable ? QPalette::Active : QPalette::Disabled;
04593     i2->setForeground(pal.brush(cg, QPalette::Text));
04594     i2->setBackground(pal.brush(cg, QPalette::Base));
04595     i2->setData(val, Qt::EditRole);
04596     i2->setData(propId, Qt::UserRole); // user data is property ID
04597     pSubResPropModel->appendRow(QList<QStandardItem*>() << i1 << i2);
04598   }
04599 }
04600 
04601 
04602 
04603 
04604 
04605 // -------------
04606 
04607 
04608 KLFLibResPropEditorDlg::KLFLibResPropEditorDlg(KLFLibResourceEngine *resource, QWidget *parent)
04609   : QDialog(parent)
04610 {
04611   QVBoxLayout *lyt = new QVBoxLayout(this);
04612 
04613   pEditor = new KLFLibResPropEditor(resource, this);
04614   lyt->addWidget(pEditor);
04615 
04616   QDialogButtonBox *btns = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel,
04617                                                 Qt::Horizontal, this);
04618   lyt->addWidget(btns);
04619 
04620   connect(btns->button(QDialogButtonBox::Ok), SIGNAL(clicked()),
04621           this, SLOT(applyAndClose()));
04622   //  connect(btns->button(QDialogButtonBox::Apply), SIGNAL(clicked()),
04623   //      pEditor, SLOT(apply()));
04624   connect(btns->button(QDialogButtonBox::Cancel), SIGNAL(clicked()),
04625           this, SLOT(cancelAndClose()));
04626   
04627   setModal(false);
04628   setWindowTitle(pEditor->windowTitle());
04629   setAttribute(Qt::WA_DeleteOnClose, true);
04630 }
04631 
04632 KLFLibResPropEditorDlg::~KLFLibResPropEditorDlg()
04633 {
04634 }
04635 
04636 void KLFLibResPropEditorDlg::applyAndClose()
04637 {
04638   if (pEditor->apply()) {
04639     accept();
04640   }
04641 }
04642 
04643 void KLFLibResPropEditorDlg::cancelAndClose()
04644 {
04645   reject();
04646 }
04647 
04648 
04649 // ---------------------------------------------------------------
04650 
04651 KLFLibNewSubResDlg::KLFLibNewSubResDlg(KLFLibResourceEngine *resource, QWidget *parent)
04652   : QDialog(parent), isAutoName(true)
04653 {
04654   u = new Ui::KLFLibNewSubResDlg;
04655   u->setupUi(this);
04656   setWindowIcon(QPixmap(":/pics/klatexformula-64.png"));
04657 
04658   u->lblNoTitle->hide();
04659 
04660   uint fl = resource->supportedFeatureFlags();
04661   if ( (fl & KLFLibResourceEngine::FeatureSubResources) == 0 ) {
04662     u->lblName->setEnabled(false);
04663     u->txtName->setEnabled(false);
04664     u->lblTitle->setEnabled(false);
04665     u->txtTitle->setEnabled(false);
04666     u->btns->button(QDialogButtonBox::Ok)->setEnabled(false);
04667   } else if ( (fl & KLFLibResourceEngine::FeatureSubResourceProps) == 0) {
04668     u->lblTitle->setEnabled(false);
04669     u->txtTitle->setEnabled(false);
04670     u->lblNoTitle->show();
04671   }
04672 
04673   u->lblResource->setText(resource->title());
04674 }
04675 
04676 KLFLibNewSubResDlg::~KLFLibNewSubResDlg()
04677 {
04678 }
04679 
04680 QString KLFLibNewSubResDlg::newSubResourceName() const
04681 {
04682   return u->txtName->text();
04683 }
04684 
04685 QString KLFLibNewSubResDlg::newSubResourceTitle() const
04686 {
04687   return u->txtTitle->text();
04688 }
04689 
04690 // static
04691 QString KLFLibNewSubResDlg::makeSubResInternalName(const QString& title)
04692 {
04693   QString nm = title;
04694   // replace "string of words-hello" with "stringOfWordsHello"
04695   QRegExp rx("(?:\\s|-)([a-z])");
04696   int i;
04697   while ((i = rx.indexIn(nm,i+1)) >= 0) {
04698     QChar c = rx.cap(1).length() ? rx.cap(1)[0] : QChar('_');
04699     nm.replace(i, rx.matchedLength(), c.toUpper());
04700   }
04701   nm.replace(QRegExp("\\s"), "");
04702   nm.replace(QRegExp("[^A-Za-z0-9_]"), "_");
04703   return nm;
04704 }
04705 
04706 void KLFLibNewSubResDlg::on_txtTitle_textChanged(const QString& text)
04707 {
04708   if (isAutoName) {
04709     u->txtName->blockSignals(true);
04710     u->txtName->setText(makeSubResInternalName(text));
04711     u->txtName->blockSignals(false);
04712   }
04713 }
04714 
04715 void KLFLibNewSubResDlg::on_txtName_textChanged(const QString& text)
04716 {
04717   if (!text.isEmpty())
04718     isAutoName = false; // manually set name
04719   else
04720     isAutoName = true; // user erased name, so auto-name again
04721 }
04722 
04723 QString KLFLibNewSubResDlg::createSubResourceIn(KLFLibResourceEngine *resource, QWidget *parent)
04724 {
04725   if ( (resource->supportedFeatureFlags() & KLFLibResourceEngine::FeatureSubResources) == 0 ) {
04726     qWarning("KLFLibNewSubResDlg::createSubResourceIn: can't create sub-resource in resource not "
04727              "supporting sub-resources (!) : %s", qPrintable(resource->url().toString()));
04728     return QString();
04729   }
04730   KLFLibNewSubResDlg d(resource, parent);
04731   int r = d.exec();
04732   if (r != QDialog::Accepted)
04733     return QString();
04734   QString name = d.newSubResourceName();
04735   QString title = d.newSubResourceTitle();
04736 
04737   bool result = resource->createSubResource(name, title);
04738   if (!result)
04739     return QString();
04740 
04741   return name;
04742 }
04743 
04744 // ---------------------------------------------------------------
04745 
04746 KLFLibLocalFileSchemeGuesser::KLFLibLocalFileSchemeGuesser()
04747 {
04748   KLFLibBasicWidgetFactory::addLocalFileSchemeGuesser(this);
04749 }
04750 KLFLibLocalFileSchemeGuesser::~KLFLibLocalFileSchemeGuesser()
04751 {
04752   KLFLibBasicWidgetFactory::removeLocalFileSchemeGuesser(this);
04753 }
04754 
04755 // ---------------------------------------------------------------
04756 
04757 KLFLibBasicWidgetFactory::KLFLibBasicWidgetFactory(QObject *parent)
04758   :  KLFLibWidgetFactory(parent)
04759 {
04760 }
04761 KLFLibBasicWidgetFactory::~KLFLibBasicWidgetFactory()
04762 {
04763 }
04764 
04765 
04766 QStringList KLFLibBasicWidgetFactory::supportedTypes() const
04767 {
04768   return QStringList() << QLatin1String("LocalFile");
04769 }
04770 
04771 
04772 QString KLFLibBasicWidgetFactory::widgetTypeTitle(const QString& wtype) const
04773 {
04774   if (wtype == QLatin1String("LocalFile"))
04775     return tr("Local File");
04776   return QString();
04777 }
04778 
04779 
04780 
04781 QWidget * KLFLibBasicWidgetFactory::createPromptUrlWidget(QWidget *parent, const QString& wtype,
04782                                                           QUrl defaultlocation)
04783 {
04784   if (wtype == QLatin1String("LocalFile")) {
04785     KLFLibLocalFileOpenWidget *w = new KLFLibLocalFileOpenWidget(parent, pLocalFileTypes);
04786     w->setUrl(defaultlocation);
04787     return w;
04788   }
04789   return NULL;
04790 }
04791 
04792 QUrl KLFLibBasicWidgetFactory::retrieveUrlFromWidget(const QString& wtype, QWidget *widget)
04793 {
04794   if (wtype == "LocalFile") {
04795     if (widget == NULL || !widget->inherits("KLFLibLocalFileOpenWidget")) {
04796       qWarning("KLFLibBasicWidgetFactory::retrieveUrlFromWidget(): Bad Widget provided!");
04797       return QUrl();
04798     }
04799     return qobject_cast<KLFLibLocalFileOpenWidget*>(widget)->url();
04800   } else {
04801     qWarning()<<"KLFLibB.W.Factory::retrieveUrlFromWidget(): Bad widget type: "<<wtype;
04802     return QUrl();
04803   }
04804 }
04805 
04806 
04807 QWidget *KLFLibBasicWidgetFactory::createPromptCreateParametersWidget(QWidget *parent,
04808                                                                       const QString& wtype,
04809                                                                       const Parameters& defaultparameters)
04810 {
04811   if (wtype == QLatin1String("LocalFile")) {
04812     KLFLibLocalFileCreateWidget *w = new KLFLibLocalFileCreateWidget(parent, pLocalFileTypes);
04813     w->setUrl(defaultparameters["Url"].toUrl());
04814     return w;
04815   }
04816   return NULL;
04817 }
04818 
04819 KLFLibWidgetFactory::Parameters
04820 /* */ KLFLibBasicWidgetFactory::retrieveCreateParametersFromWidget(const QString& scheme,
04821                                                                    QWidget *widget)
04822 {
04823   if (scheme == QLatin1String("LocalFile")) {
04824     if (widget == NULL || !widget->inherits("KLFLibLocalFileCreateWidget")) {
04825       qWarning("KLFLibBasicWidgetFactory::retrieveUrlFromWidget(): Bad Widget provided!");
04826       return Parameters();
04827     }
04828     KLFLibLocalFileCreateWidget *w = qobject_cast<KLFLibLocalFileCreateWidget*>(widget);
04829     Parameters p;
04830     QString filename = w->selectedFName();
04831     if (QFile::exists(filename) && !w->confirmedOverwrite()) {
04832       QMessageBox::StandardButton result =
04833         QMessageBox::warning(widget, tr("Overwrite?"),
04834                              tr("The specified file already exists. Overwrite it?"),
04835                              QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel,
04836                              QMessageBox::No);
04837       if (result == QMessageBox::No) {
04838         p["klfRetry"] = true;
04839         return p;
04840       } else if (result == QMessageBox::Cancel) {
04841         return Parameters();
04842       }
04843       // remove the previous file, because otherwise KLFLib*Engine may fail on existing files.
04844       bool r = QFile::remove(filename);
04845       if ( !r ) {
04846         QMessageBox::critical(widget, tr("Error"), tr("Failed to overwrite the file %1.")
04847                               .arg(filename));
04848         return Parameters();
04849       }
04850     }
04851 
04852     p["Filename"] = filename;
04853     p["klfScheme"] = w->selectedScheme();
04854     return p;
04855   }
04856 
04857   return Parameters();
04858 }
04859 
04860 // static
04861 void KLFLibBasicWidgetFactory::addLocalFileType(const LocalFileType& f)
04862 {
04863   pLocalFileTypes << f;
04864 }
04865 
04866 // static
04867 QList<KLFLibBasicWidgetFactory::LocalFileType> KLFLibBasicWidgetFactory::localFileTypes()
04868 {
04869   return pLocalFileTypes;
04870 }
04871 
04872 
04873 // static
04874 QString KLFLibBasicWidgetFactory::guessLocalFileScheme(const QString& fileName)
04875 {
04876   int k;
04877   for (k = 0; k < pSchemeGuessers.size(); ++k) {
04878     QString s = pSchemeGuessers[k]->guessScheme(fileName);
04879     if (!s.isEmpty())
04880       return s;
04881   }
04882   return QString();
04883 }
04884 
04885 // static
04886 void KLFLibBasicWidgetFactory::addLocalFileSchemeGuesser(KLFLibLocalFileSchemeGuesser *schemeguesser)
04887 {
04888   pSchemeGuessers << schemeguesser;
04889 }
04890 
04891 // static
04892 void KLFLibBasicWidgetFactory::removeLocalFileSchemeGuesser(KLFLibLocalFileSchemeGuesser *schemeguesser)
04893 {
04894   pSchemeGuessers.removeAll(schemeguesser);
04895 }
04896 
04897 
04898 
04899 // static
04900 QList<KLFLibBasicWidgetFactory::LocalFileType> KLFLibBasicWidgetFactory::pLocalFileTypes ;
04901 // static
04902 QList<KLFLibLocalFileSchemeGuesser*> KLFLibBasicWidgetFactory::pSchemeGuessers ;
04903 

Generated by doxygen 1.7.3