File indexing completed on 2024-04-28 17:05:55
0001 /* 0002 SPDX-FileCopyrightText: 2002 Shie Erlich <erlich@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2002 Rafi Yanai <yanai@users.sourceforge.net> 0004 SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "popularurls.h" 0010 0011 #include <stdio.h> 0012 0013 // QtCore 0014 #include <QList> 0015 // QtWidgets 0016 #include <QDialogButtonBox> 0017 #include <QGridLayout> 0018 #include <QHeaderView> 0019 #include <QLabel> 0020 #include <QLayout> 0021 #include <QPushButton> 0022 #include <QToolButton> 0023 0024 #include <KConfigCore/KSharedConfig> 0025 #include <KI18n/KLocalizedString> 0026 #include <KItemViews/KTreeWidgetSearchLine> 0027 #include <KWidgetsAddons/KMessageBox> 0028 0029 #include "../GUI/krtreewidget.h" 0030 #include "../icon.h" 0031 #include "../krglobal.h" 0032 #include "../krslots.h" 0033 0034 #define STARTING_RANK 20 0035 #define INCREASE 2 0036 #define DECREASE 1 0037 0038 PopularUrls::PopularUrls(QObject *parent) 0039 : QObject(parent) 0040 , head(nullptr) 0041 , tail(nullptr) 0042 , count(0) 0043 { 0044 dlg = new PopularUrlsDlg(); 0045 } 0046 0047 PopularUrls::~PopularUrls() 0048 { 0049 clearList(); 0050 delete dlg; 0051 } 0052 0053 void PopularUrls::clearList() 0054 { 0055 if (head) { 0056 UrlNodeP p = head, tmp; 0057 while (p) { 0058 tmp = p; 0059 p = p->next; 0060 delete tmp; 0061 } 0062 } 0063 ranks.clear(); 0064 head = tail = nullptr; 0065 } 0066 0067 void PopularUrls::save() 0068 { 0069 KConfigGroup svr(krConfig, "Private"); 0070 // prepare the string list containing urls and int list with ranks 0071 QStringList urlList; 0072 QList<int> rankList; 0073 UrlNodeP p = head; 0074 while (p) { 0075 urlList << p->url.toDisplayString(); 0076 rankList << p->rank; 0077 p = p->next; 0078 } 0079 svr.writeEntry("PopularUrls", urlList); 0080 svr.writeEntry("PopularUrlsRank", rankList); 0081 } 0082 0083 void PopularUrls::load() 0084 { 0085 KConfigGroup svr(krConfig, "Private"); 0086 QStringList urlList = svr.readEntry("PopularUrls", QStringList()); 0087 QList<int> rankList = svr.readEntry("PopularUrlsRank", QList<int>()); 0088 if (urlList.count() != rankList.count()) { 0089 KMessageBox::error(krMainWindow, i18n("The saved 'Popular URLs' are invalid. The list will be cleared.")); 0090 return; 0091 } 0092 clearList(); 0093 count = 0; 0094 // iterate through both lists and 0095 QStringList::Iterator uit; 0096 QList<int>::Iterator rit; 0097 for (uit = urlList.begin(), rit = rankList.begin(); uit != urlList.end() && rit != rankList.end(); ++uit, ++rit) { 0098 auto node = new UrlNode; 0099 node->url = QUrl(*uit); 0100 node->rank = *rit; 0101 appendNode(node); 0102 ranks.insert(*uit, node); 0103 } 0104 } 0105 0106 // returns a url list with the 'max' top popular urls 0107 QList<QUrl> PopularUrls::getMostPopularUrls(int max) 0108 { 0109 // get at most 'max' urls 0110 QList<QUrl> list; 0111 UrlNodeP p = head; 0112 int tmp = 0; 0113 if (maxUrls < max) 0114 max = maxUrls; // don't give more than maxUrls 0115 while (p && tmp < max) { 0116 list << p->url; 0117 p = p->next; 0118 ++tmp; 0119 } 0120 0121 return list; 0122 } 0123 0124 // adds a url to the list, or increase rank of an existing url, making 0125 // sure to bump it up the list if needed 0126 void PopularUrls::addUrl(const QUrl &url) 0127 { 0128 QUrl tmpurl = url; 0129 tmpurl.setPassword(QString()); // make sure no passwords are permanently stored 0130 if (!tmpurl.path().endsWith('/')) // make a uniform trailing slash policy 0131 tmpurl.setPath(tmpurl.path() + '/'); 0132 UrlNodeP pnode; 0133 0134 decreaseRanks(); 0135 if (!head) { // if the list is empty ... (assumes dict to be empty as well) 0136 pnode = new UrlNode; 0137 pnode->rank = STARTING_RANK; 0138 pnode->url = tmpurl; 0139 appendNode(pnode); 0140 ranks.insert(tmpurl.url(), head); 0141 } else { 0142 if (ranks.find(tmpurl.url()) == ranks.end()) { // is the added url new? if so, append it 0143 pnode = new UrlNode; 0144 pnode->rank = STARTING_RANK; 0145 pnode->url = tmpurl; 0146 appendNode(pnode); 0147 ranks.insert(tmpurl.url(), pnode); 0148 } else { 0149 pnode = ranks[tmpurl.url()]; 0150 pnode->rank += INCREASE; 0151 } 0152 } 0153 0154 // do we need to change location for this one? 0155 relocateIfNeeded(pnode); 0156 0157 // too many urls? 0158 if (count > maxUrls) 0159 removeNode(tail); 0160 0161 // dumpList(); 0162 } 0163 0164 // checks if 'node' needs to be bumped-up the ranking list and does it if needed 0165 void PopularUrls::relocateIfNeeded(UrlNodeP node) 0166 { 0167 if (node->prev && (node->prev->rank < node->rank)) { 0168 // iterate until we find the correct place to put it 0169 UrlNodeP tmp = node->prev->prev; 0170 while (tmp) { 0171 if (tmp->rank >= node->rank) 0172 break; // found it! 0173 else 0174 tmp = tmp->prev; 0175 } 0176 // now, if tmp isn't null, we need to move node to tmp->next 0177 // else move it to become head 0178 removeNode(node); 0179 insertNode(node, tmp); 0180 } 0181 } 0182 0183 // iterate over the list, decreasing each url's rank 0184 // this is very naive, but a 1..30 for loop is acceptable (i hope) 0185 void PopularUrls::decreaseRanks() 0186 { 0187 if (head) { 0188 UrlNodeP p = head; 0189 while (p) { 0190 if (p->rank - DECREASE >= 0) 0191 p->rank -= DECREASE; 0192 else 0193 p->rank = 0; 0194 p = p->next; 0195 } 0196 } 0197 } 0198 0199 // removes a node from the list, but doesn't free memory! 0200 // note: this will be buggy in case the list becomes empty (which should never happen) 0201 void PopularUrls::removeNode(UrlNodeP node) 0202 { 0203 if (node->prev) { 0204 if (tail == node) 0205 tail = node->prev; 0206 node->prev->next = node->next; 0207 } 0208 if (node->next) { 0209 if (head == node) 0210 head = node->next; 0211 node->next->prev = node->prev; 0212 } 0213 --count; 0214 } 0215 0216 void PopularUrls::insertNode(UrlNodeP node, UrlNodeP after) 0217 { 0218 if (!after) { // make node head 0219 node->next = head; 0220 node->prev = nullptr; 0221 head->prev = node; 0222 head = node; 0223 } else { 0224 if (tail == after) 0225 tail = node; 0226 node->prev = after; 0227 node->next = after->next; 0228 if (node->next) { 0229 after->next->prev = node; 0230 after->next = node; 0231 } 0232 } 0233 ++count; 0234 } 0235 0236 // appends 'node' to the end of the list, collecting garbage if needed 0237 void PopularUrls::appendNode(UrlNodeP node) 0238 { 0239 if (!tail) { // creating the first element 0240 head = tail = node; 0241 node->prev = node->next = nullptr; 0242 } else { 0243 node->next = nullptr; 0244 node->prev = tail; 0245 tail->next = node; 0246 tail = node; 0247 } 0248 ++count; 0249 } 0250 0251 void PopularUrls::dumpList() 0252 { 0253 UrlNodeP p = head; 0254 printf("====start %d====\n", count); 0255 while (p) { 0256 printf("%d : %s\n", p->rank, p->url.url().toLatin1().data()); 0257 p = p->next; 0258 } 0259 fflush(stdout); 0260 } 0261 0262 void PopularUrls::showDialog() 0263 { 0264 QList<QUrl> list = getMostPopularUrls(maxUrls); 0265 dlg->run(list); 0266 if (dlg->result() == -1) 0267 return; 0268 SLOTS->refresh(list[dlg->result()]); 0269 // printf("running %s\n", list[dlg->result()].url().toLatin1());fflush(stdout); 0270 } 0271 0272 // ===================================== PopularUrlsDlg ====================================== 0273 PopularUrlsDlg::PopularUrlsDlg() 0274 : QDialog(krMainWindow) 0275 { 0276 setWindowTitle(i18n("Popular URLs")); 0277 setWindowModality(Qt::WindowModal); 0278 0279 auto *mainLayout = new QVBoxLayout; 0280 setLayout(mainLayout); 0281 0282 auto *layout = new QGridLayout; 0283 layout->setContentsMargins(0, 0, 0, 0); 0284 0285 // listview to contain the urls 0286 urls = new KrTreeWidget(this); 0287 urls->header()->hide(); 0288 urls->setSortingEnabled(false); 0289 urls->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 0290 0291 // quick search 0292 search = new KTreeWidgetSearchLine(this, urls); 0293 QLabel *lbl = new QLabel(i18n("&Search:"), this); 0294 lbl->setBuddy(search); 0295 0296 layout->addWidget(lbl, 0, 0); 0297 layout->addWidget(search, 0, 1); 0298 layout->addWidget(urls, 1, 0, 1, 2); 0299 0300 mainLayout->addLayout(layout); 0301 0302 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); 0303 mainLayout->addWidget(buttonBox); 0304 0305 setTabOrder(search, urls); 0306 setTabOrder((QWidget *)urls, buttonBox->button(QDialogButtonBox::Close)); 0307 0308 connect(buttonBox, &QDialogButtonBox::rejected, this, &PopularUrlsDlg::reject); 0309 connect(urls, &KrTreeWidget::activated, this, &PopularUrlsDlg::slotItemSelected); 0310 connect(search, &KTreeWidgetSearchLine::hiddenChanged, this, &PopularUrlsDlg::slotVisibilityChanged); 0311 } 0312 0313 void PopularUrlsDlg::slotItemSelected(const QModelIndex &ndx) 0314 { 0315 selection = ndx.row(); 0316 accept(); 0317 } 0318 0319 void PopularUrlsDlg::slotVisibilityChanged() 0320 { 0321 // select the first visible item 0322 QList<QTreeWidgetItem *> list = urls->selectedItems(); 0323 if (list.count() > 0 && !list[0]->isHidden()) 0324 return; 0325 0326 urls->clearSelection(); 0327 urls->setCurrentItem(nullptr); 0328 0329 QTreeWidgetItemIterator it(urls); 0330 while (*it) { 0331 if (!(*it)->isHidden()) { 0332 (*it)->setSelected(true); 0333 urls->setCurrentItem(*it); 0334 break; 0335 } 0336 it++; 0337 } 0338 } 0339 0340 PopularUrlsDlg::~PopularUrlsDlg() 0341 { 0342 delete search; 0343 delete urls; 0344 } 0345 0346 void PopularUrlsDlg::run(QList<QUrl> list) 0347 { 0348 // populate the listview 0349 urls->clear(); 0350 QList<QUrl>::Iterator it; 0351 0352 QTreeWidgetItem *lastItem = nullptr; 0353 0354 for (it = list.begin(); it != list.end(); ++it) { 0355 auto *item = new QTreeWidgetItem(urls, lastItem); 0356 lastItem = item; 0357 item->setText(0, (*it).isLocalFile() ? (*it).path() : (*it).toDisplayString()); 0358 item->setIcon(0, (*it).isLocalFile() ? Icon("folder") : Icon("folder-html")); 0359 } 0360 0361 setMinimumSize(urls->sizeHint().width() + 45, 400); 0362 0363 search->clear(); 0364 search->setFocus(); 0365 selection = -1; 0366 slotVisibilityChanged(); 0367 exec(); 0368 }