File indexing completed on 2024-04-21 05:50:00

0001 //**************************************************************************
0002 //   Copyright 2006 - 2018 Martin Koller, kollix@aon.at
0003 //
0004 //   This program is free software; you can redistribute it and/or modify
0005 //   it under the terms of the GNU General Public License as published by
0006 //   the Free Software Foundation, version 2 of the License
0007 //
0008 //**************************************************************************
0009 
0010 #include <Selector.hxx>
0011 
0012 #include <kio/global.h>
0013 #include <KIconLoader>
0014 #include <KIconEffect>
0015 #include <KIconUtils>
0016 #include <KLocalizedString>
0017 #include <KPropertiesDialog>
0018 #include <KFileItem>
0019 #include <KApplicationTrader>
0020 #include <KActionCollection>
0021 #include <KMessageBox>
0022 
0023 #include <KIO/ApplicationLauncherJob>
0024 #include <KIO/OpenUrlJob>
0025 #include <kio_version.h>
0026 #include <KIO/JobUiDelegateFactory>
0027 
0028 #include <QDir>
0029 #include <QPixmap>
0030 #include <QDateTime>
0031 #include <QCollator>
0032 #include <QHeaderView>
0033 #include <QMenu>
0034 #include <QPointer>
0035 
0036 #include <iostream>
0037 using namespace std;
0038 
0039 //--------------------------------------------------------------------------------
0040 //--------------------------------------------------------------------------------
0041 
0042 class Model : public QStandardItemModel
0043 {
0044   public:
0045     explicit Model(Selector *parent) : QStandardItemModel(parent), tree(parent)
0046     {
0047       const char *lc_collate = ::getenv("LC_COLLATE");
0048       if ( lc_collate )
0049         collator.setLocale(QLocale(QLatin1String(lc_collate)));
0050 
0051       collator.setNumericMode(true);
0052     }
0053 
0054     bool hasChildren(const QModelIndex &index = QModelIndex()) const override
0055     {
0056       QStandardItem *item = itemFromIndex(index);
0057 
0058       if ( !item )
0059         return true;  // invisible root
0060 
0061       return !(item->flags() & Qt::ItemNeverHasChildren);
0062     }
0063 
0064     Selector *tree;
0065     QCollator collator;
0066 };
0067 
0068 //--------------------------------------------------------------------------------
0069 //--------------------------------------------------------------------------------
0070 
0071 class ListItem : public QStandardItem
0072 {
0073   public:
0074     ListItem(QStandardItem *parent, const QString &text, bool dir)
0075       : isDir_(dir), partly(false)
0076     {
0077       parent->appendRow(this);
0078       parent->setChild(row(), 1, new QStandardItem);
0079       parent->setChild(row(), 2, new QStandardItem);
0080 
0081       setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
0082 
0083       if ( !isDir_ )
0084         setFlags(flags() | Qt::ItemNeverHasChildren);
0085 
0086       setCheckState(Qt::Unchecked);
0087       setText(0, text, key(text));
0088     }
0089 
0090     int type() const override { return QStandardItem::UserType; }
0091 
0092     bool isOn() const { return checkState() == Qt::Checked; }
0093     void setOn(bool on) { setCheckState(on ? Qt::Checked : Qt::Unchecked); }
0094 
0095     void setText(int column, const QString &txt, const QVariant &sortKey)
0096     {
0097       QStandardItem *item;
0098 
0099       if ( parent() )
0100         item = parent()->child(row(), column);
0101       else
0102         item = model()->item(row(), column);
0103 
0104       item->setText(txt);
0105       item->setData(sortKey, Qt::UserRole);
0106     }
0107 
0108     void setData(const QVariant &value, int role = Qt::UserRole + 1) override
0109     {
0110       if ( role == Qt::CheckStateRole )
0111       {
0112         if ( value.toInt() != checkState() )
0113         {
0114           QStandardItem::setData(value, role);
0115           stateChanged();
0116           return;
0117         }
0118       }
0119 
0120       QStandardItem::setData(value, role);
0121     }
0122 
0123     // check if all siblings have the same state as the parent or are partly marked
0124     // If not, then the parent must have the partly flag set, otherwise the parents
0125     // partly flag can be cleared
0126     void recursSiblingsUp()
0127     {
0128       if ( !parent() ) return;
0129 
0130       bool allSame = true, state = static_cast<ListItem*>(parent())->isOn();
0131 
0132       for (int i = 0; i < parent()->rowCount(); i++)
0133       {
0134         QStandardItem *item = parent()->child(i);
0135 
0136         if ( (static_cast<ListItem*>(item)->isOn() != state) || static_cast<ListItem*>(item)->partly )
0137         {
0138           allSame = false;
0139           break;
0140         }
0141       }
0142 
0143       // only continue upwards if the parents partly status changes
0144       if ( static_cast<ListItem*>(parent())->partly != !allSame )
0145       {
0146         static_cast<ListItem*>(parent())->partly = !allSame;
0147 
0148         if ( !allSame )
0149           static_cast<ListItem*>(parent())->setForeground(Qt::blue);
0150         else
0151         {
0152           QWidget *w = static_cast<Model *>(model())->tree;
0153           static_cast<ListItem*>(parent())->setForeground(w->palette().color(w->foregroundRole()));
0154         }
0155 
0156         static_cast<ListItem*>(parent())->recursSiblingsUp();
0157       }
0158     }
0159 
0160     void stateChanged()
0161     {
0162       recursActivate(isOn());
0163       recursSiblingsUp();
0164     }
0165 
0166     // set all children recursively below this to on
0167     void recursActivate(bool on)
0168     {
0169       partly = false;  // all children will get the same state
0170 
0171       QWidget *w = static_cast<Model *>(model())->tree;
0172       setForeground(w->palette().color(w->foregroundRole()));
0173 
0174       setOn(on);
0175 
0176       for (int i = 0; i < rowCount(); i++)
0177         static_cast<ListItem*>(child(i))->recursActivate(on);
0178     }
0179 
0180     bool isDir() const { return isDir_; }
0181 
0182     int key(const QString &text) const
0183     {
0184       bool hidden = text[0] == QLatin1Char('.');
0185 
0186       // sort directories _always_ first, and hidden before shown
0187       if ( isDir_ )
0188         return hidden ? 0 : 1;
0189       else  // file
0190         return hidden ? 2 : 3;
0191     }
0192 
0193     bool operator<(const QStandardItem &other_) const  override
0194     {
0195       QTreeView *w = static_cast<Model *>(model())->tree;
0196       Qt::SortOrder order = w->header()->sortIndicatorOrder();
0197 
0198       const ListItem &other = static_cast<const ListItem &>(other_);
0199 
0200       int myKey = data(Qt::UserRole).toInt();
0201       int otherKey = other.data(Qt::UserRole).toInt();
0202 
0203       if ( myKey != otherKey )
0204         return (order == Qt::AscendingOrder) ? (myKey < otherKey) : (myKey > otherKey);
0205       else
0206       {
0207         // don't use localeAwareCompare. QLocale does not use LC_COLLATE QTBUG-29397
0208         return static_cast<Model *>(model())->collator.compare(text(), other.text()) < 0;
0209       }
0210     }
0211 
0212     void setSize(KIO::filesize_t size)
0213     {
0214       setText(1, KIO::convertSize(size), size);
0215     }
0216 
0217     void setLastModified(const QDateTime &time)
0218     {
0219       setText(2, QLocale().toString(time, QLocale::ShortFormat), time);
0220     }
0221 
0222     void setShowHiddenFiles(bool show)
0223     {
0224       QTreeView *w = static_cast<Model *>(model())->tree;
0225 
0226       QStandardItem *parentItem = parent() ? parent() : model()->invisibleRootItem();
0227 
0228       w->setRowHidden(row(), parentItem->index(), show ? false : text()[0] == QLatin1Char('.'));
0229 
0230       for (int i = 0; i < rowCount(); i++)
0231         static_cast<ListItem*>(child(i))->setShowHiddenFiles(show);
0232     }
0233 
0234   private:
0235     bool isDir_;
0236     bool partly;  // is this an item which is not fully (but partly - some of the children) selected
0237 
0238 };
0239 
0240 //--------------------------------------------------------------------------------
0241 //--------------------------------------------------------------------------------
0242 //--------------------------------------------------------------------------------
0243 //--------------------------------------------------------------------------------
0244 
0245 Selector::Selector(QWidget *parent, KActionCollection *actionCollection)
0246   : QTreeView(parent)
0247 {
0248   itemModel = new Model(this);
0249 
0250   setModel(itemModel);
0251 
0252   itemModel->setSortRole(Qt::UserRole);
0253   itemModel->setHorizontalHeaderLabels(QStringList() << i18n("Name") << i18n("Size") << i18n("Last Modified"));
0254 
0255   setRootIsDecorated(true);
0256 
0257   // start with / as root node
0258   ListItem *item = new ListItem(itemModel->invisibleRootItem(), QStringLiteral("/"), true);
0259   QFileInfo info(QStringLiteral("/"));
0260   item->setSize(info.size());
0261   item->setLastModified(info.lastModified());
0262   item->setIcon(QIcon::fromTheme(QStringLiteral("folder")));
0263   setExpanded(item->index(), true);
0264 
0265   fillTree(item, QStringLiteral("/"), false);
0266 
0267   connect(this, &Selector::expanded, this, &Selector::expandedSlot);
0268 
0269   minSize = QSize(columnWidth(0) + columnWidth(1), -1);
0270   resizeColumnToContents(0);
0271   resizeColumnToContents(1);
0272   resizeColumnToContents(2);
0273 
0274   sortByColumn(0, Qt::AscendingOrder);
0275 
0276   // context menu
0277   menu = new QMenu(this);
0278   QAction *action;
0279 
0280   action = KStandardAction::open(this, SLOT(open()), actionCollection);
0281   menu->addAction(action);
0282 
0283   connect(this, &Selector::doubleClicked, this, &Selector::doubleClickedSlot);
0284 
0285   openWithSubMenu = new QMenu(i18n("Open With"), this);
0286   menu->addMenu(openWithSubMenu);
0287   connect(openWithSubMenu, &QMenu::aboutToShow, this, &Selector::populateOpenMenu);
0288   connect(openWithSubMenu, &QMenu::triggered, this, &Selector::openWith);
0289 
0290   // just since KF 5.25
0291   //deleteFileAction = KStandardAction::deleteFile(this, SLOT(deleteFile()), actionCollection);
0292   deleteFileAction = actionCollection->addAction(QStringLiteral("deleteFile"), this, SLOT(deleteFile()));
0293   deleteFileAction->setText(i18n("Delete File"));
0294   deleteFileAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
0295   deleteFileAction->setShortcut(QKeySequence(Qt::SHIFT | Qt::Key_Delete));
0296   menu->addAction(deleteFileAction);
0297 
0298   action = actionCollection->addAction(QStringLiteral("properties"), this, SLOT(properties()));
0299   action->setText(i18n("Properties..."));
0300   menu->addAction(action);
0301 }
0302 
0303 //--------------------------------------------------------------------------------
0304 
0305 void Selector::openHomeDir()
0306 {
0307   const char *home = ::getenv("HOME");
0308   if ( home )
0309   {
0310     QStandardItem *homeItem = findItemByPath(QFile::decodeName(home));
0311     if ( homeItem )
0312     {
0313       setExpanded(homeItem->index(), true);
0314       scrollTo(homeItem->index());
0315     }
0316   }
0317 }
0318 
0319 //--------------------------------------------------------------------------------
0320 
0321 QSize Selector::minimumSizeHint() const
0322 {
0323   return minSize;
0324 }
0325 
0326 //--------------------------------------------------------------------------------
0327 
0328 void Selector::fillTree(ListItem *parent, const QString &path, bool on)
0329 {
0330   setSortingEnabled(false);
0331 
0332   const QDir::Filters filter = QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot;
0333 
0334   QDir dir(path, QString(), QDir::NoSort, filter);
0335   const QFileInfoList list = dir.entryInfoList();
0336 
0337   ListItem *item;
0338 
0339   for (int i = 0; i < list.count(); i++)
0340   {
0341     if ( parent )
0342       item = new ListItem(parent, list[i].fileName(), list[i].isDir());
0343     else
0344       item = new ListItem(itemModel->invisibleRootItem(), list[i].fileName(), list[i].isDir());
0345 
0346     item->setOn(on);
0347     item->setSize(list[i].size());
0348     item->setLastModified(list[i].lastModified());
0349     item->setShowHiddenFiles(showHiddenFiles);
0350 
0351     if ( item->isDir() )
0352     {
0353       QDir dir(list[i].absoluteFilePath(), QString(), QDir::NoSort, filter);
0354 
0355       // symlinked dirs can not be expanded as they are stored as single files in the archive
0356       if ( (dir.count() > 0) && !list[i].isSymLink() )
0357         ; // can have children
0358       else
0359         item->setFlags(item->flags() | Qt::ItemNeverHasChildren);
0360 
0361       static QPixmap folderIcon;
0362       static QPixmap folderLinkIcon;
0363       static QPixmap folderIconHidden;
0364       static QPixmap folderLinkIconHidden;
0365 
0366       if ( folderIcon.isNull() )  // only get the icons once
0367       {
0368         KIconEffect effect;
0369 
0370         folderIcon = QIcon::fromTheme(QStringLiteral("folder")).pixmap(KIconLoader::SizeSmall);
0371         folderIconHidden = effect.apply(folderIcon, KIconEffect::DeSaturate, 0, QColor(), true);
0372 
0373         folderLinkIcon = KIconUtils::addOverlay(folderIcon,
0374                                                 QIcon::fromTheme(QStringLiteral("emblem-symbolic-link")),
0375                                                 Qt::BottomRightCorner).pixmap(KIconLoader::SizeSmall);
0376 
0377         folderLinkIconHidden = effect.apply(folderLinkIcon, KIconEffect::DeSaturate, 0, QColor(), true);
0378       }
0379 
0380       item->setIcon(list[i].isSymLink() ?
0381                            (list[i].isHidden() ? folderLinkIconHidden : folderLinkIcon)
0382                          : (list[i].isHidden() ? folderIconHidden : folderIcon));
0383     }
0384     else
0385     {
0386       static QPixmap documentIcon;
0387       static QPixmap documentLinkIcon;
0388       static QPixmap documentIconHidden;
0389       static QPixmap documentLinkIconHidden;
0390 
0391       if ( documentIcon.isNull() )  // only get the icons once
0392       {
0393         KIconEffect effect;
0394 
0395         documentIcon = QIcon::fromTheme(QStringLiteral("text-x-generic")).pixmap(KIconLoader::SizeSmall);
0396         documentIconHidden = effect.apply(documentIcon, KIconEffect::DeSaturate, 0, QColor(), true);
0397 
0398         documentLinkIcon = KIconUtils::addOverlay(documentIcon,
0399                                                 QIcon::fromTheme(QStringLiteral("emblem-symbolic-link")),
0400                                                 Qt::BottomRightCorner).pixmap(KIconLoader::SizeSmall);
0401 
0402         documentLinkIconHidden = effect.apply(documentLinkIcon, KIconEffect::DeSaturate, 0, QColor(), true);
0403       }
0404 
0405       item->setIcon(list[i].isSymLink() ?
0406                            (list[i].isHidden() ? documentLinkIconHidden : documentLinkIcon)
0407                          : (list[i].isHidden() ? documentIconHidden : documentIcon));
0408     }
0409   }
0410   setSortingEnabled(true);
0411 }
0412 
0413 //--------------------------------------------------------------------------------
0414 
0415 QString Selector::getPath(QStandardItem *item) const
0416 {
0417   if ( !item )
0418     return QString();
0419   else if ( !item->parent() )
0420     return item->text();  // root
0421   else if ( item->parent() == itemModel->item(0) )
0422     return QStringLiteral("/") + item->text();
0423   else
0424     return getPath(item->parent()) + QStringLiteral("/") + item->text();
0425 }
0426 
0427 //--------------------------------------------------------------------------------
0428 
0429 void Selector::expandedSlot(const QModelIndex &index)
0430 {
0431   QStandardItem *item = itemModel->itemFromIndex(index);
0432 
0433   if ( item->rowCount() ) return;  // already done
0434 
0435   fillTree(static_cast<ListItem *>(item), getPath(item), static_cast<ListItem *>(item)->isOn());
0436 }
0437 
0438 //--------------------------------------------------------------------------------
0439 
0440 void Selector::getBackupList(QStringList &includes, QStringList &excludes) const
0441 {
0442   for (int i = 0; i < itemModel->rowCount(); i++)
0443     getBackupLists(itemModel->item(i, 0), includes, excludes);
0444 
0445   /*
0446   cerr << "includes:" << includes.count() << endl;
0447   for (QStringList::const_iterator it = includes.begin(); (it != includes.end()); ++it)
0448     cerr << *it << endl;
0449 
0450   cerr << "excludes:" << excludes.count() << endl;
0451   for (QStringList::const_iterator it = excludes.begin(); (it != excludes.end()); ++it)
0452     cerr << *it << endl;
0453 
0454   cerr << endl;
0455   */
0456 }
0457 
0458 //--------------------------------------------------------------------------------
0459 
0460 void Selector::getBackupLists(QStandardItem *start, QStringList &includes, QStringList &excludes, bool add) const
0461 {
0462   if ( static_cast<ListItem*>(start)->isOn() )
0463   {
0464     if ( add )
0465       includes.append(getPath(start));  // include it
0466 
0467     if ( static_cast<ListItem*>(start)->isDir() )
0468     {
0469       // get excludes from this dir
0470       for (int i = 0; i < start->rowCount(); i++)
0471       {
0472         QStandardItem *item = start->child(i);
0473 
0474         if ( !static_cast<ListItem*>(item)->isOn() )
0475           excludes.append(getPath(item));
0476 
0477         if ( static_cast<ListItem*>(item)->isDir() )
0478           getBackupLists(item, includes, excludes, false);
0479       }
0480     }
0481   }
0482   else
0483     if ( static_cast<ListItem*>(start)->isDir() )
0484     {
0485       for (int i = 0; i < start->rowCount(); i++)
0486         getBackupLists(start->child(i), includes, excludes);
0487     }
0488 }
0489 
0490 //--------------------------------------------------------------------------------
0491 
0492 void Selector::setBackupList(const QStringList &includes, const QStringList &excludes)
0493 {
0494   // clear all current settings
0495   for (int i = 0; i < itemModel->rowCount(); i++)
0496     static_cast<ListItem*>(itemModel->item(i, 0))->recursActivate(false);
0497 
0498   for (QStringList::const_iterator it = includes.begin(); (it != includes.end()); ++it)
0499   {
0500     QStandardItem *item = findItemByPath(*it);
0501     if ( item )
0502       static_cast<ListItem*>(item)->recursActivate(true);
0503   }
0504 
0505   for (QStringList::const_iterator it = excludes.begin(); (it != excludes.end()); ++it)
0506   {
0507     QStandardItem *item = findItemByPath(*it);
0508     if ( item )
0509       static_cast<ListItem*>(item)->setOn(false);
0510   }
0511 }
0512 
0513 //--------------------------------------------------------------------------------
0514 
0515 QStandardItem *Selector::findItemByPath(const QString &path)
0516 {
0517   QStringList items = path.split(QLatin1Char('/'), Qt::SkipEmptyParts);
0518   QStandardItem *item = itemModel->invisibleRootItem()->child(0);
0519 
0520   for (int i = 0; i < items.count(); i++)
0521   {
0522     item = findItem(item, items[i]);
0523 
0524     if ( !item )
0525       return nullptr;
0526     else
0527     {
0528       if ( (i != (items.count() - 1)) &&
0529            static_cast<ListItem*>(item)->isDir() && (item->rowCount() == 0) )
0530         expandedSlot(item->index());
0531     }
0532   }
0533 
0534   return item;
0535 }
0536 
0537 //--------------------------------------------------------------------------------
0538 
0539 QStandardItem *Selector::findItem(QStandardItem *start, const QString &toFind) const
0540 {
0541   for (int i = 0; i < (start ? start->rowCount() : itemModel->rowCount()); i++)
0542   {
0543     QStandardItem *item = start ? start->child(i) : itemModel->item(i, 0);
0544 
0545     if ( item->text() == toFind )
0546       return item;
0547   }
0548 
0549   return nullptr;
0550 }
0551 
0552 //--------------------------------------------------------------------------------
0553 
0554 ListItem *Selector::getSelectedItem() const
0555 {
0556   QModelIndex index = selectionModel()->currentIndex();
0557   if ( !index.isValid() )
0558     return nullptr;
0559 
0560   QStandardItem *item = itemModel->itemFromIndex(itemModel->index(index.row(), 0, index.parent()));
0561 
0562   if ( !item || (item->type() != QStandardItem::UserType) )  // just be safe
0563     return nullptr;
0564 
0565   return static_cast<ListItem *>(item);
0566 }
0567 
0568 //--------------------------------------------------------------------------------
0569 
0570 void Selector::contextMenuEvent(QContextMenuEvent *)
0571 {
0572   ListItem *item = getSelectedItem();
0573 
0574   if ( !item )
0575     return;
0576 
0577   bool canDelete = true;
0578 
0579   if ( item->isDir() )
0580   {
0581     // only if it's empty
0582     canDelete = QDir(getPath(item), QString(), QDir::NoSort,
0583                      QDir::AllEntries | QDir::System | QDir::NoDotAndDotDot).count() == 0;
0584   }
0585 
0586   deleteFileAction->setEnabled(canDelete);
0587 
0588   menu->exec(QCursor::pos());
0589 }
0590 
0591 //--------------------------------------------------------------------------------
0592 
0593 void Selector::deleteFile()
0594 {
0595   ListItem *item = getSelectedItem();
0596 
0597   if ( !item )
0598     return;
0599 
0600   QUrl sourceUrl = QUrl::fromLocalFile(getPath(item));
0601 
0602   if ( KMessageBox::warningTwoActions(this,
0603           i18n("Do you really want to delete '%1'?", sourceUrl.path()),
0604           i18n("Delete"),
0605           KStandardGuiItem::del(), KStandardGuiItem::cancel(),
0606           QStringLiteral("dontAskAgainDelete")) == KMessageBox::PrimaryAction )
0607   {
0608     QStandardItem *parent = nullptr;
0609 
0610     if ( item->isDir() )
0611     {
0612       QDir dir(sourceUrl.path());
0613 
0614       if ( !dir.removeRecursively() )
0615         KMessageBox::error(this, i18n("Could not delete directory '%1'.", sourceUrl.path()));
0616       else
0617         parent = item->parent();
0618     }
0619     else
0620     {
0621       QFile theFile(sourceUrl.path());
0622 
0623       if ( !theFile.remove(sourceUrl.path()) )
0624         KMessageBox::error(this, i18n("Could not delete file '%1'.\nReason: %2", sourceUrl.path(), theFile.errorString()));
0625       else
0626         parent = item->parent();
0627     }
0628 
0629     if ( parent )
0630     {
0631       parent->removeRow(item->row());
0632 
0633       if ( parent->type() == QStandardItem::UserType )
0634         static_cast<ListItem *>(parent)->stateChanged();  // make sure removed item is taken into account
0635     }
0636   }
0637 }
0638 
0639 //--------------------------------------------------------------------------------
0640 
0641 void Selector::properties()
0642 {
0643   ListItem *item = getSelectedItem();
0644 
0645   if ( !item )
0646     return;
0647 
0648   QUrl sourceUrl = QUrl::fromLocalFile(getPath(item));
0649 
0650   QPointer<KPropertiesDialog> dialog = new KPropertiesDialog(sourceUrl, this);
0651   connect(dialog.data(), &KPropertiesDialog::applied, this,
0652           [item, dialog]()
0653           {
0654             // make sure a renamed file is shown with the new name in the tree
0655             item->setText(0, dialog->item().name(), item->key(dialog->item().name()));
0656           });
0657 
0658   dialog->exec();
0659   delete dialog;
0660 }
0661 
0662 //--------------------------------------------------------------------------------
0663 
0664 void Selector::populateOpenMenu()
0665 {
0666   ListItem *item = getSelectedItem();
0667 
0668   if ( !item )
0669     return;
0670 
0671   QUrl sourceUrl = QUrl::fromLocalFile(getPath(item));
0672 
0673   qDeleteAll(openWithSubMenu->actions());
0674   serviceForName.clear();
0675 
0676   KFileItem fileItem(sourceUrl);
0677   QString mimeType(fileItem.determineMimeType().name());
0678 
0679   const KService::List services = KApplicationTrader::queryByMimeType(mimeType);
0680 
0681   for (const KService::Ptr &service : services)
0682   {
0683     QString text = service->name().replace(QLatin1Char('&'), QStringLiteral("&&"));
0684     QAction* action = openWithSubMenu->addAction(text);
0685     action->setIcon(QIcon::fromTheme(service->icon()));
0686     action->setData(service->name());
0687 
0688     serviceForName[service->name()] = service;
0689   }
0690 
0691   openWithSubMenu->addSeparator();
0692   openWithSubMenu->addAction(i18n("Other Application..."));
0693 
0694   QAction* action = openWithSubMenu->addAction(i18n("File Manager"));
0695   action->setIcon(QIcon::fromTheme(QStringLiteral("folder")));
0696   action->setData(QStringLiteral("-"));
0697 }
0698 
0699 //--------------------------------------------------------------------------------
0700 
0701 void Selector::doubleClickedSlot()
0702 {
0703   ListItem *item = getSelectedItem();
0704 
0705   if ( !item || item->isDir() )
0706     return;
0707 
0708   open();
0709 }
0710 
0711 //--------------------------------------------------------------------------------
0712 
0713 void Selector::open()
0714 {
0715   ListItem *item = getSelectedItem();
0716 
0717   if ( !item )
0718     return;
0719 
0720   QUrl sourceUrl = QUrl::fromLocalFile(getPath(item));
0721 
0722   auto *job = new KIO::OpenUrlJob(sourceUrl, window());
0723   job->setRunExecutables(false);
0724   job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0725   job->start();
0726 }
0727 
0728 //--------------------------------------------------------------------------------
0729 
0730 void Selector::openWith(QAction *action)
0731 {
0732   ListItem *item = getSelectedItem();
0733 
0734   if ( !item )
0735     return;
0736 
0737   QUrl sourceUrl = QUrl::fromLocalFile(getPath(item));
0738 
0739   QString name = action->data().toString();
0740 
0741   if ( name.isEmpty() ) // Other Application...
0742   {
0743     KIO::ApplicationLauncherJob *job = new KIO::ApplicationLauncherJob();
0744     job->setUrls(QList<QUrl>() << sourceUrl);
0745     job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0746     job->start();
0747     return;
0748   }
0749 
0750   if ( name == QLatin1String("-") )  // File Manager
0751   {
0752     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(sourceUrl.adjusted(QUrl::RemoveFilename), QStringLiteral("inode/directory"));
0753     job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0754     job->start();
0755 
0756     return;
0757   }
0758 
0759   KService::Ptr service = serviceForName[name];
0760   KIO::ApplicationLauncherJob *job = new KIO::ApplicationLauncherJob(service);
0761   job->setUrls(QList<QUrl>() << sourceUrl);
0762   job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0763   job->start();
0764 }
0765 
0766 //--------------------------------------------------------------------------------
0767 
0768 void Selector::setShowHiddenFiles(bool show)
0769 {
0770   showHiddenFiles = show;
0771 
0772   for (int i = 0; i < itemModel->invisibleRootItem()->rowCount(); i++)
0773     static_cast<ListItem *>(itemModel->item(i, 0))->setShowHiddenFiles(show);
0774 }
0775 
0776 //--------------------------------------------------------------------------------
0777 
0778 #include "moc_Selector.cpp"