1 /*
2  *    Copyright 2012, 2013 Thomas Schöps
3  *    Copyright 2012-2016  Kai Pastor
4  *
5  *    This file is part of OpenOrienteering.
6  *
7  *    OpenOrienteering is free software: you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation, either version 3 of the License, or
10  *    (at your option) any later version.
11  *
12  *    OpenOrienteering is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #ifdef QT_PRINTSUPPORT_LIB
23 
24 #include "print_tool.h"
25 
26 #include <QMouseEvent>
27 #include <QPainter>
28 #include <QPainterPath>
29 
30 #include "core/map.h"
31 #include "core/map_printer.h"
32 #include "gui/map/map_editor.h"
33 #include "gui/map/map_widget.h"
34 
35 
36 namespace OpenOrienteering {
37 
PrintTool(MapEditorController * editor,MapPrinter * map_printer)38 PrintTool::PrintTool(MapEditorController* editor, MapPrinter* map_printer)
39 : MapEditorTool { editor, Other, nullptr }
40 , map_printer   { map_printer }
41 , region        { Unknown }
42 , dragging      { false }
43 {
44 	Q_ASSERT(editor);
45 	Q_ASSERT(map_printer);
46 
47 	connect(map_printer, &MapPrinter::printAreaChanged, this, &PrintTool::updatePrintArea);
48 	connect(map_printer, &MapPrinter::pageFormatChanged, this, &PrintTool::updatePrintArea);
49 	// Page breaks may change upon scale changes.
50 	connect(map_printer, &MapPrinter::optionsChanged, this, &PrintTool::updatePrintArea);
51 }
52 
~PrintTool()53 PrintTool::~PrintTool()
54 {
55 	// nothing, not inlined
56 }
57 
init()58 void PrintTool::init()
59 {
60 	setStatusBarText(tr("<b>Drag</b>: Move the map, the print area or the area's borders. "));
61 	updatePrintArea();
62 
63 	MapEditorTool::init();
64 }
65 
getCursor() const66 const QCursor& PrintTool::getCursor() const
67 {
68 	static auto const cursor = QCursor{ Qt::ArrowCursor };
69 	return cursor;
70 }
71 
mousePressEvent(QMouseEvent * event,const MapCoordF & map_coord,MapWidget * widget)72 bool PrintTool::mousePressEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget)
73 {
74 	if (event->button() == Qt::LeftButton)
75 	{
76 		mouseMoved(map_coord, widget);
77 		dragging = true;
78 		click_pos = event->pos();
79 		click_pos_map = map_coord;
80 		if (region == Inside || region == Outside)
81 			widget->setCursor(Qt::ClosedHandCursor);
82 		return true;
83 	}
84 
85 	if (event->button() == Qt::RightButton)
86 	{
87 		return true; // disable context menu
88 	}
89 
90 	return false;
91 }
92 
mouseMoveEvent(QMouseEvent * event,const MapCoordF & map_coord,MapWidget * widget)93 bool PrintTool::mouseMoveEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget)
94 {
95 	if (dragging && event->buttons() & Qt::LeftButton)
96 	{
97 		if (region == Outside)
98 		{
99 			mapWidget()->getMapView()->setPanOffset(event->pos() - click_pos);
100 		}
101 		else
102 		{
103 			updateDragging(map_coord);
104 		}
105 		return true;
106 	}
107 
108 	mouseMoved(map_coord, widget);
109 	return false;
110 }
111 
mouseReleaseEvent(QMouseEvent * event,const MapCoordF & map_coord,MapWidget * widget)112 bool PrintTool::mouseReleaseEvent(QMouseEvent* event, const MapCoordF& map_coord, MapWidget* widget)
113 {
114 	if (dragging && event->button() == Qt::LeftButton)
115 	{
116 		if (region == Outside)
117 		{
118 			mapWidget()->getMapView()->finishPanning(event->pos() - click_pos);
119 		}
120 		else
121 		{
122 			updateDragging(map_coord);
123 		}
124 		dragging = false;
125 		region = Unknown; // forces mouseMoved() to update cursor and status
126 		mouseMoved(map_coord, widget);
127 		return true;
128 	}
129 
130 	return false;
131 }
132 
draw(QPainter * painter,MapWidget * widget)133 void PrintTool::draw(QPainter* painter, MapWidget* widget)
134 {
135 	painter->save();
136 
137 	QRect view_area = QRect(0, 0, widget->width(), widget->height());
138 	QRect print_area = widget->mapToViewport(map_printer->getPrintArea()).toRect();
139 	qreal scale_adjustment = map_printer->getScaleAdjustment();
140 	QSizeF page_size = widget->mapToViewport(map_printer->getPageFormat().page_rect).size() / scale_adjustment;
141 
142 	// Strongly darken the region outside the print area
143 	painter->setBrush(QColor(0, 0, 0, 160));
144 	painter->setPen(Qt::NoPen);
145 	QPainterPath outside_path;
146 	outside_path.addRect(view_area);
147 	outside_path.addRect(view_area.intersected(print_area));
148 	painter->drawPath(outside_path);
149 
150 	Q_ASSERT(!map_printer->horizontalPagePositions().empty());
151 	Q_ASSERT(!map_printer->verticalPagePositions().empty());
152 	QPointF outer_top_left = widget->mapToViewport( QPointF(
153 	  map_printer->horizontalPagePositions().front(),
154 	  map_printer->verticalPagePositions().front() ) );
155 	QPointF outer_bottom_right = widget->mapToViewport( QPointF(
156 	  map_printer->horizontalPagePositions().back(),
157 	  map_printer->verticalPagePositions().back() ) );
158 	outer_bottom_right += QPointF(page_size.width(), page_size.height());
159 	QRectF outer_rect(outer_top_left, outer_bottom_right);
160 
161 	// Draw red lines for page breaks
162 	QColor top_left_margin_color(255, 0, 0, 160);
163 	QColor bottom_right_margin_color(255, 128, 128, 160);
164 	painter->setBrush(Qt::NoBrush);
165 
166 	// The relative length of the page dimensions to be actual drawn.
167 	QSizeF drawing_size(page_size * scale_adjustment);
168 	if (map_printer->horizontalPagePositions().size() > 1)
169 	{
170 		drawing_size.setWidth(drawing_size.width() * 0.9 * (
171 		  map_printer->horizontalPagePositions()[1] -
172 		  map_printer->horizontalPagePositions()[0] ) /
173 		  map_printer->getPageFormat().page_rect.width() );
174 	}
175 	if (map_printer->verticalPagePositions().size() > 1)
176 	{
177 		drawing_size.setHeight(drawing_size.height() * 0.9 * (
178 		  map_printer->verticalPagePositions()[1] -
179 		  map_printer->verticalPagePositions()[0] ) /
180 		  map_printer->getPageFormat().page_rect.height() );
181 	}
182 
183 	int h = 0;
184 	for (auto hpos : map_printer->horizontalPagePositions())
185 	{
186 		++h;
187 		if (h > 100) // Don't visualize too many pages.
188 			break;
189 
190 		int v = 0;
191 		for (auto vpos : map_printer->verticalPagePositions())
192 		{
193 			++v;
194 			if (h+v > 100) // Don't visualize too many pages.
195 				break;
196 
197 			QPointF pos = widget->mapToViewport(MapCoordF(hpos, vpos));
198 			painter->setPen(top_left_margin_color);
199 			// Left vertical line
200 			painter->drawLine(QLineF{pos.x(), pos.y(), pos.x(), pos.y()+drawing_size.height()});
201 			// Top horizontal line
202 			painter->drawLine(QLineF{pos.x(), pos.y(), pos.x()+drawing_size.width(), pos.y()});
203 
204 			pos += QPointF(page_size.width(), page_size.height());
205 			painter->setPen(bottom_right_margin_color);
206 			// Right vertical line
207 			painter->drawLine(QLineF{pos.x(), pos.y()-drawing_size.height(), pos.x(), pos.y()});
208 			// Bottom horizontal line
209 			painter->drawLine(QLineF{pos.x()-drawing_size.width(), pos.y(), pos.x(), pos.y()});
210 		}
211 	}
212 
213 	painter->setPen(top_left_margin_color);
214 	painter->drawLine(QLineF{outer_rect.left(), outer_rect.top(), outer_rect.left(), outer_rect.bottom()});
215 	painter->drawLine(QLineF{outer_rect.left(), outer_rect.top(), outer_rect.right(), outer_rect.top()});
216 	painter->setPen(bottom_right_margin_color);
217 	painter->drawLine(QLineF{outer_rect.right(), outer_rect.top(), outer_rect.right(), outer_rect.bottom()});
218 	painter->drawLine(QLineF{outer_rect.left(), outer_rect.bottom(), outer_rect.right(), outer_rect.bottom()});
219 
220 	QRectF print_area_f(print_area);
221 	QPen marker(Qt::red);
222 	marker.setWidth(4);
223 	painter->setPen(marker);
224 	painter->setOpacity(0.5);
225 	if (region == Inside)
226 	{
227 		painter->drawRect(print_area_f);
228 	}
229 	if (region & LeftBorder)
230 	{
231 		painter->drawLine(print_area_f.topLeft(), print_area_f.bottomLeft());
232 	}
233 	if (region & TopBorder)
234 	{
235 		painter->drawLine(print_area_f.topLeft(), print_area_f.topRight());
236 	}
237 	if (region & RightBorder)
238 	{
239 		painter->drawLine(print_area_f.topRight(), print_area_f.bottomRight());
240 	}
241 	if (region & BottomBorder)
242 	{
243 		painter->drawLine(print_area_f.bottomLeft(), print_area_f.bottomRight());
244 	}
245 
246 	painter->restore();
247 }
248 
updatePrintArea()249 void PrintTool::updatePrintArea()
250 {
251 	// The print area visualization is updated by redrawing the whole map.
252 	// TODO: Replace with a more explicit way of marking the whole map area as dirty.
253 	editor->getMap()->setDrawingBoundingBox(QRectF(-1000000, -1000000, 2000000, 2000000), 0);
254 }
255 
updateDragging(const MapCoordF & mouse_pos_map)256 void PrintTool::updateDragging(const MapCoordF& mouse_pos_map)
257 {
258 	QPointF delta = QPointF(mouse_pos_map - click_pos_map);
259 	QRectF area = map_printer->getPrintArea();
260 	switch (region)
261 	{
262 		case Inside:
263 			area.moveTopLeft(area.topLeft() + delta);
264 			break;
265 		case LeftBorder:
266 			area.setLeft(area.left() + delta.rx());
267 			break;
268 		case TopLeftCorner:
269 			area.setTopLeft(area.topLeft() + delta);
270 			break;
271 		case TopBorder:
272 			area.setTop(area.top() + delta.ry());
273 			break;
274 		case TopRightCorner:
275 			area.setTopRight(area.topRight() + delta);
276 			break;
277 		case RightBorder:
278 			area.setRight(area.right() + delta.rx());
279 			break;
280 		case BottomRightCorner:
281 			area.setBottomRight(area.bottomRight() + delta);
282 			break;
283 		case BottomBorder:
284 			area.setBottom(area.bottom() + delta.ry());
285 			break;
286 		case BottomLeftCorner:
287 			area.setBottomLeft(area.bottomLeft() + delta);
288 			break;
289 		case Outside:
290 			Q_ASSERT(false); // Handled outside.
291 		case Unknown:
292 			; // Nothing
293 	}
294 
295 	if (area.left() < area.right() && area.top() < area.bottom())
296 	{
297 		map_printer->setPrintArea(area);
298 		click_pos_map = mouse_pos_map;
299 	}
300 }
301 
mouseMoved(const MapCoordF & mouse_pos_map,MapWidget * widget)302 void PrintTool::mouseMoved(const MapCoordF& mouse_pos_map, MapWidget* widget)
303 {
304 	Q_ASSERT(!dragging); // No change while dragging!
305 
306 	static const qreal margin_width = 16.0;
307 	static const qreal outer_margin = 8.0;
308 
309 	QRectF print_area = widget->mapToViewport(map_printer->getPrintArea());
310 	print_area.adjust(-outer_margin, -outer_margin, outer_margin, outer_margin);
311 	QPointF mouse_pos = widget->mapToViewport(mouse_pos_map);
312 
313 	int new_region = Outside;
314 	if (print_area.contains(mouse_pos))
315 	{
316 		new_region = Inside;
317 
318 		if (mouse_pos.rx() < print_area.left() + margin_width)
319 		{
320 			new_region |= LeftBorder;
321 		}
322 		else if (mouse_pos.rx() > print_area.right() - margin_width)
323 		{
324 			new_region |=  RightBorder;
325 		}
326 
327 		if (mouse_pos.ry() < print_area.top() + margin_width)
328 		{
329 			new_region |= TopBorder;
330 		}
331 		else if (mouse_pos.ry() > print_area.bottom() - margin_width)
332 		{
333 			new_region |= BottomBorder;
334 		}
335 	}
336 
337 	if (new_region != region)
338 	{
339 		region = InteractionRegion(new_region);
340 
341 		switch (region)
342 		{
343 			case Inside:
344 				setStatusBarText(tr("<b>Drag</b>: Move the print area. "));
345 				widget->setCursor(Qt::OpenHandCursor);
346 				break;
347 			case Outside:
348 				setStatusBarText(tr("<b>Drag</b>: Move the map. "));
349 				widget->setCursor(Qt::ArrowCursor);
350 				break;
351 			case LeftBorder:
352 			case RightBorder:
353 				setStatusBarText(tr("<b>Drag</b>: Move the print area's border. "));
354 				widget->setCursor(Qt::SizeHorCursor);
355 				break;
356 			case TopBorder:
357 			case BottomBorder:
358 				setStatusBarText(tr("<b>Drag</b>: Move the print area's border. "));
359 				widget->setCursor(Qt::SizeVerCursor);
360 				break;
361 			case TopLeftCorner:
362 			case BottomRightCorner:
363 				setStatusBarText(tr("<b>Drag</b>: Move the print area's borders. "));
364 				widget->setCursor(Qt::SizeFDiagCursor);
365 				break;
366 			case TopRightCorner:
367 			case BottomLeftCorner:
368 				setStatusBarText(tr("<b>Drag</b>: Move the print area's borders. "));
369 				widget->setCursor(Qt::SizeBDiagCursor);
370 				break;
371 			case Unknown:
372 				setStatusBarText(tr("<b>Drag</b>: Move the map, the print area or the area's borders. "));
373 				widget->setCursor(Qt::ArrowCursor);
374 		}
375 
376 		updatePrintArea();
377 	}
378 }
379 
380 
381 }  // namespace OpenOrienteering
382 
383 #endif  // QT_PRINTSUPPORT_LIB
384