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