/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */

const Clutter = imports.gi.Clutter;
const Pango = imports.gi.Pango;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const Mainloop = imports.mainloop;

const AppFavorites = imports.ui.appFavorites;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const Search = imports.ui.search;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const AppDisplay = imports.ui.appDisplay;
const AltTab = imports.ui.altTab;

const Gettext = imports.gettext.domain('gnome-shell-extensions');
const _ = Gettext.gettext;

// Settings
const DOCK_SETTINGS_SCHEMA = 'org.gnome.shell.extensions.dock';
const DOCK_POSITION_KEY = 'position';
const DOCK_SIZE_KEY = 'size';
const DOCK_HIDE_KEY = 'autohide';
const DOCK_EFFECTHIDE_KEY = 'hide-effect';
const DOCK_AUTOHIDE_ANIMATION_TIME_KEY = 'hide-effect-duration';


//hide
//const autohide_animation_time = 0.3;

// Keep enums in sync with GSettings schemas
const PositionMode = {
    LEFT: 0,
    RIGHT: 1
};

const AutoHideEffect = {
    RESIZE: 0,
    RESCALE: 1,
    MOVE: 2
};

let position = PositionMode.RIGHT;
let dockicon_size = 48;
let hideable = true;
let hideDock = true;
let hideEffect = AutoHideEffect.RESIZE;
let autohide_animation_time = 0.3;
const DND_RAISE_APP_TIMEOUT = 500;

/*************************************************************************************/
/**** start resize's Dock functions                                  *****************/
/*************************************************************************************/
function hideDock_size () {
    if (hideable){
       let monitor = Main.layoutManager.primaryMonitor
       let position_x = monitor.x;
       let height = (this._nicons)*(this._item_size + this._spacing) + 2*this._spacing;
       let width = this._item_size + 4*this._spacing;

       Tweener.addTween(this,{
              _item_size: 1,
              time: autohide_animation_time,
              transition: 'easeOutQuad',
              onUpdate: function () {
                   height = (this._nicons)*(this._item_size + this._spacing) + 2*this._spacing;
                   width = this._item_size + 4*this._spacing;
                   switch (position) {
                       case PositionMode.LEFT:
                              position_x=monitor.x-2*this._spacing;
                              break;
                       case PositionMode.RIGHT:
                       default:
                              position_x = monitor.x + (monitor.width-1-this._item_size-2*this._spacing);
                   }
                   this.actor.set_position (position_x,monitor.y+(monitor.height-height)/2);
                   this.actor.set_size(width,height);
              },
       });
       hideDock=true;
    }
}

function showDock_size () {
     let monitor = Main.layoutManager.primaryMonitor;
     let height = (this._nicons)*(this._item_size + this._spacing) + 2*this._spacing;
     let width = this._item_size + 4*this._spacing;
     let position_x = monitor.x;

     Tweener.addTween(this,{
             _item_size: dockicon_size,
             time: autohide_animation_time,
             transition: 'easeOutQuad',
             onUpdate: function () {
                height = (this._nicons)*(this._item_size + this._spacing) + 2*this._spacing;
                width = this._item_size + 4*this._spacing;
                switch (position) {
                   case PositionMode.LEFT:
                      position_x=monitor.x-2*this._spacing;
                      break;
                   case PositionMode.RIGHT:
                   default:
                      position_x=monitor.x + (monitor.width-this._item_size-2*this._spacing);
                }
                this.actor.set_position (position_x, monitor.y+(monitor.height-height)/2);
                this.actor.set_size(width,height);
             }
     });
     hideDock=false;
}

function initShowDock_size () {
        this._item_size=1;
        this._showDock();
}

function showEffectAddItem_size () {
        let primary = Main.layoutManager.primaryMonitor;
        let height = (this._nicons)*(this._item_size + this._spacing) + 2*this._spacing;
        let width = this._item_size + 4*this._spacing;

        Tweener.addTween(this.actor, {
                y: primary.y + (primary.height-height)/2,
                height: height,
                width: width,
                time: autohide_animation_time,
                transition: 'easeOutQuad'
        });
}

