File indexing completed on 2024-04-28 16:26:35
0001 /**************************************************************************************** 0002 begin : Fri Aug 1 2003 0003 copyright : (C) 2003 by Jeroen Wijnhout (Jeroen.Wijnhout@kdemail.net) 0004 2006 - 2009 by Thomas Braun 0005 2012 - 2022 by Michel Ludwig (michel.ludwig@kdemail.net) 0006 ****************************************************************************************/ 0007 0008 /*************************************************************************** 0009 * * 0010 * This program is free software; you can redistribute it and/or modify * 0011 * it under the terms of the GNU General Public License as published by * 0012 * the Free Software Foundation; either version 2 of the License, or * 0013 * (at your option) any later version. * 0014 * * 0015 ***************************************************************************/ 0016 0017 /* 0018 dani 2005-11-22 0019 - add some new symbols 0020 - rearranged source 0021 0022 tbraun 2006-07-01 0023 - added tooltips which show the keys, copied from kfileiconview 0024 - reorganized the hole thing, more flexible png loading, removing the old big code_array, more groups 0025 0026 tbraun 2007-06-04 0027 - Send a warning in the logwidget if needed packages are not included for the command 0028 tbraun 2007-06-13 0029 - Added Most frequently used symbolview, including remembering icons upon restart, removing of least popular item and configurable max item count 0030 */ 0031 0032 0033 #include "symbolview.h" 0034 0035 #include <QApplication> 0036 #include <QMouseEvent> 0037 #include <QPixmap> 0038 #include <QPainter> 0039 #include <QRegExp> 0040 #include <QStringList> 0041 #include <QTextDocument> 0042 0043 #include <KColorScheme> 0044 #include <KConfig> 0045 #include <KLocalizedString> 0046 0047 #include "kileconfig.h" 0048 #include "kiledebug.h" 0049 #include "kileinfo.h" 0050 #include "../symbolviewclasses.h" 0051 #include "utilities.h" 0052 0053 #define MFUS_GROUP "MostFrequentlyUsedSymbols" 0054 #define MFUS_PREFIX "MFUS" 0055 0056 0057 namespace KileWidget { 0058 0059 SymbolView::SymbolView(KileInfo *kileInfo, QWidget *parent, int type, const char *name) 0060 : QListWidget(parent), m_ki(kileInfo) 0061 { 0062 setObjectName(name); 0063 setViewMode(IconMode); 0064 setGridSize(QSize(36, 36)); 0065 setSpacing(5); 0066 setWordWrap(false); 0067 setResizeMode(Adjust); 0068 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0069 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0070 setMovement(Static); 0071 setSortingEnabled(false); 0072 setFlow(LeftToRight); 0073 setDragDropMode(NoDragDrop); 0074 m_brush = KStatefulBrush(KColorScheme::View, KColorScheme::NormalText); 0075 initPage(type); 0076 } 0077 0078 SymbolView::~SymbolView() 0079 { 0080 } 0081 0082 /* key format 0083 from old symbols with package info 0084 1%\textonequarter%%%{textcomp}%%/home/kdedev/.kde4/share/apps/kile/mathsymbols/misc-text/img072misc-text.png 0085 from old symbols without package info 0086 1%\oldstylenums{9}%%%%%/home/kdedev/.kde4/share/apps/kile/mathsymbols/misc-text/img070misc-text.png 0087 new symbol 0088 1%\neq%≠%[utf8x,,]{inputenc,ucs,}%[fleqn,]{amsmath,}%This command gives nice weather!%/home/kdedev/.kde4/share/apps/kile/mathsymbols/user/img002math.png 0089 */ 0090 0091 void SymbolView::extract(const QString& key, int& refCnt) 0092 { 0093 if (!key.isEmpty()) { 0094 refCnt = key.section('%', 0, 0).toInt(); 0095 } 0096 } 0097 0098 void SymbolView::extractPackageString(const QString&string, QList<Package> &packages) 0099 { 0100 QRegExp rePkgs("(?:\\[(.*)\\])?\\{(.*)\\}"); 0101 QStringList args,pkgs; 0102 Package pkg; 0103 0104 if(string.isEmpty()) { 0105 return; 0106 } 0107 0108 packages.clear(); 0109 0110 if(rePkgs.exactMatch(string)) { 0111 args = rePkgs.cap(1).split(','); 0112 pkgs = rePkgs.cap(2).split(','); 0113 } 0114 else { 0115 return; 0116 } 0117 0118 for(int i = 0 ; i < pkgs.count() && i < args.count() ; i++) { 0119 const QString packageName = pkgs.at(i); 0120 if(packageName.isEmpty()) { 0121 continue; 0122 } 0123 pkg.name = packageName; 0124 pkg.arguments = args.at(i); 0125 packages.append(pkg); 0126 } 0127 0128 } 0129 0130 void SymbolView::extract(const QString& key, Command &cmd) 0131 { 0132 if (key.isEmpty()) { 0133 return; 0134 } 0135 QStringList contents = key.split('%'); 0136 0137 cmd.referenceCount = contents.at(0).toInt(); 0138 cmd.latexCommand = contents.at(1); 0139 cmd.unicodeCommand = contents.at(2); 0140 0141 extractPackageString(contents.at(3), cmd.unicodePackages); 0142 extractPackageString(contents.at(4), cmd.packages); 0143 cmd.comment = contents.at(5); 0144 cmd.path = contents.at(6); 0145 } 0146 0147 void SymbolView::initPage(int page) 0148 { 0149 switch(page) { 0150 case MFUS: 0151 fillWidget(MFUS_PREFIX); 0152 break; 0153 0154 case Relation: 0155 fillWidget("relation"); 0156 break; 0157 0158 case Operator: 0159 fillWidget("operators"); 0160 break; 0161 0162 case Arrow: 0163 fillWidget("arrows"); 0164 break; 0165 0166 case MiscMath: 0167 fillWidget("misc-math"); 0168 break; 0169 0170 case MiscText: 0171 fillWidget("misc-text"); 0172 break; 0173 0174 case Delimiters: 0175 fillWidget("delimiters"); 0176 break; 0177 0178 case Greek: 0179 fillWidget("greek"); 0180 break; 0181 0182 case Special: 0183 fillWidget("special"); 0184 break; 0185 0186 case Cyrillic: 0187 fillWidget("cyrillic"); 0188 break; 0189 0190 case User: 0191 fillWidget("user"); 0192 break; 0193 0194 default: 0195 qWarning() << "wrong argument in initPage()"; 0196 break; 0197 } 0198 } 0199 0200 QString SymbolView::getToolTip(const QString &key) 0201 { 0202 Command cmd; 0203 extract(key, cmd); 0204 0205 QString label = "<p style='white-space:pre'>"; 0206 label += "<b>" + i18n("Command: %1", cmd.latexCommand.toHtmlEscaped()) + "</b>"; 0207 if(!cmd.unicodeCommand.isEmpty()) { 0208 label += i18n("<br/>Unicode: %1", cmd.unicodeCommand.toHtmlEscaped()); 0209 } 0210 0211 if(cmd.packages.count() > 0) { 0212 QString packageString; 0213 0214 if(cmd.packages.count() == 1) { 0215 Package pkg = cmd.packages.at(0); 0216 if(!pkg.arguments.isEmpty()) { 0217 packageString += '[' + pkg.arguments + ']' + pkg.name; 0218 } 0219 else { 0220 packageString += pkg.name; 0221 } 0222 } 0223 else { 0224 packageString = "<ul>"; 0225 for (int i = 0; i < cmd.packages.count() ; ++i) { 0226 Package pkg = cmd.packages.at(i); 0227 if(!pkg.arguments.isEmpty()) { 0228 packageString += "<li>[" + pkg.arguments + ']' + pkg.name + "</li>"; 0229 } 0230 else { 0231 packageString += "<li>" + pkg.name + "</li>"; 0232 } 0233 } 0234 packageString += "</ul>"; 0235 } 0236 label += "<br/>" + i18np("Required Package: %2", "Required Packages: %2", cmd.packages.count(), packageString); 0237 } 0238 0239 if(!cmd.comment.isEmpty()) { 0240 label += "<br/><i>" + i18n("Comment: %1", cmd.comment.toHtmlEscaped()) + "</i>"; 0241 } 0242 0243 label += "</p>"; 0244 return label; 0245 } 0246 0247 void SymbolView::mousePressEvent(QMouseEvent *event) 0248 { 0249 Command cmd; 0250 QListWidgetItem *item = Q_NULLPTR; 0251 bool math = false, bracket = false; 0252 0253 if(event->button() == Qt::LeftButton && (item = itemAt(event->pos()))) { 0254 bracket = event->modifiers() & Qt::ControlModifier; 0255 math = event->modifiers() & Qt::ShiftModifier; 0256 0257 QString code_symbol; 0258 QList<Package> packages; 0259 extract(item->data(Qt::UserRole).toString(), cmd); 0260 if(KileConfig::symbolViewUTF8()) { 0261 code_symbol = cmd.unicodeCommand; 0262 if(code_symbol.isEmpty()) { 0263 code_symbol = cmd.latexCommand; 0264 } 0265 packages = cmd.unicodePackages; 0266 } 0267 else { 0268 code_symbol = cmd.latexCommand; 0269 packages = cmd.packages; 0270 } 0271 0272 if(math != bracket) { 0273 if(math) { 0274 code_symbol = '$' + code_symbol + '$'; 0275 } 0276 else if(bracket) { 0277 code_symbol = '{' + code_symbol + '}'; 0278 } 0279 } 0280 emit(insertText(code_symbol, packages)); 0281 emit(addToList(item)); 0282 m_ki->focusEditor(); 0283 } 0284 0285 KILE_DEBUG_MAIN << "math is " << math << ", bracket is " << bracket << " and item->data(Qt::UserRole).toString() is " << (item ? item->data(Qt::UserRole).toString() : ""); 0286 } 0287 0288 QString convertLatin1StringtoUTF8(const QString &string) 0289 { 0290 if(string.isEmpty()) { 0291 return QString(); 0292 } 0293 0294 QVector<uint> stringAsIntVector; 0295 const QStringList stringList = string.split(',', Qt::SkipEmptyParts); 0296 0297 for (QString str : stringList) { 0298 str.remove("U+"); 0299 bool ok; 0300 int stringAsInt = str.toInt(&ok); 0301 if(!ok) { 0302 return QString(); 0303 } 0304 stringAsIntVector.append(stringAsInt); 0305 } 0306 0307 return QString::fromUcs4(stringAsIntVector.data(),stringAsIntVector.count()); 0308 } 0309 0310 void SymbolView::fillWidget(const QString& prefix) 0311 { 0312 KILE_DEBUG_MAIN << "===SymbolView::fillWidget(const QString& " << prefix << " )==="; 0313 QImage image; 0314 QListWidgetItem* item; 0315 QStringList refCnts, paths; 0316 QString key; 0317 0318 // find paths 0319 if (prefix == MFUS_PREFIX) { // case: most frequently used symbols 0320 KConfigGroup config = KSharedConfig::openConfig()->group(MFUS_GROUP); 0321 QString configPaths = config.readEntry("paths"); 0322 QString configrefCnts = config.readEntry("counts"); 0323 paths = configPaths.split(',', Qt::SkipEmptyParts); 0324 refCnts = configrefCnts.split(',', Qt::SkipEmptyParts); 0325 KILE_DEBUG_MAIN << "Read " << paths.count() << " paths and " << refCnts.count() << " refCnts"; 0326 if(paths.count() != refCnts.count()) { 0327 KILE_DEBUG_MAIN << "error in saved LRU list"; 0328 paths.clear(); 0329 refCnts.clear(); 0330 } 0331 } 0332 else { // case: any other group of math symbols 0333 const QStringList dirs = KileUtilities::locateAll(QStandardPaths::AppDataLocation, 0334 QLatin1String("mathsymbols/") + prefix, 0335 QStandardPaths::LocateDirectory); 0336 for(const QString &dir : dirs) { 0337 const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.png")); 0338 for(const QString &file : fileNames) { 0339 const QString path = dir + '/' + file; 0340 if (!paths.contains(path)) { 0341 paths.append(path); 0342 } 0343 } 0344 } 0345 paths.sort(); 0346 for (int i = 0; i < paths.count(); i++) { 0347 refCnts.append("1"); 0348 } 0349 } 0350 0351 // render symbols 0352 for (int i = 0; i < paths.count(); i++) { 0353 if (image.load(paths[i])) { 0354 item = new QListWidgetItem(this); 0355 0356 key = refCnts[i] + '%' + image.text("Command"); 0357 key += '%' + convertLatin1StringtoUTF8(image.text("CommandUnicode")); 0358 key += '%' + image.text("UnicodePackages"); 0359 key += '%' + image.text("Packages"); 0360 key += '%' + convertLatin1StringtoUTF8(image.text("Comment")); 0361 key += '%' + paths[i]; 0362 0363 item->setData(Qt::UserRole, key); 0364 item->setToolTip(getToolTip(key)); 0365 0366 if (prefix != QLatin1String("user")) { 0367 if (image.format() != QImage::Format_ARGB32_Premultiplied && image.format() != QImage::Format_ARGB32) { 0368 image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); 0369 } 0370 0371 QPainter p; 0372 p.begin(&image); 0373 p.setCompositionMode(QPainter::CompositionMode_SourceAtop); 0374 p.fillRect(image.rect(), m_brush.brush(QPalette::Active)); 0375 p.end(); 0376 } 0377 item->setIcon(QPixmap::fromImage(image)); 0378 } 0379 else { 0380 KILE_DEBUG_MAIN << "Loading file " << paths[i] << " failed"; 0381 } 0382 } 0383 } 0384 0385 void SymbolView::writeConfig() 0386 { 0387 Command cmd; 0388 0389 KConfigGroup grp = KSharedConfig::openConfig()->group(MFUS_GROUP); 0390 0391 if (KileConfig::clearMFUS()) { 0392 grp.deleteEntry("paths"); 0393 grp.deleteEntry("counts"); 0394 } 0395 else { 0396 QStringList paths; 0397 QList<int> refCnts; 0398 for(int i = 0; i < count(); ++i) { 0399 QListWidgetItem *item = this->item(i); 0400 extract(item->data(Qt::UserRole).toString(),cmd); 0401 refCnts.append(cmd.referenceCount); 0402 paths.append(cmd.path); 0403 KILE_DEBUG_MAIN << "path=" << paths.last() << ", count is " << refCnts.last(); 0404 } 0405 grp.writeEntry("paths", paths); 0406 grp.writeEntry("counts", refCnts); 0407 } 0408 } 0409 0410 void SymbolView::slotAddToList(const QListWidgetItem *item) 0411 { 0412 if(!item || item->icon().isNull()) { 0413 return; 0414 } 0415 0416 QListWidgetItem *tmpItem = Q_NULLPTR; 0417 bool found = false; 0418 const QRegExp reCnt("^\\d+"); 0419 0420 KILE_DEBUG_MAIN << "===void SymbolView::slotAddToList(const QIconViewItem *" << item << " )==="; 0421 0422 for(int i = 0; i < count(); ++i) { 0423 tmpItem = this->item(i); 0424 if (item->data(Qt::UserRole).toString().section('%', 1) == tmpItem->data(Qt::UserRole).toString().section('%', 1)) { 0425 found = true; 0426 break; 0427 } 0428 } 0429 0430 if(!found 0431 && static_cast<unsigned int>(this->count() + 1) > KileConfig::numSymbolsMFUS()) { // we check before adding the symbol 0432 int refCnt, minRefCnt = 10000; 0433 QListWidgetItem *unpopularItem = Q_NULLPTR; 0434 0435 KILE_DEBUG_MAIN << "Removing most unpopular item"; 0436 0437 for (int i = 0; i < count(); ++i) { 0438 tmpItem = this->item(i); 0439 extract(tmpItem->data(Qt::UserRole).toString(), refCnt); 0440 0441 if (refCnt < minRefCnt) { 0442 refCnt = minRefCnt; 0443 unpopularItem = tmpItem; 0444 } 0445 } 0446 KILE_DEBUG_MAIN << " minRefCnt is " << minRefCnt; 0447 delete unpopularItem; 0448 } 0449 0450 if(found) { 0451 KILE_DEBUG_MAIN << "item is already in the iconview"; 0452 0453 int refCnt; 0454 extract(tmpItem->data(Qt::UserRole).toString(), refCnt); 0455 0456 QString key = tmpItem->data(Qt::UserRole).toString(); 0457 key.replace(reCnt, QString::number(refCnt + 1)); 0458 tmpItem->setData(Qt::UserRole, key); 0459 tmpItem->setToolTip(getToolTip(key)); 0460 } 0461 else { 0462 tmpItem = new QListWidgetItem(this); 0463 tmpItem->setIcon(item->icon()); 0464 QString key = item->data(Qt::UserRole).toString(); 0465 tmpItem->setData(Qt::UserRole, key); 0466 tmpItem->setToolTip(getToolTip(key)); 0467 } 0468 } 0469 0470 } 0471