Source file: /~heha/mb-iwp/Energiemessung/VIPsys3/vipsys3.zip/src/WndTable.cpp

#include "vipsys3.h"
#include "WndTable.h"
#include "analyzer.h"
#include <windowsx.h>
#include <stdio.h>
#include <tchar.h>

/**********************************
 * Zeichenobjekte und -funktionen *
 **********************************/
#define FONTNAME T("Arial")	// Name of standard font used

struct TBL{		// holds GDI resources needed for painting the client area
 HWND w;
 HFONT fntBig,fntSmall;		// fonts
 HPEN penGrid;			// for table styling
 HBITMAP bmDblBuf;		// bitmap and DC, only used for double buffering
 HDC dcDblBuf;
 TBL(HWND x) {w=x;}
 void Make(int,int);		// (Re)Creates fonts and doublebuffer bitmap for given client pixel size
 void Make();			// Same but fetches the client size beforehand
 void Delete();
 ~TBL() {Delete();}		// Releases all GDI resources
 int xx;			// Spaltenbreite (Pixel)
 int yy;			// Zeilenhöhe (Pixel)
 void OutLine(HDC dc, UINT id, double f[], int n);
 void OutLine(HDC dc, UINT id, VAL3 v[4]);
 void OutLine(HDC dc, UINT id, VAL3 v[4], VAL3 a[4], VAL3 p[4]);
 void OutLine(HDC dc, UINT id, VAL2 v[4], char e);
 void HandlePaint(HDC dc,LPARAM options);
private:
 static HFONT MakeFont(int,int,int,LPCTSTR);
 static void DeleteObj(HGDIOBJ&);
};

void TBL::DeleteObj(HGDIOBJ&obj) {
 if (obj && DeleteObject(obj)) obj=0;
}

void TBL::Delete() {
 if (dcDblBuf && DeleteDC(dcDblBuf)) dcDblBuf=0;	// kicks out selected pen, font, and bitmap
 HGDIOBJ *obj=(HGDIOBJ*)this;
 for (int i=0; i<4; i++) DeleteObj(obj[i]);
}

HFONT TBL::MakeFont(int x, int y, int weight, LPCTSTR name) {
 HFONT ret=CreateFont(y,x,0,0,weight,0,0,0,DEFAULT_CHARSET,0,0,0,0,name);
 return ret;
}

// Font re-initialization, called when
// * window size changes
// * measurement unit or prefix changes
// * appearance changes between scope and multimeter mode
void TBL::Make(int x, int y) {
 Delete();
 if (Config.doubleBuf) {
  HDC dc=GetDC(0);
  bmDblBuf=CreateCompatibleBitmap(dc,x,y);
  dcDblBuf=CreateCompatibleDC(dc);
  ReleaseDC(0,dc);
  SelectBitmap(dcDblBuf,bmDblBuf);
 }
 fntBig=MakeFont(x/30,MulDiv(y,2,8),0,FONTNAME);
 xx=x/5;
 yy=30;
 fntSmall=MakeFont(MulDiv(xx,10,256),yy,0,FONTNAME);
}

void TBL::Make() {
 RECT r;
 GetClientRect(w,&r);
 Make(r.right-r.left,r.bottom-r.top);
}


/*******************
 * Hilfsfunktionen *
 *******************/

#if 0
// blockierende Datenabfrage bis "EOF" (Seite 136)
// liefert "Anzahl Bytes", oder negative Zahl für Fehlerkode
int Query(char*d, int dlen) {
 int expectblock=0;
 int len=0;
 static const char s[4]={0x1B,'O','\r','\n'};
 for(;;){
  char r[42];					// maximal 1+6+16*2+1+2 = 42 Bytes
  int l=SendRecv(s,4,r,sizeof(r));
  if (l<0) return l;
  int a;
  int e=string_decode(r,l,&a,d,dlen);
  if (e<0) return e;			// mit Fehler aussteigen
  if (!e && a==0x1000) return len;	// EOF-Block: alles fertig zusammengesetzt
  if (a!=expectblock) return -7;	// Fehler bei "Adresse"
  d+=e; len+=e;
  dlen-=e;				// verbleibender Platz
  expectblock++;
  if (dlen<0) {
   static const char s[4]={0x1B,'A','\r','\n'};	// vorzeitig abbrechen!
   SendRecv(s,4);
   return len;
  }
 }
}

