• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.1 API Reference
  • KDE Home
  • Contact Us
 

KFile

  • kfile
knewfilemenu.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE project
2  Copyright (C) 1998-2009 David Faure <faure@kde.org>
3  2003 Sven Leiber <s.leiber@web.de>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 or at your option version 3.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Library General Public License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to
17  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19 */
20 
21 #include "knewfilemenu.h"
22 #include "knameandurlinputdialog.h"
23 
24 #include <QDir>
25 #include <QVBoxLayout>
26 #include <QList>
27 #include <QLabel>
28 #include <kactioncollection.h>
29 #include <kdebug.h>
30 #include <kdesktopfile.h>
31 #include <kdirwatch.h>
32 #include <kicon.h>
33 #include <kcomponentdata.h>
34 #include <kinputdialog.h>
35 #include <kdialog.h>
36 #include <klocale.h>
37 #include <klineedit.h>
38 #include <kmessagebox.h>
39 #include <kstandarddirs.h>
40 #include <kprotocolinfo.h>
41 #include <kprotocolmanager.h>
42 #include <kmenu.h>
43 #include <krun.h>
44 #include <kshell.h>
45 #include <kio/job.h>
46 #include <kio/copyjob.h>
47 #include <kio/jobuidelegate.h>
48 #include <kio/renamedialog.h>
49 #include <kio/netaccess.h>
50 #include <kio/fileundomanager.h>
51 #include <kio/kurifilter.h>
52 
53 #include <kpropertiesdialog.h>
54 #include <ktemporaryfile.h>
55 #include <utime.h>
56 
57 static QString expandTilde(const QString& name, bool isfile = false)
58 {
59  if (!name.isEmpty() && (!isfile || name[0] == '\\'))
60  {
61  const QString expandedName = KShell::tildeExpand(name);
62  // When a tilde mark cannot be properly expanded, the above call
63  // returns an empty string...
64  if (!expandedName.isEmpty())
65  return expandedName;
66  }
67 
68  return name;
69 }
70 
71 // Singleton, with data shared by all KNewFileMenu instances
72 class KNewFileMenuSingleton
73 {
74 public:
75  KNewFileMenuSingleton()
76  : dirWatch(0),
77  filesParsed(false),
78  templatesList(0),
79  templatesVersion(0)
80  {
81  }
82 
83  ~KNewFileMenuSingleton()
84  {
85  delete dirWatch;
86  delete templatesList;
87  }
88 
89 
94  void parseFiles();
95 
103  enum EntryType { Unknown, LinkToTemplate = 1, Template, Separator };
104 
105  KDirWatch * dirWatch;
106 
107  struct Entry {
108  QString text;
109  QString filePath; // empty for Separator
110  QString templatePath; // same as filePath for Template
111  QString icon;
112  EntryType entryType;
113  QString comment;
114  QString mimeType;
115  };
116  // NOTE: only filePath is known before we call parseFiles
117 
122  typedef QList<Entry> EntryList;
123 
128  bool filesParsed;
129  EntryList * templatesList;
130 
136  int templatesVersion;
137 };
138 
139 void KNewFileMenuSingleton::parseFiles()
140 {
141  //kDebug(1203);
142  filesParsed = true;
143  KNewFileMenuSingleton::EntryList::iterator templ = templatesList->begin();
144  const KNewFileMenuSingleton::EntryList::iterator templ_end = templatesList->end();
145  for (; templ != templ_end; ++templ)
146  {
147  QString iconname;
148  QString filePath = (*templ).filePath;
149  if (!filePath.isEmpty())
150  {
151  QString text;
152  QString templatePath;
153  // If a desktop file, then read the name from it.
154  // Otherwise (or if no name in it?) use file name
155  if (KDesktopFile::isDesktopFile(filePath)) {
156  KDesktopFile desktopFile( filePath);
157  text = desktopFile.readName();
158  (*templ).icon = desktopFile.readIcon();
159  (*templ).comment = desktopFile.readComment();
160  QString type = desktopFile.readType();
161  if (type == "Link")
162  {
163  templatePath = desktopFile.desktopGroup().readPathEntry("URL", QString());
164  if (templatePath[0] != '/' && !templatePath.startsWith("__"))
165  {
166  if (templatePath.startsWith("file:/"))
167  templatePath = KUrl(templatePath).toLocalFile();
168  else
169  {
170  // A relative path, then (that's the default in the files we ship)
171  QString linkDir = filePath.left(filePath.lastIndexOf('/') + 1 /*keep / */);
172  //kDebug(1203) << "linkDir=" << linkDir;
173  templatePath = linkDir + templatePath;
174  }
175  }
176  }
177  if (templatePath.isEmpty())
178  {
179  // No URL key, this is an old-style template
180  (*templ).entryType = KNewFileMenuSingleton::Template;
181  (*templ).templatePath = (*templ).filePath; // we'll copy the file
182  } else {
183  (*templ).entryType = KNewFileMenuSingleton::LinkToTemplate;
184  (*templ).templatePath = templatePath;
185  }
186 
187  }
188  if (text.isEmpty())
189  {
190  text = KUrl(filePath).fileName();
191  if (text.endsWith(".desktop"))
192  text.truncate(text.length() - 8);
193  }
194  (*templ).text = text;
195  /*kDebug(1203) << "Updating entry with text=" << text
196  << "entryType=" << (*templ).entryType
197  << "templatePath=" << (*templ).templatePath;*/
198  }
199  else {
200  (*templ).entryType = KNewFileMenuSingleton::Separator;
201  }
202  }
203 }
204 
205 K_GLOBAL_STATIC(KNewFileMenuSingleton, kNewMenuGlobals)
206 
207 class KNewFileMenuCopyData
208 {
209 public:
210  KNewFileMenuCopyData() { m_isSymlink = false;}
211  ~KNewFileMenuCopyData() {}
212  QString chosenFileName() const { return m_chosenFileName; }
213 
214  // If empty, no copy is performed.
215  QString sourceFileToCopy() const { return m_src; }
216  QString tempFileToDelete() const { return m_tempFileToDelete; }
217  bool m_isSymlink;
218 
219  QString m_chosenFileName;
220  QString m_src;
221  QString m_tempFileToDelete;
222  QString m_templatePath;
223 };
224 
225 class KNewFileMenuPrivate
226 {
227 public:
228  KNewFileMenuPrivate(KNewFileMenu* qq)
229  : m_menuItemsVersion(0),
230  m_modal(true),
231  m_viewShowsHiddenFiles(false),
232  q(qq)
233  {}
234 
235  bool checkSourceExists(const QString& src);
236 
240  void confirmCreatingHiddenDir(const QString& name);
241 
245  void executeOtherDesktopFile(const KNewFileMenuSingleton::Entry& entry);
246 
250  void executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry);
251 
255  void executeStrategy();
256 
260  void executeSymLink(const KNewFileMenuSingleton::Entry& entry);
261 
265  void executeUrlDesktopFile(const KNewFileMenuSingleton::Entry& entry);
266 
270  void fillMenu();
271 
275  void _k_slotAbortDialog();
276 
280  void _k_slotActionTriggered(QAction* action);
281 
285  void _k_slotCreateDirectory(bool writeHiddenDir = false);
286 
291  void _k_slotCreateHiddenDirectory();
292 
296  void _k_slotFillTemplates();
297 
302  void _k_slotOtherDesktopFile();
303 
308  void _k_slotRealFileOrDir();
309 
314  void _k_slotTextChanged(const QString & text);
315 
320  void _k_slotSymLink();
321 
326  void _k_slotUrlDesktopFile();
327 
328 
329  KActionCollection * m_actionCollection;
330  KDialog* m_fileDialog;
331 
332  KActionMenu *m_menuDev;
333  int m_menuItemsVersion;
334  bool m_modal;
335  QAction* m_newDirAction;
336 
340  QActionGroup* m_newMenuGroup;
341  QWidget *m_parentWidget;
342 
347  KUrl::List m_popupFiles;
348 
349  QStringList m_supportedMimeTypes;
350  QString m_tempFileToDelete; // set when a tempfile was created for a Type=URL desktop file
351  QString m_text;
352  bool m_viewShowsHiddenFiles;
353 
354  KNewFileMenu* q;
355 
356  KNewFileMenuCopyData m_copyData;
357 };
358 
359 bool KNewFileMenuPrivate::checkSourceExists(const QString& src)
360 {
361  if (!QFile::exists(src)) {
362  kWarning(1203) << src << "doesn't exist" ;
363 
364  KDialog* dialog = new KDialog(m_parentWidget);
365  dialog->setCaption( i18n("Sorry") );
366  dialog->setButtons( KDialog::Ok );
367  dialog->setObjectName( "sorry" );
368  dialog->setModal(q->isModal());
369  dialog->setAttribute(Qt::WA_DeleteOnClose);
370  dialog->setDefaultButton( KDialog::Ok );
371  dialog->setEscapeButton( KDialog::Ok );
372 
373  KMessageBox::createKMessageBox(dialog, QMessageBox::Warning,
374  i18n("<qt>The template file <b>%1</b> does not exist.</qt>", src),
375  QStringList(), QString(), 0, KMessageBox::NoExec,
376  QString());
377 
378  dialog->show();
379 
380  return false;
381  }
382  return true;
383 }
384 
385 void KNewFileMenuPrivate::confirmCreatingHiddenDir(const QString& name)
386 {
387  if(!KMessageBox::shouldBeShownContinue("confirm_create_hidden_dir")){
388  _k_slotCreateHiddenDirectory();
389  return;
390  }
391 
392  KGuiItem continueGuiItem(KStandardGuiItem::cont());
393  continueGuiItem.setText(i18nc("@action:button", "Create directory"));
394  KGuiItem cancelGuiItem(KStandardGuiItem::cancel());
395  cancelGuiItem.setText(i18nc("@action:button", "Enter a different name"));
396 
397  KDialog* confirmDialog = new KDialog(m_parentWidget);
398  confirmDialog->setCaption(i18n("Create hidden directory?"));
399  confirmDialog->setModal(m_modal);
400  confirmDialog->setAttribute(Qt::WA_DeleteOnClose);
401  KMessageBox::createKMessageBox(confirmDialog, QMessageBox::Warning,
402  i18n("The name \"%1\" starts with a dot, so the directory will be hidden by default.", name),
403  QStringList(),
404  i18n("Do not ask again"),
405  0,
406  KMessageBox::NoExec,
407  QString());
408  confirmDialog->setButtonGuiItem(KDialog::Ok, continueGuiItem);
409  confirmDialog->setButtonGuiItem(KDialog::Cancel, cancelGuiItem);
410 
411  QObject::connect(confirmDialog, SIGNAL(accepted()), q, SLOT(_k_slotCreateHiddenDirectory()));
412  QObject::connect(confirmDialog, SIGNAL(rejected()), q, SLOT(createDirectory()));
413 
414  m_fileDialog = confirmDialog;
415  confirmDialog->show();
416 
417 }
418 
419 void KNewFileMenuPrivate::executeOtherDesktopFile(const KNewFileMenuSingleton::Entry& entry)
420 {
421  if (!checkSourceExists(entry.templatePath)) {
422  return;
423  }
424 
425  KUrl::List::const_iterator it = m_popupFiles.constBegin();
426  for (; it != m_popupFiles.constEnd(); ++it)
427  {
428  QString text = entry.text;
429  text.remove("..."); // the ... is fine for the menu item but not for the default filename
430  text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
431  // KDE5 TODO: remove the "..." from link*.desktop files and use i18n("%1...") when making
432  // the action.
433 
434  KUrl defaultFile(*it);
435  defaultFile.addPath(KIO::encodeFileName(text));
436  if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
437  text = KIO::RenameDialog::suggestName(*it, text);
438 
439  const KUrl templateUrl(entry.templatePath);
440 
441  KDialog* dlg = new KPropertiesDialog(templateUrl, *it, text, m_parentWidget);
442  dlg->setModal(q->isModal());
443  dlg->setAttribute(Qt::WA_DeleteOnClose);
444  QObject::connect(dlg, SIGNAL(applied()), q, SLOT(_k_slotOtherDesktopFile()));
445  dlg->show();
446  }
447  // We don't set m_src here -> there will be no copy, we are done.
448 }
449 
450 void KNewFileMenuPrivate::executeRealFileOrDir(const KNewFileMenuSingleton::Entry& entry)
451 {
452  // The template is not a desktop file
453  // Show the small dialog for getting the destination filename
454  QString text = entry.text;
455  text.remove("..."); // the ... is fine for the menu item but not for the default filename
456  text = text.trimmed(); // In some languages, there is a space in front of "...", see bug 268895
457  m_copyData.m_src = entry.templatePath;
458 
459  KUrl defaultFile(m_popupFiles.first());
460  defaultFile.addPath(KIO::encodeFileName(text));
461  if (defaultFile.isLocalFile() && QFile::exists(defaultFile.toLocalFile()))
462  text = KIO::RenameDialog::suggestName(m_popupFiles.first(), text);
463 
464  KDialog* fileDialog = new KDialog(m_parentWidget);
465  fileDialog->setAttribute(Qt::WA_DeleteOnClose);
466  fileDialog->setModal(q->isModal());
467  fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
468 
469  QWidget* mainWidget = new QWidget(fileDialog);
470  QVBoxLayout *layout = new QVBoxLayout(mainWidget);
471  QLabel *label = new QLabel(entry.comment);
472 
473  // We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
474  // It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
475  // TODO: should probably be investigated and fixed in KLineEdit.
476  KLineEdit *lineEdit = new KLineEdit;
477  lineEdit->setClearButtonShown(true);
478  lineEdit->setText(text);
479 
480  _k_slotTextChanged(text);
481  QObject::connect(lineEdit, SIGNAL(textChanged(QString)), q, SLOT(_k_slotTextChanged(QString)));
482 
483  layout->addWidget(label);
484  layout->addWidget(lineEdit);
485 
486  fileDialog->setMainWidget(mainWidget);
487  QObject::connect(fileDialog, SIGNAL(accepted()), q, SLOT(_k_slotRealFileOrDir()));
488  QObject::connect(fileDialog, SIGNAL(rejected()), q, SLOT(_k_slotAbortDialog()));
489 
490  fileDialog->show();
491  lineEdit->selectAll();
492  lineEdit->setFocus();
493 }
494 
495 void KNewFileMenuPrivate::executeSymLink(const KNewFileMenuSingleton::Entry& entry)
496 {
497  KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);
498  dlg->setModal(q->isModal());
499  dlg->setAttribute(Qt::WA_DeleteOnClose);
500  dlg->setCaption(i18n("Create Symlink"));
501  m_fileDialog = dlg;
502  QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotSymLink()));
503  dlg->show();
504 }
505 
506 void KNewFileMenuPrivate::executeStrategy()
507 {
508  m_tempFileToDelete = m_copyData.tempFileToDelete();
509  const QString src = m_copyData.sourceFileToCopy();
510  QString chosenFileName = expandTilde(m_copyData.chosenFileName(), true);
511 
512  if (src.isEmpty())
513  return;
514  KUrl uSrc(src);
515  if (uSrc.isLocalFile()) {
516  // In case the templates/.source directory contains symlinks, resolve
517  // them to the target files. Fixes bug #149628.
518  KFileItem item(uSrc, QString(), KFileItem::Unknown);
519  if (item.isLink())
520  uSrc.setPath(item.linkDest());
521 
522  if (!m_copyData.m_isSymlink) {
523  // If the file is not going to be detected as a desktop file, due to a
524  // known extension (e.g. ".pl"), append ".desktop". #224142.
525  QFile srcFile(uSrc.toLocalFile());
526  if (srcFile.open(QIODevice::ReadOnly)) {
527  KMimeType::Ptr wantedMime = KMimeType::findByUrl(uSrc);
528  KMimeType::Ptr mime = KMimeType::findByNameAndContent(m_copyData.m_chosenFileName, srcFile.read(1024));
529  //kDebug() << "mime=" << mime->name() << "wantedMime=" << wantedMime->name();
530  if (!mime->is(wantedMime->name()))
531  chosenFileName += wantedMime->mainExtension();
532  }
533  }
534  }
535 
536  // The template is not a desktop file [or it's a URL one]
537  // Copy it.
538  KUrl::List::const_iterator it = m_popupFiles.constBegin();
539  for (; it != m_popupFiles.constEnd(); ++it)
540  {
541  KUrl dest(*it);
542  dest.addPath(KIO::encodeFileName(chosenFileName));
543 
544  KUrl::List lstSrc;
545  lstSrc.append(uSrc);
546  KIO::Job* kjob;
547  if (m_copyData.m_isSymlink) {
548  kjob = KIO::symlink(src, dest);
549  // This doesn't work, FileUndoManager registers new links in copyingLinkDone,
550  // which KIO::symlink obviously doesn't emit... Needs code in FileUndoManager.
551  //KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Link, lstSrc, dest, kjob);
552  } else {
553  //kDebug(1203) << "KIO::copyAs(" << uSrc.url() << "," << dest.url() << ")";
554  KIO::CopyJob * job = KIO::copyAs(uSrc, dest);
555  job->setDefaultPermissions(true);
556  kjob = job;
557  KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Copy, lstSrc, dest, job);
558  }
559  kjob->ui()->setWindow(m_parentWidget);
560  QObject::connect(kjob, SIGNAL(result(KJob*)), q, SLOT(slotResult(KJob*)));
561  }
562 }
563 
564 void KNewFileMenuPrivate::executeUrlDesktopFile(const KNewFileMenuSingleton::Entry& entry)
565 {
566  KNameAndUrlInputDialog* dlg = new KNameAndUrlInputDialog(i18n("File name:"), entry.comment, m_popupFiles.first(), m_parentWidget);
567  m_copyData.m_templatePath = entry.templatePath;
568  dlg->setModal(q->isModal());
569  dlg->setAttribute(Qt::WA_DeleteOnClose);
570  dlg->setCaption(i18n("Create link to URL"));
571  m_fileDialog = dlg;
572  QObject::connect(dlg, SIGNAL(accepted()), q, SLOT(_k_slotUrlDesktopFile()));
573  dlg->show();
574 }
575 
576 void KNewFileMenuPrivate::fillMenu()
577 {
578  QMenu* menu = q->menu();
579  menu->clear();
580  m_menuDev->menu()->clear();
581  m_newDirAction = 0;
582 
583  QSet<QString> seenTexts;
584  // these shall be put at special positions
585  QAction* linkURL = 0;
586  QAction* linkApp = 0;
587  QAction* linkPath = 0;
588 
589  KNewFileMenuSingleton* s = kNewMenuGlobals;
590  int i = 1;
591  KNewFileMenuSingleton::EntryList::iterator templ = s->templatesList->begin();
592  const KNewFileMenuSingleton::EntryList::iterator templ_end = s->templatesList->end();
593  for (; templ != templ_end; ++templ, ++i)
594  {
595  KNewFileMenuSingleton::Entry& entry = *templ;
596  if (entry.entryType != KNewFileMenuSingleton::Separator) {
597  // There might be a .desktop for that one already, if it's a kdelnk
598  // This assumes we read .desktop files before .kdelnk files ...
599 
600  // In fact, we skip any second item that has the same text as another one.
601  // Duplicates in a menu look bad in any case.
602 
603  const bool bSkip = seenTexts.contains(entry.text);
604  if (bSkip) {
605  kDebug(1203) << "skipping" << entry.filePath;
606  } else {
607  seenTexts.insert(entry.text);
608  //const KNewFileMenuSingleton::Entry entry = templatesList->at(i-1);
609 
610  const QString templatePath = entry.templatePath;
611  // The best way to identify the "Create Directory", "Link to Location", "Link to Application" was the template
612  if (templatePath.endsWith("emptydir")) {
613  QAction * act = new QAction(q);
614  m_newDirAction = act;
615  act->setIcon(KIcon(entry.icon));
616  act->setText(i18nc("@item:inmenu Create New", "%1", entry.text));
617  act->setActionGroup(m_newMenuGroup);
618  menu->addAction(act);
619 
620  QAction *sep = new QAction(q);
621  sep->setSeparator(true);
622  menu->addAction(sep);
623  } else {
624 
625  if (!m_supportedMimeTypes.isEmpty()) {
626  bool keep = false;
627 
628  // We need to do mimetype filtering, for real files.
629  const bool createSymlink = entry.templatePath == "__CREATE_SYMLINK__";
630  if (createSymlink) {
631  keep = true;
632  } else if (!KDesktopFile::isDesktopFile(entry.templatePath)) {
633 
634  // Determine mimetype on demand
635  KMimeType::Ptr mime;
636  if (entry.mimeType.isEmpty()) {
637  mime = KMimeType::findByPath(entry.templatePath);
638  if (mime) {
639  //kDebug() << entry.templatePath << "is" << mime->name();
640  entry.mimeType = mime->name();
641  } else {
642  entry.mimeType = KMimeType::defaultMimeType();
643  }
644  } else {
645  mime = KMimeType::mimeType(entry.mimeType);
646  }
647  Q_FOREACH(const QString& supportedMime, m_supportedMimeTypes) {
648  if (mime && mime->is(supportedMime)) {
649  keep = true;
650  break;
651  }
652  }
653  }
654 
655  if (!keep) {
656  //kDebug() << "Not keeping" << entry.templatePath;
657  continue;
658  }
659  }
660 
661  QAction * act = new QAction(q);
662  act->setData(i);
663  act->setIcon(KIcon(entry.icon));
664  act->setText(i18nc("@item:inmenu Create New", "%1", entry.text));
665  act->setActionGroup(m_newMenuGroup);
666 
667  //kDebug() << templatePath << entry.filePath;
668 
669  if (templatePath.endsWith("/URL.desktop")) {
670  linkURL = act;
671  } else if (templatePath.endsWith("/Program.desktop")) {
672  linkApp = act;
673  } else if (entry.filePath.endsWith("/linkPath.desktop")) {
674  linkPath = act;
675  } else if (KDesktopFile::isDesktopFile(templatePath)) {
676  KDesktopFile df(templatePath);
677  if (df.readType() == "FSDevice")
678  m_menuDev->menu()->addAction(act);
679  else
680  menu->addAction(act);
681  }
682  else
683  {
684  menu->addAction(act);
685  }
686  }
687  }
688  } else { // Separate system from personal templates
689  Q_ASSERT(entry.entryType != 0);
690 
691  QAction *sep = new QAction(q);
692  sep->setSeparator(true);
693  menu->addAction(sep);
694  }
695  }
696 
697  if (m_supportedMimeTypes.isEmpty()) {
698  QAction *sep = new QAction(q);
699  sep->setSeparator(true);
700  menu->addAction(sep);
701  if (linkURL) menu->addAction(linkURL);
702  if (linkPath) menu->addAction(linkPath);
703  if (linkApp) menu->addAction(linkApp);
704  Q_ASSERT(m_menuDev);
705  menu->addAction(m_menuDev);
706  }
707 }
708 
709 void KNewFileMenuPrivate::_k_slotAbortDialog()
710 {
711  m_text = QString();
712 }
713 
714 void KNewFileMenuPrivate::_k_slotActionTriggered(QAction* action)
715 {
716  q->trigger(); // was for kdesktop's slotNewMenuActivated() in kde3 times. Can't hurt to keep it...
717 
718  if (action == m_newDirAction) {
719  q->createDirectory();
720  return;
721  }
722  const int id = action->data().toInt();
723  Q_ASSERT(id > 0);
724 
725  KNewFileMenuSingleton* s = kNewMenuGlobals;
726  const KNewFileMenuSingleton::Entry entry = s->templatesList->at(id - 1);
727 
728  const bool createSymlink = entry.templatePath == "__CREATE_SYMLINK__";
729 
730  m_copyData = KNewFileMenuCopyData();
731 
732  if (createSymlink) {
733  m_copyData.m_isSymlink = true;
734  executeSymLink(entry);
735  }
736  else if (KDesktopFile::isDesktopFile(entry.templatePath)) {
737  KDesktopFile df(entry.templatePath);
738  if (df.readType() == "Link") {
739  executeUrlDesktopFile(entry);
740  } else { // any other desktop file (Device, App, etc.)
741  executeOtherDesktopFile(entry);
742  }
743  }
744  else {
745  executeRealFileOrDir(entry);
746  }
747 
748 }
749 
750 void KNewFileMenuPrivate::_k_slotCreateDirectory(bool writeHiddenDir)
751 {
752  KUrl url;
753  KUrl baseUrl = m_popupFiles.first();
754  bool askAgain = false;
755 
756  QString name = expandTilde(m_text);
757 
758  if (!name.isEmpty()) {
759  if ((name[0] == '/'))
760  url.setPath(name);
761  else {
762  if (!m_viewShowsHiddenFiles && name.startsWith('.')) {
763  if (!writeHiddenDir) {
764  confirmCreatingHiddenDir(name);
765  return;
766  }
767  }
768  name = KIO::encodeFileName( name );
769  url = baseUrl;
770  url.addPath( name );
771  }
772  }
773 
774  if (!askAgain) {
775  KIO::SimpleJob * job = KIO::mkdir(url);
776  job->setProperty("isMkdirJob", true); // KDE5: cast to MkdirJob in slotResult instead
777  job->ui()->setWindow(m_parentWidget);
778  job->ui()->setAutoErrorHandlingEnabled(true);
779  KIO::FileUndoManager::self()->recordJob( KIO::FileUndoManager::Mkdir, KUrl(), url, job );
780 
781  if (job) {
782  // We want the error handling to be done by slotResult so that subclasses can reimplement it
783  job->ui()->setAutoErrorHandlingEnabled(false);
784  QObject::connect(job, SIGNAL(result(KJob*)), q, SLOT(slotResult(KJob*)));
785  }
786  }
787  else {
788  q->createDirectory(); // ask again for the name
789  }
790  _k_slotAbortDialog();
791 }
792 
793 void KNewFileMenuPrivate::_k_slotCreateHiddenDirectory()
794 {
795  _k_slotCreateDirectory(true);
796 }
797 
798 void KNewFileMenuPrivate::_k_slotFillTemplates()
799 {
800  KNewFileMenuSingleton* s = kNewMenuGlobals;
801  //kDebug(1203);
802  // Ensure any changes in the templates dir will call this
803  if (! s->dirWatch) {
804  s->dirWatch = new KDirWatch;
805  const QStringList dirs = m_actionCollection->componentData().dirs()->resourceDirs("templates");
806  for (QStringList::const_iterator it = dirs.constBegin() ; it != dirs.constEnd() ; ++it) {
807  //kDebug(1203) << "Templates resource dir:" << *it;
808  s->dirWatch->addDir(*it);
809  }
810  QObject::connect(s->dirWatch, SIGNAL(dirty(QString)),
811  q, SLOT(_k_slotFillTemplates()));
812  QObject::connect(s->dirWatch, SIGNAL(created(QString)),
813  q, SLOT(_k_slotFillTemplates()));
814  QObject::connect(s->dirWatch, SIGNAL(deleted(QString)),
815  q, SLOT(_k_slotFillTemplates()));
816  // Ok, this doesn't cope with new dirs in KDEDIRS, but that's another story
817  }
818  ++s->templatesVersion;
819  s->filesParsed = false;
820 
821  s->templatesList->clear();
822 
823  // Look into "templates" dirs.
824  const QStringList files = m_actionCollection->componentData().dirs()->findAllResources("templates");
825  QMap<QString, KNewFileMenuSingleton::Entry> slist; // used for sorting
826  Q_FOREACH(const QString& file, files) {
827  //kDebug(1203) << file;
828  if (file[0] != '.') {
829  KNewFileMenuSingleton::Entry e;
830  e.filePath = file;
831  e.entryType = KNewFileMenuSingleton::Unknown; // not parsed yet
832 
833  // Put Directory first in the list (a bit hacky),
834  // and TextFile before others because it's the most used one.
835  // This also sorts by user-visible name.
836  // The rest of the re-ordering is done in fillMenu.
837  const KDesktopFile config(file);
838  QString key = config.desktopGroup().readEntry("Name");
839  if (file.endsWith("Directory.desktop")) {
840  key.prepend('0');
841  } else if (file.endsWith("TextFile.desktop")) {
842  key.prepend('1');
843  } else {
844  key.prepend('2');
845  }
846  slist.insert(key, e);
847  }
848  }
849  (*s->templatesList) += slist.values();
850 }
851 
852 void KNewFileMenuPrivate::_k_slotOtherDesktopFile()
853 {
854  executeStrategy();
855 }
856 
857 void KNewFileMenuPrivate::_k_slotRealFileOrDir()
858 {
859  m_copyData.m_chosenFileName = m_text;
860  _k_slotAbortDialog();
861  executeStrategy();
862 }
863 
864 void KNewFileMenuPrivate::_k_slotSymLink()
865 {
866  KNameAndUrlInputDialog* dlg = static_cast<KNameAndUrlInputDialog*>(m_fileDialog);
867 
868  m_copyData.m_chosenFileName = dlg->name(); // no path
869  KUrl linkUrl = dlg->url(); // the url to put in the file
870 
871  if (m_copyData.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
872  return;
873 
874  if (linkUrl.isRelative())
875  m_copyData.m_src = linkUrl.url();
876  else if (linkUrl.isLocalFile())
877  m_copyData.m_src = linkUrl.toLocalFile();
878  else {
879  KDialog* dialog = new KDialog(m_parentWidget);
880  dialog->setCaption( i18n("Sorry") );
881  dialog->setButtons( KDialog::Ok );
882  dialog->setObjectName( "sorry" );
883  dialog->setModal(m_modal);
884  dialog->setAttribute(Qt::WA_DeleteOnClose);
885  dialog->setDefaultButton( KDialog::Ok );
886  dialog->setEscapeButton( KDialog::Ok );
887  m_fileDialog = dialog;
888 
889  KMessageBox::createKMessageBox(dialog, QMessageBox::Warning,
890  i18n("Basic links can only point to local files or directories.\nPlease use \"Link to Location\" for remote URLs."),
891  QStringList(), QString(), 0, KMessageBox::NoExec,
892  QString());
893 
894  dialog->show();
895  return;
896  }
897  executeStrategy();
898 }
899 
900 void KNewFileMenuPrivate::_k_slotTextChanged(const QString & text)
901 {
902  m_text = text;
903 }
904 
905 void KNewFileMenuPrivate::_k_slotUrlDesktopFile()
906 {
907  KNameAndUrlInputDialog* dlg = static_cast<KNameAndUrlInputDialog*>(m_fileDialog);
908 
909  m_copyData.m_chosenFileName = dlg->name(); // no path
910  KUrl linkUrl = dlg->url();
911 
912  // Filter user input so that short uri entries, e.g. www.kde.org, are
913  // handled properly. This not only makes the icon detection below work
914  // properly, but opening the URL link where the short uri will not be
915  // sent to the application (opening such link Konqueror fails).
916  KUriFilterData uriData;
917  uriData.setData(linkUrl); // the url to put in the file
918  uriData.setCheckForExecutables(false);
919 
920  if (KUriFilter::self()->filterUri(uriData, QStringList() << QLatin1String("kshorturifilter"))) {
921  linkUrl = uriData.uri();
922  }
923 
924  if (m_copyData.m_chosenFileName.isEmpty() || linkUrl.isEmpty())
925  return;
926 
927  // It's a "URL" desktop file; we need to make a temp copy of it, to modify it
928  // before copying it to the final destination [which could be a remote protocol]
929  KTemporaryFile tmpFile;
930  tmpFile.setAutoRemove(false); // done below
931  if (!tmpFile.open()) {
932  kError() << "Couldn't create temp file!";
933  return;
934  }
935 
936  if (!checkSourceExists(m_copyData.m_templatePath)) {
937  return;
938  }
939 
940  // First copy the template into the temp file
941  QFile file(m_copyData.m_templatePath);
942  if (!file.open(QIODevice::ReadOnly)) {
943  kError() << "Couldn't open template" << m_copyData.m_templatePath;
944  return;
945  }
946  const QByteArray data = file.readAll();
947  tmpFile.write(data);
948  const QString tempFileName = tmpFile.fileName();
949  Q_ASSERT(!tempFileName.isEmpty());
950  tmpFile.close();
951  file.close();
952 
953  KDesktopFile df(tempFileName);
954  KConfigGroup group = df.desktopGroup();
955  group.writeEntry("Icon", KProtocolInfo::icon(linkUrl.protocol()));
956  group.writePathEntry("URL", linkUrl.prettyUrl());
957  df.sync();
958 
959  m_copyData.m_src = tempFileName;
960  m_copyData.m_tempFileToDelete = tempFileName;
961 
962  executeStrategy();
963 }
964 
965 KNewFileMenu::KNewFileMenu(KActionCollection* collection, const QString& name, QObject* parent)
966  : KActionMenu(KIcon("document-new"), i18n("Create New"), parent),
967  d(new KNewFileMenuPrivate(this))
968 {
969  // Don't fill the menu yet
970  // We'll do that in checkUpToDate (should be connected to aboutToShow)
971  d->m_newMenuGroup = new QActionGroup(this);
972  connect(d->m_newMenuGroup, SIGNAL(triggered(QAction*)), this, SLOT(_k_slotActionTriggered(QAction*)));
973  d->m_actionCollection = collection;
974  d->m_parentWidget = qobject_cast<QWidget*>(parent);
975  d->m_newDirAction = 0;
976 
977  d->m_actionCollection->addAction(name, this);
978 
979  d->m_menuDev = new KActionMenu(KIcon("drive-removable-media"), i18n("Link to Device"), this);
980 }
981 
982 KNewFileMenu::~KNewFileMenu()
983 {
984  //kDebug(1203) << this;
985  delete d;
986 }
987 
988 void KNewFileMenu::checkUpToDate()
989 {
990  KNewFileMenuSingleton* s = kNewMenuGlobals;
991  //kDebug(1203) << this << "m_menuItemsVersion=" << d->m_menuItemsVersion
992  // << "s->templatesVersion=" << s->templatesVersion;
993  if (d->m_menuItemsVersion < s->templatesVersion || s->templatesVersion == 0) {
994  //kDebug(1203) << "recreating actions";
995  // We need to clean up the action collection
996  // We look for our actions using the group
997  foreach (QAction* action, d->m_newMenuGroup->actions())
998  delete action;
999 
1000  if (!s->templatesList) { // No templates list up to now
1001  s->templatesList = new KNewFileMenuSingleton::EntryList;
1002  d->_k_slotFillTemplates();
1003  s->parseFiles();
1004  }
1005 
1006  // This might have been already done for other popupmenus,
1007  // that's the point in s->filesParsed.
1008  if (!s->filesParsed) {
1009  s->parseFiles();
1010  }
1011 
1012  d->fillMenu();
1013 
1014  d->m_menuItemsVersion = s->templatesVersion;
1015  }
1016 }
1017 
1018 void KNewFileMenu::createDirectory()
1019 {
1020  if (d->m_popupFiles.isEmpty())
1021  return;
1022 
1023  KUrl baseUrl = d->m_popupFiles.first();
1024  QString name = d->m_text.isEmpty()? i18nc("Default name for a new folder", "New Folder") :
1025  d->m_text;
1026 
1027  if (baseUrl.isLocalFile() && QFileInfo(baseUrl.toLocalFile(KUrl::AddTrailingSlash) + name).exists())
1028  name = KIO::RenameDialog::suggestName(baseUrl, name);
1029 
1030  KDialog* fileDialog = new KDialog(d->m_parentWidget);
1031  fileDialog->setModal(isModal());
1032  fileDialog->setAttribute(Qt::WA_DeleteOnClose);
1033  fileDialog->setButtons(KDialog::Ok | KDialog::Cancel);
1034  fileDialog->setCaption(i18nc("@title:window", "New Folder"));
1035 
1036  QWidget* mainWidget = new QWidget(fileDialog);
1037  QVBoxLayout *layout = new QVBoxLayout(mainWidget);
1038  QLabel *label = new QLabel(i18n("Create new folder in:\n%1", baseUrl.pathOrUrl()));
1039 
1040  // We don't set the text of lineEdit in its constructor because the clear button would not be shown then.
1041  // It seems that setClearButtonShown(true) must be called *before* the text is set to make it work.
1042  // TODO: should probably be investigated and fixed in KLineEdit.
1043  KLineEdit *lineEdit = new KLineEdit;
1044  lineEdit->setClearButtonShown(true);
1045  lineEdit->setText(name);
1046 
1047  d->_k_slotTextChanged(name); // have to save string in d->m_text in case user does not touch dialog
1048  connect(lineEdit, SIGNAL(textChanged(QString)), this, SLOT(_k_slotTextChanged(QString)));
1049  layout->addWidget(label);
1050  layout->addWidget(lineEdit);
1051 
1052  fileDialog->setMainWidget(mainWidget);
1053  connect(fileDialog, SIGNAL(accepted()), this, SLOT(_k_slotCreateDirectory()));
1054  connect(fileDialog, SIGNAL(rejected()), this, SLOT(_k_slotAbortDialog()));
1055 
1056  d->m_fileDialog = fileDialog;
1057 
1058  fileDialog->show();
1059  lineEdit->selectAll();
1060  lineEdit->setFocus();
1061 }
1062 
1063 bool KNewFileMenu::isModal() const
1064 {
1065  return d->m_modal;
1066 }
1067 
1068 KUrl::List KNewFileMenu::popupFiles() const
1069 {
1070  return d->m_popupFiles;
1071 }
1072 
1073 void KNewFileMenu::setModal(bool modal)
1074 {
1075  d->m_modal = modal;
1076 }
1077 
1078 void KNewFileMenu::setPopupFiles(const KUrl::List& files)
1079 {
1080  d->m_popupFiles = files;
1081  if (files.isEmpty()) {
1082  d->m_newMenuGroup->setEnabled(false);
1083  } else {
1084  KUrl firstUrl = files.first();
1085  if (KProtocolManager::supportsWriting(firstUrl)) {
1086  d->m_newMenuGroup->setEnabled(true);
1087  if (d->m_newDirAction) {
1088  d->m_newDirAction->setEnabled(KProtocolManager::supportsMakeDir(firstUrl)); // e.g. trash:/
1089  }
1090  } else {
1091  d->m_newMenuGroup->setEnabled(true);
1092  }
1093  }
1094 }
1095 
1096 
1097 void KNewFileMenu::setParentWidget(QWidget* parentWidget)
1098 {
1099  d->m_parentWidget = parentWidget;
1100 }
1101 
1102 void KNewFileMenu::setSupportedMimeTypes(const QStringList& mime)
1103 {
1104  d->m_supportedMimeTypes = mime;
1105 }
1106 
1107 void KNewFileMenu::setViewShowsHiddenFiles(bool b)
1108 {
1109  d->m_viewShowsHiddenFiles = b;
1110 }
1111 
1112 void KNewFileMenu::slotResult(KJob * job)
1113 {
1114  if (job->error()) {
1115  static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
1116  } else {
1117  // Was this a copy or a mkdir?
1118  KIO::CopyJob* copyJob = ::qobject_cast<KIO::CopyJob*>(job);
1119  if (copyJob) {
1120  const KUrl destUrl = copyJob->destUrl();
1121  const KUrl localUrl = KIO::NetAccess::mostLocalUrl(destUrl, d->m_parentWidget);
1122  if (localUrl.isLocalFile()) {
1123  // Normal (local) file. Need to "touch" it, kio_file copied the mtime.
1124  (void) ::utime(QFile::encodeName(localUrl.toLocalFile()), 0);
1125  }
1126  emit fileCreated(destUrl);
1127  } else if (KIO::SimpleJob* simpleJob = ::qobject_cast<KIO::SimpleJob*>(job)) {
1128  // Can be mkdir or symlink
1129  if (simpleJob->property("isMkdirJob").toBool() == true) {
1130  kDebug() << "Emit directoryCreated" << simpleJob->url();
1131  emit directoryCreated(simpleJob->url());
1132  } else {
1133  emit fileCreated(simpleJob->url());
1134  }
1135  }
1136  }
1137  if (!d->m_tempFileToDelete.isEmpty())
1138  QFile::remove(d->m_tempFileToDelete);
1139 }
1140 
1141 
1142 QStringList KNewFileMenu::supportedMimeTypes() const
1143 {
1144  return d->m_supportedMimeTypes;
1145 }
1146 
1147 
1148 #include "knewfilemenu.moc"
1149 
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Wed Mar 20 2013 07:24:51 by doxygen 1.8.3.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KFile

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

kdelibs-4.10.1 API Reference

Skip menu "kdelibs-4.10.1 API Reference"
  • 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
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal