xref: /haiku/src/apps/installer/InstallerWindow.cpp (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
1 /*
2  * Copyright 2009, Stephan Aßmus <superstippi@gmx.de>
3  * Copyright 2005-2008, Jérôme DUVAL
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 
7 #include "InstallerWindow.h"
8 
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include <Alert.h>
13 #include <Application.h>
14 #include <Autolock.h>
15 #include <Box.h>
16 #include <Button.h>
17 #include <ClassInfo.h>
18 #include <Directory.h>
19 #include <FindDirectory.h>
20 #include <GridLayoutBuilder.h>
21 #include <GroupLayoutBuilder.h>
22 #include <LayoutUtils.h>
23 #include <MenuBar.h>
24 #include <MenuField.h>
25 #include <Path.h>
26 #include <PopUpMenu.h>
27 #include <Roster.h>
28 #include <Screen.h>
29 #include <ScrollView.h>
30 #include <SpaceLayoutItem.h>
31 #include <StatusBar.h>
32 #include <String.h>
33 #include <TextView.h>
34 #include <TranslationUtils.h>
35 #include <TranslatorFormats.h>
36 
37 #include "tracker_private.h"
38 
39 #include "DialogPane.h"
40 #include "PackageViews.h"
41 #include "PartitionMenuItem.h"
42 #include "WorkerThread.h"
43 
44 
45 #define DRIVESETUP_SIG "application/x-vnd.Haiku-DriveSetup"
46 
47 const uint32 BEGIN_MESSAGE = 'iBGN';
48 const uint32 SHOW_BOTTOM_MESSAGE = 'iSBT';
49 const uint32 SETUP_MESSAGE = 'iSEP';
50 const uint32 START_SCAN = 'iSSC';
51 const uint32 PACKAGE_CHECKBOX = 'iPCB';
52 
53 class LogoView : public BView {
54 public:
55 								LogoView(const BRect& frame);
56 								LogoView();
57 	virtual						~LogoView();
58 
59 	virtual	void				Draw(BRect update);
60 
61 	virtual	void				GetPreferredSize(float* _width, float* _height);
62 
63 private:
64 			void				_Init();
65 
66 			BBitmap*			fLogo;
67 };
68 
69 
70 LogoView::LogoView(const BRect& frame)
71 	:
72 	BView(frame, "logoview", B_FOLLOW_LEFT | B_FOLLOW_TOP,
73 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
74 {
75 	_Init();
76 }
77 
78 
79 LogoView::LogoView()
80 	:
81 	BView("logoview", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
82 {
83 	_Init();
84 }
85 
86 
87 LogoView::~LogoView(void)
88 {
89 	delete fLogo;
90 }
91 
92 
93 void
94 LogoView::Draw(BRect update)
95 {
96 	if (fLogo == NULL)
97 		return;
98 
99 	BRect bounds(Bounds());
100 	BPoint placement;
101 	placement.x = (bounds.left + bounds.right - fLogo->Bounds().Width()) / 2;
102 	placement.y = (bounds.top + bounds.bottom - fLogo->Bounds().Height()) / 2;
103 
104 	DrawBitmap(fLogo, placement);
105 }
106 
107 
108 void
109 LogoView::GetPreferredSize(float* _width, float* _height)
110 {
111 	float width = 0.0;
112 	float height = 0.0;
113 	if (fLogo) {
114 		width = fLogo->Bounds().Width();
115 		height = fLogo->Bounds().Height();
116 	}
117 	if (_width)
118 		*_width = width;
119 	if (_height)
120 		*_height = height;
121 }
122 
123 
124 void
125 LogoView::_Init()
126 {
127 	fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "haikulogo.png");
128 }
129 
130 
131 // #pragma mark -
132 
133 
134 class SeparatorView : public BView {
135 public:
136 								SeparatorView(enum orientation orientation);
137 	virtual						~SeparatorView();
138 
139 	virtual	BSize				MinSize();
140 	virtual	BSize				PreferredSize();
141 	virtual	BSize				MaxSize();
142 private:
143 			enum orientation	fOrientation;
144 };
145 
146 
147 SeparatorView::SeparatorView(enum orientation orientation)
148 	:
149 	BView("separator", 0),
150 	fOrientation(orientation)
151 {
152 	SetViewColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
153 		B_DARKEN_2_TINT));
154 }
155 
156 
157 SeparatorView::~SeparatorView()
158 {
159 }
160 
161 
162 BSize
163 SeparatorView::MinSize()
164 {
165 	return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(0, 0));
166 }
167 
168 
169 BSize
170 SeparatorView::MaxSize()
171 {
172 	BSize size(0, 0);
173 	if (fOrientation == B_VERTICAL)
174 		size.height = B_SIZE_UNLIMITED;
175 	else
176 		size.width = B_SIZE_UNLIMITED;
177 
178 	return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size);
179 }
180 
181 
182 BSize
183 SeparatorView::PreferredSize()
184 {
185 	BSize size(0, 0);
186 	if (fOrientation == B_VERTICAL)
187 		size.height = 10;
188 	else
189 		size.width = 10;
190 
191 	return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size);
192 }
193 
194 
195 // #pragma mark -
196 
197 
198 static BLayoutItem*
199 layout_item_for(BView* view)
200 {
201 	BLayout* layout = view->Parent()->GetLayout();
202 	int32 index = layout->IndexOfView(view);
203 	return layout->ItemAt(index);
204 }
205 
206 
207 InstallerWindow::InstallerWindow()
208 	: BWindow(BRect(-2000, -2000, -1800, -1800), "Installer", B_TITLED_WINDOW,
209 		B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
210 	fNeedsToCenterOnScreen(true),
211 	fEncouragedToSetupPartitions(false),
212 	fDriveSetupLaunched(false),
213 	fInstallStatus(kReadyForInstall),
214 	fWorkerThread(new WorkerThread(this)),
215 	fCopyEngineLock(NULL)
216 {
217 	LogoView* logoView = new LogoView();
218 
219 	fStatusView = new BTextView("statusView", be_plain_font, NULL, B_WILL_DRAW);
220 	fStatusView->SetInsets(10, 10, 10, 10);
221 	fStatusView->MakeEditable(false);
222 	fStatusView->MakeSelectable(false);
223 
224 	BSize logoSize = logoView->MinSize();
225 	fStatusView->SetExplicitMinSize(BSize(logoSize.width * 0.66, B_SIZE_UNSET));
226 
227 	fDestMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false);
228 	fSrcMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false);
229 
230 	fSrcMenuField = new BMenuField("srcMenuField", "Install from: ", fSrcMenu,
231 		NULL);
232 	fSrcMenuField->SetAlignment(B_ALIGN_RIGHT);
233 
234 	fDestMenuField = new BMenuField("destMenuField", "Onto: ", fDestMenu, NULL);
235 	fDestMenuField->SetAlignment(B_ALIGN_RIGHT);
236 
237 	fPackagesSwitch = new PaneSwitch("options_button");
238 	fPackagesSwitch->SetLabels("Hide Optional Packages",
239 		"Show Optional Packages");
240 	fPackagesSwitch->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE));
241 	fPackagesSwitch->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED,
242 		B_SIZE_UNLIMITED));
243 	fPackagesSwitch->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
244 		B_ALIGN_TOP));
245 
246 	fPackagesView = new PackagesView("packages_view");
247 	BScrollView* packagesScrollView = new BScrollView("packagesScroll",
248 		fPackagesView, B_WILL_DRAW, false, true);
249 
250 	const char* requiredDiskSpaceString
251 		= "Additional disk space required: 0.0 KB";
252 	fSizeView = new BStringView("size_view", requiredDiskSpaceString);
253 	fSizeView->SetAlignment(B_ALIGN_RIGHT);
254 	fSizeView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
255 	fSizeView->SetExplicitAlignment(
256 		BAlignment(B_ALIGN_RIGHT, B_ALIGN_MIDDLE));
257 
258 	fProgressBar = new BStatusBar("progress", "Install Progress:  ");
259 	fProgressBar->SetMaxValue(100.0);
260 
261 	fBeginButton = new BButton("begin_button", "Begin",
262 		new BMessage(BEGIN_MESSAGE));
263 	fBeginButton->MakeDefault(true);
264 	fBeginButton->SetEnabled(false);
265 
266 	fSetupButton = new BButton("setup_button",
267 		"Setup partitions" B_UTF8_ELLIPSIS, new BMessage(SETUP_MESSAGE));
268 
269 	fMakeBootableButton = new BButton("makebootable_button",
270 		"Write Boot Sector", new BMessage(MSG_WRITE_BOOT_SECTOR));
271 	fMakeBootableButton->SetEnabled(false);
272 
273 	SetLayout(new BGroupLayout(B_HORIZONTAL));
274 	AddChild(BGroupLayoutBuilder(B_VERTICAL)
275 		.Add(BGroupLayoutBuilder(B_HORIZONTAL)
276 			.Add(logoView)
277 			.Add(fStatusView)
278 		)
279 		.Add(new SeparatorView(B_HORIZONTAL))
280 		.Add(BGroupLayoutBuilder(B_VERTICAL, 10)
281 			.Add(BGridLayoutBuilder(0, 10)
282 				.Add(fSrcMenuField->CreateLabelLayoutItem(), 0, 0)
283 				.Add(fSrcMenuField->CreateMenuBarLayoutItem(), 1, 0)
284 				.Add(fDestMenuField->CreateLabelLayoutItem(), 0, 1)
285 				.Add(fDestMenuField->CreateMenuBarLayoutItem(), 1, 1)
286 
287 				.Add(BSpaceLayoutItem::CreateVerticalStrut(5), 0, 2, 2)
288 
289 				.Add(fPackagesSwitch, 0, 3, 2)
290 				.Add(packagesScrollView, 0, 4, 2)
291 				.Add(fProgressBar, 0, 5, 2)
292 				.Add(fSizeView, 0, 6, 2)
293 			)
294 
295 			.Add(BGroupLayoutBuilder(B_HORIZONTAL, 10)
296 				.Add(fSetupButton)
297 				.Add(fMakeBootableButton)
298 				.AddGlue()
299 				.Add(fBeginButton)
300 			)
301 			.SetInsets(10, 10, 10, 10)
302 		)
303 	);
304 
305 	// Make the optional packages and progress bar invisible on start
306 	fPackagesLayoutItem = layout_item_for(packagesScrollView);
307 	fPkgSwitchLayoutItem = layout_item_for(fPackagesSwitch);
308 	fSizeViewLayoutItem = layout_item_for(fSizeView);
309 	fProgressLayoutItem = layout_item_for(fProgressBar);
310 
311 	fPackagesLayoutItem->SetVisible(false);
312 	fSizeViewLayoutItem->SetVisible(false);
313 	fProgressLayoutItem->SetVisible(false);
314 
315 	// finish creating window
316 	if (!be_roster->IsRunning(kDeskbarSignature))
317 		SetFlags(Flags() | B_NOT_MINIMIZABLE);
318 
319 	Show();
320 
321 	fDriveSetupLaunched = be_roster->IsRunning(DRIVESETUP_SIG);
322 
323 	if (Lock()) {
324 		fSetupButton->SetEnabled(!fDriveSetupLaunched);
325 		Unlock();
326 	}
327 
328 	be_roster->StartWatching(this);
329 
330 	PostMessage(START_SCAN);
331 }
332 
333 
334 InstallerWindow::~InstallerWindow()
335 {
336 	be_roster->StopWatching(this);
337 }
338 
339 
340 void
341 InstallerWindow::FrameResized(float width, float height)
342 {
343 	BWindow::FrameResized(width, height);
344 
345 	if (fNeedsToCenterOnScreen) {
346 		// We have created ourselves off-screen, since the size adoption
347 		// because of the layout management may happen after Show(). We
348 		// assume that the first frame event is because of this adoption and
349 		// move ourselves to the screen center...
350 		fNeedsToCenterOnScreen = false;
351 		BRect frame = BScreen(this).Frame();
352 		MoveTo(frame.left + (frame.Width() - Frame().Width()) / 2,
353 			frame.top + (frame.Height() - Frame().Height()) / 2);
354 	}
355 }
356 
357 
358 void
359 InstallerWindow::MessageReceived(BMessage *msg)
360 {
361 	switch (msg->what) {
362 		case MSG_RESET:
363 		{
364 			delete fCopyEngineLock;
365 			fCopyEngineLock = NULL;
366 
367 			status_t error;
368 			if (msg->FindInt32("error", &error) == B_OK) {
369 				char errorMessage[2048];
370 				snprintf(errorMessage, sizeof(errorMessage), "An error was "
371 					"encountered and the installation was not completed:\n\n"
372 					"Error:  %s", strerror(error));
373 				(new BAlert("error", errorMessage, "Ok"))->Go();
374 			}
375 
376 			_DisableInterface(false);
377 
378 			fProgressLayoutItem->SetVisible(false);
379 			fPkgSwitchLayoutItem->SetVisible(true);
380 			_ShowOptionalPackages();
381 			_UpdateControls();
382 			break;
383 		}
384 		case START_SCAN:
385 			_ScanPartitions();
386 			break;
387 		case BEGIN_MESSAGE:
388 			switch (fInstallStatus) {
389 				case kReadyForInstall:
390 				{
391 					delete fCopyEngineLock;
392 					fCopyEngineLock = new BLocker("copy engine lock");
393 					BList* list = new BList();
394 					int32 size = 0;
395 					fPackagesView->GetPackagesToInstall(list, &size);
396 					fWorkerThread->SetLock(fCopyEngineLock);
397 					fWorkerThread->SetPackagesList(list);
398 					fWorkerThread->SetSpaceRequired(size);
399 					fInstallStatus = kInstalling;
400 					fWorkerThread->StartInstall();
401 					fBeginButton->SetLabel("Stop");
402 					_DisableInterface(true);
403 
404 					fProgressBar->SetTo(0.0, NULL, NULL);
405 
406 					fPkgSwitchLayoutItem->SetVisible(false);
407 					fPackagesLayoutItem->SetVisible(false);
408 					fSizeViewLayoutItem->SetVisible(false);
409 					fProgressLayoutItem->SetVisible(true);
410 					break;
411 				}
412 				case kInstalling:
413 				{
414 					_QuitCopyEngine(true);
415 					break;
416 				}
417 				case kFinished:
418 					PostMessage(B_QUIT_REQUESTED);
419 					break;
420 				case kCancelled:
421 					break;
422 			}
423 			break;
424 		case SHOW_BOTTOM_MESSAGE:
425 			_ShowOptionalPackages();
426 			break;
427 		case SRC_PARTITION:
428 			_PublishPackages();
429 			_UpdateControls();
430 			break;
431 		case TARGET_PARTITION:
432 			_UpdateControls();
433 			break;
434 		case SETUP_MESSAGE:
435 			_LaunchDriveSetup();
436 			break;
437 		case PACKAGE_CHECKBOX:
438 		{
439 			char buffer[15];
440 			fPackagesView->GetTotalSizeAsString(buffer);
441 			char string[255];
442 			sprintf(string, "Additional disk space required: %s", buffer);
443 			fSizeView->SetText(string);
444 			break;
445 		}
446 		case MSG_STATUS_MESSAGE:
447 		{
448 // TODO: Was this supposed to prevent status messages still arriving
449 // after the copy engine was shut down?
450 //			if (fInstallStatus != kInstalling)
451 //				break;
452 			float progress;
453 			if (msg->FindFloat("progress", &progress) == B_OK) {
454 				const char* currentItem;
455 				if (msg->FindString("item", &currentItem) != B_OK)
456 					currentItem = "???";
457 				BString trailingLabel;
458 				int32 currentCount;
459 				int32 maximumCount;
460 				if (msg->FindInt32("current", &currentCount) == B_OK
461 					&& msg->FindInt32("maximum", &maximumCount) == B_OK) {
462 					trailingLabel << currentCount << " of " << maximumCount;
463 				} else {
464 					trailingLabel << "?? of ??";
465 				}
466 				fProgressBar->SetTo(progress, currentItem,
467 					trailingLabel.String());
468 			} else {
469 				const char *status;
470 				if (msg->FindString("status", &status) == B_OK) {
471 					fLastStatus = fStatusView->Text();
472 					_SetStatusMessage(status);
473 				} else
474 					_SetStatusMessage(fLastStatus.String());
475 			}
476 			break;
477 		}
478 		case MSG_INSTALL_FINISHED:
479 		{
480 			delete fCopyEngineLock;
481 			fCopyEngineLock = NULL;
482 
483 			fBeginButton->SetLabel("Quit");
484 
485 			PartitionMenuItem* dstItem
486 				= (PartitionMenuItem*)fDestMenu->FindMarked();
487 
488 			const char* quitString;
489 			if (be_roster->IsRunning(kDeskbarSignature))
490 				quitString = "leave the Installer";
491 			else
492 				quitString = "restart the computer";
493 
494 			char status[1024];
495 			snprintf(status, sizeof(status), "Installation completed. "
496 				"Boot sector has been written to '%s'. Press Quit to %s "
497 				"or chose a new target volume to perform another "
498 				"installation.", dstItem ? dstItem->Name() : "???", quitString);
499 			_SetStatusMessage(status);
500 			fInstallStatus = kFinished;
501 			_DisableInterface(false);
502 			fProgressLayoutItem->SetVisible(false);
503 			fPkgSwitchLayoutItem->SetVisible(true);
504 			_ShowOptionalPackages();
505 			break;
506 		}
507 		case B_SOME_APP_LAUNCHED:
508 		case B_SOME_APP_QUIT:
509 		{
510 			const char *signature;
511 			if (msg->FindString("be:signature", &signature) == B_OK
512 				&& strcasecmp(signature, DRIVESETUP_SIG) == 0) {
513 				fDriveSetupLaunched = msg->what == B_SOME_APP_LAUNCHED;
514 				fBeginButton->SetEnabled(!fDriveSetupLaunched);
515 				_DisableInterface(fDriveSetupLaunched);
516 				if (fDriveSetupLaunched)
517 					_SetStatusMessage("Running DriveSetup" B_UTF8_ELLIPSIS
518 						"\n\nClose DriveSetup to continue with the "
519 						"installation.");
520 				else
521 					_ScanPartitions();
522 			}
523 			break;
524 		}
525 		case MSG_WRITE_BOOT_SECTOR:
526 			fWorkerThread->WriteBootSector(fDestMenu);
527 			break;
528 
529 		default:
530 			BWindow::MessageReceived(msg);
531 			break;
532 	}
533 }
534 
535 
536 bool
537 InstallerWindow::QuitRequested()
538 {
539 	if (fDriveSetupLaunched) {
540 		(new BAlert("driveSetup",
541 			"Please close the DriveSetup window before closing the "
542 			"Installer window.", "Ok"))->Go();
543 		return false;
544 	}
545 	_QuitCopyEngine(false);
546 	fWorkerThread->PostMessage(B_QUIT_REQUESTED);
547 	be_app->PostMessage(B_QUIT_REQUESTED);
548 	return true;
549 }
550 
551 
552 // #pragma mark -
553 
554 
555 void
556 InstallerWindow::_ShowOptionalPackages()
557 {
558 	if (fPackagesLayoutItem && fSizeViewLayoutItem) {
559 		fPackagesLayoutItem->SetVisible(fPackagesSwitch->Value());
560 		fSizeViewLayoutItem->SetVisible(fPackagesSwitch->Value());
561 	}
562 }
563 
564 
565 void
566 InstallerWindow::_LaunchDriveSetup()
567 {
568 	if (be_roster->Launch(DRIVESETUP_SIG) != B_OK) {
569 		// Try really hard to launch it. It's very likely that this fails,
570 		// when we run from the CD and there is only an incomplete mime
571 		// database for example...
572 		BPath path;
573 		if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK
574 			|| path.Append("DriveSetup") != B_OK) {
575 			path.SetTo("/boot/system/apps/DriveSetup");
576 		}
577 		BEntry entry(path.Path());
578 		entry_ref ref;
579 		if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) {
580 			BAlert* alert = new BAlert("error", "DriveSetup, the application "
581 				"to configure disk partitions, could not be launched.",
582 				"Ok");
583 			alert->Go();
584 		}
585 	}
586 }
587 
588 
589 void
590 InstallerWindow::_DisableInterface(bool disable)
591 {
592 	fSetupButton->SetEnabled(!disable);
593 	fMakeBootableButton->SetEnabled(!disable);
594 	fSrcMenuField->SetEnabled(!disable);
595 	fDestMenuField->SetEnabled(!disable);
596 }
597 
598 
599 void
600 InstallerWindow::_ScanPartitions()
601 {
602 	_SetStatusMessage("Scanning for disks" B_UTF8_ELLIPSIS);
603 
604 	BMenuItem *item;
605 	while ((item = fSrcMenu->RemoveItem((int32)0)))
606 		delete item;
607 	while ((item = fDestMenu->RemoveItem((int32)0)))
608 		delete item;
609 
610 	fWorkerThread->ScanDisksPartitions(fSrcMenu, fDestMenu);
611 
612 	if (fSrcMenu->ItemAt(0)) {
613 		_PublishPackages();
614 	}
615 	_UpdateControls();
616 }
617 
618 
619 void
620 InstallerWindow::_UpdateControls()
621 {
622 	PartitionMenuItem* srcItem = (PartitionMenuItem*)fSrcMenu->FindMarked();
623 	BString label;
624 	if (srcItem) {
625 		label = srcItem->MenuLabel();
626 	} else {
627 		if (fSrcMenu->CountItems() == 0)
628 			label = "<none>";
629 		else
630 			label = ((PartitionMenuItem*)fSrcMenu->ItemAt(0))->MenuLabel();
631 	}
632 	fSrcMenuField->MenuItem()->SetLabel(label.String());
633 
634 	// Disable any unsuitable target items, check if at least one partition
635 	// is suitable.
636 	bool foundOneSuitableTarget = false;
637 	for (int32 i = fDestMenu->CountItems() - 1; i >= 0; i--) {
638 		PartitionMenuItem* dstItem
639 			= (PartitionMenuItem*)fDestMenu->ItemAt(i);
640 		if (srcItem != NULL && dstItem->ID() == srcItem->ID()) {
641 			// Prevent the user from having picked the same partition as source
642 			// and destination.
643 			dstItem->SetEnabled(false);
644 			dstItem->SetMarked(false);
645 		} else
646 			dstItem->SetEnabled(dstItem->IsValidTarget());
647 
648 		if (dstItem->IsEnabled())
649 			foundOneSuitableTarget = true;
650 	}
651 
652 	PartitionMenuItem* dstItem = (PartitionMenuItem*)fDestMenu->FindMarked();
653 	if (dstItem) {
654 		label = dstItem->MenuLabel();
655 	} else {
656 		if (fDestMenu->CountItems() == 0)
657 			label = "<none>";
658 		else
659 			label = "Please Choose Target";
660 	}
661 	fDestMenuField->MenuItem()->SetLabel(label.String());
662 
663 	if (srcItem && dstItem) {
664 		char message[255];
665 		sprintf(message, "Press the Begin button to install from '%s' onto "
666 			"'%s'.", srcItem->Name(), dstItem->Name());
667 		_SetStatusMessage(message);
668 	} else if (srcItem) {
669 		_SetStatusMessage("Choose the disk you want to install onto from the "
670 			"pop-up menu. Then click \"Begin\".");
671 	} else if (dstItem) {
672 		_SetStatusMessage("Choose the source disk from the "
673 			"pop-up menu. Then click \"Begin\".");
674 	} else {
675 		_SetStatusMessage("Choose the source and destination disk from the "
676 			"pop-up menus. Then click \"Begin\".");
677 	}
678 
679 	fInstallStatus = kReadyForInstall;
680 	fBeginButton->SetLabel("Begin");
681 	fBeginButton->SetEnabled(srcItem && dstItem);
682 
683 	// adjust "Write Boot Sector" button
684 	label = "Write Boot Sector";
685 	if (dstItem)
686 		label << " to \'" <<dstItem->Name() << '\'';
687 	fMakeBootableButton->SetEnabled(dstItem);
688 	fMakeBootableButton->SetLabel(label.String());
689 
690 	if (!fEncouragedToSetupPartitions && !foundOneSuitableTarget) {
691 		// Focus the users attention on the DriveSetup button
692 		fEncouragedToSetupPartitions = true;
693 		(new BAlert("use drive setup", "No partitions have been found that "
694 			"are suitable for installation. Please setup partitions and "
695 			"initialize at least one partition with the Be File System." ,
696 			"Ok"))->Go();
697 	}
698 }
699 
700 
701 void
702 InstallerWindow::_PublishPackages()
703 {
704 	fPackagesView->Clean();
705 	PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked();
706 	if (!item)
707 		return;
708 
709 #ifdef __HAIKU__
710 	BPath directory;
711 	BDiskDeviceRoster roster;
712 	BDiskDevice device;
713 	BPartition *partition;
714 	if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) {
715 		if (partition->GetMountPoint(&directory) != B_OK)
716 			return;
717 	} else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) {
718 		if (device.GetMountPoint(&directory) != B_OK)
719 			return;
720 	} else
721 		return; // shouldn't happen
722 #else
723 	BPath directory = "/BeOS 5 PE Max Edition V3.1 beta";
724 #endif
725 
726 	directory.Append(PACKAGES_DIRECTORY);
727 	BDirectory dir(directory.Path());
728 	if (dir.InitCheck() != B_OK)
729 		return;
730 
731 	BEntry packageEntry;
732 	BList packages;
733 	while (dir.GetNextEntry(&packageEntry) == B_OK) {
734 		Package *package = Package::PackageFromEntry(packageEntry);
735 		if (package) {
736 			packages.AddItem(package);
737 		}
738 	}
739 	packages.SortItems(_ComparePackages);
740 
741 	fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX));
742 	PostMessage(PACKAGE_CHECKBOX);
743 }
744 
745 
746 void
747 InstallerWindow::_SetStatusMessage(const char *text)
748 {
749 	fStatusView->SetText(text);
750 }
751 
752 
753 void
754 InstallerWindow::_QuitCopyEngine(bool askUser)
755 {
756 	if (fCopyEngineLock == NULL)
757 		return;
758 
759 	// first of all block the copy engine
760 	fCopyEngineLock->Lock();
761 
762 	bool quit = true;
763 	if (askUser) {
764 		quit = (new BAlert("cancel",
765 			"Are you sure you want to to stop the installation?",
766 			"Continue", "Stop", 0,
767 			B_WIDTH_AS_USUAL, B_STOP_ALERT))->Go() != 0;
768 	}
769 
770 	if (quit) {
771 		int32 tries = 0;
772 		// wait until the engine blocks
773 		while (fCopyEngineLock->CountLockRequests() < 2) {
774 			// TODO: There is a race here, the copy engine
775 			// may have finished before we locked the engine
776 			// lock. That's why we limit the number of tries
777 			// here.
778 			tries++;
779 			if (tries > 100)
780 				break;
781 			snooze(3000);
782 		}
783 		// make it quit by having it's lock fail
784 		delete fCopyEngineLock;
785 		fCopyEngineLock = NULL;
786 	} else
787 		fCopyEngineLock->Unlock();
788 }
789 
790 
791 // #pragma mark -
792 
793 
794 int
795 InstallerWindow::_ComparePackages(const void *firstArg, const void *secondArg)
796 {
797 	const Group *group1 = *static_cast<const Group * const *>(firstArg);
798 	const Group *group2 = *static_cast<const Group * const *>(secondArg);
799 	const Package *package1 = dynamic_cast<const Package *>(group1);
800 	const Package *package2 = dynamic_cast<const Package *>(group2);
801 	int sameGroup = strcmp(group1->GroupName(), group2->GroupName());
802 	if (sameGroup != 0)
803 		return sameGroup;
804 	if (!package2)
805 		return -1;
806 	if (!package1)
807 		return 1;
808 	return strcmp(package1->Name(), package2->Name());
809 }
810 
811 
812