diff options
Diffstat (limited to 'lib/wx/examples/sudoku/sudoku_gui.erl')
-rwxr-xr-x | lib/wx/examples/sudoku/sudoku_gui.erl | 391 |
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. + + + |