Source file: /~heha/enas/Convac-Ätzer/RezEdit.zip/RezEdit.cpp

#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <windowsx.h>
#include <C:\programs\MSVC\w2k3sddk\inc\w2k\shlwapi.h>
#include <setupapi.h>
//#include <devguid.h>
#include <commctrl.h>
#include <stdlib.h>
#include <tchar.h>
#pragma intrinsic(memset)
#include "XYGraph.h"

#define T(x) TEXT(x)
#define elemof(x) (sizeof(x)/sizeof(*(x)))
#define nobreak
typedef unsigned char byte;
typedef unsigned short word;
#pragma warning(disable:4554)	// Klammernsetzung

HINSTANCE ghInstance;
TCHAR MBoxTitle[64];
HWND ghMainWnd,ghTree,ghGraph,ghStatus;

UINT vMBox(int id,UINT typ,va_list va) {
 TCHAR s[1024],t[1024];
 LoadString(0,id,t,elemof(t));
 wvnsprintf(s,elemof(s),t,va);
 return MessageBox(ghMainWnd,s,MBoxTitle,typ);
}

UINT MBox(int id,UINT typ=MB_OK,...) {
 va_list va;
 va_start(va,typ);
 return vMBox(id,typ,va);
}

struct CONFIG{
 short winpos[4];
 char showCmd;
 byte ComNr;	// /dev/cua0 aka COM1 upto /dev/cua255 aka COM256
 void Load();
 void Save();
}Config;

void CONFIG::Load() {
 HKEY k1;
 if (!RegOpenKeyEx(HKEY_CURRENT_USER,T("Software\\h#s"),0,KEY_READ,&k1)) {
  HKEY k2;
  if (!RegOpenKeyEx(k1,T("RezEdit"),0,KEY_READ,&k2)) {
   DWORD len=sizeof Config;
   if (!RegQueryValueEx(k2,T("Config"),0,0,(BYTE*)&Config,&len) && len==sizeof Config) {
    WINDOWPLACEMENT wp;
    wp.length=sizeof wp;
    if (GetWindowPlacement(ghMainWnd,&wp)) {
     for (int i=0; i<4; i++) (&wp.rcNormalPosition.left)[i]=Config.winpos[i];
     //wp.showCmd=Config.showCmd;
     SetWindowPlacement(ghMainWnd,&wp);
    }
   }
  }
 }
}

void CONFIG::Save() {
 HKEY k1;
 if (!RegCreateKeyEx(HKEY_CURRENT_USER,T("Software\\h#s"),0,0,0,KEY_WRITE,0,&k1,0)) {
  TCHAR s[64];
  int len=LoadString(0,0,s,elemof(s));
  if (len) RegSetValueEx(k1,0,0,REG_SZ,(BYTE*)s,(len+1)*sizeof(TCHAR));
  HKEY k2;
  if (!RegCreateKeyEx(k1,T("RezEdit"),0,0,0,KEY_WRITE,0,&k2,0)) {
   len=LoadString(0,1,s,elemof(s));
   if (len) RegSetValueEx(k2,0,0,REG_SZ,(BYTE*)s,(len+1)*sizeof(TCHAR));
   WINDOWPLACEMENT wp;
   wp.length=sizeof wp;
   if (GetWindowPlacement(ghMainWnd,&wp)) {
    for (int i=0; i<4; i++) Config.winpos[i]=short((&wp.rcNormalPosition.left)[i]);
    Config.showCmd=char(wp.showCmd);
    RegSetValueEx(k2,T("Config"),0,REG_BINARY,(BYTE*)&Config,sizeof Config);
   }
  }
 }
}

// Zum testweisen Öffnen
static HANDLE opencom(UINT comnr) {
 TCHAR s[16];
 wnsprintf(s,elemof(s),T("\\\\.\\COM%u"),comnr+1);
 return CreateFile(s,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,0);
}

