#!/usr/bin/env python3

# Copyright (C) 2007-2014 by Clement Lefebvre <root@linuxmint.com>
# Copyright (C) 2015-2017 Martin Wimpress <code@ubuntu-mate.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

import errno
import gettext
import getpass
import gi
import glob
import mmap
import os
import platform
import psutil
import setproctitle
import shutil
import signal
import subprocess
import sysconfig
import string
import sys
import time

from subprocess import DEVNULL, PIPE

from gi.repository import GLib

gi.require_version('Gdk', '3.0')
from gi.repository import Gdk
from gi.repository import GdkPixbuf

gi.require_version('GdkX11', '3.0')
from gi.repository import GdkX11
from gi.repository import Gio
from gi.repository import GObject

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

gi.require_version('Notify', '0.7')
from gi.repository import Notify

# Workaround introspection bug, gnome bug 622084
signal.signal(signal.SIGINT, signal.SIG_DFL)

__VERSION__ = '17.10.15'

__HUD__ = """[Desktop Entry]
Type=Application
Name=HUD
Exec=/usr/lib/mate-hud/mate-hud
StartupNotify=false
Terminal=false
OnlyShowIn=MATE
X-GNOME-Autostart-enabled=true
X-MATE-Autostart-enabled=true
X-GNOME-Autostart-Phase=Applications
X-MATE-Autostart-Phase=Applications"""

__TILDA__ = """[Desktop Entry]
Name=Tilda
Exec=tilda
Icon=utilities-terminal
Terminal=false
Type=Application
Categories=GNOME;GTK;System;Utility;TerminalEmulator;
X-GNOME-Autostart-enabled=true
X-MATE-Autostart-enabled=true"""

__APPLET_SOUND__ = """[Desktop Entry]
Name=Volume Control
Comment=Show desktop volume control
Icon=multimedia-volume-control
Exec=mate-volume-control-applet
Terminal=false
Type=Application
Categories=AudioVideo;Mixer;Settings;HardwareSettings;
Keywords=MATE;volume;control;mixer;settings;sound;
NoDisplay=true
OnlyShowIn=MATE;
X-MATE-Bugzilla-Bugzilla=MATE
X-MATE-Bugzilla-Product=mate-media
X-MATE-Bugzilla-Component=mate-volume-control
# See http://bugzilla.gnome.org/show_bug.cgi?id=568320
#X-MATE-Autostart-Phase=Panel
X-MATE-Autostart-Notify=true
"""

__INDICATOR_APPLICATION__ = """[Desktop Entry]
Type=Application
Name=Indicator Application
Exec=/usr/lib/MULTIARCH/indicator-application/indicator-application-service
StartupNotify=false
Terminal=false
OnlyShowIn=MATE
X-GNOME-Autostart-enabled=true
X-MATE-Autostart-enabled=true
"""

__INDICATOR_SOUND__ = """[Desktop Entry]
Type=Application
Name=Indicator Sound
Exec=/usr/lib/MULTIARCH/indicator-sound/indicator-sound-service
StartupNotify=false
Terminal=false
OnlyShowIn=MATE
X-GNOME-Autostart-enabled=true
X-MATE-Autostart-enabled=true
"""

__INDICATOR_MESSAGES__ = """[Desktop Entry]
Type=Application
Name=Indicator Messages
Exec=/usr/lib/MULTIARCH/indicator-messages/indicator-messages-service
StartupNotify=false
Terminal=false
OnlyShowIn=MATE
X-GNOME-Autostart-enabled=true
X-MATE-Autostart-enabled=true
"""

__INDICATOR_POWER__ = """[Desktop Entry]
Type=Application
Name=Indicator Power
Exec=/usr/lib/MULTIARCH/indicator-power/indicator-power-service
StartupNotify=false
Terminal=false
OnlyShowIn=MATE
X-GNOME-Autostart-enabled=true
X-MATE-Autostart-enabled=true
"""

__INDICATOR_SESSION__ = """[Desktop Entry]
Type=Application
Name=Indicator Session
Exec=/usr/lib/MULTIARCH/indicator-session/indicator-session-service
StartupNotify=false
Terminal=false
OnlyShowIn=MATE
X-GNOME-Autostart-enabled=true
X-MATE-Autostart-enabled=true
"""

__TRADITIONAL_BUTTONS__ = "menu:minimize,maximize,close"
__CONTEMPORARY_BUTTONS__ = "close,minimize,maximize:menu"

# i18n
gettext.install('mate-tweak', os.path.join('/','usr','share','locale'))

class SidePage:
    def __init__(self, notebook_index, name, icon):
        self.notebook_index = notebook_index
        self.name = name
        self.icon = icon

