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