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

Generated by doxygen 1.7.3