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