#!/usr/bin/python3
import gi, os, re, jwmkit_utils
import xml.etree.ElementTree as ET
from xml.dom import minidom
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk, GdkPixbuf, Gdk

# JWM Kit - A set of Graphical Apps to simplify use of JWM (Joe's Window Manager) <https://codeberg.org/JWMKit/JWM_Kit>
# Copyright © 2020-2022 Calvin Kent McNabb <apps.jwmkit@gmail.com>
#
# This file is part of JWM Kit.
#
# JWM Kit is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# JWM Kit 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 JWM Kit.  If not, see <https://www.gnu.org/licenses/>.


def screen_size():
    try:
        screen_data = os.popen('xrandr').read()
        screen_data = re.findall('current (\d\d+) x (\d\d+)', screen_data)[0]
    except IndexError:
        screen_data = ('8000', '4000')
    return screen_data


def spin_check(button, spin):
    state = button.get_active()
    spin.set_sensitive(state)


def check_swap(check, check2):
    if check.get_active():
        check2.set_active(False)
    else:
        check2.set_active(True)


def verify_tray(filename):
    # Ensure an imported files is a JWM tray and a valid XML file
    if filename:
        try:
            tree = ET.parse(filename)
            root = tree.getroot()
            if root.tag == 'JWM':
                if root[0].tag == 'Tray':
                    return True
        except (ET.ParseError, IndexError):
            return False


def get_paths(jwmrc):
    home = os.path.expanduser('~')
    tray_list = []
    try:
        tree = ET.parse(jwmrc)
    except Exception as exception_message:
        print(str(exception_message))
        jwmkit_utils.app_fail()
    menus = [['showdesktop', 'Minimize or restore all windows on the current desktop', 'showdesktop',
              '/usr/share/pixmaps/jwmkit/showdesktop.svg']]
    root = tree.getroot()
    icon_paths = []
    menu_list = []
    tmp = []
    for node in root:
        if node.tag == 'Include':
            node = node.text
            if node.startswith('$HOME'):
                node = '{}{}'.format(home, node[5:])
            if os.path.isfile(node):
                tmp.append(node)

    for filename in tmp:
        try:
            tree = ET.parse(filename)
            root = tree.getroot()
            if root[0].tag == 'Tray':
                tray_list.append([os.path.basename(filename), filename])
            elif root[0].tag == 'RootMenu':
                menu_list.append([os.path.basename(filename), filename])
            elif root[0].tag == 'IconPath':
                icon_path = filename
        except (ET.ParseError, IndexError):
            print("File Error! {} will not be used.\nPossible causes:\n\tThe file is corrupt or empty\n\t"
                  "The file contains syntax Errors\n\tThe file is missing data or key values\n\t".format(filename))

    for menu in menu_list:
        if menu[1].startswith('$HOME'):
            menu[1] = os.path.join(home, menu[1][6:])
        tree = ET.parse(menu[1])
        root = tree.getroot()
        try:
            rootmenu = 'root:{}'.format(root.find('RootMenu').get('onroot'))
            if rootmenu not in ['root:', 'root:None']:
                menus.append([rootmenu, menu[0], rootmenu, '/usr/share/pixmaps/jwmkit/menugray.svg'])
        except (TypeError, AttributeError):
            print('Attributes not defined in {}\nthe menu will not be included as an option'.format(menu[0]))

    if 'icon_path' in locals():
        tree = ET.parse(icon_path)
        root = tree.getroot()
        for node in root:
            if node.tag == 'IconPath':
                node = node.text
                if node.startswith('$HOME'):
                    node = '{}{}'.format(home, node[5:])
                if os.path.isdir(node):
                    icon_paths.append(node)
    return [tray_list, menus, icon_paths]


def get_unused_trays(traylist):
    def make_file_list(tmp_list, tmp_dir):
        for file in tmp_list:
            file = os.path.join(tmp_dir, file)
            if os.path.isfile(file):
                file_list.append(file)
            elif os.path.isdir(file):
                tmp_dir1 = file
                make_file_list(os.listdir(tmp_dir1), tmp_dir1)

    home = os.path.expanduser('~')
    path = os.path.join(home, '.config/jwm/')
    tmp_file_list = os.listdir(path)
    file_list, vaild_list = [], []
    make_file_list(tmp_file_list, path)

    open(os.path.join(home, '.config/jwmkit/settings'), 'a').close()
    with open(os.path.join(home, '.config/jwmkit/settings')) as f:
        f = f.read()
    f = '\n{}'.format(f)
    tray_paths = re.findall('\ntray.*=(.+)', f)
    if tray_paths:
        for file in tray_paths:
            if file.startswith('$HOME'):
                file = os.path.join(home, file[6:])
            if os.path.isfile(file):
                file_list.append(file)
    file_list = list(set(file_list))

    tray_files = []
    for tray_file in traylist:
        tray_files.append(tray_file[1])
    for file in file_list:
        if verify_tray(file):
            if file not in tray_files:
                vaild_list.append(file)
    return vaild_list


class MenusWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="JWM Kit Trays")
        self.set_border_width(15)
        self.jwmrc = jwmkit_utils.get_jwmrc()
        tray_list = get_paths(self.jwmrc)
        self.menu_list = tray_list.pop(1)
        self.jwm_icon_paths = tray_list.pop(1)
        self.app_data = jwmkit_utils.get_applications_data()
        tray_list = tray_list[0]
        self.icon_view_list, self.selected_list_item = [], ''
        self.selected_preview = Gtk.Image()
        try:
            self.set_icon_from_file('/usr/share/pixmaps/jwmkit/config.svg')
        except gi.repository.GLib.Error:
            self.set_icon_name('edit-paste')

        main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20)
        self.set_default_size(370, 610)
        self.add(main_box)

        # Top area with title and tray combo box next to buttons
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
        label = Gtk.Label()
        label.set_markup('<big><b>Tray Preferences</b></big>\nConfigure JWM Trays')
        box.add(jwmkit_utils.create_image('/usr/share/pixmaps/jwmkit/config.svg', 48, 48, True))
        box.add(label)
        main_box.add(box)

        tray_store = Gtk.ListStore(str)
        for option in tray_list:
            tray_store.append([option[0].replace('-', ' ').replace('_', ' ')])
        tray_combo = Gtk.ComboBox.new_with_model(model=tray_store)
        renderer_text = Gtk.CellRendererText()
        tray_combo.pack_start(renderer_text, expand=False)
        tray_combo.add_attribute(renderer_text, "text", 0)

        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        add_tray_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_PREFERENCES))
        add_tray_button.set_always_show_image(True)
        add_tray_button.set_tooltip_text('Create / Import / Restore / Remove a Tray')
        add_tray_button.connect("clicked", self.dialogs, [tray_list, tray_store], ['add_tray', 0], tray_combo)
        about_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_ABOUT))
        about_button.connect('clicked', jwmkit_utils.get_about, self)
        about_button.set_always_show_image(True)
        box.pack_start(tray_combo, True, True, 0)
        box.pack_end(about_button, False, False, 0)
        box.pack_end(add_tray_button, False, False, 0)
        main_box.add(box)

        # Page 1 of Notebook (properties) (window tab)
        notebook = Gtk.Notebook()
        page = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
        page.set_border_width(20)
        notebook.append_page(page, Gtk.Label(label='Properties'))
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        page.add(vbox)
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        vbox.add(box)

        layout_combo = Gtk.ComboBoxText()
        hide_combo = Gtk.ComboBoxText()
        layer_combo = Gtk.ComboBoxText()
        valign_combo = Gtk.ComboBoxText()
        halign_combo = Gtk.ComboBoxText()
        hide_combo.set_tooltip_text('The "On" option is new in JWM 2.4\nDo not set to "On" for older version')

        layouts_options = ['Vertical', 'Horizontal']
        hide_options = ['Off', 'On', 'Left', 'Right', 'Top', 'Bottom']
        layer_options = ['Above', 'Normal', 'Below']
        valign_options = ['Fixed', 'Top', 'Center', 'Bottom']
        halign_options = ['Fixed', 'Left', 'Center', 'Right']

        combos = [layout_combo, hide_combo, layer_combo, valign_combo, halign_combo]
        combo_options = [['Layout', layouts_options], ['Auto Hide', hide_options], ['Layer', layer_options],
                         ['Vertical Align', valign_options], ['Horizontal Align\t', halign_options]]
        for combo, option in zip(combos, combo_options):
            combo.set_property("width-request", 100)
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
            box.pack_start(Gtk.Label(label=option[0]), False, False, 0)
            vbox.add(box)
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
            for item in option[1]:
                combo.append_text(item)
            box.pack_start(combo, False, False, 0)
            vbox.add(box)

        page.add(Gtk.Separator(orientation=Gtk.Orientation.VERTICAL))
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        page.pack_start(vbox, False, False, 0)
        vbox.add(box)
        self.height_spin = Gtk.SpinButton()
        self.width_spin = Gtk.SpinButton()
        self.x_offset_spin = Gtk.SpinButton()
        self.y_offset_spin = Gtk.SpinButton()
        self.screen_spin = Gtk.SpinButton()
        self.x_check = Gtk.CheckButton(label="\tX Offset")
        self.y_check = Gtk.CheckButton(label="\tY Offset")
        self.x_check.connect("clicked", spin_check, self.x_offset_spin)
        self.y_check.connect("clicked", spin_check, self.y_offset_spin)

        display_size = screen_size()
        rows = [[self.height_spin, -100, 100, Gtk.Label(label='Height')],
                [self.width_spin, -int(display_size[0]), int(display_size[0]), Gtk.Label(label='Width')],
                [self.x_offset_spin, -int(display_size[0]), int(display_size[0]), self.x_check],
                [self.y_offset_spin, -int(display_size[1]), int(display_size[1]), self.y_check],
                [self.screen_spin, 0, 20, Gtk.Label(label='Screen')]]

        for row in rows:
            if row[3] == 'X Offset':
                box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
                label = Gtk.Label()
                label.set_markup('\n<b>Position</b>')
                box.pack_start(label, True, True, 0)
                vbox.add(box)
                vbox.add(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL))

            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
            box.pack_end(row[3], False, False, 0)
            vbox.add(box)
            row[0].set_property("width-request", 100)
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
            row[0].set_adjustment(Gtk.Adjustment(value=0, lower=row[1], upper=row[2], step_increment=1))
            box.pack_end(row[0], False, False, 0)
            vbox.add(box)

        # page 2 (Items) of Notebook (window tab)
        page = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        page.set_border_width(20)
        notebook.append_page(page, Gtk.Label(label='Items'))
        main_box.add(notebook)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
        page.add(vbox)
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
        vbox.add(box)
        scroll = Gtk.ScrolledWindow()
        scroll.set_min_content_width(200)
        scroll.set_min_content_height(380)

        self.item_listbox = Gtk.ListBox()
        self.item_listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
        self.item_listbox.set_activate_on_single_click(False)
        self.item_listbox.connect("row-activated", self.item_editor)

        scroll.add(self.item_listbox)
        box.pack_start(scroll, True, True, 0)
        vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
        page.add(vbox)
        up_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_GO_UP))
        down_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_GO_DOWN))
        add_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_ADD))
        remove_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_REMOVE))
        edit_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_EDIT))
        info_button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_HELP))

        buttons = [up_button, down_button, add_button, remove_button, edit_button, info_button]
        tips = ['Move the selected item up', 'Move the selected item down', 'Add a new item',
                'Remove the selected item', 'Edit the selected item', 'Show information about the selected item']
        for button, tip in zip(buttons, tips):
            button.set_tooltip_text(tip)
            button.set_property("width-request", 70)
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
            vbox.add(box)
            box.pack_start(button, False, False, 10)

        up_button.connect("clicked", self.move_up)
        down_button.connect("clicked", self.move_down)
        add_button.connect("clicked", self.add_item_dialog)
        remove_button.connect("clicked", self.remove_item)
        edit_button.connect("clicked", self.item_editor, '')
        info_button.connect("clicked", self.help)

        # Buttons at the bottom (below notebook pages)
        save_button = Gtk.Button(label="Save", image=Gtk.Image(stock=Gtk.STOCK_SAVE))
        close_button = Gtk.Button(label="Close", image=Gtk.Image(stock=Gtk.STOCK_CANCEL))
        close_button.connect("clicked", Gtk.main_quit)
        save_button.connect("clicked", self.output, combos, tray_list, tray_combo)
        save_button.set_always_show_image(True)
        close_button.set_always_show_image(True)
        save_button.set_property("width-request", 90)
        close_button.set_property("width-request", 90)
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        box.pack_end(close_button, False, False, 0)
        box.pack_end(save_button, False, False, 0)
        main_box.add(box)

        tray_combo.set_active(0)
        self.get_tray_data('', tray_list, combos, combo_options, tray_combo)
        tray_combo.connect("changed", self.get_tray_data, tray_list, combos, combo_options, tray_combo)

    def item_editor(self, button, listbox):

        def listbox_update(data):
            if data[2] != '':
                icon = data[2]
            else:
                icon = 'missing-icon'
            icon = self.update_icon(icon, 24, 'image-missing')
            image = Gtk.Image()
            image.set_from_pixbuf(icon)
            row = Gtk.ListBoxRow()
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
            row.add(box)
            label = Gtk.Label(label=data[0])
            box.pack_start(image, False, False, 0)
            box.pack_start(label, False, False, 0)
            self.item_listbox.remove(self.item_listbox.get_row_at_index(index))
            self.item_listbox.insert(row, index)
            self.item_listbox.select_row(self.item_listbox.get_row_at_index(index))
            self.item_listbox.show_all()

        def icon_preview(entry, image):
            icon = entry.get_text()
            try:
                icon = self.update_icon(icon, 48, 'image-missing')
                image.set_from_pixbuf(icon)
            except gi.repository.GLib.Error:
                print('unable to process image')
                icon = self.update_icon('image_missing', 48, 'image-missing')
                image.set_from_pixbuf(icon)

        def entry_change(entry, i, tmp):
            tmp[i] = entry.get_text()

        def set_switch(switch, parm, tmp):
            if switch.get_active():
                tmp[1] = 'true'
            else:
                tmp[1] = 'false'

        def spin_update(spin, tmp, i):
            tmp[i] = str(int(spin.get_value()))

        def edit_traybutton():
            #  Editor for Traybuttons and Clock

            def get_time_preview(entry, id_num):
                # create a clock preview and update as settings change
                #  id 1 is the time format combo/entry, id 2 is the time zone combo
                if id_num == 1:
                    active = entry.get_active()
                    if active == -1:
                        time_format = entry.get_child().get_text()
                    else:
                        actives = ['%I:%m %p', '%l:%m %p', '%H:%m', '%k:%m']
                        entry.get_child().set_text(actives[active])
                        return
                    tz = tmp[2]
                    tmp[1] = time_format
                else:
                    tz = zone_data[entry.get_active()]
                    time_format = tmp[1]
                    tmp[2] = tz
                if tz == 'Local':
                    preview = os.popen('date +"{}"'.format(time_format)).read().replace('\n', '')
                else:
                    preview = os.popen('TZ="{}" date +"{}"'.format(tz, time_format)).read().replace('\n', '')
                time_preview.set_markup('<b>{}</b>'.format(preview))
                vbox.show_all()

            tmp = self.item_data[index].copy()
            button_icons = [[], ['/usr/share/pixmaps/jwmkit/mouse-left.svg', 'left'],
                            ['/usr/share/pixmaps/jwmkit/mouse-middle.svg', 'middle'],
                            ['/usr/share/pixmaps/jwmkit/mouse-right.svg', 'right'],
                            ['/usr/share/pixmaps/jwmkit/mouse-up.svg', 'scroll Up'],
                            ['/usr/share/pixmaps/jwmkit/mouse-down.svg', 'scroll Down']]

            if item_type == 'TrayButton':
                entries = ['Name', 'Icon', 'Comment', 'Left Click', 'Middle Click',
                           'Right Click', 'Scroll up', 'Scroll Down']
                icon = self.update_icon(self.item_data[index][2], 48, 'image-missing')
                image = Gtk.Image()
                image.set_from_pixbuf(icon)
            else:
                entries = ['Format', 'Zone', 'Left Click', 'Middle Click', 'Right Click', 'Scroll up', 'Scroll Down']
                if tmp[1] == '':
                    tmp[1] = 'Local'
                time_preview = Gtk.Label(label='test')
                vbox.add(time_preview)

                time_zone_combo = Gtk.ComboBoxText()
                time_zone_combo.connect("changed", get_time_preview, 2)
                time_zone_combo.set_property("width-request", 255)
                zone_data = os.popen \
                    ('cd /usr/share/zoneinfo/posix && find * -type f -or -type l | sort').read().split('\n')
                zone_data.insert(0, 'Local')
                for item in zone_data:
                    time_zone_combo.append_text(item)

                time_format_combo = Gtk.ComboBoxText.new_with_entry()
                time_format_combo.get_child().set_text(tmp[1])
                time_format_combo.connect("changed", get_time_preview, 1)
                time_format_combo.set_property("width-request", 255)
                time_format_combo.append_text('12-Hour (zero padded hour)')
                time_format_combo.append_text('12-Hour (space padded hour)')
                time_format_combo.append_text('24-Hour (zero padded hour)')
                time_format_combo.append_text('24-Hour (space padded hour)')

            i = 1
            entry_widgets, entry_index = [], 0
            for entry, entry_data in zip(entries, self.item_data[index][1:]):
                box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
                if entry == 'Format':
                    entry_widget = time_format_combo
                    button_icon = button_icons.pop(0)
                elif entry != 'Zone':
                    entry_widget = Gtk.Entry()
                    if entry == 'Name':
                        vbox1 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
                        vbox2 = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
                        box.add(vbox1)
                        box.pack_end(vbox2, False, False, 0)
                        vbox1.add(image)
                        vbox.add(box)
                        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
                    if entry == 'Icon':
                        entry_widget.connect("changed", icon_preview, image)
                    if entry not in ['Comment', 'Name']:
                        entry_widgets.append(entry_widget)
                        entry_index += 1
                        button_icon = button_icons.pop(0)
                        entry_widget.set_property("width-request", 200) if entry == 'Format' \
                            else entry_widget.set_property("width-request", 170)
                        button = Gtk.Button(image=Gtk.Image(stock=Gtk.STOCK_OPEN))
                        button.set_property("width-request", 50)
                        button.connect("clicked", self.dialogs, entry_widgets, [entry, entry_index], button_icon)
                        box.pack_end(button, False, False, 0)
                    else:
                        if entry == 'Comment': entry = 'Tooltip'
                        entry_widgets.append(entry_widget)
                        entry_index += 1
                        entry_widget.set_property("width-request", 225)
                    entry_widget.set_text(entry_data)
                    entry_widget.connect("changed", entry_change, i, tmp)
                else:
                    entry_widget = time_zone_combo
                    try:
                        entry_widget.set_active(zone_data.index(entry_data))
                    except ValueError:
                        entry_widget.set_active(0)

                i += 1
                box.pack_end(entry_widget, False, False, 0)
                box.pack_end(Gtk.Label(label='{}:'.format(entry), xalign=1), True, True, 0)
                vbox2.pack_start(box, True, True, 2) if entry in ['Name', 'Icon'] \
                    else vbox.pack_start(box, True, True, 2)
            if item_type == 'Clock':
                edit_others('Height:', 8, tmp)
            return tmp

        def edit_pager():
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
            vbox.add(box)
            switch = Gtk.Switch()
            tmp = self.item_data[index].copy()
            switch.connect("notify::active", set_switch, tmp)
            if self.item_data[index][1] == 'true':
                switch.set_active(True)
            box.pack_end(switch, False, False, 15)
            box.pack_end(Gtk.Label(label="Show Desktop Names"), False, False, 15)
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
            box.add(Gtk.Label())
            vbox.pack_start(box, True, True, 2)
            return tmp

        def edit_others(label_value, i, tmp):
            # i is the index of the first spinbutton.
            if i == 3:
                for label in enumerate(['Name:', 'Command:']):
                    box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
                    entry = Gtk.Entry()
                    entry.set_text(self.item_data[index][label[0] + 1])
                    entry.set_property("width-request", 230)
                    entry.connect("changed", entry_change, label[0] + 1, tmp)
                    box.pack_end(entry, False, False, 0)
                    box.pack_end(Gtk.Label(label=label[1]), False, False, 0)
                    vbox.pack_start(box, True, True, 5)
            elif i == 2:
                box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
                switch = Gtk.Switch()
                switch.connect("notify::active", set_switch, tmp)
                if self.item_data[index][1] == 'true':
                    switch.set_active(True)
                elif self.item_data[index][1] != 'false':
                    if item_type == 'TaskList':
                        switch.set_active(True)

                box.pack_end(switch, False, False, 15)
                box.pack_end(Gtk.Label(label='Show Labels'), False, False, 15)
                vbox.pack_start(box, True, True, 5)

            height_spin = Gtk.SpinButton()
            width_spin = Gtk.SpinButton()
            height_spin.connect("value-changed", spin_update, tmp, i)
            width_spin.connect("value-changed", spin_update, tmp, i + 1)
            display_size = screen_size()
            value1, value2 = self.item_data[index][i], self.item_data[index][i + 1]
            rows = [[height_spin, value1, -int(display_size[1]), int(display_size[1]), Gtk.Label(label=label_value)],
                    [width_spin, value2, -int(display_size[0]), int(display_size[0]), Gtk.Label(label='Width:')]]
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
            for row in rows:
                if not row[1].replace('-', '').isdigit():
                    row[1] = '0'
                row[0].set_property("width-request", 100)
                row[0].set_adjustment(Gtk.Adjustment(value=int(row[1]), lower=row[2], upper=row[3], step_increment=1))
                box.pack_start(row[4], False, False, 0)
                box.pack_start(row[0], False, False, 0)
            vbox.pack_start(box, True, True, 5)
            return tmp

        index = self.item_listbox.get_selected_rows()[0].get_index()
        item_type = self.item_data[index][0]
        dialog = Gtk.Dialog('{} Editor'.format(item_type), self, 0)
        dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_APPLY, Gtk.ResponseType.OK)
        vbox = dialog.get_content_area()
        dialog.set_default_size(320, -1)
        vbox.set_border_width(10)

        if item_type == 'TrayButton':
            tmp = edit_traybutton()
        elif item_type == 'Clock':
            tmp = edit_traybutton()
        elif item_type == 'Pager':
            tmp = edit_pager()
        elif item_type == 'Spacer':
            tmp = edit_others('Height:', 1, self.item_data[index].copy())
        elif item_type == 'Dock':
            tmp = edit_others('Spacing:', 1, self.item_data[index].copy())
        elif item_type == 'TaskList':
            tmp = edit_others('Height:', 2, self.item_data[index].copy())
        elif item_type == 'Swallow':
            tmp = edit_others('Height:', 3, self.item_data[index].copy())

        dialog.show_all()
        response = dialog.run()
        if response == -5:
            self.item_data[index] = tmp
            if item_type == 'TrayButton':
                listbox_update(tmp)
        dialog.destroy()

    def add_item_dialog(self, button):

        def double_clicked(parm, parm2, response):
            dialog.destroy()
            if response == -5:
                try:
                    index = self.item_listbox.get_selected_rows()[0].get_index()
                except IndexError:
                    index = 0
                i = listbox.get_selected_rows()[0].get_index()
                add_type = add_types[i]
                icon = '/usr/share/pixmaps/jwmkit/{}.svg'.format(add_type.lower().replace(' sndio', ''))
                icon = self.update_icon(icon, 24, 'image-missing')
                image = Gtk.Image()
                image.set_from_pixbuf(icon)
                row = Gtk.ListBoxRow()
                box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
                row.add(box)
                label = Gtk.Label(label=add_type, xalign=0)
                box.pack_start(image, False, False, 0)
                box.pack_start(label, False, False, 0)
                self.item_listbox.insert(row, index)
                self.item_listbox.show_all()
                # create empty list for new items. length[i] is number of empty items to add_types[i]
                lengths = [8, 9, 2, 3, 1, 2, 4, 8, 8]
                length = lengths[add_types.index(add_type)]
                add_data = [add_type]
                for i in range(length): add_data.append('')
                if add_type == 'Clock':
                    add_data[1] = '%l:%m %p'
                    add_data[2] = 0
                    add_data[3] = 'exec:jwmkit_calendar'
                    add_data[4] = 'exec:jwmkit_calendar'
                    add_data[5] = 'exec:jwmkit_calendar'
                elif add_type == 'Volume':
                    add_data[0] = 'TrayButton'
                    add_data[2] = '/usr/share/pixmaps/jwmkit/volume.svg'
                    add_data[5] = 'exec:jwmkit_popvolume mute'
                    add_data[6] = 'exec:jwmkit_popvolume mute'
                    add_data[7] = 'exec:jwmkit_popvolume t +'
                    add_data[8] = 'exec:jwmkit_popvolume t -'
                elif add_type == 'Volume sndio':
                    add_data[0] = 'TrayButton'
                    add_data[4] = 'exec:jwmkit_popvolume sndio'
                    add_data[2] = '/usr/share/pixmaps/jwmkit/volume.svg'
                    add_data[5] = 'exec:jwmkit_popvolume mute'
                    add_data[6] = 'exec:jwmkit_popvolume mute'
                    add_data[7] = 'exec:jwmkit_popvolume t +'
                    add_data[8] = 'exec:jwmkit_popvolume t -'
                self.item_data.insert(index, add_data)
                self.item_listbox.select_row(self.item_listbox.get_row_at_index(index))
                self.item_editor('', '')

        add_types = ['TrayButton', 'Clock', 'Spacer', 'TaskList', 'Pager', 'Dock', 'Swallow', 'Volume', 'Volume sndio']
        type_info = ['Launcher - assign actions to specific mouse buttons',
                     'Time - assign actions to specific mouse buttons',
                     'Add empty space to preserve item placement',
                     'Visual list for switch between windows', 'Easily switch workspaces ',
                     'Display system notifications and applets', 'Place a running app inside the tray',
                     'TrayButton configured for volume control',
                     'TrayButton Configured for Volume Control featuring a sndio mixer']
        root_path = '/usr/share/pixmaps/jwmkit'
        dialog = Gtk.Dialog('Add New Item', self, 0)
        dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_ADD, Gtk.ResponseType.OK)
        scroll = Gtk.ScrolledWindow()
        scroll.set_min_content_width(360)
        scroll.set_min_content_height(300)
        listbox = Gtk.ListBox()
        listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
        listbox.set_activate_on_single_click(False)
        listbox.connect("row-activated", double_clicked, -5)
        for add_type, info in zip(add_types, type_info):
            icon = os.path.join(root_path, '{}.svg'.format(add_type.lower().replace(' sndio', '')))
            icon = self.update_icon(icon, 24, 'image-missing')
            image = Gtk.Image()
            image.set_from_pixbuf(icon)
            row = Gtk.ListBoxRow()
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
            row.add(box)
            label = Gtk.Label(label='{}\n{}'.format(add_type, info))
            box.pack_start(image, False, False, 0)
            box.pack_start(label, False, False, 0)
            listbox.add(row)
        listbox.show_all()
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        image = Gtk.Image.new_from_icon_name(Gtk.STOCK_ADD, size=Gtk.IconSize.DIALOG)
        label = Gtk.Label()
        label.set_markup('<big><b>Add a New Item</b></big>\nselect an item to Add to the tray')
        box.add(image)
        box.add(label)
        vbox = dialog.get_content_area()
        vbox.set_border_width(10)
        vbox.add(box)
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        scroll.add(listbox)
        box.add(scroll)
        vbox.add(box)
        dialog.show_all()
        response = dialog.run()
        double_clicked('', '', response)

    def confirm_dialog(self, button, title, message, info):
        dialog = Gtk.Dialog(title, self, 0)
        box = dialog.get_content_area()
        if button == 'b1':
            dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_REMOVE, Gtk.ResponseType.OK)
        elif button == 'b3':
            dialog.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_REMOVE, Gtk.ResponseType.OK)
            keep_check = Gtk.CheckButton(label="Remove but keep the file. Tray can be restored")
            delete_check = Gtk.CheckButton(label="Remove and delete the file.")
            delete_check.connect("clicked", check_swap, keep_check)
            keep_check.connect("clicked", check_swap, delete_check)
            keep_check.set_active(True)
        else:
            dialog.add_buttons(Gtk.STOCK_OK, Gtk.ResponseType.OK)
        label = Gtk.Label(xalign=0)
        label.set_markup("<b>{}</b>\n".format(message))
        dialog.set_default_size(370, -1)
        box.set_border_width(20)
        box.add(label)
        label = Gtk.Label(label=info, xalign=0)
        box.add(label)
        if button == 'b3':
            box.add(keep_check)
            box.add(delete_check)
        dialog.show_all()
        if button == 'b1':
            response = dialog.run()
            dialog.destroy()
            return response
        elif button == 'b3':
            response = dialog.run()
            dialog.destroy()
            return response, delete_check.get_active()
        else:
            dialog.run()
            dialog.destroy()

    def get_tray_data(self, combo, filename, combos, combo_options, tray_combo):

        def set_spinner():
            spin.set_value(0)
            if req == 'x':
                self.x_check.set_active(False)
                spin.set_sensitive(False)
            if req == 'y':
                self.y_check.set_active(False)
                spin.set_sensitive(False)

        def ignore_whitespace(tmp):
            if tmp:
                if re.sub('\s', '', tmp) == '':
                    tmp = ''
            else:
                tmp = ''
            return tmp

        self.clear_widgets(combos)
        home = os.path.expanduser('~')
        try:
            filename = filename[tray_combo.get_active()][1]
        except IndexError:
            return
        combo_data = []
        tree = ET.parse(filename.replace('$HOME', home))
        root = tree.getroot()
        spins = [self.height_spin, self.width_spin, self.x_offset_spin, self.y_offset_spin, self.screen_spin]
        req = ['layout', 'autohide', 'layer', 'valign', 'halign']

        for combo, req, options in zip(combos, req, combo_options):
            data = root.find('Tray').get(req)
            try:
                combo.set_active(options[1].index(data.title()))
                combo_data.append(data)
            except (AttributeError, ValueError):
                combo.set_active(-1)
        req = ['height', 'width', 'x', 'y', 'screen']
        for spin, req in zip(spins, req):
            data = root.find('Tray').get(req)
            if data is not None:
                try:
                    spin.set_value(int(data))
                    if req == 'x': self.x_check.set_active(True)
                    if req == 'y': self.y_check.set_active(True)
                except ValueError:
                    set_spinner()
            else:
                set_spinner()

        tags = ['TrayButton', 'Clock', 'Spacer', 'TaskList', 'Pager', 'Dock', 'Swallow']
        tb_values = ['label', 'icon', 'popup']
        clock_values = ['format', 'zone', 'height', 'width']
        spacer_values = ['height', 'width']
        tasklist_values = ['labeled', 'height', 'maxwidth']
        pager_values = ['labeled']
        dock_values = ['spacing', 'width']
        swallow_values = ['name', 'height', 'width']
        all_values = [tb_values, clock_values, spacer_values, tasklist_values, pager_values, dock_values,
                      swallow_values]
        self.item_data = []
        for item in root[0].iter():
            data = []
            tag = item.tag
            if tag in tags:
                data.append(item.tag)
                values = all_values[tags.index(tag)]
                exec_tmp = item.text
                exec_tmp = ignore_whitespace(exec_tmp)
                button_data = ['', '', '', '', '']
                if tag == 'Swallow': data.append(exec_tmp) if exec_tmp else data.append('')
                for value in values:
                    tmp = item.get(value)
                    if tag == 'Spacer':
                        if not tmp: tmp = '0'
                    if value == 'zone':
                        if not tmp: tmp = 'Local'
                    data.append(tmp) if tmp else data.append('')
                if data[0] in ['TrayButton', 'Clock']:
                    for node in item:
                        tmp = node.text
                        tmp = ignore_whitespace(tmp)
                        buttons = node.get('mask')
                        if buttons:
                            for button in list(buttons):
                                button_data[int(button) - 1] = tmp if tmp else data.append('')
                    if exec_tmp != '':
                        for i in range(3):
                            button_data[i] = exec_tmp
                    data.extend(button_data)
                    if data[0] == 'Clock':
                        for i in range(5):
                            data.insert(3, data.pop(-1))
                if data[0] == 'TrayButton':
                    if data[2] != '':
                        icon = data[2]
                    else:
                        icon = 'missing-icon'
                else:
                    icon = '/usr/share/pixmaps/jwmkit/{}.svg'.format(data[0].lower())
                self.item_data.append(data)
                icon = self.update_icon(icon, 24, 'image-missing')
                image = Gtk.Image()
                image.set_from_pixbuf(icon)
                row = Gtk.ListBoxRow()
                box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=15)
                row.add(box)
                label = Gtk.Label(label=data[0])
                box.pack_start(image, False, False, 0)
                box.pack_start(label, False, False, 0)
                self.item_listbox.add(row)
        self.item_listbox.show_all()
        self.item_listbox.select_row(self.item_listbox.get_row_at_index(0))

    def dialogs(self, button, entry, entry_type, button_icon):
        def close(button, parm):
            dialog.destroy()

        def clear_status(notebook, label, page):
            status_label.set_text('')

        def clean_settings():
            # clean up JWM Kit's Setting file. remove old history
            home = os.path.expanduser('~')
            filename = self.selected_list_item[len(home):]
            with open(os.path.join(home, '.config/jwmkit/settings'), 'r+') as f:
                lines = f.readlines()
                f.seek(0)
                for line in lines:
                    if not line.strip('\n').endswith(filename):
                        f.write(line)
                f.truncate()

        def selection_made(widget, row):
            def clean_settings_file():
                with open(os.path.join(home, '.config/jwmkit/settings'), 'r+') as f:
                    lines = f.readlines()
                    f.seek(0)
                    for line in lines:
                        if line.strip('\n'):
                            f.write(line)
                    f.truncate()

            def remove_xml_include():
                tree = ET.parse(self.jwmrc)
                root = tree.getroot()
                for child in root:
                    if child.text.endswith(filename):
                        root.remove(child)
                pretty = minidom.parseString(ET.tostring(root)).toprettyxml()
                pretty = os.linesep.join([s for s in pretty.splitlines() if s.strip()])
                tree = ET.ElementTree(ET.fromstring(pretty))
                tree.write(self.jwmrc, encoding="utf-8", xml_declaration=True)
                os.system('jwm -restart')

            def add_jwmrc_include():
                filename = self.selected_list_item
                home = os.path.expanduser('~')
                tree = ET.parse(self.jwmrc)
                root = tree.getroot()
                new_include = ET.SubElement(root, "Include")
                include_name = filename
                if include_name.startswith(home):
                    include_name = include_name[len(home) + 1:]
                    include_name = os.path.join('$HOME/', include_name)
                new_include.text = include_name
                root = b'\n'.join([s.strip() for s in ET.tostring(root).splitlines() if s.strip()])
                pretty = minidom.parseString(root).toprettyxml(newl='', indent='   ')
                tree = ET.ElementTree(ET.fromstring(pretty))
                tree.write(self.jwmrc, encoding="utf-8", xml_declaration=True)
                os.system('jwm -restart')

            def create_tray(filename):
                # Create a new Tray file and it add it to jwmrc
                if not os.path.exists(os.path.join(home, '.config/jwm', filename)):
                    with open(os.path.join(home, '.config/jwm', filename), "w+") as f:
                        f.write('<JWM><Tray></Tray></JWM>')
                    self.selected_list_item = os.path.join(home, '.config/jwm', filename)
                    add_jwmrc_include()

            home = os.path.expanduser('~')
            current_page = notebook.get_current_page()
            if entry_type == 'add_tray':
                if current_page == 0:
                    basename = tray_entry.get_text()
                    if basename != '':
                        if not basename.lower().startswith('tray'):
                            basename = 'tray-{}'.format(basename)
                        filename = os.path.join(home, '.config/jwm/', basename)
                        if os.path.isfile(filename):
                            status_label.set_text('Filename is already taken. Try again.')
                            return
                        else:
                            create_tray(basename)
                    else:
                        status_label.set_text('Give the tray a name')
                        return
                elif current_page == len(notebook) - 1:
                    confirm = self.confirm_dialog('b1', 'Remove Tray', 'Do you want to remove this tray', '')
                    if confirm == -6:
                        return
                    index = remove_listbox.get_selected_rows()[0].get_index()
                    filename = traylist[index][1]
                    active = tray_combo.get_active()
                    if filename[:5] == '$HOME':
                        filename = filename[6:]
                    elif filename.startswith(home):
                        filename = filename[len(home) + 1:]
                    if not filename.startswith('.config/jwm/'):
                        clean_settings_file()
                        with open(os.path.join(home, '.config/jwmkit/settings'), 'a') as f:
                            f.write('tray-history={}'.format(os.path.join('$HOME/', filename)))
                    remove_xml_include()
                    if delete_check.get_active():
                        os.remove(os.path.join(home, filename))
                    del traylist[index]
                    entry_widgets[-1].remove(entry_widgets[-1].get_iter(index))
                    if index == active:
                        tray_combo.set_active(0)
                    dialog.destroy()
                    return
                else:
                    basename = os.path.basename(self.selected_list_item)
                    filename = self.selected_list_item
                    if not os.path.isfile(filename):
                        return
                    elif not verify_tray(filename):
                        status_label.set_text('Not a valid JWM tray file. Try again')
                        return
                    elif [i[1] for i in traylist if i[1] == filename]:
                        status_label.set_text('Tray is already in use. Try again')
                        return
                    else:
                        add_jwmrc_include()
                        clean_settings()
                traylist.append([basename, filename])
                entry_widgets[-1].append([basename])
                dialog.destroy()

            elif entry_type != 'Icon':
                if current_page == 1:
                    entry_data = self.selected_list_item
                else:
                    index = listbox.get_selected_rows()[0].get_index()
                    entry_data = data[index][2]
                    if name_check.get_active():
                        entry_widgets[0].set_text(data[index][0])
                    if tooltip_check.get_active():
                        entry_widgets[2].set_text(data[index][1])
                    if icon_check.get_active():
                        entry_widgets[1].set_text(data[index][3])

                if entry_data[:5] != 'root:':
                    if entry_data != 'showdesktop':
                        entry_data = 'exec:{}'.format(entry_data)
                        while entry_data[-3:] in [' %U', ' %u', ' %F', ' %f', ' %i', ' %c', ' %k']:
                            entry_data = '{}'.format(entry_data[:-3])
                entry.set_text(entry_data)
            else:
                entry.set_text(self.selected_list_item)
            if entry_type != 'add_tray':
                dialog.destroy()

        def image_selected(button, icon):
            self.selected_list_item = icon
            icon = self.update_icon(self.selected_list_item, 48, 'image-missing')
            self.selected_preview.set_from_pixbuf(icon)

        def apply_image(button):
            if entry.get_text() != self.selected_list_item:
                if notebook.get_current_page() == 0:
                    if not path_check.get_active():
                        self.selected_list_item = os.path.basename(self.selected_list_item)
                        self.selected_list_item = os.path.splitext(self.selected_list_item)[0]
                entry.set_text(self.selected_list_item)
            dialog.destroy()

        def page_switch(notebook, label, page):
            path_check.set_visible(True) if page == 0 else path_check.set_visible(False)

        def hide_app_checks(notebook, label, page):
            # hide / show check buttons on App dialog
            if page == 0:
                name_check.set_visible(True)
                icon_check.set_visible(True)
                tooltip_check.set_visible(True)
            else:
                name_check.set_visible(False)
                icon_check.set_visible(False)
                tooltip_check.set_visible(False)

        def file_change(widget):
            if entry_type == 'add_tray':
                status_label.set_text('')
            if entry_type == 'Icon':
                icon = file_chooser.get_filename()
                if icon:
                    if os.path.isfile(icon):
                        self.selected_list_item = icon
                icon = self.update_icon(self.selected_list_item, 48, 'image-missing')
                self.selected_preview.set_from_pixbuf(icon)
            else:
                self.selected_list_item = file_chooser.get_filename()

        def remove_tab():
            scroll.set_min_content_width(-1)
            scroll.set_min_content_height(240)
            remove_listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
            remove_listbox.set_activate_on_single_click(False)
            scroll.add(remove_listbox)

            for i in traylist:
                row = Gtk.ListBoxRow()
                row_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
                if i == selected_tray:
                    label_text = '{}\t<b>* selected for editing</b>\n{}'.format(i[0], i[1])
                else:
                    label_text = '{}\n{}'.format(i[0], i[1])
                label = Gtk.Label()
                label.set_markup(label_text)
                row_box.pack_start(label, False, False, 5)
                row.add(row_box)
                remove_listbox.insert(row, -1)
            remove_listbox.show_all()
            remove_listbox.select_row(remove_listbox.get_row_at_index(0))
            vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
            row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
            vbox.add(row)
            notebook.append_page(vbox, Gtk.Label(label='Remove'))
            message = '\n<big><b>Remove a Tray</b></big>'
            label = Gtk.Label()
            label.set_markup(message)
            row.pack_start(label, False, False, 15)

            row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
            delete_check.connect("clicked", check_swap, keep_check)
            keep_check.connect("clicked", check_swap, delete_check)
            row.pack_start(keep_check, False, False, 40)
            vbox.pack_start(row, False, False, 0)
            row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
            row.pack_start(delete_check, False, False, 40)
            vbox.pack_start(row, False, False, 0)
            keep_check.set_active(True)
            vbox.pack_start(scroll, True, True, 0)

        def browser_tab(browse_type):
            # Tab 2 file chooser
            browse_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
            browse_page.add(box)
            notebook.append_page(browse_page, Gtk.Label(label='Import'))
            file_chooser.connect("selection-changed", file_change)
            file_chooser.connect("file-activated", selection_made, '')
            if entry_type == 'Icon':
                file_chooser.set_current_folder(os.path.expanduser('~'))
                file_filter = Gtk.FileFilter()
                for pat in ['*.png', '*.svg', '*.xpm,', '*.jpg', '*.jpeg']:
                    file_filter.add_pattern(pat)
                file_filter.set_name('images')
                file_chooser.add_filter(file_filter)
            elif entry_type == 'add_tray':
                file_chooser.set_current_folder(os.path.expanduser('~'))
            else:
                file_chooser.set_current_folder('/usr/bin')
            box.add(file_chooser)

        def app_tab():
            # Tab1 Add Action / Application /  items
            scroll.set_min_content_width(-1)
            scroll.set_min_content_height(240)
            notebook.append_page(scroll, Gtk.Label(label='Items'))
            listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
            listbox.set_activate_on_single_click(False)
            listbox.connect("row-activated", selection_made)
            scroll.add(listbox)
            for i in data:
                row = Gtk.ListBoxRow()
                row_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
                icon = i[3]
                try:
                    image = Gtk.Image()
                    image.set_from_pixbuf(self.update_icon(icon, 24, 'image-missing'))
                    row_box.pack_start(image, False, False, 0)
                except gi.repository.GLib.Error:
                    print('unable to process image')
                    image = Gtk.Image()
                    image.set_from_pixbuf(self.update_icon('image_missing', 24, 'image-missing'))
                    row_box.pack_start(image, False, False, 0)
                row_box.pack_start(Gtk.Label(label='{}\n{}'.format(i[0], i[1])), False, False, 0)
                row.add(row_box)
                listbox.insert(row, -1)
            listbox.show_all()
            listbox.select_row(listbox.get_row_at_index(0))

        def tray_tab():
            def row_change(widget):
                index = widget.get_selected_row().get_index()
                self.selected_list_item = data[index]

            # Tab1 Create
            vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
            row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=20)
            row.set_border_width(20)
            tray_entry.set_text('tray')
            row.pack_end(tray_entry, True, True, 0)
            row.pack_end(Gtk.Label(label='Tray Name:'), False, False, 0)
            vbox.pack_start(row, False, False, 0)
            notebook.append_page(vbox, Gtk.Label(label='Create'))
            # Tab2 Restore
            if data:
                vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5)
                vbox.set_border_width(20)
                row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
                label = Gtk.Label()
                label.set_markup('<b>Select a tray to restore</b>')
                row.pack_start(label, False, False, 10)
                scroll = Gtk.ScrolledWindow()
                scroll.set_min_content_width(-1)
                scroll.set_min_content_height(180)
                vbox.pack_start(row, False, False, 0)
                vbox.pack_start(scroll, True, True, 0)
                listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
                listbox.set_activate_on_single_click(False)
                listbox.connect("selected-rows-changed", row_change)
                listbox.connect("row-activated", selection_made)
                scroll.add(listbox)
                for i in data:
                    row = Gtk.ListBoxRow()
                    row_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
                    row_box.pack_start(Gtk.Label(label='{}'.format(i)), False, False, 0)
                    row.add(row_box)
                    listbox.insert(row, -1)
                listbox.show_all()
                listbox.select_row(listbox.get_row_at_index(0))
                notebook.append_page(vbox, Gtk.Label(label='Restore'))

        def icon_tab():
            # Tab1 Icon Viewer
            icons = []
            jwm_icon_paths = self.jwm_icon_paths
            for path in jwm_icon_paths:
                icons.append(os.listdir(path))
            icon_list = []
            for i, path in zip(icons, jwm_icon_paths):
                tmp = []
                if jwm_icon_paths[-1] == '/': jwm_icon_path = jwm_icon_paths[:-1]
                for icon in i:
                    if icon not in icon_list:
                        file = os.path.join(path, icon)
                        if os.path.isfile(file):
                            tmp.append(file)
                icon_list.extend(tmp)
            viewer_page = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
            main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0)
            notebook.set_border_width(15)
            # Try statement because Slacko Puppy Linux gives an AttributeError with the following command.
            try:
                scroll.set_propagate_natural_width(True)
            except AttributeError:
                # set the width manually.  1000 should be about right for Slacko
                scroll.set_property("width-request", 1000)
            scroll.add(main_box)

            viewer_page.pack_start(scroll, True, True, 0)
            notebook.append_page(viewer_page, Gtk.Label(label='Icon Viewer'))
            box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
            for count, icon in enumerate(icon_list):
                try:
                    image = Gtk.Image()
                    pix = GdkPixbuf.Pixbuf.new_from_file_at_scale(icon, 32, 32, preserve_aspect_ratio=False)
                    image.set_from_pixbuf(pix)
                    button = Gtk.Button(image=image)
                    button.set_relief(Gtk.ReliefStyle.NONE)
                    button.connect("clicked", image_selected, icon)
                    box.pack_start(button, True, True, 0)
                except gi.repository.GLib.Error:
                    print('unable to process image')
                if (count + 1) % 18 == 0:
                    main_box.add(box)
                    box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
            main_box.add(box)

        entry_widgets = entry
        entry = entry_widgets[entry_type[1] - 1]
        entry_type = entry_type[0]

        if entry_type == 'Icon':
            self.selected_list_item = entry.get_text()
            title = 'Select an Icon'
            head_text = '<big><b>Select an icon</b></big>\nfrom the icon viewer or file browser'
            icon = self.update_icon(self.selected_list_item, 48, 'image-missing')
            self.selected_preview.set_from_pixbuf(icon)
        elif entry_type == 'add_tray':
            traylist = entry_widgets[0]
            title = 'Add or Remove Tray'
            head_text = '\t<big><b>Add or Remove a Tray</b></big>\n\tCreate, restore, import, or remove a tray.'
            image = Gtk.Image()
            image.set_from_pixbuf(self.update_icon('/usr/share/pixmaps/jwmkit/config.svg', 48, 'image-missing'))
        else:
            title = 'Assign an Action'
            head_text = '<big><b>Select an Action</b></big>\nfor the {} mouse button'.format(button_icon[1])
            image = Gtk.Image()
            image.set_from_pixbuf(self.update_icon(button_icon[0], 48, 'image-missing'))
            data = self.menu_list
            data.extend(self.app_data)

        dialog = Gtk.Dialog(title, self, 0)
        dialog.set_border_width(10)
        dialog.connect("delete-event", close)
        vbox = dialog.get_content_area()
        vbox.set_spacing(10)
        label = Gtk.Label()
        label.set_markup(head_text)
        row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        if entry_type == 'Icon':
            row.set_border_width(20)
            row.pack_start(self.selected_preview, False, False, 0)
        else:
            listbox = Gtk.ListBox()
            row.pack_start(image, False, False, 0)
        row.pack_start(label, False, False, 0)
        vbox.pack_start(row, False, False, 0)
        notebook = Gtk.Notebook()
        notebook.set_border_width(15)
        vbox.add(notebook)
        scroll = Gtk.ScrolledWindow()

        # buttons @ bottom
        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
        box.set_border_width(20)
        close_button = Gtk.Button(label='Cancel', image=Gtk.Image(stock=Gtk.STOCK_CANCEL))
        apply_button = Gtk.Button(label='Apply', image=Gtk.Image(stock=Gtk.STOCK_APPLY))
        close_button.set_property("width-request", 120)
        apply_button.set_property("width-request", 120)
        close_button.connect("clicked", close, '')

        if entry_type == 'Icon':
            apply_button.connect("clicked", apply_image)
            icon_tab()
            path_check = Gtk.CheckButton(label='Full Path')
            path_check.set_tooltip_text('Use full path instead of icon name')
            box.pack_start(path_check, False, False, 0)
            notebook.connect("switch-page", page_switch)
        elif entry_type == 'add_tray':
            listbox = Gtk.ListBox()
            tray_entry = Gtk.Entry()
            data = get_unused_trays(traylist)
            apply_button.connect("clicked", selection_made, '')
            tray_tab()
        else:
            apply_button.connect("clicked", selection_made, '')
            app_tab()
            name_check = Gtk.CheckButton(label='Set Name')
            name_check.set_tooltip_text("Allow / Deny updating name entry")
            icon_check = Gtk.CheckButton(label='Set Icon')
            icon_check.set_tooltip_text("Allow / Deny updating icon entry")
            tooltip_check = Gtk.CheckButton(label='Set Tooltip')
            tooltip_check.set_tooltip_text("Allow / Deny updating tooltip entry")
            name_check.set_active(False)
            icon_check.set_active(False)
            tooltip_check.set_active(False)
            notebook.connect("switch-page", hide_app_checks)
            box.pack_start(name_check, False, False, 10)
            box.pack_start(icon_check, False, False, 10)
            box.pack_start(tooltip_check, False, False, 10)
        box.pack_end(apply_button, False, False, 0)
        box.pack_end(close_button, False, False, 0)
        vbox.add(box)
        file_chooser = Gtk.FileChooserWidget(action=Gtk.FileChooserAction.OPEN)
        browser_tab(entry_type)
        if entry_type == 'add_tray':
            tray_combo = button_icon
            try:
                selected_tray = traylist[tray_combo.get_active()]
            except IndexError:
                selected_tray = None
            remove_listbox = Gtk.ListBox()
            keep_check = Gtk.CheckButton(label="Keep:   Tray is removed, but can be restored")
            delete_check = Gtk.CheckButton(label="Delete: Tray is removed, and deleted.  "
                                                 "It can NOT be restored")
            status_label = Gtk.Label()
            box.pack_end(status_label, True, True, 0)
            notebook.connect("switch-page", clear_status)
            tray_entry.connect('changed', clear_status, '', '')
            remove_tab()
        dialog.show_all()
        dialog.run()

    def clear_list(self):
        ok = True
        while ok:
            try:
                self.item_listbox.select_row(self.item_listbox.get_row_at_index(0))
                self.item_listbox.remove(self.item_listbox.get_selected_rows()[0])
            except IndexError:
                ok = False

    def move_up(self, button):
        i = self.item_listbox.get_selected_rows()[0].get_index()
        if i != 0:
            selected_row = self.item_listbox.get_row_at_index(i)
            self.item_listbox.remove(self.item_listbox.get_row_at_index(i))
            self.item_listbox.insert(selected_row, i - 1)
            self.item_data[i], self.item_data[i - 1] = self.item_data[i - 1], self.item_data[i]

    def move_down(self, button):
        i = self.item_listbox.get_selected_rows()[0].get_index()
        if i != len(self.item_listbox) - 1:
            selected_row = self.item_listbox.get_row_at_index(i)
            self.item_listbox.remove(selected_row)
            self.item_listbox.insert(selected_row, i + 1)
            self.item_data[i], self.item_data[i + 1] = self.item_data[i + 1], self.item_data[i]

    def update_icon(self, icon, size, missing):
        if not os.path.isfile(icon):
            if '/' in icon:
                icon = ''
            try:
                icon = self.get_icon_dir(icon)
                icon = GdkPixbuf.Pixbuf.new_from_file_at_scale(icon, size, size, preserve_aspect_ratio=False)
            except IndexError:
                icon = Gtk.IconTheme.get_default().load_icon(missing, size, Gtk.IconLookupFlags.FORCE_SIZE)
        else:
            icon = GdkPixbuf.Pixbuf.new_from_file_at_scale(icon, size, size, preserve_aspect_ratio=False)
        return icon

    def get_icon_dir(self, icon_name):
        # search the icon directories to find the icon
        icons = []
        icon_name = icon_name.replace(' ', '\ ')
        for icon_dir in self.jwm_icon_paths:
            icon_list = os.listdir(icon_dir)
            for icon in ['{}.png'.format(icon_name), '{}.svg'.format(icon_name),
                         '{}.xpm'.format(icon_name), '{}'.format(icon_name)]:
                if icon in icon_list:
                    icons.append(os.path.join(icon_dir, icon))
                    while '' in icons: icons.remove('')
                    if icons:
                        break
        return icons[0]

    def clear_widgets(self, combos):
        self.clear_list()
        spins = [self.height_spin, self.width_spin, self.x_offset_spin, self.y_offset_spin]
        for combo in combos:
            combo.set_active(-1)
        for spin in spins:
            spin.set_value(0)

    def help(self, button):
        index = self.item_listbox.get_selected_rows()[0].get_index()
        message = self.item_data[index][0]
        tag_dic = {'TrayButton': '\u2022 Program launcher with advanced options'
                                 '\n\u2022 Assign specific action for each specific mouse button'
                                 '\n\u2022 Usage: Provide shortcuts to menus, apps, etc',
                   'Clock': '\u2022 Display a clock on the tray\n'
                            '\u2022 Customize size, Format, and Timezone\n'
                            '\u2022 Provides launcher function similar to a TrayButton',
                   'Spacer': '\u2022 A spacer adds empty space to the tray (panel)'
                             '\n\u2022 Usage: preserve tray structure and item placement',
                   'TaskList': '\u2022 Displays a Tasklist (window list) on the Tray'
                               '\n\u2022 Provides a visual list of running task'
                               '\n\u2022 Usage: switch between windows quickly and with ease',
                   'Pager': '\u2022 Also called a workspace switch\n'
                            '\u2022 Provides a visual representation of the workspaces\n'
                            '\u2022 Usage: easily switch workspaces',
                   'Dock': '\u2022 Also called a system tray.\n\u2022 Displays system notifications\n'
                           '\u2022 Displays applets for simple task like volume control\n'
                           '\u2022 Display apps you do not want listed in the tasklist.',
                   'Swallow': '\u2022 Place a running app inside the tray (swallow the app)\n'
                              '\u2022 For example display a graph of memory or CPU usage'}
        info = '{}\n'.format(tag_dic[message])
        self.confirm_dialog('b2', 'About', message, info)

    def remove_item(self, button):
        index = self.item_listbox.get_selected_rows()[0].get_index()
        message = self.item_data[index][0]
        confirm = self.confirm_dialog('b1', 'Remove Item', 'Do you want to remove this {}?'.format(message),
                                      'removed items can not be restored,\nbut can be recreated as a new item')
        if confirm == -5:
            del self.item_data[index]
            i = self.item_listbox.get_row_at_index(index)
            self.item_listbox.remove(i)
            if index != 0: index = index - 1
            self.item_listbox.select_row(self.item_listbox.get_row_at_index(index))

    def output(self, button, combos, filename, tray_combo):

        def combine_buttons(buttons):
            from collections import defaultdict

            def find_dubs(buttons):
                all_values = defaultdict(list)
                for i, button in enumerate(buttons):
                    all_values[button].append(i + 1)
                return (button_value for action, button_value in all_values.items()
                        if action != '')

            button_nums = []
            for dub in sorted(find_dubs(buttons)):
                dub = " ".join(str(x) for x in dub)
                button_nums.append(dub.replace(' ', ''))
            return button_nums

        def process_spins(spin, option, default):
            value = int(spin.get_value())
            if value != default: tray_tag.set(option, str(value))

        filename = filename[tray_combo.get_active()][1]
        if filename[:5] == '$HOME':
            filename = os.path.join(os.path.expanduser('~'), filename[6:])

        options = ['layout', 'autohide', 'layer', 'valign', 'halign']
        jwm_tag = ET.Element('JWM')
        tray_tag = ET.SubElement(jwm_tag, "Tray")
        for combo, option in zip(combos, options):
            try:
                value = combo.get_active_text().lower()
            except AttributeError:
                value = ''
            if value not in ['', 'off', 'fixed', 'above', 'horizontal', None]: tray_tag.set(option, value)

        process_spins(self.height_spin, 'height', 0)
        process_spins(self.width_spin, 'width', 0)
        process_spins(self.screen_spin, 'screen', 0)
        if self.x_check.get_active():
            process_spins(self.x_offset_spin, 'x', '')
        if self.y_check.get_active():
            process_spins(self.y_offset_spin, 'y', '')

        for item_data in self.item_data:
            item_tag = ET.SubElement(tray_tag, item_data[0])
            if item_data[0] == 'Spacer':
                if item_data[1] not in ['0', '']: item_tag.set('height', item_data[1])
                if item_data[2] not in ['0', '']: item_tag.set('width', item_data[2])
            elif item_data[0] == 'TaskList':
                if item_data[1] not in ['true', '']: item_tag.set('labeled', item_data[1])
                if item_data[2] not in ['0', '']: item_tag.set('height', item_data[2])
                if item_data[2] not in ['0', '']: item_tag.set('maxwidth', item_data[3])
            elif item_data[0] == 'Pager':
                if item_data[1] not in ['false', '']: item_tag.set('labeled', item_data[1])
            if item_data[0] == 'Dock':
                if item_data[1] not in ['0', '']: item_tag.set('spacing', item_data[1])
                if item_data[2] not in ['0', '']: item_tag.set('width', item_data[2])
            elif item_data[0] == 'Swallow':
                if item_data[1] != '': item_tag.set('name', item_data[1])
                if item_data[2] != '': item_tag.text = item_data[2]
                if item_data[3] not in ['0', '']: item_tag.set('height', item_data[3])
                if item_data[4] not in ['0', '']: item_tag.set('width', item_data[4])
            elif item_data[0] == 'TrayButton':
                if item_data[1] != '': item_tag.set('label', item_data[1])
                if item_data[2] != '': item_tag.set('icon', item_data[2])
                if item_data[3] != '': item_tag.set('popup', item_data[3])
            elif item_data[0] == 'Clock':
                if item_data[1] != '': item_tag.set('format', item_data[1])
                if item_data[2] not in ['Local', '']: item_tag.set('zone', item_data[2])
                if item_data[8] not in ['0', '']: item_tag.set('height', item_data[8])
                if item_data[9] not in ['0', '']: item_tag.set('width', item_data[9])
                item_data.insert(3, '')
            if item_data[0] in ['TrayButton', 'Clock']:
                combined_buttons = combine_buttons(item_data[4:9])
                if combined_buttons == ['123']:
                    item_tag.text = item_data[4]
                else:
                    for button in combined_buttons:
                        if button[0] == '1':
                            button1 = ET.SubElement(item_tag, 'Button', {'mask': button})
                            button1.text = item_data[4]
                        elif button[0] == '2':
                            button2 = ET.SubElement(item_tag, 'Button', {'mask': button})
                            button2.text = item_data[5]
                        elif button[0] == '3':
                            button3 = ET.SubElement(item_tag, 'Button', {'mask': button})
                            button3.text = item_data[6]
                        elif button[0] == '4':
                            button4 = ET.SubElement(item_tag, 'Button', {'mask': button})
                            button4.text = item_data[7]
                        elif button[0] == '5':
                            button5 = ET.SubElement(item_tag, 'Button', {'mask': button})
                            button5.text = item_data[8]
                if item_data[0] == 'Clock': del item_data[3]

        jwm_tag = minidom.parseString(ET.tostring(jwm_tag)).toprettyxml(indent="   ")
        jwm_tag = ET.ElementTree(ET.fromstring(jwm_tag))
        jwm_tag.write(filename, encoding="utf-8", xml_declaration=True)
        os.system('jwm -restart')


win = MenusWindow()
win.connect("delete-event", Gtk.main_quit)
win.set_position(Gtk.WindowPosition.CENTER)
win.set_resizable(False)
win.show_all()
Gtk.main()
