diff --git a/src/apps/Jamfile b/src/apps/Jamfile index 7077f131c7..1e78a890bf 100644 --- a/src/apps/Jamfile +++ b/src/apps/Jamfile @@ -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 ; diff --git a/src/apps/icon-o-matic/CanvasView.cpp b/src/apps/icon-o-matic/CanvasView.cpp new file mode 100644 index 0000000000..8343a2d59c --- /dev/null +++ b/src/apps/icon-o-matic/CanvasView.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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; +} + + + diff --git a/src/apps/icon-o-matic/CanvasView.h b/src/apps/icon-o-matic/CanvasView.h new file mode 100644 index 0000000000..18bc1def46 --- /dev/null +++ b/src/apps/icon-o-matic/CanvasView.h @@ -0,0 +1,26 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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 diff --git a/src/apps/icon-o-matic/IconEditorApp.cpp b/src/apps/icon-o-matic/IconEditorApp.cpp new file mode 100644 index 0000000000..9c0bec89a5 --- /dev/null +++ b/src/apps/icon-o-matic/IconEditorApp.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "IconEditorApp.h" + +#include +#include +#include + +#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(); +} + + diff --git a/src/apps/icon-o-matic/IconEditorApp.h b/src/apps/icon-o-matic/IconEditorApp.h new file mode 100644 index 0000000000..ffd7f31024 --- /dev/null +++ b/src/apps/icon-o-matic/IconEditorApp.h @@ -0,0 +1,34 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef ICON_EDITOR_APP_H +#define ICON_EDITOR_APP_H + +#include + +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 diff --git a/src/apps/icon-o-matic/Jamfile b/src/apps/icon-o-matic/Jamfile new file mode 100644 index 0000000000..38bf148e0d --- /dev/null +++ b/src/apps/icon-o-matic/Jamfile @@ -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 ; +} + diff --git a/src/apps/icon-o-matic/MainWindow.cpp b/src/apps/icon-o-matic/MainWindow.cpp new file mode 100644 index 0000000000..284fa88614 --- /dev/null +++ b/src/apps/icon-o-matic/MainWindow.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "MainWindow.h" + +#include + +#include + +#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; +} + diff --git a/src/apps/icon-o-matic/MainWindow.h b/src/apps/icon-o-matic/MainWindow.h new file mode 100644 index 0000000000..3977787b81 --- /dev/null +++ b/src/apps/icon-o-matic/MainWindow.h @@ -0,0 +1,38 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef MAIN_WINDOW_H +#define MAIN_WINDOW_H + +#include + +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 diff --git a/src/apps/icon-o-matic/NOTES b/src/apps/icon-o-matic/NOTES new file mode 100644 index 0000000000..52644865e3 --- /dev/null +++ b/src/apps/icon-o-matic/NOTES @@ -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 diff --git a/src/apps/icon-o-matic/document/Document.cpp b/src/apps/icon-o-matic/document/Document.cpp new file mode 100644 index 0000000000..3134af1614 --- /dev/null +++ b/src/apps/icon-o-matic/document/Document.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Document.h" + +#include + +#include + +#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; +} + diff --git a/src/apps/icon-o-matic/document/Document.h b/src/apps/icon-o-matic/document/Document.h new file mode 100644 index 0000000000..6b6c716f42 --- /dev/null +++ b/src/apps/icon-o-matic/document/Document.h @@ -0,0 +1,47 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef DOCUMENT_H +#define DOCUMENT_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/command/Command.cpp b/src/apps/icon-o-matic/generic/command/Command.cpp new file mode 100644 index 0000000000..87cbbf83da --- /dev/null +++ b/src/apps/icon-o-matic/generic/command/Command.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Command.h" + +#include + +#include + +// 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; +} diff --git a/src/apps/icon-o-matic/generic/command/Command.h b/src/apps/icon-o-matic/generic/command/Command.h new file mode 100644 index 0000000000..6f907c4a6e --- /dev/null +++ b/src/apps/icon-o-matic/generic/command/Command.h @@ -0,0 +1,40 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef COMMAND_H +#define COMMAND_H + +#include +#include + +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 diff --git a/src/apps/icon-o-matic/generic/command/CommandStack.cpp b/src/apps/icon-o-matic/generic/command/CommandStack.cpp new file mode 100644 index 0000000000..a6cfb3b401 --- /dev/null +++ b/src/apps/icon-o-matic/generic/command/CommandStack.cpp @@ -0,0 +1,223 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "CommandStack.h" + +#include +#include + +#include +#include + +#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; +} + + + diff --git a/src/apps/icon-o-matic/generic/command/CommandStack.h b/src/apps/icon-o-matic/generic/command/CommandStack.h new file mode 100644 index 0000000000..e4ddd8091b --- /dev/null +++ b/src/apps/icon-o-matic/generic/command/CommandStack.h @@ -0,0 +1,49 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef COMMAND_STACK_H +#define COMMAND_STACK_H + +#include + +#include + +#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_stack; + + command_stack fUndoHistory; + command_stack fRedoHistory; + Command* fSavedCommand; +}; + +#endif // COMMAND_STACK_H diff --git a/src/apps/icon-o-matic/generic/command/CompoundCommand.cpp b/src/apps/icon-o-matic/generic/command/CompoundCommand.cpp new file mode 100644 index 0000000000..5b9e0bd7f5 --- /dev/null +++ b/src/apps/icon-o-matic/generic/command/CompoundCommand.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "CompoundCommand.h" + +#include + +// 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()); +} diff --git a/src/apps/icon-o-matic/generic/command/CompoundCommand.h b/src/apps/icon-o-matic/generic/command/CompoundCommand.h new file mode 100644 index 0000000000..a3698ed496 --- /dev/null +++ b/src/apps/icon-o-matic/generic/command/CompoundCommand.h @@ -0,0 +1,38 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/Group.cpp b/src/apps/icon-o-matic/generic/gui/Group.cpp new file mode 100644 index 0000000000..cf833f3843 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/Group.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Group.h" + +#include + +// 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); +} + diff --git a/src/apps/icon-o-matic/generic/gui/Group.h b/src/apps/icon-o-matic/generic/gui/Group.h new file mode 100644 index 0000000000..762b61682c --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/Group.h @@ -0,0 +1,41 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef GROUP_H +#define GROUP_H + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/IconButton.cpp b/src/apps/icon-o-matic/generic/gui/IconButton.cpp new file mode 100644 index 0000000000..c7d90fbeba --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/IconButton.cpp @@ -0,0 +1,781 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "IconButton.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(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(®ion); + region.Exclude(bitmap->Bounds().OffsetByCopy(point)); + ConstrainClippingRegion(®ion); + } + // 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(); +} diff --git a/src/apps/icon-o-matic/generic/gui/IconButton.h b/src/apps/icon-o-matic/generic/gui/IconButton.h new file mode 100644 index 0000000000..20840b94d2 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/IconButton.h @@ -0,0 +1,131 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +/** 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 + +#include +#include +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/IconOptionsControl.cpp b/src/apps/icon-o-matic/generic/gui/IconOptionsControl.cpp new file mode 100644 index 0000000000..5e3f60f2ad --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/IconOptionsControl.cpp @@ -0,0 +1,281 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "IconOptionsControl.h" + +#include + +#include + +#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(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(view); + return NULL; +} + +// _TriggerRelayout +void +IconOptionsControl::_TriggerRelayout() +{ + if (!Parent()) + return; + + MView* mParent = dynamic_cast(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); + } +} diff --git a/src/apps/icon-o-matic/generic/gui/IconOptionsControl.h b/src/apps/icon-o-matic/generic/gui/IconOptionsControl.h new file mode 100644 index 0000000000..2daae8475a --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/IconOptionsControl.h @@ -0,0 +1,64 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef ICON_OPTIONS_CONTROL_H +#define ICON_OPTIONS_CONTROL_H + +#include +#include + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/InputTextView.cpp b/src/apps/icon-o-matic/generic/gui/InputTextView.cpp new file mode 100644 index 0000000000..965bd55d35 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/InputTextView.cpp @@ -0,0 +1,162 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "InputTextView.h" + +#include +#include + +#include + +// 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(©); + } + 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(); + } +} diff --git a/src/apps/icon-o-matic/generic/gui/InputTextView.h b/src/apps/icon-o-matic/generic/gui/InputTextView.h new file mode 100644 index 0000000000..d99aa2403a --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/InputTextView.h @@ -0,0 +1,57 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef INPUT_TEXT_VIEW_H +#define INPUT_TEXT_VIEW_H + +#include +#include + +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 + + diff --git a/src/apps/icon-o-matic/generic/gui/ListViews.cpp b/src/apps/icon-o-matic/generic/gui/ListViews.cpp new file mode 100644 index 0000000000..aa6ce3de30 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/ListViews.cpp @@ -0,0 +1,908 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "ListViews.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(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(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(this)); + int32 index; + for (int32 i = 0; (index = CurrentSelection(i)) >= 0; i++) + message->AddInt32( "index", index ); + } +} + diff --git a/src/apps/icon-o-matic/generic/gui/ListViews.h b/src/apps/icon-o-matic/generic/gui/ListViews.h new file mode 100644 index 0000000000..1d015ca28c --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/ListViews.h @@ -0,0 +1,185 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef LIST_VIEWS_H +#define LIST_VIEWS_H + +#include +#include +#include + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/MouseWheelFilter.h b/src/apps/icon-o-matic/generic/gui/MouseWheelFilter.h new file mode 100644 index 0000000000..d0e5af9b9e --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/MouseWheelFilter.h @@ -0,0 +1,64 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef MOUSE_WHEEL_FILTER_H +#define MOUSE_WHEEL_FILTER_H + +#include +#include + +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(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 + diff --git a/src/apps/icon-o-matic/generic/gui/NummericalTextView.cpp b/src/apps/icon-o-matic/generic/gui/NummericalTextView.cpp new file mode 100644 index 0000000000..f6c3b3a452 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/NummericalTextView.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "NummericalTextView.h" + +#include +#include + +#include + +// 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(©); + } + 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('.'); + } +} + + + + diff --git a/src/apps/icon-o-matic/generic/gui/NummericalTextView.h b/src/apps/icon-o-matic/generic/gui/NummericalTextView.h new file mode 100644 index 0000000000..a0fc3e1fe1 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/NummericalTextView.h @@ -0,0 +1,62 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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 + + diff --git a/src/apps/icon-o-matic/generic/gui/StringTextView.cpp b/src/apps/icon-o-matic/generic/gui/StringTextView.cpp new file mode 100644 index 0000000000..fab1202157 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/StringTextView.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "StringTextView.h" + +#include +#include + +// 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(©); + } + 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(); +} + diff --git a/src/apps/icon-o-matic/generic/gui/StringTextView.h b/src/apps/icon-o-matic/generic/gui/StringTextView.h new file mode 100644 index 0000000000..768dada75c --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/StringTextView.h @@ -0,0 +1,42 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef STRING_TEXT_VIEW_H +#define STRING_TEXT_VIEW_H + +#include "InputTextView.h" + +#include + +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 + + diff --git a/src/apps/icon-o-matic/generic/gui/SwatchView.cpp b/src/apps/icon-o-matic/generic/gui/SwatchView.cpp new file mode 100644 index 0000000000..84bf22b174 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/SwatchView.cpp @@ -0,0 +1,230 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "SwatchView.h" + +#include + +#include +#include +#include +#include +#include +#include + +#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(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)); +} + diff --git a/src/apps/icon-o-matic/generic/gui/SwatchView.h b/src/apps/icon-o-matic/generic/gui/SwatchView.h new file mode 100644 index 0000000000..67fcca0672 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/SwatchView.h @@ -0,0 +1,70 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef SWATCH_VIEW_H +#define SWATCH_VIEW_H + +#include + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/cursors.h b/src/apps/icon-o-matic/generic/gui/cursors.h new file mode 100644 index 0000000000..7c4c5558dc --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/cursors.h @@ -0,0 +1,344 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/panel/Panel.cpp b/src/apps/icon-o-matic/generic/gui/panel/Panel.cpp new file mode 100644 index 0000000000..e179bf49eb --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/panel/Panel.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Panel.h" + +#include + +#include +#include +#include + +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)); +} diff --git a/src/apps/icon-o-matic/generic/gui/panel/Panel.h b/src/apps/icon-o-matic/generic/gui/panel/Panel.h new file mode 100644 index 0000000000..f0cc3b1552 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/panel/Panel.h @@ -0,0 +1,37 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef PANEL_H +#define PANEL_H + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/InputSlider.cpp b/src/apps/icon-o-matic/generic/gui/popup_control/InputSlider.cpp new file mode 100644 index 0000000000..133125829b --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/InputSlider.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "InputSlider.h" + +#include + +#include +#include + +#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(*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(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); +} + diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/InputSlider.h b/src/apps/icon-o-matic/generic/gui/popup_control/InputSlider.h new file mode 100644 index 0000000000..ac8f03882c --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/InputSlider.h @@ -0,0 +1,47 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/PopupControl.cpp b/src/apps/icon-o-matic/generic/gui/popup_control/PopupControl.cpp new file mode 100644 index 0000000000..f2f9f868c8 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/PopupControl.cpp @@ -0,0 +1,153 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#include "PopupControl.h" + +#include + +#include +#include + +#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) +{ +} + + diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/PopupControl.h b/src/apps/icon-o-matic/generic/gui/popup_control/PopupControl.h new file mode 100644 index 0000000000..d1eb844675 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/PopupControl.h @@ -0,0 +1,56 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#ifndef POPUP_CONTROL_H +#define POPUP_CONTROL_H + +#include + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/PopupSlider.cpp b/src/apps/icon-o-matic/generic/gui/popup_control/PopupSlider.cpp new file mode 100644 index 0000000000..846f7128ba --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/PopupSlider.cpp @@ -0,0 +1,454 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#include "PopupSlider.h" + +#include +#include + +#include + +#include +#include + +#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 +} +*/ + + diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/PopupSlider.h b/src/apps/icon-o-matic/generic/gui/popup_control/PopupSlider.h new file mode 100644 index 0000000000..8104eafa56 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/PopupSlider.h @@ -0,0 +1,122 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#ifndef POPUP_SLIDER_H +#define POPUP_SLIDER_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/PopupView.cpp b/src/apps/icon-o-matic/generic/gui/popup_control/PopupView.cpp new file mode 100644 index 0000000000..17ca998a5a --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/PopupView.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#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); +} + diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/PopupView.h b/src/apps/icon-o-matic/generic/gui/popup_control/PopupView.h new file mode 100644 index 0000000000..a1e4e9c2ca --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/PopupView.h @@ -0,0 +1,37 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#ifndef POPUP_VIEW_H +#define POPUP_VIEW_H + +#include + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/PopupWindow.cpp b/src/apps/icon-o-matic/generic/gui/popup_control/PopupWindow.cpp new file mode 100644 index 0000000000..3f46008258 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/PopupWindow.cpp @@ -0,0 +1,126 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#include +#include +#include +#include + +#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(*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(); +} + diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/PopupWindow.h b/src/apps/icon-o-matic/generic/gui/popup_control/PopupWindow.h new file mode 100644 index 0000000000..a1f693f3e3 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/PopupWindow.h @@ -0,0 +1,44 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#ifndef POPUP_WINDOW_H +#define POPUP_WINDOW_H + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/SliderView.cpp b/src/apps/icon-o-matic/generic/gui/popup_control/SliderView.cpp new file mode 100644 index 0000000000..3d837815cc --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/SliderView.cpp @@ -0,0 +1,397 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#include "SliderView.h" + +#include +#include + +#include +#include + +#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); +} + + + + diff --git a/src/apps/icon-o-matic/generic/gui/popup_control/SliderView.h b/src/apps/icon-o-matic/generic/gui/popup_control/SliderView.h new file mode 100644 index 0000000000..227081dbb9 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/popup_control/SliderView.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#ifndef SLIDER_VIEW_H +#define SLIDER_VIEW_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/ScrollCornerBitmaps.h b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollCornerBitmaps.h new file mode 100644 index 0000000000..9f0b91d784 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollCornerBitmaps.h @@ -0,0 +1,61 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +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 +}; + diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/ScrollView.cpp b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollView.cpp new file mode 100644 index 0000000000..81966a6d66 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollView.cpp @@ -0,0 +1,861 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#include "ScrollView.h" + +#include +#include +#include + +#include +#include +#include +#include + +#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(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(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); +} + + diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/ScrollView.h b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollView.h new file mode 100644 index 0000000000..ae7d25d7c9 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollView.h @@ -0,0 +1,113 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + * Ingo Weinhold + */ + +#ifndef SCROLL_VIEW_H +#define SCROLL_VIEW_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/Scrollable.cpp b/src/apps/icon-o-matic/generic/gui/scrollview/Scrollable.cpp new file mode 100644 index 0000000000..ae20cbc351 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/Scrollable.cpp @@ -0,0 +1,227 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + +#include "Scrollable.h" + +#include +#include + +#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; +} + diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/Scrollable.h b/src/apps/icon-o-matic/generic/gui/scrollview/Scrollable.h new file mode 100644 index 0000000000..2545311bd9 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/Scrollable.h @@ -0,0 +1,59 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + +#ifndef SCROLLABLE_H +#define SCROLLABLE_H + +#include +#include + +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 + diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/ScrollableView.cpp b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollableView.cpp new file mode 100644 index 0000000000..cbf5b67abb --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollableView.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + +#include "ScrollableView.h" + +#include + +// constructor +ScrollableView::ScrollableView() + : Scrollable() +{ +} + +// destructor +ScrollableView::~ScrollableView() +{ +} + +// ScrollOffsetChanged +void +ScrollableView::ScrollOffsetChanged(BPoint oldOffset, BPoint newOffset) +{ + if (BView* view = dynamic_cast(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); + } +} + diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/ScrollableView.h b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollableView.h new file mode 100644 index 0000000000..244155616b --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/ScrollableView.h @@ -0,0 +1,28 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + +/** 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 diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/Scroller.cpp b/src/apps/icon-o-matic/generic/gui/scrollview/Scroller.cpp new file mode 100644 index 0000000000..06fdb34e06 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/Scroller.cpp @@ -0,0 +1,178 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + +#include "Scroller.h" + +#include + +#include +#include + +#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*/) +{ +} + diff --git a/src/apps/icon-o-matic/generic/gui/scrollview/Scroller.h b/src/apps/icon-o-matic/generic/gui/scrollview/Scroller.h new file mode 100644 index 0000000000..6b32a9c173 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/scrollview/Scroller.h @@ -0,0 +1,51 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/stateview/Manipulator.cpp b/src/apps/icon-o-matic/generic/gui/stateview/Manipulator.cpp new file mode 100644 index 0000000000..2664128c39 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/stateview/Manipulator.cpp @@ -0,0 +1,126 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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) +{ +} diff --git a/src/apps/icon-o-matic/generic/gui/stateview/Manipulator.h b/src/apps/icon-o-matic/generic/gui/stateview/Manipulator.h new file mode 100644 index 0000000000..c85e3c122f --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/stateview/Manipulator.h @@ -0,0 +1,61 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef MANIPULATOR_H +#define MANIPULATOR_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/stateview/MultipleManipulatorState.cpp b/src/apps/icon-o-matic/generic/gui/stateview/MultipleManipulatorState.cpp new file mode 100644 index 0000000000..b0c1af938e --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/stateview/MultipleManipulatorState.cpp @@ -0,0 +1,255 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "MultipleManipulatorState.h" + +#include + +#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); +} + + diff --git a/src/apps/icon-o-matic/generic/gui/stateview/MultipleManipulatorState.h b/src/apps/icon-o-matic/generic/gui/stateview/MultipleManipulatorState.h new file mode 100644 index 0000000000..d697175908 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/stateview/MultipleManipulatorState.h @@ -0,0 +1,62 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef MULTIPLE_MANIPULATOR_STATE_H +#define MULTIPLE_MANIPULATOR_STATE_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/stateview/StateView.cpp b/src/apps/icon-o-matic/generic/gui/stateview/StateView.cpp new file mode 100644 index 0000000000..73296c9723 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/stateview/StateView.cpp @@ -0,0 +1,487 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "StateView.h" + +#include +#include +#include + +#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); + } +} diff --git a/src/apps/icon-o-matic/generic/gui/stateview/StateView.h b/src/apps/icon-o-matic/generic/gui/stateview/StateView.h new file mode 100644 index 0000000000..7fb694ded6 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/stateview/StateView.h @@ -0,0 +1,98 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef STATE_VIEW_H +#define STATE_VIEW_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/gui/stateview/ViewState.cpp b/src/apps/icon-o-matic/generic/gui/stateview/ViewState.cpp new file mode 100644 index 0000000000..2bfddfb443 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/stateview/ViewState.cpp @@ -0,0 +1,112 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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; +} + + diff --git a/src/apps/icon-o-matic/generic/gui/stateview/ViewState.h b/src/apps/icon-o-matic/generic/gui/stateview/ViewState.h new file mode 100644 index 0000000000..abb0b0dce9 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/stateview/ViewState.h @@ -0,0 +1,89 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef VIEW_STATE_H +#define VIEW_STATE_H + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/gui/ui_defines.h b/src/apps/icon-o-matic/generic/gui/ui_defines.h new file mode 100644 index 0000000000..34cfb7f988 --- /dev/null +++ b/src/apps/icon-o-matic/generic/gui/ui_defines.h @@ -0,0 +1,18 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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 diff --git a/src/apps/icon-o-matic/generic/listener/AbstractLOAdapter.cpp b/src/apps/icon-o-matic/generic/listener/AbstractLOAdapter.cpp new file mode 100644 index 0000000000..fca20b7354 --- /dev/null +++ b/src/apps/icon-o-matic/generic/listener/AbstractLOAdapter.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + +#include "AbstractLOAdapter.h" + +#include +#include +#include + +// 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); +} + diff --git a/src/apps/icon-o-matic/generic/listener/AbstractLOAdapter.h b/src/apps/icon-o-matic/generic/listener/AbstractLOAdapter.h new file mode 100644 index 0000000000..6ecd3fb01a --- /dev/null +++ b/src/apps/icon-o-matic/generic/listener/AbstractLOAdapter.h @@ -0,0 +1,41 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Ingo Weinhold + */ + +// 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 + +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 diff --git a/src/apps/icon-o-matic/generic/listener/Observable.cpp b/src/apps/icon-o-matic/generic/listener/Observable.cpp new file mode 100644 index 0000000000..54d0180495 --- /dev/null +++ b/src/apps/icon-o-matic/generic/listener/Observable.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Observable.h" + +#include + +#include + +#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(); +} + diff --git a/src/apps/icon-o-matic/generic/listener/Observable.h b/src/apps/icon-o-matic/generic/listener/Observable.h new file mode 100644 index 0000000000..bfeeffaa32 --- /dev/null +++ b/src/apps/icon-o-matic/generic/listener/Observable.h @@ -0,0 +1,51 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef OBSERVABLE_H +#define OBSERVABLE_H + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/listener/Observer.cpp b/src/apps/icon-o-matic/generic/listener/Observer.cpp new file mode 100644 index 0000000000..2a5e0f6761 --- /dev/null +++ b/src/apps/icon-o-matic/generic/listener/Observer.cpp @@ -0,0 +1,18 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Observer.h" + +Observer::Observer() +{ +} + +Observer::~Observer() +{ +} + diff --git a/src/apps/icon-o-matic/generic/listener/Observer.h b/src/apps/icon-o-matic/generic/listener/Observer.h new file mode 100644 index 0000000000..c3b19d425f --- /dev/null +++ b/src/apps/icon-o-matic/generic/listener/Observer.h @@ -0,0 +1,24 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef OBSERVER_H +#define OBSERVER_H + +#include + +class Observable; + +class Observer { + public: + Observer(); + virtual ~Observer(); + + virtual void ObjectChanged(const Observable* object) = 0; +}; + +#endif // OBSERVER_H diff --git a/src/apps/icon-o-matic/generic/selection/Selectable.cpp b/src/apps/icon-o-matic/generic/selection/Selectable.cpp new file mode 100644 index 0000000000..2b18798900 --- /dev/null +++ b/src/apps/icon-o-matic/generic/selection/Selectable.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Selectable.h" + +// constructor +Selectable::Selectable() + : fSelected(false) +{ +} + +// destructor +Selectable::~Selectable() +{ +} + +// SetSelected +void +Selectable::SetSelected(bool selected) +{ + if (fSelected != selected) { + fSelected = selected; + SelectedChanged(); + } +} diff --git a/src/apps/icon-o-matic/generic/selection/Selectable.h b/src/apps/icon-o-matic/generic/selection/Selectable.h new file mode 100644 index 0000000000..3f581bc8e9 --- /dev/null +++ b/src/apps/icon-o-matic/generic/selection/Selectable.h @@ -0,0 +1,31 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef SELECTABLE_H +#define SELECTABLE_H + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/selection/Selection.cpp b/src/apps/icon-o-matic/generic/selection/Selection.cpp new file mode 100644 index 0000000000..6081bb0e1a --- /dev/null +++ b/src/apps/icon-o-matic/generic/selection/Selection.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "Selection.h" + +#include + +#include + +#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(); +} + diff --git a/src/apps/icon-o-matic/generic/selection/Selection.h b/src/apps/icon-o-matic/generic/selection/Selection.h new file mode 100644 index 0000000000..64af760c6e --- /dev/null +++ b/src/apps/icon-o-matic/generic/selection/Selection.h @@ -0,0 +1,40 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef SELECTION_H +#define SELECTION_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/generic/support/AutoDeleter.h b/src/apps/icon-o-matic/generic/support/AutoDeleter.h new file mode 100644 index 0000000000..9f5e351e20 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/AutoDeleter.h @@ -0,0 +1,133 @@ +/* + * Copyright 2001-2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * IngoWeinhold + */ + +/** 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 + +namespace BPrivate { + +// AutoDeleter + +template +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 +struct ObjectDelete +{ + inline void operator()(C *object) + { + delete object; + } +}; + +template +struct ObjectDeleter : AutoDeleter > +{ + ObjectDeleter() : AutoDeleter >() {} + ObjectDeleter(C *object) : AutoDeleter >(object) {} +}; + + +// ArrayDeleter + +template +struct ArrayDelete +{ + inline void operator()(C *array) + { + delete[] array; + } +}; + +template +struct ArrayDeleter : AutoDeleter > +{ + ArrayDeleter() : AutoDeleter >() {} + ArrayDeleter(C *array) : AutoDeleter >(array) {} +}; + + +// MemoryDeleter + +struct MemoryDelete +{ + inline void operator()(void *memory) + { + free(memory); + } +}; + +struct MemoryDeleter : AutoDeleter +{ + MemoryDeleter() : AutoDeleter() {} + MemoryDeleter(void *memory) : AutoDeleter(memory) {} +}; + +} // namespace BPrivate + +using BPrivate::ObjectDeleter; +using BPrivate::ArrayDeleter; +using BPrivate::MemoryDeleter; + +#endif // _AUTO_DELETER_H diff --git a/src/apps/icon-o-matic/generic/support/AutoLocker.h b/src/apps/icon-o-matic/generic/support/AutoLocker.h new file mode 100644 index 0000000000..ea568ebc61 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/AutoLocker.h @@ -0,0 +1,151 @@ +/* + * Copyright 2004-2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * IngoWeinhold + */ + +/** 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 + +// locking + +// AutoLockerStandardLocking +template +class AutoLockerStandardLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->Lock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->Unlock(); + } +}; + +// AutoLockerReadLocking +template +class AutoLockerReadLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->ReadLock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->ReadUnlock(); + } +}; + +// AutoLockerWriteLocking +template +class AutoLockerWriteLocking { +public: + inline bool Lock(Lockable *lockable) + { + return lockable->WriteLock(); + } + + inline void Unlock(Lockable *lockable) + { + lockable->WriteUnlock(); + } +}; + +// AutoLocker +template > +class AutoLocker { +private: + typedef AutoLocker 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 &operator=(Lockable *lockable) + { + SetTo(lockable); + return *this; + } + + inline AutoLocker &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 diff --git a/src/apps/icon-o-matic/generic/support/Debug.cpp b/src/apps/icon-o-matic/generic/support/Debug.cpp new file mode 100644 index 0000000000..9b29251237 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/Debug.cpp @@ -0,0 +1,151 @@ +/* + * Copyright 2003-2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * IngoWeinhold + */ + +#include "Debug.h" + +#include +#include +#include +#include +#include +#include + +#include + +/*! + \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 diff --git a/src/apps/icon-o-matic/generic/support/Debug.h b/src/apps/icon-o-matic/generic/support/Debug.h new file mode 100644 index 0000000000..3bd1c41699 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/Debug.h @@ -0,0 +1,152 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler + * IngoWeinhold + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include + +// USER defaults to 1 +#ifndef USER +# define USER 1 +#endif + +#if !USER +# include +#endif +#include +#include + +// 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 +# if DEBUG_PRINT +# define __out dbg_printf +# else +# define __out printf +# endif +#else +# include +# include +# 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 */ diff --git a/src/apps/icon-o-matic/generic/support/List.h b/src/apps/icon-o-matic/generic/support/List.h new file mode 100644 index 0000000000..9964b1aac4 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/List.h @@ -0,0 +1,78 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * IngoWeinhold + * Stephan Aßmus + */ + +#ifndef LIST_H +#define LIST_H + +#include + +template +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 diff --git a/src/apps/icon-o-matic/generic/support/RWLocker.cpp b/src/apps/icon-o-matic/generic/support/RWLocker.cpp new file mode 100644 index 0000000000..6fb06d24b5 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/RWLocker.cpp @@ -0,0 +1,471 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * IngoWeinhold + */ + +#include "RWLocker.h" + +#include + +// 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); +} + diff --git a/src/apps/icon-o-matic/generic/support/RWLocker.h b/src/apps/icon-o-matic/generic/support/RWLocker.h new file mode 100644 index 0000000000..e94cff3147 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/RWLocker.h @@ -0,0 +1,137 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * IngoWeinhold + */ + +// 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 +#include + +#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 > AutoReadLocker; +typedef AutoLocker > AutoWriteLocker; + +#endif // RW_LOCKER_H diff --git a/src/apps/icon-o-matic/generic/support/support.cpp b/src/apps/icon-o-matic/generic/support/support.cpp new file mode 100644 index 0000000000..df8d248872 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/support.cpp @@ -0,0 +1,155 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "support.h" + +#include +#include +#include + +#include +#include +#include + +// 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; +} diff --git a/src/apps/icon-o-matic/generic/support/support.h b/src/apps/icon-o-matic/generic/support/support.h new file mode 100644 index 0000000000..c948b92992 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/support.h @@ -0,0 +1,137 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef SUPPORT_H +#define SUPPORT_H + +#include + +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 +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 +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 diff --git a/src/apps/icon-o-matic/generic/support/support_settings.cpp b/src/apps/icon-o-matic/generic/support/support_settings.cpp new file mode 100644 index 0000000000..267f7036c5 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/support_settings.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "support_settings.h" + +#include + +#include +#include +#include +#include +#include + +// 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; +} + diff --git a/src/apps/icon-o-matic/generic/support/support_settings.h b/src/apps/icon-o-matic/generic/support/support_settings.h new file mode 100644 index 0000000000..9dae5831ef --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/support_settings.h @@ -0,0 +1,23 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef SUPPORT_SETTINGS_H +#define SUPPORT_SETTINGS_H + +#include + +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 diff --git a/src/apps/icon-o-matic/generic/support/support_ui.cpp b/src/apps/icon-o-matic/generic/support/support_ui.cpp new file mode 100644 index 0000000000..8e53364757 --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/support_ui.cpp @@ -0,0 +1,334 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "support.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// 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 = ""; + 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)); +} + diff --git a/src/apps/icon-o-matic/generic/support/support_ui.h b/src/apps/icon-o-matic/generic/support/support_ui.h new file mode 100644 index 0000000000..9623f8206d --- /dev/null +++ b/src/apps/icon-o-matic/generic/support/support_ui.h @@ -0,0 +1,73 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef SUPPORT_UI_H +#define SUPPORT_UI_H + +#include +#include +#include + +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 diff --git a/src/apps/icon-o-matic/main.cpp b/src/apps/icon-o-matic/main.cpp new file mode 100644 index 0000000000..c7b6492ce7 --- /dev/null +++ b/src/apps/icon-o-matic/main.cpp @@ -0,0 +1,20 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "IconEditorApp.h" + +int +main(int argc, char** argv) +{ + IconEditorApp* app = new IconEditorApp(); + app->Run(); + + delete app; + + return 0; +} diff --git a/src/apps/icon-o-matic/shape/PathManipulator.cpp b/src/apps/icon-o-matic/shape/PathManipulator.cpp new file mode 100644 index 0000000000..549e5b6fcc --- /dev/null +++ b/src/apps/icon-o-matic/shape/PathManipulator.cpp @@ -0,0 +1,1435 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "PathManipulator.h" + +#include +#include + +#include +#include +#include + +#include "cursors.h" +#include "support.h" + +#include "StateView.h" +#include "VectorPath.h" + +#include "AddPointCommand.h" +#include "ChangePointCommand.h" +//#include "CloseCommand.h" +#include "InsertPointCommand.h" +//#include "NewPathCommand.h" +//#include "NudgePointsCommand.h" +//#include "RemovePathCommand.h" +#include "RemovePointsCommand.h" +//#include "ReversePathCommand.h" +//#include "SelectPathCommand.h" +//#include "SelectPointsCommand.h" + +#define POINT_EXTEND 3.0 +#define CONTROL_POINT_EXTEND 2.0 +#define INSERT_DIST_THRESHOLD 7.0 +#define MOVE_THRESHOLD 9.0 + +enum { + UNDEFINED, + + NEW_PATH, + + ADD_POINT, + INSERT_POINT, + MOVE_POINT, + MOVE_POINT_IN, + MOVE_POINT_OUT, + CLOSE_PATH, + + TOGGLE_SHARP, + TOGGLE_SHARP_IN, + TOGGLE_SHARP_OUT, + + REMOVE_POINT, + REMOVE_POINT_IN, + REMOVE_POINT_OUT, + + SELECT_POINTS, + TRANSFORM_POINTS, + TRANSLATE_POINTS, + + SELECT_SUB_PATH, +}; + +inline const char* +string_for_mode(uint32 mode) +{ + switch (mode) { + case UNDEFINED: + return "UNDEFINED"; + case NEW_PATH: + return "NEW_PATH"; + case ADD_POINT: + return "ADD_POINT"; + case INSERT_POINT: + return "INSERT_POINT"; + case MOVE_POINT: + return "MOVE_POINT"; + case MOVE_POINT_IN: + return "MOVE_POINT_IN"; + case MOVE_POINT_OUT: + return "MOVE_POINT_OUT"; + case CLOSE_PATH: + return "CLOSE_PATH"; + case TOGGLE_SHARP: + return "TOGGLE_SHARP"; + case TOGGLE_SHARP_IN: + return "TOGGLE_SHARP_IN"; + case TOGGLE_SHARP_OUT: + return "TOGGLE_SHARP_OUT"; + case REMOVE_POINT: + return "REMOVE_POINT"; + case REMOVE_POINT_IN: + return "REMOVE_POINT_IN"; + case REMOVE_POINT_OUT: + return "REMOVE_POINT_OUT"; + case SELECT_POINTS: + return "SELECT_POINTS"; + case TRANSFORM_POINTS: + return "TRANSFORM_POINTS"; + case TRANSLATE_POINTS: + return "TRANSLATE_POINTS"; + case SELECT_SUB_PATH: + return "SELECT_SUB_PATH"; + } + return ""; +} + +class Selection : protected BList +{ +public: + inline Selection(int32 count = 20) + : BList(count) {} + inline ~Selection() {} + + inline void Add(int32 value) + { + if (value >= 0) { + // keep the list sorted + int32 count = CountItems(); + int32 index = 0; + for (; index < count; index++) { + if (IndexAt(index) > value) { + break; + } + } + BList::AddItem((void*)value, index); + } + } + + inline bool Remove(int32 value) + { return BList::RemoveItem((void*)value); } + + inline bool Contains(int32 value) const + { return BList::HasItem((void*)value); } + + inline bool IsEmpty() const + { return BList::IsEmpty(); } + + inline int32 IndexAt(int32 index) const + { return (int32)BList::ItemAt(index); } + + inline void MakeEmpty() + { BList::MakeEmpty(); } + + inline int32* Items() const + { return (int32*)BList::Items(); } + + inline const int32 CountItems() const + { return BList::CountItems(); } + + inline Selection& operator =(const Selection& other) + { + MakeEmpty(); + int32 count = other.CountItems(); + int32* items = other.Items(); + for (int32 i = 0; i < count; i++) { + Add(items[i]); + } + return *this; + } + + inline bool operator ==(const Selection& other) + { + if (other.CountItems() == CountItems()) { + int32* items = Items(); + int32* otherItems = other.Items(); + for (int32 i = 0; i < CountItems(); i++) { + if (items[i] != otherItems[i]) + return false; + items++; + otherItems++; + } + return true; + } else + return false; + } + + inline bool operator !=(const Selection& other) + { + return !(*this == other); + } +}; + + +// constructor +PathManipulator::PathManipulator(VectorPath* path) + : Manipulator(path), + fCanvasView(NULL), + + fCommandDown(false), + fOptionDown(false), + fShiftDown(false), + fAltDown(false), + + fClickToClose(false), + + fMode(NEW_PATH), + fFallBackMode(SELECT_POINTS), + + fMouseDown(false), + + fPath(path), + fCurrentPathPoint(-1), + + fChangePointCommand(NULL), + fInsertPointCommand(NULL), + fAddPointCommand(NULL), + + fSelection(new Selection()), + fOldSelection(new Selection()), + + fNudgeOffset(0.0, 0.0), + fLastNudgeTime(system_time())//, +// fNudgeCommand(NULL) +{ +} + +// destructor +PathManipulator::~PathManipulator() +{ + delete fChangePointCommand; + delete fInsertPointCommand; + delete fAddPointCommand; + + delete fSelection; + delete fOldSelection; + +// delete fNudgeCommand; +} + + +// #pragma mark - + +class StrokePathIterator : public VectorPath::Iterator { + public: + StrokePathIterator(BView* drawingView) + : fDrawingView(drawingView) + { + fDrawingView->SetHighColor(0, 0, 0, 255); + fDrawingView->SetDrawingMode(B_OP_OVER); + } + virtual ~StrokePathIterator() + {} + + virtual void MoveTo(BPoint point) + { + fBlack = true; + fDrawingView->SetHighColor(0, 0, 0, 255); + +// fCanvasView->ConvertFromCanvas(point); + fDrawingView->MovePenTo(point); + } + virtual void LineTo(BPoint point) + { + if (fBlack) + fDrawingView->SetHighColor(255, 255, 255, 255); + else + fDrawingView->SetHighColor(0, 0, 0, 255); + fBlack = !fBlack; + +// fCanvasView->ConvertFromCanvas(point); + fDrawingView->StrokeLine(point); + } + + private: + BView* fDrawingView; + bool fBlack; +}; + +// Draw +void +PathManipulator::Draw(BView* into, BRect updateRect) +{ + // draw the Bezier curve, but only if editing + // if not "editing", the path is actually on top all other modifiers + // TODO: make this customizable in the GUI + StrokePathIterator iterator(into); + fPath->Iterate(&iterator, 1.0/*fCanvasView->ZoomLevel()*/); + + into->SetLowColor(0, 0, 0, 255); + BPoint point; + BPoint pointIn; + BPoint pointOut; + rgb_color focusColor = (rgb_color){ 255, 0, 0, 255 }; + rgb_color highlightColor = (rgb_color){ 60, 60, 255, 255 }; + for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut); i++) { + bool highlight = fCurrentPathPoint == i; + bool selected = fSelection->Contains(i); + rgb_color normal = selected ? focusColor : (rgb_color){ 0, 0, 0, 255 }; + into->SetLowColor(normal); + into->SetHighColor(255, 255, 255, 255); + // convert to view coordinate space +// fCanvasView->ConvertFromCanvas(point); +// fCanvasView->ConvertFromCanvas(pointIn); +// fCanvasView->ConvertFromCanvas(pointOut); + // connect the points belonging to one control point + into->SetDrawingMode(B_OP_INVERT); + into->StrokeLine(point, pointIn); + into->StrokeLine(point, pointOut); + // draw main control point + if (highlight && (fMode == MOVE_POINT || + fMode == TOGGLE_SHARP || + fMode == REMOVE_POINT || + fMode == SELECT_POINTS || + fMode == CLOSE_PATH)) { + + into->SetLowColor(highlightColor); + } + + into->SetDrawingMode(B_OP_COPY); + BRect r(point, point); + r.InsetBy(-POINT_EXTEND, -POINT_EXTEND); + into->StrokeRect(r, B_SOLID_LOW); + r.InsetBy(1.0, 1.0); + into->FillRect(r, B_SOLID_HIGH); + // draw in control point + if (highlight && (fMode == MOVE_POINT_IN || + fMode == TOGGLE_SHARP_IN || + fMode == REMOVE_POINT_IN || + fMode == SELECT_POINTS)) + into->SetLowColor(highlightColor); + else + into->SetLowColor(normal); + if (selected) { + into->SetHighColor(220, 220, 220, 255); + } else { + into->SetHighColor(170, 170, 170, 255); + } + if (pointIn != point) { + r.Set(pointIn.x - CONTROL_POINT_EXTEND, pointIn.y - CONTROL_POINT_EXTEND, + pointIn.x + CONTROL_POINT_EXTEND, pointIn.y + CONTROL_POINT_EXTEND); + into->StrokeRect(r, B_SOLID_LOW); + r.InsetBy(1.0, 1.0); + into->FillRect(r, B_SOLID_HIGH); + } + // draw out control point + if (highlight && (fMode == MOVE_POINT_OUT || + fMode == TOGGLE_SHARP_OUT || + fMode == REMOVE_POINT_OUT || + fMode == SELECT_POINTS)) + into->SetLowColor(highlightColor); + else + into->SetLowColor(normal); + if (pointOut != point) { + r.Set(pointOut.x - CONTROL_POINT_EXTEND, pointOut.y - CONTROL_POINT_EXTEND, + pointOut.x + CONTROL_POINT_EXTEND, pointOut.y + CONTROL_POINT_EXTEND); + into->StrokeRect(r, B_SOLID_LOW); + r.InsetBy(1.0, 1.0); + into->FillRect(r, B_SOLID_HIGH); + } + } +} + +// #pragma mark - + +// MouseDown +bool +PathManipulator::MouseDown(BPoint where) +{ + fMouseDown = true; + + if (fMode == MOVE_POINT && + fSelection->CountItems() > 1 && + fSelection->Contains(fCurrentPathPoint)) { + fMode = TRANSLATE_POINTS; + } + + // TODO: translate from BView to canvas space + BPoint canvasWhere = where; + + // maybe we're changing some point, so we construct the + // "ChangePointCommand" here so that the point is remembered + // in its current state + delete fChangePointCommand; + fChangePointCommand = NULL; + switch (fMode) { + case TOGGLE_SHARP: + case TOGGLE_SHARP_IN: + case TOGGLE_SHARP_OUT: + case MOVE_POINT: + case MOVE_POINT_IN: + case MOVE_POINT_OUT: + case REMOVE_POINT_IN: + case REMOVE_POINT_OUT: + fChangePointCommand = new ChangePointCommand(fPath, + fCurrentPathPoint, + fSelection->Items(), + fSelection->CountItems()); + _Select(fCurrentPathPoint, fShiftDown); + break; + } + + // at this point we init doing something + switch (fMode) { + case ADD_POINT: + _AddPoint(canvasWhere); + break; + case INSERT_POINT: + _InsertPoint(canvasWhere, fCurrentPathPoint); + break; + + case TOGGLE_SHARP: + _SetSharp(fCurrentPathPoint); + // continue by dragging out the _connected_ in/out points + break; + case TOGGLE_SHARP_IN: + _SetInOutConnected(fCurrentPathPoint, false); + // continue by moving the "in" point + _SetMode(MOVE_POINT_IN); + break; + case TOGGLE_SHARP_OUT: + _SetInOutConnected(fCurrentPathPoint, false); + // continue by moving the "out" point + _SetMode(MOVE_POINT_OUT); + break; + + case MOVE_POINT: + case MOVE_POINT_IN: + case MOVE_POINT_OUT: + // the right thing happens since "fCurrentPathPoint" + // points to the correct index + break; + + case CLOSE_PATH: +// SetClosed(true, true); + break; + + case REMOVE_POINT: + if (fPath->CountPoints() == 1) { +// fCanvasView->Perform(new RemovePathCommand(this, fPath)); + } else { + fCanvasView->Perform(new RemovePointsCommand(fPath, + fCurrentPathPoint, + fSelection->Items(), + fSelection->CountItems())); + _RemovePoint(fCurrentPathPoint); + } + break; + case REMOVE_POINT_IN: + _RemovePointIn(fCurrentPathPoint); + break; + case REMOVE_POINT_OUT: + _RemovePointOut(fCurrentPathPoint); + break; + + case SELECT_POINTS: + if (!fShiftDown) { + fSelection->MakeEmpty(); + _UpdateSelection(); + } + *fOldSelection = *fSelection; + if (fCurrentPathPoint >= 0) { + _Select(fCurrentPathPoint, fShiftDown); + } + fCanvasView->BeginRectTracking(BRect(where, where), + B_TRACK_RECT_CORNER); + break; + } + + fTrackingStart = canvasWhere; + // remember the subpixel position + // so that MouseMoved() will work even before + // the integer position becomes different +// fCanvasView->ConvertToCanvas(where); + fLastCanvasPos = where; + + // the reason to exclude the select mode + // is that the BView rect tracking does not + // scroll the rect starting point along with us + // (since we're doing no real scrolling) +// if (fMode != SELECT_POINTS) +// fCanvasView->SetAutoScrolling(true); + + UpdateCursor(); + + return true; +} + +// MouseMoved +void +PathManipulator::MouseMoved(BPoint where) +{ + // TODO: translate from BView to canvas space + BPoint canvasWhere = where; + + // since the tablet is generating mouse moved messages + // even if only the pressure changes (and not the actual mouse position) + // we insert this additional check to prevent too much calculation + if (fLastCanvasPos == canvasWhere) + return; + + fLastCanvasPos = canvasWhere; + + if (fMode == CLOSE_PATH) { + // continue by moving the point + _SetMode(MOVE_POINT); + delete fChangePointCommand; + fChangePointCommand = new ChangePointCommand(fPath, + fCurrentPathPoint, + fSelection->Items(), + fSelection->CountItems()); + } + +// if (!fPrecise) { +// float offset = fmod(fOutlineWidth, 2.0) / 2.0; +// canvasWhere.point += BPoint(offset, offset); +// } + + switch (fMode) { + case ADD_POINT: + case INSERT_POINT: + case TOGGLE_SHARP: + // drag the "out" control point, mirror the "in" control point + fPath->SetPointOut(fCurrentPathPoint, canvasWhere, true); + break; + case MOVE_POINT: + // drag all three control points at once + fPath->SetPoint(fCurrentPathPoint, canvasWhere); + break; + case MOVE_POINT_IN: + // drag in control point + fPath->SetPointIn(fCurrentPathPoint, canvasWhere); + break; + case MOVE_POINT_OUT: + // drag out control point + fPath->SetPointOut(fCurrentPathPoint, canvasWhere); + break; + + case SELECT_POINTS: { + // change the selection + BRect r; + r.left = min_c(fTrackingStart.x, canvasWhere.x); + r.top = min_c(fTrackingStart.y, canvasWhere.y); + r.right = max_c(fTrackingStart.x, canvasWhere.x); + r.bottom = max_c(fTrackingStart.y, canvasWhere.y); + _Select(r); + break; + } + + case TRANSLATE_POINTS: { + BPoint offset = canvasWhere - fTrackingStart; + _Nudge(offset); + fTrackingStart = canvasWhere; + break; + } + } +} + +// MouseUp +Command* +PathManipulator::MouseUp() +{ + // prevent carrying out actions more than once by only + // doing it if "fMouseDown" is true at the point of + // entering this function + if (!fMouseDown) + return NULL; + fMouseDown = false; + + Command* command = NULL; + + switch (fMode) { + + case ADD_POINT: + command = fAddPointCommand; + fAddPointCommand = NULL; + _SetMode(MOVE_POINT_OUT); + break; + + case INSERT_POINT: + command = fInsertPointCommand; + fInsertPointCommand = NULL; + break; + + case SELECT_POINTS: + if (*fSelection != *fOldSelection) { +// command = new SelectPointsCommand(this, fPath, +// fOldSelection->Items(), +// fOldSelection->CountItems(), +// fSelection->Items(), +// fSelection->CountItems())); + } + fCanvasView->EndRectTracking(); + break; + + case TOGGLE_SHARP: + case TOGGLE_SHARP_IN: + case TOGGLE_SHARP_OUT: + case MOVE_POINT: + case MOVE_POINT_IN: + case MOVE_POINT_OUT: + case REMOVE_POINT_IN: + case REMOVE_POINT_OUT: + command = fChangePointCommand; + fChangePointCommand = NULL; + break; + + case TRANSLATE_POINTS: +// if (!fNudgeCommand) { + // select just the point that was clicked + *fOldSelection = *fSelection; + if (fCurrentPathPoint >= 0) { + _Select(fCurrentPathPoint, fShiftDown); + } + if (*fSelection != *fOldSelection) { +// command = new SelectPointsCommand(this, fPath, +// fOldSelection->Items(), +// fOldSelection->CountItems(), +// fSelection->Items(), +// fSelection->CountItems())); + } +// } else { +// _FinishNudging(); +// } + break; + } + + return command; +} + +// MouseOver +bool +PathManipulator::MouseOver(BPoint where) +{ + // TODO: translate from BView to canvas space + BPoint canvasWhere = where; + + // since the tablet is generating mouse moved messages + // even if only the pressure changes (and not the actual mouse position) + // we insert this additional check to prevent too much calculation + if (fMouseDown && fLastCanvasPos == canvasWhere) + return false; + + fLastCanvasPos = canvasWhere; + + // hit testing + // (use a subpixel mouse pos) +// fCanvasView->ConvertToCanvas(where); + _SetModeForMousePos(where); + + // TODO: always true? + return true; +} + +// DoubleClicked +bool +PathManipulator::DoubleClicked(BPoint where) +{ + return false; +} + +// Bounds +BRect +PathManipulator::Bounds() +{ + return _ControlPointRect(); +} + +// TrackingBounds +BRect +PathManipulator::TrackingBounds(BView* withinView) +{ + return withinView->Bounds(); +} + +// #pragma mark - + +enum { + MSG_SHAPE_TRANSFORM = 'strn', + MSG_SHAPE_REMOVE_POINTS = 'srmp', + MSG_UPDATE_SHAPE_UI = 'udsi', +}; + +// MessageReceived +bool +PathManipulator::MessageReceived(BMessage* message, Command** _command) +{ + bool result = true; + switch (message->what) { + case MSG_SHAPE_TRANSFORM: + if (!fSelection->IsEmpty()) + _SetMode(TRANSFORM_POINTS); + break; + case MSG_SHAPE_REMOVE_POINTS: + *_command = _Delete(); + break; + case B_SELECT_ALL: { + *fOldSelection = *fSelection; + fSelection->MakeEmpty(); + int32 count = fPath->CountPoints(); + for (int32 i = 0; i < count; i++) + fSelection->Add(i); + if (*fOldSelection != *fSelection) { +// *_command = new SelectPointsCommand(this, fPath, +// fOldSelection->Items(), +// fOldSelection->CountItems(), +// fSelection->Items(), +// fSelection->CountItems())); + count = fSelection->CountItems(); + int32 indices[count]; + memcpy(indices, fSelection->Items(), count * sizeof(int32)); + _Select(indices, count); + } + break; + } + default: + result = false; + break; + } + return result; +} + + +// ModifiersChanged +void +PathManipulator::ModifiersChanged(uint32 modifiers) +{ + fCommandDown = modifiers & B_COMMAND_KEY; + fOptionDown = modifiers & B_CONTROL_KEY; + fShiftDown = modifiers & B_SHIFT_KEY; + fAltDown = modifiers & B_OPTION_KEY; + + // reevaluate mode + if (!fMouseDown) + _SetModeForMousePos(fLastCanvasPos); +} + +// HandleKeyDown +bool +PathManipulator::HandleKeyDown(uint32 key, uint32 modifiers, Command** _command) +{ + bool result = true; + + float nudgeDist = 1.0; +// if (modifiers & B_SHIFT_KEY) +// nudgeDist /= fCanvasView->ZoomLevel(); + + switch (key) { + // commit + case B_RETURN: +// _Perform(); + break; + // cancel + case B_ESCAPE: + if (fFallBackMode == NEW_PATH) { + fFallBackMode = SELECT_POINTS; + _SetModeForMousePos(fLastCanvasPos); + } else +// _Cancel(); + break; + case 't': + case 'T': + if (!fSelection->IsEmpty()) + _SetMode(TRANSFORM_POINTS); + else + result = false; + break; + // nudging + case B_UP_ARROW: + _Nudge(BPoint(0.0, -nudgeDist)); + break; + case B_DOWN_ARROW: + _Nudge(BPoint(0.0, nudgeDist)); + break; + case B_LEFT_ARROW: + _Nudge(BPoint(-nudgeDist, 0.0)); + break; + case B_RIGHT_ARROW: + _Nudge(BPoint(nudgeDist, 0.0)); + break; + + case B_DELETE: + if (!fSelection->IsEmpty()) + *_command = _Delete(); + else + result = false; + break; + + default: + result = false; + } + return result; +} + +// HandleKeyUp +bool +PathManipulator::HandleKeyUp(uint32 key, uint32 modifiers, Command** _command) +{ + bool handled = true; + switch (key) { + // nudging + case B_UP_ARROW: + case B_DOWN_ARROW: + case B_LEFT_ARROW: + case B_RIGHT_ARROW: + _FinishNudging(); + break; + default: + handled = false; + break; + } + return handled; +} + +// UpdateCursor +void +PathManipulator::UpdateCursor() +{ + const uchar* cursorData; + switch (fMode) { + case ADD_POINT: + cursorData = kPathAddCursor; + break; + case INSERT_POINT: + cursorData = kPathInsertCursor; + break; + case MOVE_POINT: + case MOVE_POINT_IN: + case MOVE_POINT_OUT: + case TRANSLATE_POINTS: + cursorData = kPathMoveCursor; + break; + case CLOSE_PATH: + cursorData = kPathCloseCursor; + break; + case TOGGLE_SHARP: + case TOGGLE_SHARP_IN: + case TOGGLE_SHARP_OUT: + cursorData = kPathSharpCursor; + break; + case REMOVE_POINT: + case REMOVE_POINT_IN: + case REMOVE_POINT_OUT: + cursorData = kPathRemoveCursor; + break; + case SELECT_POINTS: + cursorData = kPathSelectCursor; + break; + + case SELECT_SUB_PATH: + cursorData = B_HAND_CURSOR; + break; + + case UNDEFINED: + default: + cursorData = kStopCursor; + break; + } + BCursor cursor(cursorData); + fCanvasView->SetViewCursor(&cursor, true); + fCanvasView->Sync(); +} + +// AttachedToView +void +PathManipulator::AttachedToView(BView* view) +{ + fCanvasView = dynamic_cast(view); +} + +// DetachedFromView +void +PathManipulator::DetachedFromView(BView* view) +{ + fCanvasView = NULL; +} + +// #pragma mark - + +// ObjectChanged +void +PathManipulator::ObjectChanged(const Observable* object) +{ + // TODO: refine VectorPath listener interface and + // implement more efficiently + BRect currentBounds = _ControlPointRect(); + _InvalidateCanvas(currentBounds | fPreviousBounds); + fPreviousBounds = currentBounds; + + // reevaluate mode + if (!fMouseDown) + _SetModeForMousePos(fLastCanvasPos); +} + +// #pragma mark - + +// ControlFlags +uint32 +PathManipulator::ControlFlags() const +{ + uint32 flags = 0; + +// flags |= SHAPE_UI_FLAGS_CAN_REVERSE_PATH; +// +// if (!fSelection->IsEmpty()) +// flags |= SHAPE_UI_FLAGS_HAS_SELECTION; +// if (fPath->CountPoints() > 1) +// flags |= SHAPE_UI_FLAGS_CAN_CLOSE_PATH; +// if (fPath->IsClosed()) +// flags |= SHAPE_UI_FLAGS_PATH_IS_CLOSED; + + return flags; +} + +// ReversePath +void +PathManipulator::ReversePath() +{ + int32 count = fSelection->CountItems(); + int32 pointCount = fPath->CountPoints(); + if (count > 0) { + Selection temp; + for (int32 i = 0; i < count; i++) { + temp.Add((pointCount - 1) - fSelection->IndexAt(i)); + } + *fSelection = temp; + } + fPath->Reverse(); +} + +// #pragma mark - + +// _SetMode +void +PathManipulator::_SetMode(uint32 mode) +{ + if (fMode != mode) { +//printf("switching mode: %s -> %s\n", string_for_mode(fMode), string_for_mode(mode)); + fMode = mode; + + if (BWindow* window = fCanvasView->Window()) { + window->PostMessage(MSG_UPDATE_SHAPE_UI); + } + UpdateCursor(); + } +} + +// _AddPoint +void +PathManipulator::_AddPoint(BPoint where) +{ + if (fPath->AddPoint(where)) { + fCurrentPathPoint = fPath->CountPoints() - 1; + + delete fAddPointCommand; + fAddPointCommand = new AddPointCommand(fPath, fCurrentPathPoint, + fSelection->Items(), + fSelection->CountItems()); + + _Select(fCurrentPathPoint, fShiftDown); + } +} + +// scale_point +BPoint +scale_point(BPoint a, BPoint b, float scale) +{ + return BPoint(a.x + (b.x - a.x) * scale, + a.y + (b.y - a.y) * scale); +} + +// _InsertPoint +void +PathManipulator::_InsertPoint(BPoint where, int32 index) +{ + double scale; + + BPoint point; + BPoint pointIn; + BPoint pointOut; + + BPoint previous; + BPoint previousOut; + BPoint next; + BPoint nextIn; + + if (fPath->FindBezierScale(index - 1, where, &scale) + && scale >= 0.0 && scale <= 1.0 + && fPath->GetPoint(index - 1, scale, point)) { + + fPath->GetPointAt(index - 1, previous); + fPath->GetPointOutAt(index - 1, previousOut); + fPath->GetPointAt(index, next); + fPath->GetPointInAt(index, nextIn); + + where = scale_point(previousOut, nextIn, scale); + + previousOut = scale_point(previous, previousOut, scale); + nextIn = scale_point(next, nextIn, 1 - scale); + pointIn = scale_point(previousOut, where, scale); + pointOut = scale_point(nextIn, where, 1 - scale); + + if (fPath->AddPoint(point, index)) { + + fPath->SetPointIn(index, pointIn); + fPath->SetPointOut(index, pointOut); + + delete fInsertPointCommand; + fInsertPointCommand = new InsertPointCommand(fPath, index, + fSelection->Items(), + fSelection->CountItems()); + + fPath->SetPointOut(index - 1, previousOut); + fPath->SetPointIn(index + 1, nextIn); + + fCurrentPathPoint = index; + _ShiftSelection(fCurrentPathPoint, 1); + _Select(fCurrentPathPoint, fShiftDown); + } + } +} + +// _SetInOutConnected +void +PathManipulator::_SetInOutConnected(int32 index, bool connected) +{ + fPath->SetInOutConnected(index, connected); +} + +// _SetSharp +void +PathManipulator::_SetSharp(int32 index) +{ + BPoint p; + fPath->GetPointAt(index, p); + fPath->SetPoint(index, p, p, p, true); +} + +// _RemoveSelection +void +PathManipulator::_RemoveSelection() +{ + int32 count = fSelection->CountItems(); + for (int32 i = 0; i < count; i++) { + if (!fPath->RemovePoint(fSelection->IndexAt(i) - i)) + break; + } + +// SetClosed(fPath->IsClosed() && fPath->CountPoints() > 1); + + fSelection->MakeEmpty(); +} + + +// _RemovePoint +void +PathManipulator::_RemovePoint(int32 index) +{ + if (fPath->RemovePoint(index)) { + _Deselect(index); + _ShiftSelection(index + 1, -1); + } +} + +// _RemovePointIn +void +PathManipulator::_RemovePointIn(int32 index) +{ + BPoint p; + if (fPath->GetPointAt(index, p)) { + fPath->SetPointIn(index, p); + fPath->SetInOutConnected(index, false); + } +} + +// _RemovePointOut +void +PathManipulator::_RemovePointOut(int32 index) +{ + BPoint p; + if (fPath->GetPointAt(index, p)) { + fPath->SetPointOut(index, p); + fPath->SetInOutConnected(index, false); + } +} + +// _Delete +Command* +PathManipulator::_Delete() +{ + Command* command = NULL; + if (!fMouseDown) { + if (fSelection->CountItems() == fPath->CountPoints()) { +// command = new RemovePathCommand(fPath); + } else { + command = new RemovePointsCommand(fPath, + fSelection->Items(), + fSelection->CountItems()); + _RemoveSelection(); + } + + _SetModeForMousePos(fLastCanvasPos); + } + + return command; +} + +// #pragma mark - + +// _Select +void +PathManipulator::_Select(BRect r) +{ + BPoint p; + int32 count = fPath->CountPoints(); + Selection temp; + for (int32 i = 0; i < count && fPath->GetPointAt(i, p); i++) { + if (r.Contains(p)) { + temp.Add(i); + } + } + // merge old and new selection + count = fOldSelection->CountItems(); + for (int32 i = 0; i < count; i++) { + int32 index = fOldSelection->IndexAt(i); + if (temp.Contains(index)) + temp.Remove(index); + else + temp.Add(index); + } + if (temp != *fSelection) { + *fSelection = temp; + _UpdateSelection(); + } +} + +// _Select +void +PathManipulator::_Select(int32 index, bool extend) +{ + if (!extend) + fSelection->MakeEmpty(); + if (fSelection->Contains(index)) + fSelection->Remove(index); + else + fSelection->Add(index); + // TODO: this can lead to unnecessary invalidation (maybe need to investigate) + _UpdateSelection(); +} + +// _Select +void +PathManipulator::_Select(const int32* indices, int32 count, bool extend) +{ + if (extend) { + for (int32 i = 0; i < count; i++) { + if (!fSelection->Contains(indices[i])) + fSelection->Add(indices[i]); + } + } else { + fSelection->MakeEmpty(); + for (int32 i = 0; i < count; i++) { + fSelection->Add(indices[i]); + } + } + _UpdateSelection(); +} + +// _Deselect +void +PathManipulator::_Deselect(int32 index) +{ + if (fSelection->Contains(index)) { + fSelection->Remove(index); + _UpdateSelection(); + } +} + +// _ShiftSelection +void +PathManipulator::_ShiftSelection(int32 startIndex, int32 direction) +{ + int32 count = fSelection->CountItems(); + if (count > 0) { + int32* selection = fSelection->Items(); + for (int32 i = 0; i < count; i++) { + if (selection[i] >= startIndex) { + selection[i] += direction; + } + } + } + _UpdateSelection(); +} + +// _IsSelected +bool +PathManipulator::_IsSelected(int32 index) const +{ + return fSelection->Contains(index); +} + +// #pragma mark - + +// _InvalidateCanvas +void +PathManipulator::_InvalidateCanvas(BRect rect) const +{ + // TODO: convert from canvas to view space + fCanvasView->Invalidate(rect); +} + +// _InvalidateHighlightPoints +void +PathManipulator::_InvalidateHighlightPoints(int32 newIndex, uint32 newMode) +{ + BRect oldRect = _ControlPointRect(fCurrentPathPoint, fMode); + BRect newRect = _ControlPointRect(newIndex, newMode); + if (oldRect.IsValid()) + _InvalidateCanvas(oldRect); + if (newRect.IsValid()) + _InvalidateCanvas(newRect); +} + +// _UpdateSelection +void +PathManipulator::_UpdateSelection() const +{ + _InvalidateCanvas(_ControlPointRect()); + if (BWindow* window = fCanvasView->Window()) { + window->PostMessage(MSG_UPDATE_SHAPE_UI); + } +} + +// _ControlPointRect +BRect +PathManipulator::_ControlPointRect() const +{ + BRect r = fPath->ControlPointBounds(); + r.InsetBy(-POINT_EXTEND, -POINT_EXTEND); + return r; +} + +// _ControlPointRect +BRect +PathManipulator::_ControlPointRect(int32 index, uint32 mode) const +{ + BRect rect(0.0, 0.0, -1.0, -1.0); + if (index >= 0) { + BPoint p, pIn, pOut; + fPath->GetPointsAt(index, p, pIn, pOut); + switch (mode) { + case MOVE_POINT: + case TOGGLE_SHARP: + case REMOVE_POINT: + case CLOSE_PATH: + rect.Set(p.x, p.y, p.x, p.y); + rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND); + break; + case MOVE_POINT_IN: + case TOGGLE_SHARP_IN: + case REMOVE_POINT_IN: + rect.Set(pIn.x, pIn.y, pIn.x, pIn.y); + rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND); + break; + case MOVE_POINT_OUT: + case TOGGLE_SHARP_OUT: + case REMOVE_POINT_OUT: + rect.Set(pOut.x, pOut.y, pOut.x, pOut.y); + rect.InsetBy(-CONTROL_POINT_EXTEND, -CONTROL_POINT_EXTEND); + break; + case SELECT_POINTS: + rect.Set(min4(p.x, pIn.x, pOut.x, pOut.x), + min4(p.y, pIn.y, pOut.y, pOut.y), + max4(p.x, pIn.x, pOut.x, pOut.x), + max4(p.y, pIn.y, pOut.y, pOut.y)); + rect.InsetBy(-POINT_EXTEND, -POINT_EXTEND); + break; + } + } + return rect; +} + +// #pragma mark - + +// _SetModeForMousePos +void +PathManipulator::_SetModeForMousePos(BPoint where) +{ + uint32 mode = UNDEFINED; + int32 index = -1; + + float zoomLevel = 1.0;//fCanvasView->ZoomLevel(); + + // see if we're close enough at a control point + BPoint point; + BPoint pointIn; + BPoint pointOut; + for (int32 i = 0; fPath->GetPointsAt(i, point, pointIn, pointOut) + && mode == UNDEFINED; i++) { + + float distM = point_point_distance(point, where) * zoomLevel; + float distIn = point_point_distance(pointIn, where) * zoomLevel; + float distOut = point_point_distance(pointOut, where) * zoomLevel; + + if (distM < MOVE_THRESHOLD) { + if (i == 0 && fClickToClose + && !fPath->IsClosed() && fPath->CountPoints() > 1) { + mode = fCommandDown ? TOGGLE_SHARP : + (fOptionDown ? REMOVE_POINT : CLOSE_PATH); + index = i; + } else { + mode = fCommandDown ? TOGGLE_SHARP : + (fOptionDown ? REMOVE_POINT : MOVE_POINT); + index = i; + } + } + if (distIn < distM && distIn < MOVE_THRESHOLD) { + mode = fCommandDown ? TOGGLE_SHARP_IN : + (fOptionDown ? REMOVE_POINT_IN : MOVE_POINT_IN); + index = i; + } + if (distOut < distIn && distOut < distM && distOut < MOVE_THRESHOLD) { + mode = fCommandDown ? TOGGLE_SHARP_OUT : + (fOptionDown ? REMOVE_POINT_OUT : MOVE_POINT_OUT); + index = i; + } + } + // selection mode overrides any other mode, + // but we need to check for it after we know + // the index of the point under the mouse (code above) + int32 pointCount = fPath->CountPoints(); + if (fShiftDown && pointCount > 0) { + mode = SELECT_POINTS; + } + + // see if user wants to start new sub path + if (fAltDown) { + mode = NEW_PATH; + index = -1; + } + + // see if we're close enough at a line + if (mode == UNDEFINED) { + float distance; + if (fPath->GetDistance(where, &distance, &index)) { + if (distance < (INSERT_DIST_THRESHOLD / zoomLevel)) { + mode = INSERT_POINT; + } + } else { + // restore index, since it was changed by call above + index = fCurrentPathPoint; + } + } + + // nope, still undefined mode, last fall back + if (mode == UNDEFINED) { + if (fFallBackMode == SELECT_POINTS) { + if (fPath->IsClosed() && pointCount > 0) { + mode = SELECT_POINTS; + index = -1; + } else { + mode = ADD_POINT; + index = pointCount - 1; + } + } else { + // user had clicked "New Path" icon + mode = fFallBackMode; + } + } + // switch mode if necessary + if (mode != fMode || index != fCurrentPathPoint) { + // invalidate path display (to highlight the respective point) + _InvalidateHighlightPoints(index, mode); + _SetMode(mode); + fCurrentPathPoint = index; + } +} + +// #pragma mark - + +// _Nudge +void +PathManipulator::_Nudge(BPoint direction) +{ + bigtime_t now = system_time(); + if (now - fLastNudgeTime > 500000) { + _FinishNudging(); + } + fLastNudgeTime = now; + fNudgeOffset += direction; + +// if (!fNudgeCommand) { +// +// bool fromSelection = !fSelection->IsEmpty(); +// +// int32 count = fromSelection ? fSelection->CountItems() +// : fPath->CountPoints(); +// int32* indices = new int32[count]; +// control_point* points = new control_point[count]; +// +// // init indices and points +// for (int32 i = 0; i < count; i++) { +// indices[i] = fromSelection ? fSelection->IndexAt(i) : i; +// fPath->GetPointsAt(indices[i], +// points[i].point, +// points[i].point_in, +// points[i].point_out, +// &points[i].connected); +// } +// +// fNudgeCommand = new NudgePointsCommand(this, fPath, +// indices, points, count); +// +// fNudgeCommand->SetNewTranslation(fNudgeOffset); +// fNudgeCommand->Redo(fCanvasView); +// +// delete[] indices; +// delete[] points; +// +// } else { +// fNudgeCommand->SetNewTranslation(fNudgeOffset); +// fNudgeCommand->Redo(fCanvasView); +// } + + if (!fMouseDown) + _SetModeForMousePos(fLastCanvasPos); +} + +// _FinishNudging +void +PathManipulator::_FinishNudging() +{ + fNudgeOffset = BPoint(0.0, 0.0); + +// if (fNudgeCommand) { +// fCanvasView->Perform(fNudgeCommand); +// fNudgeCommand = NULL; +// } +} + + diff --git a/src/apps/icon-o-matic/shape/PathManipulator.h b/src/apps/icon-o-matic/shape/PathManipulator.h new file mode 100644 index 0000000000..1113e479a2 --- /dev/null +++ b/src/apps/icon-o-matic/shape/PathManipulator.h @@ -0,0 +1,158 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#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 diff --git a/src/apps/icon-o-matic/shape/VectorPath.cpp b/src/apps/icon-o-matic/shape/VectorPath.cpp new file mode 100644 index 0000000000..4ce9e1eb76 --- /dev/null +++ b/src/apps/icon-o-matic/shape/VectorPath.cpp @@ -0,0 +1,855 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "VectorPath.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 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; +} diff --git a/src/apps/icon-o-matic/shape/VectorPath.h b/src/apps/icon-o-matic/shape/VectorPath.h new file mode 100644 index 0000000000..59f288268b --- /dev/null +++ b/src/apps/icon-o-matic/shape/VectorPath.h @@ -0,0 +1,138 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef VECTOR_PATH_H +#define VECTOR_PATH_H + +#include +#include + +#include + +#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 + diff --git a/src/apps/icon-o-matic/shape/commands/AddPointCommand.cpp b/src/apps/icon-o-matic/shape/commands/AddPointCommand.cpp new file mode 100644 index 0000000000..fae59e4711 --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/AddPointCommand.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "AddPointCommand.h" + +#include + +#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"; +} diff --git a/src/apps/icon-o-matic/shape/commands/AddPointCommand.h b/src/apps/icon-o-matic/shape/commands/AddPointCommand.h new file mode 100644 index 0000000000..963b061005 --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/AddPointCommand.h @@ -0,0 +1,40 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef ADD_POINT_COMMAND_H +#define ADD_POINT_COMMAND_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/shape/commands/ChangePointCommand.cpp b/src/apps/icon-o-matic/shape/commands/ChangePointCommand.cpp new file mode 100644 index 0000000000..dbf5b64f1e --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/ChangePointCommand.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "ChangePointCommand.h" + +#include +#include + +#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"; +} diff --git a/src/apps/icon-o-matic/shape/commands/ChangePointCommand.h b/src/apps/icon-o-matic/shape/commands/ChangePointCommand.h new file mode 100644 index 0000000000..0dc5ebcd20 --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/ChangePointCommand.h @@ -0,0 +1,42 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef CHANGE_POINT_COMMAND_H +#define CHANGE_POINT_COMMAND_H + +#include + +#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 diff --git a/src/apps/icon-o-matic/shape/commands/InsertPointCommand.cpp b/src/apps/icon-o-matic/shape/commands/InsertPointCommand.cpp new file mode 100644 index 0000000000..9bce776f38 --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/InsertPointCommand.cpp @@ -0,0 +1,128 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "InsertPointCommand.h" + +#include +#include + +#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"; +} + diff --git a/src/apps/icon-o-matic/shape/commands/InsertPointCommand.h b/src/apps/icon-o-matic/shape/commands/InsertPointCommand.h new file mode 100644 index 0000000000..a934da869d --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/InsertPointCommand.h @@ -0,0 +1,43 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef INSERT_POINT_COMMAND_H +#define INSERT_POINT_COMMAND_H + +#include + +#include "PathCommand.h" + +class InsertPointCommand : public PathCommand { + public: + InsertPointCommand(VectorPath* path, + int32 index, + const int32* selected, + int32 count); + virtual ~InsertPointCommand(); + + 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; + + BPoint fPreviousOut; + BPoint fNextIn; + + int32* fOldSelection; + int32 fOldSelectionCount; +}; + +#endif // INSERT_POINT_COMMAND_H diff --git a/src/apps/icon-o-matic/shape/commands/PathCommand.cpp b/src/apps/icon-o-matic/shape/commands/PathCommand.cpp new file mode 100644 index 0000000000..3368049adc --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/PathCommand.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "PathCommand.h" + +#include + +#include "VectorPath.h" + +// constructor +PathCommand::PathCommand(VectorPath* path) + : fPath(path) +{ +} + +// destructor +PathCommand::~PathCommand() +{ +} + +// InitCheck +status_t +PathCommand::InitCheck() +{ + return fPath ? B_OK : B_NO_INIT; +} + +// GetName +void +PathCommand::GetName(BString& name) +{ + + name << (""); +} + +// _Select +void +PathCommand::_Select(const int32* indices, int32 count, + bool extend) const +{ + // TODO: ... +} diff --git a/src/apps/icon-o-matic/shape/commands/PathCommand.h b/src/apps/icon-o-matic/shape/commands/PathCommand.h new file mode 100644 index 0000000000..aa44ecede0 --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/PathCommand.h @@ -0,0 +1,31 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef PATH_COMMAND_H +#define PATH_COMMAND_H + +#include "Command.h" + +class VectorPath; + +class PathCommand : public Command { + public: + PathCommand(VectorPath* path); + virtual ~PathCommand(); + + virtual status_t InitCheck(); + virtual void GetName(BString& name); + + protected: + void _Select(const int32* indices, int32 count, + bool extend = false) const; + + VectorPath* fPath; +}; + +#endif // PATH_COMMAND_H diff --git a/src/apps/icon-o-matic/shape/commands/RemovePointsCommand.cpp b/src/apps/icon-o-matic/shape/commands/RemovePointsCommand.cpp new file mode 100644 index 0000000000..c75bcf8ba6 --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/RemovePointsCommand.cpp @@ -0,0 +1,203 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#include "RemovePointsCommand.h" + +#include +#include + +#include "VectorPath.h" + +using std::nothrow; + +// constructor +// * when clicking a point in Remove mode, with other points selected +RemovePointsCommand::RemovePointsCommand(VectorPath* path, + int32 index, + const int32* selected, + int32 count) + : PathCommand(path), + fIndex(NULL), + fPoint(NULL), + fPointIn(NULL), + fPointOut(NULL), + fConnected(NULL), + fCount(0), + fOldSelection(NULL), + fOldSelectionCount(count) +{ + _Init(&index, 1, selected, count); +} + +// constructor +// * when hitting the Delete key, so the selected points are the +// same as the ones to be removed +RemovePointsCommand::RemovePointsCommand(VectorPath* path, + const int32* selected, + int32 count) + : PathCommand(path), + fIndex(NULL), + fPoint(NULL), + fPointIn(NULL), + fPointOut(NULL), + fConnected(NULL), + fCount(0), + fOldSelection(NULL), + fOldSelectionCount(count) +{ + _Init(selected, count, selected, count); +} + +// destructor +RemovePointsCommand::~RemovePointsCommand() +{ + delete[] fIndex; + delete[] fPoint; + delete[] fPointIn; + delete[] fPointOut; + delete[] fConnected; + delete[] fOldSelection; +} + +// InitCheck +status_t +RemovePointsCommand::InitCheck() +{ + status_t status = PathCommand::InitCheck(); + if (status < B_OK) + return status; + if (!fIndex || !fPoint || !fPointIn || !fPointOut || !fConnected) + status = B_NO_MEMORY; + return status; +} + +// Perform +status_t +RemovePointsCommand::Perform() +{ + // path points are already removed + return InitCheck(); +} + +// Undo +status_t +RemovePointsCommand::Undo() +{ + status_t status = InitCheck(); + if (status < B_OK) + return status; + + AutoNotificationSuspender _(fPath); + + // add points again at their respective index + for (int32 i = 0; i < fCount; i++) { + if (fPath->AddPoint(fPoint[i], fIndex[i])) { + fPath->SetPoint(fIndex[i], + fPoint[i], + fPointIn[i], + fPointOut[i], + fConnected[i]); + } else { + status = B_ERROR; + break; + } + } + + fPath->SetClosed(fWasClosed); + + if (status >= B_OK) { + // select the added points + _Select(fIndex, fCount); + } + + return status; +} + +// Redo +status_t +RemovePointsCommand::Redo() +{ + status_t status = InitCheck(); + if (status < B_OK) + return status; + + AutoNotificationSuspender _(fPath); + + // remove points + // the loop assumes the indices in the collection + // are increasing (removal at "index[i] - i" to account + // for items already removed) + for (int32 i = 0; i < fCount; i++) { + if (!fPath->RemovePoint(fIndex[i] - i)) { + status = B_ERROR; + break; + } + } + + fPath->SetClosed(fWasClosed && fPath->CountPoints() > 1); + + if (status >= B_OK) { + // restore selection + _Select(fOldSelection, fOldSelectionCount); + } + + return status; +} + +// GetName +void +RemovePointsCommand::GetName(BString& name) +{ +// if (fCount > 1) +// name << _GetString(REMOVE_CONTROL_POINTS, "Remove Control Points"); +// else +// name << _GetString(REMOVE_CONTROL_POINT, "Remove Control Point"); + if (fCount > 1) + name << "Remove Control Points"; + else + name << "Remove Control Point"; +} + +// _Init +void +RemovePointsCommand::_Init(const int32* indices, int32 count, + const int32* selection, int32 selectionCount) +{ + if (indices && count > 0) { + fIndex = new (nothrow) int32[count]; + fPoint = new (nothrow) BPoint[count]; + fPointIn = new (nothrow) BPoint[count]; + fPointOut = new (nothrow) BPoint[count]; + fConnected = new (nothrow) bool[count]; + fCount = count; + } + + if (InitCheck() < B_OK) + return; + + memcpy(fIndex, indices, count * sizeof(int32)); + for (int32 i = 0; i < count; i++) { + if (!fPath->GetPointsAt(fIndex[i], + fPoint[i], + fPointIn[i], + fPointOut[i], + &fConnected[i])) { + fPath = NULL; + break; + } + } + + if (fPath) + fWasClosed = fPath->IsClosed(); + + if (selectionCount > 0 && selection) { + fOldSelectionCount = selectionCount; + fOldSelection = new (nothrow) int32[selectionCount]; + memcpy(fOldSelection, selection, selectionCount * sizeof(int32)); + } +} diff --git a/src/apps/icon-o-matic/shape/commands/RemovePointsCommand.h b/src/apps/icon-o-matic/shape/commands/RemovePointsCommand.h new file mode 100644 index 0000000000..de9c5aedce --- /dev/null +++ b/src/apps/icon-o-matic/shape/commands/RemovePointsCommand.h @@ -0,0 +1,54 @@ +/* + * Copyright 2006, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Stephan Aßmus + */ + +#ifndef REMOVE_POINTS_COMMAND_H +#define REMOVE_POINTS_COMMAND_H + +#include "PathCommand.h" + +class RemovePointsCommand : public PathCommand { + public: + // for removing the point clicked on + // independent of selection + RemovePointsCommand(VectorPath* path, + int32 index, + const int32* selection, + int32 count); + // for removing the selection + RemovePointsCommand(VectorPath* path, + const int32* selection, + int32 count); + virtual ~RemovePointsCommand(); + + virtual status_t InitCheck(); + + virtual status_t Perform(); + virtual status_t Undo(); + virtual status_t Redo(); + + virtual void GetName(BString& name); + + private: + void _Init(const int32* indices, int32 count, + const int32* selection, + int32 selectionCount); + + int32* fIndex; + BPoint* fPoint; + BPoint* fPointIn; + BPoint* fPointOut; + bool* fConnected; + int32 fCount; + + bool fWasClosed; + + int32* fOldSelection; + int32 fOldSelectionCount; +}; + +#endif // REMOVE_POINTS_COMMAND_H