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