/***************************************************************************
    copyright     : (C) 1999 by Edwin Glaser
    email         : edwin@pannenleiter.de
    version       : $Id: dbdatasource.h,v 1.1.1.1 2000/02/07 21:41:55 ege Exp $
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 DBDATASOURCE_H
#define DBDATASOURCE_H

#include <qobject.h>
#include <qdatetime.h>

class DBRecord;

class DBLabel;
class DBLabelFactory;
class DBLineEdit;
class DBLineEditFactory;
class DBMultiLineEdit;
class DBMultiLineEditFactory;
class DBListBox;
class DBListBoxFactory;
class DBComboBox;
class DBComboBoxFactory;
class DBEditComboBoxFactory;
class DBGrid;
class DBNavigator;

class DBValueAdaptor;
class DBLookupAdaptor;

class DBPainter;
class DBWidgetFactory;
class DBField;
class DBColumnDescription;
class DBTableDescription;
class DBMasterDescription;

/** General part of the manager.
  *  
  * The manager consists of two classes. 
  * DBDatasource keeps track of the state and emits the signals.
  * A class derrived from DBRecord handles the database connection.
  * 
  * @author Edwin Glaser
  * @version $Name:  $ $Date: 2000/02/07 21:41:55 $
  */

class DBDataSource : public QObject
{
  Q_OBJECT

public: 

  enum state { Inactive, Error, Browse, Edit, Insert, CalcFields, DontChange};
  static char *stateNames[6];
  bool inCascadingRemove;

protected:

  DBRecord *record;

  state currentState;
  long prevCursor;
  long calcCursor;
  long browseCursor;

  int widgetsDisabled;
  state saveState;

  DBDataSource *master;
  DBMasterDescription *masterDef;
    
  bool autoEdit;
  bool autoPost;
  bool autoRefresh;
  bool userReadOnly;
  bool suspendUpdate;
  bool isAborted;
  bool userEnabled;

  void setState(state newState, bool force = false);
  void setCursor(state newState = DontChange, bool forceState = false, bool force = false, bool isCopy = false);
  bool setEditable(bool force = false);
  bool checkPost(bool force = false);

