def __init__(self, user, database): # The database connection parameters are specified in the CONFIG.py file. # Contains the reference to the database object. self.db = None # The database cursor. self.cursor = None # The current connection timeout limit. self.timeout = CONNECTION_TIMEOUT # The savepoints. self.savepoints = [] # The user to connect with. If no user is specified, picks the first user # in the dictionary. self.user = user if user is not None else LOGIN.keys()[0] # The name of the database to connect to. If none is specified, use # <user>_db as the default database. self.database = database if database is not None else "%s_db" % self.user # Separate database connection used to terminate queries. If the terminator # cannot start, the grading cannot occur. try: self.terminator = Terminator(self.user, self.database) except mysql.connector.errors.Error: err("Could not start up terminator connection! Any unruly queries " + "must be manually killed!")
def __init__(self, window): """Class initialiser""" if isinstance(window.get_child(), Gtk.Notebook): err('There is already a Notebook at the top of this window') raise (ValueError) Container.__init__(self) GObject.GObject.__init__(self) self.terminator = Terminator() self.window = window GObject.type_register(Notebook) self.register_signals(Notebook) self.connect('switch-page', self.deferred_on_tab_switch) self.connect('scroll-event', self.on_scroll_event) self.configure() child = window.get_child() window.remove(child) window.add(self) window_last_active_term = window.last_active_term self.newtab(widget=child) if window_last_active_term: self.set_last_active_term(window_last_active_term) window.last_active_term = None self.show_all()
def __init__(self): """Class initialiser""" self.terminator = Terminator() self.terminator.register_window(self) Container.__init__(self) gtk.Window.__init__(self) gobject.type_register(Window) self.register_signals(Window) self.set_property('allow-shrink', True) self.apply_icon() self.register_callbacks() self.apply_config() self.title = WindowTitle(self) self.title.update() options = self.config.options_get() if options: if options.forcedtitle is not None: self.title.force_title(options.forcedtitle) if options.role is not None: self.set_role(options.role) if options.geometry is not None: if not self.parse_geometry(options.geometry): err('Window::__init__: Unable to parse geometry: %s' % options.geometry) self.pending_set_rough_geometry_hint = False
def setup_replicator(self): ''' Target: - clone a database in PostgreSQL. ''' connecter = self.get_connecter() self.logger.debug(Messenger.BEGINNING_EXE_REPLICATOR) replicator = self.get_replicator(connecter) pg_superuser = connecter.is_pg_superuser() if not pg_superuser: connecter.cursor.execute(Queries.GET_PG_DB_SOME_DATA, (replicator.original_dbname, )) db = connecter.cursor.fetchone() if db['owner'] != connecter.user: self.logger.stop_exe(Messenger.ACTION_DB_NO_SUPERUSER) # Terminate every connection to the database which is going to be # replicated, if necessary if self.args.terminate: terminator = Terminator(connecter, target_dbs=[replicator.original_dbname], logger=self.logger) terminator.terminate_backend_dbs([replicator.original_dbname]) # Clone the database replicator.replicate_pg_db() # Close connection to PostgreSQL connecter.pg_disconnect()
def __init__(self): """Class initialiser""" self.terminator = Terminator() self.maker = Factory() Container.__init__(self) self.signals.append({'name': 'resize-term', 'flags': GObject.SignalFlags.RUN_LAST, 'return_type': None, 'param_types': (GObject.TYPE_STRING,)})
def __init__(self, addr): Thread.__init__(self) self.setName("FeedListener") self.terminator = Terminator.getInstance() certHandler = CertificateHandler("front", 'server') publicKey, privateKey = certHandler.getKeyPair() clientsPath = certHandler.getEnrolledKeysPath() self.ctxHandler = ContextHandler(clientsPath) context = self.ctxHandler.getContext() self.socket = context.socket(zmq.REP) monitorSocket = self.socket.get_monitor_socket() monitor = Monitor(monitorSocket, 'front') monitor.setDaemon(True) monitor.start() self.socket.curve_secretkey = privateKey self.socket.curve_publickey = publicKey self.socket.curve_server = True self.socket.bind(addr) self.socket.setsockopt(zmq.RCVTIMEO, 10000) logging.info('Socket setup, public key: %s', publicKey)
def __init__(self, window): """Class initialiser""" if isinstance(window.get_child(), gtk.Notebook): err('There is already a Notebook at the top of this window') raise(ValueError) Container.__init__(self) gtk.Notebook.__init__(self) self.terminator = Terminator() self.window = window gobject.type_register(Notebook) self.register_signals(Notebook) self.connect('switch-page', self.deferred_on_tab_switch) self.configure() child = window.get_child() window.remove(child) window.add(self) window_last_active_term = window.last_active_term self.newtab(widget=child) if window_last_active_term: self.set_last_active_term(window_last_active_term) window.last_active_term = None self.show_all()
def __init__(self, feedID, clientKey): Thread.__init__(self) self.setName("FeedHandler") self.terminator = Terminator.getInstance() self.certHandler = CertificateHandler(feedID, 'server') self.certHandler.prep() self.publicKey, privateKey = self.certHandler.getKeyPair() clientsPath = self.certHandler.getEnrolledKeysPath() self.ctxHandler = ContextHandler(clientsPath) context = self.ctxHandler.getContext() self.saveClientKey(clientKey) self.socket = context.socket(zmq.REP) self.socket.setsockopt(zmq.RCVTIMEO, 20000) monitorSocket = self.socket.get_monitor_socket() self.monitor = Monitor(monitorSocket, feedID) self.monitor.setDaemon(True) self.monitor.start() self.socket.curve_secretkey = privateKey self.socket.curve_publickey = self.publicKey self.socket.curve_server = True self.port = self.socket.bind_to_random_port('tcp://*', min_port=49151, max_port=65535) self.feedID = feedID
def toggle_tab_visibility(self, widget): """tab visibility""" status = self.config['tab_position'] old_tab_position = self.config['old_tab_position'] if status == 'hidden': if old_tab_position: #if there's no oldstatus, hidden is default option self.config['tab_position'] = old_tab_position self.config.save() else: self.config['old_tab_position'] = status self.config['tab_position'] = 'hidden' self.config.save() terminator = Terminator() terminator.reconfigure()
def __init__(self, app): QMainWindow.__init__(self) self.app = app authenticated = False self.terminator = Terminator.getInstance() if os.path.exists(userConfig): logging.debug('Authenticating on saved user details') authenticated, msg = self.authenticateUserDetails() if not authenticated: logging.debug('Not already logged in, presenting dialog.') loginDialog = LoginDialog() authenticated, msg, userID, key = loginDialog.getUserData() self.saveUserDetails(userID, key) if authenticated: logging.debug('User authenticated, initialising tabs.') tabs = MainWindowTabs(app) self.setCentralWidget(tabs) else: self.closeEvent(None)
def __init__(self): """Class initialiser""" self.terminator = Terminator() self.terminator.register_window(self) Container.__init__(self) gtk.Window.__init__(self) gobject.type_register(Window) self.register_signals(Window) self.set_property("allow-shrink", True) self.apply_icon() self.register_callbacks() self.apply_config() self.title = WindowTitle(self) self.title.update() options = self.config.options_get() if options: if options.forcedtitle is not None: self.title.force_title(options.forcedtitle) if options.role is not None: self.set_role(options.role) if options.geometry is not None: if not self.parse_geometry(options.geometry): err("Window::__init__: Unable to parse geometry: %s" % options.geometry) self.pending_set_rough_geometry_hint = False
class LayoutLauncher: """Class implementing the various parts of the preferences editor""" terminator = None config = None registry = None plugins = None keybindings = None window = None builder = None layouttreeview = None layouttreestore = None def __init__ (self): self.terminator = Terminator() self.terminator.register_launcher_window(self) self.config = config.Config() self.config.base.reload() self.builder = gtk.Builder() try: # Figure out where our library is on-disk so we can open our UI (head, _tail) = os.path.split(config.__file__) librarypath = os.path.join(head, 'layoutlauncher.glade') gladefile = open(librarypath, 'r') gladedata = gladefile.read() except Exception, ex: print "Failed to find layoutlauncher.glade" print ex return self.builder.add_from_string(gladedata) self.window = self.builder.get_object('layoutlauncherwin') icon_theme = gtk.IconTheme() try: icon = icon_theme.load_icon('terminator-layout', 48, 0) except (NameError, gobject.GError): dbg('Unable to load 48px Terminator preferences icon') icon = self.window.render_icon(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_BUTTON) self.window.set_icon(icon) self.builder.connect_signals(self) self.window.connect('destroy', self.on_destroy_event) self.window.show_all() self.layouttreeview = self.builder.get_object('layoutlist') self.layouttreestore = self.builder.get_object('layoutstore') self.update_layouts()
def on_stone_received(self, stone): pid_file = tempfile.mktemp() stone = pickle.loads(stone) logging.getLogger(__name__).info("received %s task from broker" % stone.name) child_pid = os.fork() time.sleep(0.1) if not child_pid: #child logging.getLogger(__name__).debug("started child %s" % os.getpid()) s = Sisyphus(stone, pid_file, ttl=SISYPHUS_JOIN_TTL, logger=logging.getLogger(__name__)) s.run() time.sleep(0.1) else: #master t = Terminator(child_pid, pid_file, ttl=SISYPHUS_WORKER_TTL) t.start() time.sleep(0.1)
def __init__(self, parent): Thread.__init__(self) self.parent = parent self.terminator = Terminator.getInstance() self.moveTopConnector = GenericConnector(self.parent.moveTop) self.progressBarSetup = GenericConnector(self.parent.setupProgressBar) self.progressBarUpdate = GenericConnector( self.parent.updateProgressBar)
def unload(self): """Handle being removed""" if not self.match: err('unload called without self.handler_name being set') return terminator = Terminator() for terminal in terminator.terminals: terminal.match_remove(self.handler_name)
def __init__(self, terminal): """Class initialiser""" GObject.GObject.__init__(self) self.terminator = Terminator() self.terminal = terminal self.config = self.terminal.config self.label = EditableLabel() self.label.connect('edit-done', self.on_edit_done) self.ebox = Gtk.EventBox() grouphbox = Gtk.HBox() self.grouplabel = Gtk.Label(ellipsize='end') self.groupicon = Gtk.Image() self.bellicon = Gtk.Image() self.bellicon.set_no_show_all(True) self.groupentry = Gtk.Entry() self.groupentry.set_no_show_all(True) self.groupentry.connect('focus-out-event', self.groupentry_cancel) self.groupentry.connect('activate', self.groupentry_activate) self.groupentry.connect('key-press-event', self.groupentry_keypress) groupsend_type = self.terminator.groupsend_type if self.terminator.groupsend == groupsend_type['all']: icon_name = 'all' elif self.terminator.groupsend == groupsend_type['group']: icon_name = 'group' elif self.terminator.groupsend == groupsend_type['off']: icon_name = 'off' self.set_from_icon_name('_active_broadcast_%s' % icon_name, Gtk.IconSize.MENU) grouphbox.pack_start(self.groupicon, False, True, 2) grouphbox.pack_start(self.grouplabel, False, True, 2) grouphbox.pack_start(self.groupentry, False, True, 2) self.ebox.add(grouphbox) self.ebox.show_all() self.bellicon.set_from_icon_name('terminal-bell', Gtk.IconSize.MENU) viewport = Gtk.Viewport(hscroll_policy='natural') viewport.add(self.label) hbox = Gtk.HBox() hbox.pack_start(self.ebox, False, True, 0) hbox.pack_start(Gtk.VSeparator(), False, True, 0) hbox.pack_start(viewport, True, True, 0) hbox.pack_end(self.bellicon, False, False, 2) self.add(hbox) hbox.show_all() self.set_no_show_all(True) self.show() self.connect('button-press-event', self.on_clicked)
def create_connection_function(data): connection_function = None con_type = data[2] if con_type == "terminator": connection_function = Terminator(data) elif con_type == "junction": connection_function = Junction(data) elif con_type == "connection": connection_function = Connection(data) return connection_function
def __init__(self): """Class initialiser""" self.terminator = Terminator() self.terminator.register_window(self) Container.__init__(self) GObject.GObject.__init__(self) GObject.type_register(Window) self.register_signals(Window) self.get_style_context().add_class("terminator-terminal-window") # self.set_property('allow-shrink', True) # FIXME FOR GTK3, or do we need this actually? icon_to_apply = '' self.register_callbacks() self.apply_config() self.title = WindowTitle(self) self.title.update() options = self.config.options_get() if options: if options.forcedtitle: self.title.force_title(options.forcedtitle) if options.role: self.set_role(options.role) # if options.classname is not None: # self.set_wmclass(options.classname, self.wmclass_class) if options.forcedicon is not None: icon_to_apply = options.forcedicon if options.geometry: if not self.parse_geometry(options.geometry): err('Window::__init__: Unable to parse geometry: %s' % options.geometry) self.apply_icon(icon_to_apply) self.pending_set_rough_geometry_hint = False
def __init__(self, terminal): """Class initialiser""" gtk.EventBox.__init__(self) self.__gobject_init__() self.terminator = Terminator() self.terminal = terminal self.config = self.terminal.config self.label = EditableLabel() self.label.connect('edit-done', self.on_edit_done) self.ebox = gtk.EventBox() grouphbox = gtk.HBox() self.grouplabel = gtk.Label() self.groupicon = gtk.Image() self.bellicon = gtk.Image() self.bellicon.set_no_show_all(True) self.groupentry = gtk.Entry() self.groupentry.set_no_show_all(True) self.groupentry.connect('focus-out-event', self.groupentry_cancel) self.groupentry.connect('activate', self.groupentry_activate) self.groupentry.connect('key-press-event', self.groupentry_keypress) groupsend_type = self.terminator.groupsend_type if self.terminator.groupsend == groupsend_type['all']: icon_name = 'all' elif self.terminator.groupsend == groupsend_type['group']: icon_name = 'group' elif self.terminator.groupsend == groupsend_type['off']: icon_name = 'off' self.set_from_icon_name('_active_broadcast_%s' % icon_name, gtk.ICON_SIZE_MENU) grouphbox.pack_start(self.groupicon, False, True, 2) grouphbox.pack_start(self.grouplabel, False, True, 2) grouphbox.pack_start(self.groupentry, False, True, 2) self.ebox.add(grouphbox) self.ebox.show_all() self.bellicon.set_from_icon_name('terminal-bell', gtk.ICON_SIZE_MENU) hbox = gtk.HBox() hbox.pack_start(self.ebox, False, True, 0) hbox.pack_start(gtk.VSeparator(), False, True, 0) hbox.pack_start(self.label, True, True) hbox.pack_end(self.bellicon, False, False, 2) self.add(hbox) hbox.show_all() self.set_no_show_all(True) self.show() self.connect('button-press-event', self.on_clicked)
def setup_dropper(self): ''' Target: - delete specified databases in PostgreSQL. ''' connecter = self.get_connecter() self.logger.debug(Messenger.BEGINNING_EXE_DROPPER) dropper = self.get_dropper(connecter) # Terminate every connection to the target databases if necessary if self.args.terminate: terminator = Terminator(connecter, target_dbs=dropper.dbnames, logger=self.logger) terminator.terminate_backend_dbs(dropper.dbnames) # Delete the databases dropper.drop_pg_dbs(dropper.dbnames) # Close connection to PostgreSQL connecter.pg_disconnect()
def setup_vacuumer(self): ''' Target: - executes the vacuumer taking into account the value of its variables. ''' connecter = self.get_connecter() self.logger.debug(Messenger.BEGINNING_EXE_VACUUMER) vacuumer = self.get_vacuumer(connecter) # Check if the role of user connected to PostgreSQL is superuser pg_superuser = connecter.is_pg_superuser() if not pg_superuser: # Users who are not superusers will only be able to vacuum the # databases they own vacuumer.db_owner = connecter.user self.logger.warning(Messenger.ACTION_DB_NO_SUPERUSER) # Get PostgreSQL databases' names, connection permissions and owners dbs_all = connecter.get_pg_dbs_data(vacuumer.ex_templates, vacuumer.db_owner) # Show and log their names Orchestrator.show_dbs(dbs_all, self.logger) # Get the target databases in a list vacuum_list = DbSelector.get_filtered_dbs( dbs_all, vacuumer.in_dbs, vacuumer.ex_dbs, vacuumer.in_regex, vacuumer.ex_regex, vacuumer.in_priority, self.logger) # Terminate every connection to these target databases if necessary if self.args.terminate: terminator = Terminator(connecter, target_dbs=vacuum_list, logger=self.logger) terminator.terminate_backend_dbs(vacuum_list) # Vacuum the target databases vacuumer.vacuum_dbs(vacuum_list) # Close connection to PostgreSQL connecter.pg_disconnect()
def setup_alterer(self): ''' Target: - change the owner of the specified databases in PostgreSQL. ''' connecter = self.get_connecter() self.logger.debug(Messenger.BEGINNING_EXE_ALTERER) alterer = self.get_alterer(connecter) # Check if the role of user connected to PostgreSQL is superuser pg_superuser = connecter.is_pg_superuser() if not pg_superuser: # Users who are not superusers will only be able to backup the # databases they own owner = connecter.user self.logger.highlight('warning', Messenger.ACTION_DB_NO_SUPERUSER, 'yellow', effect='bold') else: owner = '' # Get PostgreSQL databases' names, connection permissions and owners dbs_all = connecter.get_pg_dbs_data(ex_templates=False, db_owner=owner) # Show and log their names Orchestrator.show_dbs(dbs_all, self.logger) # Get the target databases in a list alt_list = DbSelector.get_filtered_dbs( dbs_all=dbs_all, in_dbs=alterer.in_dbs, logger=self.logger) # Terminate every connection to the target databases if necessary if self.args.terminate: terminator = Terminator(connecter, target_dbs=alt_list, logger=self.logger) terminator.terminate_backend_dbs(alt_list) # Delete the databases alterer.alter_dbs_owner(alt_list) # Close connection to PostgreSQL connecter.pg_disconnect()
class LayoutLauncher: """Class implementing the various parts of the preferences editor""" terminator = None config = None registry = None plugins = None keybindings = None window = None builder = None layouttreeview = None layouttreestore = None def __init__ (self): self.terminator = Terminator() self.terminator.register_launcher_window(self) self.config = config.Config() self.config.base.reload() self.builder = gtk.Builder() try: # Figure out where our library is on-disk so we can open our UI (head, _tail) = os.path.split(config.__file__) librarypath = os.path.join(head, 'layoutlauncher.glade') gladefile = open(librarypath, 'r') gladedata = gladefile.read() except Exception, ex: print "Failed to find layoutlauncher.glade" print ex return self.builder.add_from_string(gladedata) self.window = self.builder.get_object('layoutlauncherwin') self.builder.connect_signals(self) self.window.connect('destroy', self.on_destroy_event) self.window.show_all() self.layouttreeview = self.builder.get_object('layoutlist') self.layouttreestore = self.builder.get_object('layoutstore') self.update_layouts()
def __init__(self, title, notebook): """Class initialiser""" GObject.GObject.__init__(self) self.notebook = notebook self.terminator = Terminator() self.config = Config() self.label = EditableLabel(title) self.update_angle() self.pack_start(self.label, True, True, 0) self.update_button() self.show_all()
def __init__(self): Thread.__init__(self) self.terminator = Terminator.getInstance() self.setName("Authenticator") try: self.db = sql.connect(os.environ["DBHOST"], os.environ["DBUSER"], os.environ["DBPASS"], os.environ["DBNAME"]) self.cursor = self.db.cursor() logging.debug('Connected to database') except: logging.critical('Exception occured connecting to database', exc_info=True) self.terminator.autoTerminate() try: certHandler = CertificateHandler("front", 'server') publicKey, privateKey = certHandler.getKeyPair() clientKeysPath = certHandler.getEnrolledKeysPath() self.ctxHandler = ContextHandler(clientKeysPath) context = self.ctxHandler.getContext() self.socket = context.socket(zmq.REP) try: monitorSocket = self.socket.get_monitor_socket() self.monitor = Monitor(monitorSocket, 'Registration') self.monitor.setDaemon(True) self.monitor.start() logging.debug('Socket monitor thread started') except: logging.error( 'Exception occured starting socket monitor thread', exc_info=True) self.socket.curve_secretkey = privateKey self.socket.curve_publickey = publicKey self.socket.curve_server = True self.socket.setsockopt(zmq.RCVTIMEO, 10000) self.socket.bind('tcp://0.0.0.0:5001') logging.debug('Socket setup and bound, listening') except: logging.critical('Exception occured setting up auth socket', exc_info=True)
def __init__ (self): self.terminator = Terminator() self.terminator.register_launcher_window(self) self.config = config.Config() self.config.base.reload() self.builder = gtk.Builder() try: # Figure out where our library is on-disk so we can open our UI (head, _tail) = os.path.split(config.__file__) librarypath = os.path.join(head, 'layoutlauncher.glade') gladefile = open(librarypath, 'r') gladedata = gladefile.read() except Exception, ex: print "Failed to find layoutlauncher.glade" print ex return
def prepare_attributes(self): """Ensure we are populated""" if not self.bus_name: dbg('Checking for bus name availability: %s' % BUS_NAME) bus = dbus.SessionBus() proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') flags = 1 | 4 # allow replacement | do not queue if not proxy.RequestName(BUS_NAME, dbus.UInt32(flags)) in (1, 4): dbg('bus name unavailable: %s' % BUS_NAME) raise dbus.exceptions.DBusException( "Couldn't get DBus name %s: Name exists" % BUS_NAME) self.bus_name = dbus.service.BusName(BUS_NAME, bus=dbus.SessionBus()) if not self.bus_path: self.bus_path = BUS_PATH if not self.terminator: self.terminator = Terminator()
def __init__(self): """Class initialiser""" self.terminator = Terminator() self.terminator.register_window(self) Container.__init__(self) GObject.GObject.__init__(self) GObject.type_register(Window) self.register_signals(Window) self.get_style_context().add_class("terminator-terminal-window") # self.set_property('allow-shrink', True) # FIXME FOR GTK3, or do we need this actually? icon_to_apply='' self.register_callbacks() self.apply_config() self.title = WindowTitle(self) self.title.update() self.preventHide = False options = self.config.options_get() if options: if options.forcedtitle: self.title.force_title(options.forcedtitle) if options.role: self.set_role(options.role) if options.forcedicon is not None: icon_to_apply = options.forcedicon if options.geometry: if not self.parse_geometry(options.geometry): err('Window::__init__: Unable to parse geometry: %s' % options.geometry) self.apply_icon(icon_to_apply) self.pending_set_rough_geometry_hint = False
def run(self): terminator = Terminator.getInstance() while not terminator.isTerminating(): try: msg = recv_monitor_message(self.socket) except: break event = msg.get("event") value = msg.get("value") endpoint = msg.get("endpoint") assigned = False for key, val in self.events.items(): if event == val: assigned = True logging.debug(str(key) + ' ' + str(endpoint)) if assigned is False: logging.error('Unknown monitor message: %s', msg) logging.debug('Monitor thread %s shutting down', self.feedID)
def __init__(self, feedListener, authListener): Thread.__init__(self) self.setName("Enroller") self.feedListener = feedListener self.authListener = authListener self.terminator = Terminator.getInstance() self.unsecuredCtx = zmq.Context() self.unsecuredSocket = self.unsecuredCtx.socket(zmq.REP) monitorSocket = self.unsecuredSocket.get_monitor_socket() self.monitor = Monitor(monitorSocket, "Enroller") self.monitor.setDaemon(True) self.monitor.start() self.unsecuredSocket.bind('tcp://0.0.0.0:5002') self.unsecuredSocket.setsockopt(zmq.RCVTIMEO, 10000) self.certHandler = CertificateHandler('front', 'server') self.publicKey, _ = self.certHandler.getKeyPair() self.publicKey = self.publicKey.decode('utf-8')
def __init__(self, title, notebook): """Class initialiser""" GObject.GObject.__init__(self) self.notebook = notebook self.terminator = Terminator() self.config = Config() self.label = EditableLabel(title) self.label.add_events(2097152) # SCROLL self.label.add_events(4194304) # TOUCH self.label.connect('scroll-event', notebook.on_scroll_event) self.update_angle() self.pack_start(self.label, True, True, 0) self.update_button() if self.button: self.button.add_events(2097152) # SCROLL self.button.add_events(4194304) # TOUCH self.button.connect('scroll-event', notebook.on_scroll_event) self.show_all()
class DBusService(Borg, dbus.service.Object): """DBus Server class. This is implemented as a Borg""" bus_name = None bus_path = None terminator = None def __init__(self): """Class initialiser""" Borg.__init__(self, self.__class__.__name__) self.prepare_attributes() try: dbus.service.Object.__init__(self, self.bus_name, BUS_PATH) except: None def prepare_attributes(self): """Ensure we are populated""" if not self.bus_name: dbg('Checking for bus name availability: %s' % BUS_NAME) bus = dbus.SessionBus() proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') flags = 1 | 4 # allow replacement | do not queue if not proxy.RequestName(BUS_NAME, dbus.UInt32(flags)) in (1, 4): dbg('bus name unavailable: %s' % BUS_NAME) raise dbus.exceptions.DBusException( "Couldn't get DBus name %s: Name exists" % BUS_NAME) self.bus_name = dbus.service.BusName(BUS_NAME, bus=dbus.SessionBus()) if not self.bus_path: self.bus_path = BUS_PATH if not self.terminator: self.terminator = Terminator() @dbus.service.method(BUS_NAME, in_signature='a{ss}') def new_window_cmdline(self, options=dbus.Dictionary()): """Create a new Window""" dbg('dbus method called: new_window with parameters %s'%(options)) oldopts = self.terminator.config.options_get() oldopts.__dict__ = options self.terminator.config.options_set(oldopts) self.terminator.create_layout(oldopts.layout) self.terminator.layout_done() @dbus.service.method(BUS_NAME, in_signature='a{ss}') def new_tab_cmdline(self, options=dbus.Dictionary()): """Create a new tab""" dbg('dbus method called: new_tab with parameters %s'%(options)) oldopts = self.terminator.config.options_get() oldopts.__dict__ = options self.terminator.config.options_set(oldopts) window = self.terminator.get_windows()[0] window.tab_new() @dbus.service.method(BUS_NAME) def new_window(self): """Create a new Window""" terminals_before = set(self.get_terminals()) self.terminator.new_window() terminals_after = set(self.get_terminals()) new_terminal_set = list(terminals_after - terminals_before) if len(new_terminal_set) != 1: return "ERROR: Cannot determine the UUID of the added terminal" else: return new_terminal_set[0] @dbus.service.method(BUS_NAME) def new_tab(self, uuid=None): """Create a new tab""" return self.new_terminal(uuid, 'tab') @dbus.service.method(BUS_NAME) def hsplit(self, uuid=None): """Split a terminal horizontally, by UUID""" return self.new_terminal(uuid, 'hsplit') @dbus.service.method(BUS_NAME) def vsplit(self, uuid=None): """Split a terminal vertically, by UUID""" return self.new_terminal(uuid, 'vsplit') def new_terminal(self, uuid, type): """Split a terminal horizontally or vertically, by UUID""" dbg('dbus method called: %s' % type) if not uuid: return "ERROR: No UUID specified" terminal = self.terminator.find_terminal_by_uuid(uuid) terminals_before = set(self.get_terminals()) if not terminal: return "ERROR: Terminal with supplied UUID not found" elif type == 'tab': terminal.key_new_tab() elif type == 'hsplit': terminal.key_split_horiz() elif type == 'vsplit': terminal.key_split_vert() else: return "ERROR: Unknown type \"%s\" specified" % (type) terminals_after = set(self.get_terminals()) # Detect the new terminal UUID new_terminal_set = list(terminals_after - terminals_before) if len(new_terminal_set) != 1: return "ERROR: Cannot determine the UUID of the added terminal" else: return new_terminal_set[0] @dbus.service.method(BUS_NAME) def get_terminals(self): """Return a list of all the terminals""" return [x.uuid.urn for x in self.terminator.terminals] @dbus.service.method(BUS_NAME) def get_window(self, uuid=None): """Return the UUID of the parent window of a given terminal""" terminal = self.terminator.find_terminal_by_uuid(uuid) window = terminal.get_toplevel() return window.uuid.urn @dbus.service.method(BUS_NAME) def get_window_title(self, uuid=None): """Return the title of a parent window of a given terminal""" terminal = self.terminator.find_terminal_by_uuid(uuid) window = terminal.get_toplevel() return window.get_title() @dbus.service.method(BUS_NAME) def get_tab(self, uuid=None): """Return the UUID of the parent tab of a given terminal""" maker = Factory() terminal = self.terminator.find_terminal_by_uuid(uuid) window = terminal.get_toplevel() root_widget = window.get_children()[0] if maker.isinstance(root_widget, 'Notebook'): #return root_widget.uuid.urn for tab_child in root_widget.get_children(): terms = [tab_child] if not maker.isinstance(terms[0], "Terminal"): terms = enumerate_descendants(tab_child)[1] if terminal in terms: # FIXME: There are no uuid's assigned to the the notebook, or the actual tabs! # This would fail: return root_widget.uuid.urn return "" @dbus.service.method(BUS_NAME) def get_tab_title(self, uuid=None): """Return the title of a parent tab of a given terminal""" maker = Factory() terminal = self.terminator.find_terminal_by_uuid(uuid) window = terminal.get_toplevel() root_widget = window.get_children()[0] if maker.isinstance(root_widget, "Notebook"): for tab_child in root_widget.get_children(): terms = [tab_child] if not maker.isinstance(terms[0], "Terminal"): terms = enumerate_descendants(tab_child)[1] if terminal in terms: return root_widget.get_tab_label(tab_child).get_label()
class Window(Container, gtk.Window): """Class implementing a top-level Terminator window""" terminator = None title = None isfullscreen = None ismaximised = None hidebound = None hidefunc = None losefocus_time = 0 position = None ignore_startup_show = None set_pos_by_ratio = None zoom_data = None term_zoomed = False __gproperties__ = { 'term_zoomed': (gobject.TYPE_BOOLEAN, 'terminal zoomed', 'whether the terminal is zoomed', False, gobject.PARAM_READWRITE) } def __init__(self): """Class initialiser""" self.terminator = Terminator() self.terminator.register_window(self) Container.__init__(self) gtk.Window.__init__(self) gobject.type_register(Window) self.register_signals(Window) self.set_property('allow-shrink', True) icon_to_apply='' self.register_callbacks() self.apply_config() self.title = WindowTitle(self) self.title.update() options = self.config.options_get() if options: if options.forcedtitle: self.title.force_title(options.forcedtitle) if options.role: self.set_role(options.role) if options.classname is not None: self.set_wmclass(options.classname, self.wmclass_class) if options.forcedicon is not None: icon_to_apply = options.forcedicon if options.geometry: if not self.parse_geometry(options.geometry): err('Window::__init__: Unable to parse geometry: %s' % options.geometry) self.apply_icon(icon_to_apply) self.pending_set_rough_geometry_hint = False def do_get_property(self, prop): """Handle gobject getting a property""" if prop.name in ['term_zoomed', 'term-zoomed']: return(self.term_zoomed) else: raise AttributeError('unknown property %s' % prop.name) def do_set_property(self, prop, value): """Handle gobject setting a property""" if prop.name in ['term_zoomed', 'term-zoomed']: self.term_zoomed = value else: raise AttributeError('unknown property %s' % prop.name) def register_callbacks(self): """Connect the GTK+ signals we care about""" self.connect('key-press-event', self.on_key_press) self.connect('button-press-event', self.on_button_press) self.connect('delete_event', self.on_delete_event) self.connect('destroy', self.on_destroy_event) self.connect('window-state-event', self.on_window_state_changed) self.connect('focus-out-event', self.on_focus_out) self.connect('focus-in-event', self.on_focus_in) # Attempt to grab a global hotkey for hiding the window. # If we fail, we'll never hide the window, iconifying instead. if self.config['keybindings']['hide_window'] != None: try: self.hidebound = keybinder.bind( self.config['keybindings']['hide_window'], self.on_hide_window) except (KeyError, NameError): pass if not self.hidebound: err('Unable to bind hide_window key, another instance/window has it.') self.hidefunc = self.iconify else: self.hidefunc = self.hide def apply_config(self): """Apply various configuration options""" options = self.config.options_get() maximise = self.config['window_state'] == 'maximise' fullscreen = self.config['window_state'] == 'fullscreen' hidden = self.config['window_state'] == 'hidden' borderless = self.config['borderless'] skiptaskbar = self.config['hide_from_taskbar'] alwaysontop = self.config['always_on_top'] sticky = self.config['sticky'] if options: if options.maximise: maximise = True if options.fullscreen: fullscreen = True if options.hidden: hidden = True if options.borderless: borderless = True self.set_fullscreen(fullscreen) self.set_maximised(maximise) self.set_borderless(borderless) self.set_always_on_top(alwaysontop) self.set_real_transparency() self.set_sticky(sticky) if self.hidebound: self.set_hidden(hidden) self.set_skip_taskbar_hint(skiptaskbar) else: self.set_iconified(hidden) def apply_icon(self, requested_icon): """Set the window icon""" icon_theme = gtk.IconTheme() icon = None if requested_icon: try: self.set_icon_from_file(requested_icon) icon = self.get_icon() except (NameError, gobject.GError): dbg('Unable to load 48px %s icon as file' % (repr(requested_icon))) if requested_icon and icon is None: try: icon = icon_theme.load_icon(requested_icon, 48, 0) except (NameError, gobject.GError): dbg('Unable to load 48px %s icon' % (repr(requested_icon))) if icon is None: try: icon = icon_theme.load_icon(self.wmclass_name, 48, 0) except (NameError, gobject.GError): dbg('Unable to load 48px %s icon' % (self.wmclass_name)) if icon is None: try: icon = icon_theme.load_icon(APP_NAME, 48, 0) except (NameError, gobject.GError): dbg('Unable to load 48px Terminator icon') icon = self.render_icon(gtk.STOCK_DIALOG_INFO, gtk.ICON_SIZE_BUTTON) self.set_icon(icon) def on_key_press(self, window, event): """Handle a keyboard event""" maker = Factory() self.set_urgency_hint(False) mapping = self.terminator.keybindings.lookup(event) if mapping: dbg('Window::on_key_press: looked up %r' % mapping) if mapping == 'full_screen': self.set_fullscreen(not self.isfullscreen) elif mapping == 'close_window': if not self.on_delete_event(window, gtk.gdk.Event(gtk.gdk.DELETE)): self.on_destroy_event(window, gtk.gdk.Event(gtk.gdk.DESTROY)) elif mapping == 'new_tab': self.tab_new(self.get_focussed_terminal()) else: return(False) return(True) def on_button_press(self, window, event): """Handle a mouse button event. Mainly this is just a clean way to cancel any urgency hints that are set.""" self.set_urgency_hint(False) return(False) def on_focus_out(self, window, event): """Focus has left the window""" for terminal in self.get_visible_terminals(): terminal.on_window_focus_out() self.losefocus_time = time.time() if self.config['hide_on_lose_focus'] and self.get_property('visible'): self.position = self.get_position() self.hidefunc() def on_focus_in(self, window, event): """Focus has entered the window""" self.set_urgency_hint(False) # FIXME: Cause the terminal titlebars to update here def is_child_notebook(self): """Returns True if this Window's child is a Notebook""" maker = Factory() return(maker.isinstance(self.get_child(), 'Notebook')) def tab_new(self, widget=None, debugtab=False, _param1=None, _param2=None): """Make a new tab""" cwd = None profile = None if self.get_property('term_zoomed') == True: err("You can't create a tab while a terminal is maximised/zoomed") return if widget: cwd = widget.get_cwd() profile = widget.get_profile() maker = Factory() if not self.is_child_notebook(): dbg('Making a new Notebook') notebook = maker.make('Notebook', window=self) return self.get_child().newtab(debugtab, cwd=cwd, profile=profile) def on_delete_event(self, window, event, data=None): """Handle a window close request""" maker = Factory() if maker.isinstance(self.get_child(), 'Terminal'): dbg('Window::on_delete_event: Only one child, closing is fine') return(False) elif maker.isinstance(self.get_child(), 'Container'): return(self.confirm_close(window, _('window'))) else: dbg('unknown child: %s' % self.get_child()) def confirm_close(self, window, type): """Display a confirmation dialog when the user is closing multiple terminals in one window""" return(not (self.construct_confirm_close(window, type) == gtk.RESPONSE_ACCEPT)) def on_destroy_event(self, widget, data=None): """Handle window destruction""" dbg('destroying self') for terminal in self.get_visible_terminals(): terminal.close() self.cnxids.remove_all() self.terminator.deregister_window(self) self.destroy() del(self) def on_hide_window(self, data=None): """Handle a request to hide/show the window""" if not self.get_property('visible'): #Don't show if window has just been hidden because of #lost focus if (time.time() - self.losefocus_time < 0.1) and \ self.config['hide_on_lose_focus']: return if self.position: self.move(self.position[0], self.position[1]) self.show() else: self.position = self.get_position() self.hidefunc() # pylint: disable-msg=W0613 def on_window_state_changed(self, window, event): """Handle the state of the window changing""" self.isfullscreen = bool(event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN) self.ismaximised = bool(event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED) dbg('Window::on_window_state_changed: fullscreen=%s, maximised=%s' \ % (self.isfullscreen, self.ismaximised)) return(False) def set_maximised(self, value): """Set the maximised state of the window from the supplied value""" if value == True: self.maximize() else: self.unmaximize() def set_fullscreen(self, value): """Set the fullscreen state of the window from the supplied value""" if value == True: self.fullscreen() else: self.unfullscreen() def set_borderless(self, value): """Set the state of the window border from the supplied value""" self.set_decorated (not value) def set_hidden(self, value): """Set the visibility of the window from the supplied value""" if value == True: self.ignore_startup_show = True else: self.ignore_startup_show = False def set_iconified(self, value): """Set the minimised state of the window from the supplied value""" if value == True: self.iconify() def set_always_on_top(self, value): """Set the always on top window hint from the supplied value""" self.set_keep_above(value) def set_sticky(self, value): """Set the sticky hint from the supplied value""" if value == True: self.stick() def set_real_transparency(self, value=True): """Enable RGBA if supported on the current screen""" if self.is_composited() == False: value = False screen = self.get_screen() if value: dbg('setting rgba colormap') colormap = screen.get_rgba_colormap() else: dbg('setting rgb colormap') colormap = screen.get_rgb_colormap() if colormap: self.set_colormap(colormap) def show(self, startup=False): """Undo the startup show request if started in hidden mode""" #Present is necessary to grab focus when window is hidden from taskbar. #It is important to call present() before show(), otherwise the window #won't be brought to front if an another application has the focus. #Last note: present() will implicitly call gtk.Window.show() self.present() #Window must be shown, then hidden for the hotkeys to be registered if (self.ignore_startup_show and startup == True): self.hide() def add(self, widget, metadata=None): """Add a widget to the window by way of gtk.Window.add()""" maker = Factory() gtk.Window.add(self, widget) if maker.isinstance(widget, 'Terminal'): signals = {'close-term': self.closeterm, 'title-change': self.title.set_title, 'split-horiz': self.split_horiz, 'split-vert': self.split_vert, 'unzoom': self.unzoom, 'tab-change': self.tab_change, 'group-all': self.group_all, 'ungroup-all': self.ungroup_all, 'group-tab': self.group_tab, 'ungroup-tab': self.ungroup_tab, 'move-tab': self.move_tab, 'tab-new': [self.tab_new, widget], 'navigate': self.navigate_terminal} for signal in signals: args = [] handler = signals[signal] if isinstance(handler, list): args = handler[1:] handler = handler[0] self.connect_child(widget, signal, handler, *args) widget.grab_focus() def remove(self, widget): """Remove our child widget by way of gtk.Window.remove()""" gtk.Window.remove(self, widget) self.disconnect_child(widget) return(True) def get_children(self): """Return a single list of our child""" children = [] children.append(self.get_child()) return(children) def hoover(self): """Ensure we still have a reason to exist""" if not self.get_child(): self.emit('destroy') def closeterm(self, widget): """Handle a terminal closing""" Container.closeterm(self, widget) self.hoover() def split_axis(self, widget, vertical=True, cwd=None, sibling=None, widgetfirst=True): """Split the window""" if self.get_property('term_zoomed') == True: err("You can't split while a terminal is maximised/zoomed") return order = None maker = Factory() self.remove(widget) if vertical: container = maker.make('VPaned') else: container = maker.make('HPaned') self.set_pos_by_ratio = True if not sibling: sibling = maker.make('Terminal') sibling.set_cwd(cwd) sibling.spawn_child() if widget.group and self.config['split_to_group']: sibling.set_group(None, widget.group) if self.config['always_split_with_profile']: sibling.force_set_profile(None, widget.get_profile()) self.add(container) container.show_all() order = [widget, sibling] if widgetfirst is False: order.reverse() for term in order: container.add(term) container.show_all() sibling.grab_focus() while gtk.events_pending(): gtk.main_iteration_do(False) self.set_pos_by_ratio = False def zoom(self, widget, font_scale=True): """Zoom a terminal widget""" children = self.get_children() if widget in children: # This widget is a direct child of ours and we're a Window # so zooming is a no-op return self.zoom_data = widget.get_zoom_data() self.zoom_data['widget'] = widget self.zoom_data['old_child'] = children[0] self.zoom_data['font_scale'] = font_scale self.remove(self.zoom_data['old_child']) self.zoom_data['old_parent'].remove(widget) self.add(widget) self.set_property('term_zoomed', True) if font_scale: widget.cnxids.new(widget, 'size-allocate', widget.zoom_scale, self.zoom_data) widget.grab_focus() def unzoom(self, widget): """Restore normal terminal layout""" if not self.get_property('term_zoomed'): # We're not zoomed anyway dbg('Window::unzoom: not zoomed, no-op') return widget = self.zoom_data['widget'] if self.zoom_data['font_scale']: widget.vte.set_font(self.zoom_data['old_font']) self.remove(widget) self.add(self.zoom_data['old_child']) self.zoom_data['old_parent'].add(widget) widget.grab_focus() self.zoom_data = None self.set_property('term_zoomed', False) def rotate(self, widget, clockwise): """Rotate children in this window""" self.set_pos_by_ratio = True maker = Factory() # collect all paned children in breadth-first order paned = [] for child in self.get_children(): if maker.isinstance(child, 'Paned'): paned.append(child) for p in paned: for child in p.get_children(): if child not in paned and maker.isinstance(child, 'Paned'): paned.append(child) # then propagate the rotation for p in paned: p.rotate(widget, clockwise) self.show_all() widget.grab_focus() while gtk.events_pending(): gtk.main_iteration_do(False) self.set_pos_by_ratio = False def get_visible_terminals(self): """Walk down the widget tree to find all of the visible terminals. Mostly using Container::get_visible_terminals()""" terminals = {} if not hasattr(self, 'cached_maker'): self.cached_maker = Factory() maker = self.cached_maker child = self.get_child() if not child: return([]) # If our child is a Notebook, reset to work from its visible child if maker.isinstance(child, 'Notebook'): pagenum = child.get_current_page() child = child.get_nth_page(pagenum) if maker.isinstance(child, 'Container'): terminals.update(child.get_visible_terminals()) elif maker.isinstance(child, 'Terminal'): terminals[child] = child.get_allocation() else: err('Unknown child type %s' % type(child)) return(terminals) def get_focussed_terminal(self): """Find which terminal we want to have focus""" terminals = self.get_visible_terminals() for terminal in terminals: if terminal.vte.is_focus(): return(terminal) return(None) def deferred_set_rough_geometry_hints(self): # no parameters are used in set_rough_geometry_hints, so we can # use the set_rough_geometry_hints if self.pending_set_rough_geometry_hint == True: return self.pending_set_rough_geometry_hint = True gobject.idle_add(self.do_deferred_set_rough_geometry_hints) def do_deferred_set_rough_geometry_hints(self): self.pending_set_rough_geometry_hint = False self.set_rough_geometry_hints() def set_rough_geometry_hints(self): """Walk all the terminals along the top and left edges to fake up how many columns/rows we sort of have""" if not hasattr(self, 'cached_maker'): self.cached_maker = Factory() maker = self.cached_maker if maker.isinstance(self.get_child(), 'Notebook'): dbg("We don't currently support geometry hinting with tabs") return terminals = self.get_visible_terminals() column_sum = 0 row_sum = 0 for terminal in terminals: rect = terminal.get_allocation() if rect.x == 0: cols, rows = terminal.get_size() row_sum = row_sum + rows if rect.y == 0: cols, rows = terminal.get_size() column_sum = column_sum + cols if column_sum == 0 or row_sum == 0: dbg('column_sum=%s,row_sum=%s. No terminals found in >=1 axis' % (column_sum, row_sum)) return # FIXME: I don't think we should just use whatever font size info is on # the last terminal we inspected. Looking up the default profile font # size and calculating its character sizes would be rather expensive # though. font_width, font_height = terminal.get_font_size() total_font_width = font_width * column_sum total_font_height = font_height * row_sum win_width, win_height = self.get_size() extra_width = win_width - total_font_width extra_height = win_height - total_font_height dbg('setting geometry hints: (ewidth:%s)(eheight:%s),\ (fwidth:%s)(fheight:%s)' % (extra_width, extra_height, font_width, font_height)) self.set_geometry_hints(self, -1, -1, -1, -1, extra_width, extra_height, font_width, font_height, -1.0, -1.0) def tab_change(self, widget, num=None): """Change to a specific tab""" if num is None: err('must specify a tab to change to') maker = Factory() child = self.get_child() if not maker.isinstance(child, 'Notebook'): dbg('child is not a notebook, nothing to change to') return if num == -1: # Go to the next tab cur = child.get_current_page() pages = child.get_n_pages() if cur == pages - 1: num = 0 else: num = cur + 1 elif num == -2: # Go to the previous tab cur = child.get_current_page() if cur > 0: num = cur - 1 else: num = child.get_n_pages() - 1 child.set_current_page(num) # Work around strange bug in gtk-2.12.11 and pygtk-2.12.1 # Without it, the selection changes, but the displayed page doesn't # change child.set_current_page(child.get_current_page()) # FIXME: All of these (un)group_(all|tab) methods need refactoring work def group_all(self, widget): """Group all terminals""" # FIXME: Why isn't this being done by Terminator() ? group = _('All') self.terminator.create_group(group) for terminal in self.terminator.terminals: terminal.set_group(None, group) def ungroup_all(self, widget): """Ungroup all terminals""" for terminal in self.terminator.terminals: terminal.set_group(None, None) def group_tab(self, widget): """Group all terminals in the current tab""" maker = Factory() notebook = self.get_child() if not maker.isinstance(notebook, 'Notebook'): dbg('not in a notebook, refusing to group tab') return pagenum = notebook.get_current_page() while True: group = _('Tab %d') % pagenum if group not in self.terminator.groups: break pagenum += 1 for terminal in self.get_visible_terminals(): terminal.set_group(None, group) def ungroup_tab(self, widget): """Ungroup all terminals in the current tab""" maker = Factory() notebook = self.get_child() if not maker.isinstance(notebook, 'Notebook'): dbg('note in a notebook, refusing to ungroup tab') return for terminal in self.get_visible_terminals(): terminal.set_group(None, None) def move_tab(self, widget, direction): """Handle a keyboard shortcut for moving tab positions""" maker = Factory() notebook = self.get_child() if not maker.isinstance(notebook, 'Notebook'): dbg('not in a notebook, refusing to move tab %s' % direction) return dbg('moving tab %s' % direction) numpages = notebook.get_n_pages() page = notebook.get_current_page() child = notebook.get_nth_page(page) if direction == 'left': if page == 0: page = numpages else: page = page - 1 elif direction == 'right': if page == numpages - 1: page = 0 else: page = page + 1 else: err('unknown direction: %s' % direction) return notebook.reorder_child(child, page) def navigate_terminal(self, terminal, direction): """Navigate around terminals""" _containers, terminals = util.enumerate_descendants(self) visibles = self.get_visible_terminals() current = terminals.index(terminal) length = len(terminals) next = None if length <= 1 or len(visibles) <= 1: return if direction in ['next', 'prev']: tmpterms = copy.copy(terminals) tmpterms = tmpterms[current+1:] tmpterms.extend(terminals[0:current]) if direction == 'next': tmpterms.reverse() next = 0 while len(tmpterms) > 0: tmpitem = tmpterms.pop() if tmpitem in visibles: next = terminals.index(tmpitem) break elif direction in ['left', 'right', 'up', 'down']: layout = self.get_visible_terminals() allocation = terminal.get_allocation() possibles = [] # Get the co-ordinate of the appropriate edge for this direction edge = util.get_edge(allocation, direction) # Find all visible terminals which are, in their entirity, in the # direction we want to move for term in layout: rect = layout[term] if util.get_nav_possible(edge, rect, direction): possibles.append(term) if len(possibles) == 0: return # Find out how far away each of the possible terminals is, then # find the smallest distance. The winning terminals are all of # those who are that distance away. offsets = {} for term in possibles: rect = layout[term] offsets[term] = util.get_nav_offset(edge, rect, direction) keys = offsets.values() keys.sort() winners = [k for k, v in offsets.iteritems() if v == keys[0]] next = terminals.index(winners[0]) if len(winners) > 1: # Break an n-way tie using the cursor position term_alloc = terminal.allocation cursor_x = term_alloc.x + term_alloc.width / 2 cursor_y = term_alloc.y + term_alloc.height / 2 for term in winners: rect = layout[term] if util.get_nav_tiebreak(direction, cursor_x, cursor_y, rect): next = terminals.index(term) break; else: err('Unknown navigation direction: %s' % direction) if next is not None: terminals[next].grab_focus() def create_layout(self, layout): """Apply any config items from our layout""" if not layout.has_key('children'): err('layout describes no children: %s' % layout) return children = layout['children'] if len(children) != 1: # We're a Window, we can only have one child err('incorrect number of children for Window: %s' % layout) return child = children[children.keys()[0]] terminal = self.get_children()[0] dbg('Making a child of type: %s' % child['type']) if child['type'] == 'VPaned': self.split_axis(terminal, True) elif child['type'] == 'HPaned': self.split_axis(terminal, False) elif child['type'] == 'Notebook': self.tab_new() i = 2 while i < len(child['children']): self.tab_new() i = i + 1 elif child['type'] == 'Terminal': pass else: err('unknown child type: %s' % child['type']) return self.get_children()[0].create_layout(child)
class Notebook(Container, gtk.Notebook): """Class implementing a gtk.Notebook container""" window = None last_active_term = None pending_on_tab_switch = None pending_on_tab_switch_args = None def __init__(self, window): """Class initialiser""" if isinstance(window.get_child(), gtk.Notebook): err('There is already a Notebook at the top of this window') raise(ValueError) Container.__init__(self) gtk.Notebook.__init__(self) self.terminator = Terminator() self.window = window gobject.type_register(Notebook) self.register_signals(Notebook) self.connect('switch-page', self.deferred_on_tab_switch) self.configure() child = window.get_child() window.remove(child) window.add(self) window_last_active_term = window.last_active_term self.newtab(widget=child) if window_last_active_term: self.set_last_active_term(window_last_active_term) window.last_active_term = None self.show_all() def configure(self): """Apply widget-wide settings""" # FIXME: The old reordered handler updated Terminator.terminals with # the new order of terminals. We probably need to preserve this for # navigation to next/prev terminals. #self.connect('page-reordered', self.on_page_reordered) self.set_property('homogeneous', self.config['homogeneous_tabbar']) self.set_scrollable(self.config['scroll_tabbar']) if self.config['tab_position'] == 'hidden' or self.config['hide_tabbar']: self.set_show_tabs(False) else: self.set_show_tabs(True) pos = getattr(gtk, 'POS_%s' % self.config['tab_position'].upper()) self.set_tab_pos(pos) for tab in xrange(0, self.get_n_pages()): label = self.get_tab_label(self.get_nth_page(tab)) label.update_angle() style = gtk.RcStyle() style.xthickness = 0 style.ythickness = 0 self.modify_style(style) self.last_active_term = {} def create_layout(self, layout): """Apply layout configuration""" def child_compare(a, b): order_a = children[a]['order'] order_b = children[b]['order'] if (order_a == order_b): return 0 if (order_a < order_b): return -1 if (order_a > order_b): return 1 if not layout.has_key('children'): err('layout specifies no children: %s' % layout) return children = layout['children'] if len(children) <= 1: #Notebooks should have two or more children err('incorrect number of children for Notebook: %s' % layout) return num = 0 keys = children.keys() keys.sort(child_compare) for child_key in keys: child = children[child_key] dbg('Making a child of type: %s' % child['type']) if child['type'] == 'Terminal': pass elif child['type'] == 'VPaned': page = self.get_nth_page(num) self.split_axis(page, True) elif child['type'] == 'HPaned': page = self.get_nth_page(num) self.split_axis(page, False) num = num + 1 num = 0 for child_key in keys: page = self.get_nth_page(num) if not page: # This page does not yet exist, so make it self.newtab(children[child_key]) page = self.get_nth_page(num) if layout.has_key('labels'): labeltext = layout['labels'][num] if labeltext and labeltext != "None": label = self.get_tab_label(page) label.set_custom_label(labeltext) page.create_layout(children[child_key]) if layout.get('last_active_term', None): self.last_active_term[page] = make_uuid(layout['last_active_term'][num]) num = num + 1 if layout.has_key('active_page'): # Need to do it later, or layout changes result gobject.idle_add(self.set_current_page, int(layout['active_page'])) else: self.set_current_page(0) def split_axis(self, widget, vertical=True, cwd=None, sibling=None, widgetfirst=True): """Split the axis of a terminal inside us""" dbg('called for widget: %s' % widget) order = None page_num = self.page_num(widget) if page_num == -1: err('Notebook::split_axis: %s not found in Notebook' % widget) return label = self.get_tab_label(widget) self.remove(widget) maker = Factory() if vertical: container = maker.make('vpaned') else: container = maker.make('hpaned') self.get_toplevel().set_pos_by_ratio = True if not sibling: sibling = maker.make('terminal') sibling.set_cwd(cwd) sibling.spawn_child() if widget.group and self.config['split_to_group']: sibling.set_group(None, widget.group) if self.config['always_split_with_profile']: sibling.force_set_profile(None, widget.get_profile()) self.insert_page(container, None, page_num) self.set_tab_reorderable(container, True) self.set_tab_label(container, label) self.show_all() order = [widget, sibling] if widgetfirst is False: order.reverse() for terminal in order: container.add(terminal) self.set_current_page(page_num) self.show_all() while gtk.events_pending(): gtk.main_iteration_do(False) self.get_toplevel().set_pos_by_ratio = False gobject.idle_add(terminal.ensure_visible_and_focussed) def add(self, widget, metadata=None): """Add a widget to the container""" dbg('adding a new tab') self.newtab(widget=widget, metadata=metadata) def remove(self, widget): """Remove a widget from the container""" page_num = self.page_num(widget) if page_num == -1: err('%s not found in Notebook. Actual parent is: %s' % (widget, widget.get_parent())) return(False) self.remove_page(page_num) self.disconnect_child(widget) return(True) def replace(self, oldwidget, newwidget): """Replace a tab's contents with a new widget""" page_num = self.page_num(oldwidget) self.remove(oldwidget) self.add(newwidget) self.reorder_child(newwidget, page_num) def get_child_metadata(self, widget): """Fetch the relevant metadata for a widget which we'd need to recreate it when it's readded""" metadata = {} metadata['tabnum'] = self.page_num(widget) label = self.get_tab_label(widget) if not label: dbg('unable to find label for widget: %s' % widget) else: metadata['label'] = label.get_label() return metadata def get_children(self): """Return an ordered list of our children""" children = [] for page in xrange(0,self.get_n_pages()): children.append(self.get_nth_page(page)) return(children) def newtab(self, debugtab=False, widget=None, cwd=None, metadata=None, profile=None): """Add a new tab, optionally supplying a child widget""" dbg('making a new tab') maker = Factory() top_window = self.get_toplevel() if not widget: widget = maker.make('Terminal') if cwd: widget.set_cwd(cwd) widget.spawn_child(debugserver=debugtab) if profile and self.config['always_split_with_profile']: widget.force_set_profile(None, profile) signals = {'close-term': self.wrapcloseterm, 'split-horiz': self.split_horiz, 'split-vert': self.split_vert, 'title-change': self.propagate_title_change, 'unzoom': self.unzoom, 'tab-change': top_window.tab_change, 'group-all': top_window.group_all, 'group-all-toggle': top_window.group_all_toggle, 'ungroup-all': top_window.ungroup_all, 'group-tab': top_window.group_tab, 'group-tab-toggle': top_window.group_tab_toggle, 'ungroup-tab': top_window.ungroup_tab, 'move-tab': top_window.move_tab, 'tab-new': [top_window.tab_new, widget], 'navigate': top_window.navigate_terminal} if maker.isinstance(widget, 'Terminal'): for signal in signals: args = [] handler = signals[signal] if isinstance(handler, list): args = handler[1:] handler = handler[0] self.connect_child(widget, signal, handler, *args) if metadata and metadata.has_key('tabnum'): tabpos = metadata['tabnum'] else: tabpos = -1 label = TabLabel(self.window.get_title(), self) if metadata and metadata.has_key('label'): dbg('creating TabLabel with text: %s' % metadata['label']) label.set_custom_label(metadata['label']) label.connect('close-clicked', self.closetab) label.show_all() widget.show_all() dbg('inserting page at position: %s' % tabpos) self.insert_page(widget, None, tabpos) if maker.isinstance(widget, 'Terminal'): containers, objects = ([], [widget]) else: containers, objects = enumerate_descendants(widget) term_widget = None for term_widget in objects: if maker.isinstance(term_widget, 'Terminal'): self.set_last_active_term(term_widget.uuid) break self.set_tab_label(widget, label) gobject.idle_add(self.set_tab_label_packing, term_widget, not self.config['scroll_tabbar'], not self.config['scroll_tabbar'], gtk.PACK_START) self.set_tab_reorderable(widget, True) self.set_current_page(tabpos) self.show_all() if maker.isinstance(term_widget, 'Terminal'): widget.grab_focus() def wrapcloseterm(self, widget): """A child terminal has closed""" dbg('Notebook::wrapcloseterm: called on %s' % widget) if self.closeterm(widget): dbg('Notebook::wrapcloseterm: closeterm succeeded') self.hoover() else: dbg('Notebook::wrapcloseterm: closeterm failed') def closetab(self, widget, label): """Close a tab""" tabnum = None try: nb = widget.notebook except AttributeError: err('TabLabel::closetab: called on non-Notebook: %s' % widget) return for i in xrange(0, nb.get_n_pages() + 1): if label == nb.get_tab_label(nb.get_nth_page(i)): tabnum = i break if tabnum is None: err('TabLabel::closetab: %s not in %s. Bailing.' % (label, nb)) return maker = Factory() child = nb.get_nth_page(tabnum) if maker.isinstance(child, 'Terminal'): dbg('Notebook::closetab: child is a single Terminal') del nb.last_active_term[child] child.close() # FIXME: We only do this del and return here to avoid removing the # page below, which child.close() implicitly does del(label) return elif maker.isinstance(child, 'Container'): dbg('Notebook::closetab: child is a Container') result = self.construct_confirm_close(self.window, _('tab')) if result == gtk.RESPONSE_ACCEPT: containers = None objects = None containers, objects = enumerate_descendants(child) while len(objects) > 0: descendant = objects.pop() descendant.close() while gtk.events_pending(): gtk.main_iteration() return else: dbg('Notebook::closetab: user cancelled request') return else: err('Notebook::closetab: child is unknown type %s' % child) return def resizeterm(self, widget, keyname): """Handle a keyboard event requesting a terminal resize""" raise NotImplementedError('resizeterm') def zoom(self, widget, fontscale = False): """Zoom a terminal""" raise NotImplementedError('zoom') def unzoom(self, widget): """Unzoom a terminal""" raise NotImplementedError('unzoom') def find_tab_root(self, widget): """Look for the tab child which is or ultimately contains the supplied widget""" parent = widget.get_parent() previous = parent while parent is not None and parent is not self: previous = parent parent = parent.get_parent() if previous == self: return(widget) else: return(previous) def update_tab_label_text(self, widget, text): """Update the text of a tab label""" notebook = self.find_tab_root(widget) label = self.get_tab_label(notebook) if not label: err('Notebook::update_tab_label_text: %s not found' % widget) return label.set_label(text) def hoover(self): """Clean up any empty tabs and if we only have one tab left, die""" numpages = self.get_n_pages() while numpages > 0: numpages = numpages - 1 page = self.get_nth_page(numpages) if not page: dbg('Removing empty page: %d' % numpages) self.remove_page(numpages) if self.get_n_pages() == 1: dbg('Last page, removing self') child = self.get_nth_page(0) self.remove_page(0) parent = self.get_parent() parent.remove(self) self.cnxids.remove_all() parent.add(child) del(self) # Find the last terminal in the new parent and give it focus terms = parent.get_visible_terminals() terms.keys()[-1].grab_focus() def page_num_descendant(self, widget): """Find the tabnum of the tab containing a widget at any level""" tabnum = self.page_num(widget) dbg("widget is direct child if not equal -1 - tabnum: %d" % tabnum) while tabnum == -1 and widget.get_parent(): widget = widget.get_parent() tabnum = self.page_num(widget) dbg("found tabnum containing widget: %d" % tabnum) return tabnum def set_last_active_term(self, uuid): """Set the last active term for uuid""" widget = self.terminator.find_terminal_by_uuid(uuid.urn) if not widget: err("Cannot find terminal with uuid: %s, so cannot make it active" % (uuid.urn)) return tabnum = self.page_num_descendant(widget) if tabnum == -1: err("No tabnum found for terminal with uuid: %s" % (uuid.urn)) return nth_page = self.get_nth_page(tabnum) self.last_active_term[nth_page] = uuid def clean_last_active_term(self): """Clean up old entries in last_active_term""" if self.terminator.doing_layout == True: return last_active_term = {} for tabnum in xrange(0, self.get_n_pages()): nth_page = self.get_nth_page(tabnum) if nth_page in self.last_active_term: last_active_term[nth_page] = self.last_active_term[nth_page] self.last_active_term = last_active_term def deferred_on_tab_switch(self, notebook, page, page_num, data=None): """Prime a single idle tab switch signal, using the most recent set of params""" tabs_last_active_term = self.last_active_term.get(self.get_nth_page(page_num), None) data = {'tabs_last_active_term':tabs_last_active_term} self.pending_on_tab_switch_args = (notebook, page, page_num, data) if self.pending_on_tab_switch == True: return gobject.idle_add(self.do_deferred_on_tab_switch) self.pending_on_tab_switch = True def do_deferred_on_tab_switch(self): """Perform the latest tab switch signal, and resetting the pending flag""" self.on_tab_switch(*self.pending_on_tab_switch_args) self.pending_on_tab_switch = False self.pending_on_tab_switch_args = None def on_tab_switch(self, notebook, page, page_num, data=None): """Do the real work for a tab switch""" tabs_last_active_term = data['tabs_last_active_term'] if tabs_last_active_term: term = self.terminator.find_terminal_by_uuid(tabs_last_active_term.urn) gobject.idle_add(term.ensure_visible_and_focussed) return True
class TerminalPopupMenu(object): """Class implementing the Terminal context menu""" terminal = None terminator = None config = None def __init__(self, terminal): """Class initialiser""" self.terminal = terminal self.terminator = Terminator() self.config = Config() def show(self, widget, event=None): """Display the context menu""" terminal = self.terminal menu = gtk.Menu() url = None button = None time = None self.config.set_profile(terminal.get_profile()) if event: url = terminal.check_for_url(event) button = event.button time = event.time else: time = 0 button = 3 if url: dbg("URL matches id: %d" % url[1]) if not url[1] in terminal.matches.values(): err("Unknown URL match id: %d" % url[1]) dbg("Available matches: %s" % terminal.matches) nameopen = None namecopy = None if url[1] == terminal.matches['email']: nameopen = _('_Send email to...') namecopy = _('_Copy email address') elif url[1] == terminal.matches['voip']: nameopen = _('Ca_ll VoIP address') namecopy = _('_Copy VoIP address') elif url[1] in terminal.matches.values(): # This is a plugin match for pluginname in terminal.matches: if terminal.matches[pluginname] == url[1]: break dbg("Found match ID (%d) in terminal.matches plugin %s" % (url[1], pluginname)) registry = plugin.PluginRegistry() registry.load_plugins() plugins = registry.get_plugins_by_capability('url_handler') for urlplugin in plugins: if urlplugin.handler_name == pluginname: dbg("Identified matching plugin: %s" % urlplugin.handler_name) nameopen = _(urlplugin.nameopen) namecopy = _(urlplugin.namecopy) break if not nameopen: nameopen = _('_Open link') if not namecopy: namecopy = _('_Copy address') icon = gtk.image_new_from_stock(gtk.STOCK_JUMP_TO, gtk.ICON_SIZE_MENU) item = gtk.ImageMenuItem(nameopen) item.set_property('image', icon) item.connect('activate', lambda x: terminal.open_url(url, True)) menu.append(item) item = gtk.MenuItem(namecopy) item.connect('activate', lambda x: terminal.clipboard.set_text(terminal.prepare_url(url))) menu.append(item) menu.append(gtk.MenuItem()) item = gtk.ImageMenuItem(gtk.STOCK_COPY) item.connect('activate', lambda x: terminal.vte.copy_clipboard()) item.set_sensitive(terminal.vte.get_has_selection()) menu.append(item) item = gtk.ImageMenuItem(gtk.STOCK_PASTE) item.connect('activate', lambda x: terminal.paste_clipboard()) menu.append(item) menu.append(gtk.MenuItem()) if not terminal.is_zoomed(): item = gtk.ImageMenuItem(_('Split H_orizontally')) image = gtk.Image() image.set_from_icon_name(APP_NAME + '_horiz', gtk.ICON_SIZE_MENU) item.set_image(image) if hasattr(item, 'set_always_show_image'): item.set_always_show_image(True) item.connect('activate', lambda x: terminal.emit('split-horiz', self.terminator.pid_cwd(self.terminal.pid))) menu.append(item) item = gtk.ImageMenuItem(_('Split V_ertically')) image = gtk.Image() image.set_from_icon_name(APP_NAME + '_vert', gtk.ICON_SIZE_MENU) item.set_image(image) if hasattr(item, 'set_always_show_image'): item.set_always_show_image(True) item.connect('activate', lambda x: terminal.emit('split-vert', self.terminator.pid_cwd(self.terminal.pid))) menu.append(item) item = gtk.MenuItem(_('Open _Tab')) item.connect('activate', lambda x: terminal.emit('tab-new', False, terminal)) menu.append(item) if self.terminator.debug_address is not None: item = gtk.MenuItem(_('Open _Debug Tab')) item.connect( 'activate', lambda x: terminal.emit('tab-new', True, terminal)) menu.append(item) menu.append(gtk.MenuItem()) item = gtk.ImageMenuItem(gtk.STOCK_CLOSE) item.connect('activate', lambda x: terminal.close()) menu.append(item) menu.append(gtk.MenuItem()) if not terminal.is_zoomed(): item = gtk.MenuItem(_('_Zoom terminal')) item.connect('activate', terminal.zoom) menu.append(item) item = gtk.MenuItem(_('Ma_ximise terminal')) item.connect('activate', terminal.maximise) menu.append(item) menu.append(gtk.MenuItem()) else: item = gtk.MenuItem(_('_Restore all terminals')) item.connect('activate', terminal.unzoom) menu.append(item) menu.append(gtk.MenuItem()) if self.config['show_titlebar'] == False: item = gtk.MenuItem(_('Grouping')) submenu = self.terminal.populate_group_menu() submenu.show_all() item.set_submenu(submenu) menu.append(item) menu.append(gtk.MenuItem()) item = gtk.CheckMenuItem(_('Show _scrollbar')) item.set_active(terminal.scrollbar.get_property('visible')) item.connect('toggled', lambda x: terminal.do_scrollbar_toggle()) menu.append(item) if hasattr(gtk, 'Builder'): item = gtk.MenuItem(_('_Preferences')) item.connect('activate', lambda x: PrefsEditor(self.terminal)) menu.append(item) profilelist = self.config.list_profiles() if len(profilelist) > 1: item = gtk.MenuItem(_('Profiles')) submenu = gtk.Menu() item.set_submenu(submenu) menu.append(item) current = terminal.get_profile() group = None for profile in profilelist: item = gtk.RadioMenuItem(group, profile.capitalize()) if profile == current: item.set_active(True) item.connect('activate', terminal.force_set_profile, profile) submenu.append(item) self.add_encoding_items(menu) try: menuitems = [] registry = plugin.PluginRegistry() registry.load_plugins() plugins = registry.get_plugins_by_capability('terminal_menu') for menuplugin in plugins: menuplugin.callback(menuitems, menu, terminal) if len(menuitems) > 0: menu.append(gtk.MenuItem()) for menuitem in menuitems: menu.append(menuitem) except Exception, ex: err('TerminalPopupMenu::show: %s' % ex) menu.show_all() menu.popup(None, None, None, button, time) return (True)
def __init__(self, terminal): """Class initialiser""" self.terminal = terminal self.terminator = Terminator() self.config = Config()
class DBTools: """ Class: DBTools -------------- Handles requests to a particular database (as specified in the CONFIG file). """ def __init__(self, user, database): # The database connection parameters are specified in the CONFIG.py file. # Contains the reference to the database object. self.db = None # The database cursor. self.cursor = None # The current connection timeout limit. self.timeout = CONNECTION_TIMEOUT # The savepoints. self.savepoints = [] # The user to connect with. If no user is specified, picks the first user # in the dictionary. self.user = user if user is not None else LOGIN.keys()[0] # The name of the database to connect to. If none is specified, use # <user>_db as the default database. self.database = database if database is not None else "%s_db" % self.user # Separate database connection used to terminate queries. If the terminator # cannot start, the grading cannot occur. try: self.terminator = Terminator(self.user, self.database) except mysql.connector.errors.Error: err("Could not start up terminator connection! Any unruly queries " + "must be manually killed!") # --------------------------- Database Utilities --------------------------- # def close_db_connection(self): """ Function: close_db_connection ----------------------------- Close the database connection (only if it is already open) and any running queries. """ if self.db: # Consume remaining output. for _ in self.cursor: pass # Kill any remaining queries and close the database connection. try: self.kill_query() self.cursor.close() self.db.close() # Can't do anything if there is a database error. except mysql.connector.errors.Error: pass def commit(self): """ Function: commit ---------------- Commits the current transaction. Destroys any savepoints. """ try: self.db.commit() except mysql.connector.errors.Error as e: raise DatabaseError(e) self.savepoints = [] def get_cursor(self): """ Function: get_cursor -------------------- Gets the cursor. Assumes the database has already been connected. """ return self.cursor def get_db_connection(self, timeout=None, close=True): """ Function: get_db_connection --------------------------- Get a new database connection with a specified timeout (defaults to CONNECTION_TIMEOUT specified in the CONFIG file). Closes the old connection if there was one. timeout: The connection timeout. close: Whether or not to close the old database connection beforehand. Should set to False if a timeout occurred just before the call to this function. returns: A database connection object. """ if self.db and self.db.is_connected(): # If timeout isn't specified, check if we're already at the default. if timeout is None and self.timeout == CONNECTION_TIMEOUT: return self.db # If the timeout is the same as before, then don't change anything. if timeout is not None and timeout == self.timeout: return self.db # Close any old connections and make another one with the new setting. if close: self.close_db_connection() self.timeout = timeout or CONNECTION_TIMEOUT log("New timeout: %d" % self.timeout) try: self.db = mysql.connector.connect(user=self.user, password=LOGIN[self.user], host=HOST, database=self.database, port=PORT, connection_timeout=self.timeout, autocommit=False) self.cursor = self.db.cursor(buffered=True) except mysql.connector.errors.Error as e: raise DatabaseError(e) return self def get_state(self): """ Function: get_state ------------------- Gets the current state of the database, which includes the tables, foreign, keys, views, functions, procedures, and triggers. returns: A DatabaseState object which contains the current state. """ state = DatabaseState() try: # Get tables and their foreign keys. state.tables = self.execute_sql( "SELECT table_name FROM information_schema.tables " "WHERE table_type='BASE TABLE'" ).results state.foreign_keys = self.execute_sql( "SELECT DISTINCT table_name, constraint_name FROM " "information_schema.table_constraints WHERE constraint_type='FOREIGN KEY'" ).results # Get views, functions, procedures, and triggers. state.views = self.execute_sql( "SELECT table_name FROM information_schema.views" ).results state.functions = self.execute_sql( "SELECT routine_name FROM information_schema.routines " "WHERE routine_type='FUNCTION'" ).results state.procedures = self.execute_sql( "SELECT routine_name FROM information_schema.routines " "WHERE routine_type='PROCEDURE'" ).results state.triggers = self.execute_sql( "SELECT trigger_name FROM information_schema.triggers" ).results except (mysql.connector.errors.Error, DatabaseError, TimeoutError): raise return state def kill_query(self): """ Function: kill_query -------------------- Kills the running query by terminating the connection. """ if not self.db or not self.db.is_connected(): return thread_id = self.db.connection_id try: self.terminator.terminate(thread_id) except mysql.connector.errors.Error: err("Unable to kill %d (was probably already killed)." % thread_id) # If the terminator doesn't even exist, then this is a problem. except AttributeError: err("Terminator doesn't exist to kill queries!", True) self.savepoints = [] def purge_db(self): """ Function: purge_db ------------------ Remove everything from the database. """ state = self.get_state() self.reset_state(DatabaseState(), state) def release(self, savepoint): """ Function: release ----------------- Releases the named savepoint. savepoint: The savepoint to release. """ if savepoint not in self.savepoints: return self.savepoints.remove(savepoint) try: self.execute_sql("RELEASE SAVEPOINT %s" % savepoint) except mysql.connector.errors.Error: pass def reset_state(self, old, new): """ Function: reset_state --------------------- Resets the state of the database from 'new' back to 'old'. This involves removing all functions, views, functions, procedures, and triggers that have been newly created. old: The old state of the database to be reverted back to. new: The new (current) state of the database. """ new.subtract(old) try: # Drop all functions procedures, and triggers first. # if VERBOSE: # print("-" * 78) # print("Resetting state.") for trig in new.triggers: sql = "DROP TRIGGER IF EXISTS %s" % trig # if VERBOSE: # print(sql) self.execute_sql(sql) for proc in new.procedures: sql = "DROP PROCEDURE IF EXISTS %s" % proc # if VERBOSE: # print(sql) self.execute_sql(sql) for func in new.functions: sql = "DROP FUNCTION IF EXISTS %s" % func # if VERBOSE: # print(sql) self.execute_sql(sql) # Drop views. for view in new.views: sql = "DROP VIEW IF EXISTS %s" % view # if VERBOSE: # print(sql) self.execute_sql(sql) # Drop tables. First must drop foreign keys on the tables in order to be # able to drop the tables without any errors. for (table, fk) in new.foreign_keys: self.execute_sql("ALTER TABLE %s DROP FOREIGN KEY %s" % (table, fk)) for table in new.tables: self.execute_sql("DROP TABLE IF EXISTS %s" % table) except (mysql.connector.errors.Error, DatabaseError, TimeoutError): err("Could not reset database state. Possible errors in future grading.") # Remove all savepoints. self.savepoints = [] def rollback(self, savepoint=None): """ Function: rollback ------------------ Rolls back a database transaction, if currently in one. If a savepoint is named, rolls back to the named savepoint, otherwise, does a normal rollback which will remove all savepoints. savepoint: The savepoint to rollback to, if specified. """ if self.db.in_transaction: # Roll back to the named savepoint. All savepoints created after this # savepoint are deleted. if savepoint and savepoint in self.savepoints: # If rolling back a savepoint failed, then a commit must have occurred # at some point. Rollback as far as we can just to be safe. try: self.execute_sql("ROLLBACK TO %s" % savepoint) except: self.db.rollback() self.savepoints = self.savepoints[0:self.savepoints.index(savepoint)+1] else: try: self.db.rollback() except mysql.connector.errors.Error as e: raise DatabaseError(e) self.savepoints = [] def savepoint(self, savepoint): """ Function: savepoint ------------------- Creates a savepoint with the specified name. savepoint: The name of the savepoint. """ try: self.execute_sql("SAVEPOINT %s" % savepoint) except mysql.connector.errors.Error: err("Could not create savepoint %s!" % savepoint) # If this savepoint name already exists, add and remove it. if savepoint in self.savepoints: self.savepoints.remove(savepoint) self.savepoints.append(savepoint) def start_transaction(self): """ Function: start_transaction --------------------------- Starts a database transaction, if not already in one. """ self.db.commit() if not self.db.in_transaction: try: self.db.start_transaction() except mysql.connector.errors.Error as e: raise DatabaseError(e) # ----------------------------- Query Utilities ---------------------------- # def clear_cursor(self): try: self.cursor.fetchall() except Exception as e: if str(e) == 'No result set to fetch from.': pass else: raise def execute_sql(self, sql, setup=None, teardown=None, cached=False): """ Function: execute_sql --------------------- Runs one or more queries as well as the setup and teardown necessary for that query (if provided). sql: The SQL query to run. setup: The setup query to run before executing the actual query. teardown: The teardown query to run after executing the actual query. cached: Whether or not the result should be pulled from the cache. True if so, False otherwise. returns: A Result object containing the result. """ # Run the query setup. result = Result() if setup is not None: self.run_multi(setup) try: # if VERBOSE: # print("-" * 78) # print("Running SQL statement:\n%s\n(use cached result = %s)" % (sql, str(cached))) result = self.run_multi(sql, cached) # Run the query teardown. finally: if teardown is not None: # if VERBOSE: # print("-" * 78) # print("Running teardown:\n%s" % teardown) self.run_multi(teardown) return result def get_column_names(self): """ Function: get_column_names -------------------------- Gets the column names of the result. """ if self.cursor.description is None: return [] return [col[0] for col in self.cursor.description] def get_column_types(self): """ Function: get_column_types -------------------------- Gets the column types of the result. """ if self.cursor.description is None: return [] return [ (float if col[1] in FLOAT_FIELD_TYPES else str) \ for col in self.cursor.description ] def get_results(self): """ Function: results ----------------- Get the results of a query. """ result = Result() # Get the query results and schema. rows = [row for row in self.cursor] if len(rows) > 0: result.results = rows result.schema = self.get_schema() result.col_names = self.get_column_names() result.col_types = self.get_column_types() # Pretty-printed output. result.output = prettyprint(result.results, self.get_column_names()) return result def get_schema(self): """ Function: get_schema -------------------- Gets the schema of the result. Returns a list of tuples, where each tuple is of the form (column_name, type, None, None, None, None, null_ok, flags). """ return self.cursor.description def run_multi(self, queries, cached=False): """ Function: run_multi ------------------- Runs multiple SQL statements at once. """ # Consume old results if needed. [row for row in self.cursor] sql_list = split(queries) # Consume any additional result-sets that might have been left # on the connection. # try: # while self.cursor.nextset(): # pass # except Error: # pass result = Result() for sql in sql_list: sql = sql.rstrip().rstrip(";") if len(sql) == 0: continue query_results = Cache.get(sql) # Results are not to be cached or are not in the cache and needs to # be cached. Run the query. if not query_results or not cached: try: self.clear_cursor() self.cursor.execute(sql) # except DatabaseError as e: # if 'already exists' in str(e): # print("[warning: %s]" % str(e)) # else: # # Reraise the exception # raise e # If the query times out. except mysql.connector.errors.OperationalError as e: raise TimeoutError(e) # If something is wrong with their query. except mysql.connector.errors.ProgrammingError as e: if 'already exists' in str(e): log("[warning: %s]" % str(e)) else: raise DatabaseError(e) # If the query can't be run as a single query, attempt to do it with a # multi-line query. except mysql.connector.errors.Error as e: print("ERROR while executing SQL: %s" % sql) print(str(e)) raise DatabaseError(e) query_results = self.get_results() if cached: Cache.put(sql, query_results) result = query_results # If no longer in a transaction, remove all savepoints. if not self.db.in_transaction: self.savepoints = [] return result # ----------------------------- File Utilities ----------------------------- # def import_file(self, assignment, f): """ Function: import_files ---------------------- Imports raw data files into the database. This uses the "mysqlimport" command on the terminal. We will have to invoke the command via Python. assignment: The assignment name, which is prepended to all the files. f: The file to import. """ log("\nImporting file " + f + "...\n") filename = ASSIGNMENT_DIR + assignment + "/" + f # Make sure the file exists. if not os.path.exists(filename): err("File to import %s does not exist!" % filename, True) try: subprocess.call("mysqlimport -h " + HOST + " -P " + PORT + " -u " + self.user + " -p" + LOGIN[self.user] + " --delete --local " + self.database + " " + filename, shell=True) except OSError: err("Could not import file %s! The 'mysqlimport' utility does not exist!" % filename, True) def source_file(self, assignment, f): """ Function: source_file --------------------- Sources a file into the database. Since the "source" command is for the MySQL command-line interface, we have to parse the source file and run each command one at a time. assignment: The assignment name, which is prepended to all the files. f: The source file to source. """ try: fname = ASSIGNMENT_DIR + assignment + "/" + f f = codecs.open(fname, "r", "utf-8") except IOError: err("Could not find or open sourced file %s!" % fname, True) sql_list = split(preprocess_sql(f)) for sql in sql_list: # Skip this line if there is nothing in it. if len(sql.strip()) == 0: continue # Otherwise execute each line. Output must be consumed for the query # to actually be executed. sql = sql.rstrip() # if VERBOSE: # print("-" * 78) # print("source_file(%s): Running SQL command:\n%s" % (fname, sql)) for _ in self.cursor.execute(sql, multi=True): self.clear_cursor() self.commit() f.close()
class DBusService(Borg, dbus.service.Object): """DBus Server class. This is implemented as a Borg""" bus_name = None bus_path = None terminator = None def __init__(self): """Class initialiser""" Borg.__init__(self, self.__class__.__name__) self.prepare_attributes() dbus.service.Object.__init__(self, self.bus_name, BUS_PATH) def prepare_attributes(self): """Ensure we are populated""" if not self.bus_name: dbg('Checking for bus name availability: %s' % BUS_NAME) bus = dbus.SessionBus() proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') flags = 1 | 4 # allow replacement | do not queue if not proxy.RequestName(BUS_NAME, dbus.UInt32(flags)) in (1, 4): dbg('bus name unavailable: %s' % BUS_NAME) raise dbus.exceptions.DBusException( "Couldn't get DBus name %s: Name exists" % BUS_NAME) self.bus_name = dbus.service.BusName(BUS_NAME, bus=dbus.SessionBus()) if not self.bus_path: self.bus_path = BUS_PATH if not self.terminator: self.terminator = Terminator() @dbus.service.method(BUS_NAME) def new_window(self, layout='default'): """Create a new Window""" dbg('dbus method called: new_window') self.terminator.create_layout(layout) self.terminator.layout_done() @dbus.service.method(BUS_NAME) def terminal_hsplit(self, uuid=None): """Split a terminal horizontally, by UUID""" return self.terminal_split(uuid, True) @dbus.service.method(BUS_NAME) def terminal_vsplit(self, uuid=None): """Split a terminal vertically, by UUID""" return self.terminal_split(uuid, False) def terminal_split(self, uuid, horiz): """Split a terminal horizontally or vertically, by UUID""" dbg('dbus method called: terminal_hsplit') if not uuid: return "ERROR: No UUID specified" terminal = self.terminator.find_terminal_by_uuid(uuid) if not terminal: return "ERROR: Terminal with supplied UUID not found" if horiz: terminal.key_split_horiz() else: terminal.key_split_vert() @dbus.service.method(BUS_NAME) def get_terminals(self, uuid): """Return a list of all the terminals""" return [x.uuid.urn for x in self.terminator.terminals]
class Titlebar(gtk.EventBox): """Class implementing the Titlebar widget""" terminator = None terminal = None config = None oldtitle = None termtext = None sizetext = None label = None ebox = None groupicon = None grouplabel = None groupentry = None bellicon = None __gsignals__ = { 'clicked': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), 'edit-done': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), 'create-group': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)), } def __init__(self, terminal): """Class initialiser""" gtk.EventBox.__init__(self) self.__gobject_init__() self.terminator = Terminator() self.terminal = terminal self.config = self.terminal.config self.label = EditableLabel() self.label.connect('edit-done', self.on_edit_done) self.ebox = gtk.EventBox() grouphbox = gtk.HBox() self.grouplabel = gtk.Label() self.groupicon = gtk.Image() self.bellicon = gtk.Image() self.bellicon.set_no_show_all(True) self.groupentry = gtk.Entry() self.groupentry.set_no_show_all(True) self.groupentry.connect('focus-out-event', self.groupentry_cancel) self.groupentry.connect('activate', self.groupentry_activate) self.groupentry.connect('key-press-event', self.groupentry_keypress) groupsend_type = self.terminator.groupsend_type if self.terminator.groupsend == groupsend_type['all']: icon_name = 'all' elif self.terminator.groupsend == groupsend_type['group']: icon_name = 'group' elif self.terminator.groupsend == groupsend_type['off']: icon_name = 'off' self.set_from_icon_name('_active_broadcast_%s' % icon_name, gtk.ICON_SIZE_MENU) grouphbox.pack_start(self.groupicon, False, True, 2) grouphbox.pack_start(self.grouplabel, False, True, 2) grouphbox.pack_start(self.groupentry, False, True, 2) self.ebox.add(grouphbox) self.ebox.show_all() self.bellicon.set_from_icon_name('terminal-bell', gtk.ICON_SIZE_MENU) hbox = gtk.HBox() hbox.pack_start(self.ebox, False, True, 0) hbox.pack_start(gtk.VSeparator(), False, True, 0) hbox.pack_start(self.label, True, True) hbox.pack_end(self.bellicon, False, False, 2) self.add(hbox) hbox.show_all() self.set_no_show_all(True) self.show() self.connect('button-press-event', self.on_clicked) def connect_icon(self, func): """Connect the supplied function to clicking on the group icon""" self.ebox.connect('button-press-event', func) def update(self, other=None): """Update our contents""" default_bg = False if self.config['title_hide_sizetext']: self.label.set_text("%s" % self.termtext) else: self.label.set_text("%s %s" % (self.termtext, self.sizetext)) if other: term = self.terminal terminator = self.terminator if other == 'window-focus-out': title_fg = self.config['title_inactive_fg_color'] title_bg = self.config['title_inactive_bg_color'] icon = '_receive_off' default_bg = True group_fg = self.config['title_inactive_fg_color'] group_bg = self.config['title_inactive_bg_color'] elif term != other and term.group and term.group == other.group: if terminator.groupsend == terminator.groupsend_type['off']: title_fg = self.config['title_inactive_fg_color'] title_bg = self.config['title_inactive_bg_color'] icon = '_receive_off' default_bg = True else: title_fg = self.config['title_receive_fg_color'] title_bg = self.config['title_receive_bg_color'] icon = '_receive_on' group_fg = self.config['title_receive_fg_color'] group_bg = self.config['title_receive_bg_color'] elif term != other and not term.group or term.group != other.group: if terminator.groupsend == terminator.groupsend_type['all']: title_fg = self.config['title_receive_fg_color'] title_bg = self.config['title_receive_bg_color'] icon = '_receive_on' else: title_fg = self.config['title_inactive_fg_color'] title_bg = self.config['title_inactive_bg_color'] icon = '_receive_off' default_bg = True group_fg = self.config['title_inactive_fg_color'] group_bg = self.config['title_inactive_bg_color'] else: # We're the active terminal title_fg = self.config['title_transmit_fg_color'] title_bg = self.config['title_transmit_bg_color'] if terminator.groupsend == terminator.groupsend_type['all']: icon = '_active_broadcast_all' elif terminator.groupsend == terminator.groupsend_type['group']: icon = '_active_broadcast_group' else: icon = '_active_broadcast_off' group_fg = self.config['title_transmit_fg_color'] group_bg = self.config['title_transmit_bg_color'] self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(title_fg)) self.grouplabel.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse(group_fg)) self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(title_bg)) if not self.get_desired_visibility(): if default_bg == True: color = term.get_style().bg[gtk.STATE_NORMAL] else: color = gtk.gdk.color_parse(title_bg) self.update_visibility() self.ebox.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(group_bg)) self.set_from_icon_name(icon, gtk.ICON_SIZE_MENU) def update_visibility(self): """Make the titlebar be visible or not""" if not self.get_desired_visibility(): dbg('hiding titlebar') self.hide() self.label.hide() else: dbg('showing titlebar') self.show() self.label.show() def get_desired_visibility(self): """Returns True if the titlebar is supposed to be visible. False if not""" if self.editing() == True or self.terminal.group: dbg('implicit desired visibility') return(True) else: dbg('configured visibility: %s' % self.config['show_titlebar']) return(self.config['show_titlebar']) def set_from_icon_name(self, name, size = gtk.ICON_SIZE_MENU): """Set an icon for the group label""" if not name: self.groupicon.hide() return self.groupicon.set_from_icon_name(APP_NAME + name, size) self.groupicon.show() def update_terminal_size(self, width, height): """Update the displayed terminal size""" self.sizetext = "%sx%s" % (width, height) self.update() def set_terminal_title(self, widget, title): """Update the terminal title""" self.termtext = title self.update() # Return False so we don't interrupt any chains of signal handling return False def set_group_label(self, name): """Set the name of the group""" if name: self.grouplabel.set_text(name) self.grouplabel.show() else: self.grouplabel.set_text('') self.grouplabel.hide() self.update_visibility() def on_clicked(self, widget, event): """Handle a click on the label""" self.show() self.label.show() self.emit('clicked') def on_edit_done(self, widget): """Re-emit an edit-done signal from an EditableLabel""" self.emit('edit-done') def editing(self): """Determine if we're currently editing a group name or title""" return(self.groupentry.get_property('visible') or self.label.editing()) def create_group(self): """Create a new group""" if self.terminal.group: self.groupentry.set_text(self.terminal.group) else: defaultmembers=['Alpha','Beta','Gamma','Delta','Epsilon','Zeta','Eta', 'Theta','Iota','Kappa','Lambda','Mu','Nu','Xi', 'Omnicron','Pi','Rho','Sigma','Tau','Upsilon','Phi', 'Chi','Psi','Omega'] currentgroups=set(self.terminator.groups) for i in range(1,4): defaultgroups=set(map(''.join, list(itertools.product(defaultmembers,repeat=i)))) freegroups = list(defaultgroups-currentgroups) if freegroups: self.groupentry.set_text(random.choice(freegroups)) break else: self.groupentry.set_text('') self.groupentry.show() self.grouplabel.hide() self.groupentry.grab_focus() self.update_visibility() def groupentry_cancel(self, widget, event): """Hide the group name entry""" self.groupentry.set_text('') self.groupentry.hide() self.grouplabel.show() self.get_parent().grab_focus() def groupentry_activate(self, widget): """Actually cause a group to be created""" groupname = self.groupentry.get_text() or None dbg('Titlebar::groupentry_activate: creating group: %s' % groupname) self.groupentry_cancel(None, None) last_focused_term=self.terminator.last_focused_term if self.terminal.targets_for_new_group: [term.titlebar.emit('create-group', groupname) for term in self.terminal.targets_for_new_group] self.terminal.targets_for_new_group = None else: self.emit('create-group', groupname) last_focused_term.grab_focus() self.terminator.focus_changed(last_focused_term) def groupentry_keypress(self, widget, event): """Handle keypresses on the entry widget""" key = gtk.gdk.keyval_name(event.keyval) if key == 'Escape': self.groupentry_cancel(None, None) def icon_bell(self): """A bell signal requires we display our bell icon""" self.bellicon.show() gobject.timeout_add(1000, self.icon_bell_hide) def icon_bell_hide(self): """Handle a timeout which means we now hide the bell icon""" self.bellicon.hide() return(False) def get_custom_string(self): """If we have a custom string set, return it, otherwise None""" if self.label.is_custom(): return(self.label.get_text()) else: return(None) def set_custom_string(self, string): """Set a custom string""" self.label.set_text(string) self.label.set_custom()
class DBusService(Borg, dbus.service.Object): """DBus Server class. This is implemented as a Borg""" bus_name = None bus_path = None terminator = None def __init__(self): """Class initialiser""" Borg.__init__(self, self.__class__.__name__) self.prepare_attributes() dbus.service.Object.__init__(self, self.bus_name, BUS_PATH) def prepare_attributes(self): """Ensure we are populated""" if not self.bus_name: dbg('Checking for bus name availability: %s' % BUS_NAME) bus = dbus.SessionBus() proxy = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') flags = 1 | 4 # allow replacement | do not queue if not proxy.RequestName(BUS_NAME, dbus.UInt32(flags)) in (1, 4): dbg('bus name unavailable: %s' % BUS_NAME) raise dbus.exceptions.DBusException( "Couldn't get DBus name %s: Name exists" % BUS_NAME) self.bus_name = dbus.service.BusName(BUS_NAME, bus=dbus.SessionBus()) if not self.bus_path: self.bus_path = BUS_PATH if not self.terminator: self.terminator = Terminator() @dbus.service.method(BUS_NAME, in_signature='a{ss}') def new_window(self, options=dbus.Dictionary()): """Create a new Window""" dbg('dbus method called: new_window with parameters %s'%(options)) oldopts = self.terminator.config.options_get() oldopts.__dict__ = options self.terminator.config.options_set(oldopts) self.terminator.create_layout(oldopts.layout) self.terminator.layout_done() @dbus.service.method(BUS_NAME, in_signature='a{ss}') def new_tab(self, options=dbus.Dictionary()): """Create a new tab""" dbg('dbus method called: new_tab with parameters %s'%(options)) oldopts = self.terminator.config.options_get() oldopts.__dict__ = options self.terminator.config.options_set(oldopts) window = self.terminator.get_windows()[0] window.tab_new() @dbus.service.method(BUS_NAME) def terminal_hsplit(self, uuid=None): """Split a terminal horizontally, by UUID""" return self.terminal_split(uuid, True) @dbus.service.method(BUS_NAME) def terminal_vsplit(self, uuid=None): """Split a terminal vertically, by UUID""" return self.terminal_split(uuid, False) def terminal_split(self, uuid, horiz): """Split a terminal horizontally or vertically, by UUID""" dbg('dbus method called: terminal_hsplit') if not uuid: return "ERROR: No UUID specified" terminal = self.terminator.find_terminal_by_uuid(uuid) if not terminal: return "ERROR: Terminal with supplied UUID not found" if horiz: terminal.key_split_horiz() else: terminal.key_split_vert() @dbus.service.method(BUS_NAME) def get_terminals(self, uuid): """Return a list of all the terminals""" return [x.uuid.urn for x in self.terminator.terminals] @dbus.service.method(BUS_NAME) def get_terminal_tab(self, uuid): """Return the UUID of the parent tab of a given terminal""" maker = Factory() terminal = self.terminator.find_terminal_by_uuid(uuid) window = terminal.get_toplevel() root_widget = window.get_children()[0] if maker.isinstance(root_widget, 'Notebook'): return root_widget.uuid.urn @dbus.service.method(BUS_NAME) def get_terminal_tab_title(self, uuid): """Return the title of a parent tab of a given terminal""" maker = Factory() terminal = self.terminator.find_terminal_by_uuid(uuid) window = terminal.get_toplevel() root_widget = window.get_children()[0] if maker.isinstance(root_widget, "Notebook"): return root_widget.get_tab_label(terminal).get_label()
def setup_backer(self): ''' Target: - executes the backer depending on the type of backup to make, the role of the user who is connected to PostgreSQL and the rest of the conditions. It calls a terminator if necessary. ''' connecter = self.get_connecter() # Get databases or clusters' backer depending on the option selected # by the user in console if self.args.cluster: self.logger.debug(Messenger.BEGINNING_EXE_CL_BACKER) backer = self.get_cl_backer(connecter) else: self.logger.debug(Messenger.BEGINNING_EXE_DB_BACKER) backer = self.get_db_backer(connecter) # If necessary, add group and bkp_path to the mailer to be sent within # the process information if self.args.config_mailer: self.logger.mailer.add_group(backer.group) path = backer.bkp_path + backer.group self.logger.mailer.add_bkp_path(path) # Check if the role of user connected to PostgreSQL is superuser pg_superuser = connecter.is_pg_superuser() if not pg_superuser: if self.args.cluster is False: # Users who are not superusers will only be able to backup the # databases they own backer.db_owner = connecter.user self.logger.highlight( 'warning', Messenger.ACTION_DB_NO_SUPERUSER, 'yellow', effect='bold') else: # Backup the cluster can only be made by superuser self.logger.stop_exe(Messenger.ACTION_CL_NO_SUPERUSER) # Make the backups if self.args.cluster is False: # Backup databases # Get PostgreSQL databases' names, connection permissions and # owners dbs_all = connecter.get_pg_dbs_data(backer.ex_templates, backer.db_owner) # Show and log their names Orchestrator.show_dbs(dbs_all, self.logger) # Get the target databases in a list bkp_list = DbSelector.get_filtered_dbs( dbs_all, backer.in_dbs, backer.ex_dbs, backer.in_regex, backer.ex_regex, backer.in_priority, self.logger) # Terminate every connection to these target databases if necessary if self.args.terminate: terminator = Terminator(connecter, target_dbs=bkp_list, logger=self.logger) terminator.terminate_backend_dbs(bkp_list) backer.backup_dbs(bkp_list) # Make databases' backup else: # Backup a cluster # Terminate every connection to any database of the cluster if # necessary if self.args.terminate: terminator = Terminator(connecter, target_all=True, logger=self.logger) terminator.terminate_backend_all() backer.backup_cl() # Make cluster's backup # Close connection to PostgreSQL connecter.pg_disconnect()