From a164af45cd601aa9f4e2101803ffabb0348970d2 Mon Sep 17 00:00:00 2001 From: Craig Everett Date: Fri, 1 Nov 2024 17:00:13 +0900 Subject: [PATCH] Fewer bugs --- src/gmc_con.erl | 209 +++++++++++++++++++++++++++++++---------- src/gmc_gui.erl | 101 ++++++++++---------- src/gmc_key_master.erl | 41 -------- src/gmc_v.erl | 46 +++++++++ src/gmc_v_netman.erl | 38 +------- src/gmc_v_wallman.erl | 132 +++++++++++++++----------- zomp.meta | 4 +- 7 files changed, 338 insertions(+), 233 deletions(-) create mode 100644 src/gmc_v.erl diff --git a/src/gmc_con.erl b/src/gmc_con.erl index 39b1cd3..60db7be 100644 --- a/src/gmc_con.erl +++ b/src/gmc_con.erl @@ -12,7 +12,8 @@ -behavior(gen_server). -export([show_ui/1, - open_wallet/2, close_wallet/0, new_wallet/3, import_wallet/3, password/2, + open_wallet/2, close_wallet/0, new_wallet/3, import_wallet/3, drop_wallet/2, + password/2, refresh/0, nonce/1, spend/2, chain/1, grids/1, make_key/6, recover_key/1, mnemonic/1, rename_key/2, drop_key/1, @@ -94,6 +95,14 @@ import_wallet(Name, Path, Password) -> gen_server:cast(?MODULE, {import_wallet, Name, Path, Password}). +-spec drop_wallet(Path, Delete) -> ok + when Path :: file:filename(), + Delete :: boolean(). + +drop_wallet(Path, Delete) -> + gen_server:cast(?MODULE, {drop_wallet, Path, Delete}). + + -spec password(Old, New) -> ok when Old :: none | string(), New :: none | string(). @@ -241,20 +250,16 @@ init(none) -> Prefs = read_prefs(), GUI_Prefs = maps:get(gmc_gui, Prefs, #{}), Window = gmc_gui:start_link(GUI_Prefs), - State = #s{window = Window, prefs = Prefs}, + Wallets = get_prefs(wallets, Prefs, []), + State = #s{window = Window, wallets = Wallets, prefs = Prefs}, NewState = do_show_ui(gmc_v_wallman, State), {ok, NewState}. read_prefs() -> case file:consult(prefs_path()) of - {ok, Prefs} -> - proplists:to_map(Prefs); - _ -> - #{selected => 0, - last => save_path(), - lang => en_us, - geometry => none} + {ok, Prefs} -> proplists:to_map(Prefs); + _ -> #{} end. @@ -309,6 +314,12 @@ handle_cast(close_wallet, State) -> handle_cast({new_wallet, Name, Path, Password}, State) -> NewState = do_new_wallet(Name, Path, Password, State), {noreply, NewState}; +handle_cast({import_wallet, Name, Path, Password}, State) -> + NewState = do_import_wallet(Name, Path, Password, State), + {noreply, NewState}; +handle_cast({drop_wallet, Path, Delete}, State) -> + NewState = do_drop_wallet(Path, Delete, State), + {noreply, NewState}; handle_cast({password, Old, New}, State) -> NewState = do_password(Old, New, State), {noreply, NewState}; @@ -434,19 +445,37 @@ do_add_node(New, State) -> State. -% FIXME: This will, of course, totally explode if anything goes wrong do_set_sole_node(TOTN = #node{external = none}, State) -> do_set_sole_node(TOTN#node{external = 3013}, State); -do_set_sole_node(New, State = #s{wallet = W}) -> - ChainID = ensure_hz_set(New), - Net = #net{id = ChainID, chains = [#chain{id = ChainID, nodes = [New]}]}, - NewWallet = W#wallet{chain_id = ChainID, endpoint = New, nets = [Net]}, - NewState = State#s{wallet = NewWallet}, - do_refresh(NewState). +do_set_sole_node(New = #node{ip = IP, external = Port}, State = #s{wallet = W}) -> + ok = hz:chain_nodes([{IP, Port}]), + case ensure_hz_set(New) of + {ok, ChainID} -> + Net = #net{id = ChainID, chains = [#chain{id = ChainID, nodes = [New]}]}, + NewWallet = W#wallet{chain_id = ChainID, endpoint = New, nets = [Net]}, + NewState = State#s{wallet = NewWallet}, + do_refresh(NewState); + Error -> + gmc_gui:trouble(Error), + State + end. -do_refresh(State = #s{wallet = W = #wallet{poas = POAs, endpoint = Node}}) -> - ChainID = ensure_hz_set(Node), +do_refresh(State = #s{wallet = none}) -> + State; +do_refresh(State = #s{wallet = #wallet{endpoint = none}}) -> + State; +do_refresh(State = #s{wallet = #wallet{endpoint = Node}}) -> + case ensure_hz_set(Node) of + {ok, ChainID} -> + do_refresh2(ChainID, State); + Error -> + ok = gmc_gui:trouble({do_refresh, 1, Error}), + State + end. + + +do_refresh2(ChainID, State = #s{wallet = W = #wallet{poas = POAs}}) -> CheckBalance = fun(This = #poa{id = ID}) -> Pucks = @@ -467,14 +496,14 @@ do_refresh(State = #s{wallet = W = #wallet{poas = POAs, endpoint = Node}}) -> ensure_hz_set(Node = #node{ip = IP, external = Port}) -> case hz:status() of {ok, #{"network_id" := ChainID}} -> - list_to_binary(ChainID); - {error, no_nodes} -> - ok = hz:chain_nodes([{IP, Port}]), - {ok, #{"network_id" := C}} = hz:status(), - ChainID = list_to_binary(C), ok = hz:network_id(ChainID), ok = gmc_gui:chain(ChainID, Node), - ChainID + {ok, list_to_binary(ChainID)}; + {error, no_nodes} -> + ok = hz:chain_nodes([{IP, Port}]), + ensure_hz_set(Node); + Error -> + Error end. @@ -696,23 +725,17 @@ do_drop_key(ID, State = #s{wallet = W}) -> State#s{wallet = NewWallet}. -do_open_wallet(Path, none, State) -> - case read(Path, none) of - {ok, Recovered = #wallet{poas = POAs, chain_id = ChainID, endpoint = Node}} -> - ok = gmc_gui:show(POAs), - ok = gmc_gui:chain(ChainID, Node), - State#s{wallet = Recovered}; - Error -> - ok = gmc_gui:trouble(Error), - New = default_wallet(), - State#s{wallet = New} - end; do_open_wallet(Path, Phrase, State) -> Pass = pass(Phrase), case read(Path, Pass) of - {ok, Recovered = #wallet{poas = POAs, chain_id = ChainID, endpoint = Node}} -> + {ok, Recovered = #wallet{name = Name, poas = POAs, endpoint = Node}} -> ok = gmc_gui:show(POAs), - ok = gmc_gui:chain(ChainID, Node), + ok = gmc_gui:wallet(Name), + ok = + case ensure_hz_set(Node) of + {ok, ChainID} -> gmc_gui:chain(ChainID, Node); + Error -> gmc_gui:trouble(Error) + end, State#s{wallet = Recovered}; Error -> ok = gmc_gui:trouble(Error), @@ -723,12 +746,12 @@ do_open_wallet(Path, Phrase, State) -> default_wallet() -> DevNet = #net{id = <<"devnet">>, chains = [#chain{}]}, - TestChain1 = #chain{id = <<"groot.testnet">>, - nodes = [#node{ip = {1,2,3,4}}, #node{ip = {5,6,7,8}}]}, - TestChain2 = #chain{id = <<"test_ac.testnet">>, - nodes = [#node{ip = {11,12,13,14}}, #node{ip = {15,16,17,18}}]}, - TestNet = #net{id = <<"testnet">>, chains = [TestChain1, TestChain2]}, - #wallet{nets = [DevNet, TestNet]}. +% TestChain1 = #chain{id = <<"groot.testnet">>, +% nodes = [#node{ip = {1,2,3,4}}, #node{ip = {5,6,7,8}}]}, +% TestChain2 = #chain{id = <<"test_ac.testnet">>, +% nodes = [#node{ip = {11,12,13,14}}, #node{ip = {15,16,17,18}}]}, +% TestNet = #net{id = <<"testnet">>, chains = [TestChain1, TestChain2]}, + #wallet{nets = [DevNet]}. do_password(none, none, State) -> @@ -765,23 +788,113 @@ do_stop(State = #s{prefs = Prefs}) -> do_close_wallet(State). -do_new_wallet(Name, Path, Password, State = #s{wallets = Wallets}) -> +do_new_wallet(Name, Path, Password, State = #s{wallets = Wallets, prefs = Prefs}) -> case lists:keyfind(Name, #wr.name, Wallets) of false -> NextState = do_close_wallet(State), Pass = pass(Password), HasPass = Pass =/= none, Entry = #wr{name = Name, path = Path, pass = HasPass}, - New = #wallet{}, + New = #wallet{name = Name}, ok = save_wallet(Entry, Pass, New), ok = gmc_gui:show([]), - NextState#s{wallet = New, pass = Pass, wallets = [Entry | Wallets]}; + ok = gmc_gui:wallet(Name), + NewWallets = [Entry | Wallets], + NewPrefs = put_prefs(wallets, NewWallets, Prefs), + ok = persist(NewPrefs), + NextState#s{wallet = New, pass = Pass, wallets = NewWallets, prefs = NewPrefs}; #wr{} -> % FIXME % Need to provide feedback based on where this came from State end. +do_import_wallet(Name, Path, Password, State = #s{wallets = Wallets}) -> + NameExists = lists:keymember(Name, #wr.name, Wallets), + PathExists = lists:keymember(Path, #wr.path, Wallets), + case {NameExists, PathExists} of + {false, false} -> + do_import_wallet2(Name, Path, Password, State); + {true, false} -> + ok = gmc_gui:trouble({error, name_exists}), + State; + {false, true} -> + ok = gmc_gui:trouble({error, path_exists}), + State; + {true, true} -> + ok = gmc_gui:trouble("Whoa! This exact wallet already exists!"), + State + end. + +do_import_wallet2(Name, Path, Password, State = #s{wallets = Wallets, prefs = Prefs}) -> + Pass = pass(Password), + case read(Path, Pass) of + {ok, Recovered = #wallet{poas = POAs, chain_id = ChainID, endpoint = Endpoint}} -> + Imported = Recovered#wallet{name = Name}, + HasPass = Pass =/= none, + Record = #wr{name = Name, path = Path, pass = HasPass}, + NewWallets = [Record | Wallets], + NewPrefs = put_prefs(wallets, NewWallets, Prefs), + ok = save_wallet(Record, Pass, Imported), + ok = persist(NewPrefs), + ok = gmc_gui:show(POAs), + ok = gmc_gui:chain(ChainID, Endpoint), + ok = gmc_gui:wallet(Name), + State#s{wallet = Imported, wallets = NewWallets, prefs = NewPrefs}; + Error -> + ok = gmc_gui:trouble(Error), + State + end. + + +do_drop_wallet(Path, Delete, State = #s{tasks = Tasks, + wallet = Wallet, + wallets = Wallets, + prefs = Prefs}) -> + CurrentName = + case Wallet of + #wallet{name = N} -> N; + none -> none + end, + case lists:keytake(Path, #wr.path, Wallets) of + {value, #wr{name = Name}, NewWallets} -> + ok = + case Name =:= CurrentName of + true -> + ok = gmc_gui:show([]), + ok = gmc_gui:wallet(none), + ok = gmc_gui:chain(none, none); + false -> + ok + end, + ok = maybe_clean(Delete, Path), + NewPrefs = put_prefs(wallets, NewWallets, Prefs), + ok = persist(NewPrefs), + #ui{wx = WallMan} = lists:keyfind(gmc_v_wallman, #ui.name, Tasks), + ok = gmc_v_wallman:show(WallMan, NewWallets), + State#s{wallets = NewWallets, prefs = NewPrefs}; + false -> + State + end. + +maybe_clean(true, Path) -> + case file:delete(Path) of + ok -> ok; + Error -> gmc_gui:trouble(Error) + end; +maybe_clean(false, _) -> + ok. + + +get_prefs(K, M, D) -> + P = maps:get(?MODULE, M, #{}), + maps:get(K, P, D). + +put_prefs(K, V, M) -> + P = maps:get(?MODULE, M, #{}), + NewP = maps:put(K, V, P), + maps:put(?MODULE, NewP, M). + do_save(Module, Prefs, State = #s{prefs = Cached}) -> Updated = maps:put(Module, Prefs, Cached), @@ -838,10 +951,6 @@ read2(Bin) -> end. -save_path() -> - filename:join(zx_lib:path(var, "otpr", "clutch"), "default.gaju"). - - persist(Prefs) -> Path = prefs_path(), ok = filelib:ensure_dir(Path), diff --git a/src/gmc_gui.erl b/src/gmc_gui.erl index 11ba598..7b52e09 100644 --- a/src/gmc_gui.erl +++ b/src/gmc_gui.erl @@ -14,7 +14,7 @@ -behavior(wx_object). -include_lib("wx/include/wx.hrl"). --export([show/1, chain/2, trouble/1, ask_password/0]). +-export([show/1, wallet/1, chain/2, trouble/1, ask_password/0]). -export([start_link/1]). -export([init/1, terminate/2, code_change/3, handle_call/3, handle_cast/2, handle_info/2, handle_event/2]). @@ -52,6 +52,10 @@ show(Accounts) -> wx_object:cast(?MODULE, {show, Accounts}). +wallet(Name) -> + wx_object:cast(?MODULE, {wallet, Name}). + + chain(ChainID, Node) -> wx_object:cast(?MODULE, {chain, ChainID, Node}). @@ -178,16 +182,7 @@ init(Prefs) -> ok = wxFrame:setSizer(Frame, MainSz), ok = wxSizer:layout(MainSz), - ok = - case safe_size(Prefs) of - {pref, max} -> - wxTopLevelWindow:maximize(Frame); - {pref, WSize} -> - wxFrame:setSize(Frame, WSize); - {center, WSize} -> - ok = wxFrame:setSize(Frame, WSize), - wxFrame:center(Frame) - end, + ok = gmc_v:safe_size(Frame, Prefs), ok = wxFrame:connect(Frame, command_button_clicked), ok = wxFrame:connect(Frame, close_window), @@ -202,35 +197,6 @@ init(Prefs) -> {Frame, State}. -% Put this in a gmc bevhavior -safe_size(Prefs) -> - Display = wxDisplay:new(), - GSize = wxDisplay:getGeometry(Display), - CSize = wxDisplay:getClientArea(Display), - PPI = - try - wxDisplay:getPPI(Display) - catch - Class:Exception -> {Class, Exception} - end, - ok = log(info, "Geometry: ~p", [GSize]), - ok = log(info, "ClientArea: ~p", [CSize]), - ok = log(info, "PPI: ~p", [PPI]), - Geometry = - case maps:find(geometry, Prefs) of - {ok, none} -> - {X, Y, _, _} = GSize, - {center, {X, Y, 420, 520}}; - {ok, G} -> - {pref, G}; - error -> - {X, Y, _, _} = GSize, - {center, {X, Y, 420, 520}} - end, - ok = wxDisplay:destroy(Display), - Geometry. - - -spec handle_call(Message, From, State) -> Result when Message :: term(), From :: {pid(), reference()}, @@ -257,9 +223,15 @@ handle_call(Unexpected, From, State) -> handle_cast({show, Accounts}, State) -> NewState = do_show(Accounts, State), {noreply, NewState}; +handle_cast({wallet, Name}, State) -> + ok = do_wallet(Name, State), + {noreply, State}; handle_cast({chain, ChainID, Node}, State) -> ok = do_chain(ChainID, Node, State), {noreply, State}; +handle_cast({trouble, Info}, State) -> + ok = handle_trouble(Info, State), + {noreply, State}; handle_cast(password, State) -> ok = do_ask_password(State), {noreply, State}; @@ -629,12 +601,13 @@ drop_key(State = #s{picker = Picker}) -> Selected -> drop_key(Selected + 1, State) end. -drop_key(Selected, State = #s{frame = Frame, j = J, accounts = Accounts}) -> +drop_key(Selected, State = #s{frame = Frame, j = J, accounts = Accounts, prefs = Prefs}) -> #poa{id = ID, name = Name} = lists:nth(Selected, Accounts), - Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Key")), + Dialog = wxDialog:new(Frame, ?wxID_ANY, J("New Key"), [{size, {500, 150}}]), Sizer = wxBoxSizer:new(?wxVERTICAL), - Message = ["REALLY delete key: ", Name, " ?"], - MessageT = wxStaticText:new(Dialog, ?wxID_ANY, Message), + Message = ["REALLY delete key?\r\n\r\n\"", Name, "\"\r\n(", ID, ")"], + MessageT = wxStaticText:new(Dialog, ?wxID_ANY, Message, + [{style, ?wxALIGN_CENTRE_HORIZONTAL}]), ButtSz = wxBoxSizer:new(?wxHORIZONTAL), Affirm = wxButton:new(Dialog, ?wxID_OK), Cancel = wxButton:new(Dialog, ?wxID_CANCEL), @@ -645,13 +618,19 @@ drop_key(Selected, State = #s{frame = Frame, j = J, accounts = Accounts}) -> ok = wxDialog:setSizer(Dialog, Sizer), ok = wxBoxSizer:layout(Sizer), ok = wxFrame:center(Dialog), - ok = + NewPrefs = case wxDialog:showModal(Dialog) of - ?wxID_OK -> gmc_con:drop_key(ID); - ?wxID_CANCEL -> ok + ?wxID_OK -> + gmc_con:drop_key(ID), + case Selected =:= length(Accounts) of + true -> maps:put(selected, Selected - 2, Prefs); + false -> Prefs + end; + ?wxID_CANCEL -> + Prefs end, ok = wxDialog:destroy(Dialog), - State. + State#s{prefs = NewPrefs}. copy(State = #s{id = {_, #w{wx = ID_T}}}) -> @@ -853,13 +832,16 @@ handle_button(Name, State) -> do_selection(Selected, State = #s{prefs = Prefs, accounts = Accounts, - balance = {_, #w{wx = B}}, id = {_, #w{wx = I}}}) -> + balance = {_, #w{wx = B}}, id = {_, #w{wx = I}}}) + when Selected < length(Accounts) -> #poa{id = ID, balances = Balances} = lists:nth(Selected + 1, Accounts), [#balance{total = Pucks}] = Balances, ok = wxStaticText:setLabel(I, ID), ok = wxStaticText:setLabel(B, price_to_string(Pucks)), NewPrefs = maps:put(selected, Selected, Prefs), - State#s{prefs = NewPrefs}. + State#s{prefs = NewPrefs}; +do_selection(_, State) -> + do_selection(0, State). do_show(Accounts, State = #s{prefs = Prefs, picker = Picker}) -> @@ -873,12 +855,25 @@ do_show(Accounts, State = #s{prefs = Prefs, picker = Picker}) -> ok = wxListBox:setSelection(Picker, Selected), do_selection(Selected, State#s{accounts = Accounts}); _ -> - Selected = maps:get(selected, Prefs), + Selected = maps:get(selected, Prefs, 0), ok = wxListBox:setSelection(Picker, Selected), do_selection(Selected, State#s{accounts = Accounts}) end. +do_wallet(none, #s{buttons = Buttons}) -> + #w{wx = WalletB} = lists:keyfind(wallet, #w.name, Buttons), + ok = wxButton:setLabel(WalletB, "[no wallet]"); +do_wallet(Name, #s{buttons = Buttons}) -> + #w{wx = WalletB} = lists:keyfind(wallet, #w.name, Buttons), + ok = wxButton:setLabel(WalletB, Name). + + +do_chain(none, none, #s{buttons = Buttons}) -> + #w{wx = ChainB} = lists:keyfind(chain, #w.name, Buttons), + #w{wx = NodeB} = lists:keyfind(node, #w.name, Buttons), + ok = wxButton:setLabel(ChainB, "[no chain]"), + ok = wxButton:setLabel(NodeB, "[no node]"); do_chain(ChainID, #node{ip = IP}, #s{buttons = Buttons}) -> #w{wx = ChainB} = lists:keyfind(chain, #w.name, Buttons), #w{wx = NodeB} = lists:keyfind(node, #w.name, Buttons), @@ -892,6 +887,10 @@ do_chain(ChainID, #node{ip = IP}, #s{buttons = Buttons}) -> ok = wxButton:setLabel(NodeB, Address). +handle_trouble(Info, #s{frame = Frame}) -> + zxw:show_message(Frame, Info). + + do_ask_password(#s{frame = Frame, prefs = Prefs, j = J}) -> Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Password")), Sizer = wxBoxSizer:new(?wxVERTICAL), diff --git a/src/gmc_key_master.erl b/src/gmc_key_master.erl index b9c10e8..d54a4a3 100644 --- a/src/gmc_key_master.erl +++ b/src/gmc_key_master.erl @@ -12,50 +12,9 @@ -export([make_key/2, encode/1, decode/1]). -export([lcg/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"). -%-record(s, -% {wx = none :: none | wx:wx_object(), -% frame = none :: none | wx:wx_object(), -% lang = en :: en | jp, -% j = none :: none | fun(), -% prefs = #{} :: #{atom() := term()}, -% accounts = [] :: [clutch:ak()], -% picker = none :: none | wx:wx_object(), -% balance = {#w{}, #w{}} :: labeled(), -% buttons = [] :: [widget()]}). - - - -%%% Startup Functions - -%start_link(Accounts) -> -% wx_object:start_link({local, ?MODULE}, ?MODULE, Accounts, []). - - -%init({Accounts, Prefs}) -> -% ok = log(info, "GUI starting..."), -% Lang = maps:get(lang, Prefs, en_us), -% Trans = gmc_jt:read_translations(?MODULE), -% J = gmc_jt:j(Lang, Trans), -% -% Selected = maps:get(selected, Prefs, 1), -% AKs = [ID || #ak{id = ID} <- Accounts], -% -% Wx = wx:new(), -% Frame = wxFrame:new(Wx, ?wxID_ANY, J("Gajumaru Clutch")), -% MainSz = wxBoxSizer:new(?wxVERTICAL), - - - -%%% - - make_key("", <<>>) -> Pair = #{public := Public} = ecu_eddsa:sign_keypair(), ID = aeser_api_encoder:encode(account_pubkey, Public), diff --git a/src/gmc_v.erl b/src/gmc_v.erl new file mode 100644 index 0000000..6736057 --- /dev/null +++ b/src/gmc_v.erl @@ -0,0 +1,46 @@ +-module(gmc_v). +-vsn("0.1.0"). +-author("Craig Everett "). +-copyright("QPQ AG "). +-license("GPL-3.0-or-later"). + +-export([safe_size/2]). + + +-callback to_front(Window) -> ok + when Window :: wx:wx_object(). + + + +-spec safe_size(Frame, Prefs) -> ok + when Frame :: wx:wx_object(), + Prefs :: map(). + +safe_size(Frame, Prefs) -> + case safe_size(Prefs) of + {pref, max} -> + wxTopLevelWindow:maximize(Frame); + {pref, WSize} -> + wxFrame:setSize(Frame, WSize); + {center, WSize} -> + ok = wxFrame:setSize(Frame, WSize), + wxFrame:center(Frame) + end. + + +safe_size(Prefs) -> + Display = wxDisplay:new(), + GSize = wxDisplay:getGeometry(Display), + ok = wxDisplay:destroy(Display), + Geometry = + case maps:find(geometry, Prefs) of + {ok, none} -> + {X, Y, _, _} = GSize, + {center, {X, Y, 700, 500}}; + {ok, G} -> + {pref, G}; + error -> + {X, Y, _, _} = GSize, + {center, {X, Y, 700, 500}} + end, + Geometry. diff --git a/src/gmc_v_netman.erl b/src/gmc_v_netman.erl index 8d43769..3ac2085 100644 --- a/src/gmc_v_netman.erl +++ b/src/gmc_v_netman.erl @@ -5,6 +5,7 @@ -license("GPL-3.0-or-later"). -behavior(wx_object). +-behavior(gmc_v). -include_lib("wx/include/wx.hrl"). -export([to_front/1]). -export([set_manifest/1]). @@ -90,16 +91,7 @@ init({Prefs, Manifest}) -> _ = wxFrame:setSizer(Frame, MainSz), _ = wxSizer:layout(MainSz), - ok = - case safe_size(Prefs) of - {pref, max} -> - wxTopLevelWindow:maximize(Frame); - {pref, WSize} -> - wxFrame:setSize(Frame, WSize); - {center, WSize} -> - ok = wxFrame:setSize(Frame, WSize), - wxFrame:center(Frame) - end, + ok = gmc_v:safe_size(Frame, Prefs), ok = wxFrame:connect(Frame, close_window), ok = wxFrame:connect(Frame, command_button_clicked), @@ -112,32 +104,6 @@ init({Prefs, Manifest}) -> buttons = Buttons, nets = Manifest, book = Notebook}, {Frame, State}. -safe_size(Prefs) -> - Display = wxDisplay:new(), - GSize = wxDisplay:getGeometry(Display), - CSize = wxDisplay:getClientArea(Display), - PPI = - try - wxDisplay:getPPI(Display) - catch - Class:Exception -> {Class, Exception} - end, - ok = log(info, "Geometry: ~p", [GSize]), - ok = log(info, "ClientArea: ~p", [CSize]), - ok = log(info, "PPI: ~p", [PPI]), - Geometry = - case maps:find({?MODULE, geometry}, Prefs) of - {ok, none} -> - {X, Y, _, _} = GSize, - {center, {X, Y, 420, 520}}; - {ok, G} -> - {pref, G}; - error -> - {X, Y, _, _} = GSize, - {center, {X, Y, 420, 520}} - end, - ok = wxDisplay:destroy(Display), - Geometry. add_pages(Notebook, J, [#net{id = ID, chains = Chains} | Rest]) -> Page = wxScrolledWindow:new(Notebook), diff --git a/src/gmc_v_wallman.erl b/src/gmc_v_wallman.erl index 1a20d63..fa608a2 100644 --- a/src/gmc_v_wallman.erl +++ b/src/gmc_v_wallman.erl @@ -5,8 +5,10 @@ -license("GPL-3.0-or-later"). -behavior(wx_object). +-behavior(gmc_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]). @@ -39,6 +41,13 @@ 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 @@ -80,21 +89,11 @@ init({Prefs, Manifest}) -> ok = wxFrame:setSizer(Frame, MainSz), ok = wxSizer:layout(MainSz), - ok = - case safe_size(Prefs) of - {pref, max} -> - wxTopLevelWindow:maximize(Frame); - {pref, WSize} -> - wxFrame:setSize(Frame, WSize); - {center, WSize} -> - ok = wxFrame:setSize(Frame, WSize), - wxFrame:center(Frame) - end, + ok = gmc_v:safe_size(Frame, Prefs), ok = wxFrame:connect(Frame, command_button_clicked), ok = wxFrame:connect(Frame, close_window), true = wxFrame:show(Frame), - ok = wxListBox:connect(Picker, command_listbox_selected), ok = wxListBox:connect(Picker, command_listbox_doubleclicked), State = #s{wx = Wx, frame = Frame, lang = Lang, j = J, prefs = Prefs, wallets = Manifest, @@ -103,34 +102,6 @@ init({Prefs, Manifest}) -> {Frame, State}. -safe_size(Prefs) -> - Display = wxDisplay:new(), - GSize = wxDisplay:getGeometry(Display), - CSize = wxDisplay:getClientArea(Display), - PPI = - try - wxDisplay:getPPI(Display) - catch - Class:Exception -> {Class, Exception} - end, - ok = log(info, "Geometry: ~p", [GSize]), - ok = log(info, "ClientArea: ~p", [CSize]), - ok = log(info, "PPI: ~p", [PPI]), - Geometry = - case maps:find(geometry, Prefs) of - {ok, none} -> - {X, Y, _, _} = GSize, - {center, {X, Y, 420, 520}}; - {ok, G} -> - {pref, G}; - error -> - {X, Y, _, _} = GSize, - {center, {X, Y, 420, 520}} - end, - ok = wxDisplay:destroy(Display), - Geometry. - - %%% wx_object @@ -142,6 +113,9 @@ handle_call(Unexpected, From, 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}. @@ -160,10 +134,16 @@ handle_event(#wx{event = #wxCommand{type = command_button_clicked}, #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}; @@ -186,6 +166,12 @@ terminate(Reason, State) -> %%% 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 @@ -229,7 +215,7 @@ do_open3(Path, State = #s{frame = Frame, j = J}) -> Dialog = wxDialog:new(Frame, ?wxID_ANY, Label), Sizer = wxBoxSizer:new(?wxVERTICAL), PassSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, Label}]), - PassTx = wxTextCtrl:new(Dialog, ?wxID_ANY), + 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), @@ -242,17 +228,18 @@ do_open3(Path, State = #s{frame = Frame, j = J}) -> ok = wxStyledTextCtrl:setFocus(PassTx), case wxDialog:showModal(Dialog) of ?wxID_OK -> - ok = wxFileDialog:destroy(Dialog), case wxTextCtrl:getValue(PassTx) of "" -> + ok = wxDialog:destroy(Dialog), State; Password -> + ok = wxDialog:destroy(Dialog), ok = gmc_con:open_wallet(Path, Password), ok = do_close(State), State end; ?wxID_CANCEL -> - ok = wxFileDialog:destroy(Dialog), + ok = wxDialog:destroy(Dialog), State end. @@ -285,18 +272,19 @@ do_new(State = #s{frame = Frame, j = J, prefs = Prefs}) -> end. do_new2(Path, J, Frame) -> - Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Set Node")), + Dialog = wxDialog:new(Frame, ?wxID_ANY, J("Set Node"), [{size, {400, 250}}]), 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), + 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)), - Label = "Passphrase Confirmation", - PassConSz = wxStaticBoxSizer:new(?wxVERTICAL, Dialog, [{label, J(Label)}]), - PassConTx = wxTextCtrl:new(Dialog, ?wxID_ANY), + 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), @@ -312,8 +300,7 @@ do_new2(Path, J, Frame) -> ok = wxDialog:setSizer(Dialog, Sizer), ok = wxBoxSizer:layout(Sizer), - ok = wxFrame:setSize(Dialog, {500, 200}), - ok = wxFrame:center(Dialog), + ok = wxDialog:center(Dialog), ok = wxStyledTextCtrl:setFocus(NameTx), Result = @@ -419,6 +406,45 @@ do_import2(Dir, File, J, Frame) -> Result. -do_selection(Selected, State = #s{prefs = Prefs}) -> - NewPrefs = maps:put(selected, Selected, Prefs), - State#s{prefs = NewPrefs}. +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), + gmc_con:drop_wallet(Path, Delete); + ?wxID_CANCEL -> + ok + end, + State. diff --git a/zomp.meta b/zomp.meta index 57ba654..031ac42 100644 --- a/zomp.meta +++ b/zomp.meta @@ -5,12 +5,12 @@ {desc,"A desktop client for the Gajumaru network of blockchain networks"}. {author,"Craig Everett"}. {package_id,{"otpr","clutch",{0,1,0}}}. -{deps,[{"otpr","zj",{1,1,0}}, +{deps,[{"otpr","aeserialization",{0,1,2}}, + {"otpr","zj",{1,1,0}}, {"otpr","aesophia",{7,1,2}}, {"otpr","aebytecode",{3,2,1}}, {"otpr","hakuzaru",{0,1,0}}, {"otpr","erl_base58",{0,1,0}}, - {"otpr","aeserialization",{0,1,0}}, {"otpr","eblake2",{1,0,0}}, {"otpr","ec_utils",{1,0,0}}, {"otpr","zxwidgets",{1,0,1}}]}.