Source file: /~heha/basteln/PC/oszi/oszi.zip/src/miniwnd.cpp

//#define OEMRESOURCE
#include "wutils.h"
#include "miniwnd.h"
#include "objekte.h"
#include <stdio.h>
// Ein Wald aus 3 Bäumen, nur Pseudoknoten
NODE Anker[3];
// Anker[0] = TOPMOST, Anker[1] = NORMAL, Anker[2] = HIDDEN
MINIWND *FocusOwner;
GITTER *gitter;
COLORREF MixColor(COLORREF c1, COLORREF c2, int f1=128);

/*********************************************
 ** Quasi-globale Variablen für Hintergrund **
 *********************************************/

COLORREF GridColor;
POINT step;		// Gitternetz-Schrittweite
POINT tick;
POINT Kaestel={10,8};
POINT SubTick={5,5};
POINT bmext;		// (nicht mehr!!) Bitmap-Größe
RECT Rand;		// Gitternetz-Rand (im Client-Bereich)
POINT Mitte;
COLORREF BackColor;
HPEN XorPen;
HBITMAP blt[2]={(HBITMAP)TRUE,(HBITMAP)TRUE};
HBITMAP NullBitmap[2];
POINT lastmouse;	// Fadenkreuz-Position
HDC bltdc[2];
HPEN BackPen;
HPEN GridPen;
HBRUSH BackBrush;
COLORREF AuswahlFarbe;
HBRUSH AuswahlPinsel;	// für <fokussierte>? Elemente
POINT ClientExt;	// auch Größe der beiden Hintergrund-Bitmaps
UINT DispOpt;		// Anzeige- und Gitter-Optionen (=0)

void Gitternetz(HDC dc) {
 int x,y;
 HPEN OldPen;
 OldPen=(HPEN)SelectObject(dc,GridPen);
 if (step.x>1) for(x=Rand.left; x<Rand.right; x+=step.x)	//waagerecht
   Line(dc,x,Rand.top,x,Rand.bottom);
 if (step.y>1) for(y=Rand.top; y<Rand.bottom; y+=step.y)	//senkrecht
   Line(dc,Rand.left,y,Rand.right,y);
 if (DispOpt & DO_TICK) {
  int from,to,j;
  if (tick.x>1) {
   j=(tick.y+1)>>1; if (!j) j++;	// mindestens 1 sichtbares Pixel
   from=Mitte.y-j;
   to=Mitte.y+j+1;	// Line() selbst lässt das letzte Pixel weg
   for (x=Rand.left; x<Rand.right; x+=tick.x)
     if ((x-Mitte.x)%step.x) Line(dc,x,from,x,to);
  }
  if (tick.y>1) {
   j=(tick.x+1)>>1; if (!j) j++;
   from=Mitte.x-j;
   to=Mitte.x+j+1;
   for (y=Rand.top; y<Rand.bottom; y+=tick.y)
     if ((y-Mitte.y)%step.y) Line(dc,from,y,to,y);
  }
 }
 SelectObject(dc,OldPen);
}

void SetDispOpt(UINT ADispOpt, UINT force) {
//Display-Options-Bits setzen, ggf. weitere Datenstrukturen anlegen bzw. entfernen
 force|=DispOpt^ADispOpt;
 if (force&DO_GRID) {
  if (DispOpt&DO_GRID) DeleteObject(GridPen);
  if (ADispOpt&DO_GRID) {
   GridPen=CreatePen(PS_SOLID,0,GridColor);
   if (!GridPen) ADispOpt&=~DO_GRID;
  }
 }
 if (force&DO_TB) {
  if (DispOpt&DO_TB) {
   SelectObject(bltdc[1],NullBitmap[1]);
   DeleteObject(blt[1]);
   DeleteDC(bltdc[1]);
  }
  if (ADispOpt&DO_TB) {
   HDC dc=GetDC(MainWnd);
   bltdc[1]=CreateCompatibleDC(dc);
   blt[1]=CreateCompatibleBitmap(dc,ClientExt.x,ClientExt.y);
   NullBitmap[1]=(HBITMAP)SelectObject(bltdc[1],blt[1]);
   ReleaseDC(MainWnd,dc);
   if (blt[1]) ADispOpt|=DO_TB_INVAL;
   else ADispOpt&=~DO_TB;
  }
 }
 if (force&DO_DB) {
  if (DispOpt&DO_DB) {
   SelectObject(bltdc[0],NullBitmap[0]);
   DeleteObject(blt[0]);
   DeleteDC(bltdc[0]);
  }
  if (ADispOpt&DO_DB) {
   HDC dc=GetDC(MainWnd);
   bltdc[0]=CreateCompatibleDC(dc);
   blt[0]=CreateCompatibleBitmap(dc,ClientExt.x,ClientExt.y);
   NullBitmap[0]=(HBITMAP)SelectObject(bltdc[0],blt[0]);
   ReleaseDC(MainWnd,dc);
   if (blt[0]) ADispOpt|=DO_DB_INVAL;	// Eigentlich Rechteck oder Region,
   else ADispOpt&=~DO_DB;		// aber das wird zu kompliziert...
  }
 }
 if (force&DO_CROSS) {
  if (DispOpt&DO_CROSS) DeleteObject(XorPen);
  if (ADispOpt&DO_CROSS) XorPen=CreatePen(PS_SOLID,0,0x404040L);	// unauffällig
 }
 if (force&DO_BACK) {
  if (DispOpt&DO_BACK) {
   DeletePen(BackPen);
   DeleteBrush(AuswahlPinsel);
   DeleteBrush(BackBrush);
  }
  if (ADispOpt&DO_BACK) {
   BackBrush=CreateSolidBrush(BackColor);
   AuswahlFarbe=MixColor(GetSysColor(COLOR_HIGHLIGHT),BackColor);
   AuswahlPinsel=CreateSolidBrush(AuswahlFarbe);
   BackPen=CreatePen(PS_SOLID,0,BackColor);
  }
 }
 if (force&(DO_LINE|DO_TICK)) Inval(true);
 DispOpt=ADispOpt;
}

