00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
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
00083 d->textEdit = editor;
00084 d->textEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
00085
00086
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
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
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
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
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();
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;
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
00569 }
00570
00571 void PythonEditorView::showDebugMarker(int line)
00572 {
00573 m_debugLine = line;
00574
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
00593 }
00594
00595 #include "moc_EditorView.cpp"