aboutsummaryrefslogtreecommitdiffstats
path: root/lib/wx/examples/sudoku/sudoku_gui.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/wx/examples/sudoku/sudoku_gui.erl')
-rwxr-xr-xlib/wx/examples/sudoku/sudoku_gui.erl391
1 files changed, 391 insertions, 0 deletions
diff --git a/lib/wx/examples/sudoku/sudoku_gui.erl b/lib/wx/examples/sudoku/sudoku_gui.erl
new file mode 100755
index 0000000000..4aaecfe086
--- /dev/null
+++ b/lib/wx/examples/sudoku/sudoku_gui.erl
@@ -0,0 +1,391 @@
+%%
+%% %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 : sudoku_gui.erl
+%%% Author : <[email protected]>
+%%% Description : The Gui and it's event loop
+%%%
+%%% Created : 9 Jan 2008 by <[email protected]>
+%%%-------------------------------------------------------------------
+-module(sudoku_gui).
+
+-export([init/1, handle_info/2, handle_call/3, handle_event/2,
+ terminate/2, code_change/3]).
+
+-compile(export_all).
+
+-behaviour(wx_object).
+
+-include("sudoku.hrl").
+
+-import(sudoku_game, [indx/1]).
+
+%%%%%%%%%% Graphic engine %%%%%%%%%%%%%%
+
+-record(gs,{board,show_err=true,level=hard,game,frame,orig=[], print_d, print_psdd}).
+
+new(Game) ->
+ wx:new(),
+ wx_object:start_link(?MODULE, [Game], []).
+
+%%%%%%%%%%%%%%%%%%%%% Server callbacks %%%%%%%%%%%%%
+
+init([Game]) ->
+ {Frame, Board} = wx:batch(fun() -> create_window() end),
+ Game ! {gfx, self()},
+ {Frame, init_printer(#gs{board=Board,game=Game,frame=Frame})}.
+
+create_window() ->
+ Frame = wxFrame:new(wx:null(), -1, "Sudoku", []),
+
+ wxFrame:createStatusBar(Frame,[]),
+ wxFrame:connect(Frame, close_window),
+
+ MenuBar = wxMenuBar:new(),
+ File = wxMenu:new([]),
+ Opt = wxMenu:new([]),
+ Help = wxMenu:new([]),
+
+ wxMenu:append(File, ?NEW, "&New Game"),
+ wxMenu:append(File, ?OPEN, "&Open Game"),
+ wxMenu:append(File, ?SAVE, "&Save Game"),
+ wxMenu:appendSeparator(File),
+ wxMenu:append(File, ?PRINT, "Print"),
+ wxMenu:append(File, ?PRINT_PAGE_SETUP, "Page Setup"),
+ wxMenu:append(File, ?PRINT_PRE, "Print Preview"),
+ wxMenu:appendSeparator(File),
+ wxMenu:append(File, ?QUIT, "&Quit Game"),
+
+ wxMenu:append(Help, ?RULES, "Rules"),
+ wxMenu:append(Help, ?ABOUT, "About"),
+
+ wxMenu:appendRadioItem(Opt, ?TRIVIAL, "Level: Trivial"),
+ wxMenu:appendRadioItem(Opt, ?EASY, "Level: Easy"),
+ LItem = wxMenu:appendRadioItem(Opt, ?NORMAL, "Level: Normal"),
+ wxMenu:appendRadioItem(Opt, ?HARD, "Level: Hard"),
+ wxMenu:appendRadioItem(Opt, ?HARDEST, "Level: Hardest"),
+ wxMenu:appendSeparator(Opt),
+ EItem = wxMenu:appendCheckItem(Opt, ?SHOW_ERROR, "Show errors"),
+
+ wxMenuBar:append(MenuBar, File, "&File"),
+ wxMenuBar:append(MenuBar, Opt, "O&ptions"),
+ wxMenuBar:append(MenuBar, Help, "&Help"),
+
+ wxFrame:setMenuBar(Frame, MenuBar),
+ wxFrame:connect(Frame, command_menu_selected),
+
+ MainSz = wxBoxSizer:new(?wxVERTICAL),
+ Top = wxBoxSizer:new(?wxHORIZONTAL),
+
+ Panel = wxPanel:new(Frame),
+ NewGame = wxButton:new(Panel, ?NEW, [{label,"New Game"}]),
+ wxButton:connect(NewGame, command_button_clicked),
+ Empty = wxButton:new(Panel, ?EMPTY, [{label,"Empty Board "}]),
+ wxButton:connect(Empty, command_button_clicked),
+ Clean = wxButton:new(Panel, ?CLEAR, [{label,"Clear"}]),
+ wxButton:connect(Clean, command_button_clicked),
+ Hint = wxButton:new(Panel, ?HINT, [{label, "Hint"}]),
+ wxButton:connect(Hint, command_button_clicked),
+
+ wxSizer:addSpacer(Top,2),
+ SF = wxSizerFlags:new(),
+ wxSizerFlags:proportion(SF,1),
+ wxSizer:add(Top, NewGame, wxSizerFlags:left(SF)),
+ wxSizer:addSpacer(Top,3),
+ wxSizer:add(Top, Empty, wxSizerFlags:center(SF)),
+ wxSizer:addSpacer(Top,3),
+ wxSizer:add(Top, Clean, wxSizerFlags:center(SF)),
+ wxSizer:addSpacer(Top,3),
+ wxSizer:add(Top, Hint, wxSizerFlags:right(SF)),
+
+ wxSizer:addSpacer(MainSz,5),
+ wxSizer:add(MainSz, Top, wxSizerFlags:center(wxSizerFlags:proportion(SF,0))),
+ wxSizer:addSpacer(MainSz,10),
+
+ Board = sudoku_board:new(Panel),
+
+ wxSizer:add(MainSz, Board, wxSizerFlags:proportion(wxSizerFlags:expand(SF),1)),
+ wxWindow:setSizer(Panel,MainSz),
+ wxSizer:fit(MainSz, Frame),
+ wxSizer:setSizeHints(MainSz,Frame),
+ wxWindow:show(Frame),
+ %% Check after append so it's initialized on all platforms
+ wxMenuItem:check(LItem),
+ wxMenuItem:check(EItem),
+ {Frame, Board}.
+
+status(Win, F, A) ->
+ Str = lists:flatten(io_lib:format(F, A)),
+ wxFrame:setStatusText(Win, Str).
+
+%%%%%%%%%%%%%%%% Info i.e. messages %%%%%%%%%%%%%%%%%%%%%
+
+handle_info(quit, S=#gs{game=G,frame=F}) ->
+ wxWindow:close(F),
+ wx_core:quit(),
+ G ! quit,
+ {stop, shutdown, S};
+
+handle_info({init, Init}, S = #gs{board=Board,frame=F}) ->
+ sudoku_board:setup_board(Board, Init),
+ status(F, "Given ~p Left ~p", [length(Init), sudoku_board:left(Board)]),
+ {noreply, S#gs{orig=[indx(Id)||{Id,_}<-Init]}};
+handle_info({correct, ButtI}, S = #gs{board=Board, orig=Orig,frame=F}) ->
+ sudoku_board:butt_correct(Board, ButtI, true),
+ case sudoku_board:left(Board) of
+ 0 ->
+ Str = "Congrats, now try a harder one",
+ MD = wxMessageDialog:new(F,Str,
+ [{style, ?wxOK bor ?wxICON_INFORMATION},
+ {caption, "Complete"}]),
+ wxDialog:showModal(MD),
+ wxDialog:destroy(MD),
+ status(F, "Given ~p Left ~p", [length(Orig), 0]);
+ L ->
+ status(F, "Given ~p Left ~p", [length(Orig), L])
+ end,
+ {noreply, S};
+handle_info({wrong, ButtI}, S = #gs{board=Board}) ->
+ case S#gs.show_err of
+ true ->
+ sudoku_board:butt_correct(Board, ButtI, false);
+ false ->
+ ignore
+ end,
+ {noreply, S};
+handle_info({set_val, ButtI, Val}, S = #gs{game=G,board=Board,orig=Orig}) ->
+ case lists:member(indx(ButtI), Orig) of
+ false -> set_val(ButtI, Val, Board, G);
+ true -> ignore
+ end,
+ {noreply, S};
+handle_info({working, Done}, S = #gs{frame=F}) ->
+ status(F, "Thinking: ~p%", [Done]),
+ {noreply, S};
+handle_info({busy, Mode},S) ->
+ case Mode of
+ start -> wx_misc:beginBusyCursor();
+ stop -> wx_misc:endBusyCursor()
+ end,
+ {noreply, S}.
+
+%%%%%%%%%%%%%%%%% GUI-Events %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+handle_event(#wx{id=?HINT, event=#wxCommand{type=command_button_clicked}},
+ S = #gs{game=G}) ->
+ G ! {solve,false},
+ {noreply,S};
+
+handle_event(#wx{event=#wxClose{}},
+ S = #gs{game=G,frame=F}) ->
+ catch wxWindow:'Destroy'(F),
+ G ! quit,
+ {stop, shutdown, S};
+
+handle_event(#wx{id=?QUIT, event=#wxCommand{type=command_menu_selected}},
+ S = #gs{game=G,frame=F}) ->
+ wxWindow:close(F,[]),
+ G ! quit,
+ {stop, shutdown, S};
+
+%% type=command_button_clicked,
+handle_event(#wx{id=?NEW, event=#wxCommand{}},
+ S = #gs{game=G, board=Board}) ->
+ G ! {op,?NEW,S#gs.level},
+ sudoku_board:setup_board(Board,[]),
+ {noreply, S#gs{orig=[]}};
+handle_event(#wx{id=?EMPTY, event=#wxCommand{}},
+ S = #gs{game=G, board=Board}) ->
+ G ! {op,?EMPTY},
+ sudoku_board:setup_board(Board,[]),
+ {noreply, S#gs{orig=[]}};
+handle_event(#wx{id=?CLEAR, event=#wxCommand{}},
+ S = #gs{game=G,board=Board}) ->
+ Vals = sudoku_board:clear_board(Board),
+ G ! {loaded, Vals},
+ {noreply, S};
+handle_event(#wx{id=ID, event=#wxCommand{}}, S) when ID > 125 ->
+ New = dialog(ID, S),
+ {noreply, New};
+handle_event(Msg,S) ->
+ io:format("~p: Unhandled event ~p~n",[?MODULE, Msg]),
+ %%sudoku_board:event(Msg, Ids),
+ {noreply, S}.
+
+handle_call(What, _From, State) ->
+ {stop, {call, What}, State}.
+
+code_change(_, _, State) ->
+ {stop, not_yet_implemented, State}.
+
+terminate(_Reason, _State) ->
+ normal.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dialog(?SHOW_ERROR, S=#gs{show_err=Show}) ->
+ S#gs{show_err = not Show};
+dialog(ID, S) when ID >= 210, ID =< 240 ->
+ Level = sudoku_game:level(ID-200),
+ S#gs{level = Level};
+dialog(?SAVE, S=#gs{frame=Frame, board=Board}) ->
+ FD = wxFileDialog:new(Frame, [{style, ?wxFD_SAVE bor
+ ?wxFD_OVERWRITE_PROMPT}]),
+ case wxFileDialog:showModal(FD) of
+ ?wxID_OK ->
+ Path = wxFileDialog:getPath(FD),
+ {ok,Fd} = file:open(Path, [write]),
+ List = sudoku_board:get_board_data(Board),
+ io:format(Fd, "~w.~n", [List]),
+ file:close(Fd);
+ _ ->
+ ignore
+ end,
+ wxDialog:destroy(FD),
+ S;
+dialog(?OPEN, S=#gs{game=Server, frame=Frame, board=Board}) ->
+ FD = wxFileDialog:new(Frame,[{style, ?wxFD_OPEN bor ?wxFD_FILE_MUST_EXIST}]),
+ case wxDialog:showModal(FD) of
+ ?wxID_OK ->
+ Path = wxFileDialog:getPath(FD),
+ case file:consult(Path) of
+ {ok, [Game]} when is_list(Game) ->
+ Vals = sudoku_board:set_board_data(Board, Game),
+ Server ! {loaded, Vals};
+ _ ->
+ ignore
+ end;
+ _ ->
+ ignore
+ end,
+ wxFileDialog:destroy(FD),
+ S;
+dialog(?ABOUT, S=#gs{frame=Frame}) ->
+ Str = "I'm just testing WxWidgets.\n"
+ "Testing various features and usages.\n/Dgud",
+ MD = wxMessageDialog:new(Frame,Str,
+ [{style, ?wxOK bor ?wxICON_INFORMATION},
+ {caption, "About box"}]),
+ wxDialog:showModal(MD),
+ wxDialog:destroy(MD),
+ S;
+dialog(?RULES, S) ->
+ wx_misc:launchDefaultBrowser("http://www.sudoku.com"),
+ S;
+
+dialog(?PRINT_PAGE_SETUP, S = #gs{frame=Frame, print_psdd=PsDD0, print_d=PD0}) ->
+ wxPageSetupDialogData:setPrintData(PsDD0, PD0),
+ PSD = wxPageSetupDialog:new(Frame, [{data,PsDD0}]),
+ wxPageSetupDialog:showModal(PSD),
+
+ PSDD1 = wxPageSetupDialog:getPageSetupData(PSD),
+ PD1 = wxPageSetupDialogData:getPrintData(PSDD1),
+ %% Create new objects using copy constr.
+ PD = wxPrintData:new(PD1),
+ PsDD = wxPageSetupDialogData:new(PSDD1),
+ wxPageSetupDialog:destroy(PSD),
+ wxPageSetupDialogData:destroy(PsDD0),
+ wxPrintData:destroy(PD0),
+ S#gs{print_psdd=PsDD, print_d=PD};
+dialog(?PRINT_PRE, S = #gs{frame=Frame, print_d=PD}) ->
+ PDD = wxPrintDialogData:new(PD),
+ Printout1 = wxPrintout:new("Print", fun(This,Page) -> printout(This,Page,S) end,
+ [{getPageInfo, fun getPageInfo/1}]),
+ Printout2 = wxPrintout:new("Print", fun(This,Page) -> printout(This,Page,S) end,
+ [{getPageInfo, fun getPageInfo/1}]),
+ Preview = wxPrintPreview:new(Printout1, [{printoutForPrinting,Printout2},{data,PDD}]),
+ case wxPrintPreview:isOk(Preview) of
+ true ->
+ PF = wxPreviewFrame:new(Preview, Frame, [{title, "Print Preview"}]),
+ wxPreviewFrame:centre(PF, [{dir, ?wxBOTH}]),
+ wxPreviewFrame:initialize(PF),
+ wxPreviewFrame:centre(PF),
+ wxPreviewFrame:show(PF);
+ false ->
+ io:format("Could not create preview window.\n"
+ "Perhaps your current printer is not set correctly?~n", []),
+ wxPrintPreview:destroy(Preview)
+ end,
+ S;
+dialog(?PRINT, S = #gs{frame=Frame, print_d=PD}) ->
+ PDD = wxPrintDialogData:new(PD),
+ Printer = wxPrinter:new([{data,PDD}]),
+ Printout = wxPrintout:new("Print", fun(This,Page) -> printout(This,Page,S) end,
+ [{getPageInfo, fun getPageInfo/1}]),
+
+ case wxPrinter:print(Printer, Frame, Printout, [{prompt,true}]) of
+ false ->
+ case wxPrinter:getLastError() of
+ ?wxPRINTER_ERROR ->
+ io:format("There was a problem printing.\n"
+ "Perhaps your current printer is not set correctly?~n", []);
+ _ ->
+ io:format("You canceled printing~n", [])
+ end,
+ wxPrinter:destroy(Printer),
+ S;
+ true ->
+ PDD2 = wxPrinter:getPrintDialogData(Printer),
+ PD2 = wxPrintDialogData:getPrintData(PDD2),
+ %% Copy data PD2 will be deleted when Printer is destroyed
+ PD3 = wxPrintData:new(PD2),
+ wxPrintData:destroy(PD),
+ wxPrinter:destroy(Printer),
+ S#gs{print_d = PD3}
+ end;
+
+dialog(Other, S) ->
+ io:format("other ~p~n",[Other]),
+ S.
+
+init_printer(S) ->
+ PD = wxPrintData:new(),
+
+ %% You could set an initial paper size here
+ %% g_printData->SetPaperId(wxPAPER_LETTER); // for Americans
+ %% g_printData->SetPaperId(wxPAPER_A4); // for everyone else
+
+ PSDD = wxPageSetupDialogData:new(PD),
+ wxPageSetupDialogData:setMarginTopLeft(PSDD,{15,15}),
+ wxPageSetupDialogData:setMarginBottomRight(PSDD,{15,15}),
+
+ S#gs{print_d=PD, print_psdd=PSDD}.
+
+getPageInfo(_This) ->
+ {1,1,1,1}.
+
+printout(This, _Page, #gs{board=Board, print_psdd=PsDD}) ->
+ MX = MY = 500,
+ wxPrintout:fitThisSizeToPageMargins(This, {MX,MY}, PsDD),
+
+ _DBG = {_X,_Y,W,H} = wxPrintout:getLogicalPageMarginsRect(This, PsDD),
+ wxPrintout:offsetLogicalOrigin(This,(W-MX) div 2, (H-MY) div 2),
+%% io:format("~p ->{~p,~p} ~n", [_DBG, (W-MX) div 2, (H-MY) div 2]),
+
+ DC = wxPrintout:getDC(This),
+ sudoku_board:draw(Board, DC, {500,500}),
+ true.
+
+set_val(Id, Val, Board, G) ->
+ sudoku_board:set_butt(Board, Id,Val),
+ G ! {validate, Id, Val},
+ ok.
+
+
+