/** Editor ED - Modul ED: Editor in C und Win-API **/

// Cop. 2008, 2017 www.asdala.de. Alle Rechte vorbehalten.

/** Include-Dateien **/
#include "top.h" // Compile-Scope, muss zuerst kommen
#include <windows.h>
#include <shlobj.h> // SHGetFolderPath etc. V1.1 New
#include <commctrl.h> // SB_SETPARTS etc.
#ifdef BUILD_RE
#include <richedit.h>
#endif
#include "etc.h" // Compiler-spez. Ergaenzungen
#include "res.h" // Ressourcen-Konstanten
#include "ut.h" // Utility-Funktionen
#ifdef BUILD_FR
#include "fr.h" // Suchen/Ersetzen-Funktionen
#endif
#ifdef BUILD_ST
#include "st.h" // Systemtray-Funktionen
#endif

#ifdef UNICODE
#pragma message ("UNICODE-Version")
#else
#pragma message ("ANSI-Version")
#endif
#ifdef _WIN64
#pragma message ("WIN64-Version")
#else
#pragma message ("WIN32-Version")
#endif

/** Globale Variablen **/
static const TCHAR appTitle[] = TEXT("ED");
static TCHAR filePath[MAX_PATH]; // Von diversen Funktionen via ofn gesetzt
static const TCHAR fileDefExt[] = TEXT("txt"); // Standarddateierweiterung
static const TCHAR fileFilter[] =
  TEXT("Texte (*.txt, *.log)\0*.txt;*.log\0Alle Dateien (*.*)\0*.*\0");
static int dirty, readOnly, printPgNo=1; // Datei veraendert, schreibgeschuetzt, Drucke Seitenzahl
#ifdef BUILD_RE
static HINSTANCE richEditLib; // RichEditbibliothek
#endif

/** Praeprozessordirektiven **/
#define MAXTEXT 200000 // Max. Textgroesse von EDIT/RICHTEXT-Control
#define MESTAT(val) ((val) ? MF_ENABLED : MF_GRAYED) // Schalte Menueeintraege ein oder aus
enum { STSTAT, STINDI, STFILE }; // Namen der Statusleistenteile statt 0, 1, 2

/** Function: Ermittele HDC des Standarddruckers **/
static HDC getDefaultPrinterDC(void)
  {
  PRINTDLG pd;
  ZeroMemory(&pd, sizeof(PRINTDLG));
  pd.lStructSize = sizeof(PRINTDLG);
  pd.Flags = PD_RETURNDEFAULT | PD_RETURNDC;
  PrintDlg(&pd);
  if (pd.hDevMode)
    GlobalFree(pd.hDevMode); // Falls beim Aufruf von PrintDlg hDevMode und hDevNames NULL sind,
  if (pd.hDevNames) // alloziert OS Speicher und fuellt Strukturen mit Druckerwerten;
    GlobalFree(pd.hDevNames); // daher sollten wir Speicher wieder freigeben.
  return pd.hDC;
  }

/** Function: Menue Waehle Schrift aus **/
static BOOL menuOptionsFont(HWND hWnd, LOGFONT *lf, DWORD cfFlags)
  {
  CHOOSEFONT cf;
  BOOL result;
  ZeroMemory(&cf, sizeof(CHOOSEFONT));
  cf.lStructSize = sizeof(CHOOSEFONT);
  cf.hwndOwner = hWnd;
  if (cfFlags & CF_PRINTERFONTS) // menuOptionsFont() fuer Druckschrift aufgerufen
    if ((cf.hDC=getDefaultPrinterDC()) == NULL) // Nur Schriften des Standarddruckers verfuegbar
      return FALSE;
  cf.lpLogFont = lf; // Druck- oder Screen-Logfont
  cf.Flags = CF_INITTOLOGFONTSTRUCT | cfFlags; // Kein CF_EFFECTS, da Underlined etc. unnoetig
  result = ChooseFont(&cf); // Mit CF_INITTOLOGFONTSTRUCT befuellt ChooseFont indirekt auch logFont
  if (cf.hDC)
    DeleteDC(cf.hDC);
  return result;
  }

/** Function: Lade ANSI-Datei ins EDIT-Fenster **/
static BOOL fileLoad(HWND hWnd, TCHAR fileName[])
  {
  HANDLE hFile;
  BOOL ok = FALSE;
  DWORD fileSize, read;
  char *buf;

  hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  if (hFile != INVALID_HANDLE_VALUE) {
    fileSize = GetFileSize(hFile, NULL);
    if (fileSize != 0xFFFFFFFF) {
      if ((buf = (char*)GlobalAlloc(GPTR, fileSize + 1)) != NULL) {
        if (ReadFile(hFile, buf, fileSize, &read, NULL)) { // Es gibt keine *A- bzw. *W-Version
          buf[fileSize] = '\0'; // [ANSIONLY]; Null im Text interpretiert EDIT-Control als Textende
          ok = SetWindowTextA(hWnd, buf); // [ANSIONLY]
          }
        GlobalFree(buf);
        }
      }
    CloseHandle(hFile);
    }
  return ok;
  }

/** Function: Schreibe EDIT-Fenster in ANSI-Datei **/
static BOOL fileSave(HWND hWnd, TCHAR fileName[])
  {
  HANDLE hFile;
  BOOL ok = FALSE;
  DWORD textLen, bufSize, written;
  char *buf;

  hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  if (hFile != INVALID_HANDLE_VALUE) {
    if ((textLen=GetWindowTextLength(hWnd)) > 0) {
      bufSize = textLen + 1;
      if ((buf =(char*)GlobalAlloc(GPTR, bufSize)) != NULL) {
        if (GetWindowTextA(hWnd, buf, bufSize)) // [ANSIONLY]
          ok = WriteFile(hFile, buf, textLen, &written, NULL);
        GlobalFree(buf);
        }
      }
    CloseHandle(hFile);
    }
  return ok;
  }

/** Function: Menue Datei Neu **/
static void menuFileNew(HWND hWnd)
  {
  HWND hWinEdit = GetDlgItem(hWnd, IDW_WIN_EDIT);
  SetDlgItemText(hWnd, IDW_WIN_EDIT, TEXT("")); // Loesche EDIT-Text
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STINDI, (LPARAM)TEXT("")); // Oder "Neu"
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STFILE, (LPARAM)TEXT("")); // Oder "Unbenannt"
  dirty = 0;
  *filePath = 0;
  SendMessage(hWinEdit, EM_SETREADONLY, readOnly=0, 0);
  }

/** Function: Lade Datei **/
static BOOL fileOpen(HWND hWnd, TCHAR newFilePath[], int userReadOnly)
  {
  HWND hWinEdit = GetDlgItem(hWnd, IDW_WIN_EDIT);
  TCHAR s[6] = TEXT("\4");
  int i;
  readOnly = userReadOnly; // V1.1 Fix
  #ifdef BUILD_RO
  if (!readOnly)
    readOnly = (GetFileAttributes(newFilePath) & FILE_ATTRIBUTE_READONLY) ? 1 : 0;
  #endif
  if (!fileLoad(hWinEdit, newFilePath)) {
    ut_messageBoxExt(hWnd, TEXT("Kann Datei '%s' nicht laden!"), appTitle, MB_ICONWARNING,
    newFilePath);
    return FALSE;
    }
  lstrcpyn(filePath, newFilePath, MAX_PATH);
  SendMessage(hWinEdit, EM_SETREADONLY, readOnly, 0);
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STINDI, (LPARAM)TEXT("")); // Oder "Geoeffnet"
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STFILE, (LPARAM)filePath);
  dirty = 0;
  ShowWindow(hWnd, SW_SHOW);
  /* V1.1: Steht ein .LOG zu Beginn der Datei, fuege Datum am Ende ein */
  if (!readOnly)
    if (SendMessage(hWinEdit, EM_GETLINE, (WPARAM)0, (LPARAM)s) > 0)
      if (lstrcmp(s,TEXT(".LOG")) == 0) {
        i = GetWindowTextLength(hWinEdit);
        SendMessage(hWinEdit, EM_SETSEL, (WPARAM)i, (LPARAM)i);
        SendMessage(hWinEdit, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)TEXT("\r\n--- "));
        SendMessage(hWnd, WM_COMMAND, (WPARAM)IDM_EDIT_INSDATE, (LPARAM)0);
        SendMessage(hWinEdit, EM_REPLACESEL, (WPARAM)FALSE, (LPARAM)TEXT("\r\n"));
        }
  return TRUE;
  }

