Commit 2f376188 authored by David Saxton's avatar David Saxton

Implemented undo/redo.

svn path=/trunk/KDE/kdeedu/kmplot/; revision=524203
parent 4f51735b
......@@ -28,6 +28,7 @@
#include <QMainWindow>
#include <QPixmap>
#include <qslider.h>
#include <QTimer>
// KDE includes
#include <dcopclient.h>
......@@ -81,7 +82,7 @@ MainDlg::MainDlg(QWidget *parentWidget, const char *, QObject *parent ) : DCOPO
coordsDialog = 0;
m_popupmenu = new KMenu(parentWidget);
view = new View( m_readonly, m_modified, m_popupmenu, parentWidget, actionCollection() );
view = new View( m_readonly, m_modified, m_popupmenu, parentWidget, actionCollection(), this );
connect( view, SIGNAL( setStatusBarText(const QString &)), this, SLOT( setReadOnlyStatusBarText(const QString &) ) );
if ( !m_readonly )
......@@ -99,6 +100,15 @@ MainDlg::MainDlg(QWidget *parentWidget, const char *, QObject *parent ) : DCOPO
kmplotio = new KmPlotIO(view->parser());
m_config = KGlobal::config();
m_recentFiles->loadEntries( m_config );
//BEGIN undo/redo stuff
m_currentState = kmplotio->currentState();
m_saveCurrentStateTimer = new QTimer( this );
m_saveCurrentStateTimer->setSingleShot( true );
connect( m_saveCurrentStateTimer, SIGNAL(timeout()), this, SLOT(saveCurrentState()) );
//END undo/redo stuff
// Let's create a Configure Diloag
m_settingsDialog = new KConfigDialog( parentWidget, "settings", Settings::self() );
......@@ -150,6 +160,23 @@ void MainDlg::setupActions()
//END file menu
//BEGIN edit menu
m_undoAction = KStdAction::undo( this, SLOT(undo()), actionCollection() );
m_undoAction->setEnabled( false );
m_redoAction = KStdAction::redo( this, SLOT(redo()), actionCollection() );
m_redoAction->setEnabled( false );
KAction * editAxes = new KAction( i18n( "&Coordinate System..." ), actionCollection(), "editaxes" );
editAxes->setIcon( KIcon("coords.png") );
connect( editAxes, SIGNAL(triggered(bool)), this, SLOT( editAxes() ) );
KAction * editScaling = new KAction( i18n( "&Scaling..." ), actionCollection(), "editscaling" );
editScaling->setIcon( KIcon("scaling") );
connect( editScaling, SIGNAL(triggered(bool)), this, SLOT( editScaling() ) );
//END edit menu
//BEGIN view menu
KAction * zoomIn = new KAction( i18n("Zoom &In"), actionCollection(), "zoom_in" );
zoomIn->setShortcut( "CTRL+1" );
......@@ -164,14 +191,6 @@ void MainDlg::setupActions()
KAction * zoomTrig = new KAction( i18n("&Fit Widget to Trigonometric Functions"), actionCollection(), "zoom_trig" );
connect( zoomTrig, SIGNAL(triggered(bool)), view, SLOT( mnuTrig_clicked() ) );
KAction * editAxes = new KAction( i18n( "&Coordinate System..." ), actionCollection(), "editaxes" );
editAxes->setIcon( KIcon("coords.png") );
connect( editAxes, SIGNAL(triggered(bool)), this, SLOT( editAxes() ) );
KAction * editScaling = new KAction( i18n( "&Scaling..." ), actionCollection(), "editscaling" );
editScaling->setIcon( KIcon("scaling") );
connect( editScaling, SIGNAL(triggered(bool)), this, SLOT( editScaling() ) );
KAction * coordI = new KAction( i18n( "Coordinate System I" ), actionCollection(), "coord_i" );
coordI->setIcon( KIcon("ksys1.png") );
connect( coordI, SIGNAL(triggered(bool)), this, SLOT( slotCoord1() ) );
......@@ -251,6 +270,63 @@ void MainDlg::setupActions()
}
void MainDlg::undo()
{
kDebug() << k_funcinfo << endl;
if ( m_undoStack.isEmpty() )
return;
m_redoStack.push( m_currentState );
m_currentState = m_undoStack.pop();
kmplotio->restore( m_currentState );
view->drawPlot();
m_undoAction->setEnabled( !m_undoStack.isEmpty() );
m_redoAction->setEnabled( true );
}
void MainDlg::redo()
{
kDebug() << k_funcinfo << endl;
if ( m_redoStack.isEmpty() )
return;
m_undoStack.push( m_currentState );
m_currentState = m_redoStack.pop();
kmplotio->restore( m_currentState );
view->drawPlot();
m_undoAction->setEnabled( true );
m_redoAction->setEnabled( !m_redoStack.isEmpty() );
}
void MainDlg::requestSaveCurrentState()
{
m_saveCurrentStateTimer->start( 0 );
}
void MainDlg::saveCurrentState( )
{
kDebug() << k_funcinfo << endl;
m_redoStack.clear();
m_undoStack.push( m_currentState );
m_currentState = kmplotio->currentState();
// limit stack size to 100 items
while ( m_undoStack.count() > 100 )
m_undoStack.pop_front();
m_undoAction->setEnabled( true );
m_redoAction->setEnabled( false );
}
bool MainDlg::checkModified()
{
if( m_modified )
......@@ -270,16 +346,8 @@ bool MainDlg::checkModified()
}
return true;
}
/*
void MainDlg::slotCleanWindow()
{
if (m_readonly)
return;
view->init(); // set globals to default
view->updateSliders();
view->drawPlot();
}
*/
void MainDlg::slotSave()
{
if ( !m_modified || m_readonly) //don't save if no changes are made or readonly is enabled
......
......@@ -30,7 +30,9 @@
#define MainDlg_included
// Qt includes
#include <QDomDocument>
#include <QPicture>
#include <QStack>
// KDE includes
#include <kaction.h>
......@@ -55,19 +57,21 @@
#include "settingspagefonts.h"
#include "settingspagegeneral.h"
class BrowserExtension;
class EditScaling;
class FunctionEditor;
class KConfigDialog;
class KConstantEditor;
class KToggleFullScreenAction;
class KAboutData;
class KAction;
class KLineEdit;
class KRecentFilesAction;
class QTimer;
class SettingsPageColor;
class SettingsPageConstants;
class SettingsPageFonts;
class SettingsPageGeneral;
class KConstantEditor;
class KToggleFullScreenAction;
class BrowserExtension;
class KAboutData;
/** @short This is the main window of KmPlot.
*
......@@ -104,6 +108,12 @@ public slots:
void editScaling();
/// Toggle whether the sliders window is shown
void toggleShowSliders();
/// Revert to the previous document state (in m_undoStack).
void undo();
/// Revert to the next document state (in m_redoStack).
void redo();
/// Pushes the previous document state to the undo stack and records the current one
void requestSaveCurrentState();
// ///I'm not sure it a delete-all-functions command is necessary
// void slotCleanWindow();
......@@ -175,6 +185,21 @@ private:
CoordsConfigDialog* coordsDialog;
/// The function editor
FunctionEditor * m_functionEditor;
/// The undo stack
QStack<QDomDocument> m_undoStack;
/// The reod stack
QStack<QDomDocument> m_redoStack;
/**
* The current document state - this is pushed to the undo stack when a new
* document state is created.
*/
QDomDocument m_currentState;
/// Timer to ensure saveCurrentState() is called only once for a set of simultaneous changes
QTimer * m_saveCurrentStateTimer;
/// The undo action
KAction * m_undoAction;
/// The redo action
KAction * m_redoAction;
protected slots:
/**
......@@ -184,6 +209,8 @@ protected slots:
void slotOpenRecent( const KUrl &url );
///Update settings when there is a change in the Configure KmPlot dialog
void updateSettings();
/// @see requestSaveCurrentState
void saveCurrentState();
void setReadOnlyStatusBarText(const QString &);
......
......@@ -69,7 +69,7 @@ double View::xmin = 0;
double View::xmax = 0;
View::View(bool const r, bool &mo, KMenu *p, QWidget* parent, KActionCollection *ac )
View::View(bool const r, bool &mo, KMenu *p, QWidget* parent, KActionCollection *ac, MainDlg * mainDlg )
: DCOPObject("View"),
QWidget( parent, Qt::WStaticContents ),
dgr(this),
......@@ -78,7 +78,8 @@ View::View(bool const r, bool &mo, KMenu *p, QWidget* parent, KActionCollection
m_modified(mo),
m_readonly(r),
m_dcop_client(KApplication::kApplication()->dcopClient()),
m_ac(ac)
m_ac(ac),
m_mainDlg( mainDlg )
{
csmode = csparam = -1;
cstype = 0;
......@@ -469,7 +470,6 @@ QPen View::penForPlot( Ufkt *ufkt, Ufkt::PMode p_mode, bool antialias ) const
{
QPen pen;
pen.setCapStyle(Qt::RoundCap);
pen.setColor(ufkt->color);
double lineWidth_mm;
......@@ -477,18 +477,27 @@ QPen View::penForPlot( Ufkt *ufkt, Ufkt::PMode p_mode, bool antialias ) const
{
case 0:
lineWidth_mm = ufkt->linewidth;
pen.setColor(ufkt->color);
break;
case 1:
lineWidth_mm = ufkt->f1_linewidth;
pen.setColor(ufkt->f1_color);
break;
case 2:
lineWidth_mm = ufkt->f2_linewidth;
pen.setColor(ufkt->f2_color);
break;
case 3:
lineWidth_mm = ufkt->integral_linewidth;
pen.setColor(ufkt->integral_color);
break;
default:
assert( !"Unknown p_mode" );
break;
}
double width = mmToPenWidth( lineWidth_mm, antialias );
......
......@@ -52,9 +52,10 @@
#include "Viewiface.h"
#include "xparser.h"
class XParser;
class KMinMax;
class KSliderWindow;
class MainDlg;
class XParser;
/**
* @short This class contains the plots.
......@@ -67,7 +68,7 @@ class View : public QWidget, virtual public ViewIface
Q_OBJECT
public:
/// Contructor sets up the parser, too.
View(bool, bool &, KMenu *, QWidget* parent, KActionCollection *ac );
View(bool, bool &, KMenu *, QWidget* parent, KActionCollection *ac, MainDlg * mainDlg );
void setMinMaxDlg(KMinMax *);
virtual ~View();
......@@ -90,6 +91,8 @@ public:
/// Returns a pointer to the private parser instance m_parser.
/// @see m_parser
XParser* parser();
/// Returns a pointer to the MainDlg
MainDlg * mainDlg() const { return m_mainDlg; }
/// Slider controlling parameter values
QPointer<KSliderWindow> m_sliderWindow;
......@@ -309,6 +312,8 @@ private:
enum Cursor { CursorWait, CursorBlank, CursorArrow, CursorCross, CursorMagnify, CursorLessen, CursorMove };
Cursor m_prevCursor;
MainDlg * m_mainDlg;
};
#endif // View_included
......@@ -29,6 +29,7 @@
#include "kmplotio.h"
#include "kparametereditor.h"
#include "View.h"
#include "MainDlg.h"
#include "xparser.h"
#include <qtimer.h>
......@@ -143,6 +144,7 @@ void FunctionEditor::deleteCurrent()
return;
}
m_view->mainDlg()->requestSaveCurrentState();
m_view->drawPlot();
}
......@@ -168,7 +170,6 @@ void FunctionEditor::syncFunctionList()
if ( item->function2() != -1 )
currentIDs[ item->function2() ] = item;
}
kDebug() << k_funcinfo << "currentFunctionItems.count()="<<currentFunctionItems.count()<<endl;
FunctionListItem * toSelect = 0l;
int newFunctionCount = 0;
......@@ -205,8 +206,6 @@ void FunctionEditor::syncFunctionList()
newFunctionCount++;
}
kDebug() << k_funcinfo << "currentFunctionItems.count()="<<currentFunctionItems.count()<<endl;
// Now, any IDs left in currentIDs are of functions that have been deleted
foreach ( FunctionListItem * item, currentFunctionItems )
{
......@@ -255,18 +254,21 @@ void FunctionEditor::functionSelected( QListWidgetItem * item )
m_functionX = -1;
m_functionY = -1;
QChar prefix = m_view->parser()->functionWithID(m_function)->fstr[0];
if ( prefix == 'r' )
initFromPolar();
else
initFromCartesian();
if ( Ufkt * function = m_view->parser()->functionWithID(m_function) )
{
QChar prefix = function->fstr[0];
if ( prefix == 'r' )
initFromPolar();
else
initFromCartesian();
}
}
}
void FunctionEditor::initFromCartesian()
{
kDebug() << k_funcinfo << endl;
// kDebug() << k_funcinfo << endl;
Ufkt * f = m_view->parser()->functionWithID(m_function);
......@@ -277,31 +279,21 @@ void FunctionEditor::initFromCartesian()
}
m_parameters = f->parameters;
kDebug() << "f->parameters.count()="<<f->parameters.count()<<endl;
m_editor->cartesianEquation->setText( f->fstr );
// m_editor->cartesianHide->setChecked( !f->f_mode);
m_editor->cartesian_f_lineWidth->setValue( f->linewidth );
m_editor->cartesian_f_lineColor->setColor( f->color );
if (f->usecustomxmin)
{
m_editor->cartesianCustomMin->setChecked(true);
m_editor->cartesianMin->setText( f->str_dmin );
}
else
m_editor->cartesianCustomMin->setChecked(false);
if (f->usecustomxmax)
{
m_editor->cartesianCustomMax->setChecked(true);
m_editor->cartesianMax->setText( f->str_dmax );
}
else
m_editor->cartesianCustomMax->setChecked(false);
m_editor->cartesianCustomMin->setChecked( f->usecustomxmin );
m_editor->cartesianMin->setText( f->str_dmin );
m_editor->cartesianCustomMax->setChecked( f->usecustomxmax );
m_editor->cartesianMax->setText( f->str_dmax );
if( f->use_slider == -1 )
{
m_editor->listOfSliders->setCurrentIndex( f->use_slider );
if ( f->parameters.isEmpty() )
m_editor->cartesianDisableParameters->setChecked( true );
else
......@@ -324,14 +316,11 @@ void FunctionEditor::initFromCartesian()
m_editor->precision->setValue( f->integral_precision );
m_editor->cartesian_F_lineWidth->setValue( f->integral_linewidth );
m_editor->cartesian_F_lineColor->setColor( f->integral_color );
if ( f->integral_mode )
{
m_editor->showIntegral->setChecked( f->integral_mode );
m_editor->customPrecision->setChecked( f->integral_use_precision );
m_editor->txtInitX->setText(f->str_startx);
m_editor->txtInitY->setText(f->str_starty);
}
m_editor->showIntegral->setChecked( f->integral_mode );
m_editor->customPrecision->setChecked( f->integral_use_precision );
m_editor->txtInitX->setText(f->str_startx);
m_editor->txtInitY->setText(f->str_starty);
m_editor->stackedWidget->setCurrentIndex( 0 );
m_editor->cartesianEquation->setFocus();
......@@ -340,7 +329,7 @@ void FunctionEditor::initFromCartesian()
void FunctionEditor::initFromPolar()
{
kDebug() << k_funcinfo << endl;
// kDebug() << k_funcinfo << endl;
Ufkt * f = m_view->parser()->functionWithID(m_function);
......@@ -365,7 +354,7 @@ void FunctionEditor::initFromPolar()
void FunctionEditor::initFromParametric()
{
kDebug() << k_funcinfo << endl;
// kDebug() << k_funcinfo << endl;
Ufkt * fx = m_view->parser()->functionWithID(m_functionX);
Ufkt * fy = m_view->parser()->functionWithID(m_functionY);
......@@ -435,6 +424,8 @@ void FunctionEditor::createCartesian()
m_function = m_view->parser()->addFunction( fname );
assert( m_function != -1 );
m_view->mainDlg()->requestSaveCurrentState();
}
......@@ -452,6 +443,8 @@ void FunctionEditor::createParametric()
m_functionY = m_view->parser()->addfkt( QString("y%1(t)=0").arg( name ) );
assert( m_functionY != -1 );
m_view->mainDlg()->requestSaveCurrentState();
}
......@@ -466,11 +459,15 @@ void FunctionEditor::createPolar()
m_function = m_view->parser()->addFunction( fname );
assert( m_function != -1 );
m_view->mainDlg()->requestSaveCurrentState();
}
void FunctionEditor::save()
{
kDebug() << k_funcinfo << endl;
if ( m_function != -1 )
{
Ufkt * f = m_view->parser()->functionWithID( m_function );
......@@ -623,8 +620,13 @@ void FunctionEditor::saveCartesian()
}
//save all settings in the function now when we know no errors have appeared
f->copyFrom( tempFunction );
bool changed = f->copyFrom( tempFunction );
changed |= (old_fstr != f->fstr );
kDebug() << "old_fstr="<<old_fstr<<" f->fstr="<<f->fstr<<" changed="<<changed<<endl;
if ( !changed )
return;
m_view->mainDlg()->requestSaveCurrentState();
if ( FunctionListItem * item = static_cast<FunctionListItem*>(m_functionList->currentItem()) )
item->update();
m_view->drawPlot();
......@@ -660,7 +662,7 @@ void FunctionEditor::fixCartesianArguments( QString * f_str )
void FunctionEditor::savePolar()
{
kDebug() << k_funcinfo << endl;
// kDebug() << k_funcinfo << endl;
Ufkt * f = m_view->parser()->functionWithID( m_function );
if ( !f )
......@@ -724,8 +726,12 @@ void FunctionEditor::savePolar()
}
//save all settings in the function now when we know no errors have appeared
f->copyFrom( tempFunction );
bool changed = !f->copyFrom( tempFunction );
changed |= (old_fstr != f->fstr);
if ( !changed )
return;
m_view->mainDlg()->requestSaveCurrentState();
if ( FunctionListItem * item = static_cast<FunctionListItem*>(m_functionList->currentItem()) )
item->update();
m_view->drawPlot();
......@@ -734,7 +740,7 @@ void FunctionEditor::savePolar()
void FunctionEditor::saveParametric()
{
kDebug() << k_funcinfo << endl;
// kDebug() << k_funcinfo << endl;
Ufkt * fx = m_view->parser()->functionWithID( m_functionX );
Ufkt * fy = m_view->parser()->functionWithID( m_functionY );
......@@ -809,7 +815,8 @@ void FunctionEditor::saveParametric()
}
//save all settings in the function now when we know no errors have appeared
fx->copyFrom( tempFunction );
bool changed = fx->copyFrom( tempFunction );
changed |= (old_fstr != fx->fstr);
// now for the y function
......@@ -828,8 +835,13 @@ void FunctionEditor::saveParametric()
}
//save all settings in the function now when we now no errors have appeared
fy->copyFrom( tempFunction );
changed |= fy->copyFrom( tempFunction );
changed |= (old_fstr != fy->fstr);
if ( !changed )
return;
m_view->mainDlg()->requestSaveCurrentState();
if ( FunctionListItem * item = static_cast<FunctionListItem*>(m_functionList->currentItem()) )
item->update();
m_view->drawPlot();
......@@ -876,7 +888,7 @@ QMimeData * FunctionListWidget::mimeData( const QList<QListWidgetItem *> items )
}
QMimeData * md = new QMimeData;
md->setText( doc.toString() );
md->setData( "text/kmplot", doc.toByteArray() );
return md;
}
......@@ -885,7 +897,7 @@ QMimeData * FunctionListWidget::mimeData( const QList<QListWidgetItem *> items )
QStringList FunctionListWidget::mimeTypes() const
{
QStringList mt;
mt << "text/kmplot" << "text/plain";
mt << "text/kmplot";
return mt;
}
......@@ -893,12 +905,8 @@ QStringList FunctionListWidget::mimeTypes() const
void FunctionListWidget::dragEnterEvent( QDragEnterEvent * event )
{
const QMimeData * md = event->mimeData();
// kDebug() << "md->formats()"<<md->formats()<<endl;
// kDebug() << k_funcinfo << "event->proposedAction()="<<event->proposedAction()<<endl;
// kDebug() << "xml = "<<md->text()<<endl;
event->acceptProposedAction();
event->accept();
// if ( md->hasFormat( "text/kmplot" ) )
if ( md->hasFormat( "text/kmplot" ) )
event->acceptProposedAction();
}
......@@ -907,7 +915,7 @@ void FunctionListWidget::dropEvent( QDropEvent * event )
const QMimeData * md = event->mimeData();
QDomDocument doc( "kmpdoc" );
doc.setContent( md->text() );
doc.setContent( md->data( "text/kmplot" ) );
QDomElement element = doc.documentElement();
for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() )
......
......@@ -80,6 +80,19 @@ class FunctionEditor : public QDockWidget
* Called when a function in the list is selected.
*/
void functionSelected( QListWidgetItem * function );
/**
* Called when the user changes a function widget.
*/
void save();
/**
* Called when the "Edit [parameter] List" button is clicked.
*/
void editParameterList();
/**
* Updates the list of functions (called when a function is added or
* removed from Parser).
*/
void syncFunctionList();
/**
* Called when the user edits any of the widgets relating to a
* cartesian function.
......@@ -95,19 +108,6 @@ class FunctionEditor : public QDockWidget
* parametric function.
*/
void saveParametric();
/**
* Called when the user changes a function widget.
*/
void save();
/**
* Called when the "Edit [parameter] List" button is clicked.
*/
void editParameterList();
/**
* Updates the list of functions (called when a function is added or
* removed from Parser).
*/
void syncFunctionList();