xref: /haiku/src/apps/bootmanager/DrivesPage.cpp (revision 529cd177b573aaba391c8adc9c9f5ad76a14bf81)
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 	if (IsSelected() || complete) {
127 		if (IsSelected()) {
128 			owner->SetHighColor(tint_color(owner->LowColor(), B_DARKEN_2_TINT));
129 			owner->SetLowColor(owner->HighColor());
130 		} else
131 			owner->SetHighColor(owner->LowColor());
132 
133 		owner->FillRect(frame);
134 	}
135 
136 	rgb_color black = {0, 0, 0, 255};
137 
138 	if (!IsEnabled())
139 		owner->SetHighColor(tint_color(black, B_LIGHTEN_2_TINT));
140 	else
141 		owner->SetHighColor(black);
142 
143 	// icon
144 	owner->MovePenTo(frame.left + 4, frame.top + 1);
145 	owner->DrawBitmap(fIcon);
146 
147 	// device
148 	owner->MovePenTo(frame.left + 8 + fIcon->Bounds().Width(), frame.top + fSecondBaselineOffset);
149 	owner->DrawString(fPath.Path());
150 
151 	// size
152 	owner->MovePenTo(frame.right - 4 - fSizeWidth, frame.top + fBaselineOffset);
153 	owner->DrawString(fSize.String());
154 
155 	// name
156 	BFont boldFont;
157 	owner->GetFont(&boldFont);
158 	boldFont.SetFace(B_BOLD_FACE);
159 	owner->SetFont(&boldFont);
160 
161 	owner->MovePenTo(frame.left + 8 + fIcon->Bounds().Width(), frame.top + fBaselineOffset);
162 	owner->DrawString(fName.String());
163 
164 	if (fCanBeInstalled != B_OK) {
165 		owner->SetHighColor(140, 0, 0);
166 		owner->MovePenBy(fBaselineOffset, 0);
167 		const char* message;
168 		switch (fCanBeInstalled) {
169 			case B_PARTITION_TOO_SMALL:
170 				message = B_TRANSLATE_COMMENT("No space available!",
171 					"Cannot install");
172 				break;
173 			case B_ENTRY_NOT_FOUND:
174 				message = B_TRANSLATE_COMMENT("Incompatible format!",
175 					"Cannot install");
176 				break;
177 			default:
178 				message = B_TRANSLATE_COMMENT("Cannot access!",
179 					"Cannot install");
180 				break;
181 		}
182 		owner->DrawString(message);
183 	}
184 
185 	owner->PopState();
186 }
187 
188 
189 void
190 DriveItem::Update(BView* owner, const BFont* font)
191 {
192 	fSizeWidth = font->StringWidth(fSize.String());
193 
194 	BFont boldFont(font);
195 	boldFont.SetFace(B_BOLD_FACE);
196 	float width = 8 + boldFont.StringWidth(fPath.Path())
197 		+ be_control_look->DefaultItemSpacing() + fSizeWidth;
198 	float pathWidth = font->StringWidth(fPath.Path());
199 	if (width < pathWidth)
200 		width = pathWidth;
201 
202 	SetWidth(width);
203 
204 	font_height fheight;
205 	font->GetHeight(&fheight);
206 
207 	float lineHeight = ceilf(fheight.ascent) + ceilf(fheight.descent)
208 		+ ceilf(fheight.leading);
209 
210 	fBaselineOffset = 2 + ceilf(fheight.ascent + fheight.leading / 2);
211 	fSecondBaselineOffset = fBaselineOffset + lineHeight;
212 
213 	SetHeight(2 * lineHeight + 4);
214 }
215 
216 
217 // #pragma mark -
218 
219 
220 DrivesPage::DrivesPage(WizardView* wizardView, const BootMenuList& menus,
221 	BMessage* settings, const char* name)
222 	:
223 	WizardPageView(settings, name),
224 	fWizardView(wizardView),
225 	fHasInstallableItems(false)
226 {
227 	BString text;
228 	text << B_TRANSLATE_COMMENT("Drives", "Title") << "\n"
229 		<< B_TRANSLATE("Please select the drive you want the boot manager to "
230 			"be installed to or uninstalled from.");
231 	BTextView* description = CreateDescription("description", text);
232 	MakeHeading(description);
233 
234 	fDrivesView = new BListView("drives");
235 	fDrivesView->SetSelectionMessage(new BMessage(kMsgSelectionChanged));
236 
237 	BScrollView* scrollView = new BScrollView("scrollView", fDrivesView, 0,
238 		false, true);
239 
240 	SetLayout(new BGroupLayout(B_VERTICAL));
241 
242 	BLayoutBuilder::Group<>((BGroupLayout*)GetLayout())
243 		.Add(description, 0.5)
244 		.Add(scrollView, 1);
245 
246 	_UpdateWizardButtons(NULL);
247 	_FillDrivesView(menus);
248 }
249 
250 
251 DrivesPage::~DrivesPage()
252 {
253 }
254 
255 
256 void
257 DrivesPage::PageCompleted()
258 {
259 	DriveItem* item = _SelectedDriveItem();
260 
261 	if (fSettings->ReplaceString("disk", item->Path()) != B_OK)
262 		fSettings->AddString("disk", item->Path());
263 }
264 
265 
266 void
267 DrivesPage::AttachedToWindow()
268 {
269 	fDrivesView->SetTarget(this);
270 }
271 
272 
273 void
274 DrivesPage::MessageReceived(BMessage* message)
275 {
276 	switch (message->what) {
277 		case kMsgSelectionChanged:
278 		{
279 			_UpdateWizardButtons(_SelectedDriveItem());
280 			break;
281 		}
282 
283 		default:
284 			WizardPageView::MessageReceived(message);
285 			break;
286 	}
287 }
288 
289 
290 /*!	Builds the list view items, and adds them to fDriveView.
291 	Sets the fHasInstallableItems member to indicate if there
292 	are any possible install targets. Automatically
293 	selects the boot drive.
294 */
295 void
296 DrivesPage::_FillDrivesView(const BootMenuList& menus)
297 {
298 	const char* selected = fSettings->FindString("disk");
299 
300 	BDiskDeviceRoster roster;
301 	BDiskDevice device;
302 	while (roster.GetNextDevice(&device) == B_OK) {
303 		if (device.HasMedia() && !device.IsReadOnly()) {
304 			DriveItem* item = new DriveItem(device, menus);
305 			if (item->CanBeInstalled())
306 				fHasInstallableItems = true;
307 			fDrivesView->AddItem(item);
308 
309 			if ((selected == NULL && item->IsBootDrive())
310 				|| (selected != NULL && !strcmp(item->Path(), selected))) {
311 				fDrivesView->Select(fDrivesView->CountItems() - 1);
312 				_UpdateWizardButtons(item);
313 			}
314 		}
315 	}
316 }
317 
318 
319 DriveItem*
320 DrivesPage::_SelectedDriveItem()
321 {
322 	return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection());
323 }
324 
325 
326 void
327 DrivesPage::_UpdateWizardButtons(DriveItem* item)
328 {
329 	fWizardView->SetPreviousButtonHidden(!fHasInstallableItems);
330 	fWizardView->SetPreviousButtonLabel(
331 		B_TRANSLATE_COMMENT("Uninstall", "Button"));
332 	if (item == NULL) {
333 		fWizardView->SetPreviousButtonEnabled(false);
334 		fWizardView->SetNextButtonEnabled(false);
335 	} else {
336 		fWizardView->SetPreviousButtonEnabled(
337 			item->CanBeInstalled() && item->IsInstalled());
338 		fWizardView->SetNextButtonEnabled(item->CanBeInstalled());
339 
340 		fWizardView->SetNextButtonLabel(
341 			item->IsInstalled() && item->CanBeInstalled()
342 				? B_TRANSLATE_COMMENT("Update", "Button")
343 				: B_TRANSLATE_COMMENT("Install", "Button"));
344 	}
345 
346 }
347