|
|
|
@ -1,6 +1,6 @@
|
|
|
|
|
-module(gmc_v_devman).
|
|
|
|
|
-vsn("0.2.0").
|
|
|
|
|
-author("Craig Everett <zxq9@zxq9.com>").
|
|
|
|
|
-author("Craig Everett <craigeverett@qpq.swiss>").
|
|
|
|
|
-copyright("QPQ AG <info@qpq.swiss>").
|
|
|
|
|
-license("GPL-3.0-or-later").
|
|
|
|
|
|
|
|
|
@ -8,35 +8,41 @@
|
|
|
|
|
%-behavior(gmc_v).
|
|
|
|
|
-include_lib("wx/include/wx.hrl").
|
|
|
|
|
-export([to_front/1]).
|
|
|
|
|
-export([set_manifest/1]).
|
|
|
|
|
-export([set_manifest/1, trouble/1]).
|
|
|
|
|
-export([start_link/1]).
|
|
|
|
|
-export([init/1, terminate/2, code_change/3,
|
|
|
|
|
handle_call/3, handle_cast/2, handle_info/2, handle_event/2]).
|
|
|
|
|
-include("$zx_include/zx_logger.hrl").
|
|
|
|
|
-include("gmc.hrl").
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% Widgets
|
|
|
|
|
-record(w,
|
|
|
|
|
{name = none :: atom() | {FunName :: binary(), call | dryr},
|
|
|
|
|
id = 0 :: integer(),
|
|
|
|
|
wx = none :: none | wx:wx_object()}).
|
|
|
|
|
|
|
|
|
|
% Contract functions in an ACI
|
|
|
|
|
-record(f,
|
|
|
|
|
{name = <<"">> :: binary(),
|
|
|
|
|
call = #w{} :: #w{},
|
|
|
|
|
dryrun = #w{} :: none | #w{},
|
|
|
|
|
args = [] :: [{wx:wx_object(), wx:wx_object(), argt()}]}).
|
|
|
|
|
|
|
|
|
|
% Code book pages
|
|
|
|
|
-record(p,
|
|
|
|
|
{path = "" :: file:filename(),
|
|
|
|
|
{path = {file, ""} :: {file, file:filename()} | {hash, binary()},
|
|
|
|
|
win = none :: none | wx:wx_object(),
|
|
|
|
|
code = none :: none | wxStyledTextCtrl:wxStyledTextCtrl()}).
|
|
|
|
|
|
|
|
|
|
% Contract pages
|
|
|
|
|
-record(c,
|
|
|
|
|
{id = "" :: string(),
|
|
|
|
|
win = none :: none | wx:wx_object(),
|
|
|
|
|
code = none :: none | wxStyledTextCtrl:wxStyledTextCtrl(),
|
|
|
|
|
cons = none :: none | wxStyledTextCtrl:wxStyledTextCtrl(),
|
|
|
|
|
side_sz = none :: none | wx:wx_object(),
|
|
|
|
|
instances = none :: none | wx:wx_object(),
|
|
|
|
|
funs = {#w{}, []} :: {#w{}, [#f{}]},
|
|
|
|
|
builds = #{} :: #{InstanceID :: binary() := Build :: map()}}).
|
|
|
|
|
funs = {#w{}, []} :: {#w{}, [#f{}]}}).
|
|
|
|
|
|
|
|
|
|
% State
|
|
|
|
|
-record(s,
|
|
|
|
|
{wx = none :: none | wx:wx_object(),
|
|
|
|
|
frame = none :: none | wx:wx_object(),
|
|
|
|
@ -44,7 +50,9 @@
|
|
|
|
|
j = none :: none | fun(),
|
|
|
|
|
prefs = #{} :: map(),
|
|
|
|
|
buttons = #{} :: #{WX_ID :: integer() := #w{}},
|
|
|
|
|
book = {none, []} :: {Notebook :: none | wx:wx_object(), Pages :: [#p{}]}}).
|
|
|
|
|
tabs = none :: none | wx:wx_object(),
|
|
|
|
|
code = {none, []} :: {Codebook :: none | wx:wx_object(), Pages :: [#p{}]},
|
|
|
|
|
cons = {none, []} :: {Consbook :: none | wx:wx_object(), Pages :: [#c{}]}}).
|
|
|
|
|
|
|
|
|
|
-type argt() :: int | string | address | list(argt()).
|
|
|
|
|
|
|
|
|
@ -67,6 +75,13 @@ set_manifest(Entries) ->
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-spec trouble(Info) -> ok
|
|
|
|
|
when Info :: term().
|
|
|
|
|
|
|
|
|
|
trouble(Info) ->
|
|
|
|
|
wx_object:cast(?MODULE, {trouble, Info}).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%%% Startup Functions
|
|
|
|
@ -83,133 +98,84 @@ init({Prefs, Manifest}) ->
|
|
|
|
|
Frame = wxFrame:new(Wx, ?wxID_ANY, J("Contracts")),
|
|
|
|
|
|
|
|
|
|
MainSz = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
|
|
|
|
|
ButtonTemplates =
|
|
|
|
|
[{new, J("New")},
|
|
|
|
|
{open, J("Open")},
|
|
|
|
|
{save, J("Save")},
|
|
|
|
|
{saven, J("Save (new name)")},
|
|
|
|
|
{compile, J("Compile")},
|
|
|
|
|
{close, J("Close")}],
|
|
|
|
|
|
|
|
|
|
MakeButton =
|
|
|
|
|
fun({Name, Label}) ->
|
|
|
|
|
B = wxButton:new(Frame, ?wxID_ANY, [{label, Label}]),
|
|
|
|
|
#w{name = Name, id = wxButton:getId(B), wx = B}
|
|
|
|
|
end,
|
|
|
|
|
|
|
|
|
|
Buttons = lists:map(MakeButton, ButtonTemplates),
|
|
|
|
|
|
|
|
|
|
AddButton = fun(#w{wx = B}) -> wxSizer:add(ButtSz, B, zxw:flags(wide)) end,
|
|
|
|
|
|
|
|
|
|
Notebook = wxNotebook:new(Frame, ?wxID_ANY, [{style, ?wxBK_DEFAULT}]),
|
|
|
|
|
|
|
|
|
|
ok = lists:foreach(AddButton, Buttons),
|
|
|
|
|
_ = wxSizer:add(MainSz, ButtSz, zxw:flags(base)),
|
|
|
|
|
_ = wxSizer:add(MainSz, Notebook, zxw:flags(wide)),
|
|
|
|
|
TopBook = wxNotebook:new(Frame, ?wxID_ANY, [{style, ?wxBK_DEFAULT}]),
|
|
|
|
|
{LWin, LButtons, Codebook} = make_l_win(TopBook, J),
|
|
|
|
|
{RWin, RButtons, Consbook} = make_r_win(TopBook, J),
|
|
|
|
|
ButtonMap = maps:merge(LButtons, RButtons),
|
|
|
|
|
true = wxNotebook:addPage(TopBook, LWin, J("Contract Editor"), []),
|
|
|
|
|
true = wxNotebook:addPage(TopBook, RWin, J("Deployed Contracts"), []),
|
|
|
|
|
State =
|
|
|
|
|
#s{wx = Wx, frame = Frame,
|
|
|
|
|
j = J, prefs = Prefs,
|
|
|
|
|
buttons = ButtonMap, tabs = TopBook,
|
|
|
|
|
code = {Codebook, []}, cons = {Consbook, []}},
|
|
|
|
|
_ = wxSizer:add(MainSz, TopBook, zxw:flags(wide)),
|
|
|
|
|
_ = wxFrame:setSizer(Frame, MainSz),
|
|
|
|
|
_ = wxSizer:layout(MainSz),
|
|
|
|
|
|
|
|
|
|
ok = gmc_v:safe_size(Frame, Prefs),
|
|
|
|
|
|
|
|
|
|
ok = wxFrame:connect(Frame, close_window),
|
|
|
|
|
ok = wxFrame:connect(Frame, command_button_clicked),
|
|
|
|
|
ok = wxFrame:center(Frame),
|
|
|
|
|
true = wxFrame:show(Frame),
|
|
|
|
|
|
|
|
|
|
MapButton = fun(B = #w{id = I}, M) -> maps:put(I, B, M) end,
|
|
|
|
|
ButtonMap = lists:foldl(MapButton, #{}, Buttons),
|
|
|
|
|
State =
|
|
|
|
|
#s{wx = Wx, frame = Frame,
|
|
|
|
|
j = J, prefs = Prefs,
|
|
|
|
|
buttons = ButtonMap, book = {Notebook, []}},
|
|
|
|
|
NewState = add_pages(State, Manifest),
|
|
|
|
|
NewState = add_code_pages(State, Manifest),
|
|
|
|
|
{Frame, NewState}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
add_pages(State, Files) ->
|
|
|
|
|
lists:foldl(fun add_page/2, State, Files).
|
|
|
|
|
make_l_win(TopBook, J) ->
|
|
|
|
|
Win = wxWindow:new(TopBook, ?wxID_ANY),
|
|
|
|
|
MainSz = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
ButtonTemplates =
|
|
|
|
|
[{new, J("New")},
|
|
|
|
|
{open, J("Open")},
|
|
|
|
|
{save, J("Save")},
|
|
|
|
|
{rename, J("Save (rename)")},
|
|
|
|
|
{deploy, J("Deploy")},
|
|
|
|
|
{close_source, J("Close")}],
|
|
|
|
|
MakeButton =
|
|
|
|
|
fun({Name, Label}) ->
|
|
|
|
|
B = wxButton:new(Win, ?wxID_ANY, [{label, Label}]),
|
|
|
|
|
#w{name = Name, id = wxButton:getId(B), wx = B}
|
|
|
|
|
end,
|
|
|
|
|
Buttons = lists:map(MakeButton, ButtonTemplates),
|
|
|
|
|
AddButton = fun(#w{wx = B}) -> wxSizer:add(ButtSz, B, zxw:flags(wide)) end,
|
|
|
|
|
Codebook = wxNotebook:new(Win, ?wxID_ANY, [{style, ?wxBK_DEFAULT}]),
|
|
|
|
|
ok = lists:foreach(AddButton, Buttons),
|
|
|
|
|
_ = wxSizer:add(MainSz, ButtSz, zxw:flags(base)),
|
|
|
|
|
_ = wxSizer:add(MainSz, Codebook, zxw:flags(wide)),
|
|
|
|
|
_ = wxWindow:setSizer(Win, MainSz),
|
|
|
|
|
MapButton = fun(B = #w{id = I}, M) -> maps:put(I, B, M) end,
|
|
|
|
|
ButtonMap = lists:foldl(MapButton, #{}, Buttons),
|
|
|
|
|
{Win, ButtonMap, Codebook}.
|
|
|
|
|
|
|
|
|
|
add_page(State = #s{book = {Notebook, Pages}}, File) ->
|
|
|
|
|
case keyfind_index(File, #p.path, Pages) of
|
|
|
|
|
error ->
|
|
|
|
|
add_page2(State, File);
|
|
|
|
|
{ok, Index} ->
|
|
|
|
|
_ = wxNotebook:setSelection(Notebook, Index - 1),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
make_r_win(TopBook, J) ->
|
|
|
|
|
Win = wxWindow:new(TopBook, ?wxID_ANY),
|
|
|
|
|
MainSz = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
ButtonTemplates =
|
|
|
|
|
[{load, J("Load from Chain")},
|
|
|
|
|
{edit, J("Copy to Editor")},
|
|
|
|
|
{close_instance, J("Close")}],
|
|
|
|
|
MakeButton =
|
|
|
|
|
fun({Name, Label}) ->
|
|
|
|
|
B = wxButton:new(Win, ?wxID_ANY, [{label, Label}]),
|
|
|
|
|
#w{name = Name, id = wxButton:getId(B), wx = B}
|
|
|
|
|
end,
|
|
|
|
|
Buttons = lists:map(MakeButton, ButtonTemplates),
|
|
|
|
|
AddButton = fun(#w{wx = B}) -> wxSizer:add(ButtSz, B, zxw:flags(wide)) end,
|
|
|
|
|
Codebook = wxNotebook:new(Win, ?wxID_ANY, [{style, ?wxBK_DEFAULT}]),
|
|
|
|
|
ok = lists:foreach(AddButton, Buttons),
|
|
|
|
|
_ = wxSizer:add(MainSz, ButtSz, zxw:flags(base)),
|
|
|
|
|
_ = wxSizer:add(MainSz, Codebook, zxw:flags(wide)),
|
|
|
|
|
_ = wxWindow:setSizer(Win, MainSz),
|
|
|
|
|
MapButton = fun(B = #w{id = I}, M) -> maps:put(I, B, M) end,
|
|
|
|
|
ButtonMap = lists:foldl(MapButton, #{}, Buttons),
|
|
|
|
|
{Win, ButtonMap, Codebook}.
|
|
|
|
|
|
|
|
|
|
add_page2(State = #s{j = J}, File) ->
|
|
|
|
|
case file:read_file(File) of
|
|
|
|
|
{ok, Bin} ->
|
|
|
|
|
case unicode:characters_to_list(Bin) of
|
|
|
|
|
Code when is_list(Code) ->
|
|
|
|
|
add_page(State, File, Code);
|
|
|
|
|
Error ->
|
|
|
|
|
Message = io_lib:format(J("Opening ~p failed with: ~p"), [File, Error]),
|
|
|
|
|
ok = gmc_gui:trouble(Message),
|
|
|
|
|
State
|
|
|
|
|
end;
|
|
|
|
|
{error, Reason} ->
|
|
|
|
|
Message = io_lib:format(J("Opening ~p failed with: ~p"), [File, Reason]),
|
|
|
|
|
ok = gmc_gui:trouble(Message),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
add_page(State = #s{j = J, book = {Notebook, Pages}}, File, Code) ->
|
|
|
|
|
% FIXME: One of these days we need to define the text area as a wxStyledTextCtrl and will
|
|
|
|
|
% have to contend with system theme issues (light/dark themese, namely)
|
|
|
|
|
% Leaving this little thing here to remind myself how any of that works later.
|
|
|
|
|
% The call below returns a wx_color4() type (not that we need alpha...).
|
|
|
|
|
% Color = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW),
|
|
|
|
|
% tell("Color: ~p", [Color]),
|
|
|
|
|
Window = wxWindow:new(Notebook, ?wxID_ANY),
|
|
|
|
|
PageSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
|
|
|
|
|
ProgSz = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
CodeSz = wxStaticBoxSizer:new(?wxVERTICAL, Window, [{label, J("Code")}]),
|
|
|
|
|
ConsSz = wxStaticBoxSizer:new(?wxVERTICAL, Window, [{label, J("Console")}]),
|
|
|
|
|
CodeTxStyle = {style, ?wxTE_MULTILINE bor ?wxTE_PROCESS_TAB bor ?wxTE_DONTWRAP},
|
|
|
|
|
CodeTx = wxTextCtrl:new(Window, ?wxID_ANY, [CodeTxStyle]),
|
|
|
|
|
ConsTxStyle = {style, ?wxTE_MULTILINE bor ?wxTE_READONLY},
|
|
|
|
|
ConsTx = wxTextCtrl:new(Window, ?wxID_ANY, [ConsTxStyle]),
|
|
|
|
|
Mono = wxFont:new(10, ?wxMODERN, ?wxNORMAL, ?wxNORMAL, [{face, "Monospace"}]),
|
|
|
|
|
TextAt = wxTextAttr:new(),
|
|
|
|
|
ok = wxTextAttr:setFont(TextAt, Mono),
|
|
|
|
|
true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt),
|
|
|
|
|
ok = wxTextCtrl:setValue(CodeTx, Code),
|
|
|
|
|
|
|
|
|
|
_ = wxSizer:add(CodeSz, CodeTx, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(ConsSz, ConsTx, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(ProgSz, CodeSz, [{proportion, 5}, {flag, ?wxEXPAND}]),
|
|
|
|
|
_ = wxSizer:add(ProgSz, ConsSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
|
|
|
|
|
|
|
|
|
SideSz = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
|
|
|
|
|
InstanceSz = wxStaticBoxSizer:new(?wxVERTICAL, Window, [{label, J("Instances")}]),
|
|
|
|
|
Instances = wxChoice:new(Window, ?wxID_ANY, [{choices, []}]),
|
|
|
|
|
_ = wxSizer:add(InstanceSz, Instances, zxw:flags(wide)),
|
|
|
|
|
|
|
|
|
|
ScrollWin = wxScrolledWindow:new(Window),
|
|
|
|
|
FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Function Interfaces")}]),
|
|
|
|
|
ok = wxWindow:setSizer(ScrollWin, FunSz),
|
|
|
|
|
_ = wxSizer:add(SideSz, InstanceSz, zxw:flags(base)),
|
|
|
|
|
_ = wxSizer:add(SideSz, ScrollWin, zxw:flags(wide)),
|
|
|
|
|
|
|
|
|
|
_ = wxSizer:add(PageSz, ProgSz, [{proportion, 2}, {flag, ?wxEXPAND}]),
|
|
|
|
|
_ = wxSizer:add(PageSz, SideSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
|
|
|
|
|
|
|
|
|
ok = wxWindow:setSizer(Window, PageSz),
|
|
|
|
|
ok = wxSizer:layout(PageSz),
|
|
|
|
|
FileName = filename:basename(File),
|
|
|
|
|
true = wxNotebook:addPage(Notebook, Window, FileName, [{bSelect, true}]),
|
|
|
|
|
Page = #p{path = File, win = Window,
|
|
|
|
|
code = CodeTx, cons = ConsTx,
|
|
|
|
|
side_sz = SideSz, instances = Instances,
|
|
|
|
|
funs = {ScrollWin, []}},
|
|
|
|
|
NewPages = Pages ++ [Page],
|
|
|
|
|
State#s{book = {Notebook, NewPages}}.
|
|
|
|
|
add_code_pages(State, Files) ->
|
|
|
|
|
lists:foldl(fun add_code_page/2, State, Files).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -223,6 +189,9 @@ handle_call(Unexpected, From, State) ->
|
|
|
|
|
handle_cast(to_front, State = #s{frame = Frame}) ->
|
|
|
|
|
ok = wxFrame:raise(Frame),
|
|
|
|
|
{noreply, State};
|
|
|
|
|
handle_cast({trouble, Info}, State) ->
|
|
|
|
|
ok = handle_troubling(State, Info),
|
|
|
|
|
{noreply, State};
|
|
|
|
|
handle_cast(Unexpected, State) ->
|
|
|
|
|
ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]),
|
|
|
|
|
{noreply, State}.
|
|
|
|
@ -239,9 +208,14 @@ handle_event(E = #wx{event = #wxCommand{type = command_button_clicked},
|
|
|
|
|
NewState =
|
|
|
|
|
case maps:get(ID, Buttons, undefined) of
|
|
|
|
|
#w{name = new} -> new_file(State);
|
|
|
|
|
#w{name = open} -> open_file(State);
|
|
|
|
|
#w{name = close} -> close_file(State);
|
|
|
|
|
#w{name = compile} -> compile(State);
|
|
|
|
|
#w{name = open} -> open(State);
|
|
|
|
|
#w{name = save} -> save(State);
|
|
|
|
|
#w{name = rename} -> rename(State);
|
|
|
|
|
#w{name = deploy} -> deploy(State);
|
|
|
|
|
#w{name = close_source} -> close_source(State);
|
|
|
|
|
#w{name = load} -> load(State);
|
|
|
|
|
#w{name = edit} -> edit(State);
|
|
|
|
|
#w{name = close_instance} -> close_instance(State);
|
|
|
|
|
#w{name = Name, wx = Button} -> clicked(State, Name, Button);
|
|
|
|
|
undefined ->
|
|
|
|
|
tell("Received message: ~w", [E]),
|
|
|
|
@ -267,6 +241,10 @@ handle_event(Event, State) ->
|
|
|
|
|
{noreply, State}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
handle_troubling(#s{frame = Frame}, Info) ->
|
|
|
|
|
zxw:show_message(Frame, Info).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
code_change(_, State, _) ->
|
|
|
|
|
{ok, State}.
|
|
|
|
|
|
|
|
|
@ -279,44 +257,86 @@ terminate(Reason, State) ->
|
|
|
|
|
|
|
|
|
|
%%% Doers
|
|
|
|
|
|
|
|
|
|
clicked(State = #s{book = {Notebook, Pages}}, Name, Button) ->
|
|
|
|
|
case wxNotebook:getSelection(Notebook) of
|
|
|
|
|
clicked(State = #s{cons = {Consbook, Contracts}}, Name, Button) ->
|
|
|
|
|
case wxNotebook:getSelection(Consbook) of
|
|
|
|
|
?wxNOT_FOUND ->
|
|
|
|
|
ok = tell(warning, "Inconcievable! No notebook page is selected!"),
|
|
|
|
|
State;
|
|
|
|
|
Index ->
|
|
|
|
|
Page = lists:nth(Index + 1, Pages),
|
|
|
|
|
clicked(State, Page, Name, Button)
|
|
|
|
|
Contract = lists:nth(Index + 1, Contracts),
|
|
|
|
|
clicked(State, Contract, Name, Button)
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
clicked(State,
|
|
|
|
|
#p{instances = Is, funs = {_, Funs}, builds = Builds},
|
|
|
|
|
{<<"init">>, call},
|
|
|
|
|
_Button) ->
|
|
|
|
|
Label = wxChoice:getStringSelection(Is),
|
|
|
|
|
Build = maps:get(Label, Builds),
|
|
|
|
|
#f{args = Args} = lists:keyfind(<<"init">>, #f.name, Funs),
|
|
|
|
|
InitArgs = lists:map(fun get_arg/1, Args),
|
|
|
|
|
ok = tell("Label: ~p~nArgs: ~p~nInitArgs: ~p", [Label, Args, InitArgs]),
|
|
|
|
|
case gmc_con:deploy(Build, InitArgs) of
|
|
|
|
|
{contract_id, ConID} ->
|
|
|
|
|
ok = tell("Got ConID: ~p", [ConID]),
|
|
|
|
|
State;
|
|
|
|
|
{tx_hash, TX_Hash} ->
|
|
|
|
|
ok = tell("Got TX_Hash: ~p", [TX_Hash]),
|
|
|
|
|
State;
|
|
|
|
|
{error, Reason} ->
|
|
|
|
|
ok = tell("Deploy failed with: ~p", [Reason]),
|
|
|
|
|
State
|
|
|
|
|
end;
|
|
|
|
|
clicked(State, Page, Name, Button) ->
|
|
|
|
|
ok = tell("Button: ~p ~p~nPage: ~p", [Button, Name, Page]),
|
|
|
|
|
clicked(State, Contract, Name, Button) ->
|
|
|
|
|
ok = tell("Button: ~p ~p~nContract: ~p", [Button, Name, Contract]),
|
|
|
|
|
State.
|
|
|
|
|
|
|
|
|
|
get_arg({_, TextCtrl, _}) ->
|
|
|
|
|
wxTextCtrl:getValue(TextCtrl).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
add_code_page(State = #s{code = {Codebook, Pages}}, File) ->
|
|
|
|
|
case keyfind_index({file, File}, #p.path, Pages) of
|
|
|
|
|
error ->
|
|
|
|
|
add_code_page2(State, File);
|
|
|
|
|
{ok, Index} ->
|
|
|
|
|
_ = wxNotebook:setSelection(Codebook, Index - 1),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
add_code_page2(State = #s{j = J}, {file, File}) ->
|
|
|
|
|
case file:read_file(File) of
|
|
|
|
|
{ok, Bin} ->
|
|
|
|
|
case unicode:characters_to_list(Bin) of
|
|
|
|
|
Code when is_list(Code) ->
|
|
|
|
|
add_code_page(State, {file, File}, Code);
|
|
|
|
|
Error ->
|
|
|
|
|
Message = io_lib:format(J("Opening ~p failed with: ~p"), [File, Error]),
|
|
|
|
|
ok = handle_troubling(Message, State),
|
|
|
|
|
State
|
|
|
|
|
end;
|
|
|
|
|
{error, Reason} ->
|
|
|
|
|
Message = io_lib:format(J("Opening ~p failed with: ~p"), [File, Reason]),
|
|
|
|
|
ok = handle_troubling(Message, State),
|
|
|
|
|
State
|
|
|
|
|
end;
|
|
|
|
|
add_code_page2(State, {hash, Address}) ->
|
|
|
|
|
open_hash2(State, Address).
|
|
|
|
|
|
|
|
|
|
add_code_page(State = #s{j = J, tabs = TopBook, code = {Codebook, Pages}}, Location, Code) ->
|
|
|
|
|
% FIXME: One of these days we need to define the text area as a wxStyledTextCtrl and will
|
|
|
|
|
% have to contend with system theme issues (light/dark themese, namely)
|
|
|
|
|
% Leaving this little thing here to remind myself how any of that works later.
|
|
|
|
|
% The call below returns a wx_color4() type (not that we need alpha...).
|
|
|
|
|
% Color = wxSystemSettings:getColour(?wxSYS_COLOUR_WINDOW),
|
|
|
|
|
% tell("Color: ~p", [Color]),
|
|
|
|
|
Window = wxWindow:new(Codebook, ?wxID_ANY),
|
|
|
|
|
PageSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
|
|
|
|
|
CodeTxStyle = {style, ?wxTE_MULTILINE bor ?wxTE_PROCESS_TAB bor ?wxTE_DONTWRAP},
|
|
|
|
|
CodeTx = wxTextCtrl:new(Window, ?wxID_ANY, [CodeTxStyle]),
|
|
|
|
|
TextAt = wxTextAttr:new(),
|
|
|
|
|
Mono = wxFont:new(10, ?wxMODERN, ?wxNORMAL, ?wxNORMAL, [{face, "Monospace"}]),
|
|
|
|
|
ok = wxTextAttr:setFont(TextAt, Mono),
|
|
|
|
|
true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt),
|
|
|
|
|
ok = wxTextCtrl:setValue(CodeTx, Code),
|
|
|
|
|
|
|
|
|
|
_ = wxSizer:add(PageSz, CodeTx, zxw:flags(wide)),
|
|
|
|
|
|
|
|
|
|
ok = wxWindow:setSizer(Window, PageSz),
|
|
|
|
|
ok = wxSizer:layout(PageSz),
|
|
|
|
|
_ = wxNotebook:changeSelection(TopBook, 0),
|
|
|
|
|
FileName =
|
|
|
|
|
case Location of
|
|
|
|
|
{file, Path} -> filename:basename(Path);
|
|
|
|
|
{hash, Addr} -> Addr
|
|
|
|
|
end,
|
|
|
|
|
true = wxNotebook:addPage(Codebook, Window, FileName, [{bSelect, true}]),
|
|
|
|
|
Page = #p{path = Location, win = Window, code = CodeTx},
|
|
|
|
|
NewPages = Pages ++ [Page],
|
|
|
|
|
State#s{code = {Codebook, NewPages}}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
new_file(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
|
|
|
|
DefaultDir =
|
|
|
|
|
case maps:find(dir, Prefs) of
|
|
|
|
@ -351,7 +371,7 @@ new_file(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
|
|
|
|
Path = filename:join(Dir, File),
|
|
|
|
|
NewPrefs = maps:put(dir, Dir, Prefs),
|
|
|
|
|
NextState = State#s{prefs = NewPrefs},
|
|
|
|
|
add_page(NextState, Path, "")
|
|
|
|
|
add_code_page(NextState, {file, Path}, "")
|
|
|
|
|
end;
|
|
|
|
|
?wxID_CANCEL ->
|
|
|
|
|
State
|
|
|
|
@ -360,44 +380,74 @@ new_file(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
|
|
|
|
NewState.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
compile(State = #s{book = {Notebook, Pages}}) ->
|
|
|
|
|
case wxNotebook:getSelection(Notebook) of
|
|
|
|
|
deploy(State = #s{code = {Codebook, Pages}}) ->
|
|
|
|
|
case wxNotebook:getSelection(Codebook) of
|
|
|
|
|
?wxNOT_FOUND ->
|
|
|
|
|
State;
|
|
|
|
|
Index ->
|
|
|
|
|
Page = #p{code = CodeTx} = lists:nth(Index + 1, Pages),
|
|
|
|
|
Source = wxTextCtrl:getValue(CodeTx),
|
|
|
|
|
compile(State, Page, Source)
|
|
|
|
|
deploy2(State, Source)
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
compile(State = #s{book = {Notebook, Pages}, j = J, buttons = Buttons},
|
|
|
|
|
Page = #p{path = Path, win = Win, cons = ConsTx,
|
|
|
|
|
side_sz = SideSz, instances = Instances, funs = Funs, builds = Builds},
|
|
|
|
|
Source) ->
|
|
|
|
|
deploy2(State, Source) ->
|
|
|
|
|
case aeso_compiler:from_string(Source, [{aci, json}]) of
|
|
|
|
|
{ok, Build = #{aci := ACI}} ->
|
|
|
|
|
BuildName = build_name(Path),
|
|
|
|
|
I = wxChoice:append(Instances, BuildName),
|
|
|
|
|
ok = wxChoice:select(Instances, I),
|
|
|
|
|
NewBuilds = maps:put(BuildName, Build, Builds),
|
|
|
|
|
FunDefs = find_main(ACI),
|
|
|
|
|
UpdateInterfaces = fun() -> fun_interfaces(Win, Buttons, Funs, FunDefs, J) end,
|
|
|
|
|
{NewButtons, NewFuns} = wx:batch(UpdateInterfaces),
|
|
|
|
|
ScrollWin = element(1, NewFuns),
|
|
|
|
|
_ = wxSizer:add(SideSz, ScrollWin, zxw:flags(wide)),
|
|
|
|
|
ok = wxSizer:layout(SideSz),
|
|
|
|
|
NewPage = Page#p{funs = NewFuns, builds = NewBuilds},
|
|
|
|
|
NewPages = lists:keystore(Path, #p.path, Pages, NewPage),
|
|
|
|
|
State#s{book = {Notebook, NewPages}, buttons = NewButtons};
|
|
|
|
|
FunDefs = {#{functions := Funs}, _} = find_main(ACI),
|
|
|
|
|
Init = lom:find(name, <<"init">>, Funs),
|
|
|
|
|
ok = tell(info, "Compilation Succeeded!~n~tp~n~n~tp", [Build, FunDefs]),
|
|
|
|
|
deploy3(State, Init);
|
|
|
|
|
Other ->
|
|
|
|
|
ok = wxTextCtrl:setValue(ConsTx, io_lib:format("~tp", [Other])),
|
|
|
|
|
ok = tell(info, "Compilation Failed!~n~tp", [Other]),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
build_name(Path) ->
|
|
|
|
|
File = filename:basename(Path, ".aes"),
|
|
|
|
|
TS = integer_to_list(os:system_time(millisecond)),
|
|
|
|
|
unicode:characters_to_list([File, "-", TS]).
|
|
|
|
|
deploy3(State = #s{frame = Frame, j = J}, #{arguments := As}) ->
|
|
|
|
|
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Deploy Contract")),
|
|
|
|
|
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
ScrollWin = wxScrolledWindow:new(Dialog),
|
|
|
|
|
FunName = unicode:characters_to_list(["init/", integer_to_list(length(As))]),
|
|
|
|
|
FunSizer = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, FunName}]),
|
|
|
|
|
ok = wxScrolledWindow:setSizerAndFit(ScrollWin, FunSizer),
|
|
|
|
|
ok = wxScrolledWindow:setScrollRate(ScrollWin, 5, 5),
|
|
|
|
|
ButtSz = wxDialog:createButtonSizer(Dialog, ?wxOK bor ?wxCANCEL),
|
|
|
|
|
GridSz = wxFlexGridSizer:new(2, 4, 4),
|
|
|
|
|
ok = wxFlexGridSizer:setFlexibleDirection(GridSz, ?wxHORIZONTAL),
|
|
|
|
|
ok = wxFlexGridSizer:addGrowableCol(GridSz, 1),
|
|
|
|
|
MakeArgField =
|
|
|
|
|
fun(#{name := AN, type := T}) ->
|
|
|
|
|
Type =
|
|
|
|
|
case T of
|
|
|
|
|
<<"address">> -> address;
|
|
|
|
|
<<"int">> -> integer;
|
|
|
|
|
<<"bool">> -> boolean;
|
|
|
|
|
L when is_list(L) -> list; % FIXME
|
|
|
|
|
% I when is_binary(I) -> iface % FIXME
|
|
|
|
|
I when is_binary(I) -> address % FIXME
|
|
|
|
|
end,
|
|
|
|
|
ANT = wxStaticText:new(ScrollWin, ?wxID_ANY, AN),
|
|
|
|
|
TCT = wxTextCtrl:new(ScrollWin, ?wxID_ANY),
|
|
|
|
|
_ = wxFlexGridSizer:add(GridSz, ANT, zxw:flags(base)),
|
|
|
|
|
_ = wxFlexGridSizer:add(GridSz, TCT, zxw:flags(wide)),
|
|
|
|
|
{ANT, TCT, Type}
|
|
|
|
|
end,
|
|
|
|
|
ArgFields = lists:map(MakeArgField, As),
|
|
|
|
|
_ = wxStaticBoxSizer:add(FunSizer, GridSz, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(Sizer, ScrollWin, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(Sizer, ButtSz),
|
|
|
|
|
ok = wxDialog:setSizer(Dialog, Sizer),
|
|
|
|
|
ok = wxBoxSizer:layout(Sizer),
|
|
|
|
|
ok = wxDialog:setSize(Dialog, {500, 300}),
|
|
|
|
|
ok = wxDialog:center(Dialog),
|
|
|
|
|
ok =
|
|
|
|
|
case wxDialog:showModal(Dialog) of
|
|
|
|
|
?wxID_OK ->
|
|
|
|
|
tell(info, "DEPLOYING!");
|
|
|
|
|
?wxID_CANCEL ->
|
|
|
|
|
ok
|
|
|
|
|
end,
|
|
|
|
|
ok = wxDialog:destroy(Dialog),
|
|
|
|
|
State.
|
|
|
|
|
|
|
|
|
|
find_main(ACI) ->
|
|
|
|
|
find_main(ACI, none, []).
|
|
|
|
@ -495,6 +545,51 @@ map_iface_buttons(#f{call = C = #w{id = CID}, dryrun = none}, A) ->
|
|
|
|
|
maps:put(CID, C, A).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
open(State = #s{frame = Frame, j = J}) ->
|
|
|
|
|
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Open Contract Source")),
|
|
|
|
|
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
Choices = wxRadioBox:new(Dialog,
|
|
|
|
|
?wxID_ANY,
|
|
|
|
|
J("Select Origin"),
|
|
|
|
|
?wxDefaultPosition,
|
|
|
|
|
?wxDefaultSize,
|
|
|
|
|
[J("From File"), J("From Hash")],
|
|
|
|
|
[{majorDim, 1}]),
|
|
|
|
|
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
Affirm = wxButton:new(Dialog, ?wxID_OK),
|
|
|
|
|
Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
|
|
|
|
|
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
|
|
|
|
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(Sizer, Choices, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)),
|
|
|
|
|
ok = wxDialog:setSizer(Dialog, Sizer),
|
|
|
|
|
ok = wxDialog:setSize(Dialog, {250, 170}),
|
|
|
|
|
ok = wxBoxSizer:layout(Sizer),
|
|
|
|
|
Choice =
|
|
|
|
|
case wxDialog:showModal(Dialog) of
|
|
|
|
|
?wxID_OK ->
|
|
|
|
|
case wxRadioBox:getSelection(Choices) of
|
|
|
|
|
0 -> file;
|
|
|
|
|
1 -> hash;
|
|
|
|
|
?wxNOT_FOUND -> none
|
|
|
|
|
end;
|
|
|
|
|
?wxID_CANCEL -> cancel
|
|
|
|
|
end,
|
|
|
|
|
ok = wxDialog:destroy(Dialog),
|
|
|
|
|
case Choice of
|
|
|
|
|
file ->
|
|
|
|
|
open_file(State);
|
|
|
|
|
hash ->
|
|
|
|
|
open_hash(State);
|
|
|
|
|
none ->
|
|
|
|
|
ok = tell(info, "No selection."),
|
|
|
|
|
State;
|
|
|
|
|
cancel ->
|
|
|
|
|
ok = tell(info, "Cancelled."),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
open_file(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
|
|
|
|
DefaultDir =
|
|
|
|
|
case maps:find(dir, Prefs) of
|
|
|
|
@ -528,7 +623,7 @@ open_file(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
|
|
|
|
Path = filename:join(Dir, File),
|
|
|
|
|
NewPrefs = maps:put(dir, Dir, Prefs),
|
|
|
|
|
NextState = State#s{prefs = NewPrefs},
|
|
|
|
|
add_page(NextState, Path)
|
|
|
|
|
add_code_page(NextState, {file, Path})
|
|
|
|
|
end;
|
|
|
|
|
?wxID_CANCEL ->
|
|
|
|
|
State
|
|
|
|
@ -537,17 +632,339 @@ open_file(State = #s{frame = Frame, j = J, prefs = Prefs}) ->
|
|
|
|
|
NewState.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
close_file(State = #s{book = {Notebook, Pages}}) ->
|
|
|
|
|
case wxNotebook:getSelection(Notebook) of
|
|
|
|
|
open_hash(State = #s{frame = Frame, j = J}) ->
|
|
|
|
|
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Retrieve Contract Source")),
|
|
|
|
|
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
AddressSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Address Hash")}]),
|
|
|
|
|
AddressTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
|
|
|
|
_ = wxSizer:add(AddressSz, AddressTx, zxw:flags(wide)),
|
|
|
|
|
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
Affirm = wxButton:new(Dialog, ?wxID_OK),
|
|
|
|
|
Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
|
|
|
|
|
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
|
|
|
|
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(Sizer, AddressSz, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)),
|
|
|
|
|
ok = wxDialog:setSizer(Dialog, Sizer),
|
|
|
|
|
ok = wxBoxSizer:layout(Sizer),
|
|
|
|
|
ok = wxDialog:setSize(Dialog, {500, 200}),
|
|
|
|
|
ok = wxDialog:center(Dialog),
|
|
|
|
|
ok = wxStyledTextCtrl:setFocus(AddressTx),
|
|
|
|
|
Choice =
|
|
|
|
|
case wxDialog:showModal(Dialog) of
|
|
|
|
|
?wxID_OK ->
|
|
|
|
|
case wxTextCtrl:getValue(AddressTx) of
|
|
|
|
|
"" -> cancel;
|
|
|
|
|
A -> {ok, A}
|
|
|
|
|
end;
|
|
|
|
|
?wxID_CANCEL ->
|
|
|
|
|
cancel
|
|
|
|
|
end,
|
|
|
|
|
ok = wxDialog:destroy(Dialog),
|
|
|
|
|
case Choice of
|
|
|
|
|
{ok, Address} -> open_hash2(State, Address);
|
|
|
|
|
cancel -> State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
open_hash2(State, Address) ->
|
|
|
|
|
case hz:contract_source(Address) of
|
|
|
|
|
{ok, Source} ->
|
|
|
|
|
open_hash3(State, Address, Source);
|
|
|
|
|
Error ->
|
|
|
|
|
ok = handle_troubling(Error, State),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
open_hash3(State, Address, Source) ->
|
|
|
|
|
% TODO: Compile on load and verify the deployed hash for validity.
|
|
|
|
|
case aeso_compiler:from_string(Source, [{aci, json}]) of
|
|
|
|
|
{ok, Build = #{aci := ACI}} ->
|
|
|
|
|
{Defs = #{functions := Funs}, ConIfaces} = find_main(ACI),
|
|
|
|
|
Callable = lom:delete(name, <<"init">>, Funs),
|
|
|
|
|
FunDefs = {maps:put(functions, Callable, Defs), ConIfaces},
|
|
|
|
|
ok = tell(info, "Compilation Succeeded!~n~tp~n~n~tp", [Build, FunDefs]),
|
|
|
|
|
add_code_page(State, {hash, Address}, Source);
|
|
|
|
|
Other ->
|
|
|
|
|
ok = tell(info, "Compilation Failed!~n~tp", [Other]),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% TODO: Break this down -- tons of things in here recur.
|
|
|
|
|
save(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}}) ->
|
|
|
|
|
case wxNotebook:getSelection(Codebook) of
|
|
|
|
|
?wxNOT_FOUND ->
|
|
|
|
|
State;
|
|
|
|
|
Index ->
|
|
|
|
|
case lists:nth(Index + 1, Pages) of
|
|
|
|
|
#p{path = {file, Path}, code = Widget} ->
|
|
|
|
|
Source = wxStyledTextCtrl:getText(Widget),
|
|
|
|
|
case filelib:ensure_dir(Path) of
|
|
|
|
|
ok ->
|
|
|
|
|
case file:write_file(Path, Source) of
|
|
|
|
|
ok ->
|
|
|
|
|
State;
|
|
|
|
|
Error ->
|
|
|
|
|
ok = handle_troubling(State, Error),
|
|
|
|
|
State
|
|
|
|
|
end;
|
|
|
|
|
Error ->
|
|
|
|
|
ok = handle_troubling(State, Error),
|
|
|
|
|
State
|
|
|
|
|
end;
|
|
|
|
|
Page = #p{path = {hash, Hash}, code = Widget} ->
|
|
|
|
|
DefaultDir =
|
|
|
|
|
case maps:find(dir, Prefs) of
|
|
|
|
|
{ok, PrefDir} ->
|
|
|
|
|
PrefDir;
|
|
|
|
|
error ->
|
|
|
|
|
case os:getenv("ZOMP_DIR") of
|
|
|
|
|
"" -> file:get_pwd();
|
|
|
|
|
D -> filename:basename(D)
|
|
|
|
|
end
|
|
|
|
|
end,
|
|
|
|
|
Options =
|
|
|
|
|
[{message, J("Save Location")},
|
|
|
|
|
{defaultDir, DefaultDir},
|
|
|
|
|
{defaultFile, unicode:characters_to_list([Hash, ".aes"])},
|
|
|
|
|
{wildCard, "*.aes"},
|
|
|
|
|
{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}],
|
|
|
|
|
Dialog = wxFileDialog:new(Frame, Options),
|
|
|
|
|
NewState =
|
|
|
|
|
case wxFileDialog:showModal(Dialog) of
|
|
|
|
|
?wxID_OK ->
|
|
|
|
|
Dir = wxFileDialog:getDirectory(Dialog),
|
|
|
|
|
case wxFileDialog:getFilename(Dialog) of
|
|
|
|
|
"" ->
|
|
|
|
|
State;
|
|
|
|
|
Name ->
|
|
|
|
|
File =
|
|
|
|
|
case filename:extension(Name) of
|
|
|
|
|
".aes" -> Name;
|
|
|
|
|
_ -> Name ++ ".aes"
|
|
|
|
|
end,
|
|
|
|
|
Path = filename:join(Dir, File),
|
|
|
|
|
Source = wxTextCtrl:getValue(Widget),
|
|
|
|
|
case filelib:ensure_dir(Path) of
|
|
|
|
|
ok ->
|
|
|
|
|
case file:write_file(Path, Source) of
|
|
|
|
|
ok ->
|
|
|
|
|
true = wxNotebook:setPageText(Codebook, Index, File),
|
|
|
|
|
NewPrefs = maps:put(dir, Dir, Prefs),
|
|
|
|
|
NewPage = Page#p{path = {file, Path}},
|
|
|
|
|
NewPages = store_nth(Index + 1, NewPage, Pages),
|
|
|
|
|
NewCode = {Codebook, NewPages},
|
|
|
|
|
State#s{prefs = NewPrefs, code = NewCode};
|
|
|
|
|
Error ->
|
|
|
|
|
ok = handle_troubling(State, Error),
|
|
|
|
|
State
|
|
|
|
|
end;
|
|
|
|
|
Error ->
|
|
|
|
|
ok = handle_troubling(State, Error),
|
|
|
|
|
State
|
|
|
|
|
end
|
|
|
|
|
end;
|
|
|
|
|
?wxID_CANCEL ->
|
|
|
|
|
State
|
|
|
|
|
end,
|
|
|
|
|
ok = wxFileDialog:destroy(Dialog),
|
|
|
|
|
NewState
|
|
|
|
|
end
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
% TODO: Break this down -- tons of things in here recur.
|
|
|
|
|
rename(State = #s{frame = Frame, j = J, prefs = Prefs, code = {Codebook, Pages}}) ->
|
|
|
|
|
case wxNotebook:getSelection(Codebook) of
|
|
|
|
|
?wxNOT_FOUND ->
|
|
|
|
|
State;
|
|
|
|
|
Index ->
|
|
|
|
|
case lists:nth(Index + 1, Pages) of
|
|
|
|
|
Page = #p{path = {file, Path}, code = Widget} ->
|
|
|
|
|
DefaultDir = filename:dirname(Path),
|
|
|
|
|
Options =
|
|
|
|
|
[{message, J("Save Location")},
|
|
|
|
|
{defaultDir, DefaultDir},
|
|
|
|
|
{defaultFile, filename:basename(Path)},
|
|
|
|
|
{wildCard, "*.aes"},
|
|
|
|
|
{style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}],
|
|
|
|
|
Dialog = wxFileDialog:new(Frame, Options),
|
|
|
|
|
NewState =
|
|
|
|
|
case wxFileDialog:showModal(Dialog) of
|
|
|
|
|
?wxID_OK ->
|
|
|
|
|
Dir = wxFileDialog:getDirectory(Dialog),
|
|
|
|
|
case wxFileDialog:getFilename(Dialog) of
|
|
|
|
|
"" ->
|
|
|
|
|
State;
|
|
|
|
|
Name ->
|
|
|
|
|
File =
|
|
|
|
|
case filename:extension(Name) of
|
|
|
|
|
".aes" -> Name;
|
|
|
|
|
_ -> Name ++ ".aes"
|
|
|
|
|
end,
|
|
|
|
|
NewPath = filename:join(Dir, File),
|
|
|
|
|
Source = wxTextCtrl:getValue(Widget),
|
|
|
|
|
case filelib:ensure_dir(NewPath) of
|
|
|
|
|
ok ->
|
|
|
|
|
case file:write_file(NewPath, Source) of
|
|
|
|
|
ok ->
|
|
|
|
|
true = wxNotebook:setPageText(Codebook, Index, File),
|
|
|
|
|
NewPrefs = maps:put(dir, Dir, Prefs),
|
|
|
|
|
NewPage = Page#p{path = {file, NewPath}},
|
|
|
|
|
NewPages = store_nth(Index + 1, NewPage, Pages),
|
|
|
|
|
NewCode = {Codebook, NewPages},
|
|
|
|
|
State#s{prefs = NewPrefs, code = NewCode};
|
|
|
|
|
Error ->
|
|
|
|
|
ok = handle_troubling(State, Error),
|
|
|
|
|
State
|
|
|
|
|
end;
|
|
|
|
|
Error ->
|
|
|
|
|
ok = handle_troubling(State, Error),
|
|
|
|
|
State
|
|
|
|
|
end
|
|
|
|
|
end;
|
|
|
|
|
?wxID_CANCEL ->
|
|
|
|
|
State
|
|
|
|
|
end,
|
|
|
|
|
ok = wxFileDialog:destroy(Dialog),
|
|
|
|
|
NewState;
|
|
|
|
|
#p{path = {hash, _}} ->
|
|
|
|
|
save(State)
|
|
|
|
|
end
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
close_source(State = #s{code = {Codebook, Pages}}) ->
|
|
|
|
|
case wxNotebook:getSelection(Codebook) of
|
|
|
|
|
?wxNOT_FOUND ->
|
|
|
|
|
State;
|
|
|
|
|
Index ->
|
|
|
|
|
NewPages = drop_nth(Index + 1, Pages),
|
|
|
|
|
true = wxNotebook:deletePage(Notebook, Index),
|
|
|
|
|
State#s{book = {Notebook, NewPages}}
|
|
|
|
|
true = wxNotebook:deletePage(Codebook, Index),
|
|
|
|
|
State#s{code = {Codebook, NewPages}}
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
load(State = #s{frame = Frame, j = J}) ->
|
|
|
|
|
% TODO: Extract the exact compiler version, load it, and use only that or fail if
|
|
|
|
|
% the specific version is unavailable.
|
|
|
|
|
% TODO: Compile on load and verify the deployed hash for validity.
|
|
|
|
|
Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Retrieve Contract Source")),
|
|
|
|
|
Sizer = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
AddressSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Address Hash")}]),
|
|
|
|
|
AddressTx = wxTextCtrl:new(Dialog, ?wxID_ANY),
|
|
|
|
|
_ = wxSizer:add(AddressSz, AddressTx, zxw:flags(wide)),
|
|
|
|
|
ButtSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
Affirm = wxButton:new(Dialog, ?wxID_OK),
|
|
|
|
|
Cancel = wxButton:new(Dialog, ?wxID_CANCEL),
|
|
|
|
|
_ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)),
|
|
|
|
|
_ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(Sizer, AddressSz, zxw:flags(wide)),
|
|
|
|
|
_ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)),
|
|
|
|
|
ok = wxDialog:setSizer(Dialog, Sizer),
|
|
|
|
|
ok = wxBoxSizer:layout(Sizer),
|
|
|
|
|
ok = wxDialog:setSize(Dialog, {500, 200}),
|
|
|
|
|
ok = wxDialog:center(Dialog),
|
|
|
|
|
ok = wxStyledTextCtrl:setFocus(AddressTx),
|
|
|
|
|
Choice =
|
|
|
|
|
case wxDialog:showModal(Dialog) of
|
|
|
|
|
?wxID_OK ->
|
|
|
|
|
case wxTextCtrl:getValue(AddressTx) of
|
|
|
|
|
"" -> cancel;
|
|
|
|
|
A -> {ok, A}
|
|
|
|
|
end;
|
|
|
|
|
?wxID_CANCEL ->
|
|
|
|
|
cancel
|
|
|
|
|
end,
|
|
|
|
|
ok = wxDialog:destroy(Dialog),
|
|
|
|
|
case Choice of
|
|
|
|
|
{ok, Address} -> load2(State, Address);
|
|
|
|
|
cancel -> State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
load2(State, Address) ->
|
|
|
|
|
case hz:contract_source(Address) of
|
|
|
|
|
{ok, Source} ->
|
|
|
|
|
load3(State, Address, Source);
|
|
|
|
|
Error ->
|
|
|
|
|
ok = handle_troubling(Error, State),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
load3(State, Address, Source) ->
|
|
|
|
|
% TODO: Compile on load and verify the deployed hash for validity.
|
|
|
|
|
case aeso_compiler:from_string(Source, [{aci, json}]) of
|
|
|
|
|
{ok, Build = #{aci := ACI}} ->
|
|
|
|
|
{Defs = #{functions := Funs}, ConIfaces} = find_main(ACI),
|
|
|
|
|
Callable = lom:delete(name, <<"init">>, Funs),
|
|
|
|
|
FunDefs = {maps:put(functions, Callable, Defs), ConIfaces},
|
|
|
|
|
ok = tell(info, "Compilation Succeeded!~n~tp~n~n~tp", [Build, FunDefs]),
|
|
|
|
|
add_instance_page(State, Address, Source);
|
|
|
|
|
Other ->
|
|
|
|
|
ok = tell(info, "Compilation Failed!~n~tp", [Other]),
|
|
|
|
|
State
|
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
add_instance_page(State = #s{cons = {Consbook, Pages}, j = J}, Address, Source) ->
|
|
|
|
|
Window = wxWindow:new(Consbook, ?wxID_ANY),
|
|
|
|
|
PageSz = wxBoxSizer:new(?wxVERTICAL),
|
|
|
|
|
|
|
|
|
|
ProgSz = wxBoxSizer:new(?wxHORIZONTAL),
|
|
|
|
|
|
|
|
|
|
CodeSz = wxStaticBoxSizer:new(?wxVERTICAL, Window, [{label, J("Contract Source")}]),
|
|
|
|
|
CodeTxStyle = {style, ?wxTE_MULTILINE
|
|
|
|
|
bor ?wxTE_PROCESS_TAB
|
|
|
|
|
bor ?wxTE_DONTWRAP
|
|
|
|
|
bor ?wxTE_READONLY},
|
|
|
|
|
CodeTx = wxTextCtrl:new(Window, ?wxID_ANY, [CodeTxStyle]),
|
|
|
|
|
Mono = wxFont:new(10, ?wxMODERN, ?wxNORMAL, ?wxNORMAL, [{face, "Monospace"}]),
|
|
|
|
|
TextAt = wxTextAttr:new(),
|
|
|
|
|
ok = wxTextAttr:setFont(TextAt, Mono),
|
|
|
|
|
true = wxTextCtrl:setDefaultStyle(CodeTx, TextAt),
|
|
|
|
|
ok = wxTextCtrl:setValue(CodeTx, Source),
|
|
|
|
|
_ = wxSizer:add(CodeSz, CodeTx, zxw:flags(wide)),
|
|
|
|
|
|
|
|
|
|
ScrollWin = wxScrolledWindow:new(Window),
|
|
|
|
|
FunSz = wxStaticBoxSizer:new(?wxVERTICAL, ScrollWin, [{label, J("Function Interfaces")}]),
|
|
|
|
|
ok = wxWindow:setSizer(ScrollWin, FunSz),
|
|
|
|
|
|
|
|
|
|
ConsSz = wxStaticBoxSizer:new(?wxVERTICAL, Window, [{label, J("Console")}]),
|
|
|
|
|
ConsTxStyle = {style, ?wxTE_MULTILINE bor ?wxTE_READONLY},
|
|
|
|
|
ConsTx = wxTextCtrl:new(Window, ?wxID_ANY, [ConsTxStyle]),
|
|
|
|
|
_ = wxSizer:add(ConsSz, ConsTx, zxw:flags(wide)),
|
|
|
|
|
|
|
|
|
|
_ = wxSizer:add(ProgSz, CodeSz, [{proportion, 3}, {flag, ?wxEXPAND}]),
|
|
|
|
|
_ = wxSizer:add(ProgSz, ScrollWin, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
|
|
|
|
|
|
|
|
|
_ = wxSizer:add(PageSz, ProgSz, [{proportion, 3}, {flag, ?wxEXPAND}]),
|
|
|
|
|
_ = wxSizer:add(PageSz, ConsSz, [{proportion, 1}, {flag, ?wxEXPAND}]),
|
|
|
|
|
|
|
|
|
|
ok = wxWindow:setSizer(Window, PageSz),
|
|
|
|
|
ok = wxSizer:layout(PageSz),
|
|
|
|
|
true = wxNotebook:addPage(Consbook, Window, Address, [{bSelect, true}]),
|
|
|
|
|
Page = #c{id = Address, win = Window,
|
|
|
|
|
code = CodeTx, cons = ConsTx,
|
|
|
|
|
funs = {ScrollWin, []}},
|
|
|
|
|
NewPages = Pages ++ [Page],
|
|
|
|
|
State#s{cons = {Consbook, NewPages}}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
edit(State) ->
|
|
|
|
|
ok = tell(info, "EDIT clicked"),
|
|
|
|
|
State.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
close_instance(State) ->
|
|
|
|
|
ok = tell(info, "CLOSE_INSTANCE clicked"),
|
|
|
|
|
State.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
%% (Somewhat silly) Data operations
|
|
|
|
|
|
|
|
|
|
store_nth(1, E, [_ | T]) -> [E | T];
|
|
|
|
|
store_nth(N, E, [H | T]) -> [H | store_nth(N - 1, T, E)].
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
drop_nth(1, [_ | T]) -> T;
|
|
|
|
|
drop_nth(N, [H | T]) -> [H | drop_nth(N - 1, T)].
|
|
|
|
|
|
|
|
|
|