summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraeme Geldenhuys <graeme@mastermaths.co.za>2012-10-26 16:57:47 +0100
committerGraeme Geldenhuys <graeme@mastermaths.co.za>2012-10-26 17:03:38 +0100
commit1e8c032fb7e7d6712c161a34965a6ccdd89371b1 (patch)
tree0f9b947203f6a257201992521dee642ec406c2e4
parent4042c15028970e1d40ce3a06943a9fc4dbc3c0e2 (diff)
downloadfpGUI-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.pas41
-rw-r--r--src/corelib/gdi/fpg_interface.pas1
-rw-r--r--src/corelib/gdi/fpgui_toolkit.lpk6
-rw-r--r--src/corelib/gdi/fpgui_toolkit.pas2
-rw-r--r--src/corelib/x11/fpg_interface.pas1
-rw-r--r--src/corelib/x11/fpg_x11.pas93
-rw-r--r--src/corelib/x11/fpgui_toolkit.lpk7
-rw-r--r--src/corelib/x11/fpgui_toolkit.pas2
-rw-r--r--src/gui/fpg_trayicon.pas166
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.
+