/** Function: Menue Datei oeffnen **/
static void menuFileOpen(HWND hWnd)
  {
  OPENFILENAME ofn;
  ZeroMemory(&ofn, sizeof ofn);
  ofn.lStructSize = sizeof ofn;
  ofn.hwndOwner = hWnd;
  ofn.lpstrFilter = fileFilter;
  ofn.lpstrFile = filePath;
  ofn.nMaxFile = MAX_PATH;
  ofn.lpstrDefExt = fileDefExt;
  ofn.Flags = OFN_FILEMUSTEXIST;
  if (!GetOpenFileName(&ofn))
    return;
  fileOpen(hWnd, filePath, ofn.Flags & OFN_READONLY); // Merke Benutzerwahl RO oder RW aus Dialog
  }

/** Function: Menue Datei speichern unter **/
static void menuFileSaveAs(HWND hWnd)
  {
  HWND hWinEdit;
  OPENFILENAME ofn;
  ZeroMemory(&ofn, sizeof ofn);
  ofn.lStructSize = sizeof ofn;
  ofn.hwndOwner = hWnd;
  ofn.lpstrFilter = fileFilter;
  ofn.lpstrFile = filePath;
  ofn.nMaxFile = MAX_PATH;
  ofn.lpstrDefExt = fileDefExt;
  ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
  if (!GetSaveFileName(&ofn))
    return;
  hWinEdit = GetDlgItem(hWnd, IDW_WIN_EDIT);
  if (!fileSave(hWinEdit, filePath)) {
    ut_messageBoxExt(hWnd, TEXT("Kann Datei '%s' nicht speichern!"), appTitle, MB_ICONWARNING,
    filePath);
    return;
    }
  SendMessage(hWinEdit, EM_SETREADONLY, readOnly=0, 0); // V1.1 Fix
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STINDI, (LPARAM)TEXT(""));
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STFILE, (LPARAM)filePath);
  dirty = 0;
  }

/** Function: Menue Datei speichern **/
static void menuFileSave(HWND hWnd)
  {
  HWND hWinEdit = GetDlgItem(hWnd, IDW_WIN_EDIT);
  if (*filePath == 0) {
    menuFileSaveAs(hWnd);
    return;
    }
  if (!fileSave(hWinEdit, filePath)) {
    ut_messageBoxExt(hWnd, TEXT("Kann Datei '%s' nicht speichern!"), appTitle, MB_ICONWARNING,
    filePath);
    return;
    }
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STINDI, (LPARAM)TEXT(""));
  dirty = 0;
  }

/** Function: Menue Seite einrichten **/
static void menuFilePageSetup(PAGESETUPDLG *psd)
  {
  if (PageSetupDlg(psd)) { // Druckseiteninitialisierung schon in WM_CREATE erfolgt
    if (psd->hDevMode) { // Falls beim Aufruf von PageSetupDlg hDevMode und hDevNames NULL sind,
      GlobalFree(psd->hDevMode); psd->hDevMode = NULL; } // alloziert OS Speicher und fuellt die
    if (psd->hDevNames) { // Strukturen mit Druckerwerten; daher geben wir Speicher wieder frei.
      GlobalFree(psd->hDevNames); psd->hDevNames = NULL; }
    }
  }

/** Function: Drucke Datei **/
static BOOL filePrint(HWND hWnd, PAGESETUPDLG *psd, LOGFONT *lfPrt, PRINTDLG *pd)
  {
  BOOL ok = TRUE;
  DOCINFO di;
  HFONT hSavedPrintFont, hPrintFont;
  HDC hDCScr;
  TEXTMETRIC tm;
  TCHAR *buf;
  SIZE lnSize;
  HWND hWinEdit = GetDlgItem(hWnd, IDW_WIN_EDIT);
  int yChar, linesPerPage, line, page, dpiXPrt, dpiYPrt, dpiYScr, bufLen, i, iSOL, iEOL;
  LONG marginLeft, marginRight, marginTop, marginBottom, hSavedlfHeight, xPage, yPage, xPrint,
  yPrint;

  /* Ermittele DPI von Drucker und Bildschirm */
  hDCScr = GetWindowDC(hWnd);
  dpiYScr = GetDeviceCaps(hDCScr, LOGPIXELSY);
  ReleaseDC(hWnd, hDCScr);
  dpiXPrt = GetDeviceCaps(pd->hDC, LOGPIXELSX);
  dpiYPrt = GetDeviceCaps(pd->hDC, LOGPIXELSY);

  /* Rechne Druckraender von Hundertstel mm in Pixel um */
  marginLeft = psd->rtMargin.left * dpiXPrt / 2540; // 2500 / 100 * 600 dpi / 25,4 mmpi =
  marginTop = psd->rtMargin.top *  dpiYPrt / 2540; // 2500 * 600 / 2540 = 590 px
  marginRight = psd->rtMargin.right * dpiXPrt / 2540;
  marginBottom = psd->rtMargin.bottom * dpiYPrt / 2540;

  /* Waehle Druckschrift ueber (passager korrigierten) LOGFONT aus */
  // Da LOGFONT auf (veralteter) Bildschirmetrik von 72 dpi fusst, ist Druckschriftgroesse (300 dpi
  // etc.) zu korrigieren, sonst Ausdruck zu klein. Zudem muss alte LONGFONT-Groesse des Druckfonts
  // gemerkt werden, da sonst beim naechsten Aufruf von menuOptionsFont() der Dialog die korrig.
  // (z.B. 4fache) Groesse als Startwert darstellen und bei jedem weiteren Aufruf vergroessern
  // wuerde. In den meisten Dokus wird ueber CHOOSEFONT.iPointSize der korrigierte Wert ermittelt.
  // Annahme: Benutzer-Punktgroesse von 10 Pt (= 10*1/72 "), somit iPointSize = 10 x PtSize = 100).
  // Dann gilt: lfHeight = - Punktgroesse * Druck-DPI / Urbildschirm-DPI
  // Berechnung lfHeight vom Font 10 Pt fuer Bildschirm-Uraufloesung (72 dpi, MAC):
  // lfHeight = - 10
  // Berechnung von lfHeight vom Font 10 Pt fuer heutige Bildschirm-Standardaufloesung (96 dpi):
  // lfHeight = - 10 * 96 / 72 = -13,333
  // Berechnung lfHeight vom Font 10 Pt fuer Bildschirm-Hochaufloesung (120 dpi):
  // lfHeight = - 10 * 120 / 72 = -16,666
  // Berechnung lfHeight vom Font 10 Pt fuer Druckeraufloesung 600 dpi:
  // lfHeight = - 10 * 600 / 72 = -83,333
  // Da CHOOSEFONT.iPointSize nur in menuOptionsFont() z.V. steht und das Programmende nicht
  // ueberdauert, berechnen wir korrigiertes lfPrt.lfHeight direkt aus unkorrigiertem mit Faktor
  // DruckDPI (z.B. 600 dpi) / BildschirmDPI (meist 96 dpi):
  // lfPrt.lfHeight = lfPrt.lfHeight * dpiYPrt / dpiYScr
  hSavedlfHeight = lfPrt->lfHeight; // Merke alte LOGFONT-Groesse
  lfPrt->lfHeight = MulDiv(lfPrt->lfHeight, dpiYPrt, dpiYScr);  // Passagere LOGFONT-Korrektur
  hPrintFont = CreateFontIndirect(lfPrt); // Erstelle Druckfont
  lfPrt->lfHeight = hSavedlfHeight; // Restauriere alten LOGFONT
  hSavedPrintFont = SelectObject(pd->hDC, (HGDIOBJ)hPrintFont);
  if (hSavedPrintFont == NULL || hSavedPrintFont == (HGDIOBJ)GDI_ERROR)
    MessageBox(hWnd, TEXT("Kann Druckfont nicht laden!"), appTitle, MB_ICONWARNING);

  /* Ermittele Pixel-Metrik der Druckschrift und -seite */
  GetTextMetrics(pd->hDC, &tm); // Ermittle Metrik des aktuell phys. Fonts des ausgew. Druckers hDC
  yChar = tm.tmHeight + tm.tmExternalLeading; // Druckzeilenhoehe = Druckzeichenhoehe + Durchschuss
  xPage = GetDeviceCaps(pd->hDC, HORZRES);
  yPage = GetDeviceCaps(pd->hDC, VERTRES);
  xPrint = xPage - marginLeft - marginRight; // Bedruckbare Breite [px]
  yPrint = yPage - marginTop - marginBottom; // Bedruckbare Hoehe [Rasterlinien]
  linesPerPage = yPrint / yChar;

  /* Lade EDIT-Control in Puffer */
  if ((buf = pd->Flags & PD_SELECTION ? ut_getEditSel(hWinEdit, &bufLen) :
    ut_getEditText(hWinEdit, &bufLen)) == NULL)
    return FALSE; // EM_GETLINE unterscheidet nicht weiche / harte Umbrueche, daher alles kopieren

  /* Benenne Druckjob */
  ZeroMemory(&di, sizeof di);
  di.cbSize = sizeof di;
  if (pd->Flags & PD_PRINTTOFILE)
    di.lpszOutput = TEXT("FILE:");
  di.lpszDocName = *filePath ? filePath : TEXT("Unbenannt");

  /* Drucke */
  if (StartDoc(pd->hDC, &di) > 0) {

    /* Fuer jede Druckseite */
    for (page=i=iSOL=0; i<bufLen; page++) {
      if (StartPage(pd->hDC) <= 0) {
        ok = FALSE; break; }

      /* Fuer jede Druckzeile */
      for (line=0; i<bufLen && line<linesPerPage; line++) {

        /* Ermittele Druckzeilenende iEOL */
        for (lnSize.cx=iEOL=0; i<bufLen && lnSize.cx<xPrint; i++) {
          if (buf[i] == TEXT(' '))
            iEOL = i;
          else if (buf[i] == TEXT('-'))
            iEOL = i + 1;
          else if (buf[i] == TEXT('\r')) {
            iEOL = i; i += 2; break; }
          if (!GetTextExtentPoint32(pd->hDC, buf+iSOL, i+1-iSOL, &lnSize)) // Berechne lnSize.cx
            MessageBox(hWnd, TEXT("Druckmetrik nicht bestimmbar.\nPrüfen Sie den Ausdruck!"),
            appTitle, MB_ICONWARNING);
          };
        if (iEOL == 0) // Bei uebergrossen Zeichenketten wuerde kein iEOL gesetzt, daher
          iEOL = i - 1; // zwangsweise hier
        if (i >= bufLen && buf[iEOL] != TEXT('\r')) // Falls letzte Zeile ohne CRLF, wuerde nur bis
          iEOL = bufLen; // vorletzt. Zeichen (1 Wort auf Zeile) oder bis vor letzt. Blank gedruckt

        /* Drucke Druckzeile */
        TextOut(pd->hDC, marginLeft, marginTop+line*yChar, buf+iSOL, iEOL-iSOL); // Drucke Zeile
        switch (buf[iEOL]) { // Ermittele naechsten Druckzeilenanfang
          case TEXT(' '): iSOL = iEOL + 1; break;
          case TEXT('\r'): iSOL = iEOL + 2; break;
          default: iSOL = iEOL; // Lass iSOL nur Trennzeichen ueberspringen, aber nicht '-' etc.
          }
        } // Fuer jede Druckzeile

      /* Drucke Seitennummer */
      if (printPgNo) {
        TCHAR num[6];
        int numWidth = wsprintf(num, TEXT("%d"), page+1);
        TextOut(pd->hDC, xPage/2, yPage-marginBottom+100, num, numWidth); // Vereinfachte Position
        }
      if (EndPage(pd->hDC) <= 0) {
        ok = FALSE; break; }
      } // Fuer jede Druckseite
    if (ok)
      EndDoc(pd->hDC);
    }

  /* Raeume auf */
  GlobalFree(buf);
  SelectObject(pd->hDC, (HGDIOBJ)hSavedPrintFont);
  DeleteObject(hPrintFont);
  return ok;
  }

