1 /* 2 * Copyright 2004-2006, Jérôme DUVAL. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ExpanderApp.h" 8 #include "ExpanderWindow.h" 9 #include "ExpanderThread.h" 10 #include "ExpanderPreferences.h" 11 12 #include <Alert.h> 13 #include <Application.h> 14 #include <Box.h> 15 #include <Button.h> 16 #include <CheckBox.h> 17 #include <Entry.h> 18 #include <File.h> 19 #include <Menu.h> 20 #include <MenuBar.h> 21 #include <MenuItem.h> 22 #include <Path.h> 23 #include <ScrollView.h> 24 #include <StringView.h> 25 #include <TextView.h> 26 27 const uint32 MSG_SOURCE = 'mSOU'; 28 const uint32 MSG_DEST = 'mDES'; 29 const uint32 MSG_EXPAND = 'mEXP'; 30 const uint32 MSG_SHOW = 'mSHO'; 31 const uint32 MSG_STOP = 'mSTO'; 32 const uint32 MSG_PREFERENCES = 'mPRE'; 33 const uint32 MSG_SOURCETEXT = 'mSTX'; 34 const uint32 MSG_DESTTEXT = 'mDTX'; 35 const uint32 MSG_SHOWCONTENTS = 'mSCT'; 36 37 38 ExpanderWindow::ExpanderWindow(BRect frame, const entry_ref *ref, BMessage *settings) 39 : BWindow(frame, "Expand-O-Matic", B_TITLED_WINDOW, B_NOT_ZOOMABLE), 40 fSourceChanged(true), 41 fListingThread(NULL), 42 fListingStarted(false), 43 fExpandingThread(NULL), 44 fExpandingStarted(false), 45 fSettings(*settings), 46 fPreferences(NULL) 47 { 48 fSourcePanel = NULL; 49 fDestPanel = NULL; 50 51 // create menu bar 52 fBar = new BMenuBar(BRect(0, 0, Bounds().right, 20), "menu_bar"); 53 BMenu *menu = new BMenu("File"); 54 BMenuItem *item; 55 menu->AddItem(item = new BMenuItem("About Expander" B_UTF8_ELLIPSIS, new BMessage(B_ABOUT_REQUESTED))); 56 item->SetTarget(be_app_messenger); 57 menu->AddSeparatorItem(); 58 menu->AddItem(fSourceItem = new BMenuItem("Set Source" B_UTF8_ELLIPSIS, new BMessage(MSG_SOURCE), 'O')); 59 menu->AddItem(fDestItem = new BMenuItem("Set Destination" B_UTF8_ELLIPSIS, new BMessage(MSG_DEST), 'D')); 60 menu->AddSeparatorItem(); 61 menu->AddItem(fExpandItem = new BMenuItem("Expand", new BMessage(MSG_EXPAND), 'E')); 62 fExpandItem->SetEnabled(false); 63 menu->AddItem(fShowItem = new BMenuItem("Show Contents", new BMessage(MSG_SHOW), 'L')); 64 fShowItem->SetEnabled(false); 65 menu->AddSeparatorItem(); 66 menu->AddItem(fStopItem = new BMenuItem("Stop", new BMessage(MSG_STOP), 'K')); 67 fStopItem->SetEnabled(false); 68 menu->AddSeparatorItem(); 69 menu->AddItem(new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED), 'W')); 70 fBar->AddItem(menu); 71 72 menu = new BMenu("Edit"); 73 menu->AddItem(fPreferencesItem = new BMenuItem("Preferences" B_UTF8_ELLIPSIS, 74 new BMessage(MSG_PREFERENCES), 'P')); 75 fBar->AddItem(menu); 76 AddChild(fBar); 77 78 BRect rect = Bounds(); 79 rect.top += fBar->Bounds().Height() + 1; 80 BView *topView = new BView(rect, "background", B_FOLLOW_ALL, B_WILL_DRAW); 81 topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 82 AddChild(topView); 83 84 // Buttons 85 86 rect = topView->Bounds().InsetByCopy(8, 8); 87 fDestButton = new BButton(rect, "destButton", "Destination", 88 new BMessage(MSG_DEST), B_FOLLOW_LEFT | B_FOLLOW_TOP); 89 fDestButton->ResizeToPreferred(); 90 topView->AddChild(fDestButton); 91 92 rect = fDestButton->Frame(); 93 fDestButton->MoveBy(0, rect.Height() + 4); 94 95 fSourceButton = new BButton(rect, "sourceButton", "Source", 96 new BMessage(MSG_SOURCE), B_FOLLOW_LEFT | B_FOLLOW_TOP); 97 topView->AddChild(fSourceButton); 98 99 rect = fDestButton->Frame(); 100 rect.OffsetBy(0, rect.Height() + 4); 101 fExpandButton = new BButton(rect, "expandButton", "Expand", 102 new BMessage(MSG_EXPAND), B_FOLLOW_LEFT | B_FOLLOW_TOP); 103 topView->AddChild(fExpandButton); 104 fExpandButton->SetEnabled(false); 105 106 // TextControls 107 108 rect = fSourceButton->Frame(); 109 rect.left = rect.right + 8; 110 rect.right = frame.Width() - 8; 111 fSourceText = new BTextControl(rect, "sourceText", "", NULL, 112 new BMessage(MSG_SOURCETEXT), B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); 113 fSourceText->SetDivider(0); 114 float width, height; 115 fSourceText->GetPreferredSize(&width, &height); 116 fSourceText->ResizeTo(rect.Width(), height); 117 float offset = (rect.Height() - fSourceText->Bounds().Height()) / 2; 118 fSourceText->MoveBy(0, offset); 119 topView->AddChild(fSourceText); 120 121 rect = fSourceText->Frame(); 122 rect.OffsetBy(0, fSourceButton->Bounds().Height() + 4); 123 fDestText = new BTextControl(rect, "destText", "", NULL, 124 new BMessage(MSG_DESTTEXT), B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); 125 fDestText->SetDivider(0); 126 topView->AddChild(fDestText); 127 128 rect.OffsetBy(0, fSourceButton->Bounds().Height() + 4 + offset); 129 fShowContents = new BCheckBox(rect, "showContents", "Show Contents", 130 new BMessage(MSG_SHOWCONTENTS), B_FOLLOW_RIGHT | B_FOLLOW_TOP); 131 fShowContents->ResizeToPreferred(); 132 fShowContents->MoveTo(Bounds().right - 8 - fShowContents->Bounds().Width(), 133 rect.top); 134 topView->AddChild(fShowContents); 135 fShowContents->SetEnabled(false); 136 137 rect.left = fExpandButton->Frame().right + 8; 138 rect.right = fShowContents->Frame().left - 4; 139 fStatusView = new BStringView(rect, "statusView", "", 140 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_FRAME_EVENTS); 141 topView->AddChild(fStatusView); 142 143 ResizeTo(frame.Width(), topView->Frame().top + fExpandButton->Frame().bottom + 8); 144 SetSizeLimits(fExpandButton->Bounds().Width() * 3 + fShowContents->Bounds().Width() + 24, 145 32767.0f, Frame().Height(), Frame().Height()); 146 147 rect = topView->Bounds(); 148 rect.InsetBy(10, 0); 149 rect.top = rect.bottom + 2; 150 rect.bottom -= 10; 151 rect.right -= B_V_SCROLL_BAR_WIDTH; 152 153 BRect textRect = rect; 154 textRect.OffsetTo(B_ORIGIN); 155 textRect.InsetBy(1, 1); 156 fListingText = new BTextView(rect, "listingText", textRect, 157 be_fixed_font, NULL, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS); 158 fListingText->SetText(""); 159 fListingText->MakeEditable(false); 160 fListingScroll = new BScrollView("listingScroll", fListingText, 161 B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS, false, true); 162 topView->AddChild(fListingScroll); 163 fListingScroll->Hide(); 164 165 // finish creating window 166 Show(); 167 } 168 169 170 ExpanderWindow::~ExpanderWindow() 171 { 172 } 173 174 175 void 176 ExpanderWindow::FrameResized(float width, float height) 177 { 178 if (fListingText->DoesWordWrap()) { 179 BRect textRect; 180 textRect = fListingText->Bounds(); 181 textRect.OffsetTo(B_ORIGIN); 182 textRect.InsetBy(1, 1); 183 fListingText->SetTextRect(textRect); 184 } 185 } 186 187 188 void 189 ExpanderWindow::MessageReceived(BMessage *msg) 190 { 191 switch (msg->what) { 192 case MSG_SOURCE: 193 if (!fSourcePanel) { 194 fSourcePanel = new BFilePanel(B_OPEN_PANEL, new BMessenger(this), NULL, 195 B_FILE_NODE, false, NULL, new RuleRefFilter(fRules), true); 196 } 197 fSourcePanel->Show(); 198 break; 199 200 case MSG_DEST: 201 if (!fDestPanel) { 202 fDestPanel = new DirectoryFilePanel(B_OPEN_PANEL, new BMessenger(this), NULL, 203 B_DIRECTORY_NODE, false, NULL, new DirectoryRefFilter(), true); 204 } 205 fDestPanel->Show(); 206 break; 207 208 case MSG_DIRECTORY: 209 { 210 entry_ref ref; 211 fDestPanel->GetPanelDirectory(&ref); 212 fDestRef = ref; 213 BEntry entry(&ref); 214 BPath path(&entry); 215 fDestText->SetText(path.Path()); 216 fDestPanel->Hide(); 217 break; 218 } 219 220 case B_SIMPLE_DATA: 221 case B_REFS_RECEIVED: 222 RefsReceived(msg); 223 break; 224 225 case MSG_EXPAND: 226 if (!fExpandingStarted) { 227 StartExpanding(); 228 break; 229 } 230 // supposed to fall through 231 case MSG_STOP: 232 if (fExpandingStarted) { 233 fExpandingThread->SuspendExternalExpander(); 234 BAlert *alert = new BAlert("stopAlert", "Are you sure you want to stop expanding this\n" 235 "archive? The expanded items may not be complete.", "Stop", "Continue", NULL, 236 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 237 if (alert->Go() == 0) { 238 fExpandingThread->ResumeExternalExpander(); 239 StopExpanding(); 240 } else 241 fExpandingThread->ResumeExternalExpander(); 242 } 243 break; 244 245 case MSG_SHOW: 246 fShowContents->SetValue(fShowContents->Value() == B_CONTROL_OFF 247 ? B_CONTROL_ON : B_CONTROL_OFF); 248 // supposed to fall through 249 case MSG_SHOWCONTENTS: 250 // change menu item label 251 fShowItem->SetLabel(fShowContents->Value() == B_CONTROL_OFF 252 ? "Show Contents" : "Hide Contents"); 253 254 if (fShowContents->Value() == B_CONTROL_OFF) { 255 if (fListingStarted) 256 StopListing(); 257 258 _UpdateWindowSize(false); 259 } else 260 StartListing(); 261 break; 262 263 case MSG_SOURCETEXT: 264 { 265 BEntry entry(fSourceText->Text(), true); 266 if (!entry.Exists()) { 267 BAlert *alert = new BAlert("srcAlert", "The file doesn't exist", 268 "Cancel", NULL, NULL, 269 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 270 alert->Go(); 271 break; 272 } 273 274 entry_ref ref; 275 entry.GetRef(&ref); 276 ExpanderRule *rule = fRules.MatchingRule(&ref); 277 if (rule) { 278 fSourceChanged = true; 279 fSourceRef = ref; 280 fShowContents->SetEnabled(true); 281 fExpandButton->SetEnabled(true); 282 fExpandItem->SetEnabled(true); 283 fShowItem->SetEnabled(true); 284 break; 285 } 286 287 BString string = "The file : "; 288 string += fSourceText->Text(); 289 string += " is not supported"; 290 BAlert *alert = new BAlert("srcAlert", string.String(), 291 "Cancel", NULL, NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT); 292 alert->Go(); 293 294 fShowContents->SetEnabled(false); 295 fExpandButton->SetEnabled(false); 296 fExpandItem->SetEnabled(false); 297 fShowItem->SetEnabled(false); 298 } 299 break; 300 case MSG_DESTTEXT: 301 { 302 BEntry entry(fDestText->Text(), true); 303 if (!entry.Exists()) { 304 BAlert *alert = new BAlert("destAlert", "The directory was either moved, renamed or not\n" 305 "supported.", 306 "Cancel", NULL, NULL, 307 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 308 alert->Go(); 309 break; 310 } 311 entry.GetRef(&fDestRef); 312 } 313 break; 314 case MSG_PREFERENCES: 315 if (!fPreferences) 316 fPreferences = new ExpanderPreferences(&fSettings); 317 fPreferences->Show(); 318 break; 319 case 'outp': 320 if (!fExpandingStarted && fListingStarted) { 321 BString string; 322 int32 i = 0; 323 while (msg->FindString("output", i++, &string) == B_OK) 324 fListingText->Insert(string.String()); 325 fListingText->ScrollToSelection(); 326 } 327 328 break; 329 case 'exit': // thread has finished (finished, quit, killed, we don't know) 330 // reset window state 331 if (fExpandingStarted) { 332 fStatusView->SetText("File expanded"); 333 StopExpanding(); 334 OpenDestFolder(); 335 CloseWindowOrKeepOpen(); 336 } else if (fListingStarted){ 337 fSourceChanged = false; 338 StopListing(); 339 } else 340 fStatusView->SetText(""); 341 break; 342 case 'exrr': // thread has finished 343 // reset window state 344 345 fStatusView->SetText("Error when expanding archive"); 346 CloseWindowOrKeepOpen(); 347 break; 348 default: 349 BWindow::MessageReceived(msg); 350 break; 351 } 352 } 353 354 355 bool 356 ExpanderWindow::CanQuit() 357 { 358 if ((fSourcePanel && fSourcePanel->IsShowing()) 359 || (fDestPanel && fDestPanel->IsShowing())) 360 return false; 361 362 if (fExpandingStarted) { 363 fExpandingThread->SuspendExternalExpander(); 364 BAlert *alert = new BAlert("stopAlert", "Are you sure you want to stop expanding this\n" 365 "archive? The expanded items may not be complete.", "Stop", "Continue", NULL, 366 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 367 if (alert->Go() == 0) { 368 fExpandingThread->ResumeExternalExpander(); 369 StopExpanding(); 370 } else { 371 fExpandingThread->ResumeExternalExpander(); 372 return false; 373 } 374 } 375 return true; 376 } 377 378 379 bool 380 ExpanderWindow::QuitRequested() 381 { 382 if (!CanQuit()) 383 return false; 384 385 if (fListingStarted) 386 StopListing(); 387 388 be_app->PostMessage(B_QUIT_REQUESTED); 389 fSettings.ReplacePoint("window_position", Frame().LeftTop()); 390 ((ExpanderApp*)(be_app))->UpdateSettingsFrom(&fSettings); 391 return true; 392 } 393 394 395 void 396 ExpanderWindow::RefsReceived(BMessage *msg) 397 { 398 entry_ref ref; 399 int32 i = 0; 400 int8 destination_folder = 0x63; 401 fSettings.FindInt8("destination_folder", &destination_folder); 402 403 while (msg->FindRef("refs", i++, &ref) == B_OK) { 404 BEntry entry(&ref, true); 405 BPath path(&entry); 406 BNode node(&entry); 407 408 if (node.IsFile()) { 409 fSourceChanged = true; 410 fSourceRef = ref; 411 fSourceText->SetText(path.Path()); 412 if (destination_folder == 0x63) { 413 BPath parent; 414 path.GetParent(&parent); 415 fDestText->SetText(parent.Path()); 416 get_ref_for_path(parent.Path(), &fDestRef); 417 } else if (destination_folder == 0x65) { 418 fSettings.FindRef("destination_folder_use", &fDestRef); 419 BEntry dEntry(&fDestRef, true); 420 BPath dPath(&dEntry); 421 fDestText->SetText(dPath.Path()); 422 } 423 424 BEntry dEntry(&fDestRef, true); 425 if (dEntry.Exists()) { 426 fExpandButton->SetEnabled(true); 427 fExpandItem->SetEnabled(true); 428 } 429 430 if (fShowContents->Value() == B_CONTROL_ON) { 431 StopListing(); 432 StartListing(); 433 } else { 434 fShowContents->SetEnabled(true); 435 fShowItem->SetEnabled(true); 436 } 437 438 bool fromApp; 439 if (msg->FindBool("fromApp", &fromApp) == B_OK) { 440 AutoExpand(); 441 } else 442 AutoListing(); 443 } else if (node.IsDirectory()) { 444 fDestRef = ref; 445 fDestText->SetText(path.Path()); 446 } 447 } 448 } 449 450 451 void 452 ExpanderWindow::StartExpanding() 453 { 454 ExpanderRule *rule = fRules.MatchingRule(&fSourceRef); 455 if (!rule) 456 return; 457 458 BEntry destEntry(fDestText->Text(), true); 459 if (!destEntry.Exists()) { 460 BAlert *alert = new BAlert("destAlert", "The directory was either moved, renamed or not\n" 461 "supported.", 462 "Cancel", NULL, NULL, 463 B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); 464 alert->Go(); 465 return; 466 } 467 468 BMessage message; 469 message.AddString("cmd", rule->ExpandCmd()); 470 message.AddRef("srcRef", &fSourceRef); 471 message.AddRef("destRef", &fDestRef); 472 473 fExpandButton->SetLabel("Stop"); 474 fSourceButton->SetEnabled(false); 475 fDestButton->SetEnabled(false); 476 fShowContents->SetEnabled(false); 477 fSourceItem->SetEnabled(false); 478 fDestItem->SetEnabled(false); 479 fExpandItem->SetEnabled(false); 480 fShowItem->SetEnabled(false); 481 fStopItem->SetEnabled(true); 482 fPreferencesItem->SetEnabled(false); 483 484 BEntry entry(&fSourceRef); 485 BPath path(&entry); 486 BString text("Expanding file "); 487 text.Append(path.Leaf()); 488 fStatusView->SetText(text.String()); 489 490 fExpandingThread = new ExpanderThread(&message, new BMessenger(this)); 491 fExpandingThread->Start(); 492 493 fExpandingStarted = true; 494 } 495 496 497 void 498 ExpanderWindow::StopExpanding(void) 499 { 500 if (fExpandingThread) { 501 fExpandingThread->InterruptExternalExpander(); 502 fExpandingThread = NULL; 503 } 504 505 fExpandingStarted = false; 506 507 fExpandButton->SetLabel("Expand"); 508 fSourceButton->SetEnabled(true); 509 fDestButton->SetEnabled(true); 510 fShowContents->SetEnabled(true); 511 fSourceItem->SetEnabled(true); 512 fDestItem->SetEnabled(true); 513 fExpandItem->SetEnabled(true); 514 fShowItem->SetEnabled(true); 515 fStopItem->SetEnabled(false); 516 fPreferencesItem->SetEnabled(true); 517 } 518 519 520 void 521 ExpanderWindow::_UpdateWindowSize(bool showContents) 522 { 523 float minWidth, maxWidth, minHeight, maxHeight; 524 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 525 526 float bottom = fExpandButton->Parent()->Frame().top + fExpandButton->Frame().bottom + 8; 527 528 if (showContents) { 529 font_height fontHeight; 530 be_plain_font->GetHeight(&fontHeight); 531 float lineHeight = ceilf(fontHeight.ascent + fontHeight.descent + fontHeight.leading); 532 533 minHeight = bottom + 14 + 4 * lineHeight; 534 maxHeight = 32767.0; 535 bottom = minHeight + 10 * lineHeight; 536 } else { 537 minHeight = bottom; 538 maxHeight = bottom; 539 } 540 541 SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight); 542 ResizeTo(Frame().Width(), bottom); 543 } 544 545 546 void 547 ExpanderWindow::StartListing() 548 { 549 _UpdateWindowSize(true); 550 551 if (fListingScroll->IsHidden()) 552 fListingScroll->Show(); 553 554 if (!fSourceChanged) 555 return; 556 557 ExpanderRule *rule = fRules.MatchingRule(&fSourceRef); 558 if (!rule) 559 return; 560 561 BMessage message; 562 message.AddString("cmd", rule->ListingCmd()); 563 message.AddRef("srcRef", &fSourceRef); 564 565 fShowContents->SetEnabled(true); 566 fSourceItem->SetEnabled(false); 567 fDestItem->SetEnabled(false); 568 fExpandItem->SetEnabled(false); 569 fShowItem->SetEnabled(true); 570 fShowItem->SetLabel("Hide Contents"); 571 fStopItem->SetEnabled(false); 572 fPreferencesItem->SetEnabled(false); 573 574 fSourceButton->SetEnabled(false); 575 fDestButton->SetEnabled(false); 576 fExpandButton->SetEnabled(false); 577 578 BEntry entry(&fSourceRef); 579 BPath path(&entry); 580 BString text("Creating listing for "); 581 text.Append(path.Leaf()); 582 fStatusView->SetText(text.String()); 583 fListingText->SetText(""); 584 585 fListingThread = new ExpanderThread(&message, new BMessenger(this)); 586 fListingThread->Start(); 587 588 fListingStarted = true; 589 } 590 591 592 void 593 ExpanderWindow::StopListing(void) 594 { 595 if (fListingThread) { 596 fListingThread->InterruptExternalExpander(); 597 fListingThread = NULL; 598 } 599 600 fListingStarted = false; 601 602 fShowContents->SetEnabled(true); 603 fSourceItem->SetEnabled(true); 604 fDestItem->SetEnabled(true); 605 fExpandItem->SetEnabled(true); 606 fShowItem->SetEnabled(true); 607 fStopItem->SetEnabled(false); 608 fPreferencesItem->SetEnabled(true); 609 610 fSourceButton->SetEnabled(true); 611 fDestButton->SetEnabled(true); 612 fExpandButton->SetEnabled(true); 613 fStatusView->SetText(""); 614 } 615 616 617 void 618 ExpanderWindow::CloseWindowOrKeepOpen() 619 { 620 bool expandFiles = false; 621 fSettings.FindBool("automatically_expand_files", &expandFiles); 622 623 bool closeWhenDone = false; 624 fSettings.FindBool("close_when_done", &closeWhenDone); 625 626 if (expandFiles || closeWhenDone) 627 PostMessage(B_QUIT_REQUESTED); 628 } 629 630 631 void 632 ExpanderWindow::OpenDestFolder() 633 { 634 bool openFolder = true; 635 fSettings.FindBool("open_destination_folder", &openFolder); 636 637 if (!openFolder) 638 return; 639 640 BMessage * message = new BMessage(B_REFS_RECEIVED); 641 message->AddRef("refs", &fDestRef); 642 BPath path(&fDestRef); 643 BMessenger tracker("application/x-vnd.Be-TRAK"); 644 tracker.SendMessage(message); 645 } 646 647 648 void 649 ExpanderWindow::AutoListing() 650 { 651 bool showContents = false; 652 fSettings.FindBool("show_contents_listing", &showContents); 653 654 if (!showContents) 655 return; 656 657 fShowContents->SetValue(B_CONTROL_ON); 658 fShowContents->Invoke(); 659 } 660 661 662 void 663 ExpanderWindow::AutoExpand() 664 { 665 bool expandFiles = false; 666 fSettings.FindBool("automatically_expand_files", &expandFiles); 667 668 if (!expandFiles) { 669 AutoListing(); 670 return; 671 } 672 673 fExpandButton->Invoke(); 674 } 675