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