xref: /haiku/src/apps/bootmanager/DrivesPage.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 #include <Bitmap.h>
19 
20 #include <StringForSize.h>
21 
22 #include "BootDrive.h"
23 #include "WizardView.h"
24 
25 
26 #undef B_TRANSLATION_CONTEXT
27 #define B_TRANSLATION_CONTEXT "DrivesPage"
28 
29 
30 const uint32 kMsgSelectionChanged = 'slch';
31 
32 
33 class DriveItem : public BListItem {
34 public:
35 								DriveItem(const BDiskDevice& device,
36 									const BootMenuList& menus);
37 	virtual						~DriveItem();
38 
39 			bool				IsInstalled() const;
40 			bool				CanBeInstalled() const;
41 			bool				IsBootDrive() const;
42 			const char*			Path() const { return fPath.Path(); }
43 
44 			BootDrive*			Drive() { return fDrive; }
45 
46 protected:
47 	virtual void				DrawItem(BView* owner, BRect frame,
48 									bool complete = false);
49 	virtual	void				Update(BView* owner, const BFont* font);
50 
51 private:
52 			BootDrive*			fDrive;
53 			BBitmap*			fIcon;
54 			BString				fName;
55 			BPath				fPath;
56 			BString				fSize;
57 			float				fBaselineOffset;
58 			float				fSecondBaselineOffset;
59 			float				fSizeWidth;
60 			status_t			fCanBeInstalled;
61 			bool				fIsInstalled;
62 };
63 
64 
65 DriveItem::DriveItem(const BDiskDevice& device, const BootMenuList& menus)
66 	:
67 	fBaselineOffset(0),
68 	fSizeWidth(0)
69 {
70 	device.GetPath(&fPath);
71 	if (device.Name() != NULL && device.Name()[0])
72 		fName = device.Name();
73 	else if (strstr(fPath.Path(), "usb") != NULL)
74 		fName = B_TRANSLATE_COMMENT("USB Drive", "Default disk name");
75 	else
76 		fName = B_TRANSLATE_COMMENT("Hard Drive", "Default disk name");
77 
78 	fIcon = new BBitmap(BRect(BPoint(0, 0), be_control_look->ComposeIconSize(B_LARGE_ICON)),
79 		B_RGBA32);
80 	if (device.GetIcon(fIcon, B_LARGE_ICON) != B_OK)
81 		memset(fIcon->Bits(), 0, fIcon->BitsLength());
82 
83 	fDrive = new BootDrive(fPath.Path());
84 
85 	fIsInstalled = fDrive->InstalledMenu(menus) != NULL;
86 	fCanBeInstalled = fDrive->CanMenuBeInstalled(menus);
87 
88 	char buffer[256];
89 	fSize = string_for_size(device.Size(), buffer, sizeof(buffer));
90 }
91 
92 
93 DriveItem::~DriveItem()
94 {
95 	delete fDrive;
96 	delete fIcon;
97 }
98 
99 
100 bool
101 DriveItem::IsInstalled() const
102 {
103 	return fIsInstalled;
104 }
105 
106 
107 bool
108 DriveItem::CanBeInstalled() const
109 {
110 	return fCanBeInstalled == B_OK;
111 }
112 
113 
114 bool
115 DriveItem::IsBootDrive() const
116 {
117 	return fDrive->IsBootDrive();
118 }
119 
120 
121 void
122 DriveItem::DrawItem(BView* owner, BRect frame, bool complete)
123 {
124 	owner->PushState();
125 	owner->SetDrawingMode(B_OP_ALPHA);
126 
127 	if (IsSelected() || complete) {
128 		if (IsSelected()) {
129 			owner->SetHighColor(ui_color(B_LIST_SELECTED_BACKGROUND_COLOR));
130 			owner->SetLowColor(owner->HighColor());
131 		} else
132 			owner->SetHighColor(owner->LowColor());
133 
134 		owner->FillRect(frame);
135 	}
136 
137 	if (!IsEnabled()) {
138 		rgb_color textColor;
139 		if (IsSelected())
140 			textColor = ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR);
141 		else
142 			textColor = ui_color(B_LIST_ITEM_TEXT_COLOR);
143 
144 		if (textColor.red + textColor.green + textColor.blue > 128 * 3)
145 			owner->SetHighColor(tint_color(textColor, B_DARKEN_1_TINT));
146 		else
147 			owner->SetHighColor(tint_color(textColor, B_LIGHTEN_1_TINT));
148 	} else {
149 		if (IsSelected())
150 			owner->SetHighColor(ui_color(B_LIST_SELECTED_ITEM_TEXT_COLOR));
151 		else
152 			owner->SetHighColor(ui_color(B_LIST_ITEM_TEXT_COLOR));
153 	}
154 
155 
156 	// icon
157 	owner->MovePenTo(frame.left + 4, frame.top + 1);
158 	owner->DrawBitmap(fIcon);
159 
160 	// device
161 	owner->MovePenTo(frame.left + 8 + fIcon->Bounds().Width(),
162 		frame.top + fSecondBaselineOffset);
163 	owner->DrawString(fPath.Path());
164 
165 	// name
166 	BFont boldFont;
167 	BFont ownerFont;
168 	owner->GetFont(&ownerFont);
169 	owner->GetFont(&boldFont);
170 	boldFont.SetFace(B_BOLD_FACE);
171 	owner->SetFont(&boldFont);
172 
173 	BPoint namePosition(frame.left + 8 + fIcon->Bounds().Width(),
174 		frame.top + fBaselineOffset);
175 
176 	owner->MovePenTo(namePosition);
177 	owner->DrawString(fName.String());
178 
179 	float nameWidth = owner->StringWidth(fName.String());
180 	float messageWidth = frame.right - 4 - fSizeWidth
181 		- (frame.left + 8 + fIcon->Bounds().Width()) - nameWidth
182 		- fBaselineOffset * 2;
183 
184 	if (fCanBeInstalled != B_OK) {
185 		rgb_color highColor = owner->HighColor();
186 		owner->SetHighColor(ui_color(B_FAILURE_COLOR));
187 		owner->MovePenBy(fBaselineOffset, 0);
188 		const char* message;
189 		switch (fCanBeInstalled) {
190 			case B_PARTITION_TOO_SMALL:
191 				message = B_TRANSLATE_COMMENT("No space available!",
192 					"Cannot install");
193 				break;
194 			case B_ENTRY_NOT_FOUND:
195 				message = B_TRANSLATE_COMMENT("Incompatible format!",
196 					"Cannot install");
197 				break;
198 			case B_READ_ONLY_DEVICE:
199 				message = B_TRANSLATE_COMMENT("Read only!",
200 					"Cannot install");
201 				break;
202 			default:
203 				message = B_TRANSLATE_COMMENT("Cannot access!",
204 					"Cannot install");
205 				break;
206 		}
207 		BString truncatedMessage = message;
208 		owner->TruncateString(&truncatedMessage, B_TRUNCATE_END, messageWidth);
209 		owner->DrawString(truncatedMessage);
210 		owner->SetHighColor(highColor);
211 	}
212 	owner->SetFont(&ownerFont);
213 		// size
214 	BPoint sizePosition(frame.right - 4 - fSizeWidth,
215 		frame.top + fBaselineOffset);
216 	if (sizePosition.x > namePosition.x + nameWidth) {
217 		owner->MovePenTo(sizePosition);
218 		owner->DrawString(fSize.String());
219 	}
220 
221 	owner->PopState();
222 }
223 
224 
225 void
226 DriveItem::Update(BView* owner, const BFont* font)
227 {
228 	fSizeWidth = font->StringWidth(fSize.String());
229 
230 	BFont boldFont(font);
231 	boldFont.SetFace(B_BOLD_FACE);
232 	float width = 8 + boldFont.StringWidth(fPath.Path())
233 		+ be_control_look->DefaultItemSpacing() + fSizeWidth;
234 	float pathWidth = font->StringWidth(fPath.Path());
235 	if (width < pathWidth)
236 		width = pathWidth;
237 
238 	SetWidth(width);
239 
240 	font_height fheight;
241 	font->GetHeight(&fheight);
242 
243 	float lineHeight = ceilf(fheight.ascent) + ceilf(fheight.descent)
244 		+ ceilf(fheight.leading);
245 
246 	fBaselineOffset = 2 + ceilf(fheight.ascent + fheight.leading / 2);
247 	fSecondBaselineOffset = fBaselineOffset + lineHeight;
248 
249 	SetHeight(2 * lineHeight + 4);
250 }
251 
252 
253 // #pragma mark -
254 
255 
256 DrivesPage::DrivesPage(WizardView* wizardView, const BootMenuList& menus,
257 	BMessage* settings, const char* name)
258 	:
259 	WizardPageView(settings, name),
260 	fWizardView(wizardView),
261 	fHasInstallableItems(false)
262 {
263 	BString text;
264 	text << B_TRANSLATE_COMMENT("Drives", "Title") << "\n"
265 		<< B_TRANSLATE("Please select the drive you want the boot manager to "
266 			"be installed to or uninstalled from.");
267 	BTextView* description = CreateDescription("description", text);
268 	MakeHeading(description);
269 
270 	fDrivesView = new BListView("drives", B_SINGLE_SELECTION_LIST,
271 		B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
272 	fDrivesView->SetSelectionMessage(new BMessage(kMsgSelectionChanged));
273 
274 	BScrollView* scrollView = new BScrollView("scrollView", fDrivesView, 0,
275 		false, true);
276 
277 	SetLayout(new BGroupLayout(B_VERTICAL));
278 
279 	BLayoutBuilder::Group<>((BGroupLayout*)GetLayout())
280 		.Add(description)
281 		.Add(scrollView, 10.0);
282 
283 	_UpdateWizardButtons(NULL);
284 	_FillDrivesView(menus);
285 }
286 
287 
288 DrivesPage::~DrivesPage()
289 {
290 }
291 
292 
293 void
294 DrivesPage::PageCompleted()
295 {
296 	DriveItem* item = _SelectedDriveItem();
297 
298 	if (fSettings->ReplaceString("disk", item->Path()) != B_OK)
299 		fSettings->AddString("disk", item->Path());
300 }
301 
302 
303 void
304 DrivesPage::AttachedToWindow()
305 {
306 	fDrivesView->SetTarget(this);
307 }
308 
309 
310 void
311 DrivesPage::MessageReceived(BMessage* message)
312 {
313 	switch (message->what) {
314 		case kMsgSelectionChanged:
315 		{
316 			_UpdateWizardButtons(_SelectedDriveItem());
317 			break;
318 		}
319 
320 		default:
321 			WizardPageView::MessageReceived(message);
322 			break;
323 	}
324 }
325 
326 
327 /*!	Builds the list view items, and adds them to fDriveView.
328 	Sets the fHasInstallableItems member to indicate if there
329 	are any possible install targets. Automatically
330 	selects the boot drive.
331 */
332 void
333 DrivesPage::_FillDrivesView(const BootMenuList& menus)
334 {
335 	const char* selected = fSettings->FindString("disk");
336 
337 	BDiskDeviceRoster roster;
338 	BDiskDevice device;
339 	while (roster.GetNextDevice(&device) == B_OK) {
340 		if (device.HasMedia() && !device.IsReadOnly()) {
341 			DriveItem* item = new DriveItem(device, menus);
342 			if (item->CanBeInstalled())
343 				fHasInstallableItems = true;
344 			fDrivesView->AddItem(item);
345 
346 			if ((selected == NULL && item->IsBootDrive())
347 				|| (selected != NULL && !strcmp(item->Path(), selected))) {
348 				fDrivesView->Select(fDrivesView->CountItems() - 1);
349 				_UpdateWizardButtons(item);
350 			}
351 		}
352 	}
353 }
354 
355 
356 DriveItem*
357 DrivesPage::_SelectedDriveItem()
358 {
359 	return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection());
360 }
361 
362 
363 void
364 DrivesPage::_UpdateWizardButtons(DriveItem* item)
365 {
366 	fWizardView->SetPreviousButtonHidden(!fHasInstallableItems);
367 	fWizardView->SetPreviousButtonLabel(
368 		B_TRANSLATE_COMMENT("Uninstall", "Button"));
369 	if (item == NULL) {
370 		fWizardView->SetPreviousButtonEnabled(false);
371 		fWizardView->SetNextButtonEnabled(false);
372 	} else {
373 		fWizardView->SetPreviousButtonEnabled(
374 			item->CanBeInstalled() && item->IsInstalled());
375 		fWizardView->SetNextButtonEnabled(item->CanBeInstalled());
376 
377 		fWizardView->SetNextButtonLabel(
378 			item->IsInstalled() && item->CanBeInstalled()
379 				? B_TRANSLATE_COMMENT("Update", "Button")
380 				: B_TRANSLATE_COMMENT("Install", "Button"));
381 	}
382 
383 }
384