1 /********************************************************************************
2 * *
3 * S e t t i n g s C l a s s *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1998,2020 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published by *
10 * the Free Software Foundation; either version 3 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/> *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "fxascii.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 #include "FXElement.h"
30 #include "FXException.h"
31 #include "FXString.h"
32 #include "FXColors.h"
33 #include "FXFile.h"
34 #include "FXStringDictionary.h"
35 #include "FXSettings.h"
36
37 /*
38 Notes:
39
40 - Format for settings database file:
41
42 [SectionKey]
43 EntryKey=string-with-no-spaces
44 EntryKey="string\nwith a\nnewline in it\n"
45 EntryKey=" string with leading and trailing spaces and \"embedded\" in it "
46 EntryKey=string with no leading or trailing spaces
47
48 - EntryKey may is of the form "ali baba", "ali-baba", "ali_baba", or "ali.baba".
49
50 - Leading/trailing spaces are NOT part of the EntryKey.
51
52 - Escape sequences now allow octal (\377) as well as hex (\xff) codes.
53
54 - EntryKey format should be like values.
55
56 - Extensive error checking in unparseFile() to ensure no settings data is
57 lost when disk is full.
58 */
59
60
61 #define EMPTY ((Entry*)(__settings__empty__+3))
62 #define NOMEMORY ((Entry*)(((FXival*)NULL)+3))
63 #define BSHIFT 5
64
65 using namespace FX;
66
67 /*******************************************************************************/
68
69 namespace FX {
70
71
72 // Empty string dictionary table value
73 extern const FXint __string__empty__[];
74 extern const FXival __stringdictionary__empty__[];
75 extern const FXival __settings__empty__[];
76 const FXival __settings__empty__[6]={1,0,1,(FXival)(__string__empty__+1),(FXival)(__stringdictionary__empty__+3),0};
77
78
79 // Adjust the size of the table
no(FXival n)80 FXbool FXSettings::no(FXival n){
81 FXival m=no();
82 if(__likely(m!=n)){
83 Entry *elbat;
84
85 // Release old table
86 if(1<m){
87 destructElms(table,m);
88 ::free(((FXival*)table)-3);
89 table=EMPTY;
90 }
91
92 // Allocate new table
93 if(1<n){
94 if((elbat=(Entry*)(((FXival*)::calloc(sizeof(FXival)*3+sizeof(Entry)*n,1))+3))==NOMEMORY) return false;
95 ((FXival*)elbat)[-3]=n;
96 ((FXival*)elbat)[-2]=0;
97 ((FXival*)elbat)[-1]=n;
98 constructElms(elbat,n);
99 table=elbat;
100 }
101 }
102 return true;
103 }
104
105
106 // Resize the table to the given size; the size must be a power of two
resize(FXival n)107 FXbool FXSettings::resize(FXival n){
108 FXSettings elbat;
109 FXASSERT((n&(n-1))==0); // Power of 2
110 FXASSERT((n-used())>0); // At least one free slot
111 if(elbat.no(n)){
112 if(1<elbat.no() && 1<no()){
113 FXuval p,b,h,x; FXival i;
114 for(i=0; i<no(); ++i){ // Hash existing entries into new table
115 if(!table[i].key.empty()){
116 p=b=h=table[i].hash;
117 while(elbat.table[x=p&(n-1)].hash){ // Locate slot
118 p=(p<<2)+p+b+1;
119 b>>=BSHIFT;
120 }
121 elbat.table[x].key.adopt(table[i].key); // Steal the string buffers
122 elbat.table[x].data.adopt(table[i].data);
123 elbat.table[x].hash=(FXuint)h; // And copy the hash value
124 }
125 }
126 elbat.free(n-used()); // All non-empty slots now free
127 elbat.used(used()); // Used slots not changed
128 elbat.setModified(isModified());
129 }
130 adopt(elbat);
131 return true;
132 }
133 return false;
134 }
135
136
137 // Construct settings database
FXSettings()138 FXSettings::FXSettings():table(EMPTY),modified(false){
139 FXASSERT(sizeof(Entry)<=sizeof(FXival)*3);
140 }
141
142
143 // Construct from another string dictionary
FXSettings(const FXSettings & other)144 FXSettings::FXSettings(const FXSettings& other):table(EMPTY),modified(other.modified){
145 FXASSERT(sizeof(Entry)<=sizeof(FXival)*3);
146 if(1<other.no()){
147 if(__unlikely(!no(other.no()))){ throw FXMemoryException("FXSettings::FXSettings: out of memory\n"); }
148 copyElms(table,other.table,no());
149 free(other.free());
150 used(other.used());
151 }
152 }
153
154
155 // Assignment operator
operator =(const FXSettings & other)156 FXSettings& FXSettings::operator=(const FXSettings& other){
157 if(__likely(table!=other.table)){
158 if(1<other.no()){
159 if(__unlikely(!no(other.no()))){ throw FXMemoryException("FXSettings::operator=: out of memory\n"); }
160 copyElms(table,other.table,no());
161 free(other.free());
162 used(other.used());
163 }
164 else{
165 no(1);
166 }
167 modified=true;
168 }
169 return *this;
170 }
171
172
173 // Adopt string dictionary from another
adopt(FXSettings & other)174 FXSettings& FXSettings::adopt(FXSettings& other){
175 if(__likely(table!=other.table)){
176 swap(table,other.table);
177 other.clear();
178 modified=true;
179 }
180 return *this;
181 }
182
183
184 // Find position of given key
find(const FXchar * ky) const185 FXival FXSettings::find(const FXchar* ky) const {
186 if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::find: null or empty key\n"); }
187 if(__likely(!empty())){
188 FXuval p,b,x,h;
189 p=b=h=FXString::hash(ky);
190 FXASSERT(h);
191 while(table[x=p&(no()-1)].hash){
192 if(table[x].hash==h && table[x].key==ky) return x;
193 p=(p<<2)+p+b+1;
194 b>>=BSHIFT;
195 }
196 }
197 return -1;
198 }
199
200
201 // Return reference to string assocated with key
at(const FXchar * ky)202 FXStringDictionary& FXSettings::at(const FXchar* ky){
203 FXuval p,b,h,x;
204 if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::at: null or empty key\n"); }
205 p=b=h=FXString::hash(ky);
206 FXASSERT(h);
207 while(table[x=p&(no()-1)].hash){
208 if(table[x].hash==h && table[x].key==ky) goto x; // Return existing slot
209 p=(p<<2)+p+b+1;
210 b>>=BSHIFT;
211 }
212 if(__unlikely(free()<=1+(no()>>2)) && __unlikely(!resize(no()<<1))){ throw FXMemoryException("FXSettings::at: out of memory\n"); }
213 p=b=h;
214 while(table[x=p&(no()-1)].hash){
215 if(table[x].key.empty()) goto y; // Return voided slot
216 p=(p<<2)+p+b+1;
217 b>>=BSHIFT;
218 }
219 free(free()-1); // Put into empty slot
220 y:used(used()+1);
221 table[x].key=ky;
222 table[x].hash=(FXuint)h;
223 x:modified=true; // Assume its to be written
224 return table[x].data;
225 }
226
227
228 // Return constant reference to string assocated with key
at(const FXchar * ky) const229 const FXStringDictionary& FXSettings::at(const FXchar* ky) const {
230 if(__unlikely(!ky || !*ky)){ throw FXRangeException("FXSettings::at: null or empty key\n"); }
231 if(__likely(!empty())){
232 FXuval p,b,x,h;
233 p=b=h=FXString::hash(ky);
234 FXASSERT(h);
235 while(table[x=p&(no()-1)].hash){
236 if(table[x].hash==h && table[x].key==ky) return table[x].data;
237 p=(p<<2)+p+b+1;
238 b>>=BSHIFT;
239 }
240 }
241 return EMPTY[0].data;
242 }
243
244
245 // Parse filename
parseFile(const FXString & filename,FXbool mrk)246 FXbool FXSettings::parseFile(const FXString& filename,FXbool mrk){
247 FXTRACE((100,"FXSettings::parseFile(%s,%d)\n",filename.text(),mrk));
248 FXFile file(filename,FXFile::Reading);
249 if(file.isOpen()){
250 FXString string;
251 string.length(file.size());
252 if(file.readBlock(string.text(),string.length())==string.length()){
253 return parse(string,mrk);
254 }
255 }
256 return false;
257 }
258
259
260 // Unparse registry file
unparseFile(const FXString & filename)261 FXbool FXSettings::unparseFile(const FXString& filename){
262 FXTRACE((100,"FXSettings::unparseFile(%s)\n",filename.text()));
263 FXFile file(filename,FXFile::Writing);
264 if(file.isOpen()){
265 FXString string;
266 if(unparse(string)){
267 return file.writeBlock(string.text(),string.length())==string.length();
268 }
269 }
270 return false;
271 }
272
273
274 // Parse single string to populate settings
parse(const FXString & str,FXbool mrk)275 FXbool FXSettings::parse(const FXString& str,FXbool mrk){
276 FXint lineno=1,p=0,b,e;
277 FXString section,name,value;
278
279 // Skip over byte-order mark
280 if(str[p]=='\xef' && str[p+1]=='\xbb' && str[p+2]=='\xbf') p+=3;
281
282 // Parse one line at a time
283 while(str[p]){
284
285 // Skip leading blanks
286 while(Ascii::isBlank(str[p])) ++p;
287
288 // Parse section name
289 if(str[p]=='['){
290
291 b=++p;
292
293 // Scan over section name
294 while(str[p] && str[p]!=']' && str[p]!='\r' && str[p]!='\n'){
295 if(Ascii::isControl(str[p])){ fxwarning("%d: control character in section name.\n",lineno); goto nxt; }
296 ++p;
297 }
298
299 // Check errors
300 if(str[p]!=']'){ fxwarning("%d: expected ']' to close section name.\n",lineno); goto nxt; }
301
302 e=p++;
303
304 // Grab name
305 section=str.mid(b,e-b);
306 }
307
308 // Parse name-value pair
309 else if(str[p]!='#' && str[p]!=';' && str[p]!='\r' && str[p]!='\n'){
310
311 // Should have seen section prior to this
312 if(section.empty()){ fxwarning("%d: entry should follow a section.\n",lineno); goto nxt; }
313
314 b=p;
315
316 // Scan key name
317 while(str[p] && str[p]!='=' && str[p]!='\r' && str[p]!='\n'){
318 if(Ascii::isControl(str[p])){ fxwarning("%d: control character in entry name.\n",lineno); goto nxt; }
319 ++p;
320 }
321
322 // Check errors
323 if(str[p]!='='){ fxwarning("%d: expected '=' to follow entry name.\n",lineno); goto nxt; }
324
325 e=p++;
326
327 // Remove trailing spaces after name
328 while(b<e && Ascii::isBlank(str[e-1])) e--;
329
330 // Grab name
331 name=str.mid(b,e-b);
332
333 // Skip leading spaces
334 while(Ascii::isBlank(str[p])) p++;
335
336 // Mark value
337 b=p;
338
339 // Scan value
340 while(str[p] && str[p]!='\n' && str[p]!='\r'){
341 if(Ascii::isControl(str[p])){ fxwarning("%d: control character in entry value.\n",lineno); goto nxt; }
342 ++p;
343 }
344
345 e=p;
346
347 // Remove trailing spaces after value
348 while(b<e && Ascii::isBlank(str[e-1])) e--;
349
350 // Grab the unescaped value
351 value=FXString::unescape(str.mid(b,e-b),'"','"');
352
353 // Add entry to current section
354 at(section).at(name,mrk)=value;
355 }
356
357 // Skip to end of line
358 nxt:while(str[p]){
359 if(str[p++]=='\n'){ lineno++; break; }
360 }
361 }
362 return true;
363 }
364
365
366 // Unparse settings to a single string
unparse(FXString & str) const367 FXbool FXSettings::unparse(FXString& str) const {
368 FXival sec,ent,flg;
369
370 // Clear output
371 str.clear();
372
373 // Any keys at all?
374 if(!empty()){
375
376 // Loop over all sections
377 for(sec=0; sec<no(); ++sec){
378
379 // Get group, if any
380 if(!empty(sec)){
381
382 // Loop over all entries
383 for(ent=flg=0; ent<data(sec).no(); ++ent){
384
385 // Is key-value pair marked?
386 if(!data(sec).empty(ent) && data(sec).mark(ent)){
387
388 // Write section name if not written yet
389 if(!flg){
390 str.append("[");
391 str.append(key(sec)); // FIXME should escape group
392 str.append("]" ENDLINE);
393 flg=1;
394 }
395
396 // Write marked key-value pairs only
397 str.append(data(sec).key(ent));
398 str.append("=");
399 if(FXString::shouldEscape(data(sec).data(ent),'"','"')){
400 str.append(FXString::escape(data(sec).data(ent),'"','"'));
401 }
402 else{
403 str.append(data(sec).data(ent));
404 }
405 str.append(ENDLINE);
406 }
407 }
408
409 // Blank line after end
410 if(flg){
411 str.append(ENDLINE);
412 }
413 }
414 }
415 }
416 return true;
417 }
418
419
420 // Read a formatted registry entry
readFormatEntry(const FXchar * section,const FXchar * name,const FXchar * fmt,...) const421 FXint FXSettings::readFormatEntry(const FXchar* section,const FXchar* name,const FXchar* fmt,...) const {
422 const FXString& value=at(section).at(name);
423 FXint result=0;
424 if(!value.empty()){
425 va_list args;
426 va_start(args,fmt);
427 result=value.vscan(fmt,args);
428 va_end(args);
429 }
430 return result;
431 }
432
433
434 // Read a formatted registry entry
readFormatEntry(const FXString & section,const FXchar * name,const FXchar * fmt,...) const435 FXint FXSettings::readFormatEntry(const FXString& section,const FXchar* name,const FXchar* fmt,...) const {
436 const FXString& value=at(section).at(name);
437 FXint result=0;
438 if(!value.empty()){
439 va_list args;
440 va_start(args,fmt);
441 result=value.vscan(fmt,args);
442 va_end(args);
443 }
444 return result;
445 }
446
447
448 // Read a formatted registry entry
readFormatEntry(const FXString & section,const FXString & name,const FXchar * fmt,...) const449 FXint FXSettings::readFormatEntry(const FXString& section,const FXString& name,const FXchar* fmt,...) const {
450 const FXString& value=at(section).at(name);
451 FXint result=0;
452 if(!value.empty()){
453 va_list args;
454 va_start(args,fmt);
455 result=value.vscan(fmt,args);
456 va_end(args);
457 }
458 return result;
459 }
460
461
462 // Write a formatted registry entry
writeFormatEntry(const FXchar * section,const FXchar * name,const FXchar * fmt,...)463 FXint FXSettings::writeFormatEntry(const FXchar* section,const FXchar* name,const FXchar* fmt,...){
464 FXint result;
465 va_list args;
466 va_start(args,fmt);
467 result=at(section).at(name,true).vformat(fmt,args);
468 va_end(args);
469 return result;
470 }
471
472
473 // Write a formatted registry entry
writeFormatEntry(const FXString & section,const FXchar * name,const FXchar * fmt,...)474 FXint FXSettings::writeFormatEntry(const FXString& section,const FXchar* name,const FXchar* fmt,...){
475 FXint result;
476 va_list args;
477 va_start(args,fmt);
478 result=at(section).at(name,true).vformat(fmt,args);
479 va_end(args);
480 return result;
481 }
482
483
484 // Write a formatted registry entry
writeFormatEntry(const FXString & section,const FXString & name,const FXchar * fmt,...)485 FXint FXSettings::writeFormatEntry(const FXString& section,const FXString& name,const FXchar* fmt,...){
486 FXint result;
487 va_list args;
488 va_start(args,fmt);
489 result=at(section).at(name,true).vformat(fmt,args);
490 va_end(args);
491 return result;
492 }
493
494
495 // Read a string-valued registry entry
readStringEntry(const FXchar * section,const FXchar * name,const FXchar * def) const496 const FXchar* FXSettings::readStringEntry(const FXchar* section,const FXchar* name,const FXchar* def) const {
497 const FXStringDictionary& dict=at(section);
498 FXival slot=dict.find(name);
499 if(0<=slot){
500 return dict.data(slot).text(); // Use value at slot even if value was empty string!
501 }
502 return def;
503 }
504
505
506 // Read a string-valued registry entry
readStringEntry(const FXString & section,const FXchar * name,const FXchar * def) const507 const FXchar* FXSettings::readStringEntry(const FXString& section,const FXchar* name,const FXchar* def) const {
508 return readStringEntry(section.text(),name,def);
509 }
510
511
512 // Read a string-valued registry entry
readStringEntry(const FXString & section,const FXString & name,const FXchar * def) const513 const FXchar* FXSettings::readStringEntry(const FXString& section,const FXString& name,const FXchar* def) const {
514 return readStringEntry(section.text(),name.text(),def);
515 }
516
517
518 // Write a string-valued registry entry
writeStringEntry(const FXchar * section,const FXchar * name,const FXchar * val)519 FXbool FXSettings::writeStringEntry(const FXchar* section,const FXchar* name,const FXchar* val){
520 at(section).at(name,true)=val;
521 return true;
522 }
523
524
525 // Write a string-valued registry entry
writeStringEntry(const FXString & section,const FXchar * name,const FXchar * val)526 FXbool FXSettings::writeStringEntry(const FXString& section,const FXchar* name,const FXchar* val){
527 return writeStringEntry(section.text(),name,val);
528 }
529
530
531 // Write a string-valued registry entry
writeStringEntry(const FXString & section,const FXString & name,const FXchar * val)532 FXbool FXSettings::writeStringEntry(const FXString& section,const FXString& name,const FXchar* val){
533 return writeStringEntry(section.text(),name.text(),val);
534 }
535
536
537 // Read a int-valued registry entry
readIntEntry(const FXchar * section,const FXchar * name,FXint def) const538 FXint FXSettings::readIntEntry(const FXchar* section,const FXchar* name,FXint def) const {
539 const FXString& value=at(section).at(name);
540 if(!value.empty()){
541 FXint result;
542 FXbool ok;
543 result=value.toInt(10,&ok);
544 if(ok) return result;
545 }
546 return def;
547 }
548
549
550 // Read a int-valued registry entry
readIntEntry(const FXString & section,const FXchar * name,FXint def) const551 FXint FXSettings::readIntEntry(const FXString& section,const FXchar* name,FXint def) const {
552 return readIntEntry(section.text(),name,def);
553 }
554
555
556 // Read a int-valued registry entry
readIntEntry(const FXString & section,const FXString & name,FXint def) const557 FXint FXSettings::readIntEntry(const FXString& section,const FXString& name,FXint def) const {
558 return readIntEntry(section.text(),name.text(),def);
559 }
560
561
562 // Write a int-valued registry entry
writeIntEntry(const FXchar * section,const FXchar * name,FXint val)563 FXbool FXSettings::writeIntEntry(const FXchar* section,const FXchar* name,FXint val){
564 at(section).at(name,true).fromInt(val);
565 return true;
566 }
567
568
569 // Write a int-valued registry entry
writeIntEntry(const FXString & section,const FXchar * name,FXint val)570 FXbool FXSettings::writeIntEntry(const FXString& section,const FXchar* name,FXint val){
571 return writeIntEntry(section.text(),name,val);
572 }
573
574
575 // Write a int-valued registry entry
writeIntEntry(const FXString & section,const FXString & name,FXint val)576 FXbool FXSettings::writeIntEntry(const FXString& section,const FXString& name,FXint val){
577 return writeIntEntry(section.text(),name.text(),val);
578 }
579
580
581 // Read a unsigned int-valued registry entry
readUIntEntry(const FXchar * section,const FXchar * name,FXuint def) const582 FXuint FXSettings::readUIntEntry(const FXchar* section,const FXchar* name,FXuint def) const {
583 const FXString& value=at(section).at(name);
584 if(!value.empty()){
585 FXuint result;
586 FXbool ok;
587 result=value.toUInt(10,&ok);
588 if(ok) return result;
589 }
590 return def;
591 }
592
593
594 // Read a unsigned int-valued registry entry
readUIntEntry(const FXString & section,const FXchar * name,FXuint def) const595 FXuint FXSettings::readUIntEntry(const FXString& section,const FXchar* name,FXuint def) const {
596 return readUIntEntry(section.text(),name,def);
597 }
598
599
600 // Read a unsigned int-valued registry entry
readUIntEntry(const FXString & section,const FXString & name,FXuint def) const601 FXuint FXSettings::readUIntEntry(const FXString& section,const FXString& name,FXuint def) const {
602 return readUIntEntry(section.text(),name.text(),def);
603 }
604
605
606 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXchar * section,const FXchar * name,FXuint val)607 FXbool FXSettings::writeUIntEntry(const FXchar* section,const FXchar* name,FXuint val){
608 at(section).at(name,true).fromUInt(val);
609 return true;
610 }
611
612 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXString & section,const FXchar * name,FXuint val)613 FXbool FXSettings::writeUIntEntry(const FXString& section,const FXchar* name,FXuint val){
614 return writeUIntEntry(section.text(),name,val);
615 }
616
617
618 // Write a unsigned int-valued registry entry
writeUIntEntry(const FXString & section,const FXString & name,FXuint val)619 FXbool FXSettings::writeUIntEntry(const FXString& section,const FXString& name,FXuint val){
620 return writeUIntEntry(section.text(),name.text(),val);
621 }
622
623
624 // Read a 64-bit long integer registry entry
readLongEntry(const FXchar * section,const FXchar * name,FXlong def) const625 FXlong FXSettings::readLongEntry(const FXchar* section,const FXchar* name,FXlong def) const {
626 const FXString& value=at(section).at(name);
627 if(!value.empty()){
628 FXlong result;
629 FXbool ok;
630 result=value.toLong(10,&ok);
631 if(ok) return result;
632 }
633 return def;
634 }
635
636
637 // Read a 64-bit long integer registry entry
readLongEntry(const FXString & section,const FXchar * name,FXlong def) const638 FXlong FXSettings::readLongEntry(const FXString& section,const FXchar* name,FXlong def) const {
639 return readLongEntry(section.text(),name,def);
640 }
641
642
643 // Read a 64-bit long integer registry entry
readLongEntry(const FXString & section,const FXString & name,FXlong def) const644 FXlong FXSettings::readLongEntry(const FXString& section,const FXString& name,FXlong def) const {
645 return readLongEntry(section.text(),name.text(),def);
646 }
647
648
649 // Write a 64-bit long integer registry entry
writeLongEntry(const FXchar * section,const FXchar * name,FXlong val)650 FXbool FXSettings::writeLongEntry(const FXchar* section,const FXchar* name,FXlong val){
651 at(section).at(name,true).fromLong(val);
652 return true;
653 }
654
655
656 // Write a 64-bit long integer registry entry
writeLongEntry(const FXString & section,const FXchar * name,FXlong val)657 FXbool FXSettings::writeLongEntry(const FXString& section,const FXchar* name,FXlong val){
658 return writeLongEntry(section.text(),name,val);
659 }
660
661
662 // Write a 64-bit long integer registry entry
writeLongEntry(const FXString & section,const FXString & name,FXlong val)663 FXbool FXSettings::writeLongEntry(const FXString& section,const FXString& name,FXlong val){
664 return writeLongEntry(section.text(),name.text(),val);
665 }
666
667
668 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXchar * section,const FXchar * name,FXulong def) const669 FXulong FXSettings::readULongEntry(const FXchar* section,const FXchar* name,FXulong def) const {
670 const FXString& value=at(section).at(name);
671 if(!value.empty()){
672 FXulong result;
673 FXbool ok;
674 result=value.toULong(10,&ok);
675 if(ok) return result;
676 }
677 return def;
678 }
679
680
681 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXString & section,const FXchar * name,FXulong def) const682 FXulong FXSettings::readULongEntry(const FXString& section,const FXchar* name,FXulong def) const {
683 return readULongEntry(section.text(),name,def);
684 }
685
686
687 // Read a 64-bit unsigned long integer registry entry
readULongEntry(const FXString & section,const FXString & name,FXulong def) const688 FXulong FXSettings::readULongEntry(const FXString& section,const FXString& name,FXulong def) const {
689 return readULongEntry(section.text(),name.text(),def);
690 }
691
692
693 // Write a 64-bit long integer registry entry
writeULongEntry(const FXchar * section,const FXchar * name,FXulong val)694 FXbool FXSettings::writeULongEntry(const FXchar* section,const FXchar* name,FXulong val){
695 at(section).at(name,true).fromULong(val);
696 return true;
697 }
698
699
700 // Write a 64-bit long integer registry entry
writeULongEntry(const FXString & section,const FXchar * name,FXulong val)701 FXbool FXSettings::writeULongEntry(const FXString& section,const FXchar* name,FXulong val){
702 return writeULongEntry(section.text(),name,val);
703 }
704
705
706 // Write a 64-bit long integer registry entry
writeULongEntry(const FXString & section,const FXString & name,FXulong val)707 FXbool FXSettings::writeULongEntry(const FXString& section,const FXString& name,FXulong val){
708 return writeULongEntry(section.text(),name.text(),val);
709 }
710
711
712 // Read a double-valued registry entry
readRealEntry(const FXchar * section,const FXchar * name,FXdouble def) const713 FXdouble FXSettings::readRealEntry(const FXchar* section,const FXchar* name,FXdouble def) const {
714 const FXString& value=at(section).at(name);
715 if(!value.empty()){
716 FXdouble result;
717 FXbool ok;
718 result=value.toDouble(&ok);
719 if(ok) return result;
720 }
721 return def;
722 }
723
724 // Read a double-valued registry entry
readRealEntry(const FXString & section,const FXchar * name,FXdouble def) const725 FXdouble FXSettings::readRealEntry(const FXString& section,const FXchar* name,FXdouble def) const {
726 return readRealEntry(section.text(),name,def);
727 }
728
729
730 // Read a double-valued registry entry
readRealEntry(const FXString & section,const FXString & name,FXdouble def) const731 FXdouble FXSettings::readRealEntry(const FXString& section,const FXString& name,FXdouble def) const {
732 return readRealEntry(section.text(),name.text(),def);
733 }
734
735
736 // Write a double-valued registry entry
writeRealEntry(const FXchar * section,const FXchar * name,FXdouble val)737 FXbool FXSettings::writeRealEntry(const FXchar* section,const FXchar* name,FXdouble val){
738 at(section).at(name,true).fromDouble(val,16,2);
739 return true;
740 }
741
742
743 // Write a double-valued registry entry
writeRealEntry(const FXString & section,const FXchar * name,FXdouble val)744 FXbool FXSettings::writeRealEntry(const FXString& section,const FXchar* name,FXdouble val){
745 return writeRealEntry(section.text(),name,val);
746 }
747
748
749 // Write a double-valued registry entry
writeRealEntry(const FXString & section,const FXString & name,FXdouble val)750 FXbool FXSettings::writeRealEntry(const FXString& section,const FXString& name,FXdouble val){
751 return writeRealEntry(section.text(),name.text(),val);
752 }
753
754
755 // Read a color registry entry
readColorEntry(const FXchar * section,const FXchar * name,FXColor def) const756 FXColor FXSettings::readColorEntry(const FXchar* section,const FXchar* name,FXColor def) const {
757 const FXString& value=at(section).at(name);
758 if(!value.empty()){
759 return colorFromName(value);
760 }
761 return def;
762 }
763
764
765 // Read a color registry entry
readColorEntry(const FXString & section,const FXchar * name,FXColor def) const766 FXColor FXSettings::readColorEntry(const FXString& section,const FXchar* name,FXColor def) const {
767 return readColorEntry(section.text(),name,def);
768 }
769
770 // Read a color registry entry
readColorEntry(const FXString & section,const FXString & name,FXColor def) const771 FXColor FXSettings::readColorEntry(const FXString& section,const FXString& name,FXColor def) const {
772 return readColorEntry(section.text(),name.text(),def);
773 }
774
775
776 // Write a color registry entry
writeColorEntry(const FXchar * section,const FXchar * name,FXColor val)777 FXbool FXSettings::writeColorEntry(const FXchar* section,const FXchar* name,FXColor val){
778 at(section).at(name,true)=nameFromColor(val);
779 return true;
780 }
781
782
783 // Write a color registry entry
writeColorEntry(const FXString & section,const FXchar * name,FXColor val)784 FXbool FXSettings::writeColorEntry(const FXString& section,const FXchar* name,FXColor val){
785 return writeColorEntry(section.text(),name,val);
786 }
787
788
789 // Write a color registry entry
writeColorEntry(const FXString & section,const FXString & name,FXColor val)790 FXbool FXSettings::writeColorEntry(const FXString& section,const FXString& name,FXColor val){
791 return writeColorEntry(section.text(),name.text(),val);
792 }
793
794
795 // Read a boolean registry entry
readBoolEntry(const FXchar * section,const FXchar * name,FXbool def) const796 FXbool FXSettings::readBoolEntry(const FXchar* section,const FXchar* name,FXbool def) const {
797 const FXString& value=at(section).at(name);
798 if(!value.empty()){
799 if(comparecase(value,"true")==0) return true;
800 else if(comparecase(value,"false")==0) return false;
801 else if(comparecase(value,"yes")==0) return true;
802 else if(comparecase(value,"no")==0) return false;
803 else if(comparecase(value,"on")==0) return true;
804 else if(comparecase(value,"off")==0) return false;
805 else if(comparecase(value,"1")==0) return true;
806 else if(comparecase(value,"0")==0) return false;
807 }
808 return def;
809 }
810
811
812 // Read a boolean registry entry
readBoolEntry(const FXString & section,const FXchar * name,FXbool def) const813 FXbool FXSettings::readBoolEntry(const FXString& section,const FXchar* name,FXbool def) const {
814 return readBoolEntry(section.text(),name,def);
815 }
816
817
818 // Read a boolean registry entry
readBoolEntry(const FXString & section,const FXString & name,FXbool def) const819 FXbool FXSettings::readBoolEntry(const FXString& section,const FXString& name,FXbool def) const {
820 return readBoolEntry(section.text(),name.text(),def);
821 }
822
823
824 // Write a boolean registry entry
writeBoolEntry(const FXchar * section,const FXchar * name,FXbool val)825 FXbool FXSettings::writeBoolEntry(const FXchar* section,const FXchar* name,FXbool val){
826 at(section).at(name,true)=val?"true":"false";
827 return true;
828 }
829
830
831 // Write a boolean registry entry
writeBoolEntry(const FXString & section,const FXchar * name,FXbool val)832 FXbool FXSettings::writeBoolEntry(const FXString& section,const FXchar* name,FXbool val){
833 return writeBoolEntry(section.text(),name,val);
834 }
835
836
837 // Write a boolean registry entry
writeBoolEntry(const FXString & section,const FXString & name,FXbool val)838 FXbool FXSettings::writeBoolEntry(const FXString& section,const FXString& name,FXbool val){
839 return writeBoolEntry(section.text(),name.text(),val);
840 }
841
842
843 // See if entry exists
existingEntry(const FXchar * section,const FXchar * name) const844 FXbool FXSettings::existingEntry(const FXchar* section,const FXchar* name) const {
845 return 0<=at(section).find(name);
846 }
847
848
849 // See if entry exists
existingEntry(const FXString & section,const FXchar * name) const850 FXbool FXSettings::existingEntry(const FXString& section,const FXchar* name) const {
851 return existingEntry(section.text(),name);
852 }
853
854
855 // See if entry exists
existingEntry(const FXString & section,const FXString & name) const856 FXbool FXSettings::existingEntry(const FXString& section,const FXString& name) const {
857 return existingEntry(section.text(),name.text());
858 }
859
860
861 // See if section exists
existingSection(const FXchar * section) const862 FXbool FXSettings::existingSection(const FXchar* section) const {
863 return 0<=find(section);
864 }
865
866
867 // See if section exists
existingSection(const FXString & section) const868 FXbool FXSettings::existingSection(const FXString& section) const {
869 return existingSection(section.text());
870 }
871
872
873 // Delete a registry entry
deleteEntry(const FXchar * section,const FXchar * name)874 void FXSettings::deleteEntry(const FXchar* section,const FXchar* name){
875 at(section).remove(name);
876 }
877
878
879 // Delete a registry entry
deleteEntry(const FXString & section,const FXchar * name)880 void FXSettings::deleteEntry(const FXString& section,const FXchar* name){
881 deleteEntry(section.text(),name);
882 }
883
884
885 // Delete a registry entry
deleteEntry(const FXString & section,const FXString & name)886 void FXSettings::deleteEntry(const FXString& section,const FXString& name){
887 deleteEntry(section.text(),name.text());
888 }
889
890
891 // Delete section
deleteSection(const FXchar * section)892 void FXSettings::deleteSection(const FXchar* section){
893 if(__unlikely(!section || !*section)){ throw FXRangeException("FXSettings::deleteSection: null or empty key\n"); }
894 if(__likely(!empty())){
895 FXuval p,b,h,x;
896 p=b=h=FXString::hash(section);
897 FXASSERT(h);
898 while(table[x=p&(no()-1)].hash!=h || table[x].key!=section){
899 if(!table[x].hash) return;
900 p=(p<<2)+p+b+1;
901 b>>=BSHIFT;
902 }
903 table[x].key.clear(); // Void the slot (not empty!)
904 table[x].data.clear();
905 used(used()-1);
906 modified=true;
907 if(__unlikely(used()<=(no()>>2))) resize(no()>>1);
908 }
909 }
910
911
912 // Delete section
deleteSection(const FXString & section)913 void FXSettings::deleteSection(const FXString& section){
914 deleteSection(section.text());
915 }
916
917
918 // Clear all sections
clear()919 void FXSettings::clear(){
920 modified=true;
921 no(1);
922 }
923
924
925 // Clean up
~FXSettings()926 FXSettings::~FXSettings(){
927 clear();
928 }
929
930 }
931