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