* implemented infrastructure for undo/redo of playlist operations,
everything regarding playlist modification is now tunneled through the playlist window git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21312 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -7,6 +7,7 @@ AddSubDirSupportedPlatforms libbe_test ;
|
||||
# source directories
|
||||
local sourceDirs =
|
||||
supplier
|
||||
support
|
||||
;
|
||||
|
||||
local sourceDir ;
|
||||
@@ -21,8 +22,16 @@ Application MediaPlayer :
|
||||
MediaTrackVideoSupplier.cpp
|
||||
VideoSupplier.cpp
|
||||
|
||||
# .
|
||||
# support
|
||||
AbstractLOAdapter.cpp
|
||||
Command.cpp
|
||||
CommandStack.cpp
|
||||
Listener.cpp
|
||||
ListenerAdapter.cpp
|
||||
Notifier.cpp
|
||||
RWLocker.cpp
|
||||
|
||||
# .
|
||||
Controller.cpp
|
||||
ControllerObserver.cpp
|
||||
ControllerView.cpp
|
||||
|
||||
@@ -166,6 +166,15 @@ MainWin::MainWin()
|
||||
|
||||
_SetupWindow();
|
||||
|
||||
// setup the playlist window now, we need to have it
|
||||
// running for the undo/redo playlist editing
|
||||
fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 400, 500),
|
||||
fPlaylist, fController);
|
||||
fPlaylistWindow->Hide();
|
||||
fPlaylistWindow->Show();
|
||||
// this makes sure the window thread is running without
|
||||
// showing the window just yet
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
@@ -683,13 +692,6 @@ MainWin::ShowFileInfo()
|
||||
void
|
||||
MainWin::ShowPlaylistWindow()
|
||||
{
|
||||
if (!fPlaylistWindow) {
|
||||
fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 400, 500),
|
||||
fPlaylist, fController);
|
||||
fPlaylistWindow->Show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (fPlaylistWindow->Lock()) {
|
||||
if (fPlaylistWindow->IsHidden())
|
||||
fPlaylistWindow->Show();
|
||||
@@ -728,15 +730,18 @@ MainWin::VideoFormatChange(int width, int height, float width_scale, float heigh
|
||||
|
||||
|
||||
void
|
||||
MainWin::_RefsReceived(BMessage *msg)
|
||||
MainWin::_RefsReceived(BMessage* msg)
|
||||
{
|
||||
// the playlist ist replaced by dropped files
|
||||
// or the dropped files are appended to the end
|
||||
// of the existing playlist if <shift> is pressed
|
||||
int32 appendIndex = modifiers() & B_SHIFT_KEY ?
|
||||
fPlaylist->CountItems() : -1;
|
||||
msg->AddInt32("append_index", appendIndex);
|
||||
|
||||
fPlaylist->AppendRefs(msg, appendIndex);
|
||||
// forward the message to the playlist window,
|
||||
// so that undo/redo is used for modifying the playlist
|
||||
fPlaylistWindow->PostMessage(msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -252,12 +252,12 @@ PlaylistListView::MessageReceived(BMessage* message)
|
||||
|
||||
case B_SIMPLE_DATA:
|
||||
if (message->HasRef("refs"))
|
||||
_RefsReceived(message, fDropIndex);
|
||||
RefsReceived(message, fDropIndex);
|
||||
else if (message->HasPointer("list"))
|
||||
SimpleListView::MessageReceived(message);
|
||||
break;
|
||||
case B_REFS_RECEIVED:
|
||||
_RefsReceived(message, fDropIndex);
|
||||
RefsReceived(message, fDropIndex);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -421,6 +421,18 @@ PlaylistListView::DrawListItem(BView* owner, int32 index, BRect frame) const
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaylistListView::RefsReceived(BMessage* message, int32 appendIndex)
|
||||
{
|
||||
if (!fPlaylist->Lock())
|
||||
return;
|
||||
|
||||
fPlaylist->AppendRefs(message, appendIndex);
|
||||
|
||||
fPlaylist->Unlock();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
@@ -489,14 +501,3 @@ PlaylistListView::_SetPlaybackState(uint32 state)
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PlaylistListView::_RefsReceived(BMessage* message, int32 dropIndex)
|
||||
{
|
||||
if (!fPlaylist->Lock())
|
||||
return;
|
||||
|
||||
fPlaylist->AppendRefs(message, dropIndex);
|
||||
|
||||
fPlaylist->Unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,10 @@ class PlaylistListView : public SimpleListView {
|
||||
virtual void DrawListItem(BView* owner, int32 index,
|
||||
BRect frame) const;
|
||||
|
||||
// PlaylistListView
|
||||
void RefsReceived(BMessage* message,
|
||||
int32 appendIndex);
|
||||
|
||||
private:
|
||||
void _FullSync();
|
||||
void _AddItem(const entry_ref& ref, int32 index);
|
||||
@@ -46,9 +50,6 @@ class PlaylistListView : public SimpleListView {
|
||||
void _SetCurrentPlaylistIndex(int32 index);
|
||||
void _SetPlaybackState(uint32 state);
|
||||
|
||||
void _RefsReceived(BMessage* message,
|
||||
int32 dropIndex);
|
||||
|
||||
Playlist* fPlaylist;
|
||||
PlaylistObserver* fPlaylistObserver;
|
||||
|
||||
|
||||
@@ -7,25 +7,50 @@
|
||||
*/
|
||||
#include "PlaylistWindow.h"
|
||||
|
||||
#include <Menu.h>
|
||||
#include <MenuBar.h>
|
||||
#include <MenuItem.h>
|
||||
#include <ScrollBar.h>
|
||||
#include <ScrollView.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "CommandStack.h"
|
||||
#include "PlaylistListView.h"
|
||||
#include "RWLocker.h"
|
||||
|
||||
|
||||
PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist,
|
||||
Controller* controller)
|
||||
: BWindow(frame, "Playlist", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS)
|
||||
: BWindow(frame, "Playlist", B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
|
||||
B_ASYNCHRONOUS_CONTROLS)
|
||||
|
||||
, fLocker(new RWLocker("command stack lock"))
|
||||
, fCommandStack(new CommandStack(fLocker))
|
||||
, fCommandStackListener(this)
|
||||
{
|
||||
frame = Bounds();
|
||||
|
||||
_CreateMenu(frame);
|
||||
|
||||
frame.right -= B_V_SCROLL_BAR_WIDTH;
|
||||
PlaylistListView* listView = new PlaylistListView(frame, playlist,
|
||||
controller);
|
||||
fListView = new PlaylistListView(frame, playlist, controller);
|
||||
|
||||
fTopView = new BScrollView("playlist scrollview",
|
||||
listView, B_FOLLOW_ALL, 0, false, true, B_NO_BORDER);
|
||||
BScrollView* scrollView = new BScrollView("playlist scrollview",
|
||||
fListView, B_FOLLOW_ALL, 0, false, true, B_NO_BORDER);
|
||||
|
||||
fTopView = scrollView;
|
||||
AddChild(fTopView);
|
||||
|
||||
// small visual tweak
|
||||
if (BScrollBar* scrollBar = scrollView->ScrollBar(B_VERTICAL)) {
|
||||
// make it so the frame of the menubar is also the frame of
|
||||
// the scroll bar (appears to be)
|
||||
scrollBar->MoveBy(0, -1);
|
||||
scrollBar->ResizeBy(0, 1);
|
||||
}
|
||||
|
||||
fCommandStack->AddListener(&fCommandStackListener);
|
||||
_ObjectChanged(fCommandStack);
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +59,10 @@ PlaylistWindow::~PlaylistWindow()
|
||||
// give listeners a chance to detach themselves
|
||||
fTopView->RemoveSelf();
|
||||
delete fTopView;
|
||||
|
||||
fCommandStack->RemoveListener(&fCommandStackListener);
|
||||
delete fCommandStack;
|
||||
delete fLocker;
|
||||
}
|
||||
|
||||
|
||||
@@ -53,9 +82,91 @@ PlaylistWindow::MessageReceived(BMessage* message)
|
||||
if (LastMouseMovedView())
|
||||
PostMessage(message, LastMouseMovedView());
|
||||
break;
|
||||
|
||||
case B_UNDO:
|
||||
fCommandStack->Undo();
|
||||
break;
|
||||
case B_REDO:
|
||||
fCommandStack->Redo();
|
||||
break;
|
||||
|
||||
case MSG_OBJECT_CHANGED: {
|
||||
Notifier* notifier;
|
||||
if (message->FindPointer("object", (void**)¬ifier) == B_OK)
|
||||
_ObjectChanged(notifier);
|
||||
break;
|
||||
}
|
||||
|
||||
case B_REFS_RECEIVED:
|
||||
case B_SIMPLE_DATA: {
|
||||
// only accept this message when it comes from the
|
||||
// player window, _not_ when it is dropped in this window
|
||||
// outside of the playlist!
|
||||
int32 appendIndex;
|
||||
if (message->FindInt32("append_index", &appendIndex) == B_OK) {
|
||||
fListView->RefsReceived(message, appendIndex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
BWindow::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
void
|
||||
PlaylistWindow::_CreateMenu(BRect& frame)
|
||||
{
|
||||
frame.bottom = 15;
|
||||
BMenuBar* menuBar = new BMenuBar(frame, "main menu");
|
||||
BMenu* fileMenu = new BMenu("Playlist");
|
||||
menuBar->AddItem(fileMenu);
|
||||
// TODO add some items: "Open", "Save", "Make Empty"...
|
||||
|
||||
BMenu* editMenu = new BMenu("Edit");
|
||||
BMessage* message = new BMessage(B_UNDO);
|
||||
fUndoMI = new BMenuItem("Undo", message);
|
||||
editMenu->AddItem(fUndoMI);
|
||||
message = new BMessage(B_REDO);
|
||||
fRedoMI = new BMenuItem("Undo", message);
|
||||
editMenu->AddItem(fRedoMI);
|
||||
menuBar->AddItem(editMenu);
|
||||
|
||||
AddChild(menuBar);
|
||||
fileMenu->SetTargetForItems(this);
|
||||
editMenu->SetTargetForItems(this);
|
||||
|
||||
menuBar->ResizeToPreferred();
|
||||
frame = Bounds();
|
||||
frame.top = menuBar->Frame().bottom + 1;
|
||||
}
|
||||
|
||||
|
||||
// _ObjectChanged
|
||||
void
|
||||
PlaylistWindow::_ObjectChanged(const Notifier* object)
|
||||
{
|
||||
if (object == fCommandStack) {
|
||||
// relable Undo item and update enabled status
|
||||
BString label("Undo");
|
||||
fUndoMI->SetEnabled(fCommandStack->GetUndoName(label));
|
||||
if (fUndoMI->IsEnabled())
|
||||
fUndoMI->SetLabel(label.String());
|
||||
else
|
||||
fUndoMI->SetLabel("<nothing to undo>");
|
||||
|
||||
// relable Redo item and update enabled status
|
||||
label.SetTo("Redo");
|
||||
fRedoMI->SetEnabled(fCommandStack->GetRedoName(label));
|
||||
if (fRedoMI->IsEnabled())
|
||||
fRedoMI->SetLabel(label.String());
|
||||
else
|
||||
fRedoMI->SetLabel("<nothing to redo>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,16 @@
|
||||
|
||||
#include <Window.h>
|
||||
|
||||
#include "ListenerAdapter.h"
|
||||
|
||||
class BMenuBar;
|
||||
class BMenuItem;
|
||||
class CommandStack;
|
||||
class Controller;
|
||||
class Notifier;
|
||||
class Playlist;
|
||||
class PlaylistListView;
|
||||
class RWLocker;
|
||||
|
||||
class PlaylistWindow : public BWindow {
|
||||
public:
|
||||
@@ -25,7 +33,19 @@ class PlaylistWindow : public BWindow {
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
private:
|
||||
void _CreateMenu(BRect& frame);
|
||||
void _ObjectChanged(const Notifier* object);
|
||||
|
||||
Playlist* fPlaylist;
|
||||
PlaylistListView* fListView;
|
||||
|
||||
BView* fTopView;
|
||||
BMenuItem* fUndoMI;
|
||||
BMenuItem* fRedoMI;
|
||||
|
||||
RWLocker* fLocker;
|
||||
CommandStack* fCommandStack;
|
||||
ListenerAdapter fCommandStackListener;
|
||||
};
|
||||
|
||||
#endif // PLAYLIST_WINDOW_H
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2006, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#include "Command.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
// constructor
|
||||
Command::Command()
|
||||
: fTimeStamp(system_time())
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
Command::~Command()
|
||||
{
|
||||
}
|
||||
|
||||
// InitCheck
|
||||
status_t
|
||||
Command::InitCheck()
|
||||
{
|
||||
return B_NO_INIT;
|
||||
}
|
||||
|
||||
// Perform
|
||||
status_t
|
||||
Command::Perform()
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// Undo
|
||||
status_t
|
||||
Command::Undo()
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// Redo
|
||||
status_t
|
||||
Command::Redo()
|
||||
{
|
||||
return Perform();
|
||||
}
|
||||
|
||||
// GetName
|
||||
void
|
||||
Command::GetName(BString& name)
|
||||
{
|
||||
name << "Name of action goes here.";
|
||||
}
|
||||
|
||||
// UndoesPrevious
|
||||
bool
|
||||
Command::UndoesPrevious(const Command* previous)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// CombineWithNext
|
||||
bool
|
||||
Command::CombineWithNext(const Command* next)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// CombineWithPrevious
|
||||
bool
|
||||
Command::CombineWithPrevious(const Command* previous)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// _GetString
|
||||
const char*
|
||||
Command::_GetString(uint32 key, const char* defaultString) const
|
||||
{
|
||||
// if (LanguageManager* manager = LanguageManager::Default())
|
||||
// return manager->GetString(key, defaultString);
|
||||
// else
|
||||
return defaultString;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2006, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#ifndef COMMAND_H
|
||||
#define COMMAND_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
#include <String.h>
|
||||
|
||||
class BString;
|
||||
|
||||
class Command {
|
||||
public:
|
||||
Command();
|
||||
virtual ~Command();
|
||||
|
||||
virtual status_t InitCheck();
|
||||
|
||||
virtual status_t Perform();
|
||||
virtual status_t Undo();
|
||||
virtual status_t Redo();
|
||||
|
||||
virtual void GetName(BString& name);
|
||||
|
||||
virtual bool UndoesPrevious(const Command* previous);
|
||||
virtual bool CombineWithNext(const Command* next);
|
||||
virtual bool CombineWithPrevious(const Command* previous);
|
||||
|
||||
protected:
|
||||
const char* _GetString(uint32 key,
|
||||
const char* defaultString) const;
|
||||
|
||||
bigtime_t fTimeStamp;
|
||||
};
|
||||
|
||||
#endif // COMMAND_H
|
||||
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2006-2007, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#include "CommandStack.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Locker.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "Command.h"
|
||||
#include "RWLocker.h"
|
||||
|
||||
// constructor
|
||||
CommandStack::CommandStack(RWLocker* locker)
|
||||
: Notifier()
|
||||
, fLocker(locker)
|
||||
, fSavedCommand(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
CommandStack::~CommandStack()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
// Perform
|
||||
status_t
|
||||
CommandStack::Perform(Command* command)
|
||||
{
|
||||
if (!fLocker->WriteLock())
|
||||
return B_ERROR;
|
||||
|
||||
status_t ret = command ? B_OK : B_BAD_VALUE;
|
||||
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;
|
||||
}
|
||||
fLocker->WriteUnlock();
|
||||
|
||||
Notify();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Undo
|
||||
status_t
|
||||
CommandStack::Undo()
|
||||
{
|
||||
if (!fLocker->WriteLock())
|
||||
return B_ERROR;
|
||||
|
||||
status_t status = B_ERROR;
|
||||
if (!fUndoHistory.empty()) {
|
||||
Command* command = fUndoHistory.top();
|
||||
fUndoHistory.pop();
|
||||
status = command->Undo();
|
||||
if (status == B_OK)
|
||||
fRedoHistory.push(command);
|
||||
else
|
||||
fUndoHistory.push(command);
|
||||
}
|
||||
fLocker->WriteUnlock();
|
||||
|
||||
Notify();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Redo
|
||||
status_t
|
||||
CommandStack::Redo()
|
||||
{
|
||||
if (!fLocker->WriteLock())
|
||||
return B_ERROR;
|
||||
|
||||
status_t status = B_ERROR;
|
||||
if (!fRedoHistory.empty()) {
|
||||
Command* command = fRedoHistory.top();
|
||||
fRedoHistory.pop();
|
||||
status = command->Redo();
|
||||
if (status == B_OK)
|
||||
fUndoHistory.push(command);
|
||||
else
|
||||
fRedoHistory.push(command);
|
||||
}
|
||||
fLocker->WriteUnlock();
|
||||
|
||||
Notify();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// UndoName
|
||||
bool
|
||||
CommandStack::GetUndoName(BString& name)
|
||||
{
|
||||
bool success = false;
|
||||
if (fLocker->ReadLock()) {
|
||||
if (!fUndoHistory.empty()) {
|
||||
name << " ";
|
||||
fUndoHistory.top()->GetName(name);
|
||||
success = true;
|
||||
}
|
||||
fLocker->ReadUnlock();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// RedoName
|
||||
bool
|
||||
CommandStack::GetRedoName(BString& name)
|
||||
{
|
||||
bool success = false;
|
||||
if (fLocker->ReadLock()) {
|
||||
if (!fRedoHistory.empty()) {
|
||||
name << " ";
|
||||
fRedoHistory.top()->GetName(name);
|
||||
success = true;
|
||||
}
|
||||
fLocker->ReadUnlock();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
// Clear
|
||||
void
|
||||
CommandStack::Clear()
|
||||
{
|
||||
if (fLocker->WriteLock()) {
|
||||
while (!fUndoHistory.empty()) {
|
||||
delete fUndoHistory.top();
|
||||
fUndoHistory.pop();
|
||||
}
|
||||
while (!fRedoHistory.empty()) {
|
||||
delete fRedoHistory.top();
|
||||
fRedoHistory.pop();
|
||||
}
|
||||
fLocker->WriteUnlock();
|
||||
}
|
||||
|
||||
Notify();
|
||||
}
|
||||
|
||||
// Save
|
||||
void
|
||||
CommandStack::Save()
|
||||
{
|
||||
if (fLocker->WriteLock()) {
|
||||
if (!fUndoHistory.empty())
|
||||
fSavedCommand = fUndoHistory.top();
|
||||
fLocker->WriteUnlock();
|
||||
}
|
||||
|
||||
Notify();
|
||||
}
|
||||
|
||||
// IsSaved
|
||||
bool
|
||||
CommandStack::IsSaved()
|
||||
{
|
||||
bool saved = false;
|
||||
if (fLocker->ReadLock()) {
|
||||
saved = fUndoHistory.empty();
|
||||
if (fSavedCommand && !saved) {
|
||||
if (fSavedCommand == fUndoHistory.top())
|
||||
saved = true;
|
||||
}
|
||||
fLocker->ReadUnlock();
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// _AddCommand
|
||||
status_t
|
||||
CommandStack::_AddCommand(Command* command)
|
||||
{
|
||||
status_t status = B_OK;
|
||||
|
||||
bool add = true;
|
||||
if (!fUndoHistory.empty()) {
|
||||
// try to collapse commands to a single command
|
||||
// or remove this and the previous command if
|
||||
// they reverse each other
|
||||
if (Command* top = fUndoHistory.top()) {
|
||||
if (command->UndoesPrevious(top)) {
|
||||
add = false;
|
||||
fUndoHistory.pop();
|
||||
delete top;
|
||||
delete command;
|
||||
} else if (top->CombineWithNext(command)) {
|
||||
add = false;
|
||||
delete command;
|
||||
// after collapsing, the command might
|
||||
// have changed it's mind about InitCheck()
|
||||
// (the commands reversed each other)
|
||||
if (top->InitCheck() < B_OK) {
|
||||
fUndoHistory.pop();
|
||||
delete top;
|
||||
}
|
||||
} else if (command->CombineWithPrevious(top)) {
|
||||
fUndoHistory.pop();
|
||||
delete top;
|
||||
// after collapsing, the command might
|
||||
// have changed it's mind about InitCheck()
|
||||
// (the commands reversed each other)
|
||||
if (command->InitCheck() < B_OK) {
|
||||
delete command;
|
||||
add = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (add) {
|
||||
try {
|
||||
fUndoHistory.push(command);
|
||||
} catch (...) {
|
||||
status = B_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == B_OK) {
|
||||
// the redo stack needs to be empty
|
||||
// as soon as a command was added (also in case of collapsing)
|
||||
while (!fRedoHistory.empty()) {
|
||||
delete fRedoHistory.top();
|
||||
fRedoHistory.pop();
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2006-2007, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#ifndef COMMAND_STACK_H
|
||||
#define COMMAND_STACK_H
|
||||
|
||||
#include <stack.h>
|
||||
|
||||
#include "Notifier.h"
|
||||
|
||||
class BString;
|
||||
class RWLocker;
|
||||
class Command;
|
||||
|
||||
class CommandStack : public Notifier {
|
||||
public:
|
||||
CommandStack(RWLocker* locker);
|
||||
virtual ~CommandStack();
|
||||
|
||||
status_t Perform(Command* command);
|
||||
|
||||
status_t Undo();
|
||||
status_t Redo();
|
||||
|
||||
bool GetUndoName(BString& name);
|
||||
bool GetRedoName(BString& name);
|
||||
|
||||
void Clear();
|
||||
void Save();
|
||||
bool IsSaved();
|
||||
|
||||
private:
|
||||
status_t _AddCommand(Command* command);
|
||||
|
||||
RWLocker* fLocker;
|
||||
|
||||
typedef stack<Command*> command_stack;
|
||||
|
||||
command_stack fUndoHistory;
|
||||
command_stack fRedoHistory;
|
||||
Command* fSavedCommand;
|
||||
};
|
||||
|
||||
#endif // COMMAND_STACK_H
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2006, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#include "Listener.h"
|
||||
|
||||
Listener::Listener()
|
||||
{
|
||||
}
|
||||
|
||||
Listener::~Listener()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2006-2007, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#ifndef LISTENER_H
|
||||
#define LISTENER_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
class Notifier;
|
||||
|
||||
class Listener {
|
||||
public:
|
||||
Listener();
|
||||
virtual ~Listener();
|
||||
|
||||
virtual void ObjectChanged(const Notifier* object) = 0;
|
||||
};
|
||||
|
||||
#endif // LISTENER_H
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2006, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#include "ListenerAdapter.h"
|
||||
|
||||
#include <Message.h>
|
||||
|
||||
// constructor
|
||||
ListenerAdapter::ListenerAdapter(BHandler* handler)
|
||||
: Listener()
|
||||
, AbstractLOAdapter(handler)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
ListenerAdapter::~ListenerAdapter()
|
||||
{
|
||||
}
|
||||
|
||||
// ObjectChanged
|
||||
void
|
||||
ListenerAdapter::ObjectChanged(const Notifier* object)
|
||||
{
|
||||
BMessage message(MSG_OBJECT_CHANGED);
|
||||
message.AddPointer("object", object);
|
||||
|
||||
DeliverMessage(message);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2007, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#ifndef LISTENER_ADAPTER_H
|
||||
#define LISTENER_ADAPTER_H
|
||||
|
||||
#include "AbstractLOAdapter.h"
|
||||
#include "Listener.h"
|
||||
|
||||
enum {
|
||||
MSG_OBJECT_CHANGED = 'obch'
|
||||
};
|
||||
|
||||
class ListenerAdapter : public Listener, public AbstractLOAdapter {
|
||||
public:
|
||||
ListenerAdapter(BHandler* handler);
|
||||
virtual ~ListenerAdapter();
|
||||
|
||||
virtual void ObjectChanged(const Notifier* object);
|
||||
};
|
||||
|
||||
#endif // LISTENER_ADAPTER_H
|
||||
@@ -0,0 +1,3 @@
|
||||
This folder contains some generic classes. The same code is used in Icon-O-Matic btw. I should move my lazy butt and place this code somewhere common. Ideas?
|
||||
|
||||
-Stephan
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2006-2007, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
|
||||
#include "Notifier.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include "Listener.h"
|
||||
|
||||
// constructor
|
||||
Notifier::Notifier()
|
||||
: fListeners(2),
|
||||
fSuspended(0),
|
||||
fPendingNotifications(false)
|
||||
{
|
||||
}
|
||||
|
||||
// destructor
|
||||
Notifier::~Notifier()
|
||||
{
|
||||
if (fListeners.CountItems() > 0) {
|
||||
char message[256];
|
||||
Listener* o = (Listener*)fListeners.ItemAt(0);
|
||||
sprintf(message, "Notifier::~Notifier() - %ld "
|
||||
"listeners still watching, first: %s\n",
|
||||
fListeners.CountItems(), typeid(*o).name());
|
||||
debugger(message);
|
||||
}
|
||||
}
|
||||
|
||||
// AddListener
|
||||
bool
|
||||
Notifier::AddListener(Listener* listener)
|
||||
{
|
||||
if (listener && !fListeners.HasItem((void*)listener)) {
|
||||
return fListeners.AddItem((void*)listener);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// RemoveListener
|
||||
bool
|
||||
Notifier::RemoveListener(Listener* listener)
|
||||
{
|
||||
return fListeners.RemoveItem((void*)listener);
|
||||
}
|
||||
|
||||
// CountListeners
|
||||
int32
|
||||
Notifier::CountListeners() const
|
||||
{
|
||||
return fListeners.CountItems();
|
||||
}
|
||||
|
||||
// ListenerAtFast
|
||||
Listener*
|
||||
Notifier::ListenerAtFast(int32 index) const
|
||||
{
|
||||
return (Listener*)fListeners.ItemAtFast(index);
|
||||
}
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
// Notify
|
||||
void
|
||||
Notifier::Notify() const
|
||||
{
|
||||
if (!fSuspended) {
|
||||
BList observers(fListeners);
|
||||
int32 count = observers.CountItems();
|
||||
for (int32 i = 0; i < count; i++)
|
||||
((Listener*)observers.ItemAtFast(i))->ObjectChanged(this);
|
||||
fPendingNotifications = false;
|
||||
} else {
|
||||
fPendingNotifications = true;
|
||||
}
|
||||
}
|
||||
|
||||
// SuspendNotifications
|
||||
void
|
||||
Notifier::SuspendNotifications(bool suspend)
|
||||
{
|
||||
if (suspend)
|
||||
fSuspended++;
|
||||
else
|
||||
fSuspended--;
|
||||
|
||||
if (fSuspended < 0) {
|
||||
fprintf(stderr, "Notifier::SuspendNotifications(false) - "
|
||||
"error: suspend level below zero!\n");
|
||||
fSuspended = 0;
|
||||
}
|
||||
|
||||
if (!fSuspended && fPendingNotifications)
|
||||
Notify();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2006-2007, Haiku.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Stephan Aßmus <superstippi@gmx.de>
|
||||
*/
|
||||
#ifndef NOTIFIER_H
|
||||
#define NOTIFIER_H
|
||||
|
||||
#include <List.h>
|
||||
|
||||
class Listener;
|
||||
|
||||
class Notifier {
|
||||
public:
|
||||
Notifier();
|
||||
virtual ~Notifier();
|
||||
|
||||
bool AddListener(Listener* listener);
|
||||
bool RemoveListener(Listener* listener);
|
||||
|
||||
int32 CountListeners() const;
|
||||
Listener* ListenerAtFast(int32 index) const;
|
||||
|
||||
void Notify() const;
|
||||
|
||||
void SuspendNotifications(bool suspend);
|
||||
|
||||
bool HasPendingNotifications() const
|
||||
{ return fPendingNotifications; }
|
||||
|
||||
private:
|
||||
BList fListeners;
|
||||
|
||||
int32 fSuspended;
|
||||
mutable bool fPendingNotifications;
|
||||
};
|
||||
|
||||
class AutoNotificationSuspender {
|
||||
public:
|
||||
AutoNotificationSuspender(Notifier* object)
|
||||
: fObject(object)
|
||||
{
|
||||
fObject->SuspendNotifications(true);
|
||||
}
|
||||
|
||||
virtual ~AutoNotificationSuspender()
|
||||
{
|
||||
fObject->SuspendNotifications(false);
|
||||
}
|
||||
private:
|
||||
Notifier* fObject;
|
||||
};
|
||||
|
||||
#endif // NOTIFIER_H
|
||||
Reference in New Issue
Block a user