uses WinTypes, WinProcs, Win31, LEdit;
{$I LEDIT__S.INC}

const
  ID_CPP         = 101;
  ID_PAS         = 102;
  CPP_SYNTAX     = 27;
  PAS_SYNTAX     = 28;
  CPP_WORDS      = 14;
  PAS_WORDS      = 21;
  PAS_LEFT_BRACE = 'begin';
  PAS_RIGHT_BRACE= 'end';
  { Actually "end" has multiple braces such as "case" or
    "record", but multiple braces aren't supported now
    Pascal source is written without such words and therefore
    Find Brace will work properly on it                       }

var
  Msg: TMsg;
  MsgBuf: array[0..2047] of char;
  Wnd,
  WndPAS,                   { Pascal source window }
  WndCPP,                   { C++ source window    }
  Focus: hWnd;
  wc: TWndClass;

const
  CPPSyntax: array[0..CPP_SYNTAX-1] of word = (
    { Small subset of C++ syntax }
    (Ord('\') SHL 8) OR Ord('"'),  { Quoting character            }
    (Ord('\') SHL 8) OR Ord(''''), { Alternative quoting character}
    (Ord('/') SHL 8) OR Ord('/'),  { Comment sign                 }
    (Ord('*') SHL 8) OR Ord('/'),  { Left comment sign            }
    (Ord('/') SHL 8) OR Ord('*'),  { Right comment sign           }
    Ord('('),Ord(')'),Ord('['),Ord(']'),Ord('{'),Ord('}'),Ord('+'),Ord('-'),
    Ord('*'),Ord('/'),Ord('\'),Ord(','),Ord(';'),Ord('='),Ord(':'),Ord('%'),
    Ord('|'),Ord('!'),Ord('&'),Ord('|')*$101,Ord('&')*$101,Ord('=')*$101
  );

  PASSyntax: array[0..PAS_SYNTAX-1] of word = (
    { Small subset of Pascal syntax }
    (Ord('''') SHL 8) OR Ord(''''),{ Quoting character            }
    $101,                          { Alternative quoting character}
    $101,                          { Comment sign                 }
    Ord('{'),                      { Left comment sign            }
    Ord('}'),                      { Right comment sign           }
    (Ord('*') SHL 8) OR Ord('('),  { Left comment sign            }
    (Ord(')') SHL 8) OR Ord('*'),  { Right comment sign           }
    Ord('^'),Ord(')'),Ord('['),Ord(']'),Ord('+'),Ord('-'),Ord('*'),Ord('/'),
    Ord('\'),Ord(','),Ord(';'),Ord('='),Ord(':'),(Ord('=') SHL 8) OR Ord(':'),
    (Ord('>') SHL 8) OR Ord('<'),(Ord('=') SHL 8) OR Ord('>'),
    (Ord('=') SHL 8) OR Ord('<'),Ord('@'),Ord('#'),
    (Ord(' ') SHL 8) OR Ord('.'),Ord('.')*$101
  );

  CPPWords: array[0..CPP_WORDS-1] of PChar = (
    { Small subset of C++ keywords }
    'switch','case','default','break','return','if','else','while','for',
    '#define','#include','#ifdef','#else','#endif');

  PASWords: array[0..PAS_WORDS-1] of PChar = (
    { Small subset of Pascal keywords }
    'begin','end','case','break','exit','if','then','while','for','do',
    'uses','type','const','var','function',
    'or','and','div','mod','shl','shr');


{ Main window function }
function FrameProc (Wnd:    hWnd;
		    Msg,
		    wParam: word;
		    lParam: longint): longint;
export;

var
  i, Part1, Part2: integer;
  SyntaxIndex: word;

begin
  if Msg = WM_CREATE then
    begin
      { Create two LEdit children }
      WndCPP := CreateWindow('LEdit',
			     'LEdit-File-Init-ledit_4.cpp',
			     WS_CHILD OR WS_VISIBLE OR ES_BIGINDENT
			     OR ES_SMALLSPACING OR ES_HIGHLIGHT
			     OR WS_VSCROLL OR WS_BORDER,
			     0,0,0,0,
			     Wnd, ID_CPP, hInstance, nil);

      Behaviour(WndCPP);

      WndPAS := CreateWindow('LEdit',
			     'LEdit-File-Init-ledit_4.pas',
			     WS_CHILD OR WS_VISIBLE OR ES_BIGINDENT
			     OR ES_SMALLSPACING OR ES_HIGHLIGHT
			     OR WS_VSCROLL OR WS_BORDER,
			     0,0,0,0,
			     Wnd, ID_PAS, hInstance, nil);

      Behaviour(WndPAS);

      { Choose first focused window }
      Focus := WndCPP;

      { Change a little LEdit behaviour }
      SendMessage(WndCPP,EM_SETFONT,GetStockObject(DEVICE_DEFAULT_FONT),0);
      SendMessage(WndPAS,EM_SETFONT,GetStockObject(DEVICE_DEFAULT_FONT),0);

      { Allow multiline highlight }
      SendMessage(WndCPP,EM_SETRUNTIMEFLAGS,EMP_MULTILINEITEMS,0);
      SendMessage(WndPAS,EM_SETRUNTIMEFLAGS,EMP_MULTILINEITEMS,0);

      { Set appropriate syntaxes }
      SendMessage(WndCPP,EM_SETSYNTAX,CPP_SYNTAX,longint(@CPPSyntax));
      SendMessage(WndPAS,EM_SETSYNTAX,PAS_SYNTAX + 256 * EMP_EXTRACOMMENTS,longint(@PASSyntax));
      exit;
    end
  else if Msg = WM_SIZE then
    begin
      { Move LEdit windows }
      Part1 := HIWORD(lParam) DIV 2;
      Part2 := Part1 + HIWORD(lParam) MOD 2;
      MoveWindow(WndCPP,0,0,LOWORD(lParam),Part1,FALSE);
      MoveWindow(WndPAS,0,Part1,LOWORD(lParam),Part2,FALSE);
      exit;
    end
  else if Msg = WM_CTLCOLOR then
    begin
      { Set default colors. Actually these are comment colors }
      SetBkColor(wParam,RGB(192,192,192));
      SetTextColor(wParam,RGB(128,128,128));
      exit;
    end
  else if Msg = EM_CTLCOLOREX then
    begin
      if PWordDesc(lParam)^.wCode = WD_SIMPLEWORD then
	begin
          { Set default text color for words }
	  SetTextColor(wParam,RGB(0,0,0));

          { Mark C++ keywords if this is CPP window }
	  if PWordDesc(lParam)^.hwndSender = WndCPP then
	    for i := 0 to CPP_WORDS -1 do
	      begin
	        if lstrcmp(PWordDesc(lParam)^.caWord,CPPWords[i]) = 0 then
	          begin
	            SetTextColor(wParam,RGB(0,0,255));
                    exit;
                  end;
	      end

          { Mark Pascal keyword if this is PAS window }
	  else
	    for i := 0 to PAS_WORDS -1 do
	      begin
	        if lstrcmpi(PWordDesc(lParam)^.caWord,PASWords[i]) = 0 then
	          begin
	            SetTextColor(wParam,RGB(0,0,255));
                    exit;
                  end;
	      end;
        end
      else if PWordDesc(lParam)^.wCode = WD_SYNTAXITEM then
	begin
          { Set text color for syntax words }
	  SetTextColor(wParam,RGB(0,0,192));
          exit;
	end
      else if (PWordDesc(lParam)^.wCode = WD_BADLYQUOTEDTEXT)
        OR (PWordDesc(lParam)^.wCode = WD_QUOTEDTEXT) then
        begin
          { Set text color for quoted text }
          if HIWORD(PWordDesc(lParam)^.dwValue) = 1 then
	    SetTextColor(wParam,RGB(0,128,0))
	  else
            SetTextColor(wParam,RGB(255,0,0));
          exit;
	end
      else
        { Set text color for the rest of words, that is }
        { for numbers                                   }
          SetTextColor(wParam,RGB(0,128,0));
      exit;
    end
  else if Msg = EM_WORDCLICK then
    begin
      if lParam <> 0 then
        begin
          { Show message about word supplied if it exists }
	  lstrcpy(MsgBuf,PWordDesc(lParam)^.caWord);
	  lstrcat(MsgBuf,#13#10);
	  if PWordDesc(lParam)^.wCode = WD_SYNTAXITEM then
	    begin
              SyntaxIndex := HIWORD(PWordDesc(lParam)^.dwValue);
	      wvsprintf(MsgBuf+lstrlen(MsgBuf),'Syntax: %d',
		SyntaxIndex);
            end
	  else if (PWordDesc(lParam)^.wCode AND WDF_INTEGER) > 0 then
	    wvsprintf(MsgBuf+lstrlen(MsgBuf),'Integer: %ld',
	      PWordDesc(lParam)^.dwValue);
	  MessageBox(Wnd,MsgBuf,'Click',MB_OK OR MB_ICONINFORMATION);
        end
      else
        { Show "White space" message }
        MessageBox(Wnd,'White space','Click',MB_OK OR MB_ICONINFORMATION);
      FrameProc := 1;
      exit;
    end
  else if Msg = EM_FINDBRACE then
    begin
      if PWordDesc(lParam)^.wCode = WD_SYNTAXITEM then
	begin
          { Indicate braces for parantheises, brackets etc. }
	  SyntaxIndex := HIWORD(PWordDesc(lParam)^.dwValue);
          if SyntaxIndex > 10 then
            begin
              FrameProc := EMF_NOBRACE;
              exit;
            end;
	  if (SyntaxIndex MOD 2) = 1 then
            begin
	      inc(SyntaxIndex);
	      FrameProc := EMF_HASRIGHT;
            end
	  else
            begin
	      dec(SyntaxIndex);
	      FrameProc := EMF_HASLEFT;
            end;
	  PWordDesc(lParam)^.dwValue := MAKELONG(0,SyntaxIndex);
          exit;
        end
      else
	begin
          { If this is CPP window then no more braces }
          if wParam = WndCPP then
            begin
	      FrameProc := EMF_NOBRACE;
              exit;
            end;

          { If this is PAS window then look for "begin" }
	  if lstrcmpi(PWordDesc(lParam)^.caWord,PAS_LEFT_BRACE) = 0 then
            begin
	      lstrcpy(PWordDesc(lParam)^.caWord,PAS_RIGHT_BRACE);
              FrameProc := EMF_HASRIGHT OR EMF_CASEINSENSITIVE;
              exit;
	    end;

          { Look also for "end" }
          if lstrcmpi(PWordDesc(lParam)^.caWord,PAS_RIGHT_BRACE) = 0 then
            begin
	      lstrcpy(PWordDesc(lParam)^.caWord,PAS_LEFT_BRACE);
              FrameProc := EMF_HASLEFT OR EMF_CASEINSENSITIVE;
              exit;
	    end;
        end;
    end
  else if Msg = EM_PRINTJOBNAME then
    begin
      FrameProc := longint(pchar('Print Job Name'));
      exit;
    end
  else if Msg = WM_COMMAND then
    begin
      { Only LEdit notifications }
      if HIWORD(lParam) = EN_SETFOCUS then
        { Store current focus }
	Focus := LOWORD(lParam);
      FrameProc := 0;
      exit;
    end
  else if Msg = WM_SETFOCUS then
    begin
      { Set current focus }
      SetFocus(Focus);
      exit;
    end
  else if Msg = WM_DESTROY then
    { Stop the application }
    PostQuitMessage(0);
  { Call DefWindowProc() for the rest of messages }
  FrameProc := DefWindowProc(Wnd,Msg,wParam,lParam);
end;

{ Main block }
begin
  { Don't allow multiple instances }
  if hPrevInst <> 0 then
    exit;

  { Register window class }
  wc.style         := CS_HREDRAW OR CS_VREDRAW;
  wc.lpfnWndProc   := @FrameProc;
  wc.cbClsExtra    := 0;
  wc.cbWndExtra    := 0;
  wc.hInstance     := hInstance;
  wc.hIcon         := LoadIcon(0,IDI_APPLICATION);
  wc.hCursor       := LoadCursor(0,IDC_ARROW);
  wc.hbrBackground := HBrush(COLOR_APPWORKSPACE + 1);
  wc.lpszMenuName  := nil;
  wc.lpszClassName := 'LEditFrame';
  RegisterClass(wc);

  { Create main window }
  Wnd := CreateWindow('LEditFrame',
		      'C++ vs Pascal',
		      WS_OVERLAPPEDWINDOW,
		      0, 0,
		      GetSystemMetrics(SM_CXSCREEN),
		      GetSystemMetrics(SM_CYSCREEN),
		      0, 0, hInstance, nil);
  if Wnd <> 0 then
    begin
      { Show window }
      ShowWindow(Wnd,SW_SHOW);

      { Run message loop }
      while GetMessage(Msg,0,0,0) do
        begin
          TranslateMessage(Msg);
          DispatchMessage(Msg);
        end;
    end;
end.