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