//#define OEMRESOURCE
#include "wutils.h"
#include "miniwnd.h"
#include "objekte.h"
#include <stdio.h>
#include "quelle.h"
#include <math.h> // fabs, log, floor, exp
//#include "inout.h"
//#include "dso.h"
/*Zu verarbeitende Tasten (Idee):
1..9 Kanalumschaltung (Fokusrechteck)
A,B Zeitbasis (Fokusrechteck)
T Triggerung (Fokus)
M Messung (Fokus)
Pfeil links-rechts Zeitbasis
Shift+Pfeil links-rechts Triggerverzögerung
Strg+Pfeil links-rechts Triggerverzögerung
Pfeil hoch/runter Y-Ablenkung (fokussierter Kanal)
Shift+Pfeil hoch/runter Y-Position
Strg+Pfeil hoch/runter Triggerpegel
TAB Fokus weiterschalten
Shift+TAB Fokus zurückschalten
( c Kopplung weiterschalten
( Shift+C Kopplung zurückschalten
( Strg+C Kopplungs-Menü
= Gleichspannungskopplung
~ Wechselspannungskopplung
_ Massekopplung
p Tastkopf weiterschalten
( Shift+P Tastkopf zurückschalten
( Strg+P Tastkopf-Menü
! Tastkopf 1:1/10:1
# Tastkopf-Dialog
- Invertierung
Leer, Pause Stop,Start
Einfg, Enter Kanal-Trace als Referenz speichern
Entf Referenzspeicher löschen
*/
#ifdef WIN32
#define WriteStruct WritePrivateProfileStruct
#define GetStruct GetPrivateProfileStruct
#else
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
extern "C" { // Suffix X: Windows 3x, Suffix Y: Win9x
BOOL WINAPI WritePrivateProfileStructX(LPCSTR,LPCSTR,LPVOID,UINT,LPCSTR);
BOOL WINAPI GetPrivateProfileStructX(LPCSTR,LPCSTR,LPVOID,UINT,LPCSTR);
BOOL WINAPI CheckMenuRadioItemX(HMENU m,UINT i,UINT j,UINT k,UINT p);
BOOL WINAPI SetMenuDefaultItemX(HMENU m,UINT i,UINT p);
BOOL WINAPI WritePrivateProfileStructY(LPCSTR,LPCSTR,LPVOID,UINT,LPCSTR);
BOOL WINAPI GetPrivateProfileStructY(LPCSTR,LPCSTR,LPVOID,UINT,LPCSTR);
BOOL WINAPI CheckMenuRadioItemY(HMENU m,UINT i,UINT j,UINT k,UINT p);
BOOL WINAPI SetMenuDefaultItemY(HMENU m,UINT i,UINT p);
}
BOOL WINAPI (*WriteStruct)(LPCSTR,LPCSTR,LPVOID,UINT,LPCSTR) =WritePrivateProfileStructX;
BOOL WINAPI (*GetStruct)(LPCSTR,LPCSTR,LPVOID,UINT,LPCSTR) =GetPrivateProfileStructX;
BOOL WINAPI (*CheckMenuRadioItem)(HMENU,UINT,UINT,UINT,UINT) =CheckMenuRadioItemX;
BOOL WINAPI (*SetMenuDefaultItem)(HMENU,UINT,UINT) =SetMenuDefaultItemX;
#endif
HWND MainWnd; // Hauptfenster
HMENU MainMenu;
HMENU OsziMenu; // Der komplett dynamische Menü-Teil
HWND hSetupDlg; // Nichtmodaler Setup-Dialog
HWND hDisplayDlg;
HWND hKBHand; // Dialog-Keyboard-Handler (für nichtmodale Dialoge)
#if 0
HWND Tooltip;
#endif
TOOLTIP *tip;
COLORREF CustColors[16];
TCHAR sDecimal[2]; // Dezimaltrennzeichen von Windows
TCHAR sDezimal[2]; // mein Dezimaltrennzeichen ('.', ',' oder '?')
TCHAR sMikro[2]; // Mikro-Zeichen (µ, in Arabien, Russland: u)
TCHAR sKanalFormat[8]; // Format-String für Kanal (Standard: Y%d)
int iKanalStart; // Nummer des ersten Kanals (Standard: 1)
TCHAR WindowTitle[64];
TCHAR StdProfile[MAX_PATH]; // mit Pfad, Zusammensetzung in WinMain
TCHAR HelpFileName[]=T("OSZI.HLP");
static const TCHAR sProzentI[]=T("%i,%i,%i,%i,%i");
#define sInt (sProzentI+12) // wie T("%d")
#define sLeer (sProzentI+14) // wie T("")
static const TCHAR IniFileName[] =T("OSZI.INI");
static const TCHAR WndClassName[]=T("OSZI");
static const TCHAR HauptSektion[]=T("Elbe"); // Nicht der Fluss aus dem Fichtelgebirge
static const TCHAR S_Position[] =T("Position");
WORD wPrefix=MAKEWORD(-4,3); // piko bis Giga
const int NumZeitMenu=1; // = Anzahl an Zeitbasen
const int NumTrigMenu=1; // Anzahl Trigger (stets 1) (grau im Rollbetrieb)
int NumKanalMenu=1; // Anzahl Kanäle im Menü
// max. MaxKanalMenu+1(!), dann heißt der letzte Eintrag "Weitere Kanäle..."
int MaxKanalMenu; // =16 von INI-Datei, mindestens 4
HBRUSH EditBrushes[3]; // "wichtigste" Farbe mit höchstem Index
ATOM atom; // zum Zugriff auf Hintergrundfarbe bei Edit-Feldern (GetProp)
HWND hZeitDlg;
int ZeitDlgCurSel; // aktuelle Auswahl im Zeitbasis-Dialog
//HWND hTrigDlg; // Zukunftsmusik
HWND hKanalDlg;
UINT wm_helpmsg;
// Globale Daten zur Interpretation der Sample-Daten (außer Samplerate)
WAVEHDR WaveHdr; // Zeiger auf ganz viele(?) Sampledaten
// mit unkonventioneller Belegung der Felder!
#define GetSample st.getsample
//GETSAMPLE GetSample; // Funktionszeiger zum Sample lesen
#define BlockAlign st.blockalign
//UINT BlockAlign; // Größe eines Samples für alle Kanäle
float FullScale; // 1<<bits, wirklich float??
// RECT InvalRect; // für begrenztes Neuzeichnen
typedef enum {ZUFALL=1,DSO220,SOUND} EDatenquelle;
EDatenquelle Datenquelle;
/*********************************
** Weitere allgemeine Routinen **
*********************************/
/*
BOOL a2f(PCTSTR s, float&z) {
PTSTR p;
p=StrChr((PTSTR)s,','); // Ein Komma durch Punkt ersetzen
if (p) *p='.';
return _stscanf(s,T("%f"),&z)==1?TRUE:FALSE;
}
BOOL GetDlgItemFloat(HWND Wnd, UINT id, float&z) {
// Gleitkommazahl (auch mit Komma statt Punkt) vom Dialogelement holen
TCHAR s[32];
GetDlgItemText(Wnd,id,s,elemof(s));
if (a2f(s,z)) return TRUE;
SetEditFocus(Wnd,id);
return FALSE;
}
void SetDlgItemFloat(HWND Wnd, UINT id, int stellen, float z) {
// Gleitkommazahl in Dialogelement setzen
char buf[32];
sprintf(buf,"%*G",stellen,z); SetDlgItemTextA(Wnd,id,buf);
}
*/
PTSTR AfterTab(PTSTR buf) {
PTSTR p=StrChr(buf,'\t');
if (!p) { p=buf+lstrlen(buf); *p='\t'; }
return ++p;
}
HMENU CopyPopupMenu(HMENU m, UINT inc); // zur Rekursion
void CopyMenuItem(HMENU sm, UINT si, HMENU dm, UINT di, UINT inc) {
// Menüpunkt vom Menü <sm>, Position <si>, zum Menü <dm> Position <di>
// kopieren, dabei Menü-ID um <inc> erhöhen. Ggf. werden ganze
// Popup-Untermenüs kopiert, siehe CopyPopupMenu().
// Bitmaps (MF_BITMAP) und Checkmark-Bitmaps werden zz. nicht mit kopiert!
TCHAR buf[64];
UINT state,id;
HMENU sub;
GetMenuString(sm,si,buf,elemof(buf),MF_BYPOSITION);
sub=GetSubMenu(sm,si);
state=GetMenuState(sm,si,MF_BYPOSITION);
if (sub) {
state&=0xFF; // nur LOW-Byte ist gültig,
state|=MF_POPUP; // "checked" kann es ja trotzdem sein!
id=(UINT)CopyPopupMenu(sub,inc);
}else id=GetMenuItemID(sm,si)+inc;
InsertMenu(dm,di,MF_BYPOSITION|state,id,buf);
}
HMENU CopyPopupMenu(HMENU m, UINT inc) {
// Erstellt vom Popup-Menü <m> eine Kopie mit um <inc> erhöhten Menu-IDs.
int j=GetMenuItemCount(m);
HMENU n=CreatePopupMenu();
for (int i=0; i<j; i++) CopyMenuItem(m,i,n,(UINT)-1,inc);
return n;
}
void SetMenuPopup(HMENU m, UINT pos, HMENU popup) {
// Verändert NUR das Popup-Menü-Handle; Win32-Version extra!
#ifdef WIN32
MENUITEMINFO mii;
mii.cbSize=sizeof(mii);
mii.fMask=MIIM_SUBMENU;
mii.fType=MFT_STRING;
mii.hSubMenu=popup;
SetMenuItemInfo(m,pos,TRUE,&mii);
#else
TCHAR s[64];
GetMenuString(m,pos,s,elemof(s),MF_BYPOSITION);
ModifyMenu(m,pos,MF_BYPOSITION|MF_POPUP,(UINT)popup,s);
#endif
}
void SetMenuString(HMENU m, UINT pos, PCTSTR s) {
// Verändert NUR den String des Menü-Eintrags; Win32: via SetMenuItemInfo
#ifdef WIN32
MENUITEMINFO mii;
mii.cbSize=sizeof(mii);
mii.fMask=MIIM_TYPE;
mii.fType=MFT_STRING;
mii.dwTypeData=(LPTSTR)s;
SetMenuItemInfo(m,pos,TRUE,&mii);
#else
UINT id=(UINT)GetSubMenu(m,pos);
UINT state=GetMenuState(m,pos,MF_BYPOSITION);
if (id) {
state&=0xFF; // nur LOW-Byte ist gültig,
state|=MF_POPUP; // "checked" kann es ja trotzdem sein!
}else id=GetMenuItemID(m,pos);
ModifyMenu(m,pos,MF_BYPOSITION|state,id,s);
#endif
}
void SetMenuStringAfterTab(HMENU m, UINT pos, PCTSTR a) {
// Setzt den Teilstring hinter dem Tabulator; eigentlich Extra für &->&&?
TCHAR s[64];
int l=GetMenuString(m,pos,s,elemof(s),MF_BYPOSITION);
/*
if (l>=64-1-lstrlen(a))
return;
if (s[l]) {
_asm nop;
}
s[l]=0; // Windows-Bug töten*/
lstrcpy(AfterTab(s),a);
SetMenuString(m,pos,s);
}
void AppendAndereToMenu(HMENU m, UINT id) {
// Hängt den String "&andere..." an das (Popup-)Menü an
// Für X-Ablenkung, Samplerate und Y-Ablenkung
TCHAR s[64];
LoadString(HInstance,205/*andere...*/,s,elemof(s));
InsertMenu(m,(UINT)-1,MF_BYPOSITION,id,s);
}
void MenuRadioNextDefault(HMENU m, UINT sub, UINT item) {
// CheckMenuRadioItem für's ganze Menü, sowie nächstes "enabeltes" Item
// zur Vorgabe machen
m=GetSubMenu(m,sub);
sub=GetMenuItemCount(m);
CheckMenuRadioItem(m,0,sub-1,item,MF_BYPOSITION);
for (UINT i=item;;) { // Vom nächsten Item an 1x rundherum
i++;
if (i>=sub) i=0;
if (!(GetMenuState(m,i,MF_BYPOSITION)&(MF_DISABLED|MF_GRAYED))) {
SetMenuDefaultItem(m,i,MF_BYPOSITION);
break;
}
if (i==item) break;
}
}
void MenuEnable(HMENU m, UINT pos, UINT mask) {
// Wie EnableMenuGroup; bei Deaktivieren des Häkchens wird das nächste
// aktive Bit eingestellt und WM_COMMAND verschickt
m=GetSubMenu(m,pos);
pos=GetMenuItemCount(m);
EnableMenuGroup(m,0,pos-1,mask,MF_BYPOSITION);
for (UINT i=0; i<pos; i++) {
UINT st=GetMenuState(m,i,MF_BYPOSITION);
if (st&MF_GRAYED && st&MF_CHECKED) { // Fall vorgefunden
for (UINT j=i; ; ) {
j++;
if (j>=pos) j=0;
if (!(GetMenuState(m,j,MF_BYPOSITION)&MF_GRAYED)) {
SendMessage(MainWnd,WM_COMMAND,GetMenuItemID(m,j),0);
break;
}
if (j==i) break;
}
}
}
}
bool FarbAuswahl(HWND w,COLORREF&cr) {
CHOOSECOLOR cc;
InitStruct(&cc,sizeof(cc));
cc.hwndOwner=w;
cc.rgbResult=cr;
cc.lpCustColors=CustColors;
cc.Flags=CC_RGBINIT|CC_SHOWHELP|CC_FULLOPEN;
if (!ChooseColor(&cc)) return false;
cr=cc.rgbResult;
return true;
}
int String2Index(PCTSTR p, PCTSTR n) {
// Ermittelt String-Nummer von <n> in String-Liste <p>, -1 bei keinem Treffer
// p zeigt auf aneinandergekettete nullterminierte Strings, mit Extra-Null am Ende
// Der String-Vergleich erfolgt case-insensitiv (lstrcmpi)
for (int i=0; *p; p+=lstrlen(p)+1,i++) if (!lstrcmpi(n,p)) return i;
return -1;
}
PCTSTR Index2String(PCTSTR p, int i) {
// Gegenfunktion, liefert String zum Index, NULL wenn <i> ungültig
for (;*p;p+=lstrlen(p)+1,i--) if (!i) return p;
return NULL;
}
int hsz2Index(HSZ*p, int hszlen, HSZ n) {
//Liefert nullbasierten Index des String-Handles <n>
for (int i=0; i<hszlen; i++,p++) if (!DdeCmpStringHandles(n,*p)) return i;
return -1;
}
int String2UpDown(PCTSTR s,int sm,int big) {
// Liefert <big> für "++" oder "+?" ohne gedrückte SHIFT-Taste,
// liefert <small> für "+" oder "+?" mit gedrückter SHIFT-Taste,
// bei '-' statt '+' negative Werte, andernfalls 0
int r=0;
switch (s[0]) {
case '+':
case '-': switch (s[1]) {
case '?': if (s[2]) break; r=GetKeyState(VK_SHIFT)<0?sm:big; break;
case 0: r=sm; break;
default: if (s[0]==s[1] && !s[2]) r=big;
}
}
if (s[0]=='-') r=-r;
return r;
}
/*
BYTE PointIntoRect(const RECT*rc, POINT*pt) {
// Zieht Punkt ins Rechteck und markiert jede der Bewegung im Rückgabewert
BYTE r=0;
if (pt->x< rc->left) {pt->x=rc->left; r|=1;} // links
if (pt->x>=rc->right) {pt->x=rc->right; r|=2;} // rechts
if (pt->y< rc->top) {pt->y=rc->top; r|=4;} // oben
if (pt->y>=rc->bottom){pt->y=rc->bottom; r|=8;} // unten
return r;
}
*/
COLORREF MixColor(COLORREF c1, COLORREF c2, int f1=128) {
// Farbe c1 und c2 komponentenweise mischen, mit f1 als Gewicht für
// 1. Farbe (0 = nur 2. Farbe, 256 = nur 1. Farbe)
COLORREF r;
int f2=256-f1; // 2. Faktor
#define KOMP(cr) ((PBYTE)&(cr)) // Farbkomponenten als Array[0..2]
for (int i=0; i<3; i++) KOMP(r)[i]=HIBYTE(KOMP(c1)[i]*f1+KOMP(c2)[i]*f2);
KOMP(r)[3]=0;
#undef KOMP
return r;
}
/*******************************************
** Spielereien mit dem Hintergrundraster **
*******************************************/
void _stdcall Line(HDC dc, int x1, int y1, int x2, int y2) {
#ifdef _M_IX86
Polyline(dc,(POINT*)&x1,2);
#else
POINT p[2]={{x1,y1},{x2,y2}};
Polyline(dc,p,2);
#endif
}
void Inval(bool background, LPRECT rcitem) {
//Bildschirm/Doppelpuffer-Bitmap ungültig machen;
//Triple-Puffer-Bitmap nur wenn background=true
//LPRECT für korrekte NULL-Übergabe!
if (background) DispOpt|=DO_TB_INVAL;
DispOpt|=DO_DB_INVAL;
InvalidateRect(MainWnd,rcitem,FALSE);
}
POINT WinBitmapExt={16,16};
#if 0
HBITMAP WinBitmaps[]={
(HBITMAP)OBM_LFARROW,(HBITMAP)OBM_LFARROWD,(HBITMAP)OBM_LFARROWI,
(HBITMAP)OBM_RGARROW,(HBITMAP)OBM_RGARROWD,(HBITMAP)OBM_RGARROWI,
(HBITMAP)OBM_UPARROW,(HBITMAP)OBM_UPARROWD,(HBITMAP)OBM_UPARROWI,
(HBITMAP)OBM_DNARROW,(HBITMAP)OBM_DNARROWD,(HBITMAP)OBM_DNARROWI,
(HBITMAP)OBM_CLOSE};
void LoadWinBitmaps(void) {
int i;
BITMAP bm;
for (i=0; i<elemof(WinBitmaps); i++)
WinBitmaps[i]=LoadBitmap(0,MAKEINTRESOURCE(WinBitmaps[i]));
GetObject(WinBitmaps[0],sizeof(bm),&bm);
WinBitmapExt.x=bm.bmWidth;
WinBitmapExt.y=bm.bmHeight;
}
void FreeWinBitmaps(void) {
int i;
for (i=0; i<elemof(WinBitmaps); i++) DeleteObject(WinBitmaps[i]);
}
void PaintWinBitmap(HDC dc,int x, int y, HBITMAP bm) {
HDC src=CreateCompatibleDC(dc);
HBITMAP obm=(HBITMAP)SelectObject(src,bm);
BitBlt(dc,x,y,WinBitmapExt.x,WinBitmapExt.y,src,0,0,SRCCOPY);
SelectObject(src,obm);
DeleteDC(src);
}
#endif
void SetBetrag(float&z,float&q) { // Vorzeichen von <z> behalten, q muss positiv sein!
z=z<0?-q:q;
}
// Gleitkommazahlen sind mit ihrem Rundungseffekt die blanke Katastrophe!!
static const float Reihe[]={ // von 1n bis 1G, sind 55 Werte
1E-9F,2E-9F,5E-9F,1E-8F,2E-8F,5E-8F,1E-7F,2E-7F,5E-7F,
1E-6F,2E-6F,5E-6F,1E-5F,2E-5F,5E-5F,1E-4F,2E-4F,5E-4F,
1E-3F,2E-3F,5E-3F,1E-2F,2E-2F,5E-2F,1E-1F,2E-1F,5E-1F,
1E+0F,2E+0F,5E+0F,1E+1F,2E+1F,5E+1F,1E+2F,2E+2F,5E+2F,
1E+3F,2E+3F,5E+3F,1E+4F,2E+4F,5E+4F,1E+5F,2E+5F,5E+5F,
1E+6F,2E+6F,5E+6F,1E+7F,2E+7F,5E+7F,1E+8F,2E+8F,5E+8F,
1E+9F};
int NextFloat(float &v, int updown) {
// Liefert zu gegebener Zahl die nächste oder vorherige im "Raster"
// Ermittelt die nächste (<updown>=1) oder vorhergehende (<updown>=-1)
// Zahl im 1-2-5-Raster, im obigen Raster. Das Vorzeichen wird beibehalten.
// Bei <updown>=0 wird zum nächsten 1-2-5-Raster aufgerundet.
// Liefert Reihen-Index (braucht man kaum!)
int m; // Wüste halbieren: Halbwüste: ich trau' mich vorerst nicht!
float z=(float)fabs(v);
for (m=0;m<elemof(Reihe);m++) {
if (z<Reihe[m]) break; // durchaus mit m=0 möglich, m=größerer Index!
if (z==Reihe[m]) goto exakt;
}
if (updown>0) updown--; // wenn dazwischen: m ist schon "nächster" Index
exakt:
m+=updown;
if (m<0) z=0; // Notbremse 1
else if (m>=elemof(Reihe)) z=1E38F; // Notbremse 2
else z=Reihe[m];
SetBetrag(v,z);
return m;
}
// Zweite Version mit Begrenzung
void NextFloat(float &v, int updown, float limit) {
NextFloat(v,updown);
if (updown>=0) {
if (fabs(v)>limit) SetBetrag(v,limit);
}else{
if (fabs(v)<limit) SetBetrag(v,limit);
}
}
static TCHAR ISO_Prefixes[]=T("afpnµm kMGTPE"); // µ ist nicht "const"!
static const double log1000=6.907755278982137; //log(1000);
TCHAR MakePrefix(float number, float&multiplier, WORD minmax) {
/* Ausgehend von <number> wird ein Präfix ausgewählt,
* der eine Wiedergabe der physikalischen Größe mit 1..3
* Vorkommastellen erlaubt.
* In <multiplier> landet der Wert zum "Behandeln" der Daten vor Ausgabe.
* das kommt für dieses Problem zupass.
* Unsauber ist das Problem "Kilogramm", die Basiseinheit ist hier "Gramm",
* die "Tonne" erfordert Extrawürste beim Aufrufer.
* Bei bestimmten Zeichensätzen (Kyrillisch, Arabisch) ist die Umsetzung
* von µ in u bzw. in Symbolschriftart oder besser Unicode erforderlich.
*/
int i;
i=number?rndint(log(fabs(number))/log1000-0.5):0;
//Typecast ohne floor() rundet zu Null: falsch!
if (i<(signed char)LOBYTE(minmax)) i=0; // Präfix unerwünscht
if (i>(signed char)HIBYTE(minmax)) i=0;
multiplier=(float)exp(-i*log1000);
return ISO_Prefixes[i+6];
}
PTSTR Prefix2Ptr(TCHAR c) { // Präfix korrigieren und Pointer liefern
switch (c) {
case 'µ':
case 'u': return ISO_Prefixes+4;
case 0:
case '\'': c=' '; // Apostroph zur Unterdrückung des Vorsatzes
} // bspw. bei "mol", "at", "atm"
return StrChr(ISO_Prefixes,c);
}
TCHAR GetDecimal() { // aktuelles Dezimaltrennzeichen nehmen
if (*sDezimal!='?') return *sDezimal;
return *sDecimal;
}
void Float2String(PTSTR s, float v, WORD minmax, PCTSTR Einheit,int precis=3) {
TCHAR prefix[]=T("\0"); // 2 Zeichen mit Nullen
TCHAR *p, buf[16];
float multiplier;
*prefix=MakePrefix(v,multiplier,minmax);
if (*prefix==' ') *prefix=0; // kein String
_sntprintf(buf,elemof(buf),T("%.*G"),precis,v*multiplier);
p=StrChr(buf,'.');
if (p) *p=GetDecimal(); // Ersetzen Punkt durch Komma (nach Systemsteuerung)
wsprintf(s,T("%s %s%s"),buf,prefix,Einheit);
}
bool String2Float(PCTSTR s, float&z, PTSTR e) {
// Konvertiert Zahl, ggf. Einheitenvorsatz und Einheit, nach <z> und <e>
// Maximale Länge der Einheit (ohne Vorsatz): 3 Zeichen, (Puffergröße=4)
PTSTR p;
double v;
int i,j,k,l;
p=StrChr((PTSTR)s,GetDecimal()); // Das benutzerdefinierte Zeichen ...
if (!p) p=StrChr((PTSTR)s,','); // oder ein Komma ...
if (p) *p='.'; // durch Punkt ersetzen
l=lstrlen(s);
i=_stscanf(s,T("%lf%n %") ELENSTR T("s%n"),&v,&j,e,&k);
e[ELEN-1]=0; // zwangsterminieren
if (i==1 && j==l) { // nur Zahl gegeben: OK
*e=0; goto okay;
}
if (i==2 && k==l && *e) { // Zahl und Einheit...
if (e[1]) { // Vorsatz möglich? (Nur mit Einheit! Bspw: m=Meter)
p=Prefix2Ptr(*e); // Gefahr: min -> Milli-Inch
if (p) {
j=int(p-ISO_Prefixes)-6;
v*=exp(j*log1000); // mit »float v« gibt's Rundungsfehler!
lstrcpy(e,e+1);
}
}
okay: z=(float)v;
return true;
}
return false;
}
/*********************************************
** Strukturen, die zu Klassen mutierten... **
*********************************************/
// globale Version fürs Testen...
bool GetNameList(PCTSTR n, PTSTR buf, BYTE*bitnr) {
// Namen mit Nullen trennen, Liste mit Doppelnull anschließen (max. 16 Bytes),
// Wertigkeit zu <bitnr> speichern (Zeiger darf NULL sein, wenn ungewünscht)
// Zur Feststellung von Gruppenzugehörigkeit.
int i,bit;
TCHAR hack[32];
PTSTR p,q;
lstrcpyn(hack,n,elemof(buf)); // in den Zerhack-Puffer
n=hack;
for (;;) {
bit=0; // Ohne Angabe Bit=0 setzen
while (*n==' ') n++; // Führende Leerzeichen übergehen
if (!*n) break; // Stringende erreicht
p=StrChr(n,' ');
if (p) *p=0; // am Leerzeichen zerhacken
q=StrChr(n,':');
if (q) {
*q=0; // am Doppelpunkt zerhacken
if (_stscanf(q+1,sInt,&bit)!=1) return false; // falsche Bitnummer
}
i=lstrlen(n);
if ((unsigned)(i-1)>6) return false; // Name zu kurz oder zu lang
lstrcpy(buf,n);
buf+=i+1; // Ziel vorrücken
if (bitnr) *bitnr++=(BYTE)bit;
if (p) n=p+1; // Quelle vorrücken
else break; // Stringende erreicht
}
*buf=0; // Doppel-Null
return true;
}
int flimit(float f, int u, int o) { // Runden und begrenzen
if (f>o) return o;
if (f<u) return u;
return rndint(f);
}
int rund(float f) {
return flimit(f,-30000,30000);
}
ZEIT *zeit; // notfalls mehrere (Zeitbasen)
TRIGG *trig; // davon gibt's nur ein Exemplar!
int numkanal;
KANAL *kanal=NULL; // Kanal-Darstellungs-Objekte (<numkanal> Stück)
QUELLE *quelle=NULL; // Datenquelle-Objekt (1 Stück)
// Ich komme wohl doch nicht ohne eine Gruppen-Liste aus...
int numgruppe;
GRUPPE *gruppe; // Gruppen-Objekte (bloß Namen), <numkanal> Stück!
void MacheGruppenListe() {
// aktualisiert <numgruppe> und <gruppe> an Hand der Kanal-Namen:
// Bei nur einem Kanal gibt es gar keine Gruppen,
// ab zwei am Ende stets die Gruppe "Alle Kanäle / Beide Kanäle"
GRUPPE *gp;
KANAL *kp,*kj;
TCHAR buf[32];
PTSTR p;
int i,j,k;
numgruppe=0;
if (numkanal<=1) return;
gp=gruppe; // vorn mit dem Füllen anfangen
if (!gp) return; // Puffer noch nicht angefordert (beim Lesen der .INI)
for (i=0,kp=kanal; i<numkanal-1; i++,kp++) {
if (!kp->GetNameList(buf,NULL)) continue;
for (p=buf; *p; p+=lstrlen(p)+1) {
for (j=i+1; j<numkanal; j++) {
kj=kanal+j;
if (!kj->HatName(p)) continue;
for (k=0; k<numgruppe; k++) {
if (!lstrcmp(gruppe[k].name,p)) {
gruppe[k].ref++;
goto schondrin;
}
}
lstrcpyn(gp->name,p,elemof(gp->name));
gp->ref=2;
gp++; numgruppe++;
if (numgruppe==numkanal) return;
schondrin:;
}
}
}
gp->name[0]=0; gp->ref=numkanal; // Zuletzt "alle/beide Kanäle" anhängen
numgruppe++;
}
#if 0
bool GRUPPE::SetNulllinie(PTSTR s,PTSTR t) {
float z,v;
EINHEIT e;
if (!String2Float(s,z,e)) return false;
if (e[0] && lstrcmp(e,einheit)) return false; // falsche Einheit
if (!String2Float(t,v,e)) return false;
if (e[0] && lstrcmp(e,einheit)) return false; // falsche Einheit
for (int i=0; i<numkanal; i++) {
if (kanal[i].HasName(name)) {
if (!kanal[i].SetNulllinie(z)) return false;
z+=v; // nächste Nulllinie
}
}
return true;
}
#endif
static const TCHAR sKopplung1[]=T("DC\0AC\0GND\0");
static const TCHAR sKopplung2[]=T("=~_"); // Kurzform Kopplung
static const TCHAR sTastkopf2[]=T(" !#"); // Kurzform Tastkopf
int TextWidth(HDC dc, PCTSTR s, int slen) { // Hauptversion mit DC und Länge
POINT p;
#ifdef WIN32
GetTextExtentPoint32(dc,s,slen,(PSIZE)&p);
#else
*(DWORD*)&p=GetTextExtent(dc,s,slen);
#endif
return p.x;
}
int TextWidth(HDC dc, PCTSTR s, PCTSTR e) { // Version mit DC und Ende-Zeiger
return TextWidth(dc,s,int(e-s));
}
int TextWidth(HDC dc, PCTSTR s) { // Version mit DC und Nullterminierung
return TextWidth(dc,s,lstrlen(s));
}
int TextWidth(PCTSTR s) { // Version ohne DC, mit Nullterminierung
HDC dc=GetDC(MainWnd);
int len=TextWidth(dc,s);
ReleaseDC(MainWnd,dc);
return len;
}
ANZEIGE::ANZEIGE():MINIWND(&Anker[1]) {
RECT r;
SetRect(&r,0,0,0,0);
nagel=new MYBUTTON(this,&r,T("Systemmenü (Strg+Leertaste)"),0,MYBUTTON::EINAUS);
}
void ANZEIGE::Paint(HDC dc) {
MINIWND::Paint(dc);
SetBkColor(dc,state&STA_SELECTED?MixColor(AuswahlFarbe,BackColor,0x20):BackColor);
SelectFont(dc,GetStockFont(SYSTEM_FONT));
}
bool ANZEIGE::RelayMsg(UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_SIZE: {
nagel->Inval();
SetRect(&nagel->rcitem,rcitem.left-WinBitmapExt.x,
rcitem.top,rcitem.left,rcitem.top+WinBitmapExt.y);
nagel->Moved();
// Eigentlich: bei unten liegenden Fenstern Systemmenü nach unten ausrichten;
// bei RTL-System an rechte Kante setzen, niemals außerhalb des Fensters
// sowie Test auf "herausfallende" Fenster!
}break;
case WM_MOUSEMOVE: {
MINIWND::RelayMsg(Msg,wParam,lParam);
hitkode=0;
if (!(state&0x20)) return false;
int x=MAKEPOINTS(lParam).x-rcitem.left;
for (int i=0; i<elemof(grenzen); i++)
if (x<grenzen[i]) {hitkode=(BYTE)(HK_PRI+i); break;}
}return false;
}return MINIWND::RelayMsg(Msg,wParam,lParam);
}
void ANZEIGE::setTextColor(HDC dc, COLORREF farbe) {
SetTextColor(dc,state&0x20?farbe:MixColor(farbe,BackColor));
}
void KANALANZEIGE::Update(BYTE was) {
if (was&(WAS_YVOR|WAS_YINV|WAS_YABL|WAS_YKOP)) {
TCHAR buf[32];
knopf[0]->Enable(k->div<k->max); // Rück-Verbindung...
knopf[1]->Enable(k->min<k->div);
k->GetVar(2,buf); // Ablenkung
wnsprintf(string1,elemof(string1),T("%c %s = %s %c"),
sTastkopf2[k->GetTastkopfIndex()],
k->name,
buf,
sKopplung2[k->kopplung]);
HDC dc=GetDC(MainWnd);
int spclen2=TextWidth(dc,sTastkopf2,1)/2; // Breite halbes Leerzeichen
grenzen[0]=TextWidth(dc,string1,1)+spclen2; // Tastkopf-Bereich
PTSTR p=StrChr(string1,'=');
grenzen[1]=TextWidth(dc,string1,p); // Kanalname-Bereich
p+=2; if (*p=='-') p++;
grenzen[2]=TextWidth(dc,string1,p); // Vorzeichen-Bereich
p=StrChr(p,0)-2;
grenzen[3]=TextWidth(dc,string1,p); // Ablenkungs-Bereich
p+=2;
grenzen[4]=TextWidth(dc,string1,p); // Kopplungs-Bereich
ReleaseDC(MainWnd,dc);
int diff=rcitem.left+grenzen[4]-rcitem.right;
if (diff) {
rcitem.right+=diff;
Moved(); // "heißes" Rechteck nachführen!
knopf[0]->Schieb(diff,0,true);
knopf[1]->Schieb(diff,0,true);
}
}
if (was&(WAS_YVOR|WAS_YINV|WAS_YABL|WAS_YKOP|WAS_FARBE)) Inval();
}
KANALANZEIGE::KANALANZEIGE(KANAL*k) {
this->k=k;
grenzen[4]=16;
string1[0]=0;
RECT r;
// M_CREATESTRUCT mcs; // SCHEUSSLICH!!
// mcs.p=&Anker[1]; // Hintergrund
SetKnopfRect(&rcitem,-1); // Hier: Initiale Position
#ifdef WIN32
hint=LPSTR_TEXTCALLBACK;
#endif
id=k->idhigh;
style=0; // vorerst nicht verschiebbar!
// MINIWND::Init(&mcs); // Problem: Der Defaultkonstruktor wurde schon gerufen
SetKnopfRect(&r,0);
knopf[0]=new MYBUTTON(this,&r,MAKEINTRESOURCE(30)/*T("Größerer Koeffizient (Pfeil runter)")*/,
k->idhigh+IDC_YABL+elemof(Reihe)+1,MYBUTTON::TIEFER);
SetKnopfRect(&r,1);
knopf[1]=new MYBUTTON(this,&r,MAKEINTRESOURCE(31)/*T("Kleinerer Koeffizient (Pfeil rauf)")*/,
k->idhigh+IDC_YABL+elemof(Reihe)+2,MYBUTTON::HOEHER);
}
void KANALANZEIGE::Paint(HDC dc) {
ANZEIGE::Paint(dc);
setTextColor(dc,k->farbe);
ExtTextOut(dc,rcitem.left,rcitem.top,ETO_OPAQUE,&rcitem,
string1,lstrlen(string1),0);
}
bool KANALANZEIGE::RelayMsg(UINT Msg,WPARAM wParam,LPARAM lParam) {
switch (Msg) {
#if 0
case WM_NOTIFY: {
static const TCHAR Text[]=T("Tastteiler\0Kanal/Gruppe\0")
T("Invertierung\0Ablenkkoeffizient (pro Teilstrich)\0Kopplung\0");
if (!hitkode) break;
#define ttt ((LPTOOLTIPTEXT)lParam)
if (ttt->hdr.code==TTN_NEEDTEXT)
ttt->lpszText=(LPTSTR)Index2String(Text,hitkode&0x3F);
#undef ttt
}break;
#endif
case WM_SIZE: {
Inval(); SetKnopfRect(&rcitem,-1); Moved();
knopf[0]->Inval(); SetKnopfRect(&knopf[0]->rcitem,0); knopf[0]->Moved();
knopf[1]->Inval(); SetKnopfRect(&knopf[1]->rcitem,1); knopf[1]->Moved();
}break;
case WM_MOUSEMOVE: {
TCHAR s[256]; // Aua, jedes MouseMove!
LoadString(HInstance,24,s,elemof(s));
tip->SetTip(&rcitem,Index2String(s,hitkode&0x3F));
}break;
case WM_RBUTTONDOWN: {
HMENU m=0;
switch (hitkode) {
case HK_PRI+0: m=GetSubMenu(k->submenu,SUB_YVOR); break;
case HK_PRI+3: m=GetSubMenu(k->submenu,SUB_YABL); break;
case HK_PRI+4: m=GetSubMenu(k->submenu,SUB_YKOP); break;
}
if (m) ShowPopupMenu(m,MAKEPOINTS(lParam).x,MAKEPOINTS(lParam).y);
}break;
}
return ANZEIGE::RelayMsg(Msg,wParam,lParam);
}
void KANALANZEIGE::SetKnopfRect(RECT*r,int idx) {
if (idx<0) { // Eigenes Rechteck
r->left=k->kn*160+20;
r->top =ClientExt.y-20;
r->right=r->left+grenzen[4];
r->bottom=r->top+WinBitmapExt.y;
return;
}
int x=rcitem.right;
int y=rcitem.top;
SetRect(r,x,y,x+WinBitmapExt.x,y+WinBitmapExt.y);
if (!idx) return;
OffsetRect(r,WinBitmapExt.x,0);
}
static int _fastcall GetSampleDc(KANAL*k,LPSTR p) {
return GetSample(p,k->maske);
}
static int _fastcall GetSampleAc(KANAL*k,LPSTR p) {
int y=GetSample(p,k->maske)-GET_SUB(k->sub);
k->sub+=y; // Nachkommastellen in <sub> aufheben!
return y;
}
static int _fastcall GetSampleGnd(KANAL*,LPSTR) {
return 0;
}
void KANAL::Konstruktor(int kn) {
this->kn=kn;
idhigh=kn<<10;
wnsprintf(name,elemof(name),sKanalFormat,kn+iKanalStart);
div=0; // "ungültig" für LadeVorgabe(), SetAblenkung()
fTastkopf=0; // "ungültig" für LadeVorgabe(), SetTastkopf()
kopplung=0;
getsample=GetSampleDc;
poly=NULL/*new POINT[zeit.samples]*/;
if (kn<MaxKanalMenu) {
submenu=GetSubMenu(OsziMenu,NumZeitMenu+NumTrigMenu);
if (kn) submenu=CopyPopupMenu(submenu,idhigh);
TCHAR buf[64],KanalGruppe[32],*pg=KanalGruppe;
LoadString(HInstance,208,KanalGruppe,elemof(KanalGruppe)); // "Kanal %s\0Gruppe %s"
if (kn>=numkanal) {
pg+=lstrlen(pg)+1; // auf "Gruppe %s"
wnsprintf(buf,elemof(buf),pg,name);
}else{
wnsprintf(buf,elemof(buf),pg,name);
if (kn<10) InsertAmp(buf,elemof(buf),-1); // bei 2stelligen Zahlen ohne "&"
}
(kn?InsertMenu:ModifyMenu)(OsziMenu,NumZeitMenu+NumTrigMenu+kn,MF_BYPOSITION|MF_POPUP,(UINT)submenu,buf);
if (kn) NumKanalMenu++;
}else if (kn==MaxKanalMenu) {
TCHAR s[32];
LoadString(HInstance,212/*"Weitere Kanäle..."*/,s,elemof(s));
InsertMenu(OsziMenu,NumZeitMenu+NumTrigMenu+kn,MF_BYPOSITION,IDC_YWEITER,s);
NumKanalMenu++;
}
nulllinie=0;
farbe=kn?0x008080L:0x000080L; // gelb? // rot? Besser wäre "ungültig"
masse=new MASSE(this);
anzeige=new KANALANZEIGE(this); // Verlinkung herstellen
LadeVorgabe();
InfoNeu(0xFF);
}
void KANAL::Destruktor() {
RetteVorgabe();
if (poly) delete poly; poly=NULL;
delete anzeige;
delete masse;
if (kn) DeleteMenu(OsziMenu,NumZeitMenu+NumTrigMenu+kn,MF_BYPOSITION);
}
void ZEITANZEIGE::Paint(HDC dc) {
ANZEIGE::Paint(dc);
setTextColor(dc,z->farbe);
ExtTextOut(dc,rcitem.left,rcitem.top,ETO_OPAQUE|ETO_CLIPPED,&rcitem,
string1,lstrlen(string1),NULL);
TCHAR buf[16];
z->GetVar(1,buf);
ExtTextOut(dc,rcitem.left,rcitem.bottom,ETO_OPAQUE,NULL,buf,lstrlen(buf),NULL);
}
void TRIGGERANZEIGE::Paint(HDC dc) {
ANZEIGE::Paint(dc);
setTextColor(dc,t->farbe);
ExtTextOut(dc,rcitem.left,rcitem.top,ETO_OPAQUE|ETO_CLIPPED,&rcitem,
string1,lstrlen(string1),NULL);
}
void ZEITANZEIGE::Update(BYTE was) {
if (was&(WAS_RATE|WAS_XABLENK)) {
z->GetVar(0,string1);
knopf[0]->Enable(z->div<z->max); // Rück-Verbindung...
knopf[1]->Enable(z->min<z->div);
int diff=rcitem.left+TextWidth(string1)-rcitem.right;
if (diff) {
rcitem.right+=diff;
Moved();
knopf[0]->Schieb(diff,0,true);
knopf[1]->Schieb(diff,0,true);
}
}
if (was&(WAS_RATE|WAS_XABLENK|WAS_FARBE)) Inval();
}
const TCHAR sTrigModus[]=T("Auto\0Normal\0Single\0");
const TCHAR sTrigFlanke[]=T("+-");
const TCHAR sTrigKopplung[]=T("DC\0AC\0HF\0LF\0TVH\0TVL\0NULL\0PAT\0");
void TRIGGERANZEIGE::Update(BYTE was) {
if (was&(WAS_TQUELLE|WAS_TFLANKE|WAS_TKOPPLUNG|WAS_TPEGEL|WAS_TPRETRIG)) {
TCHAR buf[32];
KANAL *k=t->k;
PCTSTR kop=Index2String(sTrigKopplung,t->kopplung);
TRIG tr; // DEBUG!
tr.what=0;
::quelle->RelayMsg(Q_SETTRIG,&tr);
Float2String(buf,k->div*t->pegel,k->praefixe,k->einheit);
wnsprintf(string1,elemof(string1),T("%c%s: %s %s (%d%%)"),
sTrigFlanke[t->flanke],k->name,buf,
kop,t->pretrig);
// Anklick-Bereiche festlegen
HDC dc=GetDC(MainWnd);
int spclen2=TextWidth(dc,sTastkopf2,1)/2; // Breite halbes Leerzeichen
grenzen[0]=TextWidth(dc,string1,1)+spclen2; // Flanken-Bereich
PTSTR p=StrChr(string1,':');
grenzen[1]=TextWidth(dc,string1,p); // Kanalname-Bereich
p=StrChr(p,'('); // Pegel-Bereich
grenzen[2]=TextWidth(dc,string1,p-lstrlen(kop)-1);
grenzen[3]=TextWidth(dc,string1,p); // Kopplungs-Bereich
grenzen[4]=TextWidth(dc,string1); // Prätrigger-Bereich (ganzer String)
ReleaseDC(MainWnd,dc);
int diff=rcitem.left+grenzen[4]-rcitem.right;
if (diff) {
rcitem.right+=diff; Moved();
knopf[0]->Schieb(diff,0,true);
}
}
if (was&(WAS_TQUELLE|WAS_TFLANKE|WAS_TKOPPLUNG|WAS_TPEGEL|WAS_TPRETRIG|
WAS_FARBE)) Inval();
}
ZEITANZEIGE::ZEITANZEIGE(ZEIT*z) {
this->z=z;
RECT r;
// M_CREATESTRUCT mcs;
// mcs.p=&Anker[1]; // Hintergrund
SetRect(&rcitem,20,10,100,10+WinBitmapExt.y);
hint=T("Zeitbasis");
// MINIWND::Init(&mcs);
SetRect(&r,0,0,WinBitmapExt.x,WinBitmapExt.y);
OffsetRect(&r,rcitem.right,rcitem.top);
knopf[0]=new MYBUTTON(this,&r,MAKEINTRESOURCE(35)/*T("Größere Zeitbasis (Pfeil links)")*/,
IDC_XABLENK+elemof(Reihe)+1,MYBUTTON::ENGER);
OffsetRect(&r,WinBitmapExt.x,0);
knopf[1]=new MYBUTTON(this,&r,MAKEINTRESOURCE(36)/*T("Kleinere Zeitbasis (Pfeil rechts)")*/,
IDC_XABLENK+elemof(Reihe)+2,MYBUTTON::WEITER);
Update(0xFF);
}
TRIGGERANZEIGE::TRIGGERANZEIGE(TRIGG*t) {
this->t=t;
RECT r;
// M_CREATESTRUCT mcs;
// mcs.p=&Anker[1]; // Hintergrund
SetRect(&rcitem,120,10,200,10+WinBitmapExt.y);
#ifdef WIN32
hint=LPSTR_TEXTCALLBACK;
#endif
style=0;
// MINIWND::Init(&mcs);
SetRect(&r,0,0,WinBitmapExt.x,WinBitmapExt.y);
OffsetRect(&r,rcitem.right,rcitem.top);
knopf[0]=new MYBUTTON(this,&r,MAKEINTRESOURCE(37)/*T("Start/Stopp (Leertaste)")*/,
0x229,MYBUTTON::PAUSE);
Update(0xFF);
}
bool ZEITANZEIGE::RelayMsg(UINT Msg, WPARAM wParam, LPARAM lParam) {
switch (Msg) {
case WM_MOUSEMOVE: {
TCHAR s[256]; // Aua, jedes MouseMove!
LoadString(HInstance,16,s,elemof(s));
tip->SetTip(&rcitem,Index2String(s,hitkode&0x3F));
}break;
case WM_RBUTTONDOWN: {
if (!(state&0x20)) break;
ShowPopupMenu(GetSubMenu(z->submenu,SUB_XABLENK),
MAKEPOINTS(lParam).x,MAKEPOINTS(lParam).y);
}break;
}
return ANZEIGE::RelayMsg(Msg,wParam,lParam);
}
bool TRIGGERANZEIGE::RelayMsg(UINT Msg,WPARAM wParam,LPARAM lParam) {
switch (Msg) {
#if 0
case WM_NOTIFY: {
static const TCHAR Text[]=T("Flanke\0Quelle\0Pegel\0Kopplung\0Prätrigger\0");
if (!hitkode) break;
#define ttt ((LPTOOLTIPTEXT)lParam)
if (ttt->hdr.code==TTN_NEEDTEXT)
ttt->lpszText=(PTSTR)Index2String(Text,hitkode&0x3F);
#undef ttt
}break;
#endif
case WM_MOUSEMOVE: {
TCHAR s[256]; // Aua, jedes MouseMove!
LoadString(HInstance,38,s,elemof(s));
tip->SetTip(&rcitem,s,(LPCTSTR)Index2String(s,(hitkode&0x3F)+1));
}break;
case WM_RBUTTONDOWN: {
HMENU m=0;
switch (hitkode) {
case HK_PRI+0: m=GetSubMenu(t->submenu,SUB_TFLANKE); break;
case HK_PRI+1: m=GetSubMenu(t->submenu,SUB_TQUELLE); break;
case HK_PRI+3: m=GetSubMenu(t->submenu,SUB_TKOPPLUNG); break;
}
if (m) ShowPopupMenu(m,MAKEPOINTS(lParam).x,MAKEPOINTS(lParam).y);
}break;
}
return ANZEIGE::RelayMsg(Msg,wParam,lParam);
}
#ifndef WIN32
BOOL WINAPI CheckMenuRadioItemX(HMENU m,UINT i,UINT j,UINT k,UINT p) {
for (; i<=j; i++) {
CheckMenuItem(m,i,p|(i==k?
MFT_RADIOCHECK|MF_CHECKED:
MFT_RADIOCHECK|MF_UNCHECKED));
}
return TRUE;
}
BOOL WINAPI SetMenuDefaultItemX(HMENU,UINT,UINT) {
return TRUE;
}; //vorerst leer, geht nur mit OwnerDraw zu lösen
#endif
/**********************
** Hardware-Zugriff **
**********************/
/* Mal-Routinen: Gitternetz, Kurven, Fadenkreuz, alles zusammen */
void KANAL::MaleKurve(HDC dc) {
COLORREF f=farbe;
int o;
if (DispOpt&DO_XOR) {
f^=BackColor;
o=SetROP2(dc,R2_XORPEN);
}
if (DispOpt&DO_LINE) {
HPEN KPen,OldPen;
KPen=CreatePen(PS_SOLID,1,f);
OldPen=(HPEN)SelectObject(dc,KPen);
Polyline(dc,poly,polycount);
Polyline(dc,poly+polycount,polyc2);
SelectObject(dc,OldPen);
DeleteObject(KPen);
}else{
POINT *pp;
int i;
for (i=polycount+polyc2, pp=poly; i>0; i--,pp++) {
SetPixel(dc,pp->x,pp->y,f);
}
}
if (DispOpt&DO_XOR) SetROP2(dc,o);
}
size_t log2phys(size_t a) {
// Wandelt einen logischen Sampledaten-Index in WaveHdr in einen physikalischen.
// Dabei muss nur der Prätrigger-Bereich beachtet werden
// BEI ROLLBETRIEB alles.
// if (WaveHdr.dwFlags&WHDR_BEGINLOOP) {
if (a<WaveHdr.reserved) {
size_t A=WaveHdr.reserved-WaveHdr.dwLoops;
if (a<A) a+=WaveHdr.dwLoops;
else a-=A;
}
// }
return a;
}
LPSTR log2addr(size_t a) {
// Wandelt logischen Sampleraten-Index in Adresse
// (FAR reicht zum Zugriff, wenn Sample-Breite eine Potenz von 2 ist.)
return (LPSTR)((char huge*)WaveHdr.lpData+log2phys(a));
}
/*
LPSTR idx2addr(DWORD index,BYTE byteoffset) {
return log2addr(index*BlockAlign+byteoffset);
}
*/
void KANAL::CalcGraf() {
// fehlt noch:
// * Reduktion auf notwendige Punktzahl (2049..4096)
// * nur Aktualisierungsbereich
// * X/Y-Betrieb
int *pp;
int i,x;
size_t j;
float pixprosample=step.x/zeit->rate/zeit->div;
float pixprolsb=-voltprolsb/div*step.y; // Minus weil oben positiv
float pixmitte=Mitte.y-(nulllinie-voltoffset/div)*step.y;
if (!WaveHdr.lpData) return;
pp=(int*)poly;
if (!pp) return;
// Neue Samples:
x=0;
j=byteoffset;
for (i=0; j<WaveHdr.dwBytesRecorded; i++,x++) {
*pp=Rand.left+rund(x*pixprosample); pp++;
*pp=rund(getsample(this,log2addr(j))*pixprolsb+pixmitte); pp++;
j+=BlockAlign;
}
polycount=i;
// Alte Samples:
j=WaveHdr.dwUser;
x=(int)(j/BlockAlign);
j+=byteoffset;
for (i=0; j<WaveHdr.dwBufferLength; i++,x++) {
*pp=Rand.left+rund(x*pixprosample); pp++;
*pp=rund(getsample(this,log2addr(j))*pixprolsb+pixmitte); pp++;
j+=BlockAlign;
}
polyc2=i;
}
/********************************************
** Das Drama mit den veränderlichen Menüs **
********************************************/
void StringUndZahl(PTSTR buf, UINT id, float z, WORD iso, PCTSTR einheit) {
LoadString(HInstance,id,buf,64);
Float2String(AfterTab(buf),z,iso,einheit);
}
void Markiere(HMENU m,UINT idc,float z) {
// Im Menü mit gestuften Gleitkommazahlen fuhrwerken...
for (int idx=0; idx<elemof(Reihe); idx++) {
UINT flags= Reihe[idx]==z ? MFT_RADIOCHECK|MF_CHECKED : MF_UNCHECKED;
CheckMenuItem(m,idx+idc,flags);
}
}
void Markiere(HMENU m, UINT pos, UINT idc, float z) {
// Dito für Popup-Menüs
Markiere(GetSubMenu(m,pos),idc,z);
}
/*
void ZEIT::LadeXAblenkString(PTSTR buf) {
if (flags&OF_XY) StringUndZahl(buf,203,rate,MAKEWORD(0,3),T("Sa/s"));
else StringUndZahl(buf,202,div,MAKEWORD(-4,0),T("s/div"));
}*/
bool ZEIT::SetAblenkung(PCTSTR s) {
float z;
EINHEIT e;
z=div;
int i=String2UpDown(s,1,1); // Hier: nur große Schritte, vorerst
if (i>0) NextFloat(z,i,max);
else if (i<0) NextFloat(z,i,min);
else if (!String2Float(s,z,e)) z=0;
else if (e[0] && lstrcmp(e,T("s"))) z=0; // falsche Einheit
return SetAblenkung(z);
}
bool ZEIT::SetAblenkung(float z) {
// Bei ungültigem <div> erfolgt eine Initialisierung zumindest mit <min>!
// liefert NUR "false" bei versuchtem Übergang von "gültig" zu "ungültig"!
if (z<=0) {
if (div>0) return false; // Kleiner/gleich Null darf es nicht sein!
z=min; // Initialbedingung und keine .INI
}
if (div==z) return true; // Keine Änderung
div=z;
SetRate(samples/(div*Kaestel.x));
return InfoNeu(WAS_XABLENK); // setzt die Samplerate
}
bool ZEIT::SetRate(PCTSTR s) {
float z;
EINHEIT e;
if (!String2Float(s,z,e)) z=0;
else if (e[0] && lstrcmp(e,T("Sa/s")) && lstrcmp(e,T("Hz"))) z=0;
return SetRate(z);
}
bool ZEIT::SetRate(float z) {
if (z<=0) {
if (rate>0) return false;
z=maxrate;
}
if (rate==z) return true;
quelle->RelayMsg(Q_SETRATE,&z);
if (rate==z) return true; // immer noch unverändert!
rate=z;
if (z<minrate || z>maxrate) LadeListenVonRateMinMax();
// Neue Grenzen! Bei Soundkarten passiert so etwas
return InfoNeu(WAS_RATE);
}
bool ZEIT::SetAnfang(PCTSTR s) {
float z;
EINHEIT e;
if (!String2Float(s,z,e)) return false;
if (e[0] && lstrcmp(e,T("s"))) return false;
// Hier fehlt noch eine Umrechnung von <div> oder <%>
return SetAnfang(z);
}
bool ZEIT::SetAnfang(float z) {
anfang=z;
// Hier fehlen Bereichstests!
return InfoNeu(WAS_XANF);
}
bool ZEIT::SetSamples(PCTSTR s) {
DWORD sa;
if (_stscanf(s,T("%lu"),&sa)!=1) return false;
return SetSamples(sa);
}
bool ZEIT::SetSamples(DWORD sa) {
samples=sa;
// weiter: Speicher reallozieren usw!
return InfoNeu(WAS_SAMPLES);
}
bool String2Farbe(PCTSTR s, COLORREF &c) {
bool swap=false;
if (!s) return false;
if (s[0]=='#') s++,swap=true; // HTML-Syntax
if (_stscanf(s,T("%lx"),&c)!=1) return false;
if (swap) {
((PBYTE)&c)[3]=((PBYTE)&c)[2]; // Farbangabe BGR (Windows) statt RGB (HTML)
((PBYTE)&c)[2]=((PBYTE)&c)[0];
((PBYTE)&c)[0]=((PBYTE)&c)[3];
((PBYTE)&c)[3]=0;
}
return true;
}
bool Farbe2String(PTSTR s, COLORREF &c) {
if (!s) return false; // HTML-kompatibel konvertieren
_sntprintf(s,32,T("#%02X%02X%02X"),((PBYTE)&c)[0],((PBYTE)&c)[1],((PBYTE)&c)[2]);
return true;
}
/* DOPPELTER KODE: Identisch mit KANAL::SetFarbe */
bool ZEIT::SetFarbe(PCTSTR s) {
COLORREF c;
return (bool)(String2Farbe(s,c) && SetFarbe(c));
}
bool ZEIT::SetFarbe(COLORREF c) {
if (c==BackColor) return false;
if (c==GridColor) return false;
if (farbe==c) return true; // Kurzschluss wenn gleich
farbe=c;
return InfoNeu(WAS_FARBE); // FEHLT: Paletteneintrag beschaffen
}
static TCHAR sZeitVar[]=T("Ablenkung\0Rate\0Abtastwerte\0Farbe\0");
static HSZ hszZeitVar[3];
bool ZEIT::SetVar(int i, PCTSTR s) {
switch (i) {
case 0: return SetAblenkung(s);
case 1: return kn?SetAnfang(s):SetRate(s);
case 2: return SetSamples(s);
case 3: return SetFarbe(s);
}
return false;
}
bool ZEIT::SetVar(PCTSTR n, PCTSTR s) {
return SetVar(String2Index(sZeitVar,n),s);
}
bool ZEIT::SetVar(HSZ hsz,PCTSTR s) {
return SetVar(hsz2Index(hszZeitVar,elemof(hszZeitVar),hsz),s);
}
bool ZEIT::GetVar(int i, PTSTR s) {
switch (i) {
case 0: Float2String(s,div,MAKEWORD(-4,0),T("s"),5); break;
case 1: if (kn) Float2String(s,anfang,MAKEWORD(-4,0),T("s"),5);
else Float2String(s,rate,MAKEWORD(0,3),T("Sa/s"),5);
break;
case 2: wsprintf(s,T("%lu"),samples); break;
case 3: Farbe2String(s,farbe); break;
default: return false;
}
return true;
}
bool ZEIT::GetVar(PCTSTR n, PTSTR s) {
return GetVar(String2Index(sZeitVar,n),s);
}
bool ZEIT::GetVar(HSZ hsz,PTSTR s) {
return GetVar(hsz2Index(hszZeitVar,elemof(hszZeitVar),hsz),s);
}
/* DREIFACHER KODE: Identisch mit KANAL::SetFarbe */
bool TRIGG::SetFarbe(PCTSTR s) {
COLORREF c;
return (bool)(String2Farbe(s,c) && SetFarbe(c));
}
bool TRIGG::SetFarbe(COLORREF c) {
if (c==BackColor) return false;
if (c==GridColor) return false;
if (farbe==c) return true; // Kurzschluss wenn gleich
farbe=c;
return InfoNeu(WAS_FARBE); // FEHLT: Paletteneintrag beschaffen
}
static TCHAR sTriggerVar[]=T("Flanke\0Quelle\0Pegel\0Kopplung\0Prätrigger\0Farbe\0");
static HSZ hszTriggerVar[6];
bool TRIGG::SetVar(int i, PCTSTR s) {
switch (i) {
case 0: return SetFlanke(s);
case 1: return SetQuelle(s);
case 2: return SetPegel(s);
case 3: return SetKopplung(s);
case 4: return SetPretrig(s);
case 5: return SetFarbe(s);
}
return false;
}
bool TRIGG::SetVar(PCTSTR n, PCTSTR s) {
return SetVar(String2Index(sTriggerVar,n),s);
}
bool TRIGG::SetVar(HSZ hsz,PCTSTR s) {
return SetVar(hsz2Index(hszTriggerVar,elemof(hszTriggerVar),hsz),s);
}
bool TRIGG::GetVar(int i, PTSTR s) {
switch (i) {
case 0: wsprintf(s,T("%c"),sTrigFlanke[flanke]); break;
case 1: wsprintf(s,sInt,quelle); break; // hier: numerisch
case 2: Float2String(s,pegel,0,T("div")); break; // hier: in "div"
case 3: lstrcpy(s,Index2String(sTrigKopplung,kopplung)); break;
case 4: wsprintf(s,T("%d %%"),pretrig); break;
case 5: Farbe2String(s,farbe); break;
default: return false;
}
return true;
}
bool TRIGG::GetVar(PCTSTR n, PTSTR s) {
return GetVar(String2Index(sTriggerVar,n),s);
}
bool TRIGG::GetVar(HSZ hsz,PTSTR s) {
return GetVar(hsz2Index(hszTriggerVar,elemof(hszTriggerVar),hsz),s);
}
void ZEIT::LadeListenVonRateMinMax() {
SYSINFO si;
quelle->RelayMsg(Q_GETSYSINFO,&si);
minrate=si.rateminmax[0];
maxrate=si.rateminmax[1];
LadeRateListe();
min=10/maxrate; // wären 10 Samples pro div, bissel wenig!?
max=(float)si.depth/Kaestel.x/minrate;
if (si.flags&Q_CONTINUOUS) max=100; // 100 Sekunden pro Teilstrich??
LadeAblenkListe();
}
void ZEIT::LadeVorgabe() {
samples=1000; // Harte, zurzeit idiotische Vorgabe!!
LadeListenVonRateMinMax();
PCTSTR p; int i;
for (p=sZeitVar,i=0; *p; p+=lstrlen(p)+1,i++) {
TCHAR buf[32];
GetString(name,p,sLeer,buf,elemof(buf));
SetVar(i,buf);
}
// InfoNeu(WAS_XABLENK|WAS_RATE);
}
#define WM_INFONEU (WM_USER+1234) // wParam=WAS-Maske, lParam=this-Strukturptr.
bool ZEIT::InfoNeu(BYTE was) {
if (submenu) {
TCHAR s1[32],s2[32];
if (was&(WAS_XABLENK|WAS_RATE)) {
TCHAR buf[64];
Float2String(s1,div,MAKEWORD(-4,0),T("s"));
if (kn) {
Float2String(s2,anfang,MAKEWORD(-4,0),T("s"));
}else{
Float2String(s2,rate,MAKEWORD(0,3),T("Sa"),5); // Langform
}
wnsprintf(buf,elemof(buf),T("%s @ %s"),s1,s2);
SetMenuStringAfterTab(OsziMenu,kn,buf);
}
if (was&WAS_XABLENK) {
Markiere(submenu,SUB_XABLENK,IDC_XABLENK,div);
lstrcat(s1,T("/div"));
SetMenuStringAfterTab(submenu,SUB_XABLENK,s1);
// if (!(was&WAS_RATE)) SetRate(samples/(div*Kaestel.x)); // Rekursionsgefahr?
}
if (was&WAS_RATE) {
Markiere(submenu,SUB_RATE,IDC_RATE,rate);
lstrcat(s2,T("/s"));
SetMenuStringAfterTab(submenu,SUB_RATE,s2);
}
if (was&WAS_FARBE) {
Farbe2String(s1,farbe);
SetMenuStringAfterTab(submenu,SUB_XFARBE,s1);
}
}
if (anzeige) anzeige->Update(was);
if (hZeitDlg && (unsigned)ZeitDlgCurSel==kn)
PostMessage(hZeitDlg,WM_INFONEU,was,(LPARAM)this);
return true; // liefert stets true als bequeme Rückgabe für SetXxx()
}
bool TRIGG::InfoNeu(BYTE was) {
if (submenu) {
if (was&WAS_TMODUS) {
MenuRadioNextDefault(submenu,SUB_TMODUS,modus);
SetMenuStringAfterTab(submenu,SUB_TMODUS,Index2String(sTrigModus,modus));
}
if (was&WAS_TQUELLE) {
TRIGINFO ti;
::quelle->RelayMsg(Q_GETTRIGINFO,&ti);
MenuEnable(submenu,SUB_TFLANKE,ti.edges);
MenuEnable(submenu,SUB_TKOPPLUNG,ti.couplings);
MenuRadioNextDefault(submenu,SUB_TQUELLE,quelle);
SetMenuStringAfterTab(submenu,SUB_TQUELLE,k->name);
}
if (was&WAS_TFLANKE) {
MenuRadioNextDefault(submenu,SUB_TFLANKE,flanke);
SetMenuStringAfterTab(submenu,SUB_TFLANKE,flanke?T("v"):T("^"));
}
if (was&WAS_TKOPPLUNG) {
MenuRadioNextDefault(submenu,SUB_TKOPPLUNG,kopplung);
SetMenuStringAfterTab(submenu,SUB_TKOPPLUNG,Index2String(sTrigKopplung,kopplung));
}
if (was&(WAS_TMODUS|WAS_TQUELLE|WAS_TFLANKE|WAS_TKOPPLUNG)) {
TCHAR s[32];
wnsprintf(s,elemof(s),T("%s %c%s %s"),
Index2String(sTrigModus,modus),
flanke?'-':'+',
k->name,
Index2String(sTrigKopplung,kopplung));
SetMenuStringAfterTab(OsziMenu,NumZeitMenu,s);
}
if (was&WAS_TPEGEL) {
TCHAR s[32];
GetVar(2,s);
SetMenuStringAfterTab(submenu,SUB_TPEGEL,s);
}
if (was&WAS_TPRETRIG) {
TCHAR s[32];
GetVar(4,s);
SetMenuStringAfterTab(submenu,SUB_TPRETRIG,s);
}
if (was&WAS_FARBE) {
TCHAR s[32];
Farbe2String(s,farbe);
SetMenuStringAfterTab(submenu,SUB_TFARBE,s);
}
}
if (anzeige) anzeige->Update(was);
if (kreuz) kreuz->Update(was);
return true; // liefert stets true als bequeme Rückgabe für SetXxx()
}
void TRIGG::LadeVorgabe() {
TRIGINFO ti;
::quelle->RelayMsg(Q_GETTRIGINFO,&ti);
quelle=0;
// FEHLT: Menüs erstellen (gehört eher zum Konstruktor!)
PCTSTR p; int i;
for (p=sTriggerVar,i=0; *p; p+=lstrlen(p)+1,i++) {
TCHAR buf[32];
GetString(name,p,sLeer,buf,elemof(buf));
SetVar(i,buf);
}
InfoNeu(0xFF); // alles
}
bool ZEIT::RetteVorgabe() {
bool r=true;
PCTSTR p; int i;
for (p=sZeitVar,i=0; *p; p+=lstrlen(p)+1,i++) {
TCHAR buf[32];
GetVar(i,buf);
r=bool(WriteString(name,p,buf)!=0 && r);
}
return r;
}
bool TRIGG::RetteVorgabe() {
bool r=true;
PCTSTR p; int i; // Eigentlich ein Iterator fällig!
for (p=sTriggerVar,i=0; *p; p+=lstrlen(p)+1,i++) {
TCHAR buf[32];
GetVar(i,buf);
r=bool(WriteString(name,p,buf)!=0 && r);
}
return r;
}
void ZEIT::Konstruktor(int kn) {
this->kn=kn;
idhigh=kn<<10;
div=0; // ungültiger Wert
rate=0; // noch ein ungültiger Wert
wnsprintf(name,elemof(name),T("%c"),'A'+kn);
submenu=GetSubMenu(OsziMenu,0);
if (kn) submenu=CopyPopupMenu(submenu,idhigh);
TCHAR sZeitbasis[32],buf[32];
LoadString(HInstance,202,sZeitbasis,elemof(sZeitbasis));
wnsprintf(buf,elemof(buf),sZeitbasis,name); // Name der Zeitbasis einsetzen
if (kn<10) InsertAmp(buf,elemof(buf),-1);
(kn?InsertMenu:ModifyMenu)(OsziMenu,kn,MF_BYPOSITION|MF_POPUP,(UINT)submenu,buf);
anzeige=new ZEITANZEIGE(this); // Verlinkung herstellen
LadeVorgabe();
}
void TRIGG::LadeQuelleMenu() {
TCHAR s[64];
SYSINFO si;
::quelle->RelayMsg(Q_GETSYSINFO,&si);
HMENU m=CreatePopupMenu();
TCHAR fmt[32];
LoadString(HInstance,208/*"Kanal %s"*/,fmt,elemof(fmt));
for (int i=0; i<si.numtrig; i++) {
if (i<si.numchan) {
if (i>MaxKanalMenu) continue; // keinen Menüeintrag erzeugen!
if (i==MaxKanalMenu) LoadString(HInstance,212/*"&Weitere Kanäle..."*/,s,elemof(s));
else{
wnsprintf(s,elemof(s),fmt,::kanal[i].name);
if (i<10) InsertAmp(s,elemof(s),-1); // vor letztes (Zahl-)Zeichen
}
}else LoadString(HInstance,204/*"&Extern"*/,s,elemof(s));
InsertMenu(m,(UINT)-1,MF_BYPOSITION,IDC_TQUELLE+(i<<10),s);
}
SetMenuPopup(submenu,SUB_TQUELLE,m);
}
void TRIGG::Konstruktor(int) {
wnsprintf(name,elemof(name),T("%c"),'T');
submenu=GetSubMenu(OsziMenu,NumZeitMenu);
modus=TM_AUTO;
quelle=0;
LadeQuelleMenu();
flanke=TF_STEIG;
kopplung=TK_DC;
SetQuelle(0); // besser: vom Oszi holen!
pretrig=0;
pegel=0;
LadeVorgabe();
anzeige=new TRIGGERANZEIGE(this); // Verlinkung herstellen
kreuz=new KREUZ(this);
InfoNeu(0xFF);
}
void ZEIT::Destruktor() {
RetteVorgabe();
delete anzeige;
if (kn) DeleteMenu(OsziMenu,kn,MF_BYPOSITION);
}
void TRIGG::Destruktor() {
RetteVorgabe();
if (quelle>=numkanal) delete k;
delete kreuz;
delete anzeige;
}
void ZEIT::LadeAblenkListe() {
// Aufrufen bei LadeXVorgabe
HMENU m;
m=CreatePopupMenu();
float z;
float e;
int idx;
TCHAR buf[64];
z=min;
e=max;
for (idx=NextFloat(z,0); z<=e; idx=NextFloat(z,1)) {
Float2String(buf,z,MAKEWORD(-4,0),T("s"),5); // von ps bis s
InsertMenu(m,0,MF_BYPOSITION|MFT_RADIOCHECK,IDC_XABLENK+idx+idhigh,buf); // Rückwärts einsortieren!
}
AppendAndereToMenu(m,elemof(Reihe)+IDC_XABLENK);
SetMenuPopup(submenu,SUB_XABLENK,m);
}
void AppendRateToMenu(HMENU m, UINT id, float z) {
// Hängt Samplerate <z> an Popup-Menü <m> mit ID <id> an
// Hilfsfunktion für ZEIT::LadeRateListe
TCHAR buf[32];
Float2String(buf,z,MAKEWORD(0,3),T("Sa"),5); // hier: Kurz-Einheit!
InsertMenu(m,(UINT)-1,MF_BYPOSITION|MFT_RADIOCHECK,id,buf); // Vorwärts sortieren
}
void ZEIT::LadeRateListe() {
// Erzeugt Popup-Menü mit Sampleraten, inklusive absolut minimaler
// und maximaler, soweit unterschiedlich von der 1-2-5-Reihe
// Aufrufen bei ZEIT::LadeVorgabe und wenn sich (plötzlich)
// minimale und maximale Samplerate ändert(e)
HMENU m;
int idx;
float z;
m=CreatePopupMenu();
z=minrate;
idx=NextFloat(z,0);
if (z!=minrate) AppendRateToMenu(m,IDC_RATE+elemof(Reihe)+3+idhigh,minrate);
for (; z<=maxrate; idx=NextFloat(z,1)) {
AppendRateToMenu(m,IDC_RATE+idx+idhigh,z);
if (z==maxrate) goto keinmax;
}
if (minrate!=maxrate) AppendRateToMenu(m,IDC_RATE+elemof(Reihe)+4+idhigh,maxrate);
keinmax:
AppendAndereToMenu(m,IDC_RATE+elemof(Reihe)+idhigh);
SetMenuPopup(submenu,SUB_RATE,m);
}
bool TRIGG::SetModus(MODUS m) {
if (m>TM_SINGLE) return false;
if (modus==TM_AUTO) KillTimer(MainWnd,3);
modus=m;
if (modus==TM_AUTO) SetTimer(MainWnd,3,500,NULL);
return InfoNeu(WAS_TMODUS);
}
bool TRIGG::SetModus(PCTSTR s) {
int m=String2Index(sTrigModus,s);
if (m<0 && _stscanf(s,sInt,&m)!=1) return false;
return SetModus((MODUS)m);
}
bool TRIGG::SetFlanke(FLANKE f) {
if ((unsigned)f>=2) return false;
if (flanke==f) return true;
flanke=f;
TRIG t;
t.what=TRIG_EDGE;
t.edge=(BYTE)f;
::quelle->RelayMsg(Q_SETTRIG,&t); // kann simuliert werden...???
return InfoNeu(WAS_TFLANKE);
}
bool TRIGG::SetFlanke(PCTSTR s) {
int fl;
if (!s) return false;
if (!s[1]) {
if (*s==sTrigFlanke[0]) {fl=0; goto habs;}
if (*s==sTrigFlanke[1]) {fl=1; goto habs;}
}
if (_stscanf(s,sInt,&fl)!=1) return false;
habs:
return SetFlanke((FLANKE)fl);
}
bool TRIGG::SetQuelle(int q) {
if (quelle!=q) {
TRIG t;
t.what=TRIG_SOURCE;
t.source=(BYTE)q;
::quelle->RelayMsg(Q_SETTRIG,&t);
}
if (quelle>=numkanal) delete k; // Dummy-Kanal löschen
quelle=q;
if (quelle<numkanal) k=kanal+q; else{
k=new KANAL; // Dummy-Kanal (nicht in Liste)
lstrcpy(k->name,T("Ext"));
k->div=1;
k->praefixe=0;
lstrcpy(k->einheit,T("V"));
k->nulllinie=0;
k->farbe=farbe;
SetPegel(0.0);
}
return InfoNeu(WAS_TQUELLE);
}
bool TRIGG::SetQuelle(PCTSTR s) { // in div oder der Einheit des Kanals
int q=numkanal;
if (!lstrcmpi(s,T("Ext"))) goto habs;
if (kanal) for (q=0; q<numkanal; q++)
if (!lstrcmpi(s,kanal[q].name)) goto habs;
if (_stscanf(s,sInt,&q)!=1) return false;
habs:
return SetQuelle(q);
}
bool TRIGG::SetPegel(float p) { // in <div>
if (abs(rndint(p-0.5))>2*Kaestel.y) return false;
if (fabs(p)<1E-3) p=0; // Sollte sicher Null sein
if (!k) return false;
TRIG t;
t.what=TRIG_LEVEL;
t.level=rund(p*k->div/k->voltprolsb);
// SoftTrigger sofort setzen! Kontinuierliche Datenquellen brauchen's nicht
::quelle->RelayMsg(Q_SETTRIG,&t);
pegel=t.level*k->voltprolsb/k->div; // RÜCK-BERECHNUNG!
if (pegel==p) return true;
return InfoNeu(WAS_TPEGEL);
}
bool TRIGG::SetPegel(PCTSTR s) { // Sonderfälle: +, ++, +?
float p;
EINHEIT e;
int i=String2UpDown(s,1,10);
if (i) p=pegel+i*0.1F;
else{
String2Float(s,p,e);
if (*e) {
if (!lstrcmp(e,k->einheit)) {
p/=k->div; // Volt / Volt/div = div
}else if (lstrcmpi(e,T("div"))) return false;
}
}
return SetPegel(p);
}
bool TRIGG::SetKopplung(KOPPLUNG k) {
if ((unsigned)k>7) return false;
TRIG t;
t.what=TRIG_COUPLING;
t.coupling=(BYTE)k;
::quelle->RelayMsg(Q_SETTRIG,&t); // Simulation??
kopplung=k;
return InfoNeu(WAS_TKOPPLUNG);
}
bool TRIGG::SetKopplung(PCTSTR s) {
int k;
if (!s) return false;
k=String2Index(sTrigKopplung,s);
if (k<0 && _stscanf(s,sInt,&k)!=1) return false;
return SetKopplung((KOPPLUNG)k);
}
bool TRIGG::SetPretrig(int pt) {
if (pt<-100) return false;
if (pt>100) return false;
TRIG t;
t.what=TRIG_PRE;
t.pre=MulDiv(pt,(int)zeit->samples,100);
::quelle->RelayMsg(Q_SETTRIG,&t);
pretrig=pt;
return InfoNeu(WAS_TPRETRIG);
}
bool TRIGG::SetPretrig(PCTSTR s) {
int pt;
float t;
EINHEIT e;
pt=String2UpDown(s,1,10);
if (pt) pt+=pretrig;
else{
if (!String2Float(s,t,e)) return false;
pt=rund(t); // Wenn ohne Einheit, dann in Prozent
if (*e) {
if (!lstrcmp(e,T("s"))) {
pt=rund(t*100/zeit->div/Kaestel.x);
}else if (!lstrcmpi(e,T("div"))) {
pt=rund(t*100/Kaestel.x);
}else if (lstrcmp(e,T("%"))) return false;
}
}
return SetPretrig(pt);
}
void ZeigeSimul(HMENU m, UINT idx, BYTE echt) {
TCHAR s[64],*p;
GetMenuString(m,idx,s,elemof(s),MF_BYPOSITION);
p=AfterTab(s);
if (echt) *--p=0; // ohne Simulation
else LoadString(HInstance,213,p,int(s+elemof(s)-p)); //"simuliert"
SetMenuString(m,idx,s);
}
/****************************************************************************
** Wegen der fehlenden with-Anweisung ersparen Objekte in C++ tatsächlich **
** Schreibarbeit; deshalb fortan Y-bezogenes als Memberfunktionen **
****************************************************************************/
static TCHAR sKanalVar[]=T("Name\0Tastkopf\0Ablenkung\0Kopplung\0Nulllinie\0Farbe\0");
static HSZ hszKanalVar[6];
void KANAL::LadeVorgabe() {
CHANINFO ci;
ci.ch=(BYTE)kn;
quelle->RelayMsg(Q_GETCHANINFO,&ci);
min=ci.voltminmax[0]*5; // minimal: 5 Stufen pro div Auflösung
max=ci.voltminmax[1]*FullScale*numkanal; // maximal: alle Traces geradeso
byteoffset=ci.byteoffset;
maske=ci.mask;
if (submenu) {
HMENU m=GetSubMenu(submenu,SUB_YKOP);
// DC-Kopplung kann nicht aus AC simuliert werden!
EnableMenuItem(m,0,
ci.couplings&CHANINFO_DC?MF_ENABLED|MF_BYPOSITION:MF_GRAYED|MF_BYPOSITION);
ZeigeSimul(m,1,(BYTE)(ci.couplings&CHANINFO_AC));
ZeigeSimul(m,2,(BYTE)(ci.couplings&CHANINFO_GND));
}
CHAN c;
c.ch=(BYTE)kn;
c.what=0; // Nur lesen
quelle->RelayMsg(Q_SETCHAN,&c);
kopplung=c.coupling;
voltprolsb=c.volt;
voltoffset=c.dcoffset;
// div=chan.volt*fullscale/fTastkopf/Kaestel.y;
// NextFloat(div,0);
TCHAR buf[32];
PCTSTR p;
int i;
for (p=sKanalVar,i=0; *p; p+=lstrlen(p)+1,i++) {
GetString(name,p,sLeer,buf,elemof(buf));
SetVar(i,buf);
}
idhigh=kn<<10;
if (poly) delete poly;
poly=new POINT[(int)zeit->samples];
}
bool KANAL::RetteVorgabe() {
bool r=true;
int i;
PCTSTR p;
for (p=sKanalVar,i=0; *p; p+=lstrlen(p)+1,i++) {
TCHAR buf[32];
GetVar(i,buf);
r=bool(WriteString(name,p,buf)!=0 && r);
}
return r;
}
void KANAL::LadeAblenkListe() {
// AUFRUFEN bei Änderung von Datenquelle oder Tastteiler, oder Vorzeichen!
HMENU m=CreatePopupMenu();
float z=min/*fTastkopf*/;
float e=max/*fTastkopf*/;
int idx;
TCHAR buf[64];
for (idx=NextFloat(z,0); z<=e; idx=NextFloat(z,+1)) {
Float2String(buf,div<0?-z:z,praefixe,einheit);
InsertMenu(m,0,MF_BYPOSITION|MFT_RADIOCHECK,idx+IDC_YABL+idhigh,buf);
}
SetMenuPopup(submenu,SUB_YABL,m);
}
int KANAL::GetTastkopfIndex() { // fürs Menü usw.
if (lstrcmp(einheit,T("V"))) return 2;
if (fTastkopf==1) return 0;
if (fTastkopf==10) return 1;
return 2;
}
bool KANAL::SetNamen(PCTSTR s) {
TCHAR buf[32];
int l=lstrlen(s);
if (l>=elemof(sNamen)) return false; // zu lang
if (!lstrcmp(s,sNamen)) return true; // Gleich geblieben
if (!::GetNameList(s,buf,NULL)) return false; // ungültige Syntax
lstrcpy(sNamen,s);
return InfoNeu(WAS_YNAM);
}
bool KANAL::SetTastkopf(PCTSTR s) {
float z,n;
EINHEIT e;
int i,j,k,l=lstrlen(s);
if (l>=elemof(sTastkopf)) goto falsch; // zu lang zum Speichern
lstrcpy(e,T("V"));
e[ELEN-1]=0; // zwangsterminieren
i=_stscanf(s,T("%f:%f%n %") ELENSTR T("[^ -\x40[-`{-\x7F]/V%n"),&z,&n,&j,e,&k);
if (i==2 && j==l) goto ok;
if (i==3 && k==l) goto ok;
// 2. Versuch der Interpretation
i=_stscanf(s,T("%f%") ELENSTR T("[^ -@[-`{-\x7F]:%fV%n"),&z,e,&n,&k);
if (i==3 && k==l) {
ok:
if (!n || !z || !e[0]) goto falsch;
lstrcpy(einheit,e);
praefixe=wPrefix; // vom Setup (normalerweise p..G)
// Die Begrenzung der Vorsätze ist einheiten-abhängig, etwa:
// keine Kilosekunden (max=0), Megagramm (max=1, Tonne keine hübsche
// Alternative, denn es gibt keine Kilotonne, außer bei Sprengstoff),
// weder Milli- noch Kilomol (min=max=0), ebenso bei (Bruttoregister)Tonne usw.
if (!e[1]) switch (e[0]) {
case 's': ((BYTE*)&praefixe)[1]=0; break; // Sekunden (keine Kilosek.)
case 'm': // Meter (keine Megameter)
case 'g': ((BYTE*)&praefixe)[1]=1; break; // Gramm (keine Megagramm)
case 't': praefixe=0; // Tonne (keine Vorsätze)
}
z/=n; // rechenwirksames Tastkopf-Verhältnis (10:1->10)
n=fTastkopf; // vorheriges Tastkopf-Verhältnis retten
fTastkopf=z; // neues Tastkopf-Verhältnis setzen
if (n) {
z/=n; // z/n = Verhältnis beider (1:1->10:1 -> z=10)
min*=z; max*=z; // Grenzen für (sinnvolle) Koeffizienten ändern
SetAblenkung(div*z); // "div" ändern, da voltprolsb konstant bleiben soll
}
lstrcpy(sTastkopf,s);
return InfoNeu(WAS_YVOR);
}
falsch:
if (!fTastkopf) SetTastkopf(T("1:1")); // jaja, Rekursion!
return false; // 2 Zahlen müssen es mindestens sein!
}
// Kopplungs-Strings in Zahl 0..2 umwandeln
bool IsKopplung1(PCTSTR s, UINT&v) {
int k=String2Index(sKopplung1,s);
if (k<0) return false;
v=k;
return true;
}
bool IsKopplung2(PCTSTR s, UINT&v) {
if (!s[0]) return false;
if (s[1]) return false;
PCTSTR p=StrChr(sKopplung2,s[0]);
if (!p) return false;
v=UINT(p-sKopplung2);
return true;
}
bool KANAL::SetAblenkung(PCTSTR s) {
// Bei ungültigem <div> erfolgt auf jeden Fall eine Initialisierung
float z=0;
EINHEIT e;
int l=lstrlen(s);
UINT k=kopplung;
if (l) {
IsKopplung2(s+--l,k); // SCHIFFBRUCH BEI DBCS!!!
z=div;
int i=String2UpDown(s,1,1);
if (i>0) NextFloat(z,i,max/*fTastkopf*/);
else if (i<0) NextFloat(z,i,min/*fTastkopf*/);
else if (!String2Float(s,z,e)) z=0;
else if (e[0] && lstrcmp(e,einheit)) z=0; // falsche Einheit
}
SetKopplung(k);
return SetAblenkung(z);
}
bool KANAL::SetAblenkung(float z) {
// Negative Zahlen führen entsprechend zur Invertierung
CHAN chan;
BYTE was=WAS_YABL;
if (!z) {
if (div) return false; // Null darf es nicht werden!
z=max; // max = Linksanschlag
}
if (div==z) return true;
if (div*z<0) was|=WAS_YINV; // Invertierung dazu
div=z;
chan.what=CHAN_VOLT;
chan.ch=(BYTE)kn;
chan.volt=z*Kaestel.y/fTastkopf/FullScale;
quelle->RelayMsg(Q_SETCHAN,&chan);
voltprolsb=chan.volt; // rücklesen, zur Skalierung
voltoffset=chan.dcoffset;
if (trig && trig->k==this) trig->SetPegel(trig->pegel);
return InfoNeu(was);
}
bool KANAL::SetNulllinie(PCTSTR s) {
// wird vom Dialog und von DDEPOKE aufgerufen
float z=nulllinie;
EINHEIT e;
int i=String2UpDown(s,1,1);
if (i) z+=i; else{
if (!String2Float(s,z,e)) return false;
if (*e) {
if (!lstrcmp(e,einheit)) z/=div;
else if (lstrcmpi(e,T("div"))) return false; // falsche Einheit
}
}
return SetNulllinie(z); // in div
}
bool KANAL::SetNulllinie(float z) {
CHAN c;
if (nulllinie==z) return true;
nulllinie=z;
c.what=CHAN_DCOFFSET;
c.ch=(BYTE)kn;
c.dcoffset=z*div;
quelle->RelayMsg(Q_SETCHAN,&c); // Falls die Quelle einen Offset beherrscht...
voltoffset=c.dcoffset; // rücklesen
if (trig && trig->k==this && trig->kreuz) trig->kreuz->Update(WAS_TPEGEL);
return InfoNeu(WAS_YNUL);
}
bool KANAL::SetKopplung(PCTSTR s) {
UINT k;
if (!s) return false;
if (!IsKopplung1(s,k) && !IsKopplung2(s,k)) return false;
return SetKopplung(k);
}
bool KANAL::SetKopplung(UINT k) {
if (k>=3) return false; // Etwas anderes als DC,AC,GND gibt es nicht
if (kopplung==k) return true; // (Noch nicht!)
CHAN c;
c.ch=(BYTE)kn;
c.what=(BYTE)CHAN_COUPLING;
c.coupling=(BYTE)(k);
if (!quelle->RelayMsg(Q_SETCHAN,&c)) return false;
kopplung=k;
if (k==CHAN_DC && c.coupling==CHAN_AC) kopplung=CHAN_AC;
// DC-Kopplung kann nicht simuliert werden! Menüpunkt ist grau.
getsample=GetSampleDc;
if (k==CHAN_AC && c.coupling==CHAN_DC) getsample=GetSampleAc;
// Simulation der AC-Kopplung mittels GetSampleAc
if (k==CHAN_GND && c.coupling!=CHAN_GND)
getsample=GetSampleGnd;
// Simulation der GND-Kopplung durch brutales Null-Liefern
return InfoNeu(WAS_YKOP);
} // Andere Kopplungen, wie Klemmung+, Klemmung- usw. sind denkbar.
bool KANAL::SetFarbe(PCTSTR s) {
COLORREF c;
return (bool)(String2Farbe(s,c) && SetFarbe(c));
}
bool KANAL::SetFarbe(COLORREF c) {
if (c==BackColor) return false;
if (c==GridColor) return false;
if (farbe==c) return true; // Kurzschluss wenn gleich
farbe=c;
return InfoNeu(WAS_FARBE); // FEHLT: Stift, Pinsel, Paletteneinträge beschaffen
}
bool GRUPPE::SetVar(int i, PCTSTR s) {
bool r=true;
for (int k=0; k<numkanal; k++)
if (kanal[k].HatName(name)) r=bool(kanal[k].SetVar(i,s) && r);
return r;
}
bool GRUPPE::GetVar(int i, PTSTR s) {
// Die Extrawurst für die Offsets steht hier noch aus!
bool r=true;
bool first=true;
TCHAR buf[32];
for (int k=0; k<numkanal; k++) if (kanal[k].HatName(name)) {
r=bool(kanal[k].GetVar(i,buf) && r);
if (first) {
lstrcpy(s,buf);
first=false;
}else if (lstrcmp(s,buf)) {
lstrcat(s,T("?")); // Ungleiche Werte: markieren
return r; // Schleife verlassen
}
}
return r;
}
bool KANAL::SetVar(int i, PCTSTR s) {
switch (i) {
case 0: return SetNamen(s);
case 1: return SetTastkopf(s);
case 2: return SetAblenkung(s);
case 3: return SetKopplung(s);
case 4: return SetNulllinie(s);
case 5: return SetFarbe(s);
}
return false;
}
bool KANAL::SetVar(PCTSTR n, PCTSTR s) {
return SetVar(String2Index(sKanalVar,n),s);
}
bool KANAL::SetVar(HSZ hsz,PCTSTR s) {
return SetVar(hsz2Index(hszKanalVar,elemof(hszKanalVar),hsz),s);
}
bool KANAL::GetVar(int i, PTSTR s) {
switch (i) {
case 0: lstrcpy(s,sNamen); break;
case 1: lstrcpy(s,sTastkopf); break;
case 2: Float2String(s,div,praefixe,einheit); break;
case 3: lstrcpy(s,Index2String(sKopplung1,kopplung)); break;
case 4: Float2String(s,nulllinie,0,T("div")); break;
case 5: Farbe2String(s,farbe); break;
default: return false;
}
return true;
}
bool KANAL::GetVar(PCTSTR n, PTSTR s) {
return GetVar(String2Index(sKanalVar,n),s);
}
bool KANAL::GetVar(HSZ hsz,PTSTR s) {
return GetVar(hsz2Index(hszKanalVar,elemof(hszKanalVar),hsz),s);
}
void KANAL::GetInfoString(PTSTR s) {
// ASCII-Info-String zusammenstellen
TCHAR buf[32];
// Hier: <div> invertieren bei _echter_ Inversion
Float2String(buf,div,praefixe,einheit);
wnsprintf(s,elemof(s),T("%c %s %c"),
sTastkopf2[GetTastkopfIndex()],
buf,
sKopplung2[kopplung]);
}
bool KANAL::InfoNeu(BYTE was) {
TCHAR buf[64];
HMENU m; // Untermenü
if (submenu) { // Kanäle >16 oder so bekommen kein Untermenü...
if (was&(WAS_YVOR|WAS_YINV)) {
LadeAblenkListe();
}
if (was&WAS_YVOR) {
int j=GetTastkopfIndex();
m=GetSubMenu(submenu,SUB_YVOR);
CheckMenuRadioItem(m,0,2,j,MF_BYPOSITION);
if (j<2) j^=1; // bei User-Tastkopf bleibt die Vorgabe benutzerdefiniert
SetMenuDefaultItem(m,j,MF_BYPOSITION);
SetMenuStringAfterTab(submenu,SUB_YVOR,sTastkopf);
}
if (was&WAS_YINV) { // zieht stets WAS_YABL nach sich
bool neg=bool(div<0);
CheckMenuItem(submenu,SUB_YINV,
neg?MF_BYPOSITION|MF_CHECKED:MF_BYPOSITION|MF_UNCHECKED);
SetMenuStringAfterTab(submenu,SUB_YINV,neg?T("--"):T("+"));
}
if (was&WAS_YABL) {
Markiere(submenu,SUB_YABL,IDC_YABL+idhigh,div);
Float2String(buf,div,praefixe,einheit);
lstrcat(buf,T("/div"));
SetMenuStringAfterTab(submenu,SUB_YABL,buf);
}
if (was&WAS_YKOP) {
m=GetSubMenu(submenu,SUB_YKOP);
CheckMenuRadioItem(m,0,2,kopplung,MF_BYPOSITION);
SetMenuDefaultItem(m,GetMenuState(m,0,MF_BYPOSITION)==MF_GRAYED?kopplung==1?2:1:
kopplung==0?1:0,MF_BYPOSITION);
SetMenuStringAfterTab(submenu,SUB_YKOP,Index2String(sKopplung1,kopplung));
}
if (was&WAS_YNUL) {
CalcGraf(); Inval(false);
}
if (was&(WAS_YVOR|WAS_YABL|WAS_YKOP)) {
GetInfoString(buf);
SetMenuStringAfterTab(OsziMenu,NumZeitMenu+NumTrigMenu+kn,buf);
}
if (was&WAS_YNUL) {
Float2String(buf,nulllinie,0,T("div"));
SetMenuStringAfterTab(submenu,SUB_YNUL,buf);
}
if (was&WAS_YNAM) {
SetMenuStringAfterTab(submenu,SUB_YNAM,sNamen);
}
if (was&WAS_FARBE) {
Farbe2String(buf,farbe);
SetMenuStringAfterTab(submenu,SUB_YFARBE,buf);
}
}
if (was&WAS_YNAM) MacheGruppenListe();
if (masse) masse->Update(was);
if (anzeige) anzeige->Update(was);
// Zu tun: hKanalDlg, DDEADV aktualisieren
return true;
}
bool KANAL::HatName(PCTSTR n) { // Testet, ob Name enthalten ist
// Liefert <true> auch bei Kanalbezeichnung (bspw. "Y1")
// sowie wenn n=NULL (bedeutet hier "alle Kanäle")
TCHAR buf[32],*p;
if (!n) return true;
if (!*n) return true;
if (!lstrcmpi(n,name)) return true;
GetNameList(buf,NULL);
for (p=buf; *p; p+=lstrlen(p)+1) {
if (!lstrcmpi(p,n)) return true;
}
return false;
}
/****************************************************
** Dialoge (erst modusbehaftete, dann modusfreie) **
****************************************************/
static int LoadWinPos(HWND Wnd, PCTSTR key) {
// Liefert den Sichtbarkeitsstatus, auch bei Wnd=0, zum Wieder-Öffnen des Dialogs
// Lädt Position und, wenn gegeben und WS_THICKFRAME, auch die Größe
TCHAR buf[32];
WINDOWPLACEMENT wp;
RECT r;
int showCmd;
InitStruct(&wp,sizeof(wp));
if (Wnd) GetWindowPlacement(Wnd,&wp);
GetString(S_Position,key,sLeer,buf,elemof(buf));
switch (_stscanf(buf,sProzentI,&r.left,&r.top,&r.right,&r.bottom,&showCmd)) {
case 2: r.right=wp.showCmd; nobreak;
case 3: { // feste Fenstergröße
showCmd=r.right;
r.right=r.left+wp.rcNormalPosition.right-wp.rcNormalPosition.left;
r.bottom=r.top+wp.rcNormalPosition.bottom-wp.rcNormalPosition.top;
}break;
case 4: showCmd=wp.showCmd; nobreak;
case 5: if (!Wnd || GetWindowStyle(Wnd)&WS_THICKFRAME) break;
default: return SW_HIDE; // Alles andere ist Fehler!
} // (auch einem Fenster ohne WS_THICKFRAME die Größe setzen zu wollen)
if (Wnd) {
MoveRectIntoFullScreen(&r); // Bei Win32 angeblich nicht notwendig
CopyRect(&wp.rcNormalPosition,&r);
// if (showCmd) wp.showCmd=showCmd; // nie "hidden" setzen
SetWindowPlacement(Wnd,&wp);
}
return showCmd;
}
static void SaveWinPos(HWND Wnd, PCTSTR key, bool state=false) {
// Setzt den Sichtbarkeitsstatus auf SW_HIDE wenn state=false ist
// Speichert Position -- sowie Größe wenn Fenster WS_THICKFRAME hat
WINDOWPLACEMENT wp;
TCHAR buf[32],*p=buf;
if (!Wnd) return; // Fenster muss noch vorhanden sein!
InitStruct(&wp,sizeof(wp));
GetWindowPlacement(Wnd,&wp);
if (!state) wp.showCmd=0;
p+=wsprintf(p,sProzentI+9,wp.rcNormalPosition.left,wp.rcNormalPosition.top);
if (GetWindowStyle(Wnd)&WS_THICKFRAME) p+=wsprintf(p,sProzentI+8,
wp.rcNormalPosition.right,wp.rcNormalPosition.bottom);
if (wp.showCmd) wsprintf(p,sProzentI+11,wp.showCmd);
WriteString(S_Position,key,buf);
}
static INT_PTR CALLBACK AboutDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM){
switch (Msg) {
case WM_INITDIALOG: {
LoadWinPos(Wnd,T("Über"));
}return TRUE;
case WM_COMMAND: switch (LOWORD(wParam)){
case 1:
case 2:
SaveWinPos(Wnd,T("Über"));
EndDialog(Wnd,wParam);
}break;
}
return FALSE;
}
bool ScanXY(PCTSTR s, PPOINT p) {
// sscanf auf String der Form %dx%d ausführen und in p speichern,
// dabei werden beide Koordinaten auf 2..100 begrenzt
// Liefert false bei Fehler
int x,y;
if (_stscanf(s,T("%dx%d"),&x,&y)==2 && 2<=x && x<=100 && 2<=y && y<=100) {
p->x=x; p->y=y; return true;
}
return false;
}
void PrintXY(PTSTR s, const POINT *p) {
_sntprintf(s,32,T("%dx%d"),p->x,p->y);
}
bool ScanPr(PCTSTR s, WORD &pr) {
TCHAR a,e;
PTSTR pa,pe;
if (_stscanf(s,T("%c..%c"),&a,&e)!=2) return false;
pa=Prefix2Ptr(a); if (!pa) return false;
pe=Prefix2Ptr(e); if (!pe) return false;
if (pa>pe) return false;
pr=MAKEWORD(pa-ISO_Prefixes-6,pe-ISO_Prefixes-6);
return true;
}
void PrintPr(PTSTR s, WORD pr) {
_sntprintf(s,32,T("%c..%c"),ISO_Prefixes[(signed char)LOBYTE(pr)+6],
ISO_Prefixes[(signed char)HIBYTE(pr)+6]);
}
/**********************************
** Allgemeine Dialog-Behandlung **
**********************************/
#define WM_CHECKDIALOG (WM_USER+2) // konsequente Fortsetzung vom
#define WM_TAKEDIALOG (WM_USER+3) // WM_INITDIALOG-Konzept, oder?
// In beiden F„llen ist lParam=DWL_USER=Parameter von ...Dialog...Param
// WM_CHECKDIALOG muss im Fehlerfall mindestens eins von beiden tun:
// LOWORD(lParam) auf die fehlerhafte Fenster-ID setzen,
// HIWORD(lParam) auf die String-ID f�r MessageBox setzen
// und damit MyDlgHandler aufrufen - oder DWL_DLGRESULT setzen
#define IDAPPLY 3
#define IDAUTOAPPLY 4
#define IDTIMEDAPPLY 5
#define IDHELP 9
#define PROP_DDEADV 1 // DDEADVISE aktiv (normal gelb)
#define PROP_DIVERS 2 // Verschiedene Werte in Gruppe (normal grau)
#define PROP_ERROR 4 // Fehler (normal rot)
void ChangeProp(HWND Wnd, UINT and, UINT xor) {
UINT o=(UINT)GetProp(Wnd,MAKEINTATOM(atom));
UINT n=(o&and)^xor;
if (o==n) return;
if (n) SetProp(Wnd,MAKEINTATOM(atom),(HANDLE)n);
else RemoveProp(Wnd,MAKEINTATOM(atom));
InvalidateRect(Wnd,NULL,TRUE);
}
void ChangeProp(HWND Wnd, UINT id, UINT and, UINT xor) {
ChangeProp(GetDlgItem(Wnd,id),and,xor);
}
#define WM_SHOWITEMS (WM_USER+100)
#define AutoUpdate_id 3
#define AutoUpdate_ms 500
#define EN_STEPUP (EN_VSCROLL+0x10)
#define EN_STEPDOWN (EN_STEPUP+1)
WNDPROC DefEditProc;
LRESULT CALLBACK EditHook(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
UINT dir=0;
switch (Msg) {
case WM_KEYDOWN: switch (wParam) {
case VK_UP: dir=EN_STEPUP; break;
case VK_DOWN: dir=EN_STEPDOWN; break;
}break;
case WM_VSCROLL: switch (LOWORD(wParam)) {
case SB_LINEUP: dir=EN_STEPUP; break;
case SB_LINEDOWN: dir=EN_STEPDOWN; break;
}break;
case WM_DESTROY: ChangeProp(Wnd,0,0); break;
}
if (dir) {
SendMessage(CONTROLPARAMS3(WM_COMMAND,Wnd,dir));
return 0;
}
return CallWindowProc(DefEditProc,Wnd,Msg,wParam,lParam);
}
// Standard-Dialogprozedur für meine Dialoge (modal oder moduslos):
// * Position aus INI-Datei speichern/wiederherstellen
// * WM_ACTIVATE, WM_HELP behandeln
// * alle einzeiligen EDITs subklassifizieren für UpDown
BOOL MyDlgHandler(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
UINT id, HWND *wp, PTSTR PosKey) {
switch (Msg) {
case WM_INITDIALOG: {
SetWindowLongPtr(Wnd,DWLP_USER,lParam); // Compiler beruhigen (nur Win32)
if (PosKey) LoadWinPos(Wnd,PosKey);
for (HWND w=GetFirstChild(Wnd); w; w=GetNextSibling(w)) {
TCHAR s[8];
GetClassName(w,s,elemof(s));
if (!lstrcmpi(s,T("EDIT"))) DefEditProc=SubclassWindow(w,EditHook);
}
}return TRUE; // Fokus setzen _lassen_
case WM_ACTIVATE: if (wp) hKBHand=wParam?Wnd:0; break;
case WM_HELP: {
WinHelp(Wnd,HelpFileName,HELP_CONTEXTPOPUP,
MAKELONG(id,((LPCHELPINFO)lParam)->iCtrlId));
}break;
#ifdef WIN32
case WM_CTLCOLOREDIT: {
#else
case WM_CTLCOLOR: if (HIWORD(lParam)==CTLCOLOR_EDIT){
#endif
int i=(int)GetProp(Wnd,MAKEINTATOM(atom));
if (i) {
SetBkMode((HDC)wParam,TRANSPARENT);
return (BOOL)EditBrushes[bsf(i)]; // Nummer des höchsten Bits!
}
}return TRUE;
case WM_TIMER: if (wParam==AutoUpdate_id) {
KillTimer(Wnd,wParam);
SendMessage(Wnd,WM_COMMAND,IDTIMEDAPPLY,0);
}break;
case WM_CHECKDIALOG: SetWindowLongPtr(Wnd,DWLP_MSGRESULT,lParam); nobreak;
case WM_TAKEDIALOG: return TRUE; // kein DefDlgProc
case WM_COMMAND: {
switch (LOWORD(wParam)){
case IDHELP: WinHelp(Wnd,HelpFileName,HELP_CONTEXT,id); break;
case IDAUTOAPPLY: {
BOOL state=IsDlgButtonChecked(Wnd,IDAUTOAPPLY)-1;
EnableDlgItem(Wnd,IDAPPLY,state);
if (state) break;
}nobreak;
case IDTIMEDAPPLY:
case IDAPPLY:
case IDOK: {
lParam=SendMessage(Wnd,WM_CHECKDIALOG,0,GetWindowLongPtr(Wnd,DWLP_USER));
if (lParam) {
if (LOWORD(wParam)<=IDAPPLY) { // Ohne explizite Aufforderung nicht meckern
if (LOWORD(lParam)) SetEditFocus(Wnd,LOWORD(lParam));
if (HIWORD(lParam)) MBox(Wnd,HIWORD(lParam),MB_OK|MB_ICONEXCLAMATION);
}
break; // BUG: Nicht immer Edit!
}
SendMessage(Wnd,WM_TAKEDIALOG,0,GetWindowLongPtr(Wnd,DWLP_USER));
if (LOWORD(wParam)!=IDOK) break;
}
case IDCANCEL: {
if (PosKey) SaveWinPos(Wnd,PosKey);
if (wp) {
*wp=0;
DestroyWindow(Wnd);
}else{
EndDialog(Wnd,LOWORD(wParam));
}
}break;
}
switch (GET_WM_COMMAND_CMD(wParam,lParam)) {
case CBN_EDITCHANGE: {
TCHAR s[12];
GetClassName((HWND)lParam,s,elemof(s));
if (lstrcmpi(s,T("COMBOBOX"))) break;
}nobreak;
case EN_CHANGE: {
ChangeProp((HWND)lParam,~PROP_ERROR,0); // bei Änderung nicht rot!?
if (IsDlgButtonChecked(Wnd,4)
&& !SetTimer(Wnd,AutoUpdate_id,AutoUpdate_ms,NULL))
PostMessage(Wnd,WM_TIMER,AutoUpdate_id,0);
}break;
}
}
}
return FALSE;
}
void FarbRechteck(HDC dc,LPCRECT rc,COLORREF f) {
// Zeichnet (für Dialogboxen) umrandete Rechecke mit der Farbe f ausgefüllt
// Umrandung ist schwarz; weiß bei dunkler Farbe. Inhalt zweigeteilt.
HPEN p;
HBRUSH br;
int m;
RECT r;
CopyRect(&r,rc);
m=(r.left+r.right)>>1;
// 1. Pattern-Farbe (links)
r.right=m;
br=CreateSolidBrush(f);
FillRect(dc,&r,br);
DeleteBrush(br);
r.right=rc->right;
// 2. Einheitliche Farbe (rechts)
r.left=m;
br=CreateSolidBrush(GetNearestColor(dc,f));
FillRect(dc,&r,br);
DeleteBrush(br);
r.left=rc->left;
// 3. Rand (schwarz oder weiß [keine Farbkomponente >=128])
p=SelectPen(dc,GetStockPen(f&0x808080L?BLACK_PEN:WHITE_PEN));
br=SelectBrush(dc,GetStockBrush(HOLLOW_BRUSH)); // nur Rand
Rectangle(dc,r.left,r.top,r.right,r.bottom);
SelectBrush(dc,br);
SelectPen(dc,p);
}
static INT_PTR CALLBACK DisplayDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
switch (Msg) {
case WM_INITDIALOG: {
TCHAR s[32];
SetCheckboxGroup(Wnd,101,108,DispOpt);
PrintXY(s,&Kaestel);
SetDlgItemText(Wnd,120,s);
PrintXY(s,&SubTick);
SetDlgItemText(Wnd,121,s);
#ifndef WIN32
KillTimer(Wnd,3); // In 16-bit-Windows erforderlich?
#endif
}break;
case WM_TIMER: {
TCHAR s[32];
KillTimer(Wnd,wParam);
if (GetModifiedEditItemText(Wnd,120,s,elemof(s))) {
if (ScanXY(s,&Kaestel)) {
wmSize(ClientExt.x,ClientExt.y);
}else MessageBeep(MB_ICONEXCLAMATION);
}
if (GetModifiedEditItemText(Wnd,121,s,elemof(s))) {
if (ScanXY(s,&SubTick)) {
wmSize(ClientExt.x,ClientExt.y);
}else MessageBeep(MB_ICONEXCLAMATION);
}
}break;
case WM_COMMAND: switch (LOWORD(wParam)){
case 101:
case 102:
case 103:
case 104:
case 105:
case 108: {
SetDispOpt(DispOpt^(1<<(wParam-101)),0);
}break;
case 120:
case 121: {
if (GET_WM_COMMAND_CMD(wParam,lParam)==EN_CHANGE) SetTimer(Wnd,3,500,NULL);
}break;
case 122: {
Kaestel.x=10; Kaestel.y=8;
SubTick.x=5; SubTick.y=5;
DisplayDlgProc(Wnd,WM_INITDIALOG,0,0);
wmSize(ClientExt.x,ClientExt.y);
}break;
case 111: if (FarbAuswahl(Wnd,GridColor)) SetDispOpt(DispOpt,DO_GRID); break;
case 112: if (FarbAuswahl(Wnd,BackColor)) SetDispOpt(DispOpt,DO_BACK); break;
case 1:
DisplayDlgProc(Wnd,WM_TIMER,3,0); // evtl. Editfelder übernehmen
}break;
}
return MyDlgHandler(Wnd,Msg,wParam,lParam,112,&hDisplayDlg,T("Anzeige"));
}
// Grundlegende Einstellungen: Kanalnamen-Präfix, Zählweise (MODAL)
// Bonus: Ist eine Einstellung von Zählweise und Dezimaltrenner
// per Modifikation der OSZI.INI gemacht worden, dann ist kein RadioButton
// aktiv, und beim Übernehmen wird, wenn nichts gecheckt, dann auch
// nicht verändert. Wenn also bspw. Oktal-Zählung, Start ab 200,
// 5 reservierte Stellen und ein „ oder TEN (Punkt in der Mitte für Chinesen)
// als Dezimaltrenner bevorzugt werden, dann bitteschön!
static INT_PTR CALLBACK GrundDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
static const TCHAR sPunktKomma[]=T("?.,");
switch (Msg) {
case WM_INITDIALOG: {
TCHAR s[32];
{PTSTR p;
lstrcpy(s,sKanalFormat); p=StrChr(s,'%');
if (p) {
*p++=0; // Präfix von Formatanweisung trennen
if (*p=='0') {
CheckDlgButton(Wnd,107,TRUE); // Führende Null (bei 1 Stelle)
p+=2;
}
switch (*p) {
case 'd':
case 'i':
case 'u': CheckDlgButton(Wnd,104,TRUE); break; // dezimal zählen
case 'x':
case 'X': CheckDlgButton(Wnd,105,TRUE); break; // hexadezimal zählen
}
}
}
SetDlgItemText(Wnd,101,s); // Präfix
if ((unsigned)iKanalStart<2) CheckDlgButton(Wnd,102+iKanalStart,TRUE);
SetDlgItemText(Wnd,111,sMikro);
SetDlgItemInt(Wnd,112,MaxKanalMenu,FALSE);
GetDlgItemText(Wnd,120,s,elemof(s));
TCHAR buf[32];
_sntprintf(buf,elemof(buf),s,*sDecimal);
SetDlgItemText(Wnd,120,buf);
{PCTSTR p;
p=StrChr(sPunktKomma,*sDezimal);
if (p) CheckDlgButton(Wnd,120+int(p-sPunktKomma),TRUE);
}
CheckDlgButton(Wnd,123,TRUE); // Ausrichtung "von links nach rechts"
PrintPr(s,wPrefix);
SetDlgItemText(Wnd,125,s);
}break;
case WM_CHECKDIALOG: { // Zwangweise zusammenstellen, bei fehlenden Angaben Vorgabe
lParam=0;
TCHAR *p=sKanalFormat+GetDlgItemText(Wnd,101,sKanalFormat,4);
*p++='%';
if (IsDlgButtonChecked(Wnd,107)) {*p++='0'; *p++='2';}
*p++=IsDlgButtonChecked(Wnd,105) ? 'X' : 'd';
*p=0;
iKanalStart=1-IsDlgButtonChecked(Wnd,102); // ohne Angabe "ab 1" wählen
TCHAR m[2],s[8];
GetDlgItemText(Wnd,111,m,elemof(m));
if (*m!='u' && (unsigned)*m<0x80) {lParam=111; break;}
UINT i=GetDlgItemInt(Wnd,112,NULL,FALSE);
if (i<4) {lParam=112; break;}
i=GetRadioCheck(Wnd,120,122);
if ((int)i>=0) *sDezimal=sPunktKomma[i]; // des Users Wunsch ist des Users Himmelreich!
GetDlgItemText(Wnd,125,s,elemof(s)); WORD pr;
if (!ScanPr(s,pr)) {lParam=125; break;}
*sMikro=ISO_Prefixes[4]=*m; // Nur u oder Sonderzeichen akzeptieren!
MaxKanalMenu=i;
wPrefix=pr;
}break;
}
return MyDlgHandler(Wnd,Msg,wParam,lParam,110,NULL,T("Grundlage"));
}
/********************************
** Ein Super-Zeitbasis-Dialog **
********************************/
INT_PTR CALLBACK ZeitDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
switch (Msg) {
case WM_INITDIALOG: {
int i;
HWND w=GetDlgItem(Wnd,100);
for (i=0; i<NumZeitMenu; i++) {
ComboBox_AddString(w,zeit[i].name);
}
ComboBox_SetCurSel(w,ZeitDlgCurSel);
PostMessage(Wnd,WM_SHOWITEMS,0,0);
PostMessage(Wnd,WM_INFONEU,0xFF,(LPARAM)(zeit+ZeitDlgCurSel));
}break;
case WM_SHOWITEMS: {
TCHAR s[64];
LoadString(HInstance,44,s,elemof(s));
SetWindowText(GetPrevSibling(GetDlgItem(Wnd,102)),
Index2String(s,ZeitDlgCurSel?1:0));
EnableDlgItem(Wnd,108,NumZeitMenu<4); // Max. 4 Zeitbasen
EnableDlgItem(Wnd,109,ZeitDlgCurSel); // "A" kann nicht gelöscht werden
}break;
case WM_INFONEU: {
TCHAR buf[32];
#define was ((BYTE)wParam)
#define z ((ZEIT*)lParam)
if (was&WAS_XABLENK) {
z->GetVar(0,buf);
SetDlgItemText(Wnd,101,buf); // PROP fehlt noch!
}
if (was&(WAS_RATE|WAS_XANF)) {
z->GetVar(1,buf);
SetDlgItemText(Wnd,102,buf);
}
if (was&WAS_SAMPLES) {
z->GetVar(2,buf);
SetDlgItemText(Wnd,103,buf);
}
if (was&WAS_FARBE) InvalidateRect(GetDlgItem(Wnd,112),NULL,TRUE);
#undef z
#undef was
}break;
case WM_DRAWITEM: { // Farbe für Zeitbasis
#define dis ((LPDRAWITEMSTRUCT)lParam)
FarbRechteck(dis->hDC,&dis->rcItem,zeit[ZeitDlgCurSel].farbe);
#undef dis
}break;
case WM_COMMAND: switch(LOWORD(wParam)) {
case 100: switch (GET_WM_COMMAND_CMD(wParam,lParam)) {
case CBN_SELCHANGE: {
ZeitDlgCurSel=ComboBox_GetCurSel((HWND)lParam);
PostMessage(Wnd,WM_SHOWITEMS,0,0);
PostMessage(Wnd,WM_INFONEU,0xFF,(LPARAM)(zeit+ZeitDlgCurSel));
}
}break;
case 101: switch (GET_WM_COMMAND_CMD(wParam,lParam)) {
case EN_STEPUP: zeit[ZeitDlgCurSel].SetAblenkung(T("++")); break;
case EN_STEPDOWN: zeit[ZeitDlgCurSel].SetAblenkung(T("--")); break;
}break;
case 108: { // weitere Zeitbasis
// keine Ahnung, wie weiter!
}break;
case 109: { // Zeitbasis löschen
}break;
case 110: zeit[ZeitDlgCurSel].FarbAuswahl(Wnd); break;
}break;
case WM_CHECKDIALOG: {
lParam=0;
TCHAR s[32];
GetDlgItemText(Wnd,101,s,elemof(s));
if (!zeit->SetAblenkung(s)) lParam=MAKELONG(101,201);
}
}
return MyDlgHandler(Wnd,Msg,wParam,lParam,IDC_XABLENK,&hZeitDlg,T("ZeitDlg"));
}
/***************************************************
** Der Super-Dialog für alle Kanal-Einstellungen **
***************************************************/
bool GetEditText(int zero_idx,PTSTR buf) {
HWND w=GetDlgItem(hKanalDlg,101+zero_idx);
if (!GetModifiedEditText(w,buf,32)) return false;
ChangeProp(w,~PROP_ERROR,0);
InvalidateRect(w,NULL,TRUE);
return true;
}
void SetEditText(int zero_idx,PTSTR buf) {
// Wird auch bei DDEPOKE aufgerufen!
HWND w=GetDlgItem(hKanalDlg,101+zero_idx);
ChangeProp(w,~PROP_ERROR,0);
SetWindowText(w,buf);
Edit_SetModify(w,FALSE);
KillTimer(hKanalDlg,AutoUpdate_id);
}
void LadeKanalComboBox() {
HWND hCombo=GetDlgItem(hKanalDlg,100); // Combobox
TCHAR buf[32],KanalGruppe[32],*pg=KanalGruppe;
KANAL *k;
GRUPPE *g;
int i,item;
item=ComboBox_GetCurSel(hCombo);
ComboBox_ResetContent(hCombo);
LoadString(HInstance,208,KanalGruppe,elemof(KanalGruppe)); // "Kanal %s\0Gruppe %s"
for (i=0,k=kanal; i<numkanal; i++,k++) {
_sntprintf(buf,elemof(buf),pg,k->name);
if (k->sNamen[0]) {
lstrcat(buf,T(": "));
lstrcat(buf,k->sNamen);
}
ComboBox_AddString(hCombo,buf);
}
if (numkanal>=2
&& !IsWindowVisible(GetDlgItem(hKanalDlg,106))) { // Knopf "Gruppen zeigen"
pg+=lstrlen(pg)+1; // auf "Gruppe %s"
for (i=0,g=gruppe; i<numgruppe; i++,g++) {
if (g->name[0]) {
_sntprintf(buf,elemof(buf),pg,g->name); // "Gruppe <gruppenname>"
}else{ // "Alle Kanäle" / "Beide Kanäle"
LoadString(HInstance,numkanal==2?207:206,buf,elemof(buf));
}
ComboBox_AddString(hCombo,buf);
}
}
ComboBox_SetCurSel(hCombo,item);
}
#define WM_SETKANAL (WM_USER+101) // lParam=Kanal
int KanalDlgCurSel;
INT_PTR CALLBACK KanalDlgProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam) {
int i;
switch (Msg) {
case WM_INITDIALOG: {
hKanalDlg=Wnd;
// for (i=102; i<=105; i++)
// DefEditProc=SubclassWindow(GetDlgItem(Wnd,i),EditHook);
LadeKanalComboBox();
PostMessage(Wnd,WM_SETKANAL,0,lParam);
}break;
case WM_SHOWITEMS: { //HIBYTE(wParam)=zu schaltende Fenster,
//LOBYTE(wParam)=Sichtbarkeits-Zustände
HWND w; //Bit0=Name, Bit1=Tastkopf, Bit2=Ablenkung,
int show; //Bit3=DC-Pegel, Bit4=DC-Stufung, Bit5=Gruppen-Knopf
for (i=101; i<=108; i++) if (HIBYTE(LOWORD(wParam))&1) {
w=GetDlgItem(Wnd,i);
show=LOBYTE(LOWORD(wParam))&1?SW_SHOW:SW_HIDE;
ShowWindow(w,show);
if (i<=105) ShowWindow(GetPrevSibling(w),show); // Beschriftung
switch (i) {
case 103: // das /div hinter dem Ablekkoeffizienten
case 108: ShowWindow(GetNextSibling(w),show);
}
wParam>>=1;
}
if (HIBYTE(LOWORD(wParam))&1) // HIER STIMMT WAS NICHT!
ShowWindow(GetDlgItem(Wnd,5),LOBYTE(LOWORD(wParam))&1?SW_SHOW:SW_HIDE);
}break;
case WM_SETKANAL: {
SendMessage(Wnd,WM_SHOWITEMS,HIWORD(lParam),0);
HWND hCombo=GetDlgItem(Wnd,100);
if (ComboBox_GetCurSel(hCombo)!=LOBYTE(LOWORD(lParam))) {
for (i=101; i<=105; i++) {
if ((BOOL)SendDlgItemMessage(Wnd,i,EM_GETMODIFY,0,0)) {
TCHAR name[32];
ComboBox_GetLBText(hCombo,ComboBox_GetCurSel(hCombo),name);
switch (MBox(Wnd,209,MB_YESNOCANCEL|MB_ICONQUESTION,(LPSTR)name)) {
case IDOK: SendMessage(Wnd,WM_COMMAND,3,0); nobreak; // vorerst!
case IDCANCEL: return FALSE;
}
break;
}
}
i=LOBYTE(LOWORD(lParam));
ComboBox_SetCurSel(hCombo,i);
if (HIBYTE(LOWORD(lParam))) SetEditFocus(Wnd,100+HIBYTE(LOWORD(lParam)));
goto set_vars;
}
}break;
case WM_DRAWITEM: {
#define dis ((LPDRAWITEMSTRUCT)lParam)
if (KanalDlgCurSel<numkanal)
FarbRechteck(dis->hDC,&dis->rcItem,kanal[KanalDlgCurSel].farbe);
#undef dis
}break;
case WM_COMMAND: switch (LOWORD(wParam)) {
case 100: switch (GET_WM_COMMAND_CMD(wParam,lParam)) {
case CBN_SELCHANGE: {
KanalDlgCurSel=ComboBox_GetCurSel((HWND)lParam);
if (KanalDlgCurSel<0) break; // eigentlich: alle Items ausschalten!
SendMessage(Wnd,WM_SHOWITEMS,
KanalDlgCurSel>=numkanal?0x5150:0x5101,0); // Staffelung
// jetzt noch die übrigen Werte setzen...
set_vars:
TCHAR s[32];
if (KanalDlgCurSel<numkanal) {
KANAL *k=kanal+KanalDlgCurSel;
k->GetVar(0,s); SetDlgItemText(Wnd,101,s);
k->GetVar(1,s); SetDlgItemText(Wnd,102,s);
k->GetVar(2,s); SetDlgItemText(Wnd,103,s);
// wsprintf(s+lstrlen(s),T(" %c"),T("=~_")[k->kopplung]);
k->GetVar(4,s); SetDlgItemText(Wnd,104,s);
}else{
GRUPPE *g=gruppe+KanalDlgCurSel-numkanal;
g->GetVar(1,s); SetDlgItemText(Wnd,102,s);
g->GetVar(2,s); SetDlgItemText(Wnd,103,s);
// wsprintf(s+lstrlen(s),T(" %c"),T("=~_")[k->kopplung]);
g->GetVar(4,s); SetDlgItemText(Wnd,104,s);
}
}break;
}break;
case 101:
case 102:
case 103:
case 104:
case 105: switch (GET_WM_COMMAND_CMD(wParam,lParam)) {
// case EN_CHANGE: {
// ChangeProp((HWND)lParam,~PROP_DIVERS,0); // bei Änderung nicht grau!?
// if (IsDlgButtonChecked(Wnd,4)) SetTimer(Wnd,AutoUpdate_id,AutoUpdate_ms,NULL);
// }break;
case EN_STEPUP:
case EN_STEPDOWN: switch (LOWORD(wParam)){
case 103: {
TCHAR buf[32];
UINT kn=(UINT)SendDlgItemMessage(Wnd,100,CB_GETCURSEL,0,0);
if (!GetEditText(2,buf) || kanal[kn].SetAblenkung(buf))
SendMessage(MainWnd,WM_COMMAND,
(kn<<10)+IDC_YABL+elemof(Reihe)+
GET_WM_COMMAND_CMD(wParam,lParam)-EN_STEPUP+1,0);
}break;
}break;
}break;
case 106: {
// ShowWindow((HWND)lParam,SW_HIDE); // Knopf verschwindet
SendMessage(Wnd,WM_SHOWITEMS,0x2F0E,0); // aber noch keine Staffelung
LadeKanalComboBox();
}break;
case 110: if (KanalDlgCurSel<numkanal) kanal[KanalDlgCurSel].FarbAuswahl(Wnd); break;
}break;
case WM_CHECKDIALOG: {
TCHAR buf[32];
lParam=0;
i=(int)SendDlgItemMessage(Wnd,100,CB_GETCURSEL,0,0);
KANAL *k;
if ((unsigned)i<(unsigned)numkanal) k=kanal+i;
if (GetEditText(0,buf)
&& !k->SetVar(0,buf)) {
lParam=MAKELONG(101,201);
ChangeProp(Wnd,101,~PROP_ERROR,PROP_ERROR);
}
if (GetEditText(1,buf)
&& !k->SetVar(1,buf)) {
lParam=MAKELONG(102,201);
ChangeProp(Wnd,102,~PROP_ERROR,PROP_ERROR);
}
if (GetEditText(2,buf)
&& !k->SetVar(2,buf)) {
lParam=MAKELONG(103,201);
ChangeProp(Wnd,103,~PROP_ERROR,PROP_ERROR);
}
if (GetEditText(3,buf)
&& !k->SetVar(4/*!!*/,buf)) {
lParam=MAKELONG(104,201);
ChangeProp(Wnd,104,~PROP_ERROR,PROP_ERROR);
}
}break;
} // switch
return MyDlgHandler(Wnd,Msg,wParam,lParam,IDC_YABL,&hKanalDlg,T("KanalDlg"));
}
void StartDlg(HWND&Wnd, UINT DlgID, DLGPROC DlgProc, LPARAM lParam) {
if (!Wnd) Wnd=CreateDialogParam(
HInstance,MAKEINTRESOURCE(DlgID),MainWnd,DlgProc,lParam);
else{
SetActiveWindow(Wnd);
SendMessage(Wnd,WM_SETKANAL,0,lParam);
}
}
void StartKanalDlg(LPARAM lParam) {
/* Bit-Belegung von lParam:
7:0 Kanal- oder Gruppennummer
15:8 Zu fokussierende Edit-Zeile: 1=Namen, 2=Tastkopf,3=Ablenkung,4=Offset
16 Sichtbarkeit von NAMEN-Eingabezeile
17 Sichtbarkeit von TASTKOPF-Eingabezeile usw.
18 ABLENKUNG
19 OFFSET
20 STUFUNG (nur bei Gruppe)
21 GRUPPEN-Knopf
24 Schalten NAMEN-Sichtbarkeit usw.*/
StartDlg(hKanalDlg,IDC_YABL,KanalDlgProc,lParam);
}
void CreateEditBrushes() {
static DWORD XOR[]={0x7F0000L,0x1F1F1FL,0x3F3F00L}; // gelb,grau,rot
for (int i=0; i<elemof(EditBrushes); i++) {
EditBrushes[i]=CreateSolidBrush(GetSysColor(COLOR_WINDOW)^XOR[i]);
}
}
void DeleteEditBrushes() {
for (int i=0; i<elemof(EditBrushes); i++) DeleteBrush(EditBrushes[i]);
}
/*
static void LoadWinPos(void) {
WINDOWPLACEMENT wp;
TCHAR buf[32];
InitStruct(&wp,sizeof(wp));
GetWindowPlacement(MainWnd,&wp);
GetString(S_Position,T("Oszi"),sLeer,buf,elemof(buf));
if (_stscanf(buf,T("%d,%d,%d,%d"),
&wp.rcNormalPosition.left,
&wp.rcNormalPosition.top,
&wp.rcNormalPosition.right,
&wp.rcNormalPosition.bottom)!=4) return;
MoveRectIntoFullScreen(&wp.rcNormalPosition);
SetWindowPlacement(MainWnd,&wp);
}
static void SaveWinPos(void) {
WINDOWPLACEMENT wp;
TCHAR buf[32];
InitStruct(&wp,sizeof(wp));
GetWindowPlacement(MainWnd,&wp);
wvsprintf(buf,T("%d,%d,%d,%d"),(va_list)&wp.rcNormalPosition);
WriteString(S_Position,T("Oszi"),buf);
}
*/
UINT Bilder;
enum RUNNING {T_STOP,T_WAIT,T_AUTO} Running;
void ZeigBildFrequenz() { // wird aller 1 Sekunde aufgerufen
TCHAR buf[64];
static const TCHAR sRunning[]=T(
"%s - angehalten\0%s - wartet auf Trigger mit %d Hz\0%s - Bildfrequenz: %d Hz\0");
_sntprintf(buf,elemof(buf),Index2String(sRunning,Running),WindowTitle,Bilder);
SetWindowText(MainWnd,buf);
Bilder=0;
}
void SetStopRun(RUNNING neu) {
if (neu==Running) return;
if (Running==T_WAIT && trig->modus==TRIGG::TM_SINGLE) {
FlashWindow(MainWnd,TRUE); // Trigger-Blitz
FlashWindow(MainWnd,FALSE);
}
Running=neu;
if (trig && trig->anzeige && trig->anzeige->knopf[0]) {
trig->anzeige->knopf[0]->bild=neu?MYBUTTON::PAUSE:MYBUTTON::PLAY;
trig->anzeige->knopf[0]->Inval();
}
quelle->RelayMsg(Running ? Q_ARM : Q_UNARM, NULL);
ZeigBildFrequenz();
}
void ToggleStopRun(void) {
SetStopRun(Running?T_STOP:T_WAIT); // nicht korrekt wenn eh' ohne Trigger!
}
bool Idle(void) {
if (!quelle) return true;
if (!Running) return true;
if (!trig) return true;
// if (!quelle->buffer) return true;
// quelle->state&=~Q_FULL;
// Sofern keine alten Daten (also alles "neu"), alles auf alt setzen lassen
// und Trigger suchen mittels dwFlags=0 (InQueue-Bit löschen)
if (WaveHdr.dwUser==WaveHdr.dwBufferLength) {
WaveHdr.dwFlags=0; // Neu initialisieren
WaveHdr.dwBytesRecorded=0; // Null neue Daten
WaveHdr.dwUser=0; // Alles alte Daten (PROBLEM MIT PRÄTRIGGER-DATEN!!!)
// Software-Trigger initialisieren
if (st.pre>(long)WaveHdr.dwBufferLength) st.pre=WaveHdr.dwBufferLength;
WaveHdr.reserved=st.pre; // begrenzen!
st.Reset();
if (trig->modus==TRIGG::TM_AUTO) SetTimer(MainWnd,3,500,NULL);
}
if (!FindTrigger(/*TimeOut:100*/)) // keine neuen Daten?
return WaveHdr.dwFlags&WHDR_PREPARED ? false : true;
// Sobald Trigger entdeckt, ggf. Fenstertitel umändern
if (WaveHdr.dwBytesRecorded>WaveHdr.reserved) SetStopRun(T_AUTO);
// Sofern SINGLE, wenn Puffer voll, Aufnahme abschalten
if (trig->modus==TRIGG::TM_SINGLE
&& WaveHdr.dwBytesRecorded==WaveHdr.dwBufferLength) SetStopRun(T_STOP);
for (int i=0; i<numkanal; i++) kanal[i].CalcGraf();
Inval(false);
return false;
}
/*
void KANAL::SetAnzeigen(BYTE and, BYTE xor) {
// if (masse) masse->SetState(masse->state&and^xor);
// if (::trig && ::trig->k==this && ::trig->kreuz)
// ::trig->kreuz->SetState(::trig->kreuz->state&and^xor);
if (anzeige) anzeige->SetState(and,xor);
}
*/
int aktkanal; // aktiver Kanal für Tastatureingabe
KANAL *aktk;
int aktzeit; // aktive Zeitbasis für Tastatureingabe
ZEIT *aktz;
void SetAktKanal(int a) {
if (a<0) a=numkanal-1;
else if (a>=numkanal) a=0;
aktkanal=a;
if (aktk && aktk->anzeige) aktk->anzeige->SetState(~STA_SELECTED,0);
aktk=kanal+a;
if (aktk && aktk->anzeige) aktk->anzeige->SetState(~STA_SELECTED,STA_SELECTED);
}
void SetAktZeit(int a) {
if (a<0) a=NumZeitMenu-1;
else if (a>=NumZeitMenu) a=0; // Ungültiges <a> erzeugt fehlende Zeitbasen - oder?
aktzeit=a; // Zurzeit nur eine Zeitbasis!
if (aktz && aktz->anzeige) aktz->anzeige->SetState(~STA_SELECTED,0);
aktz=zeit+a;
if (aktz && aktz->anzeige) aktz->anzeige->SetState(~STA_SELECTED,STA_SELECTED);
}
void SetDatenquelle(EDatenquelle neu) {
SetStopRun(T_STOP);
if (Datenquelle==neu) return;
if (Datenquelle>0) { // falls bereits initialisiert
CheckMenuItem(MainMenu,120+Datenquelle-1,MF_UNCHECKED);
}
if (trig) {trig->Destruktor(); delete trig; trig=0;}
if (quelle) {
if (numkanal) quelle->RelayMsg(Q_DONE,0); // macht selber "unarm"
delete quelle; quelle=NULL;
}
if (kanal) {
for (int i=numkanal-1; i>=0; i--) kanal[i].Destruktor();
delete[]kanal; kanal=NULL;
}
aktk=NULL;
if (gruppe) {delete[]gruppe; gruppe=NULL;} // Gruppen sind "dumm"!
if (zeit) {zeit->Destruktor(); delete zeit; zeit=0;}
aktz=NULL;
if (WaveHdr.lpData) { GlobalFree(WaveHdr.lpData); WaveHdr.lpData=NULL;}
WaveHdr.lpNext=NULL;
numkanal=0;
Datenquelle=neu;
if (!Datenquelle) return;
CheckMenuItem(MainMenu,120+Datenquelle-1,MF_CHECKED);
switch (Datenquelle) {
case ZUFALL: quelle=new Q_ZUFALL; break;
case DSO220: quelle=new Q_DSO220; break;
case SOUND: quelle=new Q_SOUND; break;
}
if (!quelle->RelayMsg(Q_INIT,0)) return;
SYSINFO si;
quelle->RelayMsg(Q_GETSYSINFO,&si);
numkanal=si.numchan;
if (!numkanal) return;
GetSample=si.getsample; // die wichtigsten Daten herauskopieren
BlockAlign=si.blockalign;
FullScale=(float)(1L<<si.bits);
zeit=new ZEIT; // Wie zum Array machen??
zeit->Konstruktor(0);
WaveHdr.dwBufferLength=1000L*BlockAlign;
WaveHdr.lpData=(LPSTR)GlobalAlloc(GMEM_FIXED,WaveHdr.dwBufferLength);
WaveHdr.dwBytesRecorded=0; // keine neuen Daten
WaveHdr.dwUser=WaveHdr.dwBufferLength; // keine alten Daten
WaveHdr.dwFlags=0; // keine Statusbits
kanal=new KANAL[numkanal];
for (int i=0; i<numkanal; i++) kanal[i].Konstruktor(i);
gruppe=new GRUPPE[numkanal];
MacheGruppenListe();
trig=new TRIGG;
trig->Konstruktor(0);
SetAktKanal(aktkanal); // Zeiger neu setzen
SetAktZeit(aktzeit);
SetStopRun(T_WAIT);
}
BYTE HandleKeyDownUp(BYTE down, WPARAM key) {
// Zentrale Verwaltung der Hotkeys, die zur besseren Visualisierung die
// Anzeige-Schaltflächen mit betätigen. Liefert Flags: MK_SHIFT und MK_CONTROL
BYTE mk=0;
ANZEIGE *a=NULL;
int idx=0;
if (GetKeyState(VK_SHIFT)<0) mk|=MK_SHIFT;
if (GetKeyState(VK_CONTROL)<0)mk|=MK_CONTROL;
switch (key) {
case VK_SHIFT: { // Massesymbol hervorheben
if (!(mk&MK_CONTROL) && aktk && aktk->masse)
aktk->masse->SetState((BYTE)~STA_SELECTED,(BYTE)(down?STA_SELECTED:0));
}break;
case VK_CONTROL: { // Triggersymbol hervorheben
if (trig && trig->kreuz)
trig->kreuz->SetState((BYTE)~STA_SELECTED,(BYTE)(down?STA_SELECTED:0));
}break;
case VK_TAB: {
if (down) SetAktKanal(aktkanal+(mk&MK_SHIFT?-1:+1));
}break;
case VK_APPS:
case VK_SPACE: {
if (mk&MK_CONTROL) {
if (down && trig && trig->kreuz) ShowPopupMenu(trig->submenu,trig->kreuz->mitte.x,trig->kreuz->mitte.y);
break;
}
if (mk&MK_SHIFT) {
if (down && aktk && aktk->masse) ShowPopupMenu(aktk->submenu,aktk->masse->mitte.x,aktk->masse->mitte.y);
break;
}
if (key==VK_APPS) break;
}nobreak;
case VK_PAUSE: {
if (trig) a=trig->anzeige; // Wenn PortTalk fehlt, gibt's keinen Trigger
}break;
case VK_LEFT: goto A;
case VK_RIGHT: idx=1; A:{
if (mk&MK_CONTROL) break;
a=aktz->anzeige;
}break;
case VK_SUBTRACT:
case VK_DOWN: goto B;
case VK_ADD:
case VK_UP: idx=1; B:{
if (mk&MK_CONTROL) break;
if (aktk) a=aktk->anzeige;
}break;
}
if (a) { // Anzeige existiert
MYBUTTON *b=a->knopf[idx];
if (b && !(b->state&2)) { // Knopf existiert, und nicht disabled
b->SetState(down);
UpdateWindow(MainWnd); // rcitem-Aktualisierung vorziehen
}
}
return mk;
}
static void LoadConfig(void) {
TCHAR buf[16];
// Globales
GetString(HauptSektion,T("KanalFormat"),T("Y%d,1"),buf,elemof(buf));
_stscanf(buf,T("%7[^,],%d"),sKanalFormat,&iKanalStart); sKanalFormat[7]=0;
GetStruct(HauptSektion,T("Farben"),CustColors,sizeof(CustColors),StdProfile);
GetString(HauptSektion,T("Mikro"),T("µ"),sMikro,elemof(sMikro));
ISO_Prefixes[4]=*sMikro;
GetString(HauptSektion,T("Dezimaltrenner"),T("?"),sDezimal,elemof(sDezimal));
GetString(HauptSektion,T("Hintergrund"),T("#FFFFFF"),buf,elemof(buf));
String2Farbe(buf,BackColor);
GetString(HauptSektion,T("Gitternetz"),T("#008000"),buf,elemof(buf));
String2Farbe(buf,GridColor);
MaxKanalMenu=GetInt(HauptSektion,T("MaxKanalMenü"),16);
if (MaxKanalMenu<4) MaxKanalMenu=4;
if (GetString(HauptSektion,T("Kästel"),sLeer,buf,elemof(buf)))
ScanXY(buf,&Kaestel);
if (GetString(HauptSektion,T("SubTick"),sLeer,buf,elemof(buf)))
ScanXY(buf,&SubTick);
if (GetString(HauptSektion,T("Vorsätze"),sLeer,buf,elemof(buf)))
ScanPr(buf,wPrefix);
// Hintergrund
SetDispOpt(GetInt(HauptSektion,T("Anzeige"),
DO_GRID|DO_DB|DO_LINE|DO_TICK|DO_CROSS|DO_BACK),0);
gitter=new GITTER; // Konstruktor wird gerufen
// Trigger
// Kanäle
SetDatenquelle((EDatenquelle)GetInt(HauptSektion,T("Datenquelle"),1));
}
static void SaveConfig(void) {
TCHAR buf[16];
_sntprintf(buf,elemof(buf),sInt,Datenquelle);
WriteString(HauptSektion,T("Datenquelle"),buf);
WriteStruct(HauptSektion,T("Farben"),CustColors,sizeof(CustColors),StdProfile);
WriteString(HauptSektion,T("Mikro"),sMikro);
WriteString(HauptSektion,T("Dezimaltrenner"),sDezimal);
_sntprintf(buf,elemof(buf),T("%s,%d"),sKanalFormat,iKanalStart);
WriteString(HauptSektion,T("KanalFormat"),buf);
Farbe2String(buf,BackColor);
WriteString(HauptSektion,T("Hintergrund"),buf);
Farbe2String(buf,GridColor);
WriteString(HauptSektion,T("Gitternetz"),buf);
_sntprintf(buf,elemof(buf),T("%u"),DispOpt);
WriteString(HauptSektion,T("Anzeige"),buf);
_sntprintf(buf,elemof(buf),sInt,MaxKanalMenu);
WriteString(HauptSektion,T("MaxKanalMenü"),buf);
PrintXY(buf,&Kaestel); WriteString(HauptSektion,T("Kästel"),buf);
PrintXY(buf,&SubTick); WriteString(HauptSektion,T("SubTick"),buf);
PrintPr(buf, wPrefix); WriteString(HauptSektion,T("Vorsätze"),buf);
}
void SendAlleNachricht(UINT Msg, WPARAM wParam, LPARAM lParam) {
if (Anker[0].sub) Anker[0].sub->RelayMsg(Msg,wParam,lParam);
if (Anker[1].sub) Anker[1].sub->RelayMsg(Msg,wParam,lParam);
if (Anker[2].sub) Anker[2].sub->RelayMsg(Msg,wParam,lParam);
}
static MINIWND *HoverWnd; // Fenster, das Mausnachrichten erhält
static WPARAM ButtonState; // Momentaner Maustastenstatus MK_xxx
void SendMausNachricht(UINT Msg, WPARAM wParam, LPARAM lParam) {
if (HoverWnd) HoverWnd->RelayMsg(Msg,wParam,lParam);
}
void CheckHover(LPARAM lParam) {
if (ButtonState) return;// HOVER nicht bei gedrückter Maustaste wechseln
MINIWND *p=MINIWND::MiniWndFromPoint(MAKEPOINTS(lParam));
if (p==HoverWnd) return;
SendMausNachricht(WM_MOUSELEAVE,0,lParam); // Tschüss sagen
HoverWnd=p;
SendMausNachricht(WM_MOUSEHOVER,0,lParam); // Hallo sagen
}
void MausNachricht(UINT Msg, WPARAM wParam, LPARAM lParam) {
// Maus-Verarbeitung, Eintritts- und Austritts-Feststellung
switch (Msg) {
case WM_NCHITTEST:
case WM_MOUSEWHEEL: { // Diese beiden kommen in Bildschirmkoordinaten
ScreenToClientS(MainWnd,(POINTS*)&lParam);
}break;
case WM_MOUSEMOVE: {
if (wParam&(MK_LBUTTON|MK_RBUTTON|MK_MBUTTON))
SetCapture(MainWnd); // Hier System-Mausfang setzen
else ReleaseCapture();
}break;
case WM_SETCURSOR: SendMausNachricht(Msg,wParam,lParam); return;
default: if (Msg<WM_MOUSEFIRST) return;
if (Msg>WM_MOUSELAST) return;
}
WPARAM bsc=ButtonState; // um nicht 2x CheckHover aufrufen zu müssen...
CheckHover(lParam); // mein lParam=POINTS!
if (WM_MOUSEFIRST<=Msg && Msg<=WM_MOUSELAST)
ButtonState=wParam&(MK_LBUTTON|MK_RBUTTON|MK_MBUTTON);
bsc^=ButtonState; // Änderungs-Bits gesetzt
SendMausNachricht(Msg,wParam,lParam);
if (bsc) CheckHover(lParam); // alles in Client-Koordinaten!
}
LRESULT CALLBACK MainWndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam){
#if 0
if (WM_MOUSEFIRST<=Msg && Msg<=WM_MOUSELAST && Tooltip) {
MSG msg;
msg.hwnd=Wnd;
msg.message=Msg;
msg.wParam=wParam;
msg.lParam=lParam;
SendMessage(Tooltip,TTM_RELAYEVENT,0,(LPARAM)&msg);
}
#endif
MausNachricht(Msg,wParam,lParam);
switch (Msg) {
case WM_CREATE: {
MainWnd=Wnd;
MainMenu=GetMenu(Wnd);
// LoadWinBitmaps();
atom=GlobalAddAtom(T("Oszi-Edit-BackColor"));
CreateEditBrushes();
SendMessage(Wnd,WM_WININICHANGE,0,0);
#if 0
Tooltip=CreateWindow(TOOLTIPS_CLASS,NULL,TTS_ALWAYSTIP,0,0,0,0,
0,0,HInstance,NULL);
#endif
if (waveInGetNumDevs()) EnableMenuItem(MainMenu,122,MF_ENABLED);
// eigentlich ein Fall für ConfigChanged... oder?
PostMessage(Wnd,WM_USER+10,LoadWinPos(Wnd,T("Oszi")),0);
OsziMenu=GetSubMenu(MainMenu,2);
tip=new TOOLTIP;
LoadConfig();
if (LoadWinPos(0,T("Anzeige"))) PostMessage(Wnd,WM_COMMAND,112,0);
if (LoadWinPos(0,T("Grundlage"))) PostMessage(Wnd,WM_COMMAND,112,0);
if (LoadWinPos(0,T("ZeitDlg"))) StartDlg(hZeitDlg,IDC_XABLENK,
ZeitDlgProc,MAKELONG(MAKEWORD(0,1),0x0303));
if (LoadWinPos(0,T("KanalDlg"))) StartDlg(hKanalDlg,IDC_YABL,
KanalDlgProc,MAKELONG(MAKEWORD(0,1),0xFFFF));
// SetTimer(Wnd,1,20,NULL);
SetTimer(Wnd,2,1000,NULL); // zur Berechnung der Bilder pro Sekunde
// static TRACKMOUSEEVENT tme={sizeof(tme),TME_HOVER|TME_LEAVE,0,HOVER_DEFAULT};
// tme.hwndTrack=Wnd;
// _TrackMouseEvent(&tme);
wm_helpmsg=RegisterWindowMessage(HELPMSGSTRING);
}break;
case WM_USER+10: {
if (wParam) ShowWindow(Wnd,(int)wParam); // macht in WM_CREATE Murks!!
}break;
case WM_PAINT:{
PAINTSTRUCT ps;
BeginPaint(Wnd,&ps);
if (DispOpt&DO_DB) {
if (DispOpt&DO_DB_INVAL) BltAlles(bltdc[0]);
BitBlt(ps.hdc,0,0,ClientExt.x,ClientExt.y,bltdc[0],0,0,SRCCOPY);
}else{
BltAlles(ps.hdc);
}
EndPaint(Wnd,&ps);
Bilder++;
}return 0;
case WM_TIMER: {
lParam=GetMessagePos();
ScreenToClientS(Wnd,(LPPOINTS)&lParam);
if ((LOWORD(lParam)>=(unsigned)ClientExt.x || HIWORD(lParam)>=(unsigned)ClientExt.y)
&& KillTimer(Wnd,222)) tip->SetParent(Anker+2);
// SendMessage(Wnd,WM_MOUSEMOVE,ButtonState,lParam);
//DAMIT LÄSST SICH KEIN MENÜ BEDIENEN!
// MausNachricht(WM_MOUSEMOVE,ButtonState,GetMessagePos());
switch (wParam) {
case 1: Idle(); break;
case 2: ZeigBildFrequenz(); break;
case 3: {
KillTimer(Wnd,wParam);
WaveHdr.dwFlags|=WHDR_ENDLOOP; // ausbrechen lassen
if ((long)WaveHdr.reserved<0) WaveHdr.reserved=0;
}break;
}
SendAlleNachricht(Msg,wParam,lParam);
}return 0; // besser: gerichtet mittels wParam als MINIWND-Zeiger
case WM_NCMOUSEMOVE: {
if (KillTimer(Wnd,222)) tip->SetParent(Anker+2);
}break;
case WM_MOUSEMOVE:{
HDC dc=GetDC(Wnd);
if (DispOpt&DO_DB) Fadenkreuz();
Fadenkreuz(dc);
lastmouse.x=GET_X_LPARAM(lParam);
lastmouse.y=GET_Y_LPARAM(lParam);
Fadenkreuz(dc);
if (DispOpt&DO_DB) Fadenkreuz();
ReleaseDC(Wnd,dc);
}return 0;
// case WM_NCHITTEST: _asm int 3; break;
case WM_SIZE: {
wmSize((short)LOWORD(lParam),(short)HIWORD(lParam));
SetDispOpt(DispOpt,DO_DB|DO_TB); // (beide) Puffer neu allokieren lassen!
for (int i=0; i<numkanal; i++) kanal[i].CalcGraf();
Inval(false);
SendAlleNachricht(Msg,wParam,lParam);
}return 0;
case WM_WININICHANGE: {
GetProfileString(T("intl"),T("sDecimal"),T("."),sDecimal,elemof(sDecimal));
Inval(true);
}return 0;
case WM_SYSCOLORCHANGE: {
DeleteEditBrushes();
CreateEditBrushes();
SendAlleNachricht(Msg,wParam,lParam);
}return 0;
case WM_SYSKEYDOWN: switch (wParam) {
case VK_RETURN: {
ShowWindow(Wnd,IsZoomed(Wnd)?SW_SHOWNORMAL:SW_MAXIMIZE);
}break;
case 'X': SendMessage(Wnd,WM_CLOSE,0,0); break;
}break;
case WM_KEYDOWN: {
BYTE mk=HandleKeyDownUp(1,wParam);
if (mk&MK_CONTROL) switch (wParam) {
case VK_UP: trig->SetPegel(T("+?")); break;
case VK_DOWN: trig->SetPegel(T("-?")); break;
case VK_LEFT: trig->SetPretrig(T("-?")); break;
case VK_RIGHT: trig->SetPretrig(T("+?")); break;
}else switch (wParam) {
case VK_SPACE:
case VK_PAUSE: {
if (HIBYTE(HIWORD(lParam))&0x40) break; // ohne Autorepeat!
ToggleStopRun();
}break;
case VK_ADD:
case VK_UP: if (mk&MK_SHIFT) aktk->SetNulllinie(T("++"));
else aktk->SetAblenkung(T("--")); break;
case VK_SUBTRACT:
case VK_DOWN: if (mk&MK_SHIFT) aktk->SetNulllinie(T("--"));
else aktk->SetAblenkung(T("++")); break;
case VK_LEFT: aktz->SetAblenkung(T("+?")); break;
case VK_RIGHT: aktz->SetAblenkung(T("-?")); break;
}
}break;
case WM_KEYUP: HandleKeyDownUp(0,wParam); break;
case WM_CHAR: switch ((TCHAR)wParam) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': SetAktKanal(((TCHAR)wParam-'0'-iKanalStart+10)%10); break;
case 'a':
case 'b':
case 'c':
case 'd': SetAktZeit((TCHAR)wParam-'a'); break;
case '=': aktk->SetKopplung((UINT)0); break;
case '~': aktk->SetKopplung(1); break;
case '_': aktk->SetKopplung(2); break;
case '!': aktk->SetTastkopf(aktk->GetTastkopfIndex()?T("1:1"):T("10:1")); break;
case '/': aktk->SetAblenkung(-aktk->div); break;
}return 0;
case WM_COMMAND: {
UINT id=wParam&0x3FF; // Menü-ID (10 bit)
UINT kn=LOWORD(wParam)>>10; // Kanalnummer (6 bit)
KANAL *k=::kanal+kn;
ZEIT *z=::zeit +kn;
UINT j;
if ((j=id-IDC_XABLENK)<elemof(Reihe)) {
z->SetAblenkung(Reihe[j]);
}else if ((j=id-IDC_RATE)<elemof(Reihe)) {
z->SetRate(Reihe[j]);
}else if ((j=id-IDC_YABL)<elemof(Reihe)) {
k->SetAblenkung(Reihe[j]);
}else switch (id) { // <kn> enthält Kanalnummer
case IDC_XABLENK+elemof(Reihe): StartDlg(hZeitDlg,IDC_XABLENK,ZeitDlgProc,
MAKELONG(MAKEWORD(kn,1),0x0301)); break;
case IDC_XABLENK+elemof(Reihe)+1: z->SetAblenkung(T("++")); break;
case IDC_XABLENK+elemof(Reihe)+2: z->SetAblenkung(T("--")); break;
case IDC_RATE+elemof(Reihe):
case IDC_XANFANG: StartDlg(hZeitDlg,IDC_XABLENK,ZeitDlgProc,
MAKELONG(MAKEWORD(kn,2),0x0302)); break;
case IDC_XFARBE: z->FarbAuswahl(Wnd); break;
case IDC_YVOR+0: k->SetTastkopf(T("1:1")); break;
case IDC_YVOR+1: k->SetTastkopf(T("10:1")); break;
case IDC_YVOR+2: StartKanalDlg(MAKELONG(MAKEWORD(kn,2),0xFF22U)); break;
case IDC_YKOP+0: // DC
case IDC_YKOP+1: // AC
case IDC_YKOP+2: { // GND
k->SetKopplung(id-IDC_YKOP);
}break;
case IDC_YINV: k->SetAblenkung(-k->div); break;
case IDC_YNAME: StartKanalDlg(MAKELONG(MAKEWORD(kn,1),0xFF21U)); break;
case IDC_YFARBE: k->FarbAuswahl(Wnd); break;
case IDC_YABL+elemof(Reihe):
StartKanalDlg(MAKELONG(MAKEWORD(kn,3),0xFF24U)); break;
case IDC_YABL+elemof(Reihe)+1: k->SetAblenkung(T("++")); break;
case IDC_YABL+elemof(Reihe)+2: k->SetAblenkung(T("--")); break;
case 109: SendMessage(Wnd,WM_CLOSE,0,0); break;
case 110: DialogBox(HInstance,MAKEINTRESOURCE(110),Wnd,GrundDlgProc); break;
case 112: StartDlg(hDisplayDlg,112,DisplayDlgProc,0); break;
case 113: {
DLGINFO di;
di.parent=Wnd;
di.kbHand=&hKBHand;
quelle->RelayMsg(Q_SETUPDLG,&di);
}break;
case 120:
case 121:
case 122: SetDatenquelle(EDatenquelle(LOWORD(wParam)-120+1)); break;
case IDC_TMODUS: trig->SetModus((TRIGG::MODUS)kn); break;
case IDC_TQUELLE: trig->SetQuelle(kn); break;
case IDC_TFLANKE: trig->SetFlanke((TRIGG::FLANKE)kn); break;
case IDC_TKOPPLUNG: trig->SetKopplung((TRIGG::KOPPLUNG)kn); break;
case IDC_TFARBE: trig->FarbAuswahl(Wnd); break;
case 0x229: ToggleStopRun(); break;
case 191: WinHelp(Wnd,HelpFileName,HELP_INDEX,0); break;
case 192: WinHelp(Wnd,HelpFileName,HELP_CONTEXT,192); break;
case 199: DialogBox(HInstance,MAKEINTRESOURCE(199),Wnd,AboutDlgProc); break;
}
}return 0;
case WM_CLOSE:
case WM_ENDSESSION: {
if (hDisplayDlg)SaveWinPos(hDisplayDlg,T("Anzeige"),true);
if (hZeitDlg) SaveWinPos(hZeitDlg, T("ZeitDlg"),true);
if (hKanalDlg) SaveWinPos(hKanalDlg, T("KanalDlg"),true);
SaveConfig();
SaveWinPos(Wnd,T("Oszi"),true);
WriteString(NULL,NULL,NULL); // Flush
WinHelp(Wnd,HelpFileName,HELP_QUIT,0);
}break;
case WM_DESTROY: {
SetDatenquelle(EDatenquelle(0));
SetDispOpt(0,0);
DeleteEditBrushes();
GlobalDeleteAtom(atom);
// FreeWinBitmaps();
KillTimer(Wnd,1);
PostQuitMessage(0);
}return 0;
}
if (Msg==wm_helpmsg) WinHelp(Wnd,HelpFileName,HELP_CONTEXT,88);
return DefWindowProc(Wnd,Msg,wParam,lParam);
}
void CALLBACK WinMainCRTStartup() {
static WNDCLASS wc={
CS_DBLCLKS|CS_HREDRAW|CS_VREDRAW|CS_BYTEALIGNCLIENT,
MainWndProc,0,0,0,0,0,0,MAKEINTRESOURCE(100),WndClassName};
LoadString(HInstance,200,WindowTitle,elemof(WindowTitle));
MBoxTitle=WindowTitle; // Zeiger für <wutils> setzen
HInstance=GetModuleHandle(NULL);
{OSVERSIONINFO vi;
InitStruct(&vi,sizeof(vi));
if (!GetVersionEx(&vi)) {
#ifdef UNICODE
CHAR buf[256];
LoadStringA(HInstance,210,buf,elemof(buf));
MessageBoxA(0,buf,NULL,MB_OK|MB_ICONSTOP); // Zwang: ANSI-Version
#endif
return;
}
if (vi.dwPlatformId==VER_PLATFORM_WIN32s)
MBox(0,210,MB_OK|MB_ICONASTERISK); // Empfehlung: 16bit
}
InitCommonControls();
GetModuleFileName(HInstance,StdProfile,elemof(StdProfile));
lstrcpy(GetFileNamePtr(StdProfile),(PTSTR)IniFileName);
wc.hInstance=HInstance;
wc.hIcon=LoadIcon(HInstance,MAKEINTRESOURCE(100));
RegisterClass(&wc);
CreateWindowEx(WS_EX_ACCEPTFILES,WndClassName,MBoxTitle,
WS_VISIBLE|WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
0,0,HInstance,NULL);
MSG msg;
for(;;){
while (PeekMessage(&msg,0,0,0,PM_REMOVE)) {
if (msg.message==WM_QUIT) goto raus;
if (hKBHand && IsDialogMessage(hKBHand,&msg)) continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (Idle()) WaitMessage();
}
raus:
#ifndef WIN32
UnhookWindowsHookEx(MessageHook);
#endif
ExitProcess((UINT)msg.wParam);
}
Detected encoding: ANSI (CP1252) | 4
|
|