QGIS API Documentation  3.25.0-Master (10b47c2603)
qgsimagedroptextedit.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Jiří Procházka (Hobrasoft)
4 ** Contact: http://www.hobrasoft.cz/
5 **
6 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
7 ** Contact: http://www.qt-project.org/legal
8 **
9 ** This library is free software; you can redistribute it and/or
10 ** modify it under the terms of the GNU Lesser General Public
11 ** License as published by the Free Software Foundation; either
12 ** version 2.1 of the License, or (at your option) any later version.
13 **
14 ** $QT_BEGIN_LICENSE:LGPL$
15 ** GNU Lesser General Public License Usage
16 ** This file is under the terms of the GNU Lesser General Public License
17 ** version 2.1 as published by the Free Software Foundation and appearing
18 ** in the file LICENSE.LGPL included in the packaging of this file.
19 ** Please review the following information to ensure the
20 ** GNU Lesser General Public License version 2.1 requirements
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22 **
23 ** In addition, as a special exception, Digia gives you certain additional
24 ** rights. These rights are described in the Digia Qt LGPL Exception
25 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30 
31 #include "qgsimagedroptextedit.h"
32 #include "qgsguiutils.h"
33 
34 #include <QMimeData>
35 #include <QBuffer>
36 #include <QFileInfo>
37 #include <QImageReader>
38 #include <QMouseEvent>
39 #include <QApplication>
40 #include <QDesktopServices>
41 
43 QgsImageDropTextEdit::QgsImageDropTextEdit( QWidget *parent )
44  : QTextEdit( parent )
45 {
46  setTextInteractionFlags( Qt::TextEditorInteraction | Qt::LinksAccessibleByMouse );
47 }
48 
49 QgsImageDropTextEdit::~QgsImageDropTextEdit() = default;
50 
51 bool QgsImageDropTextEdit::canInsertFromMimeData( const QMimeData *source ) const
52 {
53  if ( source->hasImage() || QTextEdit::canInsertFromMimeData( source ) )
54  return true;
55 
56  const QList<QUrl> urls = source->urls();
57  QStringList files;
58  files.reserve( urls.size() );
59  for ( const QUrl &url : urls )
60  {
61  QString fileName = url.toLocalFile();
62  // seems that some drag and drop operations include an empty url
63  // so we test for length to make sure we have something
64  if ( !fileName.isEmpty() )
65  {
66  files << fileName;
67  }
68  }
69 
70  bool matched = false;
71  for ( const QString &file : std::as_const( files ) )
72  {
73  QFileInfo fi( file );
74  const QList<QByteArray> formats = QImageReader::supportedImageFormats();
75  for ( const QByteArray &format : formats )
76  {
77  if ( fi.suffix().compare( format, Qt::CaseInsensitive ) == 0 )
78  {
79  matched = true;
80  break;
81  }
82  }
83  }
84 
85  return matched;
86 }
87 
88 void QgsImageDropTextEdit::insertFromMimeData( const QMimeData *source )
89 {
90  if ( source->hasImage() )
91  {
92  const QStringList formats = source->formats();
93  QString format;
94  for ( const QString &string : formats )
95  {
96  if ( string == QLatin1String( "image/bmp" ) )
97  {
98  format = QStringLiteral( "BMP" );
99  break;
100  }
101  if ( string == QLatin1String( "image/jpeg" ) )
102  {
103  format = QStringLiteral( "JPG" );
104  break;
105  }
106  if ( string == QLatin1String( "image/jpg" ) )
107  {
108  format = QStringLiteral( "JPG" );
109  break;
110  }
111  if ( string == QLatin1String( "image/gif" ) )
112  {
113  format = QStringLiteral( "GIF" );
114  break;
115  }
116  if ( string == QLatin1String( "image/png" ) )
117  {
118  format = QStringLiteral( "PNG" );
119  break;
120  }
121  if ( string == QLatin1String( "image/pbm" ) )
122  {
123  format = QStringLiteral( "PBM" );
124  break;
125  }
126  if ( string == QLatin1String( "image/pgm" ) )
127  {
128  format = QStringLiteral( "PGM" );
129  break;
130  }
131  if ( string == QLatin1String( "image/ppm" ) )
132  {
133  format = QStringLiteral( "PPM" );
134  break;
135  }
136  if ( string == QLatin1String( "image/tiff" ) )
137  {
138  format = QStringLiteral( "TIFF" );
139  break;
140  }
141  if ( string == QLatin1String( "image/xbm" ) )
142  {
143  format = QStringLiteral( "XBM" );
144  break;
145  }
146  if ( string == QLatin1String( "image/xpm" ) )
147  {
148  format = QStringLiteral( "XPM" );
149  break;
150  }
151  }
152  if ( !format.isEmpty() )
153  {
154  dropImage( qvariant_cast<QImage>( source->imageData() ), format );
155  return;
156  }
157  }
158  else
159  {
160  const QList<QUrl> urls = source->urls();
161  QStringList files;
162  files.reserve( urls.size() );
163  for ( const QUrl &url : urls )
164  {
165  if ( url.isLocalFile() )
166  {
167  QString fileName = url.toLocalFile();
168  // seems that some drag and drop operations include an empty url
169  // so we test for length to make sure we have something
170  if ( !fileName.isEmpty() )
171  {
172  files << fileName;
173  }
174  }
175  else
176  {
177  dropLink( url );
178  }
179  }
180 
181  for ( const QString &file : std::as_const( files ) )
182  {
183  const QFileInfo fi( file );
184  const QList<QByteArray> formats = QImageReader::supportedImageFormats();
185  bool isImage = false;
186  for ( const QByteArray &format : formats )
187  {
188  if ( fi.suffix().compare( format, Qt::CaseInsensitive ) == 0 )
189  {
190  const QImage image( file );
191  dropImage( image, format );
192  isImage = true;
193  break;
194  }
195  }
196  if ( !isImage )
197  {
198  dropLink( QUrl::fromLocalFile( file ) );
199  }
200  }
201  if ( !files.empty() )
202  return;
203  }
204 
205  QTextEdit::insertFromMimeData( source );
206 }
207 
208 void QgsImageDropTextEdit::mouseMoveEvent( QMouseEvent *e )
209 {
210  QTextEdit::mouseMoveEvent( e );
211  mActiveAnchor = anchorAt( e->pos() );
212  if ( !mActiveAnchor.isEmpty() && !mCursorOverride )
213  mCursorOverride = std::make_unique< QgsTemporaryCursorOverride >( Qt::PointingHandCursor );
214  else if ( mActiveAnchor.isEmpty() && mCursorOverride )
215  mCursorOverride.reset();
216 }
217 
218 void QgsImageDropTextEdit::mouseReleaseEvent( QMouseEvent *e )
219 {
220  if ( e->button() == Qt::LeftButton && !mActiveAnchor.isEmpty() )
221  {
222  QDesktopServices::openUrl( QUrl( mActiveAnchor ) );
223  if ( mCursorOverride )
224  mCursorOverride.reset();
225  mActiveAnchor.clear();
226  }
227  else
228  {
229  QTextEdit::mouseReleaseEvent( e );
230  }
231 }
232 
233 void QgsImageDropTextEdit::dropImage( const QImage &image, const QString &format )
234 {
235  QByteArray bytes;
236  QBuffer buffer( &bytes );
237  buffer.open( QIODevice::WriteOnly );
238  image.save( &buffer, format.toLocal8Bit().data() );
239  buffer.close();
240  QByteArray base64 = bytes.toBase64();
241  QByteArray base64l;
242  for ( int i = 0; i < base64.size(); i++ )
243  {
244  base64l.append( base64[i] );
245  if ( i % 80 == 0 )
246  {
247  base64l.append( "\n" );
248  }
249  }
250 
251  QTextCursor cursor = textCursor();
252  QTextImageFormat imageFormat;
253  imageFormat.setWidth( image.width() );
254  imageFormat.setHeight( image.height() );
255  imageFormat.setName( QStringLiteral( "data:image/%1;base64,%2" )
256  .arg( QStringLiteral( "%1.%2" ).arg( rand() ).arg( format ),
257  base64l.data() )
258  );
259  cursor.insertImage( imageFormat );
260 }
261 
262 void QgsImageDropTextEdit::dropLink( const QUrl &url )
263 {
264  QTextCursor cursor = textCursor();
265  cursor.insertHtml( QStringLiteral( "<a href=\"%1\">%1</a>" ).arg( url.toString() ) );
266 }
267