LONG_PTR CALLBACK UnusedProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_INITDIALOG: {
   SetDlgItemTextA(Wnd,10,"\\eA\\r\\n");
  }return TRUE;

  case WM_COMMAND: switch (LOWORD(wParam)) {
   case 1: {
    char t[20];
    GetDlgItemTextA(Wnd,10,t,elemof(t));
    char u[20];
    int l=unescape(u,t);
    char r[44];
    l=SendRecv(u,l,r,sizeof(r));
    char j[280];
    escape(j,r,l);
    SetDlgItemTextA(Wnd,11,j);
//    l=stringdecode(j,r,l);
    if (l>=0) {
     if (j[0] != (l-3)<<1) {l=-6; goto fehler;}
     hexlisting(r,j+1,l-1);
     SetDlgItemTextA(Wnd,12,r);
// Für ESC m 1 CR LF : (Seite 170)
     if (j[2]!=0x10) {
      char z[160];
      numberdecode(z,j+3);
      lstrcatA(z," V");
      SetDlgItemTextA(Wnd,15,z);
      numberdecode(z,j+6);
      lstrcatA(z," A");
      SetDlgItemTextA(Wnd,16,z);
      numberdecode(z,j+9);
      lstrcatA(z," kW");
      SetDlgItemTextA(Wnd,17,z);
      numberdecode(z,j+12,-3);
      lstrcatA(z," °");
      SetDlgItemTextA(Wnd,18,z);
     }
    }else fehler: SetDlgItemTextA(Wnd,12,NULL);
    SetDlgItemInt(Wnd,14,l,TRUE);
   }break;
   case 2: EndDialog(Wnd,wParam); break;
   case 5: {
    char b[800];
    HCURSOR oc=SetCursor(LoadCursor(0,IDC_WAIT));
    DWORD tic=GetTickCount();

    char t[20];
    GetDlgItemTextA(Wnd,10,t,elemof(t));
    char u[20];
    int l=unescape(u,t);
    char r[44];
    l=SendRecv(u,l,r,sizeof(r));
    char j[280];
    escape(j,r,l);
    SetDlgItemTextA(Wnd,11,j);

    l=Query(b,sizeof(b));
    tic=GetTickCount()-tic;
    SetCursor(oc);
    SetDlgItemInt(Wnd,14,l,TRUE);
    wsprintfA(t,"%u ms",tic);
    SetDlgItemTextA(Wnd,11,t);
    if (l>=0) {
     if (l>256) l=256;
     char r[1000];
     hexlisting(r,b,l);
     SetDlgItemTextA(Wnd,12,r);
    }
   }break;
  }
  
 }
 return FALSE;
}
#endif

static void FillBack(HDC dc, int x1, int y1, int x2, int y2) {
 TRIVERTEX vert[]={
  {x1,y1,0xe000,0xe000,0xe000},
  {x1,y2,0xf000,0xf000,0xf000},
  {x2,y2,0xff00,0xff00,0xff00},
  {x2,y1,0xf000,0xf000,0xf000}};
 GRADIENT_TRIANGLE Tri[]={
  {0,1,2},
  {0,2,3}};
 GradientFill(dc,vert,elemof(vert),Tri,elemof(Tri),GRADIENT_FILL_TRIANGLE);
}

struct CELL{
 unsigned colspan,rowspan;
 struct HANDLERSTRUCT{
  unsigned Msg;		// notification message (must not changed by callee!)
  unsigned x,y;		// position of cell in the table
  unsigned flags;	// undefined; item states (e.g. highlighted for mouse hover)
  HDC dc;		// device context for MEASUREITEM and DRAWITEM
  RECT r;		// size (right/bottom) for MEASUREITEM, container for DRAWITEM
 };
 void (CELL::*Handler)(HANDLERSTRUCT*hs);	// a "self-made virtual" function pointer - eases iteration dramatically
// virtual void Measure(HDC,SIZE*)=0;
// virtual void Paint(HDC,RECT*)=0;
 void Iterator(HANDLERSTRUCT*hs) {
  if (hs->Msg==WM_DESTROY) LocalFree(this);
 };
 inline void fill(unsigned cspan, unsigned rspan) {
  colspan=cspan;
  rowspan=rspan;
  Handler=Iterator;
  HANDLERSTRUCT hs;
  hs.Msg=WM_CREATE;
  (this->*Handler)(&hs);
 }
 static CELL*init(unsigned cspan=1,unsigned rspan=1,size_t sizeofobject=sizeof(CELL)) {
  CELL*r=(CELL*)LocalAlloc(LPTR,sizeofobject);
  if (r) r->fill(cspan,rspan);
  return r;
 };
};

