#include <windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
#include "risorse.h"
/// ===> COSTANTI <=============================================================
const char kStrNomeProgramma[] = "Etichettatore";
const char kStrInfo[] =
"Etichettatore \nv1.0, agosto 2015 \n di Aldo Carpanelli ";
const char kStrGDIPlusNonIniz[] = "GdiPlus non inizializzato.";
const char kStrImgNonIniz[] = "Impossibile creare l'immagine.";
const char kStrSalvataggioFallito[] = "Etichette non salvate.";
const char kStrErrIndefinito[] = "Errore indefinito";
const char kStrWCasInsensata[] =
"La larghezza attribuita alle caselle è insensata. \n\
Ripristino i valori predefiniti.";
const char kStrHCasInsensata[] =
"L'altezza attribuita alle caselle è insensata. \n\
Ripristino i valori predefiniti.";
const char kStrHFntInsensata[] =
"L'altezza attribuita al font è insensata. \n\
Ripristino i valori predefiniti.";
const char kStrSalvataggioOk[] =
"Etichette salvate nel file \"etichette.png\", \n\
collocato nella stessa cartella ove si trova \n\
il programma.";
const WCHAR kStrNomeFont[] = L"Arial";
const WCHAR kStrNomeFile[] = L"etichette.png";
const REAL kPixelPerMM = 300.0/25.4; // un fattore di conversione
const REAL kWPag = 210.0f; // la classica larghezza dei fogli A4
const REAL kHPag = 297.0f; // la classica altezza dei fogli A4
// lo spessore del tratto per il tracciamento dei crocini
const REAL kWTratto = 0.2f * kPixelPerMM;
// il raggio dei crocini
const REAL kRCrocino = 3.0f * kPixelPerMM;
const REAL kWCasDflt = 50.0f;
const REAL kHCasDflt = kWCasDflt*0.4f;
const REAL kHFntDflt = kHCasDflt*0.5f;
// la quantita' massima dei caratteri per il testo d'una etichetta
const int kDimTCas = 255;
/// ===> VARIABILI GLOBALI <====================================================
/*
Lo so che ogni programmatore che si rispetti aborre le variabili globali, ma...
1. io non sono un programmatore che si rispetti
2. in un programma di dimensioni cosi' ridotte non fanno male
*/
RECT gRAp; // il rettangolo dell'anteprima
Bitmap *gBmp = NULL; // l'immagine
Graphics *gGfxBmp = NULL; // oggetto Graphics per tracciare su gBmp
REAL gWCas, gHCas; // dimensioni delle caselle
int gNOCas, gNVCas; // quantita' delle caselle in orizzontale e in verticale
REAL gHFnt; // dimensioni del font (in millimetri)
char gTCas[kDimTCas+1] = "Testo";
char gNum = 0; // se non 0, aggiunge un numero d'ordine al testo delle caselle
HINSTANCE gHInst;
/// ===> PROTOTIPI DELLE FUNZIONI <=============================================
BOOL CALLBACK DlgMain( HWND hwnd, UINT msg, WPARAM wPar, LPARAM lPar );
bool CreaImmagine( void );
void DismettiImmagine( void );
void InizializzaDialogo( HWND hwnd );
void AcquisisciDati( HWND hwnd );
void ImpostaValoriPredefiniti( void );
void CalcolaQuantitaCaselle( void );
void MostraDati( HWND hwnd );
void TracciaImmagine( Graphics *gfx );
void TracciaAnteprima( HDC hdc );
bool SalvaImmagine( HWND hwnd );
void Info( const char *msg, HWND hwnd = NULL );
void Errore( const char *msg, HWND hwnd = NULL );
UINT InizializzaGdiPlus( void );
void DismettiGdiPlus( void );
/// ===> DEFINIZIONE DELLE FUNZIONI <===========================================
int APIENTRY WinMain(
HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int nShow ) {
int esito = 0;
gHInst = hInst;
if( !InizializzaGdiPlus() ) { Errore( kStrGDIPlusNonIniz ); return 0; }
if( !CreaImmagine() ) { Errore( kStrImgNonIniz ); return 0; }
esito = DialogBox(
hInst, MAKEINTRESOURCE(IDD_DIALOGO), NULL, (DLGPROC)DlgMain );
DismettiImmagine();
DismettiGdiPlus();
return esito;
}
BOOL CALLBACK DlgMain( HWND hwnd, UINT msg, WPARAM wPar, LPARAM lPar ) {
switch(msg) {
case WM_INITDIALOG: {
ImpostaValoriPredefiniti();
InizializzaDialogo( hwnd );
// forza il tracciamento iniziale dell'anteprima
SendMessage( hwnd, WM_COMMAND, MAKELONG(IDOK,1), 0 );
} return TRUE;
case WM_CLOSE: {
EndDialog( hwnd, 0 );
} return TRUE;
case WM_COMMAND: {
switch( LOWORD(wPar) ) {
case IDOK:
AcquisisciDati( hwnd );
TracciaImmagine( gGfxBmp );
InvalidateRect( hwnd, &gRAp, FALSE );
break;
case IDCANCEL:
EndDialog( hwnd, 0 );
break;
case IDC_SALVA:
TracciaImmagine( gGfxBmp );
InvalidateRect( hwnd, &gRAp, FALSE );
SalvaImmagine( hwnd );
break;
case IDC_NUMERA:
gNum = !gNum;
TracciaImmagine( gGfxBmp );
InvalidateRect( hwnd, &gRAp, FALSE );
break;
case IDC_INFO:
Info( kStrInfo, hwnd );
break;
default:
;
}
} return TRUE;
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint( hwnd, &ps );
TracciaAnteprima( hdc );
EndPaint( hwnd, &ps );
} return TRUE;
default:
;
} return FALSE;
}
/*==============================================================================
Crea l'immagine in memoria e l'oggetto Graphics destinato a disegnare su di
essa.
==============================================================================*/
bool CreaImmagine( void ) {
try {
DismettiImmagine(); // non si sa mai...
gBmp = new Bitmap( round(kWPag*kPixelPerMM), round(kHPag*kPixelPerMM) );
if( gBmp->GetLastStatus() == Ok ) {
gBmp->SetResolution( 300.0f, 300.0f );
gGfxBmp = new Graphics( gBmp );
if( gGfxBmp->GetLastStatus() == Ok ) {
gGfxBmp->Clear( 0xFFFFFFFF );
gGfxBmp->SetTextRenderingHint( TextRenderingHintAntiAlias );
gGfxBmp->SetSmoothingMode( SmoothingModeAntiAlias );
return true;
}
else {
DismettiImmagine();
return false;
}
}
else {
DismettiImmagine();
return false;
}
} catch( ... ) {
DismettiImmagine();
return false;
}
}
void DismettiImmagine( void ) {
delete gBmp;
gBmp = NULL;
delete gGfxBmp;
gGfxBmp = NULL;
}
void CalcolaQuantitaCaselle( void ) {
gNOCas = round( kWPag/gWCas );
gNVCas = round( kHPag/gHCas );
// verifica che si stia entro i limiti della pagina
if( gNOCas*gWCas > kWPag ) gNOCas -= 1.0f;
if( gNVCas*gHCas > kHPag ) gNVCas -= 1.0f;
}
void ImpostaValoriPredefiniti( void ) {
gWCas = kWCasDflt;
gHCas = kHCasDflt;
gHFnt = kHFntDflt;
CalcolaQuantitaCaselle();
}
/*==============================================================================
Valutando la posizione del controllo nascosto IDC_RIGA, calcola le dimensioni
dello spazio riservato al margine intorno all'immagine d'anteprima nella
finestra.
==============================================================================*/
int RicavaMargineAnteprima( HWND hwnd ) {
RECT wrCtrl;
GetWindowRect( GetDlgItem(hwnd,IDC_RIGA), &wrCtrl );
ScreenToClient( hwnd, (LPPOINT)&wrCtrl );
return wrCtrl.top;
}
/*==============================================================================
Adatta la finestra alle esigenze dell'anteprima e imposta gRAp.
==============================================================================*/
void RegolaLarghezzaDlg( HWND hwnd, int bordoAp ) {
RECT crDlg, wrRiga, wrDlg;
INT sxAp; // il margine sinistro dell'area dell'anteprima
INT hAp; // l'altezza dell'area dell'anteprima
INT wAp; // la larghezza dell'area dell'anteprima
INT wDlg; // la nuova larghezza della finestra di dialogo
GetClientRect( hwnd, &crDlg );
GetWindowRect( hwnd, &wrDlg );
GetWindowRect( GetDlgItem(hwnd,IDC_RIGA), &wrRiga );
sxAp = wrRiga.right - wrDlg.left;
ScreenToClient( hwnd, (LPPOINT)(&wrRiga) );
ScreenToClient( hwnd, ((LPPOINT)(&wrRiga))+1 );
hAp = crDlg.bottom - 2*bordoAp;
wAp = round(((REAL)hAp)*kWPag/kHPag);
wDlg = sxAp + 2*bordoAp + wAp + ((wrDlg.right-wrDlg.left)-crDlg.right)/2;
SetRect( &gRAp, sxAp, bordoAp, sxAp+wAp, bordoAp+hAp );
MoveWindow( hwnd, wrDlg.left, wrDlg.top, wDlg, wrDlg.bottom-wrDlg.top, TRUE );
}
void InizializzaDialogo( HWND hwnd ) {
// scopre l'ampiezza del margine intorno all'area dell'anteprima
int bordoAp = RicavaMargineAnteprima( hwnd );
RegolaLarghezzaDlg( hwnd, bordoAp ); // adatta la finestra alle esigenze
// dell'anteprima e imposta gRAp
SendDlgItemMessage( hwnd, IDC_WETICH, EM_LIMITTEXT, 3, 0 );
SendDlgItemMessage( hwnd, IDC_HETICH, EM_LIMITTEXT, 3, 0 );
SendDlgItemMessage( hwnd, IDC_TESTOETICH, EM_LIMITTEXT, kDimTCas, 0 );
SendDlgItemMessage( hwnd, IDC_DIMFONT, EM_LIMITTEXT, 3, 0 );
MostraDati( hwnd );
ShowWindow( GetDlgItem(hwnd,IDC_RIGA), SW_HIDE );
}
/*==============================================================================
Esamina i controlli della finestra per rilevare i dati immessi dall'utente.
Immagazzina nelle variabili piu' opportune i dati rilevati.
==============================================================================*/
void AcquisisciDati( HWND hwnd ) {
const int kDimBuff = 255;
char buff[kDimBuff+1] = "";
GetDlgItemText( hwnd, IDC_WETICH, buff, kDimBuff );
gWCas = atof( buff );
if( gWCas > kWPag || gWCas < 0.5f ) {
Errore( kStrWCasInsensata, hwnd );
ImpostaValoriPredefiniti();
}
GetDlgItemText( hwnd, IDC_HETICH, buff, kDimBuff );
gHCas = atof( buff );
if( gHCas > kHPag || gHCas < 0.5f ) {
Errore( kStrHCasInsensata, hwnd );
ImpostaValoriPredefiniti();
}
GetDlgItemText( hwnd, IDC_TESTOETICH, gTCas, kDimTCas );
GetDlgItemText( hwnd, IDC_DIMFONT, buff, kDimBuff );
gHFnt = atof( buff );
if( gHFnt > gHCas || gHFnt < 0.5f ) {
Errore( kStrHFntInsensata, hwnd );
ImpostaValoriPredefiniti();
}
gNum = SendDlgItemMessage( hwnd, IDC_NUMERA, BM_GETSTATE, 0, 0 ) == BST_CHECKED;
CalcolaQuantitaCaselle();
MostraDati( hwnd );
}
/*==============================================================================
"Travasa" i valori delle variabili del programma nei campi della finestra.
==============================================================================*/
void MostraDati( HWND hwnd ) {
SetDlgItemInt( hwnd, IDC_WETICH, gWCas, FALSE );
SetDlgItemInt( hwnd, IDC_HETICH, gHCas, FALSE );
SetDlgItemInt( hwnd, IDC_ETICHXRIGA, gNOCas, FALSE );
SetDlgItemInt( hwnd, IDC_RIGHEXPAGINA, gNVCas, FALSE );
SetDlgItemInt( hwnd, IDC_TOTALEETICH, gNOCas*gNVCas, FALSE );
SetDlgItemText( hwnd, IDC_TESTOETICH, gTCas );
SetDlgItemInt( hwnd, IDC_DIMFONT, gHFnt, FALSE );
if( gNum != 0 )
SendDlgItemMessage( hwnd, IDC_NUMERA, BM_SETCHECK, BST_CHECKED, 0 );
else SendDlgItemMessage( hwnd, IDC_NUMERA, BM_SETCHECK, BST_UNCHECKED, 0 );
}
WCHAR *CharStr2WCharStr( const char *s ) {
static WCHAR wBuff[kDimTCas+1];
int i, l = strlen( s );
for( i=0; i<l; ++i )
wBuff[i] = (unsigned char)(s[i]);
wBuff[i] = 0;
return wBuff;
}
/*==============================================================================
Disegna le etichette nell'immagine gBmp, avvalendosi dei servizi dell'oggetto
di classe Graphics passato come parametro in *gfx.
==============================================================================*/
void TracciaImmagine( Graphics *gfx ) {
Pen penna( 0xFF000000, kWTratto );
if( penna.GetLastStatus() != Ok ) return;
SolidBrush nero( 0xFF000000 );
if( nero.GetLastStatus() != Ok ) return;
Font font( kStrNomeFont, gHFnt*kPixelPerMM, FontStyleBold, UnitPixel );
if( font.GetLastStatus() != Ok ) return;
const REAL wCas = gWCas*kPixelPerMM;
const REAL hCas = gHCas*kPixelPerMM;
const REAL wPag = kWPag*kPixelPerMM;
const REAL hPag = kHPag*kPixelPerMM;
const REAL iX = (wPag-gNOCas*wCas)/2.0f;
const REAL iY = (hPag-gNVCas*hCas)/2.0f;
REAL x1, y1, x2, y2;
int i=0, j=0, l=0, n=0;
gfx->Clear( 0xFFFFFFFF );
StringFormat sf;
sf.SetAlignment( StringAlignmentCenter );
sf.SetLineAlignment( StringAlignmentCenter );
RectF rf( iX, iY, wCas, hCas );
// traccia le parti verticoli dei crocini
for( i=0; i<=gNOCas; ++i ) {
x1 = x2 = iX + ((REAL)i)*wCas;
for( j=0; j<=gNVCas; ++j ) {
y1 = iY + ((REAL)(j*hCas)) - kRCrocino;
y2 = iY + ((REAL)(j*hCas)) + kRCrocino;
gfx->DrawLine( &penna, x1, y1, x2, y2 );
}
}
// traccia le parti orizzontali dei crocini
for( i=0; i<=gNVCas; ++i ) {
y1 = y2 = iY + ((REAL)i)*hCas;
for( j=0; j<=gNOCas; ++j ) {
x1 = iX + ((REAL)(j*wCas)) - kRCrocino;
x2 = iX + ((REAL)(j*wCas)) + kRCrocino;
gfx->DrawLine( &penna, x1, y1, x2, y2 );
}
}
// traccia il testo, se necessario
if( *gTCas != '\0' ) {
if( gNum ) { l = strlen( gTCas ); n = 1; }
for( i=0; i<gNVCas; ++i ) {
rf.Y = iY + ((REAL)i)*hCas;
for( j=0; j<gNOCas; ++j ) {
rf.X = iX + ((REAL)(j*wCas));
if( gNum ) wsprintf( gTCas+l, " (%d)", n++ );
gfx->DrawString( CharStr2WCharStr(gTCas), -1, &font, rf, &sf, &nero );
}
}
if( gNum ) gTCas[l] = '\0';
}
}
/*==============================================================================
Copia nel device context passato tramite il parametro hdc il contenuto della
immagine gBmp.
==============================================================================*/
void TracciaAnteprima( HDC hdc ) {
Graphics gfxWnd( hdc );
if( gfxWnd.GetLastStatus() != Ok ) return;
Pen penna( 0xFF000000, 2 );
RectF rAux( gRAp.left, gRAp.top, gRAp.right-gRAp.left, gRAp.bottom-gRAp.top );
gfxWnd.DrawImage( gBmp, rAux );
rAux.Inflate( 2, 2 );
gfxWnd.DrawRectangle( &penna, rAux );
}
/*==============================================================================
Questa funzione e' stata copiata e incollata dalla documentazione di GDI+.
==============================================================================*/
int GetEncoderClsid( const WCHAR* format, CLSID* pClsid ) {
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if(size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
/*==============================================================================
Registra l'immagine gBmp nel file chiamato kStrNomeFile.
==============================================================================*/
bool SalvaImmagine( HWND hwnd ) {
CLSID cls;
bool classeTrovata;
bool imgSalvata;
classeTrovata = GetEncoderClsid(L"image/png",&cls) > -1;
if( classeTrovata ) {
imgSalvata = gBmp->Save(kStrNomeFile,&cls,NULL) == Ok;
if( imgSalvata )
Info( kStrSalvataggioOk, hwnd );
else Errore( kStrSalvataggioFallito );
return imgSalvata;
}
else {
Errore( kStrSalvataggioFallito );
return classeTrovata;
}
}
/*==============================================================================
Mostra un message box per segnalare all'utente l'informazione descritta nella
stringa passata tramite il parametro msg.
==============================================================================*/
void Info( const char *msg, HWND hwnd ) {
if( msg == NULL ) return;
MessageBox( hwnd, msg, kStrNomeProgramma, MB_OK|MB_ICONINFORMATION );
}
/*==============================================================================
Mostra un message box per avvisare l'utente che s'e' verificato l'errore
descritto nella stringa passata tramite il parametro msg.
==============================================================================*/
void Errore( const char *msg, HWND hwnd ) {
if( msg == NULL ) msg = kStrErrIndefinito;
MessageBox( hwnd, msg, kStrNomeProgramma, MB_OK|MB_ICONERROR );
}
// ===> PER GDIPLUS <===========================================================
ULONG_PTR gdiplusToken = 0;
UINT InizializzaGdiPlus( void ) {
GdiplusStartupInput gdiplusStartupInput;
return GdiplusStartup( &gdiplusToken, &gdiplusStartupInput, NULL ) == Ok;
}
void DismettiGdiPlus( void ) {
GdiplusShutdown( gdiplusToken );
}