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