xref: /haiku/src/apps/installer/InstallerWindow.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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 	fCopyEngine(this)
69 {
70 	BRect bounds = Bounds();
71 	bounds.bottom += 1;
72 	bounds.right += 1;
73 	fBackBox = new BBox(bounds, NULL, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS, B_FANCY_BORDER);
74 	AddChild(fBackBox);
75 
76 	BRect logoRect = fBackBox->Bounds();
77 	logoRect.left += 1;
78 	logoRect.top = 12;
79 	logoRect.right -= 226;
80 	logoRect.bottom = logoRect.top + 46 + B_H_SCROLL_BAR_HEIGHT;
81 	LogoView *logoView = new LogoView(logoRect);
82 	fBackBox->AddChild(logoView);
83 
84 	BRect statusRect(bounds.right-222, logoRect.top+2, bounds.right-14, logoRect.bottom - B_H_SCROLL_BAR_HEIGHT+4);
85 	BRect textRect(statusRect);
86 	textRect.OffsetTo(B_ORIGIN);
87 	textRect.InsetBy(2,2);
88 	fStatusView = new BTextView(statusRect, "statusView", textRect,
89 		be_plain_font, NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW);
90 	fStatusView->MakeEditable(false);
91 	fStatusView->MakeSelectable(false);
92 
93 	BScrollView *scroll = new BScrollView("statusScroll", fStatusView, B_FOLLOW_LEFT|B_FOLLOW_TOP, B_WILL_DRAW|B_FRAME_EVENTS);
94         fBackBox->AddChild(scroll);
95 
96 	fBeginButton = new BButton(BRect(bounds.right-90, bounds.bottom-35, bounds.right-11, bounds.bottom-11),
97 		"begin_button", "Begin", new BMessage(BEGIN_MESSAGE), B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
98 	fBeginButton->MakeDefault(true);
99 	fBackBox->AddChild(fBeginButton);
100 
101 	fSetupButton = new BButton(BRect(bounds.left+11, bounds.bottom-35, bounds.left+121, bounds.bottom-22),
102 		"setup_button", "Setup partitions" B_UTF8_ELLIPSIS, new BMessage(SETUP_MESSAGE), B_FOLLOW_LEFT|B_FOLLOW_BOTTOM);
103 	fBackBox->AddChild(fSetupButton);
104 	fSetupButton->Hide();
105 
106 	fPackagesView = new PackagesView(BRect(bounds.left+12, bounds.top+4, bounds.right-15-B_V_SCROLL_BAR_WIDTH, bounds.bottom-61), "packages_view");
107 	fPackagesScrollView = new BScrollView("packagesScroll", fPackagesView, B_FOLLOW_LEFT | B_FOLLOW_BOTTOM, B_WILL_DRAW,
108 		false, true);
109 	fBackBox->AddChild(fPackagesScrollView);
110 	fPackagesScrollView->Hide();
111 
112 	fDrawButton = new DrawButton(BRect(bounds.left+12, bounds.bottom-33, bounds.left+120, bounds.bottom-20),
113 		"options_button", "Fewer options", "More options", new BMessage(SHOW_BOTTOM_MESSAGE));
114 	fBackBox->AddChild(fDrawButton);
115 
116 	fDestMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false);
117 	fSrcMenu = new BPopUpMenu("scanning" B_UTF8_ELLIPSIS, true, false);
118 
119 	BRect fieldRect(bounds.left+50, bounds.top+70, bounds.right-13, bounds.top+90);
120 	fSrcMenuField = new BMenuField(fieldRect, "srcMenuField",
121                 "Install from: ", fSrcMenu);
122 	fSrcMenuField->SetDivider(bounds.right-274);
123 	fSrcMenuField->SetAlignment(B_ALIGN_RIGHT);
124 	fBackBox->AddChild(fSrcMenuField);
125 
126 	fieldRect.OffsetBy(0,23);
127 	fDestMenuField = new BMenuField(fieldRect, "destMenuField",
128 		"Onto: ", fDestMenu);
129 	fDestMenuField->SetDivider(bounds.right-274);
130 	fDestMenuField->SetAlignment(B_ALIGN_RIGHT);
131 	fBackBox->AddChild(fDestMenuField);
132 
133 	BRect sizeRect = fBackBox->Bounds();
134 	sizeRect.top = 105;
135 	sizeRect.bottom = sizeRect.top + 15;
136 	sizeRect.right -= 12;
137 	sizeRect.left = sizeRect.right - 170;
138 	fSizeView = new BStringView(sizeRect, "size_view", "Disk space required: 0.0 KB", B_FOLLOW_RIGHT | B_FOLLOW_BOTTOM);
139 	fSizeView->SetAlignment(B_ALIGN_RIGHT);
140 	fBackBox->AddChild(fSizeView);
141 	fSizeView->Hide();
142 
143 	// finish creating window
144 	Show();
145 
146 	fDriveSetupLaunched = be_roster->IsRunning(DRIVESETUP_SIG);
147 	be_roster->StartWatching(this);
148 
149 	PostMessage(START_SCAN);
150 }
151 
152 InstallerWindow::~InstallerWindow()
153 {
154 	be_roster->StopWatching(this);
155 }
156 
157 
158 void
159 InstallerWindow::MessageReceived(BMessage *msg)
160 {
161 	switch (msg->what) {
162 		case START_SCAN:
163 			StartScan();
164 			break;
165 		case BEGIN_MESSAGE:
166 			fCopyEngine.Start();
167 			break;
168 		case SHOW_BOTTOM_MESSAGE:
169 			ShowBottom();
170 			break;
171 		case SRC_PARTITION:
172 			PublishPackages();
173 			AdjustMenus();
174 			break;
175 		case TARGET_PARTITION:
176 			AdjustMenus();
177 			break;
178 		case SETUP_MESSAGE:
179 			LaunchDriveSetup();
180 			break;
181 		case PACKAGE_CHECKBOX: {
182 			char buffer[15];
183 			fPackagesView->GetTotalSizeAsString(buffer);
184 			char string[255];
185 			sprintf(string, "Disk space required: %s", buffer);
186 			fSizeView->SetText(string);
187 			break;
188 		}
189 		case B_SOME_APP_LAUNCHED:
190 		case B_SOME_APP_QUIT:
191 		{
192 			const char *signature;
193 			if (msg->FindString("be:signature", &signature)==B_OK
194 				&& strcasecmp(signature, DRIVESETUP_SIG)==0) {
195 				DisableInterface(msg->what == B_SOME_APP_LAUNCHED);
196                         }
197                         break;
198                 }
199 		default:
200 			BWindow::MessageReceived(msg);
201 			break;
202 	}
203 }
204 
205 bool
206 InstallerWindow::QuitRequested()
207 {
208 	if (fDriveSetupLaunched) {
209 		(new BAlert("driveSetup",
210 			"Please close the DriveSetup window before closing the\nInstaller window.", "OK"))->Go();
211 		return false;
212 	}
213 	be_app->PostMessage(B_QUIT_REQUESTED);
214 	return true;
215 }
216 
217 
218 void
219 InstallerWindow::ShowBottom()
220 {
221 	if (fDrawButton->Value()) {
222 		ResizeTo(INSTALLER_RIGHT, 306);
223 		if (fSetupButton->IsHidden())
224 			fSetupButton->Show();
225 		if (fPackagesScrollView->IsHidden())
226 			fPackagesScrollView->Show();
227 		if (fSizeView->IsHidden())
228 			fSizeView->Show();
229 	} else {
230 		if (!fSetupButton->IsHidden())
231 			fSetupButton->Hide();
232 		if (!fPackagesScrollView->IsHidden())
233 			fPackagesScrollView->Hide();
234 		if (!fSizeView->IsHidden())
235 			fSizeView->Hide();
236 		ResizeTo(INSTALLER_RIGHT, 160);
237 	}
238 }
239 
240 
241 void
242 InstallerWindow::LaunchDriveSetup()
243 {
244 	if (be_roster->Launch(DRIVESETUP_SIG)!=B_OK)
245 		fprintf(stderr, "There was an error while launching DriveSetup\n");
246 }
247 
248 
249 void
250 InstallerWindow::DisableInterface(bool disable)
251 {
252 	if (!disable) {
253 		StartScan();
254 	}
255 	fDriveSetupLaunched = disable;
256 	fBeginButton->SetEnabled(!disable);
257 	fSetupButton->SetEnabled(!disable);
258 	fSrcMenuField->SetEnabled(!disable);
259 	fDestMenuField->SetEnabled(!disable);
260 	if (disable)
261 		SetStatusMessage("Running DriveSetup" B_UTF8_ELLIPSIS "\nClose DriveSetup to continue with the\ninstallation.");
262 }
263 
264 
265 void
266 InstallerWindow::StartScan()
267 {
268 	SetStatusMessage("Scanning for disks" B_UTF8_ELLIPSIS);
269 
270 	BMenuItem *item;
271 	while ((item = fSrcMenu->RemoveItem((int32)0)))
272 		delete item;
273 	while ((item = fDestMenu->RemoveItem((int32)0)))
274 		delete item;
275 
276 	fCopyEngine.ScanDisksPartitions(fSrcMenu, fDestMenu);
277 
278 	if (fSrcMenu->ItemAt(0)) {
279 		PublishPackages();
280 	}
281 	AdjustMenus();
282 	SetStatusMessage("Choose the disk you want to install onto from the pop-up menu. Then click \"Begin\".");
283 }
284 
285 
286 void
287 InstallerWindow::AdjustMenus()
288 {
289 	PartitionMenuItem *item1 = (PartitionMenuItem *)fSrcMenu->FindMarked();
290 	if (!item1) {
291 	} else {
292 		fSrcMenuField->MenuItem()->SetLabel(item1->MenuLabel());
293 	}
294 
295 	PartitionMenuItem *item2 = (PartitionMenuItem *)fDestMenu->FindMarked();
296 	if (!item2) {
297 	} else {
298 		fDestMenuField->MenuItem()->SetLabel(item2->MenuLabel());
299 	}
300 	char message[255];
301 	sprintf(message, "Press the Begin button to install from '%s' onto '%s'", item1->Name(), item2->Name());
302 	SetStatusMessage(message);
303 }
304 
305 
306 void
307 InstallerWindow::PublishPackages()
308 {
309 	fPackagesView->Clean();
310 	PartitionMenuItem *item = (PartitionMenuItem *)fSrcMenu->FindMarked();
311 	if (!item)
312 		return;
313 
314 	BPath directory;
315 	BDiskDeviceRoster roster;
316 	BDiskDevice device;
317 	BPartition *partition;
318 	if (roster.GetPartitionWithID(item->ID(), &device, &partition) == B_OK) {
319 		if (partition->GetMountPoint(&directory)!=B_OK)
320 			return;
321 	} else if (roster.GetDeviceWithID(item->ID(), &device) == B_OK) {
322 		if (device.GetMountPoint(&directory)!=B_OK)
323 			return;
324 	} else
325 		return; // shouldn't happen
326 
327 	directory.Append("_packages_");
328 	BDirectory dir(directory.Path());
329 	if (dir.InitCheck()!=B_OK)
330 		return;
331 
332 	BEntry packageEntry;
333 	BList packages;
334 	while (dir.GetNextEntry(&packageEntry)==B_OK) {
335 		Package *package = Package::PackageFromEntry(packageEntry);
336 		if (package) {
337 			packages.AddItem(package);
338 		}
339 	}
340 	packages.SortItems(ComparePackages);
341 
342 	fPackagesView->AddPackages(packages, new BMessage(PACKAGE_CHECKBOX));
343 	PostMessage(PACKAGE_CHECKBOX);
344 }
345 
346 
347 int
348 InstallerWindow::ComparePackages(const void *firstArg, const void *secondArg)
349 {
350 	const Group *group1 = *static_cast<const Group * const *>(firstArg);
351 	const Group *group2 = *static_cast<const Group * const *>(secondArg);
352 	const Package *package1 = dynamic_cast<const Package *>(group1);
353 	const Package *package2 = dynamic_cast<const Package *>(group2);
354 	int sameGroup = strcmp(group1->GroupName(), group2->GroupName());
355 	if (sameGroup != 0)
356 		return sameGroup;
357 	if (!package2)
358 		return -1;
359 	if (!package1)
360 		return 1;
361 	return strcmp(package1->Name(), package2->Name());
362 }
363 
364 
365 void
366 InstallerWindow::SetStatusMessage(char *text)
367 {
368 	BAutolock(this);
369 	fStatusView->SetText(text);
370 }
371