diff options
author | Graeme Geldenhuys <graeme@mastermaths.co.za> | 2012-10-26 16:57:47 +0100 |
---|---|---|
committer | Graeme Geldenhuys <graeme@mastermaths.co.za> | 2012-10-26 17:03:38 +0100 |
commit | 1e8c032fb7e7d6712c161a34965a6ccdd89371b1 (patch) | |
tree | 0f9b947203f6a257201992521dee642ec406c2e4 | |
parent | 4042c15028970e1d40ce3a06943a9fc4dbc3c0e2 (diff) | |
download | fpGUI-1e8c032fb7e7d6712c161a34965a6ccdd89371b1.tar.xz |
Adds a System Tray Icon implementation for X11.
This has been a long awaited feature. There is still some functionality missing and some
more tweaks that need to be applied, but this implementation does work. It has been tested
under JWM (Joe's Window Manager), MATE (Gnome2 fork) and KDE 4.8.x
The Windows implementation will follow shortly.
-rw-r--r-- | src/corelib/gdi/fpg_gdi.pas | 41 | ||||
-rw-r--r-- | src/corelib/gdi/fpg_interface.pas | 1 | ||||
-rw-r--r-- | src/corelib/gdi/fpgui_toolkit.lpk | 6 | ||||
-rw-r--r-- | src/corelib/gdi/fpgui_toolkit.pas | 2 | ||||
-rw-r--r-- | src/corelib/x11/fpg_interface.pas | 1 | ||||
-rw-r--r-- | src/corelib/x11/fpg_x11.pas | 93 | ||||
-rw-r--r-- | src/corelib/x11/fpgui_toolkit.lpk | 7 | ||||
-rw-r--r-- | src/corelib/x11/fpgui_toolkit.pas | 2 | ||||
-rw-r--r-- | src/gui/fpg_trayicon.pas | 166 |
9 files changed, 312 insertions, 7 deletions
diff --git a/src/corelib/gdi/fpg_gdi.pas b/src/corelib/gdi/fpg_gdi.pas index 311a4fce..fec0c511 100644 --- a/src/corelib/gdi/fpg_gdi.pas +++ b/src/corelib/gdi/fpg_gdi.pas @@ -304,8 +304,6 @@ type end; - { TfpgGDITimer } - TfpgGDITimer = class(TfpgBaseTimer) private FHandle: THandle; @@ -316,6 +314,16 @@ type end; + TfpgGDISystemTrayIcon = class(TfpgComponent) + public + constructor Create(AOwner: TComponent); override; + procedure Show; + procedure Hide; + function IsSystemTrayAvailable: boolean; + function SupportsMessages: boolean; + end; + + implementation uses @@ -3105,6 +3113,35 @@ begin end; +{ TfpgGDISystemTrayIcon } + +constructor TfpgGDISystemTrayIcon.Create(AOwner: TComponent); +begin + inherited Create(AOwner); +end; + +procedure TfpgGDISystemTrayIcon.Show; +begin + // +end; + +procedure TfpgGDISystemTrayIcon.Hide; +begin + // +end; + +function TfpgGDISystemTrayIcon.IsSystemTrayAvailable: boolean; +begin + Result := False; +end; + +function TfpgGDISystemTrayIcon.SupportsMessages: boolean; +begin + Result := True; +end; + + + initialization wapplication := nil; MouseFocusedWH := 0; diff --git a/src/corelib/gdi/fpg_interface.pas b/src/corelib/gdi/fpg_interface.pas index 8833f44d..5d5d4833 100644 --- a/src/corelib/gdi/fpg_interface.pas +++ b/src/corelib/gdi/fpg_interface.pas @@ -36,6 +36,7 @@ type TfpgMimeDataImpl = class(TfpgGDIMimeDataBase); TfpgDragImpl = class(TfpgGDIDrag); TfpgTimerImpl = class(TfpgGDITimer); + TfpgSystemTrayHandler = class(TfpgGDISystemTrayIcon); implementation diff --git a/src/corelib/gdi/fpgui_toolkit.lpk b/src/corelib/gdi/fpgui_toolkit.lpk index a4f927c2..7654d0d4 100644 --- a/src/corelib/gdi/fpgui_toolkit.lpk +++ b/src/corelib/gdi/fpgui_toolkit.lpk @@ -32,7 +32,7 @@ <Description Value="fpGUI Toolkit"/>
<License Value="LGPL 2 with static linking exception."/>
<Version Minor="8"/>
- <Files Count="96">
+ <Files Count="97">
<Item1>
<Filename Value="..\stdimages.inc"/>
<Type Value="Include"/>
@@ -417,6 +417,10 @@ <Filename Value="..\..\reportengine\u_visu.pas"/>
<UnitName Value="U_Visu"/>
</Item96>
+ <Item97>
+ <Filename Value="..\..\gui\fpg_trayicon.pas"/>
+ <UnitName Value="fpg_trayicon"/>
+ </Item97>
</Files>
<LazDoc Paths="../../../docs/xml/corelib;../../../docs/xml/corelib/x11;../../../docs/xml/corelib/gdi;../../../docs/xml/gui"/>
<RequiredPkgs Count="1">
diff --git a/src/corelib/gdi/fpgui_toolkit.pas b/src/corelib/gdi/fpgui_toolkit.pas index 5caa6228..7496bb18 100644 --- a/src/corelib/gdi/fpgui_toolkit.pas +++ b/src/corelib/gdi/fpgui_toolkit.pas @@ -21,7 +21,7 @@ uses fpg_interface, fpg_editbtn, fpg_imgfmt_jpg, fpg_imgutils, fpg_stylemanager,
fpg_style_win2k, fpg_style_motif, fpg_style_clearlooks, fpg_style_bluecurve,
fpg_style_bitmap, fpg_readonly, fpg_imgfmt_png, U_Command, U_Pdf, U_Report,
- U_ReportImages, U_Visu;
+ U_ReportImages, U_Visu, fpg_trayicon;
implementation
diff --git a/src/corelib/x11/fpg_interface.pas b/src/corelib/x11/fpg_interface.pas index 6f216179..aa30039f 100644 --- a/src/corelib/x11/fpg_interface.pas +++ b/src/corelib/x11/fpg_interface.pas @@ -36,6 +36,7 @@ type TfpgMimeDataImpl = class(TfpgX11MimeData); TfpgDragImpl = class(TfpgX11Drag); TfpgTimerImpl = class(TfpgX11Timer); + TfpgSystemTrayHandler = class(TfpgX11SystemTrayHandler); implementation diff --git a/src/corelib/x11/fpg_x11.pas b/src/corelib/x11/fpg_x11.pas index d1e83d93..046b2f74 100644 --- a/src/corelib/x11/fpg_x11.pas +++ b/src/corelib/x11/fpg_x11.pas @@ -118,6 +118,10 @@ const MWM_INPUT_SYSTEM_MODAL = 2; MWM_INPUT_FULL_APPLICATION_MODAL = 3; PROP_MWM_HINTS_ELEMENTS = 5; +// System Tray message opcodes + SYSTEM_TRAY_REQUEST_DOCK = 0; + SYSTEM_TRAY_BEGIN_MESSAGE = 1; + SYSTEM_TRAY_CANCEL_MESSAGE = 2; type TXWindowStateFlag = (xwsfMapped); @@ -392,6 +396,23 @@ type end; + TfpgX11SystemTrayHandler = class(TfpgComponent) + private + FTrayIconParent: TWindow; + FTrayWidget: TfpgWindowBase; + function GetTrayIconParent: TWindow; + function GetSysTrayWindow: TWindow; + function Send_Message(dest: TWindow; msg: longword; data1, data2, data3: longword): boolean; + property TrayIconParent: TWindow read GetTrayIconParent; + public + constructor Create(AOwner: TComponent); override; + procedure Show; + procedure Hide; + function IsSystemTrayAvailable: boolean; + function SupportsMessages: boolean; + end; + + function fpgColorToX(col: TfpgColor): longword; @@ -3674,6 +3695,78 @@ begin end; end; +{ TfpgX11SystemTrayHandler } + +function TfpgX11SystemTrayHandler.GetTrayIconParent: TWindow; +begin + if FTrayIconParent = None then + FTrayIconParent := GetSysTrayWindow; + Result := FTrayIconParent; +end; + +function TfpgX11SystemTrayHandler.GetSysTrayWindow: TWindow; +var + buf: array[0..32] of char; + selection_atom: TAtom; +begin + XGrabServer(xapplication.Display); + + buf := PChar('_NET_SYSTEM_TRAY_S' + IntToStr(xapplication.DefaultScreen)); + selection_atom := XInternAtom(xapplication.Display, buf, false); + Result := XGetSelectionOwner(xapplication.Display, selection_atom); + + XUngrabServer(xapplication.Display); +end; + +function TfpgX11SystemTrayHandler.Send_Message(dest: TWindow; msg: longword; data1, data2, data3: longword): boolean; +var + ev: TXEvent; +begin + FillChar(ev, SizeOf(TXEvent), 0); + + ev.xclient._type := ClientMessage; + ev.xclient.window := dest; { sender (tray icon window) } + ev.xclient.message_type := XInternAtom(xapplication.Display, '_NET_SYSTEM_TRAY_OPCODE', False ); + ev.xclient.format := 32; + + ev.xclient.data.l[0] := CurrentTime; + ev.xclient.data.l[1] := msg; { message opcode } + ev.xclient.data.l[2] := data1; + ev.xclient.data.l[3] := data2; + ev.xclient.data.l[4] := data3; + + Result := XSendEvent(xapplication.Display, TrayIconParent, False, NoEventMask, @ev) <> 0; + XSync(xapplication.Display, False); +end; + +procedure TfpgX11SystemTrayHandler.Show; +begin + Send_Message(TrayIconParent, SYSTEM_TRAY_REQUEST_DOCK, TfpgX11Window(Owner).WinHandle, 0, 0); +end; + +procedure TfpgX11SystemTrayHandler.Hide; +begin + TfpgX11Window(FTrayWidget).DoSetWindowVisible(False); +end; + +constructor TfpgX11SystemTrayHandler.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FTrayWidget := AOwner as TfpgWindowBase; + FTrayIconParent := None; +end; + +function TfpgX11SystemTrayHandler.IsSystemTrayAvailable: boolean; +begin + Result := GetSysTrayWindow <> None; +end; + +function TfpgX11SystemTrayHandler.SupportsMessages: boolean; +begin + Result := True; +end; + + initialization xapplication := nil; diff --git a/src/corelib/x11/fpgui_toolkit.lpk b/src/corelib/x11/fpgui_toolkit.lpk index dd6750db..5e61d76c 100644 --- a/src/corelib/x11/fpgui_toolkit.lpk +++ b/src/corelib/x11/fpgui_toolkit.lpk @@ -2,7 +2,6 @@ <CONFIG> <Package Version="4"> <Name Value="fpgui_toolkit"/> - <AddToProjectUsesSection Value="False"/> <Author Value="Graeme Geldenhuys"/> <CompilerOptions> <Version Value="11"/> @@ -30,7 +29,7 @@ <Description Value="fpGUI Toolkit"/> <License Value="LGPL 2 with static linking exception."/> <Version Minor="8"/> - <Files Count="97"> + <Files Count="98"> <Item1> <Filename Value="../stdimages.inc"/> <Type Value="Include"/> @@ -419,6 +418,10 @@ <Filename Value="../../reportengine/u_visu.pas"/> <UnitName Value="U_Visu"/> </Item97> + <Item98> + <Filename Value="../../gui/fpg_trayicon.pas"/> + <UnitName Value="fpg_trayicon"/> + </Item98> </Files> <LazDoc Paths="../../../docs/xml/corelib;../../../docs/xml/corelib/x11;../../../docs/xml/corelib/gdi;../../../docs/xml/gui"/> <RequiredPkgs Count="1"> diff --git a/src/corelib/x11/fpgui_toolkit.pas b/src/corelib/x11/fpgui_toolkit.pas index 1f957d56..a9fcc500 100644 --- a/src/corelib/x11/fpgui_toolkit.pas +++ b/src/corelib/x11/fpgui_toolkit.pas @@ -20,7 +20,7 @@ uses fpg_interface, fpg_editbtn, fpg_imgfmt_jpg, fpg_imgutils, fpg_stylemanager, fpg_style_win2k, fpg_style_motif, fpg_style_clearlooks, fpg_style_bluecurve, fpg_style_bitmap, fpg_readonly, fpg_imgfmt_png, U_Command, U_Pdf, U_Report, - U_ReportImages, U_Visu; + U_ReportImages, U_Visu, fpg_trayicon; implementation diff --git a/src/gui/fpg_trayicon.pas b/src/gui/fpg_trayicon.pas new file mode 100644 index 00000000..b439f085 --- /dev/null +++ b/src/gui/fpg_trayicon.pas @@ -0,0 +1,166 @@ +{ + fpGUI - Free Pascal GUI Toolkit + + Copyright (C) 2006 - 2012 See the file AUTHORS.txt, included in this + distribution, for details of the copyright. + + See the file COPYING.modifiedLGPL, included in this distribution, + for details about redistributing fpGUI. + + 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. + + Description: + This unit implements a class that provides an icon for an application + in the system tray. +} +unit fpg_trayicon; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, + SysUtils, + fpg_base, + fpg_main, + fpg_widget, + fpg_menu, + fpg_interface; + +type + + TfpgMessageIconType = (mitNoIcon, mitInformation, mitWarning, mitCritical); + + + TfpgSystemTrayIcon = class(TfpgWidget) + private + FPopupMenu: TfpgPopupMenu; + FImageName: TfpgString; + FImage: TfpgImage; + FOnMessageClicked: TNotifyEvent; + procedure SetImageName(AValue: TfpgString); + protected + FSysTrayHandler: TfpgSystemTrayHandler; + procedure SetVisible(const AValue: boolean); override; + procedure HandlePaint; override; + procedure HandleRMouseUp(x, y: integer; shiftstate: TShiftState); override; + public + constructor Create(AOwner: TComponent); override; + function IsSystemTrayAvailable: boolean; + function SupportsMessages: boolean; + procedure Show; + procedure Hide; + procedure ShowMessage(const ATitle: TfpgString; const AMessage: TfpgString; const AMessageIcon: TfpgMessageIconType; const AMillisecondsTimeoutHint: Word = 10000); + published + property BackgroundColor; + property Hint; + property ImageName: TfpgString read FImageName write SetImageName; + property PopupMenu: TfpgPopupMenu read FPopupMenu write FPopupMenu; + property ShowHint; + property OnClick; + property OnMessageClicked: TNotifyEvent read FOnMessageClicked write FOnMessageClicked; + property OnPaint; + end; + + +implementation + +{ TfpgSystemTrayIcon } + +procedure TfpgSystemTrayIcon.SetImageName(AValue: TfpgString); +begin + if FImageName = AValue then + Exit; + FImageName := AValue; + Repaint; +end; + +procedure TfpgSystemTrayIcon.SetVisible(const AValue: boolean); +begin + if FVisible = AValue then + Exit; + FVisible := AValue; + if FVisible then + Show + else + Hide; +end; + +procedure TfpgSystemTrayIcon.HandlePaint; +var + img: TfpgImage; +begin + inherited HandlePaint; + Canvas.Clear(FBackgroundColor); + if FImageName <> '' then + begin + { request a reference to already loaded image } + img := fpgImages.GetImage(FImageName); + if Assigned(img) then + begin + FCanvas.DrawImage((Width-img.Width) div 2, (Height-img.Height) div 2, img); + end; + end; +end; + +procedure TfpgSystemTrayIcon.HandleRMouseUp(x, y: integer; shiftstate: TShiftState); +begin + inherited HandleRMouseUp(x, y, shiftstate); + if Assigned(FPopupMenu) then + begin + FPopupMenu.ShowAt(self, x, y, True); + end; +end; + +constructor TfpgSystemTrayIcon.Create(AOwner: TComponent); +begin + inherited Create(AOwner); + FWidth := 20; + FHeight := 20; + FVisible := False; + + FHint := ''; + FImage := nil; + FImageName := ''; + FPopupMenu := nil; + + FSysTrayHandler := TfpgSystemTrayHandler.Create(self); +end; + +function TfpgSystemTrayIcon.IsSystemTrayAvailable: boolean; +begin + Result := FSysTrayHandler.IsSystemTrayAvailable; +end; + +function TfpgSystemTrayIcon.SupportsMessages: boolean; +begin + { TODO : As far as I know Mac OS X doesn't support this, though they do now + have this Notifications functionility since 10.8 } + Result := FSysTrayHandler.SupportsMessages; +end; + +procedure TfpgSystemTrayIcon.Show; +begin + FSysTrayHandler.Show; + FVisible := True; +end; + +procedure TfpgSystemTrayIcon.Hide; +begin + { TODO : TfpgSystemTrayIcon.Hide not implemented yet! } +// FVisible := False; +// FSysTrayHandler.Hide; +end; + +procedure TfpgSystemTrayIcon.ShowMessage(const ATitle: TfpgString; + const AMessage: TfpgString; const AMessageIcon: TfpgMessageIconType; + const AMillisecondsTimeoutHint: Word); +begin + { TODO : TfpgSystemTrayIcon.ShowMessage not implemented yet! } +end; + +end. + |