anchor.diff:      Statement = binary_to_term(base64:decode(proplists:get_value(<<"statement">>, BodyJson, <<>>))),
       SrtSpc = proplists:get_value(<<"spec">>, BodyJson, []),
       SortSpec = sort_json_to_term(SrtSpc),
  -    GuiResp = Statement:gui_req(sort, SortSpec),
  -    GuiRespJson = gen_adapter:gui_resp(GuiResp, Statement:get_columns()),
  -    ?Debug("sort ~p ~p", [SortSpec, GuiResp]),
  -    From ! {reply, jsx:encode([{<<"sort">>,GuiRespJson}])},
  +    Statement:gui_req(sort, SortSpec, gui_resp_cb_fun(<<"sort">>, Statement, From)),
       Priv;
   process_cmd({[<<"filter">>], ReqBody}, From, Priv) ->
       [{<<"filter">>,BodyJson}] = ReqBody,
       Statement = binary_to_term(base64:decode(proplists:get_value(<<"statement">>, BodyJson, <<>>))),
       FltrSpec = proplists:get_value(<<"spec">>, BodyJson, []),
       FilterSpec = filter_json_to_term(FltrSpec),
  -    GuiResp = Statement:gui_req(filter, FilterSpec),
  -    GuiRespJson = gen_adapter:gui_resp(GuiResp, Statement:get_columns()),
  -    ?Debug("filter ~p ~p", [FilterSpec, GuiResp]),
  -    From ! {reply, jsx:encode([{<<"filter">>,GuiRespJson}])},
  +    Statement:gui_req(filter, FilterSpec, gui_resp_cb_fun(<<"filter">>, Statement, From)),
       Priv;
   
   % gui button events
  
positive.code:     RowId = proplists:get_value(<<"rowid">>, BodyJson, <<>>),
      CellId = proplists:get_value(<<"cellid">>, BodyJson, <<>>),
      Value = binary_to_list(proplists:get_value(<<"value">>, BodyJson, <<>>)),
      GuiResp = Statement:gui_req(update, [{RowId,upd,[{CellId,Value}]}]),
      GuiRespJson = gen_adapter:gui_resp(GuiResp, Statement:get_columns()),
      ?Info("updated ~p", [GuiResp]),
      From ! {reply, jsx:encode([{<<"update_data">>, GuiRespJson}])},
      Priv;
  process_cmd({[<<"delete_row">>], ReqBody}, From, Priv) ->
      [{<<"delete_row">>,BodyJson}] = ReqBody,
      Statement = binary_to_term(base64:decode(proplists:get_value(<<"statement">>, BodyJson, <<>>))),
      RowId = proplists:get_value(<<"rowid">>, BodyJson, <<>>),
      GuiResp = Statement:gui_req(update, [{RowId,del,[]}]),
      GuiRespJson = gen_adapter:gui_resp(GuiResp, Statement:get_columns()),
      ?Info("deleted ~p", [GuiResp]),
      From ! {reply, jsx:encode([{<<"delete_row">>, GuiRespJson}])},
      Priv;
  process_cmd({[<<"insert_data">>], ReqBody}, From, Priv) ->
      [{<<"insert_data">>,BodyJson}] = ReqBody,
      Statement = binary_to_term(base64:decode(proplists:get_value(<<"statement">>, BodyJson, <<>>))),
      ClmName = binary_to_list(proplists:get_value(<<"col">>, BodyJson, <<>>)),
      Value =  binary_to_list(proplists:get_value(<<"value">>, BodyJson, <<>>)),
      GuiResp = Statement:gui_req(update, [{undefined,ins,[{ClmName,Value}]}]),
      GuiRespJson = gen_adapter:gui_resp(GuiResp, Statement:get_columns()),
      ?Info("inserted ~p", [GuiResp]),
      From ! {reply, jsx:encode([{<<"insert_data">>, GuiRespJson}])},
      Priv;
  
  % unsupported gui actions
  
negative.code:     Priv.
  
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%
  gui_resp_cb_fun(Cmd, Statement, From) ->
      fun(#gres{} = GuiResp) ->
          GuiRespJson = gen_adapter:gui_resp(GuiResp, Statement:get_columns()),
          ?Debug("resp ~p ~p", [Cmd, GuiResp]),
          From ! {reply, jsx:encode([{Cmd,GuiRespJson}])}
      end.
  
  sort_json_to_term([]) -> [];
  sort_json_to_term([[{C,T}|_]|Sorts]) ->
  