1 /*
2 * Copyright 2020-2023, Panagiotis "Ivory" Vasilopoulos <git@n0toose.net>
3 * Copyright 2009-2010, Stephan Aßmus <superstippi@gmx.de>
4 * Copyright 2005-2008, Jérôme DUVAL
5 * All rights reserved. Distributed under the terms of the MIT License.
6 */
7
8
9 #include "InstallerWindow.h"
10
11 #include <stdio.h>
12 #include <strings.h>
13
14 #include <Alert.h>
15 #include <Application.h>
16 #include <Autolock.h>
17 #include <Box.h>
18 #include <Button.h>
19 #include <Catalog.h>
20 #include <ColorConversion.h>
21 #include <ControlLook.h>
22 #include <Directory.h>
23 #include <FindDirectory.h>
24 #include <LayoutBuilder.h>
25 #include <LayoutUtils.h>
26 #include <Locale.h>
27 #include <MenuBar.h>
28 #include <MenuField.h>
29 #include <Path.h>
30 #include <PopUpMenu.h>
31 #include <Roster.h>
32 #include <Screen.h>
33 #include <ScrollView.h>
34 #include <SeparatorView.h>
35 #include <SpaceLayoutItem.h>
36 #include <StatusBar.h>
37 #include <String.h>
38 #include <TextView.h>
39 #include <TranslationUtils.h>
40 #include <TranslatorFormats.h>
41
42 #include "tracker_private.h"
43
44 #include "DialogPane.h"
45 #include "InstallerDefs.h"
46 #include "PackageViews.h"
47 #include "PartitionMenuItem.h"
48 #include "WorkerThread.h"
49
50
51 #undef B_TRANSLATION_CONTEXT
52 #define B_TRANSLATION_CONTEXT "InstallerWindow"
53
54
55 static const char* kDriveSetupSignature = "application/x-vnd.Haiku-DriveSetup";
56 static const char* kBootManagerSignature = "application/x-vnd.Haiku-BootManager";
57
58 const uint32 BEGIN_MESSAGE = 'iBGN';
59 const uint32 SHOW_BOTTOM_MESSAGE = 'iSBT';
60 const uint32 LAUNCH_DRIVE_SETUP = 'iSEP';
61 const uint32 LAUNCH_BOOTMAN = 'iWBM';
62 const uint32 START_SCAN = 'iSSC';
63 const uint32 PACKAGE_CHECKBOX = 'iPCB';
64 const uint32 ENCOURAGE_DRIVESETUP = 'iENC';
65
66
67 class LogoView : public BView {
68 public:
69 LogoView(const BRect& frame);
70 LogoView();
71 virtual ~LogoView();
72
73 virtual void Draw(BRect update);
74
75 virtual void GetPreferredSize(float* _width,
76 float* _height);
77
78 private:
79 void _Init();
80
81 BBitmap* fLogo;
82 };
83
84
LogoView(const BRect & frame)85 LogoView::LogoView(const BRect& frame)
86 :
87 BView(frame, "logoview", B_FOLLOW_LEFT | B_FOLLOW_TOP,
88 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
89 {
90 _Init();
91 }
92
93
LogoView()94 LogoView::LogoView()
95 :
96 BView("logoview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
97 {
98 _Init();
99 }
100
101
~LogoView(void)102 LogoView::~LogoView(void)
103 {
104 delete fLogo;
105 }
106
107
108 void
Draw(BRect update)109 LogoView::Draw(BRect update)
110 {
111 BRect bounds(Bounds());
112 SetLowColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
113 FillRect(bounds, B_SOLID_LOW);
114
115 if (fLogo == NULL)
116 return;
117
118 BPoint placement;
119 placement.x = (bounds.left + bounds.right - fLogo->Bounds().Width()) / 2;
120 placement.y = (bounds.top + bounds.bottom - fLogo->Bounds().Height()) / 2;
121
122 DrawBitmap(fLogo, placement);
123 }
124
125
126 void
GetPreferredSize(float * _width,float * _height)127 LogoView::GetPreferredSize(float* _width, float* _height)
128 {
129 float width = 0.0;
130 float height = 0.0;
131 if (fLogo) {
132 width = fLogo->Bounds().Width();
133 height = fLogo->Bounds().Height();
134 }
135 if (_width)
136 *_width = width;
137 if (_height)
138 *_height = height;
139 }
140
141
142 void
_Init()143 LogoView::_Init()
144 {
145 SetDrawingMode(B_OP_OVER);
146
147 #ifdef HAIKU_DISTRO_COMPATIBILITY_OFFICIAL
148 rgb_color bgColor = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
149
150 if (bgColor.IsLight())
151 fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "logo.png");
152 else
153 fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "logo_dark.png");
154 #else
155 fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "walter_logo.png");
156 #endif
157 }
158
159
160 // #pragma mark -
161
162
163 static BLayoutItem*
layout_item_for(BView * view)164 layout_item_for(BView* view)
165 {
166 BLayout* layout = view->Parent()->GetLayout();
167 int32 index = layout->IndexOfView(view);
168 return layout->ItemAt(index);
169 }
170
171
InstallerWindow()172 InstallerWindow::InstallerWindow()
173 :
174 BWindow(BRect(-2400, -2000, -1800, -1800),
175 B_TRANSLATE_SYSTEM_NAME("Installer"), B_TITLED_WINDOW,
176 B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
177 fEncouragedToSetupPartitions(false),
178 fDriveSetupLaunched(false),
179 fBootManagerLaunched(false),
180 fInstallStatus(kReadyForInstall),
181 fWorkerThread(new WorkerThread(this)),
182 fCopyEngineCancelSemaphore(-1)
183 {
184 if (!be_roster->IsRunning(kTrackerSignature))
185 SetWorkspaces(B_ALL_WORKSPACES);
186
187 LogoView* logoView = new LogoView();
188
189 rgb_color baseColor = ui_color(B_DOCUMENT_TEXT_COLOR);
190 fStatusView = new BTextView("statusView", be_plain_font, &baseColor,
191 B_WILL_DRAW);
192 fStatusView->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
193 fStatusView->MakeEditable(false);
194 fStatusView->MakeSelectable(false);
195
196 BSize logoSize = logoView->MinSize();
197 logoView->SetExplicitMaxSize(logoSize);
198
199 // In the status view, make sure that we can display 5 lines of text of ~28 characters each
200 font_height height;
201 fStatusView->GetFontHeight(&height);
202 float fontHeight = height.ascent + height.descent + height.leading;
203 fStatusView->SetExplicitMinSize(BSize(fStatusView->StringWidth("W") * 28,
204 fontHeight * 5 + 8));
205
206 // Create a group view with a white background since the logo and status text won't have the
207 // same height, this background will show in the remaining space
208 fLogoGroup = new BGroupView(B_HORIZONTAL, 10);
209 fLogoGroup->SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR);
210 fLogoGroup->GroupLayout()->SetInsets(0, 0, 10, 0);
211 fLogoGroup->AddChild(logoView);
212 fLogoGroup->AddChild(fStatusView);
213
214 fDestMenu = new BPopUpMenu(B_TRANSLATE("scanning" B_UTF8_ELLIPSIS),
215 true, false);
216 fSrcMenu = new BPopUpMenu(B_TRANSLATE("scanning" B_UTF8_ELLIPSIS),
217 true, false);
218
219 fSrcMenuField = new BMenuField("srcMenuField",
220 B_TRANSLATE("Install from:"), fSrcMenu);
221 fSrcMenuField->SetAlignment(B_ALIGN_RIGHT);
222
223 fDestMenuField = new BMenuField("destMenuField", B_TRANSLATE("Onto:"),
224 fDestMenu);
225 fDestMenuField->SetAlignment(B_ALIGN_RIGHT);
226
227 fPackagesSwitch = new PaneSwitch("options_button");
228 fPackagesSwitch->SetLabels(B_TRANSLATE("Hide optional packages"),
229 B_TRANSLATE("Show optional packages"));
230 fPackagesSwitch->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE));
231 fPackagesSwitch->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
232 B_SIZE_UNSET));
233 fPackagesSwitch->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
234 B_ALIGN_TOP));
235
236 fPackagesView = new PackagesView("packages_view");
237 BScrollView* packagesScrollView = new BScrollView("packagesScroll",
238 fPackagesView, B_WILL_DRAW, false, true);
239
240 const char* requiredDiskSpaceString
241 = B_TRANSLATE("Additional disk space required: 0.0 KiB");
242 fSizeView = new BStringView("size_view", requiredDiskSpaceString);
243 fSizeView->SetAlignment(B_ALIGN_RIGHT);
244 fSizeView->SetExplicitAlignment(
245 BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP));
246
247 fProgressBar = new BStatusBar("progress",
248 B_TRANSLATE("Install progress: "));
249 fProgressBar->SetMaxValue(100.0);
250
251 fBeginButton = new BButton("begin_button", B_TRANSLATE("Begin"),
252 new BMessage(BEGIN_MESSAGE));
253 fBeginButton->MakeDefault(true);
254 fBeginButton->SetEnabled(false);
255
256 fLaunchDriveSetupButton = new BButton("setup_button",
257 B_TRANSLATE("Set up partitions" B_UTF8_ELLIPSIS),
258 new BMessage(LAUNCH_DRIVE_SETUP));
259
260 fLaunchBootManagerItem = new BMenuItem(B_TRANSLATE("Set up boot menu" B_UTF8_ELLIPSIS),
261 new BMessage(LAUNCH_BOOTMAN));
262 fLaunchBootManagerItem->SetEnabled(false);
263
264 fMakeBootableItem = new BMenuItem(B_TRANSLATE("Write boot sector"),
265 new BMessage(MSG_WRITE_BOOT_SECTOR));
266 fMakeBootableItem->SetEnabled(false);
267 BMenuBar* mainMenu = new BMenuBar("main menu");
268 BMenu* toolsMenu = new BMenu(B_TRANSLATE("Tools"));
269 toolsMenu->AddItem(fLaunchBootManagerItem);
270 toolsMenu->AddItem(fMakeBootableItem);
271 mainMenu->AddItem(toolsMenu);
272
273 BGroupView* packagesGroup = new BGroupView(B_VERTICAL, B_USE_ITEM_SPACING);
274 packagesGroup->AddChild(fPackagesSwitch);
275 packagesGroup->AddChild(packagesScrollView);
276 packagesGroup->AddChild(fProgressBar);
277 packagesGroup->AddChild(fSizeView);
278
279 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
280 .Add(mainMenu)
281 .Add(fLogoGroup)
282 .Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
283 .AddGroup(B_VERTICAL, B_USE_ITEM_SPACING)
284 .SetInsets(B_USE_WINDOW_SPACING)
285 .AddGrid(new BGridView(B_USE_ITEM_SPACING, B_USE_ITEM_SPACING))
286 .AddMenuField(fSrcMenuField, 0, 0)
287 .AddMenuField(fDestMenuField, 0, 1)
288 .AddGlue(2, 0, 1, 2)
289 .Add(BSpaceLayoutItem::CreateVerticalStrut(5), 0, 2, 3)
290 .End()
291 .Add(packagesGroup)
292 .AddGroup(B_HORIZONTAL, B_USE_WINDOW_SPACING)
293 .Add(fLaunchDriveSetupButton)
294 .AddGlue()
295 .Add(fBeginButton)
296 .End()
297 .End()
298 .End();
299
300 // Make the optional packages and progress bar invisible on start
301 fPackagesLayoutItem = layout_item_for(packagesScrollView);
302 fPkgSwitchLayoutItem = layout_item_for(fPackagesSwitch);
303 fSizeViewLayoutItem = layout_item_for(fSizeView);
304 fProgressLayoutItem = layout_item_for(fProgressBar);
305
306 fPackagesLayoutItem->SetVisible(false);
307 fSizeViewLayoutItem->SetVisible(false);
308 fProgressLayoutItem->SetVisible(false);
309
310 // finish creating window
311 if (!be_roster->IsRunning(kDeskbarSignature))
312 SetFlags(Flags() | B_NOT_MINIMIZABLE);
313
314 CenterOnScreen();
315 Show();
316
317 // Register to receive notifications when apps launch or quit...
318 be_roster->StartWatching(this);
319 // ... and check the two we are interested in.
320 fDriveSetupLaunched = be_roster->IsRunning(kDriveSetupSignature);
321 fBootManagerLaunched = be_roster->IsRunning(kBootManagerSignature);
322
323 if (Lock()) {
324 fLaunchDriveSetupButton->SetEnabled(!fDriveSetupLaunched);
325 fLaunchBootManagerItem->SetEnabled(!fBootManagerLaunched);
326 Unlock();
327 }
328
329 PostMessage(START_SCAN);
330 }
331
332
~InstallerWindow()333 InstallerWindow::~InstallerWindow()
334 {
335 _SetCopyEngineCancelSemaphore(-1);
336 be_roster->StopWatching(this);
337 }
338
339
340 void
MessageReceived(BMessage * msg)341 InstallerWindow::MessageReceived(BMessage *msg)
342 {
343 switch (msg->what) {
344 case MSG_RESET:
345 {
346 _SetCopyEngineCancelSemaphore(-1);
347
348 status_t error;
349 if (msg->FindInt32("error", &error) == B_OK) {
350 char errorMessage[2048];
351 snprintf(errorMessage, sizeof(errorMessage),
352 B_TRANSLATE("An error was encountered and the "
353 "installation was not completed:\n\n"
354 "Error: %s"), strerror(error));
355 BAlert* alert = new BAlert("error", errorMessage, B_TRANSLATE("OK"));
356 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
357 alert->Go();
358 }
359
360 _DisableInterface(false);
361
362 fProgressLayoutItem->SetVisible(false);
363 fPkgSwitchLayoutItem->SetVisible(true);
364 _ShowOptionalPackages();
365 _UpdateControls();
366 break;
367 }
368 case START_SCAN:
369 _ScanPartitions();
370 break;
371 case BEGIN_MESSAGE:
372 switch (fInstallStatus) {
373 case kReadyForInstall:
374 {
375 // get source and target
376 PartitionMenuItem* targetItem
377 = (PartitionMenuItem*)fDestMenu->FindMarked();
378 PartitionMenuItem* srcItem
379 = (PartitionMenuItem*)fSrcMenu->FindMarked();
380 if (srcItem == NULL || targetItem == NULL)
381 break;
382
383 _SetCopyEngineCancelSemaphore(create_sem(1,
384 "copy engine cancel"));
385
386 BList* list = new BList();
387 int32 size = 0;
388 fPackagesView->GetPackagesToInstall(list, &size);
389 fWorkerThread->SetLock(fCopyEngineCancelSemaphore);
390 fWorkerThread->SetPackagesList(list);
391 fWorkerThread->SetSpaceRequired(size);
392 fInstallStatus = kInstalling;
393 fWorkerThread->StartInstall(srcItem->ID(),
394 targetItem->ID());
395 fBeginButton->SetLabel(B_TRANSLATE("Stop"));
396 _DisableInterface(true);
397
398 fProgressBar->SetTo(0.0, NULL, NULL);
399
400 fPkgSwitchLayoutItem->SetVisible(false);
401 fPackagesLayoutItem->SetVisible(false);
402 fSizeViewLayoutItem->SetVisible(false);
403 fProgressLayoutItem->SetVisible(true);
404 break;
405 }
406 case kInstalling:
407 {
408 _QuitCopyEngine(true);
409 break;
410 }
411 case kFinished:
412 PostMessage(B_QUIT_REQUESTED);
413 break;
414 case kCancelled:
415 break;
416 }
417 break;
418 case SHOW_BOTTOM_MESSAGE:
419 _ShowOptionalPackages();
420 break;
421 case SOURCE_PARTITION:
422 _PublishPackages();
423 _UpdateControls();
424 break;
425 case TARGET_PARTITION:
426 _UpdateControls();
427 break;
428 case LAUNCH_DRIVE_SETUP:
429 _LaunchDriveSetup();
430 break;
431 case LAUNCH_BOOTMAN:
432 _LaunchBootManager();
433 break;
434 case PACKAGE_CHECKBOX:
435 {
436 char buffer[15];
437 fPackagesView->GetTotalSizeAsString(buffer, sizeof(buffer));
438 char string[256];
439 snprintf(string, sizeof(string),
440 B_TRANSLATE("Additional disk space required: %s"), buffer);
441 fSizeView->SetText(string);
442 fSizeView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
443 break;
444 }
445 case ENCOURAGE_DRIVESETUP:
446 {
447 BAlert* alert = new BAlert("use drive setup", B_TRANSLATE("No partitions have "
448 "been found that are suitable for installation. Please set "
449 "up partitions and format at least one partition with the "
450 "Be File System."), B_TRANSLATE("OK"));
451 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
452 alert->Go();
453 break;
454 }
455 case MSG_STATUS_MESSAGE:
456 {
457 float progress;
458 if (msg->FindFloat("progress", &progress) == B_OK) {
459 const char* currentItem;
460 if (msg->FindString("item", ¤tItem) != B_OK) {
461 currentItem = B_TRANSLATE_COMMENT("???",
462 "Unknown currently copied item");
463 }
464 BString trailingLabel;
465 int32 currentCount;
466 int32 maximumCount;
467 if (msg->FindInt32("current", ¤tCount) == B_OK
468 && msg->FindInt32("maximum", &maximumCount) == B_OK) {
469 char buffer[64];
470 snprintf(buffer, sizeof(buffer),
471 B_TRANSLATE_COMMENT("%1ld of %2ld", "number of files copied"),
472 (long int)currentCount, (long int)maximumCount);
473 trailingLabel << buffer;
474 } else {
475 trailingLabel <<
476 B_TRANSLATE_COMMENT("?? of ??", "Unknown progress");
477 }
478 fProgressBar->SetTo(progress, currentItem,
479 trailingLabel.String());
480 } else {
481 const char *status;
482 if (msg->FindString("status", &status) == B_OK) {
483 fLastStatus = fStatusView->Text();
484 _SetStatusMessage(status);
485 } else
486 _SetStatusMessage(fLastStatus.String());
487 }
488 break;
489 }
490 case MSG_INSTALL_FINISHED:
491 {
492
493 _SetCopyEngineCancelSemaphore(-1);
494
495 PartitionMenuItem* dstItem
496 = (PartitionMenuItem*)fDestMenu->FindMarked();
497
498 BString status;
499 if (be_roster->IsRunning(kDeskbarSignature)) {
500 fBeginButton->SetLabel(B_TRANSLATE("Quit"));
501 status.SetToFormat(B_TRANSLATE("Installation "
502 "completed. Boot sector has been written to '%s'. Press "
503 "'Quit' to leave the Installer or choose a new target "
504 "volume to perform another installation."),
505 dstItem ? dstItem->Name() : B_TRANSLATE_COMMENT("???",
506 "Unknown partition name"));
507 } else {
508 fBeginButton->SetLabel(B_TRANSLATE("Restart"));
509 status.SetToFormat(B_TRANSLATE("Installation "
510 "completed. Boot sector has been written to '%s'. Press "
511 "'Restart' to restart the computer or choose a new target "
512 "volume to perform another installation."),
513 dstItem ? dstItem->Name() : B_TRANSLATE_COMMENT("???",
514 "Unknown partition name"));
515 }
516
517 _SetStatusMessage(status.String());
518 fInstallStatus = kFinished;
519
520 _DisableInterface(false);
521 fProgressLayoutItem->SetVisible(false);
522 fPkgSwitchLayoutItem->SetVisible(true);
523 _ShowOptionalPackages();
524 break;
525 }
526 case B_SOME_APP_LAUNCHED:
527 case B_SOME_APP_QUIT:
528 {
529 const char *signature;
530 if (msg->FindString("be:signature", &signature) != B_OK)
531 break;
532 bool isDriveSetup = !strcasecmp(signature, kDriveSetupSignature);
533 bool isBootManager = !strcasecmp(signature, kBootManagerSignature);
534 if (isDriveSetup || isBootManager) {
535 bool scanPartitions = false;
536 if (isDriveSetup) {
537 bool launched = msg->what == B_SOME_APP_LAUNCHED;
538 // We need to scan partitions if DriveSetup has quit.
539 scanPartitions = fDriveSetupLaunched && !launched;
540 fDriveSetupLaunched = launched;
541 }
542 if (isBootManager)
543 fBootManagerLaunched = msg->what == B_SOME_APP_LAUNCHED;
544
545 fBeginButton->SetEnabled(
546 !fDriveSetupLaunched && !fBootManagerLaunched);
547 _DisableInterface(fDriveSetupLaunched || fBootManagerLaunched);
548 if (fDriveSetupLaunched && fBootManagerLaunched) {
549 _SetStatusMessage(B_TRANSLATE("Running BootManager and "
550 "DriveSetup" B_UTF8_ELLIPSIS
551 "\n\nClose both applications to continue with the "
552 "installation."));
553 } else if (fDriveSetupLaunched) {
554 _SetStatusMessage(B_TRANSLATE("Running DriveSetup"
555 B_UTF8_ELLIPSIS
556 "\n\nClose DriveSetup to continue with the "
557 "installation."));
558 } else if (fBootManagerLaunched) {
559 _SetStatusMessage(B_TRANSLATE("Running BootManager"
560 B_UTF8_ELLIPSIS
561 "\n\nClose BootManager to continue with the "
562 "installation."));
563 } else {
564 // If neither DriveSetup nor Bootman is running, we need
565 // to scan partitions in case DriveSetup has quit, or
566 // we need to update the guidance message, unless install
567 // was already finished.
568 if (scanPartitions)
569 _ScanPartitions();
570 else if (fInstallStatus != kFinished)
571 _UpdateControls();
572 else
573 PostMessage(MSG_INSTALL_FINISHED);
574 }
575 }
576 break;
577 }
578 case MSG_WRITE_BOOT_SECTOR:
579 fWorkerThread->WriteBootSector(fDestMenu);
580 break;
581
582 default:
583 BWindow::MessageReceived(msg);
584 break;
585 }
586 }
587
588
589 bool
QuitRequested()590 InstallerWindow::QuitRequested()
591 {
592 if ((Flags() & B_NOT_MINIMIZABLE) != 0) {
593 // This means Deskbar is not running, i.e. Installer is the only
594 // thing on the screen and we will reboot the machine once it quits.
595
596 if (fDriveSetupLaunched && fBootManagerLaunched) {
597 BAlert* alert = new BAlert(B_TRANSLATE("Quit BootManager and "
598 "DriveSetup"), B_TRANSLATE("Please close the BootManager "
599 "and DriveSetup windows before closing the Installer window."),
600 B_TRANSLATE("OK"));
601 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
602 alert->Go();
603 return false;
604 }
605 if (fDriveSetupLaunched) {
606 BAlert* alert = new BAlert(B_TRANSLATE("Quit DriveSetup"),
607 B_TRANSLATE("Please close the DriveSetup window before "
608 "closing the Installer window."), B_TRANSLATE("OK"));
609 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
610 alert->Go();
611 return false;
612 }
613 if (fBootManagerLaunched) {
614 BAlert* alert = new BAlert(B_TRANSLATE("Quit BootManager"),
615 B_TRANSLATE("Please close the BootManager window before "
616 "closing the Installer window."), B_TRANSLATE("OK"));
617 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
618 alert->Go();
619 return false;
620 }
621 if (fInstallStatus != kFinished) {
622 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("Installer"),
623 B_TRANSLATE("Are you sure you want to stop the installation?"),
624 B_TRANSLATE("Cancel"), B_TRANSLATE("Stop"), NULL,
625 B_WIDTH_AS_USUAL, B_STOP_ALERT);
626 alert->SetShortcut(0, B_ESCAPE);
627 if (alert->Go() == 0)
628 return false;
629 }
630 } else if (fInstallStatus == kInstalling) {
631 BAlert* alert = new BAlert(B_TRANSLATE_SYSTEM_NAME("Installer"),
632 B_TRANSLATE("The installation is not complete yet!\n"
633 "Are you sure you want to stop it?"),
634 B_TRANSLATE("Cancel"), B_TRANSLATE("Stop"), NULL,
635 B_WIDTH_AS_USUAL, B_STOP_ALERT);
636 alert->SetShortcut(0, B_ESCAPE);
637 if (alert->Go() == 0)
638 return false;
639 }
640
641 _QuitCopyEngine(false);
642
643 BMessage quitWithInstallStatus(B_QUIT_REQUESTED);
644 quitWithInstallStatus.AddBool("install_complete",
645 fInstallStatus == kFinished);
646
647 fWorkerThread->PostMessage(&quitWithInstallStatus);
648 be_app->PostMessage(&quitWithInstallStatus);
649 return true;
650 }
651
652
653 // #pragma mark -
654
655
656 void
_ShowOptionalPackages()657 InstallerWindow::_ShowOptionalPackages()
658 {
659 if (fPackagesLayoutItem && fSizeViewLayoutItem) {
660 fPackagesLayoutItem->SetVisible(fPackagesSwitch->Value());
661 fSizeViewLayoutItem->SetVisible(fPackagesSwitch->Value());
662 }
663 }
664
665
666 void
_LaunchDriveSetup()667 InstallerWindow::_LaunchDriveSetup()
668 {
669 if (be_roster->Launch(kDriveSetupSignature) != B_OK) {
670 // Try really hard to launch it. It's very likely that this fails,
671 // when we run from the CD and there is only an incomplete mime
672 // database for example...
673 BPath path;
674 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK
675 || path.Append("DriveSetup") != B_OK) {
676 path.SetTo("/boot/system/apps/DriveSetup");
677 }
678 BEntry entry(path.Path());
679 entry_ref ref;
680 if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) {
681 BAlert* alert = new BAlert("error", B_TRANSLATE("DriveSetup, the "
682 "application to configure disk partitions, could not be "
683 "launched."), B_TRANSLATE("OK"));
684 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
685 alert->Go();
686 }
687 }
688 }
689
690
691 void
_LaunchBootManager()692 InstallerWindow::_LaunchBootManager()
693 {
694 // TODO: Currently BootManager always tries to install to the "first"
695 // harddisk. If/when it later supports being installed to a certain
696 // harddisk, we would have to pass it the disk that contains the target
697 // partition here.
698 if (be_roster->Launch(kBootManagerSignature) != B_OK) {
699 // Try really hard to launch it. It's very likely that this fails,
700 // when we run from the CD and there is only an incomplete mime
701 // database for example...
702 BPath path;
703 if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK
704 || path.Append("BootManager") != B_OK) {
705 path.SetTo("/boot/system/apps/BootManager");
706 }
707 BEntry entry(path.Path());
708 entry_ref ref;
709 if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) {
710 BAlert* alert = new BAlert(
711 B_TRANSLATE("Failed to launch BootManager"),
712 B_TRANSLATE("BootManager, the application to configure the "
713 "Haiku boot menu, could not be launched."),
714 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
715 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
716 alert->Go();
717 }
718 }
719 }
720
721
722 void
_DisableInterface(bool disable)723 InstallerWindow::_DisableInterface(bool disable)
724 {
725 fLaunchDriveSetupButton->SetEnabled(!disable);
726 fLaunchBootManagerItem->SetEnabled(!disable);
727 fMakeBootableItem->SetEnabled(!disable);
728 fSrcMenuField->SetEnabled(!disable);
729 fDestMenuField->SetEnabled(!disable);
730 }
731
732
733 void
_ScanPartitions()734 InstallerWindow::_ScanPartitions()
735 {
736 _SetStatusMessage(B_TRANSLATE("Scanning for disks" B_UTF8_ELLIPSIS));
737
738 BMenuItem *item;
739 while ((item = fSrcMenu->RemoveItem((int32)0)))
740 delete item;
741 while ((item = fDestMenu->RemoveItem((int32)0)))
742 delete item;
743
744 fWorkerThread->ScanDisksPartitions(fSrcMenu, fDestMenu);
745
746 if (fSrcMenu->ItemAt(0) != NULL)
747 _PublishPackages();
748
749 _UpdateControls();
750 }
751
752
753 void
_UpdateControls()754 InstallerWindow::_UpdateControls()
755 {
756 PartitionMenuItem* srcItem = (PartitionMenuItem*)fSrcMenu->FindMarked();
757 BString label;
758 if (srcItem) {
759 label = srcItem->MenuLabel();
760 } else {
761 if (fSrcMenu->CountItems() == 0)
762 label = B_TRANSLATE_COMMENT("<none>", "No partition available");
763 else
764 label = ((PartitionMenuItem*)fSrcMenu->ItemAt(0))->MenuLabel();
765 }
766 fSrcMenuField->MenuItem()->SetLabel(label.String());
767
768 // Disable any unsuitable target items, check if at least one partition
769 // is suitable.
770 bool foundOneSuitableTarget = false;
771 for (int32 i = fDestMenu->CountItems() - 1; i >= 0; i--) {
772 PartitionMenuItem* dstItem
773 = (PartitionMenuItem*)fDestMenu->ItemAt(i);
774 if (srcItem != NULL && dstItem->ID() == srcItem->ID()) {
775 // Prevent the user from having picked the same partition as source
776 // and destination.
777 dstItem->SetEnabled(false);
778 dstItem->SetMarked(false);
779 } else
780 dstItem->SetEnabled(dstItem->IsValidTarget());
781
782 if (dstItem->IsEnabled())
783 foundOneSuitableTarget = true;
784 }
785
786 PartitionMenuItem* dstItem = (PartitionMenuItem*)fDestMenu->FindMarked();
787 if (dstItem) {
788 label = dstItem->MenuLabel();
789 } else {
790 if (fDestMenu->CountItems() == 0)
791 label = B_TRANSLATE_COMMENT("<none>", "No partition available");
792 else
793 label = B_TRANSLATE("Please choose target");
794 }
795 fDestMenuField->MenuItem()->SetLabel(label.String());
796
797 BString statusText;
798 if (srcItem != NULL && dstItem != NULL) {
799 statusText.SetToFormat(B_TRANSLATE("Press the 'Begin' button to install "
800 "from '%1s' onto '%2s'."), srcItem->Name(), dstItem->Name());
801 } else if (srcItem != NULL) {
802 BString partitionRequiredHaiku = B_TRANSLATE(
803 "Haiku has to be installed on a partition that uses "
804 "the Be File System, but there are currently no such "
805 "partitions available on your system.");
806
807 BString partitionRequiredDebranded = B_TRANSLATE(
808 "This operating system has to be installed on a partition "
809 "that uses the Be File System, but there are currently "
810 "no such partitions available on your system.");
811
812 if (!foundOneSuitableTarget) {
813 #ifdef HAIKU_DISTRO_COMPATIBILITY_OFFICIAL
814 statusText.Append(partitionRequiredHaiku);
815 #else
816 statusText.Append(partitionRequiredDebranded);
817 #endif
818 statusText.Append(" ");
819 statusText.Append(B_TRANSLATE(
820 "Click on 'Set up partitions" B_UTF8_ELLIPSIS
821 "' to create one."));
822 } else {
823 statusText = B_TRANSLATE(
824 "Choose the disk you want to install "
825 "onto from the pop-up menu. Then click 'Begin'.");
826 }
827 } else if (dstItem != NULL) {
828 statusText = B_TRANSLATE("Choose the source disk from the "
829 "pop-up menu. Then click 'Begin'.");
830 } else {
831 statusText = B_TRANSLATE("Choose the source and destination disk "
832 "from the pop-up menus. Then click 'Begin'.");
833 }
834
835 _SetStatusMessage(statusText.String());
836
837 fInstallStatus = kReadyForInstall;
838 fBeginButton->SetLabel(B_TRANSLATE("Begin"));
839 fBeginButton->SetEnabled(srcItem && dstItem);
840
841 // adjust "Write Boot Sector" and "Set up boot menu" buttons
842 if (dstItem != NULL) {
843 char buffer[256];
844 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Write boot sector to '%s'"),
845 dstItem->Name());
846 label = buffer;
847 } else
848 label = B_TRANSLATE("Write boot sector");
849 fMakeBootableItem->SetEnabled(dstItem != NULL);
850 fMakeBootableItem->SetLabel(label.String());
851 // TODO: Once bootman support writing to specific disks, enable this, since
852 // we would pass it the disk which contains the target partition.
853 // fLaunchBootManagerItem->SetEnabled(dstItem != NULL);
854
855 if (!fEncouragedToSetupPartitions && !foundOneSuitableTarget) {
856 // Focus the users attention on the DriveSetup button
857 fEncouragedToSetupPartitions = true;
858 PostMessage(ENCOURAGE_DRIVESETUP);
859 }
860 }
861
862
863 void
_PublishPackages()864 InstallerWindow::_PublishPackages()
865 {
866 fPackagesView->Clean();
867 PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked();
868 if (item == NULL)
869 return;
870
871 BPath directory;
872 BDiskDeviceRoster roster;
873 BDiskDevice device;
874 BPartition *partition;
875 if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) {
876 if (partition->GetMountPoint(&directory) != B_OK)
877 return;
878 } else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) {
879 if (device.GetMountPoint(&directory) != B_OK)
880 return;
881 } else
882 return; // shouldn't happen
883
884 directory.Append(kPackagesDirectoryPath);
885 BDirectory dir(directory.Path());
886 if (dir.InitCheck() != B_OK)
887 return;
888
889 BEntry packageEntry;
890 BList packages;
891 while (dir.GetNextEntry(&packageEntry) == B_OK) {
892 Package* package = Package::PackageFromEntry(packageEntry);
893 if (package != NULL)
894 packages.AddItem(package);
895 }
896 packages.SortItems(_ComparePackages);
897
898 fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX));
899 PostMessage(PACKAGE_CHECKBOX);
900 }
901
902
903 void
_SetStatusMessage(const char * text)904 InstallerWindow::_SetStatusMessage(const char *text)
905 {
906 fStatusView->SetText(text);
907 fStatusView->InvalidateLayout();
908 // In case the status message makes the text view higher than the
909 // logo, then we need to resize te whole window to fit it.
910 }
911
912
913 void
_SetCopyEngineCancelSemaphore(sem_id id,bool alreadyLocked)914 InstallerWindow::_SetCopyEngineCancelSemaphore(sem_id id, bool alreadyLocked)
915 {
916 if (fCopyEngineCancelSemaphore >= 0) {
917 if (!alreadyLocked)
918 acquire_sem(fCopyEngineCancelSemaphore);
919 delete_sem(fCopyEngineCancelSemaphore);
920 }
921 fCopyEngineCancelSemaphore = id;
922 }
923
924
925 void
_QuitCopyEngine(bool askUser)926 InstallerWindow::_QuitCopyEngine(bool askUser)
927 {
928 if (fCopyEngineCancelSemaphore < 0)
929 return;
930
931 // First of all block the copy engine, so that it doesn't continue
932 // while the alert is showing, which would be irritating.
933 acquire_sem(fCopyEngineCancelSemaphore);
934
935 bool quit = true;
936 if (askUser) {
937 BAlert* alert = new BAlert("cancel",
938 B_TRANSLATE("Are you sure you want to to stop the installation?"),
939 B_TRANSLATE_COMMENT("Continue", "In alert after pressing Stop"),
940 B_TRANSLATE_COMMENT("Stop", "In alert after pressing Stop"), 0,
941 B_WIDTH_AS_USUAL, B_STOP_ALERT);
942 alert->SetShortcut(1, B_ESCAPE);
943 quit = alert->Go() != 0;
944 }
945
946 if (quit) {
947 // Make it quit by having it's lock fail...
948 _SetCopyEngineCancelSemaphore(-1, true);
949 } else
950 release_sem(fCopyEngineCancelSemaphore);
951 }
952
953
954 // #pragma mark -
955
956
957 int
_ComparePackages(const void * firstArg,const void * secondArg)958 InstallerWindow::_ComparePackages(const void* firstArg, const void* secondArg)
959 {
960 const Group* group1 = *static_cast<const Group* const *>(firstArg);
961 const Group* group2 = *static_cast<const Group* const *>(secondArg);
962 const Package* package1 = dynamic_cast<const Package*>(group1);
963 const Package* package2 = dynamic_cast<const Package*>(group2);
964 int sameGroup = strcmp(group1->GroupName(), group2->GroupName());
965 if (sameGroup != 0)
966 return sameGroup;
967 if (package2 == NULL)
968 return -1;
969 if (package1 == NULL)
970 return 1;
971 return strcmp(package1->Name(), package2->Name());
972 }
973
974
975