Index: win32wsstdctrls.pp
===================================================================
--- win32wsstdctrls.pp	(revision 57415)
+++ win32wsstdctrls.pp	(working copy)
@@ -428,6 +428,305 @@
   Result := WindowProc(Window, Msg, WParam, LParam);
 end;
 
+{-------------------------------------------------------------------------------
+This patch solves to ComboBox control issues:
+- The MS Windows ComboBox control doesn't support AutoSelect=false: It always
+  selects the whole text at activation.
+
+  With this patch the ComboBox (Style=csDropDown only) behaves like a
+  MS Windows Edit control at activation.
+  Mouse activation: The selection follows the mouse cursor as long as the left
+  button is pressed.
+  Key activation: The last selection is restored.
+
+- The MS Windows ComboBox (Style=csDropDown only) control replaces the
+  ComboBox.Text with the first matching item of the items list at any change of
+  the ComboBox font if ComboBox.Text is a prefix of one of the strings in the
+  item list.
+
+  This patch perserves the text and selection.
+
+
+  Relevant message flow during activation:
+
+  WM_LBUTTONDOWN (Mouse activation only)
+    Set saved selection to (FMouseSelect,FMouseSelect)
+
+  WM_SETFOCUS
+    Set FActivation for the next EM_SETSEL action.
+  EM_SETSEL
+    Restore the saved selection.
+    Release FActivation.
+
+  WM_MOUSEMOVE (Mouse activation only)
+    Set selection to (FMouseSelect,mouse position).
+
+  WM_KILLFOCUS
+    Save selection.
+    Unselect everything to hide the selection.
+
+  Relevant message flow during font changes:
+  WM_SETFONT
+    Save selection.
+    Set FFontChange for the next WM_SETTEXT and WM_SETSEL actions.
+  WM_SETTEXT
+    Suppress this action.
+  EM_SETSEL
+    Suppress this action.
+    Release FFontChange.
+    Restore selection.
+-------------------------------------------------------------------------------}
+
+type
+  ERWxComboBox = class(Exception);
+  TASCFPatchData = record
+    FItemHandle:         THandle;      // Handle of the edit control part of the ComboBox
+    FComboBox:           TCustomComboBox;
+    // AutoSelect=false patch
+    FActivation:         boolean;      // Set during activation
+    FAutoSelectSelStart: integer;      // Save selection on WM_KILLFOCUS
+    FAutoSelectSelEnd:   integer;      // Save selection on WM_KILLFOCUS
+    FMouseSelect:        integer;      // -1 or index of the character at mouse position during activation
+    // Font change patch
+    FFontChange:         boolean;      // Set during font change
+  end;
+  PASCFPatchData = ^TASCFPatchData;
+
+function ComboBoxItemSubClass (hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM;
+                       {%H-}uISubClass: UINT_PTR; dwRefData: DWORD_PTR):LRESULT; stdcall;
+var
+  PPD:   PASCFPatchData;
+  ss,se: DWORD;
+  pt:    TPoint;
+begin
+  PPD:={%H-}PASCFPatchData(dwRefData);
+
+  if (dwRefData=0) or
+     (PPD^.FItemHandle=INVALID_HANDLE_VALUE) then
+  begin
+    Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+    exit;
+  end;
+
+  if hWnd<>PPD^.FItemHandle then
+  begin
+    Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+    exit;
+  end;
+
+  case uMsg of
+    WM_LBUTTONDOWN:
+    // Patch AutoSelect=false
+    begin
+      if PPD^.FComboBox.AutoSelect then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      PPD^.FMouseSelect:=-1;
+      if GetFocus=hWnd then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      // Set selection acording to mouse position.
+      // Save cursor position to FMouseSelect.
+      if not GetCursorPos(pt) then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      if not Windows.ScreenToClient(hWnd,pt) then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      ss:=LOWORD(SendMessage(hWnd,EM_CHARFROMPOS,0,MAKELPARAM(pt.x,pt.y)));
+      if ss>=$FFFF then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      PPD^.FMouseSelect:=ss;
+      // Change saved selection for use with EM_SETSEL later
+      PPD^.FAutoSelectSelStart:=ss;
+      PPD^.FAutoSelectSelEnd:=ss;
+      Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+      exit;
+    end;
+
+    WM_SETFOCUS:
+    // Patch AutoSelect=false
+    begin
+      if PPD^.FComboBox.AutoSelect then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      // Supress ComboBox control selection setting
+      PPD^.FActivation:=true;
+      Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+      exit;
+    end;
+
+    WM_MOUSEMOVE:
+    // Patch AutoSelect=false
+    // Set current selection acording to mouse position.
+    begin
+      if PPD^.FComboBox.AutoSelect then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+      if ((wParam and MK_LBUTTON)=0) or
+         (PPD^.FMouseSelect<0) then
+        exit;
+      pt.x:=LOWORD(lParam);
+      pt.y:=HIWORD(lParam);
+      se:=LOWORD(SendMessage(hWnd,EM_CHARFROMPOS,0,MAKELPARAM(pt.x,pt.y)));
+      if se>=$FFFF then
+        exit;
+      // Set selection to mouse position. Override the patch.
+      if PPD^.FMouseSelect<>se then
+        DefSubClassProc(hWnd,EM_SETSEL,PPD^.FMouseSelect,se);
+      exit;
+    end;
+
+    WM_KILLFOCUS:
+    // Patch AutoSelect=false
+    begin
+      if PPD^.FComboBox.AutoSelect then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      // Get and save the current selection.
+      // Ensure to get the "real" (unpatched) values.
+      // Unselect all to hide the selection.
+      DefSubClassProc(hWnd,EM_GETSEL,{%H-}Windows.WPARAM(@ss),{%H-}Windows.LPARAM(@se));
+      Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+      DefSubClassProc(hWnd,EM_SETSEL,-1,0);
+      PPD^.FAutoSelectSelStart:=ss;
+      PPD^.FAutoSelectSelEnd:=se;
+      exit;
+    end;
+
+    WM_SETFONT:
+    // Patch change font
+    begin
+      // Get the "real" (unpatched) current selection.
+      DefSubClassProc(hWnd,EM_GETSEL,{%H-}Windows.WPARAM(@ss),{%H-}Windows.LPARAM(@se));
+      // Supress ComboBox control WM_SETTEXT
+      PPD^.FFontChange:=true;
+      Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+      exit;
+    end;
+
+    WM_SETTEXT:
+    // Patch change font
+    // Skip if FSuppress_WM_SETTEXT
+    begin
+      if PPD^.FFontChange then
+        Result:=1
+      else
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+      exit;
+    end;
+
+    EM_GETSEL:
+    // Patch AutoSelect=false
+    begin
+      if PPD^.FComboBox.AutoSelect then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      if GetFocus=hWnd then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      // No focus: use the saved values
+      if (PPD^.FAutoSelectSelStart>=0) and
+         (PPD^.FAutoSelectSelEnd>=0) then
+      begin
+        Result:=1;
+        if wParam<>0 then
+          PDWORD(wParam)^:=PPD^.FAutoSelectSelStart;
+        if lParam<>0 then
+          PDWORD(lParam)^:=PPD^.FAutoSelectSelEnd;
+        exit;
+      end;
+      Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+      exit;
+    end;
+
+    EM_SETSEL:
+    begin
+      // Patch change font
+      // Don't change the cursor position
+      if PPD^.FFontChange then
+      begin
+        // Get current selection.
+        SendMessage(hWnd,EM_GETSEL,{%H-}Windows.WPARAM(@ss),{%H-}Windows.LPARAM(@se));
+        PPD^.FFontChange:=false;
+        // Skip this selection and restore the saved one. Don't use SendMessage or DefSubClassProc.
+        // Don't restore if not focused.
+        if GetFocus=HWnd then
+        begin
+          PostMessage(hWnd,EM_SETSEL,ss,se);
+        end;
+        Result:=1;
+        exit;
+      end;
+
+      // Patch AutoSelect=false
+      if PPD^.FComboBox.AutoSelect then
+      begin
+        Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+        exit;
+      end;
+      if PPD^.FActivation then
+      begin
+        if (PPD^.FAutoSelectSelStart<0) or
+           (PPD^.FAutoSelectSelEnd<0) then
+        begin
+          PPD^.FAutoSelectSelStart:=0;
+          PPD^.FAutoSelectSelEnd:=0;
+        end;
+        // Restore the saved selection
+        Result:=DefSubClassProc(hWnd,uMsg,PPD^.FAutoSelectSelStart,PPD^.FAutoSelectSelEnd);
+        PPD^.FActivation:=false;
+        PPD^.FAutoSelectSelStart:=-1;
+        PPD^.FAutoSelectSelEnd:=-1;
+        exit;
+      end;
+      Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+      if GetFocus<>hWnd then
+      begin                            // Not focused: Update saved selection
+        PPD^.FAutoSelectSelStart:=wParam;
+        PPD^.FAutoSelectSelEnd:=lParam;
+      end;
+    end;
+  end;   { case uMsg of }
+  Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+end;
+
+function ComboBoxSubclass (hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM;
+                           {%H-}uISubClass: UINT_PTR; dwRefData: DWORD_PTR):LRESULT; stdcall;
+begin
+  Result:=DefSubClassProc(hWnd,uMsg,wParam,lParam);
+  if uMsg=WM_NCDESTROY then
+  begin
+    if dwRefData<>0 then
+      Dispose({%H-}PASCFPatchData(dwRefData));
+    exit;
+  end;
+end;
+{--End of patch ---------------------------------------------------------------}
+
+
 function ScrollBarWindowProc(Window: HWnd; Msg: UInt; WParam: Windows.WParam;
     LParam: Windows.LParam): LResult; stdcall;
 var
