'OHRRPGCE CUSTOM - Editor Editor
'(C) Copyright 1997-2020 James Paige, Ralph Versteegen, and the OHRRPGCE Developers
'Dual licensed under the GNU GPL v2+ and MIT Licenses. Read LICENSE.txt for terms and disclaimer of liability.

#include "config.bi"
#include "allmodex.bi"
#include "common.bi"
#include "slices.bi"
#include "customsubs.bi"
#include "loading.bi"
#include "reload.bi"
#include "reloadext.bi"
#include "editrunner.bi"

#include "editedit.bi"

USING Reload
USING Reload.Ext

'-----------------------------------------------------------------------

TYPE EEState
 state as MenuState
 menu as MenuDef
 indent as integer
 doc as DocPtr
 root as NodePtr
 seek_widget as NodePtr
 clipboard as NodePtr
 clipboard_is as NodePtr
 filename as string
 changed as bool
 type_data as bool
END TYPE

TYPE WEStateF as WEState
TYPE WidgetRefreshSub as SUB(byref st as WEStateF, byval widget as Nodeptr)

TYPE WidgetCode
 refresh_callback as WidgetRefreshSub
END TYPE

TYPE WEState
 state as MenuState
 menu as MenuDef
 code as WidgetCode
 changed as bool
 exit_please as bool
END TYPE

ENUM WEdMenuItemType
 wedEXIT = mtypeLAST + 1
 wedSTRING
 wedINT
 wedBOOL
END ENUM

'-----------------------------------------------------------------------

DECLARE SUB ee_create_new_editor_file(byref st as EEState)
DECLARE SUB ee_refresh OVERLOAD (byref st as EEState)
DECLARE SUB ee_refresh OVERLOAD (byref st as EEState, byval widget as NodePtr)
DECLARE FUNCTION ee_widget_string(byref st as EEState, byval widget as Nodeptr) as string
DECLARE FUNCTION ee_widget_data_node_name(byval widget as NodePtr) as string
DECLARE SUB ee_focus_widget(byref st as EEState, byval widget as Nodeptr)
DECLARE SUB ee_export(byref st as EEState)
DECLARE FUNCTION ee_browse(byref st as EEState) as integer
DECLARE FUNCTION ee_load(filename as string, byref st as EEState) as integer
DECLARE SUB ee_save(filename as string, byref st as EEState)
DECLARE FUNCTION ee_okay_to_unload(byref st as EEState) as integer
DECLARE SUB ee_insertion(byref st as EEState, byval widget as Nodeptr)
DECLARE SUB ee_rearrange(byref st as EEState, mi as MenuDefItem Ptr)
DECLARE SUB ee_swap_widget_up(byval widget as Nodeptr)
DECLARE SUB ee_swap_widget_down(byval widget as Nodeptr)
DECLARE SUB ee_swap_widget_left(byval widget as Nodeptr)
DECLARE SUB ee_swap_widget_right(byval widget as Nodeptr)
DECLARE SUB ee_edit_menu_item(byref st as EEState, mi as MenuDefItem Ptr)
DECLARE FUNCTION ee_edit_widget(byref st as EEState, byval widget as NodePtr) as bool

DECLARE FUNCTION ee_prompt_for_widget_kind() as string
DECLARE FUNCTION ee_create_widget(byref st as EEState, kind as string) as NodePtr
DECLARE FUNCTION ee_container_check(byval cont as NodePtr) as bool
DECLARE FUNCTION ee_container_single_check(byval cont as NodePtr) as bool
DECLARE FUNCTION ee_widget_has_caption(byval widget as NodePtr) as integer
DECLARE FUNCTION ee_widget_has_data(byval widget as NodePtr) as integer

DECLARE FUNCTION ee_count_children(byval cont as NodePtr) as integer
DECLARE FUNCTION ee_container_is_full(byval cont as NodePtr) as bool
DECLARE FUNCTION ee_parent_is_full(byval cont as NodePtr) as bool
DECLARE FUNCTION ee_get_widget_parent_container(byval widget as NodePtr) as NodePtr

