summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatthias Melcher <fltk@matthiasm.com>2010-01-24 00:16:33 +0000
committerMatthias Melcher <fltk@matthiasm.com>2010-01-24 00:16:33 +0000
commit416f5b0dcd3e2cf4993acf4dff02a7477be835e2 (patch)
treeab80d37da1737717d65637f739aa818e7346e19f /src
parent51acfa41eb2e0a823fc22ca76514a56ea0e152ae (diff)
Still not having added Fl_Tree and Fl_Table to Fluid, I remembered the plugin concept we had early on. It occured to me that writing plugins must not be difficult, and that FLTK already has everything needed. So here it is, a plugin implementation for FLTK. The MSWindows/Cygwin implementation is untested due to lack of a machine. The dynamic loading still needs a test implementation. Comments welcome.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@7023 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src')
-rw-r--r--src/Fl_Preferences.cxx205
1 files changed, 200 insertions, 5 deletions
diff --git a/src/Fl_Preferences.cxx b/src/Fl_Preferences.cxx
index 8864604a6..d29a1b6b8 100644
--- a/src/Fl_Preferences.cxx
+++ b/src/Fl_Preferences.cxx
@@ -28,6 +28,7 @@
#include <FL/Fl.H>
#include <FL/Fl_Preferences.H>
+#include <FL/Fl_Plugin.H>
#include <FL/Fl_Tree.H>
#include <FL/filename.H>
@@ -40,6 +41,7 @@
#include <time.h>
#if defined(WIN32) && !defined(__CYGWIN__)
+# include <windows.h>
# include <direct.h>
# include <io.h>
// Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
@@ -49,8 +51,10 @@
#elif defined (__APPLE__)
# include <Carbon/Carbon.h>
# include <unistd.h>
+# include <dlfcn.h>
#else
# include <unistd.h>
+# include <dlfcn.h>
#endif
#ifdef WIN32
@@ -65,6 +69,7 @@ typedef RPC_STATUS (WINAPI* uuid_func)(UUID __RPC_FAR *Uuid);
char Fl_Preferences::nameBuffer[128];
char Fl_Preferences::uuidBuffer[40];
+Fl_Preferences *Fl_Preferences::runtimePrefs = 0;
/**
* Returns a UUID as generated by the system.
@@ -198,8 +203,13 @@ const char *Fl_Preferences::newUUID()
name of your application. Both vendor and
application must be valid relative UNIX pathnames and
may contain '/'s to create deeper file structures.
+
+ A set of Preferences marked "run-time" exists exactly one per application and
+ only as long as the application runs. It can be used as a database for
+ volatile information. FLTK uses it to register plugins at run-time.
- \param[in] root can be \c USER or \c SYSTEM for user specific or system wide preferences
+ \param[in] root can be \c USER or \c SYSTEM for user specific or system wide
+ preferences
\param[in] vendor unique text describing the company or author of this file
\param[in] application unique text describing the application
*/
@@ -249,10 +259,26 @@ Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *group )
/**
+ \brief Create or access a group of preferences using a name.
+ \param[in] parent the parameter parent is a pointer to the parent group.
+ \p Parent may be \p NULL. It then refers to an application internal
+ database which exists only once, and remains in RAM only until the
+ application quits. This databse is used to manage plugins and other
+ data indexes by strings.
+ \param[in] group a group name that is used as a key into the databse
\see Fl_Preferences( Fl_Preferences&, const char *group )
*/
Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group )
{
+ if (parent==0) {
+ if (!runtimePrefs) {
+ runtimePrefs = new Fl_Preferences();
+ runtimePrefs->node = new Node( "." );
+ runtimePrefs->rootNode = new RootNode( runtimePrefs );
+ runtimePrefs->node->setRoot(rootNode);
+ }
+ parent = runtimePrefs;
+ }
rootNode = parent->rootNode;
node = parent->node->addChild( group );
}
@@ -313,6 +339,25 @@ Fl_Preferences::Fl_Preferences( Fl_Preferences::ID id )
rootNode = node->findRoot();
}
+/**
+ Create another reference to a Preferences group.
+ */
+Fl_Preferences::Fl_Preferences(const Fl_Preferences &rhs)
+: node(rhs.node),
+ rootNode(rhs.rootNode)
+{ }
+
+/**
+ Assign another reference to a Preference group.
+ */
+Fl_Preferences &Fl_Preferences::operator=(const Fl_Preferences &rhs) {
+ if (&rhs != this) {
+ node = rhs.node;
+ rootNode = rhs.rootNode;
+ }
+ return *this;
+}
+
/**
The destructor removes allocated resources. When used on the
@@ -1025,6 +1070,10 @@ static void makePathForFile( const char *path )
// create the root node
// - construct the name of the file that will hold our preferences
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char *vendor, const char *application )
+: prefs_(prefs),
+ filename_(0L),
+ vendor_(0L),
+ application_(0L)
{
char filename[ FL_PATH_MAX ]; filename[0] = 0;
#ifdef WIN32
@@ -1141,7 +1190,6 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char
"%s/%s.prefs", vendor, application);
#endif
- prefs_ = prefs;
filename_ = strdup(filename);
vendor_ = strdup(vendor);
application_ = strdup(application);
@@ -1152,6 +1200,10 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char
// create the root node
// - construct the name of the file that will hold our preferences
Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, const char *vendor, const char *application )
+: prefs_(prefs),
+ filename_(0L),
+ vendor_(0L),
+ application_(0L)
{
if (!vendor)
vendor = "unknown";
@@ -1163,13 +1215,22 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, con
snprintf(filename, sizeof(filename), "%s/%s.prefs", path, application);
filename_ = strdup(filename);
}
- prefs_ = prefs;
vendor_ = strdup(vendor);
application_ = strdup(application);
read();
}
+// create a root node that exists only on RAM and can not be read or written to
+// a file
+Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs )
+: prefs_(prefs),
+ filename_(0L),
+ vendor_(0L),
+ application_(0L)
+{
+}
+
// destroy the root node and all depending nodes
Fl_Preferences::RootNode::~RootNode()
{
@@ -1194,9 +1255,14 @@ Fl_Preferences::RootNode::~RootNode()
// read a preferences file and construct the group tree and with all entry leafs
int Fl_Preferences::RootNode::read()
{
+ if (!filename_) // RUNTIME preferences
+ return -1;
+
char buf[1024];
FILE *f = fl_fopen( filename_, "rb" );
- if ( !f ) return 0;
+ if ( !f )
+ return -1;
+
fgets( buf, 1024, f );
fgets( buf, 1024, f );
fgets( buf, 1024, f );
@@ -1236,9 +1302,14 @@ int Fl_Preferences::RootNode::read()
// write the group tree and all entry leafs
int Fl_Preferences::RootNode::write()
{
+ if (!filename_) // RUNTIME preferences
+ return -1;
+
fl_make_path_for_file(filename_);
FILE *f = fl_fopen( filename_, "wb" );
- if ( !f ) return 1;
+ if ( !f )
+ return -1;
+
fprintf( f, "; FLTK preferences file format 1.0\n" );
fprintf( f, "; vendor: %s\n", vendor_ );
fprintf( f, "; application: %s\n", application_ );
@@ -1250,6 +1321,9 @@ int Fl_Preferences::RootNode::write()
// get the path to the preferences directory
char Fl_Preferences::RootNode::getPath( char *path, int pathlen )
{
+ if (!filename_) // RUNTIME preferences
+ return -1;
+
strlcpy( path, filename_, pathlen);
char *s;
@@ -1673,6 +1747,127 @@ char Fl_Preferences::Node::copyTo(Fl_Tree *tree, Fl_Tree_Item *ti)
return 0;
}
+
+/**
+ * \brief Create a plugin.
+ *
+ * \param[in] klass plugins are grouped in classes
+ * \param[in] name every plugin should have a unique name
+ */
+Fl_Plugin::Fl_Plugin(const char *klass, const char *name)
+: id(0)
+{
+ Fl_Plugin_Manager pm(klass);
+ id = pm.addPlugin(name, this);
+}
+
+/**
+ * \brief Clear the plugin and remove it from the database.
+ */
+Fl_Plugin::~Fl_Plugin()
+{
+ if (id)
+ Fl_Plugin_Manager::remove(id);
+}
+
+
+/**
+ * \brief Manage all plugins belonging to one class.
+ */
+Fl_Plugin_Manager::Fl_Plugin_Manager(const char *klass)
+: Fl_Preferences(0, Fl_Preferences::Name("%s/%s", "plugins", klass))
+{
+}
+
+/**
+ * \brief Remove the plugin manager.
+ *
+ * Calling this does not remove the database itself or any plugins. It just
+ * removes the reference to the database.
+ */
+Fl_Plugin_Manager::~Fl_Plugin_Manager()
+{
+}
+
+/**
+ * \brief Return the address of a plugin by index.
+ */
+Fl_Plugin *Fl_Plugin_Manager::plugin(int index)
+{
+ char buf[32];
+ Fl_Plugin *ret = 0;
+ Fl_Preferences pin(this, index);
+ pin.get("address", buf, "@0", 32);
+ sscanf(buf, "@%p", &ret);
+ return ret;
+}
+
+/**
+ * \brief This function adds a new plugin to the databse.
+ *
+ * There is no need to call this function explicitly. Every Fl_Plugin constructor
+ * will call this function at initialization time.
+ */
+Fl_Preferences::ID Fl_Plugin_Manager::addPlugin(const char *name, Fl_Plugin *plugin)
+{
+ char buf[32];
+ Fl_Preferences pin(this, name);
+ snprintf(buf, 32, "@%p", plugin);
+ pin.set("address", buf);
+ return pin.id();
+}
+
+/**
+ * \brief Remove any plugin.
+ *
+ * There is no need to call this function explicitly. Every Fl_Plugin destructor
+ * will call this function at destruction time.
+ */
+void Fl_Plugin_Manager::removePlugin(Fl_Preferences::ID id)
+{
+ Fl_Preferences::remove(id);
+}
+
+/**
+ * \brief Load a module from disk.
+ *
+ * A module must be a dynamically linkable file for the given operating system.
+ * When loading a module, its +init function will be called which in turn calls
+ * the constructor of all statically initialized Fl_Plugin classes and adds
+ * them to the database.
+ */
+int Fl_Plugin_Manager::load(const char *filename)
+{
+ // the functions below will autmaticaly load plugins that are defined:
+ // Fl_My_Plugin plugin();
+#ifdef WIN32
+ HMODULE dl = LoadLibrary(filename);
+#else
+ void * dl = dlopen(filename, RTLD_LAZY);
+#endif
+ // There is no way of unloading a plugin!
+ return (dl!=0) ? 0 : -1;
+}
+
+/**
+ * \brief Use this function to load a whole directory full of modules.
+ */
+int Fl_Plugin_Manager::loadAll(const char *filepath, const char *pattern)
+{
+ struct dirent **dir;
+ int i, n = fl_filename_list(filepath, &dir);
+ for (i=0; i<n; i++) {
+ struct dirent *e = dir[i];
+ if (pattern==0 || fl_filename_match(e->d_name, pattern)) {
+ load(Fl_Preferences::Name("%s%s", filepath, e->d_name));
+ }
+ free(e);
+ }
+ free(dir);
+ return 0;
+}
+
+
//
// End of "$Id$".
//