* 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
|
# source directories
|
||||||
local sourceDirs =
|
local sourceDirs =
|
||||||
supplier
|
supplier
|
||||||
|
support
|
||||||
;
|
;
|
||||||
|
|
||||||
local sourceDir ;
|
local sourceDir ;
|
||||||
@@ -21,8 +22,16 @@ Application MediaPlayer :
|
|||||||
MediaTrackVideoSupplier.cpp
|
MediaTrackVideoSupplier.cpp
|
||||||
VideoSupplier.cpp
|
VideoSupplier.cpp
|
||||||
|
|
||||||
# .
|
# support
|
||||||
AbstractLOAdapter.cpp
|
AbstractLOAdapter.cpp
|
||||||
|
Command.cpp
|
||||||
|
CommandStack.cpp
|
||||||
|
Listener.cpp
|
||||||
|
ListenerAdapter.cpp
|
||||||
|
Notifier.cpp
|
||||||
|
RWLocker.cpp
|
||||||
|
|
||||||
|
# .
|
||||||
Controller.cpp
|
Controller.cpp
|
||||||
ControllerObserver.cpp
|
ControllerObserver.cpp
|
||||||
ControllerView.cpp
|
ControllerView.cpp
|
||||||
|
|||||||
@@ -166,6 +166,15 @@ MainWin::MainWin()
|
|||||||
|
|
||||||
_SetupWindow();
|
_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();
|
Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -683,13 +692,6 @@ MainWin::ShowFileInfo()
|
|||||||
void
|
void
|
||||||
MainWin::ShowPlaylistWindow()
|
MainWin::ShowPlaylistWindow()
|
||||||
{
|
{
|
||||||
if (!fPlaylistWindow) {
|
|
||||||
fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 400, 500),
|
|
||||||
fPlaylist, fController);
|
|
||||||
fPlaylistWindow->Show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fPlaylistWindow->Lock()) {
|
if (fPlaylistWindow->Lock()) {
|
||||||
if (fPlaylistWindow->IsHidden())
|
if (fPlaylistWindow->IsHidden())
|
||||||
fPlaylistWindow->Show();
|
fPlaylistWindow->Show();
|
||||||
@@ -728,15 +730,18 @@ MainWin::VideoFormatChange(int width, int height, float width_scale, float heigh
|
|||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWin::_RefsReceived(BMessage *msg)
|
MainWin::_RefsReceived(BMessage* msg)
|
||||||
{
|
{
|
||||||
// the playlist ist replaced by dropped files
|
// the playlist ist replaced by dropped files
|
||||||
// or the dropped files are appended to the end
|
// or the dropped files are appended to the end
|
||||||
// of the existing playlist if <shift> is pressed
|
// of the existing playlist if <shift> is pressed
|
||||||
int32 appendIndex = modifiers() & B_SHIFT_KEY ?
|
int32 appendIndex = modifiers() & B_SHIFT_KEY ?
|
||||||
fPlaylist->CountItems() : -1;
|
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:
|
case B_SIMPLE_DATA:
|
||||||
if (message->HasRef("refs"))
|
if (message->HasRef("refs"))
|
||||||
_RefsReceived(message, fDropIndex);
|
RefsReceived(message, fDropIndex);
|
||||||
else if (message->HasPointer("list"))
|
else if (message->HasPointer("list"))
|
||||||
SimpleListView::MessageReceived(message);
|
SimpleListView::MessageReceived(message);
|
||||||
break;
|
break;
|
||||||
case B_REFS_RECEIVED:
|
case B_REFS_RECEIVED:
|
||||||
_RefsReceived(message, fDropIndex);
|
RefsReceived(message, fDropIndex);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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 -
|
// #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,
|
virtual void DrawListItem(BView* owner, int32 index,
|
||||||
BRect frame) const;
|
BRect frame) const;
|
||||||
|
|
||||||
|
// PlaylistListView
|
||||||
|
void RefsReceived(BMessage* message,
|
||||||
|
int32 appendIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _FullSync();
|
void _FullSync();
|
||||||
void _AddItem(const entry_ref& ref, int32 index);
|
void _AddItem(const entry_ref& ref, int32 index);
|
||||||
@@ -46,9 +50,6 @@ class PlaylistListView : public SimpleListView {
|
|||||||
void _SetCurrentPlaylistIndex(int32 index);
|
void _SetCurrentPlaylistIndex(int32 index);
|
||||||
void _SetPlaybackState(uint32 state);
|
void _SetPlaybackState(uint32 state);
|
||||||
|
|
||||||
void _RefsReceived(BMessage* message,
|
|
||||||
int32 dropIndex);
|
|
||||||
|
|
||||||
Playlist* fPlaylist;
|
Playlist* fPlaylist;
|
||||||
PlaylistObserver* fPlaylistObserver;
|
PlaylistObserver* fPlaylistObserver;
|
||||||
|
|
||||||
|
|||||||
@@ -7,25 +7,50 @@
|
|||||||
*/
|
*/
|
||||||
#include "PlaylistWindow.h"
|
#include "PlaylistWindow.h"
|
||||||
|
|
||||||
|
#include <Menu.h>
|
||||||
|
#include <MenuBar.h>
|
||||||
|
#include <MenuItem.h>
|
||||||
#include <ScrollBar.h>
|
#include <ScrollBar.h>
|
||||||
#include <ScrollView.h>
|
#include <ScrollView.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
#include "CommandStack.h"
|
||||||
#include "PlaylistListView.h"
|
#include "PlaylistListView.h"
|
||||||
|
#include "RWLocker.h"
|
||||||
|
|
||||||
|
|
||||||
PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist,
|
PlaylistWindow::PlaylistWindow(BRect frame, Playlist* playlist,
|
||||||
Controller* controller)
|
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();
|
frame = Bounds();
|
||||||
|
|
||||||
|
_CreateMenu(frame);
|
||||||
|
|
||||||
frame.right -= B_V_SCROLL_BAR_WIDTH;
|
frame.right -= B_V_SCROLL_BAR_WIDTH;
|
||||||
PlaylistListView* listView = new PlaylistListView(frame, playlist,
|
fListView = new PlaylistListView(frame, playlist, controller);
|
||||||
controller);
|
|
||||||
|
|
||||||
fTopView = new BScrollView("playlist scrollview",
|
BScrollView* scrollView = new BScrollView("playlist scrollview",
|
||||||
listView, B_FOLLOW_ALL, 0, false, true, B_NO_BORDER);
|
fListView, B_FOLLOW_ALL, 0, false, true, B_NO_BORDER);
|
||||||
|
|
||||||
|
fTopView = scrollView;
|
||||||
AddChild(fTopView);
|
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
|
// give listeners a chance to detach themselves
|
||||||
fTopView->RemoveSelf();
|
fTopView->RemoveSelf();
|
||||||
delete fTopView;
|
delete fTopView;
|
||||||
|
|
||||||
|
fCommandStack->RemoveListener(&fCommandStackListener);
|
||||||
|
delete fCommandStack;
|
||||||
|
delete fLocker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -53,9 +82,91 @@ PlaylistWindow::MessageReceived(BMessage* message)
|
|||||||
if (LastMouseMovedView())
|
if (LastMouseMovedView())
|
||||||
PostMessage(message, LastMouseMovedView());
|
PostMessage(message, LastMouseMovedView());
|
||||||
break;
|
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:
|
default:
|
||||||
BWindow::MessageReceived(message);
|
BWindow::MessageReceived(message);
|
||||||
break;
|
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 <Window.h>
|
||||||
|
|
||||||
|
#include "ListenerAdapter.h"
|
||||||
|
|
||||||
|
class BMenuBar;
|
||||||
|
class BMenuItem;
|
||||||
|
class CommandStack;
|
||||||
class Controller;
|
class Controller;
|
||||||
|
class Notifier;
|
||||||
class Playlist;
|
class Playlist;
|
||||||
|
class PlaylistListView;
|
||||||
|
class RWLocker;
|
||||||
|
|
||||||
class PlaylistWindow : public BWindow {
|
class PlaylistWindow : public BWindow {
|
||||||
public:
|
public:
|
||||||
@@ -25,7 +33,19 @@ class PlaylistWindow : public BWindow {
|
|||||||
virtual void MessageReceived(BMessage* message);
|
virtual void MessageReceived(BMessage* message);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void _CreateMenu(BRect& frame);
|
||||||
|
void _ObjectChanged(const Notifier* object);
|
||||||
|
|
||||||
|
Playlist* fPlaylist;
|
||||||
|
PlaylistListView* fListView;
|
||||||
|
|
||||||
BView* fTopView;
|
BView* fTopView;
|
||||||
|
BMenuItem* fUndoMI;
|
||||||
|
BMenuItem* fRedoMI;
|
||||||
|
|
||||||
|
RWLocker* fLocker;
|
||||||
|
CommandStack* fCommandStack;
|
||||||
|
ListenerAdapter fCommandStackListener;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PLAYLIST_WINDOW_H
|
#endif // PLAYLIST_WINDOW_H
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2006, Haiku.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Stephan Aßmus <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
Command::Command()
|
||||||
|
: fTimeStamp(system_time())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
Command::~Command()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitCheck
|
||||||
|
status_t
|
||||||
|
Command::InitCheck()
|
||||||
|
{
|
||||||
|
return B_NO_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform
|
||||||
|
status_t
|
||||||
|
Command::Perform()
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undo
|
||||||
|
status_t
|
||||||
|
Command::Undo()
|
||||||
|
{
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redo
|
||||||
|
status_t
|
||||||
|
Command::Redo()
|
||||||
|
{
|
||||||
|
return Perform();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName
|
||||||
|
void
|
||||||
|
Command::GetName(BString& name)
|
||||||
|
{
|
||||||
|
name << "Name of action goes here.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMAND_H
|
||||||
|
#define COMMAND_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
class BString;
|
||||||
|
|
||||||
|
class Command {
|
||||||
|
public:
|
||||||
|
Command();
|
||||||
|
virtual ~Command();
|
||||||
|
|
||||||
|
virtual status_t InitCheck();
|
||||||
|
|
||||||
|
virtual status_t Perform();
|
||||||
|
virtual status_t Undo();
|
||||||
|
virtual status_t Redo();
|
||||||
|
|
||||||
|
virtual void GetName(BString& name);
|
||||||
|
|
||||||
|
virtual bool 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 <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <[email protected]>
|
||||||
|
*/
|
||||||
|
#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 <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <[email protected]>
|
||||||
|
*/
|
||||||
|
#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 <[email protected]>
|
||||||
|
*/
|
||||||
|
#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 <[email protected]>
|
||||||
|
*/
|
||||||
|
#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 <[email protected]>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <[email protected]>
|
||||||
|
*/
|
||||||
|
#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