aboutsummaryrefslogtreecommitdiffstats
path: root/lib/wx/examples/sudoku/sudoku_board.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/wx/examples/sudoku/sudoku_board.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/wx/examples/sudoku/sudoku_board.erl')
-rwxr-xr-xlib/wx/examples/sudoku/sudoku_board.erl358
1 files changed, 358 insertions, 0 deletions
diff --git a/lib/wx/examples/sudoku/sudoku_board.erl b/lib/wx/examples/sudoku/sudoku_board.erl
new file mode 100755
index 0000000000..756837582f
--- /dev/null
+++ b/lib/wx/examples/sudoku/sudoku_board.erl
@@ -0,0 +1,358 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%%-------------------------------------------------------------------
+%%% File : sud_board.erl
+%%% Author : <[email protected]>
+%%% Description : Manages the gui board
+%%%
+%%% Created : 9 Jan 2008 by <[email protected]>
+%%%-------------------------------------------------------------------
+-module(sudoku_board).
+
+-export([new/1, setup_board/2, clear_board/1, left/1,
+ get_board_data/1,set_board_data/2,
+ set_butt/3, butt_correct/3,
+ draw/3,
+ %% Callbacks
+ init/1, handle_sync_event/3,
+ handle_event/2, handle_info/2, handle_call/3,
+ code_change/3, terminate/2]).
+
+-include("sudoku.hrl").
+
+-record(state, {win, parent, board=[], pen, fonts=[]}).
+-record(sq, {key,val,correct=true,given=false}).
+-define(BRD,10).
+-define(ARC_R, 10).
+
+-behaviour(wx_object).
+
+%% API
+new(ParentObj) ->
+ wx_object:start_link(?MODULE, [ParentObj, self()], []).
+
+setup_board(Board, Init) ->
+ wx_object:call(Board, {setup_board, Init}).
+
+clear_board(Board) ->
+ wx_object:call(Board, clear_board).
+
+butt_correct(Board, Key, Correct) ->
+ wx_object:call(Board, {butt_correct, Key, Correct}).
+
+set_butt(Board, Indx, Val) when is_integer(Indx) ->
+ {R,C,_} = sudoku_game:rcm(Indx),
+ set_butt(Board, {R,C}, Val);
+set_butt(Board, Id, Val) ->
+ wx_object:call(Board, {set_butt, Id, Val}).
+
+left(Board) ->
+ wx_object:call(Board, left).
+
+get_board_data(Board) ->
+ wx_object:call(Board, get_board_data).
+set_board_data(Board, List) ->
+ wx_object:call(Board, {set_board_data, List}).
+
+
+draw(Board, DC, Size) ->
+ wx_object:call(Board, {draw, DC, Size}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init([ParentObj, ParentPid]) ->
+ Win = wxPanel:new(ParentObj, [{style, ?wxFULL_REPAINT_ON_RESIZE}]),
+ wxWindow:setFocus(Win), %% Get keyboard focus
+ wxWindow:setSizeHints(Win, {250,250}),
+ wxWindow:connect(Win, paint, [callback]),
+ wxWindow:connect(Win, size, []),
+ wxWindow:connect(Win, erase_background, []),
+ wxWindow:connect(Win, key_up, [{skip, true}]),
+ wxWindow:connect(Win, left_down, [{skip, true}]),
+ wxWindow:connect(Win, enter_window, [{skip, true}]),
+
+ %% Init pens and fonts
+ Pen = wxPen:new({0,0,0}, [{width, 3}]),
+ Fs0 = [{Sz,wxFont:new(Sz, ?wxSWISS, ?wxNORMAL, ?wxNORMAL,[])} ||
+ Sz <- [8,9,10,11,12,13,14,16,18,20,22,24,26,28,30,34,38,42,44,46]],
+ TestDC = wxClientDC:new(Win),
+ CW = fun({Sz,Font},Acc) ->
+ case wxFont:ok(Font) of
+ true ->
+ wxDC:setFont(TestDC, Font),
+ CH = wxDC:getCharHeight(TestDC),
+ [{CH,Sz,Font} | Acc];
+ false ->
+ Acc
+ end
+ end,
+ Fs = lists:foldl(CW, [], Fs0),
+ wxClientDC:destroy(TestDC),
+ {Win, #state{win=Win, board=[], pen=Pen, fonts=Fs, parent=ParentPid}}.
+
+handle_sync_event(#wx{event=#wxPaint{}}, _Obj, State = #state{win=Win}) ->
+ %% io:format("EPaint~n",[]),
+ Size = wxWindow:getSize(Win),
+ DC = wxPaintDC:new(Win),
+ wxDC:destroyClippingRegion(DC),
+ redraw(DC,Size,State),
+ wxPaintDC:destroy(DC),
+ %%io:format("...EPaint~n",[]),
+ ok.
+
+handle_event(#wx{event=#wxMouse{type=enter_window}}, State = #state{win=Win}) ->
+ wxWindow:setFocus(Win), %% Get keyboard focus
+ {noreply,State};
+handle_event(#wx{event=#wxKey{keyCode=KeyC, x=X,y=Y}},
+ S = #state{parent=Pid, win=Win}) ->
+ Val = if KeyC > 47, KeyC < 58 -> KeyC - $0;
+ KeyC > 325, KeyC < 336 -> KeyC - 326; %% NUM LOCK
+ true -> 0
+ end,
+ case get_butt(X,Y,S) of
+ error -> %% Mac don't get correct coordinates.
+ Global = wx_misc:getMousePosition(),
+ {CX,CY} = wxWindow:screenToClient(Win, Global),
+ case get_butt(CX,CY,S) of
+ error -> ignore;
+ Id -> Pid ! {set_val,Id,Val}
+ end;
+ Id ->
+ Pid ! {set_val,Id,Val}
+ end,
+ {noreply, S};
+handle_event(#wx{event=#wxMouse{type=left_down,x=X,y=Y}},
+ S = #state{parent=Gui, win=F}) ->
+ Id = get_butt(X,Y,S),
+ case Id of
+ error -> ignore;
+ _ -> create_popup_menu(Gui,Id,X,Y,F)
+ end,
+ {noreply, S};
+handle_event(#wx{event=#wxSize{}}, State) ->
+ redraw(State),
+ {noreply,State};
+handle_event(_Ev, State) ->
+ {noreply,State}.
+
+%%%%%%%%%%%%%%%%%%%
+
+handle_call({set_butt, Key, 0},_From,S0=#state{board=B0}) -> %% Reset
+ B = lists:keydelete(Key,2,B0),
+ S = S0#state{board=B},
+ redraw(S),
+ {reply, ok, S};
+
+handle_call({set_butt, Key, Val},_From,S0=#state{board=B0}) ->
+ case lists:keysearch(Key,2,B0) of
+ {value, _} ->
+ B = lists:keyreplace(Key, 2, B0, #sq{key=Key,val=Val});
+ false ->
+ B = [#sq{key=Key, val=Val}|B0]
+ end,
+ S = S0#state{board=B},
+ redraw(S),
+ {reply, ok, S};
+
+handle_call({butt_correct, Key, Correct},_From, S0=#state{board=B0}) ->
+ case lists:keysearch(Key,2,B0) of
+ {value, Butt} ->
+ B = lists:keyreplace(Key, 2, B0, Butt#sq{key=Key,correct=Correct});
+ false ->
+ B = B0
+ end,
+ S = S0#state{board=B},
+ redraw(S),
+ {reply, ok, S};
+
+handle_call({setup_board, Init},_From, State) ->
+ B = [#sq{given=true, correct=true, key=Key, val=Val} || {Key,Val} <- Init],
+ S = State#state{board=B},
+ redraw(S),
+ {reply, ok, S};
+
+handle_call(clear_board,_From, State = #state{board=B0}) ->
+ B = [Butt || Butt = #sq{given=true} <- B0],
+ S = State#state{board=B},
+ redraw(S),
+ Given = [{Key, Val} || #sq{key=Key,val=Val,given=true} <- B],
+ {reply, Given, S};
+handle_call(get_board_data,_From, S=#state{board=B0}) ->
+ {reply, B0, S};
+handle_call({set_board_data, B},_From, S0) ->
+ S = S0#state{board=B},
+ redraw(S),
+ G1 = [{Key, Val} || #sq{key=Key,val=Val,given=true} <- B],
+ G2 = [{Key, Val} || #sq{key=Key,val=Val,given=false,correct=true} <- B],
+ G3 = [{Key, Val} || #sq{key=Key,val=Val,given=false,correct=false} <- B],
+ {reply, G1 ++ G2 ++ G3, S};
+handle_call(left,_From, S = #state{board=B}) ->
+ Res = 81 - length([ok || #sq{correct=C} <- B, C /= false]),
+ {reply, Res, S};
+handle_call({draw, DC, Size},_From, S) ->
+ redraw(DC,Size,S),
+ {reply, ok, S}.
+
+code_change(_, _, State) ->
+ {stop, not_yet_implemented, State}.
+
+handle_info(Msg, State) ->
+ {stop, {info, Msg}, State}.
+
+terminate(_Reason, _State) ->
+ normal.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_butt(X, Y, #state{win=Win}) ->
+ {W0,H0} = wxWindow:getSize(Win),
+ BoxSz = getGeomSz(W0,H0),
+ %% io:format("~p ~p ~p ~p~n", [{X,Y}, {W0,H0}, BoxSz, calc_pos(X-?BRD,Y-?BRD, BoxSz)]),
+ case calc_pos(X-?BRD,Y-?BRD, BoxSz) of
+ Pos = {R,C} when 0 < R, R < 10, 0 < C, C < 10 -> Pos;
+ _ -> error
+ end.
+
+calc_pos(X,Y, BoxSz) ->
+ {1+(Y*3 div BoxSz), 1+(X*3 div BoxSz)}.
+
+redraw(S = #state{win=Win}) ->
+ DC0 = wxClientDC:new(Win),
+ DC = wxBufferedDC:new(DC0),
+ Size = wxWindow:getSize(Win),
+ redraw(DC, Size, S),
+ wxBufferedDC:destroy(DC),
+ wxClientDC:destroy(DC0),
+ ok.
+
+redraw(DC, Size, S) ->
+ wx:batch(fun() ->
+ wxDC:setBackground(DC, ?wxWHITE_BRUSH),
+ wxDC:clear(DC),
+ BoxSz = draw_board(DC,Size,S),
+ F = sel_font(BoxSz div 3,S#state.fonts),
+ [draw_number(DC,F,BoxSz,Sq) || Sq <- S#state.board]
+ end).
+
+sel_font(_BS,[{_H,_Sz,F}]) ->
+ %% io:format("Font sz ~p height ~p in BS ~p~n",[_Sz,_H, _BS]),
+ F;
+sel_font(BS,[{H,_Sz,F}|_]) when BS > (H + 6) ->
+ %% io:format("Font sz ~p height ~p in BS ~p~n",[_Sz,H, BS]),
+ F;
+sel_font(BS,[_|Fs]) ->
+ sel_font(BS,Fs).
+
+draw_number(DC,F,Sz,#sq{key={R,C},val=Num,given=Bold,correct=Correct}) ->
+ {X,Y} = get_coords(Sz,R-1,C-1),
+ TBox = Sz div 3,
+ if Bold ->
+ wxFont:setWeight(F,?wxBOLD),
+ wxDC:setTextForeground(DC,{0,0,0});
+ Correct =:= false ->
+ wxFont:setWeight(F,?wxNORMAL),
+ wxDC:setTextForeground(DC,{255,40,40,255});
+ true ->
+ wxFont:setWeight(F,?wxNORMAL),
+ wxDC:setTextForeground(DC,{50,50,100,255})
+ end,
+ wxDC:setFont(DC,F),
+ CH = (TBox - wxDC:getCharHeight(DC)) div 2,
+ CW = (TBox - wxDC:getCharWidth(DC)) div 2,
+ wxDC:drawText(DC, integer_to_list(Num), {X+CW,Y+CH+1}),
+ ok.
+
+get_coords(Sz,R,C) ->
+ TBox = Sz div 3,
+ R1 = R div 3,
+ R2 = R rem 3,
+ C1 = C div 3,
+ C2 = C rem 3,
+ {?BRD + C1*Sz + C2*TBox,
+ ?BRD + R1*Sz + R2*TBox}.
+
+draw_board(DC,{W0,H0},#state{pen=Pen}) ->
+ BoxSz = getGeomSz(W0,H0),
+ BS = ?BRD+3*BoxSz,
+
+ wxPen:setWidth(Pen, 3),
+ wxPen:setColour(Pen, {0,0,0}),
+ wxDC:setPen(DC,Pen),
+
+ wxDC:drawRoundedRectangle(DC, {?BRD,?BRD,3*BoxSz+1,3*BoxSz+1},
+ float(?ARC_R)),
+ %% Testing DrawLines
+ wxDC:drawLines(DC, [{?BRD+BoxSz, ?BRD}, {?BRD+BoxSz, BS}]),
+ wxDC:drawLine(DC, {?BRD+BoxSz*2, ?BRD}, {?BRD+BoxSz*2, BS}),
+ wxDC:drawLine(DC, {?BRD, ?BRD+BoxSz}, {BS, ?BRD+BoxSz}),
+ wxDC:drawLine(DC, {?BRD, ?BRD+BoxSz*2}, {BS, ?BRD+BoxSz*2}),
+
+ %% Draw inside lines
+ wxPen:setWidth(Pen, 1),
+ wxDC:setPen(DC,Pen),
+ TBox = BoxSz div 3,
+ wxDC:drawLine(DC, {?BRD+TBox, ?BRD}, {?BRD+TBox, BS}),
+ wxDC:drawLine(DC, {?BRD+TBox*2, ?BRD}, {?BRD+TBox*2, BS}),
+ wxDC:drawLine(DC, {?BRD+TBox+BoxSz, ?BRD}, {?BRD+TBox+BoxSz, BS}),
+ wxDC:drawLine(DC, {?BRD+TBox*2+BoxSz, ?BRD}, {?BRD+TBox*2+BoxSz, BS}),
+ wxDC:drawLine(DC, {?BRD+TBox+BoxSz*2, ?BRD}, {?BRD+TBox+BoxSz*2, BS}),
+ wxDC:drawLine(DC, {?BRD+TBox*2+BoxSz*2, ?BRD}, {?BRD+TBox*2+BoxSz*2, BS}),
+ %% Vert
+ wxDC:drawLine(DC, {?BRD, ?BRD+TBox}, {BS, ?BRD+TBox}),
+ wxDC:drawLine(DC, {?BRD, ?BRD+TBox*2}, {BS, ?BRD+TBox*2}),
+ wxDC:drawLine(DC, {?BRD, ?BRD+TBox+BoxSz}, {BS, ?BRD+TBox+BoxSz}),
+ wxDC:drawLine(DC, {?BRD, ?BRD+TBox*2+BoxSz}, {BS, ?BRD+TBox*2+BoxSz}),
+ wxDC:drawLine(DC, {?BRD, ?BRD+TBox+BoxSz*2}, {BS, ?BRD+TBox+BoxSz*2}),
+ wxDC:drawLine(DC, {?BRD, ?BRD+TBox*2+BoxSz*2}, {BS, ?BRD+TBox*2+BoxSz*2}),
+ BoxSz.
+
+getGeomSz(W,H) ->
+ Small = if W < H -> W; true -> H end,
+ (Small - 2*?BRD) div 3.
+
+
+%% popupmenu
+
+create_popup_menu(GFX,Butt,X,Y,Frame) ->
+ Port = wx:get_env(),
+ spawn_link(fun() -> create_popup_menu1(GFX,Butt,Port,X,Y,Frame) end).
+
+create_popup_menu1(GFX,Butt,Port,X,Y,Frame) ->
+ wx:set_env(Port),
+ PopupMenu = wxMenu:new(),
+ create_popup_menu2(1, PopupMenu),
+
+ wxEvtHandler:connect(PopupMenu, command_menu_selected),
+ wxWindow:popupMenu(Frame,PopupMenu,X,Y),
+ receive
+ #wx{event=#wxCommand{type=command_menu_selected},id=10} ->
+ GFX ! {set_val,Butt,0};
+ #wx{event=#wxCommand{type=command_menu_selected},id=What} ->
+ GFX ! {set_val,Butt,What}
+ end.
+
+create_popup_menu2(N,PP) when N > 9 ->
+ wxMenu:append(PP, 10, "Clear");
+create_popup_menu2(N,PP) ->
+ wxMenu:append(PP, N,integer_to_list(N)),
+ create_popup_menu2(N+1,PP).
+