diff -ur lazarus.orig/lcl/interfaces/lcl.lpk lazarus/lcl/interfaces/lcl.lpk --- lazarus.orig/lcl/interfaces/lcl.lpk 2014-06-14 09:36:52.000000000 +0400 +++ lazarus/lcl/interfaces/lcl.lpk 2014-09-02 17:58:51.000000000 +0400 @@ -129,7 +129,7 @@ <License Value="modified LGPL-2 "/> <Version Major="1" Minor="2" Release="4"/> - <Files Count="429"> + <Files Count="430"> <Item1> <Filename Value="carbon/agl.pp"/> <AddToUsesPkgSection Value="False"/> @@ -2134,6 +2134,11 @@ <AddToUsesPkgSection Value="False"/> <UnitName Value="LazPangoCairo1"/> </Item429> + <Item430> + <Filename Value="qt/qtsystemtrayicon.pas"/> + <AddToUsesPkgSection Value="False"/> + <UnitName Value="qtsystemtrayicon"/> + </Item430> </Files> <LazDoc Paths="../../docs/xml/lcl"/> <i18n> diff -ur lazarus.orig/lcl/interfaces/qt/qtint.pp lazarus/lcl/interfaces/qt/qtint.pp --- lazarus.orig/lcl/interfaces/qt/qtint.pp 2014-05-09 18:33:04.000000000 +0400 +++ lazarus/lcl/interfaces/qt/qtint.pp 2014-09-02 17:57:54.000000000 +0400 @@ -65,6 +65,7 @@ SavedHandlesList: TMap; FSocketEventMap: TMap; StayOnTopList: TMap; + SysTrayIconsList: TFPList; // global hooks FAppEvenFilterHook: QObject_hookH; FAppFocusChangedHook: QApplication_hookH; @@ -156,6 +157,11 @@ procedure AddHandle(AHandle: TObject); procedure RemoveHandle(AHandle: TObject); function IsValidHandle(AHandle: HWND): Boolean; + + // qt systray icons map + procedure RegisterSysTrayIcon(AHandle: TObject); + procedure UnRegisterSysTrayIcon(AHandle: TObject); + function IsValidSysTrayIcon(AHandle: HWND): Boolean; {$IFDEF HASX11} // qt hints handles map (needed on X11 only) @@ -311,7 +317,7 @@ //////////////////////////////////////////////////// Graphics, buttons, Menus, // Bindings - qtprivate, qtwidgets, qtobjects; + qtprivate, qtwidgets, qtobjects, qtsystemtrayicon; function DTFlagsToQtFlags(const Flags: Cardinal): Integer; begin diff -ur lazarus.orig/lcl/interfaces/qt/qtobject.inc lazarus/lcl/interfaces/qt/qtobject.inc --- lazarus.orig/lcl/interfaces/qt/qtobject.inc 2014-05-07 19:56:22.000000000 +0400 +++ lazarus/lcl/interfaces/qt/qtobject.inc 2014-09-02 17:54:19.000000000 +0400 @@ -91,6 +91,7 @@ System.InitCriticalSection(CriticalSection); SavedHandlesList := TMap.Create(TMapIdType(ituPtrSize), SizeOf(TObject)); FSocketEventMap := TMap.Create(TMapIdType(its4), SizeOf(Pointer)); + SysTrayIconsList := TFPList.Create; StayOnTopList := nil; FAppActive := False; {$IFDEF HASX11} @@ -158,6 +159,13 @@ SavedHintHandlesList := nil; end; {$ENDIF} + + if SysTrayIconsList <> nil then + begin + SysTrayIconsList.Free; + SysTrayIconsList := nil; + end; + FSocketEventMap.Free; FGlobalActions.Free; @@ -547,6 +555,7 @@ ASequence: QKeySequenceH; AKey: WideString; AParent: QWidgetH; + R: TRect; function IsAnyWindowActive: Boolean; begin @@ -555,8 +564,101 @@ (QApplication_activePopupWidget() <> nil); end; + function IsSystemTrayWidget(AEventType: Cardinal): boolean; + var + AName: WideString; + AWidget: QWidgetH; + RGeom: TRect; + AFlags: QtWindowFlags; + i: Integer; + begin + Result := False; + if QObject_isWidgetType(Sender) then + begin + AWidget := QWidgetH(Sender); + QObject_objectName(Sender, @AName); + if UTF8Copy(AName, 1, 16) = 'qtlclsystrayicon' then + begin + for i := 0 to SysTrayIconsList.Count - 1 do + begin + RGeom := TQtSystemTrayIcon(SysTrayIconsList.Items[i]).GetGeometry; + if TQtSystemTrayIcon(SysTrayIconsList.Items[i]).SysTrayWidget = nil then + begin + if QApplication_widgetAt(RGeom.Left, RGeom.Top) = AWidget then + TQtSystemTrayIcon(SysTrayIconsList.Items[i]).AttachSysTrayWidget(AWidget); + end; + end; + exit(True); + end; + if QWidget_isWindow(AWidget) and (QWidget_parentWidget(AWidget) = nil) then + begin + AFlags := QWidget_windowFlags(AWidget); + if QWidget_testAttribute(AWidget, QtWA_AlwaysShowToolTips) and + QWidget_testAttribute(AWidget, QtWA_PaintOnScreen) and + QWidget_testAttribute(AWidget, QtWA_NoSystemBackground) and + not QWidget_testAttribute(AWidget, QtWA_QuitOnClose) and + (AFlags and QtFramelessWindowHint = QtFramelessWindowHint) and + (AFlags and QtX11BypassWindowManagerHint = QtX11BypassWindowManagerHint) then + begin + if HwndFromWidgetH(AWidget) = 0 then + begin + // we must find it by geometry, but it's innacurate since + // qt systrayicon widget returns -1,-1 for left & top, so we + // use QApplication_widgetAt(). + // Another problem is that QSystemTrayIcon geometry is updated + // too late, much after QEventShow/QEventShowToParent + // so no way to catch private QWidget until we enter + // it by mouse. + for i := 0 to SysTrayIconsList.Count - 1 do + begin + RGeom := TQtSystemTrayIcon(SysTrayIconsList.Items[i]).GetGeometry; + if QApplication_widgetAt(RGeom.Left, RGeom.Top) = AWidget then + begin + AName := 'qtlclsystrayicon_' + dbgHex(PtrUInt(AWidget)); + QObject_setObjectName(Sender, @AName); + TQtSystemTrayIcon(SysTrayIconsList.Items[i]).AttachSysTrayWidget(AWidget); + {$IFDEF DEBUGSYSTRAYICON} + DebugLn('Attached systemtrayicon[',dbgs(I),'] with geometry ',dbgs(RGeom),' dbg=', + dbgsName(TQtSystemTrayIcon(SysTrayIconsList.Items[i]).FTrayIcon)); + {$ENDIF} + TQtSystemTrayIcon(SysTrayIconsList.Items[i]).UpdateSystemTrayWidget; + Result := True; + break; + end; + end; + end; + end; + end; + end; + end; + begin Result := False; + + // find QSystemTrayIcon + if QObject_isWidgetType(Sender) and (QObject_parent(Sender) = nil) and + QWidget_isWindow(QWidgetH(Sender)) and + (QWidget_focusPolicy(QWidgetH(Sender)) = QtNoFocus) then + begin + AParent := QWidgetH(Sender); + QWidget_frameGeometry(AParent, @R); + if (R.Left = -1) and (R.Top = -1) and (R.Right > 0) and (R.Bottom > 0) then + begin + {$IFDEF DEBUGSYSTRAYICON} + DebugLn('EVENT: ',dbgs(QEvent_type(Event)),' Sender 0x',dbgHex(PtrUInt(Sender)),' geometry ',dbgs(R)); + {$ENDIF} + if (QEvent_type(Event) = QEventShowToParent) or (QEvent_type(Event) = QEventEnter) then + begin + if IsSystemTrayWidget(QEvent_type(Event)) then + begin + {$IFDEF DEBUGSYSTRAYICON} + DebugLn('Found SystemTrayIcon via event ',dbgs(QEvent_type(Event)),' SYSTRAYICON 0x',dbgHex(PtrUInt(Sender))); + {$ENDIF} + end; + end; + end; + end; + case QEvent_type(Event) of QEventShortcutOverride: // issue #22827 begin @@ -1140,6 +1242,21 @@ System.LeaveCriticalsection(CriticalSection); end; +procedure TQtWidgetSet.RegisterSysTrayIcon(AHandle: TObject); +begin + SysTrayIconsList.Add(AHandle); +end; + +procedure TQtWidgetSet.UnRegisterSysTrayIcon(AHandle: TObject); +begin + SysTrayIconsList.Remove(AHandle); +end; + +function TQtWidgetSet.IsValidSysTrayIcon(AHandle: HWND): Boolean; +begin + Result := SysTrayIconsList.IndexOf(TObject(AHandle)) >= 0; +end; + procedure TQtWidgetSet.RemoveHandle(AHandle: TObject); begin System.EnterCriticalsection(CriticalSection); diff -ur lazarus.orig/lcl/interfaces/qt/qtobjects.pas lazarus/lcl/interfaces/qt/qtobjects.pas --- lazarus.orig/lcl/interfaces/qt/qtobjects.pas 2014-05-13 01:17:16.000000000 +0400 +++ lazarus/lcl/interfaces/qt/qtobjects.pas 2014-09-02 17:50:35.000000000 +0400 @@ -520,28 +520,6 @@ property Handle: QCursorH read FHandle; end; - { TQtSystemTrayIcon } - - TQtSystemTrayIcon = class(TObject) - private - FHook: QSystemTrayIcon_hookH; - public - Handle: QSystemTrayIconH; - FTrayIcon: TCustomTrayIcon; - public - constructor Create(vIcon: QIconH); virtual; - destructor Destroy; override; - public - procedure setContextMenu(menu: QMenuH); - procedure setIcon(icon: QIconH); - procedure setToolTip(tip: WideString); - procedure signalActivated(AReason: QSystemTrayIconActivationReason); cdecl; - procedure showBaloonHint(const ATitle, AHint: String; - const AFlag: QSystemTrayIconMessageIcon; const ATimeOut: Integer); - procedure Show; - procedure Hide; - end; - { TQtButtonGroup } TQtButtonGroup = class(TObject) @@ -3690,119 +3668,6 @@ QPixmap_fromImage(retval, image, flags); end; -{ TQtSystemTrayIcon } - -constructor TQtSystemTrayIcon.Create(vIcon: QIconH); -begin - inherited Create; - - if vIcon <> nil then - Handle := QSystemTrayIcon_create(vicon, nil) - else - Handle := QSystemTrayIcon_create(); - FHook := QSystemTrayIcon_hook_create(Handle); - QSystemTrayIcon_hook_hook_activated(FHook, @signalActivated); -end; - -destructor TQtSystemTrayIcon.Destroy; -begin - QSystemTrayIcon_hook_destroy(FHook); - QSystemTrayIcon_destroy(Handle); - - inherited Destroy; -end; - -procedure TQtSystemTrayIcon.setContextMenu(menu: QMenuH); -begin - QSystemTrayIcon_setContextMenu(handle, menu); -end; - -procedure TQtSystemTrayIcon.setIcon(icon: QIconH); -begin - QSystemTrayIcon_setIcon(handle, icon); -end; - -procedure TQtSystemTrayIcon.setToolTip(tip: WideString); -begin - QSystemTrayIcon_setToolTip(handle, @tip) -end; - -procedure TQtSystemTrayIcon.signalActivated( - AReason: QSystemTrayIconActivationReason); cdecl; -var - MousePos: TQtPoint; -begin - if not Assigned(FTrayIcon) then - exit; - - QCursor_pos(@MousePos); - {$note: TODO: Mouse events of trayicon can be catched - in QApplication event filter (TQtWidgetSet.EventFilter), - so OnMouseDown and OnMouseUp can be properly sent. - Check if it works ok on qtwin32 and qtmac and - then replace this blind calls to mouse events. - To get systryicon object handle in application event filter - add property "lclsystrayicon" to this handle.} - case AReason of - QSystemTrayIconTrigger: - begin - if Assigned(FTrayIcon.OnMouseDown) then - FTrayIcon.OnMouseDown(FTrayIcon, mbLeft, [], MousePos.x, MousePos.y); - if Assigned(FTrayIcon.OnClick) then - FTrayIcon.OnClick(FTrayIcon); - if Assigned(FTrayIcon.OnMouseUp) then - FTrayIcon.OnMouseUp(FTrayIcon, mbLeft, [], MousePos.x, MousePos.y); - end; - QSystemTrayIconDoubleClick: - begin - if Assigned(FTrayIcon.OnMouseDown) then - FTrayIcon.OnMouseDown(FTrayIcon, mbLeft, [], MousePos.x, MousePos.y); - - if Assigned(FTrayIcon.OnDblClick) then - FTrayIcon.OnDblClick(FTrayIcon); - - if Assigned(FTrayIcon.OnMouseUp) then - FTrayIcon.OnMouseUp(FTrayIcon, mbLeft, [], MousePos.x, MousePos.y); - end; - QSystemTrayIconMiddleClick: - begin - if Assigned(FTrayIcon.OnMouseDown) then - FTrayIcon.OnMouseDown(FTrayIcon, mbMiddle, [], MousePos.x, MousePos.y); - if Assigned(FTrayIcon.OnMouseUp) then - FTrayIcon.OnMouseUp(FTrayIcon, mbMiddle, [], MousePos.x, MousePos.y); - end; - QSystemTrayIconContext: - begin - if Assigned(FTrayIcon.OnMouseDown) then - FTrayIcon.OnMouseDown(FTrayIcon, mbRight, [], MousePos.x, MousePos.y); - - if Assigned(FTrayIcon.OnMouseUp) then - FTrayIcon.OnMouseUp(FTrayIcon, mbRight, [], MousePos.x, MousePos.y); - end; - end; -end; - -procedure TQtSystemTrayIcon.showBaloonHint(const ATitle, AHint: String; - const AFlag: QSystemTrayIconMessageIcon; const ATimeOut: Integer); -var - WHint: WideString; - WTitle: WideString; -begin - WHint := GetUTF8String(AHint); - WTitle := GetUTF8String(ATitle); - QSystemTrayIcon_showMessage(Handle, @WTitle, @WHint, AFlag, ATimeOut); -end; - -procedure TQtSystemTrayIcon.Show; -begin - QSystemTrayIcon_show(handle); -end; - -procedure TQtSystemTrayIcon.Hide; -begin - QSystemTrayIcon_hide(handle); -end; - { TQtButtonGroup } constructor TQtButtonGroup.Create(AParent: QObjectH); diff -ur lazarus.orig/lcl/interfaces/qt/qtwsextctrls.pp lazarus/lcl/interfaces/qt/qtwsextctrls.pp --- lazarus.orig/lcl/interfaces/qt/qtwsextctrls.pp 2013-09-06 10:16:53.000000000 +0400 +++ lazarus/lcl/interfaces/qt/qtwsextctrls.pp 2014-09-02 17:56:37.000000000 +0400 @@ -25,7 +25,7 @@ uses // Bindings qt4, - qtwidgets, qtobjects, qtproc, QtWSControls, + qtwidgets, qtobjects, qtsystemtrayicon, qtproc, QtWSControls, // LCL LCLProc, SysUtils, Classes, Controls, Graphics, Forms, ExtCtrls, LCLType, @@ -152,6 +152,7 @@ class procedure InternalUpdate(const ATrayIcon: TCustomTrayIcon); override; class function ShowBalloonHint(const ATrayIcon: TCustomTrayIcon): Boolean; override; class function GetPosition(const ATrayIcon: TCustomTrayIcon): TPoint; override; + class function GetCanvas(const ATrayIcon: TCustomTrayIcon): TCanvas; override; end; implementation @@ -288,6 +289,8 @@ if TQtMenu(ATrayIcon.PopUpMenu.Handle).Widget <> nil then SystemTrayIcon.setContextMenu(QMenuH(TQtMenu(ATrayIcon.PopUpMenu.Handle).Widget)); + SystemTrayIcon.UpdateSystemTrayWidget; + SystemTrayIcon.show; Result := True; @@ -355,6 +358,17 @@ class function TQtWSCustomTrayIcon.GetPosition(const ATrayIcon: TCustomTrayIcon): TPoint; begin Result := Point(0, 0); + if (ATrayIcon.Handle = 0) then + exit; + Result := TQtSystemTrayIcon(ATrayIcon.Handle).GetPosition; +end; + +class function TQtWSCustomTrayIcon.GetCanvas(const ATrayIcon: TCustomTrayIcon + ): TCanvas; +begin + Result := nil; + if (ATrayIcon.Handle <> 0) then + Result := TQtSystemTrayIcon(ATrayIcon.Handle).Canvas; end; end.