Page MenuHomePhorge

No OneTemporary

diff --git a/kfile/knewfilemenu.cpp b/kfile/knewfilemenu.cpp
index 0a8ecfc3d3..f5b409dee0 100644
--- a/kfile/knewfilemenu.cpp
+++ b/kfile/knewfilemenu.cpp
@@ -1,1147 +1,1153 @@
/* This file is part of the KDE project
Copyright (C) 1998-2009 David Faure <faure@kde.org>
2003 Sven Leiber <s.leiber@web.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 or at your option version 3.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "knewfilemenu.h"
#include "knameandurlinputdialog.h"
#include <QDir>
#include <QVBoxLayout>
#include <QList>
#include <QLabel>
#include <kactioncollection.h>
#include <kdebug.h>
#include <kdesktopfile.h>
#include <kdirwatch.h>
#include <kicon.h>
#include <kcomponentdata.h>
#include <kinputdialog.h>
#include <kdialog.h>
#include <klocale.h>
#include <klineedit.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kprotocolinfo.h>
#include <kprotocolmanager.h>
#include <kmenu.h>
#include <krun.h>
#include <kshell.h>
#include <kio/job.h>
#include <kio/copyjob.h>
#include <kio/jobuidelegate.h>
#include <kio/renamedialog.h>
#include <kio/netaccess.h>
#include <kio/fileundomanager.h>
#include <kio/kurifilter.h>
#include <kpropertiesdialog.h>
#include <ktemporaryfile.h>
#include <utime.h>
static QString expandTilde(const QString& name, bool isfile = false)
{
if (!name.isEmpty() && (!isfile || name[0] == '\\'))
{
const QString expandedName = KShell::tildeExpand(name);
// When a tilde mark cannot be properly expanded, the above call
// returns an empty string...
if (!expandedName.isEmpty())
return expandedName;
}
return name;
}
// Singleton, with data shared by all KNewFileMenu instances
class KNewFileMenuSingleton
{
public:
KNewFileMenuSingleton()
: dirWatch(0),
filesParsed(false),
templatesList(0),
templatesVersion(0)
{
}
~KNewFileMenuSingleton()
{
delete dirWatch;
delete templatesList;
}
/**
* Opens the desktop files and completes the Entry list
* Input: the entry list. Output: the entry list ;-)
*/
void parseFiles();
/**
* For entryType
* LINKTOTEMPLATE: a desktop file that points to a file or dir to copy
* TEMPLATE: a real file to copy as is (the KDE-1.x solution)
* SEPARATOR: to put a separator in the menu
* 0 means: not parsed, i.e. we don't know
*/
enum EntryType { Unknown, LinkToTemplate = 1, Template, Separator };
KDirWatch * dirWatch;
struct Entry {
QString text;
QString filePath; // empty for Separator
QString templatePath; // same as filePath for Template
QString icon;
EntryType entryType;
QString comment;
QString mimeType;
};
// NOTE: only filePath is known before we call parseFiles
/**
* List of all template files. It is important that they are in
* the same order as the 'New' menu.
*/
typedef QList<Entry> EntryList;
/**
* Set back to false each time new templates are found,
* and to true on the first call to parseFiles
*/
bool filesParsed;
EntryList * templatesList;
/**
* Is increased when templatesList has been updated and
* menu needs to be re-filled. Menus have their own version and compare it
* to templatesVersion before showing up
*/
int templatesVersion;
};
void KNewFileMenuSingleton::parseFiles()
{
//kDebug(1203);
filesParsed = true;
KNewFileMenuSingleton::EntryList::iterator templ = templatesList->begin();
const KNewFileMenuSingleton::EntryList::iterator templ_end = templatesList->end();
for (; templ != templ_end; ++templ)
{
QString iconname;
QString filePath = (*templ).filePath;
if (!filePath.isEmpty())
{
QString text;
QString templatePath;
// If a desktop file, then read the name from it.
// Otherwise (or if no name in it?) use file name
if (KDesktopFile::isDesktopFile(filePath)) {
KDesktopFile desktopFile( filePath);
text = desktopFile.readName();
(*templ).icon = desktopFile.readIcon();
(*templ).comment = desktopFile.readComment();
QString type = desktopFile.readType();
if (type == "Link")
{
templatePath = desktopFile.desktopGroup().readPathEntry("URL", QString());
if (templatePath[0] != '/' && !templatePath.startsWith("__"))
{
if (templatePath.startsWith("file:/"))
templatePath = KUrl(templatePath).toLocalFile();
else
{
// A relative path, then (that's the default in the files we ship)
QString linkDir = filePath.left(filePath.lastIndexOf('/') + 1 /*keep / */);
//kDebug(1203) << "linkDir=" << linkDir;
templatePath = linkDir + templatePath;
}
}
}
if (templatePath.isEmpty())
{
// No URL key, this is an old-style template
(*templ).entryType = KNewFileMenuSingleton::Template;
(*templ).templatePath = (*templ).filePath; // we'll copy the file
} else {
(*templ).entryType = KNewFileMenuSingleton::LinkToTemplate;
(*templ).templatePath = templatePath;
}
}
if (text.isEmpty())
{
text = KUrl(filePath).fileName();
if (text.endsWith(".desktop"))
text.truncate(text.length() - 8);
}
(*templ).text = text;
/*kDebug(1203) << "Updating entry with text=" << text
<< "entryType=" << (*templ).entryType
<< "templatePath=" << (*templ).templatePath;*/
}
else {
(*templ).entryType = KNewFileMenuSingleton::Separator;
}
}
}
K_GLOBAL_STATIC(KNewFileMenuSingleton, kNewMenuGlobals)
class KNewFileMenuStrategy
{
friend class KNewFileMenuPrivate;
public:
KNewFileMenuStrategy() { m_isSymlink = false;}
~KNewFileMenuStrategy() {}
QString chosenFileName() const { return m_chosenFileName; }
// If empty, no copy is performed.
QString sourceFileToCopy() const { return m_src; }
QString tempFileToDelete() const { return m_tempFileToDelete; }
bool m_isSymlink;
protected:
QString m_chosenFileName;
QString m_src;
QString m_tempFileToDelete;
QString m_templatePath;
};
class KNewFileMenuPrivate
{
public:
KNewFileMenuPrivate(KNewFileMenu* qq)
: m_menuItemsVersion(0),
m_modal(true),
m_viewShowsHiddenFiles(false),
q(qq)
{}
bool checkSourceExists(const QString& src);
/**
* Asks user whether to create a hidden directory with a dialog
*/
void confirmCreatingHiddenDir(const QString& name);
/**
* The strategy used for other desktop files than Type=Link. Example: Application, Device.
*/
void executeOtherDesktopFile(const KNewFileMenuSingleton::Entry& entry);
/**
* The strategy used for "real files or directories" (the common case)
*/
void executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry);
/**
* Actually performs file handling. Reads in m_strategy for needed data, that has been collected by execute*() before
*/
void executeStrategy();
/**
* The strategy used when creating a symlink
*/
void executeSymLink(const KNewFileMenuSingleton::Entry& entry);
/**
* The strategy used for "url" desktop files
*/
void executeUrlDesktopFile(const KNewFileMenuSingleton::Entry& entry);
/**
* Fills the menu from the templates list.
*/
void fillMenu();
/**
* Just clears the string buffer d->m_text, but I need a slot for this to occur
*/
void _k_slotAbortDialog();
/**
* Called when New->* is clicked
*/
void _k_slotActionTriggered(QAction* action);
/**
* Callback function that reads in directory name from dialog and processes it
*/
void _k_slotCreateDirectory(bool writeHiddenDir = false);
/**
* Callback function that reads in directory name from dialog and processes it. This will wirte
* a hidden directory without further questions
*/
void _k_slotCreateHiddenDirectory();
/**
* Fills the templates list.
*/
void _k_slotFillTemplates();
/**
* Callback in KNewFileMenu for the OtherDesktopFile Dialog. Handles dialog input and gives over
* to exectueStrategy()
*/
void _k_slotOtherDesktopFile();
/**
* Callback in KNewFileMenu for the RealFile Dialog. Handles dialog input and gives over
* to exectueStrategy()
*/
void _k_slotRealFileOrDir();
/**
* Dialogs use this slot to write the changed string into KNewFile menu when the user
* changes touches them
*/
void _k_slotTextChanged(const QString & text);
/**
* Callback in KNewFileMenu for the Symlink Dialog. Handles dialog input and gives over
* to exectueStrategy()
*/
void _k_slotSymLink();
/**
* Callback in KNewFileMenu for the Url/Desktop Dialog. Handles dialog input and gives over
* to exectueStrategy()
*/
void _k_slotUrlDesktopFile();
KActionCollection * m_actionCollection;
KDialog* m_fileDialog;
KActionMenu *m_menuDev;
int m_menuItemsVersion;
bool m_modal;
QAction* m_newDirAction;
/**
* The action group that our actions belong to
*/
QActionGroup* m_newMenuGroup;
QWidget *m_parentWidget;
/**
* When the user pressed the right mouse button over an URL a popup menu
* is displayed. The URL belonging to this popup menu is stored here.
*/
KUrl::List m_popupFiles;
QStringList m_supportedMimeTypes;
QString m_tempFileToDelete; // set when a tempfile was created for a Type=URL desktop file
QString m_text;
bool m_viewShowsHiddenFiles;
KNewFileMenu* q;
class Strategy;
KNewFileMenuStrategy m_strategy;
};
bool KNewFileMenuPrivate::checkSourceExists(const QString& src)
{
if (!QFile::exists(src)) {
kWarning(1203) << src << "doesn't exist" ;
KDialog* dialog = new KDialog(m_parentWidget);
dialog->setCaption( i18n("Sorry") );
dialog->setButtons( KDialog::Ok );
dialog->setObjectName( "sorry" );
dialog->setModal(q->isModal());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setDefaultButton( KDialog::Ok );
dialog->setEscapeButton( KDialog::Ok );
KMessageBox::createKMessageBox(dialog, QMessageBox::Warning,
i18n("<qt>The template file <b>%1</b> does not exist.</qt>", src),
QStringList(), QString(), 0, KMessageBox::NoExec,
QString());
dialog->show();
return false;
}
return true;
}
void KNewFileMenuPrivate::confirmCreatingHiddenDir(const QString& name)
{
if(!KMessageBox::shouldBeShownContinue("confirm_create_hidden_dir")){
_k_slotCreateHiddenDirectory();
return;
}
KGuiItem continueGuiItem(KStandardGuiItem::cont());
continueGuiItem.setText(i18nc("@action:button", "Create directory"));
KGuiItem cancelGuiItem(KStandardGuiItem::cancel());
cancelGuiItem.setText(i18nc("@action:button", "Enter a different name"));
KDialog* confirmDialog = new KDialog(m_parentWidget);
confirmDialog->setCaption(i18n("Create hidden directory?"));
confirmDialog->setModal(m_modal);
confirmDialog->setAttribute(Qt::WA_DeleteOnClose);
KMessageBox::createKMessageBox(confirmDialog, QMessageBox::Warning,
i18n("The name \"%1\" starts with a dot, so the directory will be hidden by default.", name),
QStringList(),
i18n("Do not ask again"),
0,
KMessageBox::NoExec,
QString());
confirmDialog->setButtonGuiItem(KDialog::Ok, continueGuiItem);
confirmDialog->setButtonGuiItem(KDialog::Cancel, cancelGuiItem);
QObject::connect(confirmDialog, SIGNAL(accepted()), q, SLOT(_k_slotCreateHiddenDirectory()));
QObject::connect(confirmDialog, SIGNAL(rejected()), q, SLOT(createDirectory()));
m_fileDialog = confirmDialog;
confirmDialog->show();
}
void KNewFileMenuPrivate::executeOtherDesktopFile(const KNewFileMenuSingleton::Entry& entry)
{
if (!checkSourceExists(entry.templatePath)) {
return;
}
KUrl::List::const_iterator it = m_popupFiles.constBegin();
for (; it != m_popupFiles.constEnd(); ++it)
{
QString text = entry.text;
text.remove("..."); // the ... is fine for the menu item but not for the default filename
text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
// KDE5 TODO: remove the "..." from link*.desktop files and use i18n("%1...") when making
// the action.
KUrl defaultFile(*it);
defaultFile.addPath(KIO::encodeFileName(text));
if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
text = KIO::RenameDialog::suggestName(*it, text);
const KUrl templateUrl(entry.templatePath);
KDialog* dlg = new KPropertiesDialog(templateUrl, *it, text, m_parentWidget);
dlg->setModal(q->isModal());
dlg->setAttribute(Qt::WA_DeleteOnClose);
QObject::connect(dlg, SIGNAL(applied()), q, SLOT(_k_slotOtherDesktopFile()));
dlg->show();
}
// We don't set m_src here -> there will be no copy, we are done.
}
void KNewFileMenuPrivate::executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry)
{
// The template is not a desktop file
// Show the small dialog for getting the destination filename
QString text = entry.text;
text.remove("..."); // the ... is fine for the menu item but not for the default filename
text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
m_strategy.m_src = entry.templatePath;
KUrl defaultFile(m_popupFiles.first());
defaultFile.addPath(KIO::encodeFileName(text));
if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
text = KIO::RenameDialog::suggestName(m_popupFiles.first(), text);
KDialog* fileDialog = new KDialog(m_parentWidget);
fileDialog->setAttribute(Qt::WA_DeleteOnClose);
fileDialog->setModal(q->isModal());
fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
QWidget* mainWidget = new QWidget(fileDialog);
QVBoxLayout *layout = new QVBoxLayout(mainWidget);
QLabel *label = new QLabel(entry.comment);
// We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
// It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
// TODO: should probably be investigated and fixed in KLineEdit.
KLineEdit *lineEdit = new KLineEdit;
lineEdit->setClearButtonShown(true);
lineEdit->setText(text);
_k_slotTextChanged(text);
QObject::connect(lineEdit, SIGNAL(textChanged(QString)), q, SLOT(_k_slotTextChanged(QString)));
layout->addWidget(label);
layout->addWidget(lineEdit);
fileDialog->setMainWidget(mainWidget);
QObject::connect(fileDialog, SIGNAL(accepted()), q, SLOT(_k_slotRealFileOrDir()));
QObject::connect(fileDialog, SIGNAL(rejected()), q, SLOT(_k_slotAbortDialog()));
fileDialog->show();
lineEdit->selectAll();
lineEdit->setFocus();
}
void KNewFileMenuPrivate::executeSymLink(const KNewFileMenuSingleton::Entry& entry)
{
KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);
dlg->setModal(q->isModal());
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setCaption(i18n("Create Symlink"));
m_fileDialog = dlg;
QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotSymLink()));
dlg->show();
}
void KNewFileMenuPrivate::executeStrategy()
{
m_tempFileToDelete = m_strategy.tempFileToDelete();
const QString src = m_strategy.sourceFileToCopy();
QString chosenFileName = expandTilde(m_strategy.chosenFileName(), true);
- // If the file is not going to be detected as a desktop file, due to a
- // known extension (e.g. ".pl"), append ".desktop". #224142.
- KMimeType::Ptr mime = KMimeType::findByNameAndContent(chosenFileName, "[Desktop Entry]\n");
- if (!mime->is(QLatin1String("application/x-desktop")))
- chosenFileName += QLatin1String(".desktop");
-
if (src.isEmpty())
return;
KUrl uSrc(src);
-
if (uSrc.isLocalFile()) {
// In case the templates/.source directory contains symlinks, resolve
// them to the target files. Fixes bug #149628.
KFileItem item(uSrc, QString(), KFileItem::Unknown);
if (item.isLink())
uSrc.setPath(item.linkDest());
+
+ if (!m_strategy.m_isSymlink) {
+ // If the file is not going to be detected as a desktop file, due to a
+ // known extension (e.g. ".pl"), append ".desktop". #224142.
+ QFile srcFile(uSrc.toLocalFile());
+ if (srcFile.open(QIODevice::ReadOnly)) {
+ KMimeType::Ptr wantedMime = KMimeType::findByUrl(uSrc);
+ KMimeType::Ptr mime = KMimeType::findByNameAndContent(m_strategy.m_chosenFileName, srcFile.read(1024));
+ //kDebug() << "mime=" << mime->name() << "wantedMime=" << wantedMime->name();
+ if (!mime->is(wantedMime->name()))
+ chosenFileName += wantedMime->mainExtension();
+ }
+ }
}
// The template is not a desktop file [or it's a URL one]
// Copy it.
KUrl::List::const_iterator it = m_popupFiles.constBegin();
for (; it != m_popupFiles.constEnd(); ++it)
{
KUrl dest(*it);
dest.addPath(KIO::encodeFileName(chosenFileName));
KUrl::List lstSrc;
lstSrc.append(uSrc);
KIO::Job* kjob;
if (m_strategy.m_isSymlink) {
kjob = KIO::symlink(src, dest);
// This doesn't work, FileUndoManager registers new links in copyingLinkDone,
// which KIO::symlink obviously doesn't emit... Needs code in FileUndoManager.
//KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Link, lstSrc, dest, kjob);
} else {
//kDebug(1203) << "KIO::copyAs(" << uSrc.url() << "," << dest.url() << ")";
KIO::CopyJob * job = KIO::copyAs(uSrc, dest);
job->setDefaultPermissions(true);
kjob = job;
KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Copy, lstSrc, dest, job);
}
kjob->ui()->setWindow(m_parentWidget);
QObject::connect(kjob, SIGNAL(result(KJob*)), q, SLOT(slotResult(KJob*)));
}
}
void KNewFileMenuPrivate::executeUrlDesktopFile(const KNewFileMenuSingleton::Entry& entry)
{
KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);
m_strategy.m_templatePath = entry.templatePath;
dlg->setModal(q->isModal());
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setCaption(i18n("Create link to URL"));
m_fileDialog = dlg;
QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotUrlDesktopFile()));
dlg->show();
}
void KNewFileMenuPrivate::fillMenu()
{
QMenu* menu = q->menu();
menu->clear();
m_menuDev->menu()->clear();
m_newDirAction = 0;
QSet<QString> seenTexts;
// these shall be put at special positions
QAction* linkURL = 0;
QAction* linkApp = 0;
QAction* linkPath = 0;
KNewFileMenuSingleton* s = kNewMenuGlobals;
int i = 1;
KNewFileMenuSingleton::EntryList::iterator templ = s->templatesList->begin();
const KNewFileMenuSingleton::EntryList::iterator templ_end = s->templatesList->end();
for (; templ != templ_end; ++templ, ++i)
{
KNewFileMenuSingleton::Entry& entry = *templ;
if (entry.entryType != KNewFileMenuSingleton::Separator) {
// There might be a .desktop for that one already, if it's a kdelnk
// This assumes we read .desktop files before .kdelnk files ...
// In fact, we skip any second item that has the same text as another one.
// Duplicates in a menu look bad in any case.
const bool bSkip = seenTexts.contains(entry.text);
if (bSkip) {
kDebug(1203) << "skipping" << entry.filePath;
} else {
seenTexts.insert(entry.text);
//const KNewFileMenuSingleton::Entry entry = templatesList->at(i-1);
const QString templatePath = entry.templatePath;
// The best way to identify the "Create Directory", "Link to Location", "Link to Application" was the template
if (templatePath.endsWith("emptydir")) {
QAction * act = new QAction(q);
m_newDirAction = act;
act->setIcon(KIcon(entry.icon));
act->setText(i18nc("@item:inmenu Create New", "%1", entry.text));
act->setActionGroup(m_newMenuGroup);
menu->addAction(act);
QAction *sep = new QAction(q);
sep->setSeparator(true);
menu->addAction(sep);
} else {
if (!m_supportedMimeTypes.isEmpty()) {
bool keep = false;
// We need to do mimetype filtering, for real files.
const bool createSymlink = entry.templatePath == "__CREATE_SYMLINK__";
if (createSymlink) {
keep = true;
} else if (!KDesktopFile::isDesktopFile(entry.templatePath)) {
// Determine mimetype on demand
KMimeType::Ptr mime;
if (entry.mimeType.isEmpty()) {
mime = KMimeType::findByPath(entry.templatePath);
if (mime) {
//kDebug() << entry.templatePath << "is" << mime->name();
entry.mimeType = mime->name();
} else {
entry.mimeType = KMimeType::defaultMimeType();
}
} else {
mime = KMimeType::mimeType(entry.mimeType);
}
Q_FOREACH(const QString& supportedMime, m_supportedMimeTypes) {
if (mime && mime->is(supportedMime)) {
keep = true;
break;
}
}
}
if (!keep) {
//kDebug() << "Not keeping" << entry.templatePath;
continue;
}
}
QAction * act = new QAction(q);
act->setData(i);
act->setIcon(KIcon(entry.icon));
act->setText(i18nc("@item:inmenu Create New", "%1", entry.text));
act->setActionGroup(m_newMenuGroup);
//kDebug() << templatePath << entry.filePath;
if (templatePath.endsWith("/URL.desktop")) {
linkURL = act;
} else if (templatePath.endsWith("/Program.desktop")) {
linkApp = act;
} else if (entry.filePath.endsWith("/linkPath.desktop")) {
linkPath = act;
} else if (KDesktopFile::isDesktopFile(templatePath)) {
KDesktopFile df(templatePath);
if (df.readType() == "FSDevice")
m_menuDev->menu()->addAction(act);
else
menu->addAction(act);
}
else
{
menu->addAction(act);
}
}
}
} else { // Separate system from personal templates
Q_ASSERT(entry.entryType != 0);
QAction *sep = new QAction(q);
sep->setSeparator(true);
menu->addAction(sep);
}
}
if (m_supportedMimeTypes.isEmpty()) {
QAction *sep = new QAction(q);
sep->setSeparator(true);
menu->addAction(sep);
if (linkURL) menu->addAction(linkURL);
if (linkPath) menu->addAction(linkPath);
if (linkApp) menu->addAction(linkApp);
Q_ASSERT(m_menuDev);
menu->addAction(m_menuDev);
}
}
void KNewFileMenuPrivate::_k_slotAbortDialog()
{
m_text = QString();
}
void KNewFileMenuPrivate::_k_slotActionTriggered(QAction* action)
{
q->trigger(); // was for kdesktop's slotNewMenuActivated() in kde3 times. Can't hurt to keep it...
if (action == m_newDirAction) {
q->createDirectory();
return;
}
const int id = action->data().toInt();
Q_ASSERT(id > 0);
KNewFileMenuSingleton* s = kNewMenuGlobals;
const KNewFileMenuSingleton::Entry entry = s->templatesList->at(id - 1);
const bool createSymlink = entry.templatePath == "__CREATE_SYMLINK__";
m_strategy = KNewFileMenuStrategy();
if (createSymlink) {
m_strategy.m_isSymlink = true;
executeSymLink(entry);
}
else if (KDesktopFile::isDesktopFile(entry.templatePath)) {
KDesktopFile df(entry.templatePath);
if (df.readType() == "Link") {
executeUrlDesktopFile(entry);
} else { // any other desktop file (Device, App, etc.)
executeOtherDesktopFile(entry);
}
}
else {
executeRealFileOrDir(entry);
}
}
void KNewFileMenuPrivate::_k_slotCreateDirectory(bool writeHiddenDir)
{
KUrl url;
KUrl baseUrl = m_popupFiles.first();
bool askAgain = false;
QString name = expandTilde(m_text);
if (!name.isEmpty()) {
if ((name[0] == '/'))
url.setPath(name);
else {
if (!m_viewShowsHiddenFiles && name.startsWith('.')) {
if (!writeHiddenDir) {
confirmCreatingHiddenDir(name);
return;
}
}
name = KIO::encodeFileName( name );
url = baseUrl;
url.addPath( name );
}
}
if (!askAgain) {
KIO::SimpleJob * job = KIO::mkdir(url);
job->setProperty("isMkdirJob", true); // KDE5: cast to MkdirJob in slotResult instead
job->ui()->setWindow(m_parentWidget);
job->ui()->setAutoErrorHandlingEnabled(true);
KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Mkdir, KUrl(), url, job );
if (job) {
// We want the error handling to be done by slotResult so that subclasses can reimplement it
job->ui()->setAutoErrorHandlingEnabled(false);
QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(slotResult(KJob*)));
}
}
else {
q->createDirectory(); // ask again for the name
}
_k_slotAbortDialog();
}
void KNewFileMenuPrivate::_k_slotCreateHiddenDirectory()
{
_k_slotCreateDirectory(true);
}
void KNewFileMenuPrivate::_k_slotFillTemplates()
{
KNewFileMenuSingleton* s = kNewMenuGlobals;
//kDebug(1203);
// Ensure any changes in the templates dir will call this
if (! s->dirWatch) {
s->dirWatch = new KDirWatch;
const QStringList dirs = m_actionCollection->componentData().dirs()->resourceDirs("templates");
for (QStringList::const_iterator it = dirs.constBegin() ; it != dirs.constEnd() ; ++it) {
//kDebug(1203) << "Templates resource dir:" << *it;
s->dirWatch->addDir(*it);
}
QObject::connect(s->dirWatch, SIGNAL(dirty(QString)),
q, SLOT(_k_slotFillTemplates()));
QObject::connect(s->dirWatch, SIGNAL(created(QString)),
q, SLOT(_k_slotFillTemplates()));
QObject::connect(s->dirWatch, SIGNAL(deleted(QString)),
q, SLOT(_k_slotFillTemplates()));
// Ok, this doesn't cope with new dirs in KDEDIRS, but that's another story
}
++s->templatesVersion;
s->filesParsed = false;
s->templatesList->clear();
// Look into "templates" dirs.
const QStringList files = m_actionCollection->componentData().dirs()->findAllResources("templates");
QMap<QString, KNewFileMenuSingleton::Entry> slist; // used for sorting
Q_FOREACH(const QString& file, files) {
//kDebug(1203) << file;
if (file[0] != '.') {
KNewFileMenuSingleton::Entry e;
e.filePath = file;
e.entryType = KNewFileMenuSingleton::Unknown; // not parsed yet
// Put Directory first in the list (a bit hacky),
// and TextFile before others because it's the most used one.
// This also sorts by user-visible name.
// The rest of the re-ordering is done in fillMenu.
const KDesktopFile config(file);
QString key = config.desktopGroup().readEntry("Name");
if (file.endsWith("Directory.desktop")) {
key.prepend('0');
} else if (file.endsWith("TextFile.desktop")) {
key.prepend('1');
} else {
key.prepend('2');
}
slist.insert(key, e);
}
}
(*s->templatesList) += slist.values();
}
void KNewFileMenuPrivate::_k_slotOtherDesktopFile()
{
executeStrategy();
}
void KNewFileMenuPrivate::_k_slotRealFileOrDir()
{
m_strategy.m_chosenFileName = m_text;
_k_slotAbortDialog();
executeStrategy();
}
void KNewFileMenuPrivate::_k_slotSymLink()
{
KNameAndUrlInputDialog* dlg = static_cast<KNameAndUrlInputDialog*>(m_fileDialog);
m_strategy.m_chosenFileName = dlg->name(); // no path
KUrl linkUrl = dlg->url(); // the url to put in the file
if (m_strategy.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
return;
if (linkUrl.isRelative())
m_strategy.m_src = linkUrl.url();
else if (linkUrl.isLocalFile())
m_strategy.m_src = linkUrl.toLocalFile();
else {
KDialog* dialog = new KDialog(m_parentWidget);
dialog->setCaption( i18n("Sorry") );
dialog->setButtons( KDialog::Ok );
dialog->setObjectName( "sorry" );
dialog->setModal(m_modal);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setDefaultButton( KDialog::Ok );
dialog->setEscapeButton( KDialog::Ok );
m_fileDialog = dialog;
KMessageBox::createKMessageBox(dialog, QMessageBox::Warning,
i18n("Basic links can only point to local files or directories.\nPlease use \"Link to Location\" for remote URLs."),
QStringList(), QString(), 0, KMessageBox::NoExec,
QString());
dialog->show();
return;
}
executeStrategy();
}
void KNewFileMenuPrivate::_k_slotTextChanged(const QString & text)
{
m_text = text;
}
void KNewFileMenuPrivate::_k_slotUrlDesktopFile()
{
- KNameAndUrlInputDialog* dlg = (KNameAndUrlInputDialog*) m_fileDialog;
-
+ KNameAndUrlInputDialog* dlg = static_cast<KNameAndUrlInputDialog*>(m_fileDialog);
+
m_strategy.m_chosenFileName = dlg->name(); // no path
KUrl linkUrl = dlg->url();
// Filter user input so that short uri entries, e.g. www.kde.org, are
// handled properly. This not only makes the icon detection below work
// properly, but opening the URL link where the short uri will not be
// sent to the application (opening such link Konqueror fails).
KUriFilterData uriData;
uriData.setData(linkUrl); // the url to put in the file
uriData.setCheckForExecutables(false);
if (KUriFilter::self()->filterUri(uriData, QStringList() << QLatin1String("kshorturifilter"))) {
linkUrl = uriData.uri();
}
if (m_strategy.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
return;
// It's a "URL" desktop file; we need to make a temp copy of it, to modify it
// before copying it to the final destination [which could be a remote protocol]
KTemporaryFile tmpFile;
tmpFile.setAutoRemove(false); // done below
if (!tmpFile.open()) {
kError() << "Couldn't create temp file!";
return;
}
if (!checkSourceExists(m_strategy.m_templatePath)) {
return;
}
// First copy the template into the temp file
QFile file(m_strategy.m_templatePath);
if (!file.open(QIODevice::ReadOnly)) {
kError() << "Couldn't open template" << m_strategy.m_templatePath;
return;
}
const QByteArray data = file.readAll();
tmpFile.write(data);
const QString tempFileName = tmpFile.fileName();
Q_ASSERT(!tempFileName.isEmpty());
tmpFile.close();
file.close();
KDesktopFile df(tempFileName);
KConfigGroup group = df.desktopGroup();
group.writeEntry("Icon", KProtocolInfo::icon(linkUrl.protocol()));
group.writePathEntry("URL", linkUrl.prettyUrl());
df.sync();
m_strategy.m_src = tempFileName;
m_strategy.m_tempFileToDelete = tempFileName;
executeStrategy();
}
KNewFileMenu::KNewFileMenu(KActionCollection* collection, const QString& name, QObject* parent)
: KActionMenu(KIcon("document-new"), i18n("Create New"), parent),
d(new KNewFileMenuPrivate(this))
{
// Don't fill the menu yet
// We'll do that in checkUpToDate (should be connected to aboutToShow)
d->m_newMenuGroup = new QActionGroup(this);
connect(d->m_newMenuGroup, SIGNAL(triggered(QAction*)), this, SLOT(_k_slotActionTriggered(QAction*)));
d->m_actionCollection = collection;
d->m_parentWidget = qobject_cast<QWidget*>(parent);
d->m_newDirAction = 0;
d->m_actionCollection->addAction(name, this);
d->m_menuDev = new KActionMenu(KIcon("drive-removable-media"), i18n("Link to Device"), this);
}
KNewFileMenu::~KNewFileMenu()
{
//kDebug(1203) << this;
delete d;
}
void KNewFileMenu::checkUpToDate()
{
KNewFileMenuSingleton* s = kNewMenuGlobals;
//kDebug(1203) << this << "m_menuItemsVersion=" << d->m_menuItemsVersion
// << "s->templatesVersion=" << s->templatesVersion;
if (d->m_menuItemsVersion < s->templatesVersion || s->templatesVersion == 0) {
//kDebug(1203) << "recreating actions";
// We need to clean up the action collection
// We look for our actions using the group
foreach (QAction* action, d->m_newMenuGroup->actions())
delete action;
if (!s->templatesList) { // No templates list up to now
s->templatesList = new KNewFileMenuSingleton::EntryList;
d->_k_slotFillTemplates();
s->parseFiles();
}
// This might have been already done for other popupmenus,
// that's the point in s->filesParsed.
if (!s->filesParsed) {
s->parseFiles();
}
d->fillMenu();
d->m_menuItemsVersion = s->templatesVersion;
}
}
void KNewFileMenu::createDirectory()
{
if (d->m_popupFiles.isEmpty())
return;
KUrl baseUrl = d->m_popupFiles.first();
QString name = d->m_text.isEmpty()? i18nc("Default name for a new folder", "New Folder") :
d->m_text;
if (baseUrl.isLocalFile() && QFileInfo(baseUrl.toLocalFile(KUrl::AddTrailingSlash) + name).exists())
name = KIO::RenameDialog::suggestName(baseUrl, name);
KDialog* fileDialog = new KDialog(d->m_parentWidget);
fileDialog->setModal(isModal());
fileDialog->setAttribute(Qt::WA_DeleteOnClose);
fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
fileDialog->setCaption(i18nc("@title:window", "New Folder"));
QWidget* mainWidget = new QWidget(fileDialog);
QVBoxLayout *layout = new QVBoxLayout(mainWidget);
QLabel *label = new QLabel(i18n("Create new folder in:\n%1", baseUrl.pathOrUrl()));
// We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
// It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
// TODO: should probably be investigated and fixed in KLineEdit.
KLineEdit *lineEdit = new KLineEdit;
lineEdit->setClearButtonShown(true);
lineEdit->setText(name);
d->_k_slotTextChanged(name); // have to save string in d->m_text in case user does not touch dialog
connect(lineEdit, SIGNAL(textChanged(QString)), this, SLOT(_k_slotTextChanged(QString)));
layout->addWidget(label);
layout->addWidget(lineEdit);
fileDialog->setMainWidget(mainWidget);
connect(fileDialog, SIGNAL(accepted()), this, SLOT(_k_slotCreateDirectory()));
connect(fileDialog, SIGNAL(rejected()), this, SLOT(_k_slotAbortDialog()));
d->m_fileDialog = fileDialog;
fileDialog->show();
lineEdit->selectAll();
lineEdit->setFocus();
}
bool KNewFileMenu::isModal() const
{
return d->m_modal;
}
KUrl::List KNewFileMenu::popupFiles() const
{
return d->m_popupFiles;
}
void KNewFileMenu::setModal(bool modal)
{
d->m_modal = modal;
}
void KNewFileMenu::setPopupFiles(const KUrl::List& files)
{
d->m_popupFiles = files;
if (files.isEmpty()) {
d->m_newMenuGroup->setEnabled(false);
} else {
KUrl firstUrl = files.first();
if (KProtocolManager::supportsWriting(firstUrl)) {
d->m_newMenuGroup->setEnabled(true);
if (d->m_newDirAction) {
d->m_newDirAction->setEnabled(KProtocolManager::supportsMakeDir(firstUrl)); // e.g. trash:/
}
} else {
d->m_newMenuGroup->setEnabled(true);
}
}
}
void KNewFileMenu::setParentWidget(QWidget* parentWidget)
{
d->m_parentWidget = parentWidget;
}
void KNewFileMenu::setSupportedMimeTypes(const QStringList& mime)
{
d->m_supportedMimeTypes = mime;
}
void KNewFileMenu::setViewShowsHiddenFiles(bool b)
{
d->m_viewShowsHiddenFiles = b;
}
void KNewFileMenu::slotResult(KJob * job)
{
if (job->error()) {
static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
} else {
// Was this a copy or a mkdir?
KIO::CopyJob* copyJob = ::qobject_cast<KIO::CopyJob*>(job);
if (copyJob) {
const KUrl destUrl = copyJob->destUrl();
const KUrl localUrl = KIO::NetAccess::mostLocalUrl(destUrl, d->m_parentWidget);
if (localUrl.isLocalFile()) {
// Normal (local) file. Need to "touch" it, kio_file copied the mtime.
(void) ::utime(QFile::encodeName(localUrl.toLocalFile()), 0);
}
emit fileCreated(destUrl);
} else if (KIO::SimpleJob* simpleJob = ::qobject_cast<KIO::SimpleJob*>(job)) {
// Can be mkdir or symlink
if (simpleJob->property("isMkdirJob").toBool() == true) {
kDebug() << "Emit directoryCreated" << simpleJob->url();
emit directoryCreated(simpleJob->url());
} else {
emit fileCreated(simpleJob->url());
}
}
}
if (!d->m_tempFileToDelete.isEmpty())
QFile::remove(d->m_tempFileToDelete);
}
QStringList KNewFileMenu::supportedMimeTypes() const
{
return d->m_supportedMimeTypes;
}
#include "knewfilemenu.moc"
diff --git a/kfile/tests/CMakeLists.txt b/kfile/tests/CMakeLists.txt
index bb26bc72f7..37eca31286 100644
--- a/kfile/tests/CMakeLists.txt
+++ b/kfile/tests/CMakeLists.txt
@@ -1,29 +1,30 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
MACRO(KFILE_UNIT_TESTS)
FOREACH(_testname ${ARGN})
kde4_add_unit_test(${_testname} TESTNAME kfile-${_testname} ${_testname}.cpp)
target_link_libraries(${_testname} ${KDE4_KFILE_LIBS} ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY} ${QT_QTXML_LIBRARY})
ENDFOREACH(_testname)
ENDMACRO(KFILE_UNIT_TESTS)
MACRO(KFILE_EXECUTABLE_TESTS)
FOREACH(_testname ${ARGN})
kde4_add_executable(${_testname} TEST ${_testname}.cpp)
target_link_libraries(${_testname} ${KDE4_KFILE_LIBS} ${QT_QTTEST_LIBRARY})
ENDFOREACH(_testname)
ENDMACRO(KFILE_EXECUTABLE_TESTS)
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/.. )
KFILE_EXECUTABLE_TESTS(
kdirselectdialogtest
kfiletreeviewtest
)
add_definitions(-DTEST_DATA="\\"${CMAKE_SOURCE_DIR}/solid/solid/backends/fakehw/fakecomputer.xml\\"")
KFILE_UNIT_TESTS(
kfileplacesmodeltest
kurlnavigatortest
kdiroperatortest
+ knewfilemenutest
)
diff --git a/kfile/tests/knewfilemenutest.cpp b/kfile/tests/knewfilemenutest.cpp
new file mode 100644
index 0000000000..ffb35ff033
--- /dev/null
+++ b/kfile/tests/knewfilemenutest.cpp
@@ -0,0 +1,115 @@
+/* This file is part of the KDE libraries
+ Copyright (c) 2012 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2 of the License or ( at
+ your option ) version 3 or, at the discretion of KDE e.V. ( which shall
+ act as a proxy as in section 14 of the GPLv3 ), any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <ktempdir.h>
+#include <qtest_kde.h>
+
+#include <QDialog>
+#include <QLineEdit>
+#include <QMenu>
+#include <knameandurlinputdialog.h>
+#include <kactioncollection.h>
+#include <kdebug.h>
+#include <knewfilemenu.h>
+
+class KNewFileMenuTest : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase()
+ {
+ m_first = true;
+ }
+
+ void cleanupTestCase()
+ {
+ }
+
+ void test_data()
+ {
+ QTest::addColumn<QString>("actionText");
+ QTest::addColumn<QString>("typedFilename");
+ QTest::addColumn<QString>("expectedFilename");
+
+ QTest::newRow("text file") << "Text File" << "tmp_knewfilemenutest.txt" << "tmp_knewfilemenutest.txt";
+ QTest::newRow("text file with jpeg extension") << "Text File" << "foo.jpg" << "foo.jpg.txt";
+ QTest::newRow("url desktop file") << "Link to Location " << "tmp_link.desktop" << "tmp_link.desktop";
+ QTest::newRow("url desktop file no extension") << "Link to Location " << "tmp_link" << "tmp_link";
+ QTest::newRow("url desktop file .pl extension") << "Link to Location " << "tmp_link.pl" << "tmp_link.pl.desktop";
+ QTest::newRow("symlink") << "Basic link" << "thelink" << "thelink";
+ }
+
+ void test()
+ {
+ QFETCH(QString, actionText);
+ QFETCH(QString, typedFilename);
+ QFETCH(QString, expectedFilename);
+
+ QWidget parentWidget;
+ KComponentData comp("foo");
+ KActionCollection coll(this, comp);
+ KNewFileMenu menu(&coll, "the_action", this);
+ menu.setModal(false);
+ menu.setParentWidget(&parentWidget);
+ KUrl u(m_tmpDir.name());
+ KUrl::List lst(u);
+ menu.setPopupFiles(lst);
+ menu.checkUpToDate();
+ QAction* action = coll.action("the_action");
+ QVERIFY(action);
+ QAction* textAct = 0;
+ Q_FOREACH(QAction* act, action->menu()->actions()) {
+ qDebug() << act << act->text() << act->data();
+ if (act->text().contains(actionText))
+ textAct = act;
+ }
+ if (!textAct && m_first) {
+ const QString err = "action with text" + actionText + "not found. kde-baseapps not installed?";
+ QSKIP(qPrintable(err), SkipAll);
+ }
+ textAct->trigger();
+ QDialog* dialog = qFindChild<QDialog *>(&parentWidget);
+ QVERIFY(dialog);
+ KNameAndUrlInputDialog* nauiDialog = qobject_cast<KNameAndUrlInputDialog *>(dialog);
+ if (nauiDialog) {
+ nauiDialog->setSuggestedName(typedFilename);
+ nauiDialog->setSuggestedUrl(KUrl("file:///etc"));
+ } else {
+ QLineEdit* lineEdit = qFindChild<QLineEdit *>(dialog);
+ QVERIFY(lineEdit);
+ lineEdit->setText(typedFilename);
+ }
+ dialog->accept();
+ QSignalSpy spy(&menu, SIGNAL(fileCreated(KUrl)));
+ QTest::kWaitForSignal(&menu, SIGNAL(fileCreated(KUrl)));
+ const KUrl url = spy.at(0).at(0).value<KUrl>();
+ const QString path = m_tmpDir.name() + expectedFilename;
+ QCOMPARE(url.toLocalFile(), path);
+ QFile::remove(path);
+ m_first = false;
+ }
+private:
+ KTempDir m_tmpDir;
+ bool m_first;
+};
+
+QTEST_KDEMAIN( KNewFileMenuTest, GUI )
+
+#include "knewfilemenutest.moc"

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 1, 10:03 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10076310
Default Alt Text
(46 KB)

Event Timeline