diff --git a/ba/bainst46.cpp b/ba/bainst46.cpp new file mode 100755 index 000000000..c320b3987 --- /dev/null +++ b/ba/bainst46.cpp @@ -0,0 +1,20 @@ +#include + +#include "bainstlib.h" + +class TInstall_HA : public TInstallmodule_app +{ + +protected: + virtual int module_number() const { return HAAUT; } + +public: + virtual ~TInstall_HA () {} +}; + +int bainst46(int argc, char** argv) +{ + TInstall_HA app; + app.run(argc, argv); + return 0; +} diff --git a/ha/ha0.cpp b/ha/ha0.cpp new file mode 100755 index 000000000..867514175 --- /dev/null +++ b/ha/ha0.cpp @@ -0,0 +1,30 @@ +#include +#include + +#include "ha0.h" + +#define usage "Error - usage : %s -{1|2|3|4}" + +int main(int argc,char** argv) + +{ + int rt = -1 ; + const int r = (argc > 1) ? atoi(&argv[1][1]) : -1; + + switch (r) + { + case 1: + rt = ha0200(argc, argv) ; break; //configurazione modulo Hardy + break; + case 2: + rt = ha0300(argc, argv) ; break; //gestione documenti premio (contratti premio) + break; + //case 3: + //rt = ha0400(argc,argv) ; break; //contabilizzazione contratti premio Hardy + case 4: + rt = ha0500(argc,argv) ; break; //elaborazione documenti Hardy + default: + error_box(usage, argv[0]) ; break; + } + return rt; +} diff --git a/ha/ha0.h b/ha/ha0.h new file mode 100755 index 000000000..3cfeea74a --- /dev/null +++ b/ha/ha0.h @@ -0,0 +1,4 @@ +int ha0200(int argc, char* argv[]); +int ha0300(int argc, char* argv[]); +//int ha0400(int argc, char* argv[]); +int ha0500(int argc, char* argv[]); diff --git a/ha/ha01.gif b/ha/ha01.gif new file mode 100755 index 000000000..dd8f8fbc6 Binary files /dev/null and b/ha/ha01.gif differ diff --git a/ha/ha0200.cpp b/ha/ha0200.cpp new file mode 100755 index 000000000..ffdb9d526 --- /dev/null +++ b/ha/ha0200.cpp @@ -0,0 +1,98 @@ +#include +#include + +#include "ha0200a.h" + +/////////////////////////////////////////////////// +// Maschera +/////////////////////////////////////////////////// +class TConf_Hardy_mask : public TAutomask +{ +protected: + virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); + +public: + void config_loader(TSheet_field& sf, const char* paragrafo); + void config_setter(TSheet_field& sf, const char* paragrafo); + TConf_Hardy_mask(const TFilename& f); + + virtual ~TConf_Hardy_mask(){}; +}; + +bool TConf_Hardy_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) +{ + switch (o.dlg()) + { + default: + break; + } + return true; +} + +TConf_Hardy_mask::TConf_Hardy_mask(const TFilename& f) : TAutomask (f) +{ +} + +/////////////////////////////////////////////////// +// Applicazione +/////////////////////////////////////////////////// +class TConf_Hardy : public TConfig_application +{ + TMask* _ch; + +protected: + virtual TMask* create_mask(const TFilename& f); + virtual TMask* get_mask(); + +public: + virtual bool preprocess_config (TMask& mask, TConfig& config); + virtual bool postprocess_config (TMask& mask, TConfig& config); + virtual bool user_create( ); + virtual bool user_destroy( ); + + TConf_Hardy() : TConfig_application(CONFIG_DITTA), _ch(NULL) { } + virtual ~TConf_Hardy() { } +}; + +TMask* TConf_Hardy::get_mask() +{ + return _ch; +} + +TMask* TConf_Hardy::create_mask(const TFilename& f) +{ + if (_ch == NULL) + _ch = new TConf_Hardy_mask(f); + return _ch; +} + +bool TConf_Hardy::preprocess_config (TMask& mask, TConfig& config) +{ + return true; +} + +bool TConf_Hardy::postprocess_config (TMask& mask, TConfig& config) +{ + return true; +} + +bool TConf_Hardy::user_create( ) +{ + TConfig cfg(CONFIG_DITTA, "ha"); + cfg.set("EdMask", "ha0200a"); + return true; +} + +bool TConf_Hardy::user_destroy( ) +{ + if (_ch != NULL) + delete _ch; + return true; +} + +int ha0200(int argc, char* argv[]) +{ + TConf_Hardy app; + app.run(argc, argv, TR("Configurazione Hardy")); + return 0; +} \ No newline at end of file diff --git a/ha/ha0200a.h b/ha/ha0200a.h new file mode 100755 index 000000000..d354c52d7 --- /dev/null +++ b/ha/ha0200a.h @@ -0,0 +1,25 @@ +#define F_CODTIPO_FAT 201 +#define F_DESCRTIPO_FAT 202 +#define F_STATO_INI_FAT 203 +#define F_STATO_FIN_FAT 204 +//------------------------------ +#define F_CO_ANT_NUM 205 +#define F_CO_ANT_TIP 207 + +#define F_NA_ANT_NUM 210 +#define F_NA_ANT_TIP 212 +#define F_NA_ANT_SPE 213 +//------------------------------ +#define F_CO_POST_NUM 215 +#define F_CO_POST_TIP 217 + +#define F_NA_POST_NUM 220 +#define F_NA_POST_TIP 222 +#define F_NA_POST_SPE 223 +//------------------------------ +#define F_CO_RIFA_NUM 225 +#define F_CO_RIFA_TIP 227 + +#define F_NA_RIFA_NUM 230 +#define F_NA_RIFA_TIP 232 +#define F_NA_RIFA_SPE 233 diff --git a/ha/ha0200a.uml b/ha/ha0200a.uml new file mode 100755 index 000000000..21daa2c60 --- /dev/null +++ b/ha/ha0200a.uml @@ -0,0 +1,299 @@ +#include "ha0200a.h" + +TOOLBAR "topbar" 0 0 0 2 +#include +ENDPAGE + +PAGE "Configurazione Hardy" -1 -1 78 23 + +GROUPBOX DLG_NULL 76 4 +BEGIN + PROMPT 1 1 "@bTipo documento per fatture" +END + +STRING F_CODTIPO_FAT 4 +BEGIN + PROMPT 2 2 "Tipo " + USE %TIP + INPUT CODTAB F_CODTIPO_FAT + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CODTIPO_FAT CODTAB + OUTPUT F_DESCRTIPO_FAT S0 + CHECKTYPE REQUIRED + FLAG "UP" + FIELD TipoFatt +END + +STRING F_DESCRTIPO_FAT 50 +BEGIN + PROMPT 15 2 "" + USE %TIP KEY 2 + INPUT S0 F_DESCRTIPO_FAT + DISPLAY "Descrizione@60" S0 + DISPLAY "Codice" CODTAB + COPY OUTPUT F_CODTIPO_FAT + CHECKTYPE NORMAL +END + +STRING F_STATO_INI_FAT 1 +BEGIN + PROMPT 2 3 "Stato iniziale " + USE %STD + INPUT CODTAB F_STATO_INI_FAT + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_STATO_INI_FAT CODTAB + FLAGS "U" + CHECKTYPE REQUIRED + FIELD StatoIniFatt +END + +STRING F_STATO_FIN_FAT 1 +BEGIN + PROMPT 25 3 "Stato finale " + USE %STD + INPUT CODTAB F_STATO_FIN_FAT + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_STATO_FIN_FAT CODTAB + FLAGS "U" + CHECKTYPE REQUIRED + FIELD StatoFinFatt +END + +//--Anticipi------------- + +GROUPBOX DLG_NULL 76 4 +BEGIN + PROMPT 1 5 "@bAnticipi" +END + +TEXT DLG_NULL +BEGIN + PROMPT 2 6 "@bContratti premi" +END + +STRING F_CO_ANT_NUM 4 +BEGIN + PROMPT 20 6 "Numerazione " + /*USE %NUM + INPUT CODTAB F_CO_ANT_NUM + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CO_ANT_NUM CODTAB + CHECKTYPE REQUIRED*/ + FIELD CoAntNum +END + +STRING F_CO_ANT_TIP 4 +BEGIN + PROMPT 42 6 "Tipo " + /*USE %TIP + INPUT CODTAB F_CO_ANT_TIP + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CO_ANT_TIP CODTAB + CHECKTYPE REQUIRED*/ + FIELD CoAntTip +END + +TEXT DLG_NULL +BEGIN + PROMPT 2 7 "@bNote di Accredito" +END + +STRING F_NA_ANT_NUM 4 +BEGIN + PROMPT 20 7 "Numerazione " + USE %NUM + INPUT CODTAB F_NA_ANT_NUM + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_ANT_NUM CODTAB + CHECKTYPE REQUIRED + FIELD NaAntNum +END + +STRING F_NA_ANT_TIP 4 +BEGIN + PROMPT 42 7 "Tipo " + USE %TIP + INPUT CODTAB F_NA_ANT_TIP + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_ANT_TIP CODTAB + CHECKTYPE REQUIRED + FIELD NaAntTip +END + +STRING F_NA_ANT_SPE 8 +BEGIN + PROMPT 58 7 "Riga " + USE SPP + INPUT CODTAB F_NA_ANT_SPE + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_ANT_SPE CODTAB + CHECKTYPE REQUIRED + FIELD NaAntSpe +END + + +//--Posticipi------------- + +GROUPBOX DLG_NULL 76 4 +BEGIN + PROMPT 1 9 "@bPosticipi" +END + +TEXT DLG_NULL +BEGIN + PROMPT 2 10 "@bContratti premi" +END + +STRING F_CO_POST_NUM 4 +BEGIN + PROMPT 20 10 "Numerazione " + /*USE %NUM + INPUT CODTAB F_CO_POST_NUM + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CO_POST_NUM CODTAB + CHECKTYPE REQUIRED*/ + FIELD CoPostNum +END + +STRING F_CO_POST_TIP 4 +BEGIN + PROMPT 42 10 "Tipo " + /*USE %TIP + INPUT CODTAB F_CO_POST_TIP + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CO_POST_TIP CODTAB + CHECKTYPE REQUIRED*/ + FIELD CoPostTip +END + +TEXT DLG_NULL +BEGIN + PROMPT 2 11 "@bNote di Accredito" +END + +STRING F_NA_POST_NUM 4 +BEGIN + PROMPT 20 11 "Numerazione " + USE %NUM + INPUT CODTAB F_NA_POST_NUM + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_POST_NUM CODTAB + CHECKTYPE REQUIRED + FIELD NaPostNum +END + +STRING F_NA_POST_TIP 4 +BEGIN + PROMPT 42 11 "Tipo " + USE %TIP + INPUT CODTAB F_NA_POST_TIP + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_POST_TIP CODTAB + CHECKTYPE REQUIRED + FIELD NaPostTip +END + +STRING F_NA_POST_SPE 8 +BEGIN + PROMPT 58 11 "Riga " + USE SPP + INPUT CODTAB F_NA_POST_SPE + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_POST_SPE CODTAB + CHECKTYPE REQUIRED + FIELD NaPostSpe +END + +//--Rifatturazione------------- + +GROUPBOX DLG_NULL 76 4 +BEGIN + PROMPT 1 13 "@bRifatturazione" +END + +TEXT DLG_NULL +BEGIN + PROMPT 2 14 "@bContratti premi" +END + +STRING F_CO_RIFA_NUM 4 +BEGIN + PROMPT 20 14 "Numerazione " + /*USE %NUM + INPUT CODTAB F_CO_RIFA_NUM + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CO_RIFA_NUM CODTAB + CHECKTYPE REQUIRED*/ + FIELD CoRifaNum +END + +STRING F_CO_RIFA_TIP 4 +BEGIN + PROMPT 42 14 "Tipo " + /*USE %TIP + INPUT CODTAB F_CO_RIFA_TIP + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CO_RIFA_TIP CODTAB + CHECKTYPE REQUIRED*/ + FIELD CoRifaTip +END + +TEXT DLG_NULL +BEGIN + PROMPT 2 15 "@bNote di Accredito" +END + +STRING F_NA_RIFA_NUM 4 +BEGIN + PROMPT 20 15 "Numerazione " + USE %NUM + INPUT CODTAB F_NA_RIFA_NUM + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_RIFA_NUM CODTAB + CHECKTYPE REQUIRED + FIELD NaRifaNum +END + +STRING F_NA_RIFA_TIP 4 +BEGIN + PROMPT 42 15 "Tipo " + USE %TIP + INPUT CODTAB F_NA_RIFA_TIP + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_RIFA_TIP CODTAB + CHECKTYPE REQUIRED + FIELD NaRifaTip +END + +STRING F_NA_RIFA_SPE 8 +BEGIN + PROMPT 58 15 "Riga " + USE SPP + INPUT CODTAB F_NA_RIFA_SPE + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_NA_RIFA_SPE CODTAB + CHECKTYPE REQUIRED + FIELD NaRifaSpe +END + +ENDPAGE + +ENDMASK \ No newline at end of file diff --git a/ha/ha0300.cpp b/ha/ha0300.cpp new file mode 100755 index 000000000..f84167f27 --- /dev/null +++ b/ha/ha0300.cpp @@ -0,0 +1,393 @@ +#include +#include +#include + +#include +#include +#include + +#include "../mg/umart.h" +#include "../ve/condv.h" +#include "../ve/rcondv.h" + +#include "halib.h" +#include "ha0.h" +#include "ha0300a.h" + +////////////////////////////////////////////// +// Maschera +////////////////////////////////////////////// +class TDocumenti_premio_msk : public TAutomask +{ +protected: + bool find_prezzo_articolo(const TString& codart, real& prezzo, TString& um) const; + virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); + +public: + TDocumenti_premio_msk(); +}; + + +TDocumenti_premio_msk::TDocumenti_premio_msk() : TAutomask("ha0300a") +{ +} + +bool TDocumenti_premio_msk::find_prezzo_articolo(const TString& codart, real& prezzo, TString& um) const +{ + //1) contratto + const long codcf = get_long(F_CODCF); + const TString& codcontr = get(F_CODCONTR); + + TToken_string key_umart; + key_umart.add(codart); + key_umart.add(1); + const TRectype& rec_umart = cache().get(LF_UMART, key_umart); + um = rec_umart.get(UMART_UM); + const real umart_prezzo = rec_umart.get_real(UMART_PREZZO); + + prezzo = umart_prezzo; //mal che vada sarà il prezzo di umart + TToken_string key; + + //CONTRATTI: tipo=C|catven=|tipocf=C|codcf=codcf|cod=codcontr|tiporiga=A|codriga=codart|um=um + key.add("C"); + key.add(""); + key.add("C"); + key.add(codcf); + key.add(codcontr); + + //per um è necessario se il contratto scelto ha la gestione delle um accesa (tanto per complicarsi la vita) + const bool gestum_contr = cache().get(LF_CONDV, key, CONDV_GESTUM) == "X"; + + key.add("A"); + key.add(codart); + if (gestum_contr) + key.add(um); + + const TRectype& rec_contratto = cache().get(LF_RCONDV, key); + const real contratto_prezzo = rec_contratto.get(RCONDV_PREZZO); + + //2) non c'è un prezzo sul contratto, prova con il listino standard + if (!contratto_prezzo.is_zero()) + prezzo = contratto_prezzo; + else + { + key.cut(0); + //LISTINI: tipo=L|catven=catven|tipocf=|codcf=|cod=codlis|tiporiga=A|codriga=codart|um=um + key.add("L"); + //la catven se c'è è del cliente + TToken_string key_cfven; + key_cfven.add("C"); + key_cfven.add(codcf); + const TString& catven = cache().get(LF_CFVEN, key_cfven, CFV_CATVEN); + key.add(catven); + key.add(""); + key.add(""); + const TString& codlis = get(F_CODLIS); + key.add(codlis); + + //per um è necessario se il listino scelto ha la gestione delle um accesa (tanto per complicarsi la vita) + const bool gestum_list = cache().get(LF_CONDV, key, CONDV_GESTUM) == "X"; + + key.add("A"); + key.add(codart); + if (gestum_list) + key.add(um); + + const TRectype& rec_listino = cache().get(LF_RCONDV, key); + const real listino_prezzo = rec_listino.get(RCONDV_PREZZO); + if (!listino_prezzo.is_zero()) + prezzo = listino_prezzo; + } + + return !prezzo.is_zero(); +} + +bool TDocumenti_premio_msk::on_field_event(TOperable_field& o, TField_event e, long jolly) +{ + switch (o.dlg()) + { + case F_TIPOCONTR: + if (e == fe_init || e == fe_modify) + { + //all'inizio deve proporre una lista dei possibili numerazioni e tipi che stanno in configurazione + TConfig config(CONFIG_DITTA, "ha"); + TString4 codnum, tipodoc; + + switch (o.get()[0]) + { + case 'A': + codnum = config.get("CoAntNum"); + tipodoc = config.get("CoAntTip"); + break; + case 'R': + codnum = config.get("CoRifaNum"); + tipodoc = config.get("CoRifaTip"); + break; + default: + codnum = config.get("CoPostNum"); + tipodoc = config.get("CoPostTip"); + break; + } + + set(F_CODNUM, codnum); + set(F_TIPODOC, tipodoc); + } + break; + case S_CODART: + if (e == fe_modify) + { + //caricamento del prezzo in fase modifica codart: sequenza contratto->listino->umart + //non è possibile mettere un prezzo a mano alla cazzo! + real prezzo; + TString4 um; + //se il prezzo l'ha trovato lo mette nel relativo campo + if (find_prezzo_articolo(o.get(), prezzo, um)) + { + TMask& row_mask = o.mask(); + row_mask.set(S_PREZZO, prezzo); + row_mask.set(S_UMQTA, um); + } + else + { + TString msg; + msg.format("Non esiste il prezzo per l'articolo %s nei listini selezionati!", (const char*)o.get()); + return error_box(msg); + } + } + break; + default: + break; + }; + return true; +} + +////////////////////////////////////////////// +// Applicazione +////////////////////////////////////////////// +class TDocumenti_premio : public TRelation_application +{ + TRelation* _rel; + TDocumenti_premio_msk* _msk; + +protected: + virtual bool user_create(); + virtual bool user_destroy(); + virtual TMask* get_mask(int) {return _msk;} + virtual TRelation* get_relation() const {return _rel;} + + void write_rows(const TMask& m); + void read_rows(TMask& m); + + virtual bool get_next_key(TToken_string& key); + virtual int write(const TMask& m); + virtual int rewrite(const TMask& m); + virtual int read(TMask& m); + + virtual void init_query_mode(TMask& m); + virtual void init_insert_mode(TMask& m); + virtual void init_modify_mode(TMask& m); +}; + + +//cerca il primo numero valido per NDOC +bool TDocumenti_premio::get_next_key(TToken_string& key) +{ + long n = 0; + + TLocalisamfile doc(LF_DOC); + TRectype& curr = doc.curr(); + const char provv = _msk->get(F_PROVV)[0]; + const int anno = _msk->get_int(F_ANNO); + const TString4 codnum = _msk->get(F_CODNUM); + + curr.put(DOC_PROVV, provv); + curr.put(DOC_ANNO, anno); + curr.put(DOC_CODNUM, codnum); + curr.put(DOC_NDOC, 9999999L); + + const int err = doc.read(_isgreat); + + if (err != _isemptyfile) + { + if (err == NOERR) + doc.prev(); + if (curr.get_char(DOC_PROVV) == provv && + curr.get_int(DOC_ANNO) == anno && + curr.get(DOC_CODNUM) == codnum) + n = curr.get_long(DOC_NDOC); + } + + n++; + + key.cut(0); + key.add(F_PROVV); key.add(provv); + key.add(F_ANNO); key.add(anno); + key.add(F_CODNUM); key.add(codnum); + key.add(F_NDOC); key.add(n); + + return n > 0; +} + +void TDocumenti_premio::read_rows(TMask& m) +{ + //chiave delle righe (escluso nriga) basata sulla testata + TToken_string rdoc_key; + + const char provv = m.get(F_PROVV)[0]; + const int anno = m.get_int(F_ANNO); + const TString& codnum = m.get(F_CODNUM); + const long ndoc = m.get_long(F_NDOC); + + rdoc_key.add(codnum); + rdoc_key.add(anno); + rdoc_key.add(provv); + rdoc_key.add(ndoc); + + //array con le righe che rispondono alla chiave di testata + TRecord_array righedoc(rdoc_key, LF_RIGHEDOC); + + //sheet e maschera di riga dello sheet + TSheet_field& sheet = m.sfield(F_RIGHE); + TMask& sm = sheet.sheet_mask(); + sheet.destroy(); + + //giro sulle righe documento + for (int i = 1; i <= righedoc.rows(); i++) + { + const TRectype& rec = righedoc.row(i); //record con l'elemento riga dell'array + const TString& tipo = rec.get(RDOC_TIPORIGA); //in base al tiporiga si devono fare operazioni diverse + //se è un tipo riga merce -> va aggiunta allo sheet + if (tipo == HARDY_TIPORIGA_MERCE || tipo.blank()) + { + TToken_string& row = sheet.row(-1); //aggiunge una riga vuota + for (int i = sm.fields()-1; i >= 0; i--) //giro su tutti i campi della maschera di riga... + { + TMask_field& mf = sm.fld(i); //aggiunge solo quelli che hanno un field + if ((mf.field() != NULL) && (mf.dlg() > 100)) //> 100 per evitare errori sui campi dlg_null + { + const int idx = sheet.cid2index(mf.dlg()); + row.add(mf.field()->read(rec), idx); + } + } + } + else if (tipo == HARDY_TIPORIGA_SOMMA)//se invece è la riga con le somme anticipate/maturate (solo 1 per contratto!) -> va messa in testata + { + const real anticipato = rec.get(RCA_2_ANTICIPATO); + const real maturato = rec.get(RCA_2_RESTITUITO); + m.set(F_ANTICIPATO, anticipato); + m.set(F_RESTITUITO, maturato); + } + + } +} + + +void TDocumenti_premio::write_rows(const TMask& m) +{ + //chiave delle righe basata sui campi di testata + const char provv = m.get(F_PROVV)[0]; + const int anno = m.get_int(F_ANNO); + const TString& codnum = m.get(F_CODNUM); + const long ndoc = m.get_long(F_NDOC); + + TRectype* key_rec = new TRectype(LF_RIGHEDOC); + + key_rec->put(RDOC_PROVV, provv); + key_rec->put(RDOC_ANNO, anno); + key_rec->put(RDOC_CODNUM, codnum); + key_rec->put(RDOC_NDOC, ndoc); + + //recordarray con le righe che rispondono alla chiave di testata key_rec + TRecord_array righedoc(LF_RIGHEDOC, RDOC_NRIGA); + righedoc.set_key(key_rec); + + //sheet e maschera di riga dello sheet + TSheet_field& sheet = m.sfield(F_RIGHE); + TMask& sm = sheet.sheet_mask(); + + //giro sulle righe dello sheet (righe di tipo merce) + FOR_EACH_SHEET_ROW(sheet, i, row) + { + TRectype& rec = righedoc.row(i+1, true); //record con l'elemento riga dell'array + for (int i = sm.fields()-1; i >= 0; i--) //giro su tutti i campi della maschera di riga... + { + TMask_field& mf = sm.fld(i); //aggiunge solo quelli che hanno un field + if ((mf.field() != NULL) && (mf.dlg() > 100)) //> 100 per evitare errori sui campi dlg_null + { + const int idx = sheet.cid2index(mf.dlg()); + mf.field()->write(row->get(idx), rec); + } + } + rec.put(RDOC_TIPORIGA, HARDY_TIPORIGA_MERCE); + } + //salva la riga di tipo somme anticipate/rimborsate (H02) che in realtà è in testata + const int righedoc_items = righedoc.rows(); + TRectype& last_rec = righedoc.row(righedoc_items + 1, true); + const real anticipato = m.get_real(F_ANTICIPATO); + const real maturato = m.get_real(F_RESTITUITO); + last_rec.put(RDOC_QTAGG4, anticipato); + last_rec.put(RDOC_QTAGG5, maturato); + last_rec.put(RDOC_TIPORIGA, HARDY_TIPORIGA_SOMMA); + + //e alla fine della fiera scrive tutto ufficialmente + righedoc.rewrite(); +} + + +void TDocumenti_premio::init_query_mode(TMask& m) +{ +} + +void TDocumenti_premio::init_insert_mode(TMask& m) +{ +} + +void TDocumenti_premio::init_modify_mode(TMask& m) +{ + m.disable(F_TIPOCONTR); //non si può cambiare il tipo contratto una volta stabilito sennò non funziona + un cazzo +} + +int TDocumenti_premio::write(const TMask& m) +{ + const int err = TRelation_application::write(m); + if (err == NOERR) + write_rows(m); + return err; +} + +int TDocumenti_premio::rewrite(const TMask& m) +{ + const int err = TRelation_application::rewrite(m); + if (err == NOERR) + write_rows(m); + return err; +} + +int TDocumenti_premio::read(TMask& m) +{ + const int err = TRelation_application::read(m); + if (err == NOERR) + read_rows(m); + return err; +} + +bool TDocumenti_premio::user_create() +{ + _rel = new TRelation(LF_DOC); + _msk = new TDocumenti_premio_msk; + return true; +} + +bool TDocumenti_premio::user_destroy() +{ + delete _rel; + delete _msk; + return true; +} + +int ha0300(int argc, char* argv[]) +{ + TDocumenti_premio a; + a.run(argc, argv, TR("Documenti premio Hardy")); + return 0; +} diff --git a/ha/ha0300a.h b/ha/ha0300a.h new file mode 100755 index 000000000..171efeab4 --- /dev/null +++ b/ha/ha0300a.h @@ -0,0 +1,54 @@ +//campi maschera ha0300a.uml + +#define F_CODNUM 201 +#define F_TIPODOC 202 +#define F_PROVV 203 +#define F_ANNO 204 + +#define F_TIPOCF 205 +#define F_CODCF 206 +#define F_DESCF 207 + +#define F_CODCONTR 210 +#define F_DESCONTR 211 +#define F_CODLIS 212 +#define F_DESLIS 213 + +#define F_NDOC 215 +#define F_DATADOC 216 +#define F_STATO 217 + +#define F_CODAG 220 +#define F_DESCRAG 221 +#define F_CODPAG 222 +#define F_DESCRPAG 223 +#define F_TIPOCONTR 224 +#define F_DATACOMP 225 +#define F_DATAFCOMP 226 +#define F_ANTICIPATO 227 +#define F_RESTITUITO 228 + +#define F_RIGHE 500 //questo va messo 500 sennò ve0 si incazza e non funziona più + +//campi della maschera riga sheet +#define S_CODART 101 +#define S_DESCR 102 +#define S_UMQTA 103 +#define S_PREZZO 104 +#define S_PREMIO 105 +#define S_RICARICO 106 +#define S_MATURATO 107 + +//vadetecum per il sagace programmatore sui campi dei CONTRATTI PREMI HARDY + +//campo msk/sheet campo file tipo riga +//S_PREMIO QTAGG1 H01 +//S_RICARICO QTAGG2 H01 +//S_MATURATO QTAGG5 H01 + +// QTAGG3 H01 - H02 -> usato come campo di appoggio per gli importi in elaborazione ha0500 +//F_ANTICIPATO QTAGG4 H02 +//F_RESTITUITO QTAGG5 H02 + +//ATTENZIONE! QTA H01 - H02 -> usato come campo di appoggio per umqta in elaborazione ha0500 +//ATTANZIONE! UMQTA H01 - H02 -> usato come campo di appoggio per qta in elaborazione ha0500 \ No newline at end of file diff --git a/ha/ha0300a.uml b/ha/ha0300a.uml new file mode 100755 index 000000000..35e9801dd --- /dev/null +++ b/ha/ha0300a.uml @@ -0,0 +1,405 @@ +#include "ha0300a.h" + +TOOLBAR "" 0 0 0 2 + +#include + +ENDPAGE + +PAGE "Contratto premi Hardy" -1 -1 78 23 + +GROUPBOX DLG_NULL 78 13 +BEGIN + PROMPT 1 0 "" +END + +RADIOBUTTON F_TIPOCONTR 1 76 +BEGIN + PROMPT 2 0 "@bTipo contratto" + ITEM "A|Anticipo" MESSAGE CLEAR,F_DATAFCOMP|ENABLE,1@ + ITEM "P|Posticipo" MESSAGE ENABLE,F_DATAFCOMP|CLEAR,1@ + ITEM "R|Rifatturazione" MESSAGE CLEAR,F_DATAFCOMP|ENABLE,1@ + FIELD TIPOCFFATT + FLAGS "GZ" + KEY 1 +END + +STRING F_CODNUM 4 +BEGIN + PROMPT 102 101 "Cod. num. " + FIELD CODNUM + USE %NUM KEY 1 + INPUT CODTAB F_CODNUM + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + CHECKTYPE NORMAL + FLAGS "GDU" + KEY 1 +END + +STRING F_TIPODOC 4 +BEGIN + FIELD TIPODOC + PROMPT 120 101 "Tipo doc. " + USE %TIP KEY 1 + INPUT CODTAB F_TIPODOC + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + CHECKTYPE NORMAL + FLAGS "GDU" +END + +LIST F_TIPOCF 9 +BEGIN + PROMPT 130 101 "" + FIELD TIPOCF + IT "C|Cliente" + FLAGS "D" +END + +STRING F_CODCF 6 +BEGIN + PROMPT 2 3 "Cliente " + WARNING "Cliente assente" + HELP "Codice del cliente del documento" + FLAGS "R" + FIELD CODCF + USE LF_CLIFO KEY 1 + INPUT TIPOCF "C" + INPUT CODCF F_CODCF + DISPLAY "Codice" CODCF + DISPLAY "Ragione Sociale@50" RAGSOC + DISPLAY "Partita IVA@12" PAIV + DISPLAY "Sospeso" SOSPESO + OUTPUT F_CODCF CODCF + OUTPUT F_DESCF RAGSOC + CHECKTYPE REQUIRED + ADD RUN cg0 -1 C +END + +STRING F_DESCF 50 +BEGIN + WARNING "Cliente assente" + HELP "Ragione sociale del cliente del documento" + PROMPT 24 3 "" + USE LF_CLIFO KEY 2 + INPUT TIPOCF "C" + INPUT RAGSOC F_DESCF + DISPLAY "Ragione Sociale@50" RAGSOC + DISPLAY "Partita IVA@12" PAIV + DISPLAY "Codice" CODCF + COPY OUTPUT F_CODCF + CHECKTYPE REQUIRED + ADD RUN cg0 -1 C +END + +LIST F_PROVV 1 +BEGIN + PROMPT 140 101 "" + ITEM "D|D" + FIELD PROVV + FLAGS "D" + KEY 1 +END + +NUMBER F_ANNO 4 +BEGIN + PROMPT 2 4 "Esercizio " + USE ESC + INPUT CODTAB F_ANNO + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_ANNO CODTAB + FIELD ANNO + CHECKTYPE REQUIRED + FLAGS "A" + KEY 1 +END + +NUMBER F_NDOC 6 +BEGIN + PROMPT 24 4 "N. contr. premi " + USE LF_DOC SELECT BETWEEN(CODCF,#F_CODCF,#F_CODCF) + INPUT CODNUM F_CODNUM SELECT + INPUT PROVV F_PROVV SELECT + INPUT ANNO F_ANNO SELECT + INPUT NDOC F_NDOC + DISPLAY "Numero" NDOC + DISPLAY "Data doc." DATADOC + DISPLAY "Inizio validita" DATACOMP + DISPLAY "Fine validita" DATAFCOMP + DISPLAY "Tipo" TIPOCFFATT + DISPLAY "Agente" CODAG + OUPUT F_NDOC NDOC + FIELD NDOC + KEY 1 + CHECKTYPE REQUIRED +END + +DATE F_DATADOC +BEGIN + PROMPT 50 4 "Data " + FIELD DATADOC +END + +STRING F_STATO 1 +BEGIN + PROMPT 69 4 "Stato " + FIELD STATO + USE %STD KEY 1 + INPUT CODTAB F_STATO + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_STATO CODTAB + CHECKTYPE NORMAL + FLAGS "DG" +END + +STRING F_CODCONTR 3 +BEGIN + PROMPT 2 5 "Listino cliente " + USE CONDV + INPUT TIPO "C" + INPUT TIPOCF F_TIPOCF SELECT + INPUT CODCF F_CODCF SELECT + INPUT COD F_CODCONTR + DISPLAY "Codice" COD + DISPLAY "Descrizione@50" DESCR + DISPLAY "Inizio validita'" VALIN + DISPLAY "Fine validità" VALFIN + OUTPUT F_CODCONTR COD + OUTPUT F_DESCONTR DESCR + FIELD CODCONT + CHECKTYPE NORMAL +END + +STRING F_DESCONTR 50 +BEGIN + PROMPT 26 5 "" + FLAGS "DG" +END + +STRING F_CODLIS 3 +BEGIN + PROMPT 2 6 "Listino standard " + USE CONDV + INPUT TIPO "L" + INPUT COD F_CODLIS + DISPLAY "Codice" COD + DISPLAY "Descrizione@50" DESCR + DISPLAY "Inizio validita'" VALIN + DISPLAY "Fine validità" VALFIN + OUTPUT F_CODLIS COD + OUTPUT F_DESLIS DESCR + FIELD CODLIST + CHECKTYPE NORMAL +END + +STRING F_DESLIS 50 +BEGIN + PROMPT 26 6 "" + FLAGS "DG" +END + +STRING F_CODAG 5 +BEGIN + PROMPT 2 7 "Agente " + FIELD CODAG + USE LF_AGENTI + INPUT CODAGE F_CODAG + DISPLAY "Codice@8R" CODAGE + DISPLAY "Descrizione@50" RAGSOC + OUTPUT F_CODAG CODAGE + OUTPUT F_DESCRAG RAGSOC + CHECKTYPE NORMAL + FLAGS "UZ" +END + +STRING F_DESCRAG 50 +BEGIN + PROMPT 24 7 "" + USE LF_AGENTI KEY 2 + INPUT RAGSOC F_DESCRAG + DISPLAY "Descrizione@50" RAGSOC + DISPLAY "Codice@8R" CODAGE + COPY OUTPUT F_CODAG + CHECKTYPE NORMAL +END + +STRING F_CODPAG 4 +BEGIN + PROMPT 2 8 "Cond. pag. " + FIELD CODPAG + USE %CPG + INPUT CODTAB F_CODPAG + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CODPAG CODTAB + OUTPUT F_DESCRPAG S0 + CHECKTYPE NORMAL + FLAGS "U" + HE "Inserisci il codice del tipo di pagamento" + WA "Codice tipo pagamento non trovato" + ADD RUN ba3 -6 +END + +STRING F_DESCRPAG 50 +BEGIN + PROMPT 24 8 "" + USE %CPG KEY 2 + INPUT S0 F_DESCRPAG + DISPLAY "Descrizione@50" S0 + DISPLAY "Codice" CODTAB + COPY OUTPUT F_CODPAG + CHECKTYPE NORMAL + HE "Inserisci il codice del tipo di pagamento" + WA "Codice tipo pagamento non trovato" + ADD RUN ba3 -6 +END + +DATE F_DATACOMP +BEGIN + PROMPT 2 9 "Inizio validita' " + FIELD DATACOMP + CHECKTYPE REQUIRED +END + +DATE F_DATAFCOMP +BEGIN + PROMPT 35 9 "Fine validita' " + FIELD DATAFCOMP +END + +GROUPBOX DLG_NULL 76 3 +BEGIN + PROMPT 2 10 "@bSomme anticipate/restituite" +END + +CURRENCY F_ANTICIPATO +BEGIN + PROMPT 3 11 "Anticipato " + GROUP 1 +END + +CURRENCY F_RESTITUITO +BEGIN + PROMPT 38 11 "Restituito " + GROUP 1 +END + +SPREADSHEET F_RIGHE +BEGIN +PROMPT 2 13 "" +ITEM "Codice Articolo@20" +ITEM "Descrizione@40" +ITEM "UM@2" +ITEM "Prezzo listino" +ITEM "Premio@10" +ITEM "Ns. carico" +ITEM "Bonus@10" +END + +ENDPAGE + +ENDMASK + +///////////////////////////////////////// +//maschera di riga +TOOLBAR "topbar" 0 0 0 2 + +BUTTON DLG_OK 2 2 +BEGIN + PROMPT 1 1 "" +END + +BUTTON DLG_DELREC 2 2 +BEGIN + PROMPT 2 1 "" +END + +BUTTON DLG_CANCEL 2 2 +BEGIN + PROMPT 3 1 "" +END + +ENDPAGE + +PAGE "Riga contratto premi Hardy" -1 -1 76 12 + +STRING S_CODART 20 +BEGIN + PROMPT 1 1 "Articolo " + USE LF_ANAMAG KEY 1 + INPUT CODART S_CODART + DISPLAY "Codice@20" CODART + DISPLAY "Descrizione@50" DESCR + OUTPUT S_CODART CODART + OUTPUT S_DESCR DESCR + WARNING "Articolo assente" + FLAGS "U" + FIELD CODART + ADD RUN ve2 -3 +END + +STRING S_DESCR 50 +BEGIN + PROMPT 1 2 "Descrizione " + USE 47 KEY 2 + INPUT DESCR S_DESCR + DISPLAY "Descrizione@50" DESCR + DISPLAY "Codice@20" CODART + COPY OUTPUT S_CODART + FIELD DESCR +END + +STRING S_UMQTA 2 +BEGIN + PROMPT 1 3 "U.M. " + USE LF_UMART KEY 2 + JOIN %UMS INTO CODTAB=UM + INPUT CODART S_CODART SELECT + INPUT UM S_UMQTA + DISPLAY "Codice@20" UM + DISPLAY "Descrizione@50" %UMS->S0 + OUTPUT S_UMQTA UM + FIELD UMQTA + FLAGS "U" + CHECKTYPE REQUIRED +END + +GROUPBOX DLG_NULL 74 6 +BEGIN + PROMPT 1 4 "@bValori" +END + +CURRENCY S_PREZZO +BEGIN + PROMPT 2 5 "Prezzo listino " + FLAGS "UDG" + FIELD PREZZO +END + +CURRENCY S_PREMIO +BEGIN + PROMPT 2 6 "Premio " + FLAGS "U" + FIELD QTAGG1 +END + +CURRENCY S_RICARICO +BEGIN + PROMPT 2 7 "A Ns. carico " + FLAGS "U" + FIELD QTAGG2 +END + +CURRENCY S_MATURATO +BEGIN + PROMPT 2 8 "Bonus maturato " + FLAGS "DU" + FIELD QTAGG5 +END + +ENDPAGE + +ENDMASK \ No newline at end of file diff --git a/ha/ha0500.cpp b/ha/ha0500.cpp new file mode 100755 index 000000000..cd29b853e --- /dev/null +++ b/ha/ha0500.cpp @@ -0,0 +1,780 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../cg/cglib01.h" +#include "../ve/velib.h" + +#include "halib.h" +#include "ha0.h" +#include "ha0500a.h" + +/////////////////////////////////////////////////////////// +// TAutomask +/////////////////////////////////////////////////////////// +class THardy_elab_docs_mask : public TAutomask +{ +protected: + virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); + +public: + THardy_elab_docs_mask(); + ~THardy_elab_docs_mask(); +}; + + +THardy_elab_docs_mask::THardy_elab_docs_mask() : TAutomask ("ha0500a") +{ +} + +THardy_elab_docs_mask::~THardy_elab_docs_mask() +{ +} + +bool THardy_elab_docs_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) +{ + switch (o.dlg()) + { + case F_ADATA: + if (e == fe_close || e == fe_modify) + { + //se la data iniziale è piena -> l'anno deve essere lo stesso nelle 2 date; + //se invece è vuota -> la data iniziale viene presa come la data iniziale dell'esercizio della data finale... + //..ma questo qui non serve e viene rinviato alla query principale del recordset + const TDate adata = get_date(F_ADATA); + TEsercizi_contabili esc; + const int adata_esc = esc.date2esc(adata); + + TDate dadata = o.get(); + if (dadata.ok()) + { + const int dadata_esc = esc.date2esc(dadata); + if (adata_esc != dadata_esc) + return error_box("Le date devono appartenere allo stesso esercizio!"); + } + } + break; + /*case F_TIPOCONTR: + if (e == fe_modify) + { + //in base alla tipologia di contratti da elaborare decide numerazione e tipo delle NAC da generare + TConfig config(CONFIG_DITTA, "ha"); + TString4 codnum, tipodoc; + + switch (o.get()[0]) + { + case 'A': + codnum = config.get("NaAntNum"); + tipodoc = config.get("NaAntTip"); + break; + case 'R': + codnum = config.get("NaRifaNum"); + tipodoc = config.get("NaRifaTip"); + break; + default: + codnum = config.get("NaPostNum"); + tipodoc = config.get("NaPostTip"); + break; + } + + set(F_CODNUM_NAC, codnum); + set(F_CODTIPO_NAC, tipodoc); + } + break;*/ + //in caso di elaborazione definitiva è obbligatorio.. + //..eliminare tutte le NAC provvisorie generate in precedenza + case F_DEFINITIVO: + if (e == fe_modify) + { + if (o.get() == "X") + { + set(F_KILLPROVV, "X"); + disable(F_KILLPROVV); + } + else + { + enable(F_KILLPROVV); + set(F_KILLPROVV, " "); + } + } + break; + default: + break; + } + return true; +} + + + +/////////////////////////////////////// +// TSkeleton_application +/////////////////////////////////////// +class THardy_elab_docs : public TSkeleton_application +{ + +protected: + //metodi alto livello + void elabora(const TMask& mask); + int kill_provv_nac(const TMask& mask); + long genera_recordset(const TMask& mask, TISAM_recordset& recset); + void elabora_documenti(const TMask& mask, TISAM_recordset& recset, TLog_report& log); + + //metodi medio livello + bool aggiorna_contratti_anticipo(const TString& rdoc_codart, const real& rdoc_qta, TDocumento& contratto); + bool aggiorna_contratti_posticipo(const TString& rdoc_codart, const real& rdoc_qta, TDocumento& contratto); + bool aggiorna_contratti(TDocumento& curr_doc, TArray& contratti_cliente); + bool genera_nac(const TMask& mask, TArray& contratti_cliente, TArray& documenti_cliente, TLog_report& log); + + //metodi basso livello + int find_contratti_cliente(const long codcf, const TMask& mask, TArray& contratti_cliente); + void check_date(const TDate& datafine, TDate& dataini); + int find_numerazioni(const TString& tipo_to_elab, TString_array& num_doc); + +public: + virtual void main_loop(); + virtual bool create(); + +}; + +//metodo per ricavare la data iniziale di elaborazione qualora l'utonto non la metta e l'esercizio da usare +void THardy_elab_docs::check_date(const TDate& datafine, TDate& dataini) +{ + TEsercizi_contabili esc; + TDate datafine_tmp = datafine; + const int esercizio = esc.date2esc(datafine); + esc.code2range(esercizio, dataini, datafine_tmp); +} + +int THardy_elab_docs::find_numerazioni(const TString& tipo_to_elab, TString_array& num_doc) +{ + TISAM_recordset num_recset("USE %NUM"); + for (bool ok = num_recset.move_first(); ok; ok = num_recset.move_next()) //giro sui vari tipi numerazione + { + const TString4 codtab = num_recset.get("CODTAB").as_string(); + + const TCodice_numerazione numerazione(codtab); + for (int t = numerazione.ntipi_doc() - 1; t >= 0; t--) + { + const TString& tipo_doc = numerazione.tipo_doc(t); + if (tipo_doc == tipo_to_elab) + { + if (num_doc.find(codtab) < 0) // Evito aggiunta di doppioni + num_doc.add(codtab); + break; + } + } //for (int t = codnum.. + } //for (bool ok = num_recset... + + return num_doc.items(); +} + + +//metodo per accoppare tutte le NAC provvisorie generate in precedenza +int THardy_elab_docs::kill_provv_nac(const TMask& mask) +{ + int nac_killed = 0; + const TDate& adata = mask.get_date(F_ADATA); + int anno = adata.year(); + + TConfig config(CONFIG_DITTA, "ha"); + + TToken_string numerazioni; + numerazioni.add(config.get("NaAntNum")); + numerazioni.add(config.get("NaRifaNum")); + numerazioni.add(config.get("NaPostNum")); + + FOR_EACH_TOKEN(numerazioni, codnum) + { + TRelation rel_doc(LF_DOC); + + TRectype& rec = rel_doc.curr(); + rec.put(DOC_PROVV, "P"); + rec.put(DOC_ANNO, anno); + rec.put(DOC_CODNUM, codnum); + + TCursor cur_doc (&rel_doc, "", 1, &rec, &rec); + const long items = cur_doc.items(); + cur_doc.freeze(); + TProgind progind(items, "Eliminazione NAC provvisorie in corso...", false, true); + + for (cur_doc = 0; cur_doc.pos() < items; ++cur_doc) + { + progind.addstatus(1); + TDocumento doc(rec); + int err = doc.remove(); + if (err == NOERR) + nac_killed++; + } + } + return nac_killed; +} + +//metodo che filtra tutti i documenti in base ai parametri della maschera +long THardy_elab_docs::genera_recordset(const TMask& mask, TISAM_recordset& recset) +{ + //parametri di elaborazione + //per prima cosa controlla se il cliente è stato specificato; questo influisce sulla scelta della chiave di ricerca sul file + int key = 3; + const long codcf = mask.get_long(F_CODCF); + if (codcf > 0) + key = 2; + + //e costruiamola 'sta query! + //usa i documenti con chiave variabile (vedi sopra) + TString query; + query << "USE DOC KEY " << key; + //lo stato dipende da quanto sta scritto sulla elaborazione differita (stato iniziale dei docs da considerare) + //viene messo CODNUM nella SELECT perchè, essendoci un range di date nelle chiavi, la numerazione verrebbe ignorata! (provato!) + query << "\nSELECT (STATO=#STATOINI)"; + + //in base al tipo documento che si deve elaborare (settato in configurazione), ci possono essere più numerazioni da considerare! + TConfig config(CONFIG_DITTA, "ha"); + const TString& tipo_to_elab = config.get("TipoFatt"); + //e adesso cerca le numerazioni che contengono il tipo preso dalla configurazione + TString_array num_doc; + const int numerazioni_valide = find_numerazioni(tipo_to_elab, num_doc); + if (numerazioni_valide > 0) + { + query << "&&("; + + for (int i = 0; i < numerazioni_valide; i++) + { + if (i > 0) + query << "||"; + + query << "(CODNUM='" << num_doc[i] << "')"; + } + + query << ")"; + } + //se c'è l'agente specificato... + const TString& agente = mask.get(F_CODAGE); + const bool has_007 = agente.full(); + if (has_007) + query << "&&(CODAG=#CODAG)"; + //ordinamento agente-codcf-numdoc + query << "\nBY "; + if (has_007) + query << DOC_CODAG << " "; + + query << DOC_CODCF << " " << DOC_NDOC; + + //from-to dipendente da chiave + switch (key) + { + case 2: //chiave per tipocf-codcf-provv-anno-datadoc-codnum + { + query << "\nFROM TIPOCF=C CODCF=#CODCF PROVV=D ANNO=#ANNO DATADOC=#DADATA"; + query << "\nTO TIPOCF=C CODCF=#CODCF PROVV=D ANNO=#ANNO DATADOC=#ADATA"; + } + break; + + case 3: //chiave per datadoc-provv-anno-codnum + { + query << "\nFROM DATADOC=#DADATA PROVV=D ANNO=#ANNO"; + query << "\nTO DATADOC=#ADATA PROVV=D ANNO=#ANNO"; + } + break; + + default: + break; + } + + //setta la nuova query al recordset (inizialmente nato dequeryzzato) + recset.set(query); + + //settaggio delle variabili + const TDate adata = mask.get_date(F_ADATA); + TDate dadata = mask.get_date(F_DADATA); + //se la data iniziale è vuota deve coincidere con l'inizio dell'esercizio della data finale (obbligatoria!) + int esc = adata.year(); + if (!dadata.ok()) + check_date(adata, dadata); + + //lo stato dei documenti da considerare va a raccatarlo nel config + const TString& stato_ini = config.get("StatoIniFatt"); + recset.set_var("#STATOINI", stato_ini); + + if (agente.full()) + recset.set_var("#CODAG", agente); + if (codcf > 0) + recset.set_var("#CODCF", codcf); + recset.set_var("#ANNO", long(esc)); + recset.set_var("#DADATA", dadata); + recset.set_var("#ADATA", adata); + + return recset.items(); +} + +//metodo che riempie un array con tutti i contratti del cliente passatogli (in base alla tipologia di contratti da elaborare) +int THardy_elab_docs::find_contratti_cliente(const long codcf, const TMask& mask, TArray& contratti_cliente) +{ + contratti_cliente.destroy(); + //deve cercare tutti i contratti del cliente e metterli nell'array + TString query; + query << "USE DOC KEY 4"; + query << "\nSELECT ((CODNUM=#A_CODNUM)||(CODNUM=#R_CODNUM)||(CODNUM=#P_CODNUM))"; + query << "\nFROM TIPOCF=C CODCF=#CODCF"; + query << "\nTO TIPOCF=C CODCF=#CODCF"; + + TISAM_recordset recset(query); + + //settaggio delle variabili + //il codice numerazione lo trova nella configurazione Hardy, e lo deve scegliere in base alla tipologia di contratti che sta esaminando! + TConfig config(CONFIG_DITTA, "ha"); + + const TString& num_ant = config.get("CoAntNum"); + const TString& num_rifa = config.get("CoRifaNum"); + const TString& num_post = config.get("CoPostNum"); + + recset.set_var("#A_CODNUM", num_ant); + recset.set_var("#R_CODNUM", num_rifa); + recset.set_var("#P_CODNUM", num_post); + + recset.set_var("#CODCF", codcf); + + const long n_contratti = recset.items(); //questo serve solo al sagace programmatore + + //aggiunge i contratti all'array: solo quelli in auge nel periodo di calcolo selezionato sulla maschera! + for (bool ok = recset.move_first(); ok; ok = recset.move_next()) + { + //controlla il tipo di contratto + const char tipo_contr = recset.get(DOC_TIPOCFFATT).as_string()[0]; + + //contratti anticipo 'A': datainizio esiste sempre, datafine non esiste (va ad esaurimento) + //contratti posticipo 'P': datainizio esiste sempre, datafine può non esistere + //contratti rifatturazione 'R': come contratti anticipo + + //controlla validità del contratto con le date scelte per l'elaborazione dei documenti + const TDate data_ini_contratto = recset.get(DOC_DATACOMP).as_date(); + + TDate data_ini_elab = mask.get_date(F_DADATA); + const TDate data_fine_elab = mask.get_date(F_ADATA); + if (!data_ini_elab.ok()) + check_date(data_fine_elab, data_ini_elab); + + //quindi la datainizio vale per tutti allo stesso modo (è obbligatoria nei contratti) + //se l'elaborazione finisce prima che cominci il contratto -> il contratto non serve a nulla + if (data_ini_contratto > data_fine_elab) + continue; + //la data fine vale invece solo per i contratti 'P' e potrebbe non esserci (contratti senza scadenza) + TDate data_fine_contratto; + if (tipo_contr == 'P') + { + data_fine_contratto = recset.get(DOC_DATAFCOMP).as_date(); + if (data_fine_contratto.ok()) + { + if (data_fine_contratto < data_ini_elab) + continue; + } + } + + TDocumento* curr_contratto = new TDocumento(recset.get(DOC_PROVV).as_string()[0], recset.get(DOC_ANNO).as_int(), + recset.get(DOC_CODNUM).as_string(), recset.get(DOC_NDOC).as_int()); + contratti_cliente.add(curr_contratto); + } + + return contratti_cliente.items(); +} + + +bool THardy_elab_docs::aggiorna_contratti_anticipo(const TString& rdoc_codart, const real& rdoc_qta, TDocumento& contratto) +{ + bool elaborato = false; + + FOR_EACH_PHYSICAL_RDOC(contratto, rm, rigamerce) + { + const TString& rigamerce_codart = rigamerce->get(RDOC_CODART); + //se trova il codart in una delle righe di contratto... + if (rdoc_codart == rigamerce_codart) + { + const real rigamerce_premio = rigamerce->get_real(RC_1_PREMIO); + //se il premio non è nullo procede all'aggiornamento del restituito + if (rigamerce_premio != ZERO) + { + //aggiornamento delle righe di tipo spesa (verigH02) per aggiornare le somme restituite + FOR_EACH_PHYSICAL_RDOC(contratto, ra, rigacontratto) + { + //cerca una riga anticipo da evadere sul contratto per aggiornare la somma restituita sull'anticipo + if (rigacontratto->is_spese()) + { + //usa qtagg3 come campo di appoggio per il calcolo della somma restituita dovuta al contratto (parte da zero per ogni elaborazione di NAC) + real somma_restituita = rigacontratto->get_real(RCA_2_RESTITUITO); + //la somma restituita deve essere incrementata per la qta di merce (caffè) che c'è sulla riga della fattura in esame... + //..moltiplicata per il premio che c'è nella riga di tipo merce del contratto in esame + somma_restituita += rdoc_qta * rigamerce_premio; + rigacontratto->put(RCA_2_RESTITUITO, somma_restituita); + elaborato = true; + } + } //FOR_EACH_PHYSICAL.. fine casino sulla riga di tipo spese + + if (rigamerce->is_merce()) + { + real somma_bonus = rigamerce->get_real(RC_1_BONUS); + somma_bonus += rdoc_qta * rigamerce_premio; + rigamerce->put(RC_1_BONUS, somma_bonus); + elaborato = true; + } + } + + //aggiornamento delle quantità per le righe di tipo merce (verigH01) + if (rigamerce->is_merce()) + { + real qta_tot = rigamerce->get_real(RDOC_QTA); //prende la qta tot dal contratto (è usato un campo di appoggio RDOC_QTA) + qta_tot += rdoc_qta; //aggiunge la qta della riga documento + rigamerce->put(RDOC_QTA, qta_tot); //riscrive sul campo di appoggio del contratto + } + + } //if(rdoc_codart... + } //FOR_EACH_PHYSICAL.. + return elaborato; +} + + +bool THardy_elab_docs::aggiorna_contratti_posticipo(const TString& rdoc_codart, const real& rdoc_qta, TDocumento& contratto) +{ + bool elaborato = false; + + FOR_EACH_PHYSICAL_RDOC(contratto, rm, rigamerce) + { + const TString& rigamerce_codart = rigamerce->get(RDOC_CODART); + //se trova il codart in una delle righe di contratto... + if (rdoc_codart == rigamerce_codart) + { + const real rigamerce_premio = rigamerce->get_real(RC_1_PREMIO); + //..aggiorna direttamente il bonus totale per l'articolo presente sulla riga + if (rigamerce_premio != ZERO) + { + //usa QTAGG3 come campo di appoggio per il calcolo della somma restituita dovuta al contratto (parte da zero per ogni elaborazione di NAC) + real somma_bonus = rigamerce->get_real(RC_1_BONUS); + somma_bonus += rdoc_qta * rigamerce_premio; + rigamerce->put(RC_1_BONUS, somma_bonus); + elaborato = true; + } //if(rigamerce... + + //aggiornamento delle righe di tipo merce (verigH01) + if (rigamerce->is_merce()) + { + real qta_tot = rigamerce->get_real(RDOC_QTA); + qta_tot += rdoc_qta; + rigamerce->put(RDOC_QTA, qta_tot); + } + + } //if(rdoc_codart... + } //FOR_EACH_PHYSICAL.. + return elaborato; +} + +//aggiorna, in base al documento in esame curr_doc, le somme restituite nelle righe di tipo verigh02 nei contratti validi.. +//..del cliente +bool THardy_elab_docs::aggiorna_contratti(TDocumento& curr_doc, TArray& contratti_cliente) +{ + bool elaborato = false; + + FOR_EACH_PHYSICAL_RDOC(curr_doc, r, rdoc) if (rdoc->is_merce()) //giro su tutte le righe merce delle fatture + { + TString80 rdoc_codart = rdoc->get(RDOC_CODART); //non si riesce ad usare un TString& perchè lo perde dopo un pò + rdoc_codart.trim(); + const real rdoc_qta = rdoc->quantita(); + + //controlla se il codart della riga esiste in uno dei contratti validi + for (int i = 0; i < contratti_cliente.items(); i++) + { + TDocumento& contratto = (TDocumento&)contratti_cliente[i]; + const TString& tipo_contratto = contratto.get(DOC_TIPOCFFATT); + + //in base al tipo di contratto (Anticipo/Posticipo/Rifatturazione) decide cosa fare + if (tipo_contratto == "A" || tipo_contratto == "R") + { + elaborato |= aggiorna_contratti_anticipo(rdoc_codart, rdoc_qta, contratto); + } //if(tipo_contratto... + else if (tipo_contratto == "P") + { + elaborato |= aggiorna_contratti_posticipo(rdoc_codart, rdoc_qta, contratto); + } + } //for(int i.. + } //FOR_EACH... + + return elaborato; +} + + +bool THardy_elab_docs::genera_nac(const TMask& mask, TArray& contratti_cliente, TArray& documenti_cliente, TLog_report& log) +{ + //si informa se l'elaborazione è definitiva o meno + const bool definitivo = mask.get_bool(F_DEFINITIVO); + + //giro su tutti i contratti del cliente che stanno nell'array (ogni contratto genera una NAC) + FOR_EACH_ARRAY_ITEM(contratti_cliente, r, riga) + { + TDocumento& contratto = *(TDocumento*)riga; + const long ndoc = contratto.numero(); //il numdoc del contratto serve nelle segnalazioni + const long codcf = contratto.codcf(); //il codice cliente ci serve nella generazione della NAC.. + const char tipo_contratto = contratto.get(DOC_TIPOCFFATT)[0]; //..e pure il tipo di contratto in esame! + + //segnaliamo l'elaborazione del contratto sul log + TString msg; + msg << "Elaborato contratto premi n." << ndoc << " del cliente " << codcf; + log.log(0, msg); + + + // generazione del documento NAC dal contratto + // ------------------------------------------- + + // TESTATA + + //alcuni parametri delle righe vanno presi dalla configurazione + TConfig config(CONFIG_DITTA, "ha"); + TString4 nac_codnum, nac_tipo; + TString8 cod_riga; + switch (tipo_contratto) + { + case 'A': + nac_codnum = config.get("NaAntNum"); + nac_tipo = config.get("NaAntTip"); + cod_riga = config.get("NaAntSpe"); + break; + case 'R': + nac_codnum = config.get("NaRifaNum"); + nac_tipo = config.get("NaRifaTip"); + cod_riga = config.get("NaRifaSpe"); + break; + default: + nac_codnum = config.get("NaPostNum"); + nac_tipo = config.get("NaPostTip"); + cod_riga = config.get("NaPostSpe"); + break; + } + + const int anno = mask.get_date(F_ADATA).year(); + //solo in caso di elaborazione definitiva si scrivono NAC di tipo D; sennò di tipo P, che sono uccidibili.. + //..all'inizio di ogni nuova elaborazione + char provv = 'P'; + if (definitivo) + provv = 'D'; + + TDocumento nac(provv, anno, nac_codnum, 0); //num_doc = 0 perchè viene aggiornato in fase di registrazione + nac.set_tipo(nac_tipo); + nac.put(DOC_STATO, 1); + nac.put(DOC_DATADOC, mask.get(F_DATAELAB)); + nac.put(DOC_TIPOCF, 'C'); + nac.put(DOC_CODCF, codcf); + + + // RIGHE + + //ogni riga di tipo merce (verigh01) del contratto origina una riga della NAC + //noto il codice di riga spesa (che farà le veci del codart), troviamo il tipo riga dalla tabella SPP e tutte le features che servono + const TRectype& rec_spp = cache().get("SPP", cod_riga); + const TString4 tipo_riga = rec_spp.get("S8"); + const TString80 descr_riga_spp = rec_spp.get("S0"); + const TString4 codiva = rec_spp.get("S3"); + + //giro sulle righe del contratto, che originano le righe NAC + for (int i = 1; i <= contratto.rows(); i++) + { + const TRiga_documento& riga_contratto = contratto[i]; //le righe di un documento partono da 1! (standard del mercoledì) + const TString& tiporiga = riga_contratto.get(RDOC_TIPORIGA); + const TString& tipo_riga_contratto = cache().get("%TRI", tiporiga, "S7"); + + //solo le righe di tipo merce (verigh01) dei contratti devono comparire nelle NAC + if (tipo_riga_contratto == "M") + { + TString80 riga_contratto_codart = riga_contratto.get(RDOC_CODART); + riga_contratto_codart.trim(); + const real riga_contratto_bonus = riga_contratto.get_real(RDOC_QTAGG3); + if (riga_contratto_bonus != ZERO) + int cazzone = 1; + const real riga_contratto_qta = riga_contratto.get_real(RDOC_QTA); + const TString4 riga_contratto_um = riga_contratto.get(RDOC_UMQTA); + const real riga_contratto_premio = riga_contratto.get_real(RC_1_PREMIO); + + // riga (dovrebbe essere un'unica riga per NAC, il cui valore sta in QTAGG3 ed è stato calcolato nella aggiorna_contratti()) + TRiga_documento& nac_row = nac.new_row(tipo_riga); + nac_row.put(RDOC_NRIGA, i); + nac_row.put(RDOC_CODART, cod_riga); + + //panegirico della descrizione + nac_row.put(RDOC_DESCR, descr_riga_spp); + + msg.cut(0); //risparmiamo sulle stringhe, mica sono gratis + const TDate adata = mask.get_date(F_ADATA); + TDate dadata = mask.get_date(F_DADATA); + if (!dadata.ok()) + check_date(adata, dadata); + + msg << " " << dadata << " -- " << adata << " --(Art." << riga_contratto_codart << ")"; + nac_row.put(RDOC_DESCEST, msg); + nac_row.put(RDOC_DESCLUNGA, "X"); + + //importi, qta, umqta + nac_row.put(RDOC_UMQTA, riga_contratto_um); + nac_row.put(RDOC_QTA, riga_contratto_qta); + nac_row.put(RDOC_PREZZO, riga_contratto_premio); + + //iva + nac_row.put(RDOC_CODIVA, codiva); + } + } + + // salvataggi vari + // --------------- + //la NAC viene scritta comunque, con provv='D' se elaborazione definitiva + int err = nac.write(); + + msg.cut(0); //la fase di risparmio stringhe continua + if (err == NOERR) + msg << "Generata NAC "; + else + msg << "Impossibile generare NAC "; + msg << " - provv:" << provv << " - num:" << nac_codnum << " - tipo:" << nac_tipo << " - anno:" << anno << " |" << r; + log.log(0, msg); + + // se non ci sono errori -> in caso di elaborazione definitiva procede alla registrazione.. + //.. del contratto (ricordiamo che in memoria il contratto ha già le righe aggiornate + if (definitivo && err == NOERR) + { + //prima di registrare il contratto vanno svuotati i campi appoggio sulle righe che sono stati usati.. + //..per qta e premi vari e riempiti nella aggiorna_contratti() + for (int i = 1; i <= contratto.rows(); i++) + { + TRiga_documento& riga_contratto = contratto[i]; + riga_contratto.put(RDOC_QTAGG3, ZERO); + riga_contratto.put(RDOC_QTA, ZERO); + } + + //alla fine della fiera aggiorna il contratto + err = contratto.rewrite(); + msg.cut(0); + if (err == NOERR) + msg << "Aggiornato contratto premi n. "; + else + msg << "Impossibile aggiornare il contratto premi n. "; + + msg << ndoc << " del cliente " << codcf; + log.log(0, msg); + } + + log.log(0, ""); + + } //FOR_EACH_ARRAY_ITEM(... giro sui contratti cliente + + + //il metodo ritornerà il successo o meno della registrazione + return true; +} + + +void THardy_elab_docs::elabora_documenti(const TMask& mask, TISAM_recordset& recset, TLog_report& log) +{ + TProgind pi(recset.items(), TR("Elaborazione documenti in corso..."), true, true); + + //inizializza variabili da usare nella scansione del recordset + long old_codcf = 0L; + //array con l'insieme dei contratti e dei documenti elaborati per un singolo cliente! + TArray contratti_cliente, documenti_cliente; + + //giro sulle fatture (è il giro di più alto livello che viene esteso all'interno delle aggiorna_contratti) + for (bool ok = recset.move_first(); ok; ok = recset.move_next()) + { + if (!pi.addstatus(1)) + break; + + const long codcf = recset.get(DOC_CODCF).as_int(); + //al cambio cliente deve controllare i contratti di quel cliente nel periodo di elaborazione!! + if (codcf != old_codcf) + { + //aggiorna old_codcf in modo da poter controllare i contratti solo al cambio codcf + old_codcf = codcf; + + const int n_contratti = find_contratti_cliente(codcf, mask, contratti_cliente); + if (n_contratti == 0) + { + TString msg; + msg << "Il cliente " << codcf << " non ha un contratto premi valido nel periodo di elaborazione selezionato ma ha fatture."; + log.log_error(msg); + } + } + + if (contratti_cliente.items() > 0) + { + //se ha trovato uno o più contratti validi nel periodo passa alla elaborazione dei documenti del cliente + TDocumento* curr_doc = new TDocumento(recset.cursor()->curr()); + //elabora il documento corrente aggiornando le somme restituite sui contratti validi + if (aggiorna_contratti(*curr_doc, contratti_cliente)) + documenti_cliente.add(curr_doc); + else + delete(curr_doc); + } + } //for (bool ok = recset.move_first()... + + //generazione NAC (una per contratto cliente) + genera_nac(mask, contratti_cliente, documenti_cliente, log); +} + + + +//metodo di alto livello con i punti principali del programma (come da analisi...) +void THardy_elab_docs::elabora(const TMask& mask) +{ + //1) eventuale accoppamento di tutti i documenti provvisori creati con precedenti elaborazioni precedenti + int nac_killed = 0; + if (mask.get_bool(F_KILLPROVV)) + nac_killed = kill_provv_nac(mask); + + //log report con segnalazioni sui clienti trattati (bene o male) + TLog_report log("Sintesi elaborazione"); + log.kill_duplicates(); + log.log(0, ""); + + //2) recordset ordinato codag-codcf-numdoc con tutti i docs che soddisfano i parametri dell'utente + // --------------------------------------------------------------------------------------------- + TISAM_recordset recset(""); + const long items = genera_recordset(mask, recset); + if (items == 0) + { + log.log(1, "Non esistono documenti di vendita che soddisfino i parametri selezionati! Ritenta sarai più fortunato!"); + } + + //3) elaborazione documenti e contratti, generazione NAC, salvataggi + // --------------------------------------------------------------- + elabora_documenti(mask, recset, log); + + //3) scrittura log + // ------------- + log.print_or_preview(); +} + +void THardy_elab_docs::main_loop() +{ + THardy_elab_docs_mask mask; + while (mask.run() == K_ENTER) + { + elabora(mask); + } +} + +bool THardy_elab_docs::create() +{ + //controlla se la chiave ha l'autorizzazione a questo programma (solo per hardy!) + Tdninst dninst; + if (!dninst.can_I_run(true)) + return error_box(TR("Programma non autorizzato!")); + + open_files(LF_DOC, LF_RIGHEDOC, 0); + + return TSkeleton_application::create(); +} + +int ha0500 (int argc, char* argv[]) +{ + THardy_elab_docs main_app; + main_app.run(argc, argv, TR("Elaborazione documenti Hardy")); + return true; +} \ No newline at end of file diff --git a/ha/ha0500a.h b/ha/ha0500a.h new file mode 100755 index 000000000..443542d11 --- /dev/null +++ b/ha/ha0500a.h @@ -0,0 +1,15 @@ +#define F_CODAGE 201 +#define F_DESCRAGE 202 +#define F_TIPOCF 203 +#define F_CODCF 204 +#define F_RAGSOC 205 +#define F_DADATA 206 +#define F_ADATA 207 +//#define F_TIPOCONTR 208 +//#define F_CODNUM_NAC 210 +//#define F_DESCRNUM_NAC 211 +//#define F_CODTIPO_NAC 212 +//#define F_DESCRTIPO_NAC 213 +#define F_DATAELAB 214 +#define F_DEFINITIVO 215 +#define F_KILLPROVV 216 diff --git a/ha/ha0500a.uml b/ha/ha0500a.uml new file mode 100755 index 000000000..c97019837 --- /dev/null +++ b/ha/ha0500a.uml @@ -0,0 +1,167 @@ +#include "ha0500a.h" + +PAGE "Elaborazione documenti Hardy" -1 -1 78 11 + +GROUPBOX DLG_NULL 76 5 +BEGIN + PROMPT 1 1 "@bParametri documenti" +END + +STRING F_CODAGE 6 +BEGIN + PROMPT 2 2 "Agente " + FLAGS "U" + USE LF_AGENTI + INPUT CODAGE F_CODAGE + DISPLAY "Codice" CODAGE + DISPLAY "Descrizione@50" RAGSOC + OUTPUT F_CODAGE CODAGE + OUTPUT F_DESCRAGE RAGSOC + CHECKTYPE NORMAL +END + +STRING F_DESCRAGE 50 +BEGIN + PROMPT 22 2 "" + USE LF_AGENTI KEY 2 + INPUT RAGSOC F_DESCRAGE + DISPLAY "Descrizione@50" RAGSOC + DISPLAY "Codice" CODAGE + COPY OUTPUT F_CODAGE + CHECKTYPE NORMAL +END + +STRING F_CODCF 6 +BEGIN + PROMPT 2 3 "Cliente " + USE LF_CLIFO + INPUT TIPOCF "C" + INPUT CODCF F_CODCF + DISPLAY "Codice" CODCF + DISPLAY "Ragione sociale@50" RAGSOC + OUTPUT F_CODCF CODCF + OUTPUT F_RAGSOC RAGSOC + CHECKTYPE NORMAL + FLAGS "U" +END + +STRING F_RAGSOC 50 +BEGIN + PROMPT 22 3 "" + USE LF_CLIFO KEY 2 + INPUT TIPOCF "C" + INPUT RAGSOC F_RAGSOC + DISPLAY "Ragione sociale@50" RAGSOC + DISPLAY "Codice" CODCF + COPY OUTPUT F_CODCF + CHECKTYPE NORMAL +END + +DATE F_DADATA +BEGIN + PROMPT 2 4 "Da data " +END + +DATE F_ADATA +BEGIN + PROMPT 22 4 "A data " + CHECKTYPE REQUIRED + VALIDATE DATE_CMP_FUNC >= F_DADATA +END + +GROUPBOX DLG_NULL 76 3 +BEGIN + PROMPT 1 6 "@bParametri elaborazione" +END + +DATE F_DATAELAB +BEGIN + PROMPT 2 7 "Data elaborazione " + FLAGS "A" + CHECKTYPE REQUIRED +END + +BOOLEAN F_DEFINITIVO +BEGIN + PROMPT 40 7 "Definitivo" +END + +/*RADIOBUTTON F_TIPOCONTR 1 72 +BEGIN + PROMPT 2 8 "@bTipi contratto da elaborare" + ITEM "A|Anticipo" + ITEM "P|Posticipo" + ITEM "R|Rifatturazione" + FLAGS "Z" +END + +STRING F_CODNUM_NAC 4 +BEGIN + PROMPT 2 11 "Num. doc. NAC " + USE %NUM + INPUT CODTAB F_CODNUM_NAC + DISPLAY "Codice@8" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CODNUM_NAC CODTAB + OUTPUT F_DESCRNUM_NAC S0 + FLAGS "DG" + CHECKTYPE REQUIRED +END + +STRING F_DESCRNUM_NAC 50 43 +BEGIN + PROMPT 29 11 "" + USE %NUM KEY 2 + INPUT S0 F_DESCRNUM_NAC + DISPLAY "Descrizione@50" S0 + DISPLAY "Codice@8" CODTAB + COPY OUTPUT F_CODNUM_NAC + FLAGS "DG" + CHECKTYPE NORMAL +END + +STRING F_CODTIPO_NAC 4 +BEGIN + PROMPT 2 12 "Tipo doc. NAC " + USE %TIP + INPUT CODTAB F_CODTIPO_NAC + DISPLAY "Codice" CODTAB + DISPLAY "Descrizione@50" S0 + OUTPUT F_CODTIPO_NAC CODTAB + OUTPUT F_DESCRTIPO_NAC S0 + FLAGS "DG" + CHECKTYPE REQUIRED +END + +STRING F_DESCRTIPO_NAC 50 43 +BEGIN + PROMPT 29 12 "" + USE %TIP KEY 2 + INPUT S0 F_DESCRTIPO_NAC + DISPLAY "Descrizione@60" S0 + DISPLAY "Codice" CODTAB + COPY OUTPUT F_CODTIPO_NAC + FLAGS "DG" + CHECKTYPE NORMAL +END*/ + +BOOLEAN F_KILLPROVV +BEGIN + PROMPT 1 9 "Eliminare tutte le note di accredito provvisorie" +END + +STRING DLG_PROFILE 50 +BEGIN + PROMPT 1 -1 "Profilo " + PSELECT +END + +ENDPAGE + +TOOLBAR "" 0 0 0 2 + +#include + +ENDPAGE + +ENDMASK diff --git a/ha/halib.h b/ha/halib.h new file mode 100755 index 000000000..dd981abb9 --- /dev/null +++ b/ha/halib.h @@ -0,0 +1,20 @@ +//definizioni delle righe dei contratti premio Hardy (da includere nei .cpp) +#define HARDY_TIPORIGA_MERCE "H01" +#define HARDY_TIPORIGA_SOMMA "H02" + +//ridefinizione dei campi per rendere chiare le elaborazioni +//legenda per il sagace programmatore: +//RC = riga contratto standard, tipo 1, merce +//RCA = riga contratto anticipo, tipo 2, spese (1 per ogni contratto anticipo) +// 1 = riga tipo 1, merce +// 2 = riga tipo 2, spese +// segue il vero significato del campo (vero... verosimile, che è meglio!) +#define RC_1_PREMIO RDOC_QTAGG1 +#define RC_1_NSCARICO RDOC_QTAGG2 +#define RC_1_BONUS RDOC_QTAGG5 + +#define RCA_2_ANTICIPATO RDOC_QTAGG4 +#define RCA_2_RESTITUITO RDOC_QTAGG5 + +//#define RC_1_BONUS_NAC RDOC_QTAGG3 +//#define RC_2_RESO_NAC RDOC_QTAGG3 diff --git a/ha/hamenu.men b/ha/hamenu.men new file mode 100755 index 000000000..a789c6bcc --- /dev/null +++ b/ha/hamenu.men @@ -0,0 +1,10 @@ +[HAMENU_001] +Caption = "Hardy caffe'" +Picture = +Module = 46 +Flags = "F" +Item_01 = "Configurazione Hardy", "ha0 -1", "" +Item_02 = "Gestione contratti premio", "ha0 -2", "" +Item_03 = "Contabilizzazione contratti premio", "ha0 -3", "" +Item_04 = "Generazione NAC", "ha0 -4", "" +Item_05 = "Gestione listini", "ve2 -4", "" diff --git a/ha/hatabcom.txt b/ha/hatabcom.txt new file mode 100755 index 000000000..ba373ca59 --- /dev/null +++ b/ha/hatabcom.txt @@ -0,0 +1,18 @@ +[Header] +Version=199519 +File=4 +Fields=COD,3|CODTAB,25|S0,70|S1,70|S2,70|S3,70|S4,25|S5,25|S6,5|S7,5 +Fields=S8,5|S9,5|S10,5|S11,5|I0,7|I1,7|I2,7|I3,7|I4,7|I5,7 +Fields=I6,7|I7,7|I8,7|I9,7|I10,7|I11,7|I12,7|I13,7|I14,7|R0,18 +Fields=R1,18|R2,18|R3,18|R4,18|R5,18|R6,18|R7,18|R8,18|R9,18|R10,18 +Fields=R11,18|R12,18|R13,18|R14,18|R15,18|R16,18|R17,18|R18,18|R19,18|R20,18 +Fields=R21,18|R22,18|R23,18|R24,18|R25,18|R26,18|R27,18|R28,18|R29,18|D0,10 +Fields=D1,10|D2,10|D3,10|D4,10|B0,1|B1,1|B2,1|B3,1|B4,1|B5,1 +Fields=B6,1|B7,1|B8,1|B9,1|B10,1|B11,1|B12,1|B13,1|B14,1|B15,1 +Fields=FPC,1 + +[Data] +NUM|HCO|Contratto||HCA HCP||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||X||||||||||||||| +TIP|HCO|Contratto Hardy||1259||hacontr|hacontr|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +TRI|H01|Merce|||||||M|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +TRI|H02|Somma anticipata|||||||S|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| diff --git a/projects/ha.sln b/projects/ha.sln new file mode 100755 index 000000000..94d6259ed --- /dev/null +++ b/projects/ha.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ha0", "ha0.vcxproj", "{2DB7F8EF-BABB-4A27-BC7D-8821B0D285C6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2DB7F8EF-BABB-4A27-BC7D-8821B0D285C6}.Debug|Win32.ActiveCfg = Debug|Win32 + {2DB7F8EF-BABB-4A27-BC7D-8821B0D285C6}.Debug|Win32.Build.0 = Debug|Win32 + {2DB7F8EF-BABB-4A27-BC7D-8821B0D285C6}.Release|Win32.ActiveCfg = Release|Win32 + {2DB7F8EF-BABB-4A27-BC7D-8821B0D285C6}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/res/moka.ico b/res/moka.ico new file mode 100755 index 000000000..7eeba3006 Binary files /dev/null and b/res/moka.ico differ diff --git a/xvaga/treelistctrl.cpp b/xvaga/treelistctrl.cpp new file mode 100755 index 000000000..b253b98b3 --- /dev/null +++ b/xvaga/treelistctrl.cpp @@ -0,0 +1,5077 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: treelistctrl.cpp +// Purpose: multi column tree control implementation +// Author: Robert Roebling +// Maintainer: $Author: guy $ +// Created: 01/02/97 +// RCS-ID: $Id: treelistctrl.cpp,v 1.1 2010-08-11 12:53:55 guy Exp $ +// Copyright: (c) 2004-2008 Robert Roebling, Julian Smart, Alberto Griggio, +// Vadim Zeitlin, Otto Wyss, Ronan Chartois +// Licence: wxWindows +///////////////////////////////////////////////////////////////////////////// + +// =========================================================================== +// declarations +// =========================================================================== + +// --------------------------------------------------------------------------- +// headers +// --------------------------------------------------------------------------- + +#if defined(__GNUG__) && !defined(__APPLE__) + #pragma implementation "treelistctrl.h" +#endif + +// For compilers that support precompilation, includes "wx.h". +#include "wxinc.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if wxCHECK_VERSION(2, 7, 0) +#include +#endif +#include +#include +#include + +#ifdef __WXMAC__ +#include "wx/mac/private.h" +#endif + +#include // only required for debugging purpose + +#undef WXDLLEXPORT +#define WXDLLEXPORT +#include "treelistctrl.h" + +// --------------------------------------------------------------------------- +// array types +// --------------------------------------------------------------------------- + +class wxTreeListItem; + +#if !wxCHECK_VERSION(2, 5, 0) +2WX_DEFINE_ARRAY(wxTreeListItem *, wxArrayTreeListItems); +#else +WX_DEFINE_ARRAY_PTR(wxTreeListItem *, wxArrayTreeListItems); +#endif + +#include +WX_DECLARE_OBJARRAY(wxTreeListColumnInfo, wxArrayTreeListColumnInfo); +#include +WX_DEFINE_OBJARRAY(wxArrayTreeListColumnInfo); + + +// -------------------------------------------------------------------------- +// constants +// -------------------------------------------------------------------------- + +static const int NO_IMAGE = -1; + +static const int LINEHEIGHT = 10; +static const int LINEATROOT = 5; +static const int MARGIN = 2; +static const int MININDENT = 16; +static const int BTNWIDTH = 9; +static const int BTNHEIGHT = 9; +static const int EXTRA_WIDTH = 4; +static const int EXTRA_HEIGHT = 4; +static const int HEADER_OFFSET_X = 0; // changed from 1 to 0 on 2009.03.10 for Windows (other OS untested) +static const int HEADER_OFFSET_Y = 1; + +static const int DRAG_TIMER_TICKS = 250; // minimum drag wait time in ms +static const int FIND_TIMER_TICKS = 500; // minimum find wait time in ms +static const int RENAME_TIMER_TICKS = 250; // minimum rename wait time in ms + +const wxChar* wxTreeListCtrlNameStr = _T("treelistctrl"); + +static wxTreeListColumnInfo wxInvalidTreeListColumnInfo; + + +// --------------------------------------------------------------------------- +// private classes +// --------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// wxTreeListHeaderWindow (internal) +//----------------------------------------------------------------------------- + +class wxTreeListHeaderWindow : public wxWindow +{ +protected: + wxTreeListMainWindow *m_owner; + const wxCursor *m_currentCursor; + const wxCursor *m_resizeCursor; + bool m_isDragging; + + // column being resized + int m_column; + + // divider line position in logical (unscrolled) coords + int m_currentX; + + // minimal position beyond which the divider line can't be dragged in + // logical coords + int m_minX; + + wxArrayTreeListColumnInfo m_columns; + + // total width of the columns + int m_total_col_width; + +#if wxCHECK_VERSION_FULL(2, 7, 0, 1) + // which col header is currently highlighted with mouse-over + int m_hotTrackCol; + int XToCol(int x); + void RefreshColLabel(int col); +#endif + +public: + wxTreeListHeaderWindow(); + + wxTreeListHeaderWindow( wxWindow *win, + wxWindowID id, + wxTreeListMainWindow *owner, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, + long style = 0, + const wxString &name = _T("wxtreelistctrlcolumntitles") ); + + virtual ~wxTreeListHeaderWindow(); + + void DoDrawRect( wxDC *dc, int x, int y, int w, int h ); + void DrawCurrent(); + void AdjustDC(wxDC& dc); + + void OnPaint( wxPaintEvent &event ); + void OnEraseBackground(wxEraseEvent& WXUNUSED(event)) { ;; } // reduce flicker + void OnMouse( wxMouseEvent &event ); + void OnSetFocus( wxFocusEvent &event ); + + // total width of all columns + int GetWidth() const { return m_total_col_width; } + + // column manipulation + int GetColumnCount() const { return (int)m_columns.GetCount(); } + + void AddColumn (const wxTreeListColumnInfo& colInfo); + + void InsertColumn (int before, const wxTreeListColumnInfo& colInfo); + + void RemoveColumn (int column); + + // column information manipulation + const wxTreeListColumnInfo& GetColumn (int column) const{ + wxCHECK_MSG ((column >= 0) && (column < GetColumnCount()), + wxInvalidTreeListColumnInfo, _T("Invalid column")); + return m_columns[column]; + } + wxTreeListColumnInfo& GetColumn (int column) { + wxCHECK_MSG ((column >= 0) && (column < GetColumnCount()), + wxInvalidTreeListColumnInfo, _T("Invalid column")); + return m_columns[column]; + } + void SetColumn (int column, const wxTreeListColumnInfo& info); + + wxString GetColumnText (int column) const { + wxCHECK_MSG ((column >= 0) && (column < GetColumnCount()), + wxEmptyString, _T("Invalid column")); + return m_columns[column].GetText(); + } + void SetColumnText (int column, const wxString& text) { + wxCHECK_RET ((column >= 0) && (column < GetColumnCount()), + _T("Invalid column")); + m_columns[column].SetText (text); + } + + int GetColumnAlignment (int column) const { + wxCHECK_MSG ((column >= 0) && (column < GetColumnCount()), + wxALIGN_LEFT, _T("Invalid column")); + return m_columns[column].GetAlignment(); + } + void SetColumnAlignment (int column, int flag) { + wxCHECK_RET ((column >= 0) && (column < GetColumnCount()), + _T("Invalid column")); + m_columns[column].SetAlignment (flag); + } + + int GetColumnWidth (int column) const { + wxCHECK_MSG ((column >= 0) && (column < GetColumnCount()), + -1, _T("Invalid column")); + return m_columns[column].GetWidth(); + } + void SetColumnWidth (int column, int width); + + bool IsColumnEditable (int column) const { + wxCHECK_MSG ((column >= 0) && (column < GetColumnCount()), + false, _T("Invalid column")); + return m_columns[column].IsEditable(); + } + + bool IsColumnShown (int column) const { + wxCHECK_MSG ((column >= 0) && (column < GetColumnCount()), + true, _T("Invalid column")); + return m_columns[column].IsShown(); + } + + // needs refresh + bool m_dirty; + +private: + // common part of all ctors + void Init(); + + void SendListEvent(wxEventType type, wxPoint pos); + + DECLARE_DYNAMIC_CLASS(wxTreeListHeaderWindow) + DECLARE_EVENT_TABLE() +}; + + +//----------------------------------------------------------------------------- +// wxTreeListMainWindow (internal) +//----------------------------------------------------------------------------- + +class wxEditTextCtrl; + + +// this is the "true" control +class wxTreeListMainWindow: public wxScrolledWindow +{ +public: + // creation + // -------- + wxTreeListMainWindow() { Init(); } + + wxTreeListMainWindow (wxTreeListCtrl *parent, wxWindowID id = -1, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTR_DEFAULT_STYLE, + const wxValidator &validator = wxDefaultValidator, + const wxString& name = _T("wxtreelistmainwindow")) + { + Init(); + Create (parent, id, pos, size, style, validator, name); + } + + virtual ~wxTreeListMainWindow(); + + bool Create(wxTreeListCtrl *parent, wxWindowID id = -1, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTR_DEFAULT_STYLE, + const wxValidator &validator = wxDefaultValidator, + const wxString& name = _T("wxtreelistctrl")); + + // accessors + // --------- + + // return true if this is a virtual list control + bool IsVirtual() const { return HasFlag(wxTR_VIRTUAL); } + + // get the total number of items in the control + size_t GetCount() const; + + // indent is the number of pixels the children are indented relative to + // the parents position. SetIndent() also redraws the control + // immediately. + unsigned int GetIndent() const { return m_indent; } + void SetIndent(unsigned int indent); + + // see wxTreeListCtrl for the meaning + unsigned int GetLineSpacing() const { return m_linespacing; } + void SetLineSpacing(unsigned int spacing); + + // image list: these functions allow to associate an image list with + // the control and retrieve it. Note that when assigned with + // SetImageList, the control does _not_ delete + // the associated image list when it's deleted in order to allow image + // lists to be shared between different controls. If you use + // AssignImageList, the control _does_ delete the image list. + + // The normal image list is for the icons which correspond to the + // normal tree item state (whether it is selected or not). + // Additionally, the application might choose to show a state icon + // which corresponds to an app-defined item state (for example, + // checked/unchecked) which are taken from the state image list. + wxImageList *GetImageList() const { return m_imageListNormal; } + wxImageList *GetStateImageList() const { return m_imageListState; } + wxImageList *GetButtonsImageList() const { return m_imageListButtons; } + + void SetImageList(wxImageList *imageList); + void SetStateImageList(wxImageList *imageList); + void SetButtonsImageList(wxImageList *imageList); + void AssignImageList(wxImageList *imageList); + void AssignStateImageList(wxImageList *imageList); + void AssignButtonsImageList(wxImageList *imageList); + + void SetToolTip(const wxString& tip); + void SetToolTip(wxToolTip *tip); + void SetItemToolTip(const wxTreeItemId& item, const wxString &tip); + + // Functions to work with tree ctrl items. + + // accessors + // --------- + + // retrieve item's label + wxString GetItemText (const wxTreeItemId& item) const + { return GetItemText (item, GetMainColumn()); } + wxString GetItemText (const wxTreeItemId& item, int column) const; + wxString GetItemText (wxTreeItemData* item, int column) const; + + // get one of the images associated with the item (normal by default) + int GetItemImage (const wxTreeItemId& item, + wxTreeItemIcon which = wxTreeItemIcon_Normal) const + { return GetItemImage (item, GetMainColumn(), which); } + int GetItemImage (const wxTreeItemId& item, int column, + wxTreeItemIcon which = wxTreeItemIcon_Normal) const; + + // get the data associated with the item + wxTreeItemData *GetItemData(const wxTreeItemId& item) const; + + bool GetItemBold(const wxTreeItemId& item) const; + wxColour GetItemTextColour(const wxTreeItemId& item) const; + wxColour GetItemBackgroundColour(const wxTreeItemId& item) const; + wxFont GetItemFont(const wxTreeItemId& item) const; + + // modifiers + // --------- + + // set item's label + void SetItemText (const wxTreeItemId& item, const wxString& text) + { SetItemText (item, GetMainColumn(), text); } + void SetItemText (const wxTreeItemId& item, int column, const wxString& text); + + // get one of the images associated with the item (normal by default) + void SetItemImage (const wxTreeItemId& item, int image, + wxTreeItemIcon which = wxTreeItemIcon_Normal) + { SetItemImage (item, GetMainColumn(), image, which); } + void SetItemImage (const wxTreeItemId& item, int column, int image, + wxTreeItemIcon which = wxTreeItemIcon_Normal); + + // associate some data with the item + void SetItemData(const wxTreeItemId& item, wxTreeItemData *data); + + // force appearance of [+] button near the item. This is useful to + // allow the user to expand the items which don't have any children now + // - but instead add them only when needed, thus minimizing memory + // usage and loading time. + void SetItemHasChildren(const wxTreeItemId& item, bool has = true); + + // the item will be shown in bold + void SetItemBold(const wxTreeItemId& item, bool bold = true); + + // set the item's text colour + void SetItemTextColour(const wxTreeItemId& item, const wxColour& colour); + + // set the item's background colour + void SetItemBackgroundColour(const wxTreeItemId& item, const wxColour& colour); + + // set the item's font (should be of the same height for all items) + void SetItemFont(const wxTreeItemId& item, const wxFont& font); + + // set the window font + virtual bool SetFont( const wxFont &font ); + + // set the styles. No need to specify a GetWindowStyle here since + // the base wxWindow member function will do it for us + void SetWindowStyle(const long styles); + + // item status inquiries + // --------------------- + + // is the item visible (it might be outside the view or not expanded)? + bool IsVisible(const wxTreeItemId& item, bool fullRow, bool within = true) const; + // does the item has any children? + bool HasChildren(const wxTreeItemId& item) const; + // is the item expanded (only makes sense if HasChildren())? + bool IsExpanded(const wxTreeItemId& item) const; + // is this item currently selected (the same as has focus)? + bool IsSelected(const wxTreeItemId& item) const; + // is item text in bold font? + bool IsBold(const wxTreeItemId& item) const; + // does the layout include space for a button? + + // number of children + // ------------------ + + // if 'recursively' is false, only immediate children count, otherwise + // the returned number is the number of all items in this branch + size_t GetChildrenCount(const wxTreeItemId& item, bool recursively = true); + + // navigation + // ---------- + + // wxTreeItemId.IsOk() will return false if there is no such item + + // get the root tree item + wxTreeItemId GetRootItem() const { return m_rootItem; } // implict cast from wxTreeListItem * + + // get the item currently selected, only if a single item is selected + wxTreeItemId GetSelection() const { return m_selectItem; } + + // get all the items currently selected, return count of items + size_t GetSelections(wxArrayTreeItemIds&) const; + + // get the parent of this item (may return NULL if root) + wxTreeItemId GetItemParent(const wxTreeItemId& item) const; + + // for this enumeration function you must pass in a "cookie" parameter + // which is opaque for the application but is necessary for the library + // to make these functions reentrant (i.e. allow more than one + // enumeration on one and the same object simultaneously). Of course, + // the "cookie" passed to GetFirstChild() and GetNextChild() should be + // the same! + + // get child of this item +#if !wxCHECK_VERSION(2, 5, 0) + wxTreeItemId GetFirstChild(const wxTreeItemId& item, long& cookie) const; + wxTreeItemId GetNextChild(const wxTreeItemId& item, long& cookie) const; + wxTreeItemId GetPrevChild(const wxTreeItemId& item, long& cookie) const; + wxTreeItemId GetLastChild(const wxTreeItemId& item, long& cookie) const; +#else + wxTreeItemId GetFirstChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const; + wxTreeItemId GetNextChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const; + wxTreeItemId GetPrevChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const; + wxTreeItemId GetLastChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const; +#endif + + // get sibling of this item + wxTreeItemId GetNextSibling(const wxTreeItemId& item) const; + wxTreeItemId GetPrevSibling(const wxTreeItemId& item) const; + + // get item in the full tree (currently only for internal use) + wxTreeItemId GetNext(const wxTreeItemId& item, bool fulltree = true) const; + wxTreeItemId GetPrev(const wxTreeItemId& item, bool fulltree = true) const; + + // get expanded item, see IsExpanded() + wxTreeItemId GetFirstExpandedItem() const; + wxTreeItemId GetNextExpanded(const wxTreeItemId& item) const; + wxTreeItemId GetPrevExpanded(const wxTreeItemId& item) const; + + // get visible item, see IsVisible() + wxTreeItemId GetFirstVisible( bool fullRow, bool within) const; + wxTreeItemId GetNextVisible (const wxTreeItemId& item, bool fullRow, bool within) const; + wxTreeItemId GetPrevVisible (const wxTreeItemId& item, bool fullRow, bool within) const; + wxTreeItemId GetLastVisible ( bool fullRow, bool within) const; + + // operations + // ---------- + + // add the root node to the tree + wxTreeItemId AddRoot (const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // insert a new item in as the first child of the parent + wxTreeItemId PrependItem(const wxTreeItemId& parent, + const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // insert a new item after a given one + wxTreeItemId InsertItem(const wxTreeItemId& parent, + const wxTreeItemId& idPrevious, + const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // insert a new item before the one with the given index + wxTreeItemId InsertItem(const wxTreeItemId& parent, + size_t index, + const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // insert a new item in as the last child of the parent + wxTreeItemId AppendItem(const wxTreeItemId& parent, + const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // delete this item and associated data if any + void Delete(const wxTreeItemId& item); + // delete all children (but don't delete the item itself) + // NB: this won't send wxEVT_COMMAND_TREE_ITEM_DELETED events + void DeleteChildren(const wxTreeItemId& item); + // delete the root and all its children from the tree + // NB: this won't send wxEVT_COMMAND_TREE_ITEM_DELETED events + void DeleteRoot(); + + // expand this item + void Expand(const wxTreeItemId& item); + // expand this item and all subitems recursively + void ExpandAll(const wxTreeItemId& item); + // collapse the item without removing its children + void Collapse(const wxTreeItemId& item); + // collapse the item and remove all children + void CollapseAndReset(const wxTreeItemId& item); + // toggles the current state + void Toggle(const wxTreeItemId& item); + + // remove the selection from currently selected item (if any) + void Unselect(); + void UnselectAll(); + // select this item + bool SelectItem(const wxTreeItemId& item, const wxTreeItemId& prev = (wxTreeItemId*)NULL, + bool unselect_others = true); + void SelectAll(); + // make sure this item is visible (expanding the parent item and/or + // scrolling to this item if necessary) + void EnsureVisible(const wxTreeItemId& item); + // scroll to this item (but don't expand its parent) + void ScrollTo(const wxTreeItemId& item); + void AdjustMyScrollbars(); + + // The first function is more portable (because easier to implement + // on other platforms), but the second one returns some extra info. + wxTreeItemId HitTest (const wxPoint& point) + { int flags; int column; return HitTest (point, flags, column); } + wxTreeItemId HitTest (const wxPoint& point, int& flags) + { int column; return HitTest (point, flags, column); } + wxTreeItemId HitTest (const wxPoint& point, int& flags, int& column); + + + // get the bounding rectangle of the item (or of its label only) + bool GetBoundingRect(const wxTreeItemId& item, + wxRect& rect, + bool textOnly = false) const; + + // Start editing the item label: this (temporarily) replaces the item + // with a one line edit control. The item will be selected if it hadn't + // been before. + void EditLabel (const wxTreeItemId& item, int column); + + // sorting + // this function is called to compare 2 items and should return -1, 0 + // or +1 if the first item is less than, equal to or greater than the + // second one. The base class version performs alphabetic comparaison + // of item labels (GetText) + virtual int OnCompareItems(const wxTreeItemId& item1, + const wxTreeItemId& item2); + // sort the children of this item using OnCompareItems + // + // NB: this function is not reentrant and not MT-safe (FIXME)! + void SortChildren(const wxTreeItemId& item); + + // searching + wxTreeItemId FindItem (const wxTreeItemId& item, const wxString& str, int mode = 0); + + // implementation only from now on + + // overridden base class virtuals + virtual bool SetBackgroundColour(const wxColour& colour); + virtual bool SetForegroundColour(const wxColour& colour); + + // drop over item + void SetDragItem (const wxTreeItemId& item = (wxTreeItemId*)NULL); + + // callbacks + void OnPaint( wxPaintEvent &event ); + void OnEraseBackground(wxEraseEvent& WXUNUSED(event)) { ;; } // to reduce flicker + void OnSetFocus( wxFocusEvent &event ); + void OnKillFocus( wxFocusEvent &event ); + void OnChar( wxKeyEvent &event ); + void OnMouse( wxMouseEvent &event ); + void OnIdle( wxIdleEvent &event ); + void OnScroll(wxScrollWinEvent& event); + void OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event)) { ;; } + + // implementation helpers + int GetColumnCount() const + { return m_owner->GetHeaderWindow()->GetColumnCount(); } + + void SetMainColumn (int column) + { if ((column >= 0) && (column < GetColumnCount())) m_main_column = column; } + + int GetMainColumn() const { return m_main_column; } + + int GetBestColumnWidth (int column, wxTreeItemId parent = wxTreeItemId()); + int GetItemWidth (int column, wxTreeListItem *item); + wxFont GetItemFont (wxTreeListItem *item); + + void SetFocus(); + +protected: + wxTreeListCtrl* m_owner; + + int m_main_column; + + friend class wxTreeListItem; + friend class wxTreeListRenameTimer; + friend class wxEditTextCtrl; + + wxFont m_normalFont; + wxFont m_boldFont; + + wxTreeListItem *m_rootItem; // root item + wxTreeListItem *m_curItem; // current item, either selected or marked + wxTreeListItem *m_shiftItem; // item, where the shift key was pressed + wxTreeListItem *m_selectItem; // current selected item, not with wxTR_MULTIPLE + + int m_curColumn; + + int m_btnWidth, m_btnWidth2; + int m_btnHeight, m_btnHeight2; + int m_imgWidth, m_imgWidth2; + int m_imgHeight, m_imgHeight2; + unsigned short m_indent; + int m_lineHeight; + unsigned short m_linespacing; + wxPen m_dottedPen; + wxBrush *m_hilightBrush, + *m_hilightUnfocusedBrush; + bool m_hasFocus; +public: + bool m_dirty; +protected: + bool m_ownsImageListNormal, + m_ownsImageListState, + m_ownsImageListButtons; + bool m_lastOnSame; // last click on the same item as prev + bool m_left_down_selection; + + wxImageList *m_imageListNormal, + *m_imageListState, + *m_imageListButtons; + + bool m_isDragStarted; // set at the very beginning of dragging + bool m_isDragging; // set once a drag begin event was fired + wxPoint m_dragStartPos; // set whenever m_isDragStarted is set to true + wxTreeListItem *m_dragItem; + int m_dragCol; + + wxTreeListItem *m_editItem; // item, which is currently edited + wxTimer *m_editTimer; + bool m_editAccept; // currently unused, OnRenameAccept() argument makes it redundant + wxString m_editRes; + int m_editCol; + wxEditTextCtrl *m_editControl; + + // char navigation + wxTimer *m_findTimer; + wxString m_findStr; + + bool m_isItemToolTip; // true if individual item tooltips were set (disable global tooltip) + wxString m_toolTip; // global tooltip + wxTreeListItem *m_toolTipItem; // item whose tip is currently shown (NULL==global, -1==not displayed) + + // the common part of all ctors + void Init(); + + // misc helpers + wxTreeItemId DoInsertItem(const wxTreeItemId& parent, + size_t previous, + const wxString& text, + int image, int selectedImage, + wxTreeItemData *data); + void DoDeleteItem (wxTreeListItem *item); + void SetCurrentItem(wxTreeListItem *item); + bool HasButtons(void) const + { return (m_imageListButtons) || HasFlag (wxTR_TWIST_BUTTONS|wxTR_HAS_BUTTONS); } + + void CalculateLineHeight(); + int GetLineHeight(wxTreeListItem *item) const; + void PaintLevel( wxTreeListItem *item, wxDC& dc, int level, int &y, + int x_maincol); + void PaintItem( wxTreeListItem *item, wxDC& dc); + + void CalculateLevel( wxTreeListItem *item, wxDC &dc, int level, int &y, + int x_maincol); + void CalculatePositions(); + void CalculateSize( wxTreeListItem *item, wxDC &dc ); + + void RefreshSubtree (wxTreeListItem *item); + void RefreshLine (wxTreeListItem *item); + // redraw all selected items + void RefreshSelected(); + // RefreshSelected() recursive helper + void RefreshSelectedUnder (wxTreeListItem *item); + + void OnRenameTimer(); + void OnRenameAccept(bool isCancelled); + + void FillArray(wxTreeListItem*, wxArrayTreeItemIds&) const; + bool TagAllChildrenUntilLast (wxTreeListItem *crt_item, wxTreeListItem *last_item); + bool TagNextChildren (wxTreeListItem *crt_item, wxTreeListItem *last_item); + void UnselectAllChildren (wxTreeListItem *item ); + bool SendEvent(wxEventType event_type, wxTreeListItem *item = NULL, wxTreeEvent *event = NULL); // returns true if processed + +private: + DECLARE_EVENT_TABLE() + DECLARE_DYNAMIC_CLASS(wxTreeListMainWindow) +}; + + +// timer used for enabling in-place edit +class wxTreeListRenameTimer: public wxTimer +{ +public: + wxTreeListRenameTimer( wxTreeListMainWindow *owner ); + + void Notify(); + +private: + wxTreeListMainWindow *m_owner; +}; + +// control used for in-place edit +class wxEditTextCtrl: public wxTextCtrl +{ +public: + wxEditTextCtrl (wxWindow *parent, + const wxWindowID id, + bool *accept, + wxString *res, + wxTreeListMainWindow *owner, + const wxString &value = wxEmptyString, + const wxPoint &pos = wxDefaultPosition, + const wxSize &size = wxDefaultSize, + int style = 0, + const wxValidator& validator = wxDefaultValidator, + const wxString &name = wxTextCtrlNameStr ); + ~wxEditTextCtrl(); + + virtual bool Destroy(); // wxWindow override + void EndEdit(bool isCancelled); + void SetOwner(wxTreeListMainWindow *owner) { m_owner = owner; } + + void OnChar( wxKeyEvent &event ); + void OnKeyUp( wxKeyEvent &event ); + void OnKillFocus( wxFocusEvent &event ); + + +private: + wxTreeListMainWindow *m_owner; + bool *m_accept; + wxString *m_res; + wxString m_startValue; + bool m_finished; // true==deleting, don't process events anymore + + DECLARE_EVENT_TABLE() +}; + + +// a tree item (NOTE: this class is storage only, does not generate events) +class wxTreeListItem +{ +public: + // ctors & dtor + wxTreeListItem() { m_data = NULL; m_toolTip = NULL; } + wxTreeListItem( wxTreeListMainWindow *owner, + wxTreeListItem *parent, + const wxArrayString& text, + int image, + int selImage, + wxTreeItemData *data ); + + ~wxTreeListItem(); + + // trivial accessors + wxArrayTreeListItems& GetChildren() { return m_children; } + + const wxString GetText() const + { + return GetText(0); + } + const wxString GetText (int column) const + { + if(m_text.GetCount() > 0) + { + if( IsVirtual() ) return m_owner->GetItemText( m_data, column ); + else return m_text[column]; + } + return wxEmptyString; + } + + int GetImage (wxTreeItemIcon which = wxTreeItemIcon_Normal) const + { return m_images[which]; } + int GetImage (int column, wxTreeItemIcon which=wxTreeItemIcon_Normal) const + { + if(column == m_owner->GetMainColumn()) return m_images[which]; + if(column < (int)m_col_images.GetCount()) return m_col_images[column]; + return NO_IMAGE; + } + + wxTreeItemData *GetData() const { return m_data; } + + const wxString * GetToolTip() const { return m_toolTip; } + + // returns the current image for the item (depending on its + // selected/expanded/whatever state) + int GetCurrentImage() const; + + void SetText (const wxString &text ); + void SetText (int column, const wxString& text) + { + if (column < (int)m_text.GetCount()) { + m_text[column] = text; + }else if (column < m_owner->GetColumnCount()) { + int howmany = m_owner->GetColumnCount(); + for (int i = (int)m_text.GetCount(); i < howmany; ++i) m_text.Add (wxEmptyString); + m_text[column] = text; + } + } + void SetImage (int image, wxTreeItemIcon which) { m_images[which] = image; } + void SetImage (int column, int image, wxTreeItemIcon which) + { + if (column == m_owner->GetMainColumn()) { + m_images[which] = image; + }else if (column < (int)m_col_images.GetCount()) { + m_col_images[column] = image; + }else if (column < m_owner->GetColumnCount()) { + int howmany = m_owner->GetColumnCount(); + for (int i = (int)m_col_images.GetCount(); i < howmany; ++i) m_col_images.Add (NO_IMAGE); + m_col_images[column] = image; + } + } + + void SetData(wxTreeItemData *data) { m_data = data; } + + void SetToolTip(const wxString &tip) { + if (m_toolTip) { + delete m_toolTip; m_toolTip = NULL; + } + if (tip.length() > 0) { + m_toolTip = new wxString(tip); + } + } + + void SetHasPlus(bool has = true) { m_hasPlus = has; } + + void SetBold(bool bold) { m_isBold = bold; } + + int GetX() const { return m_x; } + int GetY() const { return m_y; } + + void SetX (int x) { m_x = x; } + void SetY (int y) { m_y = y; } + + int GetHeight() const { return m_height; } + int GetWidth() const { return m_width; } + + void SetHeight (int height) { m_height = height; } + void SetWidth (int width) { m_width = width; } + + int GetTextX() const { return m_text_x; } + void SetTextX (int text_x) { m_text_x = text_x; } + + wxTreeListItem *GetItemParent() const { return m_parent; } + + // operations + // deletes all children + void DeleteChildren(); + + // get count of all children (and grand children if 'recursively') + size_t GetChildrenCount(bool recursively = true) const; + + void Insert(wxTreeListItem *child, size_t index) + { m_children.Insert(child, index); } + + void GetSize( int &x, int &y, const wxTreeListMainWindow* ); + + // return the item at given position (or NULL if no item), onButton is + // true if the point belongs to the item's button, otherwise it lies + // on the button's label + wxTreeListItem *HitTest (const wxPoint& point, + const wxTreeListMainWindow *, + int &flags, int& column, int level); + + void Expand() { m_isCollapsed = false; } + void Collapse() { m_isCollapsed = true; } + + void SetHilight( bool set = true ) { m_hasHilight = set; } + + // status inquiries + bool HasChildren() const { return !m_children.IsEmpty(); } + bool IsSelected() const { return m_hasHilight != 0; } + bool IsExpanded() const { return !m_isCollapsed; } + bool HasPlus() const { return m_hasPlus || HasChildren(); } + bool IsBold() const { return m_isBold != 0; } + bool IsVirtual() const { return m_owner->IsVirtual(); } + + // attributes + // get them - may be NULL + wxTreeItemAttr *GetAttributes() const { return m_attr; } + // get them ensuring that the pointer is not NULL + wxTreeItemAttr& Attr() + { + if ( !m_attr ) + { + m_attr = new wxTreeItemAttr; + m_ownsAttr = true; + } + return *m_attr; + } + // set them + void SetAttributes(wxTreeItemAttr *attr) + { + if ( m_ownsAttr ) delete m_attr; + m_attr = attr; + m_ownsAttr = false; + } + // set them and delete when done + void AssignAttributes(wxTreeItemAttr *attr) + { + SetAttributes(attr); + m_ownsAttr = true; + } + +private: + wxTreeListMainWindow *m_owner; // control the item belongs to + + // since there can be very many of these, we save size by chosing + // the smallest representation for the elements and by ordering + // the members to avoid padding. + wxArrayString m_text; // labels to be rendered for item + + wxTreeItemData *m_data; // user-provided data + + wxString *m_toolTip; + + wxArrayTreeListItems m_children; // list of children + wxTreeListItem *m_parent; // parent of this item + + wxTreeItemAttr *m_attr; // attributes??? + + // tree ctrl images for the normal, selected, expanded and + // expanded+selected states + short m_images[wxTreeItemIcon_Max]; + wxArrayShort m_col_images; // images for the various columns (!= main) + + // main column item positions + wxCoord m_x; // (virtual) offset from left (vertical line) + wxCoord m_y; // (virtual) offset from top + wxCoord m_text_x; // item offset from left + short m_width; // width of this item + unsigned char m_height; // height of this item + + // use bitfields to save size + int m_isCollapsed :1; + int m_hasHilight :1; // same as focused + int m_hasPlus :1; // used for item which doesn't have + // children but has a [+] button + int m_isBold :1; // render the label in bold font + int m_ownsAttr :1; // delete attribute when done +}; + +// =========================================================================== +// implementation +// =========================================================================== + +// --------------------------------------------------------------------------- +// wxTreeListRenameTimer (internal) +// --------------------------------------------------------------------------- + +wxTreeListRenameTimer::wxTreeListRenameTimer( wxTreeListMainWindow *owner ) +{ + m_owner = owner; +} + +void wxTreeListRenameTimer::Notify() +{ + m_owner->OnRenameTimer(); +} + +//----------------------------------------------------------------------------- +// wxEditTextCtrl (internal) +//----------------------------------------------------------------------------- + +BEGIN_EVENT_TABLE (wxEditTextCtrl,wxTextCtrl) + EVT_CHAR (wxEditTextCtrl::OnChar) + EVT_KEY_UP (wxEditTextCtrl::OnKeyUp) + EVT_KILL_FOCUS (wxEditTextCtrl::OnKillFocus) +END_EVENT_TABLE() + +wxEditTextCtrl::wxEditTextCtrl (wxWindow *parent, + const wxWindowID id, + bool *accept, + wxString *res, + wxTreeListMainWindow *owner, + const wxString &value, + const wxPoint &pos, + const wxSize &size, + int style, + const wxValidator& validator, + const wxString &name) + : wxTextCtrl (parent, id, value, pos, size, style | wxSIMPLE_BORDER, validator, name) +{ + m_res = res; + m_accept = accept; + m_owner = owner; + (*m_accept) = false; + (*m_res) = wxEmptyString; + m_startValue = value; + m_finished = false; +} + +wxEditTextCtrl::~wxEditTextCtrl() { + EndEdit(true); // cancelled +} + +void wxEditTextCtrl::EndEdit(bool isCancelled) { + if (m_finished) return; + m_finished = true; + + if (m_owner) { + (*m_accept) = ! isCancelled; + (*m_res) = isCancelled ? m_startValue : GetValue(); + m_owner->OnRenameAccept(*m_res == m_startValue); + m_owner->m_editControl = NULL; + m_owner->m_editItem = NULL; + m_owner->SetFocus(); // This doesn't work. TODO. + m_owner = NULL; + } + + Destroy(); +} + +bool wxEditTextCtrl::Destroy() { + Hide(); + wxTheApp->GetTraits()->ScheduleForDestroy(this); + return true; +} + +void wxEditTextCtrl::OnChar( wxKeyEvent &event ) +{ + if (m_finished) + { + event.Skip(); + return; + } + if (event.GetKeyCode() == WXK_RETURN) + { + EndEdit(false); // not cancelled + return; + } + if (event.GetKeyCode() == WXK_ESCAPE) + { + EndEdit(true); // cancelled + return; + } + event.Skip(); +} + +void wxEditTextCtrl::OnKeyUp( wxKeyEvent &event ) +{ + if (m_finished) + { + event.Skip(); + return; + } + + // auto-grow the textctrl: + wxSize parentSize = m_owner->GetSize(); + wxPoint myPos = GetPosition(); + wxSize mySize = GetSize(); + int sx, sy; + GetTextExtent(GetValue() + _T("M"), &sx, &sy); + if (myPos.x + sx > parentSize.x) sx = parentSize.x - myPos.x; + if (mySize.x > sx) sx = mySize.x; + SetSize(sx, -1); + + event.Skip(); +} + +void wxEditTextCtrl::OnKillFocus( wxFocusEvent &event ) +{ + if (m_finished) + { + event.Skip(); + return; + } + + EndEdit(false); // not cancelled +} + +//----------------------------------------------------------------------------- +// wxTreeListHeaderWindow +//----------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxTreeListHeaderWindow,wxWindow); + +BEGIN_EVENT_TABLE(wxTreeListHeaderWindow,wxWindow) + EVT_PAINT (wxTreeListHeaderWindow::OnPaint) + EVT_ERASE_BACKGROUND(wxTreeListHeaderWindow::OnEraseBackground) // reduce flicker + EVT_MOUSE_EVENTS (wxTreeListHeaderWindow::OnMouse) + EVT_SET_FOCUS (wxTreeListHeaderWindow::OnSetFocus) +END_EVENT_TABLE() + + +void wxTreeListHeaderWindow::Init() +{ + m_currentCursor = (wxCursor *) NULL; + m_isDragging = false; + m_dirty = false; + m_total_col_width = 0; +#if wxCHECK_VERSION_FULL(2, 7, 0, 1) + m_hotTrackCol = -1; +#endif + + // prevent any background repaint in order to reducing flicker + SetBackgroundStyle(wxBG_STYLE_CUSTOM); +} + +wxTreeListHeaderWindow::wxTreeListHeaderWindow() +{ + Init(); + + m_owner = (wxTreeListMainWindow *) NULL; + m_resizeCursor = (wxCursor *) NULL; +} + +wxTreeListHeaderWindow::wxTreeListHeaderWindow( wxWindow *win, + wxWindowID id, + wxTreeListMainWindow *owner, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString &name ) + : wxWindow( win, id, pos, size, style, name ) +{ + Init(); + + m_owner = owner; + m_resizeCursor = new wxCursor(wxCURSOR_SIZEWE); + +#if !wxCHECK_VERSION(2, 5, 0) + SetBackgroundColour (wxSystemSettings::GetSystemColour (wxSYS_COLOUR_BTNFACE)); +#else + SetBackgroundColour (wxSystemSettings::GetColour (wxSYS_COLOUR_BTNFACE)); +#endif +} + +wxTreeListHeaderWindow::~wxTreeListHeaderWindow() +{ + delete m_resizeCursor; +} + +void wxTreeListHeaderWindow::DoDrawRect( wxDC *dc, int x, int y, int w, int h ) +{ +#if !wxCHECK_VERSION(2, 5, 0) + wxPen pen (wxSystemSettings::GetSystemColour (wxSYS_COLOUR_BTNSHADOW ), 1, wxSOLID); +#else + wxPen pen (wxSystemSettings::GetColour (wxSYS_COLOUR_BTNSHADOW ), 1, wxSOLID); +#endif + + const int m_corner = 1; + + dc->SetBrush( *wxTRANSPARENT_BRUSH ); +#if defined( __WXMAC__ ) + dc->SetPen (pen); +#else // !GTK, !Mac + dc->SetPen( *wxBLACK_PEN ); +#endif + dc->DrawLine( x+w-m_corner+1, y, x+w, y+h ); // right (outer) + dc->DrawRectangle( x, y+h, w+1, 1 ); // bottom (outer) + +#if defined( __WXMAC__ ) + pen = wxPen( wxColour( 0x88 , 0x88 , 0x88 ), 1, wxSOLID ); +#endif + dc->SetPen( pen ); + dc->DrawLine( x+w-m_corner, y, x+w-1, y+h ); // right (inner) + dc->DrawRectangle( x+1, y+h-1, w-2, 1 ); // bottom (inner) + + dc->SetPen( *wxWHITE_PEN ); + dc->DrawRectangle( x, y, w-m_corner+1, 1 ); // top (outer) + dc->DrawRectangle( x, y, 1, h ); // left (outer) + dc->DrawLine( x, y+h-1, x+1, y+h-1 ); + dc->DrawLine( x+w-1, y, x+w-1, y+1 ); +} + +// shift the DC origin to match the position of the main window horz +// scrollbar: this allows us to always use logical coords +void wxTreeListHeaderWindow::AdjustDC(wxDC& dc) +{ + int xpix; + m_owner->GetScrollPixelsPerUnit( &xpix, NULL ); + int x; + m_owner->GetViewStart( &x, NULL ); + + // account for the horz scrollbar offset + dc.SetDeviceOrigin( -x * xpix, 0 ); +} + +void wxTreeListHeaderWindow::OnPaint( wxPaintEvent &WXUNUSED(event) ) +{ + wxAutoBufferedPaintDC dc( this ); + AdjustDC( dc ); + + int x = HEADER_OFFSET_X; + + // width and height of the entire header window + int w, h; + GetClientSize( &w, &h ); + m_owner->CalcUnscrolledPosition(w, 0, &w, NULL); + dc.SetBackgroundMode(wxTRANSPARENT); + +#if wxCHECK_VERSION_FULL(2, 7, 0, 1) + int numColumns = GetColumnCount(); + for ( int i = 0; i < numColumns && x < w; i++ ) + { + if (!IsColumnShown (i)) continue; // do next column if not shown + + wxHeaderButtonParams params; + + // TODO: columnInfo should have label colours... + params.m_labelColour = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ); + params.m_labelFont = GetFont(); + + wxTreeListColumnInfo& column = GetColumn(i); + int wCol = column.GetWidth(); + int flags = 0; + wxRect rect(x, 0, wCol, h); + x += wCol; + + if ( i == m_hotTrackCol) + flags |= wxCONTROL_CURRENT; + + params.m_labelText = column.GetText(); + params.m_labelAlignment = column.GetAlignment(); + + int image = column.GetImage(); + wxImageList* imageList = m_owner->GetImageList(); + if ((image != -1) && imageList) + params.m_labelBitmap = imageList->GetBitmap(image); + + wxRendererNative::Get().DrawHeaderButton(this, dc, rect, flags, wxHDR_SORT_ICON_NONE, ¶ms); + } + + if (x < w) { + wxRect rect(x, 0, w-x, h); + wxRendererNative::Get().DrawHeaderButton(this, dc, rect); + } + +#else // not 2.7.0.1+ + + dc.SetFont( GetFont() ); + + // do *not* use the listctrl colour for headers - one day we will have a + // function to set it separately + //dc.SetTextForeground( *wxBLACK ); +#if !wxCHECK_VERSION(2, 5, 0) + dc.SetTextForeground (wxSystemSettings::GetSystemColour( wxSYS_COLOUR_WINDOWTEXT )); +#else + dc.SetTextForeground (wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT )); +#endif + + int numColumns = GetColumnCount(); + for ( int i = 0; i < numColumns && x < w; i++ ) + { + if (!IsColumnShown (i)) continue; // do next column if not shown + + wxTreeListColumnInfo& column = GetColumn(i); + int wCol = column.GetWidth(); + + // the width of the rect to draw: make it smaller to fit entirely + // inside the column rect + int cw = wCol - 2; + +#if !wxCHECK_VERSION(2, 7, 0) + dc.SetPen( *wxWHITE_PEN ); + DoDrawRect( &dc, x, HEADER_OFFSET_Y, cw, h-2 ); +#else + wxRect rect(x, HEADER_OFFSET_Y, cw, h-2); + wxRendererNative::GetDefault().DrawHeaderButton (this, dc, rect); +#endif + + // if we have an image, draw it on the right of the label + int image = column.GetImage(); //item.m_image; + int ix = -2, iy = 0; + wxImageList* imageList = m_owner->GetImageList(); + if ((image != -1) && imageList) { + imageList->GetSize (image, ix, iy); + } + + // extra margins around the text label + int text_width = 0; + int text_x = x; + int image_offset = cw - ix - 1; + + switch(column.GetAlignment()) { + case wxALIGN_LEFT: + text_x += EXTRA_WIDTH; + cw -= ix + 2; + break; + case wxALIGN_RIGHT: + dc.GetTextExtent (column.GetText(), &text_width, NULL); + text_x += cw - text_width - EXTRA_WIDTH - MARGIN; + image_offset = 0; + break; + case wxALIGN_CENTER: + dc.GetTextExtent(column.GetText(), &text_width, NULL); + text_x += (cw - text_width)/2 + ix + 2; + image_offset = (cw - text_width - ix - 2)/2 - MARGIN; + break; + } + + // draw the image + if ((image != -1) && imageList) { + imageList->Draw (image, dc, x + image_offset/*cw - ix - 1*/, + HEADER_OFFSET_Y + (h - 4 - iy)/2, + wxIMAGELIST_DRAW_TRANSPARENT); + } + + // draw the text clipping it so that it doesn't overwrite the column boundary + wxDCClipper clipper(dc, x, HEADER_OFFSET_Y, cw, h - 4 ); + dc.DrawText (column.GetText(), text_x, HEADER_OFFSET_Y + EXTRA_HEIGHT ); + + // next column + x += wCol; + } + + int more_w = m_owner->GetSize().x - x - HEADER_OFFSET_X; + if (more_w > 0) { +#if !wxCHECK_VERSION(2, 7, 0) + DoDrawRect (&dc, x, HEADER_OFFSET_Y, more_w, h-2 ); +#else + wxRect rect (x, HEADER_OFFSET_Y, more_w, h-2); + wxRendererNative::GetDefault().DrawHeaderButton (this, dc, rect); +#endif + } + +#endif // 2.7.0.1 +} + +void wxTreeListHeaderWindow::DrawCurrent() +{ + int x1 = m_currentX; + int y1 = 0; + ClientToScreen (&x1, &y1); + + int x2 = m_currentX-1; +#ifdef __WXMSW__ + ++x2; // but why ???? +#endif + int y2 = 0; + m_owner->GetClientSize( NULL, &y2 ); + m_owner->ClientToScreen( &x2, &y2 ); + + wxScreenDC dc; + dc.SetLogicalFunction (wxINVERT); + dc.SetPen (wxPen (*wxBLACK, 2, wxSOLID)); + dc.SetBrush (*wxTRANSPARENT_BRUSH); + + AdjustDC(dc); + dc.DrawLine (x1, y1, x2, y2); + dc.SetLogicalFunction (wxCOPY); + dc.SetPen (wxNullPen); + dc.SetBrush (wxNullBrush); +} + +#if wxCHECK_VERSION_FULL(2, 7, 0, 1) +int wxTreeListHeaderWindow::XToCol(int x) +{ + int colLeft = 0; + int numColumns = GetColumnCount(); + for ( int col = 0; col < numColumns; col++ ) + { + if (!IsColumnShown(col)) continue; + wxTreeListColumnInfo& column = GetColumn(col); + + if ( x < (colLeft + column.GetWidth()) ) + return col; + + colLeft += column.GetWidth(); + } + return -1; +} + +void wxTreeListHeaderWindow::RefreshColLabel(int col) +{ + if ( col > GetColumnCount() ) + return; + + int x = 0; + int width = 0; + int idx = 0; + do { + if (!IsColumnShown(idx)) continue; + wxTreeListColumnInfo& column = GetColumn(idx); + x += width; + width = column.GetWidth(); + } while (++idx <= col); + + m_owner->CalcScrolledPosition(x, 0, &x, NULL); + RefreshRect(wxRect(x, 0, width, GetSize().GetHeight())); +} +#endif + +void wxTreeListHeaderWindow::OnMouse (wxMouseEvent &event) { + + // we want to work with logical coords + int x; + m_owner->CalcUnscrolledPosition(event.GetX(), 0, &x, NULL); + +#if wxCHECK_VERSION_FULL(2, 7, 0, 1) + if ( event.Moving() ) + { + int col = XToCol(x); + if ( col != m_hotTrackCol ) + { + // Refresh the col header so it will be painted with hot tracking + // (if supported by the native renderer.) + RefreshColLabel(col); + + // Also refresh the old hot header + if ( m_hotTrackCol >= 0 ) + RefreshColLabel(m_hotTrackCol); + + m_hotTrackCol = col; + } + } + + if ( event.Leaving() && m_hotTrackCol >= 0 ) + { + // Leaving the window so clear any hot tracking indicator that may be present + RefreshColLabel(m_hotTrackCol); + m_hotTrackCol = -1; + } +#endif + + if (m_isDragging) { + + SendListEvent (wxEVT_COMMAND_LIST_COL_DRAGGING, event.GetPosition()); + + // we don't draw the line beyond our window, but we allow dragging it + // there + int w = 0; + GetClientSize( &w, NULL ); + m_owner->CalcUnscrolledPosition(w, 0, &w, NULL); + w -= 6; + + // erase the line if it was drawn + if (m_currentX < w) DrawCurrent(); + + if (event.ButtonUp()) { + m_isDragging = false; + if (HasCapture()) ReleaseMouse(); + m_dirty = true; + SetColumnWidth (m_column, m_currentX - m_minX); + Refresh(); + SendListEvent (wxEVT_COMMAND_LIST_COL_END_DRAG, event.GetPosition()); + }else{ + m_currentX = wxMax (m_minX + 7, x); + + // draw in the new location + if (m_currentX < w) DrawCurrent(); + } + + }else{ // not dragging + + m_minX = 0; + bool hit_border = false; + + // end of the current column + int xpos = 0; + + // find the column where this event occured + int countCol = GetColumnCount(); + for (int column = 0; column < countCol; column++) { + if (!IsColumnShown (column)) continue; // do next if not shown + + xpos += GetColumnWidth (column); + m_column = column; + if (abs (x-xpos) < 3) { + // near the column border + hit_border = true; + break; + } + + if (x < xpos) { + // inside the column + break; + } + + m_minX = xpos; + } + + if (event.LeftDown() || event.RightUp()) { + if (hit_border && event.LeftDown()) { + m_isDragging = true; + CaptureMouse(); + m_currentX = x; + DrawCurrent(); + SendListEvent (wxEVT_COMMAND_LIST_COL_BEGIN_DRAG, event.GetPosition()); + }else{ // click on a column + wxEventType evt = event.LeftDown()? wxEVT_COMMAND_LIST_COL_CLICK: + wxEVT_COMMAND_LIST_COL_RIGHT_CLICK; + SendListEvent (evt, event.GetPosition()); + } + }else if (event.LeftDClick() && hit_border) { + SetColumnWidth (m_column, m_owner->GetBestColumnWidth (m_column)); + Refresh(); + + }else if (event.Moving()) { + bool setCursor; + if (hit_border) { + setCursor = m_currentCursor == wxSTANDARD_CURSOR; + m_currentCursor = m_resizeCursor; + }else{ + setCursor = m_currentCursor != wxSTANDARD_CURSOR; + m_currentCursor = wxSTANDARD_CURSOR; + } + if (setCursor) SetCursor (*m_currentCursor); + } + + } +} + +void wxTreeListHeaderWindow::OnSetFocus (wxFocusEvent &WXUNUSED(event)) { + m_owner->SetFocus(); +} + +void wxTreeListHeaderWindow::SendListEvent (wxEventType type, wxPoint pos) { + wxWindow *parent = GetParent(); + wxListEvent le (type, parent->GetId()); + le.SetEventObject (parent); + le.m_pointDrag = pos; + + // the position should be relative to the parent window, not + // this one for compatibility with MSW and common sense: the + // user code doesn't know anything at all about this header + // window, so why should it get positions relative to it? + le.m_pointDrag.y -= GetSize().y; + le.m_col = m_column; + parent->GetEventHandler()->ProcessEvent (le); +} + +void wxTreeListHeaderWindow::AddColumn (const wxTreeListColumnInfo& colInfo) { + m_columns.Add (colInfo); + m_total_col_width += colInfo.GetWidth(); + m_owner->AdjustMyScrollbars(); + m_owner->m_dirty = true; +} + +void wxTreeListHeaderWindow::SetColumnWidth (int column, int width) { + wxCHECK_RET ((column >= 0) && (column < GetColumnCount()), _T("Invalid column")); + m_total_col_width -= m_columns[column].GetWidth(); + m_columns[column].SetWidth(width); + m_total_col_width += width; + m_owner->AdjustMyScrollbars(); + m_owner->m_dirty = true; +} + +void wxTreeListHeaderWindow::InsertColumn (int before, const wxTreeListColumnInfo& colInfo) { + wxCHECK_RET ((before >= 0) && (before < GetColumnCount()), _T("Invalid column")); + m_columns.Insert (colInfo, before); + m_total_col_width += colInfo.GetWidth(); + m_owner->AdjustMyScrollbars(); + m_owner->m_dirty = true; +} + +void wxTreeListHeaderWindow::RemoveColumn (int column) { + wxCHECK_RET ((column >= 0) && (column < GetColumnCount()), _T("Invalid column")); + m_total_col_width -= m_columns[column].GetWidth(); + m_columns.RemoveAt (column); + m_owner->AdjustMyScrollbars(); + m_owner->m_dirty = true; +} + +void wxTreeListHeaderWindow::SetColumn (int column, const wxTreeListColumnInfo& info) { + wxCHECK_RET ((column >= 0) && (column < GetColumnCount()), _T("Invalid column")); + int w = m_columns[column].GetWidth(); + m_columns[column] = info; + if (w != info.GetWidth()) { + m_total_col_width += info.GetWidth() - w; + m_owner->AdjustMyScrollbars(); + } + m_owner->m_dirty = true; +} + +// --------------------------------------------------------------------------- +// wxTreeListItem +// --------------------------------------------------------------------------- + +wxTreeListItem::wxTreeListItem (wxTreeListMainWindow *owner, + wxTreeListItem *parent, + const wxArrayString& text, + int image, int selImage, + wxTreeItemData *data) + : m_text (text) { + + m_images[wxTreeItemIcon_Normal] = image; + m_images[wxTreeItemIcon_Selected] = selImage; + m_images[wxTreeItemIcon_Expanded] = NO_IMAGE; + m_images[wxTreeItemIcon_SelectedExpanded] = NO_IMAGE; + + m_data = data; + m_toolTip = NULL; + m_x = 0; + m_y = 0; + m_text_x = 0; + + m_isCollapsed = true; + m_hasHilight = false; + m_hasPlus = false; + m_isBold = false; + + m_owner = owner; + m_parent = parent; + + m_attr = (wxTreeItemAttr *)NULL; + m_ownsAttr = false; + + // We don't know the height here yet. + m_width = 0; + m_height = 0; +} + +wxTreeListItem::~wxTreeListItem() { + delete m_data; + if (m_toolTip) delete m_toolTip; + if (m_ownsAttr) delete m_attr; + + wxASSERT_MSG( m_children.IsEmpty(), _T("please call DeleteChildren() before destructor")); +} + +void wxTreeListItem::DeleteChildren () { + m_children.Empty(); +} + +void wxTreeListItem::SetText (const wxString &text) { + if (m_text.GetCount() > 0) { + m_text[0] = text; + }else{ + m_text.Add (text); + } +} + +size_t wxTreeListItem::GetChildrenCount (bool recursively) const { + size_t count = m_children.Count(); + if (!recursively) return count; + + size_t total = count; + for (size_t n = 0; n < count; ++n) { + total += m_children[n]->GetChildrenCount(); + } + return total; +} + +void wxTreeListItem::GetSize (int &x, int &y, const wxTreeListMainWindow *theButton) { + int bottomY = m_y + theButton->GetLineHeight (this); + if (y < bottomY) y = bottomY; + int width = m_x + m_width; + if ( x < width ) x = width; + + if (IsExpanded()) { + size_t count = m_children.Count(); + for (size_t n = 0; n < count; ++n ) { + m_children[n]->GetSize (x, y, theButton); + } + } +} + +wxTreeListItem *wxTreeListItem::HitTest (const wxPoint& point, + const wxTreeListMainWindow *theCtrl, + int &flags, int& column, int level) { + + // reset any previous hit infos + flags = 0; + column = -1; + + // for a hidden root node, don't evaluate it, but do evaluate children + if (!theCtrl->HasFlag(wxTR_HIDE_ROOT) || (level > 0)) { + + wxTreeListHeaderWindow* header_win = theCtrl->m_owner->GetHeaderWindow(); + + // check for right of all columns (outside) + if (point.x > header_win->GetWidth()) return (wxTreeListItem*) NULL; + // else find column + for (int x = 0, j = 0; j < theCtrl->GetColumnCount(); ++j) { + if (!header_win->IsColumnShown(j)) continue; + int w = header_win->GetColumnWidth (j); + if (point.x >= x && point.x < x+w) { + column = j; + break; + } + x += w; + } + + // evaluate if y-pos is okay + int h = theCtrl->GetLineHeight (this); + if ((point.y >= m_y) && (point.y <= m_y + h)) { + + // check for above/below middle + int y_mid = m_y + h/2; + if (point.y < y_mid) { + flags |= wxTREE_HITTEST_ONITEMUPPERPART; + }else{ + flags |= wxTREE_HITTEST_ONITEMLOWERPART; + } + + // check for button hit + if (HasPlus() && theCtrl->HasButtons()) { + int bntX = m_x - theCtrl->m_btnWidth2; + int bntY = y_mid - theCtrl->m_btnHeight2; + if ((point.x >= bntX) && (point.x <= (bntX + theCtrl->m_btnWidth)) && + (point.y >= bntY) && (point.y <= (bntY + theCtrl->m_btnHeight))) { + flags |= wxTREE_HITTEST_ONITEMBUTTON; + return this; + } + } + + // check for image hit + if (theCtrl->m_imgWidth > 0) { + int imgX = m_text_x - theCtrl->m_imgWidth - MARGIN; + int imgY = y_mid - theCtrl->m_imgHeight2; + if ((point.x >= imgX) && (point.x <= (imgX + theCtrl->m_imgWidth)) && + (point.y >= imgY) && (point.y <= (imgY + theCtrl->m_imgHeight))) { + flags |= wxTREE_HITTEST_ONITEMICON; + return this; + } + } + + // check for label hit + if ((point.x >= m_text_x) && (point.x <= (m_text_x + m_width))) { + flags |= wxTREE_HITTEST_ONITEMLABEL; + return this; + } + + // check for indent hit after button and image hit + if (point.x < m_x) { + flags |= wxTREE_HITTEST_ONITEMINDENT; +// Ronan, 2008.07.17: removed, not consistent column = -1; // considered not belonging to main column + return this; + } + + // check for right of label + int end = 0; + for (int i = 0; i <= theCtrl->GetMainColumn(); ++i) end += header_win->GetColumnWidth (i); + if ((point.x > (m_text_x + m_width)) && (point.x <= end)) { + flags |= wxTREE_HITTEST_ONITEMRIGHT; +// Ronan, 2008.07.17: removed, not consistent column = -1; // considered not belonging to main column + return this; + } + + // else check for each column except main + if (column >= 0 && column != theCtrl->GetMainColumn()) { + flags |= wxTREE_HITTEST_ONITEMCOLUMN; + return this; + } + + // no special flag or column found + return this; + + } + + // if children not expanded, return no item + if (!IsExpanded()) return (wxTreeListItem*) NULL; + } + + // in any case evaluate children + wxTreeListItem *child; + size_t count = m_children.Count(); + for (size_t n = 0; n < count; n++) { + child = m_children[n]->HitTest (point, theCtrl, flags, column, level+1); + if (child) return child; + } + + // not found + return (wxTreeListItem*) NULL; +} + +int wxTreeListItem::GetCurrentImage() const { + int image = NO_IMAGE; + if (IsExpanded()) { + if (IsSelected()) { + image = GetImage (wxTreeItemIcon_SelectedExpanded); + }else{ + image = GetImage (wxTreeItemIcon_Expanded); + } + }else{ // not expanded + if (IsSelected()) { + image = GetImage (wxTreeItemIcon_Selected); + }else{ + image = GetImage (wxTreeItemIcon_Normal); + } + } + + // maybe it doesn't have the specific image, try the default one instead + if (image == NO_IMAGE) image = GetImage(); + + return image; +} + +// --------------------------------------------------------------------------- +// wxTreeListMainWindow implementation +// --------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxTreeListMainWindow, wxScrolledWindow) + +BEGIN_EVENT_TABLE(wxTreeListMainWindow, wxScrolledWindow) + EVT_PAINT (wxTreeListMainWindow::OnPaint) + EVT_ERASE_BACKGROUND(wxTreeListMainWindow::OnEraseBackground) // to reduce flicker + EVT_MOUSE_EVENTS (wxTreeListMainWindow::OnMouse) + EVT_CHAR (wxTreeListMainWindow::OnChar) + EVT_SET_FOCUS (wxTreeListMainWindow::OnSetFocus) + EVT_KILL_FOCUS (wxTreeListMainWindow::OnKillFocus) + EVT_IDLE (wxTreeListMainWindow::OnIdle) + EVT_SCROLLWIN (wxTreeListMainWindow::OnScroll) + EVT_MOUSE_CAPTURE_LOST(wxTreeListMainWindow::OnCaptureLost) +END_EVENT_TABLE() + + +// --------------------------------------------------------------------------- +// construction/destruction +// --------------------------------------------------------------------------- + + +void wxTreeListMainWindow::Init() { + + m_rootItem = (wxTreeListItem*)NULL; + m_curItem = (wxTreeListItem*)NULL; + m_shiftItem = (wxTreeListItem*)NULL; + m_editItem = (wxTreeListItem*)NULL; + m_selectItem = (wxTreeListItem*)NULL; + + m_curColumn = -1; // no current column + + m_hasFocus = false; + m_dirty = false; + + m_lineHeight = LINEHEIGHT; + m_indent = MININDENT; // min. indent + m_linespacing = 4; + +#if !wxCHECK_VERSION(2, 5, 0) + m_hilightBrush = new wxBrush (wxSystemSettings::GetSystemColour (wxSYS_COLOUR_HIGHLIGHT), wxSOLID); + m_hilightUnfocusedBrush = new wxBrush (wxSystemSettings::GetSystemColour (wxSYS_COLOUR_BTNSHADOW), wxSOLID); +#else + m_hilightBrush = new wxBrush (wxSystemSettings::GetColour (wxSYS_COLOUR_HIGHLIGHT), wxSOLID); + m_hilightUnfocusedBrush = new wxBrush (wxSystemSettings::GetColour (wxSYS_COLOUR_BTNSHADOW), wxSOLID); +#endif + + m_imageListNormal = (wxImageList *) NULL; + m_imageListButtons = (wxImageList *) NULL; + m_imageListState = (wxImageList *) NULL; + m_ownsImageListNormal = m_ownsImageListButtons = + m_ownsImageListState = false; + + m_imgWidth = 0, m_imgWidth2 = 0; + m_imgHeight = 0, m_imgHeight2 = 0; + m_btnWidth = 0, m_btnWidth2 = 0; + m_btnHeight = 0, m_btnHeight2 = 0; + + m_isDragStarted = m_isDragging = false; + m_dragItem = NULL; + m_dragCol = -1; + + m_editTimer = new wxTreeListRenameTimer (this); + m_editControl = NULL; + + m_lastOnSame = false; + m_left_down_selection = false; + + m_findTimer = new wxTimer (this, -1); + +#if defined( __WXMAC__ ) && defined(__WXMAC_CARBON__) + m_normalFont.MacCreateThemeFont (kThemeViewsFont); +#else + m_normalFont = wxSystemSettings::GetFont (wxSYS_DEFAULT_GUI_FONT); +#endif + m_boldFont = wxFont( m_normalFont.GetPointSize(), + m_normalFont.GetFamily(), + m_normalFont.GetStyle(), + wxBOLD, + m_normalFont.GetUnderlined(), + m_normalFont.GetFaceName(), + m_normalFont.GetEncoding()); + + m_toolTip.clear(); + m_toolTipItem = (wxTreeListItem *)-1; // no tooltip displayed + m_isItemToolTip = false; // so far no item-specific tooltip +} + +bool wxTreeListMainWindow::Create (wxTreeListCtrl *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxValidator &validator, + const wxString& name) { + +#ifdef __WXMAC__ + if (style & wxTR_HAS_BUTTONS) style |= wxTR_MAC_BUTTONS; + if (style & wxTR_HAS_BUTTONS) style &= ~wxTR_HAS_BUTTONS; + style &= ~wxTR_LINES_AT_ROOT; + style |= wxTR_NO_LINES; + + int major,minor; + wxGetOsVersion( &major, &minor ); + if (major < 10) style |= wxTR_ROW_LINES; +#endif + + wxScrolledWindow::Create (parent, id, pos, size, style|wxHSCROLL|wxVSCROLL, name); + +#if wxUSE_VALIDATORS + SetValidator(validator); +#endif + +#if !wxCHECK_VERSION(2, 5, 0) + SetBackgroundColour (wxSystemSettings::GetSystemColour (wxSYS_COLOUR_LISTBOX)); +#else + SetBackgroundColour (wxSystemSettings::GetColour (wxSYS_COLOUR_LISTBOX)); +#endif + // prevent any background repaint in order to reducing flicker + SetBackgroundStyle(wxBG_STYLE_CUSTOM); + +#ifdef __WXMSW__ + { + int i, j; + wxBitmap bmp(8, 8); + wxMemoryDC bdc; + bdc.SelectObject(bmp); + bdc.SetPen(*wxGREY_PEN); + bdc.DrawRectangle(-1, -1, 10, 10); + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + if (!((i + j) & 1)) { + bdc.DrawPoint(i, j); + } + } + } + + m_dottedPen = wxPen(bmp, 1); + } +#else +//? m_dottedPen = wxPen( *wxGREY_PEN, 1, wxDOT ); // too slow under XFree86 + m_dottedPen = wxPen( _T("grey"), 0, 0 ); // Bitmap based pen is not supported by GTK! +#endif + + m_owner = parent; + m_main_column = 0; + + return true; +} + +wxTreeListMainWindow::~wxTreeListMainWindow() { + delete m_hilightBrush; + delete m_hilightUnfocusedBrush; + + delete m_editTimer; + delete m_findTimer; + if (m_ownsImageListNormal) delete m_imageListNormal; + if (m_ownsImageListState) delete m_imageListState; + if (m_ownsImageListButtons) delete m_imageListButtons; + + if (m_editControl) { + m_editControl->SetOwner(NULL); // prevent control from calling us during delete + delete m_editControl; + } + + DeleteRoot(); +} + + +//----------------------------------------------------------------------------- +// accessors +//----------------------------------------------------------------------------- + +size_t wxTreeListMainWindow::GetCount() const { + return m_rootItem == NULL? 0: m_rootItem->GetChildrenCount(); +} + +void wxTreeListMainWindow::SetIndent (unsigned int indent) { + m_indent = wxMax ((unsigned)MININDENT, indent); + m_dirty = true; +} + +void wxTreeListMainWindow::SetLineSpacing (unsigned int spacing) { + m_linespacing = spacing; + m_dirty = true; + CalculateLineHeight(); +} + +size_t wxTreeListMainWindow::GetChildrenCount (const wxTreeItemId& item, + bool recursively) { + wxCHECK_MSG (item.IsOk(), 0u, _T("invalid tree item")); + return ((wxTreeListItem*)item.m_pItem)->GetChildrenCount (recursively); +} + +void wxTreeListMainWindow::SetWindowStyle (const long styles) { + // change to selection mode, reset selection + if ((styles ^ m_windowStyle) & wxTR_MULTIPLE) { UnselectAll(); } + // right now, just sets the styles. Eventually, we may + // want to update the inherited styles, but right now + // none of the parents has updatable styles + m_windowStyle = styles; + m_dirty = true; +} + +void wxTreeListMainWindow::SetToolTip(const wxString& tip) { + m_isItemToolTip = false; + m_toolTip = tip; + m_toolTipItem = (wxTreeListItem *)-1; // no tooltip displayed (force refresh) +} +void wxTreeListMainWindow::SetToolTip(wxToolTip *tip) { + m_isItemToolTip = false; + m_toolTip = (tip == NULL) ? wxString() : tip->GetTip(); + m_toolTipItem = (wxTreeListItem *)-1; // no tooltip displayed (force refresh) +} + +void wxTreeListMainWindow::SetItemToolTip(const wxTreeItemId& item, const wxString &tip) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + m_isItemToolTip = true; + ((wxTreeListItem*) item.m_pItem)->SetToolTip(tip); + m_toolTipItem = (wxTreeListItem *)-1; // no tooltip displayed (force refresh) +} + + +//----------------------------------------------------------------------------- +// functions to work with tree items +//----------------------------------------------------------------------------- + +int wxTreeListMainWindow::GetItemImage (const wxTreeItemId& item, int column, + wxTreeItemIcon which) const { + wxCHECK_MSG (item.IsOk(), -1, _T("invalid tree item")); + return ((wxTreeListItem*) item.m_pItem)->GetImage (column, which); +} + +wxTreeItemData *wxTreeListMainWindow::GetItemData (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), NULL, _T("invalid tree item")); + return ((wxTreeListItem*) item.m_pItem)->GetData(); +} + +bool wxTreeListMainWindow::GetItemBold (const wxTreeItemId& item) const { + wxCHECK_MSG(item.IsOk(), false, _T("invalid tree item")); + return ((wxTreeListItem *)item.m_pItem)->IsBold(); +} + +wxColour wxTreeListMainWindow::GetItemTextColour (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), wxNullColour, _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + return pItem->Attr().GetTextColour(); +} + +wxColour wxTreeListMainWindow::GetItemBackgroundColour (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), wxNullColour, _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + return pItem->Attr().GetBackgroundColour(); +} + +wxFont wxTreeListMainWindow::GetItemFont (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), wxNullFont, _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + return pItem->Attr().GetFont(); +} + +void wxTreeListMainWindow::SetItemImage (const wxTreeItemId& item, int column, + int image, wxTreeItemIcon which) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + pItem->SetImage (column, image, which); + wxClientDC dc (this); + CalculateSize (pItem, dc); + RefreshLine (pItem); +} + +void wxTreeListMainWindow::SetItemData (const wxTreeItemId& item, + wxTreeItemData *data) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + ((wxTreeListItem*) item.m_pItem)->SetData(data); +} + +void wxTreeListMainWindow::SetItemHasChildren (const wxTreeItemId& item, + bool has) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + pItem->SetHasPlus (has); + RefreshLine (pItem); +} + +void wxTreeListMainWindow::SetItemBold (const wxTreeItemId& item, bool bold) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + if (pItem->IsBold() != bold) { // avoid redrawing if no real change + pItem->SetBold (bold); + RefreshLine (pItem); + } +} + +void wxTreeListMainWindow::SetItemTextColour (const wxTreeItemId& item, + const wxColour& colour) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + pItem->Attr().SetTextColour (colour); + RefreshLine (pItem); +} + +void wxTreeListMainWindow::SetItemBackgroundColour (const wxTreeItemId& item, + const wxColour& colour) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + pItem->Attr().SetBackgroundColour (colour); + RefreshLine (pItem); +} + +void wxTreeListMainWindow::SetItemFont (const wxTreeItemId& item, + const wxFont& font) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + pItem->Attr().SetFont (font); + RefreshLine (pItem); +} + +bool wxTreeListMainWindow::SetFont (const wxFont &font) { + wxScrolledWindow::SetFont (font); + m_normalFont = font; + m_boldFont = wxFont (m_normalFont.GetPointSize(), + m_normalFont.GetFamily(), + m_normalFont.GetStyle(), + wxBOLD, + m_normalFont.GetUnderlined(), + m_normalFont.GetFaceName()); + CalculateLineHeight(); + return true; +} + + +// ---------------------------------------------------------------------------- +// item status inquiries +// ---------------------------------------------------------------------------- + +bool wxTreeListMainWindow::IsVisible (const wxTreeItemId& item, bool fullRow, bool within) const { + wxCHECK_MSG (item.IsOk(), false, _T("invalid tree item")); + + // An item is only visible if it's not a descendant of a collapsed item + wxTreeListItem *pItem = (wxTreeListItem*) item.m_pItem; + wxTreeListItem* parent = pItem->GetItemParent(); + while (parent) { + if (parent == m_rootItem && HasFlag(wxTR_HIDE_ROOT)) break; + if (!parent->IsExpanded()) return false; + parent = parent->GetItemParent(); + } + + // and the item is only visible if it is currently (fully) within the view + if (within) { + wxSize clientSize = GetClientSize(); + wxRect rect; + if ((!GetBoundingRect (item, rect)) || + ((!fullRow && rect.GetWidth() == 0) || rect.GetHeight() == 0) || + (rect.GetTop() < 0 || rect.GetBottom() >= clientSize.y) || + (!fullRow && (rect.GetLeft() < 0 || rect.GetRight() >= clientSize.x))) return false; + } + + return true; +} + +bool wxTreeListMainWindow::HasChildren (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), false, _T("invalid tree item")); + + // consider that the item does have children if it has the "+" button: it + // might not have them (if it had never been expanded yet) but then it + // could have them as well and it's better to err on this side rather than + // disabling some operations which are restricted to the items with + // children for an item which does have them + return ((wxTreeListItem*) item.m_pItem)->HasPlus(); +} + +bool wxTreeListMainWindow::IsExpanded (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), false, _T("invalid tree item")); + return ((wxTreeListItem*) item.m_pItem)->IsExpanded(); +} + +bool wxTreeListMainWindow::IsSelected (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), false, _T("invalid tree item")); + return ((wxTreeListItem*) item.m_pItem)->IsSelected(); +} + +bool wxTreeListMainWindow::IsBold (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), false, _T("invalid tree item")); + return ((wxTreeListItem*) item.m_pItem)->IsBold(); +} + +// ---------------------------------------------------------------------------- +// navigation +// ---------------------------------------------------------------------------- + +wxTreeItemId wxTreeListMainWindow::GetItemParent (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + return ((wxTreeListItem*) item.m_pItem)->GetItemParent(); +} + +#if !wxCHECK_VERSION(2, 5, 0) +wxTreeItemId wxTreeListMainWindow::GetFirstChild (const wxTreeItemId& item, + long& cookie) const { +#else +wxTreeItemId wxTreeListMainWindow::GetFirstChild (const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const { +#endif + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + wxArrayTreeListItems& children = ((wxTreeListItem*) item.m_pItem)->GetChildren(); + cookie = 0; + return (!children.IsEmpty())? wxTreeItemId(children.Item(0)): wxTreeItemId(); +} + +#if !wxCHECK_VERSION(2, 5, 0) +wxTreeItemId wxTreeListMainWindow::GetNextChild (const wxTreeItemId& item, + long& cookie) const { +#else +wxTreeItemId wxTreeListMainWindow::GetNextChild (const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const { +#endif + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + wxArrayTreeListItems& children = ((wxTreeListItem*) item.m_pItem)->GetChildren(); + // it's ok to cast cookie to long, we never have indices which overflow "void*" + long *pIndex = ((long*)&cookie); + return ((*pIndex)+1 < (long)children.Count())? wxTreeItemId(children.Item(++(*pIndex))): wxTreeItemId(); +} + +#if !wxCHECK_VERSION(2, 5, 0) +wxTreeItemId wxTreeListMainWindow::GetPrevChild (const wxTreeItemId& item, + long& cookie) const { +#else +wxTreeItemId wxTreeListMainWindow::GetPrevChild (const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const { +#endif + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + wxArrayTreeListItems& children = ((wxTreeListItem*) item.m_pItem)->GetChildren(); + // it's ok to cast cookie to long, we never have indices which overflow "void*" + long *pIndex = (long*)&cookie; + return ((*pIndex)-1 >= 0)? wxTreeItemId(children.Item(--(*pIndex))): wxTreeItemId(); +} + +#if !wxCHECK_VERSION(2, 5, 0) +wxTreeItemId wxTreeListMainWindow::GetLastChild (const wxTreeItemId& item, + long& cookie) const { +#else +wxTreeItemId wxTreeListMainWindow::GetLastChild (const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const { +#endif + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + wxArrayTreeListItems& children = ((wxTreeListItem*) item.m_pItem)->GetChildren(); + // it's ok to cast cookie to long, we never have indices which overflow "void*" + long *pIndex = ((long*)&cookie); + (*pIndex) = (long)children.Count(); + return (!children.IsEmpty())? wxTreeItemId(children.Last()): wxTreeItemId(); +} + +wxTreeItemId wxTreeListMainWindow::GetNextSibling (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + + // get parent + wxTreeListItem *i = (wxTreeListItem*) item.m_pItem; + wxTreeListItem *parent = i->GetItemParent(); + if (!parent) return wxTreeItemId(); // root item doesn't have any siblings + + // get index + wxArrayTreeListItems& siblings = parent->GetChildren(); + size_t index = siblings.Index (i); + wxASSERT (index != (size_t)wxNOT_FOUND); // I'm not a child of my parent? + return (index < siblings.Count()-1)? wxTreeItemId(siblings[index+1]): wxTreeItemId(); +} + +wxTreeItemId wxTreeListMainWindow::GetPrevSibling (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + + // get parent + wxTreeListItem *i = (wxTreeListItem*) item.m_pItem; + wxTreeListItem *parent = i->GetItemParent(); + if (!parent) return wxTreeItemId(); // root item doesn't have any siblings + + // get index + wxArrayTreeListItems& siblings = parent->GetChildren(); + size_t index = siblings.Index(i); + wxASSERT (index != (size_t)wxNOT_FOUND); // I'm not a child of my parent? + return (index >= 1)? wxTreeItemId(siblings[index-1]): wxTreeItemId(); +} + +// Only for internal use right now, but should probably be public +wxTreeItemId wxTreeListMainWindow::GetNext (const wxTreeItemId& item, bool fulltree) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + + // if there are any children, return first child + if (fulltree || ((wxTreeListItem*)item.m_pItem)->IsExpanded()) { + wxArrayTreeListItems& children = ((wxTreeListItem*)item.m_pItem)->GetChildren(); + if (children.GetCount() > 0) return children.Item (0); + } + + // get sibling of this item or of the ancestors instead + wxTreeItemId next; + wxTreeItemId parent = item; + do { + next = GetNextSibling (parent); + parent = GetItemParent (parent); + } while (!next.IsOk() && parent.IsOk()); + return next; +} + +// Only for internal use right now, but should probably be public +wxTreeItemId wxTreeListMainWindow::GetPrev (const wxTreeItemId& item, bool fulltree) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + + // if there are no previous sibling get parent + wxTreeItemId prev = GetPrevSibling (item); + if (! prev.IsOk()) return GetItemParent (item); + + // while previous sibling has children, return last + while (fulltree || ((wxTreeListItem*)prev.m_pItem)->IsExpanded()) { + wxArrayTreeListItems& children = ((wxTreeListItem*)prev.m_pItem)->GetChildren(); + if (children.GetCount() == 0) break; + prev = children.Item (children.GetCount() - 1); + } + + return prev; +} + +wxTreeItemId wxTreeListMainWindow::GetFirstExpandedItem() const { + return GetNextExpanded (GetRootItem()); +} + +wxTreeItemId wxTreeListMainWindow::GetNextExpanded (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + return GetNext (item, false); +} + +wxTreeItemId wxTreeListMainWindow::GetPrevExpanded (const wxTreeItemId& item) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + return GetPrev (item, false); +} + +wxTreeItemId wxTreeListMainWindow::GetFirstVisible(bool fullRow, bool within) const { + if (HasFlag(wxTR_HIDE_ROOT) || ! IsVisible(GetRootItem(), fullRow, within)) { + return GetNextVisible (GetRootItem(), fullRow, within); + } else { + return GetRootItem(); + } +} + +wxTreeItemId wxTreeListMainWindow::GetNextVisible (const wxTreeItemId& item, bool fullRow, bool within) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + wxTreeItemId id = GetNext (item, false); + while (id.IsOk()) { + if (IsVisible (id, fullRow, within)) return id; + id = GetNext (id, false); + } + return wxTreeItemId(); +} + +wxTreeItemId wxTreeListMainWindow::GetLastVisible ( bool fullRow, bool within) const { + wxCHECK_MSG (GetRootItem().IsOk(), wxTreeItemId(), _T("invalid tree item")); + wxTreeItemId id = GetRootItem(); + wxTreeItemId res = id; + while ((id = GetNext (id, false)).IsOk()) { + if (IsVisible (id, fullRow, within)) res = id; + } + return res; +} + +wxTreeItemId wxTreeListMainWindow::GetPrevVisible (const wxTreeItemId& item, bool fullRow, bool within) const { + wxCHECK_MSG (item.IsOk(), wxTreeItemId(), _T("invalid tree item")); + wxTreeItemId id = GetPrev (item, true); + while (id.IsOk()) { + if (IsVisible (id, fullRow, within)) return id; + id = GetPrev(id, true); + } + return wxTreeItemId(); +} + +// ---------------------------------------------------------------------------- +// operations +// ---------------------------------------------------------------------------- + +// ---------------------------- ADD OPERATION ------------------------------- + +wxTreeItemId wxTreeListMainWindow::DoInsertItem (const wxTreeItemId& parentId, + size_t previous, + const wxString& text, + int image, int selImage, + wxTreeItemData *data) { + wxTreeListItem *parent = (wxTreeListItem*)parentId.m_pItem; + wxCHECK_MSG (parent, wxTreeItemId(), _T("item must have a parent, at least root!") ); + m_dirty = true; // do this first so stuff below doesn't cause flicker + + wxArrayString arr; + arr.Alloc (GetColumnCount()); + for (int i = 0; i < (int)GetColumnCount(); ++i) arr.Add (wxEmptyString); + arr[m_main_column] = text; + wxTreeListItem *item = new wxTreeListItem (this, parent, arr, image, selImage, data); + if (data != NULL) { +#if !wxCHECK_VERSION(2, 5, 0) + data->SetId ((long)item); +#else + data->SetId (item); +#endif + } + parent->Insert (item, previous); + + return item; +} + +wxTreeItemId wxTreeListMainWindow::AddRoot (const wxString& text, + int image, int selImage, + wxTreeItemData *data) { + wxCHECK_MSG(!m_rootItem, wxTreeItemId(), _T("tree can have only one root")); + wxCHECK_MSG(GetColumnCount(), wxTreeItemId(), _T("Add column(s) before adding the root item")); + m_dirty = true; // do this first so stuff below doesn't cause flicker + + wxArrayString arr; + arr.Alloc (GetColumnCount()); + for (int i = 0; i < (int)GetColumnCount(); ++i) arr.Add (wxEmptyString); + arr[m_main_column] = text; + m_rootItem = new wxTreeListItem (this, (wxTreeListItem *)NULL, arr, image, selImage, data); + if (data != NULL) { +#if !wxCHECK_VERSION(2, 5, 0) + data->SetId((long)m_rootItem); +#else + data->SetId(m_rootItem); +#endif + } + if (HasFlag(wxTR_HIDE_ROOT)) { + // if we will hide the root, make sure children are visible + m_rootItem->SetHasPlus(); + m_rootItem->Expand(); +#if !wxCHECK_VERSION(2, 5, 0) + long cookie = 0; +#else + wxTreeItemIdValue cookie = 0; +#endif + // TODO: suspect that deleting and recreating a root can leave a number of members dangling + // (here m_curItem should actually be set via SetCurrentItem() ) + m_curItem = (wxTreeListItem*)GetFirstChild (m_rootItem, cookie).m_pItem; + } + return m_rootItem; +} + +wxTreeItemId wxTreeListMainWindow::PrependItem (const wxTreeItemId& parent, + const wxString& text, + int image, int selImage, + wxTreeItemData *data) { + return DoInsertItem (parent, 0u, text, image, selImage, data); +} + +wxTreeItemId wxTreeListMainWindow::InsertItem (const wxTreeItemId& parentId, + const wxTreeItemId& idPrevious, + const wxString& text, + int image, int selImage, + wxTreeItemData *data) { + wxTreeListItem *parent = (wxTreeListItem*)parentId.m_pItem; + wxCHECK_MSG (parent, wxTreeItemId(), _T("item must have a parent, at least root!") ); + + int index = parent->GetChildren().Index((wxTreeListItem*) idPrevious.m_pItem); + wxASSERT_MSG( index != wxNOT_FOUND, + _T("previous item in wxTreeListMainWindow::InsertItem() is not a sibling") ); + + return DoInsertItem (parentId, ++index, text, image, selImage, data); +} + +wxTreeItemId wxTreeListMainWindow::InsertItem (const wxTreeItemId& parentId, + size_t before, + const wxString& text, + int image, int selImage, + wxTreeItemData *data) { + wxTreeListItem *parent = (wxTreeListItem*)parentId.m_pItem; + wxCHECK_MSG (parent, wxTreeItemId(), _T("item must have a parent, at least root!") ); + + return DoInsertItem (parentId, before, text, image, selImage, data); +} + +wxTreeItemId wxTreeListMainWindow::AppendItem (const wxTreeItemId& parentId, + const wxString& text, + int image, int selImage, + wxTreeItemData *data) { + wxTreeListItem *parent = (wxTreeListItem*) parentId.m_pItem; + wxCHECK_MSG (parent, wxTreeItemId(), _T("item must have a parent, at least root!") ); + + return DoInsertItem (parent, parent->GetChildren().Count(), text, image, selImage, data); +} + + +// -------------------------- DELETE OPERATION ------------------------------ + +void wxTreeListMainWindow::Delete (const wxTreeItemId& itemId) { + if (! itemId.IsOk()) return; + wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem; + wxTreeListItem *parent = item->GetItemParent(); + wxCHECK_RET (item != m_rootItem, _T("invalid item, root may not be deleted this way!")); + + // recursive delete + DoDeleteItem(item); + + // update parent --CAUTION: must come after delete itself, so that item's + // siblings may be found + if (parent) { + parent->GetChildren().Remove (item); // remove by value + } +} + + +void wxTreeListMainWindow::DeleteRoot() { + if (! m_rootItem) return; + + SetCurrentItem((wxTreeListItem*)NULL); + m_selectItem = (wxTreeListItem*)NULL; + m_shiftItem = (wxTreeListItem*)NULL; + + DeleteChildren (m_rootItem); + SendEvent(wxEVT_COMMAND_TREE_DELETE_ITEM, m_rootItem); + delete m_rootItem; m_rootItem = NULL; +} + + +void wxTreeListMainWindow::DeleteChildren (const wxTreeItemId& itemId) { + if (! itemId.IsOk()) return; + wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem; + + // recursive delete on all children, starting from the right to prevent + // multiple selection changes (see m_curItem handling in DoDeleteItem() ) + wxArrayTreeListItems& children = item->GetChildren(); + for (size_t n = children.GetCount(); n>0; n--) { + DoDeleteItem(children[n-1]); + // immediately remove child from array, otherwise it might get selected + // as current item (see m_curItem handling in DoDeleteItem() ) + children.RemoveAt(n-1); + } +} + + +void wxTreeListMainWindow::DoDeleteItem(wxTreeListItem *item) { + wxCHECK_RET (item, _T("invalid item for delete!")); + + m_dirty = true; // do this first so stuff below doesn't cause flicker + + // cancel any editing + if (m_editControl) { + m_editControl->EndEdit(true); // cancelled + } + + // cancel any dragging + if (item == m_dragItem) { + // stop dragging + m_isDragStarted = m_isDragging = false; + if (HasCapture()) ReleaseMouse(); + } + + // don't stay with invalid m_curItem: take next sibling or reset to NULL + // NOTE: this might be slighty inefficient when deleting a whole tree + // but has the advantage that all deletion side-effects are handled here + if (item == m_curItem) { + SetCurrentItem(item->GetItemParent()); + if (m_curItem) { + wxArrayTreeListItems& siblings = m_curItem->GetChildren(); + size_t index = siblings.Index (item); + wxASSERT (index != (size_t)wxNOT_FOUND); // I'm not a child of my parent? + SetCurrentItem(index < siblings.Count()-1 ? siblings[index+1]: (wxTreeListItem*)NULL); + } + } + // don't stay with invalid m_shiftItem: reset it to NULL + if (item == m_shiftItem) m_shiftItem = (wxTreeListItem*)NULL; + // don't stay with invalid m_selectItem: default to current item + if (item == m_selectItem) { + m_selectItem = m_curItem; + SelectItem(m_selectItem, (wxTreeItemId*)NULL, true); // unselect others + } + + // recurse children, starting from the right to prevent multiple selection + // changes (see m_curItem handling above) + wxArrayTreeListItems& children = item->GetChildren(); + for (size_t n = children.GetCount(); n>0; n--) { + DoDeleteItem(children[n-1]); + // immediately remove child from array, otherwise it might get selected + // as current item (see m_curItem handling above) + children.RemoveAt(n-1); + } + + // delete item itself + SendEvent(wxEVT_COMMAND_TREE_DELETE_ITEM, item); + delete item; +} + + +// ---------------------------------------------------------------------------- + +void wxTreeListMainWindow::SetCurrentItem(wxTreeListItem *item) { +wxTreeListItem *old_item; + + old_item = m_curItem; m_curItem = item; + + // change of item, redraw previous + if (old_item != NULL && old_item != item) { + RefreshLine(old_item); + } + +} + +// ---------------------------------------------------------------------------- + +void wxTreeListMainWindow::Expand (const wxTreeItemId& itemId) { + wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem; + wxCHECK_RET (item, _T("invalid item in wxTreeListMainWindow::Expand") ); + + if (!item->HasPlus() || item->IsExpanded()) return; + + // send event to user code + wxTreeEvent event(wxEVT_COMMAND_TREE_ITEM_EXPANDING, 0); + event.SetInt(m_curColumn); + if (SendEvent(0, item, &event) && !event.IsAllowed()) return; // expand canceled + + item->Expand(); + m_dirty = true; + + // send event to user code + event.SetEventType (wxEVT_COMMAND_TREE_ITEM_EXPANDED); + SendEvent(0, NULL, &event); +} + +void wxTreeListMainWindow::ExpandAll (const wxTreeItemId& itemId) { + wxCHECK_RET (itemId.IsOk(), _T("invalid tree item")); + + Expand (itemId); + if (!IsExpanded (itemId)) return; +#if !wxCHECK_VERSION(2, 5, 0) + long cookie; +#else + wxTreeItemIdValue cookie; +#endif + wxTreeItemId child = GetFirstChild (itemId, cookie); + while (child.IsOk()) { + ExpandAll (child); + child = GetNextChild (itemId, cookie); + } +} + +void wxTreeListMainWindow::Collapse (const wxTreeItemId& itemId) { + wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem; + wxCHECK_RET (item, _T("invalid item in wxTreeListMainWindow::Collapse") ); + + if (!item->HasPlus() || !item->IsExpanded()) return; + + // send event to user code + wxTreeEvent event (wxEVT_COMMAND_TREE_ITEM_COLLAPSING, 0 ); + event.SetInt(m_curColumn); + if (SendEvent(0, item, &event) && !event.IsAllowed()) return; // collapse canceled + + item->Collapse(); + m_dirty = true; + + // send event to user code + event.SetEventType (wxEVT_COMMAND_TREE_ITEM_COLLAPSED); + SendEvent(0, NULL, &event); +} + +void wxTreeListMainWindow::CollapseAndReset (const wxTreeItemId& item) { + wxCHECK_RET (item.IsOk(), _T("invalid tree item")); + + Collapse (item); + DeleteChildren (item); +} + +void wxTreeListMainWindow::Toggle (const wxTreeItemId& itemId) { + wxCHECK_RET (itemId.IsOk(), _T("invalid tree item")); + + if (IsExpanded (itemId)) { + Collapse (itemId); + }else{ + Expand (itemId); + } +} + +void wxTreeListMainWindow::Unselect() { + if (m_selectItem) { + m_selectItem->SetHilight (false); + RefreshLine (m_selectItem); + m_selectItem = (wxTreeListItem*)NULL; + } +} + +void wxTreeListMainWindow::UnselectAllChildren (wxTreeListItem *item) { + wxCHECK_RET (item, _T("invalid tree item")); + + if (item->IsSelected()) { + item->SetHilight (false); + RefreshLine (item); + if (item == m_selectItem) m_selectItem = (wxTreeListItem*)NULL; + if (item != m_curItem) m_lastOnSame = false; // selection change, so reset edit marker + } + if (item->HasChildren()) { + wxArrayTreeListItems& children = item->GetChildren(); + size_t count = children.Count(); + for (size_t n = 0; n < count; ++n) { + UnselectAllChildren (children[n]); + } + } +} + +void wxTreeListMainWindow::UnselectAll() { + UnselectAllChildren ((wxTreeListItem*)GetRootItem().m_pItem); +} + +// Recursive function ! +// To stop we must have crt_itemGetItemParent(); + + if (!parent) {// This is root item + return TagAllChildrenUntilLast (crt_item, last_item); + } + + wxArrayTreeListItems& children = parent->GetChildren(); + int index = children.Index(crt_item); + wxASSERT (index != wxNOT_FOUND); // I'm not a child of my parent? + + if ((parent->HasChildren() && parent->IsExpanded()) || + ((parent == (wxTreeListItem*)GetRootItem().m_pItem) && HasFlag(wxTR_HIDE_ROOT))) { + size_t count = children.Count(); + for (size_t n = (index+1); n < count; ++n) { + if (TagAllChildrenUntilLast (children[n], last_item)) return true; + } + } + + return TagNextChildren (parent, last_item); +} + +bool wxTreeListMainWindow::TagAllChildrenUntilLast (wxTreeListItem *crt_item, + wxTreeListItem *last_item) { + crt_item->SetHilight (true); + RefreshLine(crt_item); + + if (crt_item==last_item) return true; + + if (crt_item->HasChildren() && crt_item->IsExpanded()) { + wxArrayTreeListItems& children = crt_item->GetChildren(); + size_t count = children.Count(); + for (size_t n = 0; n < count; ++n) { + if (TagAllChildrenUntilLast (children[n], last_item)) return true; + } + } + + return false; +} + +bool wxTreeListMainWindow::SelectItem (const wxTreeItemId& itemId, + const wxTreeItemId& lastId, + bool unselect_others) { + + wxTreeListItem *item = itemId.IsOk() ? (wxTreeListItem*) itemId.m_pItem : NULL; + + // send selecting event to the user code + wxTreeEvent event( wxEVT_COMMAND_TREE_SEL_CHANGING, 0); + event.SetInt(m_curColumn); +#if !wxCHECK_VERSION(2, 5, 0) + event.SetOldItem ((long)m_curItem); +#else + event.SetOldItem (m_curItem); +#endif + if (SendEvent(0, item, &event) && !event.IsAllowed()) return false; // veto on selection change + + // unselect all if unselect other items + bool bUnselectedAll = false; // see that UnselectAll is done only once + if (unselect_others) { + if (HasFlag(wxTR_MULTIPLE)) { + UnselectAll(); bUnselectedAll = true; + }else{ + Unselect(); // to speed up thing + } + } + + // select item range + if (lastId.IsOk() && itemId.IsOk() && (itemId != lastId)) { + + if (! bUnselectedAll) UnselectAll(); + wxTreeListItem *last = (wxTreeListItem*) lastId.m_pItem; + + // ensure that the position of the item it calculated in any case + if (m_dirty) CalculatePositions(); + + // select item range according Y-position + if (last->GetY() < item->GetY()) { + if (!TagAllChildrenUntilLast (last, item)) { + TagNextChildren (last, item); + } + }else{ + if (!TagAllChildrenUntilLast (item, last)) { + TagNextChildren (item, last); + } + } + + // or select single item + }else if (itemId.IsOk()) { + + // select item according its old selection + item->SetHilight (!item->IsSelected()); + RefreshLine (item); + if (unselect_others) { + m_selectItem = (item->IsSelected())? item: (wxTreeListItem*)NULL; + } + + // or select nothing + } else { + if (! bUnselectedAll) UnselectAll(); + } + + // send event to user code + event.SetEventType(wxEVT_COMMAND_TREE_SEL_CHANGED); + SendEvent(0, NULL, &event); + + return true; +} + +void wxTreeListMainWindow::SelectAll() { + wxTreeItemId root = GetRootItem(); + wxCHECK_RET (HasFlag(wxTR_MULTIPLE), _T("invalid tree style")); + wxCHECK_RET (root.IsOk(), _T("no tree")); + + // send event to user code + wxTreeEvent event (wxEVT_COMMAND_TREE_SEL_CHANGING, 0); +#if !wxCHECK_VERSION(2, 5, 0) + event.SetOldItem ((long)m_curItem); +#else + event.SetOldItem (m_curItem); +#endif + event.SetInt (-1); // no colum clicked + if (SendEvent(0, m_rootItem, &event) && !event.IsAllowed()) return; // selection change vetoed + +#if !wxCHECK_VERSION(2, 5, 0) + long cookie = 0; +#else + wxTreeItemIdValue cookie = 0; +#endif + wxTreeListItem *first = (wxTreeListItem *)GetFirstChild (root, cookie).m_pItem; + wxTreeListItem *last = (wxTreeListItem *)GetLastChild (root, cookie).m_pItem; + if (!TagAllChildrenUntilLast (first, last)) { + TagNextChildren (first, last); + } + + // send event to user code + event.SetEventType (wxEVT_COMMAND_TREE_SEL_CHANGED); + SendEvent(0, NULL, &event); +} + +void wxTreeListMainWindow::FillArray (wxTreeListItem *item, + wxArrayTreeItemIds &array) const { + if (item->IsSelected()) array.Add (wxTreeItemId(item)); + + if (item->HasChildren()) { + wxArrayTreeListItems& children = item->GetChildren(); + size_t count = children.GetCount(); + for (size_t n = 0; n < count; ++n) FillArray (children[n], array); + } +} + +size_t wxTreeListMainWindow::GetSelections (wxArrayTreeItemIds &array) const { + array.Empty(); + wxTreeItemId idRoot = GetRootItem(); + if (idRoot.IsOk()) FillArray ((wxTreeListItem*) idRoot.m_pItem, array); + return array.Count(); +} + +void wxTreeListMainWindow::EnsureVisible (const wxTreeItemId& item) { + if (!item.IsOk()) return; // do nothing if no item + + // first expand all parent branches + wxTreeListItem *gitem = (wxTreeListItem*) item.m_pItem; + wxTreeListItem *parent = gitem->GetItemParent(); + while (parent) { + Expand (parent); + parent = parent->GetItemParent(); + } + + ScrollTo (item); + RefreshLine (gitem); +} + +void wxTreeListMainWindow::ScrollTo (const wxTreeItemId &item) { + if (!item.IsOk()) return; // do nothing if no item + + // ensure that the position of the item it calculated in any case + if (m_dirty) CalculatePositions(); + + wxTreeListItem *gitem = (wxTreeListItem*) item.m_pItem; + + // now scroll to the item + int item_y = gitem->GetY(); + + int xUnit, yUnit; + GetScrollPixelsPerUnit (&xUnit, &yUnit); + int start_x = 0; + int start_y = 0; + GetViewStart (&start_x, &start_y); + start_y *= yUnit; + + int client_h = 0; + int client_w = 0; + GetClientSize (&client_w, &client_h); + + int x = 0; + int y = 0; + m_rootItem->GetSize (x, y, this); + x = m_owner->GetHeaderWindow()->GetWidth(); + y += yUnit + 2; // one more scrollbar unit + 2 pixels + int x_pos = GetScrollPos( wxHORIZONTAL ); + + if (item_y < start_y+3) { + // going down, item should appear at top + SetScrollbars (xUnit, yUnit, xUnit ? x/xUnit : 0, yUnit ? y/yUnit : 0, x_pos, yUnit ? item_y/yUnit : 0); + }else if (item_y+GetLineHeight(gitem) > start_y+client_h) { + // going up, item should appear at bottom + item_y += yUnit + 2; + SetScrollbars (xUnit, yUnit, xUnit ? x/xUnit : 0, yUnit ? y/yUnit : 0, x_pos, yUnit ? (item_y+GetLineHeight(gitem)-client_h)/yUnit : 0 ); + } +} + +// FIXME: tree sorting functions are not reentrant and not MT-safe! +static wxTreeListMainWindow *s_treeBeingSorted = NULL; + +static int LINKAGEMODE tree_ctrl_compare_func(wxTreeListItem **item1, + wxTreeListItem **item2) +{ + wxCHECK_MSG (s_treeBeingSorted, 0, _T("bug in wxTreeListMainWindow::SortChildren()") ); + + return s_treeBeingSorted->OnCompareItems(*item1, *item2); +} + +int wxTreeListMainWindow::OnCompareItems(const wxTreeItemId& item1, + const wxTreeItemId& item2) +{ + return m_owner->OnCompareItems (item1, item2); +} + +void wxTreeListMainWindow::SortChildren (const wxTreeItemId& itemId) { + wxCHECK_RET (itemId.IsOk(), _T("invalid tree item")); + + wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem; + + wxCHECK_RET (!s_treeBeingSorted, + _T("wxTreeListMainWindow::SortChildren is not reentrant") ); + + wxArrayTreeListItems& children = item->GetChildren(); + if ( children.Count() > 1 ) { + m_dirty = true; + s_treeBeingSorted = this; + children.Sort(tree_ctrl_compare_func); + s_treeBeingSorted = NULL; + } +} + +wxTreeItemId wxTreeListMainWindow::FindItem (const wxTreeItemId& item, const wxString& str, int mode) { + wxString itemText; + // determine start item + wxTreeItemId next = item; + if (next.IsOk()) { + if (mode & wxTL_MODE_NAV_LEVEL) { + next = GetNextSibling (next); + }else if (mode & wxTL_MODE_NAV_VISIBLE) { // + next = GetNextVisible (next, false, true); + }else if (mode & wxTL_MODE_NAV_EXPANDED) { + next = GetNextExpanded (next); + }else{ // (mode & wxTL_MODE_NAV_FULLTREE) default + next = GetNext (next, true); + } + } + +#if !wxCHECK_VERSION(2, 5, 0) + long cookie = 0; +#else + wxTreeItemIdValue cookie = 0; +#endif + if (!next.IsOk()) { + next = GetRootItem(); + if (next.IsOk() && HasFlag(wxTR_HIDE_ROOT)) { + next = GetFirstChild (GetRootItem(), cookie); + } + } + if (!next.IsOk()) return (wxTreeItemId*)NULL; + + // start checking the next items + while (next.IsOk() && (next != item)) { + if (mode & wxTL_MODE_FIND_PARTIAL) { + itemText = GetItemText (next).Mid (0, str.Length()); + }else{ + itemText = GetItemText (next); + } + if (mode & wxTL_MODE_FIND_NOCASE) { + if (itemText.CmpNoCase (str) == 0) return next; + }else{ + if (itemText.Cmp (str) == 0) return next; + } + if (mode & wxTL_MODE_NAV_LEVEL) { + next = GetNextSibling (next); + }else if (mode & wxTL_MODE_NAV_VISIBLE) { // + next = GetNextVisible (next, false, true); + }else if (mode & wxTL_MODE_NAV_EXPANDED) { + next = GetNextExpanded (next); + }else{ // (mode & wxTL_MODE_NAV_FULLTREE) default + next = GetNext (next, true); + } + if (!next.IsOk() && item.IsOk()) { + next = (wxTreeListItem*)GetRootItem().m_pItem; + if (HasFlag(wxTR_HIDE_ROOT)) { + next = (wxTreeListItem*)GetNextChild (GetRootItem().m_pItem, cookie).m_pItem; + } + } + } + return (wxTreeItemId*)NULL; +} + +void wxTreeListMainWindow::SetDragItem (const wxTreeItemId& item) { + wxTreeListItem *prevItem = m_dragItem; + m_dragItem = (wxTreeListItem*) item.m_pItem; + if (prevItem) RefreshLine (prevItem); + if (m_dragItem) RefreshLine (m_dragItem); +} + +void wxTreeListMainWindow::CalculateLineHeight() { + wxClientDC dc (this); + dc.SetFont (m_normalFont); + m_lineHeight = (int)(dc.GetCharHeight() + m_linespacing); + + if (m_imageListNormal) { + // Calculate a m_lineHeight value from the normal Image sizes. + // May be toggle off. Then wxTreeListMainWindow will spread when + // necessary (which might look ugly). + int n = m_imageListNormal->GetImageCount(); + for (int i = 0; i < n ; i++) { + int width = 0, height = 0; + m_imageListNormal->GetSize(i, width, height); + if (height > m_lineHeight) m_lineHeight = height + m_linespacing; + } + } + + if (m_imageListButtons) { + // Calculate a m_lineHeight value from the Button image sizes. + // May be toggle off. Then wxTreeListMainWindow will spread when + // necessary (which might look ugly). + int n = m_imageListButtons->GetImageCount(); + for (int i = 0; i < n ; i++) { + int width = 0, height = 0; + m_imageListButtons->GetSize(i, width, height); + if (height > m_lineHeight) m_lineHeight = height + m_linespacing; + } + } + + if (m_lineHeight < 30) { // add 10% space if greater than 30 pixels + m_lineHeight += 2; // minimal 2 pixel space + }else{ + m_lineHeight += m_lineHeight / 10; // otherwise 10% space + } +} + +void wxTreeListMainWindow::SetImageList (wxImageList *imageList) { + if (m_ownsImageListNormal) delete m_imageListNormal; + m_imageListNormal = imageList; + m_ownsImageListNormal = false; + m_dirty = true; + CalculateLineHeight(); +} + +void wxTreeListMainWindow::SetStateImageList (wxImageList *imageList) { + if (m_ownsImageListState) delete m_imageListState; + m_imageListState = imageList; + m_ownsImageListState = false; +} + +void wxTreeListMainWindow::SetButtonsImageList (wxImageList *imageList) { + if (m_ownsImageListButtons) delete m_imageListButtons; + m_imageListButtons = imageList; + m_ownsImageListButtons = false; + m_dirty = true; + CalculateLineHeight(); +} + +void wxTreeListMainWindow::AssignImageList (wxImageList *imageList) { + SetImageList(imageList); + m_ownsImageListNormal = true; +} + +void wxTreeListMainWindow::AssignStateImageList (wxImageList *imageList) { + SetStateImageList(imageList); + m_ownsImageListState = true; +} + +void wxTreeListMainWindow::AssignButtonsImageList (wxImageList *imageList) { + SetButtonsImageList(imageList); + m_ownsImageListButtons = true; +} + +// ---------------------------------------------------------------------------- +// helpers +// ---------------------------------------------------------------------------- + +void wxTreeListMainWindow::AdjustMyScrollbars() { + if (m_rootItem) { + int xUnit, yUnit; + GetScrollPixelsPerUnit (&xUnit, &yUnit); + if (xUnit == 0) xUnit = GetCharWidth(); + if (yUnit == 0) yUnit = m_lineHeight; + int x = 0, y = 0; + m_rootItem->GetSize (x, y, this); + y += yUnit + 2; // one more scrollbar unit + 2 pixels + int x_pos = GetScrollPos (wxHORIZONTAL); + int y_pos = GetScrollPos (wxVERTICAL); + x = m_owner->GetHeaderWindow()->GetWidth() + 2; + if (x < GetClientSize().GetWidth()) x_pos = 0; + SetScrollbars (xUnit, yUnit, x/xUnit, y/yUnit, x_pos, y_pos); + }else{ + SetScrollbars (0, 0, 0, 0); + } +} + +int wxTreeListMainWindow::GetLineHeight (wxTreeListItem *item) const { + if (GetWindowStyleFlag() & wxTR_HAS_VARIABLE_ROW_HEIGHT) { + return item->GetHeight(); + }else{ + return m_lineHeight; + } +} + +void wxTreeListMainWindow::PaintItem (wxTreeListItem *item, wxDC& dc) { + + wxTreeItemAttr *attr = item->GetAttributes(); + + dc.SetFont (GetItemFont (item)); + + wxColour colText; + if (attr && attr->HasTextColour()) { + colText = attr->GetTextColour(); + }else{ + colText = GetForegroundColour(); + } +#if !wxCHECK_VERSION(2, 5, 0) + wxColour colTextHilight = wxSystemSettings::GetSystemColour (wxSYS_COLOUR_HIGHLIGHTTEXT); +#else + wxColour colTextHilight = wxSystemSettings::GetColour (wxSYS_COLOUR_HIGHLIGHTTEXT); +#endif + + int total_w = m_owner->GetHeaderWindow()->GetWidth(); + int total_h = GetLineHeight(item); + int off_h = HasFlag(wxTR_ROW_LINES) ? 1 : 0; + int off_w = HasFlag(wxTR_COLUMN_LINES) ? 1 : 0; + wxDCClipper clipper (dc, 0, item->GetY(), total_w, total_h); // only within line + + int text_w = 0, text_h = 0; + dc.GetTextExtent( item->GetText(GetMainColumn()).size() > 0 + ? item->GetText(GetMainColumn()) + : _T(" "), // dummy text to avoid zero height and no highlight width + &text_w, &text_h ); + + // determine background and show it + wxColour colBg; + if (attr && attr->HasBackgroundColour()) { + colBg = attr->GetBackgroundColour(); + }else{ + colBg = m_backgroundColour; + } + dc.SetBrush (wxBrush (colBg, wxSOLID)); + dc.SetPen (*wxTRANSPARENT_PEN); + if (HasFlag (wxTR_FULL_ROW_HIGHLIGHT)) { + if (item->IsSelected()) { + if (! m_isDragging && m_hasFocus) { + dc.SetBrush (*m_hilightBrush); +#ifndef __WXMAC__ // don't draw rect outline if we already have the background color + dc.SetPen (*wxBLACK_PEN); +#endif // !__WXMAC__ + }else{ + dc.SetBrush (*m_hilightUnfocusedBrush); +#ifndef __WXMAC__ // don't draw rect outline if we already have the background color + dc.SetPen (*wxTRANSPARENT_PEN); +#endif // !__WXMAC__ + } + dc.SetTextForeground (colTextHilight); + }else if (item == m_curItem) { + dc.SetPen (m_hasFocus? *wxBLACK_PEN: *wxTRANSPARENT_PEN); + }else{ + dc.SetTextForeground (colText); + } + dc.DrawRectangle (0, item->GetY() + off_h, total_w, total_h - off_h); + }else{ + dc.SetTextForeground (colText); + } + + int text_extraH = (total_h > text_h) ? (total_h - text_h)/2 : 0; + int img_extraH = (total_h > m_imgHeight)? (total_h-m_imgHeight)/2: 0; + int x_colstart = 0; + for (int i = 0; i < GetColumnCount(); ++i ) { + if (!m_owner->GetHeaderWindow()->IsColumnShown(i)) continue; + + int col_w = m_owner->GetHeaderWindow()->GetColumnWidth(i); + wxDCClipper clipper (dc, x_colstart, item->GetY(), col_w, total_h); // only within column + + int x = 0; + int image = NO_IMAGE; + int image_w = 0; + if(i == GetMainColumn()) { + x = item->GetX() + MARGIN; + if (HasButtons()) { + x += (m_btnWidth-m_btnWidth2) + LINEATROOT; + }else{ + x -= m_indent/2; + } + if (m_imageListNormal) image = item->GetCurrentImage(); + }else{ + x = x_colstart + MARGIN; + image = item->GetImage(i); + } + if (image != NO_IMAGE) image_w = m_imgWidth + MARGIN; + + // honor text alignment + wxString text = item->GetText(i); + int w = 0; + switch ( m_owner->GetHeaderWindow()->GetColumn(i).GetAlignment() ) { + case wxALIGN_LEFT: + // nothing to do, already left aligned + break; + case wxALIGN_RIGHT: + dc.GetTextExtent (text, &text_w, NULL); + w = col_w - (image_w + text_w + off_w + MARGIN); + x += (w > 0)? w: 0; + break; + case wxALIGN_CENTER: + dc.GetTextExtent(text, &text_w, NULL); + w = (col_w - (image_w + text_w + off_w + MARGIN))/2; + x += (w > 0)? w: 0; + break; + } + int text_x = x + image_w; + if (i == GetMainColumn()) item->SetTextX (text_x); + + if (!HasFlag (wxTR_FULL_ROW_HIGHLIGHT)) { + if (i == GetMainColumn()) { + if (item->IsSelected()) { + if (!m_isDragging && m_hasFocus) { + dc.SetBrush (*m_hilightBrush); +#ifndef __WXMAC__ // don't draw rect outline if we already have the background color + dc.SetPen (*wxBLACK_PEN); +#endif // !__WXMAC__ + }else{ + dc.SetBrush (*m_hilightUnfocusedBrush); +#ifndef __WXMAC__ // don't draw rect outline if we already have the background color + dc.SetPen (*wxTRANSPARENT_PEN); +#endif // !__WXMAC__ + } + dc.SetTextForeground (colTextHilight); + }else if (item == m_curItem) { + dc.SetPen (m_hasFocus? *wxBLACK_PEN: *wxTRANSPARENT_PEN); + }else{ + dc.SetTextForeground (colText); + } + dc.DrawRectangle (text_x, item->GetY() + off_h, text_w, total_h - off_h); + }else{ + dc.SetTextForeground (colText); + } + } + + if (HasFlag(wxTR_COLUMN_LINES)) { // vertical lines between columns +#if !wxCHECK_VERSION(2, 5, 0) + wxPen pen (wxSystemSettings::GetSystemColour (wxSYS_COLOUR_3DLIGHT ), 1, wxSOLID); +#else + wxPen pen (wxSystemSettings::GetColour (wxSYS_COLOUR_3DLIGHT ), 1, wxSOLID); +#endif + dc.SetPen ((GetBackgroundColour() == *wxWHITE)? pen: *wxWHITE_PEN); + dc.DrawLine (x_colstart+col_w-1, item->GetY(), x_colstart+col_w-1, item->GetY()+total_h); + } + + dc.SetBackgroundMode (wxTRANSPARENT); + + if (image != NO_IMAGE) { + int y = item->GetY() + img_extraH; + m_imageListNormal->Draw (image, dc, x, y, wxIMAGELIST_DRAW_TRANSPARENT ); + } + int text_y = item->GetY() + text_extraH; + dc.DrawText (text, (wxCoord)text_x, (wxCoord)text_y); + + x_colstart += col_w; + } + + // restore normal font + dc.SetFont( m_normalFont ); +} + +// Now y stands for the top of the item, whereas it used to stand for middle ! +void wxTreeListMainWindow::PaintLevel (wxTreeListItem *item, wxDC &dc, + int level, int &y, int x_maincol) { + + // Handle hide root (only level 0) + if (HasFlag(wxTR_HIDE_ROOT) && (level == 0)) { + wxArrayTreeListItems& children = item->GetChildren(); + for (size_t n = 0; n < children.Count(); n++) { + PaintLevel (children[n], dc, 1, y, x_maincol); + } + // end after expanding root + return; + } + + // calculate position of vertical lines + int x = x_maincol + MARGIN; // start of column + if (HasFlag(wxTR_LINES_AT_ROOT)) x += LINEATROOT; // space for lines at root + if (HasButtons()) { + x += (m_btnWidth-m_btnWidth2); // half button space + }else{ + x += (m_indent-m_indent/2); + } + if (HasFlag(wxTR_HIDE_ROOT)) { + x += m_indent * (level-1); // indent but not level 1 + }else{ + x += m_indent * level; // indent according to level + } + + // set position of vertical line + item->SetX (x); + item->SetY (y); + + int h = GetLineHeight (item); + int y_top = y; + int y_mid = y_top + (h/2); + y += h; + + int exposed_x = dc.LogicalToDeviceX(0); + int exposed_y = dc.LogicalToDeviceY(y_top); + + if (IsExposed(exposed_x, exposed_y, 10000, h)) { // 10000 = very much + + if (HasFlag(wxTR_ROW_LINES)) { // horizontal lines between rows + //dc.DestroyClippingRegion(); + int total_width = m_owner->GetHeaderWindow()->GetWidth(); + // if the background colour is white, choose a + // contrasting color for the lines +#if !wxCHECK_VERSION(2, 5, 0) + wxPen pen (wxSystemSettings::GetSystemColour (wxSYS_COLOUR_3DLIGHT ), 1, wxSOLID); +#else + wxPen pen (wxSystemSettings::GetColour (wxSYS_COLOUR_3DLIGHT ), 1, wxSOLID); +#endif + dc.SetPen ((GetBackgroundColour() == *wxWHITE)? pen: *wxWHITE_PEN); + dc.DrawLine (0, y_top, total_width, y_top); + dc.DrawLine (0, y_top+h, total_width, y_top+h); + } + + // draw item + PaintItem (item, dc); + + // restore DC objects + dc.SetBrush(*wxWHITE_BRUSH); + dc.SetPen(m_dottedPen); + + // clip to the column width + int clip_width = m_owner->GetHeaderWindow()-> + GetColumn(m_main_column).GetWidth(); + wxDCClipper clipper(dc, x_maincol, y_top, clip_width, 10000); + + if (!HasFlag(wxTR_NO_LINES)) { // connection lines + + // draw the horizontal line here + dc.SetPen(m_dottedPen); + int x2 = x - m_indent; + if (x2 < (x_maincol + MARGIN)) x2 = x_maincol + MARGIN; + int x3 = x + (m_btnWidth-m_btnWidth2); + if (HasButtons()) { + if (item->HasPlus()) { + dc.DrawLine (x2, y_mid, x - m_btnWidth2, y_mid); + dc.DrawLine (x3, y_mid, x3 + LINEATROOT, y_mid); + }else{ + dc.DrawLine (x2, y_mid, x3 + LINEATROOT, y_mid); + } + }else{ + dc.DrawLine (x2, y_mid, x - m_indent/2, y_mid); + } + } + + if (item->HasPlus() && HasButtons()) { // should the item show a button? + + if (m_imageListButtons) { + + // draw the image button here + int image = wxTreeItemIcon_Normal; + if (item->IsExpanded()) image = wxTreeItemIcon_Expanded; + if (item->IsSelected()) image += wxTreeItemIcon_Selected - wxTreeItemIcon_Normal; + int xx = x - m_btnWidth2 + MARGIN; + int yy = y_mid - m_btnHeight2; + dc.SetClippingRegion(xx, yy, m_btnWidth, m_btnHeight); + m_imageListButtons->Draw (image, dc, xx, yy, wxIMAGELIST_DRAW_TRANSPARENT); + dc.DestroyClippingRegion(); + + }else if (HasFlag (wxTR_TWIST_BUTTONS)) { + + // draw the twisty button here + dc.SetPen(*wxBLACK_PEN); + dc.SetBrush(*m_hilightBrush); + wxPoint button[3]; + if (item->IsExpanded()) { + button[0].x = x - (m_btnWidth2+1); + button[0].y = y_mid - (m_btnHeight/3); + button[1].x = x + (m_btnWidth2+1); + button[1].y = button[0].y; + button[2].x = x; + button[2].y = button[0].y + (m_btnHeight2+1); + }else{ + button[0].x = x - (m_btnWidth/3); + button[0].y = y_mid - (m_btnHeight2+1); + button[1].x = button[0].x; + button[1].y = y_mid + (m_btnHeight2+1); + button[2].x = button[0].x + (m_btnWidth2+1); + button[2].y = y_mid; + } + dc.DrawPolygon(3, button); + + }else{ // if (HasFlag(wxTR_HAS_BUTTONS)) + + // draw the plus sign here +#if !wxCHECK_VERSION(2, 7, 0) + dc.SetPen(*wxGREY_PEN); + dc.SetBrush(*wxWHITE_BRUSH); + dc.DrawRectangle (x-m_btnWidth2, y_mid-m_btnHeight2, m_btnWidth, m_btnHeight); + dc.SetPen(*wxBLACK_PEN); + dc.DrawLine (x-(m_btnWidth2-2), y_mid, x+(m_btnWidth2-1), y_mid); + if (!item->IsExpanded()) { // change "-" to "+" + dc.DrawLine (x, y_mid-(m_btnHeight2-2), x, y_mid+(m_btnHeight2-1)); + } +#else + wxRect rect (x-m_btnWidth2, y_mid-m_btnHeight2, m_btnWidth, m_btnHeight); + int flag = item->IsExpanded()? wxCONTROL_EXPANDED: 0; + wxRendererNative::GetDefault().DrawTreeItemButton (this, dc, rect, flag); +#endif + + } + + } + + } + + // restore DC objects + dc.SetBrush(*wxWHITE_BRUSH); + dc.SetPen(m_dottedPen); + dc.SetTextForeground(*wxBLACK); + + if (item->IsExpanded()) + { + wxArrayTreeListItems& children = item->GetChildren(); + + // clip to the column width + int clip_width = m_owner->GetHeaderWindow()-> + GetColumn(m_main_column).GetWidth(); + + // process lower levels + int oldY; + if (m_imgWidth > 0) { + oldY = y_mid + m_imgHeight2; + }else{ + oldY = y_mid + h/2; + } + int y2; + for (size_t n = 0; n < children.Count(); ++n) { + + y2 = y + h/2; + PaintLevel (children[n], dc, level+1, y, x_maincol); + + // draw vertical line + wxDCClipper clipper(dc, x_maincol, y_top, clip_width, 10000); + if (!HasFlag (wxTR_NO_LINES)) { + x = item->GetX(); + dc.DrawLine (x, oldY, x, y2); + oldY = y2; + } + } + } +} + + +// ---------------------------------------------------------------------------- +// wxWindows callbacks +// ---------------------------------------------------------------------------- + +void wxTreeListMainWindow::OnPaint (wxPaintEvent &WXUNUSED(event)) { + + // init device context, clear background (BEFORE changing DC origin...) + wxAutoBufferedPaintDC dc (this); + wxBrush brush(GetBackgroundColour(), wxSOLID); + dc.SetBackground(brush); + dc.Clear(); + DoPrepareDC (dc); + + if (!m_rootItem || (GetColumnCount() <= 0)) return; + + // calculate button size + if (m_imageListButtons) { + m_imageListButtons->GetSize (0, m_btnWidth, m_btnHeight); + }else if (HasButtons()) { + m_btnWidth = BTNWIDTH; + m_btnHeight = BTNHEIGHT; + } + m_btnWidth2 = m_btnWidth/2; + m_btnHeight2 = m_btnHeight/2; + + // calculate image size + if (m_imageListNormal) { + m_imageListNormal->GetSize (0, m_imgWidth, m_imgHeight); + } + m_imgWidth2 = m_imgWidth/2; + m_imgHeight2 = m_imgHeight/2; + + // calculate indent size + if (m_imageListButtons) { + m_indent = wxMax (MININDENT, m_btnWidth + MARGIN); + }else if (HasButtons()) { + m_indent = wxMax (MININDENT, m_btnWidth + LINEATROOT); + } + + // set default values + dc.SetFont( m_normalFont ); + dc.SetPen( m_dottedPen ); + + // calculate column start and paint + int x_maincol = 0; + int i = 0; + for (i = 0; i < (int)GetMainColumn(); ++i) { + if (!m_owner->GetHeaderWindow()->IsColumnShown(i)) continue; + x_maincol += m_owner->GetHeaderWindow()->GetColumnWidth (i); + } + int y = 0; + PaintLevel (m_rootItem, dc, 0, y, x_maincol); +} + +void wxTreeListMainWindow::OnSetFocus (wxFocusEvent &event) { + m_hasFocus = true; + RefreshSelected(); + if (m_curItem) RefreshLine (m_curItem); + event.Skip(); +} + +void wxTreeListMainWindow::OnKillFocus( wxFocusEvent &event ) +{ + m_hasFocus = false; + RefreshSelected(); + if (m_curItem) RefreshLine (m_curItem); + event.Skip(); +} + +void wxTreeListMainWindow::OnChar (wxKeyEvent &event) { + // send event to user code + wxTreeEvent nevent (wxEVT_COMMAND_TREE_KEY_DOWN, 0 ); + nevent.SetInt(m_curColumn); + nevent.SetKeyEvent (event); + if (SendEvent(0, NULL, &nevent)) return; // char event handled in user code + + // if no item current, select root + bool curItemSet = false; + if (!m_curItem) { + if (! GetRootItem().IsOk()) return; + SetCurrentItem((wxTreeListItem*)GetRootItem().m_pItem); + if (HasFlag(wxTR_HIDE_ROOT)) { +#if !wxCHECK_VERSION(2, 5, 0) + long cookie = 0; +#else + wxTreeItemIdValue cookie = 0; +#endif + SetCurrentItem((wxTreeListItem*)GetFirstChild (m_curItem, cookie).m_pItem); + } + SelectItem(m_curItem, (wxTreeItemId*)NULL, true); // unselect others + curItemSet = true; + } + + // remember item at shift down + if (HasFlag(wxTR_MULTIPLE) && event.ShiftDown()) { + if (!m_shiftItem) m_shiftItem = m_curItem; + }else{ + m_shiftItem = (wxTreeListItem*)NULL; + } + + if (curItemSet) return; // if no item was current until now, do nothing more + + // process all cases + wxTreeItemId newItem = (wxTreeItemId*)NULL; + switch (event.GetKeyCode()) { + + // '+': Expand subtree + case '+': + case WXK_ADD: { + if (m_curItem->HasPlus() && !IsExpanded (m_curItem)) Expand (m_curItem); + }break; + + // '-': collapse subtree + case '-': + case WXK_SUBTRACT: { + if (m_curItem->HasPlus() && IsExpanded (m_curItem)) Collapse (m_curItem); + }break; + + // '*': expand/collapse all subtrees // TODO: Mak it more useful + case '*': + case WXK_MULTIPLY: { + if (m_curItem->HasPlus() && !IsExpanded (m_curItem)) { + ExpandAll (m_curItem); + }else if (m_curItem->HasPlus()) { + Collapse (m_curItem); // TODO: CollapseAll + } + }break; + + // ' ': toggle current item + case ' ': { + SelectItem (m_curItem, (wxTreeListItem*)NULL, false); + }break; + + // : activate current item + case WXK_RETURN: { + if (! SendEvent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, m_curItem)) { + + // if the user code didn't process the activate event, + // handle it ourselves by toggling the item when it is + // double clicked + if (m_curItem && m_curItem->HasPlus()) Toggle(m_curItem); + } + }break; + + // : go to the parent without collapsing + case WXK_BACK: { + newItem = GetItemParent (m_curItem); + if ((newItem == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT)) { + newItem = GetPrevSibling (m_curItem); // get sibling instead of root + } + }break; + + // : go to first visible + case WXK_HOME: { + newItem = GetFirstVisible(false, false); + }break; + + // : go to the top of the page, or if we already are then one page back + case WXK_PAGEUP: { + int flags = 0; + int col = 0; + wxPoint abs_p = CalcUnscrolledPosition (wxPoint(1,1)); + // PAGE-UP: first go the the first visible row + newItem = m_rootItem->HitTest(abs_p, this, flags, col, 0); + newItem = GetFirstVisible(false, true); + // if we are already there then scroll back one page + if (newItem == m_curItem) { + abs_p.y -= GetClientSize().GetHeight() - m_curItem->GetHeight(); + if (abs_p.y < 0) abs_p.y = 0; + newItem = m_rootItem->HitTest(abs_p, this, flags, col, 0); + } + // newItem should never be NULL + } break; + + // : go to the previous sibling or for the last of its children, to the parent + case WXK_UP: { + newItem = GetPrevSibling (m_curItem); + if (newItem) { +#if !wxCHECK_VERSION(2, 5, 0) + long cookie = 0; +#else + wxTreeItemIdValue cookie = 0; +#endif + while (IsExpanded (newItem) && HasChildren (newItem)) { + newItem = GetLastChild (newItem, cookie); + } + }else { + newItem = GetItemParent (m_curItem); + if ((newItem == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT)) { + newItem = (wxTreeItemId*)NULL; // don't go to root if it is hidden + } + } + }break; + + // : if expanded collapse subtree, else go to the parent + case WXK_LEFT: { + if (IsExpanded (m_curItem)) { + Collapse (m_curItem); + }else{ + newItem = GetItemParent (m_curItem); + if ((newItem == GetRootItem()) && HasFlag(wxTR_HIDE_ROOT)) { + newItem = GetPrevSibling (m_curItem); // go to sibling if it is hidden + } + } + }break; + + // : if possible expand subtree, else go go to the first child + case WXK_RIGHT: { + if (m_curItem->HasPlus() && !IsExpanded (m_curItem)) { + Expand (m_curItem); + }else{ + if (IsExpanded (m_curItem) && HasChildren (m_curItem)) { +#if !wxCHECK_VERSION(2, 5, 0) + long cookie = 0; +#else + wxTreeItemIdValue cookie = 0; +#endif + newItem = GetFirstChild (m_curItem, cookie); + } + } + }break; + + // : if expanded go to the first child, else to the next sibling, ect + case WXK_DOWN: { + if (IsExpanded (m_curItem) && HasChildren (m_curItem)) { +#if !wxCHECK_VERSION(2, 5, 0) + long cookie = 0; +#else + wxTreeItemIdValue cookie = 0; +#endif + newItem = GetFirstChild( m_curItem, cookie ); + } + if (!newItem) { + wxTreeItemId parent = m_curItem; + do { + newItem = GetNextSibling (parent); + parent = GetItemParent (parent); + } while (!newItem && parent); + } + }break; + + // : go to the bottom of the page, or if we already are then one page further + case WXK_PAGEDOWN: { + int flags = 0; + int col = 0; + wxPoint abs_p = CalcUnscrolledPosition (wxPoint(1,GetClientSize().GetHeight() - m_curItem->GetHeight())); + // PAGE-UP: first go the the first visible row + newItem = m_rootItem->HitTest(abs_p, this, flags, col, 0); + newItem = GetLastVisible(false, true); + // if we are already there then scroll down one page + if (newItem == m_curItem) { + abs_p.y += GetClientSize().GetHeight() - m_curItem->GetHeight(); +// if (abs_p.y >= GetVirtualSize().GetHeight()) abs_p.y = GetVirtualSize().GetHeight() - 1; + newItem = m_rootItem->HitTest(abs_p, this, flags, col, 0); + } + // if we reached the empty area below the rows, return last item instead + if (! newItem) newItem = GetLastVisible(false, false); + } break; + + // : go to last item of the root + case WXK_END: { + newItem = GetLastVisible (false, false); + }break; + + // any char: go to the next matching string + default: + if (event.GetKeyCode() >= (int)' ') { + if (!m_findTimer->IsRunning()) m_findStr.Clear(); + m_findStr.Append (event.GetKeyCode()); + m_findTimer->Start (FIND_TIMER_TICKS, wxTIMER_ONE_SHOT); + wxTreeItemId prev = m_curItem? (wxTreeItemId*)m_curItem: (wxTreeItemId*)NULL; + while (true) { + newItem = FindItem (prev, m_findStr, wxTL_MODE_NAV_EXPANDED | + wxTL_MODE_FIND_PARTIAL | + wxTL_MODE_FIND_NOCASE); + if (newItem || (m_findStr.Length() <= 1)) break; + m_findStr.RemoveLast(); + }; + } + event.Skip(); + + } + + // select and show the new item + if (newItem) { + if (!event.ControlDown()) { + bool unselect_others = !((event.ShiftDown() || event.ControlDown()) && + HasFlag(wxTR_MULTIPLE)); + SelectItem (newItem, m_shiftItem, unselect_others); + } + EnsureVisible (newItem); + wxTreeListItem *oldItem = m_curItem; + SetCurrentItem((wxTreeListItem*)newItem.m_pItem); // make the new item the current item + RefreshLine (oldItem); + } + +} + +wxTreeItemId wxTreeListMainWindow::HitTest (const wxPoint& point, int& flags, int& column) { + + int w, h; + GetSize(&w, &h); + flags=0; + column = -1; + if (point.x<0) flags |= wxTREE_HITTEST_TOLEFT; + if (point.x>w) flags |= wxTREE_HITTEST_TORIGHT; + if (point.y<0) flags |= wxTREE_HITTEST_ABOVE; + if (point.y>h) flags |= wxTREE_HITTEST_BELOW; + if (flags) return wxTreeItemId(); + + if (!m_rootItem) { + flags = wxTREE_HITTEST_NOWHERE; + column = -1; + return wxTreeItemId(); + } + + wxTreeListItem *hit = m_rootItem->HitTest (CalcUnscrolledPosition(point), + this, flags, column, 0); + if (!hit) { + flags = wxTREE_HITTEST_NOWHERE; + column = -1; + return wxTreeItemId(); + } + return hit; +} + +// get the bounding rectangle of the item (or of its label only) +bool wxTreeListMainWindow::GetBoundingRect (const wxTreeItemId& itemId, wxRect& rect, + bool WXUNUSED(textOnly)) const { + wxCHECK_MSG (itemId.IsOk(), false, _T("invalid item in wxTreeListMainWindow::GetBoundingRect") ); + + wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem; + + int xUnit, yUnit; + GetScrollPixelsPerUnit (&xUnit, &yUnit); + int startX, startY; + GetViewStart(& startX, & startY); + + rect.x = item->GetX() - startX * xUnit; + rect.y = item->GetY() - startY * yUnit; + rect.width = item->GetWidth(); + rect.height = GetLineHeight (item); + + return true; +} + +/* **** */ + +void wxTreeListMainWindow::EditLabel (const wxTreeItemId& item, int column) { + +// validate + if (!item.IsOk()) return; + if (!((column >= 0) && (column < GetColumnCount()))) return; + +// cancel any editing + if (m_editControl) { + m_editControl->EndEdit(true); // cancelled + } + +// prepare edit (position) + m_editItem = (wxTreeListItem*) item.m_pItem; + + wxTreeEvent te( wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT, 0 ); + te.SetInt (column); + SendEvent(0, m_editItem, &te); if (!te.IsAllowed()) return; + + // ensure that the position of the item it calculated in any case + if (m_dirty) CalculatePositions(); + + wxTreeListHeaderWindow* header_win = m_owner->GetHeaderWindow(); + + // position & size are rather unpredictable (tsssk, tssssk) so were + // set by trial & error (on Win 2003 pre-XP style) + int x = 0; + int w = +4; // +4 is necessary, don't know why (simple border erronously counted somewhere ?) + int y = m_editItem->GetY() + 1; // this is cell, not text + int h = m_editItem->GetHeight() - 1; // consequence from above + long style = 0; + if (column == GetMainColumn()) { + x += m_editItem->GetTextX() - 2; // wrong by 2, don't know why + w += m_editItem->GetWidth(); + } else { + for (int i = 0; i < column; ++i) x += header_win->GetColumnWidth (i); // start of column + w += header_win->GetColumnWidth (column); // currently non-main column width not pre-computed + } + switch (header_win->GetColumnAlignment (column)) { + case wxALIGN_LEFT: {style = wxTE_LEFT; x -= 1; break;} + case wxALIGN_CENTER: {style = wxTE_CENTER; x -= 1; break;} + case wxALIGN_RIGHT: {style = wxTE_RIGHT; x += 0; break;} // yes, strange but that's the way it is + } + // wxTextCtrl simple border style requires 2 extra pixels before and after + // (measured by changing to style wxNO_BORDER in wxEditTextCtrl::wxEditTextCtrl() ) + y -= 2; x -= 2; + w += 4; h += 4; + + wxClientDC dc (this); + PrepareDC (dc); + x = dc.LogicalToDeviceX (x); + y = dc.LogicalToDeviceY (y); + +// now do edit (change state, show control) + m_editCol = column; // only used in OnRenameAccept() + m_editControl = new wxEditTextCtrl (this, -1, &m_editAccept, &m_editRes, + this, m_editItem->GetText (column), + wxPoint (x, y), wxSize (w, h), style); + m_editControl->SetFocus(); +} + +void wxTreeListMainWindow::OnRenameTimer() { + EditLabel (m_curItem, m_curColumn); +} + +void wxTreeListMainWindow::OnRenameAccept(bool isCancelled) { + + // TODO if the validator fails this causes a crash + wxTreeEvent le( wxEVT_COMMAND_TREE_END_LABEL_EDIT, 0 ); + le.SetLabel( m_editRes ); + le.SetEditCanceled(isCancelled); + le.SetInt(m_editCol); + SendEvent(0, m_editItem, &le); if (! isCancelled && le.IsAllowed()) + { + SetItemText (m_editItem, le.GetInt(), le.GetLabel()); + } +} + +void wxTreeListMainWindow::OnMouse (wxMouseEvent &event) { +bool mayDrag = true; +bool maySelect = true; // may change selection +bool mayClick = true; // may process DOWN clicks to expand, send click events +bool mayDoubleClick = true; // implies mayClick +bool bSkip = true; + + // send event to user code + if (m_owner->GetEventHandler()->ProcessEvent(event)) return; // handled (and not skipped) in user code + if (!m_rootItem) return; + + +// ---------- DETERMINE EVENT ---------- +/* +wxLogMessage("OnMouse: LMR down=<%d, %d, %d> up=<%d, %d, %d> LDblClick=<%d> dragging=<%d>", + event.LeftDown(), event.MiddleDown(), event.RightDown(), + event.LeftUp(), event.MiddleUp(), event.RightUp(), + event.LeftDClick(), event.Dragging()); +*/ + wxPoint p = wxPoint (event.GetX(), event.GetY()); + int flags = 0; + wxTreeListItem *item = m_rootItem->HitTest (CalcUnscrolledPosition (p), + this, flags, m_curColumn, 0); + bool bCrosshair = (item && item->HasPlus() && (flags & wxTREE_HITTEST_ONITEMBUTTON)); + // we were dragging + if (m_isDragging) { + maySelect = mayDoubleClick = false; + } + // we are starting or continuing to drag + if (event.Dragging()) { + maySelect = mayDoubleClick = mayClick = false; + } + // crosshair area is special + if (bCrosshair) { + // left click does not select + if (event.LeftDown()) maySelect = false; + // double click is ignored + mayDoubleClick = false; + } + // double click only if simple click + if (mayDoubleClick) mayDoubleClick = mayClick; + // selection conditions --remember also that selection exludes editing + if (maySelect) maySelect = mayClick; // yes, select/unselect requires a click + if (maySelect) { + + // multiple selection mode complicates things, sometimes we + // select on button-up instead of down: + if (HasFlag(wxTR_MULTIPLE)) { + + // CONTROL/SHIFT key used, don't care about anything else, will + // toggle on key down + if (event.ControlDown() || event.ShiftDown()) { + maySelect = maySelect && (event.LeftDown() || event.RightDown()); + m_lastOnSame = false; // prevent editing when keys are used + + // already selected item: to allow drag or contextual menu for multiple + // items, we only select/unselect on click-up --and only on LEFT + // click, right is reserved for contextual menu + } else if ((item != NULL && item->IsSelected())) { + maySelect = maySelect && event.LeftUp(); + + // non-selected items: select on click-down like simple select (so + // that a right-click contextual menu may be chained) + } else { + maySelect = maySelect && (event.LeftDown() || event.RightDown()); + } + + // single-select is simply on left or right click-down + } else { + maySelect = maySelect && (event.LeftDown() || event.RightDown()); + } + } + + +// ---------- GENERAL ACTIONS ---------- + + // set focus if window clicked + if (event.LeftDown() || event.MiddleDown() || event.RightDown()) SetFocus(); + + // tooltip change ? + if (item != m_toolTipItem) { + + // not over an item, use global tip + if (item == NULL) { + m_toolTipItem = NULL; + wxScrolledWindow::SetToolTip(m_toolTip); + + // over an item + } else { + const wxString *tip = item->GetToolTip(); + + // is there an item-specific tip ? + if (tip) { + m_toolTipItem = item; + wxScrolledWindow::SetToolTip(*tip); + + // no item tip, but we are in item-specific mode (SetItemToolTip() + // was called after SetToolTip() ) + } else if (m_isItemToolTip) { + m_toolTipItem = item; + wxScrolledWindow::SetToolTip(wxString()); + + // no item tip, display global tip instead; item change ignored + } else if (m_toolTipItem != NULL) { + m_toolTipItem = NULL; + wxScrolledWindow::SetToolTip(m_toolTip); + } + } + } + + +// ---------- HANDLE SIMPLE-CLICKS (selection change, contextual menu) ---------- + if (mayClick) { + + // 2nd left-click on an item might trigger edit + if (event.LeftDown()) m_lastOnSame = (item == m_curItem); + + // left-click on haircross is expand (and no select) + if (bCrosshair && event.LeftDown()) { + + bSkip = false; + + // note that we only toggle the item for a single click, double + // click on the button doesn't do anything + Toggle (item); + } + + if (maySelect) { + bSkip = false; + + // set / remember item at shift down before current item gets changed + if (event.LeftDown() && HasFlag(wxTR_MULTIPLE) && event.ShiftDown()) { + if (!m_shiftItem) m_shiftItem = m_curItem; + }else{ + m_shiftItem = (wxTreeListItem*)NULL; + } + + // how is selection altered + // keep or discard already selected ? + bool unselect_others = ! (HasFlag(wxTR_MULTIPLE) && ( + event.ShiftDown() + || event.ControlDown() + )); + + // check is selection change is not vetoed + if (SelectItem(item, m_shiftItem, unselect_others)) { + // make the new item the current item + EnsureVisible (item); + SetCurrentItem(item); + } + } + + // generate click & menu events + if (event.MiddleDown()) { + bSkip = false; + SendEvent(wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK, item); + } + if (event.RightDown()) { + bSkip = false; + SendEvent(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK, item); + } + if (event.RightUp()) { + wxTreeEvent nevent(wxEVT_COMMAND_TREE_ITEM_MENU, 0); + nevent.SetPoint(p); + nevent.SetInt(m_curColumn); + SendEvent(0, item, &nevent); + } + + // if 2nd left click finishes on same item, will edit it + if (m_lastOnSame && event.LeftUp()) { + if ((item == m_curItem) && (m_curColumn != -1) && + (m_owner->GetHeaderWindow()->IsColumnEditable (m_curColumn)) && + (flags & (wxTREE_HITTEST_ONITEMLABEL | wxTREE_HITTEST_ONITEMCOLUMN)) + ){ + m_editTimer->Start (RENAME_TIMER_TICKS, wxTIMER_ONE_SHOT); + bSkip = false; + } + m_lastOnSame = false; + } + } + + +// ---------- HANDLE DOUBLE-CLICKS ---------- + if (mayDoubleClick && event.LeftDClick()) { + + bSkip = false; + + // double clicking should not start editing the item label + m_editTimer->Stop(); + m_lastOnSame = false; + + // selection reset to that single item which was double-clicked + if (SelectItem(item, (wxTreeItemId*)NULL, true)) { // unselect others --return false if vetoed + + // selection change not vetoed, send activate event + if (! SendEvent(wxEVT_COMMAND_TREE_ITEM_ACTIVATED, item)) { + + // if the user code didn't process the activate event, + // handle it ourselves by toggling the item when it is + // double clicked + if (item && item->HasPlus()) Toggle(item); + } + } + } + + +// ---------- HANDLE DRAGGING ---------- +// NOTE: drag itself makes no change to selection + if (mayDrag) { // actually this is always true + + // CASE 1: we were dragging => continue, end, abort + if (m_isDragging) { + + // CASE 1.1: click aborts drag: + if (event.LeftDown() || event.MiddleDown() || event.RightDown()) { + + bSkip = false; + + // stop dragging + m_isDragStarted = m_isDragging = false; + if (HasCapture()) ReleaseMouse(); + RefreshSelected(); + + // CASE 1.2: still dragging + } else if (event.Dragging()) { + + ;; // nothing to do + + // CASE 1.3: dragging now ends normally + } else { + + bSkip = false; + + // stop dragging + m_isDragStarted = m_isDragging = false; + if (HasCapture()) ReleaseMouse(); + RefreshSelected(); + + // send drag end event + wxTreeEvent event(wxEVT_COMMAND_TREE_END_DRAG, 0); + event.SetPoint(p); + event.SetInt(m_curColumn); + SendEvent(0, item, &event); + } + + // CASE 2: not were not dragging => continue, start + } else if (event.Dragging()) { + + // We will really start dragging if we've moved beyond a few pixels + if (m_isDragStarted) { + const int tolerance = 3; + int dx = abs(p.x - m_dragStartPos.x); + int dy = abs(p.y - m_dragStartPos.y); + if (dx <= tolerance && dy <= tolerance) + return; + // determine drag start + } else { + m_dragStartPos = p; + m_dragCol = m_curColumn; + m_dragItem = item; + m_isDragStarted = true; + return; + } + + bSkip = false; + + // we are now dragging + m_isDragging = true; + RefreshSelected(); + CaptureMouse(); // TODO: usefulness unclear + + wxTreeEvent nevent(event.LeftIsDown() + ? wxEVT_COMMAND_TREE_BEGIN_DRAG + : wxEVT_COMMAND_TREE_BEGIN_RDRAG, 0); + nevent.SetPoint(p); + nevent.SetInt(m_dragCol); + nevent.Veto(); + SendEvent(0, m_dragItem, &nevent); + } + } + + + if (bSkip) event.Skip(); +} + + +void wxTreeListMainWindow::OnIdle (wxIdleEvent &WXUNUSED(event)) { + /* after all changes have been done to the tree control, + * we actually redraw the tree when everything is over */ + + if (!m_dirty) return; + + m_dirty = false; + + CalculatePositions(); + Refresh(); + AdjustMyScrollbars(); +} + +void wxTreeListMainWindow::OnScroll (wxScrollWinEvent& event) { + // FIXME +#if defined(__WXGTK__) && !defined(__WXUNIVERSAL__) + wxScrolledWindow::OnScroll(event); +#else + HandleOnScroll( event ); +#endif + + if(event.GetOrientation() == wxHORIZONTAL) { + m_owner->GetHeaderWindow()->Refresh(); + m_owner->GetHeaderWindow()->Update(); + } +} + +void wxTreeListMainWindow::CalculateSize (wxTreeListItem *item, wxDC &dc) { + wxCoord text_w = 0; + wxCoord text_h = 0; + + dc.SetFont (GetItemFont (item)); + dc.GetTextExtent (item->GetText(m_main_column).size() > 0 + ? item->GetText (m_main_column) + : _T(" "), // blank to avoid zero height and no highlight width + &text_w, &text_h); + // restore normal font + dc.SetFont (m_normalFont); + + int max_h = (m_imgHeight > text_h) ? m_imgHeight : text_h; + if (max_h < 30) { // add 10% space if greater than 30 pixels + max_h += 2; // minimal 2 pixel space + }else{ + max_h += max_h / 10; // otherwise 10% space + } + + item->SetHeight (max_h); + if (max_h > m_lineHeight) m_lineHeight = max_h; + item->SetWidth(m_imgWidth + text_w+2); +} + +// ----------------------------------------------------------------------------- +void wxTreeListMainWindow::CalculateLevel (wxTreeListItem *item, wxDC &dc, + int level, int &y, int x_colstart) { + + // calculate position of vertical lines + int x = x_colstart + MARGIN; // start of column + if (HasFlag(wxTR_LINES_AT_ROOT)) x += LINEATROOT; // space for lines at root + if (HasButtons()) { + x += (m_btnWidth-m_btnWidth2); // half button space + }else{ + x += (m_indent-m_indent/2); + } + if (HasFlag(wxTR_HIDE_ROOT)) { + x += m_indent * (level-1); // indent but not level 1 + }else{ + x += m_indent * level; // indent according to level + } + + // a hidden root is not evaluated, but its children are always + if (HasFlag(wxTR_HIDE_ROOT) && (level == 0)) goto Recurse; + + CalculateSize( item, dc ); + + // set its position + item->SetX (x); + item->SetY (y); + y += GetLineHeight(item); + + // we don't need to calculate collapsed branches + if ( !item->IsExpanded() ) return; + +Recurse: + wxArrayTreeListItems& children = item->GetChildren(); + long n, count = (long)children.Count(); + ++level; + for (n = 0; n < count; ++n) { + CalculateLevel( children[n], dc, level, y, x_colstart ); // recurse + } +} + +void wxTreeListMainWindow::CalculatePositions() { + if ( !m_rootItem ) return; + + wxClientDC dc(this); + PrepareDC( dc ); + + dc.SetFont( m_normalFont ); + + dc.SetPen( m_dottedPen ); + //if(GetImageList() == NULL) + // m_lineHeight = (int)(dc.GetCharHeight() + 4); + + int y = 2; + int x_colstart = 0; + for (int i = 0; i < (int)GetMainColumn(); ++i) { + if (!m_owner->GetHeaderWindow()->IsColumnShown(i)) continue; + x_colstart += m_owner->GetHeaderWindow()->GetColumnWidth(i); + } + CalculateLevel( m_rootItem, dc, 0, y, x_colstart ); // start recursion +} + +void wxTreeListMainWindow::RefreshSubtree (wxTreeListItem *item) { + if (m_dirty) return; + + wxClientDC dc(this); + PrepareDC(dc); + + int cw = 0; + int ch = 0; + GetVirtualSize( &cw, &ch ); + + wxRect rect; + rect.x = dc.LogicalToDeviceX( 0 ); + rect.width = cw; + rect.y = dc.LogicalToDeviceY( item->GetY() - 2 ); + rect.height = ch; + + Refresh (true, &rect ); + AdjustMyScrollbars(); +} + +void wxTreeListMainWindow::RefreshLine (wxTreeListItem *item) { + if (m_dirty) return; + + wxClientDC dc(this); + PrepareDC( dc ); + + int cw = 0; + int ch = 0; + GetVirtualSize( &cw, &ch ); + + wxRect rect; + rect.x = dc.LogicalToDeviceX( 0 ); + rect.y = dc.LogicalToDeviceY( item->GetY() ); + rect.width = cw; + rect.height = GetLineHeight(item); //dc.GetCharHeight() + 6; + + Refresh (true, &rect); +} + +void wxTreeListMainWindow::RefreshSelected() { + // TODO: this is awfully inefficient, we should keep the list of all + // selected items internally, should be much faster + if (m_rootItem) { + RefreshSelectedUnder (m_rootItem); + } +} + +void wxTreeListMainWindow::RefreshSelectedUnder (wxTreeListItem *item) { + if (item->IsSelected()) { + RefreshLine (item); + } + + const wxArrayTreeListItems& children = item->GetChildren(); + long count = (long)children.GetCount(); + for (long n = 0; n < count; n++ ) { + RefreshSelectedUnder (children[n]); + } +} + +// ---------------------------------------------------------------------------- +// changing colours: we need to refresh the tree control +// ---------------------------------------------------------------------------- + +bool wxTreeListMainWindow::SetBackgroundColour (const wxColour& colour) { + if (!wxWindow::SetBackgroundColour(colour)) return false; + + Refresh(); + return true; +} + +bool wxTreeListMainWindow::SetForegroundColour (const wxColour& colour) { + if (!wxWindow::SetForegroundColour(colour)) return false; + + Refresh(); + return true; +} + +void wxTreeListMainWindow::SetItemText (const wxTreeItemId& itemId, int column, + const wxString& text) { + wxCHECK_RET (itemId.IsOk(), _T("invalid tree item")); + + wxClientDC dc (this); + wxTreeListItem *item = (wxTreeListItem*) itemId.m_pItem; + item->SetText (column, text); + CalculateSize (item, dc); + RefreshLine (item); +} + +wxString wxTreeListMainWindow::GetItemText (const wxTreeItemId& itemId, + int column) const { + wxCHECK_MSG (itemId.IsOk(), _T(""), _T("invalid tree item") ); + + if( IsVirtual() ) return m_owner->OnGetItemText(((wxTreeListItem*) itemId.m_pItem)->GetData(),column); + else return ((wxTreeListItem*) itemId.m_pItem)->GetText (column); +} + +wxString wxTreeListMainWindow::GetItemText (wxTreeItemData* item, +int column) const { + wxASSERT_MSG( IsVirtual(), _T("can be used only with virtual control") ); + return m_owner->OnGetItemText(item,column); +} + +void wxTreeListMainWindow::SetFocus() { + wxWindow::SetFocus(); +} + +wxFont wxTreeListMainWindow::GetItemFont (wxTreeListItem *item) { + wxTreeItemAttr *attr = item->GetAttributes(); + + if (attr && attr->HasFont()) { + return attr->GetFont(); + }else if (item->IsBold()) { + return m_boldFont; + }else{ + return m_normalFont; + } +} + +int wxTreeListMainWindow::GetItemWidth (int column, wxTreeListItem *item) { + if (!item) return 0; + + // determine item width + int w = 0, h = 0; + wxFont font = GetItemFont (item); + GetTextExtent (item->GetText (column), &w, &h, NULL, NULL, font.Ok()? &font: NULL); + w += 2*MARGIN; + + // calculate width + int width = w + 2*MARGIN; + if (column == GetMainColumn()) { + width += MARGIN; + if (HasFlag(wxTR_LINES_AT_ROOT)) width += LINEATROOT; + if (HasButtons()) width += m_btnWidth + LINEATROOT; + if (item->GetCurrentImage() != NO_IMAGE) width += m_imgWidth; + + // count indent level + int level = 0; + wxTreeListItem *parent = item->GetItemParent(); + wxTreeListItem *root = (wxTreeListItem*)GetRootItem().m_pItem; + while (parent && (!HasFlag(wxTR_HIDE_ROOT) || (parent != root))) { + level++; + parent = parent->GetItemParent(); + } + if (level) width += level * GetIndent(); + } + + return width; +} + +int wxTreeListMainWindow::GetBestColumnWidth (int column, wxTreeItemId parent) { + int maxWidth, h; + GetClientSize (&maxWidth, &h); + int width = 0; + + // get root if on item + if (!parent.IsOk()) parent = GetRootItem(); + + // add root width + if (!HasFlag(wxTR_HIDE_ROOT)) { + int w = GetItemWidth (column, (wxTreeListItem*)parent.m_pItem); + if (width < w) width = w; + if (width > maxWidth) return maxWidth; + } + + wxTreeItemIdValue cookie = 0; + wxTreeItemId item = GetFirstChild (parent, cookie); + while (item.IsOk()) { + int w = GetItemWidth (column, (wxTreeListItem*)item.m_pItem); + if (width < w) width = w; + if (width > maxWidth) return maxWidth; + + // check the children of this item + if (((wxTreeListItem*)item.m_pItem)->IsExpanded()) { + int w = GetBestColumnWidth (column, item); + if (width < w) width = w; + if (width > maxWidth) return maxWidth; + } + + // next sibling + item = GetNextChild (parent, cookie); + } + + return width; +} + + +bool wxTreeListMainWindow::SendEvent(wxEventType event_type, wxTreeListItem *item, wxTreeEvent *event) { +wxTreeEvent nevent (event_type, 0); + + if (event == NULL) { + event = &nevent; + event->SetInt (m_curColumn); // the mouse colum + } + + event->SetEventObject (m_owner); + event->SetId(m_owner->GetId()); + if (item) { +#if !wxCHECK_VERSION(2, 5, 0) + event->SetItem ((long)item); +#else + event->SetItem (item); +#endif + } + + return m_owner->GetEventHandler()->ProcessEvent (*event); +} + + +//----------------------------------------------------------------------------- +// wxTreeListCtrl +//----------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxTreeListCtrl, wxControl); + +BEGIN_EVENT_TABLE(wxTreeListCtrl, wxControl) + EVT_SIZE(wxTreeListCtrl::OnSize) +END_EVENT_TABLE(); + +bool wxTreeListCtrl::Create(wxWindow *parent, wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, const wxValidator &validator, + const wxString& name) +{ + long main_style = style & ~(wxSIMPLE_BORDER|wxSUNKEN_BORDER|wxDOUBLE_BORDER| + wxRAISED_BORDER|wxSTATIC_BORDER); + main_style |= wxWANTS_CHARS ; + long ctrl_style = style & ~(wxVSCROLL|wxHSCROLL); + + if (!wxControl::Create(parent, id, pos, size, ctrl_style, validator, name)) { + return false; + } + m_main_win = new wxTreeListMainWindow (this, -1, wxPoint(0, 0), size, + main_style, validator); + m_header_win = new wxTreeListHeaderWindow (this, -1, m_main_win, + wxPoint(0, 0), wxDefaultSize, + wxTAB_TRAVERSAL); + CalculateAndSetHeaderHeight(); + return true; +} + +void wxTreeListCtrl::CalculateAndSetHeaderHeight() +{ + if (m_header_win) { + + // we use 'g' to get the descent, too + int h; +#if wxCHECK_VERSION_FULL(2, 7, 0, 1) +#ifdef __WXMSW__ + h = (int)(wxRendererNative::Get().GetHeaderButtonHeight(m_header_win) * 0.8) + 2; +#else + h = wxRendererNative::Get().GetHeaderButtonHeight(m_header_win); +#endif +#else + int w, d; + m_header_win->GetTextExtent(_T("Hg"), &w, &h, &d); + h += d + 2 * HEADER_OFFSET_Y + EXTRA_HEIGHT; +#endif + + // only update if changed + if (h != m_headerHeight) { + m_headerHeight = h; + DoHeaderLayout(); + } + } +} + +void wxTreeListCtrl::DoHeaderLayout() +{ + int w, h; + GetClientSize(&w, &h); + if (m_header_win) { + m_header_win->SetSize (0, 0, w, m_headerHeight); + m_header_win->Refresh(); + } + if (m_main_win) { + m_main_win->SetSize (0, m_headerHeight, w, h - m_headerHeight); + } +} + +void wxTreeListCtrl::OnSize(wxSizeEvent& WXUNUSED(event)) +{ + DoHeaderLayout(); +} + +size_t wxTreeListCtrl::GetCount() const { return m_main_win->GetCount(); } + +unsigned int wxTreeListCtrl::GetIndent() const +{ return m_main_win->GetIndent(); } + +void wxTreeListCtrl::SetIndent(unsigned int indent) +{ m_main_win->SetIndent(indent); } + +unsigned int wxTreeListCtrl::GetLineSpacing() const +{ return m_main_win->GetLineSpacing(); } + +void wxTreeListCtrl::SetLineSpacing(unsigned int spacing) +{ m_main_win->SetLineSpacing(spacing); } + +wxImageList* wxTreeListCtrl::GetImageList() const +{ return m_main_win->GetImageList(); } + +wxImageList* wxTreeListCtrl::GetStateImageList() const +{ return m_main_win->GetStateImageList(); } + +wxImageList* wxTreeListCtrl::GetButtonsImageList() const +{ return m_main_win->GetButtonsImageList(); } + +void wxTreeListCtrl::SetImageList(wxImageList* imageList) +{ m_main_win->SetImageList(imageList); } + +void wxTreeListCtrl::SetStateImageList(wxImageList* imageList) +{ m_main_win->SetStateImageList(imageList); } + +void wxTreeListCtrl::SetButtonsImageList(wxImageList* imageList) +{ m_main_win->SetButtonsImageList(imageList); } + +void wxTreeListCtrl::AssignImageList(wxImageList* imageList) +{ m_main_win->AssignImageList(imageList); } + +void wxTreeListCtrl::AssignStateImageList(wxImageList* imageList) +{ m_main_win->AssignStateImageList(imageList); } + +void wxTreeListCtrl::AssignButtonsImageList(wxImageList* imageList) +{ m_main_win->AssignButtonsImageList(imageList); } + +wxString wxTreeListCtrl::GetItemText(const wxTreeItemId& item, int column) const +{ return m_main_win->GetItemText (item, column); } + +int wxTreeListCtrl::GetItemImage(const wxTreeItemId& item, int column, + wxTreeItemIcon which) const +{ return m_main_win->GetItemImage(item, column, which); } + +wxTreeItemData* wxTreeListCtrl::GetItemData(const wxTreeItemId& item) const +{ return m_main_win->GetItemData(item); } + +bool wxTreeListCtrl::GetItemBold(const wxTreeItemId& item) const +{ return m_main_win->GetItemBold(item); } + +wxColour wxTreeListCtrl::GetItemTextColour(const wxTreeItemId& item) const +{ return m_main_win->GetItemTextColour(item); } + +wxColour wxTreeListCtrl::GetItemBackgroundColour(const wxTreeItemId& item) + const +{ return m_main_win->GetItemBackgroundColour(item); } + +wxFont wxTreeListCtrl::GetItemFont(const wxTreeItemId& item) const +{ return m_main_win->GetItemFont(item); } + + +void wxTreeListCtrl::SetItemText(const wxTreeItemId& item, int column, + const wxString& text) +{ m_main_win->SetItemText (item, column, text); } + +void wxTreeListCtrl::SetItemImage(const wxTreeItemId& item, + int column, + int image, + wxTreeItemIcon which) +{ m_main_win->SetItemImage(item, column, image, which); } + +void wxTreeListCtrl::SetItemData(const wxTreeItemId& item, + wxTreeItemData* data) +{ m_main_win->SetItemData(item, data); } + +void wxTreeListCtrl::SetItemHasChildren(const wxTreeItemId& item, bool has) +{ m_main_win->SetItemHasChildren(item, has); } + +void wxTreeListCtrl::SetItemBold(const wxTreeItemId& item, bool bold) +{ m_main_win->SetItemBold(item, bold); } + +void wxTreeListCtrl::SetItemTextColour(const wxTreeItemId& item, + const wxColour& colour) +{ m_main_win->SetItemTextColour(item, colour); } + +void wxTreeListCtrl::SetItemBackgroundColour(const wxTreeItemId& item, + const wxColour& colour) +{ m_main_win->SetItemBackgroundColour(item, colour); } + +void wxTreeListCtrl::SetItemFont(const wxTreeItemId& item, + const wxFont& font) +{ m_main_win->SetItemFont(item, font); } + +bool wxTreeListCtrl::SetFont(const wxFont& font) +{ + if (m_header_win) { + m_header_win->SetFont(font); + CalculateAndSetHeaderHeight(); + m_header_win->Refresh(); + } + if (m_main_win) { + return m_main_win->SetFont(font); + }else{ + return false; + } +} + +void wxTreeListCtrl::SetWindowStyle(const long style) +{ + if(m_main_win) + m_main_win->SetWindowStyle(style); + m_windowStyle = style; + // TODO: provide something like wxTL_NO_HEADERS to hide m_header_win +} + +long wxTreeListCtrl::GetWindowStyle() const +{ + long style = m_windowStyle; + if(m_main_win) + style |= m_main_win->GetWindowStyle(); + return style; +} + +bool wxTreeListCtrl::IsVisible(const wxTreeItemId& item, bool fullRow, bool within) const +{ return m_main_win->IsVisible(item, fullRow, within); } + +bool wxTreeListCtrl::HasChildren(const wxTreeItemId& item) const +{ return m_main_win->HasChildren(item); } + +bool wxTreeListCtrl::IsExpanded(const wxTreeItemId& item) const +{ return m_main_win->IsExpanded(item); } + +bool wxTreeListCtrl::IsSelected(const wxTreeItemId& item) const +{ return m_main_win->IsSelected(item); } + +bool wxTreeListCtrl::IsBold(const wxTreeItemId& item) const +{ return m_main_win->IsBold(item); } + +size_t wxTreeListCtrl::GetChildrenCount(const wxTreeItemId& item, bool rec) +{ return m_main_win->GetChildrenCount(item, rec); } + +wxTreeItemId wxTreeListCtrl::GetRootItem() const +{ return m_main_win->GetRootItem(); } + +wxTreeItemId wxTreeListCtrl::GetSelection() const +{ return m_main_win->GetSelection(); } + +size_t wxTreeListCtrl::GetSelections(wxArrayTreeItemIds& arr) const +{ return m_main_win->GetSelections(arr); } + +wxTreeItemId wxTreeListCtrl::GetItemParent(const wxTreeItemId& item) const +{ return m_main_win->GetItemParent(item); } + +#if !wxCHECK_VERSION(2, 5, 0) +wxTreeItemId wxTreeListCtrl::GetFirstChild (const wxTreeItemId& item, + long& cookie) const +#else +wxTreeItemId wxTreeListCtrl::GetFirstChild (const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const +#endif +{ return m_main_win->GetFirstChild(item, cookie); } + +#if !wxCHECK_VERSION(2, 5, 0) +wxTreeItemId wxTreeListCtrl::GetNextChild (const wxTreeItemId& item, + long& cookie) const +#else +wxTreeItemId wxTreeListCtrl::GetNextChild (const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const +#endif +{ return m_main_win->GetNextChild(item, cookie); } + +#if !wxCHECK_VERSION(2, 5, 0) +wxTreeItemId wxTreeListCtrl::GetPrevChild (const wxTreeItemId& item, + long& cookie) const +#else +wxTreeItemId wxTreeListCtrl::GetPrevChild (const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const +#endif +{ return m_main_win->GetPrevChild(item, cookie); } + +#if !wxCHECK_VERSION(2, 5, 0) +wxTreeItemId wxTreeListCtrl::GetLastChild (const wxTreeItemId& item, + long& cookie) const +#else +wxTreeItemId wxTreeListCtrl::GetLastChild (const wxTreeItemId& item, + wxTreeItemIdValue& cookie) const +#endif +{ return m_main_win->GetLastChild(item, cookie); } + + +wxTreeItemId wxTreeListCtrl::GetNextSibling(const wxTreeItemId& item) const +{ return m_main_win->GetNextSibling(item); } + +wxTreeItemId wxTreeListCtrl::GetPrevSibling(const wxTreeItemId& item) const +{ return m_main_win->GetPrevSibling(item); } + +wxTreeItemId wxTreeListCtrl::GetNext(const wxTreeItemId& item) const +{ return m_main_win->GetNext(item, true); } + +wxTreeItemId wxTreeListCtrl::GetPrev(const wxTreeItemId& item) const +{ return m_main_win->GetPrev(item, true); } + +wxTreeItemId wxTreeListCtrl::GetFirstExpandedItem() const +{ return m_main_win->GetFirstExpandedItem(); } + +wxTreeItemId wxTreeListCtrl::GetNextExpanded(const wxTreeItemId& item) const +{ return m_main_win->GetNextExpanded(item); } + +wxTreeItemId wxTreeListCtrl::GetPrevExpanded(const wxTreeItemId& item) const +{ return m_main_win->GetPrevExpanded(item); } + +wxTreeItemId wxTreeListCtrl::GetFirstVisibleItem(bool fullRow) const +{ return GetFirstVisible(fullRow); } +wxTreeItemId wxTreeListCtrl::GetFirstVisible(bool fullRow, bool within) const +{ return m_main_win->GetFirstVisible(fullRow, within); } + +wxTreeItemId wxTreeListCtrl::GetLastVisible(bool fullRow, bool within) const +{ return m_main_win->GetLastVisible(fullRow, within); } + +wxTreeItemId wxTreeListCtrl::GetNextVisible(const wxTreeItemId& item, bool fullRow, bool within) const +{ return m_main_win->GetNextVisible(item, fullRow, within); } + +wxTreeItemId wxTreeListCtrl::GetPrevVisible(const wxTreeItemId& item, bool fullRow, bool within) const +{ return m_main_win->GetPrevVisible(item, fullRow, within); } + +wxTreeItemId wxTreeListCtrl::AddRoot (const wxString& text, int image, + int selectedImage, wxTreeItemData* data) +{ return m_main_win->AddRoot (text, image, selectedImage, data); } + +wxTreeItemId wxTreeListCtrl::PrependItem(const wxTreeItemId& parent, + const wxString& text, int image, + int selectedImage, + wxTreeItemData* data) +{ return m_main_win->PrependItem(parent, text, image, selectedImage, data); } + +wxTreeItemId wxTreeListCtrl::InsertItem(const wxTreeItemId& parent, + const wxTreeItemId& previous, + const wxString& text, int image, + int selectedImage, + wxTreeItemData* data) +{ + return m_main_win->InsertItem(parent, previous, text, image, + selectedImage, data); +} + +wxTreeItemId wxTreeListCtrl::InsertItem(const wxTreeItemId& parent, + size_t index, + const wxString& text, int image, + int selectedImage, + wxTreeItemData* data) +{ + return m_main_win->InsertItem(parent, index, text, image, + selectedImage, data); +} + +wxTreeItemId wxTreeListCtrl::AppendItem(const wxTreeItemId& parent, + const wxString& text, int image, + int selectedImage, + wxTreeItemData* data) +{ return m_main_win->AppendItem(parent, text, image, selectedImage, data); } + +void wxTreeListCtrl::Delete(const wxTreeItemId& item) +{ m_main_win->Delete(item); } + +void wxTreeListCtrl::DeleteChildren(const wxTreeItemId& item) +{ m_main_win->DeleteChildren(item); } + +void wxTreeListCtrl::DeleteRoot() +{ m_main_win->DeleteRoot(); } + +void wxTreeListCtrl::Expand(const wxTreeItemId& item) +{ m_main_win->Expand(item); } + +void wxTreeListCtrl::ExpandAll(const wxTreeItemId& item) +{ m_main_win->ExpandAll(item); } + +void wxTreeListCtrl::Collapse(const wxTreeItemId& item) +{ m_main_win->Collapse(item); } + +void wxTreeListCtrl::CollapseAndReset(const wxTreeItemId& item) +{ m_main_win->CollapseAndReset(item); } + +void wxTreeListCtrl::Toggle(const wxTreeItemId& item) +{ m_main_win->Toggle(item); } + +void wxTreeListCtrl::Unselect() +{ m_main_win->Unselect(); } + +void wxTreeListCtrl::UnselectAll() +{ m_main_win->UnselectAll(); } + +bool wxTreeListCtrl::SelectItem(const wxTreeItemId& item, const wxTreeItemId& last, + bool unselect_others) +{ return m_main_win->SelectItem (item, last, unselect_others); } + +void wxTreeListCtrl::SelectAll() +{ m_main_win->SelectAll(); } + +void wxTreeListCtrl::EnsureVisible(const wxTreeItemId& item) +{ m_main_win->EnsureVisible(item); } + +void wxTreeListCtrl::ScrollTo(const wxTreeItemId& item) +{ m_main_win->ScrollTo(item); } + +wxTreeItemId wxTreeListCtrl::HitTest(const wxPoint& pos, int& flags, int& column) +{ + wxPoint p = m_main_win->ScreenToClient (ClientToScreen (pos)); + return m_main_win->HitTest (p, flags, column); +} + +bool wxTreeListCtrl::GetBoundingRect(const wxTreeItemId& item, wxRect& rect, + bool textOnly) const +{ return m_main_win->GetBoundingRect(item, rect, textOnly); } + +void wxTreeListCtrl::EditLabel (const wxTreeItemId& item, int column) +{ m_main_win->EditLabel (item, column); } + +int wxTreeListCtrl::OnCompareItems(const wxTreeItemId& item1, + const wxTreeItemId& item2) +{ + // do the comparison here, and not delegate to m_main_win, in order + // to let the user override it + //return m_main_win->OnCompareItems(item1, item2); + return wxStrcmp(GetItemText(item1), GetItemText(item2)); +} + +void wxTreeListCtrl::SortChildren(const wxTreeItemId& item) +{ m_main_win->SortChildren(item); } + +wxTreeItemId wxTreeListCtrl::FindItem (const wxTreeItemId& item, const wxString& str, int mode) +{ return m_main_win->FindItem (item, str, mode); } + +void wxTreeListCtrl::SetDragItem (const wxTreeItemId& item) +{ m_main_win->SetDragItem (item); } + +bool wxTreeListCtrl::SetBackgroundColour(const wxColour& colour) +{ + if (!m_main_win) return false; + return m_main_win->SetBackgroundColour(colour); +} + +bool wxTreeListCtrl::SetForegroundColour(const wxColour& colour) +{ + if (!m_main_win) return false; + return m_main_win->SetForegroundColour(colour); +} + +int wxTreeListCtrl::GetColumnCount() const +{ return m_main_win->GetColumnCount(); } + +void wxTreeListCtrl::SetColumnWidth(int column, int width) +{ + m_header_win->SetColumnWidth (column, width); + m_header_win->Refresh(); +} + +int wxTreeListCtrl::GetColumnWidth(int column) const +{ return m_header_win->GetColumnWidth(column); } + +void wxTreeListCtrl::SetMainColumn(int column) +{ m_main_win->SetMainColumn(column); } + +int wxTreeListCtrl::GetMainColumn() const +{ return m_main_win->GetMainColumn(); } + +void wxTreeListCtrl::SetColumnText(int column, const wxString& text) +{ + m_header_win->SetColumnText (column, text); + m_header_win->Refresh(); +} + +wxString wxTreeListCtrl::GetColumnText(int column) const +{ return m_header_win->GetColumnText(column); } + +void wxTreeListCtrl::AddColumn(const wxTreeListColumnInfo& colInfo) +{ + m_header_win->AddColumn (colInfo); + DoHeaderLayout(); +} + +void wxTreeListCtrl::InsertColumn(int before, const wxTreeListColumnInfo& colInfo) +{ + m_header_win->InsertColumn (before, colInfo); + m_header_win->Refresh(); +} + +void wxTreeListCtrl::RemoveColumn(int column) +{ + m_header_win->RemoveColumn (column); + m_header_win->Refresh(); +} + +void wxTreeListCtrl::SetColumn(int column, const wxTreeListColumnInfo& colInfo) +{ + m_header_win->SetColumn (column, colInfo); + m_header_win->Refresh(); +} + +const wxTreeListColumnInfo& wxTreeListCtrl::GetColumn(int column) const +{ return m_header_win->GetColumn(column); } + +wxTreeListColumnInfo& wxTreeListCtrl::GetColumn(int column) +{ return m_header_win->GetColumn(column); } + +void wxTreeListCtrl::SetColumnImage(int column, int image) +{ + m_header_win->SetColumn (column, GetColumn(column).SetImage(image)); + m_header_win->Refresh(); +} + +int wxTreeListCtrl::GetColumnImage(int column) const +{ + return m_header_win->GetColumn(column).GetImage(); +} + +void wxTreeListCtrl::SetColumnEditable(int column, bool shown) +{ + m_header_win->SetColumn (column, GetColumn(column).SetEditable(shown)); +} + +void wxTreeListCtrl::SetColumnShown(int column, bool shown) +{ + wxASSERT_MSG (column != GetMainColumn(), _T("The main column may not be hidden") ); + m_header_win->SetColumn (column, GetColumn(column).SetShown(GetMainColumn()==column? true: shown)); + m_header_win->Refresh(); +} + +bool wxTreeListCtrl::IsColumnEditable(int column) const +{ + return m_header_win->GetColumn(column).IsEditable(); +} + +bool wxTreeListCtrl::IsColumnShown(int column) const +{ + return m_header_win->GetColumn(column).IsShown(); +} + +void wxTreeListCtrl::SetColumnAlignment (int column, int flag) +{ + m_header_win->SetColumn(column, GetColumn(column).SetAlignment(flag)); + m_header_win->Refresh(); +} + +int wxTreeListCtrl::GetColumnAlignment(int column) const +{ + return m_header_win->GetColumn(column).GetAlignment(); +} + +void wxTreeListCtrl::Refresh(bool erase, const wxRect* rect) +{ + m_main_win->Refresh (erase, rect); + m_header_win->Refresh (erase, rect); +} + +void wxTreeListCtrl::SetFocus() +{ m_main_win->SetFocus(); } + +wxSize wxTreeListCtrl::DoGetBestSize() const +{ + // something is better than nothing... + return wxSize (200,200); // but it should be specified values! FIXME +} + +wxString wxTreeListCtrl::OnGetItemText( wxTreeItemData* WXUNUSED(item), long WXUNUSED(column)) const +{ + return wxEmptyString; +} + +void wxTreeListCtrl::SetToolTip(const wxString& tip) { + m_header_win->SetToolTip(tip); + m_main_win->SetToolTip(tip); +} +void wxTreeListCtrl::SetToolTip(wxToolTip *tip) { + m_header_win->SetToolTip(tip); + m_main_win->SetToolTip(tip); +} + +void wxTreeListCtrl::SetItemToolTip(const wxTreeItemId& item, const wxString &tip) { + m_main_win->SetItemToolTip(item, tip); +} diff --git a/xvaga/treelistctrl.h b/xvaga/treelistctrl.h new file mode 100755 index 000000000..026913226 --- /dev/null +++ b/xvaga/treelistctrl.h @@ -0,0 +1,552 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: treelistctrl.h +// Purpose: wxTreeListCtrl class +// Author: Robert Roebling +// Maintainer: Otto Wyss +// Created: 01/02/97 +// RCS-ID: $Id: treelistctrl.h,v 1.1 2010-08-11 12:53:55 guy Exp $ +// Copyright: (c) 2004 Robert Roebling, Julian Smart, Alberto Griggio, +// Vadim Zeitlin, Otto Wyss +// Licence: wxWindows +///////////////////////////////////////////////////////////////////////////// + + +#ifndef TREELISTCTRL_H +#define TREELISTCTRL_H + +#if defined(__GNUG__) && !defined(__APPLE__) + #pragma interface "treelistctrl.h" +#endif +#include +#include // for wxListEvent + +class WXDLLEXPORT wxTreeListItem; +class WXDLLEXPORT wxTreeListHeaderWindow; +class WXDLLEXPORT wxTreeListMainWindow; + +#define wxTR_COLUMN_LINES 0x1000 // put border around items +#define wxTR_VIRTUAL 0x4000 // The application provides items text on demand. + +// Using this typedef removes an ambiguity when calling Remove() +#ifdef __WXMSW__ +#if !wxCHECK_VERSION(2, 5, 0) +typedef long wxTreeItemIdValue; +#else +typedef void *wxTreeItemIdValue; +#endif +#endif + +//----------------------------------------------------------------------------- +// wxTreeListColumnAttrs +//----------------------------------------------------------------------------- + +enum { + DEFAULT_COL_WIDTH = 100 +}; + +class /*WXDLLEXPORT*/ wxTreeListColumnInfo: public wxObject { + +public: + wxTreeListColumnInfo (const wxString &text = wxEmptyString, + int width = DEFAULT_COL_WIDTH, + int flag = wxALIGN_LEFT, + int image = -1, + bool shown = true, + bool edit = false) { + m_text = text; + m_width = width; + m_flag = flag; + m_image = image; + m_selected_image = -1; + m_shown = shown; + m_edit = edit; + } + + wxTreeListColumnInfo (const wxTreeListColumnInfo& other) { + m_text = other.m_text; + m_width = other.m_width; + m_flag = other.m_flag; + m_image = other.m_image; + m_selected_image = other.m_selected_image; + m_shown = other.m_shown; + m_edit = other.m_edit; + } + + ~wxTreeListColumnInfo() {} + + // get/set + wxString GetText() const { return m_text; } + wxTreeListColumnInfo& SetText (const wxString& text) { m_text = text; return *this; } + + int GetWidth() const { return m_width; } + wxTreeListColumnInfo& SetWidth (int width) { m_width = width; return *this; } + + int GetAlignment() const { return m_flag; } + wxTreeListColumnInfo& SetAlignment (int flag) { m_flag = flag; return *this; } + + int GetImage() const { return m_image; } + wxTreeListColumnInfo& SetImage (int image) { m_image = image; return *this; } + + int GetSelectedImage() const { return m_selected_image; } + wxTreeListColumnInfo& SetSelectedImage (int image) { m_selected_image = image; return *this; } + + bool IsEditable() const { return m_edit; } + wxTreeListColumnInfo& SetEditable (bool edit) + { m_edit = edit; return *this; } + + bool IsShown() const { return m_shown; } + wxTreeListColumnInfo& SetShown(bool shown) { m_shown = shown; return *this; } + +private: + wxString m_text; + int m_width; + int m_flag; + int m_image; + int m_selected_image; + bool m_shown; + bool m_edit; +}; + +//---------------------------------------------------------------------------- +// wxTreeListCtrl - the multicolumn tree control +//---------------------------------------------------------------------------- + +// modes for navigation +const int wxTL_MODE_NAV_FULLTREE = 0x0000; // default +const int wxTL_MODE_NAV_EXPANDED = 0x0001; +const int wxTL_MODE_NAV_VISIBLE = 0x0002; +const int wxTL_MODE_NAV_LEVEL = 0x0004; + +// modes for FindItem +const int wxTL_MODE_FIND_EXACT = 0x0000; // default +const int wxTL_MODE_FIND_PARTIAL = 0x0010; +const int wxTL_MODE_FIND_NOCASE = 0x0020; + +// additional flag for HitTest +const int wxTREE_HITTEST_ONITEMCOLUMN = 0x2000; +extern /*WXDLLEXPORT*/ const wxChar* wxTreeListCtrlNameStr; + + +class /*WXDLLEXPORT*/ wxTreeListCtrl : public wxControl +{ +friend class wxTreeListHeaderWindow; +friend class wxTreeListMainWindow; +friend class wxTreeListItem; +public: + // creation + // -------- + wxTreeListCtrl() + : m_header_win(0), m_main_win(0), m_headerHeight(0) + {} + + wxTreeListCtrl(wxWindow *parent, wxWindowID id = -1, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTR_DEFAULT_STYLE, + const wxValidator &validator = wxDefaultValidator, + const wxString& name = wxTreeListCtrlNameStr ) + : m_header_win(0), m_main_win(0), m_headerHeight(0) + { + Create(parent, id, pos, size, style, validator, name); + } + + virtual ~wxTreeListCtrl() {} + + bool Create(wxWindow *parent, wxWindowID id = -1, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTR_DEFAULT_STYLE, + const wxValidator &validator = wxDefaultValidator, + const wxString& name = wxTreeListCtrlNameStr ); + + void Refresh(bool erase=TRUE, const wxRect* rect=NULL); + void SetFocus(); + // accessors + // --------- + + // get the total number of items in the control + size_t GetCount() const; + + // indent is the number of pixels the children are indented relative to + // the parents position. SetIndent() also redraws the control + // immediately. + unsigned int GetIndent() const; + void SetIndent(unsigned int indent); + + // line spacing is the space above and below the text on each line + unsigned int GetLineSpacing() const; + void SetLineSpacing(unsigned int spacing); + + // image list: these functions allow to associate an image list with + // the control and retrieve it. Note that when assigned with + // SetImageList, the control does _not_ delete + // the associated image list when it's deleted in order to allow image + // lists to be shared between different controls. If you use + // AssignImageList, the control _does_ delete the image list. + // + // The normal image list is for the icons which correspond to the + // normal tree item state (whether it is selected or not). + // Additionally, the application might choose to show a state icon + // which corresponds to an app-defined item state (for example, + // checked/unchecked) which are taken from the state image list. + wxImageList *GetImageList() const; + wxImageList *GetStateImageList() const; + wxImageList *GetButtonsImageList() const; + + void SetImageList(wxImageList *imageList); + void SetStateImageList(wxImageList *imageList); + void SetButtonsImageList(wxImageList *imageList); + void AssignImageList(wxImageList *imageList); + void AssignStateImageList(wxImageList *imageList); + void AssignButtonsImageList(wxImageList *imageList); + + void SetToolTip(const wxString& tip); + void SetToolTip (wxToolTip *tip); + void SetItemToolTip(const wxTreeItemId& item, const wxString &tip); + + // Functions to work with columns + + // adds a column + void AddColumn (const wxString& text, + int width = DEFAULT_COL_WIDTH, + int flag = wxALIGN_LEFT, + int image = -1, + bool shown = true, + bool edit = false) { + AddColumn (wxTreeListColumnInfo (text, width, flag, image, shown, edit)); + } + void AddColumn (const wxTreeListColumnInfo& colInfo); + + // inserts a column before the given one + void InsertColumn (int before, + const wxString& text, + int width = DEFAULT_COL_WIDTH, + int flag = wxALIGN_LEFT, + int image = -1, + bool shown = true, + bool edit = false) { + InsertColumn (before, + wxTreeListColumnInfo (text, width, flag, image, shown, edit)); + } + void InsertColumn (int before, const wxTreeListColumnInfo& colInfo); + + // deletes the given column - does not delete the corresponding column + void RemoveColumn (int column); + + // returns the number of columns in the ctrl + int GetColumnCount() const; + + // tells which column is the "main" one, i.e. the "threaded" one + void SetMainColumn (int column); + int GetMainColumn() const; + + void SetColumn (int column, const wxTreeListColumnInfo& colInfo); + wxTreeListColumnInfo& GetColumn (int column); + const wxTreeListColumnInfo& GetColumn (int column) const; + + void SetColumnText (int column, const wxString& text); + wxString GetColumnText (int column) const; + + void SetColumnWidth (int column, int width); + int GetColumnWidth (int column) const; + + void SetColumnAlignment (int column, int flag); + int GetColumnAlignment (int column) const; + + void SetColumnImage (int column, int image); + int GetColumnImage (int column) const; + + void SetColumnShown (int column, bool shown = true); + bool IsColumnShown (int column) const; + + void SetColumnEditable (int column, bool edit = true); + bool IsColumnEditable (int column) const; + + // Functions to work with items. + + // accessors + // --------- + + // retrieve item's label (of the main column) + wxString GetItemText (const wxTreeItemId& item) const + { return GetItemText (item, GetMainColumn()); } + // retrieves item's label of the given column + wxString GetItemText (const wxTreeItemId& item, int column) const; + + // get one of the images associated with the item (normal by default) + int GetItemImage (const wxTreeItemId& item, + wxTreeItemIcon which = wxTreeItemIcon_Normal) const + { return GetItemImage (item, GetMainColumn(), which); } + int GetItemImage (const wxTreeItemId& item, int column, + wxTreeItemIcon which = wxTreeItemIcon_Normal) const; + + // get the data associated with the item + wxTreeItemData *GetItemData (const wxTreeItemId& item) const; + + bool GetItemBold (const wxTreeItemId& item) const; + wxColour GetItemTextColour (const wxTreeItemId& item) const; + wxColour GetItemBackgroundColour (const wxTreeItemId& item) const; + wxFont GetItemFont (const wxTreeItemId& item) const; + + // modifiers + + // set item's label + void SetItemText (const wxTreeItemId& item, const wxString& text) + { SetItemText (item, GetMainColumn(), text); } + void SetItemText (const wxTreeItemId& item, int column, const wxString& text); + + // get one of the images associated with the item (normal by default) + void SetItemImage (const wxTreeItemId& item, int image, + wxTreeItemIcon which = wxTreeItemIcon_Normal) + { SetItemImage (item, GetMainColumn(), image, which); } + // the which parameter is ignored for all columns but the main one + void SetItemImage (const wxTreeItemId& item, int column, int image, + wxTreeItemIcon which = wxTreeItemIcon_Normal); + + // associate some data with the item + void SetItemData (const wxTreeItemId& item, wxTreeItemData *data); + + // force appearance of [+] button near the item. This is useful to + // allow the user to expand the items which don't have any children now + // - but instead add them only when needed, thus minimizing memory + // usage and loading time. + void SetItemHasChildren(const wxTreeItemId& item, bool has = true); + + // the item will be shown in bold + void SetItemBold (const wxTreeItemId& item, bool bold = true); + + // set the item's text colour + void SetItemTextColour (const wxTreeItemId& item, const wxColour& colour); + + // set the item's background colour + void SetItemBackgroundColour (const wxTreeItemId& item, const wxColour& colour); + + // set the item's font (should be of the same height for all items) + void SetItemFont (const wxTreeItemId& item, const wxFont& font); + + // set the window font + virtual bool SetFont ( const wxFont &font ); + + // set the styles. + void SetWindowStyle (const long styles); + long GetWindowStyle() const; + long GetWindowStyleFlag () const { return GetWindowStyle(); } + + // item status inquiries + // --------------------- + + // is the item visible (it might be outside the view or not expanded)? + bool IsVisible (const wxTreeItemId& item, bool fullRow = false, bool within = true) const; + // does the item has any children? + bool HasChildren (const wxTreeItemId& item) const; + // is the item expanded (only makes sense if HasChildren())? + bool IsExpanded (const wxTreeItemId& item) const; + // is this item currently selected (the same as has focus)? + bool IsSelected (const wxTreeItemId& item) const; + // is item text in bold font? + bool IsBold (const wxTreeItemId& item) const; + // does the layout include space for a button? + + // number of children + // ------------------ + + // if 'recursively' is FALSE, only immediate children count, otherwise + // the returned number is the number of all items in this branch + size_t GetChildrenCount (const wxTreeItemId& item, bool recursively = true); + + // navigation + // ---------- + + // wxTreeItemId.IsOk() will return FALSE if there is no such item + + // get the root tree item + wxTreeItemId GetRootItem() const; + + // get the item currently selected (may return NULL if no selection) + wxTreeItemId GetSelection() const; + + // get the items currently selected, return the number of such item + size_t GetSelections (wxArrayTreeItemIds&) const; + + // get the parent of this item (may return NULL if root) + wxTreeItemId GetItemParent (const wxTreeItemId& item) const; + + // for this enumeration function you must pass in a "cookie" parameter + // which is opaque for the application but is necessary for the library + // to make these functions reentrant (i.e. allow more than one + // enumeration on one and the same object simultaneously). Of course, + // the "cookie" passed to GetFirstChild() and GetNextChild() should be + // the same! + + // get child of this item +#if !wxCHECK_VERSION(2, 5, 0) + wxTreeItemId GetFirstChild(const wxTreeItemId& item, long& cookie) const; + wxTreeItemId GetNextChild(const wxTreeItemId& item, long& cookie) const; + wxTreeItemId GetPrevChild(const wxTreeItemId& item, long& cookie) const; + wxTreeItemId GetLastChild(const wxTreeItemId& item, long& cookie) const; +#else + wxTreeItemId GetFirstChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const; + wxTreeItemId GetNextChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const; + wxTreeItemId GetPrevChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const; + wxTreeItemId GetLastChild(const wxTreeItemId& item, wxTreeItemIdValue& cookie) const; +#endif + + // get sibling of this item + wxTreeItemId GetNextSibling(const wxTreeItemId& item) const; + wxTreeItemId GetPrevSibling(const wxTreeItemId& item) const; + + // get item in the full tree (currently only for internal use) + wxTreeItemId GetNext(const wxTreeItemId& item) const; + wxTreeItemId GetPrev(const wxTreeItemId& item) const; + + // get expanded item, see IsExpanded() + wxTreeItemId GetFirstExpandedItem() const; + wxTreeItemId GetNextExpanded(const wxTreeItemId& item) const; + wxTreeItemId GetPrevExpanded(const wxTreeItemId& item) const; + + // get visible item, see IsVisible() + wxTreeItemId GetFirstVisibleItem( bool fullRow = false) const; + wxTreeItemId GetFirstVisible( bool fullRow = false, bool within = true) const; + wxTreeItemId GetNextVisible (const wxTreeItemId& item, bool fullRow = false, bool within = true) const; + wxTreeItemId GetPrevVisible (const wxTreeItemId& item, bool fullRow = false, bool within = true) const; + wxTreeItemId GetLastVisible ( bool fullRow = false, bool within = true) const; + + // operations + // ---------- + + // add the root node to the tree + wxTreeItemId AddRoot (const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // insert a new item in as the first child of the parent + wxTreeItemId PrependItem (const wxTreeItemId& parent, + const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // insert a new item after a given one + wxTreeItemId InsertItem (const wxTreeItemId& parent, + const wxTreeItemId& idPrevious, + const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // insert a new item before the one with the given index + wxTreeItemId InsertItem (const wxTreeItemId& parent, + size_t index, + const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // insert a new item in as the last child of the parent + wxTreeItemId AppendItem (const wxTreeItemId& parent, + const wxString& text, + int image = -1, int selectedImage = -1, + wxTreeItemData *data = NULL); + + // delete this item (except root) + children and associated data if any + void Delete (const wxTreeItemId& item); + // delete all children (but don't delete the item itself) + void DeleteChildren (const wxTreeItemId& item); + // delete the root and all its children from the tree + void DeleteRoot(); + + // expand this item + void Expand (const wxTreeItemId& item); + // expand this item and all subitems recursively + void ExpandAll (const wxTreeItemId& item); + // collapse the item without removing its children + void Collapse (const wxTreeItemId& item); + // collapse the item and remove all children + void CollapseAndReset(const wxTreeItemId& item); //? TODO ??? + // toggles the current state + void Toggle (const wxTreeItemId& item); + + // remove the selection from currently selected item (if any) + void Unselect(); + void UnselectAll(); + // select this item - return true if selection was allowed (no veto) + bool SelectItem (const wxTreeItemId& item, + const wxTreeItemId& last = (wxTreeItemId*)NULL, + bool unselect_others = true); + // select all items in the expanded tree + void SelectAll(); + // make sure this item is visible (expanding the parent item and/or + // scrolling to this item if necessary) + void EnsureVisible (const wxTreeItemId& item); + // scroll to this item (but don't expand its parent) + void ScrollTo (const wxTreeItemId& item); + + // The first function is more portable (because easier to implement + // on other platforms), but the second one returns some extra info. + wxTreeItemId HitTest (const wxPoint& point) + { int flags; int column; return HitTest (point, flags, column); } + wxTreeItemId HitTest (const wxPoint& point, int& flags) + { int column; return HitTest (point, flags, column); } + wxTreeItemId HitTest (const wxPoint& point, int& flags, int& column); + + // get the bounding rectangle of the item (or of its label only) + bool GetBoundingRect (const wxTreeItemId& item, wxRect& rect, + bool textOnly = false) const; + + // Start editing the item label: this (temporarily) replaces the item + // with a one line edit control. The item will be selected if it hadn't + // been before. + void EditLabel (const wxTreeItemId& item) + { EditLabel (item, GetMainColumn()); } + // edit item's label of the given column + void EditLabel (const wxTreeItemId& item, int column); + + // virtual mode + virtual wxString OnGetItemText( wxTreeItemData* item, long column ) const; + + // sorting + // this function is called to compare 2 items and should return -1, 0 + // or +1 if the first item is less than, equal to or greater than the + // second one. The base class version performs alphabetic comparaison + // of item labels (GetText) + virtual int OnCompareItems (const wxTreeItemId& item1, const wxTreeItemId& item2); + // sort the children of this item using OnCompareItems + // NB: this function is not reentrant and not MT-safe (FIXME)! + void SortChildren(const wxTreeItemId& item); + + // searching + wxTreeItemId FindItem (const wxTreeItemId& item, const wxString& str, int mode = 0); + + // overridden base class virtuals + virtual bool SetBackgroundColour (const wxColour& colour); + virtual bool SetForegroundColour (const wxColour& colour); + + // drop over item + void SetDragItem (const wxTreeItemId& item = (wxTreeItemId*)NULL); + + + virtual wxSize DoGetBestSize() const; + +protected: + // header window, responsible for column visualization and manipulation + wxTreeListHeaderWindow* GetHeaderWindow() const + { return m_header_win; } + wxTreeListHeaderWindow* m_header_win; // future cleanup: make private or remove GetHeaderWindow() + + // main window, the "true" tree ctrl + wxTreeListMainWindow* GetMainWindow() const + { return m_main_win; } + wxTreeListMainWindow* m_main_win; // future cleanup: make private or remove GetMainWindow() + + int GetHeaderHeight() const { return m_headerHeight; } + + void CalculateAndSetHeaderHeight(); + void DoHeaderLayout(); + void OnSize(wxSizeEvent& event); + +private: + int m_headerHeight; + + DECLARE_EVENT_TABLE() + DECLARE_DYNAMIC_CLASS(wxTreeListCtrl) +}; + +#endif // TREELISTCTRL_H +