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;
Path() const42 const char* Path() const { return fPath.Path(); }
43
Drive()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
DriveItem(const BDiskDevice & device,const BootMenuList & menus)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
~DriveItem()93 DriveItem::~DriveItem()
94 {
95 delete fDrive;
96 delete fIcon;
97 }
98
99
100 bool
IsInstalled() const101 DriveItem::IsInstalled() const
102 {
103 return fIsInstalled;
104 }
105
106
107 bool
CanBeInstalled() const108 DriveItem::CanBeInstalled() const
109 {
110 return fCanBeInstalled == B_OK;
111 }
112
113
114 bool
IsBootDrive() const115 DriveItem::IsBootDrive() const
116 {
117 return fDrive->IsBootDrive();
118 }
119
120
121 void
DrawItem(BView * owner,BRect frame,bool complete)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
Update(BView * owner,const BFont * font)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
DrivesPage(WizardView * wizardView,const BootMenuList & menus,BMessage * settings,const char * name)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
~DrivesPage()288 DrivesPage::~DrivesPage()
289 {
290 }
291
292
293 void
PageCompleted()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
AttachedToWindow()304 DrivesPage::AttachedToWindow()
305 {
306 fDrivesView->SetTarget(this);
307 }
308
309
310 void
MessageReceived(BMessage * message)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
_FillDrivesView(const BootMenuList & menus)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*
_SelectedDriveItem()357 DrivesPage::_SelectedDriveItem()
358 {
359 return (DriveItem*)fDrivesView->ItemAt(fDrivesView->CurrentSelection());
360 }
361
362
363 void
_UpdateWizardButtons(DriveItem * item)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