lis

%%%-------------------------------------------------------------------
%%% @author Jack Tang <[email protected]>
%%% @copyright (C) 2014, Jack Tang
%%% @doc
%%%
%%% Conf = [{height, 100}, {width, 200}, {stripe_rows, true}, {id_field, uid}],
%%% ListView = ux_list_view:new(Parent, Conf),
%%% ux_list_view:columns(ListView,
%%%                      [ #col_def{text = "第一列", data_index = uid, sortable = true, align = right},
%%%                        #col_def{text = "姓名", data_index = uname} ]),
%%% ux_list_view:tbar([Item1, {Item2, left}, {Item3, right}]),
%%% ux_list_view:bbar([Item1, Item2]),
%%% ux_list_view:reg_handler s([{E1, Fun1}, {E2, Fun2}]),
%%% ux_list_view:load_data(ListView, Data),
%%% ux_list_view:update(ListView).
%%%
%%% @end
%%% Created :  6 Nov 2014 by Jack Tang <[email protected]>
%%%-------------------------------------------------------------------
-module(ux_list_view).

-behaviour(wx_object).
%% API
-export([new/2,
         columns/2,
         tbar/2,
         bbar/2]).
-export([reg_handlers/2,
         load_data/2,
         update/1,
         clear/1,
         destroy/1]).

-export([init/1,
         terminate/2,
         code_change/3,
         handle_info/2,
         handle_call/3,
         handle_cast/2,
         handle_event/2]).

-include_lib("wx/include/wx.hrl").
-include("wxclient.hrl").

-record(state, { parent,
                 panel,
                 config,
                 columns,
                 id_field,
                 header_pos,
                 list_view,
                 tbar,
                 bbar,
                 callbacks,
                 store }).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% ux_list_view:new(Parent, [{items, Items},{id, ?wxID_ANY},
%%                           {height, 550}, {width, 600},
%%                           {stripe_rows, true}, {style, ?wxLC_REPORT bor ?wxLC_SINGLE_SEL}]),
%% @spec
%% @end
%%--------------------------------------------------------------------
new(Parent, Config) ->
    NConfig = tuple_list_utils:update({parent, Parent}, Config),
    start(NConfig).

start(Config) ->
    wx_object:start_link(?MODULE, Config, []).

tbar(This, Items) ->
    wx_object:call(This, {tbar, Items}).

bbar(This, Items) ->
    wx_object:call(This, {bbar, Items}).

reg_handlers(This, EventAndHandlers) ->
    wx_object:call(This, {reg_handlers, EventAndHandlers}).

row_value(This, RowId) ->
    wx_object:call(This, {row_value, RowId}).
%%--------------------------------------------------------------------
%% @doc
%% Columns =
%% [#col_def{text = "第1列", data_index =  currency, sortable = false, align = right},
%%  #col_def{text = "第2列", data_index = uid,       sortable = false, align = right},
%%  #col_def{text = "第3列", data_index = instrument_id, sortable = false, align = right}],
%% ux_list_view:columns(ListView, Columns),
%% @spec
%% @end
%%--------------------------------------------------------------------
columns(This, Columns) ->
    wx_object:call(This, {columns, Columns}).

%%--------------------------------------------------------------------
%% @doc
%% ux_list_view:load_data(Report, Data),
%% @spec
%% @end
%%--------------------------------------------------------------------
load_data(This, Data) ->
    wx_object:call(This, {load_data, Data}).

%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------
update(This) ->
    wx_object:call(This, update).

%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------
clear(This) ->
    wx_object:call(This, clear).

%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------
destroy(This) ->
    wx_object:call(This, destroy).

%%--------------------------------------------------------------------
%% @doc
%% @spec
%% @end
%%--------------------------------------------------------------------
init(Config) ->
    wx:batch(fun() -> do_init(Config) end).

handle_event(#wx{id = Id,  event = Event}, #state{store = Store} = State) ->
    #state{callbacks = Callbacks, list_view = ListView} = State,

    EventSrc = case Event of
                   #wxCommand{type = Type}  -> {Id, Type};
                   #wxList{type = Type}     -> {Id, Type};
                   _                        -> not_supported
               end,
    case maps:get(EventSrc, Callbacks, undefined) of
        undefined -> ok;
        Fun ->
            Selections = selections(ListView, Store),  
            Fun(Selections)       
    end,
    {noreply, State}.

handle_call({tbar, TbarItems}, _From, #state{panel = Panel} = State) ->
    Tbar = create_bar(Panel, TbarItems),
    {reply, ok, State#state{tbar = Tbar}};

handle_call({bbar, BbarItems}, _From, #state{panel = Panel} = State) ->
    Bbar = create_bar(Panel, BbarItems),
    {reply, ok, State#state{bbar = Bbar}};

handle_call({row_value, RowId}, _From, #state{store = Store} = State) ->
    Reply = maps:get(RowId, Store, undefined),
    {reply, Reply, State};

handle_call({reg_handlers, EventHandlerMappings}, _From, #state{} = State) ->
    Callbacks = lists:foldl(fun({Event, Fun},  Map) ->
                                    maps:put(Event, Fun, Map)
                            end, #{}, EventHandlerMappings),
    {reply, ok, State#state{callbacks = Callbacks}};

handle_call(update, _From, #state{panel = Panel} = State) ->
    #state{tbar = Tbar, bbar = Bbar, list_view = ListView} = State,
    MainSizer = wxBoxSizer:new(?wxVERTICAL),
    case Tbar of
        undefined -> ok;
        _         -> wxSizer:add(MainSizer, Tbar,  [{flag, ?wxEXPAND}])
    end,

    wxSizer:add(MainSizer, ListView, [{flag, ?wxEXPAND}, {proportion, 1}]),

    case Bbar of
        undefined -> ok;
        _         -> wxSizer:add(MainSizer, Bbar,  [{flag, ?wxEXPAND}])
    end,
    wxPanel:setSizer(Panel, MainSizer),
    wxSizer:fit(MainSizer, Panel),
    {reply, ok, State};

handle_call({columns, Columns}, _From, State) ->
    #state{list_view = ListView, id_field = IdField} = State,
    HeaderPos = set_headers(ListView, Columns, IdField),
    {reply, ok,  State#state{columns = Columns, header_pos = HeaderPos}};

handle_call({load_data, Data}, _From, #state{store = Store} = State) ->
    #state{header_pos = HeaderPos, list_view = ListView,
           id_field = IdField, store = Store} = State,
    [HeaderPosMap, NStore] = insert_rows(ListView, HeaderPos, Data, IdField, Store),
    {reply, ok, State#state{header_pos = HeaderPosMap, store = NStore}};       

handle_call(clear, _From, #state{list_view = ListView } = State) ->
    wxListCtrl:clearAll(ListView),
    wxListCtrl:refresh(ListView),
    {reply, ok, State#state{header_pos = undefined}};

handle_call(destroy, _From, #state{} = State) ->
    {stop, normal, ok, State};

handle_call(_Req, _From, State) ->
    lager:error("Can't handle request: ~p", [_Req]),
    {reply, {error, invalid_request},State}.

handle_cast(_Msg, State) ->
    lager:error("Can't handle msg: ~p", [_Msg]),
    {noreply, State}.

handle_info(_Info, State) ->
    lager:error("Can't handle info: ~p", [_Info]),
    {noreply, State}.

code_change(_, _, State) ->
    {stop, ignore, State}.

terminate(_Reason, _State) ->
    ok.

%%%===================================================================
%%% Internal functions
%%%===================================================================
do_init(Config) ->
    {_, Parent} = tuple_list_utils:keyfind(parent, wx:null(), Config),
    {_, IdField} = tuple_list_utils:keyfind(id_field, id, Config),
   
    Style = tuple_list_utils:keyfind(style,  ?wxLC_REPORT, Config),
    Panel = wxPanel:new(Parent, [{size, wxWindow:getSize(Parent)}, {style, ?wxEXPAND}]),
    ListView = wxListCtrl:new(Panel, [Style]),
    % add listener
    wxListCtrl:connect(ListView, command_list_item_selected, []),
    process_flag(trap_exit, true),
    {Panel, #state{parent = Parent,     config = Config,
                   id_field = IdField,   panel = Panel,
                   list_view = ListView, store = maps:new()}}.

insert_rows(ListView, undefined, [FirstRow | _] = Data, IdField, Store) ->
    HeaderPos =
        case FirstRow of
            {struct, RowData} when is_list(RowData)->
                set_headers(ListView, RowData, IdField);
            {obj, RowData} when is_list(RowData)->
                set_headers(ListView, RowData, IdField);
            Row when is_list(Row)  ->
                set_headers(ListView, Row, IdField);
            _Row  ->
                #{}
        end,
    insert_rows(ListView, HeaderPos, Data, IdField, Store);

insert_rows(ListView, HeaderPos, Data, IdField, Store) ->
    LastRowNo =  wxListCtrl:getItemCount(ListView),
    [_, NDStore2] =
        lists:foldl(
          fun(Row, [RIndex, DStore]) ->
                  % fill up cells
                  [RIndex2, RData] = case Row of
                                         {struct, RowData} when is_list(RowData)->
                                             insert_one_row(ListView, HeaderPos, RIndex, RowData),
                                             [RIndex + 1, RowData];
                                         {obj, RowData} when is_list(RowData)->
                                             insert_one_row(ListView, HeaderPos, RIndex, RowData),
                                             [RIndex + 1, RowData];
                                         Row when is_list(Row)  ->
                                             insert_one_row(ListView, HeaderPos, RIndex, Row),
                                             [RIndex + 1, Row];
                                         _Row  ->
                                             [RIndex, undefined]
                                     end,
                  NDStore = case RData of
                                undefined -> DStore;
                                _ ->
                                    case tuple_list_utils:keyfind(IdField, RData) of
                                        {_, undefined} -> DStore;
                                        {_, IdVal}     ->
                                            IdValStr = type_utils:any_to_list(IdVal),
                                            maps:put(IdValStr, RData, DStore)
                                    end
                            end,
                  [RIndex2, NDStore]
          end, [LastRowNo, Store], Data),
    [HeaderPos, NDStore2].

create_bar(Panel, Items) ->
    ItemNumber = length(lists:flatten(Items)),
    LeftSizer  = wxFlexGridSizer:new(1, ItemNumber, 0, 0), 
    RightSizer = wxFlexGridSizer:new(1, ItemNumber, 0, 0),
    lists:foreach(
      fun(Item) ->
              Wx2 = case Item of
                        {Wx, left} ->
                            wxWindow:reparent(Wx, Panel),
                            wxSizer:add(LeftSizer, Wx, [{proportion, 0}, {flag, ?wxALIGN_CENTER}]),
                            Wx;
                        {Wx, right} ->
                            wxWindow:reparent(Wx, Panel),
                            wxSizer:add(RightSizer, Wx, [{proportion, 0}, {flag, ?wxALIGN_CENTER}]),
                            Wx;
                        Wx ->
                            wxWindow:reparent(Wx, Panel),
                            wxSizer:add(LeftSizer, Wx, [{proportion, 0}, {flag, ?wxALIGN_CENTER}]),
                            Wx
                    end,
              wxWindow:connect(Wx2, command_button_clicked)
      end, Items),
    MainSizer = wxBoxSizer:new(?wxHORIZONTAL),
    wxSizer:add(MainSizer, LeftSizer),
    wxSizer:add(MainSizer, 60 ,20, [{proportion, 1}, {flag, ?wxEXPAND}]),
    wxSizer:add(MainSizer, RightSizer),
    MainSizer.

set_headers(Report, Columns, IdField) ->
    HeaderPosMap  = maps:new(),
    % setup id cell
    IdHeaderCell = set_header_cell([{width, 0}, {text, "ID"}]),
    wxListCtrl:insertColumn(Report, 0, IdHeaderCell),
    NHeaderPosMap = map_utils:append(IdField, [0], HeaderPosMap),

    [HeaderPosMap2, _] =
        lists:foldl(
          fun(Column, [HeaderPos, Index]) ->
                  [HeaderCol, DIndex] =
                      case Column of
                          #col_def{} ->
                              #col_def{text = Text, data_index = DataIndex,
                                       sortable = Sortable, align = Align,
                                       width = Width} = Column,
                              % validate DataIndex, it should exist!
                              [set_header_cell([{align, Align}, {width, Width}, {text, Text}]),
                               DataIndex];
                          {Field, _} ->
                              [set_header_cell([{text, type_utils:any_to_list(Field)}]), Field]
                      end,
                  wxListCtrl:insertColumn(Report, Index, HeaderCol),
                  [map_utils:append(DIndex, [Index], HeaderPos), Index + 1]   
          end, [NHeaderPosMap, 1], Columns),
    HeaderPosMap2.

set_header_cell(Props) ->
    HeaderCol = wxListItem:new(),
    {align, Align} = tuple_list_utils:keyfind(align, ?wxLIST_FORMAT_LEFT, Props),
    {text, Text} = tuple_list_utils:keyfind(text, "", Props),
    wxListItem:setAlign(HeaderCol, Align),
    % case tuple_list_utils:keyfind(width, undefined, Props) of
    case tuple_list_utils:keyfind(width, 50, Props) of
        {_, undefined} ->
            ok;
        {_, Width} ->
            wxListItem:setWidth(HeaderCol, Width)
    end,
   
    wxListItem:setText(HeaderCol, Text),
    HeaderCol.

insert_one_row(ListView, HeaderPos, RIndex, RowData) ->
    % wxListCtrl:insertItem(ListView, RIndex),
    wxListCtrl:insertItem(ListView, RIndex, ""),
    maps:fold(fun(DIndex, CIndexList, Acc) ->
                      lists:foreach(
                        fun(CIndex) ->
                                {_, Val} = tuple_list_utils:keyfind(DIndex, "N/A", RowData),
                                Cell = wxListItem:new(),
                                wxListItem:setId(Cell, RIndex),
                                wxListItem:setColumn(Cell, CIndex),
                                wxListItem:setText(Cell, type_utils:any_to_list(Val)),
                                wxListCtrl:setItem(ListView, Cell)
                        end, CIndexList)
              end, [], HeaderPos).

selections(ListView, Store) ->
    SelectedCount = wxListCtrl:getSelectedItemCount(ListView),
    Cell = wxListItem:new(),
   
    {Selections, _LastSelected} =
        lists:foldl(
          fun(_Seq, {Sel, NextItem}) ->
                  NextSelectedItem = wxListCtrl:getNextItem(ListView, NextItem,
                                                            [{state, ?wxLIST_STATE_SELECTED}]),
                  wxListItem:setId(Cell, NextSelectedItem),
                  wxListCtrl:getItem(ListView, Cell),
                  SelectedId = wxListItem:getText(Cell),
                  Selected = maps:get(SelectedId, Store, undefined),
                  {lists:append(Sel, [Selected]), NextSelectedItem}
          end, {[], -1}, lists:seq(1, SelectedCount)),
    Selections.
   

猜你喜欢

转载自870720136.iteye.com/blog/2169331
lis