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