spiegel-keyman/windows/src/engine/keyman/langswitch/UfrmLanguageSwitch.pas
Marc Durdin e34478c173 feat(windows): scrollable keyboard menu
Fixes #1843.

If there are so many keyboards installed that the menu would grow larger
than 3/4 of the screen height, then the menu will become scrollable.
2020-09-28 12:17:00 +10:00

1176 lines
40 KiB
ObjectPascal

(*
Name: UfrmLanguageSwitch
Copyright: Copyright (C) 2003-2017 SIL International.
Documentation:
Description:
Create Date: 22 Oct 2010
Modified Date: 10 Jun 2014
Authors: mcdurdin
Related Files:
Dependencies:
Bugs:
Todo:
Notes:
History: 22 Oct 2010 - mcdurdin - I2522 - Initial version of language switch window
30 Nov 2010 - mcdurdin - I2544 - Restyle language switch window as per Paul's design
10 Dec 2010 - mcdurdin - I2555 - Transparency issues
10 Dec 2010 - mcdurdin - I2554 - Potential for crash when window closes
17 Dec 2010 - mcdurdin - I2593 - Transparent Keyman keyboard icons
17 Dec 2010 - mcdurdin - I2592 - Stop window disappearing as soon as it opens
31 Jan 2011 - mcdurdin - I2639 - Fix memory leak in language switch window
31 Jan 2011 - mcdurdin - I2677 - Lang Switch window shows old selection when popping up, disconcerting
31 Jan 2011 - mcdurdin - I2674 - Show the Selection with a little triangle on the left
28 Feb 2011 - mcdurdin - I2669 - If too many keyboards are associated with one language, the language switch window can become unusable
03 May 2011 - mcdurdin - I2867 - When no keyboards listed for a language, language switch window can crash
19 Aug 2011 - mcdurdin - I3023 - Boxes in the language switch window can be wrong size
03 Oct 2011 - mcdurdin - I3087 - Crash opening language switch window when only 1 language detected
18 May 2012 - mcdurdin - I3306 - V9.0 - Remove TntControls + Win9x support
24 Jan 2012 - mcdurdin - I3213 - Handle access denied error when lang switch window visible and Ctrl+Alt+Del pressed
03 Nov 2012 - mcdurdin - I3517 - V9.0 - Merge of I3213 - Crash when Keyman menu is visible and Ctrl+Alt+Del or Win+L pressed
24 Oct 2013 - mcdurdin - I3933 - V9.0 - Keyman tray icon menu is not showing installed keyboards
06 Mar 2014 - mcdurdin - I4124 - V9.0 - Language switch dialog is not working in v9
24 Apr 2014 - mcdurdin - I4191 - V9.0 - Lang switch window shows wrong selection with Alt+LeftShift when TIP is active
24 Apr 2014 - mcdurdin - I4190 - V9.0 - Lang switch window shifts on first view
10 Jun 2014 - mcdurdin - I4204 - V9.0 - Icons do not show background correctly in lang switch window and Win 8 languages controls
*)
unit UfrmLanguageSwitch; // I3306
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,
ActiveX,
ComObj,
Glossary,
LangSwitchManager,
msctf,
Types,
ErrorControlledRegistry,
RegistryKeys, ExtCtrls;
type
TMetrics = record
CellWidth,
ColCount,
RowCount,
TotalCellHeight,
TitleTextHeight,
KeyboardTextHeight,
KeyboardRowWidth,
KeyboardRowHeight: Integer;
CellHeight: array of Integer;
end;
TObjectType = (ptBorder, ptBorder2, ptBackground, ptLanguageIcon, ptHighlight, ptSelection, ptCellBackground,
csNormal, csHover, csSelected, csHoverSelected,
bsNormal, bsHover, bsSelected, bsHoverSelected);
TCellStatus = csNormal .. csHoverSelected;
TfrmLanguageSwitch = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure pbMouseEnter(Sender: TObject);
procedure pbMouseLeave(Sender: TObject);
procedure pbMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
procedure FormShow(Sender: TObject);
procedure pbClick(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
procedure FormHide(Sender: TObject);
private
FMetrics: TMetrics;
FHoverLanguage: Integer;
FHoverKeyboard: Integer;
FSelectedLanguage: Integer;
FSelectedKeyboard: Integer;
FOnHidden: TNotifyEvent;
hBuffer: THandle;
hLanguageFont: THandle;
hKeyboardFont: THandle;
hLanguageIconFont: THandle;
hPens: array[TObjectType] of THandle;
hBrushes: array[TObjectType] of THandle;
ptPos, ptSrc: TPoint;
szWnd: TSize;
blend: TBlendFunction;
bmi: TBitmapInfo;
pvBufferBits: Pointer;
dcLayered: Cardinal;
hLayeredBitmap: Cardinal;
pvLayeredBits: Pointer; // I2554
FTransparency: DWord; // I2555
FCurrentlySelectedLanguage: Integer; // I4191
FCurrentlySelectedKeyboard: Integer; // I4191
procedure GetColors(y: Integer; var cc: array of Cardinal);
procedure CancelDialog;
procedure WMMouseActivate(var Message: TWMMouseActivate); message WM_MOUSEACTIVATE;
procedure WMNCLButtonDown(var Message: TMessage); message WM_NCLBUTTONDOWN;
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
procedure WMUser(var Message: TMessage); message WM_USER;
procedure DrawLanguageIcons(DrawSelection: Boolean);
procedure MeasureFormSize;
procedure UpdateHover;
procedure SelectKeyboard(FromSelection: Boolean);
procedure DrawLayer;
procedure EndCanvas;
procedure StartCanvas;
function GetCellY(y: Integer): Integer; // I2669
function GetRowY(y: Integer): Integer; // I2669
function Manager: TLangSwitchManager;
function KeyboardTextWidth(Canvas: TCanvas;
Keyboard: TLangSwitchKeyboard): Integer; // I4191
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WndProc(var Message: TMessage); override;
public
procedure SelectItem(Item: TLangSwitchKeyboard); // I4191
function GetSelection: TLangSwitchKeyboard;
property OnHidden: TNotifyEvent read FOnHidden write FOnHidden;
end;
implementation
{$R *.dfm}
uses
System.Math,
Keyman.System.DebugLogClient,
KeymanControlMessages,
keymanapi_TLB,
UfrmKeyman7Main,
utilhotkey;
const
MLanguageFontHeight = -14;
MLanguageIconFontHeight = -10;
MKeyboardFontHeight = -12;
MWindowMargin: TRect = (Left: 16; Top: 16; Right: 16; Bottom: 16);
MCellSpacing: TPoint = (X: 8; Y: 8);
MCellMargin: TRect = (Left: 4; Top: 4; Right: 4; Bottom: 4);
MLanguageIconPosition: TPoint = (X: 2; Y: 2); // relative to MCellMargin
MLanguageTextPosition: TPoint = (X: 20; Y: 0); // relative to MCellMargin
MKeyboardsMargin: TRect = (Left: 10; Top: 2; Right: 0; Bottom: 0); // keyboard list margins
MKeyboardMargin: TRect = (Left: 2; Top: 2; Right: 2; Bottom: 2); // margin inside a single keyboard box
MKeyboardTextPosition: TPoint = (X: 18; Y: 0); // relative to MKeyboardsMargin + MKeyboardPadding
MKeyboardIconPosition: TPoint = (X: 0; Y: 0); // relative to MKeyboardsMargin + MKeyboardPadding
MHotkeyMargin = 8; // pixels between keyboard caption and hotkey text
MKeyboardSpacing = 2; // pixels between each keyboard entry
CTransparencyMask: Cardinal = $F0000000; // I2555
CNoTransparencyMask: Cardinal = $FF000000; // I2555
CWindowBackground: TColor = $D3BBA0;
CWindowBorder: array[0..5] of Cardinal = ($00000000,$3F4C58,$BCD1E3,$BCD1E3,$A5A2A5,$677B8E);
//CWindowBorder:array[0..3] of TColor = ($00FF00,$584C3F,$E3D0BB,$D3BBA0);
CCellBorder:array[TCellStatus,0..4] of TColor =
(($D3BBA0,$A3907C,$EADBC9,$C6B096,$D8C8B5),
($D3BBA0,$CDA77B,$F7E5CD,$E5CAA7,$E7D1B5),
($D3BBA0,$284AAD,$FFFFFF,$A793A2,$C8D1E9),
($D3BBA0,$284AAD,$FFFFFF,$A793A2,$C8D1E9));
CSelection: TColor = $8F7C67;
CSelectionText: TColor = $FFFFFF;
CNormalText: TColor = $000000;
CHighlight: TColor = $BD976B;
CHighlightText: TColor = $FFFFFF;
CLanguageIcon: TColor = $C0C2C6;
CLanguageIconText: TCOlor = $404143;
const
CColors: array[TObjectType] of TColor =
($584C3F,$E3D0BB,$D3BBA0,$C0C2C6,$BD976B,$8F7C67,$EADBC9,
$A3907C,$CDA77B,$284AAD,$284AAD,$EADBC9,$F7E5CD,$FFFFFF,$FFFFFF);
{ TForm1 }
procedure TfrmLanguageSwitch.FormCreate(Sender: TObject);
var
i: TObjectType;
begin
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED);
hLanguageFont := CreateFont(MLanguageFontHeight,0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH or FF_DONTCARE,'Tahoma');
hKeyboardFont := CreateFont(MKeyboardFontHeight,0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH or FF_DONTCARE,'Tahoma');
hLanguageIconFont := CreateFont(MLanguageIconFontHeight,0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH or FF_DONTCARE,'Tahoma');
for i := Low(TObjectType) to High(TObjectType) do
begin
hPens[i] := CreatePen(PS_SOLID, 1, CColors[i]);
hBrushes[i] := CreateSolidBrush(CColors[i]);
end;
end;
procedure TfrmLanguageSwitch.FormDestroy(Sender: TObject);
var
i: TObjectType;
begin
if hLanguageFont <> 0 then DeleteObject(hLanguageFont); // I2639
hLanguageFont := 0;
if hKeyboardFont <> 0 then DeleteObject(hKeyboardFont); // I2639
hKeyboardFont := 0;
if hLanguageIconFont <> 0 then DeleteObject(hLanguageIconFont); // I2639
hLanguageIconFont := 0;
if hBuffer <> 0 then DeleteObject(hBuffer); // I2639
hBuffer := 0;
if hLayeredBitmap <> 0 then DeleteObject(hLayeredBitmap); // I2639
hLayeredBitmap := 0;
for i := Low(TObjectType) to High(TObjectType) do
begin
if hPens[i] <> 0 then DeleteObject(hPens[i]); // I2639
hPens[i] := 0;
if hBrushes[i] <> 0 then DeleteObject(hBrushes[i]); // I2639
hBrushes[i] := 0;
end;
end;
procedure TfrmLanguageSwitch.FormHide(Sender: TObject);
begin
if Assigned(FOnHidden) then FOnHidden(Self);
if hLayeredBitmap <> 0 then DeleteObject(hLayeredBitmap); // I2639
hLayeredBitmap := 0;
UpdateLayeredWindow(Handle, 0, nil, nil, 0, nil, 0, @blend, ULW_ALPHA); // I2677
end;
procedure TfrmLanguageSwitch.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure AdjustSelectedKeyboard;
begin
if FSelectedKeyboard >= Manager.Languages[FSelectedLanguage].KeyboardCount then // I4191
FSelectedKeyboard := Manager.Languages[FSelectedLanguage].KeyboardCount - 1;
end;
begin
TDebugLogClient.Instance.WriteMessage('TfrmLanguageSwitch.FormKeyDown ENTER: %d [%d %d]', [Key, FSelectedLanguage, FSelectedKeyboard]);
try
if FSelectedLanguage = -1 then FSelectedLanguage := 0;
if FSelectedKeyboard = -1 then FSelectedKeyboard := 0;
case Key of
VK_SHIFT, VK_LSHIFT, VK_RSHIFT: // I4124
begin
Inc(FSelectedKeyboard);
if FSelectedKeyboard >= Manager.Languages[FSelectedLanguage].KeyboardCount then // I4191
begin
FSelectedKeyboard := 0;
Inc(FSelectedLanguage);
if FSelectedLanguage >= Manager.LanguageCount then // I4191
FSelectedLanguage := 0;
end;
end;
VK_LEFT:
begin
Dec(FSelectedLanguage);
if FSelectedLanguage < 0 then FSelectedLanguage := Manager.LanguageCount - 1; // I4191
AdjustSelectedKeyboard;
end;
VK_RIGHT:
begin
Inc(FSelectedLanguage);
if FSelectedLanguage >= Manager.LanguageCount then FSelectedLanguage := 0; // I4191
AdjustSelectedKeyboard;
end;
VK_UP:
begin
Dec(FSelectedKeyboard);
if FSelectedKeyboard < 0 then
begin
if FSelectedLanguage < FMetrics.ColCount then
begin
if (FMetrics.RowCount-1) * FMetrics.ColCount + FSelectedLanguage >= Manager.LanguageCount // I4191
then FSelectedLanguage := (FMetrics.RowCount-2) * FMetrics.ColCount + FSelectedLanguage
else FSelectedLanguage := (FMetrics.RowCount-1) * FMetrics.ColCount + FSelectedLanguage;
end
else
Dec(FSelectedLanguage, FMetrics.ColCount);
FSelectedKeyboard := Manager.Languages[FSelectedLanguage].KeyboardCount - 1; // I4191
end;
end;
VK_DOWN:
begin
Inc(FSelectedKeyboard);
if FSelectedKeyboard >= Manager.Languages[FSelectedLanguage].KeyboardCount then // I4191
begin
Inc(FSelectedLanguage, FMetrics.ColCount);
if FSelectedLanguage >= Manager.LanguageCount then // I4191
FSelectedLanguage := FSelectedLanguage mod FMetrics.ColCount;
FSelectedKeyboard := 0;
end;
end;
VK_HOME:
begin
FSelectedLanguage := 0;
FSelectedKeyboard := 0;
end;
VK_END:
begin
FSelectedLanguage := Manager.LanguageCount - 1; // I4191
FSelectedKeyboard := Manager.Languages[FSelectedLanguage].KeyboardCount - 1; // I4191
end;
VK_PRIOR:
begin
if FSelectedLanguage < FMetrics.ColCount then
begin
if (FMetrics.RowCount-1) * FMetrics.ColCount + FSelectedLanguage >= Manager.LanguageCount // I4191
then FSelectedLanguage := (FMetrics.RowCount-2) * FMetrics.ColCount + FSelectedLanguage
else FSelectedLanguage := (FMetrics.RowCount-1) * FMetrics.ColCount + FSelectedLanguage;
end
else
Dec(FSelectedLanguage, FMetrics.ColCount);
FSelectedKeyboard := 0;
end;
VK_NEXT:
begin
Inc(FSelectedLanguage, FMetrics.ColCount);
if FSelectedLanguage >= Manager.LanguageCount then // I4191
FSelectedLanguage := FSelectedLanguage mod FMetrics.ColCount;
FSelectedKeyboard := 0;
end;
32, VK_RETURN:
SelectKeyboard(True);
VK_ESCAPE:
CancelDialog;
else Exit;
end;
StartCanvas;
try
DrawLanguageIcons(True);
finally
EndCanvas;
end;
finally
TDebugLogClient.Instance.WriteMessage('TfrmLanguageSwitch.FormKeyDown EXIT: %d [%d %d]', [Key, FSelectedLanguage, FSelectedKeyboard]);
end;
Key := 0;
end;
procedure TfrmLanguageSwitch.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key in [VK_MENU, VK_LMENU, VK_RMENU] then // I4124
begin
Key := 0;
SelectKeyboard(True);
end;
end;
procedure TfrmLanguageSwitch.FormShow(Sender: TObject);
begin
FTransparency := CNoTransparencyMask;
MeasureFormSize;
FHoverKeyboard := -1;
FHoverLanguage := -1;
DrawLayer; // I2677
TDebugLogClient.Instance.WriteMessage('TfrmLanguageSwitch.FormShow EXIT: [%d %d]', [FSelectedLanguage, FSelectedKeyboard]);
end;
procedure TfrmLanguageSwitch.GetColors(y: Integer; var cc: array of Cardinal);
const
cbounds: array[0..1,0..3] of Cardinal = (
($9C8B7A, $B7A48F, $D2BBA3, $E3D1BC),
($8E7B67, $AD9780, $CCB296, $E0CBB3));
var
range, r1, g1, b1, r2, g2, b2, r, g, b, c: Cardinal;
i, v: Integer;
begin
v := 0;
range := ClientHeight;
cc[3] := $3F4C58 or FTransparency; // I2555
if y = 0 then y := 1;
for i := 0 to 1 do
begin
r1 := (cbounds[i,v+0] and $FF0000) shr 16;
g1 := (cbounds[i,v+0] and $00FF00) shr 8;
b1 := (cbounds[i,v+0] and $0000FF);
r2 := (cbounds[i,v+1] and $FF0000) shr 16;
g2 := (cbounds[i,v+1] and $00FF00) shr 8;
b2 := (cbounds[i,v+1] and $0000FF);
r := (r2 - r1) * Cardinal(y) div range + r1;
g := (g2 - g1) * Cardinal(y) div range + g1;
b := (b2 - b1) * Cardinal(y) div range + b1;
c := FTransparency or (r or (g shl 8) or (b shl 16)); // I2555
cc[i+1] := c;
end;
end;
function TfrmLanguageSwitch.GetSelection: TLangSwitchKeyboard;
begin
if (FSelectedLanguage < 0) or (FSelectedKeyboard < 0) // I2867
then Result := nil
else Result := Manager.Languages[FSelectedLanguage].Keyboards[FSelectedKeyboard]; // I4191
end;
function TfrmLanguageSwitch.Manager: TLangSwitchManager; // I4191
begin
Result := frmKeyman7Main.LangSwitchManager;
end;
function TfrmLanguageSwitch.KeyboardTextWidth(Canvas: TCanvas; Keyboard: TLangSwitchKeyboard): Integer;
var
FLanguage: IKeymanLanguage;
begin
Result := Canvas.TextWidth(Keyboard.Caption);
FLanguage := Keyboard.KeymanLanguage;
if Assigned(FLanguage) and (FLanguage.Hotkey.RawValue <> 0) then
Result := Result + MHotkeyMargin + Canvas.TextWidth(ShortcutToTextEx(HotkeyToShortcut(FLanguage.Hotkey)));
end;
procedure TfrmLanguageSwitch.MeasureFormSize;
var
x, y, i: Integer;
j: Integer;
FBuffer: TBitmap;
n: Integer;
begin
FBuffer := TBitmap.Create;
try
FBuffer.Canvas.Font.Name := 'Tahoma';
FBuffer.Canvas.Font.Size := MLanguageFontHeight;
FMetrics.TitleTextHeight := FBuffer.Canvas.TextHeight('A');
SetLength(FMetrics.CellHeight, Manager.LanguageCount); // I2669 // I4191
x := 1; // I3087
y := 0;
for i := 0 to Manager.LanguageCount - 1 do // I4191
begin
x := Max(x, FBuffer.Canvas.TextWidth(Manager.Languages[i].Caption) + MLanguageTextPosition.X); // I4191
y := Max(y, Manager.Languages[i].KeyboardCount); // I4191
end;
FBuffer.Canvas.Font.Size := MKeyboardFontHeight;
FMetrics.KeyboardTextHeight := FBuffer.Canvas.TextHeight('A');
FMetrics.KeyboardRowHeight := Max(FMetrics.KeyboardTextHeight, 16) + MKeyboardMargin.Top + MKeyboardMargin.Bottom;
for i := 0 to Manager.LanguageCount - 1 do // I4191
begin
for j := 0 to Manager.Languages[i].KeyboardCount - 1 do // I4191
begin
x := Max(x,
KeyboardTextWidth(FBuffer.Canvas, Manager.Languages[i].Keyboards[j]) +
MKeyboardsMargin.Left + MKeyboardsMargin.Right + MKeyboardMargin.Left + MKeyboardMargin.Right + MKeyboardTextPosition.X); // I4191
end;
end;
FMetrics.KeyboardRowWidth := x - MKeyboardsMargin.Left - MKeyboardsMargin.Right;
FMetrics.CellWidth := x + MCellMargin.Left + MCellMargin.Right;
FMetrics.TotalCellHeight := 0; // I2669
FMetrics.ColCount := Min(Manager.LanguageCount, (Screen.Width * 2 div 3) div x); // I4191
if FMetrics.ColCount = 0 then FMetrics.ColCount := 1; // I3087
FMetrics.RowCount := (Manager.LanguageCount-1) div FMetrics.ColCount + 1; // I4191
for i := 0 to Manager.LanguageCount do // I2669 // I4191
begin
if ((i mod FMetrics.ColCount) = 0) or (i = Manager.LanguageCount) then // I4191
begin
if i > 0 then
begin
y := (y * FMetrics.KeyboardRowHeight) + MKeyboardsMargin.Top + MKeyboardsMargin.Bottom + FMetrics.TitleTextHeight + MCellMargin.Top + MCellMargin.Bottom;
if (i mod FMetrics.ColCount) > 0 // I3023
then n := i - (i mod FMetrics.ColCount)
else n := i - FMetrics.ColCount;
for j := n to i - 1 do
FMetrics.CellHeight[j] := y;
FMetrics.TotalCellHeight := FMetrics.TotalCellHeight + y;
end;
y := 0;
end;
if i < Manager.LanguageCount then // I4191
y := Max(y, Manager.Languages[i].KeyboardCount); // I4191
end;
//FMetrics.CellHeight := (y * FMetrics.KeyboardRowHeight) + MKeyboardsMargin.Top + MKeyboardsMargin.Bottom + FMetrics.TitleTextHeight + MCellMargin.Top + MCellMargin.Bottom;
with FMetrics do
begin
szWnd.cx := CellWidth * ColCount + MCellSpacing.X * (ColCount - 1) + MWindowMargin.Left + MWindowMargin.Right;
szWnd.cy := TotalCellHeight + MCellSpacing.Y * (RowCount - 1) + MWindowMargin.Top + MWindowMargin.Bottom; // I2669
end;
ptPos.X := (Screen.Width div 2) - szWnd.cx div 2;
ptPos.Y := (Screen.Height div 2) - szWnd.cy div 2;
//TDebugManager.WriteMessage('TfrmLanguageSwitch.MeasureFormSize(%d, %d, %d, %d)', [ptPos.X, ptPos.Y, szWnd.cx, szWnd.cy]); // I4190
SetBounds(ptPos.X, ptPos.Y, szWnd.cx, szWnd.cy);
finally
FBuffer.Free;
end;
end;
procedure TfrmLanguageSwitch.pbClick(Sender: TObject);
begin
SelectKeyboard(False);
end;
procedure TfrmLanguageSwitch.pbMouseEnter(Sender: TObject);
begin
UpdateHover;
end;
procedure TfrmLanguageSwitch.pbMouseLeave(Sender: TObject);
begin
UpdateHover;
end;
procedure TfrmLanguageSwitch.pbMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
UpdateHover;
end;
function TfrmLanguageSwitch.GetCellY(y: Integer): Integer; // Get index of row from pixel Y // I2669
var
i: Integer;
begin
i := 0;
while i < FMetrics.RowCount do
begin
if y < FMetrics.CellHeight[i * FMetrics.ColCount] + MCellSpacing.Y then
begin
Result := i;
Exit;
end;
Dec(y, FMetrics.CellHeight[i * FMetrics.ColCount] + MCellSpacing.Y);
Inc(i);
end;
Result := i;
end;
function TfrmLanguageSwitch.GetRowY(y: Integer): Integer; // Get pixel Y from index of row // I2669
var
i: Integer;
begin
i := 0;
Result := 0;
while i < y do
begin
Inc(Result, FMetrics.CellHeight[i * FMetrics.ColCount] + MCellSpacing.Y);
Inc(i);
end;
end;
procedure TfrmLanguageSwitch.UpdateHover;
var
pt: TPoint;
r: TRect;
FNewHoverLanguage: Integer;
lang: TLangSwitchLanguage;
FNewHoverKeyboard: Integer;
begin
with FMetrics do
begin
if not GetCursorPos(pt) then pt := Point(-1,-1) // I3213 // I3517
else pt := ScreenToClient(pt);
r := ClientRect;
Inc(r.Left, MWindowMargin.Left);
Inc(r.Top, MWindowMargin.Top);
Dec(r.Right, MWindowMargin.Right);
Dec(r.Bottom, MWindowMargin.Bottom);
FNewHoverKeyboard := -1;
FNewHoverLanguage := -1;
if PtInRect(r, pt) then
begin
pt.X := (pt.X - r.Left) div (CellWidth + MCellSpacing.X);
pt.Y := GetCellY(pt.Y - r.Top); // I2669
FNewHoverLanguage := pt.X + pt.Y * ColCount;
if FNewHoverLanguage >= Manager.LanguageCount then // I4191
FNewHoverLanguage := -1
else
begin
lang := Manager.Languages[FNewHoverLanguage]; // I4191
if not GetCursorPos(pt) then pt := Point(-1,-1) // I3213 // I3517
else pt := ScreenToClient(pt);
Dec(pt.X, MWindowMargin.Left);
Dec(pt.Y, MWindowMargin.Top);
pt.X := pt.X mod (CellWidth + MCellSpacing.X);
pt.Y := pt.Y - GetRowY(FNewHoverLanguage div ColCount); // I2669
if pt.Y < MCellMargin.Top + TitleTextHeight + MKeyboardsMargin.Top then
FNewHoverKeyboard := 0 // on or around the title
else if pt.Y > MCellMargin.Top + TitleTextHeight + MKeyboardsMargin.Top + lang.KeyboardCount * KeyboardRowHeight then
FNewHoverKeyboard := 0 // below list of keyboards
else if (pt.X < MCellMargin.Left) or (pt.X > CellWidth - MCellMargin.Right) then
FNewHoverKeyboard := 0 // in the left or right margin of the cell
else
FNewHoverKeyboard := (pt.Y - (MCellMargin.Top + TitleTextHeight + MKeyboardsMargin.Top)) div KeyboardRowHeight;
end;
end;
if (FNewHoverLanguage <> FHoverLanguage) or
(FNewHoverKeyboard <> FHoverKeyboard) then
begin
FHoverLanguage := FNewHoverLanguage;
FHoverKeyboard := FNewHoverKeyboard;
StartCanvas;
try
DrawLanguageIcons(True);
finally
EndCanvas;
end;
end;
end;
end;
procedure TfrmLanguageSwitch.WMMouseActivate(var Message: TWMMouseActivate);
begin
Message.Result := MA_NOACTIVATE;
end;
procedure TfrmLanguageSwitch.WMNCHitTest(var Message: TWMNCHitTest);
begin
Message.Result := htClient;
end;
procedure TfrmLanguageSwitch.WMNCLButtonDown(var Message: TMessage);
begin
inherited;
end;
procedure TfrmLanguageSwitch.WMUser(var Message: TMessage);
begin
DrawLayer;
end;
procedure TfrmLanguageSwitch.WndProc(var Message: TMessage);
var
wParam: WORD;
begin
if Message.Msg = wm_keyman_control then
begin
wParam := Message.lParam;
case Message.WParam of
KMC_KEYDOWN:
KeyDown(wParam, []);
KMC_KEYUP:
KeyUp(WParam, []);
end;
end
else
inherited;
end;
type
CardinalArray = array[0..$effffff] of Cardinal;
PCardinalArray = ^CardinalArray;
procedure TfrmLanguageSwitch.DrawLayer;
var
dc, dcScreen: THandle;
hBuffer: THandle;
hOldBitmap: THandle;
p: PCardinalArray;
procedure DrawBackgroundAndBorders;
var
x, y: Integer;
cc: array[0..3] of Cardinal;
begin
{ Draw bottom border }
GetColors(1, cc);
y := 0;
for x := 5 to ClientWidth - 6 do p^[x + y] := cc[3];
y := ClientWidth * 1;
for x := 5 to ClientWidth - 6 do p^[x + y] := cc[1];
{ Draw top border }
GetColors(ClientHeight-1, cc);
y := ClientWidth * (ClientHeight - 1);
for x := 5 to ClientWidth - 6 do p^[x + y] := cc[3]; //CWindowBorder[5] or CTransparency;
y := ClientWidth * (ClientHeight - 2);
for x := 5 to ClientWidth - 6 do p^[x + y] := cc[1]; //CWindowBorder[2] or CTransparency;
{ Draw side borders }
for y := 2 to ClientHeight - 3 do
begin
GetColors(y, cc);
p^[ClientWidth * y] := cc[3];
p^[ClientWidth * y + 1] := cc[1];
p^[ClientWidth * y + ClientWidth - 2] := cc[1];
p^[ClientWidth * y + ClientWidth - 1] := cc[3];
for x := 2 to ClientWidth - 3 do p^[ClientWidth * y + x] := cc[2];
end;
end;
begin
dcScreen := GetDC(Handle);
dc := CreateCompatibleDC(dcScreen);
// zero the memory for the bitmap info
ZeroMemory(@bmi, sizeof(BITMAPINFO));
// setup bitmap info
bmi.bmiHeader.biSize := sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth := ClientWidth;
bmi.bmiHeader.biHeight := ClientHeight;
bmi.bmiHeader.biPlanes := 1;
bmi.bmiHeader.biBitCount := 32; // four 8-bit components
bmi.bmiHeader.biCompression := BI_RGB;
bmi.bmiHeader.biSizeImage := ClientWidth * ClientHeight * 4;
if hLayeredBitmap <> 0 then DeleteObject(hLayeredBitmap); // I2639
// create our DIB section and select the bitmap into the dc
hBuffer := CreateDIBSection(dc, bmi, DIB_RGB_COLORS, pvBufferBits, 0, 0);
hLayeredBitmap := CreateDIBSection(dc, bmi, DIB_RGB_COLORS, pvLayeredBits, 0, 0); // I2554
hOldBitmap := SelectObject(dc, hBuffer);
ptSrc := Point(0, 0);
blend.BlendOp := AC_SRC_OVER;
blend.BlendFlags := 0;
blend.SourceConstantAlpha := 255;
blend.AlphaFormat := AC_SRC_ALPHA;
p := PCardinalArray(pvBufferBits);
DrawBackgroundAndBorders;
dcLayered := dc;
DrawLanguageIcons(False);
dcLayered := 0;
SelectObject(dc, hOldBitmap);
DeleteDC(dc);
ReleaseDC(Handle, dcScreen);
StartCanvas;
DrawLanguageIcons(True);
EndCanvas;
end;
procedure TfrmLanguageSwitch.StartCanvas;
var
dcScreen: THandle;
begin
if Assigned(pvLayeredBits) and Assigned(pvBufferBits) and (hLayeredBitmap <> 0) then // I2554
begin
dcScreen := GetDC(Handle);
dcLayered := CreateCompatibleDC(dcScreen);
ReleaseDC(Handle, dcScreen);
SelectObject(dcLayered, hLayeredBitmap);
CopyMemory(pvLayeredBits, pvBufferBits, ClientWidth * ClientHeight * 4);
end;
end;
procedure TfrmLanguageSwitch.EndCanvas;
var
dcScreen: THandle;
procedure DrawCornersAndMakeTransparent;
const
pels: array[0..4,0..4] of Byte =
((0,0,0,0,0),
(0,0,0,1,1),
(0,0,1,2,2),
(0,1,2,3,3),
(0,1,2,3,3));
var
v, x, y: Integer;
X1, X2: Integer;
c: Cardinal;
cc: array[0..3] of Cardinal;
p: PCardinalArray;
//FSelectionRect: TRect;
begin
{ Draw corners }
p := PCardinalArray(pvLayeredBits); // I2554
for y := 0 to 4 do
begin
v := (ClientHeight - y - 1) * ClientWidth;
GetColors(ClientHeight - y, cc); //1, c2, c3);
for x := 0 to 4 do
begin
case pels[x, y] of
0: c := 0;
2: c := cc[1];
3: c := cc[2];
else c := CWindowBorder[pels[x, y]] or FTransparency; // I2555
end;
p^[v + x] := c;
p^[v + ClientWidth - x - 1] := c;
end;
v := y * ClientWidth;
GetColors(y, cc);
for x := 0 to 4 do
begin
case pels[x, y] of
0: c := 0;
2: c := cc[1];
3: c := cc[2];
else c := CWindowBorder[pels[x, y]] or FTransparency; // I2555
end;
p^[v + x] := c;
p^[v + ClientWidth - x - 1] := c;
end;
end;
{ Reset transparency after GDI operations }
for y := 0 to ClientHeight - 1 do
begin
v := y * Clientwidth;
if (y < ClientHeight - 5) and (y > 4) then
begin
X1 := 0;
X2 := ClientWidth - 1;
end
else
begin
X1 := 5;
X2 := ClientWidth - 6;
end;
for x := X1 to X2 do
p^[v + x] := (p^[v + x] and $FFFFFF) or FTransparency; // I2555
end;
{if FSelectedItemIndex >= 0 then
begin
FSelectionRect := FItemRects[FSelectedItemIndex];
for y := FSelectionRect.Top to FSelectionRect.Bottom - 1 do
begin
v := (ClientHeight - y) * ClientWidth;
for x := FSelectionRect.Left to FSelectionRect.Right - 1 do
p^[v + x] := (p^[v + x] and $FFFFFF) or $FF000000;
end;
end;}
end;
begin
if dcLayered = 0 then Exit; // I2554
if Assigned(pvLayeredBits) and Assigned(pvBufferBits) and (hLayeredBitmap <> 0) then // I2554
begin
SelectObject(dcLayered, GetStockObject(NULL_BRUSH));
SelectObject(dcLayered, GetStockObject(NULL_PEN));
SelectObject(dcLayered, GetStockObject(SYSTEM_FONT));
GdiFlush;
DrawCornersAndMakeTransparent;
dcScreen := GetDC(Handle);
if not UpdateLayeredWindow(Handle, dcScreen, @ptPos, @szWnd, dcLayered, @ptSrc, 0, @blend, ULW_ALPHA) then
RaiseLastOSError;
ReleaseDC(Handle, dcScreen);
end;
DeleteDC(dcLayered);
dcLayered := 0; // I2554
end;
procedure TfrmLanguageSwitch.SelectKeyboard(FromSelection: Boolean);
begin
if not FromSelection then
begin
FSelectedKeyboard := FHoverKeyboard;
FSelectedLanguage := FHoverLanguage;
end;
Hide;
end;
procedure TfrmLanguageSwitch.CancelDialog;
begin
Hide;
end;
procedure TfrmLanguageSwitch.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.ExStyle := Params.ExStyle or (WS_EX_NOACTIVATE or WS_EX_TOPMOST or WS_EX_LAYERED);
end;
procedure TfrmLanguageSwitch.DrawLanguageIcons(DrawSelection: Boolean);
const
pels: array[0..5,0..5] of Byte =
((0,0,0,0,0,1),
(0,0,0,1,1,4),
(0,0,1,4,2,2),
(0,1,4,2,2,2),
(0,1,2,2,2,2),
(1,4,2,2,2,2));
function BrushState(CellStatus: TObjectType): TObjectType;
begin
Result := TCellStatus(Ord(CellStatus)+4);
end;
var
bx, by, x, y, I: Integer;
j: Integer;
FTextRect: TRect;
FRowRect: TRect;
FCellRect: TRect;
CellStatus: TCellStatus;
FIconRect: TRect;
szHotkey, sz: TSize;
cc0,cc1: array[0..3] of Cardinal;
c0, c1: Cardinal;
Save: THandle;
MaskDC: HDC;
k, m, l: Integer;
FKeyboard_Tip: TLangSwitchKeyboard_TIP;
FHotkeyText: string;
FKeymanLanguage: IKeymanLanguage;
begin
with FMetrics do
begin
SetBkMode(dcLayered, TRANSPARENT);
for I := 0 to Manager.LanguageCount - 1 do // I4191
begin
if DrawSelection and (FHoverLanguage <> I) and (FSelectedLanguage <> I) then Continue;
x := MWindowMargin.Left + (I mod ColCount) * (CellWidth + MCellSpacing.X);
y := MWindowMargin.Top + GetRowY(I div ColCount); // I2669
FCellRect := Types.Rect(x, y, x + CellWidth, y + CellHeight[I]); // I2669
if not DrawSelection then
CellStatus := csNormal
else if FHoverLanguage = I then
if FSelectedLanguage = I
then CellStatus := csHoverSelected
else CellStatus := csHover
else if FSelectedLanguage = I
then CellStatus := csSelected
else CellStatus := csNormal;
{ Draw border }
SelectObject(dcLayered, hPens[CellStatus]);
SelectObject(dcLayered, hBrushes[BrushState(CellStatus)]);
Rectangle(dcLayered, FCellRect.Left, FCellRect.Top, FCellRect.Right, FCellRect.Bottom);
for by := 0 to 5 do
begin
GetColors(ClientHeight-FCellRect.Top-by, cc0);
GetColors(ClientHeight-FCellRect.Bottom+by-1, cc1);
for bx := 0 to 5 do
begin
if pels[bx,by] = 0 then
begin
c0 := cc0[2]; c0 := ((c0 and $FF) shl 16) or ((c0 and $FF00)) or ((c0 and $FF0000) shr 16);
c1 := cc1[2]; c1 := ((c1 and $FF) shl 16) or ((c1 and $FF00)) or ((c1 and $FF0000) shr 16);
end
else
begin
c0 := CCellBorder[CellStatus,pels[bx,by]]; c1 := c0;
end;
SetPixelV(dcLayered, FCellRect.Left+bx, FCellRect.Top+by, c0);
SetPixelV(dcLayered, FCellRect.Right-bx-1, FCellRect.Top+by, c0);
SetPixelV(dcLayered, FCellRect.Right-bx-1, FCellRect.Bottom-by-1, c1);
SetPixelV(dcLayered, FCellRect.Left+bx, FCellRect.Bottom-by-1, c1);
end;
end;
SelectObject(dcLayered, hLanguageFont);
{ Draw language title }
Inc(x, MCellMargin.Left);
Inc(y, MCellMargin.Top);
ExtTextOutW(dcLayered, x + MLanguageTextPosition.X, y + MLanguageTextPosition.Y, 0, nil, PWideChar(Manager.Languages[i].Caption),
Length(Manager.Languages[i].Caption), nil); // I4191
{ Draw language icon }
SelectObject(dcLayered, GetStockObject(NULL_PEN));
SelectObject(dcLayered, hBrushes[ptLanguageIcon]);
SelectObject(dcLayered, hLanguageIconFont);
SetTextColor(dcLayered, CLanguageIconText);
FIconRect := Types.Rect(x + MLanguageIconPosition.X, y + MLanguageIconPosition.Y, x + MLanguageIconPosition.X + 16, y + MLanguageIconPosition.Y + 16);
Rectangle(dcLayered, FIconRect.Left, FIconRect.Top, FIconRect.Right + 1, FIconRect.Bottom + 1);
GetTextExtentPoint32W(dcLayered, PWideChar(Manager.Languages[i].IconText), Length(Manager.Languages[i].IconText), sz); // I4191
ExtTextOutW(dcLayered, FIconRect.Left + 8 - sz.cx div 2, FIconRect.Top + 8 - sz.cy div 2 - 1,
ETO_CLIPPED, @FIconRect, PWideChar(Manager.Languages[i].IconText),
Length(Manager.Languages[i].IconText), nil); // I4191
SetPixelV(dcLayered, FIconRect.Left, FIconRect.Top, CCellBorder[CellStatus,2]);
SetPixelV(dcLayered, FIconRect.Right - 1, FIconRect.Top, CCellBorder[CellStatus,2]);
SetPixelV(dcLayered, FIconRect.Right - 1, FIconRect.Bottom - 1, CCellBorder[CellStatus,2]);
SetPixelV(dcLayered, FIconRect.Left, FIconRect.Bottom - 1, CCellBorder[CellStatus,2]);
{ Draw keyboard list }
Inc(x, MKeyboardsMargin.Left);
Inc(y, TitleTextHeight + MKeyboardsMargin.Top);
SelectObject(dcLayered, hKeyboardFont);
for j := 0 to Manager.Languages[i].KeyboardCount - 1 do // I4191
begin
FRowRect := Rect(x, y, x + KeyboardRowWidth, y + KeyboardRowHeight);
FTextRect := Rect(
FRowRect.Left + MKeyboardMargin.Left + MKeyboardTextPosition.X,
FRowRect.Top + MKeyboardMargin.Top + MKeyboardTextPosition.Y,
FRowRect.Right - MKeyboardMargin.Right,
FRowRect.Top + MKeyboardMargin.Top + MKeyboardTextPosition.Y + KeyboardTextHeight);
if (FHoverLanguage = i) and (FHoverKeyboard = j) and DrawSelection then
begin
SelectObject(dcLayered, hBrushes[ptHighlight]);
SetTextColor(dcLayered, CHighlightText);
end
else if (FSelectedKeyboard = j) and (FSelectedLanguage = i) and DrawSelection then
begin
SelectObject(dcLayered, hBrushes[ptSelection]);
SetTextColor(dcLayered, CSelectionText);
end
else
begin
SelectObject(dcLayered, hBrushes[BrushState(CellStatus)]);
SetTextColor(dcLayered, CNormalText);
end;
SelectObject(dcLayered, GetStockObject(NULL_PEN));
Rectangle(dcLayered, FRowRect.Left, FRowRect.Top, FRowRect.Right + 1, FRowRect.Bottom + 1);
SetPixelV(dcLayered, FRowRect.Left, FRowRect.Top, CCellBorder[CellStatus,2]);
SetPixelV(dcLayered, FRowRect.Right-1, FRowRect.Top, CCellBorder[CellStatus,2]);
SetPixelV(dcLayered, FRowRect.Right-1, FRowRect.Bottom-1, CCellBorder[CellStatus,2]);
SetPixelV(dcLayered, FRowRect.Left, FRowRect.Bottom-1, CCellBorder[CellStatus,2]);
if (I = FCurrentlySelectedLanguage) and (J = FCurrentlySelectedKeyboard) then // I4191
begin
// I2674 - Show the Selection with a little triangle on the left
for k := 0 to 8 do
begin
if k > 4 then m := 8 - k else m := k;
for l := 0 to m do
SetPixelV(dcLayered, FRowRect.Left - MKeyboardsMargin.Left + l + 2, FRowRect.Top + 5 + k, CNormalText);
end;
end;
ExtTextOutW(dcLayered, FTextRect.Left, FTextRect.Top, ETO_CLIPPED, @FTextRect,
PWideChar(Manager.Languages[i].Keyboards[j].Caption),
Length(Manager.Languages[i].Keyboards[j].Caption), nil); // I4191
FKeymanLanguage := Manager.Languages[i].Keyboards[j].KeymanLanguage;
if Assigned(FKeymanLanguage) and (FKeymanLanguage.Hotkey.RawValue <> 0) then
begin
FHotkeyText := ShortcutToTextEx(HotkeyToShortcut(FKeymanLanguage.Hotkey));
GetTextExtentPoint32W(dcLayered, PWideChar(FHotkeyText), Length(FHotkeyText), szHotkey); // I4191
ExtTextOutW(dcLayered, FTextRect.Right - szHotkey.cx, FTextRect.Top, ETO_CLIPPED, @FTextRect,
PWideChar(FHotkeyText), Length(FHotkeyText), nil); // I4191
end;
if Assigned(Manager.Languages[i].Keyboards[j].Bitmap) then // I4191
begin
Save := 0; // I2593
MaskDC := 0;
try
if Manager.Languages[i].Keyboards[j] is TLangSwitchKeyboard_TIP then // I4204
begin
FKeyboard_Tip := Manager.Languages[i].Keyboards[j] as TLangSwitchKeyboard_TIP;
if FKeyboard_Tip.IconHandle <> 0 then
DrawIconEx(dcLayered,
FRowRect.Left + MKeyboardMargin.Left + MKeyboardIconPosition.X,
FRowRect.Top + MKeyboardMargin.Top + MKeyboardIconPosition.Y,
FKeyboard_Tip.IconHandle,
16, 16, 0, 0, DI_NORMAL)
else
FKeyboard_Tip := nil;
end
else
FKeyboard_Tip := nil;
if FKeyboard_Tip = nil then
begin
MaskDC := CreateCompatibleDC(0);
Save := SelectObject(MaskDC, Manager.Languages[i].Keyboards[j].Bitmap.MaskHandle); // I4191
TransparentStretchBlt(dcLayered,
FRowRect.Left + MKeyboardMargin.Left + MKeyboardIconPosition.X,
FRowRect.Top + MKeyboardMargin.Top + MKeyboardIconPosition.Y,
16, 16, Manager.Languages[i].Keyboards[j].Bitmap.Canvas.Handle, 0, 0, 16, 16, MaskDC, 0, 0); // I4191
end;
finally
if Save <> 0 then SelectObject(MaskDC, Save);
if MaskDC <> 0 then DeleteDC(MaskDC);
end;
{
BitBlt(dcLayered,
FRowRect.Left + MKeyboardMargin.Left + MKeyboardIconPosition.X,
FRowRect.Top + MKeyboardMargin.Top + MKeyboardIconPosition.Y,
16, 16, FManager.Languages[i].Keyboards[j].Bitmap.Canvas.Handle,
0, 0, SRCCOPY);
}
end;
Inc(y, KeyboardRowHeight);
SetTextColor(dcLayered, $000000);
end;
end;
end;
end;
procedure TfrmLanguageSwitch.SelectItem(Item: TLangSwitchKeyboard); // I4191
var
I: Integer;
J: Integer;
begin
FSelectedKeyboard := 0;
FSelectedLanguage := 0;
for I := 0 to Manager.LanguageCount - 1 do
for J := 0 to Manager.Languages[i].KeyboardCount - 1 do
if Manager.Languages[I].Keyboards[J] = Item then
begin
FSelectedKeyboard := J;
FSelectedLanguage := I;
Break;
end;
FCurrentlySelectedKeyboard := FSelectedKeyboard;
FCurrentlySelectedLanguage := FSelectedLanguage;
end;
end.