DECLARE FUNCTION widget_editor(byval widget as NodePtr) as bool
DECLARE SUB widget_editor_refresh(byref st as WEState, byval widget as NodePtr)
DECLARE SUB widget_editor_edit_menu_item(byref st as WEState, mi as MenuDefItem Ptr)
DECLARE FUNCTION widget_editor_edit_node(byref st as WEState, byval kind as integer, byval node as NodePtr) as bool

DECLARE SUB ee_get_widget_code(byref code as WidgetCode, byval widget as NodePtr)

'-----------------------------------------------------------------------

SUB editor_editor()
 DIM st as EEState
 
 st.changed = NO
 st.doc = CreateDocument()
 st.root = CreateNode(st.doc, "")
 SetRootNode(st.doc, st.root)
 ee_create_new_editor_file st
  
 st.state.pt = 0
 st.state.need_update = YES
 st.state.active = YES
 st.state.autosize = YES
 st.state.autosize_ignore_pixels = 18

 InitLikeStandardMenu st.menu
 
 setkeys YES
 DO
  setwait 55
  setkeys YES

  IF st.state.need_update THEN
   DeleteMenuItems st.menu
   st.indent = 0
   ee_refresh st
   init_menu_state st.state, st.menu
   IF st.seek_widget THEN
    ee_focus_widget st, st.seek_widget
    st.seek_widget = 0
   END IF
   st.state.need_update = NO
  END IF
  
  IF keyval(ccCancel) > 1 THEN
   IF ee_okay_to_unload(st) THEN EXIT DO 
  END IF
  IF keyval(scF1) > 1 THEN show_help("editor_editor")
  IF keyval(scF3) > 1 THEN
   IF ee_okay_to_unload(st) THEN
    IF ee_browse(st) THEN
     setkeys YES
     st.state.need_update = YES
    END IF
   END IF
  END IF
  IF keyval(scF2) > 1 THEN
   ee_export st
  END IF
  IF keyval(scF5) > 1 THEN
   editor_runner st.root
  END IF
  IF keyval(scTab) > 1 THEN
   st.type_data = NOT st.type_data
   st.state.need_update = YES
  END IF

  IF st.state.pt >= 0 AND st.state.pt <= st.menu.numitems - 1 THEN
   ee_edit_menu_item st, st.menu.items[st.state.pt]
   ee_rearrange st, st.menu.items[st.state.pt]
  ELSE
   ee_insertion st, 0
  END IF

  IF keyval(scShift) = 0 THEN
   usemenu st.state
  END IF

  clearpage dpage
  draw_menu st.menu, st.state, dpage
  edgeprint "F1=Help  TAB=mode (" & IIF(st.type_data, "data node", "widget caption") & ")", pInfoX, pInfoY, uilook(uiText), dpage

  SWAP vpage, dpage
  setvispage vpage
  dowait
 LOOP

 IF st.clipboard <> 0 THEN FreeNode(st.clipboard)
 FreeDocument(st.doc)
 
END SUB

'-----------------------------------------------------------------------

SUB ee_create_new_editor_file(byref st as EEState)
 RenameNode st.root, "editor"
 AppendChildNode st.root, "datafile"
 AppendChildNode st.root, "recordnode"
 AppendChildNode st.root, "enums"
 AppendChildNode st.root, "widgets"
END SUB

SUB ee_edit_menu_item(byref st as EEState, mi as MenuDefItem Ptr)
 IF mi = 0 THEN debug "ee_edit_menu_item: null mi": EXIT SUB
 DIM widget as NodePtr
 widget = mi->dataptr
 IF widget = 0 THEN debug "ee_edit_menu_item: mi has null widget node": EXIT SUB

 IF ee_edit_widget(st, widget) THEN
  mi->caption = STRING(mi->extra(0), " ") & ee_widget_string(st, widget)
  st.state.need_update = YES
  st.changed = YES
 END IF

END SUB

