From 1f96f833d98d9f2b6ea8bf234f569d699d7910cc Mon Sep 17 00:00:00 2001 From: Sanel Zukan Date: Sat, 3 Oct 2009 07:33:08 +0000 Subject: [PATCH] Merging new panel in the trunk. --- ede-panel/Applet.h | 63 + ede-panel/AppletManager.cpp | 160 +++ ede-panel/AppletManager.h | 29 + ede-panel/Jamfile | 43 + ede-panel/Netwm.cpp | 564 +++++++++ ede-panel/Netwm.h | 89 ++ ede-panel/Panel.cpp | 357 ++++++ ede-panel/Panel.h | 36 + ede-panel/applets/Jamfile | 18 + ede-panel/applets/clock/Clock.cpp | 71 ++ ede-panel/applets/clock/Jamfile | 13 + ede-panel/applets/demo/AppletDemo.cpp | 26 + ede-panel/applets/demo/Jamfile | 13 + ede-panel/applets/pager/Jamfile | 13 + ede-panel/applets/pager/Pager.cpp | 125 ++ ede-panel/applets/pager/PagerButton.cpp | 40 + ede-panel/applets/pager/PagerButton.h | 22 + ede-panel/applets/quick-launch/Jamfile | 13 + .../applets/quick-launch/QuickLaunch.cpp | 117 ++ .../applets/quick-launch/edit-clear-icon.h | 55 + .../applets/quick-launch/icons/edit-clear.png | Bin 0 -> 771 bytes ede-panel/applets/start-menu/DesktopEntry.cpp | 182 +++ ede-panel/applets/start-menu/DesktopEntry.h | 101 ++ ede-panel/applets/start-menu/Jamfile | 21 + ede-panel/applets/start-menu/MenuRules.cpp | 156 +++ ede-panel/applets/start-menu/MenuRules.h | 40 + ede-panel/applets/start-menu/StartMenu.cpp | 168 +++ .../applets/start-menu/XdgMenuReader.cpp | 1031 +++++++++++++++++ ede-panel/applets/start-menu/XdgMenuReader.h | 13 + .../applets/start-menu/applications.menu | 55 + .../applets/start-menu/applications.menu.1 | 50 + ede-panel/applets/start-menu/ede-icon.h | 55 + .../applets/start-menu/ede-menu-spec-test.cpp | 6 + ede-panel/applets/start-menu/icons/ede.png | Bin 0 -> 542 bytes ede-panel/applets/start-menu/tests/ChangeLog | 70 ++ ede-panel/applets/start-menu/tests/README | 44 + ede-panel/applets/start-menu/tests/README.EDE | 7 + ede-panel/applets/start-menu/tests/TODO | 5 + .../start-menu/tests/data/Help.desktop | 68 ++ .../start-menu/tests/data/Home.desktop | 127 ++ .../start-menu/tests/data/KEdit.desktop | 34 + .../start-menu/tests/data/Kfind.desktop | 66 ++ .../start-menu/tests/data/apps.directory | 66 ++ .../start-menu/tests/data/freecell.desktop | 83 ++ .../start-menu/tests/data/gataxx.desktop | 81 ++ .../tests/data/gideon-legacy.desktop | 12 + .../start-menu/tests/data/gideon.desktop | 13 + .../start-menu/tests/data/glines-2.desktop | 83 ++ .../start-menu/tests/data/glines.desktop | 82 ++ .../start-menu/tests/data/hidden.desktop | 8 + .../start-menu/tests/data/hidden.directory | 5 + .../start-menu/tests/data/kate.desktop | 30 + .../start-menu/tests/data/kbabel.desktop | 69 ++ .../tests/data/kedit-legacy.desktop | 33 + .../start-menu/tests/data/kwrite.desktop | 84 ++ .../start-menu/tests/data/mahjongg-2.desktop | 86 ++ .../start-menu/tests/data/mahjongg.desktop | 86 ++ .../start-menu/tests/data/quanta.desktop | 10 + ede-panel/applets/start-menu/tests/expand | 12 + ede-panel/applets/start-menu/tests/menutest | 200 ++++ .../applets/start-menu/tests/tests/All/result | 4 + .../applets/start-menu/tests/tests/All/test | 26 + .../applets/start-menu/tests/tests/And/result | 1 + .../applets/start-menu/tests/tests/And/test | 29 + .../tests/tests/AppDir-relative/result | 3 + .../tests/tests/AppDir-relative/test | 27 + .../start-menu/tests/tests/AppDir/result | 3 + .../start-menu/tests/tests/AppDir/test | 4 + .../start-menu/tests/tests/Category/result | 3 + .../start-menu/tests/tests/Category/test | 29 + .../tests/tests/DefaultMergeDirs/result | 5 + .../tests/tests/DefaultMergeDirs/test | 43 + .../start-menu/tests/tests/Deleted/result | 2 + .../start-menu/tests/tests/Deleted/test | 35 + .../tests/tests/DesktopFileID/result | 4 + .../start-menu/tests/tests/DesktopFileID/test | 26 + .../start-menu/tests/tests/Directory/result | 3 + .../start-menu/tests/tests/Directory/test | 29 + .../tests/tests/DirectoryDir-relative/result | 3 + .../tests/tests/DirectoryDir-relative/test | 28 + .../start-menu/tests/tests/DirectoryDir/test | 4 + .../start-menu/tests/tests/Exclude/result | 3 + .../start-menu/tests/tests/Exclude/test | 32 + .../start-menu/tests/tests/Filename/result | 1 + .../start-menu/tests/tests/Filename/test | 26 + .../tests/tests/LegacyDir-Move/result | 2 + .../tests/tests/LegacyDir-Move/test | 39 + .../tests/tests/LegacyDir-relative/result | 9 + .../tests/tests/LegacyDir-relative/test | 42 + .../tests/tests/Merge-combined/result | 1 + .../tests/tests/Merge-combined/test | 46 + .../tests/tests/MergeDir-absolute/test | 3 + .../tests/tests/MergeDir-relative/result | 5 + .../tests/tests/MergeDir-relative/test | 61 + .../tests/tests/MergeFile-absolute/test | 3 + .../tests/tests/MergeFile-parent/result | 5 + .../tests/tests/MergeFile-parent/test | 61 + .../tests/tests/MergeFile-path/result | 5 + .../tests/tests/MergeFile-path/test | 61 + .../tests/tests/MergeFile-recursive/result | 5 + .../tests/tests/MergeFile-recursive/test | 58 + .../tests/tests/MergeFile-relative/result | 5 + .../tests/tests/MergeFile-relative/test | 44 + .../start-menu/tests/tests/MergeFile2/result | 5 + .../start-menu/tests/tests/MergeFile2/test | 57 + .../start-menu/tests/tests/MergeFile3/result | 5 + .../start-menu/tests/tests/MergeFile3/test | 56 + .../tests/tests/Move-collapsing/result | 4 + .../tests/tests/Move-collapsing/test | 40 + .../tests/tests/Move-ordering/result | 3 + .../start-menu/tests/tests/Move-ordering/test | 49 + .../tests/tests/Move-submenu/result | 1 + .../start-menu/tests/tests/Move-submenu/test | 32 + .../start-menu/tests/tests/Move/result | 2 + .../applets/start-menu/tests/tests/Move/test | 34 + .../start-menu/tests/tests/NoDisplay/result | 1 + .../start-menu/tests/tests/NoDisplay/test | 37 + .../start-menu/tests/tests/NoDisplay2/result | 1 + .../start-menu/tests/tests/NoDisplay2/test | 38 + .../tests/NotOnlyUnallocated-default/result | 2 + .../tests/NotOnlyUnallocated-default/test | 33 + .../tests/tests/OnlyUnallocated/result | 3 + .../tests/tests/OnlyUnallocated/test | 44 + .../applets/start-menu/tests/tests/Or/result | 4 + .../applets/start-menu/tests/tests/Or/test | 29 + .../tests/tests/boolean-logic/result | 3 + .../start-menu/tests/tests/boolean-logic/test | 36 + .../tests/tests/desktop-name-collision/result | 3 + .../tests/tests/desktop-name-collision/test | 53 + .../tests/tests/menu-multiple-matching/result | 5 + .../tests/tests/menu-multiple-matching/test | 36 + .../tests/official-categories/categories.list | 10 + .../tests/tests/official-categories/test | 73 ++ .../official-categories/unique-entry.desktop | 9 + .../tests/tests/submenu-collision/result | 5 + .../tests/tests/submenu-collision/test | 32 + .../applets/start-menu/tests/tet_menutest | 39 + ede-panel/applets/taskbar/Jamfile | 13 + ede-panel/applets/taskbar/TaskButton.cpp | 155 +++ ede-panel/applets/taskbar/TaskButton.h | 24 + ede-panel/applets/taskbar/Taskbar.cpp | 230 ++++ ede-panel/applets/taskbar/Taskbar.h | 30 + ede-panel/applets/taskbar/icons/window.png | Bin 0 -> 463 bytes ede-panel/applets/taskbar/icons/window.xpm | 76 ++ ede-panel/ede-panel.cpp | 13 + ede-panel/images/tile.xpm | 120 ++ 146 files changed, 7482 insertions(+) create mode 100644 ede-panel/Applet.h create mode 100644 ede-panel/AppletManager.cpp create mode 100644 ede-panel/AppletManager.h create mode 100644 ede-panel/Jamfile create mode 100644 ede-panel/Netwm.cpp create mode 100644 ede-panel/Netwm.h create mode 100644 ede-panel/Panel.cpp create mode 100644 ede-panel/Panel.h create mode 100644 ede-panel/applets/Jamfile create mode 100644 ede-panel/applets/clock/Clock.cpp create mode 100644 ede-panel/applets/clock/Jamfile create mode 100644 ede-panel/applets/demo/AppletDemo.cpp create mode 100644 ede-panel/applets/demo/Jamfile create mode 100644 ede-panel/applets/pager/Jamfile create mode 100644 ede-panel/applets/pager/Pager.cpp create mode 100644 ede-panel/applets/pager/PagerButton.cpp create mode 100644 ede-panel/applets/pager/PagerButton.h create mode 100644 ede-panel/applets/quick-launch/Jamfile create mode 100644 ede-panel/applets/quick-launch/QuickLaunch.cpp create mode 100644 ede-panel/applets/quick-launch/edit-clear-icon.h create mode 100644 ede-panel/applets/quick-launch/icons/edit-clear.png create mode 100644 ede-panel/applets/start-menu/DesktopEntry.cpp create mode 100644 ede-panel/applets/start-menu/DesktopEntry.h create mode 100644 ede-panel/applets/start-menu/Jamfile create mode 100644 ede-panel/applets/start-menu/MenuRules.cpp create mode 100644 ede-panel/applets/start-menu/MenuRules.h create mode 100644 ede-panel/applets/start-menu/StartMenu.cpp create mode 100644 ede-panel/applets/start-menu/XdgMenuReader.cpp create mode 100644 ede-panel/applets/start-menu/XdgMenuReader.h create mode 100644 ede-panel/applets/start-menu/applications.menu create mode 100644 ede-panel/applets/start-menu/applications.menu.1 create mode 100644 ede-panel/applets/start-menu/ede-icon.h create mode 100644 ede-panel/applets/start-menu/ede-menu-spec-test.cpp create mode 100644 ede-panel/applets/start-menu/icons/ede.png create mode 100644 ede-panel/applets/start-menu/tests/ChangeLog create mode 100644 ede-panel/applets/start-menu/tests/README create mode 100644 ede-panel/applets/start-menu/tests/README.EDE create mode 100644 ede-panel/applets/start-menu/tests/TODO create mode 100644 ede-panel/applets/start-menu/tests/data/Help.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/Home.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/KEdit.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/Kfind.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/apps.directory create mode 100644 ede-panel/applets/start-menu/tests/data/freecell.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/gataxx.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/gideon-legacy.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/gideon.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/glines-2.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/glines.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/hidden.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/hidden.directory create mode 100644 ede-panel/applets/start-menu/tests/data/kate.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/kbabel.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/kedit-legacy.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/kwrite.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/mahjongg-2.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/mahjongg.desktop create mode 100644 ede-panel/applets/start-menu/tests/data/quanta.desktop create mode 100755 ede-panel/applets/start-menu/tests/expand create mode 100755 ede-panel/applets/start-menu/tests/menutest create mode 100644 ede-panel/applets/start-menu/tests/tests/All/result create mode 100644 ede-panel/applets/start-menu/tests/tests/All/test create mode 100644 ede-panel/applets/start-menu/tests/tests/And/result create mode 100644 ede-panel/applets/start-menu/tests/tests/And/test create mode 100644 ede-panel/applets/start-menu/tests/tests/AppDir-relative/result create mode 100644 ede-panel/applets/start-menu/tests/tests/AppDir-relative/test create mode 100644 ede-panel/applets/start-menu/tests/tests/AppDir/result create mode 100644 ede-panel/applets/start-menu/tests/tests/AppDir/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Category/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Category/test create mode 100644 ede-panel/applets/start-menu/tests/tests/DefaultMergeDirs/result create mode 100644 ede-panel/applets/start-menu/tests/tests/DefaultMergeDirs/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Deleted/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Deleted/test create mode 100644 ede-panel/applets/start-menu/tests/tests/DesktopFileID/result create mode 100644 ede-panel/applets/start-menu/tests/tests/DesktopFileID/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Directory/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Directory/test create mode 100644 ede-panel/applets/start-menu/tests/tests/DirectoryDir-relative/result create mode 100644 ede-panel/applets/start-menu/tests/tests/DirectoryDir-relative/test create mode 100644 ede-panel/applets/start-menu/tests/tests/DirectoryDir/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Exclude/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Exclude/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Filename/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Filename/test create mode 100644 ede-panel/applets/start-menu/tests/tests/LegacyDir-Move/result create mode 100644 ede-panel/applets/start-menu/tests/tests/LegacyDir-Move/test create mode 100644 ede-panel/applets/start-menu/tests/tests/LegacyDir-relative/result create mode 100644 ede-panel/applets/start-menu/tests/tests/LegacyDir-relative/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Merge-combined/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Merge-combined/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeDir-absolute/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeDir-relative/result create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeDir-relative/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-absolute/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-parent/result create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-parent/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-path/result create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-path/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-recursive/result create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-recursive/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-relative/result create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile-relative/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile2/result create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile2/test create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile3/result create mode 100644 ede-panel/applets/start-menu/tests/tests/MergeFile3/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Move-collapsing/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Move-collapsing/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Move-ordering/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Move-ordering/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Move-submenu/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Move-submenu/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Move/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Move/test create mode 100644 ede-panel/applets/start-menu/tests/tests/NoDisplay/result create mode 100644 ede-panel/applets/start-menu/tests/tests/NoDisplay/test create mode 100644 ede-panel/applets/start-menu/tests/tests/NoDisplay2/result create mode 100644 ede-panel/applets/start-menu/tests/tests/NoDisplay2/test create mode 100644 ede-panel/applets/start-menu/tests/tests/NotOnlyUnallocated-default/result create mode 100644 ede-panel/applets/start-menu/tests/tests/NotOnlyUnallocated-default/test create mode 100644 ede-panel/applets/start-menu/tests/tests/OnlyUnallocated/result create mode 100644 ede-panel/applets/start-menu/tests/tests/OnlyUnallocated/test create mode 100644 ede-panel/applets/start-menu/tests/tests/Or/result create mode 100644 ede-panel/applets/start-menu/tests/tests/Or/test create mode 100644 ede-panel/applets/start-menu/tests/tests/boolean-logic/result create mode 100644 ede-panel/applets/start-menu/tests/tests/boolean-logic/test create mode 100644 ede-panel/applets/start-menu/tests/tests/desktop-name-collision/result create mode 100644 ede-panel/applets/start-menu/tests/tests/desktop-name-collision/test create mode 100644 ede-panel/applets/start-menu/tests/tests/menu-multiple-matching/result create mode 100644 ede-panel/applets/start-menu/tests/tests/menu-multiple-matching/test create mode 100644 ede-panel/applets/start-menu/tests/tests/official-categories/categories.list create mode 100644 ede-panel/applets/start-menu/tests/tests/official-categories/test create mode 100644 ede-panel/applets/start-menu/tests/tests/official-categories/unique-entry.desktop create mode 100644 ede-panel/applets/start-menu/tests/tests/submenu-collision/result create mode 100644 ede-panel/applets/start-menu/tests/tests/submenu-collision/test create mode 100755 ede-panel/applets/start-menu/tests/tet_menutest create mode 100644 ede-panel/applets/taskbar/Jamfile create mode 100644 ede-panel/applets/taskbar/TaskButton.cpp create mode 100644 ede-panel/applets/taskbar/TaskButton.h create mode 100644 ede-panel/applets/taskbar/Taskbar.cpp create mode 100644 ede-panel/applets/taskbar/Taskbar.h create mode 100644 ede-panel/applets/taskbar/icons/window.png create mode 100644 ede-panel/applets/taskbar/icons/window.xpm create mode 100644 ede-panel/ede-panel.cpp create mode 100644 ede-panel/images/tile.xpm diff --git a/ede-panel/Applet.h b/ede-panel/Applet.h new file mode 100644 index 0000000..cfeeeb8 --- /dev/null +++ b/ede-panel/Applet.h @@ -0,0 +1,63 @@ +#ifndef __APPLET_H__ +#define __APPLET_H__ + +#define EDE_PANEL_APPLET_INTERFACE_VERSION 0x01 + +class Fl_Widget; + +enum { + EDE_PANEL_APPLET_OPTION_RESIZABLE_H = (1 << 1), + EDE_PANEL_APPLET_OPTION_RESIZABLE_V = (1 << 2), + EDE_PANEL_APPLET_OPTION_ALIGN_LEFT = (1 << 3), + EDE_PANEL_APPLET_OPTION_ALIGN_RIGHT = (1 << 4) +}; + +struct AppletInfo { + const char *name; + const char *klass_name; + const char *version; + const char *icon; + const char *author; + unsigned long options; +}; + +typedef Fl_Widget* (*applet_create_t)(void); +typedef void (*applet_destroy_t)(Fl_Widget *); + +typedef AppletInfo* (*applet_get_info_t)(void); +typedef void (*applet_destroy_info_t)(AppletInfo *a); + +typedef float (*applet_version_t)(void); + +/* the main macro each applet library must implement */ + +#define EDE_PANEL_APPLET_EXPORT(klass, aoptions, aname, aversion, aicon, aauthor) \ +extern "C" Fl_Widget *ede_panel_applet_create(void) { \ + return new klass; \ +} \ + \ +extern "C" void ede_panel_applet_destroy(Fl_Widget *w) { \ + klass *k = (klass*)w; \ + delete k; \ +} \ + \ +extern "C" AppletInfo *ede_panel_applet_get_info(void) { \ + AppletInfo *a = new AppletInfo; \ + a->name = aname; \ + a->klass_name = #klass; \ + a->version = aversion; \ + a->icon = aicon; \ + a->author = aauthor; \ + a->options = aoptions; \ + return a; \ +} \ + \ +extern "C" void ede_panel_applet_destroy_info(AppletInfo *a) { \ + delete a; \ +} \ + \ +extern "C" int ede_panel_applet_get_iface_version(void) { \ + return EDE_PANEL_APPLET_INTERFACE_VERSION; \ +} + +#endif diff --git a/ede-panel/AppletManager.cpp b/ede-panel/AppletManager.cpp new file mode 100644 index 0000000..e249eba --- /dev/null +++ b/ede-panel/AppletManager.cpp @@ -0,0 +1,160 @@ +#include +#include +#include + +#include "AppletManager.h" +#include "Panel.h" + +EDELIB_NS_USING(String) +EDELIB_NS_USING(list) + +struct AppletData { + void *dl; + Fl_Widget *awidget; /* widget from the applet */ + AppletInfo *ainfo; /* applet informations */ + + applet_create_t create_func; + applet_destroy_t destroy_func; + + applet_destroy_info_t destroy_info_func; +}; + +static void clear_applet(AppletData *a) { + E_RETURN_IF_FAIL(a != NULL); + + /* clear applet information first */ + if(a->ainfo) { + E_DEBUG(E_STRLOC ": Cleaning class %s\n", a->ainfo->klass_name); + (a->destroy_info_func)(a->ainfo); + } + + /* clear applet specific suff */ + (a->destroy_func)(a->awidget); + + dlclose(a->dl); + delete a; +} + +AppletManager::~AppletManager() { + clear(); +} + +bool AppletManager::load(const char *path) { + dlerror(); + const char *dl_err = NULL; + + void *a = dlopen(path, RTLD_LAZY); + if(!a) { + dl_err = dlerror(); + E_WARNING(E_STRLOC ": Unable to load '%s' : '%s'\n", path, dl_err); + return false; + } + + /* first check if we have valid plugin by requesting version function */ + void *av = (applet_version_t*)dlsym(a, "ede_panel_applet_get_iface_version"); + if(!av) { + dl_err = dlerror(); + E_WARNING(E_STRLOC ": Invalid ede-panel plugin. The plugin will not be loaded\n"); + return false; + } + + applet_version_t avf = (applet_version_t)av; + if((avf)() > EDE_PANEL_APPLET_INTERFACE_VERSION) { + E_WARNING(E_STRLOC ": This plugin requries newer ede-panel version\n"); + return false; + } + + void *ac = (applet_create_t*)dlsym(a, "ede_panel_applet_create"); + if(!ac) { + dl_err = dlerror(); + E_WARNING(E_STRLOC ": Unable to find 'create' function in ede-panel plugin: '%s'\n", dl_err); + return false; + } + + void *ad = (applet_destroy_t*)dlsym(a, "ede_panel_applet_destroy"); + if(!ad) { + dl_err = dlerror(); + E_WARNING(E_STRLOC ": Unable to find 'destroy' function in ede-plugin plugin: '%s'\n", dl_err); + return false; + } + + void *agi = (applet_get_info_t*)dlsym(a, "ede_panel_applet_get_info"); + if(!agi) { + dl_err = dlerror(); + E_WARNING(E_STRLOC ": Don't know how to fetch applet information: '%s'\n", dl_err); + return false; + } + + void *adi = (applet_destroy_info_t*)dlsym(a, "ede_panel_applet_destroy_info"); + if(!adi) { + dl_err = dlerror(); + E_WARNING(E_STRLOC ": Don't know how to clean applet information: '%s'\n", dl_err); + return false; + } + + + AppletData *data = new AppletData; + data->dl = a; + data->awidget = NULL; + + data->create_func = (applet_create_t)ac; + data->destroy_func = (applet_destroy_t)ad; + data->destroy_info_func = (applet_destroy_info_t)adi; + + applet_get_info_t get_info_func = (applet_get_info_t)agi; + + /* load applet info first, so we can even use it when widget wasn't created yet */ + data->ainfo = (get_info_func)(); + E_DEBUG(E_STRLOC ": Loading class %s\n", data->ainfo->klass_name); + + applet_list.push_back(data); + return true; +} + +void AppletManager::clear(void) { + if(applet_list.empty()) + return; + + AListIter it = applet_list.begin(), it_end = applet_list.end(); + while(it != it_end) { + clear_applet(*it); + it = applet_list.erase(it); + } +} + +/* + * Must be called so widget can actually be added to FLTK parent. Widgets will be created and + * added to the group. + */ +void AppletManager::fill_group(Panel *p) { + AListIter it = applet_list.begin(), it_end = applet_list.end(); + AppletData *applet; + + for(; it != it_end; ++it) { + applet = *it; + + /* allocate memory for widget and append it to group */ + applet->awidget = applet->create_func(); + p->add(applet->awidget); + } +} + +void AppletManager::unfill_group(Panel *p) { + AListIter it = applet_list.begin(), it_end = applet_list.end(); + + for(; it != it_end; ++it) + p->remove((*it)->awidget); +} + +bool AppletManager::get_applet_options(Fl_Widget *o, unsigned long &opts) { + AListIter it = applet_list.begin(), it_end = applet_list.end(); + + for(; it != it_end; ++it) { + if(o == (*it)->awidget) { + opts = (*it)->ainfo->options; + return true; + } + } + + return false; +} diff --git a/ede-panel/AppletManager.h b/ede-panel/AppletManager.h new file mode 100644 index 0000000..3d08b2c --- /dev/null +++ b/ede-panel/AppletManager.h @@ -0,0 +1,29 @@ +#ifndef __APPLETMANAGER_H__ +#define __APPLETMANAGER_H__ + +#include +#include +#include "Applet.h" + +class Panel; +class Fl_Widget; +struct AppletData; + +typedef edelib::list AList; +typedef edelib::list::iterator AListIter; + +class AppletManager { +private: + AList applet_list; +public: + ~AppletManager(); + bool load(const char *path); + void clear(void); + void fill_group(Panel *p); + void unfill_group(Panel *p); + + bool get_applet_options(Fl_Widget *o, unsigned long &opts); + unsigned int napplets(void) const { return applet_list.size(); } +}; + +#endif diff --git a/ede-panel/Jamfile b/ede-panel/Jamfile new file mode 100644 index 0000000..ba597db --- /dev/null +++ b/ede-panel/Jamfile @@ -0,0 +1,43 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel-new ; + +# make it as library, so applets can use it too +ObjectC++Flags Netwm.cpp : -g3 -fPIC $(EDELIBINCLUDE) ; +StaticLibrary libnetwm : Netwm.cpp ; +NETWMLIB = -L$(SUBDIR) -lnetwm ; + +EdeProgram ede-panel : Panel.cpp AppletManager.cpp ede-panel.cpp ; +LinkAgainst ede-panel : $(NETWMLIB) ; + +# also must use this flag or program will crash +LINKFLAGS on ede-panel = -rdynamic ; + +rule PanelApplet +{ + local target ; + + # append default extension + target = $(<:S=$(SUFLIB_SHARED)) ; + + Main $(target) : $(>) ; + ObjectC++Flags $(>) : -fPIC -I [ FDirName $(TOP) ede-panel-new ] $(EDELIBINCLUDE) ; + + LinkAgainst $(target) : $(NETWMLIB) $(EDELIBLIB) $(EDELIB_GUI_LIB) $(FLTKLIB) $(STDLIB) ; + LINKFLAGS on $(target) = -shared -rdynamic [ on $(target) return $(LINKFLAGS) ] ; + LINKLIBS on $(target) = [ on $(target) return $(LINKLIBS) ] $(3) ; + + InstallData $(EDE_PANEL_APPLETS_DIR) : $(target) ; + + Clean clean : $(target) ; +} + +SubInclude TOP ede-panel-new applets ; diff --git a/ede-panel/Netwm.cpp b/ede-panel/Netwm.cpp new file mode 100644 index 0000000..bf6d084 --- /dev/null +++ b/ede-panel/Netwm.cpp @@ -0,0 +1,564 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "Netwm.h" + +EDELIB_NS_USING(list) + +struct NetwmCallbackData { + NetwmCallback cb; + void *data; +}; + +typedef list CbList; +typedef list::iterator CbListIt; + +static CbList callback_list; +static bool input_selected = false; + +static Atom _XA_NET_WORKAREA; +static Atom _XA_NET_WM_WINDOW_TYPE; +static Atom _XA_NET_WM_WINDOW_TYPE_DOCK; +static Atom _XA_NET_WM_WINDOW_TYPE_DESKTOP; +static Atom _XA_NET_WM_WINDOW_TYPE_SPLASH; +static Atom _XA_NET_WM_STRUT; +static Atom _XA_NET_NUMBER_OF_DESKTOPS; +static Atom _XA_NET_CURRENT_DESKTOP; +static Atom _XA_NET_DESKTOP_NAMES; +static Atom _XA_NET_CLIENT_LIST; +static Atom _XA_NET_WM_DESKTOP; +static Atom _XA_NET_WM_NAME; +static Atom _XA_NET_WM_VISIBLE_NAME; +static Atom _XA_NET_ACTIVE_WINDOW; +static Atom _XA_NET_CLOSE_WINDOW; + +static Atom _XA_WM_STATE; +static Atom _XA_NET_WM_STATE; +static Atom _XA_NET_WM_STATE_MAXIMIZED_HORZ; +static Atom _XA_NET_WM_STATE_MAXIMIZED_VERT; + +static Atom _XA_EDE_WM_ACTION; +static Atom _XA_EDE_WM_RESTORE_SIZE; + +/* this macro is set in xlib when X-es provides UTF-8 extension (since XFree86 4.0.2) */ +#if X_HAVE_UTF8_STRING +static Atom _XA_UTF8_STRING; +#else +# define _XA_UTF8_STRING XA_STRING +#endif + +static short atoms_inited = 0; + +static void init_atoms_once(void) { + if(atoms_inited) + return; + + _XA_NET_WORKAREA = XInternAtom(fl_display, "_NET_WORKAREA", False); + _XA_NET_WM_WINDOW_TYPE = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False); + _XA_NET_WM_WINDOW_TYPE_DOCK = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DOCK", False); + _XA_NET_WM_WINDOW_TYPE_SPLASH = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_SPLASH", False); + _XA_NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DESKTOP", False); + _XA_NET_WM_STRUT = XInternAtom(fl_display, "_NET_WM_STRUT", False); + _XA_NET_NUMBER_OF_DESKTOPS = XInternAtom(fl_display, "_NET_NUMBER_OF_DESKTOPS", False); + _XA_NET_CURRENT_DESKTOP = XInternAtom(fl_display, "_NET_CURRENT_DESKTOP", False); + _XA_NET_DESKTOP_NAMES = XInternAtom(fl_display, "_NET_DESKTOP_NAMES", False); + _XA_NET_CLIENT_LIST = XInternAtom(fl_display, "_NET_CLIENT_LIST", False); + _XA_NET_WM_DESKTOP = XInternAtom(fl_display, "_NET_WM_DESKTOP", False); + _XA_NET_WM_NAME = XInternAtom(fl_display, "_NET_WM_NAME", False); + _XA_NET_WM_VISIBLE_NAME = XInternAtom(fl_display, "_NET_WM_VISIBLE_NAME", False); + _XA_NET_ACTIVE_WINDOW = XInternAtom(fl_display, "_NET_ACTIVE_WINDOW", False); + _XA_NET_CLOSE_WINDOW = XInternAtom(fl_display, "_NET_CLOSE_WINDOW", False); + + _XA_WM_STATE = XInternAtom(fl_display, "WM_STATE", False); + _XA_NET_WM_STATE = XInternAtom(fl_display, "_NET_WM_STATE", False); + _XA_NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(fl_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + _XA_NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(fl_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + + _XA_EDE_WM_ACTION = XInternAtom(fl_display, "_EDE_WM_ACTION", False); + _XA_EDE_WM_RESTORE_SIZE = XInternAtom(fl_display, "_EDE_WM_RESTORE_SIZE", False); + +#ifdef X_HAVE_UTF8_STRING + _XA_UTF8_STRING = XInternAtom(fl_display, "UTF8_STRING", False); +#endif + + atoms_inited = 1; +} + +static int xevent_handler(int e) { + if(fl_xevent->type == PropertyNotify) { + int action = -1; + + E_DEBUG("==> %s\n", XGetAtomName(fl_display, fl_xevent->xproperty.atom)); + + if(fl_xevent->xproperty.atom == _XA_NET_NUMBER_OF_DESKTOPS) + action = NETWM_CHANGED_WORKSPACE_COUNT; + else if(fl_xevent->xproperty.atom == _XA_NET_DESKTOP_NAMES) + action = NETWM_CHANGED_WORKSPACE_NAMES; + else if(fl_xevent->xproperty.atom == _XA_NET_CURRENT_DESKTOP) + action = NETWM_CHANGED_CURRENT_WORKSPACE; + else if(fl_xevent->xproperty.atom == _XA_NET_WORKAREA) + action = NETWM_CHANGED_CURRENT_WORKAREA; + else if(fl_xevent->xproperty.atom == _XA_NET_ACTIVE_WINDOW) + action = NETWM_CHANGED_ACTIVE_WINDOW; + else if(fl_xevent->xproperty.atom == _XA_NET_WM_NAME || fl_xevent->xproperty.atom == XA_WM_NAME) + action = NETWM_CHANGED_WINDOW_NAME; + else if(fl_xevent->xproperty.atom == _XA_NET_WM_VISIBLE_NAME) + action = NETWM_CHANGED_WINDOW_VISIBLE_NAME; + else if(fl_xevent->xproperty.atom == _XA_NET_WM_DESKTOP) + action = NETWM_CHANGED_WINDOW_DESKTOP; + else if(fl_xevent->xproperty.atom == _XA_NET_CLIENT_LIST) + action = NETWM_CHANGED_WINDOW_LIST; + else + action = -1; + + if(action >= 0 && !callback_list.empty()) { + Window xid = fl_xevent->xproperty.window; + + /* TODO: locking here */ + CbListIt it = callback_list.begin(), it_end = callback_list.end(); + for(; it != it_end; ++it) { + /* execute callback */ + (*it).cb(action, xid, (*it).data); + } + } + } + + return 0; +} + +void netwm_callback_add(NetwmCallback cb, void *data) { + E_RETURN_IF_FAIL(cb != NULL); + + fl_open_display(); + init_atoms_once(); + + /* to catch _XA_NET_CURRENT_DESKTOP and such events */ + if(!input_selected) { + XSelectInput(fl_display, RootWindow(fl_display, fl_screen), PropertyChangeMask | StructureNotifyMask); + input_selected = true; + } + + NetwmCallbackData cb_data; + cb_data.cb = cb; + cb_data.data = data; + + callback_list.push_back(cb_data); + + Fl::remove_handler(xevent_handler); + Fl::add_handler(xevent_handler); +} + +void netwm_callback_remove(NetwmCallback cb) { + if(callback_list.empty()) + return; + + CbListIt it = callback_list.begin(), it_end = callback_list.end(); + while(it != it_end) { + if(cb == (*it).cb) { + it = callback_list.erase(it); + } else { + /* continue, in case the same callback is put multiple times */ + ++it; + } + } +} + +bool netwm_get_workarea(int& x, int& y, int& w, int &h) { + init_atoms_once(); + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop; + x = y = w = h = 0; + + int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen), + _XA_NET_WORKAREA, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, (unsigned char**)&prop); + + if(status != Success) + return false; + + CARD32* val = (CARD32*)prop; + if(val) { + x = val[0]; + y = val[1]; + w = val[2]; + h = val[3]; + + XFree((char*)val); + return true; + } + + return false; +} + +void netwm_make_me_dock(Fl_Window *win) { + init_atoms_once(); + + XChangeProperty(fl_display, fl_xid(win), _XA_NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, + (unsigned char*)&_XA_NET_WM_WINDOW_TYPE_DOCK, sizeof(Atom)); +} + +void netwm_set_window_strut(Fl_Window *win, int left, int right, int top, int bottom) { + init_atoms_once(); + + CARD32 strut[4] = { left, right, top, bottom }; + + XChangeProperty(fl_display, fl_xid(win), _XA_NET_WM_STRUT, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*)&strut, sizeof(CARD32) * 4); +} + +int netwm_get_workspace_count(void) { + init_atoms_once(); + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop; + + int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen), + _XA_NET_NUMBER_OF_DESKTOPS, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, + (unsigned char**)&prop); + + if(status != Success || !prop) + return -1; + + int ns = int(*(long*)prop); + XFree(prop); + return ns; +} + +/* change current workspace */ +void netwm_change_workspace(int n) { + init_atoms_once(); + + XEvent xev; + Window root_win = RootWindow(fl_display, fl_screen); + + memset(&xev, 0, sizeof(xev)); + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = root_win; + xev.xclient.display = fl_display; + xev.xclient.message_type = _XA_NET_CURRENT_DESKTOP; + xev.xclient.format = 32; + xev.xclient.data.l[0] = (long)n; + xev.xclient.data.l[1] = CurrentTime; + + XSendEvent (fl_display, root_win, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XSync(fl_display, True); +} + +int netwm_get_current_workspace(void) { + init_atoms_once(); + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop; + + int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen), + _XA_NET_CURRENT_DESKTOP, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, + (unsigned char**)&prop); + + if(status != Success || !prop) + return -1; + + int ns = int(*(long*)prop); + XFree(prop); + return ns; +} + +int netwm_get_workspace_names(char**& names) { + init_atoms_once(); + + /* FIXME: add _NET_SUPPORTING_WM_CHECK and _NET_SUPPORTED ??? */ + XTextProperty wnames; + XGetTextProperty(fl_display, RootWindow(fl_display, fl_screen), &wnames, _XA_NET_DESKTOP_NAMES); + + /* if wm does not understainds _NET_DESKTOP_NAMES this is not set */ + if(!wnames.nitems || !wnames.value) + return 0; + + int nsz; + + /* + * FIXME: Here should as alternative Xutf8TextPropertyToTextList since + * many wm's set UTF8_STRING property. Below is XA_STRING and for UTF8_STRING + * will fail. + */ + if(!XTextPropertyToStringList(&wnames, &names, &nsz)) { + XFree(wnames.value); + return 0; + } + + XFree(wnames.value); + return nsz; +} + +int netwm_get_mapped_windows(Window **windows) { + init_atoms_once(); + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop = 0; + + int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen), + _XA_NET_CLIENT_LIST, 0L, 0x7fffffff, False, XA_WINDOW, &real, &format, &n, &extra, + (unsigned char**)&prop); + + if(status != Success || !prop) + return -1; + + *windows = (Window*)prop; + return n; +} + +int netwm_get_window_workspace(Window win) { + E_RETURN_VAL_IF_FAIL(win >= 0, -1); + + init_atoms_once(); + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop = 0; + + int status = XGetWindowProperty(fl_display, win, + _XA_NET_WM_DESKTOP, 0L, 0x7fffffff, False, XA_CARDINAL, &real, &format, &n, &extra, + (unsigned char**)&prop); + + if(status != Success || !prop) + return -1; + + //int ns = int(*(long*)prop); + unsigned long desk = (unsigned long)(*(long*)prop); + XFree(prop); + + /* sticky */ + if(desk == 0xFFFFFFFF || desk == 0xFFFFFFFE) + return -1; + + return desk; +} + +int netwm_manageable_window(Window win) { + init_atoms_once(); + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop = 0; + + int status = XGetWindowProperty(fl_display, win, + _XA_NET_WM_WINDOW_TYPE, 0L, sizeof(Atom), False, XA_ATOM, &real, &format, &n, &extra, + (unsigned char**)&prop); + + if(status != Success || !prop) + return -1; + + Atom type = *(Atom*)prop; + XFree(prop); + + if(type == _XA_NET_WM_WINDOW_TYPE_DESKTOP || + type == _XA_NET_WM_WINDOW_TYPE_SPLASH || + type == _XA_NET_WM_WINDOW_TYPE_DOCK) + { + return 0; + } + + return 1; +} + +char *netwm_get_window_title(Window win) { + init_atoms_once(); + + XTextProperty xtp; + char *title = NULL, *ret = NULL; + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop = 0; + + int status = XGetWindowProperty(fl_display, win, + _XA_NET_WM_NAME, 0L, 0x7fffffff, False, _XA_UTF8_STRING , &real, &format, &n, &extra, + (unsigned char**)&prop); + + if(status == Success && prop) { + title = (char *)prop; + ret = strdup(title); + XFree(prop); + } else { + /* try WM_NAME */ + if(!XGetWMName(fl_display, win, &xtp)) + return NULL; + + if(xtp.encoding == XA_STRING) { + ret = strdup((const char*)xtp.value); + } else { +#if X_HAVE_UTF8_STRING + char **lst; + int lst_sz; + + status = Xutf8TextPropertyToTextList(fl_display, &xtp, &lst, &lst_sz); + if(status == Success && lst_sz > 0) { + ret = strdup((const char*)*lst); + XFreeStringList(lst); + } else { + ret = strdup((const char*)xtp.value); + } +#else + ret = strdup((const char*)tp.value); +#endif + } + + XFree(xtp.value); + } + + return ret; +} + +Window netwm_get_active_window(void) { + init_atoms_once(); + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop = 0; + + int status = XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen), + _XA_NET_ACTIVE_WINDOW, 0L, sizeof(Atom), False, XA_WINDOW, &real, &format, &n, &extra, + (unsigned char**)&prop); + + if(status != Success || !prop) + return -1; + + int ret = int(*(long*)prop); + XFree(prop); + + return (Window)ret; +} + +void netwm_set_active_window(Window win) { + init_atoms_once(); + + XEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = win; + xev.xclient.display = fl_display; + xev.xclient.message_type = _XA_NET_ACTIVE_WINDOW; + xev.xclient.format = 32; + xev.xclient.data.l[0] = (long)win; + xev.xclient.data.l[1] = CurrentTime; + + XSendEvent (fl_display, RootWindow(fl_display, fl_screen), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XSync(fl_display, True); +} + +void netwm_maximize_window(Window win) { + init_atoms_once(); + + XEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = win; + xev.xclient.display = fl_display; + xev.xclient.message_type = _XA_NET_WM_STATE; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 0; + xev.xclient.data.l[1] = _XA_NET_WM_STATE_MAXIMIZED_HORZ; + xev.xclient.data.l[2] = _XA_NET_WM_STATE_MAXIMIZED_VERT; + + XSendEvent (fl_display, RootWindow(fl_display, fl_screen), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XSync(fl_display, True); +} + +void netwm_close_window(Window win) { + init_atoms_once(); + + XEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = win; + xev.xclient.display = fl_display; + xev.xclient.message_type = _XA_NET_CLOSE_WINDOW; + xev.xclient.format = 32; + xev.xclient.data.l[0] = (long)win; + xev.xclient.data.l[1] = CurrentTime; + + XSendEvent (fl_display, RootWindow(fl_display, fl_screen), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XSync(fl_display, True); +} + +void wm_ede_restore_window(Window win) { + init_atoms_once(); + + XEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = win; + xev.xclient.display = fl_display; + xev.xclient.message_type = _XA_EDE_WM_ACTION; + xev.xclient.format = 32; + xev.xclient.data.l[0] = _XA_EDE_WM_RESTORE_SIZE; + xev.xclient.data.l[1] = CurrentTime; + + XSendEvent (fl_display, RootWindow(fl_display, fl_screen), False, + SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XSync(fl_display, True); +} + +WmStateValue wm_get_window_state(Window win) { + init_atoms_once(); + + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop = 0; + + int status = XGetWindowProperty(fl_display, win, + _XA_WM_STATE, 0L, sizeof(Atom), False, _XA_WM_STATE, &real, &format, &n, &extra, + (unsigned char**)&prop); + + if(status != Success || !prop) + return WM_STATE_NONE; + + WmStateValue ret = WmStateValue(*(long*)prop); + XFree(prop); + + return ret; +} + +void wm_set_window_state(Window win, WmStateValue state) { + E_RETURN_IF_FAIL((int)state > 0); + + if(state == WM_STATE_WITHDRAW) { + XWithdrawWindow(fl_display, win, fl_screen); + XSync(fl_display, True); + } else if(state == WM_STATE_ICONIC) { + XIconifyWindow(fl_display, win, fl_screen); + XSync(fl_display, True); + } + + /* nothing for WM_STATE_NORMAL */ +} diff --git a/ede-panel/Netwm.h b/ede-panel/Netwm.h new file mode 100644 index 0000000..204e164 --- /dev/null +++ b/ede-panel/Netwm.h @@ -0,0 +1,89 @@ +#ifndef __NETWM_H__ +#define __NETWM_H__ + +#include + +/* NETWM helpers for panel and applets */ + +class Fl_Window; + +enum { + NETWM_CHANGED_WORKSPACE_COUNT, + NETWM_CHANGED_WORKSPACE_NAMES, + NETWM_CHANGED_CURRENT_WORKSPACE, + NETWM_CHANGED_CURRENT_WORKAREA, + NETWM_CHANGED_ACTIVE_WINDOW, + NETWM_CHANGED_WINDOW_NAME, + NETWM_CHANGED_WINDOW_VISIBLE_NAME, + NETWM_CHANGED_WINDOW_DESKTOP, + NETWM_CHANGED_WINDOW_LIST +}; + +enum WmStateValue { + WM_STATE_NONE = -1, + WM_STATE_WITHDRAW = 0, + WM_STATE_NORMAL = 1, + WM_STATE_ICONIC = 3 +}; + +typedef void (*NetwmCallback)(int action, Window xid, void *data); + +/* register callback for NETWM_* changes */ +void netwm_callback_add(NetwmCallback cb, void *data = 0); + +/* remove callback if exists */ +void netwm_callback_remove(NetwmCallback cb); + +/* get current workare set by window manager; return false if fails */ +bool netwm_get_workarea(int& x, int& y, int& w, int &h); + +/* make 'dock' type from given window */ +void netwm_make_me_dock(Fl_Window *win); + +/* resize area by setting offsets to each side; 'win' will be outside that area */ +void netwm_set_window_strut(Fl_Window *win, int left, int right, int top, int bottom); + +/* return number of workspaces */ +int netwm_get_workspace_count(void); + +/* change current workspace */ +void netwm_change_workspace(int n); + +/* current visible workspace; workspaces are starting from 0 */ +int netwm_get_current_workspace(void); + +/* workspace names; return number of allocated ones; call on this XFreeStringList(names) */ +int netwm_get_workspace_names(char**& names); + +/* get a list of mapped windows; returns number in the list, or -1 if fails; call XFree when done */ +int netwm_get_mapped_windows(Window **windows); + +/* returns -2 on error, -1 on sticky, else is desktop */ +int netwm_get_window_workspace(Window win); + +/* return 1 if given window is manageable (desktop/dock/splash types are not manageable) */ +int netwm_manageable_window(Window win); + +/* return window title or NULL if fails; call free() on returned string */ +char *netwm_get_window_title(Window win); + +/* return ID of currently focused window; if fails, return -1 */ +Window netwm_get_active_window(void); + +/* try to focus given window */ +void netwm_set_active_window(Window win); + +/* maximize window */ +void netwm_maximize_window(Window win); + +/* close window */ +void netwm_close_window(Window win); + +/* edewm specific: restore window to previous state */ +void wm_ede_restore_window(Window win); + +/* not part of NETWM, but helpers until _NET_WM_STATE_* is implemented */ +WmStateValue wm_get_window_state(Window win); +void wm_set_window_state(Window win, WmStateValue state); + +#endif diff --git a/ede-panel/Panel.cpp b/ede-panel/Panel.cpp new file mode 100644 index 0000000..c118f1e --- /dev/null +++ b/ede-panel/Panel.cpp @@ -0,0 +1,357 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "Panel.h" +#include "Netwm.h" + +/* empty space from left and right panel border */ +#define INITIAL_SPACING 5 + +/* space between each applet */ +#define DEFAULT_SPACING 5 + +/* default panel height */ +#define DEFAULT_PANEL_H 35 + +#undef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#undef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +EDELIB_NS_USING(list) +EDELIB_NS_USING(Resource) +EDELIB_NS_USING(String) +EDELIB_NS_USING(window_xid_create) +EDELIB_NS_USING(build_filename) + +typedef list WidgetList; +typedef list::iterator WidgetListIt; + +inline bool intersects(Fl_Widget *o1, Fl_Widget *o2) { + return (MAX(o1->x(), o2->x()) <= MIN(o1->x() + o1->w(), o2->x() + o2->w()) && + MAX(o1->y(), o2->y()) <= MIN(o1->y() + o1->h(), o2->y() + o2->h())); +} + +/* horizontaly centers widget in the panel */ +static void center_widget_h(Fl_Widget *o, Panel *self) { + int ph, wy; + + ph = self->panel_h() / 2; + wy = ph - (o->h() / 2); + o->position(o->x(), wy); +} + +static void add_from_list(WidgetList &lst, Panel *self, int &X, bool inc) { + WidgetListIt it = lst.begin(), it_end = lst.end(); + Fl_Widget *o; + + while(it != it_end) { + o = *it; + + /* 'inc == false' means we are going from right to left */ + if(!inc) + X -= o->w(); + + /* place it correctly */ + o->position(X, o->y()); + + self->add(o); + + if(inc) { + X += DEFAULT_SPACING; + X += o->w(); + } else { + X -= DEFAULT_SPACING; + } + + it = lst.erase(it); + } +} + +static void move_widget(Panel *self, Fl_Widget *o, int &sx, int &sy) { + int tx, ty, px, py; + Fl_Widget *const *a; + + tx = Fl::event_x() - sx; + ty = Fl::event_y() - sy; + + px = o->x() + tx; + py = o->y() + ty; + + /* check the bounds */ + if(px < 0) + px = 0; + if(px + o->w() > self->panel_w()) + px = self->panel_w() - o->w(); + if(py < 0) + py = 0; + if(py + o->h() > self->y() + self->panel_h()) + py = self->y() + self->panel_h() - o->h(); + if(py + o->h() > self->panel_h()) + py = self->panel_h() - o->h(); + + /* o->position(px, py); */ + o->position(px, o->y()); + + /* find possible collision and move others */ + a = self->array(); + for(int i = self->children(); i--; ) { + if(o == a[i]) + continue; + + if(intersects(a[i], o)) { + px = a[i]->x() + tx; + py = a[i]->y() + ty; + + /* check the bounds */ + if(px < 0) + px = 0; + if(px + o->w() > self->panel_w()) + px = self->panel_w() - o->w(); + if(py < 0) + py = 0; + if(py + o->h() > self->y() + self->panel_h()) + py = self->y() + self->panel_h() - o->h(); + if(py + o->h() > self->panel_h()) + py = self->panel_h() - o->h(); + + /* a[i]->position(px, py); */ + a[i]->position(px, a[i]->y()); + } + } + + /* update current position */ + sx = Fl::event_x(); + sy = Fl::event_y(); + + o->parent()->redraw(); +} + +void Panel::do_layout(void) { + E_RETURN_IF_FAIL(mgr.napplets() > 0); + + Fl_Widget *o; + unsigned long opts; + unsigned int lsz; + int X, W, free_w; + + WidgetList left, right, center, unmanaged, resizable_h; + + for(int i = 0; i < children(); i++) { + o = child(i); + + /* first center it vertically */ + center_widget_h(o, this); + + /* could be slow, but I'm relaying how number of loaded applets will not be that large */ + if(!mgr.get_applet_options(o, opts)) { + /* here are put widgets not loaded as applets */ + unmanaged.push_back(o); + continue; + } + + if(opts & EDE_PANEL_APPLET_OPTION_RESIZABLE_H) + resizable_h.push_back(o); + + if(opts & EDE_PANEL_APPLET_OPTION_ALIGN_LEFT) { + /* first item will be most leftest */ + left.push_back(o); + continue; + } + + if(opts & EDE_PANEL_APPLET_OPTION_ALIGN_RIGHT) { + /* first item will be most rightest */ + right.push_back(o); + continue; + } + + /* rest of them */ + center.push_back(o); + } + + /* make sure we at the end have all widgets, so we can overwrite group array */ + lsz = left.size() + center.size() + right.size() + unmanaged.size(); + E_ASSERT(lsz == (unsigned int)children() && "Size of layout lists size not equal to group size"); + + X = INITIAL_SPACING; + + /* + * Call add() on each element, processing lists in order. add() will remove element + * in group array and put it at the end of array. At the end, we should have array ordered by + * layout flags. + */ + add_from_list(left, this, X, true); + add_from_list(center, this, X, true); + add_from_list(unmanaged, this, X, true); + + free_w = X; + + /* elements right will be put from starting from the right panel border */ + X = w() - INITIAL_SPACING; + add_from_list(right, this, X, false); + + /* + * Code for horizontal streching. + * + * FIXME: This code pretty sucks and need better rewrite in future. + * To work properly, applets that will be streched must be successive or everything will be + * messed up. Also, applets that are placed right2left does not work with it; they will be resized to right. + */ + if(resizable_h.empty()) + return; + + /* calculate free space for horizontal alignement, per item in resizable_h list */ + free_w = (X - free_w) / resizable_h.size(); + if(!free_w) free_w = 0; + + /* + * since add_from_list() will already reserve some space by current child width and default spacing, + * those values will be used again or holes will be made + */ + WidgetListIt it = resizable_h.begin(), it_end = resizable_h.end(); + o = resizable_h.front(); + X = o->x(); + + while(it != it_end) { + o = *it; + + W = o->w() + free_w; + o->resize(X, o->y(), W, o->h()); + X += W + DEFAULT_SPACING; + + it = resizable_h.erase(it); + } +} + +void Panel::show(void) { + if(shown()) { + Fl_Window::show(); + return; + } + + int X, Y, W, H; + + fl_open_display(); + + /* position it */ + if(!netwm_get_workarea(X, Y, W, H)) + Fl::screen_xywh(X, Y, W, H); + + resize(X, Y + H - DEFAULT_PANEL_H, W, DEFAULT_PANEL_H); + + screen_x = X; + screen_y = Y; + screen_w = W; + screen_h = H; + screen_h_half = screen_h / 2; + + do_layout(); + window_xid_create(this, netwm_make_me_dock); +} + +int Panel::handle(int e) { + switch(e) { + case FL_SHOW: { + int ret = PanelWindow::handle(e); + position(0, screen_h - h()); + + if(shown()) + netwm_set_window_strut(this, 0, 0, 0, h()); + + return ret; + } + + case FL_PUSH: + clicked = Fl::belowmouse(); + + if(clicked == this) + clicked = 0; + else if(clicked && clicked->takesevents()) + clicked->handle(e); + + /* record push position for possible child drag */ + sx = Fl::event_x(); + sy = Fl::event_y(); + return 1; + + case FL_DRAG: { +#if 0 + if(clicked && clicked->takesevents()) { + cursor(FL_CURSOR_MOVE); + /* moving the child */ + //move_widget(this, clicked, sx, sy); + clicked->handle(e); + } else { +#endif + cursor(FL_CURSOR_MOVE); + /* are moving the panel; only vertical moving is supported */ + + /* snap it to the top or bottom, depending on pressed mouse location */ + if(Fl::event_y_root() <= screen_h_half && y() > screen_h_half) { + position(screen_x, screen_y); + netwm_set_window_strut(this, 0, 0, h(), 0); + } + + if(Fl::event_y_root() > screen_h_half && y() < screen_h_half) { + /* TODO: use area x and area y */ + position(screen_x, screen_h - h()); + netwm_set_window_strut(this, 0, 0, 0, h()); + } + // } + + return 1; + } + + case FL_RELEASE: + cursor(FL_CURSOR_DEFAULT); + + if(clicked && clicked->takesevents()) { + clicked->handle(e); + clicked = 0; + } + + return 1; + } + + return Fl_Window::handle(e); +} + +void Panel::load_applets(void) { + /* FIXME: hardcoded order */ + static const char *applets[] = { + "start_menu.so", + "quick_launch.so", + "pager.so", + "clock.so", + "taskbar.so", + 0 + }; + + String dir = Resource::find_data("panel-applets"); + if(dir.empty()) + return; + + String tmp; + for(int i = 0; applets[i]; i++) { + tmp = build_filename(dir.c_str(), applets[i]); + mgr.load(tmp.c_str()); + } + + mgr.fill_group(this); + +#if 0 + mgr.load("./applets/start-menu/start_menu.so"); + mgr.load("./applets/quick-launch/quick_launch.so"); + mgr.load("./applets/pager/pager.so"); + mgr.load("./applets/clock/clock.so"); + mgr.load("./applets/taskbar/taskbar.so"); + mgr.fill_group(this); +#endif +} diff --git a/ede-panel/Panel.h b/ede-panel/Panel.h new file mode 100644 index 0000000..b19ac22 --- /dev/null +++ b/ede-panel/Panel.h @@ -0,0 +1,36 @@ +#ifndef __PANEL_H__ +#define __PANEL_H__ + +#include +#include "AppletManager.h" + +#define EDE_PANEL_CAST_TO_PANEL(obj) ((Panel*)(obj)) +#define EDE_PANEL_GET_PANEL_OBJECT (EDE_PANEL_CAST_TO_PANEL(parent())) + +EDELIB_NS_USING_AS(Window, PanelWindow) + +class Panel : public PanelWindow { +private: + Fl_Widget *clicked; + int sx, sy; + int screen_x, screen_y, screen_w, screen_h, screen_h_half; + bool can_move_widgets; + + AppletManager mgr; + + void do_layout(void); + +public: + Panel() : PanelWindow(300, 30), clicked(0), sx(0), sy(0), + screen_x(0), screen_y(0), screen_w(0), screen_h(0), screen_h_half(0), can_move_widgets(false) + { box(FL_UP_BOX); } + + int handle(int e); + void show(void); + void load_applets(void); + + int panel_w(void) { return w(); } + int panel_h(void) { return h(); } +}; + +#endif diff --git a/ede-panel/applets/Jamfile b/ede-panel/applets/Jamfile new file mode 100644 index 0000000..883ab8c --- /dev/null +++ b/ede-panel/applets/Jamfile @@ -0,0 +1,18 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel-new applets ; + +SubInclude TOP ede-panel-new applets demo ; +SubInclude TOP ede-panel-new applets clock ; +SubInclude TOP ede-panel-new applets pager ; +SubInclude TOP ede-panel-new applets quick-launch ; +SubInclude TOP ede-panel-new applets start-menu ; +SubInclude TOP ede-panel-new applets taskbar ; diff --git a/ede-panel/applets/clock/Clock.cpp b/ede-panel/applets/clock/Clock.cpp new file mode 100644 index 0000000..c355eec --- /dev/null +++ b/ede-panel/applets/clock/Clock.cpp @@ -0,0 +1,71 @@ +#include "Applet.h" + +#include +#include +#include + +#include +#include + +EDELIB_NS_USING(run_async) + +static void clock_refresh(void *o); + +class Clock : public Fl_Box { +private: + char buf[64]; +public: + Clock() : Fl_Box(450, 0, 80, 25, NULL) { + box(FL_THIN_DOWN_BOX); + } + + ~Clock() { + Fl::remove_timeout(clock_refresh); + } + + int handle(int e); + void update_time(void); +}; + +static void clock_refresh(void *o) { + Clock *c = (Clock *)o; + c->update_time(); + + Fl::repeat_timeout(1.0, clock_refresh, o); +} + +void Clock::update_time(void) { + time_t t; + struct tm *tmp; + + t = time(NULL); + tmp = localtime(&t); + if(!tmp) + return; + + strftime(buf, sizeof(buf), "%H:%M:%S", tmp); + label(buf); +} + +int Clock::handle(int e) { + switch(e) { + case FL_SHOW: + Fl::add_timeout(0, clock_refresh, this); + break; + + case FL_RELEASE: + run_async("ede-timedate"); + break; + } + + return Fl_Box::handle(e); +} + +EDE_PANEL_APPLET_EXPORT ( + Clock, + EDE_PANEL_APPLET_OPTION_ALIGN_RIGHT, + "Clock applet", + "0.1", + "empty", + "Sanel Zukan" +) diff --git a/ede-panel/applets/clock/Jamfile b/ede-panel/applets/clock/Jamfile new file mode 100644 index 0000000..b22ed29 --- /dev/null +++ b/ede-panel/applets/clock/Jamfile @@ -0,0 +1,13 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel-new applets clock ; + +PanelApplet clock : Clock.cpp ; diff --git a/ede-panel/applets/demo/AppletDemo.cpp b/ede-panel/applets/demo/AppletDemo.cpp new file mode 100644 index 0000000..a2e3692 --- /dev/null +++ b/ede-panel/applets/demo/AppletDemo.cpp @@ -0,0 +1,26 @@ +#include "Applet.h" +#include +#include +#include +#include + +class MyButton : public Fl_Button { +public: + MyButton() : Fl_Button(0, 0, 90, 25, "xxx") { + color(FL_RED); + puts("MyButton::MyButton"); + } + + ~MyButton() { + puts("MyButton::~MyButton"); + } +}; + +EDE_PANEL_APPLET_EXPORT ( + MyButton, + 0, + "Panel button", + "0.1", + "empty", + "Sanel Zukan" +) diff --git a/ede-panel/applets/demo/Jamfile b/ede-panel/applets/demo/Jamfile new file mode 100644 index 0000000..c6b6475 --- /dev/null +++ b/ede-panel/applets/demo/Jamfile @@ -0,0 +1,13 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel-new applets demo ; + +PanelApplet demo : AppletDemo.cpp ; diff --git a/ede-panel/applets/pager/Jamfile b/ede-panel/applets/pager/Jamfile new file mode 100644 index 0000000..23c02d1 --- /dev/null +++ b/ede-panel/applets/pager/Jamfile @@ -0,0 +1,13 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel-new applets pager ; + +PanelApplet pager : Pager.cpp PagerButton.cpp ; diff --git a/ede-panel/applets/pager/Pager.cpp b/ede-panel/applets/pager/Pager.cpp new file mode 100644 index 0000000..7726a3a --- /dev/null +++ b/ede-panel/applets/pager/Pager.cpp @@ -0,0 +1,125 @@ +#include "Applet.h" +#include "Netwm.h" + +#include + +#include +#include +#include + +#include "PagerButton.h" + +class Pager : public Fl_Group { +public: + Pager(); + ~Pager(); + void init_workspace_boxes(void); + void workspace_changed(void); +}; + +static void box_cb(Fl_Widget*, void *b) { + PagerButton *pb = (PagerButton*)b; + + /* because workspaces in button labels are increased */ + int s = pb->get_workspace_label() - 1; + netwm_change_workspace(s); +} + +static void net_event_cb(int action, Window xid, void *data) { + E_RETURN_IF_FAIL(data != NULL); + + if(action == NETWM_CHANGED_CURRENT_WORKSPACE) { + Pager *p = (Pager*)data; + p->workspace_changed(); + } +} + +Pager::Pager() : Fl_Group(0, 0, 25, 25, NULL) { + box(FL_DOWN_BOX); + /* we will explicitly add elements */ + end(); + + init_workspace_boxes(); + netwm_callback_add(net_event_cb, this); +} + +Pager::~Pager() { + netwm_callback_remove(net_event_cb); +} + +void Pager::init_workspace_boxes(void) { + int X, Y, H; + + /* prepare the sizes */ + X = x() + Fl::box_dx(box()); + Y = y() + Fl::box_dy(box()); + H = h() - Fl::box_dh(box()); + + int nworkspaces, curr_workspace; + char **names = 0; + char nbuf[16]; + + nworkspaces = netwm_get_workspace_count(); + curr_workspace = netwm_get_current_workspace(); + netwm_get_workspace_names(names); + + /* + * Resize group before childs, or they will be resized too, messing things up. + * Here, each child is 25px wide plus 1px for delimiter between the childs . + */ + int gw = nworkspaces * (25 + 1); + gw += Fl::box_dw(box()); + /* last child do not ends with 1px wide delimiter */ + gw -= 1; + + size(gw, h()); + + for(int i = 0; i < nworkspaces; i++) { + /* let box width be fixed */ + PagerButton *bx = new PagerButton(X, Y, 25, H); + bx->box(FL_FLAT_BOX); + + if(i == curr_workspace) + bx->select_it(1); + else + bx->select_it(0); + + /* workspaces are started from 0 */ + bx->set_workspace_label(i + 1); + + if(names) + bx->copy_tooltip(names[i]); + + bx->callback(box_cb, bx); + + add(bx); + /* position for the next box */ + X = bx->x() + bx->w() + 1; + } + + XFreeStringList(names); +} + +void Pager::workspace_changed(void) { + int c = netwm_get_current_workspace(); + PagerButton *pb; + + E_RETURN_IF_FAIL(c < children()); + + for(int i = 0; i < children(); i++) { + pb = (PagerButton*)child(i); + pb->select_it(0); + } + + pb = (PagerButton*)child(c); + pb->select_it(1); +} + +EDE_PANEL_APPLET_EXPORT ( + Pager, + EDE_PANEL_APPLET_OPTION_ALIGN_LEFT, + "Desktop switcher", + "0.1", + "empty", + "Sanel Zukan" +) diff --git a/ede-panel/applets/pager/PagerButton.cpp b/ede-panel/applets/pager/PagerButton.cpp new file mode 100644 index 0000000..7f9bb5f --- /dev/null +++ b/ede-panel/applets/pager/PagerButton.cpp @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include "PagerButton.h" + +PagerButton::~PagerButton() { + if(ttip) + free(ttip); +} + +void PagerButton::set_workspace_label(int l) { + wlabel = l; + + char buf[16]; + snprintf(buf, sizeof(buf), "%i", l); + copy_label(buf); +} + +void PagerButton::copy_tooltip(const char *t) { + E_RETURN_IF_FAIL(t != NULL); + + if(ttip) + free(ttip); + + ttip = strdup(t); + tooltip(ttip); +} + +void PagerButton::select_it(int i) { + if(i) { + color((Fl_Color)44); + labelcolor(FL_BLACK); + } else { + color((Fl_Color)33); + labelcolor(FL_GRAY); + } + + redraw(); +} diff --git a/ede-panel/applets/pager/PagerButton.h b/ede-panel/applets/pager/PagerButton.h new file mode 100644 index 0000000..e0708be --- /dev/null +++ b/ede-panel/applets/pager/PagerButton.h @@ -0,0 +1,22 @@ +#ifndef __PAGERBUTTON_H__ +#define __PAGERBUTTON_H__ + +#include + +class PagerButton : public Fl_Button { +private: + char *ttip; + int wlabel; +public: + PagerButton(int X, int Y, int W, int H, const char *l = 0) : Fl_Button(X, Y, W, H), ttip(NULL), wlabel(0) + { } + ~PagerButton(); + + void set_workspace_label(int l); + int get_workspace_label(void) { return wlabel; } + + void copy_tooltip(const char *t); + void select_it(int i); +}; + +#endif diff --git a/ede-panel/applets/quick-launch/Jamfile b/ede-panel/applets/quick-launch/Jamfile new file mode 100644 index 0000000..9866221 --- /dev/null +++ b/ede-panel/applets/quick-launch/Jamfile @@ -0,0 +1,13 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel-new applets quick-launch ; + +PanelApplet quick_launch : QuickLaunch.cpp ; diff --git a/ede-panel/applets/quick-launch/QuickLaunch.cpp b/ede-panel/applets/quick-launch/QuickLaunch.cpp new file mode 100644 index 0000000..bbb258c --- /dev/null +++ b/ede-panel/applets/quick-launch/QuickLaunch.cpp @@ -0,0 +1,117 @@ +#include "Applet.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "edit-clear-icon.h" + +EDELIB_NS_USING(run_async) + +class QuickLaunch : public Fl_Input { +private: + Fl_Image *img; + int img_x, img_y; +public: + QuickLaunch(); + + void draw(void); + int handle(int e); + + /* a stupid way to avoid draw_box() to draw the image */ + void image2(Fl_Image *im) { img = im; } + Fl_Image *image2(void) { return img; } +}; + +static bool empty_string(const char *s) { + for(const char *ptr = s; ptr && *ptr; ptr++) { + if(!isspace(*ptr)) + return false; + } + + return true; +} + +static void enter_cb(Fl_Widget*, void *o) { + QuickLaunch *ql = (QuickLaunch*)o; + + if(ql->value() && !empty_string(ql->value())) + run_async("ede-launch %s", ql->value()); +} + +QuickLaunch::QuickLaunch() : Fl_Input(0, 0, 130, 25), img(NULL), img_x(0), img_y(0) { + when(FL_WHEN_ENTER_KEY|FL_WHEN_NOT_CHANGED); + callback(enter_cb, this); + tooltip(_("Enter a command to be executed")); + + image2((Fl_RGB_Image*)&image_edit); +} + +void QuickLaunch::draw(void) { + Fl_Boxtype b = box(), oldb; + + int X = x() + Fl::box_dx(b); + int Y = y() + Fl::box_dy(b); + int W = w() - Fl::box_dw(b); + int H = h() - Fl::box_dh(b); + + if(img) { + W -= img->w() + 6; + img_x = X + W + 2; + img_y = (Y + H / 2) - (img->h() / 2); + } + + if(damage() & FL_DAMAGE_ALL) { + draw_box(b, color()); + if(img) img->draw(img_x, img_y); + } + + /* use flat box when text is drawn or there would be visible border line */ + oldb = box(); + box(FL_FLAT_BOX); + + Fl_Input_::drawtext(X, Y, W, H); + box(oldb); +} + +int QuickLaunch::handle(int e) { + if(!img) + goto done; + + switch(e) { + case FL_ENTER: + case FL_MOVE: + if(active_r() && window()) { + if(Fl::event_inside(img_x, img_y, img->w(), img->h())) + window()->cursor(FL_CURSOR_DEFAULT); + else + window()->cursor(FL_CURSOR_INSERT); + } + + return 1; + + case FL_RELEASE: + if(active_r() && Fl::event_inside(img_x, img_y, img->w(), img->h())) + value(0); + /* fallthrough */ + } + +done: + return Fl_Input::handle(e); +} + + +EDE_PANEL_APPLET_EXPORT ( + QuickLaunch, + EDE_PANEL_APPLET_OPTION_ALIGN_LEFT, + "Quick Launch applet", + "0.1", + "empty", + "Sanel Zukan" +) diff --git a/ede-panel/applets/quick-launch/edit-clear-icon.h b/ede-panel/applets/quick-launch/edit-clear-icon.h new file mode 100644 index 0000000..ec7466c --- /dev/null +++ b/ede-panel/applets/quick-launch/edit-clear-icon.h @@ -0,0 +1,55 @@ +#ifndef __EDIT_CLEAR_ICON_H__ +#define __EDIT_CLEAR_ICON_H__ + +#include +static unsigned char idata_edit[] = +{255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255, +255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0, +255,255,255,0,120,72,0,71,123,74,0,227,124,75,0,218,120,72,0,59,255,255,255,0, +255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255, +255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,120,72,0,68,123, +74,0,245,171,109,11,255,166,105,10,255,123,74,0,229,255,255,255,0,255,255,255, +0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255, +255,255,0,156,139,0,19,156,139,0,130,138,105,0,2,123,74,0,218,169,107,10,255, +193,125,16,255,178,114,12,255,123,74,0,237,255,255,255,0,255,255,255,0,255,255, +255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,156, +139,0,41,156,139,0,255,154,133,0,206,132,82,1,242,193,125,16,255,188,122,15,255, +134,82,3,244,121,73,0,111,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255, +0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,156,139,0,61,177, +160,29,242,190,174,50,254,161,136,1,255,178,118,11,255,128,78,1,244,123,74,0, +149,120,72,0,2,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255, +255,0,156,139,0,21,157,140,0,119,160,143,1,210,160,54,0,228,176,105,23,247,235, +222,119,255,205,189,65,255,156,136,0,254,135,100,0,129,255,255,255,0,255,255, +255,0,255,255,255,0,255,255,255,0,255,255,255,0,156,139,0,27,159,142,1,159,162, +144,9,246,204,189,67,247,234,220,114,255,215,177,98,255,171,27,13,255,200,109, +51,255,236,220,104,255,216,200,79,255,158,141,1,246,156,139,0,90,255,255,255,0, +156,139,0,2,156,139,0,64,159,142,0,163,159,143,5,247,205,190,69,250,248,234,136, +255,253,238,125,255,253,235,108,255,253,234,99,255,246,216,97,255,184,56,29,255, +187,74,40,255,187,165,41,246,158,141,0,235,156,139,0,95,255,255,255,0,157,140,0, +234,174,158,27,243,207,193,73,254,235,221,119,255,243,227,101,255,251,229,68, +255,253,233,82,255,253,236,109,255,253,239,132,255,253,236,107,255,251,234,132, +255,173,66,19,252,161,36,0,215,156,139,0,7,255,255,255,0,255,255,255,0,159,141, +1,233,226,212,103,255,228,210,73,255,220,198,16,255,231,208,24,255,243,219,42, +255,253,230,60,255,253,233,82,255,253,235,97,255,253,234,95,255,253,239,138,255, +192,176,47,245,156,127,0,95,255,255,255,0,255,255,255,0,255,255,255,0,159,142,0, +168,198,182,51,251,230,214,86,255,218,194,3,255,219,195,4,255,229,205,20,255, +238,215,35,255,246,222,47,255,251,228,55,255,252,229,60,255,251,237,139,255,165, +148,17,247,156,139,0,26,255,255,255,0,255,255,255,0,255,255,255,0,156,139,0,28, +159,142,4,244,220,205,88,255,228,211,76,255,218,194,3,255,218,194,3,255,220,196, +7,255,227,203,17,255,231,208,24,255,237,218,70,255,221,207,94,255,160,142,1, +212,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,156, +139,0,63,160,143,5,241,214,200,86,254,231,215,91,255,220,198,21,255,218,194,3, +255,218,194,3,255,220,197,16,255,232,218,108,255,171,155,26,246,156,139,0,67, +255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255, +255,0,156,139,0,38,158,141,0,230,197,180,50,251,231,217,109,255,229,212,78,255, +223,202,38,255,231,216,95,255,197,181,54,250,159,142,0,197,255,255,255,0,255, +255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0, +255,255,255,0,156,139,0,20,159,141,0,169,161,144,8,246,198,181,48,250,225,211, +91,255,210,195,79,253,158,141,0,226,156,139,0,19,255,255,255,0,255,255,255,0, +255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255, +255,0,255,255,255,0,255,255,255,0,156,139,0,33,158,141,0,158,158,141,0,236,157, +140,0,237,156,139,0,31,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0, +255,255,255,0,255,255,255,0}; +static Fl_RGB_Image image_edit(idata_edit, 16, 16, 4, 0); + +#endif diff --git a/ede-panel/applets/quick-launch/icons/edit-clear.png b/ede-panel/applets/quick-launch/icons/edit-clear.png new file mode 100644 index 0000000000000000000000000000000000000000..9f6b0d2bc5feb0e9b42754b55d57f3304deeeabc GIT binary patch literal 771 zcmV+e1N{7nP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXZ7 z4ly!Pl5tD`00M(aL_t(I%axK#Y)oMohM)ic&(ty1mZ>SyB1KDFs;&tVks#8zEJQ+L zgONxqG?KBf5F!>9BNjp;%)-V-EZ8DMu%OY1TT6;gN<(Ja&a|gvdd~Sj3zs0S-~N5x z?|a`*i3tBQ2V2xSqdBh>#UNy-`X9i-7PZD`t~5_CkNSb|w0roS^EXjCrK*9}zE>PE znlnxH(P+)s2zf66aP>bJ*8qpc*3mZFRTC%1%J4(s$(y2Qg`>_t0LI7TlLsp*~d~n7p}>8LdmmW_!7J_iE8v*_}wrMJJt78dzxz z3FAbzG|gXS=FD9ki8vmF{ZubgOuey%+dHn)zV8-nS49vHhybW`iVx#rwWI5&Hg0 zF056&?a64yaP9lRbr_QD?08ffE^4CVS`ZODub;kL54l{1Y_^-c7bTN + +#include +#include +#include +#include + +#include "DesktopEntry.h" + +EDELIB_NS_USING(DesktopFile) +EDELIB_NS_USING(build_filename) +EDELIB_NS_USING(stringtok) +EDELIB_NS_USING(DESK_FILE_TYPE_APPLICATION) + +static int age_counter = 1; + +DesktopEntry::~DesktopEntry() { + delete path; + delete id; + + delete categories; + delete name; + delete generic_name; + delete comment; + delete icon; + delete exec; +} + +void DesktopEntry::assign_path(const char *dir, const char *p, const char *basedir) { + E_ASSERT(dir != NULL); + E_ASSERT(p != NULL); + + /* make sure we do not assign things twice */ + E_RETURN_IF_FAIL(path == NULL); + E_RETURN_IF_FAIL(id == NULL); + + const char *ptr; + String *spath, *sid; + + /* full path */ + spath = new String(build_filename(dir, p)); + ptr = spath->c_str(); + + /* if we have basedir, skip directory path by jumping the size of basedir */ + if(basedir) { + ptr += strlen(basedir); + + /* skip ending delimiters */ + while(*ptr == E_DIR_SEPARATOR) + ptr++; + } + + sid = new String(ptr); + + /* replace existing separators; this is in the spec */ + sid->replace(E_DIR_SEPARATOR, '-'); + + path = spath; + id = sid; + age = age_counter++; +} + +bool DesktopEntry::load(void) { + E_RETURN_VAL_IF_FAIL(path != NULL, false); + + DesktopFile df; + if(!df.load(path->c_str())) { + E_WARNING(E_STRLOC ": Unable to load %s\n", path->c_str()); + return false; + } + + /* check if we are 'Hidden = true' or 'NoDisplay = true'; these must not be loaded */ + if(df.hidden() || df.no_display()) + return false; + + /* it must be application type */ + E_RETURN_VAL_IF_FAIL(df.type() == DESK_FILE_TYPE_APPLICATION, false); + + char buf[128]; + + /* name must be present too */ + E_RETURN_VAL_IF_FAIL(df.name(buf, sizeof(buf)) == true, false); + name = new String(buf); + + /* optional */ + if(df.get("Desktop Entry", "Categories", buf, sizeof(buf))) + categories = new String(buf); + + if(df.generic_name(buf, sizeof(buf))) + generic_name = new String(buf); + + if(df.comment(buf, sizeof(buf))) + comment = new String(buf); + + if(df.icon(buf, sizeof(buf))) + icon = new String(buf); + + if(df.exec(buf, sizeof(buf))) + exec = new String(buf); + + return true; +} + +bool DesktopEntry::in_category(const char *cat) { + E_RETURN_VAL_IF_FAIL(cat != NULL, false); + + if(!categories) + return false; + + StrListIt it, it_end; + + if(category_list.empty()) { + stringtok(category_list, *categories, ";"); + + /* remove starting/ending spaces if exists */ + it = category_list.begin(); + it_end = category_list.end(); + for(; it != it_end; ++it) + (*it).trim(); + } + + it = category_list.begin(); + it_end = category_list.end(); + for(; it != it_end; ++it) { + if((*it) == cat) + return true; + } + + return false; +} + +/* TODO: bug in edelib */ +static bool id_age_sorter(DesktopEntry* const& u1, DesktopEntry* const& u2) { + return (strcmp(u1->get_id(), u2->get_id()) < 0) && (u1->get_age() < u2->get_age()); +} + +/* TODO: bug in edelib */ +static bool name_sorter(DesktopEntry* const& u1, DesktopEntry* const& u2) { + return (strcmp(u1->get_name(), u2->get_name()) < 0); +} + +void desktop_entry_list_remove_duplicates(DesktopEntryList &lst) { + if(lst.empty()) + return; + + /* sort them respecting name and age */ + lst.sort(id_age_sorter); + + DesktopEntryListIt it = lst.begin(), it_end = lst.end(); + DesktopEntryListIt next = it; + + /* do consecutive unique */ + while(++next != it_end) { + if(strcmp((*it)->get_id(), (*next)->get_id()) == 0) { + delete *next; + lst.erase(next); + } else { + it = next; + } + + next = it; + } +} + +void desktop_entry_list_load_all(DesktopEntryList &lst) { + if(lst.empty()) + return; + + DesktopEntryListIt it = lst.begin(), it_end = lst.end(); + while(it != it_end) { + if(!(*it)->load()) { + delete *it; + it = lst.erase(it); + } else { + ++it; + } + } +} + +void desktop_entry_list_sort(DesktopEntryList &lst) { + lst.sort(name_sorter); +} diff --git a/ede-panel/applets/start-menu/DesktopEntry.h b/ede-panel/applets/start-menu/DesktopEntry.h new file mode 100644 index 0000000..9cec108 --- /dev/null +++ b/ede-panel/applets/start-menu/DesktopEntry.h @@ -0,0 +1,101 @@ +#ifndef __DESKTOPENTRY_H__ +#define __DESKTOPENTRY_H__ + +#include +#include +#include + +EDELIB_NS_USING(String) +EDELIB_NS_USING(list) + +class DesktopEntry; + +typedef list DesktopEntryList; +typedef list::iterator DesktopEntryListIt; + +typedef list StrList; +typedef list::iterator StrListIt; + +/* Represents entry for a menu. Do not confuse it with DesktopFile from edelib */ +class DesktopEntry { +private: + /* used to load "later" found .desktop file, in case duplicate desktop file id's was found */ + unsigned int age; + + /* used for and */ + bool allocated; + + /* absolute path to .desktop file */ + String *path; + + /* Desktop File Id */ + String *id; + + /* Categories value from .desktop file; filled with load() */ + String *categories; + + /* Localized Name value from .desktop file; filled with load() */ + String *name; + + /* GenericName value from .desktop file; filled with load() */ + String *generic_name; + + /* Comment value from .desktop file; filled with load() */ + String *comment; + + /* Icon value from .desktop file; filled with load() */ + String *icon; + + /* Exec value from .desktop file; filled with load() */ + String *exec; + + /* tokenized 'categories' */ + StrList category_list; + + E_DISABLE_CLASS_COPY(DesktopEntry) +public: + DesktopEntry() : age(0), allocated(false), path(NULL), id(NULL), + categories(NULL), name(NULL), generic_name(NULL), comment(NULL), icon(NULL), exec(NULL) { } + + ~DesktopEntry(); + + /* + * Construct full path by using 'dir/p' and construct desktop file id according + * to the menu spec. If 'basedir' was given (can be NULL), 'dir' will be diffed against it + * so correct prefix for desktop file id can be calculated. + * + * The id is calculated like this: + * $XDG_DATA_DIRS/applications/foo.desktop => foo.desktop + * $XDG_DATA_DIRS/applications/ede/foo.desktop => ede-foo.desktop + * $XDG_DATA_DIRS/applications/ede/menu/foo.desktop => ede-menu-foo.desktop + */ + void assign_path(const char *dir, const char *p , const char *basedir); + + /* loads actual .desktop file and reads it */ + bool load(void); + + /* check if Categories key contains given category */ + bool in_category(const char *cat); + + void mark_as_allocated(void) { allocated = true; } + bool is_allocated(void) { return allocated; } + + const char *get_path(void) { return path ? path->c_str() : NULL; } + const char *get_id(void) { return id ? id->c_str() : NULL; } + unsigned int get_age(void) { return age; } + + const char *get_name(void) { return name ? name->c_str() : NULL; } + const char *get_icon(void) { return icon ? icon->c_str() : NULL; } + const char *get_exec(void) { return exec ? exec->c_str() : NULL; } +}; + +/* remove duplicate items in the list, by looking at DesktopEntry id */ +void desktop_entry_list_remove_duplicates(DesktopEntryList &lst); + +/* call 'load()' on each member; if 'load()' fails, that member will be removed */ +void desktop_entry_list_load_all(DesktopEntryList &lst); + +/* sort a list of entries */ +void desktop_entry_list_sort(DesktopEntryList &lst); + +#endif diff --git a/ede-panel/applets/start-menu/Jamfile b/ede-panel/applets/start-menu/Jamfile new file mode 100644 index 0000000..974bd64 --- /dev/null +++ b/ede-panel/applets/start-menu/Jamfile @@ -0,0 +1,21 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel-new applets start-menu ; + +MENU_SRC = XdgMenuReader.cpp DesktopEntry.cpp MenuRules.cpp ; + +# TODO: MENU_SRC files will be compiled twice, once for applet and once for test program. + +# applet +PanelApplet start_menu : StartMenu.cpp $(MENU_SRC) ; + +# test program +EdeProgram ede-menu-spec-test : ede-menu-spec-test.cpp $(MENU_SRC) ; diff --git a/ede-panel/applets/start-menu/MenuRules.cpp b/ede-panel/applets/start-menu/MenuRules.cpp new file mode 100644 index 0000000..027e010 --- /dev/null +++ b/ede-panel/applets/start-menu/MenuRules.cpp @@ -0,0 +1,156 @@ +#include + +#include "MenuRules.h" +#include "DesktopEntry.h" + +typedef list Stack; +typedef list::iterator StackIt; + +#if 0 +static char *rules_str[] = { + "None", + "Filename", + "Category", + "And", + "Or", + "Not", + "All" +}; +#endif + +MenuRules *menu_rules_new(void) { + MenuRules *r = new MenuRules; + r->rule_operator = MENU_RULES_OPERATOR_NONE; + return r; +} + +MenuRules *menu_rules_append_rule(MenuRulesList &rules, short rule, const char *data) { + MenuRules *r = menu_rules_new(); + r->rule_operator = rule; + + if(data) + r->data = data; + + /* NOTE: things are evaluated recursively, so we push deepest rule at the top */ + rules.push_front(r); + return r; +} + +void menu_rules_delete(MenuRules *r) { + if(!r->subrules.empty()) { + MenuRulesListIt it = r->subrules.begin(), it_end = r->subrules.end(); + + while(it != it_end) { + menu_rules_delete(*it); + it = r->subrules.erase(it); + } + } + + delete r; +} + +/* + * Rules are evaluated starting from the deepest one, and are much more like + * lisp expressions. Each rule behaves like function, returning false or true and top function + * gets applied on those results. + * + * TODO: this case will not work (KDE 3.5.x applications.menu): + * + * + * c1 + * c2 + * c3 + * + * c4 + * + * + * because due recrusion '...' result will be calculated inside '...' expression and + * '' will receive the only one value. + */ +static void eval_with_stack(MenuRules *m, DesktopEntry *en, Stack &result_stack) { + if(!m->subrules.empty()) { + MenuRulesListIt it = m->subrules.begin(), it_end = m->subrules.end(); + + /* go to the deepest node */ + for(; it != it_end; ++it) + eval_with_stack(*it, en, result_stack); + } + + /* this will always evaluate to true so all items can be included */ + if(m->rule_operator == MENU_RULES_OPERATOR_ALL) { + result_stack.push_front(true); + return; + } + + if(m->rule_operator == MENU_RULES_OPERATOR_FILENAME) { + bool st = (m->data == en->get_id()); + result_stack.push_front(st); + return; + } + + if(m->rule_operator == MENU_RULES_OPERATOR_CATEGORY) { + bool st = en->in_category(m->data.c_str()); + result_stack.push_front(st); + return; + } + + if(m->rule_operator == MENU_RULES_OPERATOR_AND) { + StackIt it = result_stack.begin(), it_end = result_stack.end(); + + /* 'and' always evalutes to true */ + bool st = true; + + while(it != it_end) { + st &= *it; + it = result_stack.erase(it); + } + + result_stack.push_front(st); + return; + } + + /* operators 'not' and 'or' behaves much the same, except 'not' reverse the result additionally */ + if(m->rule_operator == MENU_RULES_OPERATOR_OR || m->rule_operator == MENU_RULES_OPERATOR_NOT) { + StackIt it = result_stack.begin(), it_end = result_stack.end(); + + /* 'or' and 'not' always evalutes to false */ + bool st = false; + + while(it != it_end) { + st |= *it; + it = result_stack.erase(it); + } + + if(m->rule_operator == MENU_RULES_OPERATOR_NOT) + st = !st; + + result_stack.push_front(st); + return; + } +} + +bool menu_rules_eval(MenuRules *m, DesktopEntry *en) { + Stack result_stack; + eval_with_stack(m, en, result_stack); + + if(result_stack.size() == 1) + goto get_first; + else { + /* + * From the spec: "Each rule in a list of rules has a logical OR relationship, that is, desktop entries + * which match any rule are included in the menu." + */ + bool result = false; + + StackIt it = result_stack.begin(), it_end = result_stack.end(); + while(it != it_end) { + result |= *it; + result_stack.erase(it); + } + + return result; + } + +get_first: + return result_stack.front(); +} diff --git a/ede-panel/applets/start-menu/MenuRules.h b/ede-panel/applets/start-menu/MenuRules.h new file mode 100644 index 0000000..fed3141 --- /dev/null +++ b/ede-panel/applets/start-menu/MenuRules.h @@ -0,0 +1,40 @@ +#ifndef __MENURULES_H__ +#define __MENURULES_H__ + +#include +#include + +EDELIB_NS_USING(list) +EDELIB_NS_USING(String) + +struct MenuRules; +struct DesktopEntry; + +typedef list MenuRulesList; +typedef list::iterator MenuRulesListIt; + +enum { + MENU_RULES_OPERATOR_NONE, + MENU_RULES_OPERATOR_FILENAME, + MENU_RULES_OPERATOR_CATEGORY, + MENU_RULES_OPERATOR_AND, + MENU_RULES_OPERATOR_OR, + MENU_RULES_OPERATOR_NOT, + MENU_RULES_OPERATOR_ALL +}; + +struct MenuRules { + short rule_operator; + String data; + + MenuRulesList subrules; +}; + +MenuRules *menu_rules_new(void); + +MenuRules *menu_rules_append_rule(MenuRulesList &rules, short rule, const char *data); +void menu_rules_delete(MenuRules *m); + +bool menu_rules_eval(MenuRules *m, DesktopEntry *en); + +#endif diff --git a/ede-panel/applets/start-menu/StartMenu.cpp b/ede-panel/applets/start-menu/StartMenu.cpp new file mode 100644 index 0000000..a801418 --- /dev/null +++ b/ede-panel/applets/start-menu/StartMenu.cpp @@ -0,0 +1,168 @@ +#include "Applet.h" + +#include +#include +#include +#include +#include + +#include "XdgMenuReader.h" +#include "ede-icon.h" + +EDELIB_NS_USING(MenuBase) + +/* some of this code was ripped from Fl_Menu_Button.cxx */ + +class StartMenu : public MenuBase { +private: + MenuItem *mcontent; +public: + StartMenu(); + ~StartMenu(); + + void popup(void); + void draw(void); + int handle(int e); +}; + +StartMenu::StartMenu() : MenuBase(0, 0, 80, 25, "EDE"), mcontent(NULL) { + down_box(FL_NO_BOX); + labelfont(FL_HELVETICA_BOLD); + labelsize(14); + image(ede_icon_image); + + tooltip(_("Click here to choose and start common programs")); + + mcontent = xdg_menu_load(); + + 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); + } + } +} + +StartMenu::~StartMenu() { + xdg_menu_delete(mcontent); +} + +static StartMenu *pressed_menu_button = 0; + +void StartMenu::draw(void) { + if(!box() || type()) + return; + + draw_box(pressed_menu_button == this ? fl_down(box()) : box(), color()); + + if(image()) { + int X, Y, lw, lh; + + X = x() + 5; + Y = (y() + h() / 2) - (image()->h() / 2); + image()->draw(X, Y); + + X += image()->w() + 10; + + fl_font(labelfont(), labelsize()); + fl_color(labelcolor()); + fl_measure(label(), lw, lh, align()); + + fl_draw(label(), X, Y, lw, lh, align(), 0, 0); + } else { + draw_label(); + } +} + +void StartMenu::popup(void) { + const MenuItem *m; + + pressed_menu_button = this; + redraw(); + + Fl_Widget *mb = this; + Fl::watch_widget_pointer(mb); + if(!box() || type()) + m = menu()->popup(Fl::event_x(), Fl::event_y(), label(), mvalue(), this); + else + m = menu()->pulldown(x(), y(), w(), h(), 0, this); + + picked(m); + pressed_menu_button = 0; + Fl::release_widget_pointer(mb); +} + +int StartMenu::handle(int e) { + if(!menu() || !menu()->text) + return 0; + + switch(e) { + case FL_ENTER: + case FL_LEAVE: + return (box() && !type()) ? 1 : 0; + case FL_PUSH: + if(!box()) { + if(Fl::event_button() != 3) + return 0; + } else if(type()) { + if(!(type() & (1 << (Fl::event_button() - 1)))) + return 0; + } + + if(Fl::visible_focus()) + Fl::focus(this); + + popup(); + return 1; + + case FL_KEYBOARD: + if(!box()) return 0; + + /* + * Win key will show the menu. + * + * In FLTK, Meta keys are equivalent to Alt keys. On other hand, eFLTK has them different, much + * the same as on M$ Windows. Here, I'm explicitly using hex codes, since Fl_Meta_L and Fl_Meta_R + * are unusable. + */ + if(Fl::event_key() == 0xffeb || Fl::event_key() == 0xffec) { + popup(); + return 1; + } + + return 0; + + case FL_SHORTCUT: + if(Fl_Widget::test_shortcut()) { + popup(); + return 1; + } + + return test_shortcut() != 0; + + case FL_FOCUS: + case FL_UNFOCUS: + if(box() && Fl::visible_focus()) { + redraw(); + return 1; + } + + default: + return 0; + } + + /* not reached */ + return 0; +} + +EDE_PANEL_APPLET_EXPORT ( + StartMenu, + EDE_PANEL_APPLET_OPTION_ALIGN_LEFT, + "Main menu", + "0.1", + "empty", + "Sanel Zukan" +) diff --git a/ede-panel/applets/start-menu/XdgMenuReader.cpp b/ede-panel/applets/start-menu/XdgMenuReader.cpp new file mode 100644 index 0000000..4ca642b --- /dev/null +++ b/ede-panel/applets/start-menu/XdgMenuReader.cpp @@ -0,0 +1,1031 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DesktopEntry.h" +#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) +EDELIB_NS_USING(user_data_dir) +EDELIB_NS_USING(build_filename) +EDELIB_NS_USING(file_test) +EDELIB_NS_USING(str_ends) +EDELIB_NS_USING(FILE_TEST_IS_DIR) +EDELIB_NS_USING(DESK_FILE_TYPE_DIRECTORY) +EDELIB_NS_USING(ICON_SIZE_SMALL) + +#define DOT_OR_DOTDOT(base) (base[0] == '.' && (base[1] == '\0' || (base[1] == '.' && base[2] == '\0'))) +#define ELEMENT_IS(elem, val) (strcmp(elem->Value(), val) == 0) +#define ELEMENT_GET_TEXT(elem) (elem->FirstChild() ? elem->FirstChild()->ToText() : NULL) + +/* max. name size */ +#define NAME_BUFSZ 128 + +/* do not allow empty menus to be shown */ +#define NO_EMPTY_MENUS 1 + +/* in FLTK, a default size */ +extern int FL_NORMAL_SIZE; + +struct MenuParseContext; +struct MenuContext; + +typedef list MenuParseList; +typedef list::iterator MenuParseListIt; + +typedef list MenuContextList; +typedef list::iterator MenuContextListIt; + +struct MenuParseContext { + /* for tags */ + bool deleted; + + /* for and (default) */ + bool only_unallocated; + + /* menu tag content */ + String *name; + + /* a stack of .directory names; the top one is used */ + StrList dir_files; + + /* directories where to find .directory file */ + StrList dir_dirs; + + /* a list of .desktop files */ + DesktopEntryList desk_files; + + /* include rules */ + MenuRulesList include_rules; + + /* exclude rules */ + MenuRulesList exclude_rules; + + /* nested menus */ + MenuParseList submenus; +}; + +struct MenuContext { + /* menu label */ + String *name; + + /* should this entry be displayed */ + bool display_it; + + /* menu icon */ + String *icon; + + /* a list of .desktop files; at the same time also items in menu list */ + DesktopEntryList items; + + /* nested menus */ + MenuContextList submenus; +}; + +/* TODO: bug in edelib */ +static bool menu_context_sorter(MenuContext* const& c1, MenuContext* const& c2) { + return *(c1->name) < *(c2->name); +} + +static MenuParseContext *menu_parse_context_new(void) { + MenuParseContext *m = new MenuParseContext; + m->name = NULL; + m->deleted = false; + m->only_unallocated = false; + return m; +} + +static void menu_parse_context_delete(MenuParseContext *m) { + E_RETURN_IF_FAIL(m != NULL); + + delete m->name; + + /* delete rules */ + if(!m->include_rules.empty()) { + MenuRulesListIt it = m->include_rules.begin(), it_end = m->include_rules.end(); + while(it != it_end) { + menu_rules_delete(*it); + it = m->include_rules.erase(it); + } + } + + if(!m->exclude_rules.empty()) { + MenuRulesListIt it = m->exclude_rules.begin(), it_end = m->exclude_rules.end(); + while(it != it_end) { + menu_rules_delete(*it); + it = m->exclude_rules.erase(it); + } + } + + /* recurse for nested menus */ + if(!m->submenus.empty()) { + MenuParseListIt it = m->submenus.begin(), it_end = m->submenus.end(); + + while(it != it_end) { + menu_parse_context_delete(*it); + it = m->submenus.erase(it); + } + } + + delete m; +} + +static void menu_parse_context_append_default_dir_dirs(MenuParseContext *ctx) { + StrList lst; + + int ret = system_data_dirs(lst); + if(ret < 1) + return; + + StrListIt it = lst.begin(), it_end = lst.end(); + + for(; it != it_end; ++it) + ctx->dir_dirs.push_back(build_filename((*it).c_str(), "desktop-directories")); +} + +static void menu_parse_context_append_desktop_files(MenuParseContext *ctx, const char *dir, const char *basedir) { + DIR *ds = opendir(dir); + if(!ds) + return; + + dirent *dp; + DesktopEntry *entry; + + while((dp = readdir(ds)) != NULL) { + /* skip dots and (possibly) hidden files */ + if(dp->d_name[0] == '.' || DOT_OR_DOTDOT(dp->d_name)) + continue; + + entry = new DesktopEntry; + entry->assign_path(dir, dp->d_name, basedir); + + if(file_test(entry->get_path(), FILE_TEST_IS_DIR)) { + /* recurse if needed; the spec said we must */ + menu_parse_context_append_desktop_files(ctx, entry->get_path(), basedir); + + /* delete it */ + delete entry; + continue; + } + + /* files must ends with this extension */ + if(str_ends(entry->get_path(), ".desktop")) { + ctx->desk_files.push_back(entry); + } else { + /* clear non .desktop items */ + delete entry; + } + } + + closedir(ds); +} + +static void menu_parse_context_append_desktop_files_from_xdg_data_dirs(MenuParseContext *ctx) { + StrList lst; + if(system_data_dirs(lst) < 1) + return; + + 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()); +} + +static void scan_include_exclude_tag(TiXmlNode *elem, MenuRulesList &rules) { + E_RETURN_IF_FAIL(elem != NULL); + + TiXmlNode *child; + TiXmlText *txt; + + for(child = elem->FirstChildElement(); child; child = child->NextSibling()) { + /* assure we do not include/exclude insinde include/exclude */ + if(ELEMENT_IS(child, "Include") || ELEMENT_IS(child, "Exclude")) { + E_WARNING(E_STRLOC ": Nesting and tags are not supported\n"); + continue; + } + + if(ELEMENT_IS(child, "Filename")) { + txt = ELEMENT_GET_TEXT(child); + menu_rules_append_rule(rules, MENU_RULES_OPERATOR_FILENAME, txt->Value()); + continue; + } + + if(ELEMENT_IS(child, "Category")) { + txt = ELEMENT_GET_TEXT(child); + menu_rules_append_rule(rules, MENU_RULES_OPERATOR_CATEGORY, txt->Value()); + continue; + } + + if(ELEMENT_IS(child, "All")) { + menu_rules_append_rule(rules, MENU_RULES_OPERATOR_ALL, NULL); + continue; + } + + if(ELEMENT_IS(child, "And")) { + MenuRules *and_rule = menu_rules_append_rule(rules, MENU_RULES_OPERATOR_AND, NULL); + /* recurse */ + scan_include_exclude_tag(child, and_rule->subrules); + continue; + } + + if(ELEMENT_IS(child, "Or")) { + MenuRules *or_rule = menu_rules_append_rule(rules, MENU_RULES_OPERATOR_OR, NULL); + /* recurse */ + scan_include_exclude_tag(child, or_rule->subrules); + continue; + } + + if(ELEMENT_IS(child, "Not")) { + MenuRules *not_rule = menu_rules_append_rule(rules, MENU_RULES_OPERATOR_NOT, NULL); + /* recurse */ + scan_include_exclude_tag(child, not_rule->subrules); + continue; + } + } +} + +static void scan_menu_tag(TiXmlNode *elem, MenuParseList &parse_list) { + E_RETURN_IF_FAIL(elem != NULL); + + TiXmlText *txt; + bool got_default_app_dirs = false, got_default_dir_dirs = false; + + MenuParseContext *ctx = menu_parse_context_new(); + + for(elem = elem->FirstChildElement(); elem; elem = elem->NextSibling()) { + /* in case we got '' as submenu, dive in it recursively and fill submenus */ + if(ELEMENT_IS(elem, "Menu")) + scan_menu_tag(elem, ctx->submenus); + + if(ELEMENT_IS(elem, "Name")) { + txt = ELEMENT_GET_TEXT(elem); + if(txt && !ctx->name) + ctx->name = new String(txt->Value()); + + continue; + } + + if(ELEMENT_IS(elem, "Directory")) { + txt = ELEMENT_GET_TEXT(elem); + /* entries must ends with .directory */ + if(txt && str_ends(txt->Value(), ".directory")) { + /* push it at the top */ + ctx->dir_files.push_front(txt->Value()); + } + + continue; + } + + if(ELEMENT_IS(elem, "AppDir")) { + txt = ELEMENT_GET_TEXT(elem); + if(txt) + menu_parse_context_append_desktop_files(ctx, txt->Value(), NULL); + + continue; + } + + if(ELEMENT_IS(elem, "DirectoryDir")) { + txt = ELEMENT_GET_TEXT(elem); + if(txt) { + /* push it at the top */ + ctx->dir_dirs.push_front(txt->Value()); + } + + continue; + } + + /* spec: '' expands to $XDG_DATA_DIRS/applications */ + if(ELEMENT_IS(elem, "DefaultAppDirs")) { + if(!got_default_app_dirs) { + menu_parse_context_append_desktop_files_from_xdg_data_dirs(ctx); + /* scan it only once */ + got_default_app_dirs = true; + } + + continue; + } + + if(ELEMENT_IS(elem, "DefaultDirectoryDirs")) { + if(!got_default_dir_dirs) { + menu_parse_context_append_default_dir_dirs(ctx); + got_default_dir_dirs = true; + } + + continue; + } + + if(ELEMENT_IS(elem, "Include")) { + scan_include_exclude_tag(elem, ctx->include_rules); + continue; + } + + if(ELEMENT_IS(elem, "Exclude")) { + scan_include_exclude_tag(elem, ctx->exclude_rules); + continue; + } + + if(ELEMENT_IS(elem, "Deleted")) { + ctx->deleted = true; + continue; + } + + if(ELEMENT_IS(elem, "NotDeleted")) { + ctx->deleted = false; + continue; + } + + if(ELEMENT_IS(elem, "OnlyUnallocated")) { + ctx->only_unallocated = true; + continue; + } + + if(ELEMENT_IS(elem, "NotOnlyUnallocated")) { + ctx->only_unallocated = false; + continue; + } + } + + parse_list.push_back(ctx); +} + +static bool menu_context_construct_name_and_get_icon(MenuParseContext *m, + MenuParseContext *top, + String **ret_name, + String **ret_icon, + bool *should_be_displayed) +{ + E_RETURN_VAL_IF_FAIL(m != NULL, false); + + *ret_name = *ret_icon = NULL; + *should_be_displayed = true; + + if(!m->dir_files.empty()) { + /* + * We have two locations where are keeping directory list: node specific and top node + * specific, where often is put tag. Here, first we will look in + * node specific directory list, then will go in top node . + */ + StrListIt dir_it = m->dir_dirs.begin(), dir_it_end = m->dir_dirs.end(); + + /* this list has 'stack-ed' items; the last one is on top */ + StrListIt file_it, file_it_end = m->dir_files.end(); + + DesktopFile df; + String tmp; + + /* first try specific directory list */ + for(; dir_it != dir_it_end; ++dir_it) { + for(file_it = m->dir_files.begin(); file_it != file_it_end; ++file_it) { + tmp = build_filename((*dir_it).c_str(), (*file_it).c_str()); + //E_DEBUG("==> %s\n", tmp.c_str()); + + /* load it and see if it is real .desktop file */ + df.load(tmp.c_str()); + + if(df && (df.type() == DESK_FILE_TYPE_DIRECTORY)) { + /* check if it can be displayed */ + if(df.no_display()) + *should_be_displayed = false; + + char buf[NAME_BUFSZ]; + + /* try icon first */ + if(!(*ret_icon) && df.icon(buf, NAME_BUFSZ)) + *ret_icon = new String(buf); + + /* then name, so we can quit nicely */ + if(!(*ret_name) && df.name(buf, NAME_BUFSZ)) { + *ret_name = new String(buf); + return true; + } + } + } + } + + /* now try default ones */ + dir_it = top->dir_dirs.begin(), dir_it_end = top->dir_dirs.end(); + + for(; dir_it != dir_it_end; ++dir_it) { + for(file_it = m->dir_files.begin(); file_it != file_it_end; ++file_it) { + tmp = build_filename((*dir_it).c_str(), (*file_it).c_str()); + //E_DEBUG("++> %s\n", tmp.c_str()); + + /* load it and see if it is real .desktop file */ + df.load(tmp.c_str()); + if(df && (df.type() == DESK_FILE_TYPE_DIRECTORY)) { + /* check if it can be displayed */ + if(df.no_display()) + *should_be_displayed = false; + + char buf[NAME_BUFSZ]; + + /* try icon first */ + if(!(*ret_icon) && df.icon(buf, NAME_BUFSZ)) + *ret_icon = new String(buf); + + /* then name, so we can quit nicely */ + if(!(*ret_name) && df.name(buf, NAME_BUFSZ)) { + *ret_name = new String(buf); + return true; + } + } + } + } + } + + E_RETURN_VAL_IF_FAIL(m->name != NULL, false); + + /* if there are no files and can be displayed, use context name; let icon name be NULL */ + *ret_name = new String(*(m->name)); + return true; +} + +static void apply_include_rules(MenuContext *ctx, DesktopEntryList &items, MenuRulesList &rules) { + if(items.empty() || rules.empty()) + return; + + MenuRulesListIt rit, rit_end = rules.end(); + DesktopEntryListIt it = items.begin(), it_end = items.end(); + + DesktopEntry *entry; + bool eval_true; + + for(; it != it_end; ++it) { + for(rit = rules.begin(); rit != rit_end; ++rit) { + entry = *it; + + eval_true = menu_rules_eval(*rit, entry); + + /* append entry if matches to the rule, and mark it as allocated */ + if(eval_true) { + entry->mark_as_allocated(); + ctx->items.push_back(entry); + + /* do not scan rules any more; go to the next item */ + break; + } + } + } +} + +static void apply_exclude_rules(DesktopEntryList& items, MenuRulesList &rules) { + if(items.empty() || rules.empty()) + return; + + MenuRulesListIt rit, rit_end = rules.end(); + DesktopEntryListIt it = items.begin(), it_end = items.end(); + bool eval_true; + + while(it != it_end) { + eval_true = false; + + for(rit = rules.begin(); rit != rit_end; ++rit) { + eval_true = menu_rules_eval(*rit, *it); + + if(eval_true) { + /* pop entry if matches */ + it = items.erase(it); + break; + } + } + + if(!eval_true) + ++it; + } +} + +#if NO_EMPTY_MENUS +/* forward decl */ +static void menu_context_delete(MenuContext *c); +#endif + +static MenuContext *menu_parse_context_to_menu_context(MenuParseContext *m, + MenuParseContext *top, + DesktopEntryList *all_unallocated) +{ + E_RETURN_VAL_IF_FAIL(m != NULL, NULL); + + /* make sure we are not processing only_unallocated node when not get all_unallocated */ + if(m->only_unallocated && !all_unallocated) + return NULL; + + /* + * 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; + + if(!menu_context_construct_name_and_get_icon(m, top, &n, &ic, &should_be_displayed)) + return NULL; + + /* + * nodes marked as 'NoDisplay' (from .directory file) or '' (from applications.menu) must + * be processed as ordinary nodes, since this operation will correctly setup allocated + * ( and ) entries + */ + if(m->deleted) + should_be_displayed = false; + + /* assure we got name here; icon can be NULL */ + E_RETURN_VAL_IF_FAIL(n != NULL, NULL); + + MenuContext *ctx = new MenuContext; + 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 */ + if(all_unallocated) { + apply_include_rules(ctx, *all_unallocated, m->include_rules); + } else { + apply_include_rules(ctx, m->desk_files, m->include_rules); + /* check the rules for top list, but make sure we are not applying them on the same node again */ + if(m != top) + apply_include_rules(ctx, top->desk_files, m->include_rules); + } + + /* pop filled MenuContext items if match the rule */ + apply_exclude_rules(ctx->items, m->exclude_rules); + + //E_DEBUG("- Menu: %s %i\n", ctx->name->c_str(), ctx->items.size()); + + /* sort entries via their full names */ + desktop_entry_list_sort(ctx->items); + + /* process submenus */ + if(!m->submenus.empty()) { + MenuParseListIt mit = m->submenus.begin(), mit_end = m->submenus.end(); + MenuContext *sub_ctx; + + for(; mit != mit_end; ++mit) { + sub_ctx = menu_parse_context_to_menu_context(*mit, top, all_unallocated); + + if(sub_ctx) + ctx->submenus.push_back(sub_ctx); + } + } + +#if NO_EMPTY_MENUS + /* do not allow empty menus */ + if(ctx->items.empty() && ctx->submenus.empty()) { + menu_context_delete(ctx); + ctx = NULL; + } +#endif + + return ctx; +} + +static void menu_context_delete(MenuContext *c) { + E_RETURN_IF_FAIL(c != NULL); + + if(!c->submenus.empty()) { + MenuContextListIt it = c->submenus.begin(), it_end = c->submenus.end(); + for(; it != it_end; ++it) + menu_context_delete(*it); + } + + c->items.clear(); + delete c->name; + delete c->icon; + delete c; +} + +static void menu_parse_context_list_get_all_unallocated_desk_files(MenuParseList &parse_list, DesktopEntryList &ret) { + if(parse_list.empty()) + return; + + MenuParseListIt it = parse_list.begin(), it_end = parse_list.end(); + DesktopEntryListIt dit, dit_end; + MenuParseContext *parse_ctx; + + for(; it != it_end; ++it) { + parse_ctx = *it; + + dit = parse_ctx->desk_files.begin(); + dit_end = parse_ctx->desk_files.end(); + + for(; dit != dit_end; ++dit) { + if((*dit)->is_allocated() == false) + ret.push_back(*dit); + } + + /* recurse */ + menu_parse_context_list_get_all_unallocated_desk_files(parse_ctx->submenus, ret); + } +} + +static void menu_context_list_sort(MenuContextList &lst) { + if(lst.empty()) + return; + + lst.sort(menu_context_sorter); + + MenuContextListIt it = lst.begin(), it_end = lst.end(); + for(; it != it_end; ++it) + menu_context_list_sort((*it)->submenus); +} + +static void menu_parse_context_list_to_menu_context_list(MenuParseList &parse_list, + MenuContextList &ret) +{ + MenuParseListIt it = parse_list.begin(), it_end = parse_list.end(); + MenuParseContext *parse_ctx; + MenuContext *ctx; + + for(; it != it_end; ++it) { + parse_ctx = *it; + + /* remove duplicate id's */ + desktop_entry_list_remove_duplicates(parse_ctx->desk_files); + + /* read all .desktop files from disk */ + desktop_entry_list_load_all(parse_ctx->desk_files); + + /* now convert it to usable menu node */ + ctx = menu_parse_context_to_menu_context(parse_ctx, parse_ctx, NULL); + + if(ctx) + ret.push_back(ctx); + } + + /* now, pickup all unallocated items */ + DesktopEntryList all_unallocated; + menu_parse_context_list_get_all_unallocated_desk_files(parse_list, all_unallocated); + + /* + * second pass; process unallocated items, but put them in second list that will later be + * merged; this is to preserve the order got in the first list + */ + MenuContextList unallocated_list; + + for(it = parse_list.begin(); it != it_end; ++it) { + parse_ctx = *it; + + /* now convert it to usable menu node */ + ctx = menu_parse_context_to_menu_context(parse_ctx, parse_ctx, &all_unallocated); + + if(ctx) + unallocated_list.push_back(ctx); + } + + /* + * both list have the same root node, so we skip the first node and merge below it; the first node is + * top level and is often only menu name and description + */ + E_RETURN_IF_FAIL(ret.size() == 1); + E_RETURN_IF_FAIL(unallocated_list.size() == 1); + + MenuContext *head = ret.front(), *unallocated_head = unallocated_list.front(); + MenuContextListIt uit = unallocated_head->submenus.begin(), uit_end = unallocated_head->submenus.end(); + + for(; uit != uit_end; ++uit) + head->submenus.push_back(*uit); + + /* sort everthing at the end */ + menu_context_list_sort(ret); +} + +/* + * Count the number of items in each submenu + submenu node itself; used to allocate + * array for edelib::MenuItem list. + */ +static unsigned int menu_context_list_count(MenuContextList &lst) { + if(lst.empty()) + return 0; + + unsigned int ret = lst.size(); + + MenuContextListIt it = lst.begin(), it_end = lst.end(); + MenuContext *cc; + + for(; it != it_end; ++it) { + cc = *it; + ret += cc->items.size(); + + ret += menu_context_list_count(cc->submenus); + + /* + * a room for NULL to deduce submenus in edelib::MenuItem, no matter if submenus are empty + * in case empty menus are going to be displayed + */ + ret += 1; + } + + return ret; +} + +static void menu_all_parse_lists_clear(MenuParseList &parse_list, MenuContextList &ctx_list) { + MenuContextListIt cit = ctx_list.begin(), cit_end = ctx_list.end(); + MenuParseListIt pit = parse_list.begin(), pit_end = parse_list.end(); + + MenuParseContext *cc; + + while(cit != cit_end) { + menu_context_delete(*cit); + cit = ctx_list.erase(cit); + } + + while(pit != pit_end) { + cc = *pit; + /* + * Desktop entries are shared among MenuContext and MenuParseContext, so they + * must be explicitly deleted. This sharing depends on 'Include' rules, so some MenuParseContext + * entries are on MenuContext list and all MenuContext entries are in MenuParseContext. + */ + DesktopEntryListIt it = cc->desk_files.begin(), it_end = cc->desk_files.end(); + while(it != it_end) { + delete *it; + it = cc->desk_files.erase(it); + } + + menu_parse_context_delete(cc); + pit = parse_list.erase(pit); + } +} + +static TiXmlNode *load_menu_file(TiXmlDocument &doc) { + char *menu_prefix = getenv("XDG_MENU_PREFIX"); + String menu_file; + + if(menu_prefix) { + menu_file = menu_prefix; + menu_file += "applications.menu"; + } else { + menu_file = "applications.menu"; + } + + StrList paths; + if(system_config_dirs(paths) < 1) + return NULL; + + String tmp; + StrListIt it = paths.begin(), it_end = paths.end(); + + for(; it != paths.end(); ++it) { + tmp = build_filename((*it).c_str(), "menus", menu_file.c_str()); + + if(doc.LoadFile(tmp.c_str())) + goto done; + } + + return NULL; + +done: + return doc.FirstChild("Menu"); +} + +static void menu_context_list_dump(MenuContextList &lst) { + if(lst.empty()) + return; + + MenuContextListIt it = lst.begin(), it_end = lst.end(); + DesktopEntryListIt ds, de; + + for(; it != it_end; ++it) { + if((*it)->display_it == false) + continue; + + ds = (*it)->items.begin(); + de = (*it)->items.end(); + + /* print each desktop entry with menu name */ + for(; ds != de; ++ds) { + printf("%s/\t%s\t%s\n", (*it)->name->c_str(), + (*ds)->get_id(), + (*ds)->get_path()); + } + + menu_context_list_dump((*it)->submenus); + } +} + +static void menu_all_parse_lists_load(MenuParseList &parse_list, MenuContextList &content) { + /* + * TiXmlDocument object must be used externaly, so as long as this object is + * alive, the whole XML tree is alive too (see DOM reference). + */ + TiXmlDocument doc; + + TiXmlNode *elem = load_menu_file(doc); + if(!elem) + return; + + /* parse XML file */ + scan_menu_tag(elem, parse_list); + + /* convert it to our list */ + menu_parse_context_list_to_menu_context_list(parse_list, content); +} + +void xdg_menu_dump_for_test_suite(void) { + MenuParseList pl; + MenuContextList cl; + + /* load everything */ + menu_all_parse_lists_load(pl, cl); + + menu_context_list_dump(cl); + + /* clear everything */ + 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; + +static void item_cb(Fl_Widget *, void *en) { + DesktopEntry *entry = (DesktopEntry*)en; + + E_DEBUG("RUN %s\n", entry->get_exec()); +} + +static unsigned int construct_edelib_menu(MenuContextList &lst, MenuItem *mi, unsigned int pos) { + if(lst.empty()) + return pos; + + MenuContextListIt it = lst.begin(), it_end = lst.end(); + MenuContext *cc; + + DesktopEntryListIt ds, de; + + unsigned long initial_pos = pos; + + for(; it != it_end; ++it) { + cc = *it; + + if(!cc->display_it) + continue; + + mi[pos].text = cc->name->c_str(); + + /* every MenuContext is submenu for itself */ + mi[pos].flags = FL_SUBMENU; + + //E_DEBUG("{%s submenu}\n", mi[pos].text); + + /* some default values that must be filled */ + mi[pos].shortcut_ = 0; + mi[pos].callback_ = 0; + mi[pos].user_data_ = 0; + mi[pos].labeltype_ = FL_NORMAL_LABEL; + mi[pos].labelfont_ = FL_HELVETICA; + mi[pos].labelsize_ = FL_NORMAL_SIZE; + mi[pos].labelcolor_ = FL_BLACK; + + mi[pos].image(NULL); + + /* set image for menu */ + if(cc->icon && IconLoader::inited()) { + Fl_Image *img = IconLoader::get(cc->icon->c_str(), ICON_SIZE_SMALL); + mi[pos].image(img); + } + + /* a room for an item */ + pos++; + + /* try with nested submenus first, so submenus be before desktop entries in current menu node */ + pos = construct_edelib_menu(cc->submenus, mi, pos); + + /* now, add the real items if they exists*/ + if(!cc->items.empty()) { + ds = cc->items.begin(); + de = cc->items.end(); + + for(; ds != de; ++ds, ++pos) { + mi[pos].text = (*ds)->get_name(); + mi[pos].flags = 0; + + //E_DEBUG(" {%s item}\n", mi[pos].text); + + /* some default values that must be filled */ + mi[pos].shortcut_ = 0; + + /* set callback and callback data to be current entry */ + mi[pos].callback_ = item_cb; + mi[pos].user_data_ = *ds; + + mi[pos].labeltype_ = FL_NORMAL_LABEL; + mi[pos].labelfont_ = FL_HELVETICA; + mi[pos].labelsize_ = FL_NORMAL_SIZE; + mi[pos].labelcolor_ = FL_BLACK; + mi[pos].image(NULL); + + /* set image for menu item*/ + if((*ds)->get_icon() && IconLoader::inited()) { + Fl_Image *img = IconLoader::get((*ds)->get_icon(), ICON_SIZE_SMALL); + mi[pos].image(img); + } + } + } + + /* to inject Logout button */ + if(initial_pos == 0) { + //E_DEBUG(" {Logout item}\n"); + + mi[pos].text = _("Logout"); + + if(pos) + mi[pos - 1].flags |= FL_MENU_DIVIDER; + + mi[pos].flags = 0; + mi[pos].shortcut_ = 0; + mi[pos].image(NULL); + mi[pos].labeltype_ = FL_NORMAL_LABEL; + mi[pos].labelfont_ = FL_HELVETICA; + mi[pos].labelsize_ = FL_NORMAL_SIZE; + mi[pos].labelcolor_ = FL_BLACK; + + /* set callback and callback data to be current entry */ + mi[pos].callback_ = 0; + mi[pos].user_data_ = 0; + + if(IconLoader::inited()) { + Fl_Image *img = IconLoader::get("system-log-out", ICON_SIZE_SMALL); + mi[pos].image(img); + } + + pos++; + } + + /* end this menu */ + mi[pos].text = NULL; + mi[pos].image(NULL); + + //E_DEBUG("{0}\n"); + + /* make a room for the next item */ + pos++; + + } + + /* return position to next item */ + 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); + + /* load everything */ + menu_all_parse_lists_load(global_parse_list, global_context_list); + + unsigned int sz = menu_context_list_count(global_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); + mi[pos].text = NULL; + + /* + * MenuItem does not have constructor, so everywhere where we access MenuItem object, image + * member must be NULL-ed too + */ + mi[pos].image(NULL); + + E_ASSERT(pos <= sz + 2); + return mi; +} + +void xdg_menu_delete(MenuItem *m) { + delete [] m; + menu_all_parse_lists_clear(global_parse_list, global_context_list); +} diff --git a/ede-panel/applets/start-menu/XdgMenuReader.h b/ede-panel/applets/start-menu/XdgMenuReader.h new file mode 100644 index 0000000..8fc3c99 --- /dev/null +++ b/ede-panel/applets/start-menu/XdgMenuReader.h @@ -0,0 +1,13 @@ +#ifndef __XDGMENUREADER_H__ +#define __XDGMENUREADER_H__ + +#include + +EDELIB_NS_USING(MenuItem) + +void xdg_menu_dump_for_test_suite(void); + +MenuItem *xdg_menu_load(void); +void xdg_menu_delete(MenuItem *it); + +#endif diff --git a/ede-panel/applets/start-menu/applications.menu b/ede-panel/applets/start-menu/applications.menu new file mode 100644 index 0000000..ff68d52 --- /dev/null +++ b/ede-panel/applets/start-menu/applications.menu @@ -0,0 +1,55 @@ + + + + Applications + Applications.directory + + <-- Search the default locations --> + + + + <-- Merge third-party submenus --> + applications-merged + + <-- Merge legacy hierarchy --> + /usr/share/applnk + + <-- Define default layout --> + + + + + More + + + <-- some random moves, maybe to clean up legacy dirs, + maybe from menu editing --> + + Foo + Bar + Foo2 + Bar2 + + + <-- A preferences submenu, kept in a separate file + so it can also be used standalone --> + + Preferences + Preferences.directory + preferences.menu + + + <-- An Office submenu, specified inline --> + + Office + Office.directory + + Office + + + foo.desktop + + + + +} diff --git a/ede-panel/applets/start-menu/applications.menu.1 b/ede-panel/applets/start-menu/applications.menu.1 new file mode 100644 index 0000000..6982b86 --- /dev/null +++ b/ede-panel/applets/start-menu/applications.menu.1 @@ -0,0 +1,50 @@ + + + + + + Applications + + + + /home/ravenlock/.local/share/applications + + + + + + + + + + + Testing + + Testing2 + + + + + Testing + TestingCat + + + Testing-sub + TestingCatSub + + + + + + + Testing2 + TestingCat2 + + + Testing-sub2 + TestingCatSub2 + + + + + diff --git a/ede-panel/applets/start-menu/ede-icon.h b/ede-panel/applets/start-menu/ede-icon.h new file mode 100644 index 0000000..a4b4371 --- /dev/null +++ b/ede-panel/applets/start-menu/ede-icon.h @@ -0,0 +1,55 @@ +#ifndef __EDE_ICON_H__ +#define __EDE_ICON_H__ + +#include +static unsigned char idata_ede_icon[] = +{49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49, +78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108, +255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78, +108,255,49,78,108,236,49,78,108,192,49,78,108,192,49,78,108,192,49,78,108,192, +49,78,108,192,49,78,108,192,49,78,108,192,49,78,108,192,49,78,108,192,49,78, +108,192,49,78,108,192,49,78,108,192,49,78,108,232,49,78,108,255,49,78,108,255, +49,78,108,176,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255, +255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255, +255,255,0,255,255,255,0,49,78,108,160,49,78,108,255,49,78,108,255,49,78,108,176, +255,255,255,0,255,255,255,0,255,255,255,0,45,75,105,17,49,78,109,98,49,78,108, +134,50,78,108,92,53,79,106,29,255,255,255,0,255,255,255,0,255,255,255,0,255,255, +255,0,49,78,108,160,49,78,108,255,49,78,108,255,49,78,108,176,255,255,255,0,255, +255,255,0,49,79,109,42,49,78,108,229,49,78,108,255,49,78,108,255,49,78,108,255, +49,78,108,241,48,77,108,80,255,255,255,0,255,255,255,0,255,255,255,0,49,78,108, +160,49,78,108,255,49,78,108,255,49,78,108,176,255,255,255,0,255,255,255,0,49,78, +109,202,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255, +48,78,108,179,51,77,102,10,255,255,255,0,49,78,108,29,49,78,108,214,49,78,108, +255,49,78,108,255,49,78,108,176,255,255,255,0,49,79,109,42,49,78,108,255,49,78, +108,255,49,78,108,255,49,78,108,255,49,78,108,210,48,78,107,69,255,255,255,0,52, +81,110,21,49,78,108,134,49,78,108,246,49,78,108,255,49,78,108,255,49,78,108, +255,49,78,108,176,255,255,255,0,47,77,109,70,49,78,108,255,49,78,108,255,49,78, +108,233,50,78,108,102,85,85,85,3,33,52,72,3,49,78,108,156,49,78,108,252,49,78, +108,255,49,78,108,210,49,78,108,186,49,78,108,255,49,78,108,255,49,78,108,176, +255,255,255,0,51,80,109,35,49,78,108,248,49,77,108,135,55,73,109,14,255,255,255, +0,49,79,108,115,49,78,108,246,49,78,108,255,49,78,108,255,49,78,108,130,49,78, +108,3,49,78,108,160,49,78,108,255,49,78,108,255,49,78,108,176,255,255,255,0,255, +255,255,0,51,77,111,30,255,255,255,0,48,78,109,74,49,78,108,232,49,78,108,255, +49,78,108,255,49,78,108,255,49,78,108,255,50,77,109,56,255,255,255,0,49,78,108, +160,49,78,108,255,49,78,108,255,49,78,108,176,255,255,255,0,255,255,255,0,0,0,0, +1,50,78,108,144,49,78,108,253,49,78,108,255,49,78,108,255,49,78,108,255,49,78, +108,255,49,79,108,224,51,102,102,5,255,255,255,0,49,78,108,160,49,78,108,255,49, +78,108,255,49,78,108,176,255,255,255,0,255,255,255,0,255,255,255,0,50,77,107, +76,49,78,108,244,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,239,49,76, +107,57,255,255,255,0,255,255,255,0,49,78,108,160,49,78,108,255,49,78,108,255,49, +78,108,176,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,48,80,112, +16,50,79,107,107,48,77,108,142,49,78,109,115,53,74,106,24,255,255,255,0,255, +255,255,0,255,255,255,0,49,78,108,160,49,78,108,255,49,78,108,255,49,78,108,176, +255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255, +255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255,255,255,0,255, +255,255,0,49,78,108,160,49,78,108,255,49,78,108,255,49,78,108,223,49,78,108,152, +49,78,108,152,49,78,108,152,49,78,108,152,49,78,108,152,49,78,108,152,49,78, +108,152,49,78,108,152,49,78,108,152,49,78,108,152,49,78,108,152,49,78,108,152, +49,78,108,217,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78, +108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255, +49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78,108,255,49,78, +108,255,49,78,108,255}; + +static Fl_RGB_Image ede_icon_image(idata_ede_icon, 16, 16, 4, 0); + +#endif diff --git a/ede-panel/applets/start-menu/ede-menu-spec-test.cpp b/ede-panel/applets/start-menu/ede-menu-spec-test.cpp new file mode 100644 index 0000000..c1a94bc --- /dev/null +++ b/ede-panel/applets/start-menu/ede-menu-spec-test.cpp @@ -0,0 +1,6 @@ +#include "XdgMenuReader.h" + +int main(int argc, char **argv) { + xdg_menu_dump_for_test_suite(); + return 0; +} diff --git a/ede-panel/applets/start-menu/icons/ede.png b/ede-panel/applets/start-menu/icons/ede.png new file mode 100644 index 0000000000000000000000000000000000000000..167041f254cf44394e4d2543e5151655595372af GIT binary patch literal 542 zcmV+(0^$9MP)ugK~y-6rIWo&TVWK3pEvjQs|C?X2^JhuQ3oNn>eQu+ljg?J z$-yB|yyD=JSr7+7+pB}3AoLHgF(QZ}6a+y`91KFiK#SlM>{WgP|p2&+KYm#s%J(j7~@D%yVo^acQC ze5@0s$Jcx;NZ(P)8Bn_RwQ1Q?z;~b{0Ps9oo^5|ANJMI`qPOSyUDL9cUDvw+lE5jj z9RO&9JWxQGuD(}qGbv~3^sZ^VAsqP)i~t5O2}FVO0N`-#b3PeSSkixC4`*8jx5_yDw( g9JB(73IPB-0-@Z2ibs_f^#A|>07*qoM6N<$f{DZA9smFU literal 0 HcmV?d00001 diff --git a/ede-panel/applets/start-menu/tests/ChangeLog b/ede-panel/applets/start-menu/tests/ChangeLog new file mode 100644 index 0000000..ac55e4f --- /dev/null +++ b/ede-panel/applets/start-menu/tests/ChangeLog @@ -0,0 +1,70 @@ +2005-04-25 Mark McLoughlin + + * tests/u: add test for recursive inclusion of .menu files. + Also know as the "get really hosed and eat all the RAM + you can find" test. + +2005-04-20 Waldo Bastian + + * tests/s/result, + tests/s/test: Test that .desktop files under $HOME correctly + override .desktop files at system level + + * tests/t/result, + tests/t/test: Test order in which elements are processed + +2005-02-18 Mark McLoughlin + + Make it a bit more difficult in order to catch: + http://bugzilla.gnome.org/show_bug.cgi?id=167758 + Thanks to Chris Lahey for the test case. + + * tests/o/test: put freecell.desktop in a subdirectory. + +2005-02-18 Mark McLoughlin + + * tests/o/result, + tests/o/test: test that + foofoo + doesn't match anything. + + * README: add bit about how to test the GNOME impl. + +2005-02-18 Mark McLoughlin + + Problem pointed out by Waldo. + + * menutest: don't try and run the "CVS" test :) + + * tests/m/result, + tests/n/result: items in a hidden or deleted menu + should be considered allocated. + +2004-12-08 Mark McLoughlin + + * tests/2/result, + tests/2/test: test the new behaviour. + +2004-08-29 Mark McLoughlin + + * tests/m: test for NoDisplay=true behavious in .desktop and + .directory files. + + * data/hidden.desktop, + data/hidden.directory: add. + + * data/apps.directory: add Type=Directory. + + * menutest: actually generate a test result if one doesn't + exist - makes it easier to write new tests. + +2003-10-23 Havoc Pennington + + * tests/f/test: remove from inside + statement, not allowed. + +2003-10-16 Havoc Pennington + + * menutest: cat log file on failure; print list of failed tests + when we're done + diff --git a/ede-panel/applets/start-menu/tests/README b/ede-panel/applets/start-menu/tests/README new file mode 100644 index 0000000..b003dc0 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/README @@ -0,0 +1,44 @@ +This directory contains regression tests for the menu-spec. + +To run these tests your menu-spec implementation should be +able to generate a menu in the following text format: + + + +Example: + +Editors/ kde-kwrite.desktop /home/bastian/.local/share/applications/kde-kwrite.desktop +Editors/ kde-kate.desktop /home/bastian/.local/share/applications/kde-kate.desktop +Editors/ kde-KEdit.desktop /home/bastian/.local/share/applications/kde-KEdit.desktop +Development/ kde-gideon.desktop /opt/kde3/share/applnk/Development/gideon.desktop +Development/ kde-kbabel.desktop /opt/kde3/share/applnk/Development/kbabel.desktop +Development/ kde-quanta.desktop /opt/kde3/share/applnk/Development/quanta.desktop +/ kde-Kfind.desktop /opt/kde3/share/applnk/Kfind.desktop +/ kde-Home.desktop /opt/kde3/share/applnk/Home.desktop +/ kde-Help.desktop /opt/kde3/share/applnk/Help.desktop + + + +The environment variable $MENUTEST should point to a command that is +able to generate the menu in the above format. + +To test KDE one can use: + MENUTEST="kbuildsycoca --menutest" + +With GNOME, you can use: + MENUTEST=gnome-menu-spec-test + +The menutest script should be used to run the tests. The following commands can +be used: + + MENUTEST="foo -bar" ./menutest + +to run all tests + + MENUTEST="foo -bar" TESTS="Deleted Directory" ./menutest + +to run the tests named "Deleted" and "Directory" only + +All tests contain of a test setup script named "test" and a file describing +the expected menu named "result". + diff --git a/ede-panel/applets/start-menu/tests/README.EDE b/ede-panel/applets/start-menu/tests/README.EDE new file mode 100644 index 0000000..c3dcab0 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/README.EDE @@ -0,0 +1,7 @@ +This is an regression suite taken from http://freedesktop.org, to test +conformance against XDG menu specification. + +Some test results (AppDir) were missing, so they are added. The rest +is unchanged. + +Sanel Zukan diff --git a/ede-panel/applets/start-menu/tests/TODO b/ede-panel/applets/start-menu/tests/TODO new file mode 100644 index 0000000..6e178bf --- /dev/null +++ b/ede-panel/applets/start-menu/tests/TODO @@ -0,0 +1,5 @@ +I hope you understand what i mean ;) + +desktop-file-id stuff & overwrite stuff absolute path + +onlyshowin - notshowin diff --git a/ede-panel/applets/start-menu/tests/data/Help.desktop b/ede-panel/applets/start-menu/tests/data/Help.desktop new file mode 100644 index 0000000..dc867cd --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/Help.desktop @@ -0,0 +1,68 @@ +[Desktop Entry] +Encoding=UTF-8 +Exec=khelpcenter +Icon=khelpcenter +DocPath=khelpcenter/index.html +Type=Application +Terminal=0 + +Name=Help +Name[af]=Hulp +Name[az]=Yardım +Name[be]=Дапамога +Name[bg]=Помощ +Name[br]=Skoazell +Name[bs]=Pomoć +Name[ca]=Ajuda +Name[cs]=Nápověda +Name[cy]=Cymorth +Name[da]=Hjælp +Name[de]=Hilfe +Name[el]=Βοήθεια +Name[eo]=Helpo +Name[es]=Ayuda +Name[et]=Abiinfo +Name[eu]=Laguntza +Name[fa]=راهنما +Name[fi]=Ohje +Name[fr]=Aide +Name[gl]=Axuda +Name[he]=עזרה +Name[hr]=Pomoć +Name[hu]=Segítség +Name[id]=Keterangan bantu +Name[is]=Hjálp +Name[it]=Aiuto +Name[ja]=ヘルプ +Name[ko]=도움말 +Name[lo]=ລະບົບຊ່ວຍເຫືລອ +Name[lt]=Pagalba +Name[lv]=Palīdzība +Name[mn]=Тусламж +Name[mt]=Għajnuna +Name[nb]=Hjelp +Name[nn]=Hjelp +Name[nso]=Thuso +Name[oc]=Ajuda +Name[pl]=Pomoc +Name[pt]=Ajuda +Name[pt_BR]=Ajuda +Name[ro]=Ajutor +Name[ru]=Справка +Name[sk]=Pomocník +Name[sl]=Pomoč +Name[sr]=Pomoć +Name[ss]=Sita +Name[sv]=Hjälp +Name[ta]=¯¾Å¢ +Name[th]=ระบบช่วยเหลือ +Name[tr]=Yardım +Name[uk]=Довідка +Name[ven]=Thuso +Name[vi]=Trợ giúp +Name[wa]=Aidance +Name[xh]=Uncedo +Name[zh_CN]=帮助 +Name[zh_TW]=求助 +Name[zu]=Usizo + diff --git a/ede-panel/applets/start-menu/tests/data/Home.desktop b/ede-panel/applets/start-menu/tests/data/Home.desktop new file mode 100644 index 0000000..2bf8178 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/Home.desktop @@ -0,0 +1,127 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Application +Exec=kfmclient openProfile filemanagement +Icon=folder_home +Terminal=0 + +Name=Home +Name[af]=Huis +Name[az]=Başlanğıc +Name[be]=Хатні +Name[bg]=Домашна директория +Name[br]=Er-gêr +Name[bs]=Početak +Name[ca]=Inici +Name[cs]=Můj adresář +Name[cy]=Cartref +Name[da]=Hjem +Name[de]=Persönliches Verzeichnis +Name[el]=Σπίτι +Name[eo]=Hejmo +Name[es]=Personal +Name[et]=Kodukataloog +Name[eu]=Etxea +Name[fa]=خانه +Name[fi]=Koti +Name[fr]=Dossier personnel +Name[gl]=Persoal +Name[he]=בית +Name[hr]=Početak +Name[hu]=Saját könyvtár +Name[id]=Rumah +Name[is]=Heimasvæðið þitt +Name[ja]=ホーム +Name[ko]=홈 +Name[lo]=ພື້ນທີ່ສ່ວນຕົວ +Name[lt]=Pradžia +Name[lv]=Mājas +Name[mn]=Хувийн лавлах +Name[mt]=Direttorju Personali +Name[nb]=Hjem +Name[nl]=Persoonlijke map +Name[nn]=Heim +Name[nso]=Gae +Name[oc]=Inici +Name[pl]=Katalog domowy +Name[pt]=Casa +Name[ro]=Acasă +Name[ru]=Домой +Name[sk]=Domov +Name[sl]=Domov +Name[sr]=Кориснички директоријум +Name[ss]=Ekhaya +Name[sv]=Hem +Name[ta]=¦¾¡¼ì¸õ +Name[th]=พื้นที่ส่วนตัว +Name[tr]=Başlangıç +Name[uk]=Домівка +Name[ven]=Haya +Name[wa]=Måjhon +Name[xh]=Ikhaya +Name[xx]=xxHomexx +Name[zh_CN]=起点 +Name[zh_TW]=家目錄 +Name[zu]=Ikhaya + +GenericName=Personal Files +GenericName[af]=Persoonlike Lêers +GenericName[az]=Şəxsi Fayllar +GenericName[be]=Пэрсанальныя файлы +GenericName[bg]=Лични файлове +GenericName[br]=Restroù deoc'h +GenericName[bs]=Osobne datoteke +GenericName[ca]=Fitxers personals +GenericName[cs]=Osobní soubory +GenericName[cy]=Ffeiliau Personol +GenericName[da]=Personlige filer +GenericName[de]=Eigene Dateien +GenericName[el]=Προσωπικά Αρχεία +GenericName[eo]=Personaj dosieroj +GenericName[es]=Archivos personales +GenericName[et]=Isiklikud failid +GenericName[eu]=Fitxategi Pertsonalak +GenericName[fa]=پرونده‌های شخصی +GenericName[fi]=Omat tiedostot +GenericName[fr]=Fichiers personnels +GenericName[gl]=Ficheiros Persoais +GenericName[he]=קבצים אישיים +GenericName[hr]=Osobne datoteke +GenericName[hu]=személyes fájlok +GenericName[id]=File Pribadi +GenericName[is]=Skrárnar þínar +GenericName[it]=File personali +GenericName[ja]=個人のファイル +GenericName[ko]=혼자만 쓰는 파일 +GenericName[lo]=ທີ່ເກັບແຟ້ມແລະເອກະສານສວ່ນຕົວຫລືອື່ນຯ +GenericName[lt]=Asmeninės Bylos +GenericName[lv]=Personālie Faili +GenericName[mn]=Өөрийн файлууд +GenericName[mt]=Fajls Personali +GenericName[nb]=Personlige filer +GenericName[nl]=persoonlijke bestanden +GenericName[nn]=Personlege filer +GenericName[nso]=Difaele tsa Botho +GenericName[oc]=FiquièRs personals +GenericName[pl]=Pliki osobiste +GenericName[pt]=Ficheiros Pessoais +GenericName[pt_BR]=Arquivos Pessoais +GenericName[ro]=Fişiere personale +GenericName[ru]=Личные файлы +GenericName[sk]=Osobné súbory +GenericName[sl]=Osebne datoteke +GenericName[sr]=Овај директоријум садржи ваше личне фајлове +GenericName[sv]=Personliga filer +GenericName[ta]=¦º¡ó¾ì §¸¡ôÒì¸û +GenericName[th]=ที่เก็บแฟ้มและเอกสารส่วนตัว หรืออื่น ๆ +GenericName[tr]=Kişisel Dosyalar +GenericName[uk]=Особисті файли +GenericName[ven]=Dzifaela dza vhune +GenericName[vi]=File cá nhân +GenericName[wa]=Fitchîs da vosse +GenericName[xh]=Iifayile Zobuqu +GenericName[xx]=xxPersonal Filesxx +GenericName[zh_CN]=个人文件 +GenericName[zh_TW]=個人檔案 +GenericName[zu]=Amafayela Omuntu siqu + diff --git a/ede-panel/applets/start-menu/tests/data/KEdit.desktop b/ede-panel/applets/start-menu/tests/data/KEdit.desktop new file mode 100644 index 0000000..4274a2b --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/KEdit.desktop @@ -0,0 +1,34 @@ +[Desktop Entry] +Encoding=UTF-8 +BinaryPattern=kedit; +MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++; +GenericName=Simple Text Editor +GenericName[ca]=Editor de text +GenericName[cs]=Jednoduchý textový editor +GenericName[da]=Simpel teksteditor +GenericName[es]=Editor de texto sencillo +GenericName[fr]=Éditeur de texte élémentaire +GenericName[pt_BR]=Editor de Texto Simples +GenericName[sv]=Enkel texteditor +GenericName[wa]=Simpe aspougneu di tecse +Exec=kedit -caption "%c" %i %m %u +Icon=kedit +TerminalOptions= +Path= +DocPath=kedit/index.html +Type=Application +Terminal=0 +Name=KEdit +Name[af]=Kredigeer +Name[eo]=Redaktilo +Name[hr]=Uređivač +Name[lv]=KRediģēt +Name[pl]=Edytor +Name[sv]=Kedit +Name[th]=แก้ไขข้อความ +Name[ven]=U sengulusa ha K +Name[xh]=Abahleli Be K +Name[zh_TW]=KDE 編輯器 +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;TextEditor diff --git a/ede-panel/applets/start-menu/tests/data/Kfind.desktop b/ede-panel/applets/start-menu/tests/data/Kfind.desktop new file mode 100644 index 0000000..8984f18 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/Kfind.desktop @@ -0,0 +1,66 @@ +[Desktop Entry] +Encoding=UTF-8 +Exec=kfind %f +Icon=kfind +DocPath=kfind/index.html +TerminalOptions= +Path= +Type=Application +Terminal=0 +Name=Find Files +Name[af]=Soek Lêers +Name[be]=Шукаць файлы +Name[bg]=Търсене на файлове +Name[br]=Klask restroù +Name[bs]=Pronađi datoteke +Name[ca]=Cerca fitxers +Name[cs]=Najít soubory +Name[cy]=Canfod Ffeiliau +Name[da]=Find filer +Name[de]=Dateien suchen +Name[el]=Εύρεση αρχείων +Name[eo]=Trovu dosierojn +Name[es]=KFind +Name[et]=Failide otsimine +Name[eu]=Fitxategiak Bilatu +Name[fa]=یافتن پرونده‌ها +Name[fi]=Etsi tiedostoja +Name[fr]=Recherche de fichiers +Name[gl]=Buscar Ficheiros +Name[he]=חפש קבצים +Name[hr]=Nađi datoteke +Name[hu]=Fájlkeresés +Name[id]=Cari Berkas +Name[it]=Trova file +Name[ja]=ファイルを検索 +Name[ko]=파일 찾기 +Name[lo]=ຄົ້ນຫາແຟ້ມ +Name[lt]=Rasti bylas +Name[lv]=Meklēt Failus +Name[mn]=Файл хайх +Name[mt]=Sib Fajls +Name[nb]=Finn filer +Name[nl]=Bestanden zoeken +Name[nn]=Finn filer +Name[nso]=Hwetsa Difaele +Name[oc]=Cerca fiquièrs +Name[pl]=Znajdź pliki +Name[pt]=Procurar Ficheiros +Name[pt_BR]=Encontrar arquivos +Name[ro]=Caută fişiere +Name[ru]=Поиск файлов +Name[sk]=Hľadať súbory +Name[sl]=Poišči datoteke +Name[sr]=Pretraga fajlova +Name[sv]=Hitta filer +Name[ta]=§¸¡ôÒì ¸ñÎÀ¢Ê +Name[th]=ค้นหาแฟ้ม +Name[tr]=Dosyalarda Bul +Name[uk]=Знайти файли +Name[ven]=Todani faela +Name[vi]=Tìm file +Name[xh]=Fumana Iifayile +Name[zh_CN]=查找文件 +Name[zh_TW]=尋找檔案 +Name[zu]=Thola Amafayela +X-KDE-StartupNotify=true diff --git a/ede-panel/applets/start-menu/tests/data/apps.directory b/ede-panel/applets/start-menu/tests/data/apps.directory new file mode 100644 index 0000000..d54b4bd --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/apps.directory @@ -0,0 +1,66 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Apps +Name[af]=Programme +Name[ar]=تطبيقات +Name[az]=Proqram Tə'minatları +Name[be]=Дастасаваньні +Name[bg]=Приложения +Name[br]=Arloadoù +Name[bs]=Aplikacije +Name[ca]=Aplicacions +Name[cs]=Aplikace +Name[da]=Øvrige programmer +Name[de]=Programme +Name[el]=Εφαρμογές +Name[eo]=Aplikaĵoj +Name[es]=Aplicaciones +Name[et]=Rakendused +Name[eu]=Aplikazioak +Name[fa]=برنامه‌های کاربردی +Name[fi]=Sovellukset +Name[fo]=Nýtsluskipanir +Name[gl]=Aplicacións +Name[he]=יישומים +Name[hr]=Programi +Name[hu]=Alkalmazások +Name[id]=Aplikasi +Name[is]=Forrit +Name[it]=Applicazioni +Name[ja]=アプリケーション +Name[ko]=응용 프로그램 +Name[lo]=ອັບພລິກເຄເຊິນ +Name[lt]=Programos +Name[lv]=Aplikācijas +Name[mk]=Апликации +Name[mt]=Applikazzjonijiet +Name[nb]=Programmer +Name[nl]=Programma's +Name[nn]=Program +Name[nso]=Ditshomiso +Name[oc]=Aplicacions +Name[pl]=Aplikacje +Name[pt]=Aplicações +Name[pt_BR]=Aplicativos +Name[ro]=Aplicaţii +Name[ru]=Приложения +Name[se]=Prográmmat +Name[sk]=Aplikácie +Name[sl]=Uporabniški programi +Name[sr]=Aplikacije +Name[ss]=Ticelo +Name[sv]=Program +Name[ta]=ÀÂýÀ¡Î¸û +Name[th]=แอพพลิเคชัน +Name[tr]=Uygulamalar +Name[uk]=Програми +Name[ven]=Apulikhesheni +Name[vi]=Chương trình +Name[wa]=Programes +Name[xh]=Izicelo +Name[zh_CN]=应用程序 +Name[zh_TW]=應用程式 +Name[zu]=Abayaleli +Type=Directory +Icon=package_applications + diff --git a/ede-panel/applets/start-menu/tests/data/freecell.desktop b/ede-panel/applets/start-menu/tests/data/freecell.desktop new file mode 100644 index 0000000..da6d251 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/freecell.desktop @@ -0,0 +1,83 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=FreeCell +Name[be]=Вольная ячэя +Name[bg]=Свободна Клетка +Name[bn]=ফ্রীসেল +Name[ca]=FreeCell +Name[cs]=FreeCell +Name[da]=Napoleon +Name[de]=FreeCell +Name[el]=FreeCell +Name[es]=FreeCell +Name[et]=Freecell +Name[fi]=Vapaakenttä +Name[fr]=Freecell +Name[ga]=FreeCell +Name[gl]=FreeCell +Name[he]=פריסל +Name[hu]=FreeCell +Name[it]=FreeCell +Name[ja]=フリーセル +Name[ko]=프리셀 +Name[lt]=FreeCell +Name[lv]=FreeCell +Name[mn]=Фриселл +Name[ms]=FreeCell +Name[nl]=FreeCell +Name[no]=Freecell +Name[pl]=FreeCell +Name[pt]=Freecell +Name[pt_BR]=FreeCell +Name[ru]=Фриселл +Name[sk]=FreeCell +Name[sl]=FreeCell +Name[sv]=Napoleon på S:t Helena +Name[tr]=İskambil falı +Name[uk]=Вільна комірка +Name[vi]=FreeCell +Name[wa]=Freecell +Name[zh_CN]=空当接龙 +Name[zh_TW]=Freecell +Comment=FreeCell game +Comment[be]=Гульня ў Вольную ячэю +Comment[bg]=игра Свободна Клетка +Comment[bn]=ফ্রীসেল খেলা +Comment[ca]=Joc del FreeCell +Comment[cs]=Hra FreeCell +Comment[da]=Kortspillet Napoleon +Comment[de]=FreeCell-Spiel +Comment[el]=Το παιχνίδι FreeCell +Comment[es]=Juego FreeCell +Comment[et]=Kaardimäng FreeCell +Comment[fi]=Vapaakenttä-peli +Comment[fr]=Jeu de cartes Freecell +Comment[he]=משחק פריסל +Comment[hu]=FreeCell játék +Comment[it]=FreeCell +Comment[ja]=フリーセルの GNOME 版 +Comment[lv]=FreeCell spēle +Comment[mn]=GNOME Фриселл тоглоом +Comment[ms]=Permainan FreeCell +Comment[nl]=FreeCell Spel +Comment[no]=Spillet FreeCell +Comment[pl]=Gra FreeCell +Comment[pt]=Jogo FreeCell +Comment[pt_BR]=Jogo de FreeCell +Comment[ru]=Пасьянс "Ячейка" +Comment[sk]=Hra FreeCell +Comment[sl]=Igra Freecell +Comment[sv]=Spelet Napoleon på S:t Helena +Comment[tr]=İskambil falı oyunu +Comment[uk]=Гра "Вільна комірка" +Comment[vi]=Trò chơi FreeCell +Comment[zh_TW]=Freecell 紙牌接龍遊戲 +Exec=freecell +Icon=gnome-cardgame.png +Terminal=false +Type=Application +Categories=GNOME;Application;Game;CardGame; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-games +X-GNOME-Bugzilla-Component=freecell +StartupNotify=true diff --git a/ede-panel/applets/start-menu/tests/data/gataxx.desktop b/ede-panel/applets/start-menu/tests/data/gataxx.desktop new file mode 100644 index 0000000..10dfec6 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/gataxx.desktop @@ -0,0 +1,81 @@ +[Desktop Entry] +Encoding=UTF-8 +Icon=gataxx.png +Name=Gataxx +Name[am]=Gataxx +Name[be]=Gataxx +Name[bg]=Гатаксс +Name[bn]=জীঅ্যাটাক্স +Name[ca]=Gataxx +Name[cs]=Gataxx +Name[da]=Ataxx +Name[de]=Gataxx +Name[el]=Gataxx +Name[es]=Gataxx +Name[et]=Gataxx +Name[fi]=Gataxx +Name[fr]=Gataxx +Name[he]=Gataxx +Name[hu]=Gataxx +Name[it]=Gataxx +Name[ja]=Gataxx +Name[ko]=Gataxx +Name[lv]=Gataxx +Name[mn]=Gataxx +Name[ms]=Gataxx +Name[nl]=Gataxx +Name[no]=Gataxx +Name[pl]=Gataxx +Name[pt]=Gataxx +Name[pt_BR]=Gataxx +Name[ru]=Gataxx +Name[sk]=Gataxx +Name[sl]=Gataxx +Name[sv]=Gataxx +Name[tr]=Gataxx +Name[uk]=Атаки +Name[vi]=Gataxx +Name[wa]=Gataxx +Name[zh_CN]=Gataxx +Name[zh_TW]=Gataxx +Comment=Ataxx game +Comment[be]=Гульня Ataxx +Comment[bg]=Игра атаксс +Comment[bn]=অ্যাটাক্স খেলা +Comment[ca]=Joc del Ataxx +Comment[cs]=Hra Ataxx +Comment[da]=Spillet Ataxx +Comment[de]=Ataxx-Spiel +Comment[el]=Παιχνίδι Ataxx +Comment[es]=Un juego tipo Ataxx. +Comment[et]=Mäng nimega ataxx +Comment[fi]=Ataxx-peli +Comment[fr]=Jeu Ataxx +Comment[he]=משחק Ataxx +Comment[hu]=Ataxx játék +Comment[it]=Gioco Ataxx +Comment[ja]=Ataxx ゲーム +Comment[lv]=Ataxx spēle +Comment[mn]=Ataxx тоглоом. +Comment[ms]=Permainan Ataxx +Comment[nl]=Ataxx +Comment[no]=Ataxx-spill +Comment[pl]=Gra Ataxx +Comment[pt]=Jogo Ataxx +Comment[pt_BR]=Jogo Ataxx +Comment[ru]=Игра ataxx +Comment[sk]=Hra Ataxx +Comment[sl]=Igra Ataxx +Comment[sv]=Spelet Ataxx +Comment[tr]=Ataxx oyunu +Comment[uk]=Гра "Атаки" +Comment[vi]=Trò chơi Ataxx. +Comment[zh_TW]=Ataxx 遊戲 +Exec=gataxx +Terminal=false +Type=Application +Categories=GNOME;Application;Game;BoardGame; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-games +X-GNOME-Bugzilla-Component=gataxx +StartupNotify=true diff --git a/ede-panel/applets/start-menu/tests/data/gideon-legacy.desktop b/ede-panel/applets/start-menu/tests/data/gideon-legacy.desktop new file mode 100644 index 0000000..4595087 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/gideon-legacy.desktop @@ -0,0 +1,12 @@ +[KDE Desktop Entry] +Encoding=UTF-8 +BinaryPattern=kdevelop; +Type=Application +Exec=gideon %u +MimeType=application/x-kdevelop; +Icon=gideon +DocPath=kdevelop/index.html +Terminal=0 +Name=KDevelop 3.0 +Comment=IDE for C++/Qt/KDE +X-DCOP-ServiceType=Multi diff --git a/ede-panel/applets/start-menu/tests/data/gideon.desktop b/ede-panel/applets/start-menu/tests/data/gideon.desktop new file mode 100644 index 0000000..1e11dc1 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/gideon.desktop @@ -0,0 +1,13 @@ +[Desktop Entry] +Encoding=UTF-8 +BinaryPattern=kdevelop; +Type=Application +Exec=gideon %u +MimeType=application/x-kdevelop; +Icon=gideon +DocPath=kdevelop/index.html +Terminal=0 +Name=KDevelop 3.0 +Comment=IDE for C++/Qt/KDE +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;Development diff --git a/ede-panel/applets/start-menu/tests/data/glines-2.desktop b/ede-panel/applets/start-menu/tests/data/glines-2.desktop new file mode 100644 index 0000000..266747d --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/glines-2.desktop @@ -0,0 +1,83 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Glines +Name[be]=Glines +Name[bg]=ГЛинии +Name[bn]=জীলাইন্‌স +Name[ca]=Glines +Name[cs]=Glines +Name[da]=Linjer +Name[de]=Glines +Name[el]=Glines +Name[es]=Glines +Name[et]=Glines +Name[eu]=Glines +Name[fi]=Glines +Name[fr]=Glines +Name[he]=Glines +Name[hu]=Glines +Name[it]=Glines +Name[ja]=Glines +Name[ko]=Glines +Name[lt]=Glines +Name[lv]=Glines +Name[mn]=Glines +Name[ms]=Glines +Name[nl]=GLines +Name[no]=Glines +Name[pl]=Glines +Name[pt]=GLinhas +Name[pt_BR]=Glines +Name[ro]=Glines +Name[ru]=Glines +Name[sk]=GČiary +Name[sl]=Glines +Name[sv]=Glinjer +Name[tr]=Sıradaki toplar +Name[uk]=Ряди +Name[vi]=Glines +Name[wa]=GRoyes +Name[zh_CN]=Glines +Name[zh_TW]=Glines +Comment=Color lines game +Comment[be]=Гульня "Каляровыя лініі" +Comment[bg]=Игра Цветни линии +Comment[bn]=কালার লাইন্‌স খেলা +Comment[ca]=Joc de línies de color +Comment[cs]=Hra Barevné linie +Comment[da]=Farvede linjer-spil +Comment[de]=Farblinienspiel +Comment[el]=Παιχνίδι γραμμών χρώματος +Comment[es]=Juego del tipo líneas de colores. +Comment[fi]=Väriviivapeli +Comment[fr]=Jeu de lignes colorées +Comment[he]=משחק שורות צבעוניות +Comment[hu]=Color lines játék +Comment[it]=Gioco Color Lines +Comment[ja]=カラーラインのゲーム +Comment[lv]=Krāsu līniju spēle +Comment[mn]=Өнгөт бөмбөлөг тоглоом +Comment[ms]=Permainan garisan warna +Comment[nl]=Kleurlijnen +Comment[no]=Spill med fargelinjer +Comment[pl]=Gra w kolorowe linie +Comment[pt]=Jogo das linhas coloridas +Comment[pt_BR]=Jogo de linhas coloridas +Comment[ru]=Игра "Цветные линии" +Comment[sk]=Hra Farebné čiary +Comment[sl]=Igra barvnih črt +Comment[sv]=Färglinjespel +Comment[tr]=Color lines oyunu +Comment[uk]=Гра "Кольорові ряди" +Comment[vi]=Trò chơi ColorLines +Comment[zh_TW]=Color lines 遊戲 +Exec=glines +Icon=glines.png +Terminal=false +Type=Application +Categories=GNOME;Application;Game;PuzzleGame; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-games +X-GNOME-Bugzilla-Component=glines +StartupNotify=true +NoDisplay=true diff --git a/ede-panel/applets/start-menu/tests/data/glines.desktop b/ede-panel/applets/start-menu/tests/data/glines.desktop new file mode 100644 index 0000000..5e5b465 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/glines.desktop @@ -0,0 +1,82 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Glines +Name[be]=Glines +Name[bg]=ГЛинии +Name[bn]=জীলাইন্‌স +Name[ca]=Glines +Name[cs]=Glines +Name[da]=Linjer +Name[de]=Glines +Name[el]=Glines +Name[es]=Glines +Name[et]=Glines +Name[eu]=Glines +Name[fi]=Glines +Name[fr]=Glines +Name[he]=Glines +Name[hu]=Glines +Name[it]=Glines +Name[ja]=Glines +Name[ko]=Glines +Name[lt]=Glines +Name[lv]=Glines +Name[mn]=Glines +Name[ms]=Glines +Name[nl]=GLines +Name[no]=Glines +Name[pl]=Glines +Name[pt]=GLinhas +Name[pt_BR]=Glines +Name[ro]=Glines +Name[ru]=Glines +Name[sk]=GČiary +Name[sl]=Glines +Name[sv]=Glinjer +Name[tr]=Sıradaki toplar +Name[uk]=Ряди +Name[vi]=Glines +Name[wa]=GRoyes +Name[zh_CN]=Glines +Name[zh_TW]=Glines +Comment=Color lines game +Comment[be]=Гульня "Каляровыя лініі" +Comment[bg]=Игра Цветни линии +Comment[bn]=কালার লাইন্‌স খেলা +Comment[ca]=Joc de línies de color +Comment[cs]=Hra Barevné linie +Comment[da]=Farvede linjer-spil +Comment[de]=Farblinienspiel +Comment[el]=Παιχνίδι γραμμών χρώματος +Comment[es]=Juego del tipo líneas de colores. +Comment[fi]=Väriviivapeli +Comment[fr]=Jeu de lignes colorées +Comment[he]=משחק שורות צבעוניות +Comment[hu]=Color lines játék +Comment[it]=Gioco Color Lines +Comment[ja]=カラーラインのゲーム +Comment[lv]=Krāsu līniju spēle +Comment[mn]=Өнгөт бөмбөлөг тоглоом +Comment[ms]=Permainan garisan warna +Comment[nl]=Kleurlijnen +Comment[no]=Spill med fargelinjer +Comment[pl]=Gra w kolorowe linie +Comment[pt]=Jogo das linhas coloridas +Comment[pt_BR]=Jogo de linhas coloridas +Comment[ru]=Игра "Цветные линии" +Comment[sk]=Hra Farebné čiary +Comment[sl]=Igra barvnih črt +Comment[sv]=Färglinjespel +Comment[tr]=Color lines oyunu +Comment[uk]=Гра "Кольорові ряди" +Comment[vi]=Trò chơi ColorLines +Comment[zh_TW]=Color lines 遊戲 +Exec=glines +Icon=glines.png +Terminal=false +Type=Application +Categories=GNOME;Application;Game;PuzzleGame; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-games +X-GNOME-Bugzilla-Component=glines +StartupNotify=true diff --git a/ede-panel/applets/start-menu/tests/data/hidden.desktop b/ede-panel/applets/start-menu/tests/data/hidden.desktop new file mode 100644 index 0000000..dbbbff9 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/hidden.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Hidden +Comment=You shouldn't see this +Exec=hidden +Type=Application +Categories=GNOME;Application;TextEditor; +NoDisplay=true diff --git a/ede-panel/applets/start-menu/tests/data/hidden.directory b/ede-panel/applets/start-menu/tests/data/hidden.directory new file mode 100644 index 0000000..5f1e776 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/hidden.directory @@ -0,0 +1,5 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Hidden +Type=Directory +NoDisplay=true diff --git a/ede-panel/applets/start-menu/tests/data/kate.desktop b/ede-panel/applets/start-menu/tests/data/kate.desktop new file mode 100644 index 0000000..3ca1c5d --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/kate.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Encoding=UTF-8 +GenericName=Advanced Text Editor +GenericName[cy]=Golygydd Testun Uwch +GenericName[da]= Avanceret teksteditor +GenericName[el]=Προχωρημένος διορθωτής κειμένου +GenericName[es]=Editor de texto avanzado +GenericName[fa]=ویرایشگر متن پیشرفته +GenericName[hu]=szövegszerkesztő +GenericName[pt_BR]=Editor de Texto Avançado +GenericName[sr]=Napredni editor teksta +GenericName[sv]=Avancerad texteditor +BinaryPattern= +Name=Kate +Name[ar]=كيت +Name[bg]=Редактор Kate +Name[eo]=Kodredaktilo +Name[fa]=کِیت +Name[ko]=카테 +Name[ru]=Редактор Kate +MimeType=text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;text/rdf; +Exec=kate %u +TerminalOptions= +Icon=kate +Path= +DocPath=kate/index.html +Type=Application +Terminal=0 +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;TextEditor diff --git a/ede-panel/applets/start-menu/tests/data/kbabel.desktop b/ede-panel/applets/start-menu/tests/data/kbabel.desktop new file mode 100644 index 0000000..2892cb8 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/kbabel.desktop @@ -0,0 +1,69 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=KBabel +Name[af]=Kbabel +Name[ar]=المترجم +Name[eo]=Babelo-tradukilo +Name[ko]=K바벨 +Name[pt_BR]=Editor de POTFiles +Name[sv]=Kbabel +Name[xx]=xxKBabelxx +Exec=kbabel %i %m -caption "%c" %U +Icon=kbabel +MiniIcon=kbabel +Type=Application +DocPath=kbabel/index.html +MimeType=application/x-gettext; +GenericName=Translation Tool +GenericName[af]=Vertaling Program +GenericName[ar]=أداة الترجمة +GenericName[bg]=Инструменти за Превод +GenericName[bs]=Alat za prevođenje +GenericName[ca]=Eina de traducció +GenericName[cs]=Překladatelský nástroj +GenericName[cy]=Erfyn Cyfieithu +GenericName[da]=Oversættelsesværktøj +GenericName[de]=Übersetzungsprogramm +GenericName[el]=Εργαλείο μετάφρασης +GenericName[eo]=Tradukilo por Qt-programoj +GenericName[es]=Herramienta de traducción +GenericName[et]=Tõlkimise rakendus +GenericName[eu]=Itzulpenerako Tresnak +GenericName[fi]=Käännöstyökalu +GenericName[fo]=Umsetingaramboð +GenericName[fr]=Outil de traduction +GenericName[he]=כלי תרגום +GenericName[hr]=Uslužni program za prevođenje +GenericName[hu]=segédprogram fordítóknak +GenericName[it]=Strumento per le traduzioni +GenericName[ja]=翻訳ツール +GenericName[lt]=Vertimo įrankis +GenericName[lv]=Tulkošanas Rīks +GenericName[mt]=Għodda tat-traduzzjoni +GenericName[nb]=Oversettingsverktøy +GenericName[nl]=vertaalprogramma +GenericName[nn]=Omsetjingsverktøy +GenericName[nso]=Sebereka sa Thlathollo +GenericName[pl]=Narzędzie dla tłumaczy +GenericName[pt]=Ferramenta de Tradução +GenericName[pt_BR]=Ferramenta de Tradução +GenericName[ro]=Utilitar de traducere +GenericName[ru]=Утилита локализации приложений +GenericName[sk]=Prekladací nástroj +GenericName[sl]=Orodje za prevajanje +GenericName[sv]=Översättningsverktyg +GenericName[ta]=¦Á¡Æ¢¦ÀÂ÷ôÒì ¸ÕÅ¢ +GenericName[th]=เครื่องมือแปลภาษา +GenericName[tr]=Çeviri Aracı +GenericName[uk]=Засіб для перекладів +GenericName[ven]=Zwishumiswa zwau Dologa +GenericName[vi]=Công cụ dịch +GenericName[xh]=Isixhobo Soguqulelo lomsebenzi kolunye ulwimi +GenericName[xx]=xxTranslation Toolxx +GenericName[zh_CN]=翻译工具 +GenericName[zh_TW]=翻譯工具 +GenericName[zu]=Ithuluzi Lokuguqulela +Terminal=0 +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Unique +Categories=Qt;KDE;Development diff --git a/ede-panel/applets/start-menu/tests/data/kedit-legacy.desktop b/ede-panel/applets/start-menu/tests/data/kedit-legacy.desktop new file mode 100644 index 0000000..fa9700e --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/kedit-legacy.desktop @@ -0,0 +1,33 @@ +[Desktop Entry] +Encoding=UTF-8 +BinaryPattern=kedit; +MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++; +GenericName=Simple Text Editor +GenericName[ca]=Editor de text +GenericName[cs]=Jednoduchý textový editor +GenericName[da]=Simpel teksteditor +GenericName[es]=Editor de texto sencillo +GenericName[fr]=Éditeur de texte élémentaire +GenericName[pt_BR]=Editor de Texto Simples +GenericName[sv]=Enkel texteditor +GenericName[wa]=Simpe aspougneu di tecse +Exec=kedit -caption "%c" %i %m %u +Icon=kedit +TerminalOptions= +Path= +DocPath=kedit/index.html +Type=Application +Terminal=0 +Name=KEdit +Name[af]=Kredigeer +Name[eo]=Redaktilo +Name[hr]=Uređivač +Name[lv]=KRediģēt +Name[pl]=Edytor +Name[sv]=Kedit +Name[th]=แก้ไขข้อความ +Name[ven]=U sengulusa ha K +Name[xh]=Abahleli Be K +Name[zh_TW]=KDE 編輯器 +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi diff --git a/ede-panel/applets/start-menu/tests/data/kwrite.desktop b/ede-panel/applets/start-menu/tests/data/kwrite.desktop new file mode 100644 index 0000000..9804fa6 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/kwrite.desktop @@ -0,0 +1,84 @@ +[Desktop Entry] +Encoding=UTF-8 +GenericName=Text Editor +GenericName[af]=Teks Redigeerder +GenericName[ar]=محرر نصوص +GenericName[be]=Тэкставы рэдактар +GenericName[bg]=Текстов Редактор +GenericName[bs]=Tekst editor +GenericName[ca]=Editor de text +GenericName[cs]=Textový editor +GenericName[cy]=Golygydd Testun +GenericName[da]= Teksteditor +GenericName[de]=Texteditor +GenericName[el]=Διορθωτής Κειμένου +GenericName[eo]=Tekstredaktilo +GenericName[es]=Editor de texto +GenericName[et]=Tekstiredaktor +GenericName[eu]=Testu Editorea +GenericName[fa]=ویرایشگر متن +GenericName[fi]=Tekstieditori +GenericName[fo]=Tekstritil +GenericName[fr]=Éditeur de texte +GenericName[he]=עורך טקסט +GenericName[hr]=Uređivač teksta +GenericName[hu]=szövegszerkesztő +GenericName[is]=Textaritill +GenericName[it]=Editor di testi +GenericName[ja]=テキストエディタ +GenericName[ko]=글월 편집기 +GenericName[lo]=ເຄື່ອງມືແກ້ໄຂຂໍ້ຄວາມ +GenericName[lt]=Teksto redaktorius +GenericName[lv]=Teksta Redaktors +GenericName[mn]=Текст боловсруулагч +GenericName[mt]=Editur tat-test +GenericName[nb]=Skriveprogram +GenericName[nl]=teksteditor +GenericName[nn]=Skriveprogram +GenericName[nso]=Mofetosi wa Sengwalwana +GenericName[pl]=Edytor tekstowy +GenericName[pt]=Editor de Texto +GenericName[pt_BR]=Editor de Texto +GenericName[ro]=Editor de text +GenericName[ru]=Текстовый редактор +GenericName[sk]=Textový editor +GenericName[sl]=Urejevalnik besedil +GenericName[sr]=Editor teksta +GenericName[ss]=Sihleli sembhalo +GenericName[sv]=Texteditor +GenericName[ta]=¯¨Ã ¦¾¡ÌôÀ¡Ç÷ +GenericName[th]=เครื่องมือแก้ไขข้อความ +GenericName[tr]=Metin Düzenleyici +GenericName[uk]=Редактор текстів +GenericName[ven]=Musengulusi wa Manwalwa +GenericName[vi]=Trình soạn văn bản +GenericName[wa]=Aspougneu di tecse +GenericName[xh]=Umhleli Wombhalo +GenericName[zh_CN]=文本编辑器 +GenericName[zh_TW]=文字編輯器 +GenericName[zu]=Umlungisi wombhalo +BinaryPattern= +Name=KWrite +Name[af]=Kskryf +Name[ar]=كاتب كيدي +Name[eo]=Simpla kodredaktilo +Name[fa]=نوشتار K +Name[fo]=KSkriva +Name[lo]=Kwrite +Name[lv]=KRakstīt +Name[nso]=KNgwala +Name[ru]=Редактор KWrite +Name[sv]=Kwrite +Name[ven]=Nwala ha K +MimeType=text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;text/x-diff;text/rdf; +Exec=kwrite %u +X-KDE-StartupNotify=true +TerminalOptions= +Icon=kwrite +Path= +DocPath=kwrite/index.html +Type=Application +Terminal=0 +InitialPreference=8 +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;TextEditor diff --git a/ede-panel/applets/start-menu/tests/data/mahjongg-2.desktop b/ede-panel/applets/start-menu/tests/data/mahjongg-2.desktop new file mode 100644 index 0000000..95cfe27 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/mahjongg-2.desktop @@ -0,0 +1,86 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Mahjongg +Name[am]=ማህጆንግ +Name[be]=Маджонг +Name[bg]=Махджонг +Name[bn]=মাহজং +Name[ca]=Mahjongg +Name[cs]=Mahjongg +Name[da]=Mahjongg +Name[de]=Mahjongg +Name[el]=Mahjongg +Name[es]=Mahjongg +Name[et]=Mahjongg +Name[fa]=ماهجونگ +Name[fi]=Mahjongg +Name[fr]=Mahjongg +Name[gl]=Mahjongg +Name[he]=Mahjongg +Name[hu]=Mahjongg +Name[it]=Mahjongg +Name[ja]=GNOME 上海 +Name[ko]=그놈 마작 +Name[lt]=Mahjongg +Name[lv]=Mahjongg +Name[mn]=Маджонг +Name[ms]=Mahjongg +Name[nl]=Mahjongg +Name[no]=Mahjongg +Name[pl]=Mahjongg +Name[pt]=Mahjongg +Name[pt_BR]=Mahjongg +Name[ru]=Маджонг +Name[sk]=Mahjongg +Name[sl]=Mahjongg +Name[sv]=Mah Jong +Name[tr]=Mahjongg +Name[uk]=Магджонґ +Name[vi]=Mahjongg +Name[wa]=Mahjongg +Name[zh_CN]=堆麻将 +Name[zh_TW]=上海麻將 +Comment=Mahjongg game +Comment[am]=የማህጆንግ ጨዋታ +Comment[be]=Гульня Маджонг +Comment[bg]=Игра на Махджонг +Comment[bn]=মাহজং খেলা +Comment[ca]=Joc del Mahjongg +Comment[cs]=Hra Mahjongg +Comment[da]=Mahjonggspil +Comment[de]=Mahjongg-Spiel +Comment[el]=Παιχνίδι Mahjongg +Comment[es]=Juego Mahjongg +Comment[et]=Mäng nimega mahjongg +Comment[fa]=بازی ماهجونگ +Comment[fi]=Mahjongg-peli +Comment[fr]=Jeu de Mahjongg +Comment[he]=משחק Mahjongg +Comment[hu]=Mahjongg játék +Comment[it]=Gioco Mahjongg +Comment[ja]=麻雀ゲーム +Comment[lv]=Mahjongg spēle +Comment[mn]=Mahjongg тоглоом +Comment[ms]=Permainan Mahjongg +Comment[nl]=Mahjongg +Comment[no]=Mahjongg-spill +Comment[pl]=Gra Mahjongg +Comment[pt]=Jogo Mahjongg +Comment[pt_BR]=Jogo Mahjongg +Comment[ru]=Игра Маджонг +Comment[sk]=Hra Mahjongg +Comment[sl]=Igra Mahjongg +Comment[sv]=Mah Jong-spel +Comment[tr]=Mahjongg oyunu +Comment[uk]=Гра "Магджонґ" +Comment[vi]=Trò chơi Mahjongg +Comment[zh_TW]=上海麻將遊戲 +Exec=mahjongg +Icon=gnome-mahjongg.png +Terminal=false +Type=Application +Categories=GNOME;Application;Development; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-games +X-GNOME-Bugzilla-Component=mahjongg +StartupNotify=true diff --git a/ede-panel/applets/start-menu/tests/data/mahjongg.desktop b/ede-panel/applets/start-menu/tests/data/mahjongg.desktop new file mode 100644 index 0000000..0d5f5de --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/mahjongg.desktop @@ -0,0 +1,86 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Mahjongg +Name[am]=ማህጆንግ +Name[be]=Маджонг +Name[bg]=Махджонг +Name[bn]=মাহজং +Name[ca]=Mahjongg +Name[cs]=Mahjongg +Name[da]=Mahjongg +Name[de]=Mahjongg +Name[el]=Mahjongg +Name[es]=Mahjongg +Name[et]=Mahjongg +Name[fa]=ماهجونگ +Name[fi]=Mahjongg +Name[fr]=Mahjongg +Name[gl]=Mahjongg +Name[he]=Mahjongg +Name[hu]=Mahjongg +Name[it]=Mahjongg +Name[ja]=GNOME 上海 +Name[ko]=그놈 마작 +Name[lt]=Mahjongg +Name[lv]=Mahjongg +Name[mn]=Маджонг +Name[ms]=Mahjongg +Name[nl]=Mahjongg +Name[no]=Mahjongg +Name[pl]=Mahjongg +Name[pt]=Mahjongg +Name[pt_BR]=Mahjongg +Name[ru]=Маджонг +Name[sk]=Mahjongg +Name[sl]=Mahjongg +Name[sv]=Mah Jong +Name[tr]=Mahjongg +Name[uk]=Магджонґ +Name[vi]=Mahjongg +Name[wa]=Mahjongg +Name[zh_CN]=堆麻将 +Name[zh_TW]=上海麻將 +Comment=Mahjongg game +Comment[am]=የማህጆንግ ጨዋታ +Comment[be]=Гульня Маджонг +Comment[bg]=Игра на Махджонг +Comment[bn]=মাহজং খেলা +Comment[ca]=Joc del Mahjongg +Comment[cs]=Hra Mahjongg +Comment[da]=Mahjonggspil +Comment[de]=Mahjongg-Spiel +Comment[el]=Παιχνίδι Mahjongg +Comment[es]=Juego Mahjongg +Comment[et]=Mäng nimega mahjongg +Comment[fa]=بازی ماهجونگ +Comment[fi]=Mahjongg-peli +Comment[fr]=Jeu de Mahjongg +Comment[he]=משחק Mahjongg +Comment[hu]=Mahjongg játék +Comment[it]=Gioco Mahjongg +Comment[ja]=麻雀ゲーム +Comment[lv]=Mahjongg spēle +Comment[mn]=Mahjongg тоглоом +Comment[ms]=Permainan Mahjongg +Comment[nl]=Mahjongg +Comment[no]=Mahjongg-spill +Comment[pl]=Gra Mahjongg +Comment[pt]=Jogo Mahjongg +Comment[pt_BR]=Jogo Mahjongg +Comment[ru]=Игра Маджонг +Comment[sk]=Hra Mahjongg +Comment[sl]=Igra Mahjongg +Comment[sv]=Mah Jong-spel +Comment[tr]=Mahjongg oyunu +Comment[uk]=Гра "Магджонґ" +Comment[vi]=Trò chơi Mahjongg +Comment[zh_TW]=上海麻將遊戲 +Exec=mahjongg +Icon=gnome-mahjongg.png +Terminal=false +Type=Application +Categories=GNOME;Application;Game;BoardGame; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-games +X-GNOME-Bugzilla-Component=mahjongg +StartupNotify=true diff --git a/ede-panel/applets/start-menu/tests/data/quanta.desktop b/ede-panel/applets/start-menu/tests/data/quanta.desktop new file mode 100644 index 0000000..4b23aef --- /dev/null +++ b/ede-panel/applets/start-menu/tests/data/quanta.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Quanta Plus +Exec=quanta +Icon=quanta +Type=Application +MimeType=text/html +DocPath=quanta/index.html +Comment=Web Development Environment +Categories=Qt;KDE;Development diff --git a/ede-panel/applets/start-menu/tests/expand b/ede-panel/applets/start-menu/tests/expand new file mode 100755 index 0000000..5dbaac8 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/expand @@ -0,0 +1,12 @@ +#!/usr/bin/perl + +# This script performs expansion of environment variables of the form ${HOME} + +while(<>) +{ + while (($a) = ($_ =~ /[^\$]*\$\{([^\}]*)\}.*/)) + { + s/([^\$]*)(\$\{$a\})(.*)/$1$ENV{$a}$3/; + } + printf $_; +} \ No newline at end of file diff --git a/ede-panel/applets/start-menu/tests/menutest b/ede-panel/applets/start-menu/tests/menutest new file mode 100755 index 0000000..9d37246 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/menutest @@ -0,0 +1,200 @@ +#!/bin/bash +# these vars are usable *only* for debugging purposes; they're not allowed as part of the spec, thus don't use them. +# set MENU_FAKE_PREFIX to a non empty val to force testing for if things would succeed if XDG_MENU_PREFIX were +# implemented +# set MENU_FAKE_APPLICATIONS to force a work around for applications-merged postfixing. +# +function installData() +{ + local DIR="$1" + shift + mkdir -p "${DIR}" + for file in $*; do + cp "data/${file}" "${DIR}" + WIPE[$WIPE_IDX]="${DIR}/${file}" + WIPE_IDX=$(( $WIPE_IDX + 1 )) + done +} + +function installDataAs() +{ + local DIR="$1" + mkdir -p "${DIR}" + cp "data/$2" "${DIR}/$3" + WIPE[${WIPE_IDX}]="${DIR}/${3}" + WIPE_IDX=$(( $WIPE_IDX + 1 )) +} + +setup_local_xdg_vars() { + export XDG_CONFIG_HOME="${MENUTESTDIR}/xdg_config_home" + export XDG_DATA_HOME="${MENUTESTDIR}/xdg_data_home" + export XDG_CONFIG_DIR="${MENUTESTDIR}/xdg_config_dir" + export XDG_CONFIG_DIRS="$XDG_CONFIG_DIR:${XDG_CONFIG_DIRS}2" + export XDG_DATA_DIR="${MENUTESTDIR}/xdg_data_dir" + export XDG_DATA_DIRS="$XDG_DATA_DIR:${XDG_DATA_DIR}2" + export XDG_CACHE_HOME="${MENUTESTDIR}/xdg_cache_home" +} + +setup_xdg_system_data_vars() +{ + export XDG_CACHE_HOME="${XDG_DATA_HOME:-${HOME}/.cache}" + export XDG_DATA_HOME="${XDG_DATA_HOME:-${HOME}/.local/share}" + export XDG_CONFIG_DIR="${XDG_CONFIG_DIR:-/etc/xdg}" + if [ -z "${XDG_DATA_DIRS}" ]; then + export XDG_DATA_DIRS="/usr/local/share:/usr/share" + fi + export XDG_DATA_DIRS="${XDG_DATA_DIRS}:${MENUTESTDIR}/xdg_cache_dir" + export XDG_DATA_DIR="${XDG_DATA_DIRS//*:}" + if [ -z "${XDG_CONFIG_DIRS}" ]; then + export XDG_CONFIG_DIRS="/etc/xdg" + fi + export XDG_CONFIG_DIR="${XDG_CONFIG_DIRS/:*}" +} + +run_test() { + if [ -z "$1" ]; then + echo "requires name of test directory to run" + exit 1 + fi + local TEST="$1" + rm -rf "${MENUTESTDIR}" 2> /dev/null + mkdir "${MENUTESTDIR}" + RESULT="${TEST}/result" + + ( + unset WIPE WIPE_IDX + declare -a WIPE + declare -i WIPE_IDX=0 + unset MODE + + . ${TEST}/test + echo ">>> Running test ${TEST}, purpose $TEST_PURPOSE" + + if [ "${MODE:-local}" == "local" ]; then + setup_local_xdg_vars + elif [ "${MODE}" == "system_data" ]; then + setup_xdg_system_data_vars + else + echo "!!! unknown MODE from $TEST, bailing" + exit -1 + fi + + test_code + + declare -i IDX=0 + while [ $WIPE_IDX -gt $IDX ]; do + echo "${WIPE[$IDX]}" >> "${MENUTESTDIR}/wipe" + IDX=$(( $IDX + 1 )) + done + + DEBUG_OVERRIDES='' + [ -n "$MENU_FAKE_PREFIX" ] && DEBUG_OVERRIDES=.menu + [ -n "$MENU_FAKE_APPLICATIONS" ] && DEBUG_OVERIDES="${DEBUG_OVERRIDES} -merged" + + for x in dir home; do + for y in ${DEBUG_OVERRIDES}; do + if [ -e "${MENUTESTDIR}/xdg_config_${x}/menus/applications${y}" ]; then + ln -s applications${y} "${MENUTESTDIR}/xdg_config_${x}/menus/kde-applications${y}" + ln -s applications${y} "${MENUTESTDIR}/xdg_config_${x}/menus/gnome-applications${y}" + fi + done + unset y + done + unset x DEBUG_OVERRIDES + + $MENUTEST > ${MENUTESTDIR}/run-result 2> ${MENUTESTDIR}/log + + if [ -e "${RESULT}" ]; then + ./expand "${RESULT}" > "${MENUTESTDIR}/required-result" + fi + + if [ "$(type -t interpret_results)" == "function" ]; then + interpret_results + else + default_interpret_results + fi + ret=$? + if [ -e "${MENUTESTDIR}/wipe" ]; then + cat "${MENUTESTDIR}/wipe" | while read l; do + [ -z "$l" ] && continue + rm "$l" + done + fi + return $ret + ) +} + +default_interpret_results() { + if [ ! -e "${RESULT}" ]; then + echo "!!! Result file (${RESULT}) for ${TEST} missing" + echo '>>> Failed' + return 1 + elif diff -q "${MENUTESTDIR}/run-result" "${MENUTESTDIR}/required-result" > /dev/null; then + echo '>>> OK' + return 0 + fi + sort "${MENUTESTDIR}/run-result" > "${MENUTESTDIR}/run-result.sorted" + sort "${MENUTESTDIR}/required-result" > "${MENUTESTDIR}/required-result.sorted" + if diff -u "${MENUTESTDIR}/run-result.sorted" "${MENUTESTDIR}/required-result.sorted" > "${MENUTESTDIR}/result.diff"; then + echo '>>> OK (different order)' + return 0 + fi + grep "${MENUTESTDIR}" "${MENUTESTDIR}/run-result" > "${MENUTESTDIR}/run-result.filtered" 2> /dev/null + if diff -q "${MENUTESTDIR}/run-result.filtered" "${MENUTESTDIR}/required-result" > /dev/null; then + echo '>>> OK (additional system items)' + return 0 + fi + grep "${MENUTESTDIR}" "${MENUTESTDIR}/run-result.sorted" > "${MENUTESTDIR}/required-result.filtered" 2> /dev/null + if diff -u "${MENUTESTDIR}/run-result.filtered" "${MENUTESTDIR}/required-result.sorted" > "${MENUTESTDIR}/result.diff"; then + echo '>>> OK (different order, additional system items)' + return 0 + fi + echo '>>> Failed' + cat "${MENUTESTDIR}/result.diff" + cat "${MENUTESTDIR}/log" + return 1 +} + +if [ -z "${TESTS}" ]; then + export TESTS=`ls tests/*/test | sed -e 's:^\(\./\)\?tests/\+::' -e 's:/\+test$::'` +fi + +if [ -z "$TET_RUN" ]; then + + if [ "x${MENUTEST}" == "x" ]; then + echo 'To run the test set $MENUTEST to your menu-spec implementation.' + exit 1 + fi + + if [ "x${MENUTESTDIR}" == "x" ]; then + MENUTESTDIR=/tmp/menutestdir + echo Using ${MENUTESTDIR} as test directory, override with \$MENUTESTDIR. + else + echo Using ${MENUTESTDIR} as test directory. + fi + + export MENUTESTDIR + export USER=${USER:-test} + + + FAILED= + SUCCEEDED= + + for TESTCASE in ${TESTS}; do + if [ "${TESTCASE}" == "CVS" ]; then + continue + fi + echo + if ! run_test "tests/${TESTCASE}" ${MODE}; then + FAILED="${FAILED} ${TESTCASE}" + else + SUCCEEDED="${SUCCEEDED} ${TESTCASE}" + fi + done + + echo "OK tests: ${SUCCEEDED}" + [ -n "${FAILED}" ] && echo "Failed tests: ${FAILED}" + echo "$(echo ${SUCCEEDED} | wc -w) tests passed, $(echo ${FAILED} | wc -w) tests failed" + [ -z "${FAILED}" ] && exit 0 + exit 1 +fi diff --git a/ede-panel/applets/start-menu/tests/tests/All/result b/ede-panel/applets/start-menu/tests/tests/All/result new file mode 100644 index 0000000..fc75a82 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/All/result @@ -0,0 +1,4 @@ +Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop +Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop +Applications/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop +Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/All/test b/ede-panel/applets/start-menu/tests/tests/All/test new file mode 100644 index 0000000..e97bddc --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/All/test @@ -0,0 +1,26 @@ +TEST_PURPOSE=" Keyword" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/And/result b/ede-panel/applets/start-menu/tests/tests/And/result new file mode 100644 index 0000000..dc159fa --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/And/result @@ -0,0 +1 @@ +Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/And/test b/ede-panel/applets/start-menu/tests/tests/And/test new file mode 100644 index 0000000..31571db --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/And/test @@ -0,0 +1,29 @@ +TEST_PURPOSE=" Keyword" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + + freecell.desktop + Game + + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/AppDir-relative/result b/ede-panel/applets/start-menu/tests/tests/AppDir-relative/result new file mode 100644 index 0000000..9e1d622 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/AppDir-relative/result @@ -0,0 +1,3 @@ +Applications/ KEdit.desktop ${XDG_CONFIG_DIR}/menus/apps/KEdit.desktop +Applications/ kate.desktop ${XDG_CONFIG_DIR}/menus/apps/kate.desktop +Applications/ kwrite.desktop ${XDG_CONFIG_DIR}/menus/apps/kwrite.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/AppDir-relative/test b/ede-panel/applets/start-menu/tests/tests/AppDir-relative/test new file mode 100644 index 0000000..75bd201 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/AppDir-relative/test @@ -0,0 +1,27 @@ +TEST_PURPOSE=" relative path tag ..." + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + apps + + + Applications + + TextEditor + + + +EOF + + # Install .desktop files + installData "${XDG_CONFIG_DIR}/menus/apps" kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop + installDataAs "${XDG_CONFIG_DIR}/menus/apps" kwrite.desktop should_be_ignored.notdesktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/AppDir/result b/ede-panel/applets/start-menu/tests/tests/AppDir/result new file mode 100644 index 0000000..9e1d622 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/AppDir/result @@ -0,0 +1,3 @@ +Applications/ KEdit.desktop ${XDG_CONFIG_DIR}/menus/apps/KEdit.desktop +Applications/ kate.desktop ${XDG_CONFIG_DIR}/menus/apps/kate.desktop +Applications/ kwrite.desktop ${XDG_CONFIG_DIR}/menus/apps/kwrite.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/AppDir/test b/ede-panel/applets/start-menu/tests/tests/AppDir/test new file mode 100644 index 0000000..8faff91 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/AppDir/test @@ -0,0 +1,4 @@ +. tests/AppDir-relative/test +export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/' +TEST_PURPOSE=" absolute path" + diff --git a/ede-panel/applets/start-menu/tests/tests/Category/result b/ede-panel/applets/start-menu/tests/tests/Category/result new file mode 100644 index 0000000..73b00c9 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Category/result @@ -0,0 +1,3 @@ +Editors/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Editors/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Category/test b/ede-panel/applets/start-menu/tests/tests/Category/test new file mode 100644 index 0000000..3ee0777 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Category/test @@ -0,0 +1,29 @@ +TEST_PURPOSE=" tag" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Editors + + TextEditor + + application + + + +EOF + +# Install .desktop files, freecell is daft but intentional to verify category support is case sensitive +installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/DefaultMergeDirs/result b/ede-panel/applets/start-menu/tests/tests/DefaultMergeDirs/result new file mode 100644 index 0000000..9e17189 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/DefaultMergeDirs/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop +Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/DefaultMergeDirs/test b/ede-panel/applets/start-menu/tests/tests/DefaultMergeDirs/test new file mode 100644 index 0000000..3b570f2 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/DefaultMergeDirs/test @@ -0,0 +1,43 @@ +TEST_PURPOSE=" tag ..." + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + TextEditor + + + +EOF + + mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/ + ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu < + + + KDE + + + Development + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/Deleted/result b/ede-panel/applets/start-menu/tests/tests/Deleted/result new file mode 100644 index 0000000..c121419 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Deleted/result @@ -0,0 +1,2 @@ +BoardGames/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop +BoardGames/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Deleted/test b/ede-panel/applets/start-menu/tests/tests/Deleted/test new file mode 100644 index 0000000..db7a413 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Deleted/test @@ -0,0 +1,35 @@ +TEST_PURPOSE=" tag" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + BoardGames + + BoardGame + + + + Games + + Game + + + + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/DesktopFileID/result b/ede-panel/applets/start-menu/tests/tests/DesktopFileID/result new file mode 100644 index 0000000..9629384 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/DesktopFileID/result @@ -0,0 +1,4 @@ +Applications/ company-games-freecell.desktop ${XDG_DATA_DIR}/applications/company/games/freecell.desktop +Applications/ company-games-gataxx.desktop ${XDG_DATA_DIR}/applications/company/games/gataxx.desktop +Applications/ company-games-glines.desktop ${XDG_DATA_DIR}/applications/company/games/glines.desktop +Applications/ company-games-mahjongg.desktop ${XDG_DATA_DIR}/applications/company/games/mahjongg.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/DesktopFileID/test b/ede-panel/applets/start-menu/tests/tests/DesktopFileID/test new file mode 100644 index 0000000..ae2c679 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/DesktopFileID/test @@ -0,0 +1,26 @@ +TEST_PURPOSE="DesktopFileIDs in submenus" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + Game + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications/company/games gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/Directory/result b/ede-panel/applets/start-menu/tests/tests/Directory/result new file mode 100644 index 0000000..2cd1d1f --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Directory/result @@ -0,0 +1,3 @@ +Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Directory/test b/ede-panel/applets/start-menu/tests/tests/Directory/test new file mode 100644 index 0000000..fe7e126 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Directory/test @@ -0,0 +1,29 @@ +TEST_PURPOSE=" tag ..." + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + + Applications + apps.directory + + TextEditor + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop + installData ${XDG_DATA_DIR}/desktop-directories apps.directory +} diff --git a/ede-panel/applets/start-menu/tests/tests/DirectoryDir-relative/result b/ede-panel/applets/start-menu/tests/tests/DirectoryDir-relative/result new file mode 100644 index 0000000..2cd1d1f --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/DirectoryDir-relative/result @@ -0,0 +1,3 @@ +Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/DirectoryDir-relative/test b/ede-panel/applets/start-menu/tests/tests/DirectoryDir-relative/test new file mode 100644 index 0000000..4a5e039 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/DirectoryDir-relative/test @@ -0,0 +1,28 @@ +TEST_PURPOSE="relative tag ..." + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + ${PATH_EXPANSION}desktop-directories + + + Applications + apps.directory + + TextEditor + + + +EOF + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop + installData ${XDG_CONFIG_DIR}/menus/desktop-directories apps.directory +} diff --git a/ede-panel/applets/start-menu/tests/tests/DirectoryDir/test b/ede-panel/applets/start-menu/tests/tests/DirectoryDir/test new file mode 100644 index 0000000..dc8bb3b --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/DirectoryDir/test @@ -0,0 +1,4 @@ +. tests/DirectoryDir-relative/test +export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/' +TEST_PURPOSE=" absolute path" + diff --git a/ede-panel/applets/start-menu/tests/tests/Exclude/result b/ede-panel/applets/start-menu/tests/tests/Exclude/result new file mode 100644 index 0000000..dfbb122 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Exclude/result @@ -0,0 +1,3 @@ +Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop +Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop +Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Exclude/test b/ede-panel/applets/start-menu/tests/tests/Exclude/test new file mode 100644 index 0000000..728ec99 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Exclude/test @@ -0,0 +1,32 @@ +TEST_PURPOSE=" Keyword" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + + freecell.desktop + Game + + + + glines.desktop + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/Filename/result b/ede-panel/applets/start-menu/tests/tests/Filename/result new file mode 100644 index 0000000..dc159fa --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Filename/result @@ -0,0 +1 @@ +Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Filename/test b/ede-panel/applets/start-menu/tests/tests/Filename/test new file mode 100644 index 0000000..b9d021d --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Filename/test @@ -0,0 +1,26 @@ +TEST_PURPOSE=" tag" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + freecell.desktop + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/LegacyDir-Move/result b/ede-panel/applets/start-menu/tests/tests/LegacyDir-Move/result new file mode 100644 index 0000000..9a2b9db --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/LegacyDir-Move/result @@ -0,0 +1,2 @@ +Editors/ gideon-legacy.desktop ${LEGACY_DIR}/Development/gideon-legacy.desktop +/ Home.desktop ${LEGACY_DIR}/Home.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/LegacyDir-Move/test b/ede-panel/applets/start-menu/tests/tests/LegacyDir-Move/test new file mode 100644 index 0000000..7e6e85e --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/LegacyDir-Move/test @@ -0,0 +1,39 @@ +TEST_PURPOSE="move entries from " + +test_code() { + LEGACY_DIR=${MENUTESTDIR}/legacy_applnk + export LEGACY_DIR + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + ${LEGACY_DIR} + + + + + Development + + gideon-legacy.desktop + + + + Editors + + gideon-legacy.desktop + + + +EOF + + # Install .desktop files + + installData ${LEGACY_DIR} Home.desktop + installData ${LEGACY_DIR}/Development gideon-legacy.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/LegacyDir-relative/result b/ede-panel/applets/start-menu/tests/tests/LegacyDir-relative/result new file mode 100644 index 0000000..0c2af9b --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/LegacyDir-relative/result @@ -0,0 +1,9 @@ +Development/ gideon-legacy.desktop ${LEGACY_DIR}/Development/gideon-legacy.desktop +Development/ kbabel.desktop ${LEGACY_DIR}/Development/kbabel.desktop +Development/ quanta.desktop ${LEGACY_DIR}/Development/quanta.desktop +Editors/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Editors/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +/ Help.desktop ${LEGACY_DIR}/Help.desktop +/ Home.desktop ${LEGACY_DIR}/Home.desktop +/ Kfind.desktop ${LEGACY_DIR}/Kfind.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/LegacyDir-relative/test b/ede-panel/applets/start-menu/tests/tests/LegacyDir-relative/test new file mode 100644 index 0000000..5082590 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/LegacyDir-relative/test @@ -0,0 +1,42 @@ +TEST_PURPOSE="Simple test" + +test_code() { + LEGACY_DIR=${MENUTESTDIR}/legacy_applnk + export LEGACY_DIR + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + ${LEGACY_DIR} + + + + + Editors + kde-editors.directory + + TextEditor + + + + Development + kde-development.directory + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop + installData ${LEGACY_DIR}/Development gideon-legacy.desktop kbabel.desktop quanta.desktop + installData ${LEGACY_DIR} Kfind.desktop Home.desktop Help.desktop +} + diff --git a/ede-panel/applets/start-menu/tests/tests/Merge-combined/result b/ede-panel/applets/start-menu/tests/tests/Merge-combined/result new file mode 100644 index 0000000..999bd1e --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Merge-combined/result @@ -0,0 +1 @@ +Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Merge-combined/test b/ede-panel/applets/start-menu/tests/tests/Merge-combined/test new file mode 100644 index 0000000..07cfab3 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Merge-combined/test @@ -0,0 +1,46 @@ +TEST_PURPOSE="Merge Two Menus and a Legacy Menu" + +test_code() { + LEGACY_DIR=${MENUTESTDIR}/legacy_applnk + export LEGACY_DIR + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + ${LEGACY_DIR} + + + Development + + kate.desktop + + + + Development + + KEdit.desktop + + + + + Editors + + kwrite.desktop + + + +EOF + + # Install .desktop files + + installData ${LEGACY_DIR}/Development gideon-legacy.desktop + installData ${XDG_DATA_DIR}/applications kate.desktop kwrite.desktop KEdit.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/MergeDir-absolute/test b/ede-panel/applets/start-menu/tests/tests/MergeDir-absolute/test new file mode 100644 index 0000000..db6c895 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeDir-absolute/test @@ -0,0 +1,3 @@ +. tests/MergeDir-relative/test +export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/' +export TEST_PURPOSE=" absolute path test" diff --git a/ede-panel/applets/start-menu/tests/tests/MergeDir-relative/result b/ede-panel/applets/start-menu/tests/tests/MergeDir-relative/result new file mode 100644 index 0000000..9e17189 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeDir-relative/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop +Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/MergeDir-relative/test b/ede-panel/applets/start-menu/tests/tests/MergeDir-relative/test new file mode 100644 index 0000000..ba96b1a --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeDir-relative/test @@ -0,0 +1,61 @@ +TEST_PURPOSE=" relative path ..." + +test_code() { + # Generate applications.menu + mkdir -p "${XDG_CONFIG_DIR}/menus" + ./expand > "${XDG_CONFIG_DIR}/menus/applications.menu" < + + + KDE + + ${PATH_EXPANSION}applications-merged + + + Applications + + TextEditor + + + +EOF + + mkdir "${XDG_CONFIG_DIR}/menus/applications-merged/" + ./expand > "${XDG_CONFIG_DIR}/menus/applications-merged/test.menu" < + + + KDE + + + Development + + Development + + + +EOF + + # intentional crap entry to verify it does _not_ get picked up + ./expand > "${XDG_CONFIG_DIR}/menus/applications-merged/dar.notmenu" < + + + KDE + + + testing + + + + + +EOF + + + # Install .desktop files + installData "${XDG_DATA_DIR}/applications" kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-absolute/test b/ede-panel/applets/start-menu/tests/tests/MergeFile-absolute/test new file mode 100644 index 0000000..24cf669 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-absolute/test @@ -0,0 +1,3 @@ +. tests/MergeFile-relative/test +export PATH_EXPANSION='${XDG_CONFIG_DIR}/menus/' +export TEST_PURPOSE=" absolute path" diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-parent/result b/ede-panel/applets/start-menu/tests/tests/MergeFile-parent/result new file mode 100644 index 0000000..9e17189 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-parent/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop +Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-parent/test b/ede-panel/applets/start-menu/tests/tests/MergeFile-parent/test new file mode 100644 index 0000000..00ed006 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-parent/test @@ -0,0 +1,61 @@ +TEST_PURPOSE=" tag ..." + +test_code() { + # Tests the type attribute in + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_HOME}/menus + ./expand > ${XDG_CONFIG_HOME}/menus/applications.menu < + + + KDE + + ${XDG_CONFIG_DIR}/menus/test.menu + + + Applications + + TextEditor + + + +EOF + + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/test.menu < + + + KDE + + + Games + + Game + + + +EOF + + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + Development + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-path/result b/ede-panel/applets/start-menu/tests/tests/MergeFile-path/result new file mode 100644 index 0000000..fbe635e --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-path/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop +Games/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-path/test b/ede-panel/applets/start-menu/tests/tests/MergeFile-path/test new file mode 100644 index 0000000..97daa5c --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-path/test @@ -0,0 +1,61 @@ +TEST_PURPOSE=" tag ..." + +test_code() { + # Tests the type attribute in + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_HOME}/menus + ./expand > ${XDG_CONFIG_HOME}/menus/applications.menu < + + + KDE + + ${XDG_CONFIG_DIR}/menus/test.menu + + + Applications + + TextEditor + + + +EOF + + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/test.menu < + + + KDE + + + Games + + Game + + + +EOF + + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + Development + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-recursive/result b/ede-panel/applets/start-menu/tests/tests/MergeFile-recursive/result new file mode 100644 index 0000000..9e17189 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-recursive/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop +Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-recursive/test b/ede-panel/applets/start-menu/tests/tests/MergeFile-recursive/test new file mode 100644 index 0000000..4dbab21 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-recursive/test @@ -0,0 +1,58 @@ +TEST_PURPOSE="test elaborate recursive look in s" + +test_code(){ + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + applications-merged/test.menu + + + Applications + + TextEditor + + + +EOF + + + mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/ + ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu < + + + KDE + + extra/test.menu + +EOF + + mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/extra/ + ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/extra/test.menu < + + + KDE + + + Development + + Development + + + + ../test.menu + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-relative/result b/ede-panel/applets/start-menu/tests/tests/MergeFile-relative/result new file mode 100644 index 0000000..9e17189 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-relative/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop +Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile-relative/test b/ede-panel/applets/start-menu/tests/tests/MergeFile-relative/test new file mode 100644 index 0000000..c3478db --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile-relative/test @@ -0,0 +1,44 @@ +TEST_PURPOSE=" tag relative path" + +test_code(){ + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + ${PATH_EXPANSION}applications-merged/test.menu + + + Applications + + TextEditor + + + +EOF + + mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/ + ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu < + + + + KDE + + + Development + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile2/result b/ede-panel/applets/start-menu/tests/tests/MergeFile2/result new file mode 100644 index 0000000..9e17189 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile2/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop +Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile2/test b/ede-panel/applets/start-menu/tests/tests/MergeFile2/test new file mode 100644 index 0000000..e5b1591 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile2/test @@ -0,0 +1,57 @@ +TEST_PURPOSE=" tag ..." + +test_code() { + # Tests the use of relative paths in + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + applications-merged/test.menu + + + Applications + + TextEditor + + + +EOF + + mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/ + ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/test.menu < + + + KDE + + extra/test.menu + +EOF + + mkdir ${XDG_CONFIG_DIR}/menus/applications-merged/extra/ + ./expand > ${XDG_CONFIG_DIR}/menus/applications-merged/extra/test.menu < + + + KDE + + + Development + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile3/result b/ede-panel/applets/start-menu/tests/tests/MergeFile3/result new file mode 100644 index 0000000..9e17189 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile3/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Development/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop +Development/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/MergeFile3/test b/ede-panel/applets/start-menu/tests/tests/MergeFile3/test new file mode 100644 index 0000000..8781888 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/MergeFile3/test @@ -0,0 +1,56 @@ +TEST_PURPOSE=" tag ..." + +test_code() { + # Tests the use of relative paths in + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + test.menu + + + Applications + + TextEditor + + + +EOF + + ./expand > ${XDG_CONFIG_DIR}/menus/test.menu < + + + KDE + + extra/test.menu + +EOF + + mkdir ${XDG_CONFIG_DIR}/menus/extra/ + ./expand > ${XDG_CONFIG_DIR}/menus/extra/test.menu < + + + KDE + + + Development + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/Move-collapsing/result b/ede-panel/applets/start-menu/tests/tests/Move-collapsing/result new file mode 100644 index 0000000..10ccec6 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Move-collapsing/result @@ -0,0 +1,4 @@ +Games/BoardGame/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop +Games/BoardGame/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop +Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop +Games/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Move-collapsing/test b/ede-panel/applets/start-menu/tests/tests/Move-collapsing/test new file mode 100644 index 0000000..b842b0a --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Move-collapsing/test @@ -0,0 +1,40 @@ +TEST_PURPOSE="complicated operation" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + Games1 + Games + + + + Games1 + + BoardGame + + BoardGame + + + + + Games + + Game + + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/Move-ordering/result b/ede-panel/applets/start-menu/tests/tests/Move-ordering/result new file mode 100644 index 0000000..b941f58 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Move-ordering/result @@ -0,0 +1,3 @@ +Development/ gideon.desktop ${XDG_DATA_DIR}/applications/gideon.desktop +Games-Correct/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop +Games-Correct/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Move-ordering/test b/ede-panel/applets/start-menu/tests/tests/Move-ordering/test new file mode 100644 index 0000000..f8883b9 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Move-ordering/test @@ -0,0 +1,49 @@ +TEST_PURPOSE="Order of operations ..." + +test_code() { + # Tests the type attribute in + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + Development + + Games + + Game + + + + Development + + + + Development/Games-New + Games-Correct + + + Development/Games + Games-Wrong + + + Development + + Games + Games-New + + + +EOF + + # Move operations in sub-menus should be performed first + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications freecell.desktop glines.desktop gideon.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/Move-submenu/result b/ede-panel/applets/start-menu/tests/tests/Move-submenu/result new file mode 100644 index 0000000..f1c44ef --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Move-submenu/result @@ -0,0 +1 @@ +A/B/Development/ gideon-legacy.desktop ${XDG_DATA_DIR}/applications/gideon-legacy.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Move-submenu/test b/ede-panel/applets/start-menu/tests/tests/Move-submenu/test new file mode 100644 index 0000000..2751983 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Move-submenu/test @@ -0,0 +1,32 @@ +TEST_PURPOSE="Move into a new Submenu" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + + Development + + gideon-legacy.desktop + + + + Development + A/B/Development + + +EOF + + # Install .desktop files + + installData ${XDG_DATA_DIR}/applications gideon-legacy.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/Move/result b/ede-panel/applets/start-menu/tests/tests/Move/result new file mode 100644 index 0000000..d9a802d --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Move/result @@ -0,0 +1,2 @@ +Games/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop +Games/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Move/test b/ede-panel/applets/start-menu/tests/tests/Move/test new file mode 100644 index 0000000..9f2d162 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Move/test @@ -0,0 +1,34 @@ +TEST_PURPOSE="simple operation" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + Foo + Bar + BoardGames + Apps + BoardGames + Games + + + + BoardGames + + BoardGame + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/NoDisplay/result b/ede-panel/applets/start-menu/tests/tests/NoDisplay/result new file mode 100644 index 0000000..7ed7169 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/NoDisplay/result @@ -0,0 +1 @@ +Other/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/NoDisplay/test b/ede-panel/applets/start-menu/tests/tests/NoDisplay/test new file mode 100644 index 0000000..48e94ea --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/NoDisplay/test @@ -0,0 +1,37 @@ +TEST_PURPOSE="NoDisplay desktop entry values" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + + Shouldn't see this + hidden.directory + + TextEditor + + + + + Other + + + + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop hidden.desktop + installData ${XDG_DATA_DIR}/desktop-directories hidden.directory +} diff --git a/ede-panel/applets/start-menu/tests/tests/NoDisplay2/result b/ede-panel/applets/start-menu/tests/tests/NoDisplay2/result new file mode 100644 index 0000000..7ed7169 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/NoDisplay2/result @@ -0,0 +1 @@ +Other/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/NoDisplay2/test b/ede-panel/applets/start-menu/tests/tests/NoDisplay2/test new file mode 100644 index 0000000..62ba5a7 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/NoDisplay2/test @@ -0,0 +1,38 @@ +TEST_PURPOSE="Allocation of desktop entry values from deleted menus" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus +./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + + Shouldn't see this + apps.directory + + TextEditor + + + + + + Other + + + + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop freecell.desktop hidden.desktop + installData ${XDG_DATA_DIR}/desktop-directories apps.directory +} diff --git a/ede-panel/applets/start-menu/tests/tests/NotOnlyUnallocated-default/result b/ede-panel/applets/start-menu/tests/tests/NotOnlyUnallocated-default/result new file mode 100644 index 0000000..0c398b7 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/NotOnlyUnallocated-default/result @@ -0,0 +1,2 @@ +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Editors/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/NotOnlyUnallocated-default/test b/ede-panel/applets/start-menu/tests/tests/NotOnlyUnallocated-default/test new file mode 100644 index 0000000..2f4956e --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/NotOnlyUnallocated-default/test @@ -0,0 +1,33 @@ +TEST_PURPOSE="Another test" + +test_code() { + # Tests + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + Applications + + kwrite.desktop + + + + Editors + + kwrite.desktop + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/OnlyUnallocated/result b/ede-panel/applets/start-menu/tests/tests/OnlyUnallocated/result new file mode 100644 index 0000000..8be4bc7 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/OnlyUnallocated/result @@ -0,0 +1,3 @@ +BoardGames/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop +BoardGames/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop +Games/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/OnlyUnallocated/test b/ede-panel/applets/start-menu/tests/tests/OnlyUnallocated/test new file mode 100644 index 0000000..efda0ff --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/OnlyUnallocated/test @@ -0,0 +1,44 @@ +TEST_PURPOSE=" tag" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + BoardGames + + + Game + + CardGame + + + + + PuzzleGame + + + + Games + + Game + + + + + + +EOF + + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/Or/result b/ede-panel/applets/start-menu/tests/tests/Or/result new file mode 100644 index 0000000..fc75a82 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Or/result @@ -0,0 +1,4 @@ +Applications/ freecell.desktop ${XDG_DATA_DIR}/applications/freecell.desktop +Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop +Applications/ glines.desktop ${XDG_DATA_DIR}/applications/glines.desktop +Applications/ mahjongg.desktop ${XDG_DATA_DIR}/applications/mahjongg.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/Or/test b/ede-panel/applets/start-menu/tests/tests/Or/test new file mode 100644 index 0000000..cd10cfd --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/Or/test @@ -0,0 +1,29 @@ +TEST_PURPOSE=" Keyword" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + + freecell.desktop + Game + + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/boolean-logic/result b/ede-panel/applets/start-menu/tests/tests/boolean-logic/result new file mode 100644 index 0000000..ea47f01 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/boolean-logic/result @@ -0,0 +1,3 @@ +Apps/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Apps/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Apps/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/boolean-logic/test b/ede-panel/applets/start-menu/tests/tests/boolean-logic/test new file mode 100644 index 0000000..dc045c4 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/boolean-logic/test @@ -0,0 +1,36 @@ +TEST_PURPOSE="foofoo shouldn't match anything" + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + + Applications + apps.directory + + + TextEditor + + Game + Game + + + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop kate.desktop + installData ${XDG_DATA_DIR}/applications/test freecell.desktop + installData ${XDG_DATA_DIR}/desktop-directories apps.directory +} diff --git a/ede-panel/applets/start-menu/tests/tests/desktop-name-collision/result b/ede-panel/applets/start-menu/tests/tests/desktop-name-collision/result new file mode 100644 index 0000000..e1e9221 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/desktop-name-collision/result @@ -0,0 +1,3 @@ +Development/ kde-gideon.desktop ${XDG_DATA_HOME}/applications/kde-gideon.desktop +Development/ mahjongg.desktop ${XDG_DATA_HOME}/applications/mahjongg.desktop +Games/ freecell.desktop ${XDG_DATA_HOME}/applications/freecell.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/desktop-name-collision/test b/ede-panel/applets/start-menu/tests/tests/desktop-name-collision/test new file mode 100644 index 0000000..46b5ded --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/desktop-name-collision/test @@ -0,0 +1,53 @@ +TEST_PURPOSE=".desktop files with same name ..." + +test_code() { + # Tests the type attribute in + + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + Games + + Game + + + + Development + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications freecell.desktop glines.desktop mahjongg.desktop + + # freecell.desktop is installed twice, only the version under ${XDG_DATA_HOME}/applications + # should show up in the menu + # freecell.desktop in ${XDG_DATA_DIR}/applications should be ignored. + installData ${XDG_DATA_HOME}/applications freecell.desktop + + # ${XDG_DATA_HOME}/applications/glines.desktop has NoDisplay=true + # glines.desktop should not be shown + # glines.desktop in ${XDG_DATA_DIR}/applications should be ignored. + installDataAs ${XDG_DATA_HOME}/applications glines-2.desktop glines.desktop + + # ${XDG_DATA_HOME}/applications/mahjongg.desktop has Categories=Development + # mahjongg.desktop should be shown under the Development menu + # mahjongg.desktop in ${XDG_DATA_DIR}/applications should be ignored. + installDataAs ${XDG_DATA_HOME}/applications mahjongg-2.desktop mahjongg.desktop + + # kde/gideon.desktop and kde-gideon.desktop are equivalent + # only the version under ${XDG_DATA_HOME}/applications should show up in the menu + # gideon.desktop in ${XDG_DATA_DIR}/applications/kde should be ignored. + installDataAs ${XDG_DATA_DIR}/applications/kde gideon.desktop + installDataAs ${XDG_DATA_HOME}/applications gideon.desktop kde-gideon.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/menu-multiple-matching/result b/ede-panel/applets/start-menu/tests/tests/menu-multiple-matching/result new file mode 100644 index 0000000..986c873 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/menu-multiple-matching/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ gataxx.desktop ${XDG_DATA_DIR}/applications/gataxx.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Applications/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/menu-multiple-matching/test b/ede-panel/applets/start-menu/tests/tests/menu-multiple-matching/test new file mode 100644 index 0000000..d8914ab --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/menu-multiple-matching/test @@ -0,0 +1,36 @@ +TEST_PURPOSE="complicated rule ..." + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + + Game + BoardGame + + + TextEditor + quanta.desktop + + + + mahjongg.desktop + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications gataxx.desktop mahjongg.desktop freecell.desktop glines.desktop kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tests/official-categories/categories.list b/ede-panel/applets/start-menu/tests/tests/official-categories/categories.list new file mode 100644 index 0000000..6e2198d --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/official-categories/categories.list @@ -0,0 +1,10 @@ +AudioVideo +Development +Education +Game +Graphics +Network +Office +Settings +System +Utility diff --git a/ede-panel/applets/start-menu/tests/tests/official-categories/test b/ede-panel/applets/start-menu/tests/tests/official-categories/test new file mode 100644 index 0000000..09621cd --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/official-categories/test @@ -0,0 +1,73 @@ +TEST_PURPOSE="verify all required categories are supported" + +base_loc="tests/official-categories" + +test_code() +{ + local category + for category in $(< "${base_loc}/categories.list"); do + CATEGORY="${category}" ./expand "${base_loc}/unique-entry.desktop" > "data/${category}.desktop" + installData "${XDG_DATA_DIR}/applications" "${category}.desktop" + rm "data/${category}.desktop" + done +} + +query() +{ + echo "$@" + ret='' + while [ -z "$ret" ]; do + echo -n "y/n? :" + read ret + if ! [ "$ret" == "y" -o "$ret" == "n" ]; then + echo "invalid response; must be 'y' or 'n'" + ret='' + fi + done + [ "$ret" == "y" ] && return 0 + return 1 +} + +interpret_results() +{ + # inefficient, but works. + local missed='' + local correct='' + for category in $(< "${base_loc}/categories.list"); do + if grep "/${category}\.desktop" "${MENUTESTDIR}/run-result" > /dev/null; then + correct="${correct} ${category}" + else + missed="${missed} ${category}" + fi + done + if [ -z "${missed}" ]; then + echo ">>> OK" + return 0 + fi + if [ "$(echo $missed)" != "Settings" ]; then + # failures. + cat "${MENUTESTDIR}/run-result" + echo "missed categories $missed" + echo "matched ${correct}" + echo ">>> Failed (missed $(echo $missed | wc -w) out of $(wc -l "${base_loc}/categories.list")" + return 1 + fi + echo ">>> Settings failed; checking interactively" + local ret + if [ "$(id -u)" != "0" ]; then + echo ">>> Cannot go interactive due to test being ran as non-root; re-run as root" + return 1; + elif ! which xdg-desktop-menu &> /dev/null; then + echo ">>> xdg-desktop-menu is not available; cannot do interactive test" + return 1; + fi + xdg-desktop-menu install --mode system --novendor "${XDG_DATA_DIR}/applications/Settings.desktop" + ( + query "Please check for a 'menu-spec-testing' in any gnome/kde system settings panel" + ) + ret=$? + xdg-desktop-menu uninstall --mode system "${XDG_DATA_DIR}/applications/Settings.desktop" + return $(($ret)) +} + +MODE=system_data diff --git a/ede-panel/applets/start-menu/tests/tests/official-categories/unique-entry.desktop b/ede-panel/applets/start-menu/tests/tests/official-categories/unique-entry.desktop new file mode 100644 index 0000000..6ee3b88 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/official-categories/unique-entry.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=menu-spec-testing +Exec=true +Icon=quanta +Type=Application +MimeType=text/html +Comment=menu-spec testing +Categories=${CATEGORY}; diff --git a/ede-panel/applets/start-menu/tests/tests/submenu-collision/result b/ede-panel/applets/start-menu/tests/tests/submenu-collision/result new file mode 100644 index 0000000..6aa47d7 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/submenu-collision/result @@ -0,0 +1,5 @@ +Applications/ KEdit.desktop ${XDG_DATA_DIR}/applications/KEdit.desktop +Applications/ kate.desktop ${XDG_DATA_DIR}/applications/kate.desktop +Applications/ kbabel.desktop ${XDG_DATA_DIR}/applications/kbabel.desktop +Applications/ kwrite.desktop ${XDG_DATA_DIR}/applications/kwrite.desktop +Applications/ quanta.desktop ${XDG_DATA_DIR}/applications/quanta.desktop diff --git a/ede-panel/applets/start-menu/tests/tests/submenu-collision/test b/ede-panel/applets/start-menu/tests/tests/submenu-collision/test new file mode 100644 index 0000000..7d29896 --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tests/submenu-collision/test @@ -0,0 +1,32 @@ +TEST_PURPOSE="two submenus with the same name ..." + +test_code() { + # Generate applications.menu + mkdir -p ${XDG_CONFIG_DIR}/menus + ./expand > ${XDG_CONFIG_DIR}/menus/applications.menu < + + + KDE + + + + + Applications + + TextEditor + + + + Applications + + Development + + + +EOF + + # Install .desktop files + installData ${XDG_DATA_DIR}/applications kwrite.desktop KEdit.desktop quanta.desktop kate.desktop kbabel.desktop +} diff --git a/ede-panel/applets/start-menu/tests/tet_menutest b/ede-panel/applets/start-menu/tests/tet_menutest new file mode 100755 index 0000000..1aae67e --- /dev/null +++ b/ede-panel/applets/start-menu/tests/tet_menutest @@ -0,0 +1,39 @@ +#!/bin/bash +TET_RUN="asdf" +export MENUTESTDIR="${MENUTESTDIR:-/tmp/menutest}" +. menutest +# hack, figure out the var to use here + +tpstart() { + tet_infoline "$*" + FAIL=N +} + +tet_startup='' +tet_cleanup='' +declare -i count=1 +iclist='' +echo $TESTS +for TESTCASE in ${TESTS}; do + [ ! -e "tests/${TESTCASE}/test" ] && continue; + # this basically curries the arg to run_test. + eval "tp${count}() { + . tests/${TESTCASE}/test + tpstart \"$(. tests/${TESTCASE}/test; echo ${TEST_PURPOSE-none stated})\"; + if ! run_test tests/\"$TESTCASE\"; then + tet_result FAIL; + else + tet_result PASS; + fi + set +x + }"; + iclist="${iclist} ic${count}" + eval "ic${count}=tp${count}" + # force subshelling, so that it doesn't pull a die on us + ((count+=1)) +done +tet_iclist=iclist +. /opt/lsb-tet3-lite/lib/posix_sh/tcm.sh +tet_outputline 100 "xdg menu test" +tet_tcm_main $tet_iclist + diff --git a/ede-panel/applets/taskbar/Jamfile b/ede-panel/applets/taskbar/Jamfile new file mode 100644 index 0000000..777d8e6 --- /dev/null +++ b/ede-panel/applets/taskbar/Jamfile @@ -0,0 +1,13 @@ +# +# $Id$ +# +# Part of Equinox Desktop Environment (EDE). +# Copyright (c) 2009 EDE Authors. +# +# This program is licensed under terms of the +# GNU General Public License version 2 or newer. +# See COPYING for details. + +SubDir TOP ede-panel-new applets taskbar ; + +PanelApplet taskbar : TaskButton.cpp Taskbar.cpp ; diff --git a/ede-panel/applets/taskbar/TaskButton.cpp b/ede-panel/applets/taskbar/TaskButton.cpp new file mode 100644 index 0000000..ef70723 --- /dev/null +++ b/ede-panel/applets/taskbar/TaskButton.cpp @@ -0,0 +1,155 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "TaskButton.h" +#include "Taskbar.h" +#include "Netwm.h" +#include "icons/window.xpm" + +EDELIB_NS_USING(MenuItem) +EDELIB_NS_USING(IconLoader) +EDELIB_NS_USING(ICON_SIZE_TINY) + +static Fl_Pixmap image_window(window_xpm); + +static void close_cb(Fl_Widget*, void*); +static void restore_cb(Fl_Widget*, void*); +static void minimize_cb(Fl_Widget*, void*); +static void maximize_cb(Fl_Widget*, void*); + +static MenuItem menu_[] = { + {_("Restore"), 0, restore_cb, 0}, + {_("Minimize"), 0, minimize_cb, 0}, + {_("Maximize"), 0, maximize_cb, 0, FL_MENU_DIVIDER}, + {_("Close"), 0, close_cb, 0}, + {0} +}; + +static void redraw_whole_panel(TaskButton *b) { + /* Taskbar specific member */ + Taskbar *tb = (Taskbar*)b->parent(); + tb->panel_redraw(); +} + +static void close_cb(Fl_Widget*, void *b) { + TaskButton *bb = (TaskButton*)b; + netwm_close_window(bb->get_window_xid()); + /* no need to redraw whole panel since taskbar elements are recreated again */ +} + +static void restore_cb(Fl_Widget*, void *b) { + TaskButton *bb = (TaskButton*)b; + wm_ede_restore_window(bb->get_window_xid()); + + netwm_set_active_window(bb->get_window_xid()); + redraw_whole_panel(bb); +} + +static void minimize_cb(Fl_Widget*, void *b) { + TaskButton *bb = (TaskButton*)b; + + if(wm_get_window_state(bb->get_window_xid()) != WM_STATE_ICONIC) + wm_set_window_state(bb->get_window_xid(), WM_STATE_ICONIC); + + redraw_whole_panel(bb); +} + +static void maximize_cb(Fl_Widget*, void *b) { + TaskButton *bb = (TaskButton*)b; + + netwm_set_active_window(bb->get_window_xid()); + netwm_maximize_window(bb->get_window_xid()); + + redraw_whole_panel(bb); +} + +TaskButton::TaskButton(int X, int Y, int W, int H, const char *l) : Fl_Button(X, Y, W, H, l), xid(0) { + box(FL_UP_BOX); + align(FL_ALIGN_INSIDE | FL_ALIGN_LEFT | FL_ALIGN_CLIP); + + /* parameters for callbacks */ + menu_[0].user_data(this); + menu_[1].user_data(this); + menu_[2].user_data(this); + menu_[3].user_data(this); + + if(IconLoader::inited()) { + Fl_Shared_Image *img = IconLoader::get("process-stop", ICON_SIZE_TINY); + menu_[3].image((Fl_Image*)img); + } + + image(image_window); +} + +void TaskButton::draw(void) { + Fl_Color col = value() ? selection_color() : color(); + draw_box(value() ? (down_box() ? down_box() : fl_down(box())) : box(), col); + + if(image()) { + int X, Y, lw, lh; + + X = x() + 5; + Y = (y() + h() / 2) - (image()->h() / 2); + image()->draw(X, Y); + + X += image()->w() + 5; + + if(label()) { + fl_font(labelfont(), labelsize()); + fl_color(labelcolor()); + + lw = lh = 0; + fl_measure(label(), lw, lh, align()); + + /* use clipping so long labels do not be drawn on the right border, which looks ugly */ + fl_push_clip(x() + Fl::box_dx(box()), + y() + Fl::box_dy(box()), + w() - Fl::box_dw(box()) - 5, + h() - Fl::box_dh(box())); + + Y = (y() + h() / 2) - (lh / 2); + fl_draw(label(), X, Y, lw, lh, align(), 0, 0); + + fl_pop_clip(); + } + } else { + draw_label(); + } + + if(Fl::focus() == this) + draw_focus(); +} + +void TaskButton::display_menu(void) { + const char *t = tooltip(); + + /* do not popup tooltip when the menu is on */ + tooltip(NULL); + + const MenuItem *item = menu_->popup(Fl::event_x(), Fl::event_y(), 0, 0, 0); + if(item && item->callback()) + item->do_callback(this); + + tooltip(t); +} + +void TaskButton::update_title_from_xid(void) { + E_RETURN_IF_FAIL(xid >= 0); + + char *title = netwm_get_window_title(xid); + if(!title) { + label("..."); + tooltip("..."); + } else { + copy_label(title); + tooltip(label()); + free(title); + } +} diff --git a/ede-panel/applets/taskbar/TaskButton.h b/ede-panel/applets/taskbar/TaskButton.h new file mode 100644 index 0000000..af26814 --- /dev/null +++ b/ede-panel/applets/taskbar/TaskButton.h @@ -0,0 +1,24 @@ +#ifndef __TASKBUTTON_H__ +#define __TASKBUTTON_H__ + +#include +#include + +class TaskButton : public Fl_Button { +private: + /* window ID this button handles */ + Window xid; + +public: + TaskButton(int X, int Y, int W, int H, const char *l = 0); + + void draw(void); + void display_menu(void); + + void set_window_xid(Window win) { xid = win; } + Window get_window_xid(void) { return xid; } + + void update_title_from_xid(void); +}; + +#endif diff --git a/ede-panel/applets/taskbar/Taskbar.cpp b/ede-panel/applets/taskbar/Taskbar.cpp new file mode 100644 index 0000000..4c280fe --- /dev/null +++ b/ede-panel/applets/taskbar/Taskbar.cpp @@ -0,0 +1,230 @@ +#include "Applet.h" + +#include +#include +#include + +#include "Netwm.h" +#include "TaskButton.h" +#include "Taskbar.h" +#include "Panel.h" + +#define DEFAULT_CHILD_W 175 +#define DEFAULT_SPACING 5 + +static void button_cb(TaskButton *b, void *t) { + Taskbar *tt = (Taskbar*)t; + + if(Fl::event_button() == FL_RIGHT_MOUSE) + b->display_menu(); + else + tt->activate_window(b); +} + +static void net_event_cb(int action, Window xid, void *data) { + E_RETURN_IF_FAIL(data != NULL); + + if(action == NETWM_CHANGED_CURRENT_WORKSPACE || action == NETWM_CHANGED_WINDOW_LIST) { + Taskbar *tt = (Taskbar*)data; + tt->create_task_buttons(); + return; + } + + if(action == NETWM_CHANGED_WINDOW_NAME) { + Taskbar *tt = (Taskbar*)data; + tt->update_child_title(xid); + return; + } + + /* this is a message, so property is not changed and netwm_get_active_window() must be called */ + if(action == NETWM_CHANGED_ACTIVE_WINDOW) { + Taskbar *tt = (Taskbar*)data; + tt->update_active_button(); + return; + } +} + +Taskbar::Taskbar() : Fl_Group(0, 0, 40, 25), curr_active(NULL), prev_active(NULL), panel(NULL) { + end(); + + panel = EDE_PANEL_GET_PANEL_OBJECT; + + /* assure display is openned */ + fl_open_display(); + + create_task_buttons(); + netwm_callback_add(net_event_cb, this); +} + +Taskbar::~Taskbar() { + netwm_callback_remove(net_event_cb); +} + +void Taskbar::create_task_buttons(void) { + /* erase all current elements */ + if(children()) + clear(); + + /* also current/prev storage */ + curr_active = prev_active = NULL; + + /* redraw it, in case no windows exists in this workspace */ + panel_redraw(); + + Window *wins; + int nwins = netwm_get_mapped_windows(&wins); + + if(nwins > 0) { + TaskButton *b; + int curr_workspace = netwm_get_current_workspace(); + char *title; + + for(int i = 0; i < nwins; i++) { + if(!netwm_manageable_window(wins[i])) + continue; + + /* + * show window per workspace + * TODO: allow showing all windows in each workspace + */ + if(curr_workspace == netwm_get_window_workspace(wins[i])) { + b = new TaskButton(0, 0, DEFAULT_CHILD_W, 25); + b->set_window_xid(wins[i]); + b->update_title_from_xid(); + + /* + * catch name changes + * TODO: put this in Netwm.{h,cpp} + */ + XSelectInput(fl_display, wins[i], PropertyChangeMask | StructureNotifyMask); + + b->callback((Fl_Callback*)button_cb, this); + add(b); + } + } + + XFree(wins); + } + + layout_children(); + update_active_button(); +} + +void Taskbar::resize(int X, int Y, int W, int H) { + Fl_Widget::resize(X, Y, W, H); + layout_children(); +} + +void Taskbar::layout_children(void) { + if(!children()) + return; + + Fl_Widget *o; + int X = x() + Fl::box_dx(box()); + int Y = y() + Fl::box_dy(box()); + int W = w() - Fl::box_dw(box()); + + int child_w = DEFAULT_CHILD_W; + int sz = children(); + int all_buttons_w = 0; + + /* figure out the bounds */ + for(int i = 0; i < sz; i++) + all_buttons_w += child(i)->w() + DEFAULT_SPACING; + + if(all_buttons_w > W) { + int reduce = (all_buttons_w - W) / sz; + child_w -= reduce; + } + + /* now, position each child and resize it if needed */ + for(int i = 0; i < sz; i++) { + o = child(i); + + o->resize(X, Y, child_w, o->h()); + X += o->w() + DEFAULT_SPACING; + } +} + +void Taskbar::update_active_button(int xid) { + if(!children()) + return; + + if(xid == -1) { + xid = netwm_get_active_window(); + /* TODO: make sure panel does not get 'active', or all buttons will be FL_UP_BOX */ + } + + TaskButton *o; + for(int i = 0; i < children(); i++) { + o = (TaskButton*)child(i); + + if(o->get_window_xid() == xid) + o->box(FL_DOWN_BOX); + else + o->box(FL_UP_BOX); + } + + redraw(); +} + +void Taskbar::activate_window(TaskButton *b) { + E_RETURN_IF_FAIL(b != NULL); + + Window xid = b->get_window_xid(); + + /* if clicked on activated button, it will be minimized, then next one will be activated */ + if(b == curr_active) { + if(wm_get_window_state(xid) != WM_STATE_ICONIC) { + /* minimize if not so */ + wm_set_window_state(xid, WM_STATE_ICONIC); + + if(prev_active && + prev_active != b && + wm_get_window_state(prev_active->get_window_xid()) != WM_STATE_ICONIC) + { + xid = prev_active->get_window_xid(); + b = prev_active; + } else { + return; + } + } + } + + /* active or restore minimized */ + netwm_set_active_window(xid); + update_active_button(xid); + + /* TODO: use stack for this (case when this can't handle: minimize three window, out of four on the workspace) */ + prev_active = curr_active; + curr_active = b; +} + +void Taskbar::update_child_title(Window xid) { + E_RETURN_IF_FAIL(xid >= 0); + + TaskButton *o; + for(int i = 0; i < children(); i++) { + o = (TaskButton*)child(i); + + if(o->get_window_xid() == xid) { + o->update_title_from_xid(); + break; + } + } +} + +void Taskbar::panel_redraw(void) { + E_RETURN_IF_FAIL(panel != NULL); + panel->redraw(); +} + + +EDE_PANEL_APPLET_EXPORT ( + Taskbar, + EDE_PANEL_APPLET_OPTION_ALIGN_LEFT | EDE_PANEL_APPLET_OPTION_RESIZABLE_H, + "Taskbar", + "0.1", + "empty", + "Sanel Zukan" +) diff --git a/ede-panel/applets/taskbar/Taskbar.h b/ede-panel/applets/taskbar/Taskbar.h new file mode 100644 index 0000000..1bf376d --- /dev/null +++ b/ede-panel/applets/taskbar/Taskbar.h @@ -0,0 +1,30 @@ +#ifndef __TASKBAR_H__ +#define __TASKBAR_H__ + +#include + +class TaskButton; +class Panel; + +class Taskbar : public Fl_Group { +public: + TaskButton *curr_active, *prev_active; + Panel *panel; + +public: + Taskbar(); + ~Taskbar(); + + void create_task_buttons(void); + + void resize(int X, int Y, int W, int H); + void layout_children(void); + + void update_active_button(int xid = -1); + void activate_window(TaskButton *b); + void update_child_title(Window xid); + + void panel_redraw(void); +}; + +#endif diff --git a/ede-panel/applets/taskbar/icons/window.png b/ede-panel/applets/taskbar/icons/window.png new file mode 100644 index 0000000000000000000000000000000000000000..e49d30c099148a07486a407de87db6511529d393 GIT binary patch literal 463 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPggaP8miX;p3kwc6hbmm72G|21Ky{o<^io!^K@|xu{b?-vZ3GM00Ec#R&4?c zOG-N0oC}z(xTLHEu0P=V62qX_=g{Tx(m_O~$=OB8>&@MTAIfutm5*vi-gg8;cnJA3A6I);@8)!G_;+oF`PUt_|sa~&d;ll?_Kw= zuG%B|8iUfD$(J`bYCc-Q=uu;H*JSC!D#q8Rx;A;--j%^1=i*XYr~Qjv$v|dy-k!^j zJFZXq7U@x?Sy<%U!*ENMk7mPGj!refKyuq<_tHQc>KZ1eep2Q`lOso&VfhUv%)WCc7 zb$jjk|IYmJ*=7@q40HR-)6wGM2lzL5Su^a9`+BxT)%5d5V9+vny85}Sb4q9e08WCi AkN^Mx literal 0 HcmV?d00001 diff --git a/ede-panel/applets/taskbar/icons/window.xpm b/ede-panel/applets/taskbar/icons/window.xpm new file mode 100644 index 0000000..a02321d --- /dev/null +++ b/ede-panel/applets/taskbar/icons/window.xpm @@ -0,0 +1,76 @@ +/* XPM */ +static char * window_xpm[] = { +"16 16 57 1", +" c None", +". c #BABDB6", +"+ c #FFFFFF", +"@ c #FEFEFE", +"# c #204A87", +"$ c #FCFCFB", +"% c #3465A4", +"& c #FAFBFA", +"* c #F9F9F9", +"= c #F8F9F8", +"- c #F8F8F8", +"; c #F6F6F6", +"> c #F5F6F6", +", c #F6F5F6", +"' c #F6F6F5", +") c #F6F5F5", +"! c #BBBDB3", +"~ c #F7F7F7", +"{ c #F4F3F3", +"] c #F3F3F3", +"^ c #F2F3F3", +"/ c #F2F2F3", +"( c #F3F2F3", +"_ c #F5F5F5", +": c #F1F0F1", +"< c #F0F1F1", +"[ c #F1F0F0", +"} c #F0F0F0", +"| c #F0F1F0", +"1 c #F0EFF0", +"2 c #EFF0EF", +"3 c #F5F5F4", +"4 c #EEEEEE", +"5 c #EDEEED", +"6 c #EDEDED", +"7 c #EDEDEE", +"8 c #EEEEED", +"9 c #F2F2F2", +"0 c #EBEBEB", +"a c #EBEBEA", +"b c #EAEAEB", +"c c #EAEAEA", +"d c #F1F2F1", +"e c #EFEFF0", +"f c #E9E8E8", +"g c #E8E8E8", +"h c #E8E7E8", +"i c #E7E7E7", +"j c #E8E7E7", +"k c #EFEFEF", +"l c #EEEFEE", +"m c #E5E5E6", +"n c #E5E5E5", +"o c #E5E6E5", +"p c #F3F2F2", +"q c #F0EFEF", +"r c #EFF0F0", +" ", +" ............ ", +" .++++++++++++. ", +" .@##########+. ", +" .$%%%%%%%%%%+. ", +" .&***==-====&. ", +" .-;>,;'')'''-! ", +" .~{]]]]^^]/(;. ", +" ._:<[}|}}}123. ", +" .]4567867666]. ", +" .900aabbbcacd. ", +" .efgggghigjhk. ", +" .lmnonnnnnnn8. ", +" .p2qkr1kkkqkk. ", +" ............ ", +" "}; diff --git a/ede-panel/ede-panel.cpp b/ede-panel/ede-panel.cpp new file mode 100644 index 0000000..c6bff01 --- /dev/null +++ b/ede-panel/ede-panel.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + +#include "Panel.h" +#include "AppletManager.h" + +int main(int argc, char **argv) { + Panel* panel = new Panel(); + panel->load_applets(); + panel->show(); + return Fl::run(); +} diff --git a/ede-panel/images/tile.xpm b/ede-panel/images/tile.xpm new file mode 100644 index 0000000..ddc656e --- /dev/null +++ b/ede-panel/images/tile.xpm @@ -0,0 +1,120 @@ +/* XPM */ +static const char * tile_xpm[] = { +"100 100 17 1", +" c None", +". c #DCDCDC", +"+ c #D9D9D9", +"@ c #E4E4E4", +"# c #DFDFDF", +"$ c #CECECE", +"% c #D2D2D2", +"& c #C8C8C8", +"* c #CACACA", +"= c #C4C4C4", +"- c #BEBEBE", +"; c #D4D4D4", +"> c #E8E8E8", +", c #D6D6D6", +"' c #C6C6C6", +") c #B2B2B2", +"! c #E2E2E2", +".+@.##$%%$$$&*=..%$*%-;*>,%$%#>,$,%%*%*.#*%++$;+,,.&&=-%%+.$,#,=;%#,@.+,%>,+=-++@@#%$.++#,--;.=*+,,>", +"%%.&$@.#,*%*&'%%&$;,*%%,&%;%,#..;$$%';+.+,++.%.;+@@@$'.;'..;@.+$%)&!!,.!>+%@%-*%@>#.+&&+.+$%,++$$#++", +",.$&$+#+.%++$%,=$;$&'+.%%&-+++;%;;$%+%+;.#.+@!!..>#+$%;,$.+@,+.%-%%-%>#+.#+$%...%,!>>;$$$.%$+#+*++&*", +",$;;;..-*!;*.+%=*%;*%%=&$+$++,-%;+++++++++++#@@@>@#,++;$%;++#.+++%&*=#.+;%>@+,...++$>@#&%*%;.$=$##=;", +"%#$,+$$%,.++.%%,'*%;,$=-$+@,%*$+%.#+%,.++%;.,#@@@>#+*,$%;,++..@#.,+@+.#.+%#+!.,%#>%,,+>#.%$!#%=%$$#+", +"+*'%$)&,+,+,+*%,,$,+,*-&++$%%*%#@#%$;+.%,!.+;+!@+>..,%$,$;..#,@.$;%.##.#!#;;++#.+%!.,%,.@##>%,>%$,+,", +",,$$$%%%';+$%,$!,$,;;,..,&)%%+#!+..,++%%++#+#@+,##,;,%.%.+++.#;%+.,+#.,#+;#%.@,+#.+,#!,+.>#..,,++++.", +";=-;##%=$%,.$%%+;%%%$+.%&%%%,.,%++..+++;%;;.+#.+%..%;+,#+$%.;,....+.++.+,$$.#>.$+@&+#;.@@.%!@.#+@!,%", +"-$$&+%.,#;;&$$,*$+%%.#*='$,,+.;,+,##...$%++%.,++%,..+;%,%,,%+,.,##.+%%+@,'%#.++,,,%++.>++.>#+#>>.%,%", +",!.;$$$#.;%'-'&*+++.$=*%;,++;;;.+;++,+++@@%++++..$..+%+&$.+*;,++.+;+*%+#+++...+;.%+##@.%@>#.!>#&%+=$", +"...#%%,,;=-%$=%%@+.;==;=+#+%+*$#.%+%+@.++!,%,;;%..,,*,+'+,;;#.$%#%+%,.%&+#.$+@!%;,+##++#@!!#@@&%@..+", +"+$=%%$#,$--$=$%+#,=%%,%;$;%.+.,,+!++#..++%;.%%$..,#;.+$#%$$#;.%;+..+,.%,%+,..@++..*+#,+.#>@#.+++!.+.", +"..+'=;%$,+,%$*&%&=-;+#+%==..;,#,#.###@+$%,,$$;+@#.,;.*;.+&.#%*.>.,.#$'+.%%,,%##++;+,#*.@>#%%#@+%$@@.", +"..;+.$%=$,##,,'=&$$%%++$$%,,;$;.,##.+#;++;#+;+.>..#$$%,%;,%+=$+#.!...+%,%++%;&+#$%.%#@,;+#.,#,+.+%;,", +"%;+@++*$%%$+@,*&$$*&;%%.#,.$$,++#@@#.,+%.;%#+!#.,!#..'%;%+;%*%;+,!@..#,;.%%,+&,*$.,%@>!+;,###&;;#.;%", +"$$;+.,.;%=$$$,+%+$,&$%,,.+=$%.,;##!#.#$%.+%,+.@.,#%*#%$$;+*.+,$%%@.+@..+,$%++$&%#+,.#,#@#.&$$++,+.,,", +",%+.+$.;&;%+,$,+,+%%**$+*$+,%+%,$%.%;@+%##;;;;#+##%*$%'*&++.;.+;.#+;#>++,+*%%%++,,.#;=@@.+,;$&;%;%$-", +"%'.+,'*;;;#+;&$#$$,%*,$=%,+,,%%$%%%+%!@++#;++&+.,.;,&$-%,;,++%..#+;++$+#,++,=%%+++!#++..+;;,%,%=&+..", +"%,;+%&$$+#%%++%;$&&%%%'%,,+++%,%;*.+%%>..+,+##+%+;=.&&,%$#.%;%!@,+,%,,+++.+.*%%;$.%+#!+&,;,;+,%+$=,$", +"%#.&%,-;+$+.,++$*$%&=$,,;#,.,&;&.,+%++;%.+%,+!#.;%%&*,,%.$,;&#!.%%%%++,.%,;+,%.#.$;++;%;++#,$=.++,$=", +";%;%$,$%=%#.@,,$*%*=%;%%+%$.%&'+%$#.=,++&;$>#*..+,%+$+;,,#.;.@,%.+,+;,#++,$$+++.++,+#%%$%##.,++*$+;.", +"$+%$;.,*$#+%.!..,%=*$&$;;%$%$+,$$%%%'++,+,.@#+.,$.,!;#;$,#,#++,%.%.,.+;.$%.,++%%$+,#%,+,$,.%.%$+.$,;", +"-%@#..%$+;%+;,%.+,$$'$*+%%;,%$$%%+;;,&'%#.##...$#.!$'#>+&,+#,+$*+.;+#,%%;,.!+,+$$%+.+++*+..+,+%,#+.=", +";%;###+@!;+$$;,$.+.%$$%.%.$*%&&,.,,;+;$&$+.+..$+#+$%.!.%$%%+#*;;&,#++%,,+,;#++$$%++,%,%%++!.+@.;*#,*", +"*%%+@%+#,...,$;,'$++,;#&,,&'=&'*+++.%$-=$$%!..#,%%++>+=#%;+,++,++.%..+$;.+,+$%%+,.;&,#+,*.++##@;%,%-", +"%$%#,.++$+#+%%+$'%&.,;#+$&$&&%-;,$.!+=*&=*.#;#.,%,@@$;#.*%..,+,#@..+,$%@,,.&%$;,%%$%,+%+%+&,@.##.+**", +"%*..+%.;+#,+.$$,;*$$%+;+%%$,%'%;%*%.;%+$=,%*$++,+>+'..,$%;!%$##@+#..$%.%;,,%$%%;==..$%%,+,++$;@.&+>#", +"%@@%'+$+.%.#.=&,%%%$';%&%$+.%&.%**+=$+#%.%%';%*$>.&=..%$$*...+,.;.#%.#+.@$%%.,$-&,,,%;*#%;#$,+;#+#@.", +"!@.;=,.;$@.;+.$''%%=.;=;+%*,..;=&%=$+,&,+%,+;%$;,.&!.+%$+#,+,#.;.,%,.,.>!$$;;%;%,,$*%;%+#;;+#%#>!+,.", +"##+-..+,+...+%+;$&&,*&,%,;%%%*+,%*,%%%%=-.+;##%,$$,!+!+%!,%%+#,%+;+.+;.%%;$$=+%+;,;'$,;++++..>>#%%##", +"#+%@@;++.++%,;+.%*%-$%%;%.@%*$%.,+;%++'%&;&.>%@++%$%.,%+%$###.+*+#.,%++'#%$%&$%*%$;%+.+,##++!.+#!!.%", +".-%##!@.%%$%;..;,+%$&%&%++.;+,+%%+;%;.++#$$@;#$.#%.=*$#,*+%$%+..+;;%++;;+!;$'%+'=%;;.##+.++.#+#.#.,.", +"#@!#+#@.%+%$,++,,;;,+%*&%;+%%$=#+&$+**+#,+@..#.'$,#;,*,+!%%,%.#;%$%$&;,%%,.$%.%-$;;!@$%%..+.##!+,$.$", +"..@>#@++;+;%%++..%;+!+%$;.;+'-%$+%,,=%.$+!+#@+%$>*-,%+..$=%%#.-=*%++$;%&%%&$.;%'%%.+%;.&,+#!!#+*;$*&", +">@%+.;%%#+$+*$++$;*.+%%%+,+%*&$&,+;',@=%+$#.%*..&''&.+#.;$&$;%&&.$=&$,$%=*+.&&,.+.+;,.;;%.#.++,+''&.", +"!+..$&+%$;.,++%,%%%,#,+.%+%$&,$*.+&.+$+%$.@&&+#%-=,,#;%%.#.$&%&+.%=','*$++.%%%.+.+%;++%,,#+,*.;%..@@", +"$;+%,.&=-;++@..$$,%+!!%%.%$;$;;==%#.$$$$##;++#+$=$...;%*+%+.,%%$&$*%$==,#$&$$+,++,%;%++;%+%-$++.+!@%", +".%;++.-*$%%;+.+$+;..++...,;*+%;%%.!$;+,++%+.+%;.;,,.;+%$,&&.#%&'-&%,=-,+%&&,+#+;;+,.+#+++,'%.;.##+$#", +"...#%%%*=%+$+,+....+,+.;!++;$$%,.+,+$,++#+#>+.$++.$#++$%%$%+*&+,%=%==+%'=%%+.,.&%%+%#.%%,%%%.;++%%;&", +"%+.+%+*+&;$%$$.+.!#;#+%=&#@,;$..%#;++=$,.#!#+..+#,,%$&&+;;,,$-+#;+-=$$&$%++,,$$%%+%.%$,*$%%+$%;=$,*%", +".%;!.$$%#,+=$$%,!#@#;;+.%*=+#.,,.+%+.$+++%%,%.#>.;+$;$&++%'$,++.+%+,,=$'+++,*%,;+*;,%$,%+&*%++&-;%$$", +"%++;+.%+$$%%$$%,+#.%.@.%;$++*>#+,;.&$@.!#='&@+@@..*$,%%+%$%$%#;+,;#%%$;+,+%$%$%++&%&;;%;$&$*&;;%;,$%", +"%+=$#+,%&''++,.+$#.!+%%+;*%.!++*%+.',.%;!;.#.;#@+%%$;..%-%%*,;+,,%$$$.!+'$%%%$;+%%+.+++*+;$%;%,%;,;$", +"*$$,,;;$$$$++#.$+%.@%;..%+.;#+&,.+*.;..$&,!.;+;..*%;,%%+%*'*&%!.'*$%$$'..++%,;$%%++;&+,%%%$%,,..++&&", +"'&$,,+*%+%%..+;#.+#$%+.+.,+;%$.%+*$++,.%++#@#$'$.!%%;$-%%+$$-%+$,.+;&)'%;.!#,%%%++,%$%';,$%%.+%,#+&+", +"'%,%*$%%%,,..;;..,+.+,%+,+,,*,#+&%%#.#.!.;@!%%...%.&=$+&&%,%#%*,;*;*=&'$;+.##,,+.$+,*-'%%+,;.;$'%%,,", +".+,$--&$,+$&..#+%,%,$!+%++,+.%+%%$+%!#!##+++*$,@!#%-$+'%.$-+%%%&%$-**=&$%%.;%+#.;$%.$;+%,%,;;%;*$%;=", +",;&===*;%*,=,#+##%).*%>#%+$#,*=.+%$+.$#.$+@%;*%+#..#+=+@#&$-*,..'-*;-$$;$=';,++#@%%$+@+%%%$$,+&&+;$#", +";'=$+$;;)%=,%+..%+#,,%&$+#,.;$$.,+#;#+,$%##;;;%&+;#$&+.$;%%-&;.;&;-;$$=$*-$,.,;;.#!.,.,$%,,,%&*;%;..", +"*)=)&%.#'-&,+%%#;;+>.$,+.*%#+!,,&,@;,,,.@=%&';,+.,;+.>.;&%=+*,+&,%;*;%%'-$++%.%,.%%+&$;%%,%.%=.=*%%*", +".&&=-%%+.$,#,=;%#,@.+,%>,+=-++@@#%$.++#,--;.=*+,,>.+@.##$%%$$$&*=..%$*%-;*>,%$%#>,$,%%*%*.#*%++$;+,,", +"@@$'.;'..;@.+$%)&!!,.!>+%@%-*%@>#.+&&+.+$%,++$$#++%%.&$@.#,*%*&'%%&$;,*%%,&%;%,#..;$$%';+.+,++.%.;+@", +"#+$%;,$.+@,+.%-%%-%>#+.#+$%...%,!>>;$$$.%$+#+*++&*,.$&$+#+.%++$%,=$;$&'+.%%&-+++;%;;$%+%+;.#.+@!!..>", +"#,++;$%;++#.+++%&*=#.+;%>@+,...++$>@#&%*%;.$=$##=;,$;;;..-*!;*.+%=*%;*%%=&$+$++,-%;+++++++++++#@@@>@", +"#+*,$%;,++..@#.,+@+.#.+%#+!.,%#>%,,+>#.%$!#%=%$$#+%#$,+$$%,.++.%%,'*%;,$=-$+@,%*$+%.#+%,.++%;.,#@@@>", +"..,%$,$;..#,@.$;%.##.#!#;;++#.+%!.,%,.@##>%,>%$,+,+*'%$)&,+,+,+*%,,$,+,*-&++$%%*%#@#%$;+.%,!.+;+!@+>", +",;,%.%.+++.#;%+.,+#.,#+;#%.@,+#.+,#!,+.>#..,,++++.,,$$$%%%';+$%,$!,$,;;,..,&)%%+#!+..,++%%++#+#@+,##", +".%;+,#+$%.;,....+.++.+,$$.#>.$+@&+#;.@@.%!@.#+@!,%;=-;##%=$%,.$%%+;%%%$+.%&%%%,.,%++..+++;%;;.+#.+%.", +"..+;%,%,,%+,.,##.+%%+@,'%#.++,,,%++.>++.>#+#>>.%,%-$$&+%.,#;;&$$,*$+%%.#*='$,,+.;,+,##...$%++%.,++%,", +"..+%+&$.+*;,++.+;+*%+#+++...+;.%+##@.%@>#.!>#&%+=$,!.;$$$#.;%'-'&*+++.$=*%;,++;;;.+;++,+++@@%++++..$", +",,*,+'+,;;#.$%#%+%,.%&+#.$+@!%;,+##++#@!!#@@&%@..+...#%%,,;=-%$=%%@+.;==;=+#+%+*$#.%+%+@.++!,%,;;%..", +"#;.+$#%$$#;.%;+..+,.%,%+,..@++..*+#,+.#>@#.+++!.+.+$=%%$#,$--$=$%+#,=%%,%;$;%.+.,,+!++#..++%;.%%$..,", +",;.*;.+&.#%*.>.,.#$'+.%%,,%##++;+,#*.@>#%%#@+%$@@...+'=;%$,+,%$*&%&=-;+#+%==..;,#,#.###@+$%,,$$;+@#.", +"#$$%,%;,%+=$+#.!...+%,%++%;&+#$%.%#@,;+#.,#,+.+%;,..;+.$%=$,##,,'=&$$%%++$$%,,;$;.,##.+#;++;#+;+.>..", +"#..'%;%+;%*%;+,!@..#,;.%%,+&,*$.,%@>!+;,###&;;#.;%%;+@++*$%%$+@,*&$$*&;%%.#,.$$,++#@@#.,+%.;%#+!#.,!", +"%*#%$$;+*.+,$%%@.+@..+,$%++$&%#+,.#,#@#.&$$++,+.,,$$;+.,.;%=$$$,+%+$,&$%,,.+=$%.,;##!#.#$%.+%,+.@.,#", +"%*$%'*&++.;.+;.#+;#>++,+*%%%++,,.#;=@@.+,;$&;%;%$-,%+.+$.;&;%+,$,+,+%%**$+*$+,%+%,$%.%;@+%##;;;;#+##", +";,&$-%,;,++%..#+;++$+#,++,=%%+++!#++..+;;,%,%=&+..%'.+,'*;;;#+;&$#$$,%*,$=%,+,,%%$%%%+%!@++#;++&+.,.", +"=.&&,%$#.%;%!@,+,%,,+++.+.*%%;$.%+#!+&,;,;+,%+$=,$%,;+%&$$+#%%++%;$&&%%%'%,,+++%,%;*.+%%>..+,+##+%+;", +"%&*,,%.$,;&#!.%%%%++,.%,;+,%.#.$;++;%;++#,$=.++,$=%#.&%,-;+$+.,++$*$%&=$,,;#,.,&;&.,+%++;%.+%,+!#.;%", +"%+$+;,,#.;.@,%.+,+;,#++,$$+++.++,+#%%$%##.,++*$+;.;%;%$,$%=%#.@,,$*%*=%;%%+%$.%&'+%$#.=,++&;$>#*..+,", +",!;#;$,#,#++,%.%.,.+;.$%.,++%%$+,#%,+,$,.%.%$+.$,;$+%$;.,*$#+%.!..,%=*$&$;;%$%$+,$$%%%'++,+,.@#+.,$.", +"!$'#>+&,+#,+$*+.;+#,%%;,.!+,+$$%+.+++*+..+,+%,#+.=-%@#..%$+;%+;,%.+,$$'$*+%%;,%$$%%+;;,&'%#.##...$#.", +"$%.!.%$%%+#*;;&,#++%,,+,;#++$$%++,%,%%++!.+@.;*#,*;%;###+@!;+$$;,$.+.%$$%.%.$*%&&,.,,;+;$&$+.+..$+#+", +"++>+=#%;+,++,++.%..+$;.+,+$%%+,.;&,#+,*.++##@;%,%-*%%+@%+#,...,$;,'$++,;#&,,&'=&'*+++.%$-=$$%!..#,%%", +"@@$;#.*%..,+,#@..+,$%@,,.&%$;,%%$%,+%+%+&,@.##.+**%$%#,.++$+#+%%+$'%&.,;#+$&$&&%-;,$.!+=*&=*.#;#.,%,", +"+'..,$%;!%$##@+#..$%.%;,,%$%%;==..$%%,+,++$;@.&+>#%*..+%.;+#,+.$$,;*$$%+;+%%$,%'%;%*%.;%+$=,%*$++,+>", +"&=..%$$*...+,.;.#%.#+.@$%%.,$-&,,,%;*#%;#$,+;#+#@.%@@%'+$+.%.#.=&,%%%$';%&%$+.%&.%**+=$+#%.%%';%*$>.", +"&!.+%$+#,+,#.;.,%,.,.>!$$;;%;%,,$*%;%+#;;+#%#>!+,.!@.;=,.;$@.;+.$''%%=.;=;+%*,..;=&%=$+,&,+%,+;%$;,.", +",!+!+%!,%%+#,%+;+.+;.%%;$$=+%+;,;'$,;++++..>>#%%####+-..+,+...+%+;$&&,*&,%,;%%%*+,%*,%%%%=-.+;##%,$$", +"$%.,%+%$###.+*+#.,%++'#%$%&$%*%$;%+.+,##++!.+#!!.%#+%@@;++.++%,;+.%*%-$%%;%.@%*$%.,+;%++'%&;&.>%@++%", +".=*$#,*+%$%+..+;;%++;;+!;$'%+'=%;;.##+.++.#+#.#.,..-%##!@.%%$%;..;,+%$&%&%++.;+,+%%+;%;.++#$$@;#$.#%", +"#;,*,+!%%,%.#;%$%$&;,%%,.$%.%-$;;!@$%%..+.##!+,$.$#@!#+#@.%+%$,++,,;;,+%*&%;+%%$=#+&$+**+#,+@..#.'$,", +"-,%+..$=%%#.-=*%++$;%&%%&$.;%'%%.+%;.&,+#!!#+*;$*&..@>#@++;+;%%++..%;+!+%$;.;+'-%$+%,,=%.$+!+#@+%$>*", +"'&.+#.;$&$;%&&.$=&$,$%=*+.&&,.+.+;,.;;%.#.++,+''&.>@%+.;%%#+$+*$++$;*.+%%%+,+%*&$&,+;',@=%+$#.%*..&'", +",,#;%%.#.$&%&+.%=','*$++.%%%.+.+%;++%,,#+,*.;%..@@!+..$&+%$;.,++%,%%%,#,+.%+%$&,$*.+&.+$+%$.@&&+#%-=", +"...;%*+%+.,%%$&$*%$==,#$&$$+,++,%;%++;%+%-$++.+!@%$;+%,.&=-;++@..$$,%+!!%%.%$;$;;==%#.$$$$##;++#+$=$", +",.;+%$,&&.#%&'-&%,=-,+%&&,+#+;;+,.+#+++,'%.;.##+$#.%;++.-*$%%;+.+$+;..++...,;*+%;%%.!$;+,++%+.+%;.;,", +"$#++$%%$%+*&+,%=%==+%'=%%+.,.&%%+%#.%%,%%%.;++%%;&...#%%%*=%+$+,+....+,+.;!++;$$%,.+,+$,++#+#>+.$++.", +",%$&&+;;,,$-+#;+-=$$&$%++,,$$%%+%.%$,*$%%+$%;=$,*%%+.+%+*+&;$%$$.+.!#;#+%=&#@,;$..%#;++=$,.#!#+..+#,", +"+$;$&++%'$,++.+%+,,=$'+++,*%,;+*;,%$,%+&*%++&-;%$$.%;!.$$%#,+=$$%,!#@#;;+.%*=+#.,,.+%+.$+++%%,%.#>.;", +"*$,%%+%$%$%#;+,;#%%$;+,+%$%$%++&%&;;%;$&$*&;;%;,$%%++;+.%+$$%%$$%,+#.%.@.%;$++*>#+,;.&$@.!#='&@+@@..", +"%$;..%-%%*,;+,,%$$$.!+'$%%%$;+%%+.+++*+;$%;%,%;,;$%+=$#+,%&''++,.+$#.!+%%+;*%.!++*%+.',.%;!;.#.;#@+%", +"%;,%%+%*'*&%!.'*$%$$'..++%,;$%%++;&+,%%%$%,,..++&&*$$,,;;$$$$++#.$+%.@%;..%+.;#+&,.+*.;..$&,!.;+;..*", +"%%;$-%%+$$-%+$,.+;&)'%;.!#,%%%++,%$%';,$%%.+%,#+&+'&$,,+*%+%%..+;#.+#$%+.+.,+;%$.%+*$++,.%++#@#$'$.!", +".&=$+&&%,%#%*,;*;*=&'$;+.##,,+.$+,*-'%%+,;.;$'%%,,'%,%*$%%%,,..;;..,+.+,%+,+,,*,#+&%%#.#.!.;@!%%...%", +"%-$+'%.$-+%%%&%$-**=&$%%.;%+#.;$%.$;+%,%,;;%;*$%;=.+,$--&$,+$&..#+%,%,$!+%++,+.%+%%$+%!#!##+++*$,@!#", +".#+=+@#&$-*,..'-*;-$$;$=';,++#@%%$+@+%%%$$,+&&+;$#,;&===*;%*,=,#+##%).*%>#%+$#,*=.+%$+.$#.$+@%;*%+#.", +"#$&+.$;%%-&;.;&;-;$$=$*-$,.,;;.#!.,.,$%,,,%&*;%;..;'=$+$;;)%=,%+..%+#,,%&$+#,.;$$.,+#;#+,$%##;;;%&+;", +";+.>.;&%=+*,+&,%;*;%%'-$++%.%,.%%+&$;%%,%.%=.=*%%**)=)&%.#'-&,+%%#;;+>.$,+.*%#+!,,&,@;,,,.@=%&';,+.,"};