(* History++ plugin for Miranda IM: the free IM client for Microsoft* Windows* Copyright (C) 2006-2009 theMIROn, 2003-2006 Art Fedorov. History+ parts (C) 2001 Christian Kastner This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) unit hpp_forms; interface uses Windows, Messages, Classes, Graphics, Controls, Forms, Menus, ComCtrls, StdCtrls, Themes; type THppHintWindow = class(THintWindow{THintWindow}) private procedure CMInvalidate(var Message: TMessage); message CM_INVALIDATE; protected procedure NCPaint(DC: HDC); override; public constructor Create(AOwner: TComponent); override; end; procedure NotifyAllForms(Msg:UInt; wParam:WPARAM; lParam: LPARAM); procedure BringFormToFront(Form: TForm); procedure MakeFontsParent(Control: TControl); procedure MakeDoubleBufferedParent(Control: TWinControl); //procedure AddMenu(M: TMenuItem; FromM,ToM: TPopupMenu; Index: integer); procedure AddMenuArray(Menu: TPopupMenu; List: Array of TMenuItem; Index: integer); procedure TranslateMenu(mi: TMenuItem); procedure TranslateToolbar(const tb: TToolBar); function ShiftStateToKeyData(ShiftState :TShiftState):Longint; function IsFormShortCut(List: Array of TComponent; Key: Word; ShiftState: TShiftState): Boolean; function Utils_RestoreFormPosition(Form: TForm; hContact: THandle; const Module,Prefix: AnsiString): Boolean; function Utils_SaveFormPosition(Form: TForm; hContact: THandle; const Module,Prefix: AnsiString): Boolean; implementation uses hpp_services, hpp_opt_dialog, hpp_database, hpp_mescatcher, HistoryForm, GlobalSearch, m_api, {$IFNDEF NO_EXTERNALGRID}hpp_external,{$ENDIF} CustomizeFiltersForm, CustomizeToolbar; {procedure AddMenu(M: TMenuItem; FromM,ToM: TPopupMenu; Index: integer); //var // i: integer; // mi: TMenuItem; begin if ToM.FindItem(M.Handle,fkHandle) = nil then begin if FromM.FindItem(M.Handle,fkHandle) <> nil then FromM.Items.Remove(M); if Index = -1 then ToM.Items.Add(M) else ToM.Items.Insert(Index,M); end; end;} procedure AddMenuArray(Menu: TPopupMenu; List: Array of TMenuItem; Index: integer); var i: integer; begin for i := 0 to High(List) do begin if List[i].Parent <> nil then begin if List[i].GetParentMenu = Menu then continue; List[i].Parent.Remove(List[i]); end; if Index = -1 then Menu.Items.Add(List[i]) else Menu.Items.Insert(Index+i,List[i]); end; end; function IsFormShortCut(List: Array of TComponent; Key: Word; ShiftState: TShiftState): Boolean; var i: integer; mes: TWMKey; begin Result := False; if Key = 0 then exit; if Key = VK_INSERT then begin if ShiftState = [ssCtrl] then begin Key := Ord('C') end else if ShiftState = [ssShift] then begin Key := Ord('V'); ShiftState := [ssCtrl]; end; end; mes.CharCode := Key; mes.KeyData := ShiftStateToKeyData(ShiftState); for i := 0 to High(List) do begin if List[i] is TMenu then begin Result := TMenu(List[i]).IsShortCut(mes); end else if List[i] is TForm then begin Result := (TForm(List[i]).Menu <> nil) and (TForm(List[i]).Menu.WindowHandle <> 0) and (TForm(List[i]).Menu.IsShortCut(mes)); end; if Result then break; end; end; function ShiftStateToKeyData(ShiftState :TShiftState):Longint; const AltMask = $20000000; begin Result := 0; if ssShift in ShiftState then Result := Result or VK_SHIFT; if ssCtrl in ShiftState then Result := Result or VK_CONTROL; if ssAlt in ShiftState then Result := Result or AltMask; end; function Utils_RestoreFormPosition(Form: TForm; hContact: THandle; const Module,Prefix: AnsiString): Boolean; var w,h,l,t,mon: Integer; maximized: Boolean; deltaW, deltaH: integer; begin Result := True; deltaW := Form.Width - Form.ClientWidth; deltaH := Form.Height - Form.ClientHeight; mon := GetDBWord(Module,Prefix+'monitor',Form.Monitor.MonitorNum); if mon >= Screen.MonitorCount then mon := Form.Monitor.MonitorNum; w := GetDBWord(Module,Prefix+'width',Form.ClientWidth) + deltaW; h := GetDBWord(Module,Prefix+'height',Form.ClientHeight) + deltaH; l := GetDBInt(Module,Prefix+'x',Screen.Monitors[mon].Left+((Screen.Monitors[mon].Width-w) div 2)); t := GetDBInt(Module,Prefix+'y',Screen.Monitors[mon].Top+((Screen.Monitors[mon].Height-h) div 2)); maximized := GetDBBool(Module,Prefix+'maximized',False); // just to be safe, don't let window jump out of the screen // at least 100 px from each side should be visible if l+100 > Screen.DesktopWidth then l := Screen.DesktopWidth-100; if t+100 > Screen.DesktopHeight then t := Screen.DesktopHeight-100; if l+w < 100 then l := 100-w; if t+h < 100 then t := 100-h; Form.SetBounds(l,t,w,h); if maximized then Form.WindowState := wsMaximized; end; function Utils_SaveFormPosition(Form: TForm; hContact: THandle; const Module,Prefix: AnsiString): Boolean; var w,h,l,t: Integer; wp: TWindowPlacement; maximized: Boolean; begin Result := True; maximized := (Form.WindowState = wsMaximized); if maximized then begin wp.length := SizeOf(TWindowPlacement); GetWindowPlacement(Form.Handle,@wp); l := wp.rcNormalPosition.Left; t := wp.rcNormalPosition.Top; w := wp.rcNormalPosition.Right - wp.rcNormalPosition.Left - (Form.Width - Form.ClientWidth); h := wp.rcNormalPosition.Bottom - wp.rcNormalPosition.Top - (Form.Height - Form.ClientHeight); end else begin l := Form.Left; t := Form.Top; w := Form.ClientWidth; h := Form.ClientHeight; end; WriteDBInt(Module,Prefix+'x',l); WriteDBInt(Module,Prefix+'y',t); WriteDBWord(Module,Prefix+'width',w); WriteDBWord(Module,Prefix+'height',h); WriteDBWord(Module,Prefix+'monitor',Form.Monitor.MonitorNum); WriteDBBool(Module,Prefix+'maximized',maximized); end; procedure BringFormToFront(Form: TForm); begin if Form.WindowState = wsMaximized then ShowWindow(Form.Handle,SW_SHOWMAXIMIZED) else ShowWindow(Form.Handle,SW_SHOWNORMAL); Form.BringToFront; end; procedure NotifyAllForms(Msg:UInt; wParam:WPARAM; lParam: LPARAM); var i: Integer; begin if hDlg <> 0 then SendMessage(hDlg,Msg,wParam,lParam); // we are going backwards here because history forms way want to // close themselves on the message, so we would have AVs if go from 0 to Count {$IFNDEF NO_EXTERNALGRID} ExternalGrids.Perform(Msg,wParam,lParam); {$ENDIF} for i := HstWindowList.Count - 1 downto 0 do begin if Assigned(THistoryFrm(HstWindowList[i]).EventDetailForm) then THistoryFrm(HstWindowList[i]).EventDetailForm.Perform(Msg,wParam,lParam); THistoryFrm(HstWindowList[i]).Perform(Msg,wParam,lParam); end; if Assigned(fmGlobalSearch) then fmGlobalSearch.Perform(Msg,wParam,lParam); if Assigned(fmCustomizeFilters) then fmCustomizeFilters.Perform(Msg,wParam,lParam); if Assigned(fmCustomizeToolbar) then fmCustomizeToolbar.Perform(Msg,wParam,lParam); end; // This procedure scans all control children and if they have // no ParentFont, sets ParentFont to true but reapplies font styles, // so even having parent font and size, controls remain bold or italic // // Of course it can be done cleaner and for all controls supporting fonts // property through TPropertyEditor and GetPropInfo, but then it would // need vcl sources to compile, so not a best alternative for open source plugin procedure MakeFontsParent(Control: TControl); var i: Integer; fs: TFontStyles; begin // Set TLabel & TLabel if (Control is TLabel) and (not TLabel(Control).ParentFont) then begin fs := TLabel(Control).Font.Style; TLabel(Control).ParentFont := True; TLabel(Control).Font.Style := fs; end; if (Control is TLabel) and (not TLabel(Control).ParentFont) then begin fs := TLabel(Control).Font.Style; TLabel(Control).ParentFont := True; TLabel(Control).Font.Style := fs; end; // Process children for i := 0 to Control.ComponentCount - 1 do begin if Control.Components[i] is TControl then begin MakeFontsParent(TControl(Control.Components[i])); end; end; end; { THppHintWindow } type THackHintWindow = class(TCustomControl) private FActivating: Boolean; end; constructor THppHintWindow.Create(AOwner: TComponent); begin inherited Create(AOwner); Color := clInfoBk; end; procedure THppHintWindow.CMInvalidate(var Message: TMessage); begin if (THackHintWindow(Self).FActivating) and (Application.Handle = 0) and (ParentWindow = 0) then begin ParentWindow := hppMainWindow; ShowWindow(Handle, SW_SHOWNOACTIVATE); end; inherited; end; procedure THppHintWindow.NCPaint(DC: HDC); var R: TRect; begin R := Rect(0, 0, Width, Height); DrawEdge(DC, R, EDGE_ETCHED, BF_RECT or BF_MONO); end; // This procedure scans all WinControl children and set them the same // DoubleBuffered property. procedure MakeDoubleBufferedParent(Control: TWinControl); var i: Integer; DoubleBuffered: Boolean; begin DoubleBuffered := Control.DoubleBuffered; for i := 0 to Control.ComponentCount - 1 do begin if not (Control.Components[i] is TCustomRichEdit) and (Control.Components[i] is TWinControl) then begin TWinControl(Control.Components[i]).DoubleBuffered := DoubleBuffered; MakeDoubleBufferedParent(TWinControl(Control.Components[i])); end; end; end; procedure TranslateMenu(mi: TMenuItem); var i: integer; begin for i := 0 to mi.Count-1 do if mi.Items[i].Caption <> '-' then begin TMenuItem(mi.Items[i]).Caption := TranslateUnicodeString(mi.Items[i].Caption{TRANSLATE-IGNORE}); if mi.Items[i].Count > 0 then TranslateMenu(mi.Items[i]); end; end; procedure TranslateToolbar(const tb: TToolBar); var i: integer; begin for i := 0 to tb.ButtonCount-1 do if tb.Buttons[i].Style <> tbsSeparator then begin TToolBar(tb.Buttons[i]).Hint := TranslateUnicodeString(tb.Buttons[i].Hint{TRANSLATE-IGNORE}); TToolBar(tb.Buttons[i]).Caption := TranslateUnicodeString(tb.Buttons[i].Caption{TRANSLATE-IGNORE}); end; end; initialization // init ThemeServices before widows open Themes.StyleServices; end.