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