1/*
2 * Copyright 2006-2009, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan Aßmus <superstippi@gmx.de>
7 */
8
9#include "TransformerListView.h"
10
11#include <new>
12#include <stdio.h>
13
14#include <Application.h>
15#include <Catalog.h>
16#include <ListItem.h>
17#include <Locale.h>
18#include <Menu.h>
19#include <MenuItem.h>
20#include <Mime.h>
21#include <Message.h>
22#include <Window.h>
23
24#include "AddTransformersCommand.h"
25#include "CommandStack.h"
26#include "MoveTransformersCommand.h"
27#include "RemoveTransformersCommand.h"
28#include "Transformer.h"
29#include "TransformerFactory.h"
30#include "Observer.h"
31#include "Selection.h"
32
33
34#undef B_TRANSLATION_CONTEXT
35#define B_TRANSLATION_CONTEXT "Icon-O-Matic-TransformersList"
36
37
38using std::nothrow;
39
40class TransformerItem : public SimpleItem,
41						public Observer {
42 public:
43					TransformerItem(Transformer* t,
44									TransformerListView* listView)
45						: SimpleItem(t->Name()),
46						  transformer(NULL),
47						  fListView(listView)
48					{
49						SetTransformer(t);
50					}
51
52	virtual			~TransformerItem()
53					{
54						SetTransformer(NULL);
55					}
56
57	// Observer interface
58	virtual	void	ObjectChanged(const Observable* object)
59					{
60						UpdateText();
61					}
62
63	// TransformerItem
64			void	SetTransformer(Transformer* t)
65					{
66						if (t == transformer)
67							return;
68
69						if (transformer) {
70							transformer->RemoveObserver(this);
71							transformer->Release();
72						}
73
74						transformer = t;
75
76						if (transformer) {
77							transformer->Acquire();
78							transformer->AddObserver(this);
79							UpdateText();
80						}
81					}
82			void	UpdateText()
83					{
84						SetText(transformer->Name());
85						// :-/
86						if (fListView->LockLooper()) {
87							fListView->InvalidateItem(
88								fListView->IndexOf(this));
89							fListView->UnlockLooper();
90						}
91					}
92
93	Transformer* 	transformer;
94 private:
95	TransformerListView* fListView;
96};
97
98// #pragma mark -
99
100enum {
101	MSG_DRAG_TRANSFORMER			= 'drgt',
102	MSG_ADD_TRANSFORMER				= 'adtr',
103};
104
105// constructor
106TransformerListView::TransformerListView(BRect frame, const char* name,
107										 BMessage* message, BHandler* target)
108	: SimpleListView(frame, name,
109					 NULL, B_MULTIPLE_SELECTION_LIST),
110	  fMessage(message),
111	  fShape(NULL),
112	  fCommandStack(NULL)
113{
114	SetDragCommand(MSG_DRAG_TRANSFORMER);
115	SetTarget(target);
116}
117
118// destructor
119TransformerListView::~TransformerListView()
120{
121	_MakeEmpty();
122	delete fMessage;
123
124	if (fShape)
125		fShape->RemoveListener(this);
126}
127
128// Draw
129void
130TransformerListView::Draw(BRect updateRect)
131{
132	SimpleListView::Draw(updateRect);
133
134	if (fShape)
135		return;
136
137	// display helpful messages
138	const char* message1 = B_TRANSLATE_CONTEXT("Click on a shape above",
139		"Empty transformers list - 1st line");
140	const char* message2 = B_TRANSLATE_CONTEXT("to attach transformers.",
141		"Empty transformers list - 2nd line");
142
143	SetHighColor(tint_color(LowColor(), B_DARKEN_2_TINT));
144	font_height fh;
145	GetFontHeight(&fh);
146	BRect b(Bounds());
147
148	BPoint middle;
149	float textHeight = (fh.ascent + fh.descent) * 1.5;
150	middle.y = (b.top + b.bottom - textHeight) / 2.0;
151	middle.x = (b.left + b.right - StringWidth(message1)) / 2.0;
152	DrawString(message1, middle);
153
154	middle.y += textHeight;
155	middle.x = (b.left + b.right - StringWidth(message2)) / 2.0;
156	DrawString(message2, middle);
157}
158
159// SelectionChanged
160void
161TransformerListView::SelectionChanged()
162{
163	if (CountSelectedItems() > 0)
164		SimpleListView::SelectionChanged();
165	// else
166	// TODO: any selected transformer will still be visible in the
167	// PropertyListView
168
169	if (!fSyncingToSelection) {
170		TransformerItem* item
171			= dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(0)));
172		if (fMessage) {
173			BMessage message(*fMessage);
174			message.AddPointer("transformer", item ? (void*)item->transformer : NULL);
175			Invoke(&message);
176		}
177	}
178}
179
180// MessageReceived
181void
182TransformerListView::MessageReceived(BMessage* message)
183{
184	switch (message->what) {
185		case MSG_ADD_TRANSFORMER: {
186			if (!fShape || !fCommandStack)
187				break;
188
189			uint32 type;
190			if (message->FindInt32("type", (int32*)&type) < B_OK)
191				break;
192
193			Transformer* transformer
194				= TransformerFactory::TransformerFor(type,
195													 fShape->VertexSource());
196			if (!transformer)
197				break;
198
199			Transformer* transformers[1];
200			transformers[0] = transformer;
201			::Command* command = new (nothrow) AddTransformersCommand(
202				fShape, transformers, 1, fShape->CountTransformers());
203
204			if (!command)
205				delete transformer;
206
207			fCommandStack->Perform(command);
208			break;
209		}
210		default:
211			SimpleListView::MessageReceived(message);
212			break;
213	}
214}
215
216// MakeDragMessage
217void
218TransformerListView::MakeDragMessage(BMessage* message) const
219{
220	SimpleListView::MakeDragMessage(message);
221	message->AddPointer("container", fShape);
222	int32 count = CountItems();
223	for (int32 i = 0; i < count; i++) {
224		TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(i)));
225		if (item) {
226			message->AddPointer("transformer", (void*)item->transformer);
227		} else
228			break;
229	}
230}
231
232// #pragma mark -
233
234// MoveItems
235void
236TransformerListView::MoveItems(BList& items, int32 toIndex)
237{
238	if (!fCommandStack || !fShape)
239		return;
240
241	int32 count = items.CountItems();
242	Transformer** transformers = new (nothrow) Transformer*[count];
243	if (!transformers)
244		return;
245
246	for (int32 i = 0; i < count; i++) {
247		TransformerItem* item
248			= dynamic_cast<TransformerItem*>((BListItem*)items.ItemAtFast(i));
249		transformers[i] = item ? item->transformer : NULL;
250	}
251
252	MoveTransformersCommand* command
253		= new (nothrow) MoveTransformersCommand(fShape,
254												transformers, count, toIndex);
255	if (!command) {
256		delete[] transformers;
257		return;
258	}
259
260	fCommandStack->Perform(command);
261}
262
263// CopyItems
264void
265TransformerListView::CopyItems(BList& items, int32 toIndex)
266{
267	MoveItems(items, toIndex);
268	// TODO: allow copying items
269}
270
271// RemoveItemList
272void
273TransformerListView::RemoveItemList(BList& items)
274{
275	if (!fCommandStack || !fShape)
276		return;
277
278	int32 count = items.CountItems();
279	int32 indices[count];
280	for (int32 i = 0; i < count; i++)
281		indices[i] = IndexOf((BListItem*)items.ItemAtFast(i));
282
283	RemoveTransformersCommand* command
284		= new (nothrow) RemoveTransformersCommand(fShape,
285												  indices, count);
286	fCommandStack->Perform(command);
287}
288
289// CloneItem
290BListItem*
291TransformerListView::CloneItem(int32 index) const
292{
293	if (TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(index))) {
294		return new TransformerItem(item->transformer,
295								   const_cast<TransformerListView*>(this));
296	}
297	return NULL;
298}
299
300// IndexOfSelectable
301int32
302TransformerListView::IndexOfSelectable(Selectable* selectable) const
303{
304	Transformer* transformer = dynamic_cast<Transformer*>(selectable);
305	if (!transformer)
306		return -1;
307
308	for (int32 i = 0;
309		 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i));
310		 i++) {
311		if (item->transformer == transformer)
312			return i;
313	}
314
315	return -1;
316}
317
318// SelectableFor
319Selectable*
320TransformerListView::SelectableFor(BListItem* item) const
321{
322	TransformerItem* transformerItem = dynamic_cast<TransformerItem*>(item);
323	if (transformerItem)
324		return transformerItem->transformer;
325	return NULL;
326}
327
328// #pragma mark -
329
330// TransformerAdded
331void
332TransformerListView::TransformerAdded(Transformer* transformer, int32 index)
333{
334	// NOTE: we are in the thread that messed with the
335	// Shape, so no need to lock the document, when this is
336	// changed to asynchronous notifications, then it would
337	// need to be read-locked!
338	if (!LockLooper())
339		return;
340
341	_AddTransformer(transformer, index);
342
343	UnlockLooper();
344}
345
346// TransformerRemoved
347void
348TransformerListView::TransformerRemoved(Transformer* transformer)
349{
350	// NOTE: we are in the thread that messed with the
351	// Shape, so no need to lock the document, when this is
352	// changed to asynchronous notifications, then it would
353	// need to be read-locked!
354	if (!LockLooper())
355		return;
356
357	_RemoveTransformer(transformer);
358
359	UnlockLooper();
360}
361
362// StyleChanged
363void
364TransformerListView::StyleChanged(Style* oldStyle, Style* newStyle)
365{
366	// we don't care
367}
368
369// #pragma mark -
370
371// SetMenu
372void
373TransformerListView::SetMenu(BMenu* menu)
374{
375	if (fMenu == menu)
376		return;
377
378	fMenu = menu;
379	if (fMenu == NULL)
380		return;
381
382	BMenu* addMenu = new BMenu(B_TRANSLATE("Add"));
383	int32 cookie = 0;
384	uint32 type;
385	BString name;
386	while (TransformerFactory::NextType(&cookie, &type, &name)) {
387		// TODO: Disable the "Transformation" and "Perspective" transformers
388		// since they are not very useful or even implemented at all.
389		if (name == B_TRANSLATE("Transformation")
390			|| name == B_TRANSLATE("Perspective"))
391			continue;
392		// End of TODO.
393		BMessage* message = new BMessage(MSG_ADD_TRANSFORMER);
394		message->AddInt32("type", type);
395		addMenu->AddItem(new BMenuItem(name.String(), message));
396	}
397	addMenu->SetTargetForItems(this);
398	fMenu->AddItem(addMenu);
399
400	_UpdateMenu();
401}
402
403// SetShape
404void
405TransformerListView::SetShape(Shape* shape)
406{
407	if (fShape == shape)
408		return;
409
410	// detach from old container
411	if (fShape)
412		fShape->RemoveListener(this);
413
414	_MakeEmpty();
415
416	fShape = shape;
417
418	if (fShape) {
419		fShape->AddListener(this);
420
421		int32 count = fShape->CountTransformers();
422		for (int32 i = 0; i < count; i++)
423			_AddTransformer(fShape->TransformerAtFast(i), i);
424	}
425
426	_UpdateMenu();
427}
428
429// SetCommandStack
430void
431TransformerListView::SetCommandStack(CommandStack* stack)
432{
433	fCommandStack = stack;
434}
435
436// #pragma mark -
437
438// _AddTransformer
439bool
440TransformerListView::_AddTransformer(Transformer* transformer, int32 index)
441{
442	if (transformer)
443		 return AddItem(new TransformerItem(transformer, this), index);
444	return false;
445}
446
447// _RemoveTransformer
448bool
449TransformerListView::_RemoveTransformer(Transformer* transformer)
450{
451	TransformerItem* item = _ItemForTransformer(transformer);
452	if (item && RemoveItem(item)) {
453		delete item;
454		return true;
455	}
456	return false;
457}
458
459// _ItemForTransformer
460TransformerItem*
461TransformerListView::_ItemForTransformer(Transformer* transformer) const
462{
463	for (int32 i = 0;
464		 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i));
465		 i++) {
466		if (item->transformer == transformer)
467			return item;
468	}
469	return NULL;
470}
471
472// _UpdateMenu
473void
474TransformerListView::_UpdateMenu()
475{
476	fMenu->SetEnabled(fShape != NULL);
477}
478