diff -r 2fcc8ef0b97d -r f3a1b9792cc9 pgconsole/editor.py --- /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 +