%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2009-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %%%------------------------------------------------------------------- %%% File : sud_board.erl %%% Author : %%% Description : Manages the gui board %%% %%% Created : 9 Jan 2008 by %%%------------------------------------------------------------------- -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, get_state/1, redraw/3, %% Callbacks init/1, handle_sync_event/3, handle_event/2, handle_info/2, handle_call/3, handle_cast/2, 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}). get_state(Board) -> wx_object:call(Board, get_state). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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 = wxMemoryDC:new(), Bitmap = wxBitmap:new(256,256), wxMemoryDC:selectObject(TestDC, Bitmap), true = wxDC:isOk(TestDC), 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), wxMemoryDC: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}}, 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, 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, {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(get_state, _From, S) -> {reply, {ok,S}, S}. handle_cast(Msg, State) -> io:format("Got cast ~p~n",[Msg]), {noreply,State}. 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).