1/*
2 Copyright (c) 2000 Matthias Elter <elter@kde.org>
3 Copyright (c) 2003 Daniel Molkentin <molkentin@kde.org>
4 Copyright (c) 2003,2006 Matthias Kretz <kretz@kde.org>
5 Copyright (c) 2004 Frans Englich <frans.englich@telia.com>
6 Copyright (c) 2006 Tobias Koenig <tokoe@kde.org>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22
23*/
24
25#include "kcmultidialog.h"
26#include "kcmultidialog_p.h"
27
28#include <QtCore/QStringList>
29#include <QtCore/QProcess>
30
31#include <kauthorized.h>
32#include <kguiitem.h>
33#include <khbox.h>
34#include <kicon.h>
35#include <klocale.h>
36#include <kpagewidgetmodel.h>
37#include <kpushbutton.h>
38#include <ktoolinvocation.h>
39#include <kdebug.h>
40#include <kmessagebox.h>
41
42#include "auth/kauthaction.h"
43
44#include "kcolorscheme.h"
45
46#include "kcmoduleloader.h"
47#include "kcmoduleproxy.h"
48
49bool KCMultiDialogPrivate::resolveChanges(KCModuleProxy *currentProxy)
50{
51 Q_Q(KCMultiDialog);
52 if( !currentProxy || !currentProxy->changed() ) {
53 return true;
54 }
55
56 // Let the user decide
57 const int queryUser = KMessageBox::warningYesNoCancel(
58 q,
59 i18n("The settings of the current module have changed.\n"
60 "Do you want to apply the changes or discard them?"),
61 i18n("Apply Settings"),
62 KStandardGuiItem::apply(),
63 KStandardGuiItem::discard(),
64 KStandardGuiItem::cancel());
65
66 switch (queryUser) {
67 case KMessageBox::Yes:
68 return moduleSave(currentProxy);
69
70 case KMessageBox::No:
71 currentProxy->load();
72 return true;
73
74 case KMessageBox::Cancel:
75 return false;
76
77 default:
78 Q_ASSERT(false);
79 return false;
80 }
81}
82
83void KCMultiDialogPrivate::_k_slotCurrentPageChanged( KPageWidgetItem *current, KPageWidgetItem *previous )
84{
85 Q_Q(KCMultiDialog);
86 kDebug(710);
87
88 q->blockSignals(true);
89 q->setCurrentPage(previous);
90
91 KCModuleProxy *previousModule = 0;
92 for ( int i = 0; i < modules.count(); ++i ) {
93 if ( modules[ i ].item == previous ) {
94 previousModule = modules[ i ].kcm;
95 break;
96 }
97 }
98
99 if( resolveChanges(previousModule) ) {
100 q->setCurrentPage(current);
101 }
102 q->blockSignals(false);
103
104 // We need to get the state of the now active module
105 _k_clientChanged();
106}
107
108void KCMultiDialogPrivate::_k_clientChanged()
109{
110 Q_Q(KCMultiDialog);
111 kDebug(710);
112 // Get the current module
113 KCModuleProxy *activeModule = 0;
114 for ( int i = 0; i < modules.count(); ++i ) {
115 if ( modules[ i ].item == q->currentPage() ) {
116 activeModule = modules[ i ].kcm;
117 break;
118 }
119 }
120
121 bool change = false;
122 if (activeModule) {
123 change = activeModule->changed();
124
125 if (q->button(KDialog::Apply)) {
126 q->disconnect(q, SIGNAL(applyClicked()), q, SLOT(slotApplyClicked()));
127 q->disconnect(q->button(KDialog::Apply), SIGNAL(authorized(KAuth::Action*)), q, SLOT(slotApplyClicked()));
128 q->button(KDialog::Apply)->setEnabled(change);
129 }
130
131 if (q->button(KDialog::Ok)) {
132 q->disconnect(q, SIGNAL(okClicked()), q, SLOT(slotOkClicked()));
133 q->disconnect(q->button(KDialog::Ok), SIGNAL(authorized(KAuth::Action*)), q, SLOT(slotOkClicked()));
134 }
135
136 if (activeModule->realModule()->needsAuthorization()) {
137 if (q->button(KDialog::Apply)) {
138 q->button(KDialog::Apply)->setAuthAction(activeModule->realModule()->authAction());
139 activeModule->realModule()->authAction()->setParentWidget(activeModule->realModule());
140 q->connect(q->button(KDialog::Apply), SIGNAL(authorized(KAuth::Action*)), SLOT(slotApplyClicked()));
141 }
142
143 if (q->button(KDialog::Ok)) {
144 q->button(KDialog::Ok)->setAuthAction(activeModule->realModule()->authAction());
145 activeModule->realModule()->authAction()->setParentWidget(activeModule->realModule());
146 q->connect(q->button(KDialog::Ok), SIGNAL(authorized(KAuth::Action*)), SLOT(slotOkClicked()));
147 }
148 } else {
149 if (q->button(KDialog::Apply)) {
150 q->connect(q, SIGNAL(applyClicked()), SLOT(slotApplyClicked()));
151 q->button(KDialog::Apply)->setAuthAction(0);
152 }
153
154 if (q->button(KDialog::Ok)) {
155 q->connect(q, SIGNAL(okClicked()), SLOT(slotOkClicked()));
156 q->button(KDialog::Ok)->setAuthAction(0);
157 }
158 }
159 }
160
161 if (q->button(KDialog::Reset)) {
162 q->button(KDialog::Reset)->setEnabled(change);
163 }
164
165 if (q->button(KDialog::Apply)) {
166 q->button(KDialog::Apply)->setEnabled(change);
167 }
168
169 if (activeModule) {
170 q->enableButton(KDialog::Help, activeModule->buttons() & KCModule::Help);
171 q->enableButton(KDialog::Default, activeModule->buttons() & KCModule::Default);
172 }
173}
174
175void KCMultiDialogPrivate::_k_updateHeader(bool use, const QString &message)
176{
177 Q_Q(KCMultiDialog);
178 KPageWidgetItem *item = q->currentPage();
179 KCModuleProxy *kcm = qobject_cast<KCModuleProxy*>(item->widget());
180
181 if (use) {
182 item->setHeader( "<b>"+kcm->moduleInfo().comment() + "</b><br><i>" +
183 message + "</i>" );
184 item->setIcon( KIcon( kcm->moduleInfo().icon(), 0, QStringList() << "dialog-warning" ) );
185 } else {
186 item->setHeader( kcm->moduleInfo().comment() );
187 item->setIcon( KIcon( kcm->moduleInfo().icon() ) );
188 }
189}
190
191void KCMultiDialogPrivate::_k_dialogClosed()
192{
193 kDebug(710) ;
194
195 /**
196 * If we don't delete them, the DBUS registration stays, and trying to load the KCMs
197 * in other situations will lead to "module already loaded in Foo," while to the user
198 * doesn't appear so(the dialog is hidden)
199 */
200 for ( int i = 0; i < modules.count(); ++i )
201 modules[ i ].kcm->deleteClient();
202}
203
204void KCMultiDialogPrivate::init()
205{
206 Q_Q(KCMultiDialog);
207 q->setFaceType(KPageDialog::Auto);
208 q->setCaption(i18n("Configure"));
209 q->setButtons(KDialog::Help | KDialog::Default |KDialog::Cancel | KDialog::Apply | KDialog::Ok | KDialog::Reset);
210
211 q->setModal(false);
212
213 q->connect(q, SIGNAL(finished()), SLOT(_k_dialogClosed()));
214 q->connect(q, SIGNAL(applyClicked()), SLOT(slotApplyClicked()));
215 q->connect(q, SIGNAL(okClicked()), SLOT(slotOkClicked()));
216 q->connect(q, SIGNAL(defaultClicked()), SLOT(slotDefaultClicked()));
217 q->connect(q, SIGNAL(helpClicked()), SLOT(slotHelpClicked()));
218 q->connect(q, SIGNAL(user1Clicked()), SLOT(slotUser1Clicked()));
219 q->connect(q, SIGNAL(resetClicked()), SLOT(slotUser1Clicked()));
220
221 q->connect(q, SIGNAL(currentPageChanged(KPageWidgetItem*,KPageWidgetItem*)),
222 SLOT(_k_slotCurrentPageChanged(KPageWidgetItem*,KPageWidgetItem*)));
223
224 q->setInitialSize(QSize(800, 550));
225}
226
227KCMultiDialog::KCMultiDialog( QWidget *parent )
228 : KPageDialog(*new KCMultiDialogPrivate, NULL, parent)
229{
230 d_func()->init();
231}
232
233KCMultiDialog::KCMultiDialog(KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags)
234 : KPageDialog(*new KCMultiDialogPrivate, pageWidget, parent, flags)
235{
236 d_func()->init();
237}
238
239KCMultiDialog::KCMultiDialog(KCMultiDialogPrivate &dd, KPageWidget *pageWidget, QWidget *parent, Qt::WindowFlags flags)
240 : KPageDialog(dd, pageWidget, parent, flags)
241{
242 d_func()->init();
243}
244
245KCMultiDialog::~KCMultiDialog()
246{
247}
248
249void KCMultiDialog::slotDefaultClicked()
250{
251 Q_D(KCMultiDialog);
252 const KPageWidgetItem *item = currentPage();
253 if ( !item )
254 return;
255
256 for ( int i = 0; i < d->modules.count(); ++i ) {
257 if ( d->modules[ i ].item == item ) {
258 d->modules[ i ].kcm->defaults();
259 d->_k_clientChanged();
260 return;
261 }
262 }
263}
264
265void KCMultiDialog::slotUser1Clicked()
266{
267 const KPageWidgetItem *item = currentPage();
268 if ( !item )
269 return;
270
271 Q_D(KCMultiDialog);
272 for ( int i = 0; i < d->modules.count(); ++i ) {
273 if ( d->modules[ i ].item == item ) {
274 d->modules[ i ].kcm->load();
275 d->_k_clientChanged();
276 return;
277 }
278 }
279}
280
281bool KCMultiDialogPrivate::moduleSave(KCModuleProxy *module)
282{
283 if( !module ) {
284 return false;
285 }
286
287 module->save();
288 return true;
289}
290
291void KCMultiDialogPrivate::apply()
292{
293 Q_Q(KCMultiDialog);
294 QStringList updatedComponents;
295
296 foreach (const CreatedModule &module, modules) {
297 KCModuleProxy *proxy = module.kcm;
298
299 if (proxy->changed()) {
300 proxy->save();
301 /**
302 * Add name of the components the kcm belongs to the list
303 * of updated components.
304 */
305 const QStringList componentNames = module.componentNames;
306 foreach (const QString &componentName, module.componentNames) {
307 if (!updatedComponents.contains(componentName)) {
308 updatedComponents.append(componentName);
309 }
310 }
311 }
312 }
313
314 // Send the configCommitted signal for every updated component.
315 foreach (const QString &name, updatedComponents) {
316 emit q->configCommitted(name.toLatin1());
317 }
318
319 emit q->configCommitted();
320}
321
322void KCMultiDialog::slotApplyClicked()
323{
324 setButtonFocus( Apply );
325
326 d_func()->apply();
327}
328
329
330void KCMultiDialog::slotOkClicked()
331{
332 setButtonFocus( Ok );
333
334 d_func()->apply();
335 accept();
336}
337
338void KCMultiDialog::slotHelpClicked()
339{
340 const KPageWidgetItem *item = currentPage();
341 if ( !item )
342 return;
343
344 Q_D(KCMultiDialog);
345 QString docPath;
346 for ( int i = 0; i < d->modules.count(); ++i ) {
347 if ( d->modules[ i ].item == item ) {
348 docPath = d->modules[ i ].kcm->moduleInfo().docPath();
349 break;
350 }
351 }
352
353 KUrl docUrl( KUrl( "help:/" ), docPath );
354 if ( docUrl.protocol() == "help" || docUrl.protocol() == "man" || docUrl.protocol() == "info" ) {
355 QProcess::startDetached("khelpcenter", QStringList() << docUrl.url());
356 } else {
357 KToolInvocation::invokeBrowser( docUrl.url() );
358 }
359}
360
361
362KPageWidgetItem* KCMultiDialog::addModule( const QString& path, const QStringList& args )
363{
364 QString complete = path;
365
366 if ( !path.endsWith( QLatin1String(".desktop") ) )
367 complete += ".desktop";
368
369 KService::Ptr service = KService::serviceByStorageId( complete );
370
371 return addModule( KCModuleInfo( service ), 0, args );
372}
373
374KPageWidgetItem* KCMultiDialog::addModule( const KCModuleInfo& moduleInfo,
375 KPageWidgetItem *parentItem, const QStringList& args )
376{
377 if ( !moduleInfo.service() )
378 return 0;
379
380 //KAuthorized::authorizeControlModule( moduleInfo.service()->menuId() ) is
381 //checked in noDisplay already
382 if ( moduleInfo.service()->noDisplay() )
383 return 0;
384
385 KCModuleProxy *kcm = new KCModuleProxy(moduleInfo, 0, args);
386
387 kDebug(710) << moduleInfo.moduleName();
388 KPageWidgetItem *item = new KPageWidgetItem(kcm, moduleInfo.moduleName());
389
390 if (kcm->useRootOnlyMessage()) {
391 item->setHeader( "<b>"+moduleInfo.comment() + "</b><br><i>" + kcm->rootOnlyMessage() + "</i>" );
392 item->setIcon( KIcon( moduleInfo.icon(), 0, QStringList() << "dialog-warning" ) );
393 } else {
394 item->setHeader( moduleInfo.comment() );
395 item->setIcon( KIcon( moduleInfo.icon() ) );
396 }
397 item->setProperty("_k_weight", moduleInfo.weight());
398
399 bool updateCurrentPage = false;
400 const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(pageWidget()->model());
401 Q_ASSERT(model);
402 if (parentItem) {
403 const QModelIndex parentIndex = model->index(parentItem);
404 const int siblingCount = model->rowCount(parentIndex);
405 int row = 0;
406 for (; row < siblingCount; ++row) {
407 KPageWidgetItem *siblingItem = model->item(parentIndex.child(row, 0));
408 if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) {
409 // the item we found is heavier than the new module
410 kDebug(710) << "adding KCM " << item->name() << " before " << siblingItem->name();
411 insertPage(siblingItem, item);
412 break;
413 }
414 }
415 if (row >= siblingCount) {
416 // the new module is either the first or the heaviest item
417 kDebug(710) << "adding KCM " << item->name() << " with parent " << parentItem->name();
418 addSubPage(parentItem, item);
419 }
420 } else {
421 const int siblingCount = model->rowCount();
422 int row = 0;
423 for (; row < siblingCount; ++row) {
424 KPageWidgetItem *siblingItem = model->item(model->index(row, 0));
425 if (siblingItem->property("_k_weight").toInt() > moduleInfo.weight()) {
426 // the item we found is heavier than the new module
427 kDebug(710) << "adding KCM " << item->name() << " before " << siblingItem->name();
428 insertPage(siblingItem, item);
429 if ( siblingItem == currentPage() )
430 updateCurrentPage = true;
431
432 break;
433 }
434 }
435 if (row == siblingCount) {
436 // the new module is either the first or the heaviest item
437 kDebug(710) << "adding KCM " << item->name() << " at the top level";
438 addPage(item);
439 }
440 }
441
442 connect(kcm, SIGNAL(changed(bool)), this, SLOT(_k_clientChanged()));
443 connect(kcm->realModule(), SIGNAL(rootOnlyMessageChanged(bool,QString)), this, SLOT(_k_updateHeader(bool,QString)));
444
445 Q_D(KCMultiDialog);
446 KCMultiDialogPrivate::CreatedModule cm;
447 cm.kcm = kcm;
448 cm.item = item;
449 cm.componentNames = moduleInfo.service()->property( "X-KDE-ParentComponents" ).toStringList();
450 d->modules.append( cm );
451
452 if ( d->modules.count() == 1 || updateCurrentPage )
453 {
454 setCurrentPage( item );
455 d->_k_clientChanged();
456 }
457 return item;
458}
459
460void KCMultiDialog::clear()
461{
462 Q_D(KCMultiDialog);
463 kDebug( 710 ) ;
464
465 for ( int i = 0; i < d->modules.count(); ++i ) {
466 removePage( d->modules[ i ].item );
467 delete d->modules[ i ].kcm;
468 }
469
470 d->modules.clear();
471
472 d->_k_clientChanged();
473}
474
475void KCMultiDialog::setButtons(ButtonCodes buttonMask)
476{
477 KPageDialog::setButtons(buttonMask);
478
479 // Set Auto-Default mode ( KDE Bug #211187 )
480 if (buttonMask & KDialog::Ok) {
481 button(KDialog::Ok)->setAutoDefault(true);
482 }
483 if (buttonMask & KDialog::Apply) {
484 button(KDialog::Apply)->setAutoDefault(true);
485 }
486 if (buttonMask & KDialog::Default) {
487 button(KDialog::Default)->setAutoDefault(true);
488 }
489 if (buttonMask & KDialog::Reset) {
490 button(KDialog::Reset)->setAutoDefault(true);
491 }
492 if (buttonMask & KDialog::Cancel) {
493 button(KDialog::Cancel)->setAutoDefault(true);
494 }
495 if (buttonMask & KDialog::Help) {
496 button(KDialog::Help)->setAutoDefault(true);
497 }
498
499 // Old Reset Button
500 enableButton(KDialog::User1, false);
501 enableButton(KDialog::Reset, false);
502 enableButton(KDialog::Apply, false);
503}
504
505
506#include "kcmultidialog.moc"
507