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