xref: /haiku/src/apps/packageinstaller/PackageView.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
1 /*
2  * Copyright 2007-2009, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Author:
6  *		Łukasz 'Sil2100' Zemczak <sil2100@vexillium.org>
7  */
8 
9 
10 #include "InstalledPackageInfo.h"
11 #include "PackageImageViewer.h"
12 #include "PackageTextViewer.h"
13 #include "PackageView.h"
14 
15 #include <Alert.h>
16 #include <Button.h>
17 #include <Directory.h>
18 #include <FindDirectory.h>
19 #include <MenuItem.h>
20 #include <Path.h>
21 #include <PopUpMenu.h>
22 #include <ScrollView.h>
23 #include <TextView.h>
24 #include <Volume.h>
25 #include <VolumeRoster.h>
26 #include <Window.h>
27 
28 #include <GroupLayout.h>
29 #include <GroupLayoutBuilder.h>
30 #include <GroupView.h>
31 
32 #include <fs_info.h>
33 #include <stdio.h> // For debugging
34 
35 // Macro reserved for later localization
36 #define T(x) x
37 
38 const float kMaxDescHeight = 125.0f;
39 const uint32 kSeparatorIndex = 3;
40 
41 
42 
43 static void
44 convert_size(uint64 size, char *buffer, uint32 n)
45 {
46 	if (size < 1024)
47 		snprintf(buffer, n, "%llu bytes", size);
48 	else if (size < 1024 * 1024)
49 		snprintf(buffer, n, "%.1f KiB", size / 1024.0f);
50 	else if (size < 1024 * 1024 * 1024)
51 		snprintf(buffer, n, "%.1f MiB", size / (1024.0f*1024.0f));
52 	else
53 		snprintf(buffer, n, "%.1f GiB", size / (1024.0f*1024.0f*1024.0f));
54 }
55 
56 
57 
58 // #pragma mark -
59 
60 
61 PackageView::PackageView(BRect frame, const entry_ref *ref)
62 	:	BView(frame, "package_view", B_FOLLOW_NONE, 0),
63 		//BView("package_view", B_WILL_DRAW, new BGroupLayout(B_HORIZONTAL)),
64 	fOpenPanel(new BFilePanel(B_OPEN_PANEL, NULL, NULL,
65 		B_DIRECTORY_NODE, false)),
66 	fInfo(ref)
67 {
68 	_InitView();
69 
70 	// Check whether the package has been successfuly parsed
71 	status_t ret = fInfo.InitCheck();
72 	if (ret == B_OK)
73 		_InitProfiles();
74 
75 	ResizeTo(Bounds().Width(), fInstall->Frame().bottom + 4);
76 }
77 
78 
79 PackageView::~PackageView()
80 {
81 	delete fOpenPanel;
82 }
83 
84 
85 void
86 PackageView::AttachedToWindow()
87 {
88 	status_t ret = fInfo.InitCheck();
89 	if (ret != B_OK && ret != B_NO_INIT) {
90 		BAlert *warning = new BAlert(T("parsing_failed"),
91 				T("The package file is not readable.\nOne of the possible "
92 				"reasons for this might be that the requested file is not a valid "
93 				"BeOS .pkg package."), T("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
94 				B_WARNING_ALERT);
95 		warning->Go();
96 
97 		Window()->PostMessage(B_QUIT_REQUESTED);
98 		return;
99 	}
100 
101 	// Set the window title
102 	BWindow *parent = Window();
103 	BString title;
104 	BString name = fInfo.GetName();
105 	if (name.CountChars() == 0) {
106 		title = T("Package installer");
107 	}
108 	else {
109 		title = T("Install ");
110 		title += name;
111 	}
112 	parent->SetTitle(title.String());
113 	fInstall->SetTarget(this);
114 
115 	fOpenPanel->SetTarget(BMessenger(this));
116 	fInstallTypes->SetTargetForItems(this);
117 
118 	if (fInfo.InitCheck() == B_OK) {
119 		// If the package is valid, we can set up the default group and all
120 		// other things. If not, then the application will close just after
121 		// attaching the view to the window
122 		_GroupChanged(0);
123 
124 		fStatusWindow = new PackageStatus(T("Installation progress"));
125 
126 		// Show the splash screen, if present
127 		BMallocIO *image = fInfo.GetSplashScreen();
128 		if (image) {
129 			PackageImageViewer *imageViewer = new PackageImageViewer(image);
130 			imageViewer->Go();
131 		}
132 
133 		// Show the disclaimer/info text popup, if present
134 		BString disclaimer = fInfo.GetDisclaimer();
135 		if (disclaimer.Length() != 0) {
136 	  		PackageTextViewer *text = new PackageTextViewer(disclaimer.String());
137 			int32 selection = text->Go();
138 			// The user didn't accept our disclaimer, this means we cannot continue.
139 	  		if (selection == 0) {
140 				BWindow *parent = Window();
141 				if (parent && parent->Lock())
142 					parent->Quit();
143   			}
144 		}
145 	}
146 }
147 
148 
149 void
150 PackageView::MessageReceived(BMessage *msg)
151 {
152 	switch (msg->what) {
153 		case P_MSG_INSTALL:
154 		{
155 			fInstall->SetEnabled(false);
156 			fStatusWindow->Show();
157 			BAlert *notify;
158 			status_t ret = Install();
159 			if (ret == B_OK) {
160 				notify = new BAlert("installation_success",
161 					T("The package you requested has been successfully installed "
162 						"on your system."), T("OK"));
163 
164 				notify->Go();
165 				fStatusWindow->Hide();
166 
167 				BWindow *parent = Window();
168 				if (parent && parent->Lock())
169 					parent->Quit();
170 			}
171 			else if (ret == B_FILE_EXISTS)
172 				notify = new BAlert("installation_aborted",
173 					T("The installation of the package has been aborted."), T("OK"));
174 			else {
175 				notify = new BAlert("installation_failed", // TODO: Review this
176 					T("The requested package failed to install on your system. This "
177 						"might be a problem with the target package file. Please consult "
178 						"this issue with the package distributor."), T("OK"), NULL,
179 					NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
180 				fprintf(stderr, "Error while installing the package : %s\n", strerror(ret));
181 			}
182 			notify->Go();
183 			fStatusWindow->Hide();
184 			fInstall->SetEnabled(true);
185 
186 			break;
187 		}
188 		case P_MSG_PATH_CHANGED:
189 		{
190 			BString path;
191 			if (msg->FindString("path", &path) == B_OK) {
192 				fCurrentPath.SetTo(path.String());
193 			}
194 			break;
195 		}
196 		case P_MSG_OPEN_PANEL:
197 			fOpenPanel->Show();
198 			break;
199 		case P_MSG_GROUP_CHANGED:
200 		{
201 			int32 index;
202 			if (msg->FindInt32("index", &index) == B_OK) {
203 				_GroupChanged(index);
204 			}
205 			break;
206 		}
207 		case B_REFS_RECEIVED:
208 		{
209 			entry_ref ref;
210 			if (msg->FindRef("refs", &ref) == B_OK) {
211 				BPath path(&ref);
212 
213 				BMenuItem * item = fDestField->MenuItem();
214 				dev_t device = dev_for_path(path.Path());
215 				BVolume volume(device);
216 				if (volume.InitCheck() != B_OK)
217 					break;
218 
219 				BString name = path.Path();
220 				char sizeString[32];
221 
222 				convert_size(volume.FreeBytes(), sizeString, 32);
223 				name << " (" << sizeString << " free)";
224 
225 				item->SetLabel(name.String());
226 				fCurrentPath.SetTo(path.Path());
227 			}
228 			break;
229 		}
230 		case B_SIMPLE_DATA:
231 			if (msg->WasDropped()) {
232 				uint32 type;
233 				int32 count;
234 				status_t ret = msg->GetInfo("refs", &type, &count);
235 				// Check whether the message means someone dropped a file
236 				// to our view
237 				if (ret == B_OK && type == B_REF_TYPE) {
238 					// If it is, send it along with the refs to the application
239 					msg->what = B_REFS_RECEIVED;
240 					be_app->PostMessage(msg);
241 				}
242 			}
243 		default:
244 			BView::MessageReceived(msg);
245 			break;
246 	}
247 }
248 
249 
250 status_t
251 PackageView::Install()
252 {
253 	pkg_profile *type = static_cast<pkg_profile *>(fInfo.GetProfile(fCurrentType));
254 	uint32 n = type->items.CountItems();
255 
256 	fStatusWindow->Reset(n + 4);
257 
258 	fStatusWindow->StageStep(1, "Preparing package");
259 
260 	InstalledPackageInfo packageInfo(fInfo.GetName(), fInfo.GetVersion());
261 
262 	status_t err = packageInfo.InitCheck();
263 	err = B_ENTRY_NOT_FOUND;
264 	if (err == B_OK) {
265 		// The package is already installed, inform the user
266 		BAlert *reinstall = new BAlert("reinstall",
267 			T("The given package seems to be already installed on your system. "
268 				"Would you like to uninstall the existing one and continue the "
269 				"installation?"), T("Continue"), T("Abort"));
270 
271 		if (reinstall->Go() == 0) {
272 			// Uninstall the package
273 			err = packageInfo.Uninstall();
274 			if (err != B_OK) {
275 				fprintf(stderr, "Error on uninstall\n");
276 				return err;
277 			}
278 
279 			err = packageInfo.SetTo(fInfo.GetName(), fInfo.GetVersion(), true);
280 			if (err != B_OK) {
281 				fprintf(stderr, "Error on SetTo\n");
282 				return err;
283 			}
284 		}
285 		else {
286 			// Abort the installation
287 			return B_FILE_EXISTS;
288 		}
289 	}
290 	else if (err == B_ENTRY_NOT_FOUND) {
291 		err = packageInfo.SetTo(fInfo.GetName(), fInfo.GetVersion(), true);
292 		if (err != B_OK) {
293 			fprintf(stderr, "Error on SetTo\n");
294 			return err;
295 		}
296 	}
297 	else if (fStatusWindow->Stopped())
298 		return B_FILE_EXISTS;
299 	else {
300 		fprintf(stderr, "returning on error\n");
301 		return err;
302 	}
303 
304 	fStatusWindow->StageStep(1, "Installing files and folders");
305 
306 	// Install files and directories
307 	PackageItem *iter;
308 	ItemState state;
309 	uint32 i;
310 	int32 choice;
311 	BString label;
312 
313 	packageInfo.SetName(fInfo.GetName());
314 	// TODO: Here's a small problem, since right now it's not quite sure
315 	//		which description is really used as such. The one displayed on
316 	//		the installer is mostly package installation description, but
317 	//		most people use it for describing the application in more detail
318 	//		then in the short description.
319 	//		For now, we'll use the short description if possible.
320 	BString description = fInfo.GetShortDescription();
321 	if (description.Length() <= 0)
322 		description = fInfo.GetDescription();
323 	packageInfo.SetDescription(description.String());
324 	packageInfo.SetSpaceNeeded(type->space_needed);
325 
326 	fItemExistsPolicy = P_EXISTS_NONE;
327 
328 	for (i = 0; i < n; i++) {
329 		state.Reset(fItemExistsPolicy); // Reset the current item state
330 		iter = static_cast<PackageItem *>(type->items.ItemAt(i));
331 
332 		err = iter->WriteToPath(fCurrentPath.Path(), &state);
333 		if (err == B_FILE_EXISTS) {
334 			// Writing to path failed because path already exists - ask the user
335 			// what to do and retry the writing process
336 			choice = _ItemExists(*iter, state.destination);
337 			if (choice != P_EXISTS_ABORT) {
338 				state.policy = choice;
339 				err = iter->WriteToPath(fCurrentPath.Path(), &state);
340 			}
341 		}
342 
343 		if (err != B_OK) {
344 			fprintf(stderr, "Error while writing path %s\n", fCurrentPath.Path());
345 			return err;
346 		}
347 
348 		if (fStatusWindow->Stopped())
349 			return B_FILE_EXISTS;
350 		label = "";
351 		label << (uint32)(i + 1) << " of " << (uint32)n;
352 		fStatusWindow->StageStep(1, NULL, label.String());
353 
354 		packageInfo.AddItem(state.destination.Path());
355 	}
356 
357 	fStatusWindow->StageStep(1, "Finishing installation", "");
358 
359 	err = packageInfo.Save();
360 	if (err != B_OK)
361 		return err;
362 
363 	fStatusWindow->StageStep(1, "Done");
364 
365 	return B_OK;
366 }
367 
368 
369 /*
370 void
371 PackageView::_InitView()
372 {
373 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
374 
375 	BTextView *description = new BTextView(BRect(0, 0, 20, 20), "description",
376 		BRect(4, 4, 16, 16), B_FOLLOW_NONE, B_WILL_DRAW);
377 	description->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
378 	description->SetText(fInfo.GetDescription());
379 	description->MakeEditable(false);
380 	description->MakeSelectable(false);
381 
382 	fInstallTypes = new BPopUpMenu("none");
383 
384 	BMenuField *installType = new BMenuField("install_type",
385 		T("Installation type:"), fInstallTypes, 0);
386 	installType->SetAlignment(B_ALIGN_RIGHT);
387 	installType->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_MIDDLE));
388 
389 	fInstallDesc = new BTextView(BRect(0, 0, 10, 10), "install_desc",
390 		BRect(2, 2, 8, 8), B_FOLLOW_NONE, B_WILL_DRAW);
391 	fInstallDesc->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
392 	fInstallDesc->MakeEditable(false);
393 	fInstallDesc->MakeSelectable(false);
394 	fInstallDesc->SetText(T("No installation type selected"));
395 	fInstallDesc->TextHeight(0, fInstallDesc->TextLength());
396 
397 	fInstall = new BButton("install_button", T("Install"),
398 			new BMessage(P_MSG_INSTALL));
399 
400 	BView *installField = BGroupLayoutBuilder(B_VERTICAL, 5.0f)
401 		.AddGroup(B_HORIZONTAL)
402 			.Add(installType)
403 			.AddGlue()
404 		.End()
405 		.Add(fInstallDesc);
406 
407 	BBox *installBox = new BBox("install_box");
408 	installBox->AddChild(installField);
409 
410 	BView *root = BGroupLayoutBuilder(B_VERTICAL, 3.0f)
411 		.Add(description)
412 		.Add(installBox)
413 		.AddGroup(B_HORIZONTAL)
414 			.AddGlue()
415 			.Add(fInstall)
416 		.End();
417 
418 	AddChild(root);
419 
420 	fInstall->MakeDefault(true);
421 }*/
422 
423 
424 void
425 PackageView::_InitView()
426 {
427 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
428 
429 	BRect rect = Bounds();
430 	BTextView *description = new BTextView(rect, "description",
431 		rect.InsetByCopy(5, 5), B_FOLLOW_NONE, B_WILL_DRAW);
432 	description->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
433 	description->SetText(fInfo.GetDescription());
434 	description->MakeEditable(false);
435 	description->MakeSelectable(false);
436 
437 	float length = description->TextHeight(0, description->TextLength()) + 5;
438 	if (length > kMaxDescHeight) {
439 		// Set a scroller for the description.
440 		description->ResizeTo(rect.Width() - B_V_SCROLL_BAR_WIDTH, kMaxDescHeight);
441 		BScrollView *scroller = new BScrollView("desciption_view", description,
442 				B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS, false, true, B_NO_BORDER);
443 
444 		AddChild(scroller);
445 		rect = scroller->Frame();
446 	}
447 	else {
448 		description->ResizeTo(rect.Width(), length);
449 		AddChild(description);
450 		rect = description->Frame();
451 	}
452 
453 	rect.top = rect.bottom + 2;
454 	rect.bottom += 100;
455 	BBox *installBox = new BBox(rect.InsetByCopy(2, 2), "install_box");
456 
457 	fInstallTypes = new BPopUpMenu("none");
458 
459 	BMenuField *installType = new BMenuField(BRect(2, 2, 100, 50), "install_type",
460 		T("Installation type:"), fInstallTypes, false);
461 	installType->SetDivider(installType->StringWidth(installType->Label()) + 8);
462 	installType->SetAlignment(B_ALIGN_RIGHT);
463 	installType->ResizeToPreferred();
464 
465 	installBox->AddChild(installType);
466 
467 	rect = installBox->Bounds().InsetBySelf(4, 4);
468 	rect.top = installType->Frame().bottom;
469 	fInstallDesc = new BTextView(rect, "install_desc",
470 		BRect(2, 2, rect.Width() - 2, rect.Height() - 2), B_FOLLOW_NONE,
471 		B_WILL_DRAW);
472 	fInstallDesc->MakeEditable(false);
473 	fInstallDesc->MakeSelectable(false);
474 	fInstallDesc->SetText(T("No installation type selected"));
475 	fInstallDesc->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
476 
477 	fInstallDesc->ResizeTo(rect.Width() - B_V_SCROLL_BAR_WIDTH, 60);
478 	BScrollView *scroller = new BScrollView("desciption_view", fInstallDesc,
479 				B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS, false, true, B_NO_BORDER);
480 
481 	installBox->ResizeTo(installBox->Bounds().Width(),
482 		scroller->Frame().bottom + 10);
483 
484 	installBox->AddChild(scroller);
485 
486 	AddChild(installBox);
487 
488 	fDestination = new BPopUpMenu("none");
489 
490 	rect = installBox->Frame();
491 	rect.top = rect.bottom + 5;
492 	rect.bottom += 35;
493 	fDestField = new BMenuField(rect, "install_to", T("Install to:"),
494 			fDestination, false);
495 	fDestField->SetDivider(fDestField->StringWidth(fDestField->Label()) + 8);
496 	fDestField->SetAlignment(B_ALIGN_RIGHT);
497 	fDestField->ResizeToPreferred();
498 
499 	AddChild(fDestField);
500 
501 	fInstall = new BButton(rect, "install_button", T("Install"),
502 			new BMessage(P_MSG_INSTALL));
503 	fInstall->ResizeToPreferred();
504 	AddChild(fInstall);
505 	fInstall->MoveTo(Bounds().Width() - fInstall->Bounds().Width() - 10, rect.top + 2);
506 	fInstall->MakeDefault(true);
507 }
508 
509 
510 void
511 PackageView::_InitProfiles()
512 {
513 	// Set all profiles
514 	int i = 0, num = fInfo.GetProfileCount();
515 	pkg_profile *prof;
516 	BMenuItem *item = 0;
517 	char sizeString[32];
518 	BString name = "";
519 	BMessage *message;
520 
521 	if (num > 0) { // Add the default item
522 		prof = fInfo.GetProfile(0);
523 		convert_size(prof->space_needed, sizeString, 32);
524 		name << prof->name << " (" << sizeString << ")";
525 
526 		message = new BMessage(P_MSG_GROUP_CHANGED);
527 		message->AddInt32("index", 0);
528 		item = new BMenuItem(name.String(), message);
529 		fInstallTypes->AddItem(item);
530 		item->SetMarked(true);
531 		fCurrentType = 0;
532 	}
533 
534 	for (i = 1; i < num; i++) {
535 		prof = fInfo.GetProfile(i);
536 
537 		if (prof) {
538 			convert_size(prof->space_needed, sizeString, 32);
539 			name = prof->name;
540 			name << " (" << sizeString << ")";
541 
542 			message = new BMessage(P_MSG_GROUP_CHANGED);
543 			message->AddInt32("index", i);
544 			item = new BMenuItem(name.String(), message);
545 			fInstallTypes->AddItem(item);
546 		}
547 		else
548 			fInstallTypes->AddSeparatorItem();
549 	}
550 }
551 
552 
553 int32
554 PackageView::_ItemExists(PackageItem &item, BPath &path)
555 {
556 	int32 choice = P_EXISTS_NONE;
557 
558 	switch (fItemExistsPolicy) {
559 		case P_EXISTS_OVERWRITE:
560 			choice = P_EXISTS_OVERWRITE;
561 			break;
562 
563 		case P_EXISTS_SKIP:
564 			choice = P_EXISTS_SKIP;
565 			break;
566 
567 		case P_EXISTS_ASK:
568 		case P_EXISTS_NONE:
569 		{
570 			BString alertString = T("The ");
571 
572 			alertString << item.ItemKind() << T(" named \'") << path.Leaf() << "\' ";
573 			alertString << T("already exists in the given path.\nReplace the file with "
574 				"the one from this package or skip it?");
575 
576 			BAlert *alert = new BAlert(T("file_exists"), alertString.String(),
577 				T("Replace"), T("Skip"), T("Abort"));
578 
579 			choice = alert->Go();
580 			switch (choice) {
581 				case 0:
582 					choice = P_EXISTS_OVERWRITE;
583 					break;
584 				case 1:
585 					choice = P_EXISTS_SKIP;
586 					break;
587 				default:
588 					return P_EXISTS_ABORT;
589 			}
590 
591 			if (fItemExistsPolicy == P_EXISTS_NONE) {
592 				// TODO: Maybe add 'No, but ask again' type of choice as well?
593 				alertString = T("Do you want to remember this decision for the rest of "
594 					"this installation?\nAll existing files will be ");
595 				alertString << ((choice == P_EXISTS_OVERWRITE)
596 					? T("replaced?") : T("skipped?"));
597 
598 				alert = new BAlert(T("policy_decision"), alertString.String(),
599 					T("Yes"), T("No"));
600 
601 				int32 decision = alert->Go();
602 				if (decision == 0)
603 					fItemExistsPolicy = choice;
604 				else
605 					fItemExistsPolicy = P_EXISTS_ASK;
606 			}
607 			break;
608 		}
609 	}
610 
611 	return choice;
612 }
613 
614 
615 status_t
616 PackageView::_GroupChanged(int32 index)
617 {
618 	if (index < 0)
619 		return B_ERROR;
620 
621 	BMenuItem *iter;
622 	int32 i, num = fDestination->CountItems();
623 
624 	// Clear the choice list
625 	for (i = 0;i < num;i++) {
626 		iter = fDestination->RemoveItem((int32)0);
627 		delete iter;
628 	}
629 
630 	fCurrentType = index;
631 	pkg_profile *prof = fInfo.GetProfile(index);
632 	BString test;
633 	fInstallDesc->SetText(prof->description.String());
634 
635 	if (prof) {
636 		BMenuItem *item = 0;
637 		BPath path;
638 		BMessage *temp;
639 		BVolume volume;
640 
641 		if (prof->path_type == P_INSTALL_PATH) {
642 			dev_t device;
643 			BString name;
644 			char sizeString[32];
645 
646 			if (find_directory(B_BEOS_APPS_DIRECTORY, &path) == B_OK) {
647 				device = dev_for_path(path.Path());
648 				if (volume.SetTo(device) == B_OK && !volume.IsReadOnly()) {
649 					temp = new BMessage(P_MSG_PATH_CHANGED);
650 					temp->AddString("path", BString(path.Path()));
651 
652 					convert_size(volume.FreeBytes(), sizeString, 32);
653 					name = path.Path();
654 					name << " (" << sizeString << " free)";
655 					item = new BMenuItem(name.String(), temp);
656 					item->SetTarget(this);
657 					fDestination->AddItem(item);
658 				}
659 			}
660 			if (find_directory(B_APPS_DIRECTORY, &path) == B_OK) {
661 				device = dev_for_path(path.Path());
662 				if (volume.SetTo(device) == B_OK && !volume.IsReadOnly()) {
663 					temp = new BMessage(P_MSG_PATH_CHANGED);
664 					temp->AddString("path", BString(path.Path()));
665 
666 					convert_size(volume.FreeBytes(), sizeString, 32);
667 					name = path.Path();
668 					name << " (" << sizeString << " free)";
669 					item = new BMenuItem(name.String(), temp);
670 					item->SetTarget(this);
671 					fDestination->AddItem(item);
672 				}
673 			}
674 
675 			if (item) {
676 				item->SetMarked(true);
677 				fCurrentPath.SetTo(path.Path());
678 			}
679 			fDestination->AddSeparatorItem();
680 
681 			item = new BMenuItem("Other" B_UTF8_ELLIPSIS, new BMessage(P_MSG_OPEN_PANEL));
682 			item->SetTarget(this);
683 			fDestination->AddItem(item);
684 
685 			fDestField->SetEnabled(true);
686 		}
687 		else if (prof->path_type == P_USER_PATH) {
688 			BString name;
689 			char sizeString[32], volumeName[B_FILE_NAME_LENGTH];
690 			BVolumeRoster roster;
691 			BDirectory mountPoint;
692 
693 			while (roster.GetNextVolume(&volume) != B_BAD_VALUE) {
694 				if (volume.IsReadOnly() ||
695 						volume.GetRootDirectory(&mountPoint) != B_OK)
696 					continue;
697 
698 				if (path.SetTo(&mountPoint, NULL) != B_OK)
699 					continue;
700 
701 				temp = new BMessage(P_MSG_PATH_CHANGED);
702 				temp->AddString("path", BString(path.Path()));
703 
704 				convert_size(volume.FreeBytes(), sizeString, 32);
705 				volume.GetName(volumeName);
706 				name = volumeName;
707 				name << " (" << sizeString << " free)";
708 				item = new BMenuItem(name.String(), temp);
709 				item->SetTarget(this);
710 				fDestination->AddItem(item);
711 			}
712 
713 			fDestField->SetEnabled(true);
714 		}
715 		else
716 			fDestField->SetEnabled(false);
717 	}
718 
719 	return B_OK;
720 }
721 
722