1 /* 2 * Copyright 2004-2006, Jérôme DUVAL. All rights reserved. 3 * Copyright 2010, Karsten Heimrich. All rights reserved. 4 * Copyright 2013, Rene Gollent, rene@gollent.com. 5 * Distributed under the terms of the MIT License. 6 */ 7 8 9 #include "ExpanderWindow.h" 10 11 #include <algorithm> 12 13 #include <Alert.h> 14 #include <Box.h> 15 #include <Button.h> 16 #include <Catalog.h> 17 #include <CheckBox.h> 18 #include <ControlLook.h> 19 #include <Entry.h> 20 #include <File.h> 21 #include <LayoutBuilder.h> 22 #include <Locale.h> 23 #include <Menu.h> 24 #include <MenuBar.h> 25 #include <MenuItem.h> 26 #include <Path.h> 27 #include <Screen.h> 28 #include <ScrollView.h> 29 #include <StringView.h> 30 #include <TextView.h> 31 32 #include "ExpanderApp.h" 33 #include "ExpanderThread.h" 34 #include "ExpanderPreferences.h" 35 #include "PasswordAlert.h" 36 37 38 const uint32 MSG_SOURCE = 'mSOU'; 39 const uint32 MSG_DEST = 'mDES'; 40 const uint32 MSG_EXPAND = 'mEXP'; 41 const uint32 MSG_SHOW = 'mSHO'; 42 const uint32 MSG_STOP = 'mSTO'; 43 const uint32 MSG_PREFERENCES = 'mPRE'; 44 const uint32 MSG_SOURCETEXT = 'mSTX'; 45 const uint32 MSG_DESTTEXT = 'mDTX'; 46 const uint32 MSG_SHOWCONTENTS = 'mSCT'; 47 48 const int32 MAX_STATUS_LENGTH = 35; 49 50 51 #undef B_TRANSLATION_CONTEXT 52 #define B_TRANSLATION_CONTEXT "ExpanderWindow" 53 54 55 ExpanderWindow::ExpanderWindow(BRect frame, const entry_ref* ref, 56 BMessage* settings) 57 : 58 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("Expander"), B_TITLED_WINDOW, 59 B_NORMAL_WINDOW_FEEL), 60 fSourcePanel(NULL), 61 fDestPanel(NULL), 62 fSourceChanged(true), 63 fListingThread(NULL), 64 fListingStarted(false), 65 fExpandingThread(NULL), 66 fExpandingStarted(false), 67 fSettings(*settings), 68 fPreferences(NULL) 69 { 70 _CreateMenuBar(); 71 72 fDestButton = new BButton(B_TRANSLATE("Destination"), 73 new BMessage(MSG_DEST)); 74 fSourceButton = new BButton(B_TRANSLATE("Source"), 75 new BMessage(MSG_SOURCE)); 76 fExpandButton = new BButton(B_TRANSLATE("Expand"), 77 new BMessage(MSG_EXPAND)); 78 79 BSize size = fDestButton->PreferredSize(); 80 size.width = std::max(size.width, fSourceButton->PreferredSize().width); 81 size.width = std::max(size.width, fExpandButton->PreferredSize().width); 82 83 fDestButton->SetExplicitMaxSize(size); 84 fSourceButton->SetExplicitMaxSize(size); 85 fExpandButton->SetExplicitMaxSize(size); 86 87 fListingText = new BTextView("listingText"); 88 fListingText->SetText(""); 89 fListingText->MakeEditable(false); 90 fListingText->SetStylable(false); 91 fListingText->SetWordWrap(false); 92 BFont font = be_fixed_font; 93 fListingText->SetFontAndColor(&font); 94 BScrollView* scrollView = new BScrollView("", fListingText, 95 B_INVALIDATE_AFTER_LAYOUT, true, true); 96 97 // workaround to let the layout manager estimate 98 // the width of status view and fix the #5289 99 // we assume that spaces are twice narrower than normal chars 100 BString statusPlaceholderString; 101 statusPlaceholderString.SetTo(' ', MAX_STATUS_LENGTH * 2); 102 103 const float spacing = be_control_look->DefaultItemSpacing(); 104 BGroupLayout* pathLayout; 105 BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0) 106 .SetInsets(0.0) 107 .Add(fBar) 108 .AddGroup(B_VERTICAL, spacing) 109 .AddGroup(B_HORIZONTAL, spacing) 110 .AddGroup(B_VERTICAL, 5.0) 111 .Add(fSourceButton) 112 .Add(fDestButton) 113 .Add(fExpandButton) 114 .End() 115 .AddGroup(B_VERTICAL, spacing) 116 .Add(fSourceText = new BTextControl(NULL, NULL, 117 new BMessage(MSG_SOURCETEXT))) 118 .Add(fDestText = new BTextControl(NULL, NULL, 119 new BMessage(MSG_DESTTEXT))) 120 .AddGroup(B_HORIZONTAL, spacing) 121 .GetLayout(&pathLayout) 122 .Add(fShowContents = new BCheckBox( 123 B_TRANSLATE("Show contents"), 124 new BMessage(MSG_SHOWCONTENTS))) 125 .Add(fStatusView = new BStringView(NULL, 126 statusPlaceholderString)) 127 .End() 128 .End() 129 .End() 130 .Add(scrollView) 131 .SetInsets(spacing, spacing, spacing, spacing) 132 .End() 133 .End(); 134 135 pathLayout->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 136 size = GetLayout()->View()->PreferredSize(); 137 fSizeLimit = size.Height() - scrollView->PreferredSize().height - spacing; 138 139 ResizeTo(Bounds().Width(), fSizeLimit); 140 SetSizeLimits(size.Width(), 32767.0f, fSizeLimit, fSizeLimit); 141 SetZoomLimits(Bounds().Width(), fSizeLimit); 142 fPreviousHeight = -1; 143 144 Show(); 145 } 146 147 148 ExpanderWindow::~ExpanderWindow() 149 { 150 if (fDestPanel && fDestPanel->RefFilter()) 151 delete fDestPanel->RefFilter(); 152 153 if (fSourcePanel && fSourcePanel->RefFilter()) 154 delete fSourcePanel->RefFilter(); 155 156 delete fDestPanel; 157 delete fSourcePanel; 158 } 159 160 161 bool 162 ExpanderWindow::ValidateDest() 163 { 164 BEntry entry(fDestText->Text(), true); 165 BVolume volume; 166 if (!entry.Exists()) { 167 BAlert* alert = new BAlert("destAlert", 168 B_TRANSLATE("Destination folder doesn't exist. " 169 "Would you like to create it?"), 170 B_TRANSLATE("Create"), B_TRANSLATE("Cancel"), NULL, 171 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 172 alert->SetShortcut(0, B_ESCAPE); 173 174 if (alert->Go() != 0) 175 return false; 176 177 if (create_directory(fDestText->Text(), 0755) != B_OK) { 178 BAlert* alert = new BAlert("stopAlert", 179 B_TRANSLATE("Failed to create the destination folder."), 180 B_TRANSLATE("Cancel"), NULL, NULL, 181 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 182 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 183 alert->Go(); 184 return false; 185 } 186 187 BEntry newEntry(fDestText->Text(), true); 188 newEntry.GetRef(&fDestRef); 189 return true; 190 191 } 192 193 if (!entry.IsDirectory()) { 194 BAlert* alert = new BAlert("destAlert", 195 B_TRANSLATE("The destination is not a folder."), 196 B_TRANSLATE("Cancel"), NULL, NULL, 197 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 198 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 199 alert->Go(); 200 return false; 201 } 202 203 if (entry.GetVolume(&volume) != B_OK || volume.IsReadOnly()) { 204 BAlert* alert = new BAlert("destAlert", 205 B_TRANSLATE("The destination is read only."), 206 B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL, 207 B_EVEN_SPACING, B_WARNING_ALERT); 208 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 209 alert->Go(); 210 return false; 211 } 212 213 entry.GetRef(&fDestRef); 214 return true; 215 } 216 217 218 void 219 ExpanderWindow::MessageReceived(BMessage* message) 220 { 221 switch (message->what) { 222 case MSG_SOURCE: 223 { 224 BEntry entry(fSourceText->Text(), true); 225 entry_ref srcRef; 226 if (entry.Exists() && entry.IsDirectory()) 227 entry.GetRef(&srcRef); 228 if (!fSourcePanel) { 229 BMessenger messenger(this); 230 fSourcePanel = new BFilePanel(B_OPEN_PANEL, &messenger, &srcRef, 231 B_FILE_NODE, false, NULL, new RuleRefFilter(fRules), true); 232 (fSourcePanel->Window())->SetTitle( 233 B_TRANSLATE("Expander: Open")); 234 } else 235 fSourcePanel->SetPanelDirectory(&srcRef); 236 fSourcePanel->Show(); 237 break; 238 } 239 240 case MSG_DEST: 241 { 242 BEntry entry(fDestText->Text(), true); 243 entry_ref destRef; 244 if (entry.Exists() && entry.IsDirectory()) 245 entry.GetRef(&destRef); 246 if (!fDestPanel) { 247 BMessenger messenger(this); 248 fDestPanel = new DirectoryFilePanel(B_OPEN_PANEL, &messenger, 249 &destRef, B_DIRECTORY_NODE, false, NULL, 250 new DirectoryRefFilter(), true); 251 } else 252 fDestPanel->SetPanelDirectory(&destRef); 253 fDestPanel->Show(); 254 break; 255 } 256 257 case MSG_DIRECTORY: 258 { 259 entry_ref ref; 260 fDestPanel->GetPanelDirectory(&ref); 261 fDestRef = ref; 262 BEntry entry(&ref); 263 BPath path(&entry); 264 fDestText->SetText(path.Path()); 265 fDestPanel->Hide(); 266 break; 267 } 268 269 case B_SIMPLE_DATA: 270 case B_REFS_RECEIVED: 271 RefsReceived(message); 272 break; 273 274 case MSG_EXPAND: 275 if (!ValidateDest()) 276 break; 277 if (!fExpandingStarted) { 278 StartExpanding(); 279 break; 280 } 281 // supposed to fall through 282 case MSG_STOP: 283 if (fExpandingStarted) { 284 fExpandingThread->SuspendExternalExpander(); 285 BAlert* alert = new BAlert("stopAlert", 286 B_TRANSLATE("Are you sure you want to stop expanding this\n" 287 "archive? The expanded items may not be complete."), 288 B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL, 289 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 290 alert->SetShortcut(0, B_ESCAPE); 291 if (alert->Go() == 0) { 292 fExpandingThread->ResumeExternalExpander(); 293 StopExpanding(); 294 } else 295 fExpandingThread->ResumeExternalExpander(); 296 } 297 break; 298 299 case MSG_SHOW: 300 fShowContents->SetValue(fShowContents->Value() == B_CONTROL_OFF 301 ? B_CONTROL_ON : B_CONTROL_OFF); 302 // supposed to fall through 303 case MSG_SHOWCONTENTS: 304 // change menu item label 305 fShowItem->SetLabel(fShowContents->Value() == B_CONTROL_OFF 306 ? B_TRANSLATE("Show contents") : B_TRANSLATE("Hide contents")); 307 308 if (fShowContents->Value() == B_CONTROL_OFF) { 309 if (fListingStarted) 310 StopListing(); 311 312 _UpdateWindowSize(false); 313 } else 314 StartListing(); 315 break; 316 317 case MSG_SOURCETEXT: 318 { 319 BEntry entry(fSourceText->Text(), true); 320 if (!entry.Exists()) { 321 BAlert* alert = new BAlert("srcAlert", 322 B_TRANSLATE("The file doesn't exist"), 323 B_TRANSLATE("Cancel"), NULL, NULL, 324 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 325 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 326 alert->Go(); 327 break; 328 } 329 330 entry_ref ref; 331 entry.GetRef(&ref); 332 ExpanderRule* rule = fRules.MatchingRule(&ref); 333 if (rule) { 334 fSourceChanged = true; 335 fSourceRef = ref; 336 fShowContents->SetEnabled(true); 337 fExpandButton->SetEnabled(true); 338 fExpandItem->SetEnabled(true); 339 fShowItem->SetEnabled(true); 340 break; 341 } 342 343 BString string = "The file : "; 344 string += fSourceText->Text(); 345 string += B_TRANSLATE_MARK(" is not supported"); 346 BAlert* alert = new BAlert("srcAlert", string.String(), 347 B_TRANSLATE("Cancel"), 348 NULL, NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT); 349 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 350 alert->Go(); 351 352 fShowContents->SetEnabled(false); 353 fExpandButton->SetEnabled(false); 354 fExpandItem->SetEnabled(false); 355 fShowItem->SetEnabled(false); 356 break; 357 } 358 359 case MSG_DESTTEXT: 360 ValidateDest(); 361 break; 362 363 case MSG_PREFERENCES: 364 if (fPreferences == NULL) 365 fPreferences = new ExpanderPreferences(&fSettings); 366 367 fPreferences->Show(); 368 break; 369 370 case 'outp': 371 if (!fExpandingStarted && fListingStarted) { 372 BString string; 373 int32 i = 0; 374 while (message->FindString("output", i++, &string) == B_OK) { 375 float length = fListingText->StringWidth(string.String()); 376 377 if (length > fLongestLine) 378 fLongestLine = length; 379 380 fListingText->Insert(string.String()); 381 } 382 fListingText->ScrollToSelection(); 383 } else if (fExpandingStarted) { 384 BString string; 385 int32 i = 0; 386 while (message->FindString("output", i++, &string) == B_OK) { 387 if (strstr(string.String(), "Enter password") != NULL) { 388 fExpandingThread->SuspendExternalExpander(); 389 BString password; 390 PasswordAlert* alert = 391 new PasswordAlert("passwordAlert", string); 392 alert->Go(password); 393 fExpandingThread->ResumeExternalExpander(); 394 fExpandingThread->PushInput(password); 395 } 396 } 397 } 398 break; 399 400 case 'errp': 401 { 402 BString string; 403 if (message->FindString("error", &string) == B_OK 404 && fExpandingStarted) { 405 fExpandingThread->SuspendExternalExpander(); 406 if (strstr(string.String(), "password") != NULL) { 407 BString password; 408 PasswordAlert* alert = new PasswordAlert("passwordAlert", 409 string); 410 alert->Go(password); 411 fExpandingThread->ResumeExternalExpander(); 412 fExpandingThread->PushInput(password); 413 } else { 414 BAlert* alert = new BAlert("stopAlert", string, 415 B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL, 416 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 417 alert->SetShortcut(0, B_ESCAPE); 418 if (alert->Go() == 0) { 419 fExpandingThread->ResumeExternalExpander(); 420 StopExpanding(); 421 } else 422 fExpandingThread->ResumeExternalExpander(); 423 } 424 } 425 break; 426 } 427 428 case 'exit': 429 // thread has finished 430 // (finished, quit, killed, we don't know) 431 // reset window state 432 if (fExpandingStarted) { 433 SetStatus(B_TRANSLATE("File expanded")); 434 StopExpanding(); 435 OpenDestFolder(); 436 CloseWindowOrKeepOpen(); 437 } else if (fListingStarted) { 438 fSourceChanged = false; 439 StopListing(); 440 _ExpandListingText(); 441 } else 442 SetStatus(""); 443 break; 444 445 case 'exrr': 446 // thread has finished 447 // reset window state 448 449 SetStatus(B_TRANSLATE("Error when expanding archive")); 450 CloseWindowOrKeepOpen(); 451 break; 452 453 default: 454 BWindow::MessageReceived(message); 455 } 456 } 457 458 459 bool 460 ExpanderWindow::CanQuit() 461 { 462 if ((fSourcePanel && fSourcePanel->IsShowing()) 463 || (fDestPanel && fDestPanel->IsShowing())) { 464 return false; 465 } 466 467 if (fExpandingStarted) { 468 fExpandingThread->SuspendExternalExpander(); 469 BAlert* alert = new BAlert("stopAlert", 470 B_TRANSLATE("Are you sure you want to stop expanding this\n" 471 "archive? The expanded items may not be complete."), 472 B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL, 473 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 474 alert->SetShortcut(0, B_ESCAPE); 475 476 if (alert->Go() == 0) { 477 fExpandingThread->ResumeExternalExpander(); 478 StopExpanding(); 479 } else { 480 fExpandingThread->ResumeExternalExpander(); 481 return false; 482 } 483 } 484 485 return true; 486 } 487 488 489 bool 490 ExpanderWindow::QuitRequested() 491 { 492 if (!CanQuit()) 493 return false; 494 495 if (fListingStarted) 496 StopListing(); 497 498 be_app->PostMessage(B_QUIT_REQUESTED); 499 fSettings.ReplacePoint("window_position", Frame().LeftTop()); 500 ((ExpanderApp*)(be_app))->UpdateSettingsFrom(&fSettings); 501 502 return true; 503 } 504 505 506 void 507 ExpanderWindow::RefsReceived(BMessage* message) 508 { 509 entry_ref ref; 510 int32 i = 0; 511 int8 destinationFolder = 0x63; 512 fSettings.FindInt8("destination_folder", &destinationFolder); 513 514 while (message->FindRef("refs", i++, &ref) == B_OK) { 515 BEntry entry(&ref, true); 516 BPath path(&entry); 517 BNode node(&entry); 518 519 if (node.IsFile()) { 520 fSourceChanged = true; 521 fSourceRef = ref; 522 fSourceText->SetText(path.Path()); 523 if (destinationFolder == 0x63) { 524 BPath parent; 525 path.GetParent(&parent); 526 fDestText->SetText(parent.Path()); 527 get_ref_for_path(parent.Path(), &fDestRef); 528 } else if (destinationFolder == 0x65) { 529 fSettings.FindRef("destination_folder_use", &fDestRef); 530 BEntry dEntry(&fDestRef, true); 531 BPath dPath(&dEntry); 532 fDestText->SetText(dPath.Path()); 533 } 534 535 BEntry dEntry(&fDestRef, true); 536 if (dEntry.Exists()) { 537 fExpandButton->SetEnabled(true); 538 fExpandItem->SetEnabled(true); 539 } 540 541 if (fShowContents->Value() == B_CONTROL_ON) { 542 StopListing(); 543 StartListing(); 544 } else { 545 fShowContents->SetEnabled(true); 546 fShowItem->SetEnabled(true); 547 } 548 549 bool fromApp; 550 if (message->FindBool("fromApp", &fromApp) == B_OK) { 551 AutoExpand(); 552 } else 553 AutoListing(); 554 } else if (node.IsDirectory()) { 555 fDestRef = ref; 556 fDestText->SetText(path.Path()); 557 } 558 } 559 } 560 561 562 #undef B_TRANSLATION_CONTEXT 563 #define B_TRANSLATION_CONTEXT "ExpanderMenu" 564 565 void 566 ExpanderWindow::_CreateMenuBar() 567 { 568 fBar = new BMenuBar("menu_bar", B_ITEMS_IN_ROW, B_INVALIDATE_AFTER_LAYOUT); 569 BMenu* menu = new BMenu(B_TRANSLATE("File")); 570 menu->AddItem(fSourceItem 571 = new BMenuItem(B_TRANSLATE("Set source" B_UTF8_ELLIPSIS), 572 new BMessage(MSG_SOURCE), 'O')); 573 menu->AddItem(fDestItem 574 = new BMenuItem(B_TRANSLATE("Set destination" B_UTF8_ELLIPSIS), 575 new BMessage(MSG_DEST), 'D')); 576 menu->AddSeparatorItem(); 577 menu->AddItem(fExpandItem = new BMenuItem(B_TRANSLATE("Expand"), 578 new BMessage(MSG_EXPAND), 'E')); 579 fExpandItem->SetEnabled(false); 580 menu->AddItem(fShowItem = new BMenuItem(B_TRANSLATE("Show contents"), 581 new BMessage(MSG_SHOW), 'L')); 582 fShowItem->SetEnabled(false); 583 menu->AddSeparatorItem(); 584 menu->AddItem(fStopItem = new BMenuItem(B_TRANSLATE("Stop"), 585 new BMessage(MSG_STOP), 'K')); 586 fStopItem->SetEnabled(false); 587 menu->AddSeparatorItem(); 588 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 589 new BMessage(B_QUIT_REQUESTED), 'W')); 590 fBar->AddItem(menu); 591 592 menu = new BMenu(B_TRANSLATE("Settings")); 593 menu->AddItem(fPreferencesItem 594 = new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), 595 new BMessage(MSG_PREFERENCES), 'S')); 596 fBar->AddItem(menu); 597 } 598 599 600 #undef B_TRANSLATION_CONTEXT 601 #define B_TRANSLATION_CONTEXT "ExpanderWindow" 602 603 void 604 ExpanderWindow::StartExpanding() 605 { 606 ExpanderRule* rule = fRules.MatchingRule(&fSourceRef); 607 if (!rule) 608 return; 609 610 BEntry destEntry(fDestText->Text(), true); 611 if (!destEntry.Exists()) { 612 BAlert* alert = new BAlert("destAlert", 613 B_TRANSLATE("The folder was either moved, renamed or not\nsupported."), 614 B_TRANSLATE("Cancel"), NULL, NULL, 615 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 616 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 617 alert->Go(); 618 return; 619 } 620 621 BMessage message; 622 message.AddString("cmd", rule->ExpandCmd()); 623 message.AddRef("srcRef", &fSourceRef); 624 message.AddRef("destRef", &fDestRef); 625 626 fExpandButton->SetLabel(B_TRANSLATE("Stop")); 627 fSourceButton->SetEnabled(false); 628 fDestButton->SetEnabled(false); 629 fShowContents->SetEnabled(false); 630 fSourceItem->SetEnabled(false); 631 fDestItem->SetEnabled(false); 632 fExpandItem->SetEnabled(false); 633 fShowItem->SetEnabled(false); 634 fStopItem->SetEnabled(true); 635 fPreferencesItem->SetEnabled(false); 636 637 BEntry entry(&fSourceRef); 638 BPath path(&entry); 639 BString text(B_TRANSLATE("Expanding '%s'")); 640 text.ReplaceFirst("%s", path.Leaf()); 641 SetStatus(text.String()); 642 643 fExpandingThread = new ExpanderThread(&message, new BMessenger(this)); 644 fExpandingThread->Start(); 645 646 fExpandingStarted = true; 647 } 648 649 650 void 651 ExpanderWindow::StopExpanding(void) 652 { 653 if (fExpandingThread) { 654 fExpandingThread->InterruptExternalExpander(); 655 fExpandingThread = NULL; 656 } 657 658 fExpandingStarted = false; 659 660 fExpandButton->SetLabel(B_TRANSLATE("Expand")); 661 fSourceButton->SetEnabled(true); 662 fDestButton->SetEnabled(true); 663 fShowContents->SetEnabled(true); 664 fSourceItem->SetEnabled(true); 665 fDestItem->SetEnabled(true); 666 fExpandItem->SetEnabled(true); 667 fShowItem->SetEnabled(true); 668 fStopItem->SetEnabled(false); 669 fPreferencesItem->SetEnabled(true); 670 } 671 672 673 void 674 ExpanderWindow::_ExpandListingText() 675 { 676 float delta = fLongestLine - fListingText->Frame().Width(); 677 678 if (delta > 0) { 679 BScreen screen; 680 BRect screenFrame = screen.Frame(); 681 682 if (Frame().right + delta > screenFrame.right) 683 delta = screenFrame.right - Frame().right - 4.0f; 684 685 ResizeBy(delta, 0.0f); 686 } 687 688 float minWidth, maxWidth, minHeight, maxHeight; 689 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 690 691 if (minWidth < Frame().Width() + delta) { 692 // set the Zoom limit as the minimal required size 693 SetZoomLimits(Frame().Width() + delta, 694 std::min(fSizeLimit + fListingText->TextRect().Height() 695 + fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f, 696 maxHeight)); 697 } else { 698 // set the zoom limit based on minimal window size allowed 699 SetZoomLimits(minWidth, 700 std::min(fSizeLimit + fListingText->TextRect().Height() 701 + fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f, 702 maxHeight)); 703 } 704 } 705 706 707 void 708 ExpanderWindow::_UpdateWindowSize(bool showContents) 709 { 710 float minWidth, maxWidth, minHeight, maxHeight; 711 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 712 713 float bottom = fSizeLimit; 714 715 if (showContents) { 716 if (fPreviousHeight < 0.0) { 717 BFont font; 718 font_height fontHeight; 719 fListingText->GetFont(&font); 720 font.GetHeight(&fontHeight); 721 fLineHeight = ceilf(fontHeight.ascent + fontHeight.descent 722 + fontHeight.leading); 723 fPreviousHeight = bottom + 10.0 * fLineHeight; 724 } 725 minHeight = bottom + 5.0 * fLineHeight; 726 maxHeight = 32767.0; 727 728 bottom = std::max(fPreviousHeight, minHeight); 729 } else { 730 minHeight = fSizeLimit; 731 maxHeight = fSizeLimit; 732 fPreviousHeight = Frame().Height(); 733 } 734 735 SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 736 ResizeTo(Frame().Width(), bottom); 737 } 738 739 740 void 741 ExpanderWindow::SetStatus(BString text) 742 { 743 if (text.Length() >= MAX_STATUS_LENGTH) { 744 text.Truncate(MAX_STATUS_LENGTH - 1); 745 text << B_UTF8_ELLIPSIS; 746 } 747 748 fStatusView->SetText(text); 749 } 750 751 752 void 753 ExpanderWindow::StartListing() 754 { 755 _UpdateWindowSize(true); 756 757 if (!fSourceChanged) 758 return; 759 760 fPreviousHeight = -1.0; 761 762 fLongestLine = 0.0f; 763 764 ExpanderRule* rule = fRules.MatchingRule(&fSourceRef); 765 if (!rule) 766 return; 767 768 BMessage message; 769 message.AddString("cmd", rule->ListingCmd()); 770 message.AddRef("srcRef", &fSourceRef); 771 772 fShowContents->SetEnabled(true); 773 fSourceItem->SetEnabled(false); 774 fDestItem->SetEnabled(false); 775 fExpandItem->SetEnabled(false); 776 fShowItem->SetEnabled(true); 777 fShowItem->SetLabel(B_TRANSLATE("Hide contents")); 778 fStopItem->SetEnabled(false); 779 fPreferencesItem->SetEnabled(false); 780 781 fSourceButton->SetEnabled(false); 782 fDestButton->SetEnabled(false); 783 fExpandButton->SetEnabled(false); 784 785 BEntry entry(&fSourceRef); 786 BPath path(&entry); 787 BString text(B_TRANSLATE("Creating listing for '%s'")); 788 text.ReplaceFirst("%s", path.Leaf()); 789 SetStatus(text.String()); 790 fListingText->SetText(""); 791 792 fListingThread = new ExpanderThread(&message, new BMessenger(this)); 793 fListingThread->Start(); 794 795 fListingStarted = true; 796 } 797 798 799 void 800 ExpanderWindow::StopListing(void) 801 { 802 if (fListingThread) { 803 fListingThread->InterruptExternalExpander(); 804 fListingThread = NULL; 805 } 806 807 fListingStarted = false; 808 809 fShowContents->SetEnabled(true); 810 fSourceItem->SetEnabled(true); 811 fDestItem->SetEnabled(true); 812 fExpandItem->SetEnabled(true); 813 fShowItem->SetEnabled(true); 814 fStopItem->SetEnabled(false); 815 fPreferencesItem->SetEnabled(true); 816 817 fSourceButton->SetEnabled(true); 818 fDestButton->SetEnabled(true); 819 fExpandButton->SetEnabled(true); 820 SetStatus(""); 821 } 822 823 824 void 825 ExpanderWindow::CloseWindowOrKeepOpen() 826 { 827 bool expandFiles = false; 828 fSettings.FindBool("automatically_expand_files", &expandFiles); 829 830 bool closeWhenDone = false; 831 fSettings.FindBool("close_when_done", &closeWhenDone); 832 833 if (expandFiles || closeWhenDone) 834 PostMessage(B_QUIT_REQUESTED); 835 } 836 837 838 void 839 ExpanderWindow::OpenDestFolder() 840 { 841 bool openFolder = true; 842 fSettings.FindBool("open_destination_folder", &openFolder); 843 844 if (!openFolder) 845 return; 846 847 BMessage* message = new BMessage(B_REFS_RECEIVED); 848 message->AddRef("refs", &fDestRef); 849 BPath path(&fDestRef); 850 BMessenger tracker("application/x-vnd.Be-TRAK"); 851 tracker.SendMessage(message); 852 } 853 854 855 void 856 ExpanderWindow::AutoListing() 857 { 858 bool showContents = false; 859 fSettings.FindBool("show_contents_listing", &showContents); 860 861 if (!showContents) 862 return; 863 864 fShowContents->SetValue(B_CONTROL_ON); 865 fShowContents->Invoke(); 866 } 867 868 869 void 870 ExpanderWindow::AutoExpand() 871 { 872 bool expandFiles = false; 873 fSettings.FindBool("automatically_expand_files", &expandFiles); 874 875 if (!expandFiles) { 876 AutoListing(); 877 return; 878 } 879 880 fExpandButton->Invoke(); 881 } 882