unit FMain;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, ComCtrls, ExtCtrls, CheckLst, Seriell, IniFiles, ShellAPI,
  Menus;

type TCustCode = Record // Typ für aktive Codeseite
	Code: String;
	Zeilenw: Boolean;
End;

Const cASC: Array [0..255] Of String =
			 ('[NUL]','[SHO]','[STX]','[ETX]','[EOT]','[ENQ]','[ACK]','[BEL]','[BS]','[HT]','[LF]','[VT]',
			  '[FF]','[CR]','[SO]','[SI]','[DLE]','[DC1]','[DC2]','[DC3]','[DC4]','[NAK]','[SYN]','[ETB]',
			  '[CAN]','[EM]','[SUB]','[ESC]','[FS]','[GS]','[RS]','[US]','[ ]','!','"','#','$','%','&',
			  '''','(',')','*','+',',','-','.','/','0','1','2','3','4','5','6','7','8','9',':',';','<','=',
			  '>','?','@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T',
			  'U','V','W','X','Y','Z','[','\',']','^','_','''','a','b','c','d','e','f','g','h','i','j','k',
			  'l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','{','|','}','~','[DEL]','[128]',
			  '[129]','[130]','[131]','[132]','[133]','[134]','[135]','[136]','[137]','[138]','[139]','[140]',
			  '[141]','[142]','[143]','[144]','[145]','[146]','[147]','[148]','[149]','[150]','[151]','[152]',
			  '[153]','[154]','[155]','[156]','[157]','[158]','[159]','[160]','[161]','[162]','[163]','[164]',
			  '[165]','[166]','[167]','[168]','[169]','[170]','[171]','[172]','[173]','[174]','[175]','[176]',
			  '[177]','[178]','[179]','[180]','[181]','[182]','[183]','[184]','[185]','[186]','[187]','[188]',
			  '[189]','[190]','[191]','[192]','[193]','[194]','[195]','[196]','[197]','[198]','[199]','[200]',
			  '[201]','[202]','[203]','[204]','[205]','[206]','[207]','[208]','[209]','[210]','[211]','[212]',
			  '[213]','[214]','[215]','[216]','[217]','[218]','[219]','[220]','[221]','[222]','[223]','[224]',
			  '[225]','[226]','[227]','[228]','[229]','[230]','[231]','[232]','[233]','[234]','[235]','[236]',
			  '[237]','[238]','[239]','[240]','[241]','[242]','[243]','[244]','[245]','[246]','[247]','[248]',
			  '[249]','[250]','[251]','[252]','[253]','[254]','[255]');