/**************************************************************************************/
/**** start rescale's Dock functions                                  *****************/
/**************************************************************************************/
function hideDock_scale () {
       this._item_size = dockicon_size;
       let monitor = Main.layoutManager.primaryMonitor;
       let cornerX = 0;
       let height = this._nicons*(this._item_size + this._spacing) + 2*this._spacing;
       let width = this._item_size + 4*this._spacing;

       switch (position) {
            case PositionMode.LEFT:
                cornerX=monitor.x;
                break;
            case PositionMode.RIGHT:
            default:
                cornerX = monitor.x + monitor.width-1;
        }

        if (hideable) {
               Tweener.addTween(this.actor,{
                       y: monitor.y + (monitor.height-height)/2,
                       x: cornerX,
                       height:height,
                       width: width,
                       scale_x: 0.025,
                       time: autohide_animation_time,
                       transition: 'easeOutQuad'
                     });
               hideDock=true;
        }
}

function showDock_scale () {
        this._item_size = dockicon_size;
        let monitor = Main.layoutManager.primaryMonitor;
        let position_x = monitor.x;
        let height = this._nicons*(this._item_size + this._spacing) + 2*this._spacing;
        let width = this._item_size + 4*this._spacing;

        switch (position) {
            case PositionMode.LEFT:
                position_x=monitor.x-2*this._spacing;
                break;
            case PositionMode.RIGHT:
            default:
                 position_x=monitor.x + (monitor.width-this._item_size-2*this._spacing);
        }
        Tweener.addTween(this.actor, {
                y: monitor.y + (monitor.height-height)/2,
                x: monitor.x + position_x,
                height: height,
                width: width,
                scale_x: 1,
                time: autohide_animation_time,
                transition: 'easeOutQuad'
        });
        hideDock=false;
}

function initShowDock_scale () {
        let primary = Main.layoutManager.primaryMonitor;
        let height = this._nicons*(this._item_size + this._spacing) + 2*this._spacing;
        let width = this._item_size + 4*this._spacing;

        this.actor.set_scale (0,0);
        this.actor.set_size (width,height);

        // set the position of the dock
        switch (position) {
                case PositionMode.LEFT:
                   this.actor.x = 0;
                   // effect of creation of the dock
                   Tweener.addTween(this.actor, {
                       x: primary.x-2*this._spacing,
                       y: primary.y + (primary.height-height)/2,
                       time: autohide_animation_time * 3,
                       transition: 'easeOutQuad'
                   });
                   break;
                case PositionMode.RIGHT:
                   default:
                   this.actor.x = primary.width-1;
                   // effect of creation of the dock
                   Tweener.addTween(this.actor, {
                      x: primary.x + primary.width-this._item_size- 2*this._spacing,
                      y: primary.y + (primary.height-height)/2,
                      time: autohide_animation_time * 3,
                      transition: 'easeOutQuad'
                   });
        }
        Tweener.addTween(this.actor,{
           scale_x: 1,
           scale_y: 1,
           time: autohide_animation_time * 3,
           transition: 'easeOutQuad'
        });
        hideDock=false;
}

function showEffectAddItem_scale () {
        let monitor = Main.layoutManager.primaryMonitor;
        let height = this._nicons*(this._item_size + this._spacing) + 2*this._spacing;
        let width = this._item_size + 4*this._spacing;

        Tweener.addTween(this.actor, {
                y: monitor.y + (monitor.height-height)/2,
                height: height,
                width: width,
                time: autohide_animation_time,
                transition: 'easeOutQuad'
        });
}

