• Skip to content
  • Skip to link menu
KDE 4.7 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(), 0, 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       0,
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         // KDE5 TODO: remove the "..." from link*.desktop files and use i18n("%1...") when making
00435         // the action.
00436 
00437         KUrl defaultFile(*it);
00438         defaultFile.addPath(KIO::encodeFileName(text));
00439         if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
00440             text = KIO::RenameDialog::suggestName(*it, text);
00441 
00442         const KUrl templateUrl(entry.templatePath);
00443     
00444     KDialog* dlg = new KPropertiesDialog(templateUrl, *it, text, m_parentWidget);
00445     dlg->setModal(q->isModal());
00446     dlg->setAttribute(Qt::WA_DeleteOnClose);
00447         QObject::connect(dlg, SIGNAL(applied()), q, SLOT(_k_slotOtherDesktopFile()));
00448     dlg->show();
00449     }
00450     // We don't set m_src here -> there will be no copy, we are done.
00451 }
00452 
00453 void KNewFileMenuPrivate::executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry)
00454 {
00455     // The template is not a desktop file
00456     // Show the small dialog for getting the destination filename
00457     QString text = entry.text;
00458     text.remove("..."); // the ... is fine for the menu item but not for the default filename
00459     text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
00460     m_strategy.m_src = entry.templatePath;
00461 
00462     KUrl defaultFile(m_popupFiles.first());
00463     defaultFile.addPath(KIO::encodeFileName(text));
00464     if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
00465         text = KIO::RenameDialog::suggestName(m_popupFiles.first(), text);
00466     
00467     KDialog* fileDialog = new KDialog(m_parentWidget);
00468     fileDialog->setAttribute(Qt::WA_DeleteOnClose);
00469     fileDialog->setModal(q->isModal());
00470     fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
00471     
00472     QWidget* mainWidget = new QWidget(fileDialog);
00473     QVBoxLayout *layout = new QVBoxLayout(mainWidget);
00474     QLabel *label = new QLabel(entry.comment);
00475 
00476     // We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
00477     // It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
00478     // TODO: should probably be investigated and fixed in KLineEdit.
00479     KLineEdit *lineEdit = new KLineEdit;
00480     lineEdit->setClearButtonShown(true);
00481     lineEdit->setText(text);
00482 
00483     _k_slotTextChanged(text);
00484     QObject::connect(lineEdit, SIGNAL(textChanged(const QString &)), q, SLOT(_k_slotTextChanged(const QString &)));
00485     
00486     layout->addWidget(label);
00487     layout->addWidget(lineEdit);
00488     
00489     fileDialog->setMainWidget(mainWidget);
00490     QObject::connect(fileDialog, SIGNAL(accepted()), q, SLOT(_k_slotRealFileOrDir()));
00491     QObject::connect(fileDialog, SIGNAL(rejected()), q, SLOT(_k_slotAbortDialog()));
00492  
00493     fileDialog->show();
00494     lineEdit->selectAll();
00495     lineEdit->setFocus();
00496 }
00497 
00498 void KNewFileMenuPrivate::executeSymLink(const KNewFileMenuSingleton::Entry& entry)
00499 {
00500     KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);    
00501     dlg->setModal(q->isModal());
00502     dlg->setAttribute(Qt::WA_DeleteOnClose);
00503     dlg->setCaption(i18n("Create Symlink"));
00504     m_fileDialog = dlg;
00505     QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotSymLink()));
00506     dlg->show();    
00507 }
00508 
00509 void KNewFileMenuPrivate::executeStrategy()
00510 {
00511     m_tempFileToDelete = m_strategy.tempFileToDelete();
00512     const QString src = m_strategy.sourceFileToCopy();
00513     QString chosenFileName = expandTilde(m_strategy.chosenFileName(), true);
00514 
00515     if (src.isEmpty())
00516         return;
00517     KUrl uSrc(src);
00518 
00519     if (uSrc.isLocalFile()) {
00520         // In case the templates/.source directory contains symlinks, resolve
00521         // them to the target files. Fixes bug #149628.
00522         KFileItem item(uSrc, QString(), KFileItem::Unknown);
00523         if (item.isLink())
00524             uSrc.setPath(item.linkDest());
00525     }
00526 
00527     // The template is not a desktop file [or it's a URL one]
00528     // Copy it.
00529     KUrl::List::const_iterator it = m_popupFiles.constBegin();
00530     for (; it != m_popupFiles.constEnd(); ++it)
00531     {
00532         KUrl dest(*it);
00533         dest.addPath(KIO::encodeFileName(chosenFileName));
00534 
00535         KUrl::List lstSrc;
00536         lstSrc.append(uSrc);
00537         KIO::Job* kjob;
00538         if (m_strategy.m_isSymlink) {
00539             kjob = KIO::symlink(src, dest);
00540             // This doesn't work, FileUndoManager registers new links in copyingLinkDone,
00541             // which KIO::symlink obviously doesn't emit... Needs code in FileUndoManager.
00542             //KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Link, lstSrc, dest, kjob);
00543         } else {
00544             //kDebug(1203) << "KIO::copyAs(" << uSrc.url() << "," << dest.url() << ")";
00545             KIO::CopyJob * job = KIO::copyAs(uSrc, dest);
00546             job->setDefaultPermissions(true);
00547             kjob = job;
00548             KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Copy, lstSrc, dest, job);
00549         }
00550         kjob->ui()->setWindow(m_parentWidget);
00551         QObject::connect(kjob, SIGNAL(result(KJob*)), q, SLOT(slotResult(KJob*)));
00552     }
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(); // the url to put in the file
00903     
00904     if (m_strategy.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
00905         return;
00906 
00907     // It's a "URL" desktop file; we need to make a temp copy of it, to modify it
00908     // before copying it to the final destination [which could be a remote protocol]
00909     KTemporaryFile tmpFile;
00910     tmpFile.setAutoRemove(false); // done below
00911     if (!tmpFile.open()) {
00912         kError() << "Couldn't create temp file!";
00913         return;
00914     }
00915 
00916     if (!checkSourceExists(m_strategy.m_templatePath)) {
00917         return;
00918     }
00919 
00920     // First copy the template into the temp file
00921     QFile file(m_strategy.m_templatePath);
00922     if (!file.open(QIODevice::ReadOnly)) {
00923         kError() << "Couldn't open template" << m_strategy.m_templatePath;
00924         return;
00925     }
00926     const QByteArray data = file.readAll();
00927     tmpFile.write(data);
00928     const QString tempFileName = tmpFile.fileName();
00929     Q_ASSERT(!tempFileName.isEmpty());
00930     tmpFile.close();
00931     file.close();
00932 
00933     KDesktopFile df(tempFileName);
00934     KConfigGroup group = df.desktopGroup();
00935     group.writeEntry("Icon", KProtocolInfo::icon(linkUrl.protocol()));
00936     group.writePathEntry("URL", linkUrl.prettyUrl());
00937     df.sync();
00938     
00939     m_strategy.m_src = tempFileName;
00940     m_strategy.m_tempFileToDelete = tempFileName;
00941     
00942     executeStrategy();
00943 }
00944 
00945 
00946 KNewFileMenu::KNewFileMenu(KActionCollection* collection, const QString& name, QObject* parent)
00947     : KActionMenu(KIcon("document-new"), i18n("Create New"), parent),
00948       d(new KNewFileMenuPrivate(this))
00949 {
00950     // Don't fill the menu yet
00951     // We'll do that in checkUpToDate (should be connected to aboutToShow)
00952     d->m_newMenuGroup = new QActionGroup(this);
00953     connect(d->m_newMenuGroup, SIGNAL(triggered(QAction*)), this, SLOT(_k_slotActionTriggered(QAction*)));
00954     d->m_actionCollection = collection;
00955     d->m_parentWidget = qobject_cast<QWidget*>(parent);
00956     d->m_newDirAction = 0;
00957 
00958     d->m_actionCollection->addAction(name, this);
00959 
00960     d->m_menuDev = new KActionMenu(KIcon("drive-removable-media"), i18n("Link to Device"), this);
00961 }
00962 
00963 KNewFileMenu::~KNewFileMenu()
00964 {
00965     //kDebug(1203) << this;
00966     delete d;
00967 }
00968 
00969 void KNewFileMenu::checkUpToDate()
00970 {
00971     KNewFileMenuSingleton* s = kNewMenuGlobals;
00972     //kDebug(1203) << this << "m_menuItemsVersion=" << d->m_menuItemsVersion
00973     //              << "s->templatesVersion=" << s->templatesVersion;
00974     if (d->m_menuItemsVersion < s->templatesVersion || s->templatesVersion == 0) {
00975         //kDebug(1203) << "recreating actions";
00976         // We need to clean up the action collection
00977         // We look for our actions using the group
00978         foreach (QAction* action, d->m_newMenuGroup->actions())
00979             delete action;
00980 
00981         if (!s->templatesList) { // No templates list up to now
00982             s->templatesList = new KNewFileMenuSingleton::EntryList;
00983             d->_k_slotFillTemplates();
00984             s->parseFiles();
00985         }
00986 
00987         // This might have been already done for other popupmenus,
00988         // that's the point in s->filesParsed.
00989         if (!s->filesParsed) {
00990             s->parseFiles();
00991         }
00992 
00993         d->fillMenu();
00994 
00995         d->m_menuItemsVersion = s->templatesVersion;
00996     }
00997 }
00998 
00999 void KNewFileMenu::createDirectory()
01000 {
01001     if (d->m_popupFiles.isEmpty())
01002     return;
01003 
01004     KUrl baseUrl = d->m_popupFiles.first();
01005     QString name = d->m_text.isEmpty()? i18nc("Default name for a new folder", "New Folder") : 
01006       d->m_text;
01007       
01008     if (baseUrl.isLocalFile() && QFileInfo(baseUrl.toLocalFile(KUrl::AddTrailingSlash) + name).exists())
01009     name = KIO::RenameDialog::suggestName(baseUrl, name);
01010     
01011     KDialog* fileDialog = new KDialog(d->m_parentWidget);
01012     fileDialog->setModal(isModal());
01013     fileDialog->setAttribute(Qt::WA_DeleteOnClose);
01014     fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
01015     fileDialog->setCaption(i18nc("@title:window", "New Folder"));
01016     
01017     QWidget* mainWidget = new QWidget(fileDialog);
01018     QVBoxLayout *layout = new QVBoxLayout(mainWidget);
01019     QLabel *label = new QLabel(i18n("Create new folder in:\n%1", baseUrl.pathOrUrl()));
01020 
01021     // We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
01022     // It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
01023     // TODO: should probably be investigated and fixed in KLineEdit.
01024     KLineEdit *lineEdit = new KLineEdit;
01025     lineEdit->setClearButtonShown(true);
01026     lineEdit->setText(name);
01027 
01028     d->_k_slotTextChanged(name); // have to save string in d->m_text in case user does not touch dialog
01029     connect(lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(_k_slotTextChanged(const QString &)));
01030     layout->addWidget(label);
01031     layout->addWidget(lineEdit);
01032     
01033     fileDialog->setMainWidget(mainWidget);
01034     connect(fileDialog, SIGNAL(accepted()), this, SLOT(_k_slotCreateDirectory()));
01035     connect(fileDialog, SIGNAL(rejected()), this, SLOT(_k_slotAbortDialog()));    
01036     
01037     d->m_fileDialog = fileDialog;
01038  
01039     fileDialog->show();
01040     lineEdit->selectAll();
01041     lineEdit->setFocus();
01042 }
01043 
01044 bool KNewFileMenu::isModal() const
01045 {
01046     return d->m_modal;
01047 }
01048 
01049 KUrl::List KNewFileMenu::popupFiles() const
01050 {
01051     return d->m_popupFiles;
01052 }
01053 
01054 void KNewFileMenu::setModal(bool modal)
01055 {
01056     d->m_modal = modal;
01057 }
01058 
01059 void KNewFileMenu::setPopupFiles(const KUrl::List& files)
01060 {
01061     d->m_popupFiles = files;
01062     if (files.isEmpty()) {
01063         d->m_newMenuGroup->setEnabled(false);
01064     } else {
01065         KUrl firstUrl = files.first();
01066         if (KProtocolManager::supportsWriting(firstUrl)) {
01067             d->m_newMenuGroup->setEnabled(true);
01068             if (d->m_newDirAction) {
01069                 d->m_newDirAction->setEnabled(KProtocolManager::supportsMakeDir(firstUrl)); // e.g. trash:/
01070             }
01071         } else {
01072             d->m_newMenuGroup->setEnabled(true);
01073         }
01074     }
01075 }
01076 
01077 
01078 void KNewFileMenu::setParentWidget(QWidget* parentWidget)
01079 {
01080     d->m_parentWidget = parentWidget;
01081 }
01082 
01083 void KNewFileMenu::setSupportedMimeTypes(const QStringList& mime)
01084 {
01085     d->m_supportedMimeTypes = mime;
01086 }
01087 
01088 void KNewFileMenu::setViewShowsHiddenFiles(bool b)
01089 {
01090     d->m_viewShowsHiddenFiles = b;
01091 }
01092 
01093 void KNewFileMenu::slotResult(KJob * job)
01094 {
01095     if (job->error()) {
01096         static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
01097     } else {
01098         // Was this a copy or a mkdir?
01099         KIO::CopyJob* copyJob = ::qobject_cast<KIO::CopyJob*>(job);
01100         if (copyJob) {
01101             const KUrl destUrl = copyJob->destUrl();
01102             const KUrl localUrl = KIO::NetAccess::mostLocalUrl(destUrl, d->m_parentWidget);
01103             if (localUrl.isLocalFile()) {
01104                 // Normal (local) file. Need to "touch" it, kio_file copied the mtime.
01105                 (void) ::utime(QFile::encodeName(localUrl.toLocalFile()), 0);
01106             }
01107             emit fileCreated(destUrl);
01108         } else if (KIO::SimpleJob* simpleJob = ::qobject_cast<KIO::SimpleJob*>(job)) {
01109             // Can be mkdir or symlink
01110             if (simpleJob->property("isMkdirJob").toBool() == true) {
01111                 kDebug() << "Emit directoryCreated" << simpleJob->url();
01112                 emit directoryCreated(simpleJob->url());
01113             } else {
01114                 emit fileCreated(simpleJob->url());
01115             }
01116         }
01117     }
01118     if (!d->m_tempFileToDelete.isEmpty())
01119         QFile::remove(d->m_tempFileToDelete);
01120 }
01121 
01122 
01123 QStringList KNewFileMenu::supportedMimeTypes() const
01124 {
01125     return d->m_supportedMimeTypes;
01126 }
01127 
01128 
01129 #include "knewfilemenu.moc"
01130 

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.5
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