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