/**************************************************************************************/
/**** start move Dock functions                                       *****************/
/**************************************************************************************/
function hideDock_move () {
       this._item_size = dockicon_size;
       let monitor = Main.layoutManager.primaryMonitor;
       let cornerX = 0;
       let height = this._nicons*(this._item_size + this._spacing) + 2*this._spacing;
       let width = this._item_size + 4*this._spacing;

       switch (position) {
            case PositionMode.LEFT:
                cornerX= monitor.x - width + this._spacing;
                break;
            case PositionMode.RIGHT:
            default:
                cornerX = monitor.x + monitor.width - this._spacing;
        }

        if (hideable) {
               Tweener.addTween(this.actor,{
                       x: cornerX,
                       y: monitor.y + (monitor.height - height)/2,
                       width: width,
                       height: height,
                       time: autohide_animation_time,
                       transition: 'easeOutQuad'
                     });
               hideDock=true;
        }
}

function showDock_move () {
        this._item_size = dockicon_size;
        let monitor = Main.layoutManager.primaryMonitor;
        let position_x = monitor.x;
        let height = this._nicons*(this._item_size + this._spacing) + 2*this._spacing;
        let width = this._item_size + 4*this._spacing;

        switch (position) {
            case PositionMode.LEFT:
                position_x=monitor.x - 2*this._spacing;
                break;
            case PositionMode.RIGHT:
            default:
                 position_x=monitor.x + (monitor.width-this._item_size-2*this._spacing);
        }
        Tweener.addTween(this.actor, {
                x: position_x,
                y: monitor.y + (monitor.height - height)/2,
                width: width,
                height: height,
                time: autohide_animation_time,
                transition: 'easeOutQuad'
        });
        hideDock=false;
}

function initShowDock_move () {
    this._showDock();
}

function showEffectAddItem_move () {
        let monitor = Main.layoutManager.primaryMonitor;
        let height = this._nicons*(this._item_size + this._spacing) + 2*this._spacing;
        let width = this._item_size + 4*this._spacing;

        Tweener.addTween(this.actor, {
                y: monitor.y + (monitor.height-height)/2,
                height: height,
                width: width,
                time: autohide_animation_time,
                transition: 'easeOutQuad'
        });
}

function Dock() {
    this._init();
}

