diff --git a/Jamfile b/Jamfile index ee19cef..289e4eb 100644 --- a/Jamfile +++ b/Jamfile @@ -5,6 +5,7 @@ SubInclude TOP libs ; SubInclude TOP application ; SubInclude TOP protocols ; SubInclude TOP smileys ; +SubInclude TOP tests ; UninstallTarget $(CAYA_DIRECTORY) ; UninstallTarget $(COMMON_INCLUDE_DIRECTORY)/caya ; diff --git a/Jamrules b/Jamrules index b01cb8b..02affa7 100644 --- a/Jamrules +++ b/Jamrules @@ -45,7 +45,10 @@ LOCATE on $(HCACHEFILE) $(JCACHEFILE) = $(GENERATED_DIR) ; # Perform configuration checks include [ FDirName $(JAM_DIR) CheckRules ] ; CheckGccPlatform ; -CheckOpenSSL ; +CheckCurl ; +if ! $(HAVE_CURL) { + Echo "** Caya needs Curl" ; +} # Include jam scripts include [ FDirName $(JAM_DIR) HelperRules ] ; diff --git a/application/views/StatusView.cpp b/application/views/StatusView.cpp index 3202715..47fab20 100644 --- a/application/views/StatusView.cpp +++ b/application/views/StatusView.cpp @@ -83,6 +83,7 @@ StatusView::StatusView(const char* name) void StatusView::AttachedToWindow() { + fNickname->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); fNickname->SetTarget(this); fStatusMenu->SetTargetForItems(this); } diff --git a/build/jam/CheckRules b/build/jam/CheckRules index 6fa4110..6fbc213 100644 --- a/build/jam/CheckRules +++ b/build/jam/CheckRules @@ -48,3 +48,30 @@ rule CheckOpenSSL HAVE_OPENSSL = $(haveLibs) ; } } + +rule CheckCurl +{ + # CheckCurl ; + # Check for Curl and defined HAVE_CURL according, it also defines + # CURL_INCLUDE_DIR and CURL_LIBRARY_DIR with location of respectively + # include and library files. + + HAVE_CURL = ; + CURL_INCLUDE_DIR = ; + CURL_LIBRARY_DIR = ; + + local haveHeaders = [ Glob $(COMMON_INCLUDE_DIRECTORY)/curl : curl.h ] ; + if $(haveHeaders) { + CURL_INCLUDE_DIR = $(COMMON_INCLUDE_DIRECTORY)/curl ; + + local haveLibs = [ Glob $(COMMON_LIB_DIRECTORY) : libcurl.so ] ; + if $(haveLibs) { + CURL_LIBRARY_DIR = $(COMMON_LIB_DIRECTORY) ; + + Echo Curl Headers: $(CURL_INCLUDE_DIR) ; + Echo Curl Libs: $(CURL_LIBRARY_DIR) ; + } + + HAVE_CURL = $(haveLibs) ; + } +} diff --git a/libs/Jamfile b/libs/Jamfile index 988e09d..3a3e528 100644 --- a/libs/Jamfile +++ b/libs/Jamfile @@ -1,6 +1,7 @@ SubDir TOP libs ; # Include all the components. +SubInclude TOP libs libdownload ; SubInclude TOP libs librunview ; SubInclude TOP libs libsupport ; SubInclude TOP libs libinterface ; diff --git a/libs/libdownload/Action.h b/libs/libdownload/Action.h new file mode 100644 index 0000000..0ee3f0d --- /dev/null +++ b/libs/libdownload/Action.h @@ -0,0 +1,29 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _ACTION_H +#define _ACTION_H + +#include +#include + +#define ActionID bigtime_t + +class Action +{ +public: + Action() { fActionID = 0; } + virtual ~Action() {} + + virtual status_t Perform(BMessage* errors) = 0; + virtual BString GetDescription() = 0; + + void SetActionID(const ActionID id) { fActionID = id; } + ActionID GetActionID() const { return fActionID; } + +private: + ActionID fActionID; +}; + +#endif //_ACTION_H diff --git a/libs/libdownload/ActionDownload.cpp b/libs/libdownload/ActionDownload.cpp new file mode 100644 index 0000000..43d2731 --- /dev/null +++ b/libs/libdownload/ActionDownload.cpp @@ -0,0 +1,285 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Andrea Anzani, andrea.anzani@gmail.com + */ + +#include +#include +#include +#include + +#include "ActionDownload.h" +#include "DownloadManager.h" + + +ActionDownload::ActionDownload(BString URL, + entry_ref dest, + bool allowResume, + BLooper* target, + uint32 msg) + : + Action(), + fUrl(URL), + fDest(dest), + fTarget(target), + fMsg(msg), + fAllowResume(allowResume) +{ + file = NULL; + curl = NULL; + resuming = 0.0; + SetShouldStop(false); +} + + +ActionDownload::~ActionDownload() +{ +} + + +void +ActionDownload::SetDownloadManager(DownloadManager* downloadManager) +{ + fDownloadManager = downloadManager; +} + +status_t +ActionDownload::openFile(BMessage* errors) +{ + uint32 openMode = 0; + + if (fAllowResume) + openMode = B_WRITE_ONLY | B_CREATE_FILE | B_OPEN_AT_END; + else + openMode = B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE; + + if (file) return B_ERROR; + + file = new BFile(&fDest, openMode); + + if (!file || file->InitCheck() != B_OK) { + BString err("Can't open file!"); + errors->AddInt32("status", ERROR_OPENFILE); + errors->AddString("error", err); + return B_ERROR; + } + + return B_OK; +} + + +status_t +ActionDownload::Perform(BMessage* errors) +{ + //is it these the right place? + fillMessage(this, errors); + // + + CURLcode res; + status_t status = B_OK; + + curl = curl_easy_init(); + + if (!curl) { + BString err("Internal error (CURL can't' init)!"); + errors->AddInt32("status", ERROR_CURL_INIT); + errors->AddString("error", err); + return B_ERROR; + } + + curl_easy_setopt(curl, CURLOPT_URL, fUrl.String()); + curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, callback); + curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this ); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, save_file); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); + + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION , 1); //follow the white link! + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER , FALSE); //https + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST , FALSE); //https + + curl_easy_setopt(curl, CURLOPT_FAILONERROR , 1); + + if (fDownloadManager) + fDownloadManager->FinishCurl(curl); + + //RESUME + if (fAllowResume) { + BNode stat(&fDest); + // file->Seek(SEEK_END,0); + // size_t resume = (size_t)file->Position(); + off_t size = 0; + stat.GetSize(&size); + + off_t resume = size; + + + if ( resume > 0 ) { + curl_easy_setopt(curl, CURLOPT_RESUME_FROM, resume); + resuming = (double)resume; + } + } + + BMessage connect(*errors); + connect.what = fMsg; + connect.AddInt32("status", OK_CONNECTING); + fTarget->PostMessage(&connect); + + res = curl_easy_perform(curl); + + if (res != 0) { + BString err("Can't connect!"); + errors->AddInt32("status", ERROR_PERFORMING); + errors->AddString("error", err); + errors->AddInt32("curl_error", res); + errors->AddBool("should_stop", ShouldStop()); + status = B_ERROR; + } else { + errors->AddInt32("status", OK_DOWNLOADED); + } + + curl_easy_cleanup(curl); + + errors->AddString("path", GetLocalPath()); + errors->AddRef("entry", &fDest); + + BMimeType mime; + + if (fFileType != "") + BNodeInfo(file).SetType(fFileType.String()); + else { + status_t guess = BMimeType::GuessMimeType(&fDest, &mime); // :( return B_ERROR.. why???????????? + if (guess == B_OK) + BNodeInfo(file).SetType(mime.Type()); + } + + delete file; + file = NULL; + + if (res == CURLE_BAD_DOWNLOAD_RESUME || res == CURLE_HTTP_RANGE_ERROR) { + // sarebbe bello chiedere.. va be per ora ci riprova e basta! + fAllowResume = false; + errors->MakeEmpty(); + Perform(errors); + } + + return status; +} + +BString +ActionDownload::GetDescription() +{ + return "HTTP File download"; +} + +BString +ActionDownload::GetLocalPath() +{ + return BPath(&fDest).Path(); +} + +void +ActionDownload::fillMessage(ActionDownload* ad, BMessage* msg) +{ + //fill with common fileds + int32 what = msg->what; //preserve original what. + *msg = ad->extraInfo; + + msg->AddInt64("sid", ad->GetActionID()); + msg->AddString("url", ad->fUrl); + + msg->what = what; +} + + + +void +ActionDownload::sendProgressX(ActionDownload* ad, double max, double current) +{ + BMessage msg(ad->fMsg); + + fillMessage(ad, &msg); + + msg.AddInt32("status", OK_PROGRESS); + msg.AddFloat("max_progress", (float)max); + msg.AddFloat("current_progress", (float)current); + + float div = 0.0; + if (max > 0.0) + div = ((float)current / (float)max ) * 100; + + msg.AddInt32("percentage_progress", (int32)floor(div)); + + double speed = 0; + if (curl_easy_getinfo(ad->curl, CURLINFO_SPEED_DOWNLOAD, &speed) == CURLE_OK) + msg.AddFloat("download_speed", (float)speed); + + + //TOTALE: + if (curl_easy_getinfo(ad->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &speed) == CURLE_OK) + msg.AddFloat("total_size", (float)speed); + + // ATTUALE: CURLINFO_SIZE_DOWNLOAD + if (curl_easy_getinfo(ad->curl, CURLINFO_SIZE_DOWNLOAD, &speed) == CURLE_OK) + msg.AddFloat("actual_size", (float)speed); + + char* array = NULL; + + + if (curl_easy_getinfo(ad->curl, CURLINFO_EFFECTIVE_URL, &array) == CURLE_OK) + msg.AddString("effective_url", BString(array)); + + if (ad->fFileType == "" && curl_easy_getinfo(ad->curl, CURLINFO_CONTENT_TYPE, &array) == CURLE_OK) { + ad->fFileType.SetTo(array); + BNodeInfo(ad->file).SetType(ad->fFileType.String()); + msg.AddString("filetype", BString(array)); + } + + ad->fTarget->PostMessage(&msg); +} + +/* +void +ActionDownload::sendError(BLooper* looper,uint32 amsg,Status st,BString descr){ + BMessage msg(amsg); + msg.AddInt32("status",st); + msg.AddString("error",descr); + looper->PostMessage(&msg); +} +*/ +// + +int +ActionDownload::callback(void* data, + double dltotal, + double dlnow, + double , + double ) +{ + ActionDownload* ad = ((ActionDownload*)data); + + if (ad->fTarget) + sendProgressX(ad, ad->resuming + dltotal, ad->resuming + dlnow); + return 0; +} + +size_t +ActionDownload::save_file( void* ptr, size_t size, size_t nmemb, void* data) +{ + ActionDownload* ad = ((ActionDownload*)data); + + if (!ad->file) { + BMessage errors; //FIIX: how to report these errors?? + if (ad->openFile(&errors) != B_OK) { + printf("ERROR: can't create destination file!\n"); + return 0; + } + } + BFile* file = ad->file; + if (ad->ShouldStop()) return 0; + return file->Write(ptr, size * nmemb); +} diff --git a/libs/libdownload/ActionDownload.h b/libs/libdownload/ActionDownload.h new file mode 100644 index 0000000..6edf4b4 --- /dev/null +++ b/libs/libdownload/ActionDownload.h @@ -0,0 +1,107 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _ACTION_DOWNLOAD_H +#define _ACTION_DOWNLOAD_H + +#include + +#include +#include +#include +#include +#include + +#include "Action.h" + +class ActionDownload : public Action +{ +public: + enum Status { + ERROR_OPENFILE, // 0 + ERROR_CURL_INIT, // 1 + ERROR_PERFORMING, // 2 + OK_CONNECTING, // 3 + OK_PROGRESS, // 4 + OK_DOWNLOADED // 5 + }; + + ActionDownload(BString URL, entry_ref dest, + bool allowResume, BLooper* target = NULL, uint32 msg = 0); + virtual ~ActionDownload(); + + status_t Perform(BMessage* errors); + BString GetDescription(); + BString GetLocalPath(); + + void SetLooper(BLooper* loop, uint32 msg) { + fTarget = loop; + fMsg = msg; + }; + BLooper* Looper() { + return fTarget; + } + uint32 MessageWhat() { + return fMsg; + } + + // For compatibility: + void SetExtraData(BString extra) { + extraInfo.AddString("extra", extra); + } + + void SetKey(BString key, BString data) { + extraInfo.AddString(key.String(), data); + } + + void SetRef(BString key, entry_ref* ref) { + extraInfo.AddRef(key.String(), ref); + } + + status_t GetRef(BString key, entry_ref* ref) { + return extraInfo.FindRef(key.String(), ref); + } + + BString GetKey(BString key) { + BString data; + extraInfo.FindString(key.String(), &data); + return data; + } + + + void SetShouldStop(bool stop) { + fShouldStop = stop; + } + bool ShouldStop() { + return fShouldStop; + } + + void SetDownloadManager(DownloadManager* downloadManager); + +private: + status_t openFile(BMessage*); + + static void fillMessage(ActionDownload*, BMessage*); + static void sendProgressX(ActionDownload*, double max, double current); + + //curl callbacks: + static size_t save_file ( void* ptr, size_t size, size_t nmemb, void* stream); + static int callback (void* clientp, double dltotal, double dlnow, double , double); + + CURL* curl; + BString fUrl; + entry_ref fDest; + BLooper* fTarget; + uint32 fMsg; + BMessage extraInfo; + bool fShouldStop; + BFile* file; + double resuming; + bool fAllowResume; + DownloadManager* fDownloadManager; + + BString fFileType; +}; + +#endif // _ACTION_DOWNLOAD_H diff --git a/libs/libdownload/DownloadManager.cpp b/libs/libdownload/DownloadManager.cpp new file mode 100644 index 0000000..aa0a0a7 --- /dev/null +++ b/libs/libdownload/DownloadManager.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Andrea Anzani, andrea.anzani@gmail.com + */ + +#include + +#include "DownloadManager.h" +#include "ActionDownload.h" + +#define DEFAULT_ITEMS_DOWNLOAD 1 + +DownloadManager::DownloadManager(BLooper* target): fTarget(target) +{ +} + +void +DownloadManager::Enqueue(QueueType type, ActionDownload* ad) +{ + if (!fQueue[type]) { + fQueue[type] = new QueueFileDownload("queue_downloads", DEFAULT_ITEMS_DOWNLOAD, fTarget, DOWNLOAD_INFO); + } + ad->SetDownloadManager(this); + fQueue[type]->AddAction(ad); +} + +void +DownloadManager::TryStopCurrentAction(QueueType type, BString key, BString value) +{ + + if (!fQueue[type]) { + return; + } + + if (fQueue[type]->Lock()) { + for (int i = 0; i < fQueue[type]->CountThreads(); i++) { + ActionDownload* ad = (ActionDownload*)fQueue[type]->CurrentAction(i); + if (ad && ad->GetKey(key).Compare(value.String()) == 0 ) + ad->SetShouldStop(true); + } + + fQueue[type]->Unlock(); + } +} + +bool +DownloadManager::RemoveFromQueue(QueueType type , BString key, BString value) +{ + + if (!fQueue[type]) { + return true; + } + + if (fQueue[type]->Lock()) { + + for (int32 i = 0; i < fQueue[type]->CountActions(); i++) { + ActionDownload* ad = (ActionDownload*)fQueue[type]->ActionAt(i); + if (ad->GetKey(key).Compare(value.String()) == 0 ) { + fQueue[type]->RemoveActionAt(i); + fQueue[type]->Unlock(); + return true; + } + } + + for (int i = 0; i < fQueue[type]->CountThreads(); i++) { + ActionDownload* ad = (ActionDownload*)fQueue[type]->CurrentAction(i); + + if (ad && ad->GetKey(key).Compare(value.String()) == 0 ) + ad->SetShouldStop(true); + } + fQueue[type]->Unlock(); + return false; + } + + return false; +} + +void +DownloadManager::RemoveQueue(QueueType type, BList* removed) +{ + + if (!fQueue[type]) { + return; + } + fQueue[type]->Lock(); + + while (fQueue[type]->CountActions()) { + // ActionDownload *ad = (ActionDownload*)fQueue[type]->ActionAt(0); + fQueue[type]->RemoveActionAt(0); + } + + // *************************************** + // we did not unlock + // so no one can add new item. + // *************************************** + + for (int i = 0; i < fQueue[type]->CountThreads(); i++) { + if (fQueue[type]->CurrentAction(i)) + removed->AddItem( (void*)fQueue[type]->CurrentAction(i)); + } +} + +void +DownloadManager::LoadProxySetting(BMessage* data) +{ + + bool value; + + fEnabled = false; + fAddress.SetTo(""); + fUserpwd.SetTo(""); + fPort = 0; + + if (data->FindBool("enable", &value) == B_OK) + fEnabled = value; + + if (!fEnabled) + return; + + BString username, password; + data->FindString("username", &username); + data->FindInt32("port", &fPort); + data->FindString("address", &fAddress); + data->FindString("password", &password); + if (username != "" || password != "") + fUserpwd << username << ":" << password; +} + + +void +DownloadManager::LoadDownloadSetting(BMessage* data) +{ + + //FIXME: fQueue[XXX] can be null. + + //here magic stuff! :=) + int32 number = 1; + if (data->FindInt32("max_downloads", &number) == B_OK) { + fQueue[DOWNLOADS_QUEUE]->SetDownloadCount(number); + } +} + + +void +DownloadManager::FinishCurl(CURL* curl) +{ + if (!fEnabled) + return; + + curl_easy_setopt(curl, CURLOPT_PROXY, fAddress.String()); + curl_easy_setopt(curl, CURLOPT_PROXYPORT, fPort); + curl_easy_setopt(curl, CURLOPT_PROXYTYPE , CURLPROXY_HTTP); + + if (fUserpwd != "") + curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD , fUserpwd.String() ); +} + +thread_id +DownloadManager::SingleThreadAction(ActionDownload* action) +{ + action->SetDownloadManager(this); + if (action->Looper() == NULL) { + action->SetLooper(fTarget, DOWNLOAD_INFO); + } + thread_id id = spawn_thread(DownloadManager::SingleThreadPerform, "single_action_thread", B_NORMAL_PRIORITY, (void*)action); + resume_thread(id); + return id; +} + +int32 +DownloadManager::SingleThreadPerform(void* a) +{ + + ActionDownload* ad = (ActionDownload*)a; + + // perform the action + BMessage err; + //status_t status = + ad->Perform(&err); + // do post-perform! + if (ad->Looper()) { + err.what = ad->MessageWhat(); + BMessenger(ad->Looper()).SendMessage(&err); + } + return 0; +} + diff --git a/libs/libdownload/DownloadManager.h b/libs/libdownload/DownloadManager.h new file mode 100644 index 0000000..e8ba986 --- /dev/null +++ b/libs/libdownload/DownloadManager.h @@ -0,0 +1,56 @@ +/* + * Copyright 2006-2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _DOWNLOAD_MANAGER_H +#define _DOWNLOAD_MANAGER_H + +#include +#include +#include +#include + +#include "QueueFileDownload.h" + +#define DOWNLOAD_INFO 'dwni' + +class ActionDownload; + +class DownloadManager +{ +public: + enum QueueType { + DOWNLOADS_QUEUE = 0 + }; + + DownloadManager(BLooper* target); + + void Enqueue(QueueType, ActionDownload*); + + void TryStopCurrentAction(QueueType, BString key, BString value); + + bool RemoveFromQueue(QueueType, BString key, BString value); + + void RemoveQueue(QueueType, BList* removed); + + void LoadProxySetting(BMessage* data); + void LoadDownloadSetting(BMessage* data); + + thread_id SingleThreadAction(ActionDownload* action); + + void FinishCurl(CURL* curl); + +private: + static int32 SingleThreadPerform(void* a); + + QueueFileDownload* fQueue[3]; + + // Proxy + int32 fPort; + BString fAddress; + BString fUserpwd; + bool fEnabled; + BLooper* fTarget; +}; + +#endif // _DOWNLOAD_MANAGER_H diff --git a/libs/libdownload/Jamfile b/libs/libdownload/Jamfile new file mode 100644 index 0000000..aba6cd3 --- /dev/null +++ b/libs/libdownload/Jamfile @@ -0,0 +1,16 @@ +SubDir TOP libs libdownload ; + +SubDirSysHdrs [ FDirName $(TOP) ] ; +SubDirSysHdrs [ FDirName $(CURL_INCLUDE_DIR) ] ; + +StaticLibrary libdownload.a : + ActionDownload.cpp + DownloadManager.cpp + QueueFileDownload.cpp + QueueMultiActions.cpp + StripeView.cpp + PercentageWindow.cpp + : be $(TARGET_LIBSTDC++) translation curl +; + +LINKFLAGS on libdownload.a += -L$(CURL_LIBRARY_DIR) ; diff --git a/libs/libdownload/ObjectList.h b/libs/libdownload/ObjectList.h new file mode 100644 index 0000000..1bd6602 --- /dev/null +++ b/libs/libdownload/ObjectList.h @@ -0,0 +1,872 @@ +/* +Open Tracker License + +Terms and Conditions + +Copyright (c) 1991-2000, Be Incorporated. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice applies to all licensees +and shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Be Incorporated shall not be +used in advertising or otherwise to promote the sale, use or other dealings in +this Software without prior written authorization from Be Incorporated. + +Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks +of Be Incorporated in the United States and other countries. Other brand product +names are registered trademarks or trademarks of their respective holders. +All rights reserved. +*/ + +/**************************************************************************** +** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ** +** ** +** DANGER, WILL ROBINSON! ** +** ** +** The interfaces contained here are part of BeOS's ** +** ** +** >> PRIVATE NOT FOR PUBLIC USE << ** +** ** +** implementation. ** +** ** +** These interfaces WILL CHANGE in future releases. ** +** If you use them, your app WILL BREAK at some future time. ** +** ** +** (And yes, this does mean that binaries built from OpenTracker will not ** +** be compatible with some future releases of the OS. When that happens, ** +** we will provide an updated version of this file to keep compatibility.) ** +** ** +** WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING ** +****************************************************************************/ + +// +// ObjectList is a wrapper around BList that adds type safety, +// optional object ownership, search, insert operations, etc. +// + +#ifndef __OBJECT_LIST__ +#define __OBJECT_LIST__ + +#ifndef _BE_H +#include +#endif + +#include + + +template class BObjectList; + +template +struct UnaryPredicate { + + virtual int operator()(const T*) const + // virtual could be avoided here if FindBinaryInsertionIndex, + // etc. were member template functions + { + return 0; + } + +private: + static int _unary_predicate_glue(const void* item, void* context); + + friend class BObjectList; +}; + +template +int +UnaryPredicate::_unary_predicate_glue(const void* item, void* context) +{ + return ((UnaryPredicate *)context)->operator()((const T*)item); +} + + +class _PointerList_ : public BList +{ +public: + _PointerList_(const _PointerList_ &list); + _PointerList_(int32 itemsPerBlock = 20, bool owning = false); + ~_PointerList_(); + + typedef void *(* GenericEachFunction)(void*, void*); + typedef int (* GenericCompareFunction)(const void*, const void*); + typedef int (* GenericCompareFunctionWithState)(const void*, const void*, + void*); + typedef int (* UnaryPredicateGlue)(const void*, void*); + + void* EachElement(GenericEachFunction, void*); + void SortItems(GenericCompareFunction); + void SortItems(GenericCompareFunctionWithState, void* state); + void HSortItems(GenericCompareFunction); + void HSortItems(GenericCompareFunctionWithState, void* state); + + void* BinarySearch(const void*, GenericCompareFunction) const; + void* BinarySearch(const void*, GenericCompareFunctionWithState, void* state) const; + + int32 BinarySearchIndex(const void*, GenericCompareFunction) const; + int32 BinarySearchIndex(const void*, GenericCompareFunctionWithState, void* state) const; + int32 BinarySearchIndexByPredicate(const void*, UnaryPredicateGlue) const; + + bool Owning() const; + bool ReplaceItem(int32, void*); + +protected: + bool owning; + +}; + +template +class BObjectList : private _PointerList_ +{ +public: + + // iteration and sorting + typedef T *(* EachFunction)(T*, void*); + typedef const T *(* ConstEachFunction)(const T*, void*); + typedef int (* CompareFunction)(const T*, const T*); + typedef int (* CompareFunctionWithState)(const T*, const T*, void* state); + + BObjectList(int32 itemsPerBlock = 20, bool owning = false); + BObjectList(const BObjectList& list); + // clones list; if list is owning, makes copies of all + // the items + + virtual ~BObjectList(); + + BObjectList& operator=(const BObjectList& list); + // clones list; if list is owning, makes copies of all + // the items + + // adding and removing + // ToDo: + // change Add calls to return const item + bool AddItem(T*); + bool AddItem(T*, int32); + bool AddList(BObjectList*); + bool AddList(BObjectList*, int32); + + bool RemoveItem(T*, bool deleteIfOwning = true); + // if owning, deletes the removed item + T* RemoveItemAt(int32); + // returns the removed item + + void MakeEmpty(bool deleteIfOwning = true); + + // item access + T* ItemAt(int32) const; + + bool ReplaceItem(int32 index, T*); + // if list is owning, deletes the item at first + T* SwapWithItem(int32 index, T* newItem); + // same as ReplaceItem, except does not delete old item at , + // returns it instead + + T* FirstItem() const; + T* LastItem() const; + + // misc. getters + int32 IndexOf(const T*) const; + bool HasItem(const T*) const; + bool IsEmpty() const; + int32 CountItems() const; + + T* EachElement(EachFunction, void*); + const T* EachElement(ConstEachFunction, void*) const; + + void SortItems(CompareFunction); + void SortItems(CompareFunctionWithState, void* state); + void HSortItems(CompareFunction); + void HSortItems(CompareFunctionWithState, void* state); + + // linear search, returns first item that matches predicate + const T* FindIf(const UnaryPredicate &) const; + T* FindIf(const UnaryPredicate &); + + // list must be sorted with CompareFunction for these to work + T* BinarySearch(const T&, CompareFunction) const; + T* BinarySearch(const T&, CompareFunctionWithState, void* state) const; + + template + T* BinarySearchByKey(const Key& key, int (*compare)(const Key*, const T*)) + const; + + template + T* BinarySearchByKey(const Key& key, + int (*compare)(const Key*, const T*, void*), void* state) const; + + int32 BinarySearchIndex(const T& item, CompareFunction compare) const; + int32 BinarySearchIndex(const T& item, CompareFunctionWithState compare, + void* state) const; + + template + int32 BinarySearchIndexByKey(const Key& key, + int (*compare)(const Key*, const T*)) const; + + // Binary insertion - list must be sorted with CompareFunction for + // these to work + + // simple insert + bool BinaryInsert(T*, CompareFunction); + bool BinaryInsert(T*, CompareFunctionWithState, void* state); + bool BinaryInsert(T*, const UnaryPredicate &); + + // unique insert, returns false if item already in list + bool BinaryInsertUnique(T*, CompareFunction); + bool BinaryInsertUnique(T*, CompareFunctionWithState, void* state); + bool BinaryInsertUnique(T*, const UnaryPredicate &); + + // insert a copy of the item, returns new inserted item + T* BinaryInsertCopy(const T& copyThis, CompareFunction); + T* BinaryInsertCopy(const T& copyThis, CompareFunctionWithState, void* state); + + // insert a copy of the item if not in list already + // returns new inserted item or existing item in case of a conflict + T* BinaryInsertCopyUnique(const T& copyThis, CompareFunction); + T* BinaryInsertCopyUnique(const T& copyThis, CompareFunctionWithState, void* state); + + int32 FindBinaryInsertionIndex(const UnaryPredicate &, bool* alreadyInList = 0) const; + // returns either the index into which a new item should be inserted + // or index of an existing item that matches the predicate + + // deprecated API, will go away + BList* AsBList() { + return this; + } + const BList* AsBList() const { + return this; + } +private: + void SetItem(int32, T*); +}; + +template +Result +WhileEachListItem(BObjectList *list, Result (Item::*func)(Param1), Param1 p1) +{ + Result result = 0; + int32 count = list->CountItems(); + + for (int32 index = 0; index < count; index++) + if ((result = (list->ItemAt(index)->*func)(p1)) != 0) + break; + + return result; +} + +template +Result +WhileEachListItem(BObjectList *list, Result (*func)(Item*, Param1), Param1 p1) +{ + Result result = 0; + int32 count = list->CountItems(); + + for (int32 index = 0; index < count; index++) + if ((result = (*func)(list->ItemAt(index), p1)) != 0) + break; + + return result; +} + +template +Result +WhileEachListItem(BObjectList *list, Result (Item::*func)(Param1, Param2), + Param1 p1, Param2 p2) +{ + Result result = 0; + int32 count = list->CountItems(); + + for (int32 index = 0; index < count; index++) + if ((result = (list->ItemAt(index)->*func)(p1, p2)) != 0) + break; + + return result; +} + +template +Result +WhileEachListItem(BObjectList *list, Result (*func)(Item*, Param1, Param2), + Param1 p1, Param2 p2) +{ + Result result = 0; + int32 count = list->CountItems(); + + for (int32 index = 0; index < count; index++) + if ((result = (*func)(list->ItemAt(index), p1, p2)) != 0) + break; + + return result; +} + +template +Result +WhileEachListItem(BObjectList *list, Result (*func)(Item*, Param1, Param2, + Param3, Param4), Param1 p1, Param2 p2, Param3 p3, Param4 p4) +{ + Result result = 0; + int32 count = list->CountItems(); + + for (int32 index = 0; index < count; index++) + if ((result = (*func)(list->ItemAt(index), p1, p2, p3, p4)) != 0) + break; + + return result; +} + +template +void +EachListItemIgnoreResult(BObjectList *list, Result (Item::*func)()) +{ + int32 count = list->CountItems(); + for (int32 index = 0; index < count; index++) + (list->ItemAt(index)->*func)(); +} + +template +void +EachListItem(BObjectList *list, void (*func)(Item*, Param1), Param1 p1) +{ + int32 count = list->CountItems(); + for (int32 index = 0; index < count; index++) + (func)(list->ItemAt(index), p1); +} + +template +void +EachListItem(BObjectList *list, void (Item::*func)(Param1, Param2), + Param1 p1, Param2 p2) +{ + int32 count = list->CountItems(); + for (int32 index = 0; index < count; index++) + (list->ItemAt(index)->*func)(p1, p2); +} + +template +void +EachListItem(BObjectList *list, void (*func)(Item*, Param1, Param2), + Param1 p1, Param2 p2) +{ + int32 count = list->CountItems(); + for (int32 index = 0; index < count; index++) + (func)(list->ItemAt(index), p1, p2); +} + +template +void +EachListItem(BObjectList *list, void (*func)(Item*, Param1, Param2, + Param3), Param1 p1, Param2 p2, Param3 p3) +{ + int32 count = list->CountItems(); + for (int32 index = 0; index < count; index++) + (func)(list->ItemAt(index), p1, p2, p3); +} + + +template +void +EachListItem(BObjectList *list, void (*func)(Item*, Param1, Param2, + Param3, Param4), Param1 p1, Param2 p2, Param3 p3, Param4 p4) +{ + int32 count = list->CountItems(); + for (int32 index = 0; index < count; index++) + (func)(list->ItemAt(index), p1, p2, p3, p4); +} + +// inline code + +inline bool +_PointerList_::Owning() const +{ + return owning; +} + +template +BObjectList::BObjectList(int32 itemsPerBlock, bool owning) + : _PointerList_(itemsPerBlock, owning) +{ +} + +template +BObjectList::BObjectList(const BObjectList &list) + : _PointerList_(list) +{ + owning = list.owning; + if (owning) { + // make our own copies in an owning list + int32 count = list.CountItems(); + for (int32 index = 0; index < count; index++) { + T* item = list.ItemAt(index); + if (item) + item = new T(*item); + SetItem(index, item); + } + } +} + +template +BObjectList::~BObjectList() +{ + if (Owning()) + // have to nuke elements first + MakeEmpty(); +} + +template +BObjectList & +BObjectList::operator=(const BObjectList &list) +{ + owning = list.owning; + BObjectList &result = (BObjectList &)_PointerList_::operator=(list); + if (owning) { + // make our own copies in an owning list + int32 count = list.CountItems(); + for (int32 index = 0; index < count; index++) { + T* item = list.ItemAt(index); + if (item) + item = new T(*item); + SetItem(index, item); + } + } + return result; +} + +template +bool +BObjectList::AddItem(T* item) +{ + // need to cast to void * to make T work for const pointers + return _PointerList_::AddItem((void*)item); +} + +template +bool +BObjectList::AddItem(T* item, int32 atIndex) +{ + return _PointerList_::AddItem((void*)item, atIndex); +} + +template +bool +BObjectList::AddList(BObjectList *newItems) +{ + return _PointerList_::AddList(newItems); +} + +template +bool +BObjectList::AddList(BObjectList *newItems, int32 atIndex) +{ + return _PointerList_::AddList(newItems, atIndex); +} + + +template +bool +BObjectList::RemoveItem(T* item, bool deleteIfOwning) +{ + bool result = _PointerList_::RemoveItem((void*)item); + + if (result && Owning() && deleteIfOwning) + delete item; + + return result; +} + +template +T * +BObjectList::RemoveItemAt(int32 index) +{ + return (T*)_PointerList_::RemoveItem(index); +} + +template +inline T * +BObjectList::ItemAt(int32 index) const +{ + return (T*)_PointerList_::ItemAt(index); +} + +template +bool +BObjectList::ReplaceItem(int32 index, T* item) +{ + if (owning) + delete ItemAt(index); + return _PointerList_::ReplaceItem(index, (void*)item); +} + +template +T * +BObjectList::SwapWithItem(int32 index, T* newItem) +{ + T* result = ItemAt(index); + _PointerList_::ReplaceItem(index, (void*)newItem); + return result; +} + +template +void +BObjectList::SetItem(int32 index, T* newItem) +{ + _PointerList_::ReplaceItem(index, (void*)newItem); +} + +template +int32 +BObjectList::IndexOf(const T* item) const +{ + return _PointerList_::IndexOf((void*)item); +} + +template +T * +BObjectList::FirstItem() const +{ + return (T*)_PointerList_::FirstItem(); +} + +template +T * +BObjectList::LastItem() const +{ + return (T*)_PointerList_::LastItem(); +} + +template +bool +BObjectList::HasItem(const T* item) const +{ + return _PointerList_::HasItem((void*)item); +} + +template +bool +BObjectList::IsEmpty() const +{ + return _PointerList_::IsEmpty(); +} + +template +int32 +BObjectList::CountItems() const +{ + return _PointerList_::CountItems(); +} + +template +void +BObjectList::MakeEmpty(bool deleteIfOwning) +{ + if (owning && deleteIfOwning) { + int32 count = CountItems(); + for (int32 index = 0; index < count; index++) + delete ItemAt(index); + } + _PointerList_::MakeEmpty(); +} + +template +T * +BObjectList::EachElement(EachFunction func, void* params) +{ + return (T*)_PointerList_::EachElement((GenericEachFunction)func, params); +} + + +template +const T * +BObjectList::EachElement(ConstEachFunction func, void* params) const +{ + return (const T*) + const_cast *>(this)->_PointerList_::EachElement( + (GenericEachFunction)func, params); +} + +template +const T * +BObjectList::FindIf(const UnaryPredicate &predicate) const +{ + int32 count = CountItems(); + for (int32 index = 0; index < count; index++) + if (predicate.operator()(ItemAt(index)) == 0) + return ItemAt(index); + return 0; +} + +template +T * +BObjectList::FindIf(const UnaryPredicate &predicate) +{ + int32 count = CountItems(); + for (int32 index = 0; index < count; index++) + if (predicate.operator()(ItemAt(index)) == 0) + return ItemAt(index); + return 0; +} + + +template +void +BObjectList::SortItems(CompareFunction function) +{ + _PointerList_::SortItems((GenericCompareFunction)function); +} + +template +void +BObjectList::SortItems(CompareFunctionWithState function, void* state) +{ + _PointerList_::SortItems((GenericCompareFunctionWithState)function, state); +} + +template +void +BObjectList::HSortItems(CompareFunction function) +{ + _PointerList_::HSortItems((GenericCompareFunction)function); +} + +template +void +BObjectList::HSortItems(CompareFunctionWithState function, void* state) +{ + _PointerList_::HSortItems((GenericCompareFunctionWithState)function, state); +} + +template +T * +BObjectList::BinarySearch(const T& key, CompareFunction func) const +{ + return (T*)_PointerList_::BinarySearch(&key, + (GenericCompareFunction)func); +} + +template +T * +BObjectList::BinarySearch(const T& key, CompareFunctionWithState func, void* state) const +{ + return (T*)_PointerList_::BinarySearch(&key, + (GenericCompareFunctionWithState)func, state); +} + + +template +template +T * +BObjectList::BinarySearchByKey(const Key& key, + int (*compare)(const Key*, const T*)) const +{ + return (T*)_PointerList_::BinarySearch(&key, + (GenericCompareFunction)compare); +} + + +template +template +T * +BObjectList::BinarySearchByKey(const Key& key, + int (*compare)(const Key*, const T*, void*), void* state) const +{ + return (T*)_PointerList_::BinarySearch(&key, + (GenericCompareFunctionWithState)compare, state); +} + + +template +int32 +BObjectList::BinarySearchIndex(const T& item, CompareFunction compare) const +{ + return _PointerList_::BinarySearchIndex(&item, + (GenericCompareFunction)compare); +} + + +template +int32 +BObjectList::BinarySearchIndex(const T& item, + CompareFunctionWithState compare, void* state) const +{ + return _PointerList_::BinarySearchIndex(&item, + (GenericCompareFunctionWithState)compare, state); +} + + +template +template +int32 +BObjectList::BinarySearchIndexByKey(const Key& key, + int (*compare)(const Key*, const T*)) const +{ + return _PointerList_::BinarySearchIndex(&key, + (GenericCompareFunction)compare); +} + + +template +bool +BObjectList::BinaryInsert(T* item, CompareFunction func) +{ + int32 index = _PointerList_::BinarySearchIndex(item, + (GenericCompareFunction)func); + if (index >= 0) { + // already in list, add after existing + return AddItem(item, index + 1); + } + + return AddItem(item, -index - 1); +} + +template +bool +BObjectList::BinaryInsert(T* item, CompareFunctionWithState func, void* state) +{ + int32 index = _PointerList_::BinarySearchIndex(item, + (GenericCompareFunctionWithState)func, state); + if (index >= 0) { + // already in list, add after existing + return AddItem(item, index + 1); + } + + return AddItem(item, -index - 1); +} + +template +bool +BObjectList::BinaryInsertUnique(T* item, CompareFunction func) +{ + int32 index = _PointerList_::BinarySearchIndex(item, + (GenericCompareFunction)func); + if (index >= 0) + return false; + + return AddItem(item, -index - 1); +} + +template +bool +BObjectList::BinaryInsertUnique(T* item, CompareFunctionWithState func, void* state) +{ + int32 index = _PointerList_::BinarySearchIndex(item, + (GenericCompareFunctionWithState)func, state); + if (index >= 0) + return false; + + return AddItem(item, -index - 1); +} + + +template +T * +BObjectList::BinaryInsertCopy(const T& copyThis, CompareFunction func) +{ + int32 index = _PointerList_::BinarySearchIndex(©This, + (GenericCompareFunction)func); + + if (index >= 0) + index++; + else + index = -index - 1; + + T* newItem = new T(copyThis); + AddItem(newItem, index); + return newItem; +} + +template +T * +BObjectList::BinaryInsertCopy(const T& copyThis, CompareFunctionWithState func, void* state) +{ + int32 index = _PointerList_::BinarySearchIndex(©This, + (GenericCompareFunctionWithState)func, state); + + if (index >= 0) + index++; + else + index = -index - 1; + + T* newItem = new T(copyThis); + AddItem(newItem, index); + return newItem; +} + +template +T * +BObjectList::BinaryInsertCopyUnique(const T& copyThis, CompareFunction func) +{ + int32 index = _PointerList_::BinarySearchIndex(©This, + (GenericCompareFunction)func); + if (index >= 0) + return ItemAt(index); + + index = -index - 1; + T* newItem = new T(copyThis); + AddItem(newItem, index); + return newItem; +} + +template +T * +BObjectList::BinaryInsertCopyUnique(const T& copyThis, CompareFunctionWithState func, + void* state) +{ + int32 index = _PointerList_::BinarySearchIndex(©This, + (GenericCompareFunctionWithState)func, state); + if (index >= 0) + return ItemAt(index); + + index = -index - 1; + T* newItem = new T(copyThis); + AddItem(newItem, index); + return newItem; +} + +template +int32 +BObjectList::FindBinaryInsertionIndex(const UnaryPredicate &pred, bool* alreadyInList) +const +{ + int32 index = _PointerList_::BinarySearchIndexByPredicate(&pred, + (UnaryPredicateGlue)&UnaryPredicate::_unary_predicate_glue); + + if (alreadyInList) + *alreadyInList = index >= 0; + + if (index < 0) + index = -index - 1; + + return index; +} + +template +bool +BObjectList::BinaryInsert(T* item, const UnaryPredicate &pred) +{ + return AddItem(item, FindBinaryInsertionIndex(pred)); +} + +template +bool +BObjectList::BinaryInsertUnique(T* item, const UnaryPredicate &pred) +{ + bool alreadyInList; + int32 index = FindBinaryInsertionIndex(pred, &alreadyInList); + if (alreadyInList) + return false; + + AddItem(item, index); + return true; +} + +#endif /* __OBJECT_LIST__ */ diff --git a/libs/libdownload/PercentageWindow.cpp b/libs/libdownload/PercentageWindow.cpp new file mode 100644 index 0000000..578a301 --- /dev/null +++ b/libs/libdownload/PercentageWindow.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Andrea Anzani, andrea.anzani@gmail.com + */ + +#include "PercentageWindow.h" + +#include "StripeView.h" +#include + +#include +#include + +#define DEFAULT_RECT BRect(0, 0, 310, 75) + +static const int kTextIconOffsetSpace = 30; + +PercentageWindow::PercentageWindow(const char* title, const char* text, BBitmap* icon) : BWindow(DEFAULT_RECT, title, B_MODAL_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE) +{ + + // Set up the "_master_" view + + StripeView* masterView = new StripeView(Bounds()); + AddChild(masterView); + masterView->SetBitmap(icon); + + kTextIconOffset = 0; + + if (masterView->Bitmap()) + kTextIconOffset = masterView->Bitmap()->Bounds().right + kTextIconOffsetSpace; + + + //ok, un String + //il percentage (a 0) con testo percentuale + float maxW; + + BStringView* text_string = new BStringView(BRect(kTextIconOffset, 6, 0, 0), "_text", text); + masterView->AddChild(text_string); + text_string->ResizeToPreferred(); + maxW = text_string->Frame().right + 6; + + BRect rect(text_string->Frame()); + rect.OffsetBy(0, text_string->Bounds().Height() + 6); + + perc = new BStringView(rect, "_percentage", "100%"); + masterView->AddChild(perc); + perc->ResizeToPreferred(); + if (perc->Frame().right + 6 > maxW) + maxW = perc->Frame().right + 6; + + perc->SetText("0%"); + + maxW += kTextIconOffsetSpace; + + ResizeTo(maxW, Bounds().bottom); + + rect = Bounds(); + + rect.top = perc->Frame().bottom + 6; + rect.left = perc->Frame().left; + rect.right -= kTextIconOffsetSpace; + + pw = new BStatusBar(rect, "status_bar", NULL, NULL); + pw->SetMaxValue(100.0); + masterView->AddChild(pw); + // pw->ResizeToPreferred(); + + ResizeTo(Bounds().right, pw->Frame().bottom + 5); + SetLook(B_FLOATING_WINDOW_LOOK); + MoveTo(BAlert::AlertPosition(Frame().Width(), Frame().Height())); +} + +void +PercentageWindow::SetPercentage(int p) +{ + + + BString text; + text << p << "%"; + + if (Lock()) { + perc->SetText(text.String()); + pw->SetTo((float)p); + Unlock(); + } + +} + +int +PercentageWindow::GetPercentage() +{ + return (int)pw->CurrentValue(); +} + +bool +PercentageWindow::QuitRequested() +{ + if (fLooper) + BMessenger(fLooper).SendMessage(fMsg); + + return true; +} + + +void +PercentageWindow::Go(BLooper* lop, int32 msg) +{ + fLooper = lop; + fMsg = msg; + Show(); +} diff --git a/libs/libdownload/PercentageWindow.h b/libs/libdownload/PercentageWindow.h new file mode 100644 index 0000000..0f40f25 --- /dev/null +++ b/libs/libdownload/PercentageWindow.h @@ -0,0 +1,37 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _PercentageWindow_H_ +#define _PercentageWindow_H_ + +#include +#include +#include +#include +#include + +class ReflowingTextView; +class BStringView; + + +class PercentageWindow : public BWindow +{ + +public: + PercentageWindow(const char* title, const char* text, BBitmap* icon = NULL); + void Go(BLooper* lop = NULL, int32 msg = 0); + void SetPercentage(int perc); + int GetPercentage(); + bool QuitRequested(); + +private: + BLooper* fLooper; + int32 fMsg; + float kTextIconOffset; + BStringView* perc; + BStatusBar* pw; + +}; + +#endif diff --git a/libs/libdownload/Queue.h b/libs/libdownload/Queue.h new file mode 100644 index 0000000..ec7f114 --- /dev/null +++ b/libs/libdownload/Queue.h @@ -0,0 +1,1054 @@ +/* This file is Copyright 2005 Level Control Systems. See the included LICENSE.txt file for details. */ + +#ifndef MuscleQueue_h +#define MuscleQueue_h + +//#include "support/MuscleSupport.h" + +#define ARRAYITEMS(x) (sizeof(x)/sizeof(x[0])) /* returns # of items in array */ +template inline const T& muscleMax(const T& p1, const T& p2) +{ + return (p1 < p2) ? p2 : p1; +} +template inline const T& muscleMin(const T& p1, const T& p2) +{ + return (p1 < p2) ? p1 : p2; +} + +# define newnothrow new +# define newnothrow_array(T, count) newnothrow T[count] + +#ifndef SMALL_QUEUE_SIZE +# define SMALL_QUEUE_SIZE 3 +#endif + +/** This class implements a templated double-ended queue data structure. + * Adding or removing items from the head or tail of a Queue is (on average) + * an O(1) operation. A Queue also makes for a nice Vector, if that's all you need. + */ +template class Queue +{ +public: + /** Constructor. + * @param initialSlots Specifies how many slots to pre-allocate. Defaults to (SMALL_QUEUE_SIZE). + */ + Queue(uint32 initialSlots = SMALL_QUEUE_SIZE); + + /** Copy constructor. */ + Queue(const Queue& copyMe); + + /** Destructor. */ + virtual ~Queue(); + + /** Assigment operator. */ + Queue& operator=(const Queue& from); + + /** Equality operator. Queues are equal if they are the same length, and + * every nth item in this queue is == to the corresponding item in (rhs). */ + bool operator==(const Queue& rhs) const; + + /** Returns the negation of the equality operator */ + bool operator!=(const Queue& rhs) const { + return !(*this == rhs); + } + + /** Appends (item) to the end of the queue. Queue size grows by one. + * @param item The item to append. + * @return B_NO_ERROR on success, B_ERROR on failure (out of memory) + */ + status_t AddTail(const ItemType& item = ItemType()); + + /** Appends some or all items in (queue) to the end of our queue. Queue size + * grows by (queue.GetNumItems()). + * For example: + * Queue a; // contains 1, 2, 3, 4 + * Queue b; // contains 5, 6, 7, 8 + * a.AddTail(b); // a now contains 1, 2, 3, 4, 5, 6, 7, 8 + * @param queue The queue to append to our queue. + * @param startIndex Index in (queue) to start adding at. Default to zero. + * @param numItems Number of items to add. If this number is too large, it will be capped appropriately. Default is to add all items. + * @return B_NO_ERROR on success, B_ERROR on failure (out of memory) + */ + status_t AddTail(const Queue & queue, uint32 startIndex = 0, uint32 numItems = (uint32) - 1); + + /** Adds the given array of items to the tail of the Queue. Equivalent + * to adding them to the tail of the Queue one at a time, but somewhat + * more efficient. On success, the queue size grows by (numItems). + * @param items Pointer to an array of items to add to the Queue. + * @param numItems Number of items in the array + * @return B_NO_ERROR on success, or B_ERROR on failure (out of memory) + */ + status_t AddTail(const ItemType* items, uint32 numItems); + + /** Prepends (item) to the head of the queue. Queue size grows by one. + * @param item The item to prepend. + * @return B_NO_ERROR on success, B_ERROR on failure (out of memory) + */ + status_t AddHead(const ItemType& item = ItemType()); + + /** Concatenates (queue) to the head of our queue. + * Our queue size grows by (queue.GetNumItems()). + * For example: + * Queue a; // contains 1, 2, 3, 4 + * Queue b; // contains 5, 6, 7, 8 + * a.AddHead(b); // a now contains 5, 6, 7, 8, 1, 2, 3, 4 + * @param queue The queue to prepend to our queue. + * @param startIndex Index in (queue) to start adding at. Default to zero. + * @param numItems Number of items to add. If this number is too large, it will be capped appropriately. Default is to add all items. + * @return B_NO_ERROR on success, B_ERROR on failure (out of memory) + */ + status_t AddHead(const Queue & queue, uint32 startIndex = 0, uint32 numItems = (uint32) - 1); + + /** Concatenates the given array of items to the head of the Queue. + * The semantics are the same as for AddHead(const Queue &). + * @param items Pointer to an array of items to add to the Queue. + * @param numItems Number of items in the array + * @return B_NO_ERROR on success, or B_ERROR on failure (out of memory) + */ + status_t AddHead(const ItemType* items, uint32 numItems); + + /** Removes the item at the head of the queue. + * @return B_NO_ERROR on success, B_ERROR if the queue was empty. + */ + status_t RemoveHead(); + + /** Removes the item at the head of the queue and places it into (returnItem). + * @param returnItem On success, the removed item is copied into this object. + * @return B_NO_ERROR on success, B_ERROR if the queue was empty + */ + status_t RemoveHead(ItemType& returnItem); + + /** Removes the item at the tail of the queue. + * @return B_NO_ERROR on success, B_ERROR if the queue was empty. + */ + status_t RemoveTail(); + + /** Removes the item at the tail of the queue and places it into (returnItem). + * @param returnItem On success, the removed item is copied into this object. + * @return B_NO_ERROR on success, B_ERROR if the queue was empty + */ + status_t RemoveTail(ItemType& returnItem); + + /** Removes the item at the (index)'th position in the queue. + * @param index Which item to remove--can range from zero + * (head of the queue) to GetNumItems()-1 (tail of the queue). + * @return B_NO_ERROR on success, B_ERROR on failure (i.e. bad index) + * Note that this method is somewhat inefficient for indices that + * aren't at the head or tail of the queue (i.e. O(n) time) + */ + status_t RemoveItemAt(uint32 index); + + /** Removes the item at the (index)'th position in the queue, and copies it into (returnItem). + * @param index Which item to remove--can range from zero + * (head of the queue) to (GetNumItems()-1) (tail of the queue). + * @param returnItem On success, the removed item is copied into this object. + * @return B_NO_ERROR on success, B_ERROR on failure (i.e. bad index) + */ + status_t RemoveItemAt(uint32 index, ItemType& returnItem); + + /** Copies the (index)'th item into (returnItem). + * @param index Which item to get--can range from zero + * (head of the queue) to (GetNumItems()-1) (tail of the queue). + * @param returnItem On success, the retrieved item is copied into this object. + * @return B_NO_ERROR on success, B_ERROR on failure (e.g. bad index) + */ + status_t GetItemAt(uint32 index, ItemType& returnItem) const; + + /** Returns a pointer to an item in the array (i.e. no copying of the item is done). + * Included for efficiency; be careful with this: modifying the queue can invalidate + * the returned pointer! + * @param index Index of the item to return a pointer to. + * @return a pointer to the internally held item, or NULL if (index) was invalid. + */ + ItemType* GetItemAt(uint32 index) const; + + /** Replaces the (index)'th item in the queue with (newItem). + * @param index Which item to replace--can range from zero + * (head of the queue) to (GetNumItems()-1) (tail of the queue). + * @param newItem The item to place into the queue at the (index)'th position. + * @return B_NO_ERROR on success, B_ERROR on failure (e.g. bad index) + */ + status_t ReplaceItemAt(uint32 index, const ItemType& newItem = ItemType()); + + /** Inserts (item) into the (nth) slot in the array. InsertItemAt(0) + * is the same as AddHead(item), InsertItemAt(GetNumItems()) is the same + * as AddTail(item). Other positions will involve an O(n) shifting of contents. + * @param index The position at which to insert the new item. + * @param newItem The item to insert into the queue. + * @return B_NO_ERROR on success, B_ERROR on failure (i.e. bad index). + */ + status_t InsertItemAt(uint32 index, const ItemType& newItem = ItemType()); + + /** Removes all items from the queue. + * @param releaseCachedBuffers If true, we will immediately free any buffers that we may be holding. Otherwise + * we will keep them around so that they can be re-used in the future. + */ + void Clear(bool releaseCachedBuffers = false); + + /** Returns the number of items in the queue. (This number does not include pre-allocated space) */ + uint32 GetNumItems() const { + return _itemCount; + } + + /** Returns the number of item-slots we have allocated space for. Note that this is NOT + * the same as the value returned by GetNumItems() -- it is generally larger, since we pre-allocate + * additional slots in advance to cut down on the number of re-allocations we need to do. + */ + uint32 GetNumAllocatedItemSlots() const { + return _queueSize; + } + + /** Returns true iff their are no items in the queue. */ + bool IsEmpty() const { + return (_itemCount == 0); + } + + /** Returns a read-only reference the head item in the queue. You must not call this when the queue is empty! */ + const ItemType& Head() const { + return *GetItemAt(0); + } + + /** Returns a read-only reference the tail item in the queue. You must not call this when the queue is empty! */ + const ItemType& Tail() const { + return *GetItemAt(_itemCount - 1); + } + + /** Returns a writable reference the head item in the queue. You must not call this when the queue is empty! */ + ItemType& Head() { + return *GetItemAt(0); + } + + /** Returns a writable reference the tail item in the queue. You must not call this when the queue is empty! */ + ItemType& Tail() { + return *GetItemAt(_itemCount - 1); + } + + /** Returns a pointer to the first item in the queue, or NULL if the queue is empty */ + ItemType* HeadPointer() const { + return (_itemCount > 0) ? GetItemAt(0) : NULL; + } + + /** Returns a pointer to the last item in the queue, or NULL if the queue is empty */ + ItemType* TailPointer() const { + return (_itemCount > 0) ? GetItemAt(_itemCount - 1) : NULL; + } + + /** Convenient read-only array-style operator (be sure to only use valid indices!) */ + const ItemType& operator [](uint32 Index) const; + + /** Convenient read-write array-style operator (be sure to only use valid indices!) */ + ItemType& operator [](uint32 Index); + + /** Deprecated synonym for GetItemAt(). Don't call this method in new code, call GetItemAt() instead! + * @deprecated + */ + ItemType* GetItemPointer(uint32 index) const { + return GetItemAt(index); + } + + /** Makes sure there is enough space allocated for at least (numSlots) items. + * You only need to call this if you wish to minimize the number of data re-allocations done, + * or wish to add or remove a large number of default items at once (by specifying setNumItems=true). + * @param numSlots the minimum amount of items to pre-allocate space for in the Queue. + * @param setNumItems If true, the length of the Queue will be altered by adding or removing + * items to (from) the tail of the Queue until the Queue is the specified size. + * If false (the default), more slots may be pre-allocated, but the + * number of items officially in the Queue remains the same as before. + * @param extraReallocItems If we have to do an array reallocation, this many extra slots will be + * added to the newly allocated array, above and beyond what is strictly + * necessary. That way the likelihood of another reallocation being necessary + * in the near future is reduced. Default value is zero, indicating that + * no extra slots will be allocated. This argument is ignored if (setNumItems) is true. + * @returns B_NO_ERROR on success, or B_ERROR on failure (out of memory) + */ + status_t EnsureSize(uint32 numSlots, bool setNumItems = false, uint32 extraReallocItems = 0); + + /** Returns the last index of the given (item), or -1 if (item) is + * not found in the list. O(n) search time. + * @param item The item to look for. + * @return The index of (item), or -1 if no such item is present. + */ + int32 IndexOf(const ItemType& item) const; + + /** + * Swaps the values of the two items at the given indices. This operation + * will involve three copies of the held items. + * @param fromIndex First index to swap. + * @param toIndex Second index to swap. + */ + void Swap(uint32 fromIndex, uint32 toIndex); + + /** + * Reverses the ordering of the items in the given subrange. + * (e.g. if the items were A,B,C,D,E, this would change them to E,D,C,B,A) + * @param from Index of the start of the subrange. Defaults to zero. + * @param to Index of the next item after the end of the subrange. If greater than + * the number of items currently in the queue, this value will be clipped + * to be equal to that number. Defaults to the largest possible uint32. + */ + void ReverseItemOrdering(uint32 from = 0, uint32 to = ((uint32) - 1)); + + /** + * This is the signature of the type of callback function that you must pass + * into the Sort() method. This function should work like strcmp(), returning + * a negative value if (item1) is less than item2, or zero if the items are + * equal, or a positive value if (item1) is greater than item2. + * @param item1 The first item + * @param item2 The second item + * @param cookie A user-defined value that was passed in to the Sort() method. + * @return A value indicating which item is "larger", as defined above. + */ + typedef int (*ItemCompareFunc)(const ItemType& item1, const ItemType& item2, void* cookie); + + /** + * Does an in-place, stable sort on a subrange of the contents of this Queue. + * (The sort algorithm is a smart, in-place merge sort). + * @param compareFunc A function that compares two items in this queue and returns + * a value indicating which is "larger". (negative indicates + * that the second parameter is larger, positive indicate that the + * first is larger, and zero indicates that the two parameters are equal) + * @param from Index of the start of the subrange. Defaults to zero. + * @param to Index of the next item after the end of the subrange. + * If greater than the number of items currently in the queue, + * the subrange will extend to the last item. Defaults to the largest possible uint32. + * @param optCookie A user-defined value that will be passed to the (compareFunc). + */ + void Sort(ItemCompareFunc compareFunc, uint32 from = 0, uint32 to = ((uint32) - 1), void* optCookie = NULL); + + /** + * Swaps our contents with the contents of (that), in an efficient manner. + * @param that The queue whose contents are to be swapped with our own. + */ + void SwapContents(Queue & that); + + /** + * Goes through the array and removes every item that is equal to (val). + * @param val the item to look for and remove + * @return The number of instances of (val) that were found and removed during this operation. + */ + uint32 RemoveAllInstancesOf(const ItemType& val); + + /** + * Goes through the array and removes the first item that is equal to (val). + * @param val the item to look for and remove + * @return B_NO_ERROR if a matching item was found and removed, or B_ERROR if it wasn't found. + */ + status_t RemoveFirstInstanceOf(const ItemType& val); + + /** + * Goes through the array and removes the last item that is equal to (val). + * @param val the item to look for and remove + * @return B_NO_ERROR if a matching item was found and removed, or B_ERROR if it wasn't found. + */ + status_t RemoveLastInstanceOf(const ItemType& val); + + /** Returns true iff the first item in our queue is equal to (prefix). */ + bool StartsWith(const ItemType& prefix) const { + return ((GetNumItems() > 0) && (Head() == prefix)); + } + + /** Returns true iff the (prefixQueue) is a prefix of this queue. */ + bool StartsWith(const Queue & prefixQueue) const; + + /** Returns true iff the last item in our queue is equal to (suffix). */ + bool EndsWith(const ItemType& suffix) const { + return ((GetNumItems() > 0) && (Tail() == suffix)); + } + + /** Returns true iff the (suffixQueue) is a suffix of this queue. */ + bool EndsWith(const Queue & suffixQueue) const; + + /** + * Returns a pointer to the nth internally-held contiguous-Item-sub-array, to allow efficient + * array-style access to groups of items in this Queue. In the current implementation + * there may be as many as two such sub-arrays present, depending on the internal state of the Queue. + * @param whichArray Index of the internal array to return a pointer to. Typically zero or one. + * @param retLength On success, the number of items in the returned sub-array will be written here. + * @return Pointer to the first item in the sub-array on success, or NULL on failure. + * Note that this array is only guaranteed valid as long as no items are + * added or removed from the Queue. + */ + ItemType* GetArrayPointer(uint32 whichArray, uint32& retLength) { + return const_cast(GetArrayPointerAux(whichArray, retLength)); + } + + /** Read-only version of the above */ + const ItemType* GetArrayPointer(uint32 whichArray, uint32& retLength) const { + return GetArrayPointerAux(whichArray, retLength); + } + +private: + const ItemType* GetArrayPointerAux(uint32 whichArray, uint32& retLength) const; + void SwapContentsAux(Queue & that); + + inline uint32 NextIndex(uint32 idx) const { + return (idx >= _queueSize - 1) ? 0 : idx + 1; + } + inline uint32 PrevIndex(uint32 idx) const { + return (idx == 0) ? _queueSize - 1 : idx - 1; + } + + // Translates a user-index into an index into the _queue array. + inline uint32 InternalizeIndex(uint32 idx) const { + return (_headIndex + idx) % _queueSize; + } + + // Helper methods, used for sorting (stolen from http://www-ihm.lri.fr/~thomas/VisuTri/inplacestablesort.html) + void Merge(ItemCompareFunc compareFunc, uint32 from, uint32 pivot, uint32 to, uint32 len1, uint32 len2, void* cookie); + uint32 Lower(ItemCompareFunc compareFunc, uint32 from, uint32 to, const ItemType& val, void* cookie) const; + uint32 Upper(ItemCompareFunc compareFunc, uint32 from, uint32 to, const ItemType& val, void* cookie) const; + + ItemType _smallQueue[SMALL_QUEUE_SIZE]; // small queues can be stored inline in this array + ItemType* _queue; // points to _smallQueue, or to a dynamically alloc'd array + uint32 _queueSize; // number of slots in the _queue array + uint32 _itemCount; // number of valid items in the array + uint32 _headIndex; // index of the first filled slot (meaningless if _itemCount is zero) + uint32 _tailIndex; // index of the last filled slot (meaningless if _itemCount is zero) + const uint32 _initialSize; // as specified in ctor +}; + +template +Queue::Queue(uint32 initialSize) + : _queue(NULL), _queueSize(0), _itemCount(0), _initialSize(initialSize) +{ + // empty +} + +template +Queue::Queue(const Queue& rhs) + : _queue(NULL), _queueSize(0), _itemCount(0), _initialSize(rhs._initialSize) +{ + *this = rhs; +} + +template +bool +Queue::operator ==(const Queue& rhs) const +{ + if (this == &rhs) return true; + if (GetNumItems() != rhs.GetNumItems()) return false; + + for (int i = GetNumItems() - 1; i >= 0; i--) if (((*this)[i] == rhs[i]) == false) return false; + + return true; +} + + +template +Queue & +Queue::operator =(const Queue& rhs) +{ + if (this != &rhs) { + uint32 numItems = rhs.GetNumItems(); + if (EnsureSize(numItems, true) == B_NO_ERROR) for (uint32 i = 0; i < numItems; i++) (*this)[i] = rhs[i]; + } + return *this; +} + +template +ItemType & +Queue::operator[](uint32 i) +{ + // MASSERT(i<_itemCount, "Invalid index to Queue::[]"); + return _queue[InternalizeIndex(i)]; +} + +template +const ItemType & +Queue::operator[](uint32 i) const +{ + // MASSERT(i<_itemCount, "Invalid index to Queue::[]"); + return _queue[InternalizeIndex(i)]; +} + +template +ItemType * +Queue::GetItemAt(uint32 i) const +{ + return &_queue[InternalizeIndex(i)]; +} + +template +Queue::~Queue() +{ + if (_queue != _smallQueue) delete [] _queue; +} + +template +status_t +Queue:: +AddTail(const ItemType& item) +{ + if (EnsureSize(_itemCount + 1, false, _itemCount + 1) == B_ERROR) return B_ERROR; + if (_itemCount == 0) _headIndex = _tailIndex = 0; + else _tailIndex = NextIndex(_tailIndex); + _queue[_tailIndex] = item; + _itemCount++; + return B_NO_ERROR; +} + +template +status_t +Queue:: +AddTail(const Queue &queue, uint32 startIndex, uint32 numNewItems) +{ + uint32 hisSize = queue.GetNumItems(); + numNewItems = muscleMin(numNewItems, (startIndex < hisSize) ? (hisSize - startIndex) : 0); + + uint32 mySize = GetNumItems(); + uint32 newSize = mySize + numNewItems; + + if (EnsureSize(newSize, true) != B_NO_ERROR) return B_ERROR; + for (uint32 i = mySize; i < newSize; i++) (*this)[i] = queue[startIndex++]; + return B_NO_ERROR; +} + +template +status_t +Queue:: +AddTail(const ItemType* items, uint32 numItems) +{ + uint32 mySize = GetNumItems(); + uint32 newSize = mySize + numItems; + uint32 rhs = 0; + + if (EnsureSize(newSize, true) != B_NO_ERROR) return B_ERROR; + for (uint32 i = mySize; i < newSize; i++) (*this)[i] = items[rhs++]; + return B_NO_ERROR; +} + +template +status_t +Queue:: +AddHead(const ItemType& item) +{ + if (EnsureSize(_itemCount + 1, false, _itemCount + 1) == B_ERROR) return B_ERROR; + if (_itemCount == 0) _headIndex = _tailIndex = 0; + else _headIndex = PrevIndex(_headIndex); + _queue[_headIndex] = item; + _itemCount++; + return B_NO_ERROR; +} + +template +status_t +Queue:: +AddHead(const Queue &queue, uint32 startIndex, uint32 numNewItems) +{ + uint32 hisSize = queue.GetNumItems(); + numNewItems = muscleMin(numNewItems, (startIndex < hisSize) ? (hisSize - startIndex) : 0); + + if (EnsureSize(numNewItems + GetNumItems()) != B_NO_ERROR) return B_ERROR; + for (int i = ((int)startIndex + numNewItems) - 1; i >= (int32)startIndex; i--) if (AddHead(queue[i]) == B_ERROR) return B_ERROR; + return B_NO_ERROR; +} + +template +status_t +Queue:: +AddHead(const ItemType* items, uint32 numItems) +{ + if (EnsureSize(_itemCount + numItems) != B_NO_ERROR) return B_ERROR; + for (int i = ((int)numItems) - 1; i >= 0; i--) if (AddHead(items[i]) == B_ERROR) return B_ERROR; + return B_NO_ERROR; +} + +template +status_t +Queue:: +RemoveHead(ItemType& returnItem) +{ + if (_itemCount == 0) return B_ERROR; + returnItem = _queue[_headIndex]; + return RemoveHead(); +} + +template +status_t +Queue:: +RemoveHead() +{ + if (_itemCount == 0) return B_ERROR; + int oldHeadIndex = _headIndex; + _headIndex = NextIndex(_headIndex); + _itemCount--; + _queue[oldHeadIndex] = ItemType(); // this must be done last, as queue state must be coherent when we do this + return B_NO_ERROR; +} + +template +status_t +Queue:: +RemoveTail(ItemType& returnItem) +{ + if (_itemCount == 0) return B_ERROR; + returnItem = _queue[_tailIndex]; + return RemoveTail(); +} + +template +status_t +Queue:: +RemoveTail() +{ + if (_itemCount == 0) return B_ERROR; + int removedItemIndex = _tailIndex; + _tailIndex = PrevIndex(_tailIndex); + _itemCount--; + _queue[removedItemIndex] = ItemType(); // this must be done last, as queue state must be coherent when we do this + return B_NO_ERROR; +} + +template +status_t +Queue:: +GetItemAt(uint32 index, ItemType& returnItem) const +{ + if (index >= _itemCount) return B_ERROR; + returnItem = _queue[InternalizeIndex(index)]; + return B_NO_ERROR; +} + +template +status_t +Queue:: +RemoveItemAt(uint32 index, ItemType& returnItem) +{ + if (index >= _itemCount) return B_ERROR; + returnItem = _queue[InternalizeIndex(index)]; + return RemoveItemAt(index); +} + +template +status_t +Queue:: +RemoveItemAt(uint32 index) +{ + if (index >= _itemCount) return B_ERROR; + + uint32 internalizedIndex = InternalizeIndex(index); + uint32 indexToClear; + + if (index < _itemCount / 2) { + // item is closer to the head: shift everything forward one, ending at the head + while (internalizedIndex != _headIndex) { + uint32 prev = PrevIndex(internalizedIndex); + _queue[internalizedIndex] = _queue[prev]; + internalizedIndex = prev; + } + indexToClear = _headIndex; + _headIndex = NextIndex(_headIndex); + } else { + // item is closer to the tail: shift everything back one, ending at the tail + while (internalizedIndex != _tailIndex) { + uint32 next = NextIndex(internalizedIndex); + _queue[internalizedIndex] = _queue[next]; + internalizedIndex = next; + } + indexToClear = _tailIndex; + _tailIndex = PrevIndex(_tailIndex); + } + + _itemCount--; + _queue[indexToClear] = ItemType(); // this must be done last, as queue state must be coherent when we do this + return B_NO_ERROR; +} + +template +status_t +Queue:: +ReplaceItemAt(uint32 index, const ItemType& newItem) +{ + if (index >= _itemCount) return B_ERROR; + _queue[InternalizeIndex(index)] = newItem; + return B_NO_ERROR; +} + +template +status_t +Queue:: +InsertItemAt(uint32 index, const ItemType& newItem) +{ + // Simple cases + if (index > _itemCount) return B_ERROR; + if (index == _itemCount) return AddTail(newItem); + if (index == 0) return AddHead(newItem); + + // Harder case: inserting into the middle of the array + if (index < _itemCount / 2) { + // Add a space at the front, and shift things back + if (AddHead() != B_NO_ERROR) return B_ERROR; // allocate an extra slot + for (uint32 i = 0; i < index; i++) ReplaceItemAt(i, *GetItemAt(i + 1)); + } else { + // Add a space at the rear, and shift things forward + if (AddTail() != B_NO_ERROR) return B_ERROR; // allocate an extra slot + for (int32 i = ((int32)_itemCount) - 1; i > ((int32)index); i--) ReplaceItemAt(i, *GetItemAt(i - 1)); + } + return ReplaceItemAt(index, newItem); +} + +template +void +Queue:: +Clear(bool releaseCachedBuffers) +{ + if ((releaseCachedBuffers) && (_queue != _smallQueue)) { + delete [] _queue; + _queue = NULL; + _queueSize = 0; + _itemCount = 0; + } else while (RemoveTail() == B_NO_ERROR) { + /* empty */ + } +} + +template +status_t +Queue:: +EnsureSize(uint32 size, bool setNumItems, uint32 extraPreallocs) +{ + if ((_queue == NULL) || (_queueSize < size)) { + const uint32 sqLen = ARRAYITEMS(_smallQueue); + uint32 temp = size + extraPreallocs; + uint32 newQLen = muscleMax(_initialSize, ((setNumItems) || (temp <= sqLen)) ? muscleMax(sqLen, temp) : temp); + + ItemType* newQueue = ((_queue == _smallQueue) || (newQLen > sqLen)) ? newnothrow_array(ItemType, newQLen) : _smallQueue; + if (newQueue == NULL) { + return B_ERROR; + } + if (newQueue == _smallQueue) newQLen = sqLen; + + for (uint32 i = 0; i < _itemCount; i++) (void) GetItemAt(i, newQueue[i]); // we know that (_itemCount < size) + if (setNumItems) _itemCount = size; + _headIndex = 0; + _tailIndex = _itemCount - 1; + + if (_queue == _smallQueue) { + ItemType blank = ItemType(); + for (uint32 i = 0; i < sqLen; i++) _smallQueue[i] = blank; + } else delete [] _queue; + + _queue = newQueue; + _queueSize = newQLen; + } + + if (setNumItems) { + // Force ourselves to contain exactly the required number of items + if (_itemCount < size) { + // We can do this quickly because the "new" items are already initialized properly + _tailIndex = (_tailIndex + (size - _itemCount)) % _queueSize; + _itemCount = size; + } else while (_itemCount > size) (void) RemoveTail(); // Gotta overwrite the "removed" items, so this is a bit slower + } + + return B_NO_ERROR; +} + +template +int32 +Queue:: +IndexOf(const ItemType& item) const +{ + if (_queue) for (int i = ((int)GetNumItems()) - 1; i >= 0; i--) if (*GetItemAt(i) == item) return i; + return -1; +} + + +template +void +Queue:: +Swap(uint32 fromIndex, uint32 toIndex) +{ + ItemType temp = *(GetItemAt(fromIndex)); + ReplaceItemAt(fromIndex, *(GetItemAt(toIndex))); + ReplaceItemAt(toIndex, temp); +} + +template +void +Queue:: +Sort(ItemCompareFunc compareFunc, uint32 from, uint32 to, void* cookie) +{ + uint32 size = GetNumItems(); + if (to > size) to = size; + if (to > from) { + if (to < from + 12) { + // too easy, just do a bubble sort (base case) + if (to > from + 1) { + for (uint32 i = from + 1; i < to; i++) { + for (uint32 j = i; j > from; j--) { + int ret = compareFunc(*(GetItemAt(j)), *(GetItemAt(j - 1)), cookie); + if (ret < 0) Swap(j, j - 1); + else break; + } + } + } + } else { + // Okay, do the real thing + uint32 middle = (from + to) / 2; + Sort(compareFunc, from, middle, cookie); + Sort(compareFunc, middle, to, cookie); + Merge(compareFunc, from, middle, to, middle - from, to - middle, cookie); + } + } +} + +template +void +Queue:: +ReverseItemOrdering(uint32 from, uint32 to) +{ + uint32 size = GetNumItems(); + if (size > 0) { + to--; // make it inclusive + if (to >= size) to = size - 1; + while (from < to) Swap(from++, to--); + } +} + +template +status_t +Queue:: +RemoveFirstInstanceOf(const ItemType& val) +{ + uint32 ni = GetNumItems(); + for (uint32 i = 0; i < ni; i++) if ((*this)[i] == val) return RemoveItemAt(i); + return B_ERROR; +} + +template +status_t +Queue:: +RemoveLastInstanceOf(const ItemType& val) +{ + for (int32 i = ((int32)GetNumItems()) - 1; i >= 0; i--) if ((*this)[i] == val) return RemoveItemAt(i); + return B_ERROR; +} + +template +uint32 +Queue:: +RemoveAllInstancesOf(const ItemType& val) +{ + // Efficiently collapse all non-matching slots up to the top of the list + uint32 ret = 0; + uint32 writeTo = 0; + uint32 origSize = GetNumItems(); + for (uint32 readFrom = 0; readFrom < origSize; readFrom++) { + const ItemType& nextRead = (*this)[readFrom]; + if (nextRead == val) ret++; + else { + if (readFrom > writeTo) (*this)[writeTo] = nextRead; + writeTo++; + } + } + + // Now get rid of any now-surplus slots + for (; writeTo < origSize; writeTo++) RemoveTail(); + + return ret; +} + +template +void +Queue:: +Merge(ItemCompareFunc compareFunc, uint32 from, uint32 pivot, uint32 to, uint32 len1, uint32 len2, void* cookie) +{ + if ((len1) && (len2)) { + if (len1 + len2 == 2) { + if (compareFunc(*(GetItemAt(pivot)), *(GetItemAt(from)), cookie) < 0) Swap(pivot, from); + } else { + uint32 first_cut, second_cut; + uint32 len11, len22; + if (len1 > len2) { + len11 = len1 / 2; + first_cut = from + len11; + second_cut = Lower(compareFunc, pivot, to, *GetItemAt(first_cut), cookie); + len22 = second_cut - pivot; + } else { + len22 = len2 / 2; + second_cut = pivot + len22; + first_cut = Upper(compareFunc, from, pivot, *GetItemAt(second_cut), cookie); + len11 = first_cut - from; + } + + // do a rotation + if ((pivot != first_cut) && (pivot != second_cut)) { + // find the greatest common denominator of (pivot-first_cut) and (second_cut-first_cut) + uint32 n = pivot - first_cut; + { + uint32 m = second_cut - first_cut; + while (n != 0) { + uint32 t = m % n; + m = n; + n = t; + } + n = m; + } + + while (n--) { + const ItemType val = *GetItemAt(first_cut + n); + uint32 shift = pivot - first_cut; + uint32 p1 = first_cut + n; + uint32 p2 = p1 + shift; + while (p2 != first_cut + n) { + ReplaceItemAt(p1, *GetItemAt(p2)); + p1 = p2; + if (second_cut - p2 > shift) p2 += shift; + else p2 = first_cut + (shift - (second_cut - p2)); + } + ReplaceItemAt(p1, val); + } + } + + uint32 new_mid = first_cut + len22; + Merge(compareFunc, from, first_cut, new_mid, len11, len22, cookie); + Merge(compareFunc, new_mid, second_cut, to, len1 - len11, len2 - len22, cookie); + } + } +} + + +template +uint32 +Queue:: +Lower(ItemCompareFunc compareFunc, uint32 from, uint32 to, const ItemType& val, void* cookie) const +{ + if (to > from) { + uint32 len = to - from; + while (len > 0) { + uint32 half = len / 2; + uint32 mid = from + half; + if (compareFunc(*(GetItemAt(mid)), val, cookie) < 0) { + from = mid + 1; + len = len - half - 1; + } else len = half; + } + } + return from; +} + +template +uint32 +Queue:: +Upper(ItemCompareFunc compareFunc, uint32 from, uint32 to, const ItemType& val, void* cookie) const +{ + if (to > from) { + uint32 len = to - from; + while (len > 0) { + uint32 half = len / 2; + uint32 mid = from + half; + if (compareFunc(val, *(GetItemAt(mid)), cookie) < 0) len = half; + else { + from = mid + 1; + len = len - half - 1; + } + } + } + return from; +} + +template +const ItemType * +Queue :: GetArrayPointerAux(uint32 whichArray, uint32& retLength) const +{ + if (_itemCount > 0) { + switch (whichArray) { + case 0: + retLength = (_headIndex <= _tailIndex) ? (_tailIndex - _headIndex) + 1 : (_queueSize - _headIndex); + return &_queue[_headIndex]; + break; + + case 1: + if (_headIndex > _tailIndex) { + retLength = _tailIndex + 1; + return &_queue[0]; + } + break; + } + } + return NULL; +} + +template +void +Queue::SwapContents(Queue & that) +{ + bool thisSmall = (_queue == _smallQueue); + bool thatSmall = (that._queue == that._smallQueue); + + if ((thisSmall) && (thatSmall)) { + // First, move any extra items from the longer queue to the shorter one... + uint32 commonSize = muscleMin(GetNumItems(), that.GetNumItems()); + int32 sizeDiff = ((int32)GetNumItems()) - ((int32)that.GetNumItems()); + Queue & copyTo = (sizeDiff > 0) ? that : *this; + Queue & copyFrom = (sizeDiff > 0) ? *this : that; + (void) copyTo.AddTail(copyFrom, commonSize); // guaranteed not to fail + (void) copyFrom.EnsureSize(commonSize, true); // remove the copied items from (copyFrom) + + // Then just swap the elements that are present in both arrays + for (uint32 i = 0; i < commonSize; i++) muscleSwap((*this)[i], that[i]); + } else if (thisSmall) SwapContentsAux(that); + else if (thatSmall) that.SwapContentsAux(*this); + else { + // this case is easy, we can just swizzle the pointers around + muscleSwap(_queue, that._queue); + muscleSwap(_queueSize, that._queueSize); + muscleSwap(_headIndex, that._headIndex); + muscleSwap(_tailIndex, that._tailIndex); + muscleSwap(_itemCount, that._itemCount); + } +} + +template +void +Queue::SwapContentsAux(Queue & largeThat) +{ + // First, copy over our (small) contents to his static buffer + uint32 ni = GetNumItems(); + for (uint32 i = 0; i < ni; i++) largeThat._smallQueue[i] = (*this)[i]; + + // Now adopt his dynamic buffer + _queue = largeThat._queue; + _queueSize = largeThat._queueSize; + _headIndex = largeThat._headIndex; + _tailIndex = largeThat._tailIndex; + + // And point him back at his static buffer + if (ni > 0) { + largeThat._queue = largeThat._smallQueue; + largeThat._queueSize = ARRAYITEMS(largeThat._smallQueue); + largeThat._headIndex = 0; + largeThat._tailIndex = ni - 1; + } else { + largeThat._queue = NULL; + largeThat._queueSize = 0; + // headIndex and tailIndex are undefined in this case anyway + } + + muscleSwap(_itemCount, largeThat._itemCount); +} + +template +bool +Queue::StartsWith(const Queue & prefixQueue) const +{ + if (prefixQueue.GetNumItems() > GetNumItems()) return false; + uint32 prefixQueueLen = prefixQueue.GetNumItems(); + for (uint32 i = 0; i < prefixQueueLen; i++) if (!(prefixQueue[i] == (*this)[i])) return false; + return true; +} + +template +bool +Queue::EndsWith(const Queue & suffixQueue) const +{ + if (suffixQueue.GetNumItems() > GetNumItems()) return false; + int32 lastToCheck = GetNumItems() - suffixQueue.GetNumItems(); + for (int32 i = GetNumItems() - 1; i >= lastToCheck; i--) if (!(suffixQueue[i] == (*this)[i])) return false; + return true; +} + + +#endif + diff --git a/libs/libdownload/QueueFileDownload.cpp b/libs/libdownload/QueueFileDownload.cpp new file mode 100644 index 0000000..e000d77 --- /dev/null +++ b/libs/libdownload/QueueFileDownload.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Andrea Anzani, andrea.anzani@gmail.com + */ + +#include "QueueFileDownload.h" +#include "ActionDownload.h" +#include "Messenger.h" + +void +QueueFileDownload::ActionReadyToPerform(Action* action) +{ + ActionDownload* ad = dynamic_cast(action); + if (!ad) return; + + ad->SetLooper(fLooper, fMsg); + +} + +void +QueueFileDownload::ActionPerformed(Action* action, status_t state, BMessage* msg) +{ + + ActionDownload* ad = dynamic_cast(action); + if (!ad) return; + + BMessage notify(*msg); + notify.what = fMsg; + + if (fLooper) + BMessenger(fLooper).SendMessage(¬ify); + + return; +} + +void +QueueFileDownload::SuppressAction(Action* action) +{ + ActionDownload* ad = dynamic_cast(action); + if (!ad) return; + + ad->SetShouldStop(true); +} + +//-- + diff --git a/libs/libdownload/QueueFileDownload.h b/libs/libdownload/QueueFileDownload.h new file mode 100644 index 0000000..49bcf49 --- /dev/null +++ b/libs/libdownload/QueueFileDownload.h @@ -0,0 +1,33 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef QueueFileDownload_H_ +#define QueueFileDownload_H_ + +#include + +#include "QueueMultiActions.h" + +class QueueFileDownload : public QueueMultiActions +{ +public: + QueueFileDownload(const char* name, int howMany, BLooper* target, uint32 msg) + : + QueueMultiActions(name, howMany) + { + fMsg = msg; + fLooper = target; + } + +protected: + void ActionPerformed(Action*, status_t, BMessage*); + void ActionReadyToPerform(Action*); + void SuppressAction(Action*); + +private: + BLooper* fLooper; + uint32 fMsg; +}; + +#endif diff --git a/libs/libdownload/QueueMultiActions.cpp b/libs/libdownload/QueueMultiActions.cpp new file mode 100644 index 0000000..262854a --- /dev/null +++ b/libs/libdownload/QueueMultiActions.cpp @@ -0,0 +1,255 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Andrea Anzani, andrea.anzani@gmail.com + */ + +#include "QueueMultiActions.h" + +#include +#include + + +#define CHKLOCK if(!IsLocked()) debugger("\nQueueMultiActions must me locked!\n"); + +QueueMultiActions::QueueMultiActions(const char* name, int count) +{ + + fCount = count; + if (count > MAX_MULTI) + debugger("wrong number of multi download settings"); + + for (int j = 0; j < MAX_MULTI; j++) { + fCurrentAction[j] = NULL; + fID[j] = 0; + } + + fName.SetTo(name); + + for (int i = 0; i < fCount; i++) { + BString n(name); + n << "'s enemy #" << i; + token* tok = new token; + tok->qa = this; + tok->index = i; + fID[i] = spawn_thread(QueueMultiActions::ManageTheQueue, n.String(), B_NORMAL_PRIORITY, tok); + //printf("Thread ready [%s] %ld\n",n.String(),fID[i]); + + } + + + + fLock = create_sem(1, name); + fLocked = false; + +} + +QueueMultiActions::~QueueMultiActions() +{ + if (Lock()) { + + for (int i = 0; i < fCount; i++) { + suspend_thread(fID[i]); + exit_thread(fID[i]); + } + delete_sem(fLock); + } +} + + +bool +QueueMultiActions::Lock() +{ + status_t value = acquire_sem(fLock) ; + if (value == B_NO_ERROR) { + fLocked = true; + } + return (value == B_NO_ERROR); +} + +void +QueueMultiActions::Unlock() +{ + fLocked = false; + release_sem(fLock); +} + +void +QueueMultiActions::SetDownloadCount(int fProposedCount) +{ + + if (Lock()) { + + + if (fProposedCount == fCount || fProposedCount <= 0 || fProposedCount > MAX_MULTI) { + Unlock(); + return; + } + + if (fProposedCount > fCount) { + + for (int i = fCount; i < fProposedCount; i++) { + if ( fID[i] == 0) { + BString n = fName; + n << "'s enemy #" << i; + token* tok = new token; + tok->qa = this; + tok->index = i; + fID[i] = spawn_thread(QueueMultiActions::ManageTheQueue, n.String(), B_NORMAL_PRIORITY, tok); + //printf("Thread ready [%s] %ld\n",n.String(),fID[i]); + } + } + } else { + //uhm what to do? + for (int i = fProposedCount; i < fCount; i++) { + SuppressAction(fCurrentAction[i]); + } + + } + + fCount = fProposedCount; + //NotifyALL + + for (int i = 0; i < fCount; i++) + resume_thread(fID[i]); + + Unlock(); + } +} + + +void +QueueMultiActions::AddAction(Action* a) +{ + + //FIX! + //esiste giĆ  un azione con lo stesso ID??? + + if (Lock()) { + //printf("adding %ld - Action name %s\n",fID,a->GetDescription().String()); + fList.AddItem(a); + if (fList.CountItems() <= fCount) { //Auto-start thread + for (int i = 0; i < fCount; i++) + resume_thread(fID[i]); + } + Unlock(); + } +} + +Action* +QueueMultiActions::CurrentAction(int index) +{ + CHKLOCK; + return fCurrentAction[index]; +} + +void +QueueMultiActions::SetCurrentAction(Action* action, int index) +{ + CHKLOCK; + fCurrentAction[index] = action; +} + +int32 +QueueMultiActions::CountActions() +{ + CHKLOCK; + return fList.CountItems(); +} + +Action* +QueueMultiActions::ActionAt(int32 pos) +{ + CHKLOCK; + return fList.ItemAt(pos); +} + +void +QueueMultiActions::RemoveActionAt(int32 pos) +{ + CHKLOCK; + fList.RemoveItemAt(pos); +} + +bool +QueueMultiActions::IsLocked() +{ + return fLocked; +} + +void +QueueMultiActions::KIllThread(int id) +{ + fID[id] = 0; +} + +int32 +QueueMultiActions::ManageTheQueue(void* data) +{ + + token* tok = (token*)data; + QueueMultiActions* qa = tok->qa; + int index = tok->index; + + //printf("Thread started %ld\n",qa->fID[index]); + + while (true) { + + Action* last = NULL; + + if (qa->Lock()) { + + //printf("Thread executing PID %ld Count %ld\n",qa->fID[index],qa->fList.CountItems()); + if (qa->fList.CountItems() > 0) { + // remove and delete the action. + last = qa->fList.ItemAt(0); + qa->fList.RemoveItemAt(0); + qa->SetCurrentAction(last, index); + + } else { + + last = NULL; + qa->SetCurrentAction(last, index); + } + + qa->Unlock(); + } + + if (last) { + // pop the action + qa->ActionReadyToPerform(last); + // perform the action + BMessage err; + status_t status = last->Perform(&err); + // do post-perform! + + qa->ActionPerformed(last, status, &err); + + if (qa->Lock()) { + qa->SetCurrentAction(NULL, index); + delete last; + qa->Unlock(); + } + + } else { + + //printf("Thread suspend PID %ld Count %ld\n",qa->fID[index],qa->fList.CountItems()); + suspend_thread(qa->fID[index]); + } + + //Check my life? + if (qa->Lock()) { + if ( index >= qa->fCount) { + qa->KIllThread(index); + qa->Unlock(); + //printf("Thread died PID %ld\n",qa->fID[index]); + return 0; //bye-bye world! + } + qa->Unlock(); + } + + } //while + +} diff --git a/libs/libdownload/QueueMultiActions.h b/libs/libdownload/QueueMultiActions.h new file mode 100644 index 0000000..af9bc8e --- /dev/null +++ b/libs/libdownload/QueueMultiActions.h @@ -0,0 +1,73 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef QueueMultiActions_H_ +#define QueueMultiActions_H_ + +#include +#include +#include + + +#include "Action.h" + +#define MAX_MULTI 5 + +class QueueMultiActions +{ + +public: + + QueueMultiActions(const char* name, int count); + virtual ~QueueMultiActions(); + + void AddAction(Action* ); + + //BEFORE CALLING CurrentAction, you MUST Lock() the Queue! + Action* CurrentAction(int index); + + int32 CountActions(); + int CountThreads() { + return fCount; + } + + Action* ActionAt(int32 pos); + void RemoveActionAt(int32 pos); + + + void SetDownloadCount(int count); + + + bool Lock(); + void Unlock(); + bool IsLocked(); + +protected: + + static int32 ManageTheQueue(void*); + + virtual void ActionReadyToPerform(Action*) = 0; + virtual void ActionPerformed(Action*, status_t, BMessage*) = 0; + virtual void SuppressAction(Action*) = 0; + + +private: + struct token { + int index; + QueueMultiActions* qa; + }; + + + void SetCurrentAction(Action*, int index); + void KIllThread(int id); + + Action* fCurrentAction[MAX_MULTI]; + thread_id fID[MAX_MULTI]; + sem_id fLock; + BObjectList fList; + bool fLocked; + int fCount; + BString fName; +}; +#endif diff --git a/libs/libdownload/ReflowingTextView.cpp b/libs/libdownload/ReflowingTextView.cpp new file mode 100644 index 0000000..3d8499d --- /dev/null +++ b/libs/libdownload/ReflowingTextView.cpp @@ -0,0 +1,232 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Andrea Anzani, andrea.anzani@gmail.com + */ + +#include "ReflowingTextView.h" + +const bigtime_t RTV_HIDE_DELAY = 1500000; +const bigtime_t RTV_SHOW_DELAY = 750000; + +enum { + RTV_SHOW_TOOLTIP = 0x1000, + RTV_HIDE_TOOLTIP +}; + +ReflowingTextView::ReflowingTextView(BRect frame, const char* name, BRect textRect, uint32 resizeMask, uint32 flags) : BTextView(frame, name, textRect, resizeMask, flags), _showTipRunner(NULL), _canShow(false), _urlTip(NULL), _currentLinkStart(-1) +{ + // empty +} + +ReflowingTextView::~ReflowingTextView() +{ + if ((_urlTip) && (_urlTip->Lock())) _urlTip->Quit(); + delete _showTipRunner; +} + +void ReflowingTextView::AttachedToWindow() +{ + BTextView::AttachedToWindow(); + // FixTextRect(); +} + +void ReflowingTextView::FrameResized(float w, float h) +{ + BTextView::FrameResized(w, h); + // FixTextRect(); +} + +void ReflowingTextView::MouseMoved(BPoint where, uint32 code, const BMessage* msg) +{ + const URLLink* link = NULL; + if (code == B_INSIDE_VIEW) { + link = GetURLAt(where); +#ifdef B_BEOS_VERSION_5 + SetViewCursor((link != NULL) ? B_CURSOR_SYSTEM_DEFAULT : B_CURSOR_I_BEAM); +#else + be_app->SetCursor((link != NULL) ? B_HAND_CURSOR : B_I_BEAM_CURSOR); +#endif + } + + BString scratch; + const BString* urlString = link ? &link->GetURL() : &scratch; + if ((link) && (urlString->Length() == 0)) { + char* buf = new char[link->GetLength()+1]; + GetText(link->GetStart(), link->GetLength(), buf); + buf[link->GetLength()] = '\0'; + scratch = buf; + delete [] buf; + urlString = &scratch; + } + + int32 linkStart = link ? (int32)link->GetStart() : -1; + if (linkStart != _currentLinkStart) { + _currentLinkStart = linkStart; + _currentURLString = *urlString; + if (linkStart >= 0) { + if ((_canShow == false) && ((_showTipRunner == NULL) || (_runnerWillHide))) { + // Schedule unsetting the show flag + delete _showTipRunner; + _runnerWillHide = false; + _showTipRunner = new BMessageRunner(this, new BMessage(RTV_SHOW_TOOLTIP), RTV_SHOW_DELAY, 1); + } + } else { + if (_canShow) { + // schedule a delayed show + delete _showTipRunner; + _runnerWillHide = true; + _showTipRunner = new BMessageRunner(this, new BMessage(RTV_HIDE_TOOLTIP), RTV_HIDE_DELAY, 1); + } else if ((_showTipRunner) && (_runnerWillHide == false)) { + delete _showTipRunner; // cancel the pending show! + _showTipRunner = NULL; + } + } + UpdateToolTip(); + } + BTextView::MouseMoved(where, code, msg); +} + +void ReflowingTextView::MessageReceived(BMessage* msg) +{ + switch (msg->what) { + case RTV_SHOW_TOOLTIP: + delete _showTipRunner; + _showTipRunner = NULL; + _canShow = true; + UpdateToolTip(); + break; + + case RTV_HIDE_TOOLTIP: + delete _showTipRunner; + _showTipRunner = NULL; + _canShow = false; + UpdateToolTip(); + break; + + default: + BTextView::MessageReceived(msg); + break; + } +} + +void ReflowingTextView::UpdateToolTip() +{ + if ((_canShow) && (_currentLinkStart >= 0)) { + if (_urlTip == NULL) { + _urlTip = new BWindow(BRect(0, 0, 5, 5), NULL, B_NO_BORDER_WINDOW_LOOK, B_FLOATING_ALL_WINDOW_FEEL, B_NOT_MOVABLE | B_NOT_CLOSABLE | B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | B_AVOID_FOCUS); + BView* blackBorder = new BView(_urlTip->Bounds(), NULL, B_FOLLOW_ALL_SIDES, 0); + const rgb_color black = {0, 0, 0, 255}; + blackBorder->SetViewColor(black); + _urlTip->AddChild(blackBorder); + + const rgb_color hiliteYellow = {255, 255, 200, 255}; + BRect inset = blackBorder->Bounds(); + inset.InsetBy(1, 1); + BView* yellowInterior = new BView(inset, NULL, B_FOLLOW_ALL_SIDES, 0); + yellowInterior->SetLowColor(hiliteYellow); + yellowInterior->SetViewColor(hiliteYellow); + blackBorder->AddChild(yellowInterior); + + _urlStringView = new BStringView(yellowInterior->Bounds(), NULL, "", B_FOLLOW_ALL_SIDES); + _urlStringView->SetLowColor(hiliteYellow); + _urlStringView->SetViewColor(hiliteYellow); + yellowInterior->AddChild(_urlStringView); + } + if (_urlTip->Lock()) { + font_height ttfh; + _urlStringView->GetFontHeight(&ttfh); + float urlHeight = 20.0f; + BPoint pt = PointAt(_currentLinkStart, &urlHeight); + _urlTip->MoveTo(ConvertToScreen(pt) + BPoint(15.0f, -(ttfh.ascent + ttfh.descent + 3.0f))); + + _urlTip->ResizeTo(muscleMin(_urlStringView->StringWidth(_currentURLString.String()) + 4.0f, 400.0f), ttfh.ascent + ttfh.descent + 2.0f); + _urlStringView->SetText(_currentURLString.String()); + _urlTip->SetWorkspaces(B_CURRENT_WORKSPACE); + if (_urlTip->IsHidden()) _urlTip->Show(); + _urlTip->Unlock(); + } + } else if (_urlTip) { + if (_urlTip->Lock()) { + if (_urlTip->IsHidden() == false) _urlTip->Hide(); + _urlTip->Unlock(); + } + } +} + +void ReflowingTextView::MouseDown(BPoint where) +{ + if (IsFocus() == false) MakeFocus(); + const URLLink* url = GetURLAt(where); + if (url) { + char* allocBuf = NULL; + const char* buf = url->GetURL().String(); + if (buf[0] == '\0') { + buf = allocBuf = new char[url->GetLength()+1]; + GetText(url->GetStart(), url->GetLength(), allocBuf); + allocBuf[url->GetLength()] = '\0'; + } + + if (strncasecmp(buf, "beshare://", 10) == 0) { + char* argv[] = {(char*) &buf[10]}; + // be_roster->Launch(BESHARE_MIME_TYPE, ARRAYITEMS(argv), argv); + } else if (strncasecmp(buf, "beshare:", 8) == 0) { + BMessage msg(_queryMessage); + msg.AddString("query", &buf[8]); + _commandTarget.SendMessage(&msg); + } else if (strncasecmp(buf, "share:", 6) == 0) { + BMessage msg(_queryMessage); + msg.AddString("query", &buf[6]); + _commandTarget.SendMessage(&msg); + } else if (strncasecmp(buf, "priv:", 5) == 0) { + BMessage msg(_privMessage); + msg.AddString("users", &buf[5]); + _commandTarget.SendMessage(&msg); + } else if (strncasecmp(buf, "audio://", 8) == 0) { + BMessage msg(B_REFS_RECEIVED); + BString temp("http://"); + temp += &buf[8]; + msg.AddString("be:url", temp); + be_roster->Launch("audio/x-scpls", &msg); + } else be_roster->Launch(strncasecmp(buf, "mailto:", 7) ? "text/html" : "text/x-email", 1, const_cast(&buf)); + + delete [] allocBuf; + } + BTextView::MouseDown(where); +} + +void ReflowingTextView::FixTextRect() +{ + BRect t(Frame()); + SetTextRect(t); +} + +void ReflowingTextView::Clear() +{ + Delete(0, TextLength() - 1); + _urls.Clear(); +} + +void ReflowingTextView::AddURLRegion(uint32 start, uint32 len, const BString& optHiddenURL) +{ + _urls.AddTail(URLLink(start, len, optHiddenURL)); +} + +void ReflowingTextView::SetCommandURLTarget(const BMessenger& target, const BMessage& queryMsg, const BMessage& privMsg) +{ + _commandTarget = target; + _queryMessage = queryMsg; + _privMessage = privMsg; +} + +const ReflowingTextView::URLLink* ReflowingTextView::GetURLAt(BPoint pt) const +{ + uint32 offset = (uint32) OffsetAt(pt); + for (int i = _urls.GetNumItems() - 1; i >= 0; i--) { + const URLLink& url = _urls[i]; + if ((offset >= url.GetStart()) && (offset < url.GetStart() + url.GetLength())) return &url; + } + return NULL; +} diff --git a/libs/libdownload/ReflowingTextView.h b/libs/libdownload/ReflowingTextView.h new file mode 100644 index 0000000..9022ae5 --- /dev/null +++ b/libs/libdownload/ReflowingTextView.h @@ -0,0 +1,95 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef REFLOWING_TEXT_VIEW_H +#define REFLOWING_TEXT_VIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Queue.h" +#include +//#include "BeShareNameSpace.h" + + +/* Subsclass of BTextView that automatically reflows the text when it is resized */ +class ReflowingTextView : public BTextView +{ +public: + ReflowingTextView(BRect frame, const char* name, BRect textRect, uint32 resizeMask, uint32 flags = B_WILL_DRAW | B_PULSE_NEEDED); + virtual ~ReflowingTextView(); + + virtual void AttachedToWindow(); + + virtual void FrameResized(float w, float h); + + virtual void MessageReceived(BMessage* msg); + + virtual void MouseMoved(BPoint where, uint32 code, const BMessage* msg); + + virtual void MouseDown(BPoint where); + + void FixTextRect(); + + void Clear(); + + void AddURLRegion(uint32 start, uint32 len, const BString& optHiddenURL); + + void SetCommandURLTarget(const BMessenger& target, const BMessage& queryMsg, const BMessage& privMsg); + +private: + void UpdateToolTip(); + + /* A simple little data class that remembers stuff we need to remember about hyperlinks in the text view */ + class URLLink + { + public: + URLLink() : _start(0), _len(0) { + /* empty */ + } + URLLink(uint32 start, uint32 len, const BString& optURL) : _start(start), _len(len), _url(optURL) { + /* empty */ + } + + inline uint32 GetStart() const { + return _start; + } + inline uint32 GetLength() const { + return _len; + } + const BString& GetURL() const { + return _url; + } + + private: + uint32 _start; + uint32 _len; + BString _url; + }; + + const URLLink* GetURLAt(BPoint pt) const; + + Queue _urls; + BMessenger _commandTarget; + BMessage _queryMessage; + BMessage _privMessage; + BMessageRunner* _showTipRunner; + bool _runnerWillHide; // If true, the runner is a hide-runner. + bool _canShow; + BWindow* _urlTip; + BStringView* _urlStringView; + int32 _currentLinkStart; + BString _currentURLString; +}; + + +#endif diff --git a/libs/libdownload/StripeView.cpp b/libs/libdownload/StripeView.cpp new file mode 100644 index 0000000..8a0ba33 --- /dev/null +++ b/libs/libdownload/StripeView.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Andrea Anzani, andrea.anzani@gmail.com + */ + +#include "StripeView.h" + +static const int kIconStripeWidth = 30; + +StripeView::StripeView(BRect frame) + : BView(frame, "StripeView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW), + fIconBitmap(NULL) +{ + SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); +} + + +StripeView::StripeView(BMessage* archive) + : BView(archive), + fIconBitmap(NULL) +{ +} + + +StripeView::~StripeView() {} + + +StripeView* +StripeView::Instantiate(BMessage* archive) +{ + if (!validate_instantiation(archive, "StripeView")) + return NULL; + + return new StripeView(archive); +} + + +status_t +StripeView::Archive(BMessage* archive, bool deep) +{ + return BView::Archive(archive, deep); +} + + +void +StripeView::Draw(BRect updateRect) +{ + // Here's the fun stuff + if (fIconBitmap) { + BRect StripeRect = Bounds(); + StripeRect.right = kIconStripeWidth; + SetHighColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); + FillRect(StripeRect); + + + SetDrawingMode(B_OP_ALPHA); + DrawBitmapAsync(fIconBitmap, BPoint(18, 6)); + SetDrawingMode(B_OP_COPY); + } +} diff --git a/libs/libdownload/StripeView.h b/libs/libdownload/StripeView.h new file mode 100644 index 0000000..ef4c751 --- /dev/null +++ b/libs/libdownload/StripeView.h @@ -0,0 +1,37 @@ +/* + * Copyright 2010, Andrea Anzani. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _StripeView_H_ +#define _StripeView_H_ + +#include + +class StripeView : public BView +{ +public: + StripeView(BRect frame); + StripeView(BMessage* archive); + ~StripeView(); + + static StripeView* Instantiate(BMessage* archive); + status_t Archive(BMessage* archive, bool deep = true); + + virtual void Draw(BRect updateRect); + + // These functions (or something analogous) are missing from libbe.so's + // dump. I can only assume that the bitmap is a public var in the + // original implementation -- or BPAlert is a friend of StripeView. + // Neither one is necessary, since I can just add these. + void SetBitmap(BBitmap* Icon) { + fIconBitmap = Icon; + } + BBitmap* Bitmap() { + return fIconBitmap; + } + +private: + BBitmap* fIconBitmap; +}; + +#endif diff --git a/tests/Jamfile b/tests/Jamfile new file mode 100644 index 0000000..9dd9149 --- /dev/null +++ b/tests/Jamfile @@ -0,0 +1,4 @@ +SubDir TOP tests ; + +# Include all the components +SubInclude TOP tests libdownload ; diff --git a/tests/libdownload/Jamfile b/tests/libdownload/Jamfile new file mode 100644 index 0000000..1bd9f16 --- /dev/null +++ b/tests/libdownload/Jamfile @@ -0,0 +1,12 @@ +SubDir TOP tests libdownload ; + +SubDirSysHdrs [ FDirName $(TOP) libs libdownload ] ; + +Application DownloadTest : + TheApp.cpp + : be $(TARGET_LIBSTDC++) translation libdownload.a curl +; + +Depends DownloadTest : libdownload.a ; + +LINKFLAGS on DownloadTest += -L$(CURL_LIBRARY_DIR) ; diff --git a/tests/libdownload/TheApp.cpp b/tests/libdownload/TheApp.cpp new file mode 100644 index 0000000..aa91560 --- /dev/null +++ b/tests/libdownload/TheApp.cpp @@ -0,0 +1,92 @@ + +#include +#include +#include "DownloadManager.h" +#include "ActionDownload.h" +#include +#include "PercentageWindow.h" +#include + +class TestApp : public BApplication +{ + public: + TestApp():BApplication("application/xeD.libdownload.test"){}; + + void ReadyToRun() + { + + printf("Ready To Run\n"); + + entry_ref ref; + get_ref_for_path("test.xxx", &ref); + ActionDownload* ad = new ActionDownload("http://www.webelin.it/website/wp-content/uploads/pdf/newsletter-webelin.pdf", ref, false); + + BBitmap* icon = BTranslationUtils::GetBitmap("/boot/home/image.bmp"); + percWindow = new PercentageWindow("Download test","Downloading useless test stuff...", icon); + percWindow->Show(); + + /*BMessage msg; + msg.AddBool("enable", true); + msg.AddString("username", ""); + msg.AddString("address", ""); + msg.AddString("password", ""); + msg.AddInt32("port", 8080); + */ + fDownloadManager = new DownloadManager(this); + //fDownloadManager->LoadProxySetting(&msg); + fDownloadManager->SingleThreadAction(ad); + percWindow->SetPercentage(0); + + + } + + void MessageReceived(BMessage* msg) + { + if (msg->what == DOWNLOAD_INFO) + { + int32 status = msg->FindInt32("status"); + switch (status) + { + case ActionDownload::ERROR_OPENFILE: + printf("Error openig dest file\n"); + break; + case ActionDownload::ERROR_CURL_INIT: + printf("Error while init curl\n"); + break; + case ActionDownload::ERROR_PERFORMING: + printf("Error performing download\n"); + msg->PrintToStream(); + break; + case ActionDownload::OK_CONNECTING: + printf("Connecting\n"); + break; + case ActionDownload::OK_PROGRESS: + printf("Progress\n"); + msg->PrintToStream(); + percWindow->SetPercentage(msg->FindInt32("percentage_progress")); + break; + case ActionDownload::OK_DOWNLOADED: + printf("Downloaded\n"); + break; + default: + msg->PrintToStream(); + break; + }; + } + else + BApplication::MessageReceived(msg); + + } + + private: + DownloadManager* fDownloadManager; + PercentageWindow* percWindow; + +}; + +int main() +{ + TestApp tApp; + tApp.Run(); + return 0; +}