[gnome-shell-extensions/wip/apps-menu] apps-menu: Replace it with a new version based on AxeMenu



commit 613ef0d61264db29d94a880b342550adcc791a3a
Author: Debarshi Ray <debarshir gnome org>
Date:   Fri Jan 4 18:31:57 2013 +0100

    apps-menu: Replace it with a new version based on AxeMenu

 extensions/apps-menu/Makefile.am                   |    1 +
 extensions/apps-menu/extension.js                  |  718 ++++++++++++++++++--
 extensions/apps-menu/metadata.json.in              |    1 +
 ...gnome.shell.extensions.apps-menu.gschema.xml.in |    8 +
 extensions/apps-menu/stylesheet.css                |  214 ++++++-
 5 files changed, 889 insertions(+), 53 deletions(-)
---
diff --git a/extensions/apps-menu/Makefile.am b/extensions/apps-menu/Makefile.am
index 86431d7..94ebc22 100644
--- a/extensions/apps-menu/Makefile.am
+++ b/extensions/apps-menu/Makefile.am
@@ -1,3 +1,4 @@
 EXTENSION_ID = apps-menu
 
 include ../../extension.mk
+include ../../settings.mk
diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js
index faf099f..6098cf4 100644
--- a/extensions/apps-menu/extension.js
+++ b/extensions/apps-menu/extension.js
@@ -1,111 +1,725 @@
-/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
-
+/**TODO:
+    1. Activites button position
+ */
+const Version = '0.8.3';
+const ShellVersion = imports.misc.config.PACKAGE_VERSION.split(".");
+const Mainloop = imports.mainloop;
 const GMenu = imports.gi.GMenu;
 const Lang = imports.lang;
 const Shell = imports.gi.Shell;
 const St = imports.gi.St;
-
+const Clutter = imports.gi.Clutter;
 const Main = imports.ui.main;
 const PanelMenu = imports.ui.panelMenu;
 const PopupMenu = imports.ui.popupMenu;
+const ModalDialog = imports.ui.modalDialog;
+const AppFavorites = imports.ui.appFavorites;
+const Gtk = imports.gi.Gtk;
+const Gio = imports.gi.Gio;
+const GnomeSession = imports.misc.gnomeSession;
+const Cairo = imports.cairo;
+const GLib = imports.gi.GLib;
+const Signals = imports.signals;
+const Layout = imports.ui.layout;
+const Gettext = imports.gettext;
+const Pango = imports.gi.Pango;
 
-const ICON_SIZE = 28;
+const _ = imports.gettext.domain('axemenu').gettext;
 