type
  TFrmMain = class(TForm)
	Page: TPageControl;
	tabBinTerm: TTabSheet;
	Panel2: TPanel;
	Panel1: TPanel;
	btnPause: TBitBtn;
	btnLoesch: TBitBtn;
	btnEnde: TBitBtn;
	tabSchnittstelle: TTabSheet;
	tabDaten: TTabSheet;
	Panel3: TPanel;
	btnOK1: TBitBtn;
	btnEnde1: TBitBtn;
	Panel4: TPanel;
	btnEnde2: TBitBtn;
	Panel5: TPanel;
	Panel6: TPanel;
	cbxCodeseite: TComboBox;
	lstCodes: TCheckListBox;
	Label6: TLabel;
	Panel7: TPanel;
	lstZeile: TListBox;
	Panel8: TPanel;
	Label7: TLabel;
	grpTextPC: TGroupBox;
	edtTextPC: TEdit;
	grpTextSPS: TGroupBox;
	edtTextSPS: TEdit;
	Timer1: TTimer;
	Label3: TLabel;
	Label8: TLabel;
	grpSSA: TRadioGroup;
	grpArt: TGroupBox;
	Label1: TLabel;
    Imag1: TImage;
	Label2: TLabel;
	lbA: TLabel;
	Label4: TLabel;
	lbB: TLabel;
	rgrpA: TRadioButton;
	rgrpB: TRadioButton;
	rgrpC: TRadioButton;
	rgrpD: TRadioButton;
	grpSSB: TRadioGroup;
	grpParitaet: TRadioGroup;
	grpStop: TRadioGroup;
	grpBaud: TRadioGroup;
	chkBuchse: TCheckBox;
	chkSound: TCheckBox;
	SDlg: TSaveDialog;
	btnSpeichern: TBitBtn;
	ed1: TMemo;
	ed2: TMemo;
	ed3: TMemo;
	rgrpSprache: TRadioGroup;
	tabInfo: TTabSheet;
	Panel9: TPanel;
	btnEnde3: TBitBtn;
    panMmVisual: TPanel;
	Label5: TLabel;
	Label9: TLabel;
	Label10: TLabel;
	Label11: TLabel;
	edHomepage: TEdit;
	edEMail: TEdit;
    imgBinTerm: TImage;
	Image2: TImage;
    Imag2: TImage;
    Imag3: TImage;
    Imag4: TImage;
    panSplitt1: TPanel;
    Panel10: TPanel;
    popCodeseite: TPopupMenu;
    mnCodeNeu: TMenuItem;
    mnCodeKopieren: TMenuItem;
    N1: TMenuItem;
    mnCodeLoeschen: TMenuItem;
	procedure btnEndeClick(Sender: TObject);
	procedure FormShow(Sender: TObject);
	procedure btnLoeschClick(Sender: TObject);
	procedure btnPauseClick(Sender: TObject);
	procedure rgrpAClick(Sender: TObject);
	procedure rgrpBClick(Sender: TObject);
	procedure rgrpCClick(Sender: TObject);
	procedure rgrpDClick(Sender: TObject);
	Procedure SetIcon(Index: Short);
	procedure grpSSBClick(Sender: TObject);
	procedure grpSSAClick(Sender: TObject);
	procedure FormCreate(Sender: TObject);
	procedure cbxCodeseiteClick(Sender: TObject);
	procedure lstCodesClick(Sender: TObject);
	procedure lstZeileDblClick(Sender: TObject);
	procedure Timer1Timer(Sender: TObject);
	procedure RxSplitter1DblClick(Sender: TObject);
	procedure RxSplitter1PosChanged(Sender: TObject);
	procedure SerialRxData(Sender: TObject; sDaten, sDSR: String);
	procedure btnOK1Click(Sender: TObject);
	procedure FormClose(Sender: TObject; var Action: TCloseAction);
	Function  ScrollDown(M: TMemo; Lines: LongInt): LongInt;
	procedure grpSSClick(Sender: TObject);
	procedure btnSpeichernClick(Sender: TObject);
	procedure FormDestroy(Sender: TObject);
	procedure rgrpSpracheClick(Sender: TObject);
	procedure Spracheinstellung(offset:Integer);
	Function  GetResString(Nr: Integer; bSprache: Boolean): String;
	procedure StartExplorer(Sender: TObject);
	procedure chkSoundClick(Sender: TObject);
	procedure panSplitt1MouseDown(Sender: TObject; Button: TMouseButton;
	  Shift: TShiftState; X, Y: Integer);
	procedure panSplitt1MouseUp(Sender: TObject; Button: TMouseButton;
	  Shift: TShiftState; X, Y: Integer);
	procedure Panel10MouseDown(Sender: TObject; Button: TMouseButton;
	  Shift: TShiftState; X, Y: Integer);
	procedure Panel10MouseUp(Sender: TObject; Button: TMouseButton;
	  Shift: TShiftState; X, Y: Integer);
	procedure lstCodesDblClick(Sender: TObject); // Internetexplorer mit Optionen Starten (html-Datei/Link)
	procedure SaveCodes;
    procedure mnCodeNeuClick(Sender: TObject);
    procedure mnCodeKopierenClick(Sender: TObject);
    procedure mnCodeLoeschenClick(Sender: TObject);
  private
	bPause: Boolean;	// Pause - Empfangene Daten verwerfen
	CodeSeite: Array [0..255] Of TCustCode; // Codeseite mit allen darstellbaren Zeichen
	iEmpfZeichen: LongInt; // Empfangene Zeichen der letzten Sekunde
	iEd2Width: LongInt; // Doppelclick auf Splitter
	iListe: Array [0..1] Of LongInt; // Position des letzten Datensatzes
	sDir: String;
	sIniFile: String;
	sCodeSeite: String;
  end;

var
  FrmMain: TFrmMain;
  Serial1: TSeriell;
  Serial2: TSeriell;
  iSplitPos1, iSplitPos2: LongInt;

implementation

{$R *.DFM}
{$R Sprache.res}

procedure TFrmMain.FormCreate(Sender: TObject);
Var AppIni: TIniFile;
	i, iCount, iSprache: LongInt;
	s: String;
