Merge branch 'master' of ssh://skeezixgit@git.openpandora.org/srv/git/pandora-libraries
[pandora-libraries.git] / minimenu / mmconf.c
1
2 #include <stdio.h>
3 #include <limits.h> /* for PATH_MAX */
4 #define __USE_GNU /* for strndup */
5 #include <string.h> /* for strdup */
6
7 #include "SDL.h"
8 #include "SDL_image.h"
9 #include "SDL_ttf.h"
10
11 #include "pnd_container.h"
12 #include "pnd_conf.h"
13 #include "pnd_discovery.h"
14
15 #include "mmenu.h"
16 #include "mmconf.h"
17 #include "mmcat.h"
18 #include "mmui.h"
19 #include "mmwrapcmd.h"
20
21 static unsigned char conf_render_text ( TTF_Font *f, char *buffer, SDL_Rect *dest, unsigned int x, unsigned int y, unsigned char selected );
22 static unsigned char conf_render_line ( SDL_Rect *dest, unsigned int y );
23 static char *conf_format_int ( int v, change_type_e c );
24
25 confitem_t page_general[] = {
26   { "Default tab to show",       "On startup, Minimenu will try to switch to this tab",     NULL /* default */, "categories.default_cat",  ct_visible_tab_list },
27   { "CPU speed within Minimenu", "Set low; speed to run Minimenu at",                       NULL,               "minimenu.mm_speed",       ct_cpu_speed },
28   { "CPU speed to run apps",     "Before running app, set this speed; app may override.",   "500",              "minimenu.run_speed",      ct_cpu_speed },
29   { "Show 'All' tab",            "Whethor an All tab is shown or not",                      "1",                "categories.do_all_cat",   ct_boolean },
30   { "Start with app selected",   "Whethor selection is placed by default or not",           "0",                "minimenu.start_selected", ct_boolean },
31   { "Wrap tab change",           "Changing tab left or right, does it wrap around?",        "0",                "tabs.wraparound",         ct_boolean },
32   { "Grid stop vertical",        "Changing selection up or down, does it stop or wrap?",    "0",                "grid.wrap_vert_stop",     ct_boolean },
33   { "Force wallpaper with..",    "You can force an override over themes background",        "/pandora/appdata/mmenu/wallpaper.png", "minimenu.force_wallpaper",  ct_filename },
34   { "",                          "",                                                        NULL,               NULL,                      ct_nil },
35   { "^- Back up to main",        "Go back to top level of configuration",                   NULL,               NULL,                      ct_switch_page, NULL },
36   { NULL }
37 };
38
39 confitem_t page_appshowhide [ CONF_MAX_LISTLENGTH ] = {
40   { "^- Back up to main",        "Go back to top level of configuration",                   NULL,               NULL,                      ct_switch_page, NULL },
41   { CONF_APPLIST_TAG,            "Show or hide this application",                           "1",                "appshow",                 ct_nil },
42   { NULL }
43 };
44
45 confitem_t page_tabshowhide [ CONF_MAX_LISTLENGTH ] = {
46   { "^- Back up to main",        "Go back to top level of configuration",                   NULL,               NULL,                      ct_switch_page, NULL },
47   { CONF_TABLIST_TAG,            "Show or hide or reorder this tab",                        "1",                "tabshow",                 ct_nil },
48   { NULL }
49 };
50
51 confitem_t pages[] = {
52   { "General Options",           "Miscellaneous handy options",                             NULL /* default */, NULL,                      ct_switch_page, page_general },
53   { "Show/Hide Applications",    "Each application can be hidden/revealed",                 NULL /* default */, NULL,                      ct_switch_page, page_appshowhide },
54   { "Show/Hide/Order Tabs",      "Each tab can be hidden/revealed or re-ordered",           NULL /* default */, NULL,                      ct_switch_page, page_tabshowhide },
55   { "",                          "",                                                        NULL,               NULL,                      ct_nil },
56   { "Exit configuration",        "Quit and save configuration",                             NULL /* default */, NULL,                      ct_exit },
57   { "",                          "",                                                        NULL,               NULL,                      ct_nil },
58   { "Reset to defaults",         "Remove any custom options set in this UI",                NULL /* default */, NULL,                      ct_reset },
59   { NULL }
60 };
61
62 extern pnd_conf_handle g_conf;
63 extern SDL_Surface *sdl_realscreen;
64 extern mm_imgcache_t g_imagecache [ IMG_TRUEMAX ];
65 extern mm_category_t *g_categories;
66 extern unsigned char g_categorycount;
67 extern mm_category_t _categories_invis [ MAX_CATS ];
68 extern unsigned char _categories_inviscount;
69 extern pnd_box_handle g_active_apps;
70
71 unsigned char conf_run_menu ( confitem_t *toplevel ) {
72   confitem_t *page = toplevel;
73   unsigned int sel = 0;
74   unsigned int first_visible = 0;
75   unsigned char max_visible = 12;
76   SDL_Event event;
77   confitem_t *lastpage = NULL;
78
79   while ( 1 ) {
80
81     if ( ! page ) {
82       page = pages;
83     }
84
85     if ( lastpage != page ) {
86       conf_prepare_page ( page );
87       lastpage = page;
88     }
89
90     conf_display_page ( page, sel, first_visible, max_visible );
91
92     // check for input
93     while ( SDL_WaitEvent ( &event ) ) {
94
95       switch ( event.type ) {
96
97       case SDL_KEYUP:
98
99         if ( event.key.keysym.sym == SDLK_UP ) {
100
101           do {
102
103             if ( sel ) {
104               sel--;
105
106               if ( sel < first_visible ) {
107                 first_visible--;
108               }
109
110             }
111
112           } while ( page [ sel ].type == ct_nil );
113
114         } else if ( event.key.keysym.sym == SDLK_DOWN ) {
115
116           do {
117
118             if ( page [ sel + 1 ].text ) {
119               sel++;
120
121               // ensure visibility
122               if ( sel >= first_visible + max_visible ) {
123                 first_visible++;
124               }
125
126             }
127
128           } while ( page [ sel ].type == ct_nil );
129
130         } else if ( event.key.keysym.sym == SDLK_LEFT || event.key.keysym.sym == SDLK_RIGHT ) {
131
132           unsigned char left = 0;
133           if ( event.key.keysym.sym == SDLK_LEFT ) {
134             left = 1;
135           }
136
137           switch ( page [ sel ].type ) {
138
139           case ct_cpu_speed:
140             {
141               int v = pnd_conf_get_as_int ( g_conf, page [ sel ].key );
142               if ( v == PND_CONF_BADNUM ) {
143                 v = 500;
144               }
145
146               if ( left ) {
147                 if ( v > 30 ) {
148                   v -= 10;
149                 }
150               } else {
151                 v += 10;
152               }
153
154               char buffer [ 20 ];
155               sprintf ( buffer, "%u", v );
156               pnd_conf_set_char ( g_conf, page [ sel ].key, buffer );
157
158             }
159             break;
160
161           case ct_visible_tab_list:
162             {
163               if ( g_categorycount ) {
164                 char *v = pnd_conf_get_as_char ( g_conf, page [ sel ].key );
165                 if ( v ) {
166                   unsigned char n = 0;
167                   for ( n = 0; n < g_categorycount; n++ ) {
168                     if ( strcmp ( v, g_categories [ n ].catname ) == 0 ) {
169                       break;
170                     }
171                   }
172                   if ( n < g_categorycount ) {
173                     if ( left && n ) {
174                       pnd_conf_set_char ( g_conf, page [ sel ].key, g_categories [ n - 1 ].catname );
175                     } else if ( ! left && n + 1 < g_categorycount ) {
176                       pnd_conf_set_char ( g_conf, page [ sel ].key, g_categories [ n + 1 ].catname );
177                     }
178                   } else {
179                     pnd_conf_set_char ( g_conf, page [ sel ].key, g_categories [ 0 ].catname );
180                   }
181                 } else {
182                   pnd_conf_set_char ( g_conf, page [ sel ].key, g_categories [ 0 ].catname );
183                 }
184               } // if category count
185             }
186             break;
187
188           case ct_boolean:
189             {
190               int v = pnd_conf_get_as_int ( g_conf, page [ sel ].key );
191               if ( v == PND_CONF_BADNUM ) {
192                 v = 0;
193               }
194               if ( v ) {
195                 v = 0;
196               } else {
197                 v = 1;
198               }
199
200               char buffer [ 20 ];
201               sprintf ( buffer, "%u", v );
202
203               pnd_conf_set_char ( g_conf, page [ sel ].key, buffer );
204             }
205             break;
206
207           case ct_filename:
208             break;
209
210           case ct_nil:
211           case ct_switch_page:
212           case ct_reset:
213           case ct_exit:
214             break;
215
216           } // switch
217
218         } else if ( event.key.keysym.sym == SDLK_ESCAPE ) {
219           emit_and_quit ( MM_QUIT );
220
221         } else if ( event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_END ) { // return, or "B"
222
223           switch ( page [ sel ].type ) {
224           case ct_exit:
225             {
226               return ( 1 /* always cause restart for now */ );
227             }
228             break;
229           case ct_reset:
230             {
231               conf_reset_to_default ( g_conf );
232               page = NULL;
233               sel = 0;
234             }
235             break;
236           case ct_switch_page:
237             page = page [ sel ].newhead;
238             sel = 0; // should use a stack..
239             break;
240           case ct_filename:
241             break;
242           case ct_nil:
243           case ct_visible_tab_list:
244           case ct_cpu_speed:
245           case ct_boolean:
246             break;
247           } // switch
248
249         } else {
250           // nada
251         }
252
253         break; // case sdl_key_up
254
255       } // switch what SDL event
256
257       break; // get out of sdl-wait-event loop
258     } // while events
259
260   } // while forever
261
262   return ( 1 /* always cause restart for now */ );
263 }
264
265 void conf_display_page ( confitem_t *page, unsigned int selitem, unsigned int first_visible, unsigned int max_visible ) {
266   extern TTF_Font *g_big_font;
267   extern TTF_Font *g_tab_font;
268
269 #define MAXRECTS 200
270   SDL_Rect rects [ MAXRECTS ];
271   SDL_Rect *dest = rects;
272   bzero ( dest, sizeof(SDL_Rect)*MAXRECTS );
273
274   unsigned short int tx, ty;
275
276   // background
277   if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
278     dest -> x = 0;
279     dest -> y = 0;
280     dest -> w = sdl_realscreen -> w;
281     dest -> h = sdl_realscreen -> h;
282     SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ );
283     dest++;
284   }
285
286   // title
287   //
288   // <items>
289   // description
290   // default
291   //
292   // controls help
293
294   // title
295   dest += conf_render_text ( g_big_font, "Minimenu Configuration", dest, 10, 10, CONF_UNSELECTED );
296   dest += conf_render_line ( dest, 45 );
297
298   // items
299   tx = 50; ty = 70;
300   unsigned char counter = first_visible;
301   while ( page [ counter ].text ) {
302
303     // item
304     conf_render_text ( g_tab_font, page [ counter ].text, dest, tx, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
305
306     // value
307     switch ( page [ counter ].type ) {
308     case ct_switch_page:
309       break;
310     case ct_reset:
311       break;
312     case ct_visible_tab_list:
313       {
314         char *v = pnd_conf_get_as_char ( g_conf, page [ counter ].key );
315         if ( v ) {
316           conf_render_text ( g_tab_font, v, dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
317         } else {
318           conf_render_text ( g_tab_font, "Not specified", dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
319         }
320       }
321       break;
322     case ct_cpu_speed:
323       {
324         int v = pnd_conf_get_as_int ( g_conf, page [ counter ].key );
325         conf_render_text ( g_tab_font, conf_format_int ( v, page [ counter ].type ), dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
326       }
327       break;
328     case ct_boolean:
329       {
330         int v = pnd_conf_get_as_int ( g_conf, page [ counter ].key );
331         conf_render_text ( g_tab_font, conf_format_int ( v, page [ counter ].type ), dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
332       }
333       break;
334     case ct_filename:
335       conf_render_text ( g_tab_font, page [ counter ].def, dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
336       break;
337     case ct_exit:
338       break;
339     case ct_nil:
340       break;
341     } // switch
342
343     // far enough?
344     if ( counter - first_visible >= max_visible - 1 ) {
345       break;
346     }
347
348     // next line
349     ty += 25;
350     counter++;
351   } // while
352
353   // description and default
354   if ( page [ selitem ].desc ) {
355     dest += conf_render_text ( g_tab_font, page [ selitem ].desc, dest, 380, 400, CONF_UNSELECTED );
356   }
357   if ( page [ selitem ].def ) {
358     char buffer [ 100 ] = "Default: ";
359
360     switch ( page [ selitem ].type ) {
361     case ct_boolean:
362       sprintf ( buffer + strlen ( buffer ), "%s", conf_format_int ( atoi ( page [ selitem ].def ), ct_boolean ) );
363       break;
364     case ct_cpu_speed:
365       sprintf ( buffer + strlen ( buffer ), "%s", conf_format_int ( atoi ( page [ selitem ].def ), ct_cpu_speed ) );
366       break;
367     case ct_filename:
368       sprintf ( buffer + strlen ( buffer ), "%s", page [ selitem ].def );
369       break;
370     case ct_nil:
371     case ct_switch_page:
372     case ct_reset:
373     case ct_exit:
374     case ct_visible_tab_list:
375       break;
376     } // switch
377
378     dest += conf_render_text ( g_tab_font, buffer, dest, 380, 420, CONF_UNSELECTED );
379   } else {
380     dest += conf_render_text ( g_tab_font, "No default value", dest, 380, 420, CONF_UNSELECTED );
381   }
382
383   // help
384   dest += conf_render_line ( dest, 380 );
385   dest += conf_render_text ( g_tab_font, "Up and down to switch selection", dest, 10, 400, CONF_UNSELECTED );
386   dest += conf_render_text ( g_tab_font, "Left and right to alter selected item", dest, 10, 420, CONF_UNSELECTED );
387   dest += conf_render_text ( g_tab_font, "B or Enter to activate an option", dest, 10, 440, CONF_UNSELECTED );
388
389   // update all the rects and send it all to sdl
390   // - at this point, we could probably just do 1 rect, of the
391   //   whole screen, and be faster :/
392   SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
393
394   return;
395 }
396
397 unsigned char conf_render_text ( TTF_Font *f, char *buffer, SDL_Rect *dest, unsigned int x, unsigned int y, unsigned char selected ) {
398   unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
399   unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
400   unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
401   unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
402
403   SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
404   SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a };
405
406   SDL_Surface *rtext = TTF_RenderText_Blended ( f, buffer, selected ? selfontcolor : tmpfontcolor );
407   dest -> x = x;
408   dest -> y = y;
409   SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
410   SDL_FreeSurface ( rtext );
411
412   return ( 1 );
413 }
414
415 unsigned char conf_render_line ( SDL_Rect *dest, unsigned int y ) {
416
417   dest -> x = 0;
418   dest -> y = y;
419
420   SDL_Surface *i = g_imagecache [ IMG_TAB_LINE ].i;
421
422   while ( dest -> x + i -> w < 800 ) {
423     SDL_BlitSurface ( i, NULL, sdl_realscreen, dest );
424     dest -> x += i -> w;
425   }
426
427   dest -> x = 0;
428   dest -> w = 480 - 10;
429   dest -> h = i -> h;
430
431   return ( 1 );
432 }
433
434 char *conf_format_int ( int v, change_type_e c ) {
435   static char buffer [ 50 ];
436   buffer [ 0 ] = '\0';
437
438   switch ( c ) {
439
440     case ct_cpu_speed:
441       {
442         if ( v == PND_CONF_BADNUM ) {
443           strcpy ( buffer, "Leave Alone" );
444         } else {
445           sprintf ( buffer, "%u", v );
446         }
447       }
448       break;
449
450     case ct_boolean:
451       {
452         if ( v == PND_CONF_BADNUM ) {
453           strcpy ( buffer, "FUBAR" );
454         } else if ( v == 0 ) {
455           strcpy ( buffer, "no" );
456         } else if ( v == 1 ) {
457           strcpy ( buffer, "yes" );
458         } else {
459           strcpy ( buffer, "FUBAR 2" );
460         }
461       }
462       break;
463
464   case ct_filename:
465     break;
466
467   case ct_exit:
468   case ct_reset:
469   case ct_switch_page:
470   case ct_visible_tab_list:
471   case ct_nil:
472     break;
473
474   } // switch
475
476   return ( buffer );
477 }
478
479 unsigned char conf_prepare_page ( confitem_t *page ) {
480   char buffer [ 100 ];
481
482   confitem_t *p = page;
483   confitem_t *template = NULL;
484   while ( p -> text != NULL ) {
485
486     if ( strcmp ( p -> text, CONF_APPLIST_TAG ) == 0 ) {
487       // rewrite this and subsequent items to be the listing
488       template = p;
489       p++;
490
491       template -> text = ""; // so it won't get repopulated again later
492
493       // for each app..
494       pnd_disco_t *iter = pnd_box_get_head ( g_active_apps );
495
496       while ( p - page < CONF_MAX_LISTLENGTH && iter ) {
497
498         p -> text = strndup ( iter -> title_en ? iter -> title_en : "Unnamed", 40 );
499         p -> desc = strdup ( iter -> unique_id );
500         p -> def = NULL;
501
502         sprintf ( buffer, "%s.%s", template -> key, iter -> unique_id );
503         p -> key = strdup ( buffer );
504         p -> type = ct_boolean;
505         p -> newhead = NULL;
506
507         // create to positive if not existant
508         if ( ! pnd_conf_get_as_char ( g_conf, buffer ) ) {
509           pnd_conf_set_char ( g_conf, buffer, "1" );
510         }
511
512         iter = pnd_box_get_next ( iter );
513         p++;
514       } // while not run off end of buffer
515
516       break;
517
518     } else if ( strcmp ( p -> text, CONF_TABLIST_TAG ) == 0 ) {
519       // rewrite this and subsequent items to be the listing
520       template = p;
521       p++;
522
523       template -> text = ""; // so it won't get repopulated again later
524
525       typedef struct {
526         mm_category_t *cat;
527         unsigned char n;
528       } _foo;
529       _foo cats [ 2 ];
530       cats [ 0 ].cat = g_categories;
531       cats [ 0 ].n = g_categorycount;
532       cats [ 1 ].cat = _categories_invis;
533       cats [ 1 ].n = _categories_inviscount;
534
535       // for each tab
536       unsigned int i, j;
537       for ( j = 0; j < 2; j++ ) {
538         mm_category_t *cc = cats [ j ].cat;
539         unsigned char cn = cats [ j ].n;
540         char catname [ 512 ];
541         char *actual_catname;
542
543         for ( i = 0;  i < cn; i++ ) {
544
545           // if this is an invisi-guy, it has parent-cat prepended; we want the real cat name.
546           strncpy ( catname, cc [ i ].catname, 500 );
547           if ( ( actual_catname = strchr ( catname, '.' ) ) ) {
548             actual_catname++; // skip the period
549           } else {
550             actual_catname = catname;
551           }
552           //fprintf ( stderr, "conf ui; got '%s' but showing '%s'\n", cc [ i ].catname, actual_catname );
553
554           if ( strncmp ( cc [ i ].catname, "All ", 4 ) == 0 ) {
555             // skip All tab, since it is generated, and managed by another config item
556             continue;
557           }
558
559           p -> text = strndup ( actual_catname, 40 );
560           p -> desc = NULL;
561           p -> def = NULL;
562
563           sprintf ( buffer, "%s.%s", template -> key, actual_catname );
564           p -> key = strdup ( buffer );
565           p -> type = ct_boolean;
566           p -> newhead = NULL;
567
568           // create to positive if not existant
569           if ( ! pnd_conf_get_as_char ( g_conf, buffer ) ) {
570             pnd_conf_set_char ( g_conf, buffer, "1" );
571           }
572
573           //fprintf ( stderr, "Created tabshow entry '%s'\n", cc [ i ].catname );
574
575           p++;
576         }
577
578       }
579
580       break;
581     }
582
583     p++;
584   }
585
586   return ( 1 );
587 }
588
589 unsigned char conf_write ( pnd_conf_handle h, char *fullpath ) {
590
591   // cherry pick the named keys from the conf-ui-array
592   // spill out the apps ands tabs 'broups' of conf keys
593
594   FILE *f = fopen ( fullpath, "w" );
595
596   if ( ! f ) {
597     return ( 0 );
598   }
599
600   fprintf ( f, "# Machine written; do not edit.\n" );
601   fprintf ( f, "# If you do edit, its KEY<tab>VALUE<newline>, nothing extra.\n" );
602   fprintf ( f, "\n" );
603
604   // deal with general keys
605   confitem_t *ci = page_general;
606   while ( ci -> text ) {
607     // does this item have a key? a value? if so, try to emit it.
608     if ( ci -> key ) {
609       char *v = pnd_conf_get_as_char ( h, ci -> key );
610       if ( v ) {
611         fprintf ( f, "%s\t%s\n", ci -> key, v );
612       }
613     }
614     ci++;
615   } // while
616
617   // deal with apps and tabs
618   char *v = pnd_box_get_head ( g_conf );
619   while ( v ) {
620
621     // does item begin with app or tab tag?
622     char *k = pnd_box_get_key ( v );
623
624     if ( k && 
625          ( strncasecmp ( k, "appshow.", 8 ) == 0 ||
626            strncasecmp ( k, "tabshow.", 8 ) == 0 )
627        )
628     {
629       fprintf ( f, "%s\t%s\n", k, v );
630     }
631
632     v = pnd_box_get_next ( v );
633   } // while
634
635   fclose ( f );
636
637   return ( 1 );
638 }
639
640 void conf_merge_into ( char *fullpath, pnd_conf_handle h ) {
641   FILE *f;
642   char buffer [ 1024 ];
643   char *s;
644
645   f = fopen ( fullpath, "r" );
646
647   if ( ! f ) {
648     return;
649   }
650
651   while ( fgets ( buffer, 1000, f ) ) {
652 #if 0
653     // trim trailing spaces
654     s = strchr ( buffer, ' ' );
655     if ( s ) {
656       *s = '\0';
657     }
658 #endif
659     // and #...
660     s = strchr ( buffer, '#' );
661     if ( s ) {
662       *s = '\0';
663     }
664     // and newline...
665     s = strchr ( buffer, '\n' );
666     if ( s ) {
667       *s = '\0';
668     }
669
670     // if theres anything left..
671     if ( buffer [ 0 ] != '\0' ) {
672
673       // assume FOO<tab>BAR<newline> since this really should be machine written, not human screwed with; or if human
674       // edited, assume they know what to do :) I even put in some 'docs' in the conf file.
675       char *s = strchr ( buffer, '\t' );
676
677       if ( s ) {
678         *s = '\0';
679
680         // set it; libpnd conf code already handles 'existant' or 'new'
681         pnd_conf_set_char ( h, buffer, s + 1 );
682
683       } // found a <tab>?
684
685     } // anything left?
686
687   } // while
688
689   fclose ( f );
690
691   return;
692 }
693
694 char *conf_determine_location ( pnd_conf_handle h ) {
695   static char path [ PATH_MAX ];
696
697   bzero ( path, PATH_MAX );
698
699   if ( ! getenv ( "HOME" ) ) {
700     return ( "." ); // wtf?
701   }
702
703   snprintf ( path, PATH_MAX - strlen(CONF_PREF_FILENAME) - 1, "%s/%s", getenv ( "HOME" ), CONF_PREF_FILENAME );
704
705   return ( path );
706 }
707
708 void conf_setup_missing ( pnd_conf_handle h ) {
709
710   confitem_t *ci = page_general;
711
712   while ( ci -> text ) {
713
714     // does this item have a default value?
715     if ( ci -> def ) {
716
717       // it does, so lets see if we can pull a current value in; if not, set one
718       char *v = pnd_conf_get_as_char ( h, ci -> key );
719
720       if ( ! v ) {
721         fprintf ( stderr, "pref conf: no value present in config, better set to default; key is '%s' def '%s'\n", ci -> key, ci -> def );
722         pnd_conf_set_char ( h, ci -> key, ci -> def );
723       }
724
725     } // has def?
726
727     ci++;
728   } // while
729
730   return;
731 }
732
733 void conf_reset_to_default ( pnd_conf_handle h ) {
734
735   // reset all keys to default value - if present
736   // reset all apps to show
737   // reset all tabs to show
738
739   // deal with general keys
740   confitem_t *ci = page_general;
741   while ( ci -> text ) {
742
743     // does this item have a default value? if so, set it
744     if ( ci -> key && ci -> def ) {
745       pnd_conf_set_char ( h, ci -> key, ci -> def );
746     }
747
748     ci++;
749   } // while
750
751   // deal with apps and tabs
752   char *v = pnd_box_get_head ( g_conf );
753   char *next;
754   while ( v ) {
755     next = pnd_box_get_next ( v );
756
757     // does item begin with app or tab tag?
758     char *k = pnd_box_get_key ( v );
759
760     if ( k && 
761          ( strncasecmp ( k, "appshow.", 8 ) == 0 ||
762            strncasecmp ( k, "tabshow.", 8 ) == 0 )
763        )
764     {
765       pnd_conf_set_char ( g_conf, k, "1" );
766     }
767
768     v = next;
769   } // while
770
771   return;
772 }
773