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

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