Newsgroup: it.comp.lang.c++
Titolo: Aiuto per borland custom controls

Infatti... ho scoperto la classe TGlyphButton nella nottata (insonne) che
ho passato
dopo il post alla ricerca della soluzione, tale classe incapsula appunto il
concetto
di pulsante con bitmap... Ho anche scoperto che il resource editor non e` in
grado di
realizzare visivamente un pulsante con bitmap, la bitmap bisogna metterla a
mano
chiamando un metodo di TGlyphButton.
In definitiva ho rinunciato ai controlli borland, mi realizzo le 4-5 bitmap
che mi servono
ed aggiungo non piu` di 15 righe di codice per creare una classe
TCustomDialog
per utilizzare i nuovi controlli standard senza dover rinunciare alla
realizzazione
visuale dei miei dialoghi.
Paolo
----------------------------------------------------------------------------
--------------
Ciao,
 non ho replicato subito alla tua ultima email perche' avevo un po' da fare
:-)
Non so se ti conviene creare un dialogo personalizzato per poter usare la
classe TGlyphButton.
Io mi sono organizzato in questo modo:
Ho creato un file .RC (piu' .RH) contenente le bitmap (actived e grayed)
corrispondenti ai pulsanti che uso di piu'.
Ho fatto una piccola funzione che ti scrivo qui:
// -----------------------------------------------------
void set_glyph(TButton *btn,int id, int idgrayed)
{
  TGlyphButton* gl = dynamic_cast<TGlyphButton*>(btn);
  if(gl) {
    if(id)
      gl->SetGlyph(id);
    if(idgrayed)
      gl->SetGlyph(idgrayed, 0, TGlyphButton::gtDisabled);
    }
}
// -----------------------------------------------------
Se il pulsante va instanziato, magari usando il ClassExpert, sostituisco,
_SOLO_nella new, la TButton con TGlyphButton. Se invece non serve il
riferimento, si puo' usare, nel costruttore del dialogo, un puntatore
temporaneo, che si passa poi, comunque, alla set_glyph().
Se vuoi gestire la attivazione e disattivazione (probabilmente nella
IdleAction), puoi anche non esporre il puntatore, puoi usare il seguente
codice:
// ----------------------------------------------------
  char t[10];
  Cod->GetText(t,sizeof(t));
  HWND hwnd = GetDlgItem(IDC_BUTTON_M_SAVE);
  if(hwnd)
    ::EnableWindow(hwnd,*t != 0);
......................
attiva o disattiva il pulsante [Salva] a seconda se nel campo codice c'e'
del testo o meno
// -------------------------------------------
Ciao
Nazzareno
--
Posted from [195.223.93.3]
via Mailgate.ORG Server - 
http://www.mailgate.org


[omissis]
Grazie per la dritta, alcune cose penso di prenderle in considerazione, anche perche`
ci sono aspetti cui non avevo pensato.
Sono tuttavia convinto della mia scelta anche perche` e` comunque spesso
necessario derivare da TDialog per poter impostare le proprieta` dei controlli
presenti nel dialogo (validatori di campi di edit, codice che recuper informazioni
da visualizzare, impostare il buffer di scambio...) che normalmente viene creato con
il resource workshop. La mia soluzione mi consente di utilizzare ancora questa
possibilita` derivando da TCustomDialog anzicche` TDialog.
C'e` invece un altro problema che non sono riuscito a risolvere, mantenere coerenza
con i colori che l'utente usa sulla sua macchina. Con le barre di controllo ci sono
riuscito facilmente, ma lo stesso codice non mi vuole funzionare per i pulsanti, e` come
se il messaggio WM_SYSCOLORCHANGE non arrivasse mai (purtroppo non posso
fidarmi di winsight, sembra che sul mio sistema non funzioni bene), hai mai avuto a che 
fare con questo problema?
Paolo


Se intendi i pulsanti normali, non occorre fare niente, si aggiornano da soli.
Se, invece, ti riferisci alla bitmap che poni sul pulsante appartenente alla classe
TGliphButton, ti riscrivo il codice che ti avevo inviato, modificato, che permette di
mantenere il colore di background della bitmap coerente con il colore dei pulsanti.
// ----------------------------------------------------------------------
void set_glyph(HINSTANCE hinstance,TButton *btn,TResId id, TResId idgrayed)
{
  TGlyphButton* gl = dynamic_cast<TGlyphButton*>(btn);
  if(gl) {
    if(id) {
        TDib glyph(hinstance, id);
        glyph.MapUIColors( TDib::MapFace);
        TBitmap *btn = new TBitmap(glyph);
        gl->SetGlyph(*btn);
        }
    if(idgrayed) {
       TDib glyph(hinstance, idgrayed);
       glyph.MapUIColors( TDib::MapFace);
       TBitmap *btn = new TBitmap(glyph);
       gl->SetGlyph(btn, TGlyphButton::gtDisabled);
       }
    }
}
// ----------------------------------------------------------------------
Ciao
Nazzareno

Se, invece, ti riferisci alla bitmap che poni sul pulsante appartenente alla classe
Infatti il problema e` proprio quello...
Il codice che mi hai fornito (grazie) e` in effetti lo stesso di quello
da me utilizzato, e va bene quando si disegna inizialmente il pulsante.
Il problema e` che tale codice dovrebbe essere eseguito in risposta ad
un messaggio WM_SYSCOLORCHANGE quando ovviamente la dialog box
e` visibile, solo che non riesco ad intercettare tale messaggio (strano...)...
ci riprovo forse la volta passata ho trascurato qualcosa.
Saluti,
Paolo


Io non ho il problema che dici.
Comunque ti posto il codice della classe con alcuni spezzoni di una implementazione.
Vedi se ci trovi qualcosa di interessante :-)
Ciao
Nazzareno
------------------------------------------------------------------------------------------
-----------
// file header
// ------------------ Chg_Col.h -----------------------------
#ifndef __CHG_SYSCOL__
#define __CHG_SYSCOL__
// se usi degli id gia' predefiniti
#ifndef IDC_BUTTON_ANN
 #define IDC_BUTTON_ANN  301
#endif
#ifndef IDC_BUTTON_SAVE
 #define IDC_BUTTON_SAVE 302
#endif
#ifndef IDC_BUTTON_DONE
 #define IDC_BUTTON_DONE 303
#endif
class TWindow;
class TButton;
class Chg_SysCol : virtual public TWindow
{
 public:
     Chg_SysCol(TWindow *parent);
     virtual void init(int idAnn = IDC_BUTTON_ANN,
              int idSave = IDC_BUTTON_SAVE,
                          int idDone = IDC_BUTTON_DONE);
  static void set_glyph(TButton *btn,int id, int grayed);
    private:
    protected:
     TButton* Ann;
        TButton* Save;
        TButton* Done;
    virtual void make_glyph();
     void EvSysColorChange();
DECLARE_RESPONSE_TABLE(Chg_SysCol);
};
#endif
--------------------------------------------------------------------------
// implementazione
// -------------------- Chg_Col.cpp -------------------------------
#include <owl/pch.h>
# include <owl/glyphbtn.h>
#include "glyphres.rh" // definizioni delle risorse
#include "chg_col.h"
DEFINE_RESPONSE_TABLE1(Chg_SysCol, TWindow)
  EV_WM_SYSCOLORCHANGE,
END_RESPONSE_TABLE;
Chg_SysCol::Chg_SysCol(TWindow *parent) : TWindow(parent),
  Ann(0), Save(0), Done(0)
 { }
void Chg_SysCol::init(int idAnn,int idSave,int idDone)
{
  if(idAnn)
   Ann  = new TGlyphButton(this,idAnn);
  if(idSave)
    Save = new TGlyphButton(this,idSave);
  if(idDone)
    Done = new TGlyphButton(this,idDone);
  make_glyph();
}
void Chg_SysCol::make_glyph()
{
  set_glyph(Ann,IDB_CANC,IDB_CANCG);
  set_glyph(Save,IDB_OK,IDB_OKG);
  set_glyph(Done,IDB_DONE,IDB_DONEG);
}
void Chg_SysCol::set_glyph(TButton *btn,int id, int grayed)
{
  TGlyphButton* gl = dynamic_cast<TGlyphButton*>(btn);
  if(gl) {
    if(id) {
      TDib glyph(::Module->GetInstance(), TResId(id));
      glyph.MapUIColors( TDib::MapFace );
   TBitmap *btn = new TBitmap(glyph);
      gl->SetGlyph(btn);
      }
    if(grayed) {
      TDib glyph(HINSTANCE(::Module->GetInstance()), TResId(grayed));
      glyph.MapUIColors( TDib::MapFace);
   TBitmap *btn = new TBitmap(glyph);
      gl->SetGlyph(btn, TGlyphButton::gtDisabled);
      }
    }
}
void Chg_SysCol::EvSysColorChange()
{
    TWindow::EvSysColorChange();
  // INSERT>> Your code here.
   make_glyph();
}
-----------------------------------------------------------
// esempio di file header che usa la classe
// --------- in test.h --------------------------------
class TD_Test : virtual public Chg_SysCol, public TDialog
{
  public:
................
-----------------------------------------------------------
// implementazione dell'esempio
// --------- in test.cpp --------------------------------
// occhio -> TABLE2
DEFINE_RESPONSE_TABLE2(TD_Test, Chg_SysCol, TDialog)
//{{TD_TestRSP_TBL_BEGIN}}
// altri eventi del dialogo
//{{TD_TestRSP_TBL_END}}
END_RESPONSE_TABLE;
TD_Test::TD_Test(TWindow* parent, TResId resId, TModule* module)
:
     Chg_SysCol(parent), TDialog(parent, resId, module)
{
  // ............
  // INSERT>> Your constructor code here.
     Chg_SysCol::init(IDC_BUTTON_ANN, IDC_BUTTON_SAVE, IDOK);
}

(quelli che dicono che l'ereditarieta' multipla non serve) :-))
In effetti spesso (molto spesso) si puo` evitare, ma a volte rappresenta una
soluzione elegante se non l'unica.
Ok detto questo, mi riprometto di vedere per benino il tuo codice, solo non capisco
perche` l'ededitarieta` multipla da TWindow  e TDialog... TDialog non deriva gia`
da TWindow?
In effetti partendo da questo presupposto io non avevo fatto altro che ridefinire
nella mia classe TCustomDialog la funzione SysColorChange... solo che a
quanto pare non era sufficiente (naturalmente mi ero assicurato che la
funzione fosse inserita nella tabella di risposta della mia classe).
Comunque grazie,
Paolo


Veramente, in questo caso non sarebbe servita l'ereditarieta' multipla, perche', se non
sbaglio, dovrebbe funzionare anche in una TDialog.
Solo che, lavorando col ClassExpert, nella malaugurata sorte che si scassi il file .APX ed
occorresse fare un rescan, meglio evitare di perdere i riferimenti originari creati da
lui.
Piu' che altro e' stato un po' come gettare un'esca per sentire alcune opinioni in
proposito.
Ho, nella mia libreria, alcune interfacce che implementano le loro varie operazioni e
demandano la rappresentazione visiva a classi derivate. Un esempio semplice e' una classe
che rappresenti lo stato corrente di una elaborazione. Ho alcune classi che la usano, ad
esempio per un reindex o per una compressione, e ho delle classi, derivate sia dalla
classe base che da un TWindow o TDialog, che instanzio e passo poi al metodo che le deve
usare.
Senza ereditarieta' multipla, dovrei creare una classe per la rappresentazione visuale (e
che gestisca gli eventi di Windows) e inglobarla in una classe derivata dall'interfaccia.
Questa (quella derivata da una TWindow) dovrebbe avere un puntatore all'altra (duale)
affinche' possa comunicare gli eventi (l'utente puo' scegliere di annullare l'operazione,
occorre ridisegnare qualcosa ecc.). Per comunicare si puo' scegliere di renderla friend
(brutto) o aggiungere una serie di set/get.
E questo e' un esempio semplice, ho un'altra classe che permette un browsing (di sola
lettura o di cancellazione) su tabelle (DBIII + mio indice) e deve gestire molti messaggi.
Sarebbe un po' pesante implementarla in questo modo. Invece, usando l'ereditarieta'
multipla, ne ho addirittura fatte due versioni, una usando la OWL e una usando le API
pure.
Secondo me, la Borland/Inprise non ha avuto una buona idea nell'usare la VCL di Delphi per
il Builder senza aggiungerne la compatibilita' (almeno nei suoi capisaldi) con il C++.
Ho provato a creare una semplice form che, alla pressione di un pulsante, mi instanzi un
oggetto del browsing di cui sopra (naturalmente quello che usa le API).
Se usavo la direttiva STRICT mi dava errori nel link. Ho modificato il codice e, appena si
avvia, da un errore di un accesso errato, dopodiche' sembra funzionare regolarmente.
Probabilmente saro' io che non lo so usare (il Builder), ma con il BorlandC++ non ho di
questi problemi.
Scusa per il mio sfogo e se sono stato un po' prolisso.
Ciao
Nazzareno

questi problemi.
Non uso il Builder anche perche` poco adatto a quello che di solito faccio, e non ho
di conseguenza mai provato VCL. Sinceramente a prima vista non mi ha convinto molto
(VCL), ad ogni modo sicuramente il discorso della compatibilita` non fa una grinza e a me
non va di dover reimplementare le mie classi (o almeno una parte) per poterle adattare
a VCL. 
In generale poi il Builder mi ha dato l'impressione di uno strumento troppo grosso, non 
vorrei che andasse a finire come con 3D Studio Max, che a detta di mio cugino (un
architetto) e` talmente massiccio da essere poco user friendly e addirittura 
controproducente poiche` molto spesso non rende (almeno per quello che puo`
servire in uno studio di architettura).
Paolo