/** Function: Menu Datei drucken **/
static void menuFilePrint(HWND hWnd, PAGESETUPDLG *psd, LOGFONT *lf)
  {
  PRINTDLG pd;
  TCHAR saveStatText[200];
  int sos, eos;
  HWND hWinEdit = GetDlgItem(hWnd, IDW_WIN_EDIT);

  /* Praepariere Druckdialog */
  ZeroMemory(&pd, sizeof pd);
  pd.lStructSize = sizeof pd;
  pd.hwndOwner = hWnd;  // Veraltetes PD_PRINTSETUP durch PageSetup ersetzt
  pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_NOPAGENUMS | PD_RETURNDC;
  pd.nCopies = 1;
  SendMessage(hWinEdit, EM_GETSEL, (WPARAM)&sos, (LPARAM)&eos);
  if (eos > sos) // Falls Text markiert ist,
    pd.Flags |= PD_SELECTION; // schlage "Markierung" als Druckbereich im Druckdialog vor

  /* Rufe Druckdialog auf */
  if (!PrintDlg(&pd))
    return;

  /* Drucke */
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_GETTEXT, STINDI, (LPARAM)saveStatText); //  Speichere
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STINDI, (LPARAM)TEXT("Drucke..."));
  SetBkMode(pd.hDC, TRANSPARENT);
  if (!filePrint(hWnd, psd, lf, &pd))
    MessageBox(hWnd, TEXT("Kann Datei nicht drucken!"), appTitle, MB_ICONWARNING);

  /* Raeume auf */
  SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STINDI, (LPARAM)saveStatText);
  if (pd.hDevMode)
    GlobalFree(pd.hDevMode); // Falls beim Aufruf von PrintDlg hDevMode und hDevNames NULL sind,
  if (pd.hDevNames) // alloziert OS Speicher und fuellt Strukturen mit Druckerwerten; daher sollten
    GlobalFree(pd.hDevNames); // wir Speicher wieder freigeben.
  if (pd.hDC)
    DeleteDC(pd.hDC);
  }

/** Function: Rueckfrage zur Dateispeicherung vor Programmende **/
static int fileSaveBeforeExit(HWND hWnd)
  {
  int i = ut_messageBoxExt(hWnd,
    TEXT("Der Text in der Datei '%s' wurde geändert.\nSollen die Änderungen gespeichert werden?"),
    appTitle, MB_ICONQUESTION | MB_YESNOCANCEL, filePath);
  if (i == IDYES)
    menuFileSave(hWnd);
  return i;
  }

#ifdef BUILD_UC

/** Function: Initialisiere EDIT-Farben **/
static void initColors(CHOOSECOLOR *cc, COLORREF *customclrs, HWND hWnd, COLORREF init)
  {
  cc->lStructSize = sizeof(CHOOSECOLOR);
  cc->hwndOwner = hWnd;
  cc->rgbResult = init;
  cc->lpCustColors = customclrs; // Lass Windows die Benutzerfarben merken zwischen Coloraufrufen
  cc->Flags = CC_ANYCOLOR | CC_RGBINIT;
  }

#endif

/** Function: Callback-Funktion des Versionsdialogs **/
static BOOL CALLBACK verDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
  {
  LITEM item;

  switch (msg) {
  case WM_INITDIALOG:
    return TRUE;
  case WM_COMMAND:
    switch (wParam) {
    case IDOK:
      EndDialog(hDlg, TRUE);
      return TRUE;
    case IDCANCEL:
      EndDialog(hDlg, FALSE);
      return TRUE;
    }
  case WM_NOTIFY:
    if (wParam == IDC_VERLINK)
      switch (((LPNMHDR)lParam)->code) {
      // WM_NOTIFY-Meldung SysLink-Element: wParam=IDC_SYSLINK und lParam=NM_CLICK oder NM_RETURN
      case NM_CLICK:
      case NM_RETURN:
        item = ((PNMLINK)lParam)->item;
        // ShellExecute zu Link fkt. nur mit UNICODE gesetzt oder explizit ShellExecuteW().
        // "open" muß ebf. Unicode sein (L""), Resource-Eintrag ist immer in UNICODE
        ShellExecuteW(NULL, L"open", item.szUrl, NULL, NULL, SW_SHOW);
        return TRUE;
      }
  } // switch(msg)
  return FALSE;
  }

