You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/lib/kofficeui/KoCommandHistory.cpp

447 lines
14 KiB

/* This file is part of the KDE project
Copyright (C) 2000 Werner Trobin <trobin@kde.org>
Copyright (C) 2000 David Faure <faure@kde.org>
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., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqlistbox.h>
#include <kaction.h>
#include <kcommand.h>
#include <kdebug.h>
#include <klocale.h>
#include <kpopupmenu.h>
#include <kshortcutlist.h>
#include <kstdaccel.h>
#include <kstdaction.h>
#include "KoCommandHistory.h"
KoListBox::KoListBox( TQWidget *parent , const char *name , WFlags f)
: TQListBox( parent, name, f)
{
setVScrollBarMode( AlwaysOn );
}
void KoListBox::contentsMouseMoveEvent ( TQMouseEvent * e)
{
TQListBoxItem *item_p = itemAt( contentsToViewport(e->pos()));
if ( item_p )
{
int itemIndex = index( item_p );
for ( int i = 0; i<=itemIndex;i++)
setSelected ( i, true );
for ( int i = itemIndex+1; i<(int)count();i++)
setSelected ( i, false );
emit changeNumberOfSelectedItem( itemIndex);
}
}
TQSize KoListBox::sizeHint() const
{
return TQSize(TQMIN(maxItemWidth() + verticalScrollBar()->width() + 4, 400),
TQMIN(count() * itemHeight() + horizontalScrollBar()->height() + 4,300));
}
class KoCommandHistory::KoCommandHistoryPrivate {
public:
KoCommandHistoryPrivate() {
m_savedAt=-1;
m_present=0;
}
~KoCommandHistoryPrivate() {}
int m_savedAt;
KCommand *m_present;
KoListBox *m_undoListBox;
KoListBox *m_redoListBox;
TQLabel *m_undoLabel;
TQLabel *m_redoLabel;
};
////////////
KoCommandHistory::KoCommandHistory() :
m_undo(0), m_redo(0), m_undoLimit(50), m_redoLimit(30), m_first(false)
{
d=new KoCommandHistoryPrivate();
m_commands.setAutoDelete(true);
clear();
}
KoCommandHistory::KoCommandHistory(KActionCollection * actionCollection, bool withMenus) :
m_undoLimit(50), m_redoLimit(30), m_first(false)
{
d=new KoCommandHistoryPrivate();
if (withMenus)
{
KToolBarPopupAction * undo = new KToolBarPopupAction( i18n("&Undo"), "undo",
KStdAccel::undo(), this, TQT_SLOT( undo() ),
actionCollection, /*KStdAction::stdName( KStdAction::Undo )*/"koffice_undo" );
connect( undo->popupMenu(), TQT_SIGNAL( aboutToShow() ), this, TQT_SLOT( slotUndoAboutToShow() ) );
connect( undo->popupMenu(), TQT_SIGNAL( activated( int ) ), this, TQT_SLOT( slotUndoActivated( int ) ) );
m_undo = undo;
m_undoPopup = undo->popupMenu();
d->m_undoListBox = new KoListBox( m_undoPopup );
d->m_undoListBox->setSelectionMode( TQListBox::Multi );
m_undoPopup->insertItem(d->m_undoListBox);
d->m_undoLabel = new TQLabel( m_undoPopup);
m_undoPopup->insertItem(d->m_undoLabel);
connect( d->m_undoListBox, TQT_SIGNAL( selected( int ) ), this, TQT_SLOT( slotUndoActivated( int ) ) );
connect( d->m_undoListBox, TQT_SIGNAL(clicked ( TQListBoxItem *)), this, TQT_SLOT( slotUndoActivated( TQListBoxItem * ) ) );
connect( d->m_undoListBox, TQT_SIGNAL( changeNumberOfSelectedItem( int )), this, TQT_SLOT( slotChangeUndoNumberOfSelectedItem( int )));
KToolBarPopupAction * redo = new KToolBarPopupAction( i18n("&Redo"), "redo",
KStdAccel::redo(), this, TQT_SLOT( redo() ),
actionCollection, /*KStdAction::stdName( KStdAction::Redo )*/
"koffice_redo");
connect( redo->popupMenu(), TQT_SIGNAL( aboutToShow() ), this, TQT_SLOT( slotRedoAboutToShow() ) );
connect( redo->popupMenu(), TQT_SIGNAL( activated( int ) ), this, TQT_SLOT( slotRedoActivated( int ) ) );
m_redo = redo;
m_redoPopup = redo->popupMenu();
d->m_redoListBox = new KoListBox( m_redoPopup );
d->m_redoListBox->setSelectionMode( TQListBox::Multi );
m_redoPopup->insertItem(d->m_redoListBox);
d->m_redoLabel = new TQLabel( m_redoPopup);
m_redoPopup->insertItem(d->m_redoLabel);
connect( d->m_redoListBox, TQT_SIGNAL( selected( int ) ), this, TQT_SLOT( slotRedoActivated( int ) ) );
connect( d->m_redoListBox, TQT_SIGNAL(clicked ( TQListBoxItem *)), this, TQT_SLOT( slotRedoActivated( TQListBoxItem * ) ) );
connect( d->m_redoListBox, TQT_SIGNAL( changeNumberOfSelectedItem( int )), this, TQT_SLOT( slotChangeRedoNumberOfSelectedItem( int )));
}
else
{
m_undo = KStdAction::undo( this, TQT_SLOT( undo() ), actionCollection, "koffice_undo");
m_redo = KStdAction::redo( this, TQT_SLOT( redo() ), actionCollection, "koffice_redo");
m_undoPopup = 0L;
m_redoPopup = 0L;
d->m_redoListBox = 0L;
d->m_undoListBox = 0L;
d->m_redoLabel = 0L;
d->m_undoLabel = 0L;
}
m_commands.setAutoDelete(true);
clear();
}
KoCommandHistory::~KoCommandHistory() {
delete d;
}
KCommand * KoCommandHistory::presentCommand ( )
{ return d->m_present;
}
void KoCommandHistory::clear() {
if (m_undo != 0) {
m_undo->setEnabled(false);
m_undo->setText(i18n("&Undo"));
}
if (m_redo != 0) {
m_redo->setEnabled(false);
m_redo->setText(i18n("&Redo"));
}
d->m_present = 0L;
d->m_savedAt=-42;
}
void KoCommandHistory::addCommand(KCommand *command, bool execute) {
if(command==0L)
return;
int index;
if(d->m_present!=0L && (index=m_commands.findRef(d->m_present))!=-1) {
if (m_first)
--index;
m_commands.insert(index+1, command);
// truncate history
unsigned int count=m_commands.count();
for(unsigned int i=index+2; i<count; ++i)
m_commands.removeLast();
// check whether we still can reach savedAt
if(index<d->m_savedAt)
d->m_savedAt=-1;
d->m_present=command;
m_first=false;
if (m_undo != 0) {
m_undo->setEnabled(true);
m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
}
if((m_redo != 0) && m_redo->isEnabled()) {
m_redo->setEnabled(false);
m_redo->setText(i18n("&Redo"));
}
clipCommands();
}
else { // either this is the first time we add a Command or something has gone wrong
kdDebug(230) << "KoCommandHistory: Initializing the Command History" << endl;
m_commands.clear();
m_commands.append(command);
d->m_present=command;
if (m_undo != 0) {
m_undo->setEnabled(true);
m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
}
if (m_redo != 0) {
m_redo->setEnabled(false);
m_redo->setText(i18n("&Redo"));
}
m_first=false; // Michael B: yes, that *is* correct :-)
}
if ( execute )
{
command->execute();
emit commandExecuted();
emit commandExecuted(command);
}
}
void KoCommandHistory::undo() {
if (m_first || (d->m_present == 0L))
return;
d->m_present->unexecute();
KCommand *commandUndone = d->m_present;
if (m_redo != 0) {
m_redo->setEnabled(true);
m_redo->setText(i18n("&Redo: %1").arg(d->m_present->name()));
}
int index;
if((index=m_commands.findRef(d->m_present))!=-1 && m_commands.prev()!=0) {
d->m_present=m_commands.current();
emit commandExecuted();
emit commandExecuted(commandUndone);
if (m_undo != 0) {
m_undo->setEnabled(true);
m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
}
--index;
if(index==d->m_savedAt)
emit documentRestored();
}
else {
emit commandExecuted();
emit commandExecuted(commandUndone);
if (m_undo != 0) {
m_undo->setEnabled(false);
m_undo->setText(i18n("&Undo"));
}
if(d->m_savedAt==-42)
emit documentRestored();
m_first=true;
}
clipCommands(); // only needed here and in addCommand, NOT in redo
}
void KoCommandHistory::redo() {
int index;
if(m_first) {
d->m_present->execute();
emit commandExecuted();
emit commandExecuted(d->m_present);
m_first=false;
m_commands.first();
if(d->m_savedAt==0)
emit documentRestored();
}
else if((index=m_commands.findRef(d->m_present))!=-1 && m_commands.next()!=0) {
d->m_present=m_commands.current();
d->m_present->execute();
emit commandExecuted();
emit commandExecuted(d->m_present);
++index;
if(index==d->m_savedAt)
emit documentRestored();
}
if (m_undo != 0) {
m_undo->setEnabled(true);
m_undo->setText(i18n("&Undo: %1").arg(d->m_present->name()));
}
if(m_commands.next()!=0) {
if (m_redo != 0) {
m_redo->setEnabled(true);
m_redo->setText(i18n("&Redo: %1").arg(m_commands.current()->name()));
}
}
else {
if((m_redo != 0) && m_redo->isEnabled()) {
m_redo->setEnabled(false);
m_redo->setText(i18n("&Redo"));
}
}
}
void KoCommandHistory::documentSaved() {
if(d->m_present!=0 && !m_first)
d->m_savedAt=m_commands.findRef(d->m_present);
else if(d->m_present==0 && !m_first)
d->m_savedAt=-42; // this value signals that the document has
// been saved with an empty history.
else if(m_first)
d->m_savedAt=-42;
}
void KoCommandHistory::setUndoLimit(int limit) {
if(limit>0 && limit!=m_undoLimit) {
m_undoLimit=limit;
clipCommands();
}
}
void KoCommandHistory::setRedoLimit(int limit) {
if(limit>0 && limit!=m_redoLimit) {
m_redoLimit=limit;
clipCommands();
}
}
void KoCommandHistory::clipCommands() {
int count=m_commands.count();
if(count<=m_undoLimit && count<=m_redoLimit)
return;
int index=m_commands.findRef(d->m_present);
if(index>=m_undoLimit) {
for(int i=0; i<=(index-m_undoLimit); ++i) {
m_commands.removeFirst();
--d->m_savedAt;
if(d->m_savedAt==-1)
d->m_savedAt=-42;
}
index=m_commands.findRef(d->m_present); // calculate the new
count=m_commands.count(); // values (for the redo-branch :)
// make it easier for us... d->m_savedAt==-1 -> invalid
if(d->m_savedAt!=-42 && d->m_savedAt<-1)
d->m_savedAt=-1;
}
// adjust the index if it's the first command
if(m_first)
index=-1;
if((index+m_redoLimit+1)<count) {
if(d->m_savedAt>(index+m_redoLimit))
d->m_savedAt=-1;
for(int i=0; i<(count-(index+m_redoLimit+1)); ++i)
m_commands.removeLast();
}
}
void KoCommandHistory::slotUndoAboutToShow()
{
d->m_undoListBox->clear();
slotChangeUndoNumberOfSelectedItem( -1 );
int i = 0;
TQStringList lst;
if (m_commands.findRef(d->m_present)!=-1)
while ( m_commands.current() && i<10 ) // TODO make number of items configurable ?
{
lst.append(i18n("Undo: %1").arg(m_commands.current()->name()));
m_commands.prev();
}
d->m_undoListBox->insertStringList( lst );
}
void KoCommandHistory::slotUndoActivated( int pos )
{
kdDebug(230) << "KoCommandHistory::slotUndoActivated " << pos << endl;
for ( int i = 0 ; i < pos+1; ++i )
undo();
m_undoPopup->hide();
}
void KoCommandHistory::slotUndoActivated( TQListBoxItem * item)
{
if ( item )
slotUndoActivated( item->listBox()->index(item));
}
void KoCommandHistory::slotRedoActivated( TQListBoxItem * item)
{
if ( item )
slotRedoActivated( item->listBox()->index(item));
}
void KoCommandHistory::slotChangeUndoNumberOfSelectedItem( int pos)
{
d->m_undoLabel->setText( i18n("Undo %n action", "Undo %n actions", pos+1) );
}
void KoCommandHistory::slotChangeRedoNumberOfSelectedItem( int pos)
{
d->m_redoLabel->setText( i18n("Redo %n action", "Redo %n actions", pos+1) );
}
void KoCommandHistory::slotRedoAboutToShow()
{
d->m_redoListBox->clear();
slotChangeRedoNumberOfSelectedItem( -1 );
TQStringList lst;
int i = 0;
if (m_first)
{
d->m_present = m_commands.first();
lst.append(i18n("Redo: %1").arg(d->m_present->name()));
}
if (m_commands.findRef(d->m_present)!=-1 && m_commands.next())
while ( m_commands.current() && i<10 ) // TODO make number of items configurable ?
{
lst.append(i18n("Redo: %1").arg(m_commands.current()->name()));
m_commands.next();
}
d->m_redoListBox->insertStringList( lst );
}
void KoCommandHistory::slotRedoActivated( int pos )
{
kdDebug(230) << "KoCommandHistory::slotRedoActivated " << pos << endl;
for ( int i = 0 ; i < pos+1; ++i )
redo();
m_redoPopup->hide();
}
void KoCommandHistory::updateActions()
{
if ( m_undo && m_redo )
{
m_undo->setEnabled( !m_first && ( d->m_present != 0L ) );
m_redo->setEnabled(m_first || (m_commands.findRef(d->m_present)!=-1 && m_commands.next()!=0));
}
}
void KoCommandHistory::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
#include "KoCommandHistory.moc"