xref: /haiku/src/apps/icon-o-matic/gui/TransformerListView.cpp (revision ed24eb5ff12640d052171c6a7feba37fab8a75d1)
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 
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->ReleaseReference();
72 						}
73 
74 						transformer = t;
75 
76 						if (transformer) {
77 							transformer->AcquireReference();
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_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 	// Dark Themes
144 	rgb_color lowColor = LowColor();
145 	if (lowColor.red + lowColor.green + lowColor.blue > 128 * 3)
146 		SetHighColor(tint_color(LowColor(), B_DARKEN_2_TINT));
147 	else
148 		SetHighColor(tint_color(LowColor(), B_LIGHTEN_2_TINT));
149 
150 	font_height fh;
151 	GetFontHeight(&fh);
152 	BRect b(Bounds());
153 
154 	BPoint middle;
155 	float textHeight = (fh.ascent + fh.descent) * 1.5;
156 	middle.y = (b.top + b.bottom - textHeight) / 2.0;
157 	middle.x = (b.left + b.right - StringWidth(message1)) / 2.0;
158 	DrawString(message1, middle);
159 
160 	middle.y += textHeight;
161 	middle.x = (b.left + b.right - StringWidth(message2)) / 2.0;
162 	DrawString(message2, middle);
163 }
164 
165 // SelectionChanged
166 void
167 TransformerListView::SelectionChanged()
168 {
169 	if (CountSelectedItems() > 0)
170 		SimpleListView::SelectionChanged();
171 	// else
172 	// TODO: any selected transformer will still be visible in the
173 	// PropertyListView
174 
175 	if (!fSyncingToSelection) {
176 		TransformerItem* item
177 			= dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(0)));
178 		if (fMessage) {
179 			BMessage message(*fMessage);
180 			message.AddPointer("transformer", item ? (void*)item->transformer : NULL);
181 			Invoke(&message);
182 		}
183 	}
184 }
185 
186 // MessageReceived
187 void
188 TransformerListView::MessageReceived(BMessage* message)
189 {
190 	switch (message->what) {
191 		case MSG_ADD_TRANSFORMER: {
192 			if (!fShape || !fCommandStack)
193 				break;
194 
195 			uint32 type;
196 			if (message->FindInt32("type", (int32*)&type) < B_OK)
197 				break;
198 
199 			Transformer* transformer
200 				= TransformerFactory::TransformerFor(type,
201 													 fShape->VertexSource());
202 			if (!transformer)
203 				break;
204 
205 			Transformer* transformers[1];
206 			transformers[0] = transformer;
207 			::Command* command = new (nothrow) AddTransformersCommand(
208 				fShape, transformers, 1, fShape->CountTransformers());
209 
210 			if (!command)
211 				delete transformer;
212 
213 			fCommandStack->Perform(command);
214 			break;
215 		}
216 		default:
217 			SimpleListView::MessageReceived(message);
218 			break;
219 	}
220 }
221 
222 // MakeDragMessage
223 void
224 TransformerListView::MakeDragMessage(BMessage* message) const
225 {
226 	SimpleListView::MakeDragMessage(message);
227 	message->AddPointer("container", fShape);
228 	int32 count = CountItems();
229 	for (int32 i = 0; i < count; i++) {
230 		TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(CurrentSelection(i)));
231 		if (item) {
232 			message->AddPointer("transformer", (void*)item->transformer);
233 		} else
234 			break;
235 	}
236 }
237 
238 // #pragma mark -
239 
240 // MoveItems
241 void
242 TransformerListView::MoveItems(BList& items, int32 toIndex)
243 {
244 	if (!fCommandStack || !fShape)
245 		return;
246 
247 	int32 count = items.CountItems();
248 	Transformer** transformers = new (nothrow) Transformer*[count];
249 	if (!transformers)
250 		return;
251 
252 	for (int32 i = 0; i < count; i++) {
253 		TransformerItem* item
254 			= dynamic_cast<TransformerItem*>((BListItem*)items.ItemAtFast(i));
255 		transformers[i] = item ? item->transformer : NULL;
256 	}
257 
258 	MoveTransformersCommand* command
259 		= new (nothrow) MoveTransformersCommand(fShape,
260 												transformers, count, toIndex);
261 	if (!command) {
262 		delete[] transformers;
263 		return;
264 	}
265 
266 	fCommandStack->Perform(command);
267 }
268 
269 // CopyItems
270 void
271 TransformerListView::CopyItems(BList& items, int32 toIndex)
272 {
273 	MoveItems(items, toIndex);
274 	// TODO: allow copying items
275 }
276 
277 // RemoveItemList
278 void
279 TransformerListView::RemoveItemList(BList& items)
280 {
281 	if (!fCommandStack || !fShape)
282 		return;
283 
284 	int32 count = items.CountItems();
285 	int32 indices[count];
286 	for (int32 i = 0; i < count; i++)
287 		indices[i] = IndexOf((BListItem*)items.ItemAtFast(i));
288 
289 	RemoveTransformersCommand* command
290 		= new (nothrow) RemoveTransformersCommand(fShape,
291 												  indices, count);
292 	fCommandStack->Perform(command);
293 }
294 
295 // CloneItem
296 BListItem*
297 TransformerListView::CloneItem(int32 index) const
298 {
299 	if (TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(index))) {
300 		return new TransformerItem(item->transformer,
301 								   const_cast<TransformerListView*>(this));
302 	}
303 	return NULL;
304 }
305 
306 // IndexOfSelectable
307 int32
308 TransformerListView::IndexOfSelectable(Selectable* selectable) const
309 {
310 	Transformer* transformer = dynamic_cast<Transformer*>(selectable);
311 	if (!transformer)
312 		return -1;
313 
314 	for (int32 i = 0;
315 		 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i));
316 		 i++) {
317 		if (item->transformer == transformer)
318 			return i;
319 	}
320 
321 	return -1;
322 }
323 
324 // SelectableFor
325 Selectable*
326 TransformerListView::SelectableFor(BListItem* item) const
327 {
328 	TransformerItem* transformerItem = dynamic_cast<TransformerItem*>(item);
329 	if (transformerItem)
330 		return transformerItem->transformer;
331 	return NULL;
332 }
333 
334 // #pragma mark -
335 
336 // TransformerAdded
337 void
338 TransformerListView::TransformerAdded(Transformer* transformer, int32 index)
339 {
340 	// NOTE: we are in the thread that messed with the
341 	// Shape, so no need to lock the document, when this is
342 	// changed to asynchronous notifications, then it would
343 	// need to be read-locked!
344 	if (!LockLooper())
345 		return;
346 
347 	_AddTransformer(transformer, index);
348 
349 	UnlockLooper();
350 }
351 
352 // TransformerRemoved
353 void
354 TransformerListView::TransformerRemoved(Transformer* transformer)
355 {
356 	// NOTE: we are in the thread that messed with the
357 	// Shape, so no need to lock the document, when this is
358 	// changed to asynchronous notifications, then it would
359 	// need to be read-locked!
360 	if (!LockLooper())
361 		return;
362 
363 	_RemoveTransformer(transformer);
364 
365 	UnlockLooper();
366 }
367 
368 // StyleChanged
369 void
370 TransformerListView::StyleChanged(Style* oldStyle, Style* newStyle)
371 {
372 	// we don't care
373 }
374 
375 // #pragma mark -
376 
377 // SetMenu
378 void
379 TransformerListView::SetMenu(BMenu* menu)
380 {
381 	if (fMenu == menu)
382 		return;
383 
384 	fMenu = menu;
385 	if (fMenu == NULL)
386 		return;
387 
388 	BMenu* addMenu = new BMenu(B_TRANSLATE("Add"));
389 	int32 cookie = 0;
390 	uint32 type;
391 	BString name;
392 	while (TransformerFactory::NextType(&cookie, &type, &name)) {
393 		// TODO: Disable the "Transformation" and "Perspective" transformers
394 		// since they are not very useful or even implemented at all.
395 		if (name == B_TRANSLATE("Transformation")
396 			|| name == B_TRANSLATE("Perspective"))
397 			continue;
398 		// End of TODO.
399 		BMessage* message = new BMessage(MSG_ADD_TRANSFORMER);
400 		message->AddInt32("type", type);
401 		addMenu->AddItem(new BMenuItem(name.String(), message));
402 	}
403 	addMenu->SetTargetForItems(this);
404 	fMenu->AddItem(addMenu);
405 
406 	_UpdateMenu();
407 }
408 
409 // SetShape
410 void
411 TransformerListView::SetShape(Shape* shape)
412 {
413 	if (fShape == shape)
414 		return;
415 
416 	// detach from old container
417 	if (fShape)
418 		fShape->RemoveListener(this);
419 
420 	_MakeEmpty();
421 
422 	fShape = shape;
423 
424 	if (fShape) {
425 		fShape->AddListener(this);
426 
427 		int32 count = fShape->CountTransformers();
428 		for (int32 i = 0; i < count; i++)
429 			_AddTransformer(fShape->TransformerAtFast(i), i);
430 	}
431 
432 	_UpdateMenu();
433 }
434 
435 // SetCommandStack
436 void
437 TransformerListView::SetCommandStack(CommandStack* stack)
438 {
439 	fCommandStack = stack;
440 }
441 
442 // #pragma mark -
443 
444 // _AddTransformer
445 bool
446 TransformerListView::_AddTransformer(Transformer* transformer, int32 index)
447 {
448 	if (transformer)
449 		 return AddItem(new TransformerItem(transformer, this), index);
450 	return false;
451 }
452 
453 // _RemoveTransformer
454 bool
455 TransformerListView::_RemoveTransformer(Transformer* transformer)
456 {
457 	TransformerItem* item = _ItemForTransformer(transformer);
458 	if (item && RemoveItem(item)) {
459 		delete item;
460 		return true;
461 	}
462 	return false;
463 }
464 
465 // _ItemForTransformer
466 TransformerItem*
467 TransformerListView::_ItemForTransformer(Transformer* transformer) const
468 {
469 	for (int32 i = 0;
470 		 TransformerItem* item = dynamic_cast<TransformerItem*>(ItemAt(i));
471 		 i++) {
472 		if (item->transformer == transformer)
473 			return item;
474 	}
475 	return NULL;
476 }
477 
478 // _UpdateMenu
479 void
480 TransformerListView::_UpdateMenu()
481 {
482 	fMenu->SetEnabled(fShape != NULL);
483 }
484