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