void wmSize(int x, int y) {	// Behandlung von WM_SIZE
 ClientExt.x=x;
 ClientExt.y=y;
 tick.x=(ClientExt.x-1)/Kaestel.x/SubTick.x;
 tick.y=(ClientExt.y-1)/Kaestel.y/SubTick.y;
 step.x=tick.x*SubTick.x;
 step.y=tick.y*SubTick.y;
 bmext.x=step.x*Kaestel.x+1;
 bmext.y=step.y*Kaestel.y+1;
 SetRect(&Rand,0,0,bmext.x,bmext.y);
 OffsetRect(&Rand,(ClientExt.x-bmext.x)/2,
		  (ClientExt.y-bmext.y)/2);
 Mitte.x=(Rand.left+Rand.right)>>1;
 Mitte.y=(Rand.top+Rand.bottom)>>1;
 Inval(true);
}

void Fadenkreuz(HDC dc) {
 int OldROP;
 HPEN OldPen;
 if ((unsigned)lastmouse.x<(unsigned)ClientExt.x
 && (unsigned)lastmouse.y<(unsigned)ClientExt.y) {
  OldROP=SetROP2(dc,R2_XORPEN);
  OldPen=SelectPen(dc,XorPen);
  Line(dc,0,lastmouse.y,ClientExt.x,lastmouse.y);
  Line(dc,lastmouse.x,0,lastmouse.x,ClientExt.y);
  SelectPen(dc,OldPen);
  SetROP2(dc,OldROP);
 }
}

void MaleHinterGraf(HDC dc) {
 HBRUSH br=SelectBrush(dc,BackBrush);
 PatBlt(dc,0,0,ClientExt.x,ClientExt.y,PATCOPY);
 SelectBrush(dc,br);
// if (DispOpt&DO_GRID) Gitternetz(dc);
 if (Anker[1].sub) Anker[1].sub->Paint(dc);
}

void MaleGraf(HDC dc) {
 int i;
 for (i=0; i<numkanal; i++) kanal[i].MaleKurve(dc);
// for (i=0; i<numkanal; i++) kanal[i].RelayMsg(M_PAINT,(WPARAM)dc,0);
 if (DispOpt&DO_CROSS) Fadenkreuz(dc);
// XAdjust_Paint(dc,&xadjust);
 if (Anker[0].sub) Anker[0].sub->Paint(dc);
}

void BltAlles(HDC dc) {
// Hintergrundraster und -Elemente einkopieren, ggf. malen lassen
 if (DispOpt&DO_TB) {
  if (DispOpt&DO_TB_INVAL) {	// TB_INVAL muss auch immer DB_INVAL hinter sich ziehen!
   MaleHinterGraf(bltdc[1]);	// hintenliegende Elemente
   DispOpt&=~DO_TB_INVAL;
  }
  BitBlt(dc,0,0,ClientExt.x,ClientExt.y,bltdc[1],0,0,SRCCOPY);
 }else MaleHinterGraf(dc);
// Vordergrund-Graf und -Elemente darüber malen lassen
 MaleGraf(dc);			// Kurvenzug und vornliegende Elemente
}

void _stdcall ShowPopupMenu(HMENU m, int x, int y) {
 ClientToScreen(MainWnd,(LPPOINT)&x);
 TrackPopupMenu(m,TPM_LEFTALIGN|TPM_RIGHTBUTTON,x,y,0,MainWnd,NULL);
}

/*************************************************************
 ** Knoten und Mini-Fenster (als Basis für weitere Fenster) **
 *************************************************************/

KNOTEN::~KNOTEN() {
 while(sub) delete sub; remove();
}
void KNOTEN::SetParent(NODE*p) {
// Knoten einfügen, als erstes Kind von <p>!
// Verändert einen evtl. vorhandenen Unterbaum nicht!!
 remove();			// Falls noch eingehängt
 next=p->sub; p->sub=this;	// Neues erstes Kind
 if (next) next->prev=this;	// Neuer Vorgänger für Nachfolger
 prev=NULL;			// Kein eigener Vorgänger
 parent=p;
}
void KNOTEN::SetSibling(KNOTEN*b) {
// Knoten einfügen, ENTWEDER als erstes Kind von <p> ODER vor <b>!
// Verändert einen evtl. vorhandenen Unterbaum nicht!!
 remove();			// falls noch eingehängt
 next=b;
 prev=b->prev; b->prev=this;
 if (prev) prev->next=this;
 parent=b->parent;		// gleiches Elter
}
void KNOTEN::remove() {
// Knoten aushängen; Kinder bleiben hängen!
 if (!parent) return;		// <>0 wenn überhaupt eingehängt
 if (prev) {prev->next=next; prev=NULL;}
 else parent->sub=next;
 if (next) {next->prev=prev; next=NULL;}
 parent=NULL;
}
void KNOTEN::Paint(HDC dc) {	// von unten nach oben
 if (next) next->Paint(dc);
 if (sub)  sub ->Paint(dc);
}

bool KNOTEN::RelayMsg(UINT Msg, WPARAM wParam, LPARAM lParam) {
 return bool(		// von oben nach unten
    sub  && sub ->RelayMsg(Msg,wParam,lParam)
 || next && next->RelayMsg(Msg,wParam,lParam));
}


#if 0
void MINIWND::ToolMessage(UINT Msg) {
 TOOLINFO ti;
 InitStruct(&ti,sizeof(ti));
 ti.hwnd=::MainWnd;
 ti.uId=id;
 CopyRect(&ti.rect,&rcitem);
 ti.hinst=::HInstance;
 ti.lpszText=(PTSTR)hint;
 SendMessage(::Tooltip,Msg,0,(LPARAM)(LPTOOLINFO)&ti);
#else
void MINIWND::ToolMessage(UINT) {
#endif
 Inval();
}
MINIWND::MINIWND(NODE*p):KNOTEN(p) {
 SetRect(&rcitem,0,0,1,1);
 hint=NULL;
 id=0;
 style=0;
 ToolMessage(TTM_ADDTOOL);
 state=0;
}

MINIWND::MINIWND(NODE*p,RECT*r,LPCTSTR h,UINT i, BYTE st):KNOTEN(p) {
 CopyRect(&rcitem,r);
 hint=h;
 id=i;
 style=st;
 ToolMessage(TTM_ADDTOOL);
 state=0;
}

void MINIWND::SetParent(NODE*p) {
 KNOTEN::SetParent(p);
 Inval();
}

void MINIWND::Schieb(int dx,int dy,bool MitMaus) {
 if (!(dx|dy)) return;
 Inval();
 if (state&0x20 && MitMaus) {	// Maus verschieben
  POINT pt;
  GetCursorPos(&pt);
  pt.x+=dx; pt.y+=dy;
  SetCursorPos(pt.x,pt.y);
 }
 OffsetRect(&rcitem,dx,dy);
 Moved();
}
void MINIWND::SetState(BYTE st) {
 if (state==st) return;
 state=st;
 Inval();
}
void MINIWND::SetState(BYTE and, BYTE xor) {
 SetState((state&and)^xor);
}
void MINIWND::Paint(HDC dc) {
 KNOTEN::Paint(dc);
 if (state&STA_SELECTED) FillRect(dc,&rcitem,AuswahlPinsel);
 if (FocusOwner==this) DrawFocusRect(dc,&rcitem);
}
bool MINIWND::RelayMsg(UINT Msg,WPARAM wParam,LPARAM lParam) {
 switch (Msg) {
  case WM_SYSCOLORCHANGE:
  case WM_SIZE: return KNOTEN::RelayMsg(Msg,wParam,lParam);
  case WM_MOUSELEAVE: SetState(~STA_HOVER,0); break;	// immer HOVER aus!
  case WM_NCHITTEST: {
   SetState(~STA_HOVER,	// Dieses HOVER-Bit ist "echtes" Hover.
     PtInRectS(&rcitem,MAKEPOINTS(lParam))?STA_HOVER:0);
   where=0;	// im Innern
   if ((unsigned)(GET_X_LPARAM(lParam)-rcitem.left)<4)	where|=1;	// links
   if ((unsigned)(rcitem.right-GET_X_LPARAM(lParam))<5)	where|=2;	// rechts
   if ((unsigned)(GET_Y_LPARAM(lParam)-rcitem.top)<4)	where|=4;	// oben
   if ((unsigned)(rcitem.bottom-GET_Y_LPARAM(lParam))<5)where|=8;	// unten
  }break;
  case WM_SETCURSOR: {
   LPCTSTR curs=IDC_ARROW;
   if (style&1) curs=IDC_SIZEWE;		// Inneres
   if (style&2) curs=IDC_SIZENS;
   if (!(~style&3)) curs=IDC_SIZEALL;
   if (style&4 && where&3) curs=IDC_SIZEWE;	// Ränder
   if (style&8 && where&12) curs=IDC_SIZENS;
   if (!(~style&12)) {
    if (!(~where&5) || !(~where&10)) curs=IDC_SIZENWSE;	// Ecken
    if (!(~where&6) || !(~where&9)) curs=IDC_SIZENESW;
   }
   SetCursor(LoadCursor(0,curs));
  }break;
  case WM_LBUTTONDOWN: {
    FocusOwner=this;
    Inval();
  }nobreak;
  case WM_LBUTTONDBLCLK:
  case WM_MBUTTONDOWN:
  case WM_MBUTTONDBLCLK:
  case WM_RBUTTONDOWN:
  case WM_RBUTTONDBLCLK: {
   if (state&1) return true;	// andere Maustaste schon gedrückt
   if (state&2) return true;	// disabled
   state|=0x11;			// Maustaste gedrückt setzen
   drag=MAKEPOINTS(lParam);	// Anklickposition (absolut) merken
  }return true;
  case WM_LBUTTONUP:
  case WM_MBUTTONUP:
  case WM_RBUTTONUP: state&=~0x11; return true;
  case WM_MOUSEMOVE: {
   if (!(state&0x01)) return true;	// Gedrückter Zustand?
   if (!(style&15)) return true;
   Inval();
   drag.x=(short)(GET_X_LPARAM(lParam)-drag.x);	// Schlepp-Entfernung seit letztem Ereignis
   drag.y=(short)(GET_Y_LPARAM(lParam)-drag.y);
   if (style&4 && where&3) {	// Rand (=Größe) ändern
    if (where&1) rcitem.left+=drag.x;
    if (where&2) rcitem.right+=drag.x;
   }else if (style&1) OffsetRect(&rcitem,drag.x,0);	// verschieben
   if (style&8 && where&12) {	// Rand
    if (where&4) rcitem.top+=drag.y;
    if (where&8) rcitem.bottom+=drag.y;
   }else if (style&2) OffsetRect(&rcitem,0,drag.y);
   drag=MAKEPOINTS(lParam);
   Moved();
  }return true;
 }
 return true;
}

// Zur Zustellung von Mausereignissen sowie WM_MOUSEHOVER/WM_MOUSELEAVE
// (wenn keine Taste gedrückt bzw. sobald sie außerhalb losgelassen wurde)
// Für WM_MOUSEFIRST..WM_MOUSELAST, WM_NCHITTEST, WM_SETCURSOR
MINIWND* _stdcall MINIWND::MiniWndFromPoint(POINTS ps) {
 for (int i=0; i<2; i++) {	// Unsichtbare nicht eingerechnet
  for (MINIWND *p=(MINIWND*)Anker[i].sub; p; p=(MINIWND*)p->next) {
   for (MINIWND *q=(MINIWND*)p->sub; q; q=(MINIWND*)q->next) {
    if (PtInRectS(&q->rcitem,ps)) return q;	// sieht blöd aus!
   }
   if (PtInRectS(&p->rcitem,ps)) return p;
  }
 }
 return NULL;
}

COLORREF GetWinIniColor(PCTSTR key, COLORREF c) {
 TCHAR s[32];
 int rgb[3];
#define cb ((PBYTE)&c)
 rgb[0]=cb[0];
 rgb[1]=cb[1];
 rgb[2]=cb[2];
 wvsprintf(s,T("%i %i %i"),(va_list)rgb);
 GetProfileString(T("colors"),key,s,s,elemof(s));
 _stscanf(s,T("%i %i %i"),rgb+0,rgb+1,rgb+2);
 cb[0]=(BYTE)rgb[0];
 cb[1]=(BYTE)rgb[1];
 cb[2]=(BYTE)rgb[2];
#undef cb
 return c;
}

// TOOLTIP: Zu tun: Bei Verlassen des Client-Bereiches Tooltips verschwinden lassen! 
TOOLTIP::TOOLTIP():MINIWND(&Anker[2]) {
 GdiObj=0;
 CreateGdiObj();
}

TOOLTIP::~TOOLTIP() {
 DeleteGdiObj();
}

void TOOLTIP::DeleteGdiObj() {
 if (Back && GdiObj&1) DeleteBrush(Back);
 if (Pen  && GdiObj&2) DeletePen  (Pen);
 if (Font && GdiObj&4) DeleteFont (Font);
 GdiObj=0;
}

void TOOLTIP::CreateGdiObj() {
#ifdef WIN32
 TextColor=GetSysColor(COLOR_INFOTEXT);
#else
 TextColor=GetWinIniColor(T("InfoText"),0x000000L);
#endif
 if (TextColor) {
  Pen=CreatePen(PS_SOLID,0,TextColor);
  GdiObj|=1;
 }else Pen=GetStockPen(BLACK_PEN);
#ifdef WIN32
 BackColor=GetSysColor(COLOR_INFOBK);
#else
 BackColor=GetWinIniColor(T("InfoWindow"),0xE1FFFFL);
#endif
 HDC dc=GetDC(MainWnd);
 BackColor=GetNearestColor(dc,BackColor);
 ReleaseDC(MainWnd,dc);
 Back=CreateSolidBrush(BackColor);
 GdiObj|=2;
#ifdef WIN32
 Font=GetStockFont(DEFAULT_GUI_FONT);
#else
 Font=CreateFont(-8,0,0,0,0,0,0,0,0,0,0,0,0,T("Helv"));
 GdiObj|=4;
#endif
}

void TOOLTIP::Paint(HDC dc) {
 RECT r;
 CopyRect(&r,&rcitem);
 SelectPen(dc,Pen);
 SelectBrush(dc,Back);
 RoundRect(dc,r.left,r.top,r.right,r.bottom,8,8);
 InsetRect(&r,4,4);
 SetTextColor(dc,TextColor);
 SetBkColor(dc,BackColor);
 SelectFont(dc,Font);
 DrawText(dc,hintbuf,-1,&r,DT_WORDBREAK);
}

void _cdecl TOOLTIP::SetTip(PCRECT nohide,LPCTSTR tip,...) {
 if (HIWORD(tip) && IsBadStringPtr(tip,64)) tip=NULL;
 SetTimer(MainWnd,222,5000,NULL);	// Zum Verschwinden lassen
// hint=tip;
 SetParent(tip?Anker+0:Anker+2);	// vorne - oder verstecken
 if (!tip) return;
 if (!HIWORD(tip)) {
  TCHAR s[256];
  LoadString(HInstance,(UINT)tip,s,elemof(s));
  tip=s;
 }
// Inval();
 TCHAR s[256];
 wvsprintf(s,tip,(va_list)(&tip+1));
 if (!lstrcmp(s,hintbuf)) return;	// keine String-Änderung
 lstrcpy(hintbuf,s);
 HDC dc=GetDC(MainWnd);
 POINT pt;
 GetCursorPos(&pt);
 ScreenToClient(MainWnd,&pt);
 pt.y+=20;	// respektvoller Abstand
 SetRect(&rcitem,pt.x,pt.y,pt.x,pt.y);
 SelectFont(dc,Font);
 DrawText(dc,hintbuf,-1,&rcitem,DT_CALCRECT);
 ReleaseDC(MainWnd,dc);
 InflateRect(&rcitem,4,4);	// Rand dazu
 RECT R2;
 SetRect(&R2,0,0,ClientExt.x,ClientExt.y);
 MoveRectIntoRect(&rcitem,&R2);		// Innen halten
 if (nohide)
  MoveRectNoIntersect(&rcitem,nohide,&R2);
 Inval();
}

bool TOOLTIP::RelayMsg(UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_SYSCOLORCHANGE: DeleteGdiObj(); CreateGdiObj(); break;
  case WM_TIMER: if (wParam==222) { /*killtip:*/
   KillTimer(MainWnd,wParam);
   SetParent(Anker+2);
//   SetTip(NULL,NULL);
  }break;
//  case WM_MOUSEMOVE: goto killtip;
 }
 return MINIWND::RelayMsg(Msg,wParam,lParam);
}

