2 * Copyright (c) 1999-2000, Eric Moon.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions, and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 // DiagramItemGroup.cpp
34 /*! \class DiagramItemGroup.
35 \brief Basic class for managing and accessing DiagramItem objects.
37 Objects of this class can manage one or more of the DiagramItem
38 type M_BOX, M_WIRE and M_ENDPOINT. Many methods let you specify
39 which type of item you want to deal with.
42 #include "DiagramItemGroup.h"
43 #include "DiagramItem.h"
47 __USE_CORTEX_NAMESPACE
50 #define D_METHOD(x) //PRINT (x)
53 DiagramItemGroup::DiagramItemGroup(uint32 acceptedTypes
, bool multiSelection
)
58 fTypes(acceptedTypes
),
59 fItemAlignment(1.0, 1.0),
60 fMultiSelection(multiSelection
),
63 D_METHOD(("DiagramItemGroup::DiagramItemGroup()\n"));
64 fSelection
= new BList(1);
68 DiagramItemGroup::~DiagramItemGroup()
70 D_METHOD(("DiagramItemGroup::~DiagramItemGroup()\n"));
73 if (fWires
&& (fTypes
& DiagramItem::M_WIRE
)) {
74 count
= fWires
->CountItems();
75 for (int32 i
= 0; i
< count
; ++i
)
76 delete static_cast<DiagramItem
*>(fWires
->ItemAt(i
));
80 if (fBoxes
&& (fTypes
& DiagramItem::M_BOX
)) {
81 count
= fBoxes
->CountItems();
82 for (int32 i
= 0; i
< count
; ++i
)
83 delete static_cast<DiagramItem
*>(fBoxes
->ItemAt(i
));
87 if (fEndPoints
&& (fTypes
& DiagramItem::M_ENDPOINT
)) {
88 count
= fEndPoints
->CountItems();
89 for (int32 i
= 0; i
< count
; ++i
)
90 delete static_cast<DiagramItem
*>(fEndPoints
->ItemAt(i
));
99 // #pragma mark - item accessors
102 /*! Returns the number of items in the group (optionally only those
103 of the given type \param whichType)
106 DiagramItemGroup::CountItems(uint32 whichType
) const
108 D_METHOD(("DiagramItemGroup::CountItems()\n"));
110 if (whichType
& fTypes
) {
111 if (whichType
& DiagramItem::M_BOX
) {
113 count
+= fBoxes
->CountItems();
116 if (whichType
& DiagramItem::M_WIRE
) {
118 count
+= fWires
->CountItems();
121 if (whichType
& DiagramItem::M_ENDPOINT
) {
123 count
+= fEndPoints
->CountItems();
131 /*! Returns a pointer to the item in the lists which is
132 at the given index; if none is found, this function
136 DiagramItemGroup::ItemAt(uint32 index
, uint32 whichType
) const
138 D_METHOD(("DiagramItemGroup::ItemAt()\n"));
139 if (fTypes
& whichType
) {
140 if (whichType
& DiagramItem::M_BOX
) {
141 if (fBoxes
&& (index
< CountItems(DiagramItem::M_BOX
)))
142 return static_cast<DiagramItem
*>(fBoxes
->ItemAt(index
));
144 index
-= CountItems(DiagramItem::M_BOX
);
147 if (whichType
& DiagramItem::M_WIRE
) {
148 if (fWires
&& (index
< CountItems(DiagramItem::M_WIRE
)))
149 return static_cast<DiagramItem
*>(fWires
->ItemAt(index
));
151 index
-= CountItems(DiagramItem::M_WIRE
);
154 if (whichType
& DiagramItem::M_ENDPOINT
) {
155 if (fEndPoints
&& (index
< CountItems(DiagramItem::M_ENDPOINT
)))
156 return static_cast<DiagramItem
*>(fEndPoints
->ItemAt(index
));
164 /*! This function returns the first box or endpoint found that
165 contains the given \param point. For connections it looks at all
166 wires that 'might' contain the point and calls their method
167 howCloseTo() to find the one closest to the point.
168 The lists should be sorted by selection time for proper results!
171 DiagramItemGroup::ItemUnder(BPoint point
)
173 D_METHOD(("DiagramItemGroup::ItemUnder()\n"));
174 if (fTypes
& DiagramItem::M_BOX
) {
175 for (uint32 i
= 0; i
< CountItems(DiagramItem::M_BOX
); i
++) {
176 DiagramItem
*item
= ItemAt(i
, DiagramItem::M_BOX
);
177 if (item
->Frame().Contains(point
) && (item
->howCloseTo(point
) == 1.0)) {
178 // DiagramItemGroup *group = dynamic_cast<DiagramItemGroup *>(item);
179 return (fLastItemUnder
= item
);
184 if (fTypes
& DiagramItem::M_WIRE
) {
186 DiagramItem
*closestItem
= 0;
187 for (uint32 i
= 0; i
< CountItems(DiagramItem::M_WIRE
); i
++) {
188 DiagramItem
*item
= ItemAt(i
, DiagramItem::M_WIRE
);
189 if (item
->Frame().Contains(point
)) {
190 float howClose
= item
->howCloseTo(point
);
191 if (howClose
> closest
) {
194 return (fLastItemUnder
= item
);
201 return (fLastItemUnder
= closestItem
);
204 if (fTypes
& DiagramItem::M_ENDPOINT
) {
205 for (uint32 i
= 0; i
< CountItems(DiagramItem::M_ENDPOINT
); i
++) {
206 DiagramItem
*item
= ItemAt(i
, DiagramItem::M_ENDPOINT
);
207 if (item
->Frame().Contains(point
) && (item
->howCloseTo(point
) == 1.0))
208 return (fLastItemUnder
= item
);
212 return (fLastItemUnder
= 0); // no item was found!
216 // #pragma mark - item operations
219 //! Adds an \param item to the group; returns true on success.
221 DiagramItemGroup::AddItem(DiagramItem
*item
)
223 D_METHOD(("DiagramItemGroup::AddItem()\n"));
224 if (item
&& (fTypes
& item
->type())) {
226 item
->m_group
->RemoveItem(item
);
228 switch (item
->type()) {
229 case DiagramItem::M_BOX
:
231 fBoxes
= new BList();
232 item
->m_group
= this;
233 return fBoxes
->AddItem(static_cast<void *>(item
));
235 case DiagramItem::M_WIRE
:
237 fWires
= new BList();
238 item
->m_group
= this;
239 return fWires
->AddItem(static_cast<void *>(item
));
241 case DiagramItem::M_ENDPOINT
:
243 fEndPoints
= new BList();
244 item
->m_group
= this;
245 return fEndPoints
->AddItem(static_cast<void *>(item
));
253 //! Removes an \param item from the group; returns true on success.
255 DiagramItemGroup::RemoveItem(DiagramItem
* item
)
257 D_METHOD(("DiagramItemGroup::RemoveItem()\n"));
258 if (item
&& (fTypes
& item
->type())) {
259 // reset the lastItemUnder-pointer if it pointed to this item
260 if (fLastItemUnder
== item
)
263 // remove it from the selection list if it was selected
264 if (item
->isSelected())
265 fSelection
->RemoveItem(static_cast<void *>(item
));
267 // try to remove the item from its list
268 switch (item
->type()) {
269 case DiagramItem::M_BOX
:
272 return fBoxes
->RemoveItem(static_cast<void *>(item
));
276 case DiagramItem::M_WIRE
:
279 return fWires
->RemoveItem(static_cast<void *>(item
));
283 case DiagramItem::M_ENDPOINT
:
286 return fEndPoints
->RemoveItem(static_cast<void *>(item
));
295 /*! Performs a quicksort on a list of items with the provided
296 compare function (one is already defined in the DiagramItem
297 implementation); can't handle more than one item type at a
301 DiagramItemGroup::SortItems(uint32 whichType
,
302 int (*compareFunc
)(const void *, const void *))
304 D_METHOD(("DiagramItemGroup::SortItems()\n"));
305 if ((whichType
!= DiagramItem::M_ANY
) && (fTypes
& whichType
)) {
307 case DiagramItem::M_BOX
:
309 fBoxes
->SortItems(compareFunc
);
312 case DiagramItem::M_WIRE
:
314 fWires
->SortItems(compareFunc
);
317 case DiagramItem::M_ENDPOINT
:
319 fEndPoints
->SortItems(compareFunc
);
326 /*! Fires a Draw() command at all items of a specific type that
327 intersect with the \param updateRect;
328 items are drawn in reverse order; they should be sorted by
329 selection time before this function gets called, so that
330 the more recently selected item are drawn above others.
333 DiagramItemGroup::DrawItems(BRect updateRect
, uint32 whichType
, BRegion
* updateRegion
)
335 D_METHOD(("DiagramItemGroup::DrawItems()\n"));
336 if (whichType
& DiagramItem::M_WIRE
) {
337 for (int32 i
= CountItems(DiagramItem::M_WIRE
) - 1; i
>= 0; i
--) {
338 DiagramItem
*item
= ItemAt(i
, DiagramItem::M_WIRE
);
339 if (item
->Frame().Intersects(updateRect
))
340 item
->Draw(updateRect
);
344 if (whichType
& DiagramItem::M_BOX
) {
345 for (int32 i
= CountItems(DiagramItem::M_BOX
) - 1; i
>= 0; i
--) {
346 DiagramItem
*item
= ItemAt(i
, DiagramItem::M_BOX
);
347 if (item
&& item
->Frame().Intersects(updateRect
)) {
348 item
->Draw(updateRect
);
350 updateRegion
->Exclude(item
->Frame());
355 if (whichType
& DiagramItem::M_ENDPOINT
) {
356 for (int32 i
= CountItems(DiagramItem::M_ENDPOINT
) - 1; i
>= 0; i
--) {
357 DiagramItem
*item
= ItemAt(i
, DiagramItem::M_ENDPOINT
);
358 if (item
&& item
->Frame().Intersects(updateRect
))
359 item
->Draw(updateRect
);
365 /*! Returns in outRegion the \param region of items that lay "over" the given
366 DiagramItem in \param which; returns false if no items are above or the item
370 DiagramItemGroup::GetClippingAbove(DiagramItem
*which
, BRegion
*region
)
372 D_METHOD(("DiagramItemGroup::GetClippingAbove()\n"));
374 if (which
&& region
) {
375 switch (which
->type()) {
376 case DiagramItem::M_BOX
:
378 int32 index
= fBoxes
->IndexOf(which
);
379 if (index
>= 0) { // the item was found
380 BRect r
= which
->Frame();
381 for (int32 i
= 0; i
< index
; i
++) {
382 DiagramItem
*item
= ItemAt(i
, DiagramItem::M_BOX
);
383 if (item
&& item
->Frame().Intersects(r
)) {
384 region
->Include(item
->Frame() & r
);
392 case DiagramItem::M_WIRE
:
394 BRect r
= which
->Frame();
395 for (uint32 i
= 0; i
< CountItems(DiagramItem::M_BOX
); i
++) {
396 DiagramItem
*item
= ItemAt(i
, DiagramItem::M_BOX
);
397 if (item
&& item
->Frame().Intersects(r
)) {
398 region
->Include(item
->Frame() & r
);
411 // #pragma mark - selection accessors
414 /*! Returns the type of DiagramItems in the current selection
415 (currently only one type at a time is supported!)
418 DiagramItemGroup::SelectedType() const
420 D_METHOD(("DiagramItemGroup::SelectedType()\n"));
421 if (CountSelectedItems() > 0)
422 return SelectedItemAt(0)->type();
428 //! Returns the number of items in the current selection
430 DiagramItemGroup::CountSelectedItems() const
432 D_METHOD(("DiagramItemGroup::CountSelectedItems()\n"));
434 return fSelection
->CountItems();
440 /*! Returns a pointer to the item in the list which is
441 at the given \param index; if none is found, this function
445 DiagramItemGroup::SelectedItemAt(uint32 index
) const
447 D_METHOD(("DiagramItemGroup::SelectedItemAt()\n"));
449 return static_cast<DiagramItem
*>(fSelection
->ItemAt(index
));
455 // #pragma mark - selection related operations
458 /*! Selects an item, optionally replacing the complete former
459 selection. If the type of the item to be selected differs
460 from the type of items currently selected, this methods
461 automatically replaces the former selection
464 DiagramItemGroup::SelectItem(DiagramItem
* which
, bool deselectOthers
)
466 D_METHOD(("DiagramItemGroup::SelectItem()\n"));
467 bool selectionChanged
= false;
468 if (which
&& !which
->isSelected() && which
->isSelectable()) {
469 // check if the item's type is the same as of the other
471 if (fMultiSelection
) {
472 if (which
->type() != SelectedType())
473 deselectOthers
= true;
476 // check if the former selection has to be deselected
477 if (deselectOthers
|| !fMultiSelection
) {
478 while (CountSelectedItems() > 0)
479 DeselectItem(SelectedItemAt(0));
483 if (deselectOthers
|| CountSelectedItems() == 0)
486 which
->selectAdding();
488 fSelection
->AddItem(which
);
489 selectionChanged
= true;
492 // resort the lists if necessary
493 if (selectionChanged
) {
494 SortItems(which
->type(), compareSelectionTime
);
495 SortSelectedItems(compareSelectionTime
);
503 //! Simply deselects one item
505 DiagramItemGroup::DeselectItem(DiagramItem
* which
)
507 D_METHOD(("DiagramItemGroup::DeselectItem()\n"));
508 if (which
&& which
->isSelected()) {
509 fSelection
->RemoveItem(which
);
511 SortItems(which
->type(), compareSelectionTime
);
512 SortSelectedItems(compareSelectionTime
);
520 //! Selects all items of the given \param itemType
522 DiagramItemGroup::SelectAll(uint32 itemType
)
524 D_METHOD(("DiagramItemGroup::SelectAll()\n"));
525 bool selectionChanged
= false;
526 if (fTypes
& itemType
) {
527 for (uint32 i
= 0; i
< CountItems(itemType
); i
++) {
528 if (SelectItem(ItemAt(i
, itemType
), false))
529 selectionChanged
= true;
533 return selectionChanged
;
537 //! Deselects all items of the given \param itemType
539 DiagramItemGroup::DeselectAll(uint32 itemType
)
541 D_METHOD(("DiagramItemGroup::DeselectAll()\n"));
542 bool selectionChanged
= false;
543 if (fTypes
& itemType
) {
544 for (uint32 i
= 0; i
< CountItems(itemType
); i
++) {
545 if (DeselectItem(ItemAt(i
, itemType
)))
546 selectionChanged
= true;
550 return selectionChanged
;
554 /*! Performs a quicksort on the list of selected items with the
555 provided compare function (one is already defined in the DiagramItem
559 DiagramItemGroup::SortSelectedItems(int (*compareFunc
)(const void *, const void *))
561 D_METHOD(("DiagramItemGroup::SortSelectedItems()\n"));
562 fSelection
->SortItems(compareFunc
);
566 /*! Moves all selected items by a given amount, taking
567 item alignment into account; in updateRegion the areas
568 that still require updating by the caller are returned
571 DiagramItemGroup::DragSelectionBy(float x
, float y
, BRegion
* updateRegion
)
573 D_METHOD(("DiagramItemGroup::DragSelectionBy()\n"));
574 if (SelectedType() == DiagramItem::M_BOX
) {
576 if ((x
!= 0) || (y
!= 0)) {
577 for (int32 i
= CountSelectedItems() - 1; i
>= 0; i
--) {
578 DiagramItem
*item
= dynamic_cast<DiagramItem
*>(SelectedItemAt(i
));
579 if (item
->isDraggable())
580 item
->MoveBy(x
, y
, updateRegion
);
587 //! Removes all selected items from the group
589 DiagramItemGroup::RemoveSelection()
591 D_METHOD(("DiagramItemGroup::RemoveSelection()\n"));
592 for (uint32 i
= 0; i
< CountSelectedItems(); i
++)
593 RemoveItem(SelectedItemAt(i
));
597 // #pragma mark - alignment related accessors & operations
601 DiagramItemGroup::GetItemAlignment(float *horizontal
, float *vertical
)
603 D_METHOD(("DiagramItemGroup::GetItemAlignment()\n"));
605 *horizontal
= fItemAlignment
.x
;
607 *vertical
= fItemAlignment
.y
;
611 //! Align a given point(\param x, \param y) to the current grid
613 DiagramItemGroup::Align(float *x
, float *y
) const
615 D_METHOD(("DiagramItemGroup::Align()\n"));
616 *x
= ((int)*x
/ (int)fItemAlignment
.x
) * fItemAlignment
.x
;
617 *y
= ((int)*y
/ (int)fItemAlignment
.y
) * fItemAlignment
.y
;
621 //! Align a given \param point to the current grid
623 DiagramItemGroup::Align(BPoint point
) const
625 D_METHOD(("DiagramItemGroup::Align()\n"));
626 float x
= point
.x
, y
= point
.y
;