static HMENU MenuWithComPorts() {
 HMENU m=CreatePopupMenu();
// serielle Schnittstellen (neu) listen (bei jedem WM_DEVICECHANGE bspw. für USB)
 HANDLE devs=SetupDiGetClassDevs(/*(LPGUID)&GUID_DEVCLASS_PORTS*/NULL,NULL,0,DIGCF_PRESENT);
 if (devs!=INVALID_HANDLE_VALUE) {
  SP_DEVINFO_DATA devInfo;
  devInfo.cbSize=sizeof devInfo;
  for (DWORD i=0; SetupDiEnumDeviceInfo(devs,i,&devInfo); i++) {
   HKEY hKey;
   TCHAR s[16];	// trotzdem ein Unicode-String
   *s=0;
   if ((hKey=SetupDiOpenDevRegKey(devs,&devInfo,DICS_FLAG_GLOBAL,0,DIREG_DEV,KEY_READ))
     ==INVALID_HANDLE_VALUE) continue;
   DWORD len=sizeof(s);
   RegQueryValueEx(hKey,T("PortName"),NULL,NULL,(LPBYTE)s,&len);
   RegCloseKey(hKey);
   if (*s=='C') {		// Fehlschläge und LPTx ausfiltern
    DWORD num=StrToInt(s+3)-1;	// nullbasierte Nummer
    UINT flags=MFT_RADIOCHECK;
    if (num==Config.ComNr) flags|=MF_CHECKED;
    HANDLE h=opencom(num);
    if (h==INVALID_HANDLE_VALUE) flags|=MF_GRAYED; else CloseHandle(h);
    AppendMenu(m,flags,0x100+num,s);
   }
  }
  SetupDiDestroyDeviceInfoList(devs);
 }
 return m;
}

char Komma[]=",";

/* Aus convac.h */
struct ANALOG{
 static struct scale_t{
  int gain,offset;
  char nk;
  char unit[7];
 }scale[21];
};

ANALOG::scale_t ANALOG::scale[21]={
//	scale	offset	nk	unit[7]		gemessen 170314
 /*0*/	{1550,	0,	1,	"U/min"},	// A/D-Wert = -18 bei -109 U/min
 /*1*/	{1000,	0,	2,	"V"},
 /*2*/	{1000,	0,	2,	"V"},
 /*3*/	{1000,	0,	2,	"V"},
 /*4*/	{1000,	0,	2,	"V"},
 /*5*/	{1000,	0,	2,	"V"},
 /*6*/	{1000,	0,	2,	"V"},
 /*7*/	{1000,	0,	2,	"V"},
 /*8*/	{1000,	0,	2,	"V"},
 /*9*/	{-2790,	0,	1,	"U/min"},	// -109 U/min bei D/A-Wert = 100
 /*10*/	{1000,	0,	2,	"V"},
 /*11*/	{1000,	0,	2,	"V"},
 /*12*/	{1000,	0,	2,	"V"},
 /*13*/	{1000,	0,	2,	"V"},
 /*14*/	{1000,	0,	2,	"V"},
 /*15*/	{1000,	0,	2,	"V"},
 /*16*/	{1000,	0,	2,	"V"},
 /*17*/	{1000,	0,	2,	"V"},
 /*18*/	{256,	0,	2,	"Hz"},
 /*19*/	{256,	0,	2,	"Hz"},
 /*20*/	{256,	0,	2,	"Hz"}};


char sbuf[64];

static PTSTR floatstr(int m, int nk=0) {
 char*p=0;
 switch (m) {
  case 0x7FFFFFFF: p="+Inf"; break;
  case 0x80000000: p="NaN"; break;
  case 0x80000001: p="-Inf"; break;
 }
 if (p) return p;
 div_t q;
 q.quot=abs(m);
 p=sbuf;
 do{
  q=div(q.quot,10);
  *p++=q.rem+'0';
  if (!--nk) *p++=Komma[0];
 }while(q.quot||nk>=0);
 if (m<0) *p++='-';
 *p=0;
 return _strrev(sbuf);
}

