QGIS API Documentation 3.27.0-Master (f261cc1f8b)
qgsjsoneditwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsjsoneditwidget.cpp
3 --------------------------------------
4 Date : 3.5.2021
5 Copyright : (C) 2021 Damiano Lombardi
6 Email : damiano at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsjsoneditwidget.h"
17
18#include <QAction>
19#include <QClipboard>
20#include <QDesktopServices>
21#include <QJsonArray>
22#include <QLabel>
23#include <QPushButton>
24#include <QToolTip>
25#include <QUrl>
26
28 : QWidget( parent )
29 , mCopyValueAction( new QAction( tr( "Copy value" ), this ) )
30 , mCopyKeyAction( new QAction( tr( "Copy key" ), this ) )
31{
32 setupUi( this );
33
35
36 mCodeEditorJson->setReadOnly( true );
37 mCodeEditorJson->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
38 mCodeEditorJson->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded );
39 mCodeEditorJson->indicatorDefine( QsciScintilla::PlainIndicator, SCINTILLA_UNDERLINE_INDICATOR_INDEX );
40 mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_SETINDICATORCURRENT, SCINTILLA_UNDERLINE_INDICATOR_INDEX );
41 mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_SETMOUSEDWELLTIME, 400 );
42
43 mTreeWidget->setStyleSheet( QStringLiteral( "font-family: %1;" ).arg( QgsCodeEditor::getMonospaceFont().family() ) );
44
45 mTreeWidget->setContextMenuPolicy( Qt::ActionsContextMenu );
46 mTreeWidget->addAction( mCopyValueAction );
47 mTreeWidget->addAction( mCopyKeyAction );
48
49 connect( mTextToolButton, &QToolButton::clicked, this, &QgsJsonEditWidget::textToolButtonClicked );
50 connect( mTreeToolButton, &QToolButton::clicked, this, &QgsJsonEditWidget::treeToolButtonClicked );
51
52 connect( mCopyValueAction, &QAction::triggered, this, &QgsJsonEditWidget::copyValueActionTriggered );
53 connect( mCopyKeyAction, &QAction::triggered, this, &QgsJsonEditWidget::copyKeyActionTriggered );
54
55 connect( mCodeEditorJson, &QgsCodeEditorJson::textChanged, this, &QgsJsonEditWidget::codeEditorJsonTextChanged );
56 // Signal indicatorClicked is used because indicatorReleased has a bug in Scintilla and the keyboard modifier state
57 // parameter is not correct. Merge request was submittet to fix it: https://sourceforge.net/p/scintilla/code/merge-requests/26/
58 connect( mCodeEditorJson, &QsciScintilla::indicatorClicked, this, &QgsJsonEditWidget::codeEditorJsonIndicatorClicked );
59 connect( mCodeEditorJson, &QsciScintillaBase::SCN_DWELLSTART, this, &QgsJsonEditWidget::codeEditorJsonDwellStart );
60 connect( mCodeEditorJson, &QsciScintillaBase::SCN_DWELLEND, this, &QgsJsonEditWidget::codeEditorJsonDwellEnd );
61}
62
63void QgsJsonEditWidget::setJsonText( const QString &jsonText )
64{
65 mJsonText = jsonText;
66 mClickableLinkList.clear();
67
68 const QJsonDocument jsonDocument = QJsonDocument::fromJson( mJsonText.toUtf8() );
69
70 mCodeEditorJson->blockSignals( true );
71 if ( jsonDocument.isNull() )
72 {
73 mCodeEditorJson->setText( mJsonText );
74 }
75 else
76 {
77 switch ( mFormatJsonMode )
78 {
80 mCodeEditorJson->setText( jsonDocument.toJson( QJsonDocument::Indented ) );
81 break;
83 mCodeEditorJson->setText( jsonDocument.toJson( QJsonDocument::Compact ) );
84 break;
86 mCodeEditorJson->setText( mJsonText );
87 break;
88 }
89 }
90 mCodeEditorJson->blockSignals( false );
91
92 refreshTreeView( jsonDocument );
93}
94
96{
97 return mJsonText;
98}
99
101{
102 switch ( view )
103 {
104 case View::Text:
105 {
106 mStackedWidget->setCurrentWidget( mStackedWidgetPageText );
107 mTextToolButton->setChecked( true );
108 mTreeToolButton->setChecked( false );
109 }
110 break;
111 case View::Tree:
112 {
113 mStackedWidget->setCurrentWidget( mStackedWidgetPageTree );
114 mTreeToolButton->setChecked( true );
115 mTextToolButton->setChecked( false );
116 }
117 break;
118 }
119}
120
122{
123 mFormatJsonMode = formatJson;
124}
125
127{
128 mControlsWidget->setVisible( visible );
129}
130
131void QgsJsonEditWidget::textToolButtonClicked( bool checked )
132{
133 if ( checked )
135 else
137}
138
139void QgsJsonEditWidget::treeToolButtonClicked( bool checked )
140{
141 if ( checked )
143 else
145}
146
147void QgsJsonEditWidget::copyValueActionTriggered()
148{
149 if ( !mTreeWidget->currentItem() )
150 return;
151
152 const QJsonValue jsonValue = QJsonValue::fromVariant( mTreeWidget->currentItem()->data( static_cast<int>( TreeWidgetColumn::Value ), Qt::UserRole ) );
153
154 switch ( jsonValue.type() )
155 {
156 case QJsonValue::Null:
157 case QJsonValue::Bool:
158 case QJsonValue::Double:
159 case QJsonValue::Undefined:
160 QApplication::clipboard()->setText( mTreeWidget->currentItem()->text( static_cast<int>( TreeWidgetColumn::Value ) ) );
161 break;
162 case QJsonValue::String:
163 QApplication::clipboard()->setText( jsonValue.toString() );
164 break;
165 case QJsonValue::Array:
166 {
167 const QJsonDocument jsonDocument( jsonValue.toArray() );
168 QApplication::clipboard()->setText( jsonDocument.toJson() );
169 }
170 break;
171 case QJsonValue::Object:
172 {
173 const QJsonDocument jsonDocument( jsonValue.toObject() );
174 QApplication::clipboard()->setText( jsonDocument.toJson() );
175 }
176 break;
177 }
178}
179
180void QgsJsonEditWidget::copyKeyActionTriggered()
181{
182 if ( !mTreeWidget->currentItem() )
183 return;
184
185 QApplication::clipboard()->setText( mTreeWidget->currentItem()->text( static_cast<int>( TreeWidgetColumn::Key ) ) );
186}
187
188void QgsJsonEditWidget::codeEditorJsonTextChanged()
189{
190 mJsonText = mCodeEditorJson->text();
191 const QJsonDocument jsonDocument = QJsonDocument::fromJson( mJsonText.toUtf8() );
192 refreshTreeView( jsonDocument );
193}
194
195void QgsJsonEditWidget::codeEditorJsonIndicatorClicked( int line, int index, Qt::KeyboardModifiers state )
196{
197 if ( !state.testFlag( Qt::ControlModifier ) )
198 return;
199
200 const int position = mCodeEditorJson->positionFromLineIndex( line, index );
201 const int clickableLinkListIndex = mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_INDICATORVALUEAT,
202 SCINTILLA_UNDERLINE_INDICATOR_INDEX,
203 position );
204 if ( clickableLinkListIndex <= 0 )
205 return;
206
207 QDesktopServices::openUrl( mClickableLinkList.at( clickableLinkListIndex - 1 ) );
208}
209
210void QgsJsonEditWidget::codeEditorJsonDwellStart( int position, int x, int y )
211{
212 Q_UNUSED( x )
213 Q_UNUSED( y )
214
215 const int clickableLinkListIndex = mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_INDICATORVALUEAT,
216 SCINTILLA_UNDERLINE_INDICATOR_INDEX,
217 position );
218 if ( clickableLinkListIndex <= 0 )
219 return;
220
221 QToolTip::showText( QCursor::pos(),
222 tr( "%1\nCTRL + click to follow link" ).arg( mClickableLinkList.at( clickableLinkListIndex - 1 ) ) );
223}
224
225void QgsJsonEditWidget::codeEditorJsonDwellEnd( int position, int x, int y )
226{
227 Q_UNUSED( position )
228 Q_UNUSED( x )
229 Q_UNUSED( y )
230 QToolTip::hideText();
231}
232
233void QgsJsonEditWidget::refreshTreeView( const QJsonDocument &jsonDocument )
234{
235 mTreeWidget->clear();
236
237 if ( jsonDocument.isNull() )
238 {
240 mTextToolButton->setDisabled( true );
241 mTreeToolButton->setDisabled( true );
242 mTreeToolButton->setToolTip( tr( "Invalid JSON, tree view not available" ) );
243 return;
244 }
245 else
246 {
247 mTextToolButton->setEnabled( true );
248 mTreeToolButton->setEnabled( true );
249 mTreeToolButton->setToolTip( tr( "Tree view" ) );
250 }
251
252 if ( jsonDocument.isObject() )
253 {
254 const QStringList keys = jsonDocument.object().keys();
255 for ( const QString &key : keys )
256 {
257 const QJsonValue jsonValue = jsonDocument.object().value( key );
258 QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem( mTreeWidget, QStringList() << key );
259 refreshTreeViewItem( treeWidgetItem, jsonValue );
260 mTreeWidget->addTopLevelItem( treeWidgetItem );
261 mTreeWidget->expandItem( treeWidgetItem );
262 }
263 }
264 else if ( jsonDocument.isArray() )
265 {
266 for ( int index = 0; index < jsonDocument.array().size(); index++ )
267 {
268 QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem( mTreeWidget, QStringList() << QString::number( index ) );
269 refreshTreeViewItem( treeWidgetItem, jsonDocument.array().at( index ) );
270 mTreeWidget->addTopLevelItem( treeWidgetItem );
271 mTreeWidget->expandItem( treeWidgetItem );
272 }
273 }
274
275 mTreeWidget->resizeColumnToContents( static_cast<int>( TreeWidgetColumn::Key ) );
276}
277
278void QgsJsonEditWidget::refreshTreeViewItem( QTreeWidgetItem *treeWidgetItem, const QJsonValue &jsonValue )
279{
280 treeWidgetItem->setData( static_cast<int>( TreeWidgetColumn::Value ), Qt::UserRole, jsonValue.toVariant() );
281
282 switch ( jsonValue.type() )
283 {
284 case QJsonValue::Null:
285 refreshTreeViewItemValue( treeWidgetItem,
286 QStringLiteral( "null" ),
288 break;
289 case QJsonValue::Bool:
290 refreshTreeViewItemValue( treeWidgetItem,
291 jsonValue.toBool() ? QStringLiteral( "true" ) : QStringLiteral( "false" ),
293 break;
294 case QJsonValue::Double:
295 refreshTreeViewItemValue( treeWidgetItem,
296 QString::number( jsonValue.toDouble() ),
298 break;
299 case QJsonValue::String:
300 {
301 const QString jsonValueString = jsonValue.toString();
302 if ( QUrl( jsonValueString ).scheme().isEmpty() )
303 {
304 refreshTreeViewItemValue( treeWidgetItem,
305 jsonValueString,
307 }
308 else
309 {
310 QLabel *label = new QLabel( QString( "<a href='%1'>%1</a>" ).arg( jsonValueString ) );
311 label->setOpenExternalLinks( true );
312 mTreeWidget->setItemWidget( treeWidgetItem, static_cast<int>( TreeWidgetColumn::Value ), label );
313
314 mClickableLinkList.append( jsonValueString );
315 mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_SETINDICATORVALUE, static_cast< int >( mClickableLinkList.size() ) );
316 mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_INDICATORFILLRANGE,
317 mCodeEditorJson->text().indexOf( jsonValueString ),
318 jsonValueString.size() );
319 }
320 }
321 break;
322 case QJsonValue::Array:
323 {
324 const QJsonArray jsonArray = jsonValue.toArray();
325 for ( int index = 0; index < jsonArray.size(); index++ )
326 {
327 QTreeWidgetItem *treeWidgetItemChild = new QTreeWidgetItem( treeWidgetItem, QStringList() << QString::number( index ) );
328 refreshTreeViewItem( treeWidgetItemChild, jsonArray.at( index ) );
329 treeWidgetItem->addChild( treeWidgetItemChild );
330 treeWidgetItem->setExpanded( true );
331 }
332 }
333 break;
334 case QJsonValue::Object:
335 {
336 const QJsonObject jsonObject = jsonValue.toObject();
337 const QStringList keys = jsonObject.keys();
338 for ( const QString &key : keys )
339 {
340 QTreeWidgetItem *treeWidgetItemChild = new QTreeWidgetItem( treeWidgetItem, QStringList() << key );
341 refreshTreeViewItem( treeWidgetItemChild, jsonObject.value( key ) );
342 treeWidgetItem->addChild( treeWidgetItemChild );
343 treeWidgetItem->setExpanded( true );
344 }
345 }
346 break;
347 case QJsonValue::Undefined:
348 refreshTreeViewItemValue( treeWidgetItem,
349 QStringLiteral( "Undefined value" ),
351 break;
352 }
353}
354
355void QgsJsonEditWidget::refreshTreeViewItemValue( QTreeWidgetItem *treeWidgetItem, const QString &jsonValueString, const QColor &textColor )
356{
357 QLabel *label = new QLabel( jsonValueString );
358 if ( textColor.isValid() )
359 label->setStyleSheet( QStringLiteral( "color: %1;" ).arg( textColor.name() ) );
360 mTreeWidget->setItemWidget( treeWidgetItem, static_cast<int>( TreeWidgetColumn::Value ), label );
361}
@ DoubleQuote
Double quote color.
static QFont getMonospaceFont()
Returns the monospaced font to use for code editors.
static QColor color(QgsCodeEditorColorScheme::ColorRole role)
Returns the color to use in the editor for the specified role.
void setJsonText(const QString &jsonText)
Set the JSON text in the widget to jsonText.
QString jsonText() const
Returns the JSON text.
View
View mode, text or tree.
@ Tree
JSON data displayed as tree. Tree view is disabled for invalid JSON data.
@ Text
JSON data displayed as text.
QgsJsonEditWidget(QWidget *parent=nullptr)
Constructor for QgsJsonEditWidget.
void setFormatJsonMode(FormatJson formatJson)
Set the formatJson mode.
void setControlsVisible(bool visible)
Set the visibility of controls to visible.
void setView(View view) const
Set the view mode.
FormatJson
Format mode in the text view.
@ Compact
JSON data formatted as a compact one line string.
@ Disabled
JSON data is not formatted.
@ Indented
JSON data formatted with regular indentation.