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