Source file: /~heha/hs/Funkuhr.zip/src/DlgHistogramm.c

/********************************
 * 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;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded