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