#pragma pack(1)
struct N{
 union{
  byte b;	// Ganzes Byte
  struct{
   byte c:6;	// Anzahl Subknoten
   byte rsv:1;	// Ungenutztes Bit (zum Markieren)
   byte f:1;	// Collapse-Bit
  };
 };
};
struct BEFEHL{
 byte operation;
 byte index;
 PTSTR print(char)	const;
 byte vlen()		const	{return operation&7;}
 const byte*end()	const	{return (byte*)(this+1)+vlen();}	// Datenende
 const BEFEHL*fin()	const	{return (const BEFEHL*)end();}
 int getv()		const;	// Datenwert lesen
 char*printKanal(bool)	const;
 char*printVal(bool)	const;
 void fillDialog(char)	const;
 void drehzahl(int&)	const;
};
struct SCHRIT{
 word time;
 N n;
 const BEFEHL*be0()	const	{return (const BEFEHL*)(this+1);}
 const BEFEHL*operator+(char j) const{const BEFEHL*p=be0();for(;j;--j)p=p->fin();return p;}
 const BEFEHL&operator[](char j) const{return *operator+(j);}
 const BEFEHL*end()	const	{return operator+(n.c);}
 const SCHRIT*fin()	const	{return (const SCHRIT*)end();}
 PTSTR print(char)	const;
};
struct REZEPT{
 N n;
 const SCHRIT*st0()	const	{return (const SCHRIT*)(this+1);}
 const SCHRIT*operator+(char j) const{const SCHRIT*p=st0();for(;j;--j)p=p->fin();return p;}
 const SCHRIT&operator[](char j) const{return *operator+(j);}
 const SCHRIT*end()	const	{return operator+(n.c);}
 const REZEPT*fin()	const	{return (const REZEPT*)end();}
 PTSTR print(char)	const;
};

union{
 byte space[1024];
 struct{
  byte config[8];
  SCHRIT gs;
 };
}eedata;
int fill;	// aktueller Füllstand
#pragma pack()

char*BEFEHL::printKanal(bool dot) const{
 if (vlen()||!dot) floatstr(index);
 else{
  char*s=sbuf;		// oktal-artige Zahl:
  *s++='0'+(index>>5);	// Modulnummer (0..7)
  *s++='.';
  *s++='0'+(index>>3&3);// Bytenummer (0..3)
  *s++='.';
  *s++='0'+(index&7);	// Bitnummer (0..7)
  *s=0;
 }
 return sbuf;
}
PTSTR BEFEHL::printVal(bool withUnit) const{
 int v=getv();
 if (vlen()) {
  const ANALOG::scale_t*k=ANALOG::scale+index;
  v=MulDiv(v,k->gain,256)+k->offset;
  floatstr(v,k->nk);
  if (withUnit) {
   char*p=sbuf+strlen(sbuf);	// Ende der Zahl
   *p++=' ';			// mit Leerzeichen (außer bei °; kommt hier nicht vor)
   p+=wsprintf(p,"%s",k->unit);	// Einheit (editierbar im RAM!) anhängen
  }
 }else floatstr(v);
 return sbuf;
}
int BEFEHL::getv() const{
 byte len=vlen();
 if (!len) return operation>>3&1;	// Bit 3, keine Daten
 len=4-len<<3;
 return *(int*)(this+1)<<len>>len;
}

static TCHAR buf[256];

PTSTR BEFEHL::print(char) const{
 TCHAR*p=buf;
 p+=wsprintf(p,
   operation&0x60?T("Wenn")	// Eingaben
   :vlen()||operation&8?T("Setze")
   :T("Lösche"));
 *p++=' ';
 p+=wsprintf(p,vlen()?T("Wert"):T("Bit"));
 *p++='[';
 p+=wsprintf(p,printKanal(true));
 *p++=' ';
 *p++='=';
 *p++=' ';
 p+=LoadString(0,(vlen()?0x200:0x100)+index,p,128);
 *p++=']';
 *p++=' ';
 p+=wsprintf(p,
   operation&0x60?
   vlen()?
   operation&8?T("größer"):T("kleiner") // lesen analog
   :T("gleich")
   :T("auf"));
 *p++=' ';
 p+=wsprintf(p,printVal(true));
 if (operation&0xE0) p+=wsprintf(p,T(" dann "));
 if (operation&0x60) {
  p+=wsprintf(p,
   operation&0x60==0x20?T("weiter")
   :operation&0x60==0x40?T("Start verhindern")
   :T("Abbruch"));
  if (operation&0x80) p+=wsprintf(p,T(" + "));
 }
 if (operation&0x80) {
  p+=wsprintf(p,T("Log"));
 }
 *p=0;
 return buf;
}