-const AppMenuItem = new Lang.Class({
-    Name: 'AppsMenu.AppMenuItem',
-    Extends: PopupMenu.PopupBaseMenuItem,
+const ExtensionUtils = imports.misc.extensionUtils;
+const Me = ExtensionUtils.getCurrentExtension();
+const Convenience = Me.imports.convenience;
 
-    _init: function (app, params) {
-        this.parent(params);
+let appsys = Shell.AppSystem.get_default();
+let _session = new GnomeSession.SessionManager();
 
-        this._app = app;
-        this.label = new St.Label({ text: app.get_name() });
-        this.addActor(this.label);
-        this._icon = app.create_icon_texture(ICON_SIZE);
-        this.addActor(this._icon, { expand: false });
-    },
+//Why are functions renames without creating a deprecated pointer..?
+const cleanActor = (ShellVersion[1]<4) ? function(o) {return o.destroy_children();} : function(o) {return o.destroy_all_children();};
+const TextDirection = (ShellVersion[1]<4) ? St.TextDirection.LTR : Clutter.TextDirection.LTR ;
+const getTextDirection = (ShellVersion[1]<4) ? function(actor) {return actor.get_direction();} : function(actor) {return actor.get_text_direction();};
 
-    activate: function (event) {
-        this._app.activate_full(-1, event.get_time());
+function fixMarkup(text, allowMarkup) {
+    if (allowMarkup) {
+        let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&amp;');
+        _text = _text.replace(/<(?!\/?[biu]>)/g, '&lt;');
+        try {
+            Pango.parse_markup(_text, -1, '');
+            return _text;
+        } catch (e) {}
+    }
+    return GLib.markup_escape_text(text, -1);
+}
 
-        this.parent(event);
+function ApplicationButton(app,iconsize) {
+    this._init(app,iconsize);
+}
+ApplicationButton.prototype = {
+    _init: function(app,iconsize) {
+        this.app = app;
+        let app_name = fixMarkup(this.app.get_name())
+        this.actor = new St.Button({ reactive: true, label: app_name, style_class: 'application-button', x_align: St.Align.START });
+        this.actor._delegate = this;
+        this.buttonbox = new St.BoxLayout();
+        let labelclass = AppFavorites.getAppFavorites().isFavorite(app.get_id())?'application-button-label-favorites':'application-button-label';
+        this.label = new St.Label({ text: this.app.get_name(), style_class: labelclass });
+        this.icon = this.app.create_icon_texture(iconsize);
+        this.buttonbox.add_actor(this.icon);
+        this.buttonbox.add(this.label, { y_align: St.Align.MIDDLE, y_fill: false });
+        this.actor.set_child(this.buttonbox);
+        this._clickEventId = this.actor.connect('clicked', Lang.bind(this, function() {
+            this.app.open_new_window(-1);
+            appsMenuButton._select_category(null, null);
+            appsMenuButton.menu.close();
+        }));
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+    },
+    _onDestroy : function() {
+        if (this._clickEventId) this.actor.disconnect(this._clickEventId);
     }
+};
+Signals.addSignalMethods(ApplicationButton.prototype);
 
-});
+function BaseButton(label,icon,iconsize,onclick) {
+    this._init(label,icon,iconsize,onclick);
+}
+BaseButton.prototype = {
+    _init: function(label,icon,iconsize,onclick) {
+        this.actor = new St.Button({ reactive: true, label: label, style_class: 'application-button am-'+icon+'-button', x_align: St.Align.START });
+        this.actor._delegate = this;
+        this.buttonbox = new St.BoxLayout();
+        if(icon){
+            this.icon = new St.Icon({icon_name: icon, icon_size: iconsize});
+            this.buttonbox.add_actor(this.icon);
+        }
+        if(label){
+            this.label = new St.Label({ text: label, style_class: 'application-button-label' });
+            this.buttonbox.add(this.label, { y_align: St.Align.MIDDLE, y_fill: false });
+        }
+        this.actor.set_child(this.buttonbox);
+        this._clickEventId = this.actor.connect('clicked', Lang.bind(this, onclick));
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+    },
+    _onDestroy : function() {
+        if (this._clickEventId)
+            this.actor.disconnect(this._clickEventId);
+    }
+};
+Signals.addSignalMethods(BaseButton.prototype);
 
-const ApplicationsButton = new Lang.Class({
-    Name: 'AppsMenu.ApplicationsButton',
-    Extends: PanelMenu.SystemStatusButton,
+function CategoryButton(parent,category) {
+    this._init(parent,category);
+}
+CategoryButton.prototype = {
+    _init: function(parent,category) {
+        var label;
+        this._parent = parent;
+        this.category = category;
+        if (category){
+           this.menu_id = this.category.get_menu_id();
+           label = category.get_name();
+        }else{
+            label = _("All");
+            this.menu_id = '';
+        }
+        this.actor = new St.Button({ reactive: true, label: label, style_class: 'category-button', x_align: St.Align.START });
+        this.actor._delegate = this;
+        this.buttonbox = new St.BoxLayout();
+        this.label = new St.Label({ text: label, style_class: 'category-button-label' });
+        this.buttonbox.add(this.label, { y_align: St.Align.MIDDLE, y_fill: false });
+        this.actor.set_child(this.buttonbox);
 
-    _init: function() {
-        this.parent('start-here-symbolic');
+        this._clickEventId = this.actor.connect('clicked', Lang.bind(this, function() {
+            this._parent._select_category(this.category, this);
+            this._parent.cm.set_val('category_menu_id', this.menu_id);
+            this._parent._scrollToCatButton(this);
+            this._parent.selectedAppTitle.set_text("");
+            this._parent.selectedAppDescription.set_text("");
+        }));
+        this._parent._addEnterEvent(this, Lang.bind(this, function() {
+            this._parent._select_category(this.category, this);
+            this._parent.cm.set_val('category_menu_id', this.menu_id);
+            this._parent.selectedAppTitle.set_text("");
+            this._parent.selectedAppDescription.set_text("");
+        }));
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
+    },
+    _onDestroy : function() {
+        if (this._clickEventId)
+            this.actor.disconnect(this._clickEventId);
+    }
+};
 
-        this._appSys = Shell.AppSystem.get_default();
-        this._installedChangedId = this._appSys.connect('installed-changed', Lang.bind(this, this._refresh));
+Signals.addSignalMethods(CategoryButton.prototype);
 
-        this._display();
+function ConfigManager(parent) {
+    this._init(parent);
+}
+ConfigManager.prototype = {
+    _init: function(parent) {
+        this.config_file = this._initConfigFile();
+        this._conf = {};
+        this.parent = parent;
+    },
+    get_val: function(key, defaultValue) {
+        return (this._conf[key]==undefined)?defaultValue:this._conf[key];
+    },
+    get_enum: function(key, defaultValue) {
+        let res;
+        try {
+            res = this._conf[key].split(',');
+        } catch (e) {
+            res = defaultValue;
+        }
+        return res;
+    },
+    set_val: function(key,value) {
+        this._conf[key] = value;
+    },
+    _initConfigFile: function(){
+        let filename;
+        if (!GLib.file_test(GLib.get_home_dir() + '/.config', GLib.FileTest.EXISTS)) {
+            filename = GLib.get_home_dir() + '/.axemenu.conf';
+        }else{
+            filename = GLib.get_home_dir() + '/.config/axemenu.conf';
+        }
+        if (!GLib.file_test(filename, GLib.FileTest.EXISTS)) {
+            this._createDefaultConfig(filename);
+        }
+        return filename;
+    },
+    _createDefaultConfig: function(filename){
+        let default_content = "{}";
+        GLib.file_set_contents(filename, default_content, default_content.length);
+    },
+    implode:function( glue, pieces ) {
+        return ( ( pieces instanceof Array ) ? pieces.join ( glue ) : pieces );
+    },
+    loadConfig: function() {
+        let data = GLib.file_get_contents(this.config_file)[1].toString();
+        this._conf = JSON.parse(data);
+        Main.panel._leftBox.remove_actor(activitiesButton.container);
+        this.button_label = decodeURIComponent(this.get_val('button_label', _("Applications")));
+        this.display_icon = this.get_val('display_icon', false);
+        this.is_hot_corner = this.get_val('is_hot_corner', true);
+        if(!this.is_hot_corner){
+            this.parent._hotCorner.actor.hide();
+            activitiesButton.hotCorner.actor.show();
+        }else{
+            this.parent._hotCorner.actor.show();
+            activitiesButton.hotCorner.actor.hide();
+        }
+        this.icon_name = this.get_val('icon_name', 'start-here-symbolic');
+        this.parent._icon.set_icon_name(this.icon_name);
+        if(!this.display_icon)
+            this.parent._iconBox.hide();
+        else
+            this.parent._iconBox.show();
+        if(this.button_label!=''){
+            this.parent._label.set_text(this.button_label);
+            this.parent._label.show();
+        }else{
+            this.parent._label.hide();
+        }
+        
+        this.main_icon_size = this.get_val('main_icon_size', 18);
+        this.parent._icon.set_icon_size(this.main_icon_size);
+        this.main_box_width = this.get_val('main_box_width', 705);
+        this.show_bottom_pane = this.get_val('show_bottom_pane', true);
+        this.category_with_scroll = this.get_val('category_with_scroll', false);
+        this.category_icon_size = this.get_val('category_icon_size',24);
+        this.application_icon_size = this.get_val('application_icon_size',32);
+        this.categories_box_width = this.get_val('categories_box_width',180);
+        this.smart_height = this.get_val('smart_height', true);
+        this.axe_in_hotcorner = this.get_val('axe_in_hotcorner', false);
+    },
+    saveConfig: function() {
+        GLib.file_set_contents(this.config_file, JSON.stringify(this._conf), -1);
     },
+    resetToDefault: function() {
+        GLib.file_set_contents(this.config_file, '{}', -1);
+        this.parent.reDisplay();
+    },
+    destroy: function() {
 
+    }
+};
+
+function AxeButton(menuAlignment) {
+    this._init(menuAlignment);
+}
+AxeButton.prototype = {
+    __proto__: PanelMenu.ButtonBox.prototype,
+    _init: function(menuAlignment) {
+        PanelMenu.ButtonBox.prototype._init.call(this, { reactive: true, can_focus: true, track_hover: true });
+        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
+        this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
+        this._menuAlignment = menuAlignment;
+        this._resetMenu();
+        global.display.add_keybinding('menu-toggle', Convenience.getSettings(), 0, function() {
+            appsMenuButton.toggleMenu();
+        });
+    },
+    toggleMenu: function(){
+        if (!this.menu.isOpen) {
+            let monitor = Main.layoutManager.primaryMonitor;
+            this.menu.actor.style = ('max-height: ' + Math.round(monitor.height - Main.panel.actor.height-80) + 'px;');
+            if (Main.overview.visible)
+                Main.overview.hide();
+        }else{
+            //this.reloadFlag = false;
+            //this.cm.saveConfig();
+            this._select_category(null, null);
+        }
+        this.menu.toggle();
+    },
+    _resetMenu: function(){
+        this.menu = new PopupMenu.PopupMenu(this.actor, this._menuAlignment, St.Side.TOP);
+        this.menu.actor.add_style_class_name('application-menu-background');
+        this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
+        Main.uiGroup.add_actor(this.menu.actor);
+        this.menu.actor.hide();
+        Main.panel.menuManager.addMenu(this.menu);
+    },
+    _onButtonPress: function(actor, event) {
+        this.toggleMenu();
+    },
+    _onSourceKeyPress: function(actor, event) {
+        let symbol = event.get_key_symbol();
+        if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
+            this.menu.toggle();
+            return true;
+        } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
+            this.menu.close();
+            return true;
+        } else if (symbol == Clutter.KEY_Down) {
+            if (!this.menu.isOpen)
+                this.menu.toggle();
+            this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
+            return true;
+        } else
+            return false;
+    },
+    _onOpenStateChanged: function(menu, open) {
+        if (open)
+            this.actor.add_style_pseudo_class('active');
+        else
+            this.actor.remove_style_pseudo_class('active');
+    },
     destroy: function() {
-        this._appSys.disconnect(this._installedChangedId);
+        this.actor._delegate = null;
+        this._monitor.disconnect(this._monitorChangedId);
+        this.menu.actor.get_children().forEach(function(c) { c.destroy() });
+        this.menu.destroy();
+        if(ShellVersion[1]<4)
+            global.window_manager.disconnect(this._keyBindingId);
+        else
+            global.display.remove_keybinding('menu-toggle');
+        this.actor.destroy();
+    }
+};
+Signals.addSignalMethods(AxeButton.prototype);
 
-        this.parent();
+function HotCorner(parent) {
+    this._init(parent);
+}
+HotCorner.prototype = {
+    __proto__: Layout.HotCorner.prototype,
+    _init : function(parent) {
+        this._parent = parent;
+        Layout.HotCorner.prototype._init.call(this);
     },
+    _onCornerEntered : function() {
+        log("onCornerEntered");
+        if (!this._entered) {
+            this._entered = true;
+            if (!Main.overview.animationInProgress) {
+                this._activationTime = Date.now() / 1000;
+                this.rippleAnimation();
+                if(!this._parent.cm.axe_in_hotcorner){
+                    Main.overview.toggle();
+                }else{
+                    this._parent.toggleMenu();
+                }
+            }
+        }
+        return false;
+    }
+};
 
-    _refresh: function() {
-        this._clearAll();
+function ApplicationsButton() {
+    this._init();
+}
+ApplicationsButton.prototype = {
+    __proto__: AxeButton.prototype,
+    _init: function() {
+        AxeButton.prototype._init.call(this, 1);
+        let container = new Shell.GenericContainer();
+        container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
+        container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
+        container.connect('allocate', Lang.bind(this, this._containerAllocate));
+        this.actor.add_actor(container);
+        this._box = new St.BoxLayout({ name: 'axeMenu' });
+        this._iconBox = new St.Bin();
+        this._box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
+        this._icon = new St.Icon({ icon_name: 'start-here-symbolic', icon_size: 18, style_class: 'axemenu-icon' });
+        this._iconBox.child = this._icon;
+        this._label = new St.Label({ track_hover: true, style_class: 'application-menu-button-label'});
+        this._box.add(this._label, { y_align: St.Align.MIDDLE, y_fill: false });
+        this._label.set_text(_("Applications"));
+        container.add_actor(this._box);
+        this._hotCorner = new HotCorner(this);
+        container.add_actor(this._hotCorner.actor);
+        this._selectedItemIndex = null;
+        this._previousSelectedItemIndex = null;
+        this._activeContainer = null;
+        this.cm = new ConfigManager(this);
+        this.reloadFlag = true;
+        this._createLayout();
         this._display();
+        _installedChangedId = appsys.connect('installed-changed', Lang.bind(this, this.reDisplay));
+        this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateToggled));
+        this._monitor = Gio.file_new_for_path(this.cm.config_file).monitor(Gio.FileMonitorFlags.NONE, null);
+        this._monitorChangedId = this._monitor.connect('changed', Lang.bind(this,this.reDisplay));
     },
-
-    _clearAll: function() {
-        this.menu.removeAll();
+    _containerGetPreferredWidth: function(actor, forHeight, alloc) {
+        [alloc.min_size, alloc.natural_size] = this._box.get_preferred_width(forHeight);
+    },
+    _containerGetPreferredHeight: function(actor, forWidth, alloc) {
+        [alloc.min_size, alloc.natural_size] = this._box.get_preferred_height(forWidth);
     },
+    _containerAllocate: function(actor, box, flags) {
+        this._box.allocate(box, flags);
+        let primary = Main.layoutManager.primaryMonitor;
+        let hotBox = new Clutter.ActorBox();
+        let ok, x, y;
+        if (getTextDirection(actor) == TextDirection) {
+            [ok, x, y] = actor.transform_stage_point(primary.x, primary.y);
+        } else {
+            [ok, x, y] = actor.transform_stage_point(primary.x + primary.width, primary.y);
+        }
+        hotBox.x1 = Math.round(x);
+        hotBox.x2 = hotBox.x1 + this._hotCorner.actor.width;
+        hotBox.y1 = Math.round(y);
+        hotBox.y2 = hotBox.y1 + this._hotCorner.actor.height;
+        this._hotCorner.actor.allocate(hotBox, flags);
+    },
+    _createActivitiesButton: function()
+    {
+        let buttonContainer = new St.BoxLayout({style: "padding: 10px 0 0 10px;"});
+        let button = new BaseButton(_("Activities Overview"), null, 0, function() {
+            appsMenuButton.toggleMenu();
+            Main.overview.toggle();
+        });
+        buttonContainer.add(button.actor);
+        return buttonContainer;
+    },
+    _createSeparator: function()
+    {
+        let separator = new St.DrawingArea({ style_class: 'popup-separator-menu-item' });
+        separator.connect('repaint', Lang.bind(this, this._onRepaintSeparator));
+        return separator;
+    },
+    _onRepaintSeparator: function(area){
+        let cr = area.get_context();
+        let themeNode = area.get_theme_node();
+        let [width, height] = area.get_surface_size();
+        let margin = themeNode.get_length('-margin-horizontal');
+        let gradientHeight = themeNode.get_length('-gradient-height');
+        let startColor = themeNode.get_color('-gradient-start');
+        let endColor = themeNode.get_color('-gradient-end');
+        let gradientWidth = (width - margin * 2);
+        let gradientOffset = (height - gradientHeight) / 2;
+        let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
+        pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
+        pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
+        pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
+        cr.setSource(pattern);
+        cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
+        cr.fill();
+    },
+    _addEnterEvent: function(button, callback) {
+        let _callback = Lang.bind(this, function() {
+            let parent = button.actor.get_parent();
+            if (this._activeContainer === this.categoriesBox && parent !== this._activeContainer) {
+                this._previousSelectedItemIndex = this._selectedItemIndex;
+            }
+            this._activeContainer = parent;
+            let children = this._activeContainer.get_children();
+            for (let i=0, l=children.length; i<l; i++) {
+                if (button.actor === children[i]) {
+                    this._selectedItemIndex = i;
+                }
+            };
+            callback();
+        });
+        button.connect('enter-event', _callback);
+        button.actor.connect('enter-event', _callback);
+    },
+    _clearSelections: function(container) {
+        container.get_children().forEach(function(actor) {
+        if(actor.style_class != 'popup-separator-menu-item')
+            actor.style_class = "category-button";
+        });
+    },
+    _onOpenStateToggled: function(menu, open) {
+       if (open) {
+           this._selectedItemIndex = null;
+           
+           this.categoriesApplicationsBox.show();
+           this._activeContainer = null;
 
-    // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
-    // (taken from js/ui/appDisplay.js in core shell)
-    _loadCategory: function(dir, menu) {
+           this.selectedAppTitle.set_text("");
+           this.selectedAppDescription.set_text("");
+       }
+       AxeButton.prototype._onOpenStateChanged.call(menu, open);
+    },
+    reDisplay : function(e,object,p0,p1) {
+        if(this.reloadFlag && (p1==3 || p1===undefined)){
+            this._cleanControls();
+            this._display();
+        }
+        this.reloadFlag = true;
+    },
+    _cleanControls: function(){
+        cleanActor(this.categoriesBox);
+        cleanActor(this.applicationsBox);
+    },
+    _loadCategory: function(dir) {
         var iter = dir.iter();
         var nextType;
         while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
             if (nextType == GMenu.TreeItemType.ENTRY) {
                 var entry = iter.get_entry();
-                var app = this._appSys.lookup_app_by_tree_entry(entry);
-                if (!entry.get_app_info().get_nodisplay())
-                    menu.addMenuItem(new AppMenuItem(app));
+                if (!entry.get_app_info().get_nodisplay()) {
+                    var app = appsys.lookup_app_by_tree_entry(entry);
+                    if (!this.applicationsByCategory[dir.get_menu_id()]) this.applicationsByCategory[dir.get_menu_id()] = new Array();
+                    this.applicationsByCategory[dir.get_menu_id()].push(app);
+                }
             } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
-                this._loadCategory(iter.get_directory(), menu);
+                let subdir = iter.get_directory();
+                if (subdir.get_is_nodisplay()) continue;
+                this.applicationsByCategory[subdir.get_menu_id()] = new Array();
+                this._loadCategory(subdir);
+                if (this.applicationsByCategory[subdir.get_menu_id()].length>0){
+                   let categoryButton = new CategoryButton(this,subdir);
+                   this.categoriesBox.add_actor(categoryButton.actor);
+                }
             }
         }
     },
-
+    _scrollToButton: function(button) {
+        var current_scroll_value = this.applicationsScrollBox.get_vscroll_bar().get_adjustment().get_value();
+        var box_height = this.applicationsScrollBox.get_allocation_box().y2-this.applicationsScrollBox.get_allocation_box().y1;
+        var new_scroll_value = current_scroll_value;
+        if (current_scroll_value > button.actor.get_allocation_box().y1-10) new_scroll_value = button.actor.get_allocation_box().y1-10;
+        if (box_height+current_scroll_value < button.actor.get_allocation_box().y2+10) new_scroll_value = button.actor.get_allocation_box().y2-box_height+10;
+        if (new_scroll_value!=current_scroll_value) this.applicationsScrollBox.get_vscroll_bar().get_adjustment().set_value(new_scroll_value);
+    },
+    _scrollToCatButton: function(button) {
+        var current_scroll_value = this.categoriesScrollBox.get_vscroll_bar().get_adjustment().get_value();
+        var box_height = this.categoriesScrollBox.get_allocation_box().y2-this.categoriesScrollBox.get_allocation_box().y1;
+        var new_scroll_value = current_scroll_value;
+        if (current_scroll_value > button.actor.get_allocation_box().y1-10) new_scroll_value = button.actor.get_allocation_box().y1-10;
+        if (box_height+current_scroll_value < button.actor.get_allocation_box().y2+10) new_scroll_value = button.actor.get_allocation_box().y2-box_height+10;
+        if (new_scroll_value!=current_scroll_value) this.categoriesScrollBox.get_vscroll_bar().get_adjustment().set_value(new_scroll_value);
+    },
+    _createLayout: function() {
+        let section = new PopupMenu.PopupMenuSection();
+        this.menu.addMenuItem(section);
+        this.rightPane = new St.BoxLayout({ style_class: 'rightpane-box', vertical: true });
+        this.categoriesApplicationsBox = new St.BoxLayout({ style_class: 'categories-app-box'});
+        this.rightPane.add(this.categoriesApplicationsBox, { expand: true,x_fill: true,y_fill: true });
+        this.categoriesBox = new St.BoxLayout({ style_class: 'categories-box', vertical: true });
+        this.applicationsScrollBox = new St.ScrollView({ x_fill: true, y_fill: false, y_align: St.Align.START, style_class: 'vfade applications-scrollbox' });
+        let vscroll = this.applicationsScrollBox.get_vscroll_bar();
+        vscroll.connect('scroll-start', Lang.bind(this, function() {
+                              this.menu.passEvents = true;
+                          }));
+        vscroll.connect('scroll-stop', Lang.bind(this, function() {
+                              this.menu.passEvents = false;
+                          }));
+        this.categoriesScrollBox = new St.ScrollView({ x_fill: true, y_fill: false, y_align: St.Align.START, style_class: 'vfade categories-scrollbox' });
+        vscroll = this.categoriesScrollBox.get_vscroll_bar();
+        vscroll.connect('scroll-start', Lang.bind(this, function() {
+                              this.menu.passEvents = true;
+                          }));
+        vscroll.connect('scroll-stop', Lang.bind(this, function() {
+                              this.menu.passEvents = false;
+                          }));
+        this.applicationsBox = new St.BoxLayout({ style_class: 'applications-box', vertical:true });
+        this.applicationsScrollBox.add_actor(this.applicationsBox);
+        this.categoriesScrollBox.add_actor(this.categoriesBox, { expand: true,x_fill: false });
+        this.applicationsScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
+        this.categoriesApplicationsBox.add(this.categoriesScrollBox, { expand: false,x_fill: true,y_fill: false, y_align: St.Align.START });
+        this.categoriesApplicationsBox.add(this.applicationsScrollBox, { expand: true,x_fill: true,y_fill: true });
+        this.mainBox = new St.BoxLayout({ style_class: 'main-box', vertical:false });
+        this.mainBox.add(this.rightPane, { expand: true,x_fill: true,y_fill: true });
+        section.actor.add_actor(this.mainBox);
+        this.selectedAppBox = new St.BoxLayout({ style_class: 'selected-app-box', vertical: true });
+        this.selectedAppTitle = new St.Label({ style_class: 'selected-app-title', text: "" });
+        this.selectedAppBox.add_actor(this.selectedAppTitle);
+        this.selectedAppDescription = new St.Label({ style_class: 'selected-app-description', text: "" });
+        this.selectedAppBox.add_actor(this.selectedAppDescription);
+        this.settingsAndselectedAppBox = new St.BoxLayout();
+        this.settingsAndselectedAppBox.add(this._createActivitiesButton(), { expand: false, x_fill: false, y_fill: false, y_align: St.Align.END });
+        this.settingsAndselectedAppBox.add(this.selectedAppBox, { expand: true, x_fill: true, y_fill: true });
+        section.actor.add_actor(this.settingsAndselectedAppBox);
+    },
     _display : function() {
-        let tree = this._appSys.get_tree();
-        let root = tree.get_root_directory();
+        this.cm.loadConfig();
+        this._activeContainer = null;
+        this._applicationsButtons = new Array();
+        this.categoriesScrollBox.style=('width: '+this.cm.categories_box_width+'px;');
+        this.mainBox.style=('width: '+this.cm.main_box_width+'px;');
+        if(this.cm.show_bottom_pane)
+            this.settingsAndselectedAppBox.show();
+        else
+            this.settingsAndselectedAppBox.hide();
+        this.categoriesApplicationsBox.hide();
 
+        //Load categories
+        this.applicationsByCategory = {};
+        let tree = appsys.get_tree();
+        let root = tree.get_root_directory();
+        let categoryButton = new CategoryButton(this,null);
+        this.categoriesBox.add_actor(categoryButton.actor);
+        this.categoriesBox.add(this._createSeparator());
         let iter = root.iter();
         let nextType;
         while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
             if (nextType == GMenu.TreeItemType.DIRECTORY) {
                 let dir = iter.get_directory();
-                let item = new PopupMenu.PopupSubMenuMenuItem(dir.get_name());
-                this._loadCategory(dir, item.menu);
-                this.menu.addMenuItem(item);
+                if (dir.get_is_nodisplay()) continue;
+                this.applicationsByCategory[dir.get_menu_id()] = new Array();
+                this._loadCategory(dir);
+                if (this.applicationsByCategory[dir.get_menu_id()].length>0){
+                   let categoryButton = new CategoryButton(this,dir);
+                   this.categoriesBox.add_actor(categoryButton.actor);
+                }
             }
         }
-    }
-});
+
+        //Load applications
+        this._displayButtons(this._listApplications(null));
+
+        let smartHeight;
+        if(this.cm.smart_height){
+            let catHeight = this.categoriesBox.height+45;
+            if(this.cm.category_with_scroll)
+                catHeight = 0;
+            smartHeight = catHeight+20+'px;';
+        }else{
+            smartHeight = 'auto;';
+        }
+        this.mainBox.style+=('height: '+smartHeight);
+    },
+    _clearApplicationsBox: function(selectedActor){
+        let actors = this.applicationsBox.get_children();
+        for (var i=0; i<actors.length; i++) {
+            let actor = actors[i];
+            this.applicationsBox.remove_actor(actor);
+        }
+        let actors = this.categoriesBox.get_children();
+        for (var i=0; i<actors.length; i++){
+            let actor = actors[i];
+            if(actor.style_class != "popup-separator-menu-item")
+                if (actor==selectedActor)
+                    actor.style_class = "category-button-selected";
+                else
+                    actor.style_class = "category-button";
+        }
+    },
+    _select_category : function(dir, categoryButton) {
+        if (categoryButton)
+            this._clearApplicationsBox(categoryButton.actor);
+        else
+            this._clearApplicationsBox(null);
+
+        if (dir)
+            this._displayButtons(this._listApplications(dir.get_menu_id()));
+        else
+            this._displayButtons(this._listApplications(null));
+    },
+    _displayButtons: function(apps){
+         if (apps){
+            for (var i=0; i<apps.length; i++) {
+               let app = apps[i];
+               if (!this._applicationsButtons[app]){
+                  let applicationButton = new ApplicationButton(app,this.cm.application_icon_size);
+                  applicationButton.actor.connect('leave-event', Lang.bind(this, function() {
+                     this.selectedAppTitle.set_text("");
+                     this.selectedAppDescription.set_text("");
+                  }));
+                  this._addEnterEvent(applicationButton, Lang.bind(this, function() {
+                      this.selectedAppTitle.set_text(applicationButton.app.get_name());
+                      if (applicationButton.app.get_description())
+                          this.selectedAppDescription.set_text(applicationButton.app.get_description());
+                      else
+                          this.selectedAppDescription.set_text("");
+                      this._clearSelections(this.applicationsBox);
+                      applicationButton.actor.style_class = "category-button-selected";
+                      this._scrollToButton(applicationButton);
+                  }));
+                  this._applicationsButtons[app] = applicationButton;
+               }
+               if (!this._applicationsButtons[app].actor.get_parent())
+                  this.applicationsBox.add_actor(this._applicationsButtons[app].actor);
+            }
+         }
+    },
+    _listApplications: function(category_menu_id, pattern){
+       var applist;
+       if (category_menu_id) applist = this.applicationsByCategory[category_menu_id];
+       else{
+          applist = new Array();
+          for (directory in this.applicationsByCategory){
+              applist = applist.concat(this.applicationsByCategory[directory]);
+          }
+       }
+       var res;
+       if (pattern){
+          res = new Array();
+          for (var i in applist){
+             let app = applist[i];
+             if (app.get_name().toLowerCase().indexOf(pattern)!=-1 || (app.get_description() && app.get_description().toLowerCase().indexOf(pattern)!=-1)) res.push(app);
+          }
+       }else res = applist;
+       res.sort(function(a,b){
+          return a.get_name().toLowerCase() > b.get_name().toLowerCase();
+       });
+       return res;
+    },
+};
 
 let appsMenuButton;
