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->SetExplicitSize(size); 84 fSourceButton->SetExplicitSize(size); 85 fExpandButton->SetExplicitSize(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 fScrollView = new BScrollView("", fListingText, B_INVALIDATE_AFTER_LAYOUT, 95 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) 106 .Add(fBar) 107 .AddGroup(B_VERTICAL, B_USE_ITEM_SPACING) 108 .AddGroup(B_HORIZONTAL, B_USE_ITEM_SPACING) 109 .Add(fSourceButton) 110 .Add(fSourceText = new BTextControl(NULL, NULL, 111 new BMessage(MSG_SOURCETEXT))) 112 .End() 113 .AddGroup(B_HORIZONTAL, B_USE_ITEM_SPACING) 114 .Add(fDestButton) 115 .Add(fDestText = new BTextControl(NULL, NULL, 116 new BMessage(MSG_DESTTEXT))) 117 .End() 118 .AddGroup(B_HORIZONTAL, B_USE_ITEM_SPACING) 119 .Add(fExpandButton) 120 .AddGroup(B_HORIZONTAL, B_USE_ITEM_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 .Add(fScrollView) 130 .SetInsets(B_USE_WINDOW_SPACING) 131 .End(); 132 133 pathLayout->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 134 size = GetLayout()->View()->PreferredSize(); 135 fSizeLimit = size.Height() - fScrollView->PreferredSize().height - spacing; 136 137 ResizeTo(Bounds().Width(), fSizeLimit); 138 SetSizeLimits(size.Width(), 32767.0f, fSizeLimit, fSizeLimit); 139 SetZoomLimits(Bounds().Width(), fSizeLimit); 140 fPreviousHeight = -1; 141 142 fScrollView->Hide(); 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 fScrollView->Hide(); 313 _UpdateWindowSize(false); 314 } else { 315 fScrollView->Show(); 316 StartListing(); 317 } 318 break; 319 320 case MSG_SOURCETEXT: 321 { 322 BEntry entry(fSourceText->Text(), true); 323 if (!entry.Exists()) { 324 BAlert* alert = new BAlert("srcAlert", 325 B_TRANSLATE("The file doesn't exist"), 326 B_TRANSLATE("Cancel"), NULL, NULL, 327 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 328 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 329 alert->Go(); 330 break; 331 } 332 333 entry_ref ref; 334 entry.GetRef(&ref); 335 ExpanderRule* rule = fRules.MatchingRule(&ref); 336 if (rule) { 337 fSourceChanged = true; 338 fSourceRef = ref; 339 fShowContents->SetEnabled(true); 340 fExpandButton->SetEnabled(true); 341 fExpandItem->SetEnabled(true); 342 fShowItem->SetEnabled(true); 343 break; 344 } 345 346 BString string = "The file : "; 347 string += fSourceText->Text(); 348 string += B_TRANSLATE_MARK(" is not supported"); 349 BAlert* alert = new BAlert("srcAlert", string.String(), 350 B_TRANSLATE("Cancel"), 351 NULL, NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT); 352 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 353 alert->Go(); 354 355 fShowContents->SetEnabled(false); 356 fExpandButton->SetEnabled(false); 357 fExpandItem->SetEnabled(false); 358 fShowItem->SetEnabled(false); 359 break; 360 } 361 362 case MSG_DESTTEXT: 363 ValidateDest(); 364 break; 365 366 case MSG_PREFERENCES: 367 if (fPreferences == NULL) 368 fPreferences = new ExpanderPreferences(&fSettings); 369 370 fPreferences->Show(); 371 break; 372 373 case 'outp': 374 if (!fExpandingStarted && fListingStarted) { 375 // Check if the vertical scroll bar is at the end 376 float max, pos; 377 fScrollView->ScrollBar(B_VERTICAL)->GetRange(NULL, &max); 378 pos = fScrollView->ScrollBar(B_VERTICAL)->Value(); 379 bool atEnd = (pos == max); 380 381 BString string; 382 int32 i = 0; 383 while (message->FindString("output", i++, &string) == B_OK) { 384 float length = fListingText->StringWidth(string.String()); 385 386 if (length > fLongestLine) 387 fLongestLine = length; 388 389 fListingText->Insert(string.String()); 390 } 391 392 if (atEnd && fScrollView->ScrollBar(B_VERTICAL)->Value() == pos) { 393 fScrollView->ScrollBar(B_VERTICAL)->GetRange(NULL, &max); 394 fScrollView->ScrollBar(B_VERTICAL)->SetValue(max); 395 } 396 } else if (fExpandingStarted) { 397 BString string; 398 int32 i = 0; 399 while (message->FindString("output", i++, &string) == B_OK) { 400 if (strstr(string.String(), "Enter password") != NULL) { 401 fExpandingThread->SuspendExternalExpander(); 402 BString password; 403 PasswordAlert* alert = 404 new PasswordAlert("passwordAlert", string); 405 alert->Go(password); 406 fExpandingThread->ResumeExternalExpander(); 407 fExpandingThread->PushInput(password); 408 } 409 } 410 } 411 break; 412 413 case 'errp': 414 { 415 BString string; 416 if (message->FindString("error", &string) == B_OK 417 && fExpandingStarted) { 418 fExpandingThread->SuspendExternalExpander(); 419 if (strstr(string.String(), "password") != NULL) { 420 BString password; 421 PasswordAlert* alert = new PasswordAlert("passwordAlert", 422 string); 423 alert->Go(password); 424 fExpandingThread->ResumeExternalExpander(); 425 fExpandingThread->PushInput(password); 426 } else { 427 BAlert* alert = new BAlert("stopAlert", string, 428 B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL, 429 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 430 alert->SetShortcut(0, B_ESCAPE); 431 if (alert->Go() == 0) { 432 fExpandingThread->ResumeExternalExpander(); 433 StopExpanding(); 434 } else 435 fExpandingThread->ResumeExternalExpander(); 436 } 437 } 438 break; 439 } 440 441 case 'exit': 442 // thread has finished 443 // (finished, quit, killed, we don't know) 444 // reset window state 445 if (fExpandingStarted) { 446 SetStatus(B_TRANSLATE("File expanded")); 447 StopExpanding(); 448 OpenDestFolder(); 449 CloseWindowOrKeepOpen(); 450 } else if (fListingStarted) { 451 fSourceChanged = false; 452 StopListing(); 453 _ExpandListingText(); 454 } else 455 SetStatus(""); 456 break; 457 458 case 'exrr': 459 // thread has finished 460 // reset window state 461 462 SetStatus(B_TRANSLATE("Error when expanding archive")); 463 CloseWindowOrKeepOpen(); 464 break; 465 466 default: 467 BWindow::MessageReceived(message); 468 } 469 } 470 471 472 bool 473 ExpanderWindow::CanQuit() 474 { 475 if ((fSourcePanel && fSourcePanel->IsShowing()) 476 || (fDestPanel && fDestPanel->IsShowing())) { 477 return false; 478 } 479 480 if (fExpandingStarted) { 481 fExpandingThread->SuspendExternalExpander(); 482 BAlert* alert = new BAlert("stopAlert", 483 B_TRANSLATE("Are you sure you want to stop expanding this\n" 484 "archive? The expanded items may not be complete."), 485 B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL, 486 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 487 alert->SetShortcut(0, B_ESCAPE); 488 489 if (alert->Go() == 0) { 490 fExpandingThread->ResumeExternalExpander(); 491 StopExpanding(); 492 } else { 493 fExpandingThread->ResumeExternalExpander(); 494 return false; 495 } 496 } 497 498 return true; 499 } 500 501 502 bool 503 ExpanderWindow::QuitRequested() 504 { 505 if (!CanQuit()) 506 return false; 507 508 if (fListingStarted) 509 StopListing(); 510 511 be_app->PostMessage(B_QUIT_REQUESTED); 512 fSettings.ReplacePoint("window_position", Frame().LeftTop()); 513 ((ExpanderApp*)(be_app))->UpdateSettingsFrom(&fSettings); 514 515 return true; 516 } 517 518 519 void 520 ExpanderWindow::RefsReceived(BMessage* message) 521 { 522 entry_ref ref; 523 int32 i = 0; 524 int8 destinationFolder = 0x63; 525 fSettings.FindInt8("destination_folder", &destinationFolder); 526 527 while (message->FindRef("refs", i++, &ref) == B_OK) { 528 BEntry entry(&ref, true); 529 BPath path(&entry); 530 BNode node(&entry); 531 532 if (node.IsFile()) { 533 fSourceChanged = true; 534 fSourceRef = ref; 535 fSourceText->SetText(path.Path()); 536 if (destinationFolder == 0x63) { 537 BPath parent; 538 path.GetParent(&parent); 539 fDestText->SetText(parent.Path()); 540 get_ref_for_path(parent.Path(), &fDestRef); 541 } else if (destinationFolder == 0x65) { 542 fSettings.FindRef("destination_folder_use", &fDestRef); 543 BEntry dEntry(&fDestRef, true); 544 BPath dPath(&dEntry); 545 fDestText->SetText(dPath.Path()); 546 } 547 548 BEntry dEntry(&fDestRef, true); 549 if (dEntry.Exists()) { 550 fExpandButton->SetEnabled(true); 551 fExpandItem->SetEnabled(true); 552 } 553 554 if (fShowContents->Value() == B_CONTROL_ON) { 555 StopListing(); 556 StartListing(); 557 } else { 558 fShowContents->SetEnabled(true); 559 fShowItem->SetEnabled(true); 560 } 561 562 bool fromApp; 563 if (message->FindBool("fromApp", &fromApp) == B_OK) { 564 AutoExpand(); 565 } else 566 AutoListing(); 567 } else if (node.IsDirectory()) { 568 fDestRef = ref; 569 fDestText->SetText(path.Path()); 570 } 571 } 572 } 573 574 575 #undef B_TRANSLATION_CONTEXT 576 #define B_TRANSLATION_CONTEXT "ExpanderMenu" 577 578 void 579 ExpanderWindow::_CreateMenuBar() 580 { 581 fBar = new BMenuBar("menu_bar", B_ITEMS_IN_ROW, B_INVALIDATE_AFTER_LAYOUT); 582 BMenu* menu = new BMenu(B_TRANSLATE("File")); 583 menu->AddItem(fSourceItem 584 = new BMenuItem(B_TRANSLATE("Set source" B_UTF8_ELLIPSIS), 585 new BMessage(MSG_SOURCE), 'O')); 586 menu->AddItem(fDestItem 587 = new BMenuItem(B_TRANSLATE("Set destination" B_UTF8_ELLIPSIS), 588 new BMessage(MSG_DEST), 'D')); 589 menu->AddSeparatorItem(); 590 menu->AddItem(fExpandItem = new BMenuItem(B_TRANSLATE("Expand"), 591 new BMessage(MSG_EXPAND), 'E')); 592 fExpandItem->SetEnabled(false); 593 menu->AddItem(fShowItem = new BMenuItem(B_TRANSLATE("Show contents"), 594 new BMessage(MSG_SHOW), 'L')); 595 fShowItem->SetEnabled(false); 596 menu->AddSeparatorItem(); 597 menu->AddItem(fStopItem = new BMenuItem(B_TRANSLATE("Stop"), 598 new BMessage(MSG_STOP), 'K')); 599 fStopItem->SetEnabled(false); 600 menu->AddSeparatorItem(); 601 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 602 new BMessage(B_QUIT_REQUESTED), 'W')); 603 fBar->AddItem(menu); 604 605 menu = new BMenu(B_TRANSLATE("Settings")); 606 menu->AddItem(fPreferencesItem 607 = new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), 608 new BMessage(MSG_PREFERENCES), 'S')); 609 fBar->AddItem(menu); 610 } 611 612 613 #undef B_TRANSLATION_CONTEXT 614 #define B_TRANSLATION_CONTEXT "ExpanderWindow" 615 616 void 617 ExpanderWindow::StartExpanding() 618 { 619 ExpanderRule* rule = fRules.MatchingRule(&fSourceRef); 620 if (!rule) 621 return; 622 623 BEntry destEntry(fDestText->Text(), true); 624 if (!destEntry.Exists()) { 625 BAlert* alert = new BAlert("destAlert", 626 B_TRANSLATE("The folder was either moved, renamed or not\nsupported."), 627 B_TRANSLATE("Cancel"), NULL, NULL, 628 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 629 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 630 alert->Go(); 631 return; 632 } 633 634 BMessage message; 635 message.AddString("cmd", rule->ExpandCmd()); 636 message.AddRef("srcRef", &fSourceRef); 637 message.AddRef("destRef", &fDestRef); 638 639 fExpandButton->SetLabel(B_TRANSLATE("Stop")); 640 fSourceButton->SetEnabled(false); 641 fDestButton->SetEnabled(false); 642 fShowContents->SetEnabled(false); 643 fSourceItem->SetEnabled(false); 644 fDestItem->SetEnabled(false); 645 fExpandItem->SetEnabled(false); 646 fShowItem->SetEnabled(false); 647 fStopItem->SetEnabled(true); 648 fPreferencesItem->SetEnabled(false); 649 650 BEntry entry(&fSourceRef); 651 BPath path(&entry); 652 BString text(B_TRANSLATE("Expanding '%s'")); 653 text.ReplaceFirst("%s", path.Leaf()); 654 SetStatus(text.String()); 655 656 fExpandingThread = new ExpanderThread(&message, new BMessenger(this)); 657 fExpandingThread->Start(); 658 659 fExpandingStarted = true; 660 } 661 662 663 void 664 ExpanderWindow::StopExpanding(void) 665 { 666 if (fExpandingThread) { 667 fExpandingThread->InterruptExternalExpander(); 668 fExpandingThread = NULL; 669 } 670 671 fExpandingStarted = false; 672 673 fExpandButton->SetLabel(B_TRANSLATE("Expand")); 674 fSourceButton->SetEnabled(true); 675 fDestButton->SetEnabled(true); 676 fShowContents->SetEnabled(true); 677 fSourceItem->SetEnabled(true); 678 fDestItem->SetEnabled(true); 679 fExpandItem->SetEnabled(true); 680 fShowItem->SetEnabled(true); 681 fStopItem->SetEnabled(false); 682 fPreferencesItem->SetEnabled(true); 683 } 684 685 686 void 687 ExpanderWindow::_ExpandListingText() 688 { 689 float delta = fLongestLine - fListingText->Frame().Width(); 690 691 if (delta > 0) { 692 BScreen screen; 693 BRect screenFrame = screen.Frame(); 694 695 if (Frame().right + delta > screenFrame.right) 696 delta = screenFrame.right - Frame().right - 4.0f; 697 698 ResizeBy(delta, 0.0f); 699 } 700 701 float minWidth, maxWidth, minHeight, maxHeight; 702 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 703 704 if (minWidth < Frame().Width() + delta) { 705 // set the Zoom limit as the minimal required size 706 SetZoomLimits(Frame().Width() + delta, 707 std::min(fSizeLimit + fListingText->TextRect().Height() 708 + fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f, 709 maxHeight)); 710 } else { 711 // set the zoom limit based on minimal window size allowed 712 SetZoomLimits(minWidth, 713 std::min(fSizeLimit + fListingText->TextRect().Height() 714 + fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f, 715 maxHeight)); 716 } 717 } 718 719 720 void 721 ExpanderWindow::_UpdateWindowSize(bool showContents) 722 { 723 float minWidth, maxWidth, minHeight, maxHeight; 724 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 725 726 float bottom = fSizeLimit; 727 728 if (showContents) { 729 if (fPreviousHeight < 0.0) { 730 BFont font; 731 font_height fontHeight; 732 fListingText->GetFont(&font); 733 font.GetHeight(&fontHeight); 734 fLineHeight = ceilf(fontHeight.ascent + fontHeight.descent 735 + fontHeight.leading); 736 fPreviousHeight = bottom + 10.0 * fLineHeight; 737 } 738 minHeight = bottom + 5.0 * fLineHeight; 739 maxHeight = 32767.0; 740 741 bottom = std::max(fPreviousHeight, minHeight); 742 } else { 743 minHeight = fSizeLimit; 744 maxHeight = fSizeLimit; 745 fPreviousHeight = Frame().Height(); 746 } 747 748 SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 749 ResizeTo(Frame().Width(), bottom); 750 } 751 752 753 void 754 ExpanderWindow::SetStatus(BString text) 755 { 756 if (text.Length() >= MAX_STATUS_LENGTH) { 757 text.Truncate(MAX_STATUS_LENGTH - 1); 758 text << B_UTF8_ELLIPSIS; 759 } 760 761 fStatusView->SetText(text); 762 } 763 764 765 void 766 ExpanderWindow::StartListing() 767 { 768 _UpdateWindowSize(true); 769 770 if (!fSourceChanged) 771 return; 772 773 fPreviousHeight = -1.0; 774 775 fLongestLine = 0.0f; 776 777 ExpanderRule* rule = fRules.MatchingRule(&fSourceRef); 778 if (!rule) 779 return; 780 781 BMessage message; 782 message.AddString("cmd", rule->ListingCmd()); 783 message.AddRef("srcRef", &fSourceRef); 784 785 fShowContents->SetEnabled(true); 786 fSourceItem->SetEnabled(false); 787 fDestItem->SetEnabled(false); 788 fExpandItem->SetEnabled(false); 789 fShowItem->SetEnabled(true); 790 fShowItem->SetLabel(B_TRANSLATE("Hide contents")); 791 fStopItem->SetEnabled(false); 792 fPreferencesItem->SetEnabled(false); 793 794 fSourceButton->SetEnabled(false); 795 fDestButton->SetEnabled(false); 796 fExpandButton->SetEnabled(false); 797 798 BEntry entry(&fSourceRef); 799 BPath path(&entry); 800 BString text(B_TRANSLATE("Creating listing for '%s'")); 801 text.ReplaceFirst("%s", path.Leaf()); 802 SetStatus(text.String()); 803 fListingText->SetText(""); 804 805 fListingThread = new ExpanderThread(&message, new BMessenger(this)); 806 fListingThread->Start(); 807 808 fListingStarted = true; 809 } 810 811 812 void 813 ExpanderWindow::StopListing(void) 814 { 815 if (fListingThread) { 816 fListingThread->InterruptExternalExpander(); 817 fListingThread = NULL; 818 } 819 820 fListingStarted = false; 821 822 fShowContents->SetEnabled(true); 823 fSourceItem->SetEnabled(true); 824 fDestItem->SetEnabled(true); 825 fExpandItem->SetEnabled(true); 826 fShowItem->SetEnabled(true); 827 fStopItem->SetEnabled(false); 828 fPreferencesItem->SetEnabled(true); 829 830 fSourceButton->SetEnabled(true); 831 fDestButton->SetEnabled(true); 832 fExpandButton->SetEnabled(true); 833 SetStatus(""); 834 } 835 836 837 void 838 ExpanderWindow::CloseWindowOrKeepOpen() 839 { 840 bool expandFiles = false; 841 fSettings.FindBool("automatically_expand_files", &expandFiles); 842 843 bool closeWhenDone = false; 844 fSettings.FindBool("close_when_done", &closeWhenDone); 845 846 if (expandFiles || closeWhenDone) 847 PostMessage(B_QUIT_REQUESTED); 848 } 849 850 851 void 852 ExpanderWindow::OpenDestFolder() 853 { 854 bool openFolder = true; 855 fSettings.FindBool("open_destination_folder", &openFolder); 856 857 if (!openFolder) 858 return; 859 860 BMessage* message = new BMessage(B_REFS_RECEIVED); 861 message->AddRef("refs", &fDestRef); 862 BPath path(&fDestRef); 863 BMessenger tracker("application/x-vnd.Be-TRAK"); 864 tracker.SendMessage(message); 865 } 866 867 868 void 869 ExpanderWindow::AutoListing() 870 { 871 bool showContents = false; 872 fSettings.FindBool("show_contents_listing", &showContents); 873 874 if (!showContents) 875 return; 876 877 fShowContents->SetValue(B_CONTROL_ON); 878 fShowContents->Invoke(); 879 } 880 881 882 void 883 ExpanderWindow::AutoExpand() 884 { 885 bool expandFiles = false; 886 fSettings.FindBool("automatically_expand_files", &expandFiles); 887 888 if (!expandFiles) { 889 AutoListing(); 890 return; 891 } 892 893 fExpandButton->Invoke(); 894 } 895