FLTK logo

[master] 09eff72 - Fixing and upgrading Fl_Preferences (#374)

FLTK matrix user chat room
(using Element browser app)   FLTK gitter user chat room   GitHub FLTK Project   FLTK News RSS Feed  
  FLTK Apps      FLTK Library      Forums      Links     Login 
 All Forums  |  Back to fltk.commit  ]
 
Previous Message ]Next Message ]

[master] 09eff72 - Fixing and upgrading Fl_Preferences (#374) "Matthias Melcher" Jan 19, 2022  
 
commit 09eff7243a6e8e37d9615df7b951ffa3c03c0ae2
Author:     Matthias Melcher <github@matthiasm.com>
AuthorDate: Wed Jan 19 16:08:29 2022 +0100
Commit:     GitHub <noreply@github.com>
CommitDate: Wed Jan 19 16:08:29 2022 +0100

    Fixing and upgrading Fl_Preferences (#374)
    
    * Added filename function to Fl_Preferences
    
    Static function to get filename before opening.
    Member to get filename after opening.
    Bug fixes for memory mapped preferences.
    
    * ERROR is a macro on Windows, don't use it
    
    * Added Fl_Preferences::dirty().
    
    User can now check if the database will be written
    when flushed or destroyed.
    Flush returns a crude error code.
    
    * Fl_Preferences::get binary data returns # of bytes read.
    
    * Verified group deletion code
    
    * Fl_Preferences ignores locale.
    
    This will make .prefs files interchangeable
    between different computers.
    
    * Updating the Preferences Mode to ignore locale.
    
    * Fixes in docs.

 FL/Fl_Preferences.H                              | 132 +++++--
 fluid/Fl_Function_Type.cxx                       |  14 +-
 fluid/alignment_panel.fl                         |  23 +-
 fluid/fluid.cxx                                  |   2 +-
 src/Fl_Preferences.cxx                           | 467 +++++++++++++++++------
 src/Fl_System_Driver.H                           |   2 +
 src/Fl_System_Driver.cxx                         |   8 +
 src/Fl_cocoa.mm                                  |   5 +-
 src/drivers/Android/Fl_Android_System_Driver.H   |   2 +
 src/drivers/Android/Fl_Android_System_Driver.cxx |  11 +
 src/drivers/Darwin/Fl_Darwin_System_Driver.H     |   2 +
 src/drivers/Darwin/Fl_Darwin_System_Driver.cxx   |  38 +-
 src/drivers/WinAPI/Fl_WinAPI_System_Driver.H     |   2 +
 src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx   |  41 +-
 src/drivers/X11/Fl_X11_System_Driver.H           |   2 +
 src/drivers/X11/Fl_X11_System_Driver.cxx         |  41 +-
 test/blocks.cxx                                  |   2 +-
 test/preferences.fl                              |  34 +-
 test/sudoku.cxx                                  |   2 +-
 19 files changed, 638 insertions(+), 192 deletions(-)

diff --git FL/Fl_Preferences.H FL/Fl_Preferences.H
index 9378ec4..21e331c 100644
--- FL/Fl_Preferences.H
+++ FL/Fl_Preferences.H
@@ -1,7 +1,7 @@
 //
 // Preferences implementation for the Fast Light Tool Kit (FLTK).
 //
-// Copyright 2002-2010 by Matthias Melcher.
+// Copyright 2002-2022 by Matthias Melcher.
 //
 // This library is free software. Distribution and use rights are outlined in
 // the file "COPYING" which should have been included with this file.  If this
@@ -24,31 +24,74 @@
 #  include "Fl_Export.H"
 
 /**
-   \brief Fl_Preferences provides methods to store user
-   settings between application starts.
-
-   It is similar to the
-   Registry on Windows and Preferences on MacOS, and provides a
-   simple configuration mechanism for UNIX.
-
-   Fl_Preferences uses a hierarchy to store data. It
-   bundles similar data into groups and manages entries in these
-   groups as name/value pairs.
-
-   Preferences are stored in text files that can be edited
-   manually. The file format is easy to read and relatively
-   forgiving. Preferences files are the same on all platforms. User
-   comments in preference files are preserved. Filenames are unique
-   for each application by using a vendor/application naming
-   scheme. The user must provide default values for all entries to
-   ensure proper operation should preferences be corrupted or not
-   yet exist.
-
-   Entries can be of any length. However, the size of each
-   preferences file should be kept small for performance
-   reasons. One application can have multiple preferences files.
-   Extensive binary data however should be stored in separate
-   files: see \a Fl_Preferences::getUserdataPath() .
+ \brief Fl_Preferences store user settings between application starts.
+
+ Fl_Preferences are similar to the Registry on Windows and Preferences on MacOS,
+ providing a simple method to store customisable user settings between app
+ launches, i.e. the previous window position or a history of previously
+ used documents.
+
+ Preferences are organized in a hierarchy of groups. Every group can contain
+ more groups and any number of kay/value pairs. Keys can be text strings
+ containing ASCII letters, digits, periods, and underscores. Forward slashes
+ in a key name are treated as subgroups, i.e the key 'window/width' would
+ actually refere to the key 'width' inside the group 'window'.
+
+ Keys have usually a unique name within their group. Duplicate kays are
+ possible though and can beaccessed using the index based functions.
+
+ A value should be an ASCII string. Control characters and utf8 sequences are
+ stores as octal values. Long strings will wrap at the line ending and will be
+ reassembled when reading the file back.
+
+ Many shortcuts exist to set and get numerical values and binary data.
+
+ Preferences are stored in text files that can be edited manually if needed.
+ The file format is easy to read and relatively forgiving. Preferences files
+ are the same on all platforms. User comments in preference files are preserved.
+ Filenames are unique for each application by using a vendor/application naming
+ scheme. The user must provide default values for all entries to ensure proper
+ operation should preferences be corrupted or not yet exist.
+
+ FLTK preferences are not meant to replace a fully features database. No merging
+ of data takes place. If several instances of an app access the same database at
+ the same time, only the most recent changes will persist.
+
+ Preferences should no be used to store document data. The .prefs file should
+ be kept small for performance reasons. One application can have multiple
+ preferences files. Extensive binary data however should be stored in separate
+ files: see \a Fl_Preferences::getUserdataPath() .
+
+ Fl_Preferences are not thread-safe. They can temprorarily change the locale
+ on some platforms during read an write access, which is alse observable in
+ other threads of the same app.
+
+ Typically a preferences database is read at startup and close, and then writte
+ again at app shutdown:
+ ```.cpp
+ int appWindowWidth, appWindowHeight;
+ void launch() {
+   Fl_Preferences app(Fl_Preferences::USER_L, "matthiasm.com", "hello");
+   // 'app' constructor will be called, reading data from .prefs file
+   Fl_Preferences window(app, "window");
+   window.get("width", appWindowWidth, 800);
+   window.get("height", appWindowHeight, 600);
+   // 'app' destructor will be called, writing data to .prefs file
+ }
+ void quit() {
+   Fl_Preferences app(Fl_Preferences::USER_L, "matthiasm.com", "hello");
+   Fl_Preferences window(app, "window");
+   window.set("width", appWindowWidth);
+   window.set("height", appWindowHeight);
+ }
+ ```
+
+ \see Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application )
+
+ As a special case, Fl_Preferences can be memeory mapped and not be associated
+ with a file on disk.
+
+ \see Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group )
 
    \note Starting with FLTK 1.3, preference databases are expected to
      be in UTF-8 encoding. Previous databases were stored in the
@@ -59,6 +102,14 @@
      the preferences files has changed slightly. Please see
      Fl_Preferences::Fl_Preferences(Root, const char*, const char*)
      for details.
+
+   \note Starting with FLTK 1.4, preference files should be created with
+     `SYSTEM_L` or `USER_L` to be interchangeable between computers with
+     differing loacale settings. The legacy modes, `LOCAL` and `SYSTEM`, will
+     read and write floating point values using the decimal point of the
+     current locale. As a result, a fp-value would be writte '3,1415' on a
+     German machine, and would be read back as '3.0' on a US machine because
+     the comma would not be recoginized as an alternative decimal point.
  */
 class FL_EXPORT Fl_Preferences {
 
@@ -67,12 +118,17 @@ public:
      Define the scope of the preferences.
    */
   enum Root {
-    SYSTEM = 0,        ///< Preferences are used system-wide
-    USER,              ///< Preferences apply only to the current user
+    UNKNOWN_ROOT_TYPE = -1, ///< Returned if storage could not be determined.
+    SYSTEM = 0,        ///< Preferences are used system-wide, deprecated, see SYSTEM_L
+    USER,              ///< Preferences apply only to the current user, deprecated, see USER_L
+    MEMORY,            ///< Returned if querying runtime prefs
     ROOT_MASK = 0xFF,  ///< masks for the values above
     CORE = 0x100,      ///< OR'd by FLTK to read and write core library preferences and options
     CORE_SYSTEM = CORE|SYSTEM,
-    CORE_USER = CORE|USER
+    CORE_USER = CORE|USER,
+    C_LOCALE = 0x1000, ///< this flag should always be set, it makes sure that floating point values wre writte correctly independently of the current locale
+    SYSTEM_L = SYSTEM|C_LOCALE, ///< Preferences are used system-wide
+    USER_L = USER|C_LOCALE,     ///< Preferences apply only to the current user
   };
 
   /**
@@ -120,6 +176,7 @@ public:
 
   static void file_access(unsigned int flags);
   static unsigned int file_access();
+  static Root filename( char *buffer, size_t buffer_size, Root root, const char *vendor, const char *application );
 
   Fl_Preferences( Root root, const char *vendor, const char *application );
   Fl_Preferences( const char *path, const char *vendor, const char *application );
@@ -131,6 +188,8 @@ public:
   Fl_Preferences( ID id );
   virtual ~Fl_Preferences();
 
+  Root filename( char *buffer, size_t buffer_size);
+
   /** Return an ID that can later be reused to open more references to this dataset.
    */
   ID id() { return (ID)node; }
@@ -176,12 +235,15 @@ public:
   char get( const char *entry, char *value,   const char *defaultValue, int maxSize );
   char get( const char *entry, void *&value,  const void *defaultValue, int defaultSize );
   char get( const char *entry, void *value,   const void *defaultValue, int defaultSize, int maxSize );
+  char get( const char *entry, void *value,   const void *defaultValue, int defaultSize, int *size );
 
   int size( const char *entry );
 
   char getUserdataPath( char *path, int pathlen );
 
-  void flush();
+  int flush();
+
+  int dirty();
 
   // char export( const char *filename, Type fileFormat );
   // char import( const char *filename );
@@ -233,10 +295,10 @@ public:  // older Sun compilers need this (public definition of the following cl
 
   class FL_EXPORT Node {        // a node contains a list to all its entries
                                 // and all means to manage the tree structure
-    Node *child_, *next_;
+    Node *first_child_, *next_;
     union {                     // these two are mutually exclusive
       Node *parent_;            // top_ bit clear
-      RootNode *root_;          // top_ bit set
+      RootNode *root_node_;          // top_ bit set
     };
     char *path_;
     Entry *entry_;
@@ -265,7 +327,7 @@ public:  // older Sun compilers need this (public definition of the following cl
     Node *addChild( const char *path );
     void setParent( Node *parent );
     Node *parent() { return top_?0L:parent_; }
-    void setRoot(RootNode *r) { root_ = r; top_ = 1; }
+    void setRoot(RootNode *r) { root_node_ = r; top_ = 1; }
     RootNode *findRoot();
     char remove();
     char dirty();
@@ -290,7 +352,7 @@ public:  // older Sun compilers need this (public definition of the following cl
     Fl_Preferences *prefs_;
     char *filename_;
     char *vendor_, *application_;
-    Root root_;
+    Root root_type_;
   public:
     RootNode( Fl_Preferences *, Root root, const char *vendor, const char *application );
     RootNode( Fl_Preferences *, const char *path, const char *vendor, const char *application );
@@ -299,6 +361,8 @@ public:  // older Sun compilers need this (public definition of the following cl
     int read();
     int write();
     char getPath( char *path, int pathlen );
+    char *filename() { return filename_; }
+    Root root() { return root_type_; }
   };
   friend class RootNode;
 
diff --git fluid/Fl_Function_Type.cxx fluid/Fl_Function_Type.cxx
index 3c72886..f51570d 100644
--- fluid/Fl_Function_Type.cxx
+++ fluid/Fl_Function_Type.cxx
@@ -1530,7 +1530,7 @@ static void load_comments_preset(Fl_Preferences &menu) {
     "FLTK/Header" };
   int i;
   menu.set("n", 5);
-  Fl_Preferences db(Fl_Preferences::USER, "fltk.org", "fluid_comments");
+  Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments");
   for (i=0; i<5; i++) {
     menu.set(Fl_Preferences::Name(i), predefined_comment[i]);
     db.set(predefined_comment[i], comment_text[i]);
@@ -1545,7 +1545,7 @@ void Fl_Comment_Type::open() {
   const char *text = name();
   {
     int i=0, n=0;
-    Fl_Preferences menu(Fl_Preferences::USER, "fltk.org", "fluid_comments_menu");
+    Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu");
     comment_predefined->clear();
     comment_predefined->add("_Edit/Add current comment...");
     comment_predefined->add("_Edit/Remove last selection...");
@@ -1583,9 +1583,9 @@ void Fl_Comment_Type::open() {
             char *name = fl_strdup(xname);
             for (char*s=name;*s;s++) if (*s==':') *s = ';';
             int n;
-            Fl_Preferences db(Fl_Preferences::USER, "fltk.org", "fluid_comments");
+            Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments");
             db.set(name, comment_input->buffer()->text());
-            Fl_Preferences menu(Fl_Preferences::USER, "fltk.org", "fluid_comments_menu");
+            Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu");
             menu.get("n", n, 0);
             menu.set(Fl_Preferences::Name(n), name);
             menu.set("n", ++n);
@@ -1599,10 +1599,10 @@ void Fl_Comment_Type::open() {
           } else if (fl_choice("Are you sure that you want to delete the entry\n"
                                "\"%s\"\nfrom the database?", "Cancel", "Delete",
                                NULL, itempath)) {
-            Fl_Preferences db(Fl_Preferences::USER, "fltk.org", "fluid_comments");
+            Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments");
             db.deleteEntry(itempath);
             comment_predefined->remove(last_selected_item);
-            Fl_Preferences menu(Fl_Preferences::USER, "fltk.org", "fluid_comments_menu");
+            Fl_Preferences menu(Fl_Preferences::USER_L, "fltk.org", "fluid_comments_menu");
             int i, n;
             for (i=4, n=0; i<comment_predefined->size(); i++) {
               const Fl_Menu_Item *mi = comment_predefined->menu()+i;
@@ -1617,7 +1617,7 @@ void Fl_Comment_Type::open() {
           // load the selected comment from the database
           if (comment_predefined->item_pathname(itempath, 255)==0) {
             if (itempath[0]=='/') memmove(itempath, itempath+1, 255);
-            Fl_Preferences db(Fl_Preferences::USER, "fltk.org", "fluid_comments");
+            Fl_Preferences db(Fl_Preferences::USER_L, "fltk.org", "fluid_comments");
             char *text;
             db.get(itempath, text, "(no text found in data base)");
             comment_input->buffer()->text(text);
diff --git fluid/alignment_panel.fl fluid/alignment_panel.fl
index 716ebcc..af64ecb 100644
--- fluid/alignment_panel.fl
+++ fluid/alignment_panel.fl
@@ -1,5 +1,10 @@
 # data file for the Fltk User Interface Designer (fluid)
 version 1.0400
+i18n_type 1
+i18n_include {<libintl.h>}
+i18n_conditional FLTK_GETTEXT_FOUND
+i18n_function gettext
+i18n_static_function gettext_noop
 header_name {.h}
 code_name {.cxx}
 comment {//
@@ -35,7 +40,7 @@ decl {\#include <FL/Fl_Text_Buffer.H>} {public local
 decl {\#include <FL/Fl_Text_Display.H>} {public local
 }
 
-decl {\#include <FL/filename.H>} {selected public local
+decl {\#include <FL/filename.H>} {public local
 }
 
 decl {\#include <FL/Fl_Preferences.H>} {private global
@@ -323,7 +328,7 @@ Function {make_shell_window()} {open
         tooltip {save the design to the .fl file before running the command} xywh {82 39 136 19} down_box DOWN_BOX labelsize 12
       }
       Fl_Check_Button shell_writecode_button {
-        label {save source code}
+        label {save source code} selected
         tooltip {generate the source code and header file before running the command} xywh {82 59 120 19} down_box DOWN_BOX labelsize 12
       }
       Fl_Check_Button shell_writemsgs_button {
@@ -524,11 +529,11 @@ wShowZoomFactor->value(opt[Fl::OPTION_SHOW_SCALING][mode]);} {}
 }
 
 Function {readPrefs()} {
-  comment {read all preferences and refresh the GUI} private return_type void
+  comment {read all preferences and refresh the GUI} open private return_type void
 } {
   code {// read all preferences and refresh the GUI
 {
-  Fl_Preferences prefs(Fl_Preferences::SYSTEM, "fltk.org", "fltk");
+  Fl_Preferences prefs(Fl_Preferences::SYSTEM_L, "fltk.org", "fltk");
   Fl_Preferences opt_prefs(prefs, "options");
   opt_prefs.get("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][1], 2);
   opt_prefs.get("VisibleFocus", opt[Fl::OPTION_VISIBLE_FOCUS][1], 2);
@@ -539,7 +544,7 @@ Function {readPrefs()} {
   opt_prefs.get("ShowZoomFactor", opt[Fl::OPTION_SHOW_SCALING ][1], 2);
 }
 {
-  Fl_Preferences prefs(Fl_Preferences::USER, "fltk.org", "fltk");
+  Fl_Preferences prefs(Fl_Preferences::USER_L, "fltk.org", "fltk");
   Fl_Preferences opt_prefs(prefs, "options");
   opt_prefs.get("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][0], 2);
   opt_prefs.get("VisibleFocus", opt[Fl::OPTION_VISIBLE_FOCUS][0], 2);
@@ -553,11 +558,11 @@ refreshUI();} {}
 }
 
 Function {writePrefs()} {
-  comment {write all preferences using the array} private return_type void
+  comment {write all preferences using the array} open private return_type void
 } {
   code {// write all preferences using the array
 {
-  Fl_Preferences prefs(Fl_Preferences::SYSTEM, "fltk.org", "fltk");
+  Fl_Preferences prefs(Fl_Preferences::SYSTEM_L, "fltk.org", "fltk");
   Fl_Preferences opt_prefs(prefs, "options");
   if (opt[Fl::OPTION_ARROW_FOCUS][1]==2) opt_prefs.deleteEntry("ArrowFocus");
   else opt_prefs.set("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][1]);
@@ -575,7 +580,7 @@ Function {writePrefs()} {
   else opt_prefs.set("ShowZoomFactor", opt[Fl::OPTION_SHOW_SCALING][1]);
 }
 {
-  Fl_Preferences prefs(Fl_Preferences::USER, "fltk.org", "fltk");
+  Fl_Preferences prefs(Fl_Preferences::USER_L, "fltk.org", "fltk");
   Fl_Preferences opt_prefs(prefs, "options");
   if (opt[Fl::OPTION_ARROW_FOCUS][0]==2) opt_prefs.deleteEntry("ArrowFocus");
   else opt_prefs.set("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][0]);
@@ -594,7 +599,7 @@ Function {writePrefs()} {
 }} {}
 }
 
-Function {show_global_settings_window()} {return_type void
+Function {show_global_settings_window()} {open return_type void
 } {
   code {if (!global_settings_window)
   make_global_settings_window();
diff --git fluid/fluid.cxx fluid/fluid.cxx
index 1ed1ad9..aa0b2ee 100644
--- fluid/fluid.cxx
+++ fluid/fluid.cxx
@@ -72,7 +72,7 @@ Fl_Menu_Bar *main_menubar = NULL;
 Fl_Window *main_window;
 
 /// Fluid application preferences, allways accessible, will be flushed when app closes.
-Fl_Preferences  fluid_prefs(Fl_Preferences::USER, "fltk.org", "fluid");
+Fl_Preferences  fluid_prefs(Fl_Preferences::USER_L, "fltk.org", "fluid");
 
 /// Align widget position and size when designing, saved in app preferences and project file.
 int gridx = 5;
diff --git src/Fl_Preferences.cxx src/Fl_Preferences.cxx
index 0ac1c17..83fa3da 100644
--- src/Fl_Preferences.cxx
+++ src/Fl_Preferences.cxx
@@ -1,7 +1,7 @@
 //
 // Preferences methods for the Fast Light Tool Kit (FLTK).
 //
-// Copyright 2011-2020 by Bill Spitzak and others.
+// Copyright 2011-2022 by Bill Spitzak and others.
 // Copyright 2002-2010 by Matthias Melcher.
 //
 // This library is free software. Distribution and use rights are outlined in
@@ -34,6 +34,24 @@ char Fl_Preferences::uuidBuffer[40];
 Fl_Preferences *Fl_Preferences::runtimePrefs = 0;
 unsigned int Fl_Preferences::fileAccess_ = Fl_Preferences::ALL;
 
+static int clocale_snprintf(char *buffer, size_t buffer_size, const char *format, ...)
+{
+  va_list args;
+  va_start(args, format);
+  int retval = Fl::system_driver()->clocale_snprintf(buffer, buffer_size, format, args);
+  va_end(args);
+  return retval;
+}
+
+static int clocale_sscanf(const char *input, const char *format, ...)
+{
+  va_list args;
+  va_start(args, format);
+  int retval = Fl::system_driver()->clocale_sscanf(input, format, args);
+  va_end(args);
+  return retval;
+}
+
 /**
  Returns a UUID as generated by the system.
 
@@ -101,68 +119,108 @@ unsigned int Fl_Preferences::file_access()
 }
 
 /**
+ Determine the file name and path to preferences that would be openend with
+ these parameters.
+
+ Find the possible location of a preference file on disk without touching any
+ of the pathname componennts. This can be used to check if a preferneces file
+ already exists.
+
+ \param[out] buffer write the reulting path into this buffer
+ \param[in] buffer_size size of the `buffer` in bytes
+ \param[in] root can be \c USER_L or \c SYSTEM_L for user specific or system
+    wide preferences
+ \param[in] vendor unique text describing the company or author of this file,
+    must be a valid filepath segment
+ \param[in] application unique text describing the application, must be a
+    valid filepath segment
+ \return the input root value, or Fl_Preferences::UNKNOWN_ROOT_TYPE if the path
+    could not be determined.
+ \see Fl_Preferences( Root root, const char *vendor, const char *application )
+ */
+Fl_Preferences::Root Fl_Preferences::filename( char *buffer, size_t buffer_size, Root root, const char *vendor, const char *application )
+{
+  Root ret = UNKNOWN_ROOT_TYPE;
+  if (buffer && buffer_size>0) {
+    char *fn = Fl::system_driver()->preference_rootnode(NULL, root, vendor, application);
+    if (fn) {
+      fl_strlcpy(buffer, fn, buffer_size);
+      // FLTK always returns forward slashes in paths
+      { char *s; for ( s = buffer; *s; s++ ) if ( *s == '\\' ) *s = '/'; }
+      ret = root;
+    } else {
+      buffer[0] = 0;
+    }
+  }
+  return ret;
+}
+
+
+/**
  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.
- "research.matthiasm.com" or "fluid.fltk.org". The
- application argument can be the working title or final
- name of your application. Both vendor and
- application must be valid UNIX path segments and
- may contain forward slashes to create deeper file structures.
-
- A set of Preferences marked "run-time" exists exactly once 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.
-
- \note On \b Windows, the directory is constructed by querying the <i>Common AppData</i>
- or <i>AppData</i> key of the <tt>Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders</tt>
- registry entry. The filename and path is then constructed as <tt>\$(query)/\$(vendor)/\$(application).prefs</tt> .
- If the query call fails, data will be stored in RAM only and be lost when the app exits.
-
- \par In FLTK versions before 1.4.0, if querying the registry failed, preferences would be written to
+ The root argument is either `Fl_Preferences::USER_L`
+ or `Fl_Preferences::SYSTEM_L`.
+
+ This constructor creates the <i>base</i> instance for all following entries
+ and reads the database from disk into memory if it exists.
+ The vendor argument is a unique text string identifying the development team
+ or vendor of an application. A domain name or an EMail address (replacing
+ the '@' with a '.') are great unique names, e.g. "research.matthiasm.com" or
+ "fluid.fltk.org".
+ The application argument can be the working title or final name of your
+ application.
+ Both vendor and application must be valid UNIX path segments as they become
+ parts of the preferences file path and may contain forward slashes to create
+ deeper file structures.
+
+ \note On \b Windows, the directory is constructed by querying the
+ <i>Common AppData</i> or <i>AppData</i> key of the
+ <tt>Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders</tt>
+ registry entry.
+ The filename and path is then constructed as
+ <tt>\$(query)/\$(vendor)/\$(application).prefs</tt> .
+ If the query call fails, data will be stored in RAM only.
+ It will be lost when the app exits.
+
+ \par In FLTK versions before 1.4.0, if querying the registry failed,
+ preferences would be written to
  <tt>C:\\FLTK\\\$(vendor)\\\$(application).prefs</tt> .
 
- \note On \b Linux, the \c USER directory is constructed by reading \c $HOME . If \c $HOME is not set
- or not pointing to an existing directory, we are checking the path member of the passwd struct returned by
- \c getpwuid(getuid()) .  If all attempts fail, data will be stored in RAM only and be lost when the app exits.
- The filename and path is then constructed as <tt>\$(directory)/.fltk/\$(vendor)/\$(application).prefs</tt> .
- The \c SYSTEM directory is hardcoded as <tt>/etc/fltk/\$(vendor)/\$(application).prefs</tt> .
-
- \par In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path would be empty,
- generating <tt>\$(vendor)/\$(application).prefs</tt>, which was used relative to the current working directory.
-
- \note On \b macOS, the \c USER directory is constructed by reading \c $HOME . If \c $HOME is not set
- or not pointing to an existing directory, we check the path returned by \c NSHomeDirectory() , and
- finally checking the path member of the passwd struct returned by \c getpwuid(getuid()) .
+ \note On \b Linux, the \c USER directory is constructed by reading \c $HOME .
+ If \c $HOME is not set or not pointing to an existing directory, FLTK will
+ check the path member of the passwd struct returned by \c getpwuid(getuid()) .
+ If all attempts fail, data will be stored in RAM only and be lost when the
+ app exits.
+ The filename and path is then constructed as
+ <tt>\$(directory)/.fltk/\$(vendor)/\$(application).prefs</tt> .
+ The \c SYSTEM directory is hardcoded as
+ <tt>/etc/fltk/\$(vendor)/\$(application).prefs</tt> .
+
+ \par In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path
+ would be empty, generating <tt>\$(vendor)/\$(application).prefs</tt>, which
+ was used relative to the current working directory.
+
+ \note On \b macOS, the \c USER directory is constructed by reading \c $HOME .
+ If \c $HOME is not set or not pointing to an existing directory, we check the
+ path returned by \c NSHomeDirectory() , and finally checking the path member
+ of the passwd struct returned by \c getpwuid(getuid()) .
  If all attempts fail, data will be stored in RAM only and be lost when the app exits.
- The filename and path is then constructed as <tt>\$(directory)/Library/Preferences/\$(vendor)/\$(application).prefs</tt> .
- The \c SYSTEM directory is hardcoded as <tt>/Library/Preferences/\$(vendor)/\$(application).prefs</tt> .
+ The filename and path is then constructed as
+ <tt>\$(directory)/Library/Preferences/\$(vendor)/\$(application).prefs</tt> .
+ The \c SYSTEM directory is hardcoded as
+ <tt>/Library/Preferences/\$(vendor)/\$(application).prefs</tt> .
 
- \par In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path would be \c NULL ,
- generating <tt>\<null\>/Library/Preferences/\$(vendor)/\$(application).prefs</tt>, which would silently fail to
- create a preferences file.
+ \par In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path
+ would be \c NULL , generating
+ <tt>\<null\>/Library/Preferences/\$(vendor)/\$(application).prefs</tt>,
+ which would silently fail to create a preferences file.
 
- \param[in] root can be \c USER or \c SYSTEM for user specific or system wide preferences
+ \param[in] root can be \c USER_L or \c SYSTEM_L for user specific or system wide preferences
  \param[in] vendor unique text describing the company or author of this file, must be a valid filepath segment
  \param[in] application unique text describing the application, must be a valid filepath segment
 
- \todo (Matt) Before the release of 1.4.0, I want to make a further attempt to write a preferences file smarter. I
- plan to use a subgroup of the "runtime" preferences to store data and stay accessible until the application
- exits. Data would be stored under <tt>./\$(vendor)/\$(application).prefs</tt> in RAM, but not on disk.
-
- \todo (Matt) I want a way to access the type of the root preferences (SYSTEM, USER, MEMORY), and the state of
- the file access (OK, FILE_SYSTEM_FAIL, PERMISSION_FAIL, etc.), and probably the dirty() flag as well.
-
- \todo (Matt) Also, I need to explain runtime preferences.
-
- \todo (Matt) Lastly, I think I have to put short sample code in the Doxygen docs. The test app ist just not enough.
+ \see Fl_Preferences(Fl_Preferences *parent, const char *group) with parent set to NULL
  */
 Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application ) {
   node = new Node( "." );
@@ -203,22 +261,49 @@ Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *group ) {
 }
 
 /**
-   \brief Create or access a group of preferences using a name.
+ \brief Create or access a group of preferences using a name.
+
+ Parent should point to a previously created parent preferences group to
+ create a preferences hierarchy.
+
+ If `parent` is set to `NULL`, an unnamed database will be accessed that exists
+ only in local memory and is not associated with a file on disk. The root type
+ of this databse is set to `Fl_Preferences::MEMORY`.
+
+ * the memory database is \em not shared among multiple instances of the same app
+ * memory databses are \em not thread safe
+ * all data will be lost when the app quits
+
+ ```{.cpp}
+ void some_function() {
+   Fl_Preferences guide( NULL, "Guide" );
+   guide.set("answer", 42);
+ }
+ void other_function() {
+   int x;
+   Fl_Preferences guide( NULL, "Guide" );
+   guide.get("answer", x, -1);
+ }
+ ```
+
+ FLTK uses the memory database to manage plugins. See `Fl_Plugin`.
+
    \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.
+              If \p Parent is \p NULL, the new Preferences item refers to an
+              application internal database ("runtime prefs") 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. Runtime Prefs are \em not thread-safe.
    \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 (parent==NULL) {
     if (!runtimePrefs) {
       runtimePrefs = new Fl_Preferences();
       runtimePrefs->node = new Node( "." );
       runtimePrefs->rootNode = new RootNode( runtimePrefs );
-      runtimePrefs->node->setRoot(rootNode);
+      runtimePrefs->node->setRoot(runtimePrefs->rootNode);
     }
     parent = runtimePrefs;
   }
@@ -314,6 +399,35 @@ Fl_Preferences::~Fl_Preferences() {
 }
 
 /**
+ Return the file name and path to the Preferences file.
+
+ If the preferences have not changed or have not been flushed, the file
+ or directory may not have been created yet.
+
+ \param[out] buffer write the reulting path into this buffer
+ \param[in] buffer_size size of the `buffer` in bytes
+ \return the root type at creation type, or MEMORY for runtime prefs, it does
+    not return CORE or LOCALE flags.
+ */
+Fl_Preferences::Root Fl_Preferences::filename( char *buffer, size_t buffer_size)
+{
+  if (!buffer || buffer_size==0)
+    return UNKNOWN_ROOT_TYPE;
+  RootNode *rn = rootNode;
+  if (!rn)
+    return UNKNOWN_ROOT_TYPE;
+  if (rn->root()==MEMORY)
+    return MEMORY;
+  char *fn = rn->filename();
+  if (!fn)
+    return UNKNOWN_ROOT_TYPE;
+  fl_strlcpy(buffer, fn, buffer_size);
+  if (buffer[0]==0)
+    return UNKNOWN_ROOT_TYPE;
+  return (Root)(rn->root() & ROOT_MASK);
+}
+
+/**
    Returns the number of groups that are contained within a group.
 
    \return 0 for no groups at all
@@ -474,8 +588,16 @@ char Fl_Preferences::set( const char *key, int value ) {
  */
 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 );
+  if (v) {
+    if (rootNode->root() & C_LOCALE) {
+      clocale_sscanf(v, "%g", &value);
+    } else {
+      value = (float)atof(v);
+    }
+  } else {
+    value = defaultValue;
+  }
+  return ( v != NULL );
 }
 
 /**
@@ -489,7 +611,11 @@ char Fl_Preferences::get( const char *key, float &value, float defaultValue ) {
  \return 0 if setting the value failed
  */
 char Fl_Preferences::set( const char *key, float value ) {
-  sprintf( nameBuffer, "%g", value );
+  if (rootNode->root() & C_LOCALE) {
+    clocale_snprintf( nameBuffer, sizeof(nameBuffer), "%g", value );
+  } else {
+    snprintf( nameBuffer, sizeof(nameBuffer), "%g", value );
+  }
   node->set( key, nameBuffer );
   return 1;
 }
@@ -506,7 +632,11 @@ char Fl_Preferences::set( const char *key, float value ) {
  \return 0 if setting the value failed
  */
 char Fl_Preferences::set( const char *key, float value, int precision ) {
-  sprintf( nameBuffer, "%.*g", precision, value );
+  if (rootNode->root() & C_LOCALE) {
+    clocale_snprintf( nameBuffer, sizeof(nameBuffer), "%.*g", precision, value );
+  } else {
+    snprintf( nameBuffer, sizeof(nameBuffer), "%.*g", precision, value );
+  }
   node->set( key, nameBuffer );
   return 1;
 }
@@ -523,8 +653,16 @@ char Fl_Preferences::set( const char *key, float value, int precision ) {
  */
 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 );
+  if (v) {
+    if (rootNode->root() & C_LOCALE) {
+      clocale_sscanf(v, "%lg", &value);
+    } else {
+      value = atof(v);
+    }
+  } else {
+    value = defaultValue;
+  }
+  return ( v != NULL );
 }
 
 /**
@@ -538,7 +676,11 @@ char Fl_Preferences::get( const char *key, double &value, double defaultValue )
  \return 0 if setting the value failed
  */
 char Fl_Preferences::set( const char *key, double value ) {
-  sprintf( nameBuffer, "%g", value );
+  if (rootNode->root() & C_LOCALE) {
+    clocale_snprintf( nameBuffer, sizeof(nameBuffer), "%lg", value );
+  } else {
+    snprintf( nameBuffer, sizeof(nameBuffer), "%lg", value );
+  }
   node->set( key, nameBuffer );
   return 1;
 }
@@ -555,7 +697,11 @@ char Fl_Preferences::set( const char *key, double value ) {
  \return 0 if setting the value failed
  */
 char Fl_Preferences::set( const char *key, double value, int precision ) {
-  sprintf( nameBuffer, "%.*g", precision, value );
+  if (rootNode->root() & C_LOCALE) {
+    clocale_snprintf( nameBuffer, sizeof(nameBuffer), "%.*lg", precision, value );
+  } else {
+    snprintf( nameBuffer, sizeof(nameBuffer), "%.*lg", precision, value );
+  }
   node->set( key, nameBuffer );
   return 1;
 }
@@ -695,19 +841,17 @@ static void *decodeHex( const char *src, int &size ) {
 }
 
 /**
- 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.
+ Reads a binary entry from the group, ancoded in hexadecimal blocks.
 
  \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] defaultValue default value
  \param[in] defaultSize size of default value array
- \param[in] maxSize maximum length of value
+ \param[in] maxSize maximum length of value, to receive the number of bytes
+    read, use the function below instead.
  \return 0 if the default value was used
 
- \todo maxSize should receive the number of bytes that were read.
+ \see Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int *maxSize )
  */
 char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int maxSize ) {
   const char *v = node->get( key );
@@ -724,6 +868,45 @@ char Fl_Preferences::get( const char *key, void *data, const void *defaultValue,
 }
 
 /**
+ Reads a binary entry from the group, ancoded in hexadecimal blocks.
+ A binary (not hex) default value can 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 and returns the
+ actual number of bytes 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[inout] maxSize maximum length of value and actual number of bytes set
+ \return 0 if the default value was used
+ */
+char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int *maxSize ) {
+  if (!maxSize || !data)
+    return -1;
+  int capacity = *maxSize;
+  const char *v = node->get( key );
+  if ( v ) {
+    int nFound;
+    void *w = decodeHex( v, nFound );
+    int nWrite = (nFound>capacity) ? capacity : nFound;
+    memmove( data, w,  nWrite);
+    free( w );
+    *maxSize = nWrite;
+    return 1;
+  }
+  if ( defaultValue ) {
+    int nWrite = (defaultSize>capacity) ? capacity : defaultSize;
+    memmove( data, defaultValue, nWrite );
+    *maxSize = nWrite;
+  } else {
+    *maxSize = 0;
+  }
+  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
@@ -836,13 +1019,40 @@ char Fl_Preferences::getUserdataPath( char *path, int pathlen ) {
 }
 
 /**
- 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.
+ Writes preferences to disk if they were modified.
+
+ This method can be used to verify that writing a preferences file went well.
+ Deleting the base preferences object will also write the contents of the
+ database to disk.
+
+ \return -1 if anything went wrong, i.e. file could not be opened, permissions
+    blocked writing, etc.
+ \return 0 if the file was written to disk. This does not check if the disk ran
+    out of space and the file is truncated.
+ \return 1 if there no data written to the database and no write attempt
+    to disk was made.
  */
-void Fl_Preferences::flush() {
-  if ( rootNode && node->dirty() )
-    rootNode->write();
+int Fl_Preferences::flush() {
+  int ret = dirty();
+  if (ret!=1)
+    return ret;
+  return rootNode->write();
+}
+
+/**
+ Check if there were changes to the database that need to be written to disk.
+
+ \return 1 if the database will be written to disk by `flush` or destructor.
+ \return 0 if the databse is unchanged since the last write operation.
+ \return -1 f there is an internal database error.
+ */
+int Fl_Preferences::dirty() {
+  Node *n = node;
+  while (n && n->parent())
+    n = n->parent();
+  if (!n)
+    return -1;
+  return n->dirty();
 }
 
 //-----------------------------------------------------------------------------
@@ -909,7 +1119,7 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, Root root, const char
   filename_(0L),
   vendor_(0L),
   application_(0L),
-  root_(root)
+  root_type_(root)
 {
   char *filename = Fl::system_driver()->preference_rootnode(prefs, root, vendor, application);
   filename_    = filename ? fl_strdup(filename) : 0L;
@@ -925,7 +1135,7 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, con
   filename_(0L),
   vendor_(0L),
   application_(0L),
-  root_(Fl_Preferences::USER)
+  root_type_(Fl_Preferences::USER)
 {
 
   if (!vendor)
@@ -950,7 +1160,7 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs )
   filename_(0L),
   vendor_(0L),
   application_(0L),
-  root_(Fl_Preferences::USER)
+  root_type_(Fl_Preferences::MEMORY)
 {
 }
 
@@ -978,15 +1188,15 @@ Fl_Preferences::RootNode::~RootNode() {
 int Fl_Preferences::RootNode::read() {
   if (!filename_)   // RUNTIME preferences, or filename could not be created
     return -1;
-  if ( (root_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_READ_OK) ) {
+  if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_READ_OK) ) {
     prefs_->node->clearDirtyFlags();
     return -1;
   }
-  if ( ((root_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::USER) && !(fileAccess_ & Fl_Preferences::USER_READ_OK) ) {
+  if ( ((root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::USER) && !(fileAccess_ & Fl_Preferences::USER_READ_OK) ) {
     prefs_->node->clearDirtyFlags();
     return -1;
   }
-  if ( ((root_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::SYSTEM) && !(fileAccess_ & Fl_Preferences::SYSTEM_READ_OK) ) {
+  if ( ((root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::SYSTEM) && !(fileAccess_ & Fl_Preferences::SYSTEM_READ_OK) ) {
     prefs_->node->clearDirtyFlags();
     return -1;
   }
@@ -1027,11 +1237,11 @@ int Fl_Preferences::RootNode::read() {
 int Fl_Preferences::RootNode::write() {
   if (!filename_)   // RUNTIME preferences, or filename could not be created
     return -1;
-  if ( (root_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_WRITE_OK) )
+  if ( (root_type_ & Fl_Preferences::CORE) && !(fileAccess_ & Fl_Preferences::CORE_WRITE_OK) )
     return -1;
-  if ( ((root_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::USER) && !(fileAccess_ & Fl_Preferences::USER_WRITE_OK) )
+  if ( ((root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::USER) && !(fileAccess_ & Fl_Preferences::USER_WRITE_OK) )
     return -1;
-  if ( ((root_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::SYSTEM) && !(fileAccess_ & Fl_Preferences::SYSTEM_WRITE_OK) )
+  if ( ((root_type_&Fl_Preferences::ROOT_MASK)==Fl_Preferences::SYSTEM) && !(fileAccess_ & Fl_Preferences::SYSTEM_WRITE_OK) )
     return -1;
   fl_make_path_for_file(filename_);
   FILE *f = fl_fopen( filename_, "wb" );
@@ -1111,10 +1321,10 @@ char Fl_Preferences::RootNode::getPath( char *path, int pathlen ) {
 }
 
 // create a node that represents a group
-// - path must be a single word, prferable alnum(), dot and underscore only. Space is ok.
+// - path must be a single word, preferable alnum(), dot and underscore only. Space is ok.
 Fl_Preferences::Node::Node( const char *path ) {
   if ( path ) path_ = fl_strdup( path ); else path_ = 0;
-  child_ = 0; next_ = 0; parent_ = 0;
+  first_child_ = 0; next_ = 0; parent_ = 0;
   entry_ = 0;
   nEntry_ = NEntry_ = 0;
   dirty_ = 0;
@@ -1125,12 +1335,12 @@ Fl_Preferences::Node::Node( const char *path ) {
 }
 
 void Fl_Preferences::Node::deleteAllChildren() {
-  Node *nx;
-  for ( Node *nd = child_; nd; nd = nx ) {
-    nx = nd->next_;
-    delete nd;
+  Node *next_node = NULL;
+  for ( Node *current_node = first_child_; current_node; current_node = next_node ) {
+    next_node = current_node->next_;
+    delete current_node;
   }
-  child_ = 0L;
+  first_child_ = NULL;
   dirty_ = 1;
   updateIndex();
 }
@@ -1139,16 +1349,16 @@ 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;
+        ::free( entry_[i].name );
+        entry_[i].name = NULL;
       }
       if ( entry_[i].value ) {
-        free( entry_[i].value );
-        entry_[i].value = 0L;
+        ::free( entry_[i].value );
+        entry_[i].value = NULL;
       }
     }
     free( entry_ );
-    entry_ = 0L;
+    entry_ = NULL;
     nEntry_ = 0;
     NEntry_ = 0;
   }
@@ -1157,22 +1367,22 @@ void Fl_Preferences::Node::deleteAllEntries() {
 
 // delete this and all depending nodes
 Fl_Preferences::Node::~Node() {
+  next_ = NULL;
+  parent_ = NULL;
   deleteAllChildren();
   deleteAllEntries();
   deleteIndex();
   if ( path_ ) {
-    free( path_ );
-    path_ = 0L;
+    ::free( path_ );
+    path_ = NULL;
   }
-  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;
+  if ( first_child_ && first_child_->dirty() ) return 1;
   return 0;
 }
 
@@ -1181,7 +1391,7 @@ void Fl_Preferences::Node::clearDirtyFlags() {
   Fl_Preferences::Node *nd = this;
   while (nd) {
     nd->dirty_ = 0;
-    if ( nd->child_ ) nd->child_->clearDirtyFlags();
+    if ( nd->first_child_ ) nd->first_child_->clearDirtyFlags();
     nd = nd->next_;
   }
 }
@@ -1214,7 +1424,7 @@ int Fl_Preferences::Node::write( FILE *f ) {
     else
       fprintf( f, "%s\n", entry_[i].name );
   }
-  if ( child_ ) child_->write( f );
+  if ( first_child_ ) first_child_->write( f );
   dirty_ = 0;
   return 0;
 }
@@ -1222,8 +1432,8 @@ int Fl_Preferences::Node::write( FILE *f ) {
 // set the parent node and create the full path
 void Fl_Preferences::Node::setParent( Node *pn ) {
   parent_ = pn;
-  next_ = pn->child_;
-  pn->child_ = this;
+  next_ = pn->first_child_;
+  pn->first_child_ = this;
   sprintf( nameBuffer, "%s/%s", pn->path_, path_ );
   free( path_ );
   path_ = fl_strdup( nameBuffer );
@@ -1234,7 +1444,7 @@ Fl_Preferences::RootNode *Fl_Preferences::Node::findRoot() {
   Node *n = this;
   do {
     if (n->top_)
-      return n->root_;
+      return n->root_node_;
     n = n->parent();
   } while (n);
   return 0L;
@@ -1346,7 +1556,7 @@ Fl_Preferences::Node *Fl_Preferences::Node::find( const char *path ) {
       return this;
     if ( path[ len ] == '/' ) {
       Node *nd;
-      for ( nd = child_; nd; nd = nd->next_ ) {
+      for ( nd = first_child_; nd; nd = nd->next_ ) {
         Node *nn = nd->find( path );
         if ( nn ) return nn;
       }
@@ -1392,7 +1602,7 @@ Fl_Preferences::Node *Fl_Preferences::Node::search( const char *path, int offset
     if ( len > 0 && path[ len ] == 0 )
       return this;
     if ( len <= 0 || path[ len ] == '/' ) {
-      for ( Node *nd = child_; nd; nd = nd->next_ ) {
+      for ( Node *nd = first_child_; nd; nd = nd->next_ ) {
         Node *nn = nd->search( path, offset );
         if ( nn ) return nn;
       }
@@ -1408,7 +1618,7 @@ int Fl_Preferences::Node::nChildren() {
     return nIndex_;
   } else {
     int cnt = 0;
-    for ( Node *nd = child_; nd; nd = nd->next_ )
+    for ( Node *nd = first_child_; nd; nd = nd->next_ )
       cnt++;
     return cnt;
   }
@@ -1444,7 +1654,7 @@ Fl_Preferences::Node *Fl_Preferences::Node::childNode( int ix ) {
     int n = nChildren();
     ix = n - ix -1;
     Node *nd;
-    for ( nd = child_; nd; nd = nd->next_ ) {
+    for ( nd = first_child_; nd; nd = nd->next_ ) {
       if ( !ix-- ) break;
       if ( !nd ) break;
     }
@@ -1454,23 +1664,25 @@ Fl_Preferences::Node *Fl_Preferences::Node::childNode( int ix ) {
 
 // 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;
+  Node *nd = NULL, *np = NULL;
+  Node *parent_node = parent();
+  if ( parent_node ) {
+    nd = parent_node->first_child_; np = NULL;
     for ( ; nd; np = nd, nd = nd->next_ ) {
       if ( nd == this ) {
         if ( np )
-          np->next_ = nd->next_;
+          np->next_ = next_;
         else
-          parent()->child_ = nd->next_;
+          parent_node->first_child_ = next_;
+        next_ = NULL;
         break;
       }
     }
-    parent()->dirty_ = 1;
-    parent()->updateIndex();
+    parent_node->dirty_ = 1;
+    parent_node->updateIndex();
   }
   delete this;
-  return ( nd != 0 );
+  return ( nd != NULL );
 }
 
 void Fl_Preferences::Node::createIndex() {
@@ -1482,7 +1694,7 @@ void Fl_Preferences::Node::createIndex() {
   }
   Node *nd;
   int i = 0;
-  for (nd = child_; nd; nd = nd->next_, i++) {
+  for (nd = first_child_; nd; nd = nd->next_, i++) {
     index_[n-i-1] = nd;
   }
   nIndex_ = n;
@@ -1494,9 +1706,10 @@ void Fl_Preferences::Node::updateIndex() {
 }
 
 void Fl_Preferences::Node::deleteIndex() {
-  if (index_) free(index_);
+  if (index_)
+    ::free(index_);
+  index_ = NULL;
   NIndex_ = nIndex_ = 0;
-  index_ = 0;
   indexed_ = 0;
 }
 
diff --git src/Fl_System_Driver.H src/Fl_System_Driver.H
index 0ba196d..37b5486 100644
--- src/Fl_System_Driver.H
+++ src/Fl_System_Driver.H
@@ -128,6 +128,8 @@ public:
   virtual unsigned utf8from_mb(char* dst, unsigned dstlen, const char* src, unsigned srclen);
   // implement to shield fprintf() from locale changes in decimal point
   virtual int clocale_printf(FILE *output, const char *format, va_list args);
+  virtual int clocale_snprintf(char *output, size_t output_size, const char *format, va_list args);
+  virtual int clocale_sscanf(const char *input, const char *format, va_list args);
   // implement functions telling whether a key is pressed
   virtual int event_key(int) {return 0;}
   virtual int get_key(int) {return 0;}
diff --git src/Fl_System_Driver.cxx src/Fl_System_Driver.cxx
index 71a60c0..75bbf3e 100644
--- src/Fl_System_Driver.cxx
+++ src/Fl_System_Driver.cxx
@@ -404,6 +404,14 @@ int Fl_System_Driver::clocale_printf(FILE *output, const char *format, va_list a
   return vfprintf(output, format, args);
 }
 
+int Fl_System_Driver::clocale_snprintf(char *output, size_t output_size, const char *format, va_list args) {
+  return vsnprintf(output, output_size, format, args);
+}
+
+int Fl_System_Driver::clocale_sscanf(const char *input, const char *format, va_list args) {
+  return vsscanf(input, format, args);
+}
+
 int Fl_System_Driver::filename_expand(char *to,int tolen, const char *from) {
   char *temp = new char[tolen];
   strlcpy(temp,from, tolen);
diff --git src/Fl_cocoa.mm src/Fl_cocoa.mm
index 0b8fc34..52680a8 100644
--- src/Fl_cocoa.mm
+++ src/Fl_cocoa.mm
@@ -4528,7 +4528,10 @@ int Fl_Darwin_System_Driver::calc_mac_os_version() {
   return fl_mac_os_version;
 }
 
-char *Fl_Darwin_System_Driver::preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root,
+/*
+ Note: `prefs` can be NULL!
+ */
+char *Fl_Darwin_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, Fl_Preferences::Root root,
                                                    const char *vendor, const char *application)
 {
   static char *filename = 0L;
diff --git src/drivers/Android/Fl_Android_System_Driver.H src/drivers/Android/Fl_Android_System_Driver.H
index 585535d..865b62a 100644
--- src/drivers/Android/Fl_Android_System_Driver.H
+++ src/drivers/Android/Fl_Android_System_Driver.H
@@ -69,6 +69,8 @@ public:
   virtual unsigned utf8to_mb(const char *src, unsigned srclen, char *dst, unsigned dstlen);
   virtual unsigned utf8from_mb(char *dst, unsigned dstlen, const char *src, unsigned srclen);
   virtual int clocale_printf(FILE *output, const char *format, va_list args);
+  virtual int clocale_snprintf(char *output, size_t output_size, const char *format, va_list args);
+  virtual int clocale_sscanf(const char *input, const char *format, va_list args);
   // these 2 are in Fl_get_key_win32.cxx
   virtual int event_key(int k);
   virtual int get_key(int k);
diff --git src/drivers/Android/Fl_Android_System_Driver.cxx src/drivers/Android/Fl_Android_System_Driver.cxx
index f164a7c..2e94396 100644
--- src/drivers/Android/Fl_Android_System_Driver.cxx
+++ src/drivers/Android/Fl_Android_System_Driver.cxx
@@ -487,6 +487,14 @@ int Fl_WinAPI_System_Driver::clocale_printf(FILE *output, const char *format, va
   return retval;
 }
 
+int Fl_WinAPI_System_Driver::clocale_snprintf(char *output, size_t output_size, const char *format, va_list args) {
+  //... write me
+}
+
+int Fl_WinAPI_System_Driver::clocale_sscanf(const char *input, const char *format, va_list args) {
+  //... write me
+}
+
 int Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list,
                                            int (*sort)(struct dirent **, struct dirent **),
                                            char *errmsg, int errmsg_sz ) {
@@ -834,6 +842,9 @@ void Fl_WinAPI_System_Driver::newUUID(char *uuidBuffer)
   }
 }
 
+/*
+ Note: `prefs` can be NULL!
+ */
 char *Fl_WinAPI_System_Driver::preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor,
                                                   const char *application)
 {
diff --git src/drivers/Darwin/Fl_Darwin_System_Driver.H src/drivers/Darwin/Fl_Darwin_System_Driver.H
index 7d076a9..8a2e3b7 100644
--- src/drivers/Darwin/Fl_Darwin_System_Driver.H
+++ src/drivers/Darwin/Fl_Darwin_System_Driver.H
@@ -45,6 +45,8 @@ public:
   virtual int single_arg(const char *arg);
   virtual int arg_and_value(const char *name, const char *value);
   virtual int clocale_printf(FILE *output, const char *format, va_list args);
+  virtual int clocale_snprintf(char *output, size_t output_size, const char *format, va_list args);
+  virtual int clocale_sscanf(const char *input, const char *format, va_list args);
   static void *get_carbon_function(const char *name);
   static int calc_mac_os_version(); // computes the fl_mac_os_version global variable
   static unsigned short *compute_macKeyLookUp();
diff --git src/drivers/Darwin/Fl_Darwin_System_Driver.cxx src/drivers/Darwin/Fl_Darwin_System_Driver.cxx
index 511b3b0..dad46df 100644
--- src/drivers/Darwin/Fl_Darwin_System_Driver.cxx
+++ src/drivers/Darwin/Fl_Darwin_System_Driver.cxx
@@ -116,10 +116,15 @@ int Fl_Darwin_System_Driver::arg_and_value(const char *name, const char *value)
   return strcmp(name, "NSDocumentRevisionsDebugMode") == 0;
 }
 
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+static locale_t postscript_locale = NULL;
+#endif
+
 int Fl_Darwin_System_Driver::clocale_printf(FILE *output, const char *format, va_list args) {
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
   if (fl_mac_os_version >= 100400) {
-    static locale_t postscript_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
+    if (!postscript_locale)
+      postscript_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
     return vfprintf_l(output, postscript_locale, format, args);
   }
 #endif
@@ -130,6 +135,37 @@ int Fl_Darwin_System_Driver::clocale_printf(FILE *output, const char *format, va
   return retval;
 }
 
+int Fl_Darwin_System_Driver::clocale_snprintf(char *output, size_t output_size, const char *format, va_list args) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+  if (fl_mac_os_version >= 100400) {
+    if (!postscript_locale)
+      postscript_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
+    return vsnprintf_l(output, output_size, postscript_locale, format, args);
+  }
+#endif
+  char *saved_locale = setlocale(LC_NUMERIC, NULL);
+  setlocale(LC_NUMERIC, "C");
+  int retval = vsnprintf(output, output_size, format, args);
+  setlocale(LC_NUMERIC, saved_locale);
+  return retval;
+}
+
+int Fl_Darwin_System_Driver::clocale_sscanf(const char *input, const char *format, va_list args) {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
+  if (fl_mac_os_version >= 100400) {
+    if (!postscript_locale)
+      postscript_locale = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0);
+    return vsscanf_l(input, postscript_locale, format, args);
+  }
+#endif
+  char *saved_locale = setlocale(LC_NUMERIC, NULL);
+  setlocale(LC_NUMERIC, "C");
+  int retval = vsscanf(input, format, args);
+  setlocale(LC_NUMERIC, saved_locale);
+  return retval;
+}
+
+
 /* Returns the address of a Carbon function after dynamically loading the Carbon library if needed.
  Supports old Mac OS X versions that may use a couple of Carbon calls:
  GetKeys used by OS X 10.3 or before (in Fl::get_key())
diff --git src/drivers/WinAPI/Fl_WinAPI_System_Driver.H src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
index a31d524..60dca9d 100644
--- src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
+++ src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
@@ -67,6 +67,8 @@ public:
   virtual unsigned utf8to_mb(const char *src, unsigned srclen, char *dst, unsigned dstlen);
   virtual unsigned utf8from_mb(char *dst, unsigned dstlen, const char *src, unsigned srclen);
   virtual int clocale_printf(FILE *output, const char *format, va_list args);
+  virtual int clocale_snprintf(char *output, size_t output_size, const char *format, va_list args);
+  virtual int clocale_sscanf(const char *input, const char *format, va_list args);
   // these 2 are in Fl_get_key_win32.cxx
   virtual int event_key(int k);
   virtual int get_key(int k);
diff --git src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
index a869b8c..e88ac8e 100644
--- src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
+++ src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
@@ -455,9 +455,14 @@ unsigned Fl_WinAPI_System_Driver::utf8from_mb(char *dst, unsigned dstlen, const
   return ret;
 }
 
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 /*Visual Studio 2005*/)
+static _locale_t c_locale = NULL;
+#endif
+
 int Fl_WinAPI_System_Driver::clocale_printf(FILE *output, const char *format, va_list args) {
 #if defined(_MSC_VER) && (_MSC_VER >= 1400 /*Visual Studio 2005*/)
-  static _locale_t c_locale = _create_locale(LC_NUMERIC, "C");
+  if (!c_locale)
+    c_locale = _create_locale(LC_NUMERIC, "C");
   int retval = _vfprintf_l(output, format, c_locale, args);
 #else
   char *saved_locale = setlocale(LC_NUMERIC, NULL);
@@ -468,6 +473,35 @@ int Fl_WinAPI_System_Driver::clocale_printf(FILE *output, const char *format, va
   return retval;
 }
 
+int Fl_WinAPI_System_Driver::clocale_snprintf(char *output, size_t output_size, const char *format, va_list args) {
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 /*Visual Studio 2005*/)
+  if (!c_locale)
+    c_locale = _create_locale(LC_NUMERIC, "C");
+  int retval = _vsnprintf_l(output, output_size, format, c_locale, args);
+#else
+  char *saved_locale = setlocale(LC_NUMERIC, NULL);
+  setlocale(LC_NUMERIC, "C");
+  int retval = vsnprintf(output, output_size, format, args);
+  setlocale(LC_NUMERIC, saved_locale);
+#endif
+  return retval;
+}
+
+int Fl_WinAPI_System_Driver::clocale_sscanf(const char *input, const char *format, va_list args) {
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 /*Visual Studio 2005*/)
+  if (!c_locale)
+    c_locale = _create_locale(LC_NUMERIC, "C");
+  int retval = _vsscanf_l(input, format, c_locale, args);
+#else
+  char *saved_locale = setlocale(LC_NUMERIC, NULL);
+  setlocale(LC_NUMERIC, "C");
+  int retval = vsscanf(input, format, args);
+  setlocale(LC_NUMERIC, saved_locale);
+#endif
+  return retval;
+}
+
+
 int Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list,
                                            int (*sort)(struct dirent **, struct dirent **),
                                            char *errmsg, int errmsg_sz) {
@@ -814,7 +848,10 @@ void Fl_WinAPI_System_Driver::newUUID(char *uuidBuffer)
   }
 }
 
-char *Fl_WinAPI_System_Driver::preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor,
+/*
+ Note: `prefs` can be NULL!
+ */
+char *Fl_WinAPI_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, Fl_Preferences::Root root, const char *vendor,
                                                   const char *application)
 {
 #  define FLPREFS_RESOURCE      "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
diff --git src/drivers/X11/Fl_X11_System_Driver.H src/drivers/X11/Fl_X11_System_Driver.H
index 146a48e..891c14d 100644
--- src/drivers/X11/Fl_X11_System_Driver.H
+++ src/drivers/X11/Fl_X11_System_Driver.H
@@ -31,6 +31,8 @@ public:
   virtual void display_arg(const char *arg);
   virtual int XParseGeometry(const char*, int*, int*, unsigned int*, unsigned int*);
   virtual int clocale_printf(FILE *output, const char *format, va_list args);
+  virtual int clocale_snprintf(char *output, size_t output_size, const char *format, va_list args);
+  virtual int clocale_sscanf(const char *input, const char *format, va_list args);
   // these 2 are in Fl_get_key.cxx
   virtual int event_key(int k);
   virtual int get_key(int k);
diff --git src/drivers/X11/Fl_X11_System_Driver.cxx src/drivers/X11/Fl_X11_System_Driver.cxx
index fcd1d2d..2f3656a 100644
--- src/drivers/X11/Fl_X11_System_Driver.cxx
+++ src/drivers/X11/Fl_X11_System_Driver.cxx
@@ -79,10 +79,15 @@ Fl_System_Driver *Fl_System_Driver::newSystemDriver()
   return new Fl_X11_System_Driver();
 }
 
+#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
+static locale_t c_locale = NULL;
+#endif
+
 
 int Fl_X11_System_Driver::clocale_printf(FILE *output, const char *format, va_list args) {
 #if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
-  static locale_t c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
+  if (!c_locale)
+    c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
   locale_t previous_locale = uselocale(c_locale);
   int retval = vfprintf(output, format, args);
   uselocale(previous_locale);
@@ -95,6 +100,35 @@ int Fl_X11_System_Driver::clocale_printf(FILE *output, const char *format, va_li
   return retval;
 }
 
+int Fl_X11_System_Driver::clocale_snprintf(char *output, size_t output_size, const char *format, va_list args) {
+#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
+  if (!c_locale)
+    c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
+  locale_t previous_locale = uselocale(c_locale);
+  int retval = vsnprintf(output, output_size, format, args);
+  uselocale(previous_locale);
+#else
+  char *saved_locale = setlocale(LC_NUMERIC, NULL);
+  setlocale(LC_NUMERIC, "C");
+  int retval = vsnprintf(output, output_size, format, args);
+  setlocale(LC_NUMERIC, saved_locale);
+#endif
+}
+
+int Fl_X11_System_Driver::clocale_sscanf(const char *input, const char *format, va_list args) {
+#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700
+  if (!c_locale)
+    c_locale = newlocale(LC_NUMERIC_MASK, "C", duplocale(LC_GLOBAL_LOCALE));
+  locale_t previous_locale = uselocale(c_locale);
+  int retval = vsscanf(input, format, args);
+  uselocale(previous_locale);
+#else
+  char *saved_locale = setlocale(LC_NUMERIC, NULL);
+  setlocale(LC_NUMERIC, "C");
+  int retval = vsscanf(input, format, args);
+  setlocale(LC_NUMERIC, saved_locale);
+#endif
+}
 
 // Find a program in the path...
 static char *path_find(const char *program, char *filename, int filesize) {
@@ -415,7 +449,10 @@ void Fl_X11_System_Driver::newUUID(char *uuidBuffer)
           b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
 }
 
-char *Fl_X11_System_Driver::preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor,
+/*
+ Note: `prefs` can be NULL!
+ */
+char *Fl_X11_System_Driver::preference_rootnode(Fl_Preferences * /*prefs*/, Fl_Preferences::Root root, const char *vendor,
                                                 const char *application)
 {
   static char *filename = 0L;
diff --git test/blocks.cxx test/blocks.cxx
index 2128d8a..a08e6ed 100644
--- test/blocks.cxx
+++ test/blocks.cxx
@@ -497,7 +497,7 @@ class BlockWindow : public Fl_Double_Window {
 };
 
 
-Fl_Preferences  BlockWindow::prefs_(Fl_Preferences::USER, "fltk.org", "blocks");
+Fl_Preferences  BlockWindow::prefs_(Fl_Preferences::USER_L, "fltk.org", "blocks");
 
 
 int main(int argc, char *argv[]) {
diff --git test/preferences.fl test/preferences.fl
index e7d5b17..ef2cc5d 100644
--- test/preferences.fl
+++ test/preferences.fl
@@ -45,7 +45,7 @@ Function {saveAndCloseWindowCB( Fl_Widget*, void* )} {open private return_type v
 Fl::delete_widget(myWindow);} {}
 }
 
-Function {} {open return_type int
+Function {} {open selected return_type int
 } {
   Fl_Window myWindow {
     label {My Preferences}
@@ -78,7 +78,7 @@ Function {} {open return_type int
           xywh {0 0 100 20}
         }
         MenuItem {} {
-          label {p.m.} selected
+          label {p.m.}
           xywh {0 0 100 20}
         }
       }
@@ -206,10 +206,32 @@ int intValue;
 char buffer[80];
 double doubleValue;
 
-Fl_Preferences app( Fl_Preferences::USER, project, application );
+char path[ FL_PATH_MAX ];
+Fl_Preferences::Root root = 
+  Fl_Preferences::filename(path, FL_PATH_MAX, Fl_Preferences::USER_L, project, application);
+if (root == Fl_Preferences::UNKNOWN_ROOT_TYPE) {
+  printf("Location of future Preferences file not found.\\n");
+} else {
+  printf("Preferences file will be located at:\\n%s\\n", path);
+}
+
+Fl_Preferences app( Fl_Preferences::USER_L, project, application );
 
-  char path[ FL_PATH_MAX ];
-  app.getUserdataPath( path, sizeof(path) );
+  root = app.filename(path, FL_PATH_MAX);
+  if (root == Fl_Preferences::UNKNOWN_ROOT_TYPE) {
+    printf("Location of app Preferences file not found.\\n");
+  } else if (root == Fl_Preferences::MEMORY) {
+    printf("App Preferences are memory mapped.\\n");
+  } else {
+    printf("App Preferences file is actually located at:\\n%s\\n", path);
+  }
+  
+  app.getUserdataPath( path, sizeof(path) );  
+  if (path[0]) {
+    printf("Preferences user data directory is located at:\\n%s\\n", path);
+  } else {
+    printf("Location of Preferences user data directory not found.\\n");
+  }
 
   Fl_Preferences bed( app, "Bed" );
     bed.get( "alarm", buffer, "8:00", 79 );
@@ -278,7 +300,7 @@ Fl_Preferences app( Fl_Preferences::USER, project, application );
 
 Function {writePrefs()} {open return_type void
 } {
-  code {Fl_Preferences app( Fl_Preferences::USER, project, application );
+  code {Fl_Preferences app( Fl_Preferences::USER_L, project, application );
 
   Fl_Preferences bed( app, "Bed" );
 
diff --git test/sudoku.cxx test/sudoku.cxx
index 28437fb..9884c11 100644
--- test/sudoku.cxx
+++ test/sudoku.cxx
@@ -625,7 +625,7 @@ SudokuCell::handle(int event) {
 
 // Sudoku class globals...
 Fl_Help_Dialog  *Sudoku::help_dialog_ = (Fl_Help_Dialog *)0;
-Fl_Preferences  Sudoku::prefs_(Fl_Preferences::USER, "fltk.org", "sudoku");
+Fl_Preferences  Sudoku::prefs_(Fl_Preferences::USER_L, "fltk.org", "sudoku");
 
 
 // Create a Sudoku game window...
Direct Link to Message ]
 
     
Previous Message ]Next Message ]
 
 

Comments are owned by the poster. All other content is copyright 1998-2024 by Bill Spitzak and others. This project is hosted by The FLTK Team. Please report site problems to 'erco@seriss.com'.