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