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