diff --git a/src/gd_con.erl b/src/gd_con.erl index 563da3d..f2a5119 100644 --- a/src/gd_con.erl +++ b/src/gd_con.erl @@ -528,13 +528,7 @@ code_change(_, State, _) -> terminate(Reason, _) -> ok = log(info, "Reason: ~p,", [Reason]), - case whereis(gmc_con) of - undefined -> - zx:stop(); - PID -> - ok = log(info, "gd_con found at: ~p", [PID]), - application:stop(gajumine) - end. + zx:stop(). diff --git a/src/gd_gui.erl b/src/gd_gui.erl index ad2d0cb..311d513 100644 --- a/src/gd_gui.erl +++ b/src/gd_gui.erl @@ -729,104 +729,28 @@ spend(Selected, State = #s{accounts = Accounts}) -> end. spend2(#poa{id = ID, name = Name}, Nonce, Height, State = #s{frame = Frame, j = J}) -> - Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Transfer"), [{size, {500, 400}}]), - Sizer = wxBoxSizer:new(?wxVERTICAL), - + Title = J("Transfer Gajus"), Account = [Name, " (", ID, ")"], - FromTx = wxStaticText:new(Dialog, ?wxID_ANY, Account), - FromSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("From")}]), - _ = wxStaticBoxSizer:add(FromSz, FromTx, zxw:flags(wide)), - - ToTx = wxTextCtrl:new(Dialog, ?wxID_ANY), - ToSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("To")}]), - _ = wxStaticBoxSizer:add(ToSz, ToTx, zxw:flags(wide)), - - AmtTx = wxTextCtrl:new(Dialog, ?wxID_ANY), - AmtSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Amount")}]), - AmtInSz = wxBoxSizer:new(?wxHORIZONTAL), - AmtLabel = wxStaticText:new(Dialog, ?wxID_ANY, "木"), - _ = wxStaticBoxSizer:add(AmtInSz, AmtLabel, zxw:flags(base)), - _ = wxStaticBoxSizer:add(AmtInSz, AmtTx, zxw:flags(wide)), - _ = wxStaticBoxSizer:add(AmtSz, AmtInSz, zxw:flags(wide)), - - DataTx = wxTextCtrl:new(Dialog, ?wxID_ANY, [{style, ?wxTE_MULTILINE}]), - DataSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Message (optional)")}]), - _ = wxStaticBoxSizer:add(DataSz, DataTx, zxw:flags(wide)), - - Style = [{style, ?wxSL_HORIZONTAL bor ?wxSL_LABELS}], - - TTL_Sl = wxSlider:new(Dialog, ?wxID_ANY, 100, 10, 1000, Style), - TTL_Sz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("TTL")}]), - _ = wxStaticBoxSizer:add(TTL_Sz, TTL_Sl, zxw:flags(wide)), - - Min = hz:min_gas_price(), - Max = Min * 2, - GasSl = wxSlider:new(Dialog, ?wxID_ANY, Min, Min, Max, Style), - GasSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J("Gas Price")}]), - _ = wxStaticBoxSizer:add(GasSz, GasSl, 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, FromSz, zxw:flags(base)), - _ = wxBoxSizer:add(Sizer, ToSz, zxw:flags(base)), - _ = wxBoxSizer:add(Sizer, AmtSz, zxw:flags(base)), - _ = wxBoxSizer:add(Sizer, DataSz, zxw:flags(wide)), - _ = wxBoxSizer:add(Sizer, TTL_Sz, zxw:flags(wide)), - _ = wxBoxSizer:add(Sizer, GasSz, zxw:flags(base)), - _ = wxBoxSizer:add(Sizer, ButtSz, zxw:flags(base)), - ok = wxDialog:setSizer(Dialog, Sizer), - ok = wxBoxSizer:layout(Sizer), - ok = wxFrame:setSize(Dialog, {500, 450}), - ok = wxFrame:center(Dialog), + Args = {Account, Nonce, Height}, + Labels = {J("From"), J("To"), J("Amount"), J("Message (optional)"), J("TTL"), J("Gas Price")}, ok = - case wxDialog:showModal(Dialog) of - ?wxID_OK -> + case gd_m_spend:show(Frame, Title, Args, Labels) of + {ok, RecipientID, Amount, GasPrice, TTL, Payload} -> TX = #spend_tx{sender_id = ID, - recipient_id = wxTextCtrl:getValue(ToTx), - amount = wxTextCtrl:getValue(AmtTx), - gas_price = wxSlider:getValue(GasSl), + recipient_id = RecipientID, + amount = Amount, + gas_price = GasPrice, gas = 20000, - ttl = Height + wxSlider:getValue(TTL_Sl), + ttl = Height + TTL, nonce = Nonce, - payload = wxTextCtrl:getValue(DataTx)}, - clean_spend(TX); - ?wxID_CANCEL -> + payload = Payload}, + gd_con:spend(TX); + cancel -> ok end, - ok = wxDialog:destroy(Dialog), State. -clean_spend(#spend_tx{recipient_id = ""}) -> - ok; -clean_spend( TX = #spend_tx{amount = S}) when is_list(S) -> - case string_to_price(S) of - {ok, Amount} -> clean_spend(TX#spend_tx{amount = Amount}); - {error, _} -> ok - end; -clean_spend(TX = #spend_tx{gas_price = S}) when is_list(S) -> - case is_int(S) of - true -> clean_spend(TX#spend_tx{gas_price = list_to_integer(S)}); - false -> ok - end; -clean_spend(TX = #spend_tx{gas = S}) when is_list(S) -> - case is_int(S) of - true -> clean_spend(TX#spend_tx{gas = list_to_integer(S)}); - false -> ok - end; -clean_spend(TX = #spend_tx{payload = S}) when is_list(S) -> - clean_spend(TX#spend_tx{payload = list_to_binary(S)}); -clean_spend(TX) -> - gd_con:spend(TX). - - -is_int(S) -> - lists:all(fun(C) -> $0 =< C andalso C =< $9 end, S). - grids_dialogue(State = #s{frame = Frame, j = J}) -> Title = J("GRIDS URL"), diff --git a/src/gd_m_spend.erl b/src/gd_m_spend.erl new file mode 100644 index 0000000..969f293 --- /dev/null +++ b/src/gd_m_spend.erl @@ -0,0 +1,274 @@ +%%% @doc +%%% A live modal for importing a wallet. +%%% +%%% Interprets [ENTER] as an "OK" button click and [ESC] as a "Cancel" button click. +%%% @end + +-module(gd_m_wallet_importer). +-vsn("0.8.0"). +-author("Craig Everett "). +-copyright("Craig Everett "). +-license("GPL-3.0-or-later"). + +-behavior(zxw_modal). + +-export([show/6]). +-export([init/1, handle_info/2, handle_event/2]). + +-include_lib("wx/include/wx.hrl"). +-include("$zx_include/zx_logger.hrl"). + +-record(s, + {frame = none :: none | wx:wx_object(), + parent = none :: none | wx:wx_object(), + caller = none :: none | pid(), + to_tx = none :: none | wx:wx_object(), + amount_tx = none :: none | wx:wx_object(), + payload_tx = none :: none | wx:wx_object(), + ttl_sl = none :: none | wx:wx_object(), + gas_sl = none :: none | wx:wx_object(), + ok = none :: none | wx:wx_object(), + cancel = none :: none | wx:wx_object()}). + + +%%% Interface + +-spec show(Parent, Title, Args, Labels) -> {ok, ReceipientID, Amount, GasPrice, TTL, Payload} | cancel + when Parent :: wxFrame:wxFrame(), + Title :: string(), + Args :: {Account :: iolist(), + Nonce :: pos_integer(), + Height :: pos_integer()}, + Labels :: {FromL :: string(), + ToL :: string(), + AmountL :: string(), + MessageL :: string(), + TTL_L :: string(), + GasL :: string(), + AffirmL :: string(), + CancelL :: string()}, + RecipientID :: binary(), % <<"ak_...">> | <<"ct_...">> + Amount :: non_neg_integer(), % Pucks + GasPrice :: pos_integer(), % Pucks + TTL :: non_neg_integer(), % Generations + Payload :: binary(). + +show(Parent, Title, Args, Labels) -> + zxw_modal:show(Parent, ?MODULE, {Title, Args, Labels}). + + + +%% Init + +init({Parent, Caller, {Title, Args, Labels}}) -> + {Account, Nonce, Height} = Args, + {FromL, ToL, AmountL, MessageL, TTL_L, GasL, AffirmL, CancelL} = Labels, + Frame = wxFrame:new(Parent, ?wxID_ANY, Title), + Panel = wxWindow:new(Frame, ?wxID_ANY), + TopSz = wxBoxSizer:new(?wxVERTICAL), + _ = wxBoxSizer:add(TopSz, Panel, zxw:flags(wide)), + MainSz = wxBoxSizer:new(?wxVERTICAL), + + FromTx = wxStaticText:new(Panel, ?wxID_ANY, Account), + FromSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, FromL}]), + _ = wxStaticBoxSizer:add(FromSz, FromTx, zxw:flags({wide, 5})), + ToTx = wxTextCtrl:new(Panel, ?wxID_ANY), + ToSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, ToL}]), + _ = wxStaticBoxSizer:add(ToSz, ToTx, zxw:flags({wide, 5})), + AmtTx = wxTextCtrl:new(Panel, ?wxID_ANY), + AmtSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, AmountL}]), + AmtInSz = wxBoxSizer:new(?wxHORIZONTAL), + AmtLabel = wxStaticText:new(Panel, ?wxID_ANY, "木"), + _ = wxStaticBoxSizer:add(AmtInSz, AmtLabel, zxw:flags(base)), + _ = wxStaticBoxSizer:add(AmtInSz, AmtTx, zxw:flags({wide, 5})), + _ = wxStaticBoxSizer:add(AmtSz, AmtInSz, zxw:flags({wide, 5})), + PayloadTx = wxTextCtrl:new(Panel, ?wxID_ANY, [{style, ?wxTE_MULTILINE}]), + PayloadSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, MessageL}]), + _ = wxStaticBoxSizer:add(PayloadSz, PayloadTx, zxw:flags({wide, 5})), + Style = [{style, ?wxSL_HORIZONTAL bor ?wxSL_LABELS}], + TTL_Sl = wxSlider:new(Panel, ?wxID_ANY, 100, 10, 1000, Style), + TTL_Sz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, TTL_L}]), + _ = wxStaticBoxSizer:add(TTL_Sz, TTL_Sl, zxw:flags({wide, 5})), + Min = hz:min_gas_price(), + Max = Min * 2, + GasSl = wxSlider:new(Panel, ?wxID_ANY, Min, Min, Max, Style), + GasSz = wxStaticBoxSizer:new(?wxVERTICAL, Panel, [{label, GasL}]), + _ = wxStaticBoxSizer:add(GasSz, GasSl, zxw:flags({wide, 5})), + ButtSz = wxBoxSizer:new(?wxHORIZONTAL), + Affirm = wxButton:new(Panel, ?wxID_ANY, [{label, AffirmL}]), + Cancel = wxButton:new(Panel, ?wxID_ANY, [{label, CancelL}]), + _ = wxBoxSizer:add(ButtSz, Affirm, zxw:flags({wide, 5})), + _ = wxBoxSizer:add(ButtSz, Cancel, zxw:flags({wide, 5})), + _ = wxBoxSizer:add(MainSz, FromSz, zxw:flags({base, 5})), + _ = wxBoxSizer:add(MainSz, ToSz, zxw:flags({base, 5})), + _ = wxBoxSizer:add(MainSz, AmtSz, zxw:flags({base, 5})), + _ = wxBoxSizer:add(MainSz, PayloadSz, zxw:flags({wide, 5})), + _ = wxBoxSizer:add(MainSz, TTL_Sz, zxw:flags({wide, 5})), + _ = wxBoxSizer:add(MainSz, GasSz, zxw:flags({base, 5})), + _ = wxBoxSizer:add(MainSz, ButtSz, zxw:flags({base, 5})), + HandleKey = key_handler(ToTx, AmtTx, PayloadTx, TTL_Sl, GasSl, OK, Cancel), + ok = wxFrame:connect(Frame, key_down, [{callback, HandleKey}]), + ok = wxTextCtrl:connect(ToTx, key_down, [{callback, HandleKey}]), + ok = wxTextCtrl:connect(AmtTx, key_down, [{callback, HandleKey}]), + ok = wxTextCtrl:connect(PayloadTx, key_down, [{callback, HandleKey}]), + ok = wxTextCtrl:connect(TTL_Sl, key_down, [{callback, HandleKey}]), + ok = wxTextCtrl:connect(GasSl, key_down, [{callback, HandleKey}]), + ok = wxTextCtrl:connect(OK, key_down, [{callback, HandleKey}]), + ok = wxTextCtrl:connect(Cancel, key_down, [{callback, HandleKey}]), + ok = wxFrame:connect(Frame, command_button_clicked), + ok = wxFrame:connect(Frame, close_window), + ok = wxFrame:setSize(Frame, {500, 450}), + ok = wxFrame:center(Frame), + ok = wxWindow:setSizer(Panel, MainSz), + ok = wxFrame:setSizer(Frame, TopSz), + ok = wxBoxSizer:layout(MainSz), + ok = wxFrame:centerOnParent(Frame), + ok = wxTextCtrl:setFocus(NameTx), + true = wxFrame:show(Frame), + State = + #s{frame = Frame, parent = Parent, caller = Caller, + to_tx = ToTx, amount_tx = AmtTx, payload_tx = PayloadTx, + ttl_sl = TTL_Sl, gas_sl = GasSl, + ok = OK, cancel = Cancel}, + {Frame, State}. + + +key_handler(ToTx, AmtTx, PayloadTx, TTL_Sl, GasSl, OK, Cancel) -> + Me = self(), + ToID = wxTextCtrl:getId(ToTx), + AmtID = wxTextCtrl:getId(AmtTx), + PL_ID = wxTextCtrl:getId(PayloadTx), + TTL_ID = wxSlider:getId(TTL_Sl), + GasID = wxSlider:getId(GasSl), + OK_ID = wxButton:getId(OK), + CancelID = wxButton:getId(Cancel), + fun(#wx{id = ID, event = #wxKey{type = key_down, keyCode = Code}}, KeyPress) -> + case Code of +% 9 -> +% case ID of +% ToID -> Me ! {tab, to_tx}; +% AmtID -> Me ! {tab, amount_tx}; +% PL_ID -> Me ! {tab, payload_tx}; +% TTL_ID -> Me ! {tab, ttl_sl}; +% GasID -> Me ! {tab, gas_sl}; +% OK_ID -> Me ! {tab, ok}; +% CancelID -> Me ! {tab, cancel}; +% _ -> wxEvent:skip(KeyPress) +% end; +% 13 -> +% case ID of +% NameID -> Me ! {enter, name}; +% PassID -> Me ! {enter, pass}; +% _ -> wxEvent:skip(KeyPress) +% end; + 27 -> + Me ! esc; + _ -> + wxEvent:skip(KeyPress) + end + end. + + + +handle_info({tab, Element}, State) -> + ok = tab_traverse(Element, State), + {noreply, State}; +handle_info({enter, name}, State = #s{pass_tx = PassTx}) -> + ok = wxTextCtrl:setFocus(PassTx), + {noreply, State}; +handle_info({enter, pass}, State) -> + done(State); +handle_info(esc, State) -> + ok = cancel(State), + {noreply, State}; +handle_info(Message, State) -> + ok = log(warning, "Message: ~p~nState: ~p~n", [Message, State]), + {noreply, State}. + + +handle_event(#wx{event = #wxCommand{type = command_button_clicked}, id = ID}, + State = #s{ok = OK, cancel = Cancel}) -> + OK_ID = wxButton:getId(OK), + CancelID = wxButton:getId(Cancel), + case ID of + OK_ID -> done(State); + CancelID -> cancel(State) + end; +handle_event(#wx{event = #wxClose{}}, State) -> + NewState = cancel(State), + {noreply, NewState}; +handle_event(Event, State) -> + ok = log(warning, "Unexpected event ~tp State: ~tp~n", [Event, State]), + {noreply, State}. + + +%%% Doers + +%tab_traverse(name, #s{pass_tx = PassTx}) -> +% wxTextCtrl:setFocus(PassTx); +%tab_traverse(pass, #s{ok = OK}) -> +% wxButton:setFocus(OK); +%tab_traverse(ok, #s{cancel = Cancel}) -> +% wxButton:setFocus(Cancel); +%tab_traverse(cancel, #s{name_tx = NameTx}) -> +% wxTextCtrl:setFocus(NameTx). + + +cancel(#s{frame = Frame, caller = Caller}) -> + ok = wxFrame:destroy(Frame), + zxw_modal:done(Caller, cancel). + + +done(#s{frame = Frame, caller = Caller, + to_tx = ToTx, amount_tx = AmountTx, payload_tx = PayloadTx, + ttl_sl = TTL_Sl, gas_sl = GasSl}) -> + DirtyTX = + #spend_tx{recipient_id = wxTextCtrl:getValue(ToTx), + amount = wxTextCtrl:getValue(AmountTx), + gas_price = wxSlider:getValue(GasSl), + ttl = wxSlider:getValue(TTL_Sl), + payload = wxTextCtrl:getValue(PayloadTX)}, + Result = + case clean_spend(TX) of + {ok, CleanTX} -> + + + Result = + case wxTextCtrl:getValue(NameTx) of + "" -> + cancel; + Name -> + case wxTextCtrl:getValue(PassTx) of + "" -> {ok, Name, none}; + Pass -> {ok, Name, Pass} + end + end, + ok = wxFrame:destroy(Frame), + zxw_modal:done(Caller, Result). + + +clean_spend(#spend_tx{recipient_id = ""}) -> + ok; +clean_spend( TX = #spend_tx{amount = S}) when is_list(S) -> + case string_to_price(S) of + {ok, Amount} -> clean_spend(TX#spend_tx{amount = Amount}); + {error, _} -> ok + end; +clean_spend(TX = #spend_tx{gas_price = S}) when is_list(S) -> + case is_int(S) of + true -> clean_spend(TX#spend_tx{gas_price = list_to_integer(S)}); + false -> ok + end; +clean_spend(TX = #spend_tx{gas = S}) when is_list(S) -> + case is_int(S) of + true -> clean_spend(TX#spend_tx{gas = list_to_integer(S)}); + false -> ok + end; +clean_spend(TX = #spend_tx{payload = S}) when is_list(S) -> + clean_spend(TX#spend_tx{payload = list_to_binary(S)}); +clean_spend(TX) -> + {ok, CleanTX}. + + +is_int(S) -> + lists:all(fun(C) -> $0 =< C andalso C =< $9 end, S).