FUNCTION ee_edit_widget(byref st as EEState, byval widget as NodePtr) as bool
 IF widget = 0 THEN debug "ee_edit_widget: null widget" : RETURN NO

 DIM changed as bool = NO

 DIM subwidget as string = ""
 IF st.type_data THEN
  IF ee_widget_has_data(widget) THEN subwidget = "data"
 ELSE
  IF ee_widget_has_caption(widget) THEN subwidget = "caption"
 END IF
  
 IF subwidget <> "" THEN
  DIM s as string
  s = GetChildNodeStr(widget, subwidget)
  IF strgrabber(s) THEN
   IF s = "" THEN
    SetChildNode(widget, subwidget)
   ELSE
    SetChildNode(widget, subwidget, s)
   END IF
   changed = YES
  END IF
 END IF
 
 IF keyval(scAnyEnter) > 1 THEN
  IF widget_editor(widget) THEN
   changed = YES
  END IF
 END IF
 
 RETURN changed
END FUNCTION

SUB ee_insertion(byref st as EEState, byval widget as Nodeptr)
 IF keyval(scInsert) > 1 THEN
  IF widget ANDALSO ee_parent_is_full(widget) THEN
   visible_debug "Parent container widget is full, and cannot hold any more child widgets"
   EXIT SUB
  END IF
  DIM kind as string
  kind = ee_prompt_for_widget_kind()
  IF kind <> "" THEN
   DIM newnode as Nodeptr
   newnode = ee_create_widget(st, kind)
   IF widget THEN
    AddSiblingAfter widget, newnode
   ELSE
    DIM node as Nodeptr
    node = NodeByPath(st.root, "/widgets")
    IF node = 0 THEN
     debuginfo "unable to find /widgets container node!"
     EXIT SUB
    END IF
    AddChild node, newnode
   END IF
   st.seek_widget = newnode
   st.changed = YES
   st.state.need_update = YES
  END IF
 END IF
END SUB

SUB ee_rearrange(byref st as EEState, mi as MenuDefItem Ptr)
 DIM widget as Nodeptr
 widget = mi->dataptr
 
 DIM changed as bool = NO

 ee_insertion st, widget

 IF keyval(scShift) > 0 THEN
  IF copy_keychord() THEN
   '--copy this widget
   IF st.clipboard <> 0 THEN FreeNode(st.clipboard)
   st.clipboard = CloneNodeTree(widget)
   st.clipboard_is = widget
   changed = YES
  END IF
  IF paste_keychord() THEN
   '--paste this widget
   IF st.clipboard <> 0 THEN
    AddSiblingAfter(widget, CloneNodeTree(st.clipboard))
    IF NodeHasAncestor(NodeParent(widget), st.clipboard_is) THEN st.clipboard_is = 0 'cosmetic importance only
    changed = YES
   END IF
  END IF
 END IF
 
 IF keyval(scShift) > 0 THEN
  IF keyval(ccUP) > 1 THEN
   ee_swap_widget_up widget
   st.seek_widget = widget
   changed = YES
  END IF
  IF keyval(ccDOWN) > 1 THEN
   ee_swap_widget_down widget
   st.seek_widget = widget
   changed = YES
  END IF
  IF keyval(ccLEFT) > 1 THEN
   ee_swap_widget_left widget
   st.seek_widget = widget
   changed = YES
  END IF
  IF keyval(ccRIGHT) > 1 THEN
   ee_swap_widget_right widget
   st.seek_widget = widget
   changed = YES
  END IF
 END IF
 
 IF keyval(scDelete) > 1 THEN
  IF yesno("Delete this widget?" & CHR(10) & ee_widget_string(st, widget)) THEN
   FreeNode(widget)
   changed = YES
  END IF
 END IF
 
 IF changed THEN
  st.state.need_update = YES
  st.changed = YES
 END IF
END SUB

SUB ee_swap_widget_up(byval widget as Nodeptr)
 IF widget = 0 THEN EXIT SUB
 DIM sib as NodePtr
 sib = PrevSibling(widget, "widget")
 IF sib = 0 THEN EXIT SUB
 SwapSiblingNodes(widget, sib)
END SUB

SUB ee_swap_widget_down(byval widget as Nodeptr)
 IF widget = 0 THEN EXIT SUB
 DIM sib as NodePtr
 sib = NextSibling(widget, "widget")
 IF sib = 0 THEN EXIT SUB
 SwapSiblingNodes(widget, sib)
