return;
}
-unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh, char *fspath, unsigned char visiblep ) {
+static char *_normalize ( char *catname, char *parentcatname ) {
+ static char buffer [ 101 ];
+ snprintf ( buffer, 100, "%s*%s", catname, parentcatname ? parentcatname : "NoParent" );
+ return ( buffer );
+}
+
+unsigned char category_push ( char *catname, char *parentcatname, pnd_disco_t *app, pnd_conf_handle ovrh, char *fspath, unsigned char visiblep ) {
mm_category_t *c;
// check category list; if found, append app to the end of it.
// find or create category
//
- if ( ( c = pnd_box_find_by_key ( m_categories, catname ) ) ) {
+ if ( ( c = pnd_box_find_by_key ( m_categories, _normalize ( catname, parentcatname ) ) ) ) {
// category was found..
} else {
// category wasn't found..
//pnd_log ( PND_LOG_DEFAULT, "New category '%s'\n", catname );
- c = pnd_box_allocinsert ( m_categories, catname, sizeof(mm_category_t) );
+ c = pnd_box_allocinsert ( m_categories, _normalize ( catname, parentcatname ), sizeof(mm_category_t) );
c -> catname = strdup ( catname );
+ if ( parentcatname ) {
+ c -> parent_catname = strdup ( parentcatname );
+ }
+
if ( visiblep ) {
c -> catflags = CFNORMAL;
} else {
c -> catflags = CFHIDDEN;
}
+
+ // if user prefers subcats-as-folders, lets reflag this sucker
+ if ( parentcatname && pnd_conf_get_as_int_d ( g_conf, "tabs.subcat_as_folders", 1 ) ) {
+ //printf ( "subcat: %s parent: %s\n", catname, parentcatname ? parentcatname : "none" );
+ c -> catflags = CFSUBCAT;
+ }
+
c -> refs = NULL;
if ( fspath ) {
// are we in a directory browser, or looking at pnd-files?
if ( cat -> fspath ) {
+ // directory browser mode
if ( s1 == s2 ) {
return ( 0 ); // equal
}
+ // pnd tab
+ //
+
+ // if this is comparing subcat folder to subcat folder, or pnd to pnd, or pnd to subcat folder?
+ unsigned char s1sub = 0;
+ unsigned char s2sub = 0;
+ if ( s1 -> ref -> object_type == pnd_object_type_directory ) {
+ s1sub = 1;
+ }
+ if ( s2 -> ref -> object_type == pnd_object_type_directory ) {
+ s2sub = 1;
+ }
+
+ if ( ( s1sub ) && ( s2sub ) ) {
+ return ( strcasecmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
+ } else if ( ( s1sub ) && ( ! s2sub ) ) {
+ return ( -1 );
+ } else if ( ( ! s1sub ) && ( s2sub ) ) {
+ return ( 1 );
+ } else if ( ( ! s1sub ) && ( ! s2sub ) ) {
+ return ( strcasecmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
+ }
+
return ( strcasecmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
}
{
//pnd_log ( pndn_debug, "target(%s) from(%s)\n", k, buffer );
- category_push ( k, NULL, 0, NULL /* fspath */, 1 );
- g_catmaps [ g_catmapcount ].target = pnd_box_find_by_key ( m_categories, k );
+ category_push ( k, NULL /* parent cat */, NULL, 0, NULL /* fspath */, 1 );
+ g_catmaps [ g_catmapcount ].target = pnd_box_find_by_key ( m_categories, _normalize ( k, NULL /* TBD: hack, not sure if this is right default value */ ) );
g_catmaps [ g_catmapcount ].from = strdup ( buffer );
g_catmapcount++;
return ( NULL );
}
-unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco_t *app, pnd_conf_handle ovrh, unsigned char visiblep ) {
+unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco_t *app, pnd_conf_handle ovrh, unsigned char visiblep, unsigned char parentp ) {
mm_category_t *cat;
+#if 0 // prepending
char catnamebuffer [ 512 ] = "";
+#endif
+
+ //fprintf ( stderr, "meta push: '%s'\n", catname );
if ( ! catname ) {
return ( 1 ); // fine, just nada
}
- //fprintf ( stderr, "meta push: '%s'\n", catname );
+ // we don't screw with "All" category that mmenu.c generates on the fly
+ if ( strncmp ( catname, "All ", 4 ) == 0 ) {
+ goto category_done_audit;
+ }
+
+ // check if category came from an ovr-file; if so, we implicitly trust it instead of enforcing rules
+ if ( app -> object_flags & ( PND_DISCO_CUSTOM1 | PND_DISCO_CUSTOM2 ) ) {
+ goto category_done_audit;
+ }
+
+ // category cleansing; lets..
+ // - ensure we only let good freedesktop categories through
+ // - we fix case.. no more UtIliTy (a good cat, studlycaps)
+ // - no more good cats but swapped ancestry; Utility as child of something?
+ // - if bogus, we just ship it off to BAD_CAT
+
+ unsigned char cat_is_clean = 1;
+ freedesktop_cat_t *fdcat = NULL, *fdpcat = NULL;
+ fdcat = freedesktop_category_query ( catname, parentcatname );
+ if ( parentcatname ) {
+ fdpcat = freedesktop_category_query ( parentcatname, NULL );
+ }
+
+ // ensure requested cat is good
+ if ( ! fdcat ) {
+ // requested cat is bad, send it to Other
+ cat_is_clean = 0;
+ pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> bad cat\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+
+ // do the Other substitution right away, so remaining code has something to look at in fdcat
+ fdcat = freedesktop_category_query ( BADCATNAME, NULL );
+ catname = fdcat -> cat;
+ fdpcat = NULL;
+ parentcatname = NULL;
+
+ } else {
+ // use canonicle entry, so our Case is now correct!
+ catname = fdcat -> cat;
+ }
+
+ // ensure parent is good, if specified
+ if ( parentcatname ) {
+ if ( ! fdpcat ) {
+ // requested cat is bad, send it to Other
+ cat_is_clean = 0;
+ pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> parent bad cat\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+ // fix immediately so code doesn't explode
+ parentcatname = NULL;
+ } else {
+ // use canonicle entry, so our Case is now correct!
+ parentcatname = fdpcat -> cat;
+ }
+ }
+
+ // ensure ancestry is good
+ // - if cat request is for child, ensure its a child
+ // - if parent specified, ensure its a parent
+ // - if child specified, ensure its parent is the right parent(?!)
+ //
+ if ( parentcatname ) {
+ // implies catname request is for child, with parent parentcatname
+
+ if ( fdcat -> parent_cat == NULL ) {
+ // but wait, catname is actually a parent cat...
+ cat_is_clean = 0;
+ pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child, but FD says its a parent\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+ }
+ if ( fdpcat -> parent_cat ) {
+ // but wait, parent cat is actually a subcat!
+ cat_is_clean = 0;
+ pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> parent cat, FD says its a child\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+ }
+
+ } else {
+ // implies request is for a parent cat - itself has no parent
+
+ if ( fdcat -> parent_cat ) {
+ // but wait, cat actually has a parent!
+ cat_is_clean = 0;
+ pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be parent, FD says its a child\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+ }
+
+ }
+
+ // ensure that if this is a child cat, its parent is the right parent
+ if ( parentcatname ) {
+ if ( ( ! fdcat -> parent_cat ) ||
+ ( ! fdpcat ) )
+ {
+ // child cat points to a different parent than requested parent!
+ cat_is_clean = 0;
+ pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child of a cat which FD says is the wrong parent (1)\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+ } else if ( strcasecmp ( fdcat -> parent_cat, fdpcat -> cat ) != 0 ) {
+ // child cat points to a different parent than requested parent!
+ cat_is_clean = 0;
+ pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child of a cat which FD says is the wrong parent (2)\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+ }
+ }
+
+ // did testing fail? if so, bump to Other!
+ //
+ if ( ! cat_is_clean ) {
+ // set Other visibility
+ visiblep = cat_is_visible ( g_conf, BADCATNAME );
+ // fix cat request
+ fdcat = freedesktop_category_query ( BADCATNAME, NULL );
+ catname = fdcat -> cat;
+ // nullify parent cat request (if any)
+ fdpcat = NULL;
+ parentcatname = NULL;
+ } else {
+ //printf ( "PXML Category Pass: Cat request %s (parent %s)\n", catname, parentcatname ? parentcatname : "n/a" );
+ }
// push bad categories into Other (if we're not targeting All right now)
- if ( pnd_conf_get_as_int_d ( g_conf, "categories.good_cats_only", 1 ) ) {
+#if 0
+ if ( 1 /*pnd_conf_get_as_int_d ( g_conf, "categories.good_cats_only", 1 )*/ ) {
// don't audit All
if ( strncmp ( catname, "All ", 4 ) != 0 ) {
} // not All
} // good cats only?
+#endif
+
+ category_done_audit:
// if invisible, and a parent category name is known, prepend it for ease of use
+#if 0 // prepending
if ( ! visiblep && parentcatname ) {
snprintf ( catnamebuffer, 500, "%s.%s", parentcatname, catname );
catname = catnamebuffer;
}
+#endif
+
+ // if this is a subcat push, and its requesting special 'no subcat', then just ditch it
+ if ( parentcatname && strcmp ( catname, "NoSubcategory" ) == 0 ) {
+ return ( 1 );
+ }
// do we honour cat mapping at all?
if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_on", 0 ) ) {
cat = category_map_query ( catname );
if ( cat ) {
- category_push ( cat -> catname, app, ovrh, NULL /* fspath */, visiblep );
+ category_push ( cat -> catname, parentcatname /* parent cat */, app, ovrh, NULL /* fspath */, visiblep );
goto meta_done;
}
if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_default_on", 0 ) ) {
char *def = pnd_conf_get_as_char ( g_conf, "categories.map_default_cat" );
if ( def ) {
- category_push ( def, app, ovrh, NULL /* fspath */, visiblep );
+ category_push ( def, parentcatname /* parent cat */, app, ovrh, NULL /* fspath */, visiblep );
goto meta_done;
}
}
} // cat map is desired?
+ // is app already in the target cat? (ie: its being pushed twice due to cat mapping or Other'ing or something..)
+ if ( app ) {
+ if ( category_contains_app ( catname, parentcatname, app -> unique_id ) ) {
+ pnd_log ( pndn_warning, "App Fail: app (%s %s) is already in cat %s\n", app -> title_en ? app -> title_en : "no name?", app -> unique_id, catname );
+ return ( 1 ); // success, already there!
+ }
+ }
+
+ // are we not putting apps into parent cat, when subcat is present? if so..
+ // if the cat we're looking at now is the main (or main alt-cat), and we also have a subcat, then ditch it.
+ // so, is the cat we're looking at right now the apps main (or main alt) cat?
+ if ( parentp ) {
+ // and does this app have sub/altsub cats?
+ if ( ( parentp == 1 && ( app -> main_category1 || app -> main_category2 ) ) ||
+ ( parentp == 2 && ( app -> alt_category1 || app -> alt_category2 ) )
+ )
+ {
+ // and we're only desiring the subcat version of the app?
+ if ( pnd_conf_get_as_int_d ( g_conf, "tabs.subcat_to_parent", 1 ) == 0 ) {
+ // create the parent category, since we need to be able to place a folder here maybe
+ category_push ( catname, parentcatname /* parent cat */, NULL /* app */, NULL /* ovrh */, NULL /* fspath */, visiblep );
+ // bail
+ return ( 1 );
+ } // subcat to parent?
+ } // app has subcat?
+ } // tab we're looking at now is the main tab?
+
// not default, just do it
- category_push ( catname, app, ovrh, NULL /* fspath */, visiblep );
+ category_push ( catname, parentcatname /* parent cat */, app, ovrh, NULL /* fspath */, visiblep );
+
+ // if subcats as folders, then lets just make up a dummy app that pretends to be a folder,
+ // and stuff it into the parent cat
+ if ( parentcatname && pnd_conf_get_as_int_d ( g_conf, "tabs.subcat_as_folders", 1 ) && cat_is_visible ( g_conf, catname ) ) {
+
+ // it is implicit that since we're talking parentcat, its already been created in a previous call
+ // therefore, we need to..
+ // i) find the parent cat
+ // ii) ensure it already has a faux-disco container
+ // iii) ensure that disco container doesn't already contain a disco-entry for this subcat
+ // iv) create the dummy app folder by pushing the disco into the apprefs as normal
+ // v) create a dummy '..' for going back up, in the child
+
+ mm_category_t *pcat = pnd_box_find_by_key ( m_categories, _normalize ( parentcatname, NULL ) );
+
+ if ( ! pcat -> disco ) {
+ pcat -> disco = pnd_box_new ( pcat -> catname );
+ }
+
+ // if this subcat is already in the faux-disco list, then its probably already
+ // handled so we needn't concern ourselves anymore. If not, then we can
+ // create it and push it into the parent as a new 'app'
+ pnd_disco_t *disco = pnd_box_find_by_key ( pcat -> disco, catname );
+
+ if ( ! disco ) {
+
+ disco = pnd_box_allocinsert ( pcat -> disco, catname, sizeof(pnd_disco_t) );
+ if ( disco ) {
+
+ // create the subcat faux-disco entry, and register into parent cat .. if its visible
+ char uid [ 30 ];
+ sprintf ( uid, "%p", catname );
+
+ disco -> unique_id = strdup ( uid );
+ if ( strchr ( catname, '.' ) ) {
+ disco -> title_en = strdup ( strchr ( catname, '.' ) + 1 );
+ } else {
+ disco -> title_en = strdup ( catname );
+ }
+ disco -> object_flags = PND_DISCO_GENERATED;
+ disco -> object_type = pnd_object_type_directory; // suggest to Grid that its a dir
+ disco -> object_path = strdup ( _normalize ( catname, parentcatname ) );
+
+ category_push ( parentcatname, NULL /* parent cat */, disco, 0 /*ovrh*/, NULL /* fspath */, 1 /* visible */ );
+
+ // create .. faux-disco entry into child cat
+ disco = pnd_box_allocinsert ( pcat -> disco, catname, sizeof(pnd_disco_t) );
+
+ sprintf ( uid, "%p", uid );
+
+ disco -> unique_id = strdup ( uid );
+ disco -> title_en = strdup ( ".." );
+ disco -> object_flags = PND_DISCO_GENERATED;
+ disco -> object_type = pnd_object_type_directory; // suggest to Grid that its a dir
+
+ category_push ( catname, parentcatname /* parent cat */, disco, 0 /*ovrh*/, NULL /* fspath */, 1 /* visible */ );
+
+ } // making faux disco entries
+
+ } // disco already exist?
+
+ } // subcat as folder?
// hack :(
meta_done:
case DT_DIR:
if ( strcmp ( de -> d_name, "." ) == 0 ) {
// ignore ".", but ".." is fine
+ } else if ( strcmp ( de -> d_name, ".." ) == 0 && strcmp ( cat -> fspath, "/" ) == 0 ) {
+ // ignore ".." only if we're at the true root
} else {
disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
disco -> object_type = pnd_object_type_directory; // suggest to Grid that its a dir
disco -> object_flags = PND_DISCO_GENERATED;
disco -> object_path = strdup ( cat -> fspath );
disco -> object_filename = strdup ( de -> d_name );
- category_push ( cat -> catname, disco, 0 /* no ovr */, NULL /* fspath already set */, 1 /* visible */ );
+ category_push ( cat -> catname, NULL /* parent cat */, disco, 0 /* no ovr */, NULL /* fspath already set */, 1 /* visible */ );
// if a override icon exists, cache it up
cache_icon ( disco, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ),
pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_height", 50 ) );
// is this category desired?
if ( filter_mask == CFALL ) {
interested = 1;
+ } else if ( filter_mask == CFBYNAME ) {
+ char *foo = strchr ( param, '*' ) + 1; // where is parent folder in this? ("Game")
+ char catname [ 256 ];
+ memset ( catname, '\0', 256 );
+ strncpy ( catname, param, foo - param - 1 ); // figure out the subfolder name ("Emulator")
+ if ( strcasecmp ( iter -> catname, catname ) == 0 && // if the iter cat is same as the desired subfolder cat
+ iter -> parent_catname && // and we have a parent (since its a subfolder...)
+ strcasecmp ( iter -> parent_catname, foo ) == 0 ) // and sed parent is the right one (in case of subfolder name reuse in other cats!)
+ {
+ interested = 1;
+ }
} else if ( iter -> catflags == filter_mask ) {
interested = 1;
} // if
+ // desired tab?
if ( interested ) {
- // set us up the bomb; notice that we're just duplicating the pointers, not making
- // any new data here; none of this should ever be free'd!
- g_categories [ g_categorycount ] = iter;
- g_categorycount++;
- }
+
+ // lets only publish tabs that actually have an app in them .. just in case we've
+ // pruned out all the apps (sent them to Other or are suppressing apps in parent cats or
+ // something) at some point.
+ if ( iter -> fspath || iter -> refs ) {
+
+ // set us up the bomb; notice that we're just duplicating the pointers, not making
+ // any new data here; none of this should ever be free'd!
+ g_categories [ g_categorycount ] = iter;
+ g_categorycount++;
+
+ } // has apps?
+
+ } // interested?
// next
iter = pnd_box_get_next ( iter );
int category_index ( char *catname ) {
unsigned char i;
-
+
for ( i = 0; i < g_categorycount; i++ ) {
-
+
if ( strcasecmp ( g_categories [ i ] -> catname, catname ) == 0 ) {
return ( i );
}
-
+
}
-
+
return ( -1 );
}
+
+unsigned char category_contains_app ( char *catname, char *parentcatname, char *unique_id ) {
+
+ mm_category_t *c = pnd_box_find_by_key ( m_categories, _normalize ( catname, parentcatname ) );
+
+ if ( ! c ) {
+ return ( 0 ); // wtf?
+ }
+
+ if ( ! c -> refs ) {
+ return ( 0 ); // no apps at all
+ }
+
+ mm_appref_t *iter = c -> refs;
+
+ while ( iter ) {
+
+ if ( strcmp ( iter -> ref -> unique_id, unique_id ) == 0 ) {
+ return ( 1 );
+ }
+
+ iter = iter -> next;
+ }
+
+ return ( 0 );
+}