begin
	Serial1 := TSeriell.Create(Self);
	Serial2 := TSeriell.Create(Self);
	Serial1.OnRxData := SerialRxData;
	Serial2.OnRxData := SerialRxData;
	GetDir(0, sDir);
	SDlg.InitialDir := sDir;
	iEd2Width := -1;
	sIniFile := sDir + '\BinTerm.ini';
	AppIni := TIniFile.Create(sIniFile);
	Try
		Left := AppIni.ReadInteger('FrmMain','Left',200);
		Top := AppIni.ReadInteger('FrmMain','Top',100);
		Width := AppIni.ReadInteger('FrmMain','Width',562);
		Height := AppIni.ReadInteger('FrmMain','Height',412);
		i := AppIni.ReadInteger('FrmMain','WindowState',3);
		Case i Of
		2:	WindowState := wsMaximized;
		Else WindowState := wsNormal;
		End;
		ed2.Visible := AppIni.ReadBool('FrmMain','ed2_Visible',False);
		ed2.Width := AppIni.ReadInteger('FrmMain','ed2_Width',287);
		Panel5.Width := AppIni.ReadInteger('FrmMain','Panel5_Width',181);
		edtTextPC.Text := AppIni.ReadString('FrmMain','edtTextPC_Text','> von PC');
		edtTextSPS.Text := AppIni.ReadString('FrmMain','edtTextSPS_Text','< von SPS');
		rgrpA.Checked := AppIni.ReadBool('FrmMain','rgrpA_Checked',True);
		rgrpB.Checked := AppIni.ReadBool('FrmMain','rgrpB_Checked',False);
		rgrpC.Checked := AppIni.ReadBool('FrmMain','rgrpC_Checked',False);
		rgrpD.Checked := AppIni.ReadBool('FrmMain','rgrpD_Checked',False);
		grpSSA.ItemIndex := AppIni.ReadInteger('FrmMain','grpSSA_ItemIndex',1);
		grpSSB.ItemIndex := AppIni.ReadInteger('FrmMain','grpSSB_ItemIndex',1);
		grpBaud.ItemIndex := AppIni.ReadInteger('FrmMain','grpBaud_ItemIndex',5);
		grpStop.ItemIndex := AppIni.ReadInteger('FrmMain','grpStop_ItemIndex',0);
		grpParitaet.ItemIndex := AppIni.ReadInteger('FrmMain','grpParitaet_ItemIndex',0);
		chkBuchse.Checked := AppIni.ReadBool('FrmMain','chkBuchse_Checked',True);
		chkSound.Checked := AppIni.ReadBool('FrmMain','chkSound_Checked',False);
		SDlg.FileName := AppIni.ReadString('FrmMain','SDlg_FileName',sDir + '\BinTerm.txt');
		i := AppIni.ReadInteger('Codes', 'Anzahl', 0);
		If i > 0 Then // User-Codeseiten laden
		Begin
			For iCount := 1 To i Do
			Begin // Alle Codeseiten einlesen
				s := AppIni.ReadString('Codes', 'Seite' + IntToStr(iCount), '');
				If s <> '' Then // Gültige Codeseiten in Liste eintragen
					cbxCodeseite.Items.Add(s);
			End;
		End;
		cbxCodeseite.ItemIndex := AppIni.ReadInteger('FrmMain','CodeSeite',0); // Codeseite einstellen
		sCodeSeite := AppIni.ReadString('FrmMain','Zeilenw',StringOfChar('0',256));
		iSprache := AppIni.ReadInteger('FrmMain','Sprache',0);
	Finally
		AppIni.Free;
		If Length(sCodeSeite) < 256 Then
			sCodeSeite := StringOfChar('0',256); // Zeilenwechselstring ist zu kurz (löschen)
		For i := 0 To 255 Do
			CodeSeite[i].Zeilenw := (sCodeSeite[i+1] = '1'); // Zeilenwechsel eintragen
	End;
	Timer1.Enabled := chkSound.Checked;
	rgrpSprache.Items.Clear; // Alle Sprachen zur Auswahl anzeigen
	i := 1001; // 1. Sprache
	Repeat
		If GetResString(i, False) <> '' Then
		Begin
			rgrpSprache.Items.Add(GetResString(i, False));
			Inc(i,1000);
		End
		Else i := 0;
	Until i = 0;
	rgrpSprache.Height := 16 + ((rgrpSprache.Items.Count) * 18);
	rgrpSprache.ItemIndex := iSprache;
	rgrpSpracheClick(Sender);
end;

procedure TFrmMain.FormShow(Sender: TObject);
begin
	Page.ActivePageIndex := 0;
	ed1.Clear;
	ed2.Clear;
	iListe[0] := 0;
	iListe[1] := 0;
	If rgrpB.Checked Then // Icon setzen
		rgrpBClick(Nil)
	Else If rgrpC.Checked Then
		rgrpCClick(Nil)
	Else If rgrpD.Checked Then
		rgrpDClick(Nil)
	Else rgrpAClick(Nil);
	lbA.Caption := 'COM ' + IntToStr(grpSSA.ItemIndex + 1); // COMn-Text
	lbB.Visible := grpSSB.Visible;
	lbB.Caption := 'COM ' + IntToStr(grpSSB.ItemIndex + 1);
	cbxCodeseiteClick(Nil); // darstellen
	btnOK1Click(Nil);
	Spracheinstellung(rgrpSprache.ItemIndex * 1000);
	If FileExists('C:\Programme\Plus!\Microsoft Internet\Iexplore.exe') Then
	Begin // Internetexplorer gefunden!
		edHomepage.Cursor := crHandPoint;
		edHomepage.OnClick := StartExplorer;
	End;
	ImgBinTerm.Picture.Icon := Application.Icon;
end;

procedure TFrmMain.btnEndeClick(Sender: TObject);
begin
	Close;
end;

procedure TFrmMain.btnLoeschClick(Sender: TObject);
begin
	ed1.Clear;
	ed2.Clear;
	iListe[0] := 0;
	iListe[1] := 0;
end;

procedure TFrmMain.btnPauseClick(Sender: TObject);
begin
	If Not bPause Then
	Begin // Empfangene Daten verwerfen
		bPause := True;
		btnPause.Caption := GetResString(1004, True);
		btnPause.Hint := GetResString(1504, True);
	End	Else Begin // Empfangene Daten wieder anzeigen
		bPause := False;
		btnPause.Caption := GetResString(1003, True);
		btnPause.Hint := GetResString(1503, True);
	End;
end;

procedure TFrmMain.rgrpAClick(Sender: TObject);
begin
	rgrpA.Checked := True;
	SetIcon(0);
end;

procedure TFrmMain.rgrpBClick(Sender: TObject);
begin
	SetIcon(2);
end;

