Menu reload support and clean of some memory leaks.

Menu reload is using DirWatch to monitor application menu folders for changes. With this, any change on some content will trigger
menu update. Update is trying to be safe as possible: when menu is open, a backup menu will be constructed and replaced as soon as menu
was closed; when is closed, it will be directly replaced.

This still can include races, but I'm hoping they will not occur.

Related to memory leaks: on couple of places in cpu applet, delete was wronly called instead delete[]. Also, when ede-panel exits,
AppletManager would not clean loaded apples. Strange. So now explicit clear was added on exit.
This commit is contained in:
Sanel Zukan 2012-08-06 14:52:09 +00:00
parent a45609e945
commit 1dde9f92da
6 changed files with 290 additions and 56 deletions

View File

@ -375,6 +375,9 @@ void Panel::show(void) {
void Panel::hide(void) {
Fl::remove_handler(x_events);
/* strange; this is not called when panel goes out :S */
mgr.clear();
save_config();
}

View File

@ -148,9 +148,9 @@ void CPUMonitor::clear()
if(!cpu) return;
for (int i = 0; i < samples(); i++)
delete cpu[i];
delete [] cpu[i];
delete cpu;
delete [] cpu;
cpu = 0;
m_old_samples = -1;

View File

@ -1,3 +1,23 @@
/*
* $Id$
*
* Copyright (C) 2012 Sanel Zukan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __MENURULES_H__
#define __MENURULES_H__

View File

@ -1,21 +1,64 @@
/*
* $Id$
*
* Copyright (C) 2012 Sanel Zukan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "Applet.h"
#include <time.h>
#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <edelib/MenuBase.h>
#include <edelib/Debug.h>
#include <edelib/Nls.h>
#include <edelib/Debug.h>
#include <edelib/StrUtil.h>
#include "XdgMenuReader.h"
#include "ede-icon.h"
/* by default is enabled */
#define EDE_PANEL_MENU_AUTOUPDATE 1
#ifdef EDE_PANEL_MENU_AUTOUPDATE
#include <edelib/DirWatch.h>
EDELIB_NS_USING(DirWatch)
EDELIB_NS_USING_LIST(4, (DW_CREATE, DW_MODIFY, DW_DELETE, DW_REPORT_RENAME))
/* when menu needs to be update, after how long to do real update */
#define MENU_UPDATE_TIMEOUT 5.0
/* elapsed seconds between changes reports from DirWatch; to prevent event throttling */
#define MENU_UPDATE_DIFF 5
#endif
EDELIB_NS_USING(MenuBase)
EDELIB_NS_USING(str_ends)
/* some of this code was ripped from Fl_Menu_Button.cxx */
class StartMenu : public MenuBase {
private:
MenuItem *mcontent;
XdgMenuContent *mcontent, *mcontent_pending;
time_t last_reload;
bool menu_opened;
void setup_menu(XdgMenuContent *m);
public:
StartMenu();
~StartMenu();
@ -23,9 +66,35 @@ public:
void popup(void);
void draw(void);
int handle(int e);
void reload_menu(void);
bool can_reload(void);
};
StartMenu::StartMenu() : MenuBase(0, 0, 80, 25, "EDE"), mcontent(NULL) {
#ifdef EDE_PANEL_MENU_AUTOUPDATE
static void menu_update_cb(void *data) {
StartMenu *m = (StartMenu*)data;
m->reload_menu();
E_DEBUG(E_STRLOC ": Scheduled menu update done\n");
}
static void folder_changed_cb(const char *dir, const char *w, int flags, void *data) {
StartMenu *m = (StartMenu*)data;
/* skip file renaming */
if(flags == DW_REPORT_RENAME) return;
if(w == NULL) w = "<none>";
/* add timeout for update so all filesystem changes gets applied */
if(str_ends(w, ".desktop") && m->can_reload()) {
E_DEBUG(E_STRLOC ": Scheduled menu update due changes inside inside '%s' folder ('%s':%i) in %i secs.\n", dir, w, flags, MENU_UPDATE_TIMEOUT);
Fl::add_timeout(MENU_UPDATE_TIMEOUT, menu_update_cb, m);
}
}
#endif
StartMenu::StartMenu() : MenuBase(0, 0, 80, 25, "EDE"), mcontent(NULL), mcontent_pending(NULL), last_reload(0), menu_opened(false) {
down_box(FL_NO_BOX);
labelfont(FL_HELVETICA_BOLD);
labelsize(14);
@ -33,21 +102,51 @@ StartMenu::StartMenu() : MenuBase(0, 0, 80, 25, "EDE"), mcontent(NULL) {
tooltip(_("Click here to choose and start common programs"));
/* load menu */
mcontent = xdg_menu_load();
setup_menu(mcontent);
if(mcontent) {
/* skip the first item, since it often contains only one submenu */
if(mcontent->submenu()) {
MenuItem *mc = mcontent + 1;
menu(mc);
} else {
menu(mcontent);
}
}
#ifdef EDE_PANEL_MENU_AUTOUPDATE
/*
* setup listeners on menu folders
* TODO: this list is constructed twice: the first time when menu was loaded and now
*/
StrList lst;
xdg_menu_applications_location(lst);
DirWatch::init();
StrListIt it = lst.begin(), ite = lst.end();
for(; it != ite; ++it)
DirWatch::add(it->c_str(), DW_CREATE | DW_MODIFY | DW_DELETE);
DirWatch::callback(folder_changed_cb, this);
#endif
}
StartMenu::~StartMenu() {
xdg_menu_delete(mcontent);
if(mcontent) xdg_menu_delete(mcontent);
if(mcontent_pending) xdg_menu_delete(mcontent_pending);
#ifdef EDE_PANEL_MENU_AUTOUPDATE
DirWatch::shutdown();
#endif
}
void StartMenu::setup_menu(XdgMenuContent *m) {
if(m == NULL) {
menu(NULL);
return;
}
MenuItem *item = xdg_menu_to_fltk_menu(m);
/* skip the first item, since it often contains only one submenu */
if(item && item->submenu()) {
menu(item + 1);
} else {
menu(item);
}
}
static StartMenu *pressed_menu_button = 0;
@ -78,6 +177,8 @@ void StartMenu::draw(void) {
}
void StartMenu::popup(void) {
menu_opened = true;
const MenuItem *m;
pressed_menu_button = this;
@ -93,6 +194,20 @@ void StartMenu::popup(void) {
picked(m);
pressed_menu_button = 0;
Fl::release_widget_pointer(mb);
menu_opened = false;
/* if we have menu that wants to be updated, swap them as soon as menu window was closed */
if(mcontent_pending) {
XdgMenuContent *tmp = mcontent;
mcontent = mcontent_pending;
setup_menu(mcontent);
mcontent_pending = tmp;
xdg_menu_delete(mcontent_pending);
mcontent_pending = NULL;
}
}
int StartMenu::handle(int e) {
@ -158,11 +273,38 @@ int StartMenu::handle(int e) {
return 0;
}
/* to prevent throttling of dirwatch events */
bool StartMenu::can_reload(void) {
#ifdef EDE_PANEL_MENU_AUTOUPDATE
time_t c, diff;
c = time(NULL);
diff = difftime(c, last_reload);
last_reload = c;
if(diff >= MENU_UPDATE_DIFF) return true;
#endif
return false;
}
void StartMenu::reload_menu(void) {
if(menu_opened) {
E_DEBUG("pending...\n");
mcontent_pending = xdg_menu_load();
} else {
xdg_menu_delete(mcontent);
mcontent = xdg_menu_load();
setup_menu(mcontent);
}
}
EDE_PANEL_APPLET_EXPORT (
StartMenu,
EDE_PANEL_APPLET_OPTION_ALIGN_LEFT,
"Main menu",
"0.1",
"0.2",
"empty",
"Sanel Zukan"
)

View File

@ -1,3 +1,23 @@
/*
* $Id$
*
* Copyright (C) 2012 Sanel Zukan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
@ -6,9 +26,7 @@
#include <FL/Fl_Shared_Image.H>
#include <edelib/TiXml.h>
#include <edelib/Debug.h>
#include <edelib/String.h>
#include <edelib/StrUtil.h>
#include <edelib/List.h>
#include <edelib/Util.h>
#include <edelib/FileTest.h>
#include <edelib/Directory.h>
@ -21,9 +39,7 @@
#include "MenuRules.h"
#include "XdgMenuReader.h"
EDELIB_NS_USING(String)
EDELIB_NS_USING(DesktopFile)
EDELIB_NS_USING(list)
EDELIB_NS_USING(IconLoader)
EDELIB_NS_USING(system_config_dirs)
EDELIB_NS_USING(system_data_dirs)
@ -204,25 +220,11 @@ static void menu_parse_context_append_desktop_files(MenuParseContext *ctx, const
static void menu_parse_context_append_desktop_files_from_xdg_data_dirs(MenuParseContext *ctx) {
StrList lst;
if(system_data_dirs(lst) < 1)
return;
xdg_menu_applications_location(lst);
StrListIt it = lst.begin(), it_end = lst.end();
String tmp;
for(; it != it_end; ++it) {
tmp = build_filename((*it).c_str(), "applications");
menu_parse_context_append_desktop_files(ctx, tmp.c_str(), tmp.c_str());
}
/*
* Add user directory too; the spec is unclear about this, but official menu spec tests
* requires it. Also, users will be able to add menu items without superuser permissions.
*/
String user_dir = user_data_dir();
tmp = build_filename(user_dir.c_str(), "applications");
menu_parse_context_append_desktop_files(ctx, tmp.c_str(), tmp.c_str());
for(; it != it_end; ++it)
menu_parse_context_append_desktop_files(ctx, it->c_str(), it->c_str());
}
static void scan_include_exclude_tag(TiXmlNode *elem, MenuRulesList &rules) {
@ -547,8 +549,8 @@ static MenuContext *menu_parse_context_to_menu_context(MenuParseContext *m,
* figure out the name first; if returns false, either menu should not be displayed, or something
* went wrong
*/
String *n, *ic;
bool should_be_displayed;
String *n = NULL, *ic = NULL;
bool should_be_displayed = false;
if(!menu_context_construct_name_and_get_icon(m, top, &n, &ic, &should_be_displayed))
return NULL;
@ -568,7 +570,6 @@ static MenuContext *menu_parse_context_to_menu_context(MenuParseContext *m,
ctx->name = n;
ctx->icon = ic;
ctx->display_it = should_be_displayed;
//E_DEBUG("+ Menu: %s %i\n", ctx->name->c_str(), m->include_rules.size());
/* fill MenuContext items, depending on what list was passed */
@ -868,9 +869,18 @@ void xdg_menu_dump_for_test_suite(void) {
menu_all_parse_lists_clear(pl, cl);
}
/* used only for xdg_menu_load() and xdg_menu_delete() */
static MenuParseList global_parse_list;
static MenuContextList global_context_list;
/* public API */
struct XdgMenuContent {
MenuItem *fltk_menu;
/*
* We are keeping them as fltk_menu references some objects, like text.
* This is since FLTK menu can't free label strings as MenuItem is plain struct.
*/
MenuParseList parse_list;
MenuContextList context_list;
};
static void item_cb(Fl_Widget*, void *en) {
DesktopEntry *entry = (DesktopEntry*)en;
@ -1009,20 +1019,37 @@ static unsigned int construct_edelib_menu(MenuContextList &lst, MenuItem *mi, un
return pos;
}
MenuItem *xdg_menu_load(void) {
/* assure they are empty */
E_RETURN_VAL_IF_FAIL(global_parse_list.empty() == true, NULL);
E_RETURN_VAL_IF_FAIL(global_context_list.empty() == true, NULL);
void xdg_menu_applications_location(StrList &lst) {
lst.clear();
if(system_data_dirs(lst) < 1)
return;
StrListIt it = lst.begin(), it_end = lst.end();
for(; it != it_end; ++it)
*it = build_filename(it->c_str(), "applications");
/*
* Add user directory too; the spec is unclear about this, but official menu spec tests
* requires it. Also, users will be able to add menu items without superuser permissions.
*/
String user_dir = user_data_dir();
lst.push_back(build_filename(user_dir.c_str(), "applications"));
}
XdgMenuContent *xdg_menu_load(void) {
XdgMenuContent *content = new XdgMenuContent();
/* load everything */
menu_all_parse_lists_load(global_parse_list, global_context_list);
menu_all_parse_lists_load(content->parse_list, content->context_list);
unsigned int sz = menu_context_list_count(global_context_list);
unsigned int sz = menu_context_list_count(content->context_list);
E_RETURN_VAL_IF_FAIL(sz > 0, NULL);
MenuItem *mi = new MenuItem[sz + 2]; /* plus logout + ending NULL */
unsigned int pos = construct_edelib_menu(global_context_list, mi, 0);
unsigned int pos = construct_edelib_menu(content->context_list, mi, 0);
mi[pos].text = NULL;
/*
@ -1032,10 +1059,20 @@ MenuItem *xdg_menu_load(void) {
MenuItem::init_extensions(&mi[pos]);
E_ASSERT(pos <= sz + 2);
return mi;
content->fltk_menu = mi;
return content;
}
void xdg_menu_delete(MenuItem *m) {
delete [] m;
menu_all_parse_lists_clear(global_parse_list, global_context_list);
void xdg_menu_delete(XdgMenuContent *m) {
E_RETURN_IF_FAIL(m != NULL);
delete [] m->fltk_menu;
menu_all_parse_lists_clear(m->parse_list, m->context_list);
delete m;
}
MenuItem *xdg_menu_to_fltk_menu(XdgMenuContent *m) {
E_RETURN_VAL_IF_FAIL(m != NULL, NULL);
return m->fltk_menu;
}

View File

@ -1,13 +1,45 @@
/*
* $Id$
*
* Copyright (C) 2012 Sanel Zukan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __XDGMENUREADER_H__
#define __XDGMENUREADER_H__
#include <edelib/MenuItem.h>
#include <edelib/List.h>
#include <edelib/String.h>
EDELIB_NS_USING(MenuItem)
EDELIB_NS_USING(String)
EDELIB_NS_USING(list)
typedef list<String> StrList;
typedef list<String>::iterator StrListIt;
void xdg_menu_dump_for_test_suite(void);
/* all locations where menu files are stored */
void xdg_menu_applications_location(StrList &lst);
MenuItem *xdg_menu_load(void);
void xdg_menu_delete(MenuItem *it);
struct XdgMenuContent;
XdgMenuContent *xdg_menu_load(void);
void xdg_menu_delete(XdgMenuContent *c);
MenuItem *xdg_menu_to_fltk_menu(XdgMenuContent *c);
#endif