From aa76d7609334ac6c5680bd0a9cd2a5a234b7d3e8 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Wed, 6 Aug 2025 14:57:27 +0900 Subject: [PATCH] WIP --- src/:w | 527 +++++++++++++++++++++++++++++++++++++++++++ src/gd_v_wallman.erl | 99 +++++++- 2 files changed, 615 insertions(+), 11 deletions(-) create mode 100644 src/:w diff --git a/src/:w b/src/:w new file mode 100644 index 0000000..5e1a02e --- /dev/null +++ b/src/:w @@ -0,0 +1,527 @@ +-module(gd_v_wallman). +-vsn("0.6.6"). +-author("Craig Everett "). +-copyright("QPQ AG "). +-license("GPL-3.0-or-later"). + +-behavior(wx_object). +%-behavior(gd_v). +-include_lib("wx/include/wx.hrl"). +-export([to_front/1]). +-export([show/2]). +-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("gd.hrl"). + + +-record(w, + {name = none :: atom(), + id = 0 :: integer(), + wx = none :: none | wx:wx_object()}). + +-record(s, + {wx = none :: none | wx:wx_object(), + frame = none :: none | wx:wx_object(), + lang = en :: en | jp, + j = none :: none | fun(), + prefs = #{} :: map(), + wallets = [] :: [#wr{}], + picker = none :: none | wx:wx_object(), + buttons = [] :: [#w{}], + wiz = none :: none | {wx:wx_object(), Buttons :: [#w{}]}). + + +%%% Interface + +-spec to_front(Win) -> ok + when Win :: wx:wx_object(). + +to_front(Win) -> + wx_object:cast(Win, to_front). + + +-spec show(Win, Manifest) -> ok + when Win :: wx:xw_object(), + Manifest :: [#wr{}]. + +show(Win, Manifest) -> + wx_object:cast(Win, {show, Manifest}). + + +%%% Startup + +start_link(Args) -> + wx_object:start_link({local, ?MODULE}, ?MODULE, Args, []). + + +init({Prefs, Manifest}) -> + Lang = maps:get(lang, Prefs, en_us), + Trans = gd_jt:read_translations(?MODULE), + J = gd_jt:j(Lang, Trans), + Wx = wx:new(), + Frame = wxFrame:new(Wx, ?wxID_ANY, J("Wallets")), + + MainSz = wxBoxSizer:new(?wxVERTICAL), + + Picker = wxListBox:new(Frame, ?wxID_ANY, [{style, ?wxLC_SINGLE_SEL}]), + Names = [Name || #wr{name = Name} <- Manifest], + ok = wxListBox:set(Picker, Names), + + ButtSz = wxBoxSizer:new(?wxHORIZONTAL), + ButtonTemplates = + [{open, J("Open")}, + {new, J("New")}, + {move, J("Move")}, + {import, J("Import")}, + {drop, J("Drop")}], + MakeButton = + fun({Name, Label}) -> + B = wxButton:new(Frame, ?wxID_ANY, [{label, Label}]), + _ = wxSizer:add(ButtSz, B, zxw:flags(wide)), + #w{name = Name, id = wxButton:getId(B), wx = B} + end, + Buttons = lists:map(MakeButton, ButtonTemplates), + + _ = wxSizer:add(MainSz, Picker, zxw:flags(wide)), + _ = wxSizer:add(MainSz, ButtSz, zxw:flags(base)), + + ok = wxFrame:setSizer(Frame, MainSz), + ok = wxSizer:layout(MainSz), + + NewPrefs = + case maps:is_key(geometry, Prefs) of + true -> + Prefs; + false -> + Display = wxDisplay:new(), + {_, _, W, H} = wxDisplay:getGeometry(Display), + ok = wxDisplay:destroy(Display), + WW = 500, + WH = 350, + X = (W div 2) - (WW div 2), + Y = (H div 2) - (WH div 2), + Prefs#{geometry => {X, Y, WW, WH}} + end, + ok = gd_v:safe_size(Frame, NewPrefs), + + ok = wxFrame:connect(Frame, command_button_clicked), + ok = wxFrame:connect(Frame, close_window), + ok = wxListBox:connect(Picker, command_listbox_doubleclicked), + Count = length(Manifest) + ok = + if + Count =:= 0 -> + self() ! wiz, + ok; + Count =:= 1 -> + self() ! open_only, + ok; + Count > 1 -> + true = wxFrame:show(Frame), + ok + end, + State = #s{wx = Wx, frame = Frame, lang = Lang, j = J, prefs = Prefs, + wallets = Manifest, + picker = Picker, + buttons = Buttons}, + {Frame, State}. + + + +%%% wx_object + +handle_call(Unexpected, From, State) -> + ok = log(warning, "Unexpected call from ~tp: ~tp~n", [From, Unexpected]), + {noreply, State}. + + +handle_cast(to_front, State = #s{frame = Frame}) -> + ok = wxFrame:raise(Frame), + {noreply, State}; +handle_cast({show, Manifest}, State) -> + NewState = do_show(Manifest, State), + {noreply, NewState}; +handle_cast(Unexpected, State) -> + ok = log(warning, "Unexpected cast: ~tp~n", [Unexpected]), + {noreply, State}. + + +handle_info(wiz, State) -> + NewState = do_wiz(State), + {noreply, NewState}; +handle_info(open_only, State) -> + NewState = do_open_only(State), + {noreply, NewState}; +handle_info(Unexpected, State) -> + ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]), + {noreply, State}. + + +handle_event(#wx{event = #wxCommand{type = command_button_clicked}, + id = ID}, + State = #s{buttons = Buttons}) -> + NewState = + case lists:keyfind(ID, #w.id, Buttons) of + #w{name = open} -> do_open(State); + #w{name = new} -> do_new(State); + #w{name = import} -> do_import(State); + #w{name = drop} -> do_drop(State); + #w{name = Name} -> handle_button(Name, State); + false -> State + end, + {noreply, NewState}; +handle_event(#wx{event = #wxCommand{type = command_listbox_doubleclicked, + commandInt = Selected}}, + State) -> + NewState = do_open2(Selected + 1, State), + {noreply, NewState}; +handle_event(#wx{event = #wxClose{}}, State) -> + ok = do_close(State), + {noreply, State}; +handle_event(Event, State) -> + ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]), + {noreply, State}. + + +code_change(_, State, _) -> + {ok, State}. + + +terminate(wx_deleted, _) -> + wx:destroy(); +terminate(Reason, State) -> + ok = log(info, "Reason: ~tp, State: ~tp", [Reason, State]), + wx:destroy(). + + + +%%% doers + +do_show(Manifest, State = #s{picker = Picker}) -> + Names = [Name || #wr{name = Name} <- Manifest], + ok = wxListBox:set(Picker, Names), + State#s{wallets = Manifest}. + + +do_close(#s{frame = Frame, prefs = Prefs}) -> + Geometry = + case wxTopLevelWindow:isMaximized(Frame) of + true -> + max; + false -> + {X, Y} = wxWindow:getPosition(Frame), + {W, H} = wxWindow:getSize(Frame), + {X, Y, W, H} + end, + NewPrefs = maps:put(geometry, Geometry, Prefs), + ok = gd_con:save(?MODULE, NewPrefs), + ok = wxWindow:destroy(Frame). + + +handle_button(Name, State) -> + ok = tell("Button Click: ~p", [Name]), + State. + + +do_open(State = #s{wallets = []}) -> + State; +do_open(State = #s{picker = Picker}) -> + case wxListBox:getSelection(Picker) of + -1 -> State; + Selected -> do_open2(Selected + 1, State) + end. + +do_open2(Selected, State = #s{wallets = Wallets}) -> + case lists:nth(Selected, Wallets) of + #wr{pass = true, path = Path} -> + do_open3(Path, State); + #wr{pass = false, path = Path} -> + ok = gd_con:open_wallet(Path, none), + ok = do_close(State), + State + end. + +do_open3(Path, State = #s{frame = Frame, j = J}) -> + Label = J("Password"), + Dialog = wxDialog:new(Frame, ?wxID_ANY, Label), + Sizer = wxBoxSizer:new(?wxVERTICAL), + PassSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, Label}]), + PassTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]), + _ = wxStaticBoxSizer:add(PassSz, PassTx, zxw:flags(wide)), + ButtSz = wxBoxSizer:new(?wxHORIZONTAL), + Affirm = wxButton:new(Dialog, ?wxID_OK), + _ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags(wide)), + _ = wxBoxSizer:add(Sizer, PassSz, zxw:flags(base)), + _ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(wide)), + ok = wxDialog:setSizer(Dialog, Sizer), + ok = wxBoxSizer:layout(Sizer), + ok = wxDialog:setSize(Dialog, {500, 130}), + ok = wxFrame:center(Dialog), + ok = wxStyledTextCtrl:setFocus(PassTx), + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + case wxTextCtrl:getValue(PassTx) of + "" -> + ok = wxDialog:destroy(Dialog), + State; + Phrase -> + ok = wxDialog:destroy(Dialog), + ok = gd_con:open_wallet(Path, Phrase), + ok = do_close(State), + State + end; + ?wxID_CANCEL -> + ok = wxDialog:destroy(Dialog), + State + end. + + +do_wiz(State = #s{wx = WX, lang = Lang, wiz = none}) -> + Trans = gd_jt:read_translations(?MODULE), + J = gd_jt:j(Lang, Trans), + + Frame = wxFrame:new(WX, ?wxID_ANY, J("Initializing Wallet")), + MainSz = wxBoxSizer:new(?wxVERTICAL), + + ButtonTemplates = + [{noob, J("Create a default wallet with a new account for me.")}, + {l33t, J("I know what I'm doing.\nJust show me the wallet manager.")}], + + 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), + + Add = fun(#w{wx = Button}) -> wxBoxSizer:add(MainSz, Button, zxw:flags(wide)) end, + ok = lists:foreach(Add, Buttons), + + ok = wxFrame:setSizer(Frame, MainSz), + ok = wxSizer:layout(MainSz), + + ok = wxFrame:connect(Frame, command_button_clicked), + ok = wxFrame:connect(Frame, close_window), + ok = wxFrame:setSize(Frame, {300, 300}), + ok = wxFrame:center(Frame), + true = wxFrame:show(Frame), + State#s{wiz = {Frame, Buttons}}. + + +do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) -> + DefaultDir = maps:get(dir, Prefs, zx_lib:path(var, "otpr", "gajudesk")), + Options = + [{message, J("Save Location")}, + {defaultDir, DefaultDir}, + {defaultFile, "default.gaju"}, + {wildCard, "*.gaju"}, + {sz, {300, 270}}, + {style, ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT}], + Dialog = wxFileDialog:new(Frame, Options), + case wxFileDialog:showModal(Dialog) of + ?wxID_OK -> + Dir = wxFileDialog:getDirectory(Dialog), + File = wxFileDialog:getFilename(Dialog), + Path = filename:join(Dir, File), + ok = wxFileDialog:destroy(Dialog), + case do_new2(Path, J, Frame) of + ok -> + NewPrefs = maps:put(dir, Dir, Prefs), + do_close(State#s{prefs = NewPrefs}); + abort -> + State + end; + ?wxID_CANCEL -> + ok = wxFileDialog:destroy(Dialog), + State + end. + +do_new2(Path, J, Frame) -> + Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Wallet"), [{size, {400, 250}}]), + Sizer = wxBoxSizer:new(?wxVERTICAL), + + Network = wxRadioBox:new(Dialog, ?wxID_ANY, J("Network"), {0, 0}, {50, 50}, [J("Mainnet"), J("Testnet")]), + NameSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Name")}]), + NameTx = wxTextCtrl:new(Dialog, ?wxID_ANY), + _ = wxSizer:add(NameSz, NameTx, zxw:flags(wide)), + Label1 = J("Passphrase (optional)"), + PassSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, Label1}]), + PassTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]), + _ = wxSizer:add(PassSz, PassTx, zxw:flags(wide)), + Label2= J("Passphrase Confirmation"), + PassConSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, Label2}]), + PassConTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_PASSWORD}]), + _ = wxSizer:add(PassConSz, PassConTx, 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)), + + _ = wxBoxSizer:add(Sizer, Network, zxw:flags(base)), + _ = wxSizer:add(Sizer, NameSz, zxw:flags(base)), + _ = wxSizer:add(Sizer, PassSz, zxw:flags(base)), + _ = wxSizer:add(Sizer, PassConSz, zxw:flags(base)), + _ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)), + + ok = wxDialog:setSizer(Dialog, Sizer), + ok = wxDialog:setSize(Dialog, {300, 270}), + ok = wxBoxSizer:layout(Sizer), + ok = wxDialog:center(Dialog), + ok = wxStyledTextCtrl:setFocus(NameTx), + + Result = + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + Net = + case wxRadioBox:getSelection(Network) of + 0 -> mainnet; + 1 -> testnet; + _ -> mainnet + end, + Name = + case wxTextCtrl:getValue(NameTx) of + "" -> Path; + N -> N + end, + Pass = + case {wxTextCtrl:getValue(PassTx), wxTextCtrl:getValue(PassConTx)} of + {"", ""} -> none; + {P, P} -> P; + {_, _} -> bad + end, + do_new3(Net, Name, Path, Pass); + ?wxID_CANCEL -> + abort + end, + ok = wxDialog:destroy(Dialog), + Result. + +do_new3(_, _, _, bad) -> + abort; +do_new3(Net, Name, Path, Pass) -> + gd_con:new_wallet(Net, Name, Path, Pass). + + +do_import(State = #s{frame = Frame, j = J, prefs = Prefs}) -> + DefaultDir = maps:get(dir, Prefs, zx_lib:path(var, "otpr", "gajudesk")), + Options = + [{message, J("Select Wallet File")}, + {defaultDir, DefaultDir}, + {wildCard, "*.gaju"}, + {style, ?wxFD_OPEN}], + Dialog = wxFileDialog:new(Frame, Options), + case wxFileDialog:showModal(Dialog) of + ?wxID_OK -> + Dir = wxFileDialog:getDirectory(Dialog), + File = wxFileDialog:getFilename(Dialog), + ok = wxFileDialog:destroy(Dialog), + case do_import2(Dir, File, J, Frame) of + ok -> + NewPrefs = maps:put(dir, Dir, Prefs), + do_close(State#s{prefs = NewPrefs}); + abort -> + State + end; + ?wxID_CANCEL -> + ok = wxFileDialog:destroy(Dialog), + State + end. + +do_import2(_, "", _, _) -> + abort; +do_import2(Dir, File, J, Frame) -> + Path = filename:join(Dir, File), + Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Import Wallet")), + Sizer = wxBoxSizer:new(?wxVERTICAL), + + NameSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Name")}]), + NameTx = wxTextCtrl:new(Dialog, ?wxID_ANY), + _ = wxSizer:add(NameSz, NameTx, zxw:flags(wide)), + PassSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Passphrase")}]), + PassTx = wxTextCtrl:new(Dialog, ?wxID_ANY), + _ = wxSizer:add(PassSz, PassTx, 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, NameSz, zxw:flags(base)), + _ = wxSizer:add(Sizer, PassSz, zxw:flags(base)), + _ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)), + + ok = wxDialog:setSizer(Dialog, Sizer), + ok = wxBoxSizer:layout(Sizer), + ok = wxFrame:setSize(Dialog, {500, 200}), + ok = wxFrame:center(Dialog), + ok = wxStyledTextCtrl:setFocus(NameTx), + + Result = + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + Name = + case wxTextCtrl:getValue(NameTx) of + "" -> Path; + N -> N + end, + Pass = + case wxTextCtrl:getValue(PassTx) of + "" -> none; + P -> P + end, + gd_con:import_wallet(Name, Path, Pass); + ?wxID_CANCEL -> + abort + end, + ok = wxDialog:destroy(Dialog), + Result. + + +do_drop(State = #s{picker = Picker}) -> + case wxListBox:getSelection(Picker) of + -1 -> State; + Selected -> do_drop(Selected + 1, State) + end. + +do_drop(Selected, State = #s{j = J, frame = Frame, wallets = Wallets}) -> + #wr{name = Name, path = Path} = lists:nth(Selected, Wallets), + Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Drop Wallet")), + Sizer = wxBoxSizer:new(?wxVERTICAL), + + MessageM = J("REALLY delete wallet?"), + Message = ["\r\n", MessageM, "\r\n\r\n\"", Name, "\""], + MessageT = wxStaticText:new(Dialog, ?wxID_ANY, Message, + [{style, ?wxALIGN_CENTRE_HORIZONTAL}]), + DeleteM = J("Delete file data?"), + DeleteCheck = wxCheckBox:new(Dialog, ?wxID_ANY, DeleteM), + + 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, MessageT, zxw:flags(base)), + _ = wxSizer:add(Sizer, DeleteCheck, zxw:flags(base)), + _ = wxSizer:add(Sizer, ButtSz, zxw:flags(wide)), + + ok = wxDialog:setSizer(Dialog, Sizer), + ok = wxBoxSizer:layout(Sizer), + ok = wxFrame:setSize(Dialog, {500, 200}), + ok = wxFrame:center(Dialog), + + ok = + case wxDialog:showModal(Dialog) of + ?wxID_OK -> + Delete = wxCheckBox:getValue(DeleteCheck), + gd_con:drop_wallet(Path, Delete); + ?wxID_CANCEL -> + ok + end, + State. diff --git a/src/gd_v_wallman.erl b/src/gd_v_wallman.erl index 49f007e..68bd09e 100644 --- a/src/gd_v_wallman.erl +++ b/src/gd_v_wallman.erl @@ -29,7 +29,8 @@ prefs = #{} :: map(), wallets = [] :: [#wr{}], picker = none :: none | wx:wx_object(), - buttons = [] :: [#w{}]}). + buttons = [] :: [#w{}], + wiz = none :: none | {wx:wx_object(), Buttons :: [#w{}]}}). %%% Interface @@ -107,14 +108,18 @@ init({Prefs, Manifest}) -> ok = wxFrame:connect(Frame, command_button_clicked), ok = wxFrame:connect(Frame, close_window), - true = wxFrame:show(Frame), ok = wxListBox:connect(Picker, command_listbox_doubleclicked), + Count = length(Manifest), ok = - case length(Manifest) =:= 0 of - false -> + if + Count =:= 0 -> + self() ! wiz, ok; - true -> - self() ! new, + Count =:= 1 -> + self() ! open, + ok; + Count > 1 -> + true = wxFrame:show(Frame), ok end, State = #s{wx = Wx, frame = Frame, lang = Lang, j = J, prefs = Prefs, @@ -143,8 +148,11 @@ handle_cast(Unexpected, State) -> {noreply, State}. -handle_info(new, State) -> - NewState = do_new(State), +handle_info(wiz, State) -> + NewState = do_wiz(State), + {noreply, NewState}; +handle_info(open, State) -> + NewState = do_open(State), {noreply, NewState}; handle_info(Unexpected, State) -> ok = log(warning, "Unexpected info: ~tp~n", [Unexpected]), @@ -153,7 +161,7 @@ handle_info(Unexpected, State) -> handle_event(#wx{event = #wxCommand{type = command_button_clicked}, id = ID}, - State = #s{buttons = Buttons}) -> + State = #s{buttons = Buttons, wiz = none}) -> NewState = case lists:keyfind(ID, #w.id, Buttons) of #w{name = open} -> do_open(State); @@ -166,12 +174,25 @@ handle_event(#wx{event = #wxCommand{type = command_button_clicked}, {noreply, NewState}; handle_event(#wx{event = #wxCommand{type = command_listbox_doubleclicked, commandInt = Selected}}, - State) -> + State = #s{wiz = none}) -> NewState = do_open2(Selected + 1, State), {noreply, NewState}; -handle_event(#wx{event = #wxClose{}}, State) -> +handle_event(#wx{event = #wxClose{}}, State = #s{wiz = none}) -> ok = do_close(State), {noreply, State}; +handle_event(#wx{event = #wxCommand{type = command_button_clicked}, + id = ID}, + State = #s{wiz = {_, WizButtons}}) -> + NewState = + case lists:keyfind(ID, #w.id, WizButtons) of + #w{name = noob} -> wiz_noob_assist(State); + #w{name = l33t} -> close_wiz(State); + false -> State + end, + {noreply, NewState}; +handle_event(#wx{event = #wxClose{}}, State = #s{wiz = {_, _}}) -> + ok = close_wiz(State), + {noreply, State}; handle_event(Event, State) -> ok = tell(info, "Unexpected event ~tp State: ~tp~n", [Event, State]), {noreply, State}. @@ -197,6 +218,12 @@ do_show(Manifest, State = #s{picker = Picker}) -> State#s{wallets = Manifest}. +close_wiz(State = #s{frame = Frame, wiz = {Wiz, _}}) -> + ok = wxWindow:destroy(Wiz), + true = wxFrame:show(Frame), + State#s{wiz = none}. + + do_close(#s{frame = Frame, prefs = Prefs}) -> Geometry = case wxTopLevelWindow:isMaximized(Frame) of @@ -219,6 +246,12 @@ handle_button(Name, State) -> do_open(State = #s{wallets = []}) -> State; +do_open(State = #s{wallets = [#wr{pass = true, path = Path}]}) -> + do_open3(Path, State); +do_open(State = #s{wallets = [#wr{pass = false, path = Path}]}) -> + ok = gd_con:open_wallet(Path, none), + ok = do_close(State), + State; do_open(State = #s{picker = Picker}) -> case wxListBox:getSelection(Picker) of -1 -> State; @@ -270,6 +303,50 @@ do_open3(Path, State = #s{frame = Frame, j = J}) -> end. +do_wiz(State = #s{wx = WX, lang = Lang, wiz = none}) -> + Trans = gd_jt:read_translations(?MODULE), + J = gd_jt:j(Lang, Trans), + + Frame = wxFrame:new(WX, ?wxID_ANY, J("Initializing Wallet")), + MainSz = wxBoxSizer:new(?wxVERTICAL), + + ButtonTemplates = + [{noob, J("Create a default wallet with a new account for me.")}, + {l33t, J("I know what I'm doing.\nJust show me the wallet manager.")}], + + 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), + + Add = fun(#w{wx = Button}) -> wxBoxSizer:add(MainSz, Button, zxw:flags(wide)) end, + ok = lists:foreach(Add, Buttons), + + ok = wxFrame:setSizer(Frame, MainSz), + ok = wxSizer:layout(MainSz), + + ok = wxFrame:connect(Frame, command_button_clicked), + ok = wxFrame:connect(Frame, close_window), + ok = wxFrame:setSize(Frame, {300, 300}), + ok = wxFrame:center(Frame), + true = wxFrame:show(Frame), + State#s{wiz = {Frame, Buttons}}. + +wiz_noob_assist(State = #s{j = J, wiz = {Wiz, _}}) -> + DefaultDir = zx_lib:path(var, "otpr", "gajudesk"), + {{YY, MM, DD}, {Hr, Mn, Sc}} = calendar:local_time(), + Form = "~4.10.0B-~2.10.0B-~2.10.0B_~2.10.0B-~2.10.0B-~2.10.0B", + Name = io_lib:format(Form, [YY, MM, DD, Hr, Mn, Sc]), + Path = filename:join(DefaultDir, Name ++ ".gaju"), + case do_new2(Path, J, Wiz) of + ok -> do_close(State); + abort -> State + end. + + do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) -> DefaultDir = maps:get(dir, Prefs, zx_lib:path(var, "otpr", "gajudesk")), Options =