  bool openIt(bool forceReopen);
  void closeIt();
  bool moveWithoutCheck(state newState, bool forceState, long pos);

public: 

/** The constructor expects a record created with new. The destructor will delete the record.
  */
  DBDataSource(DBRecord *rec);

/** Destructor. 
  */
  virtual ~DBDataSource();


/** Thin wrapper for DBRecord::setQuery
  * 
  * @see DBRecord
  */
  virtual void setQuery(const QString query, bool removeTables = false);

/** Thin wrapper for DBRecord::setTable
  * 
  * @see DBRecord
  */
  virtual void addTable(const char *table, const char *primaries, const char *columns);

/** Restricts the dataset to the correspontin detail-recors of the master-record.
  * You have to call setQuery with a proper select statement.
  * It must include parameters for the foreign keys eg. "select * from order where custid = ?"
  * 
  * @param master The master of this detail datasource.
  * @param join This string describes the foreign keys of the select statement.
  *             Pairs of index=index seperated by blanks eg. "1=0 2=1".
  *             The first one is the index of the field of the master datasource.
  *             The second is the index of the parameter.
  * @param init Appending a new record will initialize the foreign keys from the corresonding fields.
  *             Pairs of index=index seperated by blanks eg. "1=0 2=1".
  *             The first one is the index of the field of the master datasource.
  *             The second is the index of the field of this source.
  * @param cascadeRemove If true, removeing a master record also removes the detail records.
  */
  virtual void setMaster(DBDataSource *master, const char *join, const char *init = 0, bool cascadeRemove = false);

/** Thin wrapper for DBRecord::getNumCols
  */
  int getNumCols();

/** Thin wrapper for DBRecord::getColumnName
  */
  const char *getColumnName(int col);

/** Thin wrapper for DBRecord::getColumnQualifierName
  */
  const char *getColumnQualifierName(int col);

/** Thin wrapper for DBRecord::getColumnType
  *
  * @return  DBTypes::TypeLong DBTypes::TypeDouble DBTypes::TypeDate DBTypes::TypeTime DBTypes::TypeDateTime DBTypes::TypeBinary DBTypes::TypeString
  */
  int getColumnType(int col);
  int getColumnType(char *column) { return getColumnType(getColumnIdx(column)); };
  int getColumnType(char *table, char *column) { return getColumnType(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnTypeName
  */
  const char *getColumnTypeName(int col);
  const char *getColumnTypeName(char *column) { return getColumnTypeName(getColumnIdx(column)); };
  const char *getColumnTypeName(char *table, char *column) { return getColumnTypeName(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnLength
  */
  long getColumnLength(int col);
  long getColumnLength(char *column) { return getColumnLength(getColumnIdx(column)); };
  long getColumnLength(char *table, char *column) { return getColumnLength(getColumnIdx(table, column)); }; 

/** Thin wrapper for DBRecord::getColumnPrecision
  */
  int getColumnPrecision(int col);
  int getColumnPrecision(char *column) { return getColumnPrecision(getColumnIdx(column)); };
  int getColumnPrecision(char *table, char *column) { return getColumnPrecision(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnScale
  */
  int getColumnScale(int col);
  int getColumnScale(char *column) { return getColumnScale(getColumnIdx(column)); };
  int getColumnScale(char *table, char *column) { return getColumnScale(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnDisplaySize
  */
  long getColumnDisplaySize(int col);
  long getColumnDisplaySize(char *column) { return getColumnDisplaySize(getColumnIdx(column)); };
  long getColumnDisplaySize(char *table, char *column) { return getColumnDisplaySize(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnNullable
  */
  int getColumnNullable(int col);
  int getColumnNullable(char *column) { return getColumnNullable(getColumnIdx(column)); };
  int getColumnNullable(char *table, char *column) { return getColumnNullable(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnUnsigned
  */
  int getColumnUnsigned(int col);
  int getColumnUnsigned(char *column) { return getColumnUnsigned(getColumnIdx(column)); };
  int getColumnUnsigned(char *table, char *column) { return getColumnUnsigned(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnMoney
  */
  int getColumnMoney(int col);
  int getColumnMoney(char *column) { return getColumnMoney(getColumnIdx(column)); };
  int getColumnMoney(char *table, char *column) { return getColumnMoney(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnUpdatable
  */
  int getColumnUpdatable(int col);
  int getColumnUpdatable(char *column) { return getColumnUpdatable(getColumnIdx(column)); };
  int getColumnUpdatable(char *table, char *column) { return getColumnUpdatable(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getColumnAutoIncrement
  */
  int getColumnAutoIncrement(int col);
  int getColumnAutoIncrement(char *column) { return getColumnAutoIncrement(getColumnIdx(column)); };
  int getColumnAutoIncrement(char *table, char *column) { return getColumnAutoIncrement(getColumnIdx(table, column)); };

/** Thin wrapper for DBRecord::getNumRows
  */
  long getNumRows();


/** The datasource is active.
  */
  bool isActive() { return currentState != Inactive && currentState != Error && currentState != CalcFields; };

/** An error occured.
  */
  bool isError() { return currentState == Error; };

/** A value of the record was changed but not posted to the database.
  */
  bool isEditMode() { return currentState == Edit; };

/** A record was appended but not posted to the database. 
  */
  bool isInsertMode() { return currentState == Insert; };

/** You can call setValue / getValue.
  */
  bool hasRecord() { return currentState != Inactive && currentState != Error && !isBOF() && !isEOF(); };

/** You cal call post
  */
  bool isReadOnly();

/** A widget may offer editing.
  */
  bool isEditable() { return hasRecord() && !isReadOnly() && (autoEdit || currentState == Edit || currentState == Insert); };

/** SetValue will switch to edit mode.
  */
  bool isAutoEdit() { return autoEdit; };

/** Changed fields will be written to the database before changing the database cursor.
  */
  bool isAutoPost() { return autoPost; };

/** Returns the state: Inactive, Error, Browse, Edit, Insert, CalcFields
  */
  state getState() { return currentState; };

/** Translates the state id to a string representation.
  */
  const char *getStateName(state s) { return stateNames[s]; };

/** Thin wrapper for DBRecord::getErrorInfo
  */
  const QString &getErrorInfo();


/** Thin wrapper for DBRecord::getCursor
  */
  long getCursor();

/** Is the cursor before th first record ?
  */
  bool isBOF();

/** Is the cursor behind the last record ?
  */
  bool isEOF();


/** Wrapper for DBRecord::getValue
  *
  * @param col the column index, 0 based
  * @param format A printf or strftime format string. WARNING the resust string must not exeed 8000 bytes!
  * The format must correspond to getColumnType()
  * <table>
  * <tr><td>type</td><td>called by</td></tr>
  * <tr><td>DBTypes::TypeLong</td><td>sprintf(buf, format, *(long *)buffer);</td></tr>
  * <tr><td>DBTypes::TypeDouble</td><td>sprintf(buf, format, *(double*)buffer);</td></tr>
  * <tr><td>DBTypes::TypeDate</td><td>strftime(buf, 99, format &t);</td></tr>
  * <tr><td>DBTypes::TypeTime</td><td>strftime(buf, 99, format &t);</td></tr>
  * <tr><td>DBTypes::TypeDateTime</td><td>strftime(buf, 99, format &t);</td></tr>
  * <tr><td>DBTypes::TypeBinary</td><td>sprintf(buf, format, (char *)buffer);</td></tr>
  * <tr><td>DBTypes::TypeString</td><td>sprintf(buf, format, (char *)buffer);</td></tr>
  * </table>
  */
  QString getQString(int col, const char *format = 0);
  QString getQString(char *column) { return getQString(getColumnIdx(column)); };
  QString getQString(char *table, char *column) { return getQString(getColumnIdx(table, column)); };

/** Wrapper for DBRecord::getValue
  */
  QDate getQDate(int col);
  QDate getQDate(char *column) { return getQDate(getColumnIdx(column)); };
  QDate getQDate(char *table, char *column) { return getQDate(getColumnIdx(table, column)); };

/** Wrapper for DBRecord::getValue
  */
  QTime getQTime(int col);
  QTime getQTime(char *column) { return getQTime(getColumnIdx(column)); };
  QTime getQTime(char *table, char *column) { return getQTime(getColumnIdx(table, column)); };

/** Wrapper for DBRecord::getValue
  */
  QDateTime getQDateTime(int col);
  QDateTime getQDateTime(char *column) { return getQDateTime(getColumnIdx(column)); };
  QDateTime getQDateTime(char *table, char *column) { return getQDateTime(getColumnIdx(table, column)); };

/** Wrapper for DBRecord::getValue
  */
  double getDouble(int col);
  double getDouble(char *column) { return getDouble(getColumnIdx(column)); };
  double getDouble(char *table, char *column) { return getDouble(getColumnIdx(table, column)); };

/** Wrapper for DBRecord::getValue
  */
  long getLong(int col);
  long getLong(char *column) { return getLong(getColumnIdx(column)); };
  long getLong(char *table, char *column) { return getLong(getColumnIdx(table, column)); };

/** Has column a null value
  */
  bool isNull(int col);
  bool isNull(char *column) { return getLong(getColumnIdx(column)); };
  bool isNull(char *table, char *column) { return getLong(getColumnIdx(table, column)); };

/** Wrapper for DBRecord::setValue
  * Switches to edit mode, if autoedit is set.
  */
  bool setValue(const QString &str, int column);
  bool setValue(const QString &str, char *column) { return setValue(str, getColumnIdx(column)); };
  bool setValue(const QString &str, char *table, char *column) { return setValue(str, getColumnIdx(table, column)); };

/** Wrapper for DBRecord::setValue
  * Switches to edit mode, if autoedit is set.
  */
  bool setValue(const QDate &val, int column);
  bool setValue(const QDate &val, char *column) { return setValue(val, getColumnIdx(column)); };
  bool setValue(const QDate &val, char *table, char *column) { return setValue(val, getColumnIdx(table, column)); };

/** Wrapper for DBRecord::setValue
  * Switches to edit mode, if autoedit is set.
  */
  bool setValue(const QTime &val, int column);
  bool setValue(const QTime &val, char *column) { return setValue(val, getColumnIdx(column)); };
  bool setValue(const QTime &val, char *table, char *column) { return setValue(val, getColumnIdx(table, column)); };

/** Wrapper for DBRecord::setValue
  * Switches to edit mode, if autoedit is set.
  */
  bool setValue(const QDateTime &val, int column);
  bool setValue(const QDateTime &val, char *column) { return setValue(val, getColumnIdx(column)); };
  bool setValue(const QDateTime &val, char *table, char *column) { return setValue(val, getColumnIdx(table, column)); };

/** Wrapper for DBRecord::setValue
  * Switches to edit mode, if autoedit is set.
  */
  bool setValue(double num, int column);
  bool setValue(double num, char *column) { return setValue(num, getColumnIdx(column)); };
  bool setValue(double num, char *table, char *column) { return setValue(num, getColumnIdx(table, column)); };

/** Wrapper for DBRecord::setValue
  * Switches to edit mode, if autoedit is set.
  */
  bool setValue(long num, int column);
  bool setValue(long num, char *column) { return setValue(num, getColumnIdx(column)); };
  bool setValue(long num, char *table, char *column) { return setValue(num, getColumnIdx(table, column)); };

/** sets the value to null
  * Switches to edit mode, if autoedit is set.
  */
  bool setNull(int column);
  bool setNull(char *column) { return setNull(getColumnIdx(column)); };
  bool setNull(char *table, char *column) { return setNull(getColumnIdx(table, column)); };
  
/** Wrapper for DBRecord::setParameter
  */
  bool setParameter(const QString &str, int column);

/** Wrapper for DBRecord::setParameter
  */
  bool setParameter(const QDate &val, int column);

/** Wrapper for DBRecord::setParameter
  */
  bool setParameter(const QTime &val, int column);

/** Wrapper for DBRecord::setParameter
  */
  bool setParameter(const QDateTime &val, int column);

/** Wrapper for DBRecord::setParameter
  */
  bool setParameter(double val, int column);

/** Wrapper for DBRecord::setParameter
  */
  bool setParameter(long val, int column);

/** Get the column index of the column, 0 based
  *
  * @param column the name of the column
  */
  int getColumnIdx(char *column);

/** Get the column index of the column, 0 based
  *
  * @param table the name of the table respectively the alias
  *        (Id did'n work for me, the mysql odbc driver hides the tablename ???)
  * @param column the name of the column
  */
  int getColumnIdx(char *table, char *column);

protected slots:
  
  void masterStateChanged();
  void masterCursorChanged();
  void masterRemoved();

public slots:


/** Opens the DBRecord.
  */
  virtual bool open();

/** Closes the DBRecord.
  */
  virtual void close();


/** SetValue will switch to edit mode, default: false.
  */
  virtual void setAutoEdit(bool on);

/** Changed fields will be written to the database before changing the database cursor, default false.
  */
  virtual void setAutoPost(bool on);

/** Fetch resultset from database after post, default true.
  */
  virtual void setAutoRefresh(bool on);

/** Marks datasource as readonly. If the DBRecord is readonly the datasource will stay readonly! 
  */
  virtual void setReadOnly(bool on);


/** Switches to edit mode. 
  */
  virtual bool edit();

/** Apends a record and initializes it with 0 resp. "".
  * Switches to insert mode.
  *
  * @param setNull True: init with null values if column allows it.
  *                False init with 0 or "".
  */
  virtual bool append();
  virtual bool append(bool setNull);

/** Apends a record and initializes it from the current record.
  * Switches to insert mode.
  */
  virtual bool copy();

/** Deletes the current record from the database.
  */
  virtual bool remove();

/** Insert the current record if the datasource is in insert mode.
  * Updates the database if the datasource is in edit mode.
  */
  virtual bool post();

/** Fetch resultset from database.
  */
  virtual bool refresh();

/** Cancels the last edit/append/copy
  */
  virtual bool cancel();

/** If you call abort from a slot connected to a beforeXXX signel, it will stop the operation.
  */
  virtual void abort();


/** Wrapper for DBRecord::first
  */
  virtual bool first();

/** Wrapper for DBRecord::prior
  */
  virtual bool prior();

/** Wrapper for DBRecord::next
  */
  virtual bool next();

/** Wrapper for DBRecord::last
  */
  virtual bool last();

/** Wrapper for DBRecord::moveBy
  */
  virtual bool moveBy(long);

/** Wrapper for DBRecord::moveTo
  */
  virtual bool moveTo(long);


/** This method saves the current state and suspresses all signals.
  */
  virtual void disableWidgets();

/** This method resores the state.
  */
  virtual void enableWidgets();

signals:

/** The state of the datasource changed. 
  */
  void stateChanged();

/** The position of the database cursor changed.
  * If an operation changes the state and the cursor stateChanged is emited first.
  * The datasource also emits cursorChanged if the set changed.
  */
  void cursorChanged();

/** A value of a field changed.
  * If the change is caused by a cursor movement, only cursorChanged is emited.
  */
  void dataChanged(int column);
  

/** Something wants to switch to edit mode. You can call abort to keep the old state.
  */
  void beforeEdit();

/** The datasource is now in edit mode.  
  */
  void afterEdit();

/** Something wants to switch to insert mode. You can call abort to keep the old state.
  * 
  * @param isCopy True if the new record will be initialized from the current record. 
  */
  void beforeAppend(bool isCopy);

/** The datasource is now in insert mode.
  * 
  * @param isCopy True if the new record was initialized from the current record. 
  */
  void afterAppend(bool isCopy);

/** Something wants to delete the current record from the database. You can call abort to keep the old state.
  */
  void beforeRemove();
  void beforeRemove2();

/** The database record is deleted and the next current record is now the current one. 
  */
  void afterRemove();

/** Sonething wants to post the current record to the database.  You can call abort to keep the old state.
  */
  void beforePost(bool isInsert);

/** The chaned values have been written to the database.
  */
  void afterPost(bool isInsert);
  void afterPost2();

/** Sonething wants to cancel the edit/append/copy call.  You can call abort to keep the old state.
  */
  void beforeCancel();

/** The edit/append/copy is canceled.
  */
  void afterCancel();
  void afterCancel2();

/** A beforeXXX slot aborted an operation.
  */
  void aborted();
  

/** The dataset fetched a record from the database.
  * A setValue call will not be mark the record for update. 
  */
  void calcFields();

/** A append or copy created a new record. 
  * A setValue call will not be mark the record for insert. 
  */
  void newRecord(bool isCopy);

};

inline int DBDataSource::getColumnIdx(char *column)
{
	const char *cn; 
	for ( int i = 0; (cn = getColumnName(i)); i++ )
	{
		if ( strcmp(column, cn) == 0 ) return i;
	}
	return -1;
}

inline int DBDataSource::getColumnIdx(char *table, char *column)
{
	const char *cn; 
	for ( int i = 0; (cn = getColumnName(i)); i++ )
	{
		if ( strcmp(column, cn) == 0 && strcmp(getColumnQualifierName(i), cn) == 0 ) return i;
	}
	return -1;
}

#endif

Documentation generated by eg@wonko on Sam Feb 19 00:09:53 MET 2000