END SUB

SUB ee_swap_widget_left(byval widget as Nodeptr)
 IF widget = 0 THEN EXIT SUB
 DIM parent as NodePtr
 parent = NodeParent(widget)
 IF parent = 0 THEN EXIT SUB
 AddSiblingAfter(parent, widget)
END SUB

SUB ee_swap_widget_right(byval widget as Nodeptr)
 IF widget = 0 THEN EXIT SUB
 DIM sib as NodePtr
 sib = PrevSibling(widget, "widget")
 IF sib = 0 THEN EXIT SUB
 IF ee_container_check(sib) = NO THEN EXIT SUB
 IF ee_container_is_full(sib) THEN EXIT SUB
 AddChild(sib, widget)
END SUB

SUB ee_refresh (byref st as EEState)
 DIM widgets_container as NodePtr
 widgets_container = NodeByPath(st.doc, "/widgets")
 IF widgets_container = 0 THEN EXIT SUB
 DIM widget as NodePtr
 widget = FirstChild(widgets_container, "widget")
 DO WHILE widget
  ee_refresh st, widget
  widget = NextSibling(widget, "widget")
 LOOP
END SUB

SUB ee_refresh (byref st as EEState, byval widget as NodePtr)
 IF widget = 0 THEN EXIT SUB

 IF widget = 0 THEN
  EXIT SUB
 END IF

 DIM s as string
 s = STRING(st.indent, " ") & ee_widget_string(st, widget)
 
 DIM index as integer
 index = append_menu_item(st.menu, s)
 
 DIM mi as MenuDefItem Ptr
 mi = st.menu.items[index]

 mi->dataptr = widget
 mi->extra(0) = st.indent

 st.indent += 1 
 DIM chnode as Nodeptr
 chnode = FirstChild(widget, "widget")
 DO WHILE chnode
  ee_refresh st, chnode
  chnode = NextSibling(chnode, "widget")
 LOOP
 st.indent -= 1
END SUB

FUNCTION ee_grey_str(s as string) as string
 RETURN "${K" & uilook(uiDisabledItem) & "}" & s & "${K-1}"
END FUNCTION

FUNCTION ee_emphasis_str(s as string) as string
 RETURN "${K" & uilook(uiSelectedDisabled) & "}" & s & "${K-1}"
END FUNCTION

FUNCTION ee_widget_string(byref st as EEState, byval widget as Nodeptr) as string
 IF widget = 0 THEN debug "ee_widget_string: null node" : RETURN "<null ptr>"
 DIM s as string = ""
 IF NodeHasAncestor(widget, st.clipboard_is) then s &= "*"
 s &= ee_grey_str("<") & GetString(widget) & ee_grey_str(">")
 IF NOT st.type_data THEN s &= ee_emphasis_str("[")
 s &= GetChildNodeStr(widget, "caption", "")
 IF NOT st.type_data THEN s &= ee_emphasis_str("]")
 IF ee_widget_has_data(widget) THEN
  IF st.type_data THEN s &= ee_emphasis_str("[")
  s &= ee_widget_data_node_name(widget)
  IF st.type_data THEN s &= ee_emphasis_str("]")
 END IF
 RETURN s
END FUNCTION

FUNCTION ee_widget_data_node_name(byval widget as NodePtr) as string
 DIM s as string
 s = GetChildNodeStr(widget, "data", "")
 DIM cont as NodePtr
 cont = ee_get_widget_parent_container(widget)
 IF cont THEN
  DIM ps as string = ee_widget_data_node_name(cont)
  IF ps <> "" AND s <> "" THEN
   RETURN ps & "/" & s
  END IF
 END IF
 RETURN s
END FUNCTION

SUB ee_focus_widget(byref st as EEState, byval widget as Nodeptr)
 DIM mi as MenuDefItem Ptr
 DIM n as Nodeptr
 FOR i as integer = 0 TO st.menu.numitems - 1
  mi = st.menu.items[i]
  n = mi->dataptr
  IF n = widget THEN
   st.state.pt = i
   EXIT FOR
  END IF
 NEXT i
 correct_menu_state st.state
