17#include "moc_qgsjsoneditwidget.cpp" 
   21#include <QDesktopServices> 
   30  , mCopyValueAction( new QAction( tr( 
"Copy Value" ), this ) )
 
   31  , mCopyKeyAction( new QAction( tr( 
"Copy Key" ), this ) )
 
   37  mCodeEditorJson->setReadOnly( 
true );
 
   38  mCodeEditorJson->setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
 
   39  mCodeEditorJson->setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded );
 
   40  mCodeEditorJson->indicatorDefine( QsciScintilla::PlainIndicator, SCINTILLA_UNDERLINE_INDICATOR_INDEX );
 
   41  mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_SETINDICATORCURRENT, SCINTILLA_UNDERLINE_INDICATOR_INDEX );
 
   42  mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_SETMOUSEDWELLTIME, 400 );
 
   44  mTreeWidget->setContextMenuPolicy( Qt::ActionsContextMenu );
 
   45  mTreeWidget->addAction( mCopyValueAction );
 
   46  mTreeWidget->addAction( mCopyKeyAction );
 
   48  connect( mTextToolButton, &QToolButton::clicked, 
this, &QgsJsonEditWidget::textToolButtonClicked );
 
   49  connect( mTreeToolButton, &QToolButton::clicked, 
this, &QgsJsonEditWidget::treeToolButtonClicked );
 
   51  connect( mCopyValueAction, &QAction::triggered, 
this, &QgsJsonEditWidget::copyValueActionTriggered );
 
   52  connect( mCopyKeyAction, &QAction::triggered, 
this, &QgsJsonEditWidget::copyKeyActionTriggered );
 
   54  connect( mCodeEditorJson, &QgsCodeEditorJson::textChanged, 
this, &QgsJsonEditWidget::codeEditorJsonTextChanged );
 
   57  connect( mCodeEditorJson, &QsciScintilla::indicatorClicked, 
this, &QgsJsonEditWidget::codeEditorJsonIndicatorClicked );
 
   58  connect( mCodeEditorJson, &QsciScintillaBase::SCN_DWELLSTART, 
this, &QgsJsonEditWidget::codeEditorJsonDwellStart );
 
   59  connect( mCodeEditorJson, &QsciScintillaBase::SCN_DWELLEND, 
this, &QgsJsonEditWidget::codeEditorJsonDwellEnd );
 
 
   64  return mCodeEditorJson;
 
 
   70  mClickableLinkList.clear();
 
   72  const QJsonDocument jsonDocument = QJsonDocument::fromJson( mJsonText.toUtf8() );
 
   74  mCodeEditorJson->blockSignals( 
true );
 
   75  if ( jsonDocument.isNull() )
 
   77    mCodeEditorJson->setText( mJsonText );
 
   81    switch ( mFormatJsonMode )
 
   84        mCodeEditorJson->setText( jsonDocument.toJson( QJsonDocument::Indented ) );
 
   87        mCodeEditorJson->setText( jsonDocument.toJson( QJsonDocument::Compact ) );
 
   90        mCodeEditorJson->setText( mJsonText );
 
   94  mCodeEditorJson->blockSignals( 
false );
 
   96  refreshTreeView( jsonDocument );
 
 
  110      mStackedWidget->setCurrentWidget( mStackedWidgetPageText );
 
  111      mTextToolButton->setChecked( 
true );
 
  112      mTreeToolButton->setChecked( 
false );
 
  117      mStackedWidget->setCurrentWidget( mStackedWidgetPageTree );
 
  118      mTreeToolButton->setChecked( 
true );
 
  119      mTextToolButton->setChecked( 
false );
 
 
  127  mFormatJsonMode = formatJson;
 
 
  132  mControlsWidget->setVisible( visible );
 
 
  135void QgsJsonEditWidget::textToolButtonClicked( 
bool checked )
 
  143void QgsJsonEditWidget::treeToolButtonClicked( 
bool checked )
 
  151void QgsJsonEditWidget::copyValueActionTriggered()
 
  153  if ( !mTreeWidget->currentItem() )
 
  156  const QJsonValue jsonValue = QJsonValue::fromVariant( mTreeWidget->currentItem()->data( 
static_cast<int>( TreeWidgetColumn::Value ), Qt::UserRole ) );
 
  158  switch ( jsonValue.type() )
 
  160    case QJsonValue::Null:
 
  161    case QJsonValue::Bool:
 
  162    case QJsonValue::Double:
 
  163    case QJsonValue::Undefined:
 
  164      QApplication::clipboard()->setText( mTreeWidget->currentItem()->text( 
static_cast<int>( TreeWidgetColumn::Value ) ) );
 
  166    case QJsonValue::String:
 
  167      QApplication::clipboard()->setText( jsonValue.toString() );
 
  169    case QJsonValue::Array:
 
  171      const QJsonDocument jsonDocument( jsonValue.toArray() );
 
  172      QApplication::clipboard()->setText( jsonDocument.toJson() );
 
  175    case QJsonValue::Object:
 
  177      const QJsonDocument jsonDocument( jsonValue.toObject() );
 
  178      QApplication::clipboard()->setText( jsonDocument.toJson() );
 
  184void QgsJsonEditWidget::copyKeyActionTriggered()
 
  186  if ( !mTreeWidget->currentItem() )
 
  189  QApplication::clipboard()->setText( mTreeWidget->currentItem()->text( 
static_cast<int>( TreeWidgetColumn::Key ) ) );
 
  192void QgsJsonEditWidget::codeEditorJsonTextChanged()
 
  194  mJsonText = mCodeEditorJson->text();
 
  195  const QJsonDocument jsonDocument = QJsonDocument::fromJson( mJsonText.toUtf8() );
 
  196  refreshTreeView( jsonDocument );
 
  199void QgsJsonEditWidget::codeEditorJsonIndicatorClicked( 
int line, 
int index, Qt::KeyboardModifiers state )
 
  201  if ( !state.testFlag( Qt::ControlModifier ) )
 
  204  const int position = mCodeEditorJson->positionFromLineIndex( line, index );
 
  205  const int clickableLinkListIndex = mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_INDICATORVALUEAT, SCINTILLA_UNDERLINE_INDICATOR_INDEX, position );
 
  206  if ( clickableLinkListIndex <= 0 )
 
  209  QDesktopServices::openUrl( mClickableLinkList.at( clickableLinkListIndex - 1 ) );
 
  212void QgsJsonEditWidget::codeEditorJsonDwellStart( 
int position, 
int x, 
int y )
 
  217  const int clickableLinkListIndex = mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_INDICATORVALUEAT, SCINTILLA_UNDERLINE_INDICATOR_INDEX, position );
 
  218  if ( clickableLinkListIndex <= 0 )
 
  221  QToolTip::showText( QCursor::pos(), tr( 
"%1\nCTRL + click to follow link" ).arg( mClickableLinkList.at( clickableLinkListIndex - 1 ) ) );
 
  224void QgsJsonEditWidget::codeEditorJsonDwellEnd( 
int position, 
int x, 
int y )
 
  229  QToolTip::hideText();
 
  232void QgsJsonEditWidget::refreshTreeView( 
const QJsonDocument &jsonDocument )
 
  234  mTreeWidget->clear();
 
  236  if ( jsonDocument.isNull() )
 
  239    mTextToolButton->setDisabled( 
true );
 
  240    mTreeToolButton->setDisabled( 
true );
 
  241    mTreeToolButton->setToolTip( tr( 
"Invalid JSON, tree view not available" ) );
 
  246    mTextToolButton->setEnabled( 
true );
 
  247    mTreeToolButton->setEnabled( 
true );
 
  248    mTreeToolButton->setToolTip( tr( 
"Tree view" ) );
 
  251  if ( jsonDocument.isObject() )
 
  253    const QStringList keys = jsonDocument.object().keys();
 
  254    for ( 
const QString &key : keys )
 
  256      const QJsonValue jsonValue = jsonDocument.object().value( key );
 
  257      QTreeWidgetItem *treeWidgetItem = 
new QTreeWidgetItem( mTreeWidget, QStringList() << key );
 
  258      treeWidgetItem->setFont( 0, monospaceFont() );
 
  259      refreshTreeViewItem( treeWidgetItem, jsonValue );
 
  260      mTreeWidget->addTopLevelItem( treeWidgetItem );
 
  261      mTreeWidget->expandItem( treeWidgetItem );
 
  264  else if ( jsonDocument.isArray() )
 
  266    const QJsonArray array = jsonDocument.array();
 
  267    const auto arraySize = array.size();
 
  271    constexpr decltype( arraySize ) MAX_ELTS = 200;
 
  274    if ( arraySize > MAX_ELTS )
 
  275      mEnableUrlHighlighting = 
false;
 
  276    for ( 
auto index = 
decltype( arraySize ) { 0 }; index < arraySize; index++ )
 
  278      QTreeWidgetItem *treeWidgetItem = 
new QTreeWidgetItem( mTreeWidget, QStringList() << QString::number( index ) );
 
  279      treeWidgetItem->setFont( 0, monospaceFont() );
 
  280      if ( arraySize <= MAX_ELTS || ( index < MAX_ELTS / 2 || index + MAX_ELTS / 2 > arraySize ) )
 
  282        refreshTreeViewItem( treeWidgetItem, array.at( index ) );
 
  283        mTreeWidget->addTopLevelItem( treeWidgetItem );
 
  284        mTreeWidget->expandItem( treeWidgetItem );
 
  286      else if ( index == MAX_ELTS / 2 )
 
  288        index = arraySize - MAX_ELTS / 2;
 
  289        refreshTreeViewItem( treeWidgetItem, tr( 
"... truncated ..." ) );
 
  290        mTreeWidget->addTopLevelItem( treeWidgetItem );
 
  291        mTreeWidget->expandItem( treeWidgetItem );
 
  296  mTreeWidget->resizeColumnToContents( 
static_cast<int>( TreeWidgetColumn::Key ) );
 
  299void QgsJsonEditWidget::refreshTreeViewItem( QTreeWidgetItem *treeWidgetItem, 
const QJsonValue &jsonValue )
 
  301  treeWidgetItem->setData( 
static_cast<int>( TreeWidgetColumn::Value ), Qt::UserRole, jsonValue.toVariant() );
 
  303  switch ( jsonValue.type() )
 
  305    case QJsonValue::Null:
 
  308    case QJsonValue::Bool:
 
  309      refreshTreeViewItemValue( treeWidgetItem, jsonValue.toBool() ? QStringLiteral( 
"true" ) : QStringLiteral( 
"false" ), 
QgsCodeEditor::color( 
QgsCodeEditorColorScheme::ColorRole::Keyword ) );
 
  311    case QJsonValue::Double:
 
  314    case QJsonValue::String:
 
  316      const QString jsonValueString = jsonValue.toString();
 
  317      if ( !mEnableUrlHighlighting || QUrl( jsonValueString ).scheme().isEmpty() )
 
  323        QLabel *label = 
new QLabel( QString( 
"<a href='%1'>%1</a>" ).arg( jsonValueString ) );
 
  324        label->setOpenExternalLinks( 
true );
 
  325        label->setFont( monospaceFont() );
 
  326        mTreeWidget->setItemWidget( treeWidgetItem, 
static_cast<int>( TreeWidgetColumn::Value ), label );
 
  328        mClickableLinkList.append( jsonValueString );
 
  329        mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_SETINDICATORVALUE, 
static_cast<int>( mClickableLinkList.size() ) );
 
  330        mCodeEditorJson->SendScintilla( QsciScintillaBase::SCI_INDICATORFILLRANGE, mCodeEditorJson->text().indexOf( jsonValueString ), jsonValueString.size() );
 
  334    case QJsonValue::Array:
 
  336      const QJsonArray jsonArray = jsonValue.toArray();
 
  337      const auto arraySize = jsonArray.size();
 
  341      constexpr decltype( arraySize ) MAX_ELTS = 200;
 
  344      if ( arraySize > MAX_ELTS )
 
  345        mEnableUrlHighlighting = 
false;
 
  346      for ( 
auto index = 
decltype( arraySize ) { 0 }; index < arraySize; index++ )
 
  348        QTreeWidgetItem *treeWidgetItemChild = 
new QTreeWidgetItem( treeWidgetItem, QStringList() << QString::number( index ) );
 
  349        treeWidgetItemChild->setFont( 0, monospaceFont() );
 
  350        if ( arraySize <= MAX_ELTS || ( index < MAX_ELTS / 2 || index + MAX_ELTS / 2 > arraySize ) )
 
  352          refreshTreeViewItem( treeWidgetItemChild, jsonArray.at( index ) );
 
  353          treeWidgetItem->addChild( treeWidgetItemChild );
 
  354          treeWidgetItem->setExpanded( 
true );
 
  356        else if ( index == MAX_ELTS / 2 )
 
  358          index = arraySize - MAX_ELTS / 2;
 
  359          refreshTreeViewItem( treeWidgetItemChild, tr( 
"... truncated ..." ) );
 
  360          treeWidgetItem->addChild( treeWidgetItemChild );
 
  361          treeWidgetItem->setExpanded( 
true );
 
  366    case QJsonValue::Object:
 
  368      const QJsonObject jsonObject = jsonValue.toObject();
 
  369      const QStringList keys = jsonObject.keys();
 
  370      for ( 
const QString &key : keys )
 
  372        QTreeWidgetItem *treeWidgetItemChild = 
new QTreeWidgetItem( treeWidgetItem, QStringList() << key );
 
  373        treeWidgetItemChild->setFont( 0, monospaceFont() );
 
  374        refreshTreeViewItem( treeWidgetItemChild, jsonObject.value( key ) );
 
  375        treeWidgetItem->addChild( treeWidgetItemChild );
 
  376        treeWidgetItem->setExpanded( 
true );
 
  380    case QJsonValue::Undefined:
 
  386void QgsJsonEditWidget::refreshTreeViewItemValue( QTreeWidgetItem *treeWidgetItem, 
const QString &jsonValueString, 
const QColor &textColor )
 
  388  QLabel *label = 
new QLabel( jsonValueString );
 
  389  label->setFont( monospaceFont() );
 
  391  if ( textColor.isValid() )
 
  392    label->setStyleSheet( QStringLiteral( 
"color: %1;" ).arg( textColor.name() ) );
 
  393  mTreeWidget->setItemWidget( treeWidgetItem, 
static_cast<int>( TreeWidgetColumn::Value ), label );
 
  396QFont QgsJsonEditWidget::monospaceFont()
 const 
  400  f.setPointSize( font().pointSize() );
 
Defines a color scheme for use in QgsCodeEditor widgets.
 
@ DoubleQuote
Double quote color.
 
A JSON editor based on QScintilla2.
 
A text editor based on QScintilla2.
 
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.