File indexing completed on 2024-04-21 16:33:18

0001 /*
0002     SPDX-FileCopyrightText: 2001 Shie Erlich <krusader@users.sourceforge.net>
0003     SPDX-FileCopyrightText: 2001 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 "krslots.h"
0010 
0011 // QtCore
0012 #include <QDir>
0013 #include <QEvent>
0014 #include <QList>
0015 #include <QPoint>
0016 #include <QStringList>
0017 #include <QTemporaryFile>
0018 // QtGui
0019 #include <QKeyEvent>
0020 #include <QPixmapCache>
0021 // QtWidgets
0022 #include <QApplication>
0023 
0024 #include <KConfigCore/KSharedConfig>
0025 #include <KCoreAddons/KShell>
0026 #include <KI18n/KLocalizedString>
0027 #include <KWidgetsAddons/KMessageBox>
0028 #include <KWidgetsAddons/KToggleAction>
0029 #include <utility>
0030 
0031 #include "defaults.h"
0032 #include "icon.h"
0033 #include "kractions.h"
0034 #include "krservices.h"
0035 #include "krtrashhandler.h"
0036 #include "krusader.h"
0037 #include "krusaderview.h"
0038 #include "panelmanager.h"
0039 
0040 #include "ActionMan/actionman.h"
0041 #include "BookMan/krbookmarkbutton.h"
0042 #include "BookMan/krbookmarkhandler.h"
0043 #include "Dialogs/krdialogs.h"
0044 #include "Dialogs/krspecialwidgets.h"
0045 #include "Dialogs/krspwidgets.h"
0046 #include "DiskUsage/diskusagegui.h"
0047 #include "FileSystem/fileitem.h"
0048 #include "FileSystem/filesystem.h"
0049 #include "FileSystem/krquery.h"
0050 #include "GUI/dirhistorybutton.h"
0051 #include "GUI/kcmdline.h"
0052 #include "GUI/kfnkeys.h"
0053 #include "GUI/krusaderstatus.h"
0054 #include "GUI/mediabutton.h"
0055 #include "GUI/terminaldock.h"
0056 #include "KViewer/krviewer.h"
0057 #include "Konfigurator/konfigurator.h"
0058 #include "Locate/locate.h"
0059 #include "MountMan/kmountman.h"
0060 #include "Panel/PanelView/krselectionmode.h"
0061 #include "Panel/PanelView/krview.h"
0062 #include "Panel/PanelView/krviewfactory.h"
0063 #include "Panel/PanelView/krviewitem.h"
0064 #include "Panel/listpanel.h"
0065 #include "Panel/panelfunc.h"
0066 #include "Panel/sidebar.h"
0067 #include "Search/krsearchdialog.h"
0068 #include "Search/krsearchmod.h"
0069 #include "Splitter/combiner.h"
0070 #include "Splitter/splitter.h"
0071 #include "Splitter/splittergui.h"
0072 
0073 #ifdef SYNCHRONIZER_ENABLED
0074 #include "Synchronizer/synchronizergui.h"
0075 #endif
0076 
0077 #define ACTIVE_VIEW _mainWindow->activeView()
0078 
0079 static const QStringList kKioSupportCompareApps = {"kdiff3", "kompare"};
0080 
0081 KrSlots::KrSlots(QObject *parent)
0082     : QObject(parent)
0083     , _mainWindow(krApp)
0084 {
0085 }
0086 
0087 void KrSlots::sendFileByEmail(const QList<QUrl> &urls)
0088 {
0089     if (urls.count() == 0) {
0090         KMessageBox::error(nullptr, i18n("No selected files to send."));
0091         return;
0092     }
0093 
0094     QString mailProg;
0095     QStringList lst = KrServices::supportedTools();
0096     if (lst.contains("MAIL"))
0097         mailProg = lst[lst.indexOf("MAIL") + 1];
0098     else {
0099         KMessageBox::error(nullptr, i18n("Krusader cannot find a supported mail client. Please install one to your path. Hint: Krusader supports KMail."));
0100         return;
0101     }
0102 
0103     QString subject, separator;
0104     foreach (const QUrl &url, urls) {
0105         subject += separator + url.fileName();
0106         separator = ',';
0107     }
0108     subject = i18np("Sending file: %2", "Sending files: %2", urls.count(), subject);
0109 
0110     KProcess proc;
0111 
0112     QString executable = QUrl::fromLocalFile(mailProg).fileName();
0113     if (executable == QStringLiteral("kmail")) {
0114         proc << mailProg << "--subject" << subject;
0115         foreach (const QUrl &url2, urls)
0116             proc << "--attach" << url2.toDisplayString();
0117     } else if (executable == QStringLiteral("thunderbird")) {
0118         QString param = "attachment=\'";
0119         separator = "";
0120         foreach (const QUrl &url2, urls) {
0121             param += separator + url2.toDisplayString();
0122             separator = ',';
0123         }
0124         param += "\',subject=\'" + subject + "\'";
0125         proc << mailProg << "--compose" << param;
0126     } else if (executable == QStringLiteral("evolution")) {
0127         QString param = "mailto:?cc=&subject=" + subject + "&attach=";
0128         separator = "";
0129         foreach (const QUrl &url2, urls) {
0130             param += separator + url2.toDisplayString();
0131             separator = "&attach=";
0132         }
0133         proc << mailProg << param + "";
0134     }
0135 
0136     if (!proc.startDetached())
0137         KMessageBox::error(nullptr, i18n("Error executing %1.", mailProg));
0138 }
0139 
0140 void KrSlots::compareContent()
0141 {
0142     const QStringList lstLeft = LEFT_PANEL->getSelectedNames();
0143     const QStringList lstRight = RIGHT_PANEL->getSelectedNames();
0144     const QStringList lstActive = ACTIVE_PANEL->gui->isLeft() ? lstLeft : lstRight;
0145     QUrl name1, name2;
0146 
0147     if (lstLeft.count() == 1 && lstRight.count() == 1) {
0148         // first, see if we've got exactly 1 selected file in each panel:
0149         name1 = LEFT_PANEL->func->files()->getUrl(lstLeft[0]);
0150         name2 = RIGHT_PANEL->func->files()->getUrl(lstRight[0]);
0151     } else if (lstActive.count() == 2) {
0152         // next try: are in the current panel exactly 2 files selected?
0153         name1 = ACTIVE_PANEL->func->files()->getUrl(lstActive[0]);
0154         name2 = ACTIVE_PANEL->func->files()->getUrl(lstActive[1]);
0155     } else if (ACTIVE_PANEL->otherPanel()->func->files()->getFileItem(ACTIVE_VIEW->getCurrentItem())) {
0156         // next try: is in the other panel a file with the same name?
0157         name1 = ACTIVE_PANEL->func->files()->getUrl(ACTIVE_VIEW->getCurrentItem());
0158         name2 = ACTIVE_PANEL->otherPanel()->func->files()->getUrl(ACTIVE_VIEW->getCurrentItem());
0159     } else {
0160         // if we got here, then we can't be sure what file to diff
0161         KMessageBox::error(nullptr,
0162                            "<qt>" + i18n("Do not know which files to compare.") + "<br/><br/>"
0163                                + i18n("To compare two files by content, you can either:<ul><li>Select one file in the left panel, and one in the right "
0164                                       "panel.</li><li>Select exactly two files in the active panel.</li><li>Make sure there is a file in the other panel, with "
0165                                       "the same name as the current file in the active panel.</li></ul>")
0166                                + "</qt>");
0167 
0168         return;
0169     }
0170 
0171     // else implied: all ok, let's call an external program to compare files
0172     // but if any of the files isn't local, download it first
0173     compareContent(name1, name2);
0174 }
0175 
0176 bool downloadToTemp(const QUrl &url, QString &dest)
0177 {
0178     QTemporaryFile tmpFile;
0179     tmpFile.setAutoRemove(false);
0180     if (tmpFile.open()) {
0181         dest = tmpFile.fileName();
0182         KIO::Job *job = KIO::file_copy(url, QUrl::fromLocalFile(dest), -1, KIO::Overwrite | KIO::HideProgressInfo);
0183         if (!job->exec()) {
0184             KMessageBox::error(krApp, i18n("Krusader is unable to download %1", url.fileName()));
0185             return false;
0186         }
0187         return true;
0188     }
0189     return false;
0190 }
0191 
0192 void KrSlots::compareContent(const QUrl &url1, const QUrl &url2)
0193 {
0194     QString diffProg;
0195     QStringList lst = KrServices::supportedTools();
0196     if (lst.contains("DIFF"))
0197         diffProg = lst[lst.indexOf("DIFF") + 1];
0198     else {
0199         KMessageBox::error(nullptr,
0200                            i18n("Krusader cannot find any of the supported diff-frontends. Please install one to your path. Hint: Krusader supports Kompare, "
0201                                 "KDiff3 and Xxdiff."));
0202         return;
0203     }
0204 
0205     QString tmp1;
0206     QString tmp2;
0207     if (kKioSupportCompareApps.contains(QUrl::fromLocalFile(diffProg).fileName())) {
0208         tmp1 = url1.toDisplayString();
0209         tmp2 = url2.toDisplayString();
0210     } else {
0211         if (!url1.isLocalFile()) {
0212             if (!downloadToTemp(url1, tmp1)) {
0213                 return;
0214             }
0215         } else
0216             tmp1 = url1.path();
0217         if (!url2.isLocalFile()) {
0218             if (!downloadToTemp(url2, tmp2)) {
0219                 if (tmp1 != url1.path()) {
0220                     QFile::remove(tmp1);
0221                 }
0222                 return;
0223             }
0224         } else
0225             tmp2 = url2.path();
0226     }
0227 
0228     KrProcess *p = new KrProcess(tmp1 != url1.path() ? tmp1 : QString(), tmp2 != url2.path() ? tmp2 : QString());
0229     *p << diffProg << tmp1 << tmp2;
0230     p->start();
0231     if (!p->waitForStarted()) {
0232         KMessageBox::error(nullptr, i18n("Error executing %1.", diffProg));
0233     }
0234 }
0235 
0236 // GUI toggle slots
0237 void KrSlots::toggleFnkeys()
0238 {
0239     if (MAIN_VIEW->fnKeys()->isVisible())
0240         MAIN_VIEW->fnKeys()->hide();
0241     else
0242         MAIN_VIEW->fnKeys()->show();
0243 }
0244 
0245 void KrSlots::toggleCmdline()
0246 {
0247     if (MAIN_VIEW->cmdLine()->isVisible())
0248         MAIN_VIEW->cmdLine()->hide();
0249     else
0250         MAIN_VIEW->cmdLine()->show();
0251 }
0252 
0253 void KrSlots::updateStatusbarVisibility()
0254 {
0255     krApp->statusBar()->setVisible(KrActions::actShowStatusBar->isChecked());
0256 }
0257 
0258 void KrSlots::toggleTerminal()
0259 {
0260     MAIN_VIEW->setTerminalEmulator(KrActions::actToggleTerminal->isChecked());
0261 }
0262 
0263 void KrSlots::insertFileName(bool fullPath)
0264 {
0265     QString filename = ACTIVE_VIEW->getCurrentItem();
0266     if (filename.isEmpty()) {
0267         return;
0268     }
0269 
0270     if (fullPath) {
0271         const QString path = FileSystem::ensureTrailingSlash(ACTIVE_PANEL->virtualPath()).toDisplayString(QUrl::PreferLocalFile);
0272         filename = path + filename;
0273     }
0274 
0275     filename = KrServices::quote(filename);
0276 
0277     if (MAIN_VIEW->cmdLine()->isVisible() || !MAIN_VIEW->terminalDock()->isTerminalVisible()) {
0278         QString current = MAIN_VIEW->cmdLine()->text();
0279         if (current.length() != 0 && !current.endsWith(' '))
0280             current += ' ';
0281         MAIN_VIEW->cmdLine()->setText(current + filename);
0282         MAIN_VIEW->cmdLine()->setFocus();
0283     } else if (MAIN_VIEW->terminalDock()->isTerminalVisible()) {
0284         filename = ' ' + filename + ' ';
0285         MAIN_VIEW->terminalDock()->sendInput(filename, false);
0286         MAIN_VIEW->terminalDock()->setFocus();
0287     }
0288 }
0289 
0290 void KrSlots::refresh(const QUrl &u)
0291 {
0292     ACTIVE_FUNC->openUrl(u);
0293 }
0294 
0295 void KrSlots::runKonfigurator(bool firstTime)
0296 {
0297     auto *konfigurator = new Konfigurator(firstTime);
0298     connect(konfigurator, &Konfigurator::configChanged, this, &KrSlots::configChanged);
0299 
0300     // FIXME - no need to exec
0301     konfigurator->exec();
0302 
0303     delete konfigurator;
0304 }
0305 
0306 void KrSlots::configChanged(bool isGUIRestartNeeded)
0307 {
0308     krConfig->sync();
0309 
0310     if (isGUIRestartNeeded) {
0311         krApp->setUpdatesEnabled(false);
0312         KConfigGroup group(krConfig, "Look&Feel");
0313         FileItem::loadUserDefinedFolderIcons(group.readEntry("Load User Defined Folder Icons", _UserDefinedFolderIcons));
0314 
0315         bool leftActive = ACTIVE_PANEL->gui->isLeft();
0316         MAIN_VIEW->leftManager()->slotRecreatePanels();
0317         MAIN_VIEW->rightManager()->slotRecreatePanels();
0318         if (leftActive)
0319             LEFT_PANEL->slotFocusOnMe();
0320         else
0321             RIGHT_PANEL->slotFocusOnMe();
0322         MAIN_VIEW->fnKeys()->updateShortcuts();
0323         KrSelectionMode::resetSelectionHandler();
0324         krApp->setUpdatesEnabled(true);
0325     }
0326 
0327     krApp->setTray();
0328 
0329     // really ugly, but reload the Fn keys just in case - csaba: any better idea?
0330     MAIN_VIEW->fnKeys()->updateShortcuts();
0331 
0332     const bool showHidden = KConfigGroup(krConfig, "Look&Feel").readEntry("Show Hidden", KrActions::actToggleHidden->isChecked());
0333 
0334     if (showHidden != KrActions::actToggleHidden->isChecked()) {
0335         KrActions::actToggleHidden->setChecked(showHidden);
0336         MAIN_VIEW->leftManager()->reloadConfig();
0337         MAIN_VIEW->rightManager()->reloadConfig();
0338     }
0339 }
0340 
0341 void KrSlots::showHiddenFiles(bool show)
0342 {
0343     KConfigGroup group(krConfig, "Look&Feel");
0344     group.writeEntry("Show Hidden", show);
0345 
0346     MAIN_VIEW->leftManager()->reloadConfig();
0347     MAIN_VIEW->rightManager()->reloadConfig();
0348 }
0349 
0350 void KrSlots::swapPanels()
0351 {
0352     QUrl leftURL = LEFT_PANEL->virtualPath();
0353     QUrl rightURL = RIGHT_PANEL->virtualPath();
0354 
0355     LEFT_PANEL->func->openUrl(rightURL);
0356     RIGHT_PANEL->func->openUrl(leftURL);
0357 }
0358 
0359 void KrSlots::toggleSwapSides()
0360 {
0361     MAIN_VIEW->swapSides();
0362 }
0363 
0364 void KrSlots::search()
0365 {
0366     if (KrSearchDialog::SearchDialog != nullptr) {
0367         KConfigGroup group(krConfig, "Search");
0368         if (group.readEntry("Window Maximized", false))
0369             KrSearchDialog::SearchDialog->showMaximized();
0370         else
0371             KrSearchDialog::SearchDialog->showNormal();
0372 
0373         KrSearchDialog::SearchDialog->raise();
0374         KrSearchDialog::SearchDialog->activateWindow();
0375     } else
0376         KrSearchDialog::SearchDialog = new KrSearchDialog();
0377 }
0378 
0379 void KrSlots::locate()
0380 {
0381     if (!KrServices::cmdExist("locate")) {
0382         KMessageBox::error(krApp,
0383                            i18n("Cannot find the 'locate' command. Please install the "
0384                                 "findutils-locate package of GNU, or set its dependencies in "
0385                                 "Konfigurator"));
0386         return;
0387     }
0388 
0389     if (LocateDlg::LocateDialog != nullptr) {
0390         LocateDlg::LocateDialog->showNormal();
0391         LocateDlg::LocateDialog->raise();
0392         LocateDlg::LocateDialog->activateWindow();
0393         LocateDlg::LocateDialog->reset();
0394     } else
0395         LocateDlg::LocateDialog = new LocateDlg(krApp);
0396 }
0397 
0398 void KrSlots::runTerminal(const QString &dir)
0399 {
0400     KProcess proc;
0401     proc.setWorkingDirectory(dir);
0402     KConfigGroup group(krConfig, "General");
0403     QString term = group.readEntry("Terminal", _Terminal);
0404     QStringList sepdArgs = KShell::splitArgs(term, KShell::TildeExpand);
0405     if (sepdArgs.isEmpty()) {
0406         KMessageBox::error(krMainWindow, i18nc("Arg is a string containing the bad quoting.", "Bad quoting in terminal command:\n%1", term));
0407         return;
0408     }
0409     for (int i = 0; i < sepdArgs.size(); i++) {
0410         if (sepdArgs[i] == "%d") {
0411             sepdArgs[i] = dir;
0412         }
0413     }
0414     proc << sepdArgs;
0415     if (!proc.startDetached())
0416         KMessageBox::error(krApp, i18n("Error executing %1.", term));
0417 }
0418 
0419 void KrSlots::homeTerminal()
0420 {
0421     runTerminal(QDir::homePath());
0422 }
0423 
0424 void KrSlots::multiRename()
0425 {
0426     QStringList lst = KrServices::supportedTools();
0427     int i = lst.indexOf("RENAME");
0428     if (i == -1) {
0429         KMessageBox::error(
0430             krApp,
0431             i18n("Cannot find a batch rename tool.\nYou can get KRename at %1", QLatin1String("https://www.kde.org/applications/utilities/krename/")));
0432         return;
0433     }
0434     QString pathToRename = lst[i + 1];
0435 
0436     const QStringList names = ACTIVE_PANEL->gui->getSelectedNames();
0437     if (names.isEmpty()) {
0438         return;
0439     }
0440 
0441     KProcess proc;
0442     proc << pathToRename;
0443 
0444     for (const QString &name : names) {
0445         FileItem *file = ACTIVE_FUNC->files()->getFileItem(name);
0446         if (!file)
0447             continue;
0448         const QUrl url = file->getUrl();
0449         // KRename only supports the recursive option combined with a local directory path
0450         if (file->isDir() && url.scheme() == "file") {
0451             proc << "-r" << url.path();
0452         } else {
0453             proc << url.toString();
0454         }
0455     }
0456 
0457     if (!proc.startDetached())
0458         KMessageBox::error(nullptr, i18n("Error executing '%1'.", proc.program().join(" ")));
0459 }
0460 
0461 void KrSlots::rootKrusader()
0462 {
0463     if (KMessageBox::warningContinueCancel(krApp,
0464                                            i18n("Improper operations in root mode can damage your operating system. "
0465                                                 "<p>Furthermore, running UI applications as root is insecure and can "
0466                                                 "allow attackers to gain root access."),
0467                                            QString(),
0468                                            KStandardGuiItem::cont(),
0469                                            KStandardGuiItem::cancel(),
0470                                            "Confirm Root Mode",
0471                                            KMessageBox::Notify | KMessageBox::Dangerous)
0472         != KMessageBox::Continue)
0473         return;
0474 
0475     if (!KrServices::isExecutable(KDESU_PATH)) {
0476         KMessageBox::error(krApp,
0477                            i18n("Cannot start root mode Krusader, %1 not found or not executable. "
0478                                 "Please verify that kde-cli-tools are installed.",
0479                                 QString(KDESU_PATH)));
0480         return;
0481     }
0482 
0483     KProcess proc;
0484     proc << KDESU_PATH << "-c"
0485          << QApplication::instance()->applicationFilePath() + " --left=" + KrServices::quote(LEFT_PANEL->virtualPath().toDisplayString(QUrl::PreferLocalFile))
0486             + " --right=" + KrServices::quote(RIGHT_PANEL->virtualPath().toDisplayString(QUrl::PreferLocalFile));
0487 
0488     if (!proc.startDetached())
0489         KMessageBox::error(nullptr, i18n("Error executing %1.", proc.program()[0]));
0490 }
0491 
0492 void KrSlots::slotSplit()
0493 {
0494     const QStringList list = ACTIVE_PANEL->gui->getSelectedNames();
0495     QString name;
0496 
0497     // first, see if we've got exactly 1 selected file, if not, try the current one
0498     if (list.count() == 1)
0499         name = list[0];
0500 
0501     if (name.isEmpty()) {
0502         // if we got here, then one of the panel can't be sure what file to diff
0503         KMessageBox::error(nullptr, i18n("Do not know which file to split."));
0504         return;
0505     }
0506 
0507     QUrl fileURL = ACTIVE_FUNC->files()->getUrl(name);
0508     if (fileURL.isEmpty())
0509         return;
0510 
0511     if (ACTIVE_FUNC->files()->getFileItem(name)->isDir()) {
0512         KMessageBox::error(krApp, i18n("You cannot split a folder."));
0513         return;
0514     }
0515 
0516     const QUrl destDir = ACTIVE_PANEL->otherPanel()->virtualPath();
0517 
0518     SplitterGUI splitterGUI(MAIN_VIEW, fileURL, destDir);
0519 
0520     if (splitterGUI.exec() == QDialog::Accepted) {
0521         bool splitToOtherPanel = splitterGUI.getDestinationDir().matches(ACTIVE_PANEL->otherPanel()->virtualPath(), QUrl::StripTrailingSlash);
0522 
0523         Splitter split(MAIN_VIEW, fileURL, splitterGUI.getDestinationDir(), splitterGUI.overWriteFiles());
0524         split.split(splitterGUI.getSplitSize());
0525 
0526         if (splitToOtherPanel)
0527             ACTIVE_PANEL->otherPanel()->func->refresh();
0528     }
0529 }
0530 
0531 void KrSlots::slotCombine()
0532 {
0533     const QStringList list = ACTIVE_PANEL->gui->getSelectedNames();
0534     if (list.isEmpty()) {
0535         KMessageBox::error(nullptr, i18n("Do not know which files to combine."));
0536         return;
0537     }
0538 
0539     QUrl baseURL;
0540     bool unixStyle = false;
0541     bool windowsStyle = false;
0542     QString commonName;
0543     int commonLength = 0;
0544 
0545     /* checking splitter names */
0546     for (const auto &it : list) {
0547         QUrl url = ACTIVE_FUNC->files()->getUrl(it);
0548         if (url.isEmpty())
0549             return;
0550 
0551         if (ACTIVE_FUNC->files()->getFileItem(it)->isDir()) {
0552             KMessageBox::error(krApp, i18n("You cannot combine a folder."));
0553             return;
0554         }
0555 
0556         if (!unixStyle) {
0557             QString name = url.fileName();
0558             int extPos = name.lastIndexOf('.');
0559             QString ext = name.mid(extPos + 1);
0560             name.truncate(extPos);
0561             url = url.adjusted(QUrl::RemoveFilename);
0562             url.setPath(url.path() + name);
0563 
0564             bool isExtInt;
0565             ext.toInt(&isExtInt, 10);
0566 
0567             if (extPos < 1 || ext.isEmpty() || (ext != "crc" && !isExtInt)) {
0568                 if (windowsStyle) {
0569                     KMessageBox::error(nullptr, i18n("Not a split file: %1.", url.toDisplayString(QUrl::PreferLocalFile)));
0570                     return;
0571                 }
0572                 unixStyle = true;
0573             } else {
0574                 if (ext != "crc")
0575                     windowsStyle = true;
0576 
0577                 if (baseURL.isEmpty())
0578                     baseURL = url;
0579                 else if (baseURL != url) {
0580                     KMessageBox::error(nullptr, i18n("Select only one split file."));
0581                     return;
0582                 }
0583             }
0584         }
0585 
0586         if (unixStyle) {
0587             bool error = true;
0588 
0589             do {
0590                 const QString &shortName = it;
0591                 QChar lastChar = shortName.at(shortName.length() - 1);
0592 
0593                 if (lastChar.isLetter()) {
0594                     char fillLetter = (lastChar.toUpper() == lastChar) ? 'A' : 'a';
0595 
0596                     if (commonName.isNull()) {
0597                         commonLength = shortName.length();
0598                         commonName = shortName;
0599 
0600                         while (commonName.length()) {
0601                             QString shorter = commonName.left(commonName.length() - 1);
0602                             QString testFile = shorter.leftJustified(commonLength, fillLetter);
0603 
0604                             if (ACTIVE_FUNC->files()->getFileItem(testFile) == nullptr)
0605                                 break;
0606                             else {
0607                                 commonName = shorter;
0608                                 baseURL = ACTIVE_PANEL->virtualPath().adjusted(QUrl::StripTrailingSlash);
0609                                 baseURL.setPath(baseURL.path() + '/' + (testFile));
0610                             }
0611                         }
0612 
0613                         error = (commonName == shortName);
0614                     } else if (commonLength == shortName.length() && shortName.startsWith(commonName))
0615                         error = false;
0616                 }
0617             } while (false);
0618 
0619             if (error) {
0620                 KMessageBox::error(nullptr, i18n("Not a split file: %1.", url.toDisplayString(QUrl::PreferLocalFile)));
0621                 return;
0622             }
0623         }
0624     }
0625 
0626     // ask the user for the copy dest
0627     QUrl dest = KChooseDir::getDir(i18n("Combining %1.* to folder:", baseURL.toDisplayString(QUrl::PreferLocalFile)),
0628                                    ACTIVE_PANEL->otherPanel()->virtualPath(),
0629                                    ACTIVE_PANEL->virtualPath());
0630     if (dest.isEmpty())
0631         return; // the user canceled
0632 
0633     bool combineToOtherPanel = (dest.matches(ACTIVE_PANEL->otherPanel()->virtualPath(), QUrl::StripTrailingSlash));
0634 
0635     Combiner combine(MAIN_VIEW, baseURL, dest, unixStyle);
0636     combine.combine();
0637 
0638     if (combineToOtherPanel)
0639         ACTIVE_PANEL->otherPanel()->func->refresh();
0640 }
0641 
0642 void KrSlots::manageUseractions()
0643 {
0644     ActionMan actionMan(MAIN_VIEW);
0645 }
0646 
0647 #ifdef SYNCHRONIZER_ENABLED
0648 void KrSlots::slotSynchronizeDirs(QStringList selected)
0649 {
0650     SynchronizerGUI *synchronizerDialog = new SynchronizerGUI(MAIN_VIEW, LEFT_PANEL->virtualPath(), RIGHT_PANEL->virtualPath(), std::move(selected));
0651     synchronizerDialog->show(); // destroyed on close
0652 }
0653 #endif
0654 
0655 void KrSlots::compareSetup()
0656 {
0657     for (int i = 0; KrActions::compareArray[i] != nullptr; i++)
0658         if ((*KrActions::compareArray[i])->isChecked()) {
0659             KConfigGroup group(krConfig, "Private");
0660             group.writeEntry("Compare Mode", i);
0661             break;
0662         }
0663 }
0664 
0665 /** called by actions actExec* to choose the built-in command line mode  */
0666 void KrSlots::execTypeSetup()
0667 {
0668     for (int i = 0; KrActions::execTypeArray[i] != nullptr; i++)
0669         if ((*KrActions::execTypeArray[i])->isChecked()) {
0670             if (*KrActions::execTypeArray[i] == KrActions::actExecTerminalEmbedded) {
0671                 // if commands are to be executed in the TE, it must be loaded
0672                 MAIN_VIEW->terminalDock()->initialise();
0673             }
0674             KConfigGroup grp(krConfig, "Private");
0675             grp.writeEntry("Command Execution Mode", i);
0676             break;
0677         }
0678 }
0679 
0680 void KrSlots::slotDiskUsage()
0681 {
0682     DiskUsageGUI *diskUsageDialog = new DiskUsageGUI(ACTIVE_PANEL->virtualPath());
0683     diskUsageDialog->askDirAndShow();
0684 }
0685 
0686 void KrSlots::applicationStateChanged()
0687 {
0688     if (MAIN_VIEW == nullptr) { /* CRASH FIX: it's possible that the method is called after destroying the main view */
0689         return;
0690     }
0691     if (qApp->applicationState() == Qt::ApplicationActive || qApp->applicationState() == Qt::ApplicationInactive) {
0692         LEFT_PANEL->panelVisible();
0693         RIGHT_PANEL->panelVisible();
0694     } else {
0695         LEFT_PANEL->panelHidden();
0696         RIGHT_PANEL->panelHidden();
0697     }
0698 }
0699 
0700 void KrSlots::emptyTrash()
0701 {
0702     KrTrashHandler::emptyTrash();
0703 }
0704 
0705 #define OPEN_ID 100001
0706 #define EMPTY_TRASH_ID 100002
0707 
0708 void KrSlots::trashPopupMenu()
0709 {
0710     QMenu trashMenu(krApp);
0711     QAction *act = trashMenu.addAction(Icon("document-open"), i18n("Open trash bin"));
0712     act->setData(QVariant(OPEN_ID));
0713     act = trashMenu.addAction(Icon("trash-empty"), i18n("Empty trash bin"));
0714     act->setData(QVariant(EMPTY_TRASH_ID));
0715 
0716     int result = -1;
0717     QAction *res = trashMenu.exec(QCursor::pos());
0718     if (res && res->data().canConvert<int>())
0719         result = res->data().toInt();
0720 
0721     if (result == OPEN_ID) {
0722         ACTIVE_FUNC->openUrl(QUrl(QStringLiteral("trash:/")));
0723     } else if (result == EMPTY_TRASH_ID) {
0724         KrTrashHandler::emptyTrash();
0725     }
0726 }
0727 
0728 void KrSlots::addBookmark()
0729 {
0730     krBookMan->bookmarkCurrent(ACTIVE_PANEL->virtualPath());
0731 }
0732 
0733 void KrSlots::cmdlinePopup()
0734 {
0735     MAIN_VIEW->cmdLine()->popup();
0736 }