END SUB

SUB ee_export(byref st as EEState)
 DIM outfile as string
 outfile = inputfilename("Export editor definition", "", "", "input_file_export_ee", st.filename)
 IF outfile <> "" THEN
  IF INSTR(outfile, ".") = 0 THEN outfile &= ".editor"
  ee_save outfile, st
 END IF
END SUB

FUNCTION ee_browse(byref st as EEState) as integer
 DIM filename as string
 filename = browse(browseRELOAD, "", "*.editor", "browse_import_ee")
 IF filename = "" THEN RETURN NO
 RETURN ee_load(filename, st)
END FUNCTION

FUNCTION ee_load(filename as string, byref st as EEState) as integer
 st.filename = ""
 FreeDocument st.doc
 st.doc = LoadDocument(filename, optNoDelay)
 IF st.doc = 0 THEN debug "load '" & filename & "' failed: null doc": RETURN NO
 st.root = DocumentRoot(st.doc)
 IF st.root = 0 THEN debug "load '" & filename & "' failed: null root node": RETURN NO
 st.filename = trimpath(filename)
 st.changed = NO
 RETURN YES
END FUNCTION

SUB ee_save(filename as string, byref st as EEState)
 SerializeBin(filename, st.doc)
 st.filename = trimpath(filename)
 st.changed = NO
END SUB

FUNCTION ee_okay_to_unload(byref st as EEState) as integer
 IF st.changed = NO THEN RETURN YES
 DIM choice as integer
 'Prevent attempt to quit the program, stop and wait for response first
 DIM quitting as bool = getquitflag()
 setquitflag NO
 choice = twochoice("Save your changes before exiting?", "Yes, save", "No, discard")
 IF getquitflag() THEN choice = 1  'Second attempt to close the program: discard
 SELECT CASE choice
  CASE -1: 'cancelled
   RETURN NO
  CASE 0: 'yes, save!
   ee_export st
   'but only actually allow unload if the save was confirmed
   IF st.changed = NO THEN
    IF quitting THEN setquitflag
    RETURN YES
   END IF
   RETURN NO
  CASE 1: 'no discard!
   IF quitting THEN setquitflag
   RETURN YES
 END SELECT
 RETURN NO
END FUNCTION

'-----------------------------------------------------------------------
'Update these to add a new widget:

FUNCTION ee_prompt_for_widget_kind() as string
 STATIC last_kind as integer = 0
 'The order widgets are listed here doesn't matter for anything other than the order they appear in the menu
 DIM w(...) as string = { _
  "int",_
  "string",_
  "label",_
  "bool",_
  "submenu",_
  "picture",_
  "item",_
  "attack",_
  "textbox",_
  "tag",_
  "tagcheck",_
  "equipslot",_
  "multi",_
  "array",_
  "maybe",_
  "exclusive"}
 DIM choice as integer
 choice = multichoice("Insert which kind of widget?", w(), last_kind, , "ee_prompt_for_widget_kind")
 IF choice = -1 THEN RETURN ""
 last_kind = choice
 RETURN w(choice)
END FUNCTION

