--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pgconsole/editor.py Tue Aug 16 23:53:54 2011 +0200
@@ -0,0 +1,455 @@
+import gtk, pango
+import gtksourceview2 as gtksourceview
+from lxml import etree
+
+from panedext import HPanedExt
+from config import cfg
+
+
+class Editor(HPanedExt):
+ def __init__(self):
+ super(Editor, self).__init__()
+
+ self.view = gtksourceview.View()
+ self.view.connect('toggle-overwrite', self.on_toggle_overwrite)
+
+ vbox = gtk.VBox()
+ self.vbox1 = vbox
+ self.add1(vbox)
+
+ sw = gtk.ScrolledWindow()
+ sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ sw.set_shadow_type(gtk.SHADOW_IN)
+ vbox.pack_start(sw)
+ tree = gtk.TreeView()
+ tree.set_headers_visible(False)
+ tree.get_selection().set_mode(gtk.SELECTION_BROWSE)
+ sw.add(tree)
+
+ model = gtk.ListStore(str, str, object, bool) # title, filename, buffer, modified
+ tree.set_model(model)
+ cell = gtk.CellRendererPixbuf()
+ cell.set_property('stock-id', gtk.STOCK_SAVE)
+ column = gtk.TreeViewColumn("File", cell, visible=3)
+ tree.append_column(column)
+ column = gtk.TreeViewColumn("File", gtk.CellRendererText(), text=0)
+ tree.append_column(column)
+ tree.get_selection().connect('changed', self.item_change)
+ tree.set_property('can-focus', False)
+ self.filelist = tree
+
+
+ hbox = gtk.HBox()
+
+ img = gtk.Image()
+ img.set_from_stock(gtk.STOCK_NEW, gtk.ICON_SIZE_SMALL_TOOLBAR)
+ btn = gtk.Button()
+ btn.set_relief(gtk.RELIEF_NONE)
+ btn.set_focus_on_click(False)
+ btn.set_image(img)
+ btn.connect('clicked', self.item_new)
+ hbox.pack_start(btn, expand=False)
+
+ img = gtk.Image()
+ img.set_from_stock(gtk.STOCK_OPEN, gtk.ICON_SIZE_SMALL_TOOLBAR)
+ btn = gtk.Button()
+ btn.set_relief(gtk.RELIEF_NONE)
+ btn.set_focus_on_click(False)
+ btn.set_image(img)
+ btn.connect('clicked', self.item_open)
+ hbox.pack_start(btn, expand=False)
+
+ img = gtk.Image()
+ img.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_SMALL_TOOLBAR)
+ btn = gtk.Button()
+ btn.set_relief(gtk.RELIEF_NONE)
+ btn.set_focus_on_click(False)
+ btn.set_image(img)
+ btn.connect('clicked', self.item_close)
+ hbox.pack_start(btn, expand=False)
+ hbox.connect('size-request', self.leftbuttons_size_request)
+
+ vbox.pack_start(hbox, expand=False)
+
+ vbox = gtk.VBox()
+ vbox.set_property("width-request", 200)
+ self.add2(vbox)
+ self.child_set_property(vbox, 'shrink', False)
+
+
+ # scroll
+ sw = gtk.ScrolledWindow()
+ sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ sw.add(self.view)
+ vbox.pack_start(sw)
+
+
+ self.view.set_show_line_numbers(True)
+ self.view.set_smart_home_end(True)
+ #self.view.set_insert_spaces_instead_of_tabs(True)
+ self.view.set_property("tab-width", 4)
+ self.view.set_auto_indent(True)
+
+ # font
+ font_desc = pango.FontDescription('monospace')
+ if font_desc:
+ self.view.modify_font(font_desc)
+
+ # status bar
+ hbox = gtk.HBox()
+
+ self.st_file, fr1 = self.construct_status('SQL snippet')
+ self.st_file.set_ellipsize(pango.ELLIPSIZE_START)
+ self.st_insovr, fr2 = self.construct_status('INS')
+ self.st_linecol, fr3 = self.construct_status('Line: 0 Col: 0')
+
+
+ img = gtk.Image()
+ img.set_from_stock(gtk.STOCK_SAVE, gtk.ICON_SIZE_SMALL_TOOLBAR)
+ #save = img
+ save = gtk.Button()
+ save.set_relief(gtk.RELIEF_NONE)
+ save.set_focus_on_click(False)
+ save.set_image(img)
+ save.connect('clicked', self.item_save)
+ hbox.pack_start(save, expand=False)
+
+ hbox.pack_start(fr1, expand=True)
+ hbox.pack_start(fr2, expand=False)
+ hbox.pack_start(fr3, expand=False)
+
+ #sep = gtk.HSeparator()
+ #vbox.pack_start(sep, expand=False, fill=False, padding=0)
+
+ #align = gtk.Alignment()
+ #align.set_property("bottom-padding", 3)
+ #align.set_property("xscale", 1.0)
+ #align.add(hbox)
+
+ #frame = gtk.Frame()
+ #frame.add(hbox)
+ #frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ vbox.pack_start(hbox, expand=False, padding=0)
+
+ self.load_nodes()
+ self.build_context_menu()
+
+
+ def build_context_menu(self):
+ menu = gtk.Menu()
+ item = gtk.ImageMenuItem(gtk.STOCK_SAVE, "Save")
+ item.connect("activate", self.item_save)
+ item.show()
+ menu.append(item)
+
+ item = gtk.ImageMenuItem(gtk.STOCK_SAVE_AS, "Save as")
+ item.connect("activate", self.item_save_as)
+ item.show()
+ menu.append(item)
+
+ item = gtk.ImageMenuItem(gtk.STOCK_CLOSE, "Close")
+ item.connect("activate", self.item_close)
+ item.show()
+ menu.append(item)
+
+ item = gtk.SeparatorMenuItem()
+ item.show()
+ menu.append(item)
+
+ item = gtk.ImageMenuItem(gtk.STOCK_GO_UP, "Move up")
+ item.show()
+ menu.append(item)
+
+ item = gtk.ImageMenuItem(gtk.STOCK_GO_DOWN, "Move down")
+ item.show()
+ menu.append(item)
+
+ self.filelist_menu = menu
+
+ self.filelist.connect_object("button-press-event", self.on_filelist_button_press_event, menu)
+
+
+ def on_filelist_button_press_event(self, w, event):
+ if event.button == 3:
+ x = int(event.x)
+ y = int(event.y)
+ pathinfo = self.filelist.get_path_at_pos(x, y)
+ if pathinfo is not None:
+ path, col, cellx, celly = pathinfo
+ self.filelist.grab_focus()
+ self.filelist.set_cursor(path, col, 0)
+ self.filelist_menu.popup(None, None, None, event.button, event.time)
+ return True
+
+
+
+ def make_buffer(self):
+ buffer = gtksourceview.Buffer()
+ buffer.connect('mark-set', self.buffer_mark_set)
+ buffer.connect('changed', self.buffer_changed)
+
+ # style
+ mgr = gtksourceview.style_scheme_manager_get_default()
+ style_scheme = mgr.get_scheme('kate')
+ if style_scheme:
+ buffer.set_style_scheme(style_scheme)
+
+ # syntax
+ lngman = gtksourceview.language_manager_get_default()
+ langsql = lngman.get_language('sql')
+ buffer.set_language(langsql)
+ buffer.set_highlight_syntax(True)
+
+ return buffer
+
+
+ def load_nodes(self):
+ model = self.filelist.get_model()
+ sel = cfg.root.editor.nodes.get('selected')
+ for node in cfg.root.editor.nodes.node:
+ buffer = self.make_buffer()
+ name = node.get('name')
+ type = node.get('type')
+ filename = None
+ if type == 'text' and node.text:
+ buffer.set_text(node.text)
+ if type == 'file':
+ filename = node.text
+ content = open(filename).read()
+ buffer.set_text(content)
+ iter = model.append([name, filename, buffer, False])
+ if sel == name:
+ self.filelist.get_selection().select_iter(iter)
+
+
+ def set_text(self, text):
+ self.buffer.set_text(text)
+
+
+ def get_text(self):
+ start, end = self.buffer.get_bounds()
+ return self.buffer.get_text(start, end)
+
+
+ def get_selection(self):
+ bounds = self.buffer.get_selection_bounds()
+ if not bounds:
+ return None
+ return self.buffer.get_text(*bounds)
+
+
+ def construct_status(self, text):
+ st = gtk.Label(text)
+ st.set_property("single-line-mode", True)
+ st.set_property("xpad", 3)
+ st.set_alignment(0.0, 0.5)
+ fr = gtk.Frame()
+ fr.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
+ fr.add(st)
+ return st, fr
+
+
+ def on_toggle_overwrite(self, w):
+ if not w.get_overwrite():
+ self.st_insovr.set_label('OVR')
+ else:
+ self.st_insovr.set_label('INS')
+
+
+ def buffer_mark_set(self, w, iter, textmark):
+ if textmark == w.get_insert():
+ line = iter.get_line()
+ col = iter.get_visible_line_offset()
+ self.st_linecol.set_label('Line: %d Col: %d' % (line + 1, col + 1))
+
+
+ def buffer_changed(self, w):
+ iter = w.get_iter_at_mark(w.get_insert())
+ line = iter.get_line()
+ col = iter.get_visible_line_offset()
+ self.st_linecol.set_label('Line: %d Col: %d' % (line + 1, col + 1))
+
+
+ def item_change(self, w):
+ model, sel = self.filelist.get_selection().get_selected_rows()
+ if not sel:
+ return
+ iter = model.get_iter(sel[0])
+ title, filename, self.buffer = model.get(iter, 0, 1, 2)
+ self.view.set_buffer(self.buffer)
+
+ if filename:
+ self.st_file.set_text(filename)
+ else:
+ self.st_file.set_text('SQL snippet')
+
+ # update config
+ cfg.root.editor.nodes.set('selected', title)
+
+
+ def probe_title(self, model, title):
+ iter = model.get_iter_first()
+ i = 1
+ while iter:
+ if model.get_value(iter, 0).split(' /')[0] == title:
+ new = title
+ if i > 1:
+ new += ' /%d' % i
+ model.set_value(iter, 0, new)
+ i += 1
+ iter = model.iter_next(iter)
+ if i > 1:
+ title = '%s /%d' % (title, i)
+ return title
+
+
+ def item_new(self, w):
+ model = self.filelist.get_model()
+ title = 'Untitled'
+ title = self.probe_title(model, title)
+ buffer = self.make_buffer()
+ iter = model.append([title, None, buffer, False])
+ self.filelist.get_selection().select_iter(iter)
+
+ # update config
+ etree.SubElement(cfg.root.editor.nodes, 'node', type='text', name=title)
+ cfg.root.editor.nodes.set('selected', title)
+ cfg.save()
+
+
+ def item_open(self, w):
+ dialog = gtk.FileChooserDialog(title='Open file', action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
+ dialog.set_default_response(gtk.RESPONSE_OK)
+ dialog.set_select_multiple(True)
+
+ filter = gtk.FileFilter()
+ filter.set_name("SQL files")
+ filter.add_pattern("*.sql")
+ dialog.add_filter(filter)
+
+ filter = gtk.FileFilter()
+ filter.set_name("All files")
+ filter.add_pattern("*")
+ dialog.add_filter(filter)
+
+ response = dialog.run()
+ if response == gtk.RESPONSE_OK:
+ filenames = dialog.get_filenames()
+ for fname in filenames:
+ self.open_file(fname)
+
+ dialog.destroy()
+
+
+ def open_file(self, filename):
+ title = filename.rsplit('/', 1)[1]
+
+ model = self.filelist.get_model()
+ iter = model.get_iter_first()
+ i = 1
+ while iter:
+ if model.get_value(iter, 1) == filename:
+ # file already opened, select it
+ self.filelist.get_selection().select_iter(iter)
+ return
+ iter = model.iter_next(iter)
+
+ title = self.probe_title(model, title)
+
+ # add item
+ buffer = self.make_buffer()
+ buffer.set_text(open(filename).read())
+ iter = model.append([title, filename, buffer, False])
+ self.filelist.get_selection().select_iter(iter)
+
+ # update config
+ el = etree.SubElement(cfg.root.editor.nodes, 'node', type='file', name=title)
+ el._setText(filename)
+ cfg.root.editor.nodes.set('selected', title)
+ cfg.save()
+
+
+ def item_save(self, w):
+ model, sel = self.filelist.get_selection().get_selected_rows()
+ if not sel:
+ return
+ iter = model.get_iter(sel[0])
+
+ filename, buffer = model.get(iter, 1, 2)
+
+ data = buffer.get_text(*buffer.get_bounds())
+ open(filename, 'w').write(data)
+
+
+ def item_save_as(self, w):
+ model, sel = self.filelist.get_selection().get_selected_rows()
+ if not sel:
+ return
+ iter = model.get_iter(sel[0])
+
+ filename, buffer = model.get(iter, 1, 2)
+
+ path, file = filename.rsplit('/',1)
+
+ dialog = gtk.FileChooserDialog(title='Save as', action=gtk.FILE_CHOOSER_ACTION_SAVE,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
+ dialog.set_default_response(gtk.RESPONSE_OK)
+ dialog.set_current_folder(path)
+ dialog.set_current_name(file)
+
+ filter = gtk.FileFilter()
+ filter.set_name("SQL files")
+ filter.add_pattern("*.sql")
+ dialog.add_filter(filter)
+
+ filter = gtk.FileFilter()
+ filter.set_name("All files")
+ filter.add_pattern("*")
+ dialog.add_filter(filter)
+
+ response = dialog.run()
+ if response == gtk.RESPONSE_OK:
+ filename = dialog.get_filename()
+ data = buffer.get_text(*buffer.get_bounds())
+ open(filename, 'w').write(data)
+
+ title = filename.rsplit('/',1)[1]
+ title = self.probe_title(model, title)
+ model.set(iter, 0, title)
+ model.set(iter, 1, filename)
+ model.set(iter, 3, False) # modified
+
+ dialog.destroy()
+
+
+ def item_close(self, w):
+ model, sel = self.filelist.get_selection().get_selected_rows()
+ if not sel:
+ return
+ iter = model.get_iter(sel[0])
+ newiter = model.iter_next(iter)
+ if newiter is None and sel[0][0] > 0:
+ newiter = model.get_iter((sel[0][0]-1,))
+
+ title, buffer = model.get(iter, 0, 2)
+ #buffer.destroy()
+
+ model.remove(iter)
+
+ if newiter is None:
+ self.item_new(None)
+ else:
+ self.filelist.get_selection().select_iter(newiter)
+
+ # update config
+ el = cfg.root.editor.nodes.find('node[@name="%s"]' % title)
+ el.getparent().remove(el)
+ cfg.save()
+
+
+ def leftbuttons_size_request(self, w, request):
+ self.set_snap1(request[0])
+ return True
+