1/*
2 * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
3 * Copyright 2005, Jérôme DUVAL.
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
6
7#include "PackageViews.h"
8
9#include <stdio.h>
10
11#include <Catalog.h>
12#include <ControlLook.h>
13#include <Directory.h>
14#include <Entry.h>
15#include <fs_attr.h>
16#include <LayoutUtils.h>
17#include <Locale.h>
18#include <Messenger.h>
19#include <ScrollBar.h>
20#include <String.h>
21#include <View.h>
22#include <Window.h>
23
24#include "InstallerDefs.h"
25#include "StringForSize.h"
26
27
28#undef B_TRANSLATION_CONTEXT
29#define B_TRANSLATION_CONTEXT "PackagesView"
30
31#define ICON_ATTRIBUTE "INSTALLER PACKAGE: ICON"
32
33
34Package::Package(const char *folder)
35	:
36	Group(),
37	fSize(0),
38	fIcon(NULL)
39{
40	SetFolder(folder);
41}
42
43
44Package::~Package()
45{
46	delete fIcon;
47}
48
49
50Package *
51Package::PackageFromEntry(BEntry &entry)
52{
53	char folder[B_FILE_NAME_LENGTH];
54	entry.GetName(folder);
55	BDirectory directory(&entry);
56	if (directory.InitCheck() != B_OK)
57		return NULL;
58	Package *package = new Package(folder);
59	bool alwaysOn;
60	bool onByDefault;
61	int32 size;
62	char group[64];
63	memset(group, 0, 64);
64	if (directory.ReadAttr("INSTALLER PACKAGE: NAME", B_STRING_TYPE, 0,
65		package->fName, 64) < 0) {
66		goto err;
67	}
68	if (directory.ReadAttr("INSTALLER PACKAGE: GROUP", B_STRING_TYPE, 0,
69		group, 64) < 0) {
70		goto err;
71	}
72	if (directory.ReadAttr("INSTALLER PACKAGE: DESCRIPTION", B_STRING_TYPE, 0,
73		package->fDescription, 64) < 0) {
74		goto err;
75	}
76	if (directory.ReadAttr("INSTALLER PACKAGE: ON_BY_DEFAULT", B_BOOL_TYPE, 0,
77		&onByDefault, sizeof(onByDefault)) < 0) {
78		goto err;
79	}
80	if (directory.ReadAttr("INSTALLER PACKAGE: ALWAYS_ON", B_BOOL_TYPE, 0,
81		&alwaysOn, sizeof(alwaysOn)) < 0) {
82		goto err;
83	}
84	if (directory.ReadAttr("INSTALLER PACKAGE: SIZE", B_INT32_TYPE, 0,
85		&size, sizeof(size)) < 0) {
86		goto err;
87	}
88	package->SetGroupName(group);
89	package->SetSize(size);
90	package->SetAlwaysOn(alwaysOn);
91	package->SetOnByDefault(onByDefault);
92
93	attr_info info;
94	if (directory.GetAttrInfo(ICON_ATTRIBUTE, &info) == B_OK) {
95		char buffer[info.size];
96		BMessage msg;
97		if ((directory.ReadAttr(ICON_ATTRIBUTE, info.type, 0, buffer,
98				info.size) == info.size)
99			&& (msg.Unflatten(buffer) == B_OK)) {
100			package->SetIcon(new BBitmap(&msg));
101		}
102	}
103	return package;
104err:
105	delete package;
106	return NULL;
107}
108
109
110void
111Package::GetSizeAsString(char* string, size_t stringSize)
112{
113	string_for_size(fSize, string, stringSize);
114}
115
116
117Group::Group()
118{
119
120}
121
122Group::~Group()
123{
124}
125
126
127PackageCheckBox::PackageCheckBox(BRect rect, Package *item)
128	:
129	BCheckBox(rect.OffsetBySelf(7, 0), "pack_cb", item->Name(), NULL),
130	fPackage(item)
131{
132}
133
134
135PackageCheckBox::~PackageCheckBox()
136{
137	delete fPackage;
138}
139
140
141void
142PackageCheckBox::Draw(BRect update)
143{
144	BCheckBox::Draw(update);
145	char string[15];
146	fPackage->GetSizeAsString(string, sizeof(string));
147	float width = StringWidth(string);
148	DrawString(string, BPoint(Bounds().right - width - 8, 11));
149
150	const BBitmap *icon = fPackage->Icon();
151	if (icon)
152		DrawBitmap(icon, BPoint(Bounds().right - 92, 0));
153}
154
155
156void
157PackageCheckBox::MouseMoved(BPoint point, uint32 transit,
158	const BMessage* dragMessage)
159{
160	printf("%s called\n", __PRETTY_FUNCTION__);
161	if (transit == B_ENTERED_VIEW) {
162		BMessage msg(MSG_STATUS_MESSAGE);
163		msg.AddString("status", fPackage->Description());
164		BMessenger(NULL, Window()).SendMessage(&msg);
165	} else if (transit == B_EXITED_VIEW) {
166		BMessage msg(MSG_STATUS_MESSAGE);
167		BMessenger(NULL, Window()).SendMessage(&msg);
168	}
169}
170
171
172GroupView::GroupView(BRect rect, Group *group)
173	:
174	BStringView(rect, "group", group->GroupName()),
175	fGroup(group)
176{
177	SetFont(be_bold_font);
178}
179
180
181GroupView::~GroupView()
182{
183	delete fGroup;
184}
185
186
187// #pragma mark -
188
189
190PackagesView::PackagesView(BRect rect, const char* name)
191	:
192	BView(rect, name, B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FRAME_EVENTS)
193{
194}
195
196
197PackagesView::PackagesView(const char* name)
198	:
199	BView(name, B_WILL_DRAW | B_FRAME_EVENTS)
200{
201}
202
203
204PackagesView::~PackagesView()
205{
206
207}
208
209
210void
211PackagesView::Clean()
212{
213	BView* view;
214	while ((view = ChildAt(0))) {
215		if (dynamic_cast<GroupView*>(view)
216			|| dynamic_cast<PackageCheckBox*>(view)) {
217			RemoveChild(view);
218			delete view;
219		}
220	}
221	ScrollTo(0, 0);
222}
223
224
225void
226PackagesView::AddPackages(BList& packages, BMessage* msg)
227{
228	int32 count = packages.CountItems();
229	BRect rect = Bounds();
230	BRect bounds = rect;
231	rect.left = 1;
232	rect.bottom = 15;
233	rect.top = 0;
234	BString lastGroup = "";
235	for (int32 i = 0; i < count; i++) {
236		void* item = packages.ItemAt(i);
237		Package* package = static_cast<Package*>(item);
238		if (lastGroup != BString(package->GroupName())) {
239			rect.OffsetBy(0, 1);
240			lastGroup = package->GroupName();
241			Group* group = new Group();
242			group->SetGroupName(package->GroupName());
243			GroupView *view = new GroupView(rect, group);
244			AddChild(view);
245			rect.OffsetBy(0, 17);
246		}
247		PackageCheckBox* checkBox = new PackageCheckBox(rect, package);
248		checkBox->SetValue(package->OnByDefault()
249			? B_CONTROL_ON : B_CONTROL_OFF);
250		checkBox->SetEnabled(!package->AlwaysOn());
251		checkBox->SetMessage(new BMessage(*msg));
252		AddChild(checkBox);
253		rect.OffsetBy(0, 20);
254	}
255	ResizeTo(bounds.Width(), rect.top);
256	Invalidate();
257}
258
259
260void
261PackagesView::GetTotalSizeAsString(char* string, size_t stringSize)
262{
263	int32 count = CountChildren();
264	int32 size = 0;
265	for (int32 i = 0; i < count; i++) {
266		PackageCheckBox* cb = dynamic_cast<PackageCheckBox*>(ChildAt(i));
267		if (cb && cb->Value())
268			size += cb->GetPackage()->Size();
269	}
270	string_for_size(size, string, stringSize);
271}
272
273
274void
275PackagesView::GetPackagesToInstall(BList* list, int32* size)
276{
277	int32 count = CountChildren();
278	*size = 0;
279	for (int32 i = 0; i < count; i++) {
280		PackageCheckBox* cb = dynamic_cast<PackageCheckBox*>(ChildAt(i));
281		if (cb && cb->Value()) {
282			list->AddItem(cb->GetPackage());
283			*size += cb->GetPackage()->Size();
284		}
285	}
286}
287
288
289void
290PackagesView::FrameResized(float width, float height)
291{
292	if (CountChildren() == 0)
293		Invalidate();
294
295	BScrollBar* scrollBar = ScrollBar(B_VERTICAL);
296	if (scrollBar == NULL)
297		return;
298
299	float virtualHeight = 0.0;
300
301	int32 count = CountChildren();
302	if (count > 0) {
303		BView* child = ChildAt(count - 1);
304		virtualHeight = child->Frame().bottom;
305	}
306
307	if (height > virtualHeight) {
308		scrollBar->SetRange(0.0f, 0.0f);
309		scrollBar->SetValue(0.0f);
310	} else {
311		scrollBar->SetRange(0.0f, virtualHeight - height);
312		scrollBar->SetProportion(height / virtualHeight);
313	}
314
315	scrollBar->SetSteps(15, height);
316}
317
318
319void
320PackagesView::Draw(BRect updateRect)
321{
322	if (CountChildren() > 0)
323		return;
324
325	be_control_look->DrawLabel(this,
326		B_TRANSLATE("No optional packages available."),
327		Bounds(), updateRect, ViewColor(), BControlLook::B_DISABLED,
328		BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE));
329}
330
331
332void
333PackagesView::GetPreferredSize(float* _width, float* _height)
334{
335	// TODO: Something more nice as default? I need to see how this looks
336	// when there are actually any packages...
337	if (_width != NULL)
338		*_width = 400.0;
339
340	if (_height != NULL)
341		*_height = 80.0;
342}
343
344
345BSize
346PackagesView::MaxSize()
347{
348	return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
349		BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
350}
351
352