GITTER::GITTER():MINIWND(&Anker[1],&Rand,T("Gitternetz"),0,12) {};

bool GITTER::RelayMsg(UINT Msg, WPARAM wParam, LPARAM lParam) {
 MINIWND::RelayMsg(Msg,wParam,lParam);
 if (Msg==WM_MOUSEMOVE && state&STA_HOVER) tip->SetTip(NULL,MAKEINTRESOURCE(22));
 // Hier natütlich OHNE Flächenfreihaltung! (NULL)
 return false;
}

/***************************************************
 ** MYBUTTON: Look and feel eines Windows-Knopfes **
 ***************************************************/

MYBUTTON::MYBUTTON(NODE*parent,RECT*rc,LPCTSTR hint,UINT id,EBild bmidx)
  :MINIWND(parent,rc,hint,id,0) {	// Parameter durchreichen
 bild=bmidx;
}

void MYBUTTON::SetState(BYTE st) {
// diese Version ohne Beeinflussung von STA_HOVER
 if (st&2) state&=~0x11;
 MINIWND::SetState((BYTE)((state&0x20)|(st&~0x20)));
}

void MYBUTTON::Enable(BOOL en) {
 SetState((BYTE)(en?state&~2:state|2));
}
typedef struct {signed char x,y;} BPOINT;	// Byte-Punktkoordinate
static void PunktTrans(POINT *d, const BPOINT *s, BYTE bits,int num) {
 do{
  d->x=s->x;		// unverändert kopieren
  d->y=s->y;
  if (bits&2) {		// 90-°-Drehung im Uhrzeigersinn
   d->y=d->x;
   d->x=-s->y;
  }
  if (bits&4) {		// 180-°-Drehung
   d->x=-d->x;
   d->y=-d->y;
  }
  d++; s++;
 }while (--num);
}
static void _stdcall Line3(HDC dc,int x1,int y1,int x2,int y2,int x3,int y3,HPEN pen) {
 SelectPen(dc,pen);	// Nur für Lager-Objekte!
#ifdef _M_IX86
 Polyline(dc,(LPPOINT)&x1,3);
#else
 POINT p[3]={{x1,y1},{x2,y2},{x3,y3}};
 Polyline(dc,p,3);
#endif
}
static void _stdcall Line3(HDC dc,int x1,int y1,int x2,int y2,int x3,int y3,COLORREF cr) {
 HPEN pen=CreatePen(PS_SOLID,0,cr);
 HPEN open=SelectPen(dc,pen);
#ifdef _M_IX86
 Polyline(dc,(LPPOINT)&x1,3);
#else
 POINT p[3]={{x1,y1},{x2,y2},{x3,y3}};
 Polyline(dc,p,3);
#endif
 SelectPen(dc,open);
 DeletePen(pen);
}
/**************************************************************
 ** Ein Funktions-Array <PaintFunc> zur Bemalung der Buttons **
 **************************************************************/
