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