using Gtk;
using Math;

// valac --pkg gtk+-3.0

/*
Budgie Window Shuffler II
Author: Jacob Vlijm
Copyright © 2017-2021 Ubuntu Budgie Developers
Website=https://ubuntubudgie.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 3 of the License, or 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, see <https://www.gnu.org/licenses/>.
*/


namespace ShufflerControls {

    //  GLib.Settings wallstreet_settings;
    GLib.Settings shuffler_settings;
    ExtrasDaemon client;

    [DBus (name = "org.UbuntuBudgie.ExtrasDaemon")]
    interface ExtrasDaemon : Object {
        public abstract bool ReloadShortcuts () throws Error;
    }

    private void setup_client () {
        try {
            client = Bus.get_proxy_sync (
                BusType.SESSION, "org.UbuntuBudgie.ExtrasDaemon",
                ("/org/ubuntubudgie/extrasdaemon")
            );
        }
        catch (Error e) {
            stderr.printf ("%s\n", e.message);
        }
    }


    class ControlsWindow : Gtk.Window {

        SpinButton columns_spin;
        SpinButton rows_spin;
        SpinButton topmarginspin;
        SpinButton leftmarginspin;
        SpinButton rightmarginspin;
        SpinButton bottommarginspin;
        SpinButton paddingspin;
        ToggleButton toggle_gui;
        ToggleButton toggle_shuffler;
        ToggleButton toggle_layouts;
        ToggleButton toggle_swapgeo;
        ToggleButton toggle_softmove;
        ToggleButton toggle_warning;
        Button layoutsetup_button;
        Label expl_label;
        Label warninglabel;
        Gtk.Grid supergrid;
        Stack controlwin_stack;
        bool daemonruns;