class MateTweak:
    def on_checkbox_toggled(self, widget, schema_id, key):
        # If Maximus is bt_booleing tweaked, replace it to make the settings effective.
        if schema_id == "org.mate.maximus" and (key == "undecorate" or key == "no-maximize"):
            subprocess.Popen(['mate-maximus', '--replace'], stdout=DEVNULL, stderr=DEVNULL)

            # When changing undecorate state replace the window manager
            # to ensure there are no rendering or strut issues.
            if key == 'undecorate':
                self.replace_windowmanager(self.current_wm)

        elif (key == "reduced-resources"):
            if ('marco' in self.current_wm):
                marco_reduced = self.get_bool('org.mate.Marco.general', None, key)
                self.set_bool('org.mate.Marco.general', None, 'side-by-side-tiling', marco_reduced)
                self.builder.get_object('checkbox_snapping').props.sensitive = marco_reduced
            elif ('metacity' in self.current_wm):
                metacity_reduced = self.get_bool('org.gnome.metacity', None, key)
                self.set_bool('org.gnome.metacity', None, 'edge-tiling', metacity_reduced)
                self.builder.get_object('checkbox_snapping').props.sensitive = metacity_reduced

        # If desktop icons are shown or hidden, enable or not further modifications
        elif schema_id == "org.mate.background":
            self.toggle_desktop_icons_sensitiveness()

    def set_string(self, schema, path, key, value):
        if path:
            settings = Gio.Settings.new_with_path(schema, path)
        else:
            settings = Gio.Settings.new(schema)
        try:
            settings.set_string(key, value)
        except:
            print('Unable set ' + key + ' with ' + value + ' in ' + schema)
            pass

    def get_string(self, schema, path, key):
        if path:
            settings = Gio.Settings.new_with_path(schema, path)
        else:
            settings = Gio.Settings.new(schema)
        return settings.get_string(key)

    def get_int(self, schema, path, key):
        if path:
            settings = Gio.Settings.new_with_path(schema, path)
        else:
            settings = Gio.Settings.new(schema)
        return settings.get_int(key)

    def set_int(self, schema, path, key, value):
        if path:
            settings = Gio.Settings.new_with_path(schema, path)
        else:
            settings = Gio.Settings.new(schema)
        settings.set_int(key, value)

    def get_enum(self, schema, path, key):
        if path:
            settings = Gio.Settings.new_with_path(schema, path)
        else:
            settings = Gio.Settings.new(schema)
        return settings.get_enum(key)

    def set_enum(self, schema, path, key, value):
        if path:
            settings = Gio.Settings.new_with_path(schema, path)
        else:
            settings = Gio.Settings.new(schema)
        settings.set_enum(key, value)

    def get_bool(self, schema, path, key):
        if path:
            settings = Gio.Settings.new_with_path(schema, path)
        else:
            settings = Gio.Settings.new(schema)
        return settings.get_boolean(key)

    def set_bool(self, schema, path, key, value):
        if path:
            settings = Gio.Settings.new_with_path(schema, path)
        else:
            settings = Gio.Settings.new(schema)
        settings.set_boolean(key, value)


    def reset_dconf_path(self, path):
        subprocess.call(['dconf', 'reset', '-f', path], stdout=DEVNULL, stderr=DEVNULL)

    def set_dconf_value(self, path, value):
        subprocess.call(['dconf', 'write', path, value], stdout=DEVNULL, stderr=DEVNULL)

    def get_dconf_value(self, path):
        dconf = subprocess.Popen(['dconf', 'read', path], stdin=PIPE, stdout=PIPE, stderr=PIPE)
        dconf_output, dconf_error = dconf.communicate()
        return dconf_output.decode().strip()

    def init_checkbox(self, schema_id, key, name):
        source = Gio.SettingsSchemaSource.get_default()
        schema = source.lookup(schema_id, True)
        if schema:
            settings = Gio.Settings.new_full(schema)
            widget = self.builder.get_object(name)

            settings.bind(key, widget, "active", Gio.SettingsBindFlags.DEFAULT)
            widget.connect("toggled", self.on_checkbox_toggled, schema_id, key)

    def init_combobox(self, schema, key, name, default = None):
        source = Gio.SettingsSchemaSource.get_default()
        if source.lookup(schema, True) != None:
            widget = self.builder.get_object(name)
            conf = self.get_string(schema, None, key)
            index = 0
            default_index = None
            for row in widget.get_model():
                if default != None and default == row[1]:
                    default_index = index
                if conf == row[1]:
                    widget.set_active(index)
                    default_index = None
                    break
                index = index +1
            if default_index != None:
                widget.set_active(default_index)
            widget.connect("changed", self.combo_fallback, schema, key)

    def update_combobox(self, schema, key, name):
        source = Gio.SettingsSchemaSource.get_default()
        if source.lookup(schema, True) != None:
            widget = self.builder.get_object(name)
            conf = self.get_string(schema, None, key)
            index = 0
            for row in widget.get_model():
                if(conf == row[1]):
                    widget.set_active(index)
                    break
                index = index +1

    def process_running(self, name):
        uid = os.getuid()
        for process in psutil.process_iter():
            try:
                proc = process.as_dict(attrs=['name', 'uids'])
            except psutil.NoSuchProcess:
                pass
            else:
                if name == proc['name'] and uid == proc['uids'].real:
                    return True
        return False

    def kill_process(self, name):
        uid = os.getuid()
        for process in psutil.process_iter():
            try:
                proc = process.as_dict(attrs=['name', 'pid', 'uids'])
            except psutil.NoSuchProcess:
                pass
            else:
                if name == proc['name'] and uid == proc['uids'].real:
                    try:
                        target = psutil.Process(proc['pid'])
                        target.kill()
                    except psutil.NoSuchProcess:
                        pass

    def find_on_path(self, command):
        """Is command on the executable search path?"""
        if 'PATH' not in os.environ:
            return False
        path = os.environ['PATH']
        for element in path.split(os.pathsep):
            if not element:
                continue
            filename = os.path.join(element, command)
            if os.path.isfile(filename) and os.access(filename, os.X_OK):
                return True
        return False

    def mkdir_p(self, path):
        try:
            os.makedirs(path)
        except OSError as exc: # Python >2.5
            if exc.errno == errno.EEXIST and os.path.isdir(path):
                pass
            else:
                raise

    def create_autostart(self, filename, content):
        config_dir = GLib.get_user_config_dir()
        self.mkdir_p(os.path.join(config_dir, 'autostart/'))
        if not os.path.exists(os.path.join(config_dir, 'autostart',filename)):
            with open(os.path.join(config_dir, 'autostart', filename),'w') as autostart:
                autostart.write(content)

    def remove_autostart(self, filename):
        config_dir = GLib.get_user_config_dir()
        autostart_file = os.path.join(config_dir, 'autostart', filename)
        if os.path.exists(autostart_file):
            os.remove(autostart_file)

        # Make sure any system desktop files are also removed
        mate_tweak_helper = os.path.join('/','usr', 'lib', 'mate-tweak', 'mate-tweak-helper')
        subprocess.call(['pkexec', mate_tweak_helper, 'autostop', filename], stdout=DEVNULL, stderr=DEVNULL)

    def check_wm_features(self):
        screen = Gdk.Screen.get_default()
        current_wm = GdkX11.X11Screen.get_window_manager_name(screen)

        # Order is important because Marco actually advertises itself
        # as 'Marco (Metacity)'.
        if ('Marco' in current_wm):
            self.current_wm = 'marco'
            composite_enabled = self.get_bool('org.mate.Marco.general', None, 'compositing-manager')
            if self.process_running('compton'):
                self.current_wm += '-compton'
            elif self.process_running('xcompmgr'):
                self.current_wm += '-xcompmgr'
            elif not composite_enabled:
                self.current_wm += '-no-composite'
        elif ('Metacity' in current_wm):
            self.current_wm = current_wm.lower()
            composite_enabled = self.get_bool('org.gnome.metacity', None, 'compositing-manager')
            if self.process_running('compton'):
                self.current_wm += '-compton'
            elif self.process_running('xcompmgr'):
                self.current_wm += '-xcompmgr'
            elif not composite_enabled:
                self.current_wm += '-no-composite'
        elif ('Compiz' in current_wm):
            self.current_wm = current_wm.lower()
        else:
            self.current_wm = 'Unknown'

        print('Window Manager is: ' + self.current_wm)

        self.compiz_capable = False
        self.ccsm_capable = False
        self.marco_capable = False
        self.marco_compton_capable = False
        self.marco_xcompmgr_capable = False
        self.marco_no_composite_capable = False
        self.metacity_capable = False
        self.metacity_compton_capable = False
        self.metacity_xcompmgr_capable = False
        self.metacity_no_composite_capable = False

        if self.find_on_path('compiz'):
            if not self.software_rasterizer and self.texture_from_pixmap:
                 self.compiz_capable = True

        if self.find_on_path('ccsm'):
            self.ccsm_capable = True

        if self.find_on_path('marco-compton') and self.find_on_path('marco') and self.find_on_path('compton'):
            if not self.software_rasterizer and self.texture_from_pixmap:
                self.marco_compton_capable = True

        if self.find_on_path('marco-xcompmgr') and self.find_on_path('marco') and self.find_on_path('xcompmgr'):
            if not self.software_rasterizer and self.texture_from_pixmap:
                self.marco_xcompmgr_capable = True

        if self.find_on_path('marco-no-composite') and self.find_on_path('marco'):
            self.marco_no_composite_capable = True

        if self.find_on_path('marco'):
            self.marco_capable = True

        if self.find_on_path('metacity-compton') and self.find_on_path('metacity') and self.find_on_path('compton'):
            if not self.software_rasterizer and self.texture_from_pixmap:
                self.metacity_compton_capable = True

        if self.find_on_path('metacity-xcompmgr') and self.find_on_path('metacity') and self.find_on_path('xcompmgr'):
            if not self.software_rasterizer and self.texture_from_pixmap:
                self.metacity_xcompmgr_capable = True

        if self.find_on_path('metacity-no-composite') and self.find_on_path('metacity'):
            self.metacity_no_composite_capable = True

        if self.find_on_path('metacity'):
            self.metacity_capable = True

    def get_num_workspaces(self):
        # Get the number of virtual workspaces from the current window manager
        if 'marco' in self.current_wm:
            num_workspaces = self.get_int('org.mate.Marco.general', None, 'num-workspaces')
        elif 'metacity' in self.current_wm:
            num_workspaces = self.get_int('org.gnome.desktop.wm.preferences', None, 'num-workspaces')
        elif 'compiz' in self.current_wm:
            try:
                hsize = int(self.get_dconf_value('/org/compiz/profiles/mate/plugins/core/hsize'))
                vsize = int(self.get_dconf_value('/org/compiz/profiles/mate/plugins/core/vsize'))
            except:
                hsize = 0
                vsize = 0
            # Guess what the num_workspaces should be
            if vsize > hsize:
                num_workspaces = vsize
            else:
                num_workspaces = hsize
        else:
            # If we can't detect the window manager assume 0 workspaces
            num_workspaces = 0

        print('Got ' + str(num_workspaces) + ' workspaces from ' + self.current_wm)
        return num_workspaces

    def replace_windowmanager(self, new_wm):
        if new_wm == 'Unknown':
            return

        if new_wm == 'marco' or new_wm == 'metacity':
            wm_params = '--composite'
        elif new_wm == 'compiz':
            wm_params = 'ccp'
        else:
            wm_params = ''

        num_workspaces = self.get_num_workspaces()
        # Don't make any change if workspaces is zero.
        if num_workspaces != 0:
            # Set the number of virtual workspaces for the new window manager
            if 'marco' in new_wm:
                self.set_int('org.mate.Marco.general', None, 'num-workspaces', num_workspaces)
            elif 'metacity' in new_wm:
                self.set_int('org.gnome.desktop.wm.preferences', None, 'num-workspaces', num_workspaces)
            elif 'compiz' in new_wm:
                # FIXME, this is a very crude guess
                if self.current_layout.startswith('mutiny'):
                    self.set_dconf_value('/org/compiz/profiles/mate/plugins/core/hsize', '1')
                    self.set_dconf_value('/org/compiz/profiles/mate/plugins/core/vsize', str(num_workspaces))
                else:
                    self.set_dconf_value('/org/compiz/profiles/mate/plugins/core/hsize', str(num_workspaces))
                    self.set_dconf_value('/org/compiz/profiles/mate/plugins/core/vsize', '1')
            print('Set ' + str(num_workspaces) + ' workspaces for ' + new_wm)

        # Make sure Compiz and Metacity use the same theme as Marco to
        # prevent theme/xcursor changes when switching themes.
        if (new_wm == 'compiz') or ('metacity' in new_wm):
            mate_theme = self.get_string('org.mate.interface', None, 'gtk-theme')
            mate_cursor_theme = self.get_string('org.mate.peripherals-mouse', None, 'cursor-theme')
            mate_cursor_size = self.get_int('org.mate.peripherals-mouse', None, 'cursor-size')

            schemasource = Gio.SettingsSchemaSource.get_default()
            gnome_desktop_schema = schemasource.lookup('org.gnome.desktop', False)
            if gnome_desktop_schema:
                self.set_string('org.gnome.desktop.wm.preferences', None, 'theme', mate_theme)
                self.set_string('org.gnome.desktop.interface', None, 'cursor-theme', mate_cursor_theme)
                self.set_int('org.gnome.desktop.interface', None, 'cursor-size', mate_cursor_size)

            # metacity >= 3.20 - this schema may not be installed
            metacity_schema = schemasource.lookup('org.gnome.metacity.theme', False)
            if metacity_schema:
                self.set_string('org.gnome.metacity.theme', None, 'name', mate_theme)
                # FIXME! Don't assume 'metacity' is 1
                self.set_enum('org.gnome.metacity.theme', None, 'type', 1)

        # If switching away from a compton or xcompmgr composited window
        # manager kill compton.
        if 'compiz' in self.current_wm:
            self.kill_process('compiz')
        elif 'compton' in self.current_wm:
            self.kill_process('compton')
        elif 'xcompmgr' in self.current_wm:
            self.kill_process('xcompmgr')

        if ('compton' in new_wm) or ('xcompmgr' in new_wm) or ('no-composite' in new_wm):
            subprocess.Popen([new_wm], stdout=DEVNULL, stderr=DEVNULL)
        else:
            subprocess.Popen([new_wm, '--replace', wm_params], stdout=DEVNULL, stderr=DEVNULL)

        # Update current_wm to the new one.
        self.previous_wm = self.current_wm
        self.current_wm = new_wm
        self.builder.get_object("label_unknown_wm").hide()

        Notify.init(_('Window Manager Replaced'))
        replace_windowmanager_notify=Notify.Notification.new (_('Window Manager Replaced'),_('Your window manager has been replaced with ' + self.current_wm),'dialog-information')
        replace_windowmanager_notify.show()

    def enable_hud(self):
        self.create_autostart('mate-hud.desktop', __HUD__)
        subprocess.Popen(['/usr/lib/mate-hud/mate-hud'], stdout=DEVNULL, stderr=DEVNULL)

    def disable_hud(self):
        self.remove_autostart('mate-hud.desktop')
        self.kill_process('mate-hud')

    def enable_dock(self):
        self.set_string('org.mate.session.required-components', None, 'dock', self.dock)
        if self.dock:
            self.remove_autostart(self.dock + '.desktop')

        # Docks require compositing
        schema = None
        if self.current_wm == 'marco':
            schema = 'org.mate.Marco.general'
        elif self.current_wm == 'metacity':
            schema = 'org.gnome.metacity'
        elif 'compiz' in self.current_wm:
            # https://bugs.launchpad.net/ubuntu-mate/+bug/1437611
            self.set_bool('org.compiz.thumbnail', '/org/compiz/profiles/mate/plugins/thumbnail/', 'current-viewport', False)

        if schema is not None:
            compositor_enabled = self.get_bool(schema, None, 'compositing-manager')
            if not compositor_enabled:
                self.set_bool(schema, None, 'compositing-manager', True)
                subprocess.Popen([self.current_wm, '--replace'], stdout=DEVNULL, stderr=DEVNULL)

        # Launch the dock, if it is not already enabled.
        if not self.dock_enabled:
            subprocess.Popen([self.dock], stdout=DEVNULL, stderr=DEVNULL)
            self.dock_enabled = True

    def disable_dock(self):
        if 'compiz' in self.current_wm:
            # https://bugs.launchpad.net/ubuntu-mate/+bug/1437611
            self.set_bool('org.compiz.thumbnail', '/org/compiz/profiles/mate/plugins/thumbnail/', 'current-viewport', True)

        self.set_string('org.mate.session.required-components', None, 'dock', '')
        if self.dock:
            self.remove_autostart(self.dock + '.desktop')
        self.kill_process(self.dock)
        self.dock_enabled = False

    def enable_pulldown_terminal(self):
        if self.pulldown_terminal == 'tilda':
            self.create_autostart(self.pulldown_terminal + '.desktop', __TILDA__)

            # Launch tilda, if it is not already enabled.
            if not self.pulldown_terminal_enabled:
                subprocess.Popen([self.pulldown_terminal], stdout=DEVNULL, stderr=DEVNULL)
                self.pulldown_terminal_enabled = True

    def disable_pulldown_terminal(self):
        if self.pulldown_terminal == 'tilda':
            self.remove_autostart(self.pulldown_terminal + '.desktop')

        if self.process_running(self.pulldown_terminal):
            self.kill_process(self.pulldown_terminal)

        self.pulldown_terminal_enabled = False

    def disable_applets(self):
        """
        When indicators are enabled some MATE applets need disabling
        or hiding.
        """
        self.kill_process('mate-volume-control-applet')
        self.remove_autostart('mate-volume-control-applet.desktop')
        self.set_string('org.mate.power-manager', None, 'icon-policy', 'never')
        self.set_bool('org.mate.power-manager', None, 'notify-low-capacity', False)

        # Remove the incorrectly named autostart file case by an earlier bug.
        # Note the double .desktop suffix.
        self.remove_autostart('mate-volume-control-applet.desktop.desktop')

    def enable_applets(self):
        """
        When indicators are disabled some MATE applets need enabling
        or showing.
        """
        subprocess.Popen(['mate-volume-control-applet'], stdout=DEVNULL, stderr=DEVNULL)
        self.remove_autostart('mate-volume-control-applet.desktop')
        self.create_autostart('mate-volume-control-applet.desktop', __APPLET_SOUND__)
        self.set_string('org.mate.power-manager', None, 'icon-policy', 'present')
        self.set_bool('org.mate.power-manager', None, 'notify-low-capacity', True)

        # Remove the incorrectly named autostart file case by an earlier bug.
        # Note the double .desktop suffix.
        self.remove_autostart('mate-volume-control-applet.desktop.desktop')

    def disable_indicators(self):
        self.kill_process('indicator-session-service')
        self.kill_process('indicator-power-service')
        self.kill_process('indicator-messages-service')
        self.kill_process('indicator-sound-service')
        self.kill_process('indicator-application-service')
        self.remove_autostart('indicator-application.desktop')
        self.remove_autostart('indicator-sound.desktop')
        self.remove_autostart('indicator-messages.desktop')
        self.remove_autostart('indicator-power.desktop')
        self.remove_autostart('indicator-session.desktop')

    def enable_indicators(self):
        subprocess.Popen(['/usr/lib/' + self.multiarch + '/indicator-application/indicator-application-service'], stdout=DEVNULL, stderr=DEVNULL)
        subprocess.Popen(['/usr/lib/' + self.multiarch + '/indicator-sound/indicator-sound-service'], stdout=DEVNULL, stderr=DEVNULL)
        subprocess.Popen(['/usr/lib/' + self.multiarch + '/indicator-messages/indicator-messages-service'], stdout=DEVNULL, stderr=DEVNULL)
        subprocess.Popen(['/usr/lib/' + self.multiarch + '/indicator-power/indicator-power-service'], stdout=DEVNULL, stderr=DEVNULL)
        subprocess.Popen(['/usr/lib/' + self.multiarch + '/indicator-session/indicator-session-service'], stdout=DEVNULL, stderr=DEVNULL)

        # Create indicator autostart files
        indicator_application_autostart = __INDICATOR_APPLICATION__.replace('MULTIARCH', self.multiarch)
        self.create_autostart('indicator-application.desktop', indicator_application_autostart)

        indicator_sound_autostart = __INDICATOR_SOUND__.replace('MULTIARCH', self.multiarch)
        self.create_autostart('indicator-sound.desktop', indicator_sound_autostart)

        indicator_messages_autostart = __INDICATOR_MESSAGES__.replace('MULTIARCH', self.multiarch)
        self.create_autostart('indicator-messages.desktop', indicator_messages_autostart)

        indicator_power_autostart = __INDICATOR_POWER__.replace('MULTIARCH', self.multiarch)
        self.create_autostart('indicator-power.desktop', indicator_power_autostart)

        indicator_session_autostart = __INDICATOR_SESSION__.replace('MULTIARCH', self.multiarch)
        self.create_autostart('indicator-session.desktop', indicator_session_autostart)

    def toggle_desktop_icons_sensitiveness(self):
        sensitiveness = self.builder.get_object("checkbox_show_icons").get_active()
        self.builder.get_object("caption_desktop_icons").set_sensitive(sensitiveness)
        self.builder.get_object("checkbox_computer").set_sensitive(sensitiveness)
        self.builder.get_object("checkbox_computer").set_sensitive(sensitiveness)
        self.builder.get_object("checkbox_home").set_sensitive(sensitiveness)
        self.builder.get_object("checkbox_network").set_sensitive(sensitiveness)
        self.builder.get_object("checkbox_trash").set_sensitive(sensitiveness)
        self.builder.get_object("checkbox_volumes").set_sensitive(sensitiveness)

    def panel_layout_uses(self, applet, panel_layout):
        try:
            with open(os.path.join('/','usr','share','mate-panel','layouts', panel_layout + '.layout'), 'rb', 0) as layout, \
                mmap.mmap(layout.fileno(), 0, access=mmap.ACCESS_READ) as data:
                if data.find(applet.encode('utf-8')) != -1:
                    return True
            return False

        except:
            return False

    def update_panel_layout_ui(self, panel_layout):
        # Make show-applications insesitive we're using the Pantheon layout.
        if panel_layout.startswith('pantheon'):
            self.builder.get_object('checkbutton_show_applications').props.sensitive = False
        else:
            self.builder.get_object('checkbutton_show_applications').props.sensitive = True

        # Make Menu Bar configuration insensitive for layouts that don't
        # feature the menu bar.
        if self.panel_layout_uses('Object menu-bar', panel_layout):
            self.builder.get_object('box_menu_features').props.sensitive = True
            self.builder.get_object('combobox_panel_icon_size').props.sensitive = True
            self.builder.get_object('combobox_panel_menu_icon_size').props.sensitive = True
        else:
            self.builder.get_object('box_menu_features').props.sensitive = False
            self.builder.get_object('combobox_panel_icon_size').props.sensitive = False
            self.builder.get_object('combobox_panel_menu_icon_size').props.sensitive = False

        if 'tweak' in panel_layout:
            self.builder.get_object('button_delete_panel').props.sensitive = True
        else:
            self.builder.get_object('button_delete_panel').props.sensitive = False

    def reload_panel(self):
        subprocess.Popen(['mate-panel', '--replace'], stdout=DEVNULL, stderr=DEVNULL)

    def confirm_dialog(self, title, text):
        dialog = Gtk.Dialog(title, None, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OK, Gtk.ResponseType.OK))

        dialog.set_default_size(500, 160)

        label = Gtk.Label(text)
        label.set_use_markup(True)

        box = dialog.get_content_area()
        box.set_border_width(8)
        box.add(label)
        dialog.show_all()

        response = dialog.run()
        dialog.destroy()

        if response == Gtk.ResponseType.OK:
            confirmed = True
        elif response == Gtk.ResponseType.CANCEL:
            confirmed = False

        return confirmed

    def replace_panel_layout(self, new_layout):
        leds_enabled = self.get_bool('org.mate.peripherals-keyboard-xkb.general', None, 'duplicate-leds')
        icon_size = self.get_string('org.mate.panel.menubar', None, 'icon-size')
        item_icon_size = self.get_string('org.mate.panel.menubar', None, 'item-icon-size')

        print('Switching to: ' + new_layout)
        self.update_panel_layout_ui(new_layout)

        # Change show-applications state when changing to/from Pantheon.
        show_applications = self.get_bool('org.mate.panel.menubar', None, 'show-applications')
        if new_layout.startswith('pantheon') and self.panel_layout_uses('BriskMenu', new_layout):
            self.set_bool('org.mate.panel.menubar', None, 'show-applications', False)
        # If we're switching to a layout use the menu-bar, but not Brisk
        # and Applications is disabled in the menu-bar, it probably make
        # sense to enable Applications.
        elif self.panel_layout_uses('menu-bar', new_layout) and \
             not self.panel_layout_uses('BriskMenu', new_layout) and \
             not show_applications:
            self.set_bool('org.mate.panel.menubar', None, 'show-applications', True)

        # Change Brisk Menu label-visible state when changing to/from Cupertino.
        if self.brisk_menu_available:
            label_visible = self.get_bool('com.solus-project.brisk-menu', None, 'label-visible')
            if new_layout.startswith('eleven'):
                self.set_bool('com.solus-project.brisk-menu', None, 'label-visible', False)
            # If we're switching from Eleven, and Brisk Menu label-visible is disabled then enable it.
            elif self.current_layout.startswith('eleven') and not label_visible:
                self.set_bool('com.solus-project.brisk-menu', None, 'label-visible', True)

        # If we're switching to Cupertino or Mutiny to move window
        # controls to the left and enable Global Menu.
        if new_layout.startswith('eleven') or \
           new_layout.startswith('mutiny') or \
           new_layout.startswith('contemporary'):
            self.set_string("org.mate.Marco.general", None, "button-layout", __CONTEMPORARY_BUTTONS__)
            self.set_string("org.mate.interface", None, "gtk-decoration-layout", __CONTEMPORARY_BUTTONS__)
            self.set_string("org.gnome.desktop.wm.preferences", None, "button-layout", __CONTEMPORARY_BUTTONS__)
        else:
            self.set_string("org.mate.Marco.general", None, "button-layout", __TRADITIONAL_BUTTONS__)
            self.set_string("org.mate.interface", None, "gtk-decoration-layout", __TRADITIONAL_BUTTONS__)
            self.set_string("org.gnome.desktop.wm.preferences", None, "button-layout", __TRADITIONAL_BUTTONS__)

        # If we're switching to a layout that uses the AppMenu applet toggle
        # global menu and HUD accordingly.
        if self.panel_layout_uses('AppmenuApplet', new_layout) and \
           self.appmenu_applet_available:
            self.set_bool("org.mate.interface", None, 'gtk-shell-shows-menubar', True)
            if (self.hud_available and not self.process_running('mate-hud')):
                self.enable_hud()
        else:
            self.set_bool("org.mate.interface", None, 'gtk-shell-shows-menubar', False)
            self.disable_hud()

        # Update window controls
        self.update_window_controls()

        if leds_enabled:
            self.set_bool('org.mate.peripherals-keyboard-xkb.general', None, 'duplicate-leds', False)

        # Reset the panel dconf tree.
        self.reset_dconf_path('/org/mate/panel/objects/')
        self.reset_dconf_path('/org/mate/panel/toplevels/')

        # If we have a custom panel layout just replace the dconf dump.
        if os.path.exists(os.path.join('/','usr','share','mate-panel','layouts', new_layout + '.panel')):
            print('Loading additional panel configuration for ' + new_layout)
            cmd = 'dconf load /org/mate/panel/ < /usr/share/mate-panel/layouts/' + new_layout + '.panel'
            subprocess.call(cmd, shell=True, stdout=DEVNULL, stderr=DEVNULL)
        else:
            subprocess.call(['mate-panel', '--layout', new_layout, '--reset'], stdout=DEVNULL, stderr=DEVNULL)

        # Get the icon sizes now the new layout has been applied.
        new_icon_size = self.get_string('org.mate.panel.menubar', None, 'icon-size')
        new_item_icon_size = self.get_string('org.mate.panel.menubar', None, 'item-icon-size')

        # If the new icon sizes are default and the icon sizes were
        # previously set then restore the previous icon sizes.
        if (new_icon_size == 'Default') and (icon_size != 'Default'):
            print('Change icon-size')
            self.set_string('org.mate.panel.menubar', None, 'icon-size', icon_size)

        if (new_item_icon_size == 'Default') and (item_icon_size != 'Default'):
            print('Change item-icon-size')
            self.set_string('org.mate.panel.menubar', None, 'item-icon-size', item_icon_size)

        # If the panel layout is Indicator enabled the appropriate
        # indicators and applets need to be stopped/started
        if self.panel_layout_uses('IndicatorApplet', new_layout):
            self.disable_applets()
            self.enable_indicators()
        else:
            self.disable_indicators()
            self.enable_applets()

        subprocess.Popen(['mate-panel', '--replace'], stdout=DEVNULL, stderr=DEVNULL)

        # Determine if the dock should be enabled
        if (new_layout.startswith('eleven') and self.dock is not None) or \
           (new_layout.startswith('pantheon') and self.dock is not None):
            self.enable_dock()
        elif not new_layout.startswith('eleven') or \
             not new_layout.startswith('pantheon'):
            self.disable_dock()

        # Update the number and orientation of virtual workspaces for Compiz
        if 'compiz' in self.current_wm:
            num_workspaces = self.get_num_workspaces()
            # FIXME, this is a very crude guess
            if new_layout.startswith('mutiny'):
                self.set_dconf_value('/org/compiz/profiles/mate/plugins/core/hsize', '1')
                self.set_dconf_value('/org/compiz/profiles/mate/plugins/core/vsize', str(num_workspaces))
            else:
                self.set_dconf_value('/org/compiz/profiles/mate/plugins/core/hsize', str(num_workspaces))
                self.set_dconf_value('/org/compiz/profiles/mate/plugins/core/vsize', '1')
            print('Set ' + str(num_workspaces) + ' workspaces for ' + self.current_wm)

        if leds_enabled:
            time.sleep(1)
            self.set_bool('org.mate.peripherals-keyboard-xkb.general', None, 'duplicate-leds', True)

        # Make sure the correct panel name is set
        widget = self.builder.get_object('combobox_panels')
        widget.disconnect(self.combobox_panels_handler)
        self.set_string("org.mate.panel", None, "default-layout", new_layout)
        self.combobox_panels_handler = widget.connect("changed", self.combo_fallback, 'org.mate.panel', 'default-layout')

        self.current_layout = new_layout

    def get_panel_layout(self):
        self.current_layout = self.get_string('org.mate.panel', None, 'default-layout')

    def init_panel_features(self):
        self.get_panel_layout()
        print ('Current layout: ' + self.current_layout)
        self.update_panel_layout_ui(self.current_layout)

        source = Gio.SettingsSchemaSource.get_default()
        if source.lookup('org.mate.panel', True) != None:
            widget = self.builder.get_object('combobox_panels')
            index = 0
            for row in widget.get_model():
                if(self.current_layout == row[1]):
                    widget.set_active(index)
                    break
                index += 1
            self.combobox_panels_handler = widget.connect("changed", self.combo_fallback, 'org.mate.panel', 'default-layout')

    def toggle_panel_features(self, widget):
        self.reload_panel()

    def toggle_pulldown_terminal(self, checkbutton):
        if checkbutton.get_active():
            self.enable_pulldown_terminal()
        else:
            self.disable_pulldown_terminal()

    def panel_layout_exists(self, panel_layout):
        return os.path.exists('/usr/share/mate-panel/layouts/' + panel_layout + '.layout')

    def check_glx_features(self):
        if self.find_on_path('glxinfo'):
            process = subprocess.Popen(['glxinfo'], stdout=PIPE)
            out = process.communicate()[0].decode("UTF-8")
            if out.count("Software Rasterizer") == 0:
                self.software_rasterizer = False
            else:
                self.software_rasterizer = True

            if out.count("texture_from_pixmap") > 2:
                self.texture_from_pixmap = True
            else:
                self.texture_from_pixmap = False
        else:
            self.software_rasterizer = False
            self.texture_from_pixmap = False

    def update_windowmanager_widgets(self):
        # Update the widget based on the current window manager.
        if ('marco' in self.current_wm) or ('metacity' in self.current_wm):
            self.builder.get_object('frame_performance').props.sensitive = True
            self.builder.get_object('frame_behaviour').props.sensitive = True
            if 'marco' in self.current_wm:
                self.init_checkbox('org.mate.Marco.general', 'reduced-resources', 'checkbox_resources')
                self.init_checkbox('org.mate.Marco.general', 'side-by-side-tiling', 'checkbox_snapping')
                self.init_checkbox('org.mate.interface', 'enable-animations', 'checkbox_animations')

                # Make the Window Snapping checkbox inactive based if reduced resources is enabled
                #  - https://bugs.launchpad.net/ubuntu-mate/+bug/1548011
                marco_reduced = self.get_bool('org.mate.Marco.general', None, 'reduced-resources')
                if marco_reduced:
                    self.builder.get_object('checkbox_snapping').props.sensitive = False
            elif 'metacity' in self.current_wm:
                self.init_checkbox('org.gnome.metacity', 'reduced-resources', 'checkbox_resources')
                self.init_checkbox('org.gnome.metacity', 'edge-tiling', 'checkbox_snapping')
                self.init_checkbox('org.gnome.desktop.interface', 'enable-animations', 'checkbox_animations')

                # Make the Window Snapping checkbox inactive based if reduced resources is enabled
                #  - https://bugs.launchpad.net/ubuntu-mate/+bug/1548011
                metacity_reduced = self.get_bool('org.gnome.metacity', None, 'reduced-resources')
                if metacity_reduced:
                    self.builder.get_object('checkbox_snapping').props.sensitive = False
        else:
            self.builder.get_object('frame_performance').props.sensitive = False
            self.builder.get_object('frame_behaviour').props.sensitive = False

        if self.current_wm == 'compiz':
           self.builder.get_object('button_compiz_reset').show()
           if self.ccsm_capable:
               self.builder.get_object('button_ccsm').show()
           else:
               self.builder.get_object('button_ccsm').hide()
        else:
           self.builder.get_object('button_compiz_reset').hide()
           self.builder.get_object('button_ccsm').hide()

    def additional_tweaks(self, schema, key, value):
        if schema == "org.mate.Marco.general" and key == "button-layout":
            # If the button-layout is changed in MATE reflect that change
            # for GTK3 and GNOME.
            self.set_string("org.mate.interface", None, "gtk-decoration-layout", value)
            self.set_string("org.gnome.desktop.wm.preferences", None, "button-layout", value)

        elif (schema == "org.mate.interface" and key == "enable-animations"):
            self.set_bool('org.mate.panel', None, 'enable-aniations', value)
            self.set_bool('org.mate.interface', None, 'gtk-enable-aniations', value)
            self.set_bool('org.gnome.desktop.interface', None, 'enable-aniations', value)

        elif (schema == "org.gnome.desktop.interface" and key == "enable-animations"):
            self.set_bool('org.mate.panel', None, 'enable-aniations', value)
            self.set_bool('org.mate.interface', None, 'gtk-enable-aniations', value)
            self.set_bool('org.mate.interface', None, 'enable-aniations', value)

        elif schema == "org.mate.session.required-components" and key == "windowmanager":
            # If the window manager is being changed, replace it now!
            wm = value
            self.replace_windowmanager(wm)
            self.update_windowmanager_widgets()

        elif schema == "org.mate.panel" and key == "default-layout":
            panel_layout = value
            title = _('Change the layout?')
            text = _('<b>Changing the layout will completely replace and reset the current configuration!</b>\n\nAre you sure you want to replace this layout and potentially lose any customizations you might have made?')
            if not self.confirm_dialog(title, text):
                # Revert the change that the signal already caught
                source = Gio.SettingsSchemaSource.get_default()
                if source.lookup('org.mate.panel', True) != None:
                    widget = self.builder.get_object('combobox_panels')
                    widget.disconnect(self.combobox_panels_handler)
                    self.set_string("org.mate.panel", None, "default-layout", self.current_layout)
                    index = 0
                    for row in widget.get_model():
                        if(self.current_layout == row[1]):
                            widget.set_active(index)
                            break
                        index += 1
                    self.combobox_panels_handler = widget.connect("changed", self.combo_fallback, 'org.mate.panel', 'default-layout')
            else:
                # If the panel layout is being changed, replace it now!
                self.replace_panel_layout(panel_layout)

        elif schema == "org.mate.panel.menubar":
            if key == "icon-size" or key == "item-icon-size":
                self.set_string("org.mate.panel.menubar", None, key, value)
                self.reload_panel()

    def combo_fallback(self, widget, schema, key):
        act = widget.get_active()
        value = widget.get_model()[act]
        self.set_string(schema, None, key, value[1])

        # Process any additional changes required for the schema and key
        self.additional_tweaks(schema, key, value[1])

    # Change pages
    def side_view_nav(self, param):
        treePaths = param.get_selected_items()
        if (len(treePaths) > 0):
            treePath = treePaths[0]
            index = int("%s" % treePath) #Hack to turn treePath into an int
            target = self.sidePages[index].notebook_index
            self.builder.get_object("main_notebook").set_current_page(target)

    def check_dock_features(self):
        # Order matters. Plank is preferred.
        if self.find_on_path('plank') and \
            os.path.exists(os.path.join('/','usr','share','applications', 'plank.desktop')):
            self.dock = 'plank'
        elif self.find_on_path('docky') and \
            os.path.exists(os.path.join('/','usr','share','applications', 'docky.desktop')):
            self.dock = 'docky'
        else:
            self.dock = None

        if self.dock is not None and self.get_string('org.mate.session.required-components', None, 'dock'):
            self.dock_enabled = True
        else:
            self.dock_enabled = False

    def check_pulldown_terminal_features(self):
        config_dir = GLib.get_user_config_dir()

        if self.find_on_path('tilda'):
            self.pulldown_terminal = 'tilda'
        else:
            self.pulldown_terminal = None

        if self.pulldown_terminal is not None and os.path.exists(os.path.join(config_dir, 'autostart/') + self.pulldown_terminal + '.desktop'):
            self.pulldown_terminal_enabled = True
        else:
            self.pulldown_terminal_enabled = False

    def check_hud_features(self):
        if os.path.exists('/usr/lib/mate-hud/mate-hud'):
            self.hud_available = True
        else:
            self.hud_available = False

    def check_panel_features(self):
        # Determine what panel features are available
        self.gnome_menu_available = False
        self.indicators_available = False
        self.mageia_cc_available = False
        self.mate_dock_available = False
        self.mate_menu_available = False
        self.maximus_available = False
        self.mint_menu_available = False
        self.volume_applet_enabled = False
        self.brisk_menu_available = False
        self.appmenu_applet_available = False

        if os.path.exists('/usr/lib/gnome-main-menu/main-menu'):
            self.gnome_menu_available = True

        if os.path.exists('/usr/lib/indicators3/7/libapplication.so') and \
           os.path.exists('/usr/lib/' + self.multiarch + '/indicator-application/indicator-application-service') and \
           os.path.exists('/usr/lib/' + self.multiarch + '/indicator-sound/indicator-sound-service') and \
           os.path.exists('/usr/lib/' + self.multiarch + '/indicator-messages/indicator-messages-service') and \
           os.path.exists('/usr/lib/' + self.multiarch + '/indicator-power/indicator-power-service') and \
           os.path.exists('/usr/lib/' + self.multiarch + '/indicator-session/indicator-session-service') and \
           os.path.exists('/usr/share/mate-panel/applets/org.ayatana.panel.IndicatorApplet.mate-panel-applet'):
            self.indicators_available = True

        if os.path.exists('/usr/share/applications/mageia-drakconf.desktop'):
            self.mageia_cc_available = True

        if os.path.exists('/usr/lib/mate-applets/mate-dock-applet/dock.py'):
            self.mate_dock_available = True

        if os.path.exists('/usr/lib/mate-menu/mate-menu.py'):
            self.mate_menu_available = True

        if os.path.exists('/usr/lib/' + self.multiarch + '/brisk-menu/brisk-menu') or \
            os.path.exists('/usr/lib/brisk-menu/brisk-menu'):
            if os.path.exists('/usr/share/mate-panel/applets/com.solus_project.brisk.BriskMenu.mate-panel-applet'):
             self.brisk_menu_available = True

        if os.path.exists('/usr/lib/mate-panel/appmenu-mate') and \
            os.path.exists('/usr/share/mate-panel/applets/org.vala-panel.appmenu.mate-panel-applet'):
            self.appmenu_applet_available = True

        if os.path.exists('/usr/bin/mate-maximus') and \
           os.path.exists('/usr/lib/mate-netbook/mate-window-picker-applet'):
            self.maximus_available = True

        if os.path.exists('usr/lib/linuxmint/mintMenu/mintMenu.py'):
            self.mint_menu_available = True

        config_dir = GLib.get_user_config_dir()
        if os.path.exists('/etc/xdg/autostart/mate-volume-control-applet.desktop') or \
           os.path.exists(os.path.join(config_dir, 'autostart/') + 'mate-volume-control-applet.desktop'):
            self.volume_applet_enabled = True


    def update_window_controls(self):
        layouts = Gtk.ListStore(str, str)
        layouts.append([_("Right"), __TRADITIONAL_BUTTONS__])
        layouts.append([_("Left"), __CONTEMPORARY_BUTTONS__])
        self.builder.get_object("combobox_window_control").set_model(layouts)
        self.init_combobox("org.mate.Marco.general", "button-layout", "combobox_window_control")


    def make_list_of_window_managers(self):
        wms = Gtk.ListStore(str, str)
        if self.marco_no_composite_capable:
            wms.append([_("Marco (No compositor)"), 'marco-no-composite'])
        if self.marco_capable:
            wms.append([_("Marco (Software compositor)"), 'marco'])
        if self.marco_xcompmgr_capable and not self.software_rasterizer:
            wms.append([_("Marco (No effects xcompmgr compositor)"), 'marco-xcompmgr'])
        if self.marco_compton_capable and not self.software_rasterizer:
            wms.append([_("Marco (Compton GPU compositor)"), 'marco-compton'])
        if self.metacity_no_composite_capable:
            wms.append([_("Metacity (No compositor)"), 'metacity-no-composite'])
        if self.metacity_capable:
            wms.append([_("Metacity (Software compositor)"), 'metacity'])
        if self.metacity_xcompmgr_capable and not self.software_rasterizer:
            wms.append([_("Metacity (No effects xcompmgr compositor)"), 'metacity-xcompmgr'])
        if self.metacity_compton_capable and not self.software_rasterizer:
            wms.append([_("Metacity (Compton GPU compositor)"), 'metacity-compton'])
        if self.compiz_capable:
            wms.append([_("Compiz (Advanced GPU accelerated desktop effects)"), 'compiz'])

        if self.current_wm == 'Unknown':
            self.builder.get_object("label_unknown_wm").set_markup('<span size="small" foreground="#cc0000">' + _("You are currently using an unknown and unsupported window manager. Thus we cannot guarantee that changes made here will be effective.") + '</span>')
        else:
            self.builder.get_object("label_unknown_wm").hide()

        self.builder.get_object("combobox_window_manager").set_model(wms)

    def make_list_of_panel_layouts(self):
        # Panel layouts
        panels = Gtk.ListStore(str, str)

        # Add any saved panel layouts first.
        layouts = os.path.join('/','usr','share','mate-panel','layouts','*-tweak.layout')
        for layout in glob.glob(layouts):
            current_layout = layout.replace('.layout', '').replace('/usr/share/mate-panel/layouts/', '');
            panels.append([_('Custom: ') + current_layout.replace('-tweak', ''), current_layout])

        if self.panel_layout_exists('contemporary') and \
           self.appmenu_applet_available and \
           self.brisk_menu_available and \
           self.hud_available:
            panels.append([_("Contemporary"), "contemporary"])

        # Prefer Indicator enabled Cupertino layout and fallback to non-Indicator version.
        if self.dock is not None and \
           self.appmenu_applet_available and \
           self.brisk_menu_available and \
           self.indicators_available:
            if self.panel_layout_exists('eleven'):
                panels.append([_("Cupertino"), "eleven"])
        elif self.dock is not None and \
           self.appmenu_applet_available and \
           self.brisk_menu_available and \
           not self.indicators_available:
            if self.panel_layout_exists('eleven-no-indicators'):
                panels.append([_("Cupertino"), "eleven-no-indicators"])

        if platform.linux_distribution()[0] != 'Ubuntu' and \
            self.panel_layout_exists('fedora'):
            panels.append([_("Fedora"), "fedora"])

        if self.panel_layout_exists('default') and \
           not self.indicators_available:
            panels.append([_("GNOME2"), "default"])

        if self.panel_layout_exists('linuxmint') and \
           self.mint_menu_available:
            panels.append([_("Linux Mint"), "linuxmint"])

        if self.panel_layout_exists('mageia') and \
           self.mageia_cc_available:
            panels.append([_("Mageia"), "mageia"])

        if self.panel_layout_exists('manjaro') and \
           self.brisk_menu_available:
            panels.append([_("Manjaro"), "manjaro"])

        # Prefer Indicator enabled Mutiny layout and fallback to non-Indicator version.
        if self.panel_layout_exists('mutiny') and \
           self.mate_dock_available and \
           self.appmenu_applet_available and \
           self.indicators_available:
            panels.append([_("Mutiny"), "mutiny"])
        elif self.panel_layout_exists('mutiny-no-indicators') and \
           self.mate_dock_available and \
           self.appmenu_applet_available and \
           not self.indicators_available:
            panels.append([_("Mutiny"), "mutiny-no-indicators"])

        # Prefer Indicator enabled Netbook layout and fallback to non-Indicator version.
        if self.panel_layout_exists('netbook') and \
           self.maximus_available and \
           self.indicators_available:
            panels.append([_("Netbook"), "netbook"])
        elif self.panel_layout_exists('netbook-no-indicators') and \
           self.maximus_available and \
           not self.indicators_available:
            panels.append([_("Netbook"), "netbook-no-indicators"])

        if self.panel_layout_exists('opensuse') and \
           self.gnome_menu_available:
            panels.append([_("openSUSE"), "opensuse"])

        if self.dock is not None and \
           self.brisk_menu_available and \
           self.hud_available:
            if self.panel_layout_exists('pantheon'):
                panels.append([_("Pantheon"), "pantheon"])

        # Prefer Indicator enabled Redmond layout and fallback to non-Indicator version.
        if self.panel_layout_exists('redmond') and \
           self.indicators_available:
            panels.append([_("Redmond"), "redmond"])
        elif self.panel_layout_exists('redmond-no-indicators') and \
           not self.indicators_available:
            panels.append([_("Redmond"), "redmond-no-indicators"])

        if self.panel_layout_exists('solus') and \
           self.brisk_menu_available:
            panels.append([_("Solus"), "solus"])

        if self.panel_layout_exists('ubuntu-mate'):
            panels.append([_("Traditional"), "ubuntu-mate"])

        self.builder.get_object("combobox_panels").set_model(panels)

    def ask_for_layout_name(self):
        # Returns user input as a string or None
        title = _('Panel layout name')
        text = _('Enter the name for your panel layout.')

        dialog = Gtk.Dialog(title, None, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OK, Gtk.ResponseType.OK))

        dialog.set_default_size(250, 120)

        label = Gtk.Label(text)
        label.set_use_markup(True)

        box = dialog.get_content_area()
        box.set_border_width(8)
        box.add(label)

        entry = Gtk.Entry()
        box.pack_end(entry, False, False, 0)

        dialog.show_all()
        response = dialog.run()
        name = entry.get_text().strip().replace(' ', '-')
        dialog.destroy()

        if (response == Gtk.ResponseType.OK) and (name is not ''):
            return name
        else:
            return None

    def delete_panel(self, widget):
        # Make certain we don't delete a system installed layout
        if 'tweak' in self.current_layout:
            # Get default layout
            settings = Gio.Settings.new('org.mate.panel')
            default_layout = GLib.Variant.get_string(settings.get_default_value('default-layout'))
            old_layout = self.current_layout

            title = _('Delete this custom layout?')
            text = _('<b>Deleting this custom layout will also return your layout to the system default.</b>\n\nAre you sure you want to permanently delete this custom layout?')

            if self.confirm_dialog(title, text):
                mate_tweak_helper = os.path.join('/','usr', 'lib', 'mate-tweak', 'mate-tweak-helper')
                delete = subprocess.call(['pkexec', mate_tweak_helper, 'delete', old_layout], stdout=DEVNULL, stderr=DEVNULL)
                Notify.init(_('Panel Layout Deleted'))
                delete_panel_notify=Notify.Notification.new (_('Panel Layout Deleted'),_('Your panel layout has been deleted: ') + old_layout.replace('-tweak','') , 'dialog-information')
                delete_panel_notify.show()

                self.make_list_of_panel_layouts()
                self.replace_panel_layout(default_layout)

                source = Gio.SettingsSchemaSource.get_default()
                if source.lookup('org.mate.panel', True) != None:
                    widget = self.builder.get_object('combobox_panels')
                    widget.disconnect(self.combobox_panels_handler)
                    self.set_string("org.mate.panel", None, "default-layout", default_layout)
                    index = 0
                    for row in widget.get_model():
                        if(self.current_layout == row[1]):
                            widget.set_active(index)
                            break
                        index += 1
                    self.combobox_panels_handler = widget.connect("changed", self.combo_fallback, 'org.mate.panel', 'default-layout')

    def save_panels(self, widget):
        layoutname = self.ask_for_layout_name()
        if layoutname is not None:
            layoutname += '-tweak'
            print('Saving ' + layoutname)
            if self.panel_layout_exists(layoutname):
                print('Layout exists. Ignoring that for now and over writting it.')

            mate_tweak_helper = os.path.join('/','usr', 'lib', 'mate-tweak', 'mate-tweak-helper')
            backup = subprocess.call([mate_tweak_helper, 'backup', layoutname], stdout=DEVNULL, stderr=DEVNULL)
            install = subprocess.call(['pkexec', mate_tweak_helper, 'install', layoutname], stdout=DEVNULL, stderr=DEVNULL)
            Notify.init(_('Panel Layout Saved'))
            save_panels_notify=Notify.Notification.new (_('Panel Layout Saved'),_('Your panel layout has been saved as ') + layoutname.replace('-tweak',''), 'dialog-information')
            save_panels_notify.show()

            # Update the currently selected layout.
            self.make_list_of_panel_layouts()

            widget = self.builder.get_object('combobox_panels')
            widget.disconnect(self.combobox_panels_handler)
            self.set_string('org.mate.panel', None, 'default-layout', layoutname)
            index = 0
            for row in widget.get_model():
                if(layoutname == row[1]):
                    widget.set_active(index)
                    break
                index += 1
            self.combobox_panels_handler = widget.connect("changed", self.combo_fallback, 'org.mate.panel', 'default-layout')

    def compiz_reset(self, widget):
        # Kill compiz. Purge all Compiz config and cache. Switch to Compiz.
        self.kill_process('compiz')
        self.reset_dconf_path('/org/compiz/')
        config_compiz = os.path.join(GLib.get_user_config_dir(), 'compiz-1')
        cache_compiz = os.path.join(GLib.get_user_cache_dir(), 'compizconfg1')
        if os.path.exists(config_compiz):
            shutil.rmtree(config_compiz)
        if os.path.exists(cache_compiz):
            shutil.rmtree(cache_compiz)
        self.replace_windowmanager('compiz')
        Notify.init(_('Compiz Reset'))
        compiz_reset_notify=Notify.Notification.new (_('Compiz Reset'),_('Your Compiz configuration has been reset to factory defaults.'),'dialog-information')
        compiz_reset_notify.show()

    def launch_ccsm(self, widget):
        # Launch Compiz Config Settings Manager
        subprocess.Popen(['ccsm'], stdout=DEVNULL, stderr=DEVNULL)

    def close_tweak(self, widget):
        Gtk.main_quit()

    ''' Create the UI '''
    def __init__(self):
        # Check for glx, panel, dock and wm features
        self.multiarch = sysconfig.get_config_var('MULTIARCH')
        self.check_glx_features()
        self.check_dock_features()
        self.check_pulldown_terminal_features()
        self.check_hud_features()
        self.check_panel_features()
        self.check_wm_features()
        self.previous_wm = self.current_wm

        # Load the Glade UI file
        self.builder = Gtk.Builder()
        if os.path.exists('./data/mate-tweak.ui'):
            print('Development mode.')
            self.builder.add_from_file('./data/mate-tweak.ui')
        else:
            self.builder.add_from_file('/usr/lib/mate-tweak/mate-tweak.ui')

        self.window = self.builder.get_object( "main_window" )
        self.builder.get_object("main_window").connect("destroy", Gtk.main_quit)
        self.builder.get_object("button_save_panels").connect("clicked", self.save_panels)
        self.builder.get_object("button_delete_panel").connect("clicked", self.delete_panel)
        self.builder.get_object("button_compiz_reset").connect("clicked", self.compiz_reset)
        self.builder.get_object("button_ccsm").connect("clicked", self.launch_ccsm)

        side_desktop_options = SidePage(0, _("Desktop"), "user-desktop")
        side_interface = SidePage(1, _("Interface"), "preferences-desktop")
        side_panel = SidePage(2, _("Panel"), "mate-panel")
        side_windows = SidePage(3, _("Windows"), "preferences-system-windows")
        self.sidePages = [side_desktop_options, side_interface, side_panel, side_windows]

        # create the backing store for the side nav-view.
        theme = Gtk.IconTheme.get_default()
        self.store = Gtk.ListStore(str, GdkPixbuf.Pixbuf)
        for sidePage in self.sidePages:
            img = theme.load_icon(sidePage.icon, 48, 0)
            self.store.append([sidePage.name, img])

        target = int(self.sidePages[0].notebook_index)
        self.builder.get_object("main_notebook").set_current_page(target)

        # Do not show Performance or Window behaviour configuration
        # options when Marco or Metacity are not currently running.
        if 'marco' in self.current_wm or 'metacity' in self.current_wm:
            self.builder.get_object('frame_performance').props.sensitive = True
            self.builder.get_object('checkbox_snapping').props.sensitive = True
        else:
            self.builder.get_object('frame_performance').props.sensitive = False
            self.builder.get_object('checkbox_snapping').props.sensitive = False

        if not self.current_wm == "compiz":
            self.builder.get_object('button_compiz_reset').hide()
            self.builder.get_object('button_ccsm').hide()

        if not self.maximus_available:
            self.builder.get_object('checkbox_undecorate').props.sensitive = False
            self.builder.get_object('checkbox_always_maximize').props.sensitive = False
        else:
            self.init_checkbox("org.mate.maximus", "undecorate", "checkbox_undecorate")
            self.init_checkbox("org.mate.maximus", "no-maximize", "checkbox_always_maximize")

        # set up the side view - navigation.
        self.builder.get_object("side_view").set_text_column(0)
        self.builder.get_object("side_view").set_pixbuf_column(1)
        self.builder.get_object("side_view").set_model(self.store)
        self.builder.get_object("side_view").select_path(Gtk.TreePath.new_first())
        self.builder.get_object("side_view").connect("selection_changed", self.side_view_nav)

        # set up larger components.
        self.builder.get_object("main_window").set_title(_("MATE Tweak"))

        # i18n
        self.builder.get_object("label_desktop_icons").set_markup("<b>" + _("Desktop icons") + "</b>")
        self.builder.get_object("label_performance").set_markup("<b>" + _("Performance") + "</b>")
        self.builder.get_object("label_behaviour").set_markup("<b>" + _("Window Behaviour") + "</b>")
        self.builder.get_object("label_appearance").set_markup("<b>" + _("Appearance") + "</b>")
        self.builder.get_object("label_panels").set_markup("<b>" + _("Panels") + "</b>")
        self.builder.get_object("label_panel_features").set_markup("<b>" + _("Panel Features") + "</b>")
        self.builder.get_object("label_menu_features").set_markup("<b>" + _("Panel Menu Features") + "</b>")
        self.builder.get_object("label_icons").set_markup("<b>" + _("Icons") + "</b>")
        self.builder.get_object("label_context_menus").set_markup("<b>" + _("Context menus") + "</b>")
        self.builder.get_object("label_toolbars").set_markup("<b>" + _("Toolbars") + "</b>")
        self.builder.get_object("label_window_manager").set_markup("<b>" + _("Window manager") + "</b>")

        self.builder.get_object("caption_desktop_icons").set_markup("<small>" + _("Select the Desktop Icons you want enabled:") + "</small>")
        self.builder.get_object("checkbox_show_icons").set_label(_("Show Desktop Icons"))
        self.builder.get_object("checkbox_show_icons").set_tooltip_text(_("When disabled the desktop will be unmanaged with no icons or file manager access"))
        self.builder.get_object("checkbox_computer").set_label(_("Computer"))
        self.builder.get_object("checkbox_home").set_label(_("Home"))
        self.builder.get_object("checkbox_network").set_label(_("Network"))
        self.builder.get_object("checkbox_trash").set_label(_("Trash"))
        self.builder.get_object("checkbox_volumes").set_label(_("Mounted Volumes"))

        self.builder.get_object("checkbox_animations").set_label(_("Enable animations"))
        self.builder.get_object("checkbox_animations").set_tooltip_text(_("Whether animations should be displayed by the window manager and panel"))
        self.builder.get_object("checkbox_resources").set_label(_("Do not show window content when moving windows"))
        self.builder.get_object("checkbox_resources").set_tooltip_text(_("Provide less feedback when moving windows by using wireframes"))
        self.builder.get_object("label_performance_tuning").set_markup("<small>" + _("Window manager performance tuning.") + "</small>")

        self.builder.get_object("checkbox_snapping").set_label(_("Enable window snapping"))
        self.builder.get_object("checkbox_snapping").set_tooltip_text(_("Dropping windows on screen edges maximizes them vertically and resizes them horizontally to cover half the available area"))
        self.builder.get_object("checkbox_undecorate").set_label(_("Undecorate maximized windows"))
        self.builder.get_object("checkbox_undecorate").set_tooltip_text(_("Undecorate windows when maximized"))
        self.builder.get_object("checkbox_always_maximize").set_label(_("Do not auto-maximize new windows"))
        self.builder.get_object("checkbox_always_maximize").set_tooltip_text(_("Do not automatically maximize newly opened windows"))

        self.builder.get_object("label_window_control").set_markup("<small>" + _("Window control placement.") + "</small>")

        self.builder.get_object("button_save_panels").set_tooltip_text(_("Save the current panel layout as your own custom version"))
        self.builder.get_object("button_delete_panel").set_tooltip_text(_("Delete the currently selected panel layout"))
        self.builder.get_object("button_ccsm").set_label(_("Open CCSM"))
        self.builder.get_object("button_ccsm").set_tooltip_text(_("Open the Compiz configuration and settings manager"))
        self.builder.get_object("button_compiz_reset").set_label(_("Reset Compiz"))
        self.builder.get_object("button_compiz_reset").set_tooltip_text(_("Reset the current Compiz configuration to factory defaults"))

        self.builder.get_object("checkbutton_keyboard_led").set_label(_("Enable keyboard LED"))
        self.builder.get_object("checkbutton_keyboard_led").set_tooltip_text(_("Show keyboard LED indicators in the notifcation tray"))
        self.builder.get_object("checkbutton_pulldown_terminal").set_label(_("Enable pull-down terminal"))
        self.builder.get_object("checkbutton_pulldown_terminal").set_tooltip_text(_("When enabled press F12 to pull down terminal"))

        self.builder.get_object("checkbutton_show_applications").set_label(_("Show Applications"))
        self.builder.get_object("checkbutton_show_applications").set_tooltip_text(_("Show Applications item in the menu bar"))
        self.builder.get_object("checkbutton_show_places").set_label(_("Show Places"))
        self.builder.get_object("checkbutton_show_places").set_tooltip_text(_("Show Places item in the menu bar"))
        self.builder.get_object("checkbutton_show_system").set_label(_("Show System"))
        self.builder.get_object("checkbutton_show_system").set_tooltip_text(_("Show System item in the menu bar"))

        self.builder.get_object("checkbutton_menuicon").set_label(_("Show icons on menus"))
        self.builder.get_object("checkbutton_menuicon").set_tooltip_text(_("Whether menus may display an icon next to a menu entry"))
        self.builder.get_object("checkbutton_button_icons").set_label(_("Show icons on buttons"))
        self.builder.get_object("checkbutton_button_icons").set_tooltip_text(_("Whether buttons may display an icon in addition to the button text"))
        self.builder.get_object("checkbutton_im_menu").set_label(_("Show Input Methods menu in context menus"))
        self.builder.get_object("checkbutton_im_menu").set_tooltip_text(_("Whether the context menus of entries and text views should offer to change the input method"))
        self.builder.get_object("checkbutton_unicode").set_label(_("Show Unicode Control Character menu in context menus"))
        self.builder.get_object("checkbutton_unicode").set_tooltip_text(_("Whether the context menu of entries and text views should offer the insert control characters"))

        self.builder.get_object("label_toolbar_style").set_text(_("Style:"))
        self.builder.get_object("label_toolbar_icon_size").set_text(_("Icon size:"))

        # Desktop page
        self.init_checkbox("org.mate.background", "show-desktop-icons", "checkbox_show_icons")
        self.init_checkbox("org.mate.caja.desktop", "computer-icon-visible", "checkbox_computer")
        self.init_checkbox("org.mate.caja.desktop", "home-icon-visible", "checkbox_home")
        self.init_checkbox("org.mate.caja.desktop", "network-icon-visible", "checkbox_network")
        self.init_checkbox("org.mate.caja.desktop", "trash-icon-visible", "checkbox_trash")
        self.init_checkbox("org.mate.caja.desktop", "volumes-visible", "checkbox_volumes")
        self.toggle_desktop_icons_sensitiveness()

        # interface page
        self.init_checkbox("org.mate.interface", "menus-have-icons", "checkbutton_menuicon")
        self.init_checkbox("org.mate.interface", "show-input-method-menu","checkbutton_im_menu")
        self.init_checkbox("org.mate.interface", "show-unicode-menu", "checkbutton_unicode")
        self.init_checkbox("org.mate.interface", "buttons-have-icons", "checkbutton_button_icons")
        self.init_checkbox("org.mate.peripherals-keyboard-xkb.general", "duplicate-leds", "checkbutton_keyboard_led")

        iconSizes = Gtk.ListStore(str, str)
        iconSizes.append([_("Small"), "small-toolbar"])
        iconSizes.append([_("Large"), "large-toolbar"])
        self.builder.get_object("combobox_toolbar_icon_size").set_model(iconSizes)
        self.init_combobox("org.mate.interface", "toolbar-icons-size", "combobox_toolbar_icon_size")

        # Window control button
        self.update_window_controls()

        # Window manager
        self.make_list_of_window_managers()
        self.builder.get_object("combobox_window_manager").set_tooltip_text(_("The new window manager will be activated upon selection."))
        self.builder.get_object("caption_window_manager").set_markup("<small>" + _("Select a window manager.") + "</small>")
        self.init_combobox("org.mate.session.required-components", "windowmanager", "combobox_window_manager")
        self.update_windowmanager_widgets()

        # Panel layouts
        self.make_list_of_panel_layouts()
        self.builder.get_object("caption_panels").set_markup("<small>" + _("Select a panel layout to change the user interface.") + "</small>")
        self.builder.get_object("combobox_panels").set_tooltip_text(_("Select a panel layout."))
        self.init_panel_features()

        # Panel icon sizes
        panel_icon_sizes = Gtk.ListStore(str, str)
        panel_icon_sizes.append([_("Default"), "default"])
        panel_icon_sizes.append([_("16px"), "16px"])
        panel_icon_sizes.append([_("22px"), "22px"])
        panel_icon_sizes.append([_("24px"), "24px"])
        panel_icon_sizes.append([_("32px"), "32px"])
        panel_icon_sizes.append([_("48px"), "48px"])

        self.builder.get_object("combobox_panel_icon_size").set_model(panel_icon_sizes)
        self.init_combobox("org.mate.panel.menubar", "icon-size", "combobox_panel_icon_size")
        self.builder.get_object("combobox_panel_icon_size").set_tooltip_text(_("Set the panel icon size."))
        self.builder.get_object("caption_panel_icon_size").set_markup("<small>" + _("Select the icon size for panel icons.") + "</small>")

        self.builder.get_object("combobox_panel_menu_icon_size").set_model(panel_icon_sizes)
        self.init_combobox("org.mate.panel.menubar", "item-icon-size", "combobox_panel_menu_icon_size")
        self.builder.get_object("combobox_panel_menu_icon_size").set_tooltip_text(_("Set the icon size of menu items used in the panel."))
        self.builder.get_object("caption_panel_menu_icon_size").set_markup("<small>" + _("Select the icon size for menu items in the panel.") + "</small>")

        # Pulldown Terminal
        self.builder.get_object("checkbutton_pulldown_terminal").connect("toggled", self.toggle_pulldown_terminal)
        self.builder.get_object("checkbutton_pulldown_terminal").set_active(self.pulldown_terminal_enabled)
        if not self.pulldown_terminal:
            self.builder.get_object('checkbutton_pulldown_terminal').props.sensitive = False

        # Panel Menu features
        self.init_checkbox("org.mate.panel.menubar", "show-applications", "checkbutton_show_applications")
        self.init_checkbox("org.mate.panel.menubar", "show-places", "checkbutton_show_places")
        self.init_checkbox("org.mate.panel.menubar", "show-desktop", "checkbutton_show_system")
        if self.current_layout.startswith('pantheon'):
            self.builder.get_object('checkbutton_show_applications').props.sensitive = False

        # toolbar icon styles
        iconStyles = Gtk.ListStore(str, str)
        iconStyles.append([_("Text below items"), "both"])
        iconStyles.append([_("Text beside items"), "both-horiz"])
        iconStyles.append([_("Icons only"), "icons"])
        iconStyles.append([_("Text only"), "text"])
        self.builder.get_object("combobox_toolbar_style").set_model(iconStyles)
        self.init_combobox("org.mate.interface", "toolbar-style", "combobox_toolbar_style")
        self.builder.get_object("close_tweak").connect("clicked", self.close_tweak)
        self.builder.get_object("main_window").show()

if __name__ == "__main__":
    setproctitle.setproctitle('mate-tweak')
    MateTweak()
    Gtk.main()