Dock.prototype = {
    _init : function() {
        this._placeholderText = null;
        this._menus = [];
        this._menuDisplays = [];

        this._favorites = [];

        // Load Settings
        this._settings = new Gio.Settings({ schema: DOCK_SETTINGS_SCHEMA });
        position = this._settings.get_enum(DOCK_POSITION_KEY);
        dockicon_size = this._settings.get_int(DOCK_SIZE_KEY);
        hideDock = hideable = this._settings.get_boolean(DOCK_HIDE_KEY);
        hideEffect = this._settings.get_enum(DOCK_EFFECTHIDE_KEY);
        autohide_animation_time = this._settings.get_double(DOCK_AUTOHIDE_ANIMATION_TIME_KEY);
        //global.log("POSITION: " + position);
        //global.log("dockicon_size: " + dockicon_size);


        this._spacing = 4;
        this._item_size = dockicon_size;
        this._nicons = 0;
        this._selectFunctionsHide ();

        this.actor = new St.BoxLayout({ name: 'dock', vertical: true, reactive: true });

        this._grid = new Shell.GenericContainer();
        this.actor.add(this._grid, { expand: true, y_align: St.Align.START });
        this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));

        this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
        this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
        this._grid.connect('allocate', Lang.bind(this, this._allocate));

        this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));

        this._tracker = Shell.WindowTracker.get_default();
        this._appSystem = Shell.AppSystem.get_default();

        this._installedChangedId = this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
        this._appFavoritesChangedId = AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
        this._appStateChangedId = this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));

        this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, function() {
            this.actor.hide();
        }));
        this._overviewHiddenId = Main.overview.connect('hidden', Lang.bind(this, function() {
            this.actor.show();
        }));
        Main.layoutManager.addChrome(this.actor);

        //hidden
        this._settings.connect('changed::'+DOCK_POSITION_KEY, Lang.bind(this, function (){
                if (!this._settings)
                    return;

                let primary = Main.layoutManager.primaryMonitor;
                position = this._settings.get_enum(DOCK_POSITION_KEY);
                this.actor.y=primary.y;
                this._redisplay();
        }));

        this._settings.connect('changed::'+DOCK_SIZE_KEY, Lang.bind(this, function (){
                if (!this._settings)
                    return;

                dockicon_size = this._settings.get_int(DOCK_SIZE_KEY);
                this._redisplay();
        }));

        this._settings.connect('changed::'+DOCK_HIDE_KEY, Lang.bind(this, function (){
                if (!this._settings)
                    return;

                hideable = this._settings.get_boolean(DOCK_HIDE_KEY);
                if (hideable){
                        hideDock=false;
                        this._hideDock();
                } else {
                        hideDock=true;
                        this._showDock();
                }
        }));

        this._settings.connect('changed::'+DOCK_EFFECTHIDE_KEY, Lang.bind(this, function () {
                if (!this._settings)
                    return;

                hideEffect = this._settings.get_enum(DOCK_EFFECTHIDE_KEY);

                switch (hideEffect) {
                        case AutoHideEffect.RESCALE:
                           this._item_size=dockicon_size;
                           break;
                        case AutoHideEffect.RESIZE:
                           this.actor.set_scale (1,1);
                           break;
                        case AutoHideEffect.MOVE:
                           ;
                }
                this.actor.disconnect(this._leave_event);
                this.actor.disconnect(this._enter_event);

                this._selectFunctionsHide ();

                this._leave_event = this.actor.connect('leave-event', Lang.bind(this, this._hideDock));
                this._enter_event = this.actor.connect('enter-event', Lang.bind(this, this._showDock));
                this._redisplay();
        }));

        this._settings.connect('changed::'+DOCK_AUTOHIDE_ANIMATION_TIME_KEY, Lang.bind(this,function (){
                if (!this._settings)
                    return;

                autohide_animation_time = this._settings.get_double(DOCK_AUTOHIDE_ANIMATION_TIME_KEY);
        }));

        this._leave_event = this.actor.connect('leave-event', Lang.bind(this, this._hideDock));
        this._enter_event = this.actor.connect('enter-event', Lang.bind(this, this._showDock));

        this._hideDock();
    },

    destroy: function() {
        if (this._installedChangedId) {
            this._appSystem.disconnect(this._installedChangedId);
            this._installedChangedId = 0;
        }

        if (this._appFavoritesChangedId) {
            AppFavorites.getAppFavorites().disconnect(this._appFavoritesChangedId);
            this._appFavoritesChangedId = 0;
        }

        if (this._appStateChangedId) {
            this._appSystem.disconnect(this._appStateChangedId);
            this._appStateChangedId = 0;
        }

        if (this._overviewShowingId) {
            Main.overview.disconnect(this._overviewShowingId);
            this._overviewShowingId = 0;
        }

        if (this._overviewHiddenId) {
            Main.overview.disconnect(this._overviewHiddenId);
            this._overviewHiddenId = 0;
        }

        this.actor.destroy();

        // Break reference cycles
        this._settings = null;
        this._appSystem = null;
        this._tracker = null;
    },

    // fuctions hide
    _restoreHideDock: function(){
        hideable = this._settings.get_boolean(DOCK_HIDE_KEY);
    },

    _disableHideDock: function (){
        hideable = false;
    },

    _selectFunctionsHide: function () {
        switch (hideEffect) {
        case AutoHideEffect.RESCALE:
            this._hideDock = hideDock_scale;
            this._showDock = showDock_scale;
            this._initShowDock = initShowDock_scale;
            this._showEffectAddItem = showEffectAddItem_scale;
            break;
        case AutoHideEffect.MOVE:
            this._hideDock = hideDock_move;
            this._showDock = showDock_move;
            this._initShowDock = initShowDock_move;
            this._showEffectAddItem = showEffectAddItem_move;
            break;
        case AutoHideEffect.RESIZE:
        default:
            this._hideDock = hideDock_size;
            this._showDock = showDock_size;
            this._initShowDock = initShowDock_size;
            this._showEffectAddItem = showEffectAddItem_size;
        }
    },

    _appIdListToHash: function(apps) {
        let ids = {};
        for (let i = 0; i < apps.length; i++)
            ids[apps[i].get_id()] = apps[i];
        return ids;
    },

    _queueRedisplay: function () {
        Main.queueDeferredWork(this._workId);
    },

    _redisplay: function () {
        this.removeAll();

        let favorites = AppFavorites.getAppFavorites().getFavoriteMap();

        let running = this._appSystem.get_running();
        let runningIds = this._appIdListToHash(running);

        let icons = 0;

        let nFavorites = 0;
        for (let id in favorites) {
            let app = favorites[id];
            let display = new DockIcon(app,this);
            this.addItem(display.actor);
            nFavorites++;
            icons++;
        }

        for (let i = 0; i < running.length; i++) {
            let app = running[i];
            if (app.get_id() in favorites)
                continue;
            let display = new DockIcon(app,this);
            icons++;
            this.addItem(display.actor);
        }
        this._nicons=icons;

        if (this._placeholderText) {
            this._placeholderText.destroy();
            this._placeholderText = null;
        }

        if (running.length == 0 && nFavorites == 0) {
            this._placeholderText = new St.Label({ text: _("Drag here to add favorites") });
            this.actor.add_actor(this._placeholderText);
        }

        let primary = Main.layoutManager.primaryMonitor;
        let height = (icons)*(this._item_size + this._spacing) + 2*this._spacing;
        let width = this._item_size + 4*this._spacing;

        if (this.actor.y != primary.y) {
                if (hideable && hideDock) {
                        this._hideDock();
                } else {
                   if (dockicon_size == this._item_size) {
                        // only add/delete icon
                        this._showEffectAddItem ();
                    } else {
                        // change size icon
                        this._showDock ();
                    }
                }
        } else {
                // effect of creation
                this._initShowDock ();
        }
    },

    _getPreferredWidth: function (grid, forHeight, alloc) {
        alloc.min_size = this._item_size;
        alloc.natural_size = this._item_size + this._spacing;
    },

    _getPreferredHeight: function (grid, forWidth, alloc) {
        let children = this._grid.get_children();
        let nRows = children.length;
        let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
        let height = nRows * this._item_size + totalSpacing;
        alloc.min_size = height;
        alloc.natural_size = height;
    },

    _allocate: function (grid, box, flags) {
        let children = this._grid.get_children();

        let x = box.x1 + this._spacing;
        if (position == PositionMode.LEFT)
            x = box.x1 + 2*this._spacing;
        let y = box.y1 + this._spacing;

        for (let i = 0; i < children.length; i++) {
            let childBox = new Clutter.ActorBox();
            childBox.x1 = x;
            childBox.y1 = y;
            childBox.x2 = childBox.x1 + this._item_size;
            childBox.y2 = childBox.y1 + this._item_size;
            children[i].allocate(childBox, flags);
            y += this._item_size + this._spacing;
        }
    },


    _onStyleChanged: function() {
        let themeNode = this.actor.get_theme_node();
        let [success, len] = themeNode.get_length('spacing', false);
        if (success)
            this._spacing = len;
        [success, len] = themeNode.get_length('-shell-grid-item-size', false);
        if (success)
            this._item_size = len;
        this._grid.queue_relayout();
    },

    removeAll: function () {
        this._grid.get_children().forEach(Lang.bind(this, function (child) {
            child.destroy();
        }));
    },

    addItem: function(actor) {
        this._grid.add_actor(actor);
    }
};
Signals.addSignalMethods(Dock.prototype);

