kutils Library API Documentation

kcmoduleproxy.cpp

00001 /*  This file is part of the KDE project
00002     Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
00003     Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License version 2 as published by the Free Software Foundation.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017     Boston, MA 02111-1307, USA.
00018 */
00019 
00020 #include <qapplication.h>
00021 #include <qcursor.h>
00022 #include <qdatastream.h>
00023 #include <qevent.h>
00024 #include <qfileinfo.h>
00025 #include <qframe.h>
00026 #include <qlabel.h>
00027 #include <qlayout.h>
00028 #include <qpoint.h>
00029 #include <qscrollview.h>
00030 #include <qtextstream.h>
00031 #include <qvbox.h>
00032 #include <qwhatsthis.h>
00033 #include <qwidget.h>
00034 
00035 #include <dcopclient.h>
00036 #include <qxembed.h>
00037 
00038 #include <kapplication.h>
00039 #include <kaboutdata.h>
00040 #include <kcmodule.h>
00041 #include <kcmoduleinfo.h>
00042 #include <kcmoduleloader.h>
00043 #include <kdebug.h>
00044 #include <kdialog.h>
00045 #include <klocale.h>
00046 #include <kprocess.h>
00047 #include <kservice.h>
00048 #include <kstandarddirs.h>
00049 #include <kuser.h>
00050 
00051 #include <X11/Xlib.h>
00052 
00053 #include "kcmoduleproxy.h"
00054 #include "kcmoduleproxyIface.h"
00055 #include "kcmoduleproxyIfaceImpl.h"
00056 
00057 /***************************************************************/
00058 class KCModuleProxy::KCModuleProxyPrivate
00059 {
00060     public:
00061         KCModuleProxyPrivate( const KCModuleInfo & info )
00062             : args( 0 )
00063             , kcm( 0 )
00064             //, view( 0 )
00065             , embedWidget( 0 )
00066             , rootProcess ( 0 )
00067             , embedFrame ( 0 )
00068             , dcopObject( 0 )
00069             , dcopClient( 0 )
00070             , topLayout( 0 )
00071             , rootCommunicator( 0 )
00072             , rootInfo( 0 )
00073             , modInfo( info )
00074             , withFallback( false )
00075             , changed( false )
00076             , rootMode( false )
00077             , bogusOccupier( false )
00078             , isInitialized( false )
00079         {}
00080 
00081         ~KCModuleProxyPrivate()
00082         {
00083             delete embedFrame;
00084             delete dcopClient;
00085             delete dcopObject;
00086             delete embedWidget;
00087             delete rootCommunicator;
00088             delete rootProcess;
00089             delete rootInfo;
00090             delete kcm;
00091         }
00092 
00093         QStringList                         args;
00094         KCModule                            *kcm;
00095         QXEmbed                             *embedWidget;
00096         KProcess                            *rootProcess;
00097         QVBox                               *embedFrame;
00098         KCModuleProxyIfaceImpl              *dcopObject;
00099         DCOPClient                          *dcopClient;
00100         QVBoxLayout                         *topLayout; /* Contains QScrollView view, and root stuff */
00101         KCModuleProxyRootCommunicatorImpl   *rootCommunicator;
00102         QLabel                              *rootInfo;
00103         QCString                            dcopName;
00104         KCModuleInfo                        modInfo;
00105         bool                                withFallback;
00106         bool                                changed;
00107         bool                                rootMode;
00108         bool                                bogusOccupier;
00109         bool                                isInitialized;
00110 };
00111 /***************************************************************/
00112 
00113 
00114 
00115 /*
00116  TODO:
00117 
00118  - How KCModuleProxy behaves wrt memory leaks and behavior, when exiting
00119     from root mode is not tested, because no code make use of it. It needs 
00120     work, if it should be used.
00121 
00122  - Should write a document which outlines test cases, to avoid 
00123     regressions. This class is a hazard.
00124 
00125  - Two Layout problems in runAsRoot:
00126     * lblBusy doesn't show
00127     * d->kcm/d->rootInfo doesn't get it right when the user 
00128         presses cancel in the kdesu dialog
00129 
00130  - Resizing horizontally is contrained; minimum size is set somewhere. 
00131     It appears to be somehow derived from the module's size.
00132 
00133  - Prettify: set icon in KCMultiDialog.
00134 
00135  - Perhaps it's possible to link against kdesu such that 
00136     the dialog is in process?
00137 
00138  */
00139 /***************************************************************/
00140 KCModule * KCModuleProxy::realModule() const
00141 {
00142 
00143     /*
00144      * Note, don't call any function that calls realModule() since
00145      * that leads to an infinite loop.
00146      */
00147 
00148     kdDebug(711) << k_funcinfo << endl;
00149 
00150     /* Already loaded */
00151     if( d->kcm )
00152         return d->kcm;
00153 
00154     /* /We/ have no kcm, but kcmshell running with root prevs does.. */
00155     if( d->rootMode )
00156         return 0;
00157 
00158     QApplication::setOverrideCursor( Qt::WaitCursor );
00159     
00160     KCModuleProxy * that = const_cast<KCModuleProxy*>( this );
00161 
00162     if( !d->isInitialized )
00163     {
00164         d->dcopName = moduleInfo().handle().prepend("KCModuleProxy-").utf8();
00165         d->topLayout = new QVBoxLayout( that, 0, 0, "topLayout" );
00166 
00167         d->isInitialized = true;
00168     }
00169 
00170     if( !d->dcopClient )
00171         d->dcopClient = new DCOPClient();
00172 
00173     if( !d->dcopClient->isRegistered() )
00174         d->dcopClient->registerAs( d->dcopName, false );
00175 
00176     d->dcopClient->setAcceptCalls( true );
00177 
00178     if( d->dcopClient->appId() == d->dcopName || d->bogusOccupier )
00179     { /* We got the name we requested, because no one was before us, 
00180        * or, it was an random application which had picked that name */
00181         kdDebug(711) << "Module not already loaded, loading module" << endl;
00182 
00183         d->dcopObject = new KCModuleProxyIfaceImpl( d->dcopName, that );
00184 
00185         d->kcm = KCModuleLoader::loadModule( moduleInfo(), KCModuleLoader::Inline, d->withFallback,
00186             that, name(), d->args );
00187 
00188         connect( d->kcm, SIGNAL( changed( bool ) ),
00189                 SLOT(moduleChanged(bool)) );
00190         connect( d->kcm, SIGNAL( destroyed() ),
00191                 SLOT( moduleDestroyed() ) );
00192         connect( d->kcm, SIGNAL(quickHelpChanged()), 
00193                 SIGNAL(quickHelpChanged()));
00194         QWhatsThis::add( that, d->kcm->quickHelp() );
00195 
00196         d->topLayout->addWidget( d->kcm );
00197 
00198         if ( !d->rootInfo && /* If it's already done */
00199                 moduleInfo().needsRootPrivileges() /* root, anyone? */ && 
00200                 !KUser().isSuperUser() ) /* Not necessary if we're root */
00201         {
00202 
00203             d->rootInfo = new QLabel( that, "rootInfo" );
00204             d->topLayout->insertWidget( 0, d->rootInfo );
00205 
00206             d->rootInfo->setFrameShape(QFrame::Box);
00207             d->rootInfo->setFrameShadow(QFrame::Raised);
00208 
00209             const QString msg = d->kcm->rootOnlyMsg();
00210             if( msg.isEmpty() )
00211                 d->rootInfo->setText(i18n(
00212                       "<b>Changes in this section requires root access.</b><br />"
00213                       "Click the \"Administrator Mode\" button to "
00214                       "allow modifications."));
00215             else
00216                 d->rootInfo->setText(msg);
00217 
00218             QWhatsThis::add( d->rootInfo, i18n(
00219                   "This section requires special permissions, probably "
00220                   "for system-wide changes; therefore, it is "
00221                   "required that you provide the root password to be "
00222                   "able to change the module's properties. If "
00223                   "you do not provide the password, the module will be "
00224                   "disabled."));
00225         }
00226     }
00227     else
00228     {
00229         kdDebug(711) << "Module already loaded, loading KCMError" << endl;
00230 
00231         d->dcopClient->detach();
00232         /* Re-register as anonymous */
00233         d->dcopClient->attach();
00234 
00235         d->dcopClient->setNotifications( true );
00236         connect( d->dcopClient, SIGNAL( applicationRemoved( const QCString& )),
00237             SLOT( applicationRemoved( const QCString& )));
00238 
00239         /* Figure out the name of where the module is already loaded */
00240         QByteArray replyData, data;
00241         QCString replyType;
00242         QString result;
00243         QDataStream arg, stream( replyData, IO_ReadOnly );
00244 
00245         if( d->dcopClient->call( d->dcopName, d->dcopName, "applicationName()", 
00246                     data, replyType, replyData ))
00247         {
00248             stream >> result;
00249 
00250             d->kcm = KCModuleLoader::reportError( KCModuleLoader::Inline, 
00251                     i18n( "Argument is application name", "This configuration section is "
00252                         "already opened in %1" ).arg( result ), " ", that );
00253 
00254             d->topLayout->addWidget( d->kcm );
00255         }
00256         else
00257         {
00258             kdDebug(711) << "Calling KCModuleProxy's DCOP interface for fetching the name failed." << endl;
00259             d->bogusOccupier = true;
00260             QApplication::restoreOverrideCursor();
00261             return realModule();
00262         }
00263     }
00264 
00265     QApplication::restoreOverrideCursor();
00266 
00267     return d->kcm;
00268 }
00269 
00270 void KCModuleProxy::applicationRemoved( const QCString& app )
00271 {
00272     if( app == d->dcopName )
00273     {
00274         /* Violence: Get rid of KCMError & CO, so that 
00275          * realModule() attempts to reload the module */
00276         delete d->kcm;
00277         d->kcm = 0;
00278         d->dcopClient->setNotifications( false );
00279         realModule();
00280         d->kcm->show();
00281     }
00282 }
00283 
00284 void KCModuleProxy::showEvent( QShowEvent * ev )
00285 {
00286 
00287     kdDebug(711) << k_funcinfo << endl;
00288     ( void )realModule();
00289 
00290     /* We have no kcm, if we're in root mode */
00291     if( d->kcm )
00292         d->kcm->show();
00293 
00294     QWidget::showEvent( ev );
00295 
00296 }
00297 
00298 void KCModuleProxy::runAsRoot()
00299 {
00300     if ( !moduleInfo().needsRootPrivileges() )
00301         return;
00302 
00303     QApplication::setOverrideCursor( Qt::WaitCursor );
00304 
00305     delete d->rootProcess;
00306     delete d->embedWidget;
00307     delete d->embedFrame;
00308 
00309     d->embedFrame = new QVBox( this, "embedFrame" );
00310     d->embedFrame->setFrameStyle( QFrame::Box | QFrame::Raised );
00311 
00312     QPalette pal( red );
00313     pal.setColor( QColorGroup::Background, 
00314         colorGroup().background() );
00315     d->embedFrame->setPalette( pal );
00316     d->embedFrame->setLineWidth( 2 );
00317     d->embedFrame->setMidLineWidth( 2 );
00318     d->topLayout->addWidget(d->embedFrame,1);
00319 
00320     d->embedWidget = new QXEmbed( d->embedFrame, "embedWidget" );
00321 
00322     d->embedFrame->show();
00323 
00324     QLabel *lblBusy = new QLabel(i18n("<big>Loading...</big>"), d->embedWidget, "lblBusy" );
00325     lblBusy->setTextFormat(RichText);
00326     lblBusy->setAlignment(AlignCenter);
00327     lblBusy->setGeometry(0,0, d->kcm->width(), d->kcm->height());
00328     lblBusy->show();
00329 
00330     deleteClient();
00331     /* The DCOP registration is now gone, and it will occur again when kcmshell soon 
00332      * registers. Here's a race condition in other words, but how likely is that?
00333      *
00334      * - It's a user initiated action, which means the user have to do weird stuff, very
00335      *   quick.
00336      * - If the user _do_ manage to fsck up, the code will recover gracefully, see realModule().
00337      *
00338      * So no worry. At the end of this function, communication with 
00339      * the DCOP object is established.
00340      */
00341 
00342     /* Prepare the process to run the kcmshell */
00343     QString cmd = moduleInfo().service()->exec().stripWhiteSpace();
00344     if (cmd.left(5) == "kdesu")
00345     {
00346         cmd = cmd.remove(0,5).stripWhiteSpace();
00347 
00348         /* Remove all kdesu switches */
00349         while( cmd.length() > 1 && cmd[ 0 ] == '-' )
00350             cmd = cmd.remove( 0, cmd.find( ' ' ) ).stripWhiteSpace();
00351     }
00352 
00353     if (cmd.left(8) == "kcmshell")
00354         cmd = cmd.remove(0,8).stripWhiteSpace();
00355 
00356     /* Run the process */
00357     QString kdesu = KStandardDirs::findExe("kdesu");
00358     if (!kdesu.isEmpty())
00359     {
00360 
00361         d->rootProcess = new KProcess;
00362 
00363         *d->rootProcess << kdesu;
00364         *d->rootProcess << "--nonewdcop" << "-n" << "-d" << QString( "-i%1" ).arg(moduleInfo().icon());
00365 
00366         *d->rootProcess << QString("kcmshell %1 --embed-proxy %2 --lang %3").arg(cmd).arg
00367             (d->embedWidget->winId()).arg(KGlobal::locale()->language());
00368 
00369         connect(d->rootProcess, SIGNAL(processExited(KProcess*)), SLOT(rootExited()));
00370 
00371         if ( !d->rootProcess->start( KProcess::NotifyOnExit ))
00372         {
00373             d->rootMode = false;
00374             rootExited();
00375         }
00376         else
00377         {
00378             d->rootMode = true;
00379             kapp->dcopClient();
00380             d->rootCommunicator = new KCModuleProxyRootCommunicatorImpl( d->dcopName + "-RootCommunicator", this );
00381         }
00382 
00383         delete lblBusy;
00384         QApplication::restoreOverrideCursor();
00385         return;
00386     }
00387 
00388     /* Clean up in case of failure */
00389     delete d->embedFrame;
00390     d->embedFrame = 0;
00391     delete d->embedWidget;
00392     d->embedWidget = 0;
00393     delete lblBusy;
00394 
00395     QApplication::restoreOverrideCursor();
00396 }
00397 
00398 void KCModuleProxy::rootExited()
00399 {
00400     kdDebug(711) << k_funcinfo << endl;
00401 
00402     if ( d->embedWidget->embeddedWinId() )
00403         XDestroyWindow(qt_xdisplay(), d->embedWidget->embeddedWinId());
00404 
00405     delete d->embedWidget;
00406     d->embedWidget = 0;
00407 
00408     delete d->rootProcess;
00409     d->rootProcess = 0;
00410 
00411     delete d->embedFrame;
00412     d->embedFrame=0;
00413 
00414     delete d->rootCommunicator;
00415     d->rootCommunicator = 0;
00416 
00417     /* Such that the "ordinary" module loads again */
00418     d->rootMode = false;
00419 
00420     d->topLayout->invalidate();
00421 
00422     QShowEvent ev;
00423     showEvent( &ev ); 
00424 
00425     moduleChanged( false );
00426     emit childClosed();
00427 }
00428 
00429 KCModuleProxy::~KCModuleProxy()
00430 {
00431     deleteClient();
00432     KCModuleLoader::unloadModule(moduleInfo());
00433 
00434     delete d;
00435 }
00436 
00437 void KCModuleProxy::deleteClient()
00438 {
00439     if( d->embedWidget )
00440         XKillClient(qt_xdisplay(), d->embedWidget->embeddedWinId());
00441 
00442 
00443     delete d->kcm;
00444     d->kcm = 0;
00445 
00446     delete d->dcopObject;
00447     d->dcopObject = 0;
00448 
00449     if( d->dcopClient && !d->dcopClient->detach() )
00450         kdDebug(711) << "Unregistering from DCOP failed." << endl;
00451 
00452     delete d->dcopClient;
00453     d->dcopClient = 0;
00454 
00455     kapp->syncX();
00456 
00457 }
00458 
00459 void KCModuleProxy::moduleChanged( bool c )
00460 {
00461     if(  d->changed == c )
00462         return;
00463 
00464     d->changed = c;
00465     emit changed( c );
00466     emit changed( this );
00467 }
00468 
00469 void KCModuleProxy::moduleDestroyed()
00470 {
00471     d->kcm = 0;
00472 }
00473 
00474 KCModuleProxy::KCModuleProxy( const KService::Ptr & service, bool withFallback, 
00475         QWidget  * parent, const char * name, const QStringList & args)
00476     : QWidget( parent, name )
00477 {
00478     init( KCModuleInfo( service ));
00479     d->args = args;
00480     d->withFallback = withFallback;
00481 }
00482 
00483 KCModuleProxy::KCModuleProxy( const KCModuleInfo & info, bool withFallback,
00484         QWidget * parent, const char * name, const QStringList & args )
00485     : QWidget( parent, name )
00486 {
00487     init( info );
00488     d->args = args;
00489     d->withFallback = withFallback;
00490 }
00491 
00492 KCModuleProxy::KCModuleProxy( const QString& serviceName, bool withFallback, 
00493         QWidget * parent, const char * name, 
00494         const QStringList & args)
00495     : QWidget( parent, name )
00496 {
00497     init( KCModuleInfo( serviceName ));
00498     d->args = args;
00499     d->withFallback = withFallback;
00500 }
00501 
00502 void KCModuleProxy::init( const KCModuleInfo& info )
00503 { 
00504     kdDebug(711) << k_funcinfo << endl;
00505 
00506     d = new KCModuleProxyPrivate( info );
00507 
00508     /* This is all we do for now; all the heavy work is 
00509      * done in realModule(). It's called when the module
00510      * _actually_ is needed, in for example showEvent().
00511      * The module is loaded "on demand" -- lazy loading.
00512      */
00513 
00514 }
00515 
00516 void KCModuleProxy::load()
00517 {
00518 
00519     if( d->rootMode )
00520         callRootModule( "load()" );
00521     else if( realModule() )
00522     {
00523         d->kcm->load();
00524         moduleChanged( false );
00525     }
00526 }
00527 
00528 void KCModuleProxy::save()
00529 {
00530     if( d->rootMode )
00531         callRootModule( "save()" );
00532     else if( d->changed && realModule() )
00533     {
00534         d->kcm->save();
00535         moduleChanged( false );
00536     }
00537 }
00538 
00539 void KCModuleProxy::callRootModule( const QCString& function )
00540 {
00541     QByteArray sendData, replyData;
00542     QCString replyType;
00543 
00544     /* Note, we don't use d->dcopClient here, because it's used for 
00545      * the loaded module(and it's not "us" when this function is called) */
00546     if( !kapp->dcopClient()->call( d->dcopName, d->dcopName, function, sendData,
00547             replyType, replyData, true, -1 ))
00548         kdDebug(711) << "Calling function '" << function << "' failed." << endl;
00549 
00550 }
00551 
00552 void KCModuleProxy::defaults()
00553 {
00554     if( d->rootMode )
00555         callRootModule( "defaults()" );
00556     if( realModule() )
00557         d->kcm->defaults();
00558 }
00559 
00560 QString KCModuleProxy::quickHelp() const
00561 {
00562 
00563     if( !d->rootMode )
00564         return realModule() ? realModule()->quickHelp() : QString::null;
00565     else
00566     {
00567         QByteArray data, replyData;
00568         QCString replyType;
00569 
00570         if (kapp->dcopClient()->call(d->dcopName, d->dcopName, "quickHelp()",
00571                   data, replyType, replyData))
00572             kdDebug(711) << "Calling DCOP function bool changed() failed." << endl;
00573         else
00574         {
00575             QDataStream reply(replyData, IO_ReadOnly);
00576             if (replyType == "QString")
00577             {
00578                 QString result;
00579                 reply >> result;
00580                 return result;
00581             }
00582             else
00583                 kdDebug(711) << "DCOP function changed() returned mumbo jumbo." << endl;
00584         }
00585         return QString::null;
00586     }
00587 }
00588 
00589 const KAboutData * KCModuleProxy::aboutData() const
00590 {
00591     if( !d->rootMode )
00592         return realModule() ? realModule()->aboutData() : 0;
00593     else
00594     /* This needs fixing, perhaps cache a KAboutData copy 
00595      * while in root mode? */
00596         return 0;
00597         
00598 
00599 }
00600 
00601 int KCModuleProxy::buttons() const
00602 {
00603     return realModule() ? realModule()->buttons() :
00604         KCModule::Help | KCModule::Default | KCModule::Apply ;
00605 }
00606 
00607 QString KCModuleProxy::rootOnlyMsg() const
00608 {
00609     return realModule() ? realModule()->rootOnlyMsg() : QString::null;
00610 }
00611 
00612 bool KCModuleProxy::useRootOnlyMsg() const
00613 {
00614     return realModule() ? realModule()->useRootOnlyMsg() : true;
00615 }
00616 
00617 KInstance * KCModuleProxy::instance() const
00618 {
00619     return realModule() ? realModule()->instance() : 0;
00620 }
00621 
00622 bool KCModuleProxy::changed() const
00623 {
00624     return d->changed;
00625 }
00626 
00627 const KCModuleInfo& KCModuleProxy::moduleInfo() const
00628 {
00629     return d->modInfo;
00630 }
00631 
00632 bool KCModuleProxy::rootMode() const
00633 {
00634     return d->rootMode;
00635 }
00636 
00637 QCString KCModuleProxy::dcopName() const
00638 {
00639     return d->dcopName;
00640 }
00641 
00642 void KCModuleProxy::emitQuickHelpChanged()
00643 {
00644     emit quickHelpChanged();
00645 }
00646 
00647 /***************************************************************/
00648 #include "kcmoduleproxy.moc"
00649 
00650 // vim: sw=4 ts=4 noet
KDE Logo
This file is part of the documentation for kutils Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Jul 21 13:14:48 2006 by doxygen 1.4.0 written by Dimitri van Heesch, © 1997-2003