152419Sjulian/*
252419Sjulian * Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
352419Sjulian * Distributed under the terms of the MIT License.
452419Sjulian *
552419Sjulian * Authors:
652419Sjulian *		Jonas Sundstr��m, jonas@kirilla.com
770784Sjulian */
852419Sjulian
952419Sjulian
1052419Sjulian#include "ZipOMaticWindow.h"
1152419Sjulian
1252419Sjulian#include <stdio.h>
1352419Sjulian#include <stdlib.h>
1452419Sjulian
1552419Sjulian#include <Alert.h>
1652419Sjulian#include <Application.h>
1752419Sjulian#include <Catalog.h>
1870784Sjulian#include <Directory.h>
1952419Sjulian#include <File.h>
2052419Sjulian#include <FindDirectory.h>
2152419Sjulian#include <GroupLayout.h>
2252419Sjulian#include <LayoutBuilder.h>
2352419Sjulian#include <Locale.h>
2452419Sjulian#include <Path.h>
2552419Sjulian#include <Roster.h>
2652419Sjulian#include <Screen.h>
2752419Sjulian#include <SeparatorView.h>
2852419Sjulian#include <String.h>
2952419Sjulian#include <StringFormat.h>
3052419Sjulian
3152419Sjulian#include "ZipOMatic.h"
3252419Sjulian#include "ZipOMaticActivity.h"
3352419Sjulian#include "ZipOMaticMisc.h"
3452419Sjulian#include "ZipperThread.h"
3552419Sjulian
3652419Sjulian
3767506Sjulian#undef B_TRANSLATION_CONTEXT
3852419Sjulian#define B_TRANSLATION_CONTEXT "file:ZipOMaticWindow.cpp"
3952419Sjulian
4052752Sjulian
4152419SjulianZippoWindow::ZippoWindow(BList windowList, bool keepOpen)
4252419Sjulian	:
4352419Sjulian	BWindow(BRect(0, 0, 0, 0), "Zip-O-Matic", B_TITLED_WINDOW,
4452419Sjulian		B_NOT_RESIZABLE	| B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE),
4552419Sjulian	fWindowList(windowList),
4652419Sjulian	fThread(NULL),
4752419Sjulian	fKeepOpen(keepOpen),
4852419Sjulian	fZippingWasStopped(false),
4952419Sjulian	fFileCount(0),
5052419Sjulian	fWindowInvoker(new BInvoker(new BMessage(ZIPPO_QUIT_OR_CONTINUE), NULL,
5152419Sjulian		this))
5252419Sjulian{
5352419Sjulian	fActivityView = new Activity("activity");
5452419Sjulian	fActivityView->SetExplicitMinSize(BSize(171, 17));
5595759Stanimura
5652419Sjulian	fArchiveNameView = new BStringView("archive_text", "");
5752419Sjulian	fArchiveNameView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
5852419Sjulian		B_ALIGN_VERTICAL_UNSET));
5995759Stanimura
6095759Stanimura	fZipOutputView = new BStringView("output_text",
6152419Sjulian		B_TRANSLATE("Drop files here."));
6252419Sjulian	fZipOutputView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
6395759Stanimura		B_ALIGN_VERTICAL_UNSET));
6452419Sjulian
6595759Stanimura	fStopButton = new BButton("stop", B_TRANSLATE("Stop"),
6652419Sjulian		new BMessage(B_QUIT_REQUESTED));
6752419Sjulian	fStopButton->SetEnabled(false);
6852419Sjulian	fStopButton->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
6952419Sjulian		B_ALIGN_VERTICAL_UNSET));
7052419Sjulian
7152919Sjulian	BSeparatorView* separator = new BSeparatorView(B_HORIZONTAL);
7252419Sjulian
7352419Sjulian	BLayoutBuilder::Group<>(this)
7470870Sjulian		.AddGroup(B_VERTICAL, 10)
7570870Sjulian			.Add(fActivityView)
7670870Sjulian			.Add(fArchiveNameView)
7770870Sjulian			.Add(fZipOutputView)
7870870Sjulian			.Add(separator)
7970870Sjulian			.Add(fStopButton)
8070870Sjulian			.SetInsets(14, 14, 14, 14)
8170870Sjulian			.End()
8252419Sjulian		.End();
8352419Sjulian
8452419Sjulian	_FindBestPlacement();
8552419Sjulian}
8652419Sjulian
8752419Sjulian
8852419SjulianZippoWindow::~ZippoWindow()
8952419Sjulian{
9052419Sjulian	delete fWindowInvoker;
9152419Sjulian}
9252419Sjulian
9352419Sjulian
9452419Sjulianvoid
9552419SjulianZippoWindow::MessageReceived(BMessage* message)
9652419Sjulian{
9752419Sjulian	switch (message->what) {
9852419Sjulian		case B_REFS_RECEIVED:
9952419Sjulian		case B_SIMPLE_DATA:
10052419Sjulian			if (IsZipping()) {
10152419Sjulian				message->what = B_REFS_RECEIVED;
10252419Sjulian				be_app_messenger.SendMessage(message);
10352419Sjulian			} else {
10452419Sjulian				_StartZipping(message);
10552419Sjulian			}
10652419Sjulian			break;
10752419Sjulian
10852419Sjulian		case ZIPPO_THREAD_EXIT:
10952752Sjulian			fThread = NULL;
11052752Sjulian			fActivityView->Stop();
11170700Sjulian			fStopButton->SetEnabled(false);
11252752Sjulian			fArchiveNameView->SetText(" ");
11372053Sjulian			if (fZippingWasStopped)
11452752Sjulian				fZipOutputView->SetText(B_TRANSLATE("Stopped"));
11552885Sjulian			else
11652419Sjulian				fZipOutputView->SetText(B_TRANSLATE("Archive created OK"));
11752419Sjulian
11852419Sjulian			_CloseWindowOrKeepOpen();
11952419Sjulian			break;
12052419Sjulian
12152419Sjulian		case ZIPPO_THREAD_EXIT_ERROR:
12283366Sjulian			// TODO: figure out why this case does not happen when it should
12352419Sjulian			fThread = NULL;
12452419Sjulian			fActivityView->Stop();
12552419Sjulian			fStopButton->SetEnabled(false);
12652419Sjulian			fArchiveNameView->SetText("");
12752419Sjulian			fZipOutputView->SetText(B_TRANSLATE("Error creating archive"));
12852419Sjulian			break;
12952419Sjulian
13052419Sjulian		case ZIPPO_TASK_DESCRIPTION:
13152419Sjulian		{
13252419Sjulian			BString filename;
13370159Sjulian			if (message->FindString("archive_filename", &filename) == B_OK) {
13452419Sjulian				fArchiveName = filename;
13552419Sjulian				BString temp(B_TRANSLATE("Creating archive: %s"));
13652419Sjulian				temp.ReplaceFirst("%s", filename.String());
13752419Sjulian				fArchiveNameView->SetText(temp.String());
13870700Sjulian			}
13952419Sjulian			break;
14052419Sjulian		}
14172053Sjulian
14252419Sjulian		case ZIPPO_LINE_OF_STDOUT:
14353913Sarchie		{
14453913Sarchie			BString string;
14552419Sjulian			if (message->FindString("zip_output", &string) == B_OK) {
14652419Sjulian				if (string.FindFirst("Adding: ") == 0) {
14752419Sjulian
14852419Sjulian					// This is a workaround for the current window resizing
14964512Sarchie					// behavior as the window resizes for each line of output.
15052419Sjulian					// Instead of showing the true output of /bin/zip
15152419Sjulian					// we display a file count and whether the archive is
15252419Sjulian					// being created (added to) or if we're updating an
15389066Smsmith					// already existing archive.
15452419Sjulian
15553526Sjulian					static BStringFormat format(B_TRANSLATE("{0, plural, "
15652419Sjulian						"one{# file added.} other{# files added.}}"));
15752419Sjulian					BString output;
15852419Sjulian					format.Format(output, fFileCount);
15952419Sjulian
16052419Sjulian					fFileCount++;
16152419Sjulian
16252419Sjulian					fZipOutputView->SetText(output.String());
16352419Sjulian				} else {
16452419Sjulian					fZipOutputView->SetText(string.String());
16552419Sjulian				}
16652419Sjulian			}
16783366Sjulian			break;
16852419Sjulian		}
16952419Sjulian
17052419Sjulian		case ZIPPO_QUIT_OR_CONTINUE:
17193593Sjhb		{
17253530Sjulian			int32 which_button = -1;
17352419Sjulian			if (message->FindInt32("which", &which_button) == B_OK) {
17452419Sjulian				if (which_button == 0) {
17552419Sjulian					StopZipping();
17652419Sjulian				} else {
17752419Sjulian					if (fThread != NULL)
17852419Sjulian						fThread->ResumeExternalZip();
17952419Sjulian
18052419Sjulian					fActivityView->Start();
18152419Sjulian				}
18252419Sjulian			}
18352419Sjulian			break;
18452419Sjulian		}
18552419Sjulian
18652419Sjulian		default:
18752419Sjulian			BWindow::MessageReceived(message);
18852419Sjulian			break;
18952419Sjulian	}
19052419Sjulian}
19183366Sjulian
19252419Sjulian
19352419Sjulianbool
19452419SjulianZippoWindow::QuitRequested()
19570700Sjulian{
19652419Sjulian	if (!IsZipping()) {
19770700Sjulian		BMessage message(ZIPPO_WINDOW_QUIT);
19852419Sjulian		message.AddRect("frame", Frame());
19952419Sjulian		be_app_messenger.SendMessage(&message);
20052419Sjulian		return true;
20152419Sjulian	} else {
20252419Sjulian		fThread->SuspendExternalZip();
20352419Sjulian		fActivityView->Pause();
20452419Sjulian
20583366Sjulian		BString message;
20652419Sjulian		message << B_TRANSLATE("Are you sure you want to stop creating this "
20752419Sjulian			"archive?");
20852419Sjulian		message << "\n\n";
20952419Sjulian		message << B_TRANSLATE("Filename: %s");
21052419Sjulian		message << "\n";
21152419Sjulian		message.ReplaceFirst("%s", fArchiveName.String());
21252419Sjulian
21352419Sjulian		BAlert* alert = new BAlert(NULL, message.String(), B_TRANSLATE("Stop"),
21452419Sjulian			B_TRANSLATE("Continue"), NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
21552419Sjulian		alert->Go(fWindowInvoker);
21652419Sjulian
21752419Sjulian		return false;
21852419Sjulian	}
21952419Sjulian}
22052419Sjulian
22152419Sjulian
22252419Sjulianvoid
22352419SjulianZippoWindow::_StartZipping(BMessage* message)
22452419Sjulian{
22552419Sjulian	fStopButton->SetEnabled(true);
22652419Sjulian	fActivityView->Start();
22770870Sjulian	fFileCount = 0;
22852419Sjulian
22952419Sjulian	fThread = new ZipperThread(message, this);
23052419Sjulian	fThread->Start();
23152419Sjulian
23252419Sjulian	fZippingWasStopped = false;
23352419Sjulian}
23452419Sjulian
23552419Sjulian
23652419Sjulianvoid
23752419SjulianZippoWindow::StopZipping()
23852419Sjulian{
23952419Sjulian	fZippingWasStopped = true;
24052419Sjulian
24152419Sjulian	fStopButton->SetEnabled(false);
24270700Sjulian	fActivityView->Stop();
24352419Sjulian
24452419Sjulian	fThread->InterruptExternalZip();
24552419Sjulian	fThread->Quit();
24652419Sjulian
24770700Sjulian	status_t status = B_OK;
24852419Sjulian	fThread->WaitForThread(&status);
24970784Sjulian	fThread = NULL;
25070784Sjulian
25170784Sjulian	fArchiveNameView->SetText(" ");
25270784Sjulian	fZipOutputView->SetText(B_TRANSLATE("Stopped"));
25370784Sjulian
25470784Sjulian	_CloseWindowOrKeepOpen();
25570784Sjulian}
25670784Sjulian
25770784Sjulian
25870784Sjulianbool
25970784SjulianZippoWindow::IsZipping()
26070784Sjulian{
26170784Sjulian	if (fThread == NULL)
26270784Sjulian		return false;
26370784Sjulian	else
26470784Sjulian		return true;
26570784Sjulian}
26670784Sjulian
26770784Sjulian
26870784Sjulianvoid
26970784SjulianZippoWindow::_CloseWindowOrKeepOpen()
27070784Sjulian{
27170784Sjulian	if (!fKeepOpen)
27270784Sjulian		PostMessage(B_QUIT_REQUESTED);
27370784Sjulian}
27470784Sjulian
27570784Sjulian
27670784Sjulianvoid
27770784SjulianZippoWindow::_FindBestPlacement()
27870700Sjulian{
279102244Sarchie	CenterOnScreen();
28070784Sjulian
28152419Sjulian	BScreen screen;
28252419Sjulian	BRect centeredRect = Frame();
28370870Sjulian	BRect tryRect = centeredRect;
28452419Sjulian	BList tryRectList;
28552419Sjulian
28652419Sjulian	if (!screen.Frame().Contains(centeredRect))
28752419Sjulian		return;
28852419Sjulian
28952419Sjulian	// build a list of possible locations
29052419Sjulian	tryRectList.AddItem(new BRect(centeredRect));
29152419Sjulian
29283366Sjulian	// up and left
29352419Sjulian	direction primaryDirection = up;
29452419Sjulian	while (true) {
29552419Sjulian		_OffsetRect(&tryRect, primaryDirection);
29652419Sjulian
29752419Sjulian		if (!screen.Frame().Contains(tryRect))
29852419Sjulian			_OffscreenBounceBack(&tryRect, &primaryDirection, left);
29952419Sjulian
30052419Sjulian		if (!screen.Frame().Contains(tryRect))
30152419Sjulian			break;
30283366Sjulian
30352419Sjulian		tryRectList.AddItem(new BRect(tryRect));
30470784Sjulian	}
30570700Sjulian
30670784Sjulian	// down and right
30770700Sjulian	primaryDirection = down;
30870700Sjulian	tryRect = centeredRect;
30970700Sjulian	while (true) {
31052419Sjulian		_OffsetRect(&tryRect, primaryDirection);
31152419Sjulian
31252419Sjulian		if (!screen.Frame().Contains(tryRect))
31352419Sjulian			_OffscreenBounceBack(&tryRect, &primaryDirection, right);
31452419Sjulian
31552419Sjulian		if (!screen.Frame().Contains(tryRect))
31652419Sjulian			break;
31783366Sjulian
31852419Sjulian		tryRectList.AddItem(new BRect(tryRect));
31952419Sjulian	}
32052419Sjulian
32152419Sjulian	// remove rects that overlap an existing window
32252419Sjulian	for (int32 i = 0;; i++) {
32352419Sjulian		BWindow* win = static_cast<BWindow*>(fWindowList.ItemAt(i));
32452419Sjulian		if (win == NULL)
32552419Sjulian			break;
32652419Sjulian
32752419Sjulian		ZippoWindow* window = dynamic_cast<ZippoWindow*>(win);
32852419Sjulian		if (window == NULL)
32952419Sjulian			continue;
33052419Sjulian
33152419Sjulian		if (window == this)
33252419Sjulian			continue;
33352419Sjulian
33452419Sjulian		if (window->Lock()) {
33552419Sjulian			BRect frame = window->Frame();
33652419Sjulian			for (int32 m = 0;; m++) {
33752419Sjulian				BRect* rect = static_cast<BRect*>(tryRectList.ItemAt(m));
33852419Sjulian				if (rect == NULL)
33983366Sjulian					break;
34052419Sjulian
34152419Sjulian				if (frame.Intersects(*rect)) {
34252419Sjulian					tryRectList.RemoveItem(m);
34352419Sjulian					delete rect;
34453498Sjulian					m--;
34553498Sjulian				}
34652419Sjulian			}
34752419Sjulian			window->Unlock();
34852419Sjulian		}
34952419Sjulian	}
35052419Sjulian
35152419Sjulian	// find nearest rect
35252419Sjulian	bool gotRect = false;
35352419Sjulian	BRect nearestRect(0, 0, 0, 0);
35452419Sjulian
35553498Sjulian	while (true) {
35653498Sjulian		BRect* rect = static_cast<BRect*>(tryRectList.RemoveItem((int32)0));
35753498Sjulian		if (rect == NULL)
35853498Sjulian			break;
35953498Sjulian
36084777Sarchie		nearestRect = _NearestRect(centeredRect, nearestRect, *rect);
36184777Sarchie		gotRect = true;
36270784Sjulian		delete rect;
36353498Sjulian	}
36453498Sjulian
36553498Sjulian	if (gotRect)
36653498Sjulian		MoveTo(nearestRect.LeftTop());
36753498Sjulian}
36853498Sjulian
36953498Sjulian
37070784Sjulianvoid
37153498SjulianZippoWindow::_OffsetRect(BRect* rect, direction whereTo)
37253498Sjulian{
37353498Sjulian	float width = rect->Width();
37453498Sjulian	float height = rect->Height();
37553498Sjulian
37652419Sjulian	switch (whereTo) {
37753498Sjulian		case up:
37853498Sjulian			rect->OffsetBy(0, -(height * 1.5));
37953498Sjulian			break;
38053498Sjulian
38153498Sjulian		case down:
38253498Sjulian			rect->OffsetBy(0, height * 1.5);
38352419Sjulian			break;
38453498Sjulian
38570784Sjulian		case left:
38672053Sjulian			rect->OffsetBy(-(width * 1.5), 0);
38753498Sjulian			break;
38872053Sjulian
38953498Sjulian		case right:
39072053Sjulian			rect->OffsetBy(width * 1.5, 0);
39153498Sjulian			break;
39272053Sjulian	}
39352419Sjulian}
39452419Sjulian
39552419Sjulian
39669922Sjulianvoid
39752419SjulianZippoWindow::_OffscreenBounceBack(BRect* rect, direction* primaryDirection,
39852419Sjulian	direction secondaryDirection)
39952419Sjulian{
40052419Sjulian	if (*primaryDirection == up) {
40152419Sjulian		*primaryDirection = down;
40252419Sjulian	} else {
40352419Sjulian		*primaryDirection = up;
40452419Sjulian	}
40552419Sjulian
40652419Sjulian	_OffsetRect(rect, *primaryDirection);
40783366Sjulian	_OffsetRect(rect, secondaryDirection);
40852419Sjulian}
40952419Sjulian
41052419Sjulian
41152419SjulianBRect
41252419SjulianZippoWindow::_NearestRect(BRect goalRect, BRect a, BRect b)
41352419Sjulian{
41452419Sjulian	double aSum = fabs(goalRect.left - a.left) + fabs(goalRect.top - a.top);
41552419Sjulian	double bSum = fabs(goalRect.left - b.left) + fabs(goalRect.top - b.top);
41652419Sjulian
41752419Sjulian	if (aSum < bSum)
41852419Sjulian		return a;
41952419Sjulian	else
42052419Sjulian		return b;
42152419Sjulian}
42253098Sbrian
42353098Sbrian