xref: /haiku/src/apps/expander/ExpanderWindow.cpp (revision a629567a9001547736cfe892cdf992be16868fed)
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