Created
March 17, 2013 16:06
-
-
Save richienyhus/5182192 to your computer and use it in GitHub Desktop.
GUI for Transmission on Haiku
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Index: haiku/TRColumn.h | |
=================================================================== | |
--- haiku/TRColumn.h (revision 0) | |
+++ haiku/TRColumn.h (revision 0) | |
@@ -0,0 +1,53 @@ | |
+ | |
+/****************************************************************************** | |
+ * $Id: TRColumnListView.h | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#ifndef TR_COLUM_VIEW | |
+#define TR_COLUM_VIEW | |
+ | |
+#include <unordered_map> | |
+ | |
+#include "libhaiku/ColumnListView.h" | |
+#include "libhaiku/ColumnTypes.h" | |
+ | |
+class TRColumn : public BBitmapColumn { | |
+public: | |
+ TRColumn(const char *title, float width, | |
+ float minWidth, float maxWidth); | |
+ void AddBitmapState(uint32 state, BBitmap* bitmap); | |
+ BBitmap* GetBitmapFromState(uint32 state); | |
+ uint32 CheckState(uint32 state); | |
+ BBitmap* GetStartBitmap(); | |
+ ~TRColumn(); | |
+ virtual void MouseDown(BColumnListView *parent, BRow *row, BField *field, | |
+ BRect field_rect, BPoint point, uint32 buttons); | |
+private: | |
+ std::unordered_map<uint32, BBitmap*> fHashMap; | |
+}; | |
+ | |
+#endif /*TR_COLUM_VIEW*/ | |
Index: haiku/TRApplication.cpp | |
=================================================================== | |
--- haiku/TRApplication.cpp (revision 0) | |
+++ haiku/TRApplication.cpp (revision 0) | |
@@ -0,0 +1,383 @@ | |
+/****************************************************************************** | |
+ * $Id: TRApplication.cpp | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * charles | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#include "TRApplication.h" | |
+ | |
+#include <stdio.h> | |
+#include <string.h> | |
+#include <stdlib.h> | |
+ | |
+#include <AppFileInfo.h> | |
+#include <Alert.h> | |
+#include <Bitmap.h> | |
+#include <File.h> | |
+#include <FindDirectory.h> | |
+#include <Messenger.h> | |
+#include <Mime.h> | |
+#include <Path.h> | |
+#include <String.h> | |
+#include <Resources.h> | |
+#include <Roster.h> | |
+ | |
+// work-around to get rid of this stupid find_directory_r() on Zeta | |
+#ifdef find_directory | |
+#undef find_directory | |
+#endif | |
+ | |
+#define LONG_VERSION_STRING "0.1" | |
+ | |
+int | |
+main(int, char**) { | |
+ TRApplication *app = new TRApplication(); | |
+ if (app->InitCheck() == B_OK) { | |
+ app->Run(); | |
+ } else { | |
+ BString errMsg(""); | |
+ errMsg << "The following error occurred loading Transmission:\n" | |
+ << strerror(app->InitCheck()); | |
+ BAlert *ohNo = new BAlert("Transmission Error", | |
+ errMsg.String(), | |
+ "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT); | |
+ ohNo->Go(); | |
+ } | |
+ delete app; | |
+ | |
+ return (0); | |
+} | |
+ | |
+ | |
+TRApplication::TRApplication() : BApplication(APP_SIG) { | |
+ status_t err = B_OK; | |
+ torrentDir = NULL; | |
+ | |
+ // Install Mime types if necessary. | |
+ BMimeType torrentType("application/x-bittorrent"); | |
+ if (torrentType.InitCheck() == B_OK) { | |
+ if (!torrentType.IsInstalled()) { | |
+ fprintf(stderr, "Installing mime type...\n"); | |
+ // Icon | |
+ BBitmap *icon = NULL; | |
+ icon = GetBitmap('ICON', 102, BRect(0, 0, 31, 31), B_CMAP8); | |
+ torrentType.SetIcon(icon, B_LARGE_ICON); | |
+ icon = GetBitmap('ICON', 104, BRect(0, 0, 15, 15), B_CMAP8); | |
+ torrentType.SetIcon(icon, B_MINI_ICON); | |
+ | |
+ // Extensions | |
+ BMessage extensions; | |
+ extensions.AddString("extensions", "torrent"); | |
+ torrentType.SetFileExtensions(&extensions); | |
+ | |
+ torrentType.SetShortDescription("BitTorrent Meta File"); | |
+ torrentType.SetLongDescription("BitTorrent Protocol Meta File (http://www.bittorrent.com)"); | |
+ | |
+ // Set Preferred Application | |
+ torrentType.SetPreferredApp(APP_SIG, B_OPEN); | |
+ torrentType.Install(); | |
+ } | |
+ } | |
+ | |
+ // Create Settings folder and structure | |
+ BPath settingsPath; | |
+ err = find_directory(B_USER_SETTINGS_DIRECTORY, &settingsPath, false); | |
+ if (err == B_OK) { | |
+ BDirectory settings(settingsPath.Path()); | |
+ BDirectory *trSettings = new BDirectory(); | |
+ | |
+ err = settings.CreateDirectory("Transmission", trSettings); | |
+ if (err == B_FILE_EXISTS) { | |
+ err = trSettings->SetTo(&settings, "Transmission"); | |
+ } | |
+ | |
+ if (err == B_OK) { | |
+ torrentDir = new BDirectory(); | |
+ err = trSettings->CreateDirectory("Torrents", torrentDir); | |
+ if (err != B_OK) { | |
+ torrentDir->SetTo(trSettings, "Torrents"); | |
+ } | |
+ } | |
+ delete trSettings; | |
+ } | |
+ | |
+ // Create window | |
+ window = new TRWindow(); | |
+ window->RescanTorrents(); | |
+ | |
+ settings = new TRPrefsWindow(); | |
+ | |
+ openPanel = new BFilePanel(); | |
+ openPanel->SetRefFilter(new TRFilter()); | |
+} | |
+ | |
+ | |
+TRApplication::~TRApplication() { | |
+ if (window->Lock()) { | |
+ window->Quit(); | |
+ } | |
+ | |
+ if (settings->Lock()) { | |
+ settings->Quit(); | |
+ } | |
+ | |
+ if (openPanel != NULL) { | |
+ delete openPanel; | |
+ } | |
+ | |
+ if (torrentDir != NULL) { | |
+ delete torrentDir; | |
+ } | |
+} | |
+ | |
+ | |
+/** | |
+ * Checks to make sure our Settings directory structure is in place. | |
+ * If this fails, then we need to display an alert and vomit. | |
+ */ | |
+status_t | |
+TRApplication::InitCheck() { | |
+ if (torrentDir != NULL) { | |
+ return torrentDir->InitCheck(); | |
+ } | |
+ return B_ERROR; | |
+} | |
+ | |
+ | |
+void | |
+TRApplication::AboutRequested() { | |
+ // Read the application version info | |
+ app_info runningInfo; | |
+ GetAppInfo(&runningInfo); | |
+ BFile appFile(&(runningInfo.ref), B_READ_ONLY); | |
+ BAppFileInfo appInfo(&appFile); | |
+ version_info vInfo; | |
+ appInfo.GetVersionInfo(&vInfo, B_APP_VERSION_KIND); | |
+ | |
+ BString aboutMsg(""); | |
+ aboutMsg << "Version " << LONG_VERSION_STRING << '\n' | |
+ << "\nMade for BeOS / Haiku / Zeta by \n" | |
+ << "\n" | |
+ << "Gleb Posobin\n" | |
+ << "Eric Petit\n" | |
+ << "Bryan Varner\n" | |
+ << "Fredrik Modéen\n"; | |
+ BAlert *aboutBox = new BAlert("About Transmission", aboutMsg.String(), "Close"); | |
+ aboutBox->Go(); | |
+} | |
+ | |
+ | |
+void | |
+TRApplication::ReadyToRun() { | |
+ SetPulseRate(500000); | |
+ window->Show(); | |
+} | |
+ | |
+ | |
+void | |
+TRApplication::Pulse() { | |
+ window->UpdateList(-1, false, NULL); | |
+} | |
+ | |
+ | |
+/** | |
+ * When a ref is received, we copy each file that is to be opened into | |
+ * B_USER_SETTINGS/Transmission/Torrents | |
+ * | |
+ * Our window node_monitors this folder for added / removed torrent meta files. | |
+ * | |
+ * Each copy is performed in a seperate thread to avoid blocking / locking the app. | |
+ */ | |
+void | |
+TRApplication::RefsReceived(BMessage *message) { | |
+ int32 count; | |
+ type_code code; | |
+ message->GetInfo("refs", &code, &count); | |
+ for (int i = 0; i < count; i++) { | |
+ entry_ref *ref = (entry_ref*)calloc(sizeof(entry_ref), 1); | |
+ if (message->FindRef("refs", i, ref) == B_OK) { | |
+ thread_id cp_thread = spawn_thread(TRApplication::Copy, "TorrentDuper", | |
+ B_NORMAL_PRIORITY, (void *)ref); | |
+ if (!((cp_thread) < B_OK)) { | |
+ resume_thread(cp_thread); | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+/** | |
+ * Needed for browsers or command line interaction | |
+ */ | |
+void | |
+TRApplication::ArgvReceived(int32 _argc, char** _argv) | |
+{ | |
+ entry_ref ref; | |
+ BMessage refs(B_REFS_RECEIVED); | |
+ for( int32 i = 0; i < _argc; ++i ) | |
+ { | |
+ if( B_OK == get_ref_for_path(_argv[i], &ref) ) | |
+ { | |
+ refs.AddRef("refs", &ref); | |
+ } | |
+ } | |
+ | |
+ be_app_messenger.SendMessage(&refs); | |
+} | |
+ | |
+ | |
+void | |
+TRApplication::MessageReceived(BMessage *msg) { | |
+ msg->PrintToStream(); | |
+ switch(msg->what){ | |
+ case TR_ADD: { | |
+ /* | |
+ * When the copy of a torrent file is complete, we get a message signaling | |
+ * that we should add the now copied .torrent file to our Transfer Window. | |
+ */ | |
+ entry_ref torrentRef; | |
+ if (msg->FindRef("torrent", &torrentRef) == B_OK) { | |
+ BEntry *entry = new BEntry(&torrentRef, true); | |
+ window->AddEntry(entry); | |
+ delete entry; | |
+ } | |
+ }break; | |
+ case TR_OPEN: { | |
+ /* Show the Open Dialog */ | |
+ openPanel->Show(); | |
+ }break; | |
+ case TR_SETTINGS:{ | |
+ settings = new TRPrefsWindow(); | |
+ settings->MoveTo(window->Frame().left + (window->Frame().Width() - settings->Frame().Width()) / 2, | |
+ window->Frame().top + (window->Frame().Height() - settings->Frame().Height()) / 2); | |
+ settings->Show(); | |
+ }break; | |
+ case TR_RELOAD_SETTINGS: { | |
+ window->LoadSettings(); | |
+ }break; | |
+ default: { | |
+ msg->PrintToStream(); | |
+ BApplication::MessageReceived(msg); | |
+ }break; | |
+ } | |
+} | |
+ | |
+ | |
+bool | |
+TRApplication::QuitRequested() { | |
+ return BApplication::QuitRequested(); | |
+} | |
+ | |
+ | |
+/** | |
+ * Thread Function. | |
+ * The thread copies the original .torrent file into the torrents folder, | |
+ * then signals the window to load the torrent meta info from the copy. | |
+ * The torrent window will then node_monitor the file so that if it's removed | |
+ * the torrent will cease. | |
+ * | |
+ * Keeping the file copy in a separate thread keeps us from blocking up the | |
+ * rest of our program. | |
+ * | |
+ * This behavior lets us kill the original .torrent download from disc (which | |
+ * speaking as a user I have a tendency to do) but keep the meta around for | |
+ * Transmission to use later. | |
+ * | |
+ * If the user whacks the .torrent file from our settings folder, then we'll | |
+ * remove it from the list (node_monitor!) and if they remove it from our list | |
+ * we'll remove it from the file system. | |
+ */ | |
+int32 | |
+TRApplication::Copy(void *data) { | |
+ entry_ref *ref = (entry_ref*)data; | |
+ | |
+ BFile source(ref, B_READ_ONLY); | |
+ BFile target(((TRApplication*)be_app)->TorrentDir(), ref->name, | |
+ B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS); | |
+ | |
+ BEntry targetEntry(((TRApplication*)be_app)->TorrentDir(), ref->name); | |
+ entry_ref targetRef; | |
+ targetEntry.GetRef(&targetRef); | |
+ | |
+ // Only perform the copy if the target is freshly created. Everything is B_OK! | |
+ if (source.InitCheck() == B_OK && target.InitCheck() == B_OK) { | |
+ if (target.Lock() == B_OK) { | |
+ char *buffer = (char*)calloc(1, 4096); // 4k data buffer. | |
+ ssize_t read = 0; | |
+ while ((read = source.Read(buffer, 4096)) > 0) { | |
+ target.Write(buffer, read); | |
+ } | |
+ free(buffer); | |
+ target.Unlock(); | |
+ } | |
+ } | |
+ | |
+ BMessage msg(TR_ADD); | |
+ msg.AddRef("torrent", &targetRef); | |
+ BMessenger messenger(be_app); | |
+ messenger.SendMessage(&msg); | |
+ | |
+ free(ref); | |
+ return B_OK; | |
+} | |
+ | |
+ | |
+/** | |
+ * Filters the FilePanel for torrent files and directories. | |
+ */ | |
+bool | |
+TRFilter::Filter(const entry_ref *ref, BNode *node, struct stat_beos *, const char *mimetype) { | |
+ return (node->IsDirectory() || | |
+ (strcmp(mimetype, "application/x-bittorrent") == 0) || | |
+ (strstr(ref->name, ".torrent") != NULL)); | |
+ | |
+} | |
+ | |
+ | |
+TRWindow* | |
+TRApplication::GetWindow(void) | |
+{ | |
+ return window; | |
+} | |
+ | |
+ | |
+BBitmap* | |
+TRApplication::GetBitmap(type_code type, uint32 id, BRect rect,color_space space) | |
+{ | |
+ size_t size = 0; | |
+ BBitmap *bitmap = NULL; | |
+ app_info runningInfo; | |
+ GetAppInfo(&runningInfo); | |
+ BFile appFile(&(runningInfo.ref), B_READ_ONLY); | |
+ BResources res(&appFile, false); | |
+ | |
+ const void *data = res.LoadResource(type, id, &size); | |
+ if (data){ | |
+ bitmap = new BBitmap(rect, space); | |
+ bitmap->SetBits(data, size, 0, space); | |
+ } | |
+ return bitmap; | |
+} | |
Index: haiku/.Makefile | |
=================================================================== | |
--- haiku/.Makefile (revision 0) | |
+++ haiku/.Makefile (revision 0) | |
@@ -0,0 +1,14 @@ | |
+CC=g++ | |
+OBJ=Test.o | |
+CFLAGS=-I .. -I libPrefs -I ../libtransmission | |
+ | |
+%.o: %.cpp | |
+ $(CC) -c -o $@ $< $(CFLAGS) | |
+ | |
+haiku: $(OBJ) | |
+ $(CC) -o $@ $< $(CFLAGS) | |
+ | |
+.PHONY: clean | |
+ | |
+clean: | |
+ rm -f ./*.o | |
Index: haiku/TRListView.cpp | |
=================================================================== | |
--- haiku/TRListView.cpp (revision 0) | |
+++ haiku/TRListView.cpp (revision 0) | |
@@ -0,0 +1,87 @@ | |
+/****************************************************************************** | |
+ * $Id: TRListView.cpp | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#include "TRListView.h" | |
+#include "TRApplication.h" | |
+ | |
+#include <stdio.h> | |
+ | |
+TRListView::TRListView(BRect frame, const char *name) | |
+ : BListView(frame, name, B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL) | |
+{ | |
+ popupmenu = ((TRApplication *)be_app)->GetWindow()->GetTorrentMenu(); | |
+} | |
+ | |
+ | |
+TRListView::~TRListView() | |
+{ | |
+ if (popupmenu != NULL) | |
+ delete popupmenu; | |
+} | |
+ | |
+ | |
+void | |
+TRListView::MessageReceived(BMessage *msg) { | |
+ switch(msg->what){ | |
+ case TR_INFO: { | |
+ ((TRApplication *)be_app)->GetWindow()->GetTorrentInfo(); | |
+ }break; | |
+ case TR_RESUME: { | |
+ ((TRApplication *)be_app)->GetWindow()->ResumeTorrent(); | |
+ }break; | |
+ case TR_PAUSE:{ | |
+ ((TRApplication *)be_app)->GetWindow()->PauseTorrent(); | |
+ }break; | |
+ case TR_REMOVE: { | |
+ ((TRApplication *)be_app)->GetWindow()->RemoveTorrent(); | |
+ }break; | |
+ default: { | |
+ BListView::MessageReceived(msg); | |
+ }break; | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TRListView::MouseDown(BPoint point) | |
+{ | |
+ int32 selection = BListView::IndexOf(point); | |
+ if(selection != -1){ | |
+ BPoint cursor; | |
+ uint32 buttons; | |
+ BListView::Select(selection); | |
+ MakeFocus(true); | |
+ GetMouse(&cursor, &buttons, true); | |
+ if (buttons & B_SECONDARY_MOUSE_BUTTON) { | |
+ BListView::ConvertToScreen(&point); | |
+ popupmenu->SetTargetForItems(this); | |
+ popupmenu->Go(point, true, true, true); | |
+ } | |
+ ((TRApplication *)be_app)->GetWindow()->UpdateList(selection, true, popupmenu); | |
+ } | |
+} | |
Index: haiku/TRPrefsWindow.cpp | |
=================================================================== | |
--- haiku/TRPrefsWindow.cpp (revision 0) | |
+++ haiku/TRPrefsWindow.cpp (revision 0) | |
@@ -0,0 +1,333 @@ | |
+ | |
+/****************************************************************************** | |
+ * $Id: TRTorrPrefWindow.cpp | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * ? | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#include "TRPrefsWindow.h" | |
+ | |
+TRPrefsWindow::TRPrefsWindow() | |
+ :BWindow(BRect(0, 0, 450, 300), "Settings", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | |
+ | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_CURRENT_WORKSPACE) | |
+{ | |
+ BRect tabRect, mainRect, buttonRect, boxRect, textRectTab1, textRectTab2, textRectTab3, textRectTab4; | |
+ BTab *tab; | |
+ | |
+ mainRect = Bounds(); | |
+ | |
+ font_height fh; | |
+ be_plain_font->GetHeight(&fh); | |
+ | |
+ fTrprefview = new BView(mainRect, "TRPrefView", B_FOLLOW_H_CENTER, NULL); | |
+ fTrprefview->SetViewColor(216,216,216,0); | |
+ | |
+ tabRect.Set(mainRect.left, mainRect.top, mainRect.right, mainRect.bottom-50); | |
+ tabRect.InsetBy(5,5); | |
+ | |
+ fTabView = new BTabView(tabRect, "tab_view"); | |
+ fTabView->SetViewColor(216,216,216,0); | |
+ | |
+ // Buttons | |
+ //-------------------------------------------------------------- | |
+ buttonRect = BRect(tabRect.left, tabRect.bottom+10, tabRect.left+60, tabRect.bottom+50); | |
+ fBtnDefaults = new BButton(buttonRect, "btnDefault", "Defaults", new BMessage(TR_PREF_DEFAULTS)); | |
+ fTrprefview->AddChild(fBtnDefaults); | |
+ | |
+ buttonRect.OffsetBy(buttonRect.Width() + 10, 0); | |
+ fBtnCancel = new BButton(buttonRect, "btnCancel", "Cancel", new BMessage(TR_PREF_CANCEL)); | |
+ fTrprefview->AddChild(fBtnCancel); | |
+ | |
+ buttonRect.OffsetBy(buttonRect.Width() + 10, 0); | |
+ fBtnSave = new BButton(buttonRect, "btnSave", "Save", new BMessage(TR_PREF_SAVE)); | |
+ fBtnSave->MakeDefault(true); | |
+ fTrprefview->AddChild(fBtnSave); | |
+ | |
+ boxRect = BRect(tabRect.left, tabRect.top, tabRect.right-25, tabRect.bottom-45); | |
+ | |
+ //Tab General | |
+ //-------------------------------------------------------------- | |
+ tab = new BTab(); | |
+ tab->SetLabel("General"); | |
+ fGeneral = new BView(tabRect, "General", B_FOLLOW_H_CENTER, NULL); | |
+ fGeneral->SetViewColor(216,216,216,0); | |
+ fBoxGeneral = new BBox(boxRect, NULL, B_FOLLOW_ALL); | |
+ fGeneral->AddChild(fBoxGeneral); | |
+ fTabView->AddTab(fGeneral, tab); | |
+ | |
+ /* | |
+ //Tab Transfers | |
+ //-------------------------------------------------------------- | |
+ tab = new BTab(); | |
+ tab->SetLabel("Transfers"); | |
+ fTransfer = new BView(tabRect, "Transfers", B_FOLLOW_H_CENTER, NULL); | |
+ fTransfer->SetViewColor(216,216,216,0); | |
+ fBoxTransfer = new BBox(boxRect, NULL, B_FOLLOW_ALL); | |
+ fTransfer->AddChild(fBoxTransfer); | |
+ fTabView->AddTab(fTransfer, tab); | |
+ */ | |
+ | |
+ //Tab Bandwidth | |
+ //-------------------------------------------------------------- | |
+ tab = new BTab(); | |
+ tab->SetLabel("Bandwidth"); | |
+ fBandwidth = new BView(tabRect, "Bandwidth", B_FOLLOW_H_CENTER, NULL); | |
+ fBandwidth->SetViewColor(216,216,216,0); | |
+ fBoxBandwidth = new BBox(boxRect, NULL, B_FOLLOW_ALL); | |
+ fBandwidth->AddChild(fBoxBandwidth); | |
+ fTabView->AddTab(fBandwidth, tab); | |
+ | |
+ //Tab Network | |
+ //-------------------------------------------------------------- | |
+ tab = new BTab(); | |
+ tab->SetLabel("Network"); | |
+ fNetwork = new BView(tabRect, "Network", B_FOLLOW_H_CENTER, NULL); | |
+ fNetwork->SetViewColor(216,216,216,0); | |
+ fBoxNetwork = new BBox(boxRect, NULL, B_FOLLOW_ALL); | |
+ fNetwork->AddChild(fBoxNetwork); | |
+ fTabView->AddTab(fNetwork, tab); | |
+ | |
+ // General Tab stuff | |
+ //-------------------------------------------------------------- | |
+ textRectTab1.Set(5, 5, boxRect.Width() - 10, fh.leading + fh.ascent + 10); | |
+ fTxtFolder = new BTextControl(textRectTab1, "txtFolder", "Download directory:", "", | |
+ NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT); | |
+ fBoxGeneral->AddChild(fTxtFolder); | |
+ | |
+ | |
+ // Bandwidth Tab stuff | |
+ //-------------------------------------------------------------- | |
+ textRectTab3.Set(5, 5, boxRect.Width() - 10, fh.leading + fh.ascent + 10); | |
+ fTxtTotalDownload = new BTextControl(textRectTab3, "txtTotalDownload", "Total Download Limit (KB/sec):", "", | |
+ NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT); | |
+ fBoxBandwidth->AddChild(fTxtTotalDownload); | |
+ | |
+ textRectTab3.Set(textRectTab3.left, textRectTab3.bottom + 10, textRectTab3.right, | |
+ textRectTab3.bottom + fh.leading + fh.ascent + 15); | |
+ fTxtTotalUpload = new BTextControl(textRectTab3, "txtTotalUpload", "Total Upload Limit (KB/sec):", "", | |
+ NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT); | |
+ fBoxBandwidth->AddChild(fTxtTotalUpload); | |
+ | |
+ textRectTab3.Set(textRectTab3.left, textRectTab3.bottom + 10, textRectTab3.right, | |
+ textRectTab3.bottom + fh.leading + fh.ascent + 15); | |
+ fUnlimitDownload = new BCheckBox(textRectTab3, "chkUnlimitedDownload", | |
+ "Unlimit download speed", new BMessage(TR_PREF_UNLIMITDOWNLOAD)); | |
+ fBoxBandwidth->AddChild(fUnlimitDownload); | |
+ | |
+ textRectTab3.Set(textRectTab3.left, textRectTab3.bottom + 10, textRectTab3.right, | |
+ textRectTab3.bottom + fh.leading + fh.ascent + 15); | |
+ fUnlimitUpload = new BCheckBox(textRectTab3, "chkUnlimitedDownload", | |
+ "Unlimit download speed", new BMessage(TR_PREF_UNLIMITUPLOAD)); | |
+ fBoxBandwidth->AddChild(fUnlimitUpload); | |
+ | |
+ // Network Tab stuff | |
+ //-------------------------------------------------------------- | |
+ textRectTab4.Set(5, 5, boxRect.Width() - 10, fh.leading + fh.ascent + 10); | |
+ fTxtPort = new BTextControl(textRectTab4, "txtPort", "Listen On Port:", "", | |
+ NULL, B_FOLLOW_LEFT | B_FOLLOW_TOP | B_FOLLOW_RIGHT); | |
+ fBoxNetwork->AddChild(fTxtPort); | |
+ | |
+ Lock(); | |
+ fTrprefview->AddChild(fTabView); | |
+ AddChild(fTrprefview); | |
+ Unlock(); | |
+} | |
+ | |
+ | |
+TRPrefsWindow::~TRPrefsWindow() | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+TRPrefsWindow::MessageReceived(BMessage *msg) | |
+{ | |
+ switch(msg->what){ | |
+ case TR_PREF_SAVE:{ | |
+ if (WritePrefs()){ | |
+ Quit(); | |
+ }else{ | |
+ beep(); | |
+ } | |
+ }break; | |
+ case TR_PREF_CANCEL:{ | |
+ Quit(); | |
+ }break; | |
+ case TR_PREF_DEFAULTS:{ | |
+ SetDefaults(); | |
+ }break; | |
+ case TR_PREF_UNLIMITDOWNLOAD:{ | |
+ fTxtTotalDownload->SetEnabled(!fUnlimitDownload->Value()); | |
+ }break; | |
+ | |
+ case TR_PREF_UNLIMITUPLOAD:{ | |
+ fTxtTotalUpload->SetEnabled(!fUnlimitUpload->Value()); | |
+ }break; | |
+ default: { | |
+ BWindow::MessageReceived(msg); | |
+ }break; | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TRPrefsWindow::Show() | |
+{ | |
+ ReadPrefs(); | |
+ if (Lock()) | |
+ { | |
+ fTxtFolder->MakeFocus(true); | |
+ Unlock(); | |
+ } | |
+ BWindow::Show(); | |
+} | |
+ | |
+ | |
+void | |
+TRPrefsWindow::SetDefaults() | |
+{ | |
+ fTxtFolder->SetText(DOWNLOADFOLDER); | |
+ fTxtPort->SetText(PORT); | |
+ fTxtTotalUpload->SetText(TOTALUPLOADLIMIT); | |
+ fTxtTotalDownload->SetText(TOTALDOWNLOADLIMIT); | |
+ fTxtTotalDownload->SetEnabled(!CHECKBOXUNLIMITDOWNLOAD); | |
+ fTxtTotalUpload->SetEnabled(!CHECKBOXUNLIMITUPLOAD); | |
+ fUnlimitDownload->SetValue(CHECKBOXUNLIMITDOWNLOAD); | |
+ fUnlimitUpload->SetValue(CHECKBOXUNLIMITUPLOAD); | |
+} | |
+ | |
+ | |
+void | |
+TRPrefsWindow::ReadPrefs() | |
+{ | |
+ if (Lock()){ | |
+ TPreferences prefs(TRANSMISSION_SETTINGS); | |
+ BString str(""); | |
+ if (prefs.FindString("download.folder", &str) != B_OK){ | |
+ str << DOWNLOADFOLDER; | |
+ prefs.SetString("download.folder", str.String()); | |
+ } | |
+ fTxtFolder->SetText(str.String()); | |
+ | |
+ //----------------------------------------------------------- | |
+ int32 port; | |
+ if (prefs.FindInt32("transmission.bindPort", &port) != B_OK){ | |
+ port = atoi(PORT); | |
+ prefs.SetInt32("transmission.bindPort", port); | |
+ } | |
+ | |
+ str = ""; | |
+ str << port; | |
+ fTxtPort->SetText(str.String()); | |
+ | |
+ //--------------------------------------------------------------------- | |
+ int32 upload; | |
+ if (prefs.FindInt32("transmission.totalUploadLimit", &upload) != B_OK){ | |
+ upload = atoi(TOTALUPLOADLIMIT); | |
+ prefs.SetInt32("transmission.totalUploadLimit", upload); | |
+ } | |
+ str = ""; | |
+ str << upload; | |
+ fTxtTotalUpload->SetText(str.String()); | |
+ | |
+ //------------------------------------------------------------------------- | |
+ int32 download; | |
+ if (prefs.FindInt32("transmission.totalDownloadLimit", &download) != B_OK){ | |
+ download = atoi(TOTALDOWNLOADLIMIT); | |
+ prefs.SetInt32("transmission.totalDownloadLimit", download); | |
+ } | |
+ str = ""; | |
+ str << download; | |
+ fTxtTotalDownload->SetText(str.String()); | |
+ | |
+ //----------------------------------------------------------------------- | |
+ bool checkBoxDownload; | |
+ if (prefs.FindBool("transmission.checkBoxDownloadLimit", &checkBoxDownload) != B_OK){ | |
+ checkBoxDownload = CHECKBOXUNLIMITDOWNLOAD; | |
+ prefs.SetBool("transmission.checkBoxDownloadLimit", checkBoxDownload); | |
+ } | |
+ | |
+ fUnlimitDownload->SetValue(checkBoxDownload); | |
+ | |
+ if(checkBoxDownload) | |
+ fTxtTotalDownload->SetEnabled(!checkBoxDownload); | |
+ | |
+ //----------------------------------------------------------------------- | |
+ bool checkBoxUpload; | |
+ if (prefs.FindBool("transmission.checkBoxUploadLimit", &checkBoxUpload) != B_OK){ | |
+ checkBoxUpload = CHECKBOXUNLIMITUPLOAD; | |
+ prefs.SetBool("transmission.checkBoxUploadLimit", checkBoxUpload); | |
+ } | |
+ | |
+ fUnlimitUpload->SetValue(checkBoxUpload); | |
+ | |
+ if(checkBoxUpload) | |
+ fTxtTotalUpload->SetEnabled(!checkBoxUpload); | |
+ | |
+ Unlock(); | |
+ } | |
+} | |
+ | |
+ | |
+bool | |
+TRPrefsWindow::WritePrefs() | |
+{ | |
+ bool valid = true; | |
+ | |
+ int port = atoi(fTxtPort->Text()); | |
+ if (port <= 0) { | |
+ valid = false; | |
+ fTxtPort->MakeFocus(true); | |
+ } | |
+ | |
+ const char* uploadStr = fTxtTotalUpload->Text(); | |
+ int totaluploadLimit = atoi(fTxtTotalUpload->Text()); | |
+ int totaldownloadLimit = atoi(fTxtTotalDownload->Text()); | |
+ | |
+ for (uint i = 0; i < strlen(uploadStr) && valid; i++) { | |
+ if (!(uploadStr[i] >= '0' && uploadStr[i] <= '9')) { | |
+ valid = false; | |
+ fTxtTotalUpload->MakeFocus(true); | |
+ } | |
+ } | |
+ | |
+ if (valid) { | |
+ TPreferences *prefs = new TPreferences(TRANSMISSION_SETTINGS); | |
+ | |
+ prefs->SetInt32("transmission.bindPort", (int32)port); | |
+ prefs->SetString("download.folder", fTxtFolder->Text()); | |
+ prefs->SetInt32("transmission.totaluploadLimit", (int32)totaluploadLimit); | |
+ prefs->SetInt32("transmission.totalDownloadLimit", (int32)totaldownloadLimit); | |
+ prefs->SetBool("transmission.checkBoxDownloadLimit", fUnlimitDownload->Value()); | |
+ prefs->SetBool("transmission.checkBoxUploadLimit", fUnlimitUpload->Value()); | |
+ | |
+ delete prefs; | |
+ | |
+ be_app_messenger.SendMessage(new BMessage(TR_RELOAD_SETTINGS)); | |
+ } | |
+ | |
+ return valid; | |
+} | |
Index: haiku/TRWindow.cpp | |
=================================================================== | |
--- haiku/TRWindow.cpp (revision 0) | |
+++ haiku/TRWindow.cpp (revision 0) | |
@@ -0,0 +1,696 @@ | |
+/****************************************************************************** | |
+ * $Id: TRWindow.cpp | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * charles | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#include "TRWindow.h" | |
+ | |
+#include <Button.h> | |
+#include <View.h> | |
+#include <MenuBar.h> | |
+#include <Menu.h> | |
+#include <ListView.h> | |
+#include <ListItem.h> | |
+#include <ScrollView.h> | |
+#include <StringView.h> | |
+#include <MenuItem.h> | |
+#include <String.h> | |
+#include <NodeMonitor.h> | |
+#include <Roster.h> | |
+#include <Alert.h> | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+ | |
+#include "libtransmission/transmission.h" | |
+#include "libtransmission/utils.h" | |
+#include "libtransmission/variant.h" | |
+ | |
+#include "TPreferences.h" | |
+#include "TRApplication.h" | |
+#include "TRTransfer.h" | |
+#include "TRInfoWindow.h" | |
+ | |
+TRListView *TRWindow::fTorrentList = NULL; | |
+tr_session *TRWindow::fSession = NULL; | |
+ | |
+TRWindow::TRWindow() | |
+ : BWindow(BRect(100, 100, 550, 400), "Transmission", B_TITLED_WINDOW, | |
+ B_ASYNCHRONOUS_CONTROLS , B_CURRENT_WORKSPACE) | |
+{ | |
+ fSession = NULL; | |
+ stopping = false; | |
+ quitter = NULL; | |
+ TPreferences prefs(TRANSMISSION_SETTINGS); | |
+ | |
+ BRect *rectFrame = new BRect(); | |
+ if (prefs.FindRect("window.frame", rectFrame) == B_OK) { | |
+ MoveTo(rectFrame->LeftTop()); | |
+ ResizeTo(rectFrame->Width(), rectFrame->Height()); | |
+ } else { | |
+ rectFrame->Set(100, 100, 550, 400); | |
+ } | |
+ Lock(); | |
+ | |
+ BRect viewRect(0, 0, rectFrame->Width(), rectFrame->Height()); | |
+ | |
+ BMenuBar *menubar = new BMenuBar(viewRect, "MenuBar"); | |
+ BMenu *menu = new BMenu("File"); | |
+ menu->AddItem(new BMenuItem("Open", new BMessage(TR_OPEN), 'O', B_COMMAND_KEY)); | |
+ menu->FindItem(TR_OPEN)->SetTarget(be_app_messenger); // send OPEN to the be_app. | |
+ menu->AddSeparatorItem(); | |
+ menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 'Q', B_COMMAND_KEY)); | |
+ menubar->AddItem(menu); | |
+ | |
+ menu = new BMenu("Torrent"); | |
+ menu->AddItem(new BMenuItem("Get Info", new BMessage(TR_INFO), 'I', B_COMMAND_KEY)); | |
+ menu->FindItem(TR_INFO)->SetEnabled(false); | |
+ menu->AddSeparatorItem(); | |
+ menu->AddItem(new BMenuItem("Resume", new BMessage(TR_RESUME))); | |
+ menu->AddItem(new BMenuItem("Pause", new BMessage(TR_PAUSE))); | |
+ menu->AddItem(new BMenuItem("Remove", new BMessage(TR_REMOVE))); | |
+ menubar->AddItem(menu); | |
+ | |
+ menu = new BMenu("Tools"); | |
+ menu->AddItem(new BMenuItem("Settings", new BMessage(TR_SETTINGS))); | |
+ menu->FindItem(TR_SETTINGS)->SetTarget(be_app_messenger); | |
+ menu->AddSeparatorItem(); | |
+ menu->AddItem(new BMenuItem("About Transmission", new BMessage(B_ABOUT_REQUESTED))); | |
+ menu->FindItem(B_ABOUT_REQUESTED)->SetTarget(be_app_messenger); | |
+ menubar->AddItem(menu); | |
+ | |
+ AddChild(menubar); | |
+ SetKeyMenuBar(menubar); | |
+ | |
+ // TODO: Tool Bar? (Well after everything is working based on Menus) | |
+ | |
+ // Setup the transfers ListView | |
+ viewRect.Set(2, menubar->Frame().bottom + 3, rectFrame->Width() - 2 - B_V_SCROLL_BAR_WIDTH, rectFrame->Height() - 2); | |
+ TRWindow::fTorrentList = new TRListView(viewRect, "TorrentList"); | |
+ TRWindow::fTorrentList->SetSelectionMessage(new BMessage(TR_SELECT)); | |
+ AddChild(new BScrollView("TransferScroller", fTorrentList, B_FOLLOW_ALL, 0, false, true)); | |
+ | |
+ Unlock(); | |
+ delete rectFrame; | |
+ | |
+ // Bring up the Transmission Engine (the code is from transmission.h comments) | |
+ tr_variant settings; | |
+ const char *configDir = tr_getDefaultConfigDir("Transmission"); | |
+ tr_variantInitDict(&settings, 0); | |
+ tr_sessionGetDefaultSettings(&settings); | |
+ fSession = tr_sessionInit("haiku", configDir, true, &settings); | |
+ tr_variantFree(&settings); | |
+ fCtor = tr_ctorNew(fSession); | |
+ LoadSettings(); | |
+ | |
+ // These functions define conversion constants between different size types | |
+ tr_formatter_size_init(1024, "KiB", "MiB", "GiB", "TiB"); | |
+ // If you remove this line, then the speed of ther torrents will be shown as nan or inf | |
+ tr_formatter_speed_init(1000, "kB/s", "mB/s", "gB/s", "tB/s"); | |
+ tr_formatter_mem_init(1024, "KiB", "MiB", "GiB", "TiB"); | |
+ | |
+ SetSizeLimits(Bounds().Width(), 250.0f, 32767.0f, 32767.0f); | |
+ | |
+ UpdateList(-1, true, NULL); | |
+ | |
+ // Start the message loop without showing the window. | |
+ Hide(); | |
+ Show(); | |
+} | |
+ | |
+ | |
+TRWindow::~TRWindow() { | |
+ tr_ctorFree(fCtor); | |
+ tr_sessionClose(fSession); | |
+ stop_watching(this); | |
+ delete quitter; | |
+} | |
+ | |
+ | |
+void | |
+TRWindow::LoadSettings() { | |
+ if (fSession != NULL) { | |
+ TPreferences prefs(TRANSMISSION_SETTINGS); | |
+ | |
+ int32 bindPort; | |
+ if (prefs.FindInt32("transmission.bindPort", &bindPort) != B_OK) { | |
+ bindPort = atoi(PORT); | |
+ prefs.SetInt32("transmission.bindPort", bindPort); | |
+ } | |
+ tr_sessionSetPeerPort(fSession, (int)bindPort); | |
+ | |
+ //--------------------------------------------------------------------- | |
+ int32 totalUploadLimit; | |
+ if (prefs.FindInt32("transmission.totalUploadLimit", &totalUploadLimit) != B_OK){ | |
+ totalUploadLimit = atoi(TOTALUPLOADLIMIT); | |
+ prefs.SetInt32("transmission.totalUploadLimit", totalUploadLimit); | |
+ } | |
+ | |
+ //------------------------------------------------------------------------- | |
+ int32 totalDownloadLimit; | |
+ if (prefs.FindInt32("transmission.totalDownloadLimit", &totalDownloadLimit) != B_OK){ | |
+ totalDownloadLimit = atoi(TOTALDOWNLOADLIMIT); | |
+ prefs.SetInt32("transmission.totalDownloadLimit", totalDownloadLimit); | |
+ } | |
+ | |
+ tr_sessionSetSpeedLimit_KBps(fSession, TR_DOWN, (int)totalDownloadLimit); | |
+ tr_sessionSetSpeedLimit_KBps(fSession, TR_UP, (int)totalUploadLimit); | |
+ } | |
+} | |
+ | |
+ | |
+/** | |
+ * Rescans the active Torrents folder, and will add all the torrents there to the | |
+ * engine. Called during initial Application Start & Stop. | |
+ */ | |
+void | |
+TRWindow::RescanTorrents() { | |
+ if (Lock()) { | |
+ TRApplication *app = dynamic_cast<TRApplication*>(be_app); | |
+ BEntry *torrentEntry = new BEntry(); | |
+ status_t err; | |
+ | |
+ if (app->TorrentDir()->InitCheck() == B_OK) { | |
+ err = app->TorrentDir()->Rewind(); | |
+ while (err == B_OK) { | |
+ err = app->TorrentDir()->GetNextEntry(torrentEntry, true); | |
+ if (err != B_ENTRY_NOT_FOUND) { | |
+ AddEntry(torrentEntry); | |
+ } | |
+ } | |
+ } | |
+ delete torrentEntry; | |
+ Unlock(); | |
+ } | |
+} | |
+ | |
+ | |
+/** | |
+ * Adds the file specified by *torrent to the Transmission engine. | |
+ * Then adds a new TRTransfer item in the fTorrentList. | |
+ * This item holds cached information about the torrent entry and node. | |
+ * These TRTransmission items are _NOT_ guaranteed to render the entry | |
+ * they were created from. | |
+ */ | |
+void | |
+TRWindow::AddEntry(BEntry *torrent) { | |
+ node_ref node; | |
+ if (torrent->GetNodeRef(&node) == B_OK) { | |
+ if (watch_node(&node, B_WATCH_NAME, this) == B_OK) { | |
+ BPath path; | |
+ torrent->GetPath(&path); | |
+ | |
+ // Try adding the torrent to the engine. | |
+ int error; | |
+ tr_ctorSetMetainfoFromFile(fCtor, path.Path()); | |
+ tr_torrent *nTorrent = tr_torrentNew(fCtor, &error); | |
+ if (nTorrent != NULL && Lock()) { // Success. Add the TRTorrent item. | |
+ fTorrentList->AddItem(new TRTransfer(path.Path(), node, nTorrent)); | |
+ | |
+ bool autoStart = true; | |
+ | |
+ // Decide if we should auto-start this torrent or not. | |
+ BString prefName("download."); | |
+ prefName << path.Path() << ".running"; | |
+ | |
+ TPreferences *prefs = new TPreferences(TRANSMISSION_SETTINGS); | |
+ if (prefs->FindBool(prefName.String(), &autoStart) != B_OK) { | |
+ autoStart = true; | |
+ } | |
+ delete prefs; | |
+ | |
+ if (autoStart) { | |
+ // Start the newly added torrent. | |
+ worker_info *startData = (worker_info*)calloc(1, sizeof(worker_info)); | |
+ startData->window = this; | |
+ startData->torrent = nTorrent; | |
+ thread_id start_thread = spawn_thread(TRWindow::AsynchStartTorrent, "BirthCanal", | |
+ B_NORMAL_PRIORITY, (void *)startData); | |
+ if (!((start_thread) < B_OK)) { | |
+ resume_thread(start_thread); | |
+ } else { // Fallback and start the old way. | |
+ StartTorrent(startData->torrent); | |
+ free(startData); | |
+ } | |
+ } | |
+ Unlock(); | |
+ } else { | |
+ bool duplicate = false; | |
+ TRTransfer* tr; | |
+ for (int32 i = 0; i < fTorrentList->CountItems(); i++) { | |
+ tr = (TRTransfer*)fTorrentList->ItemAt(i); | |
+ if (tr->GetCachedNodeRef() == node) { | |
+ duplicate = true; | |
+ } | |
+ } | |
+ if (!duplicate) { | |
+ BString errmsg("An error occurred trying to read "); | |
+ char namebuf[B_FILE_NAME_LENGTH]; | |
+ torrent->GetName(namebuf); | |
+ errmsg << namebuf; | |
+ errmsg << "."; | |
+ | |
+ BAlert *error = new BAlert("Error Opening Torrent", | |
+ errmsg.String(), | |
+ "Ok", NULL, NULL, | |
+ B_WIDTH_AS_USUAL, B_WARNING_ALERT); | |
+ error->Go(); | |
+ torrent->Remove(); | |
+ } | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TRWindow::MessageReceived(BMessage *msg) { | |
+// msg->PrintToStream(); | |
+ switch(msg->what){ | |
+ case B_NODE_MONITOR:{ | |
+ /* | |
+ * The only messages we receive from the node_monitor are if we need to | |
+ * stop watching the node. Basically, if it's been moved or removed we stop. | |
+ */ | |
+ node_ref node; | |
+ ino_t fromDir; | |
+ ino_t toDir; | |
+ int32 opcode; | |
+ | |
+ if ((msg->FindInt32("opcode", &opcode) == B_OK) && | |
+ (msg->FindInt64("node", &node.node) == B_OK) && | |
+ (msg->FindInt32("device", &node.device) == B_OK)) | |
+ { | |
+ bool stop = (opcode == B_ENTRY_REMOVED); | |
+ | |
+ if (stop) { | |
+ msg->FindInt64("directory", &toDir); | |
+ } else { // It must have moved. | |
+ stop = ((msg->FindInt64("from directory", &fromDir) == B_OK) && | |
+ (msg->FindInt64("to directory", &toDir) == B_OK) && | |
+ (toDir != fromDir)); | |
+ } | |
+ | |
+ if (stop) { | |
+ watch_node(&node, B_STOP_WATCHING, this); | |
+ | |
+ /* Find the full path from the TRTorrents. The index of the | |
+ * TRTorrent that is caching the information IS NOT the | |
+ * index of the torrent in the engine. These are Totally | |
+ * decoupled, due to the way transmission is written. | |
+ */ | |
+ remove_info *removeData = (remove_info*)calloc(1, sizeof(remove_info)); | |
+ removeData->window = this; | |
+ TRTransfer* item; | |
+ for (int32 i = 0; i < fTorrentList->CountItems(); i++) { | |
+ item = (TRTransfer*)fTorrentList->ItemAt(i); | |
+ if (item->GetCachedNodeRef() == node) { | |
+ strcpy(removeData->path, item->GetCachedPath()); | |
+ } | |
+ } | |
+ | |
+ fTorrentList->DoForEach(TRWindow::RemovePath, (void *)removeData); | |
+ free(removeData); | |
+ } | |
+ } | |
+ }break; | |
+ case TR_INFO: { | |
+ GetTorrentInfo(); | |
+ }break; | |
+ case TR_RESUME: { | |
+ ResumeTorrent(); | |
+ }break; | |
+ case TR_PAUSE:{ | |
+ PauseTorrent(); | |
+ }break; | |
+ case TR_REMOVE: { | |
+ RemoveTorrent(); | |
+ }break; | |
+ case B_SIMPLE_DATA:{ | |
+ be_app->RefsReceived(msg); | |
+ }break; | |
+ default: { | |
+ //msg->PrintToStream(); | |
+ BWindow::MessageReceived(msg); | |
+ }break; | |
+ } | |
+} | |
+ | |
+ | |
+/** | |
+ * Handles QuitRequests. | |
+ * Displays a BAlert asking if the user really wants to quit if torrents are running. | |
+ * If affimative, then we'll stop all the running torrents. | |
+ */ | |
+bool | |
+TRWindow::QuitRequested() { | |
+ if (stopping) | |
+ return true; | |
+ | |
+ bool quit = false; | |
+ int running; | |
+ | |
+ quit_info *quitData = (quit_info*)calloc(1, sizeof(quit_info)); | |
+ quitData->running = 0; | |
+ fTorrentList->DoForEach(TRWindow::CheckQuitStatus, (void *)quitData); | |
+ running = quitData->running; | |
+ free(quitData); | |
+ | |
+ if (running > 0) { | |
+ BString quitMsg(""); | |
+ quitMsg << "There's " << running << " torrent"; | |
+ if (running > 1) { | |
+ quitMsg << "s"; | |
+ } | |
+ quitMsg << " currently running.\n" | |
+ << "What would you like to do?"; | |
+ | |
+ BAlert *confirmQuit = new BAlert("Confirm Quit", quitMsg.String(), | |
+ "Cancel", "Quit", NULL, | |
+ B_WIDTH_AS_USUAL, B_WARNING_ALERT); | |
+ quit = (confirmQuit->Go() == 1); | |
+ } else { | |
+ quit = true; | |
+ } | |
+ | |
+ if (quit) { | |
+ TPreferences *prefs = new TPreferences(TRANSMISSION_SETTINGS); | |
+ // Save window position | |
+ prefs->SetRect("window.frame", Frame()); | |
+ | |
+ // For each torrent in the list save whether it's running or not to the preferences | |
+ BString strItem(""); | |
+ for (int i = 0; i < fTorrentList->CountItems(); i++) { | |
+ // String that looks like "download.%torrent_name%.running" | |
+ // Used to find the particular torrent in the preferences | |
+ strItem = "download."; | |
+ tr_torrent *torrent = (dynamic_cast<TRTransfer*>(fTorrentList->ItemAt(i)))->GetTorrent(); | |
+ const tr_info *info = tr_torrentInfo(torrent); | |
+ const tr_stat *stat = tr_torrentStat(torrent); | |
+ | |
+ strItem << info->torrent << ".running"; | |
+ if ((stat->activity == TR_STATUS_CHECK_WAIT) || | |
+ (stat->activity == TR_STATUS_CHECK) || | |
+ (stat->activity == TR_STATUS_DOWNLOAD) || | |
+ (stat->activity == TR_STATUS_DOWNLOAD_WAIT) || | |
+ (stat->activity == TR_STATUS_SEED) || | |
+ (stat->activity == TR_STATUS_SEED_WAIT)) { | |
+ prefs->SetBool(strItem.String(), true); | |
+ tr_torrentStop(torrent); | |
+ } else { | |
+ prefs->SetBool(strItem.String(), false); | |
+ } | |
+ } | |
+ delete prefs; | |
+ | |
+ // Display alert if we have some torrents to exit from | |
+ if (running > 0) { | |
+ stopping = true; | |
+ BAlert *waiting = new BAlert("Stopping Torrents", "Waiting for torrents to stop...", "Quit"); | |
+ quitter = new BInvoker(new BMessage(B_QUIT_REQUESTED), BMessenger(be_app)); | |
+ waiting->Go(quitter); | |
+ quit = false; | |
+ } else { | |
+ be_app->PostMessage(new BMessage(B_QUIT_REQUESTED)); | |
+ } | |
+ } | |
+ return quit; | |
+} | |
+ | |
+ | |
+void | |
+TRWindow::FrameResized(float, float) { | |
+ fTorrentList->Invalidate(); | |
+} | |
+ | |
+ | |
+/** | |
+ * Called from the StopTorrent thread. | |
+ */ | |
+void | |
+TRWindow::StopTorrent(tr_torrent *torrent) { | |
+ tr_torrentStop(torrent); | |
+ | |
+ UpdateList(fTorrentList->CurrentSelection(), true, NULL); | |
+} | |
+ | |
+ | |
+BString | |
+TRWindow::GetFolder(void) { | |
+ // Read folder from the preferences | |
+ BString folder(""); | |
+ TPreferences *prefs = new TPreferences(TRANSMISSION_SETTINGS); | |
+ if (prefs->FindString("download.folder", &folder) != B_OK) { | |
+ prefs->SetString("download.folder", "/boot/home/Downloads"); | |
+ folder << "/boot/home/Downloads"; | |
+ } | |
+ delete prefs; | |
+ return folder; | |
+} | |
+ | |
+ | |
+/** | |
+ * Called from StartTorrent thread. | |
+ */ | |
+void | |
+TRWindow::StartTorrent(tr_torrent *torrent) { | |
+ // FIXME: transmission.h comments recommend NOT to use tr_torrentSetDownloadDir | |
+ tr_torrentSetDownloadDir(torrent, GetFolder().String()); | |
+ tr_torrentStart(torrent); | |
+ | |
+ if (fTorrentList->CurrentSelection() >= 0) { | |
+ UpdateList(fTorrentList->CurrentSelection(), true, NULL); | |
+ } | |
+} | |
+ | |
+ | |
+/** | |
+ * Called from the be_app Pulse(); | |
+ * This will update the data structures that the TRTorrents use to render, | |
+ * and invalidate the view. | |
+ */ | |
+void | |
+TRWindow::UpdateList(int32 selection = -1, bool menus = true, BPopUpMenu *popUpMenu = NULL) { | |
+ if (stopping) | |
+ { | |
+ quit_info *quitData = (quit_info*)calloc(1, sizeof(quit_info)); | |
+ quitData->running = 0; | |
+ fTorrentList->DoForEach(TRWindow::CheckQuitStatus, (void *)quitData); | |
+ if (quitData->running == 0) | |
+ be_app->PostMessage(new BMessage(B_QUIT_REQUESTED)); | |
+ free(quitData); | |
+ } | |
+ | |
+ update_info *upData = (update_info*)calloc(1, sizeof(update_info)); | |
+ upData->running = false; | |
+ upData->selected = selection; | |
+ upData->invalid = 0; | |
+ | |
+ fTorrentList->DoForEach(TRWindow::UpdateStats, (void *)upData); | |
+ | |
+ if (menus) { | |
+ KeyMenuBar()->FindItem(TR_INFO)->SetEnabled(selection >= 0); | |
+ KeyMenuBar()->FindItem(TR_RESUME)->SetEnabled(selection >= 0 && !upData->running); | |
+ KeyMenuBar()->FindItem(TR_PAUSE)->SetEnabled(selection >= 0 && upData->running); | |
+ KeyMenuBar()->FindItem(TR_REMOVE)->SetEnabled(selection >= 0 && !upData->running); | |
+ } | |
+ | |
+ if(popUpMenu != NULL) | |
+ { | |
+ popUpMenu->FindItem(TR_INFO)->SetEnabled(selection >= 0); | |
+ popUpMenu->FindItem(TR_RESUME)->SetEnabled(selection >= 0 && !upData->running); | |
+ popUpMenu->FindItem(TR_PAUSE)->SetEnabled(selection >= 0 && upData->running); | |
+ popUpMenu->FindItem(TR_REMOVE)->SetEnabled(selection >= 0 && !upData->running); | |
+ } | |
+ | |
+ if (upData->invalid > 0 && fTorrentList->LockLooper()) { | |
+ fTorrentList->Invalidate(); | |
+ fTorrentList->UnlockLooper(); | |
+ } | |
+ | |
+ free(upData); | |
+} | |
+ | |
+ | |
+/** | |
+ * Thread Function to stop Torrents. This can be expensive and causes the event loop to | |
+ * choke. | |
+ */ | |
+int32 | |
+TRWindow::AsynchStopTorrent(void *data) { | |
+ worker_info* stopData = (worker_info*)data; | |
+ stopData->window->StopTorrent(stopData->torrent); | |
+ free(stopData); | |
+ return B_OK; | |
+} | |
+ | |
+ | |
+/** | |
+ * Thread Function to start Torrents. This can be expensive and causes the event loop to | |
+ * choke. | |
+ */ | |
+int32 | |
+TRWindow::AsynchStartTorrent(void *data) { | |
+ worker_info* startData = (worker_info*)data; | |
+ startData->window->StartTorrent(startData->torrent); | |
+ free(startData); | |
+ return B_OK; | |
+} | |
+ | |
+ | |
+/** | |
+ * Invoked by DoForEach upon the fTorrentList list. This will | |
+ * remove the item that is caching the path specified by | |
+ * path. | |
+ */ | |
+bool | |
+TRWindow::RemovePath(BListItem *item, void *data) { | |
+ remove_info* removeData = (remove_info*)data; | |
+ TRTransfer *transfer = dynamic_cast<TRTransfer*>(item); | |
+ | |
+ if (strcmp(transfer->GetCachedPath(), removeData->path) == 0) { | |
+ removeData->window->fTorrentList->RemoveItem(transfer); | |
+ return true; | |
+ } | |
+ return false; | |
+} | |
+ | |
+ | |
+/** | |
+ * Invoked during QuitRequested, iterates all Transfers and | |
+ * checks to see how many are running. | |
+ */ | |
+bool | |
+TRWindow::CheckQuitStatus(BListItem *item, void *data) { | |
+ quit_info* quitData = (quit_info*)data; | |
+ TRTransfer *transfer = dynamic_cast<TRTransfer*>(item); | |
+ | |
+ if (transfer->IsRunning()) { | |
+ quitData->running++; | |
+ } | |
+ return false; | |
+} | |
+ | |
+ | |
+/** | |
+ * Invoked during UpdateList() | |
+ */ | |
+bool | |
+TRWindow::UpdateStats(BListItem *item, void *data) { | |
+ update_info* upData = (update_info*)data; | |
+ TRTransfer *transfer = dynamic_cast<TRTransfer*>(item); | |
+ | |
+ int32 index = fTorrentList->IndexOf(transfer); | |
+ if (transfer->UpdateStatus(tr_torrentStat(transfer->GetTorrent()), index % 2 == 0)) { | |
+ upData->invalid++; | |
+ } | |
+ | |
+ if (index == upData->selected) { | |
+ upData->running = transfer->IsRunning(); | |
+ } | |
+ | |
+ return false; | |
+} | |
+ | |
+ | |
+BPopUpMenu* | |
+TRWindow::GetTorrentMenu() | |
+{ | |
+ BPopUpMenu * temp = new BPopUpMenu("Torrent", false, false); | |
+ temp->AddItem(new BMenuItem("Get Info", new BMessage(TR_INFO), 'I', B_COMMAND_KEY)); | |
+ temp->FindItem(TR_INFO)->SetEnabled(false); | |
+ temp->AddSeparatorItem(); | |
+ temp->AddItem(new BMenuItem("Resume", new BMessage(TR_RESUME))); | |
+ temp->AddItem(new BMenuItem("Pause", new BMessage(TR_PAUSE))); | |
+ temp->AddItem(new BMenuItem("Remove", new BMessage(TR_REMOVE))); | |
+ temp->SetTargetForItems(this); | |
+ return temp; | |
+} | |
+ | |
+ | |
+void | |
+TRWindow::GetTorrentInfo() | |
+{ | |
+ // Display an Info Window | |
+ TRTransfer *transfer = dynamic_cast<TRTransfer*>(fTorrentList->ItemAt(fTorrentList->CurrentSelection())); | |
+ const tr_stat *s = tr_torrentStat(transfer->GetTorrent()); | |
+ const tr_info *i = tr_torrentInfo(transfer->GetTorrent()); | |
+ | |
+ TRInfoWindow *info = new TRInfoWindow(s, i, transfer->GetTorrent(), tr_torrentGetDownloadDir(transfer->GetTorrent())); | |
+ info->MoveTo(Frame().LeftTop() + BPoint(20, 25)); | |
+ info->Show(); | |
+} | |
+ | |
+ | |
+void | |
+TRWindow::ResumeTorrent() | |
+{ | |
+ worker_info *startData = (worker_info*)calloc(1, sizeof(worker_info)); | |
+ startData->window = this; | |
+ startData->torrent = (dynamic_cast<TRTransfer*>(fTorrentList->ItemAt(fTorrentList->CurrentSelection())))->GetTorrent(); | |
+ thread_id start_thread = spawn_thread(TRWindow::AsynchStartTorrent, "BirthCanal",B_NORMAL_PRIORITY, (void *)startData); | |
+ if (!((start_thread) < B_OK)) { | |
+ resume_thread(start_thread); | |
+ } else { // Fallback and start the old way. | |
+ StartTorrent(startData->torrent); | |
+ free(startData); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TRWindow::PauseTorrent() | |
+{ | |
+ worker_info *stopData = (worker_info*)calloc(1, sizeof(worker_info)); | |
+ stopData->window = this; | |
+ stopData->torrent = (dynamic_cast<TRTransfer*>(fTorrentList->ItemAt(fTorrentList->CurrentSelection())))->GetTorrent(); | |
+ thread_id stop_thread = spawn_thread(TRWindow::AsynchStopTorrent, "InUtero", | |
+ B_NORMAL_PRIORITY, (void *)stopData); | |
+ if (!((stop_thread) < B_OK)) { | |
+ resume_thread(stop_thread); | |
+ } else { // Fallback and stop it the old way. | |
+ StopTorrent(stopData->torrent); | |
+ free(stopData); | |
+ } | |
+} | |
+ | |
+ | |
+int | |
+RemoveFile(const char *filename) | |
+{ | |
+ BEntry(filename).Remove(); | |
+} | |
+ | |
+ | |
+void | |
+TRWindow::RemoveTorrent() { | |
+ int32 index = fTorrentList->CurrentSelection(); | |
+ // Remove the file from the filesystem. | |
+ TRTransfer *item = (TRTransfer*)fTorrentList->RemoveItem(index); | |
+ tr_torrentRemove(item->GetTorrent(), false, RemoveFile); | |
+ delete item; | |
+ UpdateList(fTorrentList->CurrentSelection(), true, NULL); | |
+} | |
Index: haiku/TRApplication.h | |
=================================================================== | |
--- haiku/TRApplication.h (revision 0) | |
+++ haiku/TRApplication.h (revision 0) | |
@@ -0,0 +1,79 @@ | |
+/****************************************************************************** | |
+ * $Id: TRApplication.h | |
+ * | |
+ * Authors: | |
+ * bvarner | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#ifndef TRAPPLICATION_H | |
+#define TRAPPLICATION_H | |
+ | |
+#include <Application.h> | |
+#include <Directory.h> | |
+#include <FilePanel.h> | |
+#include <Message.h> | |
+ | |
+#include "TRWindow.h" | |
+#include "TRPrefsWindow.h" | |
+ | |
+#include "TRDefault.h" | |
+ | |
+class TRWindow; | |
+class TRPrefsWindow; | |
+ | |
+class TRApplication : public BApplication { | |
+ public: | |
+ TRApplication(); | |
+ ~TRApplication(); | |
+ | |
+ virtual void AboutRequested(); | |
+ virtual void Pulse(); | |
+ virtual void ReadyToRun(); | |
+ virtual void RefsReceived(BMessage *message); | |
+ virtual void ArgvReceived(int32 _argc, char** _argv); | |
+ virtual bool QuitRequested(); | |
+ | |
+ virtual void MessageReceived(BMessage *message); | |
+ TRWindow* GetWindow(void); | |
+ BBitmap * GetBitmap(type_code type, uint32 id, BRect rect,color_space space); | |
+ | |
+ static int32 Copy(void *data); | |
+ | |
+ status_t InitCheck(); | |
+ inline BDirectory* TorrentDir() { return torrentDir; }; | |
+ private: | |
+ TRWindow *window; | |
+ TRPrefsWindow *settings; | |
+ BFilePanel *openPanel; | |
+ BDirectory *torrentDir; | |
+}; | |
+ | |
+/** Torrent File-Type Filter */ | |
+class TRFilter : public BRefFilter { | |
+public: | |
+ virtual bool Filter(const entry_ref *ref, BNode *node, | |
+ struct stat_beos *st, const char *mimetype); | |
+}; | |
+ | |
+#endif | |
Index: haiku/TRListView.h | |
=================================================================== | |
--- haiku/TRListView.h (revision 0) | |
+++ haiku/TRListView.h (revision 0) | |
@@ -0,0 +1,50 @@ | |
+/****************************************************************************** | |
+ * $Id: TRListView.h | |
+ * | |
+ * Authors: | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#ifndef TR_LIST_VIEW | |
+#define TR_LIST_VIEW | |
+ | |
+#include <ListView.h> | |
+#include <MenuBar.h> | |
+#include <MenuItem.h> | |
+#include <MenuField.h> | |
+#include <PopUpMenu.h> | |
+ | |
+#include "TRDefault.h" | |
+ | |
+class TRListView : public BListView { | |
+public: | |
+ TRListView(BRect frame, const char *name); | |
+ ~TRListView(); | |
+ virtual void MessageReceived(BMessage *msg); | |
+ virtual void MouseDown(BPoint where); | |
+private: | |
+ BPopUpMenu *popupmenu; | |
+ BMenuItem *mode1, *mode2, *preferences, *about; | |
+}; | |
+ | |
+#endif | |
Index: haiku/TRPrefsWindow.h | |
=================================================================== | |
--- haiku/TRPrefsWindow.h (revision 0) | |
+++ haiku/TRPrefsWindow.h (revision 0) | |
@@ -0,0 +1,72 @@ | |
+/****************************************************************************** | |
+ * $Id: TRPrefsWindow.h | |
+ * | |
+ * Authors: | |
+ * titer | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#ifndef TR_PREF_WIND | |
+#define TR_PREF_WIND | |
+ | |
+#include <stdlib.h> | |
+#include <stdio.h> | |
+ | |
+#include <Button.h> | |
+#include <TextControl.h> | |
+#include <Slider.h> | |
+#include <Window.h> | |
+#include <Beep.h> | |
+#include <Box.h> | |
+#include <Font.h> | |
+#include <String.h> | |
+#include <TabView.h> | |
+#include <View.h> | |
+#include <CheckBox.h> | |
+ | |
+#include "TRApplication.h" | |
+#include "TPreferences.h" | |
+ | |
+#include "TRDefault.h" | |
+ | |
+class TRPrefsWindow : public BWindow { | |
+public: | |
+ TRPrefsWindow(); | |
+ ~TRPrefsWindow(); | |
+ virtual void MessageReceived(BMessage *msg); | |
+ virtual void Show(); | |
+ | |
+private: | |
+ void ReadPrefs(); | |
+ void SetDefaults(); | |
+ bool WritePrefs(); | |
+ | |
+ BBox *fBoxGeneral, *fBoxTransfer, *fBoxBandwidth, *fBoxNetwork; | |
+ BTabView *fTabView; | |
+ BView *fTrprefview, *fGeneral, *fTransfer, *fBandwidth, *fNetwork; | |
+ BTextControl *fTxtFolder, *fTxtPort, *fTxtTotalDownload, *fTxtTotalUpload; | |
+ BButton *fBtnSave, *fBtnCancel, *fBtnDefaults; | |
+ BCheckBox *fUnlimitDownload, *fUnlimitUpload; | |
+}; | |
+ | |
+#endif /* TR_PREF_WIND */ | |
Index: haiku/TRWindow.h | |
=================================================================== | |
--- haiku/TRWindow.h (revision 0) | |
+++ haiku/TRWindow.h (revision 0) | |
@@ -0,0 +1,112 @@ | |
+/****************************************************************************** | |
+ * $Id: TRWindow.h | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * charles | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#ifndef TRWINDOW_H | |
+#define TRWINDOW_H | |
+ | |
+#include <Window.h> | |
+#include <Entry.h> | |
+#include <FilePanel.h> | |
+#include <ListItem.h> | |
+#include <ListView.h> | |
+#include "TRListView.h" | |
+ | |
+#include "libtransmission/transmission.h" | |
+ | |
+class TRWindow : public BWindow | |
+{ | |
+public: | |
+ TRWindow(); | |
+ ~TRWindow(); | |
+ | |
+ void MessageReceived(BMessage* msg); | |
+ virtual bool QuitRequested(); | |
+ virtual void FrameResized(float width, float height); | |
+ | |
+ static TRListView *fTorrentList; | |
+ static tr_session *fSession; | |
+ tr_ctor *fCtor; // Torrent constrictor | |
+ | |
+public: // TRWindow | |
+ void AddEntry(BEntry *torrent); | |
+ void InitTorrentMenu(); | |
+ void UpdateList(int32 selection, bool menus, BPopUpMenu *popUpMenu); | |
+ | |
+ void LoadSettings(); | |
+ BString GetFolder(); | |
+ void StopTorrent(tr_torrent *torrent); | |
+ void StartTorrent(tr_torrent *torrent); | |
+ | |
+ static int32 AsynchStopTorrent(void *data); | |
+ static int32 AsynchStartTorrent(void *data); | |
+ | |
+ void RescanTorrents(); | |
+ BPopUpMenu *GetTorrentMenu(); | |
+ void GetTorrentInfo(); | |
+ void ResumeTorrent(); | |
+ void PauseTorrent(); | |
+ void RemoveTorrent(); | |
+ | |
+private: | |
+ BFilePanel *openPanel; | |
+ | |
+ static bool RemovePath(BListItem *item, void *data); | |
+ static bool CheckQuitStatus(BListItem *item, void *data); | |
+ static bool UpdateStats(BListItem *item, void *data); | |
+ | |
+ bool stopping; | |
+ BInvoker *quitter; | |
+}; | |
+ | |
+/** | |
+ * Used to pass info off to the worker thread that runs AsynchStopTorrent | |
+ */ | |
+struct worker_info { | |
+ TRWindow *window; | |
+ tr_torrent *torrent; | |
+}; | |
+ | |
+struct remove_info { | |
+ TRWindow *window; | |
+ char path[B_FILE_NAME_LENGTH]; | |
+}; | |
+ | |
+struct quit_info { | |
+ TRWindow *window; | |
+ int running; | |
+}; | |
+ | |
+struct update_info { | |
+ TRWindow *window; | |
+ bool running; | |
+ int selected; | |
+ int invalid; | |
+}; | |
+ | |
+#endif | |
Index: haiku/libPrefs/LICENSE | |
=================================================================== | |
--- haiku/libPrefs/LICENSE (revision 0) | |
+++ haiku/libPrefs/LICENSE (revision 0) | |
@@ -0,0 +1,31 @@ | |
+---------------------- | |
+Be Sample Code License | |
+---------------------- | |
+ | |
+Copyright 1991-1999, Be Incorporated. | |
+All rights reserved. | |
+ | |
+Redistribution and use in source and binary forms, with or without | |
+modification, are permitted provided that the following conditions | |
+are met: | |
+ | |
+1. Redistributions of source code must retain the above copyright | |
+ notice, this list of conditions, and the following disclaimer. | |
+ | |
+2. Redistributions in binary form must reproduce the above copyright | |
+ notice, this list of conditions, and the following disclaimer in the | |
+ documentation and/or other materials provided with the distribution. | |
+ | |
+3. The name of the author may not be used to endorse or promote products | |
+ derived from this software without specific prior written permission. | |
+ | |
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR | |
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
+OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
Index: haiku/libPrefs/TPreferences.cpp | |
=================================================================== | |
--- haiku/libPrefs/TPreferences.cpp (revision 0) | |
+++ haiku/libPrefs/TPreferences.cpp (revision 0) | |
@@ -0,0 +1,188 @@ | |
+// | |
+// TPreferences | |
+// | |
+// Class for saving and loading preference information | |
+// via BMessages. | |
+// | |
+// Eric Shepherd | |
+// Gleb Posobin | |
+// | |
+ | |
+/* | |
+ Copyright 1999, Be Incorporated. All Rights Reserved. | |
+ This file may be used under the terms of the Be Sample Code License. | |
+*/ | |
+ | |
+#include <Message.h> | |
+#include <Entry.h> | |
+#include <Messenger.h> | |
+#include <File.h> | |
+#include <Directory.h> | |
+#include <Alert.h> | |
+#include <FindDirectory.h> | |
+ | |
+#include "TPreferences.h" | |
+ | |
+ | |
+// | |
+// TPreferences::TPreferences | |
+// | |
+// Open the settings file and read the data in. | |
+// | |
+TPreferences::TPreferences(const char *filename) : BMessage('pref') { | |
+ BFile file; | |
+ | |
+ status = find_directory(B_COMMON_SETTINGS_DIRECTORY, &path); | |
+ if (status != B_OK) { | |
+ return; | |
+ } | |
+ | |
+ path.Append(filename); | |
+ status = file.SetTo(path.Path(), B_READ_ONLY); | |
+ if (status == B_OK) { | |
+ status = Unflatten(&file); | |
+ } | |
+} | |
+ | |
+ | |
+// | |
+// TPreferences::~TPreferences | |
+// | |
+// Write the preferences to disk. | |
+// | |
+TPreferences::~TPreferences() { | |
+ BFile file; | |
+ bool error = false; | |
+ BPath parentFolder; | |
+ path.GetParent(&parentFolder); | |
+ BEntry entry(parentFolder.Path(), true); | |
+ | |
+ if (!entry.Exists()) { | |
+ if (create_directory(parentFolder.Path(), 0755) != B_OK) { | |
+ error = true; | |
+ } | |
+ } | |
+ | |
+ if (!error && file.SetTo(path.Path(), B_WRITE_ONLY | B_CREATE_FILE) == B_OK) { | |
+ Flatten(&file); | |
+ } | |
+ else { | |
+ error = true; | |
+ } | |
+ | |
+ if (error) { | |
+ BAlert *alert = new BAlert("settingsFileAlert", | |
+ "Failed to open or create settings file. Settings will not be saved", | |
+ "OK", NULL, NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_WARNING_ALERT); | |
+ alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); | |
+ alert->Go(); | |
+ } | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetBool(const char *name, bool b) { | |
+ if (HasBool(name)) { | |
+ return ReplaceBool(name, 0, b); | |
+ } | |
+ return AddBool(name, b); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetInt8(const char *name, int8 i) { | |
+ if (HasInt8(name)) { | |
+ return ReplaceInt8(name, 0, i); | |
+ } | |
+ return AddInt8(name, i); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetInt16(const char *name, int16 i) { | |
+ if (HasInt16(name)) { | |
+ return ReplaceInt16(name, 0, i); | |
+ } | |
+ return AddInt16(name, i); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetInt32(const char *name, int32 i) { | |
+ if (HasInt32(name)) { | |
+ return ReplaceInt32(name, 0, i); | |
+ } | |
+ return AddInt32(name, i); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetInt64(const char *name, int64 i) { | |
+ if (HasInt64(name)) { | |
+ return ReplaceInt64(name, 0, i); | |
+ } | |
+ return AddInt64(name, i); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetFloat(const char *name, float f) { | |
+ if (HasFloat(name)) { | |
+ return ReplaceFloat(name, 0, f); | |
+ } | |
+ return AddFloat(name, f); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetDouble(const char *name, double f) { | |
+ if (HasDouble(name)) { | |
+ return ReplaceDouble(name, 0, f); | |
+ } | |
+ return AddDouble(name, f); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetString(const char *name, const char *s) { | |
+ if (HasString(name)) { | |
+ return ReplaceString(name, 0, s); | |
+ } | |
+ return AddString(name, s); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetPoint(const char *name, BPoint p) { | |
+ if (HasPoint(name)) { | |
+ return ReplacePoint(name, 0, p); | |
+ } | |
+ return AddPoint(name, p); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetRect(const char *name, BRect r) { | |
+ if (HasRect(name)) { | |
+ return ReplaceRect(name, 0, r); | |
+ } | |
+ return AddRect(name, r); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetMessage(const char *name, const BMessage *message) { | |
+ if (HasMessage(name)) { | |
+ return ReplaceMessage(name, 0, message); | |
+ } | |
+ return AddMessage(name, message); | |
+} | |
+ | |
+ | |
+status_t | |
+TPreferences::SetFlat(const char *name, const BFlattenable *obj) { | |
+ if (HasFlat(name, obj)) { | |
+ return ReplaceFlat(name, 0, (BFlattenable *) obj); | |
+ } | |
+ return AddFlat(name, (BFlattenable *) obj); | |
+} | |
Index: haiku/libPrefs/TPreferences.h | |
=================================================================== | |
--- haiku/libPrefs/TPreferences.h (revision 0) | |
+++ haiku/libPrefs/TPreferences.h (revision 0) | |
@@ -0,0 +1,48 @@ | |
+// | |
+// TPreferences | |
+// Using BMessages to save user settings. | |
+// | |
+// Eric Shepherd | |
+// | |
+/* | |
+ Copyright 1999, Be Incorporated. All Rights Reserved. | |
+ This file may be used under the terms of the Be Sample Code License. | |
+*/ | |
+ | |
+#ifndef __TPREFS_H__ | |
+#define __TPREFS_H__ | |
+ | |
+#include <Path.h> | |
+#include <Message.h> | |
+#include <string> | |
+ | |
+class _EXPORT TPreferences : public BMessage { | |
+ public: | |
+ TPreferences(const char *filename); | |
+ ~TPreferences(); | |
+ status_t InitCheck(void); | |
+ | |
+ status_t SetBool(const char *name, bool b); | |
+ status_t SetInt8(const char *name, int8 i); | |
+ status_t SetInt16(const char *name, int16 i); | |
+ status_t SetInt32(const char *name, int32 i); | |
+ status_t SetInt64(const char *name, int64 i); | |
+ status_t SetFloat(const char *name, float f); | |
+ status_t SetDouble(const char *name, double d); | |
+ status_t SetString(const char *name, const char *string); | |
+ status_t SetPoint(const char *name, BPoint p); | |
+ status_t SetRect(const char *name, BRect r); | |
+ status_t SetMessage(const char *name, const BMessage *message); | |
+ status_t SetFlat(const char *name, const BFlattenable *obj); | |
+ | |
+ private: | |
+ | |
+ BPath path; | |
+ status_t status; | |
+}; | |
+ | |
+inline status_t TPreferences::InitCheck(void) { | |
+ return status; | |
+} | |
+ | |
+#endif | |
Index: haiku/Makefile.am | |
=================================================================== | |
--- haiku/Makefile.am (revision 0) | |
+++ haiku/Makefile.am (revision 0) | |
@@ -0,0 +1,42 @@ | |
+AM_CPPFLAGS = -I@top_srcdir@ -I../libtransmission -IlibPrefs -Ilibhaiku -std=c++0x | |
+AM_CFLAGS = $(PTHREAD_CFLAGS) | |
+ | |
+noinst_HEADERS = \ | |
+ TRApplication.h \ | |
+ TRInfoWindow.h \ | |
+ TRPrefsWindow.h \ | |
+ TRTransfer.h \ | |
+ TRListView.h \ | |
+ TRColumn.h \ | |
+ TRField.h \ | |
+ libhaiku/ColumnListView.h \ | |
+ libhaiku/ColumnTypes.h \ | |
+ libPrefs/TPreferences.h | |
+ | |
+bin_PROGRAMS = transmission-haiku | |
+ | |
+transmission_haiku_SOURCES = \ | |
+ TRApplication.cpp \ | |
+ TRInfoWindow.cpp \ | |
+ TRPrefsWindow.cpp \ | |
+ TRTransfer.cpp \ | |
+ TRWindow.cpp \ | |
+ TRListView.cpp \ | |
+ TRColumn.cpp \ | |
+ TRField.cpp \ | |
+ libPrefs/TPreferences.cpp | |
+ | |
+transmission_haiku_LDADD = \ | |
+ -lbe -ltracker -lstdc++ -lroot \ | |
+ ../libtransmission/libtransmission.a \ | |
+ ./libhaiku/libcolumnlistview.a \ | |
+ /boot/develop/lib/x86/libtranslation.so \ | |
+ @LIBUPNP_LIBS@ \ | |
+ @LIBNATPMP_LIBS@ \ | |
+ @DHT_LIBS@ \ | |
+ @LIBUTP_LIBS@ \ | |
+ @LIBEVENT_LIBS@ \ | |
+ @LIBCURL_LIBS@ \ | |
+ @OPENSSL_LIBS@ \ | |
+ @ZLIB_LIBS@ \ | |
+ @PTHREAD_LIBS@ | |
Index: haiku/transmission_haiku | |
=================================================================== | |
Cannot display: file marked as a binary type. | |
svn:mime-type = application/octet-stream | |
Property changes on: haiku/transmission_haiku | |
___________________________________________________________________ | |
Added: svn:executable | |
+ * | |
Added: svn:mime-type | |
+ application/octet-stream | |
Index: haiku/TRField.cpp | |
=================================================================== | |
--- haiku/TRField.cpp (revision 0) | |
+++ haiku/TRField.cpp (revision 0) | |
@@ -0,0 +1,49 @@ | |
+/****************************************************************************** | |
+ * $Id: TRField.cpp | |
+ * | |
+ * Authors: | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#include "TRField.h" | |
+ | |
+#include <stdio.h> | |
+ | |
+TRField::TRField(BBitmap *bitmap) | |
+ :BBitmapField(bitmap), fState(0) | |
+{ | |
+} | |
+ | |
+ | |
+const uint32 | |
+TRField::State() | |
+{ | |
+ return fState; | |
+} | |
+ | |
+ | |
+void | |
+TRField::SetState(uint32 state) | |
+{ | |
+ fState = state; | |
+} | |
Index: haiku/TRInfoWindow.cpp | |
=================================================================== | |
--- haiku/TRInfoWindow.cpp (revision 0) | |
+++ haiku/TRInfoWindow.cpp (revision 0) | |
@@ -0,0 +1,355 @@ | |
+/****************************************************************************** | |
+ * $Id: TRInfoWindow.cpp | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * charles | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#include "TRInfoWindow.h" | |
+#include "TRApplication.h" | |
+ | |
+#include <Rect.h> | |
+#include <TranslationUtils.h> | |
+ | |
+#include <malloc.h> | |
+#include <stdio.h> | |
+ | |
+TRInfoWindow::TRInfoWindow(const tr_stat *status, const tr_info *info, const tr_torrent *torrentRef, const char *folder) | |
+ : BWindow(BRect(0, 0, 600, 500), "Torrent Inspector", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | |
+ | B_NOT_ZOOMABLE | B_NOT_RESIZABLE, B_CURRENT_WORKSPACE) | |
+{ | |
+ BRect tabRect, mainRect, mainStringRect, boxRect, textRectTab1, textRectTab3, textRectTab4; | |
+ mainRect = Bounds(); | |
+ | |
+ font_height fh; | |
+ be_plain_font->GetHeight(&fh); | |
+ | |
+ _trprefview = new BView(mainRect, "TRPrefView", B_FOLLOW_H_CENTER, NULL); | |
+ _trprefview->SetViewColor(216,216,216,0); | |
+ | |
+ // Header string | |
+ //-------------------------------------------------------------- | |
+ BFont headerViewFont(be_bold_font); | |
+ headerViewFont.SetSize(18.0f); | |
+ font_height fhView; | |
+ headerViewFont.GetHeight(&fhView); | |
+ mainStringRect.Set(5, 5, mainRect.Width() - 10, fhView.leading + fhView.ascent + 10); | |
+ | |
+ BStringView *strInfoView = new BStringView(mainStringRect, "header", info->name, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); | |
+ strInfoView->SetFont(&headerViewFont); | |
+ strInfoView->SetAlignment(B_ALIGN_CENTER); | |
+ | |
+ // TabView | |
+ //-------------------------------------------------------------- | |
+ tabRect = mainRect; | |
+ tabRect.bottom = tabRect.bottom - 20; | |
+ tabRect.InsetBy(5,mainStringRect.bottom +5); | |
+ _tabView = new BTabView(tabRect, "tab_view"); | |
+ _tabView->SetViewColor(216,216,216,0); | |
+ tabRect.top = tabRect.top - mainStringRect.bottom; | |
+ | |
+ boxRect = BRect(tabRect.left, tabRect.top, tabRect.right-25, tabRect.bottom-75); | |
+ | |
+ //Tab Info | |
+ //-------------------------------------------------------------- | |
+ _Info = new BView(tabRect, "Info", B_FOLLOW_H_CENTER, NULL); | |
+ _Info->SetViewColor(216,216,216,0); | |
+ _Info->AddChild(_boxInfo = new BBox(boxRect, NULL, B_FOLLOW_ALL)); | |
+ _tabView->AddTab(_Info, new BTab()); | |
+ | |
+ // TODO: Add peers list. It's almost done. | |
+/* | |
+ //Tab Peers | |
+ //-------------------------------------------------------------- | |
+ _peers = new BView(tabRect, "Peers", B_FOLLOW_H_CENTER, NULL); | |
+ _peers->SetViewColor(216,216,216,0); | |
+ _peers->AddChild(_boxPeers = new BBox(boxRect, NULL, B_FOLLOW_ALL)); | |
+ _tabView->AddTab(_peers, new BTab()); | |
+*/ | |
+ | |
+ //Tab Files | |
+ //-------------------------------------------------------------- | |
+ _files = new BView(tabRect, "Files", B_FOLLOW_H_CENTER, NULL); | |
+ _files->SetViewColor(216,216,216,0); | |
+ _files->AddChild(_boxFiles = new BBox(boxRect, NULL, B_FOLLOW_ALL)); | |
+ _tabView->AddTab(_files, new BTab()); | |
+ | |
+ //-------------------------------------------------------------- | |
+ //stuff that are put on the info Tab | |
+ //-------------------------------------------------------------- | |
+ BFont headerFont(be_bold_font); | |
+ headerFont.SetSize(14.0f); | |
+ font_height fh1; | |
+ headerFont.GetHeight(&fh1); | |
+ textRectTab1.Set(5, 5, boxRect.Width() - 10, fh1.leading + fh1.ascent + 10); | |
+ | |
+ // TODO: add multiple number of trackers support for output | |
+ BString strTracker(""); | |
+ strTracker << info->trackers[0].announce; | |
+ | |
+ BString strPieceSize(""); | |
+ StringForFileSize(info->pieceSize, &strPieceSize); | |
+ | |
+ BString strTotalSize(""); | |
+ StringForFileSize(info->totalSize, &strTotalSize); | |
+ | |
+ BString strDownloaded(""); | |
+ StringForFileSize(status->downloadedEver, &strDownloaded); | |
+ | |
+ BString strUploaded(""); | |
+ StringForFileSize(status->uploadedEver, &strUploaded); | |
+ BString infoStr(""); | |
+ infoStr << "Announce: " << info->trackers[0].announce << "\n" | |
+ << "Pieces: " << info->pieceCount << ", " << strPieceSize << "\n" | |
+ << "Total Size: " << strTotalSize << "\n" | |
+ << "\n" | |
+ << "Folder: " << folder << "\n" | |
+ << "Downloaded: " << strDownloaded << "\n" | |
+ << "Uploaded: " << strUploaded << "\n"; | |
+ | |
+ BStringView *strView = new BStringView(textRectTab1, "header", info->name, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); | |
+ strView->SetFont(&headerFont); | |
+ strView->SetAlignment(B_ALIGN_CENTER); | |
+ | |
+ textRectTab1.top = fh1.ascent + fh1.descent+10; | |
+ textRectTab1.bottom = boxRect.bottom-10; | |
+ BRect textRect = textRectTab1; | |
+ textRect.top = 0; | |
+ | |
+ BTextView *txtView = new BTextView(textRectTab1, "infoText", textRect, B_FOLLOW_LEFT | B_FOLLOW_TOP); | |
+ txtView->MakeEditable(false); | |
+ txtView->SetText(infoStr.String()); | |
+ txtView->SetViewColor(216,216,216,0); | |
+ | |
+ Lock(); | |
+ _boxInfo->AddChild(strView); | |
+ _boxInfo->AddChild(txtView); | |
+ Unlock(); | |
+ | |
+ | |
+ //stuff that are put on the Peers Tab | |
+ //-------------------------------------------------------------- | |
+ textRectTab3.Set(5, 5, boxRect.Width() - 10, fh.leading + fh.ascent + 10); | |
+ | |
+ // create the list view | |
+ fColumnListTab2 = new BColumnListView (Bounds(), "ColumnList", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP_BOTTOM, | |
+ B_WILL_DRAW |B_NAVIGABLE); | |
+ | |
+ // add columns | |
+ //-------- | |
+ fColumnListTab2->AddColumn(new BStringColumn("IP Adress", 300, 20, 500, 0),0); | |
+ | |
+ //------- | |
+ fColumnListTab2->AddColumn(new TRColumn("Client", 50, 20, 100),1); | |
+ | |
+ //------------------ | |
+ fColumnListTab2->AddColumn(new TRColumn("%", 50, 20, 100),2); | |
+ | |
+ //--------- | |
+ | |
+// CreatePeersList(info); | |
+// Lock(); | |
+ //_boxPeers->AddChild(fColumnList); | |
+// Unlock(); | |
+ | |
+ | |
+ // stuff that are put on the Files Tab | |
+ //-------------------------------------------------------------- | |
+ textRectTab4.Set(5, 5, boxRect.Width() - 10, fh.leading + fh.ascent + 10); | |
+ Lock(); | |
+ | |
+ // create the list view | |
+ fColumnList = new BColumnListView (Bounds(), "ColumnList", B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP_BOTTOM, | |
+ B_WILL_DRAW |B_NAVIGABLE); | |
+ | |
+ // add columns | |
+ // Column 1 | |
+ fColumnList->AddColumn(new BStringColumn("Name", 300, 20, 500, 0),0); | |
+ | |
+ fTRColumView1 = new TRColumn("DL", 50, 20, 100); | |
+ BBitmap *bitmap = BTranslationUtils::GetBitmap('RAWT',"BEOS:GO"); | |
+ fTRColumView1->AddBitmapState(0, bitmap); | |
+ bitmap = NULL; | |
+ | |
+ bitmap = BTranslationUtils::GetBitmap('RAWT',"BEOS:STOP"); | |
+ if(bitmap != NULL) | |
+ fTRColumView1->AddBitmapState(1, bitmap); | |
+ bitmap = NULL; | |
+ | |
+ fColumnList->AddColumn(fTRColumView1,1); | |
+ | |
+ // Column 2 | |
+ fTRColumView2 = new TRColumn("Rank", 50, 20, 100); | |
+ | |
+ bitmap = BTranslationUtils::GetBitmap('RAWT',"BEOS:PRIORITYNORMAL"); | |
+ if(bitmap != NULL) | |
+ fTRColumView2->AddBitmapState(0, bitmap); | |
+ bitmap = NULL; | |
+ | |
+ bitmap = BTranslationUtils::GetBitmap('RAWT',"BEOS:PRIORITYHIGH"); | |
+ if(bitmap != NULL) | |
+ fTRColumView2->AddBitmapState(1, bitmap); | |
+ bitmap = NULL; | |
+ | |
+ bitmap = BTranslationUtils::GetBitmap('RAWT',"BEOS:PRIORITYLOW"); | |
+ if(bitmap != NULL) | |
+ fTRColumView2->AddBitmapState(2, bitmap); | |
+ | |
+ fColumnList->AddColumn(fTRColumView2,2); | |
+ | |
+ CreateFileList(info); | |
+ _boxFiles->AddChild(fColumnList); | |
+ Unlock(); | |
+ | |
+ //-------------------------------------------------------------- | |
+ Lock(); | |
+ _trprefview->AddChild(strInfoView); | |
+ _trprefview->AddChild(_tabView); | |
+ AddChild(_trprefview); | |
+ Unlock(); | |
+} | |
+ | |
+ | |
+TRInfoWindow::~TRInfoWindow() {} | |
+ | |
+ | |
+void | |
+TRInfoWindow::FrameResized(float, float) {} | |
+ | |
+ | |
+void | |
+TRInfoWindow::StringForFileSize(uint64_t size, BString *str) { | |
+ char *s = (char*)calloc(512, sizeof(char)); | |
+ if (size < 1024) { | |
+ sprintf(s, "%lld bytes", size); | |
+ } else if (size < 1048576) { | |
+ sprintf(s, "%lld.%lld KB", size / 1024, (size % 1024 ) / 103); | |
+ } else if (size < 1073741824 ) { | |
+ sprintf(s, "%lld.%lld MB", size / 1048576, (size % 1048576) / 104858); | |
+ } else { | |
+ sprintf(s, "%lld.%lld GB", size / 1073741824, (size % 1073741824) / 107374183); | |
+ } | |
+ | |
+ str->SetTo(s); | |
+ free(s); | |
+} | |
+ | |
+ | |
+void | |
+TRInfoWindow::CreateFileList(const tr_info *info) | |
+{ | |
+ BRow *cRow; | |
+ tr_file file; | |
+ int count, i; | |
+ BString str; | |
+ | |
+ count = info->fileCount; | |
+ | |
+ for (i = 0; i < count; i++) | |
+ { | |
+ file = info->files[i]; | |
+ str = *new BString(file.name); | |
+ if (info->isMultifile) | |
+ { | |
+ str = str.RemoveFirst(info->name); | |
+ str = str.RemoveFirst("/"); | |
+ } | |
+ | |
+ cRow = new BRow(); | |
+ cRow->SetField(new BStringField(str.String()), 0); | |
+ cRow->SetField(new TRField(fTRColumView1->GetStartBitmap()), 1); | |
+ cRow->SetField(new TRField(fTRColumView2->GetStartBitmap()), 2); | |
+ fColumnList->AddRow(cRow, (int32)i); | |
+ } | |
+} | |
+ | |
+ | |
+// TODO: Add peers list. It should be not that hard. | |
+void | |
+TRInfoWindow::CreatePeersList(const tr_torrent *torrentRef) | |
+{ | |
+/* int totalPeers, i; | |
+ tr_peer_stat_t * peers = tr_torrentPeers(fHandle, &totalPeers); | |
+ | |
+ NSMutableArray * peerDics = [NSMutableArray arrayWithCapacity: totalPeers]; | |
+ NSMutableDictionary * dic; | |
+ | |
+ tr_peer_stat_t * peer; | |
+ for (i = 0; i < totalPeers; i++) | |
+ { | |
+ peer = &peers[i]; | |
+ | |
+ dic = [NSMutableDictionary dictionaryWithObjectsAndKeys: | |
+ [NSNumber numberWithBool: peer->isConnected], @"Connected", | |
+ [NSNumber numberWithInt: peer->from], @"From", | |
+ [NSString stringWithCString: (char *) peer->addr encoding: NSUTF8StringEncoding], @"IP", | |
+ [NSNumber numberWithInt: peer->port], @"Port", nil]; | |
+ | |
+ if (peer->isConnected) | |
+ { | |
+ [dic setObject: [NSNumber numberWithFloat: peer->progress] forKey: @"Progress"]; | |
+ | |
+ if (peer->isDownloading) | |
+ [dic setObject: [NSNumber numberWithFloat: peer->uploadToRate] forKey: @"UL To Rate"]; | |
+ if (peer->isUploading) | |
+ [dic setObject: [NSNumber numberWithFloat: peer->downloadFromRate] forKey: @"DL From Rate"]; | |
+ | |
+ [dic setObject: [NSString stringWithCString: (char *) peer->client encoding: NSUTF8StringEncoding] forKey: @"Client"]; | |
+ } | |
+ else | |
+ [dic setObject: @"" forKey: @"Client"]; //needed to be set here for client sort | |
+ | |
+ [peerDics addObject: dic]; | |
+ } | |
+ | |
+ tr_torrentPeersFree(peers, totalPeers); | |
+ | |
+ return peerDics;*/ | |
+// BRow *cRow; | |
+// tr_file_t file; | |
+// int count, i; | |
+// BString str; | |
+ | |
+// int totalPeers, i; | |
+// tr_peer_stat_t * peers = tr_torrentPeers(fHandle, &totalPeers); | |
+/* count = info->fileCount; | |
+ | |
+ for (i = 0; i < count; i++) | |
+ { | |
+ file = info->files[i]; | |
+ str = *new BString(file.name); | |
+ if (info->multifile) | |
+ { | |
+ str = str.RemoveFirst(info->name); | |
+ str = str.RemoveFirst("/"); | |
+ } | |
+ | |
+ cRow = new BRow(); | |
+ cRow->SetField(new BStringField(str.String()), 0); | |
+ cRow->SetField(new TRField(fTRColumView1->GetStartBitmap()), 1); | |
+ cRow->SetField(new TRField(fTRColumView2->GetStartBitmap()), 2); | |
+ fColumnList->AddRow(cRow, (int32)i); | |
+ }*/ | |
+} | |
Index: haiku/TRTransfer.cpp | |
=================================================================== | |
--- haiku/TRTransfer.cpp (revision 0) | |
+++ haiku/TRTransfer.cpp (revision 0) | |
@@ -0,0 +1,297 @@ | |
+/****************************************************************************** | |
+ * $Id: TRTransfer.cpp | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * joshe | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#include "TRTransfer.h" | |
+ | |
+#include <Font.h> | |
+#include <MenuItem.h> | |
+#include <MenuField.h> | |
+ | |
+#include <malloc.h> | |
+#include <stdio.h> | |
+ | |
+/** | |
+ * BListItem that renders Transfer status. | |
+ */ | |
+TRTransfer::TRTransfer(const char *fullpath, node_ref node, tr_torrent *torrentRef) : BListItem(0, false), cachedNodeRef(node) { | |
+ fBaselineOffset = 0.0f; | |
+ fLineSpacing = 0.0f; | |
+ torrent = torrentRef; | |
+ | |
+ cachedPath = new BString(fullpath); | |
+ fStatusLock = new BLocker("Status Locker", true); | |
+ | |
+ fStatus = (tr_stat*)calloc(1, sizeof(tr_stat)); | |
+ const tr_info *info = tr_torrentInfo(torrent); | |
+ fName = new BString("<unknown name>"); | |
+ fName->SetTo(info->name); | |
+ | |
+ fBarColor.red = 50; | |
+ fBarColor.green = 150; | |
+ fBarColor.blue = 255; | |
+ fBarColor.alpha = 255; | |
+ | |
+ fTimeStr = (char*)calloc(78, sizeof(char)); | |
+ fTransStr = (char*)calloc(78, sizeof(char)); | |
+} | |
+ | |
+ | |
+TRTransfer::~TRTransfer() { | |
+ if (fStatusLock->Lock()) { | |
+ free(fStatus); | |
+ fStatusLock->Unlock(); | |
+ delete fStatusLock; | |
+ } | |
+ delete cachedPath; | |
+ delete fName; | |
+} | |
+ | |
+ | |
+void | |
+TRTransfer::Update(BView *owner, const BFont *font) { | |
+ BListItem::Update(owner, font); | |
+ SetHeight(BListItem::Height() * 4); | |
+ | |
+ font_height height; | |
+ font->GetHeight(&height); | |
+ | |
+ fBaselineOffset = height.leading + height.ascent; | |
+ fLineSpacing = height.descent; | |
+} | |
+ | |
+ | |
+/** | |
+ * Sets the transfer information to render. | |
+ * Returns a bool signaling the view is dirty after the update. | |
+ * | |
+ * The view is determined to be dirty if the transfer | |
+ * status, percentDone, eta or the "shade" (even or odd) | |
+ * position in the list changes from the previous state. | |
+ * If the tr_stat_t is in fact different then the new, full | |
+ * status is memcpy'd overtop the existing code. | |
+ * | |
+ * This is a thread-safe function, as all writing to the | |
+ * local fStatus requires a successful Lock on fStatusLock. | |
+ */ | |
+bool | |
+TRTransfer::UpdateStatus(const tr_stat *stat, bool shade) { | |
+ bool dirty = false; | |
+ if (stat == NULL) | |
+ return true; | |
+ if (fStatusLock->Lock()) { | |
+ if (fStatus->activity != stat->activity || | |
+ fStatus->percentDone != stat->percentDone || | |
+ fStatus->eta != stat->eta || | |
+ fShade != shade) | |
+ { | |
+ memcpy(fStatus, stat, sizeof(tr_stat)); | |
+ dirty = true; | |
+ } | |
+ fStatusLock->Unlock(); | |
+ fShade = shade; | |
+ } | |
+ return dirty; | |
+} | |
+ | |
+ | |
+bool | |
+TRTransfer::IsRunning() { | |
+ return (fStatus != NULL && | |
+ ((fStatus->activity == TR_STATUS_CHECK_WAIT) || | |
+ (fStatus->activity == TR_STATUS_CHECK) || | |
+ (fStatus->activity == TR_STATUS_DOWNLOAD) || | |
+ (fStatus->activity == TR_STATUS_DOWNLOAD_WAIT) || | |
+ (fStatus->activity == TR_STATUS_SEED) || | |
+ (fStatus->activity == TR_STATUS_SEED_WAIT))); | |
+} | |
+ | |
+ | |
+/** | |
+ * Renders (Draws) the current status into the BListView. | |
+ */ | |
+void | |
+TRTransfer::DrawItem(BView *owner, BRect frame, bool) { | |
+ rgb_color col; | |
+ col.red = 255; | |
+ col.green = 255; | |
+ col.blue = 255; | |
+ | |
+ owner->PushState(); | |
+ | |
+ // Draw the background... | |
+ if (IsSelected()) { | |
+ owner->SetLowColor(tint_color(col, B_DARKEN_2_TINT)); | |
+ } else if (fShade) { | |
+ owner->SetLowColor(tint_color(tint_color(col, B_DARKEN_1_TINT), B_LIGHTEN_2_TINT)); | |
+ } else { | |
+ owner->SetLowColor(col); | |
+ } | |
+ owner->FillRect(frame, B_SOLID_LOW); | |
+ | |
+ // Draw the informational text... | |
+ owner->SetHighColor(ui_color(B_MENU_ITEM_TEXT_COLOR)); | |
+ BPoint textLoc = frame.LeftTop(); | |
+ textLoc.y += (fBaselineOffset / 2); | |
+ textLoc += BPoint(2, fBaselineOffset); | |
+ | |
+ if (fStatus != NULL && fStatusLock->Lock()) { | |
+ owner->DrawString(fName->String(), textLoc); | |
+ | |
+ if (fStatus->activity == TR_STATUS_STOPPED ) { | |
+ sprintf(fTimeStr, "Paused (%.2f %%)", 100 * fStatus->percentDone); | |
+ } else if (fStatus->activity == TR_STATUS_CHECK_WAIT ) { | |
+ sprintf(fTimeStr, "Waiting To Check Existing Files (%.2f %%)", | |
+ 100 * fStatus->percentDone); | |
+ } else if (fStatus->activity == TR_STATUS_CHECK ) { | |
+ sprintf(fTimeStr, "Checking Existing Files (%.2f %%)", | |
+ 100 * fStatus->percentDone); | |
+ } else if (fStatus->activity == TR_STATUS_DOWNLOAD_WAIT) { | |
+ sprintf(fTimeStr, "Queued to download"); | |
+ } else if (fStatus->activity == TR_STATUS_DOWNLOAD) { | |
+ if (fStatus->eta < 0 ) { | |
+ sprintf(fTimeStr, "--:--:-- Remaining (%.2f %%Complete)", | |
+ 100 * fStatus->percentDone); | |
+ } else { | |
+ sprintf(fTimeStr, "%02d:%02d:%02d Remaining (%.2f %%Complete)", | |
+ fStatus->eta / 3600, (fStatus->eta / 60) % 60, | |
+ fStatus->eta % 60, 100 * fStatus->percentDone); | |
+ } | |
+ } else if (fStatus->activity == TR_STATUS_SEED_WAIT) { | |
+ sprintf(fTimeStr, "Queued to seed"); | |
+ } else if (fStatus->activity == TR_STATUS_SEED) { | |
+ sprintf(fTimeStr, "Seeding"); | |
+ } else { | |
+ fTimeStr[0] = '\0'; | |
+ } | |
+ | |
+ textLoc.x = frame.Width() - owner->StringWidth(fTimeStr) - 2; | |
+ owner->DrawString(fTimeStr, textLoc); | |
+ | |
+ if ((fStatus->activity == TR_STATUS_DOWNLOAD_WAIT) || | |
+ (fStatus->activity == TR_STATUS_DOWNLOAD) || | |
+ (fStatus->activity == TR_STATUS_SEED) || | |
+ (fStatus->activity == TR_STATUS_CHECK) || | |
+ (fStatus->activity == TR_STATUS_CHECK_WAIT)) { | |
+ // Move to the left of the bottom line. | |
+ textLoc.Set(frame.left + 2, | |
+ frame.top + fBaselineOffset * 3 + (2 * fLineSpacing) + (fBaselineOffset / 2)); | |
+ sprintf(fTransStr, "DL: %.2f KB/s (from %i of %i peer%s)", | |
+ fStatus->rawDownloadSpeed_KBps, fStatus->peersSendingToUs, fStatus->peersConnected, | |
+ (fStatus->peersConnected == 1) ? "" : "s"); | |
+ owner->DrawString(fTransStr, textLoc); | |
+ | |
+ sprintf(fTransStr, "UL: %.2f KB/s", fStatus->rawUploadSpeed_KBps); | |
+ textLoc.x = frame.Width() - owner->StringWidth(fTransStr) - 2; | |
+ owner->DrawString(fTransStr, textLoc); | |
+ } | |
+ | |
+ /* | |
+ * Progress Bar - Mercilessly ripped from the Haiku source code, and | |
+ * modified to handle selection tinting, and list item position shading. | |
+ */ | |
+ // Custom setup (Transmission Added) | |
+ BRect rect(frame.left + 2, frame.top + fBaselineOffset + fLineSpacing + (fBaselineOffset / 2), | |
+ frame.Width() - 2, frame.top + fBaselineOffset * 2 + fLineSpacing + (fBaselineOffset / 2)); | |
+ | |
+ // First bevel | |
+ owner->SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_1_TINT)); | |
+ if (IsSelected()) { | |
+ owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); | |
+ } | |
+ owner->StrokeLine(BPoint(rect.left, rect.bottom), BPoint(rect.left, rect.top)); | |
+ owner->StrokeLine(BPoint(rect.right, rect.top)); | |
+ | |
+ owner->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_2_TINT)); | |
+ if (IsSelected()) { | |
+ owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); | |
+ } | |
+ owner->StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), BPoint(rect.right, rect.bottom)); | |
+ owner->StrokeLine(BPoint(rect.right, rect.top + 1.0f)); | |
+ | |
+ rect.InsetBy(1.0f, 1.0f); | |
+ | |
+ // Second bevel | |
+ owner->SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_4_TINT)); | |
+ if (IsSelected()) { | |
+ owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); | |
+ } | |
+ owner->StrokeLine(BPoint(rect.left, rect.bottom), BPoint(rect.left, rect.top)); | |
+ owner->StrokeLine(BPoint(rect.right, rect.top)); | |
+ | |
+ owner->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); | |
+ if (IsSelected()) { | |
+ owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); | |
+ } | |
+ owner->StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), BPoint(rect.right, rect.bottom)); | |
+ owner->StrokeLine(BPoint(rect.right, rect.top + 1.0f)); | |
+ | |
+ rect.InsetBy(1.0f, 1.0f); | |
+ | |
+ // Filling | |
+ owner->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_MAX_TINT)); | |
+ if (IsSelected()) { | |
+ owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); | |
+ } | |
+ owner->FillRect(rect); | |
+ | |
+ if (fStatus->percentDone != 0.0f) { | |
+ rect.right = rect.left + (float)ceil(fStatus->percentDone * (rect.Width())), | |
+ | |
+ // Bevel | |
+ owner->SetHighColor(tint_color(fBarColor, B_LIGHTEN_2_TINT)); | |
+ if (IsSelected()) { | |
+ owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); | |
+ } | |
+ owner->StrokeLine(BPoint(rect.left, rect.bottom), BPoint(rect.left, rect.top)); | |
+ owner->StrokeLine(BPoint(rect.right, rect.top)); | |
+ | |
+ owner->SetHighColor(tint_color(fBarColor, B_DARKEN_2_TINT)); | |
+ if (IsSelected()) { | |
+ owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); | |
+ } | |
+ owner->StrokeLine(BPoint(rect.left, rect.bottom), BPoint(rect.right, rect.bottom)); | |
+ owner->StrokeLine(BPoint(rect.right, rect.top)); | |
+ | |
+ rect.InsetBy(1.0f, 1.0f); | |
+ | |
+ // Filling | |
+ owner->SetHighColor(fBarColor); | |
+ if (IsSelected()) { | |
+ owner->SetHighColor(tint_color(owner->HighColor(), B_DARKEN_1_TINT)); | |
+ } | |
+ owner->FillRect(rect); | |
+ } | |
+ | |
+ fStatusLock->Unlock(); | |
+ } else { | |
+ owner->DrawString("loading...", textLoc); | |
+ } | |
+ | |
+ owner->PopState(); | |
+} | |
Index: haiku/TRField.h | |
=================================================================== | |
--- haiku/TRField.h (revision 0) | |
+++ haiku/TRField.h (revision 0) | |
@@ -0,0 +1,45 @@ | |
+ | |
+/****************************************************************************** | |
+ * $Id: TRField.h | |
+ * | |
+ * Authors: | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#ifndef TR_TRFIELD | |
+#define TR_TRFIELD | |
+ | |
+#include "libhaiku/ColumnListView.h" | |
+#include "libhaiku/ColumnTypes.h" | |
+ | |
+class TRField : public BBitmapField | |
+{ | |
+ public: | |
+ TRField (BBitmap* bitmap); | |
+ const uint32 State (); | |
+ void SetState (uint32 state); | |
+ private: | |
+ uint32 fState; | |
+}; | |
+ | |
+#endif /*TR_TRFIELD*/ | |
Index: haiku/TRInfoWindow.h | |
=================================================================== | |
--- haiku/TRInfoWindow.h (revision 0) | |
+++ haiku/TRInfoWindow.h (revision 0) | |
@@ -0,0 +1,72 @@ | |
+/****************************************************************************** | |
+ * $Id: TRInfoWindow.h | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * charles | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#ifndef TR_INFO_WIND | |
+#define TR_INFO_WIND | |
+ | |
+#include <Box.h> | |
+#include <Window.h> | |
+#include <StringView.h> | |
+ | |
+#include <Button.h> | |
+#include <TextControl.h> | |
+#include <TabView.h> | |
+#include <View.h> | |
+#include <String.h> | |
+#include <ScrollView.h> | |
+#include <ListView.h> | |
+ | |
+#include "libhaiku/ColumnListView.h" | |
+#include "libhaiku/ColumnTypes.h" | |
+ | |
+#include "TRColumn.h" | |
+#include "TRField.h" | |
+ | |
+#include <libtransmission/transmission.h> | |
+ | |
+class TRInfoWindow : public BWindow { | |
+public: | |
+ TRInfoWindow(const tr_stat *status, const tr_info *info, const tr_torrent *torrentRef,const char *folder); | |
+ ~TRInfoWindow(); | |
+ virtual void FrameResized(float width, float height); | |
+ | |
+private: | |
+ void StringForFileSize(uint64_t size, BString *str); | |
+ void CreatePeersList(const tr_torrent *torrentRef); | |
+ void CreateFileList(const tr_info *info); | |
+ | |
+ BBox *_boxInfo, *_boxActivity, *_boxPeers, *_boxFiles, *_boxOptions; | |
+ BTabView *_tabView; | |
+ BView *_options, *_Info, *_activity, *_peers, *_files, *_trprefview; | |
+ BListView *_fileListView; | |
+ BColumnListView *fColumnList, *fColumnListTab2; | |
+ TRColumn *fTRColumView0, *fTRColumView1, *fTRColumView2; | |
+}; | |
+ | |
+#endif | |
Index: haiku/TRTransfer.h | |
=================================================================== | |
--- haiku/TRTransfer.h (revision 0) | |
+++ haiku/TRTransfer.h (revision 0) | |
@@ -0,0 +1,78 @@ | |
+/****************************************************************************** | |
+ * $Id: TRTransfer.h | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * charles | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#ifndef TR_TRANSFER | |
+#define TR_TRANSFER | |
+ | |
+#include <Entry.h> | |
+#include <ListItem.h> | |
+#include <Locker.h> | |
+#include <String.h> | |
+#include <Node.h> | |
+#include <View.h> | |
+ | |
+#include <libtransmission/transmission.h> | |
+ | |
+class TRTransfer : public BListItem { | |
+public: // Construction and Control methods. | |
+ TRTransfer(const char *fullpath, node_ref node, tr_torrent *torrentRef); | |
+ ~TRTransfer(); | |
+ | |
+ inline node_ref GetCachedNodeRef() { return cachedNodeRef; }; | |
+ inline const char* GetCachedPath() { return cachedPath->String(); }; | |
+ inline tr_torrent* GetTorrent() { return torrent; }; | |
+ | |
+ bool UpdateStatus(const tr_stat *stat, bool shade); | |
+ bool IsRunning(); | |
+ | |
+public: // BListItem | |
+ virtual void Update(BView *owner, const BFont *font); | |
+ virtual void DrawItem(BView *owner, BRect frame, bool complete = false); | |
+private: | |
+ node_ref cachedNodeRef; | |
+ BString *cachedPath; | |
+ tr_torrent *torrent; | |
+ | |
+private: // Private members used for rendering. | |
+ float fBaselineOffset; | |
+ float fLineSpacing; | |
+ | |
+ BLocker *fStatusLock; | |
+ tr_stat *fStatus; | |
+ BString *fName; | |
+ | |
+ rgb_color fBarColor; | |
+ | |
+ char* fTimeStr; | |
+ char* fTransStr; | |
+ | |
+ bool fShade; | |
+}; | |
+ | |
+#endif /* TR_TRANSFER */ | |
Index: haiku/TRDefault.h | |
=================================================================== | |
--- haiku/TRDefault.h (revision 0) | |
+++ haiku/TRDefault.h (revision 0) | |
@@ -0,0 +1,61 @@ | |
+/****************************************************************************** | |
+ * $Id: TRDefault.h | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+//Fredrik Modéen | |
+//This are all the default's that are in use | |
+//(so wee don't need more than one place to write them) | |
+ | |
+#ifndef TR_DEFAULT | |
+#define TR_DEFAULT | |
+ | |
+#define DOWNLOADFOLDER "/boot/home/Downloads" | |
+#define PORT "9000" | |
+#define TOTALUPLOADLIMIT "100" | |
+#define TOTALDOWNLOADLIMIT "100" | |
+#define CHECKBOXUNLIMITUPLOAD true | |
+#define CHECKBOXUNLIMITDOWNLOAD true | |
+ | |
+#define TR_PREF_SAVE 'tSve' | |
+#define TR_PREF_CANCEL 'tCan' | |
+#define TR_PREF_DEFAULTS 'tDef' | |
+#define TR_PREF_UNLIMITDOWNLOAD 'tUnD' | |
+#define TR_PREF_UNLIMITUPLOAD 'tUnU' | |
+ | |
+#define TR_INFO 'tNfo' | |
+#define TR_RESUME 'tRes' | |
+#define TR_PAUSE 'tPse' | |
+#define TR_REMOVE 'tRmv' | |
+#define TR_SELECT 'tSel' | |
+#define TR_SETTINGS 'tSet' | |
+ | |
+#define APP_SIG "application/x-vnd.haiku-Transmission" | |
+#define TRANSMISSION_SETTINGS "Transmission/settings" | |
+ | |
+#define TR_ADD 'tAdd' | |
+#define TR_OPEN 'tOpn' | |
+#define TR_RELOAD_SETTINGS 'tRSt' | |
+#endif | |
Index: haiku/TRColumn.cpp | |
=================================================================== | |
--- haiku/TRColumn.cpp (revision 0) | |
+++ haiku/TRColumn.cpp (revision 0) | |
@@ -0,0 +1,100 @@ | |
+/****************************************************************************** | |
+ * $Id: TRListView.cpp | |
+ * | |
+ * Authors: | |
+ * Gleb Posobin <posobin@gmail.com> | |
+ * Fredrik Modéen <fredrik@modeen.se> | |
+ * | |
+ * Copyright (c) 2005-2013 Transmission authors and contributors | |
+ * | |
+ * 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 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 MERCHANTABILITY, | |
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
+ * AUTHORS OR COPYRIGHT HOLDERS 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. | |
+ *****************************************************************************/ | |
+ | |
+#include "TRColumn.h" | |
+#include "TRField.h" | |
+ | |
+#include <TranslationUtils.h> | |
+ | |
+#include <stdio.h> | |
+ | |
+TRColumn::TRColumn(const char *title, float width, | |
+ float minWidth, float maxWidth) | |
+ : BBitmapColumn(title, width, minWidth, maxWidth) | |
+{ | |
+} | |
+ | |
+ | |
+TRColumn::~TRColumn() | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+TRColumn::AddBitmapState(uint32 state, BBitmap* bitmap) | |
+{ | |
+ fHashMap[state] = bitmap; | |
+} | |
+ | |
+ | |
+uint32 | |
+TRColumn::CheckState(uint32 state) | |
+{ | |
+ std::unordered_map<uint32, BBitmap*>::size_type ant; | |
+ uint32 temp; | |
+ ant = fHashMap.size(); | |
+ if(ant <= state){ | |
+ temp = 0; | |
+ }else{ | |
+ temp = state; | |
+ } | |
+ return temp; | |
+} | |
+ | |
+ | |
+BBitmap* | |
+TRColumn::GetBitmapFromState(uint32 state) | |
+{ | |
+ if (fHashMap.empty()) | |
+ return NULL; | |
+ | |
+ if (fHashMap.find(state) == fHashMap.end()) | |
+ return NULL; | |
+ | |
+ return fHashMap[state]; | |
+} | |
+ | |
+ | |
+BBitmap* | |
+TRColumn::GetStartBitmap() | |
+{ | |
+ return GetBitmapFromState(0); | |
+} | |
+ | |
+ | |
+void | |
+TRColumn::MouseDown(BColumnListView *parent, BRow *row, BField *field, | |
+ BRect field_rect, BPoint point, uint32 buttons) | |
+{ | |
+ TRField *bitmapField = static_cast<TRField *>(field); | |
+ | |
+ uint32 state = bitmapField->State(); | |
+ bitmapField->SetState(CheckState(state+1)); | |
+ | |
+ bitmapField->SetBitmap(GetBitmapFromState(state)); | |
+} | |
Index: haiku/libhaiku/ColorTools.cpp | |
=================================================================== | |
--- haiku/libhaiku/ColorTools.cpp (revision 0) | |
+++ haiku/libhaiku/ColorTools.cpp (revision 0) | |
@@ -0,0 +1,110 @@ | |
+/* | |
+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. | |
+*/ | |
+ | |
+/******************************************************************************* | |
+/ | |
+/ File: ColorTools.cpp | |
+/ | |
+/ Description: Additional experimental color manipulation functions. | |
+/ | |
+/ Copyright 2000, Be Incorporated, All Rights Reserved | |
+/ | |
+*******************************************************************************/ | |
+ | |
+#include "ColorTools.h" | |
+ | |
+#if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI | |
+ | |
+namespace BExperimental { | |
+ | |
+#if DEBUG | |
+#define DB_INLINE | |
+#else | |
+#define DB_INLINE inline | |
+#endif | |
+ | |
+static DB_INLINE void mix_color_func(rgb_color* target, const rgb_color other, uint8 amount) | |
+{ | |
+ target->red = (uint8)( ((int16(other.red)-int16(target->red))*amount)/255 | |
+ + target->red ); | |
+ target->green = (uint8)( ((int16(other.green)-int16(target->green))*amount)/255 | |
+ + target->green ); | |
+ target->blue = (uint8)( ((int16(other.blue)-int16(target->blue))*amount)/255 | |
+ + target->blue ); | |
+ target->alpha = (uint8)( ((int16(other.alpha)-int16(target->alpha))*amount)/255 | |
+ + target->alpha ); | |
+} | |
+ | |
+static DB_INLINE void blend_color_func(rgb_color* target, const rgb_color other, uint8 amount) | |
+{ | |
+ const uint8 alphaMix = (uint8)( ((int16(other.alpha)-int16(255-target->alpha))*amount)/255 | |
+ + (255-target->alpha) ); | |
+ target->red = (uint8)( ((int16(other.red)-int16(target->red))*alphaMix)/255 | |
+ + target->red ); | |
+ target->green = (uint8)( ((int16(other.green)-int16(target->green))*alphaMix)/255 | |
+ + target->green ); | |
+ target->blue = (uint8)( ((int16(other.blue)-int16(target->blue))*alphaMix)/255 | |
+ + target->blue ); | |
+ target->alpha = (uint8)( ((int16(other.alpha)-int16(target->alpha))*amount)/255 | |
+ + target->alpha ); | |
+} | |
+ | |
+static DB_INLINE void disable_color_func(rgb_color* target, const rgb_color background) | |
+{ | |
+ blend_color_func(target, background, 255-70); | |
+} | |
+ | |
+// -------------------------------------------------------------------------- | |
+ | |
+rgb_color mix_color(rgb_color color1, rgb_color color2, uint8 amount) | |
+{ | |
+ mix_color_func(&color1, color2, amount); | |
+ return color1; | |
+} | |
+ | |
+rgb_color blend_color(rgb_color color1, rgb_color color2, uint8 amount) | |
+{ | |
+ blend_color_func(&color1, color2, amount); | |
+ return color1; | |
+} | |
+ | |
+rgb_color disable_color(rgb_color color, rgb_color background) | |
+{ | |
+ disable_color_func(&color, background); | |
+ return color; | |
+} | |
+ | |
+} | |
+ | |
+#endif | |
Index: haiku/libhaiku/ColumnTypes.cpp | |
=================================================================== | |
--- haiku/libhaiku/ColumnTypes.cpp (revision 0) | |
+++ haiku/libhaiku/ColumnTypes.cpp (revision 0) | |
@@ -0,0 +1,648 @@ | |
+/******************************************************************************* | |
+/ | |
+/ File: ColumnTypes.h | |
+/ | |
+/ Description: Experimental classes that implement particular column/field | |
+/ data types for use in BColumnListView. | |
+/ | |
+/ Copyright 2000+, Be Incorporated, All Rights Reserved | |
+/ | |
+*******************************************************************************/ | |
+ | |
+#include "ColumnTypes.h" | |
+ | |
+#include <View.h> | |
+ | |
+#include <parsedate.h> | |
+#include <stdio.h> | |
+ | |
+ | |
+#define kTEXT_MARGIN 8 | |
+ | |
+ | |
+BTitledColumn::BTitledColumn(const char* title, float width, float minWidth, | |
+ float maxWidth, alignment align) | |
+ : BColumn(width, minWidth, maxWidth, align), | |
+ fTitle(title) | |
+{ | |
+ font_height fh; | |
+ | |
+ be_plain_font->GetHeight(&fh); | |
+ fFontHeight = fh.descent + fh.leading; | |
+} | |
+ | |
+ | |
+void | |
+BTitledColumn::DrawTitle(BRect rect, BView* parent) | |
+{ | |
+ float width = rect.Width() - (2 * kTEXT_MARGIN); | |
+ BString out_string(fTitle); | |
+ | |
+ parent->TruncateString(&out_string, B_TRUNCATE_END, width + 2); | |
+ DrawString(out_string.String(), parent, rect); | |
+} | |
+ | |
+ | |
+void | |
+BTitledColumn::GetColumnName(BString* into) const | |
+{ | |
+ *into = fTitle; | |
+} | |
+ | |
+ | |
+void | |
+BTitledColumn::DrawString(const char* string, BView* parent, BRect rect) | |
+{ | |
+ float width = rect.Width() - (2 * kTEXT_MARGIN); | |
+ float y; | |
+ BFont font; | |
+ font_height finfo; | |
+ | |
+ parent->GetFont(&font); | |
+ font.GetHeight(&finfo); | |
+ y = rect.top + ((rect.Height() - (finfo.ascent + finfo.descent + finfo.leading)) / 2) | |
+ + (finfo.ascent + finfo.descent) - 2; | |
+ | |
+ switch (Alignment()) { | |
+ default: | |
+ case B_ALIGN_LEFT: | |
+ parent->MovePenTo(rect.left + kTEXT_MARGIN, y); | |
+ break; | |
+ | |
+ case B_ALIGN_CENTER: | |
+ parent->MovePenTo(rect.left + kTEXT_MARGIN + ((width - font.StringWidth(string)) / 2), y); | |
+ break; | |
+ | |
+ case B_ALIGN_RIGHT: | |
+ parent->MovePenTo(rect.right - kTEXT_MARGIN - font.StringWidth(string), y); | |
+ break; | |
+ } | |
+ parent->DrawString(string); | |
+} | |
+ | |
+ | |
+void | |
+BTitledColumn::SetTitle(const char* title) | |
+{ | |
+ fTitle.SetTo(title); | |
+} | |
+ | |
+ | |
+void | |
+BTitledColumn::Title(BString* forTitle) const | |
+{ | |
+ if (forTitle) | |
+ forTitle->SetTo(fTitle.String()); | |
+} | |
+ | |
+ | |
+float | |
+BTitledColumn::FontHeight() const | |
+{ | |
+ return fFontHeight; | |
+} | |
+ | |
+ | |
+float | |
+BTitledColumn::GetPreferredWidth(BField *_field, BView* parent) const | |
+{ | |
+ return parent->StringWidth(fTitle.String()) + 2 * kTEXT_MARGIN; | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BStringField::BStringField(const char* string) | |
+ : | |
+ fWidth(0), | |
+ fString(string), | |
+ fClippedString(string) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BStringField::SetString(const char* val) | |
+{ | |
+ fString = val; | |
+ fClippedString = ""; | |
+ fWidth = 0; | |
+} | |
+ | |
+ | |
+const char* | |
+BStringField::String() const | |
+{ | |
+ return fString.String(); | |
+} | |
+ | |
+ | |
+void | |
+BStringField::SetWidth(float width) | |
+{ | |
+ fWidth = width; | |
+} | |
+ | |
+ | |
+float | |
+BStringField::Width() | |
+{ | |
+ return fWidth; | |
+} | |
+ | |
+ | |
+void | |
+BStringField::SetClippedString(const char* val) | |
+{ | |
+ fClippedString = val; | |
+} | |
+ | |
+ | |
+const char* | |
+BStringField::ClippedString() | |
+{ | |
+ return fClippedString.String(); | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BStringColumn::BStringColumn(const char* title, float width, float minWidth, | |
+ float maxWidth, uint32 truncate, alignment align) | |
+ : BTitledColumn(title, width, minWidth, maxWidth, align), | |
+ fTruncate(truncate) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BStringColumn::DrawField(BField* _field, BRect rect, BView* parent) | |
+{ | |
+ float width = rect.Width() - (2 * kTEXT_MARGIN); | |
+ BStringField* field = static_cast<BStringField*>(_field); | |
+ bool clipNeeded = width < field->Width(); | |
+ | |
+ if (clipNeeded) { | |
+ BString out_string(field->String()); | |
+ | |
+ parent->TruncateString(&out_string, fTruncate, width + 2); | |
+ field->SetClippedString(out_string.String()); | |
+ field->SetWidth(width); | |
+ } | |
+ | |
+ DrawString(clipNeeded ? field->ClippedString() : field->String(), parent, rect); | |
+} | |
+ | |
+ | |
+float | |
+BStringColumn::GetPreferredWidth(BField *_field, BView* parent) const | |
+{ | |
+ BStringField* field = static_cast<BStringField*>(_field); | |
+ return parent->StringWidth(field->String()) + 2 * kTEXT_MARGIN; | |
+} | |
+ | |
+ | |
+int | |
+BStringColumn::CompareFields(BField* field1, BField* field2) | |
+{ | |
+ return ICompare(((BStringField*)field1)->String(), | |
+ (((BStringField*)field2)->String())); | |
+} | |
+ | |
+ | |
+bool | |
+BStringColumn::AcceptsField(const BField *field) const | |
+{ | |
+ return static_cast<bool>(dynamic_cast<const BStringField*>(field)); | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BDateField::BDateField(time_t *t) | |
+ : | |
+ fTime(*localtime(t)), | |
+ fUnixTime(*t), | |
+ fSeconds(0), | |
+ fClippedString(""), | |
+ fWidth(0) | |
+{ | |
+ fSeconds = mktime(&fTime); | |
+} | |
+ | |
+ | |
+void | |
+BDateField::SetWidth(float width) | |
+{ | |
+ fWidth = width; | |
+} | |
+ | |
+ | |
+float | |
+BDateField::Width() | |
+{ | |
+ return fWidth; | |
+} | |
+ | |
+ | |
+void | |
+BDateField::SetClippedString(const char* val) | |
+{ | |
+ fClippedString = val; | |
+} | |
+ | |
+ | |
+const char* | |
+BDateField::ClippedString() | |
+{ | |
+ return fClippedString.String(); | |
+} | |
+ | |
+ | |
+time_t | |
+BDateField::Seconds() | |
+{ | |
+ return fSeconds; | |
+} | |
+ | |
+ | |
+time_t | |
+BDateField::UnixTime() | |
+{ | |
+ return fUnixTime; | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BDateColumn::BDateColumn(const char* title, float width, float minWidth, | |
+ float maxWidth, alignment align) | |
+ : BTitledColumn(title, width, minWidth, maxWidth, align), | |
+ fTitle(title) | |
+{ | |
+} | |
+ | |
+ | |
+const char *kTIME_FORMATS[] = { | |
+ "%A, %B %d %Y, %I:%M:%S %p", // Monday, July 09 1997, 05:08:15 PM | |
+ "%a, %b %d %Y, %I:%M:%S %p", // Mon, Jul 09 1997, 05:08:15 PM | |
+ "%a, %b %d %Y, %I:%M %p", // Mon, Jul 09 1997, 05:08 PM | |
+ "%b %d %Y, %I:%M %p", // Jul 09 1997, 05:08 PM | |
+ "%m/%d/%y, %I:%M %p", // 07/09/97, 05:08 PM | |
+ "%m/%d/%y", // 07/09/97 | |
+ NULL | |
+}; | |
+ | |
+ | |
+void | |
+BDateColumn::DrawField(BField* _field, BRect rect, BView* parent) | |
+{ | |
+ float width = rect.Width() - (2 * kTEXT_MARGIN); | |
+ BDateField* field = (BDateField*)_field; | |
+ | |
+ if (field->Width() != rect.Width()) { | |
+ char dateString[256]; | |
+ time_t curtime = field->UnixTime(); | |
+ tm time_data; | |
+ BFont font; | |
+ | |
+ parent->GetFont(&font); | |
+ localtime_r(&curtime, &time_data); | |
+ | |
+ for (int32 index = 0; ; index++) { | |
+ if (!kTIME_FORMATS[index]) | |
+ break; | |
+ strftime(dateString, 256, kTIME_FORMATS[index], &time_data); | |
+ if (font.StringWidth(dateString) <= width) | |
+ break; | |
+ } | |
+ | |
+ if (font.StringWidth(dateString) > width) { | |
+ BString out_string(dateString); | |
+ | |
+ parent->TruncateString(&out_string, B_TRUNCATE_MIDDLE, width + 2); | |
+ strcpy(dateString, out_string.String()); | |
+ } | |
+ field->SetClippedString(dateString); | |
+ field->SetWidth(width); | |
+ } | |
+ | |
+ DrawString(field->ClippedString(), parent, rect); | |
+} | |
+ | |
+ | |
+int | |
+BDateColumn::CompareFields(BField* field1, BField* field2) | |
+{ | |
+ return((BDateField*)field1)->Seconds() - ((BDateField*)field2)->Seconds(); | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BSizeField::BSizeField(off_t size) | |
+ : | |
+ fSize(size) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BSizeField::SetSize(off_t size) | |
+{ | |
+ fSize = size; | |
+} | |
+ | |
+ | |
+off_t | |
+BSizeField::Size() | |
+{ | |
+ return fSize; | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BSizeColumn::BSizeColumn(const char* title, float width, float minWidth, | |
+ float maxWidth, alignment align) | |
+ : BTitledColumn(title, width, minWidth, maxWidth, align) | |
+{ | |
+} | |
+ | |
+ | |
+const int64 kKB_SIZE = 1024; | |
+const int64 kMB_SIZE = 1048576; | |
+const int64 kGB_SIZE = 1073741824; | |
+const int64 kTB_SIZE = kGB_SIZE * kKB_SIZE; | |
+ | |
+const char *kSIZE_FORMATS[] = { | |
+ "%.2f %s", | |
+ "%.1f %s", | |
+ "%.f %s", | |
+ "%.f%s", | |
+ 0 | |
+}; | |
+ | |
+void | |
+BSizeColumn::DrawField(BField* _field, BRect rect, BView* parent) | |
+{ | |
+ char str[256]; | |
+ float width = rect.Width() - (2 * kTEXT_MARGIN); | |
+ BFont font; | |
+ BString string; | |
+ off_t size = ((BSizeField*)_field)->Size(); | |
+ | |
+ parent->GetFont(&font); | |
+ if (size < kKB_SIZE) { | |
+ sprintf(str, "%" B_PRId64 " bytes", size); | |
+ if (font.StringWidth(str) > width) | |
+ sprintf(str, "%" B_PRId64 " B", size); | |
+ } else { | |
+ const char* suffix; | |
+ float float_value; | |
+ if (size >= kTB_SIZE) { | |
+ suffix = "TB"; | |
+ float_value = (float)size / kTB_SIZE; | |
+ } else if (size >= kGB_SIZE) { | |
+ suffix = "GB"; | |
+ float_value = (float)size / kGB_SIZE; | |
+ } else if (size >= kMB_SIZE) { | |
+ suffix = "MB"; | |
+ float_value = (float)size / kMB_SIZE; | |
+ } else { | |
+ suffix = "KB"; | |
+ float_value = (float)size / kKB_SIZE; | |
+ } | |
+ | |
+ for (int32 index = 0; ; index++) { | |
+ if (!kSIZE_FORMATS[index]) | |
+ break; | |
+ | |
+ sprintf(str, kSIZE_FORMATS[index], float_value, suffix); | |
+ // strip off an insignificant zero so we don't get readings | |
+ // such as 1.00 | |
+ char *period = 0; | |
+ char *tmp (NULL); | |
+ for (tmp = str; *tmp; tmp++) { | |
+ if (*tmp == '.') | |
+ period = tmp; | |
+ } | |
+ if (period && period[1] && period[2] == '0') { | |
+ // move the rest of the string over the insignificant zero | |
+ for (tmp = &period[2]; *tmp; tmp++) | |
+ *tmp = tmp[1]; | |
+ } | |
+ if (font.StringWidth(str) <= width) | |
+ break; | |
+ } | |
+ } | |
+ | |
+ string = str; | |
+ parent->TruncateString(&string, B_TRUNCATE_MIDDLE, width + 2); | |
+ DrawString(string.String(), parent, rect); | |
+} | |
+ | |
+ | |
+int | |
+BSizeColumn::CompareFields(BField* field1, BField* field2) | |
+{ | |
+ return ((BSizeField*)field1)->Size() - ((BSizeField*)field2)->Size(); | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BIntegerField::BIntegerField(int32 number) | |
+ : | |
+ fInteger(number) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BIntegerField::SetValue(int32 value) | |
+{ | |
+ fInteger = value; | |
+} | |
+ | |
+ | |
+int32 | |
+BIntegerField::Value() | |
+{ | |
+ return fInteger; | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BIntegerColumn::BIntegerColumn(const char* title, float width, float minWidth, | |
+ float maxWidth, alignment align) | |
+ : BTitledColumn(title, width, minWidth, maxWidth, align) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BIntegerColumn::DrawField(BField *field, BRect rect, BView* parent) | |
+{ | |
+ char formatted[256]; | |
+ float width = rect.Width() - (2 * kTEXT_MARGIN); | |
+ BString string; | |
+ | |
+ sprintf(formatted, "%d", (int)((BIntegerField*)field)->Value()); | |
+ | |
+ string = formatted; | |
+ parent->TruncateString(&string, B_TRUNCATE_MIDDLE, width + 2); | |
+ DrawString(string.String(), parent, rect); | |
+} | |
+ | |
+ | |
+int | |
+BIntegerColumn::CompareFields(BField *field1, BField *field2) | |
+{ | |
+ return (((BIntegerField*)field1)->Value() - ((BIntegerField*)field2)->Value()); | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+GraphColumn::GraphColumn(const char* name, float width, float minWidth, | |
+ float maxWidth, alignment align) | |
+ : BIntegerColumn(name, width, minWidth, maxWidth, align) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+GraphColumn::DrawField(BField* field, BRect rect, BView* parent) | |
+{ | |
+ int number = ((BIntegerField*)field)->Value(); | |
+ | |
+ if (number > 100) | |
+ number = 100; | |
+ else if (number < 0) | |
+ number = 0; | |
+ | |
+ BRect graphRect(rect); | |
+ graphRect.InsetBy(5, 3); | |
+ parent->StrokeRect(graphRect); | |
+ if (number > 0) { | |
+ graphRect.InsetBy(1, 1); | |
+ float val = graphRect.Width() * (float) number / 100; | |
+ graphRect.right = graphRect.left + val; | |
+ parent->SetHighColor(0, 0, 190); | |
+ parent->FillRect(graphRect); | |
+ } | |
+ | |
+ parent->SetDrawingMode(B_OP_INVERT); | |
+ parent->SetHighColor(128, 128, 128); | |
+ char numstr[256]; | |
+ sprintf(numstr, "%d%%", number); | |
+ | |
+ float width = be_plain_font->StringWidth(numstr); | |
+ parent->MovePenTo(rect.left + rect.Width() / 2 - width / 2, rect.bottom - FontHeight()); | |
+ parent->DrawString(numstr); | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BBitmapField::BBitmapField(BBitmap *bitmap) | |
+ : | |
+ fBitmap(bitmap) | |
+{ | |
+} | |
+ | |
+ | |
+const BBitmap* | |
+BBitmapField::Bitmap() | |
+{ | |
+ return fBitmap; | |
+} | |
+ | |
+ | |
+void | |
+BBitmapField::SetBitmap(BBitmap* bitmap) | |
+{ | |
+ fBitmap = bitmap; | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BBitmapColumn::BBitmapColumn(const char* title, float width, float minWidth, | |
+ float maxWidth, alignment align) | |
+ : BTitledColumn(title, width, minWidth, maxWidth, align) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BBitmapColumn::DrawField(BField* field, BRect rect, BView* parent) | |
+{ | |
+ BBitmapField *bitmapField = static_cast<BBitmapField *>(field); | |
+ const BBitmap *bitmap = bitmapField->Bitmap(); | |
+ | |
+ if (bitmap != NULL) { | |
+ float x = 0.0; | |
+ BRect r = bitmap->Bounds(); | |
+ float y = rect.top + ((rect.Height() - r.Height()) / 2); | |
+ | |
+ switch (Alignment()) { | |
+ default: | |
+ case B_ALIGN_LEFT: | |
+ x = rect.left + kTEXT_MARGIN; | |
+ break; | |
+ | |
+ case B_ALIGN_CENTER: | |
+ x = rect.left + ((rect.Width() - r.Width()) / 2); | |
+ break; | |
+ | |
+ case B_ALIGN_RIGHT: | |
+ x = rect.right - kTEXT_MARGIN - r.Width(); | |
+ break; | |
+ } | |
+ // setup drawing mode according to bitmap color space, | |
+ // restore previous mode after drawing | |
+ drawing_mode oldMode = parent->DrawingMode(); | |
+ if (bitmap->ColorSpace() == B_RGBA32 | |
+ || bitmap->ColorSpace() == B_RGBA32_BIG) { | |
+ parent->SetDrawingMode(B_OP_ALPHA); | |
+ parent->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); | |
+ } else { | |
+ parent->SetDrawingMode(B_OP_OVER); | |
+ } | |
+ | |
+ parent->DrawBitmap(bitmap, BPoint(x, y)); | |
+ | |
+ parent->SetDrawingMode(oldMode); | |
+ } | |
+} | |
+ | |
+ | |
+int | |
+BBitmapColumn::CompareFields(BField* /*field1*/, BField* /*field2*/) | |
+{ | |
+ // Comparing bitmaps doesn't really make sense... | |
+ return 0; | |
+} | |
+ | |
+ | |
+bool | |
+BBitmapColumn::AcceptsField(const BField *field) const | |
+{ | |
+ return static_cast<bool>(dynamic_cast<const BBitmapField*>(field)); | |
+} | |
+ | |
+ | |
Index: haiku/libhaiku/ObjectListPrivate.h | |
=================================================================== | |
--- haiku/libhaiku/ObjectListPrivate.h (revision 0) | |
+++ haiku/libhaiku/ObjectListPrivate.h (revision 0) | |
@@ -0,0 +1,38 @@ | |
+/* | |
+ * Copyright 2011, Oliver Tappe <zooey@hirschkaefer.de> | |
+ * Distributed under the terms of the MIT License. | |
+ */ | |
+#ifndef _OBJECT_LIST_PRIVATE_H | |
+#define _OBJECT_LIST_PRIVATE_H | |
+ | |
+ | |
+#include <ObjectList.h> | |
+ | |
+ | |
+template<class T> | |
+class BObjectList<T>::Private { | |
+public: | |
+ Private(BObjectList<T>* objectList) | |
+ : | |
+ fObjectList(objectList) | |
+ { | |
+ } | |
+ | |
+ BList* | |
+ AsBList() | |
+ { | |
+ return fObjectList; | |
+ } | |
+ | |
+ const BList* | |
+ AsBList() const | |
+ { | |
+ return fObjectList; | |
+ } | |
+ | |
+private: | |
+ BObjectList<T>* fObjectList; | |
+}; | |
+ | |
+ | |
+#endif // _OBJECT_LIST_PRIVATE_H | |
Index: haiku/libhaiku/ColorTools.h | |
=================================================================== | |
--- haiku/libhaiku/ColorTools.h (revision 0) | |
+++ haiku/libhaiku/ColorTools.h (revision 0) | |
@@ -0,0 +1,105 @@ | |
+/* | |
+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. | |
+*/ | |
+ | |
+/******************************************************************************* | |
+/ | |
+/ File: ColorTools.h | |
+/ | |
+/ Description: Additional experimental color manipulation functions. | |
+/ | |
+/ Copyright 2000, Be Incorporated, All Rights Reserved | |
+/ | |
+*******************************************************************************/ | |
+ | |
+ | |
+#ifndef _COLOR_TOOLS_H | |
+#define _COLOR_TOOLS_H | |
+ | |
+#include <GraphicsDefs.h> | |
+ | |
+#if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI | |
+ | |
+namespace BExperimental { | |
+ | |
+// Comparison operators. | |
+ | |
+inline bool operator==(const rgb_color c1, const rgb_color c2) | |
+{ | |
+ return (*((uint32*)&c1)) == (*((uint32*)&c2)); | |
+} | |
+ | |
+inline bool operator!=(const rgb_color c1, const rgb_color c2) | |
+{ | |
+ return (*((uint32*)&c1)) != (*((uint32*)&c2)); | |
+} | |
+ | |
+#ifndef __HAIKU__ | |
+// Color creation. | |
+ | |
+inline rgb_color make_color(uint8 red, uint8 green, uint8 blue, uint8 alpha=255) | |
+{ | |
+ rgb_color c; | |
+ c.red = red; | |
+ c.green = green; | |
+ c.blue = blue; | |
+ c.alpha = alpha; | |
+ return c; | |
+} | |
+#endif | |
+ | |
+// Mix two colors together, ignoring their relative alpha channels. | |
+// If amount is 0, the result is color1; if 255, the result is color2; | |
+// if another value, it is somewhere in-between. The resulting alpha | |
+// channel is mixed exactly like the other color channels. | |
+rgb_color mix_color(rgb_color color1, rgb_color color2, uint8 amount); | |
+ | |
+// Blend two colors together, weighting by their relative alpha channels. | |
+// The resulting color is the same as mix_color(), except that the amount | |
+// used from color1 and color2's color channels is dependent on that color's | |
+// alpha channel. For example, if color1.alpha is 0 and color2.alpha is | |
+// 255, the resulting red, green, and blue values will be the same as those | |
+// in color2, regardless of 'amount'. | |
+rgb_color blend_color(rgb_color color1, rgb_color color2, uint8 amount); | |
+ | |
+// Return a color that is the disabled representation of 'color' when drawn | |
+// on a solid color 'background'. | |
+rgb_color disable_color(rgb_color color, rgb_color background); | |
+ | |
+} // namespace BExperimental | |
+ | |
+using namespace BExperimental; | |
+ | |
+#endif | |
+ | |
+#endif | |
Index: haiku/libhaiku/ColumnListView.cpp | |
=================================================================== | |
--- haiku/libhaiku/ColumnListView.cpp (revision 0) | |
+++ haiku/libhaiku/ColumnListView.cpp (revision 0) | |
@@ -0,0 +1,4884 @@ | |
+/* | |
+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. | |
+*/ | |
+ | |
+/******************************************************************************* | |
+/ | |
+/ File: ColumnListView.cpp | |
+/ | |
+/ Description: Experimental multi-column list view. | |
+/ | |
+/ Copyright 2000+, Be Incorporated, All Rights Reserved | |
+/ By Jeff Bush | |
+/ | |
+*******************************************************************************/ | |
+ | |
+#include "ColumnListView.h" | |
+ | |
+#include <typeinfo> | |
+ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+ | |
+#include <Application.h> | |
+#include <Bitmap.h> | |
+#include <ControlLook.h> | |
+#include <Cursor.h> | |
+#include <Debug.h> | |
+#include <GraphicsDefs.h> | |
+#include <LayoutUtils.h> | |
+#include <MenuItem.h> | |
+#include <PopUpMenu.h> | |
+#include <Region.h> | |
+#include <ScrollBar.h> | |
+#include <String.h> | |
+#include <SupportDefs.h> | |
+#include <Window.h> | |
+ | |
+#include <ObjectListPrivate.h> | |
+ | |
+#include "ColorTools.h" | |
+#include "ObjectList.h" | |
+ | |
+#define DOUBLE_BUFFERED_COLUMN_RESIZE 1 | |
+#define SMART_REDRAW 1 | |
+#define DRAG_TITLE_OUTLINE 1 | |
+#define CONSTRAIN_CLIPPING_REGION 1 | |
+#define LOWER_SCROLLBAR 0 | |
+ | |
+namespace BPrivate { | |
+ | |
+static const unsigned char kDownSortArrow8x8[] = { | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, | |
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff | |
+}; | |
+ | |
+static const unsigned char kUpSortArrow8x8[] = { | |
+ 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, | |
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff | |
+}; | |
+ | |
+static const unsigned char kDownSortArrow8x8Invert[] = { | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
+ 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff | |
+}; | |
+ | |
+static const unsigned char kUpSortArrow8x8Invert[] = { | |
+ 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0xff, 0xff, | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
+ 0xff, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, 0xff, | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | |
+ 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xff, | |
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff | |
+}; | |
+ | |
+static const float kTintedLineTint = 1.04; | |
+ | |
+static const float kTitleHeight = 16.0; | |
+static const float kLatchWidth = 15.0; | |
+ | |
+ | |
+static const rgb_color kColor[B_COLOR_TOTAL] = | |
+{ | |
+ {255, 255, 255, 255}, // B_COLOR_BACKGROUND | |
+ { 0, 0, 0, 255}, // B_COLOR_TEXT | |
+ {148, 148, 148, 255}, // B_COLOR_ROW_DIVIDER | |
+ {190, 190, 190, 255}, // B_COLOR_SELECTION | |
+ { 0, 0, 0, 255}, // B_COLOR_SELECTION_TEXT | |
+ {200, 200, 200, 255}, // B_COLOR_NON_FOCUS_SELECTION | |
+ {180, 180, 180, 180}, // B_COLOR_EDIT_BACKGROUND | |
+ { 0, 0, 0, 255}, // B_COLOR_EDIT_TEXT | |
+ {215, 215, 215, 255}, // B_COLOR_HEADER_BACKGROUND | |
+ { 0, 0, 0, 255}, // B_COLOR_HEADER_TEXT | |
+ { 0, 0, 0, 255}, // B_COLOR_SEPARATOR_LINE | |
+ { 0, 0, 0, 255}, // B_COLOR_SEPARATOR_BORDER | |
+}; | |
+ | |
+static const int32 kMaxDepth = 1024; | |
+static const float kLeftMargin = kLatchWidth; | |
+static const float kRightMargin = 8; | |
+static const float kOutlineLevelIndent = kLatchWidth; | |
+static const float kColumnResizeAreaWidth = 10.0; | |
+static const float kRowDragSensitivity = 5.0; | |
+static const float kDoubleClickMoveSensitivity = 4.0; | |
+static const float kSortIndicatorWidth = 9.0; | |
+static const float kDropHighlightLineHeight = 2.0; | |
+ | |
+static const uint32 kToggleColumn = 'BTCL'; | |
+ | |
+class BRowContainer : public BObjectList<BRow> | |
+{ | |
+}; | |
+ | |
+class TitleView : public BView { | |
+ typedef BView _inherited; | |
+public: | |
+ TitleView(BRect frame, OutlineView* outlineView, | |
+ BList* visibleColumns, BList* sortColumns, | |
+ BColumnListView* masterView, | |
+ uint32 resizingMode); | |
+ virtual ~TitleView(); | |
+ | |
+ void ColumnAdded(BColumn* column); | |
+ void ColumnResized(BColumn* column, float oldWidth); | |
+ void SetColumnVisible(BColumn* column, bool visible); | |
+ | |
+ virtual void Draw(BRect updateRect); | |
+ virtual void ScrollTo(BPoint where); | |
+ virtual void MessageReceived(BMessage* message); | |
+ virtual void MouseDown(BPoint where); | |
+ virtual void MouseMoved(BPoint where, uint32 transit, | |
+ const BMessage* dragMessage); | |
+ virtual void MouseUp(BPoint where); | |
+ virtual void FrameResized(float width, float height); | |
+ | |
+ void MoveColumn(BColumn* column, int32 index); | |
+ void SetColumnFlags(column_flags flags); | |
+ | |
+ void SetEditMode(bool state) | |
+ { fEditMode = state; } | |
+ | |
+ float MarginWidth() const; | |
+ | |
+private: | |
+ void GetTitleRect(BColumn* column, BRect* _rect); | |
+ int32 FindColumn(BPoint where, float* _leftEdge); | |
+ void FixScrollBar(bool scrollToFit); | |
+ void DragSelectedColumn(BPoint where); | |
+ void ResizeSelectedColumn(BPoint where, | |
+ bool preferred = false); | |
+ void ComputeDragBoundries(BColumn* column, | |
+ BPoint where); | |
+ void DrawTitle(BView* view, BRect frame, | |
+ BColumn* column, bool depressed); | |
+ | |
+ float _VirtualWidth() const; | |
+ | |
+ OutlineView* fOutlineView; | |
+ BList* fColumns; | |
+ BList* fSortColumns; | |
+// float fColumnsWidth; | |
+ BRect fVisibleRect; | |
+ | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ BBitmap* fDrawBuffer; | |
+ BView* fDrawBufferView; | |
+#endif | |
+ | |
+ enum { | |
+ INACTIVE, | |
+ RESIZING_COLUMN, | |
+ PRESSING_COLUMN, | |
+ DRAG_COLUMN_INSIDE_TITLE, | |
+ DRAG_COLUMN_OUTSIDE_TITLE | |
+ } fCurrentState; | |
+ | |
+ BPopUpMenu* fColumnPop; | |
+ BColumnListView* fMasterView; | |
+ bool fEditMode; | |
+ int32 fColumnFlags; | |
+ | |
+ // State information for resizing/dragging | |
+ BColumn* fSelectedColumn; | |
+ BRect fSelectedColumnRect; | |
+ bool fResizingFirstColumn; | |
+ BPoint fClickPoint; // offset within cell | |
+ float fLeftDragBoundry; | |
+ float fRightDragBoundry; | |
+ BPoint fCurrentDragPosition; | |
+ | |
+ | |
+ BBitmap* fUpSortArrow; | |
+ BBitmap* fDownSortArrow; | |
+ | |
+ BCursor* fResizeCursor; | |
+ BCursor* fMinResizeCursor; | |
+ BCursor* fMaxResizeCursor; | |
+ BCursor* fColumnMoveCursor; | |
+}; | |
+ | |
+class OutlineView : public BView { | |
+ typedef BView _inherited; | |
+public: | |
+ OutlineView(BRect, BList* visibleColumns, | |
+ BList* sortColumns, | |
+ BColumnListView* listView); | |
+ virtual ~OutlineView(); | |
+ | |
+ virtual void Draw(BRect); | |
+ const BRect& VisibleRect() const; | |
+ | |
+ void RedrawColumn(BColumn* column, float leftEdge, | |
+ bool isFirstColumn); | |
+ void StartSorting(); | |
+ float GetColumnPreferredWidth(BColumn* column); | |
+ | |
+ void AddRow(BRow*, int32 index, BRow* TheRow); | |
+ BRow* CurrentSelection(BRow* lastSelected) const; | |
+ void ToggleFocusRowSelection(bool selectRange); | |
+ void ToggleFocusRowOpen(); | |
+ void ChangeFocusRow(bool up, bool updateSelection, | |
+ bool addToCurrentSelection); | |
+ void MoveFocusToVisibleRect(); | |
+ void ExpandOrCollapse(BRow* parent, bool expand); | |
+ void RemoveRow(BRow*); | |
+ BRowContainer* RowList(); | |
+ void UpdateRow(BRow*); | |
+ bool FindParent(BRow* row, BRow** _parent, | |
+ bool* _isVisible); | |
+ int32 IndexOf(BRow* row); | |
+ void Deselect(BRow*); | |
+ void AddToSelection(BRow*); | |
+ void DeselectAll(); | |
+ BRow* FocusRow() const; | |
+ void SetFocusRow(BRow* row, bool select); | |
+ BRow* FindRow(float ypos, int32* _indent, | |
+ float* _top); | |
+ bool FindRect(const BRow* row, BRect* _rect); | |
+ void ScrollTo(const BRow* row); | |
+ | |
+ void Clear(); | |
+ void SetSelectionMode(list_view_type type); | |
+ list_view_type SelectionMode() const; | |
+ void SetMouseTrackingEnabled(bool); | |
+ void FixScrollBar(bool scrollToFit); | |
+ void SetEditMode(bool state) | |
+ { fEditMode = state; } | |
+ | |
+ virtual void FrameResized(float width, float height); | |
+ virtual void ScrollTo(BPoint where); | |
+ virtual void MouseDown(BPoint where); | |
+ virtual void MouseMoved(BPoint where, uint32 transit, | |
+ const BMessage* dragMessage); | |
+ virtual void MouseUp(BPoint where); | |
+ virtual void MessageReceived(BMessage* message); | |
+ | |
+private: | |
+ bool SortList(BRowContainer* list, bool isVisible); | |
+ static int32 DeepSortThreadEntry(void* outlineView); | |
+ void DeepSort(); | |
+ void SelectRange(BRow* start, BRow* end); | |
+ int32 CompareRows(BRow* row1, BRow* row2); | |
+ void AddSorted(BRowContainer* list, BRow* row); | |
+ void RecursiveDeleteRows(BRowContainer* list, | |
+ bool owner); | |
+ void InvalidateCachedPositions(); | |
+ bool FindVisibleRect(BRow* row, BRect* _rect); | |
+ | |
+ BList* fColumns; | |
+ BList* fSortColumns; | |
+ float fItemsHeight; | |
+ BRowContainer fRows; | |
+ BRect fVisibleRect; | |
+ | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ BBitmap* fDrawBuffer; | |
+ BView* fDrawBufferView; | |
+#endif | |
+ | |
+ BRow* fFocusRow; | |
+ BRect fFocusRowRect; | |
+ BRow* fRollOverRow; | |
+ | |
+ BRow fSelectionListDummyHead; | |
+ BRow* fLastSelectedItem; | |
+ BRow* fFirstSelectedItem; | |
+ | |
+ thread_id fSortThread; | |
+ int32 fNumSorted; | |
+ bool fSortCancelled; | |
+ | |
+ enum CurrentState { | |
+ INACTIVE, | |
+ LATCH_CLICKED, | |
+ ROW_CLICKED, | |
+ DRAGGING_ROWS | |
+ }; | |
+ | |
+ CurrentState fCurrentState; | |
+ | |
+ | |
+ BColumnListView* fMasterView; | |
+ list_view_type fSelectionMode; | |
+ bool fTrackMouse; | |
+ BField* fCurrentField; | |
+ BRow* fCurrentRow; | |
+ BColumn* fCurrentColumn; | |
+ bool fMouseDown; | |
+ BRect fFieldRect; | |
+ int32 fCurrentCode; | |
+ bool fEditMode; | |
+ | |
+ // State information for mouse/keyboard interaction | |
+ BPoint fClickPoint; | |
+ bool fDragging; | |
+ int32 fClickCount; | |
+ BRow* fTargetRow; | |
+ float fTargetRowTop; | |
+ BRect fLatchRect; | |
+ float fDropHighlightY; | |
+ | |
+ friend class RecursiveOutlineIterator; | |
+}; | |
+ | |
+class RecursiveOutlineIterator { | |
+public: | |
+ RecursiveOutlineIterator( | |
+ BRowContainer* container, | |
+ bool openBranchesOnly = true); | |
+ | |
+ BRow* CurrentRow() const; | |
+ int32 CurrentLevel() const; | |
+ void GoToNext(); | |
+ | |
+private: | |
+ struct { | |
+ BRowContainer* fRowSet; | |
+ int32 fIndex; | |
+ int32 fDepth; | |
+ } fStack[kMaxDepth]; | |
+ | |
+ int32 fStackIndex; | |
+ BRowContainer* fCurrentList; | |
+ int32 fCurrentListIndex; | |
+ int32 fCurrentListDepth; | |
+ bool fOpenBranchesOnly; | |
+}; | |
+ | |
+} // namespace BPrivate | |
+ | |
+ | |
+using namespace BPrivate; | |
+ | |
+ | |
+BField::BField() | |
+{ | |
+} | |
+ | |
+ | |
+BField::~BField() | |
+{ | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+void | |
+BColumn::MouseMoved(BColumnListView* /*parent*/, BRow* /*row*/, | |
+ BField* /*field*/, BRect /*field_rect*/, BPoint/*point*/, | |
+ uint32 /*buttons*/, int32 /*code*/) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BColumn::MouseDown(BColumnListView* /*parent*/, BRow* /*row*/, | |
+ BField* /*field*/, BRect /*field_rect*/, BPoint /*point*/, | |
+ uint32 /*buttons*/) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BColumn::MouseUp(BColumnListView* /*parent*/, BRow* /*row*/, BField* /*field*/) | |
+{ | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BRow::BRow(float height) | |
+ : | |
+ fChildList(NULL), | |
+ fIsExpanded(false), | |
+ fHeight(height), | |
+ fNextSelected(NULL), | |
+ fPrevSelected(NULL), | |
+ fParent(NULL), | |
+ fList(NULL) | |
+{ | |
+} | |
+ | |
+ | |
+BRow::~BRow() | |
+{ | |
+ while (true) { | |
+ BField* field = (BField*) fFields.RemoveItem((int32)0); | |
+ if (field == 0) | |
+ break; | |
+ | |
+ delete field; | |
+ } | |
+} | |
+ | |
+ | |
+bool | |
+BRow::HasLatch() const | |
+{ | |
+ return fChildList != 0; | |
+} | |
+ | |
+ | |
+int32 | |
+BRow::CountFields() const | |
+{ | |
+ return fFields.CountItems(); | |
+} | |
+ | |
+ | |
+BField* | |
+BRow::GetField(int32 index) | |
+{ | |
+ return (BField*)fFields.ItemAt(index); | |
+} | |
+ | |
+ | |
+const BField* | |
+BRow::GetField(int32 index) const | |
+{ | |
+ return (const BField*)fFields.ItemAt(index); | |
+} | |
+ | |
+ | |
+void | |
+BRow::SetField(BField* field, int32 logicalFieldIndex) | |
+{ | |
+ if (fFields.ItemAt(logicalFieldIndex) != 0) | |
+ delete (BField*)fFields.RemoveItem(logicalFieldIndex); | |
+ | |
+ if (NULL != fList) { | |
+ ValidateField(field, logicalFieldIndex); | |
+ BRect inv; | |
+ fList->GetRowRect(this, &inv); | |
+ fList->Invalidate(inv); | |
+ } | |
+ | |
+ fFields.AddItem(field, logicalFieldIndex); | |
+} | |
+ | |
+ | |
+float | |
+BRow::Height() const | |
+{ | |
+ return fHeight; | |
+} | |
+ | |
+ | |
+bool | |
+BRow::IsExpanded() const | |
+{ | |
+ return fIsExpanded; | |
+} | |
+ | |
+ | |
+void | |
+BRow::ValidateFields() const | |
+{ | |
+ for (int32 i = 0; i < CountFields(); i++) | |
+ ValidateField(GetField(i), i); | |
+} | |
+ | |
+ | |
+void | |
+BRow::ValidateField(const BField* field, int32 logicalFieldIndex) const | |
+{ | |
+ // The Fields may be moved by the user, but the logicalFieldIndexes | |
+ // do not change, so we need to map them over when checking the | |
+ // Field types. | |
+ BColumn* col = NULL; | |
+ int32 items = fList->CountColumns(); | |
+ for (int32 i = 0 ; i < items; ++i) { | |
+ col = fList->ColumnAt(i); | |
+ if( col->LogicalFieldNum() == logicalFieldIndex ) | |
+ break; | |
+ } | |
+ | |
+ if (NULL == col) { | |
+ BString dbmessage("\n\n\tThe parent BColumnListView does not have " | |
+ "\n\ta BColumn at the logical field index "); | |
+ dbmessage << logicalFieldIndex << ".\n\n"; | |
+ printf(dbmessage.String()); | |
+ } else { | |
+ if (!col->AcceptsField(field)) { | |
+ BString dbmessage("\n\n\tThe BColumn of type "); | |
+ dbmessage << typeid(*col).name() << "\n\tat logical field index " | |
+ << logicalFieldIndex << "\n\tdoes not support the " | |
+ "field type " | |
+ << typeid(*field).name() << ".\n\n"; | |
+ debugger(dbmessage.String()); | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BColumn::BColumn(float width, float minWidth, float maxWidth, alignment align) | |
+ : | |
+ fWidth(width), | |
+ fMinWidth(minWidth), | |
+ fMaxWidth(maxWidth), | |
+ fVisible(true), | |
+ fList(0), | |
+ fShowHeading(true), | |
+ fAlignment(align) | |
+{ | |
+} | |
+ | |
+ | |
+BColumn::~BColumn() | |
+{ | |
+} | |
+ | |
+ | |
+float | |
+BColumn::Width() const | |
+{ | |
+ return fWidth; | |
+} | |
+ | |
+ | |
+void | |
+BColumn::SetWidth(float width) | |
+{ | |
+ fWidth = width; | |
+} | |
+ | |
+ | |
+float | |
+BColumn::MinWidth() const | |
+{ | |
+ return fMinWidth; | |
+} | |
+ | |
+ | |
+float | |
+BColumn::MaxWidth() const | |
+{ | |
+ return fMaxWidth; | |
+} | |
+ | |
+ | |
+void | |
+BColumn::DrawTitle(BRect, BView*) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BColumn::DrawField(BField*, BRect, BView*) | |
+{ | |
+} | |
+ | |
+ | |
+int | |
+BColumn::CompareFields(BField*, BField*) | |
+{ | |
+ return 0; | |
+} | |
+ | |
+ | |
+void | |
+BColumn::GetColumnName(BString* into) const | |
+{ | |
+ *into = "(Unnamed)"; | |
+} | |
+ | |
+ | |
+float | |
+BColumn::GetPreferredWidth(BField* field, BView* parent) const | |
+{ | |
+ return fWidth; | |
+} | |
+ | |
+ | |
+bool | |
+BColumn::IsVisible() const | |
+{ | |
+ return fVisible; | |
+} | |
+ | |
+ | |
+void | |
+BColumn::SetVisible(bool visible) | |
+{ | |
+ if (fList && (fVisible != visible)) | |
+ fList->SetColumnVisible(this, visible); | |
+} | |
+ | |
+ | |
+bool | |
+BColumn::ShowHeading() const | |
+{ | |
+ return fShowHeading; | |
+} | |
+ | |
+ | |
+void | |
+BColumn::SetShowHeading(bool state) | |
+{ | |
+ fShowHeading = state; | |
+} | |
+ | |
+ | |
+alignment | |
+BColumn::Alignment() const | |
+{ | |
+ return fAlignment; | |
+} | |
+ | |
+ | |
+void | |
+BColumn::SetAlignment(alignment align) | |
+{ | |
+ fAlignment = align; | |
+} | |
+ | |
+ | |
+bool | |
+BColumn::WantsEvents() const | |
+{ | |
+ return fWantsEvents; | |
+} | |
+ | |
+ | |
+void | |
+BColumn::SetWantsEvents(bool state) | |
+{ | |
+ fWantsEvents = state; | |
+} | |
+ | |
+ | |
+int32 | |
+BColumn::LogicalFieldNum() const | |
+{ | |
+ return fFieldID; | |
+} | |
+ | |
+ | |
+bool | |
+BColumn::AcceptsField(const BField*) const | |
+{ | |
+ return true; | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+BColumnListView::BColumnListView(BRect rect, const char* name, | |
+ uint32 resizingMode, uint32 flags, border_style border, | |
+ bool showHorizontalScrollbar) | |
+ : | |
+ BView(rect, name, resizingMode, | |
+ flags | B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE), | |
+ fStatusView(NULL), | |
+ fSelectionMessage(NULL), | |
+ fSortingEnabled(true), | |
+ fLatchWidth(kLatchWidth), | |
+ fBorderStyle(border), | |
+ fShowingHorizontalScrollBar(showHorizontalScrollbar) | |
+{ | |
+ _Init(); | |
+} | |
+ | |
+ | |
+BColumnListView::BColumnListView(const char* name, uint32 flags, | |
+ border_style border, bool showHorizontalScrollbar) | |
+ : | |
+ BView(name, flags | B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE), | |
+ fStatusView(NULL), | |
+ fSelectionMessage(NULL), | |
+ fSortingEnabled(true), | |
+ fLatchWidth(kLatchWidth), | |
+ fBorderStyle(border), | |
+ fShowingHorizontalScrollBar(showHorizontalScrollbar) | |
+{ | |
+ _Init(); | |
+} | |
+ | |
+ | |
+BColumnListView::~BColumnListView() | |
+{ | |
+ while (BColumn* column = (BColumn*)fColumns.RemoveItem((int32)0)) | |
+ delete column; | |
+} | |
+ | |
+ | |
+bool | |
+BColumnListView::InitiateDrag(BPoint, bool) | |
+{ | |
+ return false; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::MessageDropped(BMessage*, BPoint) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::ExpandOrCollapse(BRow* row, bool Open) | |
+{ | |
+ fOutlineView->ExpandOrCollapse(row, Open); | |
+} | |
+ | |
+ | |
+status_t | |
+BColumnListView::Invoke(BMessage* message) | |
+{ | |
+ if (message == 0) | |
+ message = Message(); | |
+ | |
+ return BInvoker::Invoke(message); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::ItemInvoked() | |
+{ | |
+ Invoke(); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetInvocationMessage(BMessage* message) | |
+{ | |
+ SetMessage(message); | |
+} | |
+ | |
+ | |
+BMessage* | |
+BColumnListView::InvocationMessage() const | |
+{ | |
+ return Message(); | |
+} | |
+ | |
+ | |
+uint32 | |
+BColumnListView::InvocationCommand() const | |
+{ | |
+ return Command(); | |
+} | |
+ | |
+ | |
+BRow* | |
+BColumnListView::FocusRow() const | |
+{ | |
+ return fOutlineView->FocusRow(); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetFocusRow(int32 Index, bool Select) | |
+{ | |
+ SetFocusRow(RowAt(Index), Select); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetFocusRow(BRow* row, bool Select) | |
+{ | |
+ fOutlineView->SetFocusRow(row, Select); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetMouseTrackingEnabled(bool Enabled) | |
+{ | |
+ fOutlineView->SetMouseTrackingEnabled(Enabled); | |
+} | |
+ | |
+ | |
+list_view_type | |
+BColumnListView::SelectionMode() const | |
+{ | |
+ return fOutlineView->SelectionMode(); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::Deselect(BRow* row) | |
+{ | |
+ fOutlineView->Deselect(row); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::AddToSelection(BRow* row) | |
+{ | |
+ fOutlineView->AddToSelection(row); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::DeselectAll() | |
+{ | |
+ fOutlineView->DeselectAll(); | |
+} | |
+ | |
+ | |
+BRow* | |
+BColumnListView::CurrentSelection(BRow* lastSelected) const | |
+{ | |
+ return fOutlineView->CurrentSelection(lastSelected); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SelectionChanged() | |
+{ | |
+ if (fSelectionMessage) | |
+ Invoke(fSelectionMessage); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetSelectionMessage(BMessage* message) | |
+{ | |
+ if (fSelectionMessage == message) | |
+ return; | |
+ | |
+ delete fSelectionMessage; | |
+ fSelectionMessage = message; | |
+} | |
+ | |
+ | |
+BMessage* | |
+BColumnListView::SelectionMessage() | |
+{ | |
+ return fSelectionMessage; | |
+} | |
+ | |
+ | |
+uint32 | |
+BColumnListView::SelectionCommand() const | |
+{ | |
+ if (fSelectionMessage) | |
+ return fSelectionMessage->what; | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetSelectionMode(list_view_type mode) | |
+{ | |
+ fOutlineView->SetSelectionMode(mode); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetSortingEnabled(bool enabled) | |
+{ | |
+ fSortingEnabled = enabled; | |
+ fSortColumns.MakeEmpty(); | |
+ fTitleView->Invalidate(); // Erase sort indicators | |
+} | |
+ | |
+ | |
+bool | |
+BColumnListView::SortingEnabled() const | |
+{ | |
+ return fSortingEnabled; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetSortColumn(BColumn* column, bool add, bool ascending) | |
+{ | |
+ if (!SortingEnabled()) | |
+ return; | |
+ | |
+ if (!add) | |
+ fSortColumns.MakeEmpty(); | |
+ | |
+ if (!fSortColumns.HasItem(column)) | |
+ fSortColumns.AddItem(column); | |
+ | |
+ column->fSortAscending = ascending; | |
+ fTitleView->Invalidate(); | |
+ fOutlineView->StartSorting(); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::ClearSortColumns() | |
+{ | |
+ fSortColumns.MakeEmpty(); | |
+ fTitleView->Invalidate(); // Erase sort indicators | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::AddStatusView(BView* view) | |
+{ | |
+ BRect bounds = Bounds(); | |
+ float width = view->Bounds().Width(); | |
+ if (width > bounds.Width() / 2) | |
+ width = bounds.Width() / 2; | |
+ | |
+ fStatusView = view; | |
+ | |
+ Window()->BeginViewTransaction(); | |
+ fHorizontalScrollBar->ResizeBy(-(width + 1), 0); | |
+ fHorizontalScrollBar->MoveBy((width + 1), 0); | |
+ AddChild(view); | |
+ | |
+ BRect viewRect(bounds); | |
+ viewRect.right = width; | |
+ viewRect.top = viewRect.bottom - B_H_SCROLL_BAR_HEIGHT; | |
+ if (fBorderStyle == B_PLAIN_BORDER) | |
+ viewRect.OffsetBy(1, -1); | |
+ else if (fBorderStyle == B_FANCY_BORDER) | |
+ viewRect.OffsetBy(2, -2); | |
+ | |
+ view->SetResizingMode(B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); | |
+ view->ResizeTo(viewRect.Width(), viewRect.Height()); | |
+ view->MoveTo(viewRect.left, viewRect.top); | |
+ Window()->EndViewTransaction(); | |
+} | |
+ | |
+ | |
+BView* | |
+BColumnListView::RemoveStatusView() | |
+{ | |
+ if (fStatusView) { | |
+ float width = fStatusView->Bounds().Width(); | |
+ Window()->BeginViewTransaction(); | |
+ fStatusView->RemoveSelf(); | |
+ fHorizontalScrollBar->MoveBy(-width, 0); | |
+ fHorizontalScrollBar->ResizeBy(width, 0); | |
+ Window()->EndViewTransaction(); | |
+ } | |
+ | |
+ BView* view = fStatusView; | |
+ fStatusView = 0; | |
+ return view; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::AddColumn(BColumn* column, int32 logicalFieldIndex) | |
+{ | |
+ ASSERT(column != NULL); | |
+ | |
+ column->fList = this; | |
+ column->fFieldID = logicalFieldIndex; | |
+ | |
+ // sanity check. If there is already a field with this ID, remove it. | |
+ for (int32 index = 0; index < fColumns.CountItems(); index++) { | |
+ BColumn* existingColumn = (BColumn*) fColumns.ItemAt(index); | |
+ if (existingColumn && existingColumn->fFieldID == logicalFieldIndex) { | |
+ RemoveColumn(existingColumn); | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (column->Width() < column->MinWidth()) | |
+ column->SetWidth(column->MinWidth()); | |
+ else if (column->Width() > column->MaxWidth()) | |
+ column->SetWidth(column->MaxWidth()); | |
+ | |
+ fColumns.AddItem((void*) column); | |
+ fTitleView->ColumnAdded(column); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::MoveColumn(BColumn* column, int32 index) | |
+{ | |
+ ASSERT(column != NULL); | |
+ fTitleView->MoveColumn(column, index); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::RemoveColumn(BColumn* column) | |
+{ | |
+ if (fColumns.HasItem(column)) { | |
+ SetColumnVisible(column, false); | |
+ if (Window() != NULL) | |
+ Window()->UpdateIfNeeded(); | |
+ fColumns.RemoveItem(column); | |
+ } | |
+} | |
+ | |
+ | |
+int32 | |
+BColumnListView::CountColumns() const | |
+{ | |
+ return fColumns.CountItems(); | |
+} | |
+ | |
+ | |
+BColumn* | |
+BColumnListView::ColumnAt(int32 field) const | |
+{ | |
+ return (BColumn*) fColumns.ItemAt(field); | |
+} | |
+ | |
+ | |
+BColumn* | |
+BColumnListView::ColumnAt(BPoint point) const | |
+{ | |
+ float left = MAX(kLeftMargin, LatchWidth()); | |
+ | |
+ for (int i = 0; BColumn* column = (BColumn*)fColumns.ItemAt(i); i++) { | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ float right = left + column->Width(); | |
+ if (point.x >= left && point.x <= right) | |
+ return column; | |
+ | |
+ left = right + 1; | |
+ } | |
+ | |
+ return NULL; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetColumnVisible(BColumn* column, bool visible) | |
+{ | |
+ fTitleView->SetColumnVisible(column, visible); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetColumnVisible(int32 index, bool isVisible) | |
+{ | |
+ BColumn* column = ColumnAt(index); | |
+ if (column) | |
+ column->SetVisible(isVisible); | |
+} | |
+ | |
+ | |
+bool | |
+BColumnListView::IsColumnVisible(int32 index) const | |
+{ | |
+ BColumn* column = ColumnAt(index); | |
+ if (column) | |
+ return column->IsVisible(); | |
+ | |
+ return false; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetColumnFlags(column_flags flags) | |
+{ | |
+ fTitleView->SetColumnFlags(flags); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::ResizeColumnToPreferred(int32 index) | |
+{ | |
+ BColumn* column = ColumnAt(index); | |
+ if (column == NULL) | |
+ return; | |
+ | |
+ // get the preferred column width | |
+ float width = fOutlineView->GetColumnPreferredWidth(column); | |
+ | |
+ // set it | |
+ float oldWidth = column->Width(); | |
+ column->SetWidth(width); | |
+ | |
+ fTitleView->ColumnResized(column, oldWidth); | |
+ fOutlineView->Invalidate(); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::ResizeAllColumnsToPreferred() | |
+{ | |
+ int32 count = CountColumns(); | |
+ for (int32 i = 0; i < count; i++) | |
+ ResizeColumnToPreferred(i); | |
+} | |
+ | |
+ | |
+const BRow* | |
+BColumnListView::RowAt(int32 Index, BRow* parentRow) const | |
+{ | |
+ if (parentRow == 0) | |
+ return fOutlineView->RowList()->ItemAt(Index); | |
+ | |
+ return parentRow->fChildList ? parentRow->fChildList->ItemAt(Index) : NULL; | |
+} | |
+ | |
+ | |
+BRow* | |
+BColumnListView::RowAt(int32 Index, BRow* parentRow) | |
+{ | |
+ if (parentRow == 0) | |
+ return fOutlineView->RowList()->ItemAt(Index); | |
+ | |
+ return parentRow->fChildList ? parentRow->fChildList->ItemAt(Index) : 0; | |
+} | |
+ | |
+ | |
+const BRow* | |
+BColumnListView::RowAt(BPoint point) const | |
+{ | |
+ float top; | |
+ int32 indent; | |
+ return fOutlineView->FindRow(point.y, &indent, &top); | |
+} | |
+ | |
+ | |
+BRow* | |
+BColumnListView::RowAt(BPoint point) | |
+{ | |
+ float top; | |
+ int32 indent; | |
+ return fOutlineView->FindRow(point.y, &indent, &top); | |
+} | |
+ | |
+ | |
+bool | |
+BColumnListView::GetRowRect(const BRow* row, BRect* outRect) const | |
+{ | |
+ return fOutlineView->FindRect(row, outRect); | |
+} | |
+ | |
+ | |
+bool | |
+BColumnListView::FindParent(BRow* row, BRow** _parent, bool* _isVisible) const | |
+{ | |
+ return fOutlineView->FindParent(row, _parent, _isVisible); | |
+} | |
+ | |
+ | |
+int32 | |
+BColumnListView::IndexOf(BRow* row) | |
+{ | |
+ return fOutlineView->IndexOf(row); | |
+} | |
+ | |
+ | |
+int32 | |
+BColumnListView::CountRows(BRow* parentRow) const | |
+{ | |
+ if (parentRow == 0) | |
+ return fOutlineView->RowList()->CountItems(); | |
+ if (parentRow->fChildList) | |
+ return parentRow->fChildList->CountItems(); | |
+ else | |
+ return 0; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::AddRow(BRow* row, BRow* parentRow) | |
+{ | |
+ AddRow(row, -1, parentRow); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::AddRow(BRow* row, int32 index, BRow* parentRow) | |
+{ | |
+ row->fChildList = 0; | |
+ row->fList = this; | |
+ row->ValidateFields(); | |
+ fOutlineView->AddRow(row, index, parentRow); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::RemoveRow(BRow* row) | |
+{ | |
+ fOutlineView->RemoveRow(row); | |
+ row->fList = NULL; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::UpdateRow(BRow* row) | |
+{ | |
+ fOutlineView->UpdateRow(row); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::ScrollTo(const BRow* row) | |
+{ | |
+ fOutlineView->ScrollTo(row); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::ScrollTo(BPoint point) | |
+{ | |
+ fOutlineView->ScrollTo(point); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::Clear() | |
+{ | |
+ fOutlineView->Clear(); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetFont(const BFont* font, uint32 mask) | |
+{ | |
+ // This method is deprecated. | |
+ fOutlineView->SetFont(font, mask); | |
+ fTitleView->SetFont(font, mask); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetFont(ColumnListViewFont font_num, const BFont* font, | |
+ uint32 mask) | |
+{ | |
+ switch (font_num) { | |
+ case B_FONT_ROW: | |
+ fOutlineView->SetFont(font, mask); | |
+ break; | |
+ | |
+ case B_FONT_HEADER: | |
+ fTitleView->SetFont(font, mask); | |
+ break; | |
+ | |
+ default: | |
+ ASSERT(false); | |
+ break; | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::GetFont(ColumnListViewFont font_num, BFont* font) const | |
+{ | |
+ switch (font_num) { | |
+ case B_FONT_ROW: | |
+ fOutlineView->GetFont(font); | |
+ break; | |
+ | |
+ case B_FONT_HEADER: | |
+ fTitleView->GetFont(font); | |
+ break; | |
+ | |
+ default: | |
+ ASSERT(false); | |
+ break; | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetColor(ColumnListViewColor color_num, const rgb_color color) | |
+{ | |
+ if ((int)color_num < 0) { | |
+ ASSERT(false); | |
+ color_num = (ColumnListViewColor) 0; | |
+ } | |
+ | |
+ if ((int)color_num >= (int)B_COLOR_TOTAL) { | |
+ ASSERT(false); | |
+ color_num = (ColumnListViewColor) (B_COLOR_TOTAL - 1); | |
+ } | |
+ | |
+ fColorList[color_num] = color; | |
+} | |
+ | |
+ | |
+rgb_color | |
+BColumnListView::Color(ColumnListViewColor color_num) const | |
+{ | |
+ if ((int)color_num < 0) { | |
+ ASSERT(false); | |
+ color_num = (ColumnListViewColor) 0; | |
+ } | |
+ | |
+ if ((int)color_num >= (int)B_COLOR_TOTAL) { | |
+ ASSERT(false); | |
+ color_num = (ColumnListViewColor) (B_COLOR_TOTAL - 1); | |
+ } | |
+ | |
+ return fColorList[color_num]; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetHighColor(rgb_color color) | |
+{ | |
+ BView::SetHighColor(color); | |
+// fOutlineView->Invalidate(); // Redraw things with the new color | |
+ // Note that this will currently cause | |
+ // an infinite loop, refreshing over and over. | |
+ // A better solution is needed. | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetSelectionColor(rgb_color color) | |
+{ | |
+ fColorList[B_COLOR_SELECTION] = color; | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetBackgroundColor(rgb_color color) | |
+{ | |
+ fColorList[B_COLOR_BACKGROUND] = color; | |
+ fOutlineView->Invalidate(); // Repaint with new color | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetEditColor(rgb_color color) | |
+{ | |
+ fColorList[B_COLOR_EDIT_BACKGROUND] = color; | |
+} | |
+ | |
+ | |
+const rgb_color | |
+BColumnListView::SelectionColor() const | |
+{ | |
+ return fColorList[B_COLOR_SELECTION]; | |
+} | |
+ | |
+ | |
+const rgb_color | |
+BColumnListView::BackgroundColor() const | |
+{ | |
+ return fColorList[B_COLOR_BACKGROUND]; | |
+} | |
+ | |
+ | |
+const rgb_color | |
+BColumnListView::EditColor() const | |
+{ | |
+ return fColorList[B_COLOR_EDIT_BACKGROUND]; | |
+} | |
+ | |
+ | |
+BPoint | |
+BColumnListView::SuggestTextPosition(const BRow* row, | |
+ const BColumn* inColumn) const | |
+{ | |
+ BRect rect; | |
+ GetRowRect(row, &rect); | |
+ if (inColumn) { | |
+ float leftEdge = MAX(kLeftMargin, LatchWidth()); | |
+ for (int index = 0; index < fColumns.CountItems(); index++) { | |
+ BColumn* column = (BColumn*) fColumns.ItemAt(index); | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ if (column == inColumn) { | |
+ rect.left = leftEdge; | |
+ rect.right = rect.left + column->Width(); | |
+ break; | |
+ } | |
+ | |
+ leftEdge += column->Width() + 1; | |
+ } | |
+ } | |
+ | |
+ font_height fh; | |
+ fOutlineView->GetFontHeight(&fh); | |
+ float baseline = floor(rect.top + fh.ascent | |
+ + (rect.Height()+1-(fh.ascent+fh.descent))/2); | |
+ return BPoint(rect.left + 8, baseline); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetLatchWidth(float width) | |
+{ | |
+ fLatchWidth = width; | |
+ Invalidate(); | |
+} | |
+ | |
+ | |
+float | |
+BColumnListView::LatchWidth() const | |
+{ | |
+ return fLatchWidth; | |
+} | |
+ | |
+void | |
+BColumnListView::DrawLatch(BView* view, BRect rect, LatchType position, BRow*) | |
+{ | |
+ const int32 rectInset = 4; | |
+ | |
+ view->SetHighColor(0, 0, 0); | |
+ | |
+ // Make Square | |
+ int32 sideLen = rect.IntegerWidth(); | |
+ if (sideLen > rect.IntegerHeight()) | |
+ sideLen = rect.IntegerHeight(); | |
+ | |
+ // Make Center | |
+ int32 halfWidth = rect.IntegerWidth() / 2; | |
+ int32 halfHeight = rect.IntegerHeight() / 2; | |
+ int32 halfSide = sideLen / 2; | |
+ | |
+ float left = rect.left + halfWidth - halfSide; | |
+ float top = rect.top + halfHeight - halfSide; | |
+ | |
+ BRect itemRect(left, top, left + sideLen, top + sideLen); | |
+ | |
+ // Why it is a pixel high? I don't know. | |
+ itemRect.OffsetBy(0, -1); | |
+ | |
+ itemRect.InsetBy(rectInset, rectInset); | |
+ | |
+ // Make it an odd number of pixels wide, the latch looks better this way | |
+ if ((itemRect.IntegerWidth() % 2) == 1) { | |
+ itemRect.right += 1; | |
+ itemRect.bottom += 1; | |
+ } | |
+ | |
+ switch (position) { | |
+ case B_OPEN_LATCH: | |
+ view->StrokeRect(itemRect); | |
+ view->StrokeLine( | |
+ BPoint(itemRect.left + 2, | |
+ (itemRect.top + itemRect.bottom) / 2), | |
+ BPoint(itemRect.right - 2, | |
+ (itemRect.top + itemRect.bottom) / 2)); | |
+ break; | |
+ | |
+ case B_PRESSED_LATCH: | |
+ view->StrokeRect(itemRect); | |
+ view->StrokeLine( | |
+ BPoint(itemRect.left + 2, | |
+ (itemRect.top + itemRect.bottom) / 2), | |
+ BPoint(itemRect.right - 2, | |
+ (itemRect.top + itemRect.bottom) / 2)); | |
+ view->StrokeLine( | |
+ BPoint((itemRect.left + itemRect.right) / 2, | |
+ itemRect.top + 2), | |
+ BPoint((itemRect.left + itemRect.right) / 2, | |
+ itemRect.bottom - 2)); | |
+ view->InvertRect(itemRect); | |
+ break; | |
+ | |
+ case B_CLOSED_LATCH: | |
+ view->StrokeRect(itemRect); | |
+ view->StrokeLine( | |
+ BPoint(itemRect.left + 2, | |
+ (itemRect.top + itemRect.bottom) / 2), | |
+ BPoint(itemRect.right - 2, | |
+ (itemRect.top + itemRect.bottom) / 2)); | |
+ view->StrokeLine( | |
+ BPoint((itemRect.left + itemRect.right) / 2, | |
+ itemRect.top + 2), | |
+ BPoint((itemRect.left + itemRect.right) / 2, | |
+ itemRect.bottom - 2)); | |
+ break; | |
+ | |
+ case B_NO_LATCH: | |
+ // No drawing | |
+ break; | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::MakeFocus(bool isFocus) | |
+{ | |
+ if (fBorderStyle != B_NO_BORDER) { | |
+ // Redraw focus marks around view | |
+ Invalidate(); | |
+ fHorizontalScrollBar->SetBorderHighlighted(isFocus); | |
+ fVerticalScrollBar->SetBorderHighlighted(isFocus); | |
+ } | |
+ | |
+ BView::MakeFocus(isFocus); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::MessageReceived(BMessage* message) | |
+{ | |
+ // Propagate mouse wheel messages down to child, so that it can | |
+ // scroll. Note we have done so, so we don't go into infinite | |
+ // recursion if this comes back up here. | |
+ if (message->what == B_MOUSE_WHEEL_CHANGED) { | |
+ bool handled; | |
+ if (message->FindBool("be:clvhandled", &handled) != B_OK) { | |
+ message->AddBool("be:clvhandled", true); | |
+ fOutlineView->MessageReceived(message); | |
+ return; | |
+ } | |
+ } | |
+ | |
+ BView::MessageReceived(message); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::KeyDown(const char* bytes, int32 numBytes) | |
+{ | |
+ char c = bytes[0]; | |
+ switch (c) { | |
+ case B_RIGHT_ARROW: | |
+ case B_LEFT_ARROW: | |
+ { | |
+ float minVal, maxVal; | |
+ fHorizontalScrollBar->GetRange(&minVal, &maxVal); | |
+ float smallStep, largeStep; | |
+ fHorizontalScrollBar->GetSteps(&smallStep, &largeStep); | |
+ float oldVal = fHorizontalScrollBar->Value(); | |
+ float newVal = oldVal; | |
+ | |
+ if (c == B_LEFT_ARROW) | |
+ newVal -= smallStep; | |
+ else if (c == B_RIGHT_ARROW) | |
+ newVal += smallStep; | |
+ | |
+ if (newVal < minVal) | |
+ newVal = minVal; | |
+ else if (newVal > maxVal) | |
+ newVal = maxVal; | |
+ | |
+ fHorizontalScrollBar->SetValue(newVal); | |
+ break; | |
+ } | |
+ | |
+ case B_DOWN_ARROW: | |
+ fOutlineView->ChangeFocusRow(false, | |
+ (modifiers() & B_CONTROL_KEY) == 0, | |
+ (modifiers() & B_SHIFT_KEY) != 0); | |
+ break; | |
+ | |
+ case B_UP_ARROW: | |
+ fOutlineView->ChangeFocusRow(true, | |
+ (modifiers() & B_CONTROL_KEY) == 0, | |
+ (modifiers() & B_SHIFT_KEY) != 0); | |
+ break; | |
+ | |
+ case B_PAGE_UP: | |
+ case B_PAGE_DOWN: | |
+ { | |
+ float minValue, maxValue; | |
+ fVerticalScrollBar->GetRange(&minValue, &maxValue); | |
+ float smallStep, largeStep; | |
+ fVerticalScrollBar->GetSteps(&smallStep, &largeStep); | |
+ float currentValue = fVerticalScrollBar->Value(); | |
+ float newValue = currentValue; | |
+ | |
+ if (c == B_PAGE_UP) | |
+ newValue -= largeStep; | |
+ else | |
+ newValue += largeStep; | |
+ | |
+ if (newValue > maxValue) | |
+ newValue = maxValue; | |
+ else if (newValue < minValue) | |
+ newValue = minValue; | |
+ | |
+ fVerticalScrollBar->SetValue(newValue); | |
+ | |
+ // Option + pgup or pgdn scrolls and changes the selection. | |
+ if (modifiers() & B_OPTION_KEY) | |
+ fOutlineView->MoveFocusToVisibleRect(); | |
+ | |
+ break; | |
+ } | |
+ | |
+ case B_ENTER: | |
+ Invoke(); | |
+ break; | |
+ | |
+ case B_SPACE: | |
+ fOutlineView->ToggleFocusRowSelection( | |
+ (modifiers() & B_SHIFT_KEY) != 0); | |
+ break; | |
+ | |
+ case '+': | |
+ fOutlineView->ToggleFocusRowOpen(); | |
+ break; | |
+ | |
+ default: | |
+ BView::KeyDown(bytes, numBytes); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::AttachedToWindow() | |
+{ | |
+ if (!Messenger().IsValid()) | |
+ SetTarget(Window()); | |
+ | |
+ if (SortingEnabled()) fOutlineView->StartSorting(); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::WindowActivated(bool active) | |
+{ | |
+ fOutlineView->Invalidate(); | |
+ // Focus and selection appearance changes with focus | |
+ | |
+ Invalidate(); // Redraw focus marks around view | |
+ BView::WindowActivated(active); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::Draw(BRect updateRect) | |
+{ | |
+ BRect rect = Bounds(); | |
+ | |
+ if (be_control_look != NULL) { | |
+ uint32 flags = 0; | |
+ if (IsFocus() && Window()->IsActive()) | |
+ flags |= BControlLook::B_FOCUSED; | |
+ | |
+ rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); | |
+ | |
+ BRect verticalScrollBarFrame; | |
+ if (!fVerticalScrollBar->IsHidden()) | |
+ verticalScrollBarFrame = fVerticalScrollBar->Frame(); | |
+ BRect horizontalScrollBarFrame; | |
+ if (!fHorizontalScrollBar->IsHidden()) | |
+ horizontalScrollBarFrame = fHorizontalScrollBar->Frame(); | |
+ | |
+ if (fBorderStyle == B_NO_BORDER) { | |
+ // We still draw the left/top border, but not focused. | |
+ // The scrollbars cannot be displayed without frame and | |
+ // it looks bad to have no frame only along the left/top | |
+ // side. | |
+ rgb_color borderColor = tint_color(base, B_DARKEN_2_TINT); | |
+ SetHighColor(borderColor); | |
+ StrokeLine(BPoint(rect.left, rect.bottom), | |
+ BPoint(rect.left, rect.top)); | |
+ StrokeLine(BPoint(rect.left + 1, rect.top), | |
+ BPoint(rect.right, rect.top)); | |
+ } | |
+ | |
+ be_control_look->DrawScrollViewFrame(this, rect, updateRect, | |
+ verticalScrollBarFrame, horizontalScrollBarFrame, | |
+ base, fBorderStyle, flags); | |
+ | |
+ return; | |
+ } | |
+ | |
+ BRect cornerRect(rect.right - B_V_SCROLL_BAR_WIDTH, | |
+ rect.bottom - B_H_SCROLL_BAR_HEIGHT, rect.right, rect.bottom); | |
+ if (fBorderStyle == B_PLAIN_BORDER) { | |
+ BView::SetHighColor(0, 0, 0); | |
+ StrokeRect(rect); | |
+ cornerRect.OffsetBy(-1, -1); | |
+ } else if (fBorderStyle == B_FANCY_BORDER) { | |
+ bool isFocus = IsFocus() && Window()->IsActive(); | |
+ | |
+ if (isFocus) { | |
+ // TODO: Need to find focus color programatically | |
+ BView::SetHighColor(0, 0, 190); | |
+ } else | |
+ BView::SetHighColor(255, 255, 255); | |
+ | |
+ StrokeRect(rect); | |
+ if (!isFocus) | |
+ BView::SetHighColor(184, 184, 184); | |
+ else | |
+ BView::SetHighColor(152, 152, 152); | |
+ | |
+ rect.InsetBy(1,1); | |
+ StrokeRect(rect); | |
+ cornerRect.OffsetBy(-2, -2); | |
+ } | |
+ | |
+ BView::SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); | |
+ // fills lower right rect between scroll bars | |
+ FillRect(cornerRect); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SaveState(BMessage* msg) | |
+{ | |
+ msg->MakeEmpty(); | |
+ | |
+ for (int32 i = 0; BColumn* col = (BColumn*)fColumns.ItemAt(i); i++) { | |
+ msg->AddInt32("ID",col->fFieldID); | |
+ msg->AddFloat("width", col->fWidth); | |
+ msg->AddBool("visible", col->fVisible); | |
+ } | |
+ | |
+ msg->AddBool("sortingenabled", fSortingEnabled); | |
+ | |
+ if (fSortingEnabled) { | |
+ for (int32 i = 0; BColumn* col = (BColumn*)fSortColumns.ItemAt(i); | |
+ i++) { | |
+ msg->AddInt32("sortID", col->fFieldID); | |
+ msg->AddBool("sortascending", col->fSortAscending); | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::LoadState(BMessage* msg) | |
+{ | |
+ int32 id; | |
+ for (int i = 0; msg->FindInt32("ID", i, &id) == B_OK; i++) { | |
+ for (int j = 0; BColumn* column = (BColumn*)fColumns.ItemAt(j); j++) { | |
+ if (column->fFieldID == id) { | |
+ // move this column to position 'i' and set its attributes | |
+ MoveColumn(column, i); | |
+ float width; | |
+ if (msg->FindFloat("width", i, &width) == B_OK) | |
+ column->SetWidth(width); | |
+ bool visible; | |
+ if (msg->FindBool("visible", i, &visible) == B_OK) | |
+ column->SetVisible(visible); | |
+ } | |
+ } | |
+ } | |
+ bool b; | |
+ if (msg->FindBool("sortingenabled", &b) == B_OK) { | |
+ SetSortingEnabled(b); | |
+ for (int k = 0; msg->FindInt32("sortID", k, &id) == B_OK; k++) { | |
+ for (int j = 0; BColumn* column = (BColumn*)fColumns.ItemAt(j); | |
+ j++) { | |
+ if (column->fFieldID == id) { | |
+ // add this column to the sort list | |
+ bool value; | |
+ if (msg->FindBool("sortascending", k, &value) == B_OK) | |
+ SetSortColumn(column, true, value); | |
+ } | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::SetEditMode(bool state) | |
+{ | |
+ fOutlineView->SetEditMode(state); | |
+ fTitleView->SetEditMode(state); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::Refresh() | |
+{ | |
+ if (LockLooper()) { | |
+ Invalidate(); | |
+ fOutlineView->FixScrollBar (true); | |
+ fOutlineView->Invalidate(); | |
+ Window()->UpdateIfNeeded(); | |
+ UnlockLooper(); | |
+ } | |
+} | |
+ | |
+ | |
+BSize | |
+BColumnListView::MinSize() | |
+{ | |
+ BSize size; | |
+ size.width = 100; | |
+ size.height = kTitleHeight + 4 * B_H_SCROLL_BAR_HEIGHT; | |
+ if (!fHorizontalScrollBar->IsHidden()) | |
+ size.height += fHorizontalScrollBar->Frame().Height() + 1; | |
+ // TODO: Take border size into account | |
+ | |
+ return BLayoutUtils::ComposeSize(ExplicitMinSize(), size); | |
+} | |
+ | |
+ | |
+BSize | |
+BColumnListView::PreferredSize() | |
+{ | |
+ BSize size = MinSize(); | |
+ size.height += ceilf(be_plain_font->Size()) * 20; | |
+ | |
+ // return MinSize().width if there are no columns. | |
+ int32 count = CountColumns(); | |
+ if (count > 0) { | |
+ BRect titleRect; | |
+ BRect outlineRect; | |
+ BRect vScrollBarRect; | |
+ BRect hScrollBarRect; | |
+ _GetChildViewRects(Bounds(), titleRect, outlineRect, vScrollBarRect, | |
+ hScrollBarRect); | |
+ // Start with the extra width for border and scrollbars etc. | |
+ size.width = titleRect.left - Bounds().left; | |
+ size.width += Bounds().right - titleRect.right; | |
+ // If we want all columns to be visible at their preferred width, | |
+ // we also need to add the extra margin width that the TitleView | |
+ // uses to compute its _VirtualWidth() for the horizontal scroll bar. | |
+ size.width += fTitleView->MarginWidth(); | |
+ for (int32 i = 0; i < count; i++) { | |
+ BColumn* column = ColumnAt(i); | |
+ if (column != NULL) | |
+ size.width += fOutlineView->GetColumnPreferredWidth(column); | |
+ } | |
+ } | |
+ | |
+ return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), size); | |
+} | |
+ | |
+ | |
+BSize | |
+BColumnListView::MaxSize() | |
+{ | |
+ BSize size(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED); | |
+ return BLayoutUtils::ComposeSize(ExplicitMaxSize(), size); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::LayoutInvalidated(bool descendants) | |
+{ | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::DoLayout() | |
+{ | |
+ if (!(Flags() & B_SUPPORTS_LAYOUT)) | |
+ return; | |
+ | |
+ BRect titleRect; | |
+ BRect outlineRect; | |
+ BRect vScrollBarRect; | |
+ BRect hScrollBarRect; | |
+ _GetChildViewRects(Bounds(), titleRect, outlineRect, vScrollBarRect, | |
+ hScrollBarRect); | |
+ | |
+ fTitleView->MoveTo(titleRect.LeftTop()); | |
+ fTitleView->ResizeTo(titleRect.Width(), titleRect.Height()); | |
+ | |
+ fOutlineView->MoveTo(outlineRect.LeftTop()); | |
+ fOutlineView->ResizeTo(outlineRect.Width(), outlineRect.Height()); | |
+ | |
+ fVerticalScrollBar->MoveTo(vScrollBarRect.LeftTop()); | |
+ fVerticalScrollBar->ResizeTo(vScrollBarRect.Width(), | |
+ vScrollBarRect.Height()); | |
+ | |
+ fHorizontalScrollBar->MoveTo(hScrollBarRect.LeftTop()); | |
+ fHorizontalScrollBar->ResizeTo(hScrollBarRect.Width(), | |
+ hScrollBarRect.Height()); | |
+ | |
+ fOutlineView->FixScrollBar(true); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::_Init() | |
+{ | |
+ SetViewColor(B_TRANSPARENT_32_BIT); | |
+ | |
+ BRect bounds(Bounds()); | |
+ if (bounds.Width() <= 0) | |
+ bounds.right = 100; | |
+ if (bounds.Height() <= 0) | |
+ bounds.bottom = 100; | |
+ | |
+ for (int i = 0; i < (int)B_COLOR_TOTAL; i++) | |
+ fColorList[i] = kColor[i]; | |
+ | |
+ BRect titleRect; | |
+ BRect outlineRect; | |
+ BRect vScrollBarRect; | |
+ BRect hScrollBarRect; | |
+ _GetChildViewRects(bounds, titleRect, outlineRect, vScrollBarRect, | |
+ hScrollBarRect); | |
+ | |
+ fOutlineView = new OutlineView(outlineRect, &fColumns, &fSortColumns, this); | |
+ AddChild(fOutlineView); | |
+ | |
+ | |
+ fTitleView = new TitleView(titleRect, fOutlineView, &fColumns, | |
+ &fSortColumns, this, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); | |
+ AddChild(fTitleView); | |
+ | |
+ fVerticalScrollBar = new BScrollBar(vScrollBarRect, "vertical_scroll_bar", | |
+ fOutlineView, 0.0, bounds.Height(), B_VERTICAL); | |
+ AddChild(fVerticalScrollBar); | |
+ | |
+ fHorizontalScrollBar = new BScrollBar(hScrollBarRect, | |
+ "horizontal_scroll_bar", fTitleView, 0.0, bounds.Width(), B_HORIZONTAL); | |
+ AddChild(fHorizontalScrollBar); | |
+ | |
+ if (!fShowingHorizontalScrollBar) | |
+ fHorizontalScrollBar->Hide(); | |
+ | |
+ fOutlineView->FixScrollBar(true); | |
+} | |
+ | |
+ | |
+void | |
+BColumnListView::_GetChildViewRects(const BRect& bounds, BRect& titleRect, | |
+ BRect& outlineRect, BRect& vScrollBarRect, BRect& hScrollBarRect) | |
+{ | |
+ titleRect = bounds; | |
+ titleRect.bottom = titleRect.top + kTitleHeight; | |
+#if !LOWER_SCROLLBAR | |
+ titleRect.right -= B_V_SCROLL_BAR_WIDTH; | |
+#endif | |
+ | |
+ outlineRect = bounds; | |
+ outlineRect.top = titleRect.bottom + 1.0; | |
+ outlineRect.right -= B_V_SCROLL_BAR_WIDTH; | |
+ if (fShowingHorizontalScrollBar) | |
+ outlineRect.bottom -= B_H_SCROLL_BAR_HEIGHT; | |
+ | |
+ vScrollBarRect = bounds; | |
+#if LOWER_SCROLLBAR | |
+ vScrollBarRect.top += kTitleHeight; | |
+#endif | |
+ | |
+ vScrollBarRect.left = vScrollBarRect.right - B_V_SCROLL_BAR_WIDTH; | |
+ if (fShowingHorizontalScrollBar) | |
+ vScrollBarRect.bottom -= B_H_SCROLL_BAR_HEIGHT; | |
+ | |
+ hScrollBarRect = bounds; | |
+ hScrollBarRect.top = hScrollBarRect.bottom - B_H_SCROLL_BAR_HEIGHT; | |
+ hScrollBarRect.right -= B_V_SCROLL_BAR_WIDTH; | |
+ | |
+ // Adjust stuff so the border will fit. | |
+ if (fBorderStyle == B_PLAIN_BORDER || fBorderStyle == B_NO_BORDER) { | |
+ titleRect.InsetBy(1, 0); | |
+ titleRect.OffsetBy(0, 1); | |
+ outlineRect.InsetBy(1, 1); | |
+ } else if (fBorderStyle == B_FANCY_BORDER) { | |
+ titleRect.InsetBy(2, 0); | |
+ titleRect.OffsetBy(0, 2); | |
+ outlineRect.InsetBy(2, 2); | |
+ | |
+ vScrollBarRect.OffsetBy(-1, 0); | |
+#if LOWER_SCROLLBAR | |
+ vScrollBarRect.top += 2; | |
+ vScrollBarRect.bottom -= 1; | |
+#else | |
+ vScrollBarRect.InsetBy(0, 1); | |
+#endif | |
+ hScrollBarRect.OffsetBy(0, -1); | |
+ hScrollBarRect.InsetBy(1, 0); | |
+ } | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+TitleView::TitleView(BRect rect, OutlineView* horizontalSlave, | |
+ BList* visibleColumns, BList* sortColumns, BColumnListView* listView, | |
+ uint32 resizingMode) | |
+ : | |
+ BView(rect, "title_view", resizingMode, B_WILL_DRAW | B_FRAME_EVENTS), | |
+ fOutlineView(horizontalSlave), | |
+ fColumns(visibleColumns), | |
+ fSortColumns(sortColumns), | |
+// fColumnsWidth(0), | |
+ fVisibleRect(rect.OffsetToCopy(0, 0)), | |
+ fCurrentState(INACTIVE), | |
+ fColumnPop(NULL), | |
+ fMasterView(listView), | |
+ fEditMode(false), | |
+ fColumnFlags(B_ALLOW_COLUMN_MOVE | B_ALLOW_COLUMN_RESIZE | |
+ | B_ALLOW_COLUMN_POPUP | B_ALLOW_COLUMN_REMOVE) | |
+{ | |
+ SetViewColor(B_TRANSPARENT_COLOR); | |
+ | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ // xxx this needs to be smart about the size of the backbuffer. | |
+ BRect doubleBufferRect(0, 0, 600, 35); | |
+ fDrawBuffer = new BBitmap(doubleBufferRect, B_RGB32, true); | |
+ fDrawBufferView = new BView(doubleBufferRect, "double_buffer_view", | |
+ B_FOLLOW_ALL_SIDES, 0); | |
+ fDrawBuffer->Lock(); | |
+ fDrawBuffer->AddChild(fDrawBufferView); | |
+ fDrawBuffer->Unlock(); | |
+#endif | |
+ | |
+ fUpSortArrow = new BBitmap(BRect(0, 0, 7, 7), B_CMAP8); | |
+ fDownSortArrow = new BBitmap(BRect(0, 0, 7, 7), B_CMAP8); | |
+ | |
+ fUpSortArrow->SetBits((const void*) kUpSortArrow8x8, 64, 0, B_CMAP8); | |
+ fDownSortArrow->SetBits((const void*) kDownSortArrow8x8, 64, 0, B_CMAP8); | |
+ | |
+ fResizeCursor = new BCursor(B_CURSOR_ID_RESIZE_EAST_WEST); | |
+ fMinResizeCursor = new BCursor(B_CURSOR_ID_RESIZE_EAST); | |
+ fMaxResizeCursor = new BCursor(B_CURSOR_ID_RESIZE_WEST); | |
+ fColumnMoveCursor = new BCursor(B_CURSOR_ID_MOVE); | |
+ | |
+ FixScrollBar(true); | |
+} | |
+ | |
+ | |
+TitleView::~TitleView() | |
+{ | |
+ delete fColumnPop; | |
+ fColumnPop = NULL; | |
+ | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ delete fDrawBuffer; | |
+#endif | |
+ delete fUpSortArrow; | |
+ delete fDownSortArrow; | |
+ | |
+ delete fResizeCursor; | |
+ delete fMaxResizeCursor; | |
+ delete fMinResizeCursor; | |
+ delete fColumnMoveCursor; | |
+} | |
+ | |
+ | |
+void | |
+TitleView::ColumnAdded(BColumn* column) | |
+{ | |
+// fColumnsWidth += column->Width(); | |
+ FixScrollBar(false); | |
+ Invalidate(); | |
+} | |
+ | |
+ | |
+void | |
+TitleView::ColumnResized(BColumn* column, float oldWidth) | |
+{ | |
+// fColumnsWidth += column->Width() - oldWidth; | |
+ FixScrollBar(false); | |
+ Invalidate(); | |
+} | |
+ | |
+ | |
+void | |
+TitleView::SetColumnVisible(BColumn* column, bool visible) | |
+{ | |
+ if (column->fVisible == visible) | |
+ return; | |
+ | |
+ // If setting it visible, do this first so we can find its position | |
+ // to invalidate. If hiding it, do it last. | |
+ if (visible) | |
+ column->fVisible = visible; | |
+ | |
+ BRect titleInvalid; | |
+ GetTitleRect(column, &titleInvalid); | |
+ | |
+ // Now really set the visibility | |
+ column->fVisible = visible; | |
+ | |
+// if (visible) | |
+// fColumnsWidth += column->Width(); | |
+// else | |
+// fColumnsWidth -= column->Width(); | |
+ | |
+ BRect outlineInvalid(fOutlineView->VisibleRect()); | |
+ outlineInvalid.left = titleInvalid.left; | |
+ titleInvalid.right = outlineInvalid.right; | |
+ | |
+ Invalidate(titleInvalid); | |
+ fOutlineView->Invalidate(outlineInvalid); | |
+} | |
+ | |
+ | |
+void | |
+TitleView::GetTitleRect(BColumn* findColumn, BRect* _rect) | |
+{ | |
+ float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth()); | |
+ int32 numColumns = fColumns->CountItems(); | |
+ for (int index = 0; index < numColumns; index++) { | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(index); | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ if (column == findColumn) { | |
+ _rect->Set(leftEdge, 0, leftEdge + column->Width(), | |
+ fVisibleRect.bottom); | |
+ return; | |
+ } | |
+ | |
+ leftEdge += column->Width() + 1; | |
+ } | |
+ | |
+ TRESPASS(); | |
+} | |
+ | |
+ | |
+int32 | |
+TitleView::FindColumn(BPoint position, float* _leftEdge) | |
+{ | |
+ float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth()); | |
+ int32 numColumns = fColumns->CountItems(); | |
+ for (int index = 0; index < numColumns; index++) { | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(index); | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ if (leftEdge > position.x) | |
+ break; | |
+ | |
+ if (position.x >= leftEdge | |
+ && position.x <= leftEdge + column->Width()) { | |
+ *_leftEdge = leftEdge; | |
+ return index; | |
+ } | |
+ | |
+ leftEdge += column->Width() + 1; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
+ | |
+void | |
+TitleView::FixScrollBar(bool scrollToFit) | |
+{ | |
+ BScrollBar* hScrollBar = ScrollBar(B_HORIZONTAL); | |
+ if (hScrollBar == NULL) | |
+ return; | |
+ | |
+ float virtualWidth = _VirtualWidth(); | |
+ | |
+ if (virtualWidth > fVisibleRect.Width()) { | |
+ hScrollBar->SetProportion(fVisibleRect.Width() / virtualWidth); | |
+ | |
+ // Perform the little trick if the user is scrolled over too far. | |
+ // See OutlineView::FixScrollBar for a more in depth explanation | |
+ float maxScrollBarValue = virtualWidth - fVisibleRect.Width(); | |
+ if (scrollToFit || hScrollBar->Value() <= maxScrollBarValue) { | |
+ hScrollBar->SetRange(0.0, maxScrollBarValue); | |
+ hScrollBar->SetSteps(50, fVisibleRect.Width()); | |
+ } | |
+ } else if (hScrollBar->Value() == 0.0) { | |
+ // disable scroll bar. | |
+ hScrollBar->SetRange(0.0, 0.0); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TitleView::DragSelectedColumn(BPoint position) | |
+{ | |
+ float invalidLeft = fSelectedColumnRect.left; | |
+ float invalidRight = fSelectedColumnRect.right; | |
+ | |
+ float leftEdge; | |
+ int32 columnIndex = FindColumn(position, &leftEdge); | |
+ fSelectedColumnRect.OffsetTo(leftEdge, 0); | |
+ | |
+ MoveColumn(fSelectedColumn, columnIndex); | |
+ | |
+ fSelectedColumn->fVisible = true; | |
+ ComputeDragBoundries(fSelectedColumn, position); | |
+ | |
+ // Redraw the new column position | |
+ GetTitleRect(fSelectedColumn, &fSelectedColumnRect); | |
+ invalidLeft = MIN(fSelectedColumnRect.left, invalidLeft); | |
+ invalidRight = MAX(fSelectedColumnRect.right, invalidRight); | |
+ | |
+ Invalidate(BRect(invalidLeft, 0, invalidRight, fVisibleRect.bottom)); | |
+ fOutlineView->Invalidate(BRect(invalidLeft, 0, invalidRight, | |
+ fOutlineView->VisibleRect().bottom)); | |
+ | |
+ DrawTitle(this, fSelectedColumnRect, fSelectedColumn, true); | |
+} | |
+ | |
+ | |
+void | |
+TitleView::MoveColumn(BColumn* column, int32 index) | |
+{ | |
+ fColumns->RemoveItem((void*) column); | |
+ | |
+ if (-1 == index) { | |
+ // Re-add the column at the end of the list. | |
+ fColumns->AddItem((void*) column); | |
+ } else { | |
+ fColumns->AddItem((void*) column, index); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TitleView::SetColumnFlags(column_flags flags) | |
+{ | |
+ fColumnFlags = flags; | |
+} | |
+ | |
+ | |
+float | |
+TitleView::MarginWidth() const | |
+{ | |
+ return MAX(kLeftMargin, fMasterView->LatchWidth()) + kRightMargin; | |
+} | |
+ | |
+ | |
+void | |
+TitleView::ResizeSelectedColumn(BPoint position, bool preferred) | |
+{ | |
+ float minWidth = fSelectedColumn->MinWidth(); | |
+ float maxWidth = fSelectedColumn->MaxWidth(); | |
+ | |
+ float oldWidth = fSelectedColumn->Width(); | |
+ float originalEdge = fSelectedColumnRect.left + oldWidth; | |
+ if (preferred) { | |
+ float width = fOutlineView->GetColumnPreferredWidth(fSelectedColumn); | |
+ fSelectedColumn->SetWidth(width); | |
+ } else if (position.x > fSelectedColumnRect.left + maxWidth) | |
+ fSelectedColumn->SetWidth(maxWidth); | |
+ else if (position.x < fSelectedColumnRect.left + minWidth) | |
+ fSelectedColumn->SetWidth(minWidth); | |
+ else | |
+ fSelectedColumn->SetWidth(position.x - fSelectedColumnRect.left - 1); | |
+ | |
+ float dX = fSelectedColumnRect.left + fSelectedColumn->Width() | |
+ - originalEdge; | |
+ if (dX != 0) { | |
+ float columnHeight = fVisibleRect.Height(); | |
+ BRect originalRect(originalEdge, 0, 1000000.0, columnHeight); | |
+ BRect movedRect(originalRect); | |
+ movedRect.OffsetBy(dX, 0); | |
+ | |
+ // Update the size of the title column | |
+ BRect sourceRect(0, 0, fSelectedColumn->Width(), columnHeight); | |
+ BRect destRect(sourceRect); | |
+ destRect.OffsetBy(fSelectedColumnRect.left, 0); | |
+ | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ fDrawBuffer->Lock(); | |
+ DrawTitle(fDrawBufferView, sourceRect, fSelectedColumn, false); | |
+ fDrawBufferView->Sync(); | |
+ fDrawBuffer->Unlock(); | |
+ | |
+ CopyBits(originalRect, movedRect); | |
+ DrawBitmap(fDrawBuffer, sourceRect, destRect); | |
+#else | |
+ CopyBits(originalRect, movedRect); | |
+ DrawTitle(this, destRect, fSelectedColumn, false); | |
+#endif | |
+ | |
+ // Update the body view | |
+ BRect slaveSize = fOutlineView->VisibleRect(); | |
+ BRect slaveSource(originalRect); | |
+ slaveSource.bottom = slaveSize.bottom; | |
+ BRect slaveDest(movedRect); | |
+ slaveDest.bottom = slaveSize.bottom; | |
+ fOutlineView->CopyBits(slaveSource, slaveDest); | |
+ fOutlineView->RedrawColumn(fSelectedColumn, fSelectedColumnRect.left, | |
+ fResizingFirstColumn); | |
+ | |
+// fColumnsWidth += dX; | |
+ | |
+ // Update the cursor | |
+ if (fSelectedColumn->Width() == minWidth) | |
+ SetViewCursor(fMinResizeCursor, true); | |
+ else if (fSelectedColumn->Width() == maxWidth) | |
+ SetViewCursor(fMaxResizeCursor, true); | |
+ else | |
+ SetViewCursor(fResizeCursor, true); | |
+ | |
+ ColumnResized(fSelectedColumn, oldWidth); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TitleView::ComputeDragBoundries(BColumn* findColumn, BPoint) | |
+{ | |
+ float previousColumnLeftEdge = -1000000.0; | |
+ float nextColumnRightEdge = 1000000.0; | |
+ | |
+ bool foundColumn = false; | |
+ float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth()); | |
+ int32 numColumns = fColumns->CountItems(); | |
+ for (int index = 0; index < numColumns; index++) { | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(index); | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ if (column == findColumn) { | |
+ foundColumn = true; | |
+ continue; | |
+ } | |
+ | |
+ if (foundColumn) { | |
+ nextColumnRightEdge = leftEdge + column->Width(); | |
+ break; | |
+ } else | |
+ previousColumnLeftEdge = leftEdge; | |
+ | |
+ leftEdge += column->Width() + 1; | |
+ } | |
+ | |
+ float rightEdge = leftEdge + findColumn->Width(); | |
+ | |
+ fLeftDragBoundry = MIN(previousColumnLeftEdge + findColumn->Width(), | |
+ leftEdge); | |
+ fRightDragBoundry = MAX(nextColumnRightEdge, rightEdge); | |
+} | |
+ | |
+ | |
+void | |
+TitleView::DrawTitle(BView* view, BRect rect, BColumn* column, bool depressed) | |
+{ | |
+ BRect drawRect; | |
+ rgb_color borderColor = mix_color( | |
+ fMasterView->Color(B_COLOR_HEADER_BACKGROUND), | |
+ make_color(0, 0, 0), 128); | |
+ rgb_color backgroundColor; | |
+ | |
+ rgb_color bevelHigh; | |
+ rgb_color bevelLow; | |
+ // Want exterior borders to overlap. | |
+ if (be_control_look == NULL) { | |
+ rect.right += 1; | |
+ drawRect = rect; | |
+ drawRect.InsetBy(2, 2); | |
+ if (depressed) { | |
+ backgroundColor = mix_color( | |
+ fMasterView->Color(B_COLOR_HEADER_BACKGROUND), | |
+ make_color(0, 0, 0), 64); | |
+ bevelHigh = mix_color(backgroundColor, make_color(0, 0, 0), 64); | |
+ bevelLow = mix_color(backgroundColor, make_color(255, 255, 255), | |
+ 128); | |
+ drawRect.left++; | |
+ drawRect.top++; | |
+ } else { | |
+ backgroundColor = fMasterView->Color(B_COLOR_HEADER_BACKGROUND); | |
+ bevelHigh = mix_color(backgroundColor, make_color(255, 255, 255), | |
+ 192); | |
+ bevelLow = mix_color(backgroundColor, make_color(0, 0, 0), 64); | |
+ drawRect.bottom--; | |
+ drawRect.right--; | |
+ } | |
+ } else { | |
+ drawRect = rect; | |
+ } | |
+ | |
+ font_height fh; | |
+ GetFontHeight(&fh); | |
+ | |
+ float baseline = floor(drawRect.top + fh.ascent | |
+ + (drawRect.Height() + 1 - (fh.ascent + fh.descent)) / 2); | |
+ | |
+ if (be_control_look != NULL) { | |
+ BRect bgRect = rect; | |
+ | |
+ rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); | |
+ view->SetHighColor(tint_color(base, B_DARKEN_2_TINT)); | |
+ view->StrokeLine(bgRect.LeftBottom(), bgRect.RightBottom()); | |
+ | |
+ bgRect.bottom--; | |
+ bgRect.right--; | |
+ | |
+ if (depressed) | |
+ base = tint_color(base, B_DARKEN_1_TINT); | |
+ | |
+ be_control_look->DrawButtonBackground(view, bgRect, rect, base, 0, | |
+ BControlLook::B_TOP_BORDER | BControlLook::B_BOTTOM_BORDER); | |
+ | |
+ view->SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), | |
+ B_DARKEN_2_TINT)); | |
+ view->StrokeLine(rect.RightTop(), rect.RightBottom()); | |
+ | |
+ } else { | |
+ | |
+ view->SetHighColor(borderColor); | |
+ view->StrokeRect(rect); | |
+ view->BeginLineArray(4); | |
+ view->AddLine(BPoint(rect.left + 1, rect.top + 1), | |
+ BPoint(rect.right - 1, rect.top + 1), bevelHigh); | |
+ view->AddLine(BPoint(rect.left + 1, rect.top + 1), | |
+ BPoint(rect.left + 1, rect.bottom - 1), bevelHigh); | |
+ view->AddLine(BPoint(rect.right - 1, rect.top + 1), | |
+ BPoint(rect.right - 1, rect.bottom - 1), bevelLow); | |
+ view->AddLine(BPoint(rect.left + 2, rect.bottom-1), | |
+ BPoint(rect.right - 1, rect.bottom - 1), bevelLow); | |
+ view->EndLineArray(); | |
+ | |
+ view->SetHighColor(backgroundColor); | |
+ view->SetLowColor(backgroundColor); | |
+ | |
+ view->FillRect(rect.InsetByCopy(2, 2)); | |
+ } | |
+ | |
+ // If no column given, nothing else to draw. | |
+ if (!column) | |
+ return; | |
+ | |
+ view->SetHighColor(fMasterView->Color(B_COLOR_HEADER_TEXT)); | |
+ | |
+ BFont font; | |
+ GetFont(&font); | |
+ view->SetFont(&font); | |
+ | |
+ int sortIndex = fSortColumns->IndexOf(column); | |
+ if (sortIndex >= 0) { | |
+ // Draw sort notation. | |
+ BPoint upperLeft(drawRect.right - kSortIndicatorWidth, baseline); | |
+ | |
+ if (fSortColumns->CountItems() > 1) { | |
+ char str[256]; | |
+ sprintf(str, "%d", sortIndex + 1); | |
+ const float w = view->StringWidth(str); | |
+ upperLeft.x -= w; | |
+ | |
+ view->SetDrawingMode(B_OP_COPY); | |
+ view->MovePenTo(BPoint(upperLeft.x + kSortIndicatorWidth, | |
+ baseline)); | |
+ view->DrawString(str); | |
+ } | |
+ | |
+ float bmh = fDownSortArrow->Bounds().Height()+1; | |
+ | |
+ view->SetDrawingMode(B_OP_OVER); | |
+ | |
+ if (column->fSortAscending) { | |
+ BPoint leftTop(upperLeft.x, drawRect.top + (drawRect.IntegerHeight() | |
+ - fDownSortArrow->Bounds().IntegerHeight()) / 2); | |
+ view->DrawBitmapAsync(fDownSortArrow, leftTop); | |
+ } else { | |
+ BPoint leftTop(upperLeft.x, drawRect.top + (drawRect.IntegerHeight() | |
+ - fUpSortArrow->Bounds().IntegerHeight()) / 2); | |
+ view->DrawBitmapAsync(fUpSortArrow, leftTop); | |
+ } | |
+ | |
+ upperLeft.y = baseline - bmh + floor((fh.ascent + fh.descent - bmh) / 2); | |
+ if (upperLeft.y < drawRect.top) | |
+ upperLeft.y = drawRect.top; | |
+ | |
+ // Adjust title stuff for sort indicator | |
+ drawRect.right = upperLeft.x - 2; | |
+ } | |
+ | |
+ if (drawRect.right > drawRect.left) { | |
+#if CONSTRAIN_CLIPPING_REGION | |
+ BRegion clipRegion(drawRect); | |
+ view->PushState(); | |
+ view->ConstrainClippingRegion(&clipRegion); | |
+#endif | |
+ view->MovePenTo(BPoint(drawRect.left + 8, baseline)); | |
+ view->SetDrawingMode(B_OP_OVER); | |
+ view->SetHighColor(fMasterView->Color(B_COLOR_HEADER_TEXT)); | |
+ column->DrawTitle(drawRect, view); | |
+ | |
+#if CONSTRAIN_CLIPPING_REGION | |
+ view->PopState(); | |
+#endif | |
+ } | |
+} | |
+ | |
+ | |
+float | |
+TitleView::_VirtualWidth() const | |
+{ | |
+ float width = MarginWidth(); | |
+ | |
+ int32 count = fColumns->CountItems(); | |
+ for (int32 i = 0; i < count; i++) { | |
+ BColumn* column = reinterpret_cast<BColumn*>(fColumns->ItemAt(i)); | |
+ width += column->Width(); | |
+ } | |
+ | |
+ return width; | |
+} | |
+ | |
+ | |
+void | |
+TitleView::Draw(BRect invalidRect) | |
+{ | |
+ float columnLeftEdge = MAX(kLeftMargin, fMasterView->LatchWidth()); | |
+ for (int32 columnIndex = 0; columnIndex < fColumns->CountItems(); | |
+ columnIndex++) { | |
+ | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(columnIndex); | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ if (columnLeftEdge > invalidRect.right) | |
+ break; | |
+ | |
+ if (columnLeftEdge + column->Width() >= invalidRect.left) { | |
+ BRect titleRect(columnLeftEdge, 0, | |
+ columnLeftEdge + column->Width(), fVisibleRect.Height()); | |
+ DrawTitle(this, titleRect, column, | |
+ (fCurrentState == DRAG_COLUMN_INSIDE_TITLE | |
+ && fSelectedColumn == column)); | |
+ } | |
+ | |
+ columnLeftEdge += column->Width() + 1; | |
+ } | |
+ | |
+ | |
+ // Bevels for right title margin | |
+ if (columnLeftEdge <= invalidRect.right) { | |
+ BRect titleRect(columnLeftEdge, 0, Bounds().right + 2, | |
+ fVisibleRect.Height()); | |
+ DrawTitle(this, titleRect, NULL, false); | |
+ } | |
+ | |
+ // Bevels for left title margin | |
+ if (invalidRect.left < MAX(kLeftMargin, fMasterView->LatchWidth())) { | |
+ BRect titleRect(0, 0, MAX(kLeftMargin, fMasterView->LatchWidth()) - 1, | |
+ fVisibleRect.Height()); | |
+ DrawTitle(this, titleRect, NULL, false); | |
+ } | |
+ | |
+#if DRAG_TITLE_OUTLINE | |
+ // (Internal) Column Drag Indicator | |
+ if (fCurrentState == DRAG_COLUMN_INSIDE_TITLE) { | |
+ BRect dragRect(fSelectedColumnRect); | |
+ dragRect.OffsetTo(fCurrentDragPosition.x - fClickPoint.x, 0); | |
+ if (dragRect.Intersects(invalidRect)) { | |
+ SetHighColor(0, 0, 255); | |
+ StrokeRect(dragRect); | |
+ } | |
+ } | |
+#endif | |
+} | |
+ | |
+ | |
+void | |
+TitleView::ScrollTo(BPoint position) | |
+{ | |
+ fOutlineView->ScrollBy(position.x - fVisibleRect.left, 0); | |
+ fVisibleRect.OffsetTo(position.x, position.y); | |
+ | |
+ // Perform the little trick if the user is scrolled over too far. | |
+ // See OutlineView::ScrollTo for a more in depth explanation | |
+ float maxScrollBarValue = _VirtualWidth() - fVisibleRect.Width(); | |
+ BScrollBar* hScrollBar = ScrollBar(B_HORIZONTAL); | |
+ float min, max; | |
+ hScrollBar->GetRange(&min, &max); | |
+ if (max != maxScrollBarValue && position.x > maxScrollBarValue) | |
+ FixScrollBar(true); | |
+ | |
+ _inherited::ScrollTo(position); | |
+} | |
+ | |
+ | |
+void | |
+TitleView::MessageReceived(BMessage* message) | |
+{ | |
+ if (message->what == kToggleColumn) { | |
+ int32 num; | |
+ if (message->FindInt32("be:field_num", &num) == B_OK) { | |
+ for (int index = 0; index < fColumns->CountItems(); index++) { | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(index); | |
+ if (!column) | |
+ continue; | |
+ if (column->LogicalFieldNum() == num) | |
+ column->SetVisible(!column->IsVisible()); | |
+ } | |
+ } | |
+ return; | |
+ } else { | |
+ BView::MessageReceived(message); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TitleView::MouseDown(BPoint position) | |
+{ | |
+ if(fEditMode) | |
+ return; | |
+ | |
+ int32 buttons = 1; | |
+ Window()->CurrentMessage()->FindInt32("buttons", &buttons); | |
+ if (buttons == B_SECONDARY_MOUSE_BUTTON | |
+ && (fColumnFlags & B_ALLOW_COLUMN_POPUP)) { | |
+ // Right mouse button -- bring up menu to show/hide columns. | |
+ if (!fColumnPop) fColumnPop = new BPopUpMenu("Columns", false, false); | |
+ fColumnPop->RemoveItems(0, fColumnPop->CountItems(), true); | |
+ BMessenger me(this); | |
+ for (int index = 0; index < fColumns->CountItems(); index++) { | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(index); | |
+ if (!column) continue; | |
+ BString name; | |
+ column->GetColumnName(&name); | |
+ BMessage* msg = new BMessage(kToggleColumn); | |
+ msg->AddInt32("be:field_num", column->LogicalFieldNum()); | |
+ BMenuItem* it = new BMenuItem(name.String(), msg); | |
+ it->SetMarked(column->IsVisible()); | |
+ it->SetTarget(me); | |
+ fColumnPop->AddItem(it); | |
+ } | |
+ BPoint screenPosition = ConvertToScreen(position); | |
+ BRect sticky(screenPosition, screenPosition); | |
+ sticky.InsetBy(-5, -5); | |
+ fColumnPop->Go(ConvertToScreen(position), true, false, sticky, true); | |
+ return; | |
+ } | |
+ | |
+ fResizingFirstColumn = true; | |
+ float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth()); | |
+ for (int index = 0; index < fColumns->CountItems(); index++) { | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(index); | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ if (leftEdge > position.x + kColumnResizeAreaWidth / 2) | |
+ break; | |
+ | |
+ // Check for resizing a column | |
+ float rightEdge = leftEdge + column->Width(); | |
+ | |
+ if (column->ShowHeading()) { | |
+ if (position.x > rightEdge - kColumnResizeAreaWidth / 2 | |
+ && position.x < rightEdge + kColumnResizeAreaWidth / 2 | |
+ && column->MaxWidth() > column->MinWidth() | |
+ && (fColumnFlags & B_ALLOW_COLUMN_RESIZE)) { | |
+ | |
+ int32 clicks = 0; | |
+ Window()->CurrentMessage()->FindInt32("clicks", &clicks); | |
+ if (clicks == 2) { | |
+ ResizeSelectedColumn(position, true); | |
+ fCurrentState = INACTIVE; | |
+ break; | |
+ } | |
+ fCurrentState = RESIZING_COLUMN; | |
+ fSelectedColumn = column; | |
+ fSelectedColumnRect.Set(leftEdge, 0, rightEdge, | |
+ fVisibleRect.Height()); | |
+ fClickPoint = BPoint(position.x - rightEdge - 1, | |
+ position.y - fSelectedColumnRect.top); | |
+ SetMouseEventMask(B_POINTER_EVENTS, | |
+ B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); | |
+ break; | |
+ } | |
+ | |
+ fResizingFirstColumn = false; | |
+ | |
+ // Check for clicking on a column. | |
+ if (position.x > leftEdge && position.x < rightEdge) { | |
+ fCurrentState = PRESSING_COLUMN; | |
+ fSelectedColumn = column; | |
+ fSelectedColumnRect.Set(leftEdge, 0, rightEdge, | |
+ fVisibleRect.Height()); | |
+ DrawTitle(this, fSelectedColumnRect, fSelectedColumn, true); | |
+ fClickPoint = BPoint(position.x - fSelectedColumnRect.left, | |
+ position.y - fSelectedColumnRect.top); | |
+ SetMouseEventMask(B_POINTER_EVENTS, | |
+ B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY); | |
+ break; | |
+ } | |
+ } | |
+ leftEdge = rightEdge + 1; | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TitleView::MouseMoved(BPoint position, uint32 transit, | |
+ const BMessage* dragMessage) | |
+{ | |
+ if (fEditMode) | |
+ return; | |
+ | |
+ // Handle column manipulation | |
+ switch (fCurrentState) { | |
+ case RESIZING_COLUMN: | |
+ ResizeSelectedColumn(position - BPoint(fClickPoint.x, 0)); | |
+ break; | |
+ | |
+ case PRESSING_COLUMN: { | |
+ if (abs((int32)(position.x - (fClickPoint.x | |
+ + fSelectedColumnRect.left))) > kColumnResizeAreaWidth | |
+ || abs((int32)(position.y - (fClickPoint.y | |
+ + fSelectedColumnRect.top))) > kColumnResizeAreaWidth) { | |
+ // User has moved the mouse more than the tolerable amount, | |
+ // initiate a drag. | |
+ if (transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW) { | |
+ if(fColumnFlags & B_ALLOW_COLUMN_MOVE) { | |
+ fCurrentState = DRAG_COLUMN_INSIDE_TITLE; | |
+ ComputeDragBoundries(fSelectedColumn, position); | |
+ SetViewCursor(fColumnMoveCursor, true); | |
+#if DRAG_TITLE_OUTLINE | |
+ BRect invalidRect(fSelectedColumnRect); | |
+ invalidRect.OffsetTo(position.x - fClickPoint.x, 0); | |
+ fCurrentDragPosition = position; | |
+ Invalidate(invalidRect); | |
+#endif | |
+ } | |
+ } else { | |
+ if(fColumnFlags & B_ALLOW_COLUMN_REMOVE) { | |
+ // Dragged outside view | |
+ fCurrentState = DRAG_COLUMN_OUTSIDE_TITLE; | |
+ fSelectedColumn->SetVisible(false); | |
+ BRect dragRect(fSelectedColumnRect); | |
+ | |
+ // There is a race condition where the mouse may have | |
+ // moved by the time we get to handle this message. | |
+ // If the user drags a column very quickly, this | |
+ // results in the annoying bug where the cursor is | |
+ // outside of the rectangle that is being dragged | |
+ // around. Call GetMouse with the checkQueue flag set | |
+ // to false so we can get the most recent position of | |
+ // the mouse. This minimizes this problem (although | |
+ // it is currently not possible to completely eliminate | |
+ // it). | |
+ uint32 buttons; | |
+ GetMouse(&position, &buttons, false); | |
+ dragRect.OffsetTo(position.x - fClickPoint.x, | |
+ position.y - dragRect.Height() / 2); | |
+ BeginRectTracking(dragRect, B_TRACK_WHOLE_RECT); | |
+ } | |
+ } | |
+ } | |
+ | |
+ break; | |
+ } | |
+ | |
+ case DRAG_COLUMN_INSIDE_TITLE: { | |
+ if (transit == B_EXITED_VIEW | |
+ && (fColumnFlags & B_ALLOW_COLUMN_REMOVE)) { | |
+ // Dragged outside view | |
+ fCurrentState = DRAG_COLUMN_OUTSIDE_TITLE; | |
+ fSelectedColumn->SetVisible(false); | |
+ BRect dragRect(fSelectedColumnRect); | |
+ | |
+ // See explanation above. | |
+ uint32 buttons; | |
+ GetMouse(&position, &buttons, false); | |
+ | |
+ dragRect.OffsetTo(position.x - fClickPoint.x, | |
+ position.y - fClickPoint.y); | |
+ BeginRectTracking(dragRect, B_TRACK_WHOLE_RECT); | |
+ } else if (position.x < fLeftDragBoundry | |
+ || position.x > fRightDragBoundry) { | |
+ DragSelectedColumn(position - BPoint(fClickPoint.x, 0)); | |
+ } | |
+ | |
+#if DRAG_TITLE_OUTLINE | |
+ // Set up the invalid rect to include the rect for the previous | |
+ // position of the drag rect, as well as the new one. | |
+ BRect invalidRect(fSelectedColumnRect); | |
+ invalidRect.OffsetTo(fCurrentDragPosition.x - fClickPoint.x, 0); | |
+ if (position.x < fCurrentDragPosition.x) | |
+ invalidRect.left -= fCurrentDragPosition.x - position.x; | |
+ else | |
+ invalidRect.right += position.x - fCurrentDragPosition.x; | |
+ | |
+ fCurrentDragPosition = position; | |
+ Invalidate(invalidRect); | |
+#endif | |
+ break; | |
+ } | |
+ | |
+ case DRAG_COLUMN_OUTSIDE_TITLE: | |
+ if (transit == B_ENTERED_VIEW) { | |
+ // Drag back into view | |
+ EndRectTracking(); | |
+ fCurrentState = DRAG_COLUMN_INSIDE_TITLE; | |
+ fSelectedColumn->SetVisible(true); | |
+ DragSelectedColumn(position - BPoint(fClickPoint.x, 0)); | |
+ } | |
+ | |
+ break; | |
+ | |
+ case INACTIVE: | |
+ // Check for cursor changes if we are over the resize area for | |
+ // a column. | |
+ BColumn* resizeColumn = 0; | |
+ float leftEdge = MAX(kLeftMargin, fMasterView->LatchWidth()); | |
+ for (int index = 0; index < fColumns->CountItems(); index++) { | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(index); | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ if (leftEdge > position.x + kColumnResizeAreaWidth / 2) | |
+ break; | |
+ | |
+ float rightEdge = leftEdge + column->Width(); | |
+ if (position.x > rightEdge - kColumnResizeAreaWidth / 2 | |
+ && position.x < rightEdge + kColumnResizeAreaWidth / 2 | |
+ && column->MaxWidth() > column->MinWidth()) { | |
+ resizeColumn = column; | |
+ break; | |
+ } | |
+ | |
+ leftEdge = rightEdge + 1; | |
+ } | |
+ | |
+ // Update the cursor | |
+ if (resizeColumn) { | |
+ if (resizeColumn->Width() == resizeColumn->MinWidth()) | |
+ SetViewCursor(fMinResizeCursor, true); | |
+ else if (resizeColumn->Width() == resizeColumn->MaxWidth()) | |
+ SetViewCursor(fMaxResizeCursor, true); | |
+ else | |
+ SetViewCursor(fResizeCursor, true); | |
+ } else | |
+ SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true); | |
+ break; | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TitleView::MouseUp(BPoint position) | |
+{ | |
+ if (fEditMode) | |
+ return; | |
+ | |
+ switch (fCurrentState) { | |
+ case RESIZING_COLUMN: | |
+ ResizeSelectedColumn(position - BPoint(fClickPoint.x, 0)); | |
+ fCurrentState = INACTIVE; | |
+ FixScrollBar(false); | |
+ break; | |
+ | |
+ case PRESSING_COLUMN: { | |
+ if (fMasterView->SortingEnabled()) { | |
+ if (fSortColumns->HasItem(fSelectedColumn)) { | |
+ if ((modifiers() & B_CONTROL_KEY) == 0 | |
+ && fSortColumns->CountItems() > 1) { | |
+ fSortColumns->MakeEmpty(); | |
+ fSortColumns->AddItem(fSelectedColumn); | |
+ } | |
+ | |
+ fSelectedColumn->fSortAscending | |
+ = !fSelectedColumn->fSortAscending; | |
+ } else { | |
+ if ((modifiers() & B_CONTROL_KEY) == 0) | |
+ fSortColumns->MakeEmpty(); | |
+ | |
+ fSortColumns->AddItem(fSelectedColumn); | |
+ fSelectedColumn->fSortAscending = true; | |
+ } | |
+ | |
+ fOutlineView->StartSorting(); | |
+ } | |
+ | |
+ fCurrentState = INACTIVE; | |
+ Invalidate(); | |
+ break; | |
+ } | |
+ | |
+ case DRAG_COLUMN_INSIDE_TITLE: | |
+ fCurrentState = INACTIVE; | |
+ | |
+#if DRAG_TITLE_OUTLINE | |
+ Invalidate(); // xxx Can make this smaller | |
+#else | |
+ Invalidate(fSelectedColumnRect); | |
+#endif | |
+ SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true); | |
+ break; | |
+ | |
+ case DRAG_COLUMN_OUTSIDE_TITLE: | |
+ fCurrentState = INACTIVE; | |
+ EndRectTracking(); | |
+ SetViewCursor(B_CURSOR_SYSTEM_DEFAULT, true); | |
+ break; | |
+ | |
+ default: | |
+ ; | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+TitleView::FrameResized(float width, float height) | |
+{ | |
+ fVisibleRect.right = fVisibleRect.left + width; | |
+ fVisibleRect.bottom = fVisibleRect.top + height; | |
+ FixScrollBar(true); | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+OutlineView::OutlineView(BRect rect, BList* visibleColumns, BList* sortColumns, | |
+ BColumnListView* listView) | |
+ : | |
+ BView(rect, "outline_view", B_FOLLOW_ALL_SIDES, | |
+ B_WILL_DRAW | B_FRAME_EVENTS), | |
+ fColumns(visibleColumns), | |
+ fSortColumns(sortColumns), | |
+ fItemsHeight(0.0), | |
+ fVisibleRect(rect.OffsetToCopy(0, 0)), | |
+ fFocusRow(0), | |
+ fRollOverRow(0), | |
+ fLastSelectedItem(0), | |
+ fFirstSelectedItem(0), | |
+ fSortThread(B_BAD_THREAD_ID), | |
+ fCurrentState(INACTIVE), | |
+ fMasterView(listView), | |
+ fSelectionMode(B_MULTIPLE_SELECTION_LIST), | |
+ fTrackMouse(false), | |
+ fCurrentField(0), | |
+ fCurrentRow(0), | |
+ fCurrentColumn(0), | |
+ fMouseDown(false), | |
+ fCurrentCode(B_OUTSIDE_VIEW), | |
+ fEditMode(false), | |
+ fDragging(false), | |
+ fClickCount(0), | |
+ fDropHighlightY(-1) | |
+{ | |
+ SetViewColor(B_TRANSPARENT_COLOR); | |
+ | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ // TODO: This needs to be smart about the size of the buffer. | |
+ // Also, the buffer can be shared with the title's buffer. | |
+ BRect doubleBufferRect(0, 0, 600, 35); | |
+ fDrawBuffer = new BBitmap(doubleBufferRect, B_RGB32, true); | |
+ fDrawBufferView = new BView(doubleBufferRect, "double_buffer_view", | |
+ B_FOLLOW_ALL_SIDES, 0); | |
+ fDrawBuffer->Lock(); | |
+ fDrawBuffer->AddChild(fDrawBufferView); | |
+ fDrawBuffer->Unlock(); | |
+#endif | |
+ | |
+ FixScrollBar(true); | |
+ fSelectionListDummyHead.fNextSelected = &fSelectionListDummyHead; | |
+ fSelectionListDummyHead.fPrevSelected = &fSelectionListDummyHead; | |
+} | |
+ | |
+ | |
+OutlineView::~OutlineView() | |
+{ | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ delete fDrawBuffer; | |
+#endif | |
+ | |
+ Clear(); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::Clear() | |
+{ | |
+ DeselectAll(); | |
+ // Make sure selection list doesn't point to deleted rows! | |
+ RecursiveDeleteRows(&fRows, false); | |
+ Invalidate(); | |
+ fItemsHeight = 0.0; | |
+ FixScrollBar(true); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::SetSelectionMode(list_view_type mode) | |
+{ | |
+ DeselectAll(); | |
+ fSelectionMode = mode; | |
+} | |
+ | |
+ | |
+list_view_type | |
+OutlineView::SelectionMode() const | |
+{ | |
+ return fSelectionMode; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::Deselect(BRow* row) | |
+{ | |
+ if (row == NULL) | |
+ return; | |
+ | |
+ if (row->fNextSelected != 0) { | |
+ row->fNextSelected->fPrevSelected = row->fPrevSelected; | |
+ row->fPrevSelected->fNextSelected = row->fNextSelected; | |
+ row->fNextSelected = 0; | |
+ row->fPrevSelected = 0; | |
+ Invalidate(); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::AddToSelection(BRow* row) | |
+{ | |
+ if (row == NULL) | |
+ return; | |
+ | |
+ if (row->fNextSelected == 0) { | |
+ if (fSelectionMode == B_SINGLE_SELECTION_LIST) | |
+ DeselectAll(); | |
+ | |
+ row->fNextSelected = fSelectionListDummyHead.fNextSelected; | |
+ row->fPrevSelected = &fSelectionListDummyHead; | |
+ row->fNextSelected->fPrevSelected = row; | |
+ row->fPrevSelected->fNextSelected = row; | |
+ | |
+ BRect invalidRect; | |
+ if (FindVisibleRect(row, &invalidRect)) | |
+ Invalidate(invalidRect); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::RecursiveDeleteRows(BRowContainer* list, bool isOwner) | |
+{ | |
+ if (list == NULL) | |
+ return; | |
+ | |
+ while (true) { | |
+ BRow* row = list->RemoveItemAt(0L); | |
+ if (row == 0) | |
+ break; | |
+ | |
+ if (row->fChildList) | |
+ RecursiveDeleteRows(row->fChildList, true); | |
+ | |
+ delete row; | |
+ } | |
+ | |
+ if (isOwner) | |
+ delete list; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::RedrawColumn(BColumn* column, float leftEdge, bool isFirstColumn) | |
+{ | |
+ // TODO: Remove code duplication (private function which takes a view | |
+ // pointer, pass "this" in non-double buffered mode)! | |
+ // Watch out for sourceRect versus destRect though! | |
+ if (!column) | |
+ return; | |
+ | |
+ font_height fh; | |
+ GetFontHeight(&fh); | |
+ float line = 0.0; | |
+ bool tintedLine = true; | |
+ for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow(); | |
+ line += iterator.CurrentRow()->Height() + 1, iterator.GoToNext()) { | |
+ | |
+ BRow* row = iterator.CurrentRow(); | |
+ float rowHeight = row->Height(); | |
+ if (line > fVisibleRect.bottom) | |
+ break; | |
+ tintedLine = !tintedLine; | |
+ | |
+ if (line + rowHeight >= fVisibleRect.top) { | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ BRect sourceRect(0, 0, column->Width(), rowHeight); | |
+#endif | |
+ BRect destRect(leftEdge, line, leftEdge + column->Width(), | |
+ line + rowHeight); | |
+ | |
+ rgb_color highColor; | |
+ rgb_color lowColor; | |
+ if (row->fNextSelected != 0) { | |
+ if (fEditMode) { | |
+ highColor = fMasterView->Color(B_COLOR_EDIT_BACKGROUND); | |
+ lowColor = fMasterView->Color(B_COLOR_EDIT_BACKGROUND); | |
+ } else { | |
+ highColor = fMasterView->Color(B_COLOR_SELECTION); | |
+ lowColor = fMasterView->Color(B_COLOR_SELECTION); | |
+ } | |
+ } else { | |
+ highColor = fMasterView->Color(B_COLOR_BACKGROUND); | |
+ lowColor = fMasterView->Color(B_COLOR_BACKGROUND); | |
+ } | |
+ if (tintedLine) | |
+ lowColor = tint_color(lowColor, kTintedLineTint); | |
+ | |
+ | |
+#if DOUBLE_BUFFERED_COLUMN_RESIZE | |
+ fDrawBuffer->Lock(); | |
+ | |
+ fDrawBufferView->SetHighColor(highColor); | |
+ fDrawBufferView->SetLowColor(lowColor); | |
+ | |
+ BFont font; | |
+ GetFont(&font); | |
+ fDrawBufferView->SetFont(&font); | |
+ fDrawBufferView->FillRect(sourceRect, B_SOLID_LOW); | |
+ | |
+ if (isFirstColumn) { | |
+ // If this is the first column, double buffer drawing the latch | |
+ // too. | |
+ destRect.left += iterator.CurrentLevel() * kOutlineLevelIndent | |
+ - fMasterView->LatchWidth(); | |
+ sourceRect.left += iterator.CurrentLevel() * kOutlineLevelIndent | |
+ - fMasterView->LatchWidth(); | |
+ | |
+ LatchType pos = B_NO_LATCH; | |
+ if (row->HasLatch()) | |
+ pos = row->fIsExpanded ? B_OPEN_LATCH : B_CLOSED_LATCH; | |
+ | |
+ BRect latchRect(sourceRect); | |
+ latchRect.right = latchRect.left + fMasterView->LatchWidth(); | |
+ fMasterView->DrawLatch(fDrawBufferView, latchRect, pos, row); | |
+ } | |
+ | |
+ BField* field = row->GetField(column->fFieldID); | |
+ if (field) { | |
+ BRect fieldRect(sourceRect); | |
+ if (isFirstColumn) | |
+ fieldRect.left += fMasterView->LatchWidth(); | |
+ | |
+ #if CONSTRAIN_CLIPPING_REGION | |
+ BRegion clipRegion(fieldRect); | |
+ fDrawBufferView->PushState(); | |
+ fDrawBufferView->ConstrainClippingRegion(&clipRegion); | |
+ #endif | |
+ fDrawBufferView->SetHighColor(fMasterView->Color( | |
+ row->fNextSelected ? B_COLOR_SELECTION_TEXT | |
+ : B_COLOR_TEXT)); | |
+ float baseline = floor(fieldRect.top + fh.ascent | |
+ + (fieldRect.Height() + 1 - (fh.ascent+fh.descent)) / 2); | |
+ fDrawBufferView->MovePenTo(fieldRect.left + 8, baseline); | |
+ column->DrawField(field, fieldRect, fDrawBufferView); | |
+ #if CONSTRAIN_CLIPPING_REGION | |
+ fDrawBufferView->PopState(); | |
+ #endif | |
+ } | |
+ | |
+ if (fFocusRow == row && !fEditMode && fMasterView->IsFocus() | |
+ && Window()->IsActive()) { | |
+ fDrawBufferView->SetHighColor(fMasterView->Color( | |
+ B_COLOR_ROW_DIVIDER)); | |
+ fDrawBufferView->StrokeRect(BRect(-1, sourceRect.top, | |
+ 10000.0, sourceRect.bottom)); | |
+ } | |
+ | |
+ fDrawBufferView->Sync(); | |
+ fDrawBuffer->Unlock(); | |
+ SetDrawingMode(B_OP_COPY); | |
+ DrawBitmap(fDrawBuffer, sourceRect, destRect); | |
+ | |
+#else | |
+ | |
+ SetHighColor(highColor); | |
+ SetLowColor(lowColor); | |
+ FillRect(destRect, B_SOLID_LOW); | |
+ | |
+ BField* field = row->GetField(column->fFieldID); | |
+ if (field) { | |
+ #if CONSTRAIN_CLIPPING_REGION | |
+ BRegion clipRegion(destRect); | |
+ PushState(); | |
+ ConstrainClippingRegion(&clipRegion); | |
+ #endif | |
+ SetHighColor(fMasterView->Color(row->fNextSelected | |
+ ? B_COLOR_SELECTION_TEXT : B_COLOR_TEXT)); | |
+ float baseline = floor(destRect.top + fh.ascent | |
+ + (destRect.Height() + 1 - (fh.ascent + fh.descent)) / 2); | |
+ MovePenTo(destRect.left + 8, baseline); | |
+ column->DrawField(field, destRect, this); | |
+ #if CONSTRAIN_CLIPPING_REGION | |
+ PopState(); | |
+ #endif | |
+ } | |
+ | |
+ if (fFocusRow == row && !fEditMode && fMasterView->IsFocus() | |
+ && Window()->IsActive()) { | |
+ SetHighColor(fMasterView->Color(B_COLOR_ROW_DIVIDER)); | |
+ StrokeRect(BRect(0, destRect.top, 10000.0, destRect.bottom)); | |
+ } | |
+#endif | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::Draw(BRect invalidBounds) | |
+{ | |
+#if SMART_REDRAW | |
+ BRegion invalidRegion; | |
+ GetClippingRegion(&invalidRegion); | |
+#endif | |
+ | |
+ font_height fh; | |
+ GetFontHeight(&fh); | |
+ | |
+ float line = 0.0; | |
+ bool tintedLine = true; | |
+ int32 numColumns = fColumns->CountItems(); | |
+ for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow(); | |
+ iterator.GoToNext()) { | |
+ BRow* row = iterator.CurrentRow(); | |
+ if (line > invalidBounds.bottom) | |
+ break; | |
+ | |
+ tintedLine = !tintedLine; | |
+ float rowHeight = row->Height(); | |
+ | |
+ if (line >= invalidBounds.top - rowHeight) { | |
+ bool isFirstColumn = true; | |
+ float fieldLeftEdge = MAX(kLeftMargin, fMasterView->LatchWidth()); | |
+ | |
+ // setup background color | |
+ rgb_color lowColor; | |
+ if (row->fNextSelected != 0) { | |
+ if (Window()->IsActive()) { | |
+ if (fEditMode) | |
+ lowColor = fMasterView->Color(B_COLOR_EDIT_BACKGROUND); | |
+ else | |
+ lowColor = fMasterView->Color(B_COLOR_SELECTION); | |
+ } | |
+ else | |
+ lowColor = fMasterView->Color(B_COLOR_NON_FOCUS_SELECTION); | |
+ } else | |
+ lowColor = fMasterView->Color(B_COLOR_BACKGROUND); | |
+ if (tintedLine) | |
+ lowColor = tint_color(lowColor, kTintedLineTint); | |
+ | |
+ for (int columnIndex = 0; columnIndex < numColumns; columnIndex++) { | |
+ BColumn* column = (BColumn*) fColumns->ItemAt(columnIndex); | |
+ if (!column->IsVisible()) | |
+ continue; | |
+ | |
+ if (!isFirstColumn && fieldLeftEdge > invalidBounds.right) | |
+ break; | |
+ | |
+ if (fieldLeftEdge + column->Width() >= invalidBounds.left) { | |
+ BRect fullRect(fieldLeftEdge, line, | |
+ fieldLeftEdge + column->Width(), line + rowHeight); | |
+ | |
+ bool clippedFirstColumn = false; | |
+ // This happens when a column is indented past the | |
+ // beginning of the next column. | |
+ | |
+ SetHighColor(lowColor); | |
+ | |
+ BRect destRect(fullRect); | |
+ if (isFirstColumn) { | |
+ fullRect.left -= fMasterView->LatchWidth(); | |
+ destRect.left += iterator.CurrentLevel() | |
+ * kOutlineLevelIndent; | |
+ if (destRect.left >= destRect.right) { | |
+ // clipped | |
+ FillRect(BRect(0, line, fieldLeftEdge | |
+ + column->Width(), line + rowHeight)); | |
+ clippedFirstColumn = true; | |
+ } | |
+ | |
+ FillRect(BRect(0, line, MAX(kLeftMargin, | |
+ fMasterView->LatchWidth()), line + row->Height())); | |
+ } | |
+ | |
+ | |
+#if SMART_REDRAW | |
+ if (!clippedFirstColumn | |
+ && invalidRegion.Intersects(fullRect)) { | |
+#else | |
+ if (!clippedFirstColumn) { | |
+#endif | |
+ FillRect(fullRect); // Using color set above | |
+ | |
+ // Draw the latch widget if it has one. | |
+ if (isFirstColumn) { | |
+ if (row == fTargetRow | |
+ && fCurrentState == LATCH_CLICKED) { | |
+ // Note that this only occurs if the user is | |
+ // holding down a latch while items are added | |
+ // in the background. | |
+ BPoint pos; | |
+ uint32 buttons; | |
+ GetMouse(&pos, &buttons); | |
+ if (fLatchRect.Contains(pos)) { | |
+ fMasterView->DrawLatch(this, fLatchRect, | |
+ B_PRESSED_LATCH, fTargetRow); | |
+ } else { | |
+ fMasterView->DrawLatch(this, fLatchRect, | |
+ row->fIsExpanded ? B_OPEN_LATCH | |
+ : B_CLOSED_LATCH, fTargetRow); | |
+ } | |
+ } else { | |
+ LatchType pos = B_NO_LATCH; | |
+ if (row->HasLatch()) | |
+ pos = row->fIsExpanded ? B_OPEN_LATCH | |
+ : B_CLOSED_LATCH; | |
+ | |
+ fMasterView->DrawLatch(this, | |
+ BRect(destRect.left | |
+ - fMasterView->LatchWidth(), | |
+ destRect.top, destRect.left, | |
+ destRect.bottom), pos, row); | |
+ } | |
+ } | |
+ | |
+ SetHighColor(fMasterView->HighColor()); | |
+ // The master view just holds the high color for us. | |
+ SetLowColor(lowColor); | |
+ | |
+ BField* field = row->GetField(column->fFieldID); | |
+ if (field) { | |
+#if CONSTRAIN_CLIPPING_REGION | |
+ BRegion clipRegion(destRect); | |
+ PushState(); | |
+ ConstrainClippingRegion(&clipRegion); | |
+#endif | |
+ SetHighColor(fMasterView->Color( | |
+ row->fNextSelected ? B_COLOR_SELECTION_TEXT | |
+ : B_COLOR_TEXT)); | |
+ float baseline = floor(destRect.top + fh.ascent | |
+ + (destRect.Height() + 1 | |
+ - (fh.ascent+fh.descent)) / 2); | |
+ MovePenTo(destRect.left + 8, baseline); | |
+ column->DrawField(field, destRect, this); | |
+#if CONSTRAIN_CLIPPING_REGION | |
+ PopState(); | |
+#endif | |
+ } | |
+ } | |
+ } | |
+ | |
+ isFirstColumn = false; | |
+ fieldLeftEdge += column->Width() + 1; | |
+ } | |
+ | |
+ if (fieldLeftEdge <= invalidBounds.right) { | |
+ SetHighColor(lowColor); | |
+ FillRect(BRect(fieldLeftEdge, line, invalidBounds.right, | |
+ line + rowHeight)); | |
+ } | |
+ } | |
+ | |
+ // indicate the keyboard focus row | |
+ if (fFocusRow == row && !fEditMode && fMasterView->IsFocus() | |
+ && Window()->IsActive()) { | |
+ SetHighColor(fMasterView->Color(B_COLOR_ROW_DIVIDER)); | |
+ StrokeRect(BRect(0, line, 10000.0, line + rowHeight)); | |
+ } | |
+ | |
+ line += rowHeight + 1; | |
+ } | |
+ | |
+ if (line <= invalidBounds.bottom) { | |
+ // fill background below last item | |
+ SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND)); | |
+ FillRect(BRect(invalidBounds.left, line, invalidBounds.right, | |
+ invalidBounds.bottom)); | |
+ } | |
+ | |
+ // Draw the drop target line | |
+ if (fDropHighlightY != -1) { | |
+ InvertRect(BRect(0, fDropHighlightY - kDropHighlightLineHeight / 2, | |
+ 1000000, fDropHighlightY + kDropHighlightLineHeight / 2)); | |
+ } | |
+} | |
+ | |
+ | |
+BRow* | |
+OutlineView::FindRow(float ypos, int32* _rowIndent, float* _top) | |
+{ | |
+ if (_rowIndent && _top) { | |
+ float line = 0.0; | |
+ for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow(); | |
+ iterator.GoToNext()) { | |
+ | |
+ BRow* row = iterator.CurrentRow(); | |
+ if (line > ypos) | |
+ break; | |
+ | |
+ float rowHeight = row->Height(); | |
+ if (ypos <= line + rowHeight) { | |
+ *_top = line; | |
+ *_rowIndent = iterator.CurrentLevel(); | |
+ return row; | |
+ } | |
+ | |
+ line += rowHeight + 1; | |
+ } | |
+ } | |
+ return NULL; | |
+} | |
+ | |
+void OutlineView::SetMouseTrackingEnabled(bool enabled) | |
+{ | |
+ fTrackMouse = enabled; | |
+ if (!enabled && fDropHighlightY != -1) { | |
+ // Erase the old target line | |
+ InvertRect(BRect(0, fDropHighlightY - kDropHighlightLineHeight / 2, | |
+ 1000000, fDropHighlightY + kDropHighlightLineHeight / 2)); | |
+ fDropHighlightY = -1; | |
+ } | |
+} | |
+ | |
+ | |
+// | |
+// Note that this interaction is not totally safe. If items are added to | |
+// the list in the background, the widget rect will be incorrect, possibly | |
+// resulting in drawing glitches. The code that adds items needs to be a little smarter | |
+// about invalidating state. | |
+// | |
+void | |
+OutlineView::MouseDown(BPoint position) | |
+{ | |
+ if (!fEditMode) | |
+ fMasterView->MakeFocus(true); | |
+ | |
+ // Check to see if the user is clicking on a widget to open a section | |
+ // of the list. | |
+ bool reset_click_count = false; | |
+ int32 indent; | |
+ float rowTop; | |
+ BRow* row = FindRow(position.y, &indent, &rowTop); | |
+ if (row != NULL) { | |
+ | |
+ // Update fCurrentField | |
+ bool handle_field = false; | |
+ BField* new_field = 0; | |
+ BRow* new_row = 0; | |
+ BColumn* new_column = 0; | |
+ BRect new_rect; | |
+ | |
+ if (position.y >= 0) { | |
+ if (position.x >= 0) { | |
+ float x = 0; | |
+ for (int32 c = 0; c < fMasterView->CountColumns(); c++) { | |
+ new_column = fMasterView->ColumnAt(c); | |
+ if (!new_column->IsVisible()) | |
+ continue; | |
+ if ((MAX(kLeftMargin, fMasterView->LatchWidth()) + x) | |
+ + new_column->Width() >= position.x) { | |
+ if (new_column->WantsEvents()) { | |
+ new_field = row->GetField(c); | |
+ new_row = row; | |
+ FindRect(new_row,&new_rect); | |
+ new_rect.left = MAX(kLeftMargin, | |
+ fMasterView->LatchWidth()) + x; | |
+ new_rect.right = new_rect.left | |
+ + new_column->Width() - 1; | |
+ handle_field = true; | |
+ } | |
+ break; | |
+ } | |
+ x += new_column->Width(); | |
+ } | |
+ } | |
+ } | |
+ | |
+ // Handle mouse down | |
+ if (handle_field) { | |
+ fMouseDown = true; | |
+ fFieldRect = new_rect; | |
+ fCurrentColumn = new_column; | |
+ fCurrentRow = new_row; | |
+ fCurrentField = new_field; | |
+ fCurrentCode = B_INSIDE_VIEW; | |
+ BMessage* message = Window()->CurrentMessage(); | |
+ int32 buttons = 1; | |
+ message->FindInt32("buttons", &buttons); | |
+ fCurrentColumn->MouseDown(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, buttons); | |
+ } | |
+ | |
+ if (!fEditMode) { | |
+ | |
+ fTargetRow = row; | |
+ fTargetRowTop = rowTop; | |
+ FindVisibleRect(fFocusRow, &fFocusRowRect); | |
+ | |
+ float leftWidgetBoundry = indent * kOutlineLevelIndent | |
+ + MAX(kLeftMargin, fMasterView->LatchWidth()) | |
+ - fMasterView->LatchWidth(); | |
+ fLatchRect.Set(leftWidgetBoundry, rowTop, leftWidgetBoundry | |
+ + fMasterView->LatchWidth(), rowTop + row->Height()); | |
+ if (fLatchRect.Contains(position) && row->HasLatch()) { | |
+ fCurrentState = LATCH_CLICKED; | |
+ if (fTargetRow->fNextSelected != 0) | |
+ SetHighColor(fMasterView->Color(B_COLOR_SELECTION)); | |
+ else | |
+ SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND)); | |
+ | |
+ FillRect(fLatchRect); | |
+ if (fLatchRect.Contains(position)) { | |
+ fMasterView->DrawLatch(this, fLatchRect, B_PRESSED_LATCH, | |
+ row); | |
+ } else { | |
+ fMasterView->DrawLatch(this, fLatchRect, | |
+ fTargetRow->fIsExpanded ? B_OPEN_LATCH | |
+ : B_CLOSED_LATCH, row); | |
+ } | |
+ } else { | |
+ Invalidate(fFocusRowRect); | |
+ fFocusRow = fTargetRow; | |
+ FindVisibleRect(fFocusRow, &fFocusRowRect); | |
+ | |
+ ASSERT(fTargetRow != 0); | |
+ | |
+ if ((modifiers() & B_CONTROL_KEY) == 0) | |
+ DeselectAll(); | |
+ | |
+ if ((modifiers() & B_SHIFT_KEY) != 0 && fFirstSelectedItem != 0 | |
+ && fSelectionMode == B_MULTIPLE_SELECTION_LIST) { | |
+ SelectRange(fFirstSelectedItem, fTargetRow); | |
+ } | |
+ else { | |
+ if (fTargetRow->fNextSelected != 0) { | |
+ // Unselect row | |
+ fTargetRow->fNextSelected->fPrevSelected | |
+ = fTargetRow->fPrevSelected; | |
+ fTargetRow->fPrevSelected->fNextSelected | |
+ = fTargetRow->fNextSelected; | |
+ fTargetRow->fPrevSelected = 0; | |
+ fTargetRow->fNextSelected = 0; | |
+ fFirstSelectedItem = NULL; | |
+ } else { | |
+ // Select row | |
+ if (fSelectionMode == B_SINGLE_SELECTION_LIST) | |
+ DeselectAll(); | |
+ | |
+ fTargetRow->fNextSelected | |
+ = fSelectionListDummyHead.fNextSelected; | |
+ fTargetRow->fPrevSelected | |
+ = &fSelectionListDummyHead; | |
+ fTargetRow->fNextSelected->fPrevSelected = fTargetRow; | |
+ fTargetRow->fPrevSelected->fNextSelected = fTargetRow; | |
+ fFirstSelectedItem = fTargetRow; | |
+ } | |
+ | |
+ Invalidate(BRect(fVisibleRect.left, fTargetRowTop, | |
+ fVisibleRect.right, | |
+ fTargetRowTop + fTargetRow->Height())); | |
+ } | |
+ | |
+ fCurrentState = ROW_CLICKED; | |
+ if (fLastSelectedItem != fTargetRow) | |
+ reset_click_count = true; | |
+ fLastSelectedItem = fTargetRow; | |
+ fMasterView->SelectionChanged(); | |
+ | |
+ } | |
+ } | |
+ | |
+ SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS | | |
+ B_NO_POINTER_HISTORY); | |
+ | |
+ } else if (fFocusRow != 0) { | |
+ // User clicked in open space, unhighlight focus row. | |
+ FindVisibleRect(fFocusRow, &fFocusRowRect); | |
+ fFocusRow = 0; | |
+ Invalidate(fFocusRowRect); | |
+ } | |
+ | |
+ // We stash the click counts here because the 'clicks' field | |
+ // is not in the CurrentMessage() when MouseUp is called... ;( | |
+ if (reset_click_count) | |
+ fClickCount = 1; | |
+ else | |
+ Window()->CurrentMessage()->FindInt32("clicks", &fClickCount); | |
+ fClickPoint = position; | |
+ | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::MouseMoved(BPoint position, uint32 /*transit*/, | |
+ const BMessage* /*dragMessage*/) | |
+{ | |
+ if (!fMouseDown) { | |
+ // Update fCurrentField | |
+ bool handle_field = false; | |
+ BField* new_field = 0; | |
+ BRow* new_row = 0; | |
+ BColumn* new_column = 0; | |
+ BRect new_rect(0,0,0,0); | |
+ if (position.y >=0 ) { | |
+ float top; | |
+ int32 indent; | |
+ BRow* row = FindRow(position.y, &indent, &top); | |
+ if (row && position.x >=0 ) { | |
+ float x=0; | |
+ for (int32 c=0;c<fMasterView->CountColumns();c++) { | |
+ new_column = fMasterView->ColumnAt(c); | |
+ if (!new_column->IsVisible()) | |
+ continue; | |
+ if ((MAX(kLeftMargin, | |
+ fMasterView->LatchWidth()) + x) + new_column->Width() | |
+ > position.x) { | |
+ | |
+ if(new_column->WantsEvents()) { | |
+ new_field = row->GetField(c); | |
+ new_row = row; | |
+ FindRect(new_row,&new_rect); | |
+ new_rect.left = MAX(kLeftMargin, | |
+ fMasterView->LatchWidth()) + x; | |
+ new_rect.right = new_rect.left | |
+ + new_column->Width() - 1; | |
+ handle_field = true; | |
+ } | |
+ break; | |
+ } | |
+ x += new_column->Width(); | |
+ } | |
+ } | |
+ } | |
+ | |
+ // Handle mouse moved | |
+ if (handle_field) { | |
+ if (new_field != fCurrentField) { | |
+ if (fCurrentField) { | |
+ fCurrentColumn->MouseMoved(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, 0, | |
+ fCurrentCode = B_EXITED_VIEW); | |
+ } | |
+ fCurrentColumn = new_column; | |
+ fCurrentRow = new_row; | |
+ fCurrentField = new_field; | |
+ fFieldRect = new_rect; | |
+ if (fCurrentField) { | |
+ fCurrentColumn->MouseMoved(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, 0, | |
+ fCurrentCode = B_ENTERED_VIEW); | |
+ } | |
+ } else { | |
+ if (fCurrentField) { | |
+ fCurrentColumn->MouseMoved(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, 0, | |
+ fCurrentCode = B_INSIDE_VIEW); | |
+ } | |
+ } | |
+ } else { | |
+ if (fCurrentField) { | |
+ fCurrentColumn->MouseMoved(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, 0, | |
+ fCurrentCode = B_EXITED_VIEW); | |
+ fCurrentField = 0; | |
+ fCurrentColumn = 0; | |
+ fCurrentRow = 0; | |
+ } | |
+ } | |
+ } else { | |
+ if (fCurrentField) { | |
+ if (fFieldRect.Contains(position)) { | |
+ if (fCurrentCode == B_OUTSIDE_VIEW | |
+ || fCurrentCode == B_EXITED_VIEW) { | |
+ fCurrentColumn->MouseMoved(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, 1, | |
+ fCurrentCode = B_ENTERED_VIEW); | |
+ } else { | |
+ fCurrentColumn->MouseMoved(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, 1, | |
+ fCurrentCode = B_INSIDE_VIEW); | |
+ } | |
+ } else { | |
+ if (fCurrentCode == B_INSIDE_VIEW | |
+ || fCurrentCode == B_ENTERED_VIEW) { | |
+ fCurrentColumn->MouseMoved(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, 1, | |
+ fCurrentCode = B_EXITED_VIEW); | |
+ } else { | |
+ fCurrentColumn->MouseMoved(fMasterView, fCurrentRow, | |
+ fCurrentField, fFieldRect, position, 1, | |
+ fCurrentCode = B_OUTSIDE_VIEW); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (!fEditMode) { | |
+ | |
+ switch (fCurrentState) { | |
+ case LATCH_CLICKED: | |
+ if (fTargetRow->fNextSelected != 0) | |
+ SetHighColor(fMasterView->Color(B_COLOR_SELECTION)); | |
+ else | |
+ SetHighColor(fMasterView->Color(B_COLOR_BACKGROUND)); | |
+ | |
+ FillRect(fLatchRect); | |
+ if (fLatchRect.Contains(position)) { | |
+ fMasterView->DrawLatch(this, fLatchRect, B_PRESSED_LATCH, | |
+ fTargetRow); | |
+ } else { | |
+ fMasterView->DrawLatch(this, fLatchRect, | |
+ fTargetRow->fIsExpanded ? B_OPEN_LATCH : B_CLOSED_LATCH, | |
+ fTargetRow); | |
+ } | |
+ break; | |
+ | |
+ case ROW_CLICKED: | |
+ if (abs((int)(position.x - fClickPoint.x)) > kRowDragSensitivity | |
+ || abs((int)(position.y - fClickPoint.y)) | |
+ > kRowDragSensitivity) { | |
+ fCurrentState = DRAGGING_ROWS; | |
+ fMasterView->InitiateDrag(fClickPoint, | |
+ fTargetRow->fNextSelected != 0); | |
+ } | |
+ break; | |
+ | |
+ case DRAGGING_ROWS: | |
+#if 0 | |
+ // falls through... | |
+#else | |
+ if (fTrackMouse /*&& message*/) { | |
+ if (fVisibleRect.Contains(position)) { | |
+ float top; | |
+ int32 indent; | |
+ BRow* target = FindRow(position.y, &indent, &top); | |
+ if (target) | |
+ SetFocusRow(target, true); | |
+ } | |
+ } | |
+ break; | |
+#endif | |
+ | |
+ default: { | |
+ | |
+ if (fTrackMouse /*&& message*/) { | |
+ // Draw a highlight line... | |
+ if (fVisibleRect.Contains(position)) { | |
+ float top; | |
+ int32 indent; | |
+ BRow* target = FindRow(position.y, &indent, &top); | |
+ if (target == fRollOverRow) | |
+ break; | |
+ if (fRollOverRow) { | |
+ BRect rect; | |
+ FindRect(fRollOverRow, &rect); | |
+ Invalidate(rect); | |
+ } | |
+ fRollOverRow = target; | |
+#if 0 | |
+ SetFocusRow(fRollOverRow,false); | |
+#else | |
+ PushState(); | |
+ SetDrawingMode(B_OP_BLEND); | |
+ SetHighColor(255, 255, 255, 255); | |
+ BRect rect; | |
+ FindRect(fRollOverRow, &rect); | |
+ rect.bottom -= 1.0; | |
+ FillRect(rect); | |
+ PopState(); | |
+#endif | |
+ } else { | |
+ if (fRollOverRow) { | |
+ BRect rect; | |
+ FindRect(fRollOverRow, &rect); | |
+ Invalidate(rect); | |
+ fRollOverRow = NULL; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::MouseUp(BPoint position) | |
+{ | |
+ if (fCurrentField) { | |
+ fCurrentColumn->MouseUp(fMasterView, fCurrentRow, fCurrentField); | |
+ fMouseDown = false; | |
+ } | |
+ | |
+ if (fEditMode) | |
+ return; | |
+ | |
+ switch (fCurrentState) { | |
+ case LATCH_CLICKED: | |
+ if (fLatchRect.Contains(position)) { | |
+ fMasterView->ExpandOrCollapse(fTargetRow, | |
+ !fTargetRow->fIsExpanded); | |
+ } | |
+ | |
+ Invalidate(fLatchRect); | |
+ fCurrentState = INACTIVE; | |
+ break; | |
+ | |
+ case ROW_CLICKED: | |
+ if (fClickCount > 1 | |
+ && abs((int)fClickPoint.x - (int)position.x) | |
+ < kDoubleClickMoveSensitivity | |
+ && abs((int)fClickPoint.y - (int)position.y) | |
+ < kDoubleClickMoveSensitivity) { | |
+ fMasterView->ItemInvoked(); | |
+ } | |
+ fCurrentState = INACTIVE; | |
+ break; | |
+ | |
+ case DRAGGING_ROWS: | |
+ fCurrentState = INACTIVE; | |
+ // Falls through | |
+ | |
+ default: | |
+ if (fDropHighlightY != -1) { | |
+ InvertRect(BRect(0, | |
+ fDropHighlightY - kDropHighlightLineHeight / 2, | |
+ 1000000, fDropHighlightY + kDropHighlightLineHeight / 2)); | |
+ // Erase the old target line | |
+ fDropHighlightY = -1; | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::MessageReceived(BMessage* message) | |
+{ | |
+ if (message->WasDropped()) { | |
+ fMasterView->MessageDropped(message, | |
+ ConvertFromScreen(message->DropPoint())); | |
+ } else { | |
+ BView::MessageReceived(message); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::ChangeFocusRow(bool up, bool updateSelection, | |
+ bool addToCurrentSelection) | |
+{ | |
+ int32 indent; | |
+ float top; | |
+ float newRowPos = 0; | |
+ float verticalScroll = 0; | |
+ | |
+ if (fFocusRow) { | |
+ // A row currently has the focus, get information about it | |
+ newRowPos = fFocusRowRect.top + (up ? -4 : fFocusRow->Height() + 4); | |
+ if (newRowPos < fVisibleRect.top + 20) | |
+ verticalScroll = newRowPos - 20; | |
+ else if (newRowPos > fVisibleRect.bottom - 20) | |
+ verticalScroll = newRowPos - fVisibleRect.Height() + 20; | |
+ } else | |
+ newRowPos = fVisibleRect.top + 2; | |
+ // no row is currently focused, set this to the top of the window | |
+ // so we will select the first visible item in the list. | |
+ | |
+ BRow* newRow = FindRow(newRowPos, &indent, &top); | |
+ if (newRow) { | |
+ if (fFocusRow) { | |
+ fFocusRowRect.right = 10000; | |
+ Invalidate(fFocusRowRect); | |
+ } | |
+ fFocusRow = newRow; | |
+ fFocusRowRect.top = top; | |
+ fFocusRowRect.left = 0; | |
+ fFocusRowRect.right = 10000; | |
+ fFocusRowRect.bottom = fFocusRowRect.top + fFocusRow->Height(); | |
+ Invalidate(fFocusRowRect); | |
+ | |
+ if (updateSelection) { | |
+ if (!addToCurrentSelection | |
+ || fSelectionMode == B_SINGLE_SELECTION_LIST) { | |
+ DeselectAll(); | |
+ } | |
+ | |
+ if (fFocusRow->fNextSelected == 0) { | |
+ fFocusRow->fNextSelected | |
+ = fSelectionListDummyHead.fNextSelected; | |
+ fFocusRow->fPrevSelected = &fSelectionListDummyHead; | |
+ fFocusRow->fNextSelected->fPrevSelected = fFocusRow; | |
+ fFocusRow->fPrevSelected->fNextSelected = fFocusRow; | |
+ } | |
+ | |
+ fLastSelectedItem = fFocusRow; | |
+ } | |
+ } else | |
+ Invalidate(fFocusRowRect); | |
+ | |
+ if (verticalScroll != 0) { | |
+ BScrollBar* vScrollBar = ScrollBar(B_VERTICAL); | |
+ float min, max; | |
+ vScrollBar->GetRange(&min, &max); | |
+ if (verticalScroll < min) | |
+ verticalScroll = min; | |
+ else if (verticalScroll > max) | |
+ verticalScroll = max; | |
+ | |
+ vScrollBar->SetValue(verticalScroll); | |
+ } | |
+ | |
+ if (newRow && updateSelection) | |
+ fMasterView->SelectionChanged(); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::MoveFocusToVisibleRect() | |
+{ | |
+ fFocusRow = 0; | |
+ ChangeFocusRow(true, true, false); | |
+} | |
+ | |
+ | |
+BRow* | |
+OutlineView::CurrentSelection(BRow* lastSelected) const | |
+{ | |
+ BRow* row; | |
+ if (lastSelected == 0) | |
+ row = fSelectionListDummyHead.fNextSelected; | |
+ else | |
+ row = lastSelected->fNextSelected; | |
+ | |
+ | |
+ if (row == &fSelectionListDummyHead) | |
+ row = 0; | |
+ | |
+ return row; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::ToggleFocusRowSelection(bool selectRange) | |
+{ | |
+ if (fFocusRow == 0) | |
+ return; | |
+ | |
+ if (selectRange && fSelectionMode == B_MULTIPLE_SELECTION_LIST) | |
+ SelectRange(fLastSelectedItem, fFocusRow); | |
+ else { | |
+ if (fFocusRow->fNextSelected != 0) { | |
+ // Unselect row | |
+ fFocusRow->fNextSelected->fPrevSelected = fFocusRow->fPrevSelected; | |
+ fFocusRow->fPrevSelected->fNextSelected = fFocusRow->fNextSelected; | |
+ fFocusRow->fPrevSelected = 0; | |
+ fFocusRow->fNextSelected = 0; | |
+ } else { | |
+ // Select row | |
+ if (fSelectionMode == B_SINGLE_SELECTION_LIST) | |
+ DeselectAll(); | |
+ | |
+ fFocusRow->fNextSelected = fSelectionListDummyHead.fNextSelected; | |
+ fFocusRow->fPrevSelected = &fSelectionListDummyHead; | |
+ fFocusRow->fNextSelected->fPrevSelected = fFocusRow; | |
+ fFocusRow->fPrevSelected->fNextSelected = fFocusRow; | |
+ } | |
+ } | |
+ | |
+ fLastSelectedItem = fFocusRow; | |
+ fMasterView->SelectionChanged(); | |
+ Invalidate(fFocusRowRect); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::ToggleFocusRowOpen() | |
+{ | |
+ if (fFocusRow) | |
+ fMasterView->ExpandOrCollapse(fFocusRow, !fFocusRow->fIsExpanded); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::ExpandOrCollapse(BRow* parentRow, bool expand) | |
+{ | |
+ // TODO: Could use CopyBits here to speed things up. | |
+ | |
+ if (parentRow == NULL) | |
+ return; | |
+ | |
+ if (parentRow->fIsExpanded == expand) | |
+ return; | |
+ | |
+ parentRow->fIsExpanded = expand; | |
+ | |
+ BRect parentRect; | |
+ if (FindRect(parentRow, &parentRect)) { | |
+ // Determine my new height | |
+ float subTreeHeight = 0.0; | |
+ if (parentRow->fIsExpanded) | |
+ for (RecursiveOutlineIterator iterator(parentRow->fChildList); | |
+ iterator.CurrentRow(); | |
+ iterator.GoToNext() | |
+ ) | |
+ { | |
+ subTreeHeight += iterator.CurrentRow()->Height()+1; | |
+ } | |
+ else | |
+ for (RecursiveOutlineIterator iterator(parentRow->fChildList); | |
+ iterator.CurrentRow(); | |
+ iterator.GoToNext() | |
+ ) | |
+ { | |
+ subTreeHeight -= iterator.CurrentRow()->Height()+1; | |
+ } | |
+ fItemsHeight += subTreeHeight; | |
+ | |
+ // Adjust focus row if necessary. | |
+ if (FindRect(fFocusRow, &fFocusRowRect) == false) { | |
+ // focus row is in a subtree that has collapsed, | |
+ // move it up to the parent. | |
+ fFocusRow = parentRow; | |
+ FindRect(fFocusRow, &fFocusRowRect); | |
+ } | |
+ | |
+ Invalidate(BRect(0, parentRect.top, fVisibleRect.right, | |
+ fVisibleRect.bottom)); | |
+ FixScrollBar(false); | |
+ } | |
+} | |
+ | |
+void | |
+OutlineView::RemoveRow(BRow* row) | |
+{ | |
+ if (row == NULL) | |
+ return; | |
+ | |
+ BRow* parentRow; | |
+ bool parentIsVisible; | |
+ FindParent(row, &parentRow, &parentIsVisible); | |
+ // NOTE: This could be a root row without a parent, in which case | |
+ // it is always visible, though. | |
+ | |
+ // Adjust height for the visible sub-tree that is going to be removed. | |
+ float subTreeHeight = 0.0f; | |
+ if (parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded)) { | |
+ // The row itself is visible at least. | |
+ subTreeHeight = row->Height() + 1; | |
+ if (row->fIsExpanded) { | |
+ // Adjust for the height of visible sub-items as well. | |
+ // (By default, the iterator follows open branches only.) | |
+ for (RecursiveOutlineIterator iterator(row->fChildList); | |
+ iterator.CurrentRow(); iterator.GoToNext()) | |
+ subTreeHeight += iterator.CurrentRow()->Height() + 1; | |
+ } | |
+ BRect invalid; | |
+ if (FindRect(row, &invalid)) { | |
+ invalid.bottom = Bounds().bottom; | |
+ if (invalid.IsValid()) | |
+ Invalidate(invalid); | |
+ } | |
+ } | |
+ | |
+ fItemsHeight -= subTreeHeight; | |
+ | |
+ FixScrollBar(false); | |
+ int32 indent = 0; | |
+ float top = 0.0; | |
+ if (FindRow(fVisibleRect.top, &indent, &top) == NULL && ScrollBar(B_VERTICAL) != NULL) { | |
+ // after removing this row, no rows are actually visible any more, | |
+ // force a scroll to make them visible again | |
+ if (fItemsHeight > fVisibleRect.Height()) | |
+ ScrollBy(0.0, fItemsHeight - fVisibleRect.Height() - Bounds().top); | |
+ else | |
+ ScrollBy(0.0, -Bounds().top); | |
+ } | |
+ if (parentRow != NULL) { | |
+ parentRow->fChildList->RemoveItem(row); | |
+ if (parentRow->fChildList->CountItems() == 0) { | |
+ delete parentRow->fChildList; | |
+ parentRow->fChildList = 0; | |
+ // It was the last child row of the parent, which also means the | |
+ // latch disappears. | |
+ BRect parentRowRect; | |
+ if (parentIsVisible && FindRect(parentRow, &parentRowRect)) | |
+ Invalidate(parentRowRect); | |
+ } | |
+ } else | |
+ fRows.RemoveItem(row); | |
+ | |
+ // Adjust focus row if necessary. | |
+ if (fFocusRow && !FindRect(fFocusRow, &fFocusRowRect)) { | |
+ // focus row is in a subtree that is gone, move it up to the parent. | |
+ fFocusRow = parentRow; | |
+ if (fFocusRow) | |
+ FindRect(fFocusRow, &fFocusRowRect); | |
+ } | |
+ | |
+ // Remove this from the selection if necessary | |
+ if (row->fNextSelected != 0) { | |
+ row->fNextSelected->fPrevSelected = row->fPrevSelected; | |
+ row->fPrevSelected->fNextSelected = row->fNextSelected; | |
+ row->fPrevSelected = 0; | |
+ row->fNextSelected = 0; | |
+ fMasterView->SelectionChanged(); | |
+ } | |
+ | |
+ fCurrentColumn = 0; | |
+ fCurrentRow = 0; | |
+ fCurrentField = 0; | |
+} | |
+ | |
+ | |
+BRowContainer* | |
+OutlineView::RowList() | |
+{ | |
+ return &fRows; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::UpdateRow(BRow* row) | |
+{ | |
+ if (row) { | |
+ // Determine if this row has changed its sort order | |
+ BRow* parentRow = NULL; | |
+ bool parentIsVisible = false; | |
+ FindParent(row, &parentRow, &parentIsVisible); | |
+ | |
+ BRowContainer* list = (parentRow == NULL) ? &fRows : parentRow->fChildList; | |
+ | |
+ if(list) { | |
+ int32 rowIndex = list->IndexOf(row); | |
+ ASSERT(rowIndex >= 0); | |
+ ASSERT(list->ItemAt(rowIndex) == row); | |
+ | |
+ bool rowMoved = false; | |
+ if (rowIndex > 0 && CompareRows(list->ItemAt(rowIndex - 1), row) > 0) | |
+ rowMoved = true; | |
+ | |
+ if (rowIndex < list->CountItems() - 1 && CompareRows(list->ItemAt(rowIndex + 1), | |
+ row) < 0) | |
+ rowMoved = true; | |
+ | |
+ if (rowMoved) { | |
+ // Sort location of this row has changed. | |
+ // Remove and re-add in the right spot | |
+ SortList(list, parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded)); | |
+ } else if (parentIsVisible && (parentRow == NULL || parentRow->fIsExpanded)) { | |
+ BRect invalidRect; | |
+ if (FindVisibleRect(row, &invalidRect)) | |
+ Invalidate(invalidRect); | |
+ } | |
+ } | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::AddRow(BRow* row, int32 Index, BRow* parentRow) | |
+{ | |
+ if (!row) | |
+ return; | |
+ | |
+ row->fParent = parentRow; | |
+ | |
+ if (fMasterView->SortingEnabled()) { | |
+ // Ignore index here. | |
+ if (parentRow) { | |
+ if (parentRow->fChildList == NULL) | |
+ parentRow->fChildList = new BRowContainer; | |
+ | |
+ AddSorted(parentRow->fChildList, row); | |
+ } else | |
+ AddSorted(&fRows, row); | |
+ } else { | |
+ // Note, a -1 index implies add to end if sorting is not enabled | |
+ if (parentRow) { | |
+ if (parentRow->fChildList == 0) | |
+ parentRow->fChildList = new BRowContainer; | |
+ | |
+ if (Index < 0 || Index > parentRow->fChildList->CountItems()) | |
+ parentRow->fChildList->AddItem(row); | |
+ else | |
+ parentRow->fChildList->AddItem(row, Index); | |
+ } else { | |
+ if (Index < 0 || Index >= fRows.CountItems()) | |
+ fRows.AddItem(row); | |
+ else | |
+ fRows.AddItem(row, Index); | |
+ } | |
+ } | |
+ | |
+ if (parentRow == 0 || parentRow->fIsExpanded) | |
+ fItemsHeight += row->Height() + 1; | |
+ | |
+ FixScrollBar(false); | |
+ | |
+ BRect newRowRect; | |
+ bool newRowIsInOpenBranch = FindRect(row, &newRowRect); | |
+ | |
+ if (fFocusRow && fFocusRowRect.top > newRowRect.bottom) { | |
+ // The focus row has moved. | |
+ Invalidate(fFocusRowRect); | |
+ FindRect(fFocusRow, &fFocusRowRect); | |
+ Invalidate(fFocusRowRect); | |
+ } | |
+ | |
+ if (newRowIsInOpenBranch) { | |
+ if (fCurrentState == INACTIVE) { | |
+ if (newRowRect.bottom < fVisibleRect.top) { | |
+ // The new row is totally above the current viewport, move | |
+ // everything down and redraw the first line. | |
+ BRect source(fVisibleRect); | |
+ BRect dest(fVisibleRect); | |
+ source.bottom -= row->Height() + 1; | |
+ dest.top += row->Height() + 1; | |
+ CopyBits(source, dest); | |
+ Invalidate(BRect(fVisibleRect.left, fVisibleRect.top, fVisibleRect.right, | |
+ fVisibleRect.top + newRowRect.Height())); | |
+ } else if (newRowRect.top < fVisibleRect.bottom) { | |
+ // New item is somewhere in the current region. Scroll everything | |
+ // beneath it down and invalidate just the new row rect. | |
+ BRect source(fVisibleRect.left, newRowRect.top, fVisibleRect.right, | |
+ fVisibleRect.bottom - newRowRect.Height()); | |
+ BRect dest(source); | |
+ dest.OffsetBy(0, newRowRect.Height() + 1); | |
+ CopyBits(source, dest); | |
+ Invalidate(newRowRect); | |
+ } // otherwise, this is below the currently visible region | |
+ } else { | |
+ // Adding the item may have caused the item that the user is currently | |
+ // selected to move. This would cause annoying drawing and interaction | |
+ // bugs, as the position of that item is cached. If this happens, resize | |
+ // the scroll bar, then scroll back so the selected item is in view. | |
+ BRect targetRect; | |
+ if (FindRect(fTargetRow, &targetRect)) { | |
+ float delta = targetRect.top - fTargetRowTop; | |
+ if (delta != 0) { | |
+ // This causes a jump because ScrollBy will copy a chunk of the view. | |
+ // Since the actual contents of the view have been offset, we don't | |
+ // want this, we just want to change the virtual origin of the window. | |
+ // Constrain the clipping region so everything is clipped out so no | |
+ // copy occurs. | |
+ // | |
+ // xxx this currently doesn't work if the scroll bars aren't enabled. | |
+ // everything will still move anyway. A minor annoyance. | |
+ BRegion emptyRegion; | |
+ ConstrainClippingRegion(&emptyRegion); | |
+ PushState(); | |
+ ScrollBy(0, delta); | |
+ PopState(); | |
+ ConstrainClippingRegion(NULL); | |
+ | |
+ fTargetRowTop += delta; | |
+ fClickPoint.y += delta; | |
+ fLatchRect.OffsetBy(0, delta); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ | |
+ // If the parent was previously childless, it will need to have a latch | |
+ // drawn. | |
+ BRect parentRect; | |
+ if (parentRow && parentRow->fChildList->CountItems() == 1 | |
+ && FindVisibleRect(parentRow, &parentRect)) | |
+ Invalidate(parentRect); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::FixScrollBar(bool scrollToFit) | |
+{ | |
+ BScrollBar* vScrollBar = ScrollBar(B_VERTICAL); | |
+ if (vScrollBar) { | |
+ if (fItemsHeight > fVisibleRect.Height()) { | |
+ float maxScrollBarValue = fItemsHeight - fVisibleRect.Height(); | |
+ vScrollBar->SetProportion(fVisibleRect.Height() / fItemsHeight); | |
+ | |
+ // If the user is scrolled down too far when making the range smaller, the list | |
+ // will jump suddenly, which is undesirable. In this case, don't fix the scroll | |
+ // bar here. In ScrollTo, it checks to see if this has occured, and will | |
+ // fix the scroll bars sneakily if the user has scrolled up far enough. | |
+ if (scrollToFit || vScrollBar->Value() <= maxScrollBarValue) { | |
+ vScrollBar->SetRange(0.0, maxScrollBarValue); | |
+ vScrollBar->SetSteps(20.0, fVisibleRect.Height()); | |
+ } | |
+ } else if (vScrollBar->Value() == 0.0) | |
+ vScrollBar->SetRange(0.0, 0.0); // disable scroll bar. | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::AddSorted(BRowContainer* list, BRow* row) | |
+{ | |
+ if (list && row) { | |
+ // Find general vicinity with binary search. | |
+ int32 lower = 0; | |
+ int32 upper = list->CountItems()-1; | |
+ while( lower < upper ) { | |
+ int32 middle = lower + (upper-lower+1)/2; | |
+ int32 cmp = CompareRows(row, list->ItemAt(middle)); | |
+ if( cmp < 0 ) upper = middle-1; | |
+ else if( cmp > 0 ) lower = middle+1; | |
+ else lower = upper = middle; | |
+ } | |
+ | |
+ // At this point, 'upper' and 'lower' at the last found item. | |
+ // Arbitrarily use 'upper' and determine the final insertion | |
+ // point -- either before or after this item. | |
+ if( upper < 0 ) upper = 0; | |
+ else if( upper < list->CountItems() ) { | |
+ if( CompareRows(row, list->ItemAt(upper)) > 0 ) upper++; | |
+ } | |
+ | |
+ if (upper >= list->CountItems()) | |
+ list->AddItem(row); // Adding to end. | |
+ else | |
+ list->AddItem(row, upper); // Insert | |
+ } | |
+} | |
+ | |
+ | |
+int32 | |
+OutlineView::CompareRows(BRow* row1, BRow* row2) | |
+{ | |
+ int32 itemCount (fSortColumns->CountItems()); | |
+ if (row1 && row2) { | |
+ for (int32 index = 0; index < itemCount; index++) { | |
+ BColumn* column = (BColumn*) fSortColumns->ItemAt(index); | |
+ int comp = 0; | |
+ BField* field1 = (BField*) row1->GetField(column->fFieldID); | |
+ BField* field2 = (BField*) row2->GetField(column->fFieldID); | |
+ if (field1 && field2) | |
+ comp = column->CompareFields(field1, field2); | |
+ | |
+ if (!column->fSortAscending) | |
+ comp = -comp; | |
+ | |
+ if (comp != 0) | |
+ return comp; | |
+ } | |
+ } | |
+ return 0; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::FrameResized(float width, float height) | |
+{ | |
+ fVisibleRect.right = fVisibleRect.left + width; | |
+ fVisibleRect.bottom = fVisibleRect.top + height; | |
+ FixScrollBar(true); | |
+ _inherited::FrameResized(width, height); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::ScrollTo(BPoint position) | |
+{ | |
+ fVisibleRect.OffsetTo(position.x, position.y); | |
+ | |
+ // In FixScrollBar, we might not have been able to change the size of | |
+ // the scroll bar because the user was scrolled down too far. Take | |
+ // this opportunity to sneak it in if we can. | |
+ BScrollBar* vScrollBar = ScrollBar(B_VERTICAL); | |
+ float maxScrollBarValue = fItemsHeight - fVisibleRect.Height(); | |
+ float min, max; | |
+ vScrollBar->GetRange(&min, &max); | |
+ if (max != maxScrollBarValue && position.y > maxScrollBarValue) | |
+ FixScrollBar(true); | |
+ | |
+ _inherited::ScrollTo(position); | |
+} | |
+ | |
+ | |
+const BRect& | |
+OutlineView::VisibleRect() const | |
+{ | |
+ return fVisibleRect; | |
+} | |
+ | |
+ | |
+bool | |
+OutlineView::FindVisibleRect(BRow* row, BRect* _rect) | |
+{ | |
+ if (row && _rect) { | |
+ float line = 0.0; | |
+ for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow(); | |
+ iterator.GoToNext()) { | |
+ if (line > fVisibleRect.bottom) | |
+ break; | |
+ | |
+ if (iterator.CurrentRow() == row) { | |
+ _rect->Set(fVisibleRect.left, line, fVisibleRect.right, | |
+ line + row->Height()); | |
+ return true; | |
+ } | |
+ | |
+ line += iterator.CurrentRow()->Height() + 1; | |
+ } | |
+ } | |
+ return false; | |
+} | |
+ | |
+ | |
+bool | |
+OutlineView::FindRect(const BRow* row, BRect* _rect) | |
+{ | |
+ float line = 0.0; | |
+ for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow(); | |
+ iterator.GoToNext()) { | |
+ if (iterator.CurrentRow() == row) { | |
+ _rect->Set(fVisibleRect.left, line, fVisibleRect.right, | |
+ line + row->Height()); | |
+ return true; | |
+ } | |
+ | |
+ line += iterator.CurrentRow()->Height() + 1; | |
+ } | |
+ | |
+ return false; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::ScrollTo(const BRow* row) | |
+{ | |
+ BRect rect; | |
+ if (FindRect(row, &rect)) { | |
+ BRect bounds = Bounds(); | |
+ if (rect.top < bounds.top) | |
+ ScrollTo(BPoint(bounds.left, rect.top)); | |
+ else if (rect.bottom > bounds.bottom) | |
+ ScrollBy(0, rect.bottom - bounds.bottom); | |
+ } | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::DeselectAll() | |
+{ | |
+ // Invalidate all selected rows | |
+ float line = 0.0; | |
+ for (RecursiveOutlineIterator iterator(&fRows); iterator.CurrentRow(); | |
+ iterator.GoToNext()) { | |
+ if (line > fVisibleRect.bottom) | |
+ break; | |
+ | |
+ BRow* row = iterator.CurrentRow(); | |
+ if (line + row->Height() > fVisibleRect.top) { | |
+ if (row->fNextSelected != 0) | |
+ Invalidate(BRect(fVisibleRect.left, line, fVisibleRect.right, | |
+ line + row->Height())); | |
+ } | |
+ | |
+ line += row->Height() + 1; | |
+ } | |
+ | |
+ // Set items not selected | |
+ while (fSelectionListDummyHead.fNextSelected != &fSelectionListDummyHead) { | |
+ BRow* row = fSelectionListDummyHead.fNextSelected; | |
+ row->fNextSelected->fPrevSelected = row->fPrevSelected; | |
+ row->fPrevSelected->fNextSelected = row->fNextSelected; | |
+ row->fNextSelected = 0; | |
+ row->fPrevSelected = 0; | |
+ } | |
+} | |
+ | |
+ | |
+BRow* | |
+OutlineView::FocusRow() const | |
+{ | |
+ return fFocusRow; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::SetFocusRow(BRow* row, bool Select) | |
+{ | |
+ if (row) { | |
+ if (Select) | |
+ AddToSelection(row); | |
+ | |
+ if (fFocusRow == row) | |
+ return; | |
+ | |
+ Invalidate(fFocusRowRect); // invalidate previous | |
+ | |
+ fTargetRow = fFocusRow = row; | |
+ | |
+ FindVisibleRect(fFocusRow, &fFocusRowRect); | |
+ Invalidate(fFocusRowRect); // invalidate current | |
+ | |
+ fFocusRowRect.right = 10000; | |
+ fMasterView->SelectionChanged(); | |
+ } | |
+} | |
+ | |
+ | |
+bool | |
+OutlineView::SortList(BRowContainer* list, bool isVisible) | |
+{ | |
+ if (list) { | |
+ // Shellsort | |
+ BRow** items | |
+ = (BRow**) BObjectList<BRow>::Private(list).AsBList()->Items(); | |
+ int32 numItems = list->CountItems(); | |
+ int h; | |
+ for (h = 1; h < numItems / 9; h = 3 * h + 1) | |
+ ; | |
+ | |
+ for (;h > 0; h /= 3) { | |
+ for (int step = h; step < numItems; step++) { | |
+ BRow* temp = items[step]; | |
+ int i; | |
+ for (i = step - h; i >= 0; i -= h) { | |
+ if (CompareRows(temp, items[i]) < 0) | |
+ items[i + h] = items[i]; | |
+ else | |
+ break; | |
+ } | |
+ | |
+ items[i + h] = temp; | |
+ } | |
+ } | |
+ | |
+ if (isVisible) { | |
+ Invalidate(); | |
+ | |
+ InvalidateCachedPositions(); | |
+ int lockCount = Window()->CountLocks(); | |
+ for (int i = 0; i < lockCount; i++) | |
+ Window()->Unlock(); | |
+ | |
+ while (lockCount--) | |
+ if (!Window()->Lock()) | |
+ return false; // Window is gone... | |
+ } | |
+ } | |
+ return true; | |
+} | |
+ | |
+ | |
+int32 | |
+OutlineView::DeepSortThreadEntry(void* _outlineView) | |
+{ | |
+ ((OutlineView*) _outlineView)->DeepSort(); | |
+ return 0; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::DeepSort() | |
+{ | |
+ struct stack_entry { | |
+ bool isVisible; | |
+ BRowContainer* list; | |
+ int32 listIndex; | |
+ } stack[kMaxDepth]; | |
+ int32 stackTop = 0; | |
+ | |
+ stack[stackTop].list = &fRows; | |
+ stack[stackTop].isVisible = true; | |
+ stack[stackTop].listIndex = 0; | |
+ fNumSorted = 0; | |
+ | |
+ if (Window()->Lock() == false) | |
+ return; | |
+ | |
+ bool doneSorting = false; | |
+ while (!doneSorting && !fSortCancelled) { | |
+ | |
+ stack_entry* currentEntry = &stack[stackTop]; | |
+ | |
+ // xxx Can make the invalidate area smaller by finding the rect for the | |
+ // parent item and using that as the top of the invalid rect. | |
+ | |
+ bool haveLock = SortList(currentEntry->list, currentEntry->isVisible); | |
+ if (!haveLock) | |
+ return ; // window is gone. | |
+ | |
+ // Fix focus rect. | |
+ InvalidateCachedPositions(); | |
+ if (fCurrentState != INACTIVE) | |
+ fCurrentState = INACTIVE; // sorry... | |
+ | |
+ // next list. | |
+ bool foundNextList = false; | |
+ while (!foundNextList && !fSortCancelled) { | |
+ for (int32 index = currentEntry->listIndex; index < currentEntry->list->CountItems(); | |
+ index++) { | |
+ BRow* parentRow = currentEntry->list->ItemAt(index); | |
+ BRowContainer* childList = parentRow->fChildList; | |
+ if (childList != 0) { | |
+ currentEntry->listIndex = index + 1; | |
+ stackTop++; | |
+ ASSERT(stackTop < kMaxDepth); | |
+ stack[stackTop].listIndex = 0; | |
+ stack[stackTop].list = childList; | |
+ stack[stackTop].isVisible = (currentEntry->isVisible && parentRow->fIsExpanded); | |
+ foundNextList = true; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (!foundNextList) { | |
+ // back up | |
+ if (--stackTop < 0) { | |
+ doneSorting = true; | |
+ break; | |
+ } | |
+ | |
+ currentEntry = &stack[stackTop]; | |
+ } | |
+ } | |
+ } | |
+ | |
+ Window()->Unlock(); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::StartSorting() | |
+{ | |
+ // If this view is not yet attached to a window, don't start a sort thread! | |
+ if (Window() == NULL) | |
+ return; | |
+ | |
+ if (fSortThread != B_BAD_THREAD_ID) { | |
+ thread_info tinfo; | |
+ if (get_thread_info(fSortThread, &tinfo) == B_OK) { | |
+ // Unlock window so this won't deadlock (sort thread is probably | |
+ // waiting to lock window). | |
+ | |
+ int lockCount = Window()->CountLocks(); | |
+ for (int i = 0; i < lockCount; i++) | |
+ Window()->Unlock(); | |
+ | |
+ fSortCancelled = true; | |
+ int32 status; | |
+ wait_for_thread(fSortThread, &status); | |
+ | |
+ while (lockCount--) | |
+ if (!Window()->Lock()) | |
+ return ; // Window is gone... | |
+ } | |
+ } | |
+ | |
+ fSortCancelled = false; | |
+ fSortThread = spawn_thread(DeepSortThreadEntry, "sort_thread", B_NORMAL_PRIORITY, this); | |
+ resume_thread(fSortThread); | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::SelectRange(BRow* start, BRow* end) | |
+{ | |
+ if (!start || !end) | |
+ return; | |
+ | |
+ if (start == end) // start is always selected when this is called | |
+ return; | |
+ | |
+ RecursiveOutlineIterator iterator(&fRows, false); | |
+ while (iterator.CurrentRow() != 0) { | |
+ if (iterator.CurrentRow() == end) { | |
+ // reverse selection, swap to fix special case | |
+ BRow* temp = start; | |
+ start = end; | |
+ end = temp; | |
+ break; | |
+ } else if (iterator.CurrentRow() == start) | |
+ break; | |
+ | |
+ iterator.GoToNext(); | |
+ } | |
+ | |
+ while (true) { | |
+ BRow* row = iterator.CurrentRow(); | |
+ if (row) { | |
+ if (row->fNextSelected == 0) { | |
+ row->fNextSelected = fSelectionListDummyHead.fNextSelected; | |
+ row->fPrevSelected = &fSelectionListDummyHead; | |
+ row->fNextSelected->fPrevSelected = row; | |
+ row->fPrevSelected->fNextSelected = row; | |
+ } | |
+ } else | |
+ break; | |
+ | |
+ if (row == end) | |
+ break; | |
+ | |
+ iterator.GoToNext(); | |
+ } | |
+ | |
+ Invalidate(); // xxx make invalidation smaller | |
+} | |
+ | |
+ | |
+bool | |
+OutlineView::FindParent(BRow* row, BRow** outParent, bool* outParentIsVisible) | |
+{ | |
+ bool result = false; | |
+ if (row != NULL && outParent != NULL) { | |
+ *outParent = row->fParent; | |
+ | |
+ if (outParentIsVisible != NULL) { | |
+ // Walk up the parent chain to determine if this row is visible | |
+ *outParentIsVisible = true; | |
+ for (BRow* currentRow = row->fParent; currentRow != NULL; | |
+ currentRow = currentRow->fParent) { | |
+ if (!currentRow->fIsExpanded) { | |
+ *outParentIsVisible = false; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ | |
+ result = *outParent != NULL; | |
+ } | |
+ | |
+ return result; | |
+} | |
+ | |
+ | |
+int32 | |
+OutlineView::IndexOf(BRow* row) | |
+{ | |
+ if (row) { | |
+ if (row->fParent == 0) | |
+ return fRows.IndexOf(row); | |
+ | |
+ ASSERT(row->fParent->fChildList); | |
+ return row->fParent->fChildList->IndexOf(row); | |
+ } | |
+ | |
+ return B_ERROR; | |
+} | |
+ | |
+ | |
+void | |
+OutlineView::InvalidateCachedPositions() | |
+{ | |
+ if (fFocusRow) | |
+ FindRect(fFocusRow, &fFocusRowRect); | |
+} | |
+ | |
+ | |
+float | |
+OutlineView::GetColumnPreferredWidth(BColumn* column) | |
+{ | |
+ float preferred = 0.0; | |
+ for (RecursiveOutlineIterator iterator(&fRows); BRow* row = | |
+ iterator.CurrentRow(); iterator.GoToNext()) { | |
+ BField* field = row->GetField(column->fFieldID); | |
+ if (field) { | |
+ float width = column->GetPreferredWidth(field, this) | |
+ + iterator.CurrentLevel() * kOutlineLevelIndent; | |
+ preferred = max_c(preferred, width); | |
+ } | |
+ } | |
+ | |
+ BString name; | |
+ column->GetColumnName(&name); | |
+ preferred = max_c(preferred, StringWidth(name)); | |
+ | |
+ // Constrain to preferred width. This makes the method do a little | |
+ // more than asked, but it's for convenience. | |
+ if (preferred < column->MinWidth()) | |
+ preferred = column->MinWidth(); | |
+ else if (preferred > column->MaxWidth()) | |
+ preferred = column->MaxWidth(); | |
+ | |
+ return preferred; | |
+} | |
+ | |
+ | |
+// #pragma mark - | |
+ | |
+ | |
+RecursiveOutlineIterator::RecursiveOutlineIterator(BRowContainer* list, | |
+ bool openBranchesOnly) | |
+ : | |
+ fStackIndex(0), | |
+ fCurrentListIndex(0), | |
+ fCurrentListDepth(0), | |
+ fOpenBranchesOnly(openBranchesOnly) | |
+{ | |
+ if (list == 0 || list->CountItems() == 0) | |
+ fCurrentList = 0; | |
+ else | |
+ fCurrentList = list; | |
+} | |
+ | |
+ | |
+BRow* | |
+RecursiveOutlineIterator::CurrentRow() const | |
+{ | |
+ if (fCurrentList == 0) | |
+ return 0; | |
+ | |
+ return fCurrentList->ItemAt(fCurrentListIndex); | |
+} | |
+ | |
+ | |
+void | |
+RecursiveOutlineIterator::GoToNext() | |
+{ | |
+ if (fCurrentList == 0) | |
+ return; | |
+ if (fCurrentListIndex < 0 || fCurrentListIndex >= fCurrentList->CountItems()) { | |
+ fCurrentList = 0; | |
+ return; | |
+ } | |
+ | |
+ BRow* currentRow = fCurrentList->ItemAt(fCurrentListIndex); | |
+ if(currentRow) { | |
+ if (currentRow->fChildList && (currentRow->fIsExpanded || !fOpenBranchesOnly) | |
+ && currentRow->fChildList->CountItems() > 0) { | |
+ // Visit child. | |
+ // Put current list on the stack if it needs to be revisited. | |
+ if (fCurrentListIndex < fCurrentList->CountItems() - 1) { | |
+ fStack[fStackIndex].fRowSet = fCurrentList; | |
+ fStack[fStackIndex].fIndex = fCurrentListIndex + 1; | |
+ fStack[fStackIndex].fDepth = fCurrentListDepth; | |
+ fStackIndex++; | |
+ } | |
+ | |
+ fCurrentList = currentRow->fChildList; | |
+ fCurrentListIndex = 0; | |
+ fCurrentListDepth++; | |
+ } else if (fCurrentListIndex < fCurrentList->CountItems() - 1) | |
+ fCurrentListIndex++; // next item in current list | |
+ else if (--fStackIndex >= 0) { | |
+ fCurrentList = fStack[fStackIndex].fRowSet; | |
+ fCurrentListIndex = fStack[fStackIndex].fIndex; | |
+ fCurrentListDepth = fStack[fStackIndex].fDepth; | |
+ } else | |
+ fCurrentList = 0; | |
+ } | |
+} | |
+ | |
+ | |
+int32 | |
+RecursiveOutlineIterator::CurrentLevel() const | |
+{ | |
+ return fCurrentListDepth; | |
+} | |
+ | |
+ | |
Index: haiku/libhaiku/HashMap.h | |
=================================================================== | |
--- haiku/libhaiku/HashMap.h (revision 0) | |
+++ haiku/libhaiku/HashMap.h (revision 0) | |
@@ -0,0 +1,450 @@ | |
+// HashMap.h | |
+// | |
+// Copyright (c) 2004-2007, Ingo Weinhold (bonefish@cs.tu-berlin.de) | |
+// | |
+// 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 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 MERCHANTABILITY, | |
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
+// THE AUTHORS OR COPYRIGHT HOLDERS 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 a copyright holder shall | |
+// not be used in advertising or otherwise to promote the sale, use or other | |
+// dealings in this Software without prior written authorization of the | |
+// copyright holder. | |
+ | |
+#ifndef HASH_MAP_H | |
+#define HASH_MAP_H | |
+ | |
+//#include <Debug.h> | |
+#include <Locker.h> | |
+ | |
+#include "AutoLocker.h" | |
+#include "OpenHashTable.h" | |
+ | |
+namespace BPrivate { | |
+ | |
+// HashMapElement | |
+template<typename Key, typename Value> | |
+class HashMapElement : public OpenHashElement { | |
+private: | |
+ typedef HashMapElement<Key, Value> Element; | |
+public: | |
+ | |
+ HashMapElement() : OpenHashElement(), fKey(), fValue() | |
+ { | |
+ fNext = -1; | |
+ } | |
+ | |
+ inline uint32 Hash() const | |
+ { | |
+ return fKey.GetHashCode(); | |
+ } | |
+ | |
+ inline bool operator==(const OpenHashElement &_element) const | |
+ { | |
+ const Element &element = static_cast<const Element&>(_element); | |
+ return (fKey == element.fKey); | |
+ } | |
+ | |
+ inline void Adopt(Element &element) | |
+ { | |
+ fKey = element.fKey; | |
+ fValue = element.fValue; | |
+ } | |
+ | |
+ Key fKey; | |
+ Value fValue; | |
+}; | |
+ | |
+// HashMap | |
+template<typename Key, typename Value> | |
+class HashMap { | |
+public: | |
+ class Entry { | |
+ public: | |
+ Entry() {} | |
+ Entry(const Key& key, Value value) : key(key), value(value) {} | |
+ | |
+ Key key; | |
+ Value value; | |
+ }; | |
+ | |
+ class Iterator { | |
+ private: | |
+ typedef HashMapElement<Key, Value> Element; | |
+ public: | |
+ Iterator(const Iterator& other) | |
+ : fMap(other.fMap), | |
+ fIndex(other.fIndex), | |
+ fElement(other.fElement), | |
+ fLastElement(other.fElement) | |
+ { | |
+ } | |
+ | |
+ bool HasNext() const | |
+ { | |
+ return fElement; | |
+ } | |
+ | |
+ Entry Next() | |
+ { | |
+ if (!fElement) | |
+ return Entry(); | |
+ Entry result(fElement->fKey, fElement->fValue); | |
+ _FindNext(); | |
+ return result; | |
+ } | |
+ | |
+ Entry Remove() | |
+ { | |
+ if (!fLastElement) | |
+ return Entry(); | |
+ Entry result(fLastElement->fKey, fLastElement->fValue); | |
+ fMap->fTable.Remove(fLastElement, true); | |
+ fLastElement = NULL; | |
+ return result; | |
+ } | |
+ | |
+ Iterator& operator=(const Iterator& other) | |
+ { | |
+ fMap = other.fMap; | |
+ fIndex = other.fIndex; | |
+ fElement = other.fElement; | |
+ fLastElement = other.fLastElement; | |
+ return *this; | |
+ } | |
+ | |
+ private: | |
+ Iterator(const HashMap<Key, Value>* map) | |
+ : fMap(const_cast<HashMap<Key, Value>*>(map)), | |
+ fIndex(0), | |
+ fElement(NULL), | |
+ fLastElement(NULL) | |
+ { | |
+ // find first | |
+ _FindNext(); | |
+ } | |
+ | |
+ void _FindNext() | |
+ { | |
+ fLastElement = fElement; | |
+ if (fElement && fElement->fNext >= 0) { | |
+ fElement = fMap->fTable.ElementAt(fElement->fNext); | |
+ return; | |
+ } | |
+ fElement = NULL; | |
+ int32 arraySize = fMap->fTable.ArraySize(); | |
+ for (; !fElement && fIndex < arraySize; fIndex++) | |
+ fElement = fMap->fTable.FindFirst(fIndex); | |
+ } | |
+ | |
+ private: | |
+ friend class HashMap<Key, Value>; | |
+ | |
+ HashMap<Key, Value>* fMap; | |
+ int32 fIndex; | |
+ Element* fElement; | |
+ Element* fLastElement; | |
+ }; | |
+ | |
+ HashMap(); | |
+ ~HashMap(); | |
+ | |
+ status_t InitCheck() const; | |
+ | |
+ status_t Put(const Key& key, Value value); | |
+ Value Remove(const Key& key); | |
+ void Clear(); | |
+ Value Get(const Key& key) const; | |
+ | |
+ bool ContainsKey(const Key& key) const; | |
+ | |
+ int32 Size() const; | |
+ | |
+ Iterator GetIterator() const; | |
+ | |
+protected: | |
+ typedef HashMapElement<Key, Value> Element; | |
+ friend class Iterator; | |
+ | |
+private: | |
+ Element *_FindElement(const Key& key) const; | |
+ | |
+protected: | |
+ OpenHashElementArray<Element> fElementArray; | |
+ OpenHashTable<Element, OpenHashElementArray<Element> > fTable; | |
+}; | |
+ | |
+// SynchronizedHashMap | |
+template<typename Key, typename Value> | |
+class SynchronizedHashMap : public BLocker { | |
+public: | |
+ typedef struct HashMap<Key, Value>::Entry Entry; | |
+ typedef struct HashMap<Key, Value>::Iterator Iterator; | |
+ | |
+ SynchronizedHashMap() : BLocker("synchronized hash map") {} | |
+ ~SynchronizedHashMap() { Lock(); } | |
+ | |
+ status_t InitCheck() const | |
+ { | |
+ return fMap.InitCheck(); | |
+ } | |
+ | |
+ status_t Put(const Key& key, Value value) | |
+ { | |
+ MapLocker locker(this); | |
+ if (!locker.IsLocked()) | |
+ return B_ERROR; | |
+ return fMap.Put(key, value); | |
+ } | |
+ | |
+ Value Remove(const Key& key) | |
+ { | |
+ MapLocker locker(this); | |
+ if (!locker.IsLocked()) | |
+ return Value(); | |
+ return fMap.Remove(key); | |
+ } | |
+ | |
+ void Clear() | |
+ { | |
+ MapLocker locker(this); | |
+ return fMap.Clear(); | |
+ } | |
+ | |
+ Value Get(const Key& key) const | |
+ { | |
+ const BLocker* lock = this; | |
+ MapLocker locker(const_cast<BLocker*>(lock)); | |
+ if (!locker.IsLocked()) | |
+ return Value(); | |
+ return fMap.Get(key); | |
+ } | |
+ | |
+ bool ContainsKey(const Key& key) const | |
+ { | |
+ const BLocker* lock = this; | |
+ MapLocker locker(const_cast<BLocker*>(lock)); | |
+ if (!locker.IsLocked()) | |
+ return false; | |
+ return fMap.ContainsKey(key); | |
+ } | |
+ | |
+ int32 Size() const | |
+ { | |
+ const BLocker* lock = this; | |
+ MapLocker locker(const_cast<BLocker*>(lock)); | |
+ return fMap.Size(); | |
+ } | |
+ | |
+ Iterator GetIterator() | |
+ { | |
+ return fMap.GetIterator(); | |
+ } | |
+ | |
+ // for debugging only | |
+ const HashMap<Key, Value>& GetUnsynchronizedMap() const { return fMap; } | |
+ HashMap<Key, Value>& GetUnsynchronizedMap() { return fMap; } | |
+ | |
+protected: | |
+ typedef AutoLocker<BLocker> MapLocker; | |
+ | |
+ HashMap<Key, Value> fMap; | |
+}; | |
+ | |
+// HashKey32 | |
+template<typename Value> | |
+struct HashKey32 { | |
+ HashKey32() {} | |
+ HashKey32(const Value& value) : value(value) {} | |
+ | |
+ uint32 GetHashCode() const | |
+ { | |
+ return (uint32)value; | |
+ } | |
+ | |
+ HashKey32<Value> operator=(const HashKey32<Value>& other) | |
+ { | |
+ value = other.value; | |
+ return *this; | |
+ } | |
+ | |
+ bool operator==(const HashKey32<Value>& other) const | |
+ { | |
+ return (value == other.value); | |
+ } | |
+ | |
+ bool operator!=(const HashKey32<Value>& other) const | |
+ { | |
+ return (value != other.value); | |
+ } | |
+ | |
+ Value value; | |
+}; | |
+ | |
+ | |
+// HashKey64 | |
+template<typename Value> | |
+struct HashKey64 { | |
+ HashKey64() {} | |
+ HashKey64(const Value& value) : value(value) {} | |
+ | |
+ uint32 GetHashCode() const | |
+ { | |
+ uint64 v = (uint64)value; | |
+ return (uint32)(v >> 32) ^ (uint32)v; | |
+ } | |
+ | |
+ HashKey64<Value> operator=(const HashKey64<Value>& other) | |
+ { | |
+ value = other.value; | |
+ return *this; | |
+ } | |
+ | |
+ bool operator==(const HashKey64<Value>& other) const | |
+ { | |
+ return (value == other.value); | |
+ } | |
+ | |
+ bool operator!=(const HashKey64<Value>& other) const | |
+ { | |
+ return (value != other.value); | |
+ } | |
+ | |
+ Value value; | |
+}; | |
+ | |
+ | |
+// HashMap | |
+ | |
+// constructor | |
+template<typename Key, typename Value> | |
+HashMap<Key, Value>::HashMap() | |
+ : fElementArray(1000), | |
+ fTable(1000, &fElementArray) | |
+{ | |
+} | |
+ | |
+// destructor | |
+template<typename Key, typename Value> | |
+HashMap<Key, Value>::~HashMap() | |
+{ | |
+} | |
+ | |
+// InitCheck | |
+template<typename Key, typename Value> | |
+status_t | |
+HashMap<Key, Value>::InitCheck() const | |
+{ | |
+ return (fTable.InitCheck() && fElementArray.InitCheck() | |
+ ? B_OK : B_NO_MEMORY); | |
+} | |
+ | |
+// Put | |
+template<typename Key, typename Value> | |
+status_t | |
+HashMap<Key, Value>::Put(const Key& key, Value value) | |
+{ | |
+ Element* element = _FindElement(key); | |
+ if (element) { | |
+ // already contains the key: just set the new value | |
+ element->fValue = value; | |
+ return B_OK; | |
+ } | |
+ // does not contain the key yet: add an element | |
+ element = fTable.Add(key.GetHashCode()); | |
+ if (!element) | |
+ return B_NO_MEMORY; | |
+ element->fKey = key; | |
+ element->fValue = value; | |
+ return B_OK; | |
+} | |
+ | |
+// Remove | |
+template<typename Key, typename Value> | |
+Value | |
+HashMap<Key, Value>::Remove(const Key& key) | |
+{ | |
+ Value value = Value(); | |
+ if (Element* element = _FindElement(key)) { | |
+ value = element->fValue; | |
+ fTable.Remove(element); | |
+ } | |
+ return value; | |
+} | |
+ | |
+// Clear | |
+template<typename Key, typename Value> | |
+void | |
+HashMap<Key, Value>::Clear() | |
+{ | |
+ fTable.RemoveAll(); | |
+} | |
+ | |
+// Get | |
+template<typename Key, typename Value> | |
+Value | |
+HashMap<Key, Value>::Get(const Key& key) const | |
+{ | |
+ if (Element* element = _FindElement(key)) | |
+ return element->fValue; | |
+ return Value(); | |
+} | |
+ | |
+// ContainsKey | |
+template<typename Key, typename Value> | |
+bool | |
+HashMap<Key, Value>::ContainsKey(const Key& key) const | |
+{ | |
+ return _FindElement(key); | |
+} | |
+ | |
+// Size | |
+template<typename Key, typename Value> | |
+int32 | |
+HashMap<Key, Value>::Size() const | |
+{ | |
+ return fTable.CountElements(); | |
+} | |
+ | |
+// GetIterator | |
+template<typename Key, typename Value> | |
+struct HashMap<Key, Value>::Iterator | |
+HashMap<Key, Value>::GetIterator() const | |
+{ | |
+ return Iterator(this); | |
+} | |
+ | |
+// _FindElement | |
+template<typename Key, typename Value> | |
+struct HashMap<Key, Value>::Element * | |
+HashMap<Key, Value>::_FindElement(const Key& key) const | |
+{ | |
+ Element* element = fTable.FindFirst(key.GetHashCode()); | |
+ while (element && element->fKey != key) { | |
+ if (element->fNext >= 0) | |
+ element = fTable.ElementAt(element->fNext); | |
+ else | |
+ element = NULL; | |
+ } | |
+ return element; | |
+} | |
+ | |
+} // namespace BPrivate | |
+ | |
+using BPrivate::HashMap; | |
+ | |
+#endif // HASH_MAP_H | |
Index: haiku/libhaiku/ColumnTypes.h | |
=================================================================== | |
--- haiku/libhaiku/ColumnTypes.h (revision 0) | |
+++ haiku/libhaiku/ColumnTypes.h (revision 0) | |
@@ -0,0 +1,287 @@ | |
+/******************************************************************************* | |
+/ | |
+/ File: ColumnTypes.h | |
+/ | |
+/ Description: Experimental classes that implement particular column/field | |
+/ data types for use in BColumnListView. | |
+/ | |
+/ Copyright 2000+, Be Incorporated, All Rights Reserved | |
+/ | |
+*******************************************************************************/ | |
+ | |
+ | |
+#ifndef _COLUMN_TYPES_H | |
+#define _COLUMN_TYPES_H | |
+ | |
+#include "ColumnListView.h" | |
+#include <String.h> | |
+#include <Font.h> | |
+#include <Bitmap.h> | |
+#include <CheckBox.h> | |
+ | |
+ | |
+//===================================================================== | |
+// Common base-class: a column that draws a standard title at its top. | |
+ | |
+class BTitledColumn : public BColumn | |
+{ | |
+ public: | |
+ BTitledColumn (const char *title, | |
+ float width, | |
+ float minWidth, | |
+ float maxWidth, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual void DrawTitle (BRect rect, | |
+ BView* parent); | |
+ virtual void GetColumnName (BString* into) const; | |
+ | |
+ void DrawString (const char*, | |
+ BView*, | |
+ BRect); | |
+ void SetTitle (const char* title); | |
+ void Title (BString* forTitle) const; // sets the BString arg to be the title | |
+ float FontHeight () const; | |
+ | |
+ private: | |
+ float fFontHeight; | |
+ BString fTitle; | |
+}; | |
+ | |
+ | |
+//===================================================================== | |
+// Field and column classes for strings. | |
+ | |
+class BStringField : public BField | |
+{ | |
+ public: | |
+ BStringField (const char* string); | |
+ | |
+ void SetString (const char* string); | |
+ const char* String () const; | |
+ void SetClippedString (const char* string); | |
+ const char* ClippedString (); | |
+ void SetWidth (float); | |
+ float Width (); | |
+ | |
+ private: | |
+ float fWidth; | |
+ BString fString; | |
+ BString fClippedString; | |
+}; | |
+ | |
+ | |
+//-------------------------------------------------------------------- | |
+ | |
+class BStringColumn : public BTitledColumn | |
+{ | |
+ public: | |
+ BStringColumn (const char *title, | |
+ float width, | |
+ float minWidth, | |
+ float maxWidth, | |
+ uint32 truncate, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual void DrawField (BField* field, | |
+ BRect rect, | |
+ BView* parent); | |
+ virtual int CompareFields (BField* field1, | |
+ BField* field2); | |
+ virtual bool AcceptsField (const BField* field) const; | |
+ | |
+ private: | |
+ uint32 fTruncate; | |
+}; | |
+ | |
+ | |
+//===================================================================== | |
+// Field and column classes for dates. | |
+ | |
+class BDateField : public BField | |
+{ | |
+ public: | |
+ BDateField (time_t* t); | |
+ void SetWidth (float); | |
+ float Width (); | |
+ void SetClippedString (const char*); | |
+ const char* ClippedString (); | |
+ time_t Seconds (); | |
+ time_t UnixTime (); | |
+ | |
+ private: | |
+ struct tm fTime; | |
+ time_t fUnixTime; | |
+ time_t fSeconds; | |
+ BString fClippedString; | |
+ float fWidth; | |
+}; | |
+ | |
+ | |
+//-------------------------------------------------------------------- | |
+ | |
+class BDateColumn : public BTitledColumn | |
+{ | |
+ public: | |
+ BDateColumn (const char* title, | |
+ float width, | |
+ float minWidth, | |
+ float maxWidth, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual void DrawField (BField* field, | |
+ BRect rect, | |
+ BView* parent); | |
+ virtual int CompareFields (BField* field1, | |
+ BField* field2); | |
+ private: | |
+ BString fTitle; | |
+}; | |
+ | |
+ | |
+//===================================================================== | |
+// Field and column classes for numeric sizes. | |
+ | |
+class BSizeField : public BField | |
+{ | |
+ public: | |
+ BSizeField (off_t size); | |
+ void SetSize (off_t); | |
+ off_t Size (); | |
+ | |
+ private: | |
+ off_t fSize; | |
+}; | |
+ | |
+ | |
+//-------------------------------------------------------------------- | |
+ | |
+class BSizeColumn : public BTitledColumn | |
+{ | |
+ public: | |
+ BSizeColumn (const char* title, | |
+ float width, | |
+ float minWidth, | |
+ float maxWidth, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual void DrawField (BField* field, | |
+ BRect rect, | |
+ BView* parent); | |
+ virtual int CompareFields (BField* field1, | |
+ BField* field2); | |
+}; | |
+ | |
+ | |
+//===================================================================== | |
+// Field and column classes for integers. | |
+ | |
+class BIntegerField : public BField | |
+{ | |
+ public: | |
+ BIntegerField (int32 number); | |
+ void SetValue (int32); | |
+ int32 Value (); | |
+ | |
+ private: | |
+ int32 fInteger; | |
+}; | |
+ | |
+ | |
+//-------------------------------------------------------------------- | |
+ | |
+class BIntegerColumn : public BTitledColumn | |
+{ | |
+ public: | |
+ BIntegerColumn (const char* title, | |
+ float width, | |
+ float minWidth, | |
+ float maxWidth, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual void DrawField (BField* field, | |
+ BRect rect, | |
+ BView* parent); | |
+ virtual int CompareFields (BField* field1, | |
+ BField* field2); | |
+}; | |
+ | |
+ | |
+//===================================================================== | |
+// Field and column classes for bitmaps | |
+ | |
+class BBitmapField : public BField | |
+{ | |
+ public: | |
+ BBitmapField (BBitmap* bitmap); | |
+ const BBitmap* Bitmap (); | |
+ void SetBitmap (BBitmap* bitmap); | |
+ | |
+ private: | |
+ BBitmap* fBitmap; | |
+}; | |
+ | |
+ | |
+//-------------------------------------------------------------------- | |
+ | |
+class BBitmapColumn : public BTitledColumn | |
+{ | |
+ public: | |
+ BBitmapColumn (const char* title, | |
+ float width, | |
+ float minWidth, | |
+ float maxWidth, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual void DrawField (BField*field, | |
+ BRect rect, | |
+ BView* parent); | |
+ virtual int CompareFields (BField* field1, BField* field2); | |
+ virtual bool AcceptsField (const BField* field) const; | |
+}; | |
+ | |
+//===================================================================== | |
+// Field and column classes for CheckBox | |
+ | |
+class BCheckBoxField : public BField | |
+{ | |
+ public: | |
+ BCheckBoxField (BCheckBox* checkbox); | |
+ const BCheckBox* CheckBox (); | |
+ void SetCheckBox (BCheckBox* checkbox); | |
+ | |
+ private: | |
+ BCheckBox* fCheckBox; | |
+}; | |
+ | |
+ | |
+//-------------------------------------------------------------------- | |
+ | |
+class BCheckBoxColumn : public BTitledColumn | |
+{ | |
+ public: | |
+ BCheckBoxColumn (const char* title, | |
+ float width, | |
+ float minWidth, | |
+ float maxWidth, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual void DrawField (BField*field, | |
+ BRect rect, | |
+ BView* parent); | |
+ virtual int CompareFields (BField* field1, BField* field2); | |
+ virtual bool AcceptsField (const BField* field) const; | |
+}; | |
+ | |
+ | |
+//===================================================================== | |
+// Column to display BIntegerField objects as a graph. | |
+ | |
+class GraphColumn : public BIntegerColumn | |
+{ | |
+ public: | |
+ GraphColumn (const char* name, | |
+ float width, | |
+ float minWidth, | |
+ float maxWidth, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual void DrawField (BField*field, | |
+ BRect rect, | |
+ BView* parent); | |
+}; | |
+ | |
+#endif | |
+ | |
Index: haiku/libhaiku/ColumnListView.h | |
=================================================================== | |
--- haiku/libhaiku/ColumnListView.h (revision 0) | |
+++ haiku/libhaiku/ColumnListView.h (revision 0) | |
@@ -0,0 +1,409 @@ | |
+/* | |
+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. | |
+*/ | |
+ | |
+/******************************************************************************* | |
+/ | |
+/ File: ColumnListView.h | |
+/ | |
+/ Description: Experimental multi-column list view. | |
+/ | |
+/ Copyright 2000+, Be Incorporated, All Rights Reserved | |
+/ | |
+*******************************************************************************/ | |
+ | |
+ | |
+#ifndef _COLUMN_LIST_VIEW_H | |
+#define _COLUMN_LIST_VIEW_H | |
+ | |
+#include <BeBuild.h> | |
+#include <View.h> | |
+#include <List.h> | |
+#include <Invoker.h> | |
+#include <ListView.h> | |
+ | |
+class BScrollBar; | |
+ | |
+namespace BPrivate { | |
+ | |
+class OutlineView; | |
+class TitleView; | |
+class BRowContainer; | |
+class RecursiveOutlineIterator; | |
+ | |
+} // ns BPrivate | |
+ | |
+class BField; | |
+class BRow; | |
+class BColumn; | |
+class BColumnListView; | |
+ | |
+enum LatchType { | |
+ B_NO_LATCH = 0, | |
+ B_OPEN_LATCH = 1, | |
+ B_PRESSED_LATCH = 2, | |
+ B_CLOSED_LATCH = 3 | |
+}; | |
+ | |
+typedef enum { | |
+ B_ALLOW_COLUMN_NONE = 0, | |
+ B_ALLOW_COLUMN_MOVE = 1, | |
+ B_ALLOW_COLUMN_RESIZE = 2, | |
+ B_ALLOW_COLUMN_POPUP = 4, | |
+ B_ALLOW_COLUMN_REMOVE = 8 | |
+} column_flags; | |
+ | |
+enum ColumnListViewColor { | |
+ B_COLOR_BACKGROUND = 0, | |
+ B_COLOR_TEXT = 1, | |
+ B_COLOR_ROW_DIVIDER = 2, | |
+ B_COLOR_SELECTION = 3, | |
+ B_COLOR_SELECTION_TEXT = 4, | |
+ B_COLOR_NON_FOCUS_SELECTION = 5, | |
+ B_COLOR_EDIT_BACKGROUND = 6, | |
+ B_COLOR_EDIT_TEXT = 7, | |
+ B_COLOR_HEADER_BACKGROUND = 8, | |
+ B_COLOR_HEADER_TEXT = 9, | |
+ B_COLOR_SEPARATOR_LINE = 10, | |
+ B_COLOR_SEPARATOR_BORDER = 11, | |
+ | |
+ B_COLOR_TOTAL = 12 | |
+}; | |
+ | |
+enum ColumnListViewFont { | |
+ B_FONT_ROW = 0, | |
+ B_FONT_HEADER = 1, | |
+ | |
+ B_FONT_TOTAL = 2 | |
+}; | |
+ | |
+ | |
+// A single row/column intersection in the list. | |
+class BField { | |
+public: | |
+ BField(); | |
+ virtual ~BField(); | |
+}; | |
+ | |
+// A single line in the list. Each line contains a BField object | |
+// for each column in the list, associated by their "logical field" | |
+// index. Hierarchies are formed by adding other BRow objects as | |
+// a parent of a row, using the AddRow() function in BColumnListView(). | |
+class BRow { | |
+public: | |
+ BRow(float height = 16.0); | |
+ virtual ~BRow(); | |
+ virtual bool HasLatch() const; | |
+ | |
+ int32 CountFields() const; | |
+ BField* GetField(int32 logicalFieldIndex); | |
+ const BField* GetField(int32 logicalFieldIndex) const; | |
+ void SetField(BField* field, | |
+ int32 logicalFieldIndex); | |
+ | |
+ float Height() const; | |
+ bool IsExpanded() const; | |
+ | |
+private: | |
+ // Blows up into the debugger if the validation fails. | |
+ void ValidateFields() const; | |
+ void ValidateField(const BField* field, | |
+ int32 logicalFieldIndex) const; | |
+private: | |
+ BList fFields; | |
+ BPrivate:: | |
+ BRowContainer* fChildList; | |
+ bool fIsExpanded; | |
+ float fHeight; | |
+ BRow* fNextSelected; | |
+ BRow* fPrevSelected; | |
+ BRow* fParent; | |
+ BColumnListView* fList; | |
+ | |
+ | |
+ friend class BColumnListView; | |
+ friend class BPrivate::RecursiveOutlineIterator; | |
+ friend class BPrivate::OutlineView; | |
+}; | |
+ | |
+// Information about a single column in the list. A column knows | |
+// how to display the BField objects that occur at its location in | |
+// each of the list's rows. See ColumnTypes.h for particular | |
+// subclasses of BField and BColumn that handle common data types. | |
+class BColumn { | |
+public: | |
+ BColumn(float width, float minWidth, | |
+ float maxWidth, | |
+ alignment align = B_ALIGN_LEFT); | |
+ virtual ~BColumn(); | |
+ | |
+ float Width() const; | |
+ void SetWidth(float width); | |
+ float MinWidth() const; | |
+ float MaxWidth() const; | |
+ | |
+ virtual void DrawTitle(BRect rect, BView* targetView); | |
+ virtual void DrawField(BField* field, BRect rect, | |
+ BView* targetView); | |
+ virtual int CompareFields(BField* field1, BField* field2); | |
+ | |
+ virtual void MouseMoved(BColumnListView* parent, BRow* row, | |
+ BField* field, BRect fieldRect, | |
+ BPoint point, uint32 buttons, int32 code); | |
+ virtual void MouseDown(BColumnListView* parent, BRow* row, | |
+ BField* field, BRect fieldRect, | |
+ BPoint point, uint32 buttons); | |
+ virtual void MouseUp(BColumnListView* parent, BRow* row, | |
+ BField* field); | |
+ | |
+ virtual void GetColumnName(BString* into) const; | |
+ virtual float GetPreferredWidth(BField* field, | |
+ BView* parent) const; | |
+ | |
+ bool IsVisible() const; | |
+ void SetVisible(bool); | |
+ | |
+ bool WantsEvents() const; | |
+ void SetWantsEvents(bool); | |
+ | |
+ bool ShowHeading() const; | |
+ void SetShowHeading(bool); | |
+ | |
+ alignment Alignment() const; | |
+ void SetAlignment(alignment); | |
+ | |
+ int32 LogicalFieldNum() const; | |
+ | |
+ /*! | |
+ \param field The BField derivative to validate. | |
+ | |
+ Implement this function on your BColumn derivatives to validate | |
+ BField derivatives that your BColumn will be drawing/manipulating. | |
+ | |
+ This function will be called when BFields are added to the Column, | |
+ use dynamic_cast<> to determine if it is of a kind that your | |
+ BColumn know how ot handle. return false if it is not. | |
+ | |
+ \note The debugger will be called if you return false from here | |
+ with information about what type of BField and BColumn and the | |
+ logical field index where it occured. | |
+ | |
+ \note Do not call the inherited version of this, it just returns | |
+ true; | |
+ */ | |
+ virtual bool AcceptsField(const BField* field) const; | |
+ | |
+private: | |
+ float fWidth; | |
+ float fMinWidth; | |
+ float fMaxWidth; | |
+ bool fVisible; | |
+ int32 fFieldID; | |
+ BColumnListView* fList; | |
+ bool fSortAscending; | |
+ bool fWantsEvents; | |
+ bool fShowHeading; | |
+ alignment fAlignment; | |
+ | |
+ friend class BPrivate::OutlineView; | |
+ friend class BColumnListView; | |
+ friend class BPrivate::TitleView; | |
+}; | |
+ | |
+// The column list view class. | |
+class BColumnListView : public BView, public BInvoker { | |
+public: | |
+ BColumnListView(BRect rect, | |
+ const char* name, uint32 resizingMode, | |
+ uint32 flags, border_style = B_NO_BORDER, | |
+ bool showHorizontalScrollbar = true); | |
+ BColumnListView(const char* name, | |
+ uint32 flags, border_style = B_NO_BORDER, | |
+ bool showHorizontalScrollbar = true); | |
+ virtual ~BColumnListView(); | |
+ | |
+ // Interaction | |
+ virtual bool InitiateDrag(BPoint, bool wasSelected); | |
+ virtual void MessageDropped(BMessage*, BPoint point); | |
+ virtual void ExpandOrCollapse(BRow* row, bool expand); | |
+ virtual status_t Invoke(BMessage* message = NULL); | |
+ virtual void ItemInvoked(); | |
+ virtual void SetInvocationMessage(BMessage* message); | |
+ BMessage* InvocationMessage() const; | |
+ uint32 InvocationCommand() const; | |
+ BRow* FocusRow() const; | |
+ void SetFocusRow(int32 index, bool select = false); | |
+ void SetFocusRow(BRow* row, bool select = false); | |
+ void SetMouseTrackingEnabled(bool); | |
+ | |
+ // Selection | |
+ list_view_type SelectionMode() const; | |
+ void Deselect(BRow* row); | |
+ void AddToSelection(BRow* row); | |
+ void DeselectAll(); | |
+ BRow* CurrentSelection(BRow* lastSelected = 0) const; | |
+ virtual void SelectionChanged(); | |
+ virtual void SetSelectionMessage(BMessage* message); | |
+ BMessage* SelectionMessage(); | |
+ uint32 SelectionCommand() const; | |
+ void SetSelectionMode(list_view_type type); | |
+ // list_view_type is defined in ListView.h. | |
+ | |
+ // Sorting | |
+ void SetSortingEnabled(bool); | |
+ bool SortingEnabled() const; | |
+ void SetSortColumn(BColumn* column, bool add, | |
+ bool ascending); | |
+ void ClearSortColumns(); | |
+ | |
+ // The status view is a little area in the lower left hand corner. | |
+ void AddStatusView(BView* view); | |
+ BView* RemoveStatusView(); | |
+ | |
+ // Column Manipulation | |
+ void AddColumn(BColumn* column, | |
+ int32 logicalFieldIndex); | |
+ void MoveColumn(BColumn* column, int32 index); | |
+ void RemoveColumn(BColumn* column); | |
+ int32 CountColumns() const; | |
+ BColumn* ColumnAt(int32 index) const; | |
+ BColumn* ColumnAt(BPoint point) const; | |
+ void SetColumnVisible(BColumn* column, | |
+ bool isVisible); | |
+ void SetColumnVisible(int32, bool); | |
+ bool IsColumnVisible(int32) const; | |
+ void SetColumnFlags(column_flags flags); | |
+ void ResizeColumnToPreferred(int32 index); | |
+ void ResizeAllColumnsToPreferred(); | |
+ | |
+ // Row manipulation | |
+ const BRow* RowAt(int32 index, BRow *parent = 0) const; | |
+ BRow* RowAt(int32 index, BRow *parent = 0); | |
+ const BRow* RowAt(BPoint) const; | |
+ BRow* RowAt(BPoint); | |
+ bool GetRowRect(const BRow* row, BRect* _rect) const; | |
+ bool FindParent(BRow* row, BRow** _parent, | |
+ bool *_isVisible) const; | |
+ int32 IndexOf(BRow* row); | |
+ int32 CountRows(BRow* parent = 0) const; | |
+ void AddRow(BRow* row, BRow* parent = NULL); | |
+ void AddRow(BRow* row, int32 index, | |
+ BRow* parent = NULL); | |
+ | |
+ void ScrollTo(const BRow* Row); | |
+ void ScrollTo(BPoint point); | |
+ | |
+ // Does not delete row or children at this time. | |
+ // todo: Make delete row and children | |
+ void RemoveRow(BRow* row); | |
+ | |
+ void UpdateRow(BRow* row); | |
+ void Clear(); | |
+ | |
+ // Appearance (DEPRECATED) | |
+ void GetFont(BFont* font) const | |
+ { BView::GetFont(font); } | |
+ virtual void SetFont(const BFont* font, | |
+ uint32 mask = B_FONT_ALL); | |
+ virtual void SetHighColor(rgb_color); | |
+ void SetSelectionColor(rgb_color); | |
+ void SetBackgroundColor(rgb_color); | |
+ void SetEditColor(rgb_color); | |
+ const rgb_color SelectionColor() const; | |
+ const rgb_color BackgroundColor() const; | |
+ const rgb_color EditColor() const; | |
+ | |
+ // Appearance (NEW STYLE) | |
+ void SetColor(ColumnListViewColor colorIndex, | |
+ rgb_color color); | |
+ void SetFont(ColumnListViewFont fontIndex, | |
+ const BFont* font, | |
+ uint32 mask = B_FONT_ALL); | |
+ rgb_color Color(ColumnListViewColor colorIndex) const; | |
+ void GetFont(ColumnListViewFont fontIndex, | |
+ BFont* font) const; | |
+ | |
+ BPoint SuggestTextPosition(const BRow* row, | |
+ const BColumn* column = NULL) const; | |
+ | |
+ void SetLatchWidth(float width); | |
+ float LatchWidth() const; | |
+ virtual void DrawLatch(BView* view, BRect frame, | |
+ LatchType type, BRow* row); | |
+ virtual void MakeFocus(bool isfocus = true); | |
+ void SaveState(BMessage* archive); | |
+ void LoadState(BMessage* archive); | |
+ | |
+ BView* ScrollView() const | |
+ { return (BView*)fOutlineView; } | |
+ void SetEditMode(bool state); | |
+ void Refresh(); | |
+ | |
+ virtual BSize MinSize(); | |
+ virtual BSize PreferredSize(); | |
+ virtual BSize MaxSize(); | |
+ | |
+ | |
+protected: | |
+ virtual void MessageReceived(BMessage* message); | |
+ virtual void KeyDown(const char* bytes, int32 numBytes); | |
+ virtual void AttachedToWindow(); | |
+ virtual void WindowActivated(bool active); | |
+ virtual void Draw(BRect updateRect); | |
+ | |
+ virtual void LayoutInvalidated(bool descendants = false); | |
+ virtual void DoLayout(); | |
+ | |
+private: | |
+ void _Init(); | |
+ void _GetChildViewRects(const BRect& bounds, | |
+ BRect& titleRect, BRect& outlineRect, | |
+ BRect& vScrollBarRect, | |
+ BRect& hScrollBarRect); | |
+ | |
+ rgb_color fColorList[B_COLOR_TOTAL]; | |
+ BPrivate::TitleView* fTitleView; | |
+ BPrivate::OutlineView* fOutlineView; | |
+ BList fColumns; | |
+ BScrollBar* fHorizontalScrollBar; | |
+ BScrollBar* fVerticalScrollBar; | |
+ BList fSortColumns; | |
+ BView* fStatusView; | |
+ BMessage* fSelectionMessage; | |
+ bool fSortingEnabled; | |
+ float fLatchWidth; | |
+ border_style fBorderStyle; | |
+ bool fShowingHorizontalScrollBar; | |
+}; | |
+ | |
+#endif // _COLUMN_LIST_VIEW_H | |
Index: configure.ac | |
=================================================================== | |
--- configure.ac (revision 13780) | |
+++ configure.ac (working copy) | |
@@ -453,7 +453,13 @@ | |
[build_daemon="yes"]) | |
AM_CONDITIONAL([BUILD_DAEMON],[test "x$build_daemon" = "xyes"]) | |
+AC_ARG_ENABLE([haiku], | |
+ [AS_HELP_STRING([--enable-haiku],[build haiku client])], | |
+ [build_haiku=${enableval}], | |
+ [build_haiku="yes"]) | |
+AM_CONDITIONAL([BUILD_HAIKU],[test "x$build_haiku" = "xyes"]) | |
+ | |
if test "x$build_mac" = "xyes" ; then | |
# Make sure the proper Mac SDK is installed | |
if test ! -d /Developer/SDKs/MacOSX10.5.sdk; then | |
@@ -497,6 +503,7 @@ | |
gtk/Makefile | |
gtk/icons/Makefile | |
qt/config.pri | |
+ haiku/Makefile | |
web/Makefile | |
web/images/Makefile | |
web/style/Makefile | |
@@ -531,4 +538,5 @@ | |
Build Mac client: ${build_mac} | |
+ Build Haiku client: ${build_haiku} | |
" | |
Index: third-party/miniupnp/connecthostport.c | |
=================================================================== | |
--- third-party/miniupnp/connecthostport.c (revision 13780) | |
+++ third-party/miniupnp/connecthostport.c (working copy) | |
@@ -24,6 +24,7 @@ | |
#else /* #ifdef _WIN32 */ | |
#include <unistd.h> | |
#include <sys/param.h> | |
+#include <sys/select.h> | |
#include <errno.h> | |
#define closesocket close | |
#include <netdb.h> | |
Index: third-party/miniupnp/miniupnpc.c | |
=================================================================== | |
--- third-party/miniupnp/miniupnpc.c (revision 13780) | |
+++ third-party/miniupnp/miniupnpc.c (working copy) | |
@@ -17,7 +17,7 @@ | |
#endif | |
#endif | |
-#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) | |
+#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(MACOSX) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__HAIKU__) | |
#define HAS_IP_MREQN | |
#endif | |
Index: third-party/miniupnp/portlistingparse.c | |
=================================================================== | |
--- third-party/miniupnp/portlistingparse.c (revision 13780) | |
+++ third-party/miniupnp/portlistingparse.c (working copy) | |
@@ -28,7 +28,7 @@ | |
/* Helper function */ | |
static UNSIGNED_INTEGER | |
-atoui(const char * p, int l) | |
+_atoui(const char * p, int l) | |
{ | |
UNSIGNED_INTEGER r = 0; | |
while(l > 0 && *p) | |
@@ -92,7 +92,7 @@ | |
pm->remoteHost[l] = '\0'; | |
break; | |
case NewExternalPort: | |
- pm->externalPort = (unsigned short)atoui(data, l); | |
+ pm->externalPort = (unsigned short)_atoui(data, l); | |
break; | |
case NewProtocol: | |
if(l > 3) | |
@@ -101,21 +101,21 @@ | |
pm->protocol[l] = '\0'; | |
break; | |
case NewInternalPort: | |
- pm->internalPort = (unsigned short)atoui(data, l); | |
+ pm->internalPort = (unsigned short)_atoui(data, l); | |
break; | |
case NewInternalClient: | |
memcpy(pm->internalClient, data, l); | |
pm->internalClient[l] = '\0'; | |
break; | |
case NewEnabled: | |
- pm->enabled = (unsigned char)atoui(data, l); | |
+ pm->enabled = (unsigned char)_atoui(data, l); | |
break; | |
case NewDescription: | |
memcpy(pm->description, data, l); | |
pm->description[l] = '\0'; | |
break; | |
case NewLeaseTime: | |
- pm->leaseTime = atoui(data, l); | |
+ pm->leaseTime = _atoui(data, l); | |
break; | |
default: | |
break; | |
Index: Makefile.am | |
=================================================================== | |
--- Makefile.am (revision 13780) | |
+++ Makefile.am (working copy) | |
@@ -14,6 +14,9 @@ | |
if BUILD_MAC | |
MAC_DIR = macosx | |
endif | |
+if BUILD_HAIKU | |
+ HAIKU_DIR = haiku | |
+endif | |
SUBDIRS = \ | |
extras \ | |
@@ -24,6 +27,7 @@ | |
$(CLI_DIR) \ | |
$(GTK_DIR) \ | |
$(MAC_DIR) \ | |
+ ${HAIKU_DIR} \ | |
web | |
EXTRA_DIST = \ | |
Index: po/POTFILES.in | |
=================================================================== | |
--- po/POTFILES.in (revision 13780) | |
+++ po/POTFILES.in (working copy) | |
@@ -43,3 +43,5 @@ | |
libtransmission/utils.c | |
libtransmission/variant.c | |
libtransmission/verify.c | |
+# files added by intltool-prepare | |
+qt/transmission-qt.desktop.in | |
Index: libtransmission/platform.c | |
=================================================================== | |
--- libtransmission/platform.c (revision 13780) | |
+++ libtransmission/platform.c (working copy) | |
@@ -10,11 +10,11 @@ | |
* $Id$ | |
*/ | |
-#ifndef WIN32 | |
+#ifndef WIN32 | |
#include <sys/types.h> /* types needed by quota.h */ | |
#ifdef __FreeBSD__ | |
#include <ufs/ufs/quota.h> /* quotactl() */ | |
- #else | |
+ #elif (!defined __HAIKU__) | |
#include <sys/quota.h> /* quotactl() */ | |
#endif | |
#ifdef HAVE_GETMNTENT | |
@@ -22,8 +22,10 @@ | |
#include <paths.h> /* _PATH_MOUNTED */ | |
#else /* BSD derived systems */ | |
#include <sys/param.h> | |
- #include <sys/ucred.h> | |
- #include <sys/mount.h> | |
+ #ifndef __HAIKU__ | |
+ #include <sys/ucred.h> | |
+ #include <sys/mount.h> | |
+ #endif | |
#endif | |
#endif | |
@@ -721,7 +723,7 @@ | |
**** | |
***/ | |
-#ifndef WIN32 | |
+#if !(defined WIN32 || defined __HAIKU__) | |
static char * | |
getdev (const char * path) | |
{ | |
@@ -914,7 +916,7 @@ | |
{ | |
int64_t ret=-1; | |
-#ifndef WIN32 | |
+#if !(defined WIN32 || defined __HAIKU__) | |
/* save device for future use */ | |
if (!*device) | |
@@ -974,12 +976,16 @@ | |
int64_t | |
tr_getFreeSpace (const char * path, char * device, char * fstype) | |
{ | |
+#ifdef __HAIKU__ | |
+ return -1; | |
+#else | |
int64_t i = tr_getQuotaFreeSpace (path, device, fstype); | |
if (i < 0) | |
i = tr_getDiskFreeSpace (path); | |
return i; | |
+#endif | |
} | |
/*** |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment