Back to index

scribus-ng  1.3.4.dfsg+svn20071115
pconsole.cpp
Go to the documentation of this file.
00001 /*
00002 For general Scribus (>=1.3.2) copyright and licensing information please refer
00003 to the COPYING file provided with the program. Following this notice may exist
00004 a copyright and/or license notice that predates the release of Scribus 1.3.2
00005 for which a new license (GPL+exception) is in place.
00006 */
00007 /*
00008 This program is free software; you can redistribute it and/or modify
00009 it under the terms of the GNU General Public License as published by
00010 the Free Software Foundation; either version 2 of the License, or
00011 (at your option) any later version.
00012 */
00013 
00014 #include "pconsole.h"
00015 #include "pconsole.moc"
00016 
00017 #include <qpixmap.h>
00018 #include <qvariant.h>
00019 #include <qpushbutton.h>
00020 #include <qheader.h>
00021 #include <qlistview.h>
00022 #include <qtextedit.h>
00023 #include <qlayout.h>
00024 #include <qsplitter.h>
00025 #include <qtooltip.h>
00026 #include <qwhatsthis.h>
00027 #include <qmenubar.h>
00028 #include <qfiledialog.h>
00029 #include <qstatusbar.h>
00030 
00031 #include <qsyntaxhighlighter.h>
00032 #include "scribus.h"
00033 #include "prefsmanager.h"
00034 #include "prefsfile.h"
00035 #include "prefscontext.h"
00036 #include "scmessagebox.h"
00037 #include "commonstrings.h"
00038 
00039 
00040 extern QPixmap SCRIBUS_API loadIcon(QString nam);
00041 
00042 
00043 PythonConsole::PythonConsole( QWidget* parent)
00044        : QWidget( parent, "PythonConsole", WType_TopLevel )
00045 {
00046        setIcon(loadIcon("AppIcon.png"));
00047 
00048        // setup the menu
00049        menuBar = new QMenuBar(this, "menuBar");
00050        QPopupMenu *fileMenu = new QPopupMenu(this);
00051        fileMenu->insertItem(loadIcon("16/document-open.png"), tr("&Open..."), this, SLOT(slot_open()), CTRL+Key_O);
00052        fileMenu->insertItem(loadIcon("16/document-save.png"), tr("&Save"), this, SLOT(slot_save()), CTRL+Key_S);
00053        fileMenu->insertItem(loadIcon("16/document-save-as.png"), tr("Save &As..."), this, SLOT(slot_saveAs()));
00054        fileMenu->insertSeparator();
00055        fileMenu->insertItem(loadIcon("exit.png"), tr("&Exit"), this, SLOT(slot_quit()));
00056        menuBar->insertItem( tr("&File"), fileMenu);
00057        QPopupMenu *scriptMenu = new QPopupMenu(this);
00058        scriptMenu->insertItem(loadIcon("ok.png"), tr("&Run"), this, SLOT(slot_runScript()), Key_F9);
00059        scriptMenu->insertItem( tr("Run As &Console"), this, SLOT(slot_runScriptAsConsole()), CTRL+Key_F9);
00060        scriptMenu->insertItem( tr("&Save Output..."), this, SLOT(slot_saveOutput()));
00061        menuBar->insertItem( tr("&Script"), scriptMenu);
00062 
00063        gridLayout = new QGridLayout( this, 0, 0, 1, 6, "gridLayout");
00064        gridLayout->setMenuBar(menuBar);
00065 
00066        editorsLayout = new QVBoxLayout( 0, 0, 6, "editorsLayout");
00067 
00068        QSplitter *splitter = new QSplitter(Qt::Vertical, this, "splitter");
00069        editorsLayout->addWidget(splitter);
00070 
00071        commandEdit = new QTextEdit(splitter, "commandEdit" );
00072        commandEdit->setTextFormat(Qt::PlainText);
00073        commandEdit->setFocus();
00074        commandEdit->setTabStopWidth(commandEdit->pointSize() * 4);
00075        QSizePolicy commandEditSize( commandEdit->sizePolicy() );
00076        commandEditSize.setVerStretch(4);
00077        commandEditSize.setVerData(QSizePolicy::Preferred);
00078        commandEdit->setSizePolicy(commandEditSize);
00079        // install syntax highlighter.
00080        SyntaxHighlighter *sxHigh = new SyntaxHighlighter(commandEdit);
00081        //remove that unused warning!
00082        sxHigh->currentParagraph();
00083 
00084        outputEdit = new QTextEdit(splitter, "outputEdit" );
00085        outputEdit->setTextFormat(Qt::PlainText);
00086        outputEdit->setReadOnly(true);
00087        QSizePolicy outputEditSize( outputEdit->sizePolicy() );
00088        outputEditSize.setVerStretch(10);
00089        outputEditSize.setVerData(QSizePolicy::Expanding);
00090        outputEdit->setSizePolicy(outputEditSize);
00091 
00092        statusBar = new QStatusBar(this, "statusBar");
00093        statusBar->setSizeGripEnabled(true);
00094        commandEdit_cursorPositionChanged(0, 0);
00095        editorsLayout->addWidget(statusBar);
00096        
00097        gridLayout->addLayout( editorsLayout, 0, 0 );
00098        languageChange();
00099        resize(QSize(640, 480).expandedTo(minimumSizeHint()));
00100        clearWState( WState_Polished );
00101 
00102        // welcome note
00103        QString welcomeText("\"\"\"");
00104        welcomeText += tr("Scribus Python Console");
00105        welcomeText += "\n\n";
00106        welcomeText += tr(
00107                      "This is derived from standard Python console "
00108                      "so it contains some limitations esp. in the "
00109                      "case of whitespaces. Please consult Scribus "
00110                      "manual for more informations.");
00111        welcomeText += "\"\"\"\n";
00112        commandEdit->setText(welcomeText);
00113        commandEdit->selectAll(true);
00114 
00115        connect(commandEdit, SIGNAL(cursorPositionChanged(int, int)), this, SLOT(commandEdit_cursorPositionChanged(int, int)));
00116 }
00117 
00118 /*
00119  *  Destroys the object and frees any allocated resources
00120  */
00121 PythonConsole::~PythonConsole()
00122 {
00123        //delete the highlighter
00124        delete commandEdit->syntaxHighlighter();
00125 }
00126 
00127 void PythonConsole::setFonts()
00128 {
00129        QFont font = QFont("Fixed");
00130        font.setStyleHint(QFont::TypeWriter);
00131        font.setPointSize(PrefsManager::instance()->appPrefs.AppFontSize);
00132        commandEdit->setFont(font); 
00133        outputEdit->setFont(font);
00134 }
00135 
00136 void PythonConsole::closeEvent(QCloseEvent *)
00137 {
00138        emit paletteShown(false);
00139 }
00140 
00141 void PythonConsole::commandEdit_cursorPositionChanged(int para, int pos)
00142 {
00143        statusBar->message( tr("Line: %1 Column: %2").arg(para+1).arg(pos+1));
00144 }
00145 
00146 void PythonConsole::languageChange()
00147 {
00148        setCaption( tr("Script Console"));
00149        QToolTip::add(commandEdit, "<qt>" + tr("Write your commands here. A selection is processed as script") + "</qt>");
00150        QToolTip::add(outputEdit, "<qt>" + tr("Output of your script") + "</qt>");
00151 }
00152 
00153 void PythonConsole::slot_runScript()
00154 {
00155        outputEdit->clear();
00156        parsePythonString();
00157        emit runCommand();
00158        outputEdit->setCursorPosition(0, 0);
00159 }
00160 
00161 void PythonConsole::slot_runScriptAsConsole()
00162 {
00163        parsePythonString();
00164        commandEdit->clear();
00165        // content is destroyed. This is to prevent overwritting
00166        filename = QString::null;
00167        outputEdit->append("\n>>> " + m_command);
00168        emit runCommand();
00169 }
00170 
00171 void PythonConsole::parsePythonString()
00172 {
00173        if (commandEdit->hasSelectedText())
00174               m_command = commandEdit->selectedText();
00175        else
00176        {
00177               commandEdit->selectAll(true);
00178               m_command = commandEdit->selectedText();
00179               commandEdit->selectAll(false);
00180        }
00181        // prevent user's wrong selection
00182        m_command += '\n';
00183 }
00184 
00185 /*
00186  * supplementary slots. Saving etc.
00187  */
00188 void PythonConsole::slot_open()
00189 {
00190        filename = QFileDialog::getOpenFileName(".",
00191                      tr("Python Scripts (*.py *.PY)"),
00192                      this,
00193                      "ofdialog",
00194                      tr("Open Python Script File"));
00195        if (filename.isNull())
00196               return;
00197        QFile file(filename);
00198        if (file.open(IO_ReadOnly))
00199        {
00200               QTextStream stream(&file);
00201               commandEdit->setText(stream.read());
00202               file.close();
00203        }
00204 }
00205 
00206 void PythonConsole::slot_save()
00207 {
00208        if (filename.isNull())
00209        {
00210               slot_saveAs();
00211               return;
00212        }
00213        QFile f(filename);
00214        if (f.open(IO_WriteOnly))
00215        {
00216               QTextStream stream(&f);
00217               stream << commandEdit->text();
00218               f.close();
00219        }
00220 }
00221 
00222 void PythonConsole::slot_saveAs()
00223 {
00224        QString oldFname = filename;
00225        filename = QFileDialog::getSaveFileName(".",
00226                      tr("Python Scripts (*.py *.PY)"),
00227                      this,
00228                      "sfdialog",
00229                      tr("Save the Python Commands in File"));
00230        if (filename.isNull())
00231               return;
00232        QFile f(filename);
00233        if (f.exists())
00234        {
00235               QString fn = QDir::convertSeparators(filename);
00236               if (ScMessageBox::warning(this, CommonStrings::trWarning,
00237                      "<qt>" + tr(QString("File %1 already exists. Do you want to replace it?").arg(fn)) + "</qt>",
00238                      QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
00239               {
00240                      filename = oldFname;
00241                      return;
00242               }
00243        }
00244        slot_save();
00245 }
00246 
00247 void PythonConsole::slot_saveOutput()
00248 {
00249        QString fname = QFileDialog::getSaveFileName(".",
00250                      tr("Text Files (*.txt)"),
00251                      this,
00252                      "sfdialog",
00253                      tr("Save Current Output"));
00254        if (fname == QString::null)
00255               return;
00256        QFile f(fname);
00257        if (!f.exists())
00258        {
00259               QString fn = QDir::convertSeparators(filename);
00260               if (QMessageBox::warning(this, CommonStrings::trWarning,
00261                      "<qt>" + tr(QString("File %1 already exists. Do you want to replace it?").arg(fn)) + "</qt>",
00262                      QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
00263                      return;
00264        }
00265        // save
00266        if (f.open(IO_WriteOnly))
00267        {
00268               QTextStream stream(&f);
00269               stream << outputEdit->text();
00270               f.close();
00271        }
00272 }
00273 
00274 void PythonConsole::slot_quit()
00275 {
00276        emit paletteShown(false);
00277 }
00278 
00279 /*
00280  * Syntax highlighting
00281  */
00282 SyntaxHighlighter::SyntaxHighlighter(QTextEdit *textEdit) : QSyntaxHighlighter(textEdit)
00283 {
00284        // Reserved keywords in Python 2.4
00285        keywords << "and" << "assert" << "break" << "class" << "continue" << "def"
00286                       << "del" << "elif" << "else" << "except" << "exec" << "finally"
00287                       << "for" << "from" << "global" << "if" << "import" << "in"
00288                       << "is" << "lambda" << "not" << "or" << "pass" << "print" << "raise"
00289                       << "return" << "try" << "while" << "yield";
00290 }
00291 
00292 int SyntaxHighlighter::highlightParagraph(const QString &text, int endStateOfLastPara)
00293 {
00294        // position in the text
00295        unsigned int i = 0;
00296 
00297        /* ! ! signals error message, which we want in red */
00298        if (text.length() >= 3 && text[0] == '!' && text[1] == ' ' && text[2] == '!')
00299        {
00300               setFormat(0, 3, QColor(Qt::white));
00301               setFormat(3, text.length() - 2, colors.errorColor);
00302               return 0;
00303        }
00304 
00305        /* Turns of syntax highlighting for this line */
00306        if (text.length() >= 2 && text[0] == '!' && text[i + 1] == ' ')
00307        {
00308               setFormat(0, 2, QColor(Qt::white));
00309               setFormat(2, text.length() - 2, colors.textColor);
00310               return 0;
00311        }
00312 
00313        if (endStateOfLastPara == 1)
00314        {
00315               int docstrEnd = text.find("\"\"\"");
00316               if (docstrEnd == -1)
00317                      docstrEnd = text.length();
00318               setFormat(i, docstrEnd + 3, colors.commentColor);
00319               if (docstrEnd == (int)text.length())
00320                      return 1;
00321               i = docstrEnd + 3; // move back to """
00322        }
00323 
00324        while (i < text.length())
00325        {
00326               // HACK: remember the i is *the next* index!
00327               QChar ch = text[i++];
00328 
00329               if (ch.isLetter())
00330               {
00331                      // Read one word
00332                      unsigned long s = i - 1;
00333                      QString actualWord = ch;
00334                      while ((ch = text[i]).isLetterOrNumber() || ch == '_')
00335                      {
00336                             i++;
00337                             actualWord += ch;
00338                      }
00339 
00340                      // Check for reserved keywords
00341                      if (keywords.contains(actualWord) != 0)
00342                             setFormat(s, (i - s), colors.keywordColor);
00343                      else
00344                             setFormat(s, (i - s), colors.textColor);
00345               } // ch.isletter
00346               else if (ch == '+' && text[i] != '=')
00347                      setFormat(i - 1, 1, colors.signColor);
00348               else if (ch == '-' && text[i] != '=')
00349                      setFormat(i - 1, 1, colors.signColor);
00350               else if (ch == '*' && text[i] == '*' && text[i + 1] != '=')
00351                      setFormat(i - 1, 2, colors.signColor);
00352               else if (ch == '*' && (text[i] != '=' && text[i] != '*'))
00353                      setFormat(i - 1, 1, colors.signColor);
00354               else if (ch == '/' && text[i] == '/' && text[i + 1] != '=')
00355                      setFormat(i - 1, 2, colors.signColor);
00356               else if (ch == '/' && (text[i] != '=' && text[i] != '/'))
00357                      setFormat(i - 1, 1, colors.signColor);
00358               else if (ch == '%' && text[i] != '=')
00359                      setFormat(i - 1, 1, colors.signColor);
00360               else if (ch == '&' && text[i] != '=')
00361                      setFormat(i - 1, 1, colors.signColor);
00362               else if (ch == '|' && text[i] != '=')
00363                      setFormat(i - 1, 1, colors.signColor);
00364               else if (ch == '^' && text[i] != '=')
00365                      setFormat(i - 1, 1, colors.signColor);
00366               else if (ch == '=' && text[i] == '=')
00367               {
00368                      setFormat(i - 1, 2, colors.signColor);
00369                      i++;
00370               }
00371               else if (ch == '!' && text[i] == '=')
00372               {
00373                      setFormat(i - 1, 2, colors.signColor);
00374                      i++;
00375               }
00376               else if (ch == '~')
00377                      setFormat(i - 1, 1, colors.signColor);
00378               else if (ch == '<')
00379               {
00380                      if (text[i] == '>')
00381                      {
00382                             setFormat(i -1, 2, colors.signColor);
00383                             i++;
00384                      }
00385                      else if (text[i] == '<' && text[i + 1] == '=')
00386                      {
00387                             setFormat(i - 1, 3, colors.textColor);
00388                             i += 2;
00389                      }
00390                      else if (text[i] == '=' || text[i] == '<')
00391                      {
00392                             setFormat(i - 1, 2, colors.signColor);
00393                             i++;
00394                      }
00395                      else
00396                             setFormat(i - 1, 1, colors.signColor);
00397               } // ch == '<'
00398               else if (ch == '>')
00399               {
00400                      if (text[i] == '>' && text[i + 1] == '=')
00401                      {
00402                             setFormat(i - 1, 3, colors.textColor);
00403                             i += 2;
00404                      }
00405                      else if (text[i] == '=' || text[i] == '>')
00406                      {
00407                             setFormat(i - 1, 2, colors.signColor);
00408                             i++;
00409                      }
00410                      else
00411                             setFormat(i - 1, 1, colors.signColor);
00412               } // ch == '>'
00413               else if (ch >= '0' && ch <= '9')
00414               {
00415                      unsigned long s = i - 1; /* Start of number */
00416                      if (text[i] == 'x' || text[i] == 'X')
00417                      {
00418                             i++;
00419                             while ((text[i] >= '0' && text[i] <= '9') || (text[i] >= 'a' && text[i] <= 'f') || (text[i] >= 'A' && text[i] <= 'F'))
00420                                    i++;
00421                             if (text[i] == 'L')
00422                                    i++;
00423                             setFormat(s, (i - s), colors.numberColor);
00424                      }
00425                      while (text[i] >= '0' && text[i] <= '9')
00426                             i++;
00427 
00428                      bool floating = false;
00429                      if (text[i] == '.')
00430                      {
00431                             i++;
00432                             floating = true;
00433                             while (text[i] >= '0' && text[i] <= '9')
00434                                    i++;
00435                      }
00436                      if (text[i] == 'e' || text[i] == 'E')
00437                      {
00438                             floating = true;
00439                             i++;
00440                             if (text[i] == '+' || text[i] == '-')
00441                                    i++;
00442                             while (text[i] >= '0' && text[i] <= '9')
00443                                    i++;
00444                      }
00445                      if (!floating && text[i] == 'L')
00446                             i++;
00447                      setFormat(s, (i - s), colors.numberColor);
00448               } // if number
00449               else if ( ch == '@')
00450                      setFormat(i - 1, 1, colors.signColor);
00451               else if (ch == '#')
00452               {
00453                      setFormat((i - 1), (text.length() - i), colors.commentColor);
00454                      i = text.length();
00455               }
00456               // docstrings etc. (""" blah """)
00457               else if (ch == '"' && text.at(i) == '"' && text.at(i+1) == '"')
00458               {
00459                      bool cont = false; // continue?
00460                      int docstrEnd = text.find("\"\"\"", i + 2);
00461                      if (docstrEnd == -1)
00462                      {
00463                             docstrEnd = text.length() - i;
00464                             cont = true;
00465                      }
00466                      setFormat(i - 1, docstrEnd + 2, colors.commentColor);
00467                      if (cont)
00468                             return 1; // comment """ is opened
00469                      i += docstrEnd + 2;
00470               } // docstrings
00471               else if ( ch == '\'' || ch == '"')
00472               {
00473                      unsigned long s = i - 1;
00474                      if (text[i] != ch)
00475                      {
00476                             while (text[i] != ch && i != text.length())
00477                                    i++;
00478                             if (text[i] == ch)
00479                                    i++;
00480                             setFormat(s, (i - s), colors.stringColor);
00481                      }
00482                      else if (text[i] == ch && text[i + 1] == ch)
00483                      {
00484                             i += 2;
00485                             setFormat(s, (i - s), colors.textColor);
00486 
00487                      }
00488                      else if (text[i] == ch)
00489                      {
00490                             i++;
00491                             setFormat(s, (i - s), colors.stringColor);
00492                      }
00493               }
00494               /* Default */
00495               else setFormat(i - 1, 1, colors.textColor);
00496        }      // End of while statement
00497 
00498        return 0;
00499 }
00500 
00501 SyntaxColors::SyntaxColors()
00502 {
00503        PrefsContext* prefs = PrefsManager::instance()->prefsFile->getPluginContext("scriptplugin");
00504        errorColor.setNamedColor(prefs->get("syntaxerror", "#aa0000"));
00505        commentColor.setNamedColor(prefs->get("syntaxcomment", "#A0A0A0"));
00506        keywordColor.setNamedColor(prefs->get("syntaxkeyword", "#00007f"));
00507        signColor.setNamedColor(prefs->get("syntaxsign", "#aa00ff"));
00508        numberColor.setNamedColor(prefs->get("syntaxnumber", "#ffaa00"));
00509        stringColor.setNamedColor(prefs->get("syntaxstring", "#005500"));
00510        textColor.setNamedColor(prefs->get("syntaxtext", "#000000"));
00511 }
00512 
00513 SyntaxColors::~SyntaxColors()
00514 {
00515        PrefsContext* prefs = PrefsManager::instance()->prefsFile->getPluginContext("scriptplugin");
00516        prefs->set("syntaxerror", qcolor2named(errorColor));
00517        prefs->set("syntaxcomment", qcolor2named(commentColor));
00518        prefs->set("syntaxkeyword", qcolor2named(keywordColor));
00519        prefs->set("syntaxsign", qcolor2named(signColor));
00520        prefs->set("syntaxnumber", qcolor2named(numberColor));
00521        prefs->set("syntaxstring", qcolor2named(stringColor));
00522        prefs->set("syntaxtext", qcolor2named(textColor));
00523 }
00524 
00525 QString SyntaxColors::qcolor2named(QColor color)
00526 {
00527        int r, g, b;
00528        QString retval("#");
00529        QString oct;
00530        color.getRgb(&r, &g, &b);
00531        retval += oct.setNum(r, 16).rightJustify(2, '0');
00532        retval += oct.setNum(g, 16).rightJustify(2, '0');
00533        retval += oct.setNum(b, 16).rightJustify(2, '0');
00534        return retval;
00535 }