Non uso il Builder [......]
Purtroppo la OWL e' destinata a una piu' o meno breve obsolescenza. Se si vuole stare al
passo con i tempi occorre presentare un'interfaccia grafica che non sia troppo distante
dallo standard attuale. Poi se il programma e' una schifezza non importa, l'importante,
magari, e' che quando passi sopra un pulsante, questo si sollevi e si rivesta di bei
colori :-(
E' un po' come presentare un programma fatto in modalita' carattere, al 99% e' perdente
nei confronti di uno GUI.
Ciao
Nazzareno
// ---------------------------------------------------------
// -------------- file.h -----------------------------------
typedef long Param1;
typedef void* Param2;
typedef unsigned uint;
class send_msg;
template <class T>
class Lista
{
  struct lista
   {
      lista *next;
     T data;
 } *First, *Curr;
  public:
 Lista() : First(0), Curr(0) {}
 ~Lista();
 bool setFirst();
 bool setNext();
 T get() { return Curr->data; }
 void push(T);
 void remove(T);
};
typedef Lista<send_msg*> send_list;
class send_msg
{
  protected:
    send_msg()  { list.push(this); }
    ~send_msg() { list.remove(this); }
   static send_list list;
    struct result {
  Param1 res1;
  Param2 res2;
   };
    static result Result;
    void send(send_msg *sender, uint id, Param1 par1=0, Param2 par2=0, bool broadcast =
false);
    virtual bool process(uint id, Param1 par1, Param2 par2)=0;
  private:
};
// -------------- file.cpp -----------------------------------
#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#include "send_msg.h"
send_list send_msg::list;
template <class T> Lista<T>::~Lista()
{
  Curr = 0;
  if(!First)
    return;
  do {
    lista *t = First->next;
    delete First;
    First = t;
    } while(First);
}
template <class T> bool Lista<T>::setFirst()
{
 Curr = First;
    return Curr != 0;
}
template <class T> bool Lista<T>::setNext()
{
 if(!Curr)
      if(!setFirst())
        return 0;
 Curr = Curr->next;
 return Curr != 0;
}
template <class T> void Lista<T>::push(T f)
{
    lista *t = new lista;
    if(!t)
      return;
    t->data = f;
    t->next = First;
    First = t;
}
template <class T> void Lista<T>::remove(T v)
{
    if(!First)
      return;
    lista *t = First;
    if(First->data == v) {
       First = First->next;
       delete t;
       return;
       }
    Curr = First;
    t = t->next;
    while(t) {
      if(t->data == v) {
        Curr->next = t->next;
        delete t;
        break;
        }
      Curr = t;
      t = t->next;
      }
}
void send_msg::send(send_msg *sender, uint id, Param1 par1, Param2 par2, bool broadcast)
{
  if(!list.setFirst())
 return;
  do {
 send_msg *target = list.get();
 if(target == sender)
  continue;
 if(target->process(id,par1,par2) && !broadcast)
  break;
 } while(list.setNext());
}
#define ID_TEST1 100
#define ID_TEST2 200
#define ID_TEST3 300
class test1: public send_msg
{
  public:
   test1(uint id, int v) : Id(id), V(v) {}
    void send_val() { send(this,ID_TEST2,V); }
    void set(int v) { V = v; }
    int get() { return V; }
 friend ostream& operator <<(ostream &o, const test1 &t) { return o << t.V; }
  protected:
   bool process(uint id, Param1 par1, Param2 /*par2*/)
    {
     if(Id == id) {
         V = par1;
            return true;
            }
        return false;
    }
    int V;
  private:
   uint Id;
};
class test2: public test1
{
  public:
   test2(uint id, int v) : test1(id, v) {}
    void send_val() { send(this,ID_TEST3,V); }
};
class test3: public test1
{
  public:
   test3(uint id, int v) : test1(id, v) {}
    void send_val() { send(this,ID_TEST1,V); }
};
#define ESC 27
int main()
{
  test1 t1(ID_TEST1,1);
  test2 t2(ID_TEST2,2);
  test3 t3(ID_TEST3,3);
  cout <<  "Esc per terminare\n"
     "V per visualizzare tutti i valori\n"
     "1 per trasferire da 1 a 2\n"
     "2 per trasferire da 2 a 3\n"
            "3 per trasferire da 3 a 1\nC per cambiare i dati" << endl;
  cout << "Valori assegnati v1 = " << t1 << " v2 = " << t2 << " v3 = " << t3 << endl;
  for(;;) {
    int car = getch();
    switch(car) {
     case ESC:
         return 0;
        case '1':
           t1.send_val();
           cout << "1 -> 2\n";
           break;
        case '2':
           t2.send_val();
           cout << "2 -> 3\n";
           break;
        case '3':
           t3.send_val();
           cout << "3 -> 1\n";
           break;
        case 'C':
        case 'c':
         t1.set(rand());
         t2.set(rand());
        case 'V':
        case 'v':
           cout << "v1 = " << t1 << " v2 = " << t2 << " v3 = " << t3 << endl;
           break;
        }
   }
}











Ciao
Nazzareno

class send_msg;


typedef Lista<send_msg*> send_list;


send_list send_msg::list;



