Pairs: store vector icons, new size menu

* Search for vector icons from MIME database once at start,
  limit to application super-type, this fulfills a TODO in the code
* Use a std::map keyed by a hash to avoid duplicate icons
  eliminating _HasBitmap()
* Store the vector representation and then only build bitmaps
  when needed
* Convert uses of int to int32
* Convert from using BList to BObjectList, simplified cleanup
* Rename variables and methods to not abbreviate/be more clear
  e.g. fPosX, fPosY => fPositionX, fPositionY
  e.g. _GenerateCardPos() => _GenerateCardPositions()
* Renamed PairsTopButton to PairsButton
* Integrate Size submenu into New menu item.
  Size menu item goes away, you can select New to get a new game at
  the current difficulty, or, you can drill down in the New menu to
  choose from Beginner, Intermediate, or Expert.
* Add new Size menu to set the icon size: Small (32x32), Medium (64x64),
  or Large (128x128). Default is medium
* Rename MENU_SIZE message constant to MENU_DIFFICULTY for clarity
* Eliminate PairsGlobal.h, distribute constants to appropriate files.
This commit is contained in:
John Scipione 2014-01-27 14:01:32 -05:00
parent 0933046655
commit 97e1b053b6
11 changed files with 632 additions and 429 deletions

View File

@ -2,9 +2,9 @@ SubDir HAIKU_TOP src apps pairs ;
Application Pairs :
Pairs.cpp
PairsWindow.cpp
PairsButton.cpp
PairsView.cpp
PairsTopButton.cpp
PairsWindow.cpp
: be localestub $(TARGET_LIBSTDC++)
: Pairs.rdef
@ -13,6 +13,7 @@ Application Pairs :
DoCatalogs Pairs :
x-vnd.Haiku-Pairs
:
Pairs.cpp
PairsView.cpp
PairsWindow.cpp
;

View File