// Die Größe der Buttons könnte - oder sollte - sich noch ändern auf ca. 20 Pixel
static void _fastcall PaintTiefer(HDC dc, BYTE dreh) {	// dreh=2 zur Rechtsdrehung
 static const BPOINT poly1[]={{-5,-5},{5,-5},{-4,4},{4,4}};	// Entartetes Poly
 POINT poly[elemof(poly1)];
 PunktTrans(poly,poly1,dreh,elemof(poly1));
 Polygon(dc,poly,elemof(poly));
}
static void _fastcall PaintHoeher(HDC dc, BYTE dreh) {	// dreh=2 zur Drehung
 static const BPOINT poly1[]={{-5,-1},{0,-6},{5,-1},	// Großes Dreieck hoch
			      {-4, 1},{4, 1},{0, 5}};	// Kleines Dreieck runter
 POINT poly[elemof(poly1)];
 PunktTrans(poly,poly1,dreh,elemof(poly1));
// static const int Ecken[]={3,3};
// PolyPolygon(dc,poly,Ecken,elemof(Ecken));		// Zwei Polygone
 Polygon(dc,poly,3);	// Bei Win16 ist PolyPolygon zu umständlich,
 Polygon(dc,poly+3,3);	// bei Win32 lt. MSDN "schaumgebremst".
}
static void PaintENGER (HDC dc) {PaintTiefer(dc,2);}
static void PaintWEITER(HDC dc) {PaintHoeher(dc,2);}
static void PaintTIEFER(HDC dc) {PaintTiefer(dc,0);}
static void PaintHOEHER(HDC dc) {PaintHoeher(dc,0);}
static void PaintEINAUS(HDC dc) {
 Ellipse  (dc,-4,-4,+6,+6);		// "Netzschalter"-Beschriftung
 Rectangle(dc,-1,-6,+3,+2);
}
static void PaintPLAY(HDC dc) {		// "Play"-Dreieck
 static const BPOINT poly1[]={{-4,-5},{-4,5},{5,0}};	// Dreieck
 POINT poly[elemof(poly1)];
 PunktTrans(poly,poly1,0,elemof(poly1));
 Polygon(dc,poly,elemof(poly));
}
static void PaintPAUSE(HDC dc) {		// 2 "Pause"-Rechtecke
 Rectangle(dc,-4,-5, 0,+5);
 Rectangle(dc,+1,-5,+5,+5);
}
static void(*const PaintFunc[])(HDC dc)={
  PaintENGER,PaintWEITER,PaintTIEFER,PaintHOEHER,
  PaintEINAUS,PaintPLAY,PaintPAUSE};