+let activitiesButton;
+let activitiesButtonLabel;
+let _installedChangedId;
+let extensionMeta,egoVersion;
 
 function enable() {
+    activitiesButton = Main.panel.statusArea['activities'];
+    activitiesButtonLabel = activitiesButton._label.get_text();
+    activitiesButton.hotCorner.actor.hide();
+    activitiesButton.container.hide();
     appsMenuButton = new ApplicationsButton();
-    Main.panel.addToStatusArea('apps-menu', appsMenuButton, 1, 'left');
+    Main.panel._leftBox.insert_child_at_index(appsMenuButton.container, 0);
+    Main.panel._axeMenu = appsMenuButton;
 }
 
 function disable() {
+    Main.panel._leftBox.remove_actor(appsMenuButton.container);
+    Main.panel.menuManager.removeMenu(appsMenuButton.menu);
+    appsys.disconnect(_installedChangedId);
     appsMenuButton.destroy();
+    activitiesButton.container.show();
+    activitiesButton.hotCorner.actor.show();
+    Main.panel._leftBox.insert_child_at_index(activitiesButton.container, 0);
 }
 
-function init() {
-    /* do nothing */
+function init(metadata) {
+    let localePath = metadata.path + '/locale';
+    extensionMeta = metadata;
+    egoVersion = ShellVersion[1]<4?metadata.version:metadata.metadata['version'];
+    Gettext.bindtextdomain('axemenu', localePath);
 }
diff --git a/extensions/apps-menu/metadata.json.in b/extensions/apps-menu/metadata.json.in
index 8d5380c..46c5759 100644
--- a/extensions/apps-menu/metadata.json.in
+++ b/extensions/apps-menu/metadata.json.in
@@ -5,6 +5,7 @@
 "gettext-domain": "@gettext_domain@",
 "name": "Applications Menu",
 "description": "Add a gnome 2.x style menu for applications",
+"original-authors": [ "e2002 bk ru", "debarshir gnome org" ],
 "shell-version": [ "@shell_current@" ],
 "url": "@url@"
 }
diff --git a/extensions/apps-menu/org.gnome.shell.extensions.apps-menu.gschema.xml.in b/extensions/apps-menu/org.gnome.shell.extensions.apps-menu.gschema.xml.in
new file mode 100644
index 0000000..4e190c4
--- /dev/null
+++ b/extensions/apps-menu/org.gnome.shell.extensions.apps-menu.gschema.xml.in
@@ -0,0 +1,8 @@
+<schemalist gettext-domain="gnome-shell-extensions">
+  <schema id="org.gnome.shell.extensions.apps-menu" path="/org/gnome/shell/extensions/apps-menu/">
+    <key name="menu-toggle" type="as">
+      <default><![CDATA[['Super_R']]]></default>
+      <_summary>Toggle AxeMenu.</_summary>
+    </key>
+  </schema>
+</schemalist>
diff --git a/extensions/apps-menu/stylesheet.css b/extensions/apps-menu/stylesheet.css
index db99e0c..83abe12 100644
--- a/extensions/apps-menu/stylesheet.css
+++ b/extensions/apps-menu/stylesheet.css
@@ -1 +1,213 @@
-/* none used*/
+.applications-menu-favorites-box {
+    padding: 15px;
+}
+.axemenu-icon {
+    padding-right: 0px;
+}
+.application-menu-button-label {
+    padding-left: 10px;
+}
+.applications-menu-favorites-button {
+    padding-top: 10px;
+    padding-left: 10px;
+    padding-right: 10px;
+    padding-bottom: 10px;
+}
+.applications-menu-favorites-button:hover {
+    background-gradient-direction: vertical;
+    background-gradient-start: rgba(255,255,255,0.2);
+    background-gradient-end: rgba(255,255,255,0.08);
+    box-shadow: inset 0px 0px 1px 1px rgba(255,255,255,0.06);
+    border-radius: 4px;
+}
+.places-box {
+    padding: 10px;
+}
+.places-button {
+    padding-top: 10px;
+    padding-left: 10px;
+    padding-right: 10px;
+    padding-bottom: 10px;
+}
+.categories-box {
+    padding-left: 5px;
+    padding-right: 5px;
+}
+.categories-scrollbox {
+    padding-top: 10px;
+    padding-right: 30px;
+    padding-bottom: 10px;
+}
+.main-box {
+    padding-top: 10px;
+    padding-left: 30px;
+    padding-right: 30px;
+    padding-bottom: 10px;
+}
+.applications-box {
+    padding-right: 10px;
+}
+.applications-scrollbox {
+    padding-top: 10px;
+    padding-bottom: 10px;
+}
+.categories-app-box {
+
+}
+.rightpane-box {
+
+}
+.favorites-button-label {
+    padding-left: 10px;
+}
+.favorites-button-box {
+    padding-left: 0px;
+}
+.application-button {
+    padding-top: 7px;
+    padding-left: 7px;
+    padding-right: 7px;
+    padding-bottom: 7px;
+}
+.application-button:hover,.category-button:hover,
+.application-button-selected {
+    padding-top: 7px;
+    padding-left: 7px;
+    padding-right: 7px;
+    padding-bottom: 7px;
+    background-gradient-direction: vertical;
+    background-gradient-start: rgba(255,255,255,0.2);
+    background-gradient-end: rgba(255,255,255,0.08);
+    box-shadow: inset 0px 0px 1px 1px rgba(255,255,255,0.06);
+    border-radius: 4px;
+}
+.application-button-selected {
+    font-weight: bold;
+}
+.application-button-label { 
+    padding-left: 5px;
+}
+.application-button-label-favorites { 
+    padding-left: 5px;
+    font-weight: bold;
+    text-decoration: underline;
+}
+.category-button {
+    padding-top: 7px;
+    padding-left: 7px;
+    padding-right: 7px;
+    padding-bottom: 7px; 
+    font-weight: normal;
+}
+.category-button-selected,.category-button:hover {
+    padding-top: 7px;
+    padding-left: 7px;
+    padding-right: 7px;
+    padding-bottom: 7px; 
+    background-gradient-direction: vertical;
+    background-gradient-start: rgba(255,255,255,0.2);
+    background-gradient-end: rgba(255,255,255,0.08);
+    box-shadow: inset 0px 0px 1px 1px rgba(255,255,255,0.06);
+    border-radius: 4px;
+}
+.favswich-button {
+    width: 150px;
+    font-weight: bold;
+}
+.leftpane-box {
+    padding-right: 10px;
+    font-size: 12px;
+}
+.category-button-label { 
+    padding-left: 5px;
+}
+.category-button-button:hover {
+    background-color: #969696;
+    border-radius: 8px;
+}
+.selected-app-box {
+    padding-right: 30px;
+    padding-left: 28px;
+    text-align: right;
+}
+.selected-app-title {
+    font-weight: bold;
+    font-size: small;
+}
+.selected-app-description {
+    max-width: 220px;
+}
+.search_box {
+   padding-left: 10px;
+   padding-right: 10px;
+   padding-bottom: 10px;
+}
+.pane-title {
+    font-weight: bold;
+    padding: 7px 0;
+    font-size: 10pt;
+}
+.config-menu-dialog-box {
+    font-size: 11pt;
+}
+.config-dialog-label {
+    font-size: 10pt;
+}
+.config-dialog-header {
+    font-size: normal;
+    font-weight: bold;
+    padding: 6px;
+}
+.config-dialog-header.nb {
+    text-align: center;
+    background: rgba(255,255,255,0.08);
+    border-radius: 4px;
+}
+.config-dialog-entry {
+    padding: 3px 4px;
+    border-radius: 3px;
+    color: black;
+    selected-color: black;
+    border: 1px solid rgba(245,245,245,0.2);
+    background-gradient-direction: vertical;
+    background-gradient-start: rgb(200,200,200);
+    background-gradient-end: white;
+    transition-duration: 300;
+    box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
+    caret-color: #a8a8a8;
+    width: 55px;
+    font-size: small;
+}
+.config-dialog-entry.sysapps {
+    width: 400px;
+}
+.config-button-label-entry {
+    width: 150px;
+    padding: 3px 4px;
+}
+.config-notebook-box {
+    width: 600px;
+    height: 390px;
+    padding-top: 15px;
+}
+.config-notebook-tabs {
+    width: 140px;
+    padding-right: 30px;
+    font-size: small;
+    spacing: 8px;
+}
+.config-notebook-control-box {
+    spacing: 10px;
+}
+.button-reset {
+    border: #a60000 1px solid;
+    color: #a60000;
+    font-size: 10pt;
+    border-radius: 4px;
+    padding: 4px 6px;
+}
+.button-reset:hover {
+    padding: 4px 6px;
+    border: #f00 1px solid;
+    color: #f00;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]