diff --git a/include/array.cpp b/include/array.cpp index 5e104b024..716356aa6 100755 --- a/include/array.cpp +++ b/include/array.cpp @@ -1,619 +1,619 @@ -#include -#include - -#include -#include - -// @doc INTERNAL - -// @mfunc Modifica la dimensione dell'array. -void TArray::resize( - int arraysize) // @parm Indica la nuova dimensione che deve assumere l'array. - - // @comm Alloca la memoria necessaria per contentere i dati della nuova - // dimensione inizializzando a NULL tutti i nuovi elementi aggiunti - // all'array lasciando inalterati, sia in posizione che in - // contenuto gli elementi gia' presenti. - // Nel caso si cerchi di diminuire la dimensione viene dato un - // messaggio di errore. - -{ - CHECK(arraysize > size(), "Can't reduce array size."); - - TObject** newdata = new TObject* [arraysize]; - - int i = 0; - if (_data != NULL) - for (i = 0; i < size(); i++) newdata[i] = _data[i]; - - while (i < arraysize) newdata[i++] = NULL; - - if (_data != NULL) delete _data; - - _size = arraysize; - _data = newdata; -} - -// @doc EXTERNAL -// -// @mfunc Rimuove uno o tutti (default) gli elementi -bool TArray::destroy( - int index, // @parm Numero dell'elemento da eliminare (default -1) - bool pack) // @parm Se true richiama la funzione per compattare gli elementi dell'array (default FALSE) - - // @comm Permette la rimozione di uno (index > 0 ) o di tutti (default) gli - // elementi dell'array assegnandone il valore NULL. - // Nel caso il parametro pack sia TRUE permette di rendere contigui - // tutti gli elementi diversi da NULL tramite la chiamata alla funzione - // . - // - // @rdesc Ritorna uno dei seguenti parametri: - // - // @flag TRUE | Se l'operazione e' riuscita con successo - // @flag FALSE | Se il numero di elementi e' rimasto invariato -{ - const int old = _items; - - if (index < 0) - { - for (int i = size(); i--;) if (_data[i] != NULL) - { - delete _data[i]; - _data[i] = NULL; - } - _items = _next = 0; - } - else - { - TObject* o = remove(index, pack); - if (o) delete o; - } - - return _items < old; -} - - -TArray::TArray(int arraysize) : _size(0), _items(0), _next(0), _data(NULL) -{ - if (arraysize) resize(arraysize); -} - -TArray::TArray() : _size(0), _items(0), _next(0), _data(NULL) -{ -} - -TArray::TArray(const TArray& a) : _size(0), _items(0), _next(0), _data(NULL) -{ - (*this) = a; -} - -TArray& TArray::operator= (const TArray& a) -{ - destroy(); - if (size() < a.size()) - resize(a.size()); - for (int i = a.size()-1; i >= 0; i--) - { - const TObject* o = a.objptr(i); - if (o != NULL) _data[i] = o->dup(); - } - _items = a.items(); - _next = a._next; - - return *this; -} - -TArray::~TArray() - -{ - if (ok()) - { - destroy(); - delete _data; - } -} - - -const char* TArray::class_name() const -{ - return "Array"; -} - - -word TArray::class_id() const -{ - return CLASS_ARRAY; -} - -// @mfunc Stampa un array -void TArray::print_on( - ostream& out) const // @parm indica l'output sul quale reindirizzare la stampa (funzione standard del C++) - - // @comm Permette di stampare sull'output passato come parametro il contenuto - // dell'array, preceduto dal numero dell'elemetno a cui si riferisce. - // Nel caso vi siano degli elementi vuoti (valore = NULL) viene - // stampato il valore (null). - -{ - for (int i = 0; i < size(); i++) - { - out.width(4); - out << i << ' '; - out.width(); - if (_data[i] != NULL) out << *_data[i]; - else out << "(null)"; - out << endl; - } -} - -// @mfunc Controlla se si tratta di un oggetto valido -bool TArray::ok() const - -// @rdesc Ritorna uno dei seguenti parametri: -// -// @flag TRUE | Se l'array contiene degli elementi -// @flag FALSE | Se l'array e' vuoto -// -// @comm Controlla se la dimensione dell'array e' uguale a 0 e se esistono -// degli elementi diversi da NULL - -{ - return(size() != 0 && (_data != NULL)); -} - -// @mfunc Aggiunge un oggetto ad un array. -int TArray::add( - TObject *object, // @parm Oggetto da aggiungere nell'array - int index) // @parm Posizione in cui aggiungere l'oggetto - // @parm TObject | &object | Oggetto da aggiungere nell'array. L'oggetto viene duplicato - - // @syntax add(TObject* object,int index); - // @syntax add(TObject& object,int index); - // - // @comm Nel caso venga passata una posizione minore di 0 l'elemento viene aggiunto - // in coda all'array, diversamente viene l'elemento presente viene - // sostitito con l'oggetto passato nei prametri. - // Se e' stato passato un indice minore della dimensione dell'array, - // viene aumentata automaticamente la dimensione dell'array stesso tramite - // la chiamata alla funzione . - // ATTENZIONE: Nel caso si passi l'oggetto per indirizzo deve - // essere definita la funzione . - // - // @rdesc La funzione ritorna la posizione nella quale e' stato aggiunto - // l'oggetto. - // - // @xref -{ - if (index < 0) for (index = _next; index < size() && _data[index]; index++); - - if (index >= size()) resize(int(3L*index/2) + 1); - - if (_data[index] != NULL) - { -#ifdef DBG - if (object == _data[index]) - { - error_box("Iu ar traing tu overrait en arrei obgiect: %d", index); - return index; - } -#endif - delete _data[index]; - _items--; - } - - _data[index] = object; - if (object != NULL) - { - _items++; - _next = index+1; - } - else - { - if (index < _next) - _next = index; - } - return index; -} - -// @mfunc Inserisce un elemento dell'array nella posizione index -int TArray::insert( - TObject *object, // @parm Oggetto da inserire nell'array - int index) // @parm Posizione in cui inserire l'oggetto - // @parm TObject | &object | Oggetto da inserire nell'array. L'oggetto viene duplicato - - // @syntax insert(TObject* object, int index); - // @syntax insert(TObject& object, int index); - // - // @comm Nel caso l'elemento della posizione passata sia diverso da NULL vengono - // spostati di una posizione tutti gli elementi presenti nell'array. - // La dimensione dell'array viene automaticamente aumentata nel caso - // la stessa non sia sufficiente a contenere il nuovo oggetto. - // ATTENZIONE: Nel caso si passi l'oggetto per indirizzo deve - // essere definita la funzione . - // - // @rdesc La funzione ritorna la posizione nella quale e' stato inserito l'oggetto. - // - // @xref -{ - if (objptr(index)) - { - if (items() == size()) add(NULL); - for (int i = size()-1; i >= index; i--) - _data[i] = _data[i-1]; - _data[index] = NULL; - } - return add(object, index); -} - - -int TArray::add(const TObject& object, int index) -{ - TObject* objptr = object.dup(); - return add(objptr, index); -} - -int TArray::insert(const TObject& object, int index) -{ - TObject* objptr = object.dup(); - return insert(objptr, index); -} - -// @mfunc Elimina l'elemento nella posizione index dell'array -TObject* TArray::remove( - int index, // @parm Indica la posizione dell'elemento da eliminare - bool dopack) // @parm Indica se si vuole richiamare la funzione - // (default FALSE) per il compattamento dell'array - - // @rdesc Ritorna l'elemento dell'array eliminato senza farne la delete -{ - TObject* o = objptr(index); - if (o) - { - _data[index] = NULL; - _items--; - if (index < _next) - _next = index; - } - if (dopack) pack(); - return o; -} - -void TArray::swap(int i1, int i2) -{ - TObject* o1 = remove(i1, FALSE); - TObject* o2 = remove(i2, FALSE); - if (o1) add(o1, i2); - if (o2) add(o2, i1); -} - -// @mfunc Cerca il prossimo oggetto dell'array -int TArray::succ(int i) const -// @rdesc Ritorna l'indice del prossimo elemento trovato o size() se non esiste -{ - CHECKD(i >= -1, "Bad array index ", i); - for (i++; i < size(); i++) - if (_data[i]) break; - return i; -} - -// @mfunc Cerca il precedente oggetto dell'array -int TArray::pred(int i) const -// @rdesc Ritorna l'indice dell'elemento precedente o -1 se non esiste -{ - CHECKD(i <= size(), "Bad array index ", i); - for (i--; i >= 0; i--) - if (_data[i]) break; - return i; -} - -// @mfunc Cerca il primo oggetto dell'array -int TArray::first() const -// @rdesc Ritorna l'indice del primo elemento trovato o size() se l'array e' vuoto -{ - return succ(-1); -} - -// @mfunc Cerca l'ultimo oggetto dell'array -int TArray::last() const -// @rdesc Ritorna l'indice dell'ultimo elemento trovato o -1 se l'array e' vuoto -{ - return pred(size()); -} - -// @mfunc Rende contigui tutti gli elementi non nulli -void TArray::pack() - - // @comm Riordina gli elementi dell'array facendo che tra di loro non ve ne - // siano con valore uguale a NULL. -{ - int next = 0; - for (int i = 0; i < size(); i++) - { - if (_data[i] != NULL) - { - if (next < i) - { - _data[next] = _data[i]; - _data[i] = NULL; - } - next++; - } - } - _next = next; -} - -// @func Funzione per permettere il confonto tra 2 oggetti. -// -// @rdesc Ritorna i seguenti valori -// -// @flag 0 | se

-// @flag 0 | se

==

-// @flag 0 | se

-// @flag UNDEFINED | se l'ordine non e' definito -static int sortable_compare( - const TObject** o1, // @parm Primo oggetto da confrontare - const TObject** o2) // @parm Secondo oggetto da confrontare - - // @comm E' utilizzata dalla funzione come default per - // stabilire il criteri di ordinamento degli oggetti passati. -{ - const TSortable* s1 = (const TSortable*)*o1; - const TSortable* s2 = (const TSortable*)*o2; - return s1->compare(*s2); -} - -// @mfunc Ordina i TObject secondo il criterio definito in -void TArray::sort( - COMPARE_FUNCTION compare) // @parm Funzione indicante il criterio di ordinamento (default TSortable) - - // @comm Nel caso non venga passata nessuna funzione che permetta di conforntare - // i due oggetti viene utilizzato il criterio -{ - typedef int (*qsortfunc)(const void*, const void*); - if (compare == NULL) compare = sortable_compare; - - pack(); - qsort(_data, items(), sizeof(TObject*), (qsortfunc)compare); -} - -/////////////////////////////////////////////////////////// -// TBit_array -/////////////////////////////////////////////////////////// - -TBit_array::TBit_array(long size) : _bit(NULL), _size(0) -{ - if (size) resize(index(size)); -} - -// @doc INTERNAL -// -// @mfunc Copia nell'array l'elemento passato come parametro -void TBit_array::copy( - const TBit_array& ba) // @parm Oggetto da copiare nell'array - - // @comm Sostituisce all'elemento corrente dell'array l'elemento passato come parametro. -{ - if (_bit) - { - delete _bit; - _bit = NULL; - _size = 0; - } - resize(ba._size-1); - memcpy(_bit, ba._bit, _size); -} - -TBit_array::TBit_array(const TBit_array& ba) : _bit(NULL), _size(0) -{ copy(ba); } - -TBit_array& TBit_array::operator=(const TBit_array& ba) -{ - copy(ba); - return *this; -} - - -TBit_array::~TBit_array() -{ - if (_bit) delete _bit; -} - -// Certified 100% -void TBit_array::set() -{ - if (_bit) memset(_bit, 0xFF, _size); -} - -// Certified 100% -void TBit_array::reset() -{ - if (_bit) memset(_bit, 0x0, _size); -} - -// Certified 99% -// @doc INTERNAL -// -// @mfunc Modifica la dimensione dell'array. -void TBit_array::resize( - word size) // @parm Indica la nuova dimensione che deve assumere l'array. - - // @comm Alloca la memoria necessaria per contentere i dati della nuova - // dimensione inizializzando a NULL tutti i nuovi elementi aggiunti - // all'array lasciando inalterati, sia in posizione che in - // contenuto gli elementi gia' presenti. -{ - word oldsize = _size; - byte* oldbit = _bit; - - _size = size+1; - _bit = new byte[_size]; - reset(); - - if (oldsize) - { - memcpy(_bit, oldbit, oldsize); - delete oldbit; - } -} - - -bool TBit_array::operator[] (long n) const -{ - const word i = index(n); - if (i >= _size) return FALSE; - return (_bit[i] & mask(n)) != 0; -} - -// Certified 99% -TBit_array& TBit_array::operator|= (const TBit_array& ba) -{ - CHECK(_size >= ba._size, "TBit_array |=: right operand too big"); - - for (word i = 0; i < _size; i++) - _bit[i] |= ba._bit[i]; - - return *this; -} - -// Certified 99% -void TBit_array::set(long n) -{ - const word i = index(n); - if (i >= _size) resize(i); - _bit[i] |= mask(n); -} - -// Certified 99% -void TBit_array::reset(long n) -{ - const word i = index(n); - if (i < _size) - _bit[i] &= ~mask(n); -} - -// Certified 99% -void TBit_array::not(long n) -{ - const word i = index(n); - if (i >= _size) resize(i); - _bit[i] ^= mask(n); -} - -// Certified 90% -// @doc EXTERNAL -// -// @mfunc Ritorna il numero di 1 presenti nell'array -long TBit_array::ones() const - -// @rdesc Numero di 1 trovati. -// -// @comm Passa tutto l'array di bit e conta quanti degli elementi presenti sono -// settati ad 1. -// -// @xref -{ - long one = 0; - for (word i = 0; i < _size; i++) - { - const byte b = _bit[i]; - if (b) - { - for (byte m = 1; m; m <<= 1) - if (b & m) one++; - } - } - return one; -} - -// Certified 90% -// @doc EXTERNAL -// -// @mfunc Ritorna la posizione dell'ultimo 1 nell'array -long TBit_array::last_one() const - -// @rdesc Ritorna i seguenti valori: -// -// @flag >0 | Posizione nella quale si trova l'ultimo 1 -// @flag -1 | Se non sono presenti bit settati ad 1 -// -// @comm Cerca all'interno dell'array di bit la posizione dell'ultimo bit -// settato ad 1 -// -// @xref -{ - for (word i = _size; i--;) - { - const byte b = _bit[i]; - if (b) - { - for (byte j = 8; j--;) - if ((1<0 | Posizione nella quale si trova il primo 1 -// @flag -1 | Se non sono presenti bit settati ad 1 -// -// @comm Cerca all'interno dell'array di bit la posizione del primo bit -// settato ad 1 -// -// @xref -{ - for (word i = 0; i < _size; i++) - { - const byte b = _bit[i]; - if (b) - { - for (byte j = 0; j < 8; j++) - if ((1< 0; -} - - -void TBit_array::set(const char* numbers) -{ - TToken_string s(numbers, ' '); - for (const char* n = s.get(0); n; n = s.get()) - if (isdigit(*n)) set(atol(n)); -} - -// @doc INTERNAL -// -// @mfunc Stampa un array -void TBit_array::print_on( - ostream& out) const // @parm Indica l'output sul quale reindirizzare la stampa (funzione standard del C++) - - // @comm Stampa tutti gli indici degli elementi diversi da 0 -{ - const long last = _size<<3; - for (long i = 1; i < last; i++) - if (operator[](i)) out << ' ' << i; -} - - +#include +#include + +#include +#include + +// @doc INTERNAL + +// @mfunc Modifica la dimensione dell'array. +void TArray::resize( + int arraysize) // @parm Indica la nuova dimensione che deve assumere l'array. + + // @comm Alloca la memoria necessaria per contentere i dati della nuova + // dimensione inizializzando a NULL tutti i nuovi elementi aggiunti + // all'array lasciando inalterati, sia in posizione che in + // contenuto gli elementi gia' presenti. + // Nel caso si cerchi di diminuire la dimensione viene dato un + // messaggio di errore. + +{ + CHECK(arraysize > size(), "Can't reduce array size."); + + TObject** newdata = new TObject* [arraysize]; + + int i = 0; + if (_data != NULL) + for (i = 0; i < size(); i++) newdata[i] = _data[i]; + + while (i < arraysize) newdata[i++] = NULL; + + if (_data != NULL) delete _data; + + _size = arraysize; + _data = newdata; +} + +// @doc EXTERNAL +// +// @mfunc Rimuove uno o tutti (default) gli elementi +bool TArray::destroy( + int index, // @parm Numero dell'elemento da eliminare (default -1) + bool pack) // @parm Se true richiama la funzione per compattare gli elementi dell'array (default FALSE) + + // @comm Permette la rimozione di uno (index > 0 ) o di tutti (default) gli + // elementi dell'array assegnandone il valore NULL. + // Nel caso il parametro pack sia TRUE permette di rendere contigui + // tutti gli elementi diversi da NULL tramite la chiamata alla funzione + // . + // + // @rdesc Ritorna uno dei seguenti parametri: + // + // @flag TRUE | Se l'operazione e' riuscita con successo + // @flag FALSE | Se il numero di elementi e' rimasto invariato +{ + const int old = _items; + + if (index < 0) + { + for (int i = size(); i--;) if (_data[i] != NULL) + { + delete _data[i]; + _data[i] = NULL; + } + _items = _next = 0; + } + else + { + TObject* o = remove(index, pack); + if (o) delete o; + } + + return _items < old; +} + + +TArray::TArray(int arraysize) : _size(0), _items(0), _next(0), _data(NULL) +{ + if (arraysize) resize(arraysize); +} + +TArray::TArray() : _size(0), _items(0), _next(0), _data(NULL) +{ +} + +TArray::TArray(const TArray& a) : _size(0), _items(0), _next(0), _data(NULL) +{ + (*this) = a; +} + +TArray& TArray::operator= (const TArray& a) +{ + destroy(); + if (size() < a.size()) + resize(a.size()); + for (int i = a.size()-1; i >= 0; i--) + { + const TObject* o = a.objptr(i); + if (o != NULL) _data[i] = o->dup(); + } + _items = a.items(); + _next = a._next; + + return *this; +} + +TArray::~TArray() + +{ + if (ok()) + { + destroy(); + delete _data; + } +} + + +const char* TArray::class_name() const +{ + return "Array"; +} + + +word TArray::class_id() const +{ + return CLASS_ARRAY; +} + +// @mfunc Stampa un array +void TArray::print_on( + ostream& out) const // @parm indica l'output sul quale reindirizzare la stampa (funzione standard del C++) + + // @comm Permette di stampare sull'output passato come parametro il contenuto + // dell'array, preceduto dal numero dell'elemetno a cui si riferisce. + // Nel caso vi siano degli elementi vuoti (valore = NULL) viene + // stampato il valore (null). + +{ + for (int i = 0; i < size(); i++) + { + out.width(4); + out << i << ' '; + out.width(); + if (_data[i] != NULL) out << *_data[i]; + else out << "(null)"; + out << endl; + } +} + +// @mfunc Controlla se si tratta di un oggetto valido +bool TArray::ok() const + +// @rdesc Ritorna uno dei seguenti parametri: +// +// @flag TRUE | Se l'array contiene degli elementi +// @flag FALSE | Se l'array e' vuoto +// +// @comm Controlla se la dimensione dell'array e' uguale a 0 e se esistono +// degli elementi diversi da NULL + +{ + return(size() != 0 && (_data != NULL)); +} + +// @mfunc Aggiunge un oggetto ad un array. +int TArray::add( + TObject *object, // @parm Oggetto da aggiungere nell'array + int index) // @parm Posizione in cui aggiungere l'oggetto + // @parm TObject | &object | Oggetto da aggiungere nell'array. L'oggetto viene duplicato + + // @syntax add(TObject* object,int index); + // @syntax add(TObject& object,int index); + // + // @comm Nel caso venga passata una posizione minore di 0 l'elemento viene aggiunto + // in coda all'array, diversamente viene l'elemento presente viene + // sostitito con l'oggetto passato nei prametri. + // Se e' stato passato un indice minore della dimensione dell'array, + // viene aumentata automaticamente la dimensione dell'array stesso tramite + // la chiamata alla funzione . + // ATTENZIONE: Nel caso si passi l'oggetto per indirizzo deve + // essere definita la funzione . + // + // @rdesc La funzione ritorna la posizione nella quale e' stato aggiunto + // l'oggetto. + // + // @xref +{ + if (index < 0) for (index = _next; index < size() && _data[index]; index++); + + if (index >= size()) resize(int(3L*index/2) + 1); + + if (_data[index] != NULL) + { +#ifdef DBG + if (object == _data[index]) + { + error_box("Iu ar traing tu overrait en arrei obgiect: %d", index); + return index; + } +#endif + delete _data[index]; + _items--; + } + + _data[index] = object; + if (object != NULL) + { + _items++; + _next = index+1; + } + else + { + if (index < _next) + _next = index; + } + return index; +} + +// @mfunc Inserisce un elemento dell'array nella posizione index +int TArray::insert( + TObject *object, // @parm Oggetto da inserire nell'array + int index) // @parm Posizione in cui inserire l'oggetto + // @parm TObject | &object | Oggetto da inserire nell'array. L'oggetto viene duplicato + + // @syntax insert(TObject* object, int index); + // @syntax insert(TObject& object, int index); + // + // @comm Nel caso l'elemento della posizione passata sia diverso da NULL vengono + // spostati di una posizione tutti gli elementi presenti nell'array. + // La dimensione dell'array viene automaticamente aumentata nel caso + // la stessa non sia sufficiente a contenere il nuovo oggetto. + // ATTENZIONE: Nel caso si passi l'oggetto per indirizzo deve + // essere definita la funzione . + // + // @rdesc La funzione ritorna la posizione nella quale e' stato inserito l'oggetto. + // + // @xref +{ + if (objptr(index)) + { + if (items() == size()) add(NULL); + for (int i = size()-1; i >= index; i--) + _data[i] = _data[i-1]; + _data[index] = NULL; + } + return add(object, index); +} + + +int TArray::add(const TObject& object, int index) +{ + TObject* objptr = object.dup(); + return add(objptr, index); +} + +int TArray::insert(const TObject& object, int index) +{ + TObject* objptr = object.dup(); + return insert(objptr, index); +} + +// @mfunc Elimina l'elemento nella posizione index dell'array +TObject* TArray::remove( + int index, // @parm Indica la posizione dell'elemento da eliminare + bool dopack) // @parm Indica se si vuole richiamare la funzione + // (default FALSE) per il compattamento dell'array + + // @rdesc Ritorna l'elemento dell'array eliminato senza farne la delete +{ + TObject* o = objptr(index); + if (o) + { + _data[index] = NULL; + _items--; + if (index < _next) + _next = index; + } + if (dopack) pack(); + return o; +} + +void TArray::swap(int i1, int i2) +{ + TObject* o1 = remove(i1, FALSE); + TObject* o2 = remove(i2, FALSE); + if (o1) add(o1, i2); + if (o2) add(o2, i1); +} + +// @mfunc Cerca il prossimo oggetto dell'array +int TArray::succ(int i) const +// @rdesc Ritorna l'indice del prossimo elemento trovato o size() se non esiste +{ + CHECKD(i >= -1, "Bad array index ", i); + for (i++; i < size(); i++) + if (_data[i]) break; + return i; +} + +// @mfunc Cerca il precedente oggetto dell'array +int TArray::pred(int i) const +// @rdesc Ritorna l'indice dell'elemento precedente o -1 se non esiste +{ + CHECKD(i <= size(), "Bad array index ", i); + for (i--; i >= 0; i--) + if (_data[i]) break; + return i; +} + +// @mfunc Cerca il primo oggetto dell'array +int TArray::first() const +// @rdesc Ritorna l'indice del primo elemento trovato o size() se l'array e' vuoto +{ + return succ(-1); +} + +// @mfunc Cerca l'ultimo oggetto dell'array +int TArray::last() const +// @rdesc Ritorna l'indice dell'ultimo elemento trovato o -1 se l'array e' vuoto +{ + return pred(size()); +} + +// @mfunc Rende contigui tutti gli elementi non nulli +void TArray::pack() + + // @comm Riordina gli elementi dell'array facendo che tra di loro non ve ne + // siano con valore uguale a NULL. +{ + int next = 0; + for (int i = 0; i < size(); i++) + { + if (_data[i] != NULL) + { + if (next < i) + { + _data[next] = _data[i]; + _data[i] = NULL; + } + next++; + } + } + _next = next; +} + +// @func Funzione per permettere il confonto tra 2 oggetti. +// +// @rdesc Ritorna i seguenti valori +// +// @flag 0 | se

+// @flag 0 | se

==

+// @flag 0 | se

