Fix crash if key bindings specified in profile cannot be found. Improve
[personal-kdebase.git] / apps / konqueror / settings / konq / globalpaths.cpp
blob4472216e6e0572d8decc46e66a7d74d08e20fa5c
1 /**
2 * Copyright (c) Martin R. Jones 1996
3 * Copyright (c) Bernd Wuebben 1998
4 * Copyright (c) Christian Tibirna 1998
5 * Copyright 1998-2007 David Faure <faure@kde.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 // "Desktop Options" Tab for KDesktop configuration
26 // (c) Martin R. Jones 1996
27 // (c) Bernd Wuebben 1998
29 // Layouts
30 // (c) Christian Tibirna 1998
31 // Port to KControl, split from Misc Tab, Port to KControl2
32 // (c) David Faure 1998
33 // Desktop menus, paths
34 // (c) David Faure 2000
37 // Own
38 #include "globalpaths.h"
40 // Qt
41 #include <QtGui/QCheckBox>
42 #include <QtGui/QComboBox>
43 #include <QtGui/QLabel>
44 #include <QtGui/QLayout>
45 #include <QtGui/QGridLayout>
46 #include <QtGui/QDesktopWidget>
47 #include <QtGui/QApplication>
48 #include <QtDBus/QtDBus>
50 // KDE
51 #include <kconfiggroup.h>
52 #include <kcustommenueditor.h>
53 #include <kdebug.h>
54 #include <kfileitem.h>
55 #include <kglobalsettings.h>
56 #include <kio/copyjob.h>
57 #include <kio/deletejob.h>
58 #include <kio/job.h>
59 #include <kio/jobuidelegate.h>
60 #include <klocale.h>
61 #include <kmessagebox.h>
62 #include <kmimetype.h>
63 #include <kstandarddirs.h>
64 #include <kurlrequester.h>
66 #include "konqkcmfactory.h"
68 // Local
69 #include <config-apps.h>
71 //-----------------------------------------------------------------------------
73 DesktopPathConfig::DesktopPathConfig(QWidget *parent, const QVariantList &)
74 : KCModule( KonqKcmFactory::componentData(), parent )
76 int row = 0;
77 QGridLayout *lay = new QGridLayout(this);
78 lay->setSpacing(KDialog::spacingHint());
79 lay->setMargin(0);
81 lay->setColumnStretch(0,0);
82 lay->setColumnStretch(1,0);
83 lay->setColumnStretch(2,10);
86 setQuickHelp( i18n("<h1>Paths</h1>\n"
87 "This module allows you to choose where in the filesystem the "
88 "files on your desktop should be stored.\n"
89 "Use the \"Whats This?\" (Shift+F1) to get help on specific options."));
91 urDesktop = addRow(++row, lay, i18n("Desktop path:"),
92 i18n("This folder contains all the files"
93 " which you see on your desktop. You can change the location of this"
94 " folder if you want to, and the contents will move automatically"
95 " to the new location as well."));
97 urAutostart = addRow(++row, lay, i18n("Autostart path:"),
98 i18n("This folder contains applications or"
99 " links to applications (shortcuts) that you want to have started"
100 " automatically whenever KDE starts. You can change the location of this"
101 " folder if you want to, and the contents will move automatically"
102 " to the new location as well."));
104 urDocument = addRow(++row, lay, i18n("Documents path:"),
105 i18n("This folder will be used by default to "
106 "load or save documents from or to."));
108 urDownload = addRow(++row, lay, i18n("Downloads path:"),
109 i18n("This folder will be used by default to "
110 "save your downloaded items."));
112 urMovie = addRow(++row, lay, i18n("Movies path:"),
113 i18n("This folder will be used by default to "
114 "load or save movies from or to."));
116 urPicture = addRow(++row, lay, i18n("Pictures path:"),
117 i18n("This folder will be used by default to "
118 "load or save pictures from or to."));
120 urMusic = addRow(++row, lay, i18n("Music path:"),
121 i18n("This folder will be used by default to "
122 "load or save music from or to."));
124 row++;
126 lay->setRowStretch(row, 10); // last line grows
129 KUrlRequester* DesktopPathConfig::addRow(int row, QGridLayout *lay, const QString& label, const QString& whatsThis)
131 QLabel* tmpLabel = new QLabel(label, this);
132 tmpLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
133 lay->addWidget(tmpLabel, row, 0);
134 KUrlRequester* ur = new KUrlRequester(this);
135 ur->setMode(KFile::Directory | KFile::LocalOnly);
136 tmpLabel->setBuddy(ur);
137 lay->addWidget(ur, row, 1, 1, 2);
138 connect(ur, SIGNAL(textChanged(QString)), this, SLOT(changed()));
139 tmpLabel->setWhatsThis(whatsThis);
140 ur->setWhatsThis(whatsThis);
141 return ur;
144 void DesktopPathConfig::load()
146 // Desktop Paths
147 urDesktop->setPath( KGlobalSettings::desktopPath() );
148 urAutostart->setPath( KGlobalSettings::autostartPath() );
149 urDocument->setPath( KGlobalSettings::documentPath() );
150 urDownload->setPath( KGlobalSettings::downloadPath() );
151 urMovie->setPath( KGlobalSettings::videosPath() );
152 urPicture->setPath( KGlobalSettings::picturesPath() );
153 urMusic->setPath( KGlobalSettings::musicPath() );
154 emit changed(false);
157 void DesktopPathConfig::defaults()
159 // Desktop Paths - keep defaults in sync with kglobalsettings.cpp
160 urDesktop->setPath( QDir::homePath() + "/Desktop" );
161 urAutostart->setPath( KGlobal::dirs()->localkdedir() + "Autostart/" );
162 urDocument->setPath( QDir::homePath() + "/Documents" );
163 urDownload->setPath( QDir::homePath() + "/Downloads" );
164 urMovie->setPath( QDir::homePath() + "/Movies" );
165 urPicture->setPath( QDir::homePath() + "/Pictures" );
166 urMusic->setPath( QDir::homePath() + "/Music" );
169 // the following method is copied from kdelibs/kdecore/config/kconfiggroup.cpp
170 static bool cleanHomeDirPath( QString &path, const QString &homeDir )
172 #ifdef Q_WS_WIN //safer
173 if (!QDir::convertSeparators(path).startsWith(QDir::convertSeparators(homeDir)))
174 return false;
175 #else
176 if (!path.startsWith(homeDir))
177 return false;
178 #endif
180 int len = homeDir.length();
181 // replace by "$HOME" if possible
182 if (len && (path.length() == len || path[len] == '/')) {
183 path.replace(0, len, QString::fromLatin1("$HOME"));
184 return true;
185 } else
186 return false;
189 static QString translatePath( QString path ) // krazy:exclude=passbyvalue
191 // keep only one single '/' at the beginning - needed for cleanHomeDirPath()
192 while (path[0] == '/' && path[1] == '/')
193 path.remove(0,1);
195 // we probably should escape any $ ` and \ characters that may occur in the path, but the Qt code that reads back
196 // the file doesn't unescape them so not much point in doing so
198 // All of the 3 following functions to return the user's home directory
199 // can return different paths. We have to test all them.
200 const QString homeDir0 = QFile::decodeName(qgetenv("HOME"));
201 const QString homeDir1 = QDir::homePath();
202 const QString homeDir2 = QDir(homeDir1).canonicalPath();
203 if (cleanHomeDirPath(path, homeDir0) ||
204 cleanHomeDirPath(path, homeDir1) ||
205 cleanHomeDirPath(path, homeDir2) ) {
206 // kDebug() << "Path was replaced\n";
209 return path;
212 void DesktopPathConfig::save()
214 KSharedConfig::Ptr config = KGlobal::config();
215 KConfigGroup configGroup( config, "Paths" );
217 bool pathChanged = false;
218 bool autostartMoved = false;
220 KUrl desktopURL( KGlobalSettings::desktopPath() );
222 KUrl autostartURL( KGlobalSettings::autostartPath() );
223 KUrl newAutostartURL = urAutostart->url();
225 if ( !urDesktop->url().equals( desktopURL, KUrl::CompareWithoutTrailingSlash ) )
227 // Test which other paths were inside this one (as it is by default)
228 // and for each, test where it should go.
229 // * Inside destination -> let them be moved with the desktop (but adjust name if necessary)
230 // * Not inside destination -> move first
231 // !!!
232 kDebug() << "desktopURL=" << desktopURL;
233 QString urlDesktop = urDesktop->url().path();
234 if ( !urlDesktop.endsWith('/'))
235 urlDesktop+='/';
237 if ( desktopURL.isParentOf( autostartURL ) )
239 kDebug() << "Autostart is on the desktop";
241 // Either the Autostart field wasn't changed (-> need to update it)
242 if ( newAutostartURL.equals( autostartURL, KUrl::CompareWithoutTrailingSlash ) )
244 // Hack. It could be in a subdir inside desktop. Hmmm... Argl.
245 urAutostart->setPath( urlDesktop + "Autostart/" );
246 kDebug() << "Autostart is moved with the desktop";
247 autostartMoved = true;
249 // or it has been changed (->need to move it from here)
250 else
252 KUrl futureAutostartURL;
253 futureAutostartURL.setPath( urlDesktop + "Autostart/" );
254 if ( newAutostartURL.equals( futureAutostartURL, KUrl::CompareWithoutTrailingSlash ) )
255 autostartMoved = true;
256 else
257 autostartMoved = moveDir( KUrl( KGlobalSettings::autostartPath() ), KUrl( urAutostart->url() ), i18n("Autostart") );
261 if ( moveDir( KUrl( KGlobalSettings::desktopPath() ), KUrl( urlDesktop ), i18n("Desktop") ) )
263 //save in XDG path
264 const QString userDirsFile(KGlobal::dirs()->localxdgconfdir() + QLatin1String("user-dirs.dirs"));
265 KConfig xdgUserConf( userDirsFile, KConfig::SimpleConfig );
266 KConfigGroup g( &xdgUserConf, "" );
267 g.writeEntry( "XDG_DESKTOP_DIR", translatePath( urlDesktop ) );
268 pathChanged = true;
272 if ( !newAutostartURL.equals( autostartURL, KUrl::CompareWithoutTrailingSlash ) )
274 if (!autostartMoved)
275 autostartMoved = moveDir( KUrl( KGlobalSettings::autostartPath() ), KUrl( urAutostart->url() ), i18n("Autostart") );
276 if (autostartMoved)
278 configGroup.writePathEntry( "Autostart", urAutostart->url().path(), KConfigBase::Normal | KConfigBase::Global );
279 pathChanged = true;
283 config->sync();
285 if (xdgSavePath(urDocument, KGlobalSettings::documentPath(), "XDG_DOCUMENTS_DIR", i18n("Documents")))
286 pathChanged = true;
288 if (xdgSavePath(urDownload, KGlobalSettings::downloadPath(), "XDG_DOWNLOAD_DIR", i18n("Downloads")))
289 pathChanged = true;
291 if (xdgSavePath(urMovie, KGlobalSettings::videosPath(), "XDG_VIDEOS_DIR", i18n("Movies")))
292 pathChanged = true;
294 if (xdgSavePath(urPicture, KGlobalSettings::picturesPath(), "XDG_PICTURES_DIR", i18n("Pictures")))
295 pathChanged = true;
297 if (xdgSavePath(urMusic, KGlobalSettings::musicPath(), "XDG_MUSIC_DIR", i18n("Music")))
298 pathChanged = true;
300 if (pathChanged) {
301 kDebug() << "sending message SettingsChanged";
302 KGlobalSettings::self()->emitChange(KGlobalSettings::SettingsChanged, KGlobalSettings::SETTINGS_PATHS);
306 bool DesktopPathConfig::xdgSavePath(KUrlRequester* ur, const KUrl& currentUrl, const char* xdgKey, const QString& type)
308 const KUrl newUrl = ur->url();
309 if (!newUrl.equals(currentUrl, KUrl::CompareWithoutTrailingSlash)) {
310 const QString path = newUrl.path();
311 if (!QDir(path).exists() && !KStandardDirs::makeDir(path)) {
312 KMessageBox::sorry(this, KIO::buildErrorString(KIO::ERR_COULD_NOT_MKDIR, path));
313 ur->setPath(currentUrl.path()); // revert
314 } else if (moveDir(currentUrl, newUrl, type)) {
315 //save in XDG user-dirs.dirs config file, this is where KGlobalSettings/QDesktopServices reads from.
316 const QString userDirsFile(KGlobal::dirs()->localxdgconfdir() + QLatin1String("user-dirs.dirs"));
317 KConfig xdgUserConf(userDirsFile, KConfig::SimpleConfig);
318 KConfigGroup g(&xdgUserConf, "");
319 g.writeEntry(xdgKey, translatePath(path));
320 return true;
323 return false;
326 bool DesktopPathConfig::moveDir( const KUrl & src, const KUrl & dest, const QString & type )
328 if (!src.isLocalFile() || !dest.isLocalFile())
329 return true;
330 if (!QFile::exists(src.path()))
331 return true;
332 m_ok = true;
333 // TODO: check if the src dir is empty? Nothing to move, then...
335 // Ask for confirmation before moving the files
336 if ( KMessageBox::questionYesNo( this, i18n("The path for '%1' has been changed.\nDo you want the files to be moved from '%2' to '%3'?",
337 type, src.path(), dest.path()), i18n("Confirmation Required"),
338 KGuiItem(i18nc("Move files from old to new place", "Move")),
339 KGuiItem(i18nc("Use the new directory but do not move files", "Do not Move")))
340 == KMessageBox::Yes )
342 if (QFile::exists(dest.path())) {
343 // Destination already exists -- should always be the case, for most types,
344 // but maybe not for the complex autostart case (to be checked...)
345 m_copyToDest = dest;
346 m_copyFromSrc = src;
347 KIO::ListJob* job = KIO::listDir( src );
348 job->setAutoDelete(false); // see <noautodelete> below
349 job->ui()->setWindow(this);
350 job->ui()->setAutoErrorHandlingEnabled(true);
351 connect(job, SIGNAL(entries(KIO::Job *,KIO::UDSEntryList)),
352 this, SLOT(slotEntries(KIO::Job *,KIO::UDSEntryList)));
353 // slotEntries will move every file/subdir individually into the dest
354 job->exec();
355 if (m_ok) {
356 QDir().rmdir(src.path()); // hopefully it's empty by now
358 delete job;
360 else
362 kDebug() << "Direct move from" << src << "to" << dest;
363 KIO::Job * job = KIO::move( src, dest );
364 job->ui()->setWindow(this);
365 connect(job, SIGNAL(result(KJob *)), this, SLOT(slotResult(KJob *)));
366 job->exec();
369 kDebug() << "DesktopPathConfig::slotResult returning " << m_ok;
370 return m_ok;
373 void DesktopPathConfig::slotEntries(KIO::Job*, const KIO::UDSEntryList& list)
375 QListIterator<KIO::UDSEntry> it(list);
376 while (it.hasNext()) {
377 KFileItem file(it.next(), m_copyFromSrc, true, true);
378 kDebug() << file.url();
379 if (file.url() == m_copyFromSrc || file.url().fileName() == "..") {
380 continue;
383 KIO::Job * moveJob = KIO::move(file.url(), m_copyToDest);
384 moveJob->ui()->setWindow(this);
385 connect(moveJob, SIGNAL(result(KJob *)), this, SLOT(slotResult(KJob *)));
386 moveJob->exec(); // sub-event loop here. <noautodelete>: the main job is not autodeleted because it would be deleted here
390 void DesktopPathConfig::slotResult( KJob * job )
392 if (job->error()) {
393 if ( job->error() != KIO::ERR_DOES_NOT_EXIST )
394 m_ok = false;
396 // If the source doesn't exist, no wonder we couldn't move the dir.
397 // In that case, trust the user and set the new setting in any case.
399 static_cast<KIO::Job*>(job)->ui()->showErrorMessage();
403 #include "globalpaths.moc"