/** Function: Callbackfunktion des Hauptfensters **/
static LRESULT CALLBACK winProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  {
  static HWND hWinEdit, hWinStat;
  static TCHAR iniPath[MAX_PATH];
  static HFONT hFont;
  static PAGESETUPDLG psd;
  static LOGFONT lfScr, lfPrt;
  #ifdef BUILD_TB
  static HWND hWinTool;
  #endif
  #ifdef BUILD_UC
  static COLORREF bgClr, bgClr1, bgClr2, fgClr, fgClr1, fgClr2;
  static BOOL nightMode;
  static CHOOSECOLOR choosecolor;
  static LOGBRUSH logbrush;
  static HBRUSH hBrush;
  #ifdef BUILD_RE
  static CHARFORMAT cr;
  #endif
  #endif
  static BOOL showWinTool, showWinStat, saveOptions, makeIniDir;
  static int padding;
  #ifdef BUILD_FR
  static UINT fr_msg;
  #endif
  RECT rcWin; // NEU V 1.2.2, um Fenstergroesse von/nach INI speichern zu koennen
  static TCHAR s[MAX_PATH]; // Generisch fuer mehrere Codeabschnitte in winProc verwendet

  switch (msg) {

  case WM_CREATE:
    {
    #ifdef BUILD_TB
    const TBBUTTON tbb[] = {
      // Bei deklarativer Initialisierung wird undokumentiertes, in commctrl.h definiertes,
      // zusaetzliches Paddingfeld benoetigt, da es ein Array ist; bei expl. Initialisierung
      // der einzelnen Felder zur Laufzeit tritt Problem nicht auf.
      // V1.1 Funktioniert nicht mehr mit LCC V3.8 Build 2011
      // StdBitmapID  CommandID       State            Style           Pad  dwData Beschriftung
      { STD_FILENEW,  IDM_FILE_NEW,   TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_FILEOPEN, IDM_FILE_OPEN,  TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_FILESAVE, IDM_FILE_SAVE,  TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_PRINT,    IDM_FILE_PRINT, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      // { STD_PRINTPRE, IDM_FILE_PRINT, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { 0,            0,              TBSTATE_ENABLED, TBSTYLE_SEP,    {0}, 0L, 0 },
      { STD_UNDO,     IDM_EDIT_UNDO,  TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_CUT,      IDM_EDIT_CUT,   TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_COPY,     IDM_EDIT_COPY,  TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_PASTE,    IDM_EDIT_PASTE, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_DELETE,   IDM_EDIT_CLEAR, TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_FIND,     IDM_EDIT_FIND,  TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      { STD_REPLACE,  IDM_EDIT_REPL,  TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      // { 0,            0,              TBSTATE_ENABLED, TBSTYLE_SEP,    {0}, 0L, 0 },
      // { STD_HELP,     IDM_HELP_VER,   TBSTATE_ENABLED, TBSTYLE_BUTTON, {0}, 0L, 0 },
      };
    TBADDBITMAP tbab;
    #endif
    #ifdef BUILD_UC
    static COLORREF customcolors[16]; // Muss static sein, merkt sich benutzerdef. Farben
    #endif
    const int statwidths[] = {15, 140, -1}; // Li. Statusfeld endet bei 15, mittl. bei 140,
                                            // re. am Fensterende
    /* Lese Ini-Datei aus programPath oder appDataPath */
    lstrcpy(iniPath+GetModuleFileName(NULL,iniPath,MAX_PATH)-3,TEXT("ini"));
    if (!ut_fileExists(iniPath)) { // V1.1 New
      if (SHGetFolderPath(0, CSIDL_APPDATA, 0, 0, iniPath) == S_OK) {
        lstrcat(iniPath, TEXT("\\ed"));
        if (!ut_dirExists(iniPath))
          makeIniDir = TRUE;
        lstrcat(iniPath, TEXT("\\ed.ini"));
        }
      }
    SetRectEmpty(&rcWin); // Setze rcWin auf Default = 0
    GetPrivateProfileStruct(TEXT("Options"), TEXT("Window"), &rcWin, sizeof rcWin, iniPath);
    GetPrivateProfileString(TEXT("Options"), TEXT("ScrFontName"), TEXT("Georgia"),
      lfScr.lfFaceName, 33, iniPath);
    lfScr.lfHeight = GetPrivateProfileInt(TEXT("Options"), TEXT("ScrFontSize"), -20, iniPath);
    lfScr.lfWeight = GetPrivateProfileInt(TEXT("Options"), TEXT("ScrFontWeight"), 0, iniPath);
    GetPrivateProfileString(TEXT("Options"), TEXT("PrtFontName"), TEXT("Georgia"),
      lfPrt.lfFaceName, 33, iniPath);
    lfPrt.lfHeight = GetPrivateProfileInt(TEXT("Options"), TEXT("PrtFontSize"), 0, iniPath);
    lfPrt.lfWeight = GetPrivateProfileInt(TEXT("Options"), TEXT("PrtFontWeight"), 0, iniPath);
    #ifdef BUILD_UC
    GetPrivateProfileString(TEXT("Options"), TEXT("ScrFontColor1"), TEXT(""), s, 7, iniPath);
    fgClr = fgClr1 = s[0] ? ut_revRGBVal(s) : GetSysColor(COLOR_WINDOWTEXT);
    GetPrivateProfileString(TEXT("Options"), TEXT("ScrBgColor1"), TEXT(""), s, 7, iniPath);
    bgClr = bgClr1 = s[0] ? ut_revRGBVal(s) : GetSysColor(COLOR_WINDOW);
    GetPrivateProfileString(TEXT("Options"), TEXT("ScrFontColor2"), TEXT("C3BE98"), s, 7, iniPath);
    fgClr2 = ut_revRGBVal(s);
    GetPrivateProfileString(TEXT("Options"), TEXT("ScrBgColor2"), TEXT("1A0F0B"), s, 7, iniPath);
    bgClr2 = ut_revRGBVal(s);
    #endif
    #ifdef BUILD_TB
    showWinTool = GetPrivateProfileInt(TEXT("Options"), TEXT("ToolBar"), 0, iniPath);
    #endif
    showWinStat = GetPrivateProfileInt(TEXT("Options"), TEXT("StatusBar"), 1, iniPath);
    padding = GetPrivateProfileInt(TEXT("Options"), TEXT("Padding"), 0, iniPath);

    /* Setze Fensterkoordinaten auf vom Benutzer definierte Werte */
    if (rcWin.left)
      MoveWindow(hWnd, rcWin.left, rcWin.top, rcWin.right-rcWin.left, rcWin.bottom-rcWin.top, 0);

    /* Baue Child-Fenster EDIT */
    #ifdef BUILD_RE
    #if BUILD_RE == VER01 // RichEdit 1.0
    if ((richEditLib=LoadLibrary(TEXT("riched32.dll"))) == NULL)
      MessageBox(hWnd, TEXT("Kann riched32.dll nicht laden!"), appTitle, MB_ICONWARNING);
    hWinEdit = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("RICHEDIT"), NULL,
      WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL | ES_NOHIDESEL,
      0, 0, 0, 0, hWnd, (HMENU)IDW_WIN_EDIT, GetModuleHandle(NULL), NULL);
    #else // RichEdit 2.0 und 3.0
    if ((richEditLib=LoadLibrary(TEXT("riched20.dll"))) == NULL)
      MessageBox(hWnd, TEXT("Kann riched20.dll nicht laden!"), appTitle, MB_ICONWARNING);
    hWinEdit = CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, NULL,
      WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL | ES_NOHIDESEL,
      0, 0, 0, 0, hWnd, (HMENU)IDW_WIN_EDIT, GetModuleHandle(NULL), NULL);
    #endif
    SendMessage(hWinEdit,EM_EXLIMITTEXT,(WPARAM)0,(LPARAM)(DWORD)MAXTEXT); // RE: Erhoehe 32K Std
    #else
    hWinEdit = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), NULL, // Init. Text _innerhalb_ EDIT
      WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL | ES_AUTOVSCROLL | ES_NOHIDESEL,
      0, 0, 0, 0, hWnd, (HMENU)IDW_WIN_EDIT, GetModuleHandle(NULL), NULL);
    SendMessage(hWinEdit, EM_SETLIMITTEXT, (WPARAM)MAXTEXT, (LPARAM)0); // EDIT: Erhoehe 64K Std
    #endif
    if (!hWinEdit)
      MessageBox(hWnd, TEXT("Kann Editorfenster nicht laden!"), appTitle, MB_ICONWARNING);

    /* Initialisiere Font */
    lfScr.lfQuality = PROOF_QUALITY;
    if (lfScr.lfFaceName[0] == 0)
      GetObject(GetStockObject(SYSTEM_FONT), sizeof lfScr, (PTSTR)&lfScr); // Falls INI leer,
    hFont = CreateFontIndirect(&lfScr); // nutze Systemfont
    SendMessage(hWinEdit, WM_SETFONT, (WPARAM)hFont, (LPARAM)TRUE);

    /* Initialisiere Farben */
    #ifdef BUILD_UC
    #ifdef BUILD_RE
    ZeroMemory(&cr, sizeof cr);
    cr.cbSize = sizeof cr;
    cr.dwMask = CFM_COLOR;
    cr.crTextColor = fgClr;
    SendMessage(hWinEdit, EM_SETBKGNDCOLOR, (WPARAM)0, (LPARAM)bgClr);
    // Microsoft: SCF_ALL-Nachricht soll nach WM_SETFONT erfolgen:
    SendMessage(hWinEdit, EM_SETCHARFORMAT, (WPARAM)(UINT)SCF_ALL, (LPARAM)(CHARFORMAT*)&cr);
    #endif
    logbrush.lbHatch = 0;
    logbrush.lbStyle = BS_SOLID;
    logbrush.lbColor = bgClr;
    hBrush = CreateBrushIndirect(&logbrush);
    initColors(&choosecolor, customcolors, hWinEdit, fgClr); // Fuellt choosecolor und customcolors
    #endif

    /* Initialisiere teilweise Druckseite */
    // Ein Teil der Druckseiteninitialisierung wird bereits hier statt erst in menuePageSetup()
    // vorgenommen, damit auch ohne vorherigen Aufruf von menuePageSetup() andere Funktionen wie
    // z.B. menuPrint() die Randbreite kennen bzw. eine solche ueberhaupt erst eingestellt ist.
    // Die Variante, PageSetupDlg ohne Dialogbox als Initialisierung von DEVMODE/NAMES aufzurufen
    // wird hier nicht verwendet, um keine evt. Wartezeit auf nicht vorhandene Drucker zu erzeugen.
    ZeroMemory(&psd, sizeof(psd));
    psd.lStructSize = sizeof(psd);
    psd.hwndOwner = hWnd;
    psd.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;
    psd.rtMargin.top = psd.rtMargin.left = psd.rtMargin.right = psd.rtMargin.bottom = 2500; // 25mm

    /* Baue Child-Fenster Toolbar */
    #ifdef BUILD_TB
    hWinTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD, // Kein WS_VISIBLE, da per INI
      // Toolbar beim Start unsichtbar sein kann, Schaltung muss extra erfolgen
      0, 0, 0, 0, hWnd,(HMENU)IDW_WIN_TOOL, GetModuleHandle(NULL), NULL);
    if (!hWinTool)
      MessageBox(hWnd, TEXT("Kann Werkzeugleiste nicht laden!"), appTitle, MB_ICONWARNING);
    SendMessage(hWinTool, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0); // MS Kompatibilitaet
    tbab.hInst = HINST_COMMCTRL;
    tbab.nID = IDB_STD_SMALL_COLOR;  // Alternativ: IDB_STD_LARGE_COLOR
    SendMessage(hWinTool, TB_ADDBITMAP, 0, (LPARAM)&tbab); // Fuege Buttons hinzu
    SendMessage(hWinTool, TB_ADDBUTTONS, sizeof tbb / sizeof tbb[0],(LPARAM)&tbb);
    if (showWinTool)
       ShowWindow(hWinTool, SW_SHOW);
    #endif

    /* Baue Child-Fenster Statusleiste */
    hWinStat = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD | SBARS_SIZEGRIP,
      // Kein WS_VISIBLE, da per INI Statusleiste beim Start unsichtbar sein kann, Schaltung
      0, 0, 0, 0, hWnd, (HMENU)IDW_WIN_STAT, GetModuleHandle(NULL), NULL); // muss extra erfolgen
    if (!hWinStat)
      MessageBox(hWnd, TEXT("Kann Statusleiste nicht laden!"), appTitle, MB_ICONWARNING);
    SendMessage(hWinStat, SB_SETPARTS, sizeof statwidths/sizeof statwidths[0], (LPARAM)statwidths);
    // SendMessage(hWinStat, SB_SETTEXT, 0, (LPARAM)TEXT("Bereit"));
    if (showWinStat)
       ShowWindow(hWinStat, SW_SHOW);

    /* Melde Nachrichten fuer Suchen/Ersetzen-Dialog beim System an */
    #ifdef BUILD_FR
    fr_msg = RegisterWindowMessage(FINDMSGSTRING);
    #endif

    /* Lese Namen zu ladender Texte von Kommandozeile oder von Shell-Drop auf ed.exe ein */
    ut_getArg(GetCommandLine(), NULL); // Achtung: ut_getArg() mit (.., NULL) zu initialisieren!
    ut_getArg(NULL, s); // Ueberspringe "ed.exe"
    ut_getArg(NULL, s); // Lese Dateinamen
    if (*s)
      fileOpen(hWnd, s, 0);

    return 0;
    } // WM_CREATE

  case WM_SIZE:
    {
    RECT rcEdit, rcStat;
    #ifdef BUILD_TB
    RECT rcTool;
    #endif
    int iEditHeight, iStatHeight = 0, iToolHeight = 0;
    #ifdef BUILD_TB
    if (showWinTool) {
      SendMessage(hWinTool, TB_AUTOSIZE, 0, 0); GetWindowRect(hWinTool, &rcTool);
      iToolHeight = rcTool.bottom - rcTool.top;
      }
      #endif
    if (showWinStat) {
      SendMessage(hWinStat, WM_SIZE, 0, 0); GetWindowRect(hWinStat, &rcStat); iStatHeight =
        rcStat.bottom - rcStat.top; }
    GetClientRect(hWnd, &rcEdit); iEditHeight = rcEdit.bottom - iToolHeight - iStatHeight;
    SetWindowPos(hWinEdit, NULL, 0, iToolHeight, rcEdit.right, iEditHeight, SWP_NOZORDER);
    // Wie stellt man das Padding des EDIT-Control ein? An sich klingt SendMessage(hWinEdit,
    // EM_SETMARGINS... gut: die Einstellung wirkt permanent und muss daher nicht in WM_SIZE
    // untergebracht werden; geaendert wird aber nur der li. und re. Rand, nicht der ob. und unt.
    // Setzt man stattdessen EC_USEFONTINFO, werden Raender sogar verkleinert statt vergroessert!
    // Relativer Rand berechnet aus FontInfo:
    // SendMessage(hWinEdit, EM_SETMARGINS, (WPARAM)(EC_USEFONTINFO), 0);
    // Absoluter Rand: fkt. zwar, aber geht nicht auf variable Schriftgroesse ein: SendMessage
    // (hWinEdit, EM_SETMARGINS, (WPARAM)(EC_LEFTMARGIN | EC_RIGHTMARGIN), MAKELPARAM(30,30));
    // Daher Weg ueber EM_SETRECT gegangen, dessen Einstellung aber immer nur bis zum naechsten
    // WM_SIZE haelt; daher muss dieser Ansatz in WM_SIZE selbst untergebracht werden.
    #ifndef BUILD_RE
    // Unter RICHEDIT verkleinert sich Fensterinhalt bei jedem Sizing immer mehr, daher deaktiviert
    if (padding > 0) {
      SendMessage(hWinEdit, EM_GETRECT, 0, (LPARAM)(LPRECT)&rcEdit);
      rcEdit.left+=padding; rcEdit.top+=padding; rcEdit.right-=padding; rcEdit.bottom-=padding;
      SendMessage(hWinEdit, EM_SETRECT, 0, (LPARAM)(LPRECT)&rcEdit);
      }
    #endif
    return 0;
    }

  #ifdef BUILD_EM
  case WM_INITMENUPOPUP:
    switch (lParam) {
    case 0: /* Menue Datei */
      {
      /* Aktiviere Menueeintraege Neu, Speichern, Drucken, falls moeglich */
      // Restriktiver als die meisten Editoren:
      // - Speichern/Drucken nur bei nichtleeren Dateien gestattet
      // - Speichern nur bei readwrite gestattet
      // - Neue Datei nur moeglich, falls nichtleer oder benannte Datei aktuell geladen
      int textLen = GetWindowTextLength(hWinEdit);
      EnableMenuItem((HMENU)wParam, IDM_FILE_NEW, MESTAT(textLen || *filePath));
      EnableMenuItem((HMENU)wParam, IDM_FILE_SAVE, MESTAT(dirty && textLen));
      EnableMenuItem((HMENU)wParam, IDM_FILE_SAVEAS, MESTAT(textLen));
      EnableMenuItem((HMENU)wParam, IDM_FILE_PRINT, MESTAT(textLen));
      return 0;
      }
    case 1: /* Menue Bearbeiten */
      {
      /* Aktiviere Menueeintraege Rueckgaengig und Einfuegen, falls moeglich */
      int sel0, selz, enable; // READONLY verweigert Tippen/Einfuegen, nicht aber REPLACESEL,
      EnableMenuItem((HMENU)wParam, IDM_EDIT_UNDO, // INSDATE, daher hier
        MESTAT(!readOnly && SendMessage(hWinEdit,EM_CANUNDO,0,0L)));
      EnableMenuItem((HMENU)wParam, IDM_EDIT_PASTE,
        MESTAT(!readOnly && IsClipboardFormatAvailable(CF_TEXT)));
      SendMessage(hWinEdit, EM_GETSEL, (WPARAM)&sel0, (LPARAM)&selz);
      /* Aktiviere Menueeintraege Kopieren, Ausschneiden und Loeschen, falls Text ausgewaehlt */
      enable = sel0 != selz;
      EnableMenuItem((HMENU)wParam, IDM_EDIT_CUT, MESTAT(enable && !readOnly));
      EnableMenuItem((HMENU)wParam, IDM_EDIT_COPY, MESTAT(enable));
      EnableMenuItem((HMENU)wParam, IDM_EDIT_CLEAR, MESTAT(enable && !readOnly));
      /* Aktiviere Eintraege Suchen, Ersetzen und Weitersuchen, falls Dialoge nicht schon aktiv */
      #ifdef BUILD_FR
      EnableMenuItem((HMENU)wParam, IDM_EDIT_FIND, MESTAT(fr_hDlg==NULL));
      EnableMenuItem((HMENU)wParam, IDM_EDIT_NEXT, MESTAT(fr_hDlg==NULL));
      EnableMenuItem((HMENU)wParam, IDM_EDIT_REPL, MESTAT(fr_hDlg==NULL && !readOnly));
      #endif
      EnableMenuItem((HMENU)wParam, IDM_EDIT_INSDATE, MESTAT(!readOnly));
      return 0;
      }
    case 2: /* Menue Optionen */
      /* Setze Status bestimmter Menueeintraege */
      EnableMenuItem((HMENU)wParam, IDM_OPTION_FGCOL, MESTAT(!readOnly)); // V1.1 Fix
      EnableMenuItem((HMENU)wParam, IDM_OPTION_BGCOL, MESTAT(!readOnly)); // V1.1 Fix
      CheckMenuItem((HMENU)wParam, IDM_OPTION_PRINTPGNO, printPgNo?MF_CHECKED:MF_UNCHECKED);
      #ifdef BUILD_TB
      CheckMenuItem((HMENU)wParam, IDM_OPTION_SHOWTOOL, showWinTool?MF_CHECKED:MF_UNCHECKED);
      #endif
      CheckMenuItem((HMENU)wParam, IDM_OPTION_SHOWSTAT, showWinStat?MF_CHECKED:MF_UNCHECKED);
      CheckMenuItem((HMENU)wParam, IDM_OPTION_SAVE, saveOptions?MF_CHECKED:MF_UNCHECKED);
      return 0;
    }
  #endif

  #ifdef BUILD_UC
  #ifndef BUILD_RE
  case WM_CTLCOLOREDIT:
    {
    SetTextColor((HDC)wParam, fgClr);
    SetBkColor((HDC)wParam, bgClr);
    return (LRESULT)hBrush;
    }
  #endif
  #endif

  case WM_SETFOCUS:
    SetFocus(hWinEdit);
    #ifdef BUILD_FR
    if (fr_hDlg) // Benutzer von Suchdialog in WIN_EDIT gewechselt; informiere FindRoutinen,
      fr_setFocusLost(); // dass urspr. Selektion invalidisiert wurde
    #endif
    return 0;

  case WM_DROPFILES:
    {
    HDROP hDrop = (HDROP)wParam;
    if (DragQueryFile(hDrop, 0, s, MAX_PATH) > 0)
      if (!dirty || fileSaveBeforeExit(hWnd) != IDCANCEL)
        fileOpen(hWnd, s, 0);
    DragFinish(hDrop);
    return 0;
    }

  case WM_CLOSE:
    if (!dirty || fileSaveBeforeExit(hWnd) != IDCANCEL)
      DestroyWindow(hWnd);
    return 0;

  case WM_QUERYENDSESSION:
    return !dirty || fileSaveBeforeExit(hWnd) != IDCANCEL;

  case WM_DESTROY:
    /* Schreibe Ini-Datei */
    if (saveOptions) {
      if (makeIniDir) { // V1.1 New
        // Gewinne Verzeichnis aus iniPath ohne "\ed.ini"
        lstrcpyn(s,iniPath,lstrlen(iniPath)-sizeof("ed.ini")+1);
        if (!CreateDirectory(s,NULL))
          ut_messageBoxExt(hWnd, TEXT("Kann Verzeichnis '%s' nicht anlegen!"), appTitle,
            MB_ICONWARNING, s);
        }
      if (WritePrivateProfileString(TEXT("Ini"), TEXT("Version"), TEXT("1.0"), iniPath) == FALSE)
        ut_messageBoxExt(hWnd, TEXT("Kann Optionen nicht in '%s' schreiben!"), appTitle,
          MB_ICONWARNING, iniPath);
      else {
        GetWindowRect(hWnd, &rcWin);
        WritePrivateProfileStruct(TEXT("Options"), TEXT("Window"), &rcWin, sizeof rcWin, iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("ScrFontName"), lfScr.lfFaceName, iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("ScrFontSize"),
          ut_uLong2Str(lfScr.lfHeight,10), iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("ScrFontWeight"),
          ut_uLong2Str(lfScr.lfWeight,10), iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("PrtFontName"), lfPrt.lfFaceName, iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("PrtFontSize"),
          ut_uLong2Str(lfPrt.lfHeight,10), iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("PrtFontWeight"),
          ut_uLong2Str(lfPrt.lfWeight,10), iniPath);
        #ifdef BUILD_UC
        WritePrivateProfileString(TEXT("Options"), TEXT("ScrFontColor1"), ut_revRGBStr(fgClr1),
          iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("ScrBgColor1"), ut_revRGBStr(bgClr1),
          iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("ScrFontColor2"), ut_revRGBStr(fgClr2),
          iniPath);
        WritePrivateProfileString(TEXT("Options"), TEXT("ScrBgColor2"), ut_revRGBStr(bgClr2),
          iniPath);
        #endif
        WritePrivateProfileString(TEXT("Options"), TEXT("StatusBar"),
          showWinStat ? TEXT("1") : TEXT("0"), iniPath);
        #ifdef BUILD_TB
        WritePrivateProfileString(TEXT("Options"), TEXT("ToolBar"),
          showWinTool ? TEXT("1") : TEXT("0"), iniPath);
        #endif
        }
      }
    DeleteObject(hFont);
    #ifdef BUILD_UC
    DeleteObject(hBrush);
    #endif
    PostQuitMessage(0);
    return 0;

  #ifdef BUILD_ST
  case IDM_UNHIDE:
    if ((UINT)wParam == IDI_ICON && (UINT)lParam == WM_LBUTTONDBLCLK) {
      st_DelIcon(hWnd, IDI_ICON);
      ShowWindow(hWnd, SW_RESTORE); // Jetzt wieder sichtbar, aber noch nicht aktiv
      SetForegroundWindow(hWnd); // Jetzt aktiv
      }
    return 0;
  #endif

  case WM_COMMAND:
    switch (LOWORD(wParam)) { // EDIT-Control sendet Nachrichten an Elternfenster via WM_COMMAND

    case IDW_WIN_EDIT:
      switch(HIWORD(wParam)) {

      case EN_UPDATE:
        // Bei RichEdit erst ab V.2 automatisch an Parent gesendet; fuer V1 muesste (wie fuer
        // EN_CHANGE) es erst via EM_SETEVENTMASK angemeldet werden. Leider wird schon das initiale
        // Textladen als Aenderung interpretiert.
        if (!dirty) {
          dirty = 1;
          SendDlgItemMessage(hWnd, IDW_WIN_STAT, SB_SETTEXT, STINDI, (LPARAM)TEXT("*"));
          return 0;
          }
        break;

      case EN_ERRSPACE: case EN_MAXTEXT:
        MessageBox(hWnd, TEXT("Maximale Editiergröße überschritten!"), appTitle, MB_ICONWARNING);
        return 0;
      }
      break; // IDW_WIN_EDIT

    case IDM_FILE_NEW:
      if (!dirty || fileSaveBeforeExit(hWnd) != IDCANCEL)
        menuFileNew(hWnd);
      return 0;

    case IDM_FILE_OPEN:
      if (!dirty || fileSaveBeforeExit(hWnd) != IDCANCEL)
        menuFileOpen(hWnd);
      return 0;

    case IDM_FILE_SAVE:
      menuFileSave(hWnd);
      return 0;

    case IDM_FILE_SAVEAS:
      menuFileSaveAs(hWnd);
      return 0;

    case IDM_FILE_PAGESETUP:
      menuFilePageSetup(&psd);
      return 0;

    case IDM_FILE_PRINT:
      menuFilePrint(hWnd, &psd, &lfPrt);
      return 0;

    case IDM_FILE_EXIT: // Wird nur bei Datei|Beenden angesprungen
      PostMessage(hWnd, WM_CLOSE, 0, 0);
      return 0;

    #ifdef BUILD_EM

    case IDM_EDIT_UNDO:
      SendMessage(hWinEdit, WM_UNDO, 0, 0);
      return 0;

    case IDM_EDIT_CUT:
      SendMessage(hWinEdit, WM_CUT, 0, 0);
      return 0;

    case IDM_EDIT_COPY:
      SendMessage(hWinEdit, WM_COPY, 0, 0);
      return 0;

    case IDM_EDIT_PASTE:
      SendMessage(hWinEdit, WM_PASTE, 0, 0);
      return 0;

    case IDM_EDIT_CLEAR:
      SendMessage(hWinEdit, WM_CLEAR, 0, 0);
      return 0;

    #endif

    case IDM_EDIT_SELALL:
      SendMessage(hWinEdit, EM_SETSEL, 0, -1);
      return 0;

    case IDM_EDIT_INSDATE:
      if (GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, NULL, NULL, s, sizeof s/sizeof s[0]))
        SendMessage(hWinEdit, EM_REPLACESEL, TRUE, (LPARAM)s);
      return 0;

    #ifdef BUILD_FR
    case IDM_EDIT_FIND: case IDM_EDIT_REPL:
      fr_hDlg = fr_dlg(hWnd, LOWORD(wParam)==IDM_EDIT_REPL);
      return 0;

    case IDM_EDIT_NEXT: // F3
      if (fr_getFindStr()[0] == TEXT('\0'))
        fr_hDlg = fr_dlg(hWnd, 0);
      else
        if(!fr_find(hWinEdit))
          ut_messageBoxExt(hWnd, TEXT("Kein weiterer Suchtext '%s' gefunden."), appTitle, MB_OK,
            fr_getFindStr());
      return 0;
    #endif

    case IDM_OPTION_FONTPRT:
      if (!menuOptionsFont(hWinEdit, &lfPrt, CF_PRINTERFONTS))
        break;
      return 0;

    case IDM_OPTION_FONTSCR:
      if (!menuOptionsFont(hWinEdit, &lfScr, CF_SCREENFONTS))
      // Befuellt ueber ChooseFont() cf und logFont. Ueber cf.rgbColors koennte aus FontDialog
      // benutzergewaehlte Textfarbe aktiviert werden; der Dialog bietet aber nur 16 Grundfarben,
      // daher nicht genutzt.
        break; // Achtung! Falls i.O., muss Code hier durchlaufen!

    case IDM_OPTION_FONTDEC: case IDM_OPTION_FONTINC:
      {
      HFONT hFontNew;
      if (LOWORD(wParam) == IDM_OPTION_FONTDEC)
        lfScr.lfHeight = (8 * lfScr.lfHeight) / 10;
      else if (LOWORD(wParam) == IDM_OPTION_FONTINC)
        lfScr.lfHeight = (10 * lfScr.lfHeight) / 8;
      hFontNew = CreateFontIndirect(&lfScr);
      SendMessage(hWinEdit, WM_SETFONT, (WPARAM)hFontNew, (LPARAM)TRUE);
      DeleteObject(hFont);
      hFont = hFontNew;
      if (padding)
        SendMessage(hWnd, WM_SIZE, 0, 0);
      return 0;
      }

    case IDM_OPTION_PRINTPGNO:
      printPgNo = !printPgNo;
      return 0;

    #ifdef BUILD_UC

    case IMD_OPTION_COLORMODE:
      nightMode = !nightMode;
      if (nightMode) {
        bgClr = bgClr2; fgClr = fgClr2; }
      else {
        bgClr = bgClr1; fgClr = fgClr1; }
      DeleteObject(hBrush);
      logbrush.lbColor = bgClr;
      hBrush = CreateBrushIndirect(&logbrush);
      #ifdef BUILD_RE
      cr.crTextColor = fgClr;
      SendMessage(hWinEdit, EM_SETCHARFORMAT, (WPARAM)(UINT)SCF_ALL, (LPARAM)(CHARFORMAT*)&cr);
      SendMessage(hWinEdit, EM_SETBKGNDCOLOR, (WPARAM)0, (LPARAM)bgClr);
      #else
      InvalidateRect(hWinEdit, 0, TRUE);
      #endif
      return 0;

    case IDM_OPTION_FGCOL: // Funktioniert nicht ohne Behandlung von WM_CTLCOLOREDIT
      if (ChooseColor(&choosecolor)) {
        fgClr = choosecolor.rgbResult;
        if (nightMode)
          fgClr2 = fgClr;
        else
          fgClr1 = fgClr;
        #ifdef BUILD_RE
        cr.crTextColor = fgClr;
        SendMessage(hWinEdit, EM_SETCHARFORMAT, (WPARAM)(UINT)SCF_ALL, (LPARAM)(CHARFORMAT*)&cr);
        #else
        InvalidateRect(hWinEdit, 0, FALSE);
        #endif
        }
      return 0;

    case IDM_OPTION_BGCOL: // Funktioniert nicht ohne Behandlung von WM_CTLCOLOREDIT
      if (ChooseColor(&choosecolor)) {
        DeleteObject(hBrush);
        bgClr = choosecolor.rgbResult;
        logbrush.lbColor = bgClr;
        hBrush = CreateBrushIndirect(&logbrush);
        if (nightMode)
          bgClr2 = bgClr;
        else
          bgClr1 = bgClr;
        #ifdef BUILD_RE
        SendMessage(hWinEdit, EM_SETBKGNDCOLOR, (WPARAM)0, (LPARAM)bgClr);
        #else
        InvalidateRect(hWinEdit, 0, TRUE);
        #endif
        }
      return 0;

    #endif

    #ifdef BUILD_TB
    case IDM_OPTION_SHOWTOOL:
      showWinTool = !showWinTool;
      ShowWindow(hWinTool, showWinTool ? SW_SHOW : SW_HIDE);
      SendMessage(hWnd, WM_SIZE, 0, 0); // Ruft WM_SIZE auf
      return 0;
    #endif

    case IDM_OPTION_SHOWSTAT:
      showWinStat = !showWinStat;
      ShowWindow(hWinStat, showWinStat ? SW_SHOW : SW_HIDE);
      SendMessage(hWnd, WM_SIZE, 0, 0); // Ruft WM_SIZE auf
      return 0;

    case IDM_OPTION_SAVE:
      saveOptions =! saveOptions;
      return 0;

    case IDM_OPTION_SHOWFULL:
      {
      static BOOL showFull, saveShowWinTool, saveShowWinStat;
      static HMENU hMenu;
      LONG winStyle;
      showFull = !showFull;
      // Sowohl SetMenu() als auch ShowWindow(hWnd) rufen WM_SIZE auf (vorausgesetzt, die Aufrufe
      // erfolgen verzoegert, sonst reduziert Windows die 2 WM_SIZE-Nachrichten auf 1), so dass ein
      // SendMessage(hWnd, WM_SIZE, 0, 0) unnoetig ist
      if (showFull) {
        saveShowWinTool = showWinTool; saveShowWinStat = showWinStat; // Sichere alten Status von
        showWinTool = showWinStat = FALSE; // Status/Toolbar und setz neuen (benoetigt von WM_SIZE)
        #ifdef BUILD_TB
        ShowWindow(hWinTool, SW_HIDE);
        #endif
        ShowWindow(hWinStat, SW_HIDE);
        ShowScrollBar(hWinEdit, SB_VERT, FALSE);
        hMenu = GetMenu(hWnd);
        SetMenu(hWnd, NULL);
        ShowWindow(hWnd, SW_SHOWMAXIMIZED);
        winStyle = GetWindowLong(hWnd, GWL_STYLE) & ~WS_CAPTION;
        SetWindowLong(hWnd, GWL_STYLE, winStyle);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
        }
      else {
        showWinTool = saveShowWinTool; showWinStat = saveShowWinStat;
        #ifdef BUILD_TB
        ShowWindow(hWinTool, showWinTool ? SW_SHOW : SW_HIDE);
        #endif
        ShowWindow(hWinStat, showWinStat ? SW_SHOW : SW_HIDE);
        ShowScrollBar(hWinEdit, SB_VERT, TRUE);
        SetMenu(hWnd, hMenu);
        ShowWindow(hWnd, SW_RESTORE);
        winStyle = GetWindowLong(hWnd, GWL_STYLE) | WS_CAPTION;
        SetWindowLong(hWnd, GWL_STYLE, winStyle);
        SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
        }
      return 0;
      }

    case IDM_HELP_VER:
      DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_VER), hWnd, (DLGPROC)verDlgProc);
      return 0;

    case IDM_HELP_COMMANDS:
      MessageBox(hWnd,
      TEXT("Strg N\tNeue Datei\n")
      TEXT("Strg O\tDatei öffnen\n")
      TEXT("Strg S\tDatei schließen\n")
      TEXT("Strg P\tDatei drucken\n")
      TEXT("Alt F4\tBeenden\n")
      TEXT("\n")
      TEXT("Alt Rück\tRückgängig\n")
      TEXT("Strg Z\tRückgängig\n")
      TEXT("Strg X\tAusschneiden\n")
      TEXT("Sh Entf\tAusschneiden\n")
      TEXT("Strg C\tKopieren\n")
      TEXT("Strg Einf\tKopieren\n")
      TEXT("Strg V\tEinfügen\n")
      TEXT("Sh Einf\tEinfügen\n")
      TEXT("Entf\tLöschen\n")
      TEXT("Rück\tLöschen\n")
      TEXT("Strg F\tSuchen\n")
      TEXT("F3\tWeitersuchen\n")
      TEXT("Strg R\tErsetzen\n")
      TEXT("Strg A\tAlles markieren\n")
      TEXT("Strg D\tDatum einfügen\n")
      TEXT("\n")
      TEXT("F2\tFarbwechsel\n")
      TEXT("F11\tVollbild an/aus\n")
      TEXT("Strg +\tSchrift vergrößern\n")
      TEXT("Strg -\tSchrift verkleinern\n")
      TEXT("Esc\tFlüchten\n")
      TEXT("F1\tHilfe"),
      TEXT("Tastenkürzel"), MB_OK);
      return 0;

    #ifdef BUILD_ST
    case IDM_HIDE:
      if (fr_hDlg) { // Offener, aber inaktiver Suchdialog? Dann schliesse ihn.
        DestroyWindow(fr_hDlg);
        fr_hDlg = NULL;
        return 0;
        }
      st_AddIcon(hWnd, IDI_ICON, LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON)),
        appTitle);
      ShowWindow(hWnd, SW_HIDE);
      return 0;
    #endif

    } // LOWORD(wParam)
    break; // WM_COMMAND

  #ifdef BUILD_FR
  default: // Hier treffen durch fr_dlg() erzeugte Nachrichten fuer fr_find*() ein;
    if (msg == fr_msg) { // lParam haelt FR struct vor, hier nicht mehr benutzt
      int frCode = fr_dispatch(hWinEdit);
      switch(frCode) {
        case -3: fr_hDlg = NULL; break;
        case -2: MessageBox(hWnd, TEXT("Kein weiterer Suchtext gefunden."), appTitle,MB_OK); break;
        case -1: break;
        default: ut_messageBoxExt(hWnd, TEXT("%d Ersetzungen."), appTitle, MB_OK, frCode);
        }
      return 0;
      }
    break;
  #endif

  } // msg

  return DefWindowProc(hWnd, msg, wParam, lParam);
  }

