1 //=============================================================================
2 // MuseScore
3 // Music Composition & Notation
4 //
5 // Copyright (C) 2002-2017 Werner Schweer
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License version 2
9 // as published by the Free Software Foundation and appearing in
10 // the file LICENCE.GPL
11 //=============================================================================
12
13 #include "scoreview.h"
14 #include "musescore.h"
15 #include "libmscore/score.h"
16 #include "libmscore/lasso.h"
17 #include "icons.h"
18 #include "libmscore/page.h"
19 #include "preferences.h"
20 #include "libmscore/image.h"
21 #include "libmscore/mscore.h"
22 #include "svggenerator.h"
23 #include "inspector/inspector.h"
24 #include "fotomode.h"
25
26 namespace Ms {
27
28 //---------------------------------------------------------
29 // startEdit
30 //---------------------------------------------------------
31
startEdit(EditData & ed)32 void FotoLasso::startEdit(EditData& ed)
33 {
34 Lasso::startEdit(ed);
35 QRectF view = ((ScoreView*)ed.view)->toLogical(QRect(0.0, 0.0, ed.view->geometry().width(), ed.view->geometry().height()));
36 if (bbox().isEmpty() || !view.intersects(bbox())) {
37 // rect not found - construct new rect with default size & relative position
38 qreal w = view.width();
39 qreal h = view.height();
40 QRectF rect(w * .3, h * .3, w * .4, h * .4);
41 // convert to absolute position
42 setbbox(rect.translated(view.topLeft()));
43 }
44 setVisible(false);
45 }
46
47 //---------------------------------------------------------
48 // endEdit
49 //---------------------------------------------------------
50
endEdit(EditData &)51 void FotoLasso::endEdit(EditData&)
52 {
53 setVisible(false);
54 }
55
56 //---------------------------------------------------------
57 // drawEditMode
58 //---------------------------------------------------------
59
drawEditMode(QPainter * p,EditData & ed)60 void FotoLasso::drawEditMode(QPainter* p, EditData& ed)
61 {
62 QPointF pos(canvasPos());
63 p->translate(pos);
64 draw(p);
65 p->translate(-pos);
66 Lasso::drawEditMode(p, ed);
67 }
68
69 //---------------------------------------------------------
70 // startFotoMode
71 //---------------------------------------------------------
72
startFotomode()73 void ScoreView::startFotomode()
74 {
75 if (!_foto)
76 _foto = new FotoLasso(_score);
77 else
78 _foto->setScore(_score);
79 QRectF view = toLogical(QRect(0.0, 0.0, width(), height()));
80 if (_foto->bbox().isEmpty() || !view.intersects(_foto->bbox())) {
81 // rect not found - construct new rect with default size & relative position
82 qreal w = view.width();
83 qreal h = view.height();
84 QRectF r(w * .3, h * .3, w * .4, h * .4);
85 // convert to absolute position
86 _foto->setbbox(toPhysical(r));
87 }
88 _foto->setFlag(ElementFlag::MOVABLE, true);
89 _foto->setVisible(true);
90 _score->select(_foto);
91 setEditElement(_foto);
92 QAction* a = getAction("fotomode");
93 a->setChecked(true);
94 startEdit();
95 }
96
97 //---------------------------------------------------------
98 // stopFotomode
99 //---------------------------------------------------------
100
stopFotomode()101 void ScoreView::stopFotomode()
102 {
103 QAction* a = getAction("fotomode");
104 a->setChecked(false);
105 _foto->setVisible(false);
106 endEdit();
107 update();
108 }
109
110 //---------------------------------------------------------
111 // startFotoDrag
112 //---------------------------------------------------------
113
startFotoDrag()114 void ScoreView::startFotoDrag()
115 {
116 _score->addRefresh(_foto->abbox());
117 _score->update();
118 editData.grips = 0;
119 }
120
121 //---------------------------------------------------------
122 // doDragFoto
123 // drag canvas in foto mode
124 //---------------------------------------------------------
125
doDragFoto(QMouseEvent * ev)126 void ScoreView::doDragFoto(QMouseEvent* ev)
127 {
128 _foto->setOffset(QPointF(0.0, 0.0));
129 QPointF p = toLogical(ev->pos());
130 QPointF sm = editData.startMove;
131
132 QRectF r;
133 r.setCoords(sm.x(), sm.y(), p.x(), p.y());
134
135 _foto->setbbox(r.normalized());
136
137 QRectF rr(_foto->bbox());
138 r = _matrix.mapRect(rr);
139 //QSize sz(r.size().toSize());
140 //mscore->statusBar()->showMessage(QString("%1 x %2").arg(sz.width()).arg(sz.height()), 3000);
141
142 update();
143 //mscore->showMessage("drag", 2000);
144 }
145
146 //---------------------------------------------------------
147 // endFotoDrag
148 //---------------------------------------------------------
149
endFotoDrag()150 void ScoreView::endFotoDrag()
151 {
152 qreal w = 8.0 / _matrix.m11();
153 qreal h = 8.0 / _matrix.m22();
154 QRectF r(-w*.5, -h*.5, w, h);
155 editData.grip.resize(8);
156 for (int i = 0; i < 8; ++i)
157 editData.grip[i] = r;
158 setEditElement(_foto);
159 updateGrips();
160 _score->setUpdateAll();
161 _score->update();
162 }
163
164 //---------------------------------------------------------
165 // doFotoDragEdit
166 //---------------------------------------------------------
167
doFotoDragEdit(QMouseEvent * ev)168 void ScoreView::doFotoDragEdit(QMouseEvent* ev)
169 {
170 QPointF p = toLogical(ev->pos());
171 QPointF delta = p - editData.startMove;
172 score()->addRefresh(_foto->abbox());
173
174 editData.delta = delta;
175 _foto->editDrag(editData);
176 updateGrips();
177 editData.startMove = p;
178 _score->update();
179 if (mscore->inspector())
180 mscore->inspector()->update(_foto->score());
181 }
182
183 //---------------------------------------------------------
184 // endFotoDragEdit
185 //---------------------------------------------------------
186
endFotoDragEdit()187 void ScoreView::endFotoDragEdit()
188 {
189 }
190
191 //---------------------------------------------------------
192 // fotoEditElementDragTransition
193 //---------------------------------------------------------
194
fotoEditElementDragTransition(QMouseEvent * ev)195 bool ScoreView::fotoEditElementDragTransition(QMouseEvent* ev)
196 {
197 editData.startMove = imatrix.map(QPointF(ev->pos()));
198 int i;
199 for (i = 0; i < editData.grips; ++i) {
200 if (editData.grip[i].contains(editData.startMove)) {
201 editData.curGrip = Grip(i);
202 switch (int(editData.curGrip)) {
203 case 0:
204 case 2:
205 setCursor(Qt::SizeFDiagCursor);
206 break;
207 case 1:
208 case 3:
209 setCursor(Qt::SizeBDiagCursor);
210 break;
211 case 4:
212 case 6:
213 setCursor(Qt::SizeVerCursor);
214 break;
215 case 5:
216 case 7:
217 setCursor(Qt::SizeHorCursor);
218 break;
219 }
220 updateGrips();
221 score()->update();
222 break;
223 }
224 }
225 return i != editData.grips;
226 }
227
228 //---------------------------------------------------------
229 // fotoScoreViewDragTest
230 //---------------------------------------------------------
231
fotoScoreViewDragTest(QMouseEvent * me)232 bool ScoreView::fotoScoreViewDragTest(QMouseEvent* me)
233 {
234 QPointF p(imatrix.map(QPointF(me->pos())));
235 if (_foto->bbox().contains(p))
236 return false;
237 for (int i = 0; i < editData.grips; ++i) {
238 if (editData.grip[i].contains(p))
239 return false;
240 }
241 editData.startMove = p;
242 return true;
243 }
244
245 //---------------------------------------------------------
246 // fotoScoreViewDragRectTest
247 //---------------------------------------------------------
248
fotoScoreViewDragRectTest(QMouseEvent * me)249 bool ScoreView::fotoScoreViewDragRectTest(QMouseEvent* me)
250 {
251 QPointF p(toLogical(me->pos()));
252 if (!_foto->bbox().contains(p))
253 return false;
254 for (int i = 0; i < editData.grips; ++i) {
255 if (editData.grip[i].contains(p))
256 return false;
257 }
258 editData.startMove = p;
259 return true;
260 }
261
262 //---------------------------------------------------------
263 // doDragFotoRect
264 //---------------------------------------------------------
265
doDragFotoRect(QMouseEvent * ev)266 void ScoreView::doDragFotoRect(QMouseEvent* ev)
267 {
268 QPointF p(toLogical(ev->pos()));
269 QPointF delta = p - editData.startMove;
270 score()->addRefresh(_foto->abbox());
271 _foto->setbbox(_foto->bbox().translated(delta));
272 score()->addRefresh(_foto->abbox());
273 editData.startMove = p;
274 updateGrips();
275 _score->update();
276 if (mscore->inspector())
277 mscore->inspector()->update(_foto->score());
278 }
279
280 //---------------------------------------------------------
281 // MenuEntry
282 //---------------------------------------------------------
283
284 struct MenuEntry {
285 const char* text;
286 const char* label;
287 };
288
289 static const MenuEntry resizeEntry[4] {
290 { QT_TRANSLATE_NOOP("fotomode", "Resize to A"), "resizeA" },
291 { QT_TRANSLATE_NOOP("fotomode", "Resize to B"), "resizeB" },
292 { QT_TRANSLATE_NOOP("fotomode", "Resize to C"), "resizeC" },
293 { QT_TRANSLATE_NOOP("fotomode", "Resize to D"), "resizeD" }
294 };
295
296 static const MenuEntry setSizeEntry[4] {
297 { QT_TRANSLATE_NOOP("fotomode", "Set size A"), "setA" },
298 { QT_TRANSLATE_NOOP("fotomode", "Set size B"), "setB" },
299 { QT_TRANSLATE_NOOP("fotomode", "Set size C"), "setC" },
300 { QT_TRANSLATE_NOOP("fotomode", "Set size D"), "setD" }
301 };
302
303 //---------------------------------------------------------
304 // fotoContextPopup
305 //---------------------------------------------------------
306
fotoContextPopup(QContextMenuEvent * ev)307 void ScoreView::fotoContextPopup(QContextMenuEvent* ev)
308 {
309 QPoint pos(ev->globalPos());
310 QMenu* popup = new QMenu(this);
311 popup->setSeparatorsCollapsible(false);
312 QAction* a = popup->addSeparator();
313 a->setText(tr("Image Capture"));
314
315 a = getAction("copy");
316 popup->addAction(a);
317 a = new QAction(tr("Copy with Link to Score"), this);
318 a->setData("copy-link");
319 popup->addAction(a);
320
321 popup->addSeparator();
322 a = popup->addAction(tr("Resolution (%1 DPI)…").arg(preferences.getDouble(PREF_EXPORT_PNG_RESOLUTION)));
323 a->setData("set-res");
324 QAction* bgAction = popup->addAction(tr("Transparent background"));
325 bgAction->setCheckable(true);
326 bgAction->setChecked(preferences.getBool(PREF_EXPORT_PNG_USETRANSPARENCY));
327 bgAction->setData("set-bg");
328
329 popup->addSeparator();
330 a = new QAction(tr("Auto-resize to page"), this);
331 a->setData("resizePage");
332 popup->addAction(a);
333 for (int i = 0; i < 4; ++i) {
334 a = new QAction(qApp->translate("fotomode", resizeEntry[i].text), this);
335 a->setData(resizeEntry[i].label);
336 popup->addAction(a);
337 }
338 QMenu* setSize = new QMenu(tr("Set Standard Size…"));
339 for (int i = 0; i < 4; ++i) {
340 a = new QAction(qApp->translate("fotomode", setSizeEntry[i].text), this);
341 a->setData(setSizeEntry[i].label);
342 setSize->addAction(a);
343 }
344 popup->addMenu(setSize);
345
346 popup->addSeparator();
347 a = new QAction(tr("Save As (Print Mode)…"), this);
348 a->setData("print");
349 popup->addAction(a);
350 a = new QAction(tr("Save As (Screenshot Mode)…"), this);
351 a->setData("screenshot");
352 popup->addAction(a);
353
354 a = popup->exec(pos);
355 if (a == 0)
356 return;
357 QString cmd(a->data().toString());
358 if (cmd == "print")
359 saveFotoAs(true, _foto->canvasBoundingRect());
360 else if (cmd == "screenshot")
361 saveFotoAs(false, _foto->canvasBoundingRect());
362 else if (cmd == "copy")
363 ;
364 else if (cmd == "copy-link")
365 fotoModeCopy(true);
366 else if (cmd == "set-res") {
367 bool ok;
368 double resolution = QInputDialog::getDouble(this,
369 tr("Set Output Resolution"),
370 tr("Set output resolution for PNG"),
371 preferences.getDouble(PREF_EXPORT_PNG_RESOLUTION),
372 16.0, 2400.0, 1,
373 &ok
374 );
375 if (ok) {
376 preferences.setPreference(PREF_EXPORT_PNG_RESOLUTION, resolution);
377 }
378 }
379 else if (cmd == "resizePage") {
380 QRectF r = _foto->bbox();
381 Page* page = point2page(r.center());
382 if (page) {
383 r = page->tbbox().translated(page->canvasPos());
384 _foto->setbbox(r);
385 updateGrips();
386 }
387 }
388 else if (cmd.startsWith("resize")) {
389 QString size = QSettings().value(QString("fotoSize%1").arg(cmd[6]), "50x40").toString();
390 qreal w = size.split("x")[0].toDouble();
391 qreal h = size.split("x")[1].toDouble();
392 _foto->bbox().setSize(QSizeF(w * DPMM, h * DPMM));
393 updateGrips();
394 }
395 else if (cmd.startsWith("set")) {
396 qreal w = _foto->bbox().width() / DPMM;
397 qreal h = _foto->bbox().height() / DPMM;
398 QString val(QString("%1x%2").arg(w).arg(h));
399 QSettings().setValue(QString("fotoSize%1").arg(cmd[3]), val);
400 }
401 if (bgAction->isChecked() != preferences.getBool(PREF_EXPORT_PNG_USETRANSPARENCY)) {
402 preferences.setPreference(PREF_EXPORT_PNG_USETRANSPARENCY, bgAction->isChecked());
403 }
404 }
405
406 //---------------------------------------------------------
407 // getRectImage
408 //---------------------------------------------------------
409
getRectImage(const QRectF & rect,double dpi,bool transparent,bool printMode)410 QImage ScoreView::getRectImage(const QRectF& rect, double dpi, bool transparent, bool printMode)
411 {
412 const double mag = dpi / DPI;
413 const int w = lrint(rect.width() * mag);
414 const int h = lrint(rect.height() * mag);
415
416 QImage::Format f = QImage::Format_ARGB32_Premultiplied;
417 QImage img(w, h, f);
418 img.setDotsPerMeterX(lrint((dpi * 1000) / INCH));
419 img.setDotsPerMeterY(lrint((dpi * 1000) / INCH));
420 img.fill(transparent ? 0 : 0xffffffff);
421
422 const auto pr = MScore::pixelRatio;
423 MScore::pixelRatio = 1.0 / mag;
424 QPainter p(&img);
425 paintRect(printMode, p, rect, mag);
426 MScore::pixelRatio = pr;
427
428 return img;
429 }
430
431 //---------------------------------------------------------
432 // fotoModeCopy
433 //---------------------------------------------------------
434
fotoModeCopy(bool includeLink)435 void ScoreView::fotoModeCopy(bool includeLink)
436 {
437 #if defined(Q_OS_WIN)
438 // See https://bugreports.qt.io/browse/QTBUG-11463
439 // while transparent copy/paste works fine inside musescore,
440 // it does not paste into other programs in Windows though
441 bool transparent = false; // preferences.getBool(PREF_EXPORT_PNG_USETRANSPARENCY);
442 #else
443 bool transparent = preferences.getBool(PREF_EXPORT_PNG_USETRANSPARENCY);
444 #endif
445 double convDpi = preferences.getDouble(PREF_EXPORT_PNG_RESOLUTION);
446 QRectF r(_foto->canvasBoundingRect());
447
448 QImage printer(getRectImage(r, convDpi, transparent, /* printMode */ true));
449 QApplication::clipboard()->clear();
450
451 if (includeLink) {
452 QUrl url = QUrl::fromLocalFile(score()->masterScore()->fileInfo()->canonicalFilePath());
453 QByteArray imageData;
454 QBuffer buffer(&imageData);
455 buffer.open(QIODevice::WriteOnly);
456 printer.save(&buffer, "PNG");
457 buffer.close();
458 QString html = "<a href=\"" + url.toString() + "\"><img src=\"data:image/png," + imageData.toPercentEncoding() + "\" /></a>";
459 QMimeData *mdata = new QMimeData;
460 mdata->setHtml(html);
461 QApplication::clipboard()->setMimeData(mdata);
462 // TODO: add both, with priority to html
463 //QApplication::clipboard()->setImage(printer);
464 }
465 else {
466 QApplication::clipboard()->setImage(printer);
467 }
468 }
469
470 //---------------------------------------------------------
471 // fotoRectHit
472 //---------------------------------------------------------
473
fotoRectHit(const QPoint & pos)474 bool ScoreView::fotoRectHit(const QPoint& pos)
475 {
476 QPointF p = toLogical(pos);
477 for (int i = 0; i < editData.grips; ++i) {
478 if (editData.grip[i].contains(p))
479 return false;
480 }
481 editData.startMove = p;
482 return _foto->bbox().contains(p);
483 }
484
485 //---------------------------------------------------------
486 // saveFotoAs
487 // return true on success
488 //---------------------------------------------------------
489
saveFotoAs(bool printMode,const QRectF & r)490 bool ScoreView::saveFotoAs(bool printMode, const QRectF& r)
491 {
492 QStringList fl;
493 fl.append(tr("PNG Bitmap Graphic") + " (*.png)");
494 fl.append(tr("PDF File") + " (*.pdf)");
495 fl.append(tr("Scalable Vector Graphics") + " (*.svg)");
496
497 QString selectedFilter;
498 QString filter = fl.join(";;");
499 QString fn = mscore->getFotoFilename(filter, &selectedFilter);
500
501 if (fn.isEmpty())
502 return false;
503
504 QFileInfo fi(fn);
505 mscore->lastSaveDirectory = fi.absolutePath();
506
507 QString ext;
508 if (selectedFilter.isEmpty()) {
509 ext = fi.suffix();
510 }
511 else {
512 int idx = fl.indexOf(selectedFilter);
513 if (idx != -1) {
514 static const char* extensions[] = {
515 "png",
516 "pdf",
517 "svg"
518 };
519 ext = extensions[idx];
520 }
521 }
522
523 if (ext.isEmpty()) {
524 QMessageBox::critical(mscore, tr("Save As"), tr("Cannot determine file type"));
525 return false;
526 }
527
528 ext = ext.toLower();
529 if (fi.suffix().toLower() != ext)
530 fn += "." + ext;
531
532 bool transparent = preferences.getBool(PREF_EXPORT_PNG_USETRANSPARENCY);
533 double convDpi = preferences.getDouble(PREF_EXPORT_PNG_RESOLUTION);
534 double mag = convDpi / DPI;
535
536 if (ext == "svg")
537 mag = 1; // SVG is not scaled, it's scalable.
538
539 int w = lrint(r.width() * mag);
540 int h = lrint(r.height() * mag);
541
542 double pr = MScore::pixelRatio;
543 if (ext == "pdf") {
544 QPdfWriter pdfWriter(fn);
545 pdfWriter.setResolution(preferences.getInt(PREF_EXPORT_PDF_DPI));
546 mag = pdfWriter.logicalDpiX() / DPI;
547 QSizeF size(r.width() / DPI, r.height() / DPI);
548 QPageSize ps(size, QPageSize::Inch, "", QPageSize::ExactMatch);
549 pdfWriter.setPageSize(ps);
550 pdfWriter.setPageMargins(QMarginsF(0.0, 0.0, 0.0, 0.0));
551 pdfWriter.setCreator("MuseScore Version: " VERSION);
552 pdfWriter.setTitle(fn);
553 MScore::pixelRatio = DPI / pdfWriter.logicalDpiX();
554 QPainter p(&pdfWriter);
555 MScore::pdfPrinting = true;
556 paintRect(printMode, p, r, mag);
557 MScore::pdfPrinting = false;
558 }
559 else if (ext == "svg") {
560 // note that clipping is not implemented
561 // (as of 4.8)
562 SvgGenerator printer;
563 printer.setFileName(fn);
564 printer.setTitle(_score->title());
565 printer.setSize(QSize(w, h));
566 printer.setViewBox(QRect(0, 0, w, h));
567 MScore::pixelRatio = DPI / printer.logicalDpiX();
568 QPainter p(&printer);
569 MScore::pdfPrinting = true;
570 paintRect(printMode, p, r, mag);
571 MScore::pdfPrinting = false;
572 }
573 else if (ext == "png") {
574 QImage printer(getRectImage(r, convDpi, transparent, printMode));
575 printer.save(fn, "png");
576 }
577 else
578 qDebug("unknown extension <%s>", qPrintable(ext));
579 MScore::pixelRatio = pr;
580 return true;
581 }
582
583 //---------------------------------------------------------
584 // paintRect
585 //---------------------------------------------------------
586
paintRect(bool printMode,QPainter & p,const QRectF & r,double mag)587 void ScoreView::paintRect(bool printMode, QPainter& p, const QRectF& r, double mag)
588 {
589 p.scale(mag, mag);
590 p.translate(-r.topLeft());
591 p.setRenderHint(QPainter::Antialiasing, true);
592 p.setRenderHint(QPainter::TextAntialiasing, true);
593
594 score()->setPrinting(printMode);
595
596 foreach (Page* page, _score->pages()) {
597 // QRectF pr(page->abbox());
598 QRectF pr(page->canvasBoundingRect());
599 if (pr.right() < r.left())
600 continue;
601 if (pr.left() > r.right())
602 break;
603 p.translate(page->pos());
604 QList<Element*> ell = page->items(r.translated(-page->pos()));
605 drawElements(p, ell, nullptr);
606 p.translate(-page->pos());
607 }
608
609 score()->setPrinting(false);
610 p.end();
611 }
612
613 //---------------------------------------------------------
614 // fotoDragDrop
615 //---------------------------------------------------------
616
fotoDragDrop(QMouseEvent *)617 void ScoreView::fotoDragDrop(QMouseEvent*)
618 {
619 bool printMode = true;
620 QRectF r(_foto->bbox());
621
622 QTemporaryFile tf(QDir::tempPath() + QString("/imgXXXXXX.svg"));
623 tf.setAutoRemove(false); // TODO: find out whether, where, when and how to delete it
624 tf.open();
625 tf.close();
626 qDebug("Temp File <%s>", qPrintable(tf.fileName()));
627
628 // QString fn = "/home/ws/mops.eps";
629 QString fn = tf.fileName();
630
631 int w = lrint(r.width());
632 int h = lrint(r.height());
633 SvgGenerator printer;
634 printer.setFileName(fn);
635 printer.setTitle(_score->title());
636 printer.setSize(QSize(w, h));
637 printer.setViewBox(QRect(0, 0, w, h));
638 QPainter p(&printer);
639 MScore::pdfPrinting = true;
640 paintRect(printMode, p, r, 1);
641 MScore::pdfPrinting = false;
642
643 QDrag* drag = new QDrag(this);
644 QMimeData* mimeData = new QMimeData;
645
646 QUrl url = QUrl::fromLocalFile(fn);
647 QList<QUrl> ul;
648 ul.append(url);
649 mimeData->setUrls(ul);
650
651 drag->setMimeData(mimeData);
652 drag->exec(Qt::CopyAction);
653 }
654 }
655
656