Beispiel #1
0
class AlienFXApp(Gtk.Application):
    
    # These are the colours you can set a zone action to.
    colours = [
            "#000000",
            "#FFFFFF",
            "#00F3F1",
            "#00F89A",
            "#00FA52",
            "#00FF00",
            "#79F900",
            "#C1F800",
            "#F0E500",
            "#F0E500",
            "#FF4500",
            "#FF0000",
            "#FF006F",
            "#FF00B1",
            "#FF00F5",
            "#A400F5",
            "#6500F6",
            "#0000FF",
            "#004BF5",
            "#009AF4"
        ]
        
    def __init__(self):
        Gtk.Application.__init__(self)
        self.connect("activate", self.on_activate)
        self.controller = AlienFXProber.get_controller()
        self.themefile = AlienFXThemeFile(self.controller)
        self.selected_action = None
        self.action_type = self.themefile.KW_ACTION_TYPE_FIXED
        self.theme_edited = False
        self.set_theme_done = True

    def enable_delete_theme_button(self, enable):
        """ Enable or disable the "Delete Theme" button."""
        self.builder.get_object("toolbutton_delete_theme").set_sensitive(enable)
        
    def ask_discard_changes(self):
        """ Ask the user if he wants to discard theme edits."""
        main_window = self.builder.get_object("main_window")
        dialog = Gtk.MessageDialog(main_window, 
            Gtk.DialogFlags.MODAL,
            Gtk.MessageType.WARNING,
            Gtk.ButtonsType.YES_NO,
            "The current theme has unsaved changes. Do you want to discard them?")
        response = dialog.run()
        dialog.destroy()
        return response == Gtk.ResponseType.YES
            
    def on_action_new_theme_activate(self, widget):
        """ Handler for when the "New Theme" action is triggered."""
        if self.theme_edited:
            if not self.ask_discard_changes():
                return
                
        self.themefile = AlienFXThemeFile(self.controller)
        self.themefile.set_default_theme()
        self.load_theme("New Theme")
        self.set_theme_dirty(False)
        self.theme_loaded_from_file = False
        self.enable_delete_theme_button(False)
        
    def on_action_open_theme_activate(self, widget):
        """ Handler for when the "Open Theme" action is triggered."""
        if self.theme_edited:
            if not self.ask_discard_changes():
                return
                
        themes = self.themefile.get_themes()
        open_theme_list_store = self.builder.get_object("open_theme_list_store")
        open_theme_list_store.clear()
        for theme in themes:
            open_theme_list_store.append([theme])
        self.builder.get_object("open_theme_dialog").show()
        
    def on_action_save_theme_activate(self, widget):
        """ Handler for when the "Save Theme" action is triggered."""
        if self.themefile.theme_name == "":
            self.on_action_save_theme_as_activate(widget)
        else:
            self.themefile.save(self.themefile.theme_name)
            self.set_theme_dirty(False)
            self.theme_loaded_from_file = True
            self.enable_delete_theme_button(True)
        
    def on_action_save_theme_as_activate(self, widget):
        """ Handler for when the "Save Theme As" action is triggered."""
        themes = self.themefile.get_themes()
        saveas_theme_list_store = self.builder.get_object("saveas_theme_list store")
        saveas_theme_list_store.clear()
        for theme in themes:
            saveas_theme_list_store.append([theme])
        self.builder.get_object("saveas_theme_name").set_text("")
        self.builder.get_object("saveas_theme_dialog").show()
    
    def on_action_delete_theme_activate(self, widget):
        """ Handler for when the "Delete Theme" action is triggered."""
        main_window = self.builder.get_object("main_window")
        dialog = Gtk.MessageDialog(main_window, 
            Gtk.DialogFlags.MODAL,
            Gtk.MessageType.WARNING,
            Gtk.ButtonsType.YES_NO,
            ("Are you sure you want to delete this theme from disk? " +  
            "This cannot be undone!"""))
        response = dialog.run()
        dialog.destroy()
        if response == Gtk.ResponseType.NO:
            return
        if self.themefile.delete_theme_from_disk():
            self.theme_loaded_from_file = False
            self.enable_delete_theme_button(False)
            self.load_theme("New Theme")
            
    def on_action_delete_activate(self, widget):
        """ Handler for when the "Delete Action" action is triggered."""
        if self.selected_action is None:
            return
            
        (treeiter, action_index) = self.selected_action
        model = self.zone_list_view.get_model()
        actions = model[treeiter][1]
        del actions.actions[action_index:action_index+1]
        if len(actions.actions) == 1:
            self.builder.get_object("toolbutton_delete").set_sensitive(False)
        model[treeiter][1] = actions
        action_index -= 1
        if action_index < 0:
            action_index = 0
        self.action_cell_renderer.select_action_at_index(action_index)
        self.selected_action = (treeiter, action_index)
        self.set_theme_dirty(True)
        
    def on_action_add_activate(self, widget):
        """ Handler for when the "Add Action" action is triggered."""
        if self.selected_action is None:
            return
            
        (treeiter, action_index) = self.selected_action
        model = self.zone_list_view.get_model()
        actions = model[treeiter][1]
        new_action = self.themefile.make_zone_action(self.action_type, [[15, 15, 15]])
        actions.actions.insert(action_index+1, new_action)
        self.builder.get_object("toolbutton_delete").set_sensitive(True)
        model[treeiter][1] = actions
        self.set_theme_dirty(True)
        
    def set_theme_dirty(self, dirty):
        """ Set the current theme dirty, i.e. the current theme has 
        unsaved edits.
        """
        self.theme_edited = dirty
        title = self.builder.get_object("main_window").get_title()
        if dirty:
            if title[0] != "*":
                title = "*" + title
        else:
            if title[0] == "*":
                title = title[1:]
        self.builder.get_object("main_window").set_title(title)
        
    def set_theme(self):
        """ Set the current theme on the computer."""
        self.controller.set_theme(self.themefile)
        self.themefile.applied()
        self.set_theme_done = True
        
    def set_theme_done_cb(self):
        """ This idle task updates the GUI when the theme has been sent to the
        AlienFX controller."""
        if self.set_theme_done:
            spinner = self.builder.get_object("spinner")
            spinner.stop()
            spinner.hide()
            self.builder.get_object("statusbar").pop(self.context_id)
            self.builder.get_object("toolbar").set_sensitive(True)
            return False
        else:
            return True
        
    def on_action_apply_activate(self, widget):
        """ Handler for when the "Apply Theme" action is triggered."""
        self.builder.get_object("toolbar").set_sensitive(False)
        statusbar = self.builder.get_object("statusbar")
        self.context_id = statusbar.get_context_id("Apply")
        statusbar.push(self.context_id, "Applying theme...")
        spinner = self.builder.get_object("spinner")
        spinner.show()
        spinner.start()
        self.set_theme_done = False
        GObject.idle_add(self.set_theme_done_cb)
        self.set_theme_thread = threading.Thread(target=self.set_theme)
        self.set_theme_thread.start()

    def set_window_title(self, theme_name):
        """ Set the window title from the current theme name."""
        self.builder.get_object("main_window").set_title(theme_name + " - Alien FX")
        
    def load_theme(self, theme_name=None):
        """ Load a theme and display it in the GUI. If a theme name is supplied
        then show it in the window title; otherwise get the theme name from the
        theme file currently loaded."""
        normal_zone_list_store = self.builder.get_object("normal_zone_list_store")
        power_zone_list_store = self.builder.get_object("power_zone_list_store")
        normal_zone_list_store.clear()
        zones = self.controller.zone_map
        for zone in zones:
            if zone in self.controller.power_zones:
                power_zone_list_store.clear()
                power_states = [
                    self.controller.STATE_AC_SLEEP,
                    self.controller.STATE_AC_CHARGED,
                    self.controller.STATE_AC_CHARGING,
                    self.controller.STATE_BATTERY_SLEEP,
                    self.controller.STATE_BATTERY_ON,
                    self.controller.STATE_BATTERY_CRITICAL
                ]
                for state in power_states:
                    a = AlienFXActions()
                    a.actions = self.themefile.get_zone_actions(state, zone)
                    power_zone_list_store.append([state, a])
            elif zone in self.controller.zone_map:  # Is elif really necessary? Aren't we already iterating self.controller.zone_map?
                a = AlienFXActions()
                a.actions = self.themefile.get_zone_actions(self.controller.STATE_BOOT, zone)
                normal_zone_list_store.append([zone, a])
        self.zone_list_view.set_model(normal_zone_list_store)
        self.builder.get_object("radiobutton_normal_zones").set_active(True)
        if theme_name is not None:
            self.set_window_title(theme_name)
        elif self.themefile.theme_name == "":
            self.set_window_title("New theme")
        else:
            self.set_window_title(self.themefile.theme_name)
        self.enable_action_edit_controls(False)
    
    # Dialog box close handlers
    def on_dialog_close(self, dialog):
        # Instead of closing a dialog box, just hide it.
        GObject.signal_stop_emission_by_name(dialog, "close")
        dialog.hide()
        
    # Open dialog box handlers        
    def on_open_theme_cancel_clicked(self, button):
        self.builder.get_object("open_theme_dialog").hide()
        
    def do_load_theme(self, theme_name):
        self.themefile.load(theme_name)
        self.load_theme()
        self.builder.get_object("open_theme_dialog").hide()
        self.set_theme_dirty(False)
        self.theme_loaded_from_file = True
        self.enable_delete_theme_button(True)
        
    def on_open_theme_list_view_row_activated(self, treeview, path, column):
        """ Handler for when a theme in the theme list is double-clicked."""
        model = treeview.get_model()
        treeiter = model.get_iter(path)
        self.do_load_theme(model[treeiter][0])
        
    def on_open_theme_ok_clicked(self, button):
        """ Handler for when the OK button is clicked in the Open Theme dialog."""
        model, treeiter = self.open_theme_list_view.get_selection().get_selected()
        if treeiter is not None:
            self.do_load_theme(model[treeiter][0])
        
    def on_open_theme_list_selection_changed(self, selection):
        """ Handler for when a theme name is selected in the Open Theme dialog."""
        self.builder.get_object("open_theme_ok").set_sensitive(selection.count_selected_rows() > 0)
        
    # Save-as dialog box handlers
    def do_saveas_theme(self, theme_name):
        self.themefile.save(theme_name)
        self.set_window_title(self.themefile.theme_name)
        self.builder.get_object("saveas_theme_dialog").hide()
        self.set_theme_dirty(False)
        self.theme_loaded_from_file = True
        self.enable_delete_theme_button(True)
        
    def on_saveas_theme_name_activate(self, entry):
        """ Handler for when <Enter> is pressed in the theme name entry box."""
        self.do_saveas_theme(entry.get_text())
        
    def on_saveas_theme_list_view_row_activated(self, treeview, path, column):
        """ Handler for when a theme in the theme list is double-clicked."""
        model = treeview.get_model()
        treeiter = model.get_iter(path)
        self.do_saveas_theme(model[treeiter][0])
        
    def on_saveas_theme_ok_clicked(self, button):
        """ Handler for when the OK button is clicked in the Save Theme As dialog."""
        name = self.builder.get_object("saveas_theme_name").get_text()
        self.do_saveas_theme(name)
        
    def on_saveas_theme_cancel_clicked(self, button):
        self.builder.get_object("saveas_theme_dialog").hide()
        
    def on_saveas_theme_list_selection_changed(self, selection):
        """ Handler for when a theme name is selected in the Save Theme As dialog."""
        model, treeiter = selection.get_selected()
        if treeiter is not None:
            name = model[treeiter][0]
            self.builder.get_object("saveas_theme_name").set_text(name)
        
    def on_saveas_theme_name_changed(self, editable):
        self.builder.get_object("saveas_theme_ok").set_sensitive(
            len(editable.get_text().strip()) > 0)
        
    # Zone select button handlers
    def on_radiobutton_normal_zones_toggled(self, button):
        if button.get_active():
            self.zone_list_view.set_model(self.builder.get_object("normal_zone_list_store"))
            self.enable_action_edit_controls(False)
        
    def on_radiobutton_power_zone_toggled(self, button):
        if button.get_active():
            self.zone_list_view.set_model(self.builder.get_object("power_zone_list_store"))
            self.enable_action_edit_controls(False)
        
    def on_activate(self, data=None):
        self.builder = Gtk.Builder()
        self.builder.add_from_file(pkg_resources.resource_filename(
            "alienfx.ui.gtkui", "glade/ui.glade"))
        
        self.zone_list_view = self.builder.get_object("zone_list_view")
        self.zone_list_view.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
        self.zone_list_view.connect(
            "button-press-event", self.zone_item_selected)
        self.action_cell_renderer = AlienFXActionCellRenderer(
            treeview=self.zone_list_view, max_colour_val=0xf)
        col_colour = Gtk.TreeViewColumn(
            "Actions", self.action_cell_renderer, actions=1)
        self.zone_list_view.append_column(col_colour)
        
        self.open_theme_list_view = self.builder.get_object("open_theme_list_view")
        self.open_theme_list_view.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
        self.open_theme_list_view.get_selection().connect(
            "changed", self.on_open_theme_list_selection_changed)
            
        self.saveas_theme_list_view = self.builder.get_object("saveas_theme_list_view")
        self.saveas_theme_list_view.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
        self.saveas_theme_list_view.get_selection().connect(
            "changed", self.on_saveas_theme_list_selection_changed)
            
        self.properties_frame = self.builder.get_object("properties_frame")
        
        self.palette1 = ColourPalette(
            self.colours, max_colour_val=0xf, num_rows=2, num_cols=10, 
            horizontal=True, selected_handler=self.on_colour_selected)
        self.builder.get_object("colour_palette1").add(self.palette1)
        
        self.palette2 = ColourPalette(
            self.colours, max_colour_val=0xf, num_rows=2, num_cols=10, 
            horizontal=True, selected_handler=self.on_colour_selected)
        self.builder.get_object("colour_palette2").add(self.palette2)
        self.palette2.set_sensitive(False)
        
        self.builder.connect_signals(self)
        main_window = self.builder.get_object("main_window")
        main_window.set_icon_from_file(pkg_resources.resource_filename(
            "alienfx", "data/icons/hicolor/scalable/apps/alienfx.svg"))
        main_window.show_all()
        self.add_window(main_window)
        
        if self.controller is None:
            Gtk.MessageDialog(
                main_window, Gtk.DialogFlags.MODAL, 
                Gtk.MessageType.ERROR, 
                Gtk.ButtonsType.CLOSE, 
                "No supported Alien FX controller found!").run()
            quit()
            
        self.themefile = AlienFXThemeFile(self.controller)
        last_theme_loaded = self.themefile.load_last_theme()
        if last_theme_loaded:
            self.load_theme("Current Theme")
        else:
            self.load_theme("New Theme")
        self.theme_loaded_from_file = False
        self.enable_delete_theme_button(False)
        
    def on_main_window_delete_event(self, widget, event):
        if self.theme_edited:
            if self.ask_discard_changes():
                widget.destroy()
        else:
            widget.destroy()
        return True
        
    # Action edit control handlers
    def on_radiobutton_fixed_colour_toggled(self, button):
        if button.get_active():
            self.palette2.set_sensitive(False)
            self.action_type = self.themefile.KW_ACTION_TYPE_FIXED
    
    def on_radiobutton_blinking_colour_toggled(self, button):
        if button.get_active():
            self.palette2.set_sensitive(False)
            self.action_type = self.themefile.KW_ACTION_TYPE_BLINK
                    
    def on_radiobutton_morphing_colour_toggled(self, button):
        if button.get_active():
            self.palette2.set_sensitive(True)
            self.action_type = self.themefile.KW_ACTION_TYPE_MORPH
        
    def on_colour_selected(self, sender=None, data=None):
        if self.selected_action is None:
            return
            
        colour = sender.get_colour()
        (treeiter, action_index) = self.selected_action
        model = self.zone_list_view.get_model()
        actions = model[treeiter][1]
        action = actions.actions[action_index]
        self.themefile.set_action_type(action, self.action_type)
        old_colours = self.themefile.get_action_colours(action)
        if (self.action_type in [
                self.themefile.KW_ACTION_TYPE_FIXED, 
                self.themefile.KW_ACTION_TYPE_BLINK]):
            if len(old_colours) != 1:
                old_colours = old_colours[0:0]
        if self.action_type == self.themefile.KW_ACTION_TYPE_MORPH:
            if len(old_colours) != 2:
                old_colours.append([0, 0, 0])
        if sender.get_parent() == self.palette1:
            old_colours[0] = colour
        if sender.get_parent() == self.palette2:
            old_colours[1] = colour
        self.themefile.set_action_colours(action, old_colours)
        model[treeiter][1] = actions
        self.set_theme_dirty(True)
                    
    def enable_action_edit_controls(self, enable):
        self.builder.get_object("toolbutton_add").set_sensitive(enable)
        self.builder.get_object("toolbutton_delete").set_sensitive(enable)
        self.properties_frame.set_sensitive(enable)
        
    def zone_item_selected(self, treeview, event):
        (path, column, cell_x, cell_y) = treeview.get_path_at_pos(event.x, event.y)
        self.selected_action = None
        self.enable_action_edit_controls(False)
        self.action_cell_renderer.select_action_at_x(None)
        
        if column.get_title() == "Actions":
            action_index = self.action_cell_renderer.select_action_at_x(cell_x)
            model = treeview.get_model()
            treeiter = model.get_iter(path)
            actions = model[treeiter][1]
            if action_index < len(actions.actions):
                self.enable_action_edit_controls(True)
                self.selected_action = (treeiter, action_index)
                if len(actions.actions) == 1:
                    self.builder.get_object("toolbutton_delete").set_sensitive(False)
