diff options
Diffstat (limited to 'kspread/dialogs/kspread_dlg_formula.cpp')
-rw-r--r-- | kspread/dialogs/kspread_dlg_formula.cpp | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/kspread/dialogs/kspread_dlg_formula.cpp b/kspread/dialogs/kspread_dlg_formula.cpp new file mode 100644 index 00000000..8e53c525 --- /dev/null +++ b/kspread/dialogs/kspread_dlg_formula.cpp @@ -0,0 +1,763 @@ +/* This file is part of the KDE project + Copyright (C) 2002-2003 Ariya Hidayat <ariya@kde.org> + (C) 2002-2003 Norbert Andres <nandres@web.de> + (C) 1999-2003 Laurent Montel <montel@kde.org> + (C) 2002 Philipp Mueller <philipp.mueller@gmx.de> + (C) 2002 John Dailey <dailey@vt.edu> + (C) 2002 Daniel Herring <herring@eecs.ku.edu> + (C) 2000-2001 Werner Trobin <trobin@kde.org> + (C) 1998-2000 Torben Weis <weis@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 <tqtextbrowser.h> +#include <tqtabwidget.h> + +#include "kspread_dlg_formula.h" +#include "kspread_canvas.h" +#include "kspread_util.h" +#include "kspread_editors.h" +#include "kspread_doc.h" +#include "kspread_locale.h" +#include "kspread_map.h" +#include "selection.h" +#include "kspread_sheet.h" +#include "kspread_view.h" +#include "functions.h" + +#include <tdeapplication.h> +#include <kdebug.h> +#include <kbuttonbox.h> +#include <knumvalidator.h> +#include <tqcombobox.h> +#include <tqevent.h> +#include <tqlistbox.h> +#include <tqlabel.h> +#include <tqpushbutton.h> +#include <klineedit.h> +#include <tqlayout.h> + +using namespace KSpread; + +FormulaDialog::FormulaDialog( View* parent, const char* name,const TQString& formulaName) + : KDialogBase( parent, name,false,i18n("Function"), Ok|Cancel ) +{ + setWFlags( TQt::WDestructiveClose ); + + m_pView = parent; + m_focus = 0; + m_desc = 0; + + Cell* cell = m_pView->activeSheet()->cellAt( m_pView->canvasWidget()->markerColumn(), + m_pView->canvasWidget()->markerRow() ); + m_oldText=cell->text(); + // Make sure that there is a cell editor running. + if ( !m_pView->canvasWidget()->editor() ) + { + m_pView->canvasWidget()->createEditor( Canvas::CellEditor ); + if(cell->text().isEmpty()) + m_pView->canvasWidget()->editor()->setText( "=" ); + else + if(cell->text().at(0)!='=') + m_pView->canvasWidget()->editor()->setText( "="+cell->text() ); + else + m_pView->canvasWidget()->editor()->setText( cell->text() ); + } + + Q_ASSERT( m_pView->canvasWidget()->editor() ); + + TQWidget *page = new TQWidget( this ); + setMainWidget(page); + + TQGridLayout *grid1 = new TQGridLayout(page,11,2,KDialog::marginHint(), KDialog::spacingHint()); + + searchFunct = new KLineEdit(page); + TQSizePolicy sp3( TQSizePolicy::Preferred, TQSizePolicy::Fixed ); + searchFunct->setSizePolicy( sp3 ); + + grid1->addWidget( searchFunct, 0, 0 ); + + typeFunction = new TQComboBox(page); + TQStringList cats = FunctionRepository::self()->groups(); + cats.prepend( i18n("All") ); + typeFunction->insertStringList( cats ); + grid1->addWidget( typeFunction, 1, 0 ); + + functions = new TQListBox(page); + TQSizePolicy sp1( TQSizePolicy::Preferred, TQSizePolicy::Expanding ); + functions->setSizePolicy( sp1 ); + grid1->addWidget( functions, 2, 0 ); + + selectFunction = new TQPushButton( page ); + TQToolTip::add(selectFunction, i18n("Insert function") ); + selectFunction->setPixmap( BarIcon( "go-down", TDEIcon::SizeSmall ) ); + grid1->addWidget( selectFunction, 3, 0 ); + + result = new TQLineEdit( page ); + grid1->addMultiCellWidget( result, 4, 4, 0, 1 ); + + m_tabwidget = new TQTabWidget( page ); + TQSizePolicy sp2( TQSizePolicy::Expanding, TQSizePolicy::Expanding ); + m_tabwidget->setSizePolicy( sp2 ); + grid1->addMultiCellWidget( m_tabwidget, 0, 2, 1, 1 ); + + m_browser = new TQTextBrowser( m_tabwidget ); + m_browser->setMinimumWidth( 300 ); + + m_tabwidget->addTab( m_browser, i18n("&Help") ); + int index = m_tabwidget->currentPageIndex(); + + m_input = new TQWidget( m_tabwidget ); + TQVBoxLayout *grid2 = new TQVBoxLayout( m_input, KDialog::marginHint(), KDialog::spacingHint() ); + + // grid2->setResizeMode (TQLayout::Minimum); + + label1 = new TQLabel(m_input); + grid2->addWidget( label1 ); + + firstElement=new TQLineEdit(m_input); + grid2->addWidget( firstElement ); + + label2=new TQLabel(m_input); + grid2->addWidget( label2 ); + + secondElement=new TQLineEdit(m_input); + grid2->addWidget( secondElement ); + + label3=new TQLabel(m_input); + grid2->addWidget( label3 ); + + thirdElement=new TQLineEdit(m_input); + grid2->addWidget( thirdElement ); + + label4=new TQLabel(m_input); + grid2->addWidget( label4 ); + + fourElement=new TQLineEdit(m_input); + grid2->addWidget( fourElement ); + + label5=new TQLabel(m_input); + grid2->addWidget( label5 ); + + fiveElement=new TQLineEdit(m_input); + grid2->addWidget( fiveElement ); + + grid2->addStretch( 10 ); + + m_tabwidget->addTab( m_input, i18n("&Parameters") ); + m_tabwidget->setTabEnabled( m_input, FALSE ); + + m_tabwidget->setCurrentPage( index ); + + refresh_result = true; + + connect( this, TQT_SIGNAL( cancelClicked() ), this, TQT_SLOT( slotClose() ) ); + connect( this, TQT_SIGNAL( okClicked() ), this, TQT_SLOT( slotOk() ) ); + connect( typeFunction, TQT_SIGNAL( activated(const TQString &) ), + this, TQT_SLOT( slotActivated(const TQString &) ) ); + connect( functions, TQT_SIGNAL( highlighted(const TQString &) ), + this, TQT_SLOT( slotSelected(const TQString &) ) ); + connect( functions, TQT_SIGNAL( selected(const TQString &) ), + this, TQT_SLOT( slotSelected(const TQString &) ) ); + connect( functions, TQT_SIGNAL( doubleClicked(TQListBoxItem * ) ), + this ,TQT_SLOT( slotDoubleClicked(TQListBoxItem *) ) ); + + slotActivated(i18n("All")); + + connect( selectFunction, TQT_SIGNAL(clicked()), + this,TQT_SLOT(slotSelectButton())); + + connect( firstElement,TQT_SIGNAL(textChanged ( const TQString & )), + this,TQT_SLOT(slotChangeText(const TQString &))); + connect( secondElement,TQT_SIGNAL(textChanged ( const TQString & )), + this,TQT_SLOT(slotChangeText(const TQString &))); + connect( thirdElement,TQT_SIGNAL(textChanged ( const TQString & )), + this,TQT_SLOT(slotChangeText(const TQString &))); + connect( fourElement,TQT_SIGNAL(textChanged ( const TQString & )), + this,TQT_SLOT(slotChangeText(const TQString &))); + connect( fiveElement,TQT_SIGNAL(textChanged ( const TQString & )), + this,TQT_SLOT(slotChangeText(const TQString &))); + + connect( m_pView->choice(), TQT_SIGNAL(changed(const Region&)), + this, TQT_SLOT(slotSelectionChanged())); + + connect( m_browser, TQT_SIGNAL( linkClicked( const TQString& ) ), + this, TQT_SLOT( slotShowFunction( const TQString& ) ) ); + + // Save the name of the active sheet. + m_sheetName = m_pView->activeSheet()->sheetName(); + // Save the cells current text. + TQString tmp_oldText = m_pView->canvasWidget()->editor()->text(); + // Position of the cell. + m_column = m_pView->canvasWidget()->markerColumn(); + m_row = m_pView->canvasWidget()->markerRow(); + + if( tmp_oldText.isEmpty() ) + result->setText("="); + else + { + if( tmp_oldText.at(0)!='=') + result->setText( "=" + tmp_oldText ); + else + result->setText( tmp_oldText ); + } + + // Allow the user to select cells on the spreadsheet. + m_pView->canvasWidget()->startChoose(); + + tqApp->installEventFilter( this ); + + // Was a function name passed along with the constructor ? Then activate it. + if( !formulaName.isEmpty() ) + { + functions->setCurrentItem( functions->index( functions->findItem( formulaName ) ) ); + slotDoubleClicked( functions->findItem( formulaName ) ); + } + else + { + // Set keyboard focus to allow selection of a formula. + searchFunct->setFocus(); + } + + // Add auto completion. + searchFunct->setCompletionMode( TDEGlobalSettings::CompletionAuto ); + searchFunct->setCompletionObject( &listFunct, true ); + + if( functions->currentItem() == -1 ) + selectFunction->setEnabled( false ); + + connect( searchFunct, TQT_SIGNAL( textChanged( const TQString & ) ), + this, TQT_SLOT( slotSearchText(const TQString &) ) ); + connect( searchFunct, TQT_SIGNAL( returnPressed() ), + this, TQT_SLOT( slotPressReturn() ) ); +} + +FormulaDialog::~FormulaDialog() +{ + kdDebug(36001)<<"FormulaDialog::~FormulaDialog() \n"; +} + +void FormulaDialog::slotPressReturn() +{ + //laurent 2001-07-07 desactivate this code + //because kspread crash. + //TODO fix it + /* + if( !functions->currentText().isEmpty() ) + slotDoubleClicked( functions->findItem( functions->currentText() ) ); + */ +} + +void FormulaDialog::slotSearchText(const TQString &_text) +{ + TQString result = listFunct.makeCompletion( _text.upper() ); + if( !result.isNull() ) + functions->setCurrentItem( functions->index( functions->findItem( result ) ) ); +} + +bool FormulaDialog::eventFilter( TQObject* obj, TQEvent* ev ) +{ + if ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(firstElement) && ev->type() == TQEvent::FocusIn ) + m_focus = firstElement; + else if ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(secondElement) && ev->type() == TQEvent::FocusIn ) + m_focus = secondElement; + else if ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(thirdElement) && ev->type() == TQEvent::FocusIn ) + m_focus = thirdElement; + else if ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(fourElement) && ev->type() == TQEvent::FocusIn ) + m_focus = fourElement; + else if ( TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(fiveElement) && ev->type() == TQEvent::FocusIn ) + m_focus = fiveElement; + else + return FALSE; + + if ( m_focus ) + m_pView->canvasWidget()->startChoose(); + + return FALSE; +} + +void FormulaDialog::slotOk() +{ + m_pView->doc()->emitBeginOperation( false ); + + m_pView->canvasWidget()->endChoose(); + // Switch back to the old sheet + if( m_pView->activeSheet()->sheetName() != m_sheetName ) + { + Sheet *sheet=m_pView->doc()->map()->findSheet(m_sheetName); + if( sheet) + m_pView->setActiveSheet(sheet); + } + + // Revert the marker to its original position + m_pView->selectionInfo()->initialize( TQPoint( m_column, m_row ) ); + + // If there is still an editor then set the text. + // Usually the editor is always in place. + if( m_pView->canvasWidget()->editor() != 0 ) + { + Q_ASSERT( m_pView->canvasWidget()->editor() ); + TQString tmp = result->text(); + if( tmp.at(0) != '=') + tmp = "=" + tmp; + int pos = m_pView->canvasWidget()->editor()->cursorPosition()+ tmp.length(); + m_pView->canvasWidget()->editor()->setText( tmp ); + m_pView->canvasWidget()->editor()->setFocus(); + m_pView->canvasWidget()->editor()->setCursorPosition( pos ); + } + + m_pView->slotUpdateView( m_pView->activeSheet() ); + accept(); + // delete this; +} + +void FormulaDialog::slotClose() +{ + m_pView->doc()->emitBeginOperation( false ); + + m_pView->canvasWidget()->endChoose(); + + // Switch back to the old sheet + if(m_pView->activeSheet()->sheetName() != m_sheetName ) + { + Sheet *sheet=m_pView->doc()->map()->findSheet(m_sheetName); + if( !sheet ) + return; + m_pView->setActiveSheet(sheet); + } + + + // Revert the marker to its original position + m_pView->selectionInfo()->initialize( TQPoint( m_column, m_row ) ); + + // If there is still an editor then reset the text. + // Usually the editor is always in place. + if( m_pView->canvasWidget()->editor() != 0 ) + { + Q_ASSERT( m_pView->canvasWidget()->editor() ); + m_pView->canvasWidget()->editor()->setText( m_oldText ); + m_pView->canvasWidget()->editor()->setFocus(); + } + + m_pView->slotUpdateView( m_pView->activeSheet() ); + reject(); + //laurent 2002-01-03 comment this line otherwise kspread crash + //but dialog box is not deleted => not good + //delete this; +} + +void FormulaDialog::slotSelectButton() +{ + if( functions->currentItem() != -1 ) + { + slotDoubleClicked(functions->findItem(functions->text(functions->currentItem()))); + } +} + +void FormulaDialog::slotChangeText( const TQString& ) +{ + // Test the lock + if( !refresh_result ) + return; + + if ( m_focus == 0 ) + return; + + TQString tmp = m_leftText+m_funcName+"("; + tmp += createFormula(); + tmp = tmp+ ")" + m_rightText; + + result->setText( tmp ); +} + +TQString FormulaDialog::createFormula() +{ + TQString tmp( "" ); + + if ( !m_desc ) + return TQString(); + + bool first = TRUE; + + int count = m_desc->params(); + + if(!firstElement->text().isEmpty() && count >= 1 ) + { + tmp=tmp+createParameter(firstElement->text(), 0 ); + first = FALSE; + } + + if(!secondElement->text().isEmpty() && count >= 2 ) + { + first = FALSE; + if ( !first ) + tmp=tmp+";"+createParameter(secondElement->text(), 1 ); + else + tmp=tmp+createParameter(secondElement->text(), 1 ); + } + if(!thirdElement->text().isEmpty() && count >= 3 ) + { + first = FALSE; + if ( !first ) + tmp=tmp+";"+createParameter(thirdElement->text(), 2 ); + else + tmp=tmp+createParameter(thirdElement->text(), 2 ); + } + if(!fourElement->text().isEmpty() && count >= 4 ) + { + first = FALSE; + if ( !first ) + tmp=tmp+";"+createParameter(fourElement->text(), 3 ); + else + tmp=tmp+createParameter(fourElement->text(), 3 ); + } + if(!fiveElement->text().isEmpty() && count >= 5 ) + { + first = FALSE; + if ( !first ) + tmp=tmp+";"+createParameter(fiveElement->text(), 4 ); + else + tmp=tmp+createParameter(fiveElement->text(), 4 ); + } + + return(tmp); +} + +TQString FormulaDialog::createParameter( const TQString& _text, int param ) +{ + if ( _text.isEmpty() ) + return TQString( "" ); + + if ( !m_desc ) + return TQString( "" ); + + TQString text; + + ParameterType elementType = m_desc->param( param ).type(); + + switch( elementType ) + { + case KSpread_Any: + { + bool isNumber; + double tmp = m_pView->doc()->locale()->readNumber( _text, &isNumber ); + Q_UNUSED( tmp ); + + //In case of number or boolean return _text, else return value as KSpread_String + if ( isNumber || _text.upper() =="FALSE" || _text.upper() == "TRUE" ) + return _text; + } + // fall through + case KSpread_String: + { + // Does the text start with quotes? + if ( _text[0] == '"' ) + { + text = "\\"; // changed: was "\"" + + // Escape quotes + TQString tmp = _text; + int pos; + int start = 1; + while( ( pos = tmp.find( '"', start ) ) != -1 ) + { + if (tmp[pos - 1] != '\\') + tmp.replace( pos, 1, "\\\"" ); + else + start = pos + 1; + } + + text += tmp; + text += "\""; + } + else + { + Point p = Point( _text, m_pView->doc()->map() ); + Range r = Range( _text, m_pView->doc()->map() ); + + if( !p.isValid() && !r.isValid() ) + { + text = "\""; + + // Escape quotes + TQString tmp = _text; + int pos; + int start = 1; + while( ( pos = tmp.find( '"', start ) ) != -1 ) + { + if (tmp[pos - 1] != '\\') + tmp.replace( pos, 1, "\\\"" ); + else + start = pos + 1; + } + + text += tmp; + text += "\""; + } + else + text = _text; + } + } + return text; + case KSpread_Float: + return _text; + case KSpread_Boolean: + return _text; + case KSpread_Int: + return _text; + } + + // Never reached + return text; +} + +static void showEntry( TQLineEdit* edit, TQLabel* label, + FunctionDescription* desc, int param ) +{ + edit->show(); + label->setText( desc->param( param ).helpText()+":" ); + label->show(); + ParameterType elementType = desc->param( param ).type(); + KFloatValidator *validate=0L; + switch( elementType ) + { + case KSpread_String: + case KSpread_Boolean: + case KSpread_Any: + edit->clearValidator (); + break; + case KSpread_Float: + validate=new KFloatValidator (edit); + validate->setAcceptLocalizedNumbers(true); + edit->setValidator(validate); + edit->setText( "0" ); + break; + case KSpread_Int: + edit->setValidator(new TQIntValidator (TQT_TQOBJECT(edit))); + edit->setText( "0" ); + break; + } + +} + +void FormulaDialog::slotDoubleClicked( TQListBoxItem* item ) +{ + if ( !item ) + return; + refresh_result = false; + if ( !m_desc ) + { + m_browser->setText( "" ); + return; + } + + m_focus = 0; + int old_length = result->text().length(); + + // Dont change order of these function calls due to a bug in TQt 2.2 + m_browser->setText( m_desc->toTQML() ); + m_tabwidget->setTabEnabled( m_input, TRUE ); + m_tabwidget->setCurrentPage( 1 ); + + // + // Show as many TQLineEdits as needed. + // + if( m_desc->params() > 0 ) + { + m_focus = firstElement; + firstElement->setFocus(); + + showEntry( firstElement, label1, m_desc, 0 ); + } + else + { + label1->hide(); + firstElement->hide(); + } + + if( m_desc->params() > 1 ) + { + showEntry( secondElement, label2, m_desc, 1 ); + } + else + { + label2->hide(); + secondElement->hide(); + } + + if( m_desc->params() > 2 ) + { + showEntry( thirdElement, label3, m_desc, 2 ); + } + else + { + label3->hide(); + thirdElement->hide(); + } + + if( m_desc->params() > 3 ) + { + showEntry( fourElement, label4, m_desc, 3 ); + } + else + { + label4->hide(); + fourElement->hide(); + } + + if( m_desc->params() > 4 ) + { + showEntry( fiveElement, label5, m_desc, 4 ); + } + else + { + label5->hide(); + fiveElement->hide(); + } + + if( m_desc->params() > 5 ) + { + kdDebug(36001) << "Error in param->nb_param" << endl; + } + refresh_result= true; + // + // Put the new function call in the result. + // + if( result->cursorPosition() < old_length ) + { + m_rightText=result->text().right(old_length-result->cursorPosition()); + m_leftText=result->text().left(result->cursorPosition()); + } + else + { + m_rightText=""; + m_leftText=result->text(); + } + int pos = result->cursorPosition(); + result->setText( m_leftText+functions->text( functions->currentItem() ) + "()" + m_rightText); + + if (result->text()[0] != '=') + result->setText("=" + result->text()); + + // + // Put focus somewhere is there are no TQLineEdits visible + // + if( m_desc->params() == 0 ) + { + label1->show(); + label1->setText( i18n("This function has no parameters.") ); + + result->setFocus(); + result->setCursorPosition(pos+functions->text(functions->currentItem()).length()+2); + } + slotChangeText( "" ); +} + +void FormulaDialog::slotSelected( const TQString& function ) +{ + FunctionDescription* desc = + FunctionRepository::self()->functionInfo (function); + if ( !desc ) + { + m_browser->setText (i18n ("Description is not available.")); + return; + } + + if( functions->currentItem() !=- 1 ) + selectFunction->setEnabled( TRUE ); + + // Lock + refresh_result = false; + + m_funcName = function; + m_desc = desc; + + // Set the help text + m_browser->setText( m_desc->toTQML() ); + m_browser->setContentsPos( 0, 0 ); + + m_focus=0; + + m_tabwidget->setCurrentPage( 0 ); + m_tabwidget->setTabEnabled( m_input, FALSE ); + + // Unlock + refresh_result=true; +} + +// from hyperlink in the "Related Function" +void FormulaDialog::slotShowFunction( const TQString& function ) +{ + FunctionDescription* desc = + FunctionRepository::self()->functionInfo( function ); + if ( !desc ) return; + + // select the category + TQString category = desc->group(); + typeFunction->setCurrentText( category ); + slotActivated( category ); + + // select the function + TQListBoxItem* item = functions->findItem( function, + TQt::ExactMatch | TQt::CaseSensitive ); + if( item ) functions->setCurrentItem( item ); + + slotSelected( function ); +} + +void FormulaDialog::slotSelectionChanged() +{ + if ( !m_focus ) + return; + + if (m_pView->choice()->isValid()) + { + TQString area = m_pView->choice()->name(); + m_focus->setText( area ); + } +} + +void FormulaDialog::slotActivated( const TQString& category ) +{ + TQStringList lst; + if ( category == i18n("All") ) + lst = FunctionRepository::self()->functionNames(); + else + lst = FunctionRepository::self()->functionNames( category ); + + kdDebug(36001)<<"category: "<<category<<" ("<<lst.count()<<"functions)" << endl; + + functions->clear(); + functions->insertStringList( lst ); + + TQStringList upperList; + for ( TQStringList::Iterator it = lst.begin(); it != lst.end();++it ) + upperList.append((*it).upper()); + + listFunct.setItems( upperList ); + + // Go to the first function in the list. + functions->setCurrentItem(0); + slotSelected( functions->text(0) ); +} + +void FormulaDialog::closeEvent ( TQCloseEvent * e ) +{ + e->accept(); +} + +#include "kspread_dlg_formula.moc" |