/*
* Copyright 2003-2009, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Jonas Sundström, jonas@kirilla.com
*/
#include "ZipOMaticWindow.h"
#include <stdio.h>
#include <stdlib.h>
#include <Alert.h>
#include <Application.h>
#include <Catalog.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <GroupLayout.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <Path.h>
#include <Roster.h>
#include <Screen.h>
#include <SeparatorView.h>
#include <String.h>
#include <StringFormat.h>
#include "ZipOMatic.h"
#include "ZipOMaticActivity.h"
#include "ZipOMaticMisc.h"
#include "ZipperThread.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "file:ZipOMaticWindow.cpp"
ZippoWindow::ZippoWindow(BList windowList, bool keepOpen)
:
BWindow(BRect(0, 0, 0, 0), "Zip-O-Matic", B_TITLED_WINDOW,
B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE),
fWindowList(windowList),
fThread(NULL),
fKeepOpen(keepOpen),
fZippingWasStopped(false),
fFileCount(0),
fWindowInvoker(new BInvoker(new BMessage(ZIPPO_QUIT_OR_CONTINUE), NULL,
this))
{
fActivityView = new Activity("activity");
fActivityView->SetExplicitMinSize(BSize(171, 17));
fArchiveNameView = new BStringView("archive_text", "");
fArchiveNameView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
B_ALIGN_VERTICAL_UNSET));
fZipOutputView = new BStringView("output_text",
B_TRANSLATE("Drop files here."));
fZipOutputView->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
B_ALIGN_VERTICAL_UNSET));
fStopButton = new BButton("stop", B_TRANSLATE("Stop"),
new BMessage(B_QUIT_REQUESTED));
fStopButton->SetEnabled(false);
fStopButton->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
B_ALIGN_VERTICAL_UNSET));
BSeparatorView* separator = new BSeparatorView(B_HORIZONTAL);
BLayoutBuilder::Group<>(this)
.AddGroup(B_VERTICAL, 10)
.Add(fActivityView)
.Add(fArchiveNameView)
.Add(fZipOutputView)
.Add(separator)
.Add(fStopButton)
.SetInsets(14, 14, 14, 14)
.End()
.End();
_FindBestPlacement();
}
ZippoWindow::~ZippoWindow()
{
delete fWindowInvoker;
}
void
ZippoWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_REFS_RECEIVED:
case B_SIMPLE_DATA:
if (IsZipping()) {
message->what = B_REFS_RECEIVED;
be_app_messenger.SendMessage(message);
} else {
_StartZipping(message);
}
break;
case ZIPPO_THREAD_EXIT:
fThread = NULL;
fActivityView->Stop();
fStopButton->SetEnabled(false);
fArchiveNameView->SetText(" ");
if (fZippingWasStopped)
fZipOutputView->SetText(B_TRANSLATE("Stopped"));
else
fZipOutputView->SetText(B_TRANSLATE("Archive created OK"));
_CloseWindowOrKeepOpen();
break;
case ZIPPO_THREAD_EXIT_ERROR:
// TODO: figure out why this case does not happen when it should
fThread = NULL;
fActivityView->Stop();
fStopButton->SetEnabled(false);
fArchiveNameView->SetText("");
fZipOutputView->SetText(B_TRANSLATE("Error creating archive"));
break;
case ZIPPO_TASK_DESCRIPTION:
{
BString filename;
if (message->FindString("archive_filename", &filename) == B_OK) {
fArchiveName = filename;
BString temp(B_TRANSLATE("Creating archive: %s"));
temp.ReplaceFirst("%s", filename.String());
fArchiveNameView->SetText(temp.String());
}
break;
}
case ZIPPO_LINE_OF_STDOUT:
{
BString string;
if (message->FindString("zip_output", &string) == B_OK) {
if (string.FindFirst("Adding: ") == 0) {
// This is a workaround for the current window resizing
// behavior as the window resizes for each line of output.
// Instead of showing the true output of /bin/zip
// we display a file count and whether the archive is
// being created (added to) or if we're updating an
// already existing archive.
static BStringFormat format(B_TRANSLATE("{0, plural, "
"one{# file added.} other{# files added.}}"));
BString output;
format.Format(output, fFileCount);
fFileCount++;
fZipOutputView->SetText(output.String());
} else {
fZipOutputView->SetText(string.String());
}
}
break;
}
case ZIPPO_QUIT_OR_CONTINUE:
{
int32 which_button = -1;
if (message->FindInt32("which", &which_button) == B_OK) {
if (which_button == 0) {
StopZipping();
} else {
if (fThread != NULL)
fThread->ResumeExternalZip();
fActivityView->Start();
}
}
break;
}
default:
BWindow::MessageReceived(message);
break;
}
}
bool
ZippoWindow::QuitRequested()
{
if (!IsZipping()) {
BMessage message(ZIPPO_WINDOW_QUIT);
message.AddRect("frame", Frame());
be_app_messenger.SendMessage(&message);
return true;
} else {
fThread->SuspendExternalZip();
fActivityView->Pause();
BString message;
message << B_TRANSLATE("Are you sure you want to stop creating this "
"archive?");
message << "\n\n";
message << B_TRANSLATE("Filename: %s");
message << "\n";
message.ReplaceFirst("%s", fArchiveName.String());
BAlert* alert = new BAlert(NULL, message.String(), B_TRANSLATE("Stop"),
B_TRANSLATE("Continue"), NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->Go(fWindowInvoker);
return false;
}
}
void
ZippoWindow::_StartZipping(BMessage* message)
{
fStopButton->SetEnabled(true);
fActivityView->Start();
fFileCount = 0;
fThread = new ZipperThread(message, this);
fThread->Start();
fZippingWasStopped = false;
}
void
ZippoWindow::StopZipping()
{
fZippingWasStopped = true;
fStopButton->SetEnabled(false);
fActivityView->Stop();
fThread->InterruptExternalZip();
fThread->Quit();
status_t status = B_OK;
fThread->WaitForThread(&status);
fThread = NULL;
fArchiveNameView->SetText(" ");
fZipOutputView->SetText(B_TRANSLATE("Stopped"));
_CloseWindowOrKeepOpen();
}
bool
ZippoWindow::IsZipping()
{
if (fThread == NULL)
return false;
else
return true;
}
void
ZippoWindow::_CloseWindowOrKeepOpen()
{
if (!fKeepOpen)
PostMessage(B_QUIT_REQUESTED);
}
void
ZippoWindow::_FindBestPlacement()
{
CenterOnScreen();
BScreen screen;
BRect centeredRect = Frame();
BRect tryRect = centeredRect;
BList tryRectList;
if (!screen.Frame().Contains(centeredRect))
return;
// build a list of possible locations
tryRectList.AddItem(new BRect(centeredRect));
// up and left
direction primaryDirection = up;
while (true) {
_OffsetRect(&tryRect, primaryDirection);
if (!screen.Frame().Contains(tryRect))
_OffscreenBounceBack(&tryRect, &primaryDirection, left);
if (!screen.Frame().Contains(tryRect))
break;
tryRectList.AddItem(new BRect(tryRect));
}
// down and right
primaryDirection = down;
tryRect = centeredRect;
while (true) {
_OffsetRect(&tryRect, primaryDirection);
if (!screen.Frame().Contains(tryRect))
_OffscreenBounceBack(&tryRect, &primaryDirection, right);
if (!screen.Frame().Contains(tryRect))
break;
tryRectList.AddItem(new BRect(tryRect));
}
// remove rects that overlap an existing window
for (int32 i = 0;; i++) {
BWindow* win = static_cast<BWindow*>(fWindowList.ItemAt(i));
if (win == NULL)
break;
ZippoWindow* window = dynamic_cast<ZippoWindow*>(win);
if (window == NULL)
continue;
if (window == this)
continue;
if (window->Lock()) {
BRect frame = window->Frame();
for (int32 m = 0;; m++) {
BRect* rect = static_cast<BRect*>(tryRectList.ItemAt(m));
if (rect == NULL)
break;
if (frame.Intersects(*rect)) {
tryRectList.RemoveItem(m);
delete rect;
m--;
}
}
window->Unlock();
}
}
// find nearest rect
bool gotRect = false;
BRect nearestRect(0, 0, 0, 0);
while (true) {
BRect* rect = static_cast<BRect*>(tryRectList.RemoveItem((int32)0));
if (rect == NULL)
break;
nearestRect = _NearestRect(centeredRect, nearestRect, *rect);
gotRect = true;
delete rect;
}
if (gotRect)
MoveTo(nearestRect.LeftTop());
}
void
ZippoWindow::_OffsetRect(BRect* rect, direction whereTo)
{
float width = rect->Width();
float height = rect->Height();
switch (whereTo) {
case up:
rect->OffsetBy(0, -(height * 1.5));
break;
case down:
rect->OffsetBy(0, height * 1.5);
break;
case left:
rect->OffsetBy(-(width * 1.5), 0);
break;
case right:
rect->OffsetBy(width * 1.5, 0);
break;
}
}
void
ZippoWindow::_OffscreenBounceBack(BRect* rect, direction* primaryDirection,
direction secondaryDirection)
{
if (*primaryDirection == up) {
*primaryDirection = down;
} else {
*primaryDirection = up;
}
_OffsetRect(rect, *primaryDirection);
_OffsetRect(rect, secondaryDirection);
}
BRect
ZippoWindow::_NearestRect(BRect goalRect, BRect a, BRect b)
{
double aSum = fabs(goalRect.left - a.left) + fabs(goalRect.top - a.top);
double bSum = fabs(goalRect.left - b.left) + fabs(goalRect.top - b.top);
if (aSum < bSum)
return a;
else
return b;
}
↑ V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.