        public ControlsWindow () {

            // STRINGS & THINGS
            // settings
            string daemonexpl = (_("Enable tiling and window move shortcuts")) + ":";
            string guiexpl = (_("Enable grid tiling graphical shortcut"));
            string layoutsexpl = (_("Enable Shuffler's feature to setup a combination of windows in a grid layout"));
            string swapgeoexpl = (_("When using move shortcuts, swap window size & position if a window moves to an existing window's position"));
            string softmoveexpl = (_("Enable animation on move and tiling actions if general desktop animations are enabled"));
            string default_expl = (_("Move the mouse over a button for an explanation"));
            string cols_expl = (_("Number of columns, used by grid, move and tile-all shortcuts"));
            string rows_expl = (_("Number of rows, used by grid, move and tile-all shortcuts"));
            string warn_me = (_("Show a notification when attempting to resize a window below it's minimum size"));
            // margins
            string margin_header = (_("Margins between virtual grid and screen edges ")) + ":";
            // tiling
            string qtiling_header = (_("Shortcuts for quarter and half tiling & tiling to grid")) +":";
            string topleft = "Ctrl + Alt + 7".concat("\t\t\t\t", (_("Top-left")));
            string topright = "Ctrl + Alt + 9".concat("\t\t\t\t", (_("Top-right")));
            string bottomright = "Ctrl + Alt + 3".concat("\t\t\t\t", (_("Bottom-right")));
            string bottomleft = "Ctrl + Alt + 1".concat("\t\t\t\t", (_("Bottom-left")));
            string lefthalf = "Ctrl + Alt + 4".concat("\t\t\t\t", (_("Left-half")));
            string tophalf = "Ctrl + Alt + 8".concat("\t\t\t\t", (_("Top-half")));
            string righthalf = "Ctrl + Alt + 6".concat("\t\t\t\t", (_("Right-half")));
            string bottomhalf = "Ctrl + Alt + 2".concat("\t\t\t\t", (_("Bottom-half")));
            string tileall = "Control + Super + A".concat("\t\t", (_("Tile all windows to grid")));
            string calllayouts_header = (_("Layouts")) +":";
            string calllayouts = "Super + Alt + L".concat("\t\t\t", (_("Call the layouts quicklist & settings")));
            // Jump & resize
            string jump_header = (_("Shortcuts for moving to the nearest grid cell")) + ":";
            string jumpleft = "Super + Alt + ←".concat("\t\t\t", (_("Move left")));
            string jumpright = "Super + Alt + →".concat("\t\t\t", (_("Move right")));
            string jumpup = "Super + Alt + ↑".concat("\t\t\t", (_("Move up")));
            string jumpdown = "Super + Alt + ↓".concat("\t\t\t", (_("Move down")));
            string resize_header = (_("Shortcuts for resizing windows on grid")) + ":";
            string addhorizontally = "Control + Super + →".concat("\t\t", (_("Expand horizontally (to the right)")));
            string shrinkhorizontally = "Control + Super + ←".concat("\t\t", (_("Shrink horizontally (from the right)")));
            string addvertically = "Control + Super + ↓".concat("\t\t", (_("Expand vertically (down)")));
            string shrinkvertically = "Control + Super + ↑".concat("\t\t", (_("Shrink vertically (from the bottom)")));
            string addhorizontally_br = "Control + Super + Alt + ←".concat("\t", (_("Expand horizontally (to the left)")));
            string shrinkhorizontally_br = "Control + Super + Alt + →".concat("\t", (_("Shrink horizontally (from the left)")));
            string addvertically_br = "Control + Super + Alt + ↑".concat("\t", (_("Expand vertically (up)")));
            string shrinkvertically_br = "Control + Super + Alt + ↓".concat("\t", (_("Shrink vertically (from the top)")));
            string togglestickyneighbor = "Control + Super + N".concat("\t\t", (_("Toggle resizing opposite window")));
            // GUI grid
            string guigrid_header = (_("Shortcuts for the grid")) + ":";
            string callgrid = "Ctrl + Alt + S".concat("\t", (_("Show the grid")));
            string addcol = "→".concat("\t\t\t", (_("Add a column")));
            string addrow = "↓".concat("\t\t\t",(_("Add a row")));
            string remcol = "←".concat("\t\t\t", (_("Remove a column")));
            string remrow = "↑".concat("\t\t\t", (_("Remove a row")));
            string shift_click = (_("To select a range of tiles, use Shift + click"));
            string set_greyshade = (_("Set the shade of gray for the grid")) + ":";
            string lighter = (_("Press + or scroll up to lighten the grid shade"));
            string darker = (_("Press - or scroll down to darken the grid shade"));
            // WINDOW STUFF
            string shufflercontrols_stylecss = """
            .explanation {
                font-style: italic;
            }
            .stackbuttons {
                border-radius: 0px 0px 0px 0px;
            }
            .stackbuttonleft {
                border-radius: 8px 0px 0px 8px;
            }
            .stackbuttonright{
                border-radius: 0px 8px 8px 0px;
            }
            .header {
                font-weight: bold;
            }
            .warning {
                color: red;
            }
            """;
            // window basics
            this.set_default_size(10, 10);
            this.set_resizable(false);
            //this.set_resizable(false);
            initialiseLocaleLanguageSupport();
            this.set_position(Gtk.WindowPosition.CENTER);
            this.title = (_("Window Shuffler Control"));
            // lay out essential window elements
            supergrid = new Gtk.Grid();
            this.add(supergrid);
            controlwin_stack = new Stack();
            controlwin_stack.set_transition_type(
                Gtk.StackTransitionType.SLIDE_LEFT_RIGHT
            );
            var settingsgrid = new Gtk.Grid();
            controlwin_stack.add_named(settingsgrid, "settings");
            var marginsgrid = new Gtk.Grid();
            controlwin_stack.add_named(marginsgrid, "marginsrid");
            var shortcutsgrid = new Gtk.Grid();
            controlwin_stack.add_named(shortcutsgrid, "qhshortcuts");
            var jumpgrid = new Gtk.Grid();
            controlwin_stack.add_named(jumpgrid, "jumpshortcuts");
            var guigrid = new Gtk.Grid();
            controlwin_stack.add_named(guigrid, "guigrid");
            // SUPERGRID
            supergrid.attach(new Label("\n"), 0, 2, 1, 1);
            set_margins(supergrid);
            make_headerbutton ((_("Settings")), "stackbuttonleft", 1, "settings");
            make_headerbutton ((_("Margins")), "stackbuttons", 2, "marginsrid");
            make_headerbutton ((_("Tiling & Layouts")), "stackbuttons", 3, "qhshortcuts");
            make_headerbutton ((_("Move & resize")), "stackbuttons", 4, "jumpshortcuts");
            make_headerbutton ((_("Grid")), "stackbuttonright", 5, "guigrid");
            // STACK-PAGES
            // 1. settingsgrid - checkbuttons
            toggle_shuffler = new Gtk.CheckButton.with_label(
                (_("Enable Window Shuffler"))
            );
            settingsgrid.attach(toggle_shuffler, 1, 1, 1, 1);
            toggle_gui = new Gtk.CheckButton.with_label(
                (_("Enable Window Shuffler grid"))
            );
            settingsgrid.attach(toggle_gui, 1, 2, 1, 1);


            toggle_layouts = new Gtk.CheckButton.with_label(
                (_("Enable Window Layouts"))
            );
            settingsgrid.attach(toggle_layouts, 1, 3, 1, 1);

            var layoutsetup_box = new Box(Gtk.Orientation.HORIZONTAL, 0);

            layoutsetup_button = new Gtk.Button();
            layoutsetup_button.clicked.connect(()=> {
                string layoutsetup_path = Config.SHUFFLER_DIR + "/toggle_layouts_popup";
                print(@"$layoutsetup_path\n");

                try {
                    Process.spawn_command_line_sync(layoutsetup_path);
                }
                catch (Error e) {
                }

            });
            layoutsetup_button.label = (_("Setup Layouts"));
            layoutsetup_box.pack_start(layoutsetup_button, false, false, 2);
            settingsgrid.attach(layoutsetup_box, 1, 4, 1, 1);
            var givemesomespace = new Gtk.Label("");
            settingsgrid.attach(givemesomespace, 1, 8, 1, 1);
            toggle_swapgeo = new Gtk.CheckButton.with_label(
                (_("Swap windows"))
            );
            settingsgrid.attach(toggle_swapgeo, 1, 9, 1, 1);
            toggle_softmove = new Gtk.CheckButton.with_label(
                (_("Enable animation"))
            );
            settingsgrid.attach(toggle_softmove, 1, 10, 1, 1);
            toggle_warning = new Gtk.CheckButton.with_label(
                (_("Show notification on incorrect window size"))
            );
            settingsgrid.attach(toggle_warning, 1, 11, 1, 1);
            var empty = new Label("");
            settingsgrid.attach(empty, 1, 12, 1, 1);
            // settingsgrid - spinbuttonsection
            var colslabel = new Label("\n" + (_("Grid: columns & rows")) + "\n");
            colslabel.set_xalign(0);
            settingsgrid.attach(colslabel, 1, 13, 1, 1);
            var geogrid = new Gtk.Grid();
            settingsgrid.attach(geogrid, 1, 14, 2, 3);
            var columns_label = new Label((_("Columns")) + "\t");
            columns_label.set_xalign(0);
            geogrid.attach(columns_label, 0, 0, 1, 1);
            columns_spin = new Gtk.SpinButton.with_range(1, 10, 1);
            geogrid.attach(columns_spin, 1, 0, 1, 1);
            var rows_label = new Label((_("Rows")) + "\t");
            rows_label.set_xalign(0);
            geogrid.attach(rows_label, 0, 1, 1, 1);
            rows_spin = new Gtk.SpinButton.with_range(1, 10, 1);
            geogrid.attach(rows_spin, 1, 1, 1, 1);
            // settingsgrid - explanation section
            var empty2 = new Label("");
            geogrid.attach(empty2, 1, 20, 1, 1);
            // Prevent window bottom section from jumping: reserve space
            Gtk.Label expl_vertspace = new Gtk.Label("\n\n\n\n");
            settingsgrid.attach(expl_vertspace, 0, 21, 1, 1);
            // settingsgrid - explanation section & css provider
            Gdk.Screen screen = this.get_screen();
            Gtk.CssProvider css_provider = new Gtk.CssProvider();
            try {
                css_provider.load_from_data(shufflercontrols_stylecss);
                Gtk.StyleContext.add_provider_for_screen(
                    screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER
                );
            }
            catch (Error e) {
            }
            expl_label = new Gtk.Label(default_expl);
            expl_label.set_xalign(0);
            expl_label.set_line_wrap(true);
            expl_label.set_max_width_chars(50);
            settingsgrid.attach(expl_label, 1, 21, 110, 1);
            set_textstyle(expl_label, {"explanation"});
            // settingsgrid - bottomsection
            var okbox = new Box(Gtk.Orientation.HORIZONTAL, 0);
            var ok_button = new Button.with_label((_("Close")));
            ok_button.set_size_request(100, 5);
            okbox.pack_end(ok_button, false, false, 0);
            warninglabel = new Label("");
            warninglabel.set_xalign(0);
            supergrid.attach(warninglabel, 1, 99, 2, 1);
            supergrid.attach(okbox, 2, 99, 4, 1);
            ok_button.clicked.connect(Gtk.main_quit);
            this.destroy.connect(Gtk.main_quit);
            toggle_shuffler.enter_notify_event.connect(() => {
                expl_label.set_text(daemonexpl);
                return false;
            });
            toggle_shuffler.leave_notify_event.connect(() => {
                expl_label.set_text(default_expl);
                return false;
            });
            toggle_gui.enter_notify_event.connect(() => {
                expl_label.set_text(guiexpl);
                return false;
            });
            toggle_gui.leave_notify_event.connect(() => {
                expl_label.set_text(default_expl);
                return false;
            });
            toggle_layouts.enter_notify_event.connect(() => {
                expl_label.set_text(layoutsexpl);
                return false;
            });
            toggle_layouts.leave_notify_event.connect(() => {
                expl_label.set_text(default_expl);
                return false;
            });
            toggle_swapgeo.enter_notify_event.connect(() => {
                expl_label.set_text(swapgeoexpl);
                return false;
            });
            toggle_softmove.enter_notify_event.connect(() => {
                expl_label.set_text(softmoveexpl);
                return false;
            });
            toggle_softmove.leave_notify_event.connect(() => {
                expl_label.set_text(default_expl);
                return false;
            });
            toggle_warning.enter_notify_event.connect(() => {
                expl_label.set_text(warn_me);
                return false;
            });
            toggle_warning.leave_notify_event.connect(() => {
                expl_label.set_text(default_expl);
                return false;
            });
            toggle_swapgeo.leave_notify_event.connect(() => {
                expl_label.set_text(default_expl);
                return false;
            });
            columns_spin.enter_notify_event.connect(() => {
                expl_label.set_text(cols_expl);
                return false;
            });
            columns_spin.leave_notify_event.connect(() => {
                expl_label.set_text(default_expl);
                return false;
            });
            rows_spin.enter_notify_event.connect(() => {
                expl_label.set_text(rows_expl);
                return false;
            });
            rows_spin.leave_notify_event.connect(() => {
                expl_label.set_text(default_expl);
                return false;
            });

            // 2. marginsgrid
            Label marginheader = new Label(margin_header + "\n");
            marginheader.set_xalign(0);
            marginsgrid.attach(marginheader, 0, 0, 1, 1);
            set_textstyle(marginheader, {"header"});
            var marginsubgrid = new Gtk.Grid();
            marginsgrid.attach(marginsubgrid, 0, 1, 1, 1);
            marginsubgrid.set_column_homogeneous(true);
            marginsubgrid.attach(new Label(""), 0, 5, 1, 1);
            marginsubgrid.attach(new Label((_("Top"))+ "\n"), 2, 9, 1, 1);
            topmarginspin = new Gtk.SpinButton.with_range(0, 200, 1);
            marginsubgrid.attach(topmarginspin, 2, 10, 1, 1);
            marginsubgrid.attach(new Label((_("Left")) + "\n"), 1, 11, 1, 1);
            leftmarginspin = new Gtk.SpinButton.with_range(0, 200, 1);
            marginsubgrid.attach(leftmarginspin, 1, 12, 1, 1);
            marginsubgrid.attach(new Label((_("Right")) + "\n"), 3, 11, 1, 1);
            rightmarginspin = new Gtk.SpinButton.with_range(0, 200, 1);
            marginsubgrid.attach(rightmarginspin, 3, 12, 1, 1);
            marginsubgrid.attach(new Label((_("Bottom")) + "\n"), 2, 13, 1, 1);
            bottommarginspin = new Gtk.SpinButton.with_range(0, 200, 1);
            marginsubgrid.attach(bottommarginspin, 2, 14, 1, 1);
            var paddingbox = new Box(Gtk.Orientation.HORIZONTAL, 0);
            paddingspin = new Gtk.SpinButton.with_range(0, 40, 1);
            var paddinglabel = new Label((_("Window padding")) + ":\t\t");
            set_textstyle(paddinglabel, {"header"});
            paddingbox.pack_start(paddinglabel, false, false, 0);
            paddingbox.pack_start(paddingspin, false, false, 0);
            marginsgrid.attach(new Label(""), 0, 2, 1, 1);
            marginsgrid.attach(paddingbox, 0, 3, 1, 1);
            // 3. shortcutsgrid
            var qhshortcutsheader = new Label(qtiling_header);
            set_textstyle(qhshortcutsheader, {"header"});
            var spacer = new Label("");
            var tl = new Label(topleft);
            var tr = new Label(topright);
            var br = new Label(bottomright);
            var bl = new Label(bottomleft);
            var lh = new Label(lefthalf);
            var th = new Label(tophalf);
            var rh = new Label(righthalf);
            var bh = new Label(bottomhalf);
            var space4 = new Label("");
            var ta = new Label(tileall);
            Label[] qhshortc_labels = {
                qhshortcutsheader, spacer, tl, tr, br, bl, lh, th, rh, bh, space4, ta
            };
            int n = 1;
            foreach (Label l in qhshortc_labels) {
                l.set_xalign(0);
                shortcutsgrid.attach(l, 0, n, 1, 1);
                n += 1;
            }
            shortcutsgrid.attach(new Label("\t"), 0, 20, 1, 1);
            var calllayoutsheader = new Label(calllayouts_header);
            calllayoutsheader.set_xalign(0);
            set_textstyle(calllayoutsheader, {"header"});
            shortcutsgrid.attach(calllayoutsheader, 0, 21, 1, 1);
            var calllayouts_label = new Label(calllayouts);
            calllayouts_label.set_xalign(0);
            shortcutsgrid.attach(calllayouts_label, 0, 22, 1, 1);
            // 4. jumpgrid shortcuts
            var jumpshortcutsheader = new Label(jump_header);
            set_textstyle(jumpshortcutsheader, {"header"});
            var spacer2 = new Label("");
            var jump_left = new Label(jumpleft);
            var jump_right = new Label(jumpright);
            var jump_up = new Label(jumpup);
            var jump_down = new Label(jumpdown);
            Label[] jumpshortc_labels = {
                jumpshortcutsheader, spacer2, jump_left, jump_right, jump_up, jump_down
            };
            int n2 = 1;
            foreach (Label l in jumpshortc_labels) {
                l.set_xalign(0);
                jumpgrid.attach(l, 0, n2, 1, 1);
                n2 += 1;
            }
            jumpgrid.attach(new Label(""), 0, 9, 1, 1);
            var resizeheader = new Label(resize_header);
            set_textstyle(resizeheader, {"header"});
            var spacer4 = new Label("");
            var add_horizontally = new Label(addhorizontally);
            var shrink_horizontally = new Label(shrinkhorizontally);
            var add_vertically = new Label(addvertically);
            var shrink_vertically = new Label(shrinkvertically);

            var add_horizontally_br = new Label(addhorizontally_br);
            var shrink_horizontally_br = new Label(shrinkhorizontally_br);
            var add_vertically_br = new Label(addvertically_br);
            var shrink_vertically_br = new Label(shrinkvertically_br);
            var spacer5 = new Label("");
            var toggle_stickyneighbor = new Label(togglestickyneighbor);

            Label[] resizeshortc_labels = {
                resizeheader, spacer4, add_horizontally, shrink_horizontally,
                add_vertically, shrink_vertically, add_horizontally_br,
                shrink_horizontally_br, add_vertically_br, shrink_vertically_br,
                spacer5, toggle_stickyneighbor
            };
            n2 = 10;
            foreach (Label l in resizeshortc_labels) {
                l.set_xalign(0);
                jumpgrid.attach(l, 0, n2, 1, 1);
                n2 += 1;
            }
            // 5. guigrid
            var guigridheader = new Label(guigrid_header);
            set_textstyle(guigridheader, {"header"});
            var spacer3 = new Label("");
            var call_grid = new Label(callgrid);
            var add_col = new Label(addcol);
            var add_row = new Label(addrow);
            var rem_col = new Label(remcol);
            var rem_row = new Label(remrow);
            Label[] guigrid_labels = {
                guigridheader, spacer3, call_grid, add_col, add_row, rem_col, rem_row
            };
            int n3 = 1;
            foreach (Label l in guigrid_labels) {
                l.set_xalign(0);
                guigrid.attach(l, 0, n3, 1, 1);
                n3 += 1;
            }
            guigrid.attach(new Gtk.Label(""), 0, 20, 1, 1);
            var shiftclick = new Label(shift_click);
            shiftclick.set_xalign(0);
            guigrid.attach(shiftclick, 0, 21, 1, 1);
            guigrid.attach(new Gtk.Label(""), 0, 22, 1, 1);
            var greyshade_header = new Label(set_greyshade);
            greyshade_header.set_xalign(0);
            set_textstyle(greyshade_header, {"header"});
            guigrid.attach(greyshade_header, 0, 23, 1, 1);
            guigrid.attach(new Gtk.Label(""), 0, 24, 1, 1);
            var lighter_label = new Label(lighter);
            lighter_label.set_xalign(0);
            lighter_label.set_line_wrap(true);
            var darker_label = new Label(darker);
            darker_label.set_xalign(0);
            darker_label.set_line_wrap(true);
            guigrid.attach(lighter_label, 0, 25, 1, 1);
            guigrid.attach(darker_label, 0, 26, 1, 1);
            supergrid.attach(controlwin_stack, 1, 3, 4, 1);
            controlwin_stack.set_visible_child_name("settings");
            // get stuff
            get_currsettings();
            // connect stuff
            SpinButton[] spins = {
                columns_spin, rows_spin, topmarginspin,
                leftmarginspin, rightmarginspin, bottommarginspin,
                paddingspin
            };
            foreach (SpinButton spb in spins) {
                spb.value_changed.connect(set_fromspinbutton);
            }
            setup_client();
            toggle_shuffler.toggled.connect(manage_boolean);
            toggle_gui.toggled.connect(manage_boolean);
            toggle_layouts.toggled.connect(manage_boolean);
            toggle_layouts.toggled.connect(()=> {
                manage_boolean(toggle_layouts);
                layoutsetup_button.set_sensitive(toggle_layouts.get_active());
            });
            toggle_swapgeo.toggled.connect(manage_boolean);
            toggle_softmove.toggled.connect(manage_boolean);
            toggle_warning.toggled.connect(manage_boolean);
            this.show_all();
        }

