tdecmoduleproxy.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., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include <tqapplication.h> 00021 #include <tqcursor.h> 00022 #include <tqdatastream.h> 00023 #include <tqevent.h> 00024 #include <tqfileinfo.h> 00025 #include <tqframe.h> 00026 #include <tqlabel.h> 00027 #include <tqlayout.h> 00028 #include <tqpoint.h> 00029 #include <tqscrollview.h> 00030 #include <tqtextstream.h> 00031 #include <tqvbox.h> 00032 #include <tqwhatsthis.h> 00033 #include <tqwidget.h> 00034 00035 #include <dcopclient.h> 00036 #include <qxembed.h> 00037 00038 #include <tdeapplication.h> 00039 #include <tdeaboutdata.h> 00040 #include <tdecmodule.h> 00041 #include <tdecmoduleinfo.h> 00042 #include <tdecmoduleloader.h> 00043 #include <kdebug.h> 00044 #include <kdialog.h> 00045 #include <tdelocale.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 "tdecmoduleproxy.h" 00054 #include "tdecmoduleproxyIface.h" 00055 #include "tdecmoduleproxyIfaceImpl.h" 00056 00057 /***************************************************************/ 00058 class TDECModuleProxy::TDECModuleProxyPrivate 00059 { 00060 public: 00061 TDECModuleProxyPrivate( const TDECModuleInfo & 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 ~TDECModuleProxyPrivate() 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 TQStringList args; 00094 TDECModule *kcm; 00095 QXEmbed *embedWidget; 00096 TDEProcess *rootProcess; 00097 TQVBox *embedFrame; 00098 TDECModuleProxyIfaceImpl *dcopObject; 00099 DCOPClient *dcopClient; 00100 TQVBoxLayout *topLayout; /* Contains TQScrollView view, and root stuff */ 00101 TDECModuleProxyRootCommunicatorImpl *rootCommunicator; 00102 TQLabel *rootInfo; 00103 TQCString dcopName; 00104 TDECModuleInfo 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 TDECModuleProxy 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 tdesu 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 tdesu such that 00136 the dialog is in process? 00137 00138 */ 00139 /***************************************************************/ 00140 TDECModule * TDECModuleProxy::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 tdecmshell running with root prevs does.. */ 00155 if( d->rootMode ) 00156 return 0; 00157 00158 TQApplication::setOverrideCursor( Qt::WaitCursor ); 00159 00160 TDECModuleProxy * that = const_cast<TDECModuleProxy*>( this ); 00161 00162 if( !d->isInitialized ) 00163 { 00164 d->dcopName = TQString(moduleInfo().handle().prepend("TDECModuleProxy-")).utf8(); 00165 d->topLayout = new TQVBoxLayout( 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 TDECModuleProxyIfaceImpl( d->dcopName, that ); 00184 00185 d->kcm = TDECModuleLoader::loadModule( moduleInfo(), TDECModuleLoader::Inline, d->withFallback, 00186 that, name(), d->args ); 00187 00188 connect( d->kcm, TQT_SIGNAL( changed( bool ) ), 00189 TQT_SLOT(moduleChanged(bool)) ); 00190 connect( d->kcm, TQT_SIGNAL( destroyed() ), 00191 TQT_SLOT( moduleDestroyed() ) ); 00192 connect( d->kcm, TQT_SIGNAL(quickHelpChanged()), 00193 TQT_SIGNAL(quickHelpChanged())); 00194 TQWhatsThis::add( that, d->kcm->quickHelp() ); 00195 00196 d->topLayout->addWidget( d->kcm ); 00197 00198 if ( !d->rootInfo && /* If the message was not yet created */ 00199 d->kcm->useRootOnlyMsg() /* and the module requests the message */ && 00200 moduleInfo().needsRootPrivileges() /* and the module wants root access */ && 00201 !KUser().isSuperUser() ) /* and we are not currently root */ 00202 { 00203 00204 d->rootInfo = new TQLabel( that, "rootInfo" ); 00205 d->topLayout->insertWidget( 0, d->rootInfo ); 00206 00207 d->rootInfo->setFrameShape(TQFrame::Box); 00208 d->rootInfo->setFrameShadow(TQFrame::Raised); 00209 00210 const TQString msg = d->kcm->rootOnlyMsg(); 00211 if( msg.isEmpty() ) 00212 d->rootInfo->setText(i18n( 00213 "<b>Changes in this section requires root access.</b><br />" 00214 "Click the \"Administrator Mode\" button to " 00215 "allow modifications.")); 00216 else 00217 d->rootInfo->setText(msg); 00218 00219 TQWhatsThis::add( d->rootInfo, i18n( 00220 "This section requires special permissions, probably " 00221 "for system-wide changes; therefore, it is " 00222 "required that you provide the root password to be " 00223 "able to change the module's properties. If " 00224 "you do not provide the password, the module will be " 00225 "disabled.")); 00226 } 00227 } 00228 else 00229 { 00230 kdDebug(711) << "Module already loaded, loading KCMError" << endl; 00231 00232 d->dcopClient->detach(); 00233 /* Re-register as anonymous */ 00234 d->dcopClient->attach(); 00235 00236 d->dcopClient->setNotifications( true ); 00237 connect( d->dcopClient, TQT_SIGNAL( applicationRemoved( const TQCString& )), 00238 TQT_SLOT( applicationRemoved( const TQCString& ))); 00239 00240 /* Figure out the name of where the module is already loaded */ 00241 TQByteArray replyData, data; 00242 TQCString replyType; 00243 TQString result; 00244 TQDataStream arg, stream( replyData, IO_ReadOnly ); 00245 00246 if( d->dcopClient->call( d->dcopName, d->dcopName, "applicationName()", 00247 data, replyType, replyData )) 00248 { 00249 stream >> result; 00250 00251 d->kcm = TDECModuleLoader::reportError( TDECModuleLoader::Inline, 00252 i18n( "Argument is application name", "This configuration section is " 00253 "already opened in %1" ).arg( result ), " ", that ); 00254 00255 d->topLayout->addWidget( d->kcm ); 00256 } 00257 else 00258 { 00259 kdDebug(711) << "Calling TDECModuleProxy's DCOP interface for fetching the name failed." << endl; 00260 d->bogusOccupier = true; 00261 TQApplication::restoreOverrideCursor(); 00262 return realModule(); 00263 } 00264 } 00265 00266 TQApplication::restoreOverrideCursor(); 00267 00268 return d->kcm; 00269 } 00270 00271 void TDECModuleProxy::applicationRemoved( const TQCString& app ) 00272 { 00273 if( app == d->dcopName ) 00274 { 00275 /* Violence: Get rid of KCMError & CO, so that 00276 * realModule() attempts to reload the module */ 00277 delete d->kcm; 00278 d->kcm = 0; 00279 d->dcopClient->setNotifications( false ); 00280 realModule(); 00281 d->kcm->show(); 00282 } 00283 } 00284 00285 void TDECModuleProxy::showEvent( TQShowEvent * ev ) 00286 { 00287 00288 kdDebug(711) << k_funcinfo << endl; 00289 ( void )realModule(); 00290 00291 /* We have no kcm, if we're in root mode */ 00292 if( d->kcm ) 00293 d->kcm->show(); 00294 00295 TQWidget::showEvent( ev ); 00296 00297 } 00298 00299 void TDECModuleProxy::runAsRoot() 00300 { 00301 if ( !moduleInfo().needsRootPrivileges() ) 00302 return; 00303 00304 TQApplication::setOverrideCursor( Qt::WaitCursor ); 00305 00306 delete d->rootProcess; 00307 delete d->embedWidget; 00308 delete d->embedFrame; 00309 00310 d->embedFrame = new TQVBox( this, "embedFrame" ); 00311 d->embedFrame->setFrameStyle( TQFrame::Box | TQFrame::Raised ); 00312 00313 TQPalette pal( red ); 00314 pal.setColor( TQColorGroup::Background, 00315 colorGroup().background() ); 00316 d->embedFrame->setPalette( pal ); 00317 d->embedFrame->setLineWidth( 2 ); 00318 d->embedFrame->setMidLineWidth( 2 ); 00319 d->topLayout->addWidget(d->embedFrame,1); 00320 00321 d->embedWidget = new QXEmbed( d->embedFrame, "embedWidget" ); 00322 00323 d->embedFrame->show(); 00324 00325 TQLabel *lblBusy = new TQLabel(i18n("<big>Loading...</big>"), d->embedWidget, "lblBusy" ); 00326 lblBusy->setTextFormat(RichText); 00327 lblBusy->setAlignment(AlignCenter); 00328 lblBusy->setGeometry(0,0, d->kcm->width(), d->kcm->height()); 00329 lblBusy->show(); 00330 00331 deleteClient(); 00332 /* The DCOP registration is now gone, and it will occur again when tdecmshell soon 00333 * registers. Here's a race condition in other words, but how likely is that? 00334 * 00335 * - It's a user initiated action, which means the user have to do weird stuff, very 00336 * quick. 00337 * - If the user _do_ manage to fsck up, the code will recover gracefully, see realModule(). 00338 * 00339 * So no worry. At the end of this function, communication with 00340 * the DCOP object is established. 00341 */ 00342 00343 /* Prepare the process to run the tdecmshell */ 00344 TQString cmd = moduleInfo().service()->exec().stripWhiteSpace(); 00345 if (cmd.left(5) == "tdesu") 00346 { 00347 cmd = TQString(cmd.remove(0,5)).stripWhiteSpace(); 00348 00349 /* Remove all tdesu switches */ 00350 while( cmd.length() > 1 && cmd[ 0 ] == '-' ) 00351 cmd = TQString(cmd.remove( 0, cmd.find( ' ' ) )).stripWhiteSpace(); 00352 } 00353 00354 if (cmd.left(8) == "tdecmshell") 00355 cmd = TQString(cmd.remove(0,8)).stripWhiteSpace(); 00356 00357 /* Run the process */ 00358 TQString tdesu = TDEStandardDirs::findExe("tdesu"); 00359 if (!tdesu.isEmpty()) 00360 { 00361 00362 d->rootProcess = new TDEProcess; 00363 00364 *d->rootProcess << tdesu; 00365 *d->rootProcess << "--nonewdcop" << "-n" << "-d" << TQString( "-i%1" ).arg(moduleInfo().icon()); 00366 00367 *d->rootProcess << TQString("%1 %2 --embed-proxy %3 --lang %4").arg(locate("exe", "tdecmshell")) 00368 .arg(cmd).arg(d->embedWidget->winId()).arg(TDEGlobal::locale()->language()); 00369 00370 connect(d->rootProcess, TQT_SIGNAL(processExited(TDEProcess*)), TQT_SLOT(rootExited())); 00371 00372 if ( !d->rootProcess->start( TDEProcess::NotifyOnExit )) 00373 { 00374 d->rootMode = false; 00375 rootExited(); 00376 } 00377 else 00378 { 00379 d->rootMode = true; 00380 kapp->dcopClient(); 00381 d->rootCommunicator = new TDECModuleProxyRootCommunicatorImpl( d->dcopName + "-RootCommunicator", this ); 00382 } 00383 00384 delete lblBusy; 00385 TQApplication::restoreOverrideCursor(); 00386 return; 00387 } 00388 00389 /* Clean up in case of failure */ 00390 delete d->embedWidget; 00391 d->embedWidget = 0; 00392 delete d->embedFrame; 00393 d->embedFrame = 0; 00394 00395 TQApplication::restoreOverrideCursor(); 00396 } 00397 00398 void TDECModuleProxy::rootExited() 00399 { 00400 kdDebug(711) << k_funcinfo << endl; 00401 00402 if ( d->embedWidget->embeddedWinId() ) 00403 XDestroyWindow(tqt_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 TQShowEvent ev; 00423 showEvent( &ev ); 00424 00425 moduleChanged( false ); 00426 emit childClosed(); 00427 } 00428 00429 TDECModuleProxy::~TDECModuleProxy() 00430 { 00431 deleteClient(); 00432 TDECModuleLoader::unloadModule(moduleInfo()); 00433 00434 delete d; 00435 } 00436 00437 void TDECModuleProxy::deleteClient() 00438 { 00439 if( d->embedWidget ) 00440 XKillClient(tqt_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 TDECModuleProxy::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 TDECModuleProxy::moduleDestroyed() 00470 { 00471 d->kcm = 0; 00472 } 00473 00474 TDECModuleProxy::TDECModuleProxy( const KService::Ptr & service, bool withFallback, 00475 TQWidget * parent, const char * name, const TQStringList & args) 00476 : TQWidget( parent, name ) 00477 { 00478 init( TDECModuleInfo( service )); 00479 d->args = args; 00480 d->withFallback = withFallback; 00481 } 00482 00483 TDECModuleProxy::TDECModuleProxy( const TDECModuleInfo & info, bool withFallback, 00484 TQWidget * parent, const char * name, const TQStringList & args ) 00485 : TQWidget( parent, name ) 00486 { 00487 init( info ); 00488 d->args = args; 00489 d->withFallback = withFallback; 00490 } 00491 00492 TDECModuleProxy::TDECModuleProxy( const TQString& serviceName, bool withFallback, 00493 TQWidget * parent, const char * name, 00494 const TQStringList & args) 00495 : TQWidget( parent, name ) 00496 { 00497 init( TDECModuleInfo( serviceName )); 00498 d->args = args; 00499 d->withFallback = withFallback; 00500 } 00501 00502 void TDECModuleProxy::init( const TDECModuleInfo& info ) 00503 { 00504 kdDebug(711) << k_funcinfo << endl; 00505 00506 d = new TDECModuleProxyPrivate( 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 TDECModuleProxy::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 TDECModuleProxy::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 TDECModuleProxy::callRootModule( const TQCString& function ) 00540 { 00541 TQByteArray sendData, replyData; 00542 TQCString 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 TDECModuleProxy::defaults() 00553 { 00554 if( d->rootMode ) 00555 callRootModule( "defaults()" ); 00556 if( realModule() ) 00557 d->kcm->defaults(); 00558 } 00559 00560 TQString TDECModuleProxy::quickHelp() const 00561 { 00562 00563 if( !d->rootMode ) 00564 return realModule() ? realModule()->quickHelp() : TQString::null; 00565 else 00566 { 00567 TQByteArray data, replyData; 00568 TQCString 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 TQDataStream reply(replyData, IO_ReadOnly); 00576 if (replyType == "TQString") 00577 { 00578 TQString result; 00579 reply >> result; 00580 return result; 00581 } 00582 else 00583 kdDebug(711) << "DCOP function changed() returned mumbo jumbo." << endl; 00584 } 00585 return TQString::null; 00586 } 00587 } 00588 00589 const TDEAboutData * TDECModuleProxy::aboutData() const 00590 { 00591 if( !d->rootMode ) { 00592 return realModule() ? realModule()->aboutData() : 0; 00593 } 00594 else { 00595 /* This needs fixing, perhaps cache a TDEAboutData copy 00596 * while in root mode? */ 00597 return 0; 00598 } 00599 } 00600 00601 TQString TDECModuleProxy::handbookDocPath() const 00602 { 00603 if( !d->rootMode ) { 00604 return realModule() ? realModule()->handbookDocPath() : TQString::null; 00605 } 00606 else { 00607 /* This needs fixing, perhaps cache a handbookSection() copy 00608 * while in root mode? */ 00609 return TQString::null; 00610 } 00611 } 00612 00613 TQString TDECModuleProxy::handbookSection() const 00614 { 00615 if( !d->rootMode ) { 00616 return realModule() ? realModule()->handbookSection() : TQString::null; 00617 } 00618 else { 00619 /* This needs fixing, perhaps cache a handbookSection() copy 00620 * while in root mode? */ 00621 return TQString::null; 00622 } 00623 } 00624 00625 int TDECModuleProxy::buttons() const 00626 { 00627 return realModule() ? realModule()->buttons() : 00628 TDECModule::Help | TDECModule::Default | TDECModule::Apply ; 00629 } 00630 00631 TQString TDECModuleProxy::rootOnlyMsg() const 00632 { 00633 return realModule() ? realModule()->rootOnlyMsg() : TQString::null; 00634 } 00635 00636 bool TDECModuleProxy::useRootOnlyMsg() const 00637 { 00638 return realModule() ? realModule()->useRootOnlyMsg() : true; 00639 } 00640 00641 TDEInstance * TDECModuleProxy::instance() const 00642 { 00643 return realModule() ? realModule()->instance() : 0; 00644 } 00645 00646 bool TDECModuleProxy::changed() const 00647 { 00648 return d->changed; 00649 } 00650 00651 const TDECModuleInfo& TDECModuleProxy::moduleInfo() const 00652 { 00653 return d->modInfo; 00654 } 00655 00656 bool TDECModuleProxy::rootMode() const 00657 { 00658 return d->rootMode; 00659 } 00660 00661 TQCString TDECModuleProxy::dcopName() const 00662 { 00663 return d->dcopName; 00664 } 00665 00666 void TDECModuleProxy::emitQuickHelpChanged() 00667 { 00668 emit quickHelpChanged(); 00669 } 00670 00671 /***************************************************************/ 00672 #include "tdecmoduleproxy.moc" 00673 00674 // vim: sw=4 ts=4 noet