void MYBUTTON::Paint(HDC dc) {
 KNOTEN::Paint(dc);	// Hier: kein Fokusrechteck oder Selektbitmap malen
 FillRect(dc,&rcitem,GetStockBrush(LTGRAY_BRUSH));	// Immer in dieser Farbe
 if (!(state&STA_GRAYED)) {	// Rand, wenn drückbar
  Line3(dc,	rcitem.left,	rcitem.bottom-2,
		rcitem.left,	rcitem.top,
		rcitem.right-1,	rcitem.top,
		GetStockPen(state&1?BLACK_PEN:WHITE_PEN));
  if (state&(STA_HOVER|STA_FOCUSED)) {
   Line3(dc,	rcitem.left+1,	rcitem.bottom-3,
		rcitem.left+1,	rcitem.top+1,
		rcitem.right-2,	rcitem.top+1,	state&1?0x808080L:0xE0E0E0L);
   Line3(dc,	rcitem.left+1,	rcitem.bottom-2,
		rcitem.right-2,	rcitem.bottom-2,
		rcitem.right-2,	rcitem.top,	state&1?0xE0E0E0L:0x808080L);
  }
  Line3(dc,	rcitem.left,	rcitem.bottom-1,
		rcitem.right-1,	rcitem.bottom-1,
		rcitem.right-1,	rcitem.top-1,
		GetStockPen(state&1?WHITE_PEN:BLACK_PEN));
 }
 POINT mitte;
 mitte.x=(rcitem.left+rcitem.right)/2;
 mitte.y=(rcitem.top+rcitem.bottom)/2;
 if (state&1) mitte.y++; else mitte.x--;
 SetViewportOrgEx(dc,mitte.x,mitte.y,NULL);
 SelectPen(dc,GetStockPen(WHITE_PEN));	// Immer weißen Rand um Symbolik(?)
 SelectBrush(dc,GetStockBrush(state&STA_GRAYED?GRAY_BRUSH:BLACK_BRUSH));
 PaintFunc[bild](dc);
 SetViewportOrgEx(dc,0,0,NULL);
}
bool MYBUTTON::RelayMsg(UINT Msg, WPARAM wParam, LPARAM lParam) {
 if (!(state&2)) switch (Msg) {	// Eingaben nur wenn nicht deaktiviert
  case WM_LBUTTONDOWN:
  case WM_LBUTTONDBLCLK: {	// Äußeres Programm muss Capture setzen!
//   DWORD keydelay;
   SetState(0x31);
   SendMessage(MainWnd,WM_COMMAND,id,0);
//   SystemParametersInfo(SPI_GETKEYBOARDDELAY,0,&keydelay,0);
//   *(UINT*)&keydelay=250*((UINT)keydelay+1);	// Formel!!
//   SetTimer(MainWnd,22,(UINT)keydelay,NULL);
  }break;
  case WM_MOUSEHOVER: {
   tip->SetTip(&rcitem,hint);
  }break;
  case WM_MOUSEMOVE: {
   if (!(state&0x10)) break;
   SetState((BYTE)(state&0x20?0x31:0x10));
  }break;
  case WM_LBUTTONUP: {
   SetState(0x00);
   KillTimer(MainWnd,22);
  }break;
/*
  case WM_TIMER: switch (wParam) {
   case 22: {
    DWORD keyspeed;
    if (!(state&0x10)) break;
    if (!(state&0x20)) break;
    SystemParametersInfo(SPI_GETKEYBOARDSPEED,0,&keyspeed,0);
    if ((UINT)keyspeed) *(UINT*)&keyspeed=1000/(UINT)keyspeed; // Frequenz in Zeit
    SetTimer(MainWnd,22,(UINT)keyspeed,NULL);
    SendMessage(MainWnd,WM_COMMAND,id,lParam);
   }break;
  }break;
*/
 }
 return MINIWND::RelayMsg(Msg,wParam,lParam);
}

