xref: /haiku/src/apps/cortex/DiagramView/DiagramItemGroup.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
1 // DiagramItemGroup.cpp
2 
3 #include "DiagramItemGroup.h"
4 #include "DiagramItem.h"
5 
6 #include <Region.h>
7 
8 __USE_CORTEX_NAMESPACE
9 
10 #include <Debug.h>
11 #define D_METHOD(x) //PRINT (x)
12 
13 // -------------------------------------------------------- //
14 // *** ctor/dtor
15 // -------------------------------------------------------- //
16 
17 DiagramItemGroup::DiagramItemGroup(
18 	uint32 acceptedTypes,
19 	bool multiSelection)
20 	: m_boxes(0),
21 	  m_wires(0),
22 	  m_endPoints(0),
23 	  m_selection(0),
24 	  m_types(acceptedTypes),
25 	  m_itemAlignment(1.0, 1.0),
26 	  m_multiSelection(multiSelection),
27 	  m_lastItemUnder(0)
28 {
29 	D_METHOD(("DiagramItemGroup::DiagramItemGroup()\n"));
30 	m_selection = new BList(1);
31 }
32 
33 DiagramItemGroup::~DiagramItemGroup()
34 {
35 	D_METHOD(("DiagramItemGroup::~DiagramItemGroup()\n"));
36 	if (m_selection)
37 	{
38 		m_selection->MakeEmpty();
39 		delete m_selection;
40 	}
41 	if (m_boxes && (m_types & DiagramItem::M_BOX))
42 	{
43 		while (countItems(DiagramItem::M_BOX) > 0)
44 		{
45 			DiagramItem *item = itemAt(0, DiagramItem::M_BOX);
46 			if (removeItem(item))
47 				delete item;
48 		}
49 		delete m_boxes;
50 	}
51 	if (m_wires && (m_types & DiagramItem::M_WIRE))
52 	{
53 		while (countItems(DiagramItem::M_WIRE) > 0)
54 		{
55 			DiagramItem *item = itemAt(0, DiagramItem::M_WIRE);
56 			if (removeItem(item))
57 				delete item;
58 		}
59 		delete m_wires;
60 	}
61 	if (m_endPoints && (m_types & DiagramItem::M_ENDPOINT))
62 	{
63 		while (countItems(DiagramItem::M_ENDPOINT) > 0)
64 		{
65 			DiagramItem *item = itemAt(0, DiagramItem::M_ENDPOINT);
66 			if (removeItem(item))
67 				delete item;
68 		}
69 		delete m_endPoints;
70 	}
71 }
72 
73 // -------------------------------------------------------- //
74 // *** item accessors
75 // -------------------------------------------------------- //
76 
77 uint32 DiagramItemGroup::countItems(
78 	uint32 whichType) const
79 {
80 	D_METHOD(("DiagramItemGroup::countItems()\n"));
81 	uint32 count = 0;
82 	if (whichType & m_types)
83 	{
84 		if (whichType & DiagramItem::M_BOX)
85 		{
86 			if (m_boxes)
87 			{
88 				count += m_boxes->CountItems();
89 			}
90 		}
91 		if (whichType & DiagramItem::M_WIRE)
92 		{
93 			if (m_wires)
94 			{
95 				count += m_wires->CountItems();
96 			}
97 		}
98 		if (whichType & DiagramItem::M_ENDPOINT)
99 		{
100 			if (m_endPoints)
101 			{
102 				count += m_endPoints->CountItems();
103 			}
104 		}
105 	}
106 	return count;
107 }
108 
109 DiagramItem *DiagramItemGroup::itemAt(
110 	uint32 index,
111 	uint32 whichType) const
112 {
113 	D_METHOD(("DiagramItemGroup::itemAt()\n"));
114 	if (m_types & whichType)
115 	{
116 		if (whichType & DiagramItem::M_BOX)
117 		{
118 			if (m_boxes && (index < countItems(DiagramItem::M_BOX)))
119 			{
120 				return static_cast<DiagramItem *>(m_boxes->ItemAt(index));
121 			}
122 			else
123 			{
124 				index -= countItems(DiagramItem::M_BOX);
125 			}
126 		}
127 		if (whichType & DiagramItem::M_WIRE)
128 		{
129 			if (m_wires && (index < countItems(DiagramItem::M_WIRE)))
130 			{
131 				return static_cast<DiagramItem *>(m_wires->ItemAt(index));
132 			}
133 			else
134 			{
135 				index -= countItems(DiagramItem::M_WIRE);
136 			}
137 		}
138 		if (whichType & DiagramItem::M_ENDPOINT)
139 		{
140 			if (m_endPoints && (index < countItems(DiagramItem::M_ENDPOINT)))
141 			{
142 				return static_cast<DiagramItem *>(m_endPoints->ItemAt(index));
143 			}
144 		}
145 	}
146 	return 0;
147 }
148 
149 // This function returns the first box or endpoint found that
150 // contains the given point. For connections it looks at all
151 // wires that 'might' contain the point and calls their method
152 // howCloseTo() to find the one closest to the point.
153 // The lists should be sorted by selection time for proper results!
154 DiagramItem *DiagramItemGroup::itemUnder(
155 	BPoint point)
156 {
157 	D_METHOD(("DiagramItemGroup::itemUnder()\n"));
158 	if (m_types & DiagramItem::M_BOX)
159 	{
160 		for (uint32 i = 0; i < countItems(DiagramItem::M_BOX); i++)
161 		{
162 			DiagramItem *item = itemAt(i, DiagramItem::M_BOX);
163 			if (item->frame().Contains(point) && (item->howCloseTo(point) == 1.0))
164 			{
165 //				DiagramItemGroup *group = dynamic_cast<DiagramItemGroup *>(item);
166 				return (m_lastItemUnder = item);
167 			}
168 		}
169 	}
170 	if (m_types & DiagramItem::M_WIRE)
171 	{
172 		float closest = 0.0;
173 		DiagramItem *closestItem = 0;
174 		for (uint32 i = 0; i < countItems(DiagramItem::M_WIRE); i++)
175 		{
176 			DiagramItem *item = itemAt(i, DiagramItem::M_WIRE);
177 			if (item->frame().Contains(point))
178 			{
179 				float howClose = item->howCloseTo(point);
180 				if (howClose > closest)
181 				{
182 					closestItem = item;
183 					if (howClose == 1.0)
184 						return (m_lastItemUnder = item);
185 					closest = howClose;
186 				}
187 			}
188 		}
189 		if (closest > 0.5)
190 		{
191 			return (m_lastItemUnder = closestItem);
192 		}
193 	}
194 	if (m_types & DiagramItem::M_ENDPOINT)
195 	{
196 		for (uint32 i = 0; i < countItems(DiagramItem::M_ENDPOINT); i++)
197 		{
198 			DiagramItem *item = itemAt(i, DiagramItem::M_ENDPOINT);
199 			if (item->frame().Contains(point) && (item->howCloseTo(point) == 1.0))
200 			{
201 				return (m_lastItemUnder = item);
202 			}
203 		}
204 	}
205 	return (m_lastItemUnder = 0); // no item was found!
206 }
207 
208 // -------------------------------------------------------- //
209 // *** item operations
210 // -------------------------------------------------------- //
211 
212 bool DiagramItemGroup::addItem(
213 	DiagramItem *item)
214 {
215 	D_METHOD(("DiagramItemGroup::addItem()\n"));
216 	if (item && (m_types & item->type()))
217 	{
218 		switch (item->type())
219 		{
220 			case DiagramItem::M_BOX:
221 			{
222 				if (!m_boxes)
223 				{
224 					m_boxes = new BList();
225 				}
226 				item->m_group = this;
227 				return m_boxes->AddItem(static_cast<void *>(item));
228 			}
229 			case DiagramItem::M_WIRE:
230 			{
231 				if (!m_wires)
232 				{
233 					m_wires = new BList();
234 				}
235 				item->m_group = this;
236 				return m_wires->AddItem(static_cast<void *>(item));
237 			}
238 			case DiagramItem::M_ENDPOINT:
239 			{
240 				if (!m_endPoints)
241 				{
242 					m_endPoints = new BList();
243 				}
244 				item->m_group = this;
245 				return m_endPoints->AddItem(static_cast<void *>(item));
246 			}
247 		}
248 	}
249 	return false;
250 }
251 
252 bool DiagramItemGroup::removeItem(
253 	DiagramItem *item)
254 {
255 	D_METHOD(("DiagramItemGroup::removeItem()\n"));
256 	if (item && (m_types & item->type()))
257 	{
258 		// reset the lastItemUnder-pointer if it pointed to this item
259 		if (m_lastItemUnder == item)
260 		{
261 			m_lastItemUnder = 0;
262 		}
263 		// remove it from the selection list if it was selected
264 		if (item->isSelected())
265 		{
266 			m_selection->RemoveItem(static_cast<void *>(item));
267 		}
268 		// try to remove the item from its list
269 		switch (item->type())
270 		{
271 			case DiagramItem::M_BOX:
272 			{
273 				if (m_boxes)
274 				{
275 					item->m_group = 0;
276 					return m_boxes->RemoveItem(static_cast<void *>(item));
277 				}
278 			}
279 			case DiagramItem::M_WIRE:
280 			{
281 				if (m_wires)
282 				{
283 					item->m_group = 0;
284 					return m_wires->RemoveItem(static_cast<void *>(item));
285 				}
286 			}
287 			case DiagramItem::M_ENDPOINT:
288 			{
289 				if (m_endPoints)
290 				{
291 					item->m_group = 0;
292 					return m_endPoints->RemoveItem(static_cast<void *>(item));
293 				}
294 			}
295 		}
296 	}
297 	return false;
298 }
299 
300 void DiagramItemGroup::sortItems(
301 	uint32 whichType,
302 	int (*compareFunc)(const void *, const void *))
303 {
304 	D_METHOD(("DiagramItemGroup::sortItems()\n"));
305 	if ((whichType != DiagramItem::M_ANY) && (m_types & whichType))
306 	{
307 		switch (whichType)
308 		{
309 			case DiagramItem::M_BOX:
310 			{
311 				if (m_boxes)
312 				{
313 					m_boxes->SortItems(compareFunc);
314 				}
315 				break;
316 			}
317 			case DiagramItem::M_WIRE:
318 			{
319 				if (m_wires)
320 				{
321 					m_wires->SortItems(compareFunc);
322 				}
323 				break;
324 			}
325 			case DiagramItem::M_ENDPOINT:
326 			{
327 				if (m_endPoints)
328 				{
329 					m_endPoints->SortItems(compareFunc);
330 				}
331 				break;
332 			}
333 		}
334 	}
335 }
336 
337 // items are drawn in reverse order; they should be sorted by
338 // selection time before this function gets called, so that
339 // the more recently selected item are drawn above others
340 void DiagramItemGroup::drawItems(
341 	BRect updateRect,
342 	uint32 whichType,
343 	BRegion *updateRegion)
344 {
345 	D_METHOD(("DiagramItemGroup::drawItems()\n"));
346 	if (whichType & DiagramItem::M_WIRE)
347 	{
348 		for (int32 i = countItems(DiagramItem::M_WIRE) - 1; i >= 0; i--)
349 		{
350 			DiagramItem *item = itemAt(i, DiagramItem::M_WIRE);
351 			if (item->frame().Intersects(updateRect))
352 			{
353 				item->draw(updateRect);
354 			}
355 		}
356 	}
357 	if (whichType & DiagramItem::M_BOX)
358 	{
359 		for (int32 i = countItems(DiagramItem::M_BOX) - 1; i >= 0; i--)
360 		{
361 			DiagramItem *item = itemAt(i, DiagramItem::M_BOX);
362 			if (item && item->frame().Intersects(updateRect))
363 			{
364 				item->draw(updateRect);
365 				if (updateRegion)
366 					updateRegion->Exclude(item->frame());
367 			}
368 		}
369 	}
370 	if (whichType & DiagramItem::M_ENDPOINT)
371 	{
372 		for (int32 i = countItems(DiagramItem::M_ENDPOINT) - 1; i >= 0; i--)
373 		{
374 			DiagramItem *item = itemAt(i, DiagramItem::M_ENDPOINT);
375 			if (item && item->frame().Intersects(updateRect))
376 			{
377 				item->draw(updateRect);
378 			}
379 		}
380 	}
381 }
382 
383 bool DiagramItemGroup::getClippingAbove(
384 	DiagramItem *which,
385 	BRegion *region)
386 {
387 	D_METHOD(("DiagramItemGroup::getClippingAbove()\n"));
388 	bool found = false;
389 	if (which && region)
390 	{
391 		switch (which->type())
392 		{
393 			case DiagramItem::M_BOX:
394 			{
395 				int32 index = m_boxes->IndexOf(which);
396 				if (index >= 0) // the item was found
397 				{
398 					BRect r = which->frame();
399 					for (int32 i = 0; i < index; i++)
400 					{
401 						DiagramItem *item = itemAt(i, DiagramItem::M_BOX);
402 						if (item && item->frame().Intersects(r))
403 						{
404 							region->Include(item->frame() & r);
405 							found = true;
406 						}
407 					}
408 				}
409 				break;
410 			}
411 			case DiagramItem::M_WIRE:
412 			{
413 				BRect r = which->frame();
414 				for (uint32 i = 0; i < countItems(DiagramItem::M_BOX); i++)
415 				{
416 					DiagramItem *item = itemAt(i, DiagramItem::M_BOX);
417 					if (item && item->frame().Intersects(r))
418 					{
419 						region->Include(item->frame() & r);
420 						found = true;
421 					}
422 				}
423 				break;
424 			}
425 		}
426 	}
427 	return found;
428 }
429 
430 // -------------------------------------------------------- //
431 // *** selection accessors
432 // -------------------------------------------------------- //
433 
434 uint32 DiagramItemGroup::selectedType() const
435 {
436 	D_METHOD(("DiagramItemGroup::selectedType()\n"));
437 	if (countSelectedItems() > 0)
438 	{
439 		return selectedItemAt(0)->type();
440 	}
441 	return 0;
442 }
443 
444 uint32 DiagramItemGroup::countSelectedItems() const
445 {
446 	D_METHOD(("DiagramItemGroup::countSelectedItems()\n"));
447 	if (m_selection)
448 	{
449 		return m_selection->CountItems();
450 	}
451 	return 0;
452 }
453 
454 DiagramItem *DiagramItemGroup::selectedItemAt(
455 	uint32 index) const
456 {
457 	D_METHOD(("DiagramItemGroup::selectedItemAt()\n"));
458 	if (m_selection)
459 	{
460 		return static_cast<DiagramItem *>(m_selection->ItemAt(index));
461 	}
462 	return 0;
463 }
464 
465 // -------------------------------------------------------- //
466 // *** selection related operations
467 // -------------------------------------------------------- //
468 
469 // selects an item, optionally replacing the complete former
470 // selection. If the type of the item to be selected differs
471 // from the type of items currently selected, this methods
472 // automatically replaces the former selection
473 bool DiagramItemGroup::selectItem(
474 	DiagramItem *which,
475 	bool deselectOthers)
476 {
477 	D_METHOD(("DiagramItemGroup::selectItem()\n"));
478 	bool selectionChanged = false;
479 	if (which && !which->isSelected() && which->isSelectable())
480 	{
481 		// check if the item's type is the same as of the other
482 		// selected items
483 		if (m_multiSelection)
484 		{
485 			if (which->type() != selectedType())
486 			{
487 				deselectOthers = true;
488 			}
489 		}
490 
491 		// check if the former selection has to be deselected
492 		if (deselectOthers || !m_multiSelection)
493 		{
494 			while (countSelectedItems() > 0)
495 			{
496 				deselectItem(selectedItemAt(0));
497 			}
498 		}
499 
500 		// select the item
501 		if (deselectOthers || countSelectedItems() == 0)
502 		{
503 			which->select();
504 		}
505 		else
506 		{
507 			which->selectAdding();
508 		}
509 		m_selection->AddItem(which);
510 		selectionChanged = true;
511 	}
512 
513 	// resort the lists if necessary
514 	if (selectionChanged)
515 	{
516 		sortItems(which->type(), compareSelectionTime);
517 		sortSelectedItems(compareSelectionTime);
518 		return true;
519 	}
520 	return false;
521 }
522 
523 bool DiagramItemGroup::deselectItem(
524 	DiagramItem *which)
525 {
526 	D_METHOD(("DiagramItemGroup::deselectItem()\n"));
527 	if (which && which->isSelected())
528 	{
529 		m_selection->RemoveItem(which);
530 		which->deselect();
531 		sortItems(which->type(), compareSelectionTime);
532 		sortSelectedItems(compareSelectionTime);
533 		return true;
534 	}
535 	return false;
536 }
537 
538 bool DiagramItemGroup::selectAll(
539 	uint32 itemType)
540 {
541 	D_METHOD(("DiagramItemGroup::selectAll()\n"));
542 	bool selectionChanged = false;
543 	if (m_types & itemType)
544 	{
545 		for (int32 i = 0; i < countItems(itemType); i++)
546 		{
547 			if (selectItem(itemAt(i, itemType), false))
548 				selectionChanged = true;
549 		}
550 	}
551 	return selectionChanged;
552 }
553 
554 bool DiagramItemGroup::deselectAll(
555 	uint32 itemType)
556 {
557 	D_METHOD(("DiagramItemGroup::deselectAll()\n"));
558 	bool selectionChanged = false;
559 	if (m_types & itemType)
560 	{
561 		for (int32 i = 0; i < countItems(itemType); i++)
562 		{
563 			if (deselectItem(itemAt(i, itemType)))
564 				selectionChanged = true;
565 		}
566 	}
567 	return selectionChanged;
568 }
569 
570 void DiagramItemGroup::sortSelectedItems(
571 	int (*compareFunc)(const void *, const void *))
572 {
573 	D_METHOD(("DiagramItemGroup::sortSelectedItems()\n"));
574 	m_selection->SortItems(compareFunc);
575 }
576 
577 void DiagramItemGroup::dragSelectionBy(
578 	float x,
579 	float y,
580 	BRegion *updateRegion)
581 {
582 	D_METHOD(("DiagramItemGroup::dragSelectionBy()\n"));
583 	if (selectedType() == DiagramItem::M_BOX)
584 	{
585 		align(&x, &y);
586 		if ((x != 0) || (y != 0))
587 		{
588 			for (int32 i = countSelectedItems() - 1; i >= 0; i--)
589 			{
590 				DiagramItem *item = dynamic_cast<DiagramItem *>(selectedItemAt(i));
591 				if (item->isDraggable())
592 				{
593 					item->moveBy(x, y, updateRegion);
594 				}
595 			}
596 		}
597 	}
598 }
599 
600 void DiagramItemGroup::removeSelection()
601 {
602 	D_METHOD(("DiagramItemGroup::removeSelection()\n"));
603 	for (int32 i = 0; i < countSelectedItems(); i++)
604 	{
605 		removeItem(selectedItemAt(i));
606 	}
607 }
608 
609 // -------------------------------------------------------- //
610 // *** alignment related accessors & operations
611 // -------------------------------------------------------- //
612 
613 void DiagramItemGroup::getItemAlignment(
614 	float *horizontal,
615 	float *vertical)
616 {
617 	D_METHOD(("DiagramItemGroup::getItemAlignment()\n"));
618 	if (horizontal)
619 		*horizontal = m_itemAlignment.x;
620 	if (vertical)
621 		*vertical = m_itemAlignment.y;
622 }
623 
624 void DiagramItemGroup::align(
625 	float *x,
626 	float *y) const
627 {
628 	D_METHOD(("DiagramItemGroup::align()\n"));
629 	*x = ((int)*x / (int)m_itemAlignment.x) * m_itemAlignment.x;
630 	*y = ((int)*y / (int)m_itemAlignment.y) * m_itemAlignment.y;
631 }
632 
633 BPoint DiagramItemGroup::align(
634 	BPoint point) const
635 {
636 	D_METHOD(("DiagramItemGroup::align()\n"));
637 	float x = point.x, y = point.y;
638 	align(&x, &y);
639 	return BPoint(x, y);
640 }
641 
642 // END -- DiagramItemGroup.cpp --
643