def __init__(self, parent): """Prepare the command arguments dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('command_arguments.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_arguments.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_arguments, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the arguments self.model_arguments = self.ui.store_arguments self.arguments = '[]' # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def __init__(self, parent): """Prepare the groups dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('groups.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_groups.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_groups, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the groups self.model = ModelGroups(self.ui.store_groups) self.selected_iter = None # Sort the data in the models self.model.model.set_sort_column_id( self.ui.column_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def __init__(self, parent, services): """Prepare the services detail dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('service_detail.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_edit_service.set_transient_for(parent) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.model = services self.selected_iter = None self.name = '' self.description = '' self.command = '[]' self.terminal = False self.icon = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def __init__(self, parent, delete_event_cb): """Prepare the Debug dialog""" self.on_window_debug_delete_event = delete_event_cb # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('debug.glade')) # Initialize preferences self.actions_preferences = { DEBUG_ENABLED: self.ui.action_enable, DEBUG_ENABLED_HIDDEN: self.ui.action_enable_hidden, DEBUG_SHOW_INFO: self.ui.action_show_info, DEBUG_SHOW_WARNING: self.ui.action_show_warning, DEBUG_SHOW_ERROR: self.ui.action_show_error, DEBUG_TIMESTAMP: self.ui.action_show_timestamp, DEBUG_FOLLOW_TEXT: self.ui.action_follow_text, } for key in self.actions_preferences: self.actions_preferences[key].set_active(preferences.get(key)) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.ToolButton): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def __init__(self, parent, destinations): """Prepare the service association dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('service_association.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_association.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_association, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Load destinations self.destinations = destinations self.ui.cbo_destinations.set_model(self.destinations.model) # Load services self.services = ModelServices(self.ui.store_services) self.services.load(model_services.services) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) self.service_arguments_widgets = {}
def __init__(self, parent, destinations): """Prepare the destination dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('destination.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_destination.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position( self.ui.dialog_destination, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.model = destinations self.selected_iter = None self.name = '' self.value = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def __init__(self, parent): """Prepare the services dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('services.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_services.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position( self.ui.dialog_services, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the services self.model = ModelServices(self.ui.store_services) self.selected_iter = None self.ui.cell_icon.props.height = preferences.get(preferences.ICON_SIZE) # Sort the data in the models self.model.model.set_sort_column_id( self.ui.column_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def __init__(self, parent): """Prepare the command arguments dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file("command_arguments.glade")) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_arguments.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_arguments, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace("_", "")) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the arguments self.model_arguments = self.ui.store_arguments self.arguments = "[]" # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def loadUI(self): """Load the interface UI""" self.ui = GtkBuilderLoader(get_ui_file('main.glade')) self.ui.win_main.set_application(self.application) self.ui.win_main.set_title(APP_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.ToolButton): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Set list items row height icon_size = preferences.ICON_SIZE self.ui.cell_name.props.height = preferences.get(icon_size) self.ui.cell_group_name.props.height = preferences.get(icon_size) # Set groups visibility self.ui.scroll_groups.set_visible( preferences.get(preferences.GROUPS_SHOW)) # Add a Gtk.Headerbar, only for GTK+ 3.10.0 and higher if (not Gtk.check_version(3, 10, 0) and not preferences.get(preferences.HEADERBARS_DISABLE)): self.load_ui_headerbar() if preferences.get(preferences.HEADERBARS_REMOVE_TOOLBAR): # This is only for development, it should always be True # Remove the redundant toolbar self.ui.toolbar_main.destroy() # Flatten the Gtk.ScrolledWindows self.ui.scroll_groups.set_shadow_type(Gtk.ShadowType.NONE) self.ui.scroll_connections.set_shadow_type(Gtk.ShadowType.NONE) # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def __init__(self, parent, delete_event_cb): """Prepare the processes dialog""" self.on_window_processes_delete_event = delete_event_cb self.processes = {} self.poller_id = None # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('processes.glade')) # Restore the saved size and position settings.positions.restore_window_position( self.ui.window_processes, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.ToolButton): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.model = ModelProcesses(self.ui.store_processes) # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def __init__(self, parent): """Prepare the argument dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('command_argument.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_argument.set_transient_for(parent) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.argument = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self)
def startup(self, application): """Configure the application during the startup""" self.ui = UIMain(self) # Add the about action to the app menu action = Gio.SimpleAction(name="settings_folder") action.connect("activate", self.on_app_settings_folder_activate) self.add_action(action) # Add the about action to the app menu action = Gio.SimpleAction(name="about") action.connect("activate", self.on_app_about_activate) self.add_action(action) # Add the quit action to the app menu action = Gio.SimpleAction(name="quit") action.connect("activate", self.on_app_quit_activate) self.add_action(action) # Add the app menu builder_appmenu = GtkBuilderLoader(get_ui_file('appmenu.ui')) self.set_app_menu(builder_appmenu.app_menu)
def __init__(self, parent): """Prepare the about dialog""" # Retrieve the translators list translators = [] for line in readlines(FILE_TRANSLATORS, False): if ':' in line: line = line.split(':', 1)[1] line = line.replace('(at)', '@').strip() if line not in translators: translators.append(line) # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('about.glade')) # Set various properties self.ui.dialog_about.set_program_name(APP_NAME) self.ui.dialog_about.set_version('Version %s' % APP_VERSION) self.ui.dialog_about.set_comments(APP_DESCRIPTION) self.ui.dialog_about.set_website(APP_URL) self.ui.dialog_about.set_copyright(APP_COPYRIGHT) # Prepare lists for authors and contributors authors = ['%s <%s>' % (APP_AUTHOR, APP_AUTHOR_EMAIL)] contributors = [] for line in readlines(FILE_CONTRIBUTORS, False): contributors.append(line) if len(contributors) > 0: contributors.insert(0, _('Contributors:')) authors.extend(contributors) self.ui.dialog_about.set_authors(authors) self.ui.dialog_about.set_license('\n'.join( readlines(FILE_LICENSE, True))) self.ui.dialog_about.set_translator_credits('\n'.join(translators)) # Retrieve the external resources links # only for GTK+ 3.6.0 and higher if not Gtk.check_version(3, 6, 0): for line in readlines(FILE_RESOURCES, False): resource_type, resource_url = line.split(':', 1) self.ui.dialog_about.add_credit_section( resource_type, (resource_url, )) icon_logo = Pixbuf.new_from_file(FILE_ICON) self.ui.dialog_about.set_logo(icon_logo) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_about.set_transient_for(parent)
class UIServices(object): def __init__(self, parent): """Prepare the services dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('services.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_services.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_services, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the services self.model = ModelServices(self.ui.store_services) self.selected_iter = None self.ui.cell_icon.props.height = preferences.get(preferences.ICON_SIZE) # Sort the data in the models self.model.model.set_sort_column_id( self.ui.column_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self): """Show the Services dialog""" self.ui.dialog_services.run() self.ui.dialog_services.hide() def destroy(self): """Destroy the Services dialog""" settings.positions.save_window_position(self.ui.dialog_services, SECTION_WINDOW_NAME) self.ui.dialog_services.destroy() self.ui.dialog_services = None def on_action_add_activate(self, action): """Add a new service""" dialog = UIServiceDetail(self.ui.dialog_services, self.model) if dialog.show(default_name='', default_description='', default_command='', default_terminal=False, default_icon='', title=_('Add new service'), treeiter=None) == Gtk.ResponseType.OK: self.model.add_data( ServiceInfo(name=dialog.name, description=dialog.description, command=dialog.command, terminal=dialog.terminal, icon=dialog.icon)) dialog.destroy() def on_action_edit_activate(self, action): """Edit the selected service""" selected_row = get_treeview_selected_row(self.ui.tvw_services) if selected_row: name = self.model.get_key(selected_row) description = self.model.get_description(selected_row) command = self.model.get_command(selected_row) terminal = self.model.get_terminal(selected_row) icon = self.model.get_icon(selected_row) selected_iter = self.model.get_iter(name) dialog = UIServiceDetail(self.ui.dialog_services, self.model) if dialog.show(default_name=name, default_description=description, default_command=command, default_terminal=terminal, default_icon=icon, title=_('Edit service'), treeiter=selected_iter) == Gtk.ResponseType.OK: # Update values self.model.set_data( selected_iter, ServiceInfo(name=dialog.name, description=dialog.description, command=dialog.command, terminal=dialog.terminal, icon=dialog.icon)) dialog.destroy() def on_action_remove_activate(self, action): """Remove the selected service""" selected_row = get_treeview_selected_row(self.ui.tvw_services) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_services, message_type=Gtk.MessageType.WARNING, title=None, msg1=_("Remove service"), msg2=_("Remove the selected service?"), is_response_id=Gtk.ResponseType.YES): self.model.remove(selected_row) def on_tvw_services_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" self.ui.action_edit.activate()
class UIServices(object): def __init__(self, parent): """Prepare the services dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('services.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_services.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position( self.ui.dialog_services, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the services self.model = ModelServices(self.ui.store_services) self.selected_iter = None self.ui.cell_icon.props.height = preferences.get(preferences.ICON_SIZE) # Sort the data in the models self.model.model.set_sort_column_id( self.ui.column_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self): """Show the Services dialog""" self.ui.dialog_services.run() self.ui.dialog_services.hide() def destroy(self): """Destroy the Services dialog""" settings.positions.save_window_position( self.ui.dialog_services, SECTION_WINDOW_NAME) self.ui.dialog_services.destroy() self.ui.dialog_services = None def on_action_add_activate(self, action): """Add a new service""" dialog = UIServiceDetail(self.ui.dialog_services, self.model) if dialog.show(default_name='', default_description='', default_command='', default_terminal=False, default_icon='', title=_('Add new service'), treeiter=None) == Gtk.ResponseType.OK: self.model.add_data(ServiceInfo(name=dialog.name, description=dialog.description, command=dialog.command, terminal=dialog.terminal, icon=dialog.icon)) dialog.destroy() def on_action_edit_activate(self, action): """Edit the selected service""" selected_row = get_treeview_selected_row(self.ui.tvw_services) if selected_row: name = self.model.get_key(selected_row) description = self.model.get_description(selected_row) command = self.model.get_command(selected_row) terminal = self.model.get_terminal(selected_row) icon = self.model.get_icon(selected_row) selected_iter = self.model.get_iter(name) dialog = UIServiceDetail(self.ui.dialog_services, self.model) if dialog.show(default_name=name, default_description=description, default_command=command, default_terminal=terminal, default_icon=icon, title=_('Edit service'), treeiter=selected_iter ) == Gtk.ResponseType.OK: # Update values self.model.set_data(selected_iter, ServiceInfo( name=dialog.name, description=dialog.description, command=dialog.command, terminal=dialog.terminal, icon=dialog.icon)) dialog.destroy() def on_action_remove_activate(self, action): """Remove the selected service""" selected_row = get_treeview_selected_row(self.ui.tvw_services) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_services, message_type=Gtk.MessageType.WARNING, title=None, msg1=_("Remove service"), msg2=_("Remove the selected service?"), is_response_id=Gtk.ResponseType.YES): self.model.remove(selected_row) def on_tvw_services_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" self.ui.action_edit.activate()
class UICommandArgument(object): def __init__(self, parent): """Prepare the argument dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('command_argument.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_argument.set_transient_for(parent) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.argument = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_value): """Show the command argument dialog""" self.ui.txt_argument.set_text(default_value) self.ui.txt_argument.grab_focus() response = self.ui.dialog_argument.run() self.ui.dialog_argument.hide() self.argument = self.ui.txt_argument.get_text().strip() return response def destroy(self): """Destroy the command argument dialog""" self.ui.dialog_argument.destroy() self.ui.dialog_argument = None def on_action_confirm_activate(self, action): """Check che argument before confirm""" def show_error_message_on_infobar(widget, error_msg): """Show the error message on the GtkInfoBar""" set_error_message_on_infobar( widget=widget, widgets=(self.ui.txt_argument, ), label=self.ui.lbl_error_message, infobar=self.ui.infobar_error_message, error_msg=error_msg) if len(self.ui.txt_argument.get_text().strip()) == 0: # Show error for missing argument show_error_message_on_infobar( self.ui.txt_argument, _('The argument is missing')) else: self.ui.dialog_argument.response(Gtk.ResponseType.OK) def on_infobar_error_message_response(self, widget, response_id): """Close the infobar""" if response_id == Gtk.ResponseType.CLOSE: self.ui.infobar_error_message.set_visible(False) def on_txt_argument_changed(self, widget): """Check the argument value field""" check_invalid_input(widget, False, True, True)
class UIProcesses(object): def __init__(self, parent, delete_event_cb): """Prepare the processes dialog""" self.on_window_processes_delete_event = delete_event_cb self.processes = {} self.poller_id = None # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('processes.glade')) # Restore the saved size and position settings.positions.restore_window_position( self.ui.window_processes, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.ToolButton): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.model = ModelProcesses(self.ui.store_processes) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self): """Show the processes dialog""" settings.positions.restore_window_position( self.ui.window_processes, SECTION_WINDOW_NAME) self.ui.window_processes.show() def hide(self): """Hide the processes dialog""" settings.positions.save_window_position( self.ui.window_processes, SECTION_WINDOW_NAME) self.ui.window_processes.hide() def destroy(self): """Destroy the Debug dialog""" self.hide() self.ui.window_processes.destroy() self.ui.window_processes = None def add_process(self, host, destination, service, command): """Add a new process""" process = subprocess.Popen(args=get_list_from_string_list(command), shell=False) treeiter = self.model.add_data(ProcessInfo(host, destination, service, process)) # Enable process actions self.ui.actions_processes.set_sensitive(True) # Save process for further polling self.processes[self.model.get_key(treeiter)] = process self.model.add_detail(treeiter, _('Process started'), 'media-playback-start') self.start_polling() def poll_processes(self): """Poll each process to check if it was terminated""" for key in self.processes.keys(): process = self.processes[key] return_code = process.poll() # Has the process exited? if return_code is not None: treeiter = self.model.get_iter(key) self.model.add_detail(treeiter, _('Exit code: %d') % return_code, 'media-playback-stop') # Remove the completed process self.processes.pop(key) if len(self.processes): # Continue the polling return True else: # Stop the polling self.poller_id = None return False def start_polling(self): """Start the poller timeout if it wasn't already started""" if not self.poller_id: self.poller_id = GLib.timeout_add(1000, self.poll_processes) def on_action_process_activate(self, action): """Execute an action for the selected process""" selected_row = get_treeview_selected_row(self.ui.tvw_processes) if selected_row: iter_parent = self.model.model.iter_parent(selected_row) selected_path = self.model.model[selected_row].path # Get the path of the process if iter_parent is None: tree_path = self.model.model[selected_row].path else: tree_path = self.model.model[iter_parent].path # selected_key = self.model.get_key(tree_path) if selected_key in self.processes: selected_process = self.processes[selected_key] treeiter = self.model.get_iter(selected_key) if action is self.ui.action_kill: # Resume the process and terminate it selected_process.send_signal(subprocess.signal.SIGCONT) selected_process.terminate() debug.add_info( _('The process with the PID %d was terminated') % ( selected_process.pid)) self.model.add_detail(treeiter, _('Process terminated'), 'media-playback-stop') elif action is self.ui.action_pause: # Pause the process by sending the STOP signal selected_process.send_signal(subprocess.signal.SIGSTOP) debug.add_info( _('The process with the PID %d was paused') % ( selected_process.pid)) self.model.add_detail(treeiter, _('Process paused'), 'media-playback-pause') elif action is self.ui.action_resume: # Pause the process by sending the CONT signal selected_process.send_signal(subprocess.signal.SIGCONT) debug.add_info( _('The process with the PID %d was resumed') % ( selected_process.pid)) self.model.add_detail(treeiter, _('Process resumed'), 'media-playback-start') def on_tvw_processes_row_activated(self, widget, treepath, column): """Expand or collapse the selected row""" selected_row = get_treeview_selected_row(self.ui.tvw_processes) if selected_row: iter_parent = self.ui.store_processes.iter_parent(selected_row) selected_path = self.model.model[selected_row].path # Get the path of the process if iter_parent is None: tree_path = self.model.model[selected_row].path expanded = self.ui.tvw_processes.row_expanded(tree_path) if expanded: self.ui.tvw_processes.collapse_row(tree_path) else: self.ui.tvw_processes.expand_row(tree_path, True) def on_tvw_processes_key_press_event(self, widget, event): """Expand and collapse nodes with keyboard arrows""" selected_row = get_treeview_selected_row(self.ui.tvw_processes) if selected_row and event.keyval in (Gdk.KEY_Left, Gdk.KEY_Right): iter_parent = self.ui.store_processes.iter_parent(selected_row) if selected_row and iter_parent is None: tree_path = self.model.get_path(selected_row) expanded = self.ui.tvw_processes.row_expanded(tree_path) if event.keyval == Gdk.KEY_Left and expanded: # Collapse the selected node self.ui.tvw_processes.collapse_row(tree_path) elif event.keyval == Gdk.KEY_Right and not expanded: # Expand the selected node self.ui.tvw_processes.expand_row(tree_path, False) elif event.keyval == Gdk.KEY_Menu: # Show popup menu on Menu key press event = Gdk.EventButton() event.type = Gdk.EventType.BUTTON_RELEASE event.button = Gdk.BUTTON_SECONDARY self.ui.tvw_processes.event(event) def on_tvw_processes_button_release_event(self, widget, event): """Show processes popup menu on right click""" if event.button == Gdk.BUTTON_SECONDARY: show_popup_menu(self.ui.menu_popup, event.button)
class UIDebug(object): def __init__(self, parent, delete_event_cb): """Prepare the Debug dialog""" self.on_window_debug_delete_event = delete_event_cb # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('debug.glade')) # Initialize preferences self.actions_preferences = { DEBUG_ENABLED: self.ui.action_enable, DEBUG_ENABLED_HIDDEN: self.ui.action_enable_hidden, DEBUG_SHOW_INFO: self.ui.action_show_info, DEBUG_SHOW_WARNING: self.ui.action_show_warning, DEBUG_SHOW_ERROR: self.ui.action_show_error, DEBUG_TIMESTAMP: self.ui.action_show_timestamp, DEBUG_FOLLOW_TEXT: self.ui.action_follow_text, } for key in self.actions_preferences: self.actions_preferences[key].set_active(preferences.get(key)) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.ToolButton): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self): """Show the Debug dialog""" settings.positions.restore_window_position(self.ui.window_debug, SECTION_WINDOW_NAME) self.ui.window_debug.show() def hide(self): """Hide the Debug dialog""" settings.positions.save_window_position(self.ui.window_debug, SECTION_WINDOW_NAME) self.ui.window_debug.hide() def destroy(self): """Destroy the Debug dialog""" self.hide() self.ui.window_debug.destroy() self.ui.window_debug = None def clear(self): """Clear the debug buffer""" self.ui.buffer_debug.set_text('') def add_text(self, text): """Add a text to the debug buffer""" if preferences.get(DEBUG_ENABLED) and ( preferences.get(DEBUG_ENABLED_HIDDEN) or self.ui.window_debug.get_visible()): # Insert the text at the end self.ui.buffer_debug.insert(self.ui.buffer_debug.get_end_iter(), text) # Follow the text if requested if preferences.get(DEBUG_FOLLOW_TEXT): process_events() self.ui.textview_debug.scroll_to_iter( iter=self.ui.buffer_debug.get_end_iter(), within_margin=0.0, use_align=False, xalign=0.0, yalign=0.0) def add_line(self, severity, text): """Add a text line to the debug buffer""" # Check the requested severity level if (severity == INFO and preferences.get(DEBUG_SHOW_INFO) or (severity == WARNING and preferences.get(DEBUG_SHOW_WARNING)) or (severity == ERROR and preferences.get(DEBUG_SHOW_ERROR))): timestamp = (DEBUG_FORMAT_TIMESTAMP.format(datetime.datetime.now()) if preferences.get(DEBUG_TIMESTAMP) else '') self.add_text( DEBUG_FORMAT.format(timestamp=timestamp, severity=severity, text=text)) def add_info(self, text): """Add a text line to the debug buffer with severity info""" self.add_line(INFO, text) def add_warning(self, text): """Add a text line to the debug buffer with severity warning""" self.add_line(WARNING, text) def add_error(self, text): """Add a text line to the debug buffer with severity error""" self.add_line(ERROR, text) def on_action_clear_activate(self, action): """Clear the debug text""" self.clear() def on_action_set_debug_flag(self, action): """Enable/disable various debug flags""" for key in self.actions_preferences: if action is self.actions_preferences[key]: preferences.set(key, action.get_active()) break
class UIServiceAssociation(object): def __init__(self, parent, destinations): """Prepare the service association dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('service_association.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_association.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_association, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Load destinations self.destinations = destinations self.ui.cbo_destinations.set_model(self.destinations.model) # Load services self.services = ModelServices(self.ui.store_services) self.services.load(model_services.services) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) self.service_arguments_widgets = {} def show(self, default_description, default_destination, default_service, default_arguments): """Show the Service association dialog""" # Set default description self.ui.entry_description.set_text(default_description) # Set default destination if default_destination: self.ui.cbo_destinations.set_active_id(default_destination) elif self.destinations.count() > 0: self.ui.cbo_destinations.set_active(0) # Set default service if default_service: self.ui.cbo_services.set_active_id(default_service) elif self.services.count() > 0: self.ui.cbo_services.set_active(0) # Set default arguments for argument in self.service_arguments_widgets: (new_label, new_entry) = self.service_arguments_widgets[argument] if argument in default_arguments: new_entry.set_text(default_arguments[argument]) # Show the dialog response = self.ui.dialog_association.run() self.ui.dialog_association.hide() self.destination = self.ui.cbo_destinations.get_active_id() self.service = self.ui.cbo_services.get_active_id() self.description = self.ui.entry_description.get_text() # Prepares argument values self.arguments = {} for argument in self.service_arguments_widgets: (new_label, new_entry) = self.service_arguments_widgets[argument] self.arguments[argument] = new_entry.get_text() return response def destroy(self): """Destroy the Service association dialog""" settings.positions.save_window_position(self.ui.dialog_association, SECTION_WINDOW_NAME) self.ui.dialog_association.destroy() self.ui.dialog_association = None def on_cbo_services_changed(self, widget): """Update the service arguments widgets""" treeiter = self.ui.cbo_services.get_active_iter() # Remove the previously added arguments widgets for argument in self.service_arguments_widgets: (new_label, new_entry) = self.service_arguments_widgets[argument] new_label.destroy() new_entry.destroy() self.service_arguments_widgets = {} # Collect the needed arguments command = get_list_from_string_list( self.services.get_command(treeiter)) row_number = 0 processed_arguments = [] # The argument address, already has a default widget processed_arguments.append('address') for option in command: arguments = get_string_fields(option) # Add a pair of widgets for each argument for argument in arguments: # Skip existing arguments if argument in processed_arguments: continue row_number += 1 processed_arguments.append(argument) # Add a new descriptive label for the argument new_label = Gtk.Label('%s:' % argument.title()) new_label.set_xalign(1.0) new_label.set_visible(True) self.ui.grid_service_arguments.attach(child=new_label, left=0, top=row_number, width=1, height=1) # Add a new entry for the argument value new_entry = Gtk.Entry() new_entry.set_visible(True) new_entry.set_hexpand(True) self.ui.grid_service_arguments.attach(child=new_entry, left=1, top=row_number, width=1, height=1) # Save a tuple of widgets, to remove later self.service_arguments_widgets[argument] = (new_label, new_entry) def on_cbo_destinations_changed(self, widget): """Update the address entry for the selected destination""" treeiter = self.ui.cbo_destinations.get_active_iter() self.ui.entry_service_arguments_address.set_text( self.destinations.get_value(treeiter))
class UIDebug(object): def __init__(self, parent, delete_event_cb): """Prepare the Debug dialog""" self.on_window_debug_delete_event = delete_event_cb # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('debug.glade')) # Initialize preferences self.actions_preferences = { DEBUG_ENABLED: self.ui.action_enable, DEBUG_ENABLED_HIDDEN: self.ui.action_enable_hidden, DEBUG_SHOW_INFO: self.ui.action_show_info, DEBUG_SHOW_WARNING: self.ui.action_show_warning, DEBUG_SHOW_ERROR: self.ui.action_show_error, DEBUG_TIMESTAMP: self.ui.action_show_timestamp, DEBUG_FOLLOW_TEXT: self.ui.action_follow_text, } for key in self.actions_preferences: self.actions_preferences[key].set_active(preferences.get(key)) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.ToolButton): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self): """Show the Debug dialog""" settings.positions.restore_window_position( self.ui.window_debug, SECTION_WINDOW_NAME) self.ui.window_debug.show() def hide(self): """Hide the Debug dialog""" settings.positions.save_window_position( self.ui.window_debug, SECTION_WINDOW_NAME) self.ui.window_debug.hide() def destroy(self): """Destroy the Debug dialog""" self.hide() self.ui.window_debug.destroy() self.ui.window_debug = None def clear(self): """Clear the debug buffer""" self.ui.buffer_debug.set_text('') def add_text(self, text): """Add a text to the debug buffer""" if preferences.get(DEBUG_ENABLED) and ( preferences.get(DEBUG_ENABLED_HIDDEN) or self.ui.window_debug.get_visible()): # Insert the text at the end self.ui.buffer_debug.insert(self.ui.buffer_debug.get_end_iter(), text) # Follow the text if requested if preferences.get(DEBUG_FOLLOW_TEXT): process_events() self.ui.textview_debug.scroll_to_iter( iter=self.ui.buffer_debug.get_end_iter(), within_margin=0.0, use_align=False, xalign=0.0, yalign=0.0) def add_line(self, severity, text): """Add a text line to the debug buffer""" # Check the requested severity level if (severity == INFO and preferences.get(DEBUG_SHOW_INFO) or (severity == WARNING and preferences.get(DEBUG_SHOW_WARNING)) or (severity == ERROR and preferences.get(DEBUG_SHOW_ERROR))): timestamp = (DEBUG_FORMAT_TIMESTAMP.format(datetime.datetime.now()) if preferences.get(DEBUG_TIMESTAMP) else '') self.add_text(DEBUG_FORMAT.format(timestamp=timestamp, severity=severity, text=text)) def add_info(self, text): """Add a text line to the debug buffer with severity info""" self.add_line(INFO, text) def add_warning(self, text): """Add a text line to the debug buffer with severity warning""" self.add_line(WARNING, text) def add_error(self, text): """Add a text line to the debug buffer with severity error""" self.add_line(ERROR, text) def on_action_clear_activate(self, action): """Clear the debug text""" self.clear() def on_action_set_debug_flag(self, action): """Enable/disable various debug flags""" for key in self.actions_preferences: if action is self.actions_preferences[key]: preferences.set(key, action.get_active()) break
class UIServiceDetail(object): def __init__(self, parent, services): """Prepare the services detail dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('service_detail.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_edit_service.set_transient_for(parent) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.model = services self.selected_iter = None self.name = '' self.description = '' self.command = '[]' self.terminal = False self.icon = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_name, default_description, default_command, default_terminal, default_icon, title, treeiter): """Show the Services detail dialog""" self.ui.txt_name.set_text(default_name) self.ui.txt_description.set_text(default_description) self.ui.txt_command.set_text(default_command) self.ui.chk_terminal.set_active(default_terminal) self.ui.txt_icon.set_text(default_icon) self.ui.txt_name.grab_focus() self.ui.dialog_edit_service.set_title(title) self.selected_iter = treeiter response = self.ui.dialog_edit_service.run() self.ui.dialog_edit_service.hide() self.name = self.ui.txt_name.get_text().strip() self.description = self.ui.txt_description.get_text().strip() self.command = self.ui.txt_command.get_text().strip() self.terminal = self.ui.chk_terminal.get_active() self.icon = self.ui.txt_icon.get_text().strip() return response def destroy(self): """Destroy the Services dialog""" self.ui.dialog_edit_service.destroy() self.ui.dialog_edit_service = None def on_action_confirm_activate(self, action): """Check che service configuration before confirm""" def show_error_message_on_infobar(widget, error_msg): """Show the error message on the GtkInfoBar""" set_error_message_on_infobar(widget=widget, widgets=(self.ui.txt_name, self.ui.txt_description, self.ui.txt_command), label=self.ui.lbl_error_message, infobar=self.ui.infobar_error_message, error_msg=error_msg) name = self.ui.txt_name.get_text().strip() description = self.ui.txt_description.get_text().strip() command = self.ui.txt_command.get_text().strip() terminal = self.ui.chk_terminal.get_active() icon = self.ui.txt_icon.get_text().strip() if len(name) == 0: # Show error for missing service name show_error_message_on_infobar(self.ui.txt_name, _('The service name is missing')) elif '\'' in name or '\\' in name or '/' in name or ',' in name: # Show error for invalid service name show_error_message_on_infobar(self.ui.txt_name, _('The service name is invalid')) elif self.model.get_iter(name) not in (None, self.selected_iter): # Show error for existing service name show_error_message_on_infobar( self.ui.txt_name, _('A service with that name already exists')) elif len(description) == 0: # Show error for missing service description show_error_message_on_infobar( self.ui.txt_description, _('The service description is missing')) elif '\'' in description or '\\' in description: # Show error for invalid service description show_error_message_on_infobar( self.ui.txt_description, _('The service description is invalid')) elif len(command) == 0: # Show error for missing service description show_error_message_on_infobar(self.ui.txt_command, _('The service command is missing')) elif len(icon) > 0 and not os.path.isfile(icon): # Show error for missing service description show_error_message_on_infobar( self.ui.txt_icon, _('The service icon doesn' 't exists')) else: self.ui.dialog_edit_service.response(Gtk.ResponseType.OK) def on_infobar_error_message_response(self, widget, response_id): """Close the infobar""" if response_id == Gtk.ResponseType.CLOSE: self.ui.infobar_error_message.set_visible(False) def on_txt_name_changed(self, widget): """Check the service name field""" check_invalid_input(widget, False, False, False) def on_txt_description_changed(self, widget): """Check the service description field""" check_invalid_input(widget, False, True, False) def on_txt_icon_changed(self, widget): """Check the icon field""" text = widget.get_text().strip() if len(text) > 0 and os.path.isfile(text): icon_size = preferences.get(preferences.ICON_SIZE) self.ui.image_icon.set_from_pixbuf( GdkPixbuf.Pixbuf.new_from_file_at_size(text, icon_size, icon_size)) icon_name = None else: icon_name = 'dialog-error' if len(text) > 0 else None self.ui.image_icon.set_from_icon_name('image-missing', Gtk.IconSize.LARGE_TOOLBAR) widget.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon_name) def on_action_browse_icon_activate(self, action): """Browse for an icon file""" def update_preview_cb(widget, image, get_preview_filename, set_active): """Update preview by trying to load the image""" try: # Try to load the image from the previewed file image.set_from_pixbuf( GdkPixbuf.Pixbuf.new_from_file_at_size( get_preview_filename(), preferences.get(preferences.PREVIEW_SIZE), preferences.get(preferences.PREVIEW_SIZE))) set_active(True) except: # Hide the preview widget for errors image.set_from_pixbuf(None) set_active(False) # Prepare the browse for icon dialog dialog = UIFileChooserOpenFile(self.ui.dialog_edit_service, text("Select a File")) dialog.add_filter(_("All Image Files"), "image/*", None) dialog.add_filter(_("All Files"), None, "*") dialog.set_filename(self.ui.txt_icon.get_text()) # Set the image preview widget image_preview = Gtk.Image() image_preview.set_hexpand(False) image_preview.set_size_request( preferences.get(preferences.PREVIEW_SIZE), -1) dialog.set_preview_widget(image_preview, update_preview_cb) # Show the browse for icon dialog filename = dialog.show() if filename is not None: self.ui.txt_icon.set_text(filename) dialog.destroy() def on_action_configure_activate(self, action): """Configure the arguments list""" dialog = UICommandArguments(self.ui.dialog_edit_service) dialog.show(default_command=self.ui.txt_command.get_text()) self.ui.txt_command.set_text(dialog.arguments)
class UIGroups(object): def __init__(self, parent): """Prepare the groups dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('groups.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_groups.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_groups, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the groups self.model = ModelGroups(self.ui.store_groups) self.selected_iter = None # Sort the data in the models self.model.model.set_sort_column_id( self.ui.column_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self): """Show the Groups dialog""" self.ui.dialog_groups.run() self.ui.dialog_groups.hide() def destroy(self): """Destroy the Groups dialog""" settings.positions.save_window_position(self.ui.dialog_groups, SECTION_WINDOW_NAME) self.ui.dialog_groups.destroy() self.ui.dialog_groups = None def on_action_add_activate(self, action): """Add a new group""" dialog = UIGroupDetail(self.ui.dialog_groups, self.model) if dialog.show(default_name='', title=_('Add new group'), treeiter=None) == Gtk.ResponseType.OK: os.mkdir(os.path.join(DIR_HOSTS, dialog.name)) self.model.add_data( GroupInfo(name=dialog.name, description=dialog.name)) debug.add_info(_('Added a new group "%s"') % dialog.name) dialog.destroy() def on_action_remove_activate(self, action): """Remove the selected group""" selected_row = get_treeview_selected_row(self.ui.tvw_groups) group_name = self.model.get_key(selected_row) if selected_row else '' if selected_row and group_name and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_groups, message_type=Gtk.MessageType.WARNING, title=None, msg1=_('Remove the group'), msg2=_('Remove the group «%s»?') % group_name, is_response_id=Gtk.ResponseType.YES): group_path = os.path.join(DIR_HOSTS, group_name) # Check for directory not empty if len(os.listdir(group_path)) and not show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_groups, message_type=Gtk.MessageType.WARNING, title=None, msg1=_('The group is not empty'), msg2='%s\n%s\n\n%s' % ( text('If you delete an item, it will ' 'be permanently lost.'), _('All the hosts defined for the group will be lost.'), _('Are you sure you want to delete the ' 'group «%s»?') % group_name, ), is_response_id=Gtk.ResponseType.YES): # Exit immediately without deleting the group return # Delete all the contained files and the directory for the group for filename in os.listdir(group_path): os.remove(os.path.join(group_path, filename)) os.rmdir(group_path) debug.add_info(_('Removed the group "%s"') % group_name) self.model.remove(selected_row)
class UIDestination(object): def __init__(self, parent, destinations): """Prepare the destination dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('destination.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_destination.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_destination, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.model = destinations self.selected_iter = None self.name = '' self.value = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_name, default_value, title, treeiter): """Show the destination dialog""" self.ui.txt_name.set_text(default_name) self.ui.txt_value.set_text(default_value) self.ui.txt_name.grab_focus() self.ui.dialog_destination.set_title(title) self.selected_iter = treeiter response = self.ui.dialog_destination.run() self.ui.dialog_destination.hide() self.name = self.ui.txt_name.get_text().strip() self.value = self.ui.txt_value.get_text().strip() return response def destroy(self): """Destroy the destination dialog""" settings.positions.save_window_position(self.ui.dialog_destination, SECTION_WINDOW_NAME) self.ui.dialog_destination.destroy() self.ui.dialog_destination = None def on_action_confirm_activate(self, action): """Check the destination configuration before confirm""" def show_error_message_on_infobar(widget, error_msg): """Show the error message on the GtkInfoBar""" set_error_message_on_infobar(widget=widget, widgets=(self.ui.txt_name, self.ui.txt_value), label=self.ui.lbl_error_message, infobar=self.ui.infobar_error_message, error_msg=error_msg) name = self.ui.txt_name.get_text().strip() value = self.ui.txt_value.get_text().strip() if len(name) == 0: # Show error for missing destination name show_error_message_on_infobar(self.ui.txt_name, _('The destination name is missing')) elif '\'' in name or '\\' in name: # Show error for invalid destination name show_error_message_on_infobar(self.ui.txt_name, _('The destination name is invalid')) elif self.model.get_iter(name) not in (None, self.selected_iter): # Show error for existing destination name show_error_message_on_infobar( self.ui.txt_name, _('A destination with that name already exists')) elif len(value) == 0: # Show error for missing destination value show_error_message_on_infobar( self.ui.txt_value, _('The destination value is missing')) else: self.ui.dialog_destination.response(Gtk.ResponseType.OK) def on_infobar_error_message_response(self, widget, response_id): """Close the infobar""" if response_id == Gtk.ResponseType.CLOSE: self.ui.infobar_error_message.set_visible(False) def on_txt_name_changed(self, widget): """Check the destination name field""" check_invalid_input(widget, False, False, False)
class UIDestination(object): def __init__(self, parent, destinations): """Prepare the destination dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('destination.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_destination.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position( self.ui.dialog_destination, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.model = destinations self.selected_iter = None self.name = '' self.value = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_name, default_value, title, treeiter): """Show the destination dialog""" self.ui.txt_name.set_text(default_name) self.ui.txt_value.set_text(default_value) self.ui.txt_name.grab_focus() self.ui.dialog_destination.set_title(title) self.selected_iter = treeiter response = self.ui.dialog_destination.run() self.ui.dialog_destination.hide() self.name = self.ui.txt_name.get_text().strip() self.value = self.ui.txt_value.get_text().strip() return response def destroy(self): """Destroy the destination dialog""" settings.positions.save_window_position( self.ui.dialog_destination, SECTION_WINDOW_NAME) self.ui.dialog_destination.destroy() self.ui.dialog_destination = None def on_action_confirm_activate(self, action): """Check the destination configuration before confirm""" def show_error_message_on_infobar(widget, error_msg): """Show the error message on the GtkInfoBar""" set_error_message_on_infobar( widget=widget, widgets=(self.ui.txt_name, self.ui.txt_value), label=self.ui.lbl_error_message, infobar=self.ui.infobar_error_message, error_msg=error_msg) name = self.ui.txt_name.get_text().strip() value = self.ui.txt_value.get_text().strip() if len(name) == 0: # Show error for missing destination name show_error_message_on_infobar( self.ui.txt_name, _('The destination name is missing')) elif '\'' in name or '\\' in name: # Show error for invalid destination name show_error_message_on_infobar( self.ui.txt_name, _('The destination name is invalid')) elif self.model.get_iter(name) not in (None, self.selected_iter): # Show error for existing destination name show_error_message_on_infobar( self.ui.txt_name, _('A destination with that name already exists')) elif len(value) == 0: # Show error for missing destination value show_error_message_on_infobar( self.ui.txt_value, _('The destination value is missing')) else: self.ui.dialog_destination.response(Gtk.ResponseType.OK) def on_infobar_error_message_response(self, widget, response_id): """Close the infobar""" if response_id == Gtk.ResponseType.CLOSE: self.ui.infobar_error_message.set_visible(False) def on_txt_name_changed(self, widget): """Check the destination name field""" check_invalid_input(widget, False, False, False)
class UICommandArgument(object): def __init__(self, parent): """Prepare the argument dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('command_argument.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_argument.set_transient_for(parent) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.argument = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_value): """Show the command argument dialog""" self.ui.txt_argument.set_text(default_value) self.ui.txt_argument.grab_focus() response = self.ui.dialog_argument.run() self.ui.dialog_argument.hide() self.argument = self.ui.txt_argument.get_text().strip() return response def destroy(self): """Destroy the command argument dialog""" self.ui.dialog_argument.destroy() self.ui.dialog_argument = None def on_action_confirm_activate(self, action): """Check che argument before confirm""" def show_error_message_on_infobar(widget, error_msg): """Show the error message on the GtkInfoBar""" set_error_message_on_infobar(widget=widget, widgets=(self.ui.txt_argument, ), label=self.ui.lbl_error_message, infobar=self.ui.infobar_error_message, error_msg=error_msg) if len(self.ui.txt_argument.get_text().strip()) == 0: # Show error for missing argument show_error_message_on_infobar(self.ui.txt_argument, _('The argument is missing')) else: self.ui.dialog_argument.response(Gtk.ResponseType.OK) def on_infobar_error_message_response(self, widget, response_id): """Close the infobar""" if response_id == Gtk.ResponseType.CLOSE: self.ui.infobar_error_message.set_visible(False) def on_txt_argument_changed(self, widget): """Check the argument value field""" check_invalid_input(widget, False, True, True)
class UIGroups(object): def __init__(self, parent): """Prepare the groups dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('groups.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_groups.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position( self.ui.dialog_groups, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the groups self.model = ModelGroups(self.ui.store_groups) self.selected_iter = None # Sort the data in the models self.model.model.set_sort_column_id( self.ui.column_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self): """Show the Groups dialog""" self.ui.dialog_groups.run() self.ui.dialog_groups.hide() def destroy(self): """Destroy the Groups dialog""" settings.positions.save_window_position( self.ui.dialog_groups, SECTION_WINDOW_NAME) self.ui.dialog_groups.destroy() self.ui.dialog_groups = None def on_action_add_activate(self, action): """Add a new group""" dialog = UIGroupDetail(self.ui.dialog_groups, self.model) if dialog.show(default_name='', title=_('Add new group'), treeiter=None) == Gtk.ResponseType.OK: os.mkdir(os.path.join(DIR_HOSTS, dialog.name)) self.model.add_data(GroupInfo(name=dialog.name, description=dialog.name)) debug.add_info(_('Added a new group "%s"') % dialog.name) dialog.destroy() def on_action_remove_activate(self, action): """Remove the selected group""" selected_row = get_treeview_selected_row(self.ui.tvw_groups) group_name = self.model.get_key(selected_row) if selected_row else '' if selected_row and group_name and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_groups, message_type=Gtk.MessageType.WARNING, title=None, msg1=_('Remove the group'), msg2=_('Remove the group «%s»?') % group_name, is_response_id=Gtk.ResponseType.YES): group_path = os.path.join(DIR_HOSTS, group_name) # Check for directory not empty if len(os.listdir(group_path)) and not show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_groups, message_type=Gtk.MessageType.WARNING, title=None, msg1=_('The group is not empty'), msg2='%s\n%s\n\n%s' % ( text('If you delete an item, it will ' 'be permanently lost.'), _('All the hosts defined for the group will be lost.'), _('Are you sure you want to delete the ' 'group «%s»?') % group_name, ), is_response_id=Gtk.ResponseType.YES): # Exit immediately without deleting the group return # Delete all the contained files and the directory for the group for filename in os.listdir(group_path): os.remove(os.path.join(group_path, filename)) os.rmdir(group_path) debug.add_info(_('Removed the group "%s"') % group_name) self.model.remove(selected_row)
class UICommandArguments(object): def __init__(self, parent): """Prepare the command arguments dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file("command_arguments.glade")) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_arguments.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_arguments, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace("_", "")) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the arguments self.model_arguments = self.ui.store_arguments self.arguments = "[]" # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_command): """Show the command arguments dialog""" arguments = get_list_from_string_list(default_command) for argument in arguments: self.model_arguments.append((argument,)) self.ui.dialog_arguments.run() self.ui.dialog_arguments.hide() arguments = [] for treeiter in self.model_arguments: arguments.append(self.model_arguments[treeiter.path][0]) self.arguments = json.dumps(arguments) def destroy(self): """Destroy the command arguments dialog""" settings.positions.save_window_position(self.ui.dialog_arguments, SECTION_WINDOW_NAME) self.ui.dialog_arguments.destroy() self.ui.dialog_arguments = None def on_action_add_activate(self, action): """Add a new argument""" dialog = UICommandArgument(self.ui.dialog_arguments) if dialog.show(default_value="") == Gtk.ResponseType.OK: self.model_arguments.append((dialog.argument,)) dialog.destroy() def on_action_edit_activate(self, action): """Edit the selected service""" selected_row = get_treeview_selected_row(self.ui.tvw_arguments) if selected_row: argument = self.model_arguments[selected_row][0] dialog = UICommandArgument(self.ui.dialog_arguments) if dialog.show(default_value=argument) == Gtk.ResponseType.OK: # Update values self.model_arguments.set_value(selected_row, 0, dialog.argument) dialog.destroy() def on_action_remove_activate(self, action): """Remove the selected service""" selected_row = get_treeview_selected_row(self.ui.tvw_arguments) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_arguments, message_type=Gtk.MessageType.WARNING, title=None, msg1=_("Remove argument"), msg2=_("Remove the selected argument?"), is_response_id=Gtk.ResponseType.YES, ): self.model_arguments.remove(selected_row) def on_tvw_arguments_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" self.ui.action_edit.activate()
class UIServiceAssociation(object): def __init__(self, parent, destinations): """Prepare the service association dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('service_association.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_association.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position( self.ui.dialog_association, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Load destinations self.destinations = destinations self.ui.cbo_destinations.set_model(self.destinations.model) # Load services self.services = ModelServices(self.ui.store_services) self.services.load(model_services.services) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) self.service_arguments_widgets = {} def show(self, default_description, default_destination, default_service, default_arguments): """Show the Service association dialog""" # Set default description self.ui.entry_description.set_text(default_description) # Set default destination if default_destination: self.ui.cbo_destinations.set_active_id(default_destination) elif self.destinations.count() > 0: self.ui.cbo_destinations.set_active(0) # Set default service if default_service: self.ui.cbo_services.set_active_id(default_service) elif self.services.count() > 0: self.ui.cbo_services.set_active(0) # Set default arguments for argument in self.service_arguments_widgets: (new_label, new_entry) = self.service_arguments_widgets[argument] if argument in default_arguments: new_entry.set_text(default_arguments[argument]) # Show the dialog response = self.ui.dialog_association.run() self.ui.dialog_association.hide() self.destination = self.ui.cbo_destinations.get_active_id() self.service = self.ui.cbo_services.get_active_id() self.description = self.ui.entry_description.get_text() # Prepares argument values self.arguments = {} for argument in self.service_arguments_widgets: (new_label, new_entry) = self.service_arguments_widgets[argument] self.arguments[argument] = new_entry.get_text() return response def destroy(self): """Destroy the Service association dialog""" settings.positions.save_window_position( self.ui.dialog_association, SECTION_WINDOW_NAME) self.ui.dialog_association.destroy() self.ui.dialog_association = None def on_cbo_services_changed(self, widget): """Update the service arguments widgets""" treeiter = self.ui.cbo_services.get_active_iter() # Remove the previously added arguments widgets for argument in self.service_arguments_widgets: (new_label, new_entry) = self.service_arguments_widgets[argument] new_label.destroy() new_entry.destroy() self.service_arguments_widgets = {} # Collect the needed arguments command = get_list_from_string_list( self.services.get_command(treeiter)) row_number = 0 processed_arguments = [] # The argument address, already has a default widget processed_arguments.append('address') for option in command: arguments = get_string_fields(option) # Add a pair of widgets for each argument for argument in arguments: # Skip existing arguments if argument in processed_arguments: continue row_number += 1 processed_arguments.append(argument) # Add a new descriptive label for the argument new_label = Gtk.Label('%s:' % argument.title()) new_label.set_xalign(1.0) new_label.set_visible(True) self.ui.grid_service_arguments.attach(child=new_label, left=0, top=row_number, width=1, height=1) # Add a new entry for the argument value new_entry = Gtk.Entry() new_entry.set_visible(True) new_entry.set_hexpand(True) self.ui.grid_service_arguments.attach(child=new_entry, left=1, top=row_number, width=1, height=1) # Save a tuple of widgets, to remove later self.service_arguments_widgets[argument] = ( new_label, new_entry) def on_cbo_destinations_changed(self, widget): """Update the address entry for the selected destination""" treeiter = self.ui.cbo_destinations.get_active_iter() self.ui.entry_service_arguments_address.set_text( self.destinations.get_value(treeiter))
class UIHost(object): def __init__(self, parent, hosts): """Prepare the host dialog""" self.hosts = hosts # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('host.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_host.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position( self.ui.dialog_host, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the destinations self.model_destinations = ModelDestinations(self.ui.store_destinations) self.model_associations = ModelAssociations(self.ui.store_associations) self.selected_iter = None # Sort the data in the models self.model_destinations.model.set_sort_column_id( self.ui.column_destinations_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_name, default_description, title, treeiter): """Show the destinations dialog""" self.ui.txt_name.set_text(default_name) self.ui.txt_description.set_text(default_description) self.ui.txt_name.grab_focus() self.ui.dialog_host.set_title(title) self.selected_iter = treeiter self.model_associations.model.set_sort_column_id( self.ui.column_associations_destination.get_sort_column_id(), Gtk.SortType.ASCENDING) # Show the dialog response = self.ui.dialog_host.run() self.ui.dialog_host.hide() self.name = self.ui.txt_name.get_text().strip() self.description = self.ui.txt_description.get_text().strip() return response def destroy(self): """Destroy the destinations dialog""" settings.positions.save_window_position( self.ui.dialog_host, SECTION_WINDOW_NAME) self.ui.dialog_host.destroy() self.ui.dialog_host = None def on_action_destinations_add_activate(self, action): """Add a new destination""" dialog = UIDestination(self.ui.dialog_host, self.model_destinations) if dialog.show(default_name='', default_value='', title=_('Add new destination'), treeiter=None) == Gtk.ResponseType.OK: self.model_destinations.add_data( DestinationInfo(name=dialog.name, value=dialog.value)) # Get the new destinations list, clear and store the list again dialog.destroy() def on_action_destinations_edit_activate(self, action): """Edit the selected destination""" selected_row = get_treeview_selected_row(self.ui.tvw_destinations) if selected_row: name = self.model_destinations.get_key(selected_row) value = self.model_destinations.get_value(selected_row) selected_iter = self.model_destinations.get_iter(name) dialog = UIDestination(self.ui.dialog_host, self.model_destinations) if dialog.show(default_name=name, default_value=value, title=_('Edit destination'), treeiter=selected_iter ) == Gtk.ResponseType.OK: # Update values self.model_destinations.set_data( selected_iter, DestinationInfo(name=dialog.name, value=dialog.value)) dialog.destroy() def on_action_destinations_remove_activate(self, action): """Remove the selected destination""" selected_row = get_treeview_selected_row(self.ui.tvw_destinations) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_host, message_type=Gtk.MessageType.QUESTION, title=None, msg1=_("Remove destination"), msg2=_("Remove the selected destination?"), is_response_id=Gtk.ResponseType.YES): self.model_destinations.remove(selected_row) def on_tvw_destinations_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" self.ui.action_destinations_edit.activate() def on_action_confirm_activate(self, action): """Check che host configuration before confirm""" def show_error_message_on_infobar(widget, error_msg): """Show the error message on the GtkInfoBar""" set_error_message_on_infobar( widget=widget, widgets=(self.ui.txt_name, self.ui.txt_description), label=self.ui.lbl_error_message, infobar=self.ui.infobar_error_message, error_msg=error_msg) name = self.ui.txt_name.get_text().strip() description = self.ui.txt_description.get_text().strip() if len(name) == 0: # Show error for missing host name show_error_message_on_infobar( self.ui.txt_name, _('The host name is missing')) elif '\'' in name or '\\' in name or '/' in name: # Show error for invalid host name show_error_message_on_infobar( self.ui.txt_name, _('The host name is invalid')) elif self.hosts.get_iter(name) not in (None, self.selected_iter): # Show error for existing host name show_error_message_on_infobar( self.ui.txt_name, _('A host with that name already exists')) elif len(description) == 0: # Show error for missing host description show_error_message_on_infobar( self.ui.txt_description, _('The host description is missing')) else: self.ui.dialog_host.response(Gtk.ResponseType.OK) def on_infobar_error_message_response(self, widget, response_id): """Close the infobar""" if response_id == Gtk.ResponseType.CLOSE: self.ui.infobar_error_message.set_visible(False) def on_txt_name_changed(self, widget): """Check the host name field""" check_invalid_input(widget, False, False, False) def on_txt_description_changed(self, widget): """Check the host description field""" check_invalid_input(widget, False, True, True) def on_notebook_switch_page(self, widget, children, number): """Disable GtkActionGroup on page change""" self.ui.actions_destinations.set_sensitive(number == 0) self.ui.actions_associations.set_sensitive(number != 0) def on_action_tab_page_activate(self, action): """Switch GtkNotebook page on GtkAction activation""" self.ui.notebook.set_current_page( 0 if action is self.ui.action_tab_page1 else 1) def on_action_associations_add_activate(self, action): """Add a new service association""" dialog = UIServiceAssociation(self.ui.dialog_host, self.model_destinations) if dialog.show('', None, None) == Gtk.ResponseType.OK: self.model_associations.add_data( self.model_associations.count() + 1, dialog.destination, dialog.description, model_services.services[dialog.service], dialog.arguments) dialog.destroy() def on_action_associations_remove_activate(self, action): """Remove the selected destination""" selected_row = get_treeview_selected_row(self.ui.tvw_associations) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_host, message_type=Gtk.MessageType.QUESTION, title=None, msg1=_("Remove association"), msg2=_("Remove the selected association?"), is_response_id=Gtk.ResponseType.YES): self.model_associations.remove(selected_row) def on_action_associations_edit_activate(self, action): """Edit the selected service association""" selected_row = get_treeview_selected_row(self.ui.tvw_associations) if selected_row: dialog = UIServiceAssociation(self.ui.dialog_host, self.model_destinations) model = self.model_associations selected_key = model.get_key(selected_row) selected_iter = model.get_iter(selected_key) if dialog.show( model.get_description(selected_row), model.get_destination_name(selected_row), model.get_service_name(selected_row), json.loads(model.get_arguments(selected_row)) ) == Gtk.ResponseType.OK: model.set_data( treeiter=selected_iter, description=dialog.description, destination_name=dialog.destination, service=model_services.services[dialog.service], arguments=dialog.arguments) dialog.destroy() def on_tvw_associations_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" self.ui.action_associations_edit.activate()
class UIMain(object): def __init__(self, application): self.application = application # Load settings settings.settings = settings.Settings(FILE_SETTINGS, False) settings.positions = settings.Settings(FILE_WINDOWS_POSITION, False) settings.services = settings.Settings(FILE_SERVICES, False) preferences.preferences = preferences.Preferences() # Load services for key in settings.services.get_sections(): model_services.services[key] = ServiceInfo( name=key, description=settings.services.get(key, OPTION_SERVICE_DESCRIPTION), command=settings.services.get(key, OPTION_SERVICE_COMMAND), terminal=settings.services.get_boolean( key, OPTION_SERVICE_TERMINAL), icon=settings.services.get(key, OPTION_SERVICE_ICON)) self.loadUI() self.model_hosts = ModelHosts(self.ui.store_hosts) self.model_groups = ModelGroups(self.ui.store_groups) # Prepare the debug dialog debug.debug = debug.UIDebug(self.ui.win_main, self.on_window_debug_delete_event) # Prepare the processes dialog processes.processes = processes.UIProcesses( self.ui.win_main, self.on_window_processes_delete_event) # Load the groups and hosts list self.hosts = {} self.reload_groups() # Sort the data in the models self.model_groups.model.set_sort_column_id( self.ui.column_group.get_sort_column_id(), Gtk.SortType.ASCENDING) self.model_hosts.model.set_sort_column_id( self.ui.column_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Automatically select the first host if any self.ui.tvw_groups.set_cursor(0) if self.model_hosts.count() > 0: self.ui.tvw_connections.set_cursor(0) # Restore the saved size and position settings.positions.restore_window_position(self.ui.win_main, SECTION_WINDOW_NAME) def loadUI(self): """Load the interface UI""" self.ui = GtkBuilderLoader(get_ui_file('main.glade')) self.ui.win_main.set_application(self.application) self.ui.win_main.set_title(APP_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.ToolButton): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Set list items row height icon_size = preferences.ICON_SIZE self.ui.cell_name.props.height = preferences.get(icon_size) self.ui.cell_group_name.props.height = preferences.get(icon_size) # Set groups visibility self.ui.scroll_groups.set_visible( preferences.get(preferences.GROUPS_SHOW)) # Add a Gtk.Headerbar, only for GTK+ 3.10.0 and higher if (not Gtk.check_version(3, 10, 0) and not preferences.get(preferences.HEADERBARS_DISABLE)): self.load_ui_headerbar() if preferences.get(preferences.HEADERBARS_REMOVE_TOOLBAR): # This is only for development, it should always be True # Remove the redundant toolbar self.ui.toolbar_main.destroy() # Flatten the Gtk.ScrolledWindows self.ui.scroll_groups.set_shadow_type(Gtk.ShadowType.NONE) self.ui.scroll_connections.set_shadow_type(Gtk.ShadowType.NONE) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def load_ui_headerbar(self): """Add a Gtk.HeaderBar to the window with buttons""" def create_button_from_action(action): """Create a new Gtk.Button from a Gtk.Action""" if isinstance(action, Gtk.ToggleAction): new_button = Gtk.ToggleButton() else: new_button = Gtk.Button() new_button.set_use_action_appearance(False) new_button.set_related_action(action) # Use icon from the action icon_name = action.get_icon_name() if preferences.get(preferences.HEADERBARS_SYMBOLIC_ICONS): icon_name += '-symbolic' # Get desired icon size icon_size = (Gtk.IconSize.BUTTON if preferences.get(preferences.HEADERBARS_SMALL_ICONS) else Gtk.IconSize.LARGE_TOOLBAR) new_button.set_image( Gtk.Image.new_from_icon_name(icon_name, icon_size)) # Set the tooltip from the action label new_button.set_tooltip_text(action.get_label().replace('_', '')) return new_button # Add the Gtk.HeaderBar header_bar = Gtk.HeaderBar() header_bar.props.title = self.ui.win_main.get_title() header_bar.set_show_close_button(True) self.ui.win_main.set_titlebar(header_bar) # Add buttons to the left side for action in (self.ui.action_new, self.ui.action_edit, self.ui.action_copy, self.ui.action_connect, self.ui.action_delete): header_bar.pack_start(create_button_from_action(action)) # Add buttons to the right side (in reverse order) for action in reversed((self.ui.action_services, self.ui.action_groups, self.ui.action_debug, self.ui.action_processes, self.ui.action_about)): header_bar.pack_end(create_button_from_action(action)) def run(self): """Show the UI""" self.ui.win_main.show_all() def on_win_main_delete_event(self, widget, event): """Save the settings and close the application""" debug.debug.destroy() processes.processes.destroy() settings.positions.save_window_position(self.ui.win_main, SECTION_WINDOW_NAME) settings.positions.save() settings.services.save() settings.settings.save() self.application.quit() def on_action_about_activate(self, action): """Show the about dialog""" dialog = UIAbout(self.ui.win_main) dialog.show() dialog.destroy() def on_action_quit_activate(self, action): """Close the application by closing the main window""" event = Gdk.Event() event.key.type = Gdk.EventType.DELETE self.ui.win_main.event(event) def on_action_services_activate(self, action): """Edit services""" selected_row = get_treeview_selected_row(self.ui.tvw_connections) if selected_row: iter_parent = self.ui.store_hosts.iter_parent(selected_row) selected_path = self.model_hosts.model[selected_row].path # Get the path of the host if iter_parent is None: tree_path = self.model_hosts.model[selected_row].path else: tree_path = self.model_hosts.model[iter_parent].path expanded = self.ui.tvw_connections.row_expanded(tree_path) dialog_services = UIServices(parent=self.ui.win_main) # Load services list dialog_services.model.load(model_services.services) dialog_services.show() # Get the new services list, clear and store the list again model_services.services = dialog_services.model.dump() dialog_services.destroy() settings.services.clear() for key in model_services.services.iterkeys(): settings.services.set( section=key, option=OPTION_SERVICE_DESCRIPTION, value=model_services.services[key].description) settings.services.set(section=key, option=OPTION_SERVICE_COMMAND, value=model_services.services[key].command) settings.services.set_boolean( section=key, option=OPTION_SERVICE_TERMINAL, value=model_services.services[key].terminal) settings.services.set(section=key, option=OPTION_SERVICE_ICON, value=model_services.services[key].icon) self.reload_hosts() if selected_row: # Automatically expand the row if it was expanded before if expanded: self.ui.tvw_connections.expand_row(tree_path, True) # Automatically select again the previously selected row self.ui.tvw_connections.set_cursor(path=selected_path, column=None, start_editing=False) def reload_hosts(self): """Load hosts from the settings files""" self.model_hosts.clear() self.hosts.clear() hosts_path = self.get_current_group_path() # Fix bug where the groups model isn't yet emptied, resulting in # being still used after a clear, then an invalid path if not os.path.isdir(hosts_path): return for filename in os.listdir(hosts_path): # Skip folders, used for groups if os.path.isdir(os.path.join(hosts_path, filename)): continue debug.add_info('Loading host %s' % os.path.join(hosts_path, filename)) settings_host = settings.Settings(filename=os.path.join( hosts_path, filename), case_sensitive=True) name = settings_host.get(SECTION_HOST, OPTION_HOST_NAME) description = settings_host.get(SECTION_HOST, OPTION_HOST_DESCRIPTION) host = HostInfo(name=name, description=description) destinations = {} # Load host destinations if SECTION_DESTINATIONS in settings_host.get_sections(): for option in settings_host.get_options(SECTION_DESTINATIONS): value = settings_host.get(SECTION_DESTINATIONS, option) destinations[option] = DestinationInfo(name=option, value=value) # Load associations association_index = 1 associations_count = settings_host.get_int( section=SECTION_HOST, option=OPTION_HOST_ASSOCIATIONS) while association_index <= associations_count: section = '%s %d' % (SECTION_ASSOCIATION, association_index) host.add_association( description=settings_host.get( section=section, option=OPTION_ASSOCIATION_DESCRIPTION), destination_name=settings_host.get( section=section, option=OPTION_ASSOCIATION_DESTINATION), service_name=settings_host.get( section=section, option=OPTION_ASSOCIATION_SERVICE), arguments=json.loads( settings_host.get( section=section, option=OPTION_ASSOCIATION_ARGUMENTS))) association_index += 1 self.add_host(host, destinations, False) def add_host(self, host, destinations, update_settings): """Add a new host along as with its destinations""" # Add the host to the data and to the model self.hosts[host.name] = host treeiter = self.model_hosts.add_data(host) # Add the destinations to the data for destination_name in destinations: destination = destinations[destination_name] host.add_destination(item=destination) # Add service associations to the model for association in host.associations: description = association.description service_name = association.service_name service_arguments = json.dumps(association.service_arguments) destination = destinations[association.destination_name] if service_name in model_services.services: service = model_services.services[service_name] self.model_hosts.add_association(treeiter=treeiter, description=description, destination=destination, service=service, arguments=service_arguments) else: debug.add_warning('service %s not found' % service_name) # Update settings file if requested if update_settings: hosts_path = self.get_current_group_path() settings_host = settings.Settings(filename=os.path.join( hosts_path, '%s.conf' % host.name), case_sensitive=True) # Add host information settings_host.set(SECTION_HOST, OPTION_HOST_NAME, host.name) settings_host.set(SECTION_HOST, OPTION_HOST_DESCRIPTION, host.description) # Add destinations for key in host.destinations: destination = host.destinations[key] settings_host.set(section=SECTION_DESTINATIONS, option=destination.name, value=destination.value) association_index = 0 for association in host.associations: arguments = json.dumps(association.service_arguments) # Add associations to the settings association_index += 1 section = '%s %d' % (SECTION_ASSOCIATION, association_index) settings_host.set(section=section, option=OPTION_ASSOCIATION_DESCRIPTION, value=association.description) settings_host.set(section=section, option=OPTION_ASSOCIATION_DESTINATION, value=association.destination_name) settings_host.set(section=section, option=OPTION_ASSOCIATION_SERVICE, value=association.service_name) settings_host.set(section=section, option=OPTION_ASSOCIATION_ARGUMENTS, value=arguments) settings_host.set_int(section=SECTION_HOST, option=OPTION_HOST_ASSOCIATIONS, value=association_index) # Save the settings to the file settings_host.save() def remove_host(self, name): """Remove a host by its name""" hosts_path = self.get_current_group_path() filename = os.path.join(hosts_path, '%s.conf' % name) if os.path.isfile(filename): os.unlink(filename) self.hosts.pop(name) self.model_hosts.remove(self.model_hosts.get_iter(name)) def reload_groups(self): """Load groups from hosts folder""" self.model_groups.clear() # Always add a default group self.model_groups.add_data(GroupInfo('', _('Default group'))) for filename in os.listdir(DIR_HOSTS): if os.path.isdir(os.path.join(DIR_HOSTS, filename)): # For each folder add a new group self.model_groups.add_data(GroupInfo(filename, filename)) def on_action_new_activate(self, action): """Define a new host""" dialog = UIHost(parent=self.ui.win_main, hosts=self.model_hosts) response = dialog.show(default_name='', default_description='', title=_('Add a new host'), treeiter=None) if response == Gtk.ResponseType.OK: destinations = dialog.model_destinations.dump() associations = dialog.model_associations.dump() host = HostInfo(dialog.name, dialog.description) # Set the associations for values in associations: (destination_name, description, service_name, service_arguments) = associations[values] destination = destinations[destination_name] arguments = json.loads(service_arguments) host.add_association(description=description, destination_name=destination_name, service_name=service_name, arguments=arguments) self.add_host(host=host, destinations=destinations, update_settings=True) # Automatically select the newly added host self.ui.tvw_connections.set_cursor( path=self.model_hosts.get_path_by_name(dialog.name), column=None, start_editing=False) dialog.destroy() def on_action_edit_activate(self, action): """Define a new host""" selected_row = get_treeview_selected_row(self.ui.tvw_connections) if selected_row: if self.is_selected_row_host(): # First level (host) name = self.model_hosts.get_key(selected_row) description = self.model_hosts.get_description(selected_row) selected_iter = self.model_hosts.get_iter(name) expanded = self.ui.tvw_connections.row_expanded( self.model_hosts.get_path(selected_iter)) dialog = UIHost(parent=self.ui.win_main, hosts=self.model_hosts) # Restore the destinations for the selected host destinations = self.hosts[name].destinations for destination_name in destinations: destination = destinations[destination_name] dialog.model_destinations.add_data(destination) # Restore the associations for the selected host for association in self.hosts[name].associations: service_name = association.service_name if service_name in model_services.services: dialog.model_associations.add_data( index=dialog.model_associations.count(), name=association.destination_name, description=association.description, service=model_services.services[service_name], arguments=association.service_arguments) else: debug.add_warning('service %s not found' % service_name) # Show the edit host dialog response = dialog.show(default_name=name, default_description=description, title=_('Edit host'), treeiter=selected_iter) if response == Gtk.ResponseType.OK: # Remove older host and add the newer destinations = dialog.model_destinations.dump() associations = dialog.model_associations.dump() host = HostInfo(dialog.name, dialog.description) # Set the associations for values in associations: (destination_name, description, service_name, service_arguments) = associations[values] destination = destinations[destination_name] arguments = json.loads(service_arguments) host.add_association(description=description, destination_name=destination_name, service_name=service_name, arguments=arguments) self.remove_host(name) self.add_host(host=host, destinations=destinations, update_settings=True) # Get the path of the host tree_path = self.model_hosts.get_path_by_name(dialog.name) # Automatically select again the previously selected host self.ui.tvw_connections.set_cursor(path=tree_path, column=None, start_editing=False) # Automatically expand the row if it was expanded before if expanded: self.ui.tvw_connections.expand_row(tree_path, False) def on_tvw_connections_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" selected_row = get_treeview_selected_row(self.ui.tvw_connections) if selected_row and self.is_selected_row_host(): # Start host edit self.ui.action_edit.activate() else: # Connect to the destination self.ui.action_connect.activate() def on_action_delete_activate(self, action): """Remove the selected host""" selected_row = get_treeview_selected_row(self.ui.tvw_connections) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.win_main, message_type=Gtk.MessageType.QUESTION, title=None, msg1=_("Remove host"), msg2=_("Remove the selected host?"), is_response_id=Gtk.ResponseType.YES): self.remove_host(self.model_hosts.get_key(selected_row)) def on_action_copy_activate(self, action): """Copy the selected host to another""" selected_row = get_treeview_selected_row(self.ui.tvw_connections) if selected_row: if self.is_selected_row_host(): # First level (host) name = self.model_hosts.get_key(selected_row) description = self.model_hosts.get_description(selected_row) selected_iter = self.model_hosts.get_iter(name) expanded = self.ui.tvw_connections.row_expanded( self.model_hosts.get_path(selected_iter)) dialog = UIHost(parent=self.ui.win_main, hosts=self.model_hosts) # Restore the destinations for the selected host destinations = self.hosts[name].destinations for destination_name in destinations: destination = destinations[destination_name] dialog.model_destinations.add_data(destination) # Restore the associations for the selected host for association in self.hosts[name].associations: service_name = association.service_name if service_name in model_services.services: dialog.model_associations.add_data( index=dialog.model_associations.count(), name=association.destination_name, description=association.description, service=model_services.services[service_name], arguments=association.service_arguments) else: debug.add_warning('service %s not found' % service_name) # Show the edit host dialog response = dialog.show(default_name=_('Copy of %s') % name, default_description='', title=_('Copy host'), treeiter=None) if response == Gtk.ResponseType.OK: destinations = dialog.model_destinations.dump() associations = dialog.model_associations.dump() host = HostInfo(dialog.name, dialog.description) # Set the associations for values in associations: (destination_name, description, service_name, service_arguments) = associations[values] destination = destinations[destination_name] arguments = json.loads(service_arguments) host.add_association(description=description, destination_name=destination_name, service_name=service_name, arguments=arguments) self.add_host(host=host, destinations=destinations, update_settings=True) # Get the path of the host tree_path = self.model_hosts.get_path_by_name(dialog.name) # Automatically select again the previously selected host self.ui.tvw_connections.set_cursor(path=tree_path, column=None, start_editing=False) # Automatically expand the row if it was expanded before if expanded: self.ui.tvw_connections.expand_row(tree_path, False) # Collapse the duplicated row self.ui.tvw_connections.collapse_row( self.model_hosts.get_path(selected_iter)) def on_tvw_connections_cursor_changed(self, widget): """Set actions sensitiveness for host and connection""" if get_treeview_selected_row(self.ui.tvw_connections): self.ui.actions_connection.set_sensitive( not self.is_selected_row_host()) self.ui.actions_host.set_sensitive(self.is_selected_row_host()) def on_action_debug_toggled(self, action): """Show and hide the debug window""" if self.ui.action_debug.get_active(): debug.debug.show() else: debug.debug.hide() def on_window_debug_delete_event(self, widget, event): """Catch the delete_event in the debug window to hide the window""" self.ui.action_debug.set_active(False) return True def on_action_processes_toggled(self, action): """Show and hide the processes window""" if self.ui.action_processes.get_active(): processes.processes.show() else: processes.processes.hide() def on_window_processes_delete_event(self, widget, event): """Catch the delete_event in the processes window to hide the window""" self.ui.action_processes.set_active(False) return True def on_tvw_connections_key_press_event(self, widget, event): """Expand and collapse nodes with keyboard arrows""" if event.keyval in (Gdk.KEY_Left, Gdk.KEY_Right): # Collapse or expand the selected row using <Left> and <Right> selected_row = get_treeview_selected_row(self.ui.tvw_connections) if (selected_row and self.is_selected_row_host()): if event.keyval == Gdk.KEY_Left: self.ui.action_host_collapse.activate() elif event.keyval == Gdk.KEY_Right: self.ui.action_host_expand.activate() return True def on_action_connect_activate(self, action): """Establish the connection for the destination""" selected_row = get_treeview_selected_row(self.ui.tvw_connections) if selected_row and not self.is_selected_row_host(): host = self.hosts[self.model_hosts.get_key( self.ui.store_hosts.iter_parent(selected_row))] destination_name = self.model_hosts.get_key(selected_row) destination = host.destinations[destination_name] description = self.model_hosts.get_association(selected_row) service_name = self.model_hosts.get_service(selected_row) service_arguments = self.model_hosts.get_arguments(selected_row) arguments = json.loads(service_arguments) association = host.find_association(description=description, destination=destination.name, service=service_name, arguments=arguments) if service_name in model_services.services: service = model_services.services[service_name] command = service.command # Prepares the arguments arguments_map = {} arguments_map['address'] = destination.value for key in association.service_arguments: arguments_map[key] = association.service_arguments[key] # Execute command try: command = command.format(**arguments_map) processes.processes.add_process(host, destination, service, command) except KeyError as error: # An error occurred processing the command error_msg1 = _('Connection open failed') error_msg2 = _('An error occurred processing the ' 'service command.') show_message_dialog(class_=UIMessageDialogClose, parent=self.ui.win_main, message_type=Gtk.MessageType.ERROR, title=None, msg1=error_msg1, msg2=error_msg2, is_response_id=None) debug.add_error(error_msg2) debug.add_error('Host: "%s"' % host.name) debug.add_error('Destination name: "%s"' % destination.name) debug.add_error('Destination value: "%s"' % destination.value) debug.add_error('Service: %s' % service.name), debug.add_error('Command: "%s"' % command) else: debug.add_warning('service %s not found' % service_name) def is_selected_row_host(self): """Return if the currently selected row is an host""" return self.ui.store_hosts.iter_parent( get_treeview_selected_row(self.ui.tvw_connections)) is None def get_current_group_path(self): """Return the path of the currently selected group""" selected_row = get_treeview_selected_row(self.ui.tvw_groups) group_name = self.model_groups.get_key(selected_row) if selected_row \ else '' return os.path.join(DIR_HOSTS, group_name) if group_name else DIR_HOSTS def on_tvw_groups_cursor_changed(self, widget): """Set actions sensitiveness for host and connection""" if get_treeview_selected_row(self.ui.tvw_groups): self.reload_hosts() # Automatically select the first host for the group self.ui.tvw_connections.set_cursor(0) def on_action_groups_activate(self, widget): """Edit groups""" dialog_groups = UIGroups(parent=self.ui.win_main) dialog_groups.model = self.model_groups dialog_groups.ui.tvw_groups.set_model(self.model_groups.model) dialog_groups.show() dialog_groups.destroy() def on_tvw_groups_button_release_event(self, widget, event): """Show groups popup menu on right click""" if event.button == Gdk.BUTTON_SECONDARY: show_popup_menu(self.ui.menu_groups, event.button) def on_tvw_connections_button_release_event(self, widget, event): """Show connections popup menu on right click""" if event.button == Gdk.BUTTON_SECONDARY: show_popup_menu(self.ui.menu_connections, event.button) def on_action_group_previous_activate(self, action): """Move to the previous group""" selected_row = get_treeview_selected_row(self.ui.tvw_groups) new_iter = self.model_groups.model.iter_previous(selected_row) if new_iter: # Select the newly selected row in the groups list new_path = self.model_groups.get_path(new_iter) self.ui.tvw_groups.set_cursor(new_path) def on_action_group_next_activate(self, action): """Move to the next group""" selected_row = get_treeview_selected_row(self.ui.tvw_groups) new_iter = self.model_groups.model.iter_next(selected_row) if new_iter: # Select the newly selected row in the groups list new_path = self.model_groups.get_path(new_iter) self.ui.tvw_groups.set_cursor(new_path) def on_action_host_collapse_activate(self, action): """Collapse the selected host and hide the associations""" selected_row = get_treeview_selected_row(self.ui.tvw_connections) if (selected_row and self.is_selected_row_host()): tree_path = self.model_hosts.get_path(selected_row) if self.ui.tvw_connections.row_expanded(tree_path): self.ui.tvw_connections.collapse_row(tree_path) def on_action_host_expand_activate(self, action): """Expand the selected host and show the associations""" selected_row = get_treeview_selected_row(self.ui.tvw_connections) if (selected_row and self.is_selected_row_host()): tree_path = self.model_hosts.get_path(selected_row) if not self.ui.tvw_connections.row_expanded(tree_path): self.ui.tvw_connections.expand_row(tree_path, False)
class UIHost(object): def __init__(self, parent, hosts): """Prepare the host dialog""" self.hosts = hosts # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('host.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_host.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_host, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the destinations self.model_destinations = ModelDestinations(self.ui.store_destinations) self.model_associations = ModelAssociations(self.ui.store_associations) self.selected_iter = None # Sort the data in the models self.model_destinations.model.set_sort_column_id( self.ui.column_destinations_name.get_sort_column_id(), Gtk.SortType.ASCENDING) # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_name, default_description, title, treeiter): """Show the destinations dialog""" self.ui.txt_name.set_text(default_name) self.ui.txt_description.set_text(default_description) self.ui.txt_name.grab_focus() self.ui.dialog_host.set_title(title) self.selected_iter = treeiter self.model_associations.model.set_sort_column_id( self.ui.column_associations_destination.get_sort_column_id(), Gtk.SortType.ASCENDING) # Show the dialog response = self.ui.dialog_host.run() self.ui.dialog_host.hide() self.name = self.ui.txt_name.get_text().strip() self.description = self.ui.txt_description.get_text().strip() return response def destroy(self): """Destroy the destinations dialog""" settings.positions.save_window_position(self.ui.dialog_host, SECTION_WINDOW_NAME) self.ui.dialog_host.destroy() self.ui.dialog_host = None def on_action_destinations_add_activate(self, action): """Add a new destination""" dialog = UIDestination(self.ui.dialog_host, self.model_destinations) if dialog.show(default_name='', default_value='', title=_('Add new destination'), treeiter=None) == Gtk.ResponseType.OK: self.model_destinations.add_data( DestinationInfo(name=dialog.name, value=dialog.value)) # Get the new destinations list, clear and store the list again dialog.destroy() def on_action_destinations_edit_activate(self, action): """Edit the selected destination""" selected_row = get_treeview_selected_row(self.ui.tvw_destinations) if selected_row: name = self.model_destinations.get_key(selected_row) value = self.model_destinations.get_value(selected_row) selected_iter = self.model_destinations.get_iter(name) dialog = UIDestination(self.ui.dialog_host, self.model_destinations) if dialog.show(default_name=name, default_value=value, title=_('Edit destination'), treeiter=selected_iter) == Gtk.ResponseType.OK: # Update values self.model_destinations.set_data( selected_iter, DestinationInfo(name=dialog.name, value=dialog.value)) dialog.destroy() def on_action_destinations_remove_activate(self, action): """Remove the selected destination""" selected_row = get_treeview_selected_row(self.ui.tvw_destinations) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_host, message_type=Gtk.MessageType.QUESTION, title=None, msg1=_("Remove destination"), msg2=_("Remove the selected destination?"), is_response_id=Gtk.ResponseType.YES): self.model_destinations.remove(selected_row) def on_tvw_destinations_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" self.ui.action_destinations_edit.activate() def on_action_confirm_activate(self, action): """Check che host configuration before confirm""" def show_error_message_on_infobar(widget, error_msg): """Show the error message on the GtkInfoBar""" set_error_message_on_infobar(widget=widget, widgets=(self.ui.txt_name, self.ui.txt_description), label=self.ui.lbl_error_message, infobar=self.ui.infobar_error_message, error_msg=error_msg) name = self.ui.txt_name.get_text().strip() description = self.ui.txt_description.get_text().strip() if len(name) == 0: # Show error for missing host name show_error_message_on_infobar(self.ui.txt_name, _('The host name is missing')) elif '\'' in name or '\\' in name or '/' in name: # Show error for invalid host name show_error_message_on_infobar(self.ui.txt_name, _('The host name is invalid')) elif self.hosts.get_iter(name) not in (None, self.selected_iter): # Show error for existing host name show_error_message_on_infobar( self.ui.txt_name, _('A host with that name already exists')) elif len(description) == 0: # Show error for missing host description show_error_message_on_infobar(self.ui.txt_description, _('The host description is missing')) else: self.ui.dialog_host.response(Gtk.ResponseType.OK) def on_infobar_error_message_response(self, widget, response_id): """Close the infobar""" if response_id == Gtk.ResponseType.CLOSE: self.ui.infobar_error_message.set_visible(False) def on_txt_name_changed(self, widget): """Check the host name field""" check_invalid_input(widget, False, False, False) def on_txt_description_changed(self, widget): """Check the host description field""" check_invalid_input(widget, False, True, True) def on_notebook_switch_page(self, widget, children, number): """Disable GtkActionGroup on page change""" self.ui.actions_destinations.set_sensitive(number == 0) self.ui.actions_associations.set_sensitive(number != 0) def on_action_tab_page_activate(self, action): """Switch GtkNotebook page on GtkAction activation""" self.ui.notebook.set_current_page( 0 if action is self.ui.action_tab_page1 else 1) def on_action_associations_add_activate(self, action): """Add a new service association""" dialog = UIServiceAssociation(self.ui.dialog_host, self.model_destinations) if dialog.show('', None, None) == Gtk.ResponseType.OK: self.model_associations.add_data( self.model_associations.count() + 1, dialog.destination, dialog.description, model_services.services[dialog.service], dialog.arguments) dialog.destroy() def on_action_associations_remove_activate(self, action): """Remove the selected destination""" selected_row = get_treeview_selected_row(self.ui.tvw_associations) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_host, message_type=Gtk.MessageType.QUESTION, title=None, msg1=_("Remove association"), msg2=_("Remove the selected association?"), is_response_id=Gtk.ResponseType.YES): self.model_associations.remove(selected_row) def on_action_associations_edit_activate(self, action): """Edit the selected service association""" selected_row = get_treeview_selected_row(self.ui.tvw_associations) if selected_row: dialog = UIServiceAssociation(self.ui.dialog_host, self.model_destinations) model = self.model_associations selected_key = model.get_key(selected_row) selected_iter = model.get_iter(selected_key) if dialog.show(model.get_description(selected_row), model.get_destination_name(selected_row), model.get_service_name(selected_row), json.loads(model.get_arguments( selected_row))) == Gtk.ResponseType.OK: model.set_data(treeiter=selected_iter, description=dialog.description, destination_name=dialog.destination, service=model_services.services[dialog.service], arguments=dialog.arguments) dialog.destroy() def on_tvw_associations_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" self.ui.action_associations_edit.activate()
class UICommandArguments(object): def __init__(self, parent): """Prepare the command arguments dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('command_arguments.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_arguments.set_transient_for(parent) # Restore the saved size and position settings.positions.restore_window_position(self.ui.dialog_arguments, SECTION_WINDOW_NAME) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) # Initialize column headers for widget in self.ui.get_objects_by_type(Gtk.TreeViewColumn): widget.set_title(text(widget.get_title())) # Load the arguments self.model_arguments = self.ui.store_arguments self.arguments = '[]' # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_command): """Show the command arguments dialog""" arguments = get_list_from_string_list(default_command) for argument in arguments: self.model_arguments.append((argument, )) self.ui.dialog_arguments.run() self.ui.dialog_arguments.hide() arguments = [] for treeiter in self.model_arguments: arguments.append(self.model_arguments[treeiter.path][0]) self.arguments = json.dumps(arguments) def destroy(self): """Destroy the command arguments dialog""" settings.positions.save_window_position(self.ui.dialog_arguments, SECTION_WINDOW_NAME) self.ui.dialog_arguments.destroy() self.ui.dialog_arguments = None def on_action_add_activate(self, action): """Add a new argument""" dialog = UICommandArgument(self.ui.dialog_arguments) if dialog.show(default_value='') == Gtk.ResponseType.OK: self.model_arguments.append((dialog.argument, )) dialog.destroy() def on_action_edit_activate(self, action): """Edit the selected service""" selected_row = get_treeview_selected_row(self.ui.tvw_arguments) if selected_row: argument = self.model_arguments[selected_row][0] dialog = UICommandArgument(self.ui.dialog_arguments) if dialog.show(default_value=argument) == Gtk.ResponseType.OK: # Update values self.model_arguments.set_value(selected_row, 0, dialog.argument) dialog.destroy() def on_action_remove_activate(self, action): """Remove the selected service""" selected_row = get_treeview_selected_row(self.ui.tvw_arguments) if selected_row and show_message_dialog( class_=UIMessageDialogNoYes, parent=self.ui.dialog_arguments, message_type=Gtk.MessageType.WARNING, title=None, msg1=_("Remove argument"), msg2=_("Remove the selected argument?"), is_response_id=Gtk.ResponseType.YES): self.model_arguments.remove(selected_row) def on_tvw_arguments_row_activated(self, widget, treepath, column): """Edit the selected row on activation""" self.ui.action_edit.activate()
class UIServiceDetail(object): def __init__(self, parent, services): """Prepare the services detail dialog""" # Load the user interface self.ui = GtkBuilderLoader(get_ui_file('service_detail.glade')) if not preferences.get(preferences.DETACHED_WINDOWS): self.ui.dialog_edit_service.set_transient_for(parent) # Initialize actions for widget in self.ui.get_objects_by_type(Gtk.Action): # Connect the actions accelerators widget.connect_accelerator() # Set labels widget.set_label(text(widget.get_label())) # Initialize labels for widget in self.ui.get_objects_by_type(Gtk.Label): widget.set_label(text(widget.get_label())) widget.set_tooltip_text(widget.get_label().replace('_', '')) # Initialize tooltips for widget in self.ui.get_objects_by_type(Gtk.Button): action = widget.get_related_action() if action: widget.set_tooltip_text(action.get_label().replace('_', '')) self.model = services self.selected_iter = None self.name = '' self.description = '' self.command = '[]' self.terminal = False self.icon = '' # Connect signals from the glade file to the module functions self.ui.connect_signals(self) def show(self, default_name, default_description, default_command, default_terminal, default_icon, title, treeiter): """Show the Services detail dialog""" self.ui.txt_name.set_text(default_name) self.ui.txt_description.set_text(default_description) self.ui.txt_command.set_text(default_command) self.ui.chk_terminal.set_active(default_terminal) self.ui.txt_icon.set_text(default_icon) self.ui.txt_name.grab_focus() self.ui.dialog_edit_service.set_title(title) self.selected_iter = treeiter response = self.ui.dialog_edit_service.run() self.ui.dialog_edit_service.hide() self.name = self.ui.txt_name.get_text().strip() self.description = self.ui.txt_description.get_text().strip() self.command = self.ui.txt_command.get_text().strip() self.terminal = self.ui.chk_terminal.get_active() self.icon = self.ui.txt_icon.get_text().strip() return response def destroy(self): """Destroy the Services dialog""" self.ui.dialog_edit_service.destroy() self.ui.dialog_edit_service = None def on_action_confirm_activate(self, action): """Check che service configuration before confirm""" def show_error_message_on_infobar(widget, error_msg): """Show the error message on the GtkInfoBar""" set_error_message_on_infobar( widget=widget, widgets=(self.ui.txt_name, self.ui.txt_description, self.ui.txt_command), label=self.ui.lbl_error_message, infobar=self.ui.infobar_error_message, error_msg=error_msg) name = self.ui.txt_name.get_text().strip() description = self.ui.txt_description.get_text().strip() command = self.ui.txt_command.get_text().strip() terminal = self.ui.chk_terminal.get_active() icon = self.ui.txt_icon.get_text().strip() if len(name) == 0: # Show error for missing service name show_error_message_on_infobar( self.ui.txt_name, _('The service name is missing')) elif '\'' in name or '\\' in name or '/' in name or ',' in name: # Show error for invalid service name show_error_message_on_infobar( self.ui.txt_name, _('The service name is invalid')) elif self.model.get_iter(name) not in (None, self.selected_iter): # Show error for existing service name show_error_message_on_infobar( self.ui.txt_name, _('A service with that name already exists')) elif len(description) == 0: # Show error for missing service description show_error_message_on_infobar( self.ui.txt_description, _('The service description is missing')) elif '\'' in description or '\\' in description: # Show error for invalid service description show_error_message_on_infobar( self.ui.txt_description, _('The service description is invalid')) elif len(command) == 0: # Show error for missing service description show_error_message_on_infobar( self.ui.txt_command, _('The service command is missing')) elif len(icon) > 0 and not os.path.isfile(icon): # Show error for missing service description show_error_message_on_infobar( self.ui.txt_icon, _('The service icon doesn''t exists')) else: self.ui.dialog_edit_service.response(Gtk.ResponseType.OK) def on_infobar_error_message_response(self, widget, response_id): """Close the infobar""" if response_id == Gtk.ResponseType.CLOSE: self.ui.infobar_error_message.set_visible(False) def on_txt_name_changed(self, widget): """Check the service name field""" check_invalid_input(widget, False, False, False) def on_txt_description_changed(self, widget): """Check the service description field""" check_invalid_input(widget, False, True, False) def on_txt_icon_changed(self, widget): """Check the icon field""" text = widget.get_text().strip() if len(text) > 0 and os.path.isfile(text): icon_size = preferences.get(preferences.ICON_SIZE) self.ui.image_icon.set_from_pixbuf( GdkPixbuf.Pixbuf.new_from_file_at_size(text, icon_size, icon_size)) icon_name = None else: icon_name = 'dialog-error' if len(text) > 0 else None self.ui.image_icon.set_from_icon_name('image-missing', Gtk.IconSize.LARGE_TOOLBAR) widget.set_icon_from_icon_name( Gtk.EntryIconPosition.SECONDARY, icon_name) def on_action_browse_icon_activate(self, action): """Browse for an icon file""" def update_preview_cb(widget, image, get_preview_filename, set_active): """Update preview by trying to load the image""" try: # Try to load the image from the previewed file image.set_from_pixbuf(GdkPixbuf.Pixbuf.new_from_file_at_size( get_preview_filename(), preferences.get(preferences.PREVIEW_SIZE), preferences.get(preferences.PREVIEW_SIZE))) set_active(True) except: # Hide the preview widget for errors image.set_from_pixbuf(None) set_active(False) # Prepare the browse for icon dialog dialog = UIFileChooserOpenFile(self.ui.dialog_edit_service, text("Select a File")) dialog.add_filter(_("All Image Files"), "image/*", None) dialog.add_filter(_("All Files"), None, "*") dialog.set_filename(self.ui.txt_icon.get_text()) # Set the image preview widget image_preview = Gtk.Image() image_preview.set_hexpand(False) image_preview.set_size_request( preferences.get(preferences.PREVIEW_SIZE), -1) dialog.set_preview_widget(image_preview, update_preview_cb) # Show the browse for icon dialog filename = dialog.show() if filename is not None: self.ui.txt_icon.set_text(filename) dialog.destroy() def on_action_configure_activate(self, action): """Configure the arguments list""" dialog = UICommandArguments(self.ui.dialog_edit_service) dialog.show(default_command=self.ui.txt_command.get_text()) self.ui.txt_command.set_text(dialog.arguments)