fl_preferences.cxx

来自「SRI international 发布的OAA框架软件」· CXX 代码 · 共 1,121 行 · 第 1/2 页

CXX
1,121
字号
//
// "$Id: Fl_Preferences.cxx,v 1.1.1.1 2003/06/03 22:25:43 agno Exp $"
//
// Preferences methods for the Fast Light Tool Kit (FLTK).
//
// Copyright 2002-2003 by Matthias Melcher.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA.
//
// Please report all bugs and problems to "fltk-bugs@fltk.org".
//


#include <FL/Fl.H>
#include <FL/Fl_Preferences.H>
#include <FL/filename.H>

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "flstring.h"
#include <sys/stat.h>

#if defined(WIN32) && !defined(__CYGWIN__)
#  include <direct.h>
#  include <io.h>
#elif defined (__APPLE__)
#  include <Carbon/Carbon.H>
#  include <unistd.h>
#else
#  include <unistd.h>
#endif


char Fl_Preferences::nameBuffer[128];


/**
 * create the initial preferences base
 * - root: machine or user preferences
 * - vendor: unique identification of author or vendor of application
 *     Must be a valid directory name.
 * - application: vendor unique application name, i.e. "PreferencesTest"
 *     multiple preferences files can be created per application.
 *     Must be a valid file name.
 * example: Fl_Preferences base( Fl_Preferences::USER, "fltk.org", "test01");
 */
Fl_Preferences::Fl_Preferences( Root root, const char *vendor, const char *application )
{
  node = new Node( "." );
  rootNode = new RootNode( this, root, vendor, application );
}


/**
 * create the initial preferences base
 * - path: an application-supplied path
 * example: Fl_Preferences base( "/usr/foo" );
 */
Fl_Preferences::Fl_Preferences( const char *path, const char *vendor, const char *application )
{
  node = new Node( "." );
  rootNode = new RootNode( this, path, vendor, application );
}


/**
 * create a Preferences node in relation to a parent node for reading and writing
 * - parent: base name for group
 * - group: group name (can contain '/' seperated group names)
 * example: Fl_Preferences colors( base, "setup/colors" );
 */
Fl_Preferences::Fl_Preferences( Fl_Preferences &parent, const char *key )
{
  rootNode = 0;
  node = parent.node->addChild( key );
}


/**
 * create a Preferences node in relation to a parent node for reading and writing
 * - parent: base name for group
 * - group: group name (can contain '/' seperated group names)
 * example: Fl_Preferences colors( base, "setup/colors" );
 */
Fl_Preferences::Fl_Preferences( Fl_Preferences *parent, const char *key )
{
  rootNode = 0;
  node = parent->node->addChild( key );
}


/**
 * destroy individual keys
 * - destroying the base preferences will flush changes to the prefs file
 * - after destroying the base, none of the depending preferences must be read or written
 */
Fl_Preferences::~Fl_Preferences()
{
  delete rootNode;
  // DO NOT delete nodes! The root node will do that after writing the preferences
}


/**
 * return the number of groups that are contained within a group
 * example: int n = base.groups();
 */
int Fl_Preferences::groups()
{
  return node->nChildren();
}


/**
 * return the group name of the n'th group
 * - there is no guaranteed order of group names
 * - the index must be within the range given by groups()
 * example: printf( "Group(%d)='%s'\n", ix, base.group(ix) );
 */
const char *Fl_Preferences::group( int ix )
{
  return node->child( ix );
}


/**
 * return 1, if a group with this name exists
 * example: if ( base.groupExists( "setup/colors" ) ) ...
 */
char Fl_Preferences::groupExists( const char *key )
{
  return node->search( key ) ? 1 : 0 ;
}


/**
 * delete a group
 * example: setup.deleteGroup( "colors/buttons" );
 */
char Fl_Preferences::deleteGroup( const char *key )
{
  Node *nd = node->search( key );
  if ( nd ) return nd->remove();
  return 0;
}


/**
 * return the number of entries (name/value) pairs for a group
 * example: int n = buttonColor.entries();
 */
int Fl_Preferences::entries()
{
  return node->nEntry;
}


/**
 * return the name of an entry
 * - there is no guaranteed order of entry names
 * - the index must be within the range given by entries()
 * example: printf( "Entry(%d)='%s'\n", ix, buttonColor.entry(ix) );
 */
const char *Fl_Preferences::entry( int ix )
{
  return node->entry[ix].name;
}


/**
 * return 1, if an entry with this name exists
 * example: if ( buttonColor.entryExists( "red" ) ) ...
 */
char Fl_Preferences::entryExists( const char *key )
{
  return node->getEntry( key )>=0 ? 1 : 0 ;
}


/**
 * remove a single entry (name/value pair)
 * example: buttonColor.deleteEntry( "red" );
 */
