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
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
38 #include "globalpaths.h"
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>
51 #include <kconfiggroup.h>
52 #include <kcustommenueditor.h>
54 #include <kfileitem.h>
55 #include <kglobalsettings.h>
56 #include <kio/copyjob.h>
57 #include <kio/deletejob.h>
59 #include <kio/jobuidelegate.h>
61 #include <kmessagebox.h>
62 #include <kmimetype.h>
63 #include <kstandarddirs.h>
64 #include <kurlrequester.h>
66 #include "konqkcmfactory.h"
69 #include <config-apps.h>
71 //-----------------------------------------------------------------------------
73 DesktopPathConfig::DesktopPathConfig(QWidget
*parent
, const QVariantList
&)
74 : KCModule( KonqKcmFactory::componentData(), parent
)
77 QGridLayout
*lay
= new QGridLayout(this);
78 lay
->setSpacing(KDialog::spacingHint());
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."));
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
);
144 void DesktopPathConfig::load()
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() );
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
)))
176 if (!path
.startsWith(homeDir
))
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"));
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] == '/')
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";
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
232 kDebug() << "desktopURL=" << desktopURL
;
233 QString urlDesktop
= urDesktop
->url().path();
234 if ( !urlDesktop
.endsWith('/'))
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)
252 KUrl futureAutostartURL
;
253 futureAutostartURL
.setPath( urlDesktop
+ "Autostart/" );
254 if ( newAutostartURL
.equals( futureAutostartURL
, KUrl::CompareWithoutTrailingSlash
) )
255 autostartMoved
= true;
257 autostartMoved
= moveDir( KUrl( KGlobalSettings::autostartPath() ), KUrl( urAutostart
->url() ), i18n("Autostart") );
261 if ( moveDir( KUrl( KGlobalSettings::desktopPath() ), KUrl( urlDesktop
), i18n("Desktop") ) )
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
) );
272 if ( !newAutostartURL
.equals( autostartURL
, KUrl::CompareWithoutTrailingSlash
) )
275 autostartMoved
= moveDir( KUrl( KGlobalSettings::autostartPath() ), KUrl( urAutostart
->url() ), i18n("Autostart") );
278 configGroup
.writePathEntry( "Autostart", urAutostart
->url().path(), KConfigBase::Normal
| KConfigBase::Global
);
285 if (xdgSavePath(urDocument
, KGlobalSettings::documentPath(), "XDG_DOCUMENTS_DIR", i18n("Documents")))
288 if (xdgSavePath(urDownload
, KGlobalSettings::downloadPath(), "XDG_DOWNLOAD_DIR", i18n("Downloads")))
291 if (xdgSavePath(urMovie
, KGlobalSettings::videosPath(), "XDG_VIDEOS_DIR", i18n("Movies")))
294 if (xdgSavePath(urPicture
, KGlobalSettings::picturesPath(), "XDG_PICTURES_DIR", i18n("Pictures")))
297 if (xdgSavePath(urMusic
, KGlobalSettings::musicPath(), "XDG_MUSIC_DIR", i18n("Music")))
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
));
326 bool DesktopPathConfig::moveDir( const KUrl
& src
, const KUrl
& dest
, const QString
& type
)
328 if (!src
.isLocalFile() || !dest
.isLocalFile())
330 if (!QFile::exists(src
.path()))
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...)
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
356 QDir().rmdir(src
.path()); // hopefully it's empty by now
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
*)));
369 kDebug() << "DesktopPathConfig::slotResult returning " << 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() == "..") {
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
)
393 if ( job
->error() != KIO::ERR_DOES_NOT_EXIST
)
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"