import subprocess import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GObject from sublime.server import Server from sublime.config import ServerConfiguration from sublime.ui.common import EditFormDialog, IconButton class EditServerDialog(EditFormDialog): entity_name: str = 'Server' initial_size = (450, 250) text_fields = [ ('Name', 'name', False), ('Server address', 'server_address', False), ('Local network address', 'local_network_address', False), ('Local network SSID', 'local_network_ssid', False), ('Username', 'username', False), ('Password', 'password', True), ] boolean_fields = [ ('Browse by tags', 'browse_by_tags'), ('Sync enabled', 'sync_enabled'), ] def __init__(self, *args, **kwargs): test_server = Gtk.Button(label='Test Connection to Server') test_server.connect('clicked', self.on_test_server_clicked) open_in_browser = Gtk.Button(label='Open in Browser') open_in_browser.connect('clicked', self.on_open_in_browser_clicked) self.extra_buttons = [ (test_server, Gtk.ResponseType.OK), (open_in_browser, Gtk.ResponseType.OK), ] super().__init__(*args, **kwargs) def on_test_server_clicked(self, event): # Instantiate the server. server_address = self.data['server_address'].get_text() server = Server( self.data['name'].get_text(), server_address, self.data['username'].get_text(), self.data['password'].get_text(), ) # Try to ping, and show a message box with whether or not it worked. try: server.ping() dialog = Gtk.MessageDialog( transient_for=self, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK, text='Connection to server successful.', ) dialog.format_secondary_markup( f'Connection to {server_address} successful.') except Exception as err: dialog = Gtk.MessageDialog( transient_for=self, message_type=Gtk.MessageType.ERROR, buttons=Gtk.ButtonsType.OK, text='Connection to server unsuccessful.', ) dialog.format_secondary_markup( f'Connection to {server_address} resulted in the following ' f'error:\n\n{err}') dialog.run() dialog.destroy() def on_open_in_browser_clicked(self, event): subprocess.call(['xdg-open', self.data['server_address'].get_text()]) class ConfigureServersDialog(Gtk.Dialog): __gsignals__ = { 'server-list-changed': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object, )), 'connected-server-changed': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, (object, )), } def __init__(self, parent, config): Gtk.Dialog.__init__( self, title='Conigure Servers', transient_for=parent, flags=0, add_buttons=(), ) self.server_configs = config.servers self.selected_server_index = config.current_server self.set_default_size(500, 300) # Flow box to hold the server list and the buttons. flowbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) # Server List self.server_list = Gtk.ListBox(activate_on_single_click=False) self.server_list.connect( 'selected-rows-changed', self.server_list_on_selected_rows_changed) self.server_list.connect('row-activated', self.on_server_list_activate) flowbox.pack_start(self.server_list, True, True, 10) button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) # Tuples: (button, action function, pack side, requires_selection) # Add all of the buttons to the button box. self.buttons = [ # TODO get good icons for these ( IconButton( 'document-edit-symbolic', label='Edit...', relief=True), lambda e: self.on_edit_clicked(e, False), 'start', True), ( IconButton('list-add', label='Add...', relief=True), lambda e: self.on_edit_clicked(e, True), 'start', False), ( IconButton('list-remove', label='Remove', relief=True), self.on_remove_clicked, 'start', True), ( IconButton('window-close', label='Close', relief=True), lambda _: self.close(), 'end', False), ( IconButton( 'network-transmit-receive', label='Connect', relief=True), self.on_connect_clicked, 'end', True), ] for button_cfg in self.buttons: btn, action, pack_end, requires_selection = button_cfg if pack_end == 'end': button_box.pack_end(btn, False, True, 5) else: button_box.pack_start(btn, False, True, 5) btn.connect('clicked', action) flowbox.pack_end(button_box, False, False, 0) # Add the flowbox to the dialog and show the dialog. content_area = self.get_content_area() content_area.pack_start(flowbox, True, True, 10) self.show_all() self.refresh_server_list() self.server_list_on_selected_rows_changed(None) def refresh_server_list(self): # Clear out the list. for el in self.server_list: self.server_list.remove(el) # Add all of the rows for each of the servers. for i, config in enumerate(self.server_configs): box = Gtk.Box() image = Gtk.Image(margin=5) if i == self.selected_server_index: image.set_from_icon_name( 'network-transmit-receive-symbolic', Gtk.IconSize.SMALL_TOOLBAR, ) box.add(image) server_name_label = Gtk.Label(label=config.name) server_name_label.set_halign(Gtk.Align.START) box.add(server_name_label) self.server_list.add(box) # Show them, and select the current server. self.show_all() if self.selected_server_index is not None and self.selected_server_index >= 0: self.server_list.select_row( self.server_list.get_row_at_index(self.selected_server_index)) def on_remove_clicked(self, event): selected = self.server_list.get_selected_row() if selected: del self.server_configs[selected.get_index()] self.refresh_server_list() self.emit('server-list-changed', self.server_configs) def on_edit_clicked(self, event, add): if add: dialog = EditServerDialog(self) else: selected_index = self.server_list.get_selected_row().get_index() dialog = EditServerDialog( self, self.server_configs[selected_index]) result = dialog.run() if result == Gtk.ResponseType.OK: # Create a new server configuration to use. new_config = ServerConfiguration( name=dialog.data['name'].get_text(), server_address=dialog.data['server_address'].get_text(), local_network_address=dialog.data['local_network_address'] .get_text(), local_network_ssid=dialog.data['local_network_ssid'].get_text( ), username=dialog.data['username'].get_text(), password=dialog.data['password'].get_text(), browse_by_tags=dialog.data['browse_by_tags'].get_active(), sync_enabled=dialog.data['sync_enabled'].get_active(), ) if add: self.server_configs.append(new_config) else: self.server_configs[selected_index] = new_config self.refresh_server_list() self.emit('server-list-changed', self.server_configs) dialog.destroy() def on_server_list_activate(self, *args): self.on_connect_clicked(None) def on_connect_clicked(self, event): selected_index = self.server_list.get_selected_row().get_index() self.emit('connected-server-changed', selected_index) self.close() def server_list_on_selected_rows_changed(self, event): # Update the state of the buttons depending on whether or not a row is # selected in the server list. has_selection = self.server_list.get_selected_row() for button, *_, requires_selection in self.buttons: button.set_sensitive(not requires_selection or has_selection)