File indexing completed on 2024-05-12 17:22:03
0001 /* 0002 SPDX-FileCopyrightText: 2000-2002 Shie Erlich <krusader@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2000-2002 Rafi Yanai <krusader@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 "krview.h" 0010 0011 #include "../FileSystem/dirlisterinterface.h" 0012 #include "../FileSystem/fileitem.h" 0013 #include "../FileSystem/krpermhandler.h" 0014 #include "../Filter/filterdialog.h" 0015 #include "../defaults.h" 0016 #include "../filelisticon.h" 0017 #include "../krcolorcache.h" 0018 #include "../krglobal.h" 0019 #include "../krpreviews.h" 0020 #include "../viewactions.h" 0021 #include "krselectionmode.h" 0022 #include "krviewfactory.h" 0023 #include "krviewitem.h" 0024 0025 // QtCore 0026 #include <QDebug> 0027 #include <QDir> 0028 // QtGui 0029 #include <QBitmap> 0030 #include <QPainter> 0031 #include <QPixmap> 0032 #include <QPixmapCache> 0033 // QtWidgets 0034 #include <QAction> 0035 #include <QInputDialog> 0036 #include <QMimeDatabase> 0037 #include <QMimeType> 0038 #include <qnamespace.h> 0039 0040 #include <KConfigCore/KSharedConfig> 0041 #include <KI18n/KLocalizedString> 0042 0043 #define FILEITEM getFileItem() 0044 0045 KrView *KrViewOperator::_changedView = nullptr; 0046 KrViewProperties::PropertyType KrViewOperator::_changedProperties = KrViewProperties::NoProperty; 0047 0048 // ----------------------------- operator 0049 KrViewOperator::KrViewOperator(KrView *view, QWidget *widget) 0050 : _view(view) 0051 , _widget(widget) 0052 , _massSelectionUpdate(false) 0053 { 0054 _saveDefaultSettingsTimer.setSingleShot(true); 0055 connect(&_saveDefaultSettingsTimer, &QTimer::timeout, this, &KrViewOperator::saveDefaultSettings); 0056 } 0057 0058 KrViewOperator::~KrViewOperator() 0059 { 0060 if (_changedView == _view) 0061 saveDefaultSettings(); 0062 } 0063 0064 void KrViewOperator::startUpdate() 0065 { 0066 _view->refresh(); 0067 } 0068 0069 void KrViewOperator::cleared() 0070 { 0071 _view->clear(); 0072 } 0073 0074 void KrViewOperator::fileAdded(FileItem *fileitem) 0075 { 0076 _view->addItem(fileitem); 0077 } 0078 0079 void KrViewOperator::fileUpdated(FileItem *newFileitem) 0080 { 0081 _view->updateItem(newFileitem); 0082 } 0083 0084 void KrViewOperator::startDrag() 0085 { 0086 QStringList items; 0087 _view->getSelectedItems(&items); 0088 if (items.empty()) 0089 return; // don't drag an empty thing 0090 QPixmap px; 0091 if (items.count() > 1 || _view->getCurrentKrViewItem() == nullptr) 0092 px = FileListIcon("document-multiple").pixmap(); // we are dragging multiple items 0093 else 0094 px = _view->getCurrentKrViewItem()->icon(); 0095 emit letsDrag(items, px); 0096 } 0097 0098 bool KrViewOperator::searchItem(const QString &text, bool caseSensitive, int direction) 0099 { 0100 KrViewItem *item = _view->getCurrentKrViewItem(); 0101 if (!item) { 0102 return false; 0103 } 0104 const QRegExp regeEx(text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); 0105 if (!direction) { 0106 if (regeEx.indexIn(item->name()) == 0) { 0107 return true; 0108 } 0109 direction = 1; 0110 } 0111 KrViewItem *startItem = item; 0112 while (true) { 0113 item = (direction > 0) ? _view->getNext(item) : _view->getPrev(item); 0114 if (!item) 0115 item = (direction > 0) ? _view->getFirst() : _view->getLast(); 0116 if (regeEx.indexIn(item->name()) == 0) { 0117 _view->setCurrentKrViewItem(item); 0118 _view->makeItemVisible(item); 0119 return true; 0120 } 0121 if (item == startItem) { 0122 return false; 0123 } 0124 } 0125 } 0126 0127 bool KrViewOperator::filterSearch(const QString &text, bool caseSensitive) 0128 { 0129 _view->_quickFilterMask = QRegExp(text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); 0130 _view->refresh(); 0131 return _view->_count || !_view->_files->numFileItems(); 0132 } 0133 0134 void KrViewOperator::setMassSelectionUpdate(bool upd) 0135 { 0136 _massSelectionUpdate = upd; 0137 if (!upd) { 0138 emit selectionChanged(); 0139 _view->redraw(); 0140 } 0141 } 0142 0143 void KrViewOperator::settingsChanged(KrViewProperties::PropertyType properties) 0144 { 0145 if (!_view->_updateDefaultSettings || _view->_ignoreSettingsChange) 0146 return; 0147 0148 if (_changedView != _view) 0149 saveDefaultSettings(); 0150 _changedView = _view; 0151 _changedProperties = static_cast<KrViewProperties::PropertyType>(_changedProperties | properties); 0152 _saveDefaultSettingsTimer.start(100); 0153 } 0154 0155 void KrViewOperator::saveDefaultSettings() 0156 { 0157 _saveDefaultSettingsTimer.stop(); 0158 if (_changedView) 0159 _changedView->saveDefaultSettings(_changedProperties); 0160 _changedProperties = KrViewProperties::NoProperty; 0161 _changedView = nullptr; 0162 } 0163 0164 // ----------------------------- krview 0165 0166 const KrView::IconSizes KrView::iconSizes; 0167 0168 KrView::KrView(KrViewInstance &instance, KConfig *cfg) 0169 : _config(cfg) 0170 , _properties(nullptr) 0171 , _focused(false) 0172 , _fileIconSize(0) 0173 , _instance(instance) 0174 , _files(nullptr) 0175 , _mainWindow(nullptr) 0176 , _widget(nullptr) 0177 , _nameToMakeCurrent(QString()) 0178 , _previews(nullptr) 0179 , _updateDefaultSettings(false) 0180 , _ignoreSettingsChange(false) 0181 , _count(0) 0182 , _numDirs(0) 0183 , _dummyFileItem(nullptr) 0184 { 0185 } 0186 0187 KrView::~KrView() 0188 { 0189 _instance.m_objects.removeOne(this); 0190 delete _previews; 0191 _previews = nullptr; 0192 delete _dummyFileItem; 0193 _dummyFileItem = nullptr; 0194 if (_properties) 0195 qFatal("A class inheriting KrView didn't delete _properties!"); 0196 if (_operator) 0197 qFatal("A class inheriting KrView didn't delete _operator!"); 0198 } 0199 0200 void KrView::init(bool enableUpdateDefaultSettings) 0201 { 0202 // sanity checks: 0203 if (!_widget) 0204 qFatal("_widget must be set during construction of KrView inheritors"); 0205 // ok, continue 0206 initProperties(); 0207 _operator = createOperator(); 0208 setup(); 0209 restoreDefaultSettings(); 0210 0211 _updateDefaultSettings = enableUpdateDefaultSettings && KConfigGroup(_config, "Startup").readEntry("Update Default Panel Settings", _RememberPos); 0212 0213 _instance.m_objects.append(this); 0214 } 0215 0216 void KrView::initProperties() 0217 { 0218 const KConfigGroup grpInstance(_config, _instance.name()); 0219 const bool displayIcons = grpInstance.readEntry("With Icons", _WithIcons); 0220 0221 const KConfigGroup grpSvr(_config, "Look&Feel"); 0222 const bool numericPermissions = grpSvr.readEntry("Numeric permissions", _NumericPermissions); 0223 0224 int sortOps = 0; 0225 if (grpSvr.readEntry("Show Directories First", true)) 0226 sortOps |= KrViewProperties::DirsFirst; 0227 if (grpSvr.readEntry("Always sort dirs by name", false)) 0228 sortOps |= KrViewProperties::AlwaysSortDirsByName; 0229 if (!grpSvr.readEntry("Case Sensative Sort", _CaseSensativeSort)) 0230 sortOps |= KrViewProperties::IgnoreCase; 0231 if (grpSvr.readEntry("Locale Aware Sort", true)) 0232 sortOps |= KrViewProperties::LocaleAwareSort; 0233 auto sortOptions = static_cast<KrViewProperties::SortOptions>(sortOps); 0234 0235 KrViewProperties::SortMethod sortMethod = static_cast<KrViewProperties::SortMethod>(grpSvr.readEntry("Sort method", (int)_DefaultSortMethod)); 0236 const bool humanReadableSize = grpSvr.readEntry("Human Readable Size", _HumanReadableSize); 0237 0238 // see KDE bug #40131 0239 const bool localeAwareCompareIsCaseSensitive = QString("a").localeAwareCompare("B") > 0; 0240 0241 QStringList defaultAtomicExtensions; 0242 defaultAtomicExtensions += ".tar.gz"; 0243 defaultAtomicExtensions += ".tar.bz2"; 0244 defaultAtomicExtensions += ".tar.lzma"; 0245 defaultAtomicExtensions += ".tar.xz"; 0246 defaultAtomicExtensions += ".moc.cpp"; 0247 QStringList atomicExtensions = grpSvr.readEntry("Atomic Extensions", defaultAtomicExtensions); 0248 for (QStringList::iterator i = atomicExtensions.begin(); i != atomicExtensions.end();) { 0249 QString &ext = *i; 0250 ext = ext.trimmed(); 0251 if (!ext.length()) { 0252 i = atomicExtensions.erase(i); 0253 continue; 0254 } 0255 if (!ext.startsWith('.')) 0256 ext.insert(0, '.'); 0257 ++i; 0258 } 0259 0260 _properties = 0261 new KrViewProperties(displayIcons, numericPermissions, sortOptions, sortMethod, humanReadableSize, localeAwareCompareIsCaseSensitive, atomicExtensions); 0262 } 0263 0264 void KrView::showPreviews(bool show) 0265 { 0266 if (show) { 0267 if (!_previews) { 0268 _previews = new KrPreviews(this); 0269 _previews->update(); 0270 } 0271 } else { 0272 delete _previews; 0273 _previews = nullptr; 0274 } 0275 redraw(); 0276 // op()->settingsChanged(KrViewProperties::PropShowPreviews); 0277 op()->emitRefreshActions(); 0278 } 0279 0280 void KrView::updatePreviews() 0281 { 0282 if (_previews) 0283 _previews->update(); 0284 } 0285 0286 QPixmap KrView::processIcon(const QPixmap &icon, bool dim, const QColor &dimColor, int dimFactor, bool symlink) 0287 { 0288 QPixmap pixmap = icon; 0289 0290 if (symlink) { 0291 const QStringList overlays = QStringList() << QString() << "emblem-symbolic-link"; 0292 Icon::applyOverlays(&pixmap, overlays); 0293 } 0294 0295 if (!dim) 0296 return pixmap; 0297 0298 QImage dimmed = pixmap.toImage(); 0299 0300 QPainter p(&dimmed); 0301 p.setCompositionMode(QPainter::CompositionMode_SourceIn); 0302 p.fillRect(0, 0, icon.width(), icon.height(), dimColor); 0303 p.setCompositionMode(QPainter::CompositionMode_SourceOver); 0304 p.setOpacity((qreal)dimFactor / (qreal)100); 0305 p.drawPixmap(0, 0, pixmap); 0306 0307 return QPixmap::fromImage(dimmed, Qt::ColorOnly | Qt::ThresholdDither | Qt::ThresholdAlphaDither | Qt::NoOpaqueDetection); 0308 } 0309 0310 QPixmap KrView::getIcon(FileItem *fileitem, bool active, int size /*, KRListItem::cmpColor color*/) 0311 { 0312 // KConfigGroup ag( krConfig, "Advanced"); 0313 ////////////////////////////// 0314 QPixmap icon; 0315 QString iconName = fileitem->getIcon(); 0316 QString cacheName; 0317 0318 if (!size) 0319 size = _FilelistIconSize.toInt(); 0320 0321 QColor dimColor; 0322 int dimFactor; 0323 bool dim = !active && KrColorCache::getColorCache().getDimSettings(dimColor, dimFactor); 0324 0325 if (iconName.isNull()) 0326 iconName = ""; 0327 0328 cacheName.append(QString::number(size)); 0329 if (fileitem->isSymLink()) 0330 cacheName.append("LINK_"); 0331 if (dim) 0332 cacheName.append("DIM_"); 0333 cacheName.append(iconName); 0334 0335 // QPixmapCache::setCacheLimit( ag.readEntry("Icon Cache Size",_IconCacheSize) ); 0336 0337 // first try the cache 0338 if (!QPixmapCache::find(cacheName, &icon)) { 0339 icon = processIcon(Icon(iconName, Icon("unknown")).pixmap(size), dim, dimColor, dimFactor, fileitem->isSymLink()); 0340 // insert it into the cache 0341 QPixmapCache::insert(cacheName, icon); 0342 } 0343 0344 return icon; 0345 } 0346 0347 QPixmap KrView::getIcon(FileItem *fileitem) 0348 { 0349 if (_previews) { 0350 QPixmap icon; 0351 if (_previews->getPreview(fileitem, icon, _focused)) 0352 return icon; 0353 } 0354 return getIcon(fileitem, _focused, _fileIconSize); 0355 } 0356 0357 /** 0358 * this function ADDs a list of selected item names into 'names'. 0359 * it assumes the list is ready and doesn't initialize it, or clears it 0360 */ 0361 void KrView::getItemsByMask(const QString &mask, QStringList *names, bool dirs, bool files) 0362 { 0363 for (KrViewItem *it = getFirst(); it != nullptr; it = getNext(it)) { 0364 if ((it->name() == "..") || !QDir::match(mask, it->name())) 0365 continue; 0366 // if we got here, than the item fits the mask 0367 if (it->getFileItem()->isDir() && !dirs) 0368 continue; // do we need to skip folders? 0369 if (!it->getFileItem()->isDir() && !files) 0370 continue; // do we need to skip files 0371 names->append(it->name()); 0372 } 0373 } 0374 0375 /** 0376 * this function ADDs a list of selected item names into 'names'. 0377 * it assumes the list is ready and doesn't initialize it, or clears it 0378 */ 0379 void KrView::getSelectedItems(QStringList *names, bool fallbackToFocused) 0380 { 0381 for (KrViewItem *it = getFirst(); it != nullptr; it = getNext(it)) 0382 if (it->isSelected() && (it->name() != "..")) 0383 names->append(it->name()); 0384 0385 if (fallbackToFocused) { 0386 // if all else fails, take the current item 0387 const QString item = getCurrentItem(); 0388 if (names->empty() && !item.isEmpty() && item != "..") { 0389 names->append(item); 0390 } 0391 } 0392 } 0393 0394 KrViewItemList KrView::getSelectedKrViewItems() 0395 { 0396 KrViewItemList items; 0397 for (KrViewItem *it = getFirst(); it != nullptr; it = getNext(it)) { 0398 if (it->isSelected() && (it->name() != "..")) { 0399 items.append(it); 0400 } 0401 } 0402 0403 // if all else fails, take the current item 0404 if (items.empty()) { 0405 KrViewItem *currentItem = getCurrentKrViewItem(); 0406 if (currentItem && !currentItem->isDummy()) { 0407 items.append(getCurrentKrViewItem()); 0408 } 0409 } 0410 0411 return items; 0412 } 0413 0414 QString KrView::statistics() 0415 { 0416 KIO::filesize_t size = calcSize(); 0417 KIO::filesize_t selectedSize = calcSelectedSize(); 0418 QString tmp; 0419 KConfigGroup grp(_config, "Look&Feel"); 0420 if (grp.readEntry("Show Size In Bytes", false)) { 0421 tmp = i18nc( 0422 "%1=number of selected items,%2=total number of items, \ 0423 %3=filesize of selected items,%4=filesize in Bytes, \ 0424 %5=filesize of all items in folder,%6=filesize in Bytes", 0425 "%1 out of %2, %3 (%4) out of %5 (%6)", 0426 numSelected(), 0427 _count, 0428 KIO::convertSize(selectedSize), 0429 KrPermHandler::parseSize(selectedSize), 0430 KIO::convertSize(size), 0431 KrPermHandler::parseSize(size)); 0432 } else { 0433 tmp = i18nc( 0434 "%1=number of selected items,%2=total number of items, \ 0435 %3=filesize of selected items,%4=filesize of all items in folder", 0436 "%1 out of %2, %3 out of %4", 0437 numSelected(), 0438 _count, 0439 KIO::convertSize(selectedSize), 0440 KIO::convertSize(size)); 0441 } 0442 0443 // notify if we're running a filtered view 0444 if (filter() != KrViewProperties::All) 0445 tmp = ">> [ " + filterMask().nameFilter() + " ] " + tmp; 0446 return tmp; 0447 } 0448 0449 bool KrView::changeSelection(const KrQuery &filter, bool select) 0450 { 0451 KConfigGroup grpSvr(_config, "Look&Feel"); 0452 return changeSelection(filter, select, grpSvr.readEntry("Mark Dirs", _MarkDirs), true); 0453 } 0454 0455 bool KrView::changeSelection(const KrQuery &filter, bool select, bool includeDirs, bool makeVisible) 0456 { 0457 if (op()) 0458 op()->setMassSelectionUpdate(true); 0459 0460 KrViewItem *temp = getCurrentKrViewItem(); 0461 KrViewItem *firstMatch = nullptr; 0462 for (KrViewItem *it = getFirst(); it != nullptr; it = getNext(it)) { 0463 if (it->name() == "..") 0464 continue; 0465 if (it->getFileItem()->isDir() && !includeDirs) 0466 continue; 0467 0468 FileItem *file = it->getMutableFileItem(); // filter::match calls getMimetype which isn't const 0469 if (file == nullptr) 0470 continue; 0471 0472 if (filter.match(file)) { 0473 it->setSelected(select); 0474 if (!firstMatch) 0475 firstMatch = it; 0476 } 0477 } 0478 0479 if (op()) 0480 op()->setMassSelectionUpdate(false); 0481 updateView(); 0482 if (ensureVisibilityAfterSelect() && temp != nullptr) { 0483 makeItemVisible(temp); 0484 } else if (makeVisible && firstMatch != nullptr) { 0485 // if no selected item is visible... 0486 const KrViewItemList selectedItems = getSelectedKrViewItems(); 0487 bool anyVisible = false; 0488 for (KrViewItem *item : selectedItems) { 0489 if (isItemVisible(item)) { 0490 anyVisible = true; 0491 break; 0492 } 0493 } 0494 if (!anyVisible) { 0495 // ...scroll to fist selected item 0496 makeItemVisible(firstMatch); 0497 } 0498 } 0499 redraw(); 0500 0501 return firstMatch != nullptr; // return if any file was selected 0502 } 0503 0504 void KrView::invertSelection() 0505 { 0506 if (op()) 0507 op()->setMassSelectionUpdate(true); 0508 KConfigGroup grpSvr(_config, "Look&Feel"); 0509 bool markDirs = grpSvr.readEntry("Mark Dirs", _MarkDirs); 0510 0511 KrViewItem *temp = getCurrentKrViewItem(); 0512 for (KrViewItem *it = getFirst(); it != nullptr; it = getNext(it)) { 0513 if (it->name() == "..") 0514 continue; 0515 if (it->getFileItem()->isDir() && !markDirs && !it->isSelected()) 0516 continue; 0517 it->setSelected(!it->isSelected()); 0518 } 0519 if (op()) 0520 op()->setMassSelectionUpdate(false); 0521 updateView(); 0522 if (ensureVisibilityAfterSelect() && temp != nullptr) 0523 makeItemVisible(temp); 0524 } 0525 0526 QString KrView::firstUnmarkedBelowCurrent(const bool skipCurrent) 0527 { 0528 if (getCurrentKrViewItem() == nullptr) 0529 return QString(); 0530 0531 KrViewItem *iterator = getCurrentKrViewItem(); 0532 if (skipCurrent) 0533 iterator = getNext(iterator); 0534 while (iterator && iterator->isSelected()) 0535 iterator = getNext(iterator); 0536 if (!iterator) { 0537 iterator = getPrev(getCurrentKrViewItem()); 0538 while (iterator && iterator->isSelected()) 0539 iterator = getPrev(iterator); 0540 } 0541 if (!iterator) 0542 return QString(); 0543 return iterator->name(); 0544 } 0545 0546 void KrView::deleteItem(const QString &name, bool onUpdate) 0547 { 0548 KrViewItem *viewItem = findItemByName(name); 0549 if (!viewItem) 0550 return; 0551 0552 if (_previews) 0553 _previews->deletePreview(viewItem); 0554 0555 preDeleteItem(viewItem); 0556 0557 if (viewItem->FILEITEM->isDir()) { 0558 --_numDirs; 0559 } 0560 0561 --_count; 0562 delete viewItem; 0563 0564 if (!onUpdate) 0565 op()->emitSelectionChanged(); 0566 } 0567 0568 void KrView::addItem(FileItem *fileItem, bool onUpdate) 0569 { 0570 if (isFiltered(fileItem)) 0571 return; 0572 0573 KrViewItem *viewItem = preAddItem(fileItem); 0574 if (!viewItem) 0575 return; // not added 0576 0577 if (_previews) 0578 _previews->updatePreview(viewItem); 0579 0580 if (fileItem->isDir()) 0581 ++_numDirs; 0582 0583 ++_count; 0584 0585 if (!onUpdate) { 0586 op()->emitSelectionChanged(); 0587 } 0588 } 0589 0590 void KrView::updateItem(FileItem *newFileItem) 0591 { 0592 // file name did not change 0593 const QString name = newFileItem->getName(); 0594 0595 // preserve 'current' and 'selection' 0596 const bool isCurrent = getCurrentItem() == name; 0597 QStringList selectedNames; 0598 getSelectedItems(&selectedNames, false); 0599 const bool isSelected = selectedNames.contains(name); 0600 0601 // delete old file item 0602 deleteItem(name, true); 0603 0604 if (!isFiltered(newFileItem)) { 0605 addItem(newFileItem, true); 0606 } 0607 0608 if (isCurrent) 0609 setCurrentItem(name, false); 0610 if (isSelected) 0611 setSelected(newFileItem, true); 0612 0613 op()->emitSelectionChanged(); 0614 } 0615 0616 void KrView::clear() 0617 { 0618 if (_previews) 0619 _previews->clear(); 0620 _count = _numDirs = 0; 0621 delete _dummyFileItem; 0622 _dummyFileItem = nullptr; 0623 redraw(); 0624 } 0625 0626 bool KrView::handleKeyEvent(QKeyEvent *e) 0627 { 0628 qDebug() << "key event=" << e; 0629 switch (e->key()) { 0630 case Qt::Key_Enter: 0631 case Qt::Key_Return: { 0632 if (e->modifiers() & Qt::ControlModifier) 0633 // let the panel handle it 0634 e->ignore(); 0635 else { 0636 KrViewItem *i = getCurrentKrViewItem(); 0637 if (i == nullptr) 0638 return true; 0639 QString tmp = i->name(); 0640 op()->emitExecuted(tmp); 0641 } 0642 return true; 0643 } 0644 case Qt::Key_QuoteLeft: 0645 // Terminal Emulator bugfix 0646 if (e->modifiers() == Qt::ControlModifier) { 0647 // let the panel handle it 0648 e->ignore(); 0649 } else { 0650 // a normal click - do a lynx-like moving thing 0651 // ask krusader to move to the home directory 0652 op()->emitGoHome(); 0653 } 0654 return true; 0655 case Qt::Key_Delete: 0656 // delete/trash the file (delete with alternative mode is a panel action) 0657 // allow only no modifier or KeypadModifier (i.e. Del on a Numeric Keypad) 0658 if ((e->modifiers() & ~Qt::KeypadModifier) == Qt::NoModifier) { 0659 op()->emitDefaultDeleteFiles(); 0660 } 0661 return true; 0662 case Qt::Key_Insert: { 0663 KrViewItem *i = getCurrentKrViewItem(); 0664 if (!i) 0665 return true; 0666 i->setSelected(!i->isSelected()); 0667 if (KrSelectionMode::getSelectionHandler()->insertMovesDown()) { 0668 KrViewItem *next = getNext(i); 0669 if (next) { 0670 setCurrentKrViewItem(next); 0671 makeItemVisible(next); 0672 } 0673 } 0674 op()->emitSelectionChanged(); 0675 return true; 0676 } 0677 case Qt::Key_Space: { 0678 KrViewItem *viewItem = getCurrentKrViewItem(); 0679 if (viewItem != nullptr) { 0680 viewItem->setSelected(!viewItem->isSelected()); 0681 0682 if (viewItem->getFileItem()->isDir() && KrSelectionMode::getSelectionHandler()->spaceCalculatesDiskSpace()) { 0683 op()->emitQuickCalcSpace(viewItem); 0684 } 0685 if (KrSelectionMode::getSelectionHandler()->spaceMovesDown()) { 0686 KrViewItem *next = getNext(viewItem); 0687 if (next) { 0688 setCurrentKrViewItem(next); 0689 makeItemVisible(next); 0690 } 0691 } 0692 op()->emitSelectionChanged(); 0693 } 0694 return true; 0695 } 0696 case Qt::Key_Backspace: 0697 // Terminal Emulator bugfix 0698 case Qt::Key_Left: 0699 if (e->modifiers() == Qt::ControlModifier || e->modifiers() == Qt::ShiftModifier || e->modifiers() == Qt::AltModifier) { 0700 // let the panel handle it 0701 e->ignore(); 0702 } else { 0703 // a normal click - do a lynx-like moving thing 0704 // ask krusader to move up a directory 0705 op()->emitDirUp(); 0706 } 0707 return true; // safety 0708 case Qt::Key_Right: 0709 if (e->modifiers() == Qt::ControlModifier || e->modifiers() == Qt::ShiftModifier || e->modifiers() == Qt::AltModifier) { 0710 // let the panel handle it 0711 e->ignore(); 0712 } else { 0713 // just a normal click - do a lynx-like moving thing 0714 KrViewItem *i = getCurrentKrViewItem(); 0715 if (i) 0716 op()->emitGoInside(i->name()); 0717 } 0718 return true; 0719 case Qt::Key_Up: 0720 if (e->modifiers() == Qt::ControlModifier) { 0721 // let the panel handle it - jump to the Location Bar 0722 e->ignore(); 0723 } else { 0724 KrViewItem *item = getCurrentKrViewItem(); 0725 if (item) { 0726 if (e->modifiers() == Qt::ShiftModifier) { 0727 item->setSelected(!item->isSelected()); 0728 op()->emitSelectionChanged(); 0729 } 0730 item = getPrev(item); 0731 if (item) { 0732 setCurrentKrViewItem(item); 0733 makeItemVisible(item); 0734 } 0735 } 0736 } 0737 return true; 0738 case Qt::Key_Down: 0739 if (e->modifiers() == Qt::ControlModifier || e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { 0740 // let the panel handle it - jump to command line 0741 e->ignore(); 0742 } else { 0743 KrViewItem *item = getCurrentKrViewItem(); 0744 if (item) { 0745 if (e->modifiers() == Qt::ShiftModifier) { 0746 item->setSelected(!item->isSelected()); 0747 op()->emitSelectionChanged(); 0748 } 0749 item = getNext(item); 0750 if (item) { 0751 setCurrentKrViewItem(item); 0752 makeItemVisible(item); 0753 } 0754 } 0755 } 0756 return true; 0757 case Qt::Key_Home: { 0758 if (e->modifiers() & Qt::ShiftModifier) { 0759 bool select = true; 0760 KrViewItem *pos = getCurrentKrViewItem(); 0761 if (pos == nullptr) 0762 pos = getLast(); 0763 KrViewItem *item = getFirst(); 0764 op()->setMassSelectionUpdate(true); 0765 while (item) { 0766 item->setSelected(select); 0767 if (item == pos) 0768 select = false; 0769 item = getNext(item); 0770 } 0771 op()->setMassSelectionUpdate(false); 0772 } 0773 KrViewItem *first = getFirst(); 0774 if (first) { 0775 setCurrentKrViewItem(first); 0776 makeItemVisible(first); 0777 } 0778 } 0779 return true; 0780 case Qt::Key_End: 0781 if (e->modifiers() & Qt::ShiftModifier) { 0782 bool select = false; 0783 KrViewItem *pos = getCurrentKrViewItem(); 0784 if (pos == nullptr) 0785 pos = getFirst(); 0786 op()->setMassSelectionUpdate(true); 0787 KrViewItem *item = getFirst(); 0788 while (item) { 0789 if (item == pos) 0790 select = true; 0791 item->setSelected(select); 0792 item = getNext(item); 0793 } 0794 op()->setMassSelectionUpdate(false); 0795 } else { 0796 KrViewItem *last = getLast(); 0797 if (last) { 0798 setCurrentKrViewItem(last); 0799 makeItemVisible(last); 0800 } 0801 } 0802 return true; 0803 case Qt::Key_PageDown: { 0804 KrViewItem *current = getCurrentKrViewItem(); 0805 int downStep = itemsPerPage(); 0806 while (downStep != 0 && current) { 0807 KrViewItem *newCurrent = getNext(current); 0808 if (newCurrent == nullptr) 0809 break; 0810 current = newCurrent; 0811 downStep--; 0812 } 0813 if (current) { 0814 setCurrentKrViewItem(current); 0815 makeItemVisible(current); 0816 } 0817 return true; 0818 } 0819 case Qt::Key_PageUp: { 0820 KrViewItem *current = getCurrentKrViewItem(); 0821 int upStep = itemsPerPage(); 0822 while (upStep != 0 && current) { 0823 KrViewItem *newCurrent = getPrev(current); 0824 if (newCurrent == nullptr) 0825 break; 0826 current = newCurrent; 0827 upStep--; 0828 } 0829 if (current) { 0830 setCurrentKrViewItem(current); 0831 makeItemVisible(current); 0832 } 0833 return true; 0834 } 0835 case Qt::Key_Escape: 0836 e->ignore(); 0837 return true; // otherwise the selection gets lost??!?? 0838 // also it is needed by the panel 0839 case Qt::Key_A: // mark all 0840 if (e->modifiers() == Qt::ControlModifier) { 0841 // FIXME: shouldn't there also be a shortcut for unselecting everything ? 0842 selectAllIncludingDirs(); 0843 return true; 0844 } 0845 #if __GNUC__ >= 7 0846 [[gnu::fallthrough]]; 0847 #endif 0848 default: 0849 return false; 0850 } 0851 return false; 0852 } 0853 0854 void KrView::zoomIn() 0855 { 0856 int idx = iconSizes.indexOf(_fileIconSize); 0857 if (idx >= 0 && (idx + 1) < iconSizes.count()) 0858 setFileIconSize(iconSizes[idx + 1]); 0859 } 0860 0861 void KrView::zoomOut() 0862 { 0863 int idx = iconSizes.indexOf(_fileIconSize); 0864 if (idx > 0) 0865 setFileIconSize(iconSizes[idx - 1]); 0866 } 0867 0868 void KrView::setFileIconSize(int size) 0869 { 0870 if (iconSizes.indexOf(size) < 0) 0871 return; 0872 _fileIconSize = size; 0873 if (_previews) { 0874 _previews->clear(); 0875 _previews->update(); 0876 } 0877 redraw(); 0878 op()->emitRefreshActions(); 0879 } 0880 0881 int KrView::defaultFileIconSize() 0882 { 0883 KConfigGroup grpSvr(_config, _instance.name()); 0884 return grpSvr.readEntry("IconSize", _FilelistIconSize).toInt(); 0885 } 0886 0887 void KrView::saveDefaultSettings(KrViewProperties::PropertyType properties) 0888 { 0889 saveSettings(KConfigGroup(_config, _instance.name()), properties); 0890 op()->emitRefreshActions(); 0891 } 0892 0893 void KrView::restoreDefaultSettings() 0894 { 0895 restoreSettings(KConfigGroup(_config, _instance.name())); 0896 } 0897 0898 void KrView::saveSettings(KConfigGroup group, KrViewProperties::PropertyType properties) 0899 { 0900 if (properties & KrViewProperties::PropIconSize) 0901 group.writeEntry("IconSize", fileIconSize()); 0902 if (properties & KrViewProperties::PropShowPreviews) 0903 group.writeEntry("ShowPreviews", previewsShown()); 0904 if (properties & KrViewProperties::PropSortMode) 0905 saveSortMode(group); 0906 if (properties & KrViewProperties::PropFilter) { 0907 group.writeEntry("Filter", static_cast<int>(_properties->filter)); 0908 group.writeEntry("FilterApplysToDirs", _properties->filterApplysToDirs); 0909 if (_properties->filterSettings.isValid()) 0910 _properties->filterSettings.save(KConfigGroup(&group, "FilterSettings")); 0911 } 0912 } 0913 0914 void KrView::restoreSettings(const KConfigGroup &group) 0915 { 0916 _ignoreSettingsChange = true; 0917 doRestoreSettings(group); 0918 _ignoreSettingsChange = false; 0919 refresh(); 0920 } 0921 0922 void KrView::doRestoreSettings(KConfigGroup group) 0923 { 0924 restoreSortMode(group); 0925 setFileIconSize(group.readEntry("IconSize", defaultFileIconSize())); 0926 showPreviews(group.readEntry("ShowPreviews", false)); 0927 _properties->filter = static_cast<KrViewProperties::FilterSpec>(group.readEntry("Filter", static_cast<int>(KrViewProperties::All))); 0928 _properties->filterApplysToDirs = group.readEntry("FilterApplysToDirs", false); 0929 _properties->filterSettings.load(KConfigGroup(&group, "FilterSettings")); 0930 _properties->filterMask = _properties->filterSettings.toQuery(); 0931 } 0932 0933 void KrView::applySettingsToOthers() 0934 { 0935 for (auto view : _instance.m_objects) { 0936 if (this != view) { 0937 view->_ignoreSettingsChange = true; 0938 view->copySettingsFrom(this); 0939 view->_ignoreSettingsChange = false; 0940 } 0941 } 0942 } 0943 0944 void KrView::sortModeUpdated(KrViewProperties::ColumnType sortColumn, bool descending) 0945 { 0946 if (sortColumn == _properties->sortColumn && descending == (bool)(_properties->sortOptions & KrViewProperties::Descending)) 0947 return; 0948 0949 int options = _properties->sortOptions; 0950 if (descending) 0951 options |= KrViewProperties::Descending; 0952 else 0953 options &= ~KrViewProperties::Descending; 0954 _properties->sortColumn = sortColumn; 0955 _properties->sortOptions = static_cast<KrViewProperties::SortOptions>(options); 0956 } 0957 0958 bool KrView::drawCurrent() const 0959 { 0960 return isFocused() || KConfigGroup(_config, "Look&Feel").readEntry("Always Show Current Item", _AlwaysShowCurrentItem); 0961 } 0962 0963 void KrView::saveSortMode(KConfigGroup &group) 0964 { 0965 group.writeEntry("Sort Column", static_cast<int>(_properties->sortColumn)); 0966 group.writeEntry("Descending Sort Order", _properties->sortOptions & KrViewProperties::Descending); 0967 } 0968 0969 void KrView::restoreSortMode(KConfigGroup &group) 0970 { 0971 int column = group.readEntry("Sort Column", static_cast<int>(KrViewProperties::Name)); 0972 bool isDescending = group.readEntry("Descending Sort Order", false); 0973 setSortMode(static_cast<KrViewProperties::ColumnType>(column), isDescending); 0974 } 0975 0976 QString KrView::krPermissionText(const FileItem *fileitem) 0977 { 0978 QString tmp; 0979 switch (fileitem->isReadable()) { 0980 case ALLOWED_PERM: 0981 tmp += 'r'; 0982 break; 0983 case UNKNOWN_PERM: 0984 tmp += '?'; 0985 break; 0986 case NO_PERM: 0987 tmp += '-'; 0988 break; 0989 } 0990 switch (fileitem->isWriteable()) { 0991 case ALLOWED_PERM: 0992 tmp += 'w'; 0993 break; 0994 case UNKNOWN_PERM: 0995 tmp += '?'; 0996 break; 0997 case NO_PERM: 0998 tmp += '-'; 0999 break; 1000 } 1001 switch (fileitem->isExecutable()) { 1002 case ALLOWED_PERM: 1003 tmp += 'x'; 1004 break; 1005 case UNKNOWN_PERM: 1006 tmp += '?'; 1007 break; 1008 case NO_PERM: 1009 tmp += '-'; 1010 break; 1011 } 1012 return tmp; 1013 } 1014 1015 QString KrView::permissionsText(const KrViewProperties *properties, const FileItem *fileItem) 1016 { 1017 return properties->numericPermissions ? QString().asprintf("%.4o", fileItem->getMode() & (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)) 1018 : fileItem->getPerm(); 1019 } 1020 1021 QString KrView::sizeText(const KrViewProperties *properties, KIO::filesize_t size) 1022 { 1023 return properties->humanReadableSize ? KIO::convertSize(size) : KrPermHandler::parseSize(size); 1024 } 1025 1026 QString KrView::mimeTypeText(FileItem *fileItem) 1027 { 1028 QMimeType mt = QMimeDatabase().mimeTypeForName(fileItem->getMime()); 1029 return mt.isValid() ? mt.comment() : QString(); 1030 } 1031 1032 bool KrView::isFiltered(FileItem *fileitem) 1033 { 1034 if (_quickFilterMask.isValid() && _quickFilterMask.indexIn(fileitem->getName()) == -1) 1035 return true; 1036 1037 bool filteredOut = false; 1038 bool isDir = fileitem->isDir(); 1039 if (!isDir || (isDir && properties()->filterApplysToDirs)) { 1040 switch (properties()->filter) { 1041 case KrViewProperties::All: 1042 break; 1043 case KrViewProperties::Custom: 1044 if (!properties()->filterMask.match(fileitem)) 1045 filteredOut = true; 1046 break; 1047 case KrViewProperties::Dirs: 1048 if (!isDir) 1049 filteredOut = true; 1050 break; 1051 case KrViewProperties::Files: 1052 if (isDir) 1053 filteredOut = true; 1054 break; 1055 default: 1056 break; 1057 } 1058 } 1059 return filteredOut; 1060 } 1061 1062 void KrView::setFiles(DirListerInterface *files) 1063 { 1064 if (files != _files) { 1065 clear(); 1066 if (_files) 1067 QObject::disconnect(_files, nullptr, op(), nullptr); 1068 _files = files; 1069 } 1070 1071 if (!_files) 1072 return; 1073 1074 QObject::disconnect(_files, nullptr, op(), nullptr); 1075 QObject::connect(_files, &DirListerInterface::scanDone, op(), &KrViewOperator::startUpdate); 1076 QObject::connect(_files, &DirListerInterface::cleared, op(), &KrViewOperator::cleared); 1077 QObject::connect(_files, &DirListerInterface::addedFileItem, op(), &KrViewOperator::fileAdded); 1078 QObject::connect(_files, &DirListerInterface::updatedFileItem, op(), &KrViewOperator::fileUpdated); 1079 } 1080 1081 void KrView::setFilter(KrViewProperties::FilterSpec filter, const FilterSettings &customFilter, bool applyToDirs) 1082 { 1083 _properties->filter = filter; 1084 _properties->filterSettings = customFilter; 1085 _properties->filterMask = customFilter.toQuery(); 1086 _properties->filterApplysToDirs = applyToDirs; 1087 refresh(); 1088 } 1089 1090 void KrView::setFilter(KrViewProperties::FilterSpec filter) 1091 { 1092 KConfigGroup cfg(_config, "Look&Feel"); 1093 bool rememberSettings = cfg.readEntry("FilterDialogRemembersSettings", _FilterDialogRemembersSettings); 1094 bool applyToDirs = rememberSettings ? _properties->filterApplysToDirs : false; 1095 switch (filter) { 1096 case KrViewProperties::All: 1097 break; 1098 case KrViewProperties::Custom: { 1099 QString applyFilterToFolders = i18n("Apply filter to folder&s"); 1100 // Note: It has the same shortcut as "Apply &selection to folders" has 1101 // in a very similar dialog (which is aimed to select files/folders). 1102 // The "Alt+A" and "Alt+F" shortcuts were already taken 1103 1104 FilterDialog dialog(_widget, i18n("Filter Files"), QStringList(applyFilterToFolders), false); 1105 dialog.checkExtraOption(applyFilterToFolders, applyToDirs); 1106 if (rememberSettings) 1107 dialog.applySettings(_properties->filterSettings); 1108 dialog.exec(); 1109 FilterSettings s(dialog.getSettings()); 1110 if (!s.isValid()) // if the user canceled -> quit 1111 return; 1112 _properties->filterSettings = s; 1113 _properties->filterMask = s.toQuery(); 1114 applyToDirs = dialog.isExtraOptionChecked(applyFilterToFolders); 1115 } break; 1116 default: 1117 return; 1118 } 1119 _properties->filterApplysToDirs = applyToDirs; 1120 _properties->filter = filter; 1121 refresh(); 1122 } 1123 1124 void KrView::customSelection(bool select) 1125 { 1126 KConfigGroup grpSvr(_config, "Look&Feel"); 1127 bool includeDirs = grpSvr.readEntry("Mark Dirs", _MarkDirs); 1128 1129 QString applySelToFolders = i18n("Apply &selection to folders"); 1130 FilterDialog dialog(nullptr, i18n("Select Files"), QStringList(applySelToFolders), false); 1131 dialog.checkExtraOption(applySelToFolders, includeDirs); 1132 dialog.exec(); 1133 KrQuery query = dialog.getQuery(); 1134 // if the user canceled -> quit 1135 if (query.isNull()) 1136 return; 1137 includeDirs = dialog.isExtraOptionChecked(applySelToFolders); 1138 1139 changeSelection(query, select, includeDirs); 1140 } 1141 1142 void KrView::refresh() 1143 { 1144 const QString currentItem = !nameToMakeCurrent().isEmpty() ? // 1145 nameToMakeCurrent() 1146 : getCurrentItem(); 1147 bool scrollToCurrent = !nameToMakeCurrent().isEmpty() || isItemVisible(getCurrentKrViewItem()); 1148 setNameToMakeCurrent(QString()); 1149 1150 const QModelIndex currentIndex = getCurrentIndex(); 1151 const QList<QUrl> selection = selectedUrls(); 1152 1153 clear(); 1154 1155 if (!_files) 1156 return; 1157 1158 QList<FileItem *> fileItems; 1159 1160 // if we are not at the root add the ".." entry 1161 if (!_files->isRoot()) { 1162 _dummyFileItem = FileItem::createDummy(); 1163 fileItems << _dummyFileItem; 1164 } 1165 1166 foreach (FileItem *fileitem, _files->fileItems()) { 1167 if (!fileitem || isFiltered(fileitem)) 1168 continue; 1169 if (fileitem->isDir()) 1170 _numDirs++; 1171 _count++; 1172 fileItems << fileitem; 1173 } 1174 1175 populate(fileItems, _dummyFileItem); 1176 1177 if (!selection.isEmpty()) 1178 setSelectionUrls(selection); 1179 1180 if (!currentItem.isEmpty()) { 1181 if (currentItem == ".." && _count > 0 && // 1182 !_quickFilterMask.isEmpty() && _quickFilterMask.isValid()) { 1183 // In a filtered view we should never select the dummy entry if 1184 // there are real matches. 1185 setCurrentKrViewItem(getNext(getFirst())); 1186 } else { 1187 setCurrentItem(currentItem, scrollToCurrent, currentIndex); 1188 } 1189 } else { 1190 setCurrentKrViewItem(getFirst()); 1191 } 1192 1193 updatePreviews(); 1194 redraw(); 1195 1196 op()->emitSelectionChanged(); 1197 } 1198 1199 void KrView::setSelected(const FileItem *fileitem, bool select) 1200 { 1201 if (fileitem == _dummyFileItem) 1202 return; 1203 1204 if (select) 1205 clearSavedSelection(); 1206 intSetSelected(fileitem, select); 1207 } 1208 1209 void KrView::saveSelection() 1210 { 1211 _savedSelection = selectedUrls(); 1212 op()->emitRefreshActions(); 1213 } 1214 1215 void KrView::restoreSelection() 1216 { 1217 if (canRestoreSelection()) 1218 setSelectionUrls(_savedSelection); 1219 } 1220 1221 void KrView::clearSavedSelection() 1222 { 1223 _savedSelection.clear(); 1224 op()->emitRefreshActions(); 1225 } 1226 1227 void KrView::markSameBaseName() 1228 { 1229 KrViewItem *item = getCurrentKrViewItem(); 1230 if (!item) 1231 return; 1232 KrQuery query(QString("%1.*").arg(item->name(false))); 1233 changeSelection(query, true, false); 1234 } 1235 1236 void KrView::markSameExtension() 1237 { 1238 KrViewItem *item = getCurrentKrViewItem(); 1239 if (!item) 1240 return; 1241 KrQuery query(QString("*.%1").arg(item->extension())); 1242 changeSelection(query, true, false); 1243 }