|
|
|
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. |
|
|
|
|
Attraverso l’editor delle risorse e’ possibile
editare graficamenta la toolbar, e associarla ad un comando esistente. |
|
|
|
|
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. |
|
|
|
|
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); |
|
} |
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
Per “dragging” si intende il trascinamento o il
disegno o la creazione con il movimento di un oggetto tramite il puntatore
del mouse |
|
|
|
|
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. |
|
|
|
|
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. |
|
|
|
|
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(); |
|
|
|
|
|
|
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 |
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
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(). |
|
|
|
|
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. |
|
|
|
|
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; |
|
} |
|
|
|
|
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; |
|
} |
|
|
|
|
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; |
|
} } |
|
|
|
|
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. |
|
|
|
|
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); |
|
} |
|
} |
|
} |
|
|
|
|
… |
|
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 ); |
|
} |
|
… |
|
|
|
|
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. |
|
|
|
|
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. |
|
|
|
|
Ogni messaggio ha due callback, il comando da
eseguere e il gestore di abilitazioni. |
|
(UI=User Interface). |
|
|
|
|
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); |
|
} |
|
|
|
|
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 = 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. |
|
|
|
|
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); |
|
} |
|
… |
|
|
|
|
… |
|
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); |
|
} |
|
|
|
|
|
|
|
|
void Node::Serialize(CArchive& ar) |
|
{ if(ar.IsStoring()) |
|
{ ar << p.x; |
|
ar << p.y; |
|
} |
|
else |
|
{ ar >> p.x; |
|
ar >> p.y; |
|
} |
|
} |
|
|
|
|
Documento |
|
|
|
OnSave |
|
|
|
In questo caso l’utente deve scrivere il codice
completo (es. fopen) |
|
|
|
|
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 |
|
|
|
|
|
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’. |
|
|
|
|
Messaggio |
|
Associato |
|
|
|
|
|
|
|
|
|
|
|
|
|
Definizione |
|
Tasto |
|
|
|
|
Stringa di definizione nel nome del menu’ |
|
|
|
\t=tab |
|
|
|
|
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 |
|