/** Function: Main **/
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLn, int nCmdShow)
  {
  HWND hWinMain;
  WNDCLASSEX classMain;
  INITCOMMONCONTROLSEX iccx;
  MSG msg;
  #ifdef BUILD_AC
  HACCEL hAccel;
  #endif

  iccx.dwSize = sizeof(INITCOMMONCONTROLSEX);
  iccx.dwICC = ICC_LINK_CLASS;
  if (!InitCommonControlsEx(&iccx))
    MessageBox(NULL, TEXT("Aufruf COMCTL32.DLL V6 gescheitert!"), appTitle, MB_ICONINFORMATION);

  /* Erstelle Hauptfensterklasse und verbinde sie mit Callback-Funktion von Hauptfenster */
  ZeroMemory(&classMain, sizeof classMain);
  classMain.cbSize = sizeof classMain;
  classMain.lpfnWndProc = winProc;
  classMain.hInstance = hInst;
  classMain.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON));
  classMain.hCursor = LoadCursor(NULL, IDC_ARROW);
  // classMain.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); hWinEdit/hWinTool/hWinStat zeichnen selbst
  classMain.lpszMenuName = MAKEINTRESOURCE(IDM_MENU);
  classMain.lpszClassName = appTitle;
  classMain.hIconSm =
    (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
  if (!RegisterClassEx(&classMain)) {
    #ifdef UNICODE
    MessageBox(NULL, TEXT("Unicode-Programm, Windows NT benötigt!"), appTitle, MB_ICONSTOP);
    #else
    MessageBox(NULL, TEXT("Kann Fensterklasse nicht registrieren!"), appTitle, MB_ICONSTOP);
    #endif
    return 1;
    }

  /* Erstelle Hauptfenster */
  hWinMain = CreateWindowEx(WS_EX_ACCEPTFILES, appTitle, appTitle,
    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    CW_USEDEFAULT, NULL, NULL, hInst, NULL);
  if (hWinMain == NULL) {
    MessageBox(NULL, TEXT("Kann Hauptfenster nicht laden!"), appTitle, MB_ICONSTOP);
    return 1;
    }
  ShowWindow(hWinMain, nCmdShow);
  UpdateWindow(hWinMain);

  #ifdef BUILD_AC
  if ((hAccel=LoadAccelerators(hInst, MAKEINTRESOURCE(IDA_ACCEL))) == NULL)
    MessageBox(NULL, TEXT("Keine Accelerators. Resourcen gebunden?"), appTitle, MB_ICONWARNING);
  #endif

  /* Empfange Nachrichten der Dispatcher-Schleife */
  while (GetMessage(&msg, NULL, 0, 0) > 0)
    #ifdef BUILD_FR
    if (fr_hDlg == NULL || !IsDialogMessage(fr_hDlg, &msg)) // Somit Tab und ENTER durch Dialog
    #endif // statt durch winProc behandelt
      #ifdef BUILD_AC
      if (!TranslateAccelerator(hWinMain, hAccel, &msg))
      #endif
        {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        }

  #ifdef BUILD_RE
  if (richEditLib)
    FreeLibrary(richEditLib); // Stuerzte in WM_DESTROY immer ab; am Main-Ende aber problemlos
  #endif

  return (int)msg.wParam;
  }