class MainUI: #------------------------------------------------------------------------------ """ Generates the GUI for Veranda""" #------------------ def __init__(self): #------------------ """Initialize the program & ui""" # Define some instance variables self.name = "Veranda" # App name self.version = "0.1.0" # Version self.newNumbers = [] # for naming new tabs self.database = DBusSql() # SQL Access driver self.bottomState = False # False: notebookBottom closed self.getObject = "get object" # Versaplex command for get object self.listAll = "list all" # Versaplex command for list all self.searcher = "" # Becomes a searcher object later self.exposeEventID = 0 # Used to disconnect a signal self.bindings = gtk.AccelGroup()# Keyboard bindings group # Import Glade's XML and load it self.gladeTree = gtk.glade.XML("ui.glade") dic = {"on_exit":(gtk.mainquit)} self.gladeTree.signal_autoconnect(dic) # Grab some of the widgets for easy access self.sidebar = "" self.resulter = Resulter() self.window = self.gladeTree.get_widget("window") self.vboxMain = self.gladeTree.get_widget("vbox-main") self.vpanedEditor = self.gladeTree.get_widget("vpaned-editor") self.vpanedPanel = self.gladeTree.get_widget("vpaned-panel") self.notebookTop = self.gladeTree.get_widget("notebook-top") self.notebookBottom = self.gladeTree.get_widget("notebook-bottom") self.buttonRun = self.gladeTree.get_widget("button-run") self.buttonNewTab = self.gladeTree.get_widget("button-newtab") self.buttonClose = self.gladeTree.get_widget("button-closetab") self.buttonNext = self.gladeTree.get_widget("button-nexttab") self.buttonPrevious = self.gladeTree.get_widget("button-lasttab") self.entrySearch = self.gladeTree.get_widget("entry-search") self.statusbar = self.gladeTree.get_widget("statusbar") # Statusbar context ids: * "sidebar" # * "run query" # * "error" # * "success" # Misc Initializations hbox = gtk.HBox() hbox.show() runImage = gtk.Image() runImage.set_from_file("run.svg") runImage.show() hbox.pack_start(runImage) label = gtk.Label(" Run") label.show() hbox.pack_start(label) self.buttonRun.add(hbox) hbox = gtk.HBox() hbox.show() newTabImage = gtk.Image() newTabImage.set_from_file("new.svg") newTabImage.show() hbox.pack_start(newTabImage) label = gtk.Label(" New Tab") label.show() hbox.pack_start(label) self.buttonNewTab.add(hbox) hbox = gtk.HBox() hbox.show() newTabImage = gtk.Image() newTabImage.set_from_file("close.svg") newTabImage.show() hbox.pack_start(newTabImage) label = gtk.Label(" Close Current Tab") label.show() hbox.pack_start(label) self.buttonClose.add(hbox) hbox = gtk.HBox() hbox.show() newTabImage = gtk.Image() newTabImage.set_from_file("next.svg") newTabImage.show() hbox.pack_start(newTabImage) label = gtk.Label(" Next Tab") label.show() hbox.pack_start(label) self.buttonNext.add(hbox) hbox = gtk.HBox() hbox.show() newTabImage = gtk.Image() newTabImage.set_from_file("previous.svg") newTabImage.show() hbox.pack_start(newTabImage) label = gtk.Label(" Previous Tab") label.show() hbox.pack_start(label) self.buttonPrevious.add(hbox) # Open a first tab (comes with configured editor) self.newTab() # Connect events & key strokes self.window.connect("delete_event", gtk.main_quit) self.buttonRun.connect("clicked", self.runQuery) self.buttonNewTab.connect("clicked", self.newTab) self.buttonClose.connect("clicked", self.closeCurrentTab) self.buttonNext.connect("clicked", self.nextTab) self.buttonPrevious.connect("clicked", self.lastTab) self.entrySearch.connect("key-release-event", self.search) self.exposeEventID = self.window.connect("expose-event", self.postStartInit) self.window.add_accel_group(self.bindings) self.buttonRun.add_accelerator("clicked", self.bindings, ord("r"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) self.buttonNewTab.add_accelerator("clicked", self.bindings, ord("t"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) self.buttonNext.add_accelerator("clicked", self.bindings, ord("n"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) self.buttonPrevious.add_accelerator("clicked", self.bindings, ord("p"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) self.buttonClose.add_accelerator("clicked", self.bindings, ord("w"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) self.buttonClose.add_accelerator("clicked", self.bindings, ord("c"), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) # Show things self.window.show() #--------------------- def initSidebar(self): #--------------------- """ Initializes the sidebar with the tables list and configures it""" toList = ["table", "view", "procedure", "trigger", "scalarfunction", "tablefunction"] statusID = self.statusbar.get_context_id("sidebar") self.statusbar.push(statusID, "Initializing Sidebar") scrolls = gtk.ScrolledWindow(gtk.Adjustment(), gtk.Adjustment()) scrolls.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) treestore = gtk.TreeStore(str) self.sidebar = gtk.TreeView(treestore) cell = gtk.CellRendererText() column = gtk.TreeViewColumn("Database Objects", cell, text=0) self.sidebar.append_column(column) masterTable = [] for item in toList: result = self.database.query(self.listAll+" "+item) if "Error" not in result: statusID = self.statusbar.get_context_id("success") self.statusbar.push(statusID, "Success.") parser = Parser(result) table = parser.getTable()[:] #the [:] makes a clone table.insert(0, [item]) masterTable.append(table) rows = parser.getTableIterator() iter = treestore.append(None, [item.title()]) while rows.hasNext(): treestore.append(iter, [str(rows.getNext()[0])]) else: statusID = self.statusbar.get_context_id("error") self.statusbar.push(statusID, result) self.searcher = Searcher(masterTable) self.sidebar.connect("row-activated", self.rowClicked, masterTable) scrolls.add(self.sidebar) self.vpanedPanel.add(scrolls) scrolls.show() self.sidebar.show() self.statusbar.push(statusID, "Sidebar Loaded") #----------------------- def getNewNumber(self): #----------------------- """ Get a unique number to number a tab """ x = 0 while (True): if x in self.newNumbers: x = x+1 else: self.newNumbers.append(x) return r" "+str(x)+r" " #---------------------------------------- def removeNumber(self, editor, notebook): #---------------------------------------- """ If a given page has a label with an automatic number, remove that number from the list of numbers so that it can be reassigned to a new fresh tab in the future""" label = self.getLabelText(editor, notebook) label = label.split(" ") self.newNumbers.remove(int(label[0])) #--------------------------------------------- def configureEditor(self, editor, textbuffer): #--------------------------------------------- """Sets up a gtksourceview with the common options I want.""" languagemanager = gtksourceview.LanguageManager() textbuffer.set_language(languagemanager.get_language("sql")) textbuffer.set_highlight_syntax(True) editor.set_show_line_numbers(True) editor.set_wrap_mode(gtk.WRAP_WORD_CHAR) editor.modify_font(pango.FontDescription("monospace 10")) #--------------------------------------------- def makeBottomTabMenu(self, label, resulter): #--------------------------------------------- """Returns an hbox with the title, change button, and close button to be put in a tab""" hbox = gtk.HBox() label = gtk.Label(r" "+str(label)+r" ") hbox.pack_start(label) changeIcon = gtk.Image() changeIcon.set_from_file("cycle.svg") buttonMode = gtk.Button(None) buttonMode.add(changeIcon) hbox.pack_start(buttonMode, False, False, 1) closeIcon = gtk.Image() closeIcon.set_from_file("close.svg") buttonClose = gtk.Button(None) buttonClose.add(closeIcon) hbox.pack_start(buttonClose, False, False, 1) buttonClose.connect("clicked", self.closeTab, resulter) buttonMode.connect("clicked", self.changeMode, resulter) changeIcon.show() closeIcon.show() buttonMode.show() label.show() buttonClose.show() hbox.show() return hbox #--------------------------------------- def showOutput(self, topEditor, result): #--------------------------------------- parser = Parser(result) if self.bottomState == False: self.resulter.update(parser) self.notebookBottom.show() hbox = self.makeBottomTabMenu("Results", self.resulter) self.newTabBottom(self.resulter.getCurrentView(), hbox) self.bottomState = True else : index = self.notebookBottom.page_num(self.resulter.getCurrentView()) hbox = self.notebookBottom.get_tab_label( self.resulter.getCurrentView()) self.resulter.update(parser) self.notebookBottom.remove_page(index) self.notebookBottom.insert_page(self.resulter.getCurrentView(), hbox, index) self.notebookBottom.set_tab_reorderable( self.resulter.getCurrentView(), True) self.notebookBottom.set_current_page(index) #------------------------------------ def newTabBottom(self, widget, hbox): #------------------------------------ """Creates a new tab on the bottom notebook, with "widget" in the tab and "hbox" as the label (not actually a gtk label)""" self.notebookBottom.append_page(widget, hbox) #---------------------------------------- def getLabelText(self, editor, notebook): #---------------------------------------- """Retrieves the label number from notebook with a page which contains the given editor""" hbox = notebook.get_tab_label(editor) children = hbox.get_children() labelText = children[0].get_text() labelText = labelText.strip(' ') return str(labelText) #--------------------------------- def expandSidebar(self, sidebarList): #--------------------------------- """Will expand some of the sidebar elements to make better use of space""" expandMax = 18 usedSoFar = 0 for section in sidebarList: if len(section) + usedSoFar > expandMax: break else: usedSoFar += len(section) self.sidebar.expand_to_path((sidebarList.index(section),1)) #------------------------------------ def updateSidebar(self, sidebarList): #------------------------------------ """Given a new list, this will change the contents of the sidebar""" treestore = gtk.TreeStore(str) for section in sidebarList: iter = treestore.append(None,[section[0][0]]) for element in section[1:]: treestore.append(iter,[element[0]]) self.sidebar.set_model(treestore) self.expandSidebar(sidebarList) #----------------------# #-- CALLBACK METHODS --# #----------------------# #------------------------------------------ def postStartInit(self, widget, data=None): #------------------------------------------ """ Initializes all the stuff that should only happen after the window is already on screen""" self.initSidebar() widget.disconnect(self.exposeEventID) #------------------------------------- def runQuery(self, widget, data=None): #------------------------------------- """Uses the database abstraction (initially Dbus) To send the query that is in the current window""" scrolls = self.notebookTop.get_nth_page(self.notebookTop. get_current_page()) if scrolls != None: editor = scrolls.get_children()[0] buffer = editor.get_buffer() #get all text, not including hidden chars query = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), False) contextID = self.statusbar.get_context_id("run query") self.statusbar.push(contextID, "Ran query: "+query) result = self.database.query(query) if "Error" not in result: self.showOutput(editor, result) statusID = self.statusbar.get_context_id("success") self.statusbar.push(statusID, "Success.") else: statusID = self.statusbar.get_context_id("error") self.statusbar.push(statusID, result) else: contextID = self.statusbar.get_context_id("error") self.statusbar.push(contextID, "No query to run.") #----------------------------------- def search(self, widget, data=None): #----------------------------------- """Incremental search callback. As the user types, this method notices and modifies the sidebar""" text = widget.get_text() sidebarList = self.searcher.find(text) self.updateSidebar(sidebarList) #-------------------------------------- def newTab(self, widget=None, data=None): #-------------------------------------- """Open a new editor tab (top). Data is an optional title for the tab.""" scrolls = gtk.ScrolledWindow(gtk.Adjustment(), gtk.Adjustment()) scrolls.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) textbuffer = gtksourceview.Buffer() editor = gtksourceview.View(textbuffer) self.configureEditor(editor, textbuffer) hbox = gtk.HBox() if data == None: label = gtk.Label(self.getNewNumber()) else: label = gtk.Label(self.getNewNumber()+str(data)+" ") hbox.pack_start(label) closeIcon = gtk.Image() closeIcon.set_from_file("close.svg") buttonClose = gtk.Button(None) buttonClose.add(closeIcon) hbox.pack_start(buttonClose, False, False, 1) buttonClose.connect("clicked", self.closeTab, scrolls) scrolls.add(editor) self.notebookTop.append_page(scrolls, hbox) self.notebookTop.set_tab_reorderable(scrolls, True) scrolls.show() closeIcon.show() label.show() buttonClose.show() hbox.show() editor.show() # KEEP THIS LINE AT THE END OR ELSE! (hours of frustration...) self.notebookTop.set_current_page(-1) return editor #-------------------------------------------- def closeTab(self, sourceWidget, targetWidget): #-------------------------------------------- """Close a tab. targetWidget points to the contents of the notebook tab that you want closed.""" index = -1 try: index = self.notebookTop.page_num(targetWidget) except TypeError: pass if index != -1: self.removeNumber(targetWidget, self.notebookTop) self.notebookTop.remove_page(index) return index = self.notebookBottom.page_num(targetWidget.getCurrentView()) if index != -1: self.notebookBottom.remove_page(index) self.bottomState = False self.notebookBottom.queue_resize() self.notebookTop.queue_resize() return if index == -1: print "Worse Than Failure: Lost The Tab!" #-------------------------------------------- def closeCurrentTab(self, widget, data=None): #-------------------------------------------- """Closes the current tab in the top editor section""" index = self.notebookTop.get_current_page() self.notebookTop.remove_page(index) #------------------------------------ def nextTab(self, widget, data=None): #------------------------------------ """Changes to the previous tab""" index = self.notebookTop.get_current_page() self.notebookTop.set_current_page((index+1) % \ self.notebookTop.get_n_pages()) #------------------------------------ def lastTab(self, widget, data=None): #------------------------------------ """Changes to the next tab""" index = self.notebookTop.get_current_page() self.notebookTop.set_current_page(index-1) #-------------------------------------- def changeMode(self, widget, resulter): #-------------------------------------- """After a change button is clicked, this makes the notebook tab osscroll through the different view modes in a fixed pattern""" pageIndex = self.notebookBottom.page_num(resulter.getCurrentView()) hbox = self.notebookBottom.get_tab_label(resulter.getCurrentView()) self.notebookBottom.remove_page(pageIndex) self.notebookBottom.insert_page(resulter.getNextView(), hbox, pageIndex) self.notebookBottom.set_tab_reorderable(resulter.getCurrentView(), True) #------------------------------------------------------------- def rowClicked(self, treeview, position, column, masterTable): #------------------------------------------------------------- """ Given the position coordinates and the master table (a list of all data that is in the sidebar), this method opens a new editor tab which has code in it. The code is the source code to the object that was double clicked on in the sidebar. If the item is a table, the code is just a select statement. """ try: type = masterTable[position[0]][0][0] name = masterTable[position[0]][position[1]+1][0] except IndexError: contextID = self.statusbar.get_context_id("error") self.statusbar.push(contextID, "Can't do anything with a category title") print "Can't do anything when a category title is clicked" return if type == "table": query = "select top 100 * from [%s]" % name contextID = self.statusbar.get_context_id("run query") self.statusbar.push(contextID, "Ran query: "+query) result = self.database.query(query) if "Error" not in result: editor = self.newTab(None, name) buffer = editor.get_buffer() buffer.set_text(query) statusID = self.statusbar.get_context_id("success") self.statusbar.push(statusID, "Success.") self.showOutput(editor, result) else: statusID = self.statusbar.get_context_id("error") self.statusbar.push(statusID, result) else: query = self.getObject + " " + type + " " + name contextID = self.statusbar.get_context_id("run query") self.statusbar.push(contextID, "Ran query: "+query) result = self.database.query(query) if "Error" in result: statusID = self.statusbar.get_context_id("error") self.statusbar.push(statusID, result) return else: statusID = self.statusbar.get_context_id("success") self.statusbar.push(statusID, "Success.") parser = Parser(result) data = parser.getTable() commands = data[0][0] com2 = commands[:] pattern1 = re.compile(r"^create", re.I) commands = re.sub(pattern1, r"ALTER", commands, 1) if commands == com2: pattern2 = re.compile(r"\ncreate", re.I) commands = re.sub(pattern2, r"\nALTER", commands, 1) editor = self.newTab(None, name) buffer = editor.get_buffer() buffer.set_text(commands)