FUNCTION ee_create_widget(byref st as EEState, kind as string) as NodePtr
 DIM widget as NodePtr
 widget = CreateNode(st.doc, "widget")
 SetContent(widget, kind)
 'We could add sub-nodes below if we wanted to
 SELECT CASE kind
  CASE "int":
  CASE "string":
  CASE "label":
  CASE "bool":
  CASE "submenu":
  CASE "picture":
  CASE "item":
  CASE "attack":
  CASE "textbox":
  CASE "tag":
  CASE "tagcheck":
  CASE "multi":
  CASE "array":
  CASE "maybe":
  CASE "exclusive":
  CASE "equipslot":
  CASE ELSE
   debug "Oops! Created a widget of kind """ & kind & """, but we have no idea what that is!"
 END SELECT
 RETURN widget
END FUNCTION

FUNCTION ee_container_check(byval cont as NodePtr) as bool
 IF cont = 0 THEN RETURN NO
 SELECT CASE GetString(cont)
  CASE "submenu": RETURN YES
  CASE "array": RETURN YES
  CASE "maybe": RETURN YES
  CASE "exclusive": RETURN YES
  CASE "multi": RETURN YES
 END SELECT
 RETURN NO
END FUNCTION

FUNCTION ee_container_single_check(byval cont as NodePtr) as bool
 'For containers that only hold one child widget
 IF cont = 0 THEN RETURN NO
 SELECT CASE GetString(cont)
  CASE "multi": RETURN YES
 END SELECT
 RETURN NO
END FUNCTION

FUNCTION ee_widget_has_caption(byval widget as NodePtr) as integer
 'True for widgets that use a caption node.
 IF widget = 0 THEN RETURN NO
 SELECT CASE GetString(widget)
  CASE "array": RETURN NO
  CASE "maybe": RETURN NO
 END SELECT
 RETURN YES
END FUNCTION

FUNCTION ee_widget_has_data(byval widget as NodePtr) as integer
 'True for widgets that use a data node
 IF widget = 0 THEN RETURN NO
 SELECT CASE GetString(widget)
  CASE "label": RETURN NO
 END SELECT
 RETURN YES
END FUNCTION

'-----------------------------------------------------------------------

FUNCTION ee_count_children(byval cont as NodePtr) as integer
 IF cont = 0 THEN RETURN 0
 DIM result as integer = 0
 DIM ch as NodePtr = FirstChild(cont, "widget")
 DO WHILE ch
  result += 1
  ch = NextSibling(ch, "widget")
 LOOP
 IF result > 0 THEN
  IF NOT ee_container_check(cont) THEN debug "ee_count_children: Found " & result & " child widgets on non-container: " & GetString(cont) 
  IF result > 1 ANDALSO ee_container_single_check(cont) THEN
   debug "ee_count_children: Found " & result & " child widgets on a container that only allows one child: " & GetString(cont)
  END IF
 END IF
 RETURN result
END FUNCTION

FUNCTION ee_container_is_full(byval cont as NodePtr) as bool
 IF cont = 0 THEN debug "ee_container_is_full: null cont" : RETURN YES
 IF NOT ee_container_check(cont) THEN
  debug "ee_container_is_full: " & GetString(cont) & " is not a container"
  RETURN YES
 END IF
 IF ee_container_single_check(cont) ANDALSO ee_count_children(cont) >= 1 THEN RETURN YES
 RETURN NO
END FUNCTION

FUNCTION ee_parent_is_full(byval cont as NodePtr) as bool
 IF cont = 0 THEN debug "ee_parent_is_full: null cont" : RETURN YES
 DIM parent as NodePtr = NodeParent(cont)
 IF parent = 0 THEN debug "ee_parent_is_full: no parent exits" : RETURN YES
 DIM pname as string = NodeName(parent)
 debug "ee_parent_is_full: parent name = """ & pname & """"
 IF pname <> "widget" THEN
  'Parent is not a widget
  IF pname = "widgets" THEN
   'Top level widget is never full
   RETURN NO
  END IF
  debug "ee_parent_is_full: unknown parent node name: """ & pname & """"
  RETURN YES
 END IF
 IF NOT ee_container_check(parent) THEN
  debug "ee_parent_is_full: parent is not really a container: """ & GetString(parent) & """"
  RETURN YES
 END IF
 RETURN ee_container_is_full(parent)
END FUNCTION

FUNCTION ee_get_widget_parent_container(byval widget as NodePtr) as NodePtr
 IF widget = 0 THEN debug "ee_get_widget_parent_container: null widget": RETURN 0
 DIM p as NodePtr
 p = NodeParent(widget)
 IF p = 0 THEN RETURN 0 ' no parent exists
 IF NodeName(p) = "widgets" THEN RETURN 0 ' Is a top-level widget
 IF NodeName(p) <> "widget" THEN
  debug "ee_get_widget_parent_container: parent node of unknown name """ & NodeName(p) & """"
  RETURN 0
 END IF
 IF NOT ee_container_check(p) THEN
  debug "ee_get_widget_parent_container: parent node " & NodeName(p) & " is not a container"
  RETURN 0
 END IF
 RETURN p
END FUNCTION

'-----------------------------------------------------------------------

FUNCTION widget_editor(byval widget as NodePtr) as bool

 DIM st as WEState
 st.changed = NO

 st.state.pt = 1
 st.state.need_update = YES
 st.state.active = YES
 st.state.autosize = YES
 st.state.autosize_ignore_pixels = 16

 InitLikeStandardMenu st.menu
 
 ee_get_widget_code(st.code, widget)
 
 setkeys YES
 DO
  setwait 55
  setkeys YES

  IF st.state.need_update THEN
   st.state.need_update = NO
   DeleteMenuItems st.menu
   widget_editor_refresh st, widget
   init_menu_state st.state, st.menu
  END IF
  
  IF keyval(ccCancel) > 1 ORELSE st.exit_please THEN
   EXIT DO 
  END IF
  IF keyval(scF1) > 1 THEN show_help("widget_editor")

  IF st.state.pt >= 0 AND st.state.pt <= st.menu.numitems - 1 THEN
   widget_editor_edit_menu_item st, st.menu.items[st.state.pt]
  END IF
  
  IF keyval(scShift) = 0 THEN
   usemenu st.state
  END IF

  clearpage dpage
  draw_menu st.menu, st.state, dpage
  edgeprint "F1=Help", pInfoX, pInfoY, uilook(uiText), dpage

  SWAP vpage, dpage
  setvispage vpage
  dowait
 LOOP

 RETURN st.changed 
END FUNCTION

SUB widget_editor_edit_menu_item(byref st as WEState, mi as MenuDefItem Ptr)
 IF mi = 0 THEN debug "widget_editor_edit_menu_item: null mi": EXIT SUB
 DIM kind as integer = mi->t
 DIM node as NodePtr 
 node = mi->dataptr
 IF kind <> wedEXIT THEN
  IF node = 0 THEN debug "widget_editor_edit_menu_item: mi (" & mi->caption & ") has null node": EXIT SUB
 END IF

 IF widget_editor_edit_node(st, kind, node) THEN
  st.changed = YES
  'Something changed, signal to rebuild the menu strings
  st.state.need_update = YES
 END IF

END SUB

FUNCTION widget_editor_edit_node(byref st as WEState, byval kind as integer, byval node as NodePtr) as bool
 IF node = 0 THEN debug "widget_editor_edit_node: null node" : RETURN NO

 DIM changed as bool = NO

 SELECT CASE kind
  CASE wedEXIT:
   IF enter_or_space() THEN st.exit_please = YES
  CASE wedSTRING:
   DIM s as string = GetString(node)
   IF strgrabber(s) THEN
    SetContent node, s
    changed = YES
   END IF
  CASE wedINT:
   DIM i as integer = GetInteger(node)
   IF intgrabber(i, -1000000, 1000000) THEN 'The +- 1 million limit is totally arbitrary
    SetContent node, i
    changed = YES
   END IF
  CASE wedBOOL:
   DIM b as bool = GetInteger(node) <> 0
   IF toggle_item(st.state) THEN
    SetContent node, IIF(b, 0, 1)
    changed = YES
   END IF
 END SELECT
  
 RETURN changed
END FUNCTION

'-----------------------------------------------------------------------

SUB wed_append_editable_string(byref st as WEState, caption as string, byval widget as NodePtr, sub_widget_name as string)
 DIM n as NodePtr = GetOrCreateChild(widget, sub_widget_name)
 DIM s as string = GetString(n)
 append_menu_item(st.menu, caption & ":" & s, wedSTRING, , n)
END SUB

SUB wed_append_zdefault_int(byref st as WEState, caption as string, byval widget as NodePtr, sub_widget_name as string)
 DIM n as NodePtr = GetOrCreateChild(widget, sub_widget_name)
 DIM i as integer = GetInteger(n)
 append_menu_item(st.menu, caption & ":" & zero_default(i), wedINT, , n)
END SUB

SUB wed_append_bool(byref st as WEState, caption as string, byval widget as NodePtr, sub_widget_name as string)
 DIM n as NodePtr = GetOrCreateChild(widget, sub_widget_name)
 DIM b as bool = GetInteger(n) <> 0
 append_menu_item(st.menu, caption & ":" & yesorno(b), wedBOOL, , n)
END SUB

SUB wed_append_zero_or_one(byref st as WEState, caption as string, byval widget as NodePtr, sub_widget_name as string)
 DIM n as NodePtr = GetOrCreateChild(widget, sub_widget_name)
 DIM i as integer = GetInteger(n)
 append_menu_item(st.menu, caption & ":" & i & IIF(i < 0 ORELSE i > 1, " [should be 0 or 1!]", ""), wedINT, , n)
END SUB

SUB widget_editor_refresh(byref st as WEState, byval widget as NodePtr)
 DIM index as integer
 append_menu_item(st.menu, "Done editing this Widget...", wedEXIT)
 IF ee_widget_has_caption(widget) THEN
  wed_append_editable_string(st, "Caption", widget, "caption")
 END IF
 IF ee_widget_has_data(widget) THEN
  wed_append_editable_string(st, "Data Node", widget, "data")
 END IF
 st.code.refresh_callback(st, widget)
END SUB

'-----------------------------------------------------------------------

SUB null_widget_refresh(byref st as WEState, byval widget as NodePtr)
 'for widgets that don't have any extra properties.
END SUB

SUB int_widget_refresh(byref st as WEState, byval widget as NodePtr)
 wed_append_zdefault_int(st, "Max", widget, "max")
 wed_append_zdefault_int(st, "Min", widget, "min")
 wed_append_editable_string(st, "Enum key", widget, "enum")
 wed_append_bool(st, "Optional", widget, "optional")
 wed_append_bool(st, "Zero Default", widget, "zerodefault")
 wed_append_bool(st, "-1 Default", widget, "neg1default")
END SUB

SUB picture_widget_refresh(byref st as WEState, byval widget as NodePtr)
 append_menu_item(st.menu, "Size Group:" & GetChildNodeInt(widget, "sizegroup"))
 append_menu_item(st.menu, "Save Size:" & yesorno(GetChildNodeBool(widget, "savesize")))
END SUB

SUB tagcheck_widget_refresh(byref st as WEState, byval widget as NodePtr)
 append_menu_item(st.menu, "Default Description:" & GetChildNodeStr(widget, "default"))
END SUB

SUB multi_widget_refresh(byref st as WEState, byval widget as NodePtr)
 wed_append_zero_or_one(st, "Minimum repetitions", widget, "min_nodes")
END SUB

SUB array_widget_refresh(byref st as WEState, byval widget as NodePtr)
 append_menu_item(st.menu, "Count:" & zero_default(GetChildNodeInt(widget, "count"), "variable length"))
 append_menu_item(st.menu, "Key:" & GetChildNodeStr(widget, "key"))
 append_menu_item(st.menu, "Enum:" & GetChildNodeStr(widget, "enum"))
END SUB

SUB maybe_widget_refresh(byref st as WEState, byval widget as NodePtr)
 append_menu_item(st.menu, "Hide:" & yesorno(GetChildNodeBool(widget, "hide")))
END SUB

'-----------------------------------------------------------------------
'#######################################################################

'--this is at the end of the file because I want to be lazy and not bother
'  with separate declares for each of the callbacks above.
SUB ee_get_widget_code(byref code as WidgetCode, byval widget as NodePtr)
 WITH code
  .refresh_callback = @null_widget_refresh

  IF widget = 0 THEN EXIT SUB
  DIM kind as string
  kind = GetString(widget)
  
  SELECT CASE kind
   CASE "int":
    .refresh_callback = @int_widget_refresh
   CASE "string":
   CASE "label":
   CASE "bool":
   CASE "picture":
    .refresh_callback = @picture_widget_refresh
   CASE "item":
   CASE "attack":
   CASE "tagcheck":
    .refresh_callback = @tagcheck_widget_refresh
   CASE "tag":
   CASE "multi":
    .refresh_callback = @multi_widget_refresh
   CASE "array":
    .refresh_callback = @array_widget_refresh
   CASE "maybe":
    .refresh_callback = @maybe_widget_refresh
  END SELECT
 END WITH
END SUB
