xref: /haiku/src/apps/expander/ExpanderWindow.cpp (revision 37344020ef592f58c61b7de8cd2c83dd31203aca)
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 			}
339 			break;
340 
341 		case 'errp':
342 		{
343 			BString string;
344 			if (msg->FindString("error", &string) == B_OK
345 				&& fExpandingStarted) {
346 				fExpandingThread->SuspendExternalExpander();
347 				if (strstr(string.String(), "password") != NULL) {
348 					BString password;
349 					PasswordAlert* alert = new PasswordAlert("passwordAlert", string);
350 					alert->Go(password);
351 					fExpandingThread->ResumeExternalExpander();
352 					fExpandingThread->PushInput(password);
353 				} else {
354 					BAlert* alert = new BAlert("stopAlert", string,
355 						B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL,
356 						B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
357 					if (alert->Go() == 0) {
358 						fExpandingThread->ResumeExternalExpander();
359 							StopExpanding();
360 					} else
361 						fExpandingThread->ResumeExternalExpander();
362 				}
363 			}
364 			break;
365 		}
366 		case 'exit':
367 			// thread has finished		(finished, quit, killed, we don't know)
368 			// reset window state
369 			if (fExpandingStarted) {
370 				fStatusView->SetText(B_TRANSLATE("File expanded"));
371 				StopExpanding();
372 				OpenDestFolder();
373 				CloseWindowOrKeepOpen();
374 			} else if (fListingStarted) {
375 				fSourceChanged = false;
376 				StopListing();
377 				_ExpandListingText();
378 			} else
379 				fStatusView->SetText("");
380 			break;
381 
382 		case 'exrr':	// thread has finished
383 			// reset window state
384 
385 			fStatusView->SetText(B_TRANSLATE("Error when expanding archive"));
386 			CloseWindowOrKeepOpen();
387 			break;
388 
389 		default:
390 			BWindow::MessageReceived(msg);
391 			break;
392 	}
393 }
394 
395 
396 bool
397 ExpanderWindow::CanQuit()
398 {
399 	if ((fSourcePanel && fSourcePanel->IsShowing())
400 		|| (fDestPanel && fDestPanel->IsShowing()))
401 		return false;
402 
403 	if (fExpandingStarted) {
404 		fExpandingThread->SuspendExternalExpander();
405 		BAlert* alert = new BAlert("stopAlert",
406 			B_TRANSLATE("Are you sure you want to stop expanding this\n"
407 				"archive? The expanded items may not be complete."),
408 			B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL,
409 			B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
410 		if (alert->Go() == 0) {
411 			fExpandingThread->ResumeExternalExpander();
412 			StopExpanding();
413 		} else {
414 			fExpandingThread->ResumeExternalExpander();
415 			return false;
416 		}
417 	}
418 	return true;
419 }
420 
421 
422 bool
423 ExpanderWindow::QuitRequested()
424 {
425 	if (!CanQuit())
426 		return false;
427 
428 	if (fListingStarted)
429 		StopListing();
430 
431 	be_app->PostMessage(B_QUIT_REQUESTED);
432 	fSettings.ReplacePoint("window_position", Frame().LeftTop());
433 	((ExpanderApp*)(be_app))->UpdateSettingsFrom(&fSettings);
434 	return true;
435 }
436 
437 
438 void
439 ExpanderWindow::RefsReceived(BMessage* msg)
440 {
441 	entry_ref ref;
442 	int32 i = 0;
443 	int8 destination_folder = 0x63;
444 	fSettings.FindInt8("destination_folder", &destination_folder);
445 
446 	while (msg->FindRef("refs", i++, &ref) == B_OK) {
447 		BEntry entry(&ref, true);
448 		BPath path(&entry);
449 		BNode node(&entry);
450 
451 		if (node.IsFile()) {
452 			fSourceChanged = true;
453 			fSourceRef = ref;
454 			fSourceText->SetText(path.Path());
455 			if (destination_folder == 0x63) {
456 				BPath parent;
457 				path.GetParent(&parent);
458 				fDestText->SetText(parent.Path());
459 				get_ref_for_path(parent.Path(), &fDestRef);
460 			} else if (destination_folder == 0x65) {
461 				fSettings.FindRef("destination_folder_use", &fDestRef);
462 				BEntry dEntry(&fDestRef, true);
463 				BPath dPath(&dEntry);
464 				fDestText->SetText(dPath.Path());
465 			}
466 
467 			BEntry dEntry(&fDestRef, true);
468 			if (dEntry.Exists()) {
469 				fExpandButton->SetEnabled(true);
470 				fExpandItem->SetEnabled(true);
471 			}
472 
473 			if (fShowContents->Value() == B_CONTROL_ON) {
474 				StopListing();
475 				StartListing();
476 			} else {
477 				fShowContents->SetEnabled(true);
478 				fShowItem->SetEnabled(true);
479 			}
480 
481 			bool fromApp;
482 			if (msg->FindBool("fromApp", &fromApp) == B_OK) {
483 				AutoExpand();
484 			} else
485 				AutoListing();
486 		} else if (node.IsDirectory()) {
487 			fDestRef = ref;
488 			fDestText->SetText(path.Path());
489 		}
490 	}
491 }
492 
493 
494 #undef B_TRANSLATE_CONTEXT
495 #define B_TRANSLATE_CONTEXT "ExpanderMenu"
496 
497 void
498 ExpanderWindow::_AddMenuBar(BLayout* layout)
499 {
500 	fBar = new BMenuBar("menu_bar", B_ITEMS_IN_ROW, B_INVALIDATE_AFTER_LAYOUT);
501 	BMenu* menu = new BMenu(B_TRANSLATE("File"));
502 	BMenuItem* item;
503 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("About Expander…"),
504 		new BMessage(B_ABOUT_REQUESTED)));
505 	item->SetTarget(be_app_messenger);
506 	menu->AddSeparatorItem();
507 	menu->AddItem(fSourceItem = new BMenuItem(B_TRANSLATE("Set source…"),
508 		new BMessage(MSG_SOURCE), 'O'));
509 	menu->AddItem(fDestItem = new BMenuItem(B_TRANSLATE("Set destination…"),
510 		new BMessage(MSG_DEST), 'D'));
511 	menu->AddSeparatorItem();
512 	menu->AddItem(fExpandItem = new BMenuItem(B_TRANSLATE("Expand"),
513 		new BMessage(MSG_EXPAND), 'E'));
514 	fExpandItem->SetEnabled(false);
515 	menu->AddItem(fShowItem = new BMenuItem(B_TRANSLATE("Show contents"),
516 		new BMessage(MSG_SHOW), 'L'));
517 	fShowItem->SetEnabled(false);
518 	menu->AddSeparatorItem();
519 	menu->AddItem(fStopItem = new BMenuItem(B_TRANSLATE("Stop"),
520 		new BMessage(MSG_STOP), 'K'));
521 	fStopItem->SetEnabled(false);
522 	menu->AddSeparatorItem();
523 	menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
524 		new BMessage(B_QUIT_REQUESTED), 'W'));
525 	fBar->AddItem(menu);
526 
527 	menu = new BMenu(B_TRANSLATE("Settings"));
528 	menu->AddItem(fPreferencesItem = new BMenuItem(B_TRANSLATE("Settings…"),
529 		new BMessage(MSG_PREFERENCES), 'S'));
530 	fBar->AddItem(menu);
531 	layout->AddView(fBar);
532 }
533 
534 
535 #undef B_TRANSLATE_CONTEXT
536 #define B_TRANSLATE_CONTEXT "ExpanderWindow"
537 
538 void
539 ExpanderWindow::StartExpanding()
540 {
541 	ExpanderRule* rule = fRules.MatchingRule(&fSourceRef);
542 	if (!rule)
543 		return;
544 
545 	BEntry destEntry(fDestText->Text(), true);
546 	if (!destEntry.Exists()) {
547 		BAlert* alert = new BAlert("destAlert",
548 		B_TRANSLATE("The folder was either moved, renamed or not\nsupported."),
549 		B_TRANSLATE("Cancel"), NULL, NULL,
550 			B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
551 		alert->Go();
552 		return;
553 	}
554 
555 	BMessage message;
556 	message.AddString("cmd", rule->ExpandCmd());
557 	message.AddRef("srcRef", &fSourceRef);
558 	message.AddRef("destRef", &fDestRef);
559 
560 	fExpandButton->SetLabel(B_TRANSLATE("Stop"));
561 	fSourceButton->SetEnabled(false);
562 	fDestButton->SetEnabled(false);
563 	fShowContents->SetEnabled(false);
564 	fSourceItem->SetEnabled(false);
565 	fDestItem->SetEnabled(false);
566 	fExpandItem->SetEnabled(false);
567 	fShowItem->SetEnabled(false);
568 	fStopItem->SetEnabled(true);
569 	fPreferencesItem->SetEnabled(false);
570 
571 	BEntry entry(&fSourceRef);
572 	BPath path(&entry);
573 	BString text(B_TRANSLATE("Expanding file "));
574 	text.Append(path.Leaf());
575 	fStatusView->SetText(text.String());
576 
577 	fExpandingThread = new ExpanderThread(&message, new BMessenger(this));
578 	fExpandingThread->Start();
579 
580 	fExpandingStarted = true;
581 }
582 
583 
584 void
585 ExpanderWindow::StopExpanding(void)
586 {
587 	if (fExpandingThread) {
588 		fExpandingThread->InterruptExternalExpander();
589 		fExpandingThread = NULL;
590 	}
591 
592 	fExpandingStarted = false;
593 
594 	fExpandButton->SetLabel(B_TRANSLATE("Expand"));
595 	fSourceButton->SetEnabled(true);
596 	fDestButton->SetEnabled(true);
597 	fShowContents->SetEnabled(true);
598 	fSourceItem->SetEnabled(true);
599 	fDestItem->SetEnabled(true);
600 	fExpandItem->SetEnabled(true);
601 	fShowItem->SetEnabled(true);
602 	fStopItem->SetEnabled(false);
603 	fPreferencesItem->SetEnabled(true);
604 }
605 
606 
607 void
608 ExpanderWindow::_ExpandListingText()
609 {
610 	float delta = fLongestLine - fListingText->Frame().Width();
611 
612 	if (delta > 0) {
613 		BScreen screen;
614 		BRect screenFrame = screen.Frame();
615 
616 		if (Frame().right + delta > screenFrame.right)
617 			delta = screenFrame.right - Frame().right - 4.0f;
618 
619 		ResizeBy(delta, 0.0f);
620 	}
621 
622 	float minWidth, maxWidth, minHeight, maxHeight;
623 	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
624 
625 	if (minWidth < Frame().Width() + delta) {
626 		// set the Zoom limit as the minimal required size
627 		SetZoomLimits(Frame().Width() + delta,
628 			min_c(fSizeLimit + fListingText->TextRect().Height()
629 				+ fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f,
630 				maxHeight));
631 	} else {
632 		// set the zoom limit based on minimal window size allowed
633 		SetZoomLimits(minWidth,
634 			min_c(fSizeLimit + fListingText->TextRect().Height()
635 				+ fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f,
636 				maxHeight));
637 	}
638 }
639 
640 
641 void
642 ExpanderWindow::_UpdateWindowSize(bool showContents)
643 {
644 	float minWidth, maxWidth, minHeight, maxHeight;
645 	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
646 
647 	float bottom = fSizeLimit;
648 
649 	if (showContents) {
650 		if (fPreviousHeight < 0.0) {
651 			BFont font;
652 			font_height fontHeight;
653 			fListingText->GetFont(&font);
654 			font.GetHeight(&fontHeight);
655 			fLineHeight = ceilf(fontHeight.ascent + fontHeight.descent
656 				+ fontHeight.leading);
657 			fPreviousHeight = bottom + 10.0 * fLineHeight;
658 		}
659 		minHeight = bottom + 5.0 * fLineHeight;
660 		maxHeight = 32767.0;
661 
662 		bottom = max_c(fPreviousHeight, minHeight);
663 	} else {
664 		minHeight = fSizeLimit;
665 		maxHeight = fSizeLimit;
666 		fPreviousHeight = Frame().Height();
667 	}
668 
669 	SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
670 	ResizeTo(Frame().Width(), bottom);
671 }
672 
673 
674 void
675 ExpanderWindow::StartListing()
676 {
677 	_UpdateWindowSize(true);
678 
679 	if (!fSourceChanged)
680 		return;
681 
682 	fPreviousHeight = -1.0;
683 
684 	fLongestLine = 0.0f;
685 
686 	ExpanderRule* rule = fRules.MatchingRule(&fSourceRef);
687 	if (!rule)
688 		return;
689 
690 	BMessage message;
691 	message.AddString("cmd", rule->ListingCmd());
692 	message.AddRef("srcRef", &fSourceRef);
693 
694 	fShowContents->SetEnabled(true);
695 	fSourceItem->SetEnabled(false);
696 	fDestItem->SetEnabled(false);
697 	fExpandItem->SetEnabled(false);
698 	fShowItem->SetEnabled(true);
699 	fShowItem->SetLabel(B_TRANSLATE("Hide contents"));
700 	fStopItem->SetEnabled(false);
701 	fPreferencesItem->SetEnabled(false);
702 
703 	fSourceButton->SetEnabled(false);
704 	fDestButton->SetEnabled(false);
705 	fExpandButton->SetEnabled(false);
706 
707 	BEntry entry(&fSourceRef);
708 	BPath path(&entry);
709 	BString text(B_TRANSLATE("Creating listing for "));
710 	text.Append(path.Leaf());
711 	fStatusView->SetText(text.String());
712 	fListingText->SetText("");
713 
714 	fListingThread = new ExpanderThread(&message, new BMessenger(this));
715 	fListingThread->Start();
716 
717 	fListingStarted = true;
718 }
719 
720 
721 void
722 ExpanderWindow::StopListing(void)
723 {
724 	if (fListingThread) {
725 		fListingThread->InterruptExternalExpander();
726 		fListingThread = NULL;
727 	}
728 
729 	fListingStarted = false;
730 
731 	fShowContents->SetEnabled(true);
732 	fSourceItem->SetEnabled(true);
733 	fDestItem->SetEnabled(true);
734 	fExpandItem->SetEnabled(true);
735 	fShowItem->SetEnabled(true);
736 	fStopItem->SetEnabled(false);
737 	fPreferencesItem->SetEnabled(true);
738 
739 	fSourceButton->SetEnabled(true);
740 	fDestButton->SetEnabled(true);
741 	fExpandButton->SetEnabled(true);
742 	fStatusView->SetText("");
743 }
744 
745 
746 void
747 ExpanderWindow::CloseWindowOrKeepOpen()
748 {
749 	bool expandFiles = false;
750 	fSettings.FindBool("automatically_expand_files", &expandFiles);
751 
752 	bool closeWhenDone = false;
753 	fSettings.FindBool("close_when_done", &closeWhenDone);
754 
755 	if (expandFiles || closeWhenDone)
756 		PostMessage(B_QUIT_REQUESTED);
757 }
758 
759 
760 void
761 ExpanderWindow::OpenDestFolder()
762 {
763 	bool openFolder = true;
764 	fSettings.FindBool("open_destination_folder", &openFolder);
765 
766 	if (!openFolder)
767 		return;
768 
769 	BMessage* message = new BMessage(B_REFS_RECEIVED);
770 	message->AddRef("refs", &fDestRef);
771 	BPath path(&fDestRef);
772 	BMessenger tracker("application/x-vnd.Be-TRAK");
773 	tracker.SendMessage(message);
774 }
775 
776 
777 void
778 ExpanderWindow::AutoListing()
779 {
780 	bool showContents = false;
781 	fSettings.FindBool("show_contents_listing", &showContents);
782 
783 	if (!showContents)
784 		return;
785 
786 	fShowContents->SetValue(B_CONTROL_ON);
787 	fShowContents->Invoke();
788 }
789 
790 
791 void
792 ExpanderWindow::AutoExpand()
793 {
794 	bool expandFiles = false;
795 	fSettings.FindBool("automatically_expand_files", &expandFiles);
796 
797 	if (!expandFiles) {
798 		AutoListing();
799 		return;
800 	}
801 
802 	fExpandButton->Invoke();
803 }
804