xref: /haiku/src/apps/expander/ExpanderWindow.cpp (revision 70449c90d95aeea01b25f686d51da4217fe41317)
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->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
157 		alert->Go();
158 		return false;
159 	} else if (!entry.IsDirectory()) {
160 		BAlert* alert = new BAlert("destAlert",
161 			B_TRANSLATE("The destination is not a folder."),
162 			B_TRANSLATE("Cancel"), NULL, NULL,
163 			B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
164 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
165 		alert->Go();
166 		return false;
167 	} else if (entry.GetVolume(&volume) != B_OK || volume.IsReadOnly()) {
168 		BAlert* alert = new BAlert("destAlert",
169 			B_TRANSLATE("The destination is read only."),
170 			B_TRANSLATE("Cancel"), NULL, NULL, B_WIDTH_AS_USUAL,
171 			B_EVEN_SPACING,	B_WARNING_ALERT);
172 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
173 		alert->Go();
174 		return false;
175 	} else {
176 		entry.GetRef(&fDestRef);
177 		return true;
178 	}
179 }
180 
181 
182 void
183 ExpanderWindow::MessageReceived(BMessage* msg)
184 {
185 	switch (msg->what) {
186 		case MSG_SOURCE:
187 		{
188 			BEntry entry(fSourceText->Text(), true);
189 			entry_ref srcRef;
190 			if (entry.Exists() && entry.IsDirectory())
191 				entry.GetRef(&srcRef);
192 			if (!fSourcePanel) {
193 				BMessenger messenger(this);
194 				fSourcePanel = new BFilePanel(B_OPEN_PANEL, &messenger, &srcRef,
195 					B_FILE_NODE, false, NULL, new RuleRefFilter(fRules), true);
196 				(fSourcePanel->Window())->SetTitle(
197 					B_TRANSLATE("Expander: Open"));
198 			} else
199 				fSourcePanel->SetPanelDirectory(&srcRef);
200 			fSourcePanel->Show();
201 			break;
202 		}
203 
204 		case MSG_DEST:
205 		{
206 			BEntry entry(fDestText->Text(), true);
207 			entry_ref destRef;
208 			if (entry.Exists() && entry.IsDirectory())
209 				entry.GetRef(&destRef);
210 			if (!fDestPanel) {
211 				BMessenger messenger(this);
212 				fDestPanel = new DirectoryFilePanel(B_OPEN_PANEL, &messenger,
213 					&destRef, B_DIRECTORY_NODE, false, NULL,
214 					new DirectoryRefFilter(), true);
215 			} else
216 				fDestPanel->SetPanelDirectory(&destRef);
217 			fDestPanel->Show();
218 			break;
219 		}
220 
221 		case MSG_DIRECTORY:
222 		{
223 			entry_ref ref;
224 			fDestPanel->GetPanelDirectory(&ref);
225 			fDestRef = ref;
226 			BEntry entry(&ref);
227 			BPath path(&entry);
228 			fDestText->SetText(path.Path());
229 			fDestPanel->Hide();
230 			break;
231 		}
232 
233 		case B_SIMPLE_DATA:
234 		case B_REFS_RECEIVED:
235 			RefsReceived(msg);
236 			break;
237 
238 		case MSG_EXPAND:
239 			if (!ValidateDest())
240 				break;
241 			if (!fExpandingStarted) {
242 				StartExpanding();
243 				break;
244 			}
245 			// supposed to fall through
246 		case MSG_STOP:
247 			if (fExpandingStarted) {
248 				fExpandingThread->SuspendExternalExpander();
249 				BAlert* alert = new BAlert("stopAlert",
250 					B_TRANSLATE("Are you sure you want to stop expanding this\n"
251 						"archive? The expanded items may not be complete."),
252 					B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL,
253 					B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
254 				alert->SetShortcut(0, B_ESCAPE);
255 				if (alert->Go() == 0) {
256 					fExpandingThread->ResumeExternalExpander();
257 					StopExpanding();
258 				} else
259 					fExpandingThread->ResumeExternalExpander();
260 			}
261 			break;
262 
263 		case MSG_SHOW:
264 			fShowContents->SetValue(fShowContents->Value() == B_CONTROL_OFF
265 				? B_CONTROL_ON : B_CONTROL_OFF);
266 			// supposed to fall through
267 		case MSG_SHOWCONTENTS:
268 			// change menu item label
269 			fShowItem->SetLabel(fShowContents->Value() == B_CONTROL_OFF
270 				? B_TRANSLATE("Show contents") : B_TRANSLATE("Hide contents"));
271 
272 			if (fShowContents->Value() == B_CONTROL_OFF) {
273 				if (fListingStarted)
274 					StopListing();
275 
276 				_UpdateWindowSize(false);
277 			} else
278 				StartListing();
279 			break;
280 
281 		case MSG_SOURCETEXT:
282 		{
283 			BEntry entry(fSourceText->Text(), true);
284 			if (!entry.Exists()) {
285 				BAlert* alert = new BAlert("srcAlert",
286 					B_TRANSLATE("The file doesn't exist"),
287 					B_TRANSLATE("Cancel"), NULL, NULL,
288 					B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
289 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
290 				alert->Go();
291 				break;
292 			}
293 
294 			entry_ref ref;
295 			entry.GetRef(&ref);
296 			ExpanderRule* rule = fRules.MatchingRule(&ref);
297 			if (rule) {
298 				fSourceChanged = true;
299 				fSourceRef = ref;
300 				fShowContents->SetEnabled(true);
301 				fExpandButton->SetEnabled(true);
302 				fExpandItem->SetEnabled(true);
303 				fShowItem->SetEnabled(true);
304 				break;
305 			}
306 
307 			BString string = "The file : ";
308 			string += fSourceText->Text();
309 			string += B_TRANSLATE_MARK(" is not supported");
310 			BAlert* alert = new BAlert("srcAlert", string.String(),
311 				B_TRANSLATE("Cancel"),
312 				NULL, NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT);
313 			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
314 			alert->Go();
315 
316 			fShowContents->SetEnabled(false);
317 			fExpandButton->SetEnabled(false);
318 			fExpandItem->SetEnabled(false);
319 			fShowItem->SetEnabled(false);
320 		}
321 		break;
322 
323 		case MSG_DESTTEXT:
324 			ValidateDest();
325 			break;
326 
327 		case MSG_PREFERENCES:
328 			if (!fPreferences)
329 				fPreferences = new ExpanderPreferences(&fSettings);
330 			fPreferences->Show();
331 			break;
332 
333 		case 'outp':
334 			if (!fExpandingStarted && fListingStarted) {
335 				BString string;
336 				int32 i = 0;
337 				while (msg->FindString("output", i++, &string) == B_OK) {
338 					float length = fListingText->StringWidth(string.String());
339 
340 					if (length > fLongestLine)
341 						fLongestLine = length;
342 
343 					fListingText->Insert(string.String());
344 				}
345 				fListingText->ScrollToSelection();
346 			} else if (fExpandingStarted) {
347 				BString string;
348 				int32 i = 0;
349 				while (msg->FindString("output", i++, &string) == B_OK) {
350 					if (strstr(string.String(), "Enter password") != NULL) {
351 						fExpandingThread->SuspendExternalExpander();
352 						BString password;
353 						PasswordAlert* alert =
354 							new PasswordAlert("passwordAlert", string);
355 						alert->Go(password);
356 						fExpandingThread->ResumeExternalExpander();
357 						fExpandingThread->PushInput(password);
358 					}
359 				}
360 			}
361 			break;
362 
363 		case 'errp':
364 		{
365 			BString string;
366 			if (msg->FindString("error", &string) == B_OK
367 				&& fExpandingStarted) {
368 				fExpandingThread->SuspendExternalExpander();
369 				if (strstr(string.String(), "password") != NULL) {
370 					BString password;
371 					PasswordAlert* alert = new PasswordAlert("passwordAlert",
372 						string);
373 					alert->Go(password);
374 					fExpandingThread->ResumeExternalExpander();
375 					fExpandingThread->PushInput(password);
376 				} else {
377 					BAlert* alert = new BAlert("stopAlert", string,
378 						B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL,
379 						B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
380 					alert->SetShortcut(0, B_ESCAPE);
381 					if (alert->Go() == 0) {
382 						fExpandingThread->ResumeExternalExpander();
383 						StopExpanding();
384 					} else
385 						fExpandingThread->ResumeExternalExpander();
386 				}
387 			}
388 			break;
389 		}
390 		case 'exit':
391 			// thread has finished		(finished, quit, killed, we don't know)
392 			// reset window state
393 			if (fExpandingStarted) {
394 				fStatusView->SetText(B_TRANSLATE("File expanded"));
395 				StopExpanding();
396 				OpenDestFolder();
397 				CloseWindowOrKeepOpen();
398 			} else if (fListingStarted) {
399 				fSourceChanged = false;
400 				StopListing();
401 				_ExpandListingText();
402 			} else
403 				fStatusView->SetText("");
404 			break;
405 
406 		case 'exrr':	// thread has finished
407 			// reset window state
408 
409 			fStatusView->SetText(B_TRANSLATE("Error when expanding archive"));
410 			CloseWindowOrKeepOpen();
411 			break;
412 
413 		default:
414 			BWindow::MessageReceived(msg);
415 			break;
416 	}
417 }
418 
419 
420 bool
421 ExpanderWindow::CanQuit()
422 {
423 	if ((fSourcePanel && fSourcePanel->IsShowing())
424 		|| (fDestPanel && fDestPanel->IsShowing()))
425 		return false;
426 
427 	if (fExpandingStarted) {
428 		fExpandingThread->SuspendExternalExpander();
429 		BAlert* alert = new BAlert("stopAlert",
430 			B_TRANSLATE("Are you sure you want to stop expanding this\n"
431 				"archive? The expanded items may not be complete."),
432 			B_TRANSLATE("Stop"), B_TRANSLATE("Continue"), NULL,
433 			B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
434 			alert->SetShortcut(0, B_ESCAPE);
435 		if (alert->Go() == 0) {
436 			fExpandingThread->ResumeExternalExpander();
437 			StopExpanding();
438 		} else {
439 			fExpandingThread->ResumeExternalExpander();
440 			return false;
441 		}
442 	}
443 	return true;
444 }
445 
446 
447 bool
448 ExpanderWindow::QuitRequested()
449 {
450 	if (!CanQuit())
451 		return false;
452 
453 	if (fListingStarted)
454 		StopListing();
455 
456 	be_app->PostMessage(B_QUIT_REQUESTED);
457 	fSettings.ReplacePoint("window_position", Frame().LeftTop());
458 	((ExpanderApp*)(be_app))->UpdateSettingsFrom(&fSettings);
459 	return true;
460 }
461 
462 
463 void
464 ExpanderWindow::RefsReceived(BMessage* msg)
465 {
466 	entry_ref ref;
467 	int32 i = 0;
468 	int8 destination_folder = 0x63;
469 	fSettings.FindInt8("destination_folder", &destination_folder);
470 
471 	while (msg->FindRef("refs", i++, &ref) == B_OK) {
472 		BEntry entry(&ref, true);
473 		BPath path(&entry);
474 		BNode node(&entry);
475 
476 		if (node.IsFile()) {
477 			fSourceChanged = true;
478 			fSourceRef = ref;
479 			fSourceText->SetText(path.Path());
480 			if (destination_folder == 0x63) {
481 				BPath parent;
482 				path.GetParent(&parent);
483 				fDestText->SetText(parent.Path());
484 				get_ref_for_path(parent.Path(), &fDestRef);
485 			} else if (destination_folder == 0x65) {
486 				fSettings.FindRef("destination_folder_use", &fDestRef);
487 				BEntry dEntry(&fDestRef, true);
488 				BPath dPath(&dEntry);
489 				fDestText->SetText(dPath.Path());
490 			}
491 
492 			BEntry dEntry(&fDestRef, true);
493 			if (dEntry.Exists()) {
494 				fExpandButton->SetEnabled(true);
495 				fExpandItem->SetEnabled(true);
496 			}
497 
498 			if (fShowContents->Value() == B_CONTROL_ON) {
499 				StopListing();
500 				StartListing();
501 			} else {
502 				fShowContents->SetEnabled(true);
503 				fShowItem->SetEnabled(true);
504 			}
505 
506 			bool fromApp;
507 			if (msg->FindBool("fromApp", &fromApp) == B_OK) {
508 				AutoExpand();
509 			} else
510 				AutoListing();
511 		} else if (node.IsDirectory()) {
512 			fDestRef = ref;
513 			fDestText->SetText(path.Path());
514 		}
515 	}
516 }
517 
518 
519 #undef B_TRANSLATION_CONTEXT
520 #define B_TRANSLATION_CONTEXT "ExpanderMenu"
521 
522 void
523 ExpanderWindow::_AddMenuBar(BLayout* layout)
524 {
525 	fBar = new BMenuBar("menu_bar", B_ITEMS_IN_ROW, B_INVALIDATE_AFTER_LAYOUT);
526 	BMenu* menu = new BMenu(B_TRANSLATE("File"));
527 	menu->AddItem(fSourceItem = new BMenuItem(B_TRANSLATE("Set source…"),
528 		new BMessage(MSG_SOURCE), 'O'));
529 	menu->AddItem(fDestItem = new BMenuItem(B_TRANSLATE("Set destination…"),
530 		new BMessage(MSG_DEST), 'D'));
531 	menu->AddSeparatorItem();
532 	menu->AddItem(fExpandItem = new BMenuItem(B_TRANSLATE("Expand"),
533 		new BMessage(MSG_EXPAND), 'E'));
534 	fExpandItem->SetEnabled(false);
535 	menu->AddItem(fShowItem = new BMenuItem(B_TRANSLATE("Show contents"),
536 		new BMessage(MSG_SHOW), 'L'));
537 	fShowItem->SetEnabled(false);
538 	menu->AddSeparatorItem();
539 	menu->AddItem(fStopItem = new BMenuItem(B_TRANSLATE("Stop"),
540 		new BMessage(MSG_STOP), 'K'));
541 	fStopItem->SetEnabled(false);
542 	menu->AddSeparatorItem();
543 	menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
544 		new BMessage(B_QUIT_REQUESTED), 'W'));
545 	fBar->AddItem(menu);
546 
547 	menu = new BMenu(B_TRANSLATE("Settings"));
548 	menu->AddItem(fPreferencesItem = new BMenuItem(B_TRANSLATE("Settings…"),
549 		new BMessage(MSG_PREFERENCES), 'S'));
550 	fBar->AddItem(menu);
551 	layout->AddView(fBar);
552 }
553 
554 
555 #undef B_TRANSLATION_CONTEXT
556 #define B_TRANSLATION_CONTEXT "ExpanderWindow"
557 
558 void
559 ExpanderWindow::StartExpanding()
560 {
561 	ExpanderRule* rule = fRules.MatchingRule(&fSourceRef);
562 	if (!rule)
563 		return;
564 
565 	BEntry destEntry(fDestText->Text(), true);
566 	if (!destEntry.Exists()) {
567 		BAlert* alert = new BAlert("destAlert",
568 		B_TRANSLATE("The folder was either moved, renamed or not\nsupported."),
569 		B_TRANSLATE("Cancel"), NULL, NULL,
570 			B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT);
571 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
572 		alert->Go();
573 		return;
574 	}
575 
576 	BMessage message;
577 	message.AddString("cmd", rule->ExpandCmd());
578 	message.AddRef("srcRef", &fSourceRef);
579 	message.AddRef("destRef", &fDestRef);
580 
581 	fExpandButton->SetLabel(B_TRANSLATE("Stop"));
582 	fSourceButton->SetEnabled(false);
583 	fDestButton->SetEnabled(false);
584 	fShowContents->SetEnabled(false);
585 	fSourceItem->SetEnabled(false);
586 	fDestItem->SetEnabled(false);
587 	fExpandItem->SetEnabled(false);
588 	fShowItem->SetEnabled(false);
589 	fStopItem->SetEnabled(true);
590 	fPreferencesItem->SetEnabled(false);
591 
592 	BEntry entry(&fSourceRef);
593 	BPath path(&entry);
594 	BString text(B_TRANSLATE("Expanding '%s'"));
595 	text.ReplaceFirst("%s", path.Leaf());
596 	fStatusView->SetText(text.String());
597 
598 	fExpandingThread = new ExpanderThread(&message, new BMessenger(this));
599 	fExpandingThread->Start();
600 
601 	fExpandingStarted = true;
602 }
603 
604 
605 void
606 ExpanderWindow::StopExpanding(void)
607 {
608 	if (fExpandingThread) {
609 		fExpandingThread->InterruptExternalExpander();
610 		fExpandingThread = NULL;
611 	}
612 
613 	fExpandingStarted = false;
614 
615 	fExpandButton->SetLabel(B_TRANSLATE("Expand"));
616 	fSourceButton->SetEnabled(true);
617 	fDestButton->SetEnabled(true);
618 	fShowContents->SetEnabled(true);
619 	fSourceItem->SetEnabled(true);
620 	fDestItem->SetEnabled(true);
621 	fExpandItem->SetEnabled(true);
622 	fShowItem->SetEnabled(true);
623 	fStopItem->SetEnabled(false);
624 	fPreferencesItem->SetEnabled(true);
625 }
626 
627 
628 void
629 ExpanderWindow::_ExpandListingText()
630 {
631 	float delta = fLongestLine - fListingText->Frame().Width();
632 
633 	if (delta > 0) {
634 		BScreen screen;
635 		BRect screenFrame = screen.Frame();
636 
637 		if (Frame().right + delta > screenFrame.right)
638 			delta = screenFrame.right - Frame().right - 4.0f;
639 
640 		ResizeBy(delta, 0.0f);
641 	}
642 
643 	float minWidth, maxWidth, minHeight, maxHeight;
644 	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
645 
646 	if (minWidth < Frame().Width() + delta) {
647 		// set the Zoom limit as the minimal required size
648 		SetZoomLimits(Frame().Width() + delta,
649 			min_c(fSizeLimit + fListingText->TextRect().Height()
650 				+ fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f,
651 				maxHeight));
652 	} else {
653 		// set the zoom limit based on minimal window size allowed
654 		SetZoomLimits(minWidth,
655 			min_c(fSizeLimit + fListingText->TextRect().Height()
656 				+ fLineHeight + B_H_SCROLL_BAR_HEIGHT + 1.0f,
657 				maxHeight));
658 	}
659 }
660 
661 
662 void
663 ExpanderWindow::_UpdateWindowSize(bool showContents)
664 {
665 	float minWidth, maxWidth, minHeight, maxHeight;
666 	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
667 
668 	float bottom = fSizeLimit;
669 
670 	if (showContents) {
671 		if (fPreviousHeight < 0.0) {
672 			BFont font;
673 			font_height fontHeight;
674 			fListingText->GetFont(&font);
675 			font.GetHeight(&fontHeight);
676 			fLineHeight = ceilf(fontHeight.ascent + fontHeight.descent
677 				+ fontHeight.leading);
678 			fPreviousHeight = bottom + 10.0 * fLineHeight;
679 		}
680 		minHeight = bottom + 5.0 * fLineHeight;
681 		maxHeight = 32767.0;
682 
683 		bottom = max_c(fPreviousHeight, minHeight);
684 	} else {
685 		minHeight = fSizeLimit;
686 		maxHeight = fSizeLimit;
687 		fPreviousHeight = Frame().Height();
688 	}
689 
690 	SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
691 	ResizeTo(Frame().Width(), bottom);
692 }
693 
694 
695 void
696 ExpanderWindow::StartListing()
697 {
698 	_UpdateWindowSize(true);
699 
700 	if (!fSourceChanged)
701 		return;
702 
703 	fPreviousHeight = -1.0;
704 
705 	fLongestLine = 0.0f;
706 
707 	ExpanderRule* rule = fRules.MatchingRule(&fSourceRef);
708 	if (!rule)
709 		return;
710 
711 	BMessage message;
712 	message.AddString("cmd", rule->ListingCmd());
713 	message.AddRef("srcRef", &fSourceRef);
714 
715 	fShowContents->SetEnabled(true);
716 	fSourceItem->SetEnabled(false);
717 	fDestItem->SetEnabled(false);
718 	fExpandItem->SetEnabled(false);
719 	fShowItem->SetEnabled(true);
720 	fShowItem->SetLabel(B_TRANSLATE("Hide contents"));
721 	fStopItem->SetEnabled(false);
722 	fPreferencesItem->SetEnabled(false);
723 
724 	fSourceButton->SetEnabled(false);
725 	fDestButton->SetEnabled(false);
726 	fExpandButton->SetEnabled(false);
727 
728 	BEntry entry(&fSourceRef);
729 	BPath path(&entry);
730 	BString text(B_TRANSLATE("Creating listing for '%s'"));
731 	text.ReplaceFirst("%s", path.Leaf());
732 	fStatusView->SetText(text.String());
733 	fListingText->SetText("");
734 
735 	fListingThread = new ExpanderThread(&message, new BMessenger(this));
736 	fListingThread->Start();
737 
738 	fListingStarted = true;
739 }
740 
741 
742 void
743 ExpanderWindow::StopListing(void)
744 {
745 	if (fListingThread) {
746 		fListingThread->InterruptExternalExpander();
747 		fListingThread = NULL;
748 	}
749 
750 	fListingStarted = false;
751 
752 	fShowContents->SetEnabled(true);
753 	fSourceItem->SetEnabled(true);
754 	fDestItem->SetEnabled(true);
755 	fExpandItem->SetEnabled(true);
756 	fShowItem->SetEnabled(true);
757 	fStopItem->SetEnabled(false);
758 	fPreferencesItem->SetEnabled(true);
759 
760 	fSourceButton->SetEnabled(true);
761 	fDestButton->SetEnabled(true);
762 	fExpandButton->SetEnabled(true);
763 	fStatusView->SetText("");
764 }
765 
766 
767 void
768 ExpanderWindow::CloseWindowOrKeepOpen()
769 {
770 	bool expandFiles = false;
771 	fSettings.FindBool("automatically_expand_files", &expandFiles);
772 
773 	bool closeWhenDone = false;
774 	fSettings.FindBool("close_when_done", &closeWhenDone);
775 
776 	if (expandFiles || closeWhenDone)
777 		PostMessage(B_QUIT_REQUESTED);
778 }
779 
780 
781 void
782 ExpanderWindow::OpenDestFolder()
783 {
784 	bool openFolder = true;
785 	fSettings.FindBool("open_destination_folder", &openFolder);
786 
787 	if (!openFolder)
788 		return;
789 
790 	BMessage* message = new BMessage(B_REFS_RECEIVED);
791 	message->AddRef("refs", &fDestRef);
792 	BPath path(&fDestRef);
793 	BMessenger tracker("application/x-vnd.Be-TRAK");
794 	tracker.SendMessage(message);
795 }
796 
797 
798 void
799 ExpanderWindow::AutoListing()
800 {
801 	bool showContents = false;
802 	fSettings.FindBool("show_contents_listing", &showContents);
803 
804 	if (!showContents)
805 		return;
806 
807 	fShowContents->SetValue(B_CONTROL_ON);
808 	fShowContents->Invoke();
809 }
810 
811 
812 void
813 ExpanderWindow::AutoExpand()
814 {
815 	bool expandFiles = false;
816 	fSettings.FindBool("automatically_expand_files", &expandFiles);
817 
818 	if (!expandFiles) {
819 		AutoListing();
820 		return;
821 	}
822 
823 	fExpandButton->Invoke();
824 }
825