Note
Struttura
Toolbar (1/2)
La toolbar e’ la zona della finestra dell’applicazione che contiene la lista di bottoni con icone e controlli vari.
Spesso e’ utilizzata per duplicare i comandi dei menu’ per poterli chiamare piu’ velocemente.
Toolbar (2/2)
Attraverso l’editor delle risorse e’ possibile editare graficamenta la toolbar, e associarla ad un comando esistente.
Inserimento archi
Adesso dobbiamo implementare l’inserimento di nuovi archi.
Questa funzionalita’, molto piu’ complessa dell’inserimento dei nodi, necessita di una serie di tecniche accessorie che vedremo in seguito.
Funzione inserimento arco
Per cominciare definiamo nel documento la funzione di inserimento di un nuovo arco, dati gli indici dei due nodi estremi:
inline void add_arc( const int n0, const int n1 )
{
arcs.push_back( Arco(n0,n1) );
UpdateAllViews(0);
}
Modifiche messaggi mouse
void CGrapheditView::OnLButtonDown(UINT nFlags, CPoint pt)
{
CGrapheditDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
switch(state)
{
case S_INSERT_NODE:
pDoc->add_node(pt);
break;
case S_INSERT_ARC:
// Gestione archi
break;
}
}
Dragging
Per “dragging” si intende il trascinamento o il disegno o la creazione con il movimento di un oggetto tramite il puntatore del mouse
Dragging: disegno in “xor”
Un metodo classico per la visualizzazione del dragging e’ l’utilizzo della modalita’ di visualizzazione in xor: questa permette una facile cancellazione dell’oggetto dato che due visualizzazioni successive si annullano a vicenda.
Il metodo della classe CDC
dc->SetROP2(R2_XORPEN);
attiva la modalita’ xor.
Dragging: gestione messaggi
Per implementare il dragging bisogna gestire 3 messaggi:
WM_LBUTTONDOWN
WM_MOUSEMOVE
WM_LBUTTONUP
Il primo abilita il dragging, il secondo lo gestisce e il terzo lo termina.
Dragging: cattura dei messaggi
Tipicamente, durante un’operazione di dragging, e’ necessario catturare tutti i messaggi del mouse (da parte della finestra che gestisce il dragging) tramite le funzioni:
SetCapture();
ReleaseCapture();
Dragging: dati aggiuntivi
Alla classe View aggiungiamo i seguenti dati:
int    start_node; // Nodo di partenza
int    end_node; // Nodo di arrivo
CPoint start_drag; // Punto di partenza
CPoint end_drag; // Punto di arrivo
bool   captured; // Messaggi catturati
Visualizzazione in dragging
void CGrapheditView::drag_arc()
{
CDC * dc = GetDC();
int    old_mode = dc->SetROP2(R2_XORPEN);
CPen * old_pen  = (CPen *)dc->SelectStockObject( WHITE_PEN );
dc->MoveTo( start_drag );
dc->LineTo( end_drag );
  dc->SelectObject( old_pen );
dc->SetROP2(old_mode);
ReleaseDC(dc);
}
Note sulla visualizzazione
E’ buona regola rimettere a posto i settaggi del DC, che potrebbe essere riutilizzato
Si attiva la modalita’ xor per il dragging
E’ necessario settare il colore bianco (tutti i bit a 1) per massimizzare lo xor.
Notare che  si disegna sulla finestra fuori dalla OnDraw, si ottiene il DC dalla funzione GetDC().
Funzioni aggiunti del Doc (1/3)
Per poter inserire un nuovo arco e’ necessario identificare un nodo dato un click sulla finestra, per questo aggiungiamo una funzione al documento che cerca l’indice del nodo piu’ vicino ad un punto dato.
Funzioni aggiunti del Doc (2/3)
La prima funzione calcola la distanza (quadrata) fra punti:
inline int Dist( const CPoint & a, const CPoint & b )
{
int dx = a.x - b.x;
int dy = a.y - b.y;
return dx*dx+dy*dy;
}
Funzioni aggiunti del Doc (3/3)
nt CGrapheditDoc::FindNode( const CPoint & p )
{  const int TOLL = 25;
int mind = -1; int minn = -1;
for(int n=0;n<nodes.size();++n)
{ int d = Dist( p, nodes[n].p );
if( mind==-1 || mind>d )
{ mind = d;
minn = n;
}
}
if(mind<=TOLL) return minn;
else     return -1;
}
Messaggi: ButtonDown
void CGrapheditView::OnLButtonDown(UINT nFlags, CPoint point)
{ start_node = pDoc->FindNode(point);
  if(start_node!=-1)
  { end_node = start_node;
    start_drag = end_drag =
pDoc->nodes[start_node].p;
  drag_arc();
  SetCapture();
  captured = true;
} }
Messaggi: MouseMove
void CGrapheditView::OnMouseMove(UINT nFlags, CPoint point)
{
if(captured)
{
drag_arc(); // Cancellazione
end_drag = point;
drag_arc(); // Visualizzazione
}
}
// Notare che mousemove viene chiamato anche senza nessun click, in questo caso non deve fare niente.
Messaggi: buttonup
void CGrapheditView::OnLButtonUp(UINT nFlags, CPoint p)
{ if(captured)
{ CGrapheditDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
drag_arc(); // Cancellazione
ReleaseCapture();
captured = false;
end_node = pDoc->FindNode(p);
if( end_node!=-1 && end_node!=start_node)
{ pDoc->add_arc(start_node,end_node);
}
}
}
Aggiunte a OnDraw
…
CPen apen(PS_SOLID,3,RGB(0,0,128));
CPen * old_pen = (CPen *)
(pDC->SelectObject(&apen));
CGrapheditDoc::arc_iterator a;
for(a=pDoc->arcs.begin();a!=pDoc->arcs.end();++a)
{
pDC->MoveTo( pDoc->nodes[a->n[0]].p );
pDC->LineTo( pDoc->nodes[a->n[1]].p );
}
…
Note su OnDraw
Ci sono due meccanismi di disegno archi:
Uno durante il dragging, in modalita’ xor, la visualizzazione deve essere essenziale (veloce).
L’altro nella OnDraw, e’ la visualizzazione finale che puo’ essere curata quanto si vuole.
Gestione abilitazioni
Una interfaccia ben sviluppata, abilita e disabilita bottoni e menu’ a seconda della loro disponibilita’, questo facilita l’utilizzo dell’applicazione all’utente.
MFC prevede un sistema di abilitazioni/disabilitazioni su richiesta del sistema; vale a dire che il sistema richiede all’applicazione lo stato di una risorsa.
Abilitazione Messaggi
Ogni messaggio ha due callback, il comando da eseguere e il gestore di abilitazioni.
(UI=User Interface).
Abilitazione creazione archi
Abilitiamo l’inserimento di archi solo se ci sono piu’ di due nodi:
void CGrapheditView::OnUpdateModeInsertArc(
CCmdUI* pCmdUI)
{
CGrapheditDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pCmdUI->Enable(pDoc->nodes.size()>1);
pCmdUI->SetCheck(state == S_INSERT_ARC);
}
Esercizi su graphedit
Abbellire la visualizzazione del grafo (funzione OnDraw), sfruttando le funzioni della classe CDC
Visualizzare accanto ad ogni nodo la sua etichetta (il suo indice).
Visualizzare la direzione degli archi  con una freccia(*).
Serializzazione
Serializzazione = salvataggio, caricamento (ma anche trasmissione via rete).
Si puo’ realizzare a piu’ livello, a seconda della customizzazione richiesta:
CDocument::Serialize
Cdocument::OnSaveDocument
Personalizzando il dialogo di salvataggio.
Serialize (1/3)
void CGrapheditDoc::Serialize(CArchive& ar)
{ if (ar.IsStoring()) // Load or save??
{ ar << int(nodes.size());
ar << int(arcs.size());
}
else
{ int ns,as; ar >> ns; ar >> as;
nodes.resize(ns);
arcs.resize(as);
}
…
Serialize (2/3)
…
vector<Node>::iterator n;
vector<Arco>::iterator a;
for(n=nodes.begin();n!=nodes.end();++n)
n->Serialize(ar);
for(a=arcs.begin();a!=arcs.end();++a)
a->Serialize(ar);
}
Serialize (3/3)
void Node::Serialize(CArchive& ar)
{ if(ar.IsStoring())
{ ar << p.x;
ar << p.y;
}
else
{ ar >> p.x;
ar >> p.y;
}
}
Serializzazione custom
Documento
OnSave
In questo caso l’utente deve scrivere il codice completo (es. fopen)
Abbellimenti
Per lo sviluppo di una buona applicazione e necessario curare i particolari:
Disegnare le icone dell’applicazione e del documento (attenzione: sono in due misure)
Modificare l’about dialog (attenzione al millennium bug)
Disegnare le icone della toolbar
Acceleratori (1/3)
Gli accelerators sono le combinazioni di tasti con cui e’ possibile chiamare un menu’ (o premere un tasto).
Gli accelerators di default sono gia’ definiti
Per definire un nuovo accelerator:
Costruirlo nell’editor delle risorse
Aggiungere la stringa di definizione nel nome del menu’.
Acceleratori (2/3)
Messaggio
Associato
Definizione
Tasto
Acceleratori (3/3)
Stringa di definizione nel nome del menu’
\t=tab
Contatti
Claudio Rocchini
Istituto di Elaborazione dell’informazione
CNR, Area Ricerca di Pisa
Email: rocchini@iei.pi.cnr.it
Web: http://vcg.iei.pi.cnr.it/~rocchini
Tel. 0503152926