* Copyright 2006-2009, 2023, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus <superstippi@gmx.de>
* Zardshard
*/
#include "TransformerListView.h"
#include <new>
#include <stdio.h>
#include <Application.h>
#include <Catalog.h>
#include <ListItem.h>
#include <Locale.h>
#include <Menu.h>
#include <MenuItem.h>
#include <Mime.h>
#include <Message.h>
#include <Window.h>
#include "AddTransformersCommand.h"
#include "CommandStack.h"
#include "MoveTransformersCommand.h"
#include "ReferenceImage.h"
#include "RemoveTransformersCommand.h"
#include "Transformer.h"
#include "TransformerFactory.h"
#include "Observer.h"
#include "Selection.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Icon-O-Matic-TransformersList"
using std::nothrow;
class TransformerItem : public SimpleItem,
public Observer {
public:
TransformerItem(Transformer* t, TransformerListView* listView)
: SimpleItem(t->Name()),
transformer(NULL),
fListView(listView)
{
SetTransformer(t);
}
virtual ~TransformerItem()
{
SetTransformer(NULL);
}
virtual void ObjectChanged(const Observable* object)
{
UpdateText();
}
void SetTransformer(Transformer* t)
{
if (t == transformer)
return;
if (transformer) {
transformer->RemoveObserver(this);
transformer->ReleaseReference();
}
transformer = t;
if (transformer) {
transformer->AcquireReference();
transformer->AddObserver(this);
UpdateText();
}
}
void UpdateText()
{
SetText(transformer->Name());
if (fListView->LockLooper()) {
fListView->InvalidateItem(
fListView->IndexOf(this));
fListView->UnlockLooper();
}
}
Transformer* transformer;
private:
TransformerListView* fListView;
};
enum {
MSG_DRAG_TRANSFORMER = 'drgt',
MSG_ADD_TRANSFORMER = 'adtr',
MSG_REMOVE_TRANSFORMER = 'retr',
};
TransformerListView::TransformerListView(BRect frame, const char* name,
BMessage* message, BHandler* target)
: SimpleListView(frame, name,
NULL, B_MULTIPLE_SELECTION_LIST),
fMessage(message),
fShape(NULL),
fCommandStack(NULL)
{
SetDragCommand(MSG_DRAG_TRANSFORMER);
SetTarget(target);
}
TransformerListView::~TransformerListView()
{
_MakeEmpty();
delete fMessage;
if (fShape)
fShape->Transformers()->RemoveListener(this);
}
void
TransformerListView::Draw(BRect updateRect)
{
SimpleListView::Draw(updateRect);
if (fShape)
return;
const char* message1 = B_TRANSLATE_COMMENT("Click on a shape above",
"Empty transformers list - 1st line");
const char* message2 = B_TRANSLATE_COMMENT("to attach transformers.",
"Empty transformers list - 2nd line");
rgb_color lowColor = LowColor();
if (lowColor.red + lowColor.green + lowColor.blue > 128 * 3)
SetHighColor(tint_color(LowColor(), B_DARKEN_2_TINT));
else
SetHighColor(tint_color(LowColor(), B_LIGHTEN_2_TINT));
font_height fh;
GetFontHeight(&fh);
BRect b(Bounds());
BPoint middle;
float textHeight = (fh.ascent + fh.descent) * 1.5;
middle.y = (b.top + b.bottom - textHeight) / 2.0;
middle.x = (b.left + b.right - StringWidth(message1)) / 2.0;
DrawString(message1, middle);
middle.y += textHeight;
middle.x = (b.left + b.right - StringWidth(message2)) / 2.0;
DrawString(message2, middle);
}
void
TransformerListView::SelectionChanged()
{
if (CountSelectedItems() > 0)
SimpleListView::SelectionChanged();
if (!fSyncingToSelection) {
TransformerItem* item
= dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(0)));
if (fMessage) {
BMessage message(*fMessage);
message.AddPointer("transformer", item ? (void*)item->transformer : NULL);
Invoke(&message);
}
}
_UpdateMenu();
}
void
TransformerListView::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_ADD_TRANSFORMER: {
if (!fShape || !fCommandStack)
break;
uint32 type;
if (message->FindInt32("type", (int32*)&type) < B_OK)
break;
Transformer* transformer
= TransformerFactory::TransformerFor(type, fShape->VertexSource(), fShape);
if (!transformer)
break;
Transformer* transformers[1];
transformers[0] = transformer;
::Command* command = new (nothrow) AddTransformersCommand(
fShape->Transformers(), transformers, 1, fShape->Transformers()->CountItems());
if (!command)
delete transformer;
fCommandStack->Perform(command);
break;
}
case MSG_REMOVE_TRANSFORMER: {
RemoveSelected();
break;
}
default:
SimpleListView::MessageReceived(message);
break;
}
}
status_t
TransformerListView::ArchiveSelection(BMessage* into, bool deep) const
{
into->what = TransformerListView::kSelectionArchiveCode;
int32 count = CountSelectedItems();
for (int32 i = 0; i < count; i++) {
TransformerItem* item = dynamic_cast<TransformerItem*>(
ItemAt(CurrentSelection(i)));
if (item != NULL) {
BMessage archive;
if (item->transformer->Archive(&archive, deep) == B_OK)
into->AddMessage("transformer", &archive);
} else
return B_ERROR;
}
return B_OK;
}
bool
TransformerListView::InstantiateSelection(const BMessage* archive, int32 dropIndex)
{
if (archive->what != TransformerListView::kSelectionArchiveCode
|| fCommandStack == NULL || fShape == NULL)
return false;
int index = 0;
BList transformers;
while (true) {
BMessage transformerArchive;
if (archive->FindMessage("transformer", index, &transformerArchive) != B_OK)
break;
Transformer* transformer = TransformerFactory::TransformerFor(
&transformerArchive, fShape->VertexSource(), fShape);
if (transformer == NULL)
break;
if (!transformers.AddItem(transformer)) {
delete transformer;
break;
}
index++;
}
int32 count = transformers.CountItems();
if (count == 0)
return false;
AddTransformersCommand* command = new(nothrow) AddTransformersCommand(
fShape->Transformers(), (Transformer**)transformers.Items(), count, dropIndex);
if (command == NULL) {
for (int32 i = 0; i < count; i++)
delete (Transformer*)transformers.ItemAtFast(i);
return false;
}
fCommandStack->Perform(command);
return true;
}
void
TransformerListView::MoveItems(BList& items, int32 toIndex)
{
if (!fCommandStack || !fShape)
return;
int32 count = items.CountItems();
Transformer** transformers = new (nothrow) Transformer*[count];
if (!transformers)
return;
for (int32 i = 0; i < count; i++) {
TransformerItem* item
= dynamic_cast<TransformerItem*>((BListItem*)items.ItemAtFast(i));
transformers[i] = item ? item->transformer : NULL;
}
MoveTransformersCommand* command
= new (nothrow) MoveTransformersCommand(
fShape->Transformers(), transformers, count, toIndex);
if (!command) {
delete[] transformers;
return;
}
fCommandStack->Perform(command);
}
void
TransformerListView::CopyItems(BList& items, int32 toIndex)
{
MoveItems(items, toIndex);
}
void
TransformerListView::RemoveItemList(BList& items)
{
if (!fCommandStack || !fShape)
return;
int32 count = items.CountItems();
int32 indices[count];
for (int32 i = 0; i < count; i++)
indices[i] = IndexOf((BListItem*)items.ItemAtFast(i));
RemoveTransformersCommand* command
= new (nothrow) RemoveTransformersCommand(fShape->Transformers(), indices, count);
fCommandStack->Perform(command);
}
BListItem*
TransformerListView::CloneItem(int32 index) const
{
if (TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(index))) {
return new TransformerItem(item->transformer,
const_cast<TransformerListView*>(this));
}
return NULL;
}
int32
TransformerListView::IndexOfSelectable(Selectable* selectable) const
{
Transformer* transformer = dynamic_cast<Transformer*>(selectable);
if (!transformer)
return -1;
for (int32 i = 0;
TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i));
i++) {
if (item->transformer == transformer)
return i;
}
return -1;
}
Selectable*
TransformerListView::SelectableFor(BListItem* item) const
{
TransformerItem* transformerItem = dynamic_cast<TransformerItem*>(item);
if (transformerItem)
return transformerItem->transformer;
return NULL;
}
void
TransformerListView::ItemAdded(Transformer* transformer, int32 index)
{
if (!LockLooper())
return;
_AddTransformer(transformer, index);
UnlockLooper();
}
void
TransformerListView::ItemRemoved(Transformer* transformer)
{
if (!LockLooper())
return;
_RemoveTransformer(transformer);
UnlockLooper();
}
void
TransformerListView::SetMenu(BMenu* menu)
{
if (fMenu == menu)
return;
fMenu = menu;
if (fMenu == NULL)
return;
BMenu* addMenu = new BMenu(B_TRANSLATE("Add"));
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Transformation"
BMessage* message = new BMessage(MSG_ADD_TRANSFORMER);
message->AddInt32("type", CONTOUR_TRANSFORMER);
fContourItem = new BMenuItem(B_TRANSLATE("Contour"), message);
message = new BMessage(MSG_ADD_TRANSFORMER);
message->AddInt32("type", STROKE_TRANSFORMER);
fStrokeItem = new BMenuItem(B_TRANSLATE("Stroke"), message);
message = new BMessage(MSG_ADD_TRANSFORMER);
message->AddInt32("type", PERSPECTIVE_TRANSFORMER);
fPerspectiveItem = new BMenuItem(B_TRANSLATE("Perspective"), message);
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Icon-O-Matic-TransformersList"
addMenu->AddItem(fContourItem);
addMenu->AddItem(fStrokeItem);
addMenu->AddItem(fPerspectiveItem);
addMenu->SetTargetForItems(this);
fMenu->AddItem(addMenu);
fMenu->AddSeparatorItem();
fRemoveItem = new BMenuItem(B_TRANSLATE("Remove"), new BMessage(MSG_REMOVE_TRANSFORMER));
fMenu->AddItem(fRemoveItem);
fMenu->SetTargetForItems(this);
_UpdateMenu();
}
void
TransformerListView::SetShape(Shape* shape)
{
if (fShape == shape)
return;
if (fShape)
fShape->Transformers()->RemoveListener(this);
_MakeEmpty();
fShape = shape;
if (fShape) {
fShape->Transformers()->AddListener(this);
int32 count = fShape->Transformers()->CountItems();
for (int32 i = 0; i < count; i++)
_AddTransformer(fShape->Transformers()->ItemAtFast(i), i);
}
_UpdateMenu();
}
void
TransformerListView::SetCommandStack(CommandStack* stack)
{
fCommandStack = stack;
}
bool
TransformerListView::_AddTransformer(Transformer* transformer, int32 index)
{
if (transformer)
return AddItem(new TransformerItem(transformer, this), index);
return false;
}
bool
TransformerListView::_RemoveTransformer(Transformer* transformer)
{
TransformerItem* item = _ItemForTransformer(transformer);
if (item && RemoveItem(item)) {
delete item;
return true;
}
return false;
}
TransformerItem*
TransformerListView::_ItemForTransformer(Transformer* transformer) const
{
for (int32 i = 0;
TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i));
i++) {
if (item->transformer == transformer)
return item;
}
return NULL;
}
void
TransformerListView::_UpdateMenu()
{
fMenu->SetEnabled(fShape != NULL);
bool isReferenceImage = dynamic_cast<ReferenceImage*>(fShape) != NULL;
fContourItem->SetEnabled(!isReferenceImage);
fStrokeItem->SetEnabled(!isReferenceImage);
bool hasSelection = CurrentSelection(0) >= 0;
fRemoveItem->SetEnabled(hasSelection);
}