procedure TFrmMain.rgrpCClick(Sender: TObject);
begin
	SetIcon(1);
end;

procedure TFrmMain.rgrpDClick(Sender: TObject);
begin
	lbB.Caption := 'COM ' + IntToStr(grpSSB.ItemIndex + 1);
	SetIcon(3);
end;

Procedure TFrmMain.SetIcon(Index: Short);
Begin
	btnOK1.Enabled := True;
	ed2.Visible := Index = 3;
	panSplitt1.Visible := ed2.Visible;
	grpSSB.Visible := ed2.Visible;
	lbB.Visible := ed2.Visible;
	Imag1.Visible := False;
	Imag2.Visible := False;
	Imag3.Visible := False;
	Imag4.Visible := False;
	Case Index Of
	0: Imag1.Visible := True;
	1: Imag2.Visible := True;
	2: Imag3.Visible := True;
	3: Imag4.Visible := True;
	End;
End;

procedure TFrmMain.grpSSAClick(Sender: TObject);
begin
	lbA.Caption := 'COM ' + IntToStr(grpSSA.ItemIndex + 1);
	btnOK1.Enabled := True;
end;

procedure TFrmMain.grpSSBClick(Sender: TObject);
begin
	lbB.Caption := 'COM ' + IntToStr(grpSSB.ItemIndex + 1);
	btnOK1.Enabled := True;
end;

procedure TFrmMain.chkSoundClick(Sender: TObject);
begin
	iEmpfZeichen := 0;
	Timer1.Enabled := chkSound.Checked;
end;

procedure TFrmMain.cbxCodeseiteClick(Sender: TObject);
Var i: Short;
	s: String;
	Function GetString(Var s: String): String;
	Var i: Integer;
	Begin
		If Length(s) > 0 Then
		Begin
			If s[1] = '''' Then Delete(s, 1, 1); // Startzeichen löschen
			If Pos('''', s) = Length(s) Then // Letztes Zeichen
			Begin
				Delete(s, Length(s), 1);
				Result := s; // Zeichenkette zurückgeben
				s := ''; // restliche Zeichen
			End Else Begin
				i := Pos(''',''', s); // Trennzeichen suchen
				If i > 0 Then
				Begin // Trennzeichen gefunden
					Result := Copy(s, 1, i - 1);
					Delete(s, 1, i + 1);
				End Else Begin // Keine Zeichen mehr
					Result := '';
					s := '';
				End;
			End;
		End Else Result := ''; // Kein String mehr gefunden
	End;
begin
	Case cbxCodeseite.ItemIndex Of
	0:	Begin // Anzeige als ASCII-Codes
			lstCodes.Clear;
			For i := 0 To 255 Do
			Begin
				CodeSeite[i].Code := cAsc[i];
				lstCodes.Items.Add(StringReplace(Format('%3u',[i]),' ','0',[rfReplaceAll]) + ': ' + cAsc[i]);
				lstCodes.Checked[i] := CodeSeite[i].Zeilenw;
			End;
			mnCodeLoeschen.Enabled := False;
		End;
	1:	Begin // Anzeige als Dezimal-Codes
			lstCodes.Clear;
			For i := 0 To 255 Do
			Begin
				CodeSeite[i].Code := StringReplace(Format('%3u',[i]),' ','0',[rfReplaceAll]) + ' ';
				lstCodes.Items.Add(CodeSeite[i].Code);
				lstCodes.Checked[i] := CodeSeite[i].Zeilenw;
			End;
			mnCodeLoeschen.Enabled := False;
		End;
	2:	Begin // Anzeige als Hexadezimal-Codes
			lstCodes.Clear;
			For i := 0 To 255 Do
			Begin
				CodeSeite[i].Code := IntToHex(i, 2) + ' ';
				lstCodes.Items.Add(StringReplace(Format('%3u',[i]),' ','0',[rfReplaceAll]) + ': ' + IntToHex(i, 2));
				lstCodes.Checked[i] := CodeSeite[i].Zeilenw;
			End;
			mnCodeLoeschen.Enabled := False;
		End;
	Else Begin
			With TIniFile.Create(sIniFile) Do
			Try // Lesen der Codes
				s := '''' + ReadString('Codes', 'Codes' + IntToStr(cbxCodeseite.ItemIndex - 2), '') + '''';
			Finally
				Free;
			End;
			lstCodes.Clear;
			For i := 0 To 255 Do
			Begin
				CodeSeite[i].Code := GetString(s); // Zeichen aus Zeichenkette lesen
				lstCodes.Items.Add(StringReplace(Format('%3u',[i]),' ','0',[rfReplaceAll]) + ': ' + CodeSeite[i].Code);
				lstCodes.Checked[i] := CodeSeite[i].Zeilenw;
			End;
			mnCodeLoeschen.Enabled := True;
		End;
	End; (* Case *)
	lstCodesClick(Nil); // Liste mit Zeilenumbrüchen neu schreiben
end;