        private Gtk.Button make_headerbutton (
            string name, string style, int pos, string target
        ) {
            var stackbutton = new Gtk.Button.with_label(name);
            set_buttonstyle(stackbutton, style);
            supergrid.attach(stackbutton, pos, 1, 1, 1);
            stackbutton.set_size_request(120, 10);
            stackbutton.clicked.connect(() => {
                controlwin_stack.set_visible_child_name(target);
            });
            return stackbutton;
        }

        private void set_buttonstyle (Gtk.Button b, string style) {
            var sct = b.get_style_context();
            sct.add_class(style);
        }

        private void set_textstyle (Gtk.Label l, string[] style) {
            var sct = l.get_style_context();
            string[] style_old = {"warning", "explanation", "header"};
            foreach (string s_old in style_old) {
                sct.remove_class(s_old);
            }
            foreach (string s in style) {
                sct.add_class(s);
            }
        }

        /**
        * Ensure translations are displayed correctly
        * according to the locale
        */

        public void initialiseLocaleLanguageSupport() {
            GLib.Intl.setlocale(GLib.LocaleCategory.ALL, "");
            GLib.Intl.bindtextdomain(
                Config.GETTEXT_PACKAGE, Config.PACKAGE_LOCALEDIR
            );
            GLib.Intl.bind_textdomain_codeset(
                Config.GETTEXT_PACKAGE, "UTF-8"
            );
            GLib.Intl.textdomain(Config.GETTEXT_PACKAGE);
        }