+// @flag UNDEFINED | se l'ordine non e' definito +static int sortable_compare( + const TObject** o1, // @parm Primo oggetto da confrontare + const TObject** o2) // @parm Secondo oggetto da confrontare + + // @comm E' utilizzata dalla funzione come default per + // stabilire il criteri di ordinamento degli oggetti passati. +{ + const TSortable* s1 = (const TSortable*)*o1; + const TSortable* s2 = (const TSortable*)*o2; + return s1->compare(*s2); +} + +// @mfunc Ordina i TObject secondo il criterio definito in +void TArray::sort( + COMPARE_FUNCTION compare) // @parm Funzione indicante il criterio di ordinamento (default TSortable) + + // @comm Nel caso non venga passata nessuna funzione che permetta di conforntare + // i due oggetti viene utilizzato il criterio +{ + typedef int (*qsortfunc)(const void*, const void*); + if (compare == NULL) compare = sortable_compare; + + pack(); + qsort(_data, items(), sizeof(TObject*), (qsortfunc)compare); +} + +/////////////////////////////////////////////////////////// +// TBit_array +/////////////////////////////////////////////////////////// + +TBit_array::TBit_array(long size) : _bit(NULL), _size(0) +{ + if (size) resize(index(size)); +} + +// @doc INTERNAL +// +// @mfunc Copia nell'array l'elemento passato come parametro +void TBit_array::copy( + const TBit_array& ba) // @parm Oggetto da copiare nell'array + + // @comm Sostituisce all'elemento corrente dell'array l'elemento passato come parametro. +{ + if (_bit) + { + delete _bit; + _bit = NULL; + _size = 0; + } + resize(ba._size-1); + memcpy(_bit, ba._bit, _size); +} + +TBit_array::TBit_array(const TBit_array& ba) : _bit(NULL), _size(0) +{ copy(ba); } + +TBit_array& TBit_array::operator=(const TBit_array& ba) +{ + copy(ba); + return *this; +} + + +TBit_array::~TBit_array() +{ + if (_bit) delete _bit; +} + +// Certified 100% +void TBit_array::set() +{ + if (_bit) memset(_bit, 0xFF, _size); +} + +// Certified 100% +void TBit_array::reset() +{ + if (_bit) memset(_bit, 0x0, _size); +} + +// Certified 99% +// @doc INTERNAL +// +// @mfunc Modifica la dimensione dell'array. +void TBit_array::resize( + word size) // @parm Indica la nuova dimensione che deve assumere l'array. + + // @comm Alloca la memoria necessaria per contentere i dati della nuova + // dimensione inizializzando a NULL tutti i nuovi elementi aggiunti + // all'array lasciando inalterati, sia in posizione che in + // contenuto gli elementi gia' presenti. +{ + word oldsize = _size; + byte* oldbit = _bit; + + _size = size+1; + _bit = new byte[_size]; + reset(); + + if (oldsize) + { + memcpy(_bit, oldbit, oldsize); + delete oldbit; + } +} + + +bool TBit_array::operator[] (long n) const +{ + const word i = index(n); + if (i >= _size) return FALSE; + return (_bit[i] & mask(n)) != 0; +} + +// Certified 99% +TBit_array& TBit_array::operator|= (const TBit_array& ba) +{ + CHECK(_size >= ba._size, "TBit_array |=: right operand too big"); + + for (word i = 0; i < _size; i++) + _bit[i] |= ba._bit[i]; + + return *this; +} + +// Certified 99% +void TBit_array::set(long n) +{ + const word i = index(n); + if (i >= _size) resize(i); + _bit[i] |= mask(n); +} + +// Certified 99% +void TBit_array::reset(long n) +{ + const word i = index(n); + if (i < _size) + _bit[i] &= ~mask(n); +} + +// Certified 99% +void TBit_array::not(long n) +{ + const word i = index(n); + if (i >= _size) resize(i); + _bit[i] ^= mask(n); +} + +// Certified 90% +// @doc EXTERNAL +// +// @mfunc Ritorna il numero di 1 presenti nell'array +long TBit_array::ones() const + +// @rdesc Numero di 1 trovati. +// +// @comm Passa tutto l'array di bit e conta quanti degli elementi presenti sono +// settati ad 1. +// +// @xref +{ + long one = 0; + for (word i = 0; i < _size; i++) + { + const byte b = _bit[i]; + if (b) + { + for (byte m = 1; m; m <<= 1) + if (b & m) one++; + } + } + return one; +} + +// Certified 90% +// @doc EXTERNAL +// +// @mfunc Ritorna la posizione dell'ultimo 1 nell'array +long TBit_array::last_one() const + +// @rdesc Ritorna i seguenti valori: +// +// @flag >0 | Posizione nella quale si trova l'ultimo 1 +// @flag -1 | Se non sono presenti bit settati ad 1 +// +// @comm Cerca all'interno dell'array di bit la posizione dell'ultimo bit +// settato ad 1 +// +// @xref +{ + for (word i = _size; i--;) + { + const byte b = _bit[i]; + if (b) + { + for (byte j = 8; j--;) + if ((1<0 | Posizione nella quale si trova il primo 1 +// @flag -1 | Se non sono presenti bit settati ad 1 +// +// @comm Cerca all'interno dell'array di bit la posizione del primo bit +// settato ad 1 +// +// @xref +{ + for (word i = 0; i < _size; i++) + { + const byte b = _bit[i]; + if (b) + { + for (byte j = 0; j < 8; j++) + if ((1< 0; +} + + +void TBit_array::set(const char* numbers) +{ + TToken_string s(numbers, ' '); + for (const char* n = s.get(0); n; n = s.get()) + if (isdigit(*n)) set(atol(n)); +} + +// @doc INTERNAL +// +// @mfunc Stampa un array +void TBit_array::print_on( + ostream& out) const // @parm Indica l'output sul quale reindirizzare la stampa (funzione standard del C++) + + // @comm Stampa tutti gli indici degli elementi diversi da 0 +{ + const long last = _size<<3; + for (long i = 1; i < last; i++) + if (operator[](i)) out << ' ' << i; +} + + diff --git a/include/msksheet.cpp b/include/msksheet.cpp index ac214796d..e4013b7d7 100755 --- a/include/msksheet.cpp +++ b/include/msksheet.cpp @@ -1,1672 +1,1672 @@ -#include -#include -#include - -const short FIRST_FIELD = 101; - -#if XVT_OS == XVT_OS_WIN - -#include -#include - -/////////////////////////////////////////////////////////// -// TSpreadsheet -/////////////////////////////////////////////////////////// - -class TSpreadsheet : public TWindow -{ - friend class TSheet_field; - - enum { ITF_CID = 0, LIST_CID = 1 }; - - TString_array _str; // Array di TToken_strings - TBit_array _column_disabled; - TArray _disabled; // Array di TBit_array - - TMask _mask; - int _columns; - bool _firstfocus, _active; - - XI_OBJ *_list, *_itf; - - SPREADSHEET_NOTIFY _notify; - // Matteo - SPREADSHEET_GETMASK _getmask; - - TSheet_field* _owner; // Owner - - TMask_field* _edit_field; // Current edit field - int _cur_row, _cur_rec, _cur_col; // Current cell - bool _row_dirty; // Current row changed - bool _check_enabled; // Perform OFF_ROW and OFF_CELL checks - int _needs_update; - - void init(); - friend void XVT_CALLCONV1 xiev_handler(XI_OBJ *itf, XI_EVENT *xiev); - -protected: - void list_handler(XI_EVENT *xiev); - - TMask_field* col2field(int pos) const; - TMask_field* cell2field(const XI_OBJ* cell) const; - void update_rec(int rec); - TMask_field* field(short id) const; - - int rec2row(int rec); - int row2rec(int& row); - int set_pos(int row, int col) { _cur_col = col; _cur_row = row; return _cur_rec = row2rec(_cur_row); } - - bool notify(int row, KEY k); - void notify_change(); - void on_idle(); - -public: - void update(int row); - - TToken_string& row(int n) { return _str.row(n); } - int add(const TToken_string& s) { return _str.add(s); } - int add(TToken_string* s) { return _str.add(s); } - int insert(int rec); - bool destroy(int rec = -1); - TString_array& rows_array() { return _str; } - - void set_focus_cell(int riga, int colonna); - void activate(bool on); - void enable_column(int col, bool on = TRUE); - // Matteo - void delete_column( const int col ) const; - void move_column( const int fromindex, const int toindex ) const; - void swap_columns(const int fromid, const int toid) const; - void swap_rows( const int fromindex, const int toindex); - - void set_column_width( const int col, const int width ) const; - void set_column_header( const int col, const TString& header ) const; - - - void enable_cell(int row, int column, bool on = TRUE); - bool cell_disabled(int row, int column) const; - - TMask& sheet_mask() { return _mask; } - TMask& mask() const; - - void mask2str(int n); - void str2mask(int n); - KEY edit(int n); - - int items() const { return _str.items(); } - int selected() const { return _cur_rec; } - void select(int r); - int columns() const { return _columns; } - - bool dirty() const { return _owner->dirty(); } - void set_dirty(bool spork = TRUE) { _owner->set_dirty(spork); } - - bool active() const { return _active; } - bool test_focus_change(); - - // Matteo - void set_getmask(SPREADSHEET_GETMASK n) { _getmask = n; } - - void set_notify(SPREADSHEET_NOTIFY n) { _notify = n; } - - TSpreadsheet(short x, short y, short dx, short dy, const char* maskname, int maskno, - const char* head, WINDOW parent, TSheet_field* owner); - virtual ~TSpreadsheet(); -}; - -// Certified 100% -void TSpreadsheet::init() -{ - static bool first = TRUE; - if (!first) return; - - XVT_FNTID font = xvt_dwin_get_font(TASK_WIN); - xi_set_font_id(font); - - xi_init(); - xi_set_pref(XI_PREF_3D_LOOK, TRUE); - first = FALSE; -} - - -TSpreadsheet::TSpreadsheet(short x, short y, short dx, short dy, - const char* maskname, int maskno, - const char* head, WINDOW parent, - TSheet_field* o) -: _mask(maskname, maskno), _notify(NULL), _edit_field(NULL), _getmask( NULL ), - _owner(o), _cur_row(0), _cur_col(0), _active(TRUE), - _row_dirty(FALSE), _check_enabled(TRUE), _firstfocus(TRUE), - _needs_update(-1) -{ - const int NUMBER_WIDTH = 3; - const int MAX_COL = 32; - int m_width[MAX_COL], v_width[MAX_COL]; - int fixed_columns = 1; // Number of fixed columns - - init(); - - _mask.set_sheet(o); - - // Calcolo larghezza massima tabella - - TToken_string header(head); - TToken_string new_header(256); - int i = 0, tot_width = NUMBER_WIDTH+1; - int f_width = tot_width<<1; // Stima larghezza colonne fisse - int max_width = f_width; // Stima larghezza della colonna piu' grande - - for (const char* h = header.get(); h; h = header.get(), i++) - { - CHECKD(i < MAX_COL, "Tu meni calumns in scit: ", i); - - const int cid = FIRST_FIELD+i; // Column & Field ID - const TMask_field* f = field(cid); // Field on mask - CHECKD(f, "The spreadsheet mask needs ALSO field ", cid); - - TString80 testa(h); - const int at = testa.find('@'); - int m, v; // Memory and video width - if (at >= 0) - { - const TString& wi = testa.mid(at+1); - m = atoi(wi); - if (wi.find('F') >= 0) - { - fixed_columns++; - f_width += m+1; - } - testa.cut(at); - v = max(at, m+(f->has_query() ? 1 : 0)); - } - else - { - m = testa.len(); - v = m+(f->has_query() ? 1 : 0); - } - - if (f->is_edit() && m < f->size()) - m = f->size(); - if (v > 69) - v = 69; - - m_width[i] = m+1; // m = number of allowed chars - v_width[i] = v+1; // v = width of column - if (v >= max_width) max_width = v+1; - - tot_width += v_width[i]; - - new_header.add(testa); - } - _columns = i; - - if (x < 0) x = 0; - if (y < 0) y = 0; - if (dx == 0) - { - dx = tot_width; - if (dx > 76) dx = -x; - } - - RCT rct = resize_rect(x, y, dx, dy, WO_TE, parent); - rct.bottom -= 12; - rct.right -= 28; - - if ((f_width+max_width)*CHARX > rct.right) - fixed_columns = 1; - - XI_OBJ_DEF* itfdef = xi_create_itf_def(ITF_CID, - (XI_EVENT_HANDLER)xiev_handler, &rct, (char*)maskname, - PTR_LONG(this)); - itfdef->v.itf->automatic_back_color = FALSE; - itfdef->v.itf->back_color = MASK_BACK_COLOR; - - XI_OBJ_DEF* listdef = xi_add_list_def(itfdef, LIST_CID, - 0, 0, rct.bottom-rct.top, - XI_ATR_ENABLED | XI_ATR_VISIBLE, - NORMAL_COLOR, NORMAL_BACK_COLOR, // normal - NORMAL_COLOR, DISABLED_BACK_COLOR, // disabled - COLOR_RED, // active - LIST_CID); - listdef->v.list->sizable_columns = TRUE; - listdef->v.list->movable_columns = TRUE; - listdef->v.list->scroll_bar = TRUE; - listdef->v.list->scroll_bar_button = TRUE; - listdef->v.list->fixed_columns = fixed_columns; - listdef->v.list->width = rct.right-rct.left; - listdef->v.list->min_cell_height = CHARY; - listdef->v.list->min_heading_height = CHARY; - listdef->v.list->white_space_color = MASK_DARK_COLOR; - - word attr = XI_ATR_RJUST; - if (_mask.id2pos(FIRST_FIELD -1) != -1) - attr |= XI_ATR_SELECTABLE; - - XI_OBJ_DEF* coldef = xi_add_column_def(listdef, 0, attr, 0, NUMBER_WIDTH, NUMBER_WIDTH, - attr & XI_ATR_SELECTABLE ? "X" : ""); - - coldef->v.column->heading_platform = TRUE; - coldef->v.column->column_platform = TRUE; - - if (attr & XI_ATR_SELECTABLE) - { - coldef->v.column->icon_rid = ICO_SEARCH; - coldef->v.column->icon_x = 2; - listdef->v.list->min_heading_height = 16; - } - else - coldef->v.column->center_heading = TRUE; - - - for (h = new_header.get(0), i = 0; h; h = new_header.get(), i++) - { - const TString80 testo(h); - const int cid = FIRST_FIELD+i; // Column & Field ID - const TMask_field* f = field(cid); // Field on mask - const int acqua = f->class_id(); - - long flags = XI_ATR_EDITMENU | XI_ATR_AUTOSCROLL; - switch (acqua) - { - case CLASS_EDIT_FIELD: - if (f->right_justified()) - flags |= XI_ATR_RJUST; - break; - case CLASS_REAL_FIELD: - flags |= XI_ATR_RJUST; - break; - case CLASS_BUTTON_FIELD: - flags |= XI_ATR_SELECTABLE; - break; - default: - break; - } - if (f->active()) flags |= XI_ATR_ENABLED | XI_ATR_FOCUSBORDER | XI_ATR_AUTOSELECT; - else _column_disabled.set(i); - - coldef = xi_add_column_def(listdef, cid, flags, cid, v_width[i], m_width[i], - (char*)(const char*)testo); - coldef->v.column->heading_platform = TRUE; - coldef->v.column->center_heading = TRUE; - } - - RCT itfrct; - xi_get_def_rect(itfdef, &itfrct); - xvt_rect_offset(&itfrct, rct.left, rct.top); - itfrct.bottom++; - - WINDOW win = xvt_win_create(W_NO_BORDER, &itfrct, "", 0, parent, - 0, EM_ALL, (EVENT_HANDLER)xi_event, 0L); - CHECK(win, "Can't create a window for the spreadsheet"); - - set_win(win); // Set TWindow::_win - itfdef->v.itf->win = win; // Link interface to win - - xi_create(NULL, itfdef); // Create the whole thing! - xi_tree_free(itfdef); // Free definitions - - _itf = xi_get_itf(win); // Store useful references for later use - _list = xi_get_obj(_itf, LIST_CID); -} - -TSpreadsheet::~TSpreadsheet() -{ - set_win(NULL_WIN); -} - - -// Converts a row number in the correspondig record number -int TSpreadsheet::row2rec(int& row) -{ - int rows; - const long* rec = xi_get_list_info(_list, &rows); - - int r = -1; - if (row < 0) - { - row = 0; - r = (int)rec[row]-1; - } - else - if (row >= rows) - { - row = rows-1; - r = (int)rec[row]+1; - } - else - r = (int)rec[row]; - - CHECKD(r >= 0 && r <= items(), "Sheet line out of range: ", row); - - return r; -} - - -// Converts a row number in the correspondig record number -int TSpreadsheet::rec2row(int record) -{ - int rows; - const long* rec = xi_get_list_info(_list, &rows); - int r = int(record - rec[0]); - if (r < 0 || r >= rows) - r = -1; - - return r; -} - - -// Retrieves the corresponding field of the mask from a spredsheet cell -TMask_field* TSpreadsheet::col2field(int pos) const -{ - int num; - XI_OBJ** column = xi_get_member_list(_list, &num); - CHECKD(pos >= 0 && pos < num, "Bad column number", pos); - - TMask_field* good = NULL; - for (short id = column[pos]->cid; ; id += 100) - { - TMask_field* f = field(id); - if (f == NULL) break; // Search failed - good = f; // We've found a field with the proper ID ... - if (f->active()) break; // ... and it's active: end of search - } - - CHECKD(good, "Can't find field corresponding to column ", pos); - return good; -} - - -// Retrieves the corresponding field of the mask from a spredsheet cell -TMask_field* TSpreadsheet::cell2field(const XI_OBJ* cell) const -{ - return col2field(cell->v.cell.column); -} - -void TSpreadsheet::update_rec(int rec) -{ - const int riga = rec2row(rec); - if (riga >= 0) - { - XI_OBJ row; - XI_MAKE_ROW(&row, _list, riga); - xi_cell_request(&row); // Update internal values - } -} - - -void TSpreadsheet::set_focus_cell(int riga, int colonna) -{ - xvt_scr_set_focus_vobj(win()); - const int r = row2rec(riga); - - for (; colonna < _columns; colonna++) - if (!cell_disabled(r, colonna-1)) - break; - - if (colonna <= _columns) - { - XI_OBJ cell; - XI_MAKE_CELL(&cell, _list, riga, colonna); - xi_set_focus(&cell); - - if (_edit_field == NULL) - { - _cur_row = riga; - _cur_col = colonna; - _cur_rec = r; - _edit_field = col2field(colonna); - str2mask(_cur_rec); - _row_dirty = FALSE; - } - } -} - - -int TSpreadsheet::insert(int rec) -{ - if (items() >= 999 || !_active) - return -1; - if (rec < 0 && items() > 0 && !_owner->append() ) - rec = _cur_rec + 1; - - const int r = _str.insert(new TToken_string(80), rec); - - const bool ok = notify(r, K_INS); - - if (!ok) - { - _str.destroy(r); - return -1; - } - - _disabled.insert(NULL, r); - xi_insert_row(_list, INT_MAX); - xi_cell_request(_list); - - return r; -} - - -bool TSpreadsheet::destroy(int rec) -{ - bool ok = TRUE; - - if (rec < 0) - { - _disabled.destroy(); - _str.destroy(); - set_dirty(_row_dirty = FALSE); - } - else - { - _disabled.destroy(rec, TRUE); // Destroy enable info - ok = _str.destroy(rec, TRUE); // Destroy line - enable_cell(_str.items(), -1); // Enable last line - } - - if (ok && mask().is_running()) - update(-1); - - return ok; -} - - -void TSpreadsheet::update(int row) -{ - if (row < 0) - { - xi_cell_request(_list); // Force updatde - xi_scroll(_list, XI_SCROLL_FIRST); - } - else - update_rec(row); -} - - -void TSpreadsheet::notify_change() -{ - if (!_row_dirty) - { - notify(_cur_rec, K_SPACE); - _row_dirty = TRUE; - set_dirty(); - } -} - - -bool TSpreadsheet::test_focus_change() -{ - bool ok = dirty() != 3; - if (ok && _row_dirty) - ok = xi_move_focus(_itf) ? TRUE : FALSE; - return ok; -} - - -HIDDEN void XVT_CALLCONV1 xiev_handler(XI_OBJ *itf, XI_EVENT *xiev) -{ - TSpreadsheet* es = (TSpreadsheet*)xi_get_app_data(itf); - CHECK(es, "NULL Edit sheet in xi event"); - es->list_handler(xiev); -} - -// Certified 75% -void TSpreadsheet::list_handler(XI_EVENT *xiev) -{ - static KEY _lastab = K_TAB; - static bool _cell_dirty; - - switch (xiev->type) - { - case XIE_GET_FIRST: - if (items() > 0L) - { - long n = items() * (long)xiev->v.rec_request.percent / 100L; - if (n < 0L) n = 0L; - xiev->v.rec_request.data_rec = n; - } - else - xiev->refused = TRUE; - break; - case XIE_GET_LAST: - xiev->v.rec_request.data_rec = items()-1; - break; - case XIE_GET_PREV: - case XIE_GET_NEXT: - { - const long n = xiev->v.rec_request.spec_rec + (xiev->type == XIE_GET_NEXT ? +1 : -1) ; - if (n < 0 || n >= items()) - xiev->refused = TRUE; - else - xiev->v.rec_request.data_rec = n; - } - break; - case XIE_CELL_REQUEST: - { - const int rec = (int)xiev->v.cell_request.rec; - const char* src = NULL; - int nm; - XI_OBJ** obj = xi_get_member_list(xiev->v.cell_request.list, &nm); - const int num = xiev->v.cell_request.col_nbr; - const int cid = obj[num]->cid; - - if (cid >= FIRST_FIELD) - { - if (rec < items()) - { - const int col = cid - FIRST_FIELD; - TMask_field* f = field(cid); - const int acqua = f->class_id(); - - src = row(rec).get(col); // Set value for cell - if (src && *src && f->is_edit()) - { - src = f->picture_data(src, FALSE); // Get formatted string - } - if (field(cid)->has_query()) - { - xiev->v.cell_request.button = TRUE; - xiev->v.cell_request.button_on_focus = TRUE; - } - if (cell_disabled(rec, col)) - xiev->v.cell_request.back_color = DISABLED_BACK_COLOR; - } - } - else - { - src = format("%d", rec+1); - } - - char* dst = xiev->v.cell_request.s; - if (src && *src) - { - const int& len = xiev->v.cell_request.len; - strncpy(dst, src, len); - } - else - *dst = '\0'; - } - break; - case XIE_CHG_CELL: - if (_edit_field && !_cell_dirty) - { - notify_change(); - _cell_dirty = TRUE; - _edit_field->set_focusdirty(); - } - break; - case XIE_BUTTON: - if (xiev->v.xi_obj->type == XIT_CELL) - dispatch_e_char(win(), K_F9); - else - if (xiev->v.xi_obj->type == XIT_LIST) - insert(-1); - break; - case XIE_SELECT: - if (xiev->v.xi_obj->type == XIT_ROW) - { - _check_enabled = FALSE; - - const int oldrec = _cur_rec; - set_pos(xiev->v.select.xi_obj->v.row, xiev->v.select.column); - if (oldrec != _cur_rec) - _row_dirty = FALSE; - - str2mask(_cur_rec); - update(_cur_rec); // Forces update delayed by str2mask - - if (_mask.id2pos(FIRST_FIELD-1) != -1) - { - TMask_field* button = &_mask.field(FIRST_FIELD-1); - button->on_hit(); - if (_mask.dirty()) - { - notify_change(); - mask2str(_cur_rec); - } - } - _check_enabled = TRUE; - } - xiev->refused = TRUE; - break; - case XIE_DBL_CELL: - { - _check_enabled = FALSE; - - const int oldrec = _cur_rec; - set_pos(xiev->v.xi_obj->v.cell.row, xiev->v.xi_obj->v.cell.column); - - if (oldrec != _cur_rec || !_row_dirty) - { - _row_dirty = FALSE; - notify_change(); - } - const KEY k = edit(_cur_rec); - if (k == K_ENTER) - { - update_rec(_cur_rec); - _row_dirty = TRUE; - } else - if (k == K_DEL) - { - _row_dirty = _cell_dirty = FALSE; - if (_cur_rec >= items()) - _cur_rec = items() - 1; - } - - if (_cur_rec >= 0 && !cell_disabled(_cur_rec, _cur_col-1)) - set_focus_cell(_cur_row, _cur_col); - _check_enabled = TRUE; - } - break; - case XIE_ON_LIST: - if (_firstfocus) // Trick to avoid the sheet to keep the focus forever ... - { // .. it costed me two day worth of hard work! - xiev->refused = TRUE; - _firstfocus = FALSE; - } - else - mask().set_focus_win(win(), FALSE); - break; - case XIE_OFF_LIST: - break; - case XIE_ON_ROW: - if (_check_enabled) - { - set_pos(xiev->v.xi_obj->v.row, _cur_col); - if (_cur_rec < items() && notify(_cur_rec, K_TAB)) - { - str2mask(_cur_rec); - _row_dirty = FALSE; - } - else - { - _cur_row = _cur_rec = 0; - xiev->refused = TRUE; - } - } - break; - case XIE_OFF_ROW: - if (_row_dirty && _check_enabled) - { - _check_enabled = FALSE; // Avoid recursion! - bool ok = sheet_mask().check_fields(); - if (ok) - { - mask2str(_cur_rec); // Update sheet with mask contents - ok = notify(_cur_rec, K_ENTER); // Notify edit - } - if (ok) - { - xvt_statbar_refresh(); - _edit_field = NULL; // Reset current field - } - else - xiev->refused = TRUE; - - _check_enabled = TRUE; - } - break; - case XIE_ON_CELL: - if (_check_enabled) - { - TMask_field* f = cell2field(xiev->v.xi_obj); - const int col = (f->dlg()-FIRST_FIELD) % 100; - if (cell_disabled(_cur_rec, col)) // If the cell is disabled ... - { - dispatch_e_char(win(), _lastab); // ... skip to the next one. - _edit_field = NULL; - } - else - { - _edit_field = f; - _cur_col = xiev->v.xi_obj->v.cell.column; - _edit_field->set_focusdirty(_cell_dirty = FALSE); - } - } - break; - case XIE_OFF_CELL: - if (_edit_field && _check_enabled) - { - _check_enabled = FALSE; - if (_cell_dirty) - { - TMask_field* c = _edit_field; // Save field, it could turn out to be NULL on error - const char* nuo = c->picture_data(xi_get_text(xiev->v.xi_obj, NULL, -1), TRUE); - c->set(nuo); // Set new mask value - c->set_dirty(); // Get it dirty! - if (c->on_key(c->is_edit() ? K_TAB : K_SPACE) == FALSE) // Test it - xiev->refused = *nuo != '\0'; - - if (!xiev->refused) - mask2str(_cur_rec); // Update sheet row - } - _check_enabled = TRUE; - } - break; - case XIE_GET_PERCENT: - { - const long rec = xiev->v.get_percent.record; - long n = items(); if (n < 1) n = 1; - xiev->v.get_percent.percent = int(rec * 100L / n); - } - break; - case XIE_CLEANUP: - break; - case XIE_XVT_EVENT: - { - EVENT* ep = &xiev->v.xvte; - switch (ep->type) - { - case E_FOCUS: - if (_check_enabled && ep->v.active == FALSE) - { - const bool ok = (bool)xi_move_focus(_itf); - if (!ok) - { - set_dirty(3); - xiev->refused = TRUE; - } - } - break; - case E_CHAR: - if (_edit_field) - { - const KEY k = e_char_to_key(ep); - switch(k) - { - case K_TAB: - case K_BTAB: - _lastab = k; - break; - case K_UP: - case K_DOWN: - _lastab = _cur_col < 2 ? K_TAB : K_BTAB; - break; - case K_F1: - case K_SHIFT+K_F1: - _check_enabled = FALSE; // Disable checks - _edit_field->on_key(K_F1); - set_focus_cell(_cur_row, _cur_col); - _check_enabled = TRUE; // Enable checks - break; - case K_F2: - case K_F3: - case K_F8: - case K_F9: - case K_F11: - if (_check_enabled && active()) - { - _check_enabled = FALSE; // Disable checks - notify_change(); - const bool ok = _edit_field->on_key(k); - if (!ok && k == K_F9) // Ricerca non completata? - { - _edit_field = &_mask.fld(_mask.focus_field()); - const short foca = _edit_field->dlg(); - const int col = (foca - FIRST_FIELD) % 100 +1; - if (col > 0 && col != _cur_col) // Ricerca alternativa - { - _cur_col = col; - dispatch_e_char(win(), K_F9); - } - } - if (ok) - { - mask2str(_cur_rec); - update(_cur_rec); _needs_update = -1; // Update immediately! - set_focus_cell(_cur_row, _cur_col); - } - _check_enabled = TRUE; // Re-enable checks - } - break; - case K_PREV: - case K_NEXT: - case K_ESC: - if (xi_move_focus(_itf)) - dispatch_e_char(parent(), k); - break; - case K_ENTER: - case K_SHIFT+K_ENTER: - if (xi_move_focus(_itf)) - dispatch_e_char(parent(), k == K_ENTER ? K_TAB : K_BTAB); - break; - case K_CTRL+K_PREV: - xi_scroll(_list, XI_SCROLL_PGUP); - break; - case K_CTRL+K_NEXT: - xi_scroll(_list, XI_SCROLL_PGDOWN); - break; - case K_CTRL+K_HOME: - xi_scroll(_list, XI_SCROLL_FIRST); - break; - case K_CTRL+K_END: - xi_scroll(_list, XI_SCROLL_LAST); - break; - default: - break; - } - } - break; - default: - break; - } - } - break; - default: - break; - } -} - -void TSpreadsheet::activate(bool on) -{ - _active = on; - - const dword old = xi_get_attrib(_list); - dword att = on ? (old & ~XI_ATR_NAVIGATE) : (old | XI_ATR_NAVIGATE); - if (old != att) - { - int num; - XI_OBJ** columns = xi_get_member_list(_list, &num); - - xi_move_focus(_itf); // Set focus to interface - if (on) - att |= XI_ATR_TABWRAP; - else - att &= ~XI_ATR_TABWRAP; - - xi_set_attrib(_list, att); - - for (int col = 1; col < num; col++) - { - XI_OBJ* column = columns[col]; - att = xi_get_attrib(column); - if (on) - { - att &= ~XI_ATR_READONLY; - att |= XI_ATR_AUTOSELECT; - } - else - { - att |= XI_ATR_READONLY; - att &= ~XI_ATR_AUTOSELECT; - } - xi_set_attrib(column, att); // Set new attributes - } - } -} - -void TSpreadsheet::select(int r) -{ - xi_scroll_rec(_list, r, NORMAL_COLOR, XI_ATR_ENABLED | XI_ATR_AUTOSELECT, 0); - set_focus_cell(0, 1); -} - -#else - -#include - -/////////////////////////////////////////////////////////// -// TSpreadsheet -/////////////////////////////////////////////////////////// - -class TSpreadsheet : public TArray_sheet -{ - TMask _mask; - SPREADSHEET_NOTIFY _notify; - // Matteo - SPREADSHEET_GETMASK _getmask; - - TSheet_field * _owner; - - TBit_array _column_disabled; - TArray _disabled; // Array di TBit_array - - -protected: - virtual bool on_key(KEY key); - KEY edit(int n, KEY tasto); - bool notify(int r, KEY k); - - TMask_field* field(short id) const; - -public: - TSpreadsheet(short x, short y, short dx, short dy, const char* maskname, int maskno, - const char* head, WINDOW parent, TSheet_field * o); - - TMask& sheet_mask() { return _mask; } - TMask& mask() const; - - // Matteo - void set_getmask(SPREADSHEET_GETMASK n) { _getmask = n; } - - void set_notify(SPREADSHEET_NOTIFY n) { _notify = n; } - void set_dirty(bool spork = TRUE) { _owner->set_dirty(spork);} - bool dirty() const { return _owner->dirty(); } - bool test_focus_change() { return TRUE; } - - void mask2str(int riga); - void str2mask(int riga); - - void enable_column(int col, bool on); - // Matteo - void delete_column( const int col ) const; - void move_column( const int fromindex, const int toindex ) const; - void set_column_width( const int col, const int width ) const; - void set_column_header( const int col, const TString& header ) const; - - void enable_cell(int row, int column, bool on = TRUE); - bool cell_disabled(int row, int column) const; - virtual ~TSpreadsheet() {} -}; - -TSpreadsheet::TSpreadsheet(short x, short y, short dx, short dy, - const char* maskname, int maskno, - const char* head, WINDOW parent, - TSheet_field* o) -: TArray_sheet(x, y, dx, dy, maskname, head, 0, parent), _owner(o), - _mask(maskname, maskno), _notify(NULL), /* Matteo */ _getmask( NULL ) -{ - _mask.set_sheet(o); -} - -bool TSpreadsheet::on_key(KEY k) -{ - switch(k) - { - case K_CTRL_ENTER: - case K_ESC: - mask().send_key(k, 0); - return TRUE; - case K_ENTER: // Selezione riga per editing - if (items() < 1) k = K_INS; // Se vuoto crea riga da editare - case K_INS: - case 'A': // Aggiunge dopo - case 'I': // Aggiunge prima - { - int n = (int)selected(); - if (k != K_ENTER) - { - if (k == K_INS) n = items(); else // Aggiunge alla fine - if (k == 'A') n++; - - if (n < 0) n = 0; else - if (n > items()) n = items(); // Controlla range n - - if (notify(n, K_INS) == FALSE) // Chiede l'ok alla applicazione - return FALSE; - - insert(TToken_string(80), n); // Aggiunge una riga vuota - k = K_INS; // Inserimento in corso - } - - notify(n, K_TAB); // Notifica selezione - notify(n, K_SPACE); // Notifica inizio cambiamento - k = edit(n, k); // Edita riga selezionata o creata - if (k == K_ENTER) - notify(n, K_ENTER); // Notifica avvenuto cambiamento - - xvt_vobj_raise(win()); // Aggiorna sheet a video - open(); - } - break; - case K_TAB: - case K_BTAB: - case K_SHIFT_TAB: - dispatch_e_char(xvt_vobj_get_parent(win()), k); - return TRUE; - default: - break; - } - - return TArray_sheet::on_key(k); -} - -#endif - -/////////////////////////////////////////////////////////// -// Metodi di TSpreadsheet comuni a tutte le piattaforme -/////////////////////////////////////////////////////////// - -TMask& TSpreadsheet::mask() const -{ - TMask* m = (TMask*)xvt_vobj_get_data(parent()); - return *m; -} - - -// Ritorna il campo con l'identificatore dato della maschera dello sheet -TMask_field* TSpreadsheet::field(short id) const -{ - const int pos = _mask.id2pos(id); - return pos < 0 ? NULL : &_mask.fld(pos); -} - - -// Ricopia i campi della maschera nel record dato ed aggiorna il display -void TSpreadsheet::mask2str(int rec) -{ - TToken_string& r = row(rec); - r.cut(0); - for (short id = FIRST_FIELD; ; id++) - { - int pos = sheet_mask().id2pos(id); - if (pos < 0) break; - - for(int dlg = id; pos >= 0; pos = sheet_mask().id2pos(dlg += 100)) - { - const TMask_field& f = sheet_mask().fld(pos); - if (f.shown() || f.ghost()) - { - r.add(f.get()); - break; - } - } -#ifdef DBG - if (pos < 0) - { - yesnofatal_box("Non e' visibile il campo %d per lo sheet", dlg); - r.add(" "); - } -#endif - } -#if XVT_OS == XVT_OS_WIN - if (_needs_update != rec) - { - if (_needs_update >= 0) - { -#ifdef DBG - warning_box("Double update: %d and %d", _needs_update, rec); -#endif - update_rec(_needs_update); - } - _needs_update = rec; - } -#endif -} - -void TSpreadsheet::on_idle() -{ - if (_needs_update >= 0) - { - update_rec(_needs_update); - _needs_update = -1; - } -} - -// Certified 50% -void TSpreadsheet::enable_cell(int row, int column, bool on) -{ - TBit_array* ba = (TBit_array*)_disabled.objptr(row); - if (ba == NULL) - { - if (on) return; // Don't waste time and memory - ba = new TBit_array(_column_disabled); - _disabled.add(ba, row); - } - - if (column >= 0) - ba->set(column, !on); - else - { - if (on) - _disabled.destroy(row, FALSE); // Let's save some memory! - else - { -#if XVT_OS == XVT_OS_WIN - ba->set(_columns); // Force right array size -#else - ba->set(32); // Force array size -#endif - ba->set(); // Set all bits - } - } -} - - -void TSpreadsheet::enable_column(int col, bool on) -{ - const bool change = _column_disabled[col] == on; - _column_disabled.set(col, !on); -#if XVT_OS == XVT_OS_WIN - if (change) - { - int num; - XI_OBJ** columns = xi_get_member_list(_list, &num); - for (int c = 1; c < num; c++) - if (columns[c]->cid == FIRST_FIELD+col) - { - XI_OBJ* column = columns[c]; - dword attr = xi_get_attrib(column); - if (on) attr |= XI_ATR_ENABLED; - else attr &= ~XI_ATR_ENABLED; - - xi_move_focus(_itf); // Set focus to interface - xi_set_attrib(column, attr); // Set new attributes - RCT r; xi_get_rect(column, &r); - xi_set_column_width(column, (r.right-r.left+1) / CHARX); // Force redraw - - break; - } - - } -#endif -} - -// Matteo - -void TSpreadsheet::delete_column( const int col ) const -{ -#if XVT_OS == XVT_OS_WIN - int num; - XI_OBJ** columns = xi_get_member_list(_list, &num); - CHECKD( col+1 < num, "Can't delete column ", col ); - XI_OBJ* column = columns[ col+1]; - - xi_delete( column ); -#endif -} - - -void TSpreadsheet::move_column( const int fromindex, const int toindex) const -{ -#if XVT_OS == XVT_OS_WIN - - int num; - XI_OBJ** columns = xi_get_member_list(_list, &num); - CHECKD(fromindex+1 < num, "Can't move column ", fromindex); - XI_OBJ* column = columns[fromindex+1]; - - xi_move_column( column, toindex ); -#endif -} - -void TSpreadsheet::swap_columns(const int fromid, const int toid) const -{ -#if XVT_OS == XVT_OS_WIN - int num; - XI_OBJ** columns = xi_get_member_list(_list, &num); - - XI_OBJ* from_column = XI_NULL_OBJ; - XI_OBJ* to_column = XI_NULL_OBJ; - int from_pos = 0; - int to_pos = 0; - - for (int c = 1; c < num; c++) - { - XI_OBJ* column = columns[c]; - if (column->cid == fromid) - { - from_column = column; - from_pos = c; - } - if (column->cid == toid) - { - to_column = column; - to_pos = c; - }; - } - - xi_move_column(from_column, to_pos); - if (to_pos < from_pos) from_pos++; - xi_move_column(to_column, from_pos); -#endif -} - -void TSpreadsheet::swap_rows( const int fromindex, const int toindex) -{ - _str.swap(fromindex, toindex); - _disabled.swap(fromindex, toindex); -} - -void TSpreadsheet::set_column_width(const int col, const int width) const -{ -#if XVT_OS == XVT_OS_WIN - int num; - XI_OBJ** columns = xi_get_member_list(_list, &num); - CHECKD(col+1 < num, "Can't set column width ", col); - XI_OBJ* column = columns[col+1]; - - xi_set_column_width(column, width); // Force redraw -#endif -} - -void TSpreadsheet::set_column_header(const int col, const TString& header) const -{ -#if XVT_OS == XVT_OS_WIN - int num; - XI_OBJ** columns = xi_get_member_list(_list, &num); - CHECKD(col+1 < num, "Can't set column width ", col); - XI_OBJ* column = columns[col+1]; - - xi_set_text(column, (char *)(const char *)header ); - RCT r; xi_get_rect(column, &r); - xi_set_column_width(column, (r.right-r.left+1) / CHARX); // Force redraw -#endif -} - - - -// Certified 99% -bool TSpreadsheet::cell_disabled(int row, int column) const -{ - TBit_array* ba = (TBit_array*)_disabled.objptr(row); - if (ba == NULL) return _column_disabled[column]; // Use default - return (*ba)[column]; -} - - -// Certified 75% -void TSpreadsheet::str2mask(int riga) -{ - if (riga == items()) - { - sheet_mask().reset(); - mask2str(riga); - return; - } - - TToken_string& r = row(riga); - TString val(80); - - for (int i = 0; i < sheet_mask().fields(); i++) - { - TMask_field& f = sheet_mask().fld(i); - const short id = f.dlg(); - if (id >= FIRST_FIELD) - { - val = r.get((id % 100)-1); - f.set(val); - const bool on = active() ? !cell_disabled(riga, id-FIRST_FIELD) : FALSE; - f.enable(on); - } - } - - for (i = 0; i < sheet_mask().fields(); i++) - { - TMask_field& f = sheet_mask().fld(i); - const short id = f.dlg(); - if (id >= FIRST_FIELD && - f.class_id() != CLASS_BUTTON_FIELD && - (f.active() || f.ghost())) - { - if (f.has_check()) - f.check(STARTING_CHECK); - f.set_dirty(FALSE); - f.on_hit(); - } - } - - for (i = 0; i < sheet_mask().fields(); i++) - { - TMask_field& f = sheet_mask().fld(i); - const short id = f.dlg(); - if (id > FIRST_FIELD) - { - if (f.dirty() == TRUE) - f.set_dirty(FALSE); - } - } - - sheet_mask().set_caption(format("Riga %d", riga+1)); -} - - -// Certified 100% -bool TSpreadsheet::notify(int rec, KEY k) -{ - const bool ok = _notify ? _notify(*_owner, rec, k) : TRUE; - if (k == K_ENTER) - set_dirty(ok ? TRUE : 3); - return ok; -} - -// Certified 99% -#if XVT_OS != XVT_OS_WIN -KEY TSpreadsheet::edit(int n, KEY tasto) -#else - KEY TSpreadsheet::edit(int n) -#endif -{ - KEY k; - const int olditems = items(); - str2mask(n); - - if( _getmask ) - { - TMask& totalmask = sheet_mask(); - TMask& usermask = *_getmask( n, totalmask, FALSE ); - - // Carico la maschera dell'utente con la maschera totale - for( int i = 0; i < usermask.fields(); i ++ ) - { TMask_field& dest_field = usermask.fld( i ); - if ( dest_field.dlg( ) > 100 ) - { TMask_field& source_field = totalmask.field( dest_field.dlg( ) ); - dest_field.set( source_field.get( ) ); } - }; - k = usermask.run(); - if (k == K_ENTER) - // Carico la maschera totale con la maschera dell'utente - for( i = 0; i < usermask.fields(); i ++ ) - { - TMask_field& source_field = usermask.fld( i ); - if ( source_field.dlg( ) > 100 ) - { TMask_field& dest_field = totalmask.field( source_field.dlg() ); - dest_field.set( source_field.get( ) ); } - }; - _getmask( n, totalmask, TRUE ); - - } - else - k = sheet_mask().run(); - - - if (k == K_ENTER) - { - mask2str(n); - } else - if (k == K_DEL) - { - const bool ok = notify(n, K_DEL); // Notifica intenzione di cancellare - if (ok) - { - destroy(n); - if (n < items()) - str2mask(n); - } - } -#if XVT_OS != XVT_OS_WIN -else - if (k == K_ESC) - { - if (tasto == K_INS) - destroy(n); - } -#endif - - return k; -} - -/////////////////////////////////////////////////////////// -// TSheet_field -/////////////////////////////////////////////////////////// - -// Certified 100% -TSheet_field::TSheet_field(TMask* m) -: TMask_field(m), _sheet(NULL), _append(TRUE) -{} - -// Certified 100% -word TSheet_field::class_id() const -{ - return CLASS_SHEET_FIELD; -} - -// Certified 100% -TSheet_field::~TSheet_field() -{ - CHECK(_sheet, "Can't delete NULL sheet"); - delete _sheet; -} - -// Certified 100% -void TSheet_field::reset() -{ - _sheet->destroy(); - _sheet->sheet_mask().reset(); -} - -// Certified 100% -void TSheet_field::destroy(int r) -{ - _sheet->destroy(r); -} - - -void TSheet_field::parse_head(TScanner& scanner) -{ - _width = scanner.integer(); - _size = scanner.integer(); - if (_size == 0) _size = -1; -} - - -// Certified 100% -bool TSheet_field::parse_item(TScanner& scanner) -{ - if (scanner.key() == "IT") - { - _head.add(scanner.string()); - return TRUE; - } - return TMask_field::parse_item(scanner); -} - -// Certified 100% -void TSheet_field::create(WINDOW parent) -{ - const TMask& m = mask(); - _sheet = new TSpreadsheet(_x, _y, _width, _size, m.source_file(), - m.sheets(), _head, parent, this); - - _win = _sheet->win(); - xvt_vobj_set_visible(_win, shown()); - if (!enabled()) disable(); -} - - -// Certified 100% -TString_array& TSheet_field::rows_array() const -{ - return _sheet->rows_array(); -} - - -// Certified 100% -// Ritorna l'indice della prima riga vuota dello sheet -int TSheet_field::first_empty() const -{ - const int max = _sheet->items(); - for (int n = 0; n < max; n++) - if (_sheet->row(n).empty_items()) - break; - return n; -} - - -TToken_string& TSheet_field::row(int n) -{ - const int max = _sheet->items(); - if (n < 0 || n >= max) - { - if (n < 0) n = first_empty(); - if (n >= max) - { - n = _sheet->add(new TToken_string(80)); - force_update(); - } - } - return _sheet->row(n); -} - - -void TSheet_field::force_update(int r) -{ -#if XVT_OS == XVT_OS_WIN - _sheet->update(r); -#else - _sheet->open(); -#endif -} - -int TSheet_field::items() const -{ - return (int)_sheet->items(); -} - -int TSheet_field::selected() const -{ - return _sheet->selected(); -} - -void TSheet_field::set_notify(SPREADSHEET_NOTIFY n) -{ - _sheet->set_notify(n); -} - -void TSheet_field::set_getmask(SPREADSHEET_GETMASK n) -{ - _sheet->set_getmask( n ); -} - - -// Certified 50% -void TSheet_field::highlight() const -{ - TMask_field::highlight(); -#if XVT_OS == XVT_OS_WIN - int rows; xi_get_list_info(_sheet->_list, &rows); - if (rows > 0) - { - _sheet->_firstfocus = FALSE; - if (_sheet->_edit_field == NULL) - _sheet->set_focus_cell(0, 1); - } -#endif -} - -void TSheet_field::enable(bool on) -{ - _sheet->activate(on); -} - -void TSheet_field::enable_column(int column, bool on) -{ - _sheet->enable_column(column, on); -} - - -void TSheet_field::enable_cell(int row, int column, bool on) -{ - _sheet->enable_cell(row, column, on); -} - -bool TSheet_field::cell_disabled(int row, int column) const -{ - return _sheet->cell_disabled(row, column); -} - -// Matteo -void TSheet_field::delete_column( const int col ) const -{ - _sheet->delete_column( col ); -} - -void TSheet_field::swap_columns(const int fromid, const int toid) const -{ - _sheet->swap_columns(fromid, toid); -} - -void TSheet_field::move_column( const int fromindex, const int toindex ) const -{ - _sheet->move_column(fromindex, toindex); -} - -void TSheet_field::swap_rows( const int fromindex, const int toindex) -{ - _sheet->swap_rows(fromindex, toindex); -} - -void TSheet_field::set_column_width( const int col, const int width ) const -{ - _sheet->set_column_width(col, width); -} - -void TSheet_field::set_column_header( const int col, const TString& header ) const -{ - _sheet->set_column_header(col, header); -} - -TMask& TSheet_field::sheet_mask() const -{ - return _sheet->sheet_mask(); -} - -bool TSheet_field::on_hit() -{ - if (!mask().is_running()) - { - force_update(); - set_dirty(FALSE); - } - return TRUE; -} - -void TSheet_field::select(int r) -{ - _sheet->select(r); -} - - -bool TSheet_field::test_focus_change() -{ - return _sheet->test_focus_change(); -} - -bool TSheet_field::on_key(KEY k) -{ - if (k == K_ENTER) - { - if (!test_focus_change()) - return FALSE; - } - return TMask_field::on_key(k); -} - -void TSheet_field::on_idle() -{ -#if XVT_OS == XVT_OS_WIN - _sheet->on_idle(); -#endif -} - -void TSheet_field::exchange(bool show_value, const real& nuo) -{ - TMask& m = sheet_mask(); - - const real& vec = mask().exchange(); - - if (vec != nuo) - { - TBit_array valuta(32); - int i = 0; - for (int f = FIRST_FIELD; ;f++, i++) - { - const int pos = m.id2pos(f); - if (pos < 0) break; - if (m.fld(pos).class_id() == CLASS_REAL_FIELD) - { - if (m.fld(pos).exchangeable()) - valuta.set(i); - } - } - - for (int riga = 0; riga < items(); riga++) - { - TToken_string& r = row(riga); - for (const char* s = r.get(i = 0); s; s = r.get(++i)) - if (*s > ' ' && valuta[i]) - { - real v(s); - v *= nuo; - v /= vec; - v.round(); - r.add(v.string(), i); - } - } - } - - m.set_exchange(show_value, nuo); - - if (mask().is_running()) - force_update(); -} +#include +#include +#include + +const short FIRST_FIELD = 101; + +#if XVT_OS == XVT_OS_WIN + +#include +#include + +/////////////////////////////////////////////////////////// +// TSpreadsheet +/////////////////////////////////////////////////////////// + +class TSpreadsheet : public TWindow +{ + friend class TSheet_field; + + enum { ITF_CID = 0, LIST_CID = 1 }; + + TString_array _str; // Array di TToken_strings + TBit_array _column_disabled; + TArray _disabled; // Array di TBit_array + + TMask _mask; + int _columns; + bool _firstfocus, _active; + + XI_OBJ *_list, *_itf; + + SPREADSHEET_NOTIFY _notify; + // Matteo + SPREADSHEET_GETMASK _getmask; + + TSheet_field* _owner; // Owner + + TMask_field* _edit_field; // Current edit field + int _cur_row, _cur_rec, _cur_col; // Current cell + bool _row_dirty; // Current row changed + bool _check_enabled; // Perform OFF_ROW and OFF_CELL checks + int _needs_update; + + void init(); + friend void XVT_CALLCONV1 xiev_handler(XI_OBJ *itf, XI_EVENT *xiev); + +protected: + void list_handler(XI_EVENT *xiev); + + TMask_field* col2field(int pos) const; + TMask_field* cell2field(const XI_OBJ* cell) const; + void update_rec(int rec); + TMask_field* field(short id) const; + + int rec2row(int rec); + int row2rec(int& row); + int set_pos(int row, int col) { _cur_col = col; _cur_row = row; return _cur_rec = row2rec(_cur_row); } + + bool notify(int row, KEY k); + void notify_change(); + void on_idle(); + +public: + void update(int row); + + TToken_string& row(int n) { return _str.row(n); } + int add(const TToken_string& s) { return _str.add(s); } + int add(TToken_string* s) { return _str.add(s); } + int insert(int rec); + bool destroy(int rec = -1); + TString_array& rows_array() { return _str; } + + void set_focus_cell(int riga, int colonna); + void activate(bool on); + void enable_column(int col, bool on = TRUE); + // Matteo + void delete_column( const int col ) const; + void move_column( const int fromindex, const int toindex ) const; + void swap_columns(const int fromid, const int toid) const; + void swap_rows( const int fromindex, const int toindex); + + void set_column_width( const int col, const int width ) const; + void set_column_header( const int col, const TString& header ) const; + + + void enable_cell(int row, int column, bool on = TRUE); + bool cell_disabled(int row, int column) const; + + TMask& sheet_mask() { return _mask; } + TMask& mask() const; + + void mask2str(int n); + void str2mask(int n); + KEY edit(int n); + + int items() const { return _str.items(); } + int selected() const { return _cur_rec; } + void select(int r); + int columns() const { return _columns; } + + bool dirty() const { return _owner->dirty(); } + void set_dirty(bool spork = TRUE) { _owner->set_dirty(spork); } + + bool active() const { return _active; } + bool test_focus_change(); + + // Matteo + void set_getmask(SPREADSHEET_GETMASK n) { _getmask = n; } + + void set_notify(SPREADSHEET_NOTIFY n) { _notify = n; } + + TSpreadsheet(short x, short y, short dx, short dy, const char* maskname, int maskno, + const char* head, WINDOW parent, TSheet_field* owner); + virtual ~TSpreadsheet(); +}; + +// Certified 100% +void TSpreadsheet::init() +{ + static bool first = TRUE; + if (!first) return; + + XVT_FNTID font = xvt_dwin_get_font(TASK_WIN); + xi_set_font_id(font); + + xi_init(); + xi_set_pref(XI_PREF_3D_LOOK, TRUE); + first = FALSE; +} + + +TSpreadsheet::TSpreadsheet(short x, short y, short dx, short dy, + const char* maskname, int maskno, + const char* head, WINDOW parent, + TSheet_field* o) +: _mask(maskname, maskno), _notify(NULL), _edit_field(NULL), _getmask( NULL ), + _owner(o), _cur_row(0), _cur_col(0), _active(TRUE), + _row_dirty(FALSE), _check_enabled(TRUE), _firstfocus(TRUE), + _needs_update(-1) +{ + const int NUMBER_WIDTH = 3; + const int MAX_COL = 32; + int m_width[MAX_COL], v_width[MAX_COL]; + int fixed_columns = 1; // Number of fixed columns + + init(); + + _mask.set_sheet(o); + + // Calcolo larghezza massima tabella + + TToken_string header(head); + TToken_string new_header(256); + int i = 0, tot_width = NUMBER_WIDTH+1; + int f_width = tot_width<<1; // Stima larghezza colonne fisse + int max_width = f_width; // Stima larghezza della colonna piu' grande + + for (const char* h = header.get(); h; h = header.get(), i++) + { + CHECKD(i < MAX_COL, "Tu meni calumns in scit: ", i); + + const int cid = FIRST_FIELD+i; // Column & Field ID + const TMask_field* f = field(cid); // Field on mask + CHECKD(f, "The spreadsheet mask needs ALSO field ", cid); + + TString80 testa(h); + const int at = testa.find('@'); + int m, v; // Memory and video width + if (at >= 0) + { + const TString& wi = testa.mid(at+1); + m = atoi(wi); + if (wi.find('F') >= 0) + { + fixed_columns++; + f_width += m+1; + } + testa.cut(at); + v = max(at, m+(f->has_query() ? 1 : 0)); + } + else + { + m = testa.len(); + v = m+(f->has_query() ? 1 : 0); + } + + if (f->is_edit() && m < f->size()) + m = f->size(); + if (v > 69) + v = 69; + + m_width[i] = m+1; // m = number of allowed chars + v_width[i] = v+1; // v = width of column + if (v >= max_width) max_width = v+1; + + tot_width += v_width[i]; + + new_header.add(testa); + } + _columns = i; + + if (x < 0) x = 0; + if (y < 0) y = 0; + if (dx == 0) + { + dx = tot_width; + if (dx > 76) dx = -x; + } + + RCT rct = resize_rect(x, y, dx, dy, WO_TE, parent); + rct.bottom -= 12; + rct.right -= 28; + + if ((f_width+max_width)*CHARX > rct.right) + fixed_columns = 1; + + XI_OBJ_DEF* itfdef = xi_create_itf_def(ITF_CID, + (XI_EVENT_HANDLER)xiev_handler, &rct, (char*)maskname, + PTR_LONG(this)); + itfdef->v.itf->automatic_back_color = FALSE; + itfdef->v.itf->back_color = MASK_BACK_COLOR; + + XI_OBJ_DEF* listdef = xi_add_list_def(itfdef, LIST_CID, + 0, 0, rct.bottom-rct.top, + XI_ATR_ENABLED | XI_ATR_VISIBLE, + NORMAL_COLOR, NORMAL_BACK_COLOR, // normal + NORMAL_COLOR, DISABLED_BACK_COLOR, // disabled + COLOR_RED, // active + LIST_CID); + listdef->v.list->sizable_columns = TRUE; + listdef->v.list->movable_columns = TRUE; + listdef->v.list->scroll_bar = TRUE; + listdef->v.list->scroll_bar_button = TRUE; + listdef->v.list->fixed_columns = fixed_columns; + listdef->v.list->width = rct.right-rct.left; + listdef->v.list->min_cell_height = CHARY; + listdef->v.list->min_heading_height = CHARY; + listdef->v.list->white_space_color = MASK_DARK_COLOR; + + word attr = XI_ATR_RJUST; + if (_mask.id2pos(FIRST_FIELD -1) != -1) + attr |= XI_ATR_SELECTABLE; + + XI_OBJ_DEF* coldef = xi_add_column_def(listdef, 0, attr, 0, NUMBER_WIDTH, NUMBER_WIDTH, + attr & XI_ATR_SELECTABLE ? "X" : ""); + + coldef->v.column->heading_platform = TRUE; + coldef->v.column->column_platform = TRUE; + + if (attr & XI_ATR_SELECTABLE) + { + coldef->v.column->icon_rid = ICO_SEARCH; + coldef->v.column->icon_x = 2; + listdef->v.list->min_heading_height = 16; + } + else + coldef->v.column->center_heading = TRUE; + + + for (h = new_header.get(0), i = 0; h; h = new_header.get(), i++) + { + const TString80 testo(h); + const int cid = FIRST_FIELD+i; // Column & Field ID + const TMask_field* f = field(cid); // Field on mask + const int acqua = f->class_id(); + + long flags = XI_ATR_EDITMENU | XI_ATR_AUTOSCROLL; + switch (acqua) + { + case CLASS_EDIT_FIELD: + if (f->right_justified()) + flags |= XI_ATR_RJUST; + break; + case CLASS_REAL_FIELD: + flags |= XI_ATR_RJUST; + break; + case CLASS_BUTTON_FIELD: + flags |= XI_ATR_SELECTABLE; + break; + default: + break; + } + if (f->active()) flags |= XI_ATR_ENABLED | XI_ATR_FOCUSBORDER | XI_ATR_AUTOSELECT; + else _column_disabled.set(i); + + coldef = xi_add_column_def(listdef, cid, flags, cid, v_width[i], m_width[i], + (char*)(const char*)testo); + coldef->v.column->heading_platform = TRUE; + coldef->v.column->center_heading = TRUE; + } + + RCT itfrct; + xi_get_def_rect(itfdef, &itfrct); + xvt_rect_offset(&itfrct, rct.left, rct.top); + itfrct.bottom++; + + WINDOW win = xvt_win_create(W_NO_BORDER, &itfrct, "", 0, parent, + 0, EM_ALL, (EVENT_HANDLER)xi_event, 0L); + CHECK(win, "Can't create a window for the spreadsheet"); + + set_win(win); // Set TWindow::_win + itfdef->v.itf->win = win; // Link interface to win + + xi_create(NULL, itfdef); // Create the whole thing! + xi_tree_free(itfdef); // Free definitions + + _itf = xi_get_itf(win); // Store useful references for later use + _list = xi_get_obj(_itf, LIST_CID); +} + +TSpreadsheet::~TSpreadsheet() +{ + set_win(NULL_WIN); +} + + +// Converts a row number in the correspondig record number +int TSpreadsheet::row2rec(int& row) +{ + int rows; + const long* rec = xi_get_list_info(_list, &rows); + + int r = -1; + if (row < 0) + { + row = 0; + r = (int)rec[row]-1; + } + else + if (row >= rows) + { + row = rows-1; + r = (int)rec[row]+1; + } + else + r = (int)rec[row]; + + CHECKD(r >= 0 && r <= items(), "Sheet line out of range: ", row); + + return r; +} + + +// Converts a row number in the correspondig record number +int TSpreadsheet::rec2row(int record) +{ + int rows; + const long* rec = xi_get_list_info(_list, &rows); + int r = int(record - rec[0]); + if (r < 0 || r >= rows) + r = -1; + + return r; +} + + +// Retrieves the corresponding field of the mask from a spredsheet cell +TMask_field* TSpreadsheet::col2field(int pos) const +{ + int num; + XI_OBJ** column = xi_get_member_list(_list, &num); + CHECKD(pos >= 0 && pos < num, "Bad column number", pos); + + TMask_field* good = NULL; + for (short id = column[pos]->cid; ; id += 100) + { + TMask_field* f = field(id); + if (f == NULL) break; // Search failed + good = f; // We've found a field with the proper ID ... + if (f->active()) break; // ... and it's active: end of search + } + + CHECKD(good, "Can't find field corresponding to column ", pos); + return good; +} + + +// Retrieves the corresponding field of the mask from a spredsheet cell +TMask_field* TSpreadsheet::cell2field(const XI_OBJ* cell) const +{ + return col2field(cell->v.cell.column); +} + +void TSpreadsheet::update_rec(int rec) +{ + const int riga = rec2row(rec); + if (riga >= 0) + { + XI_OBJ row; + XI_MAKE_ROW(&row, _list, riga); + xi_cell_request(&row); // Update internal values + } +} + + +void TSpreadsheet::set_focus_cell(int riga, int colonna) +{ + xvt_scr_set_focus_vobj(win()); + const int r = row2rec(riga); + + for (; colonna < _columns; colonna++) + if (!cell_disabled(r, colonna-1)) + break; + + if (colonna <= _columns) + { + XI_OBJ cell; + XI_MAKE_CELL(&cell, _list, riga, colonna); + xi_set_focus(&cell); + + if (_edit_field == NULL) + { + _cur_row = riga; + _cur_col = colonna; + _cur_rec = r; + _edit_field = col2field(colonna); + str2mask(_cur_rec); + _row_dirty = FALSE; + } + } +} + + +int TSpreadsheet::insert(int rec) +{ + if (items() >= 999 || !_active) + return -1; + if (rec < 0 && items() > 0 && !_owner->append() ) + rec = _cur_rec + 1; + + const int r = _str.insert(new TToken_string(80), rec); + + const bool ok = notify(r, K_INS); + + if (!ok) + { + _str.destroy(r); + return -1; + } + + _disabled.insert(NULL, r); + xi_insert_row(_list, INT_MAX); + xi_cell_request(_list); + + return r; +} + + +bool TSpreadsheet::destroy(int rec) +{ + bool ok = TRUE; + + if (rec < 0) + { + _disabled.destroy(); + _str.destroy(); + set_dirty(_row_dirty = FALSE); + } + else + { + _disabled.destroy(rec, TRUE); // Destroy enable info + ok = _str.destroy(rec, TRUE); // Destroy line + enable_cell(_str.items(), -1); // Enable last line + } + + if (ok && mask().is_running()) + update(-1); + + return ok; +} + + +void TSpreadsheet::update(int row) +{ + if (row < 0) + { + xi_cell_request(_list); // Force updatde + xi_scroll(_list, XI_SCROLL_FIRST); + } + else + update_rec(row); +} + + +void TSpreadsheet::notify_change() +{ + if (!_row_dirty) + { + notify(_cur_rec, K_SPACE); + _row_dirty = TRUE; + set_dirty(); + } +} + + +bool TSpreadsheet::test_focus_change() +{ + bool ok = dirty() != 3; + if (ok && _row_dirty) + ok = xi_move_focus(_itf) ? TRUE : FALSE; + return ok; +} + + +HIDDEN void XVT_CALLCONV1 xiev_handler(XI_OBJ *itf, XI_EVENT *xiev) +{ + TSpreadsheet* es = (TSpreadsheet*)xi_get_app_data(itf); + CHECK(es, "NULL Edit sheet in xi event"); + es->list_handler(xiev); +} + +// Certified 75% +void TSpreadsheet::list_handler(XI_EVENT *xiev) +{ + static KEY _lastab = K_TAB; + static bool _cell_dirty; + + switch (xiev->type) + { + case XIE_GET_FIRST: + if (items() > 0L) + { + long n = items() * (long)xiev->v.rec_request.percent / 100L; + if (n < 0L) n = 0L; + xiev->v.rec_request.data_rec = n; + } + else + xiev->refused = TRUE; + break; + case XIE_GET_LAST: + xiev->v.rec_request.data_rec = items()-1; + break; + case XIE_GET_PREV: + case XIE_GET_NEXT: + { + const long n = xiev->v.rec_request.spec_rec + (xiev->type == XIE_GET_NEXT ? +1 : -1) ; + if (n < 0 || n >= items()) + xiev->refused = TRUE; + else + xiev->v.rec_request.data_rec = n; + } + break; + case XIE_CELL_REQUEST: + { + const int rec = (int)xiev->v.cell_request.rec; + const char* src = NULL; + int nm; + XI_OBJ** obj = xi_get_member_list(xiev->v.cell_request.list, &nm); + const int num = xiev->v.cell_request.col_nbr; + const int cid = obj[num]->cid; + + if (cid >= FIRST_FIELD) + { + if (rec < items()) + { + const int col = cid - FIRST_FIELD; + TMask_field* f = field(cid); + const int acqua = f->class_id(); + + src = row(rec).get(col); // Set value for cell + if (src && *src && f->is_edit()) + { + src = f->picture_data(src, FALSE); // Get formatted string + } + if (field(cid)->has_query()) + { + xiev->v.cell_request.button = TRUE; + xiev->v.cell_request.button_on_focus = TRUE; + } + if (cell_disabled(rec, col)) + xiev->v.cell_request.back_color = DISABLED_BACK_COLOR; + } + } + else + { + src = format("%d", rec+1); + } + + char* dst = xiev->v.cell_request.s; + if (src && *src) + { + const int& len = xiev->v.cell_request.len; + strncpy(dst, src, len); + } + else + *dst = '\0'; + } + break; + case XIE_CHG_CELL: + if (_edit_field && !_cell_dirty) + { + notify_change(); + _cell_dirty = TRUE; + _edit_field->set_focusdirty(); + } + break; + case XIE_BUTTON: + if (xiev->v.xi_obj->type == XIT_CELL) + dispatch_e_char(win(), K_F9); + else + if (xiev->v.xi_obj->type == XIT_LIST) + insert(-1); + break; + case XIE_SELECT: + if (xiev->v.xi_obj->type == XIT_ROW) + { + _check_enabled = FALSE; + + const int oldrec = _cur_rec; + set_pos(xiev->v.select.xi_obj->v.row, xiev->v.select.column); + if (oldrec != _cur_rec) + _row_dirty = FALSE; + + str2mask(_cur_rec); + update(_cur_rec); // Forces update delayed by str2mask + + if (_mask.id2pos(FIRST_FIELD-1) != -1) + { + TMask_field* button = &_mask.field(FIRST_FIELD-1); + button->on_hit(); + if (_mask.dirty()) + { + notify_change(); + mask2str(_cur_rec); + } + } + _check_enabled = TRUE; + } + xiev->refused = TRUE; + break; + case XIE_DBL_CELL: + { + _check_enabled = FALSE; + + const int oldrec = _cur_rec; + set_pos(xiev->v.xi_obj->v.cell.row, xiev->v.xi_obj->v.cell.column); + + if (oldrec != _cur_rec || !_row_dirty) + { + _row_dirty = FALSE; + notify_change(); + } + const KEY k = edit(_cur_rec); + if (k == K_ENTER) + { + update_rec(_cur_rec); + _row_dirty = TRUE; + } else + if (k == K_DEL) + { + _row_dirty = _cell_dirty = FALSE; + if (_cur_rec >= items()) + _cur_rec = items() - 1; + } + + if (_cur_rec >= 0 && !cell_disabled(_cur_rec, _cur_col-1)) + set_focus_cell(_cur_row, _cur_col); + _check_enabled = TRUE; + } + break; + case XIE_ON_LIST: + if (_firstfocus) // Trick to avoid the sheet to keep the focus forever ... + { // .. it costed me two day worth of hard work! + xiev->refused = TRUE; + _firstfocus = FALSE; + } + else + mask().set_focus_win(win(), FALSE); + break; + case XIE_OFF_LIST: + break; + case XIE_ON_ROW: + if (_check_enabled) + { + set_pos(xiev->v.xi_obj->v.row, _cur_col); + if (_cur_rec < items() && notify(_cur_rec, K_TAB)) + { + str2mask(_cur_rec); + _row_dirty = FALSE; + } + else + { + _cur_row = _cur_rec = 0; + xiev->refused = TRUE; + } + } + break; + case XIE_OFF_ROW: + if (_row_dirty && _check_enabled) + { + _check_enabled = FALSE; // Avoid recursion! + bool ok = sheet_mask().check_fields(); + if (ok) + { + mask2str(_cur_rec); // Update sheet with mask contents + ok = notify(_cur_rec, K_ENTER); // Notify edit + } + if (ok) + { + xvt_statbar_refresh(); + _edit_field = NULL; // Reset current field + } + else + xiev->refused = TRUE; + + _check_enabled = TRUE; + } + break; + case XIE_ON_CELL: + if (_check_enabled) + { + TMask_field* f = cell2field(xiev->v.xi_obj); + const int col = (f->dlg()-FIRST_FIELD) % 100; + if (cell_disabled(_cur_rec, col)) // If the cell is disabled ... + { + dispatch_e_char(win(), _lastab); // ... skip to the next one. + _edit_field = NULL; + } + else + { + _edit_field = f; + _cur_col = xiev->v.xi_obj->v.cell.column; + _edit_field->set_focusdirty(_cell_dirty = FALSE); + } + } + break; + case XIE_OFF_CELL: + if (_edit_field && _check_enabled) + { + _check_enabled = FALSE; + if (_cell_dirty) + { + TMask_field* c = _edit_field; // Save field, it could turn out to be NULL on error + const char* nuo = c->picture_data(xi_get_text(xiev->v.xi_obj, NULL, -1), TRUE); + c->set(nuo); // Set new mask value + c->set_dirty(); // Get it dirty! + if (c->on_key(c->is_edit() ? K_TAB : K_SPACE) == FALSE) // Test it + xiev->refused = *nuo != '\0'; + + if (!xiev->refused) + mask2str(_cur_rec); // Update sheet row + } + _check_enabled = TRUE; + } + break; + case XIE_GET_PERCENT: + { + const long rec = xiev->v.get_percent.record; + long n = items(); if (n < 1) n = 1; + xiev->v.get_percent.percent = int(rec * 100L / n); + } + break; + case XIE_CLEANUP: + break; + case XIE_XVT_EVENT: + { + EVENT* ep = &xiev->v.xvte; + switch (ep->type) + { + case E_FOCUS: + if (_check_enabled && ep->v.active == FALSE) + { + const bool ok = (bool)xi_move_focus(_itf); + if (!ok) + { + set_dirty(3); + xiev->refused = TRUE; + } + } + break; + case E_CHAR: + if (_edit_field) + { + const KEY k = e_char_to_key(ep); + switch(k) + { + case K_TAB: + case K_BTAB: + _lastab = k; + break; + case K_UP: + case K_DOWN: + _lastab = _cur_col < 2 ? K_TAB : K_BTAB; + break; + case K_F1: + case K_SHIFT+K_F1: + _check_enabled = FALSE; // Disable checks + _edit_field->on_key(K_F1); + set_focus_cell(_cur_row, _cur_col); + _check_enabled = TRUE; // Enable checks + break; + case K_F2: + case K_F3: + case K_F8: + case K_F9: + case K_F11: + if (_check_enabled && active()) + { + _check_enabled = FALSE; // Disable checks + notify_change(); + const bool ok = _edit_field->on_key(k); + if (!ok && k == K_F9) // Ricerca non completata? + { + _edit_field = &_mask.fld(_mask.focus_field()); + const short foca = _edit_field->dlg(); + const int col = (foca - FIRST_FIELD) % 100 +1; + if (col > 0 && col != _cur_col) // Ricerca alternativa + { + _cur_col = col; + dispatch_e_char(win(), K_F9); + } + } + if (ok) + { + mask2str(_cur_rec); + update(_cur_rec); _needs_update = -1; // Update immediately! + set_focus_cell(_cur_row, _cur_col); + } + _check_enabled = TRUE; // Re-enable checks + } + break; + case K_PREV: + case K_NEXT: + case K_ESC: + if (xi_move_focus(_itf)) + dispatch_e_char(parent(), k); + break; + case K_ENTER: + case K_SHIFT+K_ENTER: + if (xi_move_focus(_itf)) + dispatch_e_char(parent(), k == K_ENTER ? K_TAB : K_BTAB); + break; + case K_CTRL+K_PREV: + xi_scroll(_list, XI_SCROLL_PGUP); + break; + case K_CTRL+K_NEXT: + xi_scroll(_list, XI_SCROLL_PGDOWN); + break; + case K_CTRL+K_HOME: + xi_scroll(_list, XI_SCROLL_FIRST); + break; + case K_CTRL+K_END: + xi_scroll(_list, XI_SCROLL_LAST); + break; + default: + break; + } + } + break; + default: + break; + } + } + break; + default: + break; + } +} + +void TSpreadsheet::activate(bool on) +{ + _active = on; + + const dword old = xi_get_attrib(_list); + dword att = on ? (old & ~XI_ATR_NAVIGATE) : (old | XI_ATR_NAVIGATE); + if (old != att) + { + int num; + XI_OBJ** columns = xi_get_member_list(_list, &num); + + xi_move_focus(_itf); // Set focus to interface + if (on) + att |= XI_ATR_TABWRAP; + else + att &= ~XI_ATR_TABWRAP; + + xi_set_attrib(_list, att); + + for (int col = 1; col < num; col++) + { + XI_OBJ* column = columns[col]; + att = xi_get_attrib(column); + if (on) + { + att &= ~XI_ATR_READONLY; + att |= XI_ATR_AUTOSELECT; + } + else + { + att |= XI_ATR_READONLY; + att &= ~XI_ATR_AUTOSELECT; + } + xi_set_attrib(column, att); // Set new attributes + } + } +} + +void TSpreadsheet::select(int r) +{ + xi_scroll_rec(_list, r, NORMAL_COLOR, XI_ATR_ENABLED | XI_ATR_AUTOSELECT, 0); + set_focus_cell(0, 1); +} + +#else + +#include + +/////////////////////////////////////////////////////////// +// TSpreadsheet +/////////////////////////////////////////////////////////// + +class TSpreadsheet : public TArray_sheet +{ + TMask _mask; + SPREADSHEET_NOTIFY _notify; + // Matteo + SPREADSHEET_GETMASK _getmask; + + TSheet_field * _owner; + + TBit_array _column_disabled; + TArray _disabled; // Array di TBit_array + + +protected: + virtual bool on_key(KEY key); + KEY edit(int n, KEY tasto); + bool notify(int r, KEY k); + + TMask_field* field(short id) const; + +public: + TSpreadsheet(short x, short y, short dx, short dy, const char* maskname, int maskno, + const char* head, WINDOW parent, TSheet_field * o); + + TMask& sheet_mask() { return _mask; } + TMask& mask() const; + + // Matteo + void set_getmask(SPREADSHEET_GETMASK n) { _getmask = n; } + + void set_notify(SPREADSHEET_NOTIFY n) { _notify = n; } + void set_dirty(bool spork = TRUE) { _owner->set_dirty(spork);} + bool dirty() const { return _owner->dirty(); } + bool test_focus_change() { return TRUE; } + + void mask2str(int riga); + void str2mask(int riga); + + void enable_column(int col, bool on); + // Matteo + void delete_column( const int col ) const; + void move_column( const int fromindex, const int toindex ) const; + void set_column_width( const int col, const int width ) const; + void set_column_header( const int col, const TString& header ) const; + + void enable_cell(int row, int column, bool on = TRUE); + bool cell_disabled(int row, int column) const; + virtual ~TSpreadsheet() {} +}; + +TSpreadsheet::TSpreadsheet(short x, short y, short dx, short dy, + const char* maskname, int maskno, + const char* head, WINDOW parent, + TSheet_field* o) +: TArray_sheet(x, y, dx, dy, maskname, head, 0, parent), _owner(o), + _mask(maskname, maskno), _notify(NULL), /* Matteo */ _getmask( NULL ) +{ + _mask.set_sheet(o); +} + +bool TSpreadsheet::on_key(KEY k) +{ + switch(k) + { + case K_CTRL_ENTER: + case K_ESC: + mask().send_key(k, 0); + return TRUE; + case K_ENTER: // Selezione riga per editing + if (items() < 1) k = K_INS; // Se vuoto crea riga da editare + case K_INS: + case 'A': // Aggiunge dopo + case 'I': // Aggiunge prima + { + int n = (int)selected(); + if (k != K_ENTER) + { + if (k == K_INS) n = items(); else // Aggiunge alla fine + if (k == 'A') n++; + + if (n < 0) n = 0; else + if (n > items()) n = items(); // Controlla range n + + if (notify(n, K_INS) == FALSE) // Chiede l'ok alla applicazione + return FALSE; + + insert(TToken_string(80), n); // Aggiunge una riga vuota + k = K_INS; // Inserimento in corso + } + + notify(n, K_TAB); // Notifica selezione + notify(n, K_SPACE); // Notifica inizio cambiamento + k = edit(n, k); // Edita riga selezionata o creata + if (k == K_ENTER) + notify(n, K_ENTER); // Notifica avvenuto cambiamento + + xvt_vobj_raise(win()); // Aggiorna sheet a video + open(); + } + break; + case K_TAB: + case K_BTAB: + case K_SHIFT_TAB: + dispatch_e_char(xvt_vobj_get_parent(win()), k); + return TRUE; + default: + break; + } + + return TArray_sheet::on_key(k); +} + +#endif + +/////////////////////////////////////////////////////////// +// Metodi di TSpreadsheet comuni a tutte le piattaforme +/////////////////////////////////////////////////////////// + +TMask& TSpreadsheet::mask() const +{ + TMask* m = (TMask*)xvt_vobj_get_data(parent()); + return *m; +} + + +// Ritorna il campo con l'identificatore dato della maschera dello sheet +TMask_field* TSpreadsheet::field(short id) const +{ + const int pos = _mask.id2pos(id); + return pos < 0 ? NULL : &_mask.fld(pos); +} + + +// Ricopia i campi della maschera nel record dato ed aggiorna il display +void TSpreadsheet::mask2str(int rec) +{ + TToken_string& r = row(rec); + r.cut(0); + for (short id = FIRST_FIELD; ; id++) + { + int pos = sheet_mask().id2pos(id); + if (pos < 0) break; + + for(int dlg = id; pos >= 0; pos = sheet_mask().id2pos(dlg += 100)) + { + const TMask_field& f = sheet_mask().fld(pos); + if (f.shown() || f.ghost()) + { + r.add(f.get()); + break; + } + } +#ifdef DBG + if (pos < 0) + { + yesnofatal_box("Non e' visibile il campo %d per lo sheet", dlg); + r.add(" "); + } +#endif + } +#if XVT_OS == XVT_OS_WIN + if (_needs_update != rec) + { + if (_needs_update >= 0) + { +#ifdef DBG + warning_box("Double update: %d and %d", _needs_update, rec); +#endif + update_rec(_needs_update); + } + _needs_update = rec; + } +#endif +} + +void TSpreadsheet::on_idle() +{ + if (_needs_update >= 0) + { + update_rec(_needs_update); + _needs_update = -1; + } +} + +// Certified 50% +void TSpreadsheet::enable_cell(int row, int column, bool on) +{ + TBit_array* ba = (TBit_array*)_disabled.objptr(row); + if (ba == NULL) + { + if (on) return; // Don't waste time and memory + ba = new TBit_array(_column_disabled); + _disabled.add(ba, row); + } + + if (column >= 0) + ba->set(column, !on); + else + { + if (on) + _disabled.destroy(row, FALSE); // Let's save some memory! + else + { +#if XVT_OS == XVT_OS_WIN + ba->set(_columns); // Force right array size +#else + ba->set(32); // Force array size +#endif + ba->set(); // Set all bits + } + } +} + + +void TSpreadsheet::enable_column(int col, bool on) +{ + const bool change = _column_disabled[col] == on; + _column_disabled.set(col, !on); +#if XVT_OS == XVT_OS_WIN + if (change) + { + int num; + XI_OBJ** columns = xi_get_member_list(_list, &num); + for (int c = 1; c < num; c++) + if (columns[c]->cid == FIRST_FIELD+col) + { + XI_OBJ* column = columns[c]; + dword attr = xi_get_attrib(column); + if (on) attr |= XI_ATR_ENABLED; + else attr &= ~XI_ATR_ENABLED; + + xi_move_focus(_itf); // Set focus to interface + xi_set_attrib(column, attr); // Set new attributes + RCT r; xi_get_rect(column, &r); + xi_set_column_width(column, (r.right-r.left+1) / CHARX); // Force redraw + + break; + } + + } +#endif +} + +// Matteo + +void TSpreadsheet::delete_column( const int col ) const +{ +#if XVT_OS == XVT_OS_WIN + int num; + XI_OBJ** columns = xi_get_member_list(_list, &num); + CHECKD( col+1 < num, "Can't delete column ", col ); + XI_OBJ* column = columns[ col+1]; + + xi_delete( column ); +#endif +} + + +void TSpreadsheet::move_column( const int fromindex, const int toindex) const +{ +#if XVT_OS == XVT_OS_WIN + + int num; + XI_OBJ** columns = xi_get_member_list(_list, &num); + CHECKD(fromindex+1 < num, "Can't move column ", fromindex); + XI_OBJ* column = columns[fromindex+1]; + + xi_move_column( column, toindex ); +#endif +} + +void TSpreadsheet::swap_columns(const int fromid, const int toid) const +{ +#if XVT_OS == XVT_OS_WIN + int num; + XI_OBJ** columns = xi_get_member_list(_list, &num); + + XI_OBJ* from_column = XI_NULL_OBJ; + XI_OBJ* to_column = XI_NULL_OBJ; + int from_pos = 0; + int to_pos = 0; + + for (int c = 1; c < num; c++) + { + XI_OBJ* column = columns[c]; + if (column->cid == fromid) + { + from_column = column; + from_pos = c; + } + if (column->cid == toid) + { + to_column = column; + to_pos = c; + }; + } + + xi_move_column(from_column, to_pos); + if (to_pos < from_pos) from_pos++; + xi_move_column(to_column, from_pos); +#endif +} + +void TSpreadsheet::swap_rows( const int fromindex, const int toindex) +{ + _str.swap(fromindex, toindex); + _disabled.swap(fromindex, toindex); +} + +void TSpreadsheet::set_column_width(const int col, const int width) const +{ +#if XVT_OS == XVT_OS_WIN + int num; + XI_OBJ** columns = xi_get_member_list(_list, &num); + CHECKD(col+1 < num, "Can't set column width ", col); + XI_OBJ* column = columns[col+1]; + + xi_set_column_width(column, width); // Force redraw +#endif +} + +void TSpreadsheet::set_column_header(const int col, const TString& header) const +{ +#if XVT_OS == XVT_OS_WIN + int num; + XI_OBJ** columns = xi_get_member_list(_list, &num); + CHECKD(col+1 < num, "Can't set column width ", col); + XI_OBJ* column = columns[col+1]; + + xi_set_text(column, (char *)(const char *)header ); + RCT r; xi_get_rect(column, &r); + xi_set_column_width(column, (r.right-r.left+1) / CHARX); // Force redraw +#endif +} + + + +// Certified 99% +bool TSpreadsheet::cell_disabled(int row, int column) const +{ + TBit_array* ba = (TBit_array*)_disabled.objptr(row); + if (ba == NULL) return _column_disabled[column]; // Use default + return (*ba)[column]; +} + + +// Certified 75% +void TSpreadsheet::str2mask(int riga) +{ + if (riga == items()) + { + sheet_mask().reset(); + mask2str(riga); + return; + } + + TToken_string& r = row(riga); + TString val(80); + + for (int i = 0; i < sheet_mask().fields(); i++) + { + TMask_field& f = sheet_mask().fld(i); + const short id = f.dlg(); + if (id >= FIRST_FIELD) + { + val = r.get((id % 100)-1); + f.set(val); + const bool on = active() ? !cell_disabled(riga, id-FIRST_FIELD) : FALSE; + f.enable(on); + } + } + + for (i = 0; i < sheet_mask().fields(); i++) + { + TMask_field& f = sheet_mask().fld(i); + const short id = f.dlg(); + if (id >= FIRST_FIELD && + f.class_id() != CLASS_BUTTON_FIELD && + (f.active() || f.ghost())) + { + if (f.has_check()) + f.check(STARTING_CHECK); + f.set_dirty(FALSE); + f.on_hit(); + } + } + + for (i = 0; i < sheet_mask().fields(); i++) + { + TMask_field& f = sheet_mask().fld(i); + const short id = f.dlg(); + if (id > FIRST_FIELD) + { + if (f.dirty() == TRUE) + f.set_dirty(FALSE); + } + } + + sheet_mask().set_caption(format("Riga %d", riga+1)); +} + + +// Certified 100% +bool TSpreadsheet::notify(int rec, KEY k) +{ + const bool ok = _notify ? _notify(*_owner, rec, k) : TRUE; + if (k == K_ENTER) + set_dirty(ok ? TRUE : 3); + return ok; +} + +// Certified 99% +#if XVT_OS != XVT_OS_WIN +KEY TSpreadsheet::edit(int n, KEY tasto) +#else + KEY TSpreadsheet::edit(int n) +#endif +{ + KEY k; + const int olditems = items(); + str2mask(n); + + if( _getmask ) + { + TMask& totalmask = sheet_mask(); + TMask& usermask = *_getmask( n, totalmask, FALSE ); + + // Carico la maschera dell'utente con la maschera totale + for( int i = 0; i < usermask.fields(); i ++ ) + { TMask_field& dest_field = usermask.fld( i ); + if ( dest_field.dlg( ) > 100 ) + { TMask_field& source_field = totalmask.field( dest_field.dlg( ) ); + dest_field.set( source_field.get( ) ); } + }; + k = usermask.run(); + if (k == K_ENTER) + // Carico la maschera totale con la maschera dell'utente + for( i = 0; i < usermask.fields(); i ++ ) + { + TMask_field& source_field = usermask.fld( i ); + if ( source_field.dlg( ) > 100 ) + { TMask_field& dest_field = totalmask.field( source_field.dlg() ); + dest_field.set( source_field.get( ) ); } + }; + _getmask( n, totalmask, TRUE ); + + } + else + k = sheet_mask().run(); + + + if (k == K_ENTER) + { + mask2str(n); + } else + if (k == K_DEL) + { + const bool ok = notify(n, K_DEL); // Notifica intenzione di cancellare + if (ok) + { + destroy(n); + if (n < items()) + str2mask(n); + } + } +#if XVT_OS != XVT_OS_WIN +else + if (k == K_ESC) + { + if (tasto == K_INS) + destroy(n); + } +#endif + + return k; +} + +/////////////////////////////////////////////////////////// +// TSheet_field +/////////////////////////////////////////////////////////// + +// Certified 100% +TSheet_field::TSheet_field(TMask* m) +: TMask_field(m), _sheet(NULL), _append(TRUE) +{} + +// Certified 100% +word TSheet_field::class_id() const +{ + return CLASS_SHEET_FIELD; +} + +// Certified 100% +TSheet_field::~TSheet_field() +{ + CHECK(_sheet, "Can't delete NULL sheet"); + delete _sheet; +} + +// Certified 100% +void TSheet_field::reset() +{ + _sheet->destroy(); + _sheet->sheet_mask().reset(); +} + +// Certified 100% +void TSheet_field::destroy(int r) +{ + _sheet->destroy(r); +} + + +void TSheet_field::parse_head(TScanner& scanner) +{ + _width = scanner.integer(); + _size = scanner.integer(); + if (_size == 0) _size = -1; +} + + +// Certified 100% +bool TSheet_field::parse_item(TScanner& scanner) +{ + if (scanner.key() == "IT") + { + _head.add(scanner.string()); + return TRUE; + } + return TMask_field::parse_item(scanner); +} + +// Certified 100% +void TSheet_field::create(WINDOW parent) +{ + const TMask& m = mask(); + _sheet = new TSpreadsheet(_x, _y, _width, _size, m.source_file(), + m.sheets(), _head, parent, this); + + _win = _sheet->win(); + xvt_vobj_set_visible(_win, shown()); + if (!enabled()) disable(); +} + + +// Certified 100% +TString_array& TSheet_field::rows_array() const +{ + return _sheet->rows_array(); +} + + +// Certified 100% +// Ritorna l'indice della prima riga vuota dello sheet +int TSheet_field::first_empty() const +{ + const int max = _sheet->items(); + for (int n = 0; n < max; n++) + if (_sheet->row(n).empty_items()) + break; + return n; +} + + +TToken_string& TSheet_field::row(int n) +{ + const int max = _sheet->items(); + if (n < 0 || n >= max) + { + if (n < 0) n = first_empty(); + if (n >= max) + { + n = _sheet->add(new TToken_string(80)); + force_update(); + } + } + return _sheet->row(n); +} + + +void TSheet_field::force_update(int r) +{ +#if XVT_OS == XVT_OS_WIN + _sheet->update(r); +#else + _sheet->open(); +#endif +} + +int TSheet_field::items() const +{ + return (int)_sheet->items(); +} + +int TSheet_field::selected() const +{ + return _sheet->selected(); +} + +void TSheet_field::set_notify(SPREADSHEET_NOTIFY n) +{ + _sheet->set_notify(n); +} + +void TSheet_field::set_getmask(SPREADSHEET_GETMASK n) +{ + _sheet->set_getmask( n ); +} + + +// Certified 50% +void TSheet_field::highlight() const +{ + TMask_field::highlight(); +#if XVT_OS == XVT_OS_WIN + int rows; xi_get_list_info(_sheet->_list, &rows); + if (rows > 0) + { + _sheet->_firstfocus = FALSE; + if (_sheet->_edit_field == NULL) + _sheet->set_focus_cell(0, 1); + } +#endif +} + +void TSheet_field::enable(bool on) +{ + _sheet->activate(on); +} + +void TSheet_field::enable_column(int column, bool on) +{ + _sheet->enable_column(column, on); +} + + +void TSheet_field::enable_cell(int row, int column, bool on) +{ + _sheet->enable_cell(row, column, on); +} + +bool TSheet_field::cell_disabled(int row, int column) const +{ + return _sheet->cell_disabled(row, column); +} + +// Matteo +void TSheet_field::delete_column( const int col ) const +{ + _sheet->delete_column( col ); +} + +void TSheet_field::swap_columns(const int fromid, const int toid) const +{ + _sheet->swap_columns(fromid, toid); +} + +void TSheet_field::move_column( const int fromindex, const int toindex ) const +{ + _sheet->move_column(fromindex, toindex); +} + +void TSheet_field::swap_rows( const int fromindex, const int toindex) +{ + _sheet->swap_rows(fromindex, toindex); +} + +void TSheet_field::set_column_width( const int col, const int width ) const +{ + _sheet->set_column_width(col, width); +} + +void TSheet_field::set_column_header( const int col, const TString& header ) const +{ + _sheet->set_column_header(col, header); +} + +TMask& TSheet_field::sheet_mask() const +{ + return _sheet->sheet_mask(); +} + +bool TSheet_field::on_hit() +{ + if (!mask().is_running()) + { + force_update(); + set_dirty(FALSE); + } + return TRUE; +} + +void TSheet_field::select(int r) +{ + _sheet->select(r); +} + + +bool TSheet_field::test_focus_change() +{ + return _sheet->test_focus_change(); +} + +bool TSheet_field::on_key(KEY k) +{ + if (k == K_ENTER) + { + if (!test_focus_change()) + return FALSE; + } + return TMask_field::on_key(k); +} + +void TSheet_field::on_idle() +{ +#if XVT_OS == XVT_OS_WIN + _sheet->on_idle(); +#endif +} + +void TSheet_field::exchange(bool show_value, const real& nuo) +{ + TMask& m = sheet_mask(); + + const real& vec = mask().exchange(); + + if (vec != nuo) + { + TBit_array valuta(32); + int i = 0; + for (int f = FIRST_FIELD; ;f++, i++) + { + const int pos = m.id2pos(f); + if (pos < 0) break; + if (m.fld(pos).class_id() == CLASS_REAL_FIELD) + { + if (m.fld(pos).exchangeable()) + valuta.set(i); + } + } + + for (int riga = 0; riga < items(); riga++) + { + TToken_string& r = row(riga); + for (const char* s = r.get(i = 0); s; s = r.get(++i)) + if (*s > ' ' && valuta[i]) + { + real v(s); + v *= nuo; + v /= vec; + v.round(); + r.add(v.string(), i); + } + } + } + + m.set_exchange(show_value, nuo); + + if (mask().is_running()) + force_update(); +} diff --git a/include/msksheet.h b/include/msksheet.h index 637d3b533..2ac221236 100755 --- a/include/msksheet.h +++ b/include/msksheet.h @@ -1,76 +1,76 @@ -#ifndef __MSKSHEET_H -#define __MSKSHEET_H - -#ifndef __MASK_H -#include -#endif - -class TSheet_field; -class TSpreadsheet; - -typedef bool (*SPREADSHEET_NOTIFY)(TSheet_field& s, int r, KEY k); -typedef TMask* (*SPREADSHEET_GETMASK)( int numriga, TMask& fullmask, bool destroy ); - - -class TSheet_field : public TMask_field -{ - TSpreadsheet* _sheet; - TToken_string _head; - bool _append; - -protected: - virtual word class_id() const; - virtual bool on_hit(); - virtual bool on_key(KEY k); - virtual bool test_focus_change(); - - virtual void parse_head(TScanner& scanner); - virtual bool parse_item(TScanner& scanner); - virtual void create(WINDOW parent); - - virtual void highlight() const; - virtual void exchange(bool show_value, const real& n); - -public: - TToken_string& row(int n); // Get/Create a new row - TString_array& rows_array() const; // Get all rows - const char* cell(int r, int c) { return row(r).get(c); } // Get item c of row r - - int first_empty() const; // First empty row - int items() const; // Number of rows - int selected() const; // Number of current row - void select(int r); // Select row r as current - - virtual void reset(); - virtual void enable(bool on); - - void on_idle(); - - void destroy(int r = -1); // Destroy row - void force_update(int r = -1);// Update data/screen - - TMask& sheet_mask() const; - void set_notify(SPREADSHEET_NOTIFY n); - void set_getmask(SPREADSHEET_GETMASK n); - - void enable_column(int col, bool on = TRUE); - - void enable_cell(int row, int column, bool on = TRUE); - void disable_cell(int row, int column) { enable_cell(row, column, FALSE); } - bool cell_disabled(int row, int column) const; - - void delete_column( const int col ) const; - void move_column( const int fromindex, const int toindex ) const; - void swap_columns(const int fromid, const int toid) const; - void swap_rows( const int fromindex, const int toindex ); - - void set_column_width( const int col, const int width ) const; - void set_column_header( const int col, const TString& header ) const; - void set_append(bool on = TRUE) { _append = on;} - bool append() const { return _append;} - - TSheet_field(TMask* m); - virtual ~TSheet_field(); -}; - -#endif +#ifndef __MSKSHEET_H +#define __MSKSHEET_H + +#ifndef __MASK_H +#include +#endif + +class TSheet_field; +class TSpreadsheet; + +typedef bool (*SPREADSHEET_NOTIFY)(TSheet_field& s, int r, KEY k); +typedef TMask* (*SPREADSHEET_GETMASK)( int numriga, TMask& fullmask, bool destroy ); + + +class TSheet_field : public TMask_field +{ + TSpreadsheet* _sheet; + TToken_string _head; + bool _append; + +protected: + virtual word class_id() const; + virtual bool on_hit(); + virtual bool on_key(KEY k); + virtual bool test_focus_change(); + + virtual void parse_head(TScanner& scanner); + virtual bool parse_item(TScanner& scanner); + virtual void create(WINDOW parent); + + virtual void highlight() const; + virtual void exchange(bool show_value, const real& n); + +public: + TToken_string& row(int n); // Get/Create a new row + TString_array& rows_array() const; // Get all rows + const char* cell(int r, int c) { return row(r).get(c); } // Get item c of row r + + int first_empty() const; // First empty row + int items() const; // Number of rows + int selected() const; // Number of current row + void select(int r); // Select row r as current + + virtual void reset(); + virtual void enable(bool on); + + void on_idle(); + + void destroy(int r = -1); // Destroy row + void force_update(int r = -1);// Update data/screen + + TMask& sheet_mask() const; + void set_notify(SPREADSHEET_NOTIFY n); + void set_getmask(SPREADSHEET_GETMASK n); + + void enable_column(int col, bool on = TRUE); + + void enable_cell(int row, int column, bool on = TRUE); + void disable_cell(int row, int column) { enable_cell(row, column, FALSE); } + bool cell_disabled(int row, int column) const; + + void delete_column( const int col ) const; + void move_column( const int fromindex, const int toindex ) const; + void swap_columns(const int fromid, const int toid) const; + void swap_rows( const int fromindex, const int toindex ); + + void set_column_width( const int col, const int width ) const; + void set_column_header( const int col, const TString& header ) const; + void set_append(bool on = TRUE) { _append = on;} + bool append() const { return _append;} + + TSheet_field(TMask* m); + virtual ~TSheet_field(); +}; + +#endif diff --git a/include/relation.cpp b/include/relation.cpp index 88d56e7ff..f07f2856a 100755 --- a/include/relation.cpp +++ b/include/relation.cpp @@ -1,2128 +1,2128 @@ -// $Id: relation.cpp,v 1.58 1995-08-23 09:22:13 guy Exp $ -// relation.cpp -// fv 12/8/93 -// relation class for isam files - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -// *** check if not already defined -#define NOTFOUND (-1) - -// *** maximum number of elements in a cursor working page -#define CMAXELPAGE 8000 - - -#define print_name(out, f) out << (f.num() == LF_TABCOM ? "%" : "") << f.name() - -extern int get_error(int); - -HIDDEN const char* field_type_str(TFieldtypes f) -{ - const char* c = ""; - switch(f) - { - case _charfld: - c = "Carattere"; break; - case _realfld: - c = "Reale"; break; - case _datefld: - c = "Data"; break; - case _boolfld: - c = "Logico"; break; - // case _wordfld: - // case _intzerofld: - // case _longzerofld: - // case _intfld: - // case _longfld: - default: - c = "Intero"; break; - } - return c; -} - -/////////////////////////////////////////////////////////// -// TRelationdef -/////////////////////////////////////////////////////////// - -class TRelationdef : public TObject -{ - // @DPRIV - friend class TRelation; - - const TRelation* _rel; // Relazione padre - int _num; // Posizione file - int _numto; // Posizione padre - int _alias; // Alias - byte _key; // Chiave - TArray _fields; // Campi di join - TArray _exprs; // Condizioni di uguaglianza - TArray _altexprs; // Condizioni di uguaglianza alternative - TBit_array _forced; - bool _first_match : 1; // primo match (ed esiste) - bool _allow_lock : 1; // ????? - bool _write_enable : 1; - -public: // TObject - virtual void print_on(ostream& out) const; - -public: - int num() const { return _num; } - int link() const { return _numto; } - int alias() const { return _alias; } - bool write_enable() const { return _write_enable; } - void write_enable(bool we) { _write_enable = we; } - TRectype& load_rec(TRectype& r, const TBaseisamfile& from) const; - const char* evaluate_expr(int j, const TLocalisamfile& to); - - void print_on(TToken_string& out) const; - - TRelationdef(const TRelation* rel, int file, byte key, - int linkto, const char* relexprs, int alias, - bool allow_lock); - virtual ~TRelationdef() {} -}; - - -TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key, - int idx_to, const char* relexprs, int alias, - bool allow_lock) -: _num(idx_file), _key(key), _numto(idx_to), - _rel(rel), _fields(4), _exprs(4), _alias(alias), - _first_match(FALSE), _allow_lock(allow_lock), - _write_enable(FALSE) -{ - TToken_string rels(relexprs); - int i = 0; - TString80 r,s; - - for (const char* g = rels.get(); g; g = rels.get(), i++) - { - r = g; - int eq = r.find('='); - - CHECKS(eq > 0, "Ahoo! E l'uguale 'ndo sta? ", (const char*)g); - - s = r.left(eq); // Parte a sinistra dell' = - -#ifdef DBG - const char* n = s; - const int p = s.find('['); - if (p > 0) n = s.left(p); - if (rel->file(_num).curr().exist(n) == FALSE) - { - yesnofatal_box("Errore di JOIN: '%s' non e' un campo del file %d", - n, rel->file(_num).num()); - continue; - } -#endif - - if (r[eq+1] == '=') - { - _forced.set(i); - eq++; - } - - _fields.add(new TFieldref(s, 0)); - - s = r.mid(eq+1); - const int par = s.find('('); - if (par > 0) - { - _exprs.add(new TExpression(s.left(par), _strexpr), i); - _altexprs.add(new TExpression(s.sub(par+1, s.len()-1), _strexpr), i); - } - else - _exprs.add(new TExpression(s, _strexpr), i); - } -} - - -void TRelationdef::print_on(ostream& out) const -{ - const TLocalisamfile& f = _rel->file(_num); - - out << "JOIN "; - print_name(out, f); - - if (_numto > 0) - { - out << " TO "; - const int alias = _rel->reldef(_numto-1).alias(); - if (alias > 0) out << alias << '@'; - else - { - const TLocalisamfile& t = _rel->file(_numto); - print_name(out, t); - } - } - - if (_key > 1) out << " KEY " << (int)_key; - - if (_alias > 0) out << " ALIAS " << _alias; - - for (int i = 0; i < _fields.items(); i++) - { - if (i == 0) out << " INTO"; - out << ' ' << _fields[i] << '='; - if (_forced[i]) out << '='; - out << _exprs[i]; - if (_altexprs.objptr(i)) - out << '(' << _altexprs[i] << ')'; - } -} - - -void TRelationdef::print_on(TToken_string& out) const -{ - const TLocalisamfile& f = _rel->file(_num); - - out = ""; - print_name(out, f); - - // add description - const char* name = f.name(); - if (f.tab()) - name = TDir::tab_des(name); - else - name = prefix().description(name); - out.add(name); - - out << '|'; - - if (_numto > 0) - { - const int alias = _rel->reldef(_numto-1).alias(); - if (alias > 0) - out << alias << '@'; - else - { - const TLocalisamfile& t = _rel->file(_numto); - print_name(out, t); - } - } else out << ' '; - - out.add(_key); - out.add(_alias); - out << '|'; - for (int i = 0; i < _fields.items(); i++) - { - if (i) out << ' '; - out << _fields[i] << '='; - if (_forced[i]) out << '='; - out << _exprs[i]; - if (_altexprs.objptr(i)) - out << '(' << _altexprs[i] << ')'; - } -} - - -const char* TRelationdef::evaluate_expr(int j, const TLocalisamfile& to) -{ - TExpression& expr = (TExpression&)_exprs[j]; - for (int k = 0; k < expr.numvar(); k++) - expr.setvar(k, to.get(expr.varname(k))); - - const char* val = expr; - - if (*val == '\0' && _altexprs.objptr(j)) - { - TExpression& altexpr = (TExpression&)_altexprs[j]; - for (int k = 0; k < expr.numvar(); k++) - altexpr.setvar(k, to.get(altexpr.varname(k))); - val = altexpr; - } - - return val; -} - -/////////////////////////////////////////////////////////// -// TRelation -/////////////////////////////////////////////////////////// - -TRelation::TRelation(int logicnum) -: _files(4), _reldefs(4), _errors(NOERR) -{ - TLocalisamfile* f = new TLocalisamfile(logicnum, FALSE); - _files.add(f); -} - -TRelation::TRelation(const char* tabname) -: _files(4), _reldefs(4), _errors(NOERR) -{ - TTable* t = new TTable(tabname, FALSE); - _files.add(t); -} - -TRelation::TRelation(TLocalisamfile* l) -: _files(4), _reldefs(4), _errors(NOERR) -{ - _files.add(l); -} - -TRelation::~TRelation() -{} - -void TRelation::print_on(ostream& out) const -{ - const TLocalisamfile& f = file(); - - out << "USE "; print_name(out, f); - - const int k = f.getkey(); - if (k > 1) out << " KEY " << k; - out << endl; - - for (int r = 0; r < _reldefs.items(); r++) - out << _reldefs[r] << endl; -} - -void TRelation::restore_status() -{ - _status.restart(); - for (int i = 0; i < _files.items(); i++) - { - const int err = _status.get_int(); - const TRecnotype recno = _status.get_long(); - const int key = _status.get_int(); - - file(i).setkey(key); - if (recno >= 0l) file(i).readat(recno); - else file(i).curr().zero(); - file(i).setstatus(err); - } - for (i = 0; i < _reldefs.items(); i++) - { - const bool first_match = _status.get_int() ? TRUE : FALSE; - reldef(i)._first_match = first_match; - } -} - -void TRelation::save_status() -{ - _status.cut(0); - for (int i = 0; i < _files.items(); i++) - { - const int err = file(i).status(); - const TRecnotype recno = file(i).eof() ? -1l : file(i).recno(); - const int key = file(i).getkey(); - _status.add (err); - _status.add (recno); - _status.add (key); - } - for (i = 0; i < _reldefs.items(); i++) - { - const bool first_match = reldef(i)._first_match; - _status.add(format("%d",first_match)); - } -} - -int TRelation::log2ind(int log) const -{ - // returns _files index of logical number or - // NOTFOUND if not present - // sets error status - - if (log < 0) return alias2ind(-log); - - const int num = _files.items(); - if (log > 0) - { - for (int i = 0; i < num; i++) - if (file(i).num() == log) return i; - } - return num ? 0 : NOTFOUND; -} - -int TRelation::alias2ind(int alias) const -{ - if (alias < 1) return 0; - - for (int i = 0; i < _reldefs.items(); i++) - { - const TRelationdef& r = (const TRelationdef&)_reldefs[i]; - if (r.alias() == alias) return r.num(); - } - - return NOTFOUND; -} - -int TRelation::name2ind(const char* name) const -{ - const int num = name2log(name); - const int ind = log2ind(num); - return ind; -} - -TLocalisamfile& TRelation::lfile(int logicnum) const -{ - const int idx = log2ind(logicnum); - CHECKD(idx != NOTFOUND, "File not found n. ", logicnum); - return (TLocalisamfile&)_files[idx]; -} - -TLocalisamfile& TRelation::lfile(const char* name) const -{ - const int idx = name2ind(name); - CHECKS(idx != NOTFOUND, "File or Table not found:", name); - return (TLocalisamfile&)_files[idx]; -} - -void TRelation::write_enable(int logicnum, const bool on) - -{ - if (logicnum == 0) - { - for (int i = 0; i < _reldefs.items(); i++) - reldef(i).write_enable(on); - } - else - { - const int idx = log2ind(logicnum); - CHECKD(idx > 0, "File not found n. ", logicnum); - reldef(idx-1).write_enable(on); - } -} - -void TRelation::write_enable(const char* name, const bool on) - -{ - const int idx = name2ind(name); - CHECKS(idx > 0, "File or Table not found:", name); - reldef(idx-1).write_enable(on); -} - - -bool TRelation::add(TLocalisamfile* f, const char* relexprs, int key, - int linkto, int alias, bool allow_lock) -{ - const int idxto = log2ind(linkto); - if (idxto == NOTFOUND) - fatal_box("Can't join file %d to %d", f->num(), linkto); - - const int idx = _files.add(f); - - CHECK(relexprs && *relexprs, "Mancano le espressioni di collegamento"); - - TRelationdef* r = new TRelationdef(this, idx, key, idxto, - relexprs, alias, allow_lock); - _reldefs.add(r); - - return _errors; -} - - -bool TRelation::add(int logicnum, const char* relexprs, int key, - int linkto, int alias, bool allow_lock) -{ - TLocalisamfile* f = new TLocalisamfile(logicnum, FALSE); - return add(f, relexprs, key, linkto, alias, allow_lock); -} - -bool TRelation::add(const char* tabname, const char* relexprs, int key, - int linkto, int alias, bool allow_lock) -{ - TTable* t = new TTable(tabname, FALSE); - return add(t, relexprs, key, linkto, alias, allow_lock); -} - -void TRelation::replace(TLocalisamfile* f, int index) -{ - const TLocalisamfile* p = (const TLocalisamfile*)_files.objptr(index); - CHECK(p && p->num() == f->num(), "Can't replace a file with different logic number"); - _files.add(f, index); -} - -TRectype& TRelationdef::load_rec(TRectype& r, const TBaseisamfile& from) const -{ - r.zero(); - - for (int j = 0 ; j < _fields.items(); j++) // for each field - { - TFieldref& s = (TFieldref&) _fields[j]; - s.write(s.read(from.curr()),r); - } - - return r; -} - - -void TRelation::zero() -{ - for (int i = 0; i < _files.items(); i++) - file(i).zero(); -} - -int TRelation::position_rels(TIsamop op, TReclock lockop, - TDate& atdate, int first) -{ - _errors = NOERR; - - // workhorse: position files for each active relation - for (int i = first; i < _reldefs.items(); i++) - { - TRelationdef& rd = reldef(i); - TLocalisamfile& from = file(rd.num()); - TLocalisamfile& to = file(rd.link()); - TReclock lck = rd._allow_lock ? lockop : _nolock; - - if (to.curr().empty()) - { from.zero(); continue; } - - from.setkey(rd._key); - from.curr().zero(); - - // build record - if (rd._fields.items() && rd._exprs.items()) - { - for (int j = 0 ; j < rd._fields.items(); j++) // for each field - { - /* - TExpression& expr = (TExpression&)rd._exprs[j]; - for (int k = 0; k < expr.numvar(); k++) - expr.setvar(k, to.get(expr.varname(k))); - */ - const char* expr = rd.evaluate_expr(j, to); - TFieldref& s = (TFieldref&) rd._fields[j]; - s.write(expr, from.curr()); - } // for each field - } - - // read record: if not found, zero current record - const TRectype rec(from.curr()); - from.read(op, lck, atdate); - if (from.bad()) - { - rd._first_match = (from.curr() == rec); - if (rd._first_match) - { - bool eq = TRUE; - - for (int kk = 0; eq && kk < rd._fields.items(); kk++) - { - if (rd._forced[kk]) - { - TFieldref& fl = (TFieldref&)rd._fields[kk]; - const TString f_fr(fl.read(from.curr())); - eq = (f_fr == rd.evaluate_expr(kk, to)); - } - } - if (eq) from.setstatus(NOERR); - else { rd._first_match = FALSE; from.curr().zero(); } - } - else from.curr().zero(); - } - else rd._first_match = TRUE; - } // for each relation - return _errors; -} - -bool TRelation::next_match(int logicnum, const char* fieldlist, int nkey) -{ - if (logicnum == file().num()) - { - next(); - return file().good(); - } - - const int i = log2ind(logicnum); - CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum); - - for (int j = 0; j < _reldefs.items(); j++) - if (reldef(j).num() == i) break; - - TLocalisamfile& from = file(i); - TLocalisamfile& to = file(reldef(j).link()); - - reldef(j)._first_match = FALSE; - - if (from.bad()) - return FALSE; // && vaffanculo(); - - const TRecnotype last = from.recno(); - - bool ok = TRUE; // Corrispondenza trovata ? - - if (fieldlist == NULL) - { - TRectype rec(from.curr()); - reldef(j).load_rec(rec, from); - from.setkey(reldef(j)._key); - from.next(); - - ok = (from.good() && from.curr() == rec); - for (int kk = 0; ok && kk < reldef(j)._fields.items(); kk++) - { - if (reldef(j)._forced[kk]) - { - TFieldref& fl = (TFieldref&)reldef(j)._fields[kk]; - const TString f_fr(fl.read(from.curr())); - const TString f_to(fl.read(to.curr())); - - ok = (f_fr == f_to); - } - } - if (ok) from.setstatus(NOERR); - } - else - { - if (nkey > 0) - from.setkey(nkey); - TToken_string fields(fieldlist); - TToken_string old(80); - for (const char* f = fields.get(0); f; f = fields.get()) - old.add(from.curr().get(f)); - - from.next(); - - old.restart(); - - - for (f = fields.get(0); f && ok; f = fields.get()) - { - const TFixed_string v(from.curr().get(f)); - const char* o = old.get(); - if (v != o) - ok = FALSE; - } - } - - if (!ok) - { - from.readat(last); - return FALSE; - } - - // Riposiziona gli eventuali files successivi - position_rels(_isequal, _nolock, (TDate&)botime, j+1); - reldef(j)._first_match = FALSE; - - return ok; -} - -bool TRelation::is_first_match(int logicnum) - // TRUE se c'e' un record ed e' il primo match (non si e' mai fatta - // position_rels) -{ - const int i = log2ind(logicnum); - CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum); - - for (int j = 0; j < _reldefs.items(); j++) - if (reldef(j).num() == i) return reldef(j)._first_match; - const int err = file().status(); - return err == NOERR; -} - - -#ifdef DBG - -bool TRelation::isconsistent(bool reset) -{ - const int MAXREL = 24; - int bad = 0; - TRecnotype recnos[MAXREL]; - - for (int i = 0; i < _files.items() && i < MAXREL; i++) - { - // must be file OK, non-empty record - bad |= file(i).good() || file(i).curr().empty(); - recnos[i] = file(i).recno(); - } - - // if the above hold, check consistency - if (bad) - return _errors = bad; - - position_rels(_isequal, _nolock, (TDate&)botime); - for (i = 0; i < _files.items(); i++) - if (file(i).recno() != recnos[i]) - { - bad = -1; - break; - } - - if (reset == FALSE) - // leave as before - for(i = 0; i < _files.items(); i++) - file(i).readat(recnos[i]); - - return _errors = bad; -} - -#endif - - -int TRelation::write(bool force, TDate& atdate) -{ - _errors = file(0).write(atdate); - if (_errors != NOERR) - return _errors; - - for (int i = 0; i < _reldefs.items(); i++) - { - TRelationdef& rd = reldef(i); - TLocalisamfile& lf = file(rd.num()); - - if (!rd.write_enable() || lf.curr().empty()) - continue; - - int res = lf.write(atdate); - if (force && res == _isreinsert) - res = lf.rewrite(atdate); - if (_errors == NOERR) - _errors = res; - } - - return _errors; -} - -int TRelation::rewrite(bool force, TDate& atdate) -{ - _errors = file(0).rewrite(atdate); // Riscrive testata - if (force && _errors == _iskeynotfound) // Se non la trova ... - _errors = file(0).write(atdate); // ... forza la scrittura - - for (int i = 0; i < _reldefs.items(); i++) - { - TRelationdef& rd = reldef(i); - TLocalisamfile& lf = file(rd.num()); - - if (!rd.write_enable() || lf.curr().empty()) - continue; - - int res = lf.rewrite(atdate); - if (force && res == _iskeynotfound) - res = lf.write(atdate); - if (_errors == NOERR) _errors = res; - } - - return _errors; -} - -int TRelation::remove(TDate& atdate) -{ - const int res = file(0).remove(atdate); - if (_errors == NOERR && res != _iskeynotfound) _errors = res; - for (int i = 0; i < _reldefs.items(); i++) - { - TRelationdef& rd = reldef(i); - const int log = rd.num(); - - if (!rd.write_enable() || - (file(log).curr()).empty()) continue; - - const int res = file(log).remove(atdate); - if (_errors == NOERR && res != _iskeynotfound) _errors = res; - } - return _errors; -} - - -/////////////////////////////////////////////////////////// -// TCursor -/////////////////////////////////////////////////////////// - - -HIDDEN bool __evalcondition(const TRelation* r,TExpression* cond) - -{ - for (int i = 0; i < cond->numvar(); i++) - { - const char* s = cond->varname(i); - TFieldref f(s,0); - - cond->setvar(i, f.read(r)); - } - return (bool) *cond; -} - -FILE* TCursor::open_index(bool create) -{ -#if XVT_OS == XVT_OS_SCOUNIX - const char* const r = "r"; - const char* const w = "w"; -#else - const char* const r = "rb"; - const char* const w = "wb"; -#endif - - if (create && _indexname.empty()) _indexname.temp("ci$$"); - FILE* f = fopen(_indexname, create ? w : r); - if (f == NULL) - fatal_box("Can't use cursor index for file %d: '%s'\n", - file().filehnd()->ln, (const char*)_indexname); - - return f; -} - -TRecnotype TCursor::buildcursor(TRecnotype rp) -{ - - TRecnotype oldrecno=0,pos,ap = 0; - int junk, l, pagecnt = 0; - const bool filtered = has_filter(); - - FILE* _f = open_index(TRUE); - - if (DB_reccount(file().filehnd()->fhnd) == 0) - { - fclose(_f); - return 0; - } - - fseek(_f, 0L, SEEK_SET); - - l = strlen(to()); - junk=DB_index_seek(file().filehnd()->fhnd, (char*)(const char*) from()); - if (junk < 0) junk=get_error(junk); - if (junk == _iseof) return 0; - - TRecnotype* page = new TRecnotype [CMAXELPAGE]; - pos = DB_index_recno(file().filehnd()->fhnd); - _pos=-1; - - while (TRUE) - { - if (DB_index_eof(file().filehnd()->fhnd)) break; - const char* s0 = DB_index_getkey(file().filehnd()->fhnd); - const TRecnotype recno = DB_index_recno(file().filehnd()->fhnd); - if (l && (strncmp(to(), s0, l) < 0)) break; - if (recno == oldrecno) break; // means that no more keys are available - oldrecno=recno; - if (pagecnt == CMAXELPAGE) - { - if (filtered) pagecnt = filtercursor(pagecnt,page); - fwrite(page,sizeof(TRecnotype),pagecnt,_f); - for (int i= 0; i< pagecnt; i++) - if (page[i] == rp) - { - _pos = ap + i; - break; - } - ap += pagecnt; - pagecnt = 0; - } - page[pagecnt++] = recno; - DB_index_next(file().filehnd()->fhnd); - int rt=get_error(-1); - if (rt != NOERR) - fatal_box("Can't read index n. %d - file n. %d",DB_tagget(file().filehnd()->fhnd),file().filehnd()->ln); - } // while - if (pagecnt) - { - if (filtered) pagecnt = filtercursor(pagecnt, page); - fwrite(page, sizeof(TRecnotype), pagecnt, _f); - for (int i = 0; i < pagecnt; i++) - if (page[i] == rp) - { - _pos = ap + i; - break; - } - ap += pagecnt; - } - if (_pos == -1) pos=0; - delete page; - fclose (_f); - return ap; -} - - -int TCursor::filtercursor(int pagecnt, TRecnotype* page) - -{ - int np=0, handle=file().filehnd()->fhnd; - TRectype& rec=file().curr(); - for (int i=0; i< pagecnt; i++) - { - file().readat(rec,page[i]); - if (update_relation()) _if->update(); - if ((_filterfunction ? _filterfunction(_if) : TRUE ) && - (_fexpr ? __evalcondition(_if, _fexpr) : TRUE)) - { - if (np < i) page[np] = page[i]; - np++; - } - } - return np; -} - - -bool TCursor::ok() const - -{ - if (file().bad()) return FALSE; - - const TRectype& rec = file().curr(); - TString key(rec.key(_nkey)), kf(from()), kt(to()); - if (file().tab()) - { - key.ltrim(3); - kf.ltrim(3); - kt.ltrim(3); - } - - if (key < kf || (kt.not_empty() && kt < key.left(kt.len()))) - return FALSE; - if (_filter_update || _filterfunction_update) _if->update(); - if ((_filterfunction ? _filterfunction(_if) : TRUE ) && - (_fexpr ? __evalcondition(_if, _fexpr) : TRUE)) - return TRUE; - return FALSE; -} - -bool TCursor::changed() - -{ - isdef* fh = file().filehnd(); - - if (_frozen && _lastrec > 0L) return _filename != fh->d->SysName; - - const TRecnotype eod = DB_reccount(fh->fhnd); - - if (_lastrec != eod || - (_lastkrec != DB_changed(fh->fhnd)) || - (_filename != fh->d->SysName)) - { - // _lastrec = eod; - // _filename = fh->d->SysName; - // _lastkrec = DB_changed(fh->fhnd); - return TRUE; - } - else return FALSE; -} - -TRecnotype TCursor::update() - -{ - main_app().begin_wait(); - - file().setkey(_nkey); - if (file().curr().empty()) file().curr().zero(); - file().read(_isgteq); - const TRecnotype totrec = buildcursor(file().recno()); - - main_app().end_wait(); - - isdef* fh = file().filehnd(); - const TRecnotype eod = DB_reccount(fh->fhnd); - - _lastrec = eod; - _filename = fh->d->SysName; - _lastkrec = DB_changed(fh->fhnd); - - return totrec; -} - - -void TCursor::filter(const char* fil, const TRectype *from, - const TRectype* to) - -{ - CHECK(!_frozen, "Impossibile filtrare un cursore congelato"); - - TString kf(_keyfrom), kto(_keyto); - - const bool filterchanged = (fil != NULL) && (_filter != fil); - - if (file().tab()) - { - TTable& f = (TTable&) file(); - kf = kto = f.name(); - } - if (from != NULL) - { - kf = from->key(_nkey); - int p; - while ((p = kf.find('~')) != -1) kf[p] = ' '; - } - if (to != NULL) - { - kto = to->key(_nkey); - int p; - while ((p = kto.find('~')) != -1) kto[p] = ' '; - } - - if (filterchanged || (_keyfrom != kf) || (_keyto != kto)) - { - _pos = 0; - _totrec = 0; - _lastrec = 0; - if (filterchanged) - { - _filter = fil; - if (_fexpr) delete _fexpr; - TTypeexp type = (_filter.find('"') != -1) ? _strexpr : _numexpr; - if (_filter.not_empty()) - { - _fexpr = new TExpression(_filter, type); - if (_fexpr->type() == _numexpr) - for (int i = 0 ; i < _fexpr->numvar(); i++) - if (file().curr().type(_fexpr->varname(i)) == _alfafld) - { - _fexpr->set_type(_strexpr); - break; - } - } - else _fexpr = NULL; - } - _keyfrom = kf; - _keyto = kto; - } -} - -void TCursor::setkey(int nkey) - -{ - if (nkey != _nkey) - { - _lastrec = 0L; - _nkey = nkey; - file().setkey(_nkey); - filter(NULL); - } -} - - -TRecnotype TCursor::read(TIsamop op, TReclock lockop, TDate& atdate) -{ - TRecnotype *page; - int pagecnt; - - file().setkey(_nkey); - const bool approx = (op == _isgteq); - - _if->file().read(op, lockop, atdate); - if (approx) - { - while (_if->file().good() && !ok()) - _if->file().next(); - } - const TRecnotype curpos = file().recno(); - - if (changed()) - _totrec = update(); - if (approx && _if->file().status() == _iseof) - { - _pos = _totrec - 1; - return _pos; - } - - FILE* _f = open_index(); - - if (fseek(_f, 0L, SEEK_SET) != 0) - fatal_box("Can't repos cursor : File %d\n", file().filehnd()->ln); - - page = new TRecnotype [CMAXELPAGE]; - _pos = -1; - - for (TRecnotype max = _totrec; _pos == -1 && max > 0; max -= pagecnt) - { - pagecnt = (max < CMAXELPAGE) ? (int)max : CMAXELPAGE; - fread(page, sizeof(TRecnotype), pagecnt, _f); - for (int i = 0; i < pagecnt; i++) - if (page[i] == curpos) - { - _pos = _totrec - max + i; - break; - - } - } - if (_pos == -1) _pos = 0; - delete page; - fclose(_f); - - readrec(); - return _pos; -} - - -TCursor::TCursor(TRelation* r, const char* fil, int nkey, - const TRectype *from, const TRectype* to) -: _if(r), _nkey(nkey), _frozen(FALSE), _filterfunction(NULL), _fexpr(NULL), _filter_update(FALSE), _filterfunction_update(FALSE) -{ - file().setkey(_nkey); - _pos = 0; - _totrec = 0; - _lastrec = 0; - _lastkrec = 0; - filter(fil, from, to); -} - - -TCursor::~TCursor() - -{ - if (_indexname.not_empty()) - ::remove(_indexname); - if (_fexpr) - delete _fexpr; -} - - -TRecnotype TCursor::readrec() - -{ - TRecnotype& nrec = file().filehnd()->RecNo; - - if (_pos == items()) - { - file().setstatus(_iseof); - curr().zero(); - return nrec = 0L; - } - file().setstatus(NOERR); - - FILE* _f = open_index(); - - if (fseek(_f, _pos * sizeof(TRecnotype), SEEK_SET) != 0) - fatal_box("Can't seek position %ld in cursor n. %d\n", _pos, file().filehnd()->ln); - - if (fread(&nrec, sizeof(TRecnotype), 1, _f) != 1) - fatal_box("Can't read position %ld in cursor n. %d\n", _pos, file().filehnd()->ln); - - fclose(_f); - - curr().setdirty(); - file().readat(nrec); - repos(); - return nrec; -} - -int TCursor::lock(TReclock l) -{ - int rt=NOERR; - switch(l) - { - case _lock: - rt=DB_lock_rec(file().filehnd()->fhnd,_pos); - break; - case _unlock: - rt=DB_unlock(file().filehnd()->fhnd); - break; - default: - break; - } - if (rt != NOERR) rt=get_error(rt); - return(rt); -} - -TRecnotype TCursor::operator =(const TRecnotype pos) -{ - if (changed()) - _totrec = update(); - CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos); - _pos = pos; - readrec(); - return _pos; -} - -TRecnotype TCursor::operator +=(const TRecnotype npos) -{ - if (changed()) - _totrec = update(); - - _pos += npos; - if (_pos > _totrec) _pos = _totrec; - else - if (_pos < 0) _pos = 0; - readrec(); - - return _pos; -} - - -TRecnotype TCursor::items() -{ - if (changed()) - _totrec = update(); - return _totrec; -} - -bool TCursor::next_match(int lognum, const char* fl, int nk) -{ - if (lognum == 0 || lognum == file().num()) - {++(*this); return file().good(); } - else return _if->next_match(lognum, fl, nk); -} - -bool TCursor::is_first_match(int ln) -{ - return (ln == 0 || ln == file().num()) ? - (_pos == 0 && file().good()) : (_if->is_first_match(ln)); -} - - -/////////////////////////////////////////////////////////// -// TSorted_cursor -/////////////////////////////////////////////////////////// - -typedef struct { - char f[512]; - TRecnotype p; -} El_To_Sort; - -bool TSorted_cursor::check_expr(TString& s) -// Controlla la validita' dell'espressione ritornando un bool -// Modifica l'espressione in modo da avere una valido parametro per TFieldref! -// Es. UPPER(20->RAGSOC[1,40]+) ==> 20->RAGSOC[1,40]+ -// N.B. : la direzione dell'ordinamento va messa indicata sempre dopo la definizione del campo, mai -// dopo l'operatore UPPER(). E' a cura delle altre funzioni esterne che utilizzano la check_expr() -// discriminare il segno + o - che sia -{ - int p=s.find('('); - int p1=s.find(')'); - int rt=TRUE; - - if (p>-1 && p1>-1) - if (s.find("UPPER") == -1) - { - error_box("Only UPPER() operator is allowed"); - rt=FALSE; - } - else - { - s=s.mid(6,s.len()-7); - p=s.find('('); - p1=s.find(')'); - if (p>-1 || p1 >-1) - { - error_box("Too many parentheses!"); - rt=FALSE; - } - } - else - if ((p>-1 && p1==-1) || (p==-1 && p1>-1)) - { - error_box("Unbalanced parentheses in expression"); - rt=FALSE; - } - return rt; -} - -bool TSorted_cursor::is_upper(TString& s) -// Controlla se nell'espressione esiste l'operatore UPPER() -{ - bool rt=TRUE; - if (s.find("UPPER")>-1) - rt=check_expr(s); - else - rt=FALSE; - return rt; -} - -TRecnotype TSorted_cursor::buildcursor(TRecnotype rp) -{ - TRecnotype oldrecno=0,pos,ap = 0; - int abspos=0,junk, l, pagecnt = 0; - const bool filtered = has_filter(); - TString s; - El_To_Sort * Element; - - _order_expr.restart(); - while ((s=_order_expr.get()) && s.not_empty()) - { - check_expr(s); // Toglie l'eventuale operatore UPPER(), in modo che l'ultimo carattere - // indichi eventualmente la direzione dell'ordinamento - char versus = (s.right(1)=="-") ? 'd' : 'a'; - if (s.right(1) == "-" || s.right(1) == "+") - s.cut(s.len()-1); - TFieldref f(s,0); - // Il controllo del file e' automatico in f.len() - _sort->addsortkey(abspos+f.from(),f.len(relation()->curr()),versus); - CHECKS(f.len(relation()->curr())!=0,"Field can not have null length: ",(const char *) s); - int lf = (f.file()!=0 ? f.file() : file().num()); - TRectype r(lf); - abspos+=r.length(f.name()); - CHECKD(abspos<=512,"Cannot exceed 512 bytes-key %d",abspos); - } - - _sort->init(); - - FILE* _f = open_index(TRUE); - - if (DB_reccount(file().filehnd()->fhnd) == 0) - { - fclose(_f); - return 0; - } - - fseek(_f, 0L, SEEK_SET); - - l = strlen(to()); - junk=DB_index_seek(file().filehnd()->fhnd, (char*)(const char*) from()); - if (junk < 0) junk=get_error(junk); - if (junk == _iseof) return 0; - - TRecnotype* page = new TRecnotype [CMAXELPAGE]; - pos = DB_index_recno(file().filehnd()->fhnd); - TCursor::pos()=-1; - - while (TRUE) - { - if (DB_index_eof(file().filehnd()->fhnd)) break; - const char* s0 = DB_index_getkey(file().filehnd()->fhnd); - const TRecnotype recno = DB_index_recno(file().filehnd()->fhnd); - if (l && (strncmp(to(), s0, l) < 0)) break; - if (recno == oldrecno) break; // means that no more keys are available - oldrecno=recno; - if (pagecnt == CMAXELPAGE) - { - pagecnt = filtercursor(pagecnt,page); - for (int i= 0; i< pagecnt; i++) - if (page[i] == rp) - { - TCursor::pos() = ap + i; - break; - } - ap += pagecnt; - pagecnt = 0; - } - page[pagecnt++] = recno; - DB_index_next(file().filehnd()->fhnd); - int rt=get_error(-1); - if (rt != NOERR) - fatal_box("Can't read index n. %d - file n. %d",DB_tagget(file().filehnd()->fhnd),file().filehnd()->ln); - } // while - if (pagecnt) - { - pagecnt = filtercursor(pagecnt, page); - for (int i = 0; i < pagecnt; i++) - if (page[i] == rp) - { - TCursor::pos() = ap + i; - break; - } - ap += pagecnt; - } - _sort->endsort(); - pagecnt=0; - while ((Element=(El_To_Sort *)_sort->retrieve()) != NULL) - { - page[pagecnt++]=Element->p; - if (pagecnt==CMAXELPAGE) - { - fwrite(page,sizeof(TRecnotype),pagecnt,_f); - pagecnt=0; - } - } - if (pagecnt) - fwrite(page,sizeof(TRecnotype),pagecnt,_f); - if (TCursor::pos() == -1) pos=0; - delete page; - fclose (_f); - return ap; -} - -int TSorted_cursor::filtercursor(int pagecnt, TRecnotype* page) -{ - - int np=0, handle=file().filehnd()->fhnd; - TRectype& rec=file().curr(); - TString s; - El_To_Sort Element; - - for (int i=0; i< pagecnt; i++) - { - file().readat(rec,page[i]); - if (update_relation()) relation()->update(); - if ((filterfunction() ? filterfunction()(relation()) : TRUE ) && - (expression() ? __evalcondition(relation(), expression()) : TRUE)) - { - if (np < i) page[np] = page[i]; - np++; - - _order_expr.restart(); - strcpy(Element.f,""); - while ((s=_order_expr.get()) && s.not_empty()) - { - bool is_up=is_upper(s); - int p=s.find('[');// Qui estrae il nome del campo: - if (p>-1) // rimane solo l'indicazione del file (eventuale) - s.cut(p); // ed il nome del campo: - else // UPPER(20->RAGSOC[1,30]+) ===>> 20->RAGSOC - if (s.right(1) == "-" || s.right(1) == "+") - s.cut(s.len()-1); - TFieldref f(s,0); - TString sf=f.read(relation()); - bool is_date = file(f.file()).curr().type(s) ==_datefld; - if (is_date) // Se il campo e' di tipo data, la converte in ANSI! - { - TDate d(sf); - sf=d.string(ANSI); - } - if (is_up) sf.upper(); - TString fmt; - fmt.format("%%-%ds",f.len(rec)); - strcat(Element.f,(const char*)sf.format((const char *)fmt,(const char *)sf)); - } - Element.p=page[i]; - _sort->sort((char *) &Element); - } - } - - return np; -} - -bool TSorted_cursor::changed() -{ - bool rt; - - if (_is_valid_expr) - { - rt = TCursor::changed(); - if (!rt) rt=_is_changed_expr; - _is_changed_expr = FALSE; - } else - { - error_box("Can't perform changed() while expression ain't valid!"); - rt=FALSE; - } - return rt; -} - - -void TSorted_cursor::change_order(const char* order_expr) -{ - if (order_expr && *order_expr && order_expr != _order_expr) - { - TString s; - _order_expr = order_expr; - _order_expr.restart(); - while ((s=_order_expr.get()) && s.not_empty() && (_is_valid_expr=check_expr(s))) ; - if (_is_valid_expr) - _is_changed_expr=TRUE; - } -} - -TSorted_cursor::TSorted_cursor(TRelation *f, const char * order_expr, const char * filter, int key, const TRectype* from, const TRectype* to) -: TCursor(f,filter,key,from,to) -{ - change_order(order_expr); - _sort = new TSort(sizeof(El_To_Sort)); -} - -TSorted_cursor::~TSorted_cursor() -{ - if (_sort) delete _sort; -} - - - -/////////////////////////////////////////////////////////// -// TFieldRef -/////////////////////////////////////////////////////////// - -int name2log(const char* name) -{ - int log = 0; - - if (name) - while (*name == ' ') name++; - - if (name && *name) - { - if (isdigit(*name) || *name == '-') - { - log = atoi(name); - if (strchr(name, '@')) log = -log; - } - else - { - const int len = strlen(name); - if (len == 3 || len == 4) - log = TTable::name2log(name); - } - - CHECKS(log != 0, "Non e' un file ne' una tabella: ", name); - } - - return log; -} - - -TFieldref::TFieldref() : _fileid(0), _name(0) {} - - -TFieldref::TFieldref(const TString& s, short defid) -{ - operator=(s); - if (_fileid == 0) _fileid = defid; -} - - -// A Fieldref should have the following format (only NAME is mandatory): -// FILE->NAME[FROM,TO] -TFieldref& TFieldref::operator =(const TString& s) -{ - int pos = s.find("->"); - if (pos > 0) - { - _id = s.left(pos); _id.strip(" "); - _fileid = name2log(_id); - pos += 2; - } - else - { - _id.cut(0); - _fileid = pos = 0; - } - - int par = s.find('[', pos); - _name = s.sub(pos, par); _name.strip(" "); - - if (par > 0) - { - pos = par+1; - set_from(atoi(s.mid(pos))); - par = s.find(',', pos); - if (par > 0) set_to(atoi(s.mid(par+1))); - else set_to(from()+1); - } - else - { - set_from(0); - set_to(-1); - } - - return *this; -} - -void TFieldref::set_file(int f) -{ - _id.cut(0); - if (f != 0) - { - _fileid = f; - _id << abs(f); - if (f < 0) _id << '@'; - } - else _fileid = 0; -} - -void TFieldref::print_on(ostream& out) const -{ - if (_id.not_empty()) out << _id << "->"; - out << _name; - if (_from > 0 || _to > 0) - { - out << '[' << (_from+1); - out << ',' << _to << ']'; - } -} - -const char* TFieldref::read(const TRectype& rec) const -{ - static TString256 buffer; - - if (_fileid >= CNF_GENERAL) - { - TToken_string s(_name, '.'); - TConfig c(_fileid - CNF_STUDIO, s.get()); - buffer = c.get(s.get()); - } - else - { - buffer = rec.get(_name); - if (_from > 0 || _to > 0) - { - const int l = buffer.len(); - if (_to < l && _to > 0) buffer.cut(_to); - if (_from > 0) buffer.ltrim(_from); - } - } - return buffer; -} - -const char* TFieldref::read(const TRelation* c) const -{ - CHECK(c != NULL, "Can't read field from NULL relation"); - const char * s = read(c->lfile(_id).curr()); - return s; -} - -void TFieldref::write(const char* val, TRelation* c) const -{ - CHECK(c != NULL, "Can't write field on NULL relation"); - TRectype &curr = c->lfile(_id).curr(); - write(val, curr); -} - - -void TFieldref::write(const char* val, TRectype& rec) const -{ - if (_fileid >= CNF_GENERAL) return; - if (_from > 0) - { - TString256 campo(rec.get(_name)); - campo.overwrite(val, _from); - rec.put(_name, campo); - } - else - rec.put(_name, val); -} - -int TFieldref::len(TRectype &rec) const -{ - if (_to >= 0) - return _to - _from; - if (_fileid == 0) - return rec.length(_name) - _from; - - const TRectype r(_fileid); - const int len = r.length(_name); - return len - _from; -} - -/////////////////////////////////////////////////////////// -// TRecord_Array -/////////////////////////////////////////////////////////// - -TRecord_array::TRecord_array(const TRectype& r, const char* numfield, int first) -: _file(r.num()), _num(numfield), _offset(first - 1) -{ - read(r); -} - -TRecord_array::TRecord_array(int logicnum, const char* numfield, int first) -: _file(logicnum), _num(numfield), _offset(first - 1) -{ - add(new TRectype(logicnum)); -} - -TRecord_array::TRecord_array(const TRecord_array& a) -: TArray(a), _file(a._file), _offset(a._offset), _num(a._num) -{} - -const TRectype& TRecord_array::key() const -{ - TRectype* r = (TRectype*)objptr(0); - CHECK(r, "TRecord_array lost its key"); - return *r; -} - -bool TRecord_array::exist(int n) const -{ - const int i = n > 0 ? n - _offset : -1; - TRectype* r = (TRectype*)objptr(i); - return r != NULL; -} - - -TRectype& TRecord_array::row(int n, bool create) -{ - const int i = n >= 0 ? n - _offset : -1; - TRectype* r = (TRectype*)objptr(i); - if (r == NULL && create) - { - r = (TRectype*)key().dup(); // Crea nuovo record copiando la chiave - n = add(r, i) + _offset; // Riassegna n se era negativo! - r->put(_num, n); // Aggiorna campo numero riga - } - CHECKD(r && n > 0, "Bad record number ", n); - return *r; -} - -bool TRecord_array::renum_key(const char* field, const TString& num) -{ - CHECKS(!num.blank(), "Blank key value for field: ", field); - const TString& curr = key().get(field); - - if (curr == num) - return FALSE; - - for (int i = last(); i >= 0; i--) - { - TRectype* r = (TRectype*)objptr(i); - if (r == NULL) - r->put(field, num); - } - - return TRUE; -} - -bool TRecord_array::renum_key(const char* field, long num) -{ - CHECKS(num > 0, "Null key value for field: ", field); - TString16 n; n << num; - return renum_key(field, n); -} - - -int TRecord_array::rec2row(const TRectype& r) const -{ - CHECK(r.num() == key().num(), "Incompatible record"); - const int n = atoi(r.get(_num)) - _offset; // Non e' detto che sia un int! - CHECKD(n >= 0 && n < 30000, "Bad line number in record ", n + _offset); - return n; -} - -int TRecord_array::add_row(TRectype* r) -{ - const int nr = rec2row(*r); - add(r, nr); - if (nr == 0 && rows() > 0) // Se ho cambiato il record campione - { // e ci sono altre righe ... - for (int f = r->items()-1; f >= 0; f--) - { - const char* fn = r->fieldname(f); - const TString& v = r->get(fn); - if (v.not_empty()) renum_key(fn, v); // ... aggiorna tutte le righe in base - } // ai campi non vuoti del campione - } - return nr; -} - -bool TRecord_array::destroy_row(int r, bool pack) -{ - CHECKD(r > _offset, "Can't destroy row ", r); - const int index = r - _offset; - const bool ok = destroy(index, pack); - - if (ok && pack) - { - for (int i = index; i < items(); i++) - row(i + _offset, FALSE).put(_num, i + _offset); - } - return ok; -} - -void TRecord_array::destroy_rows() -{ - for (int i = last(); i > 0; i--) - destroy(i); -} - -int TRecord_array::read(TRectype* filter) -{ - CHECKD(filter->num() == _file, "Bad key record ", filter->num()); - CHECKS(filter->get(_num).empty(), "You can't specify in the filter the field ", (const char*)_num); - - destroy(); - add(filter, 0); // Store filter record for later use - - int err = NOERR; - if (!filter->empty()) - { - TLocalisamfile f(_file); - TRectype* rec = (TRectype*)filter->dup(); - err = rec->read(f, _isgteq); - for (int e = err; e == NOERR && *rec == key(); e = rec->next(f)) - { - add_row(rec); - rec = (TRectype*)filter->dup(); - } - delete rec; - } - return err; -} - - -int TRecord_array::read(const TRectype& filter) -{ - TRectype* f = (TRectype*)filter.dup(); - return read(f); -} - - - -int TRecord_array::write(bool re) const -{ - int err = NOERR; - - TLocalisamfile f(_file); - - int last_on_file = 0; - - const int u = last(); - for (int i = 1; i <= u; i++) - { - const TRectype* r = (TRectype*)objptr(i); - - if (r != NULL) - { - if (re) - { - err = r->rewrite(f); - if (err == _iskeynotfound || err == _iseof || err == _isemptyfile) - err = r->write(f); - if (err != NOERR) - break; - } - else - { - err = r->write(f); - if (err == _isreinsert) - err = r->rewrite(f); - if (err != NOERR) - break; - } - } - else - { - const int pos = i+_offset; - if (pos > last_on_file) - { - TRectype* rec = (TRectype*)key().dup(); - rec->put(_num, pos); - err = rec->read(f, _isgteq); - if (err == NOERR) // La riga c'era ma ora non piu' - { - last_on_file = atoi(rec->get(_num)); - if (last_on_file == pos) - { - err = (rec->remove(f)); - if (err != NOERR) - { - delete rec; - break; - } - } - } - else - last_on_file = 30000; - delete rec; - } - } - } - - if (re && err == NOERR && last_on_file != 30000) - { - const int pos = i+_offset; - TRectype* rec = (TRectype*)key().dup(); - // Cancella eventuali residui successivi - rec->put(_num, pos); - for (int e = rec->read(f, _isgteq); e == NOERR && *rec == key(); e = rec->next(f)) - { - err = rec->remove(f); - if (err != NOERR) - break; - } - delete rec; - } - - return err; -} - -int TRecord_array::remove() -{ - destroy_rows(); - return rewrite(); -} - -/////////////////////////////////////////////////////////// -// TRelation_description -/////////////////////////////////////////////////////////// - - -void TRelation_description::init_files_array() -{ - const TLocalisamfile& f = _rel->file(); - TToken_string s(128); - print_name(s, f); // Logic number - - const char* name = f.name(); - if (f.tab()) - name = TDir::tab_des(name); - else - name = prefix().description(name); - s.add(name); // Description - s.add(" "); // No join - s.add(f.getkey()); // Key - s.add(" | "); // No Alias nor expression - - _files.destroy(); - _files.add(s); // Main file - for (int i = 0; i < _rel->items(); i++) - { - _rel->reldef(i).print_on(s); - _files.add(s); - } -} - - -void TRelation_description::read_rel() -{ - // scan files and build description arrays - init_files_array(); - - _fields.destroy(); - for (int i = 0; i < _files.items(); i++) - { - TToken_string& tt = (TToken_string&)_files[i]; - TFilename descfname; descfname << DESCDIR << "/d"; - - const char* tn = tt.get(0); // Codice del file o tabella - - int which_file = atoi(tn); // Non spostare questa riga (tn puo' cambiare!) - if (which_file == 0) - which_file = TTable::name2log(tn); // Numero logico del file - - if (tn[0] == '%' || tn[0] == '$') - descfname << (tn+1); - else - descfname << tn; - descfname << ".des"; - - TConfig conf(descfname, DESCPAR); - // new record descriptor for _fields array - TString_array* rdesc = new TString_array; - - TTrec trec; trec.get(which_file); - TToken_string ttmp(64); - - for (int f = 0; f < trec.fields(); f++) - { - ttmp = trec.fielddef(f); - const TString16 name(ttmp.get(0)); - if (!name.blank()) - { - TString80 dfld(conf.get(name, NULL, -1, "Missing description")); - if (!dfld.blank() && dfld[0] != '#') - { - ttmp.add(dfld,4); - // contiene: nome campo, tipo, lunghezza, decimali, descrizione - rdesc->add(ttmp); - } - } - } - - _fields.add(rdesc, i); - } -} - -void TRelation_description::change_relation(TRelation& r, TString_array& a) -{ - _rel = &r; - read_rel(); - if (a.items() > 0) - _files = a; -} - -void TRelation_description::print_on(ostream& out) const -{ - for (int i = 0; i < _files.items(); i++) - { - TToken_string& r = ((TRelation_description*)this)->_files.row(i); - TString16 cod(r.get(4)); - if (atoi(cod) == 0) - cod = r.get(0); - else - cod << '@'; - out << " " << cod << "->* "; - out << "\"" << r.get(1) << "\"\n"; - } -} - - -bool TRelation_description::choose_file(int file) -{ - TArray_sheet sht(-1,-1,0,0,"Selezione archivio", "Codice|Descrizione archivio@70"); - TToken_string tt(80); - - int sel = 0; - - for (int i = 0; i < _files.items(); i++) - { - TToken_string& tf = _files.row(i); - tt = tf.get(4); - - int num = atoi(tt); - if (num == 0) - { - tt = tf.get(0); - num = atoi(tt); - if (sel == 0 && file > 0 && num == file) - sel = i; - } - else - { - if (sel == 0 && file < 0 && num == -file) - sel = i; - tt << '@'; - } - - tt.add(tf.get(1)); - sht.add(tt); - } - - sht.select(sel); - - if (sht.run() == K_ENTER) - { - _cur_file = (int)sht.selected(); - _cfile = _files.row(_cur_file); - return TRUE; - } - return FALSE; -} - -bool TRelation_description::choose_field(const char* fld) -{ - TArray_sheet sht(-1,-1,0,0,"Selezione campo", - "Campo@10|Descrizione@50|Tipo@10|Dim."); - TString_array& fd = (TString_array&)_fields[_cur_file]; - TToken_string tt(80); - - int sel = 0; - for (int i = 0; i < fd.items(); i++) - { - TToken_string& tf = fd.row(i); - tt = tf.get(0); - if (sel == 0 && tt == fld) - sel = i; - tt.add(tf.get(4)); - tt.add(field_type_str((TFieldtypes)tf.get_int(1))); - tt.add(tf.get(2)); - sht.add(tt); - } - - sht.select(sel); - if (sht.run() == K_ENTER) - { - _cur_field = (int)sht.selected(); - _cfield = fd.row(_cur_field); - return TRUE; - } - return FALSE; -} - -bool TRelation_description::build_menu(const char* title) -{ - return FALSE; -} - -bool TRelation_description::remove_menu() -{ - return FALSE; -} - -bool TRelation_description::set_cur_file(int id) -{ - const int n = _rel->log2ind(id); - if (n >= 0 && n != _cur_file) - { - _cur_file = n; - _cfile = _files.row(n); - } - return n >= 0; -} - -int TRelation_description::file_num() -{ - int n = -_cfile.get_int(4); - if (n == 0) - n = _cfile.get_int(0); - return n; -} - -const char* TRelation_description::file_desc() -{ - return _cfile.get(1); -} - -void TRelation_description::file_desc(const char* desc) -{ - _cfile.add(desc, 1); - _files.row(_cur_file) = _cfile; -} - - -const char* TRelation_description::field_desc() -{ - return _cfield.get(4); -} - -const char* TRelation_description::field_name() -{ - return _cfield.get(0); -} - -int TRelation_description::field_len() -{ - return _cfield.get_int(2); -} - -TFieldtypes TRelation_description::field_type() -{ - return (TFieldtypes)_cfield.get_int(1); -} - -bool TRelation_description::set_field_description(const char* field, const char* des) -{ - TString_array& fld = (TString_array&)_fields[_cur_file]; - - for (int i = 0; i < fld.items(); i++) - { - TToken_string& tt = fld.row(i); - if (strcmp(tt.get(0),field) == 0) - { - if (des && *des) - tt.add(des, 4); - else - { - fld.destroy(i); - fld.pack(); - } - return TRUE; - } - } - - if (des && *des) - { - TToken_string tt(80); - tt.add(field); - tt.add(des, 4); - fld.add(tt); - return TRUE; - } - - return FALSE; -} - -const char* TRelation_description::get_field_description(const char* field) -{ - TString_array& fld = (TString_array&)_fields[_cur_file]; - - for (int i = 0; i < fld.items(); i++) - { - TToken_string& tt = fld.row(i); - if (strcmp(tt.get(0),field) == 0) - return tt.get(4); - } - return ""; -} - - -TRelation_description::TRelation_description(TRelation& r) -: _rel(&r), _menu(FALSE), _cur_file(0), _cur_field(0), - _cfile(80), _cfield(80) -{ - read_rel(); - if (_files.items() > 0) - { - _cfile = _files.row(0); - _cfield = ""; - } -} - -TRelation_description::~TRelation_description() -{ - if (_menu) remove_menu(); -} - - -// *** EOF relation.cpp - +// $Id: relation.cpp,v 1.59 1995-08-23 14:33:45 guy Exp $ +// relation.cpp +// fv 12/8/93 +// relation class for isam files + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// *** check if not already defined +#define NOTFOUND (-1) + +// *** maximum number of elements in a cursor working page +#define CMAXELPAGE 8000 + + +#define print_name(out, f) out << (f.num() == LF_TABCOM ? "%" : "") << f.name() + +extern int get_error(int); + +HIDDEN const char* field_type_str(TFieldtypes f) +{ + const char* c = ""; + switch(f) + { + case _charfld: + c = "Carattere"; break; + case _realfld: + c = "Reale"; break; + case _datefld: + c = "Data"; break; + case _boolfld: + c = "Logico"; break; + // case _wordfld: + // case _intzerofld: + // case _longzerofld: + // case _intfld: + // case _longfld: + default: + c = "Intero"; break; + } + return c; +} + +/////////////////////////////////////////////////////////// +// TRelationdef +/////////////////////////////////////////////////////////// + +class TRelationdef : public TObject +{ + // @DPRIV + friend class TRelation; + + const TRelation* _rel; // Relazione padre + int _num; // Posizione file + int _numto; // Posizione padre + int _alias; // Alias + byte _key; // Chiave + TArray _fields; // Campi di join + TArray _exprs; // Condizioni di uguaglianza + TArray _altexprs; // Condizioni di uguaglianza alternative + TBit_array _forced; + bool _first_match : 1; // primo match (ed esiste) + bool _allow_lock : 1; // ????? + bool _write_enable : 1; + +public: // TObject + virtual void print_on(ostream& out) const; + +public: + int num() const { return _num; } + int link() const { return _numto; } + int alias() const { return _alias; } + bool write_enable() const { return _write_enable; } + void write_enable(bool we) { _write_enable = we; } + TRectype& load_rec(TRectype& r, const TBaseisamfile& from) const; + const char* evaluate_expr(int j, const TLocalisamfile& to); + + void print_on(TToken_string& out) const; + + TRelationdef(const TRelation* rel, int file, byte key, + int linkto, const char* relexprs, int alias, + bool allow_lock); + virtual ~TRelationdef() {} +}; + + +TRelationdef::TRelationdef(const TRelation* rel, int idx_file, byte key, + int idx_to, const char* relexprs, int alias, + bool allow_lock) +: _num(idx_file), _key(key), _numto(idx_to), + _rel(rel), _fields(4), _exprs(4), _alias(alias), + _first_match(FALSE), _allow_lock(allow_lock), + _write_enable(FALSE) +{ + TToken_string rels(relexprs); + int i = 0; + TString80 r,s; + + for (const char* g = rels.get(); g; g = rels.get(), i++) + { + r = g; + int eq = r.find('='); + + CHECKS(eq > 0, "Ahoo! E l'uguale 'ndo sta? ", (const char*)g); + + s = r.left(eq); // Parte a sinistra dell' = + +#ifdef DBG + const char* n = s; + const int p = s.find('['); + if (p > 0) n = s.left(p); + if (rel->file(_num).curr().exist(n) == FALSE) + { + yesnofatal_box("Errore di JOIN: '%s' non e' un campo del file %d", + n, rel->file(_num).num()); + continue; + } +#endif + + if (r[eq+1] == '=') + { + _forced.set(i); + eq++; + } + + _fields.add(new TFieldref(s, 0)); + + s = r.mid(eq+1); + const int par = s.find('('); + if (par > 0) + { + _exprs.add(new TExpression(s.left(par), _strexpr), i); + _altexprs.add(new TExpression(s.sub(par+1, s.len()-1), _strexpr), i); + } + else + _exprs.add(new TExpression(s, _strexpr), i); + } +} + + +void TRelationdef::print_on(ostream& out) const +{ + const TLocalisamfile& f = _rel->file(_num); + + out << "JOIN "; + print_name(out, f); + + if (_numto > 0) + { + out << " TO "; + const int alias = _rel->reldef(_numto-1).alias(); + if (alias > 0) out << alias << '@'; + else + { + const TLocalisamfile& t = _rel->file(_numto); + print_name(out, t); + } + } + + if (_key > 1) out << " KEY " << (int)_key; + + if (_alias > 0) out << " ALIAS " << _alias; + + for (int i = 0; i < _fields.items(); i++) + { + if (i == 0) out << " INTO"; + out << ' ' << _fields[i] << '='; + if (_forced[i]) out << '='; + out << _exprs[i]; + if (_altexprs.objptr(i)) + out << '(' << _altexprs[i] << ')'; + } +} + + +void TRelationdef::print_on(TToken_string& out) const +{ + const TLocalisamfile& f = _rel->file(_num); + + out = ""; + print_name(out, f); + + // add description + const char* name = f.name(); + if (f.tab()) + name = TDir::tab_des(name); + else + name = prefix().description(name); + out.add(name); + + out << '|'; + + if (_numto > 0) + { + const int alias = _rel->reldef(_numto-1).alias(); + if (alias > 0) + out << alias << '@'; + else + { + const TLocalisamfile& t = _rel->file(_numto); + print_name(out, t); + } + } else out << ' '; + + out.add(_key); + out.add(_alias); + out << '|'; + for (int i = 0; i < _fields.items(); i++) + { + if (i) out << ' '; + out << _fields[i] << '='; + if (_forced[i]) out << '='; + out << _exprs[i]; + if (_altexprs.objptr(i)) + out << '(' << _altexprs[i] << ')'; + } +} + + +const char* TRelationdef::evaluate_expr(int j, const TLocalisamfile& to) +{ + TExpression& expr = (TExpression&)_exprs[j]; + for (int k = 0; k < expr.numvar(); k++) + expr.setvar(k, to.get(expr.varname(k))); + + const char* val = expr; + + if (*val == '\0' && _altexprs.objptr(j)) + { + TExpression& altexpr = (TExpression&)_altexprs[j]; + for (int k = 0; k < expr.numvar(); k++) + altexpr.setvar(k, to.get(altexpr.varname(k))); + val = altexpr; + } + + return val; +} + +/////////////////////////////////////////////////////////// +// TRelation +/////////////////////////////////////////////////////////// + +TRelation::TRelation(int logicnum) +: _files(4), _reldefs(4), _errors(NOERR) +{ + TLocalisamfile* f = new TLocalisamfile(logicnum, FALSE); + _files.add(f); +} + +TRelation::TRelation(const char* tabname) +: _files(4), _reldefs(4), _errors(NOERR) +{ + TTable* t = new TTable(tabname, FALSE); + _files.add(t); +} + +TRelation::TRelation(TLocalisamfile* l) +: _files(4), _reldefs(4), _errors(NOERR) +{ + _files.add(l); +} + +TRelation::~TRelation() +{} + +void TRelation::print_on(ostream& out) const +{ + const TLocalisamfile& f = file(); + + out << "USE "; print_name(out, f); + + const int k = f.getkey(); + if (k > 1) out << " KEY " << k; + out << endl; + + for (int r = 0; r < _reldefs.items(); r++) + out << _reldefs[r] << endl; +} + +void TRelation::restore_status() +{ + _status.restart(); + for (int i = 0; i < _files.items(); i++) + { + const int err = _status.get_int(); + const TRecnotype recno = _status.get_long(); + const int key = _status.get_int(); + + file(i).setkey(key); + if (recno >= 0l) file(i).readat(recno); + else file(i).curr().zero(); + file(i).setstatus(err); + } + for (i = 0; i < _reldefs.items(); i++) + { + const bool first_match = _status.get_int() ? TRUE : FALSE; + reldef(i)._first_match = first_match; + } +} + +void TRelation::save_status() +{ + _status.cut(0); + for (int i = 0; i < _files.items(); i++) + { + const int err = file(i).status(); + const TRecnotype recno = file(i).eof() ? -1l : file(i).recno(); + const int key = file(i).getkey(); + _status.add (err); + _status.add (recno); + _status.add (key); + } + for (i = 0; i < _reldefs.items(); i++) + { + const bool first_match = reldef(i)._first_match; + _status.add(format("%d",first_match)); + } +} + +int TRelation::log2ind(int log) const +{ + // returns _files index of logical number or + // NOTFOUND if not present + // sets error status + + if (log < 0) return alias2ind(-log); + + const int num = _files.items(); + if (log > 0) + { + for (int i = 0; i < num; i++) + if (file(i).num() == log) return i; + } + return num ? 0 : NOTFOUND; +} + +int TRelation::alias2ind(int alias) const +{ + if (alias < 1) return 0; + + for (int i = 0; i < _reldefs.items(); i++) + { + const TRelationdef& r = (const TRelationdef&)_reldefs[i]; + if (r.alias() == alias) return r.num(); + } + + return NOTFOUND; +} + +int TRelation::name2ind(const char* name) const +{ + const int num = name2log(name); + const int ind = log2ind(num); + return ind; +} + +TLocalisamfile& TRelation::lfile(int logicnum) const +{ + const int idx = log2ind(logicnum); + CHECKD(idx != NOTFOUND, "File not found n. ", logicnum); + return (TLocalisamfile&)_files[idx]; +} + +TLocalisamfile& TRelation::lfile(const char* name) const +{ + const int idx = name2ind(name); + CHECKS(idx != NOTFOUND, "File or Table not found:", name); + return (TLocalisamfile&)_files[idx]; +} + +void TRelation::write_enable(int logicnum, const bool on) + +{ + if (logicnum == 0) + { + for (int i = 0; i < _reldefs.items(); i++) + reldef(i).write_enable(on); + } + else + { + const int idx = log2ind(logicnum); + CHECKD(idx > 0, "File not found n. ", logicnum); + reldef(idx-1).write_enable(on); + } +} + +void TRelation::write_enable(const char* name, const bool on) + +{ + const int idx = name2ind(name); + CHECKS(idx > 0, "File or Table not found:", name); + reldef(idx-1).write_enable(on); +} + + +bool TRelation::add(TLocalisamfile* f, const char* relexprs, int key, + int linkto, int alias, bool allow_lock) +{ + const int idxto = log2ind(linkto); + if (idxto == NOTFOUND) + fatal_box("Can't join file %d to %d", f->num(), linkto); + + const int idx = _files.add(f); + + CHECK(relexprs && *relexprs, "Mancano le espressioni di collegamento"); + + TRelationdef* r = new TRelationdef(this, idx, key, idxto, + relexprs, alias, allow_lock); + _reldefs.add(r); + + return _errors; +} + + +bool TRelation::add(int logicnum, const char* relexprs, int key, + int linkto, int alias, bool allow_lock) +{ + TLocalisamfile* f = new TLocalisamfile(logicnum, FALSE); + return add(f, relexprs, key, linkto, alias, allow_lock); +} + +bool TRelation::add(const char* tabname, const char* relexprs, int key, + int linkto, int alias, bool allow_lock) +{ + TTable* t = new TTable(tabname, FALSE); + return add(t, relexprs, key, linkto, alias, allow_lock); +} + +void TRelation::replace(TLocalisamfile* f, int index) +{ + const TLocalisamfile* p = (const TLocalisamfile*)_files.objptr(index); + CHECK(p && p->num() == f->num(), "Can't replace a file with different logic number"); + _files.add(f, index); +} + +TRectype& TRelationdef::load_rec(TRectype& r, const TBaseisamfile& from) const +{ + r.zero(); + + for (int j = 0 ; j < _fields.items(); j++) // for each field + { + TFieldref& s = (TFieldref&) _fields[j]; + s.write(s.read(from.curr()),r); + } + + return r; +} + + +void TRelation::zero() +{ + for (int i = 0; i < _files.items(); i++) + file(i).zero(); +} + +int TRelation::position_rels(TIsamop op, TReclock lockop, + TDate& atdate, int first) +{ + _errors = NOERR; + + // workhorse: position files for each active relation + for (int i = first; i < _reldefs.items(); i++) + { + TRelationdef& rd = reldef(i); + TLocalisamfile& from = file(rd.num()); + TLocalisamfile& to = file(rd.link()); + TReclock lck = rd._allow_lock ? lockop : _nolock; + + if (to.curr().empty()) + { from.zero(); continue; } + + from.setkey(rd._key); + from.curr().zero(); + + // build record + if (rd._fields.items() && rd._exprs.items()) + { + for (int j = 0 ; j < rd._fields.items(); j++) // for each field + { + /* + TExpression& expr = (TExpression&)rd._exprs[j]; + for (int k = 0; k < expr.numvar(); k++) + expr.setvar(k, to.get(expr.varname(k))); + */ + const char* expr = rd.evaluate_expr(j, to); + TFieldref& s = (TFieldref&) rd._fields[j]; + s.write(expr, from.curr()); + } // for each field + } + + // read record: if not found, zero current record + const TRectype rec(from.curr()); + from.read(op, lck, atdate); + if (from.bad()) + { + rd._first_match = (from.curr() == rec); + if (rd._first_match) + { + bool eq = TRUE; + + for (int kk = 0; eq && kk < rd._fields.items(); kk++) + { + if (rd._forced[kk]) + { + TFieldref& fl = (TFieldref&)rd._fields[kk]; + const TString f_fr(fl.read(from.curr())); + eq = (f_fr == rd.evaluate_expr(kk, to)); + } + } + if (eq) from.setstatus(NOERR); + else { rd._first_match = FALSE; from.curr().zero(); } + } + else from.curr().zero(); + } + else rd._first_match = TRUE; + } // for each relation + return _errors; +} + +bool TRelation::next_match(int logicnum, const char* fieldlist, int nkey) +{ + if (logicnum == file().num()) + { + next(); + return file().good(); + } + + const int i = log2ind(logicnum); + CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum); + + for (int j = 0; j < _reldefs.items(); j++) + if (reldef(j).num() == i) break; + + TLocalisamfile& from = file(i); + TLocalisamfile& to = file(reldef(j).link()); + + reldef(j)._first_match = FALSE; + + if (from.bad()) + return FALSE; // && vaffanculo(); + + const TRecnotype last = from.recno(); + + bool ok = TRUE; // Corrispondenza trovata ? + + if (fieldlist == NULL) + { + TRectype rec(from.curr()); + reldef(j).load_rec(rec, from); + from.setkey(reldef(j)._key); + from.next(); + + ok = (from.good() && from.curr() == rec); + for (int kk = 0; ok && kk < reldef(j)._fields.items(); kk++) + { + if (reldef(j)._forced[kk]) + { + TFieldref& fl = (TFieldref&)reldef(j)._fields[kk]; + const TString f_fr(fl.read(from.curr())); + const TString f_to(fl.read(to.curr())); + + ok = (f_fr == f_to); + } + } + if (ok) from.setstatus(NOERR); + } + else + { + if (nkey > 0) + from.setkey(nkey); + TToken_string fields(fieldlist); + TToken_string old(80); + for (const char* f = fields.get(0); f; f = fields.get()) + old.add(from.curr().get(f)); + + from.next(); + + old.restart(); + + + for (f = fields.get(0); f && ok; f = fields.get()) + { + const TFixed_string v(from.curr().get(f)); + const char* o = old.get(); + if (v != o) + ok = FALSE; + } + } + + if (!ok) + { + from.readat(last); + return FALSE; + } + + // Riposiziona gli eventuali files successivi + position_rels(_isequal, _nolock, (TDate&)botime, j+1); + reldef(j)._first_match = FALSE; + + return ok; +} + +bool TRelation::is_first_match(int logicnum) + // TRUE se c'e' un record ed e' il primo match (non si e' mai fatta + // position_rels) +{ + const int i = log2ind(logicnum); + CHECKD(i != NOTFOUND,"Nonexistent file referenced in relation ",logicnum); + + for (int j = 0; j < _reldefs.items(); j++) + if (reldef(j).num() == i) return reldef(j)._first_match; + const int err = file().status(); + return err == NOERR; +} + + +#ifdef DBG + +bool TRelation::isconsistent(bool reset) +{ + const int MAXREL = 24; + int bad = 0; + TRecnotype recnos[MAXREL]; + + for (int i = 0; i < _files.items() && i < MAXREL; i++) + { + // must be file OK, non-empty record + bad |= file(i).good() || file(i).curr().empty(); + recnos[i] = file(i).recno(); + } + + // if the above hold, check consistency + if (bad) + return _errors = bad; + + position_rels(_isequal, _nolock, (TDate&)botime); + for (i = 0; i < _files.items(); i++) + if (file(i).recno() != recnos[i]) + { + bad = -1; + break; + } + + if (reset == FALSE) + // leave as before + for(i = 0; i < _files.items(); i++) + file(i).readat(recnos[i]); + + return _errors = bad; +} + +#endif + + +int TRelation::write(bool force, TDate& atdate) +{ + _errors = file(0).write(atdate); + if (_errors != NOERR) + return _errors; + + for (int i = 0; i < _reldefs.items(); i++) + { + TRelationdef& rd = reldef(i); + TLocalisamfile& lf = file(rd.num()); + + if (!rd.write_enable() || lf.curr().empty()) + continue; + + int res = lf.write(atdate); + if (force && res == _isreinsert) + res = lf.rewrite(atdate); + if (_errors == NOERR) + _errors = res; + } + + return _errors; +} + +int TRelation::rewrite(bool force, TDate& atdate) +{ + _errors = file(0).rewrite(atdate); // Riscrive testata + if (force && _errors == _iskeynotfound) // Se non la trova ... + _errors = file(0).write(atdate); // ... forza la scrittura + + for (int i = 0; i < _reldefs.items(); i++) + { + TRelationdef& rd = reldef(i); + TLocalisamfile& lf = file(rd.num()); + + if (!rd.write_enable() || lf.curr().empty()) + continue; + + int res = lf.rewrite(atdate); + if (force && res == _iskeynotfound) + res = lf.write(atdate); + if (_errors == NOERR) _errors = res; + } + + return _errors; +} + +int TRelation::remove(TDate& atdate) +{ + const int res = file(0).remove(atdate); + if (_errors == NOERR && res != _iskeynotfound) _errors = res; + for (int i = 0; i < _reldefs.items(); i++) + { + TRelationdef& rd = reldef(i); + const int log = rd.num(); + + if (!rd.write_enable() || + (file(log).curr()).empty()) continue; + + const int res = file(log).remove(atdate); + if (_errors == NOERR && res != _iskeynotfound) _errors = res; + } + return _errors; +} + + +/////////////////////////////////////////////////////////// +// TCursor +/////////////////////////////////////////////////////////// + + +HIDDEN bool __evalcondition(const TRelation* r,TExpression* cond) + +{ + for (int i = 0; i < cond->numvar(); i++) + { + const char* s = cond->varname(i); + TFieldref f(s,0); + + cond->setvar(i, f.read(r)); + } + return (bool) *cond; +} + +FILE* TCursor::open_index(bool create) +{ +#if XVT_OS == XVT_OS_SCOUNIX + const char* const r = "r"; + const char* const w = "w"; +#else + const char* const r = "rb"; + const char* const w = "wb"; +#endif + + if (create && _indexname.empty()) _indexname.temp("ci$$"); + FILE* f = fopen(_indexname, create ? w : r); + if (f == NULL) + fatal_box("Can't use cursor index for file %d: '%s'\n", + file().filehnd()->ln, (const char*)_indexname); + + return f; +} + +TRecnotype TCursor::buildcursor(TRecnotype rp) +{ + + TRecnotype oldrecno=0,pos,ap = 0; + int junk, l, pagecnt = 0; + const bool filtered = has_filter(); + + FILE* _f = open_index(TRUE); + + if (DB_reccount(file().filehnd()->fhnd) == 0) + { + fclose(_f); + return 0; + } + + fseek(_f, 0L, SEEK_SET); + + l = strlen(to()); + junk=DB_index_seek(file().filehnd()->fhnd, (char*)(const char*) from()); + if (junk < 0) junk=get_error(junk); + if (junk == _iseof) return 0; + + TRecnotype* page = new TRecnotype [CMAXELPAGE]; + pos = DB_index_recno(file().filehnd()->fhnd); + _pos=-1; + + while (TRUE) + { + if (DB_index_eof(file().filehnd()->fhnd)) break; + const char* s0 = DB_index_getkey(file().filehnd()->fhnd); + const TRecnotype recno = DB_index_recno(file().filehnd()->fhnd); + if (l && (strncmp(to(), s0, l) < 0)) break; + if (recno == oldrecno) break; // means that no more keys are available + oldrecno=recno; + if (pagecnt == CMAXELPAGE) + { + if (filtered) pagecnt = filtercursor(pagecnt,page); + fwrite(page,sizeof(TRecnotype),pagecnt,_f); + for (int i= 0; i< pagecnt; i++) + if (page[i] == rp) + { + _pos = ap + i; + break; + } + ap += pagecnt; + pagecnt = 0; + } + page[pagecnt++] = recno; + DB_index_next(file().filehnd()->fhnd); + int rt=get_error(-1); + if (rt != NOERR) + fatal_box("Can't read index n. %d - file n. %d",DB_tagget(file().filehnd()->fhnd),file().filehnd()->ln); + } // while + if (pagecnt) + { + if (filtered) pagecnt = filtercursor(pagecnt, page); + fwrite(page, sizeof(TRecnotype), pagecnt, _f); + for (int i = 0; i < pagecnt; i++) + if (page[i] == rp) + { + _pos = ap + i; + break; + } + ap += pagecnt; + } + if (_pos == -1) pos=0; + delete page; + fclose (_f); + return ap; +} + + +int TCursor::filtercursor(int pagecnt, TRecnotype* page) + +{ + int np=0, handle=file().filehnd()->fhnd; + TRectype& rec=file().curr(); + for (int i=0; i< pagecnt; i++) + { + file().readat(rec,page[i]); + if (update_relation()) _if->update(); + if ((_filterfunction ? _filterfunction(_if) : TRUE ) && + (_fexpr ? __evalcondition(_if, _fexpr) : TRUE)) + { + if (np < i) page[np] = page[i]; + np++; + } + } + return np; +} + + +bool TCursor::ok() const + +{ + if (file().bad()) return FALSE; + + const TRectype& rec = file().curr(); + TString key(rec.key(_nkey)), kf(from()), kt(to()); + if (file().tab()) + { + key.ltrim(3); + kf.ltrim(3); + kt.ltrim(3); + } + + if (key < kf || (kt.not_empty() && kt < key.left(kt.len()))) + return FALSE; + if (_filter_update || _filterfunction_update) _if->update(); + if ((_filterfunction ? _filterfunction(_if) : TRUE ) && + (_fexpr ? __evalcondition(_if, _fexpr) : TRUE)) + return TRUE; + return FALSE; +} + +bool TCursor::changed() + +{ + isdef* fh = file().filehnd(); + + if (_frozen && _lastrec > 0L) return _filename != fh->d->SysName; + + const TRecnotype eod = DB_reccount(fh->fhnd); + + if (_lastrec != eod || + (_lastkrec != DB_changed(fh->fhnd)) || + (_filename != fh->d->SysName)) + { + // _lastrec = eod; + // _filename = fh->d->SysName; + // _lastkrec = DB_changed(fh->fhnd); + return TRUE; + } + else return FALSE; +} + +TRecnotype TCursor::update() + +{ + main_app().begin_wait(); + + file().setkey(_nkey); + if (file().curr().empty()) file().curr().zero(); + file().read(_isgteq); + const TRecnotype totrec = buildcursor(file().recno()); + + main_app().end_wait(); + + isdef* fh = file().filehnd(); + const TRecnotype eod = DB_reccount(fh->fhnd); + + _lastrec = eod; + _filename = fh->d->SysName; + _lastkrec = DB_changed(fh->fhnd); + + return totrec; +} + + +void TCursor::filter(const char* fil, const TRectype *from, + const TRectype* to) + +{ + CHECK(!_frozen, "Impossibile filtrare un cursore congelato"); + + TString kf(_keyfrom), kto(_keyto); + + const bool filterchanged = (fil != NULL) && (_filter != fil); + + if (file().tab()) + { + TTable& f = (TTable&) file(); + kf = kto = f.name(); + } + if (from != NULL) + { + kf = from->key(_nkey); + int p; + while ((p = kf.find('~')) != -1) kf[p] = ' '; + } + if (to != NULL) + { + kto = to->key(_nkey); + int p; + while ((p = kto.find('~')) != -1) kto[p] = ' '; + } + + if (filterchanged || (_keyfrom != kf) || (_keyto != kto)) + { + _pos = 0; + _totrec = 0; + _lastrec = 0; + if (filterchanged) + { + _filter = fil; + if (_fexpr) delete _fexpr; + TTypeexp type = (_filter.find('"') != -1) ? _strexpr : _numexpr; + if (_filter.not_empty()) + { + _fexpr = new TExpression(_filter, type); + if (_fexpr->type() == _numexpr) + for (int i = 0 ; i < _fexpr->numvar(); i++) + if (file().curr().type(_fexpr->varname(i)) == _alfafld) + { + _fexpr->set_type(_strexpr); + break; + } + } + else _fexpr = NULL; + } + _keyfrom = kf; + _keyto = kto; + } +} + +void TCursor::setkey(int nkey) + +{ + if (nkey != _nkey) + { + _lastrec = 0L; + _nkey = nkey; + file().setkey(_nkey); + filter(NULL); + } +} + + +TRecnotype TCursor::read(TIsamop op, TReclock lockop, TDate& atdate) +{ + TRecnotype *page; + int pagecnt; + + file().setkey(_nkey); + const bool approx = (op == _isgteq); + + _if->file().read(op, lockop, atdate); + if (approx) + { + while (_if->file().good() && !ok()) + _if->file().next(); + } + const TRecnotype curpos = file().recno(); + + if (changed()) + _totrec = update(); + if (approx && _if->file().status() == _iseof) + { + _pos = _totrec - 1; + return _pos; + } + + FILE* _f = open_index(); + + if (fseek(_f, 0L, SEEK_SET) != 0) + fatal_box("Can't repos cursor : File %d\n", file().filehnd()->ln); + + page = new TRecnotype [CMAXELPAGE]; + _pos = -1; + + for (TRecnotype max = _totrec; _pos == -1 && max > 0; max -= pagecnt) + { + pagecnt = (max < CMAXELPAGE) ? (int)max : CMAXELPAGE; + fread(page, sizeof(TRecnotype), pagecnt, _f); + for (int i = 0; i < pagecnt; i++) + if (page[i] == curpos) + { + _pos = _totrec - max + i; + break; + + } + } + if (_pos == -1) _pos = 0; + delete page; + fclose(_f); + + readrec(); + return _pos; +} + + +TCursor::TCursor(TRelation* r, const char* fil, int nkey, + const TRectype *from, const TRectype* to) +: _if(r), _nkey(nkey), _frozen(FALSE), _filterfunction(NULL), _fexpr(NULL), _filter_update(FALSE), _filterfunction_update(FALSE) +{ + file().setkey(_nkey); + _pos = 0; + _totrec = 0; + _lastrec = 0; + _lastkrec = 0; + filter(fil, from, to); +} + + +TCursor::~TCursor() + +{ + if (_indexname.not_empty()) + ::remove(_indexname); + if (_fexpr) + delete _fexpr; +} + + +TRecnotype TCursor::readrec() + +{ + TRecnotype& nrec = file().filehnd()->RecNo; + + if (_pos == items()) + { + file().setstatus(_iseof); + curr().zero(); + return nrec = 0L; + } + file().setstatus(NOERR); + + FILE* _f = open_index(); + + if (fseek(_f, _pos * sizeof(TRecnotype), SEEK_SET) != 0) + fatal_box("Can't seek position %ld in cursor n. %d\n", _pos, file().filehnd()->ln); + + if (fread(&nrec, sizeof(TRecnotype), 1, _f) != 1) + fatal_box("Can't read position %ld in cursor n. %d\n", _pos, file().filehnd()->ln); + + fclose(_f); + + curr().setdirty(); + file().readat(nrec); + repos(); + return nrec; +} + +int TCursor::lock(TReclock l) +{ + int rt=NOERR; + switch(l) + { + case _lock: + rt=DB_lock_rec(file().filehnd()->fhnd,_pos); + break; + case _unlock: + rt=DB_unlock(file().filehnd()->fhnd); + break; + default: + break; + } + if (rt != NOERR) rt=get_error(rt); + return(rt); +} + +TRecnotype TCursor::operator =(const TRecnotype pos) +{ + if (changed()) + _totrec = update(); + CHECKD(pos >= 0 && pos <= _totrec, "Bad cursor position : ", pos); + _pos = pos; + readrec(); + return _pos; +} + +TRecnotype TCursor::operator +=(const TRecnotype npos) +{ + if (changed()) + _totrec = update(); + + _pos += npos; + if (_pos > _totrec) _pos = _totrec; + else + if (_pos < 0) _pos = 0; + readrec(); + + return _pos; +} + + +TRecnotype TCursor::items() +{ + if (changed()) + _totrec = update(); + return _totrec; +} + +bool TCursor::next_match(int lognum, const char* fl, int nk) +{ + if (lognum == 0 || lognum == file().num()) + {++(*this); return file().good(); } + else return _if->next_match(lognum, fl, nk); +} + +bool TCursor::is_first_match(int ln) +{ + return (ln == 0 || ln == file().num()) ? + (_pos == 0 && file().good()) : (_if->is_first_match(ln)); +} + + +/////////////////////////////////////////////////////////// +// TSorted_cursor +/////////////////////////////////////////////////////////// + +typedef struct { + char f[512]; + TRecnotype p; +} El_To_Sort; + +bool TSorted_cursor::check_expr(TString& s) +// Controlla la validita' dell'espressione ritornando un bool +// Modifica l'espressione in modo da avere una valido parametro per TFieldref! +// Es. UPPER(20->RAGSOC[1,40]+) ==> 20->RAGSOC[1,40]+ +// N.B. : la direzione dell'ordinamento va messa indicata sempre dopo la definizione del campo, mai +// dopo l'operatore UPPER(). E' a cura delle altre funzioni esterne che utilizzano la check_expr() +// discriminare il segno + o - che sia +{ + int p=s.find('('); + int p1=s.find(')'); + int rt=TRUE; + + if (p>-1 && p1>-1) + if (s.find("UPPER") == -1) + { + error_box("Only UPPER() operator is allowed"); + rt=FALSE; + } + else + { + s=s.mid(6,s.len()-7); + p=s.find('('); + p1=s.find(')'); + if (p>-1 || p1 >-1) + { + error_box("Too many parentheses!"); + rt=FALSE; + } + } + else + if ((p>-1 && p1==-1) || (p==-1 && p1>-1)) + { + error_box("Unbalanced parentheses in expression"); + rt=FALSE; + } + return rt; +} + +bool TSorted_cursor::is_upper(TString& s) +// Controlla se nell'espressione esiste l'operatore UPPER() +{ + bool rt=TRUE; + if (s.find("UPPER")>-1) + rt=check_expr(s); + else + rt=FALSE; + return rt; +} + +TRecnotype TSorted_cursor::buildcursor(TRecnotype rp) +{ + TRecnotype oldrecno=0,pos,ap = 0; + int abspos=0,junk, l, pagecnt = 0; + const bool filtered = has_filter(); + TString s; + El_To_Sort * Element; + + _order_expr.restart(); + while ((s=_order_expr.get()) && s.not_empty()) + { + check_expr(s); // Toglie l'eventuale operatore UPPER(), in modo che l'ultimo carattere + // indichi eventualmente la direzione dell'ordinamento + char versus = (s.right(1)=="-") ? 'd' : 'a'; + if (s.right(1) == "-" || s.right(1) == "+") + s.cut(s.len()-1); + TFieldref f(s,0); + // Il controllo del file e' automatico in f.len() + _sort->addsortkey(abspos+f.from(),f.len(relation()->curr()),versus); + CHECKS(f.len(relation()->curr())!=0,"Field can not have null length: ",(const char *) s); + int lf = (f.file()!=0 ? f.file() : file().num()); + TRectype r(lf); + abspos+=r.length(f.name()); + CHECKD(abspos<=512,"Cannot exceed 512 bytes-key %d",abspos); + } + + _sort->init(); + + FILE* _f = open_index(TRUE); + + if (DB_reccount(file().filehnd()->fhnd) == 0) + { + fclose(_f); + return 0; + } + + fseek(_f, 0L, SEEK_SET); + + l = strlen(to()); + junk=DB_index_seek(file().filehnd()->fhnd, (char*)(const char*) from()); + if (junk < 0) junk=get_error(junk); + if (junk == _iseof) return 0; + + TRecnotype* page = new TRecnotype [CMAXELPAGE]; + pos = DB_index_recno(file().filehnd()->fhnd); + TCursor::pos()=-1; + + while (TRUE) + { + if (DB_index_eof(file().filehnd()->fhnd)) break; + const char* s0 = DB_index_getkey(file().filehnd()->fhnd); + const TRecnotype recno = DB_index_recno(file().filehnd()->fhnd); + if (l && (strncmp(to(), s0, l) < 0)) break; + if (recno == oldrecno) break; // means that no more keys are available + oldrecno=recno; + if (pagecnt == CMAXELPAGE) + { + pagecnt = filtercursor(pagecnt,page); + for (int i= 0; i< pagecnt; i++) + if (page[i] == rp) + { + TCursor::pos() = ap + i; + break; + } + ap += pagecnt; + pagecnt = 0; + } + page[pagecnt++] = recno; + DB_index_next(file().filehnd()->fhnd); + int rt=get_error(-1); + if (rt != NOERR) + fatal_box("Can't read index n. %d - file n. %d",DB_tagget(file().filehnd()->fhnd),file().filehnd()->ln); + } // while + if (pagecnt) + { + pagecnt = filtercursor(pagecnt, page); + for (int i = 0; i < pagecnt; i++) + if (page[i] == rp) + { + TCursor::pos() = ap + i; + break; + } + ap += pagecnt; + } + _sort->endsort(); + pagecnt=0; + while ((Element=(El_To_Sort *)_sort->retrieve()) != NULL) + { + page[pagecnt++]=Element->p; + if (pagecnt==CMAXELPAGE) + { + fwrite(page,sizeof(TRecnotype),pagecnt,_f); + pagecnt=0; + } + } + if (pagecnt) + fwrite(page,sizeof(TRecnotype),pagecnt,_f); + if (TCursor::pos() == -1) pos=0; + delete page; + fclose (_f); + return ap; +} + +int TSorted_cursor::filtercursor(int pagecnt, TRecnotype* page) +{ + + int np=0, handle=file().filehnd()->fhnd; + TRectype& rec=file().curr(); + TString s; + El_To_Sort Element; + + for (int i=0; i< pagecnt; i++) + { + file().readat(rec,page[i]); + if (update_relation()) relation()->update(); + if ((filterfunction() ? filterfunction()(relation()) : TRUE ) && + (expression() ? __evalcondition(relation(), expression()) : TRUE)) + { + if (np < i) page[np] = page[i]; + np++; + + _order_expr.restart(); + strcpy(Element.f,""); + while ((s=_order_expr.get()) && s.not_empty()) + { + bool is_up=is_upper(s); + int p=s.find('[');// Qui estrae il nome del campo: + if (p>-1) // rimane solo l'indicazione del file (eventuale) + s.cut(p); // ed il nome del campo: + else // UPPER(20->RAGSOC[1,30]+) ===>> 20->RAGSOC + if (s.right(1) == "-" || s.right(1) == "+") + s.cut(s.len()-1); + TFieldref f(s,0); + TString sf=f.read(relation()); + bool is_date = file(f.file()).curr().type(s) ==_datefld; + if (is_date) // Se il campo e' di tipo data, la converte in ANSI! + { + TDate d(sf); + sf=d.string(ANSI); + } + if (is_up) sf.upper(); + TString fmt; + fmt.format("%%-%ds",f.len(rec)); + strcat(Element.f,(const char*)sf.format((const char *)fmt,(const char *)sf)); + } + Element.p=page[i]; + _sort->sort((char *) &Element); + } + } + + return np; +} + +bool TSorted_cursor::changed() +{ + bool rt; + + if (_is_valid_expr) + { + rt = TCursor::changed(); + if (!rt) rt=_is_changed_expr; + _is_changed_expr = FALSE; + } else + { + error_box("Can't perform changed() while expression ain't valid!"); + rt=FALSE; + } + return rt; +} + + +void TSorted_cursor::change_order(const char* order_expr) +{ + if (order_expr && *order_expr && order_expr != _order_expr) + { + TString s; + _order_expr = order_expr; + _order_expr.restart(); + while ((s=_order_expr.get()) && s.not_empty() && (_is_valid_expr=check_expr(s))) ; + if (_is_valid_expr) + _is_changed_expr=TRUE; + } +} + +TSorted_cursor::TSorted_cursor(TRelation *f, const char * order_expr, const char * filter, int key, const TRectype* from, const TRectype* to) +: TCursor(f,filter,key,from,to) +{ + change_order(order_expr); + _sort = new TSort(sizeof(El_To_Sort)); +} + +TSorted_cursor::~TSorted_cursor() +{ + if (_sort) delete _sort; +} + + + +/////////////////////////////////////////////////////////// +// TFieldRef +/////////////////////////////////////////////////////////// + +int name2log(const char* name) +{ + int log = 0; + + if (name) + while (*name == ' ') name++; + + if (name && *name) + { + if (isdigit(*name) || *name == '-') + { + log = atoi(name); + if (strchr(name, '@')) log = -log; + } + else + { + const int len = strlen(name); + if (len == 3 || len == 4) + log = TTable::name2log(name); + } + + CHECKS(log != 0, "Non e' un file ne' una tabella: ", name); + } + + return log; +} + + +TFieldref::TFieldref() : _fileid(0), _name(0) {} + + +TFieldref::TFieldref(const TString& s, short defid) +{ + operator=(s); + if (_fileid == 0) _fileid = defid; +} + + +// A Fieldref should have the following format (only NAME is mandatory): +// FILE->NAME[FROM,TO] +TFieldref& TFieldref::operator =(const TString& s) +{ + int pos = s.find("->"); + if (pos > 0) + { + _id = s.left(pos); _id.strip(" "); + _fileid = name2log(_id); + pos += 2; + } + else + { + _id.cut(0); + _fileid = pos = 0; + } + + int par = s.find('[', pos); + _name = s.sub(pos, par); _name.strip(" "); + + if (par > 0) + { + pos = par+1; + set_from(atoi(s.mid(pos))); + par = s.find(',', pos); + if (par > 0) set_to(atoi(s.mid(par+1))); + else set_to(from()+1); + } + else + { + set_from(0); + set_to(-1); + } + + return *this; +} + +void TFieldref::set_file(int f) +{ + _id.cut(0); + if (f != 0) + { + _fileid = f; + _id << abs(f); + if (f < 0) _id << '@'; + } + else _fileid = 0; +} + +void TFieldref::print_on(ostream& out) const +{ + if (_id.not_empty()) out << _id << "->"; + out << _name; + if (_from > 0 || _to > 0) + { + out << '[' << (_from+1); + out << ',' << _to << ']'; + } +} + +const char* TFieldref::read(const TRectype& rec) const +{ + static TString256 buffer; + + if (_fileid >= CNF_GENERAL) + { + TToken_string s(_name, '.'); + TConfig c(_fileid - CNF_STUDIO, s.get()); + buffer = c.get(s.get()); + } + else + { + buffer = rec.get(_name); + if (_from > 0 || _to > 0) + { + const int l = buffer.len(); + if (_to < l && _to > 0) buffer.cut(_to); + if (_from > 0) buffer.ltrim(_from); + } + } + return buffer; +} + +const char* TFieldref::read(const TRelation* c) const +{ + CHECK(c != NULL, "Can't read field from NULL relation"); + const char * s = read(c->lfile(_id).curr()); + return s; +} + +void TFieldref::write(const char* val, TRelation* c) const +{ + CHECK(c != NULL, "Can't write field on NULL relation"); + TRectype &curr = c->lfile(_id).curr(); + write(val, curr); +} + + +void TFieldref::write(const char* val, TRectype& rec) const +{ + if (_fileid >= CNF_GENERAL) return; + if (_from > 0) + { + TString256 campo(rec.get(_name)); + campo.overwrite(val, _from); + rec.put(_name, campo); + } + else + rec.put(_name, val); +} + +int TFieldref::len(TRectype &rec) const +{ + if (_to >= 0) + return _to - _from; + if (_fileid == 0) + return rec.length(_name) - _from; + + const TRectype r(_fileid); + const int len = r.length(_name); + return len - _from; +} + +/////////////////////////////////////////////////////////// +// TRecord_Array +/////////////////////////////////////////////////////////// + +TRecord_array::TRecord_array(const TRectype& r, const char* numfield, int first) +: _file(r.num()), _num(numfield), _offset(first - 1) +{ + read(r); +} + +TRecord_array::TRecord_array(int logicnum, const char* numfield, int first) +: _file(logicnum), _num(numfield), _offset(first - 1) +{ + add(new TRectype(logicnum)); +} + +TRecord_array::TRecord_array(const TRecord_array& a) +: TArray(a), _file(a._file), _offset(a._offset), _num(a._num) +{} + +const TRectype& TRecord_array::key() const +{ + TRectype* r = (TRectype*)objptr(0); + CHECK(r, "TRecord_array lost its key"); + return *r; +} + +bool TRecord_array::exist(int n) const +{ + const int i = n > 0 ? n - _offset : -1; + TRectype* r = (TRectype*)objptr(i); + return r != NULL; +} + + +TRectype& TRecord_array::row(int n, bool create) +{ + const int i = n >= 0 ? n - _offset : -1; + TRectype* r = (TRectype*)objptr(i); + if (r == NULL && create) + { + r = (TRectype*)key().dup(); // Crea nuovo record copiando la chiave + n = add(r, i) + _offset; // Riassegna n se era negativo! + r->put(_num, n); // Aggiorna campo numero riga + } + CHECKD(r && n > 0, "Bad record number ", n); + return *r; +} + +bool TRecord_array::renum_key(const char* field, const TString& num) +{ + CHECKS(!num.blank(), "Blank key value for field: ", field); + const TString& curr = key().get(field); + + if (curr == num) + return FALSE; + + for (int i = last(); i >= 0; i--) + { + TRectype* r = (TRectype*)objptr(i); + if (r == NULL) + r->put(field, num); + } + + return TRUE; +} + +bool TRecord_array::renum_key(const char* field, long num) +{ + CHECKS(num > 0, "Null key value for field: ", field); + TString16 n; n << num; + return renum_key(field, n); +} + + +int TRecord_array::rec2row(const TRectype& r) const +{ + CHECK(r.num() == key().num(), "Incompatible record"); + const int n = atoi(r.get(_num)) - _offset; // Non e' detto che sia un int! + CHECKD(n >= 0 && n < 30000, "Bad line number in record ", n + _offset); + return n; +} + +int TRecord_array::add_row(TRectype* r) +{ + const int nr = rec2row(*r); + add(r, nr); + if (nr == 0 && rows() > 0) // Se ho cambiato il record campione + { // e ci sono altre righe ... + for (int f = r->items()-1; f >= 0; f--) + { + const char* fn = r->fieldname(f); + const TString& v = r->get(fn); + if (v.not_empty()) renum_key(fn, v); // ... aggiorna tutte le righe in base + } // ai campi non vuoti del campione + } + return nr; +} + +bool TRecord_array::destroy_row(int r, bool pack) +{ + CHECKD(r > _offset, "Can't destroy row ", r); + const int index = r - _offset; + const bool ok = destroy(index, pack); + + if (ok && pack) + { + for (int i = index; i < items(); i++) + row(i + _offset, FALSE).put(_num, i + _offset); + } + return ok; +} + +void TRecord_array::destroy_rows() +{ + for (int i = last(); i > 0; i--) + destroy(i); +} + +int TRecord_array::read(TRectype* filter) +{ + CHECKD(filter->num() == _file, "Bad key record ", filter->num()); + CHECKS(filter->get(_num).empty(), "You can't specify in the filter the field ", (const char*)_num); + + destroy(); + add(filter, 0); // Store filter record for later use + + int err = NOERR; + if (!filter->empty()) + { + TLocalisamfile f(_file); + TRectype* rec = (TRectype*)filter->dup(); + err = rec->read(f, _isgteq); + for (int e = err; e == NOERR && *rec == key(); e = rec->next(f)) + { + add_row(rec); + rec = (TRectype*)filter->dup(); + } + delete rec; + } + return err; +} + + +int TRecord_array::read(const TRectype& filter) +{ + TRectype* f = (TRectype*)filter.dup(); + return read(f); +} + + + +int TRecord_array::write(bool re) const +{ + int err = NOERR; + + TLocalisamfile f(_file); + + int last_on_file = 0; + + const int u = last(); + for (int i = 1; i <= u; i++) + { + const TRectype* r = (TRectype*)objptr(i); + + if (r != NULL) + { + if (re) + { + err = r->rewrite(f); + if (err == _iskeynotfound || err == _iseof || err == _isemptyfile) + err = r->write(f); + if (err != NOERR) + break; + } + else + { + err = r->write(f); + if (err == _isreinsert) + err = r->rewrite(f); + if (err != NOERR) + break; + } + } + else + { + const int pos = i+_offset; + if (pos > last_on_file) + { + TRectype* rec = (TRectype*)key().dup(); + rec->put(_num, pos); + err = rec->read(f, _isgteq); + if (err == NOERR) // La riga c'era ma ora non piu' + { + last_on_file = atoi(rec->get(_num)); + if (last_on_file == pos) + { + err = (rec->remove(f)); + if (err != NOERR) + { + delete rec; + break; + } + } + } + else + last_on_file = 30000; + delete rec; + } + } + } + + if (re && err == NOERR && last_on_file != 30000) + { + const int pos = i+_offset; + TRectype* rec = (TRectype*)key().dup(); + // Cancella eventuali residui successivi + rec->put(_num, pos); + for (int e = rec->read(f, _isgteq); e == NOERR && *rec == key(); e = rec->next(f)) + { + err = rec->remove(f); + if (err != NOERR) + break; + } + delete rec; + } + + return err; +} + +int TRecord_array::remove() +{ + destroy_rows(); + return rewrite(); +} + +/////////////////////////////////////////////////////////// +// TRelation_description +/////////////////////////////////////////////////////////// + + +void TRelation_description::init_files_array() +{ + const TLocalisamfile& f = _rel->file(); + TToken_string s(128); + print_name(s, f); // Logic number + + const char* name = f.name(); + if (f.tab()) + name = TDir::tab_des(name); + else + name = prefix().description(name); + s.add(name); // Description + s.add(" "); // No join + s.add(f.getkey()); // Key + s.add(" | "); // No Alias nor expression + + _files.destroy(); + _files.add(s); // Main file + for (int i = 0; i < _rel->items(); i++) + { + _rel->reldef(i).print_on(s); + _files.add(s); + } +} + + +void TRelation_description::read_rel() +{ + // scan files and build description arrays + init_files_array(); + + _fields.destroy(); + for (int i = 0; i < _files.items(); i++) + { + TToken_string& tt = (TToken_string&)_files[i]; + TFilename descfname; descfname << DESCDIR << "/d"; + + const char* tn = tt.get(0); // Codice del file o tabella + + int which_file = atoi(tn); // Non spostare questa riga (tn puo' cambiare!) + if (which_file == 0) + which_file = TTable::name2log(tn); // Numero logico del file + + if (tn[0] == '%' || tn[0] == '$') + descfname << (tn+1); + else + descfname << tn; + descfname << ".des"; + + TConfig conf(descfname, DESCPAR); + // new record descriptor for _fields array + TString_array* rdesc = new TString_array; + + TTrec trec; trec.get(which_file); + TToken_string ttmp(64); + + for (int f = 0; f < trec.fields(); f++) + { + ttmp = trec.fielddef(f); + const TString16 name(ttmp.get(0)); + if (!name.blank()) + { + TString80 dfld(conf.get(name, NULL, -1, "Missing description")); + if (!dfld.blank() && dfld[0] != '#') + { + ttmp.add(dfld,4); + // contiene: nome campo, tipo, lunghezza, decimali, descrizione + rdesc->add(ttmp); + } + } + } + + _fields.add(rdesc, i); + } +} + +void TRelation_description::change_relation(TRelation& r, TString_array& a) +{ + _rel = &r; + read_rel(); + if (a.items() > 0) + _files = a; +} + +void TRelation_description::print_on(ostream& out) const +{ + for (int i = 0; i < _files.items(); i++) + { + TToken_string& r = ((TRelation_description*)this)->_files.row(i); + TString16 cod(r.get(4)); + if (atoi(cod) == 0) + cod = r.get(0); + else + cod << '@'; + out << " " << cod << "->* "; + out << "\"" << r.get(1) << "\"\n"; + } +} + + +bool TRelation_description::choose_file(int file) +{ + TArray_sheet sht(-1,-1,0,0,"Selezione archivio", "Codice|Descrizione archivio@70"); + TToken_string tt(80); + + int sel = 0; + + for (int i = 0; i < _files.items(); i++) + { + TToken_string& tf = _files.row(i); + tt = tf.get(4); + + int num = atoi(tt); + if (num == 0) + { + tt = tf.get(0); + num = atoi(tt); + if (sel == 0 && file > 0 && num == file) + sel = i; + } + else + { + if (sel == 0 && file < 0 && num == -file) + sel = i; + tt << '@'; + } + + tt.add(tf.get(1)); + sht.add(tt); + } + + sht.select(sel); + + if (sht.run() == K_ENTER) + { + _cur_file = (int)sht.selected(); + _cfile = _files.row(_cur_file); + return TRUE; + } + return FALSE; +} + +bool TRelation_description::choose_field(const char* fld) +{ + TArray_sheet sht(-1,-1,0,0,"Selezione campo", + "Campo@10|Descrizione@50|Tipo@10|Dim."); + TString_array& fd = (TString_array&)_fields[_cur_file]; + TToken_string tt(80); + + int sel = 0; + for (int i = 0; i < fd.items(); i++) + { + TToken_string& tf = fd.row(i); + tt = tf.get(0); + if (sel == 0 && tt == fld) + sel = i; + tt.add(tf.get(4)); + tt.add(field_type_str((TFieldtypes)tf.get_int(1))); + tt.add(tf.get(2)); + sht.add(tt); + } + + sht.select(sel); + if (sht.run() == K_ENTER) + { + _cur_field = (int)sht.selected(); + _cfield = fd.row(_cur_field); + return TRUE; + } + return FALSE; +} + +bool TRelation_description::build_menu(const char* title) +{ + return FALSE; +} + +bool TRelation_description::remove_menu() +{ + return FALSE; +} + +bool TRelation_description::set_cur_file(int id) +{ + const int n = _rel->log2ind(id); + if (n >= 0 && n != _cur_file) + { + _cur_file = n; + _cfile = _files.row(n); + } + return n >= 0; +} + +int TRelation_description::file_num() +{ + int n = -_cfile.get_int(4); + if (n == 0) + n = _cfile.get_int(0); + return n; +} + +const char* TRelation_description::file_desc() +{ + return _cfile.get(1); +} + +void TRelation_description::file_desc(const char* desc) +{ + _cfile.add(desc, 1); + _files.row(_cur_file) = _cfile; +} + + +const char* TRelation_description::field_desc() +{ + return _cfield.get(4); +} + +const char* TRelation_description::field_name() +{ + return _cfield.get(0); +} + +int TRelation_description::field_len() +{ + return _cfield.get_int(2); +} + +TFieldtypes TRelation_description::field_type() +{ + return (TFieldtypes)_cfield.get_int(1); +} + +bool TRelation_description::set_field_description(const char* field, const char* des) +{ + TString_array& fld = (TString_array&)_fields[_cur_file]; + + for (int i = 0; i < fld.items(); i++) + { + TToken_string& tt = fld.row(i); + if (strcmp(tt.get(0),field) == 0) + { + if (des && *des) + tt.add(des, 4); + else + { + fld.destroy(i); + fld.pack(); + } + return TRUE; + } + } + + if (des && *des) + { + TToken_string tt(80); + tt.add(field); + tt.add(des, 4); + fld.add(tt); + return TRUE; + } + + return FALSE; +} + +const char* TRelation_description::get_field_description(const char* field) +{ + TString_array& fld = (TString_array&)_fields[_cur_file]; + + for (int i = 0; i < fld.items(); i++) + { + TToken_string& tt = fld.row(i); + if (strcmp(tt.get(0),field) == 0) + return tt.get(4); + } + return ""; +} + + +TRelation_description::TRelation_description(TRelation& r) +: _rel(&r), _menu(FALSE), _cur_file(0), _cur_field(0), + _cfile(80), _cfield(80) +{ + read_rel(); + if (_files.items() > 0) + { + _cfile = _files.row(0); + _cfield = ""; + } +} + +TRelation_description::~TRelation_description() +{ + if (_menu) remove_menu(); +} + + +// *** EOF relation.cpp + diff --git a/include/window.cpp b/include/window.cpp index 2c6c5a717..4de207415 100755 --- a/include/window.cpp +++ b/include/window.cpp @@ -1,1060 +1,1060 @@ -#include -#include -#include -#include - -#define __WINDOW_CPP -#include -#include - -#if XVT_OS == XVT_OS_WIN -#define STRICT -#include -#endif - -HIDDEN MENU_ITEM* find_menu_item(MENU_ITEM* menu, MENU_TAG id, bool ismbar) -{ - MENU_ITEM* fnd = NULL; - MENU_ITEM* mn = ismbar ? &menu[0] : &menu->child[0]; - - for (int m = 0; mn != NULL && mn->tag != 0; m++, mn=ismbar?&menu[m]:&menu->child[m]) - { - fnd = mn->tag == id ? mn : find_menu_item(mn, id, FALSE); - if (fnd != NULL) break; - } - - return fnd; -} - -HIDDEN bool remove_menu_item(MENU_ITEM* menu, MENU_TAG id, bool ismbar) -{ - MENU_ITEM* mn = ismbar ? &menu[0] : &menu->child[0]; - - for (int m = 0; mn != NULL && mn->tag != 0; m++, mn=ismbar?&menu[m]:&menu->child[m]) - { - if (mn->tag == id) - { - do - { - xvt_mem_rep((DATA_PTR)mn, (DATA_PTR)(mn+1), sizeof(MENU_ITEM), 1); - mn++; - } - while (mn->tag != 0); - return TRUE; - } - else if (remove_menu_item(mn, id, FALSE)) - return TRUE; - } - - return FALSE; -} - - -HIDDEN void set_menu_item(MENU_ITEM& m, TToken_string& tt) -{ - MENU_TAG tag = tt.get_int(0); - TString flag = tt.items() <= 2 ? "": tt.get(2); - char* text = NULL; - if (strlen(tt.get(1)) > 0) - { - text = (char*)xvt_mem_alloc(strlen(tt.get(1)) + 1); - strcpy(text, tt.get(1)); - } - - m.tag = tag; - m.text = text; - m.enabled = !(flag.find('D') != -1); - m.checkable = flag.find('C') != -1 || flag.find('c') != -1; - m.checked = flag.find('c') != -1; - m.separator = text == NULL; -} - -/////////////////////////////////////////////////////////// -// TWindow_manager -/////////////////////////////////////////////////////////// -// @C -// Classe TWindow_manager -// -// @END - -class TWindow_manager -{ - // @DPRIV - enum { MAX_WIN = 8 }; // Max number of modal windows - - TWindow* _window[MAX_WIN]; // Stack of active windows - char _current; // Stack pointer - void menu_enable(bool) const; // Abilita o disabilita il menu della task window - - FILE* _lowhandle; - -public: - // @FPUB - TWindow_manager(); // Costruttore - ~TWindow_manager(); - - void free_handle(); - void lock_handle(); - - void reg(TWindow* m); - // Registra la finestra corrente - void unreg(const TWindow* m); - // De-registra la finestra corrente - - TWindow* cur_win() const { return (_current < 0) ? NULL : _window[_current]; } // Ritorna il puntatore alla finestra corrente - void destroy(); - bool can_close() const; -} WinManager; - - -TWindow_manager::TWindow_manager() : _current(-1), _lowhandle(NULL) -{ - lock_handle(); -} - -TWindow_manager::~TWindow_manager() -{ - destroy(); - free_handle(); -} - -void TWindow_manager::destroy() -{ - while (_current >= 0) - { - TWindow* w = cur_win(); - w->stop_run(K_FORCE_CLOSE); - w->close_modal(); - } -} - -void TWindow_manager::lock_handle() -{ - CHECK(_lowhandle == NULL, "Can't relock low handle"); - _lowhandle = fopen("bagn001a.msk", "r"); - CHECK(_lowhandle != NULL, "Can't lock low handle"); -} - -void TWindow_manager::free_handle() -{ - CHECK(_lowhandle, "Can't unlock low handle"); - fclose(_lowhandle); - _lowhandle = NULL; -} - - -bool TWindow_manager::can_close() const -{ - bool ok = TRUE; - if (_current >= 0) - ok = cur_win()->can_be_closed(); - return ok; -} - - -// Dis/abilitazione del menu principale -HIDDEN void xvt_menu_enable(MENU_ITEM* m, bool on) -{ - while (m->tag) - { - switch(m->tag) - { - case MENU_FILE: // Leave it as is - case -1: // Separator - break; - default: - xvt_menu_set_item_enabled(TASK_WIN, m->tag, on); - break; - } - m++; - } -} - - -void TWindow_manager::menu_enable(bool on) const -{ - MENU_ITEM *mi = xvt_menu_get_tree(TASK_WIN); - xvt_menu_enable(mi, on); - xvt_menu_update(TASK_WIN); - xvt_res_free_menu_tree(mi); -} - - -void TWindow_manager::reg(TWindow* m) -{ - _current++; - CHECK(_current < MAX_WIN, "Too many windows"); - - switch (_current) - { - case 0 : - menu_enable(FALSE); - { - const bool on = main_app().firm_change_enabled(); - xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, on); - xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, on); - } - break; - case 1 : - xvt_menu_set_item_enabled(TASK_WIN, M_FILE_QUIT, FALSE); - xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, FALSE); - xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, FALSE); - default: - _window[_current-1]->deactivate(); break; - } - - _window[_current] = m; -} - - -void TWindow_manager::unreg(const TWindow* m) -{ -#ifdef DBG - if (m != cur_win()) - { - yesnofatal_box("E' successo un casino nel Window Manager"); - return; - } -#endif - - _current--; - - if (_current < 0) - { - menu_enable(TRUE); - } - else - { - cur_win()->activate(); - - xvt_menu_set_item_enabled(TASK_WIN, M_FILE_QUIT, _current == 0); - - const bool cf = _current == 0 && main_app().firm_change_enabled(); - xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, cf); - xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, cf); - - xvt_menu_update(TASK_WIN); - cur_win()->set_focus(); - } -} - -void close_all_dialogs() -{ - WinManager.destroy(); -} - -bool can_close() -{ - return WinManager.can_close(); -} - -WINDOW cur_win() -{ - const TWindow* w = WinManager.cur_win(); - return w ? w->win() : NULL_WIN; -} - -/////////////////////////////////////////////////////////// -// TImage -/////////////////////////////////////////////////////////// - - -// Setta l'immagine e le sue dimensioni (cancella immagine precedente) -// Certified 99% -XVT_IMAGE TImage::set(XVT_IMAGE i) -{ - if (_image) - xvt_image_destroy(_image); - _image = i; - if (i) - { - _src.left = _src.top = 0; - xvt_image_get_dimensions(i, &_src.right, &_src.bottom); - _dst = _src; - } - return _image; -} - -// Certified 100% -XVT_IMAGE TImage::load(const char* n) -{ - WinManager.free_handle(); - XVT_IMAGE i = xvt_image_read_bmp((char*)n); - WinManager.lock_handle(); - - if (i != NULL) set(i); - return i; -} - -// Certified 100% -XVT_IMAGE TImage::load(short id) -{ - return set(xvt_res_get_image(id)); -} - - -// Certified 100% -TImage::TImage(const char* n) : _image(NULL) -{ - if (n && *n) load(n); -} - -// Certified 100% -TImage::TImage(short id) : _image(NULL) -{ - if (id > 0) load(id); -} - -// Certified 90% -TImage::TImage(const TImage& im, short w, short h) : _image(NULL) -{ - const XVT_IMAGE_FORMAT fmt = xvt_image_get_format(im._image); - set(xvt_image_create(fmt, w, h, NULL)); - - if (ok()) - { - if (fmt == XVT_IMAGE_CL8) - { - const short colors = xvt_image_get_ncolors(im._image); - xvt_image_set_ncolors(_image, colors); - for (short c = 0; c < colors; c++) - xvt_image_set_clut(_image, c, xvt_image_get_clut((XVT_IMAGE)im._image, c)); - } - xvt_image_transfer(_image, (XVT_IMAGE)im._image, &_src, (RCT*)&im._src); - } -} - -// Certified 100% -TImage::~TImage() -{ - if (_image != NULL) - xvt_image_destroy(_image); -} - -// Certified 100% -void TImage::set_pos(int x, int y) -{ - _dst = _src; - xvt_rect_offset(&_dst, x, y); -} - -// Certified 100% -void TImage::draw(WINDOW w) const -{ - xvt_dwin_draw_image(w, _image, (RCT*)&_dst, (RCT*)&_src); -} - -// Certified 100% -void TImage::draw(WINDOW w, int x, int y) const -{ - RCT dst = _src; - xvt_rect_offset(&dst, x, y); - xvt_dwin_draw_image(w, _image, &dst, (RCT*)&_src); -} - -// Certified 100% -void TImage::draw(WINDOW w, const RCT& dst) const -{ - xvt_dwin_draw_image(w, _image, (RCT*)&dst, (RCT*)&_src); -} - -// Certified 100% -void TImage::draw(WINDOW w, const RCT& dst, const RCT& src) const -{ - xvt_dwin_draw_image(w, _image, (RCT*)&dst, (RCT*)&src); -} - -// Certified 99% -void TImage::set_palette(WINDOW w) const -{ - XVT_PALETTE p = xvt_palet_create(XVT_PALETTE_USER, NULL); - xvt_palet_add_colors_from_image(p, _image); - xvt_vobj_set_palet(w, p); - xvt_palet_destroy(p); -} - -// Certified 100% -void TImage::set_clut(byte n, COLOR c) -{ - if (xvt_image_get_format(_image) == XVT_IMAGE_CL8) - xvt_image_set_clut(_image, n, c); -} - -// Certified 99% -void TImage::convert_to_default_colors() -{ - if (MASK_BACK_COLOR != COLOR_DKCYAN && xvt_image_get_format(_image) == XVT_IMAGE_CL8) - { - short dx, dy; xvt_image_get_dimensions(_image, &dx, &dy); - for (short y = 0; y < dy; y++) for (short x = 0; x < dx; x++) - { - const COLOR c = xvt_image_get_pixel(_image, x, y); - switch (c) - { - case COLOR_DKCYAN & 0x00FFFFFF: - xvt_image_set_pixel(_image, x, y, MASK_BACK_COLOR); break; - case COLOR_CYAN & 0x00FFFFFF: - xvt_image_set_pixel(_image, x, y, MASK_LIGHT_COLOR); break; - case COLOR_GRAY & 0x00FFFFFF: - xvt_image_set_pixel(_image, x, y, MASK_DARK_COLOR); break; - default: - break; - } - } - } -} - -/////////////////////////////////////////////////////////// -// TWindow -/////////////////////////////////////////////////////////// - -DRAW_CTOOLS TWindow::_ct; -bool TWindow::_ctools_saved; - - -TWindow::TWindow() -: _win(NULL_WIN), _open(FALSE), _modal(FALSE), _active(TRUE), - _running(FALSE), _pixmap(FALSE), _lastkey(0) -{} - - -long XVT_CALLCONV1 TWindow::window_handler(WINDOW win, EVENT* ep) -{ - TWindow* w = (TWindow*)xvt_vobj_get_data(win); - CHECK(w != NULL, "Invalid window"); - w->handler(win, ep); - - return 0L; -} - - -WINDOW TWindow::create(short x, short y, short dx, short dy, - const char* title, long flags, WIN_TYPE wt, - WINDOW parent, int menu) -{ - if (menu == 0) flags |= WSF_NO_MENUBAR; - - if (parent == NULL_WIN) parent = TASK_WIN; - if (parent == TASK_WIN) flags |= WSF_INVISIBLE; - - _win = xvt_create_window( - wt, - x, y, dx, dy, - title, - menu, parent, - flags, - window_handler, - PTR_LONG(this) - ); - - CHECK(_win, "Can't create a window"); - - return _win; -} - - -TWindow::~TWindow() -{ - if (_win != NULL_WIN) - { - xvt_vobj_destroy(_win); - _win = NULL_WIN; - } -} - - -void TWindow::open() -{ - CHECK(win() != NULL_WIN, "Can't open a NULL window"); - xvt_vobj_set_visible(win(), _open = TRUE); - xvt_scr_set_focus_vobj(win()); - xvt_vobj_raise(win()); -} - - -void TWindow::open_modal() -{ - set_modal(TRUE); - _open = TRUE; - open(); - - WinManager.reg(this); -} - - -void TWindow::close() -{ - CHECK(_win != NULL_WIN, "Can't close a NULL window"); - xvt_vobj_set_visible(_win, _open = FALSE); -} - - -void TWindow::close_modal() -{ - WinManager.unreg(this); - close(); - _open = FALSE; -} - -bool TWindow::stop_run(KEY key) -{ - _running = FALSE; - _lastkey = key; - return TRUE; -} - -bool TWindow::can_be_closed() const -{ - const bool ok = !is_modal(); - if (!ok) - error_box("Chiudere la finestra attiva prima di uscire dal programma"); - return ok; -} - -KEY TWindow::run() -{ - const bool was_open = is_open(); - - start_run(); - _running = TRUE; - - if (!was_open) open_modal(); - else open(); - - while (_running) - do_events(); - - if (!was_open) close_modal(); - - return last_key(); -} - -void TWindow::handler(WINDOW win, EVENT* ep) -{ - switch(ep->type) - { - case E_CLOSE: - stop_run(K_ESC); - break; - case E_UPDATE: - xvt_dwin_clear(win, NORMAL_BACK_COLOR); - update(); - break; - case E_CHAR: - on_key(e_char_to_key(ep)); - break; - case E_DESTROY: - _win = NULL_WIN; - break; - default: - break; - } -} - -void TWindow::on_idle() -{ - // Non c'e' niente da fare qui, ma non si puo' mai sapere -} - - -TPoint TWindow::size() const -{ - RCT r; - xvt_vobj_get_client_rect(win() ? win() : TASK_WIN, &r); - return TPoint(r.right / CHARX, r.bottom / CHARY); -} - -WINDOW TWindow::parent() const -{ - return xvt_vobj_get_parent(win()); -} - - -void TWindow::set_focus() -{ - if (_win) - { - xvt_scr_set_focus_vobj(_win); - xvt_vobj_raise(_win); - } -} - - -void TWindow::iconize() const -{ -#if XVTWS != WMWS - HWND hwnd = (HWND)xvt_vobj_get_attr(win(), ATTR_NATIVE_WINDOW); - ShowWindow(hwnd, SW_MINIMIZE); -#endif -} - -void TWindow::maximize() const -{ -#if XVTWS != WMWS - HWND hwnd = (HWND)xvt_vobj_get_attr(win(), ATTR_NATIVE_WINDOW); - ShowWindow(hwnd, SW_SHOWMAXIMIZED); -#else - RCT r; xvt_rect_set(&r, 1,1,79,23); - xvt_vobj_move(win(),&r); -#endif -} - -void TWindow::activate(bool on) -{ - xvt_vobj_set_enabled(win(), _active = on); -} - - -void TWindow::set_caption(const char* title) -{ - xvt_vobj_set_title(win(), (char*)title); -} - - -const char* TWindow::get_caption() const -{ - char* title = &__tmp_string[512]; - xvt_vobj_get_title(win(), title, 80); - return title; -} - - -void TWindow::force_update() -{ - if (win() != NULL_WIN) - xvt_dwin_invalidate_rect(win(), NULL); -} - - -bool TWindow::save_ctools() -{ - if (_ctools_saved == FALSE) - { - xvt_dwin_get_draw_ctools(win(), &_ct); - return _ctools_saved = TRUE; - } - return FALSE; -} - - -bool TWindow::restore_ctools() -{ - if (_ctools_saved) - { - xvt_dwin_set_draw_ctools(win(), &_ct); - _ctools_saved = FALSE; - return TRUE; - } - return FALSE; -} - - -void TWindow::set_color(COLOR fore, COLOR back) -{ - xvt_dwin_set_fore_color(win(), fore); - xvt_dwin_set_back_color(win(), back); -} - - -void TWindow::set_pen(COLOR color, int width, PAT_STYLE pat, PEN_STYLE style) -{ - CPEN pen; - - pen.width = width; - pen.pat = pat; - pen.style = style; - pen.color = color; - - xvt_dwin_set_cpen(win(), &pen); -} - - -void TWindow::hide_pen() -{ - xvt_dwin_set_std_cpen(win(), TL_PEN_HOLLOW); -} - - -void TWindow::set_brush(COLOR color, PAT_STYLE pat) -{ - CBRUSH brush = { pat, color }; - xvt_dwin_set_cbrush(win(), &brush); -} - - -void TWindow::hide_brush() -{ - CBRUSH brush = { PAT_HOLLOW, COLOR_WHITE }; - xvt_dwin_set_cbrush(win(), &brush); -} - - -HIDDEN void swap(short& a, short& b) -{ - short tmp = a; - a = b; - b = tmp; -} - - -void TWindow::frame(short left, short top, short right, short bottom, - int flag) -{ - if (left > right) swap(left, right); - if (top > bottom) swap(top, bottom); - - const bool saved = flag && save_ctools(); - - if (flag & 1) hide_pen(); - if (flag & 2) hide_brush(); - if (flag & 4) - { - set_mode(M_XOR); - set_brush(COLOR_BLACK); // Needed for Windows - } - - const PNT f = log2dev(left,top); - const PNT t = log2dev(right,bottom); - RCT r; - r.top = f.v; r.left = f.h; - r.bottom = t.v; r.right = t.h; - -#if XVTWS != WMWS - if (flag & 2) - { - r.left += CHARX>>1; r.top += CHARY>>1; - r.right-= CHARX>>1; r.bottom -= CHARY>>1; - } -#endif - - xvt_dwin_draw_rect(win(), &r); - - if (saved) restore_ctools(); -} - - -void TWindow::rect(short left, short top, short right, short bottom) -{ - frame(left, top, right, bottom, 2); -} - - -void TWindow::bar(short left, short top, short right, short bottom) -{ - frame(left, top, right, bottom, 1); -} - - -void TWindow::invert_bar(short left, short top, short right, short bottom) -{ - frame(left, top, right, bottom, 5); -} - -void TWindow::set_opaque_text(bool o) -{ - DRAW_CTOOLS ct; - xvt_dwin_get_draw_ctools(win(), &ct); - ct.opaque_text = o; - xvt_dwin_set_draw_ctools(win(), &ct); -} - -void TWindow::set_font(const char* family, int style, int dim) -{ - xvt_set_font(win(), family, style, dim); -} - -PNT TWindow::log2dev(long x, long y) const -{ - PNT pnt; - - pnt.h = (int)x; - pnt.v = (int)y; - - if (!_pixmap) - { - pnt.h *= CHARX; - pnt.v *= CHARY; - } - - return pnt; -} - -TPoint TWindow::dev2log(const PNT& p) const -{ - TPoint pnt(_pixmap ? p.h : p.h/CHARX, _pixmap ? p.v : p.v/CHARY); - return pnt; -} - - -void TWindow::stringat(short x, short y, const char* str) -{ - PNT pnt = log2dev(x,y); -#if XVTWS != WMWS - pnt.v += BASEY; -#endif - - xvt_dwin_draw_text(win(), pnt.h, pnt.v, (char *)str, -1); -} - -void TWindow::printat(short x, short y, const char* fmt, ...) -{ - va_list argptr; - va_start(argptr, fmt); - vsprintf(__tmp_string, fmt, argptr); - va_end(argptr); - stringat(x, y, __tmp_string); -} - - -void TWindow::line(short x0, short y0, short x1, short y1) -{ - PNT f = log2dev(x0,y0); - PNT t = log2dev(x1,y1); - -#if XVTWS != WMWS - if (f.h == 0) f.h = -CHARX; else f.h += CHARX>>1; - if (f.v == 0) f.v = -CHARY; else f.v += CHARY>>1; - if (t.h == 0) t.h = -CHARX; else t.h += CHARX>>1; - if (t.v == 0) t.v = -CHARY; else t.v += CHARY>>1; -#endif - - xvt_dwin_draw_set_pos(_win, f); - xvt_dwin_draw_line(_win, t); -} - -void TWindow::icon(short x0, short y0, int iconid) -{ -#if XVTWS == WMWS - bar(x0, y0, x0+1, y0+1); -#else - PNT f = log2dev(x0,y0); - if (iconid < 0) iconid = ICON_RSRC; - xvt_dwin_draw_icon(win(), f.h, f.v, iconid); -#endif -} - -void TWindow::clear(COLOR color) -{ xvt_dwin_clear(win(), color); } - -void TWindow::set_mode(DRAW_MODE mode) -{ xvt_dwin_set_draw_mode(win(), mode); } - - -bool TWindow::add_menu(TString_array& menu, MENU_TAG id, bool force) -{ - CHECK(menu.items() > 0, "TWindow::add_menu: no menus to add"); - - // get menu tree - MENU_ITEM* menubar = xvt_menu_get_tree(win()); - - if (id == 0) // add to menubar - { - // count menus - for (int nmen = 0; menubar[nmen].tag != 0; nmen++); - menubar = (MENU_ITEM*)xvt_mem_realloc((DATA_PTR)menubar, - sizeof(MENU_ITEM)*(nmen+menu.items()+1)); - // zero new - xvt_mem_rep((DATA_PTR)&menubar[nmen], "\0", 1, sizeof(MENU_ITEM)*(menu.items()+1)); - - // add new menus - for (int i = 0; i < menu.items(); i++) - set_menu_item(menubar[nmen+i], menu.row(i)); - } - else // add to menu - { - MENU_ITEM* father = find_menu_item(menubar, id, TRUE); - CHECK(father != NULL, "TWindow::add_menu: you're adding to a NULL menu item"); - - // count children - for (int nmen = 0; father->child != NULL && father->child[nmen].tag != 0; nmen++); - father->child = (MENU_ITEM*)xvt_mem_realloc((DATA_PTR)father->child, - sizeof(MENU_ITEM)*(nmen+menu.items()+1)); - // zero new - xvt_mem_rep((DATA_PTR)&(father->child[nmen]), "\0", 1, sizeof(MENU_ITEM)*(menu.items()+1)); - - // add new menus - for (int i = 0; i < menu.items(); i++) - set_menu_item(father->child[nmen+i], menu.row(i)); - } - - xvt_menu_set_tree(win(), menubar); - xvt_res_free_menu_tree(menubar); - - return TRUE; -} - -bool TWindow::remove_menu(MENU_TAG id) -{ - MENU_ITEM* menubar = xvt_menu_get_tree(win()); - if (remove_menu_item(menubar, id, TRUE)) - { - xvt_menu_set_tree(win(),menubar); - xvt_res_free_menu_tree(menubar); - } - return TRUE; -} - - -/////////////////////////////////////////////////////////// -// TTemp_window -/////////////////////////////////////////////////////////// - -TTemp_window::TTemp_window(WINDOW w) -{ - set_win(w); -} - -TTemp_window::~TTemp_window() -{ - set_win(NULL_WIN); // I don't want to be closed! -} - -/////////////////////////////////////////////////////////// -// TScroll_window -/////////////////////////////////////////////////////////// - -TScroll_window::TScroll_window() -: _origin(0, 0), _max(0, 0), _shift(0), _autoscroll(TRUE), - _has_hscroll(TRUE), _has_vscroll(TRUE) -{ -} - -WINDOW TScroll_window::create(short x, short y, short dx, short dy, - const char* title, long flags, WIN_TYPE wt, WINDOW parent, int menu) -{ - _has_hscroll = (flags & WSF_HSCROLL) != 0; - _has_vscroll = (flags & WSF_VSCROLL) != 0 ; - return TWindow::create(x, y, dx, dy, title, flags, wt, parent, menu); -} - -PNT TScroll_window::log2dev(long x, long y) const -{ - if (_autoscroll) - { - x -= _origin.x; - y -= _origin.y >> _shift; - } - return TWindow::log2dev(x,y); -} - - -void TScroll_window::set_scroll_max(long maxx, long maxy) -{ - if (_has_hscroll && maxx >= 0) - { - _max.x = maxx; - xvt_sbar_set_range(win(), HSCROLL, 0, int(maxx)); - } - if (_has_vscroll && maxy >= 0) - { - _shift = 0; - while ((maxy >> _shift) > 0x7FFF) _shift++; - _max.y = maxy; - xvt_sbar_set_range(win(), VSCROLL, 0, int(maxy >> _shift)); - } -} - -void TScroll_window::update_thumb(long x, long y) -{ - if (x >= 0 && x <= _max.x) _origin.x = x; - if (y >= 0 && y <= _max.y) _origin.y = y; - - if (_has_hscroll) - xvt_sbar_set_pos(win(), HSCROLL, int(_origin.x)); - if (_has_vscroll) - xvt_sbar_set_pos(win(), VSCROLL, int(_origin.y >> _shift)); -} - -void TScroll_window::handler(WINDOW win, EVENT* ep) -{ - bool up = FALSE; - - switch (ep->type) - { - case E_HSCROLL: - case E_VSCROLL: - { - long& pos = (ep->type == E_HSCROLL) ? _origin.x : _origin.y; - long& max = (ep->type == E_HSCROLL) ? _max.x : _max.y; - short pag = (ep->type == E_HSCROLL) ? columns()/2+1 : rows()/2+1; - switch(ep->v.scroll.what) - { - case SC_PAGE_UP: - pos -= pag; - up = TRUE; - break; - case SC_LINE_UP: - pos--; - up = TRUE; - break; - case SC_PAGE_DOWN: - pos += pag; - up = TRUE; - break; - case SC_LINE_DOWN: - pos++; - up = TRUE; - break; - case SC_THUMB: - pos = ep->v.scroll.pos; - up = TRUE; - break; - default: - break; - } - if (pos < 0) pos = 0; - if (pos > max) pos = max; - } - break; -default: - break; -} - - if (up) - { - update_thumb(); - force_update(); - } - - TWindow::handler(win, ep); -} - -bool TScroll_window::on_key(KEY key) -{ - switch(key) - { - case K_LHOME: - update_thumb(0,0); - force_update(); - break; - case K_LEND: - update_thumb(0,range().y); - force_update(); - break; - case K_TAB: - update_thumb(origin().x+8); - force_update(); - break; - case K_BTAB: - { - long x = origin().x-8; - if (x < 0) x = 0; - update_thumb(x); - force_update(); - } - break; - case K_UP: - case K_DOWN: - case K_PREV: - case K_NEXT: - case K_LEFT: - case K_RIGHT: - dispatch_e_scroll(win(), key); - break; - default: - break; - } - - return TWindow::on_key(key); -} - +#include +#include +#include +#include + +#define __WINDOW_CPP +#include +#include + +#if XVT_OS == XVT_OS_WIN +#define STRICT +#include +#endif + +HIDDEN MENU_ITEM* find_menu_item(MENU_ITEM* menu, MENU_TAG id, bool ismbar) +{ + MENU_ITEM* fnd = NULL; + MENU_ITEM* mn = ismbar ? &menu[0] : &menu->child[0]; + + for (int m = 0; mn != NULL && mn->tag != 0; m++, mn=ismbar?&menu[m]:&menu->child[m]) + { + fnd = mn->tag == id ? mn : find_menu_item(mn, id, FALSE); + if (fnd != NULL) break; + } + + return fnd; +} + +HIDDEN bool remove_menu_item(MENU_ITEM* menu, MENU_TAG id, bool ismbar) +{ + MENU_ITEM* mn = ismbar ? &menu[0] : &menu->child[0]; + + for (int m = 0; mn != NULL && mn->tag != 0; m++, mn=ismbar?&menu[m]:&menu->child[m]) + { + if (mn->tag == id) + { + do + { + xvt_mem_rep((DATA_PTR)mn, (DATA_PTR)(mn+1), sizeof(MENU_ITEM), 1); + mn++; + } + while (mn->tag != 0); + return TRUE; + } + else if (remove_menu_item(mn, id, FALSE)) + return TRUE; + } + + return FALSE; +} + + +HIDDEN void set_menu_item(MENU_ITEM& m, TToken_string& tt) +{ + MENU_TAG tag = tt.get_int(0); + TString flag = tt.items() <= 2 ? "": tt.get(2); + char* text = NULL; + if (strlen(tt.get(1)) > 0) + { + text = (char*)xvt_mem_alloc(strlen(tt.get(1)) + 1); + strcpy(text, tt.get(1)); + } + + m.tag = tag; + m.text = text; + m.enabled = !(flag.find('D') != -1); + m.checkable = flag.find('C') != -1 || flag.find('c') != -1; + m.checked = flag.find('c') != -1; + m.separator = text == NULL; +} + +/////////////////////////////////////////////////////////// +// TWindow_manager +/////////////////////////////////////////////////////////// +// @C +// Classe TWindow_manager +// +// @END + +class TWindow_manager +{ + // @DPRIV + enum { MAX_WIN = 8 }; // Max number of modal windows + + TWindow* _window[MAX_WIN]; // Stack of active windows + char _current; // Stack pointer + void menu_enable(bool) const; // Abilita o disabilita il menu della task window + + FILE* _lowhandle; + +public: + // @FPUB + TWindow_manager(); // Costruttore + ~TWindow_manager(); + + void free_handle(); + void lock_handle(); + + void reg(TWindow* m); + // Registra la finestra corrente + void unreg(const TWindow* m); + // De-registra la finestra corrente + + TWindow* cur_win() const { return (_current < 0) ? NULL : _window[_current]; } // Ritorna il puntatore alla finestra corrente + void destroy(); + bool can_close() const; +} WinManager; + + +TWindow_manager::TWindow_manager() : _current(-1), _lowhandle(NULL) +{ + lock_handle(); +} + +TWindow_manager::~TWindow_manager() +{ + destroy(); + free_handle(); +} + +void TWindow_manager::destroy() +{ + while (_current >= 0) + { + TWindow* w = cur_win(); + w->stop_run(K_FORCE_CLOSE); + w->close_modal(); + } +} + +void TWindow_manager::lock_handle() +{ + CHECK(_lowhandle == NULL, "Can't relock low handle"); + _lowhandle = fopen("bagn001a.msk", "r"); + CHECK(_lowhandle != NULL, "Can't lock low handle"); +} + +void TWindow_manager::free_handle() +{ + CHECK(_lowhandle, "Can't unlock low handle"); + fclose(_lowhandle); + _lowhandle = NULL; +} + + +bool TWindow_manager::can_close() const +{ + bool ok = TRUE; + if (_current >= 0) + ok = cur_win()->can_be_closed(); + return ok; +} + + +// Dis/abilitazione del menu principale +HIDDEN void xvt_menu_enable(MENU_ITEM* m, bool on) +{ + while (m->tag) + { + switch(m->tag) + { + case MENU_FILE: // Leave it as is + case -1: // Separator + break; + default: + xvt_menu_set_item_enabled(TASK_WIN, m->tag, on); + break; + } + m++; + } +} + + +void TWindow_manager::menu_enable(bool on) const +{ + MENU_ITEM *mi = xvt_menu_get_tree(TASK_WIN); + xvt_menu_enable(mi, on); + xvt_menu_update(TASK_WIN); + xvt_res_free_menu_tree(mi); +} + + +void TWindow_manager::reg(TWindow* m) +{ + _current++; + CHECK(_current < MAX_WIN, "Too many windows"); + + switch (_current) + { + case 0 : + menu_enable(FALSE); + { + const bool on = main_app().firm_change_enabled(); + xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, on); + xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, on); + } + break; + case 1 : + xvt_menu_set_item_enabled(TASK_WIN, M_FILE_QUIT, FALSE); + xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, FALSE); + xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, FALSE); + default: + _window[_current-1]->deactivate(); break; + } + + _window[_current] = m; +} + + +void TWindow_manager::unreg(const TWindow* m) +{ +#ifdef DBG + if (m != cur_win()) + { + yesnofatal_box("E' successo un casino nel Window Manager"); + return; + } +#endif + + _current--; + + if (_current < 0) + { + menu_enable(TRUE); + } + else + { + cur_win()->activate(); + + xvt_menu_set_item_enabled(TASK_WIN, M_FILE_QUIT, _current == 0); + + const bool cf = _current == 0 && main_app().firm_change_enabled(); + xvt_menu_set_item_enabled(TASK_WIN, M_FILE_NEW, cf); + xvt_menu_set_item_enabled(TASK_WIN, M_FILE_REVERT, cf); + + xvt_menu_update(TASK_WIN); + cur_win()->set_focus(); + } +} + +void close_all_dialogs() +{ + WinManager.destroy(); +} + +bool can_close() +{ + return WinManager.can_close(); +} + +WINDOW cur_win() +{ + const TWindow* w = WinManager.cur_win(); + return w ? w->win() : NULL_WIN; +} + +/////////////////////////////////////////////////////////// +// TImage +/////////////////////////////////////////////////////////// + + +// Setta l'immagine e le sue dimensioni (cancella immagine precedente) +// Certified 99% +XVT_IMAGE TImage::set(XVT_IMAGE i) +{ + if (_image) + xvt_image_destroy(_image); + _image = i; + if (i) + { + _src.left = _src.top = 0; + xvt_image_get_dimensions(i, &_src.right, &_src.bottom); + _dst = _src; + } + return _image; +} + +// Certified 100% +XVT_IMAGE TImage::load(const char* n) +{ + WinManager.free_handle(); + XVT_IMAGE i = xvt_image_read_bmp((char*)n); + WinManager.lock_handle(); + + if (i != NULL) set(i); + return i; +} + +// Certified 100% +XVT_IMAGE TImage::load(short id) +{ + return set(xvt_res_get_image(id)); +} + + +// Certified 100% +TImage::TImage(const char* n) : _image(NULL) +{ + if (n && *n) load(n); +} + +// Certified 100% +TImage::TImage(short id) : _image(NULL) +{ + if (id > 0) load(id); +} + +// Certified 90% +TImage::TImage(const TImage& im, short w, short h) : _image(NULL) +{ + const XVT_IMAGE_FORMAT fmt = xvt_image_get_format(im._image); + set(xvt_image_create(fmt, w, h, NULL)); + + if (ok()) + { + if (fmt == XVT_IMAGE_CL8) + { + const short colors = xvt_image_get_ncolors(im._image); + xvt_image_set_ncolors(_image, colors); + for (short c = 0; c < colors; c++) + xvt_image_set_clut(_image, c, xvt_image_get_clut((XVT_IMAGE)im._image, c)); + } + xvt_image_transfer(_image, (XVT_IMAGE)im._image, &_src, (RCT*)&im._src); + } +} + +// Certified 100% +TImage::~TImage() +{ + if (_image != NULL) + xvt_image_destroy(_image); +} + +// Certified 100% +void TImage::set_pos(int x, int y) +{ + _dst = _src; + xvt_rect_offset(&_dst, x, y); +} + +// Certified 100% +void TImage::draw(WINDOW w) const +{ + xvt_dwin_draw_image(w, _image, (RCT*)&_dst, (RCT*)&_src); +} + +// Certified 100% +void TImage::draw(WINDOW w, int x, int y) const +{ + RCT dst = _src; + xvt_rect_offset(&dst, x, y); + xvt_dwin_draw_image(w, _image, &dst, (RCT*)&_src); +} + +// Certified 100% +void TImage::draw(WINDOW w, const RCT& dst) const +{ + xvt_dwin_draw_image(w, _image, (RCT*)&dst, (RCT*)&_src); +} + +// Certified 100% +void TImage::draw(WINDOW w, const RCT& dst, const RCT& src) const +{ + xvt_dwin_draw_image(w, _image, (RCT*)&dst, (RCT*)&src); +} + +// Certified 99% +void TImage::set_palette(WINDOW w) const +{ + XVT_PALETTE p = xvt_palet_create(XVT_PALETTE_USER, NULL); + xvt_palet_add_colors_from_image(p, _image); + xvt_vobj_set_palet(w, p); + xvt_palet_destroy(p); +} + +// Certified 100% +void TImage::set_clut(byte n, COLOR c) +{ + if (xvt_image_get_format(_image) == XVT_IMAGE_CL8) + xvt_image_set_clut(_image, n, c); +} + +// Certified 99% +void TImage::convert_to_default_colors() +{ + if (MASK_BACK_COLOR != COLOR_DKCYAN && xvt_image_get_format(_image) == XVT_IMAGE_CL8) + { + short dx, dy; xvt_image_get_dimensions(_image, &dx, &dy); + for (short y = 0; y < dy; y++) for (short x = 0; x < dx; x++) + { + const COLOR c = xvt_image_get_pixel(_image, x, y); + switch (c) + { + case COLOR_DKCYAN & 0x00FFFFFF: + xvt_image_set_pixel(_image, x, y, MASK_BACK_COLOR); break; + case COLOR_CYAN & 0x00FFFFFF: + xvt_image_set_pixel(_image, x, y, MASK_LIGHT_COLOR); break; + case COLOR_GRAY & 0x00FFFFFF: + xvt_image_set_pixel(_image, x, y, MASK_DARK_COLOR); break; + default: + break; + } + } + } +} + +/////////////////////////////////////////////////////////// +// TWindow +/////////////////////////////////////////////////////////// + +DRAW_CTOOLS TWindow::_ct; +bool TWindow::_ctools_saved; + + +TWindow::TWindow() +: _win(NULL_WIN), _open(FALSE), _modal(FALSE), _active(TRUE), + _running(FALSE), _pixmap(FALSE), _lastkey(0) +{} + + +long XVT_CALLCONV1 TWindow::window_handler(WINDOW win, EVENT* ep) +{ + TWindow* w = (TWindow*)xvt_vobj_get_data(win); + CHECK(w != NULL, "Invalid window"); + w->handler(win, ep); + + return 0L; +} + + +WINDOW TWindow::create(short x, short y, short dx, short dy, + const char* title, long flags, WIN_TYPE wt, + WINDOW parent, int menu) +{ + if (menu == 0) flags |= WSF_NO_MENUBAR; + + if (parent == NULL_WIN) parent = TASK_WIN; + if (parent == TASK_WIN) flags |= WSF_INVISIBLE; + + _win = xvt_create_window( + wt, + x, y, dx, dy, + title, + menu, parent, + flags, + window_handler, + PTR_LONG(this) + ); + + CHECK(_win, "Can't create a window"); + + return _win; +} + + +TWindow::~TWindow() +{ + if (_win != NULL_WIN) + { + xvt_vobj_destroy(_win); + _win = NULL_WIN; + } +} + + +void TWindow::open() +{ + CHECK(win() != NULL_WIN, "Can't open a NULL window"); + xvt_vobj_set_visible(win(), _open = TRUE); + xvt_scr_set_focus_vobj(win()); + xvt_vobj_raise(win()); +} + + +void TWindow::open_modal() +{ + set_modal(TRUE); + _open = TRUE; + open(); + + WinManager.reg(this); +} + + +void TWindow::close() +{ + CHECK(_win != NULL_WIN, "Can't close a NULL window"); + xvt_vobj_set_visible(_win, _open = FALSE); +} + + +void TWindow::close_modal() +{ + WinManager.unreg(this); + close(); + _open = FALSE; +} + +bool TWindow::stop_run(KEY key) +{ + _running = FALSE; + _lastkey = key; + return TRUE; +} + +bool TWindow::can_be_closed() const +{ + const bool ok = !is_modal(); + if (!ok) + error_box("Chiudere la finestra attiva prima di uscire dal programma"); + return ok; +} + +KEY TWindow::run() +{ + const bool was_open = is_open(); + + start_run(); + _running = TRUE; + + if (!was_open) open_modal(); + else open(); + + while (_running) + do_events(); + + if (!was_open) close_modal(); + + return last_key(); +} + +void TWindow::handler(WINDOW win, EVENT* ep) +{ + switch(ep->type) + { + case E_CLOSE: + stop_run(K_ESC); + break; + case E_UPDATE: + xvt_dwin_clear(win, NORMAL_BACK_COLOR); + update(); + break; + case E_CHAR: + on_key(e_char_to_key(ep)); + break; + case E_DESTROY: + _win = NULL_WIN; + break; + default: + break; + } +} + +void TWindow::on_idle() +{ + // Non c'e' niente da fare qui, ma non si puo' mai sapere +} + + +TPoint TWindow::size() const +{ + RCT r; + xvt_vobj_get_client_rect(win() ? win() : TASK_WIN, &r); + return TPoint(r.right / CHARX, r.bottom / CHARY); +} + +WINDOW TWindow::parent() const +{ + return xvt_vobj_get_parent(win()); +} + + +void TWindow::set_focus() +{ + if (_win) + { + xvt_scr_set_focus_vobj(_win); + xvt_vobj_raise(_win); + } +} + + +void TWindow::iconize() const +{ +#if XVTWS != WMWS + HWND hwnd = (HWND)xvt_vobj_get_attr(win(), ATTR_NATIVE_WINDOW); + ShowWindow(hwnd, SW_MINIMIZE); +#endif +} + +void TWindow::maximize() const +{ +#if XVTWS != WMWS + HWND hwnd = (HWND)xvt_vobj_get_attr(win(), ATTR_NATIVE_WINDOW); + ShowWindow(hwnd, SW_SHOWMAXIMIZED); +#else + RCT r; xvt_rect_set(&r, 1,1,79,23); + xvt_vobj_move(win(),&r); +#endif +} + +void TWindow::activate(bool on) +{ + xvt_vobj_set_enabled(win(), _active = on); +} + + +void TWindow::set_caption(const char* title) +{ + xvt_vobj_set_title(win(), (char*)title); +} + + +const char* TWindow::get_caption() const +{ + char* title = &__tmp_string[512]; + xvt_vobj_get_title(win(), title, 80); + return title; +} + + +void TWindow::force_update() +{ + if (win() != NULL_WIN) + xvt_dwin_invalidate_rect(win(), NULL); +} + + +bool TWindow::save_ctools() +{ + if (_ctools_saved == FALSE) + { + xvt_dwin_get_draw_ctools(win(), &_ct); + return _ctools_saved = TRUE; + } + return FALSE; +} + + +bool TWindow::restore_ctools() +{ + if (_ctools_saved) + { + xvt_dwin_set_draw_ctools(win(), &_ct); + _ctools_saved = FALSE; + return TRUE; + } + return FALSE; +} + + +void TWindow::set_color(COLOR fore, COLOR back) +{ + xvt_dwin_set_fore_color(win(), fore); + xvt_dwin_set_back_color(win(), back); +} + + +void TWindow::set_pen(COLOR color, int width, PAT_STYLE pat, PEN_STYLE style) +{ + CPEN pen; + + pen.width = width; + pen.pat = pat; + pen.style = style; + pen.color = color; + + xvt_dwin_set_cpen(win(), &pen); +} + + +void TWindow::hide_pen() +{ + xvt_dwin_set_std_cpen(win(), TL_PEN_HOLLOW); +} + + +void TWindow::set_brush(COLOR color, PAT_STYLE pat) +{ + CBRUSH brush = { pat, color }; + xvt_dwin_set_cbrush(win(), &brush); +} + + +void TWindow::hide_brush() +{ + CBRUSH brush = { PAT_HOLLOW, COLOR_WHITE }; + xvt_dwin_set_cbrush(win(), &brush); +} + + +HIDDEN void swap(short& a, short& b) +{ + short tmp = a; + a = b; + b = tmp; +} + + +void TWindow::frame(short left, short top, short right, short bottom, + int flag) +{ + if (left > right) swap(left, right); + if (top > bottom) swap(top, bottom); + + const bool saved = flag && save_ctools(); + + if (flag & 1) hide_pen(); + if (flag & 2) hide_brush(); + if (flag & 4) + { + set_mode(M_XOR); + set_brush(COLOR_BLACK); // Needed for Windows + } + + const PNT f = log2dev(left,top); + const PNT t = log2dev(right,bottom); + RCT r; + r.top = f.v; r.left = f.h; + r.bottom = t.v; r.right = t.h; + +#if XVTWS != WMWS + if (flag & 2) + { + r.left += CHARX>>1; r.top += CHARY>>1; + r.right-= CHARX>>1; r.bottom -= CHARY>>1; + } +#endif + + xvt_dwin_draw_rect(win(), &r); + + if (saved) restore_ctools(); +} + + +void TWindow::rect(short left, short top, short right, short bottom) +{ + frame(left, top, right, bottom, 2); +} + + +void TWindow::bar(short left, short top, short right, short bottom) +{ + frame(left, top, right, bottom, 1); +} + + +void TWindow::invert_bar(short left, short top, short right, short bottom) +{ + frame(left, top, right, bottom, 5); +} + +void TWindow::set_opaque_text(bool o) +{ + DRAW_CTOOLS ct; + xvt_dwin_get_draw_ctools(win(), &ct); + ct.opaque_text = o; + xvt_dwin_set_draw_ctools(win(), &ct); +} + +void TWindow::set_font(const char* family, int style, int dim) +{ + xvt_set_font(win(), family, style, dim); +} + +PNT TWindow::log2dev(long x, long y) const +{ + PNT pnt; + + pnt.h = (int)x; + pnt.v = (int)y; + + if (!_pixmap) + { + pnt.h *= CHARX; + pnt.v *= CHARY; + } + + return pnt; +} + +TPoint TWindow::dev2log(const PNT& p) const +{ + TPoint pnt(_pixmap ? p.h : p.h/CHARX, _pixmap ? p.v : p.v/CHARY); + return pnt; +} + + +void TWindow::stringat(short x, short y, const char* str) +{ + PNT pnt = log2dev(x,y); +#if XVTWS != WMWS + pnt.v += BASEY; +#endif + + xvt_dwin_draw_text(win(), pnt.h, pnt.v, (char *)str, -1); +} + +void TWindow::printat(short x, short y, const char* fmt, ...) +{ + va_list argptr; + va_start(argptr, fmt); + vsprintf(__tmp_string, fmt, argptr); + va_end(argptr); + stringat(x, y, __tmp_string); +} + + +void TWindow::line(short x0, short y0, short x1, short y1) +{ + PNT f = log2dev(x0,y0); + PNT t = log2dev(x1,y1); + +#if XVTWS != WMWS + if (f.h == 0) f.h = -CHARX; else f.h += CHARX>>1; + if (f.v == 0) f.v = -CHARY; else f.v += CHARY>>1; + if (t.h == 0) t.h = -CHARX; else t.h += CHARX>>1; + if (t.v == 0) t.v = -CHARY; else t.v += CHARY>>1; +#endif + + xvt_dwin_draw_set_pos(_win, f); + xvt_dwin_draw_line(_win, t); +} + +void TWindow::icon(short x0, short y0, int iconid) +{ +#if XVTWS == WMWS + bar(x0, y0, x0+1, y0+1); +#else + PNT f = log2dev(x0,y0); + if (iconid < 0) iconid = ICON_RSRC; + xvt_dwin_draw_icon(win(), f.h, f.v, iconid); +#endif +} + +void TWindow::clear(COLOR color) +{ xvt_dwin_clear(win(), color); } + +void TWindow::set_mode(DRAW_MODE mode) +{ xvt_dwin_set_draw_mode(win(), mode); } + + +bool TWindow::add_menu(TString_array& menu, MENU_TAG id, bool force) +{ + CHECK(menu.items() > 0, "TWindow::add_menu: no menus to add"); + + // get menu tree + MENU_ITEM* menubar = xvt_menu_get_tree(win()); + + if (id == 0) // add to menubar + { + // count menus + for (int nmen = 0; menubar[nmen].tag != 0; nmen++); + menubar = (MENU_ITEM*)xvt_mem_realloc((DATA_PTR)menubar, + sizeof(MENU_ITEM)*(nmen+menu.items()+1)); + // zero new + xvt_mem_rep((DATA_PTR)&menubar[nmen], "\0", 1, sizeof(MENU_ITEM)*(menu.items()+1)); + + // add new menus + for (int i = 0; i < menu.items(); i++) + set_menu_item(menubar[nmen+i], menu.row(i)); + } + else // add to menu + { + MENU_ITEM* father = find_menu_item(menubar, id, TRUE); + CHECK(father != NULL, "TWindow::add_menu: you're adding to a NULL menu item"); + + // count children + for (int nmen = 0; father->child != NULL && father->child[nmen].tag != 0; nmen++); + father->child = (MENU_ITEM*)xvt_mem_realloc((DATA_PTR)father->child, + sizeof(MENU_ITEM)*(nmen+menu.items()+1)); + // zero new + xvt_mem_rep((DATA_PTR)&(father->child[nmen]), "\0", 1, sizeof(MENU_ITEM)*(menu.items()+1)); + + // add new menus + for (int i = 0; i < menu.items(); i++) + set_menu_item(father->child[nmen+i], menu.row(i)); + } + + xvt_menu_set_tree(win(), menubar); + xvt_res_free_menu_tree(menubar); + + return TRUE; +} + +bool TWindow::remove_menu(MENU_TAG id) +{ + MENU_ITEM* menubar = xvt_menu_get_tree(win()); + if (remove_menu_item(menubar, id, TRUE)) + { + xvt_menu_set_tree(win(),menubar); + xvt_res_free_menu_tree(menubar); + } + return TRUE; +} + + +/////////////////////////////////////////////////////////// +// TTemp_window +/////////////////////////////////////////////////////////// + +TTemp_window::TTemp_window(WINDOW w) +{ + set_win(w); +} + +TTemp_window::~TTemp_window() +{ + set_win(NULL_WIN); // I don't want to be closed! +} + +/////////////////////////////////////////////////////////// +// TScroll_window +/////////////////////////////////////////////////////////// + +TScroll_window::TScroll_window() +: _origin(0, 0), _max(0, 0), _shift(0), _autoscroll(TRUE), + _has_hscroll(TRUE), _has_vscroll(TRUE) +{ +} + +WINDOW TScroll_window::create(short x, short y, short dx, short dy, + const char* title, long flags, WIN_TYPE wt, WINDOW parent, int menu) +{ + _has_hscroll = (flags & WSF_HSCROLL) != 0; + _has_vscroll = (flags & WSF_VSCROLL) != 0 ; + return TWindow::create(x, y, dx, dy, title, flags, wt, parent, menu); +} + +PNT TScroll_window::log2dev(long x, long y) const +{ + if (_autoscroll) + { + x -= _origin.x; + y -= _origin.y >> _shift; + } + return TWindow::log2dev(x,y); +} + + +void TScroll_window::set_scroll_max(long maxx, long maxy) +{ + if (_has_hscroll && maxx >= 0) + { + _max.x = maxx; + xvt_sbar_set_range(win(), HSCROLL, 0, int(maxx)); + } + if (_has_vscroll && maxy >= 0) + { + _shift = 0; + while ((maxy >> _shift) > 0x7FFF) _shift++; + _max.y = maxy; + xvt_sbar_set_range(win(), VSCROLL, 0, int(maxy >> _shift)); + } +} + +void TScroll_window::update_thumb(long x, long y) +{ + if (x >= 0 && x <= _max.x) _origin.x = x; + if (y >= 0 && y <= _max.y) _origin.y = y; + + if (_has_hscroll) + xvt_sbar_set_pos(win(), HSCROLL, int(_origin.x)); + if (_has_vscroll) + xvt_sbar_set_pos(win(), VSCROLL, int(_origin.y >> _shift)); +} + +void TScroll_window::handler(WINDOW win, EVENT* ep) +{ + bool up = FALSE; + + switch (ep->type) + { + case E_HSCROLL: + case E_VSCROLL: + { + long& pos = (ep->type == E_HSCROLL) ? _origin.x : _origin.y; + long& max = (ep->type == E_HSCROLL) ? _max.x : _max.y; + short pag = (ep->type == E_HSCROLL) ? columns()/2+1 : rows()/2+1; + switch(ep->v.scroll.what) + { + case SC_PAGE_UP: + pos -= pag; + up = TRUE; + break; + case SC_LINE_UP: + pos--; + up = TRUE; + break; + case SC_PAGE_DOWN: + pos += pag; + up = TRUE; + break; + case SC_LINE_DOWN: + pos++; + up = TRUE; + break; + case SC_THUMB: + pos = ep->v.scroll.pos; + up = TRUE; + break; + default: + break; + } + if (pos < 0) pos = 0; + if (pos > max) pos = max; + } + break; +default: + break; +} + + if (up) + { + update_thumb(); + force_update(); + } + + TWindow::handler(win, ep); +} + +bool TScroll_window::on_key(KEY key) +{ + switch(key) + { + case K_LHOME: + update_thumb(0,0); + force_update(); + break; + case K_LEND: + update_thumb(0,range().y); + force_update(); + break; + case K_TAB: + update_thumb(origin().x+8); + force_update(); + break; + case K_BTAB: + { + long x = origin().x-8; + if (x < 0) x = 0; + update_thumb(x); + force_update(); + } + break; + case K_UP: + case K_DOWN: + case K_PREV: + case K_NEXT: + case K_LEFT: + case K_RIGHT: + dispatch_e_scroll(win(), key); + break; + default: + break; + } + + return TWindow::on_key(key); +} +