xref: /haiku/src/apps/expander/ExpanderWindow.cpp (revision b62aa2381dd4578cf419e8aa823722192c89184a)
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:
StatusView()51 	StatusView()
52 		:
53 		BStringView(NULL, "")
54 	{
55 	}
56 
~StatusView()57 	virtual ~StatusView()
58 	{
59 	}
60 
SetStatus(const BString & text)61 	void SetStatus(const BString &text)
62 	{
63 		fStatus = text;
64 		Invalidate();
65 	}
66 
Draw(BRect updateRect)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 
ExpanderWindow(BRect frame,const entry_ref * ref,BMessage * settings)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 			.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 
~ExpanderWindow()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
ValidateDest()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("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 
246 void
MessageReceived(BMessage * message)247 ExpanderWindow::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 
500 bool
CanQuit()501 ExpanderWindow::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 
530 bool
QuitRequested()531 ExpanderWindow::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 
547 void
RefsReceived(BMessage * message)548 ExpanderWindow::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 
606 void
_CreateMenuBar()607 ExpanderWindow::_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 
644 void
StartExpanding()645 ExpanderWindow::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 
691 void
StopExpanding(void)692 ExpanderWindow::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 
714 void
_ExpandListingText()715 ExpanderWindow::_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 
748 void
_UpdateWindowSize(bool showContents)749 ExpanderWindow::_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 
781 void
StartListing()782 ExpanderWindow::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 
829 void
StopListing(void)830 ExpanderWindow::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 
855 void
CloseWindowOrKeepOpen()856 ExpanderWindow::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 
869 void
OpenDestFolder()870 ExpanderWindow::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 
886 void
AutoListing()887 ExpanderWindow::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 
900 void
AutoExpand()901 ExpanderWindow::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