1/*
2 * Copyright 2008 Ralf Schülke, ralf.schuelke@googlemail.com.
3 * Copyright 2010 Adam Smith <adamd.smith@utoronto.ca>
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
6
7#include "PairsView.h"
8
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include <Alert.h>
14#include <Application.h>
15#include <Bitmap.h>
16#include <Button.h>
17#include <Catalog.h>
18#include <Directory.h>
19#include <Entry.h>
20#include <FindDirectory.h>
21#include <IconUtils.h>
22#include <List.h>
23#include <Node.h>
24#include <NodeInfo.h>
25#include <Path.h>
26
27#include "Pairs.h"
28#include "PairsGlobal.h"
29#include "PairsTopButton.h"
30
31PairsView::PairsView(BRect frame, const char* name, int width, int height,
32		uint32 resizingMode)
33	:
34	BView(frame, name, resizingMode, B_WILL_DRAW),
35	fWidth(width),
36	fHeight(height),
37	fNumOfCards(width * height),
38	fRandPos(new int[fNumOfCards]),
39	fPosX(new int[fNumOfCards]),
40	fPosY(new int[fNumOfCards])
41{
42	CreateGameBoard();
43	_SetPairsBoard();
44}
45
46
47void
48PairsView::CreateGameBoard()
49{
50	// Show hidden buttons
51	for (int32 i = 0; i < CountChildren(); i++) {
52		BView* child = ChildAt(i);
53		if (child->IsHidden())
54			child->Show();
55	}
56	_GenerateCardPos();
57}
58
59
60PairsView::~PairsView()
61{
62	for (int i = 0; i < fCardBitmaps.CountItems(); i++)
63		delete ((BBitmap*)fCardBitmaps.ItemAt(i));
64
65	for (int i = 0; i < fDeckCard.CountItems(); i++)
66		delete ((TopButton*)fDeckCard.ItemAt(i));
67
68	delete fRandPos;
69	delete fPosX;
70	delete fPosY;
71}
72
73
74void
75PairsView::AttachedToWindow()
76{
77	MakeFocus(true);
78}
79
80
81bool
82PairsView::_HasBitmap(BList& bitmaps, BBitmap* bitmap)
83{
84	// TODO: if this takes too long, we could build a hash value for each
85	// bitmap in a separate list
86	for (int32 i = bitmaps.CountItems(); i-- > 0;) {
87		BBitmap* item = (BBitmap*)bitmaps.ItemAtFast(i);
88		if (!memcmp(item->Bits(), bitmap->Bits(), item->BitsLength()))
89			return true;
90	}
91
92	return false;
93}
94
95#undef B_TRANSLATION_CONTEXT
96#define B_TRANSLATION_CONTEXT "PairsView"
97
98void
99PairsView::_ReadRandomIcons()
100{
101	// TODO: maybe read the icons only once at startup
102
103	// clean out any previous icons
104	for (int i = 0; i < fCardBitmaps.CountItems(); i++)
105		delete ((BBitmap*)fCardBitmaps.ItemAt(i));
106
107	fCardBitmaps.MakeEmpty();
108
109	BDirectory appsDirectory;
110	BDirectory prefsDirectory;
111
112	BPath path;
113	if (find_directory(B_BEOS_APPS_DIRECTORY, &path) == B_OK)
114		appsDirectory.SetTo(path.Path());
115	if (find_directory(B_BEOS_PREFERENCES_DIRECTORY, &path) == B_OK)
116		prefsDirectory.SetTo(path.Path());
117
118	// read vector icons from apps and prefs folder and put them
119	// into a BList as BBitmaps
120	BList bitmaps;
121
122	BEntry entry;
123	while (appsDirectory.GetNextEntry(&entry) == B_OK
124		|| prefsDirectory.GetNextEntry(&entry) == B_OK) {
125
126		BNode node(&entry);
127		BNodeInfo nodeInfo(&node);
128
129		if (nodeInfo.InitCheck() < B_OK)
130			continue;
131
132		uint8* data;
133		size_t size;
134		type_code type;
135
136		if (nodeInfo.GetIcon(&data, &size, &type) < B_OK)
137			continue;
138
139		if (type != B_VECTOR_ICON_TYPE) {
140			delete[] data;
141			continue;
142		}
143
144		BBitmap* bitmap = new BBitmap(
145			BRect(0, 0, kBitmapSize - 1, kBitmapSize - 1), 0, B_RGBA32);
146		if (BIconUtils::GetVectorIcon(data, size, bitmap) < B_OK) {
147			delete[] data;
148			delete bitmap;
149			continue;
150		}
151
152		delete[] data;
153
154		if (_HasBitmap(bitmaps, bitmap) || !bitmaps.AddItem(bitmap))
155			delete bitmap;
156		else if (bitmaps.CountItems() >= 128) {
157			// this is enough to choose from, stop eating memory...
158			break;
159		}
160	}
161
162	// pick random bitmaps from the ones we got in the list
163	srand((unsigned)time(0));
164
165	for (int i = 0; i < fNumOfCards / 2; i++) {
166		int32 index = rand() % bitmaps.CountItems();
167		BBitmap* bitmap = ((BBitmap*)bitmaps.RemoveItem(index));
168		if (bitmap == NULL) {
169			char buffer[512];
170			snprintf(buffer, sizeof(buffer), B_TRANSLATE("Pairs did not find "
171				"enough vector icons in the system; it needs at least %d."),
172				fNumOfCards / 2);
173			BString msgStr(buffer);
174			msgStr << "\n";
175			BAlert* alert = new BAlert("Fatal", msgStr.String(),
176				B_TRANSLATE("OK"), 	NULL, NULL, B_WIDTH_FROM_WIDEST,
177				B_STOP_ALERT);
178			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
179			alert->Go();
180			exit(1);
181		}
182		fCardBitmaps.AddItem(bitmap);
183	}
184
185	// delete the remaining bitmaps from the list
186	while (BBitmap* bitmap = (BBitmap*)bitmaps.RemoveItem((int32)0))
187		delete bitmap;
188}
189
190
191void
192PairsView::_SetPairsBoard()
193{
194	for (int i = 0; i < fNumOfCards; i++) {
195		fButtonMessage = new BMessage(kMsgCardButton);
196		fButtonMessage->AddInt32("ButtonNum", i);
197
198		int x =  i % fWidth * (kBitmapSize + kSpaceSize) + kSpaceSize;
199		int y =  i / fHeight * (kBitmapSize + kSpaceSize) + kSpaceSize;
200
201		TopButton* button = new TopButton(x, y, fButtonMessage);
202		fDeckCard.AddItem(button);
203		AddChild(button);
204	}
205}
206
207
208void
209PairsView::_GenerateCardPos()
210{
211	_ReadRandomIcons();
212
213	srand((unsigned)time(0));
214
215	int* positions = new int[fNumOfCards];
216	for (int i = 0; i < fNumOfCards; i++)
217		positions[i] = i;
218
219	for (int i = fNumOfCards; i >= 1; i--) {
220		int index = rand() % i;
221
222		fRandPos[fNumOfCards - i] = positions[index];
223
224		for (int j = index; j < i - 1; j++)
225			positions[j] = positions[j + 1];
226	}
227
228	for (int i = 0; i < fNumOfCards; i++) {
229		fPosX[i] = (fRandPos[i]) % fWidth * (kBitmapSize + kSpaceSize)
230			+ kSpaceSize;
231		fPosY[i] = (fRandPos[i]) / fHeight * (kBitmapSize + kSpaceSize)
232			+ kSpaceSize;
233	}
234
235	delete [] positions;
236}
237
238
239void
240PairsView::Draw(BRect updateRect)
241{
242	SetDrawingMode(B_OP_ALPHA);
243
244	// draw rand pair 1 & 2
245	for (int i = 0; i < fNumOfCards; i++) {
246		BBitmap* bitmap = ((BBitmap*)fCardBitmaps.ItemAt(i % fNumOfCards / 2));
247		DrawBitmap(bitmap, BPoint(fPosX[i], fPosY[i]));
248	}
249}
250
251
252int
253PairsView::GetIconFromPos(int pos)
254{
255	return fRandPos[pos];
256}
257