@@ -947,6 +1246,7 @@
 var
   Params: TCreateWindowExParams;
   Info: TComboboxInfo;
+  PPD: PASCFPatchData;
 begin
   // general initialization of Params
   PrepareCreateWindow(AWinControl, AParams, Params);
@@ -979,6 +1279,26 @@
       WindowCreateInitBuddy(AWinControl, Params);
       BuddyWindowInfo^.isChildEdit := true;
       BuddyWindowInfo^.isComboEdit := true;
+
+      if not (csDesigning in AWinControl.ComponentState) then
+      begin
+        New(PPD);
+        PPD^.FItemHandle:=INVALID_HANDLE_VALUE;
+        PPD^.FComboBox:=TCustomComboBox(AWinControl);
+        PPD^.FActivation:=false;
+        PPD^.FAutoSelectSelStart:=-1;
+        PPD^.FAutoSelectSelEnd:=-1;
+        PPD^.FMouseSelect:=-1;
+        PPD^.FFontChange:=false;
+        PPD^.FItemHandle:=Info.hwndItem;
+        // Subclassing the ComboBox item control (Info.hwndItem).
+        // Do it always regardless of AutoSelect setting. AutoSelect may change at runtime.
+        if not SetWindowSubclass(Info.hwndItem,@ComboBoxItemSubClass,0,{%H-}DWORD_PTR(PPD)) then
+          raise ERWxComboBox.CreateFmt('SetWindowSubclass (item): %s',[SysErrorMessage(GetLastError)]);
+        // Subclassing the ComboBox control.
+        if not SetWindowSubclass(AWincontrol.Handle,@ComboBoxSubClass,0,{%H-}DWORD_PTR(PPD)) then
+          raise ERWxComboBox.CreateFmt('SetWindowSubclass: %s',[SysErrorMessage(GetLastError)]);
+      end;
     end
     else
       BuddyWindowInfo:=nil;