procedure TFrmMain.lstCodesClick(Sender: TObject);
Var i: Short;
begin
	sCodeSeite := StringOfChar('0',256); // Merker für Zeilenumbruch neu schreiben
	lstZeile.Clear; // Zeilenumbruchbox löschen
	For i := 0 To lstCodes.Items.Count - 1 Do
	Begin // Alle Items in der List-Box der Codes durchlaufen
		CodeSeite[i].Zeilenw := lstCodes.Checked[i];
		If CodeSeite[i].Zeilenw Then
		Begin // Eintrag der Zeilenwechsel in Listbox und Merker
			lstZeile.Items.Add(lstCodes.Items.Strings[i]);
			sCodeSeite[i+1] := '1';
		End;
	End;
end;

procedure TFrmMain.lstZeileDblClick(Sender: TObject);
begin
	If lstZeile.ItemIndex < 0 Then Exit;
	If lstZeile.Selected[lstZeile.ItemIndex] Then
	Begin // Löschen eines Eintrages aus der Zeilenwechselbox
		lstCodes.Checked[StrToInt(Copy(lstZeile.Items.Strings[lstZeile.ItemIndex],1,3))] := False;
		lstCodesClick(Nil);
	End;
end;

procedure TFrmMain.Timer1Timer(Sender: TObject);
begin // Soundausgabe unterbinden, wenn zu viele Zeichen kommen
	If (iEmpfZeichen > 0) And chkSound.Checked Then
		Sysutils.Beep;
	If iEmpfZeichen > 20 Then
		chkSound.Checked := False;
	iEmpfZeichen := 0;
end;

procedure TFrmMain.RxSplitter1DblClick(Sender: TObject);
begin
	iEd2Width := (ed2.Width + ed1.Width) Div 2;
end;

procedure TFrmMain.RxSplitter1PosChanged(Sender: TObject);
begin
	If iEd2Width > 0 Then
	Begin
		Ed2.Width := iEd2Width;
		iEd2Width := -1;
	End;
end;

procedure TFrmMain.SerialRxData(Sender: TObject; sDaten, sDSR: String);
Var t: String;
	i, k: Integer;
	j: Boolean;
begin
	i := Length(sDaten);
	If chkSound.Checked Then
		Inc(iEmpfZeichen, i); // Empfangene Zeichen addieren
	If bPause Then Exit;
	j := Sender = Serial2; // False = von Serial1 / True = von Serial 2
	If Not rgrpD.Checked Or (Not j) Then
		ed1.Lines.BeginUpdate
	Else ed2.Lines.BeginUpdate;
	For k := 1 To i Do
	Begin
		If Codeseite[Ord(sDaten[k])].Zeilenw Then
		Begin
			If sDSR[k] = #1 Then
				t := edtTextPC.Text // Zeilenende vom PC
			Else t := edtTextSPS.Text; // Zeilenende-Text
		End Else t := '';
		If Not rgrpD.Checked Or (Not j) Then
		Begin
			ed1.Lines.Strings[iListe[0]] := ed1.Lines.Strings[iListe[0]] + Codeseite[Ord(sDaten[k])].Code + t;
			If Codeseite[Ord(sDaten[k])].Zeilenw Then
			Begin
				iListe[0] := ed1.Lines.Add('');
				If (ed1.CaretPos.y + 3) >= ((ed1.Height - 8) Div 15) Then // 15 ist die Höhe einer Zeile
					ScrollDown(ed1, 1); // um eine Zeile nach Unten Scrollen
			End;
		End Else Begin
			ed2.Lines.Strings[iListe[1]] := ed2.Lines.Strings[iListe[1]] + Codeseite[Ord(sDaten[k])].Code + t;
			If Codeseite[Ord(sDaten[k])].Zeilenw Then
			Begin
				iListe[1] := ed2.Lines.Add('');
				If (ed2.CaretPos.y + 3) >= ((ed2.Height - 8) Div 15) Then // 15 ist die Höhe einer Zeile
					ScrollDown(ed2, 1); // um eine Zeile nach Unten Scrollen
			End;
		End;
	End;
	If Not rgrpD.Checked Or (Not j) Then
		ed1.Lines.EndUpdate
	Else ed2.Lines.EndUpdate;
end;

procedure TFrmMain.btnOK1Click(Sender: TObject);
begin // Alle Seriellen Schnittstellen neu öffnen
	If rgrpD.Checked And (grpSSA.ItemIndex = grpSSB.ItemIndex) Then
	Begin
		ShowMessage(GetResString(1056, True));
		Exit;
	End;
	Serial1.Active := False;
	Serial2.Active := False;
	Serial1.Port := grpSSA.ItemIndex + 1;
	Serial1.Baudrate := StrToInt(grpBaud.Items.Strings[grpBaud.ItemIndex]);
	Case grpParitaet.ItemIndex Of
	0:	Serial1.Parity := paNone; // Keine
	1:	Serial1.Parity := paEven; // Gerade
	2:	Serial1.Parity := PaOdd; // Ungerade
	End;
	Case grpStop.ItemIndex Of
	0:	Serial1.StopBits := st1bit;
	1:  Serial1.StopBits := st2bit;
	End;
	If rgrpD.Checked Then
	Begin // 2. Schnittstelle wird benötigt
		Serial2.Port := grpSSB.ItemIndex + 1;
		Serial2.Baudrate := Serial1.Baudrate;
		Serial2.Parity := Serial1.Parity;
		Serial2.StopBits := Serial1.StopBits;
		Serial2.Active := True;
		If Serial2.ErrorCode <> 0 then
		begin
			Serial2.Active := False;
			MessageDlg(GetResString(1057, True) + IntToStr( Serial2.Port ) + ' ' + GetResString(1058, True), mtError, [mbOK], 0 );
		End;
	End;
	Serial1.Active := True;
	If Serial1.ErrorCode <> 0 then
	begin
		Serial1.Active := False;
		MessageDlg(GetResString(1057, True) + IntToStr( Serial1.Port ) + ' ' + GetResString(1058, True), mtError, [mbOK], 0 );
	End;
	btnOK1.Enabled := False;
