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.
1590 lines
44 KiB
1590 lines
44 KiB
/* This file is part of the KDE project
|
|
|
|
Copyright 1999-2006 The KSpread Team <koffice-devel@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 "kspread_editors.h"
|
|
#include "kspread_canvas.h"
|
|
#include "kspread_cell.h"
|
|
#include "kspread_doc.h"
|
|
#include "selection.h"
|
|
#include "kspread_sheet.h"
|
|
#include "kspread_view.h"
|
|
#include "kspread_util.h"
|
|
#include "formula.h"
|
|
#include "functions.h"
|
|
|
|
#include <tdelistbox.h>
|
|
|
|
#include <tqapplication.h>
|
|
#include <tqlistbox.h>
|
|
#include <tqtimer.h>
|
|
#include <tqlabel.h>
|
|
#include <tqvbox.h>
|
|
#include <tqvaluelist.h>
|
|
#include <private/tqrichtext_p.h>
|
|
|
|
//#include <klineedit.h>
|
|
#include <ktextedit.h>
|
|
#include <tqapplication.h>
|
|
#include <tqbutton.h>
|
|
#include <tqfont.h>
|
|
#include <tqfontmetrics.h>
|
|
#include <tqregexp.h>
|
|
#include <kdebug.h>
|
|
|
|
using namespace KSpread;
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FormulaEditorHighlighter
|
|
*
|
|
****************************************************************************/
|
|
|
|
namespace KSpread
|
|
{
|
|
|
|
class FormulaEditorHighlighter::Private
|
|
{
|
|
public:
|
|
Private()
|
|
{
|
|
canvas = 0;
|
|
tokens = Tokens();
|
|
rangeCount = 0;
|
|
rangeChanged = false;
|
|
}
|
|
|
|
// source for cell reference checking
|
|
Canvas* canvas;
|
|
Tokens tokens;
|
|
uint rangeCount;
|
|
bool rangeChanged;
|
|
};
|
|
|
|
|
|
FormulaEditorHighlighter::FormulaEditorHighlighter(TQTextEdit* textEdit, Canvas* canvas)
|
|
: TQSyntaxHighlighter(textEdit)
|
|
{
|
|
d = new Private();
|
|
d->canvas = canvas;
|
|
}
|
|
|
|
FormulaEditorHighlighter::~FormulaEditorHighlighter()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
const Tokens& FormulaEditorHighlighter::formulaTokens() const
|
|
{
|
|
return d->tokens;
|
|
}
|
|
|
|
int FormulaEditorHighlighter::highlightParagraph(const TQString& text, int /* endStateOfLastPara */)
|
|
{
|
|
// reset syntax highlighting
|
|
setFormat(0, text.length(), TQt::black);
|
|
|
|
// save the old ones to identify range changes
|
|
Tokens oldTokens = d->tokens;
|
|
|
|
// interpret the text as formula
|
|
// we accept invalid/incomplete formulas
|
|
Formula f;
|
|
d->tokens = f.scan(text);
|
|
|
|
TQFont editorFont = textEdit()->currentFont();
|
|
TQFont font;
|
|
|
|
uint oldRangeCount = d->rangeCount;
|
|
|
|
d->rangeCount = 0;
|
|
TQValueList<TQColor> colors = d->canvas->choice()->colors();
|
|
TQValueList<Range> alreadyFoundRanges;
|
|
|
|
for (uint i = 0; i < d->tokens.count(); ++i)
|
|
{
|
|
Token token = d->tokens[i];
|
|
Token::Type type = token.type();
|
|
|
|
switch (type)
|
|
{
|
|
case Token::Cell:
|
|
case Token::Range:
|
|
{
|
|
// don't compare, if we have already found a change
|
|
if (!d->rangeChanged && i < oldTokens.count() && token.text() != oldTokens[i].text())
|
|
{
|
|
d->rangeChanged = true;
|
|
}
|
|
|
|
Range newRange( token.text() );
|
|
|
|
if (!alreadyFoundRanges.contains(newRange))
|
|
{
|
|
alreadyFoundRanges.append(newRange);
|
|
d->rangeCount++;
|
|
}
|
|
setFormat(token.pos() + 1, token.text().length(), colors[ alreadyFoundRanges.findIndex(newRange) % colors.size()] );
|
|
}
|
|
break;
|
|
case Token::Boolean: // True, False (also i18n-ized)
|
|
/* font = TQFont(editorFont);
|
|
font.setBold(true);
|
|
setFormat(token.pos() + 1, token.text().length(), font);*/
|
|
break;
|
|
case Token::Identifier: // function name or named area*/
|
|
/* font = TQFont(editorFont);
|
|
font.setBold(true);
|
|
setFormat(token.pos() + 1, token.text().length(), font);*/
|
|
break;
|
|
|
|
case Token::Unknown:
|
|
case Token::Integer: // 14, 3, 1977
|
|
case Token::Float: // 3.141592, 1e10, 5.9e-7
|
|
case Token::String: // "KOffice", "The quick brown fox..."
|
|
case Token::Operator: // +, *, /, -
|
|
{
|
|
switch (token.asOperator())
|
|
{
|
|
case Token::LeftPar:
|
|
case Token::RightPar:
|
|
//Check where this brace is in relation to the cursor and highlight it if necessary.
|
|
handleBrace( i );
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (oldRangeCount != d->rangeCount)
|
|
d->rangeChanged = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void FormulaEditorHighlighter::handleBrace( uint index )
|
|
{
|
|
int cursorParagraph;
|
|
int cursorPos;
|
|
const Token& token = d->tokens.at( index );
|
|
|
|
textEdit()->getCursorPosition( &cursorParagraph , &cursorPos );
|
|
|
|
int distance = cursorPos-token.pos();
|
|
int opType = token.asOperator();
|
|
bool highlightBrace=false;
|
|
|
|
//Check where the cursor is in relation to this left or right parenthesis token.
|
|
//Only one pair of braces should be highlighted at a time, and if the cursor
|
|
//is between two braces, the inner-most pair should be highlighted.
|
|
|
|
if ( opType == Token::LeftPar )
|
|
{
|
|
//If cursor is directly to the left of this left brace, highlight it
|
|
if ( distance == 1 )
|
|
highlightBrace=true;
|
|
else
|
|
//Cursor is directly to the right of this left brace, highlight it unless
|
|
//there is another left brace to the right (in which case that should be highlighted instead as it
|
|
//is the inner-most brace)
|
|
if (distance==2)
|
|
if ( (index == d->tokens.count()-1) || ( d->tokens.at(index+1).asOperator() != Token::LeftPar) )
|
|
highlightBrace=true;
|
|
|
|
}
|
|
else
|
|
{
|
|
//If cursor is directly to the right of this right brace, highlight it
|
|
if ( distance == 2 )
|
|
highlightBrace=true;
|
|
else
|
|
//Cursor is directly to the left of this right brace, so highlight it unless
|
|
//there is another right brace to the left (in which case that should be highlighted instead as it
|
|
//is the inner-most brace)
|
|
if ( distance == 1 )
|
|
if ( (index == 0) || (d->tokens.at(index-1).asOperator() != Token::RightPar) )
|
|
highlightBrace=true;
|
|
}
|
|
|
|
if (highlightBrace)
|
|
{
|
|
TQFont font = TQFont( textEdit()->currentFont() );
|
|
font.setBold(true);
|
|
setFormat(token.pos() + 1, token.text().length(), font);
|
|
|
|
int matching = findMatchingBrace( index );
|
|
|
|
if (matching != -1)
|
|
{
|
|
Token matchingBrace = d->tokens.at(matching);
|
|
setFormat( matchingBrace.pos() + 1 , matchingBrace.text().length() , font);
|
|
}
|
|
}
|
|
}
|
|
|
|
int FormulaEditorHighlighter::findMatchingBrace(int pos)
|
|
{
|
|
int depth=0;
|
|
int step=0;
|
|
|
|
Tokens tokens = d->tokens;
|
|
|
|
//If this is a left brace we need to step forwards through the text to find the matching right brace,
|
|
//otherwise, it is a right brace so we need to step backwards through the text to find the matching left
|
|
//brace.
|
|
if (tokens.at(pos).asOperator() == Token::LeftPar)
|
|
step = 1;
|
|
else
|
|
step = -1;
|
|
|
|
for (int index=pos ; (index >= 0) && (index < (int) tokens.count() ) ; index += step )
|
|
{
|
|
if (tokens.at(index).asOperator() == Token::LeftPar)
|
|
depth++;
|
|
if (tokens.at(index).asOperator() == Token::RightPar)
|
|
depth--;
|
|
|
|
if (depth == 0)
|
|
{
|
|
return index;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
uint FormulaEditorHighlighter::rangeCount() const
|
|
{
|
|
return d->rangeCount;
|
|
}
|
|
|
|
bool FormulaEditorHighlighter::rangeChanged() const
|
|
{
|
|
return d->rangeChanged;
|
|
}
|
|
|
|
void FormulaEditorHighlighter::resetRangeChanged()
|
|
{
|
|
d->rangeChanged=false;
|
|
}
|
|
|
|
} // namespace KSpread
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FunctionCompletion
|
|
*
|
|
****************************************************************************/
|
|
|
|
class FunctionCompletion::Private
|
|
{
|
|
public:
|
|
CellEditor* editor;
|
|
TQVBox *completionPopup;
|
|
TDEListBox *completionListBox;
|
|
TQLabel* hintLabel;
|
|
};
|
|
|
|
FunctionCompletion::FunctionCompletion( CellEditor* editor ):
|
|
TQObject( editor )
|
|
{
|
|
d = new Private;
|
|
d->editor = editor;
|
|
d->hintLabel = 0;
|
|
|
|
d->completionPopup = new TQVBox( editor->topLevelWidget(), 0, WType_Popup );
|
|
d->completionPopup->setFrameStyle( TQFrame::Box | TQFrame::Plain );
|
|
d->completionPopup->setLineWidth( 1 );
|
|
d->completionPopup->installEventFilter( this );
|
|
d->completionPopup->setSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Minimum);
|
|
|
|
d->completionListBox = new TDEListBox( d->completionPopup );
|
|
d->completionPopup->setFocusProxy( d->completionListBox );
|
|
d->completionListBox->setFrameStyle( TQFrame::NoFrame );
|
|
d->completionListBox->setVariableWidth( true );
|
|
d->completionListBox->installEventFilter( this );
|
|
connect( d->completionListBox, TQ_SIGNAL(selected(const TQString&)), this,
|
|
TQ_SLOT(itemSelected(const TQString&)) );
|
|
connect( d->completionListBox, TQ_SIGNAL(highlighted(const TQString&)), this,
|
|
TQ_SLOT(itemSelected(const TQString&)) );
|
|
|
|
d->hintLabel = new TQLabel( 0, "autocalc", TQt::WStyle_StaysOnTop |
|
|
TQt::WStyle_Customize | TQt::WStyle_NoBorder | TQt::WStyle_Tool | TQt::WX11BypassWM );
|
|
d->hintLabel->setFrameStyle( TQFrame::Plain | TQFrame::Box );
|
|
d->hintLabel->setPalette( TQToolTip::palette() );
|
|
d->hintLabel->hide();
|
|
}
|
|
|
|
FunctionCompletion::~FunctionCompletion()
|
|
{
|
|
delete d->hintLabel;
|
|
delete d;
|
|
}
|
|
|
|
void FunctionCompletion::itemSelected( const TQString& item )
|
|
{
|
|
KSpread::FunctionDescription* desc;
|
|
desc = KSpread::FunctionRepository::self()->functionInfo(item);
|
|
if(!desc)
|
|
{
|
|
d->hintLabel->hide();
|
|
return;
|
|
}
|
|
|
|
TQString helpText = desc->helpText()[0];
|
|
if( helpText.isEmpty() )
|
|
{
|
|
d->hintLabel->hide();
|
|
return;
|
|
}
|
|
|
|
helpText.append("</qt>").prepend("<qt>");
|
|
d->hintLabel->setText( helpText );
|
|
d->hintLabel->adjustSize();
|
|
|
|
// reposition nicely
|
|
TQPoint pos = d->editor->mapToGlobal( TQPoint( d->editor->width(), 0 ) );
|
|
pos.setY( pos.y() - d->hintLabel->height() - 1 );
|
|
d->hintLabel->move( pos );
|
|
d->hintLabel->show();
|
|
d->hintLabel->raise();
|
|
|
|
// do not show it forever
|
|
//TQTimer::singleShot( 5000, d->hintLabel, TQ_SLOT( hide()) );
|
|
}
|
|
|
|
bool FunctionCompletion::eventFilter( TQObject *obj, TQEvent *ev )
|
|
{
|
|
if ( obj == d->completionPopup || obj == d->completionListBox )
|
|
{
|
|
if ( ev->type() == TQEvent::KeyPress )
|
|
{
|
|
TQKeyEvent *ke = (TQKeyEvent*)ev;
|
|
if ( ke->key() == Key_Enter || ke->key() == Key_Return )
|
|
{
|
|
doneCompletion();
|
|
return true;
|
|
}
|
|
else if ( ke->key() == Key_Left || ke->key() == Key_Right ||
|
|
ke->key() == Key_Up || ke->key() == Key_Down ||
|
|
ke->key() == Key_Home || ke->key() == Key_End ||
|
|
ke->key() == Key_Prior || ke->key() == Key_Next )
|
|
return false;
|
|
|
|
d->hintLabel->hide();
|
|
d->completionPopup->close();
|
|
d->editor->setFocus();
|
|
TQApplication::sendEvent( d->editor, ev );
|
|
return true;
|
|
}
|
|
|
|
if ( ev->type() == TQEvent::MouseButtonDblClick )
|
|
{
|
|
doneCompletion();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FunctionCompletion::doneCompletion()
|
|
{
|
|
d->hintLabel->hide();
|
|
d->completionPopup->close();
|
|
d->editor->setFocus();
|
|
emit selectedCompletion( d->completionListBox->currentText() );
|
|
}
|
|
|
|
void FunctionCompletion::showCompletion( const TQStringList &choices )
|
|
{
|
|
if( !choices.count() ) return;
|
|
|
|
d->completionListBox->clear();
|
|
for( unsigned i = 0; i < choices.count(); i++ )
|
|
new TQListBoxText( (TQListBox*)d->completionListBox, choices[i] );
|
|
d->completionListBox->setCurrentItem( 0 );
|
|
|
|
// size of the pop-up
|
|
d->completionPopup->setMaximumHeight( 100 );
|
|
d->completionPopup->resize( d->completionListBox->sizeHint() +
|
|
TQSize( d->completionListBox->verticalScrollBar()->width() + 4,
|
|
d->completionListBox->horizontalScrollBar()->height() + 4 ) );
|
|
int h = d->completionListBox->height();
|
|
int w = d->completionListBox->width();
|
|
|
|
TQPoint pos = d->editor->globalCursorPosition();
|
|
|
|
// if popup is partially invisible, move to other position
|
|
// FIXME check it if it works in Xinerama multihead
|
|
int screen_num = TQApplication::desktop()->screenNumber( d->completionPopup );
|
|
TQRect screen = TQApplication::desktop()->screenGeometry( screen_num );
|
|
if( pos.y() + h > screen.y()+screen.height() )
|
|
pos.setY( pos.y() - h - d->editor->height() );
|
|
if( pos.x() + w > screen.x()+screen.width() )
|
|
pos.setX( screen.x()+screen.width() - w );
|
|
|
|
d->completionPopup->move( pos );
|
|
d->completionListBox->setFocus();
|
|
d->completionPopup->show();
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* CellEditor
|
|
*
|
|
****************************************************************************/
|
|
|
|
class CellEditor::Private
|
|
{
|
|
public:
|
|
Cell* cell;
|
|
Canvas* canvas;
|
|
KTextEdit* textEdit;
|
|
FormulaEditorHighlighter* highlighter;
|
|
FunctionCompletion* functionCompletion;
|
|
TQTimer* functionCompletionTimer;
|
|
|
|
TQPoint globalCursorPos;
|
|
|
|
bool captureAllKeyEvents : 1;
|
|
bool checkChoice : 1;
|
|
bool updateChoice : 1;
|
|
bool updatingChoice : 1;
|
|
|
|
uint length;
|
|
uint fontLength;
|
|
uint length_namecell;
|
|
uint length_text;
|
|
uint currentToken;
|
|
uint rangeCount;
|
|
};
|
|
|
|
|
|
CellEditor::CellEditor( Cell* _cell, Canvas* _parent, bool captureAllKeyEvents, const char* _name )
|
|
: TQWidget( _parent, _name )
|
|
{
|
|
d = new Private();
|
|
d->cell = _cell;
|
|
d->canvas = _parent;
|
|
d->textEdit = new KTextEdit(this);
|
|
d->globalCursorPos = TQPoint();
|
|
d->captureAllKeyEvents = captureAllKeyEvents;
|
|
d->checkChoice = true;
|
|
d->updateChoice = true;
|
|
d->updatingChoice = false;
|
|
d->length = 0;
|
|
d->fontLength = 0;
|
|
d->length_namecell = 0;
|
|
d->length_text = 0;
|
|
d->currentToken = 0;
|
|
d->rangeCount = 0;
|
|
|
|
//TODO - Get rid of TQTextEdit margins, this doesn't seem easily possible in TQt 3.3, so a job for TQt 4 porting.
|
|
|
|
d->textEdit->setHScrollBarMode(TQScrollView::AlwaysOff);
|
|
d->textEdit->setVScrollBarMode(TQScrollView::AlwaysOff);
|
|
d->textEdit->setFrameStyle(TQFrame::NoFrame);
|
|
d->textEdit->setLineWidth(0);
|
|
d->textEdit->installEventFilter( this );
|
|
|
|
d->highlighter = new FormulaEditorHighlighter(d->textEdit, _parent);
|
|
|
|
d->functionCompletion = new FunctionCompletion( this );
|
|
d->functionCompletionTimer = new TQTimer( this );
|
|
connect( d->functionCompletion, TQ_SIGNAL( selectedCompletion( const TQString& ) ),
|
|
TQ_SLOT( functionAutoComplete( const TQString& ) ) );
|
|
connect( d->textEdit, TQ_SIGNAL( textChanged() ), TQ_SLOT( checkFunctionAutoComplete() ) );
|
|
connect( d->functionCompletionTimer, TQ_SIGNAL( timeout() ),
|
|
TQ_SLOT( triggerFunctionAutoComplete() ) );
|
|
|
|
if (!cell()->format()->multiRow(cell()->column(),cell()->row()))
|
|
d->textEdit->setWordWrap(TQTextEdit::NoWrap);
|
|
else
|
|
d->textEdit->setWrapPolicy(TQTextEdit::AtWordOrDocumentBoundary);
|
|
|
|
//TODO - Custom KTextEdit class which supports text completion
|
|
/*
|
|
d->textEdit->setFrame( false );
|
|
d->textEdit->setCompletionMode((TDEGlobalSettings::Completion)canvas()->view()->doc()->completionMode() );
|
|
d->textEdit->setCompletionObject( &canvas()->view()->doc()->completion(),true );
|
|
*/
|
|
setFocusProxy( d->textEdit );
|
|
|
|
connect( d->textEdit, TQ_SIGNAL( cursorPositionChanged(int,int) ), this, TQ_SLOT (slotCursorPositionChanged(int,int)));
|
|
connect( d->textEdit, TQ_SIGNAL( cursorPositionChanged(TQTextCursor*) ), this, TQ_SLOT (slotTextCursorChanged(TQTextCursor*)));
|
|
connect( d->textEdit, TQ_SIGNAL( textChanged() ), this, TQ_SLOT( slotTextChanged() ) );
|
|
|
|
// connect( d->textEdit, TQ_SIGNAL(completionModeChanged( TDEGlobalSettings::Completion )),this,TQ_SLOT (slotCompletionModeChanged(TDEGlobalSettings::Completion)));
|
|
|
|
// A choose should always start at the edited cell
|
|
// canvas()->setChooseMarkerRow( canvas()->markerRow() );
|
|
// canvas()->setChooseMarkerColumn( canvas()->markerColumn() );
|
|
|
|
// set font size according to zoom factor
|
|
TQFont font( _cell->format()->font() );
|
|
font.setPointSizeFloat( 0.01 * _parent->doc()->zoom() * font.pointSizeFloat() );
|
|
d->textEdit->setFont( font );
|
|
|
|
if (d->fontLength == 0)
|
|
{
|
|
TQFontMetrics fm( d->textEdit->font() );
|
|
d->fontLength = fm.width('x');
|
|
}
|
|
}
|
|
|
|
CellEditor::~CellEditor()
|
|
{
|
|
canvas()->endChoose();
|
|
|
|
delete d->highlighter;
|
|
delete d->functionCompletion;
|
|
delete d->functionCompletionTimer;
|
|
delete d;
|
|
}
|
|
|
|
Cell* CellEditor::cell() const
|
|
{
|
|
return d->cell;
|
|
}
|
|
|
|
Canvas* CellEditor::canvas() const
|
|
{
|
|
return d->canvas;
|
|
}
|
|
|
|
TQPoint CellEditor::globalCursorPosition() const
|
|
{
|
|
return d->globalCursorPos;
|
|
}
|
|
|
|
void CellEditor::checkFunctionAutoComplete()
|
|
{
|
|
d->functionCompletionTimer->stop();
|
|
d->functionCompletionTimer->start( 2000, true );
|
|
}
|
|
|
|
void CellEditor::triggerFunctionAutoComplete()
|
|
{
|
|
// tokenize the expression (don't worry, this is very fast)
|
|
int para = 0, curPos = 0;
|
|
d->textEdit->getCursorPosition( ¶, &curPos );
|
|
TQString subtext = d->textEdit->text().left( curPos );
|
|
|
|
KSpread::Formula f;
|
|
KSpread::Tokens tokens = f.scan( subtext );
|
|
if( !tokens.valid() ) return;
|
|
if( tokens.count()<1 ) return;
|
|
|
|
KSpread::Token lastToken = tokens[ tokens.count()-1 ];
|
|
|
|
// last token must be an identifier
|
|
if( !lastToken.isIdentifier() ) return;
|
|
TQString id = lastToken.text();
|
|
if( id.length() < 1 ) return;
|
|
|
|
// find matches in function names
|
|
TQStringList fnames = KSpread::FunctionRepository::self()->functionNames();
|
|
TQStringList choices;
|
|
for( unsigned i=0; i<fnames.count(); i++ )
|
|
if( fnames[i].startsWith( id, false ) )
|
|
choices.append( fnames[i] );
|
|
choices.sort();
|
|
|
|
// no match, don't bother with completion
|
|
if( !choices.count() ) return;
|
|
|
|
// single perfect match, no need to give choices
|
|
if( choices.count()==1 )
|
|
if( choices[0].lower() == id.lower() )
|
|
return;
|
|
|
|
// present the user with completion choices
|
|
d->functionCompletion->showCompletion( choices );
|
|
}
|
|
|
|
void CellEditor::functionAutoComplete( const TQString& item )
|
|
{
|
|
if( item.isEmpty() ) return;
|
|
|
|
int para = 0, curPos = 0;
|
|
d->textEdit->getCursorPosition( ¶, &curPos );
|
|
TQString subtext = text().left( curPos );
|
|
|
|
KSpread::Formula f;
|
|
KSpread::Tokens tokens = f.scan( subtext );
|
|
if( !tokens.valid() ) return;
|
|
if( tokens.count()<1 ) return;
|
|
|
|
KSpread::Token lastToken = tokens[ tokens.count()-1 ];
|
|
if( !lastToken.isIdentifier() ) return;
|
|
|
|
d->textEdit->blockSignals( true );
|
|
d->textEdit->setSelection( 0, lastToken.pos()+1, 0, lastToken.pos()+lastToken.text().length()+1 );
|
|
d->textEdit->insert( item );
|
|
d->textEdit->blockSignals( false );
|
|
}
|
|
|
|
void CellEditor::slotCursorPositionChanged(int /* para */, int pos)
|
|
{
|
|
// kdDebug() << k_funcinfo << endl;
|
|
|
|
// TODO Stefan: optimize this function!
|
|
|
|
// turn choose mode on/off
|
|
if (!checkChoice())
|
|
return;
|
|
|
|
d->highlighter->rehighlight();
|
|
|
|
Tokens tokens = d->highlighter->formulaTokens();
|
|
uint rangeCounter = 0;
|
|
uint currentRange = 0;
|
|
uint regionStart = 0;
|
|
uint regionEnd = 0;
|
|
bool lastWasASemicolon = false;
|
|
d->currentToken = 0;
|
|
uint rangeCount = d->highlighter->rangeCount();
|
|
d->rangeCount = rangeCount;
|
|
|
|
Token token;
|
|
Token::Type type;
|
|
// search the current token
|
|
// determine the subregion number, btw
|
|
for (uint i = 0; i < tokens.count(); ++i)
|
|
{
|
|
if (tokens[i].pos() >= pos - 1) // without '='
|
|
{
|
|
/* kdDebug() << "token.pos >= cursor.pos" << endl;*/
|
|
type = tokens[i].type();
|
|
if (type == Token::Cell || type == Token::Range)
|
|
{
|
|
if (lastWasASemicolon)
|
|
{
|
|
regionEnd = rangeCounter++;
|
|
lastWasASemicolon = false;
|
|
continue;
|
|
}
|
|
}
|
|
if (type == Token::Operator && tokens[i].asOperator() == Token::Semicolon)
|
|
{
|
|
lastWasASemicolon = true;
|
|
continue;
|
|
}
|
|
lastWasASemicolon = false;
|
|
break;
|
|
}
|
|
token = tokens[i];
|
|
d->currentToken = i;
|
|
|
|
type = token.type();
|
|
if (type == Token::Cell || type == Token::Range)
|
|
{
|
|
if (!lastWasASemicolon)
|
|
{
|
|
regionStart = rangeCounter;
|
|
}
|
|
regionEnd = rangeCounter;
|
|
currentRange = rangeCounter++;
|
|
}
|
|
// semicolons are use as deliminiters in regions
|
|
if (type == Token::Operator)
|
|
{
|
|
if (token.asOperator() == Token::Semicolon)
|
|
{
|
|
lastWasASemicolon = true;
|
|
}
|
|
else
|
|
{
|
|
lastWasASemicolon = false;
|
|
// set the region start to the next element
|
|
regionStart = currentRange + 1;
|
|
regionEnd = regionStart - 1; // len = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
// kdDebug() << "regionStart = " << regionStart/* << endl*/
|
|
// << ", regionEnd = " << regionEnd/* << endl*/
|
|
// << ", currentRange = " << currentRange << endl;
|
|
|
|
d->canvas->choice()->setActiveElement(currentRange);
|
|
d->canvas->choice()->setActiveSubRegion(regionStart, regionEnd-regionStart+1);
|
|
|
|
// triggered by keyboard action?
|
|
if (!d->updatingChoice)
|
|
{
|
|
if (d->highlighter->rangeChanged())
|
|
{
|
|
d->highlighter->resetRangeChanged();
|
|
|
|
disconnect( d->canvas->choice(), TQ_SIGNAL(changed(const Region&)),
|
|
d->canvas->view(), TQ_SLOT(slotScrollChoice(const Region&)) );
|
|
d->canvas->doc()->emitBeginOperation();
|
|
setUpdateChoice(false);
|
|
|
|
Tokens tokens = d->highlighter->formulaTokens();
|
|
d->canvas->choice()->update(); // set the old one dirty
|
|
d->canvas->choice()->clear();
|
|
Region tmpRegion;
|
|
Region::ConstIterator it;
|
|
|
|
//A list of regions which have already been highlighted on the spreadsheet.
|
|
//This is so that we don't end up highlighting the same region twice in two different
|
|
//colours.
|
|
TQValueList<Region> alreadyUsedRegions;
|
|
|
|
for (uint i = 0; i < tokens.count(); ++i)
|
|
{
|
|
Token token = tokens[i];
|
|
Token::Type type = token.type();
|
|
if (type == Token::Cell || type == Token::Range)
|
|
{
|
|
Region region(d->canvas->view(), token.text());
|
|
it = region.constBegin();
|
|
|
|
if (!alreadyUsedRegions.contains(region))
|
|
{
|
|
TQRect r=(*it)->rect();
|
|
|
|
if (d->canvas->choice()->isEmpty())
|
|
d->canvas->choice()->initialize((*it)->rect(), (*it)->sheet());
|
|
else
|
|
d->canvas->choice()->extend((*it)->rect(), (*it)->sheet());
|
|
|
|
alreadyUsedRegions.append(region);
|
|
}
|
|
}
|
|
}
|
|
setUpdateChoice(true);
|
|
d->canvas->doc()->emitEndOperation(*d->canvas->choice());
|
|
connect( d->canvas->choice(), TQ_SIGNAL(changed(const Region&)),
|
|
d->canvas->view(), TQ_SLOT(slotScrollChoice(const Region&)) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CellEditor::slotTextCursorChanged(TQTextCursor* cursor)
|
|
{
|
|
TQTextStringChar *chr = cursor->paragraph()->at( cursor->index() );
|
|
int h = cursor->paragraph()->lineHeightOfChar( cursor->index() );
|
|
int x = cursor->paragraph()->rect().x() + chr->x;
|
|
int y, dummy;
|
|
cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y );
|
|
y += cursor->paragraph()->rect().y();
|
|
|
|
d->globalCursorPos = d->textEdit->mapToGlobal( d->textEdit-> contentsToViewport( TQPoint( x, y + h ) ) );
|
|
}
|
|
|
|
void CellEditor::cut()
|
|
{
|
|
d->textEdit->cut();
|
|
}
|
|
|
|
void CellEditor::paste()
|
|
{
|
|
d->textEdit->paste();
|
|
}
|
|
|
|
void CellEditor::copy()
|
|
{
|
|
d->textEdit->copy();
|
|
}
|
|
|
|
void CellEditor::setEditorFont(TQFont const & font, bool updateSize)
|
|
{
|
|
TQFont tmpFont( font );
|
|
tmpFont.setPointSizeFloat( 0.01 * canvas()->doc()->zoom() * tmpFont.pointSizeFloat() );
|
|
d->textEdit->setFont( tmpFont );
|
|
|
|
if (updateSize)
|
|
{
|
|
TQFontMetrics fm( d->textEdit->font() );
|
|
d->fontLength = fm.width('x');
|
|
|
|
int mw = fm.width( d->textEdit->text() ) + d->fontLength;
|
|
// don't make it smaller: then we would have to repaint the obscured cells
|
|
if (mw < width())
|
|
mw = width();
|
|
|
|
int mh = fm.height();
|
|
if (mh < height())
|
|
mh = height();
|
|
|
|
setGeometry(x(), y(), mw, mh);
|
|
}
|
|
}
|
|
|
|
void CellEditor::slotCompletionModeChanged(TDEGlobalSettings::Completion _completion)
|
|
{
|
|
canvas()->view()->doc()->setCompletionMode( _completion );
|
|
}
|
|
|
|
void CellEditor::slotTextChanged()
|
|
{
|
|
// kdDebug() << k_funcinfo << endl;
|
|
|
|
//FIXME - text() may return richtext?
|
|
TQString t = text();
|
|
|
|
if (t.length() > d->length)
|
|
{
|
|
d->length = t.length();
|
|
|
|
TQFontMetrics fm(d->textEdit->font());
|
|
// - requiredWidth = width of text plus some spacer characters
|
|
int requiredWidth = fm.width(t) + (2*fm.width('x'));
|
|
|
|
//For normal single-row cells, the text editor must be expanded horizontally to
|
|
//allow the text to fit if the new text is too wide
|
|
//For multi-row (word-wrap enabled) cells, the text editor must expand vertically to
|
|
//allow for new rows of text & the width of the text editor is not affected
|
|
if (d->textEdit->wordWrap() == TQTextEdit::NoWrap)
|
|
{
|
|
if (requiredWidth > width())
|
|
{
|
|
if (t.isRightToLeft())
|
|
{
|
|
setGeometry(x() - requiredWidth + width(), y(), requiredWidth,height());
|
|
}
|
|
else
|
|
{
|
|
setGeometry(x(), y(), requiredWidth,height());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int requiredHeight = d->textEdit->heightForWidth(width());
|
|
|
|
if (requiredHeight > height())
|
|
{
|
|
setGeometry(x(), y(), width(), requiredHeight);
|
|
}
|
|
}
|
|
|
|
/* // allocate more space than needed. Otherwise it might be too slow
|
|
d->length = t.length();
|
|
|
|
// Too slow for long texts
|
|
// TQFontMetrics fm( d->textEdit->font() );
|
|
// int mw = fm.width( t ) + fm.width('x');
|
|
int mw = d->fontLength * d->length;
|
|
|
|
if (mw < width())
|
|
mw = width();
|
|
|
|
if (t.isRightToLeft())
|
|
setGeometry(x() - mw + width(), y(), mw, height());
|
|
else
|
|
setGeometry(x(), y(), mw, height());
|
|
|
|
d->length -= 2; */
|
|
}
|
|
|
|
if ( (cell()->formatType()) == Percentage_format )
|
|
{
|
|
if ( (t.length() == 1) && t[0].isDigit() )
|
|
{
|
|
TQString tmp = t + " %";
|
|
d->textEdit->setText(tmp);
|
|
d->textEdit->setCursorPosition(0,1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
canvas()->view()->editWidget()->setText( t );
|
|
// canvas()->view()->editWidget()->setCursorPosition( d->textEdit->cursorPosition() );
|
|
}
|
|
|
|
void CellEditor::setCheckChoice(bool state)
|
|
{
|
|
d->checkChoice = state;
|
|
}
|
|
|
|
bool CellEditor::checkChoice()
|
|
{
|
|
if (!d->checkChoice)
|
|
return false;
|
|
|
|
// // prevent recursion
|
|
// d->checkChoice = false; // TODO nescessary?
|
|
|
|
d->length_namecell = 0;
|
|
d->currentToken = 0;
|
|
|
|
TQString text = d->textEdit->text();
|
|
if ( text[0] != '=' )
|
|
{
|
|
canvas()->setChooseMode(false);
|
|
}
|
|
else
|
|
{
|
|
int para, cur;
|
|
d->textEdit->getCursorPosition(¶, &cur);
|
|
|
|
Tokens tokens = d->highlighter->formulaTokens();
|
|
|
|
// empty formula?
|
|
if (tokens.count() < 1)
|
|
{
|
|
canvas()->startChoose();
|
|
}
|
|
else
|
|
{
|
|
Token token;
|
|
for (uint i = 0; i < tokens.count(); ++i)
|
|
{
|
|
if (tokens[i].pos() >= cur - 1) // without '='
|
|
{
|
|
break;
|
|
}
|
|
token = tokens[i];
|
|
d->currentToken = i;
|
|
}
|
|
|
|
Token::Type type = token.type();
|
|
if (type == Token::Operator && token.asOperator() != Token::RightPar)
|
|
{
|
|
canvas()->setChooseMode(true);
|
|
}
|
|
else if (type == Token::Cell || type == Token::Range)
|
|
{
|
|
d->length_namecell = token.text().length();
|
|
canvas()->setChooseMode(true);
|
|
}
|
|
else
|
|
{
|
|
canvas()->setChooseMode(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// d->checkChoice = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CellEditor::setUpdateChoice(bool state)
|
|
{
|
|
d->updateChoice = state;
|
|
}
|
|
|
|
void CellEditor::updateChoice()
|
|
{
|
|
// kdDebug() << k_funcinfo << endl;
|
|
|
|
if (!d->updateChoice)
|
|
return;
|
|
|
|
// // prevent recursion
|
|
// d->updateChoice = false; // TODO nescessary?
|
|
d->updatingChoice = true;
|
|
|
|
Selection* choice = d->canvas->choice();
|
|
|
|
if (choice->isEmpty())
|
|
return;
|
|
|
|
if (!choice->activeElement())
|
|
return;
|
|
|
|
// only one element TODO
|
|
if (++choice->constBegin() == choice->constEnd())
|
|
{
|
|
}
|
|
|
|
TQString name_cell = choice->activeSubRegionName();
|
|
|
|
Tokens tokens = d->highlighter->formulaTokens();
|
|
uint start = 1;
|
|
uint length = 0;
|
|
if (!tokens.empty())
|
|
{
|
|
Token token = tokens[d->currentToken];
|
|
Token::Type type = token.type();
|
|
if (type == Token::Cell || type == Token::Range)
|
|
{
|
|
start = token.pos() + 1; // don't forget the '='!
|
|
length = token.text().length();
|
|
}
|
|
else
|
|
{
|
|
start = token.pos() + token.text().length() + 1;
|
|
}
|
|
}
|
|
|
|
d->length_namecell = name_cell.length();
|
|
d->length_text = text().length();
|
|
//kdDebug(36001) << "updateChooseMarker2 len=" << d->length_namecell << endl;
|
|
|
|
TQString oldText = text();
|
|
TQString newText = oldText.left(start) + name_cell + oldText.right(d->length_text - start - length);
|
|
|
|
setCheckChoice( false );
|
|
setText( newText );
|
|
setCheckChoice( true );
|
|
setCursorPosition( start + d->length_namecell );
|
|
|
|
d->canvas->view()->editWidget()->setText( newText );
|
|
//kdDebug(36001) << "old=" << old << " len=" << d->length_namecell << " pos=" << pos << endl;
|
|
|
|
// d->updateChoice = false;
|
|
d->updatingChoice = false;
|
|
}
|
|
|
|
void CellEditor::resizeEvent( TQResizeEvent* )
|
|
{
|
|
d->textEdit->setGeometry( 0, 0, width(), height() );
|
|
}
|
|
|
|
void CellEditor::handleKeyPressEvent( TQKeyEvent * _ev )
|
|
{
|
|
if (_ev->key() == TQt::Key_F4)
|
|
{
|
|
if (d->textEdit == 0)
|
|
{
|
|
TQApplication::sendEvent( d->textEdit, _ev );
|
|
return;
|
|
}
|
|
|
|
TQRegExp exp("(\\$?)([a-zA-Z]+)(\\$?)([0-9]+)$");
|
|
|
|
int para,cur;
|
|
d->textEdit->getCursorPosition(¶,&cur);
|
|
// int cur = d->textEdit->cursorPosition();
|
|
TQString tmp, tmp2;
|
|
int n = -1;
|
|
|
|
// this is ugly, and sort of hack
|
|
// FIXME rewrite to use the real Tokenizer
|
|
unsigned i;
|
|
for( i = 0; i < 10; i++ )
|
|
{
|
|
tmp = d->textEdit->text().left( cur+i );
|
|
tmp2 = d->textEdit->text().right( d->textEdit->text().length() - cur - i );
|
|
|
|
n = exp.search(tmp);
|
|
if( n >= 0 ) break;
|
|
}
|
|
|
|
if (n == -1) return;
|
|
|
|
TQString newPart;
|
|
if ((exp.cap(1) == "$") && (exp.cap(3) == "$"))
|
|
newPart = "$" + exp.cap(2) + exp.cap(4);
|
|
else if ((exp.cap(1) != "$") && (exp.cap(3) != "$"))
|
|
newPart = "$" + exp.cap(2) + "$" + exp.cap(4);
|
|
else if ((exp.cap(1) == "$") && (exp.cap(3) != "$"))
|
|
newPart = exp.cap(2) + "$" + exp.cap(4);
|
|
else if ((exp.cap(1) != "$") && (exp.cap(3) == "$"))
|
|
newPart = exp.cap(2) + exp.cap(4);
|
|
|
|
TQString newString = tmp.left(n);
|
|
newString += newPart;
|
|
cur = newString.length() - i;
|
|
newString += tmp2;
|
|
|
|
d->textEdit->setText(newString);
|
|
d->textEdit->setCursorPosition( 0, cur );
|
|
|
|
_ev->accept();
|
|
|
|
return;
|
|
}
|
|
|
|
// Send the key event to the KLineEdit
|
|
TQApplication::sendEvent( d->textEdit, _ev );
|
|
}
|
|
|
|
void CellEditor::handleIMEvent( TQIMEvent * _ev )
|
|
{
|
|
// send the IM event to the KLineEdit
|
|
TQApplication::sendEvent( d->textEdit, _ev );
|
|
}
|
|
|
|
TQString CellEditor::text() const
|
|
{
|
|
return d->textEdit->text();
|
|
}
|
|
|
|
void CellEditor::setText(TQString text)
|
|
{
|
|
d->textEdit->setText(text);
|
|
//Usability : It is usually more convenient if the cursor is positioned at the end of the text so it can
|
|
//be quickly deleted using the backspace key
|
|
|
|
//This also ensures that the caret is sized correctly for the text
|
|
d->textEdit->setCursorPosition(0,text.length());
|
|
|
|
if (d->fontLength == 0)
|
|
{
|
|
TQFontMetrics fm( d->textEdit->font() );
|
|
d->fontLength = fm.width('x');
|
|
}
|
|
}
|
|
|
|
int CellEditor::cursorPosition() const
|
|
{
|
|
int para,cur;
|
|
d->textEdit->getCursorPosition(¶,&cur);
|
|
return cur;
|
|
// return d->textEdit->cursorPosition();
|
|
}
|
|
|
|
void CellEditor::setCursorPosition( int pos )
|
|
{
|
|
d->textEdit->setCursorPosition(0,pos);
|
|
canvas()->view()->editWidget()->setCursorPosition( pos );
|
|
}
|
|
|
|
bool CellEditor::eventFilter( TQObject* o, TQEvent* e )
|
|
{
|
|
// Only interested in KTextEdit
|
|
if ( o != d->textEdit )
|
|
return false;
|
|
if ( e->type() == TQEvent::FocusOut )
|
|
{
|
|
canvas()->setLastEditorWithFocus( Canvas::CellEditor );
|
|
return false;
|
|
}
|
|
|
|
if ( e->type() == TQEvent::KeyPress || e->type() == TQEvent::KeyRelease )
|
|
{
|
|
TQKeyEvent* k = (TQKeyEvent*)e;
|
|
if (!(k->state() & TQt::ShiftButton) || canvas()->chooseMode())
|
|
{
|
|
//If the user presses the return key to finish editing this cell, choose mode must be turned off first
|
|
//otherwise it will merely select a different cell
|
|
if (k->key() == Key_Return || k->key() == Key_Enter)
|
|
{
|
|
kdDebug() << "CellEditor::eventFilter: canvas()->endChoose();" << endl;
|
|
canvas()->endChoose();
|
|
}
|
|
|
|
//NB - Added check for Key_Return when migrating text edit from KLineEdit to KTextEdit, since
|
|
//normal behaviour for KTextEdit is to swallow return key presses
|
|
if ( k->key() == Key_Up || k->key() == Key_Down ||
|
|
k->key() == Key_Next || k->key() == Key_Prior ||
|
|
k->key() == Key_Escape || k->key() == Key_Tab ||
|
|
k->key() == Key_Return || k->key() == Key_Enter)
|
|
{
|
|
// Send directly to canvas
|
|
TQApplication::sendEvent( parent(), e );
|
|
return true;
|
|
}
|
|
}
|
|
else if ( k->state() & TQt::ShiftButton && ( k->key() == Key_Return || k->key() == Key_Enter ) )
|
|
{
|
|
// enable content wrapping
|
|
d->cell->format()->setMultiRow( true );
|
|
}
|
|
// End choosing. May be restarted by CellEditor::slotTextChanged
|
|
if ( e->type() == TQEvent::KeyPress && !k->text().isEmpty() )
|
|
{
|
|
canvas()->setChooseMode(false);
|
|
}
|
|
// forward Left/Right keys - so that pressing left/right in this
|
|
// editor leaves editing mode ... otherwise editing is annoying
|
|
// left/right arrows still work with the F2-editor.
|
|
|
|
// Forward left & right arrows to parent, unless this editor has been set to capture arrow key events
|
|
// Changed to this behaviour for consistancy with OO Calc & MS Office.
|
|
if ( ((k->key() == TQt::Key_Left) || (k->key() == TQt::Key_Right)) && (!d->captureAllKeyEvents)) {
|
|
TQApplication::sendEvent (parent(), e);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CellEditor::setCursorToRange(uint pos)
|
|
{
|
|
// kdDebug() << k_funcinfo << endl;
|
|
|
|
d->updatingChoice = true;
|
|
uint counter = 0;
|
|
Tokens tokens = d->highlighter->formulaTokens();
|
|
for (uint i = 0; i < tokens.count(); ++i)
|
|
{
|
|
Token token = tokens[i];
|
|
Token::Type type = token.type();
|
|
if (type == Token::Cell || type == Token::Range)
|
|
{
|
|
if (counter == pos)
|
|
{
|
|
setCursorPosition(token.pos() + token.text().length() + 1);
|
|
}
|
|
counter++;
|
|
}
|
|
}
|
|
d->updatingChoice = false;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* ComboboxLocationEditWidget
|
|
*
|
|
****************************************************************************/
|
|
|
|
ComboboxLocationEditWidget::ComboboxLocationEditWidget( TQWidget * _parent,
|
|
View * _view )
|
|
: KComboBox( _parent, "ComboboxLocationEditWidget" )
|
|
{
|
|
m_locationWidget = new LocationEditWidget( _parent, _view );
|
|
setLineEdit( m_locationWidget );
|
|
insertItem( "" );
|
|
|
|
TQValueList<Reference>::Iterator it;
|
|
TQValueList<Reference> area = _view->doc()->listArea();
|
|
for ( it = area.begin(); it != area.end(); ++it )
|
|
slotAddAreaName( (*it).ref_name);
|
|
connect( this, TQ_SIGNAL( activated ( const TQString & ) ), m_locationWidget, TQ_SLOT( slotActivateItem() ) );
|
|
}
|
|
|
|
|
|
void ComboboxLocationEditWidget::slotAddAreaName( const TQString &_name)
|
|
{
|
|
insertItem( _name );
|
|
m_locationWidget->addCompletionItem( _name );
|
|
}
|
|
|
|
void ComboboxLocationEditWidget::slotRemoveAreaName( const TQString &_name )
|
|
{
|
|
for ( int i = 0; i<count(); i++ )
|
|
{
|
|
if ( text(i)==_name )
|
|
{
|
|
removeItem( i );
|
|
break;
|
|
}
|
|
}
|
|
m_locationWidget->removeCompletionItem( _name );
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* LocationEditWidget
|
|
*
|
|
****************************************************************************/
|
|
|
|
LocationEditWidget::LocationEditWidget( TQWidget * _parent,
|
|
View * _view )
|
|
: KLineEdit( _parent, "LocationEditWidget" ),
|
|
m_pView(_view)
|
|
{
|
|
setCompletionObject( &completionList,true );
|
|
setCompletionMode(TDEGlobalSettings::CompletionAuto );
|
|
}
|
|
|
|
void LocationEditWidget::addCompletionItem( const TQString &_item )
|
|
{
|
|
kdDebug()<<" LocationEditWidget::addCompletionItem add :"<<_item<<endl;
|
|
if ( completionList.items().contains( _item) == 0 )
|
|
{
|
|
completionList.addItem( _item );
|
|
kdDebug()<<" _utem :"<<_item<<endl;
|
|
kdDebug()<<" completionList.items().count()"<<completionList.items().count()<<endl;
|
|
}
|
|
}
|
|
|
|
void LocationEditWidget::removeCompletionItem( const TQString &_item )
|
|
{
|
|
completionList.removeItem( _item );
|
|
}
|
|
|
|
void LocationEditWidget::slotActivateItem()
|
|
{
|
|
activateItem();
|
|
}
|
|
|
|
bool LocationEditWidget::activateItem()
|
|
{
|
|
TQString ltext = text();
|
|
TQString tmp = ltext.lower();
|
|
TQValueList<Reference>::Iterator it;
|
|
TQValueList<Reference> area = m_pView->doc()->listArea();
|
|
for ( it = area.begin(); it != area.end(); ++it )
|
|
{
|
|
if ((*it).ref_name == tmp)
|
|
{
|
|
TQString tmp = (*it).sheet_name;
|
|
tmp += "!";
|
|
tmp += util_rangeName((*it).rect);
|
|
m_pView->selectionInfo()->initialize( Region(m_pView,tmp) );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Set the cell component to uppercase:
|
|
// Sheet1!a1 -> Sheet1!A2
|
|
int pos = ltext.find('!');
|
|
if ( pos !=- 1 )
|
|
tmp = ltext.left(pos)+ltext.mid(pos).upper();
|
|
else
|
|
tmp = ltext.upper();
|
|
|
|
// Selection entered in location widget
|
|
if ( ltext.contains( ':' ) )
|
|
m_pView->selectionInfo()->initialize( Region(m_pView,tmp) );
|
|
// Location entered in location widget
|
|
else
|
|
{
|
|
Region region(m_pView,tmp);
|
|
bool validName = true;
|
|
for (unsigned int i = 0; i < ltext.length(); ++i)
|
|
{
|
|
if (!ltext[i].isLetter())
|
|
{
|
|
validName = false;
|
|
break;
|
|
}
|
|
}
|
|
if ( !region.isValid() && validName)
|
|
{
|
|
TQRect rect( m_pView->selectionInfo()->selection() );
|
|
Sheet * t = m_pView->activeSheet();
|
|
// set area name on current selection/cell
|
|
|
|
m_pView->doc()->addAreaName(rect, ltext.lower(), t->sheetName());
|
|
}
|
|
|
|
if (!validName)
|
|
{
|
|
m_pView->selectionInfo()->initialize(region);
|
|
}
|
|
}
|
|
|
|
// Set the focus back on the canvas.
|
|
m_pView->canvasWidget()->setFocus();
|
|
return false;
|
|
}
|
|
|
|
|
|
void LocationEditWidget::keyPressEvent( TQKeyEvent * _ev )
|
|
{
|
|
// Do not handle special keys and accelerators. This is
|
|
// done by TQLineEdit.
|
|
if ( _ev->state() & ( TQt::AltButton | TQt::ControlButton ) )
|
|
{
|
|
TQLineEdit::keyPressEvent( _ev );
|
|
// Never allow that keys are passed on to the parent.
|
|
_ev->accept();
|
|
|
|
return;
|
|
}
|
|
|
|
// Handle some special keys here. Eve
|
|
switch( _ev->key() )
|
|
{
|
|
case Key_Return:
|
|
case Key_Enter:
|
|
{
|
|
if ( activateItem() )
|
|
return;
|
|
_ev->accept();
|
|
}
|
|
break;
|
|
// Escape pressed, restore original value
|
|
case Key_Escape:
|
|
// #### Torben says: This is duplicated code. Bad.
|
|
if ( m_pView->selectionInfo()->isSingular() ) {
|
|
setText( Cell::columnName( m_pView->canvasWidget()->markerColumn() )
|
|
+ TQString::number( m_pView->canvasWidget()->markerRow() ) );
|
|
} else {
|
|
setText( Cell::columnName( m_pView->selectionInfo()->lastRange().left() )
|
|
+ TQString::number( m_pView->selectionInfo()->lastRange().top() )
|
|
+ ":"
|
|
+ Cell::columnName( m_pView->selectionInfo()->lastRange().right() )
|
|
+ TQString::number( m_pView->selectionInfo()->lastRange().bottom() ) );
|
|
}
|
|
m_pView->canvasWidget()->setFocus();
|
|
_ev->accept();
|
|
break;
|
|
default:
|
|
TQLineEdit::keyPressEvent( _ev );
|
|
// Never allow that keys are passed on to the parent.
|
|
_ev->accept();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************
|
|
*
|
|
* EditWidget
|
|
* The line-editor that appears above the sheet and allows to
|
|
* edit the cells content.
|
|
*
|
|
****************************************************************/
|
|
|
|
EditWidget::EditWidget( TQWidget *_parent, Canvas *_canvas,
|
|
TQButton *cancelButton, TQButton *okButton )
|
|
: TQLineEdit( _parent, "EditWidget" )
|
|
{
|
|
m_pCanvas = _canvas;
|
|
Q_ASSERT(m_pCanvas != NULL);
|
|
// Those buttons are created by the caller, so that they are inserted
|
|
// properly in the layout - but they are then managed here.
|
|
m_pCancelButton = cancelButton;
|
|
m_pOkButton = okButton;
|
|
isArray = false;
|
|
|
|
installEventFilter(m_pCanvas);
|
|
|
|
if ( !m_pCanvas->doc()->isReadWrite() || !m_pCanvas->activeSheet() )
|
|
setEnabled( false );
|
|
|
|
TQObject::connect( m_pCancelButton, TQ_SIGNAL( clicked() ),
|
|
this, TQ_SLOT( slotAbortEdit() ) );
|
|
TQObject::connect( m_pOkButton, TQ_SIGNAL( clicked() ),
|
|
this, TQ_SLOT( slotDoneEdit() ) );
|
|
|
|
setEditMode( false ); // disable buttons
|
|
}
|
|
|
|
void EditWidget::showEditWidget(bool _show)
|
|
{
|
|
if (_show)
|
|
{
|
|
m_pCancelButton->show();
|
|
m_pOkButton->show();
|
|
show();
|
|
}
|
|
else
|
|
{
|
|
m_pCancelButton->hide();
|
|
m_pOkButton->hide();
|
|
hide();
|
|
}
|
|
}
|
|
|
|
void EditWidget::slotAbortEdit()
|
|
{
|
|
m_pCanvas->deleteEditor( false /*discard changes*/ );
|
|
// will take care of the buttons
|
|
}
|
|
|
|
void EditWidget::slotDoneEdit()
|
|
{
|
|
m_pCanvas->deleteEditor( true /*keep changes*/, isArray);
|
|
isArray = false;
|
|
// will take care of the buttons
|
|
}
|
|
|
|
void EditWidget::keyPressEvent ( TQKeyEvent* _ev )
|
|
{
|
|
// Dont handle special keys and accelerators, except Enter ones
|
|
if (( ( _ev->state() & ( TQt::AltButton | TQt::ControlButton ) )
|
|
|| ( _ev->state() & TQt::ShiftButton )
|
|
|| ( _ev->key() == Key_Shift )
|
|
|| ( _ev->key() == Key_Control ) )
|
|
&& (_ev->key() != Key_Return) && (_ev->key() != Key_Enter))
|
|
{
|
|
TQLineEdit::keyPressEvent( _ev );
|
|
_ev->accept();
|
|
return;
|
|
}
|
|
|
|
if ( !m_pCanvas->doc()->isReadWrite() )
|
|
return;
|
|
|
|
if ( !m_pCanvas->editor() )
|
|
{
|
|
// Start editing the current cell
|
|
m_pCanvas->createEditor( Canvas::CellEditor,false );
|
|
}
|
|
CellEditor * cellEditor = (CellEditor*) m_pCanvas->editor();
|
|
|
|
switch ( _ev->key() )
|
|
{
|
|
case Key_Down:
|
|
case Key_Up:
|
|
case Key_Return:
|
|
case Key_Enter:
|
|
cellEditor->setText( text());
|
|
// Don't allow to start a chooser when pressing the arrow keys
|
|
// in this widget, since only up and down would work anyway.
|
|
// This is why we call slotDoneEdit now, instead of sending
|
|
// to the canvas.
|
|
//TQApplication::sendEvent( m_pCanvas, _ev );
|
|
isArray = (_ev->state() & TQt::AltButton) &&
|
|
(_ev->state() & TQt::ControlButton);
|
|
slotDoneEdit();
|
|
m_pCanvas->view()->updateEditWidget();
|
|
_ev->accept();
|
|
break;
|
|
case Key_F2:
|
|
cellEditor->setFocus();
|
|
cellEditor->setText( text());
|
|
cellEditor->setCursorPosition(cursorPosition());
|
|
break;
|
|
default:
|
|
|
|
TQLineEdit::keyPressEvent( _ev );
|
|
|
|
setFocus();
|
|
cellEditor->setCheckChoice( false );
|
|
cellEditor->setText( text() );
|
|
cellEditor->setCheckChoice( true );
|
|
cellEditor->setCursorPosition( cursorPosition() );
|
|
}
|
|
}
|
|
|
|
void EditWidget::setEditMode( bool mode )
|
|
{
|
|
m_pCancelButton->setEnabled(mode);
|
|
m_pOkButton->setEnabled(mode);
|
|
}
|
|
|
|
void EditWidget::focusOutEvent( TQFocusEvent* ev )
|
|
{
|
|
//kdDebug(36001) << "EditWidget lost focus" << endl;
|
|
// See comment about setLastEditorWithFocus
|
|
m_pCanvas->setLastEditorWithFocus( Canvas::EditWidget );
|
|
|
|
TQLineEdit::focusOutEvent( ev );
|
|
}
|
|
|
|
void EditWidget::setText( const TQString& t )
|
|
{
|
|
if ( t == text() ) // Why this? (David)
|
|
return;
|
|
|
|
TQLineEdit::setText( t );
|
|
}
|
|
|
|
|
|
|
|
#include "kspread_editors.moc"
|