#!/usr/bin/env python3

# Copyright (C) 2007-2014 by Clement Lefebvre <root@linuxmint.com>
# Copyright (C) 2015-2016 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 psutil
import shutil
import signal
import subprocess
import string
import sys
import time

from ctypes import cdll, byref, create_string_buffer
from subprocess import DEVNULL, PIPE

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

from gi.repository import GLib

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

__VERSION__ = '3.5.10'

__SYNAPSE__ = """[Desktop Entry]
Name=Synapse
Exec=synapse --startup
Encoding=UTF-8
Type=Application
X-GNOME-Autostart-enabled=true
X-MATE-Autostart-enabled=true
Icon=synapse"""

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

def set_proc_title(name=None):
    '''Set the process title'''

    if not name:
        name = os.path.basename(sys.argv[0])

    libc = cdll.LoadLibrary('libc.so.6')
    buff = create_string_buffer(len(name)+1)
    buff.value = name.encode("UTF-8")
    ret = libc.prctl(15, byref(buff), 0, 0, 0)

    if ret != 0:
        print("Failed to set process title")

    return ret

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 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 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_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 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, process_name):
        current_user = os.getuid()
        for process in psutil.process_iter():
            if process.name() == process_name and process.uids().real == current_user:
                return True

        return False

    def kill_process(self, process_name):
        current_user = os.getuid()
        for process in psutil.process_iter():
            if process.name() == process_name and process.uids().real == current_user:
                #print('Killing ' + process.name())
                process.kill()

    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 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 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 not composite_enabled:
                self.current_wm += '-no-composite'
        elif ('Mutter' in current_wm):
            self.current_wm = current_wm.lower()
        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.marco_capable = False
        self.marco_compton_capable = False
        self.marco_no_composite_capable = False
        self.metacity_capable = False
        self.metacity_compton_capable = False
        self.metacity_no_composite_capable = False
        self.mutter_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('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-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-no-composite') and self.find_on_path('metacity'):
            self.metacity_no_composite_capable = True

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

        if self.find_on_path('mutter'):
            if not self.software_rasterizer and self.texture_from_pixmap:
                self.mutter_capable = True

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

        # Make sure Compiz, Metacity and Mutter use the same theme as
        # Marco to prevent theme/xcursor changes when switching themes.

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

        if (new_wm == 'compiz') or (new_wm == 'mutter') 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.16 - this schema may not be installed
            metacity_schema = schemasource.lookup('org.gnome.metacity', False)
            if metacity_schema:
                self.set_string('org.gnome.metacity', None, 'theme', mate_theme)

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

        # Metacity has a habbit of leaving a process running.
        self.kill_process('metacity')

        if ('compton' 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)

        # Sometimes gtk-window-manager is left behind.
        if self.current_wm == 'compiz':
            self.kill_process('gtk-window-decorator')

        # Replace mate-panel and dock when switching Compiz to avoid strut issues.
        if new_wm == 'compiz':
            if self.dock_enabled:
                self.kill_process(self.dock)
                time.sleep(1)
                subprocess.Popen([self.dock], stdout=DEVNULL, stderr=DEVNULL)
            subprocess.Popen(['mate-panel', '--replace'], 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()

    def confirm_windowmanager(self):
        # Detect switch to "risky" window manager.
        if ('compiz' in self.current_wm) or ('compton' in self.current_wm):
            # Should we prompt for confirmation after switching window manager.
            if 'compton' in self.previous_wm and 'compton' in self.current_wm:
                confirm = False
            else:
                confirm = True

            if confirm:
                time.sleep(2)
                confirm_text = _('<b>Does this window manager look OK?</b>\n\nThe window manager will be restored to the fail-safe configuration in 15 seconds.')
                title_text = _('Switching to ')
                ok_label = _('Keep This Window Manager')
                cancel_label = ('Restore Fail-safe Window Manager')
                confirm = subprocess.Popen(['zenity',
                                 '--question',
                                 "--title=" + title_text + self.current_wm,
                                 "--text=" + confirm_text,
                                 "--ok-label=" + ok_label,
                                 "--cancel-label=" + cancel_label,
                                 '--window-icon=question',
                                 '--timeout=15'],
                                 stdin=PIPE, stdout=PIPE, stderr=PIPE)
                output, error = confirm.communicate()
                if confirm.returncode == 0:
                    print('Keeping ' + self.current_wm)
                elif confirm.returncode == 1 or confirm.returncode == 5:
                    print('Reverting to fail-safe.')
                    self.previous_wm = self.current_wm = 'marco'
                    self.set_string('org.mate.session.required-components', None, 'windowmanager', self.current_wm)
                    self.update_combobox('org.mate.session.required-components', 'windowmanager', 'combobox_window_manager')
                    self.replace_windowmanager(self.previous_wm)
                    return True
                else:
                    print("Unknown")

        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 remove_dock_autostart(self):
        # Old versions of MATE Tweak setup an autostart, these should
        # be removed.
        config_dir = GLib.get_user_config_dir()
        if os.path.exists(os.path.join(config_dir, 'autostart/') + self.dock + '.desktop'):
            os.remove(os.path.join(config_dir, 'autostart/') + self.dock + '.desktop')

    def enable_dock(self):
        self.set_string('org.mate.session.required-components', None, 'dock', self.dock)
        self.remove_dock_autostart()

        # Docks require compositing
        if self.current_wm == 'marco':
            schema = 'org.mate.Marco.general'
        elif self.current_wm == 'metacity':
            schema = 'org.gnome.metacity'
        else:
            schema = None

        if schema is not None:
            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):
        self.set_string('org.mate.session.required-components', None, 'dock', '')
        self.remove_dock_autostart()
        self.kill_process(self.dock)
        self.dock_enabled = False

    def enable_launcher(self):
        leds_enabled = self.get_bool('org.mate.peripherals-keyboard-xkb.general', None, 'duplicate-leds')
        config_dir = GLib.get_user_config_dir()
        if self.launcher == 'synapse':
            synapse_autostart = __SYNAPSE__

            self.mkdir_p(os.path.join(config_dir, 'autostart/'))
            if not os.path.exists(os.path.join(config_dir, 'autostart/') + self.launcher + '.desktop'):
                with open(os.path.join(config_dir, 'autostart/') + self.launcher + '.desktop','w') as autostart:
                    autostart.write(synapse_autostart)

            # Launch synapse, if it is not already enabled.
            if not self.launcher_enabled:
                if leds_enabled:
                    self.set_bool('org.mate.peripherals-keyboard-xkb.general', None, 'duplicate-leds', False)

                subprocess.Popen([self.launcher, '--startup'], stdout=DEVNULL, stderr=DEVNULL)
                self.launcher_enabled = True

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

    def disable_launcher(self):
        leds_enabled = self.get_bool('org.mate.peripherals-keyboard-xkb.general', None, 'duplicate-leds')
        config_dir = GLib.get_user_config_dir()
        if self.launcher == 'synapse':
            if os.path.exists(os.path.join(config_dir, 'autostart/') + self.launcher + '.desktop'):
                os.remove(os.path.join(config_dir, 'autostart/') + self.launcher + '.desktop')

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

            self.kill_process(self.launcher)

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

        self.launcher_enabled = False

    def enable_mate_volume_applet(self):
        subprocess.Popen(['mate-volume-control-applet'], stdout=DEVNULL, stderr=DEVNULL)

        # If the system autostart is missing create a user autostart.
        if not os.path.exists('/etc/xdg/autostart/mate-volume-control-applet.desktop'):
            config_dir = GLib.get_user_config_dir()
            self.mkdir_p(os.path.join(config_dir, 'autostart/'))
            shutil.copy2('/usr/share/mate-tweak/mate-volume-control-applet.desktop', os.path.join(config_dir, 'autostart/'))

    def disable_mate_volume_applet(self):
        self.kill_process('mate-volume-control-applet')

        # If a system or user autostart exists, remove it.
        config_dir = GLib.get_user_config_dir()
        if os.path.exists(os.path.join(config_dir, 'autostart/') + 'mate-volume-control-applet.desktop'):
            os.remove(os.path.join(config_dir, 'autostart/') + 'mate-volume-control-applet.desktop')
        if os.path.exists('/etc/xdg/autostart/mate-volume-control-applet.desktop'):
            subprocess.call(['pkexec', '/usr/lib/mate-tweak/disable-mate-volume-applet'], stdout=DEVNULL, stderr=DEVNULL)

    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_indicators(self, panel_layout):
        with open(os.path.join('/','usr','share','mate-panel','layouts', panel_layout + '.layout'), 'rb', 0) as file, \
            mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) as s:
            if s.find(b'IndicatorApplet') != -1:
                return True
        return False

    def update_panel_layout_features(self, panel_layout):
        # If the selected panel layout has MATE Menu and Indicators then
        # make those options visible.
        if self.panel_layout_exists(panel_layout + '-fresh') and \
           self.panel_layout_exists(panel_layout + '-indicators') and \
           self.panel_layout_exists(panel_layout + '-indicators-fresh'):
            if not self.indicators_available:
                self.builder.get_object('checkbutton_indicators').props.sensitive = False
            else:
                self.builder.get_object('checkbutton_indicators').props.sensitive = True
            if not self.mate_menu_available:
                self.builder.get_object('checkbutton_advanced_menu').props.sensitive = False
            else:
                self.builder.get_object('checkbutton_advanced_menu').props.sensitive = True
        else:
            self.builder.get_object('checkbutton_indicators').props.sensitive = False
            self.builder.get_object('checkbutton_advanced_menu').props.sensitive = False

    def replace_panel_layout(self, panel_layout):
        leds_enabled = self.get_bool('org.mate.peripherals-keyboard-xkb.general', None, 'duplicate-leds')

        self.update_panel_layout_features(panel_layout)

        new_layout = panel_layout
        if self.builder.get_object('checkbutton_indicators').get_active() and \
           self.builder.get_object('checkbutton_advanced_menu').get_active():
            new_layout += '-indicators-fresh'
        elif self.builder.get_object('checkbutton_indicators').get_active():
            new_layout += '-indicators'
        elif self.builder.get_object('checkbutton_advanced_menu').get_active():
            new_layout += '-fresh'

        if not self.panel_layout_exists(new_layout):
            new_layout = panel_layout

        print('Switching to: ' + new_layout)
        if ('fresh' in new_layout) or \
           (new_layout.startswith('mutiny')) or \
           (new_layout.startswith('netbook')) or \
           (new_layout.startswith('opensuse')) or \
           (new_layout.startswith('redmond')):
            self.builder.get_object('box_menu_features').props.sensitive = False
        else:
            self.builder.get_object('box_menu_features').props.sensitive = True

        if leds_enabled:
            self.set_bool('org.mate.peripherals-keyboard-xkb.general', None, 'duplicate-leds', False)
        self.kill_process('mate-panel')
        subprocess.call(['dconf', 'reset', '-f', '/org/mate/panel/objects/'], stdout=DEVNULL, stderr=DEVNULL)
        subprocess.call(['dconf', 'reset', '-f', '/org/mate/panel/toplevels/'], stdout=DEVNULL, stderr=DEVNULL)
        # 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'
            os.system(cmd)
        else:
            subprocess.call(['mate-panel', '--layout', new_layout, '--reset'], stdout=DEVNULL, stderr=DEVNULL)

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

        # Indicator enabled layouts auto-load the sound-indicator.
        # Therefore, make sure mate-volume-control-applet is killed or
        # started accordingly.
        if self.panel_layout_uses_indicators(new_layout):
            self.disable_mate_volume_applet()
        else:
            self.enable_mate_volume_applet()

        thumbnail_schema_id = "org.compiz.thumbnail"
        thumbnail_profile_path = '/org/compiz/profiles/mate/plugins/thumbnail/'

        # Determine if the dock should be enabled
        if (new_layout.startswith('eleven') and self.dock is not None) or \
           (new_layout.startswith('wimpy') and self.dock is not None):
            if 'compiz' in self.current_wm:
                # https://bugs.launchpad.net/ubuntu-mate/+bug/1437611
                self.set_bool(thumbnail_schema_id, thumbnail_profile_path, 'current-viewport', False)
            self.enable_dock()
        elif new_layout.find('-tweak') != -1 and self.dock_enabled:
            if 'compiz' in self.current_wm:
                # https://bugs.launchpad.net/ubuntu-mate/+bug/1437611
                self.set_bool(thumbnail_schema_id, thumbnail_profile_path, 'current-viewport', False)
        elif not new_layout.startswith('eleven'):
            if 'compiz' in self.current_wm:
                # https://bugs.launchpad.net/ubuntu-mate/+bug/1437611
                self.set_bool(thumbnail_schema_id, thumbnail_profile_path, 'current-viewport', True)
            self.disable_dock()

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

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

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

        if 'indicators' in self.current_layout:
            self.builder.get_object('checkbutton_indicators').set_active(True)
        else:
            self.builder.get_object('checkbutton_indicators').set_active(False)

        if 'fresh' in self.current_layout:
            self.builder.get_object('checkbutton_advanced_menu').set_active(True)
            self.builder.get_object('box_menu_features').props.sensitive = False
        else:
            self.builder.get_object('checkbutton_advanced_menu').set_active(False)
            self.builder.get_object('box_menu_features').props.sensitive = True

        self.update_panel_layout_features(self.base_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.base_layout == row[1]):
                    widget.set_active(index)
                    break
                index = index +1
            widget.connect("changed", self.combo_fallback, 'org.mate.panel', 'default-layout')

    def toggle_panel_features(self, widget):
        self.get_panel_layout()
        self.replace_panel_layout(self.base_layout)

    def toggle_launcher(self, checkbutton):
        if checkbutton.get_active():
            self.enable_launcher()
        else:
            self.disable_launcher()

    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
        elif self.current_wm == 'mutter':
            self.builder.get_object('frame_performance').props.sensitive = False
            self.builder.get_object('frame_behaviour').props.sensitive = True
            self.init_checkbox('org.gnome.mutter', 'edge-tiling', 'checkbox_snapping')
        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()
        else:
           self.builder.get_object('button_compiz_reset').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.confirm_windowmanager()
            self.update_windowmanager_widgets()

        elif schema == "org.mate.panel" and key == "default-layout":
            # If the panel layout is being changed, replace it now!
            panel_layout = value
            self.replace_panel_layout(panel_layout)

    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_launcher_features(self):
        config_dir = GLib.get_user_config_dir()

        if self.find_on_path('synapse') and \
            os.path.exists(os.path.join('/','usr','share','applications', 'synapse.desktop')):
            self.launcher = 'synapse'
        else:
            self.launcher = None

        if self.launcher is not None and os.path.exists(os.path.join(config_dir, 'autostart/') + self.launcher + '.desktop'):
            self.launcher_enabled = True
        else:
            self.launcher_enabled = 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.topmenu_available = False
        self.mate_menu_available = False
        self.maximus_available = False
        self.mint_menu_available = False
        self.volume_applet_enabled = False

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

        if os.path.exists('/usr/lib/indicators/7/libapplication.so') and \
           os.path.exists('/usr/lib/indicator-sound-gtk2/indicator-sound-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-applets/topmenu-mate-panel-applet') and \
           os.path.exists('/usr/lib/libtopmenu-client-gtk2.so.0') and \
           os.path.exists('/usr/lib/libtopmenu-client-gtk3.so.0'):
            self.topmenu_available = True

        if os.path.exists('/usr/lib/mate-menu/mate-menu.py'):
            self.mate_menu_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 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_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_compton_capable and not self.software_rasterizer:
            wms.append([_("Metacity (Compton GPU compositor)"), 'metacity-compton'])
        if self.mutter_capable:
            wms.append([_("Mutter (Elegant GPU accelerated desktop effects)"), 'mutter'])
        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, current_layout])

        if self.dock is not None:
            if self.panel_layout_exists('eleven'):
                panels.append([_("Cupertino"), "eleven"])

        if self.panel_layout_exists('fedora'):
            panels.append([_("Fedora"), "fedora"])

        if self.panel_layout_exists('default'):
            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('mutiny') and \
           self.mate_dock_available and \
           self.topmenu_available:
            panels.append([_("Mutiny"), "mutiny"])

        if self.panel_layout_exists('netbook') and self.maximus_available:
            panels.append([_("Netbook"), "netbook"])

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

        if self.panel_layout_exists('redmond'):
            panels.append([_("Redmond"), "redmond"])

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

        if self.panel_layout_exists('wimpy'):
            panels.append([_("Wimpy"), "wimpy"])

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

    def save_panels(self, widget):
        layoutname = getpass.getuser()
        self.set_string('org.mate.panel', None, 'default-layout', layoutname + '-tweak')
        subprocess.call(['/usr/lib/mate-tweak/mate-panel-backup', layoutname], stdout=DEVNULL, stderr=DEVNULL)
        subprocess.call(['pkexec', '/usr/lib/mate-tweak/install-mate-panel-layout', 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 /usr/share/mate-panel/layouts/' + layoutname +'-tweak.layout'),'dialog-information')
        save_panels_notify.show()
        self.make_list_of_panel_layouts()
        self.update_combobox('org.mate.panel', 'default-layout', 'combobox_panels')

    def compiz_reset(self, widget):
        # Switch to Marco. Purge all Compiz config and cache. Switch to Compiz.
        self.replace_windowmanager('marco')
        subprocess.call(['dconf', 'reset', '-f', '/org/compiz/'], stdout=DEVNULL, stderr=DEVNULL)
        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()

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

        # Load the Glade UI file
        self.builder = Gtk.Builder()
        self.builder.add_from_file('/usr/lib/mate-tweak/mate-tweak.ui')
        # self.builder.add_from_file('./data/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_compiz_reset").connect("clicked", self.compiz_reset)

        side_desktop_options = SidePage(0, _("Desktop"), "user-desktop")
        side_interface = SidePage(2, _("Interface"), "preferences-desktop")
        side_windows = SidePage(1, _("Windows"), "preferences-system-windows")
        self.sidePages = [side_desktop_options, side_interface, 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
        elif self.current_wm == 'mutter':
            self.builder.get_object('frame_performance').props.sensitive = False
            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()

        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_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_resources").set_label(_("Do not show window content when moving windows"))
        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_undecorate").set_label(_("Undecorate maximized windows"))
        self.builder.get_object("checkbox_always_maximize").set_label(_("Do not auto-maximize new windows"))

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

        self.builder.get_object("button_save_panels").set_label(_("Save Panel Layout"))
        self.builder.get_object("checkbutton_indicators").set_label(_("Enable indicators"))
        self.builder.get_object("checkbutton_advanced_menu").set_label(_("Enable advanced menu"))
        self.builder.get_object("checkbutton_keyboard_led").set_label(_("Enable keyboard LED"))
        self.builder.get_object("checkbutton_launcher").set_label(_("Enable launcher"))

        self.builder.get_object("checkbutton_show_applications").set_label(_("Show Applications"))
        self.builder.get_object("checkbutton_show_places").set_label(_("Show Places"))
        self.builder.get_object("checkbutton_show_system").set_label(_("Show System"))

        self.builder.get_object("checkbutton_menuicon").set_label(_("Show icons on menus"))
        self.builder.get_object("checkbutton_button_icons").set_label(_("Show icons on buttons"))
        self.builder.get_object("checkbutton_im_menu").set_label(_("Show Input Methods menu in context menus"))
        self.builder.get_object("checkbutton_unicode").set_label(_("Show Unicode Control Character menu in context menus"))

        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
        layouts = Gtk.ListStore(str, str)
        layouts.append([_("Traditional (Right)"), "menu:minimize,maximize,close"])
        layouts.append([_("Contemporary (Left)"), "close,minimize,maximize:"])
        self.builder.get_object("combobox_window_control").set_model(layouts)
        self.init_combobox("org.mate.Marco.general", "button-layout", "combobox_window_control")

        # 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(_("The new panel layout will be activated on selection and destroy any customisations you might have made."))
        self.init_panel_features()
        self.builder.get_object("checkbutton_indicators").connect("toggled", self.toggle_panel_features)
        self.builder.get_object("checkbutton_advanced_menu").connect("toggled", self.toggle_panel_features)

        # Launcher
        self.builder.get_object("checkbutton_launcher").connect("toggled", self.toggle_launcher)
        self.builder.get_object("checkbutton_launcher").set_active(self.launcher_enabled)
        if not self.launcher:
            self.builder.get_object('checkbutton_launcher').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")

        # 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("main_window").show()

if __name__ == "__main__":
    set_proc_title()
    MateTweak()
    Gtk.main()
