xref: /haiku/src/apps/installer/InstallerWindow.cpp (revision b9068dbf95e05818c251ab36f8cb1d8619d2c9be)
1 /*
2  * Copyright 2005-2008, Jérôme DUVAL. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "InstallerWindow.h"
7 
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include <Alert.h>
12 #include <Application.h>
13 #include <Autolock.h>
14 #include <Box.h>
15 #include <ClassInfo.h>
16 #include <Directory.h>
17 #include <Path.h>
18 #include <PopUpMenu.h>
19 #include <Roster.h>
20 #include <String.h>
21 #include <TranslationUtils.h>
22 #include <TranslatorFormats.h>
23 
24 #include "DialogPane.h"
25 #include "PartitionMenuItem.h"
26 
27 
28 #define DRIVESETUP_SIG "application/x-vnd.Haiku-DriveSetup"
29 
30 const uint32 BEGIN_MESSAGE = 'iBGN';
31 const uint32 SHOW_BOTTOM_MESSAGE = 'iSBT';
32 const uint32 SETUP_MESSAGE = 'iSEP';
33 const uint32 START_SCAN = 'iSSC';
34 const uint32 PACKAGE_CHECKBOX = 'iPCB';
35 
36 class LogoView : public BBox {
37 	public:
38 		LogoView(const BRect &r);
39 		~LogoView(void);
40 		virtual void Draw(BRect update);
41 	private:
42 		BBitmap			*fLogo;
43 		BPoint			fDrawPoint;
44 };
45 
46 
47 LogoView::LogoView(const BRect &r)
48 	: BBox(r, "logoview", B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW,
49 		B_NO_BORDER)
50 {
51 	fLogo = BTranslationUtils::GetBitmap(B_PNG_FORMAT, "haikulogo.png");
52 	if (fLogo) {
53 		fDrawPoint.x = (r.Width() - fLogo->Bounds().Width()) / 2;
54 		fDrawPoint.y = 0;
55 	}
56 }
57 
58 
59 LogoView::~LogoView(void)
60 {
61 	delete fLogo;
62 }
63 
64 
65 void
66 LogoView::Draw(BRect update)
67 {
68 	if (fLogo)
69 		DrawBitmap(fLogo, fDrawPoint);
70 }
71 
72 
73 // #pragma mark -
74 
75 
76 InstallerWindow::InstallerWindow(BRect frame_rect)
77 	: BWindow(frame_rect, "Installer", B_TITLED_WINDOW,
78 		B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_NOT_RESIZABLE),
79 	fDriveSetupLaunched(false),
80 	fInstallStatus(kReadyForInstall),
81 	fLastSrcItem(NULL),
82 	fLastTargetItem(NULL)
83 {
84 	fCopyEngine = new CopyEngine(this);
85 
86 	BRect bounds = Bounds();
87 	bounds.bottom += 1;
88 	bounds.right += 1;
89 	fBackBox = new BBox(bounds, NULL, B_FOLLOW_ALL,
90 		B_WILL_DRAW | B_FRAME_EVENTS, B_NO_BORDER);
91 	AddChild(fBackBox);
92 
93 	BRect logoRect = fBackBox->Bounds();
94 	logoRect.left += 1;
95 	logoRect.top = 12;
96 	logoRect.right -= 226;
97 	logoRect.bottom = logoRect.top + 46 + B_H_SCROLL_BAR_HEIGHT;
98 	LogoView *logoView = new LogoView(logoRect);
99 	fBackBox->AddChild(logoView);
100 
101 	BRect statusRect(bounds.right - 222, logoRect.top + 2, bounds.right - 14,
102 		logoRect.bottom - B_H_SCROLL_BAR_HEIGHT + 4);
103 	BRect textRect(statusRect);
104 	textRect.OffsetTo(B_ORIGIN);
105 	textRect.InsetBy(2, 2);
106 	fStatusView = new BTextView(statusRect, "statusView", textRect,
107 		be_plain_font, NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);
108 	fStatusView->MakeEditable(false);
109 	fStatusView->MakeSelectable(false);
110 
111 	BScrollView *scroll = new BScrollView("statusScroll", fStatusView,
112 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS);
113 	fBackBox->AddChild(scroll);
114 
115 	fBeginButton = new BButton(BRect(bounds.right - 90, bounds.bottom - 35,
116 		bounds.right - 11, bounds.bottom - 11),
117 		"begin_button", "Begin", new BMessage(BEGIN_MESSAGE),
118 		B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
119 	fBeginButton->MakeDefault(true);
120 	fBeginButton->SetEnabled(false);
121 	fBackBox->AddChild(fBeginButton);
122 
123 	fSetupButton = new BButton(BRect(bounds.left + 11, bounds.bottom - 35,
124 		bounds.left + be_plain_font->StringWidth("Setup partitions") + 36,
125 		bounds.bottom - 22), "setup_button", "Setup partitions" B_UTF8_ELLIPSIS,
126 		new BMessage(SETUP_MESSAGE), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
127 	fBackBox->AddChild(fSetupButton);
128 	fSetupButton->Hide();
129 
130 	fPackagesView = new PackagesView(BRect(bounds.left + 12, bounds.top + 4,
131 		bounds.right - 15 - B_V_SCROLL_BAR_WIDTH, bounds.bottom - 61),
132 		"packages_view");
133 	fPackagesScrollView = new BScrollView("packagesScroll", fPackagesView,
134 		B_FOLLOW_LEFT | B_FOLLOW_BOTTOM, B_WILL_DRAW, false, true);
135 	fBackBox->AddChild(fPackagesScrollView);
136 	fPackagesScrollView->Hide();
137 
138 	fDrawButton = new PaneSwitch(BRect(bounds.left + 12, bounds.bottom - 33,
139 		bounds.left + 120, bounds.bottom - 20), "options_button");
140 
141 	fDrawButton->SetLabels("Fewer options", "More options");
142 	fDrawButton->SetMessage(new BMessage(SHOW_BOTTOM_MESSAGE));
143 
144 	fBackBox->AddChild(fDrawButton);
145 
146 	fDestMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false);
147 	fSrcMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false);
148 
149 	BRect fieldRect(bounds.left + 13, bounds.top + 70, bounds.right - 13,
150 		bounds.top + 90);
151 	fSrcMenuField = new BMenuField(fieldRect, "srcMenuField",
152 		"Install from: ", fSrcMenu);
153 	fSrcMenuField->SetDivider(bounds.right - 300);
154 	fSrcMenuField->SetAlignment(B_ALIGN_RIGHT);
155 	fBackBox->AddChild(fSrcMenuField);
156 
157 	fieldRect.OffsetBy(0, 23);
158 	fDestMenuField = new BMenuField(fieldRect, "destMenuField",
159 		"Onto: ", fDestMenu);
160 	fDestMenuField->SetDivider(bounds.right - 300);
161 	fDestMenuField->SetAlignment(B_ALIGN_RIGHT);
162 	fBackBox->AddChild(fDestMenuField);
163 
164 	BRect sizeRect = fBackBox->Bounds();
165 	sizeRect.top = 105;
166 	sizeRect.bottom = sizeRect.top + 15;
167 	sizeRect.right -= 12;
168 	const char* requiredDiskSpaceString = "Disk space required: 0.0 KB";
169 	sizeRect.left = sizeRect.right - be_plain_font->StringWidth(
170 		requiredDiskSpaceString) - 40;
171 	fSizeView = new BStringView(sizeRect, "size_view",
172 		requiredDiskSpaceString, B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
173 	fSizeView->SetAlignment(B_ALIGN_RIGHT);
174 	fBackBox->AddChild(fSizeView);
175 	fSizeView->Hide();
176 
177 	// finish creating window
178 	Show();
179 
180 	fDriveSetupLaunched = be_roster->IsRunning(DRIVESETUP_SIG);
181 	be_roster->StartWatching(this);
182 
183 	PostMessage(START_SCAN);
184 }
185 
186 InstallerWindow::~InstallerWindow()
187 {
188 	be_roster->StopWatching(this);
189 }
190 
191 
192 void
193 InstallerWindow::MessageReceived(BMessage *msg)
194 {
195 	switch (msg->what) {
196 		case RESET_INSTALL:
197 			fInstallStatus = kReadyForInstall;
198 			fBeginButton->SetEnabled(true);
199 			DisableInterface(false);
200 			fBeginButton->SetLabel("Begin");
201 			break;
202 		case START_SCAN:
203 			StartScan();
204 			break;
205 		case BEGIN_MESSAGE:
206 			switch (fInstallStatus) {
207 				case kReadyForInstall:
208 				{
209 					BList *list = new BList();
210 					int32 size = 0;
211 					fPackagesView->GetPackagesToInstall(list, &size);
212 					fCopyEngine->SetPackagesList(list);
213 					fCopyEngine->SetSpaceRequired(size);
214 					fInstallStatus = kInstalling;
215 					BMessenger(fCopyEngine).SendMessage(ENGINE_START);
216 					fBeginButton->SetLabel("Stop");
217 					DisableInterface(true);
218 					break;
219 				}
220 				case kInstalling:
221 					if (fCopyEngine->Cancel()) {
222 						fInstallStatus = kCancelled;
223 						SetStatusMessage("Installation cancelled.");
224 					}
225 					break;
226 				case kFinished:
227 					PostMessage(B_QUIT_REQUESTED);
228 					break;
229 				case kCancelled:
230 					break;
231 			}
232 			break;
233 		case SHOW_BOTTOM_MESSAGE:
234 			ShowBottom();
235 			break;
236 		case SRC_PARTITION:
237 			if (fLastSrcItem == fSrcMenu->FindMarked())
238 				break;
239 			fLastSrcItem = fSrcMenu->FindMarked();
240 			PublishPackages();
241 			AdjustMenus();
242 			break;
243 		case TARGET_PARTITION:
244 			if (fLastTargetItem == fDestMenu->FindMarked())
245 				break;
246 			fLastTargetItem = fDestMenu->FindMarked();
247 			AdjustMenus();
248 			break;
249 		case SETUP_MESSAGE:
250 			LaunchDriveSetup();
251 			break;
252 		case PACKAGE_CHECKBOX: {
253 			char buffer[15];
254 			fPackagesView->GetTotalSizeAsString(buffer);
255 			char string[255];
256 			sprintf(string, "Disk space required: %s", buffer);
257 			fSizeView->SetText(string);
258 			break;
259 		}
260 		case STATUS_MESSAGE: {
261 			if (fInstallStatus != kInstalling)
262 				break;
263 			const char *status;
264 			if (msg->FindString("status", &status) == B_OK) {
265 				fLastStatus = fStatusView->Text();
266 				SetStatusMessage(status);
267 			} else
268 				SetStatusMessage(fLastStatus.String());
269 			break;
270 		}
271 		case INSTALL_FINISHED:
272 			fBeginButton->SetLabel("Quit");
273 			SetStatusMessage("Installation completed.");
274 			fInstallStatus = kFinished;
275 			DisableInterface(false);
276 			break;
277 		case B_SOME_APP_LAUNCHED:
278 		case B_SOME_APP_QUIT:
279 		{
280 			const char *signature;
281 			if (msg->FindString("be:signature", &signature) == B_OK
282 				&& strcasecmp(signature, DRIVESETUP_SIG) == 0) {
283 				fDriveSetupLaunched = msg->what == B_SOME_APP_LAUNCHED;
284 				fBeginButton->SetEnabled(!fDriveSetupLaunched);
285 				DisableInterface(fDriveSetupLaunched);
286 				if (fDriveSetupLaunched)
287 					SetStatusMessage("Running DriveSetup" B_UTF8_ELLIPSIS
288 						"\nClose DriveSetup to continue with the\n"
289 						"installation.");
290 				else
291 					StartScan();
292 			}
293 			break;
294 		}
295 		default:
296 			BWindow::MessageReceived(msg);
297 			break;
298 	}
299 }
300 
301 bool
302 InstallerWindow::QuitRequested()
303 {
304 	if (fDriveSetupLaunched) {
305 		(new BAlert("driveSetup",
306 			"Please close the DriveSetup window before closing the\n"
307 			"Installer window.", "OK"))->Go();
308 		return false;
309 	}
310 	be_app->PostMessage(B_QUIT_REQUESTED);
311 	fCopyEngine->PostMessage(B_QUIT_REQUESTED);
312 	return true;
313 }
314 
315 
316 void
317 InstallerWindow::ShowBottom()
318 {
319 	if (fDrawButton->Value()) {
320 		ResizeTo(INSTALLER_RIGHT, 306);
321 		if (fSetupButton->IsHidden())
322 			fSetupButton->Show();
323 		if (fPackagesScrollView->IsHidden())
324 			fPackagesScrollView->Show();
325 		if (fSizeView->IsHidden())
326 			fSizeView->Show();
327 	} else {
328 		if (!fSetupButton->IsHidden())
329 			fSetupButton->Hide();
330 		if (!fPackagesScrollView->IsHidden())
331 			fPackagesScrollView->Hide();
332 		if (!fSizeView->IsHidden())
333 			fSizeView->Hide();
334 		ResizeTo(INSTALLER_RIGHT, 160);
335 	}
336 }
337 
338 
339 void
340 InstallerWindow::LaunchDriveSetup()
341 {
342 	if (be_roster->Launch(DRIVESETUP_SIG) != B_OK) {
343 		// Try really hard to launch it. It's very likely that this fails,
344 		// when we run from the CD and there is only an incomplete mime
345 		// database for example...
346 		BPath path;
347 		if (find_directory(B_SYSTEM_APPS_DIRECTORY, &path) != B_OK
348 			|| path.Append("DriveSetup") != B_OK) {
349 			path.SetTo("/boot/system/apps/DriveSetup");
350 		}
351 		BEntry entry(path.Path());
352 		entry_ref ref;
353 		if (entry.GetRef(&ref) != B_OK || be_roster->Launch(&ref) != B_OK) {
354 			BAlert* alert = new BAlert("error", "DriveSetup, the application "
355 				"to configure disk partitions, could not be launched.",
356 				"Ok");
357 			alert->Go();
358 		}
359 	}
360 }
361 
362 
363 void
364 InstallerWindow::DisableInterface(bool disable)
365 {
366 	fSetupButton->SetEnabled(!disable);
367 	fSrcMenuField->SetEnabled(!disable);
368 	fDestMenuField->SetEnabled(!disable);
369 }
370 
371 
372 void
373 InstallerWindow::StartScan()
374 {
375 	SetStatusMessage("Scanning for disks" B_UTF8_ELLIPSIS);
376 
377 	BMenuItem *item;
378 	while ((item = fSrcMenu->RemoveItem((int32)0)))
379 		delete item;
380 	while ((item = fDestMenu->RemoveItem((int32)0)))
381 		delete item;
382 
383 	fCopyEngine->ScanDisksPartitions(fSrcMenu, fDestMenu);
384 
385 	if (fSrcMenu->ItemAt(0)) {
386 		PublishPackages();
387 	}
388 	AdjustMenus();
389 	SetStatusMessage("Choose the disk you want to install onto from the "
390 		"pop-up menu. Then click \"Begin\".");
391 }
392 
393 
394 void
395 InstallerWindow::AdjustMenus()
396 {
397 	PartitionMenuItem *item1 = (PartitionMenuItem *)fSrcMenu->FindMarked();
398 	BString label;
399 	if (item1) {
400 		label = item1->MenuLabel();
401 	} else {
402 		if (fSrcMenu->CountItems() == 0)
403 			label = "<none>";
404 		else {
405 			label = ((PartitionMenuItem *)fSrcMenu->ItemAt(0))->MenuLabel();
406 		}
407 	}
408 	fSrcMenuField->TruncateString(&label, B_TRUNCATE_END, 260);
409 	fSrcMenuField->MenuItem()->SetLabel(label.String());
410 
411 	PartitionMenuItem *item2 = (PartitionMenuItem *)fDestMenu->FindMarked();
412 	if (item2) {
413 		label = item2->MenuLabel();
414 	} else {
415 		if (fDestMenu->CountItems() == 0)
416 			label = "<none>";
417 		else {
418 			label = ((PartitionMenuItem *)fDestMenu->ItemAt(0))->MenuLabel();
419 		}
420 	}
421 	fDestMenuField->TruncateString(&label, B_TRUNCATE_END, 260);
422 	fDestMenuField->MenuItem()->SetLabel(label.String());
423 	char message[255];
424 	sprintf(message, "Press the Begin button to install from '%s' onto '%s'",
425 		item1 ? item1->Name() : "null", item2 ? item2->Name() : "null");
426 	SetStatusMessage(message);
427 	if (item1 && item2)
428 		fBeginButton->SetEnabled(true);
429 }
430 
431 
432 void
433 InstallerWindow::PublishPackages()
434 {
435 	fPackagesView->Clean();
436 	PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked();
437 	if (!item)
438 		return;
439 
440 #ifdef __HAIKU__
441 	BPath directory;
442 	BDiskDeviceRoster roster;
443 	BDiskDevice device;
444 	BPartition *partition;
445 	if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) {
446 		if (partition->GetMountPoint(&directory) != B_OK)
447 			return;
448 	} else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) {
449 		if (device.GetMountPoint(&directory) != B_OK)
450 			return;
451 	} else
452 		return; // shouldn't happen
453 #else
454 	BPath directory = "/BeOS 5 PE Max Edition V3.1 beta";
455 #endif
456 
457 	directory.Append(PACKAGES_DIRECTORY);
458 	BDirectory dir(directory.Path());
459 	if (dir.InitCheck() != B_OK)
460 		return;
461 
462 	BEntry packageEntry;
463 	BList packages;
464 	while (dir.GetNextEntry(&packageEntry) == B_OK) {
465 		Package *package = Package::PackageFromEntry(packageEntry);
466 		if (package) {
467 			packages.AddItem(package);
468 		}
469 	}
470 	packages.SortItems(ComparePackages);
471 
472 	fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX));
473 	PostMessage(PACKAGE_CHECKBOX);
474 }
475 
476 
477 int
478 InstallerWindow::ComparePackages(const void *firstArg, const void *secondArg)
479 {
480 	const Group *group1 = *static_cast<const Group * const *>(firstArg);
481 	const Group *group2 = *static_cast<const Group * const *>(secondArg);
482 	const Package *package1 = dynamic_cast<const Package *>(group1);
483 	const Package *package2 = dynamic_cast<const Package *>(group2);
484 	int sameGroup = strcmp(group1->GroupName(), group2->GroupName());
485 	if (sameGroup != 0)
486 		return sameGroup;
487 	if (!package2)
488 		return -1;
489 	if (!package1)
490 		return 1;
491 	return strcmp(package1->Name(), package2->Name());
492 }
493 
494 
495 void
496 InstallerWindow::SetStatusMessage(const char *text)
497 {
498 	fStatusView->SetText(text);
499 }
500 
501