xref: /haiku/src/apps/bootmanager/DrivesPage.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
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 
29 const uint32 kMsgSelectionChanged = 'slch';
30 
31 
32 class DriveItem : public BListItem {
33 public:
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 
45 protected:
46 	virtual void				DrawItem(BView* owner, BRect frame,
47 									bool complete = false);
48 	virtual	void				Update(BView* owner, const BFont* font);
49 
50 private:
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 
63 DriveItem::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 
86 DriveItem::~DriveItem()
87 {
88 }
89 
90 
91 bool
92 DriveItem::IsInstalled() const
93 {
94 	return fIsInstalled;
95 }
96 
97 
98 bool
99 DriveItem::CanBeInstalled() const
100 {
101 	return fCanBeInstalled == B_OK;
102 }
103 
104 
105 bool
106 DriveItem::IsBootDrive() const
107 {
108 	return fDrive->IsBootDrive();
109 }
110 
111 
112 void
113 DriveItem::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 
163 void
164 DriveItem::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 
194 DrivesPage::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 
225 DrivesPage::~DrivesPage()
226 {
227 }
228 
229 
230 void
231 DrivesPage::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 
240 void
241 DrivesPage::AttachedToWindow()
242 {
243 	fDrivesView->SetTarget(this);
244 }
245 
246 
247 void
248 DrivesPage::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 */
269 void
270 DrivesPage::_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 
293 DriveItem*
294 DrivesPage::_SelectedDriveItem()
295 {
296 	return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection());
297 }
298 
299 
300 void
301 DrivesPage::_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