/********************************
* Projekt: Funkuhr DCF77 *
* Eigenschaftsseite Histogramm *
* Impulslängenauswertung *
********************************/
#include "Funkuhr.h"
typedef struct{
POINT rand; // Platz für Skalenbeschriftung in Pixel
POINT ext; // Ausdehnung des Darstellungsbereiches in Pixel
POINT tic; // Pixel pro 10 ms (x) bzw. pro 256 Zählwerte (y)
}DRAWINFO;
static DRAWINFO di;
static const WORD steps[]={
1,2,5,10,20,50,100,200,500,1000,2000,5000,10000,20000,50000};
// Skale zeichnen (ViewportOrg/Ext gesetzt)
static void DrawSkala(HDC dc) {
HFONT of;
COLORREF oc;
RECT r;
int i,x,y,step_idx;
SIZE TextSize,s2;
WCHAR u[16];
TCHAR s[80];
Line(dc,0,di.ext.y,0,0);
Line(dc,0,0,di.ext.x,0);
SetBkMode(dc,TRANSPARENT);
GetTextExtentPoint32W(dc,L"ms",2,&TextSize);
// Abszissenachse
SetTextAlign(dc,TA_TOP|TA_CENTER);
for (i=0; i<=50; i+=10) {
x=di.tic.x*i; // = alle 40 Pixel
Line(dc,x,-2,x,0);
y=wnsprintfW(u,elemof(u),L"%u",i*10);
GetTextExtentPoint32W(dc,u,y,&s2);
if (x+(s2.cx>>1) // rechter Rand bspw. "500"
< di.ext.x-TextSize.cx) // linke Kante "ms"
TextOutW(dc,x,-2,u,y); // nur ausgeben, wenn keine Überlappung
}
SetTextAlign(dc,TA_TOP|TA_RIGHT);
TextOutW(dc,di.ext.x,-2,L"ms",2);
// Ordinatenachse (automatisch in 1-2-5-Schritten beschriften)
step_idx=0;
while (steps[step_idx]*di.tic.y>>8 < TextSize.cy) step_idx++;
for (i=0;;i+=steps[step_idx]) {
y=di.tic.y*i>>8;
if (y>di.ext.y-(TextSize.cy>>1)) break;
Line(dc,-2,y,0,y);
TextOutW(dc,-3,y+(TextSize.cy>>1),u,wnsprintfW(u,elemof(u),L"%u",i));
}
// Hintergrundschrift
SetTextAlign(dc,TA_TOP|TA_LEFT);
of=SelectFont(dc,GdiObj.fnQuer);
oc=SetTextColor(dc,GetSysColor(COLOR_GRAYTEXT)/*RGB(128,128,128)*/);
SetRect(&r,di.tic.x*Config.ZuLang/5,di.ext.y,di.ext.x,0);
DrawText(dc,s,LoadString(ghInstance,2,s,elemof(s)),&r,
DT_CENTER|DT_NOPREFIX|DT_WORDBREAK);
SetTextColor(dc,oc);
SelectFont(dc,of);
}
// zu Histogramm-Index passenden Pinsel liefern
static HBRUSH br4idx(int i) {
i*=10; // Millisekunden
if (i<Config.ZuKurz) return GdiObj.brFehl;
if (i<Config.Trenn) return GdiObj.brKurz;
if (i<Config.ZuLang<<1) return GdiObj.brLang;
return GdiObj.brFehl;
}
// Einen Balken zeichen (ViewportOrg/Ext gesetzt)
static void DrawBalken(HDC dc, int i) {
int x,y;
HBRUSH obr;
x=di.tic.x*i; // linke Seite
y=di.tic.y*Empfang.Histo[i]>>8; // oben
obr=SelectBrush(dc,br4idx(i));
Rectangle(dc,x,y,x+di.tic.x+1,-1);
SelectBrush(dc,obr);
}
// Teilerstriche zeichnen (ViewportOrg/Ext gesetzt)
static void DrawDividers(HDC dc) {
HPEN open;
int x;
open=SelectPen(dc,GdiObj.peDivi);
x=di.tic.x*Config.ZuKurz/10;
Line(dc,x,di.ext.y,x,0);
x=di.tic.x*Config.Trenn/10;
Line(dc,x,di.ext.y,x,0);
x=di.tic.x*Config.ZuLang/5;
Line(dc,x,di.ext.y,x,0);
SelectPen(dc,open);
}
// Maximalwert im Histogramm ermitteln (zur Skalierung)
static int GetMaxHisto(void) {
int i,max;
for (i=max=0; i<50; i++) { // Maximum ermitteln zum (ggf.) Skalieren
if (max<Empfang.Histo[i]) max=Empfang.Histo[i];
}
return max;
}
// DrawInfo ermitteln; liefert <true> bei Änderung (svw. Neu-Zeichnung)
static bool CheckDrawInfo(HDC dc,const RECT*r) {
bool ret=false;
int max=GetMaxHisto();
SIZE siz;
WCHAR buf[8];
if (max<5) max=5;
GetTextExtentPoint32W(dc,buf,
wnsprintfW(buf,elemof(buf),L"%u",max<<1),&siz); // das Doppelte annehmen
siz.cx+=3; siz.cy+=2;
if (di.rand.x!=siz.cx) {di.rand.x=siz.cx; ret=true;}
if (di.rand.y!=siz.cy) {di.rand.y=siz.cy; ret=true;}
siz.cx=r->right-r->left-di.rand.x;
siz.cy=r->bottom-r->top-di.rand.y;
if (di.ext.x!=siz.cx) {di.ext.x=siz.cx; ret=true;}
if (di.ext.y!=siz.cy) {di.ext.y=siz.cy; ret=true;}
siz.cx=(di.ext.x+1)/51; // = 4 (Pixel Breite pro Balken)
siz.cy=4096; // 16 Pixel pro Vorkommnis (zunächst)
while (max*siz.cy > di.ext.y<<8) siz.cy>>=1; // halbieren bis es passt
if (!siz.cy) siz.cy++; // niemals Null (110905)
if (di.tic.x!=siz.cx) {di.tic.x=siz.cx; ret=true;}
if (di.tic.y!=siz.cy) {di.tic.y=siz.cy; ret=true;}
return ret;
}
// Histogramm: 0/0 am Skalenursprung, Y-Ausdehnung nach oben (mathematisch)
static void PrepareMapMode(HDC dc,const RECT*r) {
SetViewportOrgEx(dc,r->left+di.rand.x,r->bottom-di.rand.y,NULL);
SetMapMode(dc,MM_ANISOTROPIC);
SetViewportExtEx(dc,1,-1,NULL);
}
// Histogramm aktualisieren: nur Wert bei <idx> geändert
static void ActualizeHisto(HWND Wnd,int idx) {
HDC dc;
RECT r;
if (idx==0xFF) CheckDlgButton(Wnd,17,Empfang.Invers);
Wnd=GetDlgItem(Wnd,11); // Handle zum Button
dc=GetDC(Wnd);
GetClientRect(Wnd,&r);
if (CheckDrawInfo(dc,&r) || idx>=50) {
InvalidateRect(Wnd,NULL,TRUE);// alles neu zeichnen lassen
}else{
PrepareMapMode(dc,&r);
SetBkMode(dc,TRANSPARENT);
DrawBalken(dc,idx); // nur geänderten Balken zeichnen
DrawDividers(dc); // Teilerstriche stets darüber
}
ReleaseDC(Wnd,dc);
}
// Histogramm: ungültigen Bereich zeichnen
static void DrawHisto(LPDRAWITEMSTRUCT dis) {
int i;
PrepareMapMode(dis->hDC,&dis->rcItem);
DrawSkala(dis->hDC);
for (i=0; i<50; i++) { // alle Rechtecke zeichnen
DrawBalken(dis->hDC,i);
}
DrawDividers(dis->hDC);
}
INT_PTR CALLBACK HistogrammDlgProc(HWND Wnd,UINT Msg,WPARAM wParam,LPARAM lParam) {
DefHelpProc(Wnd,Msg,lParam,107);
switch (Msg) {
case WM_INITDIALOG: {
AttachUpDown(Wnd,12,112,0,50,Config.ZuKurz);
AttachUpDown(Wnd,13,113,100,200,Config.Trenn);
AttachUpDown(Wnd,14,114,200,400,Config.ZuLang<<1);
}return TRUE;
case WM_NOTIFY: {
LPPSHNOTIFY psn=(LPPSHNOTIFY)lParam;
switch (psn->hdr.code) {
case PSN_SETACTIVE: {
ActualizeHisto(Wnd,0xFF); // Achsen berechnen lassen, Inversion anzeigen, neu zeichnen
}break;
}
}break;
case WM_FUNKRECV: switch ((BYTE)wParam) {
case 1: ActualizeHisto(Wnd,(int)lParam); break; // Impuls-Ende, lParam=Histogramm-Index
}break;
case WM_COMMAND: switch ((DWORD)wParam) {
case MAKELONG(12,EN_CHANGE): {
if (GetUpDownByte(Wnd,112,&Config.ZuKurz)) {
InvalidateRect(GetDlgItem(Wnd,11),NULL,TRUE);
}
}break;
case MAKELONG(13,EN_CHANGE): {
if (GetUpDownByte(Wnd,113,&Config.Trenn)) {
InvalidateRect(GetDlgItem(Wnd,11),NULL,TRUE);
}
}break;
case MAKELONG(14,EN_CHANGE): {
int i;
if (GetUpDownInt(Wnd,114,&i)) {
Config.ZuLang=i>>1;
InvalidateRect(GetDlgItem(Wnd,11),NULL,TRUE);
}
}break;
case MAKELONG(17,BN_CLICKED): {
Empfang.Invers=IsDlgButtonChecked(Wnd,17);
}nobreak;
case MAKELONG(16,BN_CLICKED): {
ClearHisto();
ActualizeHisto(Wnd,0xFF);
}break;
}break;
case WM_DRAWITEM: DrawHisto((LPDRAWITEMSTRUCT)lParam); break;
}
return FALSE;
}
Vorgefundene Kodierung: ANSI (CP1252) | 4
|
|