void BEFEHL::drehzahl(int&v) const{
 if (!(operation&0x60)&&vlen()&&index==9)
   v=MulDiv(getv(),ANALOG::scale[0].gain,256)+ANALOG::scale[0].offset;
}

PTSTR SCHRIT::print(char i) const{
 wnsprintf(buf,elemof(buf),i?T("Schritt %d über %s s %d Befehl%s"):T("Sicherheit"),i,floatstr(time,2),n.c,n.c==1?"":"e");
 return buf;
}

PTSTR REZEPT::print(char i) const{
 wnsprintf(buf,elemof(buf),i<0?T("Globale Sicherheit"):T("Rezept %d: %d Schritt%s, ID=%d"),i+1,n.c-1,n.c==2?"":"e",st0()->time);
 return buf;
}

/* Ende des Mikrocontroller-Programms */

void BEFEHL::fillDialog(char numb) const{
 SetWindowLong(ghMainWnd,DWL_USER,(INT_PTR)this);
 CheckRadioButton(ghMainWnd,66,67,operation&0x60?67:66);	// Ausgeben/Prüfen
 CheckRadioButton(ghMainWnd,64,65,vlen()?65:64);		// digital/analog
 HWND w=GetDlgItem(ghMainWnd,101);
 ComboBox_ResetContent(w);
 for (int i=0;i<256;i++) {
  floatstr(i);
  int l=strlen(sbuf);
  if (LoadString(0,i+(vlen()?0x200:0x100),sbuf+l+2,elemof(sbuf)-l-2)) {
   sbuf[l]=':';
   sbuf[l+1]=' ';
  }
  int j;
  if (i==index||sbuf[l]) {
   j=ComboBox_AddString(w,sbuf);
   ComboBox_SetItemData(w,j,i);
  }
  if (i==index) ComboBox_SetCurSel(w,j);
 }
}

void fillHex(WORD addr,byte len) {
 char*p=sbuf+wsprintf(sbuf,"%03X:",addr);
 byte*q=eedata.space+addr;
 do{
  p+=wsprintf(p,"%02X",*q++);
 }while(--len);
 SetDlgItemText(ghMainWnd,110,sbuf);
}

void fillHex(DWORD lParam) {
 WORD addr=LOWORD(lParam);
 char level=HIBYTE(HIWORD(lParam));
 switch (level) {
  case 0: fillHex(addr,sizeof REZEPT); break;
  case 1: fillHex(addr,sizeof SCHRIT); break;
  case 2: fillHex(addr,sizeof BEFEHL+((BEFEHL*)&eedata.space[addr])->vlen()); break;
 }
}

static void ShowFill() {
 TCHAR s[256],t[256];
 LoadString(0,4,t,elemof(t));
 wnsprintf(s,elemof(s),t,fill,sizeof eedata,floatstr(MulDiv(fill,1000,sizeof eedata),1));
 SendMessage(ghStatus,SB_SETTEXT,0,(LPARAM)s);
}

// Schnittstelle lesen/schreiben mit Overlapped und Timeout 200 ms (allgemein)
// Liefert Anzahl übertragener Bytes
static int ReadWrite(HANDLE h, void*p, int l, bool write=true, DWORD ms=200) {
 DWORD dw=0;
 OVERLAPPED o;
 ZeroMemory(&o,sizeof o);
 if (!(write?(BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD,LPOVERLAPPED))WriteFile:ReadFile)
   (h,p,l,&dw,&o)
 && (GetLastError()!=ERROR_IO_PENDING
 || WaitForSingleObject(h,ms)
 || !GetOverlappedResult(h,&o,&dw,FALSE))) {
  CancelIo(h);
 }
 return dw;
}

static bool LoadData(bool write=false) {
 HANDLE hCom=opencom(Config.ComNr);
 if (hCom==INVALID_HANDLE_VALUE) return false;
 BYTE kdo=write?'K':'J';
 int l=0;
 if (ReadWrite(hCom,&kdo,1)==1)		// Hintertür: EEPROM lesen/schreiben
   l=ReadWrite(hCom,&eedata,sizeof eedata,write);
 CloseHandle(hCom);
 return l==sizeof eedata;
}

