Blame extensions/apps-menu/extension.js

Packit Service 597ea5
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
Packit Service 597ea5
/* exported init enable disable */
Packit Service 597ea5
Packit Service 597ea5
const {
Packit Service 597ea5
    Atk, Clutter, Gio, GLib, GMenu, GObject, Gtk, Meta, Shell, St
Packit Service 597ea5
} = imports.gi;
Packit Service 597ea5
const DND = imports.ui.dnd;
Packit Service 597ea5
const Main = imports.ui.main;
Packit Service 597ea5
const PanelMenu = imports.ui.panelMenu;
Packit Service 597ea5
const PopupMenu = imports.ui.popupMenu;
Packit Service 597ea5
const Signals = imports.signals;
Packit Service 597ea5
Packit Service 597ea5
const Gettext = imports.gettext.domain('gnome-shell-extensions');
Packit Service 597ea5
const _ = Gettext.gettext;
Packit Service 597ea5
Packit Service 597ea5
const ExtensionUtils = imports.misc.extensionUtils;
Packit Service 597ea5
Packit Service 597ea5
const appSys = Shell.AppSystem.get_default();
Packit Service 597ea5
Packit Service 597ea5
const APPLICATION_ICON_SIZE = 32;
Packit Service 597ea5
const HORIZ_FACTOR = 5;
Packit Service 597ea5
const MENU_HEIGHT_OFFSET = 132;
Packit Service 597ea5
const NAVIGATION_REGION_OVERSHOOT = 50;
Packit Service 597ea5
Packit Service 597ea5
Gio._promisify(Gio._LocalFilePrototype, 'query_info_async', 'query_info_finish');
Packit Service 597ea5
Gio._promisify(Gio._LocalFilePrototype, 'set_attributes_async', 'set_attributes_finish');
Packit Service 597ea5
Packit Service 669be0
class ActivitiesMenuItem extends PopupMenu.PopupBaseMenuItem {
Packit Service 669be0
    constructor(button) {
Packit Service 669be0
        super();
Packit Service 669be0
        this._button = button;
Packit Service 669be0
        this.actor.add_child(new St.Label({ text: _('Activities Overview') }));
Packit Service 669be0
    }
Packit Service 669be0
Packit Service 669be0
    activate(event) {
Packit Service 669be0
        this._button.menu.toggle();
Packit Service 669be0
        Main.overview.toggle();
Packit Service 669be0
        super.activate(event);
Packit Service 669be0
    }
Packit Service 669be0
}
Packit Service 669be0
Packit Service 597ea5
class ApplicationMenuItem extends PopupMenu.PopupBaseMenuItem {
Packit Service 597ea5
    constructor(button, app) {
Packit Service 597ea5
        super();
Packit Service 597ea5
        this._app = app;
Packit Service 597ea5
        this._button = button;
Packit Service 597ea5
Packit Service 597ea5
        this._iconBin = new St.Bin();
Packit Service 597ea5
        this.actor.add_child(this._iconBin);
Packit Service 597ea5
Packit Service 597ea5
        let appLabel = new St.Label({
Packit Service 597ea5
            text: app.get_name(),
Packit Service 597ea5
            y_expand: true,
Packit Service 597ea5
            y_align: Clutter.ActorAlign.CENTER
Packit Service 597ea5
        });
Packit Service 597ea5
        this.actor.add_child(appLabel);
Packit Service 597ea5
        this.actor.label_actor = appLabel;
Packit Service 597ea5
Packit Service 597ea5
        let textureCache = St.TextureCache.get_default();
Packit Service 597ea5
        let iconThemeChangedId = textureCache.connect('icon-theme-changed',
Packit Service 597ea5
                                                      this._updateIcon.bind(this));
Packit Service 597ea5
        this.actor.connect('destroy', () => {
Packit Service 597ea5
            textureCache.disconnect(iconThemeChangedId);
Packit Service 597ea5
        });
Packit Service 597ea5
        this._updateIcon();
Packit Service 597ea5
Packit Service 597ea5
        this.actor._delegate = this;
Packit Service 597ea5
        let draggable = DND.makeDraggable(this.actor);
Packit Service 597ea5
Packit Service 597ea5
        let maybeStartDrag = draggable._maybeStartDrag;
Packit Service 597ea5
        draggable._maybeStartDrag = (event) => {
Packit Service 597ea5
            if (this._dragEnabled)
Packit Service 597ea5
                return maybeStartDrag.call(draggable, event);
Packit Service 597ea5
            return false;
Packit Service 597ea5
        };
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    activate(event) {
Packit Service 597ea5
        this._app.open_new_window(-1);
Packit Service 597ea5
        this._button.selectCategory(null);
Packit Service 597ea5
        this._button.menu.toggle();
Packit Service 597ea5
        super.activate(event);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    setActive(active, params) {
Packit Service 597ea5
        if (active)
Packit Service 597ea5
            this._button.scrollToButton(this);
Packit Service 597ea5
        super.setActive(active, params);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    setDragEnabled(enable) {
Packit Service 597ea5
        this._dragEnabled = enable;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    getDragActor() {
Packit Service 597ea5
        return this._app.create_icon_texture(APPLICATION_ICON_SIZE);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    getDragActorSource() {
Packit Service 597ea5
        return this._iconBin;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _updateIcon() {
Packit Service 669be0
        this._iconBin.set_child(this.getDragActor());
Packit Service 597ea5
    }
Packit Service 597ea5
}
Packit Service 597ea5
Packit Service 597ea5
class CategoryMenuItem extends PopupMenu.PopupBaseMenuItem {
Packit Service 597ea5
    constructor(button, category) {
Packit Service 597ea5
        super();
Packit Service 597ea5
        this._category = category;
Packit Service 597ea5
        this._button = button;
Packit Service 597ea5
Packit Service 597ea5
        this._oldX = -1;
Packit Service 597ea5
        this._oldY = -1;
Packit Service 597ea5
Packit Service 597ea5
        let name;
Packit Service 597ea5
        if (this._category)
Packit Service 597ea5
            name = this._category.get_name();
Packit Service 597ea5
        else
Packit Service 597ea5
            name = _('Favorites');
Packit Service 597ea5
Packit Service 669be0
        this.actor.add_child(new St.Label({ text: name }));
Packit Service 597ea5
        this.actor.connect('motion-event', this._onMotionEvent.bind(this));
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    activate(event) {
Packit Service 597ea5
        this._button.selectCategory(this._category);
Packit Service 597ea5
        this._button.scrollToCatButton(this);
Packit Service 597ea5
        super.activate(event);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _isNavigatingSubmenu([x, y]) {
Packit Service 597ea5
        let [posX, posY] = this.actor.get_transformed_position();
Packit Service 597ea5
Packit Service 597ea5
        if (this._oldX == -1) {
Packit Service 597ea5
            this._oldX = x;
Packit Service 597ea5
            this._oldY = y;
Packit Service 597ea5
            return true;
Packit Service 597ea5
        }
Packit Service 597ea5
Packit Service 597ea5
        let deltaX = Math.abs(x - this._oldX);
Packit Service 597ea5
        let deltaY = Math.abs(y - this._oldY);
Packit Service 597ea5
Packit Service 597ea5
        this._oldX = x;
Packit Service 597ea5
        this._oldY = y;
Packit Service 597ea5
Packit Service 597ea5
        // If it lies outside the x-coordinates then it is definitely outside.
Packit Service 597ea5
        if (posX > x || posX + this.actor.width < x)
Packit Service 597ea5
            return false;
Packit Service 597ea5
Packit Service 597ea5
        // If it lies inside the menu item then it is definitely inside.
Packit Service 597ea5
        if (posY <= y && posY + this.actor.height >= y)
Packit Service 597ea5
            return true;
Packit Service 597ea5
Packit Service 597ea5
        // We want the keep-up triangle only if the movement is more
Packit Service 597ea5
        // horizontal than vertical.
Packit Service 597ea5
        if (deltaX * HORIZ_FACTOR < deltaY)
Packit Service 597ea5
            return false;
Packit Service 597ea5
Packit Service 597ea5
        // Check whether the point lies inside triangle ABC, and a similar
Packit Service 597ea5
        // triangle on the other side of the menu item.
Packit Service 597ea5
        //
Packit Service 597ea5
        //   +---------------------+
Packit Service 597ea5
        //   | menu item           |
Packit Service 597ea5
        // A +---------------------+ C
Packit Service 597ea5
        //              P          |
Packit Service 597ea5
        //                         B
Packit Service 597ea5
Packit Service 597ea5
        // Ensure that the point P always lies below line AC so that we can
Packit Service 597ea5
        // only check for triangle ABC.
Packit Service 597ea5
        if (posY > y) {
Packit Service 597ea5
            let offset = posY - y;
Packit Service 597ea5
            y = posY + this.actor.height + offset;
Packit Service 597ea5
        }
Packit Service 597ea5
Packit Service 597ea5
        // Ensure that A is (0, 0).
Packit Service 597ea5
        x -= posX;
Packit Service 597ea5
        y -= posY + this.actor.height;
Packit Service 597ea5
Packit Service 597ea5
        // Check which side of line AB the point P lies on by taking the
Packit Service 597ea5
        // cross-product of AB and AP. See:
Packit Service 597ea5
        // http://stackoverflow.com/questions/3461453/determine-which-side-of-a-line-a-point-lies
Packit Service 597ea5
        if (((this.actor.width * y) - (NAVIGATION_REGION_OVERSHOOT * x)) <= 0)
Packit Service 597ea5
            return true;
Packit Service 597ea5
Packit Service 597ea5
        return false;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _onMotionEvent(actor, event) {
Packit Service 597ea5
        if (!Clutter.get_pointer_grab()) {
Packit Service 597ea5
            this._oldX = -1;
Packit Service 597ea5
            this._oldY = -1;
Packit Service 597ea5
            Clutter.grab_pointer(this.actor);
Packit Service 597ea5
        }
Packit Service 597ea5
        this.actor.hover = true;
Packit Service 597ea5
Packit Service 597ea5
        if (this._isNavigatingSubmenu(event.get_coords()))
Packit Service 597ea5
            return true;
Packit Service 597ea5
Packit Service 597ea5
        this._oldX = -1;
Packit Service 597ea5
        this._oldY = -1;
Packit Service 597ea5
        this.actor.hover = false;
Packit Service 597ea5
        Clutter.ungrab_pointer();
Packit Service 597ea5
Packit Service 597ea5
        let source = event.get_source();
Packit Service 597ea5
        if (source instanceof St.Widget)
Packit Service 597ea5
            source.sync_hover();
Packit Service 597ea5
Packit Service 597ea5
        return false;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    setActive(active, params) {
Packit Service 597ea5
        if (active) {
Packit Service 597ea5
            this._button.selectCategory(this._category);
Packit Service 597ea5
            this._button.scrollToCatButton(this);
Packit Service 597ea5
        }
Packit Service 597ea5
        super.setActive(active, params);
Packit Service 597ea5
    }
Packit Service 597ea5
}
Packit Service 597ea5
Packit Service 597ea5
class ApplicationsMenu extends PopupMenu.PopupMenu {
Packit Service 597ea5
    constructor(sourceActor, arrowAlignment, arrowSide, button) {
Packit Service 597ea5
        super(sourceActor, arrowAlignment, arrowSide);
Packit Service 597ea5
        this._button = button;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    isEmpty() {
Packit Service 597ea5
        return false;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 669be0
    open(animate) {
Packit Service 669be0
        this._button.hotCorner.setBarrierSize(0);
Packit Service 669be0
        if (this._button.hotCorner.actor) // fallback corner
Packit Service 669be0
            this._button.hotCorner.actor.hide();
Packit Service 669be0
        super.open(animate);
Packit Service 669be0
    }
Packit Service 669be0
Packit Service 669be0
    close(animate) {
Packit Service 669be0
        let size = Main.layoutManager.panelBox.height;
Packit Service 669be0
        this._button.hotCorner.setBarrierSize(size);
Packit Service 669be0
        if (this._button.hotCorner.actor) // fallback corner
Packit Service 669be0
            this._button.hotCorner.actor.show();
Packit Service 669be0
        super.close(animate);
Packit Service 669be0
    }
Packit Service 669be0
Packit Service 597ea5
    toggle() {
Packit Service 669be0
        if (this.isOpen) {
Packit Service 597ea5
            this._button.selectCategory(null);
Packit Service 669be0
        } else {
Packit Service 669be0
            if (Main.overview.visible)
Packit Service 669be0
                Main.overview.hide();
Packit Service 669be0
        }
Packit Service 597ea5
        super.toggle();
Packit Service 597ea5
    }
Packit Service 597ea5
}
Packit Service 597ea5
Packit Service 597ea5
class DesktopTarget {
Packit Service 597ea5
    constructor() {
Packit Service 597ea5
        this._desktop = null;
Packit Service 597ea5
        this._desktopDestroyedId = 0;
Packit Service 597ea5
Packit Service 597ea5
        this._windowAddedId =
Packit Service 597ea5
            global.window_group.connect('actor-added',
Packit Service 597ea5
                                        this._onWindowAdded.bind(this));
Packit Service 597ea5
Packit Service 597ea5
        global.get_window_actors().forEach(a => {
Packit Service 597ea5
            this._onWindowAdded(a.get_parent(), a);
Packit Service 597ea5
        });
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    get hasDesktop() {
Packit Service 597ea5
        return this._desktop != null;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _onWindowAdded(group, actor) {
Packit Service 597ea5
        if (!(actor instanceof Meta.WindowActor))
Packit Service 597ea5
            return;
Packit Service 597ea5
Packit Service 597ea5
        if (actor.meta_window.get_window_type() == Meta.WindowType.DESKTOP)
Packit Service 597ea5
            this._setDesktop(actor);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _setDesktop(desktop) {
Packit Service 597ea5
        if (this._desktop) {
Packit Service 597ea5
            this._desktop.disconnect(this._desktopDestroyedId);
Packit Service 597ea5
            this._desktopDestroyedId = 0;
Packit Service 597ea5
Packit Service 597ea5
            delete this._desktop._delegate;
Packit Service 597ea5
        }
Packit Service 597ea5
Packit Service 597ea5
        this._desktop = desktop;
Packit Service 597ea5
        this.emit('desktop-changed');
Packit Service 597ea5
Packit Service 597ea5
        if (this._desktop) {
Packit Service 597ea5
            this._desktopDestroyedId = this._desktop.connect('destroy', () => {
Packit Service 597ea5
                this._setDesktop(null);
Packit Service 597ea5
            });
Packit Service 597ea5
            this._desktop._delegate = this;
Packit Service 597ea5
        }
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _getSourceAppInfo(source) {
Packit Service 597ea5
        if (!(source instanceof ApplicationMenuItem))
Packit Service 597ea5
            return null;
Packit Service 597ea5
        return source._app.app_info;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    async _markTrusted(file) {
Packit Service 597ea5
        let modeAttr = Gio.FILE_ATTRIBUTE_UNIX_MODE;
Packit Service 597ea5
        let trustedAttr = 'metadata::trusted';
Packit Service 597ea5
        let queryFlags = Gio.FileQueryInfoFlags.NONE;
Packit Service 597ea5
        let ioPriority = GLib.PRIORITY_DEFAULT;
Packit Service 597ea5
Packit Service 597ea5
        try {
Packit Service 597ea5
            let info = await file.query_info_async(modeAttr, queryFlags, ioPriority, null);
Packit Service 597ea5
Packit Service 597ea5
            let mode = info.get_attribute_uint32(modeAttr) | 0o100;
Packit Service 597ea5
            info.set_attribute_uint32(modeAttr, mode);
Packit Service 597ea5
            info.set_attribute_string(trustedAttr, 'yes');
Packit Service 597ea5
            await file.set_attributes_async(info, queryFlags, ioPriority, null);
Packit Service 597ea5
Packit Service 597ea5
            // Hack: force nautilus to reload file info
Packit Service 597ea5
            info = new Gio.FileInfo();
Packit Service 597ea5
            info.set_attribute_uint64(Gio.FILE_ATTRIBUTE_TIME_ACCESS,
Packit Service 597ea5
                                      GLib.get_real_time());
Packit Service 597ea5
            try {
Packit Service 597ea5
                await file.set_attributes_async(info, queryFlags, ioPriority, null);
Packit Service 597ea5
            } catch (e) {
Packit Service 597ea5
                log(`Failed to update access time: ${e.message}`);
Packit Service 597ea5
            }
Packit Service 597ea5
        } catch (e) {
Packit Service 597ea5
            log(`Failed to mark file as trusted: ${e.message}`);
Packit Service 597ea5
        }
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    destroy() {
Packit Service 597ea5
        if (this._windowAddedId)
Packit Service 597ea5
            global.window_group.disconnect(this._windowAddedId);
Packit Service 597ea5
        this._windowAddedId = 0;
Packit Service 597ea5
Packit Service 597ea5
        this._setDesktop(null);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    handleDragOver(source, _actor, _x, _y, _time) {
Packit Service 597ea5
        let appInfo = this._getSourceAppInfo(source);
Packit Service 597ea5
        if (!appInfo)
Packit Service 597ea5
            return DND.DragMotionResult.CONTINUE;
Packit Service 597ea5
Packit Service 597ea5
        return DND.DragMotionResult.COPY_DROP;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    acceptDrop(source, _actor, _x, _y, _time) {
Packit Service 597ea5
        let appInfo = this._getSourceAppInfo(source);
Packit Service 597ea5
        if (!appInfo)
Packit Service 597ea5
            return false;
Packit Service 597ea5
Packit Service 597ea5
        this.emit('app-dropped');
Packit Service 597ea5
Packit Service 597ea5
        let desktop = GLib.get_user_special_dir(GLib.UserDirectory.DIRECTORY_DESKTOP);
Packit Service 597ea5
Packit Service 597ea5
        let src = Gio.File.new_for_path(appInfo.get_filename());
Packit Service 597ea5
        let dst = Gio.File.new_for_path(GLib.build_filenamev([desktop, src.get_basename()]));
Packit Service 597ea5
Packit Service 597ea5
        try {
Packit Service 597ea5
            // copy_async() isn't introspectable :-(
Packit Service 597ea5
            src.copy(dst, Gio.FileCopyFlags.OVERWRITE, null, null);
Packit Service 597ea5
            this._markTrusted(dst);
Packit Service 597ea5
        } catch (e) {
Packit Service 597ea5
            log(`Failed to copy to desktop: ${e.message}`);
Packit Service 597ea5
        }
Packit Service 597ea5
Packit Service 597ea5
        return true;
Packit Service 597ea5
    }
Packit Service 597ea5
}
Packit Service 597ea5
Signals.addSignalMethods(DesktopTarget.prototype);
Packit Service 597ea5
Packit Service 597ea5
let ApplicationsButton = GObject.registerClass(
Packit Service 597ea5
class ApplicationsButton extends PanelMenu.Button {
Packit Service 669be0
    _init() {
Packit Service 597ea5
        super._init(1.0, null, false);
Packit Service 597ea5
Packit Service 597ea5
        this.setMenu(new ApplicationsMenu(this, 1.0, St.Side.TOP, this));
Packit Service 597ea5
        Main.panel.menuManager.addMenu(this.menu);
Packit Service 597ea5
Packit Service 597ea5
        // At this moment applications menu is not keyboard navigable at
Packit Service 597ea5
        // all (so not accessible), so it doesn't make sense to set as
Packit Service 597ea5
        // role ATK_ROLE_MENU like other elements of the panel.
Packit Service 597ea5
        this.accessible_role = Atk.Role.LABEL;
Packit Service 597ea5
Packit Service 597ea5
        let hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
Packit Service 597ea5
rpm-build 5d0049
        let iconFile = Gio.File.new_for_path(
rpm-build 5d0049
            '/usr/share/icons/hicolor/scalable/apps/start-here.svg');
rpm-build 5d0049
        this._icon = new St.Icon({
rpm-build 5d0049
            gicon: new Gio.FileIcon({ file: iconFile }),
rpm-build 5d0049
            style_class: 'panel-logo-icon'
rpm-build 5d0049
        });
rpm-build 5d0049
        hbox.add_actor(this._icon);
rpm-build 5d0049
Packit Service 597ea5
        this._label = new St.Label({
Packit Service 597ea5
            text: _('Applications'),
Packit Service 597ea5
            y_expand: true,
Packit Service 597ea5
            y_align: Clutter.ActorAlign.CENTER
Packit Service 597ea5
        });
Packit Service 597ea5
        hbox.add_child(this._label);
Packit Service 597ea5
        hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
Packit Service 597ea5
Packit Service 597ea5
        this.add_actor(hbox);
Packit Service 597ea5
        this.name = 'panelApplications';
Packit Service 597ea5
        this.label_actor = this._label;
Packit Service 597ea5
Packit Service 669be0
        this.connect('captured-event', this._onCapturedEvent.bind(this));
Packit Service 669be0
Packit Service 597ea5
        this._showingId = Main.overview.connect('showing', () => {
Packit Service 597ea5
            this.add_accessible_state (Atk.StateType.CHECKED);
Packit Service 597ea5
        });
Packit Service 597ea5
        this._hidingId = Main.overview.connect('hiding', () => {
Packit Service 597ea5
            this.remove_accessible_state (Atk.StateType.CHECKED);
Packit Service 597ea5
        });
Packit Service 597ea5
        Main.layoutManager.connect('startup-complete',
Packit Service 597ea5
                                   this._setKeybinding.bind(this));
Packit Service 597ea5
        this._setKeybinding();
Packit Service 597ea5
Packit Service 597ea5
        this._desktopTarget = new DesktopTarget();
Packit Service 597ea5
        this._desktopTarget.connect('app-dropped', () => {
Packit Service 597ea5
            this.menu.close();
Packit Service 597ea5
        });
Packit Service 597ea5
        this._desktopTarget.connect('desktop-changed', () => {
Packit Service 597ea5
            this._applicationsButtons.forEach(item => {
Packit Service 597ea5
                item.setDragEnabled(this._desktopTarget.hasDesktop);
Packit Service 597ea5
            });
Packit Service 597ea5
        });
Packit Service 597ea5
Packit Service 597ea5
        this._tree = new GMenu.Tree({ menu_basename: 'applications.menu' });
Packit Service 597ea5
        this._treeChangedId = this._tree.connect('changed',
Packit Service 597ea5
                                                 this._onTreeChanged.bind(this));
Packit Service 597ea5
Packit Service 597ea5
        this._applicationsButtons = new Map();
Packit Service 597ea5
        this.reloadFlag = false;
Packit Service 597ea5
        this._createLayout();
Packit Service 597ea5
        this._display();
Packit Service 597ea5
        this._installedChangedId = appSys.connect('installed-changed',
Packit Service 597ea5
                                                  this._onTreeChanged.bind(this));
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _onTreeChanged() {
Packit Service 597ea5
        if (this.menu.isOpen) {
Packit Service 597ea5
            this._redisplay();
Packit Service 597ea5
            this.mainBox.show();
Packit Service 597ea5
        } else {
Packit Service 597ea5
            this.reloadFlag = true;
Packit Service 597ea5
        }
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 669be0
    get hotCorner() {
Packit Service 669be0
        return Main.layoutManager.hotCorners[Main.layoutManager.primaryIndex];
Packit Service 669be0
    }
Packit Service 669be0
Packit Service 597ea5
    _createVertSeparator() {
Packit Service 597ea5
        let separator = new St.DrawingArea({
Packit Service 597ea5
            style_class: 'calendar-vertical-separator',
Packit Service 597ea5
            pseudo_class: 'highlighted'
Packit Service 597ea5
        });
Packit Service 597ea5
        separator.connect('repaint', this._onVertSepRepaint.bind(this));
Packit Service 597ea5
        return separator;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _onDestroy() {
Packit Service 597ea5
        Main.overview.disconnect(this._showingId);
Packit Service 597ea5
        Main.overview.disconnect(this._hidingId);
Packit Service 597ea5
        appSys.disconnect(this._installedChangedId);
Packit Service 597ea5
        this._tree.disconnect(this._treeChangedId);
Packit Service 597ea5
        this._tree = null;
Packit Service 597ea5
Packit Service 597ea5
        let handler = Main.sessionMode.hasOverview ?
Packit Service 597ea5
            Main.overview.toggle.bind(Main.overview) : null;
Packit Service 597ea5
        Main.wm.setCustomKeybindingHandler('panel-main-menu',
Packit Service 597ea5
                                           Shell.ActionMode.NORMAL |
Packit Service 597ea5
                                           Shell.ActionMode.OVERVIEW,
Packit Service 597ea5
                                           handler);
Packit Service 597ea5
Packit Service 597ea5
        this._desktopTarget.destroy();
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 669be0
    _onCapturedEvent(actor, event) {
Packit Service 669be0
        if (event.type() == Clutter.EventType.BUTTON_PRESS) {
Packit Service 669be0
            if (!Main.overview.shouldToggleByCornerOrButton())
Packit Service 669be0
                return true;
Packit Service 669be0
        }
Packit Service 669be0
        return false;
Packit Service 669be0
    }
Packit Service 669be0
Packit Service 597ea5
    _onMenuKeyPress(actor, event) {
Packit Service 597ea5
        let symbol = event.get_key_symbol();
Packit Service 597ea5
        if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
Packit Service 597ea5
            let direction = symbol == Clutter.KEY_Left ?
Packit Service 597ea5
                Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
Packit Service 597ea5
            if (this.menu.actor.navigate_focus(global.stage.key_focus, direction, false))
Packit Service 597ea5
                return true;
Packit Service 597ea5
        }
Packit Service 597ea5
        return super._onMenuKeyPress(actor, event);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _onVertSepRepaint(area) {
Packit Service 597ea5
        let cr = area.get_context();
Packit Service 597ea5
        let themeNode = area.get_theme_node();
Packit Service 597ea5
        let [width, height] = area.get_surface_size();
Packit Service 597ea5
        let stippleColor = themeNode.get_color('-stipple-color');
Packit Service 597ea5
        let stippleWidth = themeNode.get_length('-stipple-width');
Packit Service 597ea5
        let x = Math.floor(width / 2) + 0.5;
Packit Service 597ea5
        cr.moveTo(x, 0);
Packit Service 597ea5
        cr.lineTo(x, height);
Packit Service 597ea5
        Clutter.cairo_set_source_color(cr, stippleColor);
Packit Service 597ea5
        cr.setDash([1, 3], 1); // Hard-code for now
Packit Service 597ea5
        cr.setLineWidth(stippleWidth);
Packit Service 597ea5
        cr.stroke();
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _onOpenStateChanged(menu, open) {
Packit Service 597ea5
        if (open) {
Packit Service 597ea5
            if (this.reloadFlag) {
Packit Service 597ea5
                this._redisplay();
Packit Service 597ea5
                this.reloadFlag = false;
Packit Service 597ea5
            }
Packit Service 597ea5
            this.mainBox.show();
Packit Service 597ea5
        }
Packit Service 597ea5
        super._onOpenStateChanged(menu, open);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _setKeybinding() {
Packit Service 597ea5
        Main.wm.setCustomKeybindingHandler('panel-main-menu',
Packit Service 597ea5
                                           Shell.ActionMode.NORMAL |
Packit Service 597ea5
                                           Shell.ActionMode.OVERVIEW,
Packit Service 597ea5
                                           () => this.menu.toggle());
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _redisplay() {
Packit Service 597ea5
        this.applicationsBox.destroy_all_children();
Packit Service 597ea5
        this.categoriesBox.destroy_all_children();
Packit Service 597ea5
        this._display();
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _loadCategory(categoryId, dir) {
Packit Service 597ea5
        let iter = dir.iter();
Packit Service 597ea5
        let nextType;
Packit Service 597ea5
        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
Packit Service 597ea5
            if (nextType == GMenu.TreeItemType.ENTRY) {
Packit Service 597ea5
                let entry = iter.get_entry();
Packit Service 597ea5
                let id;
Packit Service 597ea5
                try {
Packit Service 597ea5
                    id = entry.get_desktop_file_id(); // catch non-UTF8 filenames
Packit Service 597ea5
                } catch (e) {
Packit Service 597ea5
                    continue;
Packit Service 597ea5
                }
Packit Service 597ea5
                let app = appSys.lookup_app(id);
Packit Service 597ea5
                if (!app)
Packit Service 597ea5
                    app = new Shell.App({ app_info: entry.get_app_info() });
Packit Service 597ea5
                if (app.get_app_info().should_show())
Packit Service 597ea5
                    this.applicationsByCategory[categoryId].push(app);
Packit Service 597ea5
            } else if (nextType == GMenu.TreeItemType.SEPARATOR) {
Packit Service 597ea5
                this.applicationsByCategory[categoryId].push('separator');
Packit Service 597ea5
            } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
Packit Service 597ea5
                let subdir = iter.get_directory();
Packit Service 597ea5
                if (!subdir.get_is_nodisplay())
Packit Service 597ea5
                    this._loadCategory(categoryId, subdir);
Packit Service 597ea5
            }
Packit Service 597ea5
        }
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    scrollToButton(button) {
Packit Service 597ea5
        let appsScrollBoxAdj = this.applicationsScrollBox.get_vscroll_bar().get_adjustment();
Packit Service 597ea5
        let appsScrollBoxAlloc = this.applicationsScrollBox.get_allocation_box();
Packit Service 597ea5
        let currentScrollValue = appsScrollBoxAdj.get_value();
Packit Service 597ea5
        let boxHeight = appsScrollBoxAlloc.y2 - appsScrollBoxAlloc.y1;
Packit Service 597ea5
        let buttonAlloc = button.actor.get_allocation_box();
Packit Service 597ea5
        let newScrollValue = currentScrollValue;
Packit Service 597ea5
        if (currentScrollValue > buttonAlloc.y1 - 10)
Packit Service 597ea5
            newScrollValue = buttonAlloc.y1 - 10;
Packit Service 597ea5
        if (boxHeight + currentScrollValue < buttonAlloc.y2 + 10)
Packit Service 597ea5
            newScrollValue = buttonAlloc.y2 - boxHeight + 10;
Packit Service 597ea5
        if (newScrollValue != currentScrollValue)
Packit Service 597ea5
            appsScrollBoxAdj.set_value(newScrollValue);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    scrollToCatButton(button) {
Packit Service 597ea5
        let catsScrollBoxAdj = this.categoriesScrollBox.get_vscroll_bar().get_adjustment();
Packit Service 597ea5
        let catsScrollBoxAlloc = this.categoriesScrollBox.get_allocation_box();
Packit Service 597ea5
        let currentScrollValue = catsScrollBoxAdj.get_value();
Packit Service 597ea5
        let boxHeight = catsScrollBoxAlloc.y2 - catsScrollBoxAlloc.y1;
Packit Service 597ea5
        let buttonAlloc = button.actor.get_allocation_box();
Packit Service 597ea5
        let newScrollValue = currentScrollValue;
Packit Service 597ea5
        if (currentScrollValue > buttonAlloc.y1 - 10)
Packit Service 597ea5
            newScrollValue = buttonAlloc.y1 - 10;
Packit Service 597ea5
        if (boxHeight + currentScrollValue < buttonAlloc.y2 + 10)
Packit Service 597ea5
            newScrollValue = buttonAlloc.y2 - boxHeight + 10;
Packit Service 597ea5
        if (newScrollValue != currentScrollValue)
Packit Service 597ea5
            catsScrollBoxAdj.set_value(newScrollValue);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _createLayout() {
Packit Service 597ea5
        let section = new PopupMenu.PopupMenuSection();
Packit Service 597ea5
        this.menu.addMenuItem(section);
Packit Service 597ea5
        this.mainBox = new St.BoxLayout({ vertical: false });
Packit Service 597ea5
        this.leftBox = new St.BoxLayout({ vertical: true });
Packit Service 597ea5
        this.applicationsScrollBox = new St.ScrollView({
Packit Service 597ea5
            x_fill: true,
Packit Service 597ea5
            y_fill: false,
Packit Service 597ea5
            y_align: St.Align.START,
Packit Service 597ea5
            style_class: 'apps-menu vfade'
Packit Service 597ea5
        });
Packit Service 597ea5
        this.applicationsScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
Packit Service 597ea5
        let vscroll = this.applicationsScrollBox.get_vscroll_bar();
Packit Service 597ea5
        vscroll.connect('scroll-start', () => {
Packit Service 597ea5
            this.menu.passEvents = true;
Packit Service 597ea5
        });
Packit Service 597ea5
        vscroll.connect('scroll-stop', () => {
Packit Service 597ea5
            this.menu.passEvents = false;
Packit Service 597ea5
        });
Packit Service 597ea5
        this.categoriesScrollBox = new St.ScrollView({
Packit Service 597ea5
            x_fill: true,
Packit Service 597ea5
            y_fill: false,
Packit Service 597ea5
            y_align: St.Align.START,
Packit Service 597ea5
            style_class: 'vfade'
Packit Service 597ea5
        });
Packit Service 597ea5
        this.categoriesScrollBox.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
Packit Service 597ea5
        vscroll = this.categoriesScrollBox.get_vscroll_bar();
Packit Service 597ea5
        vscroll.connect('scroll-start', () => this.menu.passEvents = true);
Packit Service 597ea5
        vscroll.connect('scroll-stop', () => this.menu.passEvents = false);
Packit Service 597ea5
        this.leftBox.add(this.categoriesScrollBox, {
Packit Service 597ea5
            expand: true,
Packit Service 597ea5
            x_fill: true,
Packit Service 597ea5
            y_fill: true,
Packit Service 597ea5
            y_align: St.Align.START
Packit Service 597ea5
        });
Packit Service 597ea5
Packit Service 669be0
        let activities = new ActivitiesMenuItem(this);
Packit Service 669be0
        this.leftBox.add(activities.actor, {
Packit Service 669be0
            expand: false,
Packit Service 669be0
            x_fill: true,
Packit Service 669be0
            y_fill: false,
Packit Service 669be0
            y_align: St.Align.START
Packit Service 669be0
        });
Packit Service 669be0
Packit Service 597ea5
        this.applicationsBox = new St.BoxLayout({ vertical: true });
Packit Service 597ea5
        this.applicationsScrollBox.add_actor(this.applicationsBox);
Packit Service 597ea5
        this.categoriesBox = new St.BoxLayout({ vertical: true });
Packit Service 597ea5
        this.categoriesScrollBox.add_actor(this.categoriesBox);
Packit Service 597ea5
Packit Service 597ea5
        this.mainBox.add(this.leftBox);
Packit Service 597ea5
        this.mainBox.add(this._createVertSeparator(), {
Packit Service 597ea5
            expand: false,
Packit Service 597ea5
            x_fill: false,
Packit Service 597ea5
            y_fill: true
Packit Service 597ea5
        });
Packit Service 597ea5
        this.mainBox.add(this.applicationsScrollBox, {
Packit Service 597ea5
            expand: true,
Packit Service 597ea5
            x_fill: true,
Packit Service 597ea5
            y_fill: true
Packit Service 597ea5
        });
Packit Service 597ea5
        section.actor.add_actor(this.mainBox);
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _display() {
Packit Service 597ea5
        this._applicationsButtons.clear();
Packit Service 597ea5
        this.mainBox.style = 'width: 35em;';
Packit Service 597ea5
        this.mainBox.hide();
Packit Service 597ea5
Packit Service 597ea5
        //Load categories
Packit Service 597ea5
        this.applicationsByCategory = {};
Packit Service 597ea5
        this._tree.load_sync();
Packit Service 597ea5
        let root = this._tree.get_root_directory();
Packit Service 597ea5
        let categoryMenuItem = new CategoryMenuItem(this, null);
Packit Service 597ea5
        this.categoriesBox.add_actor(categoryMenuItem.actor);
Packit Service 597ea5
        let iter = root.iter();
Packit Service 597ea5
        let nextType;
Packit Service 597ea5
        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
Packit Service 597ea5
            if (nextType != GMenu.TreeItemType.DIRECTORY)
Packit Service 597ea5
                continue;
Packit Service 597ea5
Packit Service 597ea5
            let dir = iter.get_directory();
Packit Service 597ea5
            if (dir.get_is_nodisplay())
Packit Service 597ea5
                continue;
Packit Service 597ea5
Packit Service 597ea5
            let categoryId = dir.get_menu_id();
Packit Service 597ea5
            this.applicationsByCategory[categoryId] = [];
Packit Service 597ea5
            this._loadCategory(categoryId, dir);
Packit Service 597ea5
            if (this.applicationsByCategory[categoryId].length > 0) {
Packit Service 597ea5
                let categoryMenuItem = new CategoryMenuItem(this, dir);
Packit Service 597ea5
                this.categoriesBox.add_actor(categoryMenuItem.actor);
Packit Service 597ea5
            }
Packit Service 597ea5
        }
Packit Service 597ea5
Packit Service 597ea5
        //Load applications
Packit Service 597ea5
        this._displayButtons(this._listApplications(null));
Packit Service 597ea5
Packit Service 597ea5
        let themeContext = St.ThemeContext.get_for_stage(global.stage);
Packit Service 597ea5
        let scaleFactor = themeContext.scale_factor;
Packit Service 597ea5
        let categoriesHeight = this.categoriesBox.height / scaleFactor;
Packit Service 597ea5
        let height = Math.round(categoriesHeight) + MENU_HEIGHT_OFFSET;
Packit Service 597ea5
        this.mainBox.style += `height: ${height}px`;
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    selectCategory(dir) {
Packit Service 597ea5
        this.applicationsBox.get_children().forEach(c => {
Packit Service 597ea5
            if (c._delegate instanceof PopupMenu.PopupSeparatorMenuItem)
Packit Service 597ea5
                c._delegate.destroy();
Packit Service 597ea5
            else
Packit Service 597ea5
                this.applicationsBox.remove_actor(c);
Packit Service 597ea5
        });
Packit Service 597ea5
Packit Service 597ea5
        if (dir)
Packit Service 597ea5
            this._displayButtons(this._listApplications(dir.get_menu_id()));
Packit Service 597ea5
        else
Packit Service 597ea5
            this._displayButtons(this._listApplications(null));
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _displayButtons(apps) {
Packit Service 597ea5
        for (let i = 0; i < apps.length; i++) {
Packit Service 597ea5
            let app = apps[i];
Packit Service 597ea5
            let item;
Packit Service 597ea5
            if (app instanceof Shell.App)
Packit Service 597ea5
                item = this._applicationsButtons.get(app);
Packit Service 597ea5
            else
Packit Service 597ea5
                item = new PopupMenu.PopupSeparatorMenuItem();
Packit Service 597ea5
            if (!item) {
Packit Service 597ea5
                item = new ApplicationMenuItem(this, app);
Packit Service 597ea5
                item.setDragEnabled(this._desktopTarget.hasDesktop);
Packit Service 597ea5
                this._applicationsButtons.set(app, item);
Packit Service 597ea5
            }
Packit Service 597ea5
            if (!item.actor.get_parent())
Packit Service 597ea5
                this.applicationsBox.add_actor(item.actor);
Packit Service 597ea5
        }
Packit Service 597ea5
    }
Packit Service 597ea5
Packit Service 597ea5
    _listApplications(categoryMenuId) {
Packit Service 597ea5
        let applist;
Packit Service 597ea5
Packit Service 597ea5
        if (categoryMenuId) {
Packit Service 597ea5
            applist = this.applicationsByCategory[categoryMenuId];
Packit Service 597ea5
        } else {
Packit Service 597ea5
            applist = new Array();
Packit Service 597ea5
            let favorites = global.settings.get_strv('favorite-apps');
Packit Service 597ea5
            for (let i = 0; i < favorites.length; i++) {
Packit Service 597ea5
                let app = appSys.lookup_app(favorites[i]);
Packit Service 597ea5
                if (app)
Packit Service 597ea5
                    applist.push(app);
Packit Service 597ea5
            }
Packit Service 597ea5
        }
Packit Service 597ea5
Packit Service 597ea5
        return applist;
Packit Service 597ea5
    }
Packit Service 597ea5
});
Packit Service 597ea5
Packit Service 597ea5
let appsMenuButton;
Packit Service 669be0
let activitiesButton;
Packit Service 597ea5
Packit Service 597ea5
function enable() {
Packit Service 669be0
    activitiesButton = Main.panel.statusArea['activities'];
Packit Service 669be0
    activitiesButton.container.hide();
Packit Service 669be0
    appsMenuButton = new ApplicationsButton();
Packit Service 669be0
    Main.panel.addToStatusArea('apps-menu', appsMenuButton, 1, 'left');
Packit Service 597ea5
}
Packit Service 597ea5
Packit Service 597ea5
function disable() {
Packit Service 597ea5
    Main.panel.menuManager.removeMenu(appsMenuButton.menu);
Packit Service 597ea5
    appsMenuButton.destroy();
Packit Service 669be0
    activitiesButton.container.show();
Packit Service 597ea5
}
Packit Service 597ea5
Packit Service 597ea5
function init() {
Packit Service 597ea5
    ExtensionUtils.initTranslations();
Packit Service 597ea5
}