        private void get_currsettings () {
            bool currentlyactive = shuffler_settings.get_boolean("runshuffler");
            toggle_shuffler.set_active(currentlyactive);
            toggle_gui.set_sensitive(currentlyactive);
            toggle_layouts.set_sensitive(currentlyactive);
            toggle_swapgeo.set_sensitive(currentlyactive);
            toggle_softmove.set_sensitive(currentlyactive);
            toggle_warning.set_sensitive(currentlyactive);
            toggle_gui.set_active(shuffler_settings.get_boolean("runshufflergui"));
            bool layouts_isset = shuffler_settings.get_boolean("runlayouts");
            toggle_layouts.set_active(layouts_isset);
            layoutsetup_button.set_sensitive(layouts_isset);
            toggle_swapgeo.set_active(shuffler_settings.get_boolean("swapgeometry"));
            toggle_softmove.set_active(shuffler_settings.get_boolean("softmove"));
            toggle_warning.set_active(shuffler_settings.get_boolean("showwarning"));
            columns_spin.set_value(shuffler_settings.get_int("cols"));
            rows_spin.set_value(shuffler_settings.get_int("rows"));
            topmarginspin.set_value(shuffler_settings.get_int("margintop"));
            leftmarginspin.set_value(shuffler_settings.get_int("marginleft"));
            rightmarginspin.set_value(shuffler_settings.get_int("marginright"));
            bottommarginspin.set_value(shuffler_settings.get_int("marginbottom"));
            paddingspin.set_value(shuffler_settings.get_int("padding"));
        }

