1/*
2 * Copyright 2011, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "DrivesPage.h"
8
9#include <Catalog.h>
10#include <ControlLook.h>
11#include <DiskDeviceRoster.h>
12#include <DiskDevice.h>
13#include <LayoutBuilder.h>
14#include <ListView.h>
15#include <Path.h>
16#include <ScrollView.h>
17#include <TextView.h>
18
19#include <StringForSize.h>
20
21#include "BootDrive.h"
22#include "WizardView.h"
23
24
25#undef B_TRANSLATION_CONTEXT
26#define B_TRANSLATION_CONTEXT "DrivesPage"
27
28
29const uint32 kMsgSelectionChanged = 'slch';
30
31
32class DriveItem : public BListItem {
33public:
34								DriveItem(const BDiskDevice& device,
35									const BootMenuList& menus);
36	virtual						~DriveItem();
37
38			bool				IsInstalled() const;
39			bool				CanBeInstalled() const;
40			bool				IsBootDrive() const;
41			const char*			Path() const { return fPath.Path(); }
42
43			BootDrive*			Drive() { return fDrive; }
44
45protected:
46	virtual void				DrawItem(BView* owner, BRect frame,
47									bool complete = false);
48	virtual	void				Update(BView* owner, const BFont* font);
49
50private:
51			BootDrive*			fDrive;
52			BString				fName;
53			BPath				fPath;
54			BString				fSize;
55			float				fBaselineOffset;
56			float				fSecondBaselineOffset;
57			float				fSizeWidth;
58			status_t			fCanBeInstalled;
59			bool				fIsInstalled;
60};
61
62
63DriveItem::DriveItem(const BDiskDevice& device, const BootMenuList& menus)
64	:
65	fBaselineOffset(0),
66	fSizeWidth(0)
67{
68	device.GetPath(&fPath);
69	if (device.Name() != NULL && device.Name()[0])
70		fName = device.Name();
71	else if (strstr(fPath.Path(), "usb") != NULL)
72		fName = B_TRANSLATE_COMMENT("USB Drive", "Default disk name");
73	else
74		fName = B_TRANSLATE_COMMENT("Hard Drive", "Default disk name");
75
76	fDrive = new BootDrive(fPath.Path());
77
78	fIsInstalled = fDrive->InstalledMenu(menus) != NULL;
79	fCanBeInstalled = fDrive->CanMenuBeInstalled(menus);
80
81	char buffer[256];
82	fSize = string_for_size(device.Size(), buffer, sizeof(buffer));
83}
84
85
86DriveItem::~DriveItem()
87{
88}
89
90
91bool
92DriveItem::IsInstalled() const
93{
94	return fIsInstalled;
95}
96
97
98bool
99DriveItem::CanBeInstalled() const
100{
101	return fCanBeInstalled == B_OK;
102}
103
104
105bool
106DriveItem::IsBootDrive() const
107{
108	return fDrive->IsBootDrive();
109}
110
111
112void
113DriveItem::DrawItem(BView* owner, BRect frame, bool complete)
114{
115	owner->PushState();
116
117	if (IsSelected() || complete) {
118		if (IsSelected()) {
119			owner->SetHighColor(tint_color(owner->LowColor(), B_DARKEN_2_TINT));
120			owner->SetLowColor(owner->HighColor());
121		} else
122			owner->SetHighColor(owner->LowColor());
123
124		owner->FillRect(frame);
125	}
126
127	rgb_color black = {0, 0, 0, 255};
128
129	if (!IsEnabled())
130		owner->SetHighColor(tint_color(black, B_LIGHTEN_2_TINT));
131	else
132		owner->SetHighColor(black);
133
134	// device
135	owner->MovePenTo(frame.left + 4, frame.top + fSecondBaselineOffset);
136	owner->DrawString(fPath.Path());
137
138	// size
139	owner->MovePenTo(frame.right - 4 - fSizeWidth, frame.top + fBaselineOffset);
140	owner->DrawString(fSize.String());
141
142	// name
143	BFont boldFont;
144	owner->GetFont(&boldFont);
145	boldFont.SetFace(B_BOLD_FACE);
146	owner->SetFont(&boldFont);
147
148	owner->MovePenTo(frame.left + 4, frame.top + fBaselineOffset);
149	owner->DrawString(fName.String());
150
151	if (fCanBeInstalled != B_OK) {
152		owner->SetHighColor(140, 0, 0);
153		owner->MovePenBy(fBaselineOffset, 0);
154		owner->DrawString(fCanBeInstalled == B_PARTITION_TOO_SMALL
155			? B_TRANSLATE_COMMENT("No space available!", "Cannot install")
156			: B_TRANSLATE_COMMENT("Cannot access!", "Cannot install"));
157	}
158
159	owner->PopState();
160}
161
162
163void
164DriveItem::Update(BView* owner, const BFont* font)
165{
166	fSizeWidth = font->StringWidth(fSize.String());
167
168	BFont boldFont(font);
169	boldFont.SetFace(B_BOLD_FACE);
170	float width = 8 + boldFont.StringWidth(fPath.Path())
171		+ be_control_look->DefaultItemSpacing() + fSizeWidth;
172	float pathWidth = font->StringWidth(fPath.Path());
173	if (width < pathWidth)
174		width = pathWidth;
175
176	SetWidth(width);
177
178	font_height fheight;
179	font->GetHeight(&fheight);
180
181	float lineHeight = ceilf(fheight.ascent) + ceilf(fheight.descent)
182		+ ceilf(fheight.leading);
183
184	fBaselineOffset = 2 + ceilf(fheight.ascent + fheight.leading / 2);
185	fSecondBaselineOffset = fBaselineOffset + lineHeight;
186
187	SetHeight(2 * lineHeight + 4);
188}
189
190
191// #pragma mark -
192
193
194DrivesPage::DrivesPage(WizardView* wizardView, const BootMenuList& menus,
195	BMessage* settings, const char* name)
196	:
197	WizardPageView(settings, name),
198	fWizardView(wizardView),
199	fHasInstallableItems(false)
200{
201	BString text;
202	text << B_TRANSLATE_COMMENT("Drives", "Title") << "\n"
203		<< B_TRANSLATE("Please select the drive you want the boot manager to "
204			"be installed to or uninstalled from.");
205	BTextView* description = CreateDescription("description", text);
206	MakeHeading(description);
207
208	fDrivesView = new BListView("drives");
209	fDrivesView->SetSelectionMessage(new BMessage(kMsgSelectionChanged));
210
211	BScrollView* scrollView = new BScrollView("scrollView", fDrivesView, 0,
212		false, true);
213
214	SetLayout(new BGroupLayout(B_VERTICAL));
215
216	BLayoutBuilder::Group<>((BGroupLayout*)GetLayout())
217		.Add(description, 0.5)
218		.Add(scrollView, 1);
219
220	_UpdateWizardButtons(NULL);
221	_FillDrivesView(menus);
222}
223
224
225DrivesPage::~DrivesPage()
226{
227}
228
229
230void
231DrivesPage::PageCompleted()
232{
233	DriveItem* item = _SelectedDriveItem();
234
235	if (fSettings->ReplaceString("disk", item->Path()) != B_OK)
236		fSettings->AddString("disk", item->Path());
237}
238
239
240void
241DrivesPage::AttachedToWindow()
242{
243	fDrivesView->SetTarget(this);
244}
245
246
247void
248DrivesPage::MessageReceived(BMessage* message)
249{
250	switch (message->what) {
251		case kMsgSelectionChanged:
252		{
253			_UpdateWizardButtons(_SelectedDriveItem());
254			break;
255		}
256
257		default:
258			WizardPageView::MessageReceived(message);
259			break;
260	}
261}
262
263
264/*!	Builds the list view items, and adds them to fDriveView.
265	Sets the fHasInstallableItems member to indicate if there
266	are any possible install targets. Automatically
267	selects the boot drive.
268*/
269void
270DrivesPage::_FillDrivesView(const BootMenuList& menus)
271{
272	const char* selected = fSettings->FindString("disk");
273
274	BDiskDeviceRoster roster;
275	BDiskDevice device;
276	while (roster.GetNextDevice(&device) == B_OK) {
277		if (device.HasMedia() && !device.IsReadOnly()) {
278			DriveItem* item = new DriveItem(device, menus);
279			if (item->CanBeInstalled())
280				fHasInstallableItems = true;
281			fDrivesView->AddItem(item);
282
283			if ((selected == NULL && item->IsBootDrive())
284				|| (selected != NULL && !strcmp(item->Path(), selected))) {
285				fDrivesView->Select(fDrivesView->CountItems() - 1);
286				_UpdateWizardButtons(item);
287			}
288		}
289	}
290}
291
292
293DriveItem*
294DrivesPage::_SelectedDriveItem()
295{
296	return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection());
297}
298
299
300void
301DrivesPage::_UpdateWizardButtons(DriveItem* item)
302{
303	fWizardView->SetPreviousButtonHidden(!fHasInstallableItems);
304	if (fHasInstallableItems) {
305		fWizardView->SetPreviousButtonLabel(
306			B_TRANSLATE_COMMENT("Uninstall", "Button"));
307		if (item == NULL) {
308			fWizardView->SetPreviousButtonEnabled(false);
309			fWizardView->SetNextButtonEnabled(false);
310		} else {
311			fWizardView->SetPreviousButtonEnabled(
312				item->CanBeInstalled() && item->IsInstalled());
313			fWizardView->SetNextButtonEnabled(item->CanBeInstalled());
314
315			fWizardView->SetNextButtonLabel(
316				item->IsInstalled() && item->CanBeInstalled()
317					? B_TRANSLATE_COMMENT("Update", "Button")
318					: B_TRANSLATE_COMMENT("Install", "Button"));
319		}
320	} else {
321		fWizardView->SetNextButtonLabel(
322			B_TRANSLATE_COMMENT("Quit", "Button"));
323		fWizardView->SetPreviousButtonEnabled(false);
324		fWizardView->SetNextButtonEnabled(false);
325		return;
326	}
327
328}
329