2012-02-20 16:38:00 +01:00
/*
* Copyright 2005 by Aaron Seigo < aseigo @ kde . org >
* Copyright 2007 by Riccardo Iaconelli < riccardo @ kde . org >
* Copyright 2008 by Ménard Alexis < darktears31 @ gmail . com >
* Copyright ( c ) 2009 Chani Armitage < chani @ kde . org >
* Copyright 2012 by Marco Martin < mart @ kde . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation ; either version 2 , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
# include "private/applet_p.h"
2012-08-24 14:26:59 +02:00
# include <config-plasma.h>
2012-09-25 20:46:07 +02:00
# include <QFile>
2012-09-23 17:21:18 +02:00
# include <qstandardpaths.h>
2013-07-29 19:05:59 +02:00
# include <QDebug>
2014-03-20 19:22:27 +01:00
# include <QMessageBox>
2012-12-27 11:25:00 +01:00
# include <kiconloader.h>
2013-02-11 21:38:18 +01:00
# include <klocalizedstring.h>
2012-09-23 17:21:18 +02:00
# include <kkeysequencewidget.h>
2013-07-10 01:45:41 +02:00
# include <kglobalaccel.h>
2014-04-26 14:29:54 +02:00
# include <KConfigLoader>
2012-02-20 16:38:00 +01:00
# include "containment.h"
# include "corona.h"
# include "pluginloader.h"
# include "scripting/scriptengine.h"
2012-09-23 17:21:18 +02:00
# include "scripting/appletscript.h"
2012-02-20 16:38:00 +01:00
# include "private/containment_p.h"
2012-08-24 14:26:59 +02:00
2012-02-20 16:38:00 +01:00
namespace Plasma
{
AppletPrivate : : AppletPrivate ( KService : : Ptr service , const KPluginInfo * info , int uniqueID , Applet * applet )
2014-04-26 01:45:47 +02:00
: appletId ( uniqueID ) ,
q ( applet ) ,
immutability ( Types : : Mutable ) ,
appletDescription ( info ? * info : KPluginInfo ( service ) ) ,
icon ( appletDescription . isValid ( ) ? appletDescription . icon ( ) : QString ( ) ) ,
mainConfig ( 0 ) ,
pendingConstraints ( Types : : NoConstraint ) ,
script ( 0 ) ,
package ( 0 ) ,
configLoader ( 0 ) ,
actions ( AppletPrivate : : defaultActions ( applet ) ) ,
activationAction ( 0 ) ,
itemStatus ( Types : : UnknownStatus ) ,
modificationsTimer ( 0 ) ,
hasConfigurationInterface ( false ) ,
failed ( false ) ,
transient ( false ) ,
needsConfig ( false ) ,
started ( false ) ,
globalShortcutEnabled ( false ) ,
uiReady ( false ) ,
userConfiguring ( false )
2012-02-20 16:38:00 +01:00
{
if ( appletId = = 0 ) {
appletId = + + s_maxAppletId ;
} else if ( appletId > s_maxAppletId ) {
s_maxAppletId = appletId ;
}
2013-04-24 22:54:46 +02:00
QObject : : connect ( actions - > action ( " configure " ) , SIGNAL ( triggered ( ) ) ,
q , SLOT ( requestConfiguration ( ) ) ) ;
2012-02-20 16:38:00 +01:00
}
AppletPrivate : : ~ AppletPrivate ( )
{
2013-07-10 01:45:41 +02:00
if ( activationAction & & globalShortcutEnabled ) {
2013-07-29 19:05:59 +02:00
//qDebug() << "resetting global action for" << q->title() << activationAction->objectName();
2013-07-10 01:45:41 +02:00
KGlobalAccel : : self ( ) - > removeAllShortcuts ( activationAction ) ;
2012-02-20 16:38:00 +01:00
}
delete script ;
script = 0 ;
delete package ;
package = 0 ;
delete configLoader ;
configLoader = 0 ;
delete mainConfig ;
mainConfig = 0 ;
delete modificationsTimer ;
}
2014-05-06 20:52:42 +02:00
void AppletPrivate : : init ( const QString & packagePath , const QVariantList & args )
2012-02-20 16:38:00 +01:00
{
// WARNING: do not access config() OR globalConfig() in this method!
2013-02-25 17:20:29 +01:00
// that requires a Corona, which is not available at this point
q - > setHasConfigurationInterface ( true ) ;
2012-02-20 16:38:00 +01:00
QAction * closeApplet = actions - > action ( " remove " ) ;
if ( closeApplet ) {
2013-02-12 11:30:25 +01:00
closeApplet - > setText ( i18nc ( " %1 is the name of the applet " , " Remove this %1 " , q - > title ( ) ) ) ;
2012-02-20 16:38:00 +01:00
}
QAction * configAction = actions - > action ( " configure " ) ;
if ( configAction ) {
2013-02-12 11:30:25 +01:00
configAction - > setText ( i18nc ( " %1 is the name of the applet " , " %1 Settings " , q - > title ( ) ) ) ;
2012-02-20 16:38:00 +01:00
}
if ( ! appletDescription . isValid ( ) ) {
# ifndef NDEBUG
2013-07-29 19:05:59 +02:00
// qDebug() << "Check your constructor! "
2013-08-01 12:15:38 +02:00
// << "You probably want to be passing in a Service::Ptr "
// << "or a QVariantList with a valid storageid as arg[0].";
2012-02-20 16:38:00 +01:00
# endif
return ;
}
QString api = appletDescription . property ( " X-Plasma-API " ) . toString ( ) ;
if ( api . isEmpty ( ) ) {
2013-02-12 20:07:23 +01:00
q - > setLaunchErrorMessage ( i18n ( " The %2 widget did not define which ScriptEngine to use. " , appletDescription . name ( ) ) ) ;
2012-02-20 16:38:00 +01:00
return ;
}
2013-02-05 14:44:39 +01:00
const QString path = packagePath . isEmpty ( ) ? appletDescription . pluginName ( ) : packagePath ;
2012-02-20 16:38:00 +01:00
package = new Package ( PluginLoader : : self ( ) - > loadPackage ( " Plasma/Applet " , api ) ) ;
package - > setPath ( path ) ;
2013-02-05 14:44:39 +01:00
2012-02-20 16:38:00 +01:00
if ( ! package - > isValid ( ) ) {
delete package ;
package = 0 ;
2013-02-12 20:07:23 +01:00
q - > setLaunchErrorMessage ( i18nc ( " Package file, name of the widget " ,
2014-04-26 01:45:47 +02:00
" Could not open the %1 package required for the %2 widget. " ,
appletDescription . pluginName ( ) , appletDescription . name ( ) ) ) ;
2012-02-20 16:38:00 +01:00
return ;
}
2013-02-25 17:20:29 +01:00
// now we try and set up the script engine.
// it will be parented to this applet and so will get
// deleted when the applet does
2014-05-06 20:52:42 +02:00
script = Plasma : : loadScriptEngine ( api , q , args ) ;
2013-02-04 18:02:03 +01:00
2012-02-20 16:38:00 +01:00
if ( ! script ) {
delete package ;
package = 0 ;
2013-02-12 20:07:23 +01:00
q - > setLaunchErrorMessage (
2014-04-26 01:45:47 +02:00
i18nc ( " API or programming language the widget was written in, name of the widget " ,
" Could not create a %1 ScriptEngine for the %2 widget. " ,
api , appletDescription . name ( ) ) ) ;
2012-02-20 16:38:00 +01:00
}
}
void AppletPrivate : : cleanUpAndDelete ( )
{
2012-02-28 16:52:11 +01:00
// reimplemented in the UI specific library
2013-02-15 17:56:16 +01:00
if ( configLoader ) {
configLoader - > setDefaults ( ) ;
}
resetConfigurationObject ( ) ;
2013-12-11 13:41:22 +01:00
if ( q - > isContainment ( ) ) {
2013-02-15 18:07:38 +01:00
// prematurely emit our destruction if we are a Containment,
// giving Corona a chance to remove this Containment from its collection
emit q - > QObject : : destroyed ( q ) ;
}
2013-02-15 17:56:16 +01:00
q - > deleteLater ( ) ;
2012-02-20 16:38:00 +01:00
}
void AppletPrivate : : showConfigurationRequiredMessage ( bool show , const QString & reason )
{
// reimplemented in the UI specific library
Q_UNUSED ( show )
Q_UNUSED ( reason )
}
2014-03-20 19:22:27 +01:00
void AppletPrivate : : askDestroy ( )
{
if ( q - > immutability ( ) ! = Types : : Mutable | | transient | | ! started ) {
return ; //don't double delete
}
if ( q - > isContainment ( ) ) {
2014-04-26 01:45:47 +02:00
QMessageBox * box = new QMessageBox ( QMessageBox : : Warning , i18nc ( " @title:window %1 is the name of the containment " , " Remove %1 " , q - > title ( ) ) , i18nc ( " %1 is the name of the containment " , " Do you really want to remove this %1? " , q - > title ( ) ) , QMessageBox : : StandardButtons ( QMessageBox : : Yes | QMessageBox : : No ) ) ;
2014-03-20 19:22:27 +01:00
box - > setWindowFlags ( ( Qt : : WindowFlags ) ( box - > windowFlags ( ) | Qt : : WA_DeleteOnClose ) ) ;
box - > open ( ) ;
2014-05-19 11:10:24 +02:00
QObject : : connect ( q , & Applet : : immutabilityChanged , [ = ] ( ) {
box - > close ( ) ;
} ) ;
2014-03-20 19:22:27 +01:00
QObject : : connect ( box - > button ( QMessageBox : : Yes ) , & QAbstractButton : : clicked ,
2014-04-26 01:45:47 +02:00
[ = ] ( ) {
transient = true ;
cleanUpAndDelete ( ) ;
} ) ;
2014-03-20 19:22:27 +01:00
return ;
}
transient = true ;
cleanUpAndDelete ( ) ;
}
2012-02-20 16:38:00 +01:00
void AppletPrivate : : globalShortcutChanged ( )
{
if ( ! activationAction ) {
return ;
}
KConfigGroup shortcutConfig ( mainConfigGroup ( ) , " Shortcuts " ) ;
2014-04-12 20:09:08 +02:00
QString newShortCut = activationAction - > shortcut ( ) . toString ( ) ;
QString oldShortCut = shortcutConfig . readEntry ( " global " , QString ( ) ) ;
2014-04-26 01:45:47 +02:00
if ( newShortCut ! = oldShortCut ) {
2014-04-12 20:09:08 +02:00
shortcutConfig . writeEntry ( " global " , newShortCut ) ;
scheduleModificationNotification ( ) ;
}
2013-07-29 19:05:59 +02:00
//qDebug() << "after" << shortcut.primary() << d->activationAction->globalShortcut().primary();
2012-02-20 16:38:00 +01:00
}
2014-04-26 01:45:47 +02:00
KActionCollection * AppletPrivate : : defaultActions ( QObject * parent )
2012-02-20 16:38:00 +01:00
{
KActionCollection * actions = new KActionCollection ( parent ) ;
actions - > setConfigGroup ( " Shortcuts-Applet " ) ;
2013-07-10 00:11:58 +02:00
QAction * configAction = actions - > add < QAction > ( " configure " ) ;
2012-02-20 16:38:00 +01:00
configAction - > setAutoRepeat ( false ) ;
configAction - > setText ( i18n ( " Widget Settings " ) ) ;
2013-04-02 12:55:37 +02:00
configAction - > setIcon ( QIcon : : fromTheme ( " configure " ) ) ;
2013-07-10 00:11:58 +02:00
configAction - > setShortcut ( QKeySequence ( " alt+d, s " ) ) ;
2013-05-10 19:29:13 +02:00
configAction - > setData ( Plasma : : Types : : ConfigureAction ) ;
2012-02-20 16:38:00 +01:00
2013-07-10 00:11:58 +02:00
QAction * closeApplet = actions - > add < QAction > ( " remove " ) ;
2012-02-20 16:38:00 +01:00
closeApplet - > setAutoRepeat ( false ) ;
closeApplet - > setText ( i18n ( " Remove this Widget " ) ) ;
2013-04-02 12:55:37 +02:00
closeApplet - > setIcon ( QIcon : : fromTheme ( " edit-delete " ) ) ;
2013-07-10 00:11:58 +02:00
closeApplet - > setShortcut ( QKeySequence ( " alt+d, r " ) ) ;
2013-05-10 19:29:13 +02:00
closeApplet - > setData ( Plasma : : Types : : DestructiveAction ) ;
2012-02-20 16:38:00 +01:00
2013-07-10 00:11:58 +02:00
QAction * runAssociatedApplication = actions - > add < QAction > ( " run associated application " ) ;
2012-02-20 16:38:00 +01:00
runAssociatedApplication - > setAutoRepeat ( false ) ;
runAssociatedApplication - > setText ( i18n ( " Run the Associated Application " ) ) ;
2013-04-02 12:55:37 +02:00
runAssociatedApplication - > setIcon ( QIcon : : fromTheme ( " system-run " ) ) ;
2013-07-10 00:11:58 +02:00
runAssociatedApplication - > setShortcut ( QKeySequence ( " alt+d, t " ) ) ;
2012-02-20 16:38:00 +01:00
runAssociatedApplication - > setVisible ( false ) ;
runAssociatedApplication - > setEnabled ( false ) ;
2013-05-10 19:29:13 +02:00
runAssociatedApplication - > setData ( Plasma : : Types : : ControlAction ) ;
2012-02-20 16:38:00 +01:00
return actions ;
}
2013-04-20 18:27:24 +02:00
void AppletPrivate : : requestConfiguration ( )
{
if ( q - > containment ( ) ) {
emit q - > containment ( ) - > configureRequested ( q ) ;
}
}
2012-02-20 16:38:00 +01:00
void AppletPrivate : : updateShortcuts ( )
{
2013-12-11 13:41:22 +01:00
if ( q - > isContainment ( ) ) {
2012-02-20 16:38:00 +01:00
//a horrible hack to avoid clobbering corona settings
//we pull them out, then read, then put them back
QList < QString > names ;
2014-04-26 01:45:47 +02:00
QList < QAction * > qactions ;
2012-02-20 16:38:00 +01:00
names < < " add sibling containment " < < " configure shortcuts " < < " lock widgets " ;
foreach ( const QString & name , names ) {
QAction * a = actions - > action ( name ) ;
actions - > takeAction ( a ) ; //FIXME this is stupid, KActionCollection needs a takeAction(QString) method
qactions < < a ;
}
actions - > readSettings ( ) ;
for ( int i = 0 ; i < names . size ( ) ; + + i ) {
QAction * a = qactions . at ( i ) ;
if ( a ) {
2013-12-18 21:35:03 +01:00
actions - > addAction ( names . at ( i ) , a ) ;
2012-02-20 16:38:00 +01:00
}
}
} else {
actions - > readSettings ( ) ;
}
}
void AppletPrivate : : propagateConfigChanged ( )
{
2013-12-11 13:41:22 +01:00
Containment * c = qobject_cast < Containment * > ( q ) ;
if ( c ) {
c - > d - > configChanged ( ) ;
2012-02-20 16:38:00 +01:00
}
q - > configChanged ( ) ;
}
2013-08-09 21:51:13 +02:00
void AppletPrivate : : setUiReady ( )
{
//am i the containment?
Containment * c = qobject_cast < Containment * > ( q ) ;
2014-04-22 14:57:17 +02:00
if ( c & & c - > isContainment ( ) ) {
2013-08-09 21:51:13 +02:00
//if we are the containment and there is still some uncomplete applet, we're still incomplete
if ( ! c - > d - > loadingApplets . isEmpty ( ) ) {
return ;
2014-04-22 14:57:17 +02:00
} else if ( ! uiReady & & started ) {
2013-09-13 20:03:25 +02:00
emit c - > uiReadyChanged ( true ) ;
2013-08-09 21:51:13 +02:00
}
} else {
c = q - > containment ( ) ;
if ( c ) {
q - > containment ( ) - > d - > loadingApplets . remove ( q ) ;
2014-04-22 14:57:17 +02:00
Applet * a = static_cast < Applet * > ( q - > containment ( ) ) ;
if ( q - > containment ( ) - > d - > loadingApplets . isEmpty ( ) & & ! a - > d - > uiReady ) {
a - > d - > uiReady = true ;
if ( a - > d - > started ) {
emit q - > containment ( ) - > uiReadyChanged ( true ) ;
}
2013-08-09 21:51:13 +02:00
}
}
}
uiReady = true ;
}
2012-02-20 16:38:00 +01:00
// put all setup routines for script here. at this point we can assume that
// package exists and that we have a script engine
2013-02-25 17:20:29 +01:00
void AppletPrivate : : setupPackage ( )
2012-02-20 16:38:00 +01:00
{
if ( ! package ) {
return ;
}
# ifndef NDEBUG
2013-07-29 19:05:59 +02:00
// qDebug() << "setting up script support, package is in" << package->path()
2013-08-01 12:15:38 +02:00
// << ", main script is" << package->filePath("mainscript");
2012-02-20 16:38:00 +01:00
# endif
2013-08-05 11:19:11 +02:00
// FIXME: Replace with ki18n functionality once semantics is clear.
// const QString translationsPath = package->filePath("translations");
// if (!translationsPath.isEmpty()) {
// KGlobal::dirs()->addResourceDir("locale", translationsPath);
// }
2012-02-20 16:38:00 +01:00
if ( ! package - > filePath ( " mainconfigui " ) . isEmpty ( ) ) {
q - > setHasConfigurationInterface ( true ) ;
}
}
QString AppletPrivate : : globalName ( ) const
{
if ( ! appletDescription . isValid ( ) ) {
return QString ( ) ;
}
return appletDescription . service ( ) - > library ( ) ;
}
2013-05-14 18:08:58 +02:00
void AppletPrivate : : scheduleConstraintsUpdate ( Plasma : : Types : : Constraints c )
2012-02-20 16:38:00 +01:00
{
// Don't start up a timer if we're just starting up
// flushPendingConstraints will be called by Corona
2013-05-10 19:29:13 +02:00
if ( started & & ! constraintsTimer . isActive ( ) & & ! ( c & Plasma : : Types : : StartupCompletedConstraint ) ) {
2012-02-20 16:38:00 +01:00
constraintsTimer . start ( 0 , q ) ;
}
2013-05-10 19:29:13 +02:00
if ( c & Plasma : : Types : : StartupCompletedConstraint ) {
2012-02-20 16:38:00 +01:00
started = true ;
2014-04-22 14:57:17 +02:00
if ( uiReady ) {
emit q - > containment ( ) - > uiReadyChanged ( true ) ;
}
2012-02-20 16:38:00 +01:00
}
pendingConstraints | = c ;
}
void AppletPrivate : : scheduleModificationNotification ( )
{
// modificationsTimer is not allocated until we get our notice of being started
if ( modificationsTimer ) {
// schedule a save
if ( modificationsTimer - > isActive ( ) ) {
modificationsTimer - > stop ( ) ;
}
modificationsTimer - > start ( 1000 , q ) ;
}
}
KConfigGroup * AppletPrivate : : mainConfigGroup ( )
{
if ( mainConfig ) {
return mainConfig ;
}
2014-02-03 18:47:51 +01:00
Containment * c = q - > containment ( ) ;
Plasma : : Applet * parentApplet = 0 ;
2014-02-06 12:12:43 +01:00
if ( c ) {
parentApplet = qobject_cast < Plasma : : Applet * > ( c - > parent ( ) ) ;
2014-02-03 18:47:51 +01:00
}
2013-12-11 13:41:22 +01:00
if ( q - > isContainment ( ) ) {
2014-04-26 01:45:47 +02:00
Corona * corona = static_cast < Containment * > ( q ) - > corona ( ) ;
2012-02-20 16:38:00 +01:00
KConfigGroup containmentConfig ;
2013-07-29 19:05:59 +02:00
//qDebug() << "got a corona, baby?" << (QObject*)corona << (QObject*)q;
2012-02-20 16:38:00 +01:00
2014-02-03 18:47:51 +01:00
if ( parentApplet ) {
containmentConfig = parentApplet - > config ( ) ;
containmentConfig = KConfigGroup ( & containmentConfig , " Containments " ) ;
} else if ( corona ) {
2012-02-20 16:38:00 +01:00
containmentConfig = KConfigGroup ( corona - > config ( ) , " Containments " ) ;
} else {
2012-03-24 19:33:54 +01:00
containmentConfig = KConfigGroup ( KSharedConfig : : openConfig ( ) , " Containments " ) ;
2012-02-20 16:38:00 +01:00
}
mainConfig = new KConfigGroup ( & containmentConfig , QString : : number ( appletId ) ) ;
} else {
KConfigGroup appletConfig ;
2014-02-03 18:47:51 +01:00
if ( c ) {
2012-02-20 16:38:00 +01:00
// applet directly in a Containment, as usual
appletConfig = c - > config ( ) ;
appletConfig = KConfigGroup ( & appletConfig , " Applets " ) ;
} else {
2013-07-29 19:05:59 +02:00
qWarning ( ) < < " requesting config for " < < q - > title ( ) < < " without a containment! " ;
2012-03-24 19:33:54 +01:00
appletConfig = KConfigGroup ( KSharedConfig : : openConfig ( ) , " Applets " ) ;
2012-02-20 16:38:00 +01:00
}
mainConfig = new KConfigGroup ( & appletConfig , QString : : number ( appletId ) ) ;
}
2013-02-26 14:48:39 +01:00
if ( configLoader ) {
configLoader - > setSharedConfig ( KSharedConfig : : openConfig ( mainConfig - > config ( ) - > name ( ) ) ) ;
2013-02-26 14:50:22 +01:00
configLoader - > readConfig ( ) ;
2013-02-26 14:48:39 +01:00
}
2013-02-26 14:50:22 +01:00
2012-02-20 16:38:00 +01:00
return mainConfig ;
}
void AppletPrivate : : resetConfigurationObject ( )
{
// make sure mainConfigGroup exists in all cases
mainConfigGroup ( ) ;
mainConfig - > deleteGroup ( ) ;
delete mainConfig ;
mainConfig = 0 ;
2012-09-24 14:30:42 +02:00
if ( ! q - > containment ( ) ) {
return ;
}
2014-04-26 01:45:47 +02:00
Corona * corona = q - > containment ( ) - > corona ( ) ;
2012-02-20 16:38:00 +01:00
if ( corona ) {
corona - > requireConfigSync ( ) ;
}
}
uint AppletPrivate : : s_maxAppletId = 0 ;
} //namespace Plasma