end;

procedure TFrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
Var	AppIni: TIniFile;
begin
	Serial1.Active := False;
	Serial2.Active := False;
	AppIni := TIniFile.Create(sIniFile);
	Try
		If WindowState = wsNormal Then
		Begin
			AppIni.WriteInteger('FrmMain','Left',Left);
			AppIni.WriteInteger('FrmMain','Top',Top);
			AppIni.WriteInteger('FrmMain','Width',Width);
			AppIni.WriteInteger('FrmMain','Height',Height);
		End;
		AppIni.WriteInteger('FrmMain','WindowState',Ord(WindowState));
		AppIni.WriteBool('FrmMain','ed2_Visible',ed2.Visible);
		AppIni.WriteInteger('FrmMain','ed2_Width',ed2.Width);
		AppIni.WriteInteger('FrmMain','Panel5_Width',Panel5.Width);
		AppIni.WriteString('FrmMain','edtTextPC_Text',edtTextPC.Text);
		AppIni.WriteString('FrmMain','edtTextSPS_Text',edtTextSPS.Text);
		AppIni.WriteBool('FrmMain','rgrpA_Checked',rgrpA.Checked);
		AppIni.WriteBool('FrmMain','rgrpB_Checked',rgrpB.Checked);
		AppIni.WriteBool('FrmMain','rgrpC_Checked',rgrpC.Checked);
		AppIni.WriteBool('FrmMain','rgrpD_Checked',rgrpD.Checked);
		AppIni.WriteInteger('FrmMain','grpSSA_ItemIndex',grpSSA.ItemIndex);
		AppIni.WriteInteger('FrmMain','grpSSB_ItemIndex',grpSSB.ItemIndex);
		AppIni.WriteInteger('FrmMain','grpBaud_ItemIndex',grpBaud.ItemIndex);
		AppIni.WriteInteger('FrmMain','grpStop_ItemIndex',grpStop.ItemIndex);
		AppIni.WriteInteger('FrmMain','grpParitaet_ItemIndex',grpParitaet.ItemIndex);
		AppIni.WriteBool('FrmMain','chkBuchse_Checked',chkBuchse.Checked);
		AppIni.WriteBool('FrmMain','chkSound_Checked',chkSound.Checked);
		AppIni.WriteString('FrmMain','SDlg_FileName',SDlg.FileName);
		AppIni.WriteInteger('FrmMain','CodeSeite',cbxCodeseite.ItemIndex);
		AppIni.WriteString('FrmMain','Zeilenw',sCodeSeite);
		AppIni.WriteInteger('FrmMain','Sprache',rgrpSprache.ItemIndex);
	Finally
		AppIni.Free;
	End;
	Action := caFree;
end;

Function TFrmMain.ScrollDown(M: TMemo; Lines: LongInt): LongInt;
Begin
	Result := SendMessage(M.Handle, EM_LINESCROLL, 0, Lines);
End;

procedure TFrmMain.grpSSClick(Sender: TObject);
begin
	btnOK1.Enabled := True;
end;

procedure TFrmMain.btnSpeichernClick(Sender: TObject);
begin
	If SDlg.Execute Then
	Begin // Speichern der Daten in eine Datei
		If FileExists(SDlg.FileName) Then
			DeleteFile(sDlg.FileName);
		ed3.Clear;
		If rgrpD.Checked Then
			ed3.Lines.Add(GetResString(1054, True));
		Try
			ed3.Lines.AddStrings(ed1.Lines);
		Except
		End;
		If rgrpD.Checked Then
		Try
			ed3.Lines.Add(GetResString(1055, True));
			ed3.Lines.AddStrings(ed2.Lines);
		Except
		End;
		Try
			ed3.Lines.SaveToFile(SDlg.FileName);
		Except;
		End;
	End;
end;

procedure TFrmMain.FormDestroy(Sender: TObject);
begin
	Serial1.Destroy;
	Serial2.Destroy;
end;

procedure TFrmMain.rgrpSpracheClick(Sender: TObject);
begin
	Spracheinstellung(rgrpSprache.ItemIndex * 1000);
end;

Procedure TFrmMain.Spracheinstellung(offset:Integer);
Var i,k: integer;
	c  : TObject;
