/ src / qt / qrimagewidget.cpp
qrimagewidget.cpp
  1  // Copyright (c) 2011-present The Bitcoin Core developers
  2  // Distributed under the MIT software license, see the accompanying
  3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4  
  5  #include <qt/qrimagewidget.h>
  6  
  7  #include <qt/guiutil.h>
  8  
  9  #include <QApplication>
 10  #include <QClipboard>
 11  #include <QDrag>
 12  #include <QFontDatabase>
 13  #include <QMenu>
 14  #include <QMimeData>
 15  #include <QMouseEvent>
 16  #include <QPainter>
 17  
 18  #include <bitcoin-build-config.h> // IWYU pragma: keep
 19  
 20  #ifdef USE_QRCODE
 21  #include <qrencode.h>
 22  #endif
 23  
 24  QRImageWidget::QRImageWidget(QWidget* parent)
 25      : QLabel(parent)
 26  {
 27      contextMenu = new QMenu(this);
 28      contextMenu->addAction(tr("&Save Image…"), this, &QRImageWidget::saveImage);
 29      contextMenu->addAction(tr("&Copy Image"), this, &QRImageWidget::copyImage);
 30  }
 31  
 32  bool QRImageWidget::setQR(const QString& data, const QString& text)
 33  {
 34  #ifdef USE_QRCODE
 35      setText("");
 36      if (data.isEmpty()) return false;
 37  
 38      // limit length
 39      if (data.length() > MAX_URI_LENGTH) {
 40          setText(tr("Resulting URI too long, try to reduce the text for label / message."));
 41          return false;
 42      }
 43  
 44      QRcode *code = QRcode_encodeString(data.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1);
 45  
 46      if (!code) {
 47          setText(tr("Error encoding URI into QR Code."));
 48          return false;
 49      }
 50  
 51      QImage qrImage = QImage(code->width + 8, code->width + 8, QImage::Format_RGB32);
 52      qrImage.fill(0xffffff);
 53      unsigned char *p = code->data;
 54      for (int y = 0; y < code->width; ++y) {
 55          for (int x = 0; x < code->width; ++x) {
 56              qrImage.setPixel(x + 4, y + 4, ((*p & 1) ? 0x0 : 0xffffff));
 57              ++p;
 58          }
 59      }
 60      QRcode_free(code);
 61  
 62      const int qr_image_size = QR_IMAGE_SIZE + (text.isEmpty() ? 0 : 2 * QR_IMAGE_MARGIN);
 63      QImage qrAddrImage(qr_image_size, qr_image_size, QImage::Format_RGB32);
 64      qrAddrImage.fill(0xffffff);
 65      {
 66          QPainter painter(&qrAddrImage);
 67          painter.drawImage(QR_IMAGE_MARGIN, 0, qrImage.scaled(QR_IMAGE_SIZE, QR_IMAGE_SIZE));
 68  
 69          if (!text.isEmpty()) {
 70              QRect paddedRect = qrAddrImage.rect();
 71              paddedRect.setHeight(QR_IMAGE_SIZE + QR_IMAGE_TEXT_MARGIN);
 72  
 73              QFont font = GUIUtil::fixedPitchFont();
 74              font.setStretch(QFont::SemiCondensed);
 75              font.setLetterSpacing(QFont::AbsoluteSpacing, 1);
 76              const qreal font_size = GUIUtil::calculateIdealFontSize(paddedRect.width() - 2 * QR_IMAGE_TEXT_MARGIN, text, font);
 77              font.setPointSizeF(font_size);
 78  
 79              painter.setFont(font);
 80              painter.drawText(paddedRect, Qt::AlignBottom | Qt::AlignCenter, text);
 81          }
 82      }
 83  
 84      setPixmap(QPixmap::fromImage(qrAddrImage));
 85  
 86      return true;
 87  #else
 88      setText(tr("QR code support not available."));
 89      return false;
 90  #endif
 91  }
 92  
 93  QImage QRImageWidget::exportImage()
 94  {
 95      if (!GUIUtil::HasPixmap(this)) {
 96          return QImage();
 97      }
 98  
 99      return this->pixmap(Qt::ReturnByValue).toImage();
100  }
101  
102  void QRImageWidget::mousePressEvent(QMouseEvent *event)
103  {
104      if (event->button() == Qt::LeftButton && GUIUtil::HasPixmap(this)) {
105          event->accept();
106          QMimeData *mimeData = new QMimeData;
107          mimeData->setImageData(exportImage());
108  
109          QDrag *drag = new QDrag(this);
110          drag->setMimeData(mimeData);
111          drag->exec();
112      } else {
113          QLabel::mousePressEvent(event);
114      }
115  }
116  
117  void QRImageWidget::saveImage()
118  {
119      if (!GUIUtil::HasPixmap(this))
120          return;
121      QString fn = GUIUtil::getSaveFileName(
122          this, tr("Save QR Code"), QString(),
123          /*: Expanded name of the PNG file format.
124              See: https://en.wikipedia.org/wiki/Portable_Network_Graphics. */
125          tr("PNG Image") + QLatin1String(" (*.png)"), nullptr);
126      if (!fn.isEmpty())
127      {
128          exportImage().save(fn);
129      }
130  }
131  
132  void QRImageWidget::copyImage()
133  {
134      if (!GUIUtil::HasPixmap(this))
135          return;
136      QApplication::clipboard()->setImage(exportImage());
137  }
138  
139  void QRImageWidget::contextMenuEvent(QContextMenuEvent *event)
140  {
141      if (!GUIUtil::HasPixmap(this))
142          return;
143      contextMenu->exec(event->globalPos());
144  }