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