function DockIcon(app, dock) {
    this._init(app, dock);
}

DockIcon.prototype = {
    _init : function(app, dock) {
        this.app = app;
        this.actor = new St.Button({ style_class: 'dock-app',
                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
                                     reactive: true,
                                     x_fill: true,
                                     y_fill: true });
        this.actor._delegate = this;
        this.actor.set_size(dockicon_size, dockicon_size);

        this._icon = this.app.create_icon_texture(dockicon_size);
        this.actor.set_child(this._icon);

        this.actor.connect('clicked', Lang.bind(this, this._onClicked));

        this._menu = null;
        this._menuManager = new PopupMenu.PopupMenuManager(this);

        this._has_focus = false;

        let tracker = Shell.WindowTracker.get_default();
        tracker.connect('notify::focus-app', Lang.bind(this, this._onStateChanged));

        this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
        this.actor.connect('notify::hover', Lang.bind(this, this._hoverChanged));

        this._menuTimeoutId = 0;
        this._stateChangedId = this.app.connect('notify::state',
                                                Lang.bind(this, this._onStateChanged));
        this._onStateChanged();
        this._dock=dock;
    },

    _onDestroy: function() {
        if (this._stateChangedId > 0)
            this.app.disconnect(this._stateChangedId);
        this._stateChangedId = 0;
        this._removeMenuTimeout();
    },

    _removeMenuTimeout: function() {
        if (this._menuTimeoutId > 0) {
            Mainloop.source_remove(this._menuTimeoutId);
            this._menuTimeoutId = 0;
        }
    },

    _hoverChanged: function(actor) {
        if (actor != this.actor)
            this._has_focus = false;
        else
            this._has_focus = true;
        return false;
    },

    _onStateChanged: function() {
        let tracker = Shell.WindowTracker.get_default();
        let focusedApp = tracker.focus_app;
        if (this.app.state != Shell.AppState.STOPPED) {
            this.actor.add_style_class_name('running');
            if (this.app == focusedApp) {
                this.actor.add_style_class_name('focused');
            } else {
                this.actor.remove_style_class_name('focused');
            }
        } else {
            this.actor.remove_style_class_name('focused');
            this.actor.remove_style_class_name('running');
        }
    },

    _onButtonPress: function(actor, event) {
        let button = event.get_button();
        if (button == 1) {
            this._removeMenuTimeout();
            this._menuTimeoutId = Mainloop.timeout_add(AppDisplay.MENU_POPUP_TIMEOUT, Lang.bind(this, function() {
                this.popupMenu();
            }));
        } else if (button == 3) {
            this.popupMenu();
        }
    },

    _onClicked: function(actor, button) {
        this._removeMenuTimeout();

        if (button == 1) {
            this._onActivate(Clutter.get_current_event());
        } else if (button == 2) {
            // Last workspace is always empty
            let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
            launchWorkspace.activate(global.get_current_time());
            this.emit('launching');
            this.app.open_new_window(-1);
        }
        return false;
    },

    getId: function() {
        return this.app.get_id();
    },

    popupMenu: function() {
        this._removeMenuTimeout();
        this.actor.fake_release();

        this._dock._disableHideDock();

        if (!this._menu) {
            this._menu = new DockIconMenu(this);
            this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
                this.activateWindow(window);
            }));
            this._menu.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
                if (!isPoppedUp){
                    //Restore value of autohidedock
                    this._dock._restoreHideDock();
                    this._dock._hideDock();

                    this._onMenuPoppedDown();
                }
            }));

            this._menuManager.addMenu(this._menu, true);
        }

        this._menu.popup();

        return false;
    },

    activateWindow: function(metaWindow) {
        if (metaWindow) {
            this._didActivateWindow = true;
            Main.activateWindow(metaWindow);
        }
    },

    setSelected: function (isSelected) {
        this._selected = isSelected;
        if (this._selected)
            this.actor.add_style_class_name('selected');
        else
            this.actor.remove_style_class_name('selected');
    },

    _onMenuPoppedDown: function() {
        this.actor.sync_hover();
    },

    _getRunning: function() {
        return this.app.state != Shell.AppState.STOPPED;
    },

    _onActivate: function (event) {
        this.emit('launching');
        let modifiers = Shell.get_event_state(event);

        if (modifiers & Clutter.ModifierType.CONTROL_MASK
            && this.app.state == Shell.AppState.RUNNING) {
            let current_workspace = global.screen.get_active_workspace().index();
            this.app.open_new_window(current_workspace);
        } else {
            let tracker = Shell.WindowTracker.get_default();
            let focusedApp = tracker.focus_app;

            if (this.app == focusedApp) {
                let windows = this.app.get_windows();
                let current_workspace = global.screen.get_active_workspace();
                for (let i = 0; i < windows.length; i++) {
                    let w = windows[i];
                    if (w.get_workspace() == current_workspace)
                        w.minimize();
                }
            } else {
                this.app.activate(-1);
            }
        }
        Main.overview.hide();
    },

    shellWorkspaceLaunch : function() {
        this.app.open_new_window();
    }
};
Signals.addSignalMethods(DockIcon.prototype);