// TreeView-Statusbits aus N generieren
static DWORD tvstate(N n) {
 DWORD ret=0;
 if (!n.f) ret|=TVIS_EXPANDED;
 if (n.rsv) ret|=TVIS_SELECTED;
 return ret;
}

static void ShowData() {
 TreeView_DeleteAllItems(ghTree);
 TVINSERTSTRUCT tvi;
 tvi.hInsertAfter=TVI_LAST;
 tvi.item.mask=TVIF_TEXT|TVIF_STATE|TVIF_PARAM;
 tvi.item.stateMask=TVIS_EXPANDED|TVIS_SELECTED;
 union{		// Chamäleon-Zeiger
  const void*v;
  const byte*b;
  const REZEPT*re;
  const SCHRIT*st;
  const BEFEHL*be;
 // LPARAM lp;
 }p;
 p.st=&eedata.gs;
// Schleife über Rezepte, beginnend mit Globaler Sicherheit
 char nre=0;	// noch unbekannt
 for (char ire=-1;ire<nre;ire++) {
  tvi.item.state=ire?tvstate(p.re->n):0;
  tvi.item.pszText=p.re->print(ire);
  tvi.item.lParam=MAKELONG(p.b-eedata.space,MAKEWORD(ire,0));	// Listen-Zeiger
  tvi.hParent=TVI_ROOT;
  tvi.hParent=TreeView_InsertItem(ghTree,&tvi);
  char nst=1;
  if (ire>=0) {
   nst=p.re->n.c;
   p.st=p.re->st0();
  }
  int dz=0,t=0;
  SendMessage(ghGraph,GM_ADDPLOT,0,0);
// Schleife über Schritte eines Rezepts
  for (char ist=0;ist<nst;ist++) {
   char nbe=p.st->n.c;
   tvi.item.state=tvstate(p.st->n);
   tvi.item.pszText=p.st->print(ist);
   tvi.item.lParam=MAKELONG(p.b-eedata.space,MAKEWORD(ist,1));
   HTREEITEM save=tvi.hParent;
   tvi.hParent=TreeView_InsertItem(ghTree,&tvi);
   if (ist) t+=p.st->time;
   p.be=p.st->be0();
// Schleife über Befehle eines Schritts
   for (byte ibe=0;ibe<nbe;ibe++) {
    tvi.item.pszText=p.be->print(ibe);
    tvi.item.lParam=MAKELONG(p.b-eedata.space,MAKEWORD(ibe,2));
    TreeView_InsertItem(ghTree,&tvi);
    p.be->drehzahl(dz);
    p.v=p.be->end();
   }
   SendMessage(ghGraph,GM_ADDPOINT,t,dz);
   tvi.hParent=save;
  }
  if (ire<0) nre=*p.b++;	// jetzt erst kommt Anzahl Rezepte
 }
 fill=p.b-eedata.space;
 ShowFill();
}

void onComListChange() {
 MENUITEMINFO mii;
 mii.cbSize=sizeof mii;
 mii.fMask=MIIM_SUBMENU;
 mii.hSubMenu=MenuWithComPorts();
 SetMenuItemInfo(GetSubMenu(GetMenu(ghMainWnd),1),2,TRUE,&mii);
}

