QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgscodeeditor.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscodeeditor.cpp - A base code editor for QGIS and plugins. Provides
3 a base editor using QScintilla for editors
4 --------------------------------------
5 Date : 06-Oct-2013
6 Copyright : (C) 2013 by Salvatore Larosa
7 Email : lrssvtml (at) gmail (dot) com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include "qgsapplication.h"
18#include "qgscodeeditor.h"
19#include "qgssettings.h"
20#include "qgssymbollayerutils.h"
21#include "qgsgui.h"
24
25#include <QLabel>
26#include <QWidget>
27#include <QFont>
28#include <QFontDatabase>
29#include <QDebug>
30#include <QFocusEvent>
31#include <Qsci/qscistyle.h>
32#include <QMenu>
33#include <QClipboard>
34
35QMap< QgsCodeEditorColorScheme::ColorRole, QString > QgsCodeEditor::sColorRoleToSettingsKey
36{
37 {QgsCodeEditorColorScheme::ColorRole::Default, QStringLiteral( "defaultFontColor" ) },
38 {QgsCodeEditorColorScheme::ColorRole::Keyword, QStringLiteral( "keywordFontColor" ) },
39 {QgsCodeEditorColorScheme::ColorRole::Class, QStringLiteral( "classFontColor" ) },
40 {QgsCodeEditorColorScheme::ColorRole::Method, QStringLiteral( "methodFontColor" ) },
41 {QgsCodeEditorColorScheme::ColorRole::Decoration, QStringLiteral( "decoratorFontColor" ) },
42 {QgsCodeEditorColorScheme::ColorRole::Number, QStringLiteral( "numberFontColor" ) },
43 {QgsCodeEditorColorScheme::ColorRole::Comment, QStringLiteral( "commentFontColor" ) },
44 {QgsCodeEditorColorScheme::ColorRole::CommentLine, QStringLiteral( "commentLineFontColor" ) },
45 {QgsCodeEditorColorScheme::ColorRole::CommentBlock, QStringLiteral( "commentBlockFontColor" ) },
46 {QgsCodeEditorColorScheme::ColorRole::Background, QStringLiteral( "paperBackgroundColor" ) },
47 {QgsCodeEditorColorScheme::ColorRole::Cursor, QStringLiteral( "cursorColor" ) },
48 {QgsCodeEditorColorScheme::ColorRole::CaretLine, QStringLiteral( "caretLineColor" ) },
49 {QgsCodeEditorColorScheme::ColorRole::Operator, QStringLiteral( "operatorFontColor" ) },
50 {QgsCodeEditorColorScheme::ColorRole::QuotedOperator, QStringLiteral( "quotedOperatorFontColor" ) },
51 {QgsCodeEditorColorScheme::ColorRole::Identifier, QStringLiteral( "identifierFontColor" ) },
52 {QgsCodeEditorColorScheme::ColorRole::QuotedIdentifier, QStringLiteral( "quotedIdentifierFontColor" ) },
53 {QgsCodeEditorColorScheme::ColorRole::Tag, QStringLiteral( "tagFontColor" ) },
54 {QgsCodeEditorColorScheme::ColorRole::UnknownTag, QStringLiteral( "unknownTagFontColor" ) },
55 {QgsCodeEditorColorScheme::ColorRole::SingleQuote, QStringLiteral( "singleQuoteFontColor" ) },
56 {QgsCodeEditorColorScheme::ColorRole::DoubleQuote, QStringLiteral( "doubleQuoteFontColor" ) },
57 {QgsCodeEditorColorScheme::ColorRole::TripleSingleQuote, QStringLiteral( "tripleSingleQuoteFontColor" ) },
58 {QgsCodeEditorColorScheme::ColorRole::TripleDoubleQuote, QStringLiteral( "tripleDoubleQuoteFontColor" ) },
59 {QgsCodeEditorColorScheme::ColorRole::MarginBackground, QStringLiteral( "marginBackgroundColor" ) },
60 {QgsCodeEditorColorScheme::ColorRole::MarginForeground, QStringLiteral( "marginForegroundColor" ) },
61 {QgsCodeEditorColorScheme::ColorRole::SelectionBackground, QStringLiteral( "selectionBackgroundColor" ) },
62 {QgsCodeEditorColorScheme::ColorRole::SelectionForeground, QStringLiteral( "selectionForegroundColor" ) },
63 {QgsCodeEditorColorScheme::ColorRole::MatchedBraceBackground, QStringLiteral( "matchedBraceBackground" ) },
64 {QgsCodeEditorColorScheme::ColorRole::MatchedBraceForeground, QStringLiteral( "matchedBraceColor" ) },
65 {QgsCodeEditorColorScheme::ColorRole::Edge, QStringLiteral( "edgeColor" ) },
66 {QgsCodeEditorColorScheme::ColorRole::Fold, QStringLiteral( "foldColor" ) },
67 {QgsCodeEditorColorScheme::ColorRole::Error, QStringLiteral( "stderrFontColor" ) },
68 {QgsCodeEditorColorScheme::ColorRole::ErrorBackground, QStringLiteral( "stderrBackgroundColor" ) },
69 {QgsCodeEditorColorScheme::ColorRole::FoldIconForeground, QStringLiteral( "foldIconForeground" ) },
70 {QgsCodeEditorColorScheme::ColorRole::FoldIconHalo, QStringLiteral( "foldIconHalo" ) },
71 {QgsCodeEditorColorScheme::ColorRole::IndentationGuide, QStringLiteral( "indentationGuide" ) },
72};
73
74
75QgsCodeEditor::QgsCodeEditor( QWidget *parent, const QString &title, bool folding, bool margin, QgsCodeEditor::Flags flags, QgsCodeEditor::Mode mode )
76 : QsciScintilla( parent )
77 , mWidgetTitle( title )
78 , mMargin( margin )
79 , mFlags( flags )
80 , mMode( mode )
81{
82 if ( !parent && mWidgetTitle.isEmpty() )
83 {
84 setWindowTitle( QStringLiteral( "Text Editor" ) );
85 }
86 else
87 {
88 setWindowTitle( mWidgetTitle );
89 }
90
91 if ( folding )
93
94 mSoftHistory.append( QString() );
95
96 setSciWidget();
97 setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded );
98
99 SendScintilla( SCI_SETADDITIONALSELECTIONTYPING, 1 );
100 SendScintilla( SCI_SETMULTIPASTE, 1 );
101 SendScintilla( SCI_SETVIRTUALSPACEOPTIONS, SCVS_RECTANGULARSELECTION );
102
103 SendScintilla( SCI_SETMARGINTYPEN, static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), SC_MARGIN_SYMBOL );
104 SendScintilla( SCI_SETMARGINMASKN, static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 1 << MARKER_NUMBER );
105 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 0 );
106 setAnnotationDisplay( QsciScintilla::AnnotationBoxed );
107
108 connect( QgsGui::instance(), &QgsGui::optionsChanged, this, [ = ]
109 {
110 setSciWidget();
112 } );
113
114 switch ( mMode )
115 {
117 break;
118
120 {
121 // Don't want to see the horizontal scrollbar at all
122 SendScintilla( QsciScintilla::SCI_SETHSCROLLBAR, 0 );
123
124 setWrapMode( QsciScintilla::WrapCharacter );
125 break;
126 }
127
129 {
130 // Don't want to see the horizontal scrollbar at all
131 SendScintilla( QsciScintilla::SCI_SETHSCROLLBAR, 0 );
132
133 setWrapMode( QsciScintilla::WrapCharacter );
134 SendScintilla( QsciScintilla::SCI_EMPTYUNDOBUFFER );
135 break;
136 }
137 }
138}
139
140// Workaround a bug in QScintilla 2.8.X
141void QgsCodeEditor::focusOutEvent( QFocusEvent *event )
142{
143#if QSCINTILLA_VERSION >= 0x020800 && QSCINTILLA_VERSION < 0x020900
144 if ( event->reason() != Qt::ActiveWindowFocusReason )
145 {
146 /* There's a bug in all QScintilla 2.8.X, where
147 a focus out event that is not due to ActiveWindowFocusReason doesn't
148 lead to the bliking caret being disabled. The hack consists in making
149 QsciScintilla::focusOutEvent believe that the event is a ActiveWindowFocusReason
150 The bug was fixed in 2.9 per:
151 2015-04-14 Phil Thompson <[email protected]>
152
153 * qt/qsciscintillabase.cpp:
154 Fixed a problem notifying when focus is lost to another application
155 widget.
156 [41734678234e]
157 */
158 QFocusEvent newFocusEvent( QEvent::FocusOut, Qt::ActiveWindowFocusReason );
159 QsciScintilla::focusOutEvent( &newFocusEvent );
160 }
161 else
162#endif
163 {
164 QsciScintilla::focusOutEvent( event );
165 }
166}
167
168// This workaround a likely bug in QScintilla. The ESC key should not be consumned
169// by the main entry, so that the default behavior (Dialog closing) can trigger,
170// but only is the auto-completion suggestion list isn't displayed
171void QgsCodeEditor::keyPressEvent( QKeyEvent *event )
172{
173 if ( isListActive() )
174 {
175 QsciScintilla::keyPressEvent( event );
176 return;
177 }
178
179 if ( event->key() == Qt::Key_Escape )
180 {
181 // Shortcut QScintilla and redirect the event to the QWidget handler
182 QWidget::keyPressEvent( event ); // clazy:exclude=skipped-base-method
183 return;
184 }
185
187 {
188 switch ( event->key() )
189 {
190 case Qt::Key_Return:
191 case Qt::Key_Enter:
192 runCommand( text() );
193 updatePrompt();
194 return;
195
196 case Qt::Key_Down:
198 updatePrompt();
199 return;
200
201 case Qt::Key_Up:
203 updatePrompt();
204 return;
205
206 default:
207 break;
208 }
209 }
210
211 QsciScintilla::keyPressEvent( event );
212
213}
214
215void QgsCodeEditor::contextMenuEvent( QContextMenuEvent *event )
216{
218 {
219 QsciScintilla::contextMenuEvent( event );
220 return;
221 }
222
223 QMenu *menu = new QMenu( this );
224 QMenu *historySubMenu = new QMenu( tr( "Command History" ), menu );
225
226 historySubMenu->addAction( tr( "Show" ), this, &QgsCodeEditor::showHistory, QStringLiteral( "Ctrl+Shift+SPACE" ) );
227 historySubMenu->addAction( tr( "Clear File" ), this, &QgsCodeEditor::clearPersistentHistory );
228 historySubMenu->addAction( tr( "Clear Session" ), this, &QgsCodeEditor::clearSessionHistory );
229
230 menu->addMenu( historySubMenu );
231 menu->addSeparator();
232
233 QAction *copyAction = menu->addAction( QgsApplication::getThemeIcon( "mActionEditCopy.svg" ), tr( "Copy" ), this, &QgsCodeEditor::copy, QKeySequence::Copy );
234 QAction *pasteAction = menu->addAction( QgsApplication::getThemeIcon( "mActionEditPaste.svg" ), tr( "Paste" ), this, &QgsCodeEditor::paste, QKeySequence::Paste );
235 copyAction->setEnabled( hasSelectedText() );
236 pasteAction->setEnabled( !QApplication::clipboard()->text().isEmpty() );
237
238 populateContextMenu( menu );
239
240 menu->exec( mapToGlobal( event->pos() ) );
241}
242
244{
245
246}
247
249{
250 if ( mUseDefaultSettings )
251 return color( role );
252
253 if ( !mOverrideColors )
254 {
255 return defaultColor( role, mColorScheme );
256 }
257 else
258 {
259 const QColor color = mCustomColors.value( role );
260 return !color.isValid() ? defaultColor( role ) : color;
261 }
262}
263
265{
266 if ( mUseDefaultSettings )
267 return getMonospaceFont();
268
269 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
270
271 const QgsSettings settings;
272 if ( !mFontFamily.isEmpty() )
273 font.setFamily( mFontFamily );
274
275#ifdef Q_OS_MAC
276 if ( mFontSize > 0 )
277 font.setPointSize( mFontSize );
278 else
279 {
280 // The font size gotten from getMonospaceFont() is too small on Mac
281 font.setPointSize( QLabel().font().pointSize() );
282 }
283#else
284 if ( mFontSize > 0 )
285 font.setPointSize( mFontSize );
286 else
287 {
288 const int fontSize = settings.value( QStringLiteral( "qgis/stylesheet/fontPointSize" ), 10 ).toInt();
289 font.setPointSize( fontSize );
290 }
291#endif
292 font.setBold( false );
293
294 return font;
295}
296
298{
299 updateFolding();
300
303
304 SendScintilla( SCI_MARKERSETFORE, SC_MARKNUM_FOLDEROPEN, lexerColor( QgsCodeEditorColorScheme::ColorRole::FoldIconHalo ) );
305 SendScintilla( SCI_MARKERSETBACK, SC_MARKNUM_FOLDEROPEN, lexerColor( QgsCodeEditorColorScheme::ColorRole::FoldIconForeground ) );
306 SendScintilla( SCI_MARKERSETFORE, SC_MARKNUM_FOLDER, lexerColor( QgsCodeEditorColorScheme::ColorRole::FoldIconHalo ) );
307 SendScintilla( SCI_MARKERSETBACK, SC_MARKNUM_FOLDER, lexerColor( QgsCodeEditorColorScheme::ColorRole::FoldIconForeground ) );
308 SendScintilla( SCI_STYLESETFORE, STYLE_INDENTGUIDE, lexerColor( QgsCodeEditorColorScheme::ColorRole::IndentationGuide ) );
309 SendScintilla( SCI_STYLESETBACK, STYLE_INDENTGUIDE, lexerColor( QgsCodeEditorColorScheme::ColorRole::IndentationGuide ) );
310
312 {
313 setCaretLineVisible( false );
314 setLineNumbersVisible( false ); // NO linenumbers for the input line
315 // Margin 1 is used for the '>' prompt (console input)
316 setMarginLineNumbers( 1, true );
317 setMarginWidth( 1, "00000" );
318 setMarginType( 1, QsciScintilla::MarginType::TextMarginRightJustified );
319 setMarginsBackgroundColor( color( QgsCodeEditorColorScheme::ColorRole::Background ) );
320 setEdgeMode( QsciScintilla::EdgeNone );
321 }
322}
323
324void QgsCodeEditor::setSciWidget()
325{
326 const QFont font = lexerFont();
327 setFont( font );
328
329 setUtf8( true );
330 setCaretLineVisible( true );
331 setCaretLineBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::CaretLine ) );
332 setCaretForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::Cursor ) );
335
336 setBraceMatching( QsciScintilla::SloppyBraceMatch );
339
340 setLineNumbersVisible( false );
341
342 // temporarily disable folding, will be enabled later if required by updateFolding()
343 setFolding( QsciScintilla::NoFoldStyle );
344 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::FoldingControls ), 0 );
345
346 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 0 );
347
350 setIndentationGuidesForegroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginForeground ) );
351 setIndentationGuidesBackgroundColor( lexerColor( QgsCodeEditorColorScheme::ColorRole::MarginBackground ) );
352 // whether margin will be shown
353 updateFolding();
354 const QColor foldColor = lexerColor( QgsCodeEditorColorScheme::ColorRole::Fold );
355 setFoldMarginColors( foldColor, foldColor );
356 // indentation
357 setAutoIndent( true );
358 setIndentationWidth( 4 );
359 setTabIndents( true );
360 setBackspaceUnindents( true );
361 setTabWidth( 4 );
362 // autocomplete
363 setAutoCompletionThreshold( 2 );
364 setAutoCompletionSource( QsciScintilla::AcsAPIs );
365
366 markerDefine( QgsApplication::getThemePixmap( "console/iconSyntaxErrorConsoleParams.svg", lexerColor( QgsCodeEditorColorScheme::ColorRole::Error ),
368}
369
370void QgsCodeEditor::setTitle( const QString &title )
371{
372 setWindowTitle( title );
373}
374
376{
378}
379
381{
382 switch ( language )
383 {
385 return tr( "CSS" );
387 return tr( "Expression" );
389 return tr( "HTML" );
391 return tr( "JavaScript" );
393 return tr( "JSON" );
395 return tr( "Python" );
397 return tr( "R" );
399 return tr( "SQL" );
401 return QString();
402 }
404}
405
407{
408 mMargin = margin;
409 if ( margin )
410 {
411 QFont marginFont = lexerFont();
412 marginFont.setPointSize( 10 );
413 setMarginLineNumbers( 0, true );
414 setMarginsFont( marginFont );
415 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), QStringLiteral( "00000" ) );
418 }
419 else
420 {
421 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), 0 );
422 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 0 );
423 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::FoldingControls ), 0 );
424 }
425}
426
428{
429 if ( visible )
430 {
431 QFont marginFont = lexerFont();
432 marginFont.setPointSize( 10 );
433 setMarginLineNumbers( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), true );
434 setMarginsFont( marginFont );
435 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), QStringLiteral( "00000" ) );
438 }
439 else
440 {
441 setMarginLineNumbers( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), false );
442 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ), 0 );
443 }
444}
445
447{
448 return marginLineNumbers( static_cast< int >( QgsCodeEditor::MarginRole::LineNumbers ) );
449}
450
452{
453 if ( folding )
454 {
456 }
457 else
458 {
459 mFlags &= ~( static_cast< int >( QgsCodeEditor::Flag::CodeFolding ) );
460 }
461 updateFolding();
462}
463
465{
466 return mFlags & QgsCodeEditor::Flag::CodeFolding;
467}
468
469void QgsCodeEditor::updateFolding()
470{
472 {
473 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::FoldingControls ), "0" );
476 setFolding( QsciScintilla::PlainFoldStyle );
477 }
478 else
479 {
480 setFolding( QsciScintilla::NoFoldStyle );
481 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::FoldingControls ), 0 );
482 }
483}
484
485bool QgsCodeEditor::readHistoryFile()
486{
487 if ( mHistoryFilePath.isEmpty() || !QFile::exists( mHistoryFilePath ) )
488 return false;
489
490 QFile file( mHistoryFilePath );
491 if ( file.open( QIODevice::ReadOnly ) )
492 {
493 QTextStream stream( &file );
494#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
495 // Always use UTF-8
496 stream.setCodec( "UTF-8" );
497#endif
498 QString line;
499 while ( !stream.atEnd() )
500 {
501 line = stream.readLine(); // line of text excluding '\n'
502 mHistory.append( line );
503 }
504 syncSoftHistory();
505 return true;
506 }
507
508 return false;
509}
510
511void QgsCodeEditor::syncSoftHistory()
512{
513 mSoftHistory = mHistory;
514 mSoftHistory.append( QString() );
515 mSoftHistoryIndex = mSoftHistory.length() - 1;
516}
517
519{
520 mSoftHistory[mSoftHistoryIndex] = text();
521}
522
523void QgsCodeEditor::updateHistory( const QStringList &commands, bool skipSoftHistory )
524{
525 if ( commands.size() > 1 )
526 {
527 mHistory.append( commands );
528 }
529 else if ( !commands.value( 0 ).isEmpty() )
530 {
531 const QString command = commands.value( 0 );
532 if ( mHistory.empty() || command != mHistory.constLast() )
533 mHistory.append( command );
534 }
535
536 if ( !skipSoftHistory )
537 syncSoftHistory();
538}
539
541{
542
543}
544
546{
547 if ( mInterpreter )
548 {
549 const QString prompt = mInterpreter->promptForState( mInterpreter->currentState() );
550 SendScintilla( QsciScintilla::SCI_MARGINSETTEXT, static_cast< uintptr_t >( 0 ), prompt.toUtf8().constData() );
551 }
552}
553
555{
556 return mInterpreter;
557}
558
560{
561 mInterpreter = newInterpreter;
562 updatePrompt();
563}
564
565QStringList QgsCodeEditor::history() const
566{
567 return mHistory;
568}
569
570void QgsCodeEditor::runCommand( const QString &command )
571{
572 updateHistory( { command } );
573
574 if ( mInterpreter )
575 mInterpreter->exec( command );
576
577 clear();
579}
580
582{
583 mHistory.clear();
584 readHistoryFile();
585 syncSoftHistory();
586
588}
589
591{
592 mHistory.clear();
593
594 if ( !mHistoryFilePath.isEmpty() && QFile::exists( mHistoryFilePath ) )
595 {
596 QFile file( mHistoryFilePath );
597 file.open( QFile::WriteOnly | QFile::Truncate );
598 }
599
601}
602
604{
605 if ( mHistoryFilePath.isEmpty() )
606 return false;
607
608 QFile f( mHistoryFilePath );
609 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
610 {
611 return false;
612 }
613
614 QTextStream ts( &f );
615#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
616 ts.setCodec( "UTF-8" );
617#endif
618 for ( const QString &command : std::as_const( mHistory ) )
619 {
620 ts << command + '\n';
621 }
622 return true;
623}
624
626{
627 if ( mSoftHistoryIndex < mSoftHistory.length() - 1 && !mSoftHistory.isEmpty() )
628 {
629 mSoftHistoryIndex += 1;
630 setText( mSoftHistory[mSoftHistoryIndex] );
632 }
633}
634
636{
637 if ( mSoftHistoryIndex > 0 && !mSoftHistory.empty() )
638 {
639 mSoftHistoryIndex -= 1;
640 setText( mSoftHistory[mSoftHistoryIndex] );
642 }
643}
644
646{
647 QgsCodeEditorHistoryDialog *dialog = new QgsCodeEditorHistoryDialog( this, this );
648 dialog->setAttribute( Qt::WA_DeleteOnClose );
649
650 dialog->show();
651 dialog->activateWindow();
652}
653
655{
656 // remove item from the command history (just for the current session)
657 mHistory.removeAt( index );
658 mSoftHistory.removeAt( index );
659 if ( index < mSoftHistoryIndex )
660 {
661 mSoftHistoryIndex -= 1;
662 if ( mSoftHistoryIndex < 0 )
663 mSoftHistoryIndex = mSoftHistory.length() - 1;
664 }
665}
666
667void QgsCodeEditor::insertText( const QString &text )
668{
669 // Insert the text or replace selected text
670 if ( hasSelectedText() )
671 {
672 replaceSelectedText( text );
673 }
674 else
675 {
676 int line, index;
677 getCursorPosition( &line, &index );
678 insertAt( text, line, index );
679 setCursorPosition( line, index + text.length() );
680 }
681}
682
684{
685 if ( theme.isEmpty() && QgsApplication::themeName() == QLatin1String( "default" ) )
686 {
687 // if using default theme, take certain colors from the palette
688 const QPalette pal = qApp->palette();
689
690 switch ( role )
691 {
693 return pal.color( QPalette::Highlight );
695 return pal.color( QPalette::HighlightedText );
696 default:
697 break;
698 }
699 }
700 else if ( theme.isEmpty() )
701 {
702 // non default theme (e.g. Blend of Gray). Take colors from theme ini file...
703 const QSettings ini( QgsApplication::uiThemes().value( QgsApplication::themeName() ) + "/qscintilla.ini", QSettings::IniFormat );
704
705 static const QMap< QgsCodeEditorColorScheme::ColorRole, QString > sColorRoleToIniKey
706 {
707 {QgsCodeEditorColorScheme::ColorRole::Default, QStringLiteral( "python/defaultFontColor" ) },
708 {QgsCodeEditorColorScheme::ColorRole::Keyword, QStringLiteral( "python/keywordFontColor" ) },
709 {QgsCodeEditorColorScheme::ColorRole::Class, QStringLiteral( "python/classFontColor" ) },
710 {QgsCodeEditorColorScheme::ColorRole::Method, QStringLiteral( "python/methodFontColor" ) },
711 {QgsCodeEditorColorScheme::ColorRole::Decoration, QStringLiteral( "python/decoratorFontColor" ) },
712 {QgsCodeEditorColorScheme::ColorRole::Number, QStringLiteral( "python/numberFontColor" ) },
713 {QgsCodeEditorColorScheme::ColorRole::Comment, QStringLiteral( "python/commentFontColor" ) },
714 {QgsCodeEditorColorScheme::ColorRole::CommentLine, QStringLiteral( "sql/commentLineFontColor" ) },
715 {QgsCodeEditorColorScheme::ColorRole::CommentBlock, QStringLiteral( "python/commentBlockFontColor" ) },
716 {QgsCodeEditorColorScheme::ColorRole::Background, QStringLiteral( "python/paperBackgroundColor" ) },
717 {QgsCodeEditorColorScheme::ColorRole::Cursor, QStringLiteral( "cursorColor" ) },
718 {QgsCodeEditorColorScheme::ColorRole::CaretLine, QStringLiteral( "caretLineColor" ) },
719 {QgsCodeEditorColorScheme::ColorRole::Operator, QStringLiteral( "sql/operatorFontColor" ) },
720 {QgsCodeEditorColorScheme::ColorRole::QuotedOperator, QStringLiteral( "sql/QuotedOperatorFontColor" ) },
721 {QgsCodeEditorColorScheme::ColorRole::Identifier, QStringLiteral( "sql/identifierFontColor" ) },
722 {QgsCodeEditorColorScheme::ColorRole::QuotedIdentifier, QStringLiteral( "sql/QuotedIdentifierFontColor" ) },
723 {QgsCodeEditorColorScheme::ColorRole::Tag, QStringLiteral( "html/tagFontColor" ) },
724 {QgsCodeEditorColorScheme::ColorRole::UnknownTag, QStringLiteral( "html/unknownTagFontColor" ) },
725 {QgsCodeEditorColorScheme::ColorRole::SingleQuote, QStringLiteral( "sql/singleQuoteFontColor" ) },
726 {QgsCodeEditorColorScheme::ColorRole::DoubleQuote, QStringLiteral( "sql/doubleQuoteFontColor" ) },
727 {QgsCodeEditorColorScheme::ColorRole::TripleSingleQuote, QStringLiteral( "python/tripleSingleQuoteFontColor" ) },
728 {QgsCodeEditorColorScheme::ColorRole::TripleDoubleQuote, QStringLiteral( "python/tripleDoubleQuoteFontColor" ) },
729 {QgsCodeEditorColorScheme::ColorRole::MarginBackground, QStringLiteral( "marginBackgroundColor" ) },
730 {QgsCodeEditorColorScheme::ColorRole::MarginForeground, QStringLiteral( "marginForegroundColor" ) },
731 {QgsCodeEditorColorScheme::ColorRole::SelectionBackground, QStringLiteral( "selectionBackgroundColor" ) },
732 {QgsCodeEditorColorScheme::ColorRole::SelectionForeground, QStringLiteral( "selectionForegroundColor" ) },
733 {QgsCodeEditorColorScheme::ColorRole::MatchedBraceBackground, QStringLiteral( "matchedBraceBackground" ) },
734 {QgsCodeEditorColorScheme::ColorRole::MatchedBraceForeground, QStringLiteral( "matchedBraceColor" ) },
735 {QgsCodeEditorColorScheme::ColorRole::Edge, QStringLiteral( "edgeColor" ) },
736 {QgsCodeEditorColorScheme::ColorRole::Fold, QStringLiteral( "foldColor" ) },
737 {QgsCodeEditorColorScheme::ColorRole::Error, QStringLiteral( "stderrFontColor" ) },
738 {QgsCodeEditorColorScheme::ColorRole::ErrorBackground, QStringLiteral( "stderrBackground" ) },
739 {QgsCodeEditorColorScheme::ColorRole::FoldIconForeground, QStringLiteral( "foldIconForeground" ) },
740 {QgsCodeEditorColorScheme::ColorRole::FoldIconHalo, QStringLiteral( "foldIconHalo" ) },
741 {QgsCodeEditorColorScheme::ColorRole::IndentationGuide, QStringLiteral( "indentationGuide" ) },
742 };
743
744 const QgsCodeEditorColorScheme defaultScheme = QgsGui::codeEditorColorSchemeRegistry()->scheme( QStringLiteral( "default" ) );
745 return QgsSymbolLayerUtils::decodeColor( ini.value( sColorRoleToIniKey.value( role ), defaultScheme.color( role ).name() ).toString() );
746 }
747
748 const QgsCodeEditorColorScheme scheme = QgsGui::codeEditorColorSchemeRegistry()->scheme( theme.isEmpty() ? QStringLiteral( "default" ) : theme );
749 return scheme.color( role );
750}
751
753{
754 const QgsSettings settings;
755 if ( !settings.value( QStringLiteral( "codeEditor/overrideColors" ), false, QgsSettings::Gui ).toBool() )
756 {
757 const QString theme = settings.value( QStringLiteral( "codeEditor/colorScheme" ), QString(), QgsSettings::Gui ).toString();
758 return defaultColor( role, theme );
759 }
760 else
761 {
762 const QString color = settings.value( QStringLiteral( "codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ), QString(), QgsSettings::Gui ).toString();
763 return color.isEmpty() ? defaultColor( role ) : QgsSymbolLayerUtils::decodeColor( color );
764 }
765}
766
768{
769 QgsSettings settings;
770 if ( color.isValid() )
771 {
772 settings.setValue( QStringLiteral( "codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ), color.name(), QgsSettings::Gui );
773 }
774 else
775 {
776 settings.remove( QStringLiteral( "codeEditor/%1" ).arg( sColorRoleToSettingsKey.value( role ) ), QgsSettings::Gui );
777 }
778}
779
780// Settings for font and fontsize
781bool QgsCodeEditor::isFixedPitch( const QFont &font )
782{
783 return font.fixedPitch();
784}
785
787{
788 QFont font = QFontDatabase::systemFont( QFontDatabase::FixedFont );
789
790 const QgsSettings settings;
791 if ( !settings.value( QStringLiteral( "codeEditor/fontfamily" ), QString(), QgsSettings::Gui ).toString().isEmpty() )
792 font.setFamily( settings.value( QStringLiteral( "codeEditor/fontfamily" ), QString(), QgsSettings::Gui ).toString() );
793
794 const int fontSize = settings.value( QStringLiteral( "codeEditor/fontsize" ), 0, QgsSettings::Gui ).toInt();
795
796#ifdef Q_OS_MAC
797 if ( fontSize > 0 )
798 font.setPointSize( fontSize );
799 else
800 {
801 // The font size gotten from getMonospaceFont() is too small on Mac
802 font.setPointSize( QLabel().font().pointSize() );
803 }
804#else
805 if ( fontSize > 0 )
806 font.setPointSize( fontSize );
807 else
808 {
809 const int fontSize = settings.value( QStringLiteral( "qgis/stylesheet/fontPointSize" ), 10 ).toInt();
810 font.setPointSize( fontSize );
811 }
812#endif
813 font.setBold( false );
814
815 return font;
816}
817
818void QgsCodeEditor::setCustomAppearance( const QString &scheme, const QMap<QgsCodeEditorColorScheme::ColorRole, QColor> &customColors, const QString &fontFamily, int fontSize )
819{
820 mUseDefaultSettings = false;
821 mOverrideColors = !customColors.isEmpty();
822 mColorScheme = scheme;
823 mCustomColors = customColors;
824 mFontFamily = fontFamily;
825 mFontSize = fontSize;
826
827 setSciWidget();
829}
830
831void QgsCodeEditor::addWarning( const int lineNumber, const QString &warning )
832{
833 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), "000" );
834 markerAdd( lineNumber, MARKER_NUMBER );
835 QFont font = lexerFont();
836 font.setItalic( true );
837 const QsciStyle styleAnn = QsciStyle( -1, QStringLiteral( "Annotation" ),
840 font,
841 true );
842 annotate( lineNumber, warning, styleAnn );
843 mWarningLines.push_back( lineNumber );
844}
845
847{
848 for ( const int line : mWarningLines )
849 {
850 markerDelete( line );
851 clearAnnotations( line );
852 }
853 setMarginWidth( static_cast< int >( QgsCodeEditor::MarginRole::ErrorIndicators ), 0 );
854 mWarningLines.clear();
855}
856
858{
859 int line = 0;
860 int index = 0;
861 getCursorPosition( &line, &index );
862 return line == lines() - 1;
863}
864
865void QgsCodeEditor::setHistoryFilePath( const QString &path )
866{
867 mHistoryFilePath = path;
868 readHistoryFile();
869}
870
872{
873 setCursorPosition( 0, 0 );
874 ensureCursorVisible();
875 ensureLineVisible( 0 );
876
878 updatePrompt();
879}
880
882{
883 const int endLine = lines() - 1;
884 const int endLineLength = lineLength( endLine );
885 setCursorPosition( endLine, endLineLength );
886 ensureCursorVisible();
887 ensureLineVisible( endLine );
888
890 updatePrompt();
891}
892
894
895int QgsCodeInterpreter::exec( const QString &command )
896{
897 mState = execCommandImpl( command );
898 return mState;
899}
ScriptLanguage
Scripting languages.
Definition: qgis.h:2715
@ QgisExpression
QGIS expressions.
@ JavaScript
JavaScript.
@ Unknown
Unknown/other language.
static QPixmap getThemePixmap(const QString &name, const QColor &foreColor=QColor(), const QColor &backColor=QColor(), int size=16)
Helper to get a theme icon as a pixmap.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QHash< QString, QString > uiThemes()
All themes found in ~/.qgis3/themes folder.
static QString themeName()
Set the active theme to the specified theme.
QgsCodeEditorColorScheme scheme(const QString &id) const
Returns the color scheme with matching id.
Defines a color scheme for use in QgsCodeEditor widgets.
@ TripleSingleQuote
Triple single quote color.
@ CommentBlock
Comment block color.
@ QuotedOperator
Quoted operator color.
@ DoubleQuote
Double quote color.
@ QuotedIdentifier
Quoted identifier color.
@ SelectionForeground
Selection foreground color.
@ CommentLine
Line comment color.
@ FoldIconForeground
Fold icon foreground color.
@ MarginForeground
Margin foreground color.
@ ErrorBackground
Error background color.
@ MatchedBraceBackground
Matched brace background color.
@ IndentationGuide
Indentation guide line.
@ SingleQuote
Single quote color.
@ MarginBackground
Margin background color.
@ SelectionBackground
Selection background color.
@ MatchedBraceForeground
Matched brace foreground color.
@ TripleDoubleQuote
Triple double quote color.
@ FoldIconHalo
Fold icon halo color.
QColor color(ColorRole role) const
Returns the color to use in the editor for the specified role.
A dialog for displaying and managing command history for a QgsCodeEditor widget.
void sessionHistoryCleared()
Emitted when the history of commands run in the current session is cleared.
void showHistory()
Shows the command history dialog.
void setCustomAppearance(const QString &scheme=QString(), const QMap< QgsCodeEditorColorScheme::ColorRole, QColor > &customColors=QMap< QgsCodeEditorColorScheme::ColorRole, QColor >(), const QString &fontFamily=QString(), int fontSize=0)
Sets a custom appearance for the widget, disconnecting it from using the standard appearance taken fr...
Mode
Code editor modes.
@ OutputDisplay
Read only mode for display of command outputs.
@ ScriptEditor
Standard mode, allows for display and edit of entire scripts.
@ CommandInput
Command input mode.
void contextMenuEvent(QContextMenuEvent *event) override
void clearPersistentHistory()
Clears the entire persistent history of commands run in the editor.
void removeHistoryCommand(int index)
Removes the command at the specified index from the history of the code editor.
static void setColor(QgsCodeEditorColorScheme::ColorRole role, const QColor &color)
Sets the color to use in the editor for the specified role.
void setHistoryFilePath(const QString &path)
Sets the file path to use for recording and retrieving previously executed commands.
QStringList history() const
Returns the list of commands previously executed in the editor.
void keyPressEvent(QKeyEvent *event) override
virtual void moveCursorToStart()
Moves the cursor to the start of the document and scrolls to ensure it is visible.
virtual void populateContextMenu(QMenu *menu)
Called when the context menu for the widget is about to be shown, after it has been fully populated w...
void persistentHistoryCleared()
Emitted when the persistent history of commands run in the editor is cleared.
void setFoldingVisible(bool folding)
Set whether the folding controls are visible in the editor.
void setInterpreter(QgsCodeInterpreter *newInterpreter)
Sets an attached code interpreter for executing commands when the editor is in the QgsCodeEditor::Mod...
@ FoldingControls
Folding controls.
@ ErrorIndicators
Error indicators.
@ LineNumbers
Line numbers.
void runPostLexerConfigurationTasks()
Performs tasks which must be run after a lexer has been set for the widget.
virtual void initializeLexer()
Called when the dialect specific code lexer needs to be initialized (or reinitialized).
void setTitle(const QString &title)
Set the widget title.
QgsCodeEditor(QWidget *parent=nullptr, const QString &title=QString(), bool folding=false, bool margin=false, QgsCodeEditor::Flags flags=QgsCodeEditor::Flags(), QgsCodeEditor::Mode mode=QgsCodeEditor::Mode::ScriptEditor)
Flags controlling behavior of code editor.
void clearWarnings()
Clears all warning messages from the editor.
static QFont getMonospaceFont()
Returns the monospaced font to use for code editors.
void showNextCommand()
Shows the next command from the session in the editor.
void focusOutEvent(QFocusEvent *event) override
@ CodeFolding
Indicates that code folding should be enabled for the editor.
bool isCursorOnLastLine() const
Returns true if the cursor is on the last line of the document.
static bool isFixedPitch(const QFont &font)
Returns true if a font is a fixed pitch font.
void updateSoftHistory()
Updates the soft history by storing the current editor text in the history.
void clearSessionHistory()
Clears the history of commands run in the current session.
void insertText(const QString &text)
Insert text at cursor position, or replace any selected text if user has made a selection.
bool writeHistoryFile()
Stores the commands executed in the editor to the persistent history file.
virtual void moveCursorToEnd()
Moves the cursor to the end of the document and scrolls to ensure it is visible.
static QString languageToString(Qgis::ScriptLanguage language)
Returns a user-friendly, translated name of the specified script language.
void setLineNumbersVisible(bool visible)
Sets whether line numbers should be visible in the editor.
void runCommand(const QString &command)
Runs a command in the editor.
virtual Qgis::ScriptLanguage language() const
Returns the associated scripting language.
QFont lexerFont() const
Returns the font to use in the lexer.
QgsCodeInterpreter * interpreter() const
Returns the attached code interpreter, or nullptr if not set.
bool lineNumbersVisible() const
Returns whether line numbers are visible in the editor.
QColor lexerColor(QgsCodeEditorColorScheme::ColorRole role) const
Returns the color to use in the lexer for the specified role.
bool foldingVisible()
Returns true if the folding controls are visible in the editor.
void showPreviousCommand()
Shows the previous command from the session in the editor.
Q_DECL_DEPRECATED void setMarginVisible(bool margin)
Set margin visible state.
void updatePrompt()
Triggers an update of the interactive prompt part of the editor.
static QColor defaultColor(QgsCodeEditorColorScheme::ColorRole role, const QString &theme=QString())
Returns the default color for the specified role.
void addWarning(int lineNumber, const QString &warning)
Adds a warning message and indicator to the specified a lineNumber.
static QColor color(QgsCodeEditorColorScheme::ColorRole role)
Returns the color to use in the editor for the specified role.
An interface for code interpreters.
Definition: qgscodeeditor.h:40
virtual int execCommandImpl(const QString &command)=0
Pure virtual method for executing commands in the interpreter.
virtual int currentState() const
Returns the current interpreter state.
Definition: qgscodeeditor.h:58
virtual QString promptForState(int state) const =0
Returns the interactive prompt string to use for the interpreter, given a state.
int exec(const QString &command)
Executes a command in the interpreter.
virtual ~QgsCodeInterpreter()
void optionsChanged()
This signal is emitted whenever the application options have been changed.
static QgsGui * instance()
Returns a pointer to the singleton instance.
Definition: qgsgui.cpp:67
static QgsCodeEditorColorSchemeRegistry * codeEditorColorSchemeRegistry()
Returns the global code editor color scheme registry, used for registering the color schemes for QgsC...
Definition: qgsgui.cpp:148
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static QColor decodeColor(const QString &str)
#define BUILTIN_UNREACHABLE
Definition: qgis.h:4180