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