the first steps towards Icon-O-Matic

* added a framework with many classes that I think will be useful
* currently, the StateView and Manipulator interface are used to
  allow editing a single VectorPath object, nothing more... the
  CommandStack framework is also used to support Undo/Redo


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@17822 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2006-06-13 13:43:13 +00:00
parent ddcab3b57d
commit 128277c969
105 changed files with 15735 additions and 0 deletions
+1
View File
@@ -13,6 +13,7 @@ SubInclude HAIKU_TOP src apps diskprobe ;
SubInclude HAIKU_TOP src apps expander ;
SubInclude HAIKU_TOP src apps fontdemo ;
SubInclude HAIKU_TOP src apps glteapot ;
SubInclude HAIKU_TOP src apps icon-o-matic ;
SubInclude HAIKU_TOP src apps installer ;
SubInclude HAIKU_TOP src apps magnify ;
SubInclude HAIKU_TOP src apps mediaplayer ;
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "CanvasView.h"
#include "CommandStack.h"
CanvasView::CanvasView(BRect frame)
: StateView(frame, "canvas view", B_FOLLOW_ALL, B_WILL_DRAW)
{
#if __HAIKU__
SetFlags(Flags() | B_SUBPIXEL_PRECISE);
#endif // __HAIKU__
}
CanvasView::~CanvasView()
{
}
bool
CanvasView::_HandleKeyDown(uint32 key, uint32 modifiers)
{
switch (key) {
case 'z':
case 'y':
if (modifiers & B_SHIFT_KEY)
CommandStack()->Redo();
else
CommandStack()->Undo();
break;
default:
return StateView::HandleKeyDown(key, modifiers);
}
return true;
}
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef CANVAS_VIEW_H
#define CANVAS_VIEW_H
#include "StateView.h"
class CanvasView : public StateView {
public:
CanvasView(BRect frame);
virtual ~CanvasView();
protected:
// StateView interface
virtual bool _HandleKeyDown(uint32 key, uint32 modifiers);
private:
};
#endif // CANVAS_VIEW_H
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "IconEditorApp.h"
#include <new>
#include <stdio.h>
#include <string.h>
#include "Document.h"
#include "MainWindow.h"
using std::nothrow;
// constructor
IconEditorApp::IconEditorApp()
: BApplication("application/x-vnd.Haiku-Icon-O-Matic"),
fMainWindow(NULL),
fDocument(new Document("test"))
{
}
// destructor
IconEditorApp::~IconEditorApp()
{
}
// #pragma mark -
// QuitRequested
bool
IconEditorApp::QuitRequested()
{
// TODO: ask main window if quitting is ok
fMainWindow->Lock();
fMainWindow->Quit();
fMainWindow = NULL;
return true;
}
// MessageReceived
void
IconEditorApp::MessageReceived(BMessage* message)
{
switch (message->what) {
// case MSG_OPEN:
// break;
default:
BApplication::MessageReceived(message);
break;
}
}
// ReadyToRun
void
IconEditorApp::ReadyToRun()
{
fMainWindow = new MainWindow(this, fDocument);
fMainWindow->Show();
}
+34
View File
@@ -0,0 +1,34 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef ICON_EDITOR_APP_H
#define ICON_EDITOR_APP_H
#include <Application.h>
class Document;
class MainWindow;
class IconEditorApp : public BApplication {
public:
IconEditorApp();
virtual ~IconEditorApp();
// BApplication interface
virtual bool QuitRequested();
virtual void MessageReceived(BMessage* message);
virtual void ReadyToRun();
// IconEditorApp
private:
MainWindow* fMainWindow;
Document* fDocument;
};
#endif // ICON_EDITOR_APP_H
+79
View File
@@ -0,0 +1,79 @@
SubDir HAIKU_TOP src apps icon-o-matic ;
SetSubDirSupportedPlatformsBeOSCompatible ;
AddSubDirSupportedPlatforms libbe_test ;
# source directories
local sourceDirs =
document
generic
generic/command
generic/gui
generic/gui/panel
generic/gui/popup_control
generic/gui/scrollview
generic/gui/stateview
generic/listener
generic/selection
generic/support
shape
shape/commands
;
local sourceDir ;
for sourceDir in $(sourceDirs) {
SEARCH_SOURCE += [ FDirName $(HAIKU_TOP) src apps icon-o-matic $(sourceDir) ] ;
}
# system headers
UseLibraryHeaders agg ;
Application Icon-O-Matic :
#document
Document.cpp
#generic/command
Command.cpp
CommandStack.cpp
Selectable.cpp
Selection.cpp
#generic/gui
#generic/gui/panel
#generic/gui/popup_control
#generic/gui/scrollview
#generic/gui/stateview
Manipulator.cpp
MultipleManipulatorState.cpp
StateView.cpp
ViewState.cpp
#generic/listener
Observable.cpp
Observer.cpp
#generic/selection
#generic/support
RWLocker.cpp
support.cpp
#shape
PathManipulator.cpp
VectorPath.cpp
#shape/commands
AddPointCommand.cpp
ChangePointCommand.cpp
InsertPointCommand.cpp
PathCommand.cpp
RemovePointsCommand.cpp
#
CanvasView.cpp
IconEditorApp.cpp
main.cpp
MainWindow.cpp
: be libagg.a
;
# also install in app_server test environment
if ( $(TARGET_PLATFORM) = libbe_test ) {
HaikuInstall install-test-apps : $(HAIKU_APP_TEST_DIR) : Icon-O-Matic
: tests!apps ;
}
+97
View File
@@ -0,0 +1,97 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "MainWindow.h"
#include <stdio.h>
#include <Message.h>
#include "Document.h"
#include "CanvasView.h"
#include "IconEditorApp.h"
// TODO: just for testing
#include "MultipleManipulatorState.h"
#include "PathManipulator.h"
#include "VectorPath.h"
// constructor
MainWindow::MainWindow(IconEditorApp* app, Document* document)
: BWindow(BRect(50.0, 50.0, 689, 529), "Icon-O-Matic",
B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS),
fApp(app),
fDocument(document),
fCanvasView(NULL)
{
_Init();
}
// destructor
MainWindow::~MainWindow()
{
}
// #pragma mark -
// MessageReceived
void
MainWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
default:
BWindow::MessageReceived(message);
}
}
// QuitRequested
bool
MainWindow::QuitRequested()
{
// forward this to app but return "false" in order
// to have a single code path for quitting
be_app->PostMessage(B_QUIT_REQUESTED);
return false;
}
// #pragma mark -
// _Init
void
MainWindow::_Init()
{
// create the GUI
BView* topView = _CreateGUI(Bounds());
AddChild(topView);
fCanvasView->SetCatchAllEvents(true);
fCanvasView->SetCommandStack(fDocument->CommandStack());
// fCanvasView->SetSelection(fDocument->Selection());
// TODO: for testing only:
MultipleManipulatorState* state = new MultipleManipulatorState(fCanvasView);
fCanvasView->SetState(state);
VectorPath* path = new VectorPath();
PathManipulator* pathManipulator = new PathManipulator(path);
state->AddManipulator(pathManipulator);
// ----
}
// _CreateGUI
BView*
MainWindow::_CreateGUI(BRect bounds)
{
fCanvasView = new CanvasView(bounds);
return fCanvasView;
}
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef MAIN_WINDOW_H
#define MAIN_WINDOW_H
#include <Window.h>
class CanvasView;
class Document;
class IconEditorApp;
class MainWindow : public BWindow {
public:
MainWindow(IconEditorApp* app,
Document* document);
virtual ~MainWindow();
// BWindow interface
virtual void MessageReceived(BMessage* message);
virtual bool QuitRequested();
private:
void _Init();
BView* _CreateGUI(BRect frame);
IconEditorApp* fApp;
Document* fDocument;
CanvasView* fCanvasView;
};
#endif // MAIN_WINDOW_H
+33
View File
@@ -0,0 +1,33 @@
--------- random thoughts
* works a bit like WonderBrush
* list of vector path objects
* tree of object instances, such that the same vector path
object can be visible at different locations with different fill styles
* instances of paths can have additional vector transformers
* "add points" mode is problematic when having multiple manipulators for
different paths showing at the same time...
--------- icon format
* 192 "built-in" (pre-defined) style definitions
* up to 64 additional style definitions per document
* solid colors and different types of gradients
* uint8 precision for coordinates on a 64x64 virtual pixel grid
(* removal (freezing) of transformations at export time
not sure about this one, it would remove the possiblity
to store vector path only once for referenced objects)
* referencing of fill style by uint8 id
* IFF type chunk format
--------- rendering
* compound shape single pass rendering (if possible)
* auto hinting (aligning to pixels) of marked shapes
@@ -0,0 +1,62 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Document.h"
#include <new>
#include <Entry.h>
#include "CommandStack.h"
#include "Selection.h"
using std::nothrow;
// constructor
Document::Document(const char* name)
: RWLocker("document rw lock"),
fCommandStack(new (nothrow) ::CommandStack()),
fSelection(new (nothrow) ::Selection()),
fRef(NULL)
{
SetName(name);
}
// destructor
Document::~Document()
{
delete fCommandStack;
delete fSelection;
delete fRef;
}
// SetName
void
Document::SetName(const char* name)
{
fName = name;
}
// Name
const char*
Document::Name() const
{
return fName.String();
}
// SetRef
void
Document::SetRef(const entry_ref& ref)
{
if (!fRef)
fRef = new (nothrow) entry_ref(ref);
else
*fRef = ref;
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef DOCUMENT_H
#define DOCUMENT_H
#include <String.h>
#include "RWLocker.h"
struct entry_ref;
class CommandStack;
class Selection;
class Document : public RWLocker {
public:
Document(const char* name = NULL);
virtual ~Document();
::CommandStack* CommandStack() const
{ return fCommandStack; }
::Selection* Selection() const
{ return fSelection; }
void SetName(const char* name);
const char* Name() const;
void SetRef(const entry_ref& ref);
const entry_ref* Ref() const
{ return fRef; }
private:
::CommandStack* fCommandStack;
::Selection* fSelection;
BString fName;
entry_ref* fRef;
};
#endif // DOCUMENT_H
@@ -0,0 +1,84 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Command.h"
#include <stdio.h>
#include <OS.h>
// constructor
Command::Command()
: fTimeStamp(system_time())
{
}
// destructor
Command::~Command()
{
}
// InitCheck
status_t
Command::InitCheck()
{
return B_NO_INIT;
}
// Perform
status_t
Command::Perform()
{
return B_ERROR;
}
// Undo
status_t
Command::Undo()
{
return B_ERROR;
}
// Redo
status_t
Command::Redo()
{
return Perform();
}
// GetName
void
Command::GetName(BString& name)
{
name << "Name of action goes here.";
}
// CombineWithNext
bool
Command::CombineWithNext(const Command* next)
{
return false;
}
// CombineWithPrevious
bool
Command::CombineWithPrevious(const Command* previous)
{
return false;
}
// _GetString
const char*
Command::_GetString(uint32 key, const char* defaultString) const
{
// if (LanguageManager* manager = LanguageManager::Default())
// return manager->GetString(key, defaultString);
// else
return defaultString;
}
@@ -0,0 +1,40 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef COMMAND_H
#define COMMAND_H
#include <SupportDefs.h>
#include <String.h>
class BString;
class Command {
public:
Command();
virtual ~Command();
virtual status_t InitCheck();
virtual status_t Perform();
virtual status_t Undo();
virtual status_t Redo();
virtual void GetName(BString& name);
virtual bool CombineWithNext(const Command* next);
virtual bool CombineWithPrevious(const Command* previous);
protected:
const char* _GetString(uint32 key,
const char* defaultString) const;
bigtime_t fTimeStamp;
};
#endif // COMMAND_H
@@ -0,0 +1,223 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "CommandStack.h"
#include <stdio.h>
#include <string.h>
#include <Locker.h>
#include <String.h>
#include "Command.h"
// constructor
CommandStack::CommandStack()
: BLocker("history"),
Observable(),
fSavedCommand(NULL)
{
}
// destructor
CommandStack::~CommandStack()
{
Clear();
}
// Perform
status_t
CommandStack::Perform(Command* command)
{
status_t ret = command ? B_OK : B_BAD_VALUE;
if (Lock()) {
if (ret == B_OK)
ret = command->InitCheck();
if (ret == B_OK)
ret = command->Perform();
if (ret == B_OK)
ret = AddCommand(command);
if (ret != B_OK) {
// no one else feels responsible...
delete command;
}
Unlock();
}
return ret;
}
// AddCommand
status_t
CommandStack::AddCommand(Command* command)
{
status_t status = B_ERROR;
if (Lock()) {
if (command && (status = command->InitCheck()) == B_OK) {
// try to collapse commands to a single command
bool add = true;
if (!fUndoHistory.empty()) {
if (Command* top = fUndoHistory.top()) {
if (top->CombineWithNext(command)) {
add = false;
delete command;
} else if (command->CombineWithPrevious(top)) {
fUndoHistory.pop();
delete top;
}
}
}
if (add)
fUndoHistory.push(command);
// the redo stack needs to be empty
// as soon as an command was added (also in case of collapsing)
while (!fRedoHistory.empty()) {
delete fRedoHistory.top();
fRedoHistory.pop();
}
}
Unlock();
}
Notify();
return status;
}
// Undo
status_t
CommandStack::Undo()
{
status_t status = B_ERROR;
if (Lock()) {
if (!fUndoHistory.empty()) {
Command* command = fUndoHistory.top();
fUndoHistory.pop();
status = command->Undo();
if (status == B_OK)
fRedoHistory.push(command);
else
fUndoHistory.push(command);
}
Unlock();
}
Notify();
return status;
}
// Redo
status_t
CommandStack::Redo()
{
status_t status = B_ERROR;
if (Lock()) {
if (!fRedoHistory.empty()) {
Command* command = fRedoHistory.top();
fRedoHistory.pop();
status = command->Redo();
if (status == B_OK)
fUndoHistory.push(command);
else
fRedoHistory.push(command);
}
Unlock();
}
Notify();
return status;
}
// UndoName
bool
CommandStack::GetUndoName(BString& name)
{
bool success = false;
if (Lock()) {
if (!fUndoHistory.empty()) {
name << " ";
fUndoHistory.top()->GetName(name);
success = true;
}
Unlock();
}
return success;
}
// RedoName
bool
CommandStack::GetRedoName(BString& name)
{
bool success = false;
if (Lock()) {
if (!fRedoHistory.empty()) {
name << " ";
fRedoHistory.top()->GetName(name);
success = true;
}
Unlock();
}
return success;
}
// Clear
void
CommandStack::Clear()
{
if (Lock()) {
while (!fUndoHistory.empty()) {
delete fUndoHistory.top();
fUndoHistory.pop();
}
while (!fRedoHistory.empty()) {
delete fRedoHistory.top();
fRedoHistory.pop();
}
Unlock();
}
Notify();
}
// Save
void
CommandStack::Save()
{
if (Lock()) {
if (!fUndoHistory.empty())
fSavedCommand = fUndoHistory.top();
Unlock();
}
Notify();
}
// IsSaved
bool
CommandStack::IsSaved()
{
bool saved = false;
if (Lock()) {
saved = fUndoHistory.empty();
if (fSavedCommand && !saved) {
if (fSavedCommand == fUndoHistory.top())
saved = true;
}
Unlock();
}
return saved;
}
@@ -0,0 +1,49 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef COMMAND_STACK_H
#define COMMAND_STACK_H
#include <stack.h>
#include <Locker.h>
#include "Observable.h"
class BString;
class Command;
class CommandStack : public BLocker,
public Observable {
public:
CommandStack();
virtual ~CommandStack();
status_t Perform(Command* command);
status_t AddCommand(Command* command);
status_t Undo();
status_t Redo();
bool GetUndoName(BString& name);
bool GetRedoName(BString& name);
void Clear();
void Save();
bool IsSaved();
private:
typedef stack<Command*> command_stack;
command_stack fUndoHistory;
command_stack fRedoHistory;
Command* fSavedCommand;
};
#endif // COMMAND_STACK_H
@@ -0,0 +1,96 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "CompoundCommand.h"
#include <stdio.h>
// constructor
CompoundCommand::CompoundCommand(Command** commands,
int32 count,
const char* name,
int32 nameIndex)
: Command(),
fCommands(commands),
fCount(count),
fName(name),
fNameIndex(nameIndex)
{
}
// destructor
CompoundCommand::~CompoundCommand()
{
for (int32 i = 0; i < fCount; i++)
delete fCommands[i];
delete[] fCommands;
}
// InitCheck
status_t
CompoundCommand::InitCheck()
{
status_t status = fCommands && fCount > 0 ? B_OK : B_BAD_VALUE;
return status;
}
// Perform
status_t
CompoundCommand::Perform()
{
status_t status = InitCheck();
if (status >= B_OK) {
int32 i = 0;
for (; i < fCount; i++) {
if (fCommands[i])
status = fCommands[i]->Perform();
if (status < B_OK)
break;
}
/* if (status < B_OK) {
// roll back
i--;
for (; i >= 0; i--) {
if (fCommands[i])
fCommands[i]->Undo();
}
}*/
}
return status;
}
// Undo
status_t
CompoundCommand::Undo()
{
status_t status = InitCheck();
if (status >= B_OK) {
int32 i = fCount - 1;
for (; i >= 0; i--) {
if (fCommands[i])
status = fCommands[i]->Undo();
if (status < B_OK)
break;
}
}
return status;
}
// Redo
status_t
CompoundCommand::Redo()
{
return Perform();
}
// GetName
void
CompoundCommand::GetName(BString& name)
{
name << _GetString(fNameIndex, fName.String());
}
@@ -0,0 +1,38 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef COMPOUND_ACTION_H
#define COMPOUND_ACTION_H
#include "Command.h"
class CompoundCommand : public Command {
public:
CompoundCommand(Command** commands,
int32 count,
const char* name,
int32 nameIndex);
virtual ~CompoundCommand();
virtual status_t InitCheck();
virtual status_t Perform();
virtual status_t Undo();
virtual status_t Redo();
virtual void GetName(BString& name);
private:
Command** fCommands;
int32 fCount;
BString fName;
int32 fNameIndex;
};
#endif // COMPOUND_ACTION_H
+124
View File
@@ -0,0 +1,124 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Group.h"
#include <stdio.h>
// constructor
Group::Group(BRect frame, const char* name, orientation direction)
: BView(frame, name, B_FOLLOW_ALL, B_FRAME_EVENTS),
fOrientation(direction),
fInset(4.0),
fSpacing(0.0)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
// destructor
Group::~Group()
{
}
// AttachedToWindow
void
Group::AttachedToWindow()
{
// trigger a layout
FrameResized(Bounds().Width(), Bounds().Height());
}
// FrameResized
void
Group::FrameResized(float width, float height)
{
// layout controls
BRect r(Bounds());
r.InsetBy(fInset, fInset);
_LayoutControls(r);
}
// GetPreferredSize
void
Group::GetPreferredSize(float* width, float* height)
{
BRect r(_MinFrame());
if (width)
*width = r.Width();
if (height)
*height = r.Height();
}
// #pragma mark -
// _LayoutControls
void
Group::_LayoutControls(BRect r) const
{
if (fOrientation == B_HORIZONTAL) {
r.left -= fSpacing;
for (int32 i = 0; BView* child = ChildAt(i); i++) {
r.right = r.left + child->Bounds().Width() + 2 * fSpacing;
_LayoutControl(child, r);
r.left = r.right + 1.0 - fSpacing;
}
} else {
r.top -= fSpacing;
for (int32 i = 0; BView* child = ChildAt(i); i++) {
r.bottom = r.top + child->Bounds().Height() + 2 * fSpacing;
_LayoutControl(child, r);
r.top = r.bottom + 1.0 - fSpacing;
}
}
}
// _MinFrame
BRect
Group::_MinFrame() const
{
float minWidth = fInset * 2.0;
float minHeight = fInset * 2.0;
if (fOrientation == B_HORIZONTAL) {
minWidth += fSpacing;
for (int32 i = 0; BView* child = ChildAt(i); i++) {
minWidth += child->Bounds().Width() + fSpacing;
minHeight = max_c(minHeight,
child->Bounds().Height() + fInset * 2.0);
}
} else {
minHeight += fSpacing;
for (int32 i = 0; BView* child = ChildAt(i); i++) {
minHeight += child->Bounds().Height() + fSpacing;
minWidth = max_c(minWidth,
child->Bounds().Width() + fInset * 2.0);
}
}
return BRect(0.0, 0.0, minWidth - 1.0, minHeight - 1.0);
}
// _LayoutControl
void
Group::_LayoutControl(BView* view, BRect frame,
bool resizeWidth, bool resizeHeight) const
{
if (!resizeHeight)
// center vertically
frame.top = (frame.top + frame.bottom) / 2.0 - view->Bounds().Height() / 2.0;
if (!resizeWidth)
// center horizontally
frame.left = (frame.left + frame.right) / 2.0 - view->Bounds().Width() / 2.0;
view->MoveTo(frame.LeftTop());
float width = resizeWidth ? frame.Width() : view->Bounds().Width();
float height = resizeHeight ? frame.Height() : view->Bounds().Height();
if (resizeWidth || resizeHeight)
view->ResizeTo(width, height);
}
+41
View File
@@ -0,0 +1,41 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef GROUP_H
#define GROUP_H
#include <View.h>
class Group : public BView {
public:
Group(BRect frame,
const char* name,
orientation direction = B_HORIZONTAL);
virtual ~Group();
// BView interface
virtual void AttachedToWindow();
virtual void FrameResized(float width, float height);
virtual void GetPreferredSize(float* width, float* height);
// TODO: allow setting inset and spacing
private:
void _LayoutControls(BRect frame) const;
BRect _MinFrame() const;
void _LayoutControl(BView* view,
BRect frame,
bool resizeWidth = false,
bool resizeHeight = false) const;
orientation fOrientation;
float fInset;
float fSpacing;
};
#endif // GROUP_H
@@ -0,0 +1,781 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "IconButton.h"
#include <new>
#include <stdio.h>
#include <Application.h>
#include <Bitmap.h>
#include <Control.h>
#include <Entry.h>
#include <Looper.h>
#include <Message.h>
#include <Mime.h>
#include <Path.h>
#include <Region.h>
#include <Roster.h>
#include <TranslationUtils.h>
#include <Window.h>
using std::nothrow;
// constructor
IconButton::IconButton(const char* name, uint32 id, const char* label,
BMessage* message, BHandler* target)
: BView(BRect(0.0, 0.0, 10.0, 10.0), name, B_FOLLOW_NONE, B_WILL_DRAW),
BInvoker(message, target),
fButtonState(STATE_ENABLED),
fID(id),
fNormalBitmap(NULL),
fDisabledBitmap(NULL),
fClickedBitmap(NULL),
fDisabledClickedBitmap(NULL),
fLabel(label),
fTargetCache(target)
{
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetViewColor(B_TRANSPARENT_32_BIT);
}
// destructor
IconButton::~IconButton()
{
_DeleteBitmaps();
}
// MessageReceived
void
IconButton::MessageReceived(BMessage* message)
{
switch (message->what) {
default:
BView::MessageReceived(message);
break;
}
}
// AttachedToWindow
void
IconButton::AttachedToWindow()
{
SetTarget(fTargetCache);
if (!Target()) {
SetTarget(Window());
}
}
// Draw
void
IconButton::Draw(BRect area)
{
rgb_color background = LowColor();
if (MView* parent = dynamic_cast<MView*>(Parent()))
background = parent->getcolor();
rgb_color lightShadow, shadow, darkShadow, light;
BRect r(Bounds());
BBitmap* bitmap = fNormalBitmap;
// adjust colors and bitmap according to flags
if (IsEnabled()) {
lightShadow = tint_color(background, B_DARKEN_1_TINT);
shadow = tint_color(background, B_DARKEN_2_TINT);
darkShadow = tint_color(background, B_DARKEN_4_TINT);
light = tint_color(background, B_LIGHTEN_MAX_TINT);
SetHighColor(0, 0, 0, 255);
} else {
lightShadow = tint_color(background, 1.11);
shadow = tint_color(background, B_DARKEN_1_TINT);
darkShadow = tint_color(background, B_DARKEN_2_TINT);
light = tint_color(background, B_LIGHTEN_2_TINT);
bitmap = fDisabledBitmap;
SetHighColor(tint_color(background, B_DISABLED_LABEL_TINT));
}
if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED)) {
if (IsEnabled()) {
// background = tint_color(background, B_DARKEN_2_TINT);
// background = tint_color(background, B_LIGHTEN_1_TINT);
background = tint_color(background, B_DARKEN_1_TINT);
bitmap = fClickedBitmap;
} else {
// background = tint_color(background, B_DARKEN_1_TINT);
// background = tint_color(background, (B_NO_TINT + B_LIGHTEN_1_TINT) / 2.0);
background = tint_color(background, (B_NO_TINT + B_DARKEN_1_TINT) / 2.0);
bitmap = fDisabledClickedBitmap;
}
// background
SetLowColor(background);
r.InsetBy(2.0, 2.0);
StrokeLine(r.LeftBottom(), r.LeftTop(), B_SOLID_LOW);
StrokeLine(r.LeftTop(), r.RightTop(), B_SOLID_LOW);
r.InsetBy(-2.0, -2.0);
}
// draw frame only if tracking
if (DrawBorder()) {
if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED))
DrawPressedBorder(r, background, shadow, darkShadow, lightShadow, light);
else
DrawNormalBorder(r, background, shadow, darkShadow, lightShadow, light);
r.InsetBy(2.0, 2.0);
} else
_DrawFrame(r, background, background, background, background);
float width = Bounds().Width();
float height = Bounds().Height();
// bitmap
BRegion originalClippingRegion;
if (bitmap && bitmap->IsValid()) {
float x = floorf((width - bitmap->Bounds().Width()) / 2.0 + 0.5);
float y = floorf((height - bitmap->Bounds().Height()) / 2.0 + 0.5);
BPoint point(x, y);
if (_HasFlags(STATE_PRESSED) || _HasFlags(STATE_FORCE_PRESSED))
point += BPoint(1.0, 1.0);
if (bitmap->ColorSpace() == B_RGBA32 || bitmap->ColorSpace() == B_RGBA32_BIG) {
FillRect(r, B_SOLID_LOW);
SetDrawingMode(B_OP_ALPHA);
SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
}
DrawBitmap(bitmap, point);
// constrain clipping region
BRegion region= originalClippingRegion;
GetClippingRegion(&region);
region.Exclude(bitmap->Bounds().OffsetByCopy(point));
ConstrainClippingRegion(&region);
}
// background
SetDrawingMode(B_OP_COPY);
FillRect(r, B_SOLID_LOW);
ConstrainClippingRegion(&originalClippingRegion);
// label
if (fLabel.CountChars() > 0) {
SetDrawingMode(B_OP_COPY);
font_height fh;
GetFontHeight(&fh);
float y = Bounds().bottom - 4.0;
y -= fh.descent;
float x = (width - StringWidth(fLabel.String())) / 2.0;
DrawString(fLabel.String(), BPoint(x, y));
}
}
// MouseDown
void
IconButton::MouseDown(BPoint where)
{
if (IsValid()) {
if (_HasFlags(STATE_ENABLED)/* && !_HasFlags(STATE_FORCE_PRESSED)*/) {
if (Bounds().Contains(where)) {
SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
_AddFlags(STATE_PRESSED | STATE_TRACKING);
} else {
_ClearFlags(STATE_PRESSED | STATE_TRACKING);
}
}
}
}
// MouseUp
void
IconButton::MouseUp(BPoint where)
{
if (IsValid()) {
// if (!_HasFlags(STATE_FORCE_PRESSED)) {
if (_HasFlags(STATE_ENABLED) && _HasFlags(STATE_PRESSED) && Bounds().Contains(where))
Invoke();
else if (Bounds().Contains(where))
_AddFlags(STATE_INSIDE);
_ClearFlags(STATE_PRESSED | STATE_TRACKING);
// }
}
}
// MouseMoved
void
IconButton::MouseMoved(BPoint where, uint32 transit, const BMessage* message)
{
if (IsValid()) {
uint32 buttons = 0;
Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
// catch a mouse up event that we might have missed
if (!buttons && _HasFlags(STATE_PRESSED)) {
MouseUp(where);
return;
}
if (buttons && !_HasFlags(STATE_TRACKING))
return;
if ((transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW)
&& _HasFlags(STATE_ENABLED))
_AddFlags(STATE_INSIDE);
else
_ClearFlags(STATE_INSIDE);
if (_HasFlags(STATE_TRACKING)) {
if (Bounds().Contains(where))
_AddFlags(STATE_PRESSED);
else
_ClearFlags(STATE_PRESSED);
}
}
}
// GetPreferredSize
void
IconButton::GetPreferredSize(float* width, float* height)
{
layoutprefs();
if (width)
*width = mpm.mini.x;
if (height)
*height = mpm.mini.y;
}
// Invoke
status_t
IconButton::Invoke(BMessage* message)
{
if (!message)
message = Message();
if (message) {
BMessage clone(*message);
clone.AddInt64("be:when", system_time());
clone.AddPointer("be:source", (BView*)this);
clone.AddInt32("be:value", Value());
clone.AddInt32("id", ID());
return BInvoker::Invoke(&clone);
}
return BInvoker::Invoke(message);
}
#define MIN_SPACE 15.0
// layoutprefs
minimax
IconButton::layoutprefs()
{
float minWidth = 0.0;
float minHeight = 0.0;
if (IsValid()) {
minWidth += fNormalBitmap->Bounds().IntegerWidth() + 1.0;
minHeight += fNormalBitmap->Bounds().IntegerHeight() + 1.0;
} else {
minWidth += MIN_SPACE;
minHeight += MIN_SPACE;
}
if (minWidth < MIN_SPACE)
minWidth = MIN_SPACE;
if (minHeight < MIN_SPACE)
minHeight = MIN_SPACE;
if (fLabel.CountChars() > 0) {
font_height fh;
GetFontHeight(&fh);
minHeight += ceilf(fh.ascent + fh.descent) + 4.0;
minWidth += StringWidth(fLabel.String()) + 4.0;
}
mpm.mini.x = minWidth + 4.0;
// mpm.maxi.x = 10000.0 + 4.0;
mpm.maxi.x = minWidth + 4.0;
mpm.mini.y = minHeight + 4.0;
// mpm.maxi.y = 10000.0 + 4.0;
mpm.maxi.y = minHeight + 4.0;
mpm.weight = 0.0;
return mpm;
}
// layout
BRect
IconButton::layout(BRect rect)
{
MoveTo(rect.LeftTop());
ResizeTo(rect.Width(), rect.Height());
return Frame();
}
// SetPressed
void
IconButton::SetPressed(bool pressed)
{
if (pressed)
_AddFlags(STATE_FORCE_PRESSED);
else
_ClearFlags(STATE_FORCE_PRESSED);
}
// IsPressed
bool
IconButton::IsPressed() const
{
return _HasFlags(STATE_FORCE_PRESSED);
}
// SetIcon
status_t
IconButton::SetIcon(const char* pathToBitmap)
{
status_t status = B_BAD_VALUE;
if (pathToBitmap) {
BBitmap* fileBitmap = NULL;
// try to load bitmap from either relative or absolute path
BEntry entry(pathToBitmap, true);
if (!entry.Exists()) {
app_info info;
status = be_app->GetAppInfo(&info);
if (status == B_OK) {
BEntry app_entry(&info.ref, true);
BPath path;
app_entry.GetPath(&path);
status = path.InitCheck();
if (status == B_OK) {
status = path.GetParent(&path);
if (status == B_OK) {
status = path.Append(pathToBitmap, true);
if (status == B_OK)
fileBitmap = BTranslationUtils::GetBitmap(path.Path());
else
printf("IconButton::SetIcon() - path.Append() failed: %s\n", strerror(status));
} else
printf("IconButton::SetIcon() - path.GetParent() failed: %s\n", strerror(status));
} else
printf("IconButton::SetIcon() - path.InitCheck() failed: %s\n", strerror(status));
} else
printf("IconButton::SetIcon() - be_app->GetAppInfo() failed: %s\n", strerror(status));
} else
fileBitmap = BTranslationUtils::GetBitmap(pathToBitmap);
if (fileBitmap) {
status = _MakeBitmaps(fileBitmap);
delete fileBitmap;
} else
status = B_ERROR;
}
return status;
}
// SetIcon
status_t
IconButton::SetIcon(const BBitmap* bitmap)
{
if (bitmap && bitmap->ColorSpace() == B_CMAP8) {
status_t status = bitmap->InitCheck();
if (status >= B_OK) {
if (BBitmap* rgb32Bitmap = _ConvertToRGB32(bitmap)) {
status = _MakeBitmaps(rgb32Bitmap);
delete rgb32Bitmap;
} else
status = B_NO_MEMORY;
}
return status;
} else
return _MakeBitmaps(bitmap);
}
// SetIcon
status_t
IconButton::SetIcon(const BMimeType* fileType, bool small)
{
status_t status = fileType ? fileType->InitCheck() : B_BAD_VALUE;
if (status >= B_OK) {
BBitmap* mimeBitmap = new(nothrow) BBitmap(BRect(0.0, 0.0, 15.0, 15.0), B_CMAP8);
if (mimeBitmap && mimeBitmap->IsValid()) {
status = fileType->GetIcon(mimeBitmap, small ? B_MINI_ICON : B_LARGE_ICON);
if (status >= B_OK) {
if (BBitmap* bitmap = _ConvertToRGB32(mimeBitmap)) {
status = _MakeBitmaps(bitmap);
delete bitmap;
} else
printf("IconButton::SetIcon() - B_RGB32 bitmap is not valid\n");
} else
printf("IconButton::SetIcon() - fileType->GetIcon() failed: %s\n", strerror(status));
} else
printf("IconButton::SetIcon() - B_CMAP8 bitmap is not valid\n");
delete mimeBitmap;
} else
printf("IconButton::SetIcon() - fileType is not valid: %s\n", strerror(status));
return status;
}
// SetIcon
status_t
IconButton::SetIcon(const unsigned char* bitsFromQuickRes,
uint32 width, uint32 height, color_space format, bool convertToBW)
{
status_t status = B_BAD_VALUE;
if (bitsFromQuickRes && width > 0 && height > 0) {
BBitmap* quickResBitmap = new(nothrow) BBitmap(BRect(0.0, 0.0, width - 1.0, height - 1.0), format);
status = quickResBitmap ? quickResBitmap->InitCheck() : B_ERROR;
if (status >= B_OK) {
// It doesn't look right to copy BitsLength() bytes, but bitmaps
// exported from QuickRes still contain their padding, so it is alright.
memcpy(quickResBitmap->Bits(), bitsFromQuickRes, quickResBitmap->BitsLength());
if (format != B_RGB32 && format != B_RGBA32 && format != B_RGB32_BIG && format != B_RGBA32_BIG) {
// colorspace needs conversion
BBitmap* bitmap = new(nothrow) BBitmap(quickResBitmap->Bounds(), B_RGB32, true);
if (bitmap && bitmap->IsValid()) {
BView* helper = new BView(bitmap->Bounds(), "helper",
B_FOLLOW_NONE, B_WILL_DRAW);
if (bitmap->Lock()) {
bitmap->AddChild(helper);
helper->SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
helper->FillRect(helper->Bounds());
helper->SetDrawingMode(B_OP_OVER);
helper->DrawBitmap(quickResBitmap, BPoint(0.0, 0.0));
helper->Sync();
bitmap->Unlock();
}
status = _MakeBitmaps(bitmap);
} else
printf("IconButton::SetIcon() - B_RGB32 bitmap is not valid\n");
delete bitmap;
} else {
// native colorspace (32 bits)
if (convertToBW) {
// convert to gray scale icon
uint8* bits = (uint8*)quickResBitmap->Bits();
uint32 bpr = quickResBitmap->BytesPerRow();
for (uint32 y = 0; y < height; y++) {
uint8* handle = bits;
uint8 gray;
for (uint32 x = 0; x < width; x++) {
gray = uint8((116 * handle[0] + 600 * handle[1] + 308 * handle[2]) / 1024);
handle[0] = gray;
handle[1] = gray;
handle[2] = gray;
handle += 4;
}
bits += bpr;
}
}
status = _MakeBitmaps(quickResBitmap);
}
} else
printf("IconButton::SetIcon() - error allocating bitmap: %s\n", strerror(status));
delete quickResBitmap;
}
return status;
}
// ClearIcon
void
IconButton::ClearIcon()
{
_DeleteBitmaps();
_Update();
}
// Bitmap
BBitmap*
IconButton::Bitmap() const
{
BBitmap* bitmap = NULL;
if (fNormalBitmap && fNormalBitmap->IsValid()) {
bitmap = new(nothrow) BBitmap(fNormalBitmap);
if (bitmap->IsValid()) {
// TODO: remove this functionality when we use real transparent bitmaps
uint8* bits = (uint8*)bitmap->Bits();
uint32 bpr = bitmap->BytesPerRow();
uint32 width = bitmap->Bounds().IntegerWidth() + 1;
uint32 height = bitmap->Bounds().IntegerHeight() + 1;
color_space format = bitmap->ColorSpace();
if (format == B_CMAP8) {
// replace gray with magic transparent index
} else if (format == B_RGB32) {
for (uint32 y = 0; y < height; y++) {
uint8* bitsHandle = bits;
for (uint32 x = 0; x < width; x++) {
if (bitsHandle[0] == 216
&& bitsHandle[1] == 216
&& bitsHandle[2] == 216) {
bitsHandle[3] = 0; // make this pixel completely transparent
}
bitsHandle += 4;
}
bits += bpr;
}
}
} else {
delete bitmap;
bitmap = NULL;
}
}
return bitmap;
}
// DrawBorder
bool
IconButton::DrawBorder() const
{
return (IsEnabled() && (_HasFlags(STATE_INSIDE) || _HasFlags(STATE_TRACKING))
|| _HasFlags(STATE_FORCE_PRESSED));
}
// DrawNormalBorder
void
IconButton::DrawNormalBorder(BRect r, rgb_color background,
rgb_color shadow, rgb_color darkShadow,
rgb_color lightShadow, rgb_color light)
{
_DrawFrame(r, shadow, darkShadow, light, lightShadow);
}
// DrawPressedBorder
void
IconButton::DrawPressedBorder(BRect r, rgb_color background,
rgb_color shadow, rgb_color darkShadow,
rgb_color lightShadow, rgb_color light)
{
_DrawFrame(r, shadow, light, darkShadow, background);
}
// IsValid
bool
IconButton::IsValid() const
{
return (fNormalBitmap && fDisabledBitmap && fClickedBitmap && fDisabledClickedBitmap
&& fNormalBitmap->IsValid()
&& fDisabledBitmap->IsValid()
&& fClickedBitmap->IsValid()
&& fDisabledClickedBitmap->IsValid());
}
// Value
int32
IconButton::Value() const
{
return _HasFlags(STATE_PRESSED) ? B_CONTROL_ON : B_CONTROL_OFF;
}
// SetValue
void
IconButton::SetValue(int32 value)
{
if (value)
_AddFlags(STATE_PRESSED);
else
_ClearFlags(STATE_PRESSED);
}
// IsEnabled
bool
IconButton::IsEnabled() const
{
return _HasFlags(STATE_ENABLED) ? B_CONTROL_ON : B_CONTROL_OFF;
}
// SetEnabled
void
IconButton::SetEnabled(bool enabled)
{
if (enabled)
_AddFlags(STATE_ENABLED);
else
_ClearFlags(STATE_ENABLED | STATE_TRACKING | STATE_INSIDE);
}
// _ConvertToRGB32
BBitmap*
IconButton::_ConvertToRGB32(const BBitmap* bitmap) const
{
BBitmap* convertedBitmap = new(nothrow) BBitmap(bitmap->Bounds(), B_BITMAP_ACCEPTS_VIEWS, B_RGBA32);
if (convertedBitmap && convertedBitmap->IsValid()) {
memset(convertedBitmap->Bits(), 0, convertedBitmap->BitsLength());
BView* helper = new BView(bitmap->Bounds(), "helper",
B_FOLLOW_NONE, B_WILL_DRAW);
if (convertedBitmap->Lock()) {
convertedBitmap->AddChild(helper);
helper->SetDrawingMode(B_OP_OVER);
helper->DrawBitmap(bitmap, BPoint(0.0, 0.0));
helper->Sync();
convertedBitmap->Unlock();
}
} else {
delete convertedBitmap;
convertedBitmap = NULL;
}
return convertedBitmap;
}
// _MakeBitmaps
status_t
IconButton::_MakeBitmaps(const BBitmap* bitmap)
{
status_t status = bitmap ? bitmap->InitCheck() : B_BAD_VALUE;
if (status >= B_OK) {
// make our own versions of the bitmap
BRect b(bitmap->Bounds());
_DeleteBitmaps();
color_space format = bitmap->ColorSpace();
fNormalBitmap = new(nothrow) BBitmap(b, format);
fDisabledBitmap = new(nothrow) BBitmap(b, format);
fClickedBitmap = new(nothrow) BBitmap(b, format);
fDisabledClickedBitmap = new(nothrow) BBitmap(b, format);
if (IsValid()) {
// copy bitmaps from file bitmap
uint8* nBits = (uint8*)fNormalBitmap->Bits();
uint8* dBits = (uint8*)fDisabledBitmap->Bits();
uint8* cBits = (uint8*)fClickedBitmap->Bits();
uint8* dcBits = (uint8*)fDisabledClickedBitmap->Bits();
uint8* fBits = (uint8*)bitmap->Bits();
int32 nbpr = fNormalBitmap->BytesPerRow();
int32 fbpr = bitmap->BytesPerRow();
int32 pixels = b.IntegerWidth() + 1;
int32 lines = b.IntegerHeight() + 1;
// nontransparent version:
if (format == B_RGB32 || format == B_RGB32_BIG) {
// iterate over color components
for (int32 y = 0; y < lines; y++) {
for (int32 x = 0; x < pixels; x++) {
int32 nOffset = 4 * x;
int32 fOffset = 4 * x;
nBits[nOffset + 0] = fBits[fOffset + 0];
nBits[nOffset + 1] = fBits[fOffset + 1];
nBits[nOffset + 2] = fBits[fOffset + 2];
nBits[nOffset + 3] = 255;
// clicked bits are darker (lame method...)
cBits[nOffset + 0] = (uint8)((float)nBits[nOffset + 0] * 0.8);
cBits[nOffset + 1] = (uint8)((float)nBits[nOffset + 1] * 0.8);
cBits[nOffset + 2] = (uint8)((float)nBits[nOffset + 2] * 0.8);
cBits[nOffset + 3] = 255;
// disabled bits have less contrast (lame method...)
uint8 grey = 216;
float dist = (nBits[nOffset + 0] - grey) * 0.4;
dBits[nOffset + 0] = (uint8)(grey + dist);
dist = (nBits[nOffset + 1] - grey) * 0.4;
dBits[nOffset + 1] = (uint8)(grey + dist);
dist = (nBits[nOffset + 2] - grey) * 0.4;
dBits[nOffset + 2] = (uint8)(grey + dist);
dBits[nOffset + 3] = 255;
// disabled bits have less contrast (lame method...)
grey = 188;
dist = (nBits[nOffset + 0] - grey) * 0.4;
dcBits[nOffset + 0] = (uint8)(grey + dist);
dist = (nBits[nOffset + 1] - grey) * 0.4;
dcBits[nOffset + 1] = (uint8)(grey + dist);
dist = (nBits[nOffset + 2] - grey) * 0.4;
dcBits[nOffset + 2] = (uint8)(grey + dist);
dcBits[nOffset + 3] = 255;
}
nBits += nbpr;
dBits += nbpr;
cBits += nbpr;
dcBits += nbpr;
fBits += fbpr;
}
// transparent version:
} else if (format == B_RGBA32 || format == B_RGBA32_BIG) {
// iterate over color components
for (int32 y = 0; y < lines; y++) {
for (int32 x = 0; x < pixels; x++) {
int32 nOffset = 4 * x;
int32 fOffset = 4 * x;
nBits[nOffset + 0] = fBits[fOffset + 0];
nBits[nOffset + 1] = fBits[fOffset + 1];
nBits[nOffset + 2] = fBits[fOffset + 2];
nBits[nOffset + 3] = fBits[fOffset + 3];
// clicked bits are darker (lame method...)
cBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8);
cBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8);
cBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8);
cBits[nOffset + 3] = fBits[fOffset + 3];
// disabled bits have less opacity
dBits[nOffset + 0] = fBits[fOffset + 0];
dBits[nOffset + 1] = fBits[fOffset + 1];
dBits[nOffset + 2] = fBits[fOffset + 2];
dBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.5);
// disabled bits have less contrast (lame method...)
dcBits[nOffset + 0] = (uint8)(nBits[nOffset + 0] * 0.8);
dcBits[nOffset + 1] = (uint8)(nBits[nOffset + 1] * 0.8);
dcBits[nOffset + 2] = (uint8)(nBits[nOffset + 2] * 0.8);
dcBits[nOffset + 3] = (uint8)(fBits[fOffset + 3] * 0.5);
}
nBits += nbpr;
dBits += nbpr;
cBits += nbpr;
dcBits += nbpr;
fBits += fbpr;
}
// unsupported format
} else {
printf("IconButton::_MakeBitmaps() - bitmap has unsupported colorspace\n");
status = B_MISMATCHED_VALUES;
_DeleteBitmaps();
}
} else {
printf("IconButton::_MakeBitmaps() - error allocating local bitmaps\n");
status = B_NO_MEMORY;
_DeleteBitmaps();
}
} else
printf("IconButton::_MakeBitmaps() - bitmap is not valid\n");
return status;
}
// _DeleteBitmaps
void
IconButton::_DeleteBitmaps()
{
delete fNormalBitmap;
fNormalBitmap = NULL;
delete fDisabledBitmap;
fDisabledBitmap = NULL;
delete fClickedBitmap;
fClickedBitmap = NULL;
delete fDisabledClickedBitmap;
fDisabledClickedBitmap = NULL;
}
// _Update
void
IconButton::_Update()
{
if (LockLooper()) {
Invalidate();
UnlockLooper();
}
}
// _AddFlags
void
IconButton::_AddFlags(uint32 flags)
{
if (!(fButtonState & flags)) {
fButtonState |= flags;
_Update();
}
}
// _ClearFlags
void
IconButton::_ClearFlags(uint32 flags)
{
if (fButtonState & flags) {
fButtonState &= ~flags;
_Update();
}
}
// _HasFlags
bool
IconButton::_HasFlags(uint32 flags) const
{
return (fButtonState & flags);
}
// _DrawFrame
void
IconButton::_DrawFrame(BRect r, rgb_color col1, rgb_color col2,
rgb_color col3, rgb_color col4)
{
BeginLineArray(8);
AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col1);
AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col1);
AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col2);
AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col2);
r.InsetBy(1.0, 1.0);
AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), col3);
AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), col3);
AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), col4);
AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), col4);
EndLineArray();
}
@@ -0,0 +1,131 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
/** gui class that loads an image from disk and shows it
as clickable button */
// TODO: inherit from BControl?
#ifndef ICON_BUTTON_H
#define ICON_BUTTON_H
#include <layout.h>
#include <Invoker.h>
#include <String.h>
#include <View.h>
class BBitmap;
class BMimeType;
class IconButton : public MView, public BView, public BInvoker {
public:
IconButton(const char* name,
uint32 id,
const char* label = NULL,
BMessage* message = NULL,
BHandler* target = NULL);
virtual ~IconButton();
// BHandler
virtual void MessageReceived(BMessage* message);
// BView
virtual void AttachedToWindow();
virtual void Draw(BRect updateRect);
virtual void MouseDown(BPoint where);
virtual void MouseUp(BPoint where);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* message);
virtual void GetPreferredSize(float* width,
float* height);
// BInvoker
virtual status_t Invoke(BMessage* message = NULL);
// MView
virtual minimax layoutprefs();
virtual BRect layout(BRect rect);
// IconButton
bool IsValid() const;
virtual int32 Value() const;
virtual void SetValue(int32 value);
bool IsEnabled() const;
void SetEnabled(bool enable);
void SetPressed(bool pressed);
bool IsPressed() const;
uint32 ID() const
{ return fID; }
status_t SetIcon(const char* pathToBitmap);
status_t SetIcon(const BBitmap* bitmap);
status_t SetIcon(const BMimeType* fileType,
bool small = true);
status_t SetIcon(const unsigned char* bitsFromQuickRes,
uint32 width, uint32 height,
color_space format,
bool convertToBW = false);
void ClearIcon();
BBitmap* Bitmap() const; // caller has to delete the returned bitmap
virtual bool DrawBorder() const;
virtual void DrawNormalBorder(BRect r, rgb_color background,
rgb_color shadow,
rgb_color darkShadow,
rgb_color lightShadow,
rgb_color light);
virtual void DrawPressedBorder(BRect r, rgb_color background,
rgb_color shadow,
rgb_color darkShadow,
rgb_color lightShadow,
rgb_color light);
protected:
enum {
STATE_NONE = 0x0000,
STATE_TRACKING = 0x0001,
STATE_PRESSED = 0x0002,
STATE_ENABLED = 0x0004,
STATE_INSIDE = 0x0008,
STATE_FORCE_PRESSED = 0x0010,
};
void _AddFlags(uint32 flags);
void _ClearFlags(uint32 flags);
bool _HasFlags(uint32 flags) const;
void _DrawFrame(BRect frame,
rgb_color col1,
rgb_color col2,
rgb_color col3,
rgb_color col4);
// private:
BBitmap* _ConvertToRGB32(const BBitmap* bitmap) const;
status_t _MakeBitmaps(const BBitmap* bitmap);
void _DeleteBitmaps();
void _SendMessage() const;
void _Update();
uint32 fButtonState;
int32 fID;
BBitmap* fNormalBitmap;
BBitmap* fDisabledBitmap;
BBitmap* fClickedBitmap;
BBitmap* fDisabledClickedBitmap;
BString fLabel;
BHandler* fTargetCache;
};
#endif // ICON_BUTTON_H
@@ -0,0 +1,281 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "IconOptionsControl.h"
#include <stdio.h>
#include <Window.h>
#include "IconButton.h"
#define LABEL_DIST 8.0
// constructor
IconOptionsControl::IconOptionsControl(const char* name,
const char* label,
BMessage* message,
BHandler* target)
: MView(),
BControl(BRect(0.0, 0.0, 10.0, 10.0), name, label, message,
B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS),
fTargetCache(target)
{
if (Label()) {
labelwidth = StringWidth(Label()) + LABEL_DIST;
} else {
labelwidth = 0.0;
}
}
// destructor
IconOptionsControl::~IconOptionsControl()
{
}
// layoutprefs
minimax
IconOptionsControl::layoutprefs()
{
// sanity checks
if (rolemodel)
labelwidth = rolemodel->LabelWidth();
if (labelwidth < LabelWidth())
labelwidth = LabelWidth();
mpm.mini.x = 0.0 + labelwidth + 5.0;
mpm.mini.y = 0.0;
for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
minimax childPrefs = button->layoutprefs();
mpm.mini.x += childPrefs.mini.x + 1;
if (mpm.mini.y < childPrefs.mini.y + 1)
mpm.mini.y = childPrefs.mini.y + 1;
}
mpm.maxi.x = mpm.mini.x;
mpm.maxi.y = mpm.mini.y;
mpm.weight = 1.0;
return mpm;
}
// layout
BRect
IconOptionsControl::layout(BRect frame)
{
if (frame.Width() < mpm.mini.x + 1)
frame.right = frame.left + mpm.mini.x + 1;
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
return Frame();
}
// LabelWidth
float
IconOptionsControl::LabelWidth()
{
float width = ceilf(StringWidth(Label()));
if (width > 0.0)
width += LABEL_DIST;
return width;
}
// SetLabel
void
IconOptionsControl::SetLabel(const char* label)
{
BControl::SetLabel(label);
float width = LabelWidth();
if (rolemodel)
labelwidth = rolemodel->LabelWidth() > labelwidth ?
rolemodel->LabelWidth() : labelwidth;
labelwidth = width > labelwidth ? width : labelwidth;
_TriggerRelayout();
}
// AttachedToWindow
void
IconOptionsControl::AttachedToWindow()
{
BControl::AttachedToWindow();
SetViewColor(B_TRANSPARENT_32_BIT);
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
// AllAttached
void
IconOptionsControl::AllAttached()
{
for (int32 i = 0; IconButton* button = _FindIcon(i); i++)
button->SetTarget(this);
if (fTargetCache)
SetTarget(fTargetCache);
}
// Draw
void
IconOptionsControl::Draw(BRect updateRect)
{
FillRect(updateRect, B_SOLID_LOW);
if (Label()) {
if (!IsEnabled())
SetHighColor(tint_color(LowColor(), B_DISABLED_LABEL_TINT));
else
SetHighColor(tint_color(LowColor(), B_DARKEN_MAX_TINT));
font_height fh;
GetFontHeight(&fh);
BPoint p(Bounds().LeftTop());
p.y += floorf(Bounds().Height() / 2.0 + (fh.ascent + fh.descent) / 2.0) - 2.0;
DrawString(Label(), p);
}
}
// FrameResized
void
IconOptionsControl::FrameResized(float width, float height)
{
_LayoutIcons(Bounds());
}
// SetValue
void
IconOptionsControl::SetValue(int32 value)
{
if (IconButton* valueButton = _FindIcon(value)) {
for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
button->SetPressed(button == valueButton);
}
}
}
// Value
int32
IconOptionsControl::Value() const
{
for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
if (button->IsPressed())
return i;
}
return -1;
}
// SetEnabled
void
IconOptionsControl::SetEnabled(bool enable)
{
for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
button->SetEnabled(enable);
}
BControl::SetEnabled(enable);
Invalidate();
}
// MessageReceived
void
IconOptionsControl::MessageReceived(BMessage* message)
{
// catch a message from the attached IconButtons to
// handle switching the pressed icon
BView* source;
if (message->FindPointer("be:source", (void**)&source) >= B_OK) {
if (IconButton* sourceIcon = dynamic_cast<IconButton*>(source)) {
for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
if (button == sourceIcon) {
SetValue(i);
break;
}
}
// forward the message
Invoke(message);
return;
}
}
BControl::MessageReceived(message);
}
// Invoke
status_t
IconOptionsControl::Invoke(BMessage* message)
{
return BInvoker::Invoke(message);
}
// AddOption
void
IconOptionsControl::AddOption(IconButton* icon)
{
if (icon) {
if (!_FindIcon(0)) {
// first icon added, mark it
icon->SetPressed(true);
}
AddChild(icon);
icon->SetTarget(this);
layoutprefs();
_TriggerRelayout();
}
}
// _FindIcon
IconButton*
IconOptionsControl::_FindIcon(int32 index) const
{
if (BView* view = ChildAt(index))
return dynamic_cast<IconButton*>(view);
return NULL;
}
// _TriggerRelayout
void
IconOptionsControl::_TriggerRelayout()
{
if (!Parent())
return;
MView* mParent = dynamic_cast<MView*>(Parent());
if (mParent) {
if (BWindow* window = Window()) {
window->PostMessage(M_RECALCULATE_SIZE);
}
} else {
_LayoutIcons(Bounds());
}
}
// _LayoutIcons
void
IconOptionsControl::_LayoutIcons(BRect frame)
{
BPoint lt = frame.LeftTop();
// sanity checks
if (rolemodel)
labelwidth = rolemodel->LabelWidth();
if (labelwidth < LabelWidth())
labelwidth = LabelWidth();
lt.x += labelwidth;
for (int32 i = 0; IconButton* button = _FindIcon(i); i++) {
if (i == 0) {
lt.y = ceilf((frame.top + frame.bottom - button->mpm.mini.y) / 2.0);
}
button->MoveTo(lt);
button->ResizeTo(button->mpm.mini.x, button->mpm.mini.y);
lt = button->Frame().RightTop() + BPoint(1.0, 0.0);
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef ICON_OPTIONS_CONTROL_H
#define ICON_OPTIONS_CONTROL_H
#include <Control.h>
#include <Invoker.h>
#include <MDividable.h>
class IconButton;
class IconOptionsControl : public MView,
public MDividable,
public BControl {
public:
IconOptionsControl(const char* name = NULL,
const char* label = NULL,
BMessage* message = NULL,
BHandler* target = NULL);
~IconOptionsControl();
// MView interface
virtual minimax layoutprefs();
virtual BRect layout(BRect frame);
// MDividable interface
virtual float LabelWidth();
// BControl interface
virtual void AttachedToWindow();
virtual void AllAttached();
virtual void Draw(BRect updateRect);
virtual void FrameResized(float width, float height);
virtual void SetLabel(const char* label);
virtual void SetValue(int32 value);
virtual int32 Value() const;
virtual void SetEnabled(bool enable);
virtual void MessageReceived(BMessage* message);
// BInvoker interface
virtual status_t Invoke(BMessage* message = NULL);
// IconOptionsControl
void AddOption(IconButton* icon);
private:
IconButton* _FindIcon(int32 index) const;
void _TriggerRelayout();
void _LayoutIcons(BRect frame);
BHandler* fTargetCache;
};
#endif // ICON_OPTIONS_CONTROL_H
@@ -0,0 +1,162 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "InputTextView.h"
#include <stdio.h>
#include <stdlib.h>
#include <String.h>
// constructor
InputTextView::InputTextView(BRect frame, const char* name,
BRect textRect,
uint32 resizingMode,
uint32 flags)
: BTextView(frame, name, textRect, resizingMode, flags),
fWasFocus(false)
{
SetWordWrap(false);
}
// destructor
InputTextView::~InputTextView()
{
}
// MouseDown
void
InputTextView::MouseDown(BPoint where)
{
// enforce the behaviour of a typical BTextControl
// only let the BTextView handle mouse up/down when
// it already had focus
fWasFocus = IsFocus();
if (fWasFocus) {
BTextView::MouseDown(where);
} else {
// forward click
if (BView* view = Parent()) {
view->MouseDown(ConvertToParent(where));
}
}
}
// MouseUp
void
InputTextView::MouseUp(BPoint where)
{
// enforce the behaviour of a typical BTextControl
// only let the BTextView handle mouse up/down when
// it already had focus
if (fWasFocus)
BTextView::MouseUp(where);
}
// KeyDown
void
InputTextView::KeyDown(const char* bytes, int32 numBytes)
{
bool handled = true;
if (numBytes > 0) {
switch (bytes[0]) {
case B_ESCAPE:
// revert any typing changes
RevertChanges();
break;
case B_TAB:
// skip BTextView implementation
BView::KeyDown(bytes, numBytes);
// fall through
case B_RETURN:
ApplyChanges();
break;
default:
handled = false;
break;
}
}
if (!handled)
BTextView::KeyDown(bytes, numBytes);
}
// MakeFocus
void
InputTextView::MakeFocus(bool focus)
{
if (focus != IsFocus()) {
if (BView* view = Parent())
view->Invalidate();
BTextView::MakeFocus(focus);
if (focus)
SelectAll();
}
}
// Invoke
status_t
InputTextView::Invoke(BMessage* message)
{
if (!message)
message = Message();
if (message) {
BMessage copy(*message);
copy.AddInt64("when", system_time());
copy.AddPointer("source", (BView*)this);
return BInvoker::Invoke(&copy);
}
return B_BAD_VALUE;
}
// #pragma mark -
// Select
void
InputTextView::Select(int32 start, int32 finish)
{
BTextView::Select(start, finish);
_CheckTextRect();
}
// InsertText
void
InputTextView::InsertText(const char* inText, int32 inLength, int32 inOffset,
const text_run_array* inRuns)
{
BTextView::InsertText(inText, inLength, inOffset, inRuns);
_CheckTextRect();
}
// DeleteText
void
InputTextView::DeleteText(int32 fromOffset, int32 toOffset)
{
BTextView::DeleteText(fromOffset, toOffset);
_CheckTextRect();
}
// #pragma mark -
// _CheckTextRect
void
InputTextView::_CheckTextRect()
{
// update text rect and make sure
// the cursor/selection is in view
BRect textRect(TextRect());
float width = ceilf(StringWidth(Text()) + 2.0);
if (textRect.Width() != width) {
textRect.right = textRect.left + width;
SetTextRect(textRect);
ScrollToSelection();
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef INPUT_TEXT_VIEW_H
#define INPUT_TEXT_VIEW_H
#include <Invoker.h>
#include <TextView.h>
class InputTextView : public BTextView,
public BInvoker {
public:
InputTextView(BRect frame,
const char* name,
BRect textRect,
uint32 resizingMode,
uint32 flags);
virtual ~InputTextView();
// BTextView interface
virtual void MouseDown(BPoint where);
virtual void MouseUp(BPoint where);
virtual void KeyDown(const char* bytes, int32 numBytes);
virtual void MakeFocus(bool focus);
// BInvoker interface
virtual status_t Invoke(BMessage* message = NULL);
// InputTextView
virtual void RevertChanges() = 0;
virtual void ApplyChanges() = 0;
protected:
// BTextView
virtual void Select(int32 start, int32 finish);
virtual void InsertText(const char* inText,
int32 inLength,
int32 inOffset,
const text_run_array* inRuns);
virtual void DeleteText(int32 fromOffset,
int32 toOffset);
void _CheckTextRect();
bool fWasFocus;
};
#endif // INPUT_TEXT_VIEW_H
@@ -0,0 +1,908 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "ListViews.h"
#include <stdio.h>
#include <malloc.h>
#include <Bitmap.h>
#include <Cursor.h>
#include <Entry.h>
#include <MessageRunner.h>
#include <Messenger.h>
#include <ScrollBar.h>
#include <ScrollView.h>
#include <String.h>
#include <Window.h>
#include "cursors.h"
#define MAX_DRAG_HEIGHT 200.0
#define ALPHA 170
#define TEXT_OFFSET 5.0
enum {
MSG_TICK = 'tick',
};
// SimpleItem class
SimpleItem::SimpleItem( const char *name )
: BStringItem( name )
{
}
SimpleItem::~SimpleItem()
{
}
// SimpleItem::DrawItem
void
SimpleItem::Draw(BView *owner, BRect frame, uint32 flags)
{
DrawBackground(owner, frame, flags);
// label
owner->SetHighColor( 0, 0, 0, 255 );
font_height fh;
owner->GetFontHeight( &fh );
const char* text = Text();
BString truncatedString( text );
owner->TruncateString( &truncatedString, B_TRUNCATE_MIDDLE,
frame.Width() - TEXT_OFFSET - 4.0 );
float height = frame.Height();
float textHeight = fh.ascent + fh.descent;
BPoint textPoint;
textPoint.x = frame.left + TEXT_OFFSET;
textPoint.y = frame.top
+ ceilf(height / 2.0 - textHeight / 2.0
+ fh.ascent);
owner->DrawString(truncatedString.String(), textPoint);
}
// SimpleItem::DrawBackground
void
SimpleItem::DrawBackground(BView *owner, BRect frame, uint32 flags)
{
// stroke a blue frame around the item if it's focused
if (flags & FLAGS_FOCUSED) {
owner->SetLowColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
owner->StrokeRect(frame, B_SOLID_LOW);
frame.InsetBy(1.0, 1.0);
}
// figure out bg-color
rgb_color color = (rgb_color){ 255, 255, 255, 255 };
if ( flags & FLAGS_TINTED_LINE )
color = tint_color( color, 1.06 );
// background
if ( IsSelected() )
color = tint_color( color, B_DARKEN_2_TINT );
owner->SetLowColor( color );
owner->FillRect( frame, B_SOLID_LOW );
}
// DragSortableListView class
DragSortableListView::DragSortableListView(BRect frame, const char* name,
list_view_type type, uint32 resizingMode,
uint32 flags)
: BListView(frame, name, type, resizingMode, flags),
fDropRect(0.0, 0.0, -1.0, -1.0),
fMouseWheelFilter(NULL),
fScrollPulse(NULL),
fDropIndex(-1),
fLastClickedItem(NULL),
fScrollView(NULL),
fDragCommand(B_SIMPLE_DATA),
fFocusedIndex(-1)
{
SetViewColor(B_TRANSPARENT_32_BIT);
}
DragSortableListView::~DragSortableListView()
{
// delete fMouseWheelFilter;
delete fScrollPulse;
}
// AttachedToWindow
void
DragSortableListView::AttachedToWindow()
{
if (!fMouseWheelFilter)
fMouseWheelFilter = new MouseWheelFilter(this);
Window()->AddCommonFilter(fMouseWheelFilter);
BListView::AttachedToWindow();
// work arround a bug in BListView
BRect bounds = Bounds();
BListView::FrameResized(bounds.Width(), bounds.Height());
}
// DetachedFromWindow
void
DragSortableListView::DetachedFromWindow()
{
// Window()->RemoveCommonFilter(fMouseWheelFilter);
}
// FrameResized
void
DragSortableListView::FrameResized(float width, float height)
{
BListView::FrameResized(width, height);
}
/*
// MakeFocus
void
DragSortableListView::MakeFocus(bool focused)
{
if (focused != IsFocus()) {
Invalidate();
BListView::MakeFocus(focused);
}
}
*/
// Draw
void
DragSortableListView::Draw( BRect updateRect )
{
int32 firstIndex = IndexOf(updateRect.LeftTop());
int32 lastIndex = IndexOf(updateRect.RightBottom());
if (firstIndex >= 0) {
if (lastIndex < firstIndex)
lastIndex = CountItems() - 1;
// update rect contains items
BRect r = updateRect;
for (int32 i = firstIndex; i <= lastIndex; i++) {
r = ItemFrame(i);
DrawListItem(this, i, r);
}
updateRect.top = r.bottom + 1.0;
if (updateRect.IsValid()) {
SetLowColor(255, 255, 255, 255);
FillRect(updateRect, B_SOLID_LOW);
}
} else {
SetLowColor(255, 255, 255, 255);
FillRect(updateRect, B_SOLID_LOW);
}
// drop anticipation indication
if (fDropRect.IsValid()) {
SetHighColor(255, 0, 0, 255);
StrokeRect(fDropRect);
}
/* // focus indication
if (IsFocus()) {
SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
StrokeRect(Bounds());
}*/
}
// ScrollTo
void
DragSortableListView::ScrollTo(BPoint where)
{
uint32 buttons;
BPoint point;
GetMouse(&point, &buttons, false);
uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
MouseMoved(point, transit, &fDragMessageCopy);
BListView::ScrollTo(where);
}
// TargetedByScrollView
void
DragSortableListView::TargetedByScrollView(BScrollView* scrollView)
{
fScrollView = scrollView;
BListView::TargetedByScrollView(scrollView);
}
// InitiateDrag
bool
DragSortableListView::InitiateDrag( BPoint point, int32 index, bool )
{
// supress drag&drop while an item is focused
if (fFocusedIndex >= 0)
return false;
bool success = false;
BListItem* item = ItemAt( CurrentSelection( 0 ) );
if ( !item ) {
// workarround a timing problem
Select( index );
item = ItemAt( index );
}
if ( item ) {
// create drag message
BMessage msg( fDragCommand );
MakeDragMessage( &msg );
// figure out drag rect
float width = Bounds().Width();
BRect dragRect(0.0, 0.0, width, -1.0);
// figure out, how many items fit into our bitmap
int32 numItems;
bool fade = false;
for (numItems = 0; BListItem* item = ItemAt( CurrentSelection( numItems ) ); numItems++) {
dragRect.bottom += ceilf( item->Height() ) + 1.0;
if ( dragRect.Height() > MAX_DRAG_HEIGHT ) {
fade = true;
dragRect.bottom = MAX_DRAG_HEIGHT;
numItems++;
break;
}
}
BBitmap* dragBitmap = new BBitmap( dragRect, B_RGB32, true );
if ( dragBitmap && dragBitmap->IsValid() ) {
if ( BView *v = new BView( dragBitmap->Bounds(), "helper", B_FOLLOW_NONE, B_WILL_DRAW ) ) {
dragBitmap->AddChild( v );
dragBitmap->Lock();
BRect itemBounds( dragRect) ;
itemBounds.bottom = 0.0;
// let all selected items, that fit into our drag_bitmap, draw
for ( int32 i = 0; i < numItems; i++ ) {
int32 index = CurrentSelection( i );
BListItem* item = ItemAt( index );
itemBounds.bottom = itemBounds.top + ceilf( item->Height() );
if ( itemBounds.bottom > dragRect.bottom )
itemBounds.bottom = dragRect.bottom;
DrawListItem( v, index, itemBounds );
itemBounds.top = itemBounds.bottom + 1.0;
}
// make a black frame arround the edge
v->SetHighColor( 0, 0, 0, 255 );
v->StrokeRect( v->Bounds() );
v->Sync();
uint8 *bits = (uint8 *)dragBitmap->Bits();
int32 height = (int32)dragBitmap->Bounds().Height() + 1;
int32 width = (int32)dragBitmap->Bounds().Width() + 1;
int32 bpr = dragBitmap->BytesPerRow();
if (fade) {
for ( int32 y = 0; y < height - ALPHA / 2; y++, bits += bpr ) {
uint8 *line = bits + 3;
for (uint8 *end = line + 4 * width; line < end; line += 4)
*line = ALPHA;
}
for ( int32 y = height - ALPHA / 2; y < height; y++, bits += bpr ) {
uint8 *line = bits + 3;
for (uint8 *end = line + 4 * width; line < end; line += 4)
*line = (height - y) << 1;
}
} else {
for ( int32 y = 0; y < height; y++, bits += bpr ) {
uint8 *line = bits + 3;
for (uint8 *end = line + 4 * width; line < end; line += 4)
*line = ALPHA;
}
}
dragBitmap->Unlock();
}
} else {
delete dragBitmap;
dragBitmap = NULL;
}
if (dragBitmap)
DragMessage( &msg, dragBitmap, B_OP_ALPHA, BPoint( 0.0, 0.0 ) );
else
DragMessage( &msg, dragRect.OffsetToCopy( point ), this );
_SetDragMessage(&msg);
success = true;
}
return success;
}
// WindowActivated
void
DragSortableListView::WindowActivated( bool active )
{
// workarround for buggy focus indication of BScrollView
if ( BView* view = Parent() )
view->Invalidate();
}
// MessageReceived
void
DragSortableListView::MessageReceived(BMessage* message)
{
if (message->what == fDragCommand) {
DragSortableListView *list = NULL;
if ( message->FindPointer( "list", (void **)&list ) == B_OK
&& list == this ) {
int32 count = CountItems();
if ( fDropIndex < 0 || fDropIndex > count )
fDropIndex = count;
BList items;
int32 index;
for ( int32 i = 0; message->FindInt32( "index", i, &index ) == B_OK; i++ )
if ( BListItem* item = ItemAt(index) )
items.AddItem( (void*)item );
if ( items.CountItems() > 0 ) {
if ( modifiers() & B_SHIFT_KEY )
CopyItems( items, fDropIndex );
else
MoveItems( items, fDropIndex );
}
fDropIndex = -1;
}
} else {
switch ( message->what ) {
case MSG_TICK: {
float scrollV = 0.0;
BRect rect(Bounds());
BPoint point;
uint32 buttons;
GetMouse(&point, &buttons, false);
if (rect.Contains(point)) {
// calculate the vertical scrolling offset
float hotDist = rect.Height() * SCROLL_AREA;
if (point.y > rect.bottom - hotDist)
scrollV = hotDist - (rect.bottom - point.y);
else if (point.y < rect.top + hotDist)
scrollV = (point.y - rect.top) - hotDist;
}
// scroll
if (scrollV != 0.0 && fScrollView) {
if (BScrollBar* scrollBar = fScrollView->ScrollBar(B_VERTICAL)) {
float value = scrollBar->Value();
scrollBar->SetValue(scrollBar->Value() + scrollV);
if (scrollBar->Value() != value) {
// update mouse position
uint32 buttons;
BPoint point;
GetMouse(&point, &buttons, false);
uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
MouseMoved(point, transit, &fDragMessageCopy);
}
}
}
break;
}
// case B_MODIFIERS_CHANGED:
// ModifiersChanged();
// break;
case B_MOUSE_WHEEL_CHANGED: {
BListView::MessageReceived( message );
BPoint point;
uint32 buttons;
GetMouse(&point, &buttons, false);
uint32 transit = Bounds().Contains(point) ? B_INSIDE_VIEW : B_OUTSIDE_VIEW;
MouseMoved(point, transit, &fDragMessageCopy);
break;
}
default:
BListView::MessageReceived( message );
break;
}
}
}
// KeyDown
void
DragSortableListView::KeyDown( const char* bytes, int32 numBytes )
{
if ( numBytes < 1 )
return;
if ( ( bytes[0] == B_BACKSPACE ) || ( bytes[0] == B_DELETE ) )
RemoveSelected();
BListView::KeyDown( bytes, numBytes );
}
// MouseDown
void
DragSortableListView::MouseDown( BPoint where )
{
int32 clicks = 1;
uint32 buttons = 0;
Window()->CurrentMessage()->FindInt32("clicks", &clicks);
Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
int32 clickedIndex = -1;
for (int32 i = 0; BListItem* item = ItemAt(i); i++) {
if (ItemFrame(i).Contains(where)) {
if (clicks == 2) {
// only do something if user clicked the same item twice
if (fLastClickedItem == item)
DoubleClicked(i);
} else {
// remember last clicked item
fLastClickedItem = item;
}
clickedIndex = i;
break;
}
}
if (clickedIndex == -1)
fLastClickedItem = NULL;
BListItem* item = ItemAt(clickedIndex);
if (ListType() == B_MULTIPLE_SELECTION_LIST
&& item && (buttons & B_SECONDARY_MOUSE_BUTTON)) {
if (item->IsSelected())
Deselect(clickedIndex);
else
Select(clickedIndex, true);
} else {
BListView::MouseDown(where);
}
}
// MouseMoved
void
DragSortableListView::MouseMoved(BPoint where, uint32 transit, const BMessage *msg)
{
if (msg && AcceptDragMessage(msg)) {
switch (transit) {
case B_ENTERED_VIEW:
case B_INSIDE_VIEW: {
// remember drag message
// this is needed to react on modifier changes
_SetDragMessage(msg);
// set drop target through virtual function
SetDropTargetRect(msg, where);
// go into autoscrolling mode
BRect r = Bounds();
r.InsetBy(0.0, r.Height() * SCROLL_AREA);
SetAutoScrolling(!r.Contains(where));
break;
}
case B_EXITED_VIEW:
// forget drag message
_SetDragMessage(NULL);
SetAutoScrolling(false);
// fall through
case B_OUTSIDE_VIEW:
_RemoveDropAnticipationRect();
break;
}
} else {
_RemoveDropAnticipationRect();
BListView::MouseMoved(where, transit, msg);
_SetDragMessage(NULL);
SetAutoScrolling(false);
BCursor cursor(B_HAND_CURSOR);
SetViewCursor(&cursor, true);
}
fLastMousePos = where;
}
// MouseUp
void
DragSortableListView::MouseUp( BPoint where )
{
// remove drop mark
_SetDropAnticipationRect( BRect( 0.0, 0.0, -1.0, -1.0 ) );
SetAutoScrolling(false);
// be sure to forget drag message
_SetDragMessage(NULL);
BListView::MouseUp( where );
BCursor cursor(B_HAND_CURSOR);
SetViewCursor(&cursor, true);
}
// DrawItem
void
DragSortableListView::DrawItem( BListItem *item, BRect itemFrame, bool complete )
{
DrawListItem( this, IndexOf( item ), itemFrame );
/* if (IsFocus()) {
SetHighColor(ui_color(B_KEYBOARD_NAVIGATION_COLOR));
StrokeRect(Bounds());
}*/
}
// MouseWheelChanged
bool
DragSortableListView::MouseWheelChanged(float x, float y)
{
BPoint where;
uint32 buttons;
GetMouse(&where, &buttons, false);
if (Bounds().Contains(where))
return true;
else
return false;
}
// SetDragCommand
void
DragSortableListView::SetDragCommand(uint32 command)
{
fDragCommand = command;
}
// ModifiersChaned
void
DragSortableListView::ModifiersChanged()
{
SetDropTargetRect(&fDragMessageCopy, fLastMousePos);
}
// SetItemFocused
void
DragSortableListView::SetItemFocused(int32 index)
{
InvalidateItem(fFocusedIndex);
InvalidateItem(index);
fFocusedIndex = index;
}
// AcceptDragMessage
bool
DragSortableListView::AcceptDragMessage(const BMessage* message) const
{
return message->what == fDragCommand;
}
// SetDropTargetRect
void
DragSortableListView::SetDropTargetRect(const BMessage* message, BPoint where)
{
if (AcceptDragMessage(message)) {
bool copy = modifiers() & B_SHIFT_KEY;
bool replaceAll = !message->HasPointer("list") && !copy;
BRect r = Bounds();
if (replaceAll) {
r.bottom--; // compensate for scrollbar offset
_SetDropAnticipationRect(r);
fDropIndex = -1;
} else {
// offset where by half of item height
r = ItemFrame(0);
where.y += r.Height() / 2.0;
int32 index = IndexOf(where);
if (index < 0)
index = CountItems();
_SetDropIndex(index);
const uchar* cursorData = copy ? kCopyCursor : B_HAND_CURSOR;
BCursor cursor(cursorData);
SetViewCursor(&cursor, true);
}
}
}
// SetAutoScrolling
void
DragSortableListView::SetAutoScrolling(bool enable)
{
if (fScrollPulse && enable)
return;
if (enable) {
BMessenger messenger(this, Window());
BMessage message(MSG_TICK);
fScrollPulse = new BMessageRunner(messenger, &message, 40000LL);
} else {
delete fScrollPulse;
fScrollPulse = NULL;
}
}
// DoesAutoScrolling
bool
DragSortableListView::DoesAutoScrolling() const
{
return fScrollPulse;
}
// ScrollTo
void
DragSortableListView::ScrollTo(int32 index)
{
if (index < 0)
index = 0;
if (index >= CountItems())
index = CountItems() - 1;
if (BListItem* item = ItemAt(index)) {
BRect itemFrame = ItemFrame(index);
BRect bounds = Bounds();
if (itemFrame.top < bounds.top) {
ScrollTo(itemFrame.LeftTop());
} else if (itemFrame.bottom > bounds.bottom) {
ScrollTo(BPoint(0.0, itemFrame.bottom - bounds.Height()));
}
}
}
// MoveItems
void
DragSortableListView::MoveItems( BList& items, int32 index )
{
DeselectAll();
// we remove the items while we look at them, the insertion index is decreased
// when the items index is lower, so that we insert at the right spot after
// removal
BList removedItems;
int32 count = items.CountItems();
for ( int32 i = 0; i < count; i++ )
{
BListItem* item = (BListItem*)items.ItemAt( i );
int32 removeIndex = IndexOf( item );
if ( RemoveItem( item ) && removedItems.AddItem( (void*)item ) )
{
if ( removeIndex < index )
index--;
}
// else ??? -> blow up
}
for ( int32 i = 0; BListItem* item = (BListItem*)removedItems.ItemAt( i ); i++ )
{
if ( AddItem( item, index ) )
{
// after we're done, the newly inserted items will be selected
Select( index, true );
// next items will be inserted after this one
index++;
}
else
delete item;
}
}
// CopyItems
void
DragSortableListView::CopyItems( BList& items, int32 index )
{
DeselectAll();
// by inserting the items after we copied all items first, we avoid
// cloning an item we already inserted and messing everything up
// in other words, don't touch the list before we know which items
// need to be cloned
BList clonedItems;
int32 count = items.CountItems();
for ( int32 i = 0; i < count; i++ )
{
BListItem* item = CloneItem( IndexOf( (BListItem*)items.ItemAt( i ) ) );
if ( item && !clonedItems.AddItem( (void*)item ) )
delete item;
}
for ( int32 i = 0; BListItem* item = (BListItem*)clonedItems.ItemAt( i ); i++ )
{
if ( AddItem( item, index ) )
{
// after we're done, the newly inserted items will be selected
Select( index, true );
// next items will be inserted after this one
index++;
}
else
delete item;
}
}
// RemoveItemList
void
DragSortableListView::RemoveItemList( BList& items )
{
int32 count = items.CountItems();
for ( int32 i = 0; i < count; i++ )
{
BListItem* item = (BListItem*)items.ItemAt( i );
if ( RemoveItem( item ) )
delete item;
}
}
// RemoveSelected
void
DragSortableListView::RemoveSelected()
{
// if (fFocusedIndex >= 0)
// return;
BList items;
for ( int32 i = 0; BListItem* item = ItemAt( CurrentSelection( i ) ); i++ )
items.AddItem( (void*)item );
RemoveItemList( items );
}
// CountSelectedItems
int32
DragSortableListView::CountSelectedItems() const
{
int32 count = 0;
while ( CurrentSelection( count ) >= 0 )
count++;
return count;
}
// SelectAll
void
DragSortableListView::SelectAll()
{
Select(0, CountItems() - 1);
}
// DeleteItem
bool
DragSortableListView::DeleteItem(int32 index)
{
BListItem* item = ItemAt(index);
if (item && RemoveItem(item)) {
delete item;
return true;
}
return false;
}
// _SetDropAnticipationRect
void
DragSortableListView::_SetDropAnticipationRect(BRect r)
{
if (fDropRect != r) {
if (fDropRect.IsValid())
Invalidate(fDropRect);
fDropRect = r;
if (fDropRect.IsValid())
Invalidate(fDropRect);
}
}
// _SetDropIndex
void
DragSortableListView::_SetDropIndex(int32 index)
{
if (fDropIndex != index) {
fDropIndex = index;
if (fDropIndex >= 0) {
int32 count = CountItems();
if (fDropIndex == count) {
BRect r;
if (BListItem* item = ItemAt(count - 1)) {
r = ItemFrame(count - 1);
r.top = r.bottom;
r.bottom = r.top + 1.0;
} else {
r = Bounds();
r.bottom--; // compensate for scrollbars moved slightly out of window
}
_SetDropAnticipationRect(r);
} else {
BRect r = ItemFrame(fDropIndex);
r.top--;
r.bottom = r.top + 1.0;
_SetDropAnticipationRect(r);
}
}
}
}
// _RemoveDropAnticipationRect
void
DragSortableListView::_RemoveDropAnticipationRect()
{
_SetDropAnticipationRect(BRect(0.0, 0.0, -1.0, -1.0));
// _SetDropIndex(-1);
}
// _SetDragMessage
void
DragSortableListView::_SetDragMessage(const BMessage* message)
{
if (message)
fDragMessageCopy = *message;
else
fDragMessageCopy.what = 0;
}
// SimpleListView class
SimpleListView::SimpleListView( BRect frame, BMessage* selectionChangeMessage )
: DragSortableListView( frame, "playlist listview",
B_MULTIPLE_SELECTION_LIST, B_FOLLOW_ALL,
B_WILL_DRAW | B_NAVIGABLE
| B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE ),
fSelectionChangeMessage( selectionChangeMessage )
{
}
// SimpleListView class
SimpleListView::SimpleListView( BRect frame, const char* name,
BMessage* selectionChangeMessage,
list_view_type type,
uint32 resizingMode, uint32 flags )
: DragSortableListView( frame, name, type, resizingMode, flags ),
fSelectionChangeMessage( selectionChangeMessage )
{
}
// destructor
SimpleListView::~SimpleListView()
{
delete fSelectionChangeMessage;
}
// layoutprefs
minimax
SimpleListView::layoutprefs()
{
mpm.mini.x = 30.0;
mpm.maxi.x = 10000.0;
mpm.mini.y = 50.0;
mpm.maxi.y = 10000.0;
mpm.weight = 1.0;
return mpm;
}
// layout
BRect
SimpleListView::layout(BRect frame)
{
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
return Frame();
}
// MessageReceived
void
SimpleListView::MessageReceived( BMessage* message)
{
switch ( message->what ) {
default:
DragSortableListView::MessageReceived( message );
break;
}
}
// SelectionChanged
void
SimpleListView::SelectionChanged()
{
BLooper* looper = Looper();
if (fSelectionChangeMessage && looper) {
BMessage message(*fSelectionChangeMessage);
looper->PostMessage(&message);
}
}
// CloneItem
BListItem*
SimpleListView::CloneItem(int32 atIndex) const
{
BListItem* clone = NULL;
if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(atIndex)))
clone = new SimpleItem(item->Text());
return clone;
}
// DrawListItem
void
SimpleListView::DrawListItem(BView* owner, int32 index, BRect frame) const
{
if (SimpleItem* item = dynamic_cast<SimpleItem*>(ItemAt(index))) {
uint32 flags = FLAGS_NONE;
if (index == fFocusedIndex)
flags |= FLAGS_FOCUSED;
if (index % 2)
flags |= FLAGS_TINTED_LINE;
item->Draw(owner, frame, flags);
}
}
// MakeDragMessage
void
SimpleListView::MakeDragMessage(BMessage* message) const
{
if (message) {
message->AddPointer( "list", (void*)dynamic_cast<const DragSortableListView*>(this));
int32 index;
for (int32 i = 0; (index = CurrentSelection(i)) >= 0; i++)
message->AddInt32( "index", index );
}
}
@@ -0,0 +1,185 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef LIST_VIEWS_H
#define LIST_VIEWS_H
#include <ListItem.h>
#include <ListView.h>
#include <Message.h>
#include <layout.h>
#include "MouseWheelFilter.h"
enum
{
FLAGS_NONE = 0x00,
FLAGS_TINTED_LINE = 0x01,
FLAGS_FOCUSED = 0x02,
};
// portion of the listviews height that triggers autoscrolling
// when the mouse is over it with a dragmessage
#define SCROLL_AREA 0.1
class BMessageRunner;
class BMessageFilter;
class InterfaceWindow;
class BScrollView;
// SimpleItem
class SimpleItem : public BStringItem
{
public:
SimpleItem(const char* name);
virtual ~SimpleItem();
virtual void Draw(BView* owner, BRect frame,
uint32 flags);
virtual void DrawBackground(BView* owner, BRect frame,
uint32 flags);
// let the item know what's going on
/* virtual void AttachedToListView(SimpleListView* owner);
virtual void DetachedFromListView(SimpleListView* owner);
virtual void SetItemFrame(BRect frame);*/
private:
};
// DragSortableListView
class DragSortableListView : public MouseWheelTarget,
public BListView {
public:
DragSortableListView( BRect frame,
const char* name,
list_view_type type
= B_SINGLE_SELECTION_LIST,
uint32 resizingMode
= B_FOLLOW_LEFT
| B_FOLLOW_TOP,
uint32 flags
= B_WILL_DRAW
| B_NAVIGABLE
| B_FRAME_EVENTS );
virtual ~DragSortableListView();
// BListView
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void FrameResized(float width, float height);
// virtual void MakeFocus(bool focused);
virtual void Draw( BRect updateRect );
virtual void ScrollTo(BPoint where);
virtual void TargetedByScrollView(BScrollView* scrollView);
virtual bool InitiateDrag( BPoint point, int32 index,
bool wasSelected );
virtual void MessageReceived( BMessage* message );
virtual void KeyDown( const char* bytes, int32 numBytes );
virtual void MouseDown( BPoint where );
virtual void MouseMoved( BPoint where, uint32 transit,
const BMessage* dragMessage );
virtual void MouseUp( BPoint where );
virtual void WindowActivated( bool active );
virtual void DrawItem( BListItem *item, BRect itemFrame,
bool complete = false);
// MouseWheelTarget
virtual bool MouseWheelChanged(float x, float y);
// DragSortableListView
virtual void SetDragCommand(uint32 command);
virtual void ModifiersChanged(); // called by window
virtual void DoubleClicked(int32 index) {}
virtual void SetItemFocused(int32 index);
virtual bool AcceptDragMessage(const BMessage* message) const;
virtual void SetDropTargetRect(const BMessage* message,
BPoint where);
// autoscrolling
void SetAutoScrolling(bool enable);
bool DoesAutoScrolling() const;
BScrollView* ScrollView() const
{ return fScrollView; }
void ScrollTo(int32 index);
virtual void MoveItems(BList& items, int32 toIndex);
virtual void CopyItems(BList& items, int32 toIndex);
virtual void RemoveItemList(BList& indices);
void RemoveSelected(); // uses RemoveItemList()
int32 CountSelectedItems() const;
void SelectAll();
virtual bool DeleteItem(int32 index);
virtual BListItem* CloneItem(int32 atIndex) const = 0;
virtual void DrawListItem(BView* owner, int32 index,
BRect itemFrame) const = 0;
virtual void MakeDragMessage(BMessage* message) const = 0;
private:
void _RemoveDropAnticipationRect();
void _SetDragMessage(const BMessage* message);
BRect fDropRect;
BMessage fDragMessageCopy;
BMessageFilter* fMouseWheelFilter;
BMessageRunner* fScrollPulse;
BPoint fLastMousePos;
protected:
void _SetDropAnticipationRect(BRect r);
void _SetDropIndex(int32 index);
int32 fDropIndex;
BListItem* fLastClickedItem;
BScrollView* fScrollView;
uint32 fDragCommand;
int32 fFocusedIndex;
};
// SimpleListView
class SimpleListView : public MView, public DragSortableListView {
public:
SimpleListView( BRect frame,
BMessage* selectionChangeMessage = NULL );
SimpleListView( BRect frame,
const char* name,
BMessage* selectionChangeMessage = NULL,
list_view_type type
= B_MULTIPLE_SELECTION_LIST,
uint32 resizingMode
= B_FOLLOW_ALL_SIDES,
uint32 flags
= B_WILL_DRAW | B_NAVIGABLE
| B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE );
~SimpleListView();
// MView
virtual minimax layoutprefs();
virtual BRect layout(BRect frame);
// BListView
virtual void MessageReceived(BMessage* message);
virtual void SelectionChanged();
virtual BListItem* CloneItem(int32 atIndex) const;
virtual void DrawListItem(BView* owner, int32 index,
BRect itemFrame) const;
virtual void MakeDragMessage(BMessage* message) const;
private:
BMessage* fSelectionChangeMessage;
};
#endif // LIST_VIEWS_H
@@ -0,0 +1,64 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef MOUSE_WHEEL_FILTER_H
#define MOUSE_WHEEL_FILTER_H
#include <Message.h>
#include <MessageFilter.h>
class MouseWheelTarget {
public:
MouseWheelTarget()
{
}
virtual ~MouseWheelTarget()
{
}
virtual bool MouseWheelChanged(float x, float y) = 0;
};
class MouseWheelFilter : public BMessageFilter {
public:
MouseWheelFilter(MouseWheelTarget* target)
: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
fTarget(target),
fTargetHandler(dynamic_cast<BHandler*>(fTarget))
{
}
virtual ~MouseWheelFilter()
{
}
virtual filter_result Filter(BMessage* message, BHandler** target)
{
filter_result result = B_DISPATCH_MESSAGE;
switch (message->what) {
case B_MOUSE_WHEEL_CHANGED: {
float x;
float y;
if (message->FindFloat("be:wheel_delta_x", &x) >= B_OK
&& message->FindFloat("be:wheel_delta_y", &y) >= B_OK) {
if (fTarget->MouseWheelChanged(x, y))
//result = B_SKIP_MESSAGE;
*target = fTargetHandler;
}
break;
}
default:
break;
}
return result;
}
private:
MouseWheelTarget* fTarget;
BHandler* fTargetHandler;
};
#endif // MOUSE_WHEEL_FILTER_H
@@ -0,0 +1,218 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "NummericalTextView.h"
#include <stdio.h>
#include <stdlib.h>
#include <String.h>
// constructor
NummericalTextView::NummericalTextView(BRect frame, const char* name,
BRect textRect,
uint32 resizingMode,
uint32 flags)
: InputTextView(frame, name, textRect, resizingMode, flags)
{
for (uint32 i = 0; i < '0'; i++) {
DisallowChar(i);
}
for (uint32 i = '9' + 1; i < 255; i++) {
DisallowChar(i);
}
AllowChar('-');
}
// destructor
NummericalTextView::~NummericalTextView()
{
}
// Invoke
status_t
NummericalTextView::Invoke(BMessage* message)
{
if (!message)
message = Message();
if (message) {
BMessage copy(*message);
copy.AddInt32("be:value", IntValue());
copy.AddFloat("float value", FloatValue());
return InputTextView::Invoke(&copy);
}
return B_BAD_VALUE;
}
// RevertChanges
void
NummericalTextView::RevertChanges()
{
if (fFloatMode)
SetValue(fFloatValueCache);
else
SetValue(fIntValueCache);
}
// ApplyChanges
void
NummericalTextView::ApplyChanges()
{
int32 i = atoi(Text());
float f = atof(Text());
if ((fFloatMode && f != fFloatValueCache) ||
(!fFloatMode && i != fIntValueCache)) {
Invoke();
}
}
// SetFloatMode
void
NummericalTextView::SetFloatMode(bool floatingPoint)
{
fFloatMode = floatingPoint;
if (floatingPoint)
AllowChar('.');
else
DisallowChar('.');
}
// SetValue
void
NummericalTextView::SetValue(int32 value)
{
BString helper;
helper << value;
SetText(helper.String());
// update caches
IntValue();
FloatValue();
if (IsFocus())
SelectAll();
}
// SetValue
void
NummericalTextView::SetValue(float value)
{
BString helper;
helper << value;
SetText(helper.String());
// update caches
IntValue();
FloatValue();
if (IsFocus())
SelectAll();
}
// IntValue
int32
NummericalTextView::IntValue() const
{
fIntValueCache = atoi(Text());
return fIntValueCache;
}
// FloatValue
float
NummericalTextView::FloatValue() const
{
fFloatValueCache = atof(Text());
return fFloatValueCache;
}
// #pragma mark -
// Select
void
NummericalTextView::Select(int32 start, int32 finish)
{
InputTextView::Select(start, finish);
_CheckMinusAllowed();
_CheckDotAllowed();
}
// InsertText
void
NummericalTextView::InsertText(const char* inText, int32 inLength, int32 inOffset,
const text_run_array* inRuns)
{
InputTextView::InsertText(inText, inLength, inOffset, inRuns);
_CheckMinusAllowed();
_CheckDotAllowed();
}
// DeleteText
void
NummericalTextView::DeleteText(int32 fromOffset, int32 toOffset)
{
InputTextView::DeleteText(fromOffset, toOffset);
_CheckMinusAllowed();
_CheckDotAllowed();
}
// #pragma mark -
// _ToggleAllowChar
void
NummericalTextView::_ToggleAllowChar(char c)
{
const char* text = Text();
if (text) {
bool found = false;
int32 selectionStart;
int32 selectionEnd;
GetSelection(&selectionStart, &selectionEnd);
int32 pos = 0;
while (text[pos]) {
// skip selection
if (selectionStart < selectionEnd
&& pos == selectionStart) {
pos = selectionEnd;
}
if (text[pos] == c) {
found = true;
break;
}
pos++;
}
if (found)
DisallowChar(c);
else
AllowChar(c);
}
}
// _CheckMinusAllowed
void
NummericalTextView::_CheckMinusAllowed()
{
_ToggleAllowChar('-');
}
// _CheckDotAllowed
void
NummericalTextView::_CheckDotAllowed()
{
if (fFloatMode) {
_ToggleAllowChar('.');
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef NUMMERICAL_TEXT_VIEW_H
#define NUMMERICAL_TEXT_VIEW_H
#include "InputTextView.h"
class NummericalTextView : public InputTextView {
public:
NummericalTextView(BRect frame,
const char* name,
BRect textRect,
uint32 resizingMode,
uint32 flags);
virtual ~NummericalTextView();
// BInvoker interface
virtual status_t Invoke(BMessage* message = NULL);
// InputTextView interface
virtual void RevertChanges();
virtual void ApplyChanges();
// NummericalTextView
void SetFloatMode(bool floatingPoint);
void SetValue(int32 value);
void SetValue(float value);
int32 IntValue() const;
float FloatValue() const;
protected:
// BTextView
virtual void Select(int32 start, int32 finish);
virtual void InsertText(const char* inText,
int32 inLength,
int32 inOffset,
const text_run_array* inRuns);
virtual void DeleteText(int32 fromOffset,
int32 toOffset);
// NummericalTextView
void _ToggleAllowChar(char c);
void _CheckMinusAllowed();
void _CheckDotAllowed();
bool fFloatMode;
mutable int32 fIntValueCache;
mutable float fFloatValueCache;
};
#endif // NUMMERICAL_TEXT_VIEW_H
@@ -0,0 +1,80 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "StringTextView.h"
#include <stdio.h>
#include <stdlib.h>
// constructor
StringTextView::StringTextView(BRect frame, const char* name,
BRect textRect,
uint32 resizingMode,
uint32 flags)
: InputTextView(frame, name, textRect, resizingMode, flags),
fStringCache("")
{
}
// destructor
StringTextView::~StringTextView()
{
}
// Invoke
status_t
StringTextView::Invoke(BMessage* message)
{
if (!message)
message = Message();
if (message) {
BMessage copy(*message);
copy.AddString("value", Value());
return InputTextView::Invoke(&copy);
}
return B_BAD_VALUE;
}
// RevertChanges
void
StringTextView::RevertChanges()
{
SetValue(fStringCache.String());
}
// ApplyChanges
void
StringTextView::ApplyChanges()
{
if (fStringCache != Text()) {
Invoke();
}
}
// SetValue
void
StringTextView::SetValue(const char* string)
{
SetText(string);
// update cache
Value();
if (IsFocus())
SelectAll();
}
// Value
const char*
StringTextView::Value() const
{
fStringCache = Text();
return fStringCache.String();
}
@@ -0,0 +1,42 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef STRING_TEXT_VIEW_H
#define STRING_TEXT_VIEW_H
#include "InputTextView.h"
#include <String.h>
class StringTextView : public InputTextView {
public:
StringTextView(BRect frame,
const char* name,
BRect textRect,
uint32 resizingMode,
uint32 flags);
virtual ~StringTextView();
// BInvoker interface
virtual status_t Invoke(BMessage* message = NULL);
// InputTextView interface
virtual void RevertChanges();
virtual void ApplyChanges();
// StringTextView
void SetValue(const char* string);
const char* Value() const;
protected:
mutable BString fStringCache;
};
#endif // STRING_TEXT_VIEW_H
@@ -0,0 +1,230 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "SwatchView.h"
#include <stdio.h>
#include <Bitmap.h>
#include <Cursor.h>
#include <Looper.h>
#include <Message.h>
#include <TypeConstants.h>
#include <Window.h>
#include "cursors.h"
#include "support.h"
#include "support_ui.h"
#define DRAG_INIT_DIST 10.0
// constructor
SwatchView::SwatchView(const char* name, BMessage* message,
BHandler* target, rgb_color color,
float width, float height)
: BView(BRect(0.0, 0.0, 23.0, 17.0), name,
B_FOLLOW_NONE, B_WILL_DRAW),
fColor(color),
fTrackingStart(-1.0, -1.0),
fActive(false),
fDropInvokes(false),
fClickMessage(message),
fDroppedMessage(NULL),
fTarget(target),
fWidth(width),
fHeight(height)
{
SetViewColor(B_TRANSPARENT_32_BIT);
SetHighColor(fColor);
}
// destructor
SwatchView::~SwatchView()
{
delete fClickMessage;
delete fDroppedMessage;
}
// layoutprefs
minimax
SwatchView::layoutprefs()
{
if (fWidth > 6.0 && fHeight > 6.0) {
mpm.mini.x = mpm.maxi.x = fWidth;
mpm.mini.y = mpm.maxi.y = fHeight;
} else {
mpm.mini.x = 6.0;
mpm.maxi.x = 10000.0;
mpm.mini.y = 6.0;
mpm.maxi.y = 10000.0;
}
mpm.weight = 1.0;
return mpm;
}
// layout
BRect
SwatchView::layout(BRect frame)
{
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
return Frame();
}
// Draw
void
SwatchView::Draw(BRect updateRect)
{
// rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
// rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
// rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
// rgb_color darkShadow = tint_color(background, B_DARKEN_3_TINT);
// rgb_color lightShadow = tint_color(background, B_DARKEN_1_TINT);
rgb_color colorLight = tint_color(fColor, B_LIGHTEN_2_TINT);
rgb_color colorShadow = tint_color(fColor, B_DARKEN_2_TINT);
BRect r(Bounds());
// _StrokeRect(r, background, background);
// r.InsetBy(1.0, 1.0);
/* _StrokeRect(r, lightShadow, light);
r.InsetBy(1.0, 1.0);
_StrokeRect(r, darkShadow, shadow);
r.InsetBy(1.0, 1.0);*/
_StrokeRect(r, colorLight, colorShadow);
r.InsetBy(1.0, 1.0);
FillRect(r);
}
// MessageReceived
void
SwatchView::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_PASTE: {
rgb_color color;
if (restore_color_from_message(message,
color) >= B_OK) {
SetColor(color);
_Invoke(fDroppedMessage);
}
break;
}
default:
BView::MessageReceived(message);
break;
}
}
// MouseDown
void
SwatchView::MouseDown(BPoint where)
{
if (Bounds().Contains(where))
fTrackingStart = where;
}
// MouseUp
void
SwatchView::MouseUp(BPoint where)
{
if (Bounds().Contains(where)
&& Bounds().Contains(fTrackingStart))
_Invoke(fClickMessage);
fTrackingStart.x = -1.0;
fTrackingStart.y = -1.0;
}
// MouseMoved
void
SwatchView::MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage)
{
if (transit == B_ENTERED_VIEW) {
BCursor cursor(kDropperCursor);
SetViewCursor(&cursor, true);
}
if (Bounds().Contains(fTrackingStart)) {
if (point_point_distance(where, fTrackingStart)
> DRAG_INIT_DIST || transit == B_EXITED_VIEW) {
_DragColor();
fTrackingStart.x = -1.0;
fTrackingStart.y = -1.0;
}
}
}
// SetColor
void
SwatchView::SetColor(rgb_color color)
{
fColor = color;
SetHighColor(fColor);
Invalidate();
}
// SetClickedMessage
void
SwatchView::SetClickedMessage(BMessage* message)
{
delete fClickMessage;
fClickMessage = message;
}
// SetDroppedMessage
void
SwatchView::SetDroppedMessage(BMessage* message)
{
delete fDroppedMessage;
fDroppedMessage = message;
}
// _Invoke
void
SwatchView::_Invoke(const BMessage* _message)
{
if (_message) {
BHandler* target = fTarget ? fTarget
: dynamic_cast<BHandler*>(Window());
BLooper* looper;
if (target && (looper = target->Looper())) {
BMessage message(*_message);
message.AddPointer("be:source", (void*)this);
message.AddInt64("be:when", system_time());
message.AddBool("begin", true);
store_color_in_message(&message, fColor);
looper->PostMessage(&message, target);
}
}
}
// _StrokeRect
void
SwatchView::_StrokeRect(BRect r, rgb_color leftTop,
rgb_color rightBottom)
{
BeginLineArray(4);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), leftTop);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), leftTop);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), rightBottom);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), rightBottom);
EndLineArray();
}
// _DragColor
void
SwatchView::_DragColor()
{
BBitmap *bitmap = new BBitmap(BRect(0.0, 0.0, 15.0, 15.0), B_RGB32);
BMessage message = make_color_drop_message(fColor, bitmap);
DragMessage(&message, bitmap, B_OP_ALPHA, BPoint(9.0, 9.0));
}
@@ -0,0 +1,70 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef SWATCH_VIEW_H
#define SWATCH_VIEW_H
#include <View.h>
#include <layout.h>
enum {
MSG_COLOR_DROP = 'PSTE',
};
class SwatchView : public MView, public BView {
public:
SwatchView(const char* name,
BMessage* message,
BHandler* target,
rgb_color color,
float width = 24.0,
float height = 24.0);
virtual ~SwatchView();
// MView
virtual minimax layoutprefs();
virtual BRect layout(BRect frame);
// BView
virtual void Draw(BRect updateRect);
virtual void MessageReceived(BMessage* message);
virtual void MouseDown(BPoint where);
virtual void MouseUp(BPoint where);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage);
// SwatchView
void SetColor(rgb_color color);
rgb_color Color() const
{ return fColor; }
void SetClickedMessage(BMessage* message);
void SetDroppedMessage(BMessage* message);
private:
void _Invoke(const BMessage* message);
void _StrokeRect(BRect frame, rgb_color leftTop,
rgb_color rightBottom);
void _DragColor();
rgb_color fColor;
BPoint fTrackingStart;
bool fActive;
bool fDropInvokes;
BMessage* fClickMessage;
BMessage* fDroppedMessage;
BHandler* fTarget;
float fWidth;
float fHeight;
};
#endif // SWATCH_VIEW_H
+344
View File
@@ -0,0 +1,344 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef CURSORS_H
#define CURSORS_H
const unsigned char kEmptyCursor[] = { 16, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const unsigned char kDropperCursor[] = { 16, 1, 14, 1,
0x00, 0x0e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0xff,
0x00, 0x7e, 0x00, 0xb8, 0x01, 0x18, 0x03, 0x28,
0x04, 0x40, 0x0c, 0x80, 0x11, 0x00, 0x32, 0x00,
0x44, 0x00, 0x48, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x0e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0xff,
0x00, 0x7e, 0x00, 0xf8, 0x01, 0xf8, 0x03, 0xe8,
0x07, 0xc0, 0x0f, 0x80, 0x1f, 0x00, 0x3e, 0x00,
0x7c, 0x00, 0x78, 0x00, 0x30, 0x00, 0x00, 0x00 };
const unsigned char kHandCursor[] = { 16, 1, 8, 9,
0x01, 0x80, 0x1a, 0x70, 0x26, 0x48, 0x26, 0x4a,
0x12, 0x4d, 0x12, 0x49, 0x68, 0x09, 0x98, 0x01,
0x88, 0x02, 0x40, 0x02, 0x20, 0x02, 0x20, 0x04,
0x10, 0x04, 0x08, 0x08, 0x04, 0x08, 0x04, 0x08,
0x01, 0x80, 0x1b, 0xf0, 0x3f, 0xf8, 0x3f, 0xfa,
0x1f, 0xff, 0x1f, 0xff, 0x6f, 0xff, 0xff, 0xff,
0xff, 0xfe, 0x7f, 0xfe, 0x3f, 0xfe, 0x3f, 0xfc,
0x1f, 0xfc, 0x0f, 0xf8, 0x07, 0xf8, 0x07, 0xf8 };
const unsigned char kCopyCursor[] = { 16, 1, 1, 1,
0x00, 0x00, 0x70, 0x00, 0x48, 0x00, 0x48, 0x00,
0x27, 0xc0, 0x24, 0xb8, 0x12, 0x54, 0x10, 0x02,
0x79, 0xe2, 0x99, 0x22, 0x85, 0x7a, 0x61, 0x4a,
0x19, 0xca, 0x04, 0x4a, 0x02, 0x78, 0x00, 0x00,
0x00, 0x00, 0x70, 0x00, 0x78, 0x00, 0x78, 0x00,
0x3f, 0xc0, 0x3f, 0xf8, 0x1f, 0xfc, 0x1f, 0xfe,
0x7f, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0x7f, 0xfe,
0x1f, 0xfe, 0x07, 0xfe, 0x03, 0xf8, 0x00, 0x00 };
const unsigned char kStopCursor[] = { 16, 1, 8, 9,
0x07, 0xe0, 0x18, 0x18, 0x20, 0x04, 0x47, 0xe2,
0x48, 0x42, 0x90, 0x89, 0x91, 0x19, 0x92, 0x29,
0x94, 0x49, 0x98, 0x89, 0x91, 0x09, 0x42, 0x12,
0x47, 0xe2, 0x20, 0x04, 0x18, 0x18, 0x07, 0xe0,
0x07, 0xe0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe,
0x78, 0x7e, 0xf0, 0xff, 0xf1, 0xff, 0xf3, 0xef,
0xf7, 0xcf, 0xff, 0x8f, 0xff, 0x0f, 0x7e, 0x1e,
0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, 0x07, 0xe0 };
const unsigned char kGrabCursor[] = { 16, 1, 8, 9,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0d, 0xb0, 0x12, 0x4c, 0x10, 0x0a, 0x08, 0x02,
0x18, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x04,
0x10, 0x04, 0x08, 0x08, 0x04, 0x08, 0x04, 0x08,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0d, 0xb0, 0x1f, 0xfc, 0x1f, 0xfe, 0x0f, 0xfe,
0x1f, 0xfe, 0x3f, 0xfe, 0x3f, 0xfe, 0x3f, 0xfc,
0x1f, 0xfc, 0x0f, 0xf8, 0x07, 0xf8, 0x07, 0xf8 };
const unsigned char kFillBucketCursor[] = { 16, 1, 15, 2,
0x00, 0xe0, 0x01, 0x10, 0x01, 0x90, 0x03, 0x50,
0x0d, 0x30, 0x39, 0x10, 0x71, 0x08, 0xe2, 0x84,
0xa1, 0x02, 0xb0, 0x01, 0xa8, 0x01, 0xa4, 0x02,
0xa2, 0x04, 0xa1, 0x08, 0x60, 0x90, 0x20, 0x60,
0x00, 0xe0, 0x01, 0x10, 0x01, 0x90, 0x03, 0xd0,
0x0f, 0xf0, 0x3f, 0xf0, 0x7f, 0xf8, 0xff, 0xfc,
0xff, 0xfe, 0xff, 0xff, 0xef, 0xff, 0xe7, 0xfe,
0xe3, 0xfc, 0xe1, 0xf8, 0x60, 0xf0, 0x20, 0x60 };
// ----------- Transformation cursors
const unsigned char kMoveCursor[] = { 16, 1, 8, 8,
0x01, 0x80, 0x02, 0x40, 0x04, 0x20, 0x08, 0x10,
0x1e, 0x78, 0x2a, 0x54, 0x4e, 0x72, 0x80, 0x01,
0x80, 0x01, 0x4e, 0x72, 0x2a, 0x54, 0x1e, 0x78,
0x08, 0x10, 0x04, 0x20, 0x02, 0x40, 0x01, 0x80,
0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0,
0x1f, 0xf8, 0x3b, 0xdc, 0x7f, 0xfe, 0xff, 0xff,
0xff, 0xff, 0x7f, 0xfe, 0x3b, 0xdc, 0x1f, 0xf8,
0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80 };
const unsigned char kLeftRightCursor[] = { 16, 1, 8, 8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x28, 0x14, 0x4f, 0xf2, 0x80, 0x01,
0x80, 0x01, 0x4f, 0xf2, 0x28, 0x14, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x18, 0x38, 0x1c, 0x7f, 0xfe, 0xff, 0xff,
0xff, 0xff, 0x7f, 0xfe, 0x38, 0x1c, 0x18, 0x18,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const unsigned char kUpDownCursor[] = { 16, 1, 8, 8,
0x01, 0x80, 0x02, 0x40, 0x04, 0x20, 0x08, 0x10,
0x0e, 0x70, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x0e, 0x70,
0x08, 0x10, 0x04, 0x20, 0x02, 0x40, 0x01, 0x80,
0x01, 0x80, 0x03, 0xc0, 0x07, 0xe0, 0x0f, 0xf0,
0x0f, 0xf0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0,
0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x0f, 0xf0,
0x0f, 0xf0, 0x07, 0xe0, 0x03, 0xc0, 0x01, 0x80 };
const unsigned char kLeftTopRightBottomCursor[] = { 16, 1, 8, 8,
0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x21, 0x00,
0x22, 0x00, 0x21, 0x00, 0x28, 0x80, 0x34, 0x40,
0x02, 0x2c, 0x01, 0x14, 0x00, 0x84, 0x00, 0x44,
0x00, 0x84, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00,
0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x80, 0x37, 0xc0,
0x03, 0xec, 0x01, 0xfc, 0x00, 0xfc, 0x00, 0x7c,
0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00 };
const unsigned char kLeftBottomRightTopCursor[] = { 16, 1, 8, 8,
0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x84,
0x00, 0x44, 0x00, 0x84, 0x01, 0x14, 0x02, 0x2c,
0x34, 0x40, 0x28, 0x80, 0x21, 0x00, 0x22, 0x00,
0x21, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc,
0x00, 0x7c, 0x00, 0xfc, 0x01, 0xfc, 0x03, 0xec,
0x37, 0xc0, 0x3f, 0x80, 0x3f, 0x00, 0x3e, 0x00,
0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00 };
const unsigned char kRotateLCursor[] = { 16, 1, 8, 8,
0x01, 0x80, 0x03, 0x40, 0x0c, 0x20, 0x10, 0x10,
0x23, 0x20, 0x25, 0x40, 0x49, 0x80, 0x48, 0x00,
0x48, 0x00, 0x49, 0x80, 0x25, 0x40, 0x23, 0x20,
0x10, 0x10, 0x0c, 0x20, 0x03, 0x40, 0x01, 0x80,
0x01, 0x80, 0x03, 0xc0, 0x0f, 0xe0, 0x1f, 0xf0,
0x3f, 0xe0, 0x3d, 0xc0, 0x79, 0x80, 0x78, 0x00,
0x78, 0x00, 0x79, 0x80, 0x3d, 0xc0, 0x3f, 0xe0,
0x1f, 0xf0, 0x0f, 0xe0, 0x03, 0xc0, 0x01, 0x80 };
const unsigned char kRotateLBCursor[] = { 16, 1, 8, 8,
0x00, 0x00, 0x3f, 0x00, 0x21, 0x00, 0x11, 0x00,
0x21, 0x00, 0x25, 0x00, 0x4b, 0x00, 0x48, 0x00,
0x48, 0x7e, 0x48, 0x42, 0x24, 0x22, 0x23, 0xc2,
0x10, 0x0a, 0x0c, 0x36, 0x03, 0xc0, 0x00, 0x00,
0x00, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x1f, 0x00,
0x3f, 0x00, 0x3f, 0x00, 0x7b, 0x00, 0x78, 0x00,
0x78, 0x7e, 0x78, 0x7e, 0x3c, 0x3e, 0x3f, 0xfe,
0x1f, 0xfe, 0x0f, 0xf6, 0x03, 0xc0, 0x00, 0x00 };
const unsigned char kRotateBCursor[] = { 16, 1, 8, 8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x08, 0x28, 0x14, 0x44, 0x22,
0x82, 0x41, 0xce, 0x73, 0x48, 0x12, 0x24, 0x24,
0x23, 0xc4, 0x10, 0x08, 0x0c, 0x30, 0x03, 0xc0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x08, 0x38, 0x1c, 0x7c, 0x3e,
0xfe, 0x7f, 0xfe, 0x7f, 0x78, 0x1e, 0x3c, 0x3c,
0x3f, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x03, 0xc0 };
const unsigned char kRotateRBCursor[] = { 16, 1, 8, 8,
0x00, 0x00, 0x00, 0xfc, 0x00, 0x84, 0x00, 0x88,
0x00, 0x84, 0x00, 0xa4, 0x00, 0xd2, 0x00, 0x12,
0x7e, 0x12, 0x42, 0x12, 0x44, 0x24, 0x43, 0xc4,
0x50, 0x08, 0x6c, 0x30, 0x03, 0xc0, 0x00, 0x00,
0x00, 0x00, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xf8,
0x00, 0xfc, 0x00, 0xfc, 0x00, 0xde, 0x00, 0x1e,
0x7e, 0x1e, 0x7e, 0x1e, 0x7c, 0x3c, 0x7f, 0xfc,
0x7f, 0xf8, 0x6f, 0xf0, 0x03, 0xc0, 0x00, 0x00 };
const unsigned char kRotateRCursor[] = { 16, 1, 8, 8,
0x00, 0xc0, 0x01, 0x60, 0x02, 0x18, 0x04, 0x04,
0x02, 0x62, 0x01, 0x52, 0x00, 0xc9, 0x00, 0x09,
0x00, 0x09, 0x00, 0xc9, 0x01, 0x52, 0x02, 0x62,
0x04, 0x04, 0x02, 0x18, 0x01, 0x60, 0x00, 0xc0,
0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf8, 0x07, 0xfc,
0x03, 0xfe, 0x01, 0xde, 0x00, 0xcf, 0x00, 0x0f,
0x00, 0x0f, 0x00, 0xcf, 0x01, 0xde, 0x03, 0xfe,
0x07, 0xfc, 0x03, 0xf8, 0x01, 0xe0, 0x00, 0xc0 };
const unsigned char kRotateRTCursor[] = { 16, 1, 8, 8,
0x00, 0x00, 0x03, 0xc0, 0x6c, 0x30, 0x50, 0x08,
0x43, 0xc4, 0x44, 0x24, 0x42, 0x12, 0x7e, 0x12,
0x00, 0x12, 0x00, 0xd2, 0x00, 0xa4, 0x00, 0x84,
0x00, 0x88, 0x00, 0x84, 0x00, 0xfc, 0x00, 0x00,
0x00, 0x00, 0x03, 0xc0, 0x6f, 0xf0, 0x7f, 0xf8,
0x7f, 0xfc, 0x7c, 0x3c, 0x7e, 0x1e, 0x7e, 0x1e,
0x00, 0x1e, 0x00, 0xde, 0x00, 0xfc, 0x00, 0xfc,
0x00, 0xf8, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0x00 };
const unsigned char kRotateTCursor[] = { 16, 1, 8, 8,
0x03, 0xc0, 0x0c, 0x30, 0x10, 0x08, 0x23, 0xc4,
0x24, 0x24, 0x48, 0x12, 0xce, 0x73, 0x82, 0x41,
0x44, 0x22, 0x28, 0x14, 0x10, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xc0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc,
0x3c, 0x3c, 0x78, 0x1e, 0xfe, 0x7f, 0xfe, 0x7f,
0x7c, 0x3e, 0x38, 0x1c, 0x10, 0x08, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const unsigned char kRotateLTCursor[] = { 16, 1, 8, 8,
0x00, 0x00, 0x03, 0xc0, 0x0c, 0x36, 0x10, 0x0a,
0x23, 0xc2, 0x24, 0x22, 0x48, 0x42, 0x48, 0x7e,
0x48, 0x00, 0x4b, 0x00, 0x25, 0x00, 0x21, 0x00,
0x11, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xc0, 0x0f, 0xf6, 0x1f, 0xfe,
0x3f, 0xfe, 0x3c, 0x3e, 0x78, 0x7e, 0x78, 0x7e,
0x78, 0x00, 0x7b, 0x00, 0x3f, 0x00, 0x3f, 0x00,
0x1f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00 };
// ------------- Path cursors
const unsigned char kPathNewCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x20, 0x08, 0x50, 0x09, 0x54,
0x08, 0x88, 0x03, 0x06, 0x04, 0x01, 0x03, 0x06,
0x00, 0x88, 0x01, 0x54, 0x00, 0x50, 0x00, 0x20,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0xa0, 0x1c, 0x70, 0x1d, 0x74,
0x1c, 0xf8, 0x03, 0xfe, 0x07, 0xff, 0x03, 0xfe,
0x00, 0xf8, 0x01, 0x74, 0x00, 0x70, 0x00, 0x20 };
const unsigned char kPathAddCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08, 0x78,
0x08, 0x48, 0x01, 0xce, 0x01, 0x02, 0x01, 0x02,
0x01, 0xce, 0x00, 0x48, 0x00, 0x78, 0x00, 0x00,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0x80, 0x1c, 0x00, 0x1c, 0x78,
0x1c, 0x78, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe,
0x01, 0xfe, 0x00, 0x78, 0x00, 0x78, 0x00, 0x00 };
const unsigned char kPathRemoveCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
0x08, 0x00, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x02,
0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0x80, 0x1c, 0x00, 0x1c, 0x00,
0x1c, 0x00, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe,
0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const unsigned char kPathInsertCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x00, 0x08, 0x00, 0x09, 0xf0,
0x09, 0x10, 0x01, 0x10, 0x00, 0xa0, 0x00, 0x40,
0x1f, 0x1f, 0x11, 0x11, 0x1f, 0x1f, 0x00, 0x00,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0x80, 0x1c, 0x00, 0x1d, 0xf0,
0x1d, 0xf0, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x40,
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x00 };
const unsigned char kPathMoveCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x20, 0x08, 0x50, 0x08, 0x88,
0x09, 0x74, 0x02, 0x8a, 0x04, 0x89, 0x02, 0x8a,
0x01, 0x74, 0x00, 0x88, 0x00, 0x50, 0x00, 0x20,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0xa0, 0x1c, 0x70, 0x1c, 0xf8,
0x1d, 0x74, 0x03, 0x8e, 0x07, 0x8f, 0x03, 0x8e,
0x01, 0x74, 0x00, 0xf8, 0x00, 0x70, 0x00, 0x20 };
const unsigned char kPathCloseCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x00, 0x08, 0x70, 0x08, 0x88,
0x09, 0x04, 0x02, 0x72, 0x02, 0x52, 0x02, 0x72,
0x01, 0x04, 0x00, 0x88, 0x00, 0x70, 0x00, 0x00,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0x80, 0x1c, 0x70, 0x1c, 0xf8,
0x1d, 0xfc, 0x03, 0xfe, 0x03, 0xde, 0x03, 0xfe,
0x01, 0xfc, 0x00, 0xf8, 0x00, 0x70, 0x00, 0x00 };
const unsigned char kPathSharpCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
0x08, 0x00, 0x01, 0x8c, 0x02, 0x52, 0x02, 0x22,
0x01, 0x04, 0x00, 0x88, 0x00, 0x50, 0x00, 0x20,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0x80, 0x1c, 0x00, 0x1c, 0x00,
0x1c, 0x00, 0x01, 0x8c, 0x03, 0xde, 0x03, 0xfe,
0x01, 0xfc, 0x00, 0xf8, 0x00, 0x70, 0x00, 0x20 };
const unsigned char kPathSelectCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
0x09, 0x55, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x01, 0x01, 0x00, 0x00, 0x01, 0x55, 0x00, 0x00,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0x80, 0x1c, 0x00, 0x1c, 0x00,
0x1d, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0x00, 0x00 };
// -------------------
const unsigned char kEllipseCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x00, 0x08, 0x78, 0x09, 0x86,
0x09, 0x02, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x01, 0x02, 0x01, 0x86, 0x00, 0x78,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0x80, 0x1c, 0x78, 0x1d, 0xfe,
0x1d, 0xfe, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff,
0x03, 0xff, 0x01, 0xfe, 0x01, 0xfe, 0x00, 0x78 };
const unsigned char kRectCursor[] = { 16, 1, 4, 4,
0x08, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00,
0xe3, 0x80, 0x00, 0x00, 0x08, 0x00, 0x08, 0x00,
0x0b, 0xff, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x03, 0xff,
0x1c, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0xe3, 0x80,
0xe3, 0x80, 0xe3, 0x80, 0x1c, 0x00, 0x1c, 0x00,
0x1f, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff,
0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff };
#endif // CURSORS_H
@@ -0,0 +1,86 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Panel.h"
#include <stdio.h>
#include <InterfaceDefs.h>
#include <Message.h>
#include <MessageFilter.h>
class EscapeFilter : public BMessageFilter {
public:
EscapeFilter(Panel* target)
: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
fPanel(target)
{
}
virtual ~EscapeFilter()
{
}
virtual filter_result Filter(BMessage* message, BHandler** target)
{
filter_result result = B_DISPATCH_MESSAGE;
switch (message->what) {
case B_KEY_DOWN:
case B_UNMAPPED_KEY_DOWN: {
uint32 key;
if (message->FindInt32("raw_char", (int32*)&key) >= B_OK) {
if (key == B_ESCAPE) {
result = B_SKIP_MESSAGE;
fPanel->Cancel();
}
}
break;
}
default:
break;
}
return result;
}
private:
Panel* fPanel;
};
// constructor
Panel::Panel(BRect frame, const char* title,
window_type type, uint32 flags,
uint32 workspace)
: BWindow(frame, title, type, flags, workspace)
{
_InstallFilter();
}
// constructor
Panel::Panel(BRect frame, const char* title,
window_look look, window_feel feel,
uint32 flags, uint32 workspace)
: BWindow(frame, title, look, feel, flags, workspace)
{
_InstallFilter();
}
// destructor
Panel::~Panel()
{
}
// MessageReceived
void
Panel::Cancel()
{
PostMessage(B_QUIT_REQUESTED);
}
// _InstallFilter
void
Panel::_InstallFilter()
{
AddCommonFilter(new EscapeFilter(this));
}
@@ -0,0 +1,37 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef PANEL_H
#define PANEL_H
#include <Window.h>
class Panel : public BWindow {
public:
Panel(BRect frame,
const char* title,
window_type type,
uint32 flags,
uint32 workspace = B_CURRENT_WORKSPACE);
Panel(BRect frame,
const char* title,
window_look look,
window_feel feel,
uint32 flags,
uint32 workspace = B_CURRENT_WORKSPACE);
virtual ~Panel();
// Panel
virtual void Cancel();
private:
void _InstallFilter();
};
#endif // PANEL_H
@@ -0,0 +1,238 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "InputSlider.h"
#include <stdio.h>
#include <Message.h>
#include <MessageFilter.h>
#include "NummericalTextView.h"
// MouseDownFilter
class NumericInputFilter : public BMessageFilter {
public:
NumericInputFilter(InputSlider* slider);
virtual filter_result Filter(BMessage*, BHandler** target);
private:
InputSlider* fSlider;
};
// constructor
NumericInputFilter::NumericInputFilter(InputSlider* slider)
: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
fSlider(slider)
{
}
// Filter
filter_result
NumericInputFilter::Filter(BMessage* msg, BHandler** target)
{
filter_result result = B_DISPATCH_MESSAGE;
switch (msg->what)
{
case B_KEY_DOWN:
case B_KEY_UP:
{
msg->PrintToStream();
const char *string;
if (msg->FindString("bytes", &string) == B_OK) {
while (*string != 0) {
if (*string < '0' || *string > '9') {
if (*string != '-') {
result = B_SKIP_MESSAGE;
}
}
string++;
}
}
break;
}
default:
break;
}
/*
if (fWindow) {
if (BView* view = dynamic_cast<BView*>(*target)) {
BPoint point;
if (message->FindPoint("where", &point) == B_OK) {
if (!fWindow->Frame().Contains(view->ConvertToScreen(point)))
*target = fWindow;
}
}
}*/
return result;
}
// constructor
InputSlider::InputSlider(const char* name, const char* label,
BMessage* model, BHandler* target,
int32 min, int32 max, int32 value,
const char* formatString)
: PopupSlider(name, label, model, target, min, max, value, formatString),
fTextView(new NummericalTextView(BRect(0, 0 , 20, 20),
"input",
BRect(5, 5, 15, 15),
B_FOLLOW_NONE,
B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE))
//fTextViewFilter(dynamic_cast<BMessageFilter*>(new NumericInputFilter(this)))
{
// prepare fTextView
fTextView->SetWordWrap(false);
fTextView->SetViewColor(255,255,255,0);
fTextView->SetValue(Value());
//fTextView->AddFilter(fTextViewFilter);
AddChild(fTextView);
}
// destructor
InputSlider::~InputSlider()
{
//delete fTextViewFilter;
}
// layout
BRect
InputSlider::layout(BRect frame)
{
PopupSlider::layout(frame);
frame = SliderFrame();
frame.right -= 10.0;
frame.InsetBy(2, 2);
fTextView->MoveTo(frame.LeftTop());
fTextView->ResizeTo(frame.Width(), frame.Height());
BRect textRect(fTextView->Bounds());
textRect.InsetBy(1, 1);
fTextView->SetTextRect(textRect);
fTextView->SetAlignment(B_ALIGN_CENTER);
return Frame();
}
// MouseDown
void
InputSlider::MouseDown(BPoint where)
{
if (fTextView->Frame().Contains(where))
return;
fTextView->MakeFocus(true);
if (SliderFrame().Contains(where)) {
SetValue(fTextView->IntValue());
PopupSlider::MouseDown(where);
}
}
// SetEnabled
void
InputSlider::SetEnabled(bool enable)
{
PopupSlider::SetEnabled(enable);
// fTextView->SetEnabled(enable);
}
// ValueChanged
void
InputSlider::ValueChanged(int32 newValue)
{
PopupSlider::ValueChanged(newValue);
// change fTextView's value
if (LockLooper()) {
fTextView->SetValue(Value());
UnlockLooper();
}
}
// DrawSlider
void
InputSlider::DrawSlider(BRect frame, bool enabled)
{
rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color lightShadow;
rgb_color midShadow;
rgb_color darkShadow;
rgb_color light;
if (enabled) {
lightShadow = tint_color(background, B_DARKEN_1_TINT);
midShadow = tint_color(background, B_DARKEN_2_TINT);
darkShadow = tint_color(background, B_DARKEN_4_TINT);
light = tint_color(background, B_LIGHTEN_MAX_TINT);
} else {
lightShadow = background;
midShadow = tint_color(background, B_DARKEN_1_TINT);
darkShadow = tint_color(background, B_DARKEN_2_TINT);
light = tint_color(background, B_LIGHTEN_1_TINT);
}
// frame around text view
BRect r(frame);
BeginLineArray(16);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), lightShadow);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), lightShadow);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), light);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), light);
r = fTextView->Frame().InsetByCopy(-1, -1);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), darkShadow);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), darkShadow);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), background);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), background);
r.left = r.right + 1;
r.right = frame.right - 1;
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top + 1.0), midShadow);
AddLine(BPoint(r.left, r.top),
BPoint(r.right, r.top), darkShadow);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), midShadow);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), midShadow);
r.InsetBy(1, 1);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), light);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), light);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), lightShadow);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), lightShadow);
EndLineArray();
r.InsetBy(1, 1);
SetLowColor(background);
FillRect(r, B_SOLID_LOW);
}
@@ -0,0 +1,47 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef INPUT_SLIDER_H
#define INPUT_SLIDER_H
#include "PopupSlider.h"
class NummericalTextView;
class BMessageFilter;
class InputSlider : public PopupSlider {
public:
InputSlider(const char* name = NULL,
const char* label = NULL,
BMessage* model = NULL,
BHandler* target = NULL,
int32 min = 0,
int32 max = 100,
int32 value = 0,
const char* formatString = "%ld");
virtual ~InputSlider();
// MView
virtual BRect layout(BRect frame);
// BView
virtual void MouseDown(BPoint where);
// PopupSlider
void SetEnabled(bool enabled);
// override this to take some action
virtual void ValueChanged(int32 newValue);
virtual void DrawSlider(BRect frame, bool enabled);
private:
NummericalTextView* fTextView;
BMessageFilter* fTextViewFilter;
};
#endif // INPUT_SLIDER_H
@@ -0,0 +1,153 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#include "PopupControl.h"
#include <stdio.h>
#include <Message.h>
#include <Screen.h>
#include "PopupView.h"
#include "PopupWindow.h"
// constructor
PopupControl::PopupControl(const char* name, PopupView* child)
: BView(BRect(0.0f, 0.0f, 10.0f, 10.0f),
name, B_FOLLOW_NONE, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
fPopupWindow(NULL),
fPopupChild(child),
fHPopupAlignment(0.5),
fVPopupAlignment(0.5)
{
}
// destructor
PopupControl::~PopupControl()
{
}
// MessageReceived
void
PopupControl::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_POPUP_SHOWN:
PopupShown();
break;
case MSG_POPUP_HIDDEN:
bool canceled;
if (message->FindBool("canceled", &canceled) != B_OK)
canceled = true;
PopupHidden(canceled);
HidePopup();
break;
default:
BView::MessageReceived(message);
break;
}
}
// SetPopupLocation
void
PopupControl::SetPopupAlignment(float hPopupAlignment, float vPopupAlignment)
{
fHPopupAlignment = hPopupAlignment;
fVPopupAlignment = vPopupAlignment;
}
// SetPopupLocation
//
// overrides Alignment
void
PopupControl::SetPopupLocation(BPoint leftTop)
{
fPopupLeftTop = leftTop;
fHPopupAlignment = -1.0;
fVPopupAlignment = -1.0;
}
// ShowPopup
void
PopupControl::ShowPopup(BPoint* offset)
{
if (!fPopupWindow) {
fPopupWindow = new PopupWindow(fPopupChild, this);
fPopupWindow->RecalcSize();
BRect frame(fPopupWindow->Frame());
BPoint leftLocation;
if (fHPopupAlignment >= 0.0 && fVPopupAlignment >= 0.0) {
leftLocation = ConvertToScreen(Bounds().LeftTop());
leftLocation.x -= fPopupWindow->Frame().Width() + 1.0;
leftLocation.y -= fPopupWindow->Frame().Height() + 1.0;
float totalWidth = Bounds().Width() + fPopupWindow->Frame().Width();
float totalHeight = Bounds().Height() + fPopupWindow->Frame().Height();
leftLocation.x += fHPopupAlignment * totalWidth;
leftLocation.y += fHPopupAlignment * totalHeight;
} else
leftLocation = ConvertToScreen(fPopupLeftTop);
frame.OffsetTo(leftLocation);
BScreen screen(fPopupWindow);
BRect dest(screen.Frame());
// check if too big
if (frame.Width() > dest.Width())
frame.right = frame.left + dest.Width();
if (frame.Height() > dest.Height())
frame.bottom = frame.top + dest.Height();
// check if out of screen
float hOffset = 0.0;
float vOffset = 0.0;
if (frame.bottom > dest.bottom)
vOffset = dest.bottom - frame.bottom;
if (frame.top < dest.top)
vOffset = dest.top - frame.top;
if (frame.right > dest.right)
hOffset = dest.right - frame.right;
if (frame.left < dest.left)
hOffset = dest.left - frame.left;
// finally move/resize our popup window
frame.OffsetBy(hOffset, vOffset);
if (offset) {
offset->x += hOffset;
offset->y += vOffset;
}
fPopupWindow->MoveTo(frame.LeftTop());
fPopupWindow->ResizeTo(frame.Width(), frame.Height());
fPopupWindow->Show();
}
}
// HidePopup
void
PopupControl::HidePopup()
{
if (fPopupWindow) {
fPopupWindow->Lock();
fPopupChild->SetPopupWindow(NULL);
fPopupWindow->RemoveChild(fPopupChild);
fPopupWindow->Quit();
fPopupWindow = NULL;
}
}
// PopupShown
void
PopupControl::PopupShown()
{
}
// PopupHidden
void
PopupControl::PopupHidden(bool canceled)
{
}
@@ -0,0 +1,56 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#ifndef POPUP_CONTROL_H
#define POPUP_CONTROL_H
#include <View.h>
#include <layout.h>
class PopupView;
class PopupWindow;
class PopupControl : public MView, public BView {
public:
PopupControl(const char* name,
PopupView* child);
virtual ~PopupControl();
// MView
virtual minimax layoutprefs() = 0;
virtual BRect layout(BRect frame) = 0;
// BHandler
virtual void MessageReceived(BMessage* message);
// PopupControl
void SetPopupAlignment(float hPopupAlignment,
float vPopupAlignment);
void SetPopupLocation(BPoint leftTop);
// "offset" will be modified if the popup
// window had to be moved in order to
// show entirely on screen
virtual void ShowPopup(BPoint* offset = NULL);
virtual void HidePopup();
virtual void PopupShown();
virtual void PopupHidden(bool canceled);
private:
PopupWindow* fPopupWindow;
PopupView* fPopupChild;
float fHPopupAlignment;
float fVPopupAlignment;
BPoint fPopupLeftTop;
};
#endif // POPUP_CONTROL_H
@@ -0,0 +1,454 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#include "PopupSlider.h"
#include <math.h>
#include <stdio.h>
#include <Message.h>
#include <MDividable.h>
#include <MWindow.h>
#include "SliderView.h"
// constructor
PopupSlider::PopupSlider(const char* name, const char* label,
BMessage* model, BHandler* target,
int32 min, int32 max, int32 value,
const char* formatString)
: PopupControl(name, fSlider = new SliderView(this, min, max, value,
formatString)),
MDividable(),
fModel(model),
fPressModel(NULL),
fReleaseModel(NULL),
fTarget(target),
fLabel(label),
fSliderButtonRect(0.0, 0.0, -1.0, -1.0),
fEnabled(true),
fTracking(false)
{
SetViewColor(B_TRANSPARENT_32_BIT);
}
// destructor
PopupSlider::~PopupSlider()
{
delete fModel;
if (BWindow* window = fSlider->Window()) {
window->Lock();
window->RemoveChild(fSlider);
window->Unlock();
}
delete fSlider;
}
// layoutprefs
minimax
PopupSlider::layoutprefs()
{
BFont font;
GetFont(&font);
font_height fh;
font.GetHeight(&fh);
float labelHeight = 2.0 + ceilf(fh.ascent + fh.descent) + 2.0;
float sliderWidth, sliderHeight;
SliderView::GetSliderButtonDimensions(Max(), FormatString(), &font,
sliderWidth, sliderHeight);
float height = labelHeight > sliderHeight + 2.0 ?
labelHeight : sliderHeight + 2.0;
float minLabelWidth = LabelWidth();
if (rolemodel)
labelwidth = rolemodel->LabelWidth();
labelwidth = minLabelWidth > labelwidth ? minLabelWidth : labelwidth;
fSliderButtonRect.left = labelwidth;
fSliderButtonRect.right = fSliderButtonRect.left + sliderWidth + 2.0;
fSliderButtonRect.top = floorf(height / 2.0 - (sliderHeight + 2.0) / 2.0);
fSliderButtonRect.bottom = fSliderButtonRect.top + sliderHeight + 2.0;
fSliderButtonRect.OffsetTo(Bounds().right - fSliderButtonRect.Width(),
fSliderButtonRect.top);
mpm.mini.x = labelwidth + fSliderButtonRect.Width() + 1.0;
mpm.maxi.x = 10000.0;
mpm.mini.y = mpm.maxi.y = height + 1.0;
mpm.weight = 1.0;
return mpm;
}
// layout
BRect
PopupSlider::layout(BRect frame)
{
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
fSliderButtonRect.OffsetTo(Bounds().right - fSliderButtonRect.Width(),
fSliderButtonRect.top);
return Frame();
}
// MessageReceived
void
PopupSlider::MessageReceived(BMessage* message)
{
switch (message->what) {
default:
PopupControl::MessageReceived(message);
break;
}
}
// AttachedToWindow
void
PopupSlider::AttachedToWindow()
{
fSliderButtonRect.OffsetTo(Bounds().right - fSliderButtonRect.Width(),
fSliderButtonRect.top);
PopupControl::AttachedToWindow();
}
// Draw
void
PopupSlider::Draw(BRect updateRect)
{
bool enabled = IsEnabled();
rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color black;
if (enabled) {
black = tint_color(background, B_DARKEN_MAX_TINT);
} else {
black = tint_color(background, B_DISABLED_LABEL_TINT);
}
// draw label
BRect r(Bounds());
r.right = fSliderButtonRect.left - 1.0;
font_height fh;
GetFontHeight(&fh);
BPoint textPoint(0.0, (r.top + r.bottom) / 2.0 + fh.ascent / 2.0);
SetLowColor(background);
SetHighColor(black);
FillRect(r, B_SOLID_LOW);
DrawString(fLabel.String(), textPoint);
// draw slider button
DrawSlider(fSliderButtonRect, enabled);
}
// MouseDown
void
PopupSlider::MouseDown(BPoint where)
{
if (fEnabled && fSliderButtonRect.Contains(where) &&
!fSlider->LockLooper()) {
SetPopupLocation(BPoint(fSliderButtonRect.left + 1.0
- fSlider->ButtonOffset(),
-5.0));
where.x -= fSliderButtonRect.left + 1.0;
fSlider->SetDragOffset(where.x);
// just to be on the safe side (avoid a dead lock)
fTracking = true;
ShowPopup(&where);
// fSlider->SetDragOffset(where.x);
}
}
// PopupShown
void
PopupSlider::PopupShown()
{
TriggerValueChanged(fPressModel);
fTracking = true;
}
// PopupHidden
void
PopupSlider::PopupHidden(bool canceled)
{
TriggerValueChanged(fReleaseModel);
fTracking = false;
}
// SetValue
void
PopupSlider::SetValue(int32 value)
{
if (!fTracking) {
/* if (fSlider->LockLooper()) {
fSlider->SetValue(value);
fSlider->UnlockLooper();
} else*/
if (value != Value()) {
fSlider->SetValue(value);
if (LockLooperWithTimeout(0) >= B_OK) {
Invalidate();
UnlockLooper();
}
}
} else
ValueChanged(value);
}
// Value
int32
PopupSlider::Value() const
{
int32 value = 0;
/* if (fSlider->LockLooper()) {
value = fSlider->Value();
fSlider->UnlockLooper();
} else*/
value = fSlider->Value();
return value;
}
// SetEnabled
void
PopupSlider::SetEnabled(bool enable)
{
if (enable != fEnabled) {
fEnabled = enable;
if (LockLooper()) {
Invalidate();
UnlockLooper();
}
}
}
// SetEnabled
bool
PopupSlider::IsEnabled() const
{
return fEnabled;
}
// TriggerValueChanged
void
PopupSlider::TriggerValueChanged(const BMessage* message) const
{
if (message && fTarget) {
BMessage msg(*message);
msg.AddInt64("be:when", system_time());
msg.AddInt32("be:value", Value());
msg.AddPointer("be:source", (void*)this);
if (BLooper* looper = fTarget->Looper())
looper->PostMessage(&msg, fTarget);
}
}
// IsTracking
bool
PopupSlider::IsTracking() const
{
return fTracking;
}
// ValueChanged
void
PopupSlider::ValueChanged(int32 newValue)
{
TriggerValueChanged(fModel);
}
// DrawSlider
void
PopupSlider::DrawSlider(BRect frame, bool enabled)
{
rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color lightShadow;
rgb_color darkShadow;
if (enabled) {
lightShadow = tint_color(background, B_DARKEN_2_TINT);
darkShadow = tint_color(background, B_DARKEN_4_TINT);
} else {
lightShadow = tint_color(background, B_DARKEN_1_TINT);
darkShadow = tint_color(background, B_DARKEN_2_TINT);
}
BeginLineArray(4);
AddLine(BPoint(frame.left, frame.bottom),
BPoint(frame.left, frame.top), lightShadow);
AddLine(BPoint(frame.left + 1.0, frame.top),
BPoint(frame.right, frame.top), lightShadow);
AddLine(BPoint(frame.right, frame.top + 1.0),
BPoint(frame.right, frame.bottom), darkShadow);
AddLine(BPoint(frame.right - 1.0, frame.bottom),
BPoint(frame.left + 1.0, frame.bottom), darkShadow);
EndLineArray();
frame.InsetBy(1.0, 1.0);
SliderView::DrawSliderButton(this, frame, Value(), FormatString(), enabled);
}
// Scale
float
PopupSlider::Scale(float ratio) const
{
return ratio;
}
// DeScale
float
PopupSlider::DeScale(float ratio) const
{
return ratio;
}
// SetMessage
void
PopupSlider::SetMessage(BMessage* message)
{
delete fModel;
fModel = message;
}
// SetPressedMessage
void
PopupSlider::SetPressedMessage(BMessage* message)
{
delete fPressModel;
fPressModel = message;
}
// SetReleasedMessage
void
PopupSlider::SetReleasedMessage(BMessage* message)
{
delete fReleaseModel;
fReleaseModel = message;
}
// SetMin
void
PopupSlider::SetMin(int32 min)
{
/* if (fSlider->LockLooper()) {
fSlider->SetMin(min);
fSlider->UnlockLooper();
} else*/
fSlider->SetMin(min);
}
// Min
int32
PopupSlider::Min() const
{
int32 value = 0;
/* if (fSlider->LockLooper()) {
value = fSlider->Min();
fSlider->UnlockLooper();
} else*/
value = fSlider->Min();
return value;
}
// SetMax
void
PopupSlider::SetMax(int32 max)
{
/* if (fSlider->LockLooper()) {
fSlider->SetMax(max);
fSlider->UnlockLooper();
} else*/
fSlider->SetMax(max);
}
// Max
int32
PopupSlider::Max() const
{
int32 value = 0;
/* if (fSlider->LockLooper()) {
value = fSlider->Max();
fSlider->UnlockLooper();
} else*/
value = fSlider->Max();
return value;
}
// SetLabel
void
PopupSlider::SetLabel(const char* label)
{
fLabel.SetTo(label);
Invalidate();
}
// Label
const char*
PopupSlider::Label() const
{
return fLabel.String();
}
// LabelWidth
float
PopupSlider::LabelWidth()
{
return _MinLabelWidth();
}
// StringForValue
const char*
PopupSlider::StringForValue(int32 value)
{
return NULL;
}
// MaxValueStringWidth
float
PopupSlider::MaxValueStringWidth()
{
return 0.0;
}
// SetFormatString
void
PopupSlider::SetFormatString(const char* formatString)
{
/* if (fSlider->LockLooper()) {
fSlider->SetFormatString(formatString);
fSlider->UnlockLooper();
} else*/
fSlider->SetFormatString(formatString);
}
// FormatString
const char*
PopupSlider::FormatString() const
{
return fSlider->FormatString();
}
// _MinLabelWidth
float
PopupSlider::_MinLabelWidth() const
{
return ceilf(StringWidth(fLabel.String())) + 5.0;
}
/*
// StringForValue
const char*
PercentSlider::StringForValue(int32 value)
{
BString string;
string << (value * 100) / Max() << "%";
return
}
*/
@@ -0,0 +1,122 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#ifndef POPUP_SLIDER_H
#define POPUP_SLIDER_H
#include <String.h>
#include "MDividable.h"
#include "PopupControl.h"
class MDividable;
class SliderView;
class PopupSlider : public PopupControl,
public MDividable {
public:
PopupSlider(const char* name = NULL,
const char* label = NULL,
BMessage* model = NULL,
BHandler* target = NULL,
int32 min = 0,
int32 max = 100,
int32 value = 0,
const char* formatString = "%ld");
virtual ~PopupSlider();
// MView
virtual minimax layoutprefs();
virtual BRect layout(BRect frame);
// BHandler
virtual void MessageReceived(BMessage* message);
// BView
virtual void AttachedToWindow();
virtual void Draw(BRect updateRect);
virtual void MouseDown(BPoint where);
// PopupControl
virtual void PopupShown();
virtual void PopupHidden(bool canceled);
// PopupSlider
void SetValue(int32 value);
int32 Value() const;
void SetEnabled(bool enabled);
bool IsEnabled() const;
void TriggerValueChanged(const BMessage* message) const;
bool IsTracking() const;
// override this to take some action
virtual void ValueChanged(int32 newValue);
virtual void DrawSlider(BRect frame, bool enabled);
virtual float Scale(float ratio) const;
virtual float DeScale(float ratio) const;
void SetMessage(BMessage* message);
const BMessage* Message() const
{ return fModel; }
void SetPressedMessage(BMessage* message);
void SetReleasedMessage(BMessage* message);
virtual void SetMin(int32 min);
int32 Min() const;
virtual void SetMax(int32 max);
int32 Max() const;
virtual void SetLabel(const char* label);
const char* Label() const;
// support for MDividable
virtual float LabelWidth();
// TODO: change design to implement these features:
// you can override this function
// to have costum value strings
virtual const char* StringForValue(int32 value);
// but you should override this
// as well to make sure the width
// of the slider is calculated properly
virtual float MaxValueStringWidth();
virtual void SetFormatString(const char* formatString);
const char* FormatString() const;
protected:
BRect SliderFrame() const
{ return fSliderButtonRect; }
private:
float _MinLabelWidth() const;
SliderView* fSlider;
BMessage* fModel;
BMessage* fPressModel;
BMessage* fReleaseModel;
BHandler* fTarget;
BString fLabel;
BRect fSliderButtonRect;
bool fEnabled;
bool fTracking;
};
/*
class PercentSlider : public PopupSlider {
public:
virtual const char* StringForValue(int32 value);
virtual float MaxValueStringWidth();
};
*/
#endif // POPUP_SLIDER_H
@@ -0,0 +1,45 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#include "PopupView.h"
#include "PopupWindow.h"
// constructor
PopupView::PopupView(const char* name)
: BView(BRect(0.0, 0.0, 10.0, 10.0), name,
B_FOLLOW_NONE, B_WILL_DRAW),
fWindow(NULL)
{
}
// destructor
PopupView::~PopupView()
{
}
// SetPopupWindow
void
PopupView::SetPopupWindow(PopupWindow* window)
{
fWindow = window;
if (fWindow)
SetEventMask(B_POINTER_EVENTS);
else
SetEventMask(0);
}
// PopupDown
void
PopupView::PopupDone(bool canceled)
{
if (fWindow)
fWindow->PopupDone(canceled);
}
@@ -0,0 +1,37 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#ifndef POPUP_VIEW_H
#define POPUP_VIEW_H
#include <View.h>
#include <layout.h>
class PopupWindow;
class PopupView : public MView, public BView {
public:
PopupView(const char* name);
virtual ~PopupView();
// MView
virtual minimax layoutprefs() = 0;
virtual BRect layout(BRect frame) = 0;
virtual void SetPopupWindow(PopupWindow* window);
virtual void PopupDone(bool canceled);
private:
PopupWindow* fWindow;
};
#endif // POPUP_CONTROL_H
@@ -0,0 +1,126 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#include <AppDefs.h>
#include <Message.h>
#include <MessageFilter.h>
#include <View.h>
#include "PopupControl.h"
#include "PopupView.h"
#include "PopupWindow.h"
// MouseDownFilter
class MouseDownFilter : public BMessageFilter {
public:
MouseDownFilter(BWindow* window);
virtual filter_result Filter(BMessage*, BHandler** target);
private:
BWindow* fWindow;
};
// constructor
MouseDownFilter::MouseDownFilter(BWindow* window)
: BMessageFilter(B_MOUSE_DOWN),
fWindow(window)
{
}
// Filter
filter_result
MouseDownFilter::Filter(BMessage* message, BHandler** target)
{
if (fWindow) {
if (BView* view = dynamic_cast<BView*>(*target)) {
BPoint point;
if (message->FindPoint("where", &point) == B_OK) {
if (!fWindow->Frame().Contains(view->ConvertToScreen(point)))
*target = fWindow;
}
}
}
return B_DISPATCH_MESSAGE;
}
// PopupWindow
// constructor
PopupWindow::PopupWindow(PopupView* child, PopupControl* control)
: MWindow(BRect(0.0, 0.0, 10.0, 10.0), "popup",
B_NO_BORDER_WINDOW_LOOK, B_FLOATING_ALL_WINDOW_FEEL,
B_ASYNCHRONOUS_CONTROLS),
fCanceled(true),
fControl(control)
{
AddChild(child);
child->SetPopupWindow(this);
AddCommonFilter(new MouseDownFilter(this));
}
// destructor
PopupWindow::~PopupWindow()
{
}
// MessageReceived
void
PopupWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case B_MOUSE_DOWN:
fCanceled = true;
Hide();
break;
default:
MWindow::MessageReceived(message);
break;
}
}
// Show
void
PopupWindow::Show()
{
if (BLooper *looper = fControl->Looper())
looper->PostMessage(MSG_POPUP_SHOWN, fControl);
MWindow::Show();
}
// Hide
void
PopupWindow::Hide()
{
if (BLooper *looper = fControl->Looper()) {
BMessage msg(MSG_POPUP_HIDDEN);
msg.AddBool("canceled", fCanceled);
looper->PostMessage(&msg, fControl);
}
MWindow::Hide();
}
// QuitRequested
bool
PopupWindow::QuitRequested()
{
return false;
}
// PopupDone
void
PopupWindow::PopupDone(bool canceled)
{
fCanceled = canceled;
Hide();
}
@@ -0,0 +1,44 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#ifndef POPUP_WINDOW_H
#define POPUP_WINDOW_H
#include <MWindow.h>
enum {
MSG_POPUP_SHOWN = 'push',
MSG_POPUP_HIDDEN = 'puhi',
};
class PopupControl;
class PopupView;
class PopupWindow : public MWindow {
public:
PopupWindow(PopupView* child,
PopupControl* control);
virtual ~PopupWindow();
virtual void MessageReceived(BMessage* message);
// MWindow
virtual void Show();
virtual void Hide();
virtual bool QuitRequested();
virtual void PopupDone(bool canceled = true);
private:
bool fCanceled;
PopupControl* fControl;
};
#endif // POPUP_CONTROL_H
@@ -0,0 +1,397 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#include "SliderView.h"
#include <math.h>
#include <stdio.h>
#include <Message.h>
#include <Window.h>
#include "PopupSlider.h"
// constructor
SliderView::SliderView(PopupSlider* target,
int32 min, int32 max, int32 value,
const char* formatString)
: PopupView("slider"),
fTarget(target),
fFormatString(formatString),
fMin(min),
fMax(max),
fValue(value),
fButtonRect(0.0, 0.0, -1.0, -1.0),
fDragOffset(0.0)
{
SetViewColor(B_TRANSPARENT_32_BIT);
if (Max() < Min())
SetMax(Min());
BFont font;
GetFont(&font);
float buttonWidth, buttonHeight;
GetSliderButtonDimensions(Max(), FormatString(), &font,
buttonWidth, buttonHeight);
fButtonRect.right = fButtonRect.left + buttonWidth;
fButtonRect.bottom = fButtonRect.top + buttonHeight;
float size = Max() - Min();
if (size > 200)
size = 200;
ResizeTo(6.0 + fButtonRect.Width() + size + 6.0,
6.0 + fButtonRect.Height() + 6.0);
}
// destructor
SliderView::~SliderView()
{
}
// minimax
minimax
SliderView::layoutprefs()
{
mpm.mini.x = mpm.maxi.x = Bounds().Width() + 1.0;
mpm.mini.y = mpm.maxi.y = Bounds().Height() + 1.0;
mpm.weight = 1.0;
return mpm;
}
// layout
BRect
SliderView::layout(BRect frame)
{
MoveTo(frame.LeftTop());
ResizeTo(frame.Width(), frame.Height());
return Frame();
}
// Draw
void
SliderView::Draw(BRect updateRect)
{
fButtonRect.OffsetTo(ButtonOffset(), 6.0);
rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
rgb_color lightShadow = tint_color(background, B_DARKEN_1_TINT);
rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
BRect r(Bounds());
BeginLineArray(24);
// outer dark line
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), lightShadow);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), lightShadow);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), darkShadow);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), darkShadow);
// second line (raised)
r.InsetBy(1.0, 1.0);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), light);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), light);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), shadow);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), shadow);
// third line (normal)
r.InsetBy(1.0, 1.0);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), background);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), background);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), background);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), background);
// fourth line (normal)
r.InsetBy(1.0, 1.0);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), background);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), background);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), background);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), background);
// fifth line (depressed)
r.InsetBy(1.0, 1.0);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), lightShadow);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), lightShadow);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), light);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), light);
// fifth line (strongly depressed)
r.InsetBy(1.0, 1.0);
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), darkShadow);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), darkShadow);
AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), shadow);
AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), shadow);
EndLineArray();
r.InsetBy(1.0, 1.0);
SetLowColor(lightShadow);
BRect leftOfButton(r.left + 1.0, r.top + 1.0, fButtonRect.left - 2.0, r.bottom);
if (leftOfButton.IsValid())
FillRect(leftOfButton, B_SOLID_LOW);
BRect rightOfButton(fButtonRect.right + 2.0, r.top + 1.0,
r.right, r.bottom);
if (rightOfButton.IsValid())
FillRect(rightOfButton, B_SOLID_LOW);
// inner shadow and knob out lines
BeginLineArray(5);
// shadow
AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), shadow);
AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), shadow);
// at knob
if (fButtonRect.left == 6.0)
AddLine(BPoint(fButtonRect.left - 1.0, r.top),
BPoint(fButtonRect.left - 1.0, r.bottom), darkShadow);
else
AddLine(BPoint(fButtonRect.left - 1.0, r.top),
BPoint(fButtonRect.left - 1.0, r.bottom), shadow);
AddLine(BPoint(fButtonRect.left, fButtonRect.bottom + 1.0),
BPoint(fButtonRect.right + 1.0, fButtonRect.bottom + 1.0), darkShadow);
AddLine(BPoint(fButtonRect.right + 1.0, fButtonRect.bottom),
BPoint(fButtonRect.right + 1.0, fButtonRect.top), darkShadow);
EndLineArray();
DrawSliderButton(this, fButtonRect, fValue, fFormatString.String(), fTarget->IsEnabled());
}
// MouseUp
void
SliderView::MouseUp(BPoint where)
{
PopupDone(false);
fTarget->TriggerValueChanged(fTarget->Message());
}
// MouseMoved
void
SliderView::MouseMoved(BPoint where, uint32 transit, const BMessage* message)
{
uint32 buttons = 0;
if (BMessage* message = Window()->CurrentMessage()) {
if (message->FindInt32("buttons", (int32*)&buttons) < B_OK)
buttons = 0;
}
if (buttons == 0) {
MouseUp(where);
return;
}
SetValue(_ValueAt(where.x - fDragOffset - 6.0));
}
// MessageReceived
void
SliderView::MessageReceived(BMessage* message)
{
switch (message->what) {
default:
PopupView::MessageReceived(message);
break;
}
}
// SetValue
void
SliderView::SetValue(int32 value)
{
if (value < fMin)
value = fMin;
if (value > fMax)
value = fMax;
if (value != fValue) {
fValue = value;
fTarget->SetValue(value);
Invalidate();
}
}
// Value
int32
SliderView::Value() const
{
return fValue;
}
// SetMin
void
SliderView::SetMin(int32 min)
{
if (min != fMax) {
fMin = min;
if (fValue < fMin)
SetValue(fMin);
Invalidate();
}
}
// Min
int32
SliderView::Min() const
{
return fMin;
}
// SetMax
void
SliderView::SetMax(int32 max)
{
if (max != fMax) {
fMax = max;
if (fValue > fMax)
SetValue(fMax);
Invalidate();
}
}
// Max
int32
SliderView::Max() const
{
return fMax;
}
// SetFormatString
void
SliderView::SetFormatString(const char* formatString)
{
// TODO: check if formatString contains "%ld"
fFormatString.SetTo(formatString);
}
// FormatString
const char*
SliderView::FormatString() const
{
return fFormatString.String();
}
// SetDragOffset
void
SliderView::SetDragOffset(float offset)
{
fDragOffset = offset;
}
// ButtonOffset
float
SliderView::ButtonOffset()
{
// float range = Bounds().Width() - 12.0 - fButtonRect.Width() + 1.0;
// return 6.0 + range / (float)(fMax - fMin + 1) * (float)(fValue - fMin);
float ratio = fTarget->DeScale((float)(fValue - fMin) / (float)(fMax - fMin));
return 6.0 + ratio * (Bounds().Width() - 12.0 - fButtonRect.Width());
}
// GetSliderButtonDimensions
void
SliderView::GetSliderButtonDimensions(int32 max, const char* formatString,
BFont* font,
float& width, float& height)
{
if (font) {
char label[256];
sprintf(label, formatString, max);
font_height fh;
font->GetHeight(&fh);
// 4 pixels room on each side,
// 1 pixel room at top and bottom
width = 5.0 + ceilf(font->StringWidth(label)) + 5.0;
height = 2.0 + ceilf(fh.ascent + fh.descent) + 2.0;
}
}
// DrawSliderButton
void
SliderView::DrawSliderButton(BView* v, BRect r, int32 value,
const char* formatString, bool enabled)
{
rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color light;
rgb_color shadow;
rgb_color button;
rgb_color black;
if (enabled) {
light = tint_color(background, B_LIGHTEN_MAX_TINT);
shadow = tint_color(background, B_DARKEN_1_TINT);
button = tint_color(background, B_LIGHTEN_1_TINT);
black = tint_color(background, B_DARKEN_MAX_TINT);
} else {
light = tint_color(background, B_LIGHTEN_1_TINT);
shadow = tint_color(background, 1.1);
button = tint_color(background, 0.8);
black = tint_color(background, B_DISABLED_LABEL_TINT);
}
// border
v->BeginLineArray(4);
v->AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), light);
v->AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), light);
v->AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), shadow);
v->AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), shadow);
v->EndLineArray();
// background & label
r.InsetBy(1.0, 1.0);
char label[256];
sprintf(label, formatString, value);
float width = v->StringWidth(label);
font_height fh;
v->GetFontHeight(&fh);
BPoint textPoint((r.left + r.right) / 2.0 - width / 2.0,
(r.top + r.bottom) / 2.0 + fh.ascent / 2.0);
v->SetHighColor(black);
v->SetLowColor(button);
v->FillRect(r, B_SOLID_LOW);
v->DrawString(label, textPoint);
}
// _ValueAt
int32
SliderView::_ValueAt(float h)
{
/* return fMin + (int32)(((float)(fMax - fMin + 1) * h) /
(Bounds().Width() - fButtonRect.Width() - 12.0));*/
float ratio = h / (Bounds().Width() - fButtonRect.Width() - 12.0);
if (ratio < 0.0)
ratio = 0.0;
if (ratio > 1.0)
ratio = 1.0;
ratio = fTarget->Scale(ratio);
return fMin + (int32)((fMax - fMin + 1) * ratio);
}
@@ -0,0 +1,83 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#ifndef SLIDER_VIEW_H
#define SLIDER_VIEW_H
#include <String.h>
#include "PopupView.h"
class BFont;
class PopupSlider;
class SliderView : public PopupView {
public:
SliderView(PopupSlider* target,
int32 min,
int32 max,
int32 value,
const char* formatString);
virtual ~SliderView();
// MView
virtual minimax layoutprefs();
virtual BRect layout(BRect frame);
// BView
virtual void Draw(BRect updateRect);
virtual void MouseMoved(BPoint where, uint32 transit,
const BMessage* message);
virtual void MouseUp(BPoint where);
// BHandler
virtual void MessageReceived(BMessage* message);
// SliderView
void SetValue(int32 value);
int32 Value() const;
void SetMin(int32 min);
int32 Min() const;
void SetMax(int32 max);
int32 Max() const;
void SetFormatString(const char* formatString);
const char* FormatString() const;
void SetDragOffset(float offset);
float ButtonOffset();
static void GetSliderButtonDimensions(int32 max,
const char* formatString,
BFont* font,
float& width,
float& height);
static void DrawSliderButton(BView* into, BRect frame,
int32 value,
const char* formatString,
bool enabled);
private:
int32 _ValueAt(float h);
PopupSlider* fTarget;
BString fFormatString;
int32 fMin;
int32 fMax;
int32 fValue;
BRect fButtonRect;
float fDragOffset;
};
#endif // SLIDER_VIEW_H
@@ -0,0 +1,61 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
const int32 sBitmapWidth = 14;
const int32 sBitmapHeight = 14;
const color_space sColorSpace = B_RGBA32;
const unsigned char sScrollCornerNormalBits [] = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0x94,0x95,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,
0xd5,0xd6,0xd5,0xff,0xd5,0xda,0xd5,0xff,0xde,0xd6,0xde,0xff,0xd5,0xda,0xd5,0xff,0xd5,0xda,0xd5,0xff,0xde,0xd6,0xde,0xff,0xd5,0xda,0xd5,0xff,0xde,0xd6,0xde,0xff,0xd5,0xda,0xd5,0xff,0xd5,0xd6,0xd5,0xff,0xc5,0xc2,0xc5,0xff,0x94,0x99,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xd6,0xd5,0xff,0xd5,0xda,0xd5,0xff,
0xde,0xd6,0xde,0xff,0xd5,0xda,0xd5,0xff,0xde,0xda,0xde,0xff,0xd5,0xda,0xd5,0xff,0xd5,0xd6,0xd5,0xff,0xde,0xda,0xde,0xff,0xd5,0xda,0xd5,0xff,0xde,0xd6,0xde,0xff,0xbd,0xc2,0xbd,0xff,0x9c,0x99,0x9c,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xd6,0xd5,0xff,0xd5,0xda,0xd5,0xff,0xc5,0xbe,0xc5,0xff,0x6a,0x71,0x6a,0xff,
0x8b,0x85,0x8b,0xff,0x83,0x85,0x83,0xff,0xc5,0xc6,0xc5,0xff,0xd5,0xd6,0xd5,0xff,0xde,0xda,0xde,0xff,0xc5,0xca,0xc5,0xff,0xc5,0xc2,0xc5,0xff,0x94,0x95,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xd6,0xd5,0xff,0xbd,0xc2,0xbd,0xff,0x73,0x71,0x73,0xff,0xff,0xff,0xff,0xff,0xee,0xea,0xee,0xff,0xc5,0xca,0xc5,0xff,
0x83,0x85,0x83,0xff,0xee,0xea,0xee,0xff,0xd5,0xd6,0xd5,0xff,0xde,0xda,0xde,0xff,0xbd,0xc2,0xbd,0xff,0x94,0x95,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xda,0xd5,0xff,0x73,0x6d,0x73,0xff,0xff,0xff,0xff,0xff,0xe6,0xe6,0xe6,0xff,0xd5,0xd6,0xd5,0xff,0xd5,0xda,0xd5,0xff,0xbd,0xba,0xbd,0xff,0x83,0x81,0x83,0xff,
0xe6,0xea,0xe6,0xff,0xde,0xda,0xde,0xff,0xbd,0xc2,0xbd,0xff,0x94,0x95,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xda,0xd5,0xff,0x83,0x81,0x83,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xda,0xd5,0xff,0xd5,0xd6,0xd5,0xff,0xc5,0xc2,0xc5,0xff,0x7b,0x7d,0x7b,0xff,0xff,0xff,0xff,0xff,0xcd,0xca,0xcd,0xff,
0xc5,0xc2,0xc5,0xff,0x94,0x99,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xda,0xd5,0xff,0x83,0x81,0x83,0xff,0xee,0xee,0xee,0xff,0xde,0xde,0xde,0xff,0xd5,0xd6,0xd5,0xff,0xd5,0xd2,0xd5,0xff,0xbd,0xc2,0xbd,0xff,0x62,0x65,0x62,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xc5,0xc2,0xc5,0xff,0x94,0x95,0x94,0xff,
0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xd6,0xd5,0xff,0xb4,0xb6,0xb4,0xff,0x83,0x85,0x83,0xff,0xe6,0xe2,0xe6,0xff,0xcd,0xd2,0xcd,0xff,0xc5,0xc2,0xc5,0xff,0x62,0x65,0x62,0xff,0xff,0xfa,0xff,0xff,0xee,0xee,0xee,0xff,0xd5,0xd6,0xd5,0xff,0xc5,0xc2,0xc5,0xff,0x94,0x99,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,
0xd5,0xd6,0xd5,0xff,0xd5,0xda,0xd5,0xff,0xb4,0xb6,0xb4,0xff,0x83,0x81,0x83,0xff,0x73,0x71,0x73,0xff,0x62,0x65,0x62,0xff,0xff,0xfa,0xff,0xff,0xff,0xff,0xff,0xff,0xd5,0xda,0xd5,0xff,0xcd,0xca,0xcd,0xff,0xc5,0xc2,0xc5,0xff,0x94,0x95,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xd6,0xd5,0xff,0xd5,0xda,0xd5,0xff,
0xde,0xd6,0xde,0xff,0xcd,0xce,0xcd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe6,0xea,0xe6,0xff,0xde,0xda,0xde,0xff,0xc5,0xca,0xc5,0xff,0xd5,0xd6,0xd5,0xff,0xc5,0xc2,0xc5,0xff,0x94,0x99,0x94,0xff,0xff,0xff,0xff,0xff,0xde,0xda,0xde,0xff,0xd5,0xd6,0xd5,0xff,0xc5,0xca,0xc5,0xff,0xde,0xda,0xde,0xff,0xd5,0xd6,0xd5,0xff,
0xcd,0xca,0xcd,0xff,0xd5,0xda,0xd5,0xff,0xd5,0xd6,0xd5,0xff,0xcd,0xca,0xcd,0xff,0xd5,0xda,0xd5,0xff,0xcd,0xca,0xcd,0xff,0xc5,0xc2,0xc5,0xff,0x94,0x95,0x94,0xff,0xde,0xda,0xde,0xff,0xbd,0xc2,0xbd,0xff,0xc5,0xc2,0xc5,0xff,0xbd,0xc2,0xbd,0xff,0xc5,0xc2,0xc5,0xff,0xc5,0xc2,0xc5,0xff,0xbd,0xc2,0xbd,0xff,0xc5,0xc2,0xc5,0xff,
0xbd,0xc2,0xbd,0xff,0xc5,0xc2,0xc5,0xff,0xc5,0xc2,0xc5,0xff,0xbd,0xc2,0xbd,0xff,0xc5,0xc2,0xc5,0xff,0x94,0x95,0x94,0xff,0x9c,0x99,0x9c,0xff,0x94,0x95,0x94,0xff,0x9c,0x99,0x9c,0xff,0x94,0x99,0x94,0xff,0x9c,0x99,0x9c,0xff,0x94,0x95,0x94,0xff,0x9c,0x99,0x9c,0xff,0x94,0x99,0x94,0xff,0x9c,0x99,0x9c,0xff,0x94,0x95,0x94,0xff,
0x9c,0x99,0x9c,0xff,0x94,0x99,0x94,0xff,0x9c,0x99,0x9c,0xff,0x94,0x95,0x94,0xff
};
const unsigned char sScrollCornerPushedBits [] = {
0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0x90,0x90,0x90,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xaa,0xaa,0xaa,0xff,
0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,
0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0xaa,0xaa,0xaa,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xab,0xab,0xab,0xff,0x97,0x97,0x97,0xff,0x59,0x59,0x59,0xff,
0x69,0x69,0x69,0xff,0x69,0x69,0x69,0xff,0x9a,0x9a,0x9a,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xb0,0xb0,0xb0,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xaa,0xaa,0xaa,0xff,0xab,0xab,0xab,0xff,0x97,0x97,0x97,0xff,0x59,0x59,0x59,0xff,0xc9,0xc9,0xc9,0xff,0xb8,0xb8,0xb8,0xff,0xa0,0xa0,0xa0,0xff,
0x69,0x69,0x69,0xff,0xb8,0xb8,0xb8,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0x59,0x59,0x59,0xff,0xc9,0xc9,0xc9,0xff,0xb3,0xb3,0xb3,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0x93,0x93,0x93,0xff,0x66,0x66,0x66,0xff,
0xb8,0xb8,0xb8,0xff,0xaa,0xaa,0xaa,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0x69,0x69,0x69,0xff,0xc9,0xc9,0xc9,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0x9a,0x9a,0x9a,0xff,0x62,0x62,0x62,0xff,0xc9,0xc9,0xc9,0xff,0xb0,0xb0,0xb0,0xff,
0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xab,0xab,0xab,0xff,0xab,0xab,0xab,0xff,0x69,0x69,0x69,0xff,0xba,0xba,0xba,0xff,0xad,0xad,0xad,0xff,0xaa,0xaa,0xaa,0xff,0xa5,0xa5,0xa5,0xff,0x9a,0x9a,0x9a,0xff,0x4f,0x4f,0x4f,0xff,0xc9,0xc9,0xc9,0xff,0xaa,0xaa,0xaa,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,
0x90,0x90,0x90,0xff,0xaa,0xaa,0xaa,0xff,0xab,0xab,0xab,0xff,0x8f,0x8f,0x8f,0xff,0x69,0x69,0x69,0xff,0xb1,0xb1,0xb1,0xff,0xa5,0xa5,0xa5,0xff,0x9a,0x9a,0x9a,0xff,0x4f,0x4f,0x4f,0xff,0xc6,0xc6,0xc6,0xff,0xb9,0xb9,0xb9,0xff,0xaa,0xaa,0xaa,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xaa,0xaa,0xaa,0xff,
0xab,0xab,0xab,0xff,0xaa,0xaa,0xaa,0xff,0x8f,0x8f,0x8f,0xff,0x66,0x66,0x66,0xff,0x58,0x58,0x58,0xff,0x4f,0x4f,0x4f,0xff,0xc6,0xc6,0xc6,0xff,0xc9,0xc9,0xc9,0xff,0xaa,0xaa,0xaa,0xff,0xb0,0xb0,0xb0,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,
0xaa,0xaa,0xaa,0xff,0xa2,0xa2,0xa2,0xff,0xc9,0xc9,0xc9,0xff,0xc9,0xc9,0xc9,0xff,0xb9,0xb9,0xb9,0xff,0xaa,0xaa,0xaa,0xff,0xb0,0xb0,0xb0,0xff,0xaa,0xaa,0xaa,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xb0,0xb0,0xb0,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,
0xb0,0xb0,0xb0,0xff,0xaa,0xaa,0xaa,0xff,0xaa,0xaa,0xaa,0xff,0xb0,0xb0,0xb0,0xff,0xaa,0xaa,0xaa,0xff,0xb0,0xb0,0xb0,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x90,0x90,0x90,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,
0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0xce,0xce,0xce,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,
0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff
};
const unsigned char sScrollCornerDisabledBits [] = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xd8,0xd8,0xd8,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,
0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,
0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,
0xc7,0xc7,0xc7,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,
0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,
0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,
0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,
0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,
0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc7,0xc7,0xc7,0xff,0xc7,0xc7,0xc7,0xff,0xc7,0xc7,0xc7,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,
0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,
0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xf0,0xf0,0xf0,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0xd8,0xd8,0xd8,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,
0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0xc2,0xc2,0xc2,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,
0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff,0x98,0x98,0x98,0xff
};
@@ -0,0 +1,861 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#include "ScrollView.h"
#include <algobase.h>
#include <stdio.h>
#include <string.h>
#include <Bitmap.h>
#include <Message.h>
#include <ScrollBar.h>
#include <Window.h>
#include "Scrollable.h"
#include "ScrollCornerBitmaps.h"
// InternalScrollBar
class InternalScrollBar : public BScrollBar {
public:
InternalScrollBar(ScrollView* scrollView,
BRect frame,
orientation posture);
virtual ~InternalScrollBar();
virtual void ValueChanged(float value);
private:
ScrollView* fScrollView;
};
// constructor
InternalScrollBar::InternalScrollBar(ScrollView* scrollView, BRect frame,
orientation posture)
: BScrollBar(frame, NULL, NULL, 0, 0, posture),
fScrollView(scrollView)
{
}
// destructor
InternalScrollBar::~InternalScrollBar()
{
}
// ValueChanged
void
InternalScrollBar::ValueChanged(float value)
{
// Notify our parent scroll view. Note: the value already has changed,
// so that we can't check, if it really has changed.
if (fScrollView)
fScrollView->_ScrollValueChanged(this, value);
}
// ScrollCorner
class ScrollCorner : public BView {
public:
ScrollCorner(ScrollView* scrollView);
virtual ~ScrollCorner();
virtual void MouseDown(BPoint point);
virtual void MouseUp(BPoint point);
virtual void MouseMoved(BPoint point, uint32 transit,
const BMessage* message);
virtual void Draw(BRect updateRect);
virtual void WindowActivated(bool active);
void SetActive(bool active);
inline bool IsActive() const
{ return fState & STATE_ACTIVE; }
private:
ScrollView* fScrollView;
uint32 fState;
BPoint fStartPoint;
BPoint fStartScrollOffset;
BBitmap* fBitmaps[3];
inline bool IsEnabled() const
{ return ((fState & STATE_ENABLED) ==
STATE_ENABLED); }
void SetDragging(bool dragging);
inline bool IsDragging() const
{ return (fState & STATE_DRAGGING); }
enum {
STATE_DRAGGING = 0x01,
STATE_WINDOW_ACTIVE = 0x02,
STATE_ACTIVE = 0x04,
STATE_ENABLED = STATE_WINDOW_ACTIVE | STATE_ACTIVE,
};
};
// constructor
ScrollCorner::ScrollCorner(ScrollView* scrollView)
: BView(BRect(0.0, 0.0, B_V_SCROLL_BAR_WIDTH - 1.0f, B_H_SCROLL_BAR_HEIGHT - 1.0f), NULL,
0, B_WILL_DRAW),
fScrollView(scrollView),
fState(0),
fStartPoint(0, 0),
fStartScrollOffset(0, 0)
{
//printf("ScrollCorner::ScrollCorner(%p)\n", scrollView);
SetViewColor(B_TRANSPARENT_32_BIT);
//printf("setting up bitmap 0\n");
fBitmaps[0] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace);
// fBitmaps[0]->SetBits((void *)sScrollCornerNormalBits, fBitmaps[0]->BitsLength(), 0L, sColorSpace);
char *bits = (char *)fBitmaps[0]->Bits();
int32 bpr = fBitmaps[0]->BytesPerRow();
for (int i = 0; i <= sBitmapHeight; i++, bits += bpr)
memcpy(bits, &sScrollCornerNormalBits[i * sBitmapHeight * 4], sBitmapWidth * 4);
//printf("setting up bitmap 1\n");
fBitmaps[1] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace);
// fBitmaps[1]->SetBits((void *)sScrollCornerPushedBits, fBitmaps[1]->BitsLength(), 0L, sColorSpace);
bits = (char *)fBitmaps[1]->Bits();
bpr = fBitmaps[1]->BytesPerRow();
for (int i = 0; i <= sBitmapHeight; i++, bits += bpr)
memcpy(bits, &sScrollCornerPushedBits[i * sBitmapHeight * 4], sBitmapWidth * 4);
//printf("setting up bitmap 2\n");
fBitmaps[2] = new BBitmap(BRect(0.0f, 0.0f, sBitmapWidth, sBitmapHeight), sColorSpace);
// fBitmaps[2]->SetBits((void *)sScrollCornerDisabledBits, fBitmaps[2]->BitsLength(), 0L, sColorSpace);
bits = (char *)fBitmaps[2]->Bits();
bpr = fBitmaps[2]->BytesPerRow();
for (int i = 0; i <= sBitmapHeight; i++, bits += bpr)
memcpy(bits, &sScrollCornerDisabledBits[i * sBitmapHeight * 4], sBitmapWidth * 4);
}
// destructor
ScrollCorner::~ScrollCorner()
{
for (int i = 0; i < 3; i++)
delete fBitmaps[i];
}
// MouseDown
void
ScrollCorner::MouseDown(BPoint point)
{
BView::MouseDown(point);
uint32 buttons = 0;
Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
if (buttons & B_PRIMARY_MOUSE_BUTTON) {
SetMouseEventMask(B_POINTER_EVENTS);
if (fScrollView && IsEnabled() && Bounds().Contains(point)) {
SetDragging(true);
fStartPoint = point;
fStartScrollOffset = fScrollView->ScrollOffset();
}
}
}
// MouseUp
void
ScrollCorner::MouseUp(BPoint point)
{
BView::MouseUp(point);
uint32 buttons = 0;
Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
if (!(buttons & B_PRIMARY_MOUSE_BUTTON))
SetDragging(false);
}
// MouseMoved
void
ScrollCorner::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
{
BView::MouseMoved(point, transit, message);
if (IsDragging()) {
uint32 buttons = 0;
Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
// This is a work-around for a BeOS bug: We sometimes don't get a
// MouseUp(), but fortunately it seems, that within the last
// MouseMoved() the button is not longer pressed.
if (buttons & B_PRIMARY_MOUSE_BUTTON) {
BPoint diff = point - fStartPoint;
if (fScrollView) {
fScrollView->_ScrollCornerValueChanged(fStartScrollOffset
- diff);
// + diff);
}
} else
SetDragging(false);
}
}
// Draw
void
ScrollCorner::Draw(BRect updateRect)
{
if (IsEnabled()) {
if (IsDragging())
DrawBitmap(fBitmaps[1], BPoint(0.0f, 0.0f));
else
DrawBitmap(fBitmaps[0], BPoint(0.0f, 0.0f));
}
else
DrawBitmap(fBitmaps[2], BPoint(0.0f, 0.0f));
}
// WindowActivated
void
ScrollCorner::WindowActivated(bool active)
{
if (active != (fState & STATE_WINDOW_ACTIVE)) {
bool enabled = IsEnabled();
if (active)
fState |= STATE_WINDOW_ACTIVE;
else
fState &= ~STATE_WINDOW_ACTIVE;
if (enabled != IsEnabled())
Invalidate();
}
}
// SetActive
void
ScrollCorner::SetActive(bool active)
{
if (active != IsActive()) {
bool enabled = IsEnabled();
if (active)
fState |= STATE_ACTIVE;
else
fState &= ~STATE_ACTIVE;
if (enabled != IsEnabled())
Invalidate();
}
}
// SetDragging
void
ScrollCorner::SetDragging(bool dragging)
{
if (dragging != IsDragging()) {
if (dragging)
fState |= STATE_DRAGGING;
else
fState &= ~STATE_DRAGGING;
Invalidate();
}
}
// ScrollView
// constructor
ScrollView::ScrollView(BView* child, uint32 scrollingFlags, BRect frame,
const char *name, uint32 resizingMode, uint32 flags)
: BView(frame, name, resizingMode, flags | B_FRAME_EVENTS | B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE),
Scroller(),
fChild(NULL),
fScrollingFlags(scrollingFlags),
fHScrollBar(NULL),
fVScrollBar(NULL),
fScrollCorner(NULL),
fHVisible(true),
fVVisible(true),
fCornerVisible(true),
fWindowActive(false),
fChildFocused(false),
fHSmallStep(1),
fVSmallStep(1)
{
// Set transparent view color -- our area is completely covered by
// our children.
SetViewColor(B_TRANSPARENT_32_BIT);
// create scroll bars
if (fScrollingFlags & (SCROLL_HORIZONTAL | SCROLL_HORIZONTAL_MAGIC)) {
fHScrollBar = new InternalScrollBar(this,
BRect(0.0, 0.0, 100.0, B_H_SCROLL_BAR_HEIGHT), B_HORIZONTAL);
AddChild(fHScrollBar);
}
if (fScrollingFlags & (SCROLL_VERTICAL | SCROLL_VERTICAL_MAGIC)) {
fVScrollBar = new InternalScrollBar(this,
BRect(0.0, 0.0, B_V_SCROLL_BAR_WIDTH, 100.0), B_VERTICAL);
AddChild(fVScrollBar);
}
// Create a scroll corner, if we can scroll into both direction.
if (fHScrollBar && fVScrollBar) {
fScrollCorner = new ScrollCorner(this);
AddChild(fScrollCorner);
}
// add child
if (child) {
fChild = child;
AddChild(child);
if (Scrollable* scrollable = dynamic_cast<Scrollable*>(child))
SetScrollTarget(scrollable);
}
}
// destructor
ScrollView::~ScrollView()
{
}
// AllAttached
void
ScrollView::AllAttached()
{
// do a first layout
_Layout(_UpdateScrollBarVisibility());
}
// Draw
void ScrollView::Draw(BRect updateRect)
{
rgb_color keyboardFocus = keyboard_navigation_color();
rgb_color light = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_LIGHTEN_MAX_TINT);
rgb_color shadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_DARKEN_1_TINT);
rgb_color darkShadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_DARKEN_2_TINT);
rgb_color darkerShadow = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
B_DARKEN_3_TINT);
float left = Bounds().left, right = Bounds().right;
float top = Bounds().top, bottom = Bounds().bottom;
if (fChildFocused && fWindowActive) {
BeginLineArray(4);
AddLine(BPoint(left, bottom),
BPoint(left, top), keyboardFocus);
AddLine(BPoint(left + 1.0, top),
BPoint(right, top), keyboardFocus);
AddLine(BPoint(right, top + 1.0),
BPoint(right, bottom), keyboardFocus);
AddLine(BPoint(right - 1.0, bottom),
BPoint(left + 1.0, bottom), keyboardFocus);
EndLineArray();
} else {
BeginLineArray(4);
AddLine(BPoint(left, bottom),
BPoint(left, top), shadow);
AddLine(BPoint(left + 1.0, top),
BPoint(right, top), shadow);
AddLine(BPoint(right, top + 1.0),
BPoint(right, bottom), light);
AddLine(BPoint(right - 1.0, bottom),
BPoint(left + 1.0, bottom), light);
EndLineArray();
}
// The right and bottom lines will be hidden if the scroll views are
// visible. But that doesn't harm.
BRect innerRect(_InnerRect());
left = innerRect.left;
top = innerRect.top;
right = innerRect.right;
bottom = innerRect.bottom;
BeginLineArray(4);
AddLine(BPoint(left, bottom),
BPoint(left, top), darkerShadow);
AddLine(BPoint(left + 1.0, top),
BPoint(right, top), darkShadow);
AddLine(BPoint(right, top + 1.0),
BPoint(right, bottom), darkShadow);
AddLine(BPoint(right - 1.0, bottom),
BPoint(left + 1.0, bottom), darkShadow);
EndLineArray();
}
// FrameResized
void
ScrollView::FrameResized(float width, float height)
{
_Layout(0);
}
// WindowActivated
void ScrollView::WindowActivated(bool activated)
{
fWindowActive = activated;
if (fChildFocused)
Invalidate();
}
// ScrollingFlags
uint32
ScrollView::ScrollingFlags() const
{
return fScrollingFlags;
}
// SetVisibleRectIsChildBounds
void
ScrollView::SetVisibleRectIsChildBounds(bool flag)
{
if (flag != VisibleRectIsChildBounds()) {
if (flag)
fScrollingFlags |= SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS;
else
fScrollingFlags &= ~SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS;
if (fChild && _UpdateScrollBarVisibility())
_Layout(0);
}
}
// VisibleRectIsChildBounds
bool
ScrollView::VisibleRectIsChildBounds() const
{
return (fScrollingFlags & SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS);
}
// Child
BView*
ScrollView::Child() const
{
return fChild;
}
// ChildFocusChanged
//
// To be called by the scroll child, when its has got or lost the focus.
// We need this to know, when to draw the blue focus frame.
void
ScrollView::ChildFocusChanged(bool focused)
{
if (fChildFocused != focused) {
fChildFocused = focused;
Invalidate();
}
}
// HScrollBar
BScrollBar*
ScrollView::HScrollBar() const
{
return fHScrollBar;
}
// VScrollBar
BScrollBar*
ScrollView::VScrollBar() const
{
return fVScrollBar;
}
// HVScrollCorner
BView*
ScrollView::HVScrollCorner() const
{
return fScrollCorner;
}
// SetHSmallStep
void
ScrollView::SetHSmallStep(float hStep)
{
SetSmallSteps(hStep, fVSmallStep);
}
// SetVSmallStep
void
ScrollView::SetVSmallStep(float vStep)
{
SetSmallSteps(fHSmallStep, vStep);
}
// SetSmallSteps
void
ScrollView::SetSmallSteps(float hStep, float vStep)
{
if (fHSmallStep != hStep || fVSmallStep != vStep) {
fHSmallStep = hStep;
fVSmallStep = vStep;
_UpdateScrollBars();
}
}
// GetSmallSteps
void
ScrollView::GetSmallSteps(float* hStep, float* vStep) const
{
*hStep = fHSmallStep;
*vStep = fVSmallStep;
}
// HSmallStep
float
ScrollView::HSmallStep() const
{
return fHSmallStep;
}
// VSmallStep
float
ScrollView::VSmallStep() const
{
return fVSmallStep;
}
// DataRectChanged
void
ScrollView::DataRectChanged(BRect /*oldDataRect*/, BRect /*newDataRect*/)
{
if (ScrollTarget()) {
if (_UpdateScrollBarVisibility())
_Layout(0);
else
_UpdateScrollBars();
}
}
// ScrollOffsetChanged
void
ScrollView::ScrollOffsetChanged(BPoint /*oldOffset*/, BPoint newOffset)
{
if (fHScrollBar && fHScrollBar->Value() != newOffset.x)
fHScrollBar->SetValue(newOffset.x);
if (fVScrollBar && fVScrollBar->Value() != newOffset.y)
fVScrollBar->SetValue(newOffset.y);
}
// VisibleSizeChanged
void
ScrollView::VisibleSizeChanged(float /*oldWidth*/, float /*oldHeight*/,
float /*newWidth*/, float /*newHeight*/)
{
if (ScrollTarget()) {
if (_UpdateScrollBarVisibility())
_Layout(0);
else
_UpdateScrollBars();
}
}
// ScrollTargetChanged
void
ScrollView::ScrollTargetChanged(Scrollable* /*oldTarget*/,
Scrollable* newTarget)
{
/* // remove the old child
if (fChild)
RemoveChild(fChild);
// add the new child
BView* view = dynamic_cast<BView*>(newTarget);
fChild = view;
if (view)
AddChild(view);
else if (newTarget) // set the scroll target to NULL, if it isn't a BView
SetScrollTarget(NULL);
*/
}
// _ScrollValueChanged
void
ScrollView::_ScrollValueChanged(InternalScrollBar* scrollBar, float value)
{
switch (scrollBar->Orientation()) {
case B_HORIZONTAL:
if (fHScrollBar)
SetScrollOffset(BPoint(value, ScrollOffset().y));
break;
case B_VERTICAL:
if (fVScrollBar)
SetScrollOffset(BPoint(ScrollOffset().x, value));
break;
default:
break;
}
}
// _ScrollCornerValueChanged
void
ScrollView::_ScrollCornerValueChanged(BPoint offset)
{
// The logic in Scrollable::SetScrollOffset() handles offsets, that
// are out of range.
SetScrollOffset(offset);
}
// _Layout
//
// Relayouts all children (fChild, scroll bars).
// flags indicates which scrollbars' visibility has changed.
// May be overridden to do a custom layout -- the SCROLL_*_MAGIC must
// be disabled in this case, or strange things happen.
void
ScrollView::_Layout(uint32 flags)
{
bool hbar = (fHScrollBar && fHVisible);
bool vbar = (fVScrollBar && fVVisible);
bool corner = (fScrollCorner && fCornerVisible);
BRect childRect(_ChildRect());
float innerWidth = childRect.Width();
float innerHeight = childRect.Height();
BPoint scrollLT(_InnerRect().LeftTop());
BPoint scrollRB(childRect.RightBottom() + BPoint(1.0f, 1.0f));
if (fScrollingFlags & SCROLL_NO_FRAME) {
// cut off the top line and left line of the
// scroll bars, otherwise they are used for the
// frame appearance
scrollLT.x--;
scrollLT.y--;
}
// layout scroll bars and scroll corner
if (corner) {
// In this case the scrollbars overlap one pixel.
fHScrollBar->MoveTo(scrollLT.x, scrollRB.y);
fHScrollBar->ResizeTo(innerWidth + 2.0, B_H_SCROLL_BAR_HEIGHT);
fVScrollBar->MoveTo(scrollRB.x, scrollLT.y);
fVScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, innerHeight + 2.0);
fScrollCorner->MoveTo(childRect.right + 2.0, childRect.bottom + 2.0);
} else if (hbar) {
fHScrollBar->MoveTo(scrollLT.x, scrollRB.y);
fHScrollBar->ResizeTo(innerWidth + 2.0, B_H_SCROLL_BAR_HEIGHT);
} else if (vbar) {
fVScrollBar->MoveTo(scrollRB.x, scrollLT.y);
fVScrollBar->ResizeTo(B_V_SCROLL_BAR_WIDTH, innerHeight + 2.0);
}
// layout child
if (fChild) {
fChild->MoveTo(childRect.LeftTop());
fChild->ResizeTo(innerWidth, innerHeight);
if (VisibleRectIsChildBounds())
SetVisibleSize(innerWidth, innerHeight);
// Due to a BeOS bug sometimes the area under a recently hidden
// scroll bar isn't updated correctly.
// We force this manually: The position of hidden scroll bar isn't
// updated any longer, so we can't just invalidate it.
if (fChild->Window()) {
if (flags & SCROLL_HORIZONTAL && !fHVisible)
fChild->Invalidate(fHScrollBar->Frame());
if (flags & SCROLL_VERTICAL && !fVVisible)
fChild->Invalidate(fVScrollBar->Frame());
}
}
}
// _UpdateScrollBars
//
// Probably somewhat misnamed. This function updates the scroll bars'
// proportion, range attributes and step widths according to the scroll
// target's DataRect() and VisibleBounds(). May also be called, if there's
// no scroll target -- then the scroll bars are disabled.
void
ScrollView::_UpdateScrollBars()
{
BRect dataRect = DataRect();
BRect visibleBounds = VisibleBounds();
if (!fScrollTarget) {
dataRect.Set(0.0, 0.0, 0.0, 0.0);
visibleBounds.Set(0.0, 0.0, 0.0, 0.0);
}
float hProportion = min(1.0f, (visibleBounds.Width() + 1.0f) /
(dataRect.Width() + 1.0f));
float hMaxValue = max(dataRect.left,
dataRect.Width() - visibleBounds.Width());
float vProportion = min(1.0f, (visibleBounds.Height() + 1.0f) /
(dataRect.Height() + 1.0f));
float vMaxValue = max(dataRect.top,
dataRect.Height() - visibleBounds.Height());
// update horizontal scroll bar
if (fHScrollBar) {
fHScrollBar->SetProportion(hProportion);
fHScrollBar->SetRange(dataRect.left, hMaxValue);
// This obviously ineffective line works around a BScrollBar bug:
// As documented the scrollbar's value is adjusted, if the range
// has been changed and it therefore falls out of the range. But if,
// after resetting the range to what it has been before, the user
// moves the scrollbar to the original value via one click
// it is failed to invoke BScrollBar::ValueChanged().
fHScrollBar->SetValue(fHScrollBar->Value());
fHScrollBar->SetSteps(fHSmallStep, visibleBounds.Width());
}
// update vertical scroll bar
if (fVScrollBar) {
fVScrollBar->SetProportion(vProportion);
fVScrollBar->SetRange(dataRect.top, vMaxValue);
// This obviously ineffective line works around a BScrollBar bug.
fVScrollBar->SetValue(fVScrollBar->Value());
fVScrollBar->SetSteps(fVSmallStep, visibleBounds.Height());
}
// update scroll corner
if (fScrollCorner) {
fScrollCorner->SetActive(hProportion < 1.0f || vProportion < 1.0f);
}
}
// set_visible_state
//
// Convenience function: Sets a view's visibility state to /visible/.
// Returns true, if the state was actually changed, false otherwise.
// This function never calls Hide() on a hidden or Show() on a visible
// view. /view/ must be valid.
static inline
bool
set_visible_state(BView* view, bool visible, bool* currentlyVisible)
{
bool changed = false;
if (*currentlyVisible != visible) {
if (visible)
view->Show();
else
view->Hide();
*currentlyVisible = visible;
changed = true;
}
return changed;
}
// _UpdateScrollBarVisibility
//
// Checks which of scroll bars need to be visible according to
// SCROLL_*_MAGIG and shows/hides them, if necessary.
// Returns a bitwise combination of SCROLL_HORIZONTAL and SCROLL_VERTICAL
// according to which scroll bar's visibility state has changed, 0 if none.
// A return value != 0 usually means that the layout isn't valid any longer.
uint32
ScrollView::_UpdateScrollBarVisibility()
{
uint32 changed = 0;
BRect childRect(_MaxVisibleRect());
float width = childRect.Width();
float height = childRect.Height();
BRect dataRect = DataRect(); // Invalid if !ScrollTarget(),
float dataWidth = dataRect.Width(); // but that doesn't harm.
float dataHeight = dataRect.Height(); //
bool hbar = (fScrollingFlags & SCROLL_HORIZONTAL_MAGIC);
bool vbar = (fScrollingFlags & SCROLL_VERTICAL_MAGIC);
if (!ScrollTarget()) {
if (hbar) {
if (set_visible_state(fHScrollBar, false, &fHVisible))
changed |= SCROLL_HORIZONTAL;
}
if (vbar) {
if (set_visible_state(fVScrollBar, false, &fVVisible))
changed |= SCROLL_VERTICAL;
}
} else if (hbar && width >= dataWidth && vbar && height >= dataHeight) {
// none
if (set_visible_state(fHScrollBar, false, &fHVisible))
changed |= SCROLL_HORIZONTAL;
if (set_visible_state(fVScrollBar, false, &fVVisible))
changed |= SCROLL_VERTICAL;
} else {
// The case, that both scroll bars are magic and invisible is catched,
// so that while checking one bar we can suppose, that the other one
// is visible (if it does exist at all).
BRect innerRect(_GuessVisibleRect(fHScrollBar, fVScrollBar));
float innerWidth = innerRect.Width();
float innerHeight = innerRect.Height();
// the horizontal one?
if (hbar) {
if (innerWidth >= dataWidth) {
if (set_visible_state(fHScrollBar, false, &fHVisible))
changed |= SCROLL_HORIZONTAL;
} else {
if (set_visible_state(fHScrollBar, true, &fHVisible))
changed |= SCROLL_HORIZONTAL;
}
}
// the vertical one?
if (vbar) {
if (innerHeight >= dataHeight) {
if (set_visible_state(fVScrollBar, false, &fVVisible))
changed |= SCROLL_VERTICAL;
} else {
if (set_visible_state(fVScrollBar, true, &fVVisible))
changed |= SCROLL_VERTICAL;
}
}
}
// If anything has changed, update the scroll corner as well.
if (changed && fScrollCorner)
set_visible_state(fScrollCorner, fHVisible && fVVisible, &fCornerVisible);
return changed;
}
// _InnerRect
//
// Returns the rectangle that actually can be used for the child and the
// scroll bars, i.e. the view's Bounds() subtracted the space for the
// decorative frame.
BRect
ScrollView::_InnerRect() const
{
if (fScrollingFlags & SCROLL_NO_FRAME)
return Bounds();
return Bounds().InsetBySelf(1.0f, 1.0f);
}
// _ChildRect
//
// Returns the rectangle, that should be the current child frame.
// `should' because 1. we might not have a child at all or 2. a
// relayout is pending.
BRect
ScrollView::_ChildRect() const
{
return _ChildRect(fHScrollBar && fHVisible, fVScrollBar && fVVisible);
}
// _ChildRect
//
// The same as _ChildRect() with the exception that not the current
// scroll bar visibility, but a fictitious one given by /hbar/ and /vbar/
// is considered.
BRect
ScrollView::_ChildRect(bool hbar, bool vbar) const
{
BRect rect(_InnerRect());
float frameWidth = (fScrollingFlags & SCROLL_NO_FRAME) ? 0.0 : 1.0;
if (hbar)
rect.bottom -= B_H_SCROLL_BAR_HEIGHT + frameWidth;
else
rect.bottom -= frameWidth;
if (vbar)
rect.right -= B_V_SCROLL_BAR_WIDTH + frameWidth;
else
rect.right -= frameWidth;
rect.top += frameWidth;
rect.left += frameWidth;
return rect;
}
// _GuessVisibleRect
//
// Returns an approximation of the visible rect for the
// fictitious scroll bar visibility given by /hbar/ and /vbar/.
// In the case !VisibleRectIsChildBounds() it is simply the current
// visible rect.
BRect
ScrollView::_GuessVisibleRect(bool hbar, bool vbar) const
{
if (VisibleRectIsChildBounds())
return _ChildRect(hbar, vbar).OffsetToCopy(ScrollOffset());
return VisibleRect();
}
// _MaxVisibleRect
//
// Returns the maximal possible visible rect in the current situation, that
// is depending on if the visible rect is the child's bounds either the
// rectangle the child covers when both scroll bars are hidden (offset to
// the scroll offset) or the current visible rect.
BRect
ScrollView::_MaxVisibleRect() const
{
return _GuessVisibleRect(true, true);
}
@@ -0,0 +1,113 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
* Ingo Weinhold <[email protected]>
*/
#ifndef SCROLL_VIEW_H
#define SCROLL_VIEW_H
#include <View.h>
#include "Scroller.h"
class Scrollable;
class InternalScrollBar;
class ScrollCorner;
enum {
SCROLL_HORIZONTAL = 0x01,
SCROLL_VERTICAL = 0x02,
SCROLL_HORIZONTAL_MAGIC = 0x04,
SCROLL_VERTICAL_MAGIC = 0x08,
SCROLL_VISIBLE_RECT_IS_CHILD_BOUNDS = 0x10,
SCROLL_NO_FRAME = 0x20,
};
class ScrollView : public BView, public Scroller {
public:
ScrollView(BView* child,
uint32 scrollingFlags,
BRect frame,
const char *name,
uint32 resizingMode, uint32 flags);
virtual ~ScrollView();
virtual void AllAttached();
virtual void Draw(BRect updateRect);
virtual void FrameResized(float width, float height);
virtual void WindowActivated(bool activated);
uint32 ScrollingFlags() const;
void SetVisibleRectIsChildBounds(bool flag);
bool VisibleRectIsChildBounds() const;
BView* Child() const;
void ChildFocusChanged(bool focused);
BScrollBar* HScrollBar() const;
BScrollBar* VScrollBar() const;
BView* HVScrollCorner() const;
void SetHSmallStep(float hStep);
void SetVSmallStep(float vStep);
void SetSmallSteps(float hStep, float vStep);
void GetSmallSteps(float* hStep,
float* vStep) const;
float HSmallStep() const;
float VSmallStep() const;
protected:
virtual void DataRectChanged(BRect oldDataRect,
BRect newDataRect);
virtual void ScrollOffsetChanged(BPoint oldOffset,
BPoint newOffset);
virtual void VisibleSizeChanged(float oldWidth,
float oldHeight,
float newWidth,
float newHeight);
virtual void ScrollTargetChanged(Scrollable* oldTarget,
Scrollable* newTarget);
private:
BView* fChild; // child view
uint32 fScrollingFlags;
InternalScrollBar* fHScrollBar; // horizontal scroll bar
InternalScrollBar* fVScrollBar; // vertical scroll bar
ScrollCorner* fScrollCorner; // scroll corner
bool fHVisible; // horizontal/vertical scroll
bool fVVisible; // bar visible flag
bool fCornerVisible; // scroll corner visible flag
bool fWindowActive;
bool fChildFocused;
float fHSmallStep;
float fVSmallStep;
void _ScrollValueChanged(
InternalScrollBar* scrollBar,
float value);
void _ScrollCornerValueChanged(BPoint offset);
protected:
virtual void _Layout(uint32 flags);
private:
void _UpdateScrollBars();
uint32 _UpdateScrollBarVisibility();
BRect _InnerRect() const;
BRect _ChildRect() const;
BRect _ChildRect(bool hbar, bool vbar) const;
BRect _GuessVisibleRect(bool hbar, bool vbar) const;
BRect _MaxVisibleRect() const;
friend class InternalScrollBar;
friend class ScrollCorner;
};
#endif // SCROLL_VIEW_H
@@ -0,0 +1,227 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#include "Scrollable.h"
#include <algobase.h>
#include <stdio.h>
#include "Scroller.h"
// constructor
Scrollable::Scrollable()
: fDataRect(0.0, 0.0, 0.0, 0.0),
fScrollOffset(0.0, 0.0),
fVisibleWidth(0),
fVisibleHeight(0),
fScrollSource(NULL)
{
}
// destructor
Scrollable::~Scrollable()
{
if (fScrollSource)
fScrollSource->SetScrollTarget(NULL);
}
// SetScrollSource
//
// Sets a new scroll source. Notifies the old and the new source
// of the change if necessary .
void
Scrollable::SetScrollSource(Scroller* source)
{
Scroller* oldSource = fScrollSource;
if (oldSource != source) {
fScrollSource = NULL;
// Notify the old source, if it doesn't know about the change.
if (oldSource && oldSource->ScrollTarget() == this)
fScrollSource->SetScrollTarget(NULL);
fScrollSource = source;
// Notify the new source, if it doesn't know about the change.
if (source && source->ScrollTarget() != this)
source->SetScrollTarget(this);
// Notify ourselves.
ScrollSourceChanged(oldSource, fScrollSource);
}
}
// ScrollSource
//
// Returns the current scroll source. May be NULL, if we don't have any.
Scroller*
Scrollable::ScrollSource() const
{
return fScrollSource;
}
// SetDataRect
//
// Sets the data rect.
void
Scrollable::SetDataRect(BRect dataRect)
{
if (fDataRect != dataRect && dataRect.IsValid()) {
BRect oldDataRect = fDataRect;
fDataRect = dataRect;
// notify ourselves
DataRectChanged(oldDataRect, fDataRect);
// notify scroller
if (fScrollSource)
fScrollSource->DataRectChanged(oldDataRect, fDataRect);
// adjust the scroll offset, if necessary
BPoint offset = _ValidScrollOffsetFor(fScrollOffset);
if (offset != fScrollOffset)
SetScrollOffset(offset);
}
}
// DataRect
//
// Returns the current data rect.
BRect
Scrollable::DataRect() const
{
return fDataRect;
}
// SetScrollOffset
//
// Sets the scroll offset.
void
Scrollable::SetScrollOffset(BPoint offset)
{
// adjust the supplied offset to be valid
offset = _ValidScrollOffsetFor(offset);
if (fScrollOffset != offset) {
BPoint oldOffset = fScrollOffset;
fScrollOffset = offset;
// notify ourselves
ScrollOffsetChanged(oldOffset, fScrollOffset);
// notify scroller
if (fScrollSource)
fScrollSource->ScrollOffsetChanged(oldOffset, fScrollOffset);
}
}
// ScrollOffset
//
// Returns the current scroll offset.
BPoint
Scrollable::ScrollOffset() const
{
return fScrollOffset;
}
// SetVisibleSize
//
// Sets the visible size.
void
Scrollable::SetVisibleSize(float width, float height)
{
if ((fVisibleWidth != width || fVisibleHeight != height) &&
width >= 0 && height >= 0) {
float oldWidth = fVisibleWidth;
float oldHeight = fVisibleHeight;
fVisibleWidth = width;
fVisibleHeight = height;
// notify ourselves
VisibleSizeChanged(oldWidth, oldHeight, fVisibleWidth, fVisibleHeight);
// notify scroller
if (fScrollSource) {
fScrollSource->VisibleSizeChanged(oldWidth, oldHeight,
fVisibleWidth, fVisibleHeight);
}
// adjust the scroll offset, if necessary
BPoint offset = _ValidScrollOffsetFor(fScrollOffset);
if (offset != fScrollOffset)
SetScrollOffset(offset);
}
}
// VisibleBounds
//
// Returns the visible bounds, i.e. a rectangle of the visible size
// located at (0.0, 0.0).
BRect
Scrollable::VisibleBounds() const
{
return BRect(0.0, 0.0, fVisibleWidth, fVisibleHeight);
}
// VisibleRect
//
// Returns the visible rect, i.e. a rectangle of the visible size located
// at the scroll offset.
BRect
Scrollable::VisibleRect() const
{
BRect rect(0.0, 0.0, fVisibleWidth, fVisibleHeight);
rect.OffsetBy(fScrollOffset);
return rect;
}
// DataRectChanged
//
// Hook function. Implemented by derived classes to get notified when
// the data rect has changed.
void
Scrollable::DataRectChanged(BRect /*oldDataRect*/, BRect /*newDataRect*/)
{
}
// ScrollOffsetChanged
//
// Hook function. Implemented by derived classes to get notified when
// the scroll offset has changed.
void
Scrollable::ScrollOffsetChanged(BPoint /*oldOffset*/, BPoint /*newOffset*/)
{
}
// VisibleSizeChanged
//
// Hook function. Implemented by derived classes to get notified when
// the visible size has changed.
void
Scrollable::VisibleSizeChanged(float /*oldWidth*/, float /*oldHeight*/,
float /*newWidth*/, float /*newHeight*/)
{
}
// ScrollSourceChanged
//
// Hook function. Implemented by derived classes to get notified when
// the scroll source has changed.
void
Scrollable::ScrollSourceChanged(Scroller* /*oldSource*/,
Scroller* /*newSource*/)
{
}
// _ValidScrollOffsetFor
//
// Returns the valid scroll offset next to the supplied offset.
BPoint
Scrollable::_ValidScrollOffsetFor(BPoint offset) const
{
float maxX = max(fDataRect.left, fDataRect.Width() - fVisibleWidth);
float maxY = max(fDataRect.top, fDataRect.Height() - fVisibleHeight);
// adjust the offset to be valid
if (offset.x < fDataRect.left)
offset.x = fDataRect.left;
else if (offset.x > maxX)
offset.x = maxX;
if (offset.y < fDataRect.top)
offset.y = fDataRect.top;
else if (offset.y > maxY)
offset.y = maxY;
return offset;
}
@@ -0,0 +1,59 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#ifndef SCROLLABLE_H
#define SCROLLABLE_H
#include <Point.h>
#include <Rect.h>
class Scroller;
class Scrollable {
public:
Scrollable();
virtual ~Scrollable();
void SetScrollSource(Scroller* source);
Scroller* ScrollSource() const;
void SetDataRect(BRect dataRect);
BRect DataRect() const;
void SetScrollOffset(BPoint offset);
BPoint ScrollOffset() const;
void SetVisibleSize(float width, float height);
BRect VisibleBounds() const;
BRect VisibleRect() const;
protected:
virtual void DataRectChanged(BRect oldDataRect,
BRect newDataRect);
virtual void ScrollOffsetChanged(BPoint oldOffset,
BPoint newOffset);
virtual void VisibleSizeChanged(float oldWidth,
float oldHeight,
float newWidth,
float newHeight);
virtual void ScrollSourceChanged(Scroller* oldSource,
Scroller* newSource);
private:
BRect fDataRect;
BPoint fScrollOffset;
float fVisibleWidth;
float fVisibleHeight;
Scroller* fScrollSource;
BPoint _ValidScrollOffsetFor(BPoint offset) const;
};
#endif // SCROLLABLE_H
@@ -0,0 +1,39 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#include "ScrollableView.h"
#include <View.h>
// constructor
ScrollableView::ScrollableView()
: Scrollable()
{
}
// destructor
ScrollableView::~ScrollableView()
{
}
// ScrollOffsetChanged
void
ScrollableView::ScrollOffsetChanged(BPoint oldOffset, BPoint newOffset)
{
if (BView* view = dynamic_cast<BView*>(this)) {
// We keep it simple: The part of the data rect we shall show now
// has existed before as well (even if partially or completely
// obscured), so we let CopyBits() do the messy details.
BRect bounds(view->Bounds());
view->CopyBits(bounds.OffsetByCopy(newOffset - oldOffset), bounds);
// move our children
for (int32 i = 0; BView* child = view->ChildAt(i); i++)
child->MoveTo(child->Frame().LeftTop() + oldOffset - newOffset);
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
/** Simple extension to the Scrollable class to simplify the creation
of derived view classes. */
#ifndef SCROLLABLE_VIEW_H
#define SCROLLABLE_VIEW_H
#include "Scrollable.h"
class ScrollableView : public Scrollable {
public:
ScrollableView();
virtual ~ScrollableView();
protected:
virtual void ScrollOffsetChanged(BPoint oldOffset,
BPoint newOffset);
};
#endif // SCROLLABLE_VIEW_H
@@ -0,0 +1,178 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#include "Scroller.h"
#include <stdio.h>
#include <Point.h>
#include <Rect.h>
#include "Scrollable.h"
// constructor
Scroller::Scroller()
: fScrollTarget(NULL)
{
}
// destructor
Scroller::~Scroller()
{
}
// SetScrollTarget
//
// Sets a new scroll target. Notifies the old and the new target
// of the change if necessary .
void
Scroller::SetScrollTarget(Scrollable* target)
{
Scrollable* oldTarget = fScrollTarget;
if (oldTarget != target) {
fScrollTarget = NULL;
// Notify the old target, if it doesn't know about the change.
if (oldTarget && oldTarget->ScrollSource() == this)
oldTarget->SetScrollSource(NULL);
fScrollTarget = target;
// Notify the new target, if it doesn't know about the change.
if (target && target->ScrollSource() != this)
target->SetScrollSource(this);
// Notify ourselves.
ScrollTargetChanged(oldTarget, target);
}
}
// ScrollTarget
//
// Returns the current scroll target. May be NULL, if we don't have any.
Scrollable*
Scroller::ScrollTarget() const
{
return fScrollTarget;
}
// SetDataRect
//
// Sets the data rect of the scroll target, if we have one.
void
Scroller::SetDataRect(BRect dataRect)
{
if (fScrollTarget)
fScrollTarget->SetDataRect(dataRect);
}
// DataRect
//
// Returns the data rect of the scroll target or a undefined value, if
// we have none.
BRect
Scroller::DataRect() const
{
if (fScrollTarget)
return fScrollTarget->DataRect();
return BRect();
}
// SetScrollOffset
//
// Sets the scroll offset of the scroll target, if we have one.
void
Scroller::SetScrollOffset(BPoint offset)
{
if (fScrollTarget)
fScrollTarget->SetScrollOffset(offset);
}
// ScrollOffset
//
// Returns the scroll offset of the scroll target or a undefined value, if
// we have none.
BPoint
Scroller::ScrollOffset() const
{
if (fScrollTarget)
return fScrollTarget->ScrollOffset();
return BPoint(0.0, 0.0);
}
// SetVisibleSize
//
// Sets the visible size of the scroll target, if we have one.
void
Scroller::SetVisibleSize(float width, float height)
{
if (fScrollTarget)
fScrollTarget->SetVisibleSize(width, height);
}
// VisibleBounds
//
// Returns the visible bounds of the scroll target or a undefined value, if
// we have none.
BRect
Scroller::VisibleBounds() const
{
if (fScrollTarget)
return fScrollTarget->VisibleBounds();
return BRect();
}
// VisibleRect
//
// Returns the visible rect of the scroll target or a undefined value, if
// we have none.
BRect
Scroller::VisibleRect() const
{
if (fScrollTarget)
return fScrollTarget->VisibleRect();
return BRect();
}
// hooks
// DataRectChanged
//
// Hook function. Implemented by derived classes to get notified when
// the data rect of the sroll target has changed.
void
Scroller::DataRectChanged(BRect /*oldDataRect*/, BRect /*newDataRect*/)
{
}
// ScrollOffsetChanged
//
// Hook function. Implemented by derived classes to get notified when
// the scroll offset of the sroll target has changed.
void
Scroller::ScrollOffsetChanged(BPoint /*oldOffset*/, BPoint /*newOffset*/)
{
}
// VisiblSizeChanged
//
// Hook function. Implemented by derived classes to get notified when
// the visible size of the sroll target has changed.
void
Scroller::VisibleSizeChanged(float /*oldWidth*/, float /*oldHeight*/,
float /*newWidth*/, float /*newHeight*/)
{
}
// ScrollTargetChanged
//
// Hook function. Implemented by derived classes to get notified when
// the sroll target has changed. /target/ may be NULL.
void
Scroller::ScrollTargetChanged(Scrollable* /*oldTarget*/,
Scrollable* /*newTarget*/)
{
}
@@ -0,0 +1,51 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#ifndef SCROLLER_H
#define SCROLLER_H
class Scrollable;
class Scroller {
public:
Scroller();
virtual ~Scroller();
void SetScrollTarget(Scrollable* target);
Scrollable* ScrollTarget() const;
void SetDataRect(BRect dataRect);
BRect DataRect() const;
void SetScrollOffset(BPoint offset);
BPoint ScrollOffset() const;
void SetVisibleSize(float width, float height);
BRect VisibleBounds() const;
BRect VisibleRect() const;
protected:
virtual void DataRectChanged(BRect oldDataRect,
BRect newDataRect);
virtual void ScrollOffsetChanged(BPoint oldOffset,
BPoint newOffset);
virtual void VisibleSizeChanged(float oldWidth,
float oldHeight,
float newWidth,
float newHeight);
virtual void ScrollTargetChanged(Scrollable* oldTarget,
Scrollable* newTarget);
protected:
Scrollable* fScrollTarget;
friend class Scrollable;
};
#endif // SCROLLER_H
@@ -0,0 +1,126 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Manipulator.h"
#include "Observable.h"
// constructor
Manipulator::Manipulator(Observable* object)
: Observer(),
fManipulatedObject(object)
{
if (fManipulatedObject)
fManipulatedObject->AddObserver(this);
}
// destructor
Manipulator::~Manipulator()
{
if (fManipulatedObject)
fManipulatedObject->RemoveObserver(this);
}
// #pragma mark -
// Draw
void
Manipulator::Draw(BView* into, BRect updateRect)
{
}
// MouseDown
bool
Manipulator::MouseDown(BPoint where)
{
return false;
}
// MouseMoved
void
Manipulator::MouseMoved(BPoint where)
{
}
// MouseUp
Command*
Manipulator::MouseUp()
{
return NULL;
}
// MouseOver
bool
Manipulator::MouseOver(BPoint where)
{
return false;
}
// DoubleClicked
bool
Manipulator::DoubleClicked(BPoint where)
{
return false;
}
// #pragma mark -
bool
Manipulator::MessageReceived(BMessage* message, Command** _command)
{
return false;
}
// #pragma mark -
// ModifiersChanged
void
Manipulator::ModifiersChanged(uint32 modifiers)
{
}
// HandleKeyDown
bool
Manipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
{
return false;
}
// HandleKeyUp
bool
Manipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
{
return false;
}
// UpdateCursor
void
Manipulator::UpdateCursor()
{
}
// #pragma mark -
// TrackingBounds
BRect
Manipulator::TrackingBounds(BView* withinView)
{
return Bounds();
}
// AttachedToView
void
Manipulator::AttachedToView(BView* view)
{
}
// DetachedFromView
void
Manipulator::DetachedFromView(BView* view)
{
}
@@ -0,0 +1,61 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef MANIPULATOR_H
#define MANIPULATOR_H
#include <Rect.h>
#include "Observer.h"
class BView;
class Command;
// TODO: merge ViewState and Manipulator
class Manipulator : public Observer {
public:
Manipulator(Observable* object);
virtual ~Manipulator();
// Manipulator interface
virtual void Draw(BView* into, BRect updateRect);
virtual bool MouseDown(BPoint where);
virtual void MouseMoved(BPoint where);
virtual Command* MouseUp();
virtual bool MouseOver(BPoint where);
virtual bool DoubleClicked(BPoint where);
virtual bool MessageReceived(BMessage* message,
Command** _command);
virtual void ModifiersChanged(uint32 modifiers);
virtual bool HandleKeyDown(uint32 key, uint32 modifiers,
Command** _command);
virtual bool HandleKeyUp(uint32 key, uint32 modifiers,
Command** _command);
virtual void UpdateCursor();
virtual BRect Bounds() = 0;
// the area that the manipulator is
// occupying in the "parent" view
virtual BRect TrackingBounds(BView* withinView);
// the area within "view" in which the
// Manipulator wants to receive MouseOver()
// events
virtual void AttachedToView(BView* view);
virtual void DetachedFromView(BView* view);
protected:
Observable* fManipulatedObject;
};
#endif // MANIPULATOR_H
@@ -0,0 +1,255 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "MultipleManipulatorState.h"
#include <stdio.h>
#include "Manipulator.h"
#include "StateView.h"
// constructor
MultipleManipulatorState::MultipleManipulatorState(StateView* view)
: ViewState(view),
fManipulators(24),
fCurrentManipulator(NULL),
fPreviousManipulator(NULL)
{
}
// destructor
MultipleManipulatorState::~MultipleManipulatorState()
{
DeleteManipulators();
}
// #pragma mark -
// Init
void
MultipleManipulatorState::Init()
{
}
// Cleanup
void
MultipleManipulatorState::Cleanup()
{
}
// #pragma mark -
// Draw
void
MultipleManipulatorState::Draw(BView* into, BRect updateRect)
{
int32 count = fManipulators.CountItems();
for (int32 i = 0; i < count; i++) {
Manipulator* manipulator =
(Manipulator*)fManipulators.ItemAtFast(i);
if (manipulator->Bounds().Intersects(updateRect))
manipulator->Draw(into, updateRect);
}
}
// MessageReceived
bool
MultipleManipulatorState::MessageReceived(BMessage* message,
Command** _command)
{
int32 count = fManipulators.CountItems();
for (int32 i = 0; i < count; i++) {
Manipulator* manipulator =
(Manipulator*)fManipulators.ItemAtFast(i);
if (manipulator->MessageReceived(message, _command))
return true;
}
return false;
}
// #pragma mark -
// MouseDown
void
MultipleManipulatorState::MouseDown(BPoint where, uint32 buttons, uint32 clicks)
{
// NOTE: buttons currently ignored
if (clicks == 2
&& fPreviousManipulator
&& fManipulators.HasItem(fPreviousManipulator)) {
// valid double click (onto the same, still existing manipulator)
if (fPreviousManipulator->TrackingBounds(fView).Contains(where)
&& fPreviousManipulator->DoubleClicked(where)) {
// TODO: eat the click here or wait for MouseUp?
fPreviousManipulator = NULL;
return;
}
}
int32 count = fManipulators.CountItems();
for (int32 i = count - 1; i >= 0; i--) {
Manipulator* manipulator =
(Manipulator*)fManipulators.ItemAtFast(i);
if (manipulator->MouseDown(where)) {
fCurrentManipulator = manipulator;
break;
}
}
fView->SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
}
// MouseMoved
void
MultipleManipulatorState::MouseMoved(BPoint where, uint32 transit,
const BMessage* dragMessage)
{
if (fCurrentManipulator) {
// the mouse is currently pressed
fCurrentManipulator->MouseMoved(where);
} else {
// the mouse is currently NOT pressed
// call MouseOver on all manipulators
// until one feels responsible
int32 count = fManipulators.CountItems();
for (int32 i = 0; i < count; i++) {
Manipulator* manipulator =
(Manipulator*)fManipulators.ItemAtFast(i);
if (manipulator->TrackingBounds(fView).Contains(where)
&& manipulator->MouseOver(where)) {
break;
}
}
}
}
// MouseUp
Command*
MultipleManipulatorState::MouseUp()
{
Command* command = NULL;
if (fCurrentManipulator) {
command = fCurrentManipulator->MouseUp();
fPreviousManipulator = fCurrentManipulator;
fCurrentManipulator = NULL;
}
return command;
}
// #pragma mark -
// ModifiersChanged
void
MultipleManipulatorState::ModifiersChanged(uint32 modifiers)
{
int32 count = fManipulators.CountItems();
for (int32 i = 0; i < count; i++) {
Manipulator* manipulator =
(Manipulator*)fManipulators.ItemAtFast(i);
manipulator->ModifiersChanged(modifiers);
}
}
// HandleKeyDown
bool
MultipleManipulatorState::HandleKeyDown(uint32 key, uint32 modifiers,
Command** _command)
{
int32 count = fManipulators.CountItems();
for (int32 i = 0; i < count; i++) {
Manipulator* manipulator =
(Manipulator*)fManipulators.ItemAtFast(i);
if (manipulator->HandleKeyDown(key, modifiers, _command))
return true;
}
return false;
}
// HandleKeyUp
bool
MultipleManipulatorState::HandleKeyUp(uint32 key, uint32 modifiers,
Command** _command)
{
int32 count = fManipulators.CountItems();
for (int32 i = 0; i < count; i++) {
Manipulator* manipulator =
(Manipulator*)fManipulators.ItemAtFast(i);
if (manipulator->HandleKeyUp(key, modifiers, _command))
return true;
}
return false;
}
// #pragma mark -
// AddManipulator
bool
MultipleManipulatorState::AddManipulator(Manipulator* manipulator)
{
if (!manipulator)
return false;
if (fManipulators.AddItem((void*)manipulator)) {
manipulator->AttachedToView(fView);
return true;
}
return false;
}
// RemoveManipulator
Manipulator*
MultipleManipulatorState::RemoveManipulator(int32 index)
{
Manipulator* manipulator = (Manipulator*)fManipulators.RemoveItem(index);
if (manipulator == fCurrentManipulator)
fCurrentManipulator = NULL;
return manipulator;
}
// DeleteManipulators
void
MultipleManipulatorState::DeleteManipulators()
{
int32 count = fManipulators.CountItems();
for (int32 i = 0; i < count; i++) {
Manipulator* m = (Manipulator*)fManipulators.ItemAtFast(i);
m->DetachedFromView(fView);
delete m;
}
fManipulators.MakeEmpty();
fCurrentManipulator = NULL;
fPreviousManipulator = NULL;
}
// CountManipulators
int32
MultipleManipulatorState::CountManipulators() const
{
return fManipulators.CountItems();
}
// ManipulatorAt
Manipulator*
MultipleManipulatorState::ManipulatorAt(int32 index) const
{
return (Manipulator*)fManipulators.ItemAt(index);
}
// ManipulatorAtFast
Manipulator*
MultipleManipulatorState::ManipulatorAtFast(int32 index) const
{
return (Manipulator*)fManipulators.ItemAtFast(index);
}
@@ -0,0 +1,62 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef MULTIPLE_MANIPULATOR_STATE_H
#define MULTIPLE_MANIPULATOR_STATE_H
#include <List.h>
#include "ViewState.h"
class Manipulator;
class MultipleManipulatorState : public ViewState {
public:
MultipleManipulatorState(StateView* view);
virtual ~MultipleManipulatorState();
// ViewState interface
virtual void Init();
virtual void Cleanup();
virtual void Draw(BView* into, BRect updateRect);
virtual bool MessageReceived(BMessage* message,
Command** _command);
virtual void MouseDown(BPoint where,
uint32 buttons,
uint32 clicks);
virtual void MouseMoved(BPoint where,
uint32 transit,
const BMessage* dragMessage);
virtual Command* MouseUp();
virtual void ModifiersChanged(uint32 modifiers);
virtual bool HandleKeyDown(uint32 key, uint32 modifiers,
Command** _command);
virtual bool HandleKeyUp(uint32 key, uint32 modifiers,
Command** _command);
// MultipleManipulatorState
bool AddManipulator(Manipulator* manipulator);
Manipulator* RemoveManipulator(int32 index);
void DeleteManipulators();
int32 CountManipulators() const;
Manipulator* ManipulatorAt(int32 index) const;
Manipulator* ManipulatorAtFast(int32 index) const;
private:
BList fManipulators;
Manipulator* fCurrentManipulator;
Manipulator* fPreviousManipulator;
};
#endif // MULTIPLE_MANIPULATOR_STATE_H
@@ -0,0 +1,487 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "StateView.h"
#include <Message.h>
#include <MessageFilter.h>
#include <Window.h>
#include "Command.h"
#include "CommandStack.h"
#include "RWLocker.h"
class EventFilter : public BMessageFilter {
public:
EventFilter(StateView* target)
: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
fTarget(target)
{
}
virtual ~EventFilter()
{
}
virtual filter_result Filter(BMessage* message, BHandler** target)
{
filter_result result = B_DISPATCH_MESSAGE;
switch (message->what) {
case B_KEY_DOWN: {
uint32 key;
uint32 modifiers;
if (message->FindInt32("raw_char", (int32*)&key) >= B_OK
&& message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK)
if (fTarget->HandleKeyDown(key, modifiers))
result = B_SKIP_MESSAGE;
break;
}
case B_KEY_UP: {
uint32 key;
uint32 modifiers;
if (message->FindInt32("raw_char", (int32*)&key) >= B_OK
&& message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK)
if (fTarget->HandleKeyUp(key, modifiers))
result = B_SKIP_MESSAGE;
break;
}
case B_MODIFIERS_CHANGED:
*target = fTarget;
break;
case B_MOUSE_WHEEL_CHANGED: {
float x;
float y;
if (message->FindFloat("be:wheel_delta_x", &x) >= B_OK
&& message->FindFloat("be:wheel_delta_y", &y) >= B_OK) {
if (fTarget->MouseWheelChanged(x, y))
result = B_SKIP_MESSAGE;
}
break;
}
default:
break;
}
return result;
}
private:
StateView* fTarget;
};
// #pragma mark -
// constructor
StateView::StateView(BRect frame, const char* name,
uint32 resizingMode, uint32 flags)
: BView(frame, name, resizingMode, flags),
fCurrentState(NULL),
fDropAnticipatingState(NULL),
fMouseInfo(),
fCommandStack(NULL),
fLocker(NULL),
fEventFilter(NULL),
fCatchAllEvents(false),
fUpdateTarget(NULL),
fUpdateCommand(0)
{
}
// destructor
StateView::~StateView()
{
delete fEventFilter;
}
// #pragma mark -
// AttachedToWindow
void
StateView::AttachedToWindow()
{
_InstallEventFilter();
BView::AttachedToWindow();
}
// DetachedFromWindow
void
StateView::DetachedFromWindow()
{
_RemoveEventFilter();
StateView::AttachedToWindow();
}
// Draw
void
StateView::Draw(BRect updateRect)
{
Draw(this, updateRect);
}
// MessageReceived
void
StateView::MessageReceived(BMessage* message)
{
// let the state handle the message if it wants
if (fCurrentState) {
AutoWriteLocker locker(fLocker);
if (fLocker && !locker.IsLocked())
return;
Command* command = NULL;
if (fCurrentState->MessageReceived(message, &command)) {
Perform(command);
return;
}
}
switch (message->what) {
case B_MODIFIERS_CHANGED:
// NOTE: received only if the view has focus!!
if (fCurrentState) {
uint32 mods;
if (message->FindInt32("modifiers", (int32*)&mods) != B_OK)
mods = modifiers();
fCurrentState->ModifiersChanged(mods);
fMouseInfo.modifiers = mods;
}
break;
default:
BView::MessageReceived(message);
}
}
// #pragma mark -
// MouseDown
void
StateView::MouseDown(BPoint where)
{
if (fLocker && !fLocker->WriteLock())
return;
// query more info from the windows current message if available
uint32 buttons;
uint32 clicks;
BMessage* message = Window() ? Window()->CurrentMessage() : NULL;
if (!message || message->FindInt32("buttons", (int32*)&buttons) != B_OK)
buttons = B_PRIMARY_MOUSE_BUTTON;
if (!message || message->FindInt32("clicks", (int32*)&clicks) != B_OK)
clicks = 1;
if (fCurrentState)
fCurrentState->MouseDown(where, buttons, clicks);
// update mouse info *after* having called the ViewState hook
fMouseInfo.buttons = buttons;
fMouseInfo.position = where;
if (fLocker)
fLocker->WriteUnlock();
}
// MouseMoved
void
StateView::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
{
if (fLocker && !fLocker->WriteLock())
return;
if (dragMessage && !fDropAnticipatingState) {
// switch to a drop anticipating state if there is one available
fDropAnticipatingState = StateForDragMessage(dragMessage);
if (fDropAnticipatingState)
fDropAnticipatingState->Init();
}
// TODO: I don't like this too much
if (!dragMessage && fDropAnticipatingState) {
fDropAnticipatingState->Cleanup();
fDropAnticipatingState = NULL;
}
if (fDropAnticipatingState)
fDropAnticipatingState->MouseMoved(where, transit, dragMessage);
else {
if (fCurrentState) {
fCurrentState->MouseMoved(where, transit, dragMessage);
if (fMouseInfo.buttons != 0)
_TriggerUpdate();
}
}
// update mouse info *after* having called the ViewState hook
fMouseInfo.position = where;
fMouseInfo.transit = transit;
if (fLocker)
fLocker->WriteUnlock();
}
// MouseUp
void
StateView::MouseUp(BPoint where)
{
if (fLocker && !fLocker->WriteLock())
return;
if (fDropAnticipatingState) {
Perform(fDropAnticipatingState->MouseUp());
fDropAnticipatingState->Cleanup();
fDropAnticipatingState = NULL;
if (fCurrentState) {
fCurrentState->MouseMoved(fMouseInfo.position, fMouseInfo.transit,
NULL);
}
} else {
if (fCurrentState) {
Perform(fCurrentState->MouseUp());
_TriggerUpdate();
}
}
// update mouse info *after* having called the ViewState hook
fMouseInfo.buttons = 0;
if (fLocker)
fLocker->WriteUnlock();
}
// #pragma mark -
// KeyDown
void
StateView::KeyDown(const char* bytes, int32 numBytes)
{
uint32 key;
uint32 modifiers;
BMessage* message = Window() ? Window()->CurrentMessage() : NULL;
if (message
&& message->FindInt32("raw_char", (int32*)&key) >= B_OK
&& message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) {
if (HandleKeyDown(key, modifiers))
return;
}
BView::KeyDown(bytes, numBytes);
}
// KeyUp
void
StateView::KeyUp(const char* bytes, int32 numBytes)
{
uint32 key;
uint32 modifiers;
BMessage* message = Window() ? Window()->CurrentMessage() : NULL;
if (message
&& message->FindInt32("raw_char", (int32*)&key) >= B_OK
&& message->FindInt32("modifiers", (int32*)&modifiers) >= B_OK) {
if (HandleKeyUp(key, modifiers))
return;
}
BView::KeyUp(bytes, numBytes);
}
// #pragma mark -
// SetState
void
StateView::SetState(ViewState* state)
{
if (fCurrentState == state)
return;
// switch states as appropriate
if (fCurrentState)
fCurrentState->Cleanup();
fCurrentState = state;
if (fCurrentState)
fCurrentState->Init();
}
// Draw
void
StateView::Draw(BView* into, BRect updateRect)
{
if (fLocker && !fLocker->ReadLock()) {
return;
}
if (fCurrentState)
fCurrentState->Draw(into, updateRect);
if (fDropAnticipatingState)
fDropAnticipatingState->Draw(into, updateRect);
if (fLocker)
fLocker->ReadUnlock();
}
// MouseWheelChanged
bool
StateView::MouseWheelChanged(float x, float y)
{
return false;
}
// HandleKeyDown
bool
StateView::HandleKeyDown(uint32 key, uint32 modifiers)
{
AutoWriteLocker locker(fLocker);
if (fLocker && !locker.IsLocked())
return false;
if (_HandleKeyDown(key, modifiers))
return true;
if (fCurrentState) {
Command* command = NULL;
if (fCurrentState->HandleKeyDown(key, modifiers, &command)) {
Perform(command);
return true;
}
}
return false;
}
// HandleKeyUp
bool
StateView::HandleKeyUp(uint32 key, uint32 modifiers)
{
AutoWriteLocker locker(fLocker);
if (fLocker && !locker.IsLocked())
return false;
if (_HandleKeyUp(key, modifiers))
return true;
if (fCurrentState) {
Command* command = NULL;
if (fCurrentState->HandleKeyUp(key, modifiers, &command)) {
Perform(command);
return true;
}
}
return false;
}
// StateForDragMessage
ViewState*
StateView::StateForDragMessage(const BMessage* message)
{
return NULL;
}
// SetCommandStack
void
StateView::SetCommandStack(::CommandStack* stack)
{
fCommandStack = stack;
}
// SetLocker
void
StateView::SetLocker(RWLocker* locker)
{
fLocker = locker;
}
// SetUpdateTarget
void
StateView::SetUpdateTarget(BHandler* target, uint32 command)
{
fUpdateTarget = target;
fUpdateCommand = command;
}
// SetCatchAllEvents
void
StateView::SetCatchAllEvents(bool catchAll)
{
if (fCatchAllEvents == catchAll)
return;
fCatchAllEvents = catchAll;
if (fCatchAllEvents)
_InstallEventFilter();
else
_RemoveEventFilter();
}
// Perform
status_t
StateView::Perform(Command* command)
{
if (fCommandStack)
return fCommandStack->Perform(command);
// if there is no command stack, then nobody
// else feels responsible...
delete command;
return B_NO_INIT;
}
// #pragma mark -
// _HandleKeyDown
bool
StateView::_HandleKeyDown(uint32 key, uint32 modifiers)
{
return false;
}
// _HandleKeyUp
bool
StateView::_HandleKeyUp(uint32 key, uint32 modifiers)
{
return false;
}
// _InstallEventFilter
void
StateView::_InstallEventFilter()
{
if (!fCatchAllEvents)
return;
if (!fEventFilter)
fEventFilter = new (nothrow) EventFilter(this);
if (!fEventFilter || !Window())
return;
Window()->AddCommonFilter(fEventFilter);
}
void
StateView::_RemoveEventFilter()
{
if (!fEventFilter || !Window())
return;
Window()->RemoveCommonFilter(fEventFilter);
}
// _TriggerUpdate
void
StateView::_TriggerUpdate()
{
if (fUpdateTarget && fUpdateTarget->Looper()) {
fUpdateTarget->Looper()->PostMessage(fUpdateCommand);
}
}
@@ -0,0 +1,98 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef STATE_VIEW_H
#define STATE_VIEW_H
#include <View.h>
#include "ViewState.h"
class BMessageFilter;
class Command;
class CommandStack;
class RWLocker;
class StateView : public BView {
public:
StateView(BRect frame, const char* name,
uint32 resizingMode, uint32 flags);
virtual ~StateView();
// BView interface
virtual void AttachedToWindow();
virtual void DetachedFromWindow();
virtual void Draw(BRect updateRect);
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 KeyDown(const char* bytes, int32 numBytes);
virtual void KeyUp(const char* bytes, int32 numBytes);
// StateView interface
void SetState(ViewState* state);
void Draw(BView* into, BRect updateRect);
virtual bool MouseWheelChanged(float x, float y);
bool HandleKeyDown(uint32 key, uint32 modifiers);
bool HandleKeyUp(uint32 key, uint32 modifiers);
const mouse_info* MouseInfo() const
{ return &fMouseInfo; }
virtual ViewState* StateForDragMessage(const BMessage* message);
void SetLocker(RWLocker* locker);
RWLocker* Locker() const
{ return fLocker; }
void SetCommandStack(::CommandStack* stack);
::CommandStack* CommandStack() const
{ return fCommandStack; }
void SetUpdateTarget(BHandler* target,
uint32 command);
void SetCatchAllEvents(bool catchAll);
status_t Perform(Command* command);
protected:
virtual bool _HandleKeyDown(uint32 key, uint32 modifiers);
virtual bool _HandleKeyUp(uint32 key, uint32 modifiers);
void _InstallEventFilter();
void _RemoveEventFilter();
void _TriggerUpdate();
ViewState* fCurrentState;
ViewState* fDropAnticipatingState;
// the drop anticipation state is some
// kind of "temporary" state that is
// used on top of the current state (it
// doesn't replace it)
mouse_info fMouseInfo;
::CommandStack* fCommandStack;
RWLocker* fLocker;
BMessageFilter* fEventFilter;
bool fCatchAllEvents;
BHandler* fUpdateTarget;
uint32 fUpdateCommand;
};
#endif // STATE_VIEW_H
@@ -0,0 +1,112 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "ViewState.h"
#include "StateView.h"
mouse_info::mouse_info()
: buttons(0),
position(B_ORIGIN),
transit(B_OUTSIDE_VIEW),
modifiers(::modifiers())
{
}
// constructor
ViewState::ViewState(StateView* view)
: fView(view),
fMouseInfo(view->MouseInfo())
{
}
// constructor
ViewState::ViewState(const ViewState& other)
: fView(other.fView),
fMouseInfo(other.fMouseInfo)
{
}
// destructor
ViewState::~ViewState()
{
}
// #pragma mark -
// Init
void
ViewState::Init()
{
}
// Cleanup
void
ViewState::Cleanup()
{
}
// #pragma mark -
// Draw
void
ViewState::Draw(BView* into, BRect updateRect)
{
}
// MessageReceived
bool
ViewState::MessageReceived(BMessage* message, Command** _command)
{
return false;
}
// #pragma mark -
// MouseDown
void
ViewState::MouseDown(BPoint where, uint32 buttons, uint32 clicks)
{
}
// MouseMoved
void
ViewState::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
{
}
// MouseUp
Command*
ViewState::MouseUp()
{
return NULL;
}
// #pragma mark -
// ModifiersChanged
void
ViewState::ModifiersChanged(uint32 modifiers)
{
}
// HandleKeyDown
bool
ViewState::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command)
{
return false;
}
// HandleKeyUp
bool
ViewState::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command)
{
return false;
}
@@ -0,0 +1,89 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef VIEW_STATE_H
#define VIEW_STATE_H
#include <View.h>
class BMessage;
class Command;
class StateView;
struct mouse_info {
mouse_info();
uint32 buttons;
BPoint position;
uint32 transit;
uint32 modifiers;
};
class ViewState {
public:
ViewState(StateView* view);
ViewState(const ViewState& other);
virtual ~ViewState();
// ViewState interface
virtual void Init();
virtual void Cleanup();
virtual void Draw(BView* into, BRect updateRect);
virtual bool MessageReceived(BMessage* message,
Command** _command);
// mouse tracking
virtual void MouseDown(BPoint where,
uint32 buttons,
uint32 clicks);
virtual void MouseMoved(BPoint where,
uint32 transit,
const BMessage* dragMessage);
virtual Command* MouseUp();
// modifiers
virtual void ModifiersChanged(uint32 modifiers);
// TODO: mouse wheel
virtual bool HandleKeyDown(uint32 key, uint32 modifiers,
Command** _command);
virtual bool HandleKeyUp(uint32 key, uint32 modifiers,
Command** _command);
inline uint32 PressedMouseButtons() const
{ return fMouseInfo->buttons; }
inline bool IsFirstButtonDown() const
{ return fMouseInfo->buttons & B_PRIMARY_MOUSE_BUTTON; }
inline bool IsSecondButtonDown() const
{ return fMouseInfo->buttons & B_SECONDARY_MOUSE_BUTTON; }
inline bool IsThirdButtonDown() const
{ return fMouseInfo->buttons & B_TERTIARY_MOUSE_BUTTON; }
inline BPoint MousePos() const
{ return fMouseInfo->position; }
inline uint32 Modifiers() const
{ return fMouseInfo->modifiers; }
protected:
StateView* fView;
// NOTE: the intention of using a pointer
// to a mouse_info struct is that all
// ViewStates belonging to the same StateView
// should have the same pointer, so that
// they will all be up to date with the same info
const mouse_info* fMouseInfo;
};
#endif // VIEW_STATE_H
@@ -0,0 +1,18 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef UI_DEFINES_H
#define UI_DEFINES_H
const rgb_color kBlack = { 0, 0, 0, 255 };
const rgb_color kWhite = { 255, 255, 255, 255 };
const rgb_color kOrange = { 255, 217, 121, 255 };
const rgb_color kLightOrange = { 255, 217, 138, 255 };
const rgb_color kDarkOrange = { 255, 145, 71, 255 };
#endif // UI_DEFINES_H
@@ -0,0 +1,60 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
#include "AbstractLOAdapter.h"
#include <Handler.h>
#include <Looper.h>
#include <Messenger.h>
// constructor
AbstractLOAdapter::AbstractLOAdapter(BHandler* handler)
: fHandler(handler),
fMessenger(NULL)
{
}
// constructor
AbstractLOAdapter::AbstractLOAdapter(const BMessenger& messenger)
: fHandler(NULL),
fMessenger(new BMessenger(messenger))
{
}
// destructor
AbstractLOAdapter::~AbstractLOAdapter()
{
delete fMessenger;
}
// DeliverMessage
void
AbstractLOAdapter::DeliverMessage(BMessage* message)
{
if (fHandler) {
if (BLooper* looper = fHandler->Looper())
looper->PostMessage(message, fHandler);
} else if (fMessenger)
fMessenger->SendMessage(message);
}
// DeliverMessage
void
AbstractLOAdapter::DeliverMessage(BMessage& message)
{
DeliverMessage(&message);
}
// DeliverMessage
void
AbstractLOAdapter::DeliverMessage(uint32 command)
{
BMessage message(command);
DeliverMessage(&message);
}
@@ -0,0 +1,41 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <[email protected]>
*/
// This class provides some basic functionality for derivation of a
// listener -> observer adapter.
// The derived class should implement constructors similar to the
// ones of this class and pass the respective parameter.
// Each of the listener hook functions should construct a message
// and let it be delivered by DeliverMessage().
#ifndef ABSTRACT_LO_ADAPTER_H
#define ABSTRACT_LO_ADAPTER_H
#include <SupportDefs.h>
class BHandler;
class BLooper;
class BMessage;
class BMessenger;
class AbstractLOAdapter {
public:
AbstractLOAdapter(BHandler* handler);
AbstractLOAdapter(const BMessenger& messenger);
virtual ~AbstractLOAdapter();
void DeliverMessage(BMessage* message);
void DeliverMessage(BMessage& message);
void DeliverMessage(uint32 command);
private:
BHandler* fHandler;
BMessenger* fMessenger;
};
#endif // ABSTRACT_LO_ADAPTER_H
@@ -0,0 +1,86 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Observable.h"
#include <stdio.h>
#include <OS.h>
#include "Observer.h"
// constructor
Observable::Observable()
: fObservers(2),
fSuspended(0),
fPendingNotifications(false)
{
}
// destructor
Observable::~Observable()
{
if (fObservers.CountItems() > 0) {
char message[256];
sprintf(message, "Observable::~Observable() - %ld "
"observers still watching!\n", fObservers.CountItems());
debugger(message);
}
}
// AddObserver
bool
Observable::AddObserver(Observer* observer)
{
if (observer && !fObservers.HasItem((void*)observer)) {
return fObservers.AddItem((void*)observer);
}
return false;
}
// RemoveObserver
bool
Observable::RemoveObserver(Observer* observer)
{
return fObservers.RemoveItem((void*)observer);
}
// Notify
void
Observable::Notify() const
{
if (!fSuspended) {
BList observers(fObservers);
int32 count = observers.CountItems();
for (int32 i = 0; i < count; i++)
((Observer*)observers.ItemAtFast(i))->ObjectChanged(this);
fPendingNotifications = false;
} else {
fPendingNotifications = true;
}
}
// SuspendNotifications
void
Observable::SuspendNotifications(bool suspend)
{
if (suspend)
fSuspended++;
else
fSuspended--;
if (fSuspended < 0) {
fprintf(stderr, "Observable::SuspendNotifications(false) - "
"error: suspend level below zero!\n");
fSuspended = 0;
}
if (!fSuspended && fPendingNotifications)
Notify();
}
@@ -0,0 +1,51 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef OBSERVABLE_H
#define OBSERVABLE_H
#include <List.h>
class Observer;
class Observable {
public:
Observable();
virtual ~Observable();
bool AddObserver(Observer* observer);
bool RemoveObserver(Observer* observer);
void Notify() const;
void SuspendNotifications(bool suspend);
private:
BList fObservers;
int32 fSuspended;
mutable bool fPendingNotifications;
};
class AutoNotificationSuspender {
public:
AutoNotificationSuspender(Observable* object)
: fObject(object)
{
fObject->SuspendNotifications(true);
}
virtual ~AutoNotificationSuspender()
{
fObject->SuspendNotifications(false);
}
private:
Observable* fObject;
};
#endif // OBSERVABLE_H
@@ -0,0 +1,18 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Observer.h"
Observer::Observer()
{
}
Observer::~Observer()
{
}
@@ -0,0 +1,24 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef OBSERVER_H
#define OBSERVER_H
#include <SupportDefs.h>
class Observable;
class Observer {
public:
Observer();
virtual ~Observer();
virtual void ObjectChanged(const Observable* object) = 0;
};
#endif // OBSERVER_H
@@ -0,0 +1,30 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Selectable.h"
// constructor
Selectable::Selectable()
: fSelected(false)
{
}
// destructor
Selectable::~Selectable()
{
}
// SetSelected
void
Selectable::SetSelected(bool selected)
{
if (fSelected != selected) {
fSelected = selected;
SelectedChanged();
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef SELECTABLE_H
#define SELECTABLE_H
#include <SupportDefs.h>
class Selectable {
public:
Selectable();
virtual ~Selectable();
inline bool IsSelected() const
{ return fSelected; }
virtual void SelectedChanged() = 0;
private:
friend class Selection;
void SetSelected(bool selected);
bool fSelected;
};
#endif // SELECTABLE_H
@@ -0,0 +1,147 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "Selection.h"
#include <stdio.h>
#include <debugger.h>
#include "Selectable.h"
#define DEBUG 1
// constructor
Selection::Selection()
: fSelected(20)
{
}
// destructor
Selection::~Selection()
{
}
// Select
bool
Selection::Select(Selectable* object, bool extend)
{
AutoNotificationSuspender _(this);
if (!extend)
_DeselectAllExcept(object);
bool success = false;
if (!object->IsSelected()) {
#if DEBUG
if (fSelected.HasItem((void*)object))
debugger("Selection::Select() - "
"unselected object in list!");
#endif
if (fSelected.AddItem((void*)object)) {
object->SetSelected(true);
success = true;
Notify();
} else {
fprintf(stderr, "Selection::Select() - out of memory\n");
}
} else {
#if DEBUG
if (!fSelected.HasItem((void*)object))
debugger("Selection::Select() - "
"already selected object not in list!");
#endif
success = true;
// object already in list
}
return success;
}
// Deselect
void
Selection::Deselect(Selectable* object)
{
if (object->IsSelected()) {
if (!fSelected.RemoveItem((void*)object))
debugger("Selection::Deselect() - "
"selected object not within list!");
object->SetSelected(false);
Notify();
}
}
// DeselectAll
void
Selection::DeselectAll()
{
_DeselectAllExcept(NULL);
}
// #pragma mark -
// SelectableAt
Selectable*
Selection::SelectableAt(int32 index) const
{
return (Selectable*)fSelected.ItemAt(index);
}
// SelectableAtFast
Selectable*
Selection::SelectableAtFast(int32 index) const
{
return (Selectable*)fSelected.ItemAtFast(index);
}
// CountSelected
int32
Selection::CountSelected() const
{
return fSelected.CountItems();
}
// #pragma mark -
// _DeselectAllExcept
void
Selection::_DeselectAllExcept(Selectable* except)
{
bool notify = false;
bool containedExcept = false;
int32 count = fSelected.CountItems();
for (int32 i = 0; i < count; i++) {
Selectable* object = (Selectable*)fSelected.ItemAtFast(i);
if (object != except) {
object->SetSelected(false);
notify = true;
} else {
containedExcept = true;
}
}
fSelected.MakeEmpty();
// if the "except" object was previously
// in the selection, add it again after
// making the selection list empty
if (containedExcept)
fSelected.AddItem(except);
if (notify)
Notify();
}
@@ -0,0 +1,40 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef SELECTION_H
#define SELECTION_H
#include <List.h>
#include "Observable.h"
class Selectable;
class Selection : public Observable {
public:
Selection();
virtual ~Selection();
// modify selection
bool Select(Selectable* object,
bool extend = false);
void Deselect(Selectable* object);
void DeselectAll();
// query selection
Selectable* SelectableAt(int32 index) const;
Selectable* SelectableAtFast(int32 index) const;
int32 CountSelected() const;
private:
void _DeselectAllExcept(Selectable* object);
BList fSelected;
};
#endif // SELECTION_H
@@ -0,0 +1,133 @@
/*
* Copyright 2001-2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* IngoWeinhold <[email protected]>
*/
/** Scope-based automatic deletion of objects/arrays.
* ObjectDeleter - deletes an object
* ArrayDeleter - deletes an array
* MemoryDeleter - free()s malloc()ed memory
*/
#ifndef _AUTO_DELETER_H
#define _AUTO_DELETER_H
#include <stdlib.h>
namespace BPrivate {
// AutoDeleter
template<typename C, typename DeleteFunc>
class AutoDeleter {
public:
inline AutoDeleter()
: fObject(NULL)
{
}
inline AutoDeleter(C *object)
: fObject(object)
{
}
inline ~AutoDeleter()
{
fDelete(fObject);
}
inline void SetTo(C *object)
{
if (object != fObject) {
fDelete(fObject);
fObject = object;
}
}
inline void Unset()
{
SetTo(NULL);
}
inline void Delete()
{
SetTo(NULL);
}
inline C *Detach()
{
C *object = fObject;
fObject = NULL;
return object;
}
private:
C *fObject;
DeleteFunc fDelete;
};
// ObjectDeleter
template<typename C>
struct ObjectDelete
{
inline void operator()(C *object)
{
delete object;
}
};
template<typename C>
struct ObjectDeleter : AutoDeleter<C, ObjectDelete<C> >
{
ObjectDeleter() : AutoDeleter<C, ObjectDelete<C> >() {}
ObjectDeleter(C *object) : AutoDeleter<C, ObjectDelete<C> >(object) {}
};
// ArrayDeleter
template<typename C>
struct ArrayDelete
{
inline void operator()(C *array)
{
delete[] array;
}
};
template<typename C>
struct ArrayDeleter : AutoDeleter<C, ArrayDelete<C> >
{
ArrayDeleter() : AutoDeleter<C, ArrayDelete<C> >() {}
ArrayDeleter(C *array) : AutoDeleter<C, ArrayDelete<C> >(array) {}
};
// MemoryDeleter
struct MemoryDelete
{
inline void operator()(void *memory)
{
free(memory);
}
};
struct MemoryDeleter : AutoDeleter<void, MemoryDelete >
{
MemoryDeleter() : AutoDeleter<void, MemoryDelete >() {}
MemoryDeleter(void *memory) : AutoDeleter<void, MemoryDelete >(memory) {}
};
} // namespace BPrivate
using BPrivate::ObjectDeleter;
using BPrivate::ArrayDeleter;
using BPrivate::MemoryDeleter;
#endif // _AUTO_DELETER_H
@@ -0,0 +1,151 @@
/*
* Copyright 2004-2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* IngoWeinhold <[email protected]>
*/
/** Scope-based automatic deletion of objects/arrays.
* ObjectDeleter - deletes an object
* ArrayDeleter - deletes an array
* MemoryDeleter - free()s malloc()ed memory
*/
#ifndef AUTO_LOCKER_H
#define AUTO_LOCKER_H
#include <SupportDefs.h>
// locking
// AutoLockerStandardLocking
template<typename Lockable>
class AutoLockerStandardLocking {
public:
inline bool Lock(Lockable *lockable)
{
return lockable->Lock();
}
inline void Unlock(Lockable *lockable)
{
lockable->Unlock();
}
};
// AutoLockerReadLocking
template<typename Lockable>
class AutoLockerReadLocking {
public:
inline bool Lock(Lockable *lockable)
{
return lockable->ReadLock();
}
inline void Unlock(Lockable *lockable)
{
lockable->ReadUnlock();
}
};
// AutoLockerWriteLocking
template<typename Lockable>
class AutoLockerWriteLocking {
public:
inline bool Lock(Lockable *lockable)
{
return lockable->WriteLock();
}
inline void Unlock(Lockable *lockable)
{
lockable->WriteUnlock();
}
};
// AutoLocker
template<typename Lockable,
typename Locking = AutoLockerStandardLocking<Lockable> >
class AutoLocker {
private:
typedef AutoLocker<Lockable, Locking> ThisClass;
public:
inline AutoLocker(Lockable *lockable, bool alreadyLocked = false)
: fLockable(lockable),
fLocked(fLockable && alreadyLocked)
{
if (!fLocked)
_Lock();
}
inline AutoLocker(Lockable &lockable, bool alreadyLocked = false)
: fLockable(&lockable),
fLocked(fLockable && alreadyLocked)
{
if (!fLocked)
_Lock();
}
inline ~AutoLocker()
{
Unlock();
}
inline void SetTo(Lockable *lockable, bool alreadyLocked)
{
Unlock();
fLockable = lockable;
fLocked = alreadyLocked;
if (!fLocked)
_Lock();
}
inline void SetTo(Lockable &lockable, bool alreadyLocked)
{
SetTo(&lockable, alreadyLocked);
}
inline void Unset()
{
Unlock();
}
inline AutoLocker<Lockable, Locking> &operator=(Lockable *lockable)
{
SetTo(lockable);
return *this;
}
inline AutoLocker<Lockable, Locking> &operator=(Lockable &lockable)
{
SetTo(&lockable);
return *this;
}
inline bool IsLocked() const { return fLocked; }
inline void Unlock()
{
if (fLockable && fLocked) {
fLocking.Unlock(fLockable);
fLocked = false;
}
}
inline operator bool() const { return fLocked; }
private:
inline void _Lock()
{
if (fLockable)
fLocked = fLocking.Lock(fLockable);
}
private:
Lockable *fLockable;
bool fLocked;
Locking fLocking;
};
#endif // AUTO_LOCKER_H
@@ -0,0 +1,151 @@
/*
* Copyright 2003-2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* IngoWeinhold <[email protected]>
*/
#include "Debug.h"
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <OS.h>
/*!
\file Debug.cpp
\brief Defines debug output function with printf() signature printing
into a file.
\note The initialization is not thread safe!
*/
// locking support
static int32 init_counter = 0;
static sem_id dbg_printf_sem = -1;
static thread_id dbg_printf_thread = -1;
static int dbg_printf_nesting = 0;
#if DEBUG_PRINT
static int out = -1;
#endif
// init_debugging
status_t
init_debugging()
{
status_t error = B_OK;
if (init_counter++ == 0) {
// open the file
#if DEBUG_PRINT
out = open(DEBUG_PRINT_FILE, O_RDWR | O_CREAT | O_TRUNC);
if (out < 0) {
error = errno;
init_counter--;
}
#endif // DEBUG_PRINT
// allocate the semaphore
if (error == B_OK) {
dbg_printf_sem = create_sem(1, "dbg_printf");
if (dbg_printf_sem < 0)
error = dbg_printf_sem;
}
if (error == B_OK) {
#if DEBUG
__out("##################################################\n");
#endif
} else
exit_debugging();
}
return error;
}
// exit_debugging
status_t
exit_debugging()
{
status_t error = B_OK;
if (--init_counter == 0) {
#if DEBUG_PRINT
close(out);
out = -1;
#endif // DEBUG_PRINT
delete_sem(dbg_printf_sem);
} else
error = B_NO_INIT;
return error;
}
// dbg_printf_lock
static inline
bool
dbg_printf_lock()
{
thread_id thread = find_thread(NULL);
if (thread != dbg_printf_thread) {
if (acquire_sem(dbg_printf_sem) != B_OK)
return false;
dbg_printf_thread = thread;
}
dbg_printf_nesting++;
return true;
}
// dbg_printf_unlock
static inline
void
dbg_printf_unlock()
{
thread_id thread = find_thread(NULL);
if (thread != dbg_printf_thread)
return;
dbg_printf_nesting--;
if (dbg_printf_nesting == 0) {
dbg_printf_thread = -1;
release_sem(dbg_printf_sem);
}
}
// dbg_printf_begin
void
dbg_printf_begin()
{
dbg_printf_lock();
}
// dbg_printf_end
void
dbg_printf_end()
{
dbg_printf_unlock();
}
#if DEBUG_PRINT
// dbg_printf
void
dbg_printf(const char *format,...)
{
if (!dbg_printf_lock())
return;
char buffer[1024];
va_list args;
va_start(args, format);
// no vsnprintf() on PPC and in kernel
#if defined(__INTEL__) && USER
vsnprintf(buffer, sizeof(buffer) - 1, format, args);
#else
vsprintf(buffer, format, args);
#endif
va_end(args);
buffer[sizeof(buffer) - 1] = '\0';
write(out, buffer, strlen(buffer));
dbg_printf_unlock();
}
#endif // DEBUG_PRINT
@@ -0,0 +1,152 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler <[email protected]>
* IngoWeinhold <[email protected]>
*/
#ifndef DEBUG_H
#define DEBUG_H
#include <string.h>
// USER defaults to 1
#ifndef USER
# define USER 1
#endif
#if !USER
# include <KernelExport.h>
#endif
#include <OS.h>
#include <SupportDefs.h>
// define all macros we work with -- undefined macros are set to defaults
#ifndef DEBUG
# define DEBUG 0
#endif
#if !DEBUG
# undef DEBUG_PRINT
# define DEBUG_PRINT 0
#endif
#ifndef DEBUG_PRINT
# define DEBUG_PRINT 0
#endif
#ifndef DEBUG_APP
# define DEBUG_APP "debug"
#endif
#ifndef DEBUG_PRINT_FILE
# define DEBUG_PRINT_FILE "/var/log/" DEBUG_APP ".log"
#endif
// define the debug output function
#if USER
# include <stdio.h>
# if DEBUG_PRINT
# define __out dbg_printf
# else
# define __out printf
# endif
#else
# include <KernelExport.h>
# include <null.h>
# if DEBUG_PRINT
# define __out dbg_printf
# else
# define __out dprintf
# endif
#endif
// define the PANIC() macro
#ifndef PANIC
# if USER
# define PANIC(str) debugger(str)
# else
# define PANIC(str) panic(str)
# endif
#endif
// functions exported by this module
status_t init_debugging();
status_t exit_debugging();
void dbg_printf_begin();
void dbg_printf_end();
#if DEBUG_PRINT
void dbg_printf(const char *format,...);
#else
static inline void dbg_printf(const char *,...) {}
#endif
// Short overview over the debug output macros:
// PRINT()
// is for general messages that very unlikely should appear in a release build
// FATAL()
// this is for fatal messages, when something has really gone wrong
// INFORM()
// general information, as disk size, etc.
// REPORT_ERROR(status_t)
// prints out error information
// RETURN_ERROR(status_t)
// calls REPORT_ERROR() and return the value
// D()
// the statements in D() are only included if DEBUG is defined
#if __MWERKS__
# define __FUNCTION__ ""
#endif
#define DEBUG_THREAD find_thread(NULL)
#define DEBUG_CONTEXT(x) { dbg_printf_begin(); __out(DEBUG_APP " [%Ld: %5ld] ", system_time(), DEBUG_THREAD); x; dbg_printf_end(); }
#define DEBUG_CONTEXT_FUNCTION(prefix, x) { dbg_printf_begin(); __out(DEBUG_APP " [%Ld: %5ld] %s()" prefix, system_time(), DEBUG_THREAD, __FUNCTION__); x; dbg_printf_end(); }
#define DEBUG_CONTEXT_LINE(x) { dbg_printf_begin(); __out(DEBUG_APP " [%Ld: %5ld] %s():%d: ", system_time(), DEBUG_THREAD, __FUNCTION__, __LINE__); x; dbg_printf_end(); }
#define TPRINT(x) DEBUG_CONTEXT( __out x )
#define TREPORT_ERROR(status) DEBUG_CONTEXT_LINE( __out("%s\n", strerror(status)) )
#define TRETURN_ERROR(err) { status_t _status = err; if (_status < B_OK) TREPORT_ERROR(_status); return _status;}
#define TSET_ERROR(var, err) { status_t _status = err; if (_status < B_OK) TREPORT_ERROR(_status); var = _status; }
#define TFUNCTION(x) DEBUG_CONTEXT_FUNCTION( ": ", __out x )
#define TFUNCTION_START() DEBUG_CONTEXT_FUNCTION( "\n", )
#define TFUNCTION_END() DEBUG_CONTEXT_FUNCTION( " done\n", )
#if DEBUG
#define PRINT(x) TPRINT(x)
#define REPORT_ERROR(status) TREPORT_ERROR(status)
#define RETURN_ERROR(err) TRETURN_ERROR(err)
#define SET_ERROR(var, err) TSET_ERROR(var, err)
#define FATAL(x) DEBUG_CONTEXT( __out x )
#define ERROR(x) DEBUG_CONTEXT( __out x )
#define WARN(x) DEBUG_CONTEXT( __out x )
#define INFORM(x) DEBUG_CONTEXT( __out x )
#define FUNCTION(x) TFUNCTION(x)
#define FUNCTION_START() TFUNCTION_START()
#define FUNCTION_END() TFUNCTION_END()
#define D(x) {x;};
#else
#define PRINT(x) ;
#define REPORT_ERROR(status) ;
#define RETURN_ERROR(status) return status;
#define SET_ERROR(var, err) var = err;
#define FATAL(x) DEBUG_CONTEXT( __out x )
#define ERROR(x) DEBUG_CONTEXT( __out x )
#define WARN(x) DEBUG_CONTEXT( __out x )
#define INFORM(x) DEBUG_CONTEXT( __out x )
#define FUNCTION(x) ;
#define FUNCTION_START() ;
#define FUNCTION_END() ;
#define D(x) ;
#endif
#ifndef TOUCH
#define TOUCH(var) (void)var
#endif
static inline void nodebug(...)
{
}
#define debug dbg_printf
#endif /* DEBUG_H */
@@ -0,0 +1,78 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* IngoWeinhold <[email protected]>
* Stephan Aßmus <[email protected]>
*/
#ifndef LIST_H
#define LIST_H
#include <List.h>
template<class T, bool delete_on_destruction = true>
class List : protected BList {
public:
List(int32 count = 10)
: BList(count) {}
~List()
{ MakeEmpty(); }
// adding items
inline void AddItem(T value)
{ BList::AddItem((void*)value); }
inline void AddItem(T value, int32 index)
{ BList::AddItem((void*)value, index); }
// information
inline bool HasItem(T value) const
{ return BList::HasItem((void*)value); }
inline int32 IndexOf(T value) const
{ return BList::IndexOf((void*)value); }
inline bool IsEmpty() const
{ return BList::IsEmpty(); }
inline int32 CountItems() const
{ return BList::CountItems(); }
// retrieving items
inline T ItemAt(int32 index) const
{ return (T)BList::ItemAt(index); }
inline T ItemAtFast(int32 index) const
{ return (T)BList::ItemAtFast(index); }
inline T FirstItem() const
{ return (T)BList::FirstItem(); }
inline T LastItem() const
{ return (T)BList::LastItem(); }
// removing items
inline bool RemoveItem(T value)
{ return BList::RemoveItem((void*)value); }
inline T RemoveItem(int32 index)
{ return (T)BList::RemoveItem(index); }
inline bool RemoveItems(int32 index, int32 count)
{ return BList::RemoveItems(index, count); }
inline void MakeEmpty() {
if (delete_on_destruction) {
// delete all values
int32 count = CountItems();
for (int32 i = 0; i < count; i++)
delete (T)BList::ItemAtFast(i);
}
BList::MakeEmpty();
}
};
#endif // LIST_H
@@ -0,0 +1,471 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* IngoWeinhold <[email protected]>
*/
#include "RWLocker.h"
#include <String.h>
// info about a read lock owner
struct RWLocker::ReadLockInfo {
thread_id reader;
int32 count;
};
// constructor
RWLocker::RWLocker()
: fLock(),
fMutex(),
fQueue(),
fReaderCount(0),
fWriterCount(0),
fReadLockInfos(8),
fWriter(B_ERROR),
fWriterWriterCount(0),
fWriterReaderCount(0)
{
_Init(NULL);
}
// constructor
RWLocker::RWLocker(const char* name)
: fLock(name),
fMutex(),
fQueue(),
fReaderCount(0),
fWriterCount(0),
fReadLockInfos(8),
fWriter(B_ERROR),
fWriterWriterCount(0),
fWriterReaderCount(0)
{
_Init(name);
}
// destructor
RWLocker::~RWLocker()
{
fLock.Lock();
delete_sem(fMutex.semaphore);
delete_sem(fQueue.semaphore);
for (int32 i = 0; ReadLockInfo* info = _ReadLockInfoAt(i); i++)
delete info;
}
// ReadLock
bool
RWLocker::ReadLock()
{
status_t error = _ReadLock(B_INFINITE_TIMEOUT);
return (error == B_OK);
}
// ReadLockWithTimeout
status_t
RWLocker::ReadLockWithTimeout(bigtime_t timeout)
{
bigtime_t absoluteTimeout = system_time() + timeout;
// take care of overflow
if (timeout > 0 && absoluteTimeout < 0)
absoluteTimeout = B_INFINITE_TIMEOUT;
return _ReadLock(absoluteTimeout);
}
// ReadUnlock
void
RWLocker::ReadUnlock()
{
if (fLock.Lock()) {
thread_id thread = find_thread(NULL);
if (thread == fWriter) {
// We (also) have a write lock.
if (fWriterReaderCount > 0)
fWriterReaderCount--;
// else: error: unmatched ReadUnlock()
} else {
int32 index = _IndexOf(thread);
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
fReaderCount--;
if (--info->count == 0) {
// The outer read lock bracket for the thread has been
// reached. Dispose the info.
_DeleteReadLockInfo(index);
}
if (fReaderCount == 0) {
// The last reader needs to unlock the mutex.
_ReleaseBenaphore(fMutex);
}
} // else: error: caller has no read lock
}
fLock.Unlock();
} // else: we are probably going to be destroyed
}
// IsReadLocked
//
// Returns whether or not the calling thread owns a read lock or even a
// write lock.
bool
RWLocker::IsReadLocked() const
{
bool result = false;
if (fLock.Lock()) {
thread_id thread = find_thread(NULL);
result = (thread == fWriter || _IndexOf(thread) >= 0);
fLock.Unlock();
}
return result;
}
// WriteLock
bool
RWLocker::WriteLock()
{
status_t error = _WriteLock(B_INFINITE_TIMEOUT);
return (error == B_OK);
}
// WriteLockWithTimeout
status_t
RWLocker::WriteLockWithTimeout(bigtime_t timeout)
{
bigtime_t absoluteTimeout = system_time() + timeout;
// take care of overflow
if (timeout > 0 && absoluteTimeout < 0)
absoluteTimeout = B_INFINITE_TIMEOUT;
return _WriteLock(absoluteTimeout);
}
// WriteUnlock
void
RWLocker::WriteUnlock()
{
if (fLock.Lock()) {
thread_id thread = find_thread(NULL);
if (thread == fWriter) {
fWriterCount--;
if (--fWriterWriterCount == 0) {
// The outer write lock bracket for the thread has been
// reached.
fWriter = B_ERROR;
if (fWriterReaderCount > 0) {
// We still own read locks.
_NewReadLockInfo(thread, fWriterReaderCount);
// A reader that expects to be the first reader may wait
// at the mutex semaphore. We need to wake it up.
if (fReaderCount > 0)
_ReleaseBenaphore(fMutex);
fReaderCount += fWriterReaderCount;
fWriterReaderCount = 0;
} else {
// We don't own any read locks. So we have to release the
// mutex benaphore.
_ReleaseBenaphore(fMutex);
}
}
} // else: error: unmatched WriteUnlock()
fLock.Unlock();
} // else: We're probably going to die.
}
// IsWriteLocked
//
// Returns whether or not the calling thread owns a write lock.
bool
RWLocker::IsWriteLocked() const
{
return (fWriter == find_thread(NULL));
}
// _Init
void
RWLocker::_Init(const char* name)
{
// init the mutex benaphore
BString mutexName(name);
mutexName += "_RWLocker_mutex";
fMutex.semaphore = create_sem(0, mutexName.String());
fMutex.counter = 0;
// init the queueing benaphore
BString queueName(name);
queueName += "_RWLocker_queue";
fQueue.semaphore = create_sem(0, queueName.String());
fQueue.counter = 0;
}
// _ReadLock
//
// /timeout/ -- absolute timeout
status_t
RWLocker::_ReadLock(bigtime_t timeout)
{
status_t error = B_OK;
thread_id thread = find_thread(NULL);
bool locked = false;
if (fLock.Lock()) {
// Check, if we already own a read (or write) lock. In this case we
// can skip the usual locking procedure.
if (thread == fWriter) {
// We already own a write lock.
fWriterReaderCount++;
locked = true;
} else if (ReadLockInfo* info = _ReadLockInfoAt(_IndexOf(thread))) {
// We already own a read lock.
info->count++;
fReaderCount++;
locked = true;
}
fLock.Unlock();
} else // failed to lock the data
error = B_ERROR;
// Usual locking, i.e. we do not already own a read or write lock.
if (error == B_OK && !locked) {
error = _AcquireBenaphore(fQueue, timeout);
if (error == B_OK) {
if (fLock.Lock()) {
bool firstReader = false;
if (++fReaderCount == 1) {
// We are the first reader.
_NewReadLockInfo(thread);
firstReader = true;
} else
_NewReadLockInfo(thread);
fLock.Unlock();
// The first reader needs to lock the mutex.
if (firstReader) {
error = _AcquireBenaphore(fMutex, timeout);
switch (error) {
case B_OK:
// fine
break;
case B_TIMED_OUT: {
// clean up
if (fLock.Lock()) {
_DeleteReadLockInfo(_IndexOf(thread));
fReaderCount--;
fLock.Unlock();
}
break;
}
default:
// Probably we are going to be destroyed.
break;
}
}
// Let the next candidate enter the game.
_ReleaseBenaphore(fQueue);
} else {
// We couldn't lock the data, which can only happen, if
// we're going to be destroyed.
error = B_ERROR;
}
}
}
return error;
}
// _WriteLock
//
// /timeout/ -- absolute timeout
status_t
RWLocker::_WriteLock(bigtime_t timeout)
{
status_t error = B_ERROR;
if (fLock.Lock()) {
bool infiniteTimeout = (timeout == B_INFINITE_TIMEOUT);
bool locked = false;
int32 readerCount = 0;
thread_id thread = find_thread(NULL);
int32 index = _IndexOf(thread);
if (ReadLockInfo* info = _ReadLockInfoAt(index)) {
// We already own a read lock.
if (fWriterCount > 0) {
// There are writers before us.
if (infiniteTimeout) {
// Timeout is infinite and there are writers before us.
// Unregister the read locks and lock as usual.
readerCount = info->count;
fWriterCount++;
fReaderCount -= readerCount;
_DeleteReadLockInfo(index);
error = B_OK;
} else {
// The timeout is finite and there are readers before us:
// let the write lock request fail.
error = B_WOULD_BLOCK;
}
} else if (info->count == fReaderCount) {
// No writers before us.
// We are the only read lock owners. Just move the read lock
// info data to the special writer fields and then we are done.
// Note: At this point we may overtake readers that already
// have acquired the queueing benaphore, but have not yet
// locked the data. But that doesn't harm.
fWriter = thread;
fWriterCount++;
fWriterWriterCount = 1;
fWriterReaderCount = info->count;
fReaderCount -= fWriterReaderCount;
_DeleteReadLockInfo(index);
locked = true;
error = B_OK;
} else {
// No writers before us, but other readers.
// Note, we're quite restrictive here. If there are only
// readers before us, we could reinstall our readers, if
// our request times out. Unfortunately it is not easy
// to ensure, that no writer overtakes us between unlocking
// the data and acquiring the queuing benaphore.
if (infiniteTimeout) {
// Unregister the readers and lock as usual.
readerCount = info->count;
fWriterCount++;
fReaderCount -= readerCount;
_DeleteReadLockInfo(index);
error = B_OK;
} else
error = B_WOULD_BLOCK;
}
} else {
// We don't own a read lock.
if (fWriter == thread) {
// ... but a write lock.
fWriterCount++;
fWriterWriterCount++;
locked = true;
error = B_OK;
} else {
// We own neither read nor write locks.
// Lock as usual.
fWriterCount++;
error = B_OK;
}
}
fLock.Unlock();
// Usual locking...
// First step: acquire the queueing benaphore.
if (!locked && error == B_OK) {
error = _AcquireBenaphore(fQueue, timeout);
switch (error) {
case B_OK:
break;
case B_TIMED_OUT: {
// clean up
if (fLock.Lock()) {
fWriterCount--;
fLock.Unlock();
} // else: failed to lock the data: we're probably going
// to die.
break;
}
default:
// Probably we're going to die.
break;
}
}
// Second step: acquire the mutex benaphore.
if (!locked && error == B_OK) {
error = _AcquireBenaphore(fMutex, timeout);
switch (error) {
case B_OK: {
// Yeah, we made it. Set the special writer fields.
fWriter = thread;
fWriterWriterCount = 1;
fWriterReaderCount = readerCount;
break;
}
case B_TIMED_OUT: {
// clean up
if (fLock.Lock()) {
fWriterCount--;
fLock.Unlock();
} // else: failed to lock the data: we're probably going
// to die.
break;
}
default:
// Probably we're going to die.
break;
}
// Whatever happened, we have to release the queueing benaphore.
_ReleaseBenaphore(fQueue);
}
} else // failed to lock the data
error = B_ERROR;
return error;
}
// _AddReadLockInfo
int32
RWLocker::_AddReadLockInfo(ReadLockInfo* info)
{
int32 index = fReadLockInfos.CountItems();
fReadLockInfos.AddItem(info, index);
return index;
}
// _NewReadLockInfo
//
// Create a new read lock info for the supplied thread and add it to the
// list. Returns the index of the info.
int32
RWLocker::_NewReadLockInfo(thread_id thread, int32 count)
{
ReadLockInfo* info = new ReadLockInfo;
info->reader = thread;
info->count = count;
return _AddReadLockInfo(info);
}
// _DeleteReadLockInfo
void
RWLocker::_DeleteReadLockInfo(int32 index)
{
if (ReadLockInfo* info = (ReadLockInfo*)fReadLockInfos.RemoveItem(index))
delete info;
}
// _ReadLockInfoAt
RWLocker::ReadLockInfo*
RWLocker::_ReadLockInfoAt(int32 index) const
{
return (ReadLockInfo*)fReadLockInfos.ItemAt(index);
}
// _IndexOf
int32
RWLocker::_IndexOf(thread_id thread) const
{
int32 count = fReadLockInfos.CountItems();
for (int32 i = 0; i < count; i++) {
if (_ReadLockInfoAt(i)->reader == thread)
return i;
}
return -1;
}
// _AcquireBenaphore
status_t
RWLocker::_AcquireBenaphore(Benaphore& benaphore, bigtime_t timeout)
{
status_t error = B_OK;
if (atomic_add(&benaphore.counter, 1) > 0) {
error = acquire_sem_etc(benaphore.semaphore, 1, B_ABSOLUTE_TIMEOUT,
timeout);
}
return error;
}
// _ReleaseBenaphore
void
RWLocker::_ReleaseBenaphore(Benaphore& benaphore)
{
if (atomic_add(&benaphore.counter, -1) > 1)
release_sem(benaphore.semaphore);
}
@@ -0,0 +1,137 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* IngoWeinhold <[email protected]>
*/
// This class provides a reader/writer locking mechanism:
// * A writer needs an exclusive lock.
// * For a reader a non-exclusive lock to be shared with other readers is
// sufficient.
// * The ownership of a lock is bound to the thread that requested the lock;
// the same thread has to call Unlock() later.
// * Nested locking is supported: a number of XXXLock() calls needs to be
// bracketed by the same number of XXXUnlock() calls.
// * The lock acquiration strategy is fair: a lock applicant needs to wait
// only for those threads that already own a lock or requested one before
// the current thread. No one can overtake. E.g. if a thread owns a read
// lock, another one is waiting for a write lock, then a third one
// requesting a read lock has to wait until the write locker is done.
// This does not hold for threads that already own a lock (nested locking).
// A read lock owner is immediately granted another read lock and a write
// lock owner another write or a read lock.
// * A write lock owner is allowed to request a read lock and a read lock
// owner a write lock. While the first case is not problematic, the
// second one needs some further explanation: A read lock owner requesting
// a write lock temporarily looses its read lock(s) until the write lock
// is granted. Otherwise two read lock owning threads trying to get
// write locks at the same time would dead lock each other. The only
// problem with this solution is, that the write lock acquiration must
// not fail, because in that case the thread could not be given back
// its read lock(s), since another thread may have been given a write lock
// in the mean time. Fortunately locking can fail only either, if the
// locker has been deleted, or, if a timeout occured. Therefore
// WriteLockWithTimeout() immediatlely returns with a B_WOULD_BLOCK error
// code, if the caller already owns a read lock (but no write lock) and
// another thread already owns or has requested a read or write lock.
// * Calls to read and write locking methods may interleave arbitrarily,
// e.g.: ReadLock(); WriteLock(); ReadUnlock(); WriteUnlock();
//
// Important note: Read/WriteLock() can fail only, if the locker has been
// deleted. However, it is NOT save to invoke any method on a deleted
// locker object.
//
// Implementation details:
// A locker needs three semaphores (a BLocker and two semaphores): one
// to protect the lockers data, one as a reader/writer mutex (to be
// acquired by each writer and the first reader) and one for queueing
// waiting readers and writers. The simplified locking/unlocking
// algorithm is the following:
//
// writer reader
// queue.acquire() queue.acquire()
// mutex.acquire() if (first reader) mutex.acquire()
// queue.release() queue.release()
// ... ...
// mutex.release() if (last reader) mutex.release()
//
// One thread at maximum waits at the mutex, the others at the queueing
// semaphore. Unfortunately features as nested locking and timeouts make
// things more difficult. Therefore readers as well as writers need to check
// whether they already own a lock before acquiring the queueing semaphore.
// The data for the readers are stored in a list of ReadLockInfo structures;
// the writer data are stored in some special fields. /fReaderCount/ and
// /fWriterCount/ contain the total count of unbalanced Read/WriteLock()
// calls, /fWriterReaderCount/ and /fWriterWriterCount/ only from those of
// the current write lock owner (/fWriter/). To be a bit more precise:
// /fWriterReaderCount/ is not contained in /fReaderCount/, but
// /fWriterWriterCount/ is contained in /fWriterCount/. Therefore
// /fReaderCount/ can be considered to be the count of true reader's read
// locks.
#ifndef RW_LOCKER_H
#define RW_LOCKER_H
#include <List.h>
#include <Locker.h>
#include "AutoLocker.h"
class RWLocker {
public:
RWLocker();
RWLocker(const char* name);
virtual ~RWLocker();
bool ReadLock();
status_t ReadLockWithTimeout(bigtime_t timeout);
void ReadUnlock();
bool IsReadLocked() const;
bool WriteLock();
status_t WriteLockWithTimeout(bigtime_t timeout);
void WriteUnlock();
bool IsWriteLocked() const;
private:
struct ReadLockInfo;
struct Benaphore {
sem_id semaphore;
int32 counter;
};
private:
void _Init(const char* name);
status_t _ReadLock(bigtime_t timeout);
status_t _WriteLock(bigtime_t timeout);
int32 _AddReadLockInfo(ReadLockInfo* info);
int32 _NewReadLockInfo(thread_id thread,
int32 count = 1);
void _DeleteReadLockInfo(int32 index);
ReadLockInfo* _ReadLockInfoAt(int32 index) const;
int32 _IndexOf(thread_id thread) const;
static status_t _AcquireBenaphore(Benaphore& benaphore,
bigtime_t timeout);
static void _ReleaseBenaphore(Benaphore& benaphore);
private:
mutable BLocker fLock; // data lock
Benaphore fMutex; // critical code mutex
Benaphore fQueue; // queueing semaphore
int32 fReaderCount; // total count...
int32 fWriterCount; // total count...
BList fReadLockInfos;
thread_id fWriter; // current write lock owner
int32 fWriterWriterCount; // write lock owner count
int32 fWriterReaderCount; // writer read lock owner
// count
};
typedef AutoLocker<RWLocker, AutoLockerReadLocking<RWLocker> > AutoReadLocker;
typedef AutoLocker<RWLocker, AutoLockerWriteLocking<RWLocker> > AutoWriteLocker;
#endif // RW_LOCKER_H
@@ -0,0 +1,155 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "support.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <DataIO.h>
#include <Point.h>
#include <String.h>
// point_line_distance
double
point_line_distance(double x1, double y1,
double x2, double y2,
double x, double y)
{
double dx = x2 - x1;
double dy = y2 - y1;
return ((x - x2) * dy - (y - y2) * dx) / sqrt(dx * dx + dy * dy);
}
// point_line_distance
double
point_line_distance(BPoint point, BPoint pa, BPoint pb)
{
// first figure out if point is between segment start and end points
double a = point_point_distance(point, pb);
double b = point_point_distance(point, pa);
double c = point_point_distance(pa, pb);
float currentDist = min_c(a, b);
if (a > 0.0 && b > 0.0) {
double alpha = acos((b*b + c*c - a*a) / (2*b*c));
double beta = acos((a*a + c*c - b*b) / (2*a*c));
if (alpha <= PI2 && beta <= PI2) {
currentDist = fabs(point_line_distance(pa.x, pa.y, pb.x, pb.y,
point.x, point.y));
}
}
return currentDist;
}
// calc_angle
double
calc_angle(BPoint origin, BPoint from, BPoint to, bool degree)
{
double angle = 0.0;
double d = point_line_distance(from.x, from.y,
origin.x, origin.y,
to.x, to.y);
if (d != 0.0) {
double a = point_point_distance(from, to);
double b = point_point_distance(from, origin);
double c = point_point_distance(to, origin);
if (a > 0.0 && b > 0.0 && c > 0.0) {
angle = acos((b*b + c*c - a*a) / (2.0*b*c));
if (d < 0.0)
angle = -angle;
if (degree)
angle = angle * 180.0 / PI;
}
}
return angle;
}
// write_string
status_t
write_string(BPositionIO* stream, BString& string)
{
if (!stream)
return B_BAD_VALUE;
ssize_t written = stream->Write(string.String(), string.Length());
if (written > B_OK && written < string.Length())
written = B_ERROR;
string.SetTo("");
return written;
}
// append_float
void
append_float(BString& string, float n, int32 maxDigits = 4)
{
int32 rounded = n >= 0.0 ? (int32)fabs(floorf(n)) : (int32)fabs(ceilf(n));
if (n < 0.0) {
string << "-";
n *= -1.0;
}
string << rounded;
if ((float)rounded != n) {
// find out how many digits remain
n = n - rounded;
rounded = (int32)(n * pow(10, maxDigits));
char tmp[maxDigits + 1];
sprintf(tmp, "%0*ld", (int)maxDigits, rounded);
tmp[maxDigits] = 0;
int32 digits = strlen(tmp);
for (int32 i = strlen(tmp) - 1; i >= 0; i--) {
if (tmp[i] == '0')
digits--;
else
break;
}
// write after decimal
if (digits > 0) {
string << ".";
for (int32 i = 0; i < digits; i++) {
string << tmp[i];
}
}
}
}
// gauss
double
gauss(double f)
{
// this aint' a real gauss function
/* if (f >= -1.0 && f <= 1.0) {
if (f < -0.5) {
f = -1.0 - f;
return (2.0 * f*f);
}
if (f < 0.5)
return (1.0 - 2.0 * f*f);
f = 1.0 - f;
return (2.0 * f*f);
}*/
if (f > 0.0) {
if (f < 0.5)
return (1.0 - 2.0 * f*f);
f = 1.0 - f;
return (2.0 * f*f);
}
return 1.0;
}
@@ -0,0 +1,137 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef SUPPORT_H
#define SUPPORT_H
#include <Rect.h>
class BPositionIO;
class BString;
// constrain
inline void
constrain(float& value, float min, float max)
{
if (value < min)
value = min;
if (value > max)
value = max;
}
// constrain_int32_0_255_asm
inline int32
constrain_int32_0_255_asm(int32 value) {
asm("movl $0, %%ecx;
movl $255, %%edx;
cmpl %%ecx, %%eax;
cmovl %%ecx, %%eax;
cmpl %%edx, %%eax;
cmovg %%edx, %%eax"
: "=a" (value)
: "a" (value)
: "%ecx", "%edx" );
return value;
}
inline int32
constrain_int32_0_255_c(int32 value) {
return max_c(0, min_c(255, value));
}
#define constrain_int32_0_255 constrain_int32_0_255_asm
// rect_to_int
inline void
rect_to_int(BRect r,
int32& left, int32& top, int32& right, int32& bottom)
{
left = (int32)floorf(r.left);
top = (int32)floorf(r.top);
right = (int32)ceilf(r.right);
bottom = (int32)ceilf(r.bottom);
}
// point_point_distance
inline float
point_point_distance(BPoint a, BPoint b)
{
float xDiff = b.x - a.x;
float yDiff = b.y - a.y;
return sqrtf(xDiff * xDiff + yDiff * yDiff);
}
// point_line_distance
double
point_line_distance(double x1, double y1,
double x2, double y2,
double x, double y);
// point_line_distance
double
point_line_distance(BPoint point, BPoint a, BPoint b);
// calc_angle
double
calc_angle(BPoint origin, BPoint from, BPoint to, bool degree = true);
/*
template <class T>
T min4(const T a, const T b, const T c, const T d)
{
T e = a < b ? a : b;
T f = c < d ? c : d;
return e < f ? e : f;
}
template <class T>
T max4(const T a, const T b, const T c, const T d)
{
T e = a > b ? a : b;
T f = c > d ? c : d;
return e > f ? e : f;
}
*/
inline float
min4(float a, float b, float c, float d)
{
return min_c(a, min_c(b, min_c(c, d)));
}
inline float
max4(float a, float b, float c, float d)
{
return max_c(a, max_c(b, max_c(c, d)));
}
inline float
min5(float v1, float v2, float v3, float v4, float v5)
{
return min_c(min4(v1, v2, v3, v4), v5);
}
inline float
max5(float v1, float v2, float v3, float v4, float v5)
{
return max_c(max4(v1, v2, v3, v4), v5);
}
inline float
roundf(float v)
{
if (v >= 0.0)
return floorf(v + 0.5);
return ceilf(v - 0.5);
}
status_t write_string(BPositionIO* stream, BString& string);
void append_float(BString& string, float n, int32 maxDigits = 4);
double gauss(double f);
# endif // SUPPORT_H
@@ -0,0 +1,65 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "support_settings.h"
#include <stdio.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <Message.h>
#include <Path.h>
// load_settings
status_t
load_settings(BMessage* message, const char* fileName, const char* folder)
{
status_t ret = B_BAD_VALUE;
if (message) {
BPath path;
if ((ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path)) == B_OK) {
// passing folder is optional
if (folder)
ret = path.Append(folder);
if (ret == B_OK && (ret = path.Append(fileName)) == B_OK) {
BFile file(path.Path(), B_READ_ONLY);
if ((ret = file.InitCheck()) == B_OK) {
ret = message->Unflatten(&file);
file.Unset();
}
}
}
}
return ret;
}
// save_settings
status_t
save_settings(BMessage* message, const char* fileName, const char* folder)
{
status_t ret = B_BAD_VALUE;
if (message) {
BPath path;
if ((ret = find_directory(B_USER_SETTINGS_DIRECTORY, &path)) == B_OK) {
// passing folder is optional
if (folder && (ret = path.Append(folder)) == B_OK)
ret = create_directory(path.Path(), 0777);
if (ret == B_OK && (ret = path.Append(fileName)) == B_OK) {
BFile file(path.Path(),
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
if ((ret = file.InitCheck()) == B_OK) {
ret = message->Flatten(&file);
file.Unset();
}
}
}
}
return ret;
}
@@ -0,0 +1,23 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef SUPPORT_SETTINGS_H
#define SUPPORT_SETTINGS_H
#include <GraphicsDefs.h>
class BMessage;
status_t load_settings(BMessage* message, const char* fileName,
const char* folder = NULL);
status_t save_settings(BMessage* message, const char* fileName,
const char* folder = NULL);
# endif // SUPPORT_SETTINGS_H
@@ -0,0 +1,334 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "support.h"
#include <stdio.h>
#include <string.h>
#include <Bitmap.h>
#include <DataIO.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
#include <Screen.h>
#include <String.h>
#include <Path.h>
#include <View.h>
#include <Space.h>
// stroke_frame
void
stroke_frame(BView* v, BRect r, rgb_color left, rgb_color top,
rgb_color right, rgb_color bottom)
{
if (v && r.IsValid()) {
v->BeginLineArray(4);
v->AddLine(BPoint(r.left, r.bottom),
BPoint(r.left, r.top), left);
v->AddLine(BPoint(r.left + 1.0, r.top),
BPoint(r.right, r.top), top);
v->AddLine(BPoint(r.right, r.top + 1.0),
BPoint(r.right, r.bottom), right);
v->AddLine(BPoint(r.right - 1.0, r.bottom),
BPoint(r.left + 1.0, r.bottom), bottom);
v->EndLineArray();
}
}
// vertical_space
Space*
vertical_space()
{
return new Space(minimax(0.0, 3.0, 10000.0, 3.0, 1.0));
}
// horizontal_space
Space*
horizontal_space()
{
return new Space(minimax(3.0, 0.0, 3.0, 10000.0, 1.0));
}
// store_color_in_message
status_t
store_color_in_message(BMessage* message, rgb_color color)
{
status_t ret = B_BAD_VALUE;
if (message) {
ret = message->AddData("RGBColor", B_RGB_COLOR_TYPE,
(void*)&color, sizeof(rgb_color));
}
return ret;
}
// restore_color_from_message
status_t
restore_color_from_message(const BMessage* message, rgb_color& color, int32 index)
{
status_t ret = B_BAD_VALUE;
if (message) {
const void* colorPointer;
ssize_t size = sizeof(rgb_color);
ret = message->FindData("RGBColor", B_RGB_COLOR_TYPE, index,
&colorPointer, &size);
if (ret >= B_OK)
color = *(const rgb_color*)colorPointer;
}
return ret;
}
// make_color_drop_message
BMessage
make_color_drop_message(rgb_color color, BBitmap* bitmap)
{
// prepare message
BMessage message(B_PASTE);
char hexstr[7];
sprintf(hexstr, "#%.2X%.2X%.2X", color.red, color.green, color.blue);
message.AddData("text/plain", B_MIME_TYPE, &hexstr, sizeof(hexstr));
message.AddData("RGBColor", B_RGB_COLOR_TYPE, &color, sizeof(color));
// prepare bitmap
if (bitmap && bitmap->IsValid()
&& (bitmap->ColorSpace() == B_RGB32
|| bitmap->ColorSpace() == B_RGBA32)) {
uint8* bits = (uint8*)bitmap->Bits();
uint32 bpr = bitmap->BytesPerRow();
uint32 width = bitmap->Bounds().IntegerWidth() + 1;
uint32 height = bitmap->Bounds().IntegerHeight() + 1;
for (uint32 y = 0; y < height; y++) {
uint8* bitsHandle = bits;
for (uint32 x = 0; x < width; x++) {
if (x == 0 || y == 0 ) {
// top or left border
bitsHandle[0] = (uint8)min_c(255, color.blue * 1.2 + 40);
bitsHandle[1] = (uint8)min_c(255, color.green * 1.2 + 40);
bitsHandle[2] = (uint8)min_c(255, color.red * 1.2 + 40);
bitsHandle[3] = 180;
} else if ((x == width - 2 || y == height - 2)
&& !(x == width - 1 || y == height - 1)) {
// bottom or right border
bitsHandle[0] = (uint8)(color.blue * 0.8);
bitsHandle[1] = (uint8)(color.green * 0.8);
bitsHandle[2] = (uint8)(color.red * 0.8);
bitsHandle[3] = 180;
} else if (x == width - 1 || y == height - 1) {
// shadow
bitsHandle[0] = 0;
bitsHandle[1] = 0;
bitsHandle[2] = 0;
bitsHandle[3] = 100;
} else {
// color
bitsHandle[0] = color.blue;
bitsHandle[1] = color.green;
bitsHandle[2] = color.red;
bitsHandle[3] = 180;
}
if ((x == 0 && y == height - 1) || (y == 0 && x == width - 1)) {
// spare pixels of shadow
bitsHandle[0] = 0;
bitsHandle[1] = 0;
bitsHandle[2] = 0;
bitsHandle[3] = 50;
}
bitsHandle += 4;
}
bits += bpr;
}
}
return message;
}
// make_sure_frame_is_on_screen
void
make_sure_frame_is_on_screen(BRect& frame, BWindow* window)
{
BScreen screen(window);
if (frame.IsValid() && screen.IsValid()) {
if (!screen.Frame().Contains(frame)) {
// make sure frame fits in the screen
if (frame.Width() > screen.Frame().Width())
frame.right -= frame.Width() - screen.Frame().Width() + 10.0;
if (frame.Height() > screen.Frame().Height())
frame.bottom -= frame.Height() - screen.Frame().Height() + 30.0;
// frame is now at the most the size of the screen
if (frame.right > screen.Frame().right)
frame.OffsetBy(-(frame.right - screen.Frame().right), 0.0);
if (frame.bottom > screen.Frame().bottom)
frame.OffsetBy(0.0, -(frame.bottom - screen.Frame().bottom));
if (frame.left < screen.Frame().left)
frame.OffsetBy((screen.Frame().left - frame.left), 0.0);
if (frame.top < screen.Frame().top)
frame.OffsetBy(0.0, (screen.Frame().top - frame.top));
}
}
}
// print_modifiers
void
print_modifiers()
{
uint32 mods = modifiers();
if (mods & B_SHIFT_KEY)
printf("B_SHIFT_KEY\n");
if (mods & B_COMMAND_KEY)
printf("B_COMMAND_KEY\n");
if (mods & B_CONTROL_KEY)
printf("B_CONTROL_KEY\n");
if (mods & B_CAPS_LOCK)
printf("B_CAPS_LOCK\n");
if (mods & B_SCROLL_LOCK)
printf("B_SCROLL_LOCK\n");
if (mods & B_NUM_LOCK)
printf("B_NUM_LOCK\n");
if (mods & B_OPTION_KEY)
printf("B_OPTION_KEY\n");
if (mods & B_MENU_KEY)
printf("B_MENU_KEY\n");
if (mods & B_LEFT_SHIFT_KEY)
printf("B_LEFT_SHIFT_KEY\n");
if (mods & B_RIGHT_SHIFT_KEY)
printf("B_RIGHT_SHIFT_KEY\n");
if (mods & B_LEFT_COMMAND_KEY)
printf("B_LEFT_COMMAND_KEY\n");
if (mods & B_RIGHT_COMMAND_KEY)
printf("B_RIGHT_COMMAND_KEY\n");
if (mods & B_LEFT_CONTROL_KEY)
printf("B_LEFT_CONTROL_KEY\n");
if (mods & B_RIGHT_CONTROL_KEY)
printf("B_RIGHT_CONTROL_KEY\n");
if (mods & B_LEFT_OPTION_KEY)
printf("B_LEFT_OPTION_KEY\n");
if (mods & B_RIGHT_OPTION_KEY)
printf("B_RIGHT_OPTION_KEY\n");
}
/*
// convert_cap_mode
agg::line_cap_e
convert_cap_mode(uint32 mode)
{
agg::line_cap_e aggMode = agg::butt_cap;
switch (mode) {
case CAP_MODE_BUTT:
aggMode = agg::butt_cap;
break;
case CAP_MODE_SQUARE:
aggMode = agg::square_cap;
break;
case CAP_MODE_ROUND:
aggMode = agg::round_cap;
break;
}
return aggMode;
}
// convert_cap_mode
agg::line_join_e
convert_join_mode(uint32 mode)
{
agg::line_join_e aggMode = agg::miter_join;
switch (mode) {
case JOIN_MODE_MITER:
aggMode = agg::miter_join;
break;
case JOIN_MODE_ROUND:
aggMode = agg::round_join;
break;
case JOIN_MODE_BEVEL:
aggMode = agg::bevel_join;
break;
}
return aggMode;
}
*/
// string_for_color_space
const char*
string_for_color_space(color_space format)
{
const char* name = "<unkown format>";
switch (format) {
case B_RGB32:
name = "B_RGB32";
break;
case B_RGBA32:
name = "B_RGBA32";
break;
case B_RGB32_BIG:
name = "B_RGB32_BIG";
break;
case B_RGBA32_BIG:
name = "B_RGBA32_BIG";
break;
case B_RGB24:
name = "B_RGB24";
break;
case B_RGB24_BIG:
name = "B_RGB24_BIG";
break;
case B_CMAP8:
name = "B_CMAP8";
break;
case B_GRAY8:
name = "B_GRAY8";
break;
case B_GRAY1:
name = "B_GRAY1";
break;
// YCbCr
case B_YCbCr422:
name = "B_YCbCr422";
break;
case B_YCbCr411:
name = "B_YCbCr411";
break;
case B_YCbCr444:
name = "B_YCbCr444";
break;
case B_YCbCr420:
name = "B_YCbCr420";
break;
// YUV
case B_YUV422:
name = "B_YUV422";
break;
case B_YUV411:
name = "B_YUV411";
break;
case B_YUV444:
name = "B_YUV444";
break;
case B_YUV420:
name = "B_YUV420";
break;
case B_YUV9:
name = "B_YUV9";
break;
case B_YUV12:
name = "B_YUV12";
break;
default:
break;
}
return name;
}
// print_color_space
void
print_color_space(color_space format)
{
printf("%s\n", string_for_color_space(format));
}
@@ -0,0 +1,73 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#ifndef SUPPORT_UI_H
#define SUPPORT_UI_H
#include <GraphicsDefs.h>
#include <Rect.h>
#include <agg_math_stroke.h>
class BBitmap;
class BDataIO;
class BMessage;
class BPositionIO;
class BString;
class BView;
class BWindow;
class Space;
// looper of view must be locked!
void stroke_frame(BView* view, BRect frame,
rgb_color left, rgb_color top,
rgb_color right, rgb_color bottom);
Space* vertical_space();
Space* horizontal_space();
status_t store_color_in_message(BMessage* message, rgb_color color);
status_t restore_color_from_message(const BMessage* message, rgb_color& color, int32 index = 0);
BMessage make_color_drop_message(rgb_color color, BBitmap* bitmap);
void make_sure_frame_is_on_screen(BRect& frame, BWindow* window);
void print_modifiers();
//agg::line_cap_e convert_cap_mode(uint32 mode);
//agg::line_join_e convert_join_mode(uint32 mode);
const char* string_for_color_space(color_space format);
void print_color_space(color_space format);
// Those are already defined in newer versions of BeOS
#if !defined(B_BEOS_VERSION_DANO) && !defined(__HAIKU__)
// rgb_color == rgb_color
static inline bool
operator==(const rgb_color& a, const rgb_color& b)
{
return a.red == b.red
&& a.green == b.green
&& a.blue == b.blue
&& a.alpha == b.alpha;
}
// rgb_color != rgb_color
static inline bool
operator!=(const rgb_color& a, const rgb_color& b)
{
return !(a == b);
}
#endif // B_BEOS_VERSION <= ...
#endif // SUPPORT_UI_H
+20
View File
@@ -0,0 +1,20 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <[email protected]>
*/
#include "IconEditorApp.h"
int
main(int argc, char** argv)
{
IconEditorApp* app = new IconEditorApp();
app->Run();
delete app;
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,158 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef PATH_MANIPULATOR_H
#define PATH_MANIPULATOR_H
#include "Manipulator.h"
class AddPointCommand;
class ChangePointCommand;
class UndoStack;
class InsertPointCommand;
class Selection;
class StateView;
class VectorPath;
//class PathSelection {
// public:
// PathSelection();
// virtual ~PathSelection();
//
// virtual PathSelection* Clone() const;
// virtual bool SetTo(const PathSelection* other);
//
// virtual Command* Delete();
//};
class PathManipulator : public Manipulator {
public:
PathManipulator(VectorPath* path);
virtual ~PathManipulator();
// Manipulator interface
virtual void Draw(BView* into, BRect updateRect);
virtual bool MouseDown(BPoint where);
virtual void MouseMoved(BPoint where);
virtual Command* MouseUp();
virtual bool MouseOver(BPoint where);
virtual bool DoubleClicked(BPoint where);
virtual BRect Bounds();
virtual BRect TrackingBounds(BView* withinView);
virtual bool MessageReceived(BMessage* message,
Command** _command);
virtual void ModifiersChanged(uint32 modifiers);
virtual bool HandleKeyDown(uint32 key, uint32 modifiers,
Command** _command);
virtual bool HandleKeyUp(uint32 key, uint32 modifiers,
Command** _command);
virtual void UpdateCursor();
virtual void AttachedToView(BView* view);
virtual void DetachedFromView(BView* view);
// Observer interface (Manipulator)
virtual void ObjectChanged(const Observable* object);
// PathManipulator
uint32 ControlFlags() const;
// PathSelection* Selection() const;
//
// path manipulation
void ReversePath();
private:
friend class PathCommand;
friend class PointSelection;
friend class EnterTransformPointsCommand;
friend class ExitTransformPointsCommand;
friend class TransformPointsCommand;
// friend class NewPathCommand;
friend class NudgePointsCommand;
// friend class RemovePathCommand;
friend class ReversePathCommand;
// friend class SelectPathCommand;
void _SetMode(uint32 mode);
// BEGIN functions that need to be undoable
void _AddPoint(BPoint where);
void _InsertPoint(BPoint where, int32 index);
void _SetInOutConnected(int32 index, bool connected);
void _SetSharp(int32 index);
void _RemoveSelection();
void _RemovePoint(int32 index);
void _RemovePointIn(int32 index);
void _RemovePointOut(int32 index);
Command* _Delete();
void _Select(BRect canvasRect);
void _Select(int32 index, bool extend = false);
void _Select(const int32* indices, int32 count, bool extend = false);
void _Deselect(int32 index);
void _ShiftSelection(int32 startIndex, int32 direction);
bool _IsSelected(int32 index) const;
// END functions that need to be undoable
void _InvalidateCanvas(BRect rect) const;
void _InvalidateHighlightPoints(int32 newIndex, uint32 newMode);
void _UpdateSelection() const;
BRect _ControlPointRect() const;
BRect _ControlPointRect(int32 index, uint32 mode) const;
void _GetChangableAreas(BRect* pathArea,
BRect* controlPointArea) const;
void _SetModeForMousePos(BPoint canvasWhere);
void _Nudge(BPoint direction);
void _FinishNudging();
StateView* fCanvasView;
bool fCommandDown;
bool fOptionDown;
bool fShiftDown;
bool fAltDown;
bool fClickToClose;
uint32 fMode;
uint32 fFallBackMode;
bool fMouseDown;
BPoint fTrackingStart;
BPoint fLastCanvasPos;
VectorPath* fPath;
int32 fCurrentPathPoint;
BRect fPreviousBounds;
ChangePointCommand* fChangePointCommand;
InsertPointCommand* fInsertPointCommand;
AddPointCommand* fAddPointCommand;
Selection* fSelection;
Selection* fOldSelection;
// stuff needed for nudging
BPoint fNudgeOffset;
bigtime_t fLastNudgeTime;
// NudgePointsCommand* fNudgeCommand;
};
#endif // SHAPE_STATE_H
+855
View File
@@ -0,0 +1,855 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "VectorPath.h"
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <Bitmap.h>
#include <Message.h>
#include <TypeConstants.h>
#include <agg_basics.h>
#include <agg_bounding_rect.h>
#include <agg_conv_curve.h>
#include <agg_curves.h>
#include <agg_math.h>
#include "support.h"
#define obj_new(type, n) ((type *)malloc ((n) * sizeof(type)))
#define obj_renew(p, type, n) ((type *)realloc (p, (n) * sizeof(type)))
#define obj_free free
#define ALLOC_CHUNKS 20
// get_path_storage
bool
get_path_storage(agg::path_storage& path,
const control_point* points, int32 count, bool closed)
{
if (count > 1) {
path.move_to(points[0].point.x,
points[0].point.y);
for (int32 i = 1; i < count; i++) {
path.curve4(points[i - 1].point_out.x,
points[i - 1].point_out.y,
points[i].point_in.x,
points[i].point_in.y,
points[i].point.x,
points[i].point.y);
}
if (closed) {
// curve from last to first control point
path.curve4(points[count - 1].point_out.x,
points[count - 1].point_out.y,
points[0].point_in.x,
points[0].point_in.y,
points[0].point.x,
points[0].point.y);
path.close_polygon();
} else {
// straight line from last to first control point
path.line_to(points[0].point.x,
points[0].point.y);
path.close_polygon();
}
return true;
}
return false;
}
// constructor
VectorPath::VectorPath()
: BArchivable(),
fPath(NULL),
fClosed(false),
fPointCount(0),
fAllocCount(0),
fCachedBounds(0.0, 0.0, -1.0, -1.0)
{
}
// constructor
VectorPath::VectorPath(const VectorPath& from)
: BArchivable(),
Observable(),
fPath(NULL),
fPointCount(0),
fAllocCount(0),
fCachedBounds(0.0, 0.0, -1.0, -1.0)
{
*this = from;
}
// constructor
VectorPath::VectorPath(const BMessage* archive)
: BArchivable(),
Observable(),
fPath(NULL),
fClosed(false),
fPointCount(0),
fAllocCount(0),
fCachedBounds(0.0, 0.0, -1.0, -1.0)
{
if (archive) {
type_code typeFound;
int32 countFound;
if (archive->GetInfo("point", &typeFound, &countFound) >= B_OK
&& typeFound == B_POINT_TYPE && _SetPointCount(countFound)) {
memset(fPath, 0, fAllocCount * sizeof(control_point));
BPoint point;
BPoint pointIn;
BPoint pointOut;
bool connected;
for (int32 i = 0; i < fPointCount
&& archive->FindPoint("point", i, &point) >= B_OK
&& archive->FindPoint("point in", i, &pointIn) >= B_OK
&& archive->FindPoint("point out", i, &pointOut) >= B_OK
&& archive->FindBool("connected", i, &connected) >= B_OK; i++) {
fPath[i].point = point;
fPath[i].point_in = pointIn;
fPath[i].point_out = pointOut;
fPath[i].connected = connected;
}
}
if (archive->FindBool("path closed", &fClosed) < B_OK) {
fClosed = false;
}
}
}
// destructor
VectorPath::~VectorPath()
{
if (fPath)
obj_free(fPath);
}
// operator=
VectorPath&
VectorPath::operator=(const VectorPath& from)
{
_SetPointCount(from.fPointCount);
fClosed = from.fClosed;
if (fPath) {
memcpy(fPath, from.fPath, fPointCount * sizeof(control_point));
fCachedBounds = from.fCachedBounds;
} else {
fprintf(stderr, "VectorPath() -> allocation failed in operator=!\n");
fAllocCount = 0;
fPointCount = 0;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
}
return *this;
}
// MakeEmpty
void
VectorPath::MakeEmpty()
{
_SetPointCount(0);
}
// Archive
status_t
VectorPath::Archive(BMessage* into, bool deep) const
{
status_t ret = BArchivable::Archive(into, deep);
if (ret >= B_OK) {
if (fPointCount > 0) {
// improve BMessage efficency by preallocating storage for all points
// with the first call
ret = into->AddData("point", B_POINT_TYPE, &fPath[0].point,
sizeof(BPoint), true, fPointCount);
if (ret >= B_OK)
ret = into->AddData("point in", B_POINT_TYPE, &fPath[0].point_in,
sizeof(BPoint), true, fPointCount);
if (ret >= B_OK)
ret = into->AddData("point out", B_POINT_TYPE, &fPath[0].point_out,
sizeof(BPoint), true, fPointCount);
if (ret >= B_OK)
ret = into->AddData("connected", B_BOOL_TYPE, &fPath[0].connected,
sizeof(bool), true, fPointCount);
// add the rest of the points
for (int32 i = 1; i < fPointCount && ret >= B_OK; i++) {
ret = into->AddData("point", B_POINT_TYPE, &fPath[i].point, sizeof(BPoint));
if (ret >= B_OK)
ret = into->AddData("point in", B_POINT_TYPE, &fPath[i].point_in, sizeof(BPoint));
if (ret >= B_OK)
ret = into->AddData("point out", B_POINT_TYPE, &fPath[i].point_out, sizeof(BPoint));
if (ret >= B_OK)
ret = into->AddData("connected", B_BOOL_TYPE, &fPath[i].connected, sizeof(bool));
}
}
if (ret >= B_OK) {
ret = into->AddBool("path closed", fClosed);
} else {
fprintf(stderr, "failed adding points!\n");
}
if (ret < B_OK) {
fprintf(stderr, "failed adding closed!\n");
}
// finish off
if (ret < B_OK) {
ret = into->AddString("class", "VectorPath");
}
}
return ret;
}
// AddPoint
bool
VectorPath::AddPoint(BPoint point)
{
int32 index = fPointCount;
if (_SetPointCount(fPointCount + 1)) {
_SetPoint(index, point);
Notify();
return true;
}
return false;
}
// AddPoint
bool
VectorPath::AddPoint(BPoint point, int32 index)
{
if (index < 0)
index = 0;
if (index > fPointCount)
index = fPointCount;
if (_SetPointCount(fPointCount + 1)) {
// handle insert
if (index < fPointCount - 1) {
for (int32 i = fPointCount; i > index; i--) {
fPath[i].point = fPath[i - 1].point;
fPath[i].point_in = fPath[i - 1].point_in;
fPath[i].point_out = fPath[i - 1].point_out;
fPath[i].connected = fPath[i - 1].connected;
}
}
_SetPoint(index, point);
Notify();
return true;
}
return false;
}
// RemovePoint
bool
VectorPath::RemovePoint(int32 index)
{
if (index >= 0 && index < fPointCount) {
if (index < fPointCount - 1) {
// move points
for (int32 i = index; i < fPointCount - 1; i++) {
fPath[i].point = fPath[i + 1].point;
fPath[i].point_in = fPath[i + 1].point_in;
fPath[i].point_out = fPath[i + 1].point_out;
fPath[i].connected = fPath[i + 1].connected;
}
}
fPointCount -= 1;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
Notify();
return true;
}
return false;
}
// SetPoint
bool
VectorPath::SetPoint(int32 index, BPoint point)
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
BPoint offset = point - fPath[index].point;
fPath[index].point = point;
fPath[index].point_in += offset;
fPath[index].point_out += offset;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
Notify();
return true;
}
return false;
}
// SetPoint
bool
VectorPath::SetPoint(int32 index, BPoint point,
BPoint pointIn, BPoint pointOut,
bool connected)
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
fPath[index].point = point;
fPath[index].point_in = pointIn;
fPath[index].point_out = pointOut;
fPath[index].connected = connected;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
Notify();
return true;
}
return false;
}
// SetPointIn
bool
VectorPath::SetPointIn(int32 i, BPoint point)
{
if (i == fPointCount)
i = 0;
if (i >= 0 && i < fPointCount) {
// first, set the "in" point
fPath[i].point_in = point;
// now see what to do about the "out" point
if (fPath[i].connected) {
// keep all three points in one line
BPoint v = fPath[i].point - fPath[i].point_in;
float distIn = sqrtf(v.x * v.x + v.y * v.y);
if (distIn > 0.0) {
float distOut = point_point_distance(fPath[i].point, fPath[i].point_out);
float scale = (distIn + distOut) / distIn;
v.x *= scale;
v.y *= scale;
fPath[i].point_out = fPath[i].point_in + v;
}
}
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
Notify();
return true;
}
return false;
}
// SetPointOut
bool
VectorPath::SetPointOut(int32 i, BPoint point, bool mirrorDist)
{
if (i == fPointCount)
i = 0;
if (i >= 0 && i < fPointCount) {
// first, set the "out" point
fPath[i].point_out = point;
// now see what to do about the "out" point
if (mirrorDist) {
// mirror "in" point around main control point
BPoint v = fPath[i].point - fPath[i].point_out;
fPath[i].point_in = fPath[i].point + v;
} else if (fPath[i].connected) {
// keep all three points in one line
BPoint v = fPath[i].point - fPath[i].point_out;
float distOut = sqrtf(v.x * v.x + v.y * v.y);
if (distOut > 0.0) {
float distIn = point_point_distance(fPath[i].point, fPath[i].point_in);
float scale = (distIn + distOut) / distOut;
v.x *= scale;
v.y *= scale;
fPath[i].point_in = fPath[i].point_out + v;
}
}
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
Notify();
return true;
}
return false;
}
// SetInOutConnected
bool
VectorPath::SetInOutConnected(int32 index, bool connected)
{
if (index >= 0 && index < fPointCount) {
fPath[index].connected = connected;
Notify();
return true;
}
return false;
}
// GetPointAt
bool
VectorPath::GetPointAt(int32 index, BPoint& point) const
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
point = fPath[index].point;
return true;
}
return false;
}
// GetPointInAt
bool
VectorPath::GetPointInAt(int32 index, BPoint& point) const
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
point = fPath[index].point_in;
return true;
}
return false;
}
// GetPointOutAt
bool
VectorPath::GetPointOutAt(int32 index, BPoint& point) const
{
if (index == fPointCount)
index = 0;
if (index >= 0 && index < fPointCount) {
point = fPath[index].point_out;
return true;
}
return false;
}
// GetPointsAt
bool
VectorPath::GetPointsAt(int32 index, BPoint& point,
BPoint& pointIn, BPoint& pointOut, bool* connected) const
{
if (index >= 0 && index < fPointCount) {
point = fPath[index].point;
pointIn = fPath[index].point_in;
pointOut = fPath[index].point_out;
if (connected)
*connected = fPath[index].connected;
return true;
}
return false;
}
// CountPoints
int32
VectorPath::CountPoints() const
{
return fPointCount;
}
// distance_to_curve
static float
distance_to_curve(const BPoint& p, const BPoint& a, const BPoint& aOut, const BPoint& bIn, const BPoint& b)
{
agg::curve4 curve(a.x, a.y, aOut.x, aOut.y,
bIn.x, bIn.y, b.x, b.y);
float segDist = FLT_MAX;
double x1, y1, x2, y2;
unsigned cmd = curve.vertex(&x1, &y1);
while (!agg::is_stop(cmd)) {
cmd = curve.vertex(&x2, &y2);
// first figure out if point is between segment start and end points
double a = agg::calc_distance(p.x, p.y, x2, y2);
double b = agg::calc_distance(p.x, p.y, x1, y1);
float currentDist = min_c(a, b);
if (a > 0.0 && b > 0.0) {
double c = agg::calc_distance(x1, y1, x2, y2);
double alpha = acos((b*b + c*c - a*a) / (2*b*c));
double beta = acos((a*a + c*c - b*b) / (2*a*c));
if (alpha <= PI2 && beta <= PI2) {
currentDist = fabs(point_line_distance(x1, y1, x2, y2, p.x, p.y));
}
}
if (currentDist < segDist) {
segDist = currentDist;
}
x1 = x2;
y1 = y2;
}
return segDist;
}
// GetDistance
bool
VectorPath::GetDistance(BPoint p, float* distance, int32* index) const
{
if (fPointCount > 1) {
// generate a curve for each segment of the path
// then iterate over the segments of the curve measuring the distance
*distance = FLT_MAX;
for (int32 i = 0; i < fPointCount - 1; i++) {
float segDist = distance_to_curve(p,
fPath[i].point,
fPath[i].point_out,
fPath[i + 1].point_in,
fPath[i + 1].point);
if (segDist < *distance) {
*distance = segDist;
*index = i + 1;
}
}
if (fClosed) {
float segDist = distance_to_curve(p,
fPath[fPointCount - 1].point,
fPath[fPointCount - 1].point_out,
fPath[0].point_in,
fPath[0].point);
if (segDist < *distance) {
*distance = segDist;
*index = fPointCount;
}
}
return true;
}
return false;
}
// FindBezierScale
bool
VectorPath::FindBezierScale(int32 index, BPoint point, double* scale) const
{
if (index >= 0 && index < fPointCount && scale) {
int maxStep = 1000;
double t = 0.0;
double dt = 1.0 / maxStep;
*scale = 0.0;
double min = FLT_MAX;
BPoint curvePoint;
for (int step = 1; step < maxStep; step++) {
t += dt;
GetPoint(index, t, curvePoint);
double d = point_point_distance(curvePoint, point);
if (d < min) {
min = d;
*scale = t;
}
}
return true;
}
return false;
}
// GetPoint
bool
VectorPath::GetPoint(int32 index, double t, BPoint& point) const
{
if (index >= 0 && index < fPointCount) {
double t1 = (1 - t) * (1 - t) * (1 - t);
double t2 = (1 - t) * (1 - t) * t * 3;
double t3 = (1 - t) * t * t * 3;
double t4 = t * t * t;
if (index < fPointCount - 1) {
point.x = fPath[index].point.x * t1 +
fPath[index].point_out.x * t2 +
fPath[index + 1].point_in.x * t3 +
fPath[index + 1].point.x * t4;
point.y = fPath[index].point.y * t1 +
fPath[index].point_out.y * t2 +
fPath[index + 1].point_in.y * t3 +
fPath[index + 1].point.y * t4;
} else if (fClosed) {
point.x = fPath[fPointCount - 1].point.x * t1 +
fPath[fPointCount - 1].point_out.x * t2 +
fPath[0].point_in.x * t3 +
fPath[0].point.x * t4;
point.y = fPath[fPointCount - 1].point.y * t1 +
fPath[fPointCount - 1].point_out.y * t2 +
fPath[0].point_in.y * t3 +
fPath[0].point.y * t4;
}
return true;
}
return false;
}
// SetClosed
void
VectorPath::SetClosed(bool closed)
{
if (fClosed != closed) {
fClosed = closed;
Notify();
}
}
// Bounds
BRect
VectorPath::Bounds() const
{
// the bounds of the actual curves, not the control points!
if (!fCachedBounds.IsValid())
fCachedBounds = _Bounds();
return fCachedBounds;
}
// Bounds
BRect
VectorPath::_Bounds() const
{
agg::path_storage path;
BRect b;
if (get_path_storage(path, fPath, fPointCount, fClosed)) {
agg::conv_curve<agg::path_storage> curve(path);
uint32 pathID[1];
pathID[0] = 0;
double left, top, right, bottom;
agg::bounding_rect(curve, pathID, 0, 1, &left, &top, &right, &bottom);
b.Set(left, top, right, bottom);
} else if (fPointCount == 1) {
b.Set(fPath[0].point.x, fPath[0].point.y, fPath[0].point.x, fPath[0].point.y);
} else {
b.Set(0.0, 0.0, -1.0, -1.0);
}
return b;
}
// ControlPointBounds
BRect
VectorPath::ControlPointBounds() const
{
if (fPointCount > 0) {
BRect r(fPath[0].point, fPath[0].point);
for (int32 i = 0; i < fPointCount; i++) {
// include point
r.left = min_c(r.left, fPath[i].point.x);
r.top = min_c(r.top, fPath[i].point.y);
r.right = max_c(r.right, fPath[i].point.x);
r.bottom = max_c(r.bottom, fPath[i].point.y);
// include "in" point
r.left = min_c(r.left, fPath[i].point_in.x);
r.top = min_c(r.top, fPath[i].point_in.y);
r.right = max_c(r.right, fPath[i].point_in.x);
r.bottom = max_c(r.bottom, fPath[i].point_in.y);
// include "out" point
r.left = min_c(r.left, fPath[i].point_out.x);
r.top = min_c(r.top, fPath[i].point_out.y);
r.right = max_c(r.right, fPath[i].point_out.x);
r.bottom = max_c(r.bottom, fPath[i].point_out.y);
}
return r;
}
return BRect(0.0, 0.0, -1.0, -1.0);
}
// Iterate
void
VectorPath::Iterate(Iterator* iterator, float smoothScale) const
{
if (fPointCount > 1) {
// generate a curve for each segment of the path
// then iterate over the segments of the curve
// agg::curve4_inc curve;
agg::curve4 curve;
for (int32 i = 0; i < fPointCount - 1; i++) {
iterator->MoveTo(fPath[i].point);
curve.init(fPath[i].point.x, fPath[i].point.y,
fPath[i].point_out.x, fPath[i].point_out.y,
fPath[i + 1].point_in.x, fPath[i + 1].point_in.y,
fPath[i + 1].point.x, fPath[i + 1].point.y);
double x, y;
unsigned cmd = curve.vertex(&x, &y);
while (!agg::is_stop(cmd)) {
BPoint p(x, y);
iterator->LineTo(p);
cmd = curve.vertex(&x, &y);
}
}
if (fClosed) {
iterator->MoveTo(fPath[fPointCount - 1].point);
curve.init(fPath[fPointCount - 1].point.x, fPath[fPointCount - 1].point.y,
fPath[fPointCount - 1].point_out.x, fPath[fPointCount - 1].point_out.y,
fPath[0].point_in.x, fPath[0].point_in.y,
fPath[0].point.x, fPath[0].point.y);
double x, y;
unsigned cmd = curve.vertex(&x, &y);
while (!agg::is_stop(cmd)) {
BPoint p(x, y);
iterator->LineTo(p);
cmd = curve.vertex(&x, &y);
}
}
}
}
// CleanUp
void
VectorPath::CleanUp()
{
if (fPointCount == 0)
return;
bool notify = false;
// remove last point if it is coincident with the first
if (fClosed && fPointCount >= 1) {
if (fPath[0].point == fPath[fPointCount - 1].point) {
fPath[0].point_in = fPath[fPointCount - 1].point_in;
_SetPointCount(fPointCount - 1);
notify = true;
}
}
for (int32 i = 0; i < fPointCount; i++) {
// check for unnecessary, duplicate points
if (i > 0) {
if (fPath[i - 1].point == fPath[i].point &&
fPath[i - 1].point == fPath[i - 1].point_out &&
fPath[i].point == fPath[i].point_in) {
// the previous point can be removed
BPoint in = fPath[i - 1].point_in;
if (RemovePoint(i - 1)) {
i--;
fPath[i].point_in = in;
notify = true;
}
}
}
// re-establish connections of in-out control points if
// they line up with the main control point
if (fPath[i].point_in == fPath[i].point_out ||
fPath[i].point == fPath[i].point_out ||
fPath[i].point == fPath[i].point_in ||
(fabs(point_line_distance(fPath[i].point_in.x, fPath[i].point_in.y,
fPath[i].point.x, fPath[i].point.y,
fPath[i].point_out.x, fPath[i].point_out.y)) < 0.01 &&
fabs(point_line_distance(fPath[i].point_out.x, fPath[i].point_out.y,
fPath[i].point.x, fPath[i].point.y,
fPath[i].point_in.x, fPath[i].point_in.y)) < 0.01)) {
fPath[i].connected = true;
notify = true;
}
}
if (notify)
Notify();
}
// Reverse
void
VectorPath::Reverse()
{
VectorPath temp(*this);
int32 index = 0;
for (int32 i = fPointCount - 1; i >= 0; i--) {
temp.SetPoint(index, fPath[i].point,
fPath[i].point_out,
fPath[i].point_in,
fPath[i].connected);
index++;
}
*this = temp;
Notify();
}
// PrintToStream
void
VectorPath::PrintToStream() const
{
for (int32 i = 0; i < fPointCount; i++) {
printf("point %ld: (%f, %f) -> (%f, %f) -> (%f, %f) (%d)\n", i,
fPath[i].point_in.x, fPath[i].point_in.y,
fPath[i].point.x, fPath[i].point.y,
fPath[i].point_out.x, fPath[i].point_out.y,
fPath[i].connected);
}
}
// GetAGGPathStorage
bool
VectorPath::GetAGGPathStorage(agg::path_storage& path) const
{
return get_path_storage(path, fPath, fPointCount, fClosed);
}
// _SetPoint
void
VectorPath::_SetPoint(int32 index, BPoint point)
{
fPath[index].point = point;
fPath[index].point_in = point;
fPath[index].point_out = point;
fPath[index].connected = true;
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
}
// _SetPointCount
bool
VectorPath::_SetPointCount(int32 count)
{
// handle reallocation if we run out of room
if (count >= fAllocCount) {
fAllocCount = ((count) / ALLOC_CHUNKS + 1) * ALLOC_CHUNKS;
if (fPath) {
fPath = obj_renew(fPath, control_point, fAllocCount);
} else {
fPath = obj_new(control_point, fAllocCount);
}
memset(fPath + fPointCount, 0, (fAllocCount - fPointCount) * sizeof(control_point));
}
// update point count
if (fPath) {
fPointCount = count;
} else {
// reallocation might have failed
fPointCount = 0;
fAllocCount = 0;
fprintf(stderr, "VectorPath::_SetPointCount(%ld) - allocation failed!\n", count);
}
fCachedBounds.Set(0.0, 0.0, -1.0, -1.0);
return fPath != NULL;
}
+138
View File
@@ -0,0 +1,138 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef VECTOR_PATH_H
#define VECTOR_PATH_H
#include <Archivable.h>
#include <Rect.h>
#include <agg_path_storage.h>
#include "Observable.h"
class BBitmap;
class BMessage;
class BView;
struct control_point {
BPoint point; // actual point on path
BPoint point_in; // control point for incomming curve
BPoint point_out; // control point for outgoing curve
bool connected; // if all 3 points should be on one line
};
class VectorPath : public BArchivable,
public Observable {
public:
class Iterator {
public:
Iterator() {}
virtual ~Iterator() {}
virtual void MoveTo(BPoint point) = 0;
virtual void LineTo(BPoint point) = 0;
};
VectorPath();
VectorPath(const VectorPath& from);
VectorPath(const BMessage* archive);
virtual ~VectorPath();
VectorPath& operator=(const VectorPath& from);
// bool operator==(const VectorPath& frrom) const;
void MakeEmpty();
// the BArchivable protocoll
status_t Archive(BMessage* into, bool deep = true) const;
bool AddPoint(BPoint point);
bool AddPoint(BPoint point, int32 index);
bool RemovePoint(int32 index);
// modify existing points position
bool SetPoint(int32 index, BPoint point);
bool SetPoint(int32 index, BPoint point,
BPoint pointIn,
BPoint pointOut,
bool connected);
bool SetPointIn(int32 index, BPoint point);
bool SetPointOut(int32 index, BPoint point,
bool mirrorDist = false);
bool SetInOutConnected(int32 index, bool connected);
// query existing points position
bool GetPointAt(int32 index, BPoint& point) const;
bool GetPointInAt(int32 index, BPoint& point) const;
bool GetPointOutAt(int32 index, BPoint& point) const;
bool GetPointsAt(int32 index,
BPoint& point,
BPoint& pointIn,
BPoint& pointOut,
bool* connected = NULL) const;
int32 CountPoints() const;
// iterates over curve segments and returns
// the distance and index of the point that
// started the segment that is closest
bool GetDistance(BPoint point,
float* distance, int32* index) const;
// at curve segment indicated by "index", this
// function looks for the closest point
// directly on the curve and returns a "scale"
// that indicates the distance on the curve
// between [0..1]
bool FindBezierScale(int32 index, BPoint point,
double* scale) const;
// this function can be used to get a point
// directly on the segment indicated by "index"
// "scale" is on [0..1] indicating the distance
// from the start of the segment to the end
bool GetPoint(int32 index, double scale,
BPoint& point) const;
void SetClosed(bool closed);
bool IsClosed() const
{ return fClosed; }
BRect Bounds() const;
BRect ControlPointBounds() const;
void Iterate(Iterator* iterator,
float smoothScale = 1.0) const;
void CleanUp();
void Reverse();
void PrintToStream() const;
bool GetAGGPathStorage(agg::path_storage& path) const;
private:
BRect _Bounds() const;
void _SetPoint(int32 index, BPoint point);
bool _SetPointCount(int32 count);
control_point* fPath;
bool fClosed;
int32 fPointCount;
int32 fAllocCount;
mutable BRect fCachedBounds;
};
#endif // VECTOR_PATH_H
@@ -0,0 +1,100 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "AddPointCommand.h"
#include <stdio.h>
#include "VectorPath.h"
// constructor
AddPointCommand::AddPointCommand(VectorPath* path,
int32 index,
const int32* selected,
int32 count)
: PathCommand(path),
fIndex(index),
fOldSelection(NULL),
fOldSelectionCount(count)
{
if (fOldSelectionCount > 0 && selected) {
fOldSelection = new int32[fOldSelectionCount];
memcpy(fOldSelection, selected, fOldSelectionCount * sizeof(int32));
}
}
// destructor
AddPointCommand::~AddPointCommand()
{
delete[] fOldSelection;
}
// Perform
status_t
AddPointCommand::Perform()
{
status_t status = InitCheck();
if (status < B_OK)
return status;
// path point is already added,
// but we don't know the parameters yet
if (!fPath->GetPointsAt(fIndex, fPoint, fPointIn, fPointOut))
status = B_NO_INIT;
return status;
}
// Undo
status_t
AddPointCommand::Undo()
{
status_t status = InitCheck();
if (status < B_OK)
return status;
// remove point
if (fPath->RemovePoint(fIndex)) {
// restore selection before adding point
_Select(fOldSelection, fOldSelectionCount);
} else {
status = B_ERROR;
}
return status;
}
// Redo
status_t
AddPointCommand::Redo()
{
status_t status = InitCheck();
if (status < B_OK)
return status;
AutoNotificationSuspender _(fPath);
// add point again
if (fPath->AddPoint(fPoint, fIndex)) {
fPath->SetPoint(fIndex, fPoint, fPointIn, fPointOut, true);
// select added point
_Select(&fIndex, 1);
} else {
status = B_ERROR;
}
return status;
}
// GetName
void
AddPointCommand::GetName(BString& name)
{
// name << _GetString(ADD_CONTROL_POINT, "Add Control Point");
name << "Add Control Point";
}
@@ -0,0 +1,40 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef ADD_POINT_COMMAND_H
#define ADD_POINT_COMMAND_H
#include <Point.h>
#include "PathCommand.h"
class AddPointCommand : public PathCommand {
public:
AddPointCommand(VectorPath* path,
int32 index,
const int32* selected,
int32 count);
virtual ~AddPointCommand();
virtual status_t Perform();
virtual status_t Undo();
virtual status_t Redo();
virtual void GetName(BString& name);
private:
int32 fIndex;
BPoint fPoint;
BPoint fPointIn;
BPoint fPointOut;
int32* fOldSelection;
int32 fOldSelectionCount;
};
#endif // ADD_POINT_COMMAND_H
@@ -0,0 +1,96 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "ChangePointCommand.h"
#include <new>
#include <stdio.h>
#include "VectorPath.h"
using std::nothrow;
// constructor
ChangePointCommand::ChangePointCommand(VectorPath* path,
int32 index,
const int32* selected,
int32 count)
: PathCommand(path),
fIndex(index),
fOldSelection(NULL),
fOldSelectionCount(count)
{
if (fPath && !fPath->GetPointsAt(fIndex, fPoint, fPointIn, fPointOut, &fConnected))
fPath = NULL;
if (fOldSelectionCount > 0 && selected) {
fOldSelection = new (nothrow) int32[fOldSelectionCount];
memcpy(fOldSelection, selected, fOldSelectionCount * sizeof(int32));
}
}
// destructor
ChangePointCommand::~ChangePointCommand()
{
delete[] fOldSelection;
}
// Perform
status_t
ChangePointCommand::Perform()
{
// path point is already changed
return B_OK;
}
// Undo
status_t
ChangePointCommand::Undo()
{
status_t status = InitCheck();
if (status < B_OK)
return status;
// set the point to the remembered state and
// save the previous state of the point
BPoint point;
BPoint pointIn;
BPoint pointOut;
bool connected;
if (fPath->GetPointsAt(fIndex, point, pointIn, pointOut, &connected)
&& fPath->SetPoint(fIndex, fPoint, fPointIn, fPointOut, fConnected)) {
// toggle the remembered settings
fPoint = point;
fPointIn = pointIn;
fPointOut = pointOut;
fConnected = connected;
// restore old selection
_Select(fOldSelection, fOldSelectionCount);
} else {
status = B_ERROR;
}
return status;
}
// Redo
status_t
ChangePointCommand::Redo()
{
status_t status = Undo();
if (status >= B_OK)
_Select(&fIndex, 1);
return status;
}
// GetName
void
ChangePointCommand::GetName(BString& name)
{
// name << _GetString(MODIFY_CONTROL_POINT, "Modify Control Point");
name << "Modify Control Point";
}
@@ -0,0 +1,42 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#ifndef CHANGE_POINT_COMMAND_H
#define CHANGE_POINT_COMMAND_H
#include <Point.h>
#include "PathCommand.h"
class ChangePointCommand : public PathCommand {
public:
ChangePointCommand(VectorPath* path,
int32 index,
const int32* selected,
int32 count);
virtual ~ChangePointCommand();
virtual status_t Perform();
virtual status_t Undo();
virtual status_t Redo();
virtual void GetName(BString& name);
private:
int32 fIndex;
BPoint fPoint;
BPoint fPointIn;
BPoint fPointOut;
bool fConnected;
int32* fOldSelection;
int32 fOldSelectionCount;
};
#endif // CHANGE_POINT_COMMAND_H
@@ -0,0 +1,128 @@
/*
* Copyright 2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
*/
#include "InsertPointCommand.h"
#include <new>
#include <stdio.h>
#include "VectorPath.h"
using std::nothrow;
// constructor
InsertPointCommand::InsertPointCommand(VectorPath* path,
int32 index,
const int32* selected,
int32 count)
: PathCommand(path),
fIndex(index),
fOldSelection(NULL),
fOldSelectionCount(count)
{
if (fPath && (!fPath->GetPointsAt(fIndex, fPoint, fPointIn, fPointOut)
|| !fPath->GetPointOutAt(fIndex - 1, fPreviousOut)
|| !fPath->GetPointInAt(fIndex + 1, fNextIn))) {
fPath = NULL;
}
if (fOldSelectionCount > 0 && selected) {
fOldSelection = new (nothrow) int32[count];
memcpy(fOldSelection, selected, count * sizeof(int32));
}
}
// destructor
InsertPointCommand::~InsertPointCommand()
{
delete[] fOldSelection;
}
// Perform
status_t
InsertPointCommand::Perform()
{
status_t status = InitCheck();
if (status < B_OK)
return status;
// path point is already added
// but in/out points might still have changed
fPath->GetPointInAt(fIndex, fPointIn);
fPath->GetPointOutAt(fIndex, fPointOut);
return status;
}
// Undo
status_t
InsertPointCommand::Undo()
{
status_t status = InitCheck();
if (status < B_OK)
return status;
AutoNotificationSuspender _(fPath);
// remove the inserted point
if (fPath->RemovePoint(fIndex)) {
// remember current previous "out" and restore it
BPoint previousOut = fPreviousOut;
fPath->GetPointOutAt(fIndex - 1, fPreviousOut);
fPath->SetPointOut(fIndex - 1, previousOut);
// remember current next "in" and restore it
BPoint nextIn = fNextIn;
fPath->GetPointInAt(fIndex, fNextIn);
fPath->SetPointIn(fIndex, nextIn);
// restore previous selection
_Select(fOldSelection, fOldSelectionCount);
} else {
status = B_ERROR;
}
return status;
}
// Redo
status_t
InsertPointCommand::Redo()
{
status_t status = InitCheck();
if (status < B_OK)
return status;
AutoNotificationSuspender _(fPath);
// insert point again
if (fPath->AddPoint(fPoint, fIndex)) {
fPath->SetPoint(fIndex, fPoint, fPointIn, fPointOut, true);
// remember current previous "out" and restore it
BPoint previousOut = fPreviousOut;
fPath->GetPointOutAt(fIndex - 1, fPreviousOut);
fPath->SetPointOut(fIndex - 1, previousOut);
// remember current next "in" and restore it
BPoint nextIn = fNextIn;
fPath->GetPointInAt(fIndex + 1, fNextIn);
fPath->SetPointIn(fIndex + 1, nextIn);
// select inserted point
_Select(&fIndex, 1);
} else {
status = B_ERROR;
}
return status;
}
// GetName
void
InsertPointCommand::GetName(BString& name)
{
// name << _GetString(INSERT_CONTROL_POINT, "Insert Control Point");
name << "Insert Control Point";
}

Some files were not shown because too many files have changed in this diff Show More