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