Rename to pydbkit.
from gi.repository import Gtk
from gi.repository import Pango
from gi.repository import GtkSource
from lxml import etree
from pgconsole.panedext import HPanedExt
from pgconsole.config import cfg
class Editor(HPanedExt):
def __init__(self):
super(Editor, self).__init__()
self.view = GtkSource.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.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
sw.set_shadow_type(Gtk.ShadowType.IN)
vbox.pack_start(sw, expand=True, fill=True, padding=0)
tree = Gtk.TreeView()
tree.set_headers_visible(False)
tree.get_selection().set_mode(Gtk.SelectionMode.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.IconSize.SMALL_TOOLBAR)
btn = Gtk.Button()
btn.set_relief(Gtk.ReliefStyle.NONE)
btn.set_focus_on_click(False)
btn.set_image(img)
btn.connect('clicked', self.item_new)
hbox.pack_start(btn, expand=False, fill=True, padding=0)
img = Gtk.Image()
img.set_from_stock(Gtk.STOCK_OPEN, Gtk.IconSize.SMALL_TOOLBAR)
btn = Gtk.Button()
btn.set_relief(Gtk.ReliefStyle.NONE)
btn.set_focus_on_click(False)
btn.set_image(img)
btn.connect('clicked', self.item_open)
hbox.pack_start(btn, expand=False, fill=True, padding=0)
img = Gtk.Image()
img.set_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.SMALL_TOOLBAR)
btn = Gtk.Button()
btn.set_relief(Gtk.ReliefStyle.NONE)
btn.set_focus_on_click(False)
btn.set_image(img)
btn.connect('clicked', self.item_close)
hbox.pack_start(btn, expand=False, fill=True, padding=0)
hbox.connect('size-allocate', self.leftbuttons_size_request)
vbox.pack_start(hbox, expand=False, fill=True, padding=0)
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.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw.add(self.view)
vbox.pack_start(sw, expand=True, fill=True, padding=0)
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.EllipsizeMode.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.IconSize.SMALL_TOOLBAR)
#save = img
save = Gtk.Button()
save.set_relief(Gtk.ReliefStyle.NONE)
save.set_focus_on_click(False)
save.set_image(img)
save.connect('clicked', self.item_save)
hbox.pack_start(save, expand=False, fill=True, padding=0)
hbox.pack_start(fr1, expand=True, fill=True, padding=0)
hbox.pack_start(fr2, expand=False, fill=True, padding=0)
hbox.pack_start(fr3, expand=False, fill=True, padding=0)
#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, fill=True, padding=0)
self.load_nodes()
self.build_context_menu()
def build_context_menu(self):
menu = Gtk.Menu()
item = Gtk.ImageMenuItem(use_stock=True, label=Gtk.STOCK_SAVE)
item.connect("activate", self.item_save)
item.show()
menu.append(item)
item = Gtk.ImageMenuItem(use_stock=True, label=Gtk.STOCK_SAVE_AS)
item.connect("activate", self.item_save_as)
item.show()
menu.append(item)
item = Gtk.ImageMenuItem(use_stock=True, label=Gtk.STOCK_CLOSE)
item.connect("activate", self.item_close)
item.show()
menu.append(item)
item = Gtk.SeparatorMenuItem()
item.show()
menu.append(item)
item = Gtk.ImageMenuItem(use_stock=True, label=Gtk.STOCK_GO_UP)
item.show()
menu.append(item)
item = Gtk.ImageMenuItem(use_stock=True, label=Gtk.STOCK_GO_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, None, event.button, event.time)
return True
def make_buffer(self):
buffer = GtkSource.Buffer()
buffer.connect('mark-set', self.buffer_mark_set)
buffer.connect('changed', self.buffer_changed)
# style
mgr = GtkSource.StyleSchemeManager.get_default()
style_scheme = mgr.get_scheme('kate')
if style_scheme:
buffer.set_style_scheme(style_scheme)
# syntax
lngman = GtkSource.LanguageManager.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
try:
content = open(filename).read()
except IOError:
content = ''
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.ShadowType.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.FileChooserAction.OPEN,
buttons=(Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL,Gtk.STOCK_OPEN,Gtk.ResponseType.OK))
dialog.set_default_response(Gtk.ResponseType.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.ResponseType.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.FileChooserAction.SAVE,
buttons=(Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL,Gtk.STOCK_SAVE,Gtk.ResponseType.OK))
dialog.set_default_response(Gtk.ResponseType.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.ResponseType.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].get_indices()[0] > 0:
newiter = model.get_iter((sel[0].get_indices()[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.width)
return True