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 <StringFormat.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(new BMenuItem(B_TRANSLATE("Intermediate (6x6)"), 104 difficultyMessage)); 105 106 difficultyMessage = new BMessage(MENU_DIFFICULTY); 107 difficultyMessage->AddInt32("rows", 8); 108 difficultyMessage->AddInt32("cols", 8); 109 newMenu->AddItem(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(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(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(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 fIsFirstClick = true; 182 fPairsView->CreateGameBoard(); 183 } 184 185 186 void 187 PairsWindow::SetGameSize(uint8 rows, uint8 cols) 188 { 189 RemoveChild(fPairsView); 190 delete fPairsView; 191 192 _MakeGameView(rows, cols); 193 NewGame(); 194 } 195 196 197 void 198 PairsWindow::MessageReceived(BMessage* message) 199 { 200 switch (message->what) { 201 case MENU_NEW: 202 NewGame(); 203 break; 204 205 case MENU_DIFFICULTY: 206 { 207 int32 rows; 208 int32 cols; 209 if (message->FindInt32("rows", &rows) == B_OK 210 && message->FindInt32("cols", &cols) == B_OK) { 211 SetGameSize(rows, cols); 212 } 213 break; 214 } 215 216 case MENU_ICON_SIZE: 217 { 218 int32 size; 219 if (message->FindInt32("size", &size) == B_OK) { 220 fPairsView->SetIconSize(size); 221 _ResizeWindow(fPairsView->Rows(), fPairsView->Cols()); 222 } 223 224 break; 225 } 226 227 case MENU_QUIT: 228 be_app->PostMessage(B_QUIT_REQUESTED); 229 break; 230 231 case kMsgCardButton: 232 { 233 if (!fIsPairsActive) 234 break; 235 236 int32 buttonNumber; 237 if (message->FindInt32("button number", &buttonNumber) != B_OK) 238 break; 239 240 BObjectList<PairsButton>* pairsButtonList 241 = fPairsView->PairsButtonList(); 242 if (pairsButtonList == NULL) 243 break; 244 245 // look at what icon is behind a button 246 int32 buttonCount = pairsButtonList->CountItems(); 247 for (int32 i = 0; i < buttonCount; i++) { 248 int32 iconPosition = fPairsView->GetIconPosition(i); 249 if (iconPosition == buttonNumber) { 250 fPairCardPosition = i % (buttonCount / 2); 251 fButtonPosition = iconPosition; 252 break; 253 } 254 } 255 256 // gameplay 257 fButtonClicks++; 258 pairsButtonList->ItemAt(fButtonPosition)->Hide(); 259 260 if (fIsFirstClick) { 261 fPairCardTmpPosition = fPairCardPosition; 262 fButtonTmpPosition = fButtonPosition; 263 } else { 264 delete fPairComparing; 265 // message of message runner might not have arrived 266 // yet, so it is deleted here to prevent any leaking 267 // just in case 268 BMessage message(kMsgPairComparing); 269 fPairComparing = new BMessageRunner(BMessenger(this), 270 &message, 5 * 100000L, 1); 271 fIsPairsActive = false; 272 } 273 274 fIsFirstClick = !fIsFirstClick; 275 break; 276 } 277 278 case kMsgPairComparing: 279 { 280 BObjectList<PairsButton>* pairsButtonList 281 = fPairsView->PairsButtonList(); 282 if (pairsButtonList == NULL) 283 break; 284 285 delete fPairComparing; 286 fPairComparing = NULL; 287 288 fIsPairsActive = true; 289 290 if (fPairCardPosition == fPairCardTmpPosition) 291 fFinishPairs++; 292 else { 293 pairsButtonList->ItemAt(fButtonPosition)->Show(); 294 pairsButtonList->ItemAt(fButtonTmpPosition)->Show(); 295 } 296 297 // game end and results 298 if (fFinishPairs == pairsButtonList->CountItems() / 2) { 299 BString strAbout = B_TRANSLATE("%app%\n" 300 "\twritten by Ralf Schülke\n" 301 "\tCopyright 2008-2010, Haiku Inc.\n" 302 "\n"); 303 304 strAbout.ReplaceFirst("%app%", 305 B_TRANSLATE_SYSTEM_NAME("Pairs")); 306 307 // Note: in english the singular form is never used, but other 308 // languages behave differently. 309 static BStringFormat format(B_TRANSLATE( 310 "You completed the game in " 311 "{0, plural, one{# click} other{# clicks}}.\n")); 312 format.Format(strAbout, fButtonClicks); 313 314 BAlert* alert = new BAlert("about", 315 strAbout.String(), 316 B_TRANSLATE("New game"), 317 B_TRANSLATE("Quit game")); 318 319 BTextView* view = alert->TextView(); 320 BFont font; 321 322 view->SetStylable(true); 323 324 view->GetFont(&font); 325 font.SetSize(18); 326 font.SetFace(B_BOLD_FACE); 327 view->SetFontAndColor(0, 328 strlen(B_TRANSLATE_SYSTEM_NAME("Pairs")), &font); 329 view->ResizeToPreferred(); 330 alert->SetShortcut(0, B_ESCAPE); 331 332 if (alert->Go() == 0) 333 NewGame(); 334 else 335 be_app->PostMessage(B_QUIT_REQUESTED); 336 } 337 break; 338 } 339 340 default: 341 BWindow::MessageReceived(message); 342 } 343 } 344 345 346 // #pragma mark - PairsWindow private methods 347 348 349 void 350 PairsWindow::_ResizeWindow(uint8 rows, uint8 cols) 351 { 352 int32 iconSize = fPairsView->IconSize(); 353 int32 spacing = fPairsView->Spacing(); 354 355 ResizeTo((iconSize + spacing) * rows + spacing, 356 (iconSize + spacing) * cols + spacing + fMenuBar->Bounds().Height()); 357 } 358