EditorView.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net>     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <QAbstractTextDocumentLayout>
00027 # include <QApplication>
00028 # include <QClipboard>
00029 # include <QDateTime>
00030 # include <QHBoxLayout>
00031 # include <QMessageBox>
00032 # include <QPainter>
00033 # include <QPrinter>
00034 # include <QPrintDialog>
00035 # include <QScrollBar>
00036 # include <QPlainTextEdit>
00037 # include <QPrintPreviewDialog>
00038 # include <QTextBlock>
00039 # include <QTextCodec>
00040 # include <QTextStream>
00041 # include <QTimer>
00042 #endif
00043 
00044 #include "EditorView.h"
00045 #include "Application.h"
00046 #include "BitmapFactory.h"
00047 #include "FileDialog.h"
00048 #include "Macro.h"
00049 #include "PythonDebugger.h"
00050 
00051 #include <Base/Interpreter.h>
00052 #include <Base/Parameter.h>
00053 
00054 using namespace Gui;
00055 namespace Gui {
00056 class EditorViewP {
00057 public:
00058     QPlainTextEdit* textEdit;
00059     QString fileName;
00060     QTimer*  activityTimer;
00061     uint timeStamp;
00062     bool lock;
00063     QStringList undos;
00064     QStringList redos;
00065 };
00066 }
00067 
00068 // -------------------------------------------------------
00069 
00070 /* TRANSLATOR Gui::EditorView */
00071 
00076 EditorView::EditorView(QPlainTextEdit* editor, QWidget* parent)
00077     : MDIView(0,parent,0), WindowParameter( "Editor" )
00078 {
00079     d = new EditorViewP;
00080     d->lock = false;
00081 
00082     // create the editor first
00083     d->textEdit = editor;
00084     d->textEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
00085 
00086     // Create the layout containing the workspace and a tab bar
00087     QFrame* hbox = new QFrame(this);
00088     hbox->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
00089     QHBoxLayout* layout = new QHBoxLayout();
00090     layout->setMargin(1);
00091     layout->addWidget(d->textEdit);
00092     d->textEdit->setParent(hbox);
00093     hbox->setLayout(layout);
00094     setCentralWidget(hbox);
00095 
00096     setCurrentFileName(QString());
00097     d->textEdit->setFocus();
00098 
00099     setWindowIcon(d->textEdit->windowIcon());
00100 
00101     ParameterGrp::handle hPrefGrp = getWindowParameter();
00102     hPrefGrp->Attach( this );
00103     hPrefGrp->NotifyAll();
00104 
00105     d->activityTimer = new QTimer(this);
00106     connect(d->activityTimer, SIGNAL(timeout()),
00107             this, SLOT(checkTimestamp()) );
00108     connect(d->textEdit->document(), SIGNAL(modificationChanged(bool)),
00109             this, SLOT(setWindowModified(bool)));
00110     connect(d->textEdit->document(), SIGNAL(undoAvailable(bool)),
00111             this, SLOT(undoAvailable(bool)));
00112     connect(d->textEdit->document(), SIGNAL(redoAvailable(bool)),
00113             this, SLOT(redoAvailable(bool)));
00114     connect(d->textEdit->document(), SIGNAL(contentsChange(int, int, int)),
00115             this, SLOT(contentsChange(int, int, int)));
00116 }
00117 
00119 EditorView::~EditorView()
00120 {
00121     d->activityTimer->stop();
00122     delete d->activityTimer;
00123     delete d;
00124     getWindowParameter()->Detach( this );
00125 }
00126 
00127 void EditorView::drawMarker(int line, int x, int y, QPainter*)
00128 {
00129 }
00130 
00131 QPlainTextEdit* EditorView::getEditor() const
00132 {
00133     return d->textEdit;
00134 }
00135 
00136 void EditorView::OnChange(Base::Subject<const char*> &rCaller,const char* rcReason)
00137 {
00138     ParameterGrp::handle hPrefGrp = getWindowParameter();
00139     if (strcmp(rcReason, "EnableLineNumber") == 0) {
00140         //bool show = hPrefGrp->GetBool( "EnableLineNumber", true );
00141     }
00142 }
00143 
00144 void EditorView::checkTimestamp()
00145 {
00146     QFileInfo fi(d->fileName);
00147     uint timeStamp =  fi.lastModified().toTime_t();
00148     if (timeStamp != d->timeStamp) {
00149         switch( QMessageBox::question( this, tr("Modified file"), 
00150                 tr("%1.\n\nThis has been modified outside of the source editor. Do you want to reload it?").arg(d->fileName),
00151                 QMessageBox::Yes|QMessageBox::Default, QMessageBox::No|QMessageBox::Escape) )
00152         {
00153             case QMessageBox::Yes:
00154                 // updates time stamp and timer
00155                 open( d->fileName );
00156                 return;
00157             case QMessageBox::No:
00158                 d->timeStamp = timeStamp;
00159                 break;
00160         }
00161     }
00162 
00163     d->activityTimer->setSingleShot(true);
00164     d->activityTimer->start(3000);
00165 }
00166 
00170 bool EditorView::onMsg(const char* pMsg,const char** ppReturn)
00171 {
00172     if (strcmp(pMsg,"Save")==0){
00173         saveFile();
00174         return true;
00175     } else if (strcmp(pMsg,"SaveAs")==0){
00176         saveAs();
00177         return true;
00178     } else if (strcmp(pMsg,"Cut")==0){
00179         cut();
00180         return true;
00181     } else if (strcmp(pMsg,"Copy")==0){
00182         copy();
00183         return true;
00184     } else if (strcmp(pMsg,"Paste")==0){
00185         paste();
00186         return true;
00187     } else if (strcmp(pMsg,"Undo")==0){
00188         undo();
00189         return true;
00190     } else if (strcmp(pMsg,"Redo")==0){
00191         redo();
00192         return true;
00193     } else if (strcmp(pMsg,"ViewFit")==0){
00194         // just ignore this
00195         return true;
00196     }
00197 
00198     return false;
00199 }
00200 
00205 bool EditorView::onHasMsg(const char* pMsg) const
00206 {
00207     if (strcmp(pMsg,"Run")==0)  return true;
00208     if (strcmp(pMsg,"DebugStart")==0)  return true;
00209     if (strcmp(pMsg,"DebugStop")==0)  return true;
00210     if (strcmp(pMsg,"SaveAs")==0)  return true;
00211     if (strcmp(pMsg,"Print")==0) return true;
00212     if (strcmp(pMsg,"PrintPreview")==0) return true;
00213     if (strcmp(pMsg,"PrintPdf")==0) return true;
00214     if (strcmp(pMsg,"Save")==0) { 
00215         return d->textEdit->document()->isModified();
00216     } else if (strcmp(pMsg,"Cut")==0) {
00217         bool canWrite = !d->textEdit->isReadOnly();
00218         return (canWrite && (d->textEdit->textCursor().hasSelection()));
00219     } else if (strcmp(pMsg,"Copy")==0) {
00220         return ( d->textEdit->textCursor().hasSelection() );
00221     } else if (strcmp(pMsg,"Paste")==0) {
00222         QClipboard *cb = QApplication::clipboard();
00223         QString text;
00224 
00225         // Copy text from the clipboard (paste)
00226         text = cb->text();
00227 
00228         bool canWrite = !d->textEdit->isReadOnly();
00229         return ( !text.isEmpty() && canWrite );
00230     } else if (strcmp(pMsg,"Undo")==0) {
00231         return d->textEdit->document()->isUndoAvailable ();
00232     } else if (strcmp(pMsg,"Redo")==0) {
00233         return d->textEdit->document()->isRedoAvailable ();
00234     }
00235 
00236     return false;
00237 }
00238 
00240 bool EditorView::canClose(void)
00241 {
00242     if ( !d->textEdit->document()->isModified() )
00243         return true;
00244     this->setFocus(); // raises the view to front
00245     switch( QMessageBox::question(this, tr("Unsaved document"), 
00246                                     tr("The document has been modified.\n"
00247                                        "Do you want to save your changes?"),
00248                                      QMessageBox::Yes|QMessageBox::Default, QMessageBox::No, 
00249                                      QMessageBox::Cancel|QMessageBox::Escape))
00250     {
00251         case QMessageBox::Yes:
00252             return saveFile();
00253         case QMessageBox::No:
00254             return true;
00255         case QMessageBox::Cancel:
00256             return false;
00257         default:
00258             return false;
00259     }
00260 }
00261 
00265 bool EditorView::saveAs(void)
00266 {
00267     QString fn = FileDialog::getSaveFileName(this, QObject::tr("Save Macro"),
00268         QString::null, tr("FreeCAD macro (*.FCMacro);;Python (*.py)"));
00269     if (fn.isEmpty())
00270         return false;
00271     setCurrentFileName(fn);
00272     return saveFile();
00273 }
00274 
00278 bool EditorView::open(const QString& fileName)
00279 {
00280     if (!QFile::exists(fileName))
00281         return false;
00282     QFile file(fileName);
00283     if (!file.open(QFile::ReadOnly))
00284         return false;
00285 
00286     d->lock = true;
00287     d->textEdit->setPlainText(QString::fromUtf8(file.readAll()));
00288     d->lock = false;
00289     d->undos.clear();
00290     d->redos.clear();
00291     file.close();
00292 
00293     QFileInfo fi(fileName);
00294     d->timeStamp =  fi.lastModified().toTime_t();
00295     d->activityTimer->setSingleShot(true);
00296     d->activityTimer->start(3000);      
00297 
00298     setCurrentFileName(fileName);
00299     return true;
00300 }
00301 
00306 void EditorView::cut(void)
00307 {
00308     d->textEdit->cut();
00309 }
00310 
00314 void EditorView::copy(void)
00315 {
00316     d->textEdit->copy();
00317 }
00318 
00323 void EditorView::paste(void)
00324 {
00325     d->textEdit->paste();
00326 }
00327 
00332 void EditorView::undo(void)
00333 {
00334     d->lock = true;
00335     if (!d->undos.isEmpty()) {
00336         d->redos << d->undos.back();
00337         d->undos.pop_back();
00338     }
00339     d->textEdit->document()->undo();
00340     d->lock = false;
00341 }
00342 
00347 void EditorView::redo(void)
00348 {
00349     d->lock = true;
00350     if (!d->redos.isEmpty()) {
00351         d->undos << d->redos.back();
00352         d->redos.pop_back();
00353     }
00354     d->textEdit->document()->redo();
00355     d->lock = false;
00356 }
00357 
00361 void EditorView::print()
00362 {
00363     QPrinter printer(QPrinter::HighResolution);
00364     printer.setFullPage(true);
00365     QPrintDialog dlg(&printer, this);
00366     if (dlg.exec() == QDialog::Accepted) {
00367         d->textEdit->document()->print(&printer);
00368     }
00369 }
00370 
00371 void EditorView::printPreview()
00372 {
00373     QPrinter printer(QPrinter::HighResolution);
00374     QPrintPreviewDialog dlg(&printer, this);
00375     connect(&dlg, SIGNAL(paintRequested (QPrinter *)),
00376             this, SLOT(print(QPrinter *)));
00377     dlg.exec();
00378 }
00379 
00380 void EditorView::print(QPrinter* printer)
00381 {
00382     d->textEdit->document()->print(printer);
00383 }
00384 
00388 void EditorView::printPdf()
00389 {
00390     QString filename = FileDialog::getSaveFileName(this, tr("Export PDF"), QString(), tr("PDF file (*.pdf)"));
00391     if (!filename.isEmpty()) {
00392         QPrinter printer(QPrinter::HighResolution);
00393         printer.setOutputFormat(QPrinter::PdfFormat);
00394         printer.setOutputFileName(filename);
00395         d->textEdit->document()->print(&printer);
00396     }
00397 }
00398 
00399 void EditorView::setCurrentFileName(const QString &fileName)
00400 {
00401     d->fileName = fileName;
00402     d->textEdit->document()->setModified(false);
00403 
00404     QString shownName;
00405     if (fileName.isEmpty())
00406         shownName = tr("untitled[*]");
00407     else
00408         shownName = QString::fromAscii("%1[*]").arg(fileName);
00409     shownName += tr(" - Editor");
00410     setWindowTitle(shownName);
00411     setWindowModified(false);
00412 }
00413 
00414 QString EditorView::fileName() const
00415 {
00416     return d->fileName;
00417 }
00418 
00422 bool EditorView::saveFile()
00423 {
00424     if (d->fileName.isEmpty())
00425         return saveAs();
00426 
00427     QFile file(d->fileName);
00428     if (!file.open(QFile::WriteOnly))
00429         return false;
00430     QTextStream ts(&file);
00431     ts.setCodec(QTextCodec::codecForName("UTF-8"));
00432     ts << d->textEdit->document()->toPlainText();
00433     file.close();
00434     d->textEdit->document()->setModified(false);
00435 
00436     QFileInfo fi(d->fileName);
00437     d->timeStamp =  fi.lastModified().toTime_t();
00438     return true;
00439 }
00440 
00441 void EditorView::undoAvailable(bool undo)
00442 {
00443     if (!undo)
00444         d->undos.clear();
00445 }
00446 
00447 void EditorView::redoAvailable(bool redo)
00448 {
00449     if (!redo)
00450         d->redos.clear();
00451 }
00452 
00453 void EditorView::contentsChange(int position, int charsRemoved, int charsAdded)
00454 {
00455     if (d->lock)
00456         return;
00457     if (charsRemoved > 0 && charsAdded > 0)
00458         return; // syntax highlighting
00459     else if (charsRemoved > 0)
00460         d->undos << tr("%1 chars removed").arg(charsRemoved);
00461     else if (charsAdded > 0)
00462         d->undos << tr("%1 chars added").arg(charsAdded);
00463     else
00464         d->undos << tr("Formatted");
00465     d->redos.clear();
00466 }
00467 
00471 QStringList EditorView::undoActions() const
00472 {
00473     return d->undos;
00474 }
00475 
00479 QStringList EditorView::redoActions() const
00480 {
00481     return d->redos;;
00482 }
00483 
00484 void EditorView::focusInEvent (QFocusEvent * e)
00485 {
00486     d->textEdit->setFocus();
00487 }
00488 
00489 // ---------------------------------------------------------
00490 
00491 PythonEditorView::PythonEditorView(QPlainTextEdit* editor, QWidget* parent)
00492   : EditorView(editor, parent), m_debugLine(-1),
00493     breakpoint(QLatin1String(":/icons/breakpoint.png")),
00494     debugMarker(QLatin1String(":/icons/debug-marker.png"))
00495 {
00496     _dbg = Application::Instance->macroManager()->debugger();
00497 }
00498 
00499 PythonEditorView::~PythonEditorView()
00500 {
00501 }
00502 
00506 bool PythonEditorView::onMsg(const char* pMsg,const char** ppReturn)
00507 {
00508     if (strcmp(pMsg,"Run")==0) {
00509         executeScript();
00510         return true;
00511     }
00512     else if (strcmp(pMsg,"StartDebug")==0) {
00513         startDebug();
00514         return true;
00515     }
00516     else if (strcmp(pMsg,"ToggleBreakpoint")==0) {
00517         toggleBreakpoint();
00518         return true;
00519     }
00520     return EditorView::onMsg(pMsg, ppReturn);
00521 }
00522 
00527 bool PythonEditorView::onHasMsg(const char* pMsg) const
00528 {
00529     if (strcmp(pMsg,"Run")==0)  return true;
00530     if (strcmp(pMsg,"StartDebug")==0)  return true;
00531     if (strcmp(pMsg,"ToggleBreakpoint")==0)  return true;
00532     return EditorView::onHasMsg(pMsg);
00533 }
00534 
00535 void PythonEditorView::drawMarker(int line, int x, int y, QPainter* p)
00536 {
00537     Breakpoint bp = _dbg->getBreakpoint(fileName());
00538     if (bp.checkLine(line)) {
00539         p->drawPixmap(x, y, breakpoint);
00540     }
00541     if (m_debugLine == line) {
00542         p->drawPixmap(x, y+2, debugMarker);
00543         debugRect = QRect(x, y+2, debugMarker.width(), debugMarker.height());
00544     }
00545 }
00546 
00550 void PythonEditorView::executeScript()
00551 {
00552     Application::Instance->macroManager()->run(Gui::MacroManager::File,fileName().toUtf8());
00553 }
00554 
00555 void PythonEditorView::startDebug()
00556 {
00557     if (_dbg->start()) {
00558         _dbg->runFile(fileName());
00559         _dbg->stop();
00560     }
00561 }
00562 
00563 void PythonEditorView::toggleBreakpoint()
00564 {
00565     QTextCursor cursor = getEditor()->textCursor();
00566     int line = cursor.blockNumber() + 1;
00567     _dbg->toggleBreakpoint(line, fileName());
00568 //    getMarker()->update();
00569 }
00570 
00571 void PythonEditorView::showDebugMarker(int line)
00572 {
00573     m_debugLine = line;
00574 //    getMarker()->update();
00575     QTextCursor cursor = getEditor()->textCursor();
00576     cursor.movePosition(QTextCursor::StartOfBlock);
00577     int cur = cursor.blockNumber() + 1;
00578     if (cur > line) {
00579         for (int i=line; i<cur; i++)
00580             cursor.movePosition(QTextCursor::Up);
00581     }
00582     else if (cur < line) {
00583         for (int i=cur; i<line; i++)
00584             cursor.movePosition(QTextCursor::Down);
00585     }
00586     getEditor()->setTextCursor(cursor);
00587 }
00588 
00589 void PythonEditorView::hideDebugMarker()
00590 {
00591     m_debugLine = -1;
00592 //    getMarker()->update();
00593 }
00594 
00595 #include "moc_EditorView.cpp"

Generated on Wed Nov 23 19:00:10 2011 for FreeCAD by  doxygen 1.6.1