#!/usr/bin/python

""" TODO:
  - preserve - or at least move and then preserve - comments
  - split off type specs and carry them around extra?
  - comment out: put something into "rem" node.
  - drag&drop refactoring
  - colorized IDs if on it, maybe.
"""
import pygtk
pygtk.require("2.0")
import gtk
import gtk.keysyms
from StringIO import StringIO
in_ = "let"
rem = ";"
colonequal = ":="
prog = [in_, [colonequal, "x", "2"], "x"]
def list_P(obj):
  return isinstance(obj, list)
C_NAME = 0
C_DISPLAYSTRING = 1
store = gtk.TreeStore(str, str)

def summarize_treeview(treeview, iter, path):
  dest = StringIO()
  model = treeview.props.model
  summarize(model, iter, dest)
  rep = dest.getvalue().replace("\n", "")
  model.set_value(iter, C_DISPLAYSTRING, rep)
def unsummarize_treeview(treeview, iter, path):
  model = treeview.props.model
  name = model.get_value(iter, C_NAME)
  model.set_value(iter, C_DISPLAYSTRING, name)
def load_part(parent, obj):
  if list_P(obj):
    iter = store.append(parent, (obj[0], obj[0], ))
    for item in obj[1:]:
      load_part(iter, item)
    dest = StringIO()
    #summarize(model, iter, dest)
    summarize_treeview(treeview, iter, None)
  else:
    store.append(parent, (str(obj), str(obj), ))
def load_prog(prog):
  load_part(None, prog)
def summarize(model, parent, dest):
  iter = model.iter_children(parent)
  p = iter
  if p:
    dest.write("(")
  name = model.get_value(parent, C_NAME)
  dest.write(name)
  while iter:
    dest.write(" ")
    summarize(model, iter, dest)
    iter = model.iter_next(iter)
  if p:
    dest.write(")")

contextmenu = gtk.Menu()
def show_popup_menu(widget, event = None): # Shift-F10
  """ event is optional """
  contextmenu.show_all()
  contextmenu.popup(None, None, None, event.button if event else 0, event.time if event else 0)
def handle_tree_button_press(treeview, event):
  if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3:
    selection = treeview.get_selection()
    path, column, x, y = treeview.get_path_at_pos(int(event.x), int(event.y))
    treeview.set_cursor(path, column)
    # select node.
    if selection.count_selected_rows() <= 1:
      treeview.get_selection().select_path(path)
    show_popup_menu(treeview, event)
    return True # handled
  return False # unhandled
treeview = gtk.TreeView()
treeview.connect("button-press-event", handle_tree_button_press)
treeview.connect("popup-menu", show_popup_menu)
treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
treeview.connect("test-collapse-row", summarize_treeview)
treeview.connect("test-expand-row", unsummarize_treeview)
cell0 = gtk.CellRendererText()
cell0.props.editable = True
def update_cells_from_text(cell, pathstr, newtext):
  model = treeview.props.model
  #path = tuple(map(int, pathstr.split(":"))) # FIXME gtk.TreePath(pathstr) # model.get_path_from_string(pathstr)
  iter = model.get_iter_from_string(pathstr)
  path = model.get_path(iter)
  # FIXME check & update
  expanded = treeview.row_expanded(path)
  if expanded or not model.iter_children(iter):
    model.set_value(iter, C_NAME, newtext)
  else:
    oldtext = model.get_value(iter, C_DISPLAYSTRING)
    if oldtext == newtext: # unchanged
      return
  model.set_value(iter, C_DISPLAYSTRING, newtext)
cell0.connect("edited", update_cells_from_text)
col0 = gtk.TreeViewColumn("Expression")
col0.pack_start(cell0, True)
col0.add_attribute(cell0, "text", C_DISPLAYSTRING)
treeview.append_column(col0)
treeview.props.model = store
# TODO set_cursor, grab_focus
# treeview.set_reorderable(True) # incompatible with drag&drop
targets = [
  ("text/plain", 0, 1), # mime type, flags, user-internal id
]
dactions = gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE | gtk.gdk.ACTION_LINK | gtk.gdk.ACTION_ASK
treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON2_MASK | gtk.gdk.BUTTON3_MASK | gtk.gdk.MOD1_MASK | gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK, targets, dactions)
treeview.enable_model_drag_dest(targets, dactions)
treeview.expand_all()
treeview.collapse_all()
# column.pack_start, pack_end
scroller = gtk.ScrolledWindow()
scroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
scroller.add(treeview)

textbuffer = gtk.TextBuffer()
textview = gtk.TextView(textbuffer)
textscroller = gtk.ScrolledWindow()
textscroller.add(textview)

def edit_node(action):
  path, col = treeview.get_cursor()
  cell = col.get_cell_renderers()[0]
  if not cell.props.editing:
    treeview.set_cursor(path, col, True)
