Added category mapping support to mmenu .. lets user define what categories to have...
[pandora-libraries.git] / minimenu / mmcat.c
1
2 #include <string.h>
3 #include <strings.h>
4 #include <stdlib.h>
5
6 #include "pnd_conf.h"
7 #include "pnd_logger.h"
8 #include "pnd_pxml.h"
9 #include "pnd_container.h"
10 #include "pnd_discovery.h"
11 #include "../lib/pnd_pathiter.h"
12
13 #include "mmenu.h"
14 #include "mmcache.h"
15 #include "mmcat.h"
16
17 mm_category_t g_categories [ MAX_CATS ];
18 unsigned char g_categorycount = 0;
19
20 mm_catmap_t g_catmaps [ MAX_CATS ];
21 unsigned char g_catmapcount = 0;
22
23 extern pnd_conf_handle g_conf;
24
25 unsigned char category_push ( char *catname, pnd_disco_t *app ) {
26   mm_category_t *c;
27
28   // check category list; if found, append app to the end of it.
29   // if not found, add it to the category list and plop the app in there.
30   // app's are just app-refs, which contain links to the disco-t list -- thus removal is only in one place, and
31   // an app can be in multiple categories if we like..
32   //
33
34   // find or create category
35   //
36
37   if ( ( c = category_query ( catname ) ) ) {
38     // category was found..
39   } else {
40     // category wasn't found..
41     pnd_log ( PND_LOG_DEFAULT, "New category '%s'\n", catname );
42     g_categories [ g_categorycount ].catname = strdup ( catname );
43     g_categories [ g_categorycount ].refs = NULL;
44     c = &(g_categories [ g_categorycount ]);
45     g_categorycount++;
46   }
47
48   if ( ! app ) {
49     return ( 1 ); // create cat, but skip app
50   }
51
52   // alloc and populate appref
53   //
54   mm_appref_t *ar = malloc ( sizeof(mm_appref_t) );
55   if ( ! ar ) {
56     return ( 0 );
57   }
58
59   bzero ( ar, sizeof(mm_appref_t) );
60
61   ar -> ref = app;
62
63   // plug it into category
64   //   and sort it on insert!
65 #if 0 // no sorting
66   ar -> next = c -> refs;
67   c -> refs = ar;
68 #else // with sorting
69   // if no refs at all, or new guy has no title, just stick it in at head
70   if ( c -> refs && ar -> ref -> title_en ) {
71     mm_appref_t *iter = c -> refs;
72     mm_appref_t *last = NULL;
73
74     while ( iter ) {
75
76       if ( iter -> ref -> title_en ) {
77         if ( strcmp ( ar -> ref -> title_en, iter -> ref -> title_en ) < 0 ) {
78           // new guy is smaller than the current guy!
79           break;
80         }
81       } else {
82         // since new guy must have a name by here, we're bigger than any guy who does not have a name
83         // --> continue
84       }
85
86       last = iter;
87       iter = iter -> next;
88     }
89
90     if ( iter ) {
91       // smaller than the current guy, so stitch in
92       if ( last ) {
93         ar -> next = iter;
94         last -> next = ar;
95       } else {
96         ar -> next = c -> refs;
97         c -> refs = ar;
98       }
99     } else {
100       // we're the biggest, just append to last
101       last -> next = ar;
102     }
103
104   } else {
105     ar -> next = c -> refs;
106     c -> refs = ar;
107   }
108 #endif
109   c -> refcount++;
110
111   return ( 1 );
112 }
113
114 mm_category_t *category_query ( char *catname ) {
115   unsigned char i;
116
117   for ( i = 0; i < g_categorycount; i++ ) {
118
119     if ( strcasecmp ( g_categories [ i ].catname, catname ) == 0 ) {
120       return ( &(g_categories [ i ]) );
121     }
122
123   }
124
125   return ( NULL );
126 }
127
128 void category_dump ( void ) {
129   unsigned int i;
130
131   // WHY AREN'T I SORTING ON INSERT?
132
133   // dump
134   for ( i = 0; i < g_categorycount; i++ ) {
135     pnd_log ( PND_LOG_DEFAULT, "Category %u: '%s' * %u\n", i, g_categories [ i ].catname, g_categories [ i ].refcount );
136     mm_appref_t *ar = g_categories [ i ].refs;
137
138     while ( ar ) {
139       pnd_log ( PND_LOG_DEFAULT, "  Appref %s\n", IFNULL(ar -> ref -> title_en,"No Name") );
140       ar = ar -> next;
141     }
142
143   } // for
144
145   return;
146 }
147
148 void category_freeall ( void ) {
149   unsigned int i;
150   mm_appref_t *iter, *next;
151
152   for ( i = 0; i < g_categorycount; i++ ) {
153
154     iter = g_categories [ i ].refs;
155
156     while ( iter ) {
157       next = iter -> next;
158       free ( iter );
159       iter = next;
160     }
161
162     g_categories [ i ].refs = NULL;
163
164   } // for
165
166   g_categorycount = 0;
167
168   return;
169 }
170
171 unsigned char category_map_setup ( void ) {
172
173   char *searchpath = pnd_box_get_head ( g_conf );
174
175   if ( ! searchpath ) {
176     return ( 0 );
177   }
178
179   // look through conf for useful keys
180   while ( searchpath ) {
181     char *k = pnd_box_get_key ( searchpath );
182
183     // does this key look like a category mapping key?
184     if ( strncasecmp ( k, "categories.@", 12 ) == 0 ) {
185       k += 12;
186
187       // iterate across 'words' in v, assigning catmaps to them
188       SEARCHCHUNK_PRE
189       {
190         //pnd_log ( pndn_debug, "target(%s) from(%s)\n", k, buffer );
191
192         category_push ( k, NULL );
193         g_catmaps [ g_catmapcount ].target = category_query ( k );
194         g_catmaps [ g_catmapcount ].from = strdup ( buffer );
195         g_catmapcount++;
196
197       }
198       SEARCHCHUNK_POST
199
200     } // if key looks like catmap
201
202     searchpath = pnd_box_get_next ( searchpath );
203   } // while each conf key
204
205   return ( 1 );
206 }
207
208 mm_category_t *category_map_query ( char *cat ) {
209   unsigned char i;
210
211   for ( i = 0; i < g_catmapcount; i++ ) {
212     if ( strcasecmp ( g_catmaps [ i ].from, cat ) == 0 ) {
213       return ( g_catmaps [ i ].target );
214     }
215   }
216
217   return ( NULL );
218 }
219
220 unsigned char category_meta_push ( char *catname, pnd_disco_t *app ) {
221   mm_category_t *cat;
222
223   // do we honour cat mapping at all?
224   if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_on", 0 ) ) {
225
226     // is this guy mapped?
227     cat = category_map_query ( catname );
228
229     if ( cat ) {
230       return ( category_push ( cat -> catname, app ) );
231     }
232
233     // not mapped.. but default?
234     if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_default_on", 0 ) ) {
235       char *def = pnd_conf_get_as_char ( g_conf, "categories.map_default_cat" );
236       if ( def ) {
237         return ( category_push ( def, app ) );
238       }
239     }
240
241   } // cat map is desired?
242
243   // not default, just do it
244   return ( category_push ( catname, app ) );
245 }