xref: /haiku/src/apps/icon-o-matic/gui/TransformerListView.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
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_TRANSLATE_CONTEXT
35 #define B_TRANSLATE_CONTEXT "Icon-O-Matic-TransformersList"
36 
37 
38 using std::nothrow;
39 
40 class 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 
100 enum {
101 	MSG_DRAG_TRANSFORMER			= 'drgt',
102 	MSG_ADD_TRANSFORMER				= 'adtr',
103 };
104 
105 // constructor
106 TransformerListView::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
119 TransformerListView::~TransformerListView()
120 {
121 	_MakeEmpty();
122 	delete fMessage;
123 
124 	if (fShape)
125 		fShape->RemoveListener(this);
126 }
127 
128 // Draw
129 void
130 TransformerListView::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_WITH_CONTEXT("Click on a shape above",
139 		"Empty transformers list - 1st line");
140 	const char* message2 = B_TRANSLATE_WITH_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
160 void
161 TransformerListView::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
181 void
182 TransformerListView::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
217 void
218 TransformerListView::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
235 void
236 TransformerListView::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
264 void
265 TransformerListView::CopyItems(BList& items, int32 toIndex)
266 {
267 	MoveItems(items, toIndex);
268 	// TODO: allow copying items
269 }
270 
271 // RemoveItemList
272 void
273 TransformerListView::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
290 BListItem*
291 TransformerListView::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
301 int32
302 TransformerListView::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
319 Selectable*
320 TransformerListView::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
331 void
332 TransformerListView::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
347 void
348 TransformerListView::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
363 void
364 TransformerListView::StyleChanged(Style* oldStyle, Style* newStyle)
365 {
366 	// we don't care
367 }
368 
369 // #pragma mark -
370 
371 // SetMenu
372 void
373 TransformerListView::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
404 void
405 TransformerListView::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
430 void
431 TransformerListView::SetCommandStack(CommandStack* stack)
432 {
433 	fCommandStack = stack;
434 }
435 
436 // #pragma mark -
437 
438 // _AddTransformer
439 bool
440 TransformerListView::_AddTransformer(Transformer* transformer, int32 index)
441 {
442 	if (transformer)
443 		 return AddItem(new TransformerItem(transformer, this), index);
444 	return false;
445 }
446 
447 // _RemoveTransformer
448 bool
449 TransformerListView::_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
460 TransformerItem*
461 TransformerListView::_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
473 void
474 TransformerListView::_UpdateMenu()
475 {
476 	fMenu->SetEnabled(fShape != NULL);
477 }
478