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.
This commit is contained in:
parent
793f4b90fa
commit
09eff7243a
@ -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.
|
||||
\brief Fl_Preferences 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 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.
|
||||
|
||||
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 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'.
|
||||
|
||||
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.
|
||||
Keys have usually a unique name within their group. Duplicate kays are
|
||||
possible though and can beaccessed using the index based functions.
|
||||
|
||||
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() .
|
||||
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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -100,69 +118,109 @@ unsigned int Fl_Preferences::file_access()
|
||||
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
|
||||
child groups. Groups are ready for reading and writing at any time.
|
||||
The root argument is either Fl_Preferences::USER
|
||||
or Fl_Preferences::SYSTEM.
|
||||
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 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
It will be lost when the app exits.
|
||||
|
||||
\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
|
||||
\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> .
|
||||
\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.
|
||||
\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 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;
|
||||
}
|
||||
@ -313,6 +398,35 @@ Fl_Preferences::~Fl_Preferences() {
|
||||
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.
|
||||
|
||||
@ -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 );
|
||||
@ -723,6 +867,45 @@ char Fl_Preferences::get( const char *key, void *data, const void *defaultValue,
|
||||
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
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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[]) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
char path[ FL_PATH_MAX ];
|
||||
app.getUserdataPath( path, sizeof(path) );
|
||||
Fl_Preferences app( Fl_Preferences::USER_L, project, application );
|
||||
|
||||
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" );
|
||||
|
||||
|
||||
@ -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...
|
||||
|
||||
Loading…
Reference in New Issue
Block a user