Begin
	For i := 0 To ComponentCount-1 Do
	Begin
		If Components[i].Tag > 1000 Then
		Begin
			c := Components[i];
			If (c Is TLabel) Then
			Begin
				(c As TLabel).Caption := GetResString((c As TLabel).Tag + Offset, False);
				(c As TLabel).Hint := GetResString((c As TLabel).Tag + Offset + 500, False);
			End;
			If (c Is TBitBtn) Then
			Begin
				(c As TBitBtn).Caption := GetResString((c As TBitBtn).Tag + Offset, False);
				(c As TBitBtn).Hint := GetResString((c As TBitBtn).Tag + Offset + 500, False);
			End;
			If (c Is TCheckbox) Then
			Begin
				(c As TCheckbox).Caption := GetResString((c As TCheckbox).Tag + Offset, False);
				(c As TCheckbox).Hint := GetResString((c As TCheckbox).Tag + Offset + 500, False);
			End;
			If (c Is TRadioButton) Then
			Begin
				(c As TRadioButton).Caption := GetResString((c As TRadioButton).Tag + Offset, False);
				(c As TRadioButton).Hint := GetResString((c As TRadioButton).Tag + Offset + 500, False);
			End;
			If (c Is TGroupBox) Then
			Begin
				(c As TGroupBox).Caption := GetResString((c As TGroupBox).Tag + Offset, False);
				(c As TGroupBox).Hint := GetResString((c As TGroupBox).Tag + Offset + 500, False);
			End;
			If (c Is TTabSheet) Then
			Begin
				(c As TTabSheet).Caption := GetResString((c As TTabSheet).Tag + Offset, False);
				(c As TTabSheet).Hint := GetResString((c As TTabSheet).Tag + Offset + 500, False);
			End;
			If (c Is TListBox) Then
				(c As TListBox).Hint := GetResString((c As TListBox).Tag + Offset + 500, False);
			If (c Is TCheckListBox) Then
				(c As TCheckListBox).Hint := GetResString((c As TCheckListBox).Tag + Offset + 500, False);
			If (c Is TComboBox) Then
				(c As TComboBox).Hint := GetResString((c As TComboBox).Tag + Offset + 500, False);
			If (c Is TEdit) Then
				(c As TEdit).Hint := GetResString((c As TEdit).Tag + Offset + 500, False);
			If (c Is TRadioGroup) Then
			Begin
				(c As TRadioGroup).Caption := GetResString((c As TRadioGroup).Tag + Offset, False);
				(c As TRadioGroup).Hint := GetResString((c As TRadioGroup).Tag + Offset + 500, False);
				If (c As TRadioGroup).Tag Mod 10 = 0 Then
				Begin // Wenn letzte Stelle von Tag eine 0 ist, dann Items ändern
					For k := 1 To (c As TRadioGroup).Items.Count Do
					Begin
						(c As TRadioGroup).Items.Strings[k - 1] := GetResString(k + Offset + (c As TRadioGroup).Tag, False);
					End;
				End;
			End;
			If (c Is TMenuItem) Then
			Begin
				(c As TMenuItem).Caption := GetResString((c As TMenuItem).Tag + Offset, False);
				(c As TMenuItem).Hint := GetResString((c As TMenuItem).Tag + Offset + 500, False);
			End;
		End;
	End;
End;

Function TFrmMain.GetResString(Nr: Integer; bSprache: Boolean): String;
Var p: PChar;
Begin
	If (Nr < 2000) And bSprache Then // Sprache dazurechnen
		Nr := Nr + (rgrpSprache.ItemIndex * 1000);
	p := StrAlloc(256);
	LoadString(Hinstance, Nr, p, 255);
	result := p;
	StrDispose(p);
End;

