summaryrefslogtreecommitdiffstats
path: root/src/widgets
diff options
context:
space:
mode:
authorMichele Calgaro <michele.calgaro@yahoo.it>2024-10-13 11:56:14 +0900
committerMichele Calgaro <michele.calgaro@yahoo.it>2024-10-21 09:29:11 +0900
commit0c8ed6c9a4000af8f48581a81c4b5c2f5b9fd502 (patch)
tree10f9d3223f0a0904a0748a28ca44da52ee1092b7 /src/widgets
parent7d5ba3180a82a0827c1fbd6dc93a2abf4f882c37 (diff)
downloadkrecipes-0c8ed6c9a4000af8f48581a81c4b5c2f5b9fd502.tar.gz
krecipes-0c8ed6c9a4000af8f48581a81c4b5c2f5b9fd502.zip
Rearrange folders structure to remove unnecessary 'krecipes' second level subfolder
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'src/widgets')
-rw-r--r--src/widgets/Makefile.am28
-rw-r--r--src/widgets/amountunitinput.cpp68
-rw-r--r--src/widgets/amountunitinput.h59
-rw-r--r--src/widgets/authorlistview.cpp275
-rw-r--r--src/widgets/authorlistview.h107
-rw-r--r--src/widgets/categorycombobox.cpp182
-rw-r--r--src/widgets/categorycombobox.h59
-rw-r--r--src/widgets/categorylistview.cpp637
-rw-r--r--src/widgets/categorylistview.h288
-rw-r--r--src/widgets/conversiontable.cpp426
-rw-r--r--src/widgets/conversiontable.h101
-rw-r--r--src/widgets/criteriacombobox.cpp49
-rw-r--r--src/widgets/criteriacombobox.h41
-rw-r--r--src/widgets/dblistviewbase.cpp334
-rw-r--r--src/widgets/dblistviewbase.h89
-rw-r--r--src/widgets/fractioninput.cpp121
-rw-r--r--src/widgets/fractioninput.h62
-rw-r--r--src/widgets/headercombobox.cpp42
-rw-r--r--src/widgets/headercombobox.h34
-rw-r--r--src/widgets/headerlistview.cpp196
-rw-r--r--src/widgets/headerlistview.h70
-rw-r--r--src/widgets/inglistviewitem.cpp225
-rw-r--r--src/widgets/inglistviewitem.h82
-rw-r--r--src/widgets/ingredientcombobox.cpp188
-rw-r--r--src/widgets/ingredientcombobox.h58
-rw-r--r--src/widgets/ingredientinputwidget.cpp542
-rw-r--r--src/widgets/ingredientinputwidget.h134
-rw-r--r--src/widgets/ingredientlistview.cpp287
-rw-r--r--src/widgets/ingredientlistview.h119
-rw-r--r--src/widgets/kdateedit.cpp388
-rw-r--r--src/widgets/kdateedit.h139
-rw-r--r--src/widgets/kdatepickerpopup.cpp123
-rw-r--r--src/widgets/kdatepickerpopup.h102
-rw-r--r--src/widgets/krelistview.cpp116
-rw-r--r--src/widgets/krelistview.h65
-rw-r--r--src/widgets/kremenu.cpp631
-rw-r--r--src/widgets/kremenu.h170
-rw-r--r--src/widgets/kreruler.cpp1049
-rw-r--r--src/widgets/kreruler.h366
-rw-r--r--src/widgets/kretextedit.cpp183
-rw-r--r--src/widgets/kretextedit.h52
-rw-r--r--src/widgets/kwidgetlistbox.cpp210
-rw-r--r--src/widgets/kwidgetlistbox.h71
-rw-r--r--src/widgets/paneldeco.cpp173
-rw-r--r--src/widgets/paneldeco.h85
-rw-r--r--src/widgets/prepmethodcombobox.cpp186
-rw-r--r--src/widgets/prepmethodcombobox.h48
-rw-r--r--src/widgets/prepmethodlistview.cpp189
-rw-r--r--src/widgets/prepmethodlistview.h70
-rw-r--r--src/widgets/propertylistview.cpp289
-rw-r--r--src/widgets/propertylistview.h203
-rw-r--r--src/widgets/ratingdisplaywidget.ui232
-rw-r--r--src/widgets/ratingwidget.cpp164
-rw-r--r--src/widgets/ratingwidget.h63
-rw-r--r--src/widgets/recipelistview.cpp447
-rw-r--r--src/widgets/recipelistview.h166
-rw-r--r--src/widgets/unitcombobox.cpp145
-rw-r--r--src/widgets/unitcombobox.h52
-rw-r--r--src/widgets/unitlistview.cpp369
-rw-r--r--src/widgets/unitlistview.h76
-rw-r--r--src/widgets/weightinput.cpp63
-rw-r--r--src/widgets/weightinput.h42
62 files changed, 11630 insertions, 0 deletions
diff --git a/src/widgets/Makefile.am b/src/widgets/Makefile.am
new file mode 100644
index 0000000..b92e241
--- /dev/null
+++ b/src/widgets/Makefile.am
@@ -0,0 +1,28 @@
+## Makefile.am for krecipes
+
+# this is the program that gets installed. it's name is used for all
+# of the other Makefile.am variables
+
+# set the include path for X, tqt and TDE
+INCLUDES = -I$(srcdir)/.. $(all_includes)
+
+noinst_LTLIBRARIES=libkrecipeswidgets.la
+
+libkrecipeswidgets_la_SOURCES= \
+ krelistview.cpp kremenu.cpp \
+ paneldeco.cpp ingredientlistview.cpp unitlistview.cpp \
+ propertylistview.cpp prepmethodlistview.cpp categorylistview.cpp \
+ authorlistview.cpp recipelistview.cpp categorycombobox.cpp \
+ kretextedit.cpp dblistviewbase.cpp \
+ conversiontable.cpp fractioninput.cpp ingredientcombobox.cpp \
+ headercombobox.cpp prepmethodcombobox.cpp \
+ inglistviewitem.cpp kdateedit.cpp kdatepickerpopup.cpp \
+ headerlistview.cpp ratingwidget.cpp kwidgetlistbox.cpp \
+ ratingdisplaywidget.ui criteriacombobox.cpp ingredientinputwidget.cpp \
+ unitcombobox.cpp amountunitinput.cpp weightinput.cpp
+
+
+libkrecipeswidgets_la_METASOURCES=AUTO
+
+#the library search path.
+libkrecipeswidgets_la_LDFLAGS = $(KDE_RPATH) $(all_libraries)
diff --git a/src/widgets/amountunitinput.cpp b/src/widgets/amountunitinput.cpp
new file mode 100644
index 0000000..699f76d
--- /dev/null
+++ b/src/widgets/amountunitinput.cpp
@@ -0,0 +1,68 @@
+/***************************************************************************
+* Copyright (C) 2006 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "amountunitinput.h"
+
+#include <tqheader.h>
+#include <tqlistview.h>
+
+#include "fractioninput.h"
+#include "unitcombobox.h"
+#include "backends/recipedb.h"
+#include "datablocks/mixednumber.h"
+
+AmountUnitInput::AmountUnitInput( TQWidget *parent, RecipeDB *database, Unit::Type type, MixedNumber::Format format ) : TQHBox(parent),
+ m_item(0), m_database(database)
+{
+ amountInput = new FractionInput(this,format);
+ unitBox = new UnitComboBox(this,database,type);
+ unitBox->reload();
+
+ connect( amountInput, TQ_SIGNAL(valueChanged(const MixedNumber &)), TQ_SLOT(emitValueChanged()) );
+ connect( unitBox, TQ_SIGNAL(activated(int)), TQ_SLOT(emitValueChanged()) );
+ connect( amountInput, TQ_SIGNAL(returnPressed()), TQ_SIGNAL(doneEditing()) );
+}
+
+void AmountUnitInput::emitValueChanged()
+{
+ emit valueChanged( amount(), unit() );
+}
+
+void AmountUnitInput::setAmount( const MixedNumber &amount )
+{
+ amountInput->disconnect( this );
+ if ( amount.toDouble() < 0 )
+ amountInput->clear();
+ else
+ amountInput->setValue( amount, 0 );
+ connect( amountInput, TQ_SIGNAL(valueChanged(const MixedNumber &)), TQ_SLOT(emitValueChanged()) );
+}
+
+void AmountUnitInput::setUnit( const Unit &unit )
+{
+ if ( unit.id == -1 )
+ unitBox->setCurrentItem(0);
+ else
+ unitBox->setSelected( unit.id );
+
+}
+
+MixedNumber AmountUnitInput::amount() const
+{
+ return amountInput->value();
+}
+
+Unit AmountUnitInput::unit() const
+{
+ //TODO Potential for optimization here... avoid the database call
+ return m_database->unitName( unitBox->id( unitBox->currentItem() ) );
+}
+
+#include "amountunitinput.moc"
diff --git a/src/widgets/amountunitinput.h b/src/widgets/amountunitinput.h
new file mode 100644
index 0000000..911e67c
--- /dev/null
+++ b/src/widgets/amountunitinput.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+* Copyright (C) 2003 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* Copyright (C) 2006 Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef AMOUNTUNITINPUT_H
+#define AMOUNTUNITINPUT_H
+
+#include <tqhbox.h>
+
+#include "datablocks/unit.h"
+#include "datablocks/mixednumber.h"
+
+class TQListViewItem;
+
+class RecipeDB;
+class FractionInput;
+class UnitComboBox;
+
+class AmountUnitInput : public TQHBox
+{
+TQ_OBJECT
+
+public:
+ AmountUnitInput( TQWidget *parent, RecipeDB *database, Unit::Type type = Unit::All, MixedNumber::Format f = MixedNumber::MixedNumberFormat );
+
+ void setAmount( const MixedNumber &amount );
+ void setUnit( const Unit &unit );
+
+ MixedNumber amount() const;
+ Unit unit() const;
+ TQListViewItem *item() const { return m_item; }
+ void setItem( TQListViewItem *it ){ m_item = it; }
+
+ void insertIntoListview( TQListViewItem *it, int col );
+
+public slots:
+ void emitValueChanged();
+
+signals:
+ void valueChanged( const MixedNumber &, const Unit &unit );
+ void doneEditing();
+
+private:
+ FractionInput *amountInput;
+ UnitComboBox *unitBox;
+
+ TQListViewItem *m_item;
+
+ RecipeDB *m_database;
+};
+#endif
diff --git a/src/widgets/authorlistview.cpp b/src/widgets/authorlistview.cpp
new file mode 100644
index 0000000..7ef696d
--- /dev/null
+++ b/src/widgets/authorlistview.cpp
@@ -0,0 +1,275 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "authorlistview.h"
+
+#include <tdemessagebox.h>
+#include <tdeconfig.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kiconloader.h>
+#include <tdepopupmenu.h>
+
+#include "backends/recipedb.h"
+#include "dialogs/createelementdialog.h"
+#include "dialogs/dependanciesdialog.h"
+
+AuthorListView::AuthorListView( TQWidget *parent, RecipeDB *db ) : DBListViewBase( parent, db, db->authorCount() )
+{
+ setAllColumnsShowFocus( true );
+ setDefaultRenameAction( TQListView::Reject );
+}
+
+void AuthorListView::init()
+{
+ connect( database, TQ_SIGNAL( authorCreated( const Element & ) ), TQ_SLOT( checkCreateAuthor( const Element & ) ) );
+ connect( database, TQ_SIGNAL( authorRemoved( int ) ), TQ_SLOT( removeAuthor( int ) ) );
+}
+
+void AuthorListView::load( int limit, int offset )
+{
+ ElementList authorList;
+ database->loadAuthors( &authorList, limit, offset );
+
+ setTotalItems(authorList.count());
+
+ for ( ElementList::const_iterator ing_it = authorList.begin(); ing_it != authorList.end(); ++ing_it )
+ createAuthor( *ing_it );
+}
+
+void AuthorListView::checkCreateAuthor( const Element &el )
+{
+ if ( handleElement(el.name) ) { //only create this author if the base class okays it
+ createAuthor(el);
+ }
+}
+
+
+StdAuthorListView::StdAuthorListView( TQWidget *parent, RecipeDB *db, bool editable ) : AuthorListView( parent, db )
+{
+ addColumn( i18n( "Author" ) );
+
+ TDEConfig * config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ if ( editable ) {
+ setRenameable( 0, true );
+
+ TDEIconLoader *il = new TDEIconLoader;
+
+ kpop = new TDEPopupMenu( this );
+ kpop->insertItem( il->loadIcon( "document-new", TDEIcon::NoGroup, 16 ), i18n( "&Create" ), this, TQ_SLOT( createNew() ), CTRL + Key_N );
+ kpop->insertItem( il->loadIcon( "edit-delete", TDEIcon::NoGroup, 16 ), i18n( "&Delete" ), this, TQ_SLOT( remove
+ () ), Key_Delete );
+ kpop->insertItem( il->loadIcon( "edit", TDEIcon::NoGroup, 16 ), i18n( "&Rename" ), this, TQ_SLOT( rename() ), CTRL + Key_R );
+ kpop->polish();
+
+ delete il;
+
+ connect( this, TQ_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ), TQ_SLOT( showPopup( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
+ connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem* ) ), this, TQ_SLOT( modAuthor( TQListViewItem* ) ) );
+ connect( this, TQ_SIGNAL( itemRenamed( TQListViewItem* ) ), this, TQ_SLOT( saveAuthor( TQListViewItem* ) ) );
+ }
+}
+
+void StdAuthorListView::showPopup( TDEListView * /*l*/, TQListViewItem *i, const TQPoint &p )
+{
+ if ( i )
+ kpop->exec( p );
+}
+
+void StdAuthorListView::createNew()
+{
+ CreateElementDialog * elementDialog = new CreateElementDialog( this, i18n( "New Author" ) );
+
+ if ( elementDialog->exec() == TQDialog::Accepted ) {
+ TQString result = elementDialog->newElementName();
+
+ //check bounds first
+ if ( checkBounds( result ) )
+ database->createNewAuthor( result ); // Create the new author in the database
+ }
+}
+
+void StdAuthorListView::remove
+ ()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item ) {
+ int id = item->text( 1 ).toInt();
+
+ ElementList recipeDependancies;
+ database->findUseOfAuthorInRecipes( &recipeDependancies, id );
+
+ if ( recipeDependancies.isEmpty() ) {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "Are you sure you want to delete this author?" ) ) ) {
+ case KMessageBox::Continue:
+ database->removeAuthor( id );
+ break;
+ }
+ return;
+ }
+ else { // need warning!
+ ListInfo info;
+ info.list = recipeDependancies;
+ info.name = i18n("Recipes");
+
+ DependanciesDialog warnDialog( this, info, false );
+ if ( warnDialog.exec() == TQDialog::Accepted )
+ database->removeAuthor( id );
+ }
+ }
+}
+
+void StdAuthorListView::rename()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item )
+ AuthorListView::rename( item, 0 );
+}
+
+void StdAuthorListView::createAuthor( const Element &author )
+{
+ createElement(new TQListViewItem( this, author.name, TQString::number( author.id ) ));
+}
+
+void StdAuthorListView::removeAuthor( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 1 );
+ removeElement(item);
+}
+
+void StdAuthorListView::modAuthor( TQListViewItem* i )
+{
+ if ( i )
+ AuthorListView::rename( i, 0 );
+}
+
+void StdAuthorListView::saveAuthor( TQListViewItem* i )
+{
+ if ( !checkBounds( i->text( 0 ) ) ) {
+ reload(ForceReload); //reset the changed text
+ return ;
+ }
+
+ int existing_id = database->findExistingAuthorByName( i->text( 0 ) );
+ int author_id = i->text( 1 ).toInt();
+ if ( existing_id != -1 && existing_id != author_id ) //category already exists with this label... merge the two
+ {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "This author already exists. Continuing will merge these two authors into one. Are you sure?" ) ) )
+ {
+ case KMessageBox::Continue: {
+ database->mergeAuthors( existing_id, author_id );
+ break;
+ }
+ default:
+ reload(ForceReload);
+ break;
+ }
+ }
+ else {
+ database->modAuthor( ( i->text( 1 ) ).toInt(), i->text( 0 ) );
+ }
+}
+
+bool StdAuthorListView::checkBounds( const TQString &name )
+{
+ if ( name.length() > uint(database->maxAuthorNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Author name cannot be longer than %1 characters." ) ).arg( database->maxAuthorNameLength() ) );
+ return false;
+ }
+
+ return true;
+}
+
+
+AuthorCheckListItem::AuthorCheckListItem( AuthorCheckListView* qlv, const Element &author ) : TQCheckListItem( qlv, TQString::null, TQCheckListItem::CheckBox ),
+ authorStored(author),
+ m_listview(qlv)
+{
+}
+
+AuthorCheckListItem::AuthorCheckListItem( AuthorCheckListView* qlv, TQListViewItem *after, const Element &author ) : TQCheckListItem( qlv, after, TQString::null, TQCheckListItem::CheckBox ),
+ authorStored(author),
+ m_listview(qlv)
+{
+}
+
+Element AuthorCheckListItem::author() const
+{
+ return authorStored;
+}
+
+TQString AuthorCheckListItem::text( int column ) const
+{
+ switch ( column ) {
+ case 0:
+ return ( authorStored.name );
+ case 1:
+ return ( TQString::number( authorStored.id ) );
+ default:
+ return TQString::null;
+ }
+}
+
+void AuthorCheckListItem::stateChange( bool on )
+{
+ m_listview->stateChange(this,on);
+}
+
+
+AuthorCheckListView::AuthorCheckListView( TQWidget *parent, RecipeDB *db ) : AuthorListView( parent, db )
+{
+ addColumn( i18n( "Author" ) );
+
+ TDEConfig *config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+}
+
+void AuthorCheckListView::createAuthor( const Element &author )
+{
+ createElement(new AuthorCheckListItem( this, author ));
+}
+
+void AuthorCheckListView::removeAuthor( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 1 );
+ removeElement(item);
+}
+
+void AuthorCheckListView::load( int limit, int offset )
+{
+ AuthorListView::load(limit,offset);
+
+ for ( TQValueList<Element>::const_iterator author_it = m_selections.begin(); author_it != m_selections.end(); ++author_it ) {
+ TQCheckListItem * item = ( TQCheckListItem* ) findItem( TQString::number( (*author_it).id ), 1 );
+ if ( item ) {
+ item->setOn(true);
+ }
+ }
+}
+
+void AuthorCheckListView::stateChange(AuthorCheckListItem *it,bool on)
+{
+ if ( !reloading() ) {
+ if ( on )
+ m_selections.append(it->author());
+ else
+ m_selections.remove(it->author());
+ }
+}
+
+#include "authorlistview.moc"
diff --git a/src/widgets/authorlistview.h b/src/widgets/authorlistview.h
new file mode 100644
index 0000000..a1cdc49
--- /dev/null
+++ b/src/widgets/authorlistview.h
@@ -0,0 +1,107 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef AUTHORLISTVIEW_H
+#define AUTHORLISTVIEW_H
+
+#include "dblistviewbase.h"
+#include "datablocks/element.h"
+
+class RecipeDB;
+class TDEPopupMenu;
+
+class AuthorCheckListView;
+
+class AuthorCheckListItem: public TQCheckListItem
+{
+public:
+ AuthorCheckListItem( AuthorCheckListView* qlv, const Element &author );
+ AuthorCheckListItem( AuthorCheckListView* qlv, TQListViewItem *after, const Element &author );
+
+ Element author() const;
+
+ virtual TQString text( int column ) const;
+
+protected:
+ virtual void stateChange( bool on );
+
+private:
+ Element authorStored;
+ AuthorCheckListView *m_listview;
+};
+
+
+class AuthorListView : public DBListViewBase
+{
+ TQ_OBJECT
+
+public:
+ AuthorListView( TQWidget *parent, RecipeDB *db );
+
+protected slots:
+ void checkCreateAuthor( const Element &el );
+ virtual void createAuthor( const Element & ) = 0;
+ virtual void removeAuthor( int ) = 0;
+ virtual void load( int curr_limit, int curr_offset );
+
+protected:
+ virtual void init();
+};
+
+class StdAuthorListView : public AuthorListView
+{
+ TQ_OBJECT
+
+public:
+ StdAuthorListView( TQWidget *parent, RecipeDB *db, bool editable = false );
+
+protected:
+ virtual void createAuthor( const Element & );
+ virtual void removeAuthor( int );
+
+private slots:
+ void showPopup( TDEListView *, TQListViewItem *, const TQPoint & );
+
+ void createNew();
+ void remove
+ ();
+ void rename();
+
+ void modAuthor( TQListViewItem* i );
+ void saveAuthor( TQListViewItem* i );
+
+private:
+ bool checkBounds( const TQString &name );
+
+ TDEPopupMenu *kpop;
+};
+
+
+class AuthorCheckListView : public AuthorListView
+{
+public:
+ AuthorCheckListView( TQWidget *parent, RecipeDB *db );
+
+ virtual void stateChange(AuthorCheckListItem *,bool);
+
+ TQValueList<Element> selections() const{ return m_selections; }
+
+protected:
+ virtual void createAuthor( const Element &ing );
+ virtual void removeAuthor( int );
+
+ virtual void load( int limit, int offset );
+
+private:
+ TQValueList<Element> m_selections;
+};
+
+#endif //AUTHORLISTVIEW_H
diff --git a/src/widgets/categorycombobox.cpp b/src/widgets/categorycombobox.cpp
new file mode 100644
index 0000000..7935cbb
--- /dev/null
+++ b/src/widgets/categorycombobox.cpp
@@ -0,0 +1,182 @@
+/***************************************************************************
+* Copyright (C) 2003-2004 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "categorycombobox.h"
+
+#include <tqlistbox.h>
+
+#include <tdelocale.h>
+#include <tdeconfig.h>
+#include <tdeglobal.h>
+
+#include "backends/recipedb.h"
+#include "backends/progressinterface.h"
+#include "datablocks/elementlist.h"
+#include "datablocks/categorytree.h"
+
+CategoryComboBox::CategoryComboBox( TQWidget *parent, RecipeDB *db ) : KComboBox( parent ),
+ database( db ),
+ m_offset(0)
+{
+ connect( database, TQ_SIGNAL( categoryCreated( const Element &, int ) ), TQ_SLOT( createCategory( const Element &, int ) ) );
+ connect( database, TQ_SIGNAL( categoryRemoved( int ) ), TQ_SLOT( removeCategory( int ) ) );
+ connect( database, TQ_SIGNAL( categoryModified( const Element & ) ), TQ_SLOT( modifyCategory( const Element & ) ) );
+ connect( database, TQ_SIGNAL( categoriesMerged( int, int ) ), TQ_SLOT( mergeCategories( int, int ) ) );
+
+ // Insert default "All Categories" (row 0, which will be translated to -1 as category in the filtering process)
+ // the rest of the items are loaded when needed in order to significantly speed up startup
+ insertItem( i18n( "All Categories" ) );
+}
+
+void CategoryComboBox::popup()
+{
+ if ( count() == 1 )
+ reload();
+ KComboBox::popup();
+}
+
+void CategoryComboBox::reload()
+{
+ TQString remember_cat_filter = currentText();
+
+ TDEConfig * config = TDEGlobal::config();config->setGroup( "Performance" );
+ int limit = config->readNumEntry( "CategoryLimit", -1 );
+
+ //ProgressInterface pi(this);
+ //pi.listenOn(database);
+
+ CategoryTree categoryList;
+ database->loadCategories( &categoryList, limit, m_offset, -1 );
+
+ clear();
+ categoryComboRows.clear();
+
+ // Insert default "All Categories" (row 0, which will be translated to -1 as category in the filtering process)
+ insertItem( i18n( "All Categories" ) );
+
+ //Now load the categories
+ int row = 1;
+ loadCategories(&categoryList,row);
+
+ if ( listBox() ->findItem( remember_cat_filter, TQt::ExactMatch ) ) {
+ setCurrentText( remember_cat_filter );
+ }
+}
+
+void CategoryComboBox::loadCategories( CategoryTree *categoryTree, int &row )
+{
+ for ( CategoryTree * child_it = categoryTree->firstChild(); child_it; child_it = child_it->nextSibling() ) {
+ insertItem( child_it->category.name );
+ categoryComboRows.insert( row, child_it->category.id ); // store category id's in the combobox position to obtain the category id later
+ row++;
+ loadCategories( child_it, row );
+ }
+}
+
+void CategoryComboBox::loadNextGroup()
+{
+ TDEConfig * config = TDEGlobal::config();config->setGroup( "Performance" );
+ int limit = config->readNumEntry( "CategoryLimit", -1 );
+
+ m_offset += limit;
+
+ reload();
+}
+
+void CategoryComboBox::loadPrevGroup()
+{
+ TDEConfig * config = TDEGlobal::config();config->setGroup( "Performance" );
+ int limit = config->readNumEntry( "CategoryLimit", -1 );
+
+ m_offset -= limit;
+
+ reload();
+}
+
+int CategoryComboBox::id( int row )
+{
+ if ( row )
+ return categoryComboRows[ row ];
+ else
+ return -1; // No category filtering
+}
+
+void CategoryComboBox::createCategory( const Element &element, int /*parent_id*/ )
+{
+ int row = findInsertionPoint( element.name );
+
+ insertItem( element.name, row );
+
+ //now update the map by pushing everything after this item down
+ TQMap<int, int> new_map;
+ for ( TQMap<int, int>::iterator it = categoryComboRows.begin(); it != categoryComboRows.end(); ++it ) {
+ if ( it.key() >= row ) {
+ new_map.insert( it.key() + 1, it.data() );
+ }
+ else
+ new_map.insert( it.key(), it.data() );
+ }
+ categoryComboRows = new_map;
+ categoryComboRows.insert( row, element.id );
+}
+
+void CategoryComboBox::removeCategory( int id )
+{
+ int row = -1;
+ for ( TQMap<int, int>::iterator it = categoryComboRows.begin(); it != categoryComboRows.end(); ++it ) {
+ if ( it.data() == id ) {
+ row = it.key();
+ removeItem( row );
+ categoryComboRows.remove( it );
+ break;
+ }
+ }
+
+ if ( row == -1 )
+ return ;
+
+ //now update the map by pushing everything after this item up
+ TQMap<int, int> new_map;
+ for ( TQMap<int, int>::iterator it = categoryComboRows.begin(); it != categoryComboRows.end(); ++it ) {
+ if ( it.key() > row ) {
+ new_map.insert( it.key() - 1, it.data() );
+ }
+ else
+ new_map.insert( it.key(), it.data() );
+ }
+ categoryComboRows = new_map;
+}
+
+void CategoryComboBox::modifyCategory( const Element &element )
+{
+ for ( TQMap<int, int>::const_iterator it = categoryComboRows.begin(); it != categoryComboRows.end(); ++it ) {
+ if ( it.data() == element.id )
+ changeItem( element.name, it.key() );
+ }
+}
+
+void CategoryComboBox::mergeCategories( int /*to_id*/, int from_id )
+{
+ removeCategory( from_id );
+}
+
+int CategoryComboBox::findInsertionPoint( const TQString &name )
+{
+ for ( int i = 1; i < count(); i++ ) {
+ if ( TQString::localeAwareCompare( name, text( i ) ) < 0 )
+ return i;
+ }
+
+ return count();
+}
+
+#include "categorycombobox.moc"
diff --git a/src/widgets/categorycombobox.h b/src/widgets/categorycombobox.h
new file mode 100644
index 0000000..4cc99aa
--- /dev/null
+++ b/src/widgets/categorycombobox.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+* Copyright (C) 2003 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef CATEGORYCOMBOBOX_H
+#define CATEGORYCOMBOBOX_H
+
+#include <kcombobox.h>
+
+#include <tqmap.h>
+
+#include "datablocks/element.h"
+
+class RecipeDB;
+class CategoryTree;
+
+class CategoryComboBox : public KComboBox
+{
+ TQ_OBJECT
+
+public:
+ CategoryComboBox( TQWidget *parent, RecipeDB *db );
+
+ void reload();
+ int id( int row );
+
+public slots:
+ void loadNextGroup();
+ void loadPrevGroup();
+
+protected:
+ virtual void popup();
+
+private slots:
+ void createCategory( const Element &element, int /*parent_id*/ );
+ void removeCategory( int id );
+ void modifyCategory( const Element &element );
+ void mergeCategories( int /*to_id*/, int from_id );
+
+ int findInsertionPoint( const TQString &name );
+
+private:
+ void loadCategories( CategoryTree *categoryList, int &row );
+
+ RecipeDB *database;
+ TQMap<int, int> categoryComboRows; // Contains the category id for every given row in the category combobox
+ int m_offset;
+};
+
+#endif //CATEGORYCOMBOBOX_H
+
diff --git a/src/widgets/categorylistview.cpp b/src/widgets/categorylistview.cpp
new file mode 100644
index 0000000..2ebe6e0
--- /dev/null
+++ b/src/widgets/categorylistview.cpp
@@ -0,0 +1,637 @@
+
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "categorylistview.h"
+
+#include <tdelocale.h>
+#include <tdemessagebox.h>
+#include <kiconloader.h>
+#include <tdepopupmenu.h>
+#include <tdeconfig.h>
+#include <tdeglobal.h>
+#include <kdebug.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/categorytree.h"
+#include "dialogs/createcategorydialog.h"
+#include "dialogs/dependanciesdialog.h"
+
+CategoryCheckListItem::CategoryCheckListItem( CategoryCheckListView* klv, const Element &category, bool _exclusive ) : TQCheckListItem( klv, TQString::null, TQCheckListItem::CheckBox ), CategoryItemInfo( category ),
+ locked( false ),
+ exclusive( _exclusive ),
+ m_listview(klv)
+{
+ setOn( false ); // Set unchecked by default
+}
+
+CategoryCheckListItem::CategoryCheckListItem( TQListViewItem* it, const Element &category, bool _exclusive ) : TQCheckListItem( it, TQString::null, TQCheckListItem::CheckBox ), CategoryItemInfo( category ),
+ locked( false ),
+ exclusive( _exclusive ),
+ m_listview((CategoryCheckListView*)it->listView())
+{
+ setOn( false ); // Set unchecked by default
+}
+
+CategoryCheckListItem::CategoryCheckListItem( CategoryCheckListView* klv, TQListViewItem* it, const Element &category, bool _exclusive ) : TQCheckListItem( klv, it, TQString::null, TQCheckListItem::CheckBox ), CategoryItemInfo( category ),
+ locked( false ),
+ exclusive( _exclusive ),
+ m_listview(klv)
+{
+ setOn( false ); // Set unchecked by default
+}
+
+TQString CategoryCheckListItem::text( int column ) const
+{
+ if ( column == 1 )
+ return ( TQString::number( ctyStored.id ) );
+ else
+ return ( ctyStored.name );
+}
+
+void CategoryCheckListItem::setText( int column, const TQString &text )
+{
+ switch ( column ) {
+ case 0:
+ ctyStored.name = text;
+ break;
+ default:
+ break;
+ }
+}
+
+void CategoryCheckListItem::stateChange( bool on )
+{
+ m_listview->stateChange(this,on);
+
+ if ( locked )
+ return;
+
+ if ( on && exclusive ) {
+ setParentsState( false );
+ setChildrenState( false );
+ }
+}
+
+void CategoryCheckListItem::setChildrenState( bool on )
+{
+ if ( !isPopulated() )
+ return;
+
+ for ( CategoryCheckListItem * cat_it = ( CategoryCheckListItem* ) firstChild(); cat_it; cat_it = ( CategoryCheckListItem* ) cat_it->nextSibling() ) {
+ cat_it->locked = true;
+ cat_it->setOn( on );
+ cat_it->setChildrenState( on );
+ cat_it->locked = false;
+ }
+}
+
+void CategoryCheckListItem::setParentsState( bool on )
+{
+ locked = true;
+
+ CategoryCheckListItem *cat_it;
+ for ( cat_it = ( CategoryCheckListItem* ) parent(); cat_it; cat_it = ( CategoryCheckListItem* ) cat_it->parent() )
+ cat_it->setOn( on );
+
+ locked = false;
+}
+
+
+
+
+CategoryListItem::CategoryListItem( TQListView* klv, const Element &category ) : TQListViewItem( klv ),
+ CategoryItemInfo(category)
+{}
+
+CategoryListItem::CategoryListItem( TQListViewItem* it, const Element &category ) : TQListViewItem( it ),
+ CategoryItemInfo(category)
+{}
+
+CategoryListItem::CategoryListItem( TQListView* klv, TQListViewItem* it, const Element &category ) : TQListViewItem( klv, it ),
+ CategoryItemInfo(category)
+{}
+
+TQString CategoryListItem::text( int column ) const
+{
+ if ( column == 1 )
+ return ( TQString::number( ctyStored.id ) );
+ else
+ return ( ctyStored.name );
+}
+
+void CategoryListItem::setText( int column, const TQString &text )
+{
+ if ( column == 0 )
+ ctyStored.name = text;
+}
+
+
+
+CategoryListView::CategoryListView( TQWidget *parent, RecipeDB *db ) : DBListViewBase( parent, db, db->categoryTopLevelCount() ),
+ m_item_to_delete(0)
+{
+ //connect( this, TQ_SIGNAL( spacePressed(TQListViewItem*) ), TQ_SLOT( open(TQListViewItem*) ) );
+ //connect( this, TQ_SIGNAL( returnPressed(TQListViewItem*) ), TQ_SLOT( open(TQListViewItem*) ) );
+ //connect( this, TQ_SIGNAL( executed(TQListViewItem*) ), TQ_SLOT( open(TQListViewItem*) ) );
+
+ connect( this, TQ_SIGNAL( expanded(TQListViewItem*) ), TQ_SLOT( open(TQListViewItem*) ) );
+
+ setRootIsDecorated( true );
+ setAllColumnsShowFocus( true );
+ setDefaultRenameAction( TQListView::Reject );
+}
+
+void CategoryListView::init()
+{
+ connect( database, TQ_SIGNAL( categoryCreated( const Element &, int ) ), TQ_SLOT( checkCreateCategory( const Element &, int ) ) );
+ connect( database, TQ_SIGNAL( categoryRemoved( int ) ), TQ_SLOT( removeCategory( int ) ) );
+ connect( database, TQ_SIGNAL( categoryModified( const Element & ) ), TQ_SLOT( modifyCategory( const Element & ) ) );
+ connect( database, TQ_SIGNAL( categoryModified( int, int ) ), TQ_SLOT( modifyCategory( int, int ) ) );
+ connect( database, TQ_SIGNAL( categoriesMerged( int, int ) ), TQ_SLOT( mergeCategories( int, int ) ) );
+}
+
+// (Re)loads the data from the database
+void CategoryListView::load( int limit, int offset )
+{
+ items_map.clear();
+
+ CategoryTree list;
+ CategoryTree *p_list = &list;
+ database->loadCachedCategories( &p_list, limit, offset, -1, false );
+
+ setTotalItems(p_list->count());
+
+ for ( CategoryTree * child_it = p_list->firstChild(); child_it; child_it = child_it->nextSibling() ) {
+ createCategory( child_it->category, -1 );
+ }
+}
+
+void CategoryListView::populate( TQListViewItem *item )
+{
+ CategoryItemInfo *cat_item = dynamic_cast<CategoryItemInfo*>(item);
+ if ( !cat_item || cat_item->isPopulated() ) return;
+
+ if ( item->firstChild() && item->firstChild()->rtti() != PSEUDOLISTITEM_RTTI )
+ return;
+
+ delete item->firstChild(); //delete the "pseudo item"
+
+ int id = cat_item->categoryId();
+ cat_item->setPopulated(true);
+
+ CategoryTree categoryTree;
+ database->loadCategories( &categoryTree, -1, 0, id, false );
+
+ for ( CategoryTree * child_it = categoryTree.firstChild(); child_it; child_it = child_it->nextSibling() ) {
+ createCategory( child_it->category, id );
+ }
+}
+
+void CategoryListView::populateAll( TQListViewItem *parent )
+{
+ if ( !parent )
+ parent = firstChild();
+
+ for ( TQListViewItem *item = parent; item; item = item->nextSibling() ) {
+ populate( item );
+ if ( item->firstChild() )
+ populateAll( item->firstChild() );
+ }
+}
+
+void CategoryListView::open( TQListViewItem *item )
+{
+ Q_ASSERT( item );
+ if ( !item->firstChild() || item->firstChild()->rtti() != PSEUDOLISTITEM_RTTI ) return;
+
+ populate(item);
+
+ item->setOpen(true);
+}
+
+void CategoryListView::checkCreateCategory( const Element &el, int parent_id )
+{
+ if ( parent_id != -1 || handleElement(el.name) ) { //only create this category if the base class okays it; allow all non-top-level items
+ createCategory(el,parent_id);
+ }
+}
+
+void CategoryListView::modifyCategory( const Element &category )
+{
+ TQListViewItem * item = items_map[ category.id ];
+
+ if ( item )
+ item->setText( 0, category.name );
+}
+
+void CategoryListView::modifyCategory( int id, int parent_id )
+{
+ TQMap<int,TQListViewItem*>::iterator item_it = items_map.find(id);
+ if ( item_it != items_map.end() ) {
+ TQListViewItem *item = *item_it;
+ Q_ASSERT( item );
+
+ removeElement(item,false);
+ if ( !item->parent() )
+ takeItem( item );
+ else
+ item->parent() ->takeItem( item );
+
+ if ( parent_id == -1 ) {
+ insertItem(item);
+ createElement(item);
+ }
+ else {
+ TQMap<int,TQListViewItem*>::iterator parent_item_it = items_map.find(parent_id);
+ if ( parent_item_it != items_map.end() &&
+ dynamic_cast<CategoryItemInfo*>(*parent_item_it)->isPopulated() ) {
+ (*parent_item_it)->insertItem( item );
+ createElement(item);
+ }
+ else {
+ if ( !(*parent_item_it)->firstChild() )
+ new PseudoListItem( *parent_item_it );
+
+ //removeElement() was already called on this item, so we just delete it
+ //we can't delete it just yet because this function is called by a slot
+ delete m_item_to_delete;
+ m_item_to_delete = item;
+ }
+ }
+ }
+}
+
+void CategoryListView::mergeCategories( int id1, int id2 )
+{
+ TQListViewItem * to_item = items_map[ id1 ];
+ TQListViewItem *from_item = items_map[ id2 ];
+
+ CategoryItemInfo *info_item = dynamic_cast<CategoryItemInfo*>(to_item);
+
+ if ( to_item && info_item->isPopulated() && from_item ) {
+ //note that this takes care of any recipes that may be children as well
+ TQListViewItem *next_sibling;
+ for ( TQListViewItem * it = from_item->firstChild(); it; it = next_sibling ) {
+ next_sibling = it->nextSibling(); //get the sibling before we move the item
+
+ removeElement(it,false);
+ from_item->takeItem( it );
+
+ to_item->insertItem( it );
+ createElement(it);
+ }
+ }
+
+ removeCategory( id2 );
+}
+
+
+StdCategoryListView::StdCategoryListView( TQWidget *parent, RecipeDB *db, bool editable ) : CategoryListView( parent, db ),
+ clipboard_item( 0 ),
+ clipboard_parent( 0 )
+{
+ addColumn( i18n( "Category" ) );
+
+ TDEConfig *config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ if ( editable ) {
+ setRenameable( 0, true );
+ setDragEnabled( true );
+ setAcceptDrops( true );
+
+ TDEIconLoader *il = new TDEIconLoader;
+
+ kpop = new TDEPopupMenu( this );
+ kpop->insertItem( il->loadIcon( "document-new", TDEIcon::NoGroup, 16 ), i18n( "&Create" ), this, TQ_SLOT( createNew() ), CTRL + Key_C );
+ kpop->insertItem( il->loadIcon( "edit-delete", TDEIcon::NoGroup, 16 ), i18n( "&Delete" ), this, TQ_SLOT( remove
+ () ), Key_Delete );
+ kpop->insertItem( il->loadIcon( "edit", TDEIcon::NoGroup, 16 ), i18n( "&Rename" ), this, TQ_SLOT( rename() ), CTRL + Key_R );
+ kpop->insertSeparator();
+ kpop->insertItem( il->loadIcon( "edit-cut", TDEIcon::NoGroup, 16 ), i18n( "Cu&t" ), this, TQ_SLOT( cut() ), CTRL + Key_X );
+ kpop->insertItem( il->loadIcon( "edit-paste", TDEIcon::NoGroup, 16 ), i18n( "&Paste" ), this, TQ_SLOT( paste() ), CTRL + Key_V );
+ kpop->insertItem( il->loadIcon( "edit-paste", TDEIcon::NoGroup, 16 ), i18n( "Paste as Subcategory" ), this, TQ_SLOT( pasteAsSub() ), CTRL + SHIFT + Key_V );
+ kpop->polish();
+
+ delete il;
+
+ connect( kpop, TQ_SIGNAL( aboutToShow() ), TQ_SLOT( preparePopup() ) );
+ connect( this, TQ_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ), TQ_SLOT( showPopup( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
+ connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem*, const TQPoint &, int ) ), TQ_SLOT( modCategory( TQListViewItem* ) ) );
+ connect( this, TQ_SIGNAL( itemRenamed ( TQListViewItem* ) ), TQ_SLOT( saveCategory( TQListViewItem* ) ) );
+ connect( this, TQ_SIGNAL( moved( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ), TQ_SLOT( changeCategoryParent( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ) );
+ }
+}
+
+StdCategoryListView::~StdCategoryListView()
+{
+ delete clipboard_item;
+}
+
+void StdCategoryListView::setPixmap( const TQPixmap &icon )
+{
+ m_folder_icon = icon;
+}
+
+void StdCategoryListView::preparePopup()
+{
+ //only enable the paste items if clipboard_item isn't null
+ kpop->setItemEnabled( kpop->idAt( 5 ), clipboard_item );
+ kpop->setItemEnabled( kpop->idAt( 6 ), clipboard_item );
+}
+
+void StdCategoryListView::showPopup( TDEListView * /*l*/, TQListViewItem *i, const TQPoint &p )
+{
+ if ( i )
+ kpop->exec( p );
+}
+
+void StdCategoryListView::createNew()
+{
+ ElementList categories;
+ database->loadCategories( &categories );
+ CreateCategoryDialog* categoryDialog = new CreateCategoryDialog( this, categories );
+
+ if ( categoryDialog->exec() == TQDialog::Accepted ) {
+ TQString result = categoryDialog->newCategoryName();
+ int subcategory = categoryDialog->subcategory();
+
+ //check bounds first
+ if ( checkBounds( result ) )
+ database->createNewCategory( result, subcategory ); // Create the new category in the database
+ }
+ delete categoryDialog;
+}
+
+void StdCategoryListView::remove
+ ()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item ) {
+ int id = item->text( 1 ).toInt();
+
+ ElementList recipeDependancies;
+ database->findUseOfCategoryInRecipes( &recipeDependancies, id );
+
+ if ( recipeDependancies.isEmpty() ) {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "Are you sure you want to delete this category and all its subcategories?" ) ) ) {
+ case KMessageBox::Continue:
+ database->removeCategory( id );
+ break;
+ }
+ return;
+ }
+ else { // need warning!
+ ListInfo info;
+ info.list = recipeDependancies;
+ info.name = i18n("Recipes");
+ DependanciesDialog warnDialog( this, info, false );
+
+ if ( warnDialog.exec() == TQDialog::Accepted )
+ database->removeCategory( id );
+ }
+ }
+}
+
+void StdCategoryListView::rename()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item )
+ CategoryListView::rename( item, 0 );
+}
+
+void StdCategoryListView::cut()
+{
+ //restore a never used cut
+ if ( clipboard_item ) {
+ if ( clipboard_parent )
+ clipboard_parent->insertItem( clipboard_item );
+ else
+ insertItem( clipboard_item );
+ clipboard_item = 0;
+ }
+
+ TQListViewItem *item = currentItem();
+
+ if ( item ) {
+ clipboard_item = item;
+ clipboard_parent = item->parent();
+
+ if ( item->parent() )
+ item->parent() ->takeItem( item );
+ else
+ takeItem( item );
+ }
+}
+
+void StdCategoryListView::paste()
+{
+ TQListViewItem * item = currentItem();
+ if ( item && clipboard_item ) {
+ if ( item->parent() )
+ item->parent() ->insertItem( clipboard_item );
+ else
+ insertItem( clipboard_item );
+
+ database->modCategory( clipboard_item->text( 1 ).toInt(), item->parent() ? item->parent() ->text( 1 ).toInt() : -1 );
+ clipboard_item = 0;
+ }
+}
+
+void StdCategoryListView::pasteAsSub()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item && clipboard_item ) {
+ item->insertItem( clipboard_item );
+ database->modCategory( clipboard_item->text( 1 ).toInt(), item->text( 1 ).toInt() );
+ clipboard_item = 0;
+ }
+}
+
+void StdCategoryListView::changeCategoryParent( TQListViewItem *item, TQListViewItem * /*afterFirst*/, TQListViewItem * /*afterNow*/ )
+{
+ int new_parent_id = -1;
+ if ( TQListViewItem * parent = item->parent() )
+ new_parent_id = parent->text( 1 ).toInt();
+
+ int cat_id = item->text( 1 ).toInt();
+
+ disconnect( TQ_SIGNAL( moved( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ) );
+ database->modCategory( cat_id, new_parent_id );
+ connect( this, TQ_SIGNAL( moved( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ), TQ_SLOT( changeCategoryParent( TQListViewItem *, TQListViewItem *, TQListViewItem * ) ) );
+}
+
+void StdCategoryListView::removeCategory( int id )
+{
+ TQListViewItem * item = items_map[ id ];
+
+ items_map.remove( id );
+ removeElement(item);
+}
+
+void StdCategoryListView::createCategory( const Element &category, int parent_id )
+{
+ CategoryListItem * new_item = 0;
+ if ( parent_id == -1 ) {
+ new_item = new CategoryListItem( this, category );
+ }
+ else {
+ CategoryListItem *parent = (CategoryListItem*)items_map[ parent_id ];
+
+ if ( parent ) {
+ if ( parent->isPopulated() )
+ new_item = new CategoryListItem( parent, category );
+ else if ( !parent->firstChild() ) {
+ new PseudoListItem( parent );
+ parent->setOpen(true);
+ }
+ }
+ }
+
+ if ( new_item ) {
+ items_map.insert( category.id, new_item );
+ new_item->setPixmap( 0, m_folder_icon );
+ createElement(new_item);//new TQListViewItem(new_item);
+
+ CategoryTree list;
+ CategoryTree *p_list = &list;
+ database->loadCachedCategories( &p_list, 1, 0, category.id, false );
+
+ if ( p_list->firstChild() )
+ new PseudoListItem( new_item );
+ }
+}
+
+void StdCategoryListView::modCategory( TQListViewItem* i )
+{
+ if ( i )
+ CategoryListView::rename( i, 0 );
+}
+
+void StdCategoryListView::saveCategory( TQListViewItem* i )
+{
+ CategoryListItem * cat_it = ( CategoryListItem* ) i;
+
+ if ( !checkBounds( cat_it->categoryName() ) ) {
+ reload(ForceReload); //reset the changed text
+ return ;
+ }
+
+ int existing_id = database->findExistingCategoryByName( cat_it->categoryName() );
+ int cat_id = cat_it->categoryId();
+ if ( existing_id != -1 && existing_id != cat_id ) //category already exists with this label... merge the two
+ {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "This category already exists. Continuing will merge these two categories into one. Are you sure?" ) ) )
+ {
+ case KMessageBox::Continue: {
+ database->mergeCategories( existing_id, cat_id );
+ break;
+ }
+ default:
+ reload(ForceReload);
+ break;
+ }
+ }
+ else
+ database->modCategory( cat_id, cat_it->categoryName() );
+}
+
+bool StdCategoryListView::checkBounds( const TQString &name )
+{
+ if ( name.length() > uint(database->maxCategoryNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Category name cannot be longer than %1 characters." ) ).arg( database->maxCategoryNameLength() ) );
+ return false;
+ }
+
+ return true;
+}
+
+
+
+CategoryCheckListView::CategoryCheckListView( TQWidget *parent, RecipeDB *db, bool _exclusive, const ElementList &init_items_checked ) : CategoryListView( parent, db ),
+ exclusive(_exclusive)
+{
+ addColumn( i18n( "Category" ) );
+
+ TDEConfig *config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ for ( ElementList::const_iterator it = init_items_checked.begin(); it != init_items_checked.end(); ++it )
+ m_selections.append(*it);
+}
+
+void CategoryCheckListView::removeCategory( int id )
+{
+ TQListViewItem * item = items_map[ id ];
+
+ items_map.remove( id );
+ removeElement(item);
+}
+
+void CategoryCheckListView::createCategory( const Element &category, int parent_id )
+{
+ CategoryCheckListItem * new_item = 0;
+ if ( parent_id == -1 ) {
+ new_item = new CategoryCheckListItem( this, category, exclusive );
+ }
+ else {
+ TQListViewItem *parent = items_map[ parent_id ];
+ if ( parent )
+ new_item = new CategoryCheckListItem( parent, category, exclusive );
+ }
+
+ if ( new_item ) {
+ items_map.insert( category.id, new_item );
+ createElement(new_item);
+
+ CategoryTree list;
+ CategoryTree *p_list = &list;
+ database->loadCachedCategories( &p_list, 1, 0, category.id, false );
+
+ if ( p_list->firstChild() )
+ new PseudoListItem( new_item );
+
+
+ new_item->setOpen( false );
+ }
+}
+
+void CategoryCheckListView::stateChange( CategoryCheckListItem* it, bool on )
+{
+ if ( !reloading() ) {
+ if ( on )
+ m_selections.append(it->element());
+ else
+ m_selections.remove(it->element());
+ }
+}
+
+void CategoryCheckListView::load( int limit, int offset )
+{
+ CategoryListView::load(limit,offset);
+
+ for ( TQValueList<Element>::const_iterator it = m_selections.begin(); it != m_selections.end(); ++it ) {
+ TQCheckListItem * item = ( TQCheckListItem* ) findItem( TQString::number( (*it).id ), 1 );
+ if ( item ) {
+ item->setOn(true);
+ }
+ }
+}
+
+#include "categorylistview.moc"
diff --git a/src/widgets/categorylistview.h b/src/widgets/categorylistview.h
new file mode 100644
index 0000000..1dcabba
--- /dev/null
+++ b/src/widgets/categorylistview.h
@@ -0,0 +1,288 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef CATEGORYLISTVIEW_H
+#define CATEGORYLISTVIEW_H
+
+#include <tqmap.h>
+#include <tqpixmap.h>
+
+#include "dblistviewbase.h"
+
+#include "datablocks/elementlist.h"
+
+class TDEPopupMenu;
+
+class RecipeDB;
+class CategoryTree;
+class CategoryCheckListView;
+
+#define CATEGORYCHECKLISTITEM_RTTI 1005
+#define CATEGORYLISTITEM_RTTI 1001
+#define PSEUDOLISTITEM_RTTI 1008
+
+/** Category listitems inherit this class to provide a common interface for accessing this information.
+ */
+class CategoryItemInfo
+{
+public:
+ CategoryItemInfo( const Element &category ) : ctyStored( category ), populated(false){}
+ bool isPopulated() const { return populated; }
+ void setPopulated( bool b ){ populated = b; }
+
+ Element element() const
+ {
+ return ctyStored;
+ }
+
+ int categoryId() const
+ {
+ return ctyStored.id;
+ }
+ TQString categoryName() const
+ {
+ return ctyStored.name;
+ }
+
+protected:
+ Element ctyStored;
+
+private:
+ bool populated;
+};
+
+class CategoryCheckListItem : public TQCheckListItem, public CategoryItemInfo
+{
+public:
+ CategoryCheckListItem( CategoryCheckListView* klv, const Element &category, bool exclusive = true );
+ CategoryCheckListItem( TQListViewItem* it, const Element &category, bool exclusive = true );
+ CategoryCheckListItem( CategoryCheckListView* klv, TQListViewItem* it, const Element &category, bool exclusive = true );
+
+ virtual TQString text( int column ) const;
+ virtual void setText( int column, const TQString &text );
+
+ int rtti() const
+ {
+ return CATEGORYCHECKLISTITEM_RTTI;
+ }
+
+protected:
+ virtual void stateChange( bool );
+ void setChildrenState( bool );
+ void setParentsState( bool );
+
+ bool locked;
+ bool exclusive;
+
+private:
+ CategoryCheckListView *m_listview;
+};
+
+
+class CategoryListItem : public TQListViewItem, public CategoryItemInfo
+{
+public:
+ CategoryListItem( TQListView* klv, const Element &category );
+ CategoryListItem( TQListViewItem* it, const Element &category );
+ CategoryListItem( TQListView* klv, TQListViewItem* it, const Element &category );
+
+ virtual TQString text( int column ) const;
+ virtual void setText( int column, const TQString &text );
+
+ int rtti() const
+ {
+ return CATEGORYLISTITEM_RTTI;
+ }
+};
+
+
+
+class CategoryListView : public DBListViewBase
+{
+ TQ_OBJECT
+
+public:
+ CategoryListView( TQWidget *parent, RecipeDB * );
+
+ void populateAll( TQListViewItem *parent = 0 );
+
+public slots:
+ void open( TQListViewItem *item );
+
+protected:
+ virtual void init();
+
+ virtual void load( int limit, int offset );
+
+ /** so that it allows dropping into
+ * subchildren that aren't expandable. The code is taken from TDE's TDEListView with
+ * one line commented out.
+ */
+ void findDrop( const TQPoint &pos, TQListViewItem *&parent, TQListViewItem *&after )
+ {
+ TQPoint p ( contentsToViewport( pos ) );
+
+ // Get the position to put it in
+ TQListViewItem *atpos = itemAt( p );
+
+ TQListViewItem *above;
+ if ( !atpos ) // put it at the end
+ above = lastItem();
+ else {
+ // Get the closest item before us ('atpos' or the one above, if any)
+ if ( p.y() - itemRect( atpos ).topLeft().y() < ( atpos->height() / 2 ) )
+ above = atpos->itemAbove();
+ else
+ above = atpos;
+ }
+
+ if ( above ) {
+ // if above has children, I might need to drop it as the first item there
+
+ if ( above->firstChild() && above->isOpen() ) {
+ parent = above;
+ after = 0;
+ return ;
+ }
+
+ // Now, we know we want to go after "above". But as a child or as a sibling ?
+ // We have to ask the "above" item if it accepts children.
+ // ### NOTE: Here is the one line commented out so that "above" always accepts children
+ //if (above->isExpandable())
+ {
+ // The mouse is sufficiently on the right ? - doesn't matter if 'above' has visible children
+ if ( p.x() >= depthToPixels( above->depth() + 1 ) ||
+ ( above->isOpen() && above->childCount() > 0 ) )
+ {
+ parent = above;
+ after = 0L;
+ return ;
+ }
+ }
+
+ // Ok, there's one more level of complexity. We may want to become a new
+ // sibling, but of an upper-level group, rather than the "above" item
+ TQListViewItem * betterAbove = above->parent();
+ TQListViewItem * last = above;
+ while ( betterAbove ) {
+ // We are allowed to become a sibling of "betterAbove" only if we are
+ // after its last child
+ if ( last->nextSibling() == 0 ) {
+ if ( p.x() < depthToPixels ( betterAbove->depth() + 1 ) )
+ above = betterAbove; // store this one, but don't stop yet, there may be a better one
+ else
+ break; // not enough on the left, so stop
+ last = betterAbove;
+ betterAbove = betterAbove->parent(); // up one level
+ }
+ else
+ break; // we're among the child of betterAbove, not after the last one
+ }
+ }
+ // set as sibling
+ after = above;
+ parent = after ? after->parent() : 0L ;
+ }
+
+protected slots:
+ virtual void removeCategory( int id ) = 0;
+ virtual void createCategory( const Element &category, int parent_id ) = 0;
+ virtual void modifyCategory( const Element &category );
+ virtual void modifyCategory( int id, int parent_id );
+ virtual void mergeCategories( int id1, int id2 );
+
+ virtual void checkCreateCategory( const Element &, int parent_id );
+ virtual void populate( TQListViewItem *item );
+
+ TQMap<int, TQListViewItem*> items_map;
+
+private:
+ TQListViewItem *m_item_to_delete;
+};
+
+
+class StdCategoryListView : public CategoryListView
+{
+ TQ_OBJECT
+
+public:
+ StdCategoryListView( TQWidget *parent, RecipeDB *, bool editable = false );
+ ~StdCategoryListView();
+
+protected:
+ virtual void removeCategory( int id );
+ virtual void createCategory( const Element &category, int parent_id );
+
+ void setPixmap( const TQPixmap &pixmap );
+
+private slots:
+ void preparePopup();
+ void showPopup( TDEListView *, TQListViewItem *, const TQPoint & );
+
+ void createNew();
+ void remove
+ ();
+ void rename();
+ void cut();
+ void paste();
+ void pasteAsSub();
+
+ void changeCategoryParent( TQListViewItem *item, TQListViewItem * /*afterFirst*/, TQListViewItem * /*afterNow*/ );
+
+ void modCategory( TQListViewItem* i );
+ void saveCategory( TQListViewItem* i );
+
+private:
+ bool checkBounds( const TQString &name );
+
+ TDEPopupMenu *kpop;
+ TQListViewItem *clipboard_item;
+ TQListViewItem *clipboard_parent;
+
+ TQPixmap m_folder_icon;
+};
+
+
+class CategoryCheckListView : public CategoryListView
+{
+ TQ_OBJECT
+
+public:
+ CategoryCheckListView( TQWidget *parent, RecipeDB *, bool exclusive=true, const ElementList &init_items_checked = ElementList() );
+
+ virtual void stateChange( CategoryCheckListItem*, bool );
+
+ ElementList selections() const{ return m_selections; }
+
+protected:
+ virtual void removeCategory( int id );
+ virtual void createCategory( const Element &category, int parent_id );
+
+ virtual void load( int limit, int offset );
+
+ bool exclusive;
+
+private:
+ ElementList m_selections;
+};
+
+
+class PseudoListItem : public TQListViewItem
+{
+public:
+ PseudoListItem( TQListView* lv ) : TQListViewItem(lv){}
+ PseudoListItem( TQListViewItem* it ) : TQListViewItem(it){}
+
+protected:
+ int rtti() const { return PSEUDOLISTITEM_RTTI; }
+};
+
+#endif //CATEGORYLISTVIEW_H
diff --git a/src/widgets/conversiontable.cpp b/src/widgets/conversiontable.cpp
new file mode 100644
index 0000000..a48b67e
--- /dev/null
+++ b/src/widgets/conversiontable.cpp
@@ -0,0 +1,426 @@
+/***************************************************************************
+* Copyright (C) 2003-2004 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* Copyright (C) 2003-2006 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "conversiontable.h"
+#include "datablocks/mixednumber.h"
+#include "widgets/fractioninput.h"
+
+#include <tqtooltip.h>
+
+#include <tdeglobal.h>
+#include <tdelocale.h>
+
+class ConversionTableToolTip : public TQToolTip
+{
+public:
+ ConversionTableToolTip( ConversionTable *t ) : TQToolTip( t->viewport() ),
+ table( t )
+ {}
+
+ void maybeTip( const TQPoint &pos )
+ {
+ if ( !table )
+ return ;
+
+ TQPoint cp = table->viewportToContents( pos );
+
+ int row = table->rowAt( cp.y() );
+ int col = table->columnAt( cp.x() );
+
+ if ( row == col )
+ return ;
+
+ TQString row_unit = table->verticalHeader() ->label( row );
+ TQString col_unit = table->horizontalHeader() ->label( col );
+ TQString text = table->text( row, col );
+ if ( text.isEmpty() )
+ text = "X"; //### Is this i18n friendly???
+
+ TQRect cr = table->cellGeometry( row, col );
+ cr.moveTopLeft( table->contentsToViewport( cr.topLeft() ) );
+ tip( cr, TQString( "1 %1 = %2 %3" ).arg( row_unit ).arg( text ).arg( col_unit ) );
+ }
+
+private:
+ ConversionTable *table;
+};
+
+ConversionTable::ConversionTable( TQWidget* parent, int maxrows, int maxcols ) : TQTable( maxrows, maxcols, parent, "table" )
+{
+ editBoxValue = -1;
+ items.setAutoDelete( true );
+ widgets.setAutoDelete( true );
+
+ ( void ) new ConversionTableToolTip( this );
+}
+
+ConversionTable::~ConversionTable()
+{}
+#include <kdebug.h>
+void ConversionTable::unitRemoved( int id )
+{
+ int index = *unitIDs.find( id );
+ kdDebug() << "index:" << index << endl;
+ removeRow( index );
+ removeColumn( index );
+ kdDebug() << "done" << endl;
+}
+
+void ConversionTable::unitCreated( const Unit &unit )
+{
+ insertColumns( numCols() );
+ insertRows( numRows() );
+ unitIDs.append( unit.id );
+ horizontalHeader() ->setLabel( numRows() - 1, unit.name );
+ verticalHeader() ->setLabel( numCols() - 1, unit.name );
+}
+
+TQTableItem* ConversionTable::item( int r, int c ) const
+{
+ return items.find( indexOf( r, c ) );
+}
+
+void ConversionTable::setItem( int r, int c, TQTableItem *i )
+{
+ items.replace( indexOf( r, c ), i );
+ i->setRow( r ); // Otherwise the item
+ i->setCol( c ); //doesn't know where it is!
+ updateCell( r, c );
+}
+
+void ConversionTable::clearCell( int r, int c )
+{
+ items.remove( indexOf( r, c ) );
+}
+
+void ConversionTable::takeItem( TQTableItem *item )
+{
+ items.setAutoDelete( false );
+ items.remove( indexOf( item->row(), item->col() ) );
+ items.setAutoDelete( true );
+}
+
+void ConversionTable::insertWidget( int r, int c, TQWidget *w )
+{
+ widgets.replace( indexOf( r, c ), w );
+}
+
+TQWidget* ConversionTable::cellWidget( int r, int c ) const
+{
+ return widgets.find( indexOf( r, c ) );
+}
+
+void ConversionTable::clearCellWidget( int r, int c )
+{
+ TQWidget * w = widgets.take( indexOf( r, c ) );
+ if ( w )
+ w->deleteLater();
+}
+
+
+ConversionTableItem::ConversionTableItem( TQTable *t, EditType et ) : TQTableItem( t, et, TQString::null )
+{
+ // we do not want this item to be replaced
+ setReplaceable( false );
+}
+
+void ConversionTableItem::paint( TQPainter *p, const TQColorGroup &cg, const TQRect &cr, bool selected )
+{
+ TQColorGroup g( cg );
+
+ // Draw in gray all those cells which are not editable
+
+ if ( row() == col() )
+ g.setColor( TQColorGroup::Base, gray );
+ TQTableItem::paint( p, g, cr, selected );
+}
+
+TQWidget* ConversionTableItem::createEditor() const
+{
+ FractionInput *editor = new FractionInput( table()->viewport(), MixedNumber::DecimalFormat );
+
+ MixedNumber current = MixedNumber::fromString(text());
+ if ( current.toDouble() > 1e-8 )
+ editor->setValue( current, 0 );
+
+ return editor;
+}
+
+void ConversionTableItem::setContentFromEditor( TQWidget *w )
+{
+ // the user changed the value of the combobox, so synchronize the
+ // value of the item (its text), with the value of the combobox
+ if ( w->inherits( "FractionInput" ) ) {
+ FractionInput* editor = ( FractionInput* ) w;
+ if ( editor->isInputValid() && !editor->isEmpty() && editor->value().toDouble() > 1e-6 ) {
+ setText( editor->value().toString(MixedNumber::DecimalFormat) );
+ emit ratioChanged( row(), col(), editor->value().toDouble() ); // Signal to store
+ }
+ else {
+ setText( TQString::null );
+ emit ratioRemoved( row(), col() );
+ }
+ }
+ else
+ TQTableItem::setContentFromEditor( w );
+}
+
+void ConversionTableItem::setText( const TQString &s )
+{
+ TQTableItem::setText( s );
+}
+TQString ConversionTable::text( int r, int c ) const // without this function, the usual (text(r,c)) won't work
+{
+ if ( item( r, c ) )
+ return item( r, c ) ->text(); //Note that item(r,c) was reimplemented here for large sparse tables...
+ else
+ return TQString::null;
+}
+
+void ConversionTable::initTable()
+{
+
+ for ( int r = 0;r < numRows();r++ ) {
+ this->createNewItem( r, r, 1.0 );
+ item( r, r ) ->setEnabled( false ); // Diagonal is not editable
+ }
+}
+
+void ConversionTable::createNewItem( int r, int c, double amount )
+{
+
+ ConversionTableItem * ci = new ConversionTableItem( this, TQTableItem::WhenCurrent );
+ ci->setText( beautify( TDEGlobal::locale() ->formatNumber( amount, 5 ) ) );
+ setItem( r, c, ci );
+ // connect signal (forward) to know when it's actually changed
+ connect( ci, TQ_SIGNAL( ratioChanged( int, int, double ) ), this, TQ_SIGNAL( ratioChanged( int, int, double ) ) );
+ connect( ci, TQ_SIGNAL( ratioRemoved( int, int ) ), this, TQ_SIGNAL( ratioRemoved( int, int ) ) );
+ connect( ci, TQ_SIGNAL( signalRepaintCell( int, int ) ), this, TQ_SLOT( repaintCell( int, int ) ) );
+}
+
+void ConversionTable::setUnitIDs( const IDList &idList )
+{
+ unitIDs = idList;
+}
+
+void ConversionTable::setRatio( int ingID1, int ingID2, double ratio )
+{
+ int indexID1 = unitIDs.findIndex( ingID1 );
+ int indexID2 = unitIDs.findIndex( ingID2 );
+
+ createNewItem( indexID1, indexID2, ratio );
+}
+
+
+int ConversionTable::getUnitID( int rc )
+{
+ return ( *( unitIDs.at( rc ) ) );
+}
+
+TQWidget * ConversionTable::beginEdit ( int row, int col, bool replace )
+{
+ // If there's no item, create it first.
+ if ( !item( row, col ) ) {
+ createNewItem( row, col, 0 );
+ }
+
+ // Then call normal beginEdit
+ return TQTable::beginEdit( row, col, replace );
+}
+
+void ConversionTableItem::setTextAndSave( const TQString &s )
+{
+ setText( s ); // Change text
+ emit signalRepaintCell( row(), col() ); // Indicate to update the cell to the table. Otherwise it's not repainted
+ emit ratioChanged( row(), col(), s.toDouble() ); // Signal to store
+}
+
+void ConversionTable::repaintCell( int r, int c )
+{
+ TQTable::updateCell( r, c );
+}
+
+void ConversionTable::resize( int r, int c )
+{
+ setNumRows( r );
+ setNumCols( c );
+ initTable();
+}
+
+void ConversionTable::clear( void )
+{
+ items.clear();
+ widgets.clear();
+ unitIDs.clear();
+ resize( 0, 0 );
+
+}
+
+//TODO this is incomplete/wrong
+void ConversionTable::swapRows( int row1, int row2, bool /*swapHeader*/ )
+{
+ //if ( swapHeader )
+ //((TQTableHeader*)verticalHeader())->swapSections( row1, row2, FALSE );
+
+ TQPtrVector<TQTableItem> tmpContents;
+ tmpContents.resize( numCols() );
+ TQPtrVector<TQWidget> tmpWidgets;
+ tmpWidgets.resize( numCols() );
+ int i;
+
+ items.setAutoDelete( FALSE );
+ widgets.setAutoDelete( FALSE );
+ for ( i = 0; i < numCols(); ++i ) {
+ TQTableItem *i1, *i2;
+ i1 = item( row1, i );
+ i2 = item( row2, i );
+ if ( i1 || i2 ) {
+ tmpContents.insert( i, i1 );
+ items.remove( indexOf( row1, i ) );
+ items.insert( indexOf( row1, i ), i2 );
+ items.remove( indexOf( row2, i ) );
+ items.insert( indexOf( row2, i ), tmpContents[ i ] );
+ if ( items[ indexOf( row1, i ) ] )
+ items[ indexOf( row1, i ) ] ->setRow( row1 );
+ if ( items[ indexOf( row2, i ) ] )
+ items[ indexOf( row2, i ) ] ->setRow( row2 );
+ }
+
+ TQWidget *w1, *w2;
+ w1 = cellWidget( row1, i );
+ w2 = cellWidget( row2, i );
+ if ( w1 || w2 ) {
+ tmpWidgets.insert( i, w1 );
+ widgets.remove( indexOf( row1, i ) );
+ widgets.insert( indexOf( row1, i ), w2 );
+ widgets.remove( indexOf( row2, i ) );
+ widgets.insert( indexOf( row2, i ), tmpWidgets[ i ] );
+ }
+ }
+ items.setAutoDelete( FALSE );
+ widgets.setAutoDelete( TRUE );
+
+ //updateRowWidgets( row1 );
+ //updateRowWidgets( row2 );
+ /*
+ if ( curRow == row1 )
+ curRow = row2;
+ else if ( curRow == row2 )
+ curRow = row1;
+ if ( editRow == row1 )
+ editRow = row2;
+ else if ( editRow == row2 )
+ editRow = row1;*/
+}
+
+//TODO this is incomplete/wrong
+void ConversionTable::swapColumns( int col1, int col2, bool /*swapHeader*/ )
+{
+ //if ( swapHeader )
+ //((TQTableHeader*)horizontalHeader())->swapSections( col1, col2, FALSE );
+
+ TQPtrVector<TQTableItem> tmpContents;
+ tmpContents.resize( numRows() );
+ TQPtrVector<TQWidget> tmpWidgets;
+ tmpWidgets.resize( numRows() );
+ int i;
+
+ items.setAutoDelete( FALSE );
+ widgets.setAutoDelete( FALSE );
+ for ( i = 0; i < numRows(); ++i ) {
+ TQTableItem *i1, *i2;
+ i1 = item( i, col1 );
+ i2 = item( i, col2 );
+ if ( i1 || i2 ) {
+ tmpContents.insert( i, i1 );
+ items.remove( indexOf( i, col1 ) );
+ items.insert( indexOf( i, col1 ), i2 );
+ items.remove( indexOf( i, col2 ) );
+ items.insert( indexOf( i, col2 ), tmpContents[ i ] );
+ if ( items[ indexOf( i, col1 ) ] )
+ items[ indexOf( i, col1 ) ] ->setCol( col1 );
+ if ( items[ indexOf( i, col2 ) ] )
+ items[ indexOf( i, col2 ) ] ->setCol( col2 );
+ }
+
+ TQWidget *w1, *w2;
+ w1 = cellWidget( i, col1 );
+ w2 = cellWidget( i, col2 );
+ if ( w1 || w2 ) {
+ tmpWidgets.insert( i, w1 );
+ widgets.remove( indexOf( i, col1 ) );
+ widgets.insert( indexOf( i, col1 ), w2 );
+ widgets.remove( indexOf( i, col2 ) );
+ widgets.insert( indexOf( i, col2 ), tmpWidgets[ i ] );
+ }
+ }
+ items.setAutoDelete( FALSE );
+ widgets.setAutoDelete( TRUE );
+
+ columnWidthChanged( col1 );
+ columnWidthChanged( col2 );
+ /*
+ if ( curCol == col1 )
+ curCol = col2;
+ else if ( curCol == col2 )
+ curCol = col1;
+ if ( editCol == col1 )
+ editCol = col2;
+ else if ( editCol == col2 )
+ editCol = col1;*/
+}
+
+//TODO this is incomplete/wrong
+void ConversionTable::swapCells( int row1, int col1, int row2, int col2 )
+{
+ items.setAutoDelete( FALSE );
+ widgets.setAutoDelete( FALSE );
+ TQTableItem *i1, *i2;
+ i1 = item( row1, col1 );
+ i2 = item( row2, col2 );
+ if ( i1 || i2 ) {
+ TQTableItem * tmp = i1;
+ items.remove( indexOf( row1, col1 ) );
+ items.insert( indexOf( row1, col1 ), i2 );
+ items.remove( indexOf( row2, col2 ) );
+ items.insert( indexOf( row2, col2 ), tmp );
+ if ( items[ indexOf( row1, col1 ) ] ) {
+ items[ indexOf( row1, col1 ) ] ->setRow( row1 );
+ items[ indexOf( row1, col1 ) ] ->setCol( col1 );
+ }
+ if ( items[ indexOf( row2, col2 ) ] ) {
+ items[ indexOf( row2, col2 ) ] ->setRow( row2 );
+ items[ indexOf( row2, col2 ) ] ->setCol( col2 );
+ }
+ }
+
+ TQWidget *w1, *w2;
+ w1 = cellWidget( row1, col1 );
+ w2 = cellWidget( row2, col2 );
+ if ( w1 || w2 ) {
+ TQWidget * tmp = w1;
+ widgets.remove( indexOf( row1, col1 ) );
+ widgets.insert( indexOf( row1, col1 ), w2 );
+ widgets.remove( indexOf( row2, col2 ) );
+ widgets.insert( indexOf( row2, col2 ), tmp );
+ }
+
+ //updateRowWidgets( row1 );
+ //updateRowWidgets( row2 );
+ //updateColWidgets( col1 );
+ //updateColWidgets( col2 );
+ items.setAutoDelete( FALSE );
+ widgets.setAutoDelete( TRUE );
+}
+
+#include "conversiontable.moc"
diff --git a/src/widgets/conversiontable.h b/src/widgets/conversiontable.h
new file mode 100644
index 0000000..16f761c
--- /dev/null
+++ b/src/widgets/conversiontable.h
@@ -0,0 +1,101 @@
+/***************************************************************************
+* Copyright (C) 2003-2004 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+#ifndef CONVERSIONTABLE_H
+#define CONVERSIONTABLE_H
+#include <tqstring.h>
+#include <tqtable.h>
+#include <tqobject.h>
+
+#include "datablocks/unitratio.h"
+#include "datablocks/elementlist.h"
+#include "datablocks/unit.h"
+
+/**
+@author Unai Garro
+*/
+
+
+class ConversionTable: public TQTable
+{
+ TQ_OBJECT
+public:
+
+ ConversionTable( TQWidget* parent, int maxrows, int maxcols );
+ ~ConversionTable();
+ void createNewItem( int r, int c, double amount );
+ void setUnitIDs( const IDList &idList );
+ void setRatio( int ingID1, int ingID2, double ratio );
+ void setRatio( const UnitRatio &r )
+ {
+ setRatio( r.uID1, r.uID2, r.ratio );
+ }
+ int getUnitID( int rc );
+ TQString text( int r, int c ) const; //Reimplement, otherwise it won't work this way
+ void resize( int r, int c );
+ void clear( void );
+private:
+
+ //Internal Variables
+ double editBoxValue;
+ TQIntDict<TQTableItem> items;
+ TQIntDict<TQWidget> widgets;
+ IDList unitIDs; // unit ID list to know the units by ID, not name
+ //Internal Methods
+ void resizeData( int )
+ {}
+ ;
+ TQTableItem *item( int r, int c ) const;
+ void setItem( int r, int c, TQTableItem *i );
+ void clearCell( int r, int c );
+ void takeItem( TQTableItem *item );
+ void insertWidget( int r, int c, TQWidget *w );
+ TQWidget *cellWidget( int r, int c ) const;
+ void clearCellWidget( int r, int c );
+ void initTable();
+ void swapRows( int, int, bool );
+ void swapColumns( int, int, bool );
+ void swapCells( int, int, int, int );
+protected:
+ TQWidget* beginEdit ( int row, int col, bool replace );
+
+private slots:
+ void repaintCell( int r, int c );
+
+ void unitRemoved( int );
+ void unitCreated( const Unit& );
+signals:
+ void ratioChanged( int row, int col, double value );
+ void ratioRemoved( int row, int col );
+};
+
+class ConversionTableItem: public TQObject, public TQTableItem
+{
+ TQ_OBJECT
+public:
+ ConversionTableItem( TQTable *t, EditType et );
+ TQWidget *createEditor() const;
+ void setContentFromEditor( TQWidget *w );
+ void setText( const TQString &s );
+ void paint( TQPainter *p, const TQColorGroup &cg, const TQRect &cr, bool selected );
+ void setTextAndSave( const TQString &s );
+ int alignment() const
+ {
+ return TQt::AlignRight;
+ }
+signals:
+ void ratioChanged( int row, int col, double value );
+ void ratioRemoved( int row, int col );
+ void signalRepaintCell( int r, int c );
+
+};
+
+
+#endif
diff --git a/src/widgets/criteriacombobox.cpp b/src/widgets/criteriacombobox.cpp
new file mode 100644
index 0000000..8fd911a
--- /dev/null
+++ b/src/widgets/criteriacombobox.cpp
@@ -0,0 +1,49 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "criteriacombobox.h"
+
+#include <tqlistbox.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/elementlist.h"
+
+CriteriaComboBox::CriteriaComboBox( bool b, TQWidget *parent, RecipeDB *db ) : KComboBox( b, parent ),
+ database( db )
+{
+ connect( db, TQ_SIGNAL(ratingCriteriaCreated(const Element &)), this, TQ_SLOT(addCriteria(const Element &)) );
+}
+
+void CriteriaComboBox::addCriteria( const Element &criteria )
+{
+ idMap.insert(count(),criteria.id);
+
+ insertItem(criteria.name);
+ completionObject()->addItem(criteria.name);
+}
+
+void CriteriaComboBox::reload()
+{
+ ElementList criteriaList;
+ database->loadRatingCriterion( &criteriaList );
+
+ clear();
+
+ for ( ElementList::const_iterator it = criteriaList.begin(); it != criteriaList.end(); ++it ) {
+ addCriteria((*it));
+ }
+}
+
+int CriteriaComboBox::criteriaID( int index )
+{
+ return idMap[index];
+}
+
+#include "criteriacombobox.moc"
diff --git a/src/widgets/criteriacombobox.h b/src/widgets/criteriacombobox.h
new file mode 100644
index 0000000..4e35bb4
--- /dev/null
+++ b/src/widgets/criteriacombobox.h
@@ -0,0 +1,41 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef CRITERIACOMBOBOX_H
+#define CRITERIACOMBOBOX_H
+
+#include <tqmap.h>
+
+#include <kcombobox.h>
+
+#include "datablocks/element.h"
+
+class RecipeDB;
+
+class CriteriaComboBox : public KComboBox
+{
+ TQ_OBJECT
+
+public:
+ CriteriaComboBox( bool, TQWidget *parent, RecipeDB *db );
+
+ void reload();
+ int criteriaID( int index );
+
+protected slots:
+ void addCriteria( const Element &criteria );
+
+private:
+ RecipeDB *database;
+ TQMap< int, int > idMap;
+};
+
+#endif //CRITERIACOMBOBOX_H
+
diff --git a/src/widgets/dblistviewbase.cpp b/src/widgets/dblistviewbase.cpp
new file mode 100644
index 0000000..4a22330
--- /dev/null
+++ b/src/widgets/dblistviewbase.cpp
@@ -0,0 +1,334 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "dblistviewbase.h"
+
+#include <tdeapplication.h>
+#include <tdeconfig.h>
+#include <kcursor.h>
+#include <kdebug.h>
+#include <tdeglobal.h>
+#include <tdelocale.h>
+#include <kprogress.h>
+
+
+//These two classes are used to identify the "Next" and "Prev" items, which are identified through rtti(). This also prevents renaming, even if it is enabled.
+class PrevListViewItem : public TQListViewItem
+{
+public:
+ PrevListViewItem( TQListView *parent ) : TQListViewItem(parent){}
+
+ virtual int rtti() const { return PREVLISTITEM_RTTI; }
+
+ TQString text( int c ) const {
+ if ( c == 0 ) {
+ return TQString("<< %1").arg(i18n("Previous"));
+ }
+ else
+ return TQString::null;
+ }
+};
+
+class NextListViewItem : public TQListViewItem
+{
+public:
+ NextListViewItem( TQListView *parent, TQListViewItem *after ) : TQListViewItem(parent,after){}
+
+ virtual int rtti() const { return NEXTLISTITEM_RTTI; }
+
+ TQString text( int c ) const {
+ if ( c == 0 ) {
+ return TQString("%1 >>").arg(i18n("Next"));
+ }
+ else
+ return TQString::null;
+ }
+};
+
+DBListViewBase::DBListViewBase( TQWidget *parent, RecipeDB *db, int t ) : TDEListView(parent),
+ database(db),
+ curr_limit(-1),
+ curr_offset(0),
+ total(t),
+ bulk_load(false),
+ delete_me_later(0),
+ m_progress(0),
+ m_totalSteps(0)
+{
+ setSorting(-1);
+
+ if ( curr_limit == -1 ) { //only use the default limit if a subclass hasn't given curr_limit its own value
+ TDEConfig * config = TDEGlobal::config();config->setGroup( "Performance" );
+ curr_limit = config->readNumEntry( "Limit", -1 );
+ }
+
+ connect(this,TQ_SIGNAL(executed(TQListViewItem*)),TQ_SLOT(slotDoubleClicked(TQListViewItem*)));
+}
+
+DBListViewBase::~DBListViewBase()
+{
+ delete m_progress;
+}
+
+void DBListViewBase::activatePrev()
+{
+ if ( curr_offset != 0 ) {
+ curr_offset -= curr_limit;
+ if ( curr_offset < 0 )
+ curr_offset = 0;
+
+ reload(ForceReload);
+ emit prevGroupLoaded();
+ }
+}
+
+void DBListViewBase::activateNext()
+{
+ curr_offset += curr_limit;
+
+ reload(ForceReload);
+ emit nextGroupLoaded();
+}
+
+void DBListViewBase::rename( TQListViewItem *it, int c )
+{
+ if ( it->rtti() == PREVLISTITEM_RTTI || it->rtti() == NEXTLISTITEM_RTTI ) {
+ return;
+ }
+
+ TDEListView::rename(it,c);
+}
+
+void DBListViewBase::slotDoubleClicked( TQListViewItem *it )
+{
+ //we can't delete the item the was double clicked
+ //and yet these functions will clear() the listview.
+ //We'll take the item from the view so it isn't deleted
+ //and delete it ourselves.
+ delete delete_me_later; delete_me_later = 0;
+
+ if ( it->rtti() == PREVLISTITEM_RTTI ) {
+ delete_me_later = it;
+ takeItem(it);
+ activatePrev();
+ }
+ else if ( it->rtti() == NEXTLISTITEM_RTTI ) {
+ delete_me_later = it;
+ takeItem(it);
+ activateNext();
+ }
+}
+
+void DBListViewBase::keyPressEvent( TQKeyEvent *k )
+{
+ if ( k->state() == TQt::ShiftButton ) {
+ switch ( k->key() ) {
+ case TQt::Key_N: {
+ if ( curr_offset + curr_limit >= total || curr_limit == -1 ) {
+ k->accept();
+ return;
+ }
+
+ kapp->processEvents(); //if auto-repeating, user won't otherwise see change in the listview
+ activateNext();
+ k->accept();
+ break;
+ }
+ case TQt::Key_P: {
+ kapp->processEvents(); //if auto-repeating, user won't otherwise see change in the listview
+ activatePrev();
+ k->accept();
+ break;
+ }
+ default: break;
+ }
+ }
+
+ TDEListView::keyPressEvent(k);
+}
+
+void DBListViewBase::reload( ReloadFlags flag )
+{
+ if ( flag == ForceReload || (!firstChild() && flag == Load) || (firstChild() && flag == ReloadIfPopulated) ) {
+ TDEApplication::setOverrideCursor( KCursor::waitCursor() );
+
+ init();
+
+ //m_progress = new KProgressDialog(this,0,TQString::null,i18n("Loading..."),true);
+ //m_progress->setAllowCancel(false);
+ //m_progress->progressBar()->setPercentageVisible(false);
+ //m_progress->progressBar()->setTotalSteps( m_totalSteps );
+ //m_progress->show();
+ //kapp->processEvents();
+
+ //reset some things
+ clear();
+ lastElementMap.clear();
+
+ bulk_load=true;
+ load(curr_limit,curr_offset);
+ bulk_load=false;
+
+ if ( curr_limit != -1 && curr_offset + curr_limit < total )
+ new NextListViewItem(this,lastElementMap[0]);
+
+ if ( curr_offset != 0 )
+ new PrevListViewItem(this);
+
+ //delete m_progress; m_progress = 0;
+
+ TDEApplication::restoreOverrideCursor();
+ }
+}
+
+void DBListViewBase::setTotalItems(int i)
+{
+ m_totalSteps = i;
+ if ( m_progress ) {
+ m_progress->progressBar()->setTotalSteps( m_totalSteps );
+ }
+}
+
+void DBListViewBase::createElement( TQListViewItem *it )
+{
+ Q_ASSERT(it);
+
+ TQListViewItem *lastElement;
+ TQMap<TQListViewItem*,TQListViewItem*>::iterator map_it = lastElementMap.find(it->parent());
+ if ( map_it != lastElementMap.end() ) {
+ lastElement = map_it.data();
+ }
+ else
+ lastElement = 0;
+
+ if ( bulk_load ) { //this can be much faster if we know the elements are already in order
+ if ( lastElement ) it->moveItem(lastElement);
+ lastElementMap.insert(it->parent(),it);
+ if ( m_progress ) { m_progress->progressBar()->advance(1); }
+ }
+ else {
+ if ( lastElement == 0 ) {
+ lastElementMap.insert(it->parent(),it);
+ }
+ else {
+
+ int c = 0;//FIXME: the column used should be variable (set by a subclass)
+
+ if ( it->parent() == 0 ) {
+ //start it out below the "Prev" item... currently it will be at firstChild()
+ if ( firstChild()->nextSibling() &&
+ ( firstChild()->nextSibling()->rtti() == PREVLISTITEM_RTTI ||
+ firstChild()->nextSibling()->rtti() == 1006 ) ) { //A hack to skip the Uncategorized item
+ it->moveItem( firstChild()->nextSibling() );
+ }
+ }
+
+ if ( TQString::localeAwareCompare(it->text(c),lastElement->text(c)) >= 0 ) {
+ it->moveItem(lastElement);
+ lastElementMap.insert(it->parent(),it);
+ }
+ else {
+ TQListViewItem *last_it = 0;
+
+ for ( TQListViewItem *search_it = it; search_it; search_it = search_it->nextSibling() ) {
+ if ( search_it->rtti() == NEXTLISTITEM_RTTI ) {
+ it->moveItem(lastElement);
+ lastElementMap.insert(it->parent(),it);
+ }
+ else if ( TQString::localeAwareCompare(it->text(c),search_it->text(c)) < 0 ) { //we assume the list is sorted, as it should stay
+ if ( last_it ) it->moveItem(last_it);
+ break;
+ }
+ last_it = search_it;
+ }
+ }
+ }
+ }
+}
+
+void DBListViewBase::removeElement( TQListViewItem *it, bool delete_item )
+{
+ total--;
+ if ( !it ) return;
+
+ TQListViewItem *lastElement = lastElementMap[it->parent()];
+ if ( it == lastElement ) {
+ for ( TQListViewItem *search_it = (it->parent())?it->parent()->firstChild():firstChild(); search_it->nextSibling(); search_it = search_it->nextSibling() ) {
+ if ( it == search_it->nextSibling() ) {
+ lastElementMap.insert(it->parent(),search_it);
+ lastElement = search_it;
+ break;
+ }
+ }
+
+ if ( lastElement == it || lastElement->rtti() == PREVLISTITEM_RTTI ) { //there are no more items in the view if this happens
+ if ( firstChild() && firstChild()->rtti() == PREVLISTITEM_RTTI ) {
+ activatePrev();
+ it = 0; //keep 'delete it' below from segfault'ing
+ }
+ else if ( lastElement->nextSibling() && lastElement->nextSibling()->rtti() == NEXTLISTITEM_RTTI ) {
+ reload();
+ it = 0; //keep 'delete it' below from segfault'ing
+ }
+ else //the list is now empty, there is no last element
+ lastElementMap.remove(it->parent());
+ }
+ }
+
+ if ( delete_item )
+ delete it;
+}
+
+bool DBListViewBase::handleElement( const TQString &name )
+{
+ total++;
+
+ TQListViewItem *lastElement = lastElementMap[0];
+
+ int c = 0;//FIXME: the column used should be variable (set by a subclass)
+
+ int child_count = childCount();
+ if ( child_count == 0 ) return true;
+
+ if ( firstChild()->rtti() == PREVLISTITEM_RTTI || firstChild()->rtti() == 1006 ){ child_count--; } //"Prev" item
+ if ( child_count == 0 ) return true;
+
+ if ( lastElement->nextSibling() ){ child_count--; } //"Next" item
+
+ if ( curr_limit != -1 && child_count >= curr_limit ) {
+ TQListViewItem *firstElement = firstChild();
+ if (firstElement->rtti() == PREVLISTITEM_RTTI || firstElement->rtti() == 1006 ) {
+ firstElement = firstElement->nextSibling();
+ }
+ else if ( name < firstElement->text(c) ) { //provide access to this new element if we need to
+ new PrevListViewItem(this);
+ curr_offset++;
+ return false;
+ }
+
+ if ( name < firstElement->text(c) ) {
+ curr_offset++;
+ return false;
+ }
+ else if ( name >= lastElement->text(c) ) {
+ if ( lastElement->nextSibling() == 0 )
+ new NextListViewItem(this,lastElement);
+
+ return false;
+ }
+ else {
+ removeElement(lastElement);
+ }
+ }
+
+ return true;
+}
+
+#include "dblistviewbase.moc"
diff --git a/src/widgets/dblistviewbase.h b/src/widgets/dblistviewbase.h
new file mode 100644
index 0000000..d1514fe
--- /dev/null
+++ b/src/widgets/dblistviewbase.h
@@ -0,0 +1,89 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef LISTVIEWHANDLER_H
+#define LISTVIEWHANDLER_H
+
+#include <tqobject.h>
+#include <tqmap.h>
+
+#include <tdelistview.h>
+
+#define PREVLISTITEM_RTTI 1002
+#define NEXTLISTITEM_RTTI 1003
+
+class KProgressDialog;
+
+class RecipeDB;
+
+enum ReloadFlags {
+ Load, /** Only performs the reload if the list hasn't already been loaded */
+ ReloadIfPopulated, /** Only performs the reload if the list has been loaded */
+ ForceReload /** Load/reload the list regardless of whether or not it's been loaded */
+};
+
+class DBListViewBase : public TDEListView
+{
+TQ_OBJECT
+
+public:
+ DBListViewBase( TQWidget *, RecipeDB *, int total );
+ ~DBListViewBase();
+
+ void reload( ReloadFlags flag = Load );
+
+signals:
+ void nextGroupLoaded();
+ void prevGroupLoaded();
+
+protected:
+ /**
+ * Called when the list view is ready to be used, i.e., it has been loaded with data.
+ * Until the list view has been loaded, we can ignore all database signals regarding changes
+ * of data. Therefore, subclasses should connect to these signals during this call.
+ */
+ virtual void init(){}
+ virtual void load(int limit, int offset) = 0;
+ virtual void keyPressEvent( TQKeyEvent *e );
+ bool handleElement( const TQString & );
+ virtual void createElement( TQListViewItem * );
+ void removeElement( TQListViewItem *, bool delete_item = true );
+
+ bool reloading(){ return bulk_load; }
+ void setSorting(int c){TDEListView::setSorting(c);} //don't do sorting, the database comes sorted from the database anyways
+ void setTotalItems(int);
+
+ RecipeDB *database;
+ int curr_limit;
+ int curr_offset;
+
+protected slots:
+ void rename( TQListViewItem *, int c );
+ void slotDoubleClicked( TQListViewItem * );
+
+private:
+ void activatePrev();
+ void activateNext();
+
+ //make this private because the data should always be synced with the database
+ void clear(){TDEListView::clear();}
+
+ int total;
+
+ bool bulk_load;
+
+ TQMap<TQListViewItem*,TQListViewItem*> lastElementMap;
+ TQListViewItem *delete_me_later;
+
+ KProgressDialog *m_progress;
+ int m_totalSteps;
+};
+
+#endif //LISTVIEWHANDLER_H
diff --git a/src/widgets/fractioninput.cpp b/src/widgets/fractioninput.cpp
new file mode 100644
index 0000000..68a3cfd
--- /dev/null
+++ b/src/widgets/fractioninput.cpp
@@ -0,0 +1,121 @@
+/***************************************************************************
+* Copyright (C) 2003 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "fractioninput.h"
+
+#include <tqtimer.h>
+
+#include <tdeglobalsettings.h>
+
+#include "datablocks/ingredient.h"
+
+FractionInput::FractionInput( TQWidget *parent, MixedNumber::Format format ) : KLineEdit( parent ),
+ m_allowRange(false),
+ m_validateTimer(new TQTimer(this)),
+ m_format(format)
+{
+ setAlignment( TQt::AlignRight );
+
+ connect( this, TQ_SIGNAL(textChanged(const TQString&)), this, TQ_SLOT(slotStartValidateTimer()) );
+ connect( m_validateTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(validate()) );
+}
+
+FractionInput::~FractionInput()
+{
+ delete m_validateTimer;
+}
+
+void FractionInput::setValue( double d, double amount_offset )
+{
+ MixedNumber m( d );
+ setValue( m, amount_offset );
+}
+
+void FractionInput::setValue( const MixedNumber &m, double amount_offset )
+{
+ TQString text = m.toString( m_format );
+ if ( amount_offset > 0 ) {
+ text += "-" + MixedNumber(m+amount_offset).toString( MixedNumber::MixedNumberFormat );
+ }
+ setText(text);
+}
+
+void FractionInput::value( MixedNumber &amount, double &amount_offset ) const
+{
+ Ingredient i; i.setAmount( text() );
+
+ amount = MixedNumber(i.amount);
+ amount_offset = i.amount_offset;
+}
+
+void FractionInput::value( double &amount, double &amount_offset ) const
+{
+ Ingredient i; i.setAmount( text() );
+
+ amount = i.amount;
+ amount_offset = i.amount_offset;
+}
+
+MixedNumber FractionInput::value() const
+{
+ Ingredient i; i.setAmount( text() );
+
+ return MixedNumber(i.amount);
+}
+
+MixedNumber FractionInput::minValue() const
+{
+ Ingredient i; i.setAmount( text() );
+
+ return MixedNumber(i.amount);
+}
+
+MixedNumber FractionInput::maxValue() const
+{
+ Ingredient i; i.setAmount( text() );
+
+ return MixedNumber(i.amount_offset+i.amount);
+}
+
+bool FractionInput::isInputValid() const
+{
+ if ( !m_allowRange && text().contains("-") )
+ return false;
+
+ bool ok;
+ Ingredient i; i.setAmount( text(), &ok );
+
+ return ok;
+}
+
+void FractionInput::slotStartValidateTimer()
+{
+ if ( !m_validateTimer->isActive() )
+ m_validateTimer->start( 1000, true );
+
+ if ( isInputValid() )
+ emit valueChanged( value() );
+}
+
+void FractionInput::validate()
+{
+ if ( isInputValid() ) {
+ setPaletteForegroundColor( TDEGlobalSettings::textColor() );
+ }
+ else
+ setPaletteForegroundColor( TQt::red );
+}
+
+bool FractionInput::isEmpty() const
+{
+ return text().isEmpty();
+}
+
+#include "fractioninput.moc"
diff --git a/src/widgets/fractioninput.h b/src/widgets/fractioninput.h
new file mode 100644
index 0000000..b682a32
--- /dev/null
+++ b/src/widgets/fractioninput.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+* Copyright (C) 2003 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef FRACTIONINPUT_H
+#define FRACTIONINPUT_H
+
+#include <klineedit.h>
+
+#include "datablocks/mixednumber.h"
+
+class TQTimer;
+
+/** A KLineEdit widget extended to allow input of decimals and fractions or ranges of such.
+ * Input is returned as a @ref MixedNumber class.
+ * @author Jason Kivlighn
+ */
+class FractionInput : public KLineEdit
+{
+TQ_OBJECT
+
+public:
+ FractionInput( TQWidget *parent = 0, MixedNumber::Format = MixedNumber::MixedNumberFormat );
+ ~FractionInput();
+
+ void setAllowRange( bool b ){ m_allowRange = b; }
+
+ void setValue( double amount, double amount_offset );
+ void setValue( const MixedNumber &, double amount_offset );
+
+ void value( MixedNumber &amount, double &amount_offset ) const;
+ void value( double &amount, double &amount_offset ) const;
+ MixedNumber minValue() const;
+ MixedNumber maxValue() const;
+ MixedNumber value() const;
+
+ bool isInputValid() const;
+ bool isEmpty() const;
+
+signals:
+ void valueChanged( const MixedNumber & );
+
+public slots:
+ void validate();
+
+private slots:
+ void slotStartValidateTimer();
+
+private:
+ bool m_allowRange;
+ TQTimer *m_validateTimer;
+ MixedNumber::Format m_format;
+};
+
+#endif //FRACTIONINPUT_H
+
diff --git a/src/widgets/headercombobox.cpp b/src/widgets/headercombobox.cpp
new file mode 100644
index 0000000..3c43cbc
--- /dev/null
+++ b/src/widgets/headercombobox.cpp
@@ -0,0 +1,42 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "headercombobox.h"
+
+#include <tqlistbox.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/elementlist.h"
+
+HeaderComboBox::HeaderComboBox( bool b, TQWidget *parent, RecipeDB *db ) : KComboBox( b, parent ),
+ database( db )
+{
+}
+
+void HeaderComboBox::reload()
+{
+ TQString remember_text = currentText();
+
+ ElementList headerList;
+ database->loadIngredientGroups( &headerList );
+
+ clear();
+
+ for ( ElementList::const_iterator it = headerList.begin(); it != headerList.end(); ++it ) {
+ insertItem((*it).name);
+ completionObject()->addItem((*it).name);
+ }
+
+ if ( listBox()->findItem( remember_text, TQt::ExactMatch ) ) {
+ setCurrentText( remember_text );
+ }
+}
+
+#include "headercombobox.moc"
diff --git a/src/widgets/headercombobox.h b/src/widgets/headercombobox.h
new file mode 100644
index 0000000..5e07b20
--- /dev/null
+++ b/src/widgets/headercombobox.h
@@ -0,0 +1,34 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef HEADERCOMBOBOX_H
+#define HEADERCOMBOBOX_H
+
+#include <kcombobox.h>
+
+#include "datablocks/element.h"
+
+class RecipeDB;
+
+class HeaderComboBox : public KComboBox
+{
+ TQ_OBJECT
+
+public:
+ HeaderComboBox( bool, TQWidget *parent, RecipeDB *db );
+
+ void reload();
+
+private:
+ RecipeDB *database;
+};
+
+#endif //HEADERCOMBOBOX_H
+
diff --git a/src/widgets/headerlistview.cpp b/src/widgets/headerlistview.cpp
new file mode 100644
index 0000000..51e9d4c
--- /dev/null
+++ b/src/widgets/headerlistview.cpp
@@ -0,0 +1,196 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "headerlistview.h"
+
+#include <tdemessagebox.h>
+#include <tdeconfig.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kiconloader.h>
+#include <tdepopupmenu.h>
+
+#include "backends/recipedb.h"
+#include "dialogs/createelementdialog.h"
+#include "dialogs/dependanciesdialog.h"
+
+HeaderListView::HeaderListView( TQWidget *parent, RecipeDB *db ) : DBListViewBase( parent,db,db->unitCount() )
+{
+ setAllColumnsShowFocus( true );
+ setDefaultRenameAction( TQListView::Reject );
+}
+
+void HeaderListView::init()
+{
+ connect( database, TQ_SIGNAL( ingGroupCreated( const Element & ) ), TQ_SLOT( checkCreateHeader( const Element & ) ) );
+ connect( database, TQ_SIGNAL( ingGroupRemoved( int ) ), TQ_SLOT( removeHeader( int ) ) );
+}
+
+void HeaderListView::load( int /*limit*/, int /*offset*/ )
+{
+ ElementList headerList;
+ database->loadIngredientGroups( &headerList );
+
+ setTotalItems(headerList.count());
+
+ for ( ElementList::const_iterator it = headerList.begin(); it != headerList.end(); ++it ) {
+ createHeader( *it );
+ }
+}
+
+void HeaderListView::checkCreateHeader( const Element &el )
+{
+ if ( handleElement(el.name) ) { //only create this header if the base class okays it
+ createHeader(el);
+ }
+}
+
+
+StdHeaderListView::StdHeaderListView( TQWidget *parent, RecipeDB *db, bool editable ) : HeaderListView( parent, db )
+{
+ addColumn( i18n( "Header" ) );
+
+ TDEConfig * config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ if ( editable ) {
+ setRenameable( 0, true );
+
+ TDEIconLoader *il = new TDEIconLoader;
+
+ kpop = new TDEPopupMenu( this );
+ kpop->insertItem( il->loadIcon( "document-new", TDEIcon::NoGroup, 16 ), i18n( "&Create" ), this, TQ_SLOT( createNew() ), CTRL + Key_C );
+ kpop->insertItem( il->loadIcon( "edit-delete", TDEIcon::NoGroup, 16 ), i18n( "&Delete" ), this, TQ_SLOT( remove
+ () ), Key_Delete );
+ kpop->insertItem( il->loadIcon( "edit", TDEIcon::NoGroup, 16 ), i18n( "&Rename" ), this, TQ_SLOT( rename() ), CTRL + Key_R );
+ kpop->polish();
+
+ delete il;
+
+ connect( this, TQ_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ), TQ_SLOT( showPopup( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
+ connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem*, const TQPoint &, int ) ), this, TQ_SLOT( modHeader( TQListViewItem*, const TQPoint &, int ) ) );
+ connect( this, TQ_SIGNAL( itemRenamed( TQListViewItem*, const TQString &, int ) ), this, TQ_SLOT( saveHeader( TQListViewItem*, const TQString &, int ) ) );
+ }
+}
+
+void StdHeaderListView::showPopup( TDEListView * /*l*/, TQListViewItem *i, const TQPoint &p )
+{
+ if ( i )
+ kpop->exec( p );
+}
+
+void StdHeaderListView::createNew()
+{
+ CreateElementDialog * headerDialog = new CreateElementDialog( this, i18n("Header") );
+
+ if ( headerDialog->exec() == TQDialog::Accepted ) {
+ TQString result = headerDialog->newElementName();
+
+ //check bounds first
+ if ( checkBounds( result ) )
+ database->createNewIngGroup( result );
+ }
+ delete headerDialog;
+}
+
+void StdHeaderListView::remove()
+{
+ // Find selected header item
+ TQListViewItem * it = selectedItem();
+
+ if ( it ) {
+ int headerID = it->text( 1 ).toInt();
+
+ ElementList recipeDependancies;
+ database->findUseOfIngGroupInRecipes( &recipeDependancies, headerID );
+
+ if ( recipeDependancies.isEmpty() )
+ database->removeIngredientGroup( headerID );
+ else { // need warning!
+ ListInfo info;
+ info.list = recipeDependancies;
+ info.name = i18n( "Recipes" );
+
+ DependanciesDialog warnDialog( this, info );
+ warnDialog.setCustomWarning( i18n("You are about to permanantly delete recipes from your database.") );
+ if ( warnDialog.exec() == TQDialog::Accepted )
+ database->removeIngredientGroup( headerID );
+ }
+ }
+}
+
+void StdHeaderListView::rename()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item )
+ HeaderListView::rename( item, 0 );
+}
+
+void StdHeaderListView::createHeader( const Element &header )
+{
+ createElement(new TQListViewItem( this, header.name, TQString::number( header.id ) ));
+}
+
+void StdHeaderListView::removeHeader( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 1 );
+ removeElement(item);
+}
+
+void StdHeaderListView::modHeader( TQListViewItem* i, const TQPoint & /*p*/, int c )
+{
+ if ( i )
+ HeaderListView::rename( i, c );
+}
+
+void StdHeaderListView::saveHeader( TQListViewItem* i, const TQString &text, int /*c*/ )
+{
+ if ( !checkBounds( text ) ) {
+ reload(ForceReload); //reset the changed text
+ return ;
+ }
+
+ int existing_id = database->findExistingIngredientGroupByName( text );
+ int header_id = i->text( 1 ).toInt();
+ if ( existing_id != -1 && existing_id != header_id ) { //already exists with this label... merge the two
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "This header already exists. Continuing will merge these two headers into one. Are you sure?" ) ) ) {
+ case KMessageBox::Continue: {
+ database->modIngredientGroup( header_id, i->text( 0 ) );
+ database->mergeIngredientGroups( header_id, existing_id );
+ break;
+ }
+ default:
+ reload(ForceReload);
+ break;
+ }
+ }
+ else {
+ database->modIngredientGroup( header_id, i->text( 0 ) );
+ }
+}
+
+bool StdHeaderListView::checkBounds( const TQString &header )
+{
+ if ( header.length() > uint(database->maxIngGroupNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Header cannot be longer than %1 characters." ) ).arg( database->maxIngGroupNameLength() ) );
+ return false;
+ }
+ else if ( header.stripWhiteSpace().isEmpty() )
+ return false;
+
+ return true;
+}
+
+#include "headerlistview.moc"
diff --git a/src/widgets/headerlistview.h b/src/widgets/headerlistview.h
new file mode 100644
index 0000000..716d002
--- /dev/null
+++ b/src/widgets/headerlistview.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef HEADERLISTVIEW_H
+#define HEADERLISTVIEW_H
+
+#include "dblistviewbase.h"
+
+#include "datablocks/element.h"
+
+class RecipeDB;
+class TDEPopupMenu;
+
+class HeaderListView : public DBListViewBase
+{
+ TQ_OBJECT
+
+public:
+ HeaderListView( TQWidget *parent, RecipeDB *db );
+
+public slots:
+ virtual void load( int curr_limit, int curr_offset );
+
+protected slots:
+ virtual void createHeader( const Element & ) = 0;
+ virtual void removeHeader( int ) = 0;
+
+ void checkCreateHeader( const Element &el );
+
+protected:
+ virtual void init();
+};
+
+class StdHeaderListView : public HeaderListView
+{
+ TQ_OBJECT
+
+public:
+ StdHeaderListView( TQWidget *parent, RecipeDB *db, bool editable = false );
+
+protected:
+ virtual void createHeader( const Element & );
+ virtual void removeHeader( int );
+
+private slots:
+ void showPopup( TDEListView *, TQListViewItem *, const TQPoint & );
+
+ void createNew();
+ void remove();
+ void rename();
+
+ void modHeader( TQListViewItem* i, const TQPoint &p, int c );
+ void saveHeader( TQListViewItem* i, const TQString &text, int c );
+
+private:
+ bool checkBounds( const TQString &unit );
+
+ TDEPopupMenu *kpop;
+};
+
+#endif //HEADERLISTVIEW_H
diff --git a/src/widgets/inglistviewitem.cpp b/src/widgets/inglistviewitem.cpp
new file mode 100644
index 0000000..0670dc4
--- /dev/null
+++ b/src/widgets/inglistviewitem.cpp
@@ -0,0 +1,225 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "inglistviewitem.h"
+
+#include <tdelocale.h>
+#include <tdeconfig.h>
+#include <tdeglobal.h>
+#include <kdebug.h>
+
+#include "datablocks/unit.h"
+#include "datablocks/mixednumber.h"
+
+IngSubListViewItem::IngSubListViewItem( TQListViewItem* qli, const Ingredient &i ) : IngListViewItem( qli, 0, i )
+{
+}
+
+TQString IngSubListViewItem::text( int column ) const
+{
+ if ( column == 0 ) {
+ //kdDebug()<<"displaying col 0 for "<<m_ing.name<<endl;
+ return TQString("%1 ").arg(i18n("OR"))+m_ing.name;
+ //return m_ing.name;
+ }
+ else
+ return IngListViewItem::text(column);
+
+}
+
+void IngSubListViewItem::setText( int column, const TQString &text )
+{
+ switch ( column ) {
+ case 0: {
+ TQString compare = TQString("%1 ").arg(i18n("OR"));
+ if ( text.left(compare.length()) == compare )
+ m_ing.name = text.right(text.length()-compare.length());
+ else
+ m_ing.name = text;
+ break;
+ }
+ default:
+ IngListViewItem::setText(column,text);
+ break;
+ }
+}
+
+int IngSubListViewItem::rtti() const
+{
+ return INGSUBLISTVIEWITEM_RTTI;
+}
+
+
+IngListViewItem::IngListViewItem( TQListView* qlv, const Ingredient &i ) : TQListViewItem( qlv )
+{
+ init( i );
+}
+
+IngListViewItem::IngListViewItem( TQListView* qlv, TQListViewItem *after, const Ingredient &i ) : TQListViewItem( qlv, after )
+{
+ init( i );
+}
+
+IngListViewItem::IngListViewItem( TQListViewItem* qli, TQListViewItem *after, const Ingredient &i ) : TQListViewItem( qli, after )
+{
+ init( i );
+}
+
+int IngListViewItem::rtti() const
+{
+ return INGLISTVIEWITEM_RTTI;
+}
+
+Ingredient IngListViewItem::ingredient() const
+{
+ return m_ing;
+}
+
+void IngListViewItem::setAmount( double amount, double amount_offset )
+{
+ amount_str = TQString::null;
+
+ if ( amount > 0 ) {
+ TDEConfig * config = TDEGlobal::config();
+ config->setGroup( "Formatting" );
+
+ if ( config->readBoolEntry( "Fraction" ) )
+ amount_str = MixedNumber( amount ).toString();
+ else
+ amount_str = beautify( TDEGlobal::locale() ->formatNumber( amount, 5 ) );
+ }
+ if ( amount_offset > 0 ) {
+ if ( amount < 1e-10 )
+ amount_str += "0";
+ amount_str += "-" + MixedNumber(amount+amount_offset).toString();
+ }
+
+ m_ing.amount = amount;
+ m_ing.amount_offset = amount_offset;
+
+ //FIXME: make sure the right unit is showing after changing this (force a repaint... repaint() doesn't do the job right because it gets caught in a loop)
+}
+
+void IngListViewItem::setUnit( const Unit &unit )
+{
+ //### This shouldn't be necessary... the db backend should ensure this doesn't happen
+ if ( !unit.name.isEmpty() )
+ m_ing.units.name = unit.name;
+ if ( !unit.plural.isEmpty() )
+ m_ing.units.plural = unit.plural;
+}
+
+void IngListViewItem::setPrepMethod( const TQString &prepMethod )
+{
+ m_ing.prepMethodList = ElementList::split(",",prepMethod);
+}
+
+void IngListViewItem::setText( int column, const TQString &text )
+{
+ switch ( column ) {
+ case 0: {
+ m_ing.name = text;
+ break;
+ }
+ case 1: {
+ Ingredient i; i.setAmount(text);
+ setAmount( i.amount, i.amount_offset );
+ break;
+ }
+ case 2:
+ setUnit( Unit( text, m_ing.amount+m_ing.amount_offset ) );
+ break;
+ case 3:
+ setPrepMethod( text );
+ break;
+ default:
+ break;
+ }
+}
+
+TQString IngListViewItem::text( int column ) const
+{
+ switch ( column ) {
+ case 0:
+ return m_ing.name;
+ break;
+ case 1:
+ return amount_str;
+ break;
+ case 2:
+ return ( m_ing.amount+m_ing.amount_offset > 1 ) ? m_ing.units.plural : m_ing.units.name;
+ break;
+ case 3:
+ return m_ing.prepMethodList.join(",");
+ break;
+ default:
+ return ( TQString::null );
+ }
+}
+
+void IngListViewItem::init( const Ingredient &i )
+{
+ m_ing = i;
+
+ setAmount( i.amount, i.amount_offset );
+}
+
+
+IngGrpListViewItem::IngGrpListViewItem( TQListView* qlv, TQListViewItem *after, const TQString &group, int id ) : TQListViewItem( qlv, after )
+{
+ init( group, id );
+}
+
+int IngGrpListViewItem::rtti() const
+{
+ return INGGRPLISTVIEWITEM_RTTI;
+}
+
+TQString IngGrpListViewItem::group() const
+{
+ return m_group;
+}
+
+int IngGrpListViewItem::id() const
+{
+ return m_id;
+}
+
+TQString IngGrpListViewItem::text( int column ) const
+{
+ switch ( column ) {
+ case 0:
+ return m_group + ":";
+ break;
+ default:
+ return ( TQString::null );
+ }
+}
+
+void IngGrpListViewItem::setText( int column, const TQString &text )
+{
+ switch ( column ) {
+ case 0:
+ if ( text.right(1) == ":" )
+ m_group = text.left(text.length()-1);
+ else
+ m_group = text;
+ break;
+ default:
+ break;
+ }
+}
+
+void IngGrpListViewItem::init( const TQString &group, int id )
+{
+ m_group = group;
+ m_id = id;
+}
+
diff --git a/src/widgets/inglistviewitem.h b/src/widgets/inglistviewitem.h
new file mode 100644
index 0000000..89de283
--- /dev/null
+++ b/src/widgets/inglistviewitem.h
@@ -0,0 +1,82 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef INGLISTVIEWITEM_H
+#define INGLISTVIEWITEM_H
+
+#include "tqlistview.h"
+
+#include "datablocks/ingredient.h"
+
+#define INGGRPLISTVIEWITEM_RTTI 1003
+#define INGLISTVIEWITEM_RTTI 1004
+#define INGSUBLISTVIEWITEM_RTTI 1009
+
+class IngListViewItem : public TQListViewItem
+{
+public:
+ IngListViewItem( TQListView* qlv, const Ingredient &i );
+ IngListViewItem( TQListView* qlv, TQListViewItem *after, const Ingredient &i );
+ IngListViewItem( TQListViewItem* qli, TQListViewItem *after, const Ingredient &i );
+
+ int rtti() const;
+
+ Ingredient ingredient() const;
+
+ void setAmount( double amount, double amount_offset );
+ void setUnit( const Unit &unit );
+ void setPrepMethod( const TQString &prepMethod );
+
+protected:
+ Ingredient m_ing;
+ TQString amount_str;
+
+public:
+ virtual TQString text( int column ) const;
+ virtual void setText( int column, const TQString &text );
+
+private:
+ void init( const Ingredient &i );
+};
+
+
+class IngSubListViewItem : public IngListViewItem
+{
+public:
+ IngSubListViewItem( TQListViewItem* qli, const Ingredient &i );
+
+ virtual TQString text( int column ) const;
+ virtual void setText( int column, const TQString &text );
+ virtual int rtti() const;
+};
+
+
+class IngGrpListViewItem : public TQListViewItem
+{
+public:
+ IngGrpListViewItem( TQListView* qlv, TQListViewItem *after, const TQString &group, int id );
+
+ int rtti() const;
+
+ TQString group() const;
+ int id() const;
+
+ virtual TQString text( int column ) const;
+ virtual void setText( int column, const TQString &text );
+
+protected:
+ TQString m_group;
+ int m_id;
+
+private:
+ void init( const TQString &group, int id );
+};
+
+#endif
diff --git a/src/widgets/ingredientcombobox.cpp b/src/widgets/ingredientcombobox.cpp
new file mode 100644
index 0000000..0e036b0
--- /dev/null
+++ b/src/widgets/ingredientcombobox.cpp
@@ -0,0 +1,188 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "ingredientcombobox.h"
+
+#include <tqlistbox.h>
+#include <tqtimer.h>
+
+#include <kdebug.h>
+#include <tdeapplication.h>
+#include <tdeglobal.h>
+#include <tdeconfig.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/elementlist.h"
+
+IngredientComboBox::IngredientComboBox( bool b, TQWidget *parent, RecipeDB *db, const TQString &specialItem ) : KComboBox( b, parent ),
+ database( db ), loading_at(0), load_timer(new TQTimer(this)), m_specialItem(specialItem)
+{
+ connect( load_timer, TQ_SIGNAL(timeout()), TQ_SLOT(loadMore()) );
+ completionObject()->setIgnoreCase(true);
+}
+
+void IngredientComboBox::reload()
+{
+ TQString remember_text;
+ if ( editable() )
+ remember_text = lineEdit()->text();
+
+ ElementList ingredientList;
+ database->loadIngredients( &ingredientList );
+
+ clear();
+ ingredientComboRows.clear();
+
+ int row = 0;
+ if ( !m_specialItem.isNull() ) {
+ insertItem(m_specialItem);
+ ingredientComboRows.insert( row, -1 );
+ row++;
+ }
+ for ( ElementList::const_iterator it = ingredientList.begin(); it != ingredientList.end(); ++it, ++row ) {
+ insertItem((*it).name);
+ completionObject()->addItem((*it).name);
+ ingredientComboRows.insert( row, (*it).id );
+ }
+
+ if ( editable() )
+ setEditText( remember_text );
+
+ database->disconnect( this );
+ connect( database, TQ_SIGNAL( ingredientCreated( const Element & ) ), TQ_SLOT( createIngredient( const Element & ) ) );
+ connect( database, TQ_SIGNAL( ingredientRemoved( int ) ), TQ_SLOT( removeIngredient( int ) ) );
+}
+
+void IngredientComboBox::loadMore()
+{
+ if ( loading_at >= ing_count-1 ) {
+ endLoad();
+ return;
+ }
+
+ ElementList ingredientList;
+ database->loadIngredients( &ingredientList, load_limit, loading_at );
+
+ for ( ElementList::const_iterator it = ingredientList.begin(); it != ingredientList.end(); ++it, ++loading_at ) {
+ insertItem((*it).name);
+ completionObject()->addItem((*it).name);
+ ingredientComboRows.insert( loading_at, (*it).id );
+ }
+}
+
+void IngredientComboBox::startLoad()
+{
+ //don't receive ingredient created/removed events from the database
+ database->disconnect( this );
+
+ TDEConfig * config = TDEGlobal::config(); config->setGroup( "Performance" );
+ load_limit = config->readNumEntry( "Limit", -1 );
+ if ( load_limit == -1 ) {
+ reload();
+ endLoad();
+ }
+ else {
+ loading_at = 0;
+ ing_count = database->ingredientCount();
+
+ load_timer->start( 0, false );
+ }
+}
+
+void IngredientComboBox::endLoad()
+{
+ load_timer->stop();
+
+ //now we're ready to receive ingredient created/removed events from the database
+ connect( database, TQ_SIGNAL( ingredientCreated( const Element & ) ), TQ_SLOT( createIngredient( const Element & ) ) );
+ connect( database, TQ_SIGNAL( ingredientRemoved( int ) ), TQ_SLOT( removeIngredient( int ) ) );
+}
+
+int IngredientComboBox::id( int row )
+{
+ return ingredientComboRows[ row ];
+}
+
+int IngredientComboBox::id( const TQString &ing )
+{
+ for ( int i = 0; i < count(); i++ ) {
+ if ( ing == text( i ) )
+ return id(i);
+ }
+ kdDebug()<<"Warning: couldn't find the ID for "<<ing<<endl;
+ return -1;
+}
+
+void IngredientComboBox::createIngredient( const Element &element )
+{
+ int row = findInsertionPoint( element.name );
+
+ TQString remember_text;
+ if ( editable() )
+ remember_text = lineEdit()->text();
+
+ insertItem( element.name, row );
+ completionObject()->addItem(element.name);
+
+ if ( editable() )
+ lineEdit()->setText( remember_text );
+
+ //now update the map by pushing everything after this item down
+ TQMap<int, int> new_map;
+ for ( TQMap<int, int>::iterator it = ingredientComboRows.begin(); it != ingredientComboRows.end(); ++it ) {
+ if ( it.key() >= row ) {
+ new_map.insert( it.key() + 1, it.data() );
+ }
+ else
+ new_map.insert( it.key(), it.data() );
+ }
+ ingredientComboRows = new_map;
+ ingredientComboRows.insert( row, element.id );
+}
+
+void IngredientComboBox::removeIngredient( int id )
+{
+ int row = -1;
+ for ( TQMap<int, int>::iterator it = ingredientComboRows.begin(); it != ingredientComboRows.end(); ++it ) {
+ if ( it.data() == id ) {
+ row = it.key();
+ completionObject()->removeItem( text(row) );
+ removeItem( row );
+ ingredientComboRows.remove( it );
+ break;
+ }
+ }
+
+ if ( row == -1 )
+ return ;
+
+ //now update the map by pushing everything after this item up
+ TQMap<int, int> new_map;
+ for ( TQMap<int, int>::iterator it = ingredientComboRows.begin(); it != ingredientComboRows.end(); ++it ) {
+ if ( it.key() > row ) {
+ new_map.insert( it.key() - 1, it.data() );
+ }
+ else
+ new_map.insert( it.key(), it.data() );
+ }
+ ingredientComboRows = new_map;
+}
+
+int IngredientComboBox::findInsertionPoint( const TQString &name )
+{
+ for ( int i = 0; i < count(); i++ ) {
+ if ( TQString::localeAwareCompare( name, text( i ) ) < 0 )
+ return i;
+ }
+
+ return count();
+}
+
+#include "ingredientcombobox.moc"
diff --git a/src/widgets/ingredientcombobox.h b/src/widgets/ingredientcombobox.h
new file mode 100644
index 0000000..9bd5fb7
--- /dev/null
+++ b/src/widgets/ingredientcombobox.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef INGREDIENTCOMBOBOX_H
+#define INGREDIENTCOMBOBOX_H
+
+#include <kcombobox.h>
+
+#include <tqmap.h>
+
+#include "datablocks/element.h"
+
+class TQTimer;
+
+class RecipeDB;
+class ElementList;
+
+class IngredientComboBox : public KComboBox
+{
+ TQ_OBJECT
+
+public:
+ IngredientComboBox( bool, TQWidget *parent, RecipeDB *db, const TQString &specialItem = TQString::null );
+
+ void reload();
+ int id( int row );
+ int id( const TQString &ing );
+
+ void startLoad();
+ void endLoad();
+
+private slots:
+ void createIngredient( const Element &element );
+ void removeIngredient( int id );
+
+ int findInsertionPoint( const TQString &name );
+ void loadMore();
+
+private:
+ RecipeDB *database;
+ TQMap<int, int> ingredientComboRows; // Contains the category id for every given row in the category combobox
+
+ int loading_at;
+ int ing_count;
+ int load_limit;
+ TQTimer *load_timer;
+ TQString m_specialItem;
+};
+
+#endif //INGREDIENTCOMBOBOX_H
+
diff --git a/src/widgets/ingredientinputwidget.cpp b/src/widgets/ingredientinputwidget.cpp
new file mode 100644
index 0000000..3c0aa7e
--- /dev/null
+++ b/src/widgets/ingredientinputwidget.cpp
@@ -0,0 +1,542 @@
+/***************************************************************************
+* Copyright (C) 2003-2005 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* Copyright (C) 2006 Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "ingredientinputwidget.h"
+
+#include <tqlabel.h>
+#include <tqwidgetstack.h>
+#include <tqhbox.h>
+#include <tqvbox.h>
+#include <tqgroupbox.h>
+#include <tqbuttongroup.h>
+#include <tqradiobutton.h>
+#include <tqcheckbox.h>
+
+#include <kcombobox.h>
+#include <tdelocale.h>
+#include <tdemessagebox.h>
+#include <kdebug.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/unit.h"
+#include "widgets/fractioninput.h"
+#include "widgets/ingredientcombobox.h"
+#include "widgets/headercombobox.h"
+#include "widgets/prepmethodcombobox.h"
+#include "dialogs/createunitdialog.h"
+
+#include "profiling.h"
+
+IngredientInput::IngredientInput( RecipeDB *db, TQWidget *parent, bool allowHeader ) : TQHBox(parent), database(db)
+{
+ TQVBox *ingredientVBox = new TQVBox( this );
+ TQHBox *typeHBox = new TQHBox( ingredientVBox );
+
+ if ( allowHeader ) {
+ typeButtonGrp = new TQButtonGroup();
+ TQRadioButton *ingredientRadioButton = new TQRadioButton( i18n( "Ingredient:" ), typeHBox );
+ typeButtonGrp->insert( ingredientRadioButton, 0 );
+
+ TQRadioButton *headerRadioButton = new TQRadioButton( i18n( "Ingredient grouping name", "Header:" ), typeHBox );
+ typeButtonGrp->insert( headerRadioButton, 1 );
+
+ typeButtonGrp->setButton( 0 );
+ connect( typeButtonGrp, TQ_SIGNAL( clicked( int ) ), TQ_SLOT( typeButtonClicked( int ) ) );
+ }
+ else {
+ (void) new TQLabel( i18n( "Ingredient:" ), typeHBox );
+ typeButtonGrp = 0;
+ }
+
+ header_ing_stack = new TQWidgetStack(ingredientVBox);
+ ingredientBox = new IngredientComboBox( TRUE, header_ing_stack, database );
+ ingredientBox->setAutoCompletion( TRUE );
+ ingredientBox->lineEdit() ->disconnect( ingredientBox ); //so hitting enter doesn't enter the item into the box
+ ingredientBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Fixed ) );
+ header_ing_stack->addWidget( ingredientBox );
+ headerBox = new HeaderComboBox( TRUE, header_ing_stack, database );
+ headerBox->setAutoCompletion( TRUE );
+ headerBox->lineEdit() ->disconnect( ingredientBox ); //so hitting enter doesn't enter the item into the box
+ headerBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Fixed ) );
+ header_ing_stack->addWidget( headerBox );
+
+ TQVBox *amountVBox = new TQVBox( this );
+ amountLabel = new TQLabel( i18n( "Amount:" ), amountVBox );
+ amountEdit = new FractionInput( amountVBox );
+ amountEdit->setAllowRange(true);
+ amountEdit->setSizePolicy( TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ) );
+
+ TQVBox *unitVBox = new TQVBox( this );
+ unitLabel = new TQLabel( i18n( "Unit:" ), unitVBox );
+ unitBox = new KComboBox( TRUE, unitVBox );
+ unitBox->setAutoCompletion( TRUE );
+ unitBox->lineEdit() ->disconnect( unitBox ); //so hitting enter doesn't enter the item into the box
+ unitBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Fixed ) );
+
+ TQVBox *prepMethodVBox = new TQVBox( this );
+ prepMethodLabel = new TQLabel( i18n( "Preparation Method:" ), prepMethodVBox );
+ prepMethodBox = new PrepMethodComboBox( TRUE, prepMethodVBox, database );
+ prepMethodBox->setAutoCompletion( TRUE );
+ prepMethodBox->lineEdit() ->disconnect( prepMethodBox ); //so hitting enter doesn't enter the item into the box
+ prepMethodBox->setSizePolicy( TQSizePolicy( TQSizePolicy::Ignored, TQSizePolicy::Fixed ) );
+
+ orButton = new TQCheckBox( i18n( "OR" ), this );
+
+ setStretchFactor( ingredientVBox, 5 );
+ setStretchFactor( amountVBox, 1 );
+ setStretchFactor( unitVBox, 2 );
+ setStretchFactor( prepMethodVBox, 3 );
+
+ connect( ingredientBox, TQ_SIGNAL( activated( int ) ), this, TQ_SLOT( loadUnitListCombo() ) );
+ connect( ingredientBox->lineEdit(), TQ_SIGNAL( lostFocus() ), this, TQ_SLOT( slotIngredientBoxLostFocus() ) );
+ connect( unitBox->lineEdit(), TQ_SIGNAL( lostFocus() ), this, TQ_SLOT( slotUnitBoxLostFocus() ) );
+ connect( prepMethodBox->lineEdit(), TQ_SIGNAL( lostFocus() ), this, TQ_SLOT( slotPrepMethodBoxLostFocus() ) );
+ connect( orButton, TQ_SIGNAL( toggled(bool) ), this, TQ_SLOT( orToggled(bool) ) );
+
+ connect( unitBox->lineEdit(), TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( signalIngredient() ) );
+ connect( ingredientBox->lineEdit(), TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( signalIngredient() ) );
+ connect( headerBox->lineEdit(), TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( signalIngredient() ) );
+ connect( prepMethodBox->lineEdit(), TQ_SIGNAL( returnPressed() ), this, TQ_SLOT( signalIngredient() ) );
+ connect( amountEdit, TQ_SIGNAL( returnPressed( const TQString & ) ), this, TQ_SLOT( signalIngredient() ) );
+
+ unitComboList = new UnitList;
+
+ setFocusProxy( ingredientBox );
+}
+
+IngredientInput::~IngredientInput()
+{
+ delete unitComboList;
+ delete typeButtonGrp;
+}
+
+void IngredientInput::clear()
+{
+ unitComboList->clear();
+
+ orButton->setChecked(false);
+ typeButtonGrp->setButton( 0 ); //put back to ingredient input
+ typeButtonClicked( 0 );
+
+ amountEdit->clear();
+ ingredientBox->lineEdit()->setText("");
+ prepMethodBox->lineEdit()->setText("");
+ headerBox->lineEdit()->setText("");
+ unitBox->lineEdit()->setText("");
+}
+
+void IngredientInput::orToggled(bool b)
+{
+ emit orToggled(b,this);
+}
+
+void IngredientInput::reloadCombos()
+{
+ //these only needed to be loaded once
+ if ( ingredientBox->count() == 0 ) {
+ START_TIMER("Loading ingredient input auto-completion");
+ ingredientBox->reload();
+ END_TIMER();
+ }
+ if ( headerBox->count() == 0 ) {
+ START_TIMER("Loading ingredient header input auto-completion");
+ headerBox->reload();
+ END_TIMER();
+ }
+ if ( prepMethodBox->count() == 0 ) {
+ START_TIMER("Loading prep method input auto-completion");
+ prepMethodBox->reload();
+ END_TIMER();
+ }
+
+ loadUnitListCombo();
+}
+
+void IngredientInput::slotIngredientBoxLostFocus( void )
+{
+ if ( ingredientBox->contains( ingredientBox->currentText() ) ) {
+ ingredientBox->setCurrentItem( ingredientBox->currentText() );
+ loadUnitListCombo();
+ }
+ else {
+ unitBox->clear();
+ unitBox->completionObject() ->clear();
+ unitComboList->clear();
+ }
+}
+
+void IngredientInput::slotUnitBoxLostFocus()
+{
+ if ( unitBox->contains( unitBox->currentText() ) )
+ unitBox->setCurrentItem( unitBox->currentText() );
+}
+
+void IngredientInput::slotPrepMethodBoxLostFocus()
+{
+ if ( prepMethodBox->contains( prepMethodBox->currentText() ) )
+ prepMethodBox->setCurrentItem( prepMethodBox->currentText() );
+}
+
+void IngredientInput::typeButtonClicked( int button_id )
+{
+ if ( amountEdit->isEnabled() == !bool( button_id ) ) //it is already set (the same button was clicked more than once)
+ return ;
+
+ amountEdit->setEnabled( !bool( button_id ) );
+ unitBox->setEnabled( !bool( button_id ) );
+ prepMethodBox->setEnabled( !bool( button_id ) );
+
+ if ( button_id == 1 ) { //Header
+ header_ing_stack->raiseWidget( headerBox );
+ }
+ else {
+ header_ing_stack->raiseWidget( ingredientBox );
+ }
+}
+
+void IngredientInput::enableHeader( bool enable )
+{
+ if ( !enable ) {
+ typeButtonGrp->setButton( 0 ); //put back to ingredient input
+ typeButtonClicked( 0 );
+ }
+ typeButtonGrp->find(1)->setEnabled(enable);
+}
+
+void IngredientInput::signalIngredient()
+{
+ //validate input; if successful, emit signal
+ if ( isHeader() ) {
+ if ( header().isEmpty() )
+ return;
+ }
+ else {
+ if ( !isInputValid() )
+ return;
+ }
+
+ emit addIngredient();
+}
+
+bool IngredientInput::isInputValid()
+{
+ if ( ingredientBox->currentText().stripWhiteSpace().isEmpty() ) {
+ KMessageBox::error( this, i18n( "Please enter an ingredient" ), TQString::null );
+ ingredientBox->setFocus();
+ return false;
+ }
+ return checkAmountEdit() && checkBounds();
+}
+
+bool IngredientInput::checkBounds()
+{
+ if ( ingredientBox->currentText().length() > uint(database->maxIngredientNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Ingredient name cannot be longer than %1 characters." ) ).arg( database->maxIngredientNameLength() ) );
+ ingredientBox->lineEdit() ->setFocus();
+ ingredientBox->lineEdit() ->selectAll();
+ return false;
+ }
+
+ if ( unitBox->currentText().length() > uint(database->maxUnitNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Unit name cannot be longer than %1 characters." ) ).arg( database->maxUnitNameLength() ) );
+ unitBox->lineEdit() ->setFocus();
+ unitBox->lineEdit() ->selectAll();
+ return false;
+ }
+
+ TQStringList prepMethodList = TQStringList::split(",",prepMethodBox->currentText());
+ for ( TQStringList::const_iterator it = prepMethodList.begin(); it != prepMethodList.end(); ++it ) {
+ if ( (*it).stripWhiteSpace().length() > uint(database->maxPrepMethodNameLength()) )
+ {
+ KMessageBox::error( this, TQString( i18n( "Preparation method cannot be longer than %1 characters." ) ).arg( database->maxPrepMethodNameLength() ) );
+ prepMethodBox->lineEdit() ->setFocus();
+ prepMethodBox->lineEdit() ->selectAll();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool IngredientInput::checkAmountEdit()
+{
+ if ( amountEdit->isInputValid() )
+ return true;
+ else {
+ KMessageBox::error( this, i18n( "Amount field contains invalid input." ),
+ i18n( "Invalid input" ) );
+ amountEdit->setFocus();
+ amountEdit->selectAll();
+ return false;
+ }
+}
+
+void IngredientInput::loadUnitListCombo()
+{
+ TQString store_unit = unitBox->currentText();
+ unitBox->clear(); // Empty the combo first
+ unitBox->completionObject() ->clear();
+
+ int comboIndex = ingredientBox->currentItem();
+ int comboCount = ingredientBox->count();
+
+ if ( comboCount > 0 ) { // If not, the list may be empty (no ingredient list defined) and crashes while reading
+ int selectedIngredient = ingredientBox->id( comboIndex );
+ database->loadPossibleUnits( selectedIngredient, unitComboList );
+
+ //Populate this data into the ComboBox
+ for ( UnitList::const_iterator unit_it = unitComboList->begin(); unit_it != unitComboList->end(); ++unit_it ) {
+ unitBox->insertItem( ( *unit_it ).name );
+ unitBox->completionObject() ->addItem( ( *unit_it ).name );
+ if ( ( *unit_it ).name != (*unit_it ).plural ) {
+ unitBox->insertItem( ( *unit_it ).plural );
+ unitBox->completionObject() ->addItem( ( *unit_it ).plural );
+ }
+
+ if ( !( *unit_it ).name_abbrev.isEmpty() ) {
+ unitBox->insertItem( ( *unit_it ).name_abbrev );
+ unitBox->completionObject() ->addItem( ( *unit_it ).name_abbrev );
+ }
+ if ( !(*unit_it ).plural_abbrev.isEmpty() &&
+ ( *unit_it ).name_abbrev != (*unit_it ).plural_abbrev ) {
+ unitBox->insertItem( ( *unit_it ).plural_abbrev );
+ unitBox->completionObject() ->addItem( ( *unit_it ).plural_abbrev );
+ }
+
+ }
+ }
+ unitBox->lineEdit() ->setText( store_unit );
+}
+
+bool IngredientInput::isHeader() const
+{
+ return typeButtonGrp && (typeButtonGrp->id( typeButtonGrp->selected() ) == 1);
+}
+
+Ingredient IngredientInput::ingredient() const
+{
+ Ingredient ing;
+
+ ing.prepMethodList = ElementList::split(",",prepMethodBox->currentText());
+ ing.name = ingredientBox->currentText();
+ amountEdit->value(ing.amount,ing.amount_offset);
+ ing.units = Unit(unitBox->currentText().stripWhiteSpace(),ing.amount+ing.amount_offset);
+ ing.ingredientID = ingredientBox->id( ingredientBox->currentItem() );
+
+ return ing;
+}
+
+TQString IngredientInput::header() const
+{
+ return headerBox->currentText().stripWhiteSpace();
+}
+
+void IngredientInput::updateTabOrder()
+{
+ TQWidget::setTabOrder( ingredientBox, amountEdit );
+ TQWidget::setTabOrder( amountEdit, unitBox );
+ TQWidget::setTabOrder( unitBox, prepMethodBox );
+ TQWidget::setTabOrder( prepMethodBox, orButton );
+}
+
+
+IngredientInputWidget::IngredientInputWidget( RecipeDB *db, TQWidget *parent ) : TQVBox(parent), database(db)
+{
+ setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ) );
+
+ m_ingInputs.append(new IngredientInput(database,this));
+
+ // Connect signals & Slots
+ connect( m_ingInputs[0], TQ_SIGNAL(addIngredient()), this, TQ_SLOT(addIngredient()) );
+ connect( m_ingInputs[0], TQ_SIGNAL(orToggled(bool,IngredientInput*)), this, TQ_SLOT(updateInputs(bool,IngredientInput*)) );
+
+ reloadCombos();
+}
+
+IngredientInputWidget::~IngredientInputWidget()
+{
+}
+
+void IngredientInputWidget::clear()
+{
+ //clearing the first input deletes all substitute inputs
+ m_ingInputs[0]->clear();
+}
+
+void IngredientInputWidget::updateInputs(bool on, IngredientInput* input)
+{
+ TQValueList<IngredientInput*>::iterator curr = m_ingInputs.find(input);
+ IngredientInput *prev_input = *curr;
+ ++curr;
+
+ if ( on ) {
+ IngredientInput *new_input = new IngredientInput(database,this,false);
+ new_input->reloadCombos();
+
+ TQWidget::setTabOrder( prev_input, new_input );
+ new_input->updateTabOrder();
+
+ connect( new_input, TQ_SIGNAL(addIngredient()), this, TQ_SLOT(addIngredient()) );
+ connect( new_input, TQ_SIGNAL(orToggled(bool,IngredientInput*)), this, TQ_SLOT(updateInputs(bool,IngredientInput*)) );
+
+ new_input->show();
+ m_ingInputs.insert(curr,new_input);
+
+ m_ingInputs[0]->enableHeader(false);
+
+ }
+ else {
+ while ( curr != m_ingInputs.end() ) {
+ (*curr)->deleteLater();
+ curr = m_ingInputs.remove(curr);
+ }
+ if ( m_ingInputs.count() == 1 )
+ m_ingInputs[0]->enableHeader(true);
+ }
+}
+
+void IngredientInputWidget::addIngredient()
+{
+ if ( m_ingInputs[0]->isHeader() ) {
+ TQString header = m_ingInputs[0]->header();
+ if ( header.isEmpty() )
+ return;
+
+ int group_id = createNewGroupIfNecessary( header,database );
+ emit headerEntered( Element(header,group_id) );
+ }
+ else {
+ for ( TQValueList<IngredientInput*>::iterator it = m_ingInputs.begin(); it != m_ingInputs.end(); ++it ) {
+ if ( !(*it)->isInputValid() )
+ return;
+ }
+
+ TQValueList<IngredientData> list;
+ for ( TQValueList<IngredientInput*>::const_iterator it = m_ingInputs.begin(); it != m_ingInputs.end(); ++it ) {
+ Ingredient ing = (*it)->ingredient();
+ ing.ingredientID = createNewIngredientIfNecessary(ing.name,database);
+
+ bool plural = ing.amount+ing.amount_offset > 1;
+ ing.units.id = createNewUnitIfNecessary( (plural)?ing.units.plural:ing.units.name, plural, ing.ingredientID, ing.units,database );
+ if ( ing.units.id == -1 ) // this will happen if the dialog to create a unit was cancelled
+ return ;
+
+ TQValueList<int> prepIDs = createNewPrepIfNecessary( ing.prepMethodList,database );
+ TQValueList<int>::const_iterator id_it = prepIDs.begin();
+ for ( ElementList::iterator it = ing.prepMethodList.begin(); it != ing.prepMethodList.end(); ++it, ++id_it ) {
+ (*it).id = *id_it;
+ }
+
+ list.append(ing);
+ }
+
+ Ingredient ing = list.first();
+ list.pop_front();
+ ing.substitutes = list;
+ emit ingredientEntered( ing );
+ }
+ clear();
+
+ m_ingInputs[0]->setFocus(); //put cursor back to the ingredient name so user can begin next ingredient
+}
+
+int IngredientInputWidget::createNewIngredientIfNecessary( const TQString &ing, RecipeDB *database )
+{
+ int id = -1;
+ if ( ing.isEmpty() )
+ return -1;
+
+ id = database->findExistingIngredientByName( ing );
+ if ( id == -1 ) {
+ database->createNewIngredient( ing );
+ id = database->lastInsertID();
+ }
+ return id;
+}
+
+int IngredientInputWidget::createNewUnitIfNecessary( const TQString &unit, bool plural, int ingredientID, Unit &new_unit, RecipeDB *database )
+{
+ int id = database->findExistingUnitByName( unit );
+ if ( -1 == id )
+ {
+ CreateUnitDialog getUnit( 0, ( plural ) ? TQString::null : unit, ( !plural ) ? TQString::null : unit );
+ if ( getUnit.exec() == TQDialog::Accepted ) {
+ new_unit = getUnit.newUnit();
+ database->createNewUnit( new_unit );
+
+ id = database->lastInsertID();
+ }
+ }
+
+ if ( !database->ingredientContainsUnit(
+ ingredientID,
+ id ) )
+ {
+ database->addUnitToIngredient(
+ ingredientID,
+ id );
+ }
+
+ new_unit = database->unitName( id );
+
+ //loadUnitListCombo();
+ return id;
+}
+
+TQValueList<int> IngredientInputWidget::createNewPrepIfNecessary( const ElementList &prepMethods, RecipeDB *database )
+{
+ TQValueList<int> ids;
+
+ if ( prepMethods.isEmpty() ) //no prep methods
+ return ids;
+ else
+ {
+ for ( ElementList::const_iterator it = prepMethods.begin(); it != prepMethods.end(); ++it ) {
+ int id = database->findExistingPrepByName( (*it).name.stripWhiteSpace() );
+ if ( id == -1 )
+ {
+ database->createNewPrepMethod( (*it).name.stripWhiteSpace() );
+ id = database->lastInsertID();
+ }
+ ids << id;
+ }
+
+ return ids;
+ }
+}
+
+int IngredientInputWidget::createNewGroupIfNecessary( const TQString &group, RecipeDB *database )
+{
+ if ( group.stripWhiteSpace().isEmpty() ) //no group
+ return -1;
+ else
+ {
+ int id = database->findExistingIngredientGroupByName( group );
+ if ( id == -1 ) //creating new
+ {
+ database->createNewIngGroup( group );
+ id = database->lastInsertID();
+ }
+
+ return id;
+ }
+}
+
+void IngredientInputWidget::reloadCombos()
+{
+ for ( TQValueList<IngredientInput*>::iterator it = m_ingInputs.begin(); it != m_ingInputs.end(); ++it )
+ (*it)->reloadCombos();
+}
+
+#include "ingredientinputwidget.moc"
diff --git a/src/widgets/ingredientinputwidget.h b/src/widgets/ingredientinputwidget.h
new file mode 100644
index 0000000..ecc623a
--- /dev/null
+++ b/src/widgets/ingredientinputwidget.h
@@ -0,0 +1,134 @@
+/***************************************************************************
+* Copyright (C) 2003-2005 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* Copyright (C) 2006 Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef INGREDIENTINPUTWIDGET_H
+#define INGREDIENTINPUTWIDGET_H
+
+#include <tqvbox.h>
+
+#include "datablocks/unit.h"
+
+class TQVBox;
+class TQHBox;
+class TQLabel;
+class TQButtonGroup;
+class TQWidgetStack;
+class TQGroupBox;
+class TQRadioButton;
+class TQCheckBox;
+
+class KComboBox;
+
+class IngredientComboBox;
+class HeaderComboBox;
+class PrepMethodComboBox;
+class RecipeDB;
+class FractionInput;
+class Ingredient;
+class Element;
+class ElementList;
+class IngredientInput;
+
+class IngredientInputWidget : public TQVBox
+{
+TQ_OBJECT
+
+public:
+ IngredientInputWidget( RecipeDB *db, TQWidget *parent );
+ ~IngredientInputWidget();
+
+ void clear();
+
+ static int createNewIngredientIfNecessary( const TQString &ing, RecipeDB *db );
+ static int createNewUnitIfNecessary( const TQString &unit, bool plural, int ingredient_id, Unit &new_unit, RecipeDB *db );
+ static TQValueList<int> createNewPrepIfNecessary( const ElementList &prepMethods, RecipeDB *db );
+ static int createNewGroupIfNecessary( const TQString &group, RecipeDB *db );
+
+signals:
+ void ingredientEntered( const Ingredient &ing );
+
+ void headerEntered( const Element &header );
+
+public slots:
+ void addIngredient();
+
+private slots:
+ void updateInputs(bool,IngredientInput*);
+
+private:
+ /** Reloads lists of units, ingredients, and preparation methods */
+ void reloadCombos();
+
+ void checkIfNewUnits();
+
+ RecipeDB *database;
+
+ TQValueList<IngredientInput*> m_ingInputs;
+};
+
+class IngredientInput : public TQHBox
+{
+TQ_OBJECT
+
+public:
+ IngredientInput( RecipeDB *db, TQWidget *parent, bool allowHeader = true );
+ ~IngredientInput();
+
+ void clear();
+ bool isInputValid();
+
+ bool isHeader() const;
+ Ingredient ingredient() const;
+ TQString header() const;
+
+ void reloadCombos();
+ void enableHeader( bool );
+ void updateTabOrder();
+
+signals:
+ void addIngredient();
+ void orToggled(bool,IngredientInput*);
+
+private slots:
+ void loadUnitListCombo();
+ void signalIngredient();
+ void typeButtonClicked( int );
+ void slotIngredientBoxLostFocus();
+ void slotUnitBoxLostFocus();
+ void slotPrepMethodBoxLostFocus();
+ void orToggled(bool);
+
+private:
+ bool checkBounds();
+ bool checkAmountEdit();
+
+ RecipeDB *database;
+ UnitList *unitComboList;
+
+ TQCheckBox *orButton;
+ TQGroupBox *ingredientGBox;
+ TQLabel *amountLabel;
+ FractionInput* amountEdit;
+ TQLabel *unitLabel;
+ KComboBox* unitBox;
+ TQLabel *prepMethodLabel;
+ PrepMethodComboBox* prepMethodBox;
+ TQLabel *ingredientLabel;
+ IngredientComboBox* ingredientBox;
+ HeaderComboBox* headerBox;
+ TQWidgetStack *header_ing_stack;
+ TQButtonGroup *typeButtonGrp;
+};
+
+#endif //INGREDIENTINPUTWIDGET_H
diff --git a/src/widgets/ingredientlistview.cpp b/src/widgets/ingredientlistview.cpp
new file mode 100644
index 0000000..1343530
--- /dev/null
+++ b/src/widgets/ingredientlistview.cpp
@@ -0,0 +1,287 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "ingredientlistview.h"
+
+#include <tdemessagebox.h>
+#include <tdeconfig.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kiconloader.h>
+#include <tdepopupmenu.h>
+
+#include "backends/recipedb.h"
+#include "dialogs/createelementdialog.h"
+#include "dialogs/dependanciesdialog.h"
+
+IngredientCheckListItem::IngredientCheckListItem( IngredientCheckListView* qlv, const Element &ing ) : TQCheckListItem( qlv, TQString::null, TQCheckListItem::CheckBox ),
+ m_listview(qlv)
+{
+ // Initialize the ingredient data with the the property data
+ ingStored = new Element();
+ ingStored->id = ing.id;
+ ingStored->name = ing.name;
+}
+
+IngredientCheckListItem::IngredientCheckListItem( IngredientCheckListView* qlv, TQListViewItem *after, const Element &ing ) : TQCheckListItem( qlv, after, TQString::null, TQCheckListItem::CheckBox ),
+ m_listview(qlv)
+{
+ // Initialize the ingredient data with the the property data
+ ingStored = new Element();
+ ingStored->id = ing.id;
+ ingStored->name = ing.name;
+}
+
+IngredientCheckListItem::~IngredientCheckListItem( void )
+{
+ delete ingStored;
+}
+int IngredientCheckListItem::id( void ) const
+{
+ return ingStored->id;
+}
+TQString IngredientCheckListItem::name( void ) const
+{
+ return ingStored->name;
+}
+Element IngredientCheckListItem::ingredient() const
+{
+ return *ingStored;
+}
+
+TQString IngredientCheckListItem::text( int column ) const
+{
+ switch ( column ) {
+ case 0:
+ return ( ingStored->name );
+ case 1:
+ return ( TQString::number( ingStored->id ) );
+ default:
+ return TQString::null;
+ }
+}
+
+void IngredientCheckListItem::stateChange( bool on )
+{
+ m_listview->stateChange(this,on);
+}
+
+IngredientListView::IngredientListView( TQWidget *parent, RecipeDB *db ) : DBListViewBase( parent,db, db->ingredientCount() )
+{
+ setAllColumnsShowFocus( true );
+ setDefaultRenameAction( TQListView::Reject );
+}
+
+void IngredientListView::init()
+{
+ connect( database, TQ_SIGNAL( ingredientCreated( const Element & ) ), TQ_SLOT( checkCreateIngredient( const Element & ) ) );
+ connect( database, TQ_SIGNAL( ingredientRemoved( int ) ), TQ_SLOT( removeIngredient( int ) ) );
+}
+
+void IngredientListView::load( int limit, int offset )
+{
+ ElementList ingredientList;
+ database->loadIngredients( &ingredientList, limit, offset );
+
+ setTotalItems(ingredientList.count());
+
+ for ( ElementList::const_iterator ing_it = ingredientList.begin(); ing_it != ingredientList.end(); ++ing_it )
+ createIngredient( *ing_it );
+}
+
+void IngredientListView::checkCreateIngredient( const Element &el )
+{
+ if ( handleElement(el.name) ) { //only create this ingredient if the base class okays it
+ createIngredient(el);
+ }
+}
+
+
+StdIngredientListView::StdIngredientListView( TQWidget *parent, RecipeDB *db, bool editable ) : IngredientListView( parent, db )
+{
+ addColumn( i18n( "Ingredient" ) );
+
+ TDEConfig * config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ if ( editable ) {
+ setRenameable( 0, true );
+
+ TDEIconLoader *il = new TDEIconLoader;
+
+ kpop = new TDEPopupMenu( this );
+ kpop->insertItem( il->loadIcon( "document-new", TDEIcon::NoGroup, 16 ), i18n( "&Create" ), this, TQ_SLOT( createNew() ), CTRL + Key_C );
+ kpop->insertItem( il->loadIcon( "edit-delete", TDEIcon::NoGroup, 16 ), i18n( "&Delete" ), this, TQ_SLOT( remove
+ () ), Key_Delete );
+ kpop->insertItem( il->loadIcon( "edit", TDEIcon::NoGroup, 16 ), i18n( "&Rename" ), this, TQ_SLOT( rename() ), CTRL + Key_R );
+ kpop->polish();
+
+ delete il;
+
+ connect( this, TQ_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ), TQ_SLOT( showPopup( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
+ connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem* ) ), this, TQ_SLOT( modIngredient( TQListViewItem* ) ) );
+ connect( this, TQ_SIGNAL( itemRenamed( TQListViewItem* ) ), this, TQ_SLOT( saveIngredient( TQListViewItem* ) ) );
+ }
+}
+
+void StdIngredientListView::showPopup( TDEListView * /*l*/, TQListViewItem *i, const TQPoint &p )
+{
+ if ( i )
+ kpop->exec( p );
+}
+
+void StdIngredientListView::createNew()
+{
+ CreateElementDialog * elementDialog = new CreateElementDialog( this, i18n( "New Ingredient" ) );
+
+ if ( elementDialog->exec() == TQDialog::Accepted ) {
+ TQString result = elementDialog->newElementName();
+
+ if ( checkBounds( result ) )
+ database->createNewIngredient( result ); // Create the new author in the database
+ }
+}
+
+void StdIngredientListView::remove
+ ()
+{
+ TQListViewItem * it = currentItem();
+
+ if ( it ) {
+ int ingredientID = it->text( 1 ).toInt();
+
+ ElementList dependingRecipes;
+ database->findIngredientDependancies( ingredientID, &dependingRecipes );
+
+ if ( dependingRecipes.isEmpty() )
+ database->removeIngredient( ingredientID );
+ else { // Need Warning!
+ ListInfo list;
+ list.list = dependingRecipes;
+ list.name = i18n( "Recipes" );
+
+ DependanciesDialog warnDialog( this, list );
+ warnDialog.setCustomWarning( i18n("You are about to permanantly delete recipes from your database.") );
+ if ( warnDialog.exec() == TQDialog::Accepted )
+ database->removeIngredient( ingredientID );
+ }
+ }
+}
+
+void StdIngredientListView::rename()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item )
+ IngredientListView::rename( item, 0 );
+}
+
+void StdIngredientListView::createIngredient( const Element &ing )
+{
+ createElement(new TQListViewItem( this, ing.name, TQString::number( ing.id ) ));
+}
+
+void StdIngredientListView::removeIngredient( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 1 );
+ removeElement(item);
+}
+
+void StdIngredientListView::modIngredient( TQListViewItem* i )
+{
+ if ( i )
+ IngredientListView::rename( i, 0);
+}
+
+void StdIngredientListView::saveIngredient( TQListViewItem* i )
+{
+ if ( !checkBounds( i->text( 0 ) ) ) {
+ reload(ForceReload); //reset the changed text
+ return ;
+ }
+
+ int existing_id = database->findExistingIngredientByName( i->text( 0 ) );
+ int ing_id = i->text( 1 ).toInt();
+ if ( existing_id != -1 && existing_id != ing_id ) //category already exists with this label... merge the two
+ {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "This ingredient already exists. Continuing will merge these two ingredients into one. Are you sure?" ) ) )
+ {
+ case KMessageBox::Continue: {
+ database->mergeIngredients( existing_id, ing_id );
+ break;
+ }
+ default:
+ reload(ForceReload);
+ break; //we have to reload because the ingredient was renamed, and we need to reset it
+ }
+ }
+ else {
+ database->modIngredient( ( i->text( 1 ) ).toInt(), i->text( 0 ) );
+ }
+}
+
+bool StdIngredientListView::checkBounds( const TQString &name )
+{
+ if ( name.length() > uint(database->maxIngredientNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Ingredient name cannot be longer than %1 characters." ) ).arg( database->maxIngredientNameLength() ) );
+ return false;
+ }
+
+ return true;
+}
+
+
+
+IngredientCheckListView::IngredientCheckListView( TQWidget *parent, RecipeDB *db ) : IngredientListView( parent, db )
+{
+ addColumn( i18n( "Ingredient" ) );
+
+ TDEConfig *config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+}
+
+void IngredientCheckListView::createIngredient( const Element &ing )
+{
+ createElement(new IngredientCheckListItem( this, ing ));
+}
+
+void IngredientCheckListView::removeIngredient( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 1 );
+ removeElement(item);
+}
+
+void IngredientCheckListView::load( int limit, int offset )
+{
+ IngredientListView::load(limit,offset);
+
+ for ( TQValueList<Element>::const_iterator ing_it = m_selections.begin(); ing_it != m_selections.end(); ++ing_it ) {
+ TQCheckListItem * item = ( TQCheckListItem* ) findItem( TQString::number( (*ing_it).id ), 1 );
+ if ( item ) {
+ item->setOn(true);
+ }
+ }
+}
+
+void IngredientCheckListView::stateChange(IngredientCheckListItem *it,bool on)
+{
+ if ( !reloading() ) {
+ if ( on )
+ m_selections.append(it->ingredient());
+ else
+ m_selections.remove(it->ingredient());
+ }
+}
+
+#include "ingredientlistview.moc"
diff --git a/src/widgets/ingredientlistview.h b/src/widgets/ingredientlistview.h
new file mode 100644
index 0000000..58990c4
--- /dev/null
+++ b/src/widgets/ingredientlistview.h
@@ -0,0 +1,119 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef INGREDIENTLISTVIEW_H
+#define INGREDIENTLISTVIEW_H
+
+#include "dblistviewbase.h"
+
+#include "datablocks/element.h"
+
+class RecipeDB;
+class TDEPopupMenu;
+class IngredientCheckListView;
+
+/**
+@author Unai Garro
+*/
+class IngredientCheckListItem: public TQCheckListItem
+{
+public:
+ IngredientCheckListItem( IngredientCheckListView* qlv, const Element &ing );
+ IngredientCheckListItem( IngredientCheckListView* qlv, TQListViewItem *after, const Element &ing );
+ ~IngredientCheckListItem( void );
+
+ int id( void ) const;
+ TQString name( void ) const;
+ Element ingredient() const;
+
+ virtual TQString text( int column ) const;
+
+protected:
+ virtual void stateChange( bool on );
+
+private:
+ Element *ingStored;
+ IngredientCheckListView *m_listview;
+};
+
+
+
+class IngredientListView : public DBListViewBase
+{
+ TQ_OBJECT
+
+public:
+ IngredientListView( TQWidget *parent, RecipeDB *db );
+
+protected slots:
+ virtual void createIngredient( const Element & ) = 0;
+ virtual void removeIngredient( int ) = 0;
+ virtual void load(int limit,int offset);
+
+protected:
+ virtual void init();
+
+private slots:
+ virtual void checkCreateIngredient( const Element & );
+};
+
+
+
+class StdIngredientListView : public IngredientListView
+{
+ TQ_OBJECT
+
+public:
+ StdIngredientListView( TQWidget *parent, RecipeDB *db, bool editable = false );
+
+protected:
+ virtual void createIngredient( const Element & );
+ virtual void removeIngredient( int );
+
+private slots:
+ void showPopup( TDEListView *, TQListViewItem *, const TQPoint & );
+
+ void createNew();
+ void remove
+ ();
+ void rename();
+
+ void modIngredient( TQListViewItem* i );
+ void saveIngredient( TQListViewItem* i );
+
+private:
+ bool checkBounds( const TQString &name );
+
+ TDEPopupMenu *kpop;
+};
+
+
+
+class IngredientCheckListView : public IngredientListView
+{
+public:
+ IngredientCheckListView( TQWidget *parent, RecipeDB *db );
+
+ virtual void stateChange(IngredientCheckListItem *,bool);
+
+ TQValueList<Element> selections() const{ return m_selections; }
+
+protected:
+ virtual void createIngredient( const Element &ing );
+ virtual void removeIngredient( int );
+
+ virtual void load( int limit, int offset );
+
+private:
+ TQValueList<Element> m_selections;
+};
+
+#endif //INGREDIENTLISTVIEW_H
diff --git a/src/widgets/kdateedit.cpp b/src/widgets/kdateedit.cpp
new file mode 100644
index 0000000..8becc83
--- /dev/null
+++ b/src/widgets/kdateedit.cpp
@@ -0,0 +1,388 @@
+/*
+ This file is part of libtdepim.
+
+ Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
+ Copyright (c) 2004 Tobias Koenig <tokoe@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 Steet, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include <tqapplication.h>
+#include <tqlineedit.h>
+#include <tqlistbox.h>
+#include <tqvalidator.h>
+
+//#include <kcalendarsystem.h>
+#include <tdeglobal.h>
+#include <tdeglobalsettings.h>
+#include <tdelocale.h>
+#include <tdeconfig.h>
+
+#include "kdateedit.h"
+
+TQRect desktopGeometry(TQWidget* w)
+{
+ TQDesktopWidget *dw = TQApplication::desktop();
+ if (dw->isVirtualDesktop()) {
+ TDEConfigGroup group(TDEGlobal::config(), "Windows");
+ if (group.readBoolEntry("XineramaEnabled", true) &&
+ group.readBoolEntry("XineramaPlacementEnabled", true)) {
+ if (w)
+ return dw->screenGeometry(dw->screenNumber(w));
+ else return dw->screenGeometry(-1);
+ } else {
+ return dw->geometry();
+ }
+ } else {
+ return dw->geometry();
+ }
+}
+
+class DateValidator : public TQValidator
+{
+ public:
+ DateValidator( const TQStringList &keywords, TQWidget* parent, const char* name = 0 )
+ : TQValidator( parent, name ), mKeywords( keywords )
+ {}
+
+ virtual State validate( TQString &str, int& ) const
+ {
+ int length = str.length();
+
+ // empty string is intermediate so one can clear the edit line and start from scratch
+ if ( length <= 0 )
+ return Intermediate;
+
+ if ( mKeywords.contains( str.lower() ) )
+ return Acceptable;
+
+ bool ok = false;
+ TDEGlobal::locale()->readDate( str, &ok );
+ if ( ok )
+ return Acceptable;
+ else
+ return Intermediate;
+ }
+
+ private:
+ TQStringList mKeywords;
+};
+
+KDateEdit::KDateEdit( TQWidget *parent, const char *name )
+ : TQComboBox( true, parent, name ),
+ mReadOnly( false ),
+ mDiscardNextMousePress( false )
+{
+ // need at least one entry for popup to work
+ setMaxCount( 1 );
+
+ mDate = TQDate::currentDate();
+ TQString today = TDEGlobal::locale()->formatDate( mDate, true );
+
+ insertItem( today );
+ setCurrentItem( 0 );
+ changeItem( today, 0 );
+ setMinimumSize( sizeHint() );
+
+ connect( lineEdit(), TQ_SIGNAL( returnPressed() ),
+ this, TQ_SLOT( lineEnterPressed() ) );
+ connect( this, TQ_SIGNAL( textChanged( const TQString& ) ),
+ TQ_SLOT( slotTextChanged( const TQString& ) ) );
+
+ mPopup = new KDatePickerPopup( KDatePickerPopup::DatePicker | KDatePickerPopup::Words );
+ mPopup->hide();
+ mPopup->installEventFilter( this );
+
+ connect( mPopup, TQ_SIGNAL( dateChanged( TQDate ) ),
+ TQ_SLOT( dateSelected( TQDate ) ) );
+
+ // handle keyword entry
+ setupKeywords();
+ lineEdit()->installEventFilter( this );
+
+ setValidator( new DateValidator( mKeywordMap.keys(), this ) );
+
+ mTextChanged = false;
+}
+
+KDateEdit::~KDateEdit()
+{
+ delete mPopup;
+ mPopup = 0;
+}
+
+void KDateEdit::setDate( const TQDate& date )
+{
+ assignDate( date );
+ updateView();
+}
+
+TQDate KDateEdit::date() const
+{
+ return mDate;
+}
+
+void KDateEdit::setReadOnly( bool readOnly )
+{
+ mReadOnly = readOnly;
+ lineEdit()->setReadOnly( readOnly );
+}
+
+bool KDateEdit::isReadOnly() const
+{
+ return mReadOnly;
+}
+
+void KDateEdit::popup()
+{
+ if ( mReadOnly )
+ return;
+
+ TQRect desk = desktopGeometry( this );
+
+ TQPoint popupPoint = mapToGlobal( TQPoint( 0,0 ) );
+
+ int dateFrameHeight = mPopup->sizeHint().height();
+ if ( popupPoint.y() + height() + dateFrameHeight > desk.bottom() )
+ popupPoint.setY( popupPoint.y() - dateFrameHeight );
+ else
+ popupPoint.setY( popupPoint.y() + height() );
+
+ int dateFrameWidth = mPopup->sizeHint().width();
+ if ( popupPoint.x() + dateFrameWidth > desk.right() )
+ popupPoint.setX( desk.right() - dateFrameWidth );
+
+ if ( popupPoint.x() < desk.left() )
+ popupPoint.setX( desk.left() );
+
+ if ( popupPoint.y() < desk.top() )
+ popupPoint.setY( desk.top() );
+
+ if ( mDate.isValid() )
+ mPopup->setDate( mDate );
+ else
+ mPopup->setDate( TQDate::currentDate() );
+
+ mPopup->popup( popupPoint );
+
+ // The combo box is now shown pressed. Make it show not pressed again
+ // by causing its (invisible) list box to emit a 'selected' signal.
+ // First, ensure that the list box contains the date currently displayed.
+ TQDate date = parseDate();
+ assignDate( date );
+ updateView();
+ // Now, simulate an Enter to unpress it
+ TQListBox *lb = listBox();
+ if (lb) {
+ lb->setCurrentItem(0);
+ TQKeyEvent* keyEvent = new TQKeyEvent(TQEvent::KeyPress, TQt::Key_Enter, 0, 0);
+ TQApplication::postEvent(lb, keyEvent);
+ }
+}
+
+void KDateEdit::dateSelected( TQDate date )
+{
+ if (assignDate( date ) ) {
+ updateView();
+ emit dateChanged( date );
+
+ if ( date.isValid() ) {
+ mPopup->hide();
+ }
+ }
+}
+
+void KDateEdit::dateEntered( TQDate date )
+{
+ if (assignDate( date ) ) {
+ updateView();
+ emit dateChanged( date );
+ }
+}
+
+void KDateEdit::lineEnterPressed()
+{
+ bool replaced = false;
+
+ TQDate date = parseDate( &replaced );
+
+ if (assignDate( date ) ) {
+ if ( replaced )
+ updateView();
+
+ emit dateChanged( date );
+ }
+}
+
+TQDate KDateEdit::parseDate( bool *replaced ) const
+{
+ TQString text = currentText();
+ TQDate result;
+
+ if ( replaced )
+ (*replaced) = false;
+
+ if ( text.isEmpty() )
+ result = TQDate();
+ else if ( mKeywordMap.contains( text.lower() ) ) {
+ TQDate today = TQDate::currentDate();
+ int i = mKeywordMap[ text.lower() ];
+ if ( i >= 100 ) {
+ /* A day name has been entered. Convert to offset from today.
+ * This uses some math tricks to figure out the offset in days
+ * to the next date the given day of the week occurs. There
+ * are two cases, that the new day is >= the current day, which means
+ * the new day has not occurred yet or that the new day < the current day,
+ * which means the new day is already passed (so we need to find the
+ * day in the next week).
+ */
+ i -= 100;
+ int currentDay = today.dayOfWeek();
+ if ( i >= currentDay )
+ i -= currentDay;
+ else
+ i += 7 - currentDay;
+ }
+
+ result = today.addDays( i );
+ if ( replaced )
+ (*replaced) = true;
+ } else {
+ result = TDEGlobal::locale()->readDate( text );
+ }
+
+ return result;
+}
+
+bool KDateEdit::eventFilter( TQObject *object, TQEvent *event )
+{
+ if ( object == lineEdit() ) {
+ // We only process the focus out event if the text has changed
+ // since we got focus
+ if ( (event->type() == TQEvent::FocusOut) && mTextChanged ) {
+ lineEnterPressed();
+ mTextChanged = false;
+ } else if ( event->type() == TQEvent::KeyPress ) {
+ // Up and down arrow keys step the date
+ TQKeyEvent* keyEvent = (TQKeyEvent*)event;
+
+ if ( keyEvent->key() == TQt::Key_Return ) {
+ lineEnterPressed();
+ return true;
+ }
+
+ int step = 0;
+ if ( keyEvent->key() == TQt::Key_Up )
+ step = 1;
+ else if ( keyEvent->key() == TQt::Key_Down )
+ step = -1;
+ if ( step && !mReadOnly ) {
+ TQDate date = parseDate();
+ if ( date.isValid() ) {
+ date = date.addDays( step );
+ if ( assignDate( date ) ) {
+ updateView();
+ emit dateChanged( date );
+ return true;
+ }
+ }
+ }
+ }
+ } else {
+ // It's a date picker event
+ switch ( event->type() ) {
+ case TQEvent::MouseButtonDblClick:
+ case TQEvent::MouseButtonPress: {
+ TQMouseEvent *mouseEvent = (TQMouseEvent*)event;
+ if ( !mPopup->rect().contains( mouseEvent->pos() ) ) {
+ TQPoint globalPos = mPopup->mapToGlobal( mouseEvent->pos() );
+ if ( TQApplication::widgetAt( globalPos, true ) == this ) {
+ // The date picker is being closed by a click on the
+ // KDateEdit widget. Avoid popping it up again immediately.
+ mDiscardNextMousePress = true;
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+void KDateEdit::mousePressEvent( TQMouseEvent *event )
+{
+ if ( event->button() == TQt::LeftButton && mDiscardNextMousePress ) {
+ mDiscardNextMousePress = false;
+ return;
+ }
+
+ TQComboBox::mousePressEvent( event );
+}
+
+void KDateEdit::slotTextChanged( const TQString& )
+{
+ TQDate date = parseDate();
+
+ if ( assignDate( date ) )
+ emit dateChanged( date );
+
+ mTextChanged = true;
+}
+
+void KDateEdit::setupKeywords()
+{
+ // Create the keyword list. This will be used to match against when the user
+ // enters information.
+ mKeywordMap.insert( i18n( "tomorrow" ), 1 );
+ mKeywordMap.insert( i18n( "today" ), 0 );
+ mKeywordMap.insert( i18n( "yesterday" ), -1 );
+
+ #if 0 //depends on KDE 3.2
+ TQString dayName;
+ for ( int i = 1; i <= 7; ++i ) {
+ dayName = TDEGlobal::locale()->calendar()->weekDayName( i ).lower();
+ mKeywordMap.insert( dayName, i + 100 );
+ }
+ #endif
+}
+
+bool KDateEdit::assignDate( const TQDate& date )
+{
+ mDate = date;
+ mTextChanged = false;
+ return true;
+}
+
+void KDateEdit::updateView()
+{
+ TQString dateString;
+ if ( mDate.isValid() )
+ dateString = TDEGlobal::locale()->formatDate( mDate, true );
+
+ // We do not want to generate a signal here,
+ // since we explicitly setting the date
+ bool blocked = signalsBlocked();
+ blockSignals( true );
+ changeItem( dateString, 0 );
+ blockSignals( blocked );
+}
+
+#include "kdateedit.moc"
diff --git a/src/widgets/kdateedit.h b/src/widgets/kdateedit.h
new file mode 100644
index 0000000..e3957b2
--- /dev/null
+++ b/src/widgets/kdateedit.h
@@ -0,0 +1,139 @@
+/*
+ This file is part of libtdepim.
+
+ Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
+ Copyright (c) 2004 Tobias Koenig <tokoe@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 Steet, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#ifndef KDATEEDIT_H
+#define KDATEEDIT_H
+
+#include <tqcombobox.h>
+#include <tqdatetime.h>
+#include <tqmap.h>
+
+#include "kdatepickerpopup.h"
+
+class TQEvent;
+
+/**
+ A date editing widget that consists of an editable combo box.
+ The combo box contains the date in text form, and clicking the combo
+ box arrow will display a 'popup' style date picker.
+
+ This widget also supports advanced features like allowing the user
+ to type in the day name to get the date. The following keywords
+ are supported (in the native language): tomorrow, yesturday, today,
+ monday, tuesday, wednesday, thursday, friday, saturday, sunday.
+
+ @image html kdateedit.png "This is how it looks"
+
+ @author Cornelius Schumacher <schumacher@kde.org>
+ @author Mike Pilone <mpilone@slac.com>
+ @author David Jarvie <software@astrojar.org.uk>
+ @author Tobias Koenig <tokoe@kde.org>
+*/
+class KDateEdit : public TQComboBox
+{
+ TQ_OBJECT
+
+ public:
+ KDateEdit( TQWidget *parent = 0, const char *name = 0 );
+ virtual ~KDateEdit();
+
+ /**
+ @return The date entered. This date could be invalid,
+ you have to check validity yourself.
+ */
+ TQDate date() const;
+
+ /**
+ Sets whether the widget is read-only for the user. If read-only,
+ the date picker pop-up is inactive, and the displayed date cannot be edited.
+
+ @param readOnly True to set the widget read-only, false to set it read-write.
+ */
+ void setReadOnly( bool readOnly );
+
+ /**
+ @return True if the widget is read-only, false if read-write.
+ */
+ bool isReadOnly() const;
+
+ virtual void popup();
+
+ signals:
+ /**
+ This signal is emitted whenever the user modifies the date.
+ The passed date can be invalid.
+ */
+ void dateChanged( const TQDate &date );
+
+ public slots:
+ /**
+ Sets the date.
+
+ @param date The new date to display. This date must be valid or
+ it will not be set
+ */
+ void setDate( const TQDate &date );
+
+ protected slots:
+ void lineEnterPressed();
+ void slotTextChanged( const TQString& );
+ void dateEntered( TQDate );
+ void dateSelected( TQDate );
+
+ protected:
+ virtual bool eventFilter( TQObject*, TQEvent* );
+ virtual void mousePressEvent( TQMouseEvent* );
+
+ /**
+ Sets the date, without altering the display.
+ This method is used internally to set the widget's date value.
+ As a virtual method, it allows derived classes to perform additional validation
+ on the date value before it is set. Derived classes should return true if
+ TQDate::isValid(@p date) returns false.
+
+ @param date The new date to set.
+ @return True if the date was set, false if it was considered invalid and
+ remains unchanged.
+ */
+ virtual bool assignDate( const TQDate &date );
+
+ /**
+ Fills the keyword map. Reimplement it if you want additional
+ keywords.
+ */
+ void setupKeywords();
+
+ private:
+ TQDate parseDate( bool* = 0 ) const;
+ void updateView();
+
+ KDatePickerPopup *mPopup;
+
+ TQDate mDate;
+ bool mReadOnly;
+ bool mTextChanged;
+ bool mDiscardNextMousePress;
+
+ TQMap<TQString, int> mKeywordMap;
+};
+
+#endif
diff --git a/src/widgets/kdatepickerpopup.cpp b/src/widgets/kdatepickerpopup.cpp
new file mode 100644
index 0000000..da68918
--- /dev/null
+++ b/src/widgets/kdatepickerpopup.cpp
@@ -0,0 +1,123 @@
+/*
+ This file is part of libtdepim.
+
+ Copyright (c) 2004 Bram Schoenmakers <bramschoenmakers@kde.nl>
+
+ 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 Steet, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+
+#include <tqdatetime.h>
+#include <tqpopupmenu.h>
+
+#include <tdelocale.h>
+
+#include "kdatepickerpopup.h"
+
+KDatePickerPopup::KDatePickerPopup( int items, const TQDate &date, TQWidget *parent,
+ const char *name )
+ : TQPopupMenu( parent, name )
+{
+ mItems = items;
+
+ mDatePicker = new KDatePicker( this );
+ mDatePicker->setCloseButton( false );
+
+ connect( mDatePicker, TQ_SIGNAL( dateEntered( TQDate ) ),
+ TQ_SLOT( slotDateChanged( TQDate ) ) );
+ connect( mDatePicker, TQ_SIGNAL( dateSelected( TQDate ) ),
+ TQ_SLOT( slotDateChanged( TQDate ) ) );
+
+ mDatePicker->setDate( date );
+
+ buildMenu();
+}
+
+void KDatePickerPopup::buildMenu()
+{
+ if ( isVisible() ) return;
+ clear();
+
+ if ( mItems & DatePicker ) {
+ insertItem( mDatePicker );
+
+ if ( ( mItems & NoDate ) || ( mItems & Words ) )
+ insertSeparator();
+ }
+
+ if ( mItems & Words ) {
+ insertItem( i18n("&Today"), this, TQ_SLOT( slotToday() ) );
+ insertItem( i18n("&Yesterday"), this, TQ_SLOT( slotYesterday() ) );
+ insertItem( i18n("Last &Week"), this, TQ_SLOT( slotLastWeek() ) );
+ insertItem( i18n("Last M&onth"), this, TQ_SLOT( slotLastMonth() ) );
+
+ if ( mItems & NoDate )
+ insertSeparator();
+ }
+
+ if ( mItems & NoDate )
+ insertItem( i18n("No Date"), this, TQ_SLOT( slotNoDate() ) );
+}
+
+KDatePicker *KDatePickerPopup::datePicker() const
+{
+ return mDatePicker;
+}
+
+void KDatePickerPopup::setDate( const TQDate &date )
+{
+ mDatePicker->setDate( date );
+}
+
+#if 0
+void KDatePickerPopup::setItems( int items )
+{
+ mItems = items;
+ buildMenu();
+}
+#endif
+
+void KDatePickerPopup::slotDateChanged( TQDate date )
+{
+ emit dateChanged( date );
+ hide();
+}
+
+void KDatePickerPopup::slotToday()
+{
+ emit dateChanged( TQDate::currentDate() );
+}
+
+void KDatePickerPopup::slotYesterday()
+{
+ emit dateChanged( TQDate::currentDate().addDays( -1 ) );
+}
+
+void KDatePickerPopup::slotNoDate()
+{
+ emit dateChanged( TQDate() );
+}
+
+void KDatePickerPopup::slotLastWeek()
+{
+ emit dateChanged( TQDate::currentDate().addDays( -7 ) );
+}
+
+void KDatePickerPopup::slotLastMonth()
+{
+ emit dateChanged( TQDate::currentDate().addMonths( -1 ) );
+}
+
+#include "kdatepickerpopup.moc"
diff --git a/src/widgets/kdatepickerpopup.h b/src/widgets/kdatepickerpopup.h
new file mode 100644
index 0000000..67f36dd
--- /dev/null
+++ b/src/widgets/kdatepickerpopup.h
@@ -0,0 +1,102 @@
+/*
+ This file is part of libtdepim.
+
+ Copyright (c) 2004 Bram Schoenmakers <bramschoenmakers@kde.nl>
+
+ 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 Steet, Fifth Floor,
+ Boston, MA 02110-1301 USA
+*/
+#ifndef KDATEPICKERPOPUP_H
+#define KDATEPICKERPOPUP_H
+
+#include <tqdatetime.h>
+#include <tqpopupmenu.h>
+
+#include <kdatepicker.h>
+
+/**
+ @short This menu helps the user to select a date quickly.
+
+ This menu helps the user to select a date tquicly. It offers various ways of selecting, e.g. with a KDatePicker or with words like "Tomorrow".
+
+ The available items are:
+
+ @li NoDate: A menu-item with "No Date". If choosen, the datepicker will emit a null TQDate.
+ @li DatePicker: Show a KDatePicker-widget.
+ @li Words: Show items like "Today", "Tomorrow" or "Next Week".
+
+ When supplying multiple items, separate each item with a bitwise OR.
+
+ @author Bram Schoenmakers <bram_s@softhome.net>
+*/
+class KDatePickerPopup: public TQPopupMenu
+{
+ TQ_OBJECT
+ public:
+ enum { NoDate = 1, DatePicker = 2, Words = 4 };
+
+ /**
+ A constructor for the KDatePickerPopup.
+
+ @param items List of all desirable items, separated with a bitwise OR.
+ @param date Initial date of datepicker-widget.
+ @param parent The object's parent.
+ @param name The object's name.
+ */
+ KDatePickerPopup( int items = 2, const TQDate &date = TQDate::currentDate(),
+ TQWidget *parent = 0, const char *name = 0 );
+
+ /**
+ @return A pointer to the private variable mDatePicker, an instance of
+ KDatePicker.
+ */
+ KDatePicker *datePicker() const;
+
+ void setDate( const TQDate &date );
+
+#if 0
+ /** Set items which should be shown and rebuilds the menu afterwards. Only if the menu is not visible.
+ @param items List of all desirable items, separated with a bitwise OR.
+ */
+ void setItems( int items = 1 );
+#endif
+ /** @return Returns the bitwise result of the active items in the popup. */
+ int items() const { return mItems; }
+
+ signals:
+
+ /**
+ This signal emits the new date (selected with datepicker or other
+ menu-items).
+ */
+ void dateChanged ( TQDate );
+
+ protected slots:
+ void slotDateChanged ( TQDate );
+
+ void slotToday();
+ void slotYesterday();
+ void slotLastWeek();
+ void slotLastMonth();
+ void slotNoDate();
+
+ private:
+ void buildMenu();
+
+ KDatePicker *mDatePicker;
+ int mItems;
+};
+
+#endif
diff --git a/src/widgets/krelistview.cpp b/src/widgets/krelistview.cpp
new file mode 100644
index 0000000..3c06216
--- /dev/null
+++ b/src/widgets/krelistview.cpp
@@ -0,0 +1,116 @@
+/***************************************************************************
+* Copyright (C) 2004-2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* Copyright (C) 2003 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "krelistview.h"
+
+#include <tdeglobalsettings.h>
+#include <tdelocale.h>
+#include <kdebug.h>
+
+#include "widgets/dblistviewbase.h"
+
+KreListView::KreListView( TQWidget *parent, const TQString &title, bool filter, int filterCol, TQWidget *embeddedWidget ) : TQVBox( parent )
+{
+
+ filteredColumn = filterCol;
+ TQWidget *header = this;
+ if ( filter || embeddedWidget ) {
+ header = new TQHBox( this );
+ ( ( TQHBox* ) header ) ->setSpacing( 15 );
+ }
+
+ if ( !title.isNull() ) {
+ listLabel = new TQLabel( header );
+ listLabel->setFrameShape( TQFrame::GroupBoxPanel );
+ listLabel->setFrameShadow( TQFrame::Sunken );
+ listLabel->setPaletteForegroundColor( TDEGlobalSettings::highlightedTextColor() );
+ listLabel->setPaletteBackgroundColor( TDEGlobalSettings::highlightColor().light( 120 ) ); // 120, to match the kremenu settings
+ listLabel->setText( title );
+
+ }
+
+ if ( filter ) {
+ filterBox = new TQHBox( header );
+ filterBox->setFrameShape( TQFrame::Box );
+ filterBox->setMargin( 2 );
+ filterLabel = new TQLabel( filterBox );
+ filterLabel->setText( i18n( "Search:" ) );
+ filterEdit = new KLineEdit( filterBox );
+ }
+
+
+ list = new TDEListView( this );
+ list->setSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding );
+ setSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding );
+ setSpacing( 10 );
+
+
+ // If the user provides a widget, embed it into the header
+ if ( embeddedWidget )
+ embeddedWidget->reparent( header, TQPoint( 0, 0 ) );
+ //Connect Signals & Slots
+ if ( filter ) {
+ connect( filterEdit, TQ_SIGNAL( textChanged( const TQString& ) ), TQ_SIGNAL( textChanged(const TQString&) ) );
+ connect( this, TQ_SIGNAL( textChanged( const TQString& ) ), TQ_SLOT( filter( const TQString& ) ) );
+ }
+}
+
+KreListView::~KreListView()
+{}
+
+void KreListView::filter( const TQString& s )
+{
+ for ( TQListViewItem * it = list->firstChild();it;it = it->nextSibling() ) {
+ if ( it->rtti() == NEXTLISTITEM_RTTI || it->rtti() == PREVLISTITEM_RTTI )
+ continue;
+
+ if ( s.isEmpty() ) // Don't filter if the filter text is empty
+ {
+ it->setVisible( true );
+ }
+ else
+ {
+
+ if ( it->text( filteredColumn ).contains( s, false ) )
+ it->setVisible( true );
+ else
+ it->setVisible( false );
+
+ }
+
+
+ }
+}
+
+void KreListView::refilter()
+{
+ if ( !filterEdit->text().isEmpty() ) {
+ emit textChanged( filterEdit->text() );
+ }
+}
+
+void KreListView::setCustomFilter( TQObject *receiver, const char *slot )
+{
+ connect( this, TQ_SIGNAL( textChanged( const TQString& ) ), receiver, slot );
+}
+
+void KreListView::setListView( DBListViewBase *list_view )
+{
+ delete list;
+
+ connect( list_view, TQ_SIGNAL( nextGroupLoaded() ), TQ_SLOT( refilter() ) );
+ connect( list_view, TQ_SIGNAL( prevGroupLoaded() ), TQ_SLOT( refilter() ) );
+ list = list_view;
+}
+
+#include "krelistview.moc"
diff --git a/src/widgets/krelistview.h b/src/widgets/krelistview.h
new file mode 100644
index 0000000..0884bda
--- /dev/null
+++ b/src/widgets/krelistview.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+* Copyright (C) 2003 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef KRELISTVIEW_H
+#define KRELISTVIEW_H
+
+#include <tqlabel.h>
+#include <tqvbox.h>
+#include <tdelistview.h>
+#include <klineedit.h>
+
+class DBListViewBase;
+
+/**
+@author Unai Garro
+*/
+
+class KreListView: public TQVBox
+{
+ TQ_OBJECT
+public:
+
+ KreListView( TQWidget *parent, const TQString &title = TQString::null, bool filter = false, int filterCol = 0, TQWidget *embeddedWidget = 0 );
+ ~KreListView();
+ TDEListView *listView()
+ {
+ return list;
+ }
+
+ void setListView( TDEListView *list_view )
+ {
+ delete list;
+ list = list_view;
+ }
+ void setListView( DBListViewBase *list_view );
+
+ void setCustomFilter( TQObject *receiver, const char *slot );
+ TQString filterText() const { return filterEdit->text(); }
+
+public slots:
+ void refilter();
+
+signals:
+ void textChanged( const TQString & );
+
+private:
+ TQHBox *filterBox;
+ TQLabel *listLabel;
+ int filteredColumn;
+ TQLabel *filterLabel;
+ KLineEdit *filterEdit;
+ TDEListView *list;
+
+private slots:
+ void filter( const TQString& s );
+};
+
+#endif
diff --git a/src/widgets/kremenu.cpp b/src/widgets/kremenu.cpp
new file mode 100644
index 0000000..365fba9
--- /dev/null
+++ b/src/widgets/kremenu.cpp
@@ -0,0 +1,631 @@
+/***************************************************************************
+* Copyright (C) 2003 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as publishfed by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+#include "kremenu.h"
+
+#include <tqbitmap.h>
+#include <tqcursor.h>
+#include <tqfont.h>
+#include <tqimage.h>
+#include <tqobjectlist.h>
+#include <tqpainter.h>
+#include <tqpixmap.h>
+#include <tqsignalmapper.h>
+
+#include <tdeapplication.h>
+#include <kcursor.h>
+#include <kdebug.h>
+#include <tdeglobalsettings.h>
+#include <kiconloader.h>
+#include <kimageeffect.h>
+#include <tdelocale.h>
+#include <kpixmap.h>
+#include <kpixmapeffect.h>
+
+KreMenu::KreMenu( TQWidget *parent, const char *name ) :
+ TQWidget( parent, name, TQt::WNoAutoErase )
+{
+ Menu newMenu;
+
+ mainMenuId = menus.append( newMenu );
+
+ currentMenuId = mainMenuId;
+ m_currentMenu = &( *currentMenuId );
+
+ setMouseTracking( true );
+ setFocusPolicy( TQWidget::StrongFocus );
+ setSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Preferred );
+}
+
+
+KreMenu::~KreMenu()
+{}
+
+void KreMenu::childEvent ( TQChildEvent *e )
+{
+ if ( e->type() == TQChildEvent::ChildInserted ) {
+
+ TQObject * child = e->child();
+ if ( child->inherits( "KreMenuButton" ) ) {
+ KreMenuButton * button = ( KreMenuButton* ) child;
+
+ Menu *buttonMenu = &( *( button->menuId ) );
+
+
+
+ if ( !( buttonMenu->activeButton ) ) // Highlight the button if it's the first in the menu
+ {
+ button->setActive( true );
+ buttonMenu->activeButton = button;
+ }
+
+ buttonMenu->addButton( button );
+
+ if ( buttonMenu != m_currentMenu )
+ button->hide();
+ else
+ button->show();
+
+ connect ( button, TQ_SIGNAL( clicked( KreMenuButton* ) ), this, TQ_SLOT( collectClicks( KreMenuButton* ) ) );
+ }
+ }
+ else if ( e->type() == TQChildEvent::ChildRemoved ) {
+ TQObject * child = e->child();
+
+ KreMenuButton *button = ( KreMenuButton* ) child;
+ if ( m_currentMenu->positionList.find( button ) != m_currentMenu->positionList.end() ) // Ensure that what was removed was a button
+ {
+ // Remove the button from the list first
+ int pos = m_currentMenu->positionList[ button ]; // FIXME: this works only if the button is removed from the main menu
+ m_currentMenu->widgetList.remove( pos ); // FIXME: this works only if the button is removed from the main menu
+ m_currentMenu->positionList.remove( button ); // FIXME: this works only if the button is removed from the main menu
+
+ // Now recalculate the position of the next button
+ ( m_currentMenu->widgetNumber ) --; // FIXME: this works only if the button is removed from the main menu
+
+ KreMenuButton *lastButton = m_currentMenu->widgetList[ ( m_currentMenu->widgetNumber ) - 1 ];
+ if ( lastButton )
+ m_currentMenu->childPos = lastButton->y() + lastButton->height();
+ m_currentMenu->activeButton = 0;
+
+ setMinimumWidth( minimumSizeHint().width() + 10 ); //update the minimum width
+ }
+
+ }
+ TQWidget::childEvent( e );
+}
+
+void KreMenu::collectClicks( KreMenuButton *w )
+{
+ setFocus();
+
+ highlightButton( w );
+
+ // Emit signal indicating button activation with button ID
+ KrePanel panel = w->getPanel();
+ emit clicked( panel );
+}
+
+MenuId KreMenu::createSubMenu( const TQString &title, const TQString &icon )
+{
+
+ // Create the new menu
+ Menu newMenu;
+ MenuId id = menus.append( newMenu );
+
+ // Add a button to the main menu for this submenu
+ TDEIconLoader il;
+ KreMenuButton *newMenuButton = new KreMenuButton( this );
+ newMenuButton->subMenuId = id;
+ newMenuButton->setTitle( title );
+ newMenuButton->setIconSet( il.loadIconSet( icon, TDEIcon::Panel ) );
+
+ // Add a button to the submenu to go back to the top menu
+ KreMenuButton *newSubMenuButton = new KreMenuButton( this );
+ newSubMenuButton->menuId = id;
+ newSubMenuButton->subMenuId = mainMenuId;
+ newSubMenuButton->setTitle( i18n( "Up" ) );
+ newSubMenuButton->setIconSet( il.loadIconSet( "1uparrow", TDEIcon::Panel ) );
+
+ connect( newMenuButton, TQ_SIGNAL( clicked( MenuId ) ), this, TQ_SLOT( showMenu( MenuId ) ) );
+ connect( newSubMenuButton, TQ_SIGNAL( clicked( MenuId ) ), this, TQ_SLOT( showMenu( MenuId ) ) );
+
+
+ return id;
+}
+
+void KreMenu::highlightButton( KreMenuButton *button )
+{
+ MenuId buttonMenuId = button->menuId;
+ Menu *buttonMenu = &( *buttonMenuId );
+
+ //Deactivate the old button
+ if ( buttonMenu->activeButton ) {
+ buttonMenu->activeButton->setActive( false );
+ buttonMenu->activeButton->update();
+ }
+
+ //Activate the new button
+
+ button->setActive( true );
+ button->update();
+ buttonMenu->activeButton = button;
+}
+
+void KreMenu::keyPressEvent( TQKeyEvent *e )
+{
+ switch ( e->key() ) {
+ case TQt::Key_Up: {
+ int current_index = m_currentMenu->positionList[ m_currentMenu->activeButton ];
+ if ( current_index > 0 ) {
+ highlightButton( m_currentMenu->widgetList[ current_index - 1 ] );
+
+ //simulate a mouse click
+ TQMouseEvent me( TQEvent::MouseButtonPress, TQPoint(), 0, 0 );
+ TDEApplication::sendEvent( m_currentMenu->activeButton, &me );
+
+ e->accept();
+ }
+ break;
+ }
+ case TQt::Key_Down: {
+ int current_index = m_currentMenu->positionList[ m_currentMenu->activeButton ];
+ if ( current_index < int( m_currentMenu->positionList.count() ) - 1 ) {
+ highlightButton( m_currentMenu->widgetList[ current_index + 1 ] );
+
+ //simulate a mouse click
+ TQMouseEvent me( TQEvent::MouseButtonPress, TQPoint(), 0, 0 );
+ TDEApplication::sendEvent( m_currentMenu->activeButton, &me );
+
+ e->accept();
+ }
+ break;
+ }
+ case TQt::Key_Enter:
+ case TQt::Key_Return:
+ case TQt::Key_Space: {
+ //simulate a mouse click
+ TQMouseEvent me( TQEvent::MouseButtonPress, TQPoint(), 0, 0 );
+ TDEApplication::sendEvent( m_currentMenu->activeButton, &me );
+
+ e->accept();
+ break;
+ }
+ default:
+ e->ignore();
+ }
+}
+
+TQSize KreMenu::sizeHint() const
+{
+ return minimumSizeHint();
+}
+
+//the minimum size hint will be the minimum size hint of the largest child
+TQSize KreMenu::minimumSizeHint() const
+{
+ int width = 30;
+
+ TQObjectList *childElements = queryList( 0, 0, false, false ); //only first-generation children (not recursive)
+ TQObjectListIterator it( *childElements );
+
+ TQObject *obj;
+ while ( ( obj = it.current() ) != 0 ) {
+ ++it;
+
+ if ( obj->isWidgetType() ) {
+ int obj_width_hint = ( ( TQWidget* ) obj ) ->minimumSizeHint().width();
+
+ if ( obj_width_hint > width )
+ width = obj_width_hint;
+ }
+ }
+
+ return TQSize( width, 150 );
+}
+
+void KreMenu::paintEvent( TQPaintEvent * )
+{
+ // Make sure the size is bigger than the minimum necessary
+ //if (minimumWidth() <45) setMinimumWidth(45); // FIXME: can somehow setMinimumWidth be restricted? This may not be the best place to do this
+
+ // Get gradient colors
+ TQColor c = colorGroup().button();
+ TQColor c1 = c.dark( 130 );
+ TQColor c2 = c.light( 120 );
+
+ // Draw the gradient
+ KPixmap kpm;
+ kpm.resize( size() );
+ KPixmapEffect::unbalancedGradient ( kpm, c2, c1, KPixmapEffect::HorizontalGradient, -150, -150 );
+
+ // Draw the handle
+ TQPainter painter( &kpm );
+ painter.setPen( c1 );
+ painter.drawLine( width() - 5, 20, width() - 5, height() - 20 );
+ painter.end();
+
+ //Set the border transparent using a mask
+ TQBitmap mask( kpm.size() );
+ mask.fill( TQt::color0 );
+ painter.begin( &mask );
+ painter.setPen( TQt::color1 );
+ painter.setBrush( TQt::color1 );
+ painter.drawRoundRect( 0, 0, width(), height(), ( int ) ( 2.0 / width() * height() ), 2 );
+ painter.end();
+ kpm.setMask( mask );
+
+ //Draw the border line
+ painter.begin( &kpm );
+ painter.setPen( c1 );
+ painter.drawRoundRect( 0, 0, width(), height(), ( int ) ( 2.0 / width() * height() ), 2 );
+
+ //Draw the top line bordering with the first button
+ if ( m_currentMenu->activeButton ) // draw only if there's a button
+ {
+ int w = m_currentMenu->activeButton->width();
+ painter.setPen( c1 );
+ painter.drawLine( w / 5, 8, w - 1, 8 );
+ painter.setPen( c2 );
+ painter.drawLine( w / 5, 9, w - 1, 9 );
+ }
+
+ painter.end();
+
+ // Copy the pixmap to the widget
+ bitBlt( this, 0, 0, &kpm );
+}
+
+void KreMenu::resizeEvent( TQResizeEvent* e )
+{
+ emit resized( ( e->size() ).width(), ( e->size() ).height() );
+}
+
+void KreMenu::showMenu( MenuId id )
+{
+
+ // Hide the buttons in the current menu
+ // and show the ones in the new menu
+
+ TQObjectList * childElements = queryList();
+ TQObjectListIterator it( *childElements );
+
+ TQObject *obj;
+ while ( ( obj = it.current() ) != 0 ) {
+ ++it;
+ if ( obj->inherits( "KreMenuButton" ) ) {
+ KreMenuButton * button = ( KreMenuButton* ) obj;
+ if ( button->menuId == currentMenuId )
+ button->hide();
+ else if ( button->menuId == id )
+ button->show();
+ }
+ }
+
+
+ // Set the new menu as current
+ currentMenuId = id;
+ m_currentMenu = &( *( currentMenuId ) );
+}
+
+
+
+
+
+KreMenuButton::KreMenuButton( KreMenu *parent, KrePanel _panel, MenuId id, const char *name ) :
+ TQWidget( parent, name, TQt::WNoAutoErase ),
+ panel( _panel )
+{
+ icon = 0;
+ highlighted = false;
+ text = TQString::null;
+
+ if ( id == 0 )
+ menuId = parent->mainMenu();
+ else
+ menuId = id;
+
+ subMenuId = 0; // By default it's not a submenu button
+
+ resize( parent->size().width(), 55 );
+ connect ( parent, TQ_SIGNAL( resized( int, int ) ), this, TQ_SLOT( rescale() ) );
+ connect( this, TQ_SIGNAL( clicked() ), this, TQ_SLOT( forwardClicks() ) );
+ setCursor( TQCursor( KCursor::handCursor() ) );
+}
+
+
+KreMenuButton::~KreMenuButton()
+{
+ delete icon;
+}
+
+void KreMenuButton::setTitle( const TQString &s )
+{
+ text = s;
+
+#if 0 //this causes problems for the button to go back to editing a recipe
+ //adjust text to two lines if needed
+ if ( fontMetrics().width( text ) > 110 ) {
+ text.replace( ' ', "\n" );
+ }
+#endif
+
+ setMinimumWidth( minimumSizeHint().width() );
+ if ( parentWidget() ->minimumWidth() < minimumSizeHint().width() )
+ parentWidget() ->setMinimumWidth( minimumSizeHint().width() + 10 );
+
+ update();
+}
+
+void KreMenuButton::mousePressEvent ( TQMouseEvent * )
+{
+ emit clicked();
+}
+
+void KreMenuButton::rescale()
+{
+ resize( parentWidget() ->width() - 10, height() );
+}
+TQSize KreMenuButton::sizeHint() const
+{
+ if ( parentWidget() )
+ return ( TQSize( parentWidget() ->size().width() - 10, 40 ) );
+ else
+ return TQSize( 100, 30 );
+}
+
+TQSize KreMenuButton::minimumSizeHint() const
+{
+ int text_width = TQMAX( fontMetrics().width( text.section( '\n', 0, 0 ) ), fontMetrics().width( text.section( '\n', 1, 1 ) ) );
+
+ if ( icon )
+ return TQSize( 40 + icon->width() + text_width, 30 );
+ else
+ return TQSize( 40 + text_width, 30 );
+}
+
+void KreMenuButton::paintEvent( TQPaintEvent * )
+{
+ if ( !isShown() )
+ return ;
+ // First draw the gradient
+ int darken = 130, lighten = 120;
+ TQColor c1, c2, c1h, c2h; //non-highlighted and highlighted versions
+
+ // Set the gradient colors
+
+ c1 = colorGroup().button().dark( darken );
+ c2 = colorGroup().button().light( lighten );
+
+ if ( highlighted ) {
+ darken -= 10;
+ lighten += 10;
+ c1h = TDEGlobalSettings::highlightColor().dark( darken );
+ c2h = TDEGlobalSettings::highlightColor().light( lighten );
+ }
+
+ // draw the gradient now
+
+ TQPainter painter;
+ KPixmap kpm;
+ kpm.resize( ( ( TQWidget * ) parent() ) ->size() ); // use parent's same size to obtain the same gradient
+
+ if ( !highlighted ) {
+
+ // first the gradient
+ KPixmapEffect::unbalancedGradient ( kpm, c2, c1, KPixmapEffect::HorizontalGradient, -150, -150 );
+
+ }
+ else {
+
+ // top gradient (highlighted)
+ kpm.resize( width(), height() );
+ KPixmapEffect::unbalancedGradient ( kpm, c2h, c1h, KPixmapEffect::HorizontalGradient, -150, -150 );
+ // low gradient besides the line (not hightlighted)
+ KPixmap kpmb;
+ kpmb.resize( width(), 2 );
+ KPixmapEffect::unbalancedGradient ( kpmb, c2, c1, KPixmapEffect::HorizontalGradient, -150, -150 );
+ // mix the two
+ bitBlt( &kpm, 0, height() - 2, &kpmb );
+
+ }
+
+ // Draw the line
+ painter.begin( &kpm );
+ painter.setPen( colorGroup().button().dark( darken ) );
+ painter.drawLine( width() / 5, height() - 2, width() - 1, height() - 2 );
+ painter.setPen( colorGroup().button().light( lighten ) );
+ painter.drawLine( width() / 5, height() - 1, width() - 1, height() - 1 );
+ painter.end();
+
+
+ // Now Add the icon
+
+ painter.begin( &kpm );
+ int xPos, yPos;
+ if ( icon ) {
+ // Set the icon's desired horizontal position
+
+ xPos = 10;
+ yPos = 0;
+
+
+ // Make sure it fits in the area
+ // If not, resize and reposition horizontally to be centered
+
+ TQPixmap scaledIcon = *icon;
+
+ if ( ( icon->height() > height() ) || ( icon->width() > width() / 3 ) ) // Nice effect, make sure you take less than half in width and fit in height (try making the menu very short in width)
+ {
+ TQImage image;
+ image = ( *icon );
+ scaledIcon.convertFromImage( image.smoothScale( width() / 3, height(), TQImage::ScaleMin ) );
+ }
+
+ // Calculate the icon's vertical position
+
+ yPos = ( height() - scaledIcon.height() ) / 2 - 1;
+
+
+ // Now draw it
+
+ painter.drawPixmap( xPos, yPos, scaledIcon );
+
+ xPos += scaledIcon.width(); // increase it to place the text area correctly
+ }
+
+ painter.end();
+
+ // If it's highlighted, draw a rounded area around the text
+
+ // Calculate the rounded area
+
+ int areax = xPos + 10;
+ int areah = fontMetrics().height() * ( text.contains( '\n' ) + 1 ) + fontMetrics().lineSpacing() * text.contains( '\n' ) + 6; // Make sure the area is sensible for text and adjust for multiple lines
+
+ int areaw = width() - areax - 10;
+
+ if ( areah > ( height() - 4 ) ) {
+ areah = height() - 4; // Limit to button height
+ }
+
+ int areay = ( height() - areah - 2 ) / 2 + 1; // Center the area vertically
+
+
+ // Calculate roundness
+
+ int roundy = 99, roundx = ( int ) ( ( float ) roundy * areah / areaw ); //Make corners round
+
+
+ if ( highlighted && areaw > 0 ) // If there is no space for the text area do not draw it
+ {
+
+ // Draw the gradient
+ KPixmap area;
+ area.resize( areaw, areah );
+
+
+ KPixmapEffect::gradient( area, c2h.light( 150 ), c1h.light( 150 ), KPixmapEffect::VerticalGradient );
+
+ painter.begin( &area );
+ painter.setPen( c1h );
+ painter.setBrush( TQt::NoBrush );
+ painter.drawRoundRect( 0, 0, areaw, areah, roundx, roundy );
+ painter.end();
+
+ // Make it round
+ TQBitmap mask( TQSize( areaw, areah ) );
+ mask.fill( TQt::color0 );
+ painter.begin( &mask );
+ painter.setPen( TQt::color1 );
+ painter.setBrush( TQt::color1 );
+ painter.drawRoundRect( 0, 0, areaw, areah, roundx, roundy );
+ painter.end();
+ area.setMask( mask );
+
+ // Copy it to the button
+ bitBlt( &kpm, areax, areay, &area );
+ }
+
+ // Finally, draw the text besides the icon
+ TQRect r = rect();
+ r.setLeft( areax + 5 );
+ r.setWidth( areaw - 10 );
+
+ painter.begin( &kpm );
+ if ( highlighted )
+ painter.setPen( TDEGlobalSettings::highlightedTextColor() );
+ else
+ painter.setPen( TDEGlobalSettings::textColor() );
+ painter.setClipRect( r );
+ painter.drawText( r, TQt::AlignVCenter, text );
+ painter.end();
+
+ // Copy the offscreen button to the widget
+ bitBlt( this, 0, 0, &kpm, 0, 0, width(), height() ); // Copy the image with correct button size (button is already smaller than parent in width to leave space for the handle, so no need to use -10)
+
+}
+
+void KreMenuButton::setIconSet( const TQIconSet &is )
+{
+ delete icon;
+
+ icon = new TQPixmap( is.pixmap( TQIconSet::Small, TQIconSet::Normal, TQIconSet::On ) );
+
+ setMinimumWidth( minimumSizeHint().width() );
+ if ( parentWidget() ->minimumWidth() < minimumSizeHint().width() )
+ parentWidget() ->setMinimumWidth( minimumSizeHint().width() + 10 );
+}
+
+Menu::Menu( void )
+{
+ childPos = 10; // Initial button is on top (10px), then keep scrolling down
+ widgetNumber = 0; // Initially we have no buttons
+ activeButton = 0; // Button that is highlighted
+}
+
+
+Menu::Menu( const Menu &m )
+{
+ activeButton = m.activeButton;
+ childPos = m.childPos;
+ widgetNumber = m.widgetNumber;
+
+ copyMap( positionList, m.positionList );
+ copyMap( widgetList, m.widgetList );
+}
+
+Menu::~Menu( void )
+{}
+
+Menu& Menu::operator=( const Menu &m )
+{
+
+ activeButton = m.activeButton;
+ childPos = m.childPos;
+ widgetNumber = m.widgetNumber;
+
+ copyMap( positionList, m.positionList );
+ copyMap( widgetList, m.widgetList );
+
+ return *this;
+}
+
+
+void Menu::addButton( KreMenuButton* button )
+{
+ button->move( 0, childPos );
+ button->rescale();
+ childPos += button->height();
+ positionList[ button ] = widgetNumber; // Store index for this widget, and increment number
+ widgetList[ widgetNumber ] = button; // Store the button in the list (the inverse mapping of the previous one)
+ widgetNumber++;
+}
+
+void Menu::copyMap( TQMap <int, KreMenuButton*> &destMap, const TQMap <int, KreMenuButton*> &origMap )
+{
+ TQMap<int, KreMenuButton*>::ConstIterator it;
+ destMap.clear();
+ for ( it = origMap.begin(); it != origMap.end(); ++it ) {
+ destMap[ it.key() ] = it.data();
+ }
+}
+
+void Menu::copyMap( TQMap <KreMenuButton*, int> &destMap, const TQMap <KreMenuButton*, int> &origMap )
+{
+ TQMap<KreMenuButton*, int>::ConstIterator it;
+ destMap.clear();
+ for ( it = origMap.begin(); it != origMap.end(); ++it ) {
+ destMap[ it.key() ] = it.data();
+ }
+}
+
+#include "kremenu.moc"
diff --git a/src/widgets/kremenu.h b/src/widgets/kremenu.h
new file mode 100644
index 0000000..4b02632
--- /dev/null
+++ b/src/widgets/kremenu.h
@@ -0,0 +1,170 @@
+/***************************************************************************
+* Copyright (C) 2003 by *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef KREMENU_H
+#define KREMENU_H
+
+#include <tqbuttongroup.h>
+#include <tqevent.h>
+#include <tqiconset.h>
+#include <tqmap.h>
+#include <tqpushbutton.h>
+#include <tqstring.h>
+
+#include "krecipesview.h" //for KrePanel enum
+
+
+/**
+* @author Unai Garro
+* @author Bosselut Cyril
+*/
+
+class Menu;
+class KreMenu;
+class KreMenuButton;
+typedef TQValueList <Menu>::Iterator MenuId;
+
+
+class Menu
+{
+public:
+ // Methods
+
+ Menu( void );
+ Menu( const Menu &m );
+ ~Menu( void );
+ void addButton( KreMenuButton *button );
+ Menu& operator=( const Menu &m );
+
+ // Variables
+
+ TQMap <KreMenuButton*, int> positionList; // Stores the indexes for the widgets
+ TQMap <int, KreMenuButton*> widgetList; // Stores the widgets for each position (just the inverse mapping)
+ KreMenuButton* activeButton; // Indicates which button is highlighted
+ int childPos;
+ int widgetNumber;
+private:
+ // Methods
+ void copyMap( TQMap <int, KreMenuButton*> &destMap, const TQMap <int, KreMenuButton*> &origMap );
+ void copyMap( TQMap <KreMenuButton*, int> &destMap, const TQMap <KreMenuButton*, int> &origMap );
+};
+
+
+class KreMenu : public TQWidget
+{
+ TQ_OBJECT
+public:
+ KreMenu( TQWidget *parent = 0, const char *name = 0 );
+ ~KreMenu();
+
+ MenuId createSubMenu( const TQString &title, const TQString &icon );
+ MenuId mainMenu( void )
+ {
+ return mainMenuId;
+ }
+ MenuId currentMenu( void )
+ {
+ return currentMenuId;
+ }
+ TQSize sizeHint() const;
+ TQSize minimumSizeHint() const;
+ void resizeEvent( TQResizeEvent* e );
+ void highlightButton( KreMenuButton *button );
+
+
+protected:
+
+ virtual void paintEvent ( TQPaintEvent *e );
+ virtual void childEvent ( TQChildEvent *e );
+ virtual void keyPressEvent( TQKeyEvent *e );
+
+private:
+ //Variables
+ TQValueList <Menu> menus;
+ MenuId mainMenuId;
+ MenuId currentMenuId;
+ Menu *m_currentMenu;
+
+signals:
+ void resized( int, int );
+ void clicked( KrePanel );
+
+private slots:
+ void collectClicks( KreMenuButton *w );
+ void showMenu( MenuId id );
+
+};
+
+class KreMenuButton: public TQWidget
+{
+ TQ_OBJECT
+public:
+ KreMenuButton( KreMenu *parent, KrePanel panel = KrePanel( -1 ), MenuId id = 0, const char *name = 0 );
+
+ ~KreMenuButton();
+
+ TQSize sizeHint() const;
+ TQSize minimumSizeHint() const;
+
+ TQString title( void )
+ {
+ return text;
+ }
+ void setActive( bool active = true )
+ {
+ highlighted = active;
+ }
+ void setIconSet( const TQIconSet &is );
+ MenuId menuId;
+ MenuId subMenuId;
+
+ KrePanel getPanel() const
+ {
+ return panel;
+ }
+
+signals:
+ void resized( int, int );
+ void clicked( void );
+ void clicked( KreMenuButton* ); // sent together with clicked()
+ void clicked( MenuId ); // sent together with clicked()
+
+public slots:
+ void setTitle( const TQString &s );
+ void rescale( void );
+
+private:
+ // Button parts
+ TQPixmap* icon;
+ TQString text;
+ bool highlighted;
+
+ KrePanel panel;
+
+private slots:
+
+ void forwardClicks( void )
+ {
+ emit clicked( this );
+ if ( subMenuId != 0 )
+ emit clicked( subMenuId );
+ }
+
+protected:
+
+ virtual void paintEvent( TQPaintEvent *e );
+ virtual void mousePressEvent ( TQMouseEvent * e );
+
+};
+
+
+
+#endif
diff --git a/src/widgets/kreruler.cpp b/src/widgets/kreruler.cpp
new file mode 100644
index 0000000..448a375
--- /dev/null
+++ b/src/widgets/kreruler.cpp
@@ -0,0 +1,1049 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
+ Copyright (C) 2005 Jason Kivlighn <jkivlighn@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Description: Ruler (header)
+
+/******************************************************************/
+
+#include "kreruler.h"
+
+#include <tdelocale.h>
+#include <tdeglobalsettings.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <tqcursor.h>
+#include <tqpainter.h>
+#include <tqpopupmenu.h>
+#include <tqtooltip.h>
+
+#include "krepagelayout.h"
+
+class KoRulerPrivate {
+public:
+ KoRulerPrivate() {
+ }
+ ~KoRulerPrivate() {}
+
+ TQWidget *canvas;
+ int flags;
+ int oldMx, oldMy;
+ bool whileMovingBorderLeft, whileMovingBorderRight;
+ bool whileMovingBorderTop, whileMovingBorderBottom;
+ TQPixmap pmFirst, pmLeft;
+ KoPageLayout layout;
+ KoTabulatorList tabList;
+ // Do we have to remove a certain tab in the DC Event?
+ KoTabulator removeTab;
+ // The tab we're moving / clicking on - basically only valid between press and release time
+ KoTabulator currTab;
+ // The action we're currently doing - basically only valid between press and release time
+ KoRuler::Action action;
+ TQPopupMenu *rb_menu;
+ int mRemoveTab, mPageLayout; // menu item ids
+ int frameEnd;
+ double i_right;
+ bool m_bReadWrite;
+ bool doubleClickedIndent;
+ bool rtl;
+ bool mousePressed;
+};
+
+// Equality test for tab positions in particular
+static inline bool equals( double a, double b ) {
+ return kAbs( a - b ) < 1E-4;
+}
+
+
+/******************************************************************/
+/* Class: KoRuler */
+/******************************************************************/
+
+const int KoRuler::F_TABS = 1;
+const int KoRuler::F_INDENTS = 2;
+const int KoRuler::F_HELPLINES = 4;
+const int KoRuler::F_NORESIZE = 8;
+
+/*================================================================*/
+KoRuler::KoRuler( TQWidget *_parent, TQWidget *_canvas, Orientation _orientation,
+ const KoPageLayout& _layout, int _flags, KoUnit::Unit _unit )
+ : TQFrame( _parent ), buffer( width(), height() ), m_zoom(1.0), m_1_zoom(1.0),
+ m_unit( _unit )
+{
+ setWFlags( WResizeNoErase | WRepaintNoErase );
+ setFrameStyle( MenuBarPanel );
+
+ d=new KoRulerPrivate();
+
+ d->canvas = _canvas;
+ orientation = _orientation;
+ d->layout = _layout;
+ d->flags = _flags;
+
+ d->m_bReadWrite=true;
+ d->doubleClickedIndent=false;
+ diffx = 0;
+ diffy = 0;
+ i_left=0.0;
+ i_first=0.0;
+ d->i_right=0.0;
+
+ setMouseTracking( true );
+ d->mousePressed = false;
+ d->action = A_NONE;
+
+ d->oldMx = 0;
+ d->oldMy = 0;
+ d->rtl = false;
+
+ showMPos = false;
+ mposX = 0;
+ mposY = 0;
+ gridSize=0.0;
+ hasToDelete = false;
+ d->whileMovingBorderLeft = d->whileMovingBorderRight = d->whileMovingBorderTop = d->whileMovingBorderBottom = false;
+
+ d->pmFirst = UserIcon( "koRulerFirst" );
+ d->pmLeft = UserIcon( "koRulerLeft" );
+ d->currTab.type = T_INVALID;
+
+ d->removeTab.type = T_INVALID;
+ if ( orientation == TQt::Horizontal ) {
+ frameStart = tqRound( zoomIt(d->layout.ptLeft) );
+ d->frameEnd = tqRound( zoomIt(d->layout.ptWidth - d->layout.ptRight) );
+ } else {
+ frameStart = tqRound( zoomIt(d->layout.ptTop) );
+ d->frameEnd = tqRound( zoomIt(d->layout.ptHeight - d->layout.ptBottom) );
+ }
+ m_bFrameStartSet = false;
+
+ setupMenu();
+
+ // For compatibility, emitting doubleClicked shall emit openPageLayoutDia
+ connect( this, TQ_SIGNAL( doubleClicked() ), this, TQ_SIGNAL( openPageLayoutDia() ) );
+}
+
+/*================================================================*/
+KoRuler::~KoRuler()
+{
+ delete d->rb_menu;
+ delete d;
+}
+
+void KoRuler::setPageLayoutMenuItemEnabled(bool b)
+{
+ d->rb_menu->setItemEnabled(d->mPageLayout, b);
+}
+
+/*================================================================*/
+void KoRuler::setMousePos( int mx, int my )
+{
+ if ( !showMPos || ( mx == mposX && my == mposY ) ) return;
+
+ TQPainter p( this );
+ p.setRasterOp( TQt::NotROP );
+
+ if ( orientation == TQt::Horizontal ) {
+ if ( hasToDelete )
+ p.drawLine( mposX, 1, mposX, height() - 1 );
+ p.drawLine( mx, 1, mx, height() - 1 );
+ hasToDelete = true;
+ }
+ else {
+ if ( hasToDelete )
+ p.drawLine( 1, mposY, width() - 1, mposY );
+ p.drawLine( 1, my, width() - 1, my );
+ hasToDelete = true;
+ }
+ p.end();
+
+ mposX = mx;
+ mposY = my;
+}
+
+// distance between the main lines (those with a number)
+double KoRuler::lineDistance() const
+{
+ switch( m_unit ) {
+ case KoUnit::U_INCH:
+ return INCH_TO_POINT( m_zoom ); // every inch
+ case KoUnit::U_PT:
+ return 100.0 * m_zoom; // every 100 pt
+ case KoUnit::U_MM:
+ case KoUnit::U_CM:
+ case KoUnit::U_DM:
+ return CM_TO_POINT ( m_zoom ); // every cm
+ case KoUnit::U_PI:
+ return PI_TO_POINT ( 10.0 * m_zoom ); // every 10 pica
+ case KoUnit::U_DD:
+ return DD_TO_POINT( m_zoom ); // every diderot
+ case KoUnit::U_CC:
+ return CC_TO_POINT( 10.0 * m_zoom ); // every 10 cicero
+ }
+ // should never end up here
+ return 100.0 * m_zoom;
+}
+
+/*================================================================*/
+void KoRuler::drawHorizontal( TQPainter *_painter )
+{
+ TQFont font = TDEGlobalSettings::toolBarFont();
+ TQFontMetrics fm( font );
+ resize( width(), TQMAX( fm.height() + 4, 20 ) );
+
+ // Use a double-buffer pixmap
+ TQPainter p( &buffer );
+ p.fillRect( 0, 0, width(), height(), TQBrush( colorGroup().brush( TQColorGroup::Background ) ) );
+
+ int totalw = tqRound( zoomIt(d->layout.ptWidth) );
+ TQString str;
+
+ p.setBrush( colorGroup().brush( TQColorGroup::Base ) );
+
+ // Draw white rect
+ TQRect r;
+ if ( !d->whileMovingBorderLeft )
+ r.setLeft( -diffx + frameStart );
+ else
+ r.setLeft( d->oldMx );
+ r.setTop( 0 );
+ if ( !d->whileMovingBorderRight )
+ r.setWidth(d->frameEnd-frameStart);
+ else
+ r.setRight( d->oldMx );
+ r.setBottom( height() );
+
+ p.drawRect( r );
+ p.setFont( font );
+
+ // Draw the numbers
+ double dist = lineDistance();
+ int maxwidth = 0;
+
+ for ( double i = 0.0;i <= (double)totalw;i += dist ) {
+ str = TQString::number( KoUnit::toUserValue( i / m_zoom, m_unit ) );
+ int textwidth = fm.width( str );
+ maxwidth = TQMAX( maxwidth, textwidth );
+ }
+
+ // Make sure that the ruler stays readable at lower zoom levels
+ while( dist <= maxwidth ) {
+ dist += lineDistance();
+ }
+
+ for ( double i = 0.0;i <= (double)totalw;i += dist ) {
+ str = TQString::number( KoUnit::toUserValue( i / m_zoom, m_unit ) );
+ int textwidth = fm.width( str );
+ maxwidth = TQMAX( maxwidth, textwidth );
+ p.drawText( tqRound(i) - diffx - tqRound(textwidth * 0.5),
+ tqRound(( height() - fm.height() ) * 0.5),
+ textwidth, height(), AlignLeft | AlignTop, str );
+ }
+
+ // Draw the medium-sized lines
+ // Only if we have enough space (i.e. not at 33%)
+ if ( dist > maxwidth + 2 )
+ {
+ for ( double i = dist * 0.5;i <= (double)totalw;i += dist ) {
+ int ii=tqRound(i);
+ p.drawLine( ii - diffx, 7, ii - diffx, height() - 7 );
+ }
+ }
+
+ // Draw the small lines
+ // Only if we have enough space (i.e. not at 33%)
+ if ( dist * 0.5 > maxwidth + 2 )
+ {
+ for ( double i = dist * 0.25;i <= (double)totalw;i += dist * 0.5 ) {
+ int ii=tqRound(i);
+ p.drawLine( ii - diffx, 9, ii - diffx, height() - 9 );
+ }
+ }
+
+ // Draw ending bar (at page width)
+ //int constant=zoomIt(1);
+ //p.drawLine( totalw - diffx + constant, 1, totalw - diffx + constant, height() - 1 );
+ //p.setPen( colorGroup().color( TQColorGroup::Base ) );
+ //p.drawLine( totalw - diffx, 1, totalw - diffx, height() - 1 );
+
+ // Draw starting bar (at 0)
+ //p.setPen( colorGroup().color( TQColorGroup::Text ) );
+ //p.drawLine( -diffx, 1, -diffx, height() - 1 );
+ //p.setPen( colorGroup().color( TQColorGroup::Base ) );
+ //p.drawLine( -diffx - constant, 1, -diffx - constant, height() - 1 );
+
+ // Show the mouse position
+ if ( d->action == A_NONE && showMPos ) {
+ p.setPen( colorGroup().color( TQColorGroup::Text ) );
+ p.drawLine( mposX, 1, mposX, height() - 1 );
+ }
+ hasToDelete = false;
+
+ p.end();
+ _painter->drawPixmap( 0, 0, buffer );
+}
+
+
+/*================================================================*/
+void KoRuler::drawVertical( TQPainter *_painter )
+{
+ TQFont font = TDEGlobalSettings::toolBarFont();
+ TQFontMetrics fm( font );
+ resize( TQMAX( fm.height() + 4, 20 ), height() );
+
+ TQPainter p( &buffer );
+ p.fillRect( 0, 0, width(), height(), TQBrush( colorGroup().brush( TQColorGroup::Background ) ) );
+
+ int totalh = tqRound( zoomIt(d->layout.ptHeight) );
+ // Clip rect - this gives basically always a rect like (2,2,width-2,height-2)
+ TQRect paintRect = _painter->clipRegion( TQPainter::CoordPainter ).boundingRect();
+ // Ruler rect
+ TQRect rulerRect( 0, -diffy, width(), totalh );
+
+ if ( paintRect.intersects( rulerRect ) ) {
+ TQString str;
+
+ p.setBrush( colorGroup().brush( TQColorGroup::Base ) );
+
+ // Draw white rect
+ TQRect r;
+ if ( !d->whileMovingBorderTop )
+ r.setTop( -diffy + frameStart );
+ else
+ r.setTop( d->oldMy );
+ r.setLeft( 0 );
+ if ( !d->whileMovingBorderBottom )
+ r.setHeight(d->frameEnd-frameStart);
+ else
+ r.setBottom( d->oldMy );
+ r.setRight( width() );
+
+ p.drawRect( r );
+ p.setFont( font );
+
+ // Draw the numbers
+ double dist = lineDistance();
+ int maxheight = 0;
+
+ for ( double i = 0.0;i <= (double)totalh;i += dist ) {
+ str = TQString::number( KoUnit::toUserValue( i / m_zoom, m_unit ) );
+ int textwidth = fm.width( str );
+ maxheight = TQMAX( maxheight, textwidth );
+ }
+
+ // Make sure that the ruler stays readable at lower zoom levels
+ while( dist <= maxheight ) {
+ dist += lineDistance();
+ }
+
+ for ( double i = 0.0;i <= (double)totalh;i += dist ) {
+ str = TQString::number( KoUnit::toUserValue( i / m_zoom, m_unit ) );
+ int textheight = fm.height();
+ int textwidth = fm.width( str );
+ maxheight = TQMAX( maxheight, textwidth );
+ p.save();
+ p.translate( tqRound(( width() - textheight ) * 0.5),
+ tqRound(i) - diffy + tqRound(textwidth * 0.5) );
+ p.rotate( -90 );
+ p.drawText( 0, 0, textwidth + 1, textheight, AlignLeft | AlignTop, str );
+ p.restore();
+ }
+
+ // Draw the medium-sized lines
+ if ( dist > maxheight + 2 )
+ {
+ for ( double i = dist * 0.5;i <= (double)totalh;i += dist ) {
+ int ii=tqRound(i);
+ p.drawLine( 7, ii - diffy, width() - 7, ii - diffy );
+ }
+ }
+
+ // Draw the small lines
+ if ( dist * 0.5 > maxheight + 2 )
+ {
+ for ( double i = dist * 0.25;i <=(double)totalh;i += dist *0.5 ) {
+ int ii=tqRound(i);
+ p.drawLine( 9, ii - diffy, width() - 9, ii - diffy );
+ }
+ }
+
+ // Draw ending bar (at page height)
+ //p.drawLine( 1, totalh - diffy + 1, width() - 1, totalh - diffy + 1 );
+ //p.setPen( colorGroup().color( TQColorGroup::Base ) );
+ //p.drawLine( 1, totalh - diffy, width() - 1, totalh - diffy );
+
+ // Draw starting bar (at 0)
+ //p.setPen( colorGroup().color( TQColorGroup::Text ) );
+ //p.drawLine( 1, -diffy, width() - 1, -diffy );
+ //p.setPen( colorGroup().color( TQColorGroup::Base ) );
+ //p.drawLine( 1, -diffy - 1, width() - 1, -diffy - 1 );
+ }
+
+ // Show the mouse position
+ if ( d->action == A_NONE && showMPos ) {
+ p.setPen( colorGroup().color( TQColorGroup::Text ) );
+ p.drawLine( 1, mposY, width() - 1, mposY );
+ }
+ hasToDelete = false;
+
+ p.end();
+ _painter->drawPixmap( 0, 0, buffer );
+}
+
+void KoRuler::mousePressEvent( TQMouseEvent *e )
+{
+ if( !d->m_bReadWrite)
+ return;
+
+ d->oldMx = e->x();
+ d->oldMy = e->y();
+ d->mousePressed = true;
+ d->removeTab.type = T_INVALID;
+
+ switch ( e->button() ) {
+ case RightButton:
+ if(d->currTab.type == T_INVALID || !(d->flags & F_TABS))
+ d->rb_menu->setItemEnabled(d->mRemoveTab, false);
+ else
+ d->rb_menu->setItemEnabled(d->mRemoveTab, true);
+ d->rb_menu->popup( TQCursor::pos() );
+ d->action = A_NONE;
+ d->mousePressed = false;
+ return;
+ case MidButton:
+ // MMB shall do like double-click (it opens a dialog).
+ handleDoubleClick();
+ return;
+ case LeftButton:
+ if ( d->action == A_BR_RIGHT || d->action == A_BR_LEFT ) {
+ if ( d->action == A_BR_RIGHT )
+ d->whileMovingBorderRight = true;
+ else
+ d->whileMovingBorderLeft = true;
+
+ if ( d->canvas )
+ drawLine(d->oldMx, -1);
+ update();
+ } else if ( d->action == A_BR_TOP || d->action == A_BR_BOTTOM ) {
+ if ( d->action == A_BR_TOP )
+ d->whileMovingBorderTop = true;
+ else
+ d->whileMovingBorderBottom = true;
+
+ if ( d->canvas ) {
+ TQPainter p( d->canvas );
+ p.setRasterOp( TQt::NotROP );
+ p.drawLine( 0, d->oldMy, d->canvas->width(), d->oldMy );
+ p.end();
+ }
+ update();
+ } else if ( d->action == A_FIRST_INDENT || d->action == A_LEFT_INDENT || d->action == A_RIGHT_INDENT ) {
+ if ( d->canvas )
+ drawLine(d->oldMx, -1);
+ } else if ( d->action == A_TAB ) {
+ if ( d->canvas && d->currTab.type != T_INVALID ) {
+ drawLine( tqRound( applyRtlAndZoom(d->currTab.ptPos) ) + frameStart - diffx, -1 );
+ }
+ }
+ else if ( d->flags & F_HELPLINES )
+ {
+ setCursor( orientation == TQt::Horizontal ?
+ TQt::sizeVerCursor : TQt::sizeHorCursor );
+ d->action = A_HELPLINES;
+ }
+ default:
+ break;
+ }
+}
+
+void KoRuler::mouseReleaseEvent( TQMouseEvent *e )
+{
+ d->mousePressed = false;
+
+ // Hacky, but necessary to prevent multiple tabs with the same coordinates (Werner)
+ bool fakeMovement=false;
+ if(d->removeTab.type != T_INVALID) {
+ mouseMoveEvent(e);
+ fakeMovement=true;
+ }
+
+ if ( d->action == A_BR_RIGHT || d->action == A_BR_LEFT ) {
+ d->whileMovingBorderRight = false;
+ d->whileMovingBorderLeft = false;
+
+ if ( d->canvas )
+ drawLine(d->oldMx, -1);
+ update();
+ emit newPageLayout( d->layout );
+ } else if ( d->action == A_BR_TOP || d->action == A_BR_BOTTOM ) {
+ d->whileMovingBorderTop = false;
+ d->whileMovingBorderBottom = false;
+
+ if ( d->canvas ) {
+ TQPainter p( d->canvas );
+ p.setRasterOp( TQt::NotROP );
+ p.drawLine( 0, d->oldMy, d->canvas->width(), d->oldMy );
+ p.end();
+ }
+ update();
+ emit newPageLayout( d->layout );
+ } else if ( d->action == A_FIRST_INDENT ) {
+ if ( d->canvas )
+ drawLine(d->oldMx, -1);
+ update();
+ emit newFirstIndent( i_first );
+ } else if ( d->action == A_LEFT_INDENT ) {
+ if ( d->canvas )
+ drawLine(d->oldMx, -1);
+ update();
+ emit newLeftIndent( i_left );
+ } else if ( d->action == A_RIGHT_INDENT ) {
+ if ( d->canvas )
+ drawLine(d->oldMx, -1);
+ update();
+ emit newRightIndent( d->i_right );
+ } else if ( d->action == A_TAB ) {
+ if ( d->canvas && !fakeMovement ) {
+ drawLine( tqRound( applyRtlAndZoom( d->currTab.ptPos ) ) + frameStart - diffx, -1);
+ }
+ if ( willRemoveTab( e->y() ) )
+ {
+ d->tabList.remove(d->currTab);
+ }
+ qHeapSort( d->tabList );
+
+ // Delete the new tabulator if it is placed on top of another.
+ KoTabulatorList::ConstIterator tmpTab=d->tabList.begin();
+ int count=0;
+ while(tmpTab!=d->tabList.end()) {
+ if( equals( (*tmpTab).ptPos, d->currTab.ptPos ) ) {
+ count++;
+ if(count > 1) {
+ d->tabList.remove(d->currTab);
+ break;
+ }
+ }
+ tmpTab++;
+ }
+ //searchTab( e->x() ); // DF: why set currTab here?
+ emit tabListChanged( d->tabList );
+ update();
+ }
+ else if( d->action == A_HELPLINES )
+ {
+ emit addHelpline( e->pos(), orientation == TQt::Horizontal);
+ setCursor( ArrowCursor );
+ }
+ d->currTab.type = T_INVALID; // added (DF)
+}
+
+void KoRuler::mouseMoveEvent( TQMouseEvent *e )
+{
+ hasToDelete = false;
+
+ int pw = d->frameEnd - frameStart;
+ int ph = tqRound(zoomIt(d->layout.ptHeight));
+ int left = frameStart - diffx;
+ int top = tqRound(zoomIt(d->layout.ptTop));
+ top -= diffy;
+ int right = d->frameEnd - diffx;
+ int bottom = tqRound(zoomIt(d->layout.ptBottom));
+ bottom = ph - bottom - diffy;
+ // Cumulate first-line-indent
+ int ip_first = tqRound( zoomIt( i_first + ( d->rtl ? d->i_right : i_left) ) );
+ int ip_left = tqRound(zoomIt(i_left));
+ int ip_right = tqRound(zoomIt(d->i_right));
+
+ int mx = e->x();
+ mx = mx+diffx < 0 ? 0 : mx;
+ int my = e->y();
+ my = my+diffy < 0 ? 0 : my;
+
+ TQToolTip::remove( this);
+ switch ( orientation ) {
+ case TQt::Horizontal: {
+ if ( !d->mousePressed ) {
+ setCursor( ArrowCursor );
+ d->action = A_NONE;
+ /////// ######
+ // At the moment, moving the left and right border indicators
+ // is disabled when setFrameStartEnd has been called (i.e. in KWord)
+ // Changing the layout margins directly from it would be utterly wrong
+ // (just try the 2-columns modes...). What needs to be done is:
+ // emitting a signal frameResized in mouseReleaseEvent, when a left/right
+ // border has been moved, and in kword we need to update the margins from
+ // there, if the left border of the 1st column or the right border of the
+ // last column was moved... and find what to do with the other borders.
+ // And for normal frames, resize the frame without touching the page layout.
+ // All that is too much for now -> disabling.
+ if ( !m_bFrameStartSet )
+ {
+ if ( mx > left - 5 && mx < left + 5 ) {
+ setCursor( TQt::sizeHorCursor );
+ d->action = A_BR_LEFT;
+ } else if ( mx > right - 5 && mx < right + 5 ) {
+ setCursor( TQt::sizeHorCursor );
+ d->action = A_BR_RIGHT;
+ }
+ }
+ if ( d->flags & F_TABS )
+ searchTab(mx);
+ } else {
+ // Calculate the new value.
+ int newPos=mx;
+ if( newPos!=right && gridSize!=0.0 && (e->state() & ShiftButton)==0) { // apply grid.
+ double grid=zoomIt(gridSize * 16);
+ newPos=tqRound( ((newPos * 16 / grid) * grid) / 16 );
+ }
+ if(newPos-left < 0) newPos=left;
+ else if (right-newPos < 0) newPos=right;
+ double newValue = unZoomIt(static_cast<double>(newPos) - frameStart + diffx);
+
+ switch ( d->action ) {
+ case A_BR_LEFT: {
+ if ( d->canvas && mx < right-10 && mx+diffx-2 > 0) {
+ drawLine( d->oldMx, mx );
+ d->layout.ptLeft = unZoomIt(static_cast<double>(mx + diffx));
+ if( ip_left > right-left-15 ) {
+ ip_left=right-left-15;
+ ip_left=ip_left<0 ? 0 : ip_left;
+ i_left=unZoomIt( ip_left );
+ emit newLeftIndent( i_left );
+ }
+ if ( ip_right > right-left-15 ) {
+ ip_right=right-left-15;
+ ip_right=ip_right<0? 0 : ip_right;
+ d->i_right=unZoomIt( ip_right );
+ emit newRightIndent( d->i_right );
+ }
+ d->oldMx = mx;
+ d->oldMy = my;
+ update();
+ }
+ else
+ return;
+ } break;
+ case A_BR_RIGHT: {
+ if ( d->canvas && mx > left+10 && mx+diffx <= pw-2) {
+ drawLine( d->oldMx, mx );
+ d->layout.ptRight = unZoomIt(static_cast<double>(pw - ( mx + diffx )));
+ if( ip_left > right-left-15 ) {
+ ip_left=right-left-15;
+ ip_left=ip_left<0 ? 0 : ip_left;
+ i_left=unZoomIt( ip_left );
+ emit newLeftIndent( i_left );
+ }
+ if ( ip_right > right-left-15 ) {
+ ip_right=right-left-15;
+ ip_right=ip_right<0? 0 : ip_right;
+ d->i_right=unZoomIt( ip_right );
+ emit newRightIndent( d->i_right );
+ }
+ d->oldMx = mx;
+ d->oldMy = my;
+ update();
+ }
+ else
+ return;
+ } break;
+ case A_FIRST_INDENT: {
+ if ( d->canvas ) {
+ if (d->rtl)
+ newValue = unZoomIt(pw) - newValue - d->i_right;
+ else
+ newValue -= i_left;
+ if(newValue == i_first) break;
+ drawLine( d->oldMx, newPos);
+ d->oldMx=newPos;
+ i_first = newValue;
+ update();
+ }
+ } break;
+ case A_LEFT_INDENT: {
+ if ( d->canvas ) {
+ //if (d->rtl) newValue = unZoomIt(pw) - newValue;
+ if(newValue == i_left) break;
+
+ drawLine( d->oldMx, newPos);
+ i_left = newValue;
+ d->oldMx = newPos;
+ update();
+ }
+ } break;
+ case A_RIGHT_INDENT: {
+ if ( d->canvas ) {
+ double rightValue = unZoomIt(right - newPos);
+ //if (d->rtl) rightValue = unZoomIt(pw) - rightValue;
+ if(rightValue == d->i_right) break;
+
+ drawLine( d->oldMx, newPos);
+ d->i_right=rightValue;
+ d->oldMx = newPos;
+ update();
+ }
+ } break;
+ case A_TAB: {
+ if ( d->canvas) {
+ if (d->rtl) newValue = unZoomIt(pw) - newValue;
+ if(newValue == d->currTab.ptPos) break; // no change
+ TQPainter p( d->canvas );
+ p.setRasterOp( TQt::NotROP );
+ // prevent 1st drawLine when we just created a new tab
+ // (it's a NOT line)
+ double pt;
+ int pt_fr;
+ if( d->currTab != d->removeTab )
+ {
+ pt = applyRtlAndZoom(d->currTab.ptPos);
+ pt_fr = tqRound(pt) + frameStart - diffx;
+ p.drawLine( pt_fr, 0, pt_fr, d->canvas->height() );
+ }
+
+ KoTabulatorList::Iterator it = d->tabList.find( d->currTab );
+ Q_ASSERT( it != d->tabList.end() );
+ if ( it != d->tabList.end() )
+ (*it).ptPos = newValue;
+ d->currTab.ptPos = newValue;
+
+ pt = applyRtlAndZoom( newValue );
+ pt_fr = tqRound(pt) + frameStart - diffx;
+ p.drawLine( pt_fr, 0, pt_fr, d->canvas->height() );
+
+ p.end();
+ d->oldMx = mx;
+ d->oldMy = my;
+ d->removeTab.type = T_INVALID;
+ update();
+ }
+ } break;
+ default: break;
+ }
+ }
+ if( d->action == A_HELPLINES )
+ {
+ emit moveHelpLines( e->pos(), orientation == TQt::Horizontal);
+ }
+
+ return;
+ } break;
+ case TQt::Vertical: {
+ if ( !d->mousePressed ) {
+ setCursor( ArrowCursor );
+ d->action = A_NONE;
+ if ( d->flags & F_NORESIZE )
+ break;
+ if ( my > top - 5 && my < top + 5 ) {
+ TQToolTip::add( this, i18n("Top margin") );
+ setCursor( TQt::sizeVerCursor );
+ d->action = A_BR_TOP;
+ } else if ( my > bottom - 5 && my < bottom + 5 ) {
+ TQToolTip::add( this, i18n("Bottom margin") );
+ setCursor( TQt::sizeVerCursor );
+ d->action = A_BR_BOTTOM;
+ }
+ } else {
+ switch ( d->action ) {
+ case A_BR_TOP: {
+ if ( d->canvas && my < bottom-20 && my+diffy-2 > 0) {
+ TQPainter p( d->canvas );
+ p.setRasterOp( TQt::NotROP );
+ p.drawLine( 0, d->oldMy, d->canvas->width(), d->oldMy );
+ p.drawLine( 0, my, d->canvas->width(), my );
+ p.end();
+ d->layout.ptTop = unZoomIt(static_cast<double>(my + diffy));
+ d->oldMx = mx;
+ d->oldMy = my;
+ update();
+ }
+ else
+ return;
+ } break;
+ case A_BR_BOTTOM: {
+ if ( d->canvas && my > top+20 && my+diffy < ph-2) {
+ TQPainter p( d->canvas );
+ p.setRasterOp( TQt::NotROP );
+ p.drawLine( 0, d->oldMy, d->canvas->width(), d->oldMy );
+ p.drawLine( 0, my, d->canvas->width(), my );
+ p.end();
+ d->layout.ptBottom = unZoomIt(static_cast<double>(ph - ( my + diffy )));
+ d->oldMx = mx;
+ d->oldMy = my;
+ update();
+ }
+ else
+ return;
+ } break;
+ default: break;
+ }
+ }
+ } break;
+ }
+ if( d->action == A_HELPLINES )
+ {
+ emit moveHelpLines( e->pos(), orientation == TQt::Horizontal);
+ }
+
+ d->oldMx = mx;
+ d->oldMy = my;
+}
+
+void KoRuler::resizeEvent( TQResizeEvent *e )
+{
+ TQFrame::resizeEvent( e );
+ buffer.resize( size() );
+}
+
+void KoRuler::mouseDoubleClickEvent( TQMouseEvent* )
+{
+ handleDoubleClick();
+}
+
+void KoRuler::handleDoubleClick()
+{
+ if ( !d->m_bReadWrite )
+ return;
+
+ d->doubleClickedIndent = false;
+
+ // When Binary Compatibility is broken this will hopefully emit a
+ // doubleClicked(int) to differentiate between double-clicking an
+ // indent and double-clicking the ruler
+ if ( d->flags & F_INDENTS ) {
+ if ( d->action == A_LEFT_INDENT || d->action == A_RIGHT_INDENT || d->action == A_FIRST_INDENT ) {
+ d->doubleClickedIndent = true;
+ emit doubleClicked(); // usually paragraph dialog
+ return;
+ }
+ }
+
+ // Double-clicked nothing
+ d->action = A_NONE;
+ emit doubleClicked(); // usually page layout dialog
+}
+
+void KoRuler::setTabList( const KoTabulatorList & _tabList )
+{
+ d->tabList = _tabList;
+ qHeapSort(d->tabList); // "Trust no one." as opposed to "In David we trust."
+
+ // Note that d->currTab and d->removeTab could now point to
+ // tabs which don't exist in d->tabList
+
+ update();
+}
+
+double KoRuler::makeIntern( double _v )
+{
+ return KoUnit::fromUserValue( _v, m_unit );
+}
+
+void KoRuler::setupMenu()
+{
+ d->rb_menu = new TQPopupMenu();
+ TQ_CHECK_PTR( d->rb_menu );
+ for ( uint i = 0 ; i <= KoUnit::U_LASTUNIT ; ++i )
+ {
+ KoUnit::Unit unit = static_cast<KoUnit::Unit>( i );
+ d->rb_menu->insertItem( KoUnit::unitDescription( unit ), i /*as id*/ );
+ if ( m_unit == unit )
+ d->rb_menu->setItemChecked( i, true );
+ }
+ connect( d->rb_menu, TQ_SIGNAL( activated( int ) ), TQ_SLOT( slotMenuActivated( int ) ) );
+
+ d->rb_menu->insertSeparator();
+ d->mPageLayout=d->rb_menu->insertItem(i18n("Page Layout..."), this, TQ_SLOT(pageLayoutDia()));
+#if 0
+ d->rb_menu->insertSeparator();
+ d->mRemoveTab=d->rb_menu->insertItem(i18n("Remove Tabulator"), this, TQ_SLOT(rbRemoveTab()));
+ d->rb_menu->setItemEnabled( d->mRemoveTab, false );
+#endif
+}
+
+void KoRuler::uncheckMenu()
+{
+ for ( uint i = 0 ; i <= KoUnit::U_LASTUNIT ; ++i )
+ d->rb_menu->setItemChecked( i, false );
+}
+
+void KoRuler::setUnit( KoUnit::Unit unit )
+{
+ m_unit = unit;
+ uncheckMenu();
+ d->rb_menu->setItemChecked( m_unit, true );
+ update();
+}
+
+void KoRuler::setZoom( const double& zoom )
+{
+ if(zoom==m_zoom)
+ return;
+ if(zoom < 1E-4) // Don't do 0 or negative values
+ return;
+ m_zoom=zoom;
+ m_1_zoom=1/m_zoom;
+ update();
+}
+
+bool KoRuler::willRemoveTab( int y ) const
+{
+ return (y < -50 || y > height() + 25) && d->currTab.type != T_INVALID;
+}
+
+void KoRuler::rbRemoveTab() {
+
+ d->tabList.remove( d->currTab );
+ d->currTab.type = T_INVALID;
+ emit tabListChanged( d->tabList );
+ update();
+}
+
+void KoRuler::setReadWrite(bool _readWrite)
+{
+ d->m_bReadWrite=_readWrite;
+}
+
+void KoRuler::searchTab(int mx) {
+
+ int pos;
+ d->currTab.type = T_INVALID;
+ KoTabulatorList::ConstIterator it = d->tabList.begin();
+ for ( ; it != d->tabList.end() ; ++it ) {
+ pos = tqRound(applyRtlAndZoom((*it).ptPos)) - diffx + frameStart;
+ if ( mx > pos - 5 && mx < pos + 5 ) {
+ setCursor( TQt::sizeHorCursor );
+ d->action = A_TAB;
+ d->currTab = *it;
+ break;
+ }
+ }
+}
+
+void KoRuler::drawLine(int oldX, int newX) {
+
+ TQPainter p( d->canvas );
+ p.setRasterOp( TQt::NotROP );
+ p.drawLine( oldX, 0, oldX, d->canvas->height() );
+ if(newX!=-1)
+ p.drawLine( newX, 0, newX, d->canvas->height() );
+ p.end();
+}
+
+void KoRuler::showMousePos( bool _showMPos )
+{
+ showMPos = _showMPos;
+ hasToDelete = false;
+ mposX = -1;
+ mposY = -1;
+ update();
+}
+
+void KoRuler::setOffset( int _diffx, int _diffy )
+{
+ //kdDebug() << "KoRuler::setOffset " << _diffx << "," << _diffy << endl;
+ diffx = _diffx;
+ diffy = _diffy;
+ update();
+}
+
+void KoRuler::setFrameStartEnd( int _frameStart, int _frameEnd )
+{
+ if ( _frameStart != frameStart || _frameEnd != d->frameEnd || !m_bFrameStartSet )
+ {
+ frameStart = _frameStart;
+ d->frameEnd = _frameEnd;
+ // Remember that setFrameStartEnd was called. This activates a slightly
+ // different mode (when moving start and end positions).
+ m_bFrameStartSet = true;
+ update();
+ }
+}
+
+void KoRuler::setRightIndent( double _right )
+{
+ d->i_right = makeIntern( _right );
+ update();
+}
+
+void KoRuler::setDirection( bool rtl )
+{
+ d->rtl = rtl;
+ update();
+}
+
+void KoRuler::changeFlags(int _flags)
+{
+ d->flags = _flags;
+}
+
+int KoRuler::flags() const
+{
+ return d->flags;
+}
+
+bool KoRuler::doubleClickedIndent() const
+{
+ return d->doubleClickedIndent;
+}
+
+double KoRuler::applyRtlAndZoom( double value ) const
+{
+ int frameWidth = d->frameEnd - frameStart;
+ return d->rtl ? ( frameWidth - zoomIt( value ) ) : zoomIt( value );
+}
+
+double KoRuler::unZoomItRtl( int pixValue ) const
+{
+ int frameWidth = d->frameEnd - frameStart;
+ return d->rtl ? ( unZoomIt( (double)(frameWidth - pixValue) ) ) : unZoomIt( (double)pixValue );
+}
+
+void KoRuler::slotMenuActivated( int i )
+{
+ if ( i >= 0 && i <= KoUnit::U_LASTUNIT )
+ {
+ KoUnit::Unit unit = static_cast<KoUnit::Unit>(i);
+ setUnit( unit );
+ emit unitChanged( unit );
+ }
+}
+
+TQSize KoRuler::minimumSizeHint() const
+{
+ TQSize size;
+ TQFont font = TDEGlobalSettings::toolBarFont();
+ TQFontMetrics fm( font );
+
+ size.setWidth( TQMAX( fm.height() + 4, 20 ) );
+ size.setHeight( TQMAX( fm.height() + 4, 20 ) );
+
+ return size;
+}
+
+TQSize KoRuler::sizeHint() const
+{
+ return minimumSizeHint();
+}
+
+void KoRuler::setPageLayout( const KoPageLayout& _layout )
+{
+ d->layout = _layout;
+ update();
+}
+
+#include "kreruler.moc"
diff --git a/src/widgets/kreruler.h b/src/widgets/kreruler.h
new file mode 100644
index 0000000..ccf76f2
--- /dev/null
+++ b/src/widgets/kreruler.h
@@ -0,0 +1,366 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Reginald Stadlbauer <reggie@kde.org>
+ Copyright (C) 2005 Jason Kivlighn <jkivlighn@gmail.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+// Description: Ruler (header)
+
+/******************************************************************/
+
+#ifndef KRERULER_H
+#define KRERULER_H
+
+#include <tqframe.h>
+#include <tqpixmap.h>
+
+#include <tdemacros.h>
+
+#if 0
+#include <koGlobal.h>
+#include <koTabChooser.h>
+#endif
+
+#include "kreunit.h"
+
+class KoPageLayout;
+class TQPainter;
+
+enum KoTabulators { T_LEFT = 0, T_CENTER = 1, T_RIGHT = 2, T_DEC_PNT = 3, T_INVALID = -1 };
+enum KoTabulatorFilling { TF_BLANK = 0, TF_DOTS = 1, TF_LINE = 2, TF_DASH = 3, TF_DASH_DOT = 4, TF_DASH_DOT_DOT = 5};
+
+/**
+ * Struct: KoTabulator
+ * Defines the position of a tabulation (in pt), and its type
+ */
+struct KoTabulator {
+ /**
+ * Position of the tab in pt
+ */
+ double ptPos;
+ /**
+ * Type of tab (left/center/right/decimalpoint)
+ */
+ KoTabulators type;
+ /**
+ * Type of tab filling.
+ */
+ KoTabulatorFilling filling;
+ /**
+ * Width of the tab filling line.
+ */
+ double ptWidth;
+ /**
+ * Alignment character.
+ */
+ TQChar alignChar;
+
+ bool operator==( const KoTabulator & t ) const {
+ return TQABS( ptPos - t.ptPos ) < 1E-4 && type == t.type &&
+ filling == t.filling && TQABS( ptWidth - t.ptWidth ) < 1E-4;
+ }
+ bool operator!=( const KoTabulator & t ) const {
+ return !operator==(t);
+ }
+ // Operators used for sorting
+ bool operator < ( const KoTabulator & t ) const {
+ return ptPos < t.ptPos;
+ }
+ bool operator <= ( const KoTabulator & t ) const {
+ return ptPos <= t.ptPos;
+ }
+ bool operator > ( const KoTabulator & t ) const {
+ return ptPos > t.ptPos;
+ }
+};
+
+typedef TQValueList<KoTabulator> KoTabulatorList;
+
+class KoRulerPrivate;
+
+/**
+ * KoRuler is the horizontal or vertical ruler, to be used around
+ * the drawing area of most KOffice programs.
+ *
+ * It shows the graduated ruler with numbering, in any of the base units (like mm/pt/inch),
+ * and supports zooming, tabulators, paragraph indents, showing the mouse position, etc.
+ *
+ * It also offers a popupmenu upon right-clicking, for changing the unit,
+ * the page layout, or removing a tab.
+ */
+class KoRuler : public TQFrame
+{
+ TQ_OBJECT
+ friend class KoRulerPrivate; // for the Action enum
+public:
+ static const int F_TABS;
+ static const int F_INDENTS;
+ static const int F_HELPLINES;
+ static const int F_NORESIZE;
+
+ /**
+ * Create a ruler
+ * TODO document params
+ */
+ KoRuler( TQWidget *_parent, TQWidget *_canvas, Orientation _orientation,
+ const KoPageLayout& _layout, int _flags, KoUnit::Unit _unit );
+ ~KoRuler();
+
+ /**
+ * Set the unit to be used.
+ */
+ void setUnit( KoUnit::Unit unit );
+
+ /**
+ * Set the zoom of the ruler (default value of 1.0 is 100%)
+ */
+ void setZoom( const double& zoom=1.0 );
+ /**
+ * @return the current zoom level
+ */
+ const double& zoom() const { return m_zoom; }
+
+ /**
+ * Set the page layout, see @ref KoPageLayout.
+ * This defines the size of the page and the margins,
+ * from which the size of the ruler is deducted.
+ */
+ void setPageLayout( const KoPageLayout& _layout );
+
+ /**
+ * Call showMousePos(true) if the ruler should indicate the position
+ * of the mouse. This is usually only the case for drawing applications,
+ * so it is not enabled by default.
+ */
+ void showMousePos( bool _showMPos );
+ /**
+ * Set the position of the mouse, to update the indication in the ruler.
+ * This is only effective if showMousePos(true) was called previously.
+ * The position to give is not zoomed, it's in real pixel coordinates!
+ */
+ void setMousePos( int mx, int my );
+
+ /**
+ * Set a global offset to the X and Y coordinates.
+ * Usually the main drawing area is a TQScrollView, and this is called
+ * with contentsX() and contentsY(), each time those values change.
+ */
+ void setOffset( int _diffx, int _diffy );
+
+ /**
+ * Set the [paragraph] left indent to the specified position (in the current unit)
+ */
+ void setLeftIndent( double _left )
+ { i_left = makeIntern( _left ); update(); }
+
+ /**
+ * Set the [paragraph] first-line left indent to the specified position (in the current unit)
+ * This indent is cumulated with the left or right margin, depending on the [paragraph] direction.
+ */
+ void setFirstIndent( double _first )
+ { i_first = makeIntern( _first ); update(); }
+
+ /**
+ * Set the [paragraph] right indent to the specified position (in the current unit)
+ */
+ void setRightIndent( double _right );
+
+ /**
+ * Set the [paragraph] direction. By default (rtl=false), the left indent is on the
+ * left, and the right indent is on the right ;)
+ * If rtl=true, it's the opposite.
+ */
+ void setDirection( bool rtl );
+
+ /**
+ * Set the list of tabulators to show in the ruler.
+ */
+ void setTabList( const KoTabulatorList & tabList );
+
+ /**
+ * Set the start and the end of the current 'frame', i.e. the part
+ * of the page in which we are currently working. See KWord frames
+ * for an example where this is used. The tab positions and paragraph
+ * indents then become relative to the beginning of the frame, and the
+ * ruler goes from frameStart to frameEnd instead of using the page margins.
+ * @p _frameStart et @p _frameEnd are in pixel coordinates.
+ */
+ void setFrameStartEnd( int _frameStart, int _frameEnd );
+
+ /**
+ * KoRuler is in "read write" mode by default.
+ * Use setReadWrite(false) to use it in read-only mode.
+ */
+ void setReadWrite( bool _readWrite );
+
+ /**
+ * Change the flag (i.e. activate or deactivate certain features of KoRuler)
+ */
+ void changeFlags(int _flags);
+
+ /**
+ * Set the size of the grid used for tabs positioning, size in pt.
+ * default value is 0. 0 means no grid.
+ */
+ void setGridSize(double newGridSize) { gridSize=newGridSize; }
+
+ /**
+ * @return the current flags
+ */
+ int flags() const;
+
+ /**
+ * @return whether the current doubleClicked() signal was triggered
+ * by the user double-clicking on an indent (BCI). It returns false
+ * if the user just double-clicked on an "empty" part of the ruler.
+ *
+ * This method is strictly provided for use in a slot connected to the
+ * doubleClicked() signal; calling it at any other time results in
+ * undefined behavior.
+ */
+ bool doubleClickedIndent() const;
+
+ /**
+ * Enable or disable the "Page Layout" menu item.
+ */
+ void setPageLayoutMenuItemEnabled(bool b);
+
+ /**
+ * Reimplemented from TQWidget
+ */
+ virtual TQSize minimumSizeHint() const;
+
+ /**
+ * Reimplemented from TQWidget
+ */
+ virtual TQSize sizeHint() const;
+
+signals:
+ void newPageLayout( const KoPageLayout & );
+ void newLeftIndent( double );
+ void newFirstIndent( double );
+ void newRightIndent( double );
+ /** Old signal, kept for compatibility. Use doubleClicked instead. */
+ void openPageLayoutDia();
+ /** This signal is emitted when double-clicking the ruler (or an indent) */
+ void doubleClicked();
+ /** This signal is emitted when double-clicking a tab */
+ void doubleClicked( double ptPos );
+
+ void tabListChanged( const KoTabulatorList & );
+ void unitChanged( KoUnit::Unit );
+
+ void addHelpline(const TQPoint &, bool );
+ void moveHelpLines( const TQPoint &, bool );
+
+protected:
+ enum Action {A_NONE, A_BR_LEFT, A_BR_RIGHT, A_BR_TOP, A_BR_BOTTOM,
+ A_LEFT_INDENT, A_FIRST_INDENT, A_TAB, A_RIGHT_INDENT,
+ A_HELPLINES };
+
+ void drawContents( TQPainter *_painter )
+ { orientation == TQt::Horizontal ? drawHorizontal( _painter ) : drawVertical( _painter ); }
+
+ void drawHorizontal( TQPainter *_painter );
+ void drawVertical( TQPainter *_painter );
+
+ void mousePressEvent( TQMouseEvent *e );
+ void mouseReleaseEvent( TQMouseEvent *e );
+ void mouseMoveEvent( TQMouseEvent *e );
+ void mouseDoubleClickEvent( TQMouseEvent* );
+ void resizeEvent( TQResizeEvent *e );
+ void handleDoubleClick();
+
+ double makeIntern( double _v );
+ double zoomIt(const double &value) const;
+ int zoomIt(const int &value) const;
+ unsigned int zoomIt(const unsigned int &value) const;
+ double unZoomIt(const double &value) const;
+ int unZoomIt(const int &value) const;
+ unsigned int unZoomIt(const unsigned int &value) const;
+ void setupMenu();
+ void uncheckMenu();
+ void searchTab(int mx);
+ void drawLine(int oldX, int newX);
+
+private:
+ double applyRtlAndZoom( double value ) const;
+ double unZoomItRtl( int pixValue ) const;
+ double lineDistance() const;
+ bool willRemoveTab( int y ) const;
+
+ KoRulerPrivate *d;
+
+ TQt::Orientation orientation;
+ int diffx, diffy;
+ double i_left, i_first;
+ TQPixmap buffer;
+ double m_zoom, m_1_zoom;
+ KoUnit::Unit m_unit;
+ bool hasToDelete;
+ bool showMPos;
+ bool m_bFrameStartSet;
+ bool m_bReadWrite;
+ int mposX, mposY;
+ int frameStart;
+
+ double gridSize;
+
+protected slots:
+ void slotMenuActivated( int i );
+ void pageLayoutDia() { emit doubleClicked()/*openPageLayoutDia()*/; }
+ void rbRemoveTab();
+
+};
+
+inline double KoRuler::zoomIt(const double &value) const {
+ if (m_zoom==1.0)
+ return value;
+ return m_zoom*value;
+}
+
+inline int KoRuler::zoomIt(const int &value) const {
+ if (m_zoom==1.0)
+ return value;
+ return tqRound(m_zoom*value);
+}
+
+inline unsigned int KoRuler::zoomIt(const unsigned int &value) const {
+ if (m_zoom==1.0)
+ return value;
+ return static_cast<unsigned int>(tqRound(m_zoom*value));
+}
+
+inline double KoRuler::unZoomIt(const double &value) const {
+ if(m_zoom==1.0)
+ return value;
+ return value*m_1_zoom;
+}
+
+inline int KoRuler::unZoomIt(const int &value) const {
+ if(m_zoom==1.0)
+ return value;
+ return tqRound(value*m_1_zoom);
+}
+
+inline unsigned int KoRuler::unZoomIt(const unsigned int &value) const {
+ if(m_zoom==1.0)
+ return value;
+ return static_cast<unsigned int>(tqRound(value*m_1_zoom));
+}
+
+#endif
diff --git a/src/widgets/kretextedit.cpp b/src/widgets/kretextedit.cpp
new file mode 100644
index 0000000..c6d150d
--- /dev/null
+++ b/src/widgets/kretextedit.cpp
@@ -0,0 +1,183 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "kretextedit.h"
+
+#include <tqtextstream.h>
+
+#include <tdeaccel.h>
+#include <kdebug.h>
+
+KreTextEdit::KreTextEdit( TQWidget *parent ) : KTextEdit( parent ), TDECompletionBase()
+{
+ TDECompletion * comp = completionObject(); //creates the completion object
+ comp->setIgnoreCase( true );
+
+ completing = false;
+
+ connect( this, TQ_SIGNAL( clicked( int, int ) ), TQ_SLOT( haltCompletion() ) );
+}
+
+void KreTextEdit::haltCompletion()
+{
+ completing = false;
+}
+
+void KreTextEdit::keyPressEvent( TQKeyEvent *e )
+{
+ // Filter key-events if completion mode is not set to CompletionNone
+ KKey key( e );
+
+ KeyBindingMap keys = getKeyBindings();
+ TDEShortcut cut;
+ bool noModifier = ( e->state() == NoButton || e->state() == ShiftButton );
+
+ if ( noModifier ) {
+ TQString keycode = e->text();
+ if ( !keycode.isEmpty() && keycode.unicode() ->isPrint() ) {
+ TQTextEdit::keyPressEvent ( e );
+ tryCompletion();
+ e->accept();
+ return ;
+ }
+ }
+
+ // Handles completion
+ if ( keys[ TextCompletion ].isNull() )
+ cut = TDEStdAccel::shortcut( TDEStdAccel::TextCompletion );
+ else
+ cut = keys[ TextCompletion ];
+
+ //using just the standard Ctrl+E isn't user-friendly enough for Grandma...
+ if ( completing && ( cut.contains( key ) || e->key() == TQt::Key_Enter || e->key() == TQt::Key_Return ) ) {
+ int paraFrom, indexFrom, paraTo, indexTo;
+ getSelection ( &paraFrom, &indexFrom, &paraTo, &indexTo );
+
+ removeSelection();
+ setCursorPosition( paraTo, indexTo );
+
+ completing = false;
+ return ;
+ }
+
+ // handle rotation
+
+ // Handles previous match
+ if ( keys[ PrevCompletionMatch ].isNull() )
+ cut = TDEStdAccel::shortcut( TDEStdAccel::PrevCompletion );
+ else
+ cut = keys[ PrevCompletionMatch ];
+
+ if ( cut.contains( key ) ) {
+ rotateText( TDECompletionBase::PrevCompletionMatch );
+ return ;
+ }
+
+ // Handles next match
+ if ( keys[ NextCompletionMatch ].isNull() )
+ cut = TDEStdAccel::shortcut( TDEStdAccel::NextCompletion );
+ else
+ cut = keys[ NextCompletionMatch ];
+
+ if ( cut.contains( key ) ) {
+ rotateText( TDECompletionBase::NextCompletionMatch );
+ return ;
+ }
+
+ //any other key events will end any text completion execpt for modifiers
+ switch ( e->key() ) {
+ case TQt::Key_Shift:
+ case TQt::Key_Control:
+ case TQt::Key_Alt:
+ case TQt::Key_Meta:
+ break;
+ default:
+ completing = false;
+ break;
+ }
+
+ // Let KTextEdit handle any other keys events.
+ KTextEdit::keyPressEvent ( e );
+}
+
+void KreTextEdit::setCompletedText( const TQString &txt )
+{
+ int para, index;
+ getCursorPosition( &para, &index );
+
+ TQString para_text = text( para );
+ int word_length = index - completion_begin;
+
+ insert( txt.right( txt.length() - word_length ) );
+ setSelection( para, index, para, completion_begin + txt.length() );
+ setCursorPosition( para, index );
+
+ completing = true;
+}
+
+void KreTextEdit::setCompletedItems( const TQStringList &/*items*/ )
+{}
+
+void KreTextEdit::tryCompletion()
+{
+ int para, index;
+ getCursorPosition( &para, &index );
+
+ TQString para_text = text( para );
+ if ( para_text.at( index ).isSpace() || completing ) {
+ if ( !completing )
+ completion_begin = para_text.findRev( ' ', index - 1 ) + 1;
+
+ TQString completing_word = para_text.mid( completion_begin, index - completion_begin );
+
+ TQString match = compObj() ->makeCompletion( completing_word );
+
+ if ( !match.isNull() && match != completing_word )
+ setCompletedText( match );
+ else
+ completing = false;
+ }
+}
+
+void KreTextEdit::rotateText( TDECompletionBase::KeyBindingType type )
+{
+ TDECompletion * comp = compObj();
+ if ( comp && completing &&
+ ( type == TDECompletionBase::PrevCompletionMatch ||
+ type == TDECompletionBase::NextCompletionMatch ) ) {
+ TQString input = ( type == TDECompletionBase::PrevCompletionMatch ) ? comp->previousMatch() : comp->nextMatch();
+
+ // Skip rotation if previous/next match is null or the same text
+ int para, index;
+ getCursorPosition( &para, &index );
+ TQString para_text = text( para );
+ TQString complete_word = para_text.mid( completion_begin, index - completion_begin );
+ if ( input.isNull() || input == complete_word )
+ return ;
+ setCompletedText( input );
+ }
+}
+
+void KreTextEdit::addCompletionItem( const TQString &name )
+{
+ compObj() ->addItem( name );
+}
+
+void KreTextEdit::removeCompletionItem( const TQString &name )
+{
+ compObj() ->removeItem( name );
+}
+
+void KreTextEdit::clearCompletionItems()
+{
+ compObj() ->clear();
+}
+
+#include "kretextedit.moc"
diff --git a/src/widgets/kretextedit.h b/src/widgets/kretextedit.h
new file mode 100644
index 0000000..ec9e5fa
--- /dev/null
+++ b/src/widgets/kretextedit.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef KRETEXTEDIT_H
+#define KRETEXTEDIT_H
+
+#include <ktextedit.h>
+#include <kcompletion.h>
+
+#include "datablocks/elementlist.h"
+
+/* @author Jason Kivlighn
+ * @brief An extended KTextEdit that allows for text completion
+ */
+class KreTextEdit : public KTextEdit, TDECompletionBase
+{
+ TQ_OBJECT
+
+public:
+ KreTextEdit( TQWidget *parent );
+
+ virtual void setCompletedText( const TQString &text );
+ virtual void setCompletedItems( const TQStringList &items );
+
+public slots:
+ void addCompletionItem( const TQString & );
+ void removeCompletionItem( const TQString & );
+ void clearCompletionItems();
+
+protected:
+ void keyPressEvent( TQKeyEvent * );
+
+private slots:
+ void haltCompletion();
+
+private:
+ void tryCompletion();
+ void rotateText( TDECompletionBase::KeyBindingType type );
+
+ bool completing;
+ int completion_begin;
+
+};
+
+#endif //KRETEXTEDIT_H
diff --git a/src/widgets/kwidgetlistbox.cpp b/src/widgets/kwidgetlistbox.cpp
new file mode 100644
index 0000000..a05ddcb
--- /dev/null
+++ b/src/widgets/kwidgetlistbox.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2005 Petri Damstén <petri.damsten@iki.fi>
+ *
+ * Note: This file is now part of Krecipes, which is a slightly modified version of the
+ * original used in SuperKaramba
+ *
+ * This file is part of SuperKaramba.
+ *
+ * SuperKaramba is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * SuperKaramba 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SuperKaramba; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+#include "kwidgetlistbox.h"
+#include <kdebug.h>
+#include <tdeglobalsettings.h>
+
+KWidgetListbox::KWidgetListbox(TQWidget *parent, const char *name)
+ : TQTable(parent, name)
+{
+ setNumRows(0);
+ setNumCols(1);
+ setColumnStretchable(0, true);
+ setLeftMargin(0);
+ setTopMargin(0);
+ horizontalHeader()->hide();
+ verticalHeader()->hide();
+ setSelectionMode(TQTable::NoSelection);
+ setFocusStyle(TQTable::FollowStyle);
+ connect(this, TQ_SIGNAL(currentChanged(int, int)),
+ this, TQ_SLOT(selectionChanged(int, int)));
+ setHScrollBarMode(TQScrollView::AlwaysOff);
+ setVScrollBarMode(TQScrollView::Auto);
+}
+
+KWidgetListbox::~KWidgetListbox()
+{
+ clear();
+}
+
+void KWidgetListbox::clear()
+{
+ for(int i = 0; i < numRows(); ++i)
+ clearCellWidget(i, 0);
+ setNumRows(0);
+}
+
+int KWidgetListbox::insertItem(TQWidget* item, int index)
+{
+ int row = index;
+
+ if(index == -1)
+ {
+ row = numRows();
+ }
+ //else
+ // return -1;
+
+ insertRows(row);
+ setRowHeight(row, item->height());
+ setCellWidget(row, 0, item);
+
+ for ( int i = row; i < numRows(); ++i ) {
+ setItemColors(i, even(i));
+ }
+
+ ensureCellVisible(row,0);
+
+ return row;
+}
+
+void KWidgetListbox::setSelected(TQWidget* item)
+{
+ setSelected(index(item));
+}
+
+void KWidgetListbox::selectionChanged(int row, int col)
+{
+ ensureCellVisible(row, col);
+ updateColors();
+ emit selected(row);
+}
+
+void KWidgetListbox::removeItem(TQWidget* item)
+{
+ removeItem(index(item));
+}
+
+void KWidgetListbox::removeItem(int index)
+{
+ removeRow(index);
+ updateColors();
+}
+
+void KWidgetListbox::setSelected(int index)
+{
+ setCurrentCell(index, 0);
+}
+
+int KWidgetListbox::selected() const
+{
+ return currentRow();
+}
+
+TQWidget* KWidgetListbox::selectedItem() const
+{
+ return item(selected());
+}
+
+TQWidget* KWidgetListbox::item(int index) const
+{
+ return cellWidget(index, 0);
+}
+
+int KWidgetListbox::index(TQWidget* itm) const
+{
+ for(int i = 0; i < numRows(); ++i)
+ if(item(i) == itm)
+ return i;
+ return -1;
+}
+
+bool KWidgetListbox::even(int index)
+{
+ int v = 0;
+ for(int i = 0; i < numRows(); ++i)
+ {
+ if(index == i)
+ break;
+ //if(!isRowHidden(i))
+ ++v;
+ }
+ return (v%2 == 0);
+}
+
+void KWidgetListbox::updateColors()
+{
+ int v = 0;
+ for(int i = 0; i < numRows(); ++i)
+ {
+ //if(!isRowHidden(i))
+ {
+ setItemColors(i, (v%2 == 0));
+ ++v;
+ }
+ }
+}
+
+void KWidgetListbox::setItemColors(int index, bool even)
+{
+ TQWidget* itm = item(index);
+if ( !itm){ kdDebug()<<"no widget at index "<<index<<endl; return; }
+/*
+ if(index == selected())
+ {
+ itm->setPaletteBackgroundColor(TDEGlobalSettings::highlightColor());
+ itm->setPaletteForegroundColor(TDEGlobalSettings::highlightedTextColor());
+ }*/
+ if(even)
+ {
+ itm->setPaletteBackgroundColor(TDEGlobalSettings::baseColor());
+ itm->setPaletteForegroundColor(TDEGlobalSettings::textColor());
+ }
+ else
+ {
+ itm->setPaletteBackgroundColor(
+ TDEGlobalSettings::alternateBackgroundColor());
+ itm->setPaletteForegroundColor(TDEGlobalSettings::textColor());
+ }
+}
+
+void KWidgetListbox::showItems(show_callback func, void* data)
+{
+ for(int i = 0; i < numRows(); ++i)
+ {
+ if(func == 0)
+ showRow(i);
+ else
+ {
+ if(func(i, item(i), data))
+ showRow(i);
+ else
+ hideRow(i);
+ }
+ }
+ updateColors();
+}
+
+void KWidgetListbox::showEvent(TQShowEvent*)
+{
+ //kdDebug() << k_funcinfo << endl;
+ repaintContents(false);
+}
+
+void KWidgetListbox::paintCell(TQPainter*, int, int, const TQRect&,
+ bool, const TQColorGroup&)
+{
+ //kdDebug() << k_funcinfo << endl;
+}
+
+#include "kwidgetlistbox.moc"
diff --git a/src/widgets/kwidgetlistbox.h b/src/widgets/kwidgetlistbox.h
new file mode 100644
index 0000000..0ad463c
--- /dev/null
+++ b/src/widgets/kwidgetlistbox.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2005 Petri Damstén <petri.damsten@iki.fi>
+ *
+ * Note: This file is now part of Krecipes, which is a slightly modified version of the
+ * original used in SuperKaramba
+ *
+ * This file is part of SuperKaramba.
+ *
+ * SuperKaramba is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * SuperKaramba 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with SuperKaramba; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ ****************************************************************************/
+#ifndef KWIDGETLISTBOX_H
+#define KWIDGETLISTBOX_H
+
+#include <tqtable.h>
+
+/**
+@author See README for the list of authors
+*/
+
+typedef bool (*show_callback) (int index, TQWidget* widget, void* data);
+
+class KWidgetListbox : public TQTable
+{
+ TQ_OBJECT
+
+ public:
+ KWidgetListbox(TQWidget *parent = 0, const char *name = 0);
+ ~KWidgetListbox();
+
+ int insertItem(TQWidget* item, int index = -1);
+ void setSelected(TQWidget* item);
+ void setSelected(int index);
+ void removeItem(TQWidget* item);
+ void removeItem(int index);
+ void clear();
+ int selected() const;
+ TQWidget* selectedItem() const;
+ TQWidget* item(int index) const;
+ int index(TQWidget* itm) const;
+ uint count() const { return numRows(); };
+
+ void showItems(show_callback func = 0, void* data = 0);
+
+ void paintCell(TQPainter* p, int row, int col, const TQRect& cr,
+ bool selected, const TQColorGroup& cg);
+ protected:
+ void setItemColors(int index, bool even);
+ void updateColors();
+ bool even(int index);
+ virtual void showEvent(TQShowEvent* e);
+
+ protected slots:
+ void selectionChanged(int row, int col);
+
+ signals:
+ void selected(int index);
+};
+
+#endif
diff --git a/src/widgets/paneldeco.cpp b/src/widgets/paneldeco.cpp
new file mode 100644
index 0000000..8045a5b
--- /dev/null
+++ b/src/widgets/paneldeco.cpp
@@ -0,0 +1,173 @@
+/***************************************************************************
+* Copyright (C) 2003 by Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+#include "paneldeco.h"
+
+#include <tqpainter.h>
+#include <tqpoint.h>
+#include <tqrect.h>
+
+#include <kiconloader.h>
+#include <kpixmap.h>
+#include <kpixmapeffect.h>
+
+
+// Panel decoration
+
+PanelDeco::PanelDeco( TQWidget *parent, const char *name, const TQString &title, const TQString &iconName ) : TQVBox( parent, name )
+{
+
+ // Top decoration
+ tDeco = new TopDeco( this, "TopDecoration", title, iconName );
+
+ hbox = new TQHBox( this );
+
+ //Left decoration
+ lDeco = new LeftDeco( hbox, "LeftDecoration" );
+
+ //The widget stack (panels)
+ stack = new TQWidgetStack( hbox );
+ stack->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding ) );
+
+}
+
+
+PanelDeco::~PanelDeco()
+{}
+
+void PanelDeco::childEvent( TQChildEvent *e )
+{
+ if ( e->type() == TQEvent::ChildInserted ) {
+ TQObject * obj = e->child();
+ if ( obj->inherits( "TQWidget" ) ) {
+ TQWidget * w = ( TQWidget* ) obj;
+ if ( w != hbox && w != tDeco )
+ w->reparent( stack, TQPoint( 0, 0 ) );
+ }
+ }
+}
+
+
+int PanelDeco::id( TQWidget* w )
+{
+ return ( stack->id( w ) );
+}
+
+void PanelDeco::raise( TQWidget *w )
+{
+ TQWidget * old_w = visiblePanel();
+
+ stack->raiseWidget( w );
+
+ if ( old_w != w )
+ emit panelRaised( w, old_w );
+}
+
+TQWidget* PanelDeco::visiblePanel( void )
+{
+ return ( stack->visibleWidget() );
+}
+
+void PanelDeco::setHeader( const TQString &title, const TQString &icon )
+{
+ tDeco->setHeader( title, icon );
+}
+
+// Left part of the decoration
+
+LeftDeco::LeftDeco( TQWidget *parent, const char *name ) :
+ TQWidget( parent, name, TQt::WNoAutoErase )
+{}
+
+LeftDeco::~LeftDeco()
+{}
+
+// Top part of the decoration
+
+TopDeco::TopDeco( TQWidget *parent, const char *name, const TQString &title, const TQString &iconName ) :
+ TQWidget( parent, name, TQt::WNoAutoErase )
+{
+ setMinimumHeight( 30 );
+ icon = 0;
+ panelTitle = TQString::null;
+ if ( !iconName.isNull() ) {
+ TDEIconLoader il;
+ icon = new TQPixmap( il.loadIcon( iconName, TDEIcon::NoGroup, 22 ) );
+ }
+
+ if ( !title.isNull() ) {
+ panelTitle = title;
+ }
+}
+
+TopDeco::~TopDeco()
+{
+ delete icon;
+}
+
+
+void TopDeco::paintEvent( TQPaintEvent * )
+{
+ // Get gradient colors
+ TQColor c1 = colorGroup().button().light( 120 );
+ TQColor c2 = paletteBackgroundColor();
+
+ // Draw the gradient
+ KPixmap kpm;
+ kpm.resize( size() );
+ KPixmapEffect::unbalancedGradient ( kpm, c1, c2, KPixmapEffect::VerticalGradient, 150, 150 );
+
+ // Add a line on top
+ TQPainter painter( &kpm );
+ painter.setPen( colorGroup().button().dark( 130 ) );
+ painter.drawLine( 0, 0, width(), 0 );
+
+ // Now Add the icon
+ int xpos = 0, ypos = 0;
+ if ( icon ) {
+ xpos = 20;
+ ypos = ( height() - icon->height() ) / 2 - 1;
+ painter.drawPixmap( xpos, ypos, *icon );
+ xpos += icon->width(); // Move it so that later we can easily place the text
+ }
+
+ // Finally, draw the text besides the icon
+ if ( !panelTitle.isNull() ) {
+ xpos += 15;
+ TQRect r = rect();
+ r.setLeft( xpos );
+ painter.setPen( TQColor( 0x00, 0x00, 0x00 ) );
+ TQFont ft = font();
+ ft.setBold( true );
+ painter.setFont( ft );
+ painter.drawText( r, TQt::AlignVCenter, panelTitle );
+ }
+ painter.end();
+ // Copy the pixmap to the widget
+ bitBlt( this, 0, 0, &kpm );
+}
+
+void TopDeco::setHeader( const TQString &title, const TQString &iconName )
+{
+ if ( !title.isNull() )
+ panelTitle = title;
+ if ( !iconName.isNull() ) {
+ TDEIconLoader il;
+ icon = new TQPixmap( il.loadIcon( iconName, TDEIcon::NoGroup, 22 ) );
+ }
+ if ( !title.isNull() || !iconName.isNull() )
+ update();
+}
+
+TQSize TopDeco::sizeHint( void )
+{
+ return ( TQSize( parentWidget() ->width(), 30 ) );
+}
+
+#include "paneldeco.moc"
diff --git a/src/widgets/paneldeco.h b/src/widgets/paneldeco.h
new file mode 100644
index 0000000..126f7fb
--- /dev/null
+++ b/src/widgets/paneldeco.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+* Copyright (C) 2003 by Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+#ifndef PANELDECO_H
+#define PANELDECO_H
+
+
+#include <tqevent.h>
+#include <tqiconset.h>
+#include <tqstring.h>
+#include <tqhbox.h>
+#include <tqpixmap.h>
+#include <tqvbox.h>
+#include <tqwidget.h>
+#include <tqwidgetstack.h>
+
+
+/**
+* @author Unai Garro
+*/
+
+class PanelDeco;
+class LeftDeco;
+class TopDeco;
+
+class PanelDeco : public TQVBox
+{
+ TQ_OBJECT
+public:
+ // Methods
+ PanelDeco( TQWidget *parent = 0, const char *name = 0, const TQString &title = TQString::null, const TQString &iconName = TQString::null );
+ ~PanelDeco();
+ int id( TQWidget* w ); // obtain the id of the given panel
+ TQWidget* visiblePanel( void ); // obtain the current active panel no.
+
+signals:
+ void panelRaised( TQWidget *w, TQWidget *old_w );
+
+private:
+ TQHBox *hbox;
+ LeftDeco *lDeco;
+ TopDeco *tDeco;
+ TQWidgetStack *stack;
+
+public slots:
+ void raise( TQWidget *w );
+ void setHeader( const TQString &title = TQString::null, const TQString &icon = TQString::null );
+protected:
+ virtual void childEvent( TQChildEvent *e );
+
+
+};
+
+class LeftDeco: public TQWidget
+{
+ TQ_OBJECT
+public:
+ LeftDeco( TQWidget *parent = 0, const char *name = 0 );
+
+ ~LeftDeco();
+};
+
+class TopDeco: public TQWidget
+{
+ TQ_OBJECT
+public:
+ TopDeco( TQWidget *parent = 0, const char *name = 0, const TQString &title = TQString::null, const TQString &iconName = TQString::null );
+ ~TopDeco();
+ virtual TQSize sizeHint( void );
+public slots:
+ void setHeader( const TQString &title = TQString::null, const TQString &iconName = TQString::null );
+protected:
+ virtual void paintEvent( TQPaintEvent *e );
+private:
+ TQPixmap *icon;
+ TQString panelTitle;
+};
+
+#endif
diff --git a/src/widgets/prepmethodcombobox.cpp b/src/widgets/prepmethodcombobox.cpp
new file mode 100644
index 0000000..b0b7660
--- /dev/null
+++ b/src/widgets/prepmethodcombobox.cpp
@@ -0,0 +1,186 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "prepmethodcombobox.h"
+
+#include <tqlistbox.h>
+
+#include <kdebug.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/elementlist.h"
+
+/** Completion object which allows completing completing items
+ * the last item in a comma-separated list
+ */
+class PrepMethodCompletion : public TDECompletion
+{
+public:
+ PrepMethodCompletion() : TDECompletion()
+ {}
+
+ virtual TQString makeCompletion( const TQString &string ) {
+ kdDebug()<<"original makeCompletion( "<<string<<" )"<<endl;
+
+ int comma_index = string.findRev(",");
+ TQString completion_txt = string;
+ if ( comma_index != -1 )
+ completion_txt = completion_txt.right( completion_txt.length() - comma_index - 1 ).stripWhiteSpace();
+ if ( completion_txt.isEmpty() )
+ return string;
+
+ kdDebug()<<"altered makeCompletion( "<<completion_txt<<" )"<<endl;
+
+ completion_txt = TDECompletion::makeCompletion(completion_txt);
+ kdDebug()<<"got: "<<completion_txt<<endl;
+
+ if ( completion_txt.isEmpty() )
+ completion_txt = string;
+ else if ( comma_index != -1 )
+ completion_txt = string.left( comma_index ) + "," + completion_txt;
+
+ kdDebug()<<"returning: "<<completion_txt<<endl;
+ return completion_txt;
+ }
+};
+
+PrepMethodComboBox::PrepMethodComboBox( bool b, TQWidget *parent, RecipeDB *db, const TQString &specialItem ) :
+ KComboBox( b, parent ),
+ database( db ), m_specialItem(specialItem)
+{
+ setAutoDeleteCompletionObject(true);
+ setCompletionObject(new PrepMethodCompletion());
+}
+
+void PrepMethodComboBox::reload()
+{
+ TQString remember_text;
+ if ( editable() )
+ remember_text = lineEdit()->text();
+
+ ElementList prepMethodList;
+ database->loadPrepMethods( &prepMethodList );
+
+ clear();
+ prepMethodComboRows.clear();
+
+ int row = 0;
+ if ( !m_specialItem.isNull() ) {
+ insertItem(m_specialItem);
+ prepMethodComboRows.insert( row, -1 );
+ row++;
+ }
+ for ( ElementList::const_iterator it = prepMethodList.begin(); it != prepMethodList.end(); ++it, ++row ) {
+ insertItem((*it).name);
+ completionObject()->addItem((*it).name);
+ prepMethodComboRows.insert( row,(*it).id );
+ }
+
+ if ( editable() )
+ lineEdit()->setText( remember_text );
+
+ database->disconnect( this );
+ connect( database, TQ_SIGNAL( prepMethodCreated( const Element & ) ), TQ_SLOT( createPrepMethod( const Element & ) ) );
+ connect( database, TQ_SIGNAL( prepMethodRemoved( int ) ), TQ_SLOT( removePrepMethod( int ) ) );
+}
+
+int PrepMethodComboBox::id( int row )
+{
+ return prepMethodComboRows[ row ];
+}
+
+int PrepMethodComboBox::id( const TQString &ing )
+{
+ for ( int i = 0; i < count(); i++ ) {
+ if ( ing == text( i ) )
+ return id(i);
+ }
+ kdDebug()<<"Warning: couldn't find the ID for "<<ing<<endl;
+ return -1;
+}
+
+void PrepMethodComboBox::createPrepMethod( const Element &element )
+{
+ int row = findInsertionPoint( element.name );
+
+ TQString remember_text;
+ if ( editable() )
+ remember_text = lineEdit()->text();
+
+ insertItem( element.name, row );
+ completionObject()->addItem(element.name);
+
+ if ( editable() )
+ lineEdit()->setText( remember_text );
+
+ //now update the map by pushing everything after this item down
+ TQMap<int, int> new_map;
+ for ( TQMap<int, int>::iterator it = prepMethodComboRows.begin(); it != prepMethodComboRows.end(); ++it ) {
+ if ( it.key() >= row ) {
+ new_map.insert( it.key() + 1, it.data() );
+ }
+ else
+ new_map.insert( it.key(), it.data() );
+ }
+ prepMethodComboRows = new_map;
+ prepMethodComboRows.insert( row, element.id );
+}
+
+void PrepMethodComboBox::removePrepMethod( int id )
+{
+ int row = -1;
+ for ( TQMap<int, int>::iterator it = prepMethodComboRows.begin(); it != prepMethodComboRows.end(); ++it ) {
+ if ( it.data() == id ) {
+ row = it.key();
+ completionObject()->removeItem( text(row) );
+ removeItem( row );
+ prepMethodComboRows.remove( it );
+ break;
+ }
+ }
+
+ if ( row == -1 )
+ return ;
+
+ //now update the map by pushing everything after this item up
+ TQMap<int, int> new_map;
+ for ( TQMap<int, int>::iterator it = prepMethodComboRows.begin(); it != prepMethodComboRows.end(); ++it ) {
+ if ( it.key() > row ) {
+ new_map.insert( it.key() - 1, it.data() );
+ }
+ else
+ new_map.insert( it.key(), it.data() );
+ }
+ prepMethodComboRows = new_map;
+}
+
+int PrepMethodComboBox::findInsertionPoint( const TQString &name )
+{
+ for ( int i = 0; i < count(); i++ ) {
+ if ( TQString::localeAwareCompare( name, text( i ) ) < 0 )
+ return i;
+ }
+
+ return count();
+}
+
+void PrepMethodComboBox::setSelected( int prepID )
+{
+ //do a reverse lookup on the row->id map
+ TQMap<int, int>::const_iterator it;
+ for ( it = prepMethodComboRows.begin(); it != prepMethodComboRows.end(); ++it ) {
+ if ( it.data() == prepID ) {
+ setCurrentItem(it.key());
+ break;
+ }
+ }
+}
+
+#include "prepmethodcombobox.moc"
diff --git a/src/widgets/prepmethodcombobox.h b/src/widgets/prepmethodcombobox.h
new file mode 100644
index 0000000..ad902d7
--- /dev/null
+++ b/src/widgets/prepmethodcombobox.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+* Copyright (C) 2005 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef PREPMETHODCOMBOBOX_H
+#define PREPMETHODCOMBOBOX_H
+
+#include <kcombobox.h>
+
+#include <tqmap.h>
+
+#include "datablocks/element.h"
+
+class RecipeDB;
+class ElementList;
+
+class PrepMethodComboBox : public KComboBox
+{
+ TQ_OBJECT
+
+public:
+ PrepMethodComboBox( bool, TQWidget *parent, RecipeDB *db, const TQString &specialItem = TQString::null );
+
+ void reload();
+ int id( int row );
+ int id( const TQString &ing );
+ void setSelected( int prepID );
+
+private slots:
+ void createPrepMethod( const Element &element );
+ void removePrepMethod( int id );
+
+ int findInsertionPoint( const TQString &name );
+
+private:
+ RecipeDB *database;
+ TQMap<int, int> prepMethodComboRows; // Contains the prep method id for every given row in the combobox
+ TQString m_specialItem;
+};
+
+#endif //PREPMETHODCOMBOBOX_H
+
diff --git a/src/widgets/prepmethodlistview.cpp b/src/widgets/prepmethodlistview.cpp
new file mode 100644
index 0000000..1f1939b
--- /dev/null
+++ b/src/widgets/prepmethodlistview.cpp
@@ -0,0 +1,189 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "prepmethodlistview.h"
+
+#include <tdemessagebox.h>
+#include <tdeconfig.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kiconloader.h>
+#include <tdepopupmenu.h>
+
+#include "backends/recipedb.h"
+#include "dialogs/createelementdialog.h"
+#include "dialogs/dependanciesdialog.h"
+
+PrepMethodListView::PrepMethodListView( TQWidget *parent, RecipeDB *db ) : DBListViewBase( parent,db,db->prepMethodCount())
+{
+ setAllColumnsShowFocus( true );
+ setDefaultRenameAction( TQListView::Reject );
+}
+
+void PrepMethodListView::init()
+{
+ connect( database, TQ_SIGNAL( prepMethodCreated( const Element & ) ), TQ_SLOT( checkCreatePrepMethod( const Element & ) ) );
+ connect( database, TQ_SIGNAL( prepMethodRemoved( int ) ), TQ_SLOT( removePrepMethod( int ) ) );
+}
+
+void PrepMethodListView::load( int limit, int offset )
+{
+ ElementList prepMethodList;
+ database->loadPrepMethods( &prepMethodList, limit, offset );
+
+ setTotalItems(prepMethodList.count());
+
+ for ( ElementList::const_iterator ing_it = prepMethodList.begin(); ing_it != prepMethodList.end(); ++ing_it )
+ createPrepMethod( *ing_it );
+}
+
+void PrepMethodListView::checkCreatePrepMethod( const Element &el )
+{
+ if ( handleElement(el.name) ) { //only create this prep method if the base class okays it
+ createPrepMethod(el);
+ }
+}
+
+
+StdPrepMethodListView::StdPrepMethodListView( TQWidget *parent, RecipeDB *db, bool editable ) : PrepMethodListView( parent, db )
+{
+ addColumn( i18n( "Preparation Method" ) );
+
+ TDEConfig * config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ if ( editable ) {
+ setRenameable( 0, true );
+
+ TDEIconLoader *il = new TDEIconLoader;
+
+ kpop = new TDEPopupMenu( this );
+ kpop->insertItem( il->loadIcon( "document-new", TDEIcon::NoGroup, 16 ), i18n( "&Create" ), this, TQ_SLOT( createNew() ), CTRL + Key_C );
+ kpop->insertItem( il->loadIcon( "edit-delete", TDEIcon::NoGroup, 16 ), i18n( "&Delete" ), this, TQ_SLOT( remove
+ () ), Key_Delete );
+ kpop->insertItem( il->loadIcon( "edit", TDEIcon::NoGroup, 16 ), i18n( "&Rename" ), this, TQ_SLOT( rename() ), CTRL + Key_R );
+ kpop->polish();
+
+ delete il;
+
+ connect( this, TQ_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ), TQ_SLOT( showPopup( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
+ connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem* ) ), this, TQ_SLOT( modPrepMethod( TQListViewItem* ) ) );
+ connect( this, TQ_SIGNAL( itemRenamed( TQListViewItem* ) ), this, TQ_SLOT( savePrepMethod( TQListViewItem* ) ) );
+ }
+}
+
+void StdPrepMethodListView::showPopup( TDEListView * /*l*/, TQListViewItem *i, const TQPoint &p )
+{
+ if ( i )
+ kpop->exec( p );
+}
+
+void StdPrepMethodListView::createNew()
+{
+ CreateElementDialog * elementDialog = new CreateElementDialog( this, i18n( "New Preparation Method" ) );
+
+ if ( elementDialog->exec() == TQDialog::Accepted ) {
+ TQString result = elementDialog->newElementName();
+
+ //check bounds first
+ if ( checkBounds( result ) )
+ database->createNewPrepMethod( result ); // Create the new prepMethod in the database
+ }
+}
+
+void StdPrepMethodListView::remove
+ ()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item ) {
+ ElementList dependingRecipes;
+ int prepMethodID = item->text( 1 ).toInt();
+ database->findPrepMethodDependancies( prepMethodID, &dependingRecipes );
+ if ( dependingRecipes.isEmpty() )
+ database->removePrepMethod( prepMethodID );
+ else // Need Warning!
+ {
+ ListInfo info;
+ info.list = dependingRecipes;
+ info.name = i18n("Recipes");
+ DependanciesDialog warnDialog( this, info );
+ warnDialog.setCustomWarning( i18n("You are about to permanantly delete recipes from your database.") );
+ if ( warnDialog.exec() == TQDialog::Accepted )
+ database->removePrepMethod( prepMethodID );
+ }
+ }
+}
+
+void StdPrepMethodListView::rename()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item )
+ PrepMethodListView::rename( item, 0 );
+}
+
+void StdPrepMethodListView::createPrepMethod( const Element &ing )
+{
+ createElement(new TQListViewItem( this, ing.name, TQString::number( ing.id ) ));
+}
+
+void StdPrepMethodListView::removePrepMethod( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 1 );
+ removeElement(item);
+}
+
+void StdPrepMethodListView::modPrepMethod( TQListViewItem* i )
+{
+ if ( i )
+ PrepMethodListView::rename( i, 0 );
+}
+
+void StdPrepMethodListView::savePrepMethod( TQListViewItem* i )
+{
+ if ( !checkBounds( i->text( 0 ) ) ) {
+ reload(ForceReload); //reset the changed text
+ return ;
+ }
+
+ int existing_id = database->findExistingPrepByName( i->text( 0 ) );
+ int prep_id = i->text( 1 ).toInt();
+ if ( existing_id != -1 && existing_id != prep_id ) //category already exists with this label... merge the two
+ {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "This preparation method already exists. Continuing will merge these two into one. Are you sure?" ) ) )
+ {
+ case KMessageBox::Continue: {
+ database->mergePrepMethods( existing_id, prep_id );
+ break;
+ }
+ default:
+ reload(ForceReload);
+ break;
+ }
+ }
+ else {
+ database->modPrepMethod( ( i->text( 1 ) ).toInt(), i->text( 0 ) );
+ }
+}
+
+bool StdPrepMethodListView::checkBounds( const TQString &name )
+{
+ if ( name.length() > uint(database->maxPrepMethodNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Preparation method cannot be longer than %1 characters." ) ).arg( database->maxPrepMethodNameLength() ) );
+ return false;
+ }
+
+ return true;
+}
+
+#include "prepmethodlistview.moc"
diff --git a/src/widgets/prepmethodlistview.h b/src/widgets/prepmethodlistview.h
new file mode 100644
index 0000000..a31fccd
--- /dev/null
+++ b/src/widgets/prepmethodlistview.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef PREPMETHODLISTVIEW_H
+#define PREPMETHODLISTVIEW_H
+
+#include "dblistviewbase.h"
+
+#include "datablocks/element.h"
+
+class RecipeDB;
+class TDEPopupMenu;
+
+class PrepMethodListView : public DBListViewBase
+{
+ TQ_OBJECT
+
+public:
+ PrepMethodListView( TQWidget *parent, RecipeDB *db );
+
+public slots:
+ virtual void load( int curr_limit, int curr_offset );
+
+protected slots:
+ virtual void createPrepMethod( const Element & ) = 0;
+ virtual void removePrepMethod( int ) = 0;
+
+ void checkCreatePrepMethod( const Element &el );
+
+protected:
+ virtual void init();
+};
+
+
+class StdPrepMethodListView : public PrepMethodListView
+{
+ TQ_OBJECT
+
+public:
+ StdPrepMethodListView( TQWidget *parent, RecipeDB *db, bool editable = false );
+
+protected:
+ virtual void createPrepMethod( const Element & );
+ virtual void removePrepMethod( int );
+
+private slots:
+ void showPopup( TDEListView *, TQListViewItem *, const TQPoint & );
+
+ void createNew();
+ void remove
+ ();
+ void rename();
+
+ void modPrepMethod( TQListViewItem* i );
+ void savePrepMethod( TQListViewItem* i );
+
+private:
+ bool checkBounds( const TQString &name );
+
+ TDEPopupMenu *kpop;
+};
+
+#endif //PREPMETHODLISTVIEW_H
diff --git a/src/widgets/propertylistview.cpp b/src/widgets/propertylistview.cpp
new file mode 100644
index 0000000..29a3321
--- /dev/null
+++ b/src/widgets/propertylistview.cpp
@@ -0,0 +1,289 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "propertylistview.h"
+
+#include <tdelocale.h>
+#include <tdemessagebox.h>
+#include <tdeconfig.h>
+#include <tdeglobal.h>
+#include <kiconloader.h>
+#include <tdepopupmenu.h>
+#include <kdebug.h>
+
+#include "backends/recipedb.h"
+#include "dialogs/createpropertydialog.h"
+
+PropertyCheckListItem::PropertyCheckListItem( TQListView* klv, const IngredientProperty &property ) : TQCheckListItem( klv, TQString::null, TQCheckListItem::CheckBox ),
+ m_property( property )
+{
+ //setOn( false ); // Set unchecked by default
+}
+
+PropertyCheckListItem::PropertyCheckListItem( TQListViewItem* it, const IngredientProperty &property ) : TQCheckListItem( it, TQString::null, TQCheckListItem::CheckBox ),
+ m_property( property )
+{
+ //setOn( false ); // Set unchecked by default
+}
+
+TQString PropertyCheckListItem::text( int column ) const
+{
+ switch ( column ) {
+ case 0:
+ return m_property.name;
+ break;
+ case 1:
+ return m_property.units;
+ break;
+ case 2:
+ return TQString::number( m_property.id );
+ break;
+
+ }
+
+ return TQString::null;
+}
+
+
+HidePropertyCheckListItem::HidePropertyCheckListItem( TQListView* klv, const IngredientProperty &property, bool enable ) : PropertyCheckListItem( klv, property )
+{
+ m_holdSettings = true;
+ setOn( enable ); // Set checked by default
+ m_holdSettings = false;
+}
+
+HidePropertyCheckListItem::HidePropertyCheckListItem( TQListViewItem* it, const IngredientProperty &property, bool enable ) : PropertyCheckListItem( it, property )
+{
+ m_holdSettings = true;
+ setOn( enable ); // Set checked by default
+ m_holdSettings = false;
+}
+
+void HidePropertyCheckListItem::stateChange( bool on )
+{
+ if ( !m_holdSettings ) {
+ TDEConfig *config = TDEGlobal::config();
+ config->setGroup("Formatting");
+
+ config->sync();
+ TQStringList hiddenList = config->readListEntry("HiddenProperties");
+ if ( on )
+ hiddenList.remove(m_property.name);
+ else if ( !hiddenList.contains(m_property.name) )
+ hiddenList.append(m_property.name);
+
+ config->writeEntry("HiddenProperties",hiddenList);
+ }
+}
+
+PropertyListView::PropertyListView( TQWidget *parent, RecipeDB *db ) : TDEListView( parent ),
+ database( db )
+{
+ setAllColumnsShowFocus( true );
+ setDefaultRenameAction( TQListView::Reject );
+
+ connect( db, TQ_SIGNAL( propertyCreated( const IngredientProperty & ) ), TQ_SLOT( createProperty( const IngredientProperty & ) ) );
+ connect( db, TQ_SIGNAL( propertyRemoved( int ) ), TQ_SLOT( removeProperty( int ) ) );
+}
+
+void PropertyListView::reload()
+{
+ clear(); // Clear the view
+
+ m_loading = true;
+
+ IngredientPropertyList propertyList;
+ database->loadProperties( &propertyList );
+
+ //Populate this data into the TDEListView
+ IngredientPropertyList::const_iterator prop_it;
+ for ( prop_it = propertyList.begin(); prop_it != propertyList.end(); ++prop_it )
+ createProperty( *prop_it );
+
+ m_loading = false;
+}
+
+
+
+StdPropertyListView::StdPropertyListView( TQWidget *parent, RecipeDB *db, bool editable ) : PropertyListView( parent, db )
+{
+ addColumn( i18n( "Property" ) );
+ addColumn( i18n( "Units" ) );
+
+ TDEConfig * config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 2 );
+
+ setSorting( 0 );
+
+ if ( editable ) {
+ setRenameable( 0, true );
+
+ TDEIconLoader *il = new TDEIconLoader;
+
+ kpop = new TDEPopupMenu( this );
+ kpop->insertItem( il->loadIcon( "document-new", TDEIcon::NoGroup, 16 ), i18n( "&Create" ), this, TQ_SLOT( createNew() ), CTRL + Key_C );
+ kpop->insertItem( il->loadIcon( "edit-delete", TDEIcon::NoGroup, 16 ), i18n( "&Delete" ), this, TQ_SLOT( remove
+ () ), Key_Delete );
+ kpop->insertItem( il->loadIcon( "edit", TDEIcon::NoGroup, 16 ), i18n( "&Rename" ), this, TQ_SLOT( rename() ), CTRL + Key_R );
+ kpop->polish();
+
+ delete il;
+
+ connect( this, TQ_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ), TQ_SLOT( showPopup( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
+ connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem* ) ), this, TQ_SLOT( modProperty( TQListViewItem* ) ) );
+ connect( this, TQ_SIGNAL( itemRenamed( TQListViewItem* ) ), this, TQ_SLOT( saveProperty( TQListViewItem* ) ) );
+ }
+}
+
+void StdPropertyListView::showPopup( TDEListView * /*l*/, TQListViewItem *i, const TQPoint &p )
+{
+ if ( i )
+ kpop->exec( p );
+}
+
+void StdPropertyListView::createNew()
+{
+ UnitList list;
+ database->loadUnits( &list );
+ CreatePropertyDialog* propertyDialog = new CreatePropertyDialog( this, &list );
+
+ if ( propertyDialog->exec() == TQDialog::Accepted ) {
+ TQString name = propertyDialog->newPropertyName();
+ TQString units = propertyDialog->newUnitsName();
+ if ( !( ( name.isEmpty() ) || ( units.isEmpty() ) ) ) // Make sure none of the fields are empty
+ {
+ //check bounds first
+ if ( checkBounds( name ) )
+ database->addProperty( name, units );
+ }
+ }
+ delete propertyDialog;
+}
+
+void StdPropertyListView::remove
+ ()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item ) {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "Are you sure you want to delete this property?" ) ) ) {
+ case KMessageBox::Continue:
+ database->removeProperty( item->text( 2 ).toInt() );
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void StdPropertyListView::rename()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item )
+ PropertyListView::rename( item, 0 );
+}
+
+void StdPropertyListView::removeProperty( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 2 );
+
+ Q_ASSERT( item );
+
+ delete item;
+}
+
+void StdPropertyListView::createProperty( const IngredientProperty &property )
+{
+ ( void ) new TQListViewItem( this, property.name, property.units, TQString::number( property.id ) );
+}
+
+void StdPropertyListView::modProperty( TQListViewItem* i )
+{
+ if ( i )
+ PropertyListView::rename( i, 0 );
+}
+
+void StdPropertyListView::saveProperty( TQListViewItem* i )
+{
+ if ( !checkBounds( i->text( 0 ) ) ) {
+ reload(); //reset the changed text
+ return ;
+ }
+kdDebug() << "saveProp: " << i->text( 0 ) << endl;
+ int existing_id = database->findExistingPropertyByName( i->text( 0 ) );
+ int prop_id = i->text( 2 ).toInt();
+ if ( existing_id != -1 && existing_id != prop_id ) //category already exists with this label... merge the two
+ {
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "This property already exists. Continuing will merge these two properties into one. Are you sure?" ) ) )
+ {
+ case KMessageBox::Continue: {
+ database->mergeProperties( existing_id, prop_id );
+ break;
+ }
+ default:
+ reload();
+ break;
+ }
+ }
+ else
+ database->modProperty( prop_id, i->text( 0 ) );
+}
+
+bool StdPropertyListView::checkBounds( const TQString &name )
+{
+ if ( name.length() > database->maxPropertyNameLength() ) {
+ KMessageBox::error( this, TQString( i18n( "Property name cannot be longer than %1 characters." ) ).arg( database->maxPropertyNameLength() ) );
+ return false;
+ }
+
+ return true;
+}
+
+
+
+PropertyConstraintListView::PropertyConstraintListView( TQWidget *parent, RecipeDB *db ) : PropertyListView( parent, db )
+{
+ addColumn( i18n( "Enabled" ) );
+ addColumn( i18n( "Property" ) );
+ addColumn( i18n( "Min. Value" ) );
+ addColumn( i18n( "Max. Value" ) );
+ addColumn( "Id", 0 ); //hidden, only for internal purposes
+
+ setRenameable( 0, true );
+}
+
+void PropertyConstraintListView::removeProperty( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 4 );
+
+ Q_ASSERT( item );
+
+ delete item;
+}
+
+void PropertyConstraintListView::createProperty( const IngredientProperty &property )
+{
+ ( void ) new ConstraintsListItem( this, property );
+}
+
+
+CheckPropertyListView::CheckPropertyListView( TQWidget *parent, RecipeDB *db, bool editable ) : StdPropertyListView( parent, db, editable )
+{
+}
+
+void CheckPropertyListView::createProperty( const IngredientProperty &property )
+{
+ ( void ) new HidePropertyCheckListItem( this, property, (m_loading)?false:true );
+}
+
+#include "propertylistview.moc"
diff --git a/src/widgets/propertylistview.h b/src/widgets/propertylistview.h
new file mode 100644
index 0000000..62c249f
--- /dev/null
+++ b/src/widgets/propertylistview.h
@@ -0,0 +1,203 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef PROPERTYLISTVIEW_H
+#define PROPERTYLISTVIEW_H
+
+#include <tdelistview.h>
+
+#include "datablocks/element.h"
+#include "datablocks/ingredientproperty.h"
+#include "datablocks/constraintlist.h"
+
+class RecipeDB;
+class TDEPopupMenu;
+
+class PropertyCheckListItem : public TQCheckListItem
+{
+public:
+ PropertyCheckListItem( TQListView* klv, const IngredientProperty &property );
+ PropertyCheckListItem( TQListViewItem* it, const IngredientProperty &property );
+
+ ~PropertyCheckListItem( void )
+ {}
+ virtual TQString text( int column ) const;
+
+ IngredientProperty property() const
+ {
+ return m_property;
+ }
+
+protected:
+ IngredientProperty m_property;
+
+};
+
+class HidePropertyCheckListItem : public PropertyCheckListItem
+{
+public:
+ HidePropertyCheckListItem( TQListView* klv, const IngredientProperty &property, bool enable = false );
+ HidePropertyCheckListItem( TQListViewItem* it, const IngredientProperty &property, bool enable = false );
+
+protected:
+ virtual void stateChange( bool on );
+
+private:
+ bool m_holdSettings;
+};
+
+
+class ConstraintsListItem: public TQCheckListItem
+{
+public:
+ ConstraintsListItem( TQListView* klv, const IngredientProperty &pty ) : TQCheckListItem( klv, TQString::null, TQCheckListItem::CheckBox )
+ {
+ // Initialize the constraint data with the the property data
+ ctStored = new Constraint();
+ ctStored->id = pty.id;
+ ctStored->name = pty.name;
+ ctStored->perUnit = pty.perUnit;
+ ctStored->units = pty.units;
+ ctStored->max = 0;
+ ctStored->min = 0;
+ }
+
+ ~ConstraintsListItem( void )
+ {
+ delete ctStored;
+ }
+
+private:
+ Constraint *ctStored;
+
+public:
+ void setConstraint( const Constraint &constraint )
+ {
+ delete ctStored;
+ ctStored = new Constraint( constraint );
+
+ setOn( ctStored->enabled );
+ }
+ double maxVal()
+ {
+ return ctStored->max;
+ }
+ double minVal()
+ {
+ return ctStored->min;
+ }
+ int propertyId()
+ {
+ return ctStored->id;
+ }
+ void setMax( double maxValue )
+ {
+ ctStored->max = maxValue;
+ setText( 3, TQString::number( maxValue ) );
+ }
+ void setMin( double minValue )
+ {
+ ctStored->min = minValue;
+ setText( 2, TQString::number( minValue ) );
+ }
+ virtual TQString text( int column ) const
+ {
+ switch ( column ) {
+ case 1:
+ return ( ctStored->name );
+ case 2:
+ return ( TQString::number( ctStored->min ) );
+ case 3:
+ return ( TQString::number( ctStored->max ) );
+ case 4:
+ return ( TQString::number( ctStored->id ) );
+ default:
+ return ( TQString::null );
+ }
+ }
+};
+
+
+class PropertyListView : public TDEListView
+{
+ TQ_OBJECT
+
+public:
+ PropertyListView( TQWidget *parent, RecipeDB * );
+
+public slots:
+ void reload( void );
+
+protected:
+ RecipeDB *database;
+ bool m_loading;
+
+protected slots:
+ virtual void removeProperty( int id ) = 0;
+ virtual void createProperty( const IngredientProperty &property ) = 0;
+};
+
+
+
+class StdPropertyListView : public PropertyListView
+{
+ TQ_OBJECT
+
+public:
+ StdPropertyListView( TQWidget *parent, RecipeDB *, bool editable = false );
+
+protected:
+ virtual void removeProperty( int id );
+ virtual void createProperty( const IngredientProperty &property );
+
+private slots:
+ void showPopup( TDEListView *, TQListViewItem *, const TQPoint & );
+
+ void createNew();
+ void remove
+ ();
+ void rename();
+
+ void modProperty( TQListViewItem* i );
+ void saveProperty( TQListViewItem* i );
+
+private:
+ bool checkBounds( const TQString &name );
+
+ TDEPopupMenu *kpop;
+};
+
+
+
+class PropertyConstraintListView : public PropertyListView
+{
+public:
+ PropertyConstraintListView( TQWidget *parent, RecipeDB * );
+
+protected:
+ virtual void removeProperty( int id );
+ virtual void createProperty( const IngredientProperty &property );
+};
+
+class CheckPropertyListView : public StdPropertyListView
+{
+ TQ_OBJECT
+
+public:
+ CheckPropertyListView( TQWidget *parent, RecipeDB *, bool editable = false );
+
+protected:
+ virtual void createProperty( const IngredientProperty &property );
+
+private:
+ bool checkBounds( const TQString &name );
+};
+
+#endif //PROPERTYLISTVIEW_H
diff --git a/src/widgets/ratingdisplaywidget.ui b/src/widgets/ratingdisplaywidget.ui
new file mode 100644
index 0000000..bd1380e
--- /dev/null
+++ b/src/widgets/ratingdisplaywidget.ui
@@ -0,0 +1,232 @@
+<!DOCTYPE UI><UI version="3.1" stdsetdef="1">
+<class>RatingDisplayWidget</class>
+<widget class="TQWidget">
+ <property name="name">
+ <cstring>RatingDisplayWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>533</width>
+ <height>211</height>
+ </rect>
+ </property>
+ <property name="paletteBackgroundColor">
+ <color>
+ <red>250</red>
+ <green>248</green>
+ <blue>241</blue>
+ </color>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="TQLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Fixed</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="TQLabel">
+ <property name="name">
+ <cstring>icon</cstring>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>76</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string></string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>21</width>
+ <height>80</height>
+ </size>
+ </property>
+ </spacer>
+ </vbox>
+ </widget>
+ <widget class="TQLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="TQLabel">
+ <property name="name">
+ <cstring>raterName</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <bold>1</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Rater</string>
+ </property>
+ </widget>
+ <widget class="TDEListView">
+ <column>
+ <property name="text">
+ <string>Criteria</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Stars</string>
+ </property>
+ <property name="clickable">
+ <bool>true</bool>
+ </property>
+ <property name="resizable">
+ <bool>true</bool>
+ </property>
+ </column>
+ <property name="name">
+ <cstring>criteriaListView</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="selectionMode" stdset="0">
+ <enum>NoSelection</enum>
+ </property>
+ </widget>
+ <widget class="TQLabel">
+ <property name="name">
+ <cstring>comment</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Comments</string>
+ </property>
+ <property name="alignment">
+ <set>WordBreak|AlignTop</set>
+ </property>
+ </widget>
+ <widget class="TQLayoutWidget">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>spacer</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>150</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonRemove</cstring>
+ </property>
+ <property name="text">
+ <string>Remove</string>
+ </property>
+ </widget>
+ <widget class="KPushButton">
+ <property name="name">
+ <cstring>buttonEdit</cstring>
+ </property>
+ <property name="text">
+ <string>Edit...</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </hbox>
+</widget>
+<includes>
+ <include location="local" impldecl="in declaration">datablocks/rating.h</include>
+</includes>
+<variables>
+ <variable access="public">RatingList::iterator rating_it;</variable>
+</variables>
+<layoutdefaults spacing="6" margin="6"/>
+<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/>
+<includes>
+ <include location="global" impldecl="in implementation">kpushbutton.h</include>
+ <include location="global" impldecl="in implementation">tdelistview.h</include>
+</includes>
+</UI>
diff --git a/src/widgets/ratingwidget.cpp b/src/widgets/ratingwidget.cpp
new file mode 100644
index 0000000..10f35af
--- /dev/null
+++ b/src/widgets/ratingwidget.cpp
@@ -0,0 +1,164 @@
+/***************************************************************************
+ copyright : (C) 2003-2005 by Robby Stephenson
+ email : robby@periapsis.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+#include "ratingwidget.h"
+
+#include <tdeglobal.h> // needed for KMAX
+#include <kiconloader.h>
+#include <kdebug.h>
+
+#include <tqintdict.h>
+#include <tqlayout.h>
+
+namespace {
+ static const int RATING_WIDGET_MAX_STAR_SIZE = 24;
+}
+
+const TQPixmap& RatingWidget::pixmap(const TQString& value_) {
+ static TQIntDict<TQPixmap> pixmaps;
+ if(pixmaps.isEmpty()) {
+ pixmaps.insert(-1, new TQPixmap());
+ }
+ bool ok;
+ int n = value_.toInt(&ok);
+ if(!ok || n < 1 || n > 10) {
+ return *pixmaps[-1];
+ }
+ if(pixmaps[n]) {
+ return *pixmaps[n];
+ }
+
+ TQString picName = TQString::fromLatin1("stars%1").arg(n);
+ TQPixmap* pix = new TQPixmap(UserIcon(picName));
+ pixmaps.insert(n, pix);
+ return *pix;
+}
+
+RatingWidget::RatingWidget(int stars, TQWidget* parent_, const char* name_/*=0*/)
+ : TQHBox(parent_, name_), m_currIndex(-1), m_min(0), m_max(stars*2) {
+ m_pixOn = UserIcon(TQString::fromLatin1("star_on"));
+ m_pixOff = UserIcon(TQString::fromLatin1("star_off"));
+ m_pixHalf = UserIcon(TQString::fromLatin1("star_half"));
+ setSpacing(0);
+
+ // find maximum width and height
+ int w = KMAX(RATING_WIDGET_MAX_STAR_SIZE, KMAX(m_pixOn.width(), m_pixOff.width()));
+ int h = KMAX(RATING_WIDGET_MAX_STAR_SIZE, KMAX(m_pixOn.height(), m_pixOff.height()));
+ for(int i = 0; i < stars; ++i) {
+ TQLabel* l = new TQLabel(this);
+ l->setFixedSize(w, h);
+ m_widgets.append(l);
+ }
+ init();
+
+ TQBoxLayout* l = dynamic_cast<TQBoxLayout*>(layout());
+ if(l) {
+ l->addStretch(1);
+ }
+}
+
+void RatingWidget::init() {
+ m_total = KMIN(m_max/2, static_cast<int>(m_widgets.count()));
+ uint i = 0;
+ for( ; static_cast<int>(i) < m_total; ++i) {
+ m_widgets.at(i)->setPixmap(m_pixOff);
+ }
+ for( ; i < m_widgets.count(); ++i) {
+ m_widgets.at(i)->setPixmap(TQPixmap());
+ }
+ update();
+}
+
+void RatingWidget::update() {
+ int i = 0;
+ for( ; i <= (m_currIndex-1)/2; ++i) {
+ m_widgets.at(i)->setPixmap(m_pixOn);
+ }
+ for( ; i < m_total; ++i) {
+ m_widgets.at(i)->setPixmap(m_pixOff);
+ }
+
+ if ( m_currIndex % 2 == 0 ) {
+ m_widgets.at(m_currIndex/2)->setPixmap(m_pixHalf);
+ }
+
+ TQHBox::update();
+}
+
+void RatingWidget::mousePressEvent(TQMouseEvent* event_) {
+ // only react to left button
+ if(event_->button() != TQt::LeftButton) {
+ return;
+ }
+
+ int idx;
+ TQWidget* child = childAt(event_->pos());
+ bool left = false;
+ if(child) {
+ TQRect child_geom_left_half = child->geometry();
+ child_geom_left_half.setWidth(child_geom_left_half.width()/2);
+ if ( child_geom_left_half.contains(event_->pos()) )
+ left = true;
+
+ idx = m_widgets.findRef(static_cast<TQLabel*>(child));
+ // if the widget is clicked beyond the maximum value, clear it
+ // remember total and min are values, but index is zero-based!
+ if(idx > m_total-1) {
+ idx = -1;
+ } else if(idx < m_min-1) {
+ idx = m_min-1; // limit to minimum, remember index is zero-based
+ }
+ } else {
+ idx = -1;
+ }
+
+ int oldCurrent = m_currIndex;
+
+ m_currIndex = idx*2+1;
+
+ if ( left )
+ m_currIndex--;
+
+ if ( oldCurrent != m_currIndex ) {
+ update();
+ emit modified();
+ }
+}
+
+void RatingWidget::clear() {
+ m_currIndex = -1;
+ update();
+}
+
+TQString RatingWidget::text() const {
+ // index is index of the list, which is zero-based. Add 1!
+ return m_currIndex == -1 ? TQString::null : TQString::number(double(m_currIndex+1)/2);
+}
+
+void RatingWidget::setText(const TQString& text_) {
+ bool ok;
+ // text is value, subtract one to get index
+ m_currIndex =text_.toInt(&ok)-1;
+ if(ok) {
+ if(m_currIndex > m_total-1) {
+ m_currIndex = -1;
+ } else if(m_currIndex < m_min-1) {
+ m_currIndex = m_min-1; // limit to minimum, remember index is zero-based
+ }
+ } else {
+ m_currIndex = -1;
+ }
+ update();
+}
+
+#include "ratingwidget.moc"
diff --git a/src/widgets/ratingwidget.h b/src/widgets/ratingwidget.h
new file mode 100644
index 0000000..a603def
--- /dev/null
+++ b/src/widgets/ratingwidget.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ copyright : (C) 2003-2005 by Robby Stephenson
+ email : robby@periapsis.org
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+#ifndef RATINGWIDGET_H
+#define RATINGWIDGET_H
+
+#include <tqhbox.h>
+#include <tqptrlist.h>
+#include <tqlabel.h>
+#include <tqpixmap.h>
+#include <tqstringlist.h>
+
+/**
+ * @author Robby Stephenson
+ */
+class RatingWidget : public TQHBox {
+TQ_OBJECT
+
+typedef TQPtrList<TQLabel> LabelList;
+
+public:
+ RatingWidget(int stars, TQWidget* parent, const char* name = 0);
+
+ void clear();
+ TQString text() const;
+ void setText(const TQString& text);
+
+ static const TQPixmap& pixmap(const TQString& value);
+
+public slots:
+ void update();
+
+signals:
+ void modified();
+
+protected:
+ virtual void mousePressEvent(TQMouseEvent* e);
+
+private:
+ void init();
+
+ LabelList m_widgets;
+
+ int m_currIndex;
+ int m_total;
+ int m_min;
+ int m_max;
+
+ TQPixmap m_pixOn;
+ TQPixmap m_pixOff;
+ TQPixmap m_pixHalf;
+};
+#endif
diff --git a/src/widgets/recipelistview.cpp b/src/widgets/recipelistview.cpp
new file mode 100644
index 0000000..536d06c
--- /dev/null
+++ b/src/widgets/recipelistview.cpp
@@ -0,0 +1,447 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "recipelistview.h"
+
+#include <tqintdict.h>
+#include <tqdatastream.h>
+#include <tqtooltip.h>
+
+#include <tdeapplication.h>
+#include <kdebug.h>
+#include <tdeconfig.h>
+#include <tdeglobal.h>
+#include <tdelocale.h>
+#include <kiconloader.h>
+#include <kprogress.h>
+
+#include "backends/recipedb.h"
+
+class UncategorizedItem : public TQListViewItem
+{
+public:
+ UncategorizedItem( TQListView *lv ) : TQListViewItem( lv, i18n("Uncategorized") ){}
+ int rtti() const { return 1006; }
+};
+
+RecipeItemDrag::RecipeItemDrag( RecipeListItem *recipeItem, TQWidget *dragSource, const char *name )
+ : TQStoredDrag( RECIPEITEMMIMETYPE, dragSource, name )
+{
+ if ( recipeItem ) {
+ TQByteArray data;
+ TQDataStream out( data, IO_WriteOnly );
+ out << recipeItem->recipeID();
+ out << recipeItem->title();
+ setEncodedData( data );
+ }
+}
+
+bool RecipeItemDrag::canDecode( TQMimeSource* e )
+{
+ return e->provides( RECIPEITEMMIMETYPE );
+}
+
+bool RecipeItemDrag::decode( const TQMimeSource* e, RecipeListItem& item )
+{
+ if ( !e )
+ return false;
+
+ TQByteArray data = e->encodedData( RECIPEITEMMIMETYPE );
+ if ( data.isEmpty() )
+ return false;
+
+ TQString title;
+ int recipeID;
+ TQDataStream in( data, IO_ReadOnly );
+ in >> recipeID;
+ in >> title;
+
+ item.setTitle( title );
+ item.setRecipeID( recipeID );
+
+ return true;
+}
+
+class RecipeListToolTip : public TQToolTip
+{
+public:
+ RecipeListToolTip( RecipeListView *view ) : TQToolTip(view->viewport()), m_view(view)
+ {}
+
+ void maybeTip( const TQPoint &point )
+ {
+ TQListViewItem *item = m_view->itemAt( point );
+ if ( item ) {
+ TQString text = m_view->tooltip(item,0);
+ if ( !text.isEmpty() )
+ tip( m_view->itemRect( item ), text );
+ }
+ }
+
+private:
+ RecipeListView *m_view;
+
+};
+
+
+RecipeListView::RecipeListView( TQWidget *parent, RecipeDB *db ) : StdCategoryListView( parent, db ),
+ flat_list( false ),
+ m_uncat_item(0),
+ m_progress_dlg(0)
+{
+ setColumnText( 0, i18n( "Recipe" ) );
+
+ TDEConfig *config = TDEGlobal::config(); config->setGroup( "Performance" );
+ curr_limit = config->readNumEntry("CategoryLimit",-1);
+
+ TDEIconLoader il;
+ setPixmap( il.loadIcon( "categories", TDEIcon::NoGroup, 16 ) );
+
+ setSelectionMode( TQListView::Extended );
+
+ (void)new RecipeListToolTip(this);
+}
+
+void RecipeListView::init()
+{
+ connect( database, TQ_SIGNAL( recipeCreated( const Element &, const ElementList & ) ), TQ_SLOT( createRecipe( const Element &, const ElementList & ) ) );
+ connect( database, TQ_SIGNAL( recipeRemoved( int ) ), TQ_SLOT( removeRecipe( int ) ) );
+ connect( database, TQ_SIGNAL( recipeRemoved( int, int ) ), TQ_SLOT( removeRecipe( int, int ) ) );
+ connect( database, TQ_SIGNAL( recipeModified( const Element &, const ElementList & ) ), TQ_SLOT( modifyRecipe( const Element &, const ElementList & ) ) );
+
+ StdCategoryListView::init();
+}
+
+TQDragObject *RecipeListView::dragObject()
+{
+ RecipeListItem * item = dynamic_cast<RecipeListItem*>( currentItem() );
+ if ( item != 0 ) {
+ RecipeItemDrag * obj = new RecipeItemDrag( item, this, "Recipe drag item" );
+ /*const TQPixmap *pm = item->pixmap(0);
+ if( pm )
+ obj->setPixmap( *pm );*/
+ return obj;
+ }
+ return 0;
+}
+
+bool RecipeListView::acceptDrag( TQDropEvent *event ) const
+{
+ return RecipeItemDrag::canDecode( event );
+}
+
+TQString RecipeListView::tooltip(TQListViewItem *item, int /*column*/) const
+{
+ if ( item->rtti() == RECIPELISTITEM_RTTI ) {
+ RecipeListItem *recipe_it = (RecipeListItem*)item;
+
+ Recipe r;
+ database->loadRecipe(&r,RecipeDB::Meta|RecipeDB::Noatime,recipe_it->recipeID() );
+
+ TDELocale *locale = TDEGlobal::locale();
+
+ return TQString("<center><b>%7</b></center><center>__________</center>%1 %2<br />%3 %4<br />%5 %6")
+ .arg(i18n("Created:")).arg(locale->formatDateTime(r.ctime))
+ .arg(i18n("Modified:")).arg(locale->formatDateTime(r.mtime))
+ .arg(i18n("Last Accessed:")).arg(locale->formatDateTime(r.atime))
+ .arg(recipe_it->title());
+ }/* Maybe this would be handy
+ else if ( item->rtti() == CATEGORYLISTITEM_RTTI ) {
+ CategoryListItem *cat_it = (CategoryListItem*)item;
+
+ return TQString("<b>%1</b><hr />%2: %3")
+ .arg(cat_it->categoryName())
+ .arg(i18n("Recipes"))
+ .arg(TQString::number(WHATEVER THE CHILD COUNT IS));
+ }*/
+
+ return TQString::null;
+}
+
+void RecipeListView::load(int limit, int offset)
+{
+ m_uncat_item = 0;
+
+ if ( flat_list ) {
+ ElementList recipeList;
+ database->loadRecipeList( &recipeList );
+
+ ElementList::const_iterator recipe_it;
+ for ( recipe_it = recipeList.begin();recipe_it != recipeList.end();++recipe_it ) {
+ Recipe recipe;
+ recipe.recipeID = ( *recipe_it ).id;
+ recipe.title = ( *recipe_it ).name;
+ createRecipe( recipe, -1 );
+ }
+ }
+ else {
+ StdCategoryListView::load(limit,offset);
+
+ if ( offset == 0 ) {
+ ElementList recipeList;
+ database->loadUncategorizedRecipes( &recipeList );
+
+ ElementList::const_iterator recipe_it;
+ for ( recipe_it = recipeList.begin();recipe_it != recipeList.end();++recipe_it ) {
+ Recipe recipe;
+ recipe.recipeID = ( *recipe_it ).id;
+ recipe.title = ( *recipe_it ).name;
+ createRecipe( recipe, -1 );
+ }
+ }
+ }
+}
+
+void RecipeListView::populate( TQListViewItem *item )
+{
+ CategoryItemInfo *cat_item = dynamic_cast<CategoryItemInfo*>(item);
+ if ( !cat_item || cat_item->isPopulated() ) return;
+
+ delete item->firstChild(); //delete the "pseudo item"
+
+ if ( m_progress_dlg ){
+ m_progress_dlg->progressBar()->advance(1);
+ kapp->processEvents();
+ }
+
+ StdCategoryListView::populate(item);
+
+ if ( !flat_list ) {
+ int id = cat_item->categoryId();
+
+ // Now show the recipes
+ ElementList recipeList;
+ database->loadRecipeList( &recipeList, id );
+
+ ElementList::const_iterator recipe_it;
+ for ( recipe_it = recipeList.begin(); recipe_it != recipeList.end(); ++recipe_it ) {
+ Recipe recipe;
+ recipe.recipeID = ( *recipe_it ).id;
+ recipe.title = ( *recipe_it ).name;
+ createRecipe( recipe, id );
+ }
+ }
+}
+
+void RecipeListView::populateAll( TQListViewItem *parent )
+{
+ bool first = false;
+ if ( !parent ) {
+ first = true;
+ m_progress_dlg = new KProgressDialog(this,"populate_all_prog_dlg",TQString::null,i18n("Loading recipes"),true);
+ m_progress_dlg->setAllowCancel(false);
+ m_progress_dlg->progressBar()->setTotalSteps(0);
+ m_progress_dlg->progressBar()->setPercentageVisible(false);
+
+ m_progress_dlg->grabKeyboard(); //don't let the user keep hitting keys
+
+ parent = firstChild();
+ }
+ else {
+ populate( parent );
+ parent = parent->firstChild();
+ }
+
+ for ( TQListViewItem *item = parent; item; item = item->nextSibling() ) {
+ if ( m_progress_dlg && m_progress_dlg->wasCancelled() )
+ break;
+
+ populateAll( item );
+ }
+
+ if ( first ) {
+ delete m_progress_dlg;
+ m_progress_dlg = 0;
+ }
+}
+
+void RecipeListView::createRecipe( const Recipe &recipe, int parent_id )
+{
+ if ( parent_id == -1 ) {
+ if ( !m_uncat_item && curr_offset == 0 ) {
+ m_uncat_item = new UncategorizedItem(this);
+ if ( childCount() == 1 ) //only call createElement if this is the only item in the list
+ createElement(m_uncat_item); //otherwise, this item won't stay at the top
+ }
+
+ if ( m_uncat_item )
+ new RecipeListItem( m_uncat_item, recipe );
+ }
+ else {
+ CategoryListItem *parent = (CategoryListItem*)items_map[ parent_id ];
+ if ( parent && parent->isPopulated() )
+ createElement(new RecipeListItem( parent, recipe ));
+ }
+}
+
+void RecipeListView::createRecipe( const Element &recipe_el, const ElementList &categories )
+{
+ Recipe recipe;
+ recipe.recipeID = recipe_el.id;
+ recipe.title = recipe_el.name;
+
+ if ( categories.count() == 0 ) {
+ createRecipe( recipe, -1 );
+ }
+ else {
+ for ( ElementList::const_iterator cat_it = categories.begin(); cat_it != categories.end(); ++cat_it ) {
+ int cur_cat_id = ( *cat_it ).id;
+
+ TQListViewItemIterator iterator( this );
+ while ( iterator.current() ) {
+ if ( iterator.current() ->rtti() == 1001 ) {
+ CategoryListItem * cat_item = ( CategoryListItem* ) iterator.current();
+ if ( cat_item->categoryId() == cur_cat_id ) {
+ createRecipe( recipe, cur_cat_id );
+ }
+ }
+ ++iterator;
+ }
+ }
+ }
+}
+
+void RecipeListView::createElement( TQListViewItem *item )
+{
+ CategoryItemInfo *cat_item = dynamic_cast<CategoryItemInfo*>(item);
+ if ( cat_item && !cat_item->isPopulated() ) {
+ new PseudoListItem( item );
+ }
+
+ //if ( cat_item && !cat_item->isPopulated() && item->rtti() == RECIPELISTITEM_RTTI )
+ // return;
+
+ #if 0
+ ElementList list;
+ database->loadRecipeList( &list, cat_item->categoryId() );
+ if ( list.count() > 0 )
+ #endif
+
+ CategoryListView::createElement(item);
+}
+
+void RecipeListView::modifyRecipe( const Element &recipe, const ElementList &categories )
+{
+ removeRecipe( recipe.id );
+ createRecipe( recipe, categories );
+}
+
+void RecipeListView::removeRecipe( int id )
+{
+ TQListViewItemIterator iterator( this );
+ while ( iterator.current() ) {
+ if ( iterator.current() ->rtti() == 1000 ) {
+ RecipeListItem * recipe_it = ( RecipeListItem* ) iterator.current();
+ if ( recipe_it->recipeID() == id ) {
+ removeElement(recipe_it);
+
+ //delete the "Uncategorized" item if we removed the last recipe that was under it
+ if ( m_uncat_item && m_uncat_item->childCount() == 0 ) {
+ delete m_uncat_item;
+ m_uncat_item = 0;
+ }
+ }
+ }
+ ++iterator;
+ }
+}
+
+void RecipeListView::removeRecipe( int recipe_id, int cat_id )
+{
+ TQListViewItem * item = items_map[ cat_id ];
+
+ //find out if this is the only category the recipe belongs to
+ int finds = 0;
+ TQListViewItemIterator iterator( this );
+ while ( iterator.current() ) {
+ if ( iterator.current() ->rtti() == 1000 ) {
+ RecipeListItem * recipe_it = ( RecipeListItem* ) iterator.current();
+
+ if ( recipe_it->recipeID() == recipe_id ) {
+ if ( finds > 1 )
+ break;
+ finds++;
+ }
+ }
+ ++iterator;
+ }
+
+ //do this to only iterate over children of 'item'
+ TQListViewItem *pEndItem = NULL;
+ TQListViewItem *pStartItem = item;
+ do {
+ if ( pStartItem->nextSibling() )
+ pEndItem = pStartItem->nextSibling();
+ else
+ pStartItem = pStartItem->parent();
+ }
+ while ( pStartItem && !pEndItem );
+
+ iterator = TQListViewItemIterator( item );
+ while ( iterator.current() != pEndItem ) {
+ if ( iterator.current() ->rtti() == 1000 ) {
+ RecipeListItem * recipe_it = ( RecipeListItem* ) iterator.current();
+
+ if ( recipe_it->recipeID() == recipe_id ) {
+
+ if ( finds == 1 ) {
+ //the item is now uncategorized
+ if ( !m_uncat_item && curr_offset == 0 )
+ m_uncat_item = new UncategorizedItem(this);
+ if ( m_uncat_item ) {
+ Recipe r;
+ r.title = recipe_it->title(); r.recipeID = recipe_id;
+ new RecipeListItem(m_uncat_item,r);
+ }
+ }
+ removeElement(recipe_it);
+ break;
+ }
+ }
+ ++iterator;
+ }
+}
+
+void RecipeListView::removeCategory( int id )
+{
+ TQListViewItem * item = items_map[ id ];
+ if ( !item )
+ return ; //this may have been deleted already by its parent being deleted
+
+ moveChildrenToRoot( item );
+
+ StdCategoryListView::removeCategory( id );
+}
+
+void RecipeListView::moveChildrenToRoot( TQListViewItem *item )
+{
+ TQListViewItem * next_sibling;
+ for ( TQListViewItem * it = item->firstChild(); it; it = next_sibling ) {
+ next_sibling = it->nextSibling();
+ if ( it->rtti() == 1000 ) {
+ RecipeListItem *recipe_it = (RecipeListItem*) it;
+ Recipe r;
+ r.title = recipe_it->title(); r.recipeID = recipe_it->recipeID();
+
+ //the item is now uncategorized
+ removeElement(it,false);
+ it->parent() ->takeItem( it );
+ if ( !m_uncat_item && curr_offset == 0 )
+ m_uncat_item = new UncategorizedItem(this);
+ if ( m_uncat_item )
+ new RecipeListItem(m_uncat_item,r);
+ }
+ moveChildrenToRoot( it );
+ delete it;
+ }
+}
+
+#include "recipelistview.moc"
diff --git a/src/widgets/recipelistview.h b/src/widgets/recipelistview.h
new file mode 100644
index 0000000..642f768
--- /dev/null
+++ b/src/widgets/recipelistview.h
@@ -0,0 +1,166 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* *
+* Copyright (C) 2006 Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef RECIPELISTVIEW_H
+#define RECIPELISTVIEW_H
+
+#include <tqdragobject.h>
+
+#include "categorylistview.h"
+#include "datablocks/recipe.h"
+
+class TQDragObject;
+class TQDropEvent;
+
+class KProgressDialog;
+
+class RecipeDB;
+
+#define RECIPELISTITEM_RTTI 1000
+
+#define RECIPEITEMMIMETYPE "data/x-kde.recipe.item"
+
+class RecipeListItem : public TQListViewItem
+{
+public:
+ RecipeListItem( TQListView* qlv, const Recipe &r ) : TQListViewItem( qlv )
+ {
+ init( r );
+ }
+
+ RecipeListItem( TQListView* qlv, TQListViewItem *after, const Recipe &r ) : TQListViewItem( qlv, after )
+ {
+ init( r );
+ }
+
+ RecipeListItem( CategoryListItem* it, const Recipe &r ) : TQListViewItem( it )
+ {
+ init( r );
+ }
+
+ RecipeListItem( CategoryListItem* it, TQListViewItem *after, const Recipe &r ) : TQListViewItem( it, after )
+ {
+ init( r );
+ }
+
+ RecipeListItem( TQListViewItem* it, const Recipe &r ) : TQListViewItem( it )
+ {
+ init( r );
+ }
+
+ int rtti() const
+ {
+ return RECIPELISTITEM_RTTI;
+ }
+
+ ~RecipeListItem( void )
+ {
+ delete recipeStored;
+ }
+
+ int recipeID() const
+ {
+ return recipeStored->recipeID;
+ }
+ TQString title() const
+ {
+ return recipeStored->title;
+ }
+
+ void setRecipeID( int id )
+ {
+ recipeStored->recipeID = id;
+ }
+ void setTitle( const TQString &title )
+ {
+ recipeStored->title = title;
+ }
+
+protected:
+ Recipe *recipeStored;
+
+public:
+ virtual TQString text( int column ) const
+ {
+ switch ( column ) {
+ case 0:
+ return ( recipeStored->title );
+ break;
+ case 1:
+ return ( TQString::number( recipeStored->recipeID ) );
+ break;
+ default:
+ return ( TQString::null );
+ }
+ }
+
+private:
+ void init( const Recipe &r )
+ {
+ recipeStored = new Recipe();
+
+ //note: we only store the title and id
+ recipeStored->recipeID = r.recipeID;
+ recipeStored->title = r.title;
+ }
+};
+
+class RecipeItemDrag : public TQStoredDrag
+{
+public:
+ RecipeItemDrag( RecipeListItem *recipeItem, TQWidget *dragSource = 0, const char *name = 0 );
+ static bool canDecode( TQMimeSource* e );
+ static bool decode( const TQMimeSource* e, RecipeListItem& item );
+};
+
+class RecipeListView : public StdCategoryListView
+{
+ TQ_OBJECT
+
+public:
+ RecipeListView( TQWidget *parent, RecipeDB *db );
+
+public slots:
+ void populateAll( TQListViewItem *parent = 0 );
+
+protected slots:
+ virtual void createRecipe( const Recipe &, int parent_id );
+ virtual void createRecipe( const Element &recipe, const ElementList &categories );
+ virtual void modifyRecipe( const Element &recipe, const ElementList &categories );
+ virtual void removeRecipe( int );
+ virtual void removeRecipe( int, int );
+
+protected:
+ virtual void init();
+ virtual void createElement( TQListViewItem * );
+ virtual void removeCategory( int id );
+ virtual TQDragObject *dragObject();
+ virtual bool acceptDrag( TQDropEvent *event ) const;
+ virtual void populate( TQListViewItem *item );
+ virtual TQString tooltip(TQListViewItem *item, int column) const;
+
+ friend class RecipeListToolTip;
+
+ void load(int limit, int offset);
+
+private:
+ void moveChildrenToRoot( TQListViewItem * );
+
+ bool flat_list;
+ TQListViewItem *m_uncat_item;
+ TQListViewItem *lastElementCurrLevel;
+
+ KProgressDialog *m_progress_dlg;
+};
+
+#endif //RECIPELISTVIEW_H
diff --git a/src/widgets/unitcombobox.cpp b/src/widgets/unitcombobox.cpp
new file mode 100644
index 0000000..526f61a
--- /dev/null
+++ b/src/widgets/unitcombobox.cpp
@@ -0,0 +1,145 @@
+/***************************************************************************
+* Copyright (C) 2006 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "unitcombobox.h"
+
+#include <tqlistbox.h>
+
+#include <tdelocale.h>
+#include <tdeconfig.h>
+#include <tdeglobal.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/elementlist.h"
+
+UnitComboBox::UnitComboBox( TQWidget *parent, RecipeDB *db, Unit::Type type ) : KComboBox( parent ),
+ database( db ), m_type(type)
+{
+ connect( database, TQ_SIGNAL( unitCreated( const Unit & ) ), TQ_SLOT( createUnit( const Unit & ) ) );
+ connect( database, TQ_SIGNAL( unitRemoved( int ) ), TQ_SLOT( removeUnit( int ) ) );
+}
+
+void UnitComboBox::popup()
+{
+ if ( count() == 1 )
+ reload();
+ KComboBox::popup();
+}
+
+Unit UnitComboBox::unit() const
+{
+ Unit u;
+ u.name = currentText();
+ u.id = id(currentItem());
+ return u;
+}
+
+void UnitComboBox::reload()
+{
+ TQString remember_filter = currentText();
+
+ UnitList unitList;
+ database->loadUnits( &unitList, m_type );
+
+ clear();
+ unitComboRows.clear();
+
+ //Now load the categories
+ loadUnits(unitList);
+
+ if ( listBox()->findItem( remember_filter, TQt::ExactMatch ) ) {
+ setCurrentText( remember_filter );
+ }
+}
+
+void UnitComboBox::loadUnits( const UnitList &unitList )
+{
+ int row = 0;
+ for ( UnitList::const_iterator it = unitList.begin(); it != unitList.end(); ++it ) {
+ insertItem( (*it).name );
+ unitComboRows.insert( row, (*it).id ); // store unit id's in the combobox position to obtain the unit id later
+ row++;
+ }
+}
+
+void UnitComboBox::setSelected( int unitID )
+{
+ //do a reverse lookup on the row->id map
+ TQMap<int, int>::const_iterator it;
+ for ( it = unitComboRows.begin(); it != unitComboRows.end(); ++it ) {
+ if ( it.data() == unitID ) {
+ setCurrentItem(it.key());
+ break;
+ }
+ }
+}
+
+int UnitComboBox::id( int row ) const
+{
+ return unitComboRows[ row ];
+}
+
+void UnitComboBox::createUnit( const Unit &element )
+{
+ int row = findInsertionPoint( element.name );
+
+ insertItem( element.name, row );
+
+ //now update the map by pushing everything after this item down
+ TQMap<int, int> new_map;
+ for ( TQMap<int, int>::iterator it = unitComboRows.begin(); it != unitComboRows.end(); ++it ) {
+ if ( it.key() >= row ) {
+ new_map.insert( it.key() + 1, it.data() );
+ }
+ else
+ new_map.insert( it.key(), it.data() );
+ }
+ unitComboRows = new_map;
+ unitComboRows.insert( row, element.id );
+}
+
+void UnitComboBox::removeUnit( int id )
+{
+ int row = -1;
+ for ( TQMap<int, int>::iterator it = unitComboRows.begin(); it != unitComboRows.end(); ++it ) {
+ if ( it.data() == id ) {
+ row = it.key();
+ removeItem( row );
+ unitComboRows.remove( it );
+ break;
+ }
+ }
+
+ if ( row == -1 )
+ return ;
+
+ //now update the map by pushing everything after this item up
+ TQMap<int, int> new_map;
+ for ( TQMap<int, int>::iterator it = unitComboRows.begin(); it != unitComboRows.end(); ++it ) {
+ if ( it.key() > row ) {
+ new_map.insert( it.key() - 1, it.data() );
+ }
+ else
+ new_map.insert( it.key(), it.data() );
+ }
+ unitComboRows = new_map;
+}
+
+int UnitComboBox::findInsertionPoint( const TQString &name )
+{
+ for ( int i = 1; i < count(); i++ ) {
+ if ( TQString::localeAwareCompare( name, text( i ) ) < 0 )
+ return i;
+ }
+
+ return count();
+}
+
+#include "unitcombobox.moc"
diff --git a/src/widgets/unitcombobox.h b/src/widgets/unitcombobox.h
new file mode 100644
index 0000000..dd75107
--- /dev/null
+++ b/src/widgets/unitcombobox.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+* Copyright (C) 2006 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef UNITCOMBOBOX_H
+#define UNITCOMBOBOX_H
+
+#include <kcombobox.h>
+
+#include <tqmap.h>
+
+#include "datablocks/unit.h"
+
+class RecipeDB;
+
+class UnitComboBox : public KComboBox
+{
+ TQ_OBJECT
+
+public:
+ UnitComboBox( TQWidget *parent, RecipeDB *db, Unit::Type type = Unit::All );
+
+ void reload();
+ int id( int row ) const;
+ void setSelected( int unitID );
+ Unit unit() const;
+
+protected:
+ virtual void popup();
+
+private slots:
+ void createUnit( const Unit & );
+ void removeUnit( int id );
+
+ int findInsertionPoint( const TQString &name );
+
+private:
+ void loadUnits( const UnitList &unitList );
+
+ RecipeDB *database;
+ TQMap<int, int> unitComboRows; // Contains the unit id for every given row in the unit combobox
+ Unit::Type m_type;
+};
+
+#endif //UNITCOMBOBOX_H
+
diff --git a/src/widgets/unitlistview.cpp b/src/widgets/unitlistview.cpp
new file mode 100644
index 0000000..9c70b1b
--- /dev/null
+++ b/src/widgets/unitlistview.cpp
@@ -0,0 +1,369 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* *
+* Copyright (C) 2006 Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "unitlistview.h"
+
+#include <tqcombobox.h>
+#include <tqheader.h>
+
+#include <tdemessagebox.h>
+#include <tdeconfig.h>
+#include <tdelocale.h>
+#include <tdeglobal.h>
+#include <kiconloader.h>
+#include <tdepopupmenu.h>
+#include <kdebug.h>
+
+#include "backends/recipedb.h"
+#include "dialogs/createunitdialog.h"
+#include "dialogs/dependanciesdialog.h"
+#include "datablocks/unit.h"
+
+class UnitListViewItem : public TQListViewItem
+{
+public:
+ UnitListViewItem( TQListView* qlv, const Unit &u ) : TQListViewItem( qlv ), m_unit(u)
+ {
+ updateType(m_unit.type);
+ }
+
+ virtual TQString text( int column ) const
+ {
+ switch ( column ) {
+ case 0: return m_unit.name;
+ case 1: return m_unit.name_abbrev;
+ case 2: return m_unit.plural;
+ case 3: return m_unit.plural_abbrev;
+ case 4: return m_type;
+ case 5: return TQString::number(m_unit.id);
+ default: return TQString::null;
+ }
+ }
+
+ void setType( Unit::Type type ){ m_unit.type = type; updateType(type); }
+
+ Unit unit() const { return m_unit; };
+ void setUnit( const Unit &u ) { m_unit = u; }
+
+protected:
+ virtual void setText( int column, const TQString &text ) {
+ switch ( column ) {
+ case 0: m_unit.name = text; break;
+ case 1: m_unit.name_abbrev = text; break;
+ case 2: m_unit.plural = text; break;
+ case 3: m_unit.plural_abbrev = text; break;
+ }
+ }
+
+private:
+ void updateType( Unit::Type t ) {
+ switch ( t ) {
+ case Unit::Other: m_type = i18n("Other"); break;
+ case Unit::Mass: m_type = i18n("Mass"); break;
+ case Unit::Volume: m_type = i18n("Volume"); break;
+ default: break;
+ }
+ }
+
+ Unit m_unit;
+ TQString m_type;
+};
+
+UnitListView::UnitListView( TQWidget *parent, RecipeDB *db ) : DBListViewBase( parent,db,db->unitCount() )
+{
+ setAllColumnsShowFocus( true );
+ setDefaultRenameAction( TQListView::Reject );
+}
+
+void UnitListView::init()
+{
+ connect( database, TQ_SIGNAL( unitCreated( const Unit & ) ), TQ_SLOT( checkCreateUnit( const Unit & ) ) );
+ connect( database, TQ_SIGNAL( unitRemoved( int ) ), TQ_SLOT( removeUnit( int ) ) );
+}
+
+void UnitListView::load( int limit, int offset )
+{
+ UnitList unitList;
+ database->loadUnits( &unitList, Unit::All, limit, offset );
+
+ for ( UnitList::const_iterator it = unitList.begin(); it != unitList.end(); ++it ) {
+ if ( !( *it ).name.isEmpty() || !( *it ).plural.isEmpty() )
+ createUnit( *it );
+ }
+}
+
+void UnitListView::checkCreateUnit( const Unit &el )
+{
+ if ( handleElement(el.name) ) { //only create this unit if the base class okays it
+ createUnit(el);
+ }
+}
+
+
+StdUnitListView::StdUnitListView( TQWidget *parent, RecipeDB *db, bool editable ) : UnitListView( parent, db )
+{
+ addColumn( i18n( "Unit" ) );
+ addColumn( i18n( "Abbreviation" ) );
+ addColumn( i18n( "Plural" ) );
+ addColumn( i18n( "Abbreviation" ) );
+ addColumn( i18n( "Type" ) );
+
+ TDEConfig * config = TDEGlobal::config();
+ config->setGroup( "Advanced" );
+ bool show_id = config->readBoolEntry( "ShowID", false );
+ addColumn( i18n( "Id" ), show_id ? -1 : 0 );
+
+ if ( editable ) {
+ setRenameable( 0, true );
+ setRenameable( 1, true );
+ setRenameable( 2, true );
+ setRenameable( 3, true );
+ setRenameable( 4, true );
+
+ TDEIconLoader il;
+
+ kpop = new TDEPopupMenu( this );
+ kpop->insertItem( il.loadIcon( "document-new", TDEIcon::NoGroup, 16 ), i18n( "&Create" ), this, TQ_SLOT( createNew() ), CTRL + Key_C );
+ kpop->insertItem( il.loadIcon( "edit-delete", TDEIcon::NoGroup, 16 ), i18n( "&Delete" ), this, TQ_SLOT( remove
+ () ), Key_Delete );
+ kpop->insertItem( il.loadIcon( "edit", TDEIcon::NoGroup, 16 ), i18n( "&Rename" ), this, TQ_SLOT( rename() ), CTRL + Key_R );
+ kpop->polish();
+
+ typeComboBox = new TQComboBox( false, viewport() );
+ typeComboBox->insertItem(i18n("Other"));
+ typeComboBox->insertItem(i18n("Mass"));
+ typeComboBox->insertItem(i18n("Volume"));
+ addChild( typeComboBox );
+ typeComboBox->hide();
+
+ connect( typeComboBox, TQ_SIGNAL( activated(int) ), TQ_SLOT( updateType(int) ) );
+ connect( this, TQ_SIGNAL( selectionChanged() ), TQ_SLOT( hideTypeCombo() ) );
+
+ connect( this, TQ_SIGNAL( contextMenu( TDEListView *, TQListViewItem *, const TQPoint & ) ), TQ_SLOT( showPopup( TDEListView *, TQListViewItem *, const TQPoint & ) ) );
+ connect( this, TQ_SIGNAL( doubleClicked( TQListViewItem*, const TQPoint &, int ) ), this, TQ_SLOT( modUnit( TQListViewItem*, const TQPoint &, int ) ) );
+ connect( this, TQ_SIGNAL( itemRenamed( TQListViewItem*, const TQString &, int ) ), this, TQ_SLOT( saveUnit( TQListViewItem*, const TQString &, int ) ) );
+ }
+}
+
+void StdUnitListView::insertTypeComboBox( TQListViewItem* it )
+{
+ TQRect r;
+
+ // Constraints Box1
+ r = header() ->sectionRect( 4 ); //start at the section 2 header
+ r.moveBy( 0, itemRect( it ).y() ); //Move down to the item, note that its height is same as header's right now.
+
+ r.setHeight( it->height() ); // Set the item's height
+ r.setWidth( header() ->sectionRect( 4 ).width() ); // and width
+ typeComboBox->setGeometry( r );
+
+ UnitListViewItem *unit_it = (UnitListViewItem*)it;
+ typeComboBox->setCurrentItem( unit_it->unit().type );
+
+ typeComboBox->show();
+}
+
+void StdUnitListView::updateType( int type )
+{
+ UnitListViewItem *unit_it = (UnitListViewItem*)currentItem();
+ unit_it->setType((Unit::Type)type);
+
+ database->modUnit( unit_it->unit() );
+}
+
+void StdUnitListView::hideTypeCombo()
+{
+ typeComboBox->hide();
+}
+
+void StdUnitListView::showPopup( TDEListView * /*l*/, TQListViewItem *i, const TQPoint &p )
+{
+ if ( i )
+ kpop->exec( p );
+}
+
+void StdUnitListView::createNew()
+{
+ CreateUnitDialog * unitDialog = new CreateUnitDialog( this );
+
+ if ( unitDialog->exec() == TQDialog::Accepted ) {
+ Unit result = unitDialog->newUnit();
+
+ //check bounds first
+ if ( checkBounds( result )
+ && database->findExistingUnitByName( result.name ) == -1
+ && database->findExistingUnitByName( result.plural ) == -1
+ ) {
+ database->createNewUnit( result );
+ }
+ }
+ delete unitDialog;
+}
+
+void StdUnitListView::remove()
+{
+ // Find selected unit item
+ UnitListViewItem* it = (UnitListViewItem*)currentItem();
+
+ if ( it ) {
+ int unitID = it->unit().id;
+
+ ElementList recipeDependancies, propertyDependancies, weightDependancies;
+ database->findUnitDependancies( unitID, &propertyDependancies, &recipeDependancies, &weightDependancies );
+
+ TQValueList<ListInfo> lists;
+ if ( !recipeDependancies.isEmpty() ) {
+ ListInfo info;
+ info.list = recipeDependancies;
+ info.name = i18n("Recipes");
+ lists << info;
+ }
+ if ( !propertyDependancies.isEmpty() ) {
+ ListInfo info;
+ info.list = propertyDependancies;
+ info.name = i18n("Properties");
+ lists << info;
+ }
+ if ( !weightDependancies.isEmpty() ) {
+ ListInfo info;
+ info.list = weightDependancies;
+ info.name = i18n("Ingredient Weights");
+ lists << info;
+ }
+
+ if ( lists.isEmpty() )
+ database->removeUnit( unitID );
+ else { // need warning!
+ DependanciesDialog warnDialog( this, lists );
+ if ( !recipeDependancies.isEmpty() )
+ warnDialog.setCustomWarning( i18n("You are about to permanantly delete recipes from your database.") );
+ if ( warnDialog.exec() == TQDialog::Accepted )
+ database->removeUnit( unitID );
+ }
+ }
+}
+
+void StdUnitListView::rename()
+{
+ TQListViewItem * item = currentItem();
+
+ if ( item ) {
+ CreateUnitDialog unitDialog( this, item->text(0), item->text(2), item->text(1), item->text(3), false );
+
+ if ( unitDialog.exec() == TQDialog::Accepted ) {
+ UnitListViewItem *unit_item = (UnitListViewItem*)item;
+ Unit origUnit = unit_item->unit();
+ Unit newUnit = unitDialog.newUnit();
+
+ //for each changed entry, save the change individually
+
+ Unit unit = origUnit;
+
+ if ( newUnit.name != origUnit.name ) {
+ unit.name = newUnit.name;
+ unit_item->setUnit( unit );
+ saveUnit( unit_item, newUnit.name, 0 );
+
+ //saveUnit will call database->modUnit which deletes the list item we were using
+ unit_item = (UnitListViewItem*) findItem( TQString::number(unit.id), 5 );
+ }
+
+ if ( newUnit.plural != origUnit.plural ) {
+ unit.plural = newUnit.plural;
+ unit_item->setUnit( unit );
+ saveUnit( unit_item, newUnit.plural, 2 );
+ unit_item = (UnitListViewItem*) findItem( TQString::number(unit.id), 5 );
+ }
+
+ if ( !newUnit.name_abbrev.stripWhiteSpace().isEmpty() && newUnit.name_abbrev != origUnit.name_abbrev ) {
+ unit.name_abbrev = newUnit.name_abbrev;
+ unit_item->setUnit( unit );
+ saveUnit( unit_item, newUnit.name_abbrev, 1 );
+ unit_item = (UnitListViewItem*) findItem( TQString::number(unit.id), 5 );
+ }
+ if ( !newUnit.plural_abbrev.stripWhiteSpace().isEmpty() && newUnit.plural_abbrev != origUnit.plural_abbrev ) {
+ unit.plural_abbrev = newUnit.plural_abbrev;
+ unit_item->setUnit( unit );
+ saveUnit( unit_item, newUnit.plural_abbrev, 3 );
+ }
+ }
+ }
+}
+
+void StdUnitListView::createUnit( const Unit &unit )
+{
+ createElement(new UnitListViewItem( this, unit ));
+}
+
+void StdUnitListView::removeUnit( int id )
+{
+ TQListViewItem * item = findItem( TQString::number( id ), 5 );
+ removeElement(item);
+}
+
+void StdUnitListView::modUnit( TQListViewItem* i, const TQPoint & /*p*/, int c )
+{
+ if ( i ) {
+ if ( c != 4 )
+ UnitListView::rename( i, c );
+ else {
+ insertTypeComboBox(i);
+ }
+ }
+}
+
+void StdUnitListView::saveUnit( TQListViewItem* i, const TQString &text, int c )
+{
+ //skip abbreviations
+ if ( c == 0 || c == 2 ) {
+ if ( !checkBounds( Unit( text, text ) ) ) {
+ reload(ForceReload); //reset the changed text
+ return ;
+ }
+ }
+
+ int existing_id = database->findExistingUnitByName( text );
+
+ UnitListViewItem *unit_it = (UnitListViewItem*)i;
+ int unit_id = unit_it->unit().id;
+ if ( existing_id != -1 && existing_id != unit_id && !text.stripWhiteSpace().isEmpty() ) { //unit already exists with this label... merge the two
+ switch ( KMessageBox::warningContinueCancel( this, i18n( "This unit already exists. Continuing will merge these two units into one. Are you sure?" ) ) ) {
+ case KMessageBox::Continue: {
+ database->modUnit( unit_it->unit() );
+ database->mergeUnits( unit_id, existing_id );
+ break;
+ }
+ default:
+ reload(ForceReload);
+ break;
+ }
+ }
+ else {
+ database->modUnit( unit_it->unit() );
+ }
+}
+
+bool StdUnitListView::checkBounds( const Unit &unit )
+{
+ if ( unit.name.length() > uint(database->maxUnitNameLength()) || unit.plural.length() > uint(database->maxUnitNameLength()) ) {
+ KMessageBox::error( this, TQString( i18n( "Unit name cannot be longer than %1 characters." ) ).arg( database->maxUnitNameLength() ) );
+ return false;
+ }
+ else if ( unit.name.stripWhiteSpace().isEmpty() || unit.plural.stripWhiteSpace().isEmpty() )
+ return false;
+
+ return true;
+}
+
+#include "unitlistview.moc"
diff --git a/src/widgets/unitlistview.h b/src/widgets/unitlistview.h
new file mode 100644
index 0000000..b71b67f
--- /dev/null
+++ b/src/widgets/unitlistview.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+* Copyright (C) 2004 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* Unai Garro (ugarro@users.sourceforge.net) *
+* Cyril Bosselut (bosselut@b1project.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef UNITLISTVIEW_H
+#define UNITLISTVIEW_H
+
+#include "dblistviewbase.h"
+
+#include "datablocks/unit.h"
+
+class TQComboBox;
+
+class RecipeDB;
+class TDEPopupMenu;
+
+class UnitListView : public DBListViewBase
+{
+ TQ_OBJECT
+
+public:
+ UnitListView( TQWidget *parent, RecipeDB *db );
+
+public slots:
+ virtual void load( int curr_limit, int curr_offset );
+
+protected slots:
+ virtual void createUnit( const Unit & ) = 0;
+ virtual void removeUnit( int ) = 0;
+
+ void checkCreateUnit( const Unit &el );
+
+protected:
+ virtual void init();
+};
+
+class StdUnitListView : public UnitListView
+{
+ TQ_OBJECT
+
+public:
+ StdUnitListView( TQWidget *parent, RecipeDB *db, bool editable = false );
+
+protected:
+ virtual void createUnit( const Unit & );
+ virtual void removeUnit( int );
+
+private slots:
+ void showPopup( TDEListView *, TQListViewItem *, const TQPoint & );
+ void hideTypeCombo();
+ void updateType( int type );
+
+ void createNew();
+ void remove();
+ void rename();
+
+ void modUnit( TQListViewItem* i, const TQPoint &p, int c );
+ void saveUnit( TQListViewItem* i, const TQString &text, int c );
+
+private:
+ bool checkBounds( const Unit &unit );
+ void insertTypeComboBox( TQListViewItem* );
+
+ TDEPopupMenu *kpop;
+ TQComboBox *typeComboBox;
+};
+
+#endif //UNITLISTVIEW_H
diff --git a/src/widgets/weightinput.cpp b/src/widgets/weightinput.cpp
new file mode 100644
index 0000000..17d3d0a
--- /dev/null
+++ b/src/widgets/weightinput.cpp
@@ -0,0 +1,63 @@
+/***************************************************************************
+* Copyright (C) 2006 by *
+* Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#include "weightinput.h"
+
+#include <tdelocale.h>
+
+#include "backends/recipedb.h"
+#include "datablocks/element.h"
+#include "datablocks/weight.h"
+#include "prepmethodcombobox.h"
+
+WeightInput::WeightInput( TQWidget *parent, RecipeDB *database, Unit::Type type, MixedNumber::Format format ) :
+ AmountUnitInput(parent,database,type,format),
+ m_database(database)
+{
+ prepMethodBox = new PrepMethodComboBox(false,this,database,i18n("-No Preparation-"));
+ prepMethodBox->reload();
+
+ connect( prepMethodBox, TQ_SIGNAL(activated(int)), TQ_SLOT(emitValueChanged()) );
+}
+
+void WeightInput::emitValueChanged()
+{
+ Weight w;
+ w.perAmount = amount().toDouble();
+
+ Unit u = unit();
+ w.perAmountUnitID = u.id;
+ w.perAmountUnit = (w.perAmount>1)?u.plural:u.name;
+
+ Element prep;
+ w.prepMethod = prep.name;
+ w.prepMethodID = prep.id;
+ emit valueChanged( w );
+}
+
+void WeightInput::setPrepMethod( const Element &prep )
+{
+ if ( prep.id == -1 )
+ prepMethodBox->setCurrentItem(0);
+ else
+ prepMethodBox->setSelected( prep.id );
+
+}
+
+Element WeightInput::prepMethod() const
+{
+ Element prep;
+ prep.id = prepMethodBox->id( prepMethodBox->currentItem() );
+ if ( prep.id != -1 )
+ prep.name = prepMethodBox->currentText();
+ return prep;
+}
+
+#include "weightinput.moc"
diff --git a/src/widgets/weightinput.h b/src/widgets/weightinput.h
new file mode 100644
index 0000000..83ff6b3
--- /dev/null
+++ b/src/widgets/weightinput.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+* Copyright (C) 2006 Jason Kivlighn (jkivlighn@gmail.com) *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+***************************************************************************/
+
+#ifndef WEIGHTINPUT_H
+#define WEIGHTINPUT_H
+
+#include "widgets/amountunitinput.h"
+#include "datablocks/unit.h"
+
+class RecipeDB;
+class PrepMethodComboBox;
+class Element;
+class Weight;
+
+class WeightInput : public AmountUnitInput
+{
+TQ_OBJECT
+
+public:
+ WeightInput( TQWidget *parent, RecipeDB *database, Unit::Type type = Unit::All, MixedNumber::Format f = MixedNumber::MixedNumberFormat );
+
+ Element prepMethod() const;
+ void setPrepMethod( const Element & );
+
+public slots:
+ void emitValueChanged();
+
+signals:
+ void valueChanged( const Weight & );
+
+private:
+ PrepMethodComboBox *prepMethodBox;
+
+ RecipeDB *m_database;
+};
+#endif