char Fl_Preferences::deleteEntry( const char *key )
{
  return node->deleteEntry( key );
}


/**
 * read an entry from the group
 */
char Fl_Preferences::get( const char *key, int &value, int defaultValue )
{
  const char *v = node->get( key );
  value = v ? atoi( v ) : defaultValue;
  return ( v != 0 );
}


/**
 * set an entry (name/value pair)
 */
char Fl_Preferences::set( const char *key, int value )
{
  sprintf( nameBuffer, "%d", value );
  node->set( key, nameBuffer );
  return 1;
}


/**
 * read an entry from the group
 */
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 );
}


/**
 * set an entry (name/value pair)
 */
char Fl_Preferences::set( const char *key, float value )
{
  sprintf( nameBuffer, "%g", value );
  node->set( key, nameBuffer );
  return 1;
}


/**
 * read an entry from the group
 */
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 );
}


/**
 * set an entry (name/value pair)
 */
char Fl_Preferences::set( const char *key, double value )
{
  sprintf( nameBuffer, "%g", value );
  node->set( key, nameBuffer );
  return 1;
}


// remove control sequences from a string
static char *decodeText( const char *src )
{
  int len = 0;
  const char *s = src;
  for ( ; *s; s++, len++ )
  {
    if ( *s == '\\' )
      if ( isdigit( s[1] ) ) s+=3; else s+=1;
  }
  char *dst = (char*)malloc( len+1 ), *d = dst;
  for ( s = src; *s; s++ )
  {
    char c = *s;
    if ( c == '\\' )
    {
      if ( s[1] == '\\' ) { *d++ = c; s++; }
      else if ( s[1] == 'n' ) { *d++ = '\n'; s++; }
      else if ( s[1] == 'r' ) { *d++ = '\r'; s++; }
      else if ( isdigit( s[1] ) ) { *d++ = ((s[1]-'0')<<6) + ((s[2]-'0')<<3) + (s[3]-'0'); s+=3; }
      else s++; // error
    }
    else
      *d++ = c;
  }
  *d = 0;
  return dst;
}


/**
 * read a text entry from the group
 * the text will be moved into the given text buffer
 * text will be clipped to the buffer size
 */
char Fl_Preferences::get( const char *key, char *text, const char *defaultValue, int maxSize )
{
  const char *v = node->get( key );
  if ( v && strchr( v, '\\' ) ) {
    char *w = decodeText( v );
    strlcpy(text, w, maxSize);
    free( w );
    return 1;
  }
  if ( !v ) v = defaultValue;
  if ( v ) strlcpy(text, v, maxSize);
  else text = 0;
  return ( v != defaultValue );
}


/**
 * read a text entry from the group
 * 'text' will be changed to point to a new text buffer
 * the text buffer must be deleted with 'free(text)' by the user.
 */
char Fl_Preferences::get( const char *key, char *&text, const char *defaultValue )
{
  const char *v = node->get( key );
  if ( v && strchr( v, '\\' ) )
  {
    text = decodeText( v );
    return 1;
  }    
  if ( !v ) v = defaultValue;
  if ( v )
    text = strdup( v );
  else
    text = 0;
  return ( v != defaultValue );
}


/**
 * set an entry (name/value pair)
 */
char Fl_Preferences::set( const char *key, const char *text )
{
  const char *s = text;
  int n=0, ns=0;
  for ( ; *s; s++ ) { n++; if ( *s<32 || *s=='\\' || *s==0x7f ) ns+=4; }
  if ( ns )
  {
    char *buffer = (char*)malloc( n+ns+1 ), *d = buffer;
    for ( s=text; *s; ) 
    { 
      char c = *s;
      if ( c=='\\' ) { *d++ = '\\'; *d++ = '\\'; s++; }
      else if ( c=='\n' ) { *d++ = '\\'; *d++ = 'n'; s++; }
      else if ( c=='\r' ) { *d++ = '\\'; *d++ = 'r'; s++; }
      else if ( c<32 || c==0x7f ) 
	{ *d++ = '\\'; *d++ = '0'+((c>>6)&3); *d++ = '0'+((c>>3)&7); *d++ = '0'+(c&7);  s++; }
      else *d++ = *s++;
    }
    *d = 0;
    node->set( key, buffer );
    free( buffer );
  }
  else
    node->set( key, text );
  return 1;
}


// convert a hex string to binary data
static void *decodeHex( const char *src, int &size )
{
  size = strlen( src )/2;
  unsigned char *data = (unsigned char*)malloc( size ), *d = data;
  const char *s = src;
  int i;

  for ( i=size; i>0; i-- )
  {
    int v;
    char x = tolower(*s++);
    if ( x >= 'a' ) v = x-'a'+10; else v = x-'0';
    v = v<<4;
    x = tolower(*s++);
    if ( x >= 'a' ) v += x-'a'+10; else v += x-'0';
    *d++ = (uchar)v;
  }

  return (void*)data;
}


/**
 * read a binary entry from the group
 * the data will be moved into the given destination buffer
 * data will be clipped to the buffer size
 */
char Fl_Preferences::get( const char *key, void *data, const void *defaultValue, int defaultSize, int maxSize )
{
  const char *v = node->get( key );
  if ( v )
  {
    int dsize;
    void *w = decodeHex( v, dsize );
    memmove( data, w, dsize>maxSize?maxSize:dsize );
    free( w );
    return 1;
  }    
  if ( defaultValue )
    memmove( data, defaultValue, defaultSize>maxSize?maxSize:defaultSize );
  return 0;
}


/**
 * read a binary entry from the group
 * 'data' will be changed to point to a new data buffer
 * the data buffer must be deleted with 'free(data)' by the user.
 */
char Fl_Preferences::get( const char *key, void *&data, const void *defaultValue, int defaultSize )
{
  const char *v = node->get( key );
  if ( v )
  {
    int dsize;
    data = decodeHex( v, dsize );
    return 1;
  }    
  if ( defaultValue )
  {
    data = (void*)malloc( defaultSize );
    memmove( data, defaultValue, defaultSize );
  }
  else
    data = 0;
  return 0;
}


/**
 * set an entry (name/value pair)
 */
char Fl_Preferences::set( const char *key, const void *data, int dsize )
{
  char *buffer = (char*)malloc( dsize*2+1 ), *d = buffer;;
  unsigned char *s = (unsigned char*)data;
  for ( ; dsize>0; dsize-- )
  {
    static char lu[] = "0123456789abcdef";
    unsigned char v = *s++;
    *d++ = lu[v>>4];
    *d++ = lu[v&0xf];
  }
  *d = 0;
  node->set( key, buffer );
  free( buffer );
  return 1;
}


/**
 * return the size of the value part of an entry
 */
int Fl_Preferences::size( const char *key )
{
  const char *v = node->get( key );
  return v ? strlen( v ) : 0 ;
}

/**
 * creates a path that is related to the preferences file
 * and that is usable for application data beyond what is covered 
 * by Fl_Preferences.
 * - 'getUserdataPath' actually creates the directory
 * - 'path' must be large enough to receive a complete file path
 * example:
 *   Fl_Preferences prefs( USER, "matthiasm.com", "test" );
 *   char path[FL_PATH_MAX];
 *   prefs.getUserdataPath( path );
 * sample returns:
 *   Win32: c:/Documents and Settings/matt/Application Data/matthiasm.com/test/
 *   prefs: c:/Documents and Settings/matt/Application Data/matthiasm.com/test.prefs
 */
char Fl_Preferences::getUserdataPath( char *path, int pathlen )
{
  if ( rootNode )
    return rootNode->getPath( path, pathlen );
  return 0;
}

/**
 * write all preferences to disk
 * - this function works only with the base preference group
 * - this function is rarely used as deleting the base preferences flushes automatically
 */
void Fl_Preferences::flush()
{
  if ( rootNode && node->dirty() )
    rootNode->write();
}

//-----------------------------------------------------------------------------
// helper class to create dynamic group and entry names on the fly
//

/**
 * create a group name or entry name on the fly
 * - this version creates a simple unsigned integer as an entry name
 * example:
 *   int n, i;
 *   Fl_Preferences prev( appPrefs, "PreviousFiles" );
 *   prev.get( "n", 0 );
 *   for ( i=0; i<n; i++ )
 *     prev.get( Fl_Preferences::Name(i), prevFile[i], "" );
 */
Fl_Preferences::Name::Name( unsigned int n )
{
  data_ = (char*)malloc(20);
  sprintf(data_, "%u", n);
}

/**
 * create a group name or entry name on the fly
 * - this version creates entry names as in 'printf'
 * example:
 *   int n, i;
 *   Fl_Preferences prefs( USER, "matthiasm.com", "test" );
 *   prev.get( "nFiles", 0 );
 *   for ( i=0; i<n; i++ )
 *     prev.get( Fl_Preferences::Name( "File%d", i ), prevFile[i], "" );
 */
Fl_Preferences::Name::Name( const char *format, ... )
{
  data_ = (char*)malloc(1024);
  va_list args;
  va_start(args, format);
  vsnprintf(data_, 1024, format, args);
  va_end(args);
}

// delete the name
Fl_Preferences::Name::~Name()
{
  free(data_);
}

//-----------------------------------------------------------------------------
// internal methods, do not modify or use as they will change without notice
//

int Fl_Preferences::Node::lastEntrySet = -1;

// recursively create a path in the file system
static char makePath( const char *path ) {
  if (access(path, 0)) {
    const char *s = strrchr( path, '/' );
    if ( !s ) return 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?