themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_Preferences.cxx
blobe343821bae9b343b4bf2670f99eaae2c1eb114df
1 //
2 // "$Id: Fl_Preferences.cxx 8291 2011-01-19 06:33:48Z manolo $"
3 //
4 // Preferences methods for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 2002-2010 by Matthias Melcher.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <FL/Fl.H>
29 #include <FL/Fl_Preferences.H>
30 #include <FL/Fl_Plugin.H>
31 #include <FL/filename.H>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <stdarg.h>
36 #include <FL/fl_utf8.h>
37 #include "flstring.h"
38 #include <sys/stat.h>
39 #include <time.h>
41 #if defined(WIN32) && !defined(__CYGWIN__)
42 # include <windows.h>
43 # include <direct.h>
44 # include <io.h>
45 // Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
46 // on Windows, which is supposed to be POSIX compliant...
47 # define access _access
48 # define mkdir _mkdir
49 #elif defined (__APPLE__)
50 # include <ApplicationServices/ApplicationServices.h>
51 # include <unistd.h>
52 # include <dlfcn.h>
53 #else
54 # include <unistd.h>
55 # include <dlfcn.h>
56 #endif
58 #ifdef WIN32
59 # include <windows.h>
60 # include <rpc.h>
61 // function pointer for the UuidCreate Function
62 // RPC_STATUS RPC_ENTRY UuidCreate(UUID __RPC_FAR *Uuid);
63 typedef RPC_STATUS (WINAPI* uuid_func)(UUID __RPC_FAR *Uuid);
64 #else
65 # include <sys/time.h>
66 #endif // WIN32
68 #ifdef __CYGWIN__
69 # include <wchar.h>
70 #endif
72 char Fl_Preferences::nameBuffer[128];
73 char Fl_Preferences::uuidBuffer[40];
74 Fl_Preferences *Fl_Preferences::runtimePrefs = 0;
76 /**
77 * Returns a UUID as generated by the system.
79 * A UUID is a "universally unique identifier" which is commonly used in
80 * configuration files to create identities. A UUID in ASCII looks like this:
81 * <tt>937C4900-51AA-4C11-8DD3-7AB59944F03E</tt>. It has always 36 bytes plus
82 * a trailing zero.
84 * \return a pointer to a static buffer containing the new UUID in ASCII format.
85 * The buffer is overwritten during every call to this function!
87 const char *Fl_Preferences::newUUID() {
88 #ifdef __APPLE__
89 CFUUIDRef theUUID = CFUUIDCreate(NULL);
90 CFUUIDBytes b = CFUUIDGetUUIDBytes(theUUID);
91 sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
92 b.byte0, b.byte1, b.byte2, b.byte3, b.byte4, b.byte5, b.byte6, b.byte7,
93 b.byte8, b.byte9, b.byte10, b.byte11, b.byte12, b.byte13, b.byte14, b.byte15);
94 CFRelease(theUUID);
95 #elif defined (WIN32)
96 // First try and use the win API function UuidCreate(), but if that is not
97 // available, fall back to making something up from scratch.
98 // We do not want to link against the Rpcrt4.dll, as we will rarely use it,
99 // so we load the DLL dynamically, if it is available, and work from there.
100 static HMODULE hMod = NULL;
101 UUID ud;
102 UUID *pu = &ud;
103 int got_uuid = 0;
105 if (!hMod) { // first time in?
106 hMod = LoadLibrary("Rpcrt4.dll");
109 if (hMod) { // do we have a usable handle to Rpcrt4.dll?
110 uuid_func uuid_crt = (uuid_func)GetProcAddress(hMod, "UuidCreate");
111 if (uuid_crt != NULL) {
112 RPC_STATUS rpc_res = uuid_crt(pu);
113 if ( // is the return status OK for our needs?
114 (rpc_res == RPC_S_OK) || // all is well
115 (rpc_res == RPC_S_UUID_LOCAL_ONLY) || // only unique to this machine
116 (rpc_res == RPC_S_UUID_NO_ADDRESS) // probably only locally unique
118 got_uuid = -1;
119 sprintf(uuidBuffer, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
120 pu->Data1, pu->Data2, pu->Data3, pu->Data4[0], pu->Data4[1],
121 pu->Data4[2], pu->Data4[3], pu->Data4[4],
122 pu->Data4[5], pu->Data4[6], pu->Data4[7]);
126 if (got_uuid == 0) { // did not make a UUID - use fallback logic
127 unsigned char b[16];
128 time_t t = time(0); // first 4 byte
129 b[0] = (unsigned char)t;
130 b[1] = (unsigned char)(t>>8);
131 b[2] = (unsigned char)(t>>16);
132 b[3] = (unsigned char)(t>>24);
133 int r = rand(); // four more bytes
134 b[4] = (unsigned char)r;
135 b[5] = (unsigned char)(r>>8);
136 b[6] = (unsigned char)(r>>16);
137 b[7] = (unsigned char)(r>>24);
138 // Now we try to find 4 more "random" bytes. We extract the
139 // lower 4 bytes from the address of t - it is created on the
140 // stack so *might* be in a different place each time...
141 // This is now done via a union to make it compile OK on 64-bit systems.
142 union { void *pv; unsigned char a[sizeof(void*)]; } v;
143 v.pv = (void *)(&t);
144 // NOTE: This assume that all WinXX systems are little-endian
145 b[8] = v.a[0];
146 b[9] = v.a[1];
147 b[10] = v.a[2];
148 b[11] = v.a[3];
149 TCHAR name[MAX_COMPUTERNAME_LENGTH + 1]; // only used to make last four bytes
150 DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
151 // GetComputerName() does not depend on any extra libs, and returns something
152 // analogous to gethostname()
153 GetComputerName(name, &nSize);
154 // use the first 4 TCHAR's of the name to create the last 4 bytes of our UUID
155 for (int ii = 0; ii < 4; ii++) {
156 b[12 + ii] = (unsigned char)name[ii];
158 sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
159 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
160 b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
162 #else
163 // warning Unix implementation of Fl_Preferences::newUUID() incomplete!
164 // #include <uuid/uuid.h>
165 // void uuid_generate(uuid_t out);
166 unsigned char b[16];
167 time_t t = time(0); // first 4 byte
168 b[0] = (unsigned char)t;
169 b[1] = (unsigned char)(t>>8);
170 b[2] = (unsigned char)(t>>16);
171 b[3] = (unsigned char)(t>>24);
172 int r = rand(); // four more bytes
173 b[4] = (unsigned char)r;
174 b[5] = (unsigned char)(r>>8);
175 b[6] = (unsigned char)(r>>16);
176 b[7] = (unsigned char)(r>>24);
177 unsigned long a = (unsigned long)&t; // four more bytes
178 b[8] = (unsigned char)a;
179 b[9] = (unsigned char)(a>>8);
180 b[10] = (unsigned char)(a>>16);
181 b[11] = (unsigned char)(a>>24);
182 // Now we try to find 4 more "random" bytes. We extract the
183 // lower 4 bytes from the address of t - it is created on the
184 // stack so *might* be in a different place each time...
185 // This is now done via a union to make it compile OK on 64-bit systems.
186 union { void *pv; unsigned char a[sizeof(void*)]; } v;
187 v.pv = (void *)(&t);
188 // NOTE: May need to handle big- or little-endian systems here
189 # if WORDS_BIGENDIAN
190 b[8] = v.a[sizeof(void*) - 1];
191 b[9] = v.a[sizeof(void*) - 2];
192 b[10] = v.a[sizeof(void*) - 3];
193 b[11] = v.a[sizeof(void*) - 4];
194 # else /* data ordered for a little-endian system */
195 b[8] = v.a[0];
196 b[9] = v.a[1];
197 b[10] = v.a[2];
198 b[11] = v.a[3];
199 # endif
200 char name[80]; // last four bytes
201 gethostname(name, 79);
202 memcpy(b+12, name, 4);
203 sprintf(uuidBuffer, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
204 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
205 b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
206 #endif
208 return uuidBuffer;
212 The constructor creates a group that manages name/value pairs and
213 child groups. Groups are ready for reading and writing at any time.
214 The root argument is either Fl_Preferences::USER
215 or Fl_Preferences::SYSTEM.
217 This constructor creates the <i>base</i> instance for all
218 following entries and reads existing databases into memory. The
219 vendor argument is a unique text string identifying the
220 development team or vendor of an application. A domain name or
221 an EMail address are great unique names, e.g.
222 "researchATmatthiasm.com" or "fltk.org". The
223 application argument can be the working title or final
224 name of your application. Both vendor and
225 application must be valid relative UNIX pathnames and
226 may contain '/'s to create deeper file structures.
228 A set of Preferences marked "run-time" exists exactly one per application and
229 only as long as the application runs. It can be used as a database for
230 volatile information. FLTK uses it to register plugins at run-time.
232 \param[in] root can be \c USER or \c SYSTEM for user specific or system wide
233 preferences
234 \param[in] vendor unique text describing the company or author of this file
235 \param[in] application unique text describing the application
237 Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application ) {
238 node = new Node( "." );
239 rootNode = new RootNode( this, root, vendor, application );
240 node->setRoot(rootNode);
244 \brief Use this constructor to create or read a preferences file at an
245 arbitrary position in the file system.
247 The file name is generated in the form
248 <tt><i>path</i>/<i>application</i>.prefs</tt>. If \p application
249 is \c NULL, \p path must contain the full file name.
251 \param[in] path path to the directory that contains the preferences file
252 \param[in] vendor unique text describing the company or author of this file
253 \param[in] application unique text describing the application
255 Fl_Preferences::Fl_Preferences( const char *path, const char *vendor, const char *application ) {
256 node = new Node( "." );
257 rootNode = new RootNode( this, path, vendor, application );
258 node->setRoot(rootNode);
262 \brief Generate or read a new group of entries within another group.
264 Use the \p group argument to name the group that you would like to access.
265 \p Group can also contain a path to a group further down the hierarchy by
266 separating group names with a forward slash '/'.
268 \param[in] parent reference object for the new group
269 \param[in] group name of the group to access (may contain '/'s)
271 Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *group ) {
272 rootNode = parent.rootNode;
273 node = parent.node->addChild( group );
277 \brief Create or access a group of preferences using a name.
278 \param[in] parent the parameter parent is a pointer to the parent group.
279 \p Parent may be \p NULL. It then refers to an application internal
280 database which exists only once, and remains in RAM only until the
281 application quits. This database is used to manage plugins and other
282 data indexes by strings.
283 \param[in] group a group name that is used as a key into the database
284 \see Fl_Preferences( Fl_Preferences&, const char *group )
286 Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group ) {
287 if (parent==0) {
288 if (!runtimePrefs) {
289 runtimePrefs = new Fl_Preferences();
290 runtimePrefs->node = new Node( "." );
291 runtimePrefs->rootNode = new RootNode( runtimePrefs );
292 runtimePrefs->node->setRoot(rootNode);
294 parent = runtimePrefs;
296 rootNode = parent->rootNode;
297 node = parent->node->addChild( group );
301 \brief Open a child group using a given index.
303 Use the \p groupIndex argument to find the group that you would like to access.
304 If the given index is invalid (negative or too high), a new group is created
305 with a UUID as a name.
307 The index needs to be fixed. It is currently backward. Index 0 points
308 to the last member in the 'list' of preferences.
310 \param[in] parent reference object for the new group
311 \param[in] groupIndex zero based index into child groups
313 Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, int groupIndex ) {
314 rootNode = parent.rootNode;
315 if (groupIndex<0 || groupIndex>=parent.groups()) {
316 node = parent.node->addChild( newUUID() );
317 } else {
318 node = parent.node->childNode( groupIndex );
323 \see Fl_Preferences( Fl_Preferences&, int groupIndex )
325 Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, int groupIndex ) {
326 rootNode = parent->rootNode;
327 if (groupIndex<0 || groupIndex>=parent->groups()) {
328 node = parent->node->addChild( newUUID() );
329 } else {
330 node = parent->node->childNode( groupIndex );
335 Create a new dataset access point using a dataset ID.
337 ID's are a great way to remember shortcuts to database entries that are deeply
338 nested in a preferences database, as long as the database root is not deleted.
339 An ID can be retrieved from any Fl_Preferences dataset, and can then be used
340 to create multiple new references to the same dataset.
342 ID's can be put very helpful when put into the <tt>user_data()</tt> field of
343 widget callbacks.
345 Fl_Preferences::Fl_Preferences( Fl_Preferences::ID id ) {
346 node = (Node*)id;
347 rootNode = node->findRoot();
351 Create another reference to a Preferences group.
353 Fl_Preferences::Fl_Preferences(const Fl_Preferences &rhs)
354 : node(rhs.node),
355 rootNode(rhs.rootNode)
359 Assign another reference to a Preference group.
361 Fl_Preferences &Fl_Preferences::operator=(const Fl_Preferences &rhs) {
362 if (&rhs != this) {
363 node = rhs.node;
364 rootNode = rhs.rootNode;
366 return *this;
370 The destructor removes allocated resources. When used on the
371 \em base preferences group, the destructor flushes all
372 changes to the preferences file and deletes all internal
373 databases.
375 The destructor does not remove any data from the database. It merely
376 deletes your reference to the database.
378 Fl_Preferences::~Fl_Preferences() {
379 if (node && !node->parent()) delete rootNode;
380 // DO NOT delete nodes! The root node will do that after writing the preferences
381 // zero all pointer to avoid memory errors, even though
382 // Valgrind does not complain (Cygwind does though)
383 node = 0L;
384 rootNode = 0L;
388 Returns the number of groups that are contained within a group.
390 \return 0 for no groups at all
392 int Fl_Preferences::groups() {
393 return node->nChildren();
397 Returns the name of the Nth (\p num_group) group.
398 There is no guaranteed order of group names. The index must
399 be within the range given by groups().
401 \param[in] num_group number indexing the requested group
402 \return 'C' string pointer to the group name
404 const char *Fl_Preferences::group( int num_group ) {
405 return node->child( num_group );
409 Returns non-zero if a group with this name exists.
410 Group names are relative to the Preferences node and can contain a path.
411 "." describes the current node, "./" describes the topmost node.
412 By preceding a groupname with a "./", its path becomes relative to the topmost node.
414 \param[in] key name of group that is searched for
415 \return 0 if no group by that name was found
417 char Fl_Preferences::groupExists( const char *key ) {
418 return node->search( key ) ? 1 : 0 ;
422 Deletes a group.
424 Removes a group and all keys and groups within that group
425 from the database.
427 \param[in] group name of the group to delete
428 \return 0 if call failed
430 char Fl_Preferences::deleteGroup( const char *group ) {
431 Node *nd = node->search( group );
432 if ( nd ) return nd->remove();
433 return 0;
437 Delete all groups.
439 char Fl_Preferences::deleteAllGroups() {
440 node->deleteAllChildren();
441 return 1;
445 Returns the number of entries (name/value pairs) in a group.
447 \return number of entries
449 int Fl_Preferences::entries() {
450 return node->nEntry();
454 Returns the name of an entry. There is no guaranteed order of
455 entry names. The index must be within the range given by
456 entries().
458 \param[in] index number indexing the requested entry
459 \return pointer to value cstring
461 const char *Fl_Preferences::entry( int index ) {
462 return node->entry(index).name;
466 Returns non-zero if an entry with this name exists.
468 \param[in] key name of entry that is searched for
469 \return 0 if entry was not found
471 char Fl_Preferences::entryExists( const char *key ) {
472 return node->getEntry( key )>=0 ? 1 : 0 ;
476 Deletes a single name/value pair.
478 This function removes the entry \p key from the database.
480 \param[in] key name of entry to delete
481 \return 0 if deleting the entry failed
483 char Fl_Preferences::deleteEntry( const char *key ) {
484 return node->deleteEntry( key );
488 Delete all entries.
490 char Fl_Preferences::deleteAllEntries() {
491 node->deleteAllEntries();
492 return 1;
496 Delete all groups and all entries.
498 char Fl_Preferences::clear() {
499 char ret1 = deleteAllGroups();
500 char ret2 = deleteAllEntries();
501 return ret1 & ret2;
505 Reads an entry from the group. A default value must be
506 supplied. The return value indicates if the value was available
507 (non-zero) or the default was used (0).
509 \param[in] key name of entry
510 \param[out] value returned from preferences or default value if none was set
511 \param[in] defaultValue default value to be used if no preference was set
512 \return 0 if the default value was used
514 char Fl_Preferences::get( const char *key, int &value, int defaultValue ) {
515 const char *v = node->get( key );
516 value = v ? atoi( v ) : defaultValue;
517 return ( v != 0 );
521 Sets an entry (name/value pair). The return value indicates if there
522 was a problem storing the data in memory. However it does not
523 reflect if the value was actually stored in the preferences
524 file.
526 \param[in] key name of entry
527 \param[in] value set this entry to \p value
528 \return 0 if setting the value failed
530 char Fl_Preferences::set( const char *key, int value ) {
531 sprintf( nameBuffer, "%d", value );
532 node->set( key, nameBuffer );
533 return 1;
537 Reads an entry from the group. A default value must be
538 supplied. The return value indicates if the value was available
539 (non-zero) or the default was used (0).
541 \param[in] key name of entry
542 \param[out] value returned from preferences or default value if none was set
543 \param[in] defaultValue default value to be used if no preference was set
544 \return 0 if the default value was used
546 char Fl_Preferences::get( const char *key, float &value, float defaultValue ) {
547 const char *v = node->get( key );
548 value = v ? (float)atof( v ) : defaultValue;
549 return ( v != 0 );
553 Sets an entry (name/value pair). The return value indicates if there
554 was a problem storing the data in memory. However it does not
555 reflect if the value was actually stored in the preferences
556 file.
558 \param[in] key name of entry
559 \param[in] value set this entry to \p value
560 \return 0 if setting the value failed
562 char Fl_Preferences::set( const char *key, float value ) {
563 sprintf( nameBuffer, "%g", value );
564 node->set( key, nameBuffer );
565 return 1;
569 Sets an entry (name/value pair). The return value indicates if there
570 was a problem storing the data in memory. However it does not
571 reflect if the value was actually stored in the preferences
572 file.
574 \param[in] key name of entry
575 \param[in] value set this entry to \p value
576 \param[in] precision number of decimal digits to represent value
577 \return 0 if setting the value failed
579 char Fl_Preferences::set( const char *key, float value, int precision ) {
580 sprintf( nameBuffer, "%.*g", precision, value );
581 node->set( key, nameBuffer );
582 return 1;
586 Reads an entry from the group. A default value must be
587 supplied. The return value indicates if the value was available
588 (non-zero) or the default was used (0).
590 \param[in] key name of entry
591 \param[out] value returned from preferences or default value if none was set
592 \param[in] defaultValue default value to be used if no preference was set
593 \return 0 if the default value was used
595 char Fl_Preferences::get( const char *key, double &value, double defaultValue ) {
596 const char *v = node->get( key );
597 value = v ? atof( v ) : defaultValue;
598 return ( v != 0 );
602 Sets an entry (name/value pair). The return value indicates if there
603 was a problem storing the data in memory. However it does not
604 reflect if the value was actually stored in the preferences
605 file.
607 \param[in] key name of entry
608 \param[in] value set this entry to \p value
609 \return 0 if setting the value failed
611 char Fl_Preferences::set( const char *key, double value ) {
612 sprintf( nameBuffer, "%g", value );
613 node->set( key, nameBuffer );
614 return 1;
618 Sets an entry (name/value pair). The return value indicates if there
619 was a problem storing the data in memory. However it does not
620 reflect if the value was actually stored in the preferences
621 file.
623 \param[in] key name of entry
624 \param[in] value set this entry to \p value
625 \param[in] precision number of decimal digits to represent value
626 \return 0 if setting the value failed
628 char Fl_Preferences::set( const char *key, double value, int precision ) {
629 sprintf( nameBuffer, "%.*g", precision, value );
630 node->set( key, nameBuffer );
631 return 1;
634 // remove control sequences from a string
635 static char *decodeText( const char *src ) {
636 int len = 0;
637 const char *s = src;
638 for ( ; *s; s++, len++ ) {
639 if ( *s == '\\' ) {
640 if ( isdigit( s[1] ) ) {
641 s+=3;
642 } else {
643 s+=1;
647 char *dst = (char*)malloc( len+1 ), *d = dst;
648 for ( s = src; *s; s++ ) {
649 char c = *s;
650 if ( c == '\\' ) {
651 if ( s[1] == '\\' ) { *d++ = c; s++; }
652 else if ( s[1] == 'n' ) { *d++ = '\n'; s++; }
653 else if ( s[1] == 'r' ) { *d++ = '\r'; s++; }
654 else if ( isdigit( s[1] ) ) { *d++ = ((s[1]-'0')<<6) + ((s[2]-'0')<<3) + (s[3]-'0'); s+=3; }
655 else s++; // error
657 else
658 *d++ = c;
660 *d = 0;
661 return dst;
665 Reads an entry from the group. A default value must be
666 supplied. The return value indicates if the value was available
667 (non-zero) or the default was used (0).
668 'maxSize' is the maximum length of text that will be read.
669 The text buffer must allow for one additional byte for a trailling zero.
671 \param[in] key name of entry
672 \param[out] text returned from preferences or default value if none was set
673 \param[in] defaultValue default value to be used if no preference was set
674 \param[in] maxSize maximum length of value plus one byte for a trailing zero
675 \return 0 if the default value was used
677 char Fl_Preferences::get( const char *key, char *text, const char *defaultValue, int maxSize ) {
678 const char *v = node->get( key );
679 if ( v && strchr( v, '\\' ) ) {
680 char *w = decodeText( v );
681 strlcpy(text, w, maxSize);
682 free( w );
683 return 1;
685 if ( !v ) v = defaultValue;
686 if ( v ) strlcpy(text, v, maxSize);
687 else text = 0;
688 return ( v != defaultValue );
692 Reads an entry from the group. A default value must be
693 supplied. The return value indicates if the value was available
694 (non-zero) or the default was used (0). get() allocates memory of
695 sufficient size to hold the value. The buffer must be free'd by
696 the developer using 'free(value)'.
698 \param[in] key name of entry
699 \param[out] text returned from preferences or default value if none was set
700 \param[in] defaultValue default value to be used if no preference was set
701 \return 0 if the default value was used
703 char Fl_Preferences::get( const char *key, char *&text, const char *defaultValue ) {
704 const char *v = node->get( key );
705 if ( v && strchr( v, '\\' ) ) {
706 text = decodeText( v );
707 return 1;
709 if ( !v ) v = defaultValue;
710 if ( v )
711 text = strdup( v );
712 else
713 text = 0;
714 return ( v != defaultValue );
718 Sets an entry (name/value pair). The return value indicates if there
719 was a problem storing the data in memory. However it does not
720 reflect if the value was actually stored in the preferences
721 file.
723 \param[in] key name of entry
724 \param[in] text set this entry to \p value
725 \return 0 if setting the value failed
727 char Fl_Preferences::set( const char *key, const char *text ) {
728 const char *s = text ? text : "";
729 int n=0, ns=0;
730 for ( ; *s; s++ ) { n++; if ( *s<32 || *s=='\\' || *s==0x7f ) ns+=4; }
731 if ( ns ) {
732 char *buffer = (char*)malloc( n+ns+1 ), *d = buffer;
733 for ( s=text; *s; ) {
734 char c = *s;
735 if ( c=='\\' ) { *d++ = '\\'; *d++ = '\\'; s++; }
736 else if ( c=='\n' ) { *d++ = '\\'; *d++ = 'n'; s++; }
737 else if ( c=='\r' ) { *d++ = '\\'; *d++ = 'r'; s++; }
738 else if ( c<32 || c==0x7f )
739 { *d++ = '\\'; *d++ = '0'+((c>>6)&3); *d++ = '0'+((c>>3)&7); *d++ = '0'+(c&7); s++; }
740 else *d++ = *s++;
742 *d = 0;
743 node->set( key, buffer );
744 free( buffer );
746 else
747 node->set( key, text );
748 return 1;
751 // convert a hex string to binary data
752 static void *decodeHex( const char *src, int &size ) {
753 size = strlen( src )/2;
754 unsigned char *data = (unsigned char*)malloc( size ), *d = data;
755 const char *s = src;
756 for ( int i=size; i>0; i-- ) {
757 int v;
758 char x = tolower(*s++);
759 if ( x >= 'a' ) v = x-'a'+10; else v = x-'0';
760 v = v<<4;
761 x = tolower(*s++);
762 if ( x >= 'a' ) v += x-'a'+10; else v += x-'0';
763 *d++ = (uchar)v;
765 return (void*)data;
769 Reads an entry from the group. A default value must be
770 supplied. The return value indicates if the value was available
771 (non-zero) or the default was used (0).
772 'maxSize' is the maximum length of text that will be read.
774 \param[in] key name of entry
775 \param[out] data value returned from preferences or default value if none was set
776 \param[in] defaultValue default value to be used if no preference was set
777 \param[in] defaultSize size of default value array
778 \param[in] maxSize maximum length of value
779 \return 0 if the default value was used
781 \todo maxSize should receive the number of bytes that were read.
783 char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int maxSize ) {
784 const char *v = node->get( key );
785 if ( v ) {
786 int dsize;
787 void *w = decodeHex( v, dsize );
788 memmove( data, w, dsize>maxSize?maxSize:dsize );
789 free( w );
790 return 1;
792 if ( defaultValue )
793 memmove( data, defaultValue, defaultSize>maxSize?maxSize:defaultSize );
794 return 0;
798 Reads an entry from the group. A default value must be
799 supplied. The return value indicates if the value was available
800 (non-zero) or the default was used (0). get() allocates memory of
801 sufficient size to hold the value. The buffer must be free'd by
802 the developer using 'free(value)'.
804 \param[in] key name of entry
805 \param[out] data returned from preferences or default value if none was set
806 \param[in] defaultValue default value to be used if no preference was set
807 \param[in] defaultSize size of default value array
808 \return 0 if the default value was used
810 char Fl_Preferences::get( const char *key, void *&data, const void *defaultValue, int defaultSize ) {
811 const char *v = node->get( key );
812 if ( v ) {
813 int dsize;
814 data = decodeHex( v, dsize );
815 return 1;
817 if ( defaultValue ) {
818 data = (void*)malloc( defaultSize );
819 memmove( data, defaultValue, defaultSize );
821 else
822 data = 0;
823 return 0;
827 Sets an entry (name/value pair). The return value indicates if there
828 was a problem storing the data in memory. However it does not
829 reflect if the value was actually stored in the preferences
830 file.
832 \param[in] key name of entry
833 \param[in] data set this entry to \p value
834 \param[in] dsize size of data array
835 \return 0 if setting the value failed
837 char Fl_Preferences::set( const char *key, const void *data, int dsize ) {
838 char *buffer = (char*)malloc( dsize*2+1 ), *d = buffer;;
839 unsigned char *s = (unsigned char*)data;
840 for ( ; dsize>0; dsize-- ) {
841 static char lu[] = "0123456789abcdef";
842 unsigned char v = *s++;
843 *d++ = lu[v>>4];
844 *d++ = lu[v&0xf];
846 *d = 0;
847 node->set( key, buffer );
848 free( buffer );
849 return 1;
853 Returns the size of the value part of an entry.
855 \param[in] key name of entry
856 \return size of value
858 int Fl_Preferences::size( const char *key ) {
859 const char *v = node->get( key );
860 return v ? strlen( v ) : 0 ;
864 \brief Creates a path that is related to the preferences file and
865 that is usable for additional application data.
867 This function creates a directory that is named after the preferences
868 database without the \c .prefs extension and located in the same directory.
869 It then fills the given buffer with the complete path name.
871 Exmaple:
872 \code
873 Fl_Preferences prefs( USER, "matthiasm.com", "test" );
874 char path[FL_PATH_MAX];
875 prefs.getUserdataPath( path );
876 \endcode
877 creates the preferences database in (MS Windows):
878 \code
879 c:/Documents and Settings/matt/Application Data/matthiasm.com/test.prefs
880 \endcode
881 and returns the userdata path:
882 \code
883 c:/Documents and Settings/matt/Application Data/matthiasm.com/test/
884 \endcode
886 \param[out] path buffer for user data path
887 \param[in] pathlen size of path buffer (should be at least \c FL_PATH_MAX)
888 \return 0 if path was not created or pathname can't fit into buffer
890 char Fl_Preferences::getUserdataPath( char *path, int pathlen ) {
891 if ( rootNode )
892 return rootNode->getPath( path, pathlen );
893 return 0;
897 Writes all preferences to disk. This function works only with
898 the base preferences group. This function is rarely used as
899 deleting the base preferences flushes automatically.
901 void Fl_Preferences::flush() {
902 if ( rootNode && node->dirty() )
903 rootNode->write();
906 //-----------------------------------------------------------------------------
907 // helper class to create dynamic group and entry names on the fly
911 Creates a group name or entry name on the fly.
913 This version creates a simple unsigned integer as an entry name.
915 \code
916 int n, i;
917 Fl_Preferences prev( appPrefs, "PreviousFiles" );
918 prev.get( "n", 0 );
919 for ( i=0; i<n; i++ )
920 prev.get( Fl_Preferences::Name(i), prevFile[i], "" );
921 \endcode
923 Fl_Preferences::Name::Name( unsigned int n ) {
924 data_ = (char*)malloc(20);
925 sprintf(data_, "%u", n);
929 Creates a group name or entry name on the fly.
931 This version creates entry names as in 'printf'.
933 \code
934 int n, i;
935 Fl_Preferences prefs( USER, "matthiasm.com", "test" );
936 prev.get( "nFiles", 0 );
937 for ( i=0; i<n; i++ )
938 prev.get( Fl_Preferences::Name( "File%d", i ), prevFile[i], "" );
939 \endcode
941 Fl_Preferences::Name::Name( const char *format, ... ) {
942 data_ = (char*)malloc(1024);
943 va_list args;
944 va_start(args, format);
945 vsnprintf(data_, 1024, format, args);
946 va_end(args);
949 // delete the name
950 Fl_Preferences::Name::~Name() {
951 if (data_) {
952 free(data_);
953 data_ = 0L;
957 //-----------------------------------------------------------------------------
958 // internal methods, do not modify or use as they will change without notice
961 int Fl_Preferences::Node::lastEntrySet = -1;
963 // recursively create a path in the file system
964 static char makePath( const char *path ) {
965 if (access(path, 0)) {
966 const char *s = strrchr( path, '/' );
967 if ( !s ) return 0;
968 int len = s-path;
969 char *p = (char*)malloc( len+1 );
970 memcpy( p, path, len );
971 p[len] = 0;
972 makePath( p );
973 free( p );
974 #if defined(WIN32) && !defined(__CYGWIN__)
975 return ( mkdir( path ) == 0 );
976 #else
977 return ( mkdir( path, 0777 ) == 0 );
978 #endif // WIN32 && !__CYGWIN__
980 return 1;
983 #if 0
984 // strip the filename and create a path
985 static void makePathForFile( const char *path ) {
986 const char *s = strrchr( path, '/' );
987 if ( !s ) return;
988 int len = s-path;
989 char *p = (char*)malloc( len+1 );
990 memcpy( p, path, len );
991 p[len] = 0;
992 makePath( p );
993 free( p );
995 #endif
997 // create the root node
998 // - construct the name of the file that will hold our preferences
999 Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char *vendor, const char *application )
1000 : prefs_(prefs),
1001 filename_(0L),
1002 vendor_(0L),
1003 application_(0L) {
1005 char filename[ FL_PATH_MAX ]; filename[0] = 0;
1006 #ifdef WIN32
1007 # define FLPREFS_RESOURCE "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
1008 # define FLPREFS_RESOURCEW L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
1009 int appDataLen = strlen(vendor) + strlen(application) + 8;
1010 DWORD type, nn;
1011 LONG err;
1012 HKEY key;
1014 switch (root) {
1015 case SYSTEM:
1016 err = RegOpenKeyW( HKEY_LOCAL_MACHINE, FLPREFS_RESOURCEW, &key );
1017 if (err == ERROR_SUCCESS) {
1018 nn = FL_PATH_MAX - appDataLen;
1019 err = RegQueryValueExW( key, L"Common AppData", 0L, &type,
1020 (BYTE*)filename, &nn );
1021 if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) ) {
1022 filename[0] = 0;
1023 filename[1] = 0;
1025 RegCloseKey(key);
1027 break;
1028 case USER:
1029 err = RegOpenKeyW( HKEY_CURRENT_USER, FLPREFS_RESOURCEW, &key );
1030 if (err == ERROR_SUCCESS) {
1031 nn = FL_PATH_MAX - appDataLen;
1032 err = RegQueryValueExW( key, L"AppData", 0L, &type,
1033 (BYTE*)filename, &nn );
1034 if ( ( err != ERROR_SUCCESS ) && ( type == REG_SZ ) ) {
1035 filename[0] = 0;
1036 filename[1] = 0;
1038 RegCloseKey(key);
1040 break;
1042 if (!filename[1] && !filename[0]) {
1043 strcpy(filename, "C:\\FLTK");
1044 } else {
1045 #if 0
1046 xchar *b = (xchar*)_wcsdup((xchar *)filename);
1047 #else
1048 // cygwin does not come with _wcsdup. Use malloc + wcscpy.
1049 // For implementation of wcsdup functionality See
1050 // - http://linenum.info/p/glibc/2.7/wcsmbs/wcsdup.c
1051 xchar *b = (xchar*) malloc((wcslen((xchar *) filename) + 1) * sizeof(xchar));
1052 wcscpy(b, (xchar *) filename);
1053 #endif
1054 // filename[fl_unicode2utf(b, wcslen((xchar*)b), filename)] = 0;
1055 unsigned len = fl_utf8fromwc(filename, (FL_PATH_MAX-1), b, wcslen(b));
1056 filename[len] = 0;
1057 free(b);
1059 snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
1060 "/%s/%s.prefs", vendor, application);
1061 for (char *s = filename; *s; s++) if (*s == '\\') *s = '/';
1062 #elif defined ( __APPLE__ )
1063 // TODO: verify that this is the Apple sanctioned way of finding these folders
1064 // (On MSWindows, this frequently leads to issues with internationalized systems)
1065 // Carbon: err = FindFolder( kLocalDomain, kPreferencesFolderType, 1, &spec.vRefNum, &spec.parID );
1066 switch (root) {
1067 case SYSTEM:
1068 strcpy(filename, "/Library/Preferences");
1069 break;
1070 case USER:
1071 sprintf(filename, "%s/Library/Preferences", fl_getenv("HOME"));
1072 break;
1074 snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
1075 "/%s/%s.prefs", vendor, application );
1076 #else
1077 const char *e;
1078 switch (root) {
1079 case USER:
1080 if ((e = fl_getenv("HOME")) != NULL) {
1081 strlcpy(filename, e, sizeof(filename));
1083 if (filename[strlen(filename)-1] != '/') {
1084 strlcat(filename, "/.fltk/", sizeof(filename));
1085 } else {
1086 strlcat(filename, ".fltk/", sizeof(filename));
1088 break;
1090 case SYSTEM:
1091 strcpy(filename, "/etc/fltk/");
1092 break;
1094 snprintf(filename + strlen(filename), sizeof(filename) - strlen(filename),
1095 "%s/%s.prefs", vendor, application);
1096 #endif
1097 filename_ = strdup(filename);
1098 vendor_ = strdup(vendor);
1099 application_ = strdup(application);
1100 read();
1103 // create the root node
1104 // - construct the name of the file that will hold our preferences
1105 Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, const char *vendor, const char *application )
1106 : prefs_(prefs),
1107 filename_(0L),
1108 vendor_(0L),
1109 application_(0L) {
1111 if (!vendor)
1112 vendor = "unknown";
1113 if (!application) {
1114 application = "unknown";
1115 filename_ = strdup(path);
1116 } else {
1117 char filename[ FL_PATH_MAX ]; filename[0] = 0;
1118 snprintf(filename, sizeof(filename), "%s/%s.prefs", path, application);
1119 filename_ = strdup(filename);
1121 vendor_ = strdup(vendor);
1122 application_ = strdup(application);
1123 read();
1126 // create a root node that exists only on RAM and can not be read or written to
1127 // a file
1128 Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs )
1129 : prefs_(prefs),
1130 filename_(0L),
1131 vendor_(0L),
1132 application_(0L) {
1135 // destroy the root node and all depending nodes
1136 Fl_Preferences::RootNode::~RootNode() {
1137 if ( prefs_->node->dirty() )
1138 write();
1139 if ( filename_ ) {
1140 free( filename_ );
1141 filename_ = 0L;
1143 if ( vendor_ ) {
1144 free( vendor_ );
1145 vendor_ = 0L;
1147 if ( application_ ) {
1148 free( application_ );
1149 application_ = 0L;
1151 delete prefs_->node;
1152 prefs_->node = 0L;
1155 // read a preferences file and construct the group tree and with all entry leafs
1156 int Fl_Preferences::RootNode::read() {
1157 if (!filename_) // RUNTIME preferences
1158 return -1;
1159 char buf[1024];
1160 FILE *f = fl_fopen( filename_, "rb" );
1161 if ( !f )
1162 return -1;
1163 if (fgets( buf, 1024, f )==0) { /* ignore */ }
1164 if (fgets( buf, 1024, f )==0) { /* ignore */ }
1165 if (fgets( buf, 1024, f )==0) { /* ignore */ }
1166 Node *nd = prefs_->node;
1167 for (;;) {
1168 if ( !fgets( buf, 1024, f ) ) break; // EOF or Error
1169 if ( buf[0]=='[' ) { // read a new group
1170 int end = strcspn( buf+1, "]\n\r" );
1171 buf[ end+1 ] = 0;
1172 nd = prefs_->node->find( buf+1 );
1173 } else if ( buf[0]=='+' ) { // value of previous name/value pair spans multiple lines
1174 int end = strcspn( buf+1, "\n\r" );
1175 if ( end != 0 ) { // if entry is not empty
1176 buf[ end+1 ] = 0;
1177 nd->add( buf+1 );
1179 } else { // read a name/value pair
1180 int end = strcspn( buf, "\n\r" );
1181 if ( end != 0 ) { // if entry is not empty
1182 buf[ end ] = 0;
1183 nd->set( buf );
1187 fclose( f );
1188 return 0;
1191 // write the group tree and all entry leafs
1192 int Fl_Preferences::RootNode::write() {
1193 if (!filename_) // RUNTIME preferences
1194 return -1;
1195 fl_make_path_for_file(filename_);
1196 FILE *f = fl_fopen( filename_, "wb" );
1197 if ( !f )
1198 return -1;
1199 fprintf( f, "; FLTK preferences file format 1.0\n" );
1200 fprintf( f, "; vendor: %s\n", vendor_ );
1201 fprintf( f, "; application: %s\n", application_ );
1202 prefs_->node->write( f );
1203 fclose( f );
1204 #if !(defined(__APPLE__) || defined(WIN32))
1205 // unix: make sure that system prefs are user-readable
1206 if (strncmp(filename_, "/etc/fltk/", 10) == 0) {
1207 char *p;
1208 p = filename_ + 9;
1209 do { // for each directory to the pref file
1210 *p = 0;
1211 fl_chmod(filename_, 0755); // rwxr-xr-x
1212 *p = '/';
1213 p = strchr(p+1, '/');
1214 } while (p);
1215 fl_chmod(filename_, 0644); // rw-r--r--
1217 #endif
1218 return 0;
1221 // get the path to the preferences directory
1222 char Fl_Preferences::RootNode::getPath( char *path, int pathlen ) {
1223 if (!filename_) // RUNTIME preferences
1224 return -1;
1225 strlcpy( path, filename_, pathlen);
1227 char *s;
1228 for ( s = path; *s; s++ ) if ( *s == '\\' ) *s = '/';
1229 s = strrchr( path, '.' );
1230 if ( !s ) return 0;
1231 *s = 0;
1232 char ret = fl_make_path( path );
1233 #if !(defined(__APPLE__) || defined(WIN32))
1234 // unix: make sure that system prefs dir. is user-readable
1235 if (strncmp(path, "/etc/fltk/", 10) == 0) {
1236 fl_chmod(path, 0755); // rwxr-xr-x
1238 #endif
1239 strcpy( s, "/" );
1240 return ret;
1243 // create a node that represents a group
1244 // - path must be a single word, prferable alnum(), dot and underscore only. Space is ok.
1245 Fl_Preferences::Node::Node( const char *path ) {
1246 if ( path ) path_ = strdup( path ); else path_ = 0;
1247 child_ = 0; next_ = 0; parent_ = 0;
1248 entry_ = 0;
1249 nEntry_ = NEntry_ = 0;
1250 dirty_ = 0;
1251 top_ = 0;
1252 indexed_ = 0;
1253 index_ = 0;
1254 nIndex_ = NIndex_ = 0;
1257 void Fl_Preferences::Node::deleteAllChildren() {
1258 Node *nx;
1259 for ( Node *nd = child_; nd; nd = nx ) {
1260 nx = nd->next_;
1261 delete nd;
1263 child_ = 0L;
1264 dirty_ = 1;
1265 updateIndex();
1268 void Fl_Preferences::Node::deleteAllEntries() {
1269 if ( entry_ ) {
1270 for ( int i = 0; i < nEntry_; i++ ) {
1271 if ( entry_[i].name ) {
1272 free( entry_[i].name );
1273 entry_[i].name = 0L;
1275 if ( entry_[i].value ) {
1276 free( entry_[i].value );
1277 entry_[i].value = 0L;
1280 free( entry_ );
1281 entry_ = 0L;
1282 nEntry_ = 0;
1283 NEntry_ = 0;
1285 dirty_ = 1;
1288 // delete this and all depending nodes
1289 Fl_Preferences::Node::~Node() {
1290 deleteAllChildren();
1291 deleteAllEntries();
1292 deleteIndex();
1293 if ( path_ ) {
1294 free( path_ );
1295 path_ = 0L;
1297 next_ = 0L;
1298 parent_ = 0L;
1301 // recursively check if any entry is dirty (was changed after loading a fresh prefs file)
1302 char Fl_Preferences::Node::dirty() {
1303 if ( dirty_ ) return 1;
1304 if ( next_ && next_->dirty() ) return 1;
1305 if ( child_ && child_->dirty() ) return 1;
1306 return 0;
1309 // write this node (recursively from the last neighbor back to this)
1310 // write all entries
1311 // write all children
1312 int Fl_Preferences::Node::write( FILE *f ) {
1313 if ( next_ ) next_->write( f );
1314 fprintf( f, "\n[%s]\n\n", path_ );
1315 for ( int i = 0; i < nEntry_; i++ ) {
1316 char *src = entry_[i].value;
1317 if ( src ) { // hack it into smaller pieces if needed
1318 fprintf( f, "%s:", entry_[i].name );
1319 int cnt, written;
1320 for ( cnt = 0; cnt < 60; cnt++ )
1321 if ( src[cnt]==0 ) break;
1322 written = fwrite( src, cnt, 1, f );
1323 fprintf( f, "\n" );
1324 src += cnt;
1325 for (;*src;) {
1326 for ( cnt = 0; cnt < 80; cnt++ )
1327 if ( src[cnt]==0 ) break;
1328 fputc( '+', f );
1329 written = fwrite( src, cnt, 1, f );
1330 fputc( '\n', f );
1331 src += cnt;
1334 else
1335 fprintf( f, "%s\n", entry_[i].name );
1337 if ( child_ ) child_->write( f );
1338 dirty_ = 0;
1339 return 0;
1342 // set the parent node and create the full path
1343 void Fl_Preferences::Node::setParent( Node *pn ) {
1344 parent_ = pn;
1345 next_ = pn->child_;
1346 pn->child_ = this;
1347 sprintf( nameBuffer, "%s/%s", pn->path_, path_ );
1348 free( path_ );
1349 path_ = strdup( nameBuffer );
1352 // find the corresponding root node
1353 Fl_Preferences::RootNode *Fl_Preferences::Node::findRoot() {
1354 Node *n = this;
1355 do {
1356 if (n->top_)
1357 return n->root_;
1358 n = n->parent();
1359 } while (n);
1360 return 0L;
1363 // add a child to this node and set its path (try to find it first...)
1364 Fl_Preferences::Node *Fl_Preferences::Node::addChild( const char *path ) {
1365 sprintf( nameBuffer, "%s/%s", path_, path );
1366 char *name = strdup( nameBuffer );
1367 Node *nd = find( name );
1368 free( name );
1369 dirty_ = 1;
1370 updateIndex();
1371 return nd;
1374 // create and set, or change an entry within this node
1375 void Fl_Preferences::Node::set( const char *name, const char *value )
1377 for ( int i=0; i<nEntry_; i++ ) {
1378 if ( strcmp( name, entry_[i].name ) == 0 ) {
1379 if ( !value ) return; // annotation
1380 if ( strcmp( value, entry_[i].value ) != 0 ) {
1381 if ( entry_[i].value )
1382 free( entry_[i].value );
1383 entry_[i].value = strdup( value );
1384 dirty_ = 1;
1386 lastEntrySet = i;
1387 return;
1390 if ( NEntry_==nEntry_ ) {
1391 NEntry_ = NEntry_ ? NEntry_*2 : 10;
1392 entry_ = (Entry*)realloc( entry_, NEntry_ * sizeof(Entry) );
1394 entry_[ nEntry_ ].name = strdup( name );
1395 entry_[ nEntry_ ].value = value?strdup( value ):0;
1396 lastEntrySet = nEntry_;
1397 nEntry_++;
1398 dirty_ = 1;
1401 // create or set a value (or annotation) from a single line in the file buffer
1402 void Fl_Preferences::Node::set( const char *line ) {
1403 // hmm. If we assume that we always read this file in the beginning,
1404 // we can handle the dirty flag 'quick and dirty'
1405 char dirt = dirty_;
1406 if ( line[0]==';' || line[0]==0 || line[0]=='#' ) {
1407 set( line, 0 );
1408 } else {
1409 const char *c = strchr( line, ':' );
1410 if ( c ) {
1411 unsigned int len = c-line+1;
1412 if ( len >= sizeof( nameBuffer ) )
1413 len = sizeof( nameBuffer );
1414 strlcpy( nameBuffer, line, len );
1415 set( nameBuffer, c+1 );
1416 } else {
1417 set( line, "" );
1420 dirty_ = dirt;
1423 // add more data to an existing entry
1424 void Fl_Preferences::Node::add( const char *line ) {
1425 if ( lastEntrySet<0 || lastEntrySet>=nEntry_ ) return;
1426 char *&dst = entry_[ lastEntrySet ].value;
1427 int a = strlen( dst );
1428 int b = strlen( line );
1429 dst = (char*)realloc( dst, a+b+1 );
1430 memcpy( dst+a, line, b+1 );
1431 dirty_ = 1;
1434 // get the value for a name, returns 0 if no such name
1435 const char *Fl_Preferences::Node::get( const char *name ) {
1436 int i = getEntry( name );
1437 return i>=0 ? entry_[i].value : 0 ;
1440 // find the index of an entry, returns -1 if no such entry
1441 int Fl_Preferences::Node::getEntry( const char *name ) {
1442 for ( int i=0; i<nEntry_; i++ ) {
1443 if ( strcmp( name, entry_[i].name ) == 0 ) {
1444 return i;
1447 return -1;
1450 // remove one entry form this group
1451 char Fl_Preferences::Node::deleteEntry( const char *name ) {
1452 int ix = getEntry( name );
1453 if ( ix == -1 ) return 0;
1454 memmove( entry_+ix, entry_+ix+1, (nEntry_-ix-1) * sizeof(Entry) );
1455 nEntry_--;
1456 dirty_ = 1;
1457 return 1;
1460 // find a group somewhere in the tree starting here
1461 // - this method will always return a valid node (except for memory allocation problems)
1462 // - if the node was not found, 'find' will create the required branch
1463 Fl_Preferences::Node *Fl_Preferences::Node::find( const char *path ) {
1464 int len = strlen( path_ );
1465 if ( strncmp( path, path_, len ) == 0 ) {
1466 if ( path[ len ] == 0 )
1467 return this;
1468 if ( path[ len ] == '/' ) {
1469 Node *nd;
1470 for ( nd = child_; nd; nd = nd->next_ ) {
1471 Node *nn = nd->find( path );
1472 if ( nn ) return nn;
1474 const char *s = path+len+1;
1475 const char *e = strchr( s, '/' );
1476 if (e) strlcpy( nameBuffer, s, e-s+1 );
1477 else strlcpy( nameBuffer, s, sizeof(nameBuffer));
1478 nd = new Node( nameBuffer );
1479 nd->setParent( this );
1480 return nd->find( path );
1483 return 0;
1486 // find a group somewhere in the tree starting here
1487 // caller must not set 'offset' argument
1488 // - if the node does not exist, 'search' returns NULL
1489 // - if the pathname is "." (current node) return this node
1490 // - if the pathname is "./" (root node) return the topmost node
1491 // - if the pathname starts with "./", start the search at the root node instead
1492 Fl_Preferences::Node *Fl_Preferences::Node::search( const char *path, int offset ) {
1493 if ( offset == 0 ) {
1494 if ( path[0] == '.' ) {
1495 if ( path[1] == 0 ) {
1496 return this; // user was searching for current node
1497 } else if ( path[1] == '/' ) {
1498 Node *nn = this;
1499 while ( nn->parent() ) nn = nn->parent();
1500 if ( path[2]==0 ) { // user is searching for root ( "./" )
1501 return nn;
1503 return nn->search( path+2, 2 ); // do a relative search on the root node
1506 offset = strlen( path_ ) + 1;
1508 int len = strlen( path_ );
1509 if ( len < offset-1 ) return 0;
1510 len -= offset;
1511 if ( ( len <= 0 ) || ( strncmp( path, path_+offset, len ) == 0 ) ) {
1512 if ( len > 0 && path[ len ] == 0 )
1513 return this;
1514 if ( len <= 0 || path[ len ] == '/' ) {
1515 for ( Node *nd = child_; nd; nd = nd->next_ ) {
1516 Node *nn = nd->search( path, offset );
1517 if ( nn ) return nn;
1519 return 0;
1522 return 0;
1525 // return the number of child nodes (groups)
1526 int Fl_Preferences::Node::nChildren() {
1527 if (indexed_) {
1528 return nIndex_;
1529 } else {
1530 int cnt = 0;
1531 for ( Node *nd = child_; nd; nd = nd->next_ )
1532 cnt++;
1533 return cnt;
1537 // return the node name
1538 const char *Fl_Preferences::Node::name() {
1539 if ( path_ ) {
1540 char *r = strrchr( path_, '/' );
1541 return r ? r+1 : path_ ;
1542 } else {
1543 return 0L ;
1547 // return the n'th child node's name
1548 const char *Fl_Preferences::Node::child( int ix ) {
1549 Node *nd = childNode( ix );
1550 if ( nd )
1551 return nd->name();
1552 else
1553 return 0L ;
1556 // return the n'th child node
1557 Fl_Preferences::Node *Fl_Preferences::Node::childNode( int ix ) {
1558 createIndex();
1559 if (indexed_) {
1560 // usually faster access in correct order, but needing more memory
1561 return index_[ix];
1562 } else {
1563 // slow access and reverse order
1564 int n = nChildren();
1565 ix = n - ix -1;
1566 Node *nd;
1567 for ( nd = child_; nd; nd = nd->next_ ) {
1568 if ( !ix-- ) break;
1569 if ( !nd ) break;
1571 return nd;
1575 // remove myself from the list and delete me (and all children)
1576 char Fl_Preferences::Node::remove() {
1577 Node *nd = 0, *np;
1578 if ( parent() ) {
1579 nd = parent()->child_; np = 0L;
1580 for ( ; nd; np = nd, nd = nd->next_ ) {
1581 if ( nd == this ) {
1582 if ( np )
1583 np->next_ = nd->next_;
1584 else
1585 parent()->child_ = nd->next_;
1586 break;
1589 parent()->dirty_ = 1;
1590 parent()->updateIndex();
1592 delete this;
1593 return ( nd != 0 );
1596 void Fl_Preferences::Node::createIndex() {
1597 if (indexed_) return;
1598 int n = nChildren();
1599 if (n>NIndex_) {
1600 NIndex_ = n + 16;
1601 index_ = (Node**)realloc(index_, NIndex_*sizeof(Node**));
1603 Node *nd;
1604 int i = 0;
1605 for (nd = child_; nd; nd = nd->next_, i++) {
1606 index_[n-i-1] = nd;
1608 nIndex_ = n;
1609 indexed_ = 1;
1612 void Fl_Preferences::Node::updateIndex() {
1613 indexed_ = 0;
1616 void Fl_Preferences::Node::deleteIndex() {
1617 if (index_) free(index_);
1618 NIndex_ = nIndex_ = 0;
1619 index_ = 0;
1620 indexed_ = 0;
1624 * \brief Create a plugin.
1626 * \param[in] klass plugins are grouped in classes
1627 * \param[in] name every plugin should have a unique name
1629 Fl_Plugin::Fl_Plugin(const char *klass, const char *name)
1630 : id(0) {
1631 #ifdef FL_PLUGIN_VERBOSE
1632 printf("Fl_Plugin: creating a plugin, class \"%s\", name \"%s\"\n",
1633 klass, name);
1634 #endif
1635 Fl_Plugin_Manager pm(klass);
1636 id = pm.addPlugin(name, this);
1640 * \brief Clear the plugin and remove it from the database.
1642 Fl_Plugin::~Fl_Plugin() {
1643 #ifdef FL_PLUGIN_VERBOSE
1644 printf("Fl_Plugin: deleting a plugin\n");
1645 #endif
1646 if (id)
1647 Fl_Plugin_Manager::remove(id);
1651 * \brief Manage all plugins belonging to one class.
1653 Fl_Plugin_Manager::Fl_Plugin_Manager(const char *klass)
1654 : Fl_Preferences(0, Fl_Preferences::Name("%s/%s", "plugins", klass)) {
1655 #ifdef FL_PLUGIN_VERBOSE
1656 printf("Fl_Plugin: creating a plugin manager for class \"%s\"\n", klass);
1657 #endif
1661 * \brief Remove the plugin manager.
1663 * Calling this does not remove the database itself or any plugins. It just
1664 * removes the reference to the database.
1666 Fl_Plugin_Manager::~Fl_Plugin_Manager() {
1667 #ifdef FL_PLUGIN_VERBOSE
1668 printf("Fl_Plugin: deleting a plugin manager\n");
1669 #endif
1672 static unsigned char x2i(char hi, char lo) {
1673 return ((hi-'A')<<4) | (lo-'A');
1676 static void i2x(unsigned char v, char *d) {
1677 d[0] = ((v>>4)&0x0f)+'A'; d[1] = (v&0x0f)+'A';
1680 static void *a2p(const char *s) {
1681 union { void *ret; unsigned char d[sizeof(void*)]; } v;
1682 v.ret = 0L;
1683 int i=0, n=sizeof(void*);
1684 for (i=0; i<n; i++) {
1685 v.d[i] = x2i(s[2*i], s[2*i+1]);
1687 return v.ret;
1690 static void p2a(void *vp, char *d) {
1691 union { void *vp; unsigned char s[sizeof(void*)]; } v;
1692 v.vp = vp;
1693 int i=0, n=sizeof(void*);
1694 for (i=0; i<n; i++) {
1695 i2x(v.s[i], d+i*2);
1697 d[2*i] = 0;
1701 * \brief Return the address of a plugin by index.
1703 Fl_Plugin *Fl_Plugin_Manager::plugin(int index) {
1704 char buf[34];
1705 Fl_Plugin *ret = 0;
1706 Fl_Preferences pin(this, index);
1707 pin.get("address", buf, "", 34);
1708 if (buf[0]=='@') ret = (Fl_Plugin*)a2p(buf+1);
1709 #ifdef FL_PLUGIN_VERBOSE
1710 printf("Fl_Plugin: returning plugin at index %d: (%s) %p\n", index, buf, ret);
1711 #endif
1712 return ret;
1716 * \brief Return the address of a plugin by name.
1718 Fl_Plugin *Fl_Plugin_Manager::plugin(const char *name) {
1719 char buf[34];
1720 Fl_Plugin *ret = 0;
1721 if (groupExists(name)) {
1722 Fl_Preferences pin(this, name);
1723 pin.get("address", buf, "", 34);
1724 if (buf[0]=='@') ret = (Fl_Plugin*)a2p(buf+1);
1725 #ifdef FL_PLUGIN_VERBOSE
1726 printf("Fl_Plugin: returning plugin named \"%s\": (%s) %p\n", name, buf, ret);
1727 #endif
1728 return ret;
1729 } else {
1730 #ifdef FL_PLUGIN_VERBOSE
1731 printf("Fl_Plugin: no plugin found named \"%s\"\n", name);
1732 #endif
1733 return 0L;
1738 * \brief This function adds a new plugin to the database.
1740 * There is no need to call this function explicitly. Every Fl_Plugin constructor
1741 * will call this function at initialization time.
1743 Fl_Preferences::ID Fl_Plugin_Manager::addPlugin(const char *name, Fl_Plugin *plugin) {
1744 char buf[34];
1745 #ifdef FL_PLUGIN_VERBOSE
1746 printf("Fl_Plugin: adding plugin named \"%s\" at 0x%p\n", name, plugin);
1747 #endif
1748 Fl_Preferences pin(this, name);
1749 buf[0] = '@'; p2a(plugin, buf+1);
1750 pin.set("address", buf);
1751 return pin.id();
1755 * \brief Remove any plugin.
1757 * There is no need to call this function explicitly. Every Fl_Plugin destructor
1758 * will call this function at destruction time.
1760 void Fl_Plugin_Manager::removePlugin(Fl_Preferences::ID id) {
1761 Fl_Preferences::remove(id);
1765 * \brief Load a module from disk.
1767 * A module must be a dynamically linkable file for the given operating system.
1768 * When loading a module, its +init function will be called which in turn calls
1769 * the constructor of all statically initialized Fl_Plugin classes and adds
1770 * them to the database.
1772 int Fl_Plugin_Manager::load(const char *filename) {
1773 // the functions below will autmaticaly load plugins that are defined:
1774 // Fl_My_Plugin plugin();
1775 #if defined(WIN32) && !defined(__CYGWIN__)
1776 HMODULE dl = LoadLibrary(filename);
1777 #else
1778 void * dl = dlopen(filename, RTLD_LAZY);
1779 #endif
1780 // There is no way of unloading a plugin!
1781 return (dl!=0) ? 0 : -1;
1785 * \brief Use this function to load a whole directory full of modules.
1787 int Fl_Plugin_Manager::loadAll(const char *filepath, const char *pattern) {
1788 struct dirent **dir;
1789 int i, n = fl_filename_list(filepath, &dir);
1790 for (i=0; i<n; i++) {
1791 struct dirent *e = dir[i];
1792 if (pattern==0 || fl_filename_match(e->d_name, pattern)) {
1793 load(Fl_Preferences::Name("%s%s", filepath, e->d_name));
1795 free(e);
1797 free(dir);
1798 return 0;
1802 // End of "$Id: Fl_Preferences.cxx 8291 2011-01-19 06:33:48Z manolo $".