BOOL CALLBACK MainWndProc(HWND Wnd, UINT msg, WPARAM wParam, LPARAM lParam) {
 switch (msg) {
  case WM_INITDIALOG: {
   ghMainWnd=Wnd;
   GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,Komma,elemof(Komma));
   ghStatus=CreateStatusWindow(WS_VISIBLE|WS_CHILD|SBARS_SIZEGRIP,0,Wnd,2);
   static const int widths[]={250,-1};
   SendMessage(ghStatus,SB_SETPARTS,elemof(widths),(LPARAM)widths);
   ghTree=GetDlgItem(Wnd,10);
   ghGraph=GetDlgItem(Wnd,11);
   ShowData();
   SetFocus(ghTree);
  }return TRUE;
  case WM_NCCALCSIZE: {
  }break;
  case WM_SIZE: {
   SendMessage(ghStatus,msg,wParam,lParam);	// eigene Size-Prozedur
   RECT r;
   static const int info[]={1,0,1,2,0,0};
   GetEffectiveClientRect(Wnd,&r,(int*)info);
   r.right-=r.left; r.bottom-=r.top;	// in Höhe und Breite wandeln
   static int pw,ph;	// vorherige Breite und Höhe
   if (pw) {
    int dw=r.right-pw;	// Veränderung zu vorher
    int dh=r.bottom-ph;
    if (dw||dh) for (HWND w=GetWindow(Wnd,GW_CHILD);w;w=GetNextSibling(w)) {
     RECT rc;
     GetWindowRect(w,&rc);
     rc.right-=rc.left; rc.bottom-=rc.top;	// in Höhe und Breite wandeln
     ScreenToClient(Wnd,(POINT*)&rc);
     if (w==ghStatus);	// bereits erledigt
     else if (w==ghTree) SetWindowPos(w,0,0,0,rc.right+dw,rc.bottom+dh,SWP_NOMOVE|SWP_NOZORDER);
     else if (w==ghGraph) SetWindowPos(w,0,rc.left+dw,rc.top,rc.right,rc.bottom+dh,SWP_NOZORDER);
     else SetWindowPos(w,0,rc.left+dw,rc.top+dh,0,0,SWP_NOSIZE|SWP_NOZORDER);
    }
   }
   pw=r.right;
   ph=r.bottom;
  }break;
  case WM_CLOSE: Config.Save(); CloseWindow(Wnd); PostQuitMessage(0); break;
  case WM_COMMAND: switch ((unsigned)wParam) {
   case 0x11:	// Datei-Dialoge
   case 0x12: {
    TCHAR filename[260];
    *filename=0;
    TCHAR filter[128];
    filter[LoadString(0,2,filter,elemof(filter)-1)+1]=0;
    OPENFILENAME ofn;
    ZeroMemory(&ofn,sizeof ofn);
    ofn.lStructSize=sizeof ofn;
    ofn.hwndOwner=Wnd;
    ofn.lpstrFile=filename;
    ofn.nMaxFile=elemof(filename);
    ofn.lpstrFilter=filter;
    ofn.lpstrDefExt=T("rez");
    if (wParam==0x11) {
     ofn.Flags=OFN_FILEMUSTEXIST|OFN_HIDEREADONLY;
     if (GetOpenFileName(&ofn)) {
      HANDLE f=CreateFile(filename,GENERIC_READ,0,0,OPEN_EXISTING,0,0);
      if (f!=INVALID_HANDLE_VALUE) {
       DWORD dw;
       ReadFile(f,eedata.space,sizeof eedata,&dw,0);
       CloseHandle(f);
       ShowData();
       if (dw<(unsigned)fill) MBox(5);	// Daten korrupt
      }
     }
    }else{
     ofn.Flags=OFN_PATHMUSTEXIST|OFN_OVERWRITEPROMPT|OFN_HIDEREADONLY;
     if (GetSaveFileName(&ofn)) {
      HANDLE f=CreateFile(filename,GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
      if (f!=INVALID_HANDLE_VALUE) {
       DWORD dw;
       WriteFile(f,eedata.space,fill,&dw,0);
       CloseHandle(f);
      }
     }
    }
   }break;
   case 0x13: SendMessage(Wnd,WM_CLOSE,0,0); break;
   case 0x21: if (LoadData()) ShowData(); else MBox(3,MB_OK,GetLastError()); break;
   case 0x22: LoadData(true); break;
  }
  if (0x100<=wParam && wParam<0x200) {
   MENUITEMINFO mii;
   mii.cbSize=sizeof mii;
   mii.fMask=MIIM_STATE;
   mii.fState=0;
   SetMenuItemInfo(GetMenu(Wnd),0x100+Config.ComNr,FALSE,&mii);
   Config.ComNr=LOBYTE(wParam);
   mii.fState=MFS_CHECKED;
   SetMenuItemInfo(GetMenu(Wnd),wParam,FALSE,&mii);
  }break;
  case WM_NOTIFY: switch ((unsigned)wParam) {
   case 10: NMHDR*nm=(NMHDR*)lParam; switch (nm->code) {
    case TVN_SELCHANGED: {
     NMTREEVIEW*ptv=(NMTREEVIEW*)lParam;
     fillHex(ptv->itemNew.lParam);
     char level=HIBYTE(HIWORD(ptv->itemNew.lParam));
     char numb =LOBYTE(HIWORD(ptv->itemNew.lParam));
     switch (level) {
      case 2: ((BEFEHL*)(eedata.space+LOWORD(ptv->itemNew.lParam)))->fillDialog(numb); break;
     }
    }break;
    case NM_RCLICK: {
     TVHITTESTINFO hti;
     GetCursorPos(&hti.pt);
     ScreenToClient(nm->hwndFrom,&hti.pt);
     HTREEITEM htvi=TreeView_HitTest(nm->hwndFrom,&hti);
     if (htvi) {
      TVITEM tvi;
      tvi.mask=TVIF_SELECTEDIMAGE|TVIF_PARAM;
      tvi.hItem=htvi;
      if (TreeView_GetItem(nm->hwndFrom,&tvi)) {
/*
     HTREEITEM h=TreeView_GetDropHilight();
     if (h) {	//TreeView_SelectItem(nm->hwndFrom,h);
*/
       HMENU m=CreatePopupMenu();
       char level=HIBYTE(HIWORD(tvi.lParam));
       char numb =LOBYTE(HIWORD(tvi.lParam));
       if (level==2) {
        AppendMenu(m,0,0x62,"Kanal ändern");
	AppendMenu(m,0,0x63,"Aktion festlegen");
       }else{
        AppendMenu(m,0,0x64,"nach oben verschieben");
	AppendMenu(m,0,0x65,"nach unten verschieben");
	AppendMenu(m,0,0x66,"duplizieren");
       }
       PTSTR p=0;
       switch (level) {
        case 0: if (level>=0) p="ID ändern"; break;
	case 1: if (level>0) p="Zeit ändern"; break;
	case 2: p="Wert ändern"; break;
       }
       if (p) AppendMenu(m,0,0x61,p);
       AppendMenu(m,0,0x67,"Neu");
       if (p) AppendMenu(m,0,0x68,"Löschen");
       ClientToScreen(nm->hwndFrom,&hti.pt);
       TrackPopupMenu(m,TPM_RIGHTBUTTON|TPM_RETURNCMD,hti.pt.x,hti.pt.y,0,Wnd,0);
       MBox(6);
      }
     }
    }break;
   }break;
  }break;
  case WM_DEVICECHANGE: SetTimer(Wnd,1,1500,NULL); break;
  case WM_TIMER: switch (wParam) {
   case 1: KillTimer(Wnd,wParam); onComListChange(); break;
  }break;
  case WM_ENDSESSION: if (wParam) Config.Save(); break;
 }
 return FALSE;
}

