Browse Source

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.
pull/376/head
Matthias Melcher 3 years ago committed by GitHub
parent
commit
09eff7243a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 132
      FL/Fl_Preferences.H
  2. 14
      fluid/Fl_Function_Type.cxx
  3. 23
      fluid/alignment_panel.fl
  4. 2
      fluid/fluid.cxx
  5. 467
      src/Fl_Preferences.cxx
  6. 2
      src/Fl_System_Driver.H
  7. 8
      src/Fl_System_Driver.cxx
  8. 5
      src/Fl_cocoa.mm
  9. 2
      src/drivers/Android/Fl_Android_System_Driver.H
  10. 11
      src/drivers/Android/Fl_Android_System_Driver.cxx
  11. 2
      src/drivers/Darwin/Fl_Darwin_System_Driver.H
  12. 38
      src/drivers/Darwin/Fl_Darwin_System_Driver.cxx
  13. 2
      src/drivers/WinAPI/Fl_WinAPI_System_Driver.H
  14. 41
      src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx
  15. 2
      src/drivers/X11/Fl_X11_System_Driver.H
  16. 41
      src/drivers/X11/Fl_X11_System_Driver.cxx
  17. 2
      test/blocks.cxx
  18. 34
      test/preferences.fl
  19. 2
      test/sudoku.cxx

132
FL/Fl_Preferences.H

@ -1,7 +1,7 @@
// //
// Preferences implementation for the Fast Light Tool Kit (FLTK). // 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 // 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 // the file "COPYING" which should have been included with this file. If this
@ -24,31 +24,74 @@
# include "Fl_Export.H" # include "Fl_Export.H"
/** /**
\brief Fl_Preferences provides methods to store user \brief Fl_Preferences store user settings between application starts.
settings between application starts.
Fl_Preferences are similar to the Registry on Windows and Preferences on MacOS,
It is similar to the providing a simple method to store customisable user settings between app
Registry on Windows and Preferences on MacOS, and provides a launches, i.e. the previous window position or a history of previously
simple configuration mechanism for UNIX. used documents.
Fl_Preferences uses a hierarchy to store data. It Preferences are organized in a hierarchy of groups. Every group can contain
bundles similar data into groups and manages entries in these more groups and any number of kay/value pairs. Keys can be text strings
groups as name/value pairs. containing ASCII letters, digits, periods, and underscores. Forward slashes
in a key name are treated as subgroups, i.e the key 'window/width' would
Preferences are stored in text files that can be edited actually refere to the key 'width' inside the group 'window'.
manually. The file format is easy to read and relatively
forgiving. Preferences files are the same on all platforms. User Keys have usually a unique name within their group. Duplicate kays are
comments in preference files are preserved. Filenames are unique possible though and can beaccessed using the index based functions.
for each application by using a vendor/application naming
scheme. The user must provide default values for all entries to A value should be an ASCII string. Control characters and utf8 sequences are
ensure proper operation should preferences be corrupted or not stores as octal values. Long strings will wrap at the line ending and will be
yet exist. reassembled when reading the file back.
Entries can be of any length. However, the size of each Many shortcuts exist to set and get numerical values and binary data.
preferences file should be kept small for performance
reasons. One application can have multiple preferences files. Preferences are stored in text files that can be edited manually if needed.
Extensive binary data however should be stored in separate The file format is easy to read and relatively forgiving. Preferences files
files: see \a Fl_Preferences::getUserdataPath() . 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 \note Starting with FLTK 1.3, preference databases are expected to
be in UTF-8 encoding. Previous databases were stored in the be in UTF-8 encoding. Previous databases were stored in the
@ -59,6 +102,14 @@
the preferences files has changed slightly. Please see the preferences files has changed slightly. Please see
Fl_Preferences::Fl_Preferences(Root, const char*, const char*) Fl_Preferences::Fl_Preferences(Root, const char*, const char*)
for details. 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 { class FL_EXPORT Fl_Preferences {
@ -67,12 +118,17 @@ public:
Define the scope of the preferences. Define the scope of the preferences.
*/ */
enum Root { enum Root {
SYSTEM = 0, ///< Preferences are used system-wide UNKNOWN_ROOT_TYPE = -1, ///< Returned if storage could not be determined.
USER, ///< Preferences apply only to the current user 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 ROOT_MASK = 0xFF, ///< masks for the values above
CORE = 0x100, ///< OR'd by FLTK to read and write core library preferences and options CORE = 0x100, ///< OR'd by FLTK to read and write core library preferences and options
CORE_SYSTEM = CORE|SYSTEM, 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 void file_access(unsigned int flags);
static unsigned int file_access(); 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( Root root, const char *vendor, const char *application );
Fl_Preferences( const char *path, 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 ); Fl_Preferences( ID id );
virtual ~Fl_Preferences(); 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. /** Return an ID that can later be reused to open more references to this dataset.
*/ */
ID id() { return (ID)node; } 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, 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 );
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 maxSize );
char get( const char *entry, void *value, const void *defaultValue, int defaultSize, int *size );
int size( const char *entry ); int size( const char *entry );
char getUserdataPath( char *path, int pathlen ); char getUserdataPath( char *path, int pathlen );
void flush(); int flush();
int dirty();
// char export( const char *filename, Type fileFormat ); // char export( const char *filename, Type fileFormat );
// char import( const char *filename ); // 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 class FL_EXPORT Node { // a node contains a list to all its entries
// and all means to manage the tree structure // and all means to manage the tree structure
Node *child_, *next_; Node *first_child_, *next_;
union { // these two are mutually exclusive union { // these two are mutually exclusive
Node *parent_; // top_ bit clear Node *parent_; // top_ bit clear
RootNode *root_; // top_ bit set RootNode *root_node_; // top_ bit set
}; };
char *path_; char *path_;
Entry *entry_; Entry *entry_;
@ -265,7 +327,7 @@ public: // older Sun compilers need this (public definition of the following cl
Node *addChild( const char *path ); Node *addChild( const char *path );
void setParent( Node *parent ); void setParent( Node *parent );
Node *parent() { return top_?0L: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(); RootNode *findRoot();
char remove(); char remove();
char dirty(); char dirty();
@ -290,7 +352,7 @@ public: // older Sun compilers need this (public definition of the following cl
Fl_Preferences *prefs_; Fl_Preferences *prefs_;
char *filename_; char *filename_;
char *vendor_, *application_; char *vendor_, *application_;
Root root_; Root root_type_;
public: public:
RootNode( Fl_Preferences *, Root root, const char *vendor, const char *application ); RootNode( Fl_Preferences *, Root root, const char *vendor, const char *application );
RootNode( Fl_Preferences *, const char *path, 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 read();
int write(); int write();
char getPath( char *path, int pathlen ); char getPath( char *path, int pathlen );
char *filename() { return filename_; }
Root root() { return root_type_; }
}; };
friend class RootNode; friend class RootNode;

14
fluid/Fl_Function_Type.cxx

@ -1530,7 +1530,7 @@ static void load_comments_preset(Fl_Preferences &menu) {
"FLTK/Header" }; "FLTK/Header" };
int i; int i;
menu.set("n", 5); 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++) { for (i=0; i<5; i++) {
menu.set(Fl_Preferences::Name(i), predefined_comment[i]); menu.set(Fl_Preferences::Name(i), predefined_comment[i]);
db.set(predefined_comment[i], comment_text[i]); db.set(predefined_comment[i], comment_text[i]);
@ -1545,7 +1545,7 @@ void Fl_Comment_Type::open() {
const char *text = name(); const char *text = name();
{ {
int i=0, n=0; 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->clear();
comment_predefined->add("_Edit/Add current comment..."); comment_predefined->add("_Edit/Add current comment...");
comment_predefined->add("_Edit/Remove last selection..."); comment_predefined->add("_Edit/Remove last selection...");
@ -1583,9 +1583,9 @@ void Fl_Comment_Type::open() {
char *name = fl_strdup(xname); char *name = fl_strdup(xname);
for (char*s=name;*s;s++) if (*s==':') *s = ';'; for (char*s=name;*s;s++) if (*s==':') *s = ';';
int n; 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()); 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.get("n", n, 0);
menu.set(Fl_Preferences::Name(n), name); menu.set(Fl_Preferences::Name(n), name);
menu.set("n", ++n); 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" } else if (fl_choice("Are you sure that you want to delete the entry\n"
"\"%s\"\nfrom the database?", "Cancel", "Delete", "\"%s\"\nfrom the database?", "Cancel", "Delete",
NULL, itempath)) { 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); db.deleteEntry(itempath);
comment_predefined->remove(last_selected_item); 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; int i, n;
for (i=4, n=0; i<comment_predefined->size(); i++) { for (i=4, n=0; i<comment_predefined->size(); i++) {
const Fl_Menu_Item *mi = comment_predefined->menu()+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 // load the selected comment from the database
if (comment_predefined->item_pathname(itempath, 255)==0) { if (comment_predefined->item_pathname(itempath, 255)==0) {
if (itempath[0]=='/') memmove(itempath, itempath+1, 255); 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; char *text;
db.get(itempath, text, "(no text found in data base)"); db.get(itempath, text, "(no text found in data base)");
comment_input->buffer()->text(text); comment_input->buffer()->text(text);

23
fluid/alignment_panel.fl

@ -1,5 +1,10 @@
# data file for the Fltk User Interface Designer (fluid) # data file for the Fltk User Interface Designer (fluid)
version 1.0400 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} header_name {.h}
code_name {.cxx} code_name {.cxx}
comment {// 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/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 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 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 { 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 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 { Fl_Check_Button shell_writemsgs_button {
@ -524,11 +529,11 @@ wShowZoomFactor->value(opt[Fl::OPTION_SHOW_SCALING][mode]);} {}
} }
Function {readPrefs()} { 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 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"); Fl_Preferences opt_prefs(prefs, "options");
opt_prefs.get("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][1], 2); opt_prefs.get("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][1], 2);
opt_prefs.get("VisibleFocus", opt[Fl::OPTION_VISIBLE_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); 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"); Fl_Preferences opt_prefs(prefs, "options");
opt_prefs.get("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][0], 2); opt_prefs.get("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][0], 2);
opt_prefs.get("VisibleFocus", opt[Fl::OPTION_VISIBLE_FOCUS][0], 2); opt_prefs.get("VisibleFocus", opt[Fl::OPTION_VISIBLE_FOCUS][0], 2);
@ -553,11 +558,11 @@ refreshUI();} {}
} }
Function {writePrefs()} { 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 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"); Fl_Preferences opt_prefs(prefs, "options");
if (opt[Fl::OPTION_ARROW_FOCUS][1]==2) opt_prefs.deleteEntry("ArrowFocus"); if (opt[Fl::OPTION_ARROW_FOCUS][1]==2) opt_prefs.deleteEntry("ArrowFocus");
else opt_prefs.set("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][1]); 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]); 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"); Fl_Preferences opt_prefs(prefs, "options");
if (opt[Fl::OPTION_ARROW_FOCUS][0]==2) opt_prefs.deleteEntry("ArrowFocus"); if (opt[Fl::OPTION_ARROW_FOCUS][0]==2) opt_prefs.deleteEntry("ArrowFocus");
else opt_prefs.set("ArrowFocus", opt[Fl::OPTION_ARROW_FOCUS][0]); 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) code {if (!global_settings_window)
make_global_settings_window(); make_global_settings_window();

2
fluid/fluid.cxx

@ -72,7 +72,7 @@ Fl_Menu_Bar *main_menubar = NULL;
Fl_Window *main_window; Fl_Window *main_window;
/// Fluid application preferences, allways accessible, will be flushed when app closes. /// 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. /// Align widget position and size when designing, saved in app preferences and project file.
int gridx = 5; int gridx = 5;

467
src/Fl_Preferences.cxx

@ -1,7 +1,7 @@
// //
// Preferences methods for the Fast Light Tool Kit (FLTK). // 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. // Copyright 2002-2010 by Matthias Melcher.
// //
// This library is free software. Distribution and use rights are outlined in // 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; Fl_Preferences *Fl_Preferences::runtimePrefs = 0;
unsigned int Fl_Preferences::fileAccess_ = Fl_Preferences::ALL; 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. Returns a UUID as generated by the system.
@ -100,69 +118,109 @@ unsigned int Fl_Preferences::file_access()
return fileAccess_; return fileAccess_;
} }
/**
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 The constructor creates a group that manages name/value pairs and
child groups. Groups are ready for reading and writing at any time. child groups. Groups are ready for reading and writing at any time.
The root argument is either Fl_Preferences::USER The root argument is either `Fl_Preferences::USER_L`
or Fl_Preferences::SYSTEM. or `Fl_Preferences::SYSTEM_L`.
This constructor creates the <i>base</i> instance for all This constructor creates the <i>base</i> instance for all following entries
following entries and reads existing databases into memory. The and reads the database from disk into memory if it exists.
vendor argument is a unique text string identifying the The vendor argument is a unique text string identifying the development team
development team or vendor of an application. A domain name or or vendor of an application. A domain name or an EMail address (replacing
an EMail address are great unique names, e.g. the '@' with a '.') are great unique names, e.g. "research.matthiasm.com" or
"research.matthiasm.com" or "fluid.fltk.org". The "fluid.fltk.org".
application argument can be the working title or final The application argument can be the working title or final name of your
name of your application. Both vendor and application.
application must be valid UNIX path segments and Both vendor and application must be valid UNIX path segments as they become
may contain forward slashes to create deeper file structures. parts of the preferences file path 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 \note On \b Windows, the directory is constructed by querying the
volatile information. FLTK uses it to register plugins at run-time. <i>Common AppData</i> or <i>AppData</i> key of the
<tt>Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders</tt>
\note On \b Windows, the directory is constructed by querying the <i>Common AppData</i> registry entry.
or <i>AppData</i> key of the <tt>Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders</tt> The filename and path is then constructed as
registry entry. The filename and path is then constructed as <tt>\$(query)/\$(vendor)/\$(application).prefs</tt> . <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. 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
\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> . <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 \note On \b Linux, the \c USER directory is constructed by reading \c $HOME .
or not pointing to an existing directory, we are checking the path member of the passwd struct returned by If \c $HOME is not set or not pointing to an existing directory, FLTK will
\c getpwuid(getuid()) . If all attempts fail, data will be stored in RAM only and be lost when the app exits. check the path member of the passwd struct returned by \c getpwuid(getuid()) .
The filename and path is then constructed as <tt>\$(directory)/.fltk/\$(vendor)/\$(application).prefs</tt> . If all attempts fail, data will be stored in RAM only and be lost when the
The \c SYSTEM directory is hardcoded as <tt>/etc/fltk/\$(vendor)/\$(application).prefs</tt> . app exits.
The filename and path is then constructed as
\par In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path would be empty, <tt>\$(directory)/.fltk/\$(vendor)/\$(application).prefs</tt> .
generating <tt>\$(vendor)/\$(application).prefs</tt>, which was used relative to the current working directory. The \c SYSTEM directory is hardcoded as
<tt>/etc/fltk/\$(vendor)/\$(application).prefs</tt> .
\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 \par In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path
finally checking the path member of the passwd struct returned by \c getpwuid(getuid()) . 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. 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 filename and path is then constructed as
The \c SYSTEM directory is hardcoded as <tt>/Library/Preferences/\$(vendor)/\$(application).prefs</tt> . <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 , \par In FLTK versions before 1.4.0, if \c $HOME was not set, the \c USER path
generating <tt>\<null\>/Library/Preferences/\$(vendor)/\$(application).prefs</tt>, which would silently fail to would be \c NULL , generating
create a preferences file. <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] 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 \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 \see Fl_Preferences(Fl_Preferences *parent, const char *group) with parent set to NULL
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.
*/ */
Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application ) { Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application ) {
node = new Node( "." ); 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. \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 If \p Parent is \p NULL, the new Preferences item refers to an
database which exists only once, and remains in RAM only until the application internal database ("runtime prefs") which exists only
application quits. This database is used to manage plugins and other once, and remains in RAM only until the application quits.
data indexes by strings. 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 \param[in] group a group name that is used as a key into the database
\see Fl_Preferences( Fl_Preferences&, const char *group ) \see Fl_Preferences( Fl_Preferences&, const char *group )
*/ */
Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group ) { Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *group ) {
if (parent==0) { if (parent==NULL) {
if (!runtimePrefs) { if (!runtimePrefs) {
runtimePrefs = new Fl_Preferences(); runtimePrefs = new Fl_Preferences();
runtimePrefs->node = new Node( "." ); runtimePrefs->node = new Node( "." );
runtimePrefs->rootNode = new RootNode( runtimePrefs ); runtimePrefs->rootNode = new RootNode( runtimePrefs );
runtimePrefs->node->setRoot(rootNode); runtimePrefs->node->setRoot(runtimePrefs->rootNode);
} }
parent = runtimePrefs; parent = runtimePrefs;
} }
@ -313,6 +398,35 @@ Fl_Preferences::~Fl_Preferences() {
rootNode = 0L; rootNode = 0L;
} }
/**
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. Returns the number of groups that are contained within a group.
@ -474,8 +588,16 @@ char Fl_Preferences::set( const char *key, int value ) {
*/ */
char Fl_Preferences::get( const char *key, float &value, float defaultValue ) { char Fl_Preferences::get( const char *key, float &value, float defaultValue ) {
const char *v = node->get( key ); const char *v = node->get( key );
value = v ? (float)atof( v ) : defaultValue; if (v) {
return ( v != 0 ); 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 \return 0 if setting the value failed
*/ */
char Fl_Preferences::set( const char *key, float value ) { 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 ); node->set( key, nameBuffer );
return 1; return 1;
} }
@ -506,7 +632,11 @@ char Fl_Preferences::set( const char *key, float value ) {
\return 0 if setting the value failed \return 0 if setting the value failed
*/ */
char Fl_Preferences::set( const char *key, float value, int precision ) { 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 ); node->set( key, nameBuffer );
return 1; 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 ) { char Fl_Preferences::get( const char *key, double &value, double defaultValue ) {
const char *v = node->get( key ); const char *v = node->get( key );
value = v ? atof( v ) : defaultValue; if (v) {
return ( v != 0 ); 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 \return 0 if setting the value failed
*/ */
char Fl_Preferences::set( const char *key, double value ) { 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 ); node->set( key, nameBuffer );
return 1; return 1;
} }
@ -555,7 +697,11 @@ char Fl_Preferences::set( const char *key, double value ) {
\return 0 if setting the value failed \return 0 if setting the value failed
*/ */
char Fl_Preferences::set( const char *key, double value, int precision ) { 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 ); node->set( key, nameBuffer );
return 1; 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 Reads a binary entry from the group, ancoded in hexadecimal blocks.
supplied. The return value indicates if the value was available
(non-zero) or the default was used (0).
'maxSize' is the maximum length of text that will be read.
\param[in] key name of entry \param[in] key name of entry
\param[out] data value returned from preferences or default value if none was set \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] 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 \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 ) { char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int maxSize ) {
const char *v = node->get( key ); const char *v = node->get( key );
@ -723,6 +867,45 @@ char Fl_Preferences::get( const char *key, void *data, const void *defaultValue,
return 0; return 0;
} }
/**
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 Reads an entry from the group. A default value must be
supplied. The return value indicates if the value was available supplied. The return value indicates if the value was available
@ -836,13 +1019,40 @@ char Fl_Preferences::getUserdataPath( char *path, int pathlen ) {
} }
/** /**
Writes all preferences to disk. This function works only with Writes preferences to disk if they were modified.
the base preferences group. This function is rarely used as
deleting the base preferences flushes automatically. 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() { int Fl_Preferences::flush() {
if ( rootNode && node->dirty() ) int ret = dirty();
rootNode->write(); 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), filename_(0L),
vendor_(0L), vendor_(0L),
application_(0L), application_(0L),
root_(root) root_type_(root)
{ {
char *filename = Fl::system_driver()->preference_rootnode(prefs, root, vendor, application); char *filename = Fl::system_driver()->preference_rootnode(prefs, root, vendor, application);
filename_ = filename ? fl_strdup(filename) : 0L; filename_ = filename ? fl_strdup(filename) : 0L;
@ -925,7 +1135,7 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs, const char *path, con
filename_(0L), filename_(0L),
vendor_(0L), vendor_(0L),
application_(0L), application_(0L),
root_(Fl_Preferences::USER) root_type_(Fl_Preferences::USER)
{ {
if (!vendor) if (!vendor)
@ -950,7 +1160,7 @@ Fl_Preferences::RootNode::RootNode( Fl_Preferences *prefs )
filename_(0L), filename_(0L),
vendor_(0L), vendor_(0L),
application_(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() { int Fl_Preferences::RootNode::read() {
if (!filename_) // RUNTIME preferences, or filename could not be created if (!filename_) // RUNTIME preferences, or filename could not be created
return -1; 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(); prefs_->node->clearDirtyFlags();
return -1; 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(); prefs_->node->clearDirtyFlags();
return -1; 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(); prefs_->node->clearDirtyFlags();
return -1; return -1;
} }
@ -1027,11 +1237,11 @@ int Fl_Preferences::RootNode::read() {
int Fl_Preferences::RootNode::write() { int Fl_Preferences::RootNode::write() {
if (!filename_) // RUNTIME preferences, or filename could not be created if (!filename_) // RUNTIME preferences, or filename could not be created
return -1; 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; 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; 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; return -1;
fl_make_path_for_file(filename_); fl_make_path_for_file(filename_);
FILE *f = fl_fopen( filename_, "wb" ); 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 // 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 ) { Fl_Preferences::Node::Node( const char *path ) {
if ( path ) path_ = fl_strdup( path ); else path_ = 0; if ( path ) path_ = fl_strdup( path ); else path_ = 0;
child_ = 0; next_ = 0; parent_ = 0; first_child_ = 0; next_ = 0; parent_ = 0;
entry_ = 0; entry_ = 0;
nEntry_ = NEntry_ = 0; nEntry_ = NEntry_ = 0;
dirty_ = 0; dirty_ = 0;
@ -1125,12 +1335,12 @@ Fl_Preferences::Node::Node( const char *path ) {
} }
void Fl_Preferences::Node::deleteAllChildren() { void Fl_Preferences::Node::deleteAllChildren() {
Node *nx; Node *next_node = NULL;
for ( Node *nd = child_; nd; nd = nx ) { for ( Node *current_node = first_child_; current_node; current_node = next_node ) {
nx = nd->next_; next_node = current_node->next_;
delete nd; delete current_node;
} }
child_ = 0L; first_child_ = NULL;
dirty_ = 1; dirty_ = 1;
updateIndex(); updateIndex();
} }
@ -1139,16 +1349,16 @@ void Fl_Preferences::Node::deleteAllEntries() {
if ( entry_ ) { if ( entry_ ) {
for ( int i = 0; i < nEntry_; i++ ) { for ( int i = 0; i < nEntry_; i++ ) {
if ( entry_[i].name ) { if ( entry_[i].name ) {
free( entry_[i].name ); ::free( entry_[i].name );
entry_[i].name = 0L; entry_[i].name = NULL;
} }
if ( entry_[i].value ) { if ( entry_[i].value ) {
free( entry_[i].value ); ::free( entry_[i].value );
entry_[i].value = 0L; entry_[i].value = NULL;
} }
} }
free( entry_ ); free( entry_ );
entry_ = 0L; entry_ = NULL;
nEntry_ = 0; nEntry_ = 0;
NEntry_ = 0; NEntry_ = 0;
} }
@ -1157,22 +1367,22 @@ void Fl_Preferences::Node::deleteAllEntries() {
// delete this and all depending nodes // delete this and all depending nodes
Fl_Preferences::Node::~Node() { Fl_Preferences::Node::~Node() {
next_ = NULL;
parent_ = NULL;
deleteAllChildren(); deleteAllChildren();
deleteAllEntries(); deleteAllEntries();
deleteIndex(); deleteIndex();
if ( path_ ) { if ( path_ ) {
free( path_ ); ::free( path_ );
path_ = 0L; path_ = NULL;
} }
next_ = 0L;
parent_ = 0L;
} }
// recursively check if any entry is dirty (was changed after loading a fresh prefs file) // recursively check if any entry is dirty (was changed after loading a fresh prefs file)
char Fl_Preferences::Node::dirty() { char Fl_Preferences::Node::dirty() {
if ( dirty_ ) return 1; if ( dirty_ ) return 1;
if ( next_ && next_->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; return 0;
} }
@ -1181,7 +1391,7 @@ void Fl_Preferences::Node::clearDirtyFlags() {
Fl_Preferences::Node *nd = this; Fl_Preferences::Node *nd = this;
while (nd) { while (nd) {
nd->dirty_ = 0; nd->dirty_ = 0;
if ( nd->child_ ) nd->child_->clearDirtyFlags(); if ( nd->first_child_ ) nd->first_child_->clearDirtyFlags();
nd = nd->next_; nd = nd->next_;
} }
} }
@ -1214,7 +1424,7 @@ int Fl_Preferences::Node::write( FILE *f ) {
else else
fprintf( f, "%s\n", entry_[i].name ); fprintf( f, "%s\n", entry_[i].name );
} }
if ( child_ ) child_->write( f ); if ( first_child_ ) first_child_->write( f );
dirty_ = 0; dirty_ = 0;
return 0; return 0;
} }
@ -1222,8 +1432,8 @@ int Fl_Preferences::Node::write( FILE *f ) {
// set the parent node and create the full path // set the parent node and create the full path
void Fl_Preferences::Node::setParent( Node *pn ) { void Fl_Preferences::Node::setParent( Node *pn ) {
parent_ = pn; parent_ = pn;
next_ = pn->child_; next_ = pn->first_child_;
pn->child_ = this; pn->first_child_ = this;
sprintf( nameBuffer, "%s/%s", pn->path_, path_ ); sprintf( nameBuffer, "%s/%s", pn->path_, path_ );
free( path_ ); free( path_ );
path_ = fl_strdup( nameBuffer ); path_ = fl_strdup( nameBuffer );
@ -1234,7 +1444,7 @@ Fl_Preferences::RootNode *Fl_Preferences::Node::findRoot() {
Node *n = this; Node *n = this;
do { do {
if (n->top_) if (n->top_)
return n->root_; return n->root_node_;
n = n->parent(); n = n->parent();
} while (n); } while (n);
return 0L; return 0L;
@ -1346,7 +1556,7 @@ Fl_Preferences::Node *Fl_Preferences::Node::find( const char *path ) {
return this; return this;
if ( path[ len ] == '/' ) { if ( path[ len ] == '/' ) {
Node *nd; Node *nd;
for ( nd = child_; nd; nd = nd->next_ ) { for ( nd = first_child_; nd; nd = nd->next_ ) {
Node *nn = nd->find( path ); Node *nn = nd->find( path );
if ( nn ) return nn; 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 ) if ( len > 0 && path[ len ] == 0 )
return this; return this;
if ( len <= 0 || path[ len ] == '/' ) { 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 ); Node *nn = nd->search( path, offset );
if ( nn ) return nn; if ( nn ) return nn;
} }
@ -1408,7 +1618,7 @@ int Fl_Preferences::Node::nChildren() {
return nIndex_; return nIndex_;
} else { } else {
int cnt = 0; int cnt = 0;
for ( Node *nd = child_; nd; nd = nd->next_ ) for ( Node *nd = first_child_; nd; nd = nd->next_ )
cnt++; cnt++;
return cnt; return cnt;
} }
@ -1444,7 +1654,7 @@ Fl_Preferences::Node *Fl_Preferences::Node::childNode( int ix ) {
int n = nChildren(); int n = nChildren();
ix = n - ix -1; ix = n - ix -1;
Node *nd; Node *nd;
for ( nd = child_; nd; nd = nd->next_ ) { for ( nd = first_child_; nd; nd = nd->next_ ) {
if ( !ix-- ) break; if ( !ix-- ) break;
if ( !nd ) 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) // remove myself from the list and delete me (and all children)
char Fl_Preferences::Node::remove() { char Fl_Preferences::Node::remove() {
Node *nd = 0, *np; Node *nd = NULL, *np = NULL;
if ( parent() ) { Node *parent_node = parent();
nd = parent()->child_; np = 0L; if ( parent_node ) {
nd = parent_node->first_child_; np = NULL;
for ( ; nd; np = nd, nd = nd->next_ ) { for ( ; nd; np = nd, nd = nd->next_ ) {
if ( nd == this ) { if ( nd == this ) {
if ( np ) if ( np )
np->next_ = nd->next_; np->next_ = next_;
else else
parent()->child_ = nd->next_; parent_node->first_child_ = next_;
next_ = NULL;
break; break;
} }
} }
parent()->dirty_ = 1; parent_node->dirty_ = 1;
parent()->updateIndex(); parent_node->updateIndex();
} }
delete this; delete this;
return ( nd != 0 ); return ( nd != NULL );
} }
void Fl_Preferences::Node::createIndex() { void Fl_Preferences::Node::createIndex() {
@ -1482,7 +1694,7 @@ void Fl_Preferences::Node::createIndex() {
} }
Node *nd; Node *nd;
int i = 0; 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; index_[n-i-1] = nd;
} }
nIndex_ = n; nIndex_ = n;
@ -1494,9 +1706,10 @@ void Fl_Preferences::Node::updateIndex() {
} }
void Fl_Preferences::Node::deleteIndex() { void Fl_Preferences::Node::deleteIndex() {
if (index_) free(index_); if (index_)
::free(index_);
index_ = NULL;
NIndex_ = nIndex_ = 0; NIndex_ = nIndex_ = 0;
index_ = 0;
indexed_ = 0; indexed_ = 0;
} }

2
src/Fl_System_Driver.H

@ -128,6 +128,8 @@ public:
virtual unsigned utf8from_mb(char* dst, unsigned dstlen, const char* src, unsigned srclen); virtual unsigned utf8from_mb(char* dst, unsigned dstlen, const char* src, unsigned srclen);
// implement to shield fprintf() from locale changes in decimal point // 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_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 // implement functions telling whether a key is pressed
virtual int event_key(int) {return 0;} virtual int event_key(int) {return 0;}
virtual int get_key(int) {return 0;} virtual int get_key(int) {return 0;}

8
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); 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) { int Fl_System_Driver::filename_expand(char *to,int tolen, const char *from) {
char *temp = new char[tolen]; char *temp = new char[tolen];
strlcpy(temp,from, tolen); strlcpy(temp,from, tolen);

5
src/Fl_cocoa.mm

@ -4528,7 +4528,10 @@ int Fl_Darwin_System_Driver::calc_mac_os_version() {
return fl_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) const char *vendor, const char *application)
{ {
static char *filename = 0L; static char *filename = 0L;

2
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 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 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_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 // these 2 are in Fl_get_key_win32.cxx
virtual int event_key(int k); virtual int event_key(int k);
virtual int get_key(int k); virtual int get_key(int k);

11
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; 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 Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list,
int (*sort)(struct dirent **, struct dirent **), int (*sort)(struct dirent **, struct dirent **),
char *errmsg, int errmsg_sz ) { 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, char *Fl_WinAPI_System_Driver::preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor,
const char *application) const char *application)
{ {

2
src/drivers/Darwin/Fl_Darwin_System_Driver.H

@ -45,6 +45,8 @@ public:
virtual int single_arg(const char *arg); virtual int single_arg(const char *arg);
virtual int arg_and_value(const char *name, const char *value); 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_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 void *get_carbon_function(const char *name);
static int calc_mac_os_version(); // computes the fl_mac_os_version global variable static int calc_mac_os_version(); // computes the fl_mac_os_version global variable
static unsigned short *compute_macKeyLookUp(); static unsigned short *compute_macKeyLookUp();

38
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; 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) { 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 MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
if (fl_mac_os_version >= 100400) { 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); return vfprintf_l(output, postscript_locale, format, args);
} }
#endif #endif
@ -130,6 +135,37 @@ int Fl_Darwin_System_Driver::clocale_printf(FILE *output, const char *format, va
return retval; 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. /* 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: 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()) GetKeys used by OS X 10.3 or before (in Fl::get_key())

2
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 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 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_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 // these 2 are in Fl_get_key_win32.cxx
virtual int event_key(int k); virtual int event_key(int k);
virtual int get_key(int k); virtual int get_key(int k);

41
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; 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) { 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*/) #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); int retval = _vfprintf_l(output, format, c_locale, args);
#else #else
char *saved_locale = setlocale(LC_NUMERIC, NULL); 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; 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 Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list,
int (*sort)(struct dirent **, struct dirent **), int (*sort)(struct dirent **, struct dirent **),
char *errmsg, int errmsg_sz) { 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) const char *application)
{ {
# define FLPREFS_RESOURCE "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders" # define FLPREFS_RESOURCE "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"

2
src/drivers/X11/Fl_X11_System_Driver.H

@ -31,6 +31,8 @@ public:
virtual void display_arg(const char *arg); virtual void display_arg(const char *arg);
virtual int XParseGeometry(const char*, int*, int*, unsigned int*, unsigned int*); 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_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 // these 2 are in Fl_get_key.cxx
virtual int event_key(int k); virtual int event_key(int k);
virtual int get_key(int k); virtual int get_key(int k);

41
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(); 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) { int Fl_X11_System_Driver::clocale_printf(FILE *output, const char *format, va_list args) {
#if defined(__linux__) && defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700 #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); locale_t previous_locale = uselocale(c_locale);
int retval = vfprintf(output, format, args); int retval = vfprintf(output, format, args);
uselocale(previous_locale); uselocale(previous_locale);
@ -95,6 +100,35 @@ int Fl_X11_System_Driver::clocale_printf(FILE *output, const char *format, va_li
return retval; 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... // Find a program in the path...
static char *path_find(const char *program, char *filename, int filesize) { 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]); 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) const char *application)
{ {
static char *filename = 0L; static char *filename = 0L;

2
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[]) { int main(int argc, char *argv[]) {

34
test/preferences.fl

@ -45,7 +45,7 @@ Function {saveAndCloseWindowCB( Fl_Widget*, void* )} {open private return_type v
Fl::delete_widget(myWindow);} {} Fl::delete_widget(myWindow);} {}
} }
Function {} {open return_type int Function {} {open selected return_type int
} { } {
Fl_Window myWindow { Fl_Window myWindow {
label {My Preferences} label {My Preferences}
@ -78,7 +78,7 @@ Function {} {open return_type int
xywh {0 0 100 20} xywh {0 0 100 20}
} }
MenuItem {} { MenuItem {} {
label {p.m.} selected label {p.m.}
xywh {0 0 100 20} xywh {0 0 100 20}
} }
} }
@ -206,10 +206,32 @@ int intValue;
char buffer[80]; char buffer[80];
double doubleValue; 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 ]; root = app.filename(path, FL_PATH_MAX);
app.getUserdataPath( path, sizeof(path) ); 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" ); Fl_Preferences bed( app, "Bed" );
bed.get( "alarm", buffer, "8:00", 79 ); 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 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" ); Fl_Preferences bed( app, "Bed" );

2
test/sudoku.cxx

@ -625,7 +625,7 @@ SudokuCell::handle(int event) {
// Sudoku class globals... // Sudoku class globals...
Fl_Help_Dialog *Sudoku::help_dialog_ = (Fl_Help_Dialog *)0; 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... // Create a Sudoku game window...

Loading…
Cancel
Save