Beispiel #2
0
 def on_activate(self, data=None):
     self.builder = Gtk.Builder()
     self.builder.add_from_file(pkg_resources.resource_filename(
         "alienfx.ui.gtkui", "glade/ui.glade"))
     
     self.zone_list_view = self.builder.get_object("zone_list_view")
     self.zone_list_view.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
     self.zone_list_view.connect(
         "button-press-event", self.zone_item_selected)
     self.action_cell_renderer = AlienFXActionCellRenderer(
         treeview=self.zone_list_view, max_colour_val=0xf)
     col_colour = Gtk.TreeViewColumn(
         "Actions", self.action_cell_renderer, actions=1)
     self.zone_list_view.append_column(col_colour)
     
     self.open_theme_list_view = self.builder.get_object("open_theme_list_view")
     self.open_theme_list_view.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
     self.open_theme_list_view.get_selection().connect(
         "changed", self.on_open_theme_list_selection_changed)
         
     self.saveas_theme_list_view = self.builder.get_object("saveas_theme_list_view")
     self.saveas_theme_list_view.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
     self.saveas_theme_list_view.get_selection().connect(
         "changed", self.on_saveas_theme_list_selection_changed)
         
     self.properties_frame = self.builder.get_object("properties_frame")
     
     self.palette1 = ColourPalette(
         self.colours, max_colour_val=0xf, num_rows=2, num_cols=10, 
         horizontal=True, selected_handler=self.on_colour_selected)
     self.builder.get_object("colour_palette1").add(self.palette1)
     
     self.palette2 = ColourPalette(
         self.colours, max_colour_val=0xf, num_rows=2, num_cols=10, 
         horizontal=True, selected_handler=self.on_colour_selected)
     self.builder.get_object("colour_palette2").add(self.palette2)
     self.palette2.set_sensitive(False)
     
     self.builder.connect_signals(self)
     main_window = self.builder.get_object("main_window")
     main_window.set_icon_from_file(pkg_resources.resource_filename(
         "alienfx", "data/icons/hicolor/scalable/apps/alienfx.svg"))
     main_window.show_all()
     self.add_window(main_window)
     
     if self.controller is None:
         Gtk.MessageDialog(
             main_window, Gtk.DialogFlags.MODAL, 
             Gtk.MessageType.ERROR, 
             Gtk.ButtonsType.CLOSE, 
             "No supported Alien FX controller found!").run()
         quit()
         
     self.themefile = AlienFXThemeFile(self.controller)
     last_theme_loaded = self.themefile.load_last_theme()
     if last_theme_loaded:
         self.load_theme("Current Theme")
     else:
         self.load_theme("New Theme")
     self.theme_loaded_from_file = False
     self.enable_delete_theme_button(False)