1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 /***************************************************************************
8 copyright : (C) 2005 by Craig Bradney
9 email : cbradney@zip.com.au
10 ***************************************************************************/
11
12 /***************************************************************************
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 ***************************************************************************/
20
21 #include "sclimits.h"
22 #include "scribusdoc.h"
23 #include "selection.h"
24 #include <QDebug>
25
Selection(QObject * parent)26 Selection::Selection(QObject* parent) :
27 QObject(parent),
28 m_isGUISelection(false),
29 m_delaySignals(0),
30 m_sigSelectionChanged(false)
31 {
32 m_groupX = m_groupY = m_groupW = m_groupH = 0;
33 m_visualGX = m_visualGY = m_visualGW = m_visualGH = 0;
34 }
35
Selection(QObject * parent,bool guiSelection)36 Selection::Selection(QObject* parent, bool guiSelection) :
37 QObject(parent),
38 m_isGUISelection(guiSelection),
39 m_delaySignals(0),
40 m_sigSelectionChanged(false)
41 {
42 m_groupX = m_groupY = m_groupW = m_groupH = 0;
43 m_visualGX = m_visualGY = m_visualGW = m_visualGH = 0;
44 }
45
Selection(const Selection & other)46 Selection::Selection(const Selection& other) :
47 QObject(other.parent()),
48 m_SelList(other.m_SelList),
49 // We do not copy m_isGUISelection as :
50 // 1) copy ctor is used for temporary selections
51 // 2) having two GUI selections for same doc should really be avoided
52 m_isGUISelection(false),
53 // We do not copy m_delaySignals as that can potentially
54 // cause much trouble balancing delaySignalOff/On right
55 m_delaySignals(0),
56 m_sigSelectionChanged(other.m_sigSelectionChanged)
57 {
58 if (m_isGUISelection && !m_SelList.isEmpty())
59 {
60 m_SelList[0]->connectToGUI();
61 m_SelList[0]->emitAllToGUI();
62 m_SelList[0]->setSelected(true);
63 emit selectionChanged();
64 }
65 m_groupX = other.m_groupX;
66 m_groupY = other.m_groupY;
67 m_groupW = other.m_groupW;
68 m_groupH = other.m_groupH;
69 m_visualGX = other.m_visualGX;
70 m_visualGY = other.m_visualGY;
71 m_visualGW = other.m_visualGW;
72 m_visualGH = other.m_visualGH;
73 }
74
operator =(const Selection & other)75 Selection& Selection::operator=(const Selection &other)
76 {
77 if (&other == this)
78 return *this;
79 if (m_isGUISelection)
80 {
81 SelectionList::Iterator itend = m_SelList.end();
82 for (SelectionList::Iterator it = m_SelList.begin(); it != itend; ++it)
83 (*it)->setSelected(false);
84 }
85 m_SelList = other.m_SelList;
86 // Do not copy m_isGUISelection for consistency with cpy ctor
87 /* m_isGUISelection = other.m_isGUISelection; */
88 // We do not copy m_delaySignals as that can potentially
89 // cause much trouble balancing delaySignalOff/On right
90 m_sigSelectionChanged = other.m_sigSelectionChanged;
91 if (m_isGUISelection && !m_SelList.isEmpty())
92 {
93 m_SelList[0]->connectToGUI();
94 m_SelList[0]->emitAllToGUI();
95 m_SelList[0]->setSelected(true);
96 emit selectionChanged();
97 }
98 return *this;
99 }
100
copy(Selection & other,bool emptyOther)101 void Selection::copy(Selection& other, bool emptyOther)
102 {
103 if (&other == this)
104 return;
105 if (m_isGUISelection)
106 {
107 SelectionList::Iterator itend = m_SelList.end();
108 for (SelectionList::Iterator it = m_SelList.begin(); it != itend; ++it)
109 (*it)->setSelected(false);
110 }
111 m_SelList = other.m_SelList;
112 if (m_isGUISelection)
113 m_sigSelectionChanged = true;
114 if (emptyOther)
115 other.clear();
116 sendSignals();
117 }
118
~Selection()119 Selection::~Selection()
120 {
121 }
122
clear()123 bool Selection::clear()
124 {
125 if (!m_SelList.isEmpty())
126 {
127 SelectionList::Iterator itend = m_SelList.end();
128 SelectionList::Iterator it = m_SelList.begin();
129 while (it != itend)
130 {
131 (*it)->isSingleSel=false;
132 if (m_isGUISelection)
133 {
134 (*it)->setSelected(false);
135 (*it)->disconnectFromGUI();
136 }
137 ++it;
138 }
139 }
140 m_SelList.clear();
141 m_sigSelectionChanged = true;
142 sendSignals();
143 return true;
144 }
145
connectItemToGUI()146 bool Selection::connectItemToGUI()
147 {
148 if (!m_isGUISelection || m_SelList.isEmpty())
149 return false;
150
151 QPointer<PageItem> pi = m_SelList.first();
152 //Quick check to see if the pointer is nullptr, if its nullptr, we should remove it from the list now
153 while (pi.isNull())
154 {
155 m_SelList.removeAll(pi);
156 if (m_SelList.isEmpty())
157 break;
158 pi = m_SelList.first();
159 }
160
161 if (pi.isNull())
162 return false;
163
164 bool ret = pi->connectToGUI();
165 pi->emitAllToGUI();
166 m_sigSelectionChanged = true;
167
168 sendSignals(false);
169 return ret;
170 }
171
disconnectAllItemsFromGUI()172 bool Selection::disconnectAllItemsFromGUI()
173 {
174 if (!m_isGUISelection || m_SelList.isEmpty())
175 return false;
176 SelectionList::Iterator it2end = m_SelList.end();
177 SelectionList::Iterator it2 = m_SelList.begin();
178 while (it2 != it2end)
179 {
180 (*it2)->disconnectFromGUI();
181 ++it2;
182 }
183 return true;
184 }
185
addItem(PageItem * item,bool)186 bool Selection::addItem(PageItem *item, bool /*ignoreGUI*/)
187 {
188 if (item == nullptr)
189 return false;
190 bool listWasEmpty = m_SelList.isEmpty();
191 if (listWasEmpty || !m_SelList.contains(item))
192 {
193 m_SelList.append(item);
194 if (m_isGUISelection)
195 {
196 item->setSelected(true);
197 m_sigSelectionChanged = true;
198 }
199 sendSignals();
200 return true;
201 }
202 return false;
203 }
204
addItems(const QList<PageItem * > items)205 bool Selection::addItems(const QList<PageItem *> items)
206 {
207 if (items.isEmpty())
208 return false;
209
210 QList< QPointer<PageItem> > toAdd;
211 toAdd.reserve(items.count());
212 for (int i = 0; i < items.count(); ++i)
213 {
214 PageItem* item = items.at(i);
215 if (m_SelList.contains(item))
216 continue;
217 toAdd.append(item);
218 item->setSelected(true);
219 }
220
221 if (toAdd.count() <= 0)
222 return false;
223
224 m_SelList.append(toAdd);
225 if (m_isGUISelection)
226 m_sigSelectionChanged = true;
227 sendSignals();
228 return true;
229 }
230
prependItem(PageItem * item)231 bool Selection::prependItem(PageItem *item)
232 {
233 if (item == nullptr)
234 return false;
235 if (m_SelList.contains(item))
236 return false;
237
238 if (m_isGUISelection && !m_SelList.isEmpty())
239 m_SelList[0]->disconnectFromGUI();
240 m_SelList.prepend(item);
241 if (m_isGUISelection)
242 {
243 item->setSelected(true);
244 m_sigSelectionChanged = true;
245 }
246 sendSignals();
247 return true;
248 }
249
itemAt_(int index)250 PageItem *Selection::itemAt_(int index)
251 {
252 if (!m_SelList.isEmpty() && index < m_SelList.count())
253 {
254 QPointer<PageItem> pi = m_SelList[index];
255 //If not nullptr return it, otherwise remove from the list and return nullptr
256 if (!pi.isNull())
257 return pi;
258 m_SelList.removeAt(index);
259 }
260 return nullptr;
261 }
262
items() const263 QList<PageItem*> Selection::items() const
264 {
265 QList<PageItem*> selectedItems;
266 for (int i = 0; i < m_SelList.count(); ++i)
267 {
268 QPointer<PageItem> pi = m_SelList.at(i);
269 if (pi.isNull())
270 continue;
271 selectedItems.append(pi.data());
272 }
273 return selectedItems;
274 }
275
removeFirst()276 bool Selection::removeFirst()
277 {
278 if (!m_SelList.isEmpty())
279 {
280 if (m_isGUISelection && m_SelList.first())
281 m_SelList.first()->setSelected(false);
282 removeItem(m_SelList.first());
283 if (m_SelList.isEmpty())
284 return true;
285 if (m_isGUISelection)
286 m_sigSelectionChanged = true;
287 sendSignals();
288 }
289 return false;
290 }
291
removeItem(PageItem * item)292 bool Selection::removeItem(PageItem *item)
293 {
294 if (m_SelList.isEmpty() || !m_SelList.contains(item))
295 return false;
296
297 bool removeOk = (m_SelList.removeAll(item) == 1);
298 if (removeOk)
299 {
300 if (m_isGUISelection)
301 {
302 item->setSelected(false);
303 item->disconnectFromGUI();
304 }
305 item->isSingleSel = false;
306 }
307
308 if (m_isGUISelection)
309 {
310 m_sigSelectionChanged = true;
311 sendSignals();
312 }
313 return removeOk;
314 }
315
removeItemsOfLayer(int layedID)316 bool Selection::removeItemsOfLayer(int layedID)
317 {
318 if (m_SelList.isEmpty())
319 return false;
320
321 int oldSelCount = m_SelList.count();
322
323 delaySignalsOn();
324
325 int selIndex = 0;
326 while (selIndex < m_SelList.count())
327 {
328 QPointer<PageItem> pi = m_SelList.at(selIndex);
329 if (pi.isNull())
330 {
331 removeItem(itemAt(selIndex));
332 continue;
333 }
334
335 if (pi->m_layerID != layedID)
336 {
337 ++selIndex;
338 continue;
339 }
340
341 removeItem(itemAt(selIndex));
342 }
343
344 delaySignalsOff();
345
346 bool itemsRemoved = (oldSelCount != m_SelList.count());
347 return itemsRemoved;
348 }
349
replaceItem(PageItem * oldItem,PageItem * newItem)350 void Selection::replaceItem(PageItem* oldItem, PageItem* newItem)
351 {
352 delaySignalsOn();
353
354 int itemIndex = findItem(oldItem);
355 if (itemIndex >= 0)
356 m_SelList.replace(itemIndex, newItem);
357
358 delaySignalsOff();
359 }
360
takeItem(int itemIndex)361 PageItem* Selection::takeItem(int itemIndex)
362 {
363 if (m_SelList.isEmpty() || itemIndex >= m_SelList.count())
364 return nullptr;
365
366 PageItem *item = m_SelList[itemIndex];
367 bool removeOk = (m_SelList.removeAll(item) == 1);
368 if (removeOk)
369 {
370 item->isSingleSel = false;
371 if (m_isGUISelection)
372 {
373 item->setSelected(false);
374 m_sigSelectionChanged = true;
375 if (itemIndex == 0)
376 item->disconnectFromGUI();
377 }
378 sendSignals();
379 return item;
380 }
381 return nullptr;
382 }
383
getSelectedItemsByName() const384 QStringList Selection::getSelectedItemsByName() const
385 {
386 QStringList names;
387 SelectionList::ConstIterator itend = m_SelList.end();
388 for (auto it = m_SelList.begin(); it != itend ; ++it)
389 names.append((*it)->itemName());
390 return names;
391 }
392
width() const393 double Selection::width() const
394 {
395 if (m_SelList.isEmpty())
396 return 0.0;
397 double minX = std::numeric_limits<double>::max();
398 double maxX = -std::numeric_limits<double>::max();
399 double x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;
400
401 SelectionList::ConstIterator itend = m_SelList.end();
402 for (auto it = m_SelList.begin(); it != itend ; ++it)
403 {
404 (*it)->getBoundingRect(&x1, &y1, &x2, &y2);
405 if (x1 < minX)
406 minX = x1;
407 if (x2 > maxX)
408 maxX = x2;
409 }
410 return maxX - minX;
411 }
412
height() const413 double Selection::height() const
414 {
415 if (m_SelList.isEmpty())
416 return 0.0;
417 double minY = std::numeric_limits<double>::max();
418 double maxY = -std::numeric_limits<double>::max();
419 double x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0;
420
421 SelectionList::ConstIterator itend = m_SelList.end();
422 for (auto it = m_SelList.begin(); it != itend ; ++it)
423 {
424 (*it)->getBoundingRect(&x1, &y1, &x2, &y2);
425 if (y1 < minY)
426 minY = y1;
427 if (y2 > maxY)
428 maxY = y2;
429 }
430 return maxY - minY;
431 }
432
setGroupRect()433 void Selection::setGroupRect()
434 {
435 PageItem *currItem;
436 int selectedItemCount = count();
437 if (selectedItemCount == 0)
438 {
439 m_groupX = m_groupY = m_groupW = m_groupH = 0;
440 m_visualGX = m_visualGY = m_visualGW = m_visualGH = 0;
441 return;
442 }
443 double minx = std::numeric_limits<double>::max();
444 double miny = std::numeric_limits<double>::max();
445 double maxx = -std::numeric_limits<double>::max();
446 double maxy = -std::numeric_limits<double>::max();
447 double vminx = std::numeric_limits<double>::max();
448 double vminy = std::numeric_limits<double>::max();
449 double vmaxx = -std::numeric_limits<double>::max();
450 double vmaxy = -std::numeric_limits<double>::max();
451
452 for (int i = 0; i < selectedItemCount; ++i)
453 {
454 currItem = itemAt(i);
455 if (currItem->rotation() != 0.0)
456 {
457 QRectF itRect(currItem->getBoundingRect());
458 minx = qMin(minx, itRect.x());
459 miny = qMin(miny, itRect.y());
460 maxx = qMax(maxx, itRect.right());
461 maxy = qMax(maxy, itRect.bottom());
462
463 // Same for visual
464 QRectF itVisualRect(currItem->getVisualBoundingRect());
465 vminx = qMin(vminx, itVisualRect.x());
466 vminy = qMin(vminy, itVisualRect.y());
467 vmaxx = qMax(vmaxx, itVisualRect.right());
468 vmaxy = qMax(vmaxy, itVisualRect.bottom());
469 }
470 else
471 {
472 minx = qMin(minx, currItem->xPos());
473 miny = qMin(miny, currItem->yPos());
474 maxx = qMax(maxx, currItem->xPos() + currItem->width());
475 maxy = qMax(maxy, currItem->yPos() + currItem->height());
476
477 vminx = qMin(vminx, currItem->visualXPos());
478 vminy = qMin(vminy, currItem->visualYPos());
479 vmaxx = qMax(vmaxx, currItem->visualXPos() + currItem->visualWidth());
480 vmaxy = qMax(vmaxy, currItem->visualYPos() + currItem->visualHeight());
481 }
482 }
483 m_groupX = minx;
484 m_groupY = miny;
485 m_groupW = maxx - minx;
486 m_groupH = maxy - miny;
487
488 m_visualGX = vminx;
489 m_visualGY = vminy;
490 m_visualGW = vmaxx - vminx;
491 m_visualGH = vmaxy - vminy;
492 }
493
getGroupRect(double * x,double * y,double * w,double * h)494 void Selection::getGroupRect(double *x, double *y, double *w, double *h)
495 {
496 setGroupRect();
497 *x = m_groupX;
498 *y = m_groupY;
499 *w = m_groupW;
500 *h = m_groupH;
501 }
502
getGroupRect()503 QRectF Selection::getGroupRect()
504 {
505 double x, y, w, h;
506 getGroupRect(&x, &y, &w, &h);
507 return QRectF(x, y, w, h);
508 }
509
getVisualGroupRect(double * x,double * y,double * w,double * h)510 void Selection::getVisualGroupRect(double * x, double * y, double * w, double * h)
511 {
512 setGroupRect();
513 *x = m_visualGX;
514 *y = m_visualGY;
515 *w = m_visualGW;
516 *h = m_visualGH;
517 }
518
getVisualGroupRect()519 QRectF Selection::getVisualGroupRect()
520 {
521 double x, y, w, h;
522 getGroupRect(&x, &y, &w, &h);
523 return QRectF(x, y, w, h);
524 }
525
containsItemType(PageItem::ItemType type) const526 bool Selection::containsItemType(PageItem::ItemType type) const
527 {
528 if (m_SelList.isEmpty())
529 return false;
530 SelectionList::ConstIterator it = m_SelList.begin();
531 SelectionList::ConstIterator itend = m_SelList.end();
532 for (; it != itend; ++it)
533 {
534 if ((*it)->itemType() == type)
535 return true;
536 }
537 return false;
538 }
539
itemsAreSameType() const540 bool Selection::itemsAreSameType() const
541 {
542 //CB Putting count=1 before isempty test as its probably the most likely, given our view code.
543 if (m_SelList.count() == 1)
544 return true;
545 if (m_SelList.isEmpty())
546 return false;
547 SelectionList::ConstIterator it = m_SelList.begin();
548 SelectionList::ConstIterator itend = m_SelList.end();
549 PageItem::ItemType itemType = (*it)->itemType();
550 for ( ; it != itend ; ++it)
551 {
552 if ((*it)->isGroup()) // ignore GroupControl items
553 continue;
554 if ((*it)->itemType() != itemType)
555 return false;
556 }
557 return true;
558 }
559
itemsAreOnSamePage() const560 bool Selection::itemsAreOnSamePage() const
561 {
562 //CB Putting count=1 before isempty test as its probably the most likely, given our view code.
563 if (m_SelList.count() == 1)
564 return true;
565 if (m_SelList.isEmpty())
566 return false;
567 auto it = m_SelList.begin();
568 auto itend = m_SelList.end();
569 auto ownPage = (*it)->OwnPage;
570 for ( ; it != itend ; ++it)
571 {
572 if ((*it)->OwnPage != ownPage)
573 return false;
574 }
575 return true;
576 }
577
objectsLayer() const578 int Selection::objectsLayer() const
579 {
580 if (m_SelList.isEmpty())
581 return -1;
582 int layerID = m_SelList.at(0)->m_layerID;
583 for (int i = 1; i < m_SelList.count(); ++i)
584 {
585 if (m_SelList.at(i)->m_layerID != layerID)
586 {
587 layerID = -1;
588 break;
589 }
590 }
591 return layerID;
592 }
593
objectsHaveSameParent() const594 bool Selection::objectsHaveSameParent() const
595 {
596 int selectedItemCount = m_SelList.count();
597 if (selectedItemCount <= 1)
598 return true;
599
600 bool haveSameParent = true;
601 const PageItem *firstItem = itemAt(0);
602 for (int i = 1; i < selectedItemCount; ++i)
603 {
604 if (itemAt(i)->Parent != firstItem->Parent)
605 {
606 haveSameParent = false;
607 break;
608 }
609 }
610 return haveSameParent;
611 }
612
signalsDelayed()613 bool Selection::signalsDelayed()
614 {
615 return (m_isGUISelection && (m_delaySignals > 0));
616 }
617
delaySignalsOn()618 void Selection::delaySignalsOn()
619 {
620 ++m_delaySignals;
621 }
622
delaySignalsOff()623 void Selection::delaySignalsOff()
624 {
625 --m_delaySignals;
626 if (m_delaySignals <= 0)
627 sendSignals();
628 }
629
sendSignals(bool guiConnect)630 void Selection::sendSignals(bool guiConnect)
631 {
632 if (m_isGUISelection && (m_delaySignals <= 0))
633 {
634 setGroupRect();
635 // JG - We should probably add an m_sigSelectionChanged here
636 // to avoid multiple connectItemToGUI() if sendSignals() is called
637 // several times successively (but does that happen?)
638 if (guiConnect /*&& m_sigSelectionChanged*/)
639 connectItemToGUI();
640 if (m_sigSelectionChanged)
641 emit selectionChanged();
642 m_sigSelectionChanged = false;
643 }
644 }
645
646
647