procedure TFrmMain.StartExplorer(Sender: TObject); // Internetexplorer mit Optionen Starten (html-Datei/Link)
begin
	If Sender Is TEdit Then
	Try // Internetlink in TEdit
		If (Sender As TEdit).Cursor = crHandPoint Then
			ShellExecute(Handle, 'open', PChar('Iexplore.exe'),
				PChar((Sender As TEdit).Text),
				PChar('C:\Programme\Plus!\Microsoft Internet\'), SW_RESTORE);
	Except
	End;
end;

procedure TFrmMain.panSplitt1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
	iSplitPos1 := x;
	Screen.Cursor := crHSplit;
end;

procedure TFrmMain.panSplitt1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
Var i: LongInt;
begin
	Screen.Cursor := crDefault;
	i := ed2.Left + X - iSplitPos1;
	If i < 3 Then i := 3;
	If i > Panel2.Width Then i := Panel2.Width;
	ed2.Width := Panel2.Width - i;
end;

procedure TFrmMain.Panel10MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
	iSplitPos2 := x;
	Screen.Cursor := crHSplit;
end;

procedure TFrmMain.Panel10MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
Var i: LongInt;
begin
	Screen.Cursor := crDefault;
	i := Panel5.Width + X - iSplitPos2;
	If i < 0 Then i := 0;
	If i > tabDaten.Width - 2 Then i := tabDaten.Width - 2;
	Panel5.Width := i;
end;

procedure TFrmMain.lstCodesDblClick(Sender: TObject);
Var s, sVor: String;
begin
	If lstCodes.ItemIndex < 0 Then Exit; // Kein Element angewählt
	If cbxCodeseite.ItemIndex < 3 Then Exit; // Feste Einträge können nicht geändert werden
	sVor := Copy(lstCodes.Items.Strings[lstCodes.ItemIndex], 1, 5);
	s := Copy(lstCodes.Items.Strings[lstCodes.ItemIndex], 6, 30); // Eintragsbezeichnung
	If InputQuery(cbxCodeseite.Text, GetResString(1070, True), s) Then
	Begin // Eintrag ändern
		lstCodes.Items.Strings[lstCodes.ItemIndex] := sVor + Copy(s, 1, 30);
		SaveCodes;
		lstCodesClick(Sender);
	End;
end;

procedure TFrmMain.SaveCodes;
Var iCount: LongInt;
	sCodes: String;
Begin
	sCodes := '''' + Copy(lstCodes.Items.Strings[0], 6, 30) + '''';
	For iCount := 1 To 255 Do
		sCodes := sCodes + ',''' + Copy(lstCodes.Items.Strings[iCount], 6, 30) + '''';
	With TIniFile.Create(sIniFile) Do
	Try
		WriteString('Codes', 'Codes' + IntToStr(cbxCodeseite.ItemIndex - 2), sCodes);
	Finally
		Free;
	End;
End;

procedure TFrmMain.mnCodeNeuClick(Sender: TObject); // Neue Codeseite erzeugen
Var s: String;
	i: LongInt;
begin
	s := '';
	If InputQuery(StringReplace(tabDaten.Caption, '&', '', []), GetResString(1071, True), s) Then
	Begin // Neue Codeseite erzeugen
		If s <> '' Then
		Begin
			i := cbxCodeseite.Items.Add(s);
			With TIniFile.Create(sIniFile) Do
			Try // Neue Codeseite eintragen
				WriteInteger('Codes', 'Anzahl', cbxCodeseite.Items.Count - 3);
				WriteString('Codes', 'Seite' + IntToStr(i - 2), s);
				WriteString('Codes', 'Codes' + IntToStr(i - 2), '');
			Finally
				Free;
			End;
			cbxCodeseite.ItemIndex := i; // Anzeigen
			cbxCodeseiteClick(Sender);
		End;
	End;
end;

procedure TFrmMain.mnCodeKopierenClick(Sender: TObject); // Codeseite kopieren
Var s: String;
	i, iCount: LongInt;
begin
	s := cbxCodeseite.Text;
	If InputQuery(StringReplace(tabDaten.Caption, '&', '', []), GetResString(1072, True), s) Then
	Begin // Neue Codeseite erzeugen
		If s <> '' Then
		Begin
			i := cbxCodeseite.Items.Add(s);
			If cbxCodeseite.ItemIndex = 1 Then
			Begin // Dezimal kopieren
				lstCodes.Clear;
				For iCount := 0 To 255 Do
					lstCodes.Items.Add(StringReplace(Format('%3u',[iCount]),' ','0',[rfReplaceAll]) + ': ' + StringReplace(Format('%3u',[iCount]),' ','0',[rfReplaceAll]));
			End;
			With TIniFile.Create(sIniFile) Do
			Try // Neue Codeseite eintragen
				WriteInteger('Codes', 'Anzahl', cbxCodeseite.Items.Count - 3);
				WriteString('Codes', 'Seite' + IntToStr(i - 2), s);
			Finally
				Free;
			End;
			cbxCodeseite.ItemIndex := i; // Anzeigen
			SaveCodes; // Kopie Sichern
			cbxCodeseiteClick(Sender);
		End;
	End;
end;

procedure TFrmMain.mnCodeLoeschenClick(Sender: TObject); // Codeseite löschen
Var s: String;
	i, iCount: LongInt;
begin
	If MessageDlg(GetResString(1074, True), mtConfirmation, [mbYes, mbNo], 0) = mrYes Then
	Begin
		If cbxCodeseite.ItemIndex = (cbxCodeseite.Items.Count - 1) Then
			i := cbxCodeseite.ItemIndex - 1 // Letzter Eintrag wird gelöscht
		Else i := cbxCodeseite.ItemIndex; // ein Zwischeneintrag wird gelöscht
		With TIniFile.Create(sIniFile) Do
		Try
			For iCount := cbxCodeseite.ItemIndex + 1 To cbxCodeseite.Items.Count - 1 Do
			Begin // Alle nachfolgenden Einträge verschieben
				s := ReadString('Codes', 'Seite' + IntToStr(iCount - 2), '');
				WriteString('Codes', 'Seite' + IntToStr(iCount - 3), s);
				s := ReadString('Codes', 'Codes' + IntToStr(iCount - 2), '');
				WriteString('Codes', 'Codes' + IntToStr(iCount - 3), s);
			End;
			WriteInteger('Codes', 'Anzahl', cbxCodeseite.Items.Count - 4);
			DeleteKey('Codes', 'Seite' + IntToStr(cbxCodeseite.Items.Count - 3));
			DeleteKey('Codes', 'Codes' + IntToStr(cbxCodeseite.Items.Count - 3));
		Finally
			Free;
		End;
		cbxCodeseite.Items.Delete(cbxCodeseite.ItemIndex);
		cbxCodeseite.ItemIndex := i; // Anzeigen
		cbxCodeseiteClick(Sender);
	End;
end;

end.