@ -13,14 +13,22 @@
#include <stdlib.h>
#include <Application.h>
#include <Alert.h>
#include <Catalog.h>
#include <MimeType.h>
#include "PairsWindow.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Pairs"
const char* kSignature = "application/x-vnd.Haiku-Pairs";
static const size_t kMinIconCount = 64;
static const size_t kMaxIconCount = 384;
// #pragma mark - Pairs
@ -30,6 +38,7 @@ Pairs::Pairs()
BApplication(kSignature),
fWindow(NULL)
{
_GetVectorIcons();
}
@ -60,6 +69,92 @@ Pairs::MessageReceived(BMessage* message)
}
bool
Pairs::QuitRequested()
{
// delete vector icons
for (IconMap::iterator iter = fIconMap.begin(); iter != fIconMap.end();
++iter) {
delete fIconMap[iter->first];
}
return true;
}
// #pragma mark - Pairs private methods
void
Pairs::_GetVectorIcons()
{
// Load vector icons from the MIME type database and add a pointer to them
// into a std::map keyed by a generated hash.
BMessage types;
if (BMimeType::GetInstalledTypes("application", &types) != B_OK)
return;
const char* type;
for (int32 i = 0; types.FindString("types", i, &type) == B_OK; i++) {
BMimeType mimeType(type);
if (mimeType.InitCheck() != B_OK)
continue;
uint8* data;
size_t size;
if (mimeType.GetIcon(&data, &size) != B_OK) {
// didn't find an icon
continue;
}
size_t hash = 0xdeadbeef;
for (size_t i = 0; i < size; i++)
hash = 31 * hash + data[i];
if (fIconMap.find(hash) != fIconMap.end()) {
// key has already been added to the map
delete[] data;
continue;
}
vector_icon* icon = (vector_icon*)malloc(sizeof(vector_icon));
if (icon == NULL) {
delete[] data;
free(icon);
continue;
}
icon->data = data;
icon->size = size;
// found a vector icon, add it to the list
fIconMap[hash] = icon;
if (fIconMap.size() >= kMaxIconCount) {
// this is enough to choose from, stop eating memory...
return;
}
}
if (fIconMap.size() < kMinIconCount) {
char buffer[512];
snprintf(buffer, sizeof(buffer),
B_TRANSLATE_COMMENT("Pairs did not find enough vector icons "
"to start; it needs at least %zu, found %zu.\n",
"Don't translate \"%zu\", but make sure to keep them."),
kMinIconCount, fIconMap.size());
BString messageString(buffer);
BAlert* alert = new BAlert("Fatal", messageString.String(),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_WIDEST,
B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
exit(1);
}
}
// #pragma mark - main

View File

@ -11,27 +11,45 @@
#define PAIRS_H
#include <map>
#include <Application.h>
#include <Catalog.h>
extern const char* kSignature;
struct vector_icon {
uint8* data;
size_t size;
};
class BBitmap;
class BMessage;
class PairsWindow;
typedef std::map<size_t, vector_icon*> IconMap;
class Pairs : public BApplication {
public:
Pairs();
virtual ~Pairs();
Pairs();
virtual ~Pairs();
virtual void ReadyToRun();
virtual void RefsReceived(BMessage* message);
virtual void MessageReceived(BMessage* message);
virtual void ReadyToRun();
virtual void RefsReceived(BMessage* message);
virtual void MessageReceived(BMessage* message);
virtual bool QuitRequested();
IconMap GetIconMap() const { return fIconMap; };
private:
PairsWindow* fWindow;
void _GetVectorIcons();
PairsWindow* fWindow;
IconMap fIconMap;
};

View File

@ -0,0 +1,28 @@
/*
* Copyright 2008 Ralf Schülke, ralf.schuelke@googlemail.com.
* Copyright 2014 Haiku, Inc. All rights reserved.
*
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, jscipione@gmail.com
*/
#include "PairsButton.h"
// #pragma mark - PairsButton
PairsButton::PairsButton(int32 x, int32 y, int32 size, BMessage* message)
:
BButton(BRect(x, y, x + size, y + size), "pairs button", "?", message)
{
SetFontSize(size - 15);
}
PairsButton::~PairsButton()
{
}

View File

@ -7,17 +7,19 @@
* Authors:
* John Scipione, jscipione@gmail.com
*/
#ifndef PAIRS_TOP_BUTTON_H
#define PAIRS_TOP_BUTTON_H
#ifndef PAIRS_BUTTON_H
#define PAIRS_BUTTON_H
#include <OS.h>
class BButton;
#include <Button.h>
class TopButton : public BButton {
class PairsButton : public BButton {
public:
TopButton(int x, int y, BMessage* message);
virtual ~TopButton();
PairsButton(int32 x, int32 y, int32 size,
BMessage* message);
virtual ~PairsButton();
};
#endif // PAIRS_TOP_BUTTON_H
#endif // PAIRS_BUTTON_H

View File

@ -1,23 +0,0 @@
/*
* Copyright 2008 Ralf Schülke, ralf.schuelke@googlemail.com.
* Copyright 2014 Haiku, Inc. All rights reserved.
*
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, jscipione@gmail.com
*/
#ifndef PAIRS_GLOBAL_H
#define PAIRS_GLOBAL_H
#include <SupportDefs.h>
const uint32 kMsgCardButton = 'card';
const uint32 kMsgPairComparing = 'pcom';
const int kBitmapSize = 64;
const int kSpaceSize = 10;
#endif // PAIRS_GLOBAL_H

View File

@ -1,30 +0,0 @@
/*
* Copyright 2008 Ralf Schülke, ralf.schuelke@googlemail.com.
* Copyright 2014 Haiku, Inc. All rights reserved.
*
* Distributed under the terms of the MIT License.
*
* Authors:
* John Scipione, jscipione@gmail.com
*/
#include <stdio.h>
#include <unistd.h>
#include <Button.h>
#include "PairsTopButton.h"
#include "PairsGlobal.h"
TopButton::TopButton(int x, int y, BMessage* message)
: BButton(BRect(x, y, x + kBitmapSize, y + kBitmapSize), "top_button",
"?", message)
{
SetFontSize(54);
}
TopButton::~TopButton()
{
}

View File

@ -12,39 +12,44 @@
#include "PairsView.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Alert.h>
#include <Application.h>
#include <Bitmap.h>
#include <Button.h>
#include <ControlLook.h>
#include <Catalog.h>
#include <Directory.h>
#include <Entry.h>
#include <FindDirectory.h>
#include <IconUtils.h>
#include <List.h>
#include <Node.h>
#include <NodeInfo.h>
#include <Path.h>
#include <InterfaceDefs.h>
#include <Window.h>
#include "Pairs.h"
#include "PairsGlobal.h"
#include "PairsTopButton.h"
#include "PairsButton.h"
PairsView::PairsView(BRect frame, const char* name, int width, int height,
uint32 resizingMode)
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PairsView"
// #pragma mark - PairsView
PairsView::PairsView(BRect frame, const char* name, uint8 rows, uint8 cols,
uint8 iconSize)
:
BView(frame, name, resizingMode, B_WILL_DRAW),
fWidth(width),
fHeight(height),
fNumOfCards(width * height),
fRandPos(new int[fNumOfCards]),
fPosX(new int[fNumOfCards]),
fPosY(new int[fNumOfCards])
BView(frame, name, B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS),
fRows(rows),
fCols(cols),
fIconSize(iconSize),
fButtonsCount(rows * cols),
fCardsCount(fButtonsCount / 2),
fPairsButtonList(new BObjectList<PairsButton>(fButtonsCount)),
fSmallBitmapsList(new BObjectList<BBitmap>(fCardsCount)),
fMediumBitmapsList(new BObjectList<BBitmap>(fCardsCount)),
fLargeBitmapsList(new BObjectList<BBitmap>(fCardsCount)),
fRandomPosition(new int32[fButtonsCount]),
fPositionX(new int32[fButtonsCount]),
fPositionY(new int32[fButtonsCount])
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
CreateGameBoard();
_SetPairsBoard();
}
@ -54,209 +59,211 @@ void
PairsView::CreateGameBoard()
{
// Show hidden buttons
for (int32 i = 0; i < CountChildren(); i++) {
int32 childrenCount = CountChildren();
for (int32 i = 0; i < childrenCount; i++) {
BView* child = ChildAt(i);
if (child->IsHidden())
child->Show();
}
_GenerateCardPos();
_GenerateCardPositions();
}
PairsView::~PairsView()
{
for (int i = 0; i < fCardBitmaps.CountItems(); i++)
delete ((BBitmap*)fCardBitmaps.ItemAt(i));
for (int i = 0; i < fDeckCard.CountItems(); i++)
delete ((TopButton*)fDeckCard.ItemAt(i));
delete fRandPos;
delete fPosX;
delete fPosY;
delete fSmallBitmapsList;
delete fMediumBitmapsList;
delete fLargeBitmapsList;
delete fPairsButtonList;
delete fRandomPosition;
delete fPositionX;
delete fPositionY;
}
void
PairsView::AttachedToWindow()
{
for (int32 i = 0; i < fButtonsCount; i++) {
PairsButton* button = fPairsButtonList->ItemAt(i);
if (button != NULL)
button->SetTarget(Window());
}
MakeFocus(true);
}
bool
PairsView::_HasBitmap(BList& bitmaps, BBitmap* bitmap)
{
// TODO: if this takes too long, we could build a hash value for each
// bitmap in a separate list
for (int32 i = bitmaps.CountItems(); i-- > 0;) {
BBitmap* item = (BBitmap*)bitmaps.ItemAtFast(i);
if (!memcmp(item->Bits(), bitmap->Bits(), item->BitsLength()))
return true;
}
return false;
}
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PairsView"
void
PairsView::_ReadRandomIcons()
{
// TODO: maybe read the icons only once at startup
// clean out any previous icons
for (int i = 0; i < fCardBitmaps.CountItems(); i++)
delete ((BBitmap*)fCardBitmaps.ItemAt(i));
fCardBitmaps.MakeEmpty();
BDirectory appsDirectory;
BDirectory prefsDirectory;
BPath path;
if (find_directory(B_BEOS_APPS_DIRECTORY, &path) == B_OK)
appsDirectory.SetTo(path.Path());
if (find_directory(B_BEOS_PREFERENCES_DIRECTORY, &path) == B_OK)
prefsDirectory.SetTo(path.Path());
// read vector icons from apps and prefs folder and put them
// into a BList as BBitmaps
BList bitmaps;
BEntry entry;
while (appsDirectory.GetNextEntry(&entry) == B_OK
|| prefsDirectory.GetNextEntry(&entry) == B_OK) {
BNode node(&entry);
BNodeInfo nodeInfo(&node);
if (nodeInfo.InitCheck() < B_OK)
continue;
uint8* data;
size_t size;
type_code type;
if (nodeInfo.GetIcon(&data, &size, &type) < B_OK)
continue;
if (type != B_VECTOR_ICON_TYPE) {
delete[] data;
continue;
}
BBitmap* bitmap = new BBitmap(
BRect(0, 0, kBitmapSize - 1, kBitmapSize - 1), 0, B_RGBA32);
if (BIconUtils::GetVectorIcon(data, size, bitmap) < B_OK) {
delete[] data;
delete bitmap;
continue;
}
delete[] data;
if (_HasBitmap(bitmaps, bitmap) || !bitmaps.AddItem(bitmap))
delete bitmap;
else if (bitmaps.CountItems() >= 128) {
// this is enough to choose from, stop eating memory...
break;
}
}
// pick random bitmaps from the ones we got in the list
srand((unsigned)time(0));
for (int i = 0; i < fNumOfCards / 2; i++) {
int32 index = rand() % bitmaps.CountItems();
BBitmap* bitmap = ((BBitmap*)bitmaps.RemoveItem(index));
if (bitmap == NULL) {
char buffer[512];
snprintf(buffer, sizeof(buffer), B_TRANSLATE("Pairs did not find "
"enough vector icons in the system; it needs at least %d."),
fNumOfCards / 2);
BString msgStr(buffer);
msgStr << "\n";
BAlert* alert = new BAlert("Fatal", msgStr.String(),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_WIDEST,
B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
exit(1);
}
fCardBitmaps.AddItem(bitmap);
}
// delete the remaining bitmaps from the list
while (BBitmap* bitmap = (BBitmap*)bitmaps.RemoveItem((int32)0))
delete bitmap;
}
void
PairsView::_SetPairsBoard()
{
for (int i = 0; i < fNumOfCards; i++) {
fButtonMessage = new BMessage(kMsgCardButton);
fButtonMessage->AddInt32("ButtonNum", i);
int x = i % fWidth * (kBitmapSize + kSpaceSize) + kSpaceSize;
int y = i / fHeight * (kBitmapSize + kSpaceSize) + kSpaceSize;
TopButton* button = new TopButton(x, y, fButtonMessage);
fDeckCard.AddItem(button);
AddChild(button);
}
}
void
PairsView::_GenerateCardPos()
{
_ReadRandomIcons();
srand((unsigned)time(0));
int* positions = new int[fNumOfCards];
for (int i = 0; i < fNumOfCards; i++)
positions[i] = i;
for (int i = fNumOfCards; i >= 1; i--) {
int index = rand() % i;
fRandPos[fNumOfCards - i] = positions[index];
for (int j = index; j < i - 1; j++)
positions[j] = positions[j + 1];
}
for (int i = 0; i < fNumOfCards; i++) {
fPosX[i] = (fRandPos[i]) % fWidth * (kBitmapSize + kSpaceSize)
+ kSpaceSize;
fPosY[i] = (fRandPos[i]) / fHeight * (kBitmapSize + kSpaceSize)
+ kSpaceSize;
}
delete [] positions;
BView::AttachedToWindow();
}
void
PairsView::Draw(BRect updateRect)
{
SetDrawingMode(B_OP_ALPHA);
BObjectList<BBitmap>* bitmapsList;
switch (fIconSize) {
case kSmallIconSize:
bitmapsList = fSmallBitmapsList;
break;
// draw rand pair 1 & 2
for (int i = 0; i < fNumOfCards; i++) {
BBitmap* bitmap = ((BBitmap*)fCardBitmaps.ItemAt(i % fNumOfCards / 2));
DrawBitmap(bitmap, BPoint(fPosX[i], fPosY[i]));
case kLargeIconSize:
bitmapsList = fLargeBitmapsList;
break;
case kMediumIconSize:
default:
bitmapsList = fMediumBitmapsList;
}
for (int32 i = 0; i < fButtonsCount; i++) {
SetDrawingMode(B_OP_ALPHA);
DrawBitmap(bitmapsList->ItemAt(i % (fButtonsCount / 2)),
BPoint(fPositionX[i], fPositionY[i]));
SetDrawingMode(B_OP_COPY);
}
}
int
PairsView::GetIconFromPos(int pos)
void
PairsView::FrameResized(float newWidth, float newHeight)
{
return fRandPos[pos];
int32 spacing = Spacing();
for (int32 i = 0; i < fButtonsCount; i++) {
PairsButton* button = fPairsButtonList->ItemAt(i);
if (button != NULL) {
button->ResizeTo(fIconSize, fIconSize);
int32 x = i % fRows * (fIconSize + spacing) + spacing;
int32 y = i / fCols * (fIconSize + spacing) + spacing;
button->MoveTo(x, y);
button->SetFontSize(fIconSize - 15);
}
}
_SetPositions();
Invalidate(BRect(0, 0, newWidth, newHeight));
BView::FrameResized(newWidth, newHeight);
}
int32
PairsView::GetIconPosition(int32 index)
{
return fRandomPosition[index];
}
// #pragma mark - PairsView private methods
void
PairsView::_GenerateCardPositions()
{
// seed the random number generator based on the current timestamp
srand((unsigned)time(0));
_ReadRandomIcons();
int32* positions = new int32[fButtonsCount];
for (int32 i = 0; i < fButtonsCount; i++)
positions[i] = i;
for (int32 i = fButtonsCount; i > 0; i--) {
int32 index = rand() % i;
fRandomPosition[fButtonsCount - i] = positions[index];
for (int32 j = index; j < i - 1; j++)
positions[j] = positions[j + 1];
}
delete[] positions;
_SetPositions();
}
void
PairsView::_ReadRandomIcons()
{
Pairs* app = dynamic_cast<Pairs*>(be_app);
if (app == NULL) // check if NULL to make Coverity happy
return;
// Create a copy of the icon map so we can erase elements from it as we
// add them to the list eliminating repeated icons without altering the
// orginal IconMap.
IconMap tmpIconMap(app->GetIconMap());
size_t mapSize = tmpIconMap.size();
if (mapSize < (size_t)fCardsCount) {
// not enough icons, we're screwed
return;
}
// clean out any previous icons
fSmallBitmapsList->MakeEmpty();
fMediumBitmapsList->MakeEmpty();
fLargeBitmapsList->MakeEmpty();
// pick bitmaps at random from the icon map
for (int32 i = 0; i < fCardsCount; i++) {
IconMap::iterator iter = tmpIconMap.begin();
if (mapSize < (size_t)fCardsCount) {
// not enough valid icons, we're really screwed
return;
}
std::advance(iter, rand() % mapSize);
size_t key = iter->first;
vector_icon* icon = iter->second;
BBitmap* smallBitmap = new BBitmap(
BRect(0, 0, kSmallIconSize - 1, kSmallIconSize - 1), B_RGBA32);
status_t smallResult = BIconUtils::GetVectorIcon(icon->data,
icon->size, smallBitmap);
BBitmap* mediumBitmap = new BBitmap(
BRect(0, 0, kMediumIconSize - 1, kMediumIconSize - 1), B_RGBA32);
status_t mediumResult = BIconUtils::GetVectorIcon(icon->data,
icon->size, mediumBitmap);
BBitmap* largeBitmap = new BBitmap(
BRect(0, 0, kLargeIconSize - 1, kLargeIconSize - 1), B_RGBA32);
status_t largeResult = BIconUtils::GetVectorIcon(icon->data,
icon->size, largeBitmap);
if (smallResult + mediumResult + largeResult == B_OK) {
fSmallBitmapsList->AddItem(smallBitmap);
fMediumBitmapsList->AddItem(mediumBitmap);
fLargeBitmapsList->AddItem(largeBitmap);
} else {
delete smallBitmap;
delete mediumBitmap;
delete largeBitmap;
i--;
}
mapSize -= tmpIconMap.erase(key);
// remove the element from the map so we don't read it again
}
}
void
PairsView::_SetPairsBoard()
{
int32 spacing = Spacing();
for (int32 i = 0; i < fButtonsCount; i++) {
BMessage* buttonMessage = new BMessage(kMsgCardButton);
buttonMessage->AddInt32("button number", i);
int32 x = i % fRows * (fIconSize + spacing) + spacing;
int32 y = i / fCols * (fIconSize + spacing) + spacing;
PairsButton* button = new PairsButton(x, y, fIconSize, buttonMessage);
fPairsButtonList->AddItem(button);
AddChild(button);
}
}
void
PairsView::_SetPositions()
{
int32 spacing = Spacing();
for (int32 i = 0; i < fButtonsCount; i++) {
fPositionX[i] = fRandomPosition[i] % fRows * (fIconSize + spacing) + spacing;
fPositionY[i] = fRandomPosition[i] / fCols * (fIconSize + spacing) + spacing;
}
}

View File

@ -12,40 +12,63 @@
#define PAIRS_VIEW_H
#include <ObjectList.h>
#include <View.h>
class TopButton;
const uint8 kSmallIconSize = 32;
const uint8 kMediumIconSize = 64;
const uint8 kLargeIconSize = 128;
const uint32 kMsgCardButton = 'card';
class BBitmap;
class PairsButton;
class PairsView : public BView {
public:
PairsView(BRect frame, const char* name,
int width, int height,
uint32 resizingMode);
uint8 rows, uint8 cols, uint8 iconSize);
virtual ~PairsView();
virtual void AttachedToWindow();
virtual void Draw(BRect updateRect);
virtual void FrameResized(float newWidth, float newHeight);
virtual void CreateGameBoard();
int fWidth;
int fHeight;
int fNumOfCards;
int32 Rows() const { return fRows; };
int32 Cols() const { return fCols; };
BObjectList<PairsButton>* PairsButtonList() const
{ return fPairsButtonList; };
BList fDeckCard;
int GetIconFromPos(int pos);
int32 GetIconPosition(int32 index);
int32 IconSize() const { return fIconSize; };
void SetIconSize(int32 size) { fIconSize = size; };
int32 Spacing() const { return fIconSize / 6; };
private:
void _SetPairsBoard();
void _GenerateCardPositions();
void _ReadRandomIcons();
void _GenerateCardPos();
bool _HasBitmap(BList& bitmaps, BBitmap* bitmap);
void _SetPairsBoard();
void _SetPositions();
BMessage* fButtonMessage;
BList fCardBitmaps;
int* fRandPos;
int* fPosX;
int* fPosY;
uint8 fRows;
uint8 fCols;
uint8 fIconSize;
int32 fButtonsCount;
int32 fCardsCount;
BObjectList<PairsButton>* fPairsButtonList;
BObjectList<BBitmap>* fSmallBitmapsList;
BObjectList<BBitmap>* fMediumBitmapsList;
BObjectList<BBitmap>* fLargeBitmapsList;
int32* fRandomPosition;
int32* fPositionX;
int32* fPositionY;
};

View File

@ -12,12 +12,11 @@
#include "PairsWindow.h"
#include <stdio.h>
#include <Application.h>
#include <Alert.h>
#include <Button.h>
#include <Catalog.h>
#include <ObjectList.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
@ -26,9 +25,8 @@
#include <TextView.h>
#include "Pairs.h"
#include "PairsGlobal.h"
#include "PairsButton.h"
#include "PairsView.h"
#include "PairsTopButton.h"
#undef B_TRANSLATION_CONTEXT
@ -36,8 +34,11 @@
const uint32 MENU_NEW = 'MGnw';
const uint32 MENU_SIZE = 'MGsz';
const uint32 MENU_DIFFICULTY = 'MGdf';
const uint32 MENU_QUIT = 'MGqu';
const uint32 MENU_ICON_SIZE = 'MSIs';
const uint32 kMsgPairComparing = 'pcom';
// #pragma mark - PairsWindow
@ -45,18 +46,19 @@ const uint32 MENU_QUIT = 'MGqu';
PairsWindow::PairsWindow()
:
BWindow(BRect(100, 100, 405, 423), B_TRANSLATE_SYSTEM_NAME("Pairs"),
BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Pairs"),
B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_QUIT_ON_WINDOW_CLOSE
| B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
| B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
fPairComparing(NULL),
fIsFirstClick(true),
fIsPairsActive(true),
fPairCard(0),
fPairCardTmp(0),
fButtonTmp(0),
fButton(0),
fPairCardPosition(0),
fPairCardTmpPosition(0),
fButtonTmpPosition(0),
fButtonPosition(0),
fButtonClicks(0),
fFinishPairs(0)
fFinishPairs(0),
fIconSizeMenu(NULL)
{
_MakeMenuBar();
_MakeGameView(4, 4);
@ -77,56 +79,94 @@ PairsWindow::_MakeMenuBar()
fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar");
AddChild(fMenuBar);
BMenu* menu = new BMenu(B_TRANSLATE("Game"));
fMenuBar->AddItem(menu);
BMenu* gameMenu = new BMenu(B_TRANSLATE("Game"));
fMenuBar->AddItem(gameMenu);
BMenuItem* menuItem;
menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("New"),
new BMessage(MENU_NEW), 'N'));
menu->AddSeparatorItem();
BMenu* newMenu = new BMenu(B_TRANSLATE("New"));
newMenu->SetRadioMode(true);
BMenu* sizeMenu = new BMenu(B_TRANSLATE("Size"));
sizeMenu->SetRadioMode(true);
BMessage* sizeMessage = new BMessage(MENU_SIZE);
sizeMessage->AddInt32("width", 4);
sizeMessage->AddInt32("height", 4);
sizeMenu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Beginner (4x4)"),
sizeMessage));
BMessage* difficultyMessage = new BMessage(MENU_DIFFICULTY);
difficultyMessage->AddInt32("rows", 4);
difficultyMessage->AddInt32("cols", 4);
newMenu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Beginner (4x4)"),
difficultyMessage));
menuItem->SetMarked(true);
sizeMessage = new BMessage(MENU_SIZE);
sizeMessage->AddInt32("width", 6);
sizeMessage->AddInt32("height", 6);
sizeMenu->AddItem(menuItem = new BMenuItem(
B_TRANSLATE("Intermediate (6x6)"), sizeMessage));
difficultyMessage = new BMessage(MENU_DIFFICULTY);
difficultyMessage->AddInt32("rows", 6);
difficultyMessage->AddInt32("cols", 6);
newMenu->AddItem(menuItem = new BMenuItem(
B_TRANSLATE("Intermediate (6x6)"), difficultyMessage));
sizeMessage = new BMessage(MENU_SIZE);
sizeMessage->AddInt32("width", 8);
sizeMessage->AddInt32("height", 8);
sizeMenu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Expert (8x8)"),
sizeMessage));
difficultyMessage = new BMessage(MENU_DIFFICULTY);
difficultyMessage->AddInt32("rows", 8);
difficultyMessage->AddInt32("cols", 8);
newMenu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Expert (8x8)"),
difficultyMessage));
menu->AddItem(sizeMenu);
menuItem = new BMenuItem(newMenu, new BMessage(MENU_NEW));
menuItem->SetShortcut('N', B_COMMAND_KEY);
gameMenu->AddItem(menuItem);
menu->AddSeparatorItem();
gameMenu->AddSeparatorItem();
menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Quit"),
gameMenu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Quit"),
new BMessage(MENU_QUIT), 'Q'));
fIconSizeMenu = new BMenu(B_TRANSLATE("Size"));
fIconSizeMenu->SetRadioMode(true);
fMenuBar->AddItem(fIconSizeMenu);
BMessage* iconSizeMessage = new BMessage(MENU_ICON_SIZE);
iconSizeMessage->AddInt32("size", kSmallIconSize);
fIconSizeMenu->AddItem(menuItem = new BMenuItem(
B_TRANSLATE("Small"), iconSizeMessage), 0);
iconSizeMessage = new BMessage(MENU_ICON_SIZE);
iconSizeMessage->AddInt32("size", kMediumIconSize);
fIconSizeMenu->AddItem(menuItem = new BMenuItem(
B_TRANSLATE("Medium"), iconSizeMessage), 1);
menuItem->SetMarked(true);
iconSizeMessage = new BMessage(MENU_ICON_SIZE);
iconSizeMessage->AddInt32("size", kLargeIconSize);
fIconSizeMenu->AddItem(menuItem = new BMenuItem(
B_TRANSLATE("Large"), iconSizeMessage), 2);
}
void
PairsWindow::_MakeGameView(int width, int height)
PairsWindow::_MakeGameView(uint8 rows, uint8 cols)
{
BRect viewBounds = Bounds();
viewBounds.top = fMenuBar->Bounds().Height() + 1;
fPairsView = new PairsView(viewBounds, "PairsView", width, height,
B_FOLLOW_NONE);
fPairsView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
uint8 iconSize;
BMenuItem* marked = fIconSizeMenu->FindMarked();
if (marked != NULL) {
switch (fIconSizeMenu->IndexOf(marked)) {
case 0:
iconSize = kSmallIconSize;
break;
case 2:
iconSize = kLargeIconSize;
break;
case 1:
default:
iconSize = kMediumIconSize;
}
} else {
iconSize = kMediumIconSize;
fIconSizeMenu->ItemAt(1)->SetMarked(true);
}
fPairsView = new PairsView(viewBounds, "PairsView", rows, cols, iconSize);
AddChild(fPairsView);
_ResizeWindow(rows, cols);
}
@ -140,14 +180,12 @@ PairsWindow::NewGame()
void
PairsWindow::SetGameSize(int width, int height)
PairsWindow::SetGameSize(uint8 rows, uint8 cols)
{
ResizeTo((kBitmapSize + kSpaceSize) * width + kSpaceSize,
(kBitmapSize + kSpaceSize) * height + kSpaceSize
+ fMenuBar->Bounds().Height());
RemoveChild(fPairsView);
delete fPairsView;
_MakeGameView(width, height);
_MakeGameView(rows, cols);
NewGame();
}
@ -160,112 +198,153 @@ PairsWindow::MessageReceived(BMessage* message)
NewGame();
break;
case MENU_SIZE:
case MENU_DIFFICULTY:
{
int32 width;
int32 height;
if (message->FindInt32("width", &width) == B_OK
&& message->FindInt32("height", &height) == B_OK) {
SetGameSize(width, height);
int32 rows;
int32 cols;
if (message->FindInt32("rows", &rows) == B_OK
&& message->FindInt32("cols", &cols) == B_OK) {
SetGameSize(rows, cols);
}
break;
}
case MENU_ICON_SIZE:
{
int32 size;
if (message->FindInt32("size", &size) == B_OK) {
fPairsView->SetIconSize(size);
_ResizeWindow(fPairsView->Rows(), fPairsView->Cols());
}
break;
}
case MENU_QUIT:
be_app->PostMessage(B_QUIT_REQUESTED);
break;
case kMsgCardButton:
if (fIsPairsActive) {
fButtonClicks++;
{
if (!fIsPairsActive)
break;
int32 num;
if (message->FindInt32("ButtonNum", &num) < B_OK)
int32 buttonNumber;
if (message->FindInt32("button number", &buttonNumber) != B_OK)
break;
BObjectList<PairsButton>* pairsButtonList
= fPairsView->PairsButtonList();
if (pairsButtonList == NULL)
break;
// look at what icon is behind a button
int32 buttonCount = pairsButtonList->CountItems();
for (int32 i = 0; i < buttonCount; i++) {
int32 iconPosition = fPairsView->GetIconPosition(i);
if (iconPosition == buttonNumber) {
fPairCardPosition = i % (buttonCount / 2);
fButtonPosition = iconPosition;
break;
// look what Icon is behind a button
for (int h = 0; h < fPairsView->fNumOfCards; h++) {
if (fPairsView->GetIconFromPos(h) == num) {
fPairCard = (h % fPairsView->fNumOfCards / 2);
fButton = fPairsView->GetIconFromPos(h);
break;
}
}
}
// gameplay
((TopButton*)fPairsView->fDeckCard.ItemAt(fButton))->Hide();
// gameplay
fButtonClicks++;
pairsButtonList->ItemAt(fButtonPosition)->Hide();
if (fIsFirstClick) {
fPairCardTmp = fPairCard;
fButtonTmp = fButton;
} else {
delete fPairComparing;
// message of message runner might not have arrived
// yet, so it is deleted here to prevent any leaking
// just in case
BMessage message(kMsgPairComparing);
fPairComparing = new BMessageRunner(BMessenger(this),
&message, 5 * 100000L, 1);
fIsPairsActive = false;
}
if (fIsFirstClick) {
fPairCardTmpPosition = fPairCardPosition;
fButtonTmpPosition = fButtonPosition;
} else {
delete fPairComparing;
// message of message runner might not have arrived
// yet, so it is deleted here to prevent any leaking
// just in case
BMessage message(kMsgPairComparing);
fPairComparing = new BMessageRunner(BMessenger(this),
&message, 5 * 100000L, 1);
fIsPairsActive = false;
}
fIsFirstClick = !fIsFirstClick;
fIsFirstClick = !fIsFirstClick;
break;
}
case kMsgPairComparing:
{
BObjectList<PairsButton>* pairsButtonList
= fPairsView->PairsButtonList();
if (pairsButtonList == NULL)
break;
delete fPairComparing;
fPairComparing = NULL;
fIsPairsActive = true;
if (fPairCardPosition == fPairCardTmpPosition)
fFinishPairs++;
else {
pairsButtonList->ItemAt(fButtonPosition)->Show();
pairsButtonList->ItemAt(fButtonTmpPosition)->Show();
}
// game end and results
if (fFinishPairs == pairsButtonList->CountItems() / 2) {
BString score;
score << fButtonClicks;
BString strAbout = B_TRANSLATE("%app%\n"
"\twritten by Ralf Schülke\n"
"\tCopyright 2008-2010, Haiku Inc.\n"
"\n"
"You completed the game in %num% clicks.\n");
strAbout.ReplaceFirst("%app%",
B_TRANSLATE_SYSTEM_NAME("Pairs"));
strAbout.ReplaceFirst("%num%", score);
BAlert* alert = new BAlert("about",
strAbout.String(),
B_TRANSLATE("New game"),
B_TRANSLATE("Quit game"));
BTextView* view = alert->TextView();
BFont font;
view->SetStylable(true);
view->GetFont(&font);
font.SetSize(18);
font.SetFace(B_BOLD_FACE);
view->SetFontAndColor(0,
strlen(B_TRANSLATE_SYSTEM_NAME("Pairs")), &font);
view->ResizeToPreferred();
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
NewGame();
else
be_app->PostMessage(B_QUIT_REQUESTED);
}
break;
case kMsgPairComparing:
delete fPairComparing;
fPairComparing = NULL;
fIsPairsActive = true;
if (fPairCard == fPairCardTmp)
fFinishPairs++;
else {
((TopButton*)fPairsView->fDeckCard.ItemAt(fButton))->Show();
((TopButton*)fPairsView->fDeckCard.ItemAt(fButtonTmp))->Show();
}
// game end and results
if (fFinishPairs == fPairsView->fNumOfCards / 2) {
BString score;
score << fButtonClicks;
BString strAbout = B_TRANSLATE("%app%\n"
"\twritten by Ralf Schülke\n"
"\tCopyright 2008-2010, Haiku Inc.\n"
"\n"
"You completed the game in %num% clicks.\n");
strAbout.ReplaceFirst("%app%",
B_TRANSLATE_SYSTEM_NAME("Pairs"));
strAbout.ReplaceFirst("%num%", score);
BAlert* alert = new BAlert("about",
strAbout.String(),
B_TRANSLATE("New game"),
B_TRANSLATE("Quit game"));
BTextView* view = alert->TextView();
BFont font;
view->SetStylable(true);
view->GetFont(&font);
font.SetSize(18);
font.SetFace(B_BOLD_FACE);
view->SetFontAndColor(0,
strlen(B_TRANSLATE_SYSTEM_NAME("Pairs")), &font);
view->ResizeToPreferred();
alert->SetShortcut(0, B_ESCAPE);
if (alert->Go() == 0)
NewGame();
else
be_app->PostMessage(B_QUIT_REQUESTED);
}
break;
}
default:
BWindow::MessageReceived(message);
}
}
// #pragma mark - PairsWindow private methods
void
PairsWindow::_ResizeWindow(uint8 rows, uint8 cols)
{
int32 iconSize = fPairsView->IconSize();
int32 spacing = fPairsView->Spacing();
ResizeTo((iconSize + spacing) * rows + spacing,
(iconSize + spacing) * cols + spacing + fMenuBar->Bounds().Height());
}

View File

@ -15,8 +15,9 @@
#include <Window.h>
class PairsView;
class BMenu;
class BMessageRunner;
class PairsView;
class PairsWindow : public BWindow {
@ -27,11 +28,12 @@ public:
virtual void MessageReceived(BMessage* message);
void NewGame();
void SetGameSize(int width, int height);
void SetGameSize(uint8 rows, uint8 cols);
private:
void _MakeGameView(int width, int height);
void _MakeGameView(uint8 rows, uint8 cols);
void _MakeMenuBar();
void _ResizeWindow(uint8 rows, uint8 cols);
BView* fBackgroundView;
PairsView* fPairsView;
@ -39,12 +41,13 @@ private:
BMessageRunner* fPairComparing;
bool fIsFirstClick;
bool fIsPairsActive;
int fPairCard;
int fPairCardTmp;
int fButtonTmp;
int fButton;
int fButtonClicks;
int fFinishPairs;
int32 fPairCardPosition;
int32 fPairCardTmpPosition;
int32 fButtonTmpPosition;
int32 fButtonPosition;
int32 fButtonClicks;
int32 fFinishPairs;
BMenu* fIconSizeMenu;
};
#endif // PAIRS_WINDOW_H