📄 tntforms.pas
字号:
procedure TTntForm.WMWindowPosChanging(var Message: TMessage);
begin
inherited;
// This message *sometimes* means that the Menu.BiDiMode changed.
FixMenuBiDiProblem(Menu);
end;
function TTntForm.CreateDockManager: IDockManager;
begin
if (DockManager = nil) and DockSite and UseDockManager then
HandleNeeded; // force TNT subclassing to occur first
Result := inherited CreateDockManager;
end;
{ TTntApplication }
constructor TTntApplication.Create(AOwner: TComponent);
begin
inherited;
Application.HookMainWindow(WndProc);
FSettingChangeTime := GetTickCount;
TntSysUtils._SettingChangeTime := GetTickCount;
end;
destructor TTntApplication.Destroy;
begin
FreeAndNil(FTntAppIdleEventControl);
Application.UnhookMainWindow(WndProc);
inherited;
end;
function TTntApplication.GetHint: WideString;
begin
// check to see if the hint has already been set on application.idle
if Application.Hint = AnsiString(ApplicationMouseControlHint) then
FHint := ApplicationMouseControlHint;
// get the synced string
Result := GetSyncedWideString(FHint, Application.Hint)
end;
procedure TTntApplication.SetAnsiAppHint(const Value: AnsiString);
begin
Application.Hint := Value;
end;
procedure TTntApplication.SetHint(const Value: WideString);
begin
SetSyncedWideString(Value, FHint, Application.Hint, SetAnsiAppHint);
end;
function TTntApplication.GetExeName: WideString;
begin
Result := WideParamStr(0);
end;
function TTntApplication.GetTitle: WideString;
begin
if (Application.Handle <> 0) and Win32PlatformIsUnicode then begin
SetLength(Result, DefWindowProcW(Application.Handle, WM_GETTEXTLENGTH, 0, 0) + 1);
DefWindowProcW(Application.Handle, WM_GETTEXT, Length(Result), Integer(PWideChar(Result)));
SetLength(Result, Length(Result) - 1);
end else
Result := GetSyncedWideString(FTitle, Application.Title);
end;
procedure TTntApplication.SetAnsiApplicationTitle(const Value: AnsiString);
begin
Application.Title := Value;
end;
procedure TTntApplication.SetTitle(const Value: WideString);
begin
if (Application.Handle <> 0) and Win32PlatformIsUnicode then begin
if (GetTitle <> Value) or (FTitle <> '') then begin
DefWindowProcW(Application.Handle, WM_SETTEXT, 0, lParam(PWideChar(Value)));
FTitle := '';
end
end else
SetSyncedWideString(Value, FTitle, Application.Title, SetAnsiApplicationTitle);
end;
{$IFDEF COMPILER_6} // verified against VCL source in Delphi 6 and BCB 6
type
THackApplication = class(TComponent)
protected
FxxxxxxxxxHandle: HWnd;
FxxxxxxxxxBiDiMode: TBiDiMode;
FxxxxxxxxxBiDiKeyboard: AnsiString;
FxxxxxxxxxNonBiDiKeyboard: AnsiString;
FxxxxxxxxxObjectInstance: Pointer;
FxxxxxxxxxMainForm: TForm{TNT-ALLOW TForm};
FMouseControl: TControl;
end;
{$ENDIF}
{$IFDEF DELPHI_7} // verified against VCL source in Delphi 7
type
THackApplication = class(TComponent)
protected
FxxxxxxxxxHandle: HWnd;
FxxxxxxxxxBiDiMode: TBiDiMode;
FxxxxxxxxxBiDiKeyboard: AnsiString;
FxxxxxxxxxNonBiDiKeyboard: AnsiString;
FxxxxxxxxxObjectInstance: Pointer;
FxxxxxxxxxMainForm: TForm{TNT-ALLOW TForm};
FMouseControl: TControl;
end;
{$ENDIF}
{$IFDEF DELPHI_9} // verified against VCL source in Delphi 9
type
THackApplication = class(TComponent)
protected
FxxxxxxxxxHandle: HWnd;
FxxxxxxxxxBiDiMode: TBiDiMode;
FxxxxxxxxxBiDiKeyboard: AnsiString;
FxxxxxxxxxNonBiDiKeyboard: AnsiString;
FxxxxxxxxxObjectInstance: Pointer;
FxxxxxxxxxMainForm: TForm{TNT-ALLOW TForm};
FMouseControl: TControl;
end;
{$ENDIF}
{$IFDEF DELPHI_10} // verified against VCL source in Delphi 10
type
THackApplication = class(TComponent)
protected
FxxxxxxxxxHandle: HWnd;
FxxxxxxxxxBiDiMode: TBiDiMode;
FxxxxxxxxxBiDiKeyboard: AnsiString;
FxxxxxxxxxNonBiDiKeyboard: AnsiString;
FxxxxxxxxxObjectInstance: Pointer;
FxxxxxxxxxMainForm: TForm{TNT-ALLOW TForm};
FMouseControl: TControl;
end;
{$ENDIF}
function TTntApplication.ApplicationMouseControlHint: WideString;
var
MouseControl: TControl;
begin
MouseControl := THackApplication(Application).FMouseControl;
Result := WideGetLongHint(WideGetHint(MouseControl));
end;
procedure TTntApplication.DoIdle;
begin
// update TntApplication.Hint only when Ansi encodings are the same... (otherwise there are problems with action menus)
if Application.Hint = AnsiString(ApplicationMouseControlHint) then
Hint := ApplicationMouseControlHint;
end;
function TTntApplication.IsDlgMsg(var Msg: TMsg): Boolean;
begin
Result := False;
if (Application.DialogHandle <> 0) then begin
if IsWindowUnicode(Application.DialogHandle) then
Result := IsDialogMessageW(Application.DialogHandle, Msg)
else
Result := IsDialogMessageA(Application.DialogHandle, Msg);
end;
end;
type
TTntAppIdleEventControl = class(TControl)
protected
procedure OnIdle(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TTntAppIdleEventControl.Create(AOwner: TComponent);
begin
inherited;
ParentFont := False; { This allows Parent (Application) to be in another module. }
Parent := Application.MainForm;
Visible := True;
Action := TTntAction.Create(Self);
Action.OnExecute := OnIdle;
Action.OnUpdate := OnIdle;
TntApplication.FTntAppIdleEventControl := Self;
end;
destructor TTntAppIdleEventControl.Destroy;
begin
if TntApplication <> nil then
TntApplication.FTntAppIdleEventControl := nil;
inherited;
end;
procedure TTntAppIdleEventControl.OnIdle(Sender: TObject);
begin
TntApplication.DoIdle;
end;
function TTntApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
// Check Main Form
if (not FMainFormChecked) and (Application.MainForm <> nil) then begin
if not (Application.MainForm is TTntForm) then begin
// This control will help ensure that DoIdle is called
TTntAppIdleEventControl.Create(Application.MainForm);
end;
FMainFormChecked := True;
end;
// Check for Unicode char messages
if (Msg.message = WM_CHAR)
and (Msg.wParam > Integer(High(AnsiChar)))
and IsWindowUnicode(Msg.hwnd)
and ((Application.DialogHandle = 0) or IsWindowUnicode(Application.DialogHandle))
then begin
Result := True;
// more than 8-bit WM_CHAR destined for Unicode window
Handled := False;
if Assigned(Application.OnMessage) then
Application.OnMessage(Msg, Handled);
Application.CancelHint;
// dispatch msg if not a dialog message
if (not Handled) and (not IsDlgMsg(Msg)) then
DispatchMessageW(Msg);
end;
end;
function TTntApplication.WndProc(var Message: TMessage): Boolean;
var
BasicAction: TBasicAction;
begin
Result := False; { not handled }
if (Message.Msg = WM_SETTINGCHANGE) then begin
FSettingChangeTime := GetTickCount;
TntSysUtils._SettingChangeTime := FSettingChangeTime;
end;
if (Message.Msg = WM_CREATE)
and (FTitle <> '') then begin
SetTitle(FTitle);
FTitle := '';
end;
if (Message.Msg = CM_ACTIONEXECUTE) then begin
BasicAction := TBasicAction(Message.LParam);
if (BasicAction.ClassType = THintAction{TNT-ALLOW THintAction})
and (THintAction{TNT-ALLOW THintAction}(BasicAction).Hint = AnsiString(Hint))
then begin
Result := True;
Message.Result := 1;
with TTntHintAction.Create(Self) do
begin
Hint := Self.Hint;
try
Execute;
finally
Free;
end;
end;
end;
end;
end;
function TTntApplication.MessageBox(const Text, Caption: PWideChar;
Flags: Integer): Integer;
var
ActiveWindow, TaskActiveWindow: HWnd;
WindowList: Pointer;
MBMonitor, AppMonitor: HMonitor;
MonInfo: TMonitorInfo;
Rect: TRect;
FocusState: TFocusState;
begin
with Application do
begin
{$IFDEF DELPHI_9_UP}
ActiveWindow := ActiveFormHandle;
{$ELSE}
ActiveWindow := GetActiveWindow;
if ActiveWindow = 0 then
ActiveWindow := GetLastActivePopup(Handle);
{$ENDIF}
if ActiveWindow = 0 then
TaskActiveWindow := Handle
else
TaskActiveWindow := ActiveWindow;
MBMonitor := MonitorFromWindow(ActiveWindow, MONITOR_DEFAULTTONEAREST);
AppMonitor := MonitorFromWindow(Handle, MONITOR_DEFAULTTONEAREST);
if MBMonitor <> AppMonitor then
begin
MonInfo.cbSize := Sizeof(TMonitorInfo);
GetMonitorInfo(MBMonitor, @MonInfo);
GetWindowRect(Handle, Rect);
SetWindowPos(Handle, 0,
MonInfo.rcMonitor.Left + ((MonInfo.rcMonitor.Right - MonInfo.rcMonitor.Left) div 2),
MonInfo.rcMonitor.Top + ((MonInfo.rcMonitor.Bottom - MonInfo.rcMonitor.Top) div 2),
0, 0, SWP_NOACTIVATE or SWP_NOREDRAW or SWP_NOSIZE or SWP_NOZORDER);
end;
WindowList := DisableTaskWindows(ActiveWindow);
FocusState := SaveFocusState;
if UseRightToLeftReading then Flags := Flags or MB_RTLREADING;
try
Result := Windows.MessageBoxW(TaskActiveWindow, Text, Caption, Flags);
finally
if MBMonitor <> AppMonitor then
SetWindowPos(Handle, 0,
Rect.Left + ((Rect.Right - Rect.Left) div 2),
Rect.Top + ((Rect.Bottom - Rect.Top) div 2),
0, 0, SWP_NOACTIVATE or SWP_NOREDRAW or SWP_NOSIZE or SWP_NOZORDER);
EnableTaskWindows(WindowList);
SetActiveWindow(ActiveWindow);
RestoreFocusState(FocusState);
end;
end;
end;
procedure TTntApplication.ShowException(E: Exception);
var
Msg: WideString;
begin
// Because of OverwriteProcedure call in TntSystem-unit, Self could point
// to a TApplication instance instead of a TTntApplication instance, thus
// causing access violations. Therefore frame the whole code with a
// "with TntApplication do"-statement.
with TntApplication do
begin
if E is WideException then
begin
Msg := WideException(E).Message;
if (Msg <> '') and (TntWideLastChar(Msg) > '.') then Msg := Msg + '.';
MessageBox(PWideChar(Msg), PWideChar(Title), MB_OK + MB_ICONSTOP);
end
else
begin
Msg := Exception(E).Message;
if (Msg <> '') and (TntWideLastChar(Msg) > '.') then Msg := Msg + '.';
MessageBox(PWideChar(Msg), PWideChar(Title), MB_OK + MB_ICONSTOP);
end
end
end;
//===========================================================================
// The NT GetMessage Hook is needed to support entering Unicode
// characters directly from the keyboard (bypassing the IME).
// Special thanks go to Francisco Leong for developing this solution.
//
// Example:
// 1. Install "Turkic" language support.
// 2. Add "Azeri (Latin)" as an input locale.
// 3. In an EDIT, enter Shift+I. (You should see a capital "I" with dot.)
// 4. In an EDIT, enter single quote (US Keyboard). (You should see an upturned "e".)
//
var
ManualPeekMessageWithRemove: Integer = 0;
procedure EnableManualPeekMessageWithRemove;
begin
Inc(ManualPeekMessageWithRemove);
end;
procedure DisableManualPeekMessageWithRemove;
begin
if (ManualPeekMessageWithRemove > 0) then
Dec(ManualPeekMessageWithRemove);
end;
var
NTGetMessageHook: HHOOK;
function GetMessageForNT(Code: Integer; wParam: Integer; lParam: Integer): LRESULT; stdcall;
var
ThisMsg: PMSG;
begin
if (Code >= 0)
and (wParam = PM_REMOVE)
and (ManualPeekMessageWithRemove = 0) then
begin
ThisMsg := PMSG(lParam);
if (TntApplication <> nil)
and TntApplication.ProcessMessage(ThisMsg^) then
ThisMsg.message := WM_NULL; { clear for further processing }
end;
Result := CallNextHookEx(NTGetMessageHook, Code, wParam, lParam);
end;
procedure CreateGetMessageHookForNT;
begin
Assert(Win32Platform = VER_PLATFORM_WIN32_NT);
NTGetMessageHook := SetWindowsHookExW(WH_GETMESSAGE, GetMessageForNT, 0, GetCurrentThreadID);
if NTGetMessageHook = 0 then
RaiseLastOSError;
end;
//---------------------------------------------------------------------------------------------
// Tnt Environment Setup
//---------------------------------------------------------------------------------------------
procedure InitTntEnvironment;
function GetDefaultFont: WideString;
function RunningUnderIDE: Boolean;
begin
Result := ModuleIsPackage and
( WideSameText(WideExtractFileName(WideGetModuleFileName(0)), 'bds.exe')
or WideSameText(WideExtractFileName(WideGetModuleFileName(0)), 'delphi32.exe')
or WideSameText(WideExtractFileName(WideGetModuleFileName(0)), 'bcb.exe'));
end;
function GetProfileStr(const Section, Key, Default: AnsiString; MaxLen: Integer): AnsiString;
var
Len: Integer;
begin
SetLength(Result, MaxLen + 1);
Len := GetProfileString(PAnsiChar(Section), PAnsiChar(Key), PAnsiChar(Default),
PAnsiChar(Result), Length(Result));
SetLength(Result, Len);
end;
procedure SetProfileStr(const Section, Key, Value: AnsiString);
var
DummyResult: Cardinal;
begin
try
Win32Check(WriteProfileString(PAnsiChar(Section), PAnsiChar(Key), PAnsiChar(Value)));
if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
WriteProfileString(nil, nil, nil); {this flushes the WIN.INI cache}
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, Integer(PAnsiChar(Section)),
SMTO_NORMAL, 250, DummyResult);
except
on E: Exception do begin
E.Message := 'Couldn''t create font substitutes.' + CRLF + E.Message;
Application.HandleException(nil);
end;
end;
end;
var
ShellDlgFontName_1: WideString;
ShellDlgFontName_2: WideString;
begin
ShellDlgFontName_1 := GetProfileStr('FontSubstitutes', 'MS Shell Dlg', '', LF_FACESIZE);
if ShellDlgFontName_1 = '' then begin
ShellDlgFontName_1 := 'MS Sans Serif';
SetProfileStr('FontSubstitutes', 'MS Shell Dlg', ShellDlgFontName_1);
end;
ShellDlgFontName_2 := GetProfileStr('FontSubstitutes', 'MS Shell Dlg 2', '', LF_FACESIZE);
if ShellDlgFontName_2 = '' then begin
if Screen.Fonts.IndexOf('Tahoma') <> -1 then
ShellDlgFontName_2 := 'Tahoma'
else
ShellDlgFontName_2 := ShellDlgFontName_1;
SetProfileStr('FontSubstitutes', 'MS Shell Dlg 2', ShellDlgFontName_2);
end;
if RunningUnderIDE then begin
Result := 'MS Shell Dlg 2' {Delphi is running}
end else
Result := ShellDlgFontName_2;
end;
begin
// Tnt Environment Setup
InstallTntSystemUpdates;
DefFontData.Name := GetDefaultFont;
Forms.HintWindowClass := TntControls.TTntHintWindow;
end;
initialization
TntApplication := TTntApplication.Create(nil);
if Win32Platform = VER_PLATFORM_WIN32_NT then
CreateGetMessageHookForNT;
finalization
if NTGetMessageHook <> 0 then begin
UnhookWindowsHookEx(NTGetMessageHook) // no Win32Check, fails in too many cases, and doesn't matter
end;
FreeAndNil(TntApplication);
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -