xref: /haiku/src/apps/expander/ExpanderWindow.cpp (revision 1294543de9ac0eff000eaea1b18368c36435d08e)
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_TRANSLATE_CONTEXT
48 #define B_TRANSLATE_CONTEXT "ExpanderWindow"
49 
50 ExpanderWindow::ExpanderWindow(BRect frame, const entry_ref* ref,
51 		BMessage* settings)
52 	:
53 	BWindow(frame, B_TRANSLATE_COMMENT("Expander", "!! Window Title !!"),
54 		B_TITLED_WINDOW, 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_TRANSLATE_CONTEXT
510 #define B_TRANSLATE_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 	BMenuItem* item;
518 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("About Expander…"),
519 		new BMessage(B_ABOUT_REQUESTED)));
520 	item->SetTarget(be_app_messenger);
521 	menu->AddSeparatorItem();
522 	menu->AddItem(fSourceItem = new BMenuItem(B_TRANSLATE("Set source…"),
523 		new BMessage(MSG_SOURCE), 'O'));
524 	menu->AddItem(fDestItem = new BMenuItem(B_TRANSLATE("Set destination…"),
525 		new BMessage(MSG_DEST), 'D'));
526 	menu->AddSeparatorItem();
527 	menu->AddItem(fExpandItem = new BMenuItem(B_TRANSLATE("Expand"),
528 		new BMessage(MSG_EXPAND), 'E'));
529 	fExpandItem->SetEnabled(false);
530 	menu->AddItem(fShowItem = new BMenuItem(B_TRANSLATE("Show contents"),
531 		new BMessage(MSG_SHOW), 'L'));
532 	fShowItem->SetEnabled(false);
533 	menu->AddSeparatorItem();
534 	menu->AddItem(fStopItem = new BMenuItem(B_TRANSLATE("Stop"),
535 		new BMessage(MSG_STOP), 'K'));
536 	fStopItem->SetEnabled(false);
537 	menu->AddSeparatorItem();
538 	menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
539 		new BMessage(B_QUIT_REQUESTED), 'W'));
540 	fBar->AddItem(menu);
541 
542 	menu = new BMenu(B_TRANSLATE("Settings"));
543 	menu->AddItem(fPreferencesItem = new BMenuItem(B_TRANSLATE("Settings…"),
544 		new BMessage(MSG_PREFERENCES), 'S'));
545 	fBar->AddItem(menu);
546 	layout->AddView(fBar);
547 }
548 
549 
550 #undef B_TRANSLATE_CONTEXT
551 #define B_TRANSLATE_CONTEXT "ExpanderWindow"
552 
553 void
554 ExpanderWindow::StartExpanding()
555 {
556 	ExpanderRule* rule = fRules.MatchingRule(&fSourceRef);
557 	if (!rule)
558 		return;
559 
560 	BEntry destEntry(fDestText->Text(), true);
561 	if (!destEntry.Exists()) {
562 		BAlert* alert = new BAlert("destAlert",
563 		B_TRANSLATE("The folder was either moved, renamed or not\nsupported."),
564 		B_TRANSLATE("Cancel"), NULL, NULL,
565 			B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
566 		alert->Go();
567 		return;
568 	}
569 
570 	BMessage message;
571 	message.AddString("cmd", rule->ExpandCmd());
572 	message.AddRef("srcRef", &fSourceRef);
573 	message.AddRef("destRef", &fDestRef);
574 
575 	fExpandButton->SetLabel(B_TRANSLATE("Stop"));
576 	fSourceButton->SetEnabled(false);
577 	fDestButton->SetEnabled(false);
578 	fShowContents->SetEnabled(false);
579 	fSourceItem->SetEnabled(false);
580 	fDestItem->SetEnabled(false);
581 	fExpandItem->SetEnabled(false);
582 	fShowItem->SetEnabled(false);
583 	fStopItem->SetEnabled(true);
584 	fPreferencesItem->SetEnabled(false);
585 
586 	BEntry entry(&fSourceRef);
587 	BPath path(&entry);
588 	BString text(B_TRANSLATE("Expanding file "));
589 	text.Append(path.Leaf());
590 	fStatusView->SetText(text.String());
591 
592 	fExpandingThread = new ExpanderThread(&message, new BMessenger(this));
593 	fExpandingThread->Start();
594 
595 	fExpandingStarted = true;
596 }
597 
598 
599 void
600 ExpanderWindow::StopExpanding(void)
601 {
602 	if (fExpandingThread) {
603 		fExpandingThread->InterruptExternalExpander();
604 		fExpandingThread = NULL;
605 	}
606 
607 	fExpandingStarted = false;
608 
609 	fExpandButton->SetLabel(B_TRANSLATE("Expand"));
610 	fSourceButton->SetEnabled(true);
611 	fDestButton->SetEnabled(true);
612 	fShowContents->SetEnabled(true);
613 	fSourceItem->SetEnabled(true);
614 	fDestItem->SetEnabled(true);
615 	fExpandItem->SetEnabled(true);
616 	fShowItem->SetEnabled(true);
617 	fStopItem->SetEnabled(false);
618 	fPreferencesItem->SetEnabled(true);
619 }
620 
621 
622 void
623 ExpanderWindow::_ExpandListingText()
624 {
625 	float delta = fLongestLine - fListingText->Frame().Width();
626 
627 	if (delta > 0) {
628 		BScreen screen;
629 		BRect screenFrame = screen.Frame();
630 
631 		if (Frame().right + delta > screenFrame.right)
632 			delta = screenFrame.right - Frame().right - 4.0f;
633 
634 		ResizeBy(delta, 0.0f);
635 	}
636 
637 	float minWidth, maxWidth, minHeight, maxHeight;
638 	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
639 
640 	if (minWidth < Frame().Width() + delta) {
641 		// set the Zoom limit as the minimal required size
642 		SetZoomLimits(Frame().Width() + delta,
643 			min_c(fSizeLimit + fListingText->TextRect().Height()
644 				+ fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f,
645 				maxHeight));
646 	} else {
647 		// set the zoom limit based on minimal window size allowed
648 		SetZoomLimits(minWidth,
649 			min_c(fSizeLimit + fListingText->TextRect().Height()
650 				+ fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f,
651 				maxHeight));
652 	}
653 }
654 
655 
656 void
657 ExpanderWindow::_UpdateWindowSize(bool showContents)
658 {
659 	float minWidth, maxWidth, minHeight, maxHeight;
660 	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
661 
662 	float bottom = fSizeLimit;
663 
664 	if (showContents) {
665 		if (fPreviousHeight < 0.0) {
666 			BFont font;
667 			font_height fontHeight;
668 			fListingText->GetFont(&font);
669 			font.GetHeight(&fontHeight);
670 			fLineHeight = ceilf(fontHeight.ascent + fontHeight.descent
671 				+ fontHeight.leading);
672 			fPreviousHeight = bottom + 10.0 * fLineHeight;
673 		}
674 		minHeight = bottom + 5.0 * fLineHeight;
675 		maxHeight = 32767.0;
676 
677 		bottom = max_c(fPreviousHeight, minHeight);
678 	} else {
679 		minHeight = fSizeLimit;
680 		maxHeight = fSizeLimit;
681 		fPreviousHeight = Frame().Height();
682 	}
683 
684 	SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
685 	ResizeTo(Frame().Width(), bottom);
686 }
687 
688 
689 void
690 ExpanderWindow::StartListing()
691 {
692 	_UpdateWindowSize(true);
693 
694 	if (!fSourceChanged)
695 		return;
696 
697 	fPreviousHeight = -1.0;
698 
699 	fLongestLine = 0.0f;
700 
701 	ExpanderRule* rule = fRules.MatchingRule(&fSourceRef);
702 	if (!rule)
703 		return;
704 
705 	BMessage message;
706 	message.AddString("cmd", rule->ListingCmd());
707 	message.AddRef("srcRef", &fSourceRef);
708 
709 	fShowContents->SetEnabled(true);
710 	fSourceItem->SetEnabled(false);
711 	fDestItem->SetEnabled(false);
712 	fExpandItem->SetEnabled(false);
713 	fShowItem->SetEnabled(true);
714 	fShowItem->SetLabel(B_TRANSLATE("Hide contents"));
715 	fStopItem->SetEnabled(false);
716 	fPreferencesItem->SetEnabled(false);
717 
718 	fSourceButton->SetEnabled(false);
719 	fDestButton->SetEnabled(false);
720 	fExpandButton->SetEnabled(false);
721 
722 	BEntry entry(&fSourceRef);
723 	BPath path(&entry);
724 	BString text(B_TRANSLATE("Creating listing for "));
725 	text.Append(path.Leaf());
726 	fStatusView->SetText(text.String());
727 	fListingText->SetText("");
728 
729 	fListingThread = new ExpanderThread(&message, new BMessenger(this));
730 	fListingThread->Start();
731 
732 	fListingStarted = true;
733 }
734 
735 
736 void
737 ExpanderWindow::StopListing(void)
738 {
739 	if (fListingThread) {
740 		fListingThread->InterruptExternalExpander();
741 		fListingThread = NULL;
742 	}
743 
744 	fListingStarted = false;
745 
746 	fShowContents->SetEnabled(true);
747 	fSourceItem->SetEnabled(true);
748 	fDestItem->SetEnabled(true);
749 	fExpandItem->SetEnabled(true);
750 	fShowItem->SetEnabled(true);
751 	fStopItem->SetEnabled(false);
752 	fPreferencesItem->SetEnabled(true);
753 
754 	fSourceButton->SetEnabled(true);
755 	fDestButton->SetEnabled(true);
756 	fExpandButton->SetEnabled(true);
757 	fStatusView->SetText("");
758 }
759 
760 
761 void
762 ExpanderWindow::CloseWindowOrKeepOpen()
763 {
764 	bool expandFiles = false;
765 	fSettings.FindBool("automatically_expand_files", &expandFiles);
766 
767 	bool closeWhenDone = false;
768 	fSettings.FindBool("close_when_done", &closeWhenDone);
769 
770 	if (expandFiles || closeWhenDone)
771 		PostMessage(B_QUIT_REQUESTED);
772 }
773 
774 
775 void
776 ExpanderWindow::OpenDestFolder()
777 {
778 	bool openFolder = true;
779 	fSettings.FindBool("open_destination_folder", &openFolder);
780 
781 	if (!openFolder)
782 		return;
783 
784 	BMessage* message = new BMessage(B_REFS_RECEIVED);
785 	message->AddRef("refs", &fDestRef);
786 	BPath path(&fDestRef);
787 	BMessenger tracker("application/x-vnd.Be-TRAK");
788 	tracker.SendMessage(message);
789 }
790 
791 
792 void
793 ExpanderWindow::AutoListing()
794 {
795 	bool showContents = false;
796 	fSettings.FindBool("show_contents_listing", &showContents);
797 
798 	if (!showContents)
799 		return;
800 
801 	fShowContents->SetValue(B_CONTROL_ON);
802 	fShowContents->Invoke();
803 }
804 
805 
806 void
807 ExpanderWindow::AutoExpand()
808 {
809 	bool expandFiles = false;
810 	fSettings.FindBool("automatically_expand_files", &expandFiles);
811 
812 	if (!expandFiles) {
813 		AutoListing();
814 		return;
815 	}
816 
817 	fExpandButton->Invoke();
818 }
819