Skip to content

Instantly share code, notes, and snippets.

@richienyhus
Created March 17, 2013 16:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richienyhus/5182192 to your computer and use it in GitHub Desktop.
Save richienyhus/5182192 to your computer and use it in GitHub Desktop.
GUI for Transmission on Haiku
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