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