The main code is as follows:
Code: Select all
//some constants
const static char * header = "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" \"http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd\">\n<Menu>\n\t<Name>Applications</Name>\n<MergeFile type=\"parent\">/etc/xdg/menus/lxde-applications.menu</MergeFile>\n";
const static char * footer = "\n</Menu>";
const static char * menu_start = "\n\t\t<Menu>";
const static char * menu_end = "\n\t\t</Menu>";
const static char * name_start = "\n\t\t\t<Name>";
const static char * name_end = "</Name>";
const static char * inc_exc = "\n\t\t\t<Include>\n\t\t\t</Include>\n\t\t\t<Exclude>\n\t\t\t</Exclude>";
const static char * menu_inc_exc = "\n\t\t\t<Include>\n\t\t\t<Filename>empty.desktop</Filename>\n\t\t\t</Include>\n\t\t\t<Exclude>\n\t\t\t<Filename>empty.desktop</Filename>\n\t\t\t</Exclude>";
const static char menus[12][17] = { "Accessories",
"Universal Access",
"Development",
"Education",
"Games",
"Graphics",
"Internet",
"Multimedia",
"Office",
"System",
"Other",
"DesktopSettings"
};
//hack for showing empty menu
const static char * empty_file = "[Desktop Entry]\nType=Application\nName=\nExec=\n";
/*
* <Menu>
* <Include>
* desktop_file.desktop
* </Include>
* <Menu>
*
* -find menu_name in string
* -if (found)
* find <Include>
* insert "<Filename>desktop_file</Filename>" after first <Include>
* else:
* error!
*
*/
static int add_launcher_to_menu(char * desktop_file, char * menu_name)
{
GString * file;
char * menu_pos;
char * include_pos;
char * user_file;
char * include_string;
user_file = get_user_file_path();
if(user_file_exists(user_file))
{
file = read_user_file();
}
else
{
file = create_user_file();
}
if(file)
{
//menu position
if((menu_pos= g_strstr_len(file->str,-1,menu_name)))
{
//include position
if((include_pos = g_strstr_len(menu_pos,-1,"<Include>")))
{
include_string = g_strconcat("<Filename>",desktop_file,".desktop","</Filename>",NULL);
file = g_string_insert(file,include_pos-(file->str)+9,include_string);
return write_user_file(file);
}
else
{
g_debug("Menu hasn't got <Include> section!");
}
}
else
{
g_debug("Menu name %s does not exist!",menu_name);
}
}
else
{
g_debug("File read eror!");
}
return 0;
}
/*
* <Menu>
* <Exclude>
* desktop_file.desktop
* </Exclude>
*
* -find menu_name in string
* - find <Include> & </Include>
* - if (desktop_file) between <Inc> & </Inc>
* remove <Filename>desktop_file</Filename>
* else:
* find <Exclude> and </Exclude>
* if (found):
* insert <Filename>"desktop_file"</Filename> after <Exclude>
* else
* error!
*
*/
static int remove_launcher_from_menu(char* desktop_file, char * menu_name)
{
GString * file;
char * user_file;
gchar * include_string;
//positions
char * menu_pos;
char * include_start;
char * include_end;
char * filename_pos;
char * filename_start;
char * filename_end;
char * exclude_start;
user_file = get_user_file_path();
if(user_file_exists(user_file))
{
file = read_user_file();
}
else
{
file = create_user_file();
}
if(file)
{
//menu position
if((menu_pos = g_strstr_len(file->str,-1,menu_name)))
{
//include_start position
if((include_start = g_strstr_len(menu_pos,-1,"<Include>")))
{
//include end position
include_end = g_strstr_len(include_start,-1,"</Include>");
//if already under includes
if((filename_pos = g_strstr_len(include_start,(include_end-include_start),desktop_file)))
{
//just erase it from <Include>
if((filename_start = g_strrstr_len(include_start,(filename_pos-include_start),"<Filename>")))
{
if((filename_end = g_strstr_len(filename_pos,-1,"</Filename>")))
{
//erase it
file = g_string_erase(file,filename_start-(file->str),(filename_end-filename_start)+11);
return write_user_file(file);
}
else
{
g_debug("No Filename end tag!");
}
}
else
{
g_debug("Desktop file not under <Filename> section!");
}
}
else
{
//find excludes and add it
exclude_start = g_strstr_len(menu_pos,-1,"<Exclude>");
include_string = g_strconcat("<Filename>",desktop_file,"</Filename>\n",NULL);
file = g_string_insert(file,exclude_start-(file->str)+10,include_string);
printf("%s",file->str);
return write_user_file(file);
}
}
else
{
g_debug("Menu hasn't got <Include> section!");
}
}
else
{
g_debug("Menu name %s does not exist!",menu_name);
}
}
return 0;
}
/*
*
* - find menu_name
* - if (found):
* -add <Menu><Name>submenu_name</Name><Directory>...</Directory><Include></Include>..</Menu> after first occurence of </Exclude>
*
*/
static int add_submenu_to_menu(char * submenu, char * menu_name)
{
GString * file;
char * user_file;
gchar * include_string;
//positions
char * menu_pos;
char * exclude_end;
user_file = get_user_file_path();
if(user_file_exists(user_file))
{
file = read_user_file();
}
else
{
file = create_user_file();
}
if(file)
{
//menu position
//check if menu already exists?
if((menu_pos= g_strstr_len(file->str,-1,menu_name)))
{
//find exc
if((exclude_end = g_strstr_len(menu_pos,-1,"</Exclude>")))
{
include_string = g_strconcat("<Menu>\n<Name>",submenu,"</Name>\n","<Directory>",submenu,".directory","</Directory>",menu_inc_exc,"</Menu>\n",NULL);
file = g_string_insert(file,exclude_end-(file->str)+10,include_string);
if(create_empty_desktop_file())
{
return write_user_file(file);
}
else
{
g_debug("Couldn't create empty desktop file!");
}
}
else
{
g_debug("Menu hasn't got Excludes end tag!");
}
}
else
{
g_debug("Menu name %s does not exist!",menu_name);
}
}
else
{
g_debug("Error reading file!");
}
return FALSE;
}
/*
*
* <Menu>
* <Menu>
* <Deleted/>
* </Menu>
*
* find menu_name
* - if (found):
* find submenu name
* if(found)
* -add <Deleted/> after first occurence of </Name>
*
*/
static int remove_submenu_from_menu(char * submenu, char * menu_name)
{
GString * file;
char * menu_pos;
char * submenu_pos;
char * name_pos;
char * user_file;
user_file = get_user_file_path();
if(user_file_exists(user_file))
{
file = read_user_file();
}
else
{
file = create_user_file();
}
if(file)
{
//menu position
if((menu_pos= g_strstr_len(file->str,-1,menu_name)))
{
//submenu position
if((submenu_pos= g_strstr_len(menu_pos,-1,submenu)))
{
//end of Name tag
if((name_pos = g_strstr_len(submenu_pos,-1,"</Name>")))
{
//include_string = g_strconcat("<Filename>",desktop_file,".desktop","</Filename>",NULL);
file = g_string_insert(file,name_pos-(file->str)+8,"<Deleted/>");
return write_user_file(file);
}
else
{
g_debug("Menu hasn't got end Name tag!");
}
}
else
{
g_debug("Menu %s has no submenu %s!",menu_name,submenu);
}
}
else
{
g_debug("Menu name %s does not exist!",menu_name);
}
}
else
{
g_debug("File read eror!");
}
return 0;
}
static char * get_user_file_path()
{
return g_build_filename(g_get_user_config_dir(), "menus", "lxde-applications.menu", NULL );
}
static int user_file_exists(char * filename)
{
return g_file_test (filename,G_FILE_TEST_EXISTS);
}
static GString * create_user_file()
{
GString * out;
out = g_string_new("");
//header
out = g_string_append(out,header);
//menus'n'stuff
int i;
for (i = 0; i < 12; i++)
{
//append menu start -> <Menu>
out = g_string_append(out,menu_start);
//append menu name -> <Name>
out = g_string_append(out,name_start);
//menu name
out = g_string_append(out,menus[i]);
//append menu name -> </Name>
out = g_string_append(out,name_end);
//append includes/excludes
out = g_string_append(out,inc_exc);
//append menu end -> </Menu>
out = g_string_append(out,menu_end);
}
//footer
out = g_string_append(out,footer);
return out;
}
static int create_empty_desktop_file()
{
char * empty_filename;
empty_filename = g_build_filename( g_get_user_data_dir(), "applications", "empty.desktop",NULL );
if(!user_file_exists(empty_filename))
{
return g_file_set_contents (empty_filename,empty_file,-1,NULL);
}
else
return TRUE;
}
static GString * read_user_file()
{
char * file;
char * in;
GString * out;
file = get_user_file_path();
if(g_file_get_contents (file,&in,NULL,NULL))
{
out = g_string_new(in);
}
g_free(in);
g_free(file);
return out;
}
static int write_user_file(GString * in)
{
char * filename;
filename = get_user_file_path();
return g_file_set_contents (filename,in->str,-1,NULL);
}
Code: Select all
//added
static void on_menu_item_delete(GtkMenuItem* item, MenuCacheApp* app)
{
char * menu_name;
char * desktop_id;
MenuCacheItem * parent;
parent = MENU_CACHE_ITEM(menu_cache_item_get_parent(MENU_CACHE_ITEM(app)));
menu_name = menu_cache_item_get_id(parent);
desktop_id = menu_cache_item_get_id(MENU_CACHE_ITEM(app));
g_debug("Removing launcher %s from menu %s.",desktop_id,menu_name);
/*
* FIXME:
* - backup user file?
* - error handling
*/
delete_launcher_from_menu(desktop_id,menu_name);
g_free(menu_name);
g_free(desktop_id);
}
//doesn't work yet
static void on_menu_delete(GtkMenuItem* item, MenuCacheApp* app)
{
char * menu_name;
char * menu_id;
MenuCacheItem * parent;
parent = MENU_CACHE_ITEM(menu_cache_item_get_parent(MENU_CACHE_ITEM(app)));
menu_name = menu_cache_item_get_id(parent);
menu_id = menu_cache_item_get_id(MENU_CACHE_ITEM(app));
g_debug("Removing submenu %s from menu %s.",menu_id,menu_name);
/*
* FIXME:
* - backup user file?
* - error handling
*/
delete_submenu_from_menu(menu_id,menu_name);
//g_free(menu_name);
//g_free(menu_id);
}
Code: Select all
//changed
if( ! ifile && ! ofile && !menu && !desktop)
return 1;
//two added arguments
static char* menu = NULL;
static char* desktop = NULL;
static GOptionEntry opt_entries[] =
{
{"no-display", 'n', 0, G_OPTION_ARG_NONE, &no_display, NULL, NULL},
{"input", 'i', 0, G_OPTION_ARG_FILENAME, &ifile, N_("input file"), N_("file name or desktop id")},
{"output", 'o', 0, G_OPTION_ARG_FILENAME, &ofile, N_("file name"), NULL},
//added
{"desktop", 'd', 0, G_OPTION_ARG_STRING, &desktop, N_("create desktop file and add it to the given menu name"), NULL},
{"menu", 'm', 0, G_OPTION_ARG_STRING, &menu, N_("create menu file and add it to the given menu name"), NULL},
{ NULL }
};
Code: Select all
//for LXPanel's 'Add new Item','Add new submenu' shortcut output filename
if(desktop)
{
ofile = g_build_filename( g_get_user_data_dir(), "applications", g_strconcat(gtk_entry_get_text( GTK_ENTRY(name)),".desktop",NULL),NULL );
}
else
if(menu)
{
ofile = g_build_filename( g_get_user_data_dir(), "desktop-directories",g_strconcat(gtk_entry_get_text( GTK_ENTRY(name)),".directory",NULL), NULL );
}
//and just before the end of the clause
if(desktop)
{
add_new_launcher_to_menu(gtk_entry_get_text( GTK_ENTRY(name)),desktop);
}
else
if(menu)
{
add_submenu_to_menu(gtk_entry_get_text(GTK_ENTRY(name)),menu);
}
Code: Select all
static void on_menu_item_add(GtkMenuItem* item, MenuCacheApp* app)
{
char* desktop;
char* menu_name;
char t[200];
menu_name = menu_cache_item_get_id(MENU_CACHE_ITEM(app));
g_debug("%s",menu_name);
char* argv[] = {
"lxshortcut",
"-d",
NULL,
NULL};
argv[2] = menu_name;
g_spawn_async( NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL );
//g_free( cats );
}
Code: Select all
//modified
static gboolean on_menu_button_press(GtkWidget* mi, GdkEventButton* evt, MenuCacheItem* data)
{
if( evt->button == 3 ) /* right */
{
char* tmp;
GtkWidget* item;
GtkMenu* p = GTK_MENU(gtk_menu_new());
tmp = g_find_program_in_path("lxshortcut");
if(menu_cache_item_get_type(data)==MENU_CACHE_TYPE_APP)
{
item = gtk_menu_item_new_with_label(_("Add to desktop"));
g_signal_connect(item, "activate", G_CALLBACK(on_add_menu_item_to_desktop), data);
gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
item = gtk_menu_item_new_with_label(_("Delete launcher"));
g_signal_connect(item, "activate", G_CALLBACK(on_menu_item_delete), data);
gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
}
else
if(menu_cache_item_get_type(data)==MENU_CACHE_TYPE_DIR)
{
if( tmp )
{
item = gtk_menu_item_new_with_label(_("Add launcher"));
g_signal_connect(item, "activate", G_CALLBACK(on_menu_item_add), data);
gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
item = gtk_menu_item_new_with_label(_("Add menu"));
g_signal_connect(item, "activate", G_CALLBACK(on_menu_add), data);
gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
}
item = gtk_menu_item_new_with_label(_("Delete menu"));
g_signal_connect(item, "activate", G_CALLBACK(on_menu_delete), data);
gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
}
if( tmp )
{
item = gtk_separator_menu_item_new();
gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
item = gtk_menu_item_new_with_label(_("Properties"));
g_signal_connect(item, "activate", G_CALLBACK(on_menu_item_properties), data);
gtk_menu_shell_append(GTK_MENU_SHELL(p), item);
}
g_free(tmp);
g_signal_connect(p, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL);
g_signal_connect(p, "deactivate", G_CALLBACK(restore_grabs), mi);
gtk_widget_show_all(GTK_WIDGET(p));
gtk_menu_popup(p, NULL, NULL, NULL, NULL, 0, evt->time);
return TRUE;
}
return FALSE;
}
Code: Select all
//in write_dir(...) function:
else if( type == GMENU_TREE_ITEM_ENTRY )
{
//edited
if(!gmenu_tree_entry_get_is_excluded((GMenuTreeEntry*)item))
write_entry( of, (GMenuTreeEntry*)item );
}
NOTE: this is only a POC - it needs more error checkng, cleaning up, testing and optimizations. Should it be better to use XML parser? (Have looked at GMarkup but I think it is a bit overkill, for simple inserting/deleting of a short string) Use only basic char arrays? (GString has simple erase/insert functions). Memory usage? (Many needles in haystack searches, (eventually?) long GStrings).
EDIT: added some new functions, more cleanup, changed right click menu.
EDIT2: deleting submenu now works, fixed a launcher delete bug, cleaned code to mostly use only glib functions.