/fltk/fltk/Fl_Preferences.cxx
C++ | 1803 lines | 1125 code | 110 blank | 568 comment | 269 complexity | c8f64c94162172fa60b81eafc4028999 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- //
- // "$Id: Fl_Preferences.cxx 8291 2011-01-19 06:33:48Z manolo $"
- //
- // Preferences methods for the Fast Light Tool Kit (FLTK).
- //
- // Copyright 2002-2010 by Matthias Melcher.
- //
- // This library 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 of the License, or (at your option) any later version.
- //
- // This library 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
- // Library General Public License for more details.
- //
- // You should have received a copy of the GNU Library General Public
- // License along with this library; if not, write to the Free Software
- // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- // USA.
- //
- // Please report all bugs and problems on the following page:
- //
- // http://www.fltk.org/str.php
- //
- #include <FL/Fl.H>
- #include <FL/Fl_Preferences.H>
- #include <FL/Fl_Plugin.H>
- #include <FL/filename.H>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <FL/fl_utf8.h>
- #include "flstring.h"
- #include <sys/stat.h>
- #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
- // on Windows, which is supposed to be POSIX compliant...
- # define access _access
- # define mkdir _mkdir
- #elif defined (__APPLE__)
- # include <ApplicationServices/ApplicationServices.h>
- # include <unistd.h>
- # include <dlfcn.h>
- #else
- # include <unistd.h>
- # include <dlfcn.h>
- #endif
- #ifdef WIN32
- # include <windows.h>
- # include <rpc.h>
- // function pointer for the UuidCreate Function
- // RPC_STATUS RPC_ENTRY UuidCreate(UUID __RPC_FAR *Uuid);
- typedef RPC_STATUS (WINAPI* uuid_func)(UUID __RPC_FAR *Uuid);
- #else
- # include <sys/time.h>
- #endif // WIN32
- #ifdef __CYGWIN__
- # include <wchar.h>
- #endif
- char Fl_Preferences::nameBuffer[128];
- char Fl_Preferences::uuidBuffer[40];
- Fl_Preferences *Fl_Preferences::runtimePrefs = 0;
- /**
- * Returns a UUID as generated by the system.
- *
- * A UUID is a "universally unique identifier" which is commonly used in
- * configuration files to create identities. A UUID in ASCII looks like this:
- * <tt>937C4900-51AA-4C11-8DD3-7AB59944F03E</tt>. It has always 36 bytes plus
- * a trailing zero.
- *
- * \return a pointer to a static buffer containing the new UUID in ASCII format.
- * The buffer is overwritten during every call to this function!
- */
- const char *Fl_Preferences::newUUID() {
- #ifdef __APPLE__
- CFUUIDRef theUUID = CFUUIDCreate(NULL);
- CFUUIDBytes b = CFUUIDGetUUIDBytes(theUUID);
- sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
- b.byte0, b.byte1, b.byte2, b.byte3, b.byte4, b.byte5, b.byte6, b.byte7,
- b.byte8, b.byte9, b.byte10, b.byte11, b.byte12, b.byte13, b.byte14, b.byte15);
- CFRelease(theUUID);
- #elif defined (WIN32)
- // First try and use the win API function UuidCreate(), but if that is not
- // available, fall back to making something up from scratch.
- // We do not want to link against the Rpcrt4.dll, as we will rarely use it,
- // so we load the DLL dynamically, if it is available, and work from there.
- static HMODULE hMod = NULL;
- UUID ud;
- UUID *pu = &ud;
- int got_uuid = 0;
- if (!hMod) { // first time in?
- hMod = LoadLibrary("Rpcrt4.dll");
- }
- if (hMod) { // do we have a usable handle to Rpcrt4.dll?
- uuid_func uuid_crt = (uuid_func)GetProcAddress(hMod, "UuidCreate");
- if (uuid_crt != NULL) {
- RPC_STATUS rpc_res = uuid_crt(pu);
- if ( // is the return status OK for our needs?
- (rpc_res == RPC_S_OK) || // all is well
- (rpc_res == RPC_S_UUID_LOCAL_ONLY) || // only unique to this machine
- (rpc_res == RPC_S_UUID_NO_ADDRESS) // probably only locally unique
- ) {
- got_uuid = -1;
- sprintf(uuidBuffer, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
- pu->Data1, pu->Data2, pu->Data3, pu->Data4[0], pu->Data4[1],
- pu->Data4[2], pu->Data4[3], pu->Data4[4],
- pu->Data4[5], pu->Data4[6], pu->Data4[7]);
- }
- }
- }
- if (got_uuid == 0) { // did not make a UUID - use fallback logic
- unsigned char b[16];
- time_t t = time(0); // first 4 byte
- b[0] = (unsigned char)t;
- b[1] = (unsigned char)(t>>8);
- b[2] = (unsigned char)(t>>16);
- b[3] = (unsigned char)(t>>24);
- int r = rand(); // four more bytes
- b[4] = (unsigned char)r;
- b[5] = (unsigned char)(r>>8);
- b[6] = (unsigned char)(r>>16);
- b[7] = (unsigned char)(r>>24);
- // Now we try to find 4 more "random" bytes. We extract the
- // lower 4 bytes from the address of t - it is created on the
- // stack so *might* be in a different place each time...
- // This is now done via a union to make it compile OK on 64-bit systems.
- union { void *pv; unsigned char a[sizeof(void*)]; } v;
- v.pv = (void *)(&t);
- // NOTE: This assume that all WinXX systems are little-endian
- b[8] = v.a[0];
- b[9] = v.a[1];
- b[10] = v.a[2];
- b[11] = v.a[3];
- TCHAR name[MAX_COMPUTERNAME_LENGTH + 1]; // only used to make last four bytes
- DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
- // GetComputerName() does not depend on any extra libs, and returns something
- // analogous to gethostname()
- GetComputerName(name, &nSize);
- // use the first 4 TCHAR's of the name to create the last 4 bytes of our UUID
- for (int ii = 0; ii < 4; ii++) {
- b[12 + ii] = (unsigned char)name[ii];
- }
- sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
- b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
- b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
- }
- #else
- // warning Unix implementation of Fl_Preferences::newUUID() incomplete!
- // #include <uuid/uuid.h>
- // void uuid_generate(uuid_t out);
- unsigned char b[16];
- time_t t = time(0); // first 4 byte
- b[0] = (unsigned char)t;
- b[1] = (unsigned char)(t>>8);
- b[2] = (unsigned char)(t>>16);
- b[3] = (unsigned char)(t>>24);
- int r = rand(); // four more bytes
- b[4] = (unsigned char)r;
- b[5] = (unsigned char)(r>>8);
- b[6] = (unsigned char)(r>>16);
- b[7] = (unsigned char)(r>>24);
- unsigned long a = (unsigned long)&t; // four more bytes
- b[8] = (unsigned char)a;
- b[9] = (unsigned char)(a>>8);
- b[10] = (unsigned char)(a>>16);
- b[11] = (unsigned char)(a>>24);
- // Now we try to find 4 more "random" bytes. We extract the
- // lower 4 bytes from the address of t - it is created on the
- // stack so *might* be in a different place each time...
- // This is now done via a union to make it compile OK on 64-bit systems.
- union { void *pv; unsigned char a[sizeof(void*)]; } v;
- v.pv = (void *)(&t);
- // NOTE: May need to handle big- or little-endian systems here
- # if WORDS_BIGENDIAN
- b[8] = v.a[sizeof(void*) - 1];
- b[9] = v.a[sizeof(void*) - 2];
- b[10] = v.a[sizeof(void*) - 3];
- b[11] = v.a[sizeof(void*) - 4];
- # else /* data ordered for a little-endian system */
- b[8] = v.a[0];
- b[9] = v.a[1];
- b[10] = v.a[2];
- b[11] = v.a[3];
- # endif
- char name[80]; // last four bytes
- gethostname(name, 79);
- memcpy(b+12, name, 4);
- sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
- b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
- b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
- #endif
- return uuidBuffer;
- }
- /**
- The constructor creates a group that manages name/value pairs and
- child groups. Groups are ready for reading and writing at any time.
- The root argument is either Fl_Preferences::USER
- or Fl_Preferences::SYSTEM.
- This constructor creates the <i>base</i> instance for all
- following entries and reads existing databases into memory. The
- vendor argument is a unique text string identifying the
- development team or vendor of an application. A domain name or
- an EMail address are great unique names, e.g.
- "researchATmatthiasm.com" or "fltk.org". The
- application argument can be the working title or final
- 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] vendor unique text describing the company or author of this file
- \param[in] application unique text describing the application
- */
- Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application ) {
- node = new Node( "." );
- rootNode = new RootNode( this, root, vendor, application );
- node->setRoot(rootNode);
- }
- /**
- \brief Use this constructor to create or read a preferences file at an
- arbitrary position in the file system.
- The file name is generated in the form
- <tt><i>path</i>/<i>application</i>.prefs</tt>. If \p application
- is \c NULL, \p path must contain the full file name.
- \param[in] path path to the directory that contains the preferences file
- \param[in] vendor unique text describing the company or author of this file
- \param[in] application unique text describing the application
- */
- Fl_Preferences::Fl_Preferences( const char *path, const char *vendor, const char *application ) {
- node = new Node( "." );
- rootNode = new RootNode( this, path, vendor, application );
- node->setRoot(rootNode);
- }
- /**
- \brief Generate or read a new group of entries within another group.
- Use the \p group argument to name the group that you would like to access.
- \p Group can also contain a path to a group further down the hierarchy by
- separating group names with a forward slash '/'.
- \param[in] parent reference object for the new group
- \param[in] group name of the group to access (may contain '/'s)
- */
- Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *group ) {
- rootNode = parent.rootNode;
- node = parent.node->addChild( 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 database 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 database
- \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 );
- }
- /**
- \brief Open a child group using a given index.
- Use the \p groupIndex argument to find the group that you would like to access.
- If the given index is invalid (negative or too high), a new group is created
- with a UUID as a name.
- The index needs to be fixed. It is currently backward. Index 0 points
- to the last member in the 'list' of preferences.
- \param[in] parent reference object for the new group
- \param[in] groupIndex zero based index into child groups
- */
- Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, int groupIndex ) {
- rootNode = parent.rootNode;
- if (groupIndex<0 || groupIndex>=parent.groups()) {
- node = parent.node->addChild( newUUID() );
- } else {
- node = parent.node->childNode( groupIndex );
- }
- }
- /**
- \see Fl_Preferences( Fl_Preferences&, int groupIndex )
- */
- Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, int groupIndex ) {
- rootNode = parent->rootNode;
- if (groupIndex<0 || groupIndex>=parent->groups()) {
- node = parent->node->addChild( newUUID() );
- } else {
- node = parent->node->childNode( groupIndex );
- }
- }
- /**
- Create a new dataset access point using a dataset ID.
- ID's are a great way to remember shortcuts to database entries that are deeply
- nested in a preferences database, as long as the database root is not deleted.
- An ID can be retrieved from any Fl_Preferences dataset, and can then be used
- to create multiple new references to the same dataset.
- ID's can be put very helpful when put into the <tt>user_data()</tt> field of
- widget callbacks.
- */
- Fl_Preferences::Fl_Preferences( Fl_Preferences::ID id ) {
- node = (Node*)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
- \em base preferences group, the destructor flushes all
- changes to the preferences file and deletes all internal
- databases.
- The destructor does not remove any data from the database. It merely
- deletes your reference to the database.
- */
- Fl_Preferences::~Fl_Preferences() {
- if (node && !node->parent()) delete rootNode;
- // DO NOT delete nodes! The root node will do that after writing the preferences
- // zero all pointer to avoid memory errors, even though
- // Valgrind does not complain (Cygwind does though)
- node = 0L;
- rootNode = 0L;
- }
- /**
- Returns the number of groups that are contained within a group.
- \return 0 for no groups at all
- */
- int Fl_Preferences::groups() {
- return node->nChildren();
- }
- /**
- Returns the name of the Nth (\p num_group) group.
- There is no guaranteed order of group names. The index must
- be within the range given by groups().
- \param[in] num_group number indexing the requested group
- \return 'C' string pointer to the group name
- */
- const char *Fl_Preferences::group( int num_group ) {
- return node->child( num_group );
- }
- /**
- Returns non-zero if a group with this name exists.
- Group names are relative to the Preferences node and can contain a path.
- "." describes the current node, "./" describes the topmost node.
- By preceding a groupname with a "./", its path becomes relative to the topmost node.
- \param[in] key name of group that is searched for
- \return 0 if no group by that name was found
- */
- char Fl_Preferences::groupExists( const char *key ) {
- return node->search( key ) ? 1 : 0 ;
- }
- /**
- Deletes a group.
- Removes a group and all keys and groups within that group
- from the database.
- \param[in] group name of the group to delete
- \return 0 if call failed
- */
- char Fl_Preferences::deleteGroup( const char *group ) {
- Node *nd = node->search( group );
- if ( nd ) return nd->remove();
- return 0;
- }
- /**
- Delete all groups.
- */
- char Fl_Preferences::deleteAllGroups() {
- node->deleteAllChildren();
- return 1;
- }
- /**
- Returns the number of entries (name/value pairs) in a group.
- \return number of entries
- */
- int Fl_Preferences::entries() {
- return node->nEntry();
- }
- /**
- Returns the name of an entry. There is no guaranteed order of
- entry names. The index must be within the range given by
- entries().
- \param[in] index number indexing the requested entry
- \return pointer to value cstring
- */
- const char *Fl_Preferences::entry( int index ) {
- return node->entry(index).name;
- }
- /**
- Returns non-zero if an entry with this name exists.
- \param[in] key name of entry that is searched for
- \return 0 if entry was not found
- */
- char Fl_Preferences::entryExists( const char *key ) {
- return node->getEntry( key )>=0 ? 1 : 0 ;
- }
- /**
- Deletes a single name/value pair.
- This function removes the entry \p key from the database.
- \param[in] key name of entry to delete
- \return 0 if deleting the entry failed
- */
- char Fl_Preferences::deleteEntry( const char *key ) {
- return node->deleteEntry( key );
- }
- /**
- Delete all entries.
- */
- char Fl_Preferences::deleteAllEntries() {
- node->deleteAllEntries();
- return 1;
- }
- /**
- Delete all groups and all entries.
- */
- char Fl_Preferences::clear() {
- char ret1 = deleteAllGroups();
- char ret2 = deleteAllEntries();
- return ret1 & ret2;
- }
- /**
- Reads an entry from the group. A default value must be
- supplied. The return value indicates if the value was available
- (non-zero) or the default was used (0).
- \param[in] key name of entry
- \param[out] value returned from preferences or default value if none was set
- \param[in] defaultValue default value to be used if no preference was set
- \return 0 if the default value was used
- */
- char Fl_Preferences::get( const char *key, int &value, int defaultValue ) {
- const char *v = node->get( key );
- value = v ? atoi( v ) : defaultValue;
- return ( v != 0 );
- }
- /**
- Sets an entry (name/value pair). The return value indicates if there
- was a problem storing the data in memory. However it does not
- reflect if the value was actually stored in the preferences
- file.
- \param[in] key name of entry
- \param[in] value set this entry to \p value
- \return 0 if setting the value failed
- */
- char Fl_Preferences::set( const char *key, int value ) {
- sprintf( nameBuffer, "%d", value );
- node->set( key, nameBuffer );
- return 1;
- }
- /**
- Reads an entry from the group. A default value must be
- supplied. The return value indicates if the value was available
- (non-zero) or the default was used (0).
- \param[in] key name of entry
- \param[out] value returned from preferences or default value if none was set
- \param[in] defaultValue default value to be used if no preference was set
- \return 0 if the default value was used
- */
- char Fl_Preferences::get( const char *key, float &value, float defaultValue ) {
- const char *v = node->get( key );
- value = v ? (float)atof( v ) : defaultValue;
- return ( v != 0 );
- }
- /**
- Sets an entry (name/value pair). The return value indicates if there
- was a problem storing the data in memory. However it does not
- reflect if the value was actually stored in the preferences
- file.
- \param[in] key name of entry
- \param[in] value set this entry to \p value
- \return 0 if setting the value failed
- */
- char Fl_Preferences::set( const char *key, float value ) {
- sprintf( nameBuffer, "%g", value );
- node->set( key, nameBuffer );
- return 1;
- }
- /**
- Sets an entry (name/value pair). The return value indicates if there
- was a problem storing the data in memory. However it does not
- reflect if the value was actually stored in the preferences
- file.
- \param[in] key name of entry
- \param[in] value set this entry to \p value
- \param[in] precision number of decimal digits to represent value
- \return 0 if setting the value failed
- */
- char Fl_Preferences::set( const char *key, float value, int precision ) {
- sprintf( nameBuffer, "%.*g", precision, value );
- node->set( key, nameBuffer );
- return 1;
- }
- /**
- Reads an entry from the group. A default value must be
- supplied. The return value indicates if the value was available
- (non-zero) or the default was used (0).
- \param[in] key name of entry
- \param[out] value returned from preferences or default value if none was set
- \param[in] defaultValue default value to be used if no preference was set
- \return 0 if the default value was used
- */
- char Fl_Preferences::get( const char *key, double &value, double defaultValue ) {
- const char *v = node->get( key );
- value = v ? atof( v ) : defaultValue;
- return ( v != 0 );
- }
- /**
- Sets an entry (name/value pair). The return value indicates if there
- was a problem storing the data in memory. However it does not
- reflect if the value was actually stored in the preferences
- file.
- \param[in] key name of entry
- \param[in] value set this entry to \p value
- \return 0 if setting the value failed
- */
- char Fl_Preferences::set( const char *key, double value ) {
- sprintf( nameBuffer, "%g", value );
- node->set( key, nameBuffer );
- return 1;
- }
- /**
- Sets an entry (name/value pair). The return value indicates if there
- was a problem storing the data in memory. However it does not
- reflect if the value was actually stored in the preferences
- file.
- \param[in] key name of entry
- \param[in] value set this entry to \p value
- \param[in] precision number of decimal digits to represent value
- \return 0 if setting the value failed
- */
- char Fl_Preferences::set( const char *key, double value, int precision ) {
- sprintf( nameBuffer, "%.*g", precision, value );
- node->set( key, nameBuffer );
- return 1;
- }
- // remove control sequences from a string
- static char *decodeText( const char *src ) {
- int len = 0;
- const char *s = src;
- for ( ; *s; s++, len++ ) {
- if ( *s == '\\' ) {
- if ( isdigit( s[1] ) ) {
- s+=3;
- } else {
- s+=1;
- }
- }
- }
- char *dst = (char*)malloc( len+1 ), *d = dst;
- for ( s = src; *s; s++ ) {
- char c = *s;
- if ( c == '\\' ) {
- if ( s[1] == '\\' ) { *d++ = c; s++; }
- else if ( s[1] == 'n' ) { *d++ = '\n'; s++; }
- else if ( s[1] == 'r' ) { *d++ = '\r'; s++; }
- else if ( isdigit( s[1] ) ) { *d++ = ((s[1]-'0')<<6) + ((s[2]-'0')<<3) + (s[3]-'0'); s+=3; }
- else s++; // error
- }
- else
- *d++ = c;
- }
- *d = 0;
- return dst;
- }
- /**
- Reads an entry from the group. A default value must be
- supplied. The return value indicates if the value was available
- (non-zero) or the default was used (0).
- 'maxSize' is the maximum length of text that will be read.
- The text buffer must allow for one additional byte for a trailling zero.
- \param[in] key name of entry
- \param[out] text returned from preferences or default value if none was set
- \param[in] defaultValue default value to be used if no preference was set
- \param[in] maxSize maximum length of value plus one byte for a trailing zero
- \return 0 if the default value was used
- */
- char Fl_Preferences::get( const char *key, char *text, const char *defaultValue, int maxSize ) {
- const char *v = node->get( key );
- if ( v && strchr( v, '\\' ) ) {
- char *w = decodeText( v );
- strlcpy(text, w, maxSize);
- free( w );
- return 1;
- }
- if ( !v ) v = defaultValue;
- if ( v ) strlcpy(text, v, maxSize);
- else text = 0;
- return ( v != defaultValue );
- }
- /**
- Reads an entry from the group. A default value must be
- supplied. The return value indicates if the value was available
- (non-zero) or the default was used (0). get() allocates memory of
- sufficient size to hold the value. The buffer must be free'd by
- the developer using 'free(value)'.
- \param[in] key name of entry
- \param[out] text returned from preferences or default value if none was set
- \param[in] defaultValue default value to be used if no preference was set
- \return 0 if the default value was used
- */
- char Fl_Preferences::get( const char *key, char *&text, const char *defaultValue ) {
- const char *v = node->get( key );
- if ( v && strchr( v, '\\' ) ) {
- text = decodeText( v );
- return 1;
- }
- if ( !v ) v = defaultValue;
- if ( v )
- text = strdup( v );
- else
- text = 0;
- return ( v != defaultValue );
- }
- /**
- Sets an entry (name/value pair). The return value indicates if there
- was a problem storing the data in memory. However it does not
- reflect if the value was actually stored in the preferences
- file.
- \param[in] key name of entry
- \param[in] text set this entry to \p value
- \return 0 if setting the value failed
- */
- char Fl_Preferences::set( const char *key, const char *text ) {
- const char *s = text ? text : "";
- int n=0, ns=0;
- for ( ; *s; s++ ) { n++; if ( *s<32 || *s=='\\' || *s==0x7f ) ns+=4; }
- if ( ns ) {
- char *buffer = (char*)malloc( n+ns+1 ), *d = buffer;
- for ( s=text; *s; ) {
- char c = *s;
- if ( c=='\\' ) { *d++ = '\\'; *d++ = '\\'; s++; }
- else if ( c=='\n' ) { *d++ = '\\'; *d++ = 'n'; s++; }
- else if ( c=='\r' ) { *d++ = '\\'; *d++ = 'r'; s++; }
- else if ( c<32 || c==0x7f )
- { *d++ = '\\'; *d++ = '0'+((c>>6)&3); *d++ = '0'+((c>>3)&7); *d++ = '0'+(c&7); s++; }
- else *d++ = *s++;
- }
- *d = 0;
- node->set( key, buffer );
- free( buffer );
- }
- else
- node->set( key, text );
- return 1;
- }
- // convert a hex string to binary data
- static void *decodeHex( const char *src, int &size ) {
- size = strlen( src )/2;
- unsigned char *data = (unsigned char*)malloc( size ), *d = data;
- const char *s = src;
- for ( int i=size; i>0; i-- ) {
- int v;
- char x = tolower(*s++);
- if ( x >= 'a' ) v = x-'a'+10; else v = x-'0';
- v = v<<4;
- x = tolower(*s++);
- if ( x >= 'a' ) v += x-'a'+10; else v += x-'0';
- *d++ = (uchar)v;
- }
- return (void*)data;
- }
- /**
- Reads an entry from the group. A default value must be
- supplied. The return value indicates if the value was available
- (non-zero) or the default was used (0).
- 'maxSize' is the maximum length of text that will be read.
- \param[in] key name of entry
- \param[out] data value returned from preferences or default value if none was set
- \param[in] defaultValue default value to be used if no preference was set
- \param[in] defaultSize size of default value array
- \param[in] maxSize maximum length of value
- \return 0 if the default value was used
- \todo maxSize should receive the number of bytes that were read.
- */
- char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int maxSize ) {
- const char *v = node->get( key );
- if ( v ) {
- int dsize;
- void *w = decodeHex( v, dsize );
- memmove( data, w, dsize>maxSize?maxSize:dsize );
- free( w );
- return 1;
- }
- if ( defaultValue )
- memmove( data, defaultValue, defaultSize>maxSize?maxSize:defaultSize );
- return 0;
- }
- /**
- Reads an entry from the group. A default value must be
- supplied. The return value indicates if the value was available
- (non-zero) or the default was used (0). get() allocates memory of
- sufficient size to hold the value. The buffer must be free'd by
- the developer using 'free(value)'.
- \param[in] key name of entry
- \param[out] data returned from preferences or default value if none was set
- \param[in] defaultValue default value to be used if no preference was set
- \param[in] defaultSize size of default value array
- \return 0 if the default value was used
- */
- char Fl_Preferences::get( const char *key, void *&data, const void *defaultValue, int defaultSize ) {
- const char *v = node->get( key );
- if ( v ) {
- int dsize;
- data = decodeHex( v, dsize );
- return 1;
- }
- if ( defaultValue ) {
- data = (void*)malloc( defaultSize );
- memmove( data, defaultValue, defaultSize );
- }
- else
- data = 0;
- return 0;
- }
- /**
- Sets an entry (name/value pair). The return value indicates if there
- was a problem storing the data in memory. However it does not
- reflect if the value was actually stored in the preferences
- file.
- \param[in] key name of entry
- \param[in] data set this entry to \p value
- \param[in] dsize size of data array
- \return 0 if setting the value failed
- */
- char Fl_Preferences::set( const char *key, const void *data, int dsize ) {
- char *buffer = (char*)malloc( dsize*2+1 ), *d = buffer;;
- unsigned char *s = (unsigned char*)data;
- for ( ; dsize>0; dsize-- ) {
- static char lu[] = "0123456789abcdef";
- unsigned char v = *s++;
- *d++ = lu[v>>4];
- *d++ = lu[v&0xf];
- }
- *d = 0;
- node->set( key, buffer );
- free( buffer );
- return 1;
- }
- /**
- Returns the size of the value part of an entry.
- \param[in] key name of entry
- \return size of value
- */
- int Fl_Preferences::size( const char *key ) {
- const char *v = node->get( key );
- return v ? strlen( v ) : 0 ;
- }
- /**
- \brief Creates a path that is related to the preferences file and
- that is usable for additional application data.
- This function creates a directory that is named after the preferences
- database without the \c .prefs extension and located in the same directory.
- It then fills the given buffer with the complete path name.
- Exmaple:
- \code
- Fl_Preferences prefs( USER, "matthiasm.com", "test" );
- char path[FL_PATH_MAX];
- prefs.getUserdataPath( path );
- \endcode
- creates the preferences database in (MS Windows):
- \code
- c:/Documents and Settings/matt/Application Data/matthiasm.com/test.prefs
- \endcode
- and returns the userdata path:
- \code
- c:/Documents and Settings/matt/Application Data/matthiasm.com/test/
- \endcode
- \param[out] path buffer for user data path
- \param[in] pathlen size of path buffer (should be at least \c FL_PATH_MAX)
- \return 0 if path was not created or pathname can't fit into buffer
- */
- char Fl_Preferences::getUserdataPath( char *path, int pathlen ) {
- if ( rootNode )
- return rootNode->getPath( path, pathlen );
- return 0;
- }
- /**
- Writes all preferences to disk. This function works only with
- the base preferences group. This function is rarely used as
- deleting the base preferences flushes automatically.
- */
- void Fl_Preferences::flush() {
- if ( rootNode && node->dirty() )
- rootNode->write();
- }
- //-----------------------------------------------------------------------------
- // helper class to create dynamic group and entry names on the fly
- //
- /**
- Creates a group name or entry name on the fly.
- This version creates a simple unsigned integer as an entry name.
- \code
- int n, i;
- Fl_Preferences prev( appPrefs, "PreviousFiles" );
- prev.get( "n", 0 );
- for ( i=0; i<n; i++ )
- prev.get( Fl_Preferences::Name(i), prevFile[i], "" );
- \endcode
- */
- Fl_Preferences::Name::Name( unsigned int n ) {
- data_ = (char*)malloc(20);
- sprintf(data_, "%u", n);
- }
- /**
- Creates a group name or entry name on the fly.
- This version creates entry names as in 'printf'.
- \code
- int n, i;
- Fl_Preferences prefs( USER, "matthiasm.com", "test" );
- prev.get( "nFiles", 0 );
- for ( i=0; i<n; i++ )
- prev.get( Fl_Preferences::Name( "File%d", i ), prevFile[i], "" );
- \endcode
- */
- Fl_Preferences::Name::Name( const char *format, ... ) {
- data_ = (char*)malloc(1024);
- va_list args;
- va_start(args, format);
- vsnprintf(data_, 1024, format, args);
- va_end(args);
- }
- // delete the name
- Fl_Preferences::Name::~Name() {
- if (data_) {
- free(data_);
- data_ = 0L;
- }
- }
- //-----------------------------------------------------------------------------
- // internal methods, do not modify or use as they will change without notice
- //
- int Fl_Preferences::Node::lastEntrySet = -1;
- // recursively create a path in the file system
- static char makePath( const char *path ) {
- if (access(path, 0)) {
- const char *s = strrchr( path, '/' );
- if ( !s ) return 0;
- int len = s-path;
- char *p = (char*)malloc( len+1 );
- memcpy( p, path, len );
- p[len] = 0;
- makePath( p );
- free( p );
- #if defined(WIN32) && !defined(__CYGWIN__)
- return ( mkdir( path ) == 0 );
- #else
- return ( mkdir( path, 0777 ) == 0 );
- #endif // WIN32 && !__CYGWIN__
- }
- return 1;
- }
- #if 0
- // strip the filename and create a path
- static void makePathForFile( const char *path ) {
- const char *s = strrchr( path, '/' );
- if ( !s ) return;
- int len = s-path;
- char *p = (char*)malloc( len+1 );
- memcpy( p, path, len );
- p[len] = 0;
- makePath( p );
- free( p );
- }
- #endif
- // 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
- # define FLPREFS_RESOURCE "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
- # define FLPREFS_RESOURCEW L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
- int appDataLen = strlen(vendor) + strlen(application) + 8;
- DWORD type, nn;
- LONG err;
- HKEY key;
- switch (root) {
- case SYSTEM:
- err = RegOpenKeyW( HKEY_LOCAL_MACHINE, FLPREFS_RESOURCEW, &key );
- if (err == ERROR_SUCCESS) {
- nn = FL_PATH_MAX - appDataLen;
- err = RegQueryValueExW( key, L"Common AppData", 0L, &type,
- (BYTE*)filename, &nn );
- if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) ) {
- filename[0] = 0;
- filename[1] = 0;
- }
- RegCloseKey(key);
- }
- break;
- case USER:
- err = RegOpenKeyW( HKEY_CURRENT_USER, FLPREFS_RESOURCEW, &key );
- if (err == ERROR_SUCCESS) {
- nn = FL_PATH_MAX - appDataLen;
- err = RegQueryValueExW( key, L"AppData", 0L, &type,
- (BYTE*)filename, &nn );
- if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) ) {
- filename[0] = 0;
- filename[1] = 0;
- }
- RegCloseKey(key);
- }
- break;
- }
- if (!filename[1] && !filename[0]) {
- strcpy(filename, "C:\\FLTK");
- } else {
- #if 0
- xchar *b = (xchar*)_wcsdup((xchar *)filename);
- #else
- // cygwin does not come with _wcsdup. Use malloc + wcscpy.
- // For implementation of wcsdup functionality See
- // - http://linenum.info/p/glibc/2.7/wcsmbs/wcsdup.c
- xchar *b = (xchar*) malloc((wcslen((xchar *) filename) + 1) * sizeof(xchar));
- wcscpy(b, (xchar *) filename);
- #endif
- // filename[fl_unicode2utf(b, wcslen((xchar*)b), filename)] = 0;
- unsigned len = fl_utf8fromwc(filename, (FL_PATH_MAX-1), b, wcslen(b));
- filename[len] = 0;
- free(b);
- }
- snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
- "/%s/%s.prefs", vendor, application);
- for (char *s = filename; *s; s++) if (*s == '\\') *s = '/';
- #elif defined ( __APPLE__ )
- // TODO: verify that this is the Apple sanctioned way of finding these folders
- // (On MSWindows, this frequently leads to issues with internationalized systems)
- // Carbon: err = FindFolder( kLocalDomain, kPreferencesFolderType, 1, &spec.vRefNum, &spec.parID );
- switch (root) {
- case SYSTEM:
- strcpy(filename, "/Library/Preferences");
- break;
- case USER:
- sprintf(filename, "%s/Library/Preferences", fl_getenv("HOME"));
- break;
- }
- snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
- "/%s/%s.prefs", vendor, application );
- #else
- const char *e;
- switch (root) {
- case USER:
- if ((e = fl_getenv("HOME")) != NULL) {
- strlcpy(filename, e, sizeof(filename));
- if (filename[strlen(filename)-1] != '/') {
- strlcat(filename, "/.fltk/", sizeof(filename));
- } else {
- strlcat(filename, ".fltk/", sizeof(filename));
- }
- break;
- }
- case SYSTEM:
- strcpy(filename, "/etc/fltk/");
- break;
- }
- snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
- "%s/%s.prefs", vendor, application);
- #endif
- filename_ = strdup(filename);
- vendor_ = strdup(vendor);
- application_ = strdup(application);
- read();
- }
- // 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";
- if (!application) {
- application = "unknown";
- filename_ = strdup(path);
- } else {
- char filename[ FL_PATH_MAX ]; filename[0] = 0;
- snprintf(filename, sizeof(filename), "%s/%s.prefs", path, application);
- filename_ = strdup(filename);
- }
- 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() {
- if ( prefs_->node->dirty() )
- write();
- if ( filename_ ) {
- free( filename_ );
- filename_ = 0L;
- }
- if ( vendor_ ) {
- free( vendor_ );
- vendor_ = 0L;
- }
- if ( application_ ) {
- free( application_ );
- application_ = 0L;
- }
- delete prefs_->node;
- prefs_->node = 0L;
- }
- // 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 -1;
- if (fgets( buf, 1024, f )==0) { /* ignore */ }
- if (fgets( buf, 1024, f )==0) { /* ignore */ }
- if (fgets( buf, 1024, f )==0) { /* ignore */ }
- Node *nd = prefs_->node;
- for (;;) {
- if ( !fgets( buf, 1024, f ) ) break; // EOF or Error
- if ( buf[0]=='[' ) { // read a new group
- int end = strcspn( buf+1, "]\n\r" );
- buf[ end+1 ] = 0;
- nd = prefs_->node->find( buf+1 );
- } else if ( buf[0]=='+' ) { // value of previous name/value pair spans multiple lines
- int end = strcspn( buf+1, "\n\r" );
- if ( end != 0 ) { // if entry is not empty
- buf[ end+1 ] = 0;
- nd->add( buf+1 );
- }
- } else { // read a name/value pair
- int end = strcspn( buf, "\n\r" );
- if ( end != 0 ) { // if entry is not empty
- buf[ end ] = 0;
- nd->set( buf );
- }
- }
- }
- fclose( f );
- return 0;
- }
- // 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;
- fprintf( f, "; FLTK preferences file format 1.0\n" );
- fprintf( f, "; vendor: %s\n", vendor_ );
- fprintf( f, "; application: %s\n", application_ );
- prefs_->node->write( f );
- fclose( f );
- #if !(defined(__APPLE__) || defined(WIN32))
- // unix: make sure that system prefs are user-readable
- if (strncmp(filename_, "/etc/fltk/", 10) == 0) {
- char *p;
- p = filename_ + 9;
- do { // for each directory to the pref file
- *p = 0;
- fl_chmod(filename_, 0755); // rwxr-xr-x
- *p = '/';
- p = strchr(p+1, '/');
- } while (p);
- fl_chmod(filename_, 0644); // rw-r--r--
- }
- #endif
- return 0;
- }
- // 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;
- for ( s = path; *s; s++ ) if ( *s == '\\' ) *s = '/';
- s = strrchr( path, '.' );
- if ( !s ) return 0;
- *s = 0;
- char ret = fl_make_path( path );
- #if !(defined(__APPLE__) || defined(WIN32))
- // unix: make sure that system prefs dir. is user-readable
- if (strncmp(path, "/etc/fltk/", 10) == 0) {
- fl_chmod(path, 0755); // rwxr-xr-x
- }
- #endif
- strcpy( s, "/" );
- return ret;
- }
- // create a node that represents a group
- // - path must be a single word, prferable alnum(), dot and underscore only. Space is ok.
- Fl_Preferences::Node::Node( const char *path ) {
- if ( path ) path_ = strdup( path ); else path_ = 0;
- child_ = 0; next_ = 0; parent_ = 0;
- entry_ = 0;
- nEntry_ = NEntry_ = 0;
- dirty_ = 0;
- top_ = 0;
- indexed_ = 0;
- index_ = 0;
- nIndex_ = NIndex_ = 0;
- }
- void Fl_Preferences::Node::deleteAllChildren() {
- Node *nx;
- for ( Node *nd = child_; nd; nd = nx ) {
- nx = nd->next_;
- delete nd;
- }
- child_ = 0L;
- dirty_ = 1;
- updateIndex();
- }
- void Fl_Preferences::Node::deleteAllEntries() {
- if ( entry_ ) {
- for ( int i = 0; i < nEntry_; i++ ) {
- if ( entry_[i].name ) {
- free( entry_[i].name );
- entry_[i].name = 0L;
- }
- if ( entry_[i].value ) {
- free( entry_[i].value );
- entry_[i].value = 0L;
- }
- }
- free( entry_ );
- entry_ = 0L;
- nEntry_ = 0;
- NEntry_ = 0;
- }
- dirty_ = 1;
- }
- // delete this and all depending nodes
- Fl_Preferences::Node::~Node() {
- deleteAllChildren();
- deleteAllEntries();
- deleteIndex();
- if ( path_ ) {
- free( path_ );
- path_ = 0L;
- }
- next_ = 0L;
- parent_ = 0L;
- }
- // recursively check if any entry is dirty (was changed after loading a fresh prefs file)
- char Fl_Preferences::Node::dirty() {
- if ( dirty_ ) return 1;
- if ( next_ && next_->dirty() ) return 1;
- if ( child_ && child_->dirty() ) return 1;
- return 0;
- }
- // write this node (recursively from the last neighbor back to this)
- // write all entries
- // write all children
- int Fl_Preferences::Node::write( FILE *f ) {
- if ( next_ ) next_->write( f );
- fprintf( f, "\n[%s]\n\n", path_ );
- for ( int i = 0; i < nEntry_; i++ ) {
- char *src = entry_[i].value;
- if ( src ) { // hack it into smaller pieces if needed
- fprintf( f, "%s:", entry_[i].name );
- int cnt, written;
- for ( cnt = 0; cnt < 60; cnt++ )
- if ( src[cnt]==0 ) break;
- written = fwrite( src, cnt, 1, f );
- fprintf( f, "\n" );
- src += cnt;
- for (;*src;) {
- for ( cnt = 0; cnt < 80; cnt++ )
- if ( src[cnt]==0 ) break;
- fputc( '+', f );
- written = fwrite( src, cnt, 1, f );
- fputc( '\n', f );
- src += cnt;
- }
- }
- else
- fprintf( f, "%s\n", entry_[i].name );
- }
- if ( child_ ) child_->write( f );
- dirty_ = 0;
- return 0;
- }
- // set the parent node and create the full path
- void Fl_Preferences::Node::setParent( Node *pn ) {
- parent_ = pn;
- next_ = pn->child_;
- pn->child_ = this;
- sprintf( nameBuffer, "%s/%s", pn->path_, path_ );
- free( path_ );
- path_ = strdup( nameBuffer );
- }
- // find the corresponding root node
- Fl_Preferences::RootNode *Fl_Preferences::Node::findRoot() {
- Node *n = this;
- do {
- if (n->top_)
- return n->root_;
- n = n->parent();
- } while (n);
- return 0L;
- }
- // add a child to this node and set its path (try to find it first...)
- Fl_Preferences::Node *Fl_Preferences::Node::addChild( const char *path ) {
- sprintf( nameBuffer, "%s/%s", path_, path );
- char *name = strdup( nameBuffer );
- Node *nd = find( name );
- free( name );
- dirty_ = 1;
- updateIndex();
- return nd;
- }
- // create and set, or change an entry within this node
- void Fl_Preferences::Node::set( const char *name, const char *value )
- {
- for ( int i=0; i<nEntry_; i++ ) {
- if ( strcmp( name, entry_[i].name ) == 0 ) {
- if ( !value ) return; // annotation
- if ( strcmp( value, entry_[i].value ) != 0 ) {
- if ( entry_[i].value )
- free( entry_[i].value );
- entry_[i].value = strdup( value );
- dirty_ = 1;
- }
- lastEntrySet = i;
- return;
- }
- }
- if ( NEntry_==nEntry_ ) {
- NEntry_ = NEntry_ ? NEntry_*2 : 10;
- entry_ = (Entry*)realloc( entry_, NEntry_ * sizeof(Entry) );
- }
- entry_[ nEntry_ ].name = strdup( name );
- entry_[ nEntry_ ].value = value?strdup( value ):0;
- lastEntrySet = nEntry_;
- nEntry_++;
- dirty_ = 1;
- }
- // create or set a value (or annotation) from a single line in the file buffer
- void Fl_Preferences::Node::set( const char *line ) {
- // hmm. If we assume that we always read this file in the beginning,
- // we can handle the dirty flag 'quick and dirty'
- char dirt = dirty_;
- if ( line[0]==';' || line[0]==0 || line[0]=='#' ) {
- set( line, 0 );
- } else {
- const char *c = strchr( line, ':' );
- if ( c ) {
- unsigned int len = c-line+1;
- if ( len >= sizeof( nameBuffer ) )
- len = sizeof( nameBuffer );
- strlcpy( nameBuffer, line, len );
- set( nameBuffer, c+1 );
- } else {
- set( line, "" );
- }
- }
- dirty_ = dirt;
- }
- // add more data to an existing entry
- void Fl_Preferences::Node::add( const char *line ) {
- if ( lastEntrySet<0 || lastEntrySet>=nEntry_ ) return;
- char *&dst = entry_[ lastEntrySet ].value;
- int a = strlen( dst );
- int b = strlen( line );
- dst = (char*)realloc( dst, a+b+1 );
- memcpy( dst+a, line, b+1 );
- dirty_ = 1;
- }
- // get the value for a name, returns 0 if no such name
- const char *Fl_Preferences::Node::get( const char *name ) {
- int i = getEntry( name );
- return i>=0 ? entry_[i].value : 0 ;
- }
- // find the index of an entry, returns -1 if no such entry
- int Fl_Preferences::Node::getEntry( const char *name ) {
- for ( int i=0; i<nEntry_; i++ ) {
- if ( strcmp( name, entry_[i].name ) == 0 ) {
- return i;
- }
- }
- return -1;
- }
- // remove one entry form this group
- char Fl_Preferences::Node::deleteEntry( const char *name ) {
- int ix = getEntry( name );
- if ( ix == -1 ) return 0;
- memmove( entry_+ix, entry_+ix+1, (nEntry_-ix-1) * sizeof(Entry) );
- nEntry_--;
- dirty_ = 1;
- return 1;
- }
- // find a group somewhere in the tree starting here
- // - this method will always return a valid node (except for memory allocation problems)
- // - if the node was not found, 'find' will create the required branch
- Fl_Preferences::Node *Fl_Preferences::Node::find( const char *path ) {
- int len = strlen( path_ );
- if ( strncmp( path, path_, len ) == 0 ) {
- if ( path[ len ] == 0 )
- return this;
- if ( path[ len ] == '/' ) {
- Node *nd;
- for ( nd = child_; nd; nd = nd->next_ ) {
- Node *nn = nd->find( path );
- if ( nn ) return nn;
- }
- const char *s = path+len+1;
- const char *e = strchr( s, '/' );
- if (e) strlcpy( nameBuffer, s, e-s+1 );
- else strlcpy( nameBuffer, s, sizeof(nameBuffer));
- nd = new Node( nameBuffer );
- nd->setParent( this );
- return nd->find( path );
- }
- }
- return 0;
- }
- // find a group somewhere in the tree starting here
- // caller must not set 'offset' argument
- // - if the node does not exist, 'search' returns NULL
- // - if the pathname is "." (current node) return this node
- // - if the pathname is "./" (root node) return the topmost node
- // - if the pathname starts with "./", start the search at the root node instead
- Fl_Preferences::Node *Fl_Preferences::Node::search( const char *path, int offset ) {
- if ( offset == 0 ) {
- if ( path[0] == '.' ) {
- if ( path[1] == 0 ) {
- return this; // user was searching for current node
- } else if ( path[1] == '/' ) {
- Node *nn = this;
- while ( nn->parent() ) nn = nn->parent();
- if ( path[2]==0 ) { // user is searching for root ( "./" )
- return nn;
- }
- return nn->search( path+2, 2 ); // do a relative search on the root node
- }
- }
- offset = strlen( path_ ) + 1;
- }
- int len = strlen( path_ );
- if ( len < offset-1 ) return 0;
- len -= offset;
- if ( ( len <= 0 ) || ( strncmp( path, path_+offset, len ) == 0 ) ) {
- if ( len > 0 && path[ len ] == 0 )
- return this;
- if ( len <= 0 || path[ len ] == '/' ) {
- for ( Node *nd = child_; nd; nd = nd->next_ ) {
- Node *nn = nd->search( path, offset );
- if ( nn ) return nn;
- }
- return 0;
- }
- }
- return 0;
- }
- // return the number of child nodes (groups)
- int Fl_Preferences::Node::nChildren() {
- if (indexed_) {
- return nIndex_;
- } else {
- int cnt = 0;
- for ( Node *nd = child_; nd; nd = nd->next_ )
- cnt++;
- return cnt;
- }
- }
- // return the node name
- const char *Fl_Preferences::Node::name() {
- if ( path_ ) {
- char *r = strrchr( path_, '/' );
- return r ? r+1 : path_ ;
- } else {
- return 0L ;
- }
- }
- // return the n'th child node's name
- const char *Fl_Preferences::Node::child( int ix ) {
- Node *nd = childNode( ix );
- if ( nd )
- return nd->name();
- else
- return 0L ;
- }
- // return the n'th child node
- Fl_Preferences::Node *Fl_Preferences::Node::childNode( int ix ) {
- createIndex();
- if (indexed_) {
- // usually faster access in correct order, but needing more memory
- return index_[ix];
- } else {
- // slow access and reverse order
- int n = nChildren();
- ix = n - ix -1;
- Node *nd;
- for ( nd = child_; nd; nd = nd->next_ ) {
- if ( !ix-- ) break;
- if ( !nd ) break;
- }
- return nd;
- }
- }
- // remove myself from the list and delete me (and all children)
- char Fl_Preferences::Node::remove() {
- Node *nd = 0, *np;
- if ( parent() ) {
- nd = parent()->child_; np = 0L;
- for ( ; nd; np = nd, nd = nd->next_ ) {
- if ( nd == this ) {
- if ( np )
- np->next_ = nd->next_;
- else
- parent()->child_ = nd->next_;
- break;
- }
- }
- parent()->dirty_ = 1;
- parent()->updateIndex();
- }
- delete this;
- return ( nd != 0 );
- }
- void Fl_Preferences::Node::createIndex() {
- if (indexed_) return;
- int n = nChildren();
- if (n>NIndex_) {
- NIndex_ = n + 16;
- index_ = (Node**)realloc(index_, NIndex_*sizeof(Node**));
- }
- Node *nd;
- int i = 0;
- for (nd = child_; nd; nd = nd->next_, i++) {
- index_[n-i-1] = nd;
- }
- nIndex_ = n;
- indexed_ = 1;
- }
- void Fl_Preferences::Node::updateIndex() {
- indexed_ = 0;
- }
- void Fl_Preferences::Node::deleteIndex() {
- if (index_) free(index_);
- NIndex_ = nIndex_ = 0;
- index_ = 0;
- indexed_ = 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) {
- #ifdef FL_PLUGIN_VERBOSE
- printf("Fl_Plugin: creating a plugin, class \"%s\", name \"%s\"\n",
- klass, name);
- #endif
- Fl_Plugin_Manager pm(klass);
- id = pm.addPlugin(name, this);
- }
- /**
- * \brief Clear the plugin and remove it from the database.
- */
- Fl_Plugin::~Fl_Plugin() {
- #ifdef FL_PLUGIN_VERBOSE
- printf("Fl_Plugin: deleting a plugin\n");
- #endif
- 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)) {
- #ifdef FL_PLUGIN_VERBOSE
- printf("Fl_Plugin: creating a plugin manager for class \"%s\"\n", klass);
- #endif
- }
- /**
- * \brief Remove th…
Large files files are truncated, but you can click here to view the full file