        private void manage_boolean (ToggleButton button) {
            int n = 0;
            string match = "";
            bool newval = button.get_active();
            ToggleButton[] toggles = {
                toggle_gui, toggle_shuffler, toggle_swapgeo,
                toggle_softmove, toggle_warning, toggle_layouts
            };
            string[] vals = {
                "runshufflergui", "runshuffler", "swapgeometry",
                "softmove", "showwarning", "runlayouts"
            };
            foreach (ToggleButton b in toggles) {
                if (b == button) {
                    match = vals[n];
                    break;
                }
                n += 1;
            }
            shuffler_settings.set_boolean(match, newval);
            if (n == 1) {
                GLib.Timeout.add(250, () => {
                    try {
                        daemonruns = procruns("windowshufflerdaemon");
                        if (newval && !daemonruns) {
                            string cm = Config.SHUFFLER_DIR + "/windowshufflerdaemon";
                            Process.spawn_command_line_sync(cm);
                        }
                        // make sure if shuffler is switched off, layouts also quits
                        else if (!newval) {
                            toggle_layouts.set_active(false);
                        }
                        client.ReloadShortcuts();
                    }
                    catch (Error e) {
                        stderr.printf ("%s\n", e.message);
                    }
                    return false;
                });
                toggle_gui.set_sensitive(newval);
                toggle_layouts.set_sensitive(newval);
                toggle_swapgeo.set_sensitive(newval);
                toggle_softmove.set_sensitive(newval);
                toggle_warning.set_sensitive(newval);
            }
        }