function DockIconMenu(source) {
    this._init(source);
}

DockIconMenu.prototype = {
    __proto__: AppDisplay.AppIconMenu.prototype,

    _init: function(source) {
        switch (position) {
            case PositionMode.LEFT:
                PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.LEFT, 0);
                break;
            case PositionMode.RIGHT:
            default:
                PopupMenu.PopupMenu.prototype._init.call(this, source.actor, St.Align.MIDDLE, St.Side.RIGHT, 0);
        }

        this._source = source;

        this.connect('activate', Lang.bind(this, this._onActivate));

        this.actor.add_style_class_name('dock-menu');

        // Chain our visibility and lifecycle to that of the source
        source.actor.connect('notify::mapped', Lang.bind(this, function () {
            if (!source.actor.mapped)
                this.close();
        }));
        source.actor.connect('destroy', Lang.bind(this, function () { this.actor.destroy(); }));

        Main.layoutManager.addChrome(this.actor);
    },

    _redisplay: function() {
        this.removeAll();

        let windows = this._source.app.get_windows();

        // Display the app windows menu items and the separator between windows
        // of the current desktop and other windows.
        let activeWorkspace = global.screen.get_active_workspace();
        let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;

        for (let i = 0; i < windows.length; i++) {
            if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
                this._appendSeparator();
                separatorShown = true;
            }
            let item = this._appendMenuItem(windows[i].title);
            item._window = windows[i];
        }

        if (windows.length > 0)
            this._appendSeparator();

        let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());

        this._newWindowMenuItem = windows.length > 0 ? this._appendMenuItem(_("New Window")) : null;

        this._quitAppMenuItem = windows.length >0 ? this._appendMenuItem(_("Quit Application")) : null;

        if (windows.length > 0)
            this._appendSeparator();
        this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ?
                                                            _("Remove from Favorites")
                                                            : _("Add to Favorites"));

        this._highlightedItem = null;
    },

    _onActivate: function (actor, child) {
        if (child._window) {
            let metaWindow = child._window;
            this.emit('activate-window', metaWindow);
        } else if (child == this._newWindowMenuItem) {
            let current_workspace = global.screen.get_active_workspace().index();
            this._source.app.open_new_window(current_workspace);
            this.emit('activate-window', null);
        } else if (child == this._quitAppMenuItem) {
            this._source.app.request_quit();
        } else if (child == this._toggleFavoriteMenuItem) {
            let favs = AppFavorites.getAppFavorites();
            let isFavorite = favs.isFavorite(this._source.app.get_id());
            if (isFavorite)
                favs.removeFavorite(this._source.app.get_id());
            else
                favs.addFavorite(this._source.app.get_id());
        }
        this.close();
    }
}

function init(extensionMeta) {
    imports.gettext.bindtextdomain('gnome-shell-extensions', extensionMeta.localedir);
}

let dock;

function enable() {
    dock = new Dock();
}

function disable() {
    dock.destroy();
    dock = null;
}