/********************************************************
 ** Klasse M_K: Basis für Massesymbol und Triggerkreuz **
 ********************************************************/

void M_K::CalcRect(RECT*rcitem) {
// von <mitte> Rechteck berechnen, ggf. in Client-Bereich einziehen und
// <mitte> nachziehen
 RECT r;
 SetRect(&r,0,0,ClientExt.x-1,ClientExt.y-1);
 SetRect(rcitem,mitte.x-10,mitte.y-10,mitte.x+11,mitte.y+11);
 aussen=MoveRectIntoRect(rcitem,&r);
 if (aussen) {
  mitte.x=(rcitem->left+rcitem->right)/2;
  mitte.y=(rcitem->top+rcitem->bottom)/2;
 }
}
void M_K::SetGdiObjekte(COLORREF farbe) {
 if (PolygonPinsel && DeleteBrush(PolygonPinsel)) PolygonPinsel=0;
// if (PfeilStift && DeletePen(PfeilStift)) PfeilStift=0;
 if (!HIBYTE(HIWORD(farbe))) {	// kein "Abmelde-Kode"
//  PfeilStift=CreatePen(PS_SOLID,2,farbe);
  PolygonPinsel=CreateSolidBrush(farbe);
 }
}

void M_K::PaintPoly(HDC dc,const POINT *p, int n) {
 MINIWND::Paint(dc);
 HPEN open=SelectPen(dc,BackPen);
 HBRUSH obr=SelectBrush(dc,PolygonPinsel);
 SetViewportOrgEx(dc,mitte.x,mitte.y,NULL);
 Hilfspfeil(dc);
 Polygon(dc,p,n);
 SetViewportOrgEx(dc,0,0,NULL);
 SelectBrush(dc,obr);
 SelectPen(dc,open);
}
void M_K::Hilfspfeil(HDC dc) {	// Ein Pfeil ist ein 7-Eck!
 static const BPOINT PolyW[]={	// Gerader Pfeil (West)
  {-6,-11},{-11,-6},{-6,-1},{-6,-4},{-3,-4},{-3,-8},{-6,-8}};
 static const BPOINT PolyNW[]={	// Schräger Pfeil (Nord-West)
  {-2,-10},{-10,-10},{-10,-2},{-7,-5},{-5,-3},{-3,-5},{-5,-7}};
 static const BYTE rand2richtung[16]={8,0,2,1,4,8,3,8,6,7,8,8,5,8,8,8};
 POINT poly[9];		// für Aufruf von Polygon()
 BYTE bits=rand2richtung[aussen&15]; // Rand-Bits (4 bit) in Richtung (3 bit)
 if (bits==8) return;	// unmögliche Kombinationen für Rand-Bits
	// Himmelsrichtung: Bit 0: 45°, Bit 1: 90°, Bit 2: 180°
 PunktTrans(poly,bits&1?PolyNW:PolyW,bits,elemof(PolyW));
 Polygon(dc,poly,elemof(PolyW));
}

/*************************************
 ** Das Massesymbol, eins pro Kanal **
 *************************************/

MASSE::MASSE(KANAL*k): M_K(&Anker[1]) {
 TCHAR buf[64];
 this->k=k;
 UpdateGdiObjekte();
 _sntprintf(buf,elemof(buf),T("Massesymbol Kanal %s (Shift+Pfeil vertikal)"),k->name);
// M_CREATESTRUCT mcs;
// mcs.p=;
 CalcRect(&rcitem);
 hint=buf;
 id=k->idhigh+333;
 style=2;
// MINIWND::Init(&mcs);
}
void MASSE::Paint(HDC dc) {
 static const POINT Form[]={
   {-2,-8},{-2,-2},{-7,-2},{-7,+2},{+7,+2},{+7,-2},{+2,-2},{+2,-8}};
 M_K::PaintPoly(dc,Form,elemof(Form));	// Verkettete zeichnen (Gitter usw.)
}
bool MASSE::RelayMsg(UINT Msg,WPARAM wParam,LPARAM lParam) {
 switch (Msg) {
  case WM_LBUTTONUP: if (!(state&1)) break; state&=~1; nobreak;
  case WM_SIZE: Update(WAS_YNUL); return MINIWND::RelayMsg(Msg,wParam,lParam);
  case WM_SYSCOLORCHANGE: Update(WAS_FARBE); break;
//  case WM_NCHITTEST:
//   MessageBeep((UINT)-1);
//   if (state&0x20) _asm int 3;
//  break;
 }
 MINIWND::RelayMsg(Msg,wParam,lParam);
 switch (Msg) {
  case WM_MOUSEHOVER: tip->SetTip(&rcitem,MAKEINTRESOURCE(21),(LPCTSTR)k->name);
  case WM_MOUSEMOVE: {	// Gedrückt bewegen
   if (!(state&1)) break;
   mitte.x=(rcitem.left+rcitem.right)/2;
   mitte.y=(rcitem.top+rcitem.bottom)/2;
   k->SetNulllinie((Mitte.y-mitte.y)/(float)step.y);
  }break;
  case WM_RBUTTONDOWN: {
   if (!(state&1)) break;
   state&=~1;
   ShowPopupMenu(k->submenu,MAKEPOINTS(lParam).x,MAKEPOINTS(lParam).y);
  }break;
  case WM_MOUSEWHEEL: {
   int delta;
#ifdef WIN32
   delta=(short)HIWORD(wParam);
#else
   delta=(short)LOWORD(GetMessageExtraInfo());	//??
#endif   
   k->SetAblenkung(delta<0?T("+?"):T("-?"));
  }break;
 }
 return true;
}
void MASSE::CalcRect(RECT *rcitem) {
 mitte.x=(-10*(numkanal-1)+k->kn*20)+Mitte.x;
 mitte.y=Mitte.y-rund(k->nulllinie*step.y);
 M_K::CalcRect(rcitem);
}
bool MASSE::Update(BYTE was) {
 if (was&WAS_FARBE) UpdateGdiObjekte();
 if (was&WAS_YNUL) {
  if (state&1) return true; // Solange gedrückt, nicht "unterm Arsch" wegsetzen
  Inval();
  CalcRect(&rcitem);
  Moved();
 }return true;
}
void MASSE::UpdateGdiObjekte() {SetGdiObjekte(k->farbe);}

/************************************************************************
 ** Das Triggersymbol, eins pro Trigger (aber davon gibt's nur einen!) **
 ************************************************************************/

KREUZ::KREUZ(TRIGG*t): M_K(&Anker[1]) {
 this->t=t;
 UpdateGdiObjekte();
 CalcRect(&rcitem);
 hint=T("Triggerkreuz (Pegel und Position) (Strg+Pfeiltasten)");
 id=0;
 style=3;
// MINIWND::Init(&mcs);
}
void KREUZ::Paint(HDC dc) {
 static const BPOINT Form4[]={{-2,+8},{-2,+2},{-8,+2}};	// ¼ Kreuz
 POINT Form[12],*p;
 BYTE bits;
 for (p=Form,bits=0; bits<8; bits+=(BYTE)2) {
  PunktTrans(p,Form4,bits,3); p+=3;
 }
 M_K::PaintPoly(dc,Form,elemof(Form));
}
bool KREUZ::RelayMsg(UINT Msg, WPARAM wParam, LPARAM lParam) {
 switch (Msg) {
  case WM_LBUTTONUP: if (!(state&1)) break; state&=~1; nobreak;
  case WM_SIZE: Update(WAS_TPEGEL|WAS_TPRETRIG); return MINIWND::RelayMsg(Msg,wParam,lParam);
  case WM_SYSCOLORCHANGE: UpdateGdiObjekte(); break;
 }
 MINIWND::RelayMsg(Msg,wParam,lParam);
 switch (Msg) {
  case WM_MOUSEHOVER: tip->SetTip(&rcitem,MAKEINTRESOURCE(20)); break;
  case WM_MOUSEMOVE: {
   if (!(state&1)) break;
   mitte.x=(rcitem.left+rcitem.right)/2;
   mitte.y=(rcitem.top+rcitem.bottom)/2;
   t->SetPretrig(iitrafo(mitte.x,
     Rand.left,Rand.right-1,0,100));
   t->SetPegel((Mitte.y-mitte.y)/
     (float)step.y-t->k->nulllinie);
  }break;
  case WM_RBUTTONDOWN: {
   if (!(state&1)) break;
   state&=~1;
   ShowPopupMenu(t->submenu,MAKEPOINTS(lParam).x,MAKEPOINTS(lParam).y);
  }break;
 }
 return true;
}
void KREUZ::CalcRect(RECT*rcitem) {
 mitte.x=iitrafo(t->pretrig,0,100,Rand.left,Rand.right-1);
 mitte.y=Mitte.y-rund((t->pegel+t->k->nulllinie)*step.y);
 M_K::CalcRect(rcitem);
}
bool KREUZ::Update(BYTE was) {	// Position an Prätrigger und Triggerpegel halten
 if (was&(WAS_TQUELLE|WAS_FARBE)) UpdateGdiObjekte();
 if (!(was&(WAS_TPEGEL|WAS_TPRETRIG|WAS_TQUELLE))) return true;
 if (state&1) return true;	// Nicht "unterm Arsch" wegsetzen lassen!
 Inval();
 CalcRect(&rcitem);
 Moved();
 return true;
}
void KREUZ::UpdateGdiObjekte() {SetGdiObjekte(t->k->farbe);}

Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded