• Skip to content
  • Skip to link menu
KDE 4.8 API Reference
  • KDE API Reference
  • kdelibs
  • 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 
00208 class KNewFileMenuStrategy
00209 {
00210 friend class KNewFileMenuPrivate;
00211 public:
00212     KNewFileMenuStrategy() { m_isSymlink = false;}
00213     ~KNewFileMenuStrategy() {}
00214     QString chosenFileName() const { return m_chosenFileName; }
00215 
00216     // If empty, no copy is performed.
00217     QString sourceFileToCopy() const { return m_src; }
00218     QString tempFileToDelete() const { return m_tempFileToDelete; }
00219     bool m_isSymlink;
00220     
00221 protected:    
00222     QString m_chosenFileName;
00223     QString m_src;
00224     QString m_tempFileToDelete;
00225     QString m_templatePath;
00226 };
00227 
00228 class KNewFileMenuPrivate
00229 {
00230 public:
00231     KNewFileMenuPrivate(KNewFileMenu* qq)
00232         : m_menuItemsVersion(0),          
00233           m_modal(true),
00234           m_viewShowsHiddenFiles(false),
00235           q(qq)
00236     {}
00237         
00238     bool checkSourceExists(const QString& src);
00239     
00243     void confirmCreatingHiddenDir(const QString& name);
00244     
00248     void executeOtherDesktopFile(const KNewFileMenuSingleton::Entry& entry);
00249     
00253     void executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry);
00254 
00258     void executeStrategy();
00259         
00263     void executeSymLink(const KNewFileMenuSingleton::Entry& entry);    
00264         
00268     void executeUrlDesktopFile(const KNewFileMenuSingleton::Entry& entry);
00269 
00273     void fillMenu();
00274       
00278     void _k_slotAbortDialog();
00279     
00283     void _k_slotActionTriggered(QAction* action);
00284   
00288     void _k_slotCreateDirectory(bool writeHiddenDir = false);
00289     
00294     void _k_slotCreateHiddenDirectory();
00295             
00299     void _k_slotFillTemplates();
00300     
00305     void _k_slotOtherDesktopFile();
00306     
00311     void _k_slotRealFileOrDir();
00312         
00317     void _k_slotTextChanged(const QString & text);
00318         
00323     void _k_slotSymLink();
00324     
00329     void _k_slotUrlDesktopFile();
00330 
00331 
00332     KActionCollection * m_actionCollection;
00333     KDialog* m_fileDialog;
00334     
00335     KActionMenu *m_menuDev;
00336     int m_menuItemsVersion;
00337     bool m_modal;
00338     QAction* m_newDirAction;
00339 
00343     QActionGroup* m_newMenuGroup;
00344     QWidget *m_parentWidget;
00345 
00350     KUrl::List m_popupFiles;
00351 
00352     QStringList m_supportedMimeTypes;
00353     QString m_tempFileToDelete; // set when a tempfile was created for a Type=URL desktop file
00354     QString m_text;
00355     bool m_viewShowsHiddenFiles;
00356 
00357     KNewFileMenu* q;
00358 
00359     class Strategy;
00360     KNewFileMenuStrategy m_strategy;
00361 };
00362 
00363 bool KNewFileMenuPrivate::checkSourceExists(const QString& src)
00364 {
00365     if (!QFile::exists(src)) {
00366         kWarning(1203) << src << "doesn't exist" ;
00367     
00368     KDialog* dialog = new KDialog(m_parentWidget);
00369     dialog->setCaption( i18n("Sorry") );
00370     dialog->setButtons( KDialog::Ok );
00371     dialog->setObjectName( "sorry" );
00372     dialog->setModal(q->isModal());
00373     dialog->setAttribute(Qt::WA_DeleteOnClose);
00374     dialog->setDefaultButton( KDialog::Ok );
00375     dialog->setEscapeButton( KDialog::Ok );
00376     
00377     KMessageBox::createKMessageBox(dialog, QMessageBox::Warning, 
00378       i18n("<qt>The template file <b>%1</b> does not exist.</qt>", src), 
00379       QStringList(), QString(), 0, KMessageBox::NoExec,
00380       QString());
00381     
00382     dialog->show();
00383     
00384         return false;
00385     }
00386     return true;
00387 }
00388 
00389 void KNewFileMenuPrivate::confirmCreatingHiddenDir(const QString& name)
00390 {
00391     if(!KMessageBox::shouldBeShownContinue("confirm_create_hidden_dir")){
00392     _k_slotCreateHiddenDirectory();
00393     return;
00394     }
00395   
00396     KGuiItem continueGuiItem(KStandardGuiItem::cont());
00397     continueGuiItem.setText(i18nc("@action:button", "Create directory"));
00398     KGuiItem cancelGuiItem(KStandardGuiItem::cancel());
00399     cancelGuiItem.setText(i18nc("@action:button", "Enter a different name"));
00400     
00401     KDialog* confirmDialog = new KDialog(m_parentWidget);
00402     confirmDialog->setCaption(i18n("Create hidden directory?"));
00403     confirmDialog->setModal(m_modal);
00404     confirmDialog->setAttribute(Qt::WA_DeleteOnClose);
00405     KMessageBox::createKMessageBox(confirmDialog, QMessageBox::Warning, 
00406       i18n("The name \"%1\" starts with a dot, so the directory will be hidden by default.", name),
00407       QStringList(),
00408       i18n("Do not ask again"),
00409       0,
00410       KMessageBox::NoExec,
00411       QString());
00412     confirmDialog->setButtonGuiItem(KDialog::Ok, continueGuiItem);
00413     confirmDialog->setButtonGuiItem(KDialog::Cancel, cancelGuiItem);
00414     
00415     QObject::connect(confirmDialog, SIGNAL(accepted()), q, SLOT(_k_slotCreateHiddenDirectory()));
00416     QObject::connect(confirmDialog, SIGNAL(rejected()), q, SLOT(createDirectory()));
00417     
00418     m_fileDialog = confirmDialog;
00419     confirmDialog->show();
00420     
00421 }
00422 
00423 void KNewFileMenuPrivate::executeOtherDesktopFile(const KNewFileMenuSingleton::Entry& entry)
00424 {
00425     if (!checkSourceExists(entry.templatePath)) {
00426         return;
00427     }
00428 
00429     KUrl::List::const_iterator it = m_popupFiles.constBegin();
00430     for (; it != m_popupFiles.constEnd(); ++it)
00431     {
00432         QString text = entry.text;
00433         text.remove("..."); // the ... is fine for the menu item but not for the default filename
00434         text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
00435         // KDE5 TODO: remove the "..." from link*.desktop files and use i18n("%1...") when making
00436         // the action.
00437 
00438         KUrl defaultFile(*it);
00439         defaultFile.addPath(KIO::encodeFileName(text));
00440         if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
00441             text = KIO::RenameDialog::suggestName(*it, text);
00442 
00443         const KUrl templateUrl(entry.templatePath);
00444     
00445     KDialog* dlg = new KPropertiesDialog(templateUrl, *it, text, m_parentWidget);
00446     dlg->setModal(q->isModal());
00447     dlg->setAttribute(Qt::WA_DeleteOnClose);
00448         QObject::connect(dlg, SIGNAL(applied()), q, SLOT(_k_slotOtherDesktopFile()));
00449     dlg->show();
00450     }
00451     // We don't set m_src here -> there will be no copy, we are done.
00452 }
00453 
00454 void KNewFileMenuPrivate::executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry)
00455 {
00456     // The template is not a desktop file
00457     // Show the small dialog for getting the destination filename
00458     QString text = entry.text;
00459     text.remove("..."); // the ... is fine for the menu item but not for the default filename
00460     text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
00461     m_strategy.m_src = entry.templatePath;
00462 
00463     KUrl defaultFile(m_popupFiles.first());
00464     defaultFile.addPath(KIO::encodeFileName(text));
00465     if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
00466         text = KIO::RenameDialog::suggestName(m_popupFiles.first(), text);
00467     
00468     KDialog* fileDialog = new KDialog(m_parentWidget);
00469     fileDialog->setAttribute(Qt::WA_DeleteOnClose);
00470     fileDialog->setModal(q->isModal());
00471     fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
00472     
00473     QWidget* mainWidget = new QWidget(fileDialog);
00474     QVBoxLayout *layout = new QVBoxLayout(mainWidget);
00475     QLabel *label = new QLabel(entry.comment);
00476 
00477     // We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
00478     // It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
00479     // TODO: should probably be investigated and fixed in KLineEdit.
00480     KLineEdit *lineEdit = new KLineEdit;
00481     lineEdit->setClearButtonShown(true);
00482     lineEdit->setText(text);
00483 
00484     _k_slotTextChanged(text);
00485     QObject::connect(lineEdit, SIGNAL(textChanged(const QString &)), q, SLOT(_k_slotTextChanged(const QString &)));
00486     
00487     layout->addWidget(label);
00488     layout->addWidget(lineEdit);
00489     
00490     fileDialog->setMainWidget(mainWidget);
00491     QObject::connect(fileDialog, SIGNAL(accepted()), q, SLOT(_k_slotRealFileOrDir()));
00492     QObject::connect(fileDialog, SIGNAL(rejected()), q, SLOT(_k_slotAbortDialog()));
00493  
00494     fileDialog->show();
00495     lineEdit->selectAll();
00496     lineEdit->setFocus();
00497 }
00498 
00499 void KNewFileMenuPrivate::executeSymLink(const KNewFileMenuSingleton::Entry& entry)
00500 {
00501     KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);
00502     dlg->setModal(q->isModal());
00503     dlg->setAttribute(Qt::WA_DeleteOnClose);
00504     dlg->setCaption(i18n("Create Symlink"));
00505     m_fileDialog = dlg;
00506     QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotSymLink()));
00507     dlg->show();    
00508 }
00509 
00510 void KNewFileMenuPrivate::executeStrategy()
00511 {
00512     m_tempFileToDelete = m_strategy.tempFileToDelete();
00513     const QString src = m_strategy.sourceFileToCopy();
00514     QString chosenFileName = expandTilde(m_strategy.chosenFileName(), true);
00515 
00516     if (src.isEmpty())
00517         return;
00518     KUrl uSrc(src);
00519 
00520     if (uSrc.isLocalFile()) {
00521         // In case the templates/.source directory contains symlinks, resolve
00522         // them to the target files. Fixes bug #149628.
00523         KFileItem item(uSrc, QString(), KFileItem::Unknown);
00524         if (item.isLink())
00525             uSrc.setPath(item.linkDest());
00526     }
00527 
00528     // The template is not a desktop file [or it's a URL one]
00529     // Copy it.
00530     KUrl::List::const_iterator it = m_popupFiles.constBegin();
00531     for (; it != m_popupFiles.constEnd(); ++it)
00532     {
00533         KUrl dest(*it);
00534         dest.addPath(KIO::encodeFileName(chosenFileName));
00535 
00536         KUrl::List lstSrc;
00537         lstSrc.append(uSrc);
00538         KIO::Job* kjob;
00539         if (m_strategy.m_isSymlink) {
00540             kjob = KIO::symlink(src, dest);
00541             // This doesn't work, FileUndoManager registers new links in copyingLinkDone,
00542             // which KIO::symlink obviously doesn't emit... Needs code in FileUndoManager.
00543             //KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Link, lstSrc, dest, kjob);
00544         } else {
00545             //kDebug(1203) << "KIO::copyAs(" << uSrc.url() << "," << dest.url() << ")";
00546             KIO::CopyJob * job = KIO::copyAs(uSrc, dest);
00547             job->setDefaultPermissions(true);
00548             kjob = job;
00549             KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Copy, lstSrc, dest, job);
00550         }
00551         kjob->ui()->setWindow(m_parentWidget);
00552         QObject::connect(kjob, SIGNAL(result(KJob*)), q, SLOT(slotResult(KJob*)));
00553     }
00554 }
00555 
00556 void KNewFileMenuPrivate::executeUrlDesktopFile(const KNewFileMenuSingleton::Entry& entry)
00557 {
00558     KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);
00559     m_strategy.m_templatePath = entry.templatePath;
00560     dlg->setModal(q->isModal());
00561     dlg->setAttribute(Qt::WA_DeleteOnClose);
00562     dlg->setCaption(i18n("Create link to URL"));
00563     m_fileDialog = dlg;
00564     QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotUrlDesktopFile()));
00565     dlg->show();    
00566 }
00567 
00568 void KNewFileMenuPrivate::fillMenu()
00569 {
00570     QMenu* menu = q->menu();
00571     menu->clear();
00572     m_menuDev->menu()->clear();
00573     m_newDirAction = 0;
00574 
00575     QSet<QString> seenTexts;
00576     // these shall be put at special positions
00577     QAction* linkURL = 0;
00578     QAction* linkApp = 0;
00579     QAction* linkPath = 0;
00580 
00581     KNewFileMenuSingleton* s = kNewMenuGlobals;
00582     int i = 1;
00583     KNewFileMenuSingleton::EntryList::iterator templ = s->templatesList->begin();
00584     const KNewFileMenuSingleton::EntryList::iterator templ_end = s->templatesList->end();
00585     for (; templ != templ_end; ++templ, ++i)
00586     {
00587         KNewFileMenuSingleton::Entry& entry = *templ;
00588         if (entry.entryType != KNewFileMenuSingleton::Separator) {
00589             // There might be a .desktop for that one already, if it's a kdelnk
00590             // This assumes we read .desktop files before .kdelnk files ...
00591 
00592             // In fact, we skip any second item that has the same text as another one.
00593             // Duplicates in a menu look bad in any case.
00594 
00595             const bool bSkip = seenTexts.contains(entry.text);
00596             if (bSkip) {
00597                 kDebug(1203) << "skipping" << entry.filePath;
00598             } else {
00599                 seenTexts.insert(entry.text);
00600                 //const KNewFileMenuSingleton::Entry entry = templatesList->at(i-1);
00601 
00602                 const QString templatePath = entry.templatePath;
00603                 // The best way to identify the "Create Directory", "Link to Location", "Link to Application" was the template
00604                 if (templatePath.endsWith("emptydir")) {
00605                     QAction * act = new QAction(q);
00606                     m_newDirAction = act;
00607                     act->setIcon(KIcon(entry.icon));
00608                     act->setText(i18nc("@item:inmenu Create New", "%1", entry.text));
00609                     act->setActionGroup(m_newMenuGroup);
00610                     menu->addAction(act);
00611 
00612                     QAction *sep = new QAction(q);
00613                     sep->setSeparator(true);
00614                     menu->addAction(sep);
00615                 } else {
00616 
00617                     if (!m_supportedMimeTypes.isEmpty()) {
00618                         bool keep = false;
00619 
00620                         // We need to do mimetype filtering, for real files.
00621                         const bool createSymlink = entry.templatePath == "__CREATE_SYMLINK__";
00622                         if (createSymlink) {
00623                             keep = true;
00624                         } else if (!KDesktopFile::isDesktopFile(entry.templatePath)) {
00625 
00626                             // Determine mimetype on demand
00627                             KMimeType::Ptr mime;
00628                             if (entry.mimeType.isEmpty()) {
00629                                 mime = KMimeType::findByPath(entry.templatePath);
00630                                 if (mime) {
00631                                     //kDebug() << entry.templatePath << "is" << mime->name();
00632                                     entry.mimeType = mime->name();
00633                                 } else {
00634                                     entry.mimeType = KMimeType::defaultMimeType();
00635                                 }
00636                             } else {
00637                                 mime = KMimeType::mimeType(entry.mimeType);
00638                             }
00639                             Q_FOREACH(const QString& supportedMime, m_supportedMimeTypes) {
00640                                 if (mime && mime->is(supportedMime)) {
00641                                     keep = true;
00642                                     break;
00643                                 }
00644                             }
00645                         }
00646 
00647                         if (!keep) {
00648                             //kDebug() << "Not keeping" << entry.templatePath;
00649                             continue;
00650                         }
00651                     }
00652 
00653                     QAction * act = new QAction(q);
00654                     act->setData(i);
00655                     act->setIcon(KIcon(entry.icon));
00656                     act->setText(i18nc("@item:inmenu Create New", "%1", entry.text));
00657                     act->setActionGroup(m_newMenuGroup);
00658 
00659                     //kDebug() << templatePath << entry.filePath;
00660 
00661                     if (templatePath.endsWith("/URL.desktop")) {
00662                         linkURL = act;
00663                     } else if (templatePath.endsWith("/Program.desktop")) {
00664                         linkApp = act;
00665                     } else if (entry.filePath.endsWith("/linkPath.desktop")) {
00666                         linkPath = act;
00667                     } else if (KDesktopFile::isDesktopFile(templatePath)) {
00668                         KDesktopFile df(templatePath);
00669                         if (df.readType() == "FSDevice")
00670                             m_menuDev->menu()->addAction(act);
00671                         else
00672                             menu->addAction(act);
00673                     }
00674                     else
00675                     {
00676                         menu->addAction(act);
00677                     }
00678                 }
00679             }
00680         } else { // Separate system from personal templates
00681             Q_ASSERT(entry.entryType != 0);
00682 
00683             QAction *sep = new QAction(q);
00684             sep->setSeparator(true);
00685             menu->addAction(sep);
00686         }
00687     }
00688 
00689     if (m_supportedMimeTypes.isEmpty()) {
00690         QAction *sep = new QAction(q);
00691         sep->setSeparator(true);
00692         menu->addAction(sep);
00693         if (linkURL) menu->addAction(linkURL);
00694         if (linkPath) menu->addAction(linkPath);
00695         if (linkApp) menu->addAction(linkApp);
00696         Q_ASSERT(m_menuDev);
00697         menu->addAction(m_menuDev);
00698     }
00699 }
00700 
00701 void KNewFileMenuPrivate::_k_slotAbortDialog()
00702 {
00703     m_text = QString();
00704 }
00705 
00706 void KNewFileMenuPrivate::_k_slotActionTriggered(QAction* action)
00707 {
00708     q->trigger(); // was for kdesktop's slotNewMenuActivated() in kde3 times. Can't hurt to keep it...
00709 
00710     if (action == m_newDirAction) {
00711         q->createDirectory();
00712         return;
00713     }
00714     const int id = action->data().toInt();
00715     Q_ASSERT(id > 0);
00716 
00717     KNewFileMenuSingleton* s = kNewMenuGlobals;
00718     const KNewFileMenuSingleton::Entry entry = s->templatesList->at(id - 1);
00719 
00720     const bool createSymlink = entry.templatePath == "__CREATE_SYMLINK__";
00721 
00722     m_strategy = KNewFileMenuStrategy();
00723     
00724     if (createSymlink) {
00725         m_strategy.m_isSymlink = true;
00726     executeSymLink(entry);
00727     } 
00728     else if (KDesktopFile::isDesktopFile(entry.templatePath)) {
00729         KDesktopFile df(entry.templatePath);
00730         if (df.readType() == "Link") {
00731         executeUrlDesktopFile(entry);
00732         } else { // any other desktop file (Device, App, etc.)
00733         executeOtherDesktopFile(entry);
00734         }
00735     } 
00736     else {
00737     executeRealFileOrDir(entry);
00738     }
00739     
00740 }
00741 
00742 void KNewFileMenuPrivate::_k_slotCreateDirectory(bool writeHiddenDir)
00743 {    
00744     KUrl url;
00745     KUrl baseUrl = m_popupFiles.first();
00746     bool askAgain = false;
00747   
00748     QString name = expandTilde(m_text);
00749     
00750     if (!name.isEmpty()) {
00751       if ((name[0] == '/'))
00752         url.setPath(name);
00753       else {
00754         if (!m_viewShowsHiddenFiles && name.startsWith('.')) {
00755           if (!writeHiddenDir) {
00756             confirmCreatingHiddenDir(name);
00757             return;
00758           }
00759         }
00760         name = KIO::encodeFileName( name );
00761         url = baseUrl;
00762         url.addPath( name );
00763       }
00764     }
00765 
00766     if (!askAgain) {
00767       KIO::SimpleJob * job = KIO::mkdir(url);
00768       job->setProperty("isMkdirJob", true); // KDE5: cast to MkdirJob in slotResult instead
00769       job->ui()->setWindow(m_parentWidget);
00770       job->ui()->setAutoErrorHandlingEnabled(true);
00771       KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Mkdir, KUrl(), url, job );
00772 
00773       if (job) {
00774         // We want the error handling to be done by slotResult so that subclasses can reimplement it
00775         job->ui()->setAutoErrorHandlingEnabled(false);
00776         QObject::connect(job, SIGNAL(result(KJob *)), q, SLOT(slotResult(KJob *)));
00777       }
00778     }
00779     else {
00780       q->createDirectory(); // ask again for the name
00781     }
00782     _k_slotAbortDialog();
00783 }
00784 
00785 void KNewFileMenuPrivate::_k_slotCreateHiddenDirectory()
00786 {
00787     _k_slotCreateDirectory(true);    
00788 }
00789 
00790 void KNewFileMenuPrivate::_k_slotFillTemplates()
00791 {
00792     KNewFileMenuSingleton* s = kNewMenuGlobals;
00793     //kDebug(1203);
00794     // Ensure any changes in the templates dir will call this
00795     if (! s->dirWatch) {
00796         s->dirWatch = new KDirWatch;
00797         const QStringList dirs = m_actionCollection->componentData().dirs()->resourceDirs("templates");
00798         for (QStringList::const_iterator it = dirs.constBegin() ; it != dirs.constEnd() ; ++it) {
00799             //kDebug(1203) << "Templates resource dir:" << *it;
00800             s->dirWatch->addDir(*it);
00801         }
00802         QObject::connect(s->dirWatch, SIGNAL(dirty(const QString &)),
00803                          q, SLOT(_k_slotFillTemplates()));
00804         QObject::connect(s->dirWatch, SIGNAL(created(const QString &)),
00805                          q, SLOT(_k_slotFillTemplates()));
00806         QObject::connect(s->dirWatch, SIGNAL(deleted(const QString &)),
00807                          q, SLOT(_k_slotFillTemplates()));
00808         // Ok, this doesn't cope with new dirs in KDEDIRS, but that's another story
00809     }
00810     ++s->templatesVersion;
00811     s->filesParsed = false;
00812 
00813     s->templatesList->clear();
00814 
00815     // Look into "templates" dirs.
00816     const QStringList files = m_actionCollection->componentData().dirs()->findAllResources("templates");
00817     QMap<QString, KNewFileMenuSingleton::Entry> slist; // used for sorting
00818     Q_FOREACH(const QString& file, files) {
00819         //kDebug(1203) << file;
00820         if (file[0] != '.') {
00821             KNewFileMenuSingleton::Entry e;
00822             e.filePath = file;
00823             e.entryType = KNewFileMenuSingleton::Unknown; // not parsed yet
00824 
00825             // Put Directory first in the list (a bit hacky),
00826             // and TextFile before others because it's the most used one.
00827             // This also sorts by user-visible name.
00828             // The rest of the re-ordering is done in fillMenu.
00829             const KDesktopFile config(file);
00830             QString key = config.desktopGroup().readEntry("Name");
00831             if (file.endsWith("Directory.desktop")) {
00832                 key.prepend('0');
00833             } else if (file.endsWith("TextFile.desktop")) {
00834                 key.prepend('1');
00835             } else {
00836                 key.prepend('2');
00837             }
00838             slist.insert(key, e);
00839         }
00840     }
00841     (*s->templatesList) += slist.values();
00842 }
00843 
00844 void KNewFileMenuPrivate::_k_slotOtherDesktopFile()
00845 {
00846     executeStrategy();
00847 }
00848 
00849 void KNewFileMenuPrivate::_k_slotRealFileOrDir()
00850 {
00851     m_strategy.m_chosenFileName = m_text;
00852     _k_slotAbortDialog();
00853     executeStrategy();
00854 }
00855 
00856 void KNewFileMenuPrivate::_k_slotSymLink()
00857 {
00858     KNameAndUrlInputDialog* dlg = static_cast<KNameAndUrlInputDialog*>(m_fileDialog);
00859     
00860     m_strategy.m_chosenFileName = dlg->name(); // no path
00861     KUrl linkUrl = dlg->url(); // the url to put in the file
00862     
00863     if (m_strategy.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
00864         return;
00865     
00866     if (linkUrl.isRelative())
00867         m_strategy.m_src = linkUrl.url();
00868     else if (linkUrl.isLocalFile())
00869         m_strategy.m_src = linkUrl.toLocalFile();
00870     else {
00871     KDialog* dialog = new KDialog(m_parentWidget);
00872     dialog->setCaption( i18n("Sorry") );
00873     dialog->setButtons( KDialog::Ok );
00874     dialog->setObjectName( "sorry" );
00875     dialog->setModal(m_modal);
00876     dialog->setAttribute(Qt::WA_DeleteOnClose);
00877     dialog->setDefaultButton( KDialog::Ok );
00878     dialog->setEscapeButton( KDialog::Ok );
00879     m_fileDialog = dialog;
00880     
00881     KMessageBox::createKMessageBox(dialog, QMessageBox::Warning, 
00882       i18n("Basic links can only point to local files or directories.\nPlease use \"Link to Location\" for remote URLs."), 
00883       QStringList(), QString(), 0, KMessageBox::NoExec,
00884       QString());
00885     
00886     dialog->show();
00887     return;
00888     }
00889     executeStrategy();
00890 }
00891 
00892 void KNewFileMenuPrivate::_k_slotTextChanged(const QString & text)
00893 {
00894     m_text = text;
00895 }
00896 
00897 void KNewFileMenuPrivate::_k_slotUrlDesktopFile()
00898 {
00899     KNameAndUrlInputDialog* dlg = (KNameAndUrlInputDialog*) m_fileDialog;
00900     
00901     m_strategy.m_chosenFileName = dlg->name(); // no path
00902     KUrl linkUrl = dlg->url();
00903 
00904     // Filter user input so that short uri entries, e.g. www.kde.org, are
00905     // handled properly. This not only makes the icon detection below work
00906     // properly, but opening the URL link where the short uri will not be
00907     // sent to the application (opening such link Konqueror fails).
00908     KUriFilterData uriData;
00909     uriData.setData(linkUrl); // the url to put in the file
00910     uriData.setCheckForExecutables(false);
00911 
00912     if (KUriFilter::self()->filterUri(uriData, QStringList() << QLatin1String("kshorturifilter"))) {
00913         linkUrl = uriData.uri();
00914     }
00915 
00916     if (m_strategy.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
00917         return;
00918 
00919     // It's a "URL" desktop file; we need to make a temp copy of it, to modify it
00920     // before copying it to the final destination [which could be a remote protocol]
00921     KTemporaryFile tmpFile;
00922     tmpFile.setAutoRemove(false); // done below
00923     if (!tmpFile.open()) {
00924         kError() << "Couldn't create temp file!";
00925         return;
00926     }
00927 
00928     if (!checkSourceExists(m_strategy.m_templatePath)) {
00929         return;
00930     }
00931 
00932     // First copy the template into the temp file
00933     QFile file(m_strategy.m_templatePath);
00934     if (!file.open(QIODevice::ReadOnly)) {
00935         kError() << "Couldn't open template" << m_strategy.m_templatePath;
00936         return;
00937     }
00938     const QByteArray data = file.readAll();
00939     tmpFile.write(data);
00940     const QString tempFileName = tmpFile.fileName();
00941     Q_ASSERT(!tempFileName.isEmpty());
00942     tmpFile.close();
00943     file.close();
00944 
00945     KDesktopFile df(tempFileName);
00946     KConfigGroup group = df.desktopGroup();
00947     group.writeEntry("Icon", KProtocolInfo::icon(linkUrl.protocol()));
00948     group.writePathEntry("URL", linkUrl.prettyUrl());
00949     df.sync();
00950 
00951     m_strategy.m_src = tempFileName;
00952     m_strategy.m_tempFileToDelete = tempFileName;
00953 
00954     executeStrategy();
00955 }
00956 
00957 KNewFileMenu::KNewFileMenu(KActionCollection* collection, const QString& name, QObject* parent)
00958     : KActionMenu(KIcon("document-new"), i18n("Create New"), parent),
00959       d(new KNewFileMenuPrivate(this))
00960 {
00961     // Don't fill the menu yet
00962     // We'll do that in checkUpToDate (should be connected to aboutToShow)
00963     d->m_newMenuGroup = new QActionGroup(this);
00964     connect(d->m_newMenuGroup, SIGNAL(triggered(QAction*)), this, SLOT(_k_slotActionTriggered(QAction*)));
00965     d->m_actionCollection = collection;
00966     d->m_parentWidget = qobject_cast<QWidget*>(parent);
00967     d->m_newDirAction = 0;
00968 
00969     d->m_actionCollection->addAction(name, this);
00970 
00971     d->m_menuDev = new KActionMenu(KIcon("drive-removable-media"), i18n("Link to Device"), this);
00972 }
00973 
00974 KNewFileMenu::~KNewFileMenu()
00975 {
00976     //kDebug(1203) << this;
00977     delete d;
00978 }
00979 
00980 void KNewFileMenu::checkUpToDate()
00981 {
00982     KNewFileMenuSingleton* s = kNewMenuGlobals;
00983     //kDebug(1203) << this << "m_menuItemsVersion=" << d->m_menuItemsVersion
00984     //              << "s->templatesVersion=" << s->templatesVersion;
00985     if (d->m_menuItemsVersion < s->templatesVersion || s->templatesVersion == 0) {
00986         //kDebug(1203) << "recreating actions";
00987         // We need to clean up the action collection
00988         // We look for our actions using the group
00989         foreach (QAction* action, d->m_newMenuGroup->actions())
00990             delete action;
00991 
00992         if (!s->templatesList) { // No templates list up to now
00993             s->templatesList = new KNewFileMenuSingleton::EntryList;
00994             d->_k_slotFillTemplates();
00995             s->parseFiles();
00996         }
00997 
00998         // This might have been already done for other popupmenus,
00999         // that's the point in s->filesParsed.
01000         if (!s->filesParsed) {
01001             s->parseFiles();
01002         }
01003 
01004         d->fillMenu();
01005 
01006         d->m_menuItemsVersion = s->templatesVersion;
01007     }
01008 }
01009 
01010 void KNewFileMenu::createDirectory()
01011 {
01012     if (d->m_popupFiles.isEmpty())
01013     return;
01014 
01015     KUrl baseUrl = d->m_popupFiles.first();
01016     QString name = d->m_text.isEmpty()? i18nc("Default name for a new folder", "New Folder") : 
01017       d->m_text;
01018       
01019     if (baseUrl.isLocalFile() && QFileInfo(baseUrl.toLocalFile(KUrl::AddTrailingSlash) + name).exists())
01020     name = KIO::RenameDialog::suggestName(baseUrl, name);
01021     
01022     KDialog* fileDialog = new KDialog(d->m_parentWidget);
01023     fileDialog->setModal(isModal());
01024     fileDialog->setAttribute(Qt::WA_DeleteOnClose);
01025     fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
01026     fileDialog->setCaption(i18nc("@title:window", "New Folder"));
01027     
01028     QWidget* mainWidget = new QWidget(fileDialog);
01029     QVBoxLayout *layout = new QVBoxLayout(mainWidget);
01030     QLabel *label = new QLabel(i18n("Create new folder in:\n%1", baseUrl.pathOrUrl()));
01031 
01032     // We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
01033     // It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
01034     // TODO: should probably be investigated and fixed in KLineEdit.
01035     KLineEdit *lineEdit = new KLineEdit;
01036     lineEdit->setClearButtonShown(true);
01037     lineEdit->setText(name);
01038 
01039     d->_k_slotTextChanged(name); // have to save string in d->m_text in case user does not touch dialog
01040     connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(_k_slotTextChanged(const QString &)));
01041     layout->addWidget(label);
01042     layout->addWidget(lineEdit);
01043     
01044     fileDialog->setMainWidget(mainWidget);
01045     connect(fileDialog, SIGNAL(accepted()), this, SLOT(_k_slotCreateDirectory()));
01046     connect(fileDialog, SIGNAL(rejected()), this, SLOT(_k_slotAbortDialog()));    
01047     
01048     d->m_fileDialog = fileDialog;
01049  
01050     fileDialog->show();
01051     lineEdit->selectAll();
01052     lineEdit->setFocus();
01053 }
01054 
01055 bool KNewFileMenu::isModal() const
01056 {
01057     return d->m_modal;
01058 }
01059 
01060 KUrl::List KNewFileMenu::popupFiles() const
01061 {
01062     return d->m_popupFiles;
01063 }
01064 
01065 void KNewFileMenu::setModal(bool modal)
01066 {
01067     d->m_modal = modal;
01068 }
01069 
01070 void KNewFileMenu::setPopupFiles(const KUrl::List& files)
01071 {
01072     d->m_popupFiles = files;
01073     if (files.isEmpty()) {
01074         d->m_newMenuGroup->setEnabled(false);
01075     } else {
01076         KUrl firstUrl = files.first();
01077         if (KProtocolManager::supportsWriting(firstUrl)) {
01078             d->m_newMenuGroup->setEnabled(true);
01079             if (d->m_newDirAction) {
01080                 d->m_newDirAction->setEnabled(KProtocolManager::supportsMakeDir(firstUrl)); // e.g. trash:/
01081             }
01082         } else {
01083             d->m_newMenuGroup->setEnabled(true);
01084         }
01085     }
01086 }
01087 
01088 
01089 void KNewFileMenu::setParentWidget(QWidget* parentWidget)
01090 {
01091     d->m_parentWidget = parentWidget;
01092 }
01093 
01094 void KNewFileMenu::setSupportedMimeTypes(const QStringList& mime)
01095 {
01096     d->m_supportedMimeTypes = mime;
01097 }
01098 
01099 void KNewFileMenu::setViewShowsHiddenFiles(bool b)
01100 {
01101     d->m_viewShowsHiddenFiles = b;
01102 }
01103 
01104 void KNewFileMenu::slotResult(KJob * job)
01105 {
01106     if (job->error()) {
01107         static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
01108     } else {
01109         // Was this a copy or a mkdir?
01110         KIO::CopyJob* copyJob = ::qobject_cast<KIO::CopyJob*>(job);
01111         if (copyJob) {
01112             const KUrl destUrl = copyJob->destUrl();
01113             const KUrl localUrl = KIO::NetAccess::mostLocalUrl(destUrl, d->m_parentWidget);
01114             if (localUrl.isLocalFile()) {
01115                 // Normal (local) file. Need to "touch" it, kio_file copied the mtime.
01116                 (void) ::utime(QFile::encodeName(localUrl.toLocalFile()), 0);
01117             }
01118             emit fileCreated(destUrl);
01119         } else if (KIO::SimpleJob* simpleJob = ::qobject_cast<KIO::SimpleJob*>(job)) {
01120             // Can be mkdir or symlink
01121             if (simpleJob->property("isMkdirJob").toBool() == true) {
01122                 kDebug() << "Emit directoryCreated" << simpleJob->url();
01123                 emit directoryCreated(simpleJob->url());
01124             } else {
01125                 emit fileCreated(simpleJob->url());
01126             }
01127         }
01128     }
01129     if (!d->m_tempFileToDelete.isEmpty())
01130         QFile::remove(d->m_tempFileToDelete);
01131 }
01132 
01133 
01134 QStringList KNewFileMenu::supportedMimeTypes() const
01135 {
01136     return d->m_supportedMimeTypes;
01137 }
01138 
01139 
01140 #include "knewfilemenu.moc"
01141 

KFile

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

kdelibs

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal