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