def delete_nodes(action):
  selection = treeview.get_selection()
  model, paths = selection.get_selected_rows()
  refs = map(lambda path: gtk.TreeRowReference(model, path), paths)
  for rref in refs:
    path = rref.get_path()
    iter = model.get_iter(path)
    model.remove(iter)
def insertion_permitted_under_node(model, parent):
  # FIXME check operator kind etc.
  return True
def append_child_node(action):
  path, column = treeview.get_cursor()
  treeview.expand_to_path(path)
  model = treeview.props.model
  iter = model.get_iter(path)
  if insertion_permitted_under_node(model, iter):
    iter = model.append(iter, ("", "")) # FIXME nicer initializer?
    path = model.get_path(iter)
    treeview.expand_to_path(path)
    treeview.set_cursor(path, column) # TODO hard-code column
    edit_node(action)
def insert_sibling_before_this_node(action):
  path, column = treeview.get_cursor()
  treeview.expand_to_path(path)
  model = treeview.props.model
  iter = model.get_iter(path)
  parent = model.iter_parent(iter)
  if insertion_permitted_under_node(model, parent):
    iter = model.insert_before(parent, iter, ("", "")) # FIXME nicer initializer?
    path = model.get_path(iter)
    treeview.expand_to_path(path)
    treeview.set_cursor(path, column) # TODO hard-code column
    edit_node(action)
def create_Action(name, label, tooltiptext, stockid, cb, key = None, keymods = 0):
  action = gtk.Action(name, label, tooltiptext, stockid)
  action.connect("activate", cb)
  menuitem = action.create_menu_item()
  path = "<L1-Editor>/Contextmenu/%s" % (name, )
  menuitem.set_accel_path(path)
  if key and not gtk.accel_map_lookup_entry(path):
    gtk.accel_map_add_entry(path, key, keymods)
  contextmenu.append(menuitem)
  return action
  
# see also: gtk_binding_entry_remove() 
# FIXME "FindText" action.
actions = {
  "EditNode": create_Action("EditNode", "Edit...", "Edit Node", gtk.STOCK_EDIT, edit_node, gtk.keysyms.F2, 0),
  "AppendChild": create_Action("AppendChild", "Insert Child", "Insert Child Under This Node", gtk.STOCK_ADD, append_child_node, gtk.keysyms.Insert, 0),
  "InsertSibling": create_Action("InsertSibling", "Insert Sibling Before", "Insert Sibling Before This Node", 0, insert_sibling_before_this_node, gtk.keysyms.Insert, gtk.gdk.SHIFT_MASK),
  "DeleteTree": create_Action("DeleteTree", "Delete Tree", "Delete Tree under this Node", gtk.STOCK_DELETE, delete_nodes, gtk.keysyms.Delete),
  "CommentOut": gtk.Action("CommentOut", "Comment Out", "Comment Tree Out", 0), # FIXME
  "DeleteComment": gtk.Action("DeleteComment", "Delete Comment", "Delete Comment", 0), # FIXME
  "CutNode": gtk.Action("CutNode", "Cut", "Cut into Clipboard", gtk.STOCK_CUT), # FIXME
  "CopyNode": gtk.Action("CopyNode", "Copy", "Copy into Clipboard", gtk.STOCK_COPY), # FIXME
  "PasteNode": gtk.Action("PasteNode", "Paste", "Paste from Clipboard", gtk.STOCK_PASTE), # FIXME
}
#for name in ["EditNode", "AppendChild", "InsertSibling", "DeleteTree", "CommentOut", "DeleteComment", "CutNode", "CopyNode", "PasteNode"]:
#  contextmenu.append(actions[name].create_menu_item())

def update_textview(selection):
  model, paths = selection.get_selected_rows()
  if len(paths) == 1:
    path = paths[0]
    dest = StringIO()
    iter = store.get_iter(path)
    summarize(store, iter, dest)
    text = dest.getvalue()
    textbuffer.set_text(text)
  else:
    textbuffer.set_text("No selection")
treeview.get_selection().connect("changed", update_textview)

textscroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
box = gtk.HBox()
box.pack_start(scroller, True, True)
# unused for now box.pack_start(textscroller, True, True)
window = gtk.Window()
accelgroup = gtk.AccelGroup()
contextmenu.set_accel_group(accelgroup)
contextmenu.set_accel_path("<L1-Editor>/Contextmenu")
#accel_map = gtk.accel_map_get()
#print(dir(accel_map))
gtk.accel_map_add_entry("<L1-Editor>/Contextmenu/EditNode", gtk.keysyms.F2, 0)

window.add_accel_group(accelgroup)
window.connect("delete-event", gtk.main_quit)
window.add(box)
window.show_all()
load_prog(prog)
gtk.main()
