• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KUtils

kcmoduleproxy.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00017     Boston, MA 02110-1301, 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 rootInfo; // Delete before embedWidget!
00084             delete embedWidget; // Delete before embedFrame!
00085             delete embedFrame;
00086             delete dcopClient;
00087             delete dcopObject;
00088             delete rootCommunicator;
00089             delete rootProcess;
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("%1 %2 --embed-proxy %3 --lang %4").arg(locate("exe", "kcmshell"))
00367             .arg(cmd).arg(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->embedWidget;
00390     d->embedWidget = 0;
00391     delete d->embedFrame;
00392     d->embedFrame = 0;
00393 
00394     QApplication::restoreOverrideCursor();
00395 }
00396 
00397 void KCModuleProxy::rootExited()
00398 {
00399     kdDebug(711) << k_funcinfo << endl;
00400 
00401     if ( d->embedWidget->embeddedWinId() )
00402         XDestroyWindow(qt_xdisplay(), d->embedWidget->embeddedWinId());
00403 
00404     delete d->embedWidget;
00405     d->embedWidget = 0;
00406 
00407     delete d->rootProcess;
00408     d->rootProcess = 0;
00409 
00410     delete d->embedFrame;
00411     d->embedFrame=0;
00412 
00413     delete d->rootCommunicator;
00414     d->rootCommunicator = 0;
00415 
00416     /* Such that the "ordinary" module loads again */
00417     d->rootMode = false;
00418 
00419     d->topLayout->invalidate();
00420 
00421     QShowEvent ev;
00422     showEvent( &ev ); 
00423 
00424     moduleChanged( false );
00425     emit childClosed();
00426 }
00427 
00428 KCModuleProxy::~KCModuleProxy()
00429 {
00430     deleteClient();
00431     KCModuleLoader::unloadModule(moduleInfo());
00432 
00433     delete d;
00434 }
00435 
00436 void KCModuleProxy::deleteClient()
00437 {
00438     if( d->embedWidget )
00439         XKillClient(qt_xdisplay(), d->embedWidget->embeddedWinId());
00440 
00441 
00442     delete d->kcm;
00443     d->kcm = 0;
00444 
00445     delete d->dcopObject;
00446     d->dcopObject = 0;
00447 
00448     if( d->dcopClient && !d->dcopClient->detach() )
00449         kdDebug(711) << "Unregistering from DCOP failed." << endl;
00450 
00451     delete d->dcopClient;
00452     d->dcopClient = 0;
00453 
00454     kapp->syncX();
00455 
00456 }
00457 
00458 void KCModuleProxy::moduleChanged( bool c )
00459 {
00460     if(  d->changed == c )
00461         return;
00462 
00463     d->changed = c;
00464     emit changed( c );
00465     emit changed( this );
00466 }
00467 
00468 void KCModuleProxy::moduleDestroyed()
00469 {
00470     d->kcm = 0;
00471 }
00472 
00473 KCModuleProxy::KCModuleProxy( const KService::Ptr & service, bool withFallback, 
00474         QWidget  * parent, const char * name, const QStringList & args)
00475     : QWidget( parent, name )
00476 {
00477     init( KCModuleInfo( service ));
00478     d->args = args;
00479     d->withFallback = withFallback;
00480 }
00481 
00482 KCModuleProxy::KCModuleProxy( const KCModuleInfo & info, bool withFallback,
00483         QWidget * parent, const char * name, const QStringList & args )
00484     : QWidget( parent, name )
00485 {
00486     init( info );
00487     d->args = args;
00488     d->withFallback = withFallback;
00489 }
00490 
00491 KCModuleProxy::KCModuleProxy( const QString& serviceName, bool withFallback, 
00492         QWidget * parent, const char * name, 
00493         const QStringList & args)
00494     : QWidget( parent, name )
00495 {
00496     init( KCModuleInfo( serviceName ));
00497     d->args = args;
00498     d->withFallback = withFallback;
00499 }
00500 
00501 void KCModuleProxy::init( const KCModuleInfo& info )
00502 { 
00503     kdDebug(711) << k_funcinfo << endl;
00504 
00505     d = new KCModuleProxyPrivate( info );
00506 
00507     /* This is all we do for now; all the heavy work is 
00508      * done in realModule(). It's called when the module
00509      * _actually_ is needed, in for example showEvent().
00510      * The module is loaded "on demand" -- lazy loading.
00511      */
00512 
00513 }
00514 
00515 void KCModuleProxy::load()
00516 {
00517 
00518     if( d->rootMode )
00519         callRootModule( "load()" );
00520     else if( realModule() )
00521     {
00522         d->kcm->load();
00523         moduleChanged( false );
00524     }
00525 }
00526 
00527 void KCModuleProxy::save()
00528 {
00529     if( d->rootMode )
00530         callRootModule( "save()" );
00531     else if( d->changed && realModule() )
00532     {
00533         d->kcm->save();
00534         moduleChanged( false );
00535     }
00536 }
00537 
00538 void KCModuleProxy::callRootModule( const QCString& function )
00539 {
00540     QByteArray sendData, replyData;
00541     QCString replyType;
00542 
00543     /* Note, we don't use d->dcopClient here, because it's used for 
00544      * the loaded module(and it's not "us" when this function is called) */
00545     if( !kapp->dcopClient()->call( d->dcopName, d->dcopName, function, sendData,
00546             replyType, replyData, true, -1 ))
00547         kdDebug(711) << "Calling function '" << function << "' failed." << endl;
00548 
00549 }
00550 
00551 void KCModuleProxy::defaults()
00552 {
00553     if( d->rootMode )
00554         callRootModule( "defaults()" );
00555     if( realModule() )
00556         d->kcm->defaults();
00557 }
00558 
00559 QString KCModuleProxy::quickHelp() const
00560 {
00561 
00562     if( !d->rootMode )
00563         return realModule() ? realModule()->quickHelp() : QString::null;
00564     else
00565     {
00566         QByteArray data, replyData;
00567         QCString replyType;
00568 
00569         if (kapp->dcopClient()->call(d->dcopName, d->dcopName, "quickHelp()",
00570                   data, replyType, replyData))
00571             kdDebug(711) << "Calling DCOP function bool changed() failed." << endl;
00572         else
00573         {
00574             QDataStream reply(replyData, IO_ReadOnly);
00575             if (replyType == "QString")
00576             {
00577                 QString result;
00578                 reply >> result;
00579                 return result;
00580             }
00581             else
00582                 kdDebug(711) << "DCOP function changed() returned mumbo jumbo." << endl;
00583         }
00584         return QString::null;
00585     }
00586 }
00587 
00588 const KAboutData * KCModuleProxy::aboutData() const
00589 {
00590     if( !d->rootMode )
00591         return realModule() ? realModule()->aboutData() : 0;
00592     else
00593     /* This needs fixing, perhaps cache a KAboutData copy 
00594      * while in root mode? */
00595         return 0;
00596         
00597 
00598 }
00599 
00600 int KCModuleProxy::buttons() const
00601 {
00602     return realModule() ? realModule()->buttons() :
00603         KCModule::Help | KCModule::Default | KCModule::Apply ;
00604 }
00605 
00606 QString KCModuleProxy::rootOnlyMsg() const
00607 {
00608     return realModule() ? realModule()->rootOnlyMsg() : QString::null;
00609 }
00610 
00611 bool KCModuleProxy::useRootOnlyMsg() const
00612 {
00613     return realModule() ? realModule()->useRootOnlyMsg() : true;
00614 }
00615 
00616 KInstance * KCModuleProxy::instance() const
00617 {
00618     return realModule() ? realModule()->instance() : 0;
00619 }
00620 
00621 bool KCModuleProxy::changed() const
00622 {
00623     return d->changed;
00624 }
00625 
00626 const KCModuleInfo& KCModuleProxy::moduleInfo() const
00627 {
00628     return d->modInfo;
00629 }
00630 
00631 bool KCModuleProxy::rootMode() const
00632 {
00633     return d->rootMode;
00634 }
00635 
00636 QCString KCModuleProxy::dcopName() const
00637 {
00638     return d->dcopName;
00639 }
00640 
00641 void KCModuleProxy::emitQuickHelpChanged()
00642 {
00643     emit quickHelpChanged();
00644 }
00645 
00646 /***************************************************************/
00647 #include "kcmoduleproxy.moc"
00648 
00649 // vim: sw=4 ts=4 noet

KUtils

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal