4
%% Copyright Ericsson AB 2009. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
18
%%%-------------------------------------------------------------------
19
%%% File : sud_board.erl
20
%%% Author : <dgud@erix.ericsson.se>
21
%%% Description : Manages the gui board
23
%%% Created : 9 Jan 2008 by <dgud@erix.ericsson.se>
24
%%%-------------------------------------------------------------------
25
-module(sudoku_board).
27
-export([new/1, setup_board/2, clear_board/1, left/1,
28
get_board_data/1,set_board_data/2,
29
set_butt/3, butt_correct/3,
32
init/1, handle_sync_event/3,
33
handle_event/2, handle_info/2, handle_call/3,
34
code_change/3, terminate/2]).
36
-include("sudoku.hrl").
38
-record(state, {win, parent, board=[], pen, fonts=[]}).
39
-record(sq, {key,val,correct=true,given=false}).
43
-behaviour(wx_object).
47
wx_object:start_link(?MODULE, [ParentObj, self()], []).
49
setup_board(Board, Init) ->
50
wx_object:call(Board, {setup_board, Init}).
53
wx_object:call(Board, clear_board).
55
butt_correct(Board, Key, Correct) ->
56
wx_object:call(Board, {butt_correct, Key, Correct}).
58
set_butt(Board, Indx, Val) when is_integer(Indx) ->
59
{R,C,_} = sudoku_game:rcm(Indx),
60
set_butt(Board, {R,C}, Val);
61
set_butt(Board, Id, Val) ->
62
wx_object:call(Board, {set_butt, Id, Val}).
65
wx_object:call(Board, left).
67
get_board_data(Board) ->
68
wx_object:call(Board, get_board_data).
69
set_board_data(Board, List) ->
70
wx_object:call(Board, {set_board_data, List}).
73
draw(Board, DC, Size) ->
74
wx_object:call(Board, {draw, DC, Size}).
77
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79
init([ParentObj, ParentPid]) ->
80
Win = wxWindow:new(ParentObj, ?wxID_ANY, [{style, ?wxFULL_REPAINT_ON_RESIZE}]),
81
wxWindow:setFocus(Win), %% Get keyboard focus
82
wxWindow:setSizeHints(Win, {250,250}),
83
wxWindow:connect(Win, paint, [callback]),
84
wxWindow:connect(Win, size, []),
85
wxWindow:connect(Win, erase_background, []),
86
wxWindow:connect(Win, key_up, [{skip, true}]),
87
wxWindow:connect(Win, left_down, [{skip, true}]),
88
wxWindow:connect(Win, enter_window, [{skip, true}]),
90
%% Init pens and fonts
91
Pen = wxPen:new({0,0,0}, [{width, 3}]),
92
Fs0 = [{Sz,wxFont:new(Sz, ?wxSWISS, ?wxNORMAL, ?wxNORMAL,[])} ||
93
Sz <- [8,9,10,11,12,13,14,16,18,20,22,24,26,28,30,34,38,42,44,46]],
94
TestDC = wxClientDC:new(Win),
95
CW = fun({Sz,Font},Acc) ->
96
case wxFont:ok(Font) of
98
wxDC:setFont(TestDC, Font),
99
CH = wxDC:getCharHeight(TestDC),
100
[{CH,Sz,Font} | Acc];
105
Fs = lists:foldl(CW, [], Fs0),
106
wxClientDC:destroy(TestDC),
107
{Win, #state{win=Win, board=[], pen=Pen, fonts=Fs, parent=ParentPid}}.
109
handle_sync_event(#wx{event=#wxPaint{}}, _Obj, State = #state{win=Win}) ->
110
%% io:format("EPaint~n",[]),
111
Size = wxWindow:getSize(Win),
112
DC = wxPaintDC:new(Win),
113
wxDC:destroyClippingRegion(DC),
114
redraw(DC,Size,State),
115
wxPaintDC:destroy(DC),
116
%%io:format("...EPaint~n",[]),
119
handle_event(#wx{event=#wxMouse{type=enter_window}}, State = #state{win=Win}) ->
120
wxWindow:setFocus(Win), %% Get keyboard focus
122
handle_event(#wx{event=#wxKey{keyCode=KeyC, x=X,y=Y}},
123
S = #state{parent=Pid, win=Win}) ->
124
Val = if KeyC > 47, KeyC < 58 -> KeyC - $0;
125
KeyC > 325, KeyC < 336 -> KeyC - 326; %% NUM LOCK
128
case get_butt(X,Y,S) of
129
error -> %% Mac don't get correct coordinates.
130
Global = wx_misc:getMousePosition(),
131
{CX,CY} = wxWindow:screenToClient(Win, Global),
132
case get_butt(CX,CY,S) of
134
Id -> Pid ! {set_val,Id,Val}
137
Pid ! {set_val,Id,Val}
140
handle_event(#wx{event=#wxMouse{type=left_down,x=X,y=Y}},
141
S = #state{parent=Gui, win=F}) ->
142
Id = get_butt(X,Y,S),
145
_ -> create_popup_menu(Gui,Id,X,Y,F)
148
handle_event(#wx{event=#wxSize{}}, State) ->
151
handle_event(_Ev, State) ->
156
handle_call({set_butt, Key, 0},_From,S0=#state{board=B0}) -> %% Reset
157
B = lists:keydelete(Key,2,B0),
158
S = S0#state{board=B},
162
handle_call({set_butt, Key, Val},_From,S0=#state{board=B0}) ->
163
case lists:keysearch(Key,2,B0) of
165
B = lists:keyreplace(Key, 2, B0, #sq{key=Key,val=Val});
167
B = [#sq{key=Key, val=Val}|B0]
169
S = S0#state{board=B},
173
handle_call({butt_correct, Key, Correct},_From, S0=#state{board=B0}) ->
174
case lists:keysearch(Key,2,B0) of
176
B = lists:keyreplace(Key, 2, B0, Butt#sq{key=Key,correct=Correct});
180
S = S0#state{board=B},
184
handle_call({setup_board, Init},_From, State) ->
185
B = [#sq{given=true, correct=true, key=Key, val=Val} || {Key,Val} <- Init],
186
S = State#state{board=B},
190
handle_call(clear_board,_From, State = #state{board=B0}) ->
191
B = [Butt || Butt = #sq{given=true} <- B0],
192
S = State#state{board=B},
194
Given = [{Key, Val} || #sq{key=Key,val=Val,given=true} <- B],
196
handle_call(get_board_data,_From, S=#state{board=B0}) ->
198
handle_call({set_board_data, B},_From, S0) ->
199
S = S0#state{board=B},
201
G1 = [{Key, Val} || #sq{key=Key,val=Val,given=true} <- B],
202
G2 = [{Key, Val} || #sq{key=Key,val=Val,given=false,correct=true} <- B],
203
G3 = [{Key, Val} || #sq{key=Key,val=Val,given=false,correct=false} <- B],
204
{reply, G1 ++ G2 ++ G3, S};
205
handle_call(left,_From, S = #state{board=B}) ->
206
Res = 81 - length([ok || #sq{correct=C} <- B, C /= false]),
208
handle_call({draw, DC, Size},_From, S) ->
212
code_change(_, _, State) ->
213
{stop, not_yet_implemented, State}.
215
handle_info(Msg, State) ->
216
{stop, {info, Msg}, State}.
218
terminate(_Reason, _State) ->
221
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
226
get_butt(X, Y, #state{win=Win}) ->
227
{W0,H0} = wxWindow:getSize(Win),
228
BoxSz = getGeomSz(W0,H0),
229
%% io:format("~p ~p ~p ~p~n", [{X,Y}, {W0,H0}, BoxSz, calc_pos(X-?BRD,Y-?BRD, BoxSz)]),
230
case calc_pos(X-?BRD,Y-?BRD, BoxSz) of
231
Pos = {R,C} when 0 < R, R < 10, 0 < C, C < 10 -> Pos;
235
calc_pos(X,Y, BoxSz) ->
236
{1+(Y*3 div BoxSz), 1+(X*3 div BoxSz)}.
238
redraw(S = #state{win=Win}) ->
239
DC0 = wxClientDC:new(Win),
240
DC = wxBufferedDC:new(DC0),
241
Size = wxWindow:getSize(Win),
243
wxBufferedDC:destroy(DC),
244
wxClientDC:destroy(DC0),
247
redraw(DC, Size, S) ->
249
wxDC:setBackground(DC, ?wxWHITE_BRUSH),
251
BoxSz = draw_board(DC,Size,S),
252
F = sel_font(BoxSz div 3,S#state.fonts),
253
[draw_number(DC,F,BoxSz,Sq) || Sq <- S#state.board]
256
sel_font(_BS,[{_H,_Sz,F}]) ->
257
%% io:format("Font sz ~p height ~p in BS ~p~n",[_Sz,_H, _BS]),
259
sel_font(BS,[{H,_Sz,F}|_]) when BS > (H + 6) ->
260
%% io:format("Font sz ~p height ~p in BS ~p~n",[_Sz,H, BS]),
262
sel_font(BS,[_|Fs]) ->
265
draw_number(DC,F,Sz,#sq{key={R,C},val=Num,given=Bold,correct=Correct}) ->
266
{X,Y} = get_coords(Sz,R-1,C-1),
269
wxFont:setWeight(F,?wxBOLD),
270
wxDC:setTextForeground(DC,{0,0,0});
272
wxFont:setWeight(F,?wxNORMAL),
273
wxDC:setTextForeground(DC,{255,40,40,255});
275
wxFont:setWeight(F,?wxNORMAL),
276
wxDC:setTextForeground(DC,{50,50,100,255})
279
CH = (TBox - wxDC:getCharHeight(DC)) div 2,
280
CW = (TBox - wxDC:getCharWidth(DC)) div 2,
281
wxDC:drawText(DC, integer_to_list(Num), {X+CW,Y+CH+1}),
284
get_coords(Sz,R,C) ->
290
{?BRD + C1*Sz + C2*TBox,
291
?BRD + R1*Sz + R2*TBox}.
293
draw_board(DC,{W0,H0},#state{pen=Pen}) ->
294
BoxSz = getGeomSz(W0,H0),
297
wxPen:setWidth(Pen, 3),
298
wxPen:setColour(Pen, {0,0,0}),
301
wxDC:drawRoundedRectangle(DC, {?BRD,?BRD,3*BoxSz+1,3*BoxSz+1},
304
wxDC:drawLines(DC, [{?BRD+BoxSz, ?BRD}, {?BRD+BoxSz, BS}]),
305
wxDC:drawLine(DC, {?BRD+BoxSz*2, ?BRD}, {?BRD+BoxSz*2, BS}),
306
wxDC:drawLine(DC, {?BRD, ?BRD+BoxSz}, {BS, ?BRD+BoxSz}),
307
wxDC:drawLine(DC, {?BRD, ?BRD+BoxSz*2}, {BS, ?BRD+BoxSz*2}),
310
wxPen:setWidth(Pen, 1),
313
wxDC:drawLine(DC, {?BRD+TBox, ?BRD}, {?BRD+TBox, BS}),
314
wxDC:drawLine(DC, {?BRD+TBox*2, ?BRD}, {?BRD+TBox*2, BS}),
315
wxDC:drawLine(DC, {?BRD+TBox+BoxSz, ?BRD}, {?BRD+TBox+BoxSz, BS}),
316
wxDC:drawLine(DC, {?BRD+TBox*2+BoxSz, ?BRD}, {?BRD+TBox*2+BoxSz, BS}),
317
wxDC:drawLine(DC, {?BRD+TBox+BoxSz*2, ?BRD}, {?BRD+TBox+BoxSz*2, BS}),
318
wxDC:drawLine(DC, {?BRD+TBox*2+BoxSz*2, ?BRD}, {?BRD+TBox*2+BoxSz*2, BS}),
320
wxDC:drawLine(DC, {?BRD, ?BRD+TBox}, {BS, ?BRD+TBox}),
321
wxDC:drawLine(DC, {?BRD, ?BRD+TBox*2}, {BS, ?BRD+TBox*2}),
322
wxDC:drawLine(DC, {?BRD, ?BRD+TBox+BoxSz}, {BS, ?BRD+TBox+BoxSz}),
323
wxDC:drawLine(DC, {?BRD, ?BRD+TBox*2+BoxSz}, {BS, ?BRD+TBox*2+BoxSz}),
324
wxDC:drawLine(DC, {?BRD, ?BRD+TBox+BoxSz*2}, {BS, ?BRD+TBox+BoxSz*2}),
325
wxDC:drawLine(DC, {?BRD, ?BRD+TBox*2+BoxSz*2}, {BS, ?BRD+TBox*2+BoxSz*2}),
329
Small = if W < H -> W; true -> H end,
330
(Small - 2*?BRD) div 3.
335
create_popup_menu(GFX,Butt,X,Y,Frame) ->
337
spawn_link(fun() -> create_popup_menu1(GFX,Butt,Port,X,Y,Frame) end).
339
create_popup_menu1(GFX,Butt,Port,X,Y,Frame) ->
341
PopupMenu = wxMenu:new(),
342
create_popup_menu2(1, PopupMenu),
344
wxEvtHandler:connect(PopupMenu, command_menu_selected),
345
wxWindow:popupMenu(Frame,PopupMenu,X,Y),
347
#wx{event=#wxCommand{type=command_menu_selected},id=What} ->
348
GFX ! {set_val,Butt,What}
351
create_popup_menu2(N,PP) when N > 9 ->
352
wxMenu:append(PP, 0, "Clear");
353
create_popup_menu2(N,PP) ->
354
wxMenu:append(PP, N,integer_to_list(N)),
355
create_popup_menu2(N+1,PP).