xref: /haiku/src/apps/pairs/PairsWindow.cpp (revision 5e96d7d537fbec23bad4ae9b4c8e7b02e769f0c6)
1 /*
2  * Copyright 2008 Ralf Schülke, ralf.schuelke@googlemail.com.
3  * Copyright 2010 Adam Smith <adamd.smith@utoronto.ca>
4  * Copyright 2014 Haiku, Inc. All rights reserved.
5  *
6  * Distributed under the terms of the MIT License.
7  *
8  * Authors:
9  *		Ralf Schülke, ralf.schuelke@googlemail.com
10  *		John Scipione, jscipione@gmail.com
11  *		Adam Smith, adamd.smith@utoronto.ca
12  */
13 
14 
15 #include "PairsWindow.h"
16 
17 #include <Application.h>
18 #include <Alert.h>
19 #include <Button.h>
20 #include <Catalog.h>
21 #include <ObjectList.h>
22 #include <Menu.h>
23 #include <MenuBar.h>
24 #include <MenuItem.h>
25 #include <MessageRunner.h>
26 #include <String.h>
27 #include <TextView.h>
28 
29 #include "Pairs.h"
30 #include "PairsButton.h"
31 #include "PairsView.h"
32 
33 
34 #undef B_TRANSLATION_CONTEXT
35 #define B_TRANSLATION_CONTEXT "PairsWindow"
36 
37 
38 const uint32 MENU_NEW					= 'MGnw';
39 const uint32 MENU_DIFFICULTY			= 'MGdf';
40 const uint32 MENU_QUIT					= 'MGqu';
41 const uint32 MENU_ICON_SIZE				= 'MSIs';
42 
43 const uint32 kMsgPairComparing			= 'pcom';
44 
45 
46 //	#pragma mark - PairsWindow
47 
48 
49 PairsWindow::PairsWindow()
50 	:
51 	BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Pairs"),
52 		B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE
53 			| B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
54 	fPairComparing(NULL),
55 	fIsFirstClick(true),
56 	fIsPairsActive(true),
57 	fPairCardPosition(0),
58 	fPairCardTmpPosition(0),
59 	fButtonTmpPosition(0),
60 	fButtonPosition(0),
61 	fButtonClicks(0),
62 	fFinishPairs(0),
63 	fIconSizeMenu(NULL)
64 {
65 	_MakeMenuBar();
66 	_MakeGameView(4, 4);
67 
68 	CenterOnScreen();
69 }
70 
71 
72 PairsWindow::~PairsWindow()
73 {
74 	delete fPairComparing;
75 }
76 
77 
78 void
79 PairsWindow::_MakeMenuBar()
80 {
81 	fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar");
82 	AddChild(fMenuBar);
83 
84 	BMenu* gameMenu = new BMenu(B_TRANSLATE("Game"));
85 	fMenuBar->AddItem(gameMenu);
86 
87 	BMenuItem* menuItem;
88 
89 	BMenu* newMenu = new BMenu(B_TRANSLATE("New"));
90 	newMenu->SetRadioMode(true);
91 
92 	BMessage* difficultyMessage = new BMessage(MENU_DIFFICULTY);
93 	difficultyMessage->AddInt32("rows", 4);
94 	difficultyMessage->AddInt32("cols", 4);
95 	newMenu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Beginner (4x4)"),
96 		difficultyMessage));
97 	menuItem->SetMarked(true);
98 
99 	difficultyMessage = new BMessage(MENU_DIFFICULTY);
100 	difficultyMessage->AddInt32("rows", 6);
101 	difficultyMessage->AddInt32("cols", 6);
102 	newMenu->AddItem(menuItem = new BMenuItem(
103 		B_TRANSLATE("Intermediate (6x6)"), difficultyMessage));
104 
105 	difficultyMessage = new BMessage(MENU_DIFFICULTY);
106 	difficultyMessage->AddInt32("rows", 8);
107 	difficultyMessage->AddInt32("cols", 8);
108 	newMenu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Expert (8x8)"),
109 		difficultyMessage));
110 
111 	menuItem = new BMenuItem(newMenu, new BMessage(MENU_NEW));
112 	menuItem->SetShortcut('N', B_COMMAND_KEY);
113 	gameMenu->AddItem(menuItem);
114 
115 	gameMenu->AddSeparatorItem();
116 
117 	gameMenu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Quit"),
118 		new BMessage(MENU_QUIT), 'Q'));
119 
120 	fIconSizeMenu = new BMenu(B_TRANSLATE("Size"));
121 	fIconSizeMenu->SetRadioMode(true);
122 	fMenuBar->AddItem(fIconSizeMenu);
123 
124 	BMessage* iconSizeMessage = new BMessage(MENU_ICON_SIZE);
125 	iconSizeMessage->AddInt32("size", kSmallIconSize);
126 	fIconSizeMenu->AddItem(menuItem = new BMenuItem(
127 		B_TRANSLATE("Small"), iconSizeMessage), 0);
128 
129 	iconSizeMessage = new BMessage(MENU_ICON_SIZE);
130 	iconSizeMessage->AddInt32("size", kMediumIconSize);
131 	fIconSizeMenu->AddItem(menuItem = new BMenuItem(
132 		B_TRANSLATE("Medium"), iconSizeMessage), 1);
133 	menuItem->SetMarked(true);
134 
135 	iconSizeMessage = new BMessage(MENU_ICON_SIZE);
136 	iconSizeMessage->AddInt32("size", kLargeIconSize);
137 	fIconSizeMenu->AddItem(menuItem = new BMenuItem(
138 		B_TRANSLATE("Large"), iconSizeMessage), 2);
139 }
140 
141 
142 void
143 PairsWindow::_MakeGameView(uint8 rows, uint8 cols)
144 {
145 	BRect viewBounds = Bounds();
146 	viewBounds.top = fMenuBar->Bounds().Height() + 1;
147 
148 	uint8 iconSize;
149 	BMenuItem* marked = fIconSizeMenu->FindMarked();
150 	if (marked != NULL) {
151 		switch (fIconSizeMenu->IndexOf(marked)) {
152 			case 0:
153 				iconSize = kSmallIconSize;
154 				break;
155 
156 			case 2:
157 				iconSize = kLargeIconSize;
158 				break;
159 
160 			case 1:
161 			default:
162 				iconSize = kMediumIconSize;
163 		}
164 	} else {
165 		iconSize = kMediumIconSize;
166 		fIconSizeMenu->ItemAt(1)->SetMarked(true);
167 	}
168 
169 	fPairsView = new PairsView(viewBounds, "PairsView", rows, cols, iconSize);
170 	AddChild(fPairsView);
171 	_ResizeWindow(rows, cols);
172 }
173 
174 
175 void
176 PairsWindow::NewGame()
177 {
178 	fButtonClicks = 0;
179 	fFinishPairs = 0;
180 	fPairsView->CreateGameBoard();
181 }
182 
183 
184 void
185 PairsWindow::SetGameSize(uint8 rows, uint8 cols)
186 {
187 	RemoveChild(fPairsView);
188 	delete fPairsView;
189 
190 	_MakeGameView(rows, cols);
191 	NewGame();
192 }
193 
194 
195 void
196 PairsWindow::MessageReceived(BMessage* message)
197 {
198 	switch (message->what) {
199 		case MENU_NEW:
200 			NewGame();
201 			break;
202 
203 		case MENU_DIFFICULTY:
204 		{
205 			int32 rows;
206 			int32 cols;
207 			if (message->FindInt32("rows", &rows) == B_OK
208 				&& message->FindInt32("cols", &cols) == B_OK) {
209 				SetGameSize(rows, cols);
210 			}
211 			break;
212 		}
213 
214 		case MENU_ICON_SIZE:
215 		{
216 			int32 size;
217 			if (message->FindInt32("size", &size) == B_OK) {
218 				fPairsView->SetIconSize(size);
219 				_ResizeWindow(fPairsView->Rows(), fPairsView->Cols());
220 			}
221 
222 			break;
223 		}
224 
225 		case MENU_QUIT:
226 			be_app->PostMessage(B_QUIT_REQUESTED);
227 			break;
228 
229 		case kMsgCardButton:
230 		{
231 			if (!fIsPairsActive)
232 				break;
233 
234 			int32 buttonNumber;
235 			if (message->FindInt32("button number", &buttonNumber) != B_OK)
236 				break;
237 
238 			BObjectList<PairsButton>* pairsButtonList
239 				= fPairsView->PairsButtonList();
240 			if (pairsButtonList == NULL)
241 				break;
242 
243 			// look at what icon is behind a button
244 			int32 buttonCount = pairsButtonList->CountItems();
245 			for (int32 i = 0; i < buttonCount; i++) {
246 				int32 iconPosition = fPairsView->GetIconPosition(i);
247 				if (iconPosition == buttonNumber) {
248 					fPairCardPosition = i % (buttonCount / 2);
249 					fButtonPosition = iconPosition;
250 					break;
251 				}
252 			}
253 
254 			// gameplay
255 			fButtonClicks++;
256 			pairsButtonList->ItemAt(fButtonPosition)->Hide();
257 
258 			if (fIsFirstClick) {
259 				fPairCardTmpPosition = fPairCardPosition;
260 				fButtonTmpPosition = fButtonPosition;
261 			} else {
262 				delete fPairComparing;
263 					// message of message runner might not have arrived
264 					// yet, so it is deleted here to prevent any leaking
265 					// just in case
266 				BMessage message(kMsgPairComparing);
267 				fPairComparing = new BMessageRunner(BMessenger(this),
268 					&message,  5 * 100000L, 1);
269 				fIsPairsActive = false;
270 			}
271 
272 			fIsFirstClick = !fIsFirstClick;
273 			break;
274 		}
275 
276 		case kMsgPairComparing:
277 		{
278 			BObjectList<PairsButton>* pairsButtonList
279 				= fPairsView->PairsButtonList();
280 			if (pairsButtonList == NULL)
281 				break;
282 
283 			delete fPairComparing;
284 			fPairComparing = NULL;
285 
286 			fIsPairsActive = true;
287 
288 			if (fPairCardPosition == fPairCardTmpPosition)
289 				fFinishPairs++;
290 			else {
291 				pairsButtonList->ItemAt(fButtonPosition)->Show();
292 				pairsButtonList->ItemAt(fButtonTmpPosition)->Show();
293 			}
294 
295 			// game end and results
296 			if (fFinishPairs == pairsButtonList->CountItems() / 2) {
297 				BString score;
298 				score << fButtonClicks;
299 				BString strAbout = B_TRANSLATE("%app%\n"
300 					"\twritten by Ralf Schülke\n"
301 					"\tCopyright 2008-2010, Haiku Inc.\n"
302 					"\n"
303 					"You completed the game in %num% clicks.\n");
304 
305 				strAbout.ReplaceFirst("%app%",
306 					B_TRANSLATE_SYSTEM_NAME("Pairs"));
307 				strAbout.ReplaceFirst("%num%", score);
308 
309 				BAlert* alert = new BAlert("about",
310 					strAbout.String(),
311 					B_TRANSLATE("New game"),
312 					B_TRANSLATE("Quit game"));
313 
314 				BTextView* view = alert->TextView();
315 				BFont font;
316 
317 				view->SetStylable(true);
318 
319 				view->GetFont(&font);
320 				font.SetSize(18);
321 				font.SetFace(B_BOLD_FACE);
322 				view->SetFontAndColor(0,
323 					strlen(B_TRANSLATE_SYSTEM_NAME("Pairs")), &font);
324 				view->ResizeToPreferred();
325 				alert->SetShortcut(0, B_ESCAPE);
326 
327 				if (alert->Go() == 0)
328 					NewGame();
329 				else
330 					be_app->PostMessage(B_QUIT_REQUESTED);
331 			}
332 			break;
333 		}
334 
335 		default:
336 			BWindow::MessageReceived(message);
337 	}
338 }
339 
340 
341 //	#pragma mark - PairsWindow private methods
342 
343 
344 void
345 PairsWindow::_ResizeWindow(uint8 rows, uint8 cols)
346 {
347 	int32 iconSize = fPairsView->IconSize();
348 	int32 spacing = fPairsView->Spacing();
349 
350 	ResizeTo((iconSize + spacing) * rows + spacing,
351 		(iconSize + spacing) * cols + spacing + fMenuBar->Bounds().Height());
352 }
353