//============================================================================= // // File : ScriptEditorImplementation.cpp // Creation date : Sun Mar 28 1999 16:11:48 CEST by Szymon Stefanek // // This file is part of the KVIrc IRC client distribution // Copyright (C) 1999 Szymon Stefanek // Copyright (C) 2005 Tonino Imbesi // Copyright (C) 2005-2008 Alessandro Carbone (elfonol at gmail dot com) // // This program is FREE software. You can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the HOPE that it will be USEFUL, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #include "ScriptEditorImplementation.h" #include "KviFileUtils.h" #include "kvi_fileextensions.h" #include "KviLocale.h" #include "KviFileDialog.h" #include "KviQString.h" #include "KviConfigurationFile.h" #include "KviModule.h" #include "KviApplication.h" #include "KviConsoleWindow.h" #include "KviWindow.h" #include "KviIconManager.h" #include "KviKvsKernel.h" #include "KviModuleManager.h" #include "KviConfigurationFile.h" #include "KviTalVBox.h" #include "KviTalHBox.h" #include "KviTalGroupBox.h" #include "KviOptions.h" #include #include #include #include #include #include #include #include #include #include #include #include #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW) #include #endif //COMPILE_ON_WINDOWS || COMPILE_ON_MINGW extern KVIRC_API KviModuleManager * g_pModuleManager; extern std::set g_pScriptEditorWindowList; extern KviModule * g_pEditorModulePointer; static QColor g_clrBackground(0, 0, 0); static QColor g_clrNormalText(100, 255, 0); static QColor g_clrBracket(255, 0, 0); static QColor g_clrComment(0, 120, 0); static QColor g_clrFunction(255, 255, 0); static QColor g_clrKeyword(120, 120, 150); static QColor g_clrVariable(200, 200, 200); static QColor g_clrPunctuation(180, 180, 0); static QColor g_clrFind(255, 0, 0); #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW) #define KVI_SCRIPTEDITOR_DEFAULT_FONT "Courier New" #define KVI_SCRIPTEDITOR_DEFAULT_FONT_SIZE 8 #elif defined(COMPILE_ON_MAC) #define KVI_SCRIPTEDITOR_DEFAULT_FONT "Monaco" #define KVI_SCRIPTEDITOR_DEFAULT_FONT_SIZE 10 #else #define KVI_SCRIPTEDITOR_DEFAULT_FONT "Monospace" #define KVI_SCRIPTEDITOR_DEFAULT_FONT_SIZE 8 #endif static QFont g_fntNormal(KVI_SCRIPTEDITOR_DEFAULT_FONT, KVI_SCRIPTEDITOR_DEFAULT_FONT_SIZE); static bool bSemaphore = false; static bool bCompleterReady = false; ScriptEditorWidget::ScriptEditorWidget(QWidget * pParent) : QTextEdit(pParent) { m_pSyntaxHighlighter = nullptr; setTabStopDistance(48); setAcceptRichText(false); setWordWrapMode(QTextOption::NoWrap); m_pParent = pParent; m_szHelp = "Nothing"; updateOptions(); m_szFind = ""; m_pCompleter = nullptr; QStringList szListFunctionsCommands; QString tmp("kvscompleter.idx"); iModulesCount = 0; iIndex = 0; QString szPath; g_pApp->getLocalKvircDirectory(szPath, KviApplication::ConfigPlugins, tmp); if(!KviFileUtils::fileExists(szPath)) { if(!bSemaphore) { bSemaphore = true; m_pStartTimer = new QTimer(); m_pStartTimer->setInterval(1000); connect(m_pStartTimer, SIGNAL(timeout()), this, SLOT(asyncCompleterCreation())); m_pStartTimer->start(500); } else { m_pStartTimer = new QTimer(); m_pStartTimer->setInterval(2000); connect(m_pStartTimer, SIGNAL(timeout()), this, SLOT(checkReadyCompleter())); m_pStartTimer->start(1000); } } else loadCompleterFromFile(); } ScriptEditorWidget::~ScriptEditorWidget() { if(m_pCompleter) delete m_pCompleter; disableSyntaxHighlighter(); } void ScriptEditorWidget::checkReadyCompleter() { if(bCompleterReady) { m_pStartTimer->stop(); delete m_pStartTimer; m_pStartTimer = nullptr; loadCompleterFromFile(); } } void ScriptEditorWidget::asyncCompleterCreation() { //static int iIndex = 0; //static int iModulesCount = 0; if(!iIndex) { m_pListCompletition = new QStringList(); QString szPath; g_pApp->getGlobalKvircDirectory(szPath, KviApplication::Modules); QDir d(szPath); #if defined(COMPILE_ON_WINDOWS) d.setNameFilters(QStringList("kvi*.dll")); #elif defined(COMPILE_ON_MINGW) d.setNameFilters(QStringList("libkvi*.dll")); #else d.setNameFilters(QStringList("libkvi*.so")); #endif m_pListModulesNames = new QStringList(d.entryList(QDir::Files | QDir::Readable)); iModulesCount = m_pListModulesNames->count(); } if (iIndex <= m_pListModulesNames->size()) return; QString szModuleName = m_pListModulesNames->at(iIndex); iIndex++; #if defined(COMPILE_ON_WINDOWS) szModuleName = szModuleName.replace("kvi", ""); szModuleName = szModuleName.replace(".dll", ""); #elif defined(COMPILE_ON_MINGW) szModuleName = szModuleName.replace("libkvi", ""); szModuleName = szModuleName.replace(".dll", ""); #else szModuleName = szModuleName.replace("libkvi", ""); szModuleName = szModuleName.replace(".so", ""); #endif KviModule * pModule = g_pModuleManager->getModule(szModuleName); if(pModule) { pModule->getAllFunctionsCommandsModule(m_pListCompletition, szModuleName); } if(iIndex == iModulesCount) { m_pStartTimer->stop(); m_pStartTimer->deleteLater(); m_pStartTimer = nullptr; QString szTmp("kvscompleter.idx"); QString szPath; g_pApp->getLocalKvircDirectory(szPath, KviApplication::ConfigPlugins, szTmp); KviKvsKernel::instance()->getAllFunctionsCommandsCore(m_pListCompletition); QString szBuffer = m_pListCompletition->join(","); QFile f(szPath); f.open(QIODevice::WriteOnly); f.write(szBuffer.toUtf8()); f.close(); createCompleter(*m_pListCompletition); iIndex = 0; iModulesCount = 0; bCompleterReady = true; delete m_pListCompletition; delete m_pListModulesNames; } } void ScriptEditorWidget::loadCompleterFromFile() { QStringList szListFunctionsCommand; QString szTmp("kvscompleter.idx"); QString szPath; g_pApp->getLocalKvircDirectory(szPath, KviApplication::ConfigPlugins, szTmp); QString szBuffer; QFile f(szPath); f.open(QIODevice::ReadOnly); szBuffer = f.readAll(); f.close(); szListFunctionsCommand = szBuffer.split(','); createCompleter(szListFunctionsCommand); } void ScriptEditorWidget::createCompleter(QStringList & list) { m_pCompleter = new QCompleter(list); m_pCompleter->setCaseSensitivity(Qt::CaseInsensitive); m_pCompleter->setWrapAround(false); m_pCompleter->setWidget(this); m_pCompleter->setModelSorting(QCompleter::CaseSensitivelySortedModel); m_pCompleter->setCompletionMode(QCompleter::PopupCompletion); m_pCompleter->setCaseSensitivity(Qt::CaseInsensitive); QObject::connect(m_pCompleter, SIGNAL(activated(const QString &)), this, SLOT(insertCompletion(const QString &))); } void ScriptEditorWidget::insertCompletion(const QString & szCompletion) { QTextCursor tc = textCursor(); int iExtra = szCompletion.length() - m_pCompleter->completionPrefix().length(); tc.movePosition(QTextCursor::Left); tc.movePosition(QTextCursor::EndOfWord); QString szTmp = szCompletion.right(iExtra); if(szCompletion.left(1) == "$") szTmp.append("("); else szTmp.append(" "); tc.insertText(szTmp); setTextCursor(tc); } void ScriptEditorWidget::contextMenuEvent(QContextMenuEvent * e) { QMenu * pMenu = createStandardContextMenu(); pMenu->addAction(__tr2qs_ctx("Context Sensitive Help", "editor"), this, SLOT(slotHelp()), Qt::CTRL | Qt::Key_H); pMenu->addAction(__tr2qs_ctx("&Replace", "editor"), this, SLOT(slotReplace()), Qt::CTRL | Qt::Key_R); pMenu->exec(e->globalPos()); delete pMenu; } void ScriptEditorWidget::slotFind() { m_szFind = ((ScriptEditorImplementation *)m_pParent)->findLineEdit()->text(); setText(toPlainText()); } void ScriptEditorWidget::slotReplace() { ScriptEditorReplaceDialog * pDialog = new ScriptEditorReplaceDialog(this, __tr2qs_ctx("Find & Replace", "editor")); connect(pDialog, SIGNAL(replaceAll(const QString &, const QString &)), m_pParent, SLOT(slotReplaceAll(const QString &, const QString &))); connect(pDialog, SIGNAL(initFind()), m_pParent, SLOT(slotInitFind())); connect(pDialog, SIGNAL(nextFind(const QString &)), m_pParent, SLOT(slotNextFind(const QString &))); if(pDialog->exec()) { }; } void ScriptEditorWidget::slotHelp() { contextSensitiveHelp(); } void ScriptEditorWidget::enableSyntaxHighlighter() { // we currently delete and re-create the m_pSyntaxHighlighter // as a trick to ensure proper re-highlighting to occur if(!m_pSyntaxHighlighter) m_pSyntaxHighlighter = new ScriptEditorSyntaxHighlighter(this); } void ScriptEditorWidget::disableSyntaxHighlighter() { if(m_pSyntaxHighlighter) delete m_pSyntaxHighlighter; m_pSyntaxHighlighter = nullptr; } void ScriptEditorWidget::updateOptions() { QPalette p = palette(); p.setColor(QPalette::Base, g_clrBackground); p.setColor(QPalette::Text, g_clrNormalText); setPalette(p); setFont(g_fntNormal); setTextColor(g_clrNormalText); disableSyntaxHighlighter(); enableSyntaxHighlighter(); //set cursor custom width if(KVI_OPTION_BOOL(KviOption_boolEnableCustomCursorWidth)) { setCursorWidth(KVI_OPTION_UINT(KviOption_uintCustomCursorWidth)); } else { #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW) int iCursorWidth = 1; SystemParametersInfo(SPI_GETCARETWIDTH, 0, &iCursorWidth, 0); setCursorWidth(iCursorWidth); #endif //COMPILE_ON_WINDOWS || COMPILE_ON_MINGW } } void ScriptEditorWidget::keyPressEvent(QKeyEvent * e) { if(m_pCompleter && m_pCompleter->popup()->isVisible()) { switch(e->key()) { case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_Escape: case Qt::Key_Tab: case Qt::Key_Backtab: e->ignore(); return; default: break; } } if(e->modifiers() == Qt::ControlModifier) { switch(e->key()) { case Qt::Key_B: insertPlainText("$b"); return; case Qt::Key_I: insertPlainText("$i"); return; case Qt::Key_K: insertPlainText("$k"); return; case Qt::Key_O: insertPlainText("$o"); return; case Qt::Key_U: insertPlainText("$u"); return; case Qt::Key_Enter: case Qt::Key_Return: case Qt::Key_PageUp: e->ignore(); // allow the parent to process it return; break; } } // Adapted from QT4 QCompleter example bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E if(!m_pCompleter || !isShortcut) // don't process the shortcut when we have a completer QTextEdit::keyPressEvent(e); const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier + Qt::ShiftModifier); if(!m_pCompleter || (ctrlOrShift && e->text().isEmpty())) return; static QString eow("~!@#$%^&*()_+{}|:\"<>?,/;'[]\\-="); // end of word bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift; QString completionPrefix = textUnderCursor(); if(!isShortcut && (hasModifier || e->text().isEmpty() || completionPrefix.length() < 3 || eow.contains(e->text().right(1)))) { m_pCompleter->popup()->hide(); return; } if(completionPrefix != m_pCompleter->completionPrefix()) { m_pCompleter->setCompletionPrefix(completionPrefix); m_pCompleter->popup()->setCurrentIndex(m_pCompleter->completionModel()->index(0, 0)); } QRect cr = cursorRect(); cr.setWidth(m_pCompleter->popup()->sizeHintForColumn(0) + m_pCompleter->popup()->verticalScrollBar()->sizeHint().width()); m_pCompleter->complete(cr); } QString ScriptEditorWidget::textUnderCursor() const { QString szWord; QTextCursor tc = textCursor(); if(tc.atBlockStart()) return QString(); tc.clearSelection(); tc.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor); if(tc.atBlockStart()) { szWord.append(tc.selectedText()); tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); szWord.append(tc.selectedText()); if(tc.atBlockEnd()) { return szWord; } tc.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); szWord.append(tc.selectedText()); if(szWord.right(1) != ".") szWord.chop(1); return szWord; } tc.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); szWord = tc.selectedText(); if(szWord.left(1) == ".") { tc.movePosition(QTextCursor::StartOfWord); tc.movePosition(QTextCursor::PreviousCharacter); tc.movePosition(QTextCursor::PreviousWord); tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor, 1); szWord.prepend(tc.selectedText()); } else szWord.remove(0, 1); return szWord; } bool ScriptEditorWidget::contextSensitiveHelp() const { QRect r = cursorRect(); QTextCursor cur = cursorForPosition(QPoint(r.x(), r.y())); cur.select(QTextCursor::WordUnderCursor); QString szText = cur.selectedText(); QString szTmp = szText; return true; } ScriptEditorWidgetColorOptions::ScriptEditorWidgetColorOptions(QWidget * pParent) : QDialog(pParent) { setWindowTitle(__tr2qs_ctx("Editor Configuration - KVIrc", "editor")); QGridLayout * g = new QGridLayout(this); KviTalVBox * box = new KviTalVBox(this); g->addWidget(box, 0, 0); box->setContentsMargins(0, 0, 0, 0); box->setSpacing(0); box->setMinimumWidth(280); KviFontSelector * f = new KviFontSelector(box, __tr2qs_ctx("Font:", "editor"), &g_fntNormal, true); m_pSelectorInterfaceList.push_back(f); KviTalGroupBox * gbox = new KviTalGroupBox(Qt::Horizontal, __tr2qs_ctx("Colors", "editor"), box); gbox->setInsideSpacing(0); addColorSelector(gbox, __tr2qs_ctx("Background:", "editor"), &g_clrBackground, true); addColorSelector(gbox, __tr2qs_ctx("Normal text:", "editor"), &g_clrNormalText, true); addColorSelector(gbox, __tr2qs_ctx("Brackets:", "editor"), &g_clrBracket, true); addColorSelector(gbox, __tr2qs_ctx("Comments:", "editor"), &g_clrComment, true); addColorSelector(gbox, __tr2qs_ctx("Functions:", "editor"), &g_clrFunction, true); addColorSelector(gbox, __tr2qs_ctx("Keywords:", "editor"), &g_clrKeyword, true); addColorSelector(gbox, __tr2qs_ctx("Variables:", "editor"), &g_clrVariable, true); addColorSelector(gbox, __tr2qs_ctx("Punctuation:", "editor"), &g_clrPunctuation, true); addColorSelector(gbox, __tr2qs_ctx("Find:", "editor"), &g_clrFind, true); KviTalHBox * hbox = new KviTalHBox(box); QPushButton * b = new QPushButton(__tr2qs_ctx("OK", "editor"), hbox); b->setDefault(true); connect(b, SIGNAL(clicked()), this, SLOT(okClicked())); b = new QPushButton(__tr2qs_ctx("Cancel", "editor"), hbox); connect(b, SIGNAL(clicked()), this, SLOT(reject())); } KviColorSelector * ScriptEditorWidgetColorOptions::addColorSelector(QWidget * pParent, const QString & txt, QColor * pOption, bool bEnabled) { KviColorSelector * s = new KviColorSelector(pParent, txt, pOption, bEnabled); m_pSelectorInterfaceList.push_back(s); return s; } void ScriptEditorWidgetColorOptions::okClicked() { for(const auto & i : m_pSelectorInterfaceList) i->commit(); accept(); } ScriptEditorSyntaxHighlighter::ScriptEditorSyntaxHighlighter(ScriptEditorWidget * pWidget) : QSyntaxHighlighter(pWidget), m_pTextEdit(pWidget) { // code adpated from QT4 example // FIX-ME: "function ..." - "function internal ..." // FIX-ME: "@$" // FIX-ME: "\" escape char updateSyntaxtTextFormat(); KviScriptHighlightingRule rule; rule.pattern = KviRegExp("([=()[\\]!\"?<>;:.,+-])+"); rule.format = punctuationFormat; highlightingRules.append(rule); rule.pattern = KviRegExp("[{};]([a-zA-Z]+[a-zA-Z0-9_\\.:]*)"); rule.format = keywordFormat; highlightingRules.append(rule); rule.pattern = KviRegExp("[$]([a-zA-Z0-9]+[a-zA-Z0-9_\\.:]*)"); rule.format = functionFormat; highlightingRules.append(rule); rule.pattern = KviRegExp("[%]([a-zA-Z]+[a-zA-Z0-9_\\.]*)"); rule.format = variableFormat; highlightingRules.append(rule); rule.pattern = KviRegExp("([{}])+"); rule.format = bracketFormat; highlightingRules.append(rule); } ScriptEditorSyntaxHighlighter::~ScriptEditorSyntaxHighlighter() = default; void ScriptEditorSyntaxHighlighter::updateSyntaxtTextFormat() { punctuationFormat.setForeground(g_clrPunctuation); keywordFormat.setForeground(g_clrKeyword); functionFormat.setForeground(g_clrFunction); variableFormat.setForeground(g_clrVariable); bracketFormat.setForeground(g_clrBracket); commentFormat.setForeground(g_clrComment); findFormat.setForeground(g_clrFind); } #define SKIP_SPACES \ while(iIndexStart < szText.size()) \ { \ if(szText.at(iIndexStart).unicode() == '\t' || szText.at(iIndexStart).unicode() == ' ') \ { \ iIndexStart++; \ } \ else \ { \ break; \ } \ } void ScriptEditorSyntaxHighlighter::highlightBlock(const QString & szText) { if(szText.isEmpty()) { setCurrentBlockState(previousBlockState()); return; } int iIndexStart = 0; setCurrentBlockState(0); if(previousBlockState() == 1) { // inside a multiline comment int iCommentEnd = szText.indexOf("*/"); if(iCommentEnd < 0) { // not found: comment until the end of the block setCurrentBlockState(1); setFormat(iIndexStart, szText.length(), commentFormat); return; } // skip 2 chars iCommentEnd += 2; // end of comment found setFormat(iIndexStart, iCommentEnd - iIndexStart, commentFormat); iIndexStart = iCommentEnd; } SKIP_SPACES if(iIndexStart == szText.size()) return; // check 'commands' and comments int iCommandStart = iIndexStart; if(szText.at(iIndexStart).unicode() != '$' && szText.at(iIndexStart).unicode() != '{' && szText.at(iIndexStart).unicode() != '}' && szText.at(iIndexStart).unicode() != '%') { switch(szText.at(iIndexStart).unicode()) { case '#': // single line comment, bash style while(iIndexStart < szText.size() && szText.at(iIndexStart).unicode() != '\n') iIndexStart++; setFormat(iCommandStart, iIndexStart - iCommandStart, commentFormat); break; case '/': if((iIndexStart + 1) < szText.size()) { if(szText.at(iIndexStart + 1).unicode() == '/') { // single line comment, c++ style iIndexStart++; while(iIndexStart < szText.size() && szText.at(iIndexStart).unicode() != '\n') iIndexStart++; setFormat(iCommandStart, iIndexStart - iCommandStart, commentFormat); break; } if(szText.at(iIndexStart + 1).unicode() == '*') { // multi line comment, c style iIndexStart++; setCurrentBlockState(1); while(iIndexStart < szText.size()) { if((iIndexStart + 1) < szText.size()) { if(szText.at(iIndexStart).unicode() == '*' && szText.at(iIndexStart + 1).unicode() == '/') { iIndexStart += 2; setCurrentBlockState(0); break; } } iIndexStart++; } setFormat(iCommandStart, iIndexStart - iCommandStart, commentFormat); SKIP_SPACES if(currentBlockState() == 0) { iCommandStart = iIndexStart; } else { break; } // else fallback to command (a command can start at the end of a /* comment */) } // else not a comment, fallback to command } default: //command while(iIndexStart < szText.size() && (szText.at(iIndexStart).isLetterOrNumber() || (szText.at(iIndexStart).unicode() == '.') || (szText.at(iIndexStart).unicode() == '_') || (szText.at(iIndexStart).unicode() == ':'))) { iIndexStart++; } setFormat(iCommandStart, iIndexStart - iCommandStart, keywordFormat); break; } } // code from QT4 example int index = 0; foreach(KviScriptHighlightingRule rule, highlightingRules) { KviRegExp expression(rule.pattern); QString sz = expression.pattern(); index = expression.indexIn(szText, iIndexStart); while(index >= 0) { int length = expression.matchedLength(); setFormat(index, length, rule.format); index = expression.indexIn(szText, index + length); } } // 'found matches' highlighting ScriptEditorWidget * pEditor = ((ScriptEditorWidget *)textEdit()); QString szFind = pEditor->m_szFind; if(!szFind.isEmpty()) { index = szText.indexOf(szFind, 0, Qt::CaseInsensitive); int length = szFind.length(); while(index >= 0) { setFormat(index, length, findFormat); index = szText.indexOf(szFind, index + length, Qt::CaseInsensitive); } } } ScriptEditorImplementation::ScriptEditorImplementation(QWidget * par) : KviScriptEditor(par) { m_pOptionsDialog = nullptr; if(g_pScriptEditorWindowList.empty()) loadOptions(); g_pScriptEditorWindowList.insert(this); m_lastCursorPos = 0; QGridLayout * g = new QGridLayout(this); m_pEditor = new ScriptEditorWidget(this); m_pFindLineEdit = new QLineEdit(" ", this); m_pFindLineEdit->setText(""); QPalette p = m_pFindLineEdit->palette(); p.setColor(QPalette::Text, g_clrFind); m_pFindLineEdit->setPalette(p); g->addWidget(m_pEditor, 0, 0, 1, 5); g->setRowStretch(0, 1); QToolButton * b = new QToolButton(this); b->setIcon(*(g_pIconManager->getSmallIcon(KviIconManager::Options))); b->setObjectName("ToolButtonEditor"); b->setMinimumWidth(24); b->setText(__tr2qs_ctx("Options", "editor")); b->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); g->addWidget(b, 1, 0); QMenu * pop = new QMenu(b); pop->addAction(__tr2qs_ctx("&Open...", "editor"), this, SLOT(loadFromFile())); pop->addAction(__tr2qs_ctx("&Save As...", "editor"), this, SLOT(saveToFile())); pop->addSeparator(); pop->addAction(__tr2qs_ctx("&Configure Editor...", "editor"), this, SLOT(configureColors())); b->setMenu(pop); b->setPopupMode(QToolButton::InstantPopup); g->setColumnStretch(1, 1); g->setColumnStretch(2, 10); g->addWidget(m_pFindLineEdit, 1, 2); QLabel * pLab = new QLabel(this); pLab->setText(__tr2qs_ctx("Find:", "editor")); pLab->setAlignment(Qt::AlignRight | Qt::AlignVCenter); g->addWidget(pLab, 1, 1); m_pFindButton = new QPushButton(QString(__tr2qs_ctx("&Find", "editor")), this); g->addWidget(m_pFindButton, 1, 3); m_pRowColLabel = new QLabel(QString(__tr2qs_ctx("Line: %1 Col: %2", "editor")).arg(1).arg(1), this); m_pRowColLabel->setFrameStyle(QFrame::Sunken | QFrame::Panel); m_pRowColLabel->setMinimumWidth(80); g->addWidget(m_pRowColLabel, 1, 4); connect(m_pFindLineEdit, SIGNAL(returnPressed()), m_pEditor, SLOT(slotFind())); connect(m_pFindLineEdit, SIGNAL(returnPressed()), this, SLOT(slotFind())); connect(m_pFindButton, SIGNAL(clicked()), m_pEditor, SLOT(slotFind())); connect(m_pFindButton, SIGNAL(clicked()), this, SLOT(slotFind())); connect(m_pEditor, SIGNAL(cursorPositionChanged()), this, SLOT(updateRowColLabel())); connect(m_pEditor, SIGNAL(selectionChanged()), this, SLOT(updateRowColLabel())); m_lastCursorPos = 0; } ScriptEditorImplementation::~ScriptEditorImplementation() { if(m_pOptionsDialog) { m_pOptionsDialog->deleteLater(); m_pOptionsDialog = nullptr; } g_pScriptEditorWindowList.erase(this); if(g_pScriptEditorWindowList.empty()) saveOptions(); } void ScriptEditorImplementation::loadOptions() { QString szTmp; g_pEditorModulePointer->getDefaultConfigFileName(szTmp); KviConfigurationFile cfg(szTmp, KviConfigurationFile::Read); g_clrBackground = cfg.readColorEntry("Background", QColor(0, 0, 0)); g_clrNormalText = cfg.readColorEntry("NormalText", QColor(100, 255, 0)); g_clrBracket = cfg.readColorEntry("Bracket", QColor(255, 0, 0)); g_clrComment = cfg.readColorEntry("Comment", QColor(0, 120, 0)); g_clrFunction = cfg.readColorEntry("Function", QColor(255, 255, 0)); g_clrKeyword = cfg.readColorEntry("Keyword", QColor(120, 120, 150)); g_clrVariable = cfg.readColorEntry("Variable", QColor(200, 200, 200)); g_clrPunctuation = cfg.readColorEntry("Punctuation", QColor(180, 180, 0)); g_clrFind = cfg.readColorEntry("Find", QColor(255, 0, 0)); g_fntNormal = cfg.readFontEntry("Font", QFont(KVI_SCRIPTEDITOR_DEFAULT_FONT, KVI_SCRIPTEDITOR_DEFAULT_FONT_SIZE)); } bool ScriptEditorImplementation::isModified() { return m_pEditor->document()->isModified(); } void ScriptEditorImplementation::setModified(bool bModified) { return m_pEditor->document()->setModified(bModified); } void ScriptEditorImplementation::slotFind() { emit find(m_pFindLineEdit->text()); } void ScriptEditorImplementation::slotNextFind(const QString &) //szText { // emit nextFind(const QString & szText); } void ScriptEditorImplementation::slotInitFind() { emit initFind(); } void ScriptEditorImplementation::slotReplaceAll(const QString & szToReplace, const QString & szReplaceWith) { emit replaceAll(szToReplace, szReplaceWith); } void ScriptEditorImplementation::saveOptions() { QString szTmp; g_pEditorModulePointer->getDefaultConfigFileName(szTmp); KviConfigurationFile cfg(szTmp, KviConfigurationFile::Write); cfg.writeEntry("Background", g_clrBackground); cfg.writeEntry("NormalText", g_clrNormalText); cfg.writeEntry("Bracket", g_clrBracket); cfg.writeEntry("Comment", g_clrComment); cfg.writeEntry("Function", g_clrFunction); cfg.writeEntry("Keyword", g_clrKeyword); cfg.writeEntry("Variable", g_clrVariable); cfg.writeEntry("Punctuation", g_clrPunctuation); cfg.writeEntry("Find", g_clrFind); cfg.writeEntry("Font", g_fntNormal); } void ScriptEditorImplementation::setFocus() { m_pEditor->setFocus(); } void ScriptEditorImplementation::focusInEvent(QFocusEvent *) { m_pEditor->setFocus(); } void ScriptEditorImplementation::setEnabled(bool bEnabled) { QWidget::setEnabled(bEnabled); m_pEditor->setEnabled(bEnabled); m_pRowColLabel->setEnabled(bEnabled); } void ScriptEditorImplementation::saveToFile() { QString szFileName; if(KviFileDialog::askForSaveFileName(szFileName, __tr2qs_ctx("Choose a Filename - KVIrc", "editor"), QString(), QString(), false, true, true, this)) { QString szBuffer = m_pEditor->toPlainText(); if(!KviFileUtils::writeFile(szFileName, szBuffer)) { QString szTmp; QMessageBox::warning(this, __tr2qs_ctx("Writing to File Failed - KVIrc", "editor"), szTmp = QString(__tr2qs_ctx("Can't open file %1 for writing.", "editor")).arg(szFileName)); } } } void ScriptEditorImplementation::setText(const char * txt) { setText(QByteArray(txt)); } void ScriptEditorImplementation::setUnHighlightedText(const QString & szText) { m_pEditor->disableSyntaxHighlighter(); m_pEditor->setText(szText); } void ScriptEditorImplementation::setText(const QByteArray & szText) { m_pEditor->setPlainText(szText.data()); m_pEditor->document()->setModified(false); updateRowColLabel(); } void ScriptEditorImplementation::setReadOnly(bool bReadOnly) { m_pEditor->setReadOnly(bReadOnly); } void ScriptEditorImplementation::getText(QByteArray & szText) { szText = m_pEditor->toPlainText().toUtf8(); } void ScriptEditorImplementation::setText(const QString & szText) { m_pEditor->enableSyntaxHighlighter(); m_pEditor->setPlainText(szText); QTextCursor cur = m_pEditor->textCursor(); cur.movePosition(QTextCursor::End); m_pEditor->setTextCursor(cur); m_pEditor->document()->setModified(false); updateRowColLabel(); } void ScriptEditorImplementation::getText(QString & szText) { szText = m_pEditor->toPlainText(); } void ScriptEditorImplementation::setFindText(const QString & szText) { m_pFindLineEdit->setText(szText); m_pEditor->slotFind(); } void ScriptEditorImplementation::updateRowColLabel() { if(m_lastCursorPos == m_pEditor->textCursor().position()) return; int iRow = m_pEditor->textCursor().blockNumber() + 1; int iCol = m_pEditor->textCursor().columnNumber() + 1; QString szTmp = QString(__tr2qs_ctx("Line: %1 Col: %2", "editor")).arg(iRow).arg(iCol); m_pRowColLabel->setText(szTmp); m_lastCursorPos = m_pEditor->textCursor().position(); } void ScriptEditorImplementation::setCursorPosition(int iPos) { QTextCursor cur = m_pEditor->textCursor(); cur.setPosition(iPos); m_pEditor->setTextCursor(cur); updateRowColLabel(); } void ScriptEditorImplementation::loadFromFile() { QString szFileName; if(KviFileDialog::askForOpenFileName(szFileName, __tr2qs_ctx("Select a File - KVIrc", "editor"), QString(), KVI_FILTER_SCRIPT, false, true, this)) { QString szBuffer; if(KviFileUtils::loadFile(szFileName, szBuffer)) { m_pEditor->setPlainText(szBuffer); setCursorPosition(0); } else { QString szTmp; QMessageBox::warning(this, __tr2qs_ctx("Opening File Failed - KVIrc", "editor"), szTmp = QString(__tr2qs_ctx("Can't open file %1 for reading.", "editor")).arg(szFileName)); } } } void ScriptEditorImplementation::configureColors() { if(!m_pOptionsDialog) { m_pOptionsDialog = new ScriptEditorWidgetColorOptions(this); connect(m_pOptionsDialog, SIGNAL(finished(int)), this, SLOT(optionsDialogFinished(int))); } m_pOptionsDialog->show(); } void ScriptEditorImplementation::optionsDialogFinished(int iResult) { if(iResult == QDialog::Accepted) { m_pEditor->updateOptions(); saveOptions(); } } ScriptEditorReplaceDialog::ScriptEditorReplaceDialog(QWidget * pParent, const QString & szName) : QDialog(pParent) { setObjectName(szName); setWindowTitle(__tr2qs("Find and Replace - KVIrc")); m_pParent = pParent; emit initFind(); QPalette p = palette(); p.setColor(foregroundRole(), QColor(0, 0, 0)); p.setColor(backgroundRole(), QColor(255, 255, 255)); setPalette(p); QGridLayout * pLayout = new QGridLayout(this); pLayout->setObjectName("replace layout"); QLabel * m_pFindLabel = new QLabel(this); m_pFindLabel->setObjectName("findlabel"); m_pFindLabel->setText(__tr2qs_ctx("Word to find:", "editor")); pLayout->addWidget(m_pFindLabel, 0, 0); m_pFindLineEdit = new QLineEdit(this); m_pFindLineEdit->setObjectName("findlineedit"); pLayout->addWidget(m_pFindLineEdit, 0, 1); QLabel * m_pReplaceLabel = new QLabel(this); m_pReplaceLabel->setObjectName("replacelabel"); m_pReplaceLabel->setText(__tr2qs_ctx("Replace with:", "editor")); pLayout->addWidget(m_pReplaceLabel, 1, 0); m_pReplaceLineEdit = new QLineEdit(this); m_pReplaceLineEdit->setObjectName("replacelineedit"); pLayout->addWidget(m_pReplaceLineEdit, 1, 1); m_pFindLineEdit->setFocus(); m_pCheckReplaceAll = new QCheckBox(this); m_pCheckReplaceAll->setObjectName("replaceAll"); m_pCheckReplaceAll->setText(__tr2qs_ctx("&Replace in all editor's items", "editor")); pLayout->addWidget(m_pCheckReplaceAll, 2, 0); QPushButton * pCancelButton = new QPushButton(this); pCancelButton->setObjectName("cancelButton"); pCancelButton->setText(__tr2qs_ctx("&Cancel", "editor")); pLayout->addWidget(pCancelButton, 3, 0); m_pReplaceButton = new QPushButton(this); m_pReplaceButton->setObjectName("replacebutton"); m_pReplaceButton->setText(__tr2qs_ctx("&Replace", "editor")); m_pReplaceButton->setEnabled(false); pLayout->addWidget(m_pReplaceButton, 3, 1); setLayout(pLayout); // signals and slots connections connect(m_pReplaceButton, SIGNAL(clicked()), this, SLOT(slotReplace())); connect(pCancelButton, SIGNAL(clicked()), this, SLOT(reject())); connect(m_pFindLineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &))); } void ScriptEditorReplaceDialog::textChanged(const QString & szText) { if(!szText.isEmpty()) m_pReplaceButton->setEnabled(true); else m_pReplaceButton->setEnabled(false); } void ScriptEditorReplaceDialog::slotReplace() { QString szText = ((ScriptEditorWidget *)m_pParent)->toPlainText(); if(m_pCheckReplaceAll->isChecked()) emit replaceAll(m_pFindLineEdit->text(), m_pReplaceLineEdit->text()); szText.replace(m_pFindLineEdit->text(), m_pReplaceLineEdit->text(), Qt::CaseInsensitive); ((ScriptEditorWidget *)m_pParent)->setText(szText); ((ScriptEditorWidget *)m_pParent)->document()->setModified(true); m_pFindLineEdit->setText(""); m_pReplaceLineEdit->setText(""); setTabOrder(m_pFindLineEdit, m_pReplaceLineEdit); } void ScriptEditorReplaceDialog::slotNextFind() { emit nextFind(m_pFindLineEdit->text()); }