        private void set_fromspinbutton (SpinButton b) {
            SpinButton[] sp_buttons = {
                columns_spin, rows_spin, topmarginspin,
                leftmarginspin, rightmarginspin, bottommarginspin,
                paddingspin
            };
            string[] vals = {
                "cols", "rows", "margintop", "marginleft",
                "marginright", "marginbottom", "padding"
            };
            int newval = (int)b.get_value();
            string match = "";
            int n = 0;
            foreach (SpinButton sb in sp_buttons) {
                if (sb == b) {
                    match = vals[n];
                    break;
                }
                n += 1;
            }
            shuffler_settings.set_int(match, newval);
        }

        private void set_margins (Grid grid) {
            int[,] corners = {
                {0, 0}, {100, 0}, {2, 0}, {0, 100}, {100, 100}
            };
            int lencorners = corners.length[0];
            for (int i=0; i < lencorners; i++) {
                var spacelabel = new Label("\t");
                grid.attach(
                    spacelabel, corners[i, 0], corners[i, 1], 1, 1
                );
            }
        }

        private bool procruns (string processname) {
            string cmd = Config.PACKAGE_BINDIR + @"/pgrep -f $processname";
            string output;
            try {
                GLib.Process.spawn_command_line_sync(cmd, out output);
                if (output == "") {
                    return false;
                }
            }
            /* on an unlike to happen exception, return true */
            catch (SpawnError e) {
                return true;
            }
            return true;
        }
    }

    private void sendwarning (string msg_header, string message) {
        string set_icon = Config.PACKAGE_BINDIR + "/notify-send -i shuffler-togglesticky-symbolic ";
        string header = "'" + msg_header + "'";
        // WindowPreviews is the name of a Budgie Applet and does not need to be translated
        string body = " '" + message + "'";
        string command = set_icon.concat(header, body);
        try {
            Process.spawn_command_line_async(command);
        }
        catch (Error e) {
            print("Error sending notification\n");
        }
    }

    public static int main (string[] args) {
        Gtk.init(ref args);
        shuffler_settings = new GLib.Settings(
            "org.ubuntubudgie.windowshuffler"
        );
        if (args[1] == "togglestickyneighbors") {
            bool sticky = shuffler_settings.get_boolean("stickyneighbors");
            shuffler_settings.set_boolean("stickyneighbors", !sticky);
            string msg = "";
            if (sticky) {
                msg = (_("Sticky Neighbors switched off"));
            }
            else {
                msg = (_("Sticky Neighbors switched on"));
            }
            sendwarning("Window Shuffler notification", msg);
        }
        else {
            new ControlsWindow();
            Gtk.main();
        }
        return 0;
    }
}
