• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.3 API Reference
  • KDE Home
  • Contact Us
 

KFile

knewfilemenu.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 1998-2009 David Faure <faure@kde.org>
00003                  2003      Sven Leiber <s.leiber@web.de>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 or at your option version 3.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "knewfilemenu.h"
00022 #include "knameandurlinputdialog.h"
00023 
00024 #include <QDir>
00025 #include <QVBoxLayout>
00026 #include <QList>
00027 #include <QLabel>
00028 #include <kactioncollection.h>
00029 #include <kdebug.h>
00030 #include <kdesktopfile.h>
00031 #include <kdirwatch.h>
00032 #include <kicon.h>
00033 #include <kcomponentdata.h>
00034 #include <kinputdialog.h>
00035 #include <kdialog.h>
00036 #include <klocale.h>
00037 #include <klineedit.h>
00038 #include <kmessagebox.h>
00039 #include <kstandarddirs.h>
00040 #include <kprotocolinfo.h>
00041 #include <kprotocolmanager.h>
00042 #include <kmenu.h>
00043 #include <krun.h>
00044 #include <kshell.h>
00045 #include <kio/job.h>
00046 #include <kio/copyjob.h>
00047 #include <kio/jobuidelegate.h>
00048 #include <kio/renamedialog.h>
00049 #include <kio/netaccess.h>
00050 #include <kio/fileundomanager.h>
00051 #include <kio/kurifilter.h>
00052 
00053 #include <kpropertiesdialog.h>
00054 #include <ktemporaryfile.h>
00055 #include <utime.h>
00056 
00057 static QString expandTilde(const QString& name, bool isfile = false)
00058 {
00059     if (!name.isEmpty() && (!isfile || name[0] == '\\'))
00060     {
00061         const QString expandedName = KShell::tildeExpand(name);
00062         // When a tilde mark cannot be properly expanded, the above call
00063         // returns an empty string...
00064         if (!expandedName.isEmpty())
00065             return expandedName;
00066     }
00067 
00068     return name;
00069 }
00070 
00071 // Singleton, with data shared by all KNewFileMenu instances
00072 class KNewFileMenuSingleton
00073 {
00074 public:
00075     KNewFileMenuSingleton()
00076         : dirWatch(0),
00077       filesParsed(false),
00078       templatesList(0),
00079           templatesVersion(0)
00080     {
00081     }
00082 
00083     ~KNewFileMenuSingleton()
00084     {
00085     delete dirWatch;
00086         delete templatesList;
00087     }
00088 
00089 
00094     void parseFiles();
00095 
00103     enum EntryType { Unknown, LinkToTemplate = 1, Template, Separator };
00104 
00105     KDirWatch * dirWatch;
00106 
00107     struct Entry {
00108         QString text;
00109         QString filePath; // empty for Separator
00110         QString templatePath; // same as filePath for Template
00111         QString icon;
00112         EntryType entryType;
00113         QString comment;
00114         QString mimeType;
00115     };
00116     // NOTE: only filePath is known before we call parseFiles
00117 
00122     typedef QList<Entry> EntryList;
00123 
00128     bool filesParsed;
00129     EntryList * templatesList;
00130 
00136     int templatesVersion;
00137 };
00138 
00139 void KNewFileMenuSingleton::parseFiles()
00140 {
00141     //kDebug(1203);
00142     filesParsed = true;
00143     KNewFileMenuSingleton::EntryList::iterator templ = templatesList->begin();
00144     const KNewFileMenuSingleton::EntryList::iterator templ_end = templatesList->end();
00145     for (; templ != templ_end; ++templ)
00146     {
00147         QString iconname;
00148         QString filePath = (*templ).filePath;
00149         if (!filePath.isEmpty())
00150         {
00151             QString text;
00152             QString templatePath;
00153             // If a desktop file, then read the name from it.
00154             // Otherwise (or if no name in it?) use file name
00155             if (KDesktopFile::isDesktopFile(filePath)) {
00156                 KDesktopFile desktopFile( filePath);
00157                 text = desktopFile.readName();
00158                 (*templ).icon = desktopFile.readIcon();
00159                 (*templ).comment = desktopFile.readComment();
00160                 QString type = desktopFile.readType();
00161                 if (type == "Link")
00162                 {
00163                     templatePath = desktopFile.desktopGroup().readPathEntry("URL", QString());
00164                     if (templatePath[0] != '/' && !templatePath.startsWith("__"))
00165                     {
00166                         if (templatePath.startsWith("file:/"))
00167                             templatePath = KUrl(templatePath).toLocalFile();
00168                         else
00169                         {
00170                             // A relative path, then (that's the default in the files we ship)
00171                             QString linkDir = filePath.left(filePath.lastIndexOf('/') + 1 /*keep / */);
00172                             //kDebug(1203) << "linkDir=" << linkDir;
00173                             templatePath = linkDir + templatePath;
00174                         }
00175                     }
00176                 }
00177                 if (templatePath.isEmpty())
00178                 {
00179                     // No URL key, this is an old-style template
00180                     (*templ).entryType = KNewFileMenuSingleton::Template;
00181                     (*templ).templatePath = (*templ).filePath; // we'll copy the file
00182                 } else {
00183                     (*templ).entryType = KNewFileMenuSingleton::LinkToTemplate;
00184                     (*templ).templatePath = templatePath;
00185                 }
00186 
00187             }
00188             if (text.isEmpty())
00189             {
00190                 text = KUrl(filePath).fileName();
00191                 if (text.endsWith(".desktop"))
00192                     text.truncate(text.length() - 8);
00193             }
00194             (*templ).text = text;
00195             /*kDebug(1203) << "Updating entry with text=" << text
00196                           << "entryType=" << (*templ).entryType
00197                           << "templatePath=" << (*templ).templatePath;*/
00198         }
00199         else {
00200             (*templ).entryType = KNewFileMenuSingleton::Separator;
00201         }
00202     }
00203 }
00204 
00205 K_GLOBAL_STATIC(KNewFileMenuSingleton, kNewMenuGlobals)
00206 
00207 class KNewFileMenuCopyData
00208 {
00209 public:
00210     KNewFileMenuCopyData() { m_isSymlink = false;}
00211     ~KNewFileMenuCopyData() {}
00212     QString chosenFileName() const { return m_chosenFileName; }
00213 
00214     // If empty, no copy is performed.
00215     QString sourceFileToCopy() const { return m_src; }
00216     QString tempFileToDelete() const { return m_tempFileToDelete; }
00217     bool m_isSymlink;
00218 
00219     QString m_chosenFileName;
00220     QString m_src;
00221     QString m_tempFileToDelete;
00222     QString m_templatePath;
00223 };
00224 
00225 class KNewFileMenuPrivate
00226 {
00227 public:
00228     KNewFileMenuPrivate(KNewFileMenu* qq)
00229         : m_menuItemsVersion(0),
00230           m_modal(true),
00231           m_viewShowsHiddenFiles(false),
00232           q(qq)
00233     {}
00234 
00235     bool checkSourceExists(const QString& src);
00236 
00240     void confirmCreatingHiddenDir(const QString& name);
00241 
00245     void executeOtherDesktopFile(const KNewFileMenuSingleton::Entry& entry);
00246 
00250     void executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry);
00251 
00255     void executeStrategy();
00256 
00260     void executeSymLink(const KNewFileMenuSingleton::Entry& entry);
00261 
00265     void executeUrlDesktopFile(const KNewFileMenuSingleton::Entry& entry);
00266 
00270     void fillMenu();
00271 
00275     void _k_slotAbortDialog();
00276 
00280     void _k_slotActionTriggered(QAction* action);
00281 
00285     void _k_slotCreateDirectory(bool writeHiddenDir = false);
00286 
00291     void _k_slotCreateHiddenDirectory();
00292 
00296     void _k_slotFillTemplates();
00297 
00302     void _k_slotOtherDesktopFile();
00303 
00308     void _k_slotRealFileOrDir();
00309 
00314     void _k_slotTextChanged(const QString & text);
00315 
00320     void _k_slotSymLink();
00321 
00326     void _k_slotUrlDesktopFile();
00327 
00328 
00329     KActionCollection * m_actionCollection;
00330     KDialog* m_fileDialog;
00331 
00332     KActionMenu *m_menuDev;
00333     int m_menuItemsVersion;
00334     bool m_modal;
00335     QAction* m_newDirAction;
00336 
00340     QActionGroup* m_newMenuGroup;
00341     QWidget *m_parentWidget;
00342 
00347     KUrl::List m_popupFiles;
00348 
00349     QStringList m_supportedMimeTypes;
00350     QString m_tempFileToDelete; // set when a tempfile was created for a Type=URL desktop file
00351     QString m_text;
00352     bool m_viewShowsHiddenFiles;
00353 
00354     KNewFileMenu* q;
00355 
00356     KNewFileMenuCopyData m_copyData;
00357 };
00358 
00359 bool KNewFileMenuPrivate::checkSourceExists(const QString& src)
00360 {
00361     if (!QFile::exists(src)) {
00362         kWarning(1203) << src << "doesn't exist" ;
00363 
00364     KDialog* dialog = new KDialog(m_parentWidget);
00365     dialog->setCaption( i18n("Sorry") );
00366     dialog->setButtons( KDialog::Ok );
00367     dialog->setObjectName( "sorry" );
00368     dialog->setModal(q->isModal());
00369     dialog->setAttribute(Qt::WA_DeleteOnClose);
00370     dialog->setDefaultButton( KDialog::Ok );
00371     dialog->setEscapeButton( KDialog::Ok );
00372 
00373     KMessageBox::createKMessageBox(dialog, QMessageBox::Warning,
00374       i18n("<qt>The template file <b>%1</b> does not exist.</qt>", src),
00375       QStringList(), QString(), 0, KMessageBox::NoExec,
00376       QString());
00377 
00378     dialog->show();
00379 
00380         return false;
00381     }
00382     return true;
00383 }
00384 
00385 void KNewFileMenuPrivate::confirmCreatingHiddenDir(const QString& name)
00386 {
00387     if(!KMessageBox::shouldBeShownContinue("confirm_create_hidden_dir")){
00388     _k_slotCreateHiddenDirectory();
00389     return;
00390     }
00391 
00392     KGuiItem continueGuiItem(KStandardGuiItem::cont());
00393     continueGuiItem.setText(i18nc("@action:button", "Create directory"));
00394     KGuiItem cancelGuiItem(KStandardGuiItem::cancel());
00395     cancelGuiItem.setText(i18nc("@action:button", "Enter a different name"));
00396 
00397     KDialog* confirmDialog = new KDialog(m_parentWidget);
00398     confirmDialog->setCaption(i18n("Create hidden directory?"));
00399     confirmDialog->setModal(m_modal);
00400     confirmDialog->setAttribute(Qt::WA_DeleteOnClose);
00401     KMessageBox::createKMessageBox(confirmDialog, QMessageBox::Warning,
00402       i18n("The name \"%1\" starts with a dot, so the directory will be hidden by default.", name),
00403       QStringList(),
00404       i18n("Do not ask again"),
00405       0,
00406       KMessageBox::NoExec,
00407       QString());
00408     confirmDialog->setButtonGuiItem(KDialog::Ok, continueGuiItem);
00409     confirmDialog->setButtonGuiItem(KDialog::Cancel, cancelGuiItem);
00410 
00411     QObject::connect(confirmDialog, SIGNAL(accepted()), q, SLOT(_k_slotCreateHiddenDirectory()));
00412     QObject::connect(confirmDialog, SIGNAL(rejected()), q, SLOT(createDirectory()));
00413 
00414     m_fileDialog = confirmDialog;
00415     confirmDialog->show();
00416 
00417 }
00418 
00419 void KNewFileMenuPrivate::executeOtherDesktopFile(const KNewFileMenuSingleton::Entry& entry)
00420 {
00421     if (!checkSourceExists(entry.templatePath)) {
00422         return;
00423     }
00424 
00425     KUrl::List::const_iterator it = m_popupFiles.constBegin();
00426     for (; it != m_popupFiles.constEnd(); ++it)
00427     {
00428         QString text = entry.text;
00429         text.remove("..."); // the ... is fine for the menu item but not for the default filename
00430         text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
00431         // KDE5 TODO: remove the "..." from link*.desktop files and use i18n("%1...") when making
00432         // the action.
00433 
00434         KUrl defaultFile(*it);
00435         defaultFile.addPath(KIO::encodeFileName(text));
00436         if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
00437             text = KIO::RenameDialog::suggestName(*it, text);
00438 
00439         const KUrl templateUrl(entry.templatePath);
00440 
00441     KDialog* dlg = new KPropertiesDialog(templateUrl, *it, text, m_parentWidget);
00442     dlg->setModal(q->isModal());
00443     dlg->setAttribute(Qt::WA_DeleteOnClose);
00444         QObject::connect(dlg, SIGNAL(applied()), q, SLOT(_k_slotOtherDesktopFile()));
00445     dlg->show();
00446     }
00447     // We don't set m_src here -> there will be no copy, we are done.
00448 }
00449 
00450 void KNewFileMenuPrivate::executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry)
00451 {
00452     // The template is not a desktop file
00453     // Show the small dialog for getting the destination filename
00454     QString text = entry.text;
00455     text.remove("..."); // the ... is fine for the menu item but not for the default filename
00456     text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
00457     m_copyData.m_src = entry.templatePath;
00458 
00459     KUrl defaultFile(m_popupFiles.first());
00460     defaultFile.addPath(KIO::encodeFileName(text));
00461     if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
00462         text = KIO::RenameDialog::suggestName(m_popupFiles.first(), text);
00463 
00464     KDialog* fileDialog = new KDialog(m_parentWidget);
00465     fileDialog->setAttribute(Qt::WA_DeleteOnClose);
00466     fileDialog->setModal(q->isModal());
00467     fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
00468 
00469     QWidget* mainWidget = new QWidget(fileDialog);
00470     QVBoxLayout *layout = new QVBoxLayout(mainWidget);
00471     QLabel *label = new QLabel(entry.comment);
00472 
00473     // We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
00474     // It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
00475     // TODO: should probably be investigated and fixed in KLineEdit.
00476     KLineEdit *lineEdit = new KLineEdit;
00477     lineEdit->setClearButtonShown(true);
00478     lineEdit->setText(text);
00479 
00480     _k_slotTextChanged(text);
00481     QObject::connect(lineEdit, SIGNAL(textChanged(QString)), q, SLOT(_k_slotTextChanged(QString)));
00482 
00483     layout->addWidget(label);
00484     layout->addWidget(lineEdit);
00485 
00486     fileDialog->setMainWidget(mainWidget);
00487     QObject::connect(fileDialog, SIGNAL(accepted()), q, SLOT(_k_slotRealFileOrDir()));
00488     QObject::connect(fileDialog, SIGNAL(rejected()), q, SLOT(_k_slotAbortDialog()));
00489 
00490     fileDialog->show();
00491     lineEdit->selectAll();
00492     lineEdit->setFocus();
00493 }
00494 
00495 void KNewFileMenuPrivate::executeSymLink(const KNewFileMenuSingleton::Entry& entry)
00496 {
00497     KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);
00498     dlg->setModal(q->isModal());
00499     dlg->setAttribute(Qt::WA_DeleteOnClose);
00500     dlg->setCaption(i18n("Create Symlink"));
00501     m_fileDialog = dlg;
00502     QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotSymLink()));
00503     dlg->show();
00504 }
00505 
00506 void KNewFileMenuPrivate::executeStrategy()
00507 {
00508     m_tempFileToDelete = m_copyData.tempFileToDelete();
00509     const QString src = m_copyData.sourceFileToCopy();
00510     QString chosenFileName = expandTilde(m_copyData.chosenFileName(), true);
00511 
00512     if (src.isEmpty())
00513         return;
00514     KUrl uSrc(src);
00515     if (uSrc.isLocalFile()) {
00516         // In case the templates/.source directory contains symlinks, resolve
00517         // them to the target files. Fixes bug #149628.
00518         KFileItem item(uSrc, QString(), KFileItem::Unknown);
00519         if (item.isLink())
00520             uSrc.setPath(item.linkDest());
00521 
00522         if (!m_copyData.m_isSymlink) {
00523             // If the file is not going to be detected as a desktop file, due to a
00524             // known extension (e.g. ".pl"), append ".desktop". #224142.
00525             QFile srcFile(uSrc.toLocalFile());
00526             if (srcFile.open(QIODevice::ReadOnly)) {
00527                 KMimeType::Ptr wantedMime = KMimeType::findByUrl(uSrc);
00528                 KMimeType::Ptr mime = KMimeType::findByNameAndContent(m_copyData.m_chosenFileName, srcFile.read(1024));
00529                 //kDebug() << "mime=" << mime->name() << "wantedMime=" << wantedMime->name();
00530                 if (!mime->is(wantedMime->name()))
00531                     chosenFileName += wantedMime->mainExtension();
00532             }
00533         }
00534     }
00535 
00536     // The template is not a desktop file [or it's a URL one]
00537     // Copy it.
00538     KUrl::List::const_iterator it = m_popupFiles.constBegin();
00539     for (; it != m_popupFiles.constEnd(); ++it)
00540     {
00541         KUrl dest(*it);
00542         dest.addPath(KIO::encodeFileName(chosenFileName));
00543 
00544         KUrl::List lstSrc;
00545         lstSrc.append(uSrc);
00546         KIO::Job* kjob;
00547         if (m_copyData.m_isSymlink) {
00548             kjob = KIO::symlink(src, dest);
00549             // This doesn't work, FileUndoManager registers new links in copyingLinkDone,
00550             // which KIO::symlink obviously doesn't emit... Needs code in FileUndoManager.
00551             //KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Link, lstSrc, dest, kjob);
00552         } else {
00553             //kDebug(1203) << "KIO::copyAs(" << uSrc.url() << "," << dest.url() << ")";
00554             KIO::CopyJob * job = KIO::copyAs(uSrc, dest);
00555             job->setDefaultPermissions(true);
00556             kjob = job;
00557             KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Copy, lstSrc, dest, job);
00558         }
00559         kjob->ui()->setWindow(m_parentWidget);
00560         QObject::connect(kjob, SIGNAL(result(KJob*)), q, SLOT(slotResult(KJob*)));
00561     }
00562 }
00563 
00564 void KNewFileMenuPrivate::executeUrlDesktopFile(const KNewFileMenuSingleton::Entry& entry)
00565 {
00566     KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);
00567     m_copyData.m_templatePath = entry.templatePath;
00568     dlg->setModal(q->isModal());
00569     dlg->setAttribute(Qt::WA_DeleteOnClose);
00570     dlg->setCaption(i18n("Create link to URL"));
00571     m_fileDialog = dlg;
00572     QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotUrlDesktopFile()));
00573     dlg->show();
00574 }
00575 
00576 void KNewFileMenuPrivate::fillMenu()
00577 {
00578     QMenu* menu = q->menu();
00579     menu->clear();
00580     m_menuDev->menu()->clear();
00581     m_newDirAction = 0;
00582 
00583     QSet<QString> seenTexts;
00584     // these shall be put at special positions
00585     QAction* linkURL = 0;
00586     QAction* linkApp = 0;
00587     QAction* linkPath = 0;
00588 
00589     KNewFileMenuSingleton* s = kNewMenuGlobals;
00590     int i = 1;
00591     KNewFileMenuSingleton::EntryList::iterator templ = s->templatesList->begin();
00592     const KNewFileMenuSingleton::EntryList::iterator templ_end = s->templatesList->end();
00593     for (; templ != templ_end; ++templ, ++i)
00594     {
00595         KNewFileMenuSingleton::Entry& entry = *templ;
00596         if (entry.entryType != KNewFileMenuSingleton::Separator) {
00597             // There might be a .desktop for that one already, if it's a kdelnk
00598             // This assumes we read .desktop files before .kdelnk files ...
00599 
00600             // In fact, we skip any second item that has the same text as another one.
00601             // Duplicates in a menu look bad in any case.
00602 
00603             const bool bSkip = seenTexts.contains(entry.text);
00604             if (bSkip) {
00605                 kDebug(1203) << "skipping" << entry.filePath;
00606             } else {
00607                 seenTexts.insert(entry.text);
00608                 //const KNewFileMenuSingleton::Entry entry = templatesList->at(i-1);
00609 
00610                 const QString templatePath = entry.templatePath;
00611                 // The best way to identify the "Create Directory", "Link to Location", "Link to Application" was the template
00612                 if (templatePath.endsWith("emptydir")) {
00613                     QAction * act = new QAction(q);
00614                     m_newDirAction = act;
00615                     act->setIcon(KIcon(entry.icon));
00616                     act->setText(i18nc("@item:inmenu Create New", "%1", entry.text));
00617                     act->setActionGroup(m_newMenuGroup);
00618                     menu->addAction(act);
00619 
00620                     QAction *sep = new QAction(q);
00621                     sep->setSeparator(true);
00622                     menu->addAction(sep);
00623                 } else {
00624 
00625                     if (!m_supportedMimeTypes.isEmpty()) {
00626                         bool keep = false;
00627 
00628                         // We need to do mimetype filtering, for real files.
00629                         const bool createSymlink = entry.templatePath == "__CREATE_SYMLINK__";
00630                         if (createSymlink) {
00631                             keep = true;
00632                         } else if (!KDesktopFile::isDesktopFile(entry.templatePath)) {
00633 
00634                             // Determine mimetype on demand
00635                             KMimeType::Ptr mime;
00636                             if (entry.mimeType.isEmpty()) {
00637                                 mime = KMimeType::findByPath(entry.templatePath);
00638                                 if (mime) {
00639                                     //kDebug() << entry.templatePath << "is" << mime->name();
00640                                     entry.mimeType = mime->name();
00641                                 } else {
00642                                     entry.mimeType = KMimeType::defaultMimeType();
00643                                 }
00644                             } else {
00645                                 mime = KMimeType::mimeType(entry.mimeType);
00646                             }
00647                             Q_FOREACH(const QString& supportedMime, m_supportedMimeTypes) {
00648                                 if (mime && mime->is(supportedMime)) {
00649                                     keep = true;
00650                                     break;
00651                                 }
00652                             }
00653                         }
00654 
00655                         if (!keep) {
00656                             //kDebug() << "Not keeping" << entry.templatePath;
00657                             continue;
00658                         }
00659                     }
00660 
00661                     QAction * act = new QAction(q);
00662                     act->setData(i);
00663                     act->setIcon(KIcon(entry.icon));
00664                     act->setText(i18nc("@item:inmenu Create New", "%1", entry.text));
00665                     act->setActionGroup(m_newMenuGroup);
00666 
00667                     //kDebug() << templatePath << entry.filePath;
00668 
00669                     if (templatePath.endsWith("/URL.desktop")) {
00670                         linkURL = act;
00671                     } else if (templatePath.endsWith("/Program.desktop")) {
00672                         linkApp = act;
00673                     } else if (entry.filePath.endsWith("/linkPath.desktop")) {
00674                         linkPath = act;
00675                     } else if (KDesktopFile::isDesktopFile(templatePath)) {
00676                         KDesktopFile df(templatePath);
00677                         if (df.readType() == "FSDevice")
00678                             m_menuDev->menu()->addAction(act);
00679                         else
00680                             menu->addAction(act);
00681                     }
00682                     else
00683                     {
00684                         menu->addAction(act);
00685                     }
00686                 }
00687             }
00688         } else { // Separate system from personal templates
00689             Q_ASSERT(entry.entryType != 0);
00690 
00691             QAction *sep = new QAction(q);
00692             sep->setSeparator(true);
00693             menu->addAction(sep);
00694         }
00695     }
00696 
00697     if (m_supportedMimeTypes.isEmpty()) {
00698         QAction *sep = new QAction(q);
00699         sep->setSeparator(true);
00700         menu->addAction(sep);
00701         if (linkURL) menu->addAction(linkURL);
00702         if (linkPath) menu->addAction(linkPath);
00703         if (linkApp) menu->addAction(linkApp);
00704         Q_ASSERT(m_menuDev);
00705         menu->addAction(m_menuDev);
00706     }
00707 }
00708 
00709 void KNewFileMenuPrivate::_k_slotAbortDialog()
00710 {
00711     m_text = QString();
00712 }
00713 
00714 void KNewFileMenuPrivate::_k_slotActionTriggered(QAction* action)
00715 {
00716     q->trigger(); // was for kdesktop's slotNewMenuActivated() in kde3 times. Can't hurt to keep it...
00717 
00718     if (action == m_newDirAction) {
00719         q->createDirectory();
00720         return;
00721     }
00722     const int id = action->data().toInt();
00723     Q_ASSERT(id > 0);
00724 
00725     KNewFileMenuSingleton* s = kNewMenuGlobals;
00726     const KNewFileMenuSingleton::Entry entry = s->templatesList->at(id - 1);
00727 
00728     const bool createSymlink = entry.templatePath == "__CREATE_SYMLINK__";
00729 
00730     m_copyData = KNewFileMenuCopyData();
00731 
00732     if (createSymlink) {
00733         m_copyData.m_isSymlink = true;
00734     executeSymLink(entry);
00735     }
00736     else if (KDesktopFile::isDesktopFile(entry.templatePath)) {
00737         KDesktopFile df(entry.templatePath);
00738         if (df.readType() == "Link") {
00739         executeUrlDesktopFile(entry);
00740         } else { // any other desktop file (Device, App, etc.)
00741         executeOtherDesktopFile(entry);
00742         }
00743     }
00744     else {
00745     executeRealFileOrDir(entry);
00746     }
00747 
00748 }
00749 
00750 void KNewFileMenuPrivate::_k_slotCreateDirectory(bool writeHiddenDir)
00751 {
00752     KUrl url;
00753     KUrl baseUrl = m_popupFiles.first();
00754     bool askAgain = false;
00755 
00756     QString name = expandTilde(m_text);
00757 
00758     if (!name.isEmpty()) {
00759       if ((name[0] == '/'))
00760         url.setPath(name);
00761       else {
00762         if (!m_viewShowsHiddenFiles && name.startsWith('.')) {
00763           if (!writeHiddenDir) {
00764             confirmCreatingHiddenDir(name);
00765             return;
00766           }
00767         }
00768         name = KIO::encodeFileName( name );
00769         url = baseUrl;
00770         url.addPath( name );
00771       }
00772     }
00773 
00774     if (!askAgain) {
00775       KIO::SimpleJob * job = KIO::mkdir(url);
00776       job->setProperty("isMkdirJob", true); // KDE5: cast to MkdirJob in slotResult instead
00777       job->ui()->setWindow(m_parentWidget);
00778       job->ui()->setAutoErrorHandlingEnabled(true);
00779       KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Mkdir, KUrl(), url, job );
00780 
00781       if (job) {
00782         // We want the error handling to be done by slotResult so that subclasses can reimplement it
00783         job->ui()->setAutoErrorHandlingEnabled(false);
00784         QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(slotResult(KJob*)));
00785       }
00786     }
00787     else {
00788       q->createDirectory(); // ask again for the name
00789     }
00790     _k_slotAbortDialog();
00791 }
00792 
00793 void KNewFileMenuPrivate::_k_slotCreateHiddenDirectory()
00794 {
00795     _k_slotCreateDirectory(true);
00796 }
00797 
00798 void KNewFileMenuPrivate::_k_slotFillTemplates()
00799 {
00800     KNewFileMenuSingleton* s = kNewMenuGlobals;
00801     //kDebug(1203);
00802     // Ensure any changes in the templates dir will call this
00803     if (! s->dirWatch) {
00804         s->dirWatch = new KDirWatch;
00805         const QStringList dirs = m_actionCollection->componentData().dirs()->resourceDirs("templates");
00806         for (QStringList::const_iterator it = dirs.constBegin() ; it != dirs.constEnd() ; ++it) {
00807             //kDebug(1203) << "Templates resource dir:" << *it;
00808             s->dirWatch->addDir(*it);
00809         }
00810         QObject::connect(s->dirWatch, SIGNAL(dirty(QString)),
00811                          q, SLOT(_k_slotFillTemplates()));
00812         QObject::connect(s->dirWatch, SIGNAL(created(QString)),
00813                          q, SLOT(_k_slotFillTemplates()));
00814         QObject::connect(s->dirWatch, SIGNAL(deleted(QString)),
00815                          q, SLOT(_k_slotFillTemplates()));
00816         // Ok, this doesn't cope with new dirs in KDEDIRS, but that's another story
00817     }
00818     ++s->templatesVersion;
00819     s->filesParsed = false;
00820 
00821     s->templatesList->clear();
00822 
00823     // Look into "templates" dirs.
00824     const QStringList files = m_actionCollection->componentData().dirs()->findAllResources("templates");
00825     QMap<QString, KNewFileMenuSingleton::Entry> slist; // used for sorting
00826     Q_FOREACH(const QString& file, files) {
00827         //kDebug(1203) << file;
00828         if (file[0] != '.') {
00829             KNewFileMenuSingleton::Entry e;
00830             e.filePath = file;
00831             e.entryType = KNewFileMenuSingleton::Unknown; // not parsed yet
00832 
00833             // Put Directory first in the list (a bit hacky),
00834             // and TextFile before others because it's the most used one.
00835             // This also sorts by user-visible name.
00836             // The rest of the re-ordering is done in fillMenu.
00837             const KDesktopFile config(file);
00838             QString key = config.desktopGroup().readEntry("Name");
00839             if (file.endsWith("Directory.desktop")) {
00840                 key.prepend('0');
00841             } else if (file.endsWith("TextFile.desktop")) {
00842                 key.prepend('1');
00843             } else {
00844                 key.prepend('2');
00845             }
00846             slist.insert(key, e);
00847         }
00848     }
00849     (*s->templatesList) += slist.values();
00850 }
00851 
00852 void KNewFileMenuPrivate::_k_slotOtherDesktopFile()
00853 {
00854     executeStrategy();
00855 }
00856 
00857 void KNewFileMenuPrivate::_k_slotRealFileOrDir()
00858 {
00859     m_copyData.m_chosenFileName = m_text;
00860     _k_slotAbortDialog();
00861     executeStrategy();
00862 }
00863 
00864 void KNewFileMenuPrivate::_k_slotSymLink()
00865 {
00866     KNameAndUrlInputDialog* dlg = static_cast<KNameAndUrlInputDialog*>(m_fileDialog);
00867 
00868     m_copyData.m_chosenFileName = dlg->name(); // no path
00869     KUrl linkUrl = dlg->url(); // the url to put in the file
00870 
00871     if (m_copyData.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
00872         return;
00873 
00874     if (linkUrl.isRelative())
00875         m_copyData.m_src = linkUrl.url();
00876     else if (linkUrl.isLocalFile())
00877         m_copyData.m_src = linkUrl.toLocalFile();
00878     else {
00879     KDialog* dialog = new KDialog(m_parentWidget);
00880     dialog->setCaption( i18n("Sorry") );
00881     dialog->setButtons( KDialog::Ok );
00882     dialog->setObjectName( "sorry" );
00883     dialog->setModal(m_modal);
00884     dialog->setAttribute(Qt::WA_DeleteOnClose);
00885     dialog->setDefaultButton( KDialog::Ok );
00886     dialog->setEscapeButton( KDialog::Ok );
00887     m_fileDialog = dialog;
00888 
00889     KMessageBox::createKMessageBox(dialog, QMessageBox::Warning,
00890       i18n("Basic links can only point to local files or directories.\nPlease use \"Link to Location\" for remote URLs."),
00891       QStringList(), QString(), 0, KMessageBox::NoExec,
00892       QString());
00893 
00894     dialog->show();
00895     return;
00896     }
00897     executeStrategy();
00898 }
00899 
00900 void KNewFileMenuPrivate::_k_slotTextChanged(const QString & text)
00901 {
00902     m_text = text;
00903 }
00904 
00905 void KNewFileMenuPrivate::_k_slotUrlDesktopFile()
00906 {
00907     KNameAndUrlInputDialog* dlg = static_cast<KNameAndUrlInputDialog*>(m_fileDialog);
00908 
00909     m_copyData.m_chosenFileName = dlg->name(); // no path
00910     KUrl linkUrl = dlg->url();
00911 
00912     // Filter user input so that short uri entries, e.g. www.kde.org, are
00913     // handled properly. This not only makes the icon detection below work
00914     // properly, but opening the URL link where the short uri will not be
00915     // sent to the application (opening such link Konqueror fails).
00916     KUriFilterData uriData;
00917     uriData.setData(linkUrl); // the url to put in the file
00918     uriData.setCheckForExecutables(false);
00919 
00920     if (KUriFilter::self()->filterUri(uriData, QStringList() << QLatin1String("kshorturifilter"))) {
00921         linkUrl = uriData.uri();
00922     }
00923 
00924     if (m_copyData.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
00925         return;
00926 
00927     // It's a "URL" desktop file; we need to make a temp copy of it, to modify it
00928     // before copying it to the final destination [which could be a remote protocol]
00929     KTemporaryFile tmpFile;
00930     tmpFile.setAutoRemove(false); // done below
00931     if (!tmpFile.open()) {
00932         kError() << "Couldn't create temp file!";
00933         return;
00934     }
00935 
00936     if (!checkSourceExists(m_copyData.m_templatePath)) {
00937         return;
00938     }
00939 
00940     // First copy the template into the temp file
00941     QFile file(m_copyData.m_templatePath);
00942     if (!file.open(QIODevice::ReadOnly)) {
00943         kError() << "Couldn't open template" << m_copyData.m_templatePath;
00944         return;
00945     }
00946     const QByteArray data = file.readAll();
00947     tmpFile.write(data);
00948     const QString tempFileName = tmpFile.fileName();
00949     Q_ASSERT(!tempFileName.isEmpty());
00950     tmpFile.close();
00951     file.close();
00952 
00953     KDesktopFile df(tempFileName);
00954     KConfigGroup group = df.desktopGroup();
00955     group.writeEntry("Icon", KProtocolInfo::icon(linkUrl.protocol()));
00956     group.writePathEntry("URL", linkUrl.prettyUrl());
00957     df.sync();
00958 
00959     m_copyData.m_src = tempFileName;
00960     m_copyData.m_tempFileToDelete = tempFileName;
00961 
00962     executeStrategy();
00963 }
00964 
00965 KNewFileMenu::KNewFileMenu(KActionCollection* collection, const QString& name, QObject* parent)
00966     : KActionMenu(KIcon("document-new"), i18n("Create New"), parent),
00967       d(new KNewFileMenuPrivate(this))
00968 {
00969     // Don't fill the menu yet
00970     // We'll do that in checkUpToDate (should be connected to aboutToShow)
00971     d->m_newMenuGroup = new QActionGroup(this);
00972     connect(d->m_newMenuGroup, SIGNAL(triggered(QAction*)), this, SLOT(_k_slotActionTriggered(QAction*)));
00973     d->m_actionCollection = collection;
00974     d->m_parentWidget = qobject_cast<QWidget*>(parent);
00975     d->m_newDirAction = 0;
00976 
00977     d->m_actionCollection->addAction(name, this);
00978 
00979     d->m_menuDev = new KActionMenu(KIcon("drive-removable-media"), i18n("Link to Device"), this);
00980 }
00981 
00982 KNewFileMenu::~KNewFileMenu()
00983 {
00984     //kDebug(1203) << this;
00985     delete d;
00986 }
00987 
00988 void KNewFileMenu::checkUpToDate()
00989 {
00990     KNewFileMenuSingleton* s = kNewMenuGlobals;
00991     //kDebug(1203) << this << "m_menuItemsVersion=" << d->m_menuItemsVersion
00992     //              << "s->templatesVersion=" << s->templatesVersion;
00993     if (d->m_menuItemsVersion < s->templatesVersion || s->templatesVersion == 0) {
00994         //kDebug(1203) << "recreating actions";
00995         // We need to clean up the action collection
00996         // We look for our actions using the group
00997         foreach (QAction* action, d->m_newMenuGroup->actions())
00998             delete action;
00999 
01000         if (!s->templatesList) { // No templates list up to now
01001             s->templatesList = new KNewFileMenuSingleton::EntryList;
01002             d->_k_slotFillTemplates();
01003             s->parseFiles();
01004         }
01005 
01006         // This might have been already done for other popupmenus,
01007         // that's the point in s->filesParsed.
01008         if (!s->filesParsed) {
01009             s->parseFiles();
01010         }
01011 
01012         d->fillMenu();
01013 
01014         d->m_menuItemsVersion = s->templatesVersion;
01015     }
01016 }
01017 
01018 void KNewFileMenu::createDirectory()
01019 {
01020     if (d->m_popupFiles.isEmpty())
01021     return;
01022 
01023     KUrl baseUrl = d->m_popupFiles.first();
01024     QString name = d->m_text.isEmpty()? i18nc("Default name for a new folder", "New Folder") :
01025       d->m_text;
01026 
01027     if (baseUrl.isLocalFile() && QFileInfo(baseUrl.toLocalFile(KUrl::AddTrailingSlash) + name).exists())
01028     name = KIO::RenameDialog::suggestName(baseUrl, name);
01029 
01030     KDialog* fileDialog = new KDialog(d->m_parentWidget);
01031     fileDialog->setModal(isModal());
01032     fileDialog->setAttribute(Qt::WA_DeleteOnClose);
01033     fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
01034     fileDialog->setCaption(i18nc("@title:window", "New Folder"));
01035 
01036     QWidget* mainWidget = new QWidget(fileDialog);
01037     QVBoxLayout *layout = new QVBoxLayout(mainWidget);
01038     QLabel *label = new QLabel(i18n("Create new folder in:\n%1", baseUrl.pathOrUrl()));
01039 
01040     // We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
01041     // It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
01042     // TODO: should probably be investigated and fixed in KLineEdit.
01043     KLineEdit *lineEdit = new KLineEdit;
01044     lineEdit->setClearButtonShown(true);
01045     lineEdit->setText(name);
01046 
01047     d->_k_slotTextChanged(name); // have to save string in d->m_text in case user does not touch dialog
01048     connect(lineEdit, SIGNAL(textChanged(QString)), this, SLOT(_k_slotTextChanged(QString)));
01049     layout->addWidget(label);
01050     layout->addWidget(lineEdit);
01051 
01052     fileDialog->setMainWidget(mainWidget);
01053     connect(fileDialog, SIGNAL(accepted()), this, SLOT(_k_slotCreateDirectory()));
01054     connect(fileDialog, SIGNAL(rejected()), this, SLOT(_k_slotAbortDialog()));
01055 
01056     d->m_fileDialog = fileDialog;
01057 
01058     fileDialog->show();
01059     lineEdit->selectAll();
01060     lineEdit->setFocus();
01061 }
01062 
01063 bool KNewFileMenu::isModal() const
01064 {
01065     return d->m_modal;
01066 }
01067 
01068 KUrl::List KNewFileMenu::popupFiles() const
01069 {
01070     return d->m_popupFiles;
01071 }
01072 
01073 void KNewFileMenu::setModal(bool modal)
01074 {
01075     d->m_modal = modal;
01076 }
01077 
01078 void KNewFileMenu::setPopupFiles(const KUrl::List& files)
01079 {
01080     d->m_popupFiles = files;
01081     if (files.isEmpty()) {
01082         d->m_newMenuGroup->setEnabled(false);
01083     } else {
01084         KUrl firstUrl = files.first();
01085         if (KProtocolManager::supportsWriting(firstUrl)) {
01086             d->m_newMenuGroup->setEnabled(true);
01087             if (d->m_newDirAction) {
01088                 d->m_newDirAction->setEnabled(KProtocolManager::supportsMakeDir(firstUrl)); // e.g. trash:/
01089             }
01090         } else {
01091             d->m_newMenuGroup->setEnabled(true);
01092         }
01093     }
01094 }
01095 
01096 
01097 void KNewFileMenu::setParentWidget(QWidget* parentWidget)
01098 {
01099     d->m_parentWidget = parentWidget;
01100 }
01101 
01102 void KNewFileMenu::setSupportedMimeTypes(const QStringList& mime)
01103 {
01104     d->m_supportedMimeTypes = mime;
01105 }
01106 
01107 void KNewFileMenu::setViewShowsHiddenFiles(bool b)
01108 {
01109     d->m_viewShowsHiddenFiles = b;
01110 }
01111 
01112 void KNewFileMenu::slotResult(KJob * job)
01113 {
01114     if (job->error()) {
01115         static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
01116     } else {
01117         // Was this a copy or a mkdir?
01118         KIO::CopyJob* copyJob = ::qobject_cast<KIO::CopyJob*>(job);
01119         if (copyJob) {
01120             const KUrl destUrl = copyJob->destUrl();
01121             const KUrl localUrl = KIO::NetAccess::mostLocalUrl(destUrl, d->m_parentWidget);
01122             if (localUrl.isLocalFile()) {
01123                 // Normal (local) file. Need to "touch" it, kio_file copied the mtime.
01124                 (void) ::utime(QFile::encodeName(localUrl.toLocalFile()), 0);
01125             }
01126             emit fileCreated(destUrl);
01127         } else if (KIO::SimpleJob* simpleJob = ::qobject_cast<KIO::SimpleJob*>(job)) {
01128             // Can be mkdir or symlink
01129             if (simpleJob->property("isMkdirJob").toBool() == true) {
01130                 kDebug() << "Emit directoryCreated" << simpleJob->url();
01131                 emit directoryCreated(simpleJob->url());
01132             } else {
01133                 emit fileCreated(simpleJob->url());
01134             }
01135         }
01136     }
01137     if (!d->m_tempFileToDelete.isEmpty())
01138         QFile::remove(d->m_tempFileToDelete);
01139 }
01140 
01141 
01142 QStringList KNewFileMenu::supportedMimeTypes() const
01143 {
01144     return d->m_supportedMimeTypes;
01145 }
01146 
01147 
01148 #include "knewfilemenu.moc"
01149 
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Thu May 10 2012 21:01:45 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KFile

Skip menu "KFile"
  • Main Page
  • Namespace List
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.8.3 API Reference

Skip menu "kdelibs-4.8.3 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal