Files
create/src/Gui/EditorView.cpp
2015-09-29 10:00:23 +02:00

585 lines
16 KiB
C++

/***************************************************************************
* Copyright (c) 2007 Werner Mayer <wmayer[at]users.sourceforge.net> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library 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 Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#ifndef _PreComp_
# include <QAbstractTextDocumentLayout>
# include <QApplication>
# include <QClipboard>
# include <QDateTime>
# include <QHBoxLayout>
# include <QMessageBox>
# include <QPainter>
# include <QPrinter>
# include <QPrintDialog>
# include <QScrollBar>
# include <QPlainTextEdit>
# include <QPrintPreviewDialog>
# include <QTextBlock>
# include <QTextCodec>
# include <QTextStream>
# include <QTimer>
#endif
#include "EditorView.h"
#include "Application.h"
#include "BitmapFactory.h"
#include "FileDialog.h"
#include "Macro.h"
#include "PythonDebugger.h"
#include "PythonEditor.h"
#include <Base/Interpreter.h>
#include <Base/Parameter.h>
using namespace Gui;
namespace Gui {
class EditorViewP {
public:
QPlainTextEdit* textEdit;
QString fileName;
EditorView::DisplayName displayName;
QTimer* activityTimer;
uint timeStamp;
bool lock;
QStringList undos;
QStringList redos;
};
}
// -------------------------------------------------------
/* TRANSLATOR Gui::EditorView */
/**
* Constructs a EditorView which is a child of 'parent', with the
* name 'name'.
*/
EditorView::EditorView(QPlainTextEdit* editor, QWidget* parent)
: MDIView(0,parent,0), WindowParameter( "Editor" )
{
d = new EditorViewP;
d->lock = false;
d->displayName = EditorView::FullName;
// create the editor first
d->textEdit = editor;
d->textEdit->setLineWrapMode(QPlainTextEdit::NoWrap);
// Create the layout containing the workspace and a tab bar
QFrame* hbox = new QFrame(this);
hbox->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
QHBoxLayout* layout = new QHBoxLayout();
layout->setMargin(1);
layout->addWidget(d->textEdit);
d->textEdit->setParent(hbox);
hbox->setLayout(layout);
setCentralWidget(hbox);
setCurrentFileName(QString());
d->textEdit->setFocus();
setWindowIcon(d->textEdit->windowIcon());
ParameterGrp::handle hPrefGrp = getWindowParameter();
hPrefGrp->Attach( this );
hPrefGrp->NotifyAll();
d->activityTimer = new QTimer(this);
connect(d->activityTimer, SIGNAL(timeout()),
this, SLOT(checkTimestamp()) );
connect(d->textEdit->document(), SIGNAL(modificationChanged(bool)),
this, SLOT(setWindowModified(bool)));
connect(d->textEdit->document(), SIGNAL(undoAvailable(bool)),
this, SLOT(undoAvailable(bool)));
connect(d->textEdit->document(), SIGNAL(redoAvailable(bool)),
this, SLOT(redoAvailable(bool)));
connect(d->textEdit->document(), SIGNAL(contentsChange(int, int, int)),
this, SLOT(contentsChange(int, int, int)));
}
/** Destroys the object and frees any allocated resources */
EditorView::~EditorView()
{
d->activityTimer->stop();
delete d->activityTimer;
delete d;
getWindowParameter()->Detach( this );
}
QPlainTextEdit* EditorView::getEditor() const
{
return d->textEdit;
}
void EditorView::OnChange(Base::Subject<const char*> &rCaller,const char* rcReason)
{
ParameterGrp::handle hPrefGrp = getWindowParameter();
if (strcmp(rcReason, "EnableLineNumber") == 0) {
//bool show = hPrefGrp->GetBool( "EnableLineNumber", true );
}
}
void EditorView::checkTimestamp()
{
QFileInfo fi(d->fileName);
uint timeStamp = fi.lastModified().toTime_t();
if (timeStamp != d->timeStamp) {
switch( QMessageBox::question( this, tr("Modified file"),
tr("%1.\n\nThis has been modified outside of the source editor. Do you want to reload it?").arg(d->fileName),
QMessageBox::Yes|QMessageBox::Default, QMessageBox::No|QMessageBox::Escape) )
{
case QMessageBox::Yes:
// updates time stamp and timer
open( d->fileName );
return;
case QMessageBox::No:
d->timeStamp = timeStamp;
break;
}
}
d->activityTimer->setSingleShot(true);
d->activityTimer->start(3000);
}
/**
* Runs the action specified by \a pMsg.
*/
bool EditorView::onMsg(const char* pMsg,const char** ppReturn)
{
if (strcmp(pMsg,"Save")==0){
saveFile();
return true;
} else if (strcmp(pMsg,"SaveAs")==0){
saveAs();
return true;
} else if (strcmp(pMsg,"Cut")==0){
cut();
return true;
} else if (strcmp(pMsg,"Copy")==0){
copy();
return true;
} else if (strcmp(pMsg,"Paste")==0){
paste();
return true;
} else if (strcmp(pMsg,"Undo")==0){
undo();
return true;
} else if (strcmp(pMsg,"Redo")==0){
redo();
return true;
} else if (strcmp(pMsg,"ViewFit")==0){
// just ignore this
return true;
}
return false;
}
/**
* Checks if the action \a pMsg is available. This is for enabling/disabling
* the corresponding buttons or menu items for this action.
*/
bool EditorView::onHasMsg(const char* pMsg) const
{
if (strcmp(pMsg,"Run")==0) return true;
if (strcmp(pMsg,"DebugStart")==0) return true;
if (strcmp(pMsg,"DebugStop")==0) return true;
if (strcmp(pMsg,"SaveAs")==0) return true;
if (strcmp(pMsg,"Print")==0) return true;
if (strcmp(pMsg,"PrintPreview")==0) return true;
if (strcmp(pMsg,"PrintPdf")==0) return true;
if (strcmp(pMsg,"Save")==0) {
return d->textEdit->document()->isModified();
} else if (strcmp(pMsg,"Cut")==0) {
bool canWrite = !d->textEdit->isReadOnly();
return (canWrite && (d->textEdit->textCursor().hasSelection()));
} else if (strcmp(pMsg,"Copy")==0) {
return ( d->textEdit->textCursor().hasSelection() );
} else if (strcmp(pMsg,"Paste")==0) {
QClipboard *cb = QApplication::clipboard();
QString text;
// Copy text from the clipboard (paste)
text = cb->text();
bool canWrite = !d->textEdit->isReadOnly();
return ( !text.isEmpty() && canWrite );
} else if (strcmp(pMsg,"Undo")==0) {
return d->textEdit->document()->isUndoAvailable ();
} else if (strcmp(pMsg,"Redo")==0) {
return d->textEdit->document()->isRedoAvailable ();
}
return false;
}
/** Checking on close state. */
bool EditorView::canClose(void)
{
if ( !d->textEdit->document()->isModified() )
return true;
this->setFocus(); // raises the view to front
switch( QMessageBox::question(this, tr("Unsaved document"),
tr("The document has been modified.\n"
"Do you want to save your changes?"),
QMessageBox::Yes|QMessageBox::Default, QMessageBox::No,
QMessageBox::Cancel|QMessageBox::Escape))
{
case QMessageBox::Yes:
return saveFile();
case QMessageBox::No:
return true;
case QMessageBox::Cancel:
return false;
default:
return false;
}
}
void EditorView::setDisplayName(EditorView::DisplayName type)
{
d->displayName = type;
}
/**
* Saves the content of the editor to a file specified by the appearing file dialog.
*/
bool EditorView::saveAs(void)
{
QString fn = FileDialog::getSaveFileName(this, QObject::tr("Save Macro"),
QString::null, tr("FreeCAD macro (*.FCMacro);;Python (*.py)"));
if (fn.isEmpty())
return false;
setCurrentFileName(fn);
return saveFile();
}
/**
* Opens the file \a fileName.
*/
bool EditorView::open(const QString& fileName)
{
if (!QFile::exists(fileName))
return false;
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return false;
d->lock = true;
d->textEdit->setPlainText(QString::fromUtf8(file.readAll()));
d->lock = false;
d->undos.clear();
d->redos.clear();
file.close();
QFileInfo fi(fileName);
d->timeStamp = fi.lastModified().toTime_t();
d->activityTimer->setSingleShot(true);
d->activityTimer->start(3000);
setCurrentFileName(fileName);
return true;
}
/**
* Copies the selected text to the clipboard and deletes it from the text edit.
* If there is no selected text nothing happens.
*/
void EditorView::cut(void)
{
d->textEdit->cut();
}
/**
* Copies any selected text to the clipboard.
*/
void EditorView::copy(void)
{
d->textEdit->copy();
}
/**
* Pastes the text from the clipboard into the text edit at the current cursor position.
* If there is no text in the clipboard nothing happens.
*/
void EditorView::paste(void)
{
d->textEdit->paste();
}
/**
* Undoes the last operation.
* If there is no operation to undo, i.e. there is no undo step in the undo/redo history, nothing happens.
*/
void EditorView::undo(void)
{
d->lock = true;
if (!d->undos.isEmpty()) {
d->redos << d->undos.back();
d->undos.pop_back();
}
d->textEdit->document()->undo();
d->lock = false;
}
/**
* Redoes the last operation.
* If there is no operation to undo, i.e. there is no undo step in the undo/redo history, nothing happens.
*/
void EditorView::redo(void)
{
d->lock = true;
if (!d->redos.isEmpty()) {
d->undos << d->redos.back();
d->redos.pop_back();
}
d->textEdit->document()->redo();
d->lock = false;
}
/**
* Shows the printer dialog.
*/
void EditorView::print()
{
QPrinter printer(QPrinter::ScreenResolution);
printer.setFullPage(true);
QPrintDialog dlg(&printer, this);
if (dlg.exec() == QDialog::Accepted) {
d->textEdit->document()->print(&printer);
}
}
void EditorView::printPreview()
{
QPrinter printer(QPrinter::ScreenResolution);
QPrintPreviewDialog dlg(&printer, this);
connect(&dlg, SIGNAL(paintRequested (QPrinter *)),
this, SLOT(print(QPrinter *)));
dlg.exec();
}
void EditorView::print(QPrinter* printer)
{
d->textEdit->document()->print(printer);
}
/**
* Prints the document into a Pdf file.
*/
void EditorView::printPdf()
{
QString filename = FileDialog::getSaveFileName(this, tr("Export PDF"), QString(), tr("PDF file (*.pdf)"));
if (!filename.isEmpty()) {
QPrinter printer(QPrinter::ScreenResolution);
printer.setOutputFormat(QPrinter::PdfFormat);
printer.setOutputFileName(filename);
d->textEdit->document()->print(&printer);
}
}
void EditorView::setCurrentFileName(const QString &fileName)
{
d->fileName = fileName;
/*emit*/ changeFileName(d->fileName);
d->textEdit->document()->setModified(false);
QString name;
QFileInfo fi(fileName);
switch (d->displayName) {
case FullName:
name = fileName;
break;
case FileName:
name = fi.fileName();
break;
case BaseName:
name = fi.baseName();
break;
}
QString shownName;
if (fileName.isEmpty())
shownName = tr("untitled[*]");
else
shownName = QString::fromAscii("%1[*]").arg(name);
shownName += tr(" - Editor");
setWindowTitle(shownName);
setWindowModified(false);
}
QString EditorView::fileName() const
{
return d->fileName;
}
/**
* Saves the contents to a file.
*/
bool EditorView::saveFile()
{
if (d->fileName.isEmpty())
return saveAs();
QFile file(d->fileName);
if (!file.open(QFile::WriteOnly))
return false;
QTextStream ts(&file);
ts.setCodec(QTextCodec::codecForName("UTF-8"));
ts << d->textEdit->document()->toPlainText();
file.close();
d->textEdit->document()->setModified(false);
QFileInfo fi(d->fileName);
d->timeStamp = fi.lastModified().toTime_t();
return true;
}
void EditorView::undoAvailable(bool undo)
{
if (!undo)
d->undos.clear();
}
void EditorView::redoAvailable(bool redo)
{
if (!redo)
d->redos.clear();
}
void EditorView::contentsChange(int position, int charsRemoved, int charsAdded)
{
if (d->lock)
return;
if (charsRemoved > 0 && charsAdded > 0)
return; // syntax highlighting
else if (charsRemoved > 0)
d->undos << tr("%1 chars removed").arg(charsRemoved);
else if (charsAdded > 0)
d->undos << tr("%1 chars added").arg(charsAdded);
else
d->undos << tr("Formatted");
d->redos.clear();
}
/**
* Get the undo history.
*/
QStringList EditorView::undoActions() const
{
return d->undos;
}
/**
* Get the redo history.
*/
QStringList EditorView::redoActions() const
{
return d->redos;;
}
void EditorView::focusInEvent (QFocusEvent * e)
{
d->textEdit->setFocus();
}
// ---------------------------------------------------------
PythonEditorView::PythonEditorView(PythonEditor* editor, QWidget* parent)
: EditorView(editor, parent), _pye(editor)
{
connect(this, SIGNAL(changeFileName(const QString&)),
editor, SLOT(setFileName(const QString&)));
}
PythonEditorView::~PythonEditorView()
{
}
/**
* Runs the action specified by \a pMsg.
*/
bool PythonEditorView::onMsg(const char* pMsg,const char** ppReturn)
{
if (strcmp(pMsg,"Run")==0) {
executeScript();
return true;
}
else if (strcmp(pMsg,"StartDebug")==0) {
QTimer::singleShot(300, this, SLOT(startDebug()));
return true;
}
else if (strcmp(pMsg,"ToggleBreakpoint")==0) {
toggleBreakpoint();
return true;
}
return EditorView::onMsg(pMsg, ppReturn);
}
/**
* Checks if the action \a pMsg is available. This is for enabling/disabling
* the corresponding buttons or menu items for this action.
*/
bool PythonEditorView::onHasMsg(const char* pMsg) const
{
if (strcmp(pMsg,"Run")==0) return true;
if (strcmp(pMsg,"StartDebug")==0) return true;
if (strcmp(pMsg,"ToggleBreakpoint")==0) return true;
return EditorView::onHasMsg(pMsg);
}
/**
* Runs the opened script in the macro manager.
*/
void PythonEditorView::executeScript()
{
// always save the macro when it is modified
if (EditorView::onHasMsg("Save"))
EditorView::onMsg("Save", 0);
Application::Instance->macroManager()->run(Gui::MacroManager::File,fileName().toUtf8());
}
void PythonEditorView::startDebug()
{
_pye->startDebug();
}
void PythonEditorView::toggleBreakpoint()
{
_pye->toggleBreakpoint();
}
void PythonEditorView::showDebugMarker(int line)
{
_pye->showDebugMarker(line);
}
void PythonEditorView::hideDebugMarker()
{
_pye->hideDebugMarker();
}
#include "moc_EditorView.cpp"