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