-rw-r--r-- | libopie2/examples/opiecore/oplugins/oplugins.cpp | 37 | ||||
-rw-r--r-- | libopie2/examples/opiecore/oplugins/oplugins.pro | 11 | ||||
-rw-r--r-- | libopie2/opiecore/opiecore.pro | 2 | ||||
-rw-r--r-- | libopie2/opiecore/opluginloader.cpp | 270 | ||||
-rw-r--r-- | libopie2/opiecore/opluginloader.h | 22 |
5 files changed, 266 insertions, 76 deletions
diff --git a/libopie2/examples/opiecore/oplugins/oplugins.cpp b/libopie2/examples/opiecore/oplugins/oplugins.cpp new file mode 100644 index 0000000..4aa2da5 --- a/dev/null +++ b/libopie2/examples/opiecore/oplugins/oplugins.cpp @@ -0,0 +1,37 @@ +/* + * You may copy, modify and or distribute without any limitation + */ +#include <opie2/odebug.h> +#include <opie2/opluginloader.h> + +#include <opie2/todayplugininterface.h> + +using Opie::Core::OPluginItem; +using Opie::Core::OGenericPluginLoader; + +void debugLst( const OPluginItem::List& lst ) { + for ( OPluginItem::List::ConstIterator it = lst.begin(); it != lst.end(); ++it ) + odebug << "Name " << (*it).name() << " " << (*it).path() << " " << (*it).position() << oendl; +} + + +int main( void ) { + OGenericPluginLoader loader( "today", true ); + loader.setAutoDelete( true ); + + odebug << "IS in Safe Mode" << loader.isInSafeMode() << oendl; + + OPluginItem::List lst = loader.allAvailable( true ); + debugLst( lst ); + + lst = loader.filtered( true ); + debugLst( lst ); + + for ( OPluginItem::List::Iterator it = lst.begin(); it != lst.end(); ++it ) { + QUnknownInterface* iface = loader.load( *it, IID_TodayPluginInterface ); + } + + /* + * now it's autodelete so cleaned up for us + */ +} diff --git a/libopie2/examples/opiecore/oplugins/oplugins.pro b/libopie2/examples/opiecore/oplugins/oplugins.pro new file mode 100644 index 0000000..7286de9 --- a/dev/null +++ b/libopie2/examples/opiecore/oplugins/oplugins.pro @@ -0,0 +1,11 @@ +TEMPLATE = app +CONFIG += qt warn_on + +SOURCES = oplugins.cpp + +INCLUDEPATH += $(OPIEDIR)/include +DEPENDSPATH += $(OPIEDIR)/include + +LIBS += -lopiecore2 + +include ( $(OPIEDIR)/include.pro ) diff --git a/libopie2/opiecore/opiecore.pro b/libopie2/opiecore/opiecore.pro index a9562b5..5056d48 100644 --- a/libopie2/opiecore/opiecore.pro +++ b/libopie2/opiecore/opiecore.pro @@ -1,45 +1,47 @@ TEMPLATE = lib CONFIG += qt warn_on DESTDIR = $(OPIEDIR)/lib HEADERS = oapplication.h \ oconfig.h \ odebug.h \ oglobal.h \ oglobalsettings.h \ okeyconfigmanager.h \ + opluginloader.h \ oprocess.h \ oprocctrl.h \ osmartpointer.h \ ostorageinfo.h \ xmltree.h SOURCES = oapplication.cpp \ oconfig.cpp \ odebug.cpp \ oglobal.cpp \ oglobalsettings.cpp \ okeyconfigmanager.cpp \ + opluginloader.cpp \ oprocess.cpp \ oprocctrl.cpp \ ostorageinfo.cpp \ xmltree.cpp # The following files are currently not compileable on mac ! # Therfore I removed them from the build .. (eilers) CONFTEST = $$system( echo $CONFIG_TARGET_MACOSX ) !contains( CONFTEST, y ) { HEADERS += ofilenotify.h SOURCES += ofilenotify.cpp } else { message( "ofilenotify is not available in a mac build !" ) } include ( device/device.pro ) INTERFACES = TARGET = opiecore2 VERSION = 1.9.1 INCLUDEPATH += $(OPIEDIR)/include DEPENDPATH += $(OPIEDIR)/include diff --git a/libopie2/opiecore/opluginloader.cpp b/libopie2/opiecore/opluginloader.cpp index e5a0903..b473cd7 100644 --- a/libopie2/opiecore/opluginloader.cpp +++ b/libopie2/opiecore/opluginloader.cpp @@ -1,189 +1,246 @@ /* * LGPLv2 or later * zecke@handhelds.org */ #include "opluginloader.h" +#include "oconfig.h" + +#include <qpe/qpeapplication.h> + +#include <qdir.h> +#include <qdict.h> +#include <qtl.h> #include <stdlib.h> namespace Opie { namespace Core { namespace Internal { struct OPluginLibraryHolder { static OPluginLibraryHolder *self(); - QLibrary *getLibrary( const QString& ); - void putLibrary( QLibrary* ); + QLibrary *ref( const QString& ); + void deref( QLibrary* ); private: + OPluginLibraryHolder(); ~OPluginLibraryHolder(); - OPluginLibraryHolder* m_self; - + QDict<QLibrary> m_libs; + static OPluginLibraryHolder* m_self; }; + + OPluginLibraryHolder* OPluginLibraryHolder::m_self = 0; + OPluginLibraryHolder* OPluginLibraryHolder::self() { + if ( !m_self ) + m_self = new OPluginLibraryHolder; + + return m_self; + } + + OPluginLibraryHolder::OPluginLibraryHolder() {} + OPluginLibraryHolder::~OPluginLibraryHolder() {} + + /* + * We do simple ref counting... We will add the QLibrary again + * and again to the dictionary and on deref we will pop and pop it + * until there are no more library and we will unload and delete the library + * luckily dlopen does some ref counting as well so we don't need + * to hack QPEApplication + */ + QLibrary* OPluginLibraryHolder::ref(const QString& str) { + QLibrary *lib = m_libs[str]; + + /* if not in the dict try to load it */ + if ( !lib ) { + lib = new QLibrary( str, QLibrary::Immediately ); + if ( !lib->isLoaded() ) { + delete lib; + return 0l; + } + } + + /* now refcount one up */ + m_libs.insert( str, lib ); + return lib; + } + + /* + * 'unshadow' the items until we're the last then unload and delete + */ + void OPluginLibraryHolder::deref( QLibrary* lib ) { + if ( !lib ) + return; + + QString str = lib->library(); + /* no need to check if the lib was inserted or such */ + (void) m_libs.take( str ); + if ( !m_libs[str] ) { + lib->unload(); + delete lib; + } + } +} + +/** + * We want values with 30,29,28 at the beginning of the list + */ + +bool operator<( const OPluginItem& l, const OPluginItem& r ) { + return l.position() > r.position(); +} + +bool operator>( const OPluginItem& l, const OPluginItem& r ) { + return l.position() < r.position(); +} + +bool operator<=( const OPluginItem& l, const OPluginItem& r ) { + return l.position() >= r.position(); } -}} /** * \brief Creates an Empty OPluginItem * * create an empty pluginitem. Position is set to -1 and the * other things are used with the default ctors */ OPluginItem::OPluginItem() : m_pos( -1 ) { } /** * \brief Create an OPluginItem * * Create a Plugin Item with all information. * * @param name The if available translated Name - * @param conf The name of the config group * @param path The path to the plugin * @param pos The position of the plugin if used for sorting * */ -OPluginItem::OPluginItem( const QString& name, const QCString& conf, - const QString& path, int pos = -1 ) - : m_name( name ), m_conf( conf ), m_path( path ), m_pos( pos ) { +OPluginItem::OPluginItem( const QString& name, const QString& path, int pos ) + : m_name( name ), m_path( path ), m_pos( pos ) { } /** * \brief simple d'tor */ OPluginItem::~OPluginItem() { } /** * operator to test equalness of two OPluginItem */ bool OPluginItem::operator==( const OPluginItem& r )const{ if ( m_pos != r.m_pos ) return false; if ( m_name != r.m_name ) return false; - if ( m_conf != r.m_conf ) return false; if ( m_path != r.m_path ) return false; return true; } -bool OPluginItem::operator!=( const OPluginItem& r ) { +bool OPluginItem::operator!=( const OPluginItem& r )const{ return !( *this == r ); } /** * return the name of this Plugin */ QString OPluginItem::name()const { return m_name; } /** - * return the config key - */ -QCString OPluginItem::configKey()const{ - return m_conf; -} - -/** * return the path of the plugin */ QString OPluginItem::path()const { return m_path; } int OPluginItem::position()const{ return m_pos; } /** * Set the name of the Plugin Item * @param name */ void OPluginItem::setName( const QString& name ) { m_name = name; } /** - * Set the config key - * @param key The config key/group of this plugin - */ -void OPluginItem::setConfigKey( const QCString& conf ) { - m_conf = conf; -} - -/** * Set the path of Plugin Item * @param name The path of the plugin */ void OPluginItem::setPath( const QString& name ) { m_name = name; } /** * Set the position * @param pos The position */ void OPluginItem::setPosition( int pos ) { m_pos = pos; } /** * \brief create a PluginLoader * * Create a PluginLoader autoDelete is set to false * * \code * Opie::Core::OGenericPluginLoader loader("myapp-plugin"); * Opie::Core::OPluginItem::List lst = loader.filtered(); * for(Opie::Core::OPluginItem::List::Iterator it = lst.begin(); it!=lst.end();++it){ * MyIface* iface = static_cast<MyIface*>(loader.load(*it,IID_MyIface)); * } * \endcode * * \code * Opie::Core::OGenericPluginLoader loader("myapp-plugin"); * Opie::Core::OPluginItem::List lst = loader.filtered(); * for(Opie::Core::OPluginItem::List::Iterator it = lst.begin(); it!=lst.end();++it){ * MyIface* iface = static_cast<MyIface*>(loader.load(*it,IID_MyIface)); * } * ... * loader.clear(); * * \endcode * * @param name The name of the plugin directory. * @param isSorted Tell the PluginLoader if your Plugins are sorted */ OGenericPluginLoader::OGenericPluginLoader( const QString& name, bool isSorted) : m_dir( name ), m_autoDelete( false ), m_isSafeMode( false ), - m_isSorted( isSorted ), m_readConfig( false ) + m_isSorted( isSorted ) { setPluginDir( QPEApplication::qpeDir() + "/plugins/"+name ); + readConfig(); } /** * calls clear if autoDelete is true. This will release all interfaces * and remove the library from this process if the refcount falls to zero */ OGenericPluginLoader::~OGenericPluginLoader() { if ( m_autoDelete ) clear(); } /** * enable autoDelete. This will call clear on the d'tor * * @see ~OGenericPluginLoader * @see clear() */ void OGenericPluginLoader::setAutoDelete( bool t ) { m_autoDelete = t; } /** * see if autoDelet is enabled @@ -192,206 +249,293 @@ bool OGenericPluginLoader::autoDelete()const{ return m_autoDelete; } /** * This will unload all returned QUnknownInterfaces by load. Unload * will be called. */ void OGenericPluginLoader::clear() { QPtrDictIterator<QLibrary> it( m_library ); for ( ;it.current(); ) unload( static_cast<QUnknownInterface*>( it.currentKey() ) ); } /** * This will take the iface from the internal QPtrDict, Release it, * and deref the libray used. * The visibility depends on the QPtrDict. * @see QPtrDict::insert */ void OGenericPluginLoader::unload( QUnknownInterface* iface ) { if ( !iface ) return; iface->release(); - OPluginLibraryHolder::self()->deref( m_library.take( iface ) ); + Internal::OPluginLibraryHolder::self()->deref( m_library.take( iface ) ); } /** * This tells you * if by previous tries to load, loading crashed your application. * If isInSafeMode you can use the GUI to configure the plugins prior to loading * * @return true if prior loading failed */ bool OGenericPluginLoader::isInSafeMode()const { - if ( !m_readConfig ) - readConfig(); return m_isSafeMode; } -/** - * return if the plugin list is sorted. - */ -bool OGenericPluginLoader::isSorted()const { - if ( !m_readConfig ) - readConfig(); - return m_isSorted; -} /** * Return the list of all available plugins. This will go through all plugin * directories and search for your type of plugins ( by subdir ) * * @param sorted Tell if you want to have the positions sorted. This only makes sense if you */ OPluginItem::List OGenericPluginLoader::allAvailable( bool sorted )const { OPluginItem::List lst; for ( QStringList::ConstIterator it = m_plugDirs.begin(); it != m_plugDirs.end(); ++it ) lst += plugins( *it, sorted, false ); if ( sorted ) qHeapSort( lst ); return lst; } /** * Return only activated plugins. * * @param sorted If the list should be sorted */ OPluginItem::List OGenericPluginLoader::filtered( bool sorted )const { OPluginItem::List lst; for ( QStringList::ConstIterator it = m_plugDirs.begin(); it != m_plugDirs.end(); ++it ) lst += plugins( *it, sorted, true ); if ( sorted ) qHeapSort( lst ); return lst; } /** * */ QUnknownInterface* OGenericPluginLoader::load( const OPluginItem& item, const QUuid& uuid) { /* * Check if there could be a library */ QString pa = item.path(); if ( pa.isEmpty() ) return 0l; /* * See if we get a library * return if we've none */ - setSafeMode( true ); - QLibrary *lib = OPluginLibraryHolder::self()->getLibrary( pa ); + setSafeMode( pa, true ); + QLibrary *lib = Internal::OPluginLibraryHolder::self()->ref( pa ); if ( !lib ) { setSafeMode(); return 0l; } /** * try to load the plugin and just in case initialize the pointer to a pointer again */ - QUnknownInterface** iface = 0; - if ( lib->queryInterface( uuid, iface ) == QS_OK ) { + QUnknownInterface* iface=0; + if ( lib->queryInterface( uuid, &iface ) == QS_OK ) { installTranslators(pa.left( pa.find("."))); - m_library.insert( *iface, lib ); + m_library.insert( iface, lib ); }else - *iface = 0; + iface = 0; setSafeMode(); - return *iface; + return iface; } +/** + * @internal and reads in the safe mode + */ void OGenericPluginLoader::readConfig() { - m_readConfig = true; - /* read the config for SafeMode */ - OConfig conf( m_name + "-odpplugins" ); + OConfig conf( m_dir + "-odpplugins" ); conf.setGroup( "General" ); m_isSafeMode = conf.readBoolEntry( "SafeMode", false ); } -void OGenericPluginLoader::setSafeMode(bool b) { - OConfig conf( m_name + "-odpplugins" ); +/** + * Enter or leave SafeMode + */ +void OGenericPluginLoader::setSafeMode(const QString& str, bool b) { + OConfig conf( m_dir + "-odpplugins" ); conf.setGroup( "General" ); conf.writeEntry( "SafeMode", b ); + conf.writeEntry( "CrashedPlugin", str ); } +/** + * @internal + * + * Set the List of Plugin Dirs to lst. Currently only QPEApplication::qpeDir()+"/plugins/"+mytype + * is used as plugin dir + */ void OGenericPluginLoader::setPluginDirs( const QStringList& lst ) { m_plugDirs = lst; } +/** + * + * @internal + * Set the Plugin Dir to str. Str will be the only element in the list of plugin dirs + */ void OGenericPluginLoader::setPluginDir( const QString& str) { m_plugDirs.clear(); m_plugDirs.append( str ); } -bool &OGenericPluginLoader::isSafeMode()const { - return m_isSafeMode; -} -bool &OGenericPluginLoader::isSorted()const { +/** + * @internal + */ +bool OGenericPluginLoader::isSorted()const{ return m_isSorted; } -OPluginItem::List OGenericPluginLoader::plugins( const QString& dir, bool sorted, bool disabled ) { +/* + * make libfoo.so.1.0.0 -> foo on UNIX + * make libfoo.dylib -> foo on MAC OS X Unix + * windows is obviously missing + */ +/** + * @internal + */ +QString OGenericPluginLoader::unlibify( const QString& str ) { + QString st = str.mid( str.find( "lib" )+3 ); +#ifdef Q_OS_MACX + return st.left( st.findRev( ".dylib" ) ); +#else + return st.left( st.findRev( ".so" ) ); +#endif +} + +/** + * Return a List of Plugins for a dir and add positions and remove disabled. + * If a plugin is on the excluded list assign position -2 + * + * @param dir The dir to look in + * @param sorted Should positions be read? + * @param disabled Remove excluded from the list + */ +OPluginItem::List OGenericPluginLoader::plugins( const QString& _dir, bool sorted, bool disabled )const { #ifdef Q_OS_MACX - QDir dir( dir, "lib*.dylib" ); + QDir dir( _dir, "lib*.dylib" ); #else - QDir dir( dir, "lib*.so" ); + QDir dir( _dir, "lib*.so" ); #endif + + OPluginItem::List lst; + /* * get excluded list and then iterate over them + * Excluded list contains the name + * Position is a list with 'name.pos.name.pos.name.pos' + * + * For the look up we will create two QMap<QString,pos> */ - OConfig cfg( m_name+"odpplugins" ); - cfg.setGroup( dir ); - QStringList excludes = cfg.readListEntry( "Excluded" ); + QMap<QString, int> positionMap; + QMap<QString, int> excludedMap; + + + OConfig cfg( m_dir+"odpplugins" ); + cfg.setGroup( _dir ); + + + QStringList excludes = cfg.readListEntry( "Excluded", ',' ); + for ( QStringList::Iterator it = excludes.begin(); it != excludes.end(); ++it ) + excludedMap.insert( *it, -2 ); + + if ( m_isSorted ) { + QStringList pos = cfg.readListEntry( "Positions", '.' ); + QStringList::Iterator it = pos.begin(); + while ( it != pos.end() ) + positionMap.insert( *it++, (*it++).toInt() ); + } + + + QStringList list = dir.entryList(); -// for ( + QStringList::Iterator it; + for (QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + QString str = unlibify( *it ); + OPluginItem item( str, _dir + "/" + *it ); + bool ex = excludedMap.contains( str ); + /* + * if disabled but we should show all assign a -2 + * else continue because we don't want to add the item + * else if sorted we assign the right position + */ + if ( ex && !disabled) + item.setPosition( -2 ); + else if ( ex && disabled ) + continue; + else if ( sorted ) + item.setPosition( positionMap[str] ); + + lst.append( item ); } + return lst; +} +/** + * @internal generate a list of languages from $LANG + */ QStringList OGenericPluginLoader::languageList() { - if ( languageList.isEmpty() ) { + if ( m_languages.isEmpty() ) { /* * be_BY.CP1251 We will add, be_BY.CP1251,be_BY,be * to our list of languages. */ QString str = ::getenv( "LANG" ); m_languages += str; int pos = str.find( '.' ); if ( pos > 0 ) m_languages += str.left( pos ); int n_pos = str.find( '_' ); if ( pos > 0 && n_pos >= pos ) m_languages += str.left( n_pos ); } return m_languages; } void OGenericPluginLoader::installTranslators(const QString& type) { QStringList lst = languageList(); + + /* + * for each language and maybe later for each language dir... + * try to load a Translator + */ for ( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) { QTranslator* trans = new QTranslator( qApp ); - QString tfn = QPEApplication::qpeDir()+"/i18n/" + lang + "/" + type + ".qm" ; + QString tfn = QPEApplication::qpeDir()+"/i18n/" + *it + "/" + type + ".qm" ; + + /* + * If loaded then install else clean up and don't leak + */ if ( trans->load( tfn ) ) qApp->installTranslator( trans ); else delete trans; } } } } diff --git a/libopie2/opiecore/opluginloader.h b/libopie2/opiecore/opluginloader.h index a7df4a8..421d1f6 100644 --- a/libopie2/opiecore/opluginloader.h +++ b/libopie2/opiecore/opluginloader.h @@ -1,168 +1,164 @@ /* * LGPLv2 or later * zecke@handhelds.org */ #ifndef ODP_CORE_OPLUGIN_LOADER_H #define ODP_CORE_OPLUGIN_LOADER_H #include <qpe/qlibrary.h> +#include <qptrdict.h> #include <qstringlist.h> namespace Opie { namespace Core { class OConfig; namespace Internal { class OPluginLibraryHolder; } template class QPtrDict<QLibrary>; /** * \brief A small item representing the Plugin Information * This class contains the information about a Plugin. It contains * a translated name if available to the system, a config key, * and the path location. * * @since 1.2 * */ class OPluginItem { public: typedef QValueList<OPluginItem> List; OPluginItem(); - OPluginItem( const QString& name, const QCString& confopt, const QString& path, int pos = -1 ); + OPluginItem( const QString& name, const QString& path, int pos = -1 ); ~OPluginItem(); bool operator==( const OPluginItem& )const; bool operator!=( const OPluginItem& )const; QString name()const; - QCString configKey()const; QString path()const; int position()const; void setName( const QString& ); - void setConfigKey( const QCString& ); void setPath( const QString& ); void setPosition( int ); private: QString m_name; - QCString m_conf; QString m_path; int m_pos; struct Private; Private *d; }; /** * \brief A generic class to easily load and manage plugins * * This is the generic non sepcialised loader for plugins. Normally * you would prefer using the OPluginLoader directly. This class * exists to minimize the application binary size due the usage * of templates in the specialized API * * @since 1.2 * @see OPluginLoader */ class OGenericPluginLoader { public: typedef OPluginItem::List List; OGenericPluginLoader( const QString &name, bool isSorted = false ); virtual ~OGenericPluginLoader(); void setAutoDelete( bool ); bool autoDelete()const; void clear(); bool isInSafeMode()const; List allAvailable(bool sorted = FALSE)const; List filtered(bool sorted = FALSE)const; virtual QUnknownInterface* load( const OPluginItem& item, const QUuid& ); virtual void unload( QUnknownInterface* ); protected: - virtual void readConfig(); + void readConfig(); virtual List plugins( const QString& dir, bool sorted, bool disabled )const; void setPluginDirs( const QStringList& ); void setPluginDir( const QString& ); - bool &isSafeMode()const; - bool &isSorted()const; - void readConfig()const; - void setSafeMode(bool b = false); + bool isSorted()const; + void setSafeMode(const QString& app = QString::null, bool b = false); + static QString unlibify( const QString& str ); private: - QString languageList(); + QStringList languageList(); void installTranslators(const QString& type); QString m_dir; QStringList m_plugDirs; QStringList m_languages; bool m_autoDelete : 1; bool m_isSafeMode : 1; - bool m_readConfig : 1; bool m_isSorted : 1; QPtrDict<QLibrary> m_library; struct Private; Private* d; }; /** * \brief The class to load your QCOM+ plugins * * This class takes care of activation and even the order * if you need it. It is normally good to place a .directory file * into your plugin directory if you need order of activation. * * You'll create the OPluginLoader and then use it to load the filtered * plugins. * * There is also a GUI for the configuration and a Manager to write the * mentioned .directory file * * On crash the safe mode is activated for the next run. You can then decide * if you want to load plugins or come up with the Configuration on * next start yourself then. * * @since 1.2 */ class OPluginLoader : public OGenericPluginLoader { public: OPluginLoader( const QString& name, bool sorted = false ); ~OPluginLoader(); - temlate<class IFace> + template<class IFace> IFace* load( const QString& name, const QUuid& ); - temlate<class IFace> + template<class IFace> IFace* load( const OPluginItem& item, const QUuid& ); }; /** * \brief A class to manager order and activation of plugins * * Manage order and activation. This is used by the Opie::Ui::OPluginConfig * This class controls the activation and order of plugins depending * on the OPluginLoader you supply. * * @see OPluginConfig * */ class OPluginManager { public: OPluginManager( OGenericPluginLoader* , const QString& name); OPluginManager( OConfig* conf, const QString&, const QCString& group, const OPluginItem::List& ); ~OPluginManager(); QString name(); void setName( const QString& ); void setPosition( const OPluginItem& ); |