void WinMainCRTStartup() {
 ghInstance=GetModuleHandle(0);
 INITCOMMONCONTROLSEX icc={sizeof icc,ICC_WIN95_CLASSES|ICC_DATE_CLASSES|ICC_USEREX_CLASSES|ICC_COOL_CLASSES};
 InitCommonControlsEx(&icc);
 WNDCLASS wc={
  CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
  DefDlgProc,
  0,DLGWINDOWEXTRA,0,
  LoadIcon(ghInstance,MAKEINTRESOURCE(1)),
  LoadCursor(0,IDC_ARROW),
  0,
  0,
  MAKEINTRESOURCE(1),
 };
 RegisterClass(&wc);
 wc.lpfnWndProc=XYGraph::WndProc;
 wc.cbWndExtra=sizeof(XYGraph*);
 wc.hIcon=0;
 wc.lpszClassName="XYGraph";
 RegisterClass(&wc);
 LoadString(0,1,MBoxTitle,elemof(MBoxTitle));
 ghMainWnd=CreateDialog(0,MAKEINTRESOURCE(1),0,MainWndProc);
 Config.Load();
 onComListChange();
 ShowWindow(ghMainWnd,Config.showCmd?Config.showCmd:SW_SHOWDEFAULT);
 MSG Msg;
 while (GetMessage(&Msg,0,0,0)) {
  if (IsDialogMessage(ghMainWnd,&Msg)) continue;
  TranslateMessage(&Msg);
  DispatchMessage(&Msg);
 }
 ExitProcess(Msg.wParam);
// return Msg.wParam;
}

extern"C" int _fltused;
int _fltused;
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded