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) 2007 by Franz Schmid                                     *
9 *   franz.schmid@altmuehlnet.de                                            *
10 *                                                                          *
11 *   This program is free software; you can redistribute it and/or modify   *
12 *   it under the terms of the GNU General Public License as published by   *
13 *   the Free Software Foundation; either version 2 of the License, or      *
14 *   (at your option) any later version.                                    *
15 *                                                                          *
16 *   This program is distributed in the hope that it will be useful,        *
17 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
18 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
19 *   GNU General Public License for more details.                           *
20 *                                                                          *
21 *   You should have received a copy of the GNU General Public License      *
22 *   along with this program; if not, write to the                          *
23 *   Free Software Foundation, Inc.,                                        *
24 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.              *
25 ****************************************************************************/
26 
27 #include "pathfinder.h"
28 
29 #include "pageitem_polygon.h"
30 #include "pathfinderdialog.h"
31 #include "selection.h"
32 #include "scribuscore.h"
33 #include "scribusview.h"
34 #include "sccolorengine.h"
35 #include "ui/propertiespalette.h"
36 #include "undomanager.h"
37 #include "undostate.h"
38 #include "util_color.h"
39 #include "util_math.h"
40 
41 #include <QMessageBox>
42 #include <QPixmap>
43 #include <QScopedPointer>
44 
pathfinder_getPluginAPIVersion()45 int pathfinder_getPluginAPIVersion()
46 {
47 	return PLUGIN_API_VERSION;
48 }
49 
pathfinder_getPlugin()50 ScPlugin* pathfinder_getPlugin()
51 {
52 	PathFinderPlugin* plug = new PathFinderPlugin();
53 	Q_CHECK_PTR(plug);
54 	return plug;
55 }
56 
pathfinder_freePlugin(ScPlugin * plugin)57 void pathfinder_freePlugin(ScPlugin* plugin)
58 {
59 	PathFinderPlugin* plug = qobject_cast<PathFinderPlugin*>(plugin);
60 	Q_ASSERT(plug);
61 	delete plug;
62 }
63 
PathFinderPlugin()64 PathFinderPlugin::PathFinderPlugin()
65 {
66 	// Set action info in languageChange, so we only have to do
67 	// it in one place.
68 	languageChange();
69 }
70 
~PathFinderPlugin()71 PathFinderPlugin::~PathFinderPlugin() {}
72 
languageChange()73 void PathFinderPlugin::languageChange()
74 {
75 	// Note that we leave the unused members unset. They'll be initialised
76 	// with their default ctors during construction.
77 	// Action name
78 	m_actionInfo.name = "PathFinder";
79 	// Action text for menu, including accel
80 	m_actionInfo.text = tr("Path Operations...");
81 	// Menu
82 	m_actionInfo.menu = "ItemPathOps";
83 	m_actionInfo.parentMenu = "Item";
84 	m_actionInfo.subMenuName = tr("Path Tools");
85 //	m_actionInfo.toolbar = "Vector";
86 //	m_actionInfo.toolBarName = tr("Vector-Tools");
87 //	m_actionInfo.iconPath1 = "pathintersection.png";
88 //	m_actionInfo.iconPath2 = "pathintersection.png";
89 	m_actionInfo.enabledOnStartup = false;
90 	m_actionInfo.notSuitableFor.append(PageItem::Line);
91 	m_actionInfo.notSuitableFor.append(PageItem::TextFrame);
92 	m_actionInfo.notSuitableFor.append(PageItem::ImageFrame);
93 	m_actionInfo.notSuitableFor.append(PageItem::PolyLine);
94 	m_actionInfo.notSuitableFor.append(PageItem::PathText);
95 	m_actionInfo.notSuitableFor.append(PageItem::LatexFrame);
96 	m_actionInfo.notSuitableFor.append(PageItem::Symbol);
97 	m_actionInfo.notSuitableFor.append(PageItem::RegularPolygon);
98 	m_actionInfo.notSuitableFor.append(PageItem::Arc);
99 	m_actionInfo.notSuitableFor.append(PageItem::Spiral);
100 	m_actionInfo.needsNumObjects = 2;
101 }
102 
fullTrName() const103 QString PathFinderPlugin::fullTrName() const
104 {
105 	return QObject::tr("PathFinder");
106 }
107 
getAboutData() const108 const ScActionPlugin::AboutData* PathFinderPlugin::getAboutData() const
109 {
110 	AboutData* about = new AboutData;
111 	Q_CHECK_PTR(about);
112 	about->authors = QString::fromUtf8("Franz Schmid <Franz.Schmid@altmuehlnet.de>");
113 	about->shortDescription = tr("Path Operations");
114 	about->description = tr("Apply fancy boolean operations to paths.");
115 	// about->version
116 	// about->releaseDate
117 	// about->copyright
118 	about->license = "GPL";
119 	return about;
120 }
121 
deleteAboutData(const AboutData * about) const122 void PathFinderPlugin::deleteAboutData(const AboutData* about) const
123 {
124 	Q_ASSERT(about);
125 	delete about;
126 }
127 
run(ScribusDoc * doc,const QString &)128 bool PathFinderPlugin::run(ScribusDoc* doc, const QString&)
129 {
130 	ScribusDoc* currDoc = doc;
131 	if (currDoc == nullptr)
132 		currDoc = ScCore->primaryMainWindow()->doc;
133 	if (currDoc->m_Selection->count() <= 1)
134 		return true;
135 
136 	PageItem *Item1 = currDoc->m_Selection->itemAt(0);
137 	PageItem *Item2 = currDoc->m_Selection->itemAt(1);
138 	QScopedPointer<PathFinderDialog> dia(new PathFinderDialog(currDoc->scMW(), currDoc, Item1, Item2));
139 	if (dia->exec() != QDialog::Accepted)
140 		return true;
141 
142 	//<<#9046
143 	UndoTransaction activeTransaction;
144 	UndoManager* undoManager = UndoManager::instance();
145 	if (UndoManager::undoEnabled())
146 		activeTransaction = undoManager->beginTransaction(Um::SelectionGroup, Um::IDocument, Um::PathOperation, "", Um::IPolygon);
147 	//>>#9046
148 
149 	int opMode = dia->opMode;
150 	if (dia->keepItem1)
151 	{
152 		PageItem *newItem;
153 		if (dia->swapped)
154 		{
155 			newItem = new PageItem_Polygon(*Item2);
156 			newItem->setSelected(false);
157 			currDoc->Items->insert(currDoc->Items->indexOf(Item2), newItem);
158 		}
159 		else
160 		{
161 			newItem = new PageItem_Polygon(*Item1);
162 			newItem->setSelected(false);
163 			currDoc->Items->insert(currDoc->Items->indexOf(Item1), newItem);
164 		}
165 		if (UndoManager::undoEnabled())
166 		{
167 			int targetIndex = (newItem->OwnPage >= 0) ? newItem->OwnPage : 0;
168 			ScItemState<PageItem*> *is = new ScItemState<PageItem*>("Create PageItem");
169 			is->set("CREATE_ITEM");
170 			is->setItem(newItem);
171 			UndoObject *target = currDoc->Pages->at(targetIndex);
172 			undoManager->action(target, is);
173 		}
174 	}
175 	if (dia->keepItem2)
176 	{
177 		PageItem *newItem;
178 		if (dia->swapped)
179 		{
180 			newItem = new PageItem_Polygon(*Item1);
181 			newItem->setSelected(false);
182 			currDoc->Items->insert(currDoc->Items->indexOf(Item1), newItem);
183 		}
184 		else
185 		{
186 			newItem = new PageItem_Polygon(*Item2);
187 			newItem->setSelected(false);
188 			currDoc->Items->insert(currDoc->Items->indexOf(Item2), newItem);
189 		}
190 		if (UndoManager::undoEnabled())
191 		{
192 			int targetIndex = (newItem->OwnPage >= 0) ? newItem->OwnPage : 0;
193 			ScItemState<PageItem*> *is = new ScItemState<PageItem*>("Create PageItem");
194 			is->set("CREATE_ITEM");
195 			is->setItem(newItem);
196 			UndoObject *target = currDoc->Pages->at(targetIndex);
197 			undoManager->action(target, is);
198 		}
199 	}
200 	if (opMode != 4)
201 	{
202 		PageItem *currItem;
203 		QPainterPath path;
204 		FPointArray points;
205 		if (dia->targetColor == 0)
206 		{
207 			currItem = Item1;
208 			if (dia->swapped)
209 			{
210 				currItem = Item2;
211 				currItem->setXYPos(Item1->xPos(), Item1->yPos());
212 				currItem->setRotation(0.0);
213 			}
214 		}
215 		else
216 		{
217 			if (dia->swapped)
218 				currItem = Item1;
219 			else
220 			{
221 				currItem = Item2;
222 				currItem->setXYPos(Item1->xPos(), Item1->yPos());
223 				currItem->setRotation(0.0);
224 			}
225 		}
226 
227 		path = dia->result;
228 		points.fromQPainterPath(path);
229 
230 		//<<#9046
231 		FPointArray oldPOLine = currItem->PoLine;
232 		FPointArray oldContourLine = currItem->ContourLine;
233 		ScItemState<QPair<QPair<FPointArray, FPointArray>, QPair<FPointArray, FPointArray> > >* state = nullptr;
234 		if (UndoManager::undoEnabled())
235 		{
236 			state = new ScItemState<QPair<QPair<FPointArray, FPointArray>, QPair<FPointArray, FPointArray> > >(Um::PathOperation);
237 			state->set("PATH_OPERATION");
238 			state->set("PATH_OP_OLD_CLIPEDITED", currItem->ClipEdited);
239 			state->set("PATH_OP_OLD_FRAMETYPE", currItem->FrameType);
240 			state->set("PATH_OP_OLD_OLDB2", currItem->OldB2);
241 			state->set("PATH_OP_OLD_OLDH2", currItem->OldH2);
242 			state->set("PATH_OP_NEW_FRAME", false);
243 			state->set("PATH_OP_NEW_CLIPEDITED", true);
244 			state->set("PATH_OP_NEW_FRAMETYPE", 3);
245 		}
246 		//>>#9046
247 
248 		int oldRotMode = currDoc->rotationMode();
249 		currItem->setRotation(0.0);
250 		currItem->PoLine = points;
251 		currItem->ClipEdited = true;
252 		currItem->FrameType = 3;
253 		currDoc->setRotationMode(0);
254 		currDoc->adjustItemSize(currItem);
255 		currDoc->setRotationMode(oldRotMode);
256 		currItem->OldB2 = currItem->width();
257 		currItem->OldH2 = currItem->height();
258 		currItem->updateClip();
259 		currItem->ContourLine = currItem->PoLine.copy();
260 
261 		//<<#9046
262 		if (UndoManager::undoEnabled())
263 		{
264 			state->set("PATH_OP_NEW_OLDB2", currItem->OldB2);
265 			state->set("PATH_OP_NEW_OLDH2", currItem->OldH2);
266 			state->setItem(qMakePair(qMakePair(oldPOLine, oldContourLine), qMakePair(currItem->PoLine, currItem->ContourLine)));
267 			undoManager->action(currItem, state);
268 		}
269 		//>>#9046
270 
271 		currDoc->m_Selection->removeItem(currItem);
272 		currDoc->itemSelection_DeleteItem();
273 	}
274 	else
275 	{
276 		QPainterPath path;
277 		FPointArray points;
278 		PageItem *newItem;
279 		double i1x = Item1->xPos();
280 		double i1y = Item1->yPos();
281 		path = dia->result;
282 		if (!path.isEmpty())
283 		{
284 			points.fromQPainterPath(path);
285 			//<<#9046
286 			FPointArray oldPOLine = Item1->PoLine;
287 			FPointArray oldContourLine = Item1->ContourLine;
288 			ScItemState<QPair<QPair<FPointArray, FPointArray>, QPair<FPointArray, FPointArray> > >* state = nullptr;
289 			if (UndoManager::undoEnabled())
290 			{
291 				state = new ScItemState<QPair<QPair<FPointArray, FPointArray>, QPair<FPointArray, FPointArray> > >(Um::PathOperation);
292 				state->set("PATH_OPERATION");
293 				state->set("PATH_OP_OLD_CLIPEDITED", Item1->ClipEdited);
294 				state->set("PATH_OP_OLD_FRAMETYPE", Item1->FrameType);
295 				state->set("PATH_OP_OLD_OLDB2", Item1->OldB2);
296 				state->set("PATH_OP_OLD_OLDH2", Item1->OldH2);
297 				state->set("PATH_OP_NEW_FRAME", false);
298 				state->set("PATH_OP_NEW_CLIPEDITED", true);
299 				state->set("PATH_OP_NEW_FRAMETYPE", 3);
300 			}
301 			//>>#9046
302 
303 			int oldRotMode = currDoc->rotationMode();
304 			Item1->setRotation(0.0);
305 			Item1->PoLine = points;
306 			Item1->ClipEdited = true;
307 			Item1->FrameType = 3;
308 			currDoc->setRotationMode(0);
309 			currDoc->adjustItemSize(Item1);
310 			currDoc->setRotationMode(oldRotMode);
311 			Item1->OldB2 = Item1->width();
312 			Item1->OldH2 = Item1->height();
313 			Item1->updateClip();
314 			Item1->ContourLine = Item1->PoLine.copy();
315 
316 			//<<#9046
317 			if (UndoManager::undoEnabled())
318 			{
319 				state->set("PATH_OP_NEW_OLDB2", Item1->OldB2);
320 				state->set("PATH_OP_NEW_OLDH2", Item1->OldH2);
321 				state->setItem(qMakePair(qMakePair(oldPOLine, oldContourLine), qMakePair(Item1->PoLine, Item1->ContourLine)));
322 				undoManager->action(Item1, state);
323 			}
324 			//>>#9046
325 		}
326 
327 		path = QPainterPath();
328 		path = dia->result1;
329 		if (!path.isEmpty())
330 		{
331 			points.fromQPainterPath(path);
332 			//<<#9046
333 			FPointArray oldPOLine = Item2->PoLine;
334 			FPointArray oldContourLine = Item2->ContourLine;
335 			ScItemState<QPair<QPair<FPointArray, FPointArray>, QPair<FPointArray, FPointArray> > >* state = nullptr;
336 			if (UndoManager::undoEnabled())
337 			{
338 				state = new ScItemState<QPair<QPair<FPointArray, FPointArray>, QPair<FPointArray, FPointArray> > >(Um::PathOperation);
339 				state->set("PATH_OPERATION");
340 				state->set("PATH_OP_OLD_CLIPEDITED", Item2->ClipEdited);
341 				state->set("PATH_OP_OLD_FRAMETYPE", Item2->FrameType);
342 				state->set("PATH_OP_OLD_OLDB2", Item2->OldB2);
343 				state->set("PATH_OP_OLD_OLDH2", Item2->OldH2);
344 				state->set("PATH_OP_NEW_FRAME", false);
345 				state->set("PATH_OP_NEW_CLIPEDITED", true);
346 				state->set("PATH_OP_NEW_FRAMETYPE", 3);
347 			}
348 			//>>#9046
349 
350 			int oldRotMode = currDoc->rotationMode();
351 			Item2->setXYPos(i1x, i1y);
352 			Item2->setRotation(0.0);
353 			Item2->PoLine = points;
354 			Item2->ClipEdited = true;
355 			Item2->FrameType = 3;
356 			currDoc->setRotationMode(0);
357 			currDoc->adjustItemSize(Item2);
358 			currDoc->setRotationMode(oldRotMode);
359 			Item2->OldB2 = Item2->width();
360 			Item2->OldH2 = Item2->height();
361 			Item2->updateClip();
362 			Item2->ContourLine = Item2->PoLine.copy();
363 
364 			//<<#9046
365 			if (UndoManager::undoEnabled())
366 			{
367 				state->set("PATH_OP_NEW_OLDB2", Item2->OldB2);
368 				state->set("PATH_OP_NEW_OLDH2", Item2->OldH2);
369 				state->setItem(qMakePair(qMakePair(oldPOLine, oldContourLine), qMakePair(Item2->PoLine, Item2->ContourLine)));
370 				undoManager->action(Item2, state);
371 			}
372 			//>>#9046
373 		}
374 
375 		path = QPainterPath();
376 		path = dia->result2;
377 		if (!path.isEmpty())
378 		{
379 			if (dia->targetColor == 0)
380 			{
381 				newItem = new PageItem_Polygon(*Item1);
382 				newItem->setXYPos(i1x, i1y);
383 			}
384 			else
385 			{
386 				newItem = new PageItem_Polygon(*Item2);
387 				newItem->setXYPos(i1x, i1y);
388 				newItem->setRotation(0.0);
389 			}
390 			currDoc->Items->append(newItem);
391 			newItem->setSelected(false);
392 
393 			points.fromQPainterPath(path);
394 
395 			int oldRotMode = currDoc->rotationMode();
396 			newItem->PoLine = points;
397 			newItem->ClipEdited = true;
398 			newItem->FrameType = 3;
399 			currDoc->setRotationMode(0);
400 			currDoc->adjustItemSize(newItem);
401 			currDoc->setRotationMode(oldRotMode);
402 			newItem->OldB2 = newItem->width();
403 			newItem->OldH2 = newItem->height();
404 			newItem->updateClip();
405 			newItem->ContourLine = newItem->PoLine.copy();
406 
407 			if (UndoManager::undoEnabled())
408 			{
409 				int targetIndex = (newItem->OwnPage >= 0) ? newItem->OwnPage : 0;
410 				ScItemState<PageItem*> *is = new ScItemState<PageItem*>("Create PageItem");
411 				is->set("CREATE_ITEM");
412 				is->setItem(newItem);
413 				UndoObject *target = currDoc->Pages->at(targetIndex);
414 				undoManager->action(target, is);
415 			}
416 
417 			if (dia->targetColor == 2)
418 			{
419 				QString fill = dia->getOtherFillColor();
420 				if (fill == CommonStrings::tr_NoneColor)
421 					fill = CommonStrings::None;
422 				newItem->setFillColor(fill);
423 				QString stroke = dia->getOtherLineColor();
424 				if (stroke == CommonStrings::tr_NoneColor)
425 					stroke = CommonStrings::None;
426 				newItem->setLineColor(stroke);
427 			}
428 		}
429 		currDoc->m_Selection->clear();
430 		currDoc->view()->deselectItems(true);
431 	}
432 
433 	currDoc->changed();
434 	currDoc->view()->DrawNew();
435 
436 	//<<#9046
437 	if (activeTransaction)
438 		activeTransaction.commit();
439 	//>>#9046
440 
441 	return true;
442 }
443