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