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

KFile

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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.3
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