xref: /haiku/src/apps/bootmanager/DrivesPage.cpp (revision fce4895d1884da5ae6fb299d23c735c598e690b1)
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(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1),
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 			default:
199 				message = B_TRANSLATE_COMMENT("Cannot access!",
200 					"Cannot install");
201 				break;
202 		}
203 		BString truncatedMessage = message;
204 		owner->TruncateString(&truncatedMessage, B_TRUNCATE_END, messageWidth);
205 		owner->DrawString(truncatedMessage);
206 		owner->SetHighColor(highColor);
207 	}
208 	owner->SetFont(&ownerFont);
209 		// size
210 	BPoint sizePosition(frame.right - 4 - fSizeWidth,
211 		frame.top + fBaselineOffset);
212 	if (sizePosition.x > namePosition.x + nameWidth) {
213 		owner->MovePenTo(sizePosition);
214 		owner->DrawString(fSize.String());
215 	}
216 
217 	owner->PopState();
218 }
219 
220 
221 void
222 DriveItem::Update(BView* owner, const BFont* font)
223 {
224 	fSizeWidth = font->StringWidth(fSize.String());
225 
226 	BFont boldFont(font);
227 	boldFont.SetFace(B_BOLD_FACE);
228 	float width = 8 + boldFont.StringWidth(fPath.Path())
229 		+ be_control_look->DefaultItemSpacing() + fSizeWidth;
230 	float pathWidth = font->StringWidth(fPath.Path());
231 	if (width < pathWidth)
232 		width = pathWidth;
233 
234 	SetWidth(width);
235 
236 	font_height fheight;
237 	font->GetHeight(&fheight);
238 
239 	float lineHeight = ceilf(fheight.ascent) + ceilf(fheight.descent)
240 		+ ceilf(fheight.leading);
241 
242 	fBaselineOffset = 2 + ceilf(fheight.ascent + fheight.leading / 2);
243 	fSecondBaselineOffset = fBaselineOffset + lineHeight;
244 
245 	SetHeight(2 * lineHeight + 4);
246 }
247 
248 
249 // #pragma mark -
250 
251 
252 DrivesPage::DrivesPage(WizardView* wizardView, const BootMenuList& menus,
253 	BMessage* settings, const char* name)
254 	:
255 	WizardPageView(settings, name),
256 	fWizardView(wizardView),
257 	fHasInstallableItems(false)
258 {
259 	BString text;
260 	text << B_TRANSLATE_COMMENT("Drives", "Title") << "\n"
261 		<< B_TRANSLATE("Please select the drive you want the boot manager to "
262 			"be installed to or uninstalled from.");
263 	BTextView* description = CreateDescription("description", text);
264 	MakeHeading(description);
265 
266 	fDrivesView = new BListView("drives", B_SINGLE_SELECTION_LIST,
267 		B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE | B_FULL_UPDATE_ON_RESIZE);
268 	fDrivesView->SetSelectionMessage(new BMessage(kMsgSelectionChanged));
269 
270 	BScrollView* scrollView = new BScrollView("scrollView", fDrivesView, 0,
271 		false, true);
272 
273 	SetLayout(new BGroupLayout(B_VERTICAL));
274 
275 	BLayoutBuilder::Group<>((BGroupLayout*)GetLayout())
276 		.Add(description, 0.5)
277 		.Add(scrollView, 1);
278 
279 	_UpdateWizardButtons(NULL);
280 	_FillDrivesView(menus);
281 }
282 
283 
284 DrivesPage::~DrivesPage()
285 {
286 }
287 
288 
289 void
290 DrivesPage::PageCompleted()
291 {
292 	DriveItem* item = _SelectedDriveItem();
293 
294 	if (fSettings->ReplaceString("disk", item->Path()) != B_OK)
295 		fSettings->AddString("disk", item->Path());
296 }
297 
298 
299 void
300 DrivesPage::AttachedToWindow()
301 {
302 	fDrivesView->SetTarget(this);
303 }
304 
305 
306 void
307 DrivesPage::MessageReceived(BMessage* message)
308 {
309 	switch (message->what) {
310 		case kMsgSelectionChanged:
311 		{
312 			_UpdateWizardButtons(_SelectedDriveItem());
313 			break;
314 		}
315 
316 		default:
317 			WizardPageView::MessageReceived(message);
318 			break;
319 	}
320 }
321 
322 
323 /*!	Builds the list view items, and adds them to fDriveView.
324 	Sets the fHasInstallableItems member to indicate if there
325 	are any possible install targets. Automatically
326 	selects the boot drive.
327 */
328 void
329 DrivesPage::_FillDrivesView(const BootMenuList& menus)
330 {
331 	const char* selected = fSettings->FindString("disk");
332 
333 	BDiskDeviceRoster roster;
334 	BDiskDevice device;
335 	while (roster.GetNextDevice(&device) == B_OK) {
336 		if (device.HasMedia() && !device.IsReadOnly()) {
337 			DriveItem* item = new DriveItem(device, menus);
338 			if (item->CanBeInstalled())
339 				fHasInstallableItems = true;
340 			fDrivesView->AddItem(item);
341 
342 			if ((selected == NULL && item->IsBootDrive())
343 				|| (selected != NULL && !strcmp(item->Path(), selected))) {
344 				fDrivesView->Select(fDrivesView->CountItems() - 1);
345 				_UpdateWizardButtons(item);
346 			}
347 		}
348 	}
349 }
350 
351 
352 DriveItem*
353 DrivesPage::_SelectedDriveItem()
354 {
355 	return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection());
356 }
357 
358 
359 void
360 DrivesPage::_UpdateWizardButtons(DriveItem* item)
361 {
362 	fWizardView->SetPreviousButtonHidden(!fHasInstallableItems);
363 	fWizardView->SetPreviousButtonLabel(
364 		B_TRANSLATE_COMMENT("Uninstall", "Button"));
365 	if (item == NULL) {
366 		fWizardView->SetPreviousButtonEnabled(false);
367 		fWizardView->SetNextButtonEnabled(false);
368 	} else {
369 		fWizardView->SetPreviousButtonEnabled(
370 			item->CanBeInstalled() && item->IsInstalled());
371 		fWizardView->SetNextButtonEnabled(item->CanBeInstalled());
372 
373 		fWizardView->SetNextButtonLabel(
374 			item->IsInstalled() && item->CanBeInstalled()
375 				? B_TRANSLATE_COMMENT("Update", "Button")
376 				: B_TRANSLATE_COMMENT("Install", "Button"));
377 	}
378 
379 }
380