"use strict";
// *****************************************************************************
// Copyright (C) 2017 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomTitleWidget = exports.ElectronMenuContribution = exports.CustomTitleWidgetFactory = exports.ElectronMenus = exports.ElectronCommands = void 0;
const inversify_1 = require("inversify");
const common_1 = require("../../common");
const browser_1 = require("../../browser");
const electron_main_menu_factory_1 = require("./electron-main-menu-factory");
const frontend_application_state_1 = require("../../browser/frontend-application-state");
const frontend_application_config_provider_1 = require("../../browser/frontend-application-config-provider");
const electron_window_preferences_1 = require("../window/electron-window-preferences");
const browser_menu_plugin_1 = require("../../browser/menu/browser-menu-plugin");
const window_service_1 = require("../../browser/window/window-service");
const window_title_service_1 = require("../../browser/window/window-title-service");
require("../../../src/electron-browser/menu/electron-menu-style.css");
var ElectronCommands;
(function (ElectronCommands) {
    ElectronCommands.TOGGLE_DEVELOPER_TOOLS = common_1.Command.toDefaultLocalizedCommand({
        id: 'theia.toggleDevTools',
        label: 'Toggle Developer Tools'
    });
    ElectronCommands.RELOAD = common_1.Command.toDefaultLocalizedCommand({
        id: 'view.reload',
        label: 'Reload Window'
    });
    ElectronCommands.ZOOM_IN = common_1.Command.toDefaultLocalizedCommand({
        id: 'view.zoomIn',
        label: 'Zoom In'
    });
    ElectronCommands.ZOOM_OUT = common_1.Command.toDefaultLocalizedCommand({
        id: 'view.zoomOut',
        label: 'Zoom Out'
    });
    ElectronCommands.RESET_ZOOM = common_1.Command.toDefaultLocalizedCommand({
        id: 'view.resetZoom',
        label: 'Reset Zoom'
    });
    ElectronCommands.CLOSE_WINDOW = common_1.Command.toDefaultLocalizedCommand({
        id: 'close.window',
        label: 'Close Window'
    });
    ElectronCommands.TOGGLE_FULL_SCREEN = common_1.Command.toDefaultLocalizedCommand({
        id: 'workbench.action.toggleFullScreen',
        category: browser_1.CommonCommands.VIEW_CATEGORY,
        label: 'Toggle Full Screen'
    });
})(ElectronCommands = exports.ElectronCommands || (exports.ElectronCommands = {}));
var ElectronMenus;
(function (ElectronMenus) {
    ElectronMenus.VIEW_WINDOW = [...browser_1.CommonMenus.VIEW, 'window'];
    ElectronMenus.VIEW_ZOOM = [...browser_1.CommonMenus.VIEW_APPEARANCE_SUBMENU, '4_appearance_submenu_zoom'];
})(ElectronMenus = exports.ElectronMenus || (exports.ElectronMenus = {}));
(function (ElectronMenus) {
    ElectronMenus.HELP_TOGGLE = [...browser_1.CommonMenus.HELP, 'z_toggle'];
})(ElectronMenus = exports.ElectronMenus || (exports.ElectronMenus = {}));
(function (ElectronMenus) {
    ElectronMenus.FILE_CLOSE = [...browser_1.CommonMenus.FILE_CLOSE, 'window-close'];
})(ElectronMenus = exports.ElectronMenus || (exports.ElectronMenus = {}));
exports.CustomTitleWidgetFactory = Symbol('CustomTitleWidgetFactory');
let ElectronMenuContribution = class ElectronMenuContribution extends browser_menu_plugin_1.BrowserMenuBarContribution {
    constructor(factory) {
        super(factory);
        this.factory = factory;
        this.titleBarStyleChangeFlag = false;
    }
    onStart(app) {
        this.handleTitleBarStyling(app);
        if (common_1.isOSX) {
            this.attachWindowFocusListener(app);
        }
        // Make sure the application menu is complete, once the frontend application is ready.
        // https://github.com/theia-ide/theia/issues/5100
        let onStateChange = undefined;
        const stateServiceListener = (state) => {
            if (state === 'closing_window') {
                if (!!onStateChange) {
                    onStateChange.dispose();
                }
            }
        };
        onStateChange = this.stateService.onStateChanged(stateServiceListener);
        this.shell.mainPanel.onDidToggleMaximized(() => {
            this.handleToggleMaximized();
        });
        this.shell.bottomPanel.onDidToggleMaximized(() => {
            this.handleToggleMaximized();
        });
        this.attachMenuBarVisibilityListener();
    }
    attachWindowFocusListener(app) {
        // OSX: Recreate the menus when changing windows.
        // OSX only has one menu bar for all windows, so we need to swap
        // between them as the user switches windows.
        const disposeHandler = window.electronTheiaCore.onWindowEvent('focus', () => {
            this.setMenu(app);
        });
        window.addEventListener('unload', () => disposeHandler.dispose());
    }
    attachMenuBarVisibilityListener() {
        this.preferenceService.onPreferenceChanged(e => {
            if (e.preferenceName === 'window.menuBarVisibility') {
                this.handleFullScreen(e.newValue);
            }
        });
    }
    handleTitleBarStyling(app) {
        this.hideTopPanel(app);
        window.electronTheiaCore.getTitleBarStyleAtStartup().then(style => {
            this.titleBarStyle = style;
            this.setMenu(app);
            this.preferenceService.ready.then(() => {
                this.preferenceService.set('window.titleBarStyle', this.titleBarStyle, browser_1.PreferenceScope.User);
            });
        });
        this.preferenceService.ready.then(() => {
            window.electronTheiaCore.setMenuBarVisible(['classic', 'visible'].includes(this.preferenceService.get('window.menuBarVisibility', 'classic')));
        });
        this.preferenceService.onPreferenceChanged(change => {
            if (change.preferenceName === 'window.titleBarStyle') {
                if (this.titleBarStyleChangeFlag && this.titleBarStyle !== change.newValue) {
                    window.electronTheiaCore.setTitleBarStyle(change.newValue);
                    this.handleRequiredRestart();
                }
                this.titleBarStyleChangeFlag = true;
            }
        });
    }
    handleToggleMaximized() {
        const preference = this.preferenceService.get('window.menuBarVisibility');
        if (preference === 'classic') {
            this.factory.setMenuBar();
        }
    }
    /**
     * Hides the `theia-top-panel` depending on the selected `titleBarStyle`.
     * The `theia-top-panel` is used as the container of the main, application menu-bar for the
     * browser. Native Electron has it's own.
     * By default, this method is called on application `onStart`.
     */
    hideTopPanel(app) {
        const itr = app.shell.children();
        let child = itr.next();
        while (child) {
            // Top panel for the menu contribution is not required for native Electron title bar.
            if (child.id === 'theia-top-panel') {
                child.setHidden(this.titleBarStyle !== 'custom');
                break;
            }
            else {
                child = itr.next();
            }
        }
    }
    setMenu(app, electronMenu = this.factory.createElectronMenuBar()) {
        if (!common_1.isOSX) {
            this.hideTopPanel(app);
            if (this.titleBarStyle === 'custom' && !this.menuBar) {
                this.createCustomTitleBar(app);
                return;
            }
        }
        window.electronTheiaCore.setMenu(electronMenu);
    }
    createCustomTitleBar(app) {
        const dragPanel = new browser_1.Widget();
        dragPanel.id = 'theia-drag-panel';
        app.shell.addWidget(dragPanel, { area: 'top' });
        this.appendMenu(app.shell);
        this.createCustomTitleWidget(app);
        const controls = document.createElement('div');
        controls.id = 'window-controls';
        controls.append(this.createControlButton('minimize', () => window.electronTheiaCore.minimize()), this.createControlButton('maximize', () => window.electronTheiaCore.maximize()), this.createControlButton('restore', () => window.electronTheiaCore.unMaximize()), this.createControlButton('close', () => window.electronTheiaCore.close()));
        app.shell.topPanel.node.append(controls);
        this.handleWindowControls();
    }
    createCustomTitleWidget(app) {
        const titleWidget = this.customTitleWidgetFactory();
        if (titleWidget) {
            app.shell.addWidget(titleWidget, { area: 'top' });
        }
    }
    handleWindowControls() {
        toggleControlButtons();
        window.electronTheiaCore.onWindowEvent('maximize', toggleControlButtons);
        window.electronTheiaCore.onWindowEvent('unmaximize', toggleControlButtons);
        function toggleControlButtons() {
            if (window.electronTheiaCore.isMaximized()) {
                document.body.classList.add('maximized');
            }
            else {
                document.body.classList.remove('maximized');
            }
        }
    }
    createControlButton(id, handler) {
        const button = document.createElement('div');
        button.id = `${id}-button`;
        button.className = `control-button ${(0, browser_1.codicon)(`chrome-${id}`)}`;
        button.addEventListener('click', handler);
        return button;
    }
    async handleRequiredRestart() {
        const msgNode = document.createElement('div');
        const message = document.createElement('p');
        message.textContent = common_1.nls.localizeByDefault('A setting has changed that requires a restart to take effect.');
        const detail = document.createElement('p');
        detail.textContent = common_1.nls.localizeByDefault('Press the restart button to restart {0} and enable the setting.', frontend_application_config_provider_1.FrontendApplicationConfigProvider.get().applicationName);
        msgNode.append(message, detail);
        const restart = common_1.nls.localizeByDefault('Restart');
        const dialog = new browser_1.ConfirmDialog({
            title: restart,
            msg: msgNode,
            ok: restart,
            cancel: browser_1.Dialog.CANCEL
        });
        if (await dialog.open()) {
            this.windowService.setSafeToShutDown();
            window.electronTheiaCore.restart();
        }
    }
    registerCommands(registry) {
        registry.registerCommand(ElectronCommands.TOGGLE_DEVELOPER_TOOLS, {
            execute: () => {
                window.electronTheiaCore.toggleDevTools();
            }
        });
        registry.registerCommand(ElectronCommands.RELOAD, {
            execute: () => this.windowService.reload()
        });
        registry.registerCommand(ElectronCommands.CLOSE_WINDOW, {
            execute: () => window.electronTheiaCore.close()
        });
        registry.registerCommand(ElectronCommands.ZOOM_IN, {
            execute: async () => {
                const curentLevel = await window.electronTheiaCore.getZoomLevel();
                // When starting at a level that is not a multiple of 0.5, increment by at most 0.5 to reach the next highest multiple of 0.5.
                let zoomLevel = (Math.floor(curentLevel / electron_window_preferences_1.ZoomLevel.VARIATION) * electron_window_preferences_1.ZoomLevel.VARIATION) + electron_window_preferences_1.ZoomLevel.VARIATION;
                if (zoomLevel > electron_window_preferences_1.ZoomLevel.MAX) {
                    zoomLevel = electron_window_preferences_1.ZoomLevel.MAX;
                    return;
                }
                ;
                this.preferenceService.set('window.zoomLevel', zoomLevel, browser_1.PreferenceScope.User);
            }
        });
        registry.registerCommand(ElectronCommands.ZOOM_OUT, {
            execute: async () => {
                const curentLevel = await window.electronTheiaCore.getZoomLevel();
                // When starting at a level that is not a multiple of 0.5, decrement by at most 0.5 to reach the next lowest multiple of 0.5.
                let zoomLevel = (Math.ceil(curentLevel / electron_window_preferences_1.ZoomLevel.VARIATION) * electron_window_preferences_1.ZoomLevel.VARIATION) - electron_window_preferences_1.ZoomLevel.VARIATION;
                if (zoomLevel < electron_window_preferences_1.ZoomLevel.MIN) {
                    zoomLevel = electron_window_preferences_1.ZoomLevel.MIN;
                    return;
                }
                ;
                this.preferenceService.set('window.zoomLevel', zoomLevel, browser_1.PreferenceScope.User);
            }
        });
        registry.registerCommand(ElectronCommands.RESET_ZOOM, {
            execute: () => this.preferenceService.set('window.zoomLevel', electron_window_preferences_1.ZoomLevel.DEFAULT, browser_1.PreferenceScope.User)
        });
        registry.registerCommand(ElectronCommands.TOGGLE_FULL_SCREEN, {
            isEnabled: () => window.electronTheiaCore.isFullScreenable(),
            isVisible: () => window.electronTheiaCore.isFullScreenable(),
            execute: () => this.toggleFullScreen()
        });
    }
    registerKeybindings(registry) {
        registry.registerKeybindings({
            command: ElectronCommands.TOGGLE_DEVELOPER_TOOLS.id,
            keybinding: 'ctrlcmd+alt+i'
        }, {
            command: ElectronCommands.RELOAD.id,
            keybinding: 'ctrlcmd+r'
        }, {
            command: ElectronCommands.ZOOM_IN.id,
            keybinding: 'ctrlcmd+='
        }, {
            command: ElectronCommands.ZOOM_IN.id,
            keybinding: 'ctrlcmd+add'
        }, {
            command: ElectronCommands.ZOOM_OUT.id,
            keybinding: 'ctrlcmd+subtract'
        }, {
            command: ElectronCommands.ZOOM_OUT.id,
            keybinding: 'ctrlcmd+-'
        }, {
            command: ElectronCommands.RESET_ZOOM.id,
            keybinding: 'ctrlcmd+0'
        }, {
            command: ElectronCommands.CLOSE_WINDOW.id,
            keybinding: (common_1.isOSX ? 'cmd+shift+w' : (common_1.isWindows ? 'ctrl+w' : /* Linux */ 'ctrl+q'))
        }, {
            command: ElectronCommands.TOGGLE_FULL_SCREEN.id,
            keybinding: common_1.isOSX ? 'ctrl+ctrlcmd+f' : 'f11'
        });
    }
    registerMenus(registry) {
        registry.registerMenuAction(ElectronMenus.HELP_TOGGLE, {
            commandId: ElectronCommands.TOGGLE_DEVELOPER_TOOLS.id
        });
        registry.registerMenuAction(ElectronMenus.VIEW_WINDOW, {
            commandId: ElectronCommands.RELOAD.id,
            order: 'z0'
        });
        registry.registerMenuAction(ElectronMenus.VIEW_ZOOM, {
            commandId: ElectronCommands.ZOOM_IN.id,
            order: 'z1'
        });
        registry.registerMenuAction(ElectronMenus.VIEW_ZOOM, {
            commandId: ElectronCommands.ZOOM_OUT.id,
            order: 'z2'
        });
        registry.registerMenuAction(ElectronMenus.VIEW_ZOOM, {
            commandId: ElectronCommands.RESET_ZOOM.id,
            order: 'z3'
        });
        registry.registerMenuAction(ElectronMenus.FILE_CLOSE, {
            commandId: ElectronCommands.CLOSE_WINDOW.id,
        });
        registry.registerMenuAction(browser_1.CommonMenus.VIEW_APPEARANCE_SUBMENU_SCREEN, {
            commandId: ElectronCommands.TOGGLE_FULL_SCREEN.id,
            label: common_1.nls.localizeByDefault('Full Screen'),
            order: '0'
        });
    }
    toggleFullScreen() {
        window.electronTheiaCore.toggleFullScreen();
        const menuBarVisibility = this.preferenceService.get('window.menuBarVisibility', 'classic');
        this.handleFullScreen(menuBarVisibility);
    }
    handleFullScreen(menuBarVisibility) {
        const shouldShowTop = !window.electronTheiaCore.isFullScreen() || menuBarVisibility === 'visible';
        if (this.titleBarStyle === 'native') {
            window.electronTheiaCore.setMenuBarVisible(shouldShowTop);
        }
        else if (shouldShowTop) {
            this.shell.topPanel.show();
        }
        else {
            this.shell.topPanel.hide();
        }
    }
};
__decorate([
    (0, inversify_1.inject)(frontend_application_state_1.FrontendApplicationStateService),
    __metadata("design:type", frontend_application_state_1.FrontendApplicationStateService)
], ElectronMenuContribution.prototype, "stateService", void 0);
__decorate([
    (0, inversify_1.inject)(window_service_1.WindowService),
    __metadata("design:type", Object)
], ElectronMenuContribution.prototype, "windowService", void 0);
__decorate([
    (0, inversify_1.inject)(exports.CustomTitleWidgetFactory),
    __metadata("design:type", Function)
], ElectronMenuContribution.prototype, "customTitleWidgetFactory", void 0);
ElectronMenuContribution = __decorate([
    (0, inversify_1.injectable)(),
    __param(0, (0, inversify_1.inject)(electron_main_menu_factory_1.ElectronMainMenuFactory)),
    __metadata("design:paramtypes", [electron_main_menu_factory_1.ElectronMainMenuFactory])
], ElectronMenuContribution);
exports.ElectronMenuContribution = ElectronMenuContribution;
let CustomTitleWidget = class CustomTitleWidget extends browser_1.Widget {
    constructor() {
        super();
        this.id = 'theia-custom-title';
    }
    init() {
        this.updateTitle(this.windowTitleService.title);
        this.windowTitleService.onDidChangeTitle(title => {
            this.updateTitle(title);
        });
    }
    onResize(msg) {
        this.adjustTitleToCenter();
        super.onResize(msg);
    }
    onAfterShow(msg) {
        this.adjustTitleToCenter();
        super.onAfterShow(msg);
    }
    updateTitle(title) {
        this.node.textContent = title;
        this.adjustTitleToCenter();
    }
    adjustTitleToCenter() {
        const menubar = this.electronMenuContribution.menuBar;
        if (menubar) {
            const titleWidth = this.node.clientWidth;
            const margin = 16;
            const leftMarker = menubar.node.offsetLeft + menubar.node.clientWidth + margin;
            const panelWidth = this.applicationShell.topPanel.node.clientWidth;
            const controlsWidth = 48 * 3; // Each window button has a width of 48px
            const rightMarker = panelWidth - controlsWidth - margin;
            let hidden = false;
            let relative = false;
            this.node.style.left = '50%';
            // The title has not enough space between the menu and the window controls
            // So we simply hide it
            if (rightMarker - leftMarker < titleWidth) {
                hidden = true;
            }
            else if ((panelWidth - titleWidth) / 2 < leftMarker || (panelWidth + titleWidth) / 2 > rightMarker) {
                // This indicates that the title has either hit the left (menu) or right (window controls) marker
                relative = true;
                this.node.style.left = `${leftMarker + (rightMarker - leftMarker - titleWidth) / 2}px`;
            }
            this.node.classList.toggle('hidden', hidden);
            this.node.classList.toggle('relative', relative);
        }
    }
};
__decorate([
    (0, inversify_1.inject)(ElectronMenuContribution),
    __metadata("design:type", ElectronMenuContribution)
], CustomTitleWidget.prototype, "electronMenuContribution", void 0);
__decorate([
    (0, inversify_1.inject)(window_title_service_1.WindowTitleService),
    __metadata("design:type", window_title_service_1.WindowTitleService)
], CustomTitleWidget.prototype, "windowTitleService", void 0);
__decorate([
    (0, inversify_1.inject)(browser_1.ApplicationShell),
    __metadata("design:type", browser_1.ApplicationShell)
], CustomTitleWidget.prototype, "applicationShell", void 0);
__decorate([
    (0, inversify_1.postConstruct)(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", void 0)
], CustomTitleWidget.prototype, "init", null);
CustomTitleWidget = __decorate([
    (0, inversify_1.injectable)(),
    __metadata("design:paramtypes", [])
], CustomTitleWidget);
exports.CustomTitleWidget = CustomTitleWidget;
//# sourceMappingURL=electron-menu-contribution.js.map