struct SPAN{		// handles COLSPAN and ROWSPAN
 unsigned n;		// number of columns/rows of the table
 unsigned changed;	// changed (i.e. expanded) item?
 unsigned*w;		// triangular widths array (TODO: The first <n> items contain widths for each single column)
 static unsigned BF(unsigned x) {return x*(x+1)/2;};	// Binomic Formula
 void init(unsigned x) {w=(unsigned*)LocalAlloc(LPTR,BF(n=x)*sizeof(unsigned));};
 void done() const{LocalFree(w);};
 unsigned&PW(unsigned x, unsigned spanx=1) const{
// possible bugs:
  if (x>=n) x=n-1;
  if (x+spanx>n) spanx=n-x;
  if (!spanx) spanx=1;
// calculate index
  return w[(BF(n+1-spanx)-1)+x];
 };
 void setmax(unsigned x, unsigned spanx, unsigned w) {
  unsigned&pw=PW(x,spanx);
  if (pw<w) {pw=w; changed++;}
 };
 unsigned get(unsigned x, unsigned spanx=1) const{return PW(x,spanx);};
 inline operator[] (unsigned x) const {return get(x);};

// Except that each terminal child has correct size, expands all spanning parents up to start+span
 unsigned expandspans(unsigned start, unsigned span) {
  unsigned wmax=0;
  for (unsigned span2=1; span2<span; span2++) {
   unsigned w=expandspans(start,span2);			// should evaluate to the same value!!
             +expandspans(start+span2,span-span2);
   if (wmax<w) wmax=w;
  }
  if (wmax) setmax(start,span,wmax);
  return get(start,span);
 };

// Except that each parent has a correct size, it expands size of terminal childs
 void expandchilds(unsigned start, unsigned span, unsigned w) {
  setmax(start,span,w);
  if (span>1) {
   unsigned span2=span/2;	// roughly the middle
   unsigned wl,wr;		// widths left and right
   wl=get(start,span2);
   wr=get(start+span2,span-span2);
   wl=MulDiv(wl,w,wl+wr);	// new left width
   expandchilds(start,span2,wl);
   expandspans(start,span2);
   expandchilds(start+span2,span-span2,w-wl);
   expandspans(start+span2,span-span2);
  }
 };

 void adjust() {	// This routine expands all spans and partial widths, if necessary
  unsigned savechanged;
  do{
   savechanged=changed;
   expandspans(0,n);
   expandchilds(0,n,get(0,n));
  }while (savechanged!=changed);
 }
};

#pragma warning(disable:4200)
// simple table with fixed sizes (cols, rows) at initialization,
// however, cells are class pointers, NULL pointers are empty cells
struct TABLE{
 SPAN cols;	// columns (x extent and widths array)
 SPAN rows;	// rows (y extent and heights array)
 CELL *td[];
// Get the place where the pointer to cell is located (for LocalAlloc, LocalReAlloc etc.)
 CELL **pcell(unsigned x, unsigned y) {
#ifdef _DEBUG
  if (x>=cols.n || y>=rows.n) {
   OutputDebugStringA("TABLE::pcell(): index not valid\r\n");
   return NULL;
  }
#endif
  return td+y*rows.n+x;
 };
// Get the pointer to the cell
 CELL *cell(int x, int y) const{
#ifdef _DEBUG
  CELL**p=((TABLE*)this)->pcell(x,y);	// The typecast removes the "const"
  if (!p) return NULL;
  return *p;
#else
  return *pcell(x,y);
#endif
 };
// Initalize a table structure
 static TABLE* init(unsigned x,unsigned y) {
  unsigned c=x*y;
  TABLE*r=(TABLE*)LocalAlloc(LPTR,sizeof(TABLE)+c*sizeof(CELL*));
#ifdef _DEBUG
  if (!r) OutputDebugStringA("TABLE::init(): alloc failed\r\n");
#endif
  if (r) {
//   r->cols.init(x);
//   r->rows.init(y);
  }
  return r;
 };
// Iterate through all cells in x-then-y order and call a function
// with the place where the pointer to cell is located (for LocalAlloc, LocalReAllc)
// void iterate(void(*f)(CELL**,CELL::HANDLERSTRUCT*),CELL::HANDLERSTRUCT*hs) {
//  CELL **c=td;
//  for (hs->y=0; hs->y<rows.n; hs->y++) {
//   for (hs->x=0; hs->x<cols.n; hs->x++) {
//    f(c,hs);
//    c++;
//   }
//  }
// }
// static void callHandler(CELL**c,CELL::HANDLERSTRUCT*hs) {
//  if (*c) (*c)->*Handler(hs);
// };
// Iterate through all cells in x-then-y order and call then Handler member function pointer,
// skipping empty (NULL) cells.
// Spanned (hidden) cells are not skipped, so take care to create spans consistently!
 void iterate(CELL::HANDLERSTRUCT*hs) const{
  CELL *const*c=td;
  for (hs->y=0; hs->y<rows.n; hs->y++) {
   for (hs->x=0; hs->x<cols.n; hs->x++) {
    CELL *p;
    if (p=*c) {
     (p->*p->Handler)(hs);
    }
    c++;
   }
  }
//  ((TABLE*)this)->iterate(callHandler,hs);
 };

// Destructor for the whole table, calls destructors of the cells
 void done() const{
  CELL::HANDLERSTRUCT hs;
  hs.Msg=WM_DESTROY;
  iterate(&hs);
  LocalFree((void*)this);
 };

 unsigned Measure(HDC dc, SIZE*sz) {	// takes size, returns needed size of entire table
  CELL *const*cp=td;
  CELL::HANDLERSTRUCT hs;
  hs.Msg=WM_PAINT;
  hs.flags=0;
  hs.dc=dc;
  for (hs.y=0; hs.y<rows.n; hs.y++) {
   for (hs.x=0; hs.x<cols.n; hs.x++) {
    CELL *c=(*cp);
    if (c) {
     SetRect(&hs.r,0,0,0,0);
     (c->*c->Handler)(&hs);		// cryptic, yeah!
     cols.setmax(hs.x,c->colspan,hs.r.right-hs.r.left);
     rows.setmax(hs.y,c->rowspan,hs.r.bottom-hs.r.top);
    }
    cp++;
   }
  }
  cols.adjust();
  rows.adjust();
  sz->cx=cols.get(0,cols.n);
  sz->cy=rows.get(0,rows.n);
  return cols.changed|rows.changed;
 };

 void Paint(HDC dc, const RECT*rc=0) const{
  CELL *const*cp=td;
  CELL::HANDLERSTRUCT hs;
  hs.Msg=WM_PAINT;
  hs.flags=0;
  hs.dc=dc;
  int yy=0;				// running y pixels
  for (hs.y=0; hs.y<rows.n; hs.y++) {
   int xx=0;				// running x pixels
   for (hs.x=0; hs.x<cols.n; hs.x++) {
    CELL *c=(*cp);
    if (c) {				// cells hidden by colspan or rowspan MUST be NULL
     SetRect(&hs.r,xx,yy,xx+cols.get(hs.x,c->colspan),yy+rows.get(hs.y,c->rowspan));
     RECT r;
     if (!rc || IntersectRect(&r,&hs.r,rc)) (c->*c->Handler)(&hs);
    }
    xx+=cols[hs.x];
    cp++;
   }
   yy+=rows[hs.y];
  }
 };
};

void TestTable() {
 TABLE *T=TABLE::init(2,2);
 unsigned x,y;
 CELL **C;
 for (y=0; y<T->rows.n; y++) {
  for (x=0; x<T->cols.n; x++) {
   *C=CELL::init();
   C++;
  }
 }
 T->done();
}

struct TH{		// table header cell item
 UINT sid;		// string resource index for text display
 LONG_PTR par;		// one free parameter for sprintf()
 char dde2[4];		// second part of DDE name (e.g. "-1")
};

struct TD{		// table body cell item
 double val,min,max;	// data for DDE
 char str[10];		// text for display, not localized (contains "." decimal point)
 char unitprefix;	// 0-based exponent of 10
 char colspan;		// colspan, 0 = unused column!
};

union COL{
 TH th;			// first row
 TD td;			// non-first rows
};

struct TR{
 UINT sid;		// string resource index for descriptive text display
 LONG_PTR par;		// one free parameter for sprintf()
 UINT nid;		// string resource index for name display
 char name[7];		// alternative name, as UTF-8 (unused)
 char ndeco;		// name decoration flags: average, peak
 char dde1[8];		// first part of DDE name (e.g. "Up" for U-peak), 7-bit ASCII only
 BYTE ncol;		// number of columns following, 0 = sub-divider ("Tariff Band")
 char unitcode;		// the unit code for common units, appended to each(?) <td> item
 char unit[8];		// the unit for uncommon units, as UTF-8 (unused)
 COL col[];
};

static PWSTR GenUnitW(PWSTR d, char unitprefix, char unitcode, char f) {
 return d;
}

// obige Tabellenstruktur ausgeben
static void OutTable(HDC dc, TR*tr, int n){
// 0. Check whether table is valid
 if (!tr || n<2) return;
 int xx=tr[0].ncol;	// the header dictates the number of possible columns
 if (!xx) return;	// no data column
// 1. Check for needed column widths
 SPAN w;
 w.init(xx+2);
 for (int y=0; y<n; y++) {
  TR*tr=tr+y;		// TODO: Not so easy!
  WCHAR t[32],s[32];
  MyLoadStringW(tr->sid,t,elemof(t));
  int l=wnsprintfW(s,elemof(s),t,tr->par);
  SIZE size;
  GetTextExtentPoint32W(dc,s,l,&size);
  w.setmax(0,1,size.cx);	// build maximum
  l=MyLoadStringW(tr->sid,t,elemof(t));
  GetTextExtentPoint32W(dc,t,l,&size);
  w.setmax(1,1,size.cx);	// build maximum
  for (int x=0; x<tr->ncol; x++) {
   if (x==xx) break;	// it's a bug in given table structure
   COL*col=tr->col+x;
   if (y) {
    MyLoadStringW(col->th.sid,t,elemof(t));
    l=wnsprintfW(s,elemof(s),t,col->th.par);
   }else{
    l=wnsprintfW(s,elemof(s),L"%S",col->td.str);
    l=GenUnitW(s+l,col->td.unitprefix,tr->unitcode,1)-s;
   }
   GetTextExtentPoint32W(dc,s,l,&size);
   w.setmax(2+x,col->td.colspan,size.cx);	// build maximum
  }
 }
// 2. Emit the table
}

void TBL::OutLine(HDC dc, UINT id, double f[], int n) {
 POINT p;		// origin
 MoveToEx(dc,0,0,&p);
 WCHAR s[128];
 FillBack(dc,p.x,p.y,p.x+xx,p.y+yy);
 int l=MyLoadStringW(id,s,elemof(s));
 TextOutW(dc,p.x+1,p.y,s,l);
 if (n==12) {
  for (int i=0,k=0; i<4; i++) {
   l=toStringW(f[k++],s,elemof(s));
   StrCpyNW(s+l,L" / ",elemof(s)-l); l+=3;
   l+=toStringW(f[k++],s+l,elemof(s)-l);
   StrCpyNW(s+l,L" / ",elemof(s)-l); l+=3;
   l+=toStringW(f[k++],s+l,elemof(s)-l);
   FillBack(dc,p.x+xx*(i+1),p.y,p.x+xx*(i+2),p.y+yy);
   TextOutW(dc,p.x+xx*(i+1)+1,p.y,s,l);
  }
 }else{
  for (int i=0; i<4; i++) {
   l=toStringW(f[i],s,elemof(s));
   FillBack(dc,p.x+xx*(i+1),p.y,p.x+xx*(i+2),p.y+yy);
   TextOutW(dc,p.x+xx*(i+1)+1,p.y,s,l);
  }
 }
 MoveToEx(dc,p.x,p.y+yy,NULL);	// next line
}

void TBL::OutLine(HDC dc, UINT id, VAL3 v[4]) {
 double f[4];
 for (int i=0; i<4; i++) f[i]=v[i].toFloat();
 OutLine(dc,id,f,4);
}

void TBL::OutLine(HDC dc, UINT id, VAL3 v[4], VAL3 a[4], VAL3 p[4]) {
 double f[12];
 for (int i=0,k=0; i<4; i++) {
  f[k++]=v[i].toFloat();
  f[k++]=a[i].toFloat();
  f[k++]=p[i].toFloat();
 }
 OutLine(dc,id,f,12);
}

void TBL::OutLine(HDC dc, UINT id, VAL2 v[4], char e) {
 double f[4];
 for (int i=0; i<4; i++) f[i]=v[i].toFloat(e);
 OutLine(dc,id,f,4);
}

static STRING_ESCM escm_data;

void TBL::HandlePaint(HDC dc,LPARAM options) {
 RECT r;
 GetClientRect(w,&r);
 if (options&PRF_ERASEBKGND) {
  HBRUSH brBack=GetSysColorBrush(COLOR_WINDOW);
  SelectBrush(dc,brBack);
  PatBlt(dc,0,0,r.right,r.bottom,PATCOPY);
 }
// if (w.hMutex && WaitForSingleObject(w.hMutex,200)) return;	// timeout - should never occur, SetData() is fast
// Value (with unit) sent by multimeter
// int slen;
// SIZE size;
// WCHAR buf[32];
 HFONT ofont=SelectFont(dc,fntSmall);
 SetBkMode(dc,TRANSPARENT);
 OutLine(dc,19,escm_data.u);
 OutLine(dc,18,escm_data.i);
 OutLine(dc,20,escm_data.p,escm_data.pa,escm_data.pp);
 OutLine(dc,21,escm_data.s,escm_data.sa,escm_data.sp);
 OutLine(dc,22,escm_data.q,escm_data.qa,escm_data.qp);
 OutLine(dc,24,escm_data.c,-3);
// if (w.hMutex) ReleaseMutex(w.hMutex);
 SelectFont(dc,ofont);
}

/********************
 * Window procedure *
 ********************/

static INT_PTR CALLBACK WndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
 TBL*tbl=(TBL*)GetWindowLongPtr(Wnd,0);

 switch (Msg) {
  case WM_NCCREATE: {
   tbl=new TBL(Wnd);
   SetWindowLongPtr(Wnd,0,(INT_PTR)tbl);
  }break;

  case WM_CREATE: {
   TestTable();
  }break;

  case WM_USER+22: {	// lParam = bits indicating what portion of information has changed
   if (lParam) {
    if (lParam&0xA8888) tbl->Make();
    if (tbl->dcDblBuf) tbl->HandlePaint(tbl->dcDblBuf,PRF_ERASEBKGND);
    InvalidateRect(Wnd,NULL,TRUE);
   }
  }break;

  case WM_SIZE: {
   tbl->Make(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
   if (tbl->dcDblBuf) tbl->HandlePaint(tbl->dcDblBuf,PRF_ERASEBKGND);
  }break;

  case WM_PAINT: {
   PAINTSTRUCT ps;
   BeginPaint(Wnd,&ps);
   SendMessage(Wnd,WM_PRINTCLIENT,(WPARAM)ps.hdc,PRF_ERASEBKGND);
   EndPaint(Wnd,&ps);
  }return 0;

  case WM_PRINTCLIENT: {
   if (tbl->dcDblBuf) {
    RECT r;
    GetClientRect(Wnd,&r);
    BitBlt((HDC)wParam,0,0,r.right,r.bottom,tbl->dcDblBuf,0,0,SRCCOPY);
   }else tbl->HandlePaint((HDC)wParam,lParam);
  }break;
  
  case WM_NCDESTROY: {
   delete tbl;
  }break;

 }
 return DefWindowProc(Wnd,Msg,wParam,lParam);
}

ATOM RegisterWndTable() {
 WNDCLASS wc={
  CS_BYTEALIGNWINDOW|CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW,
  (WNDPROC)WndProc,
  0,
  sizeof(void*),
  ghInst,
  LoadIcon(ghInst,MAKEINTRESOURCE(1)),
  LoadCursor(0,IDC_ARROW),
  0,
  0,
  TABLE_CLASS};

 return RegisterClass(&wc);
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded