From 1e8c032fb7e7d6712c161a34965a6ccdd89371b1 Mon Sep 17 00:00:00 2001 From: Graeme Geldenhuys Date: Fri, 26 Oct 2012 16:57:47 +0100 Subject: 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. --- src/corelib/gdi/fpg_gdi.pas | 41 +++++++++- src/corelib/gdi/fpg_interface.pas | 1 + src/corelib/gdi/fpgui_toolkit.lpk | 6 +- src/corelib/gdi/fpgui_toolkit.pas | 2 +- src/corelib/x11/fpg_interface.pas | 1 + src/corelib/x11/fpg_x11.pas | 93 +++++++++++++++++++++ src/corelib/x11/fpgui_toolkit.lpk | 7 +- src/corelib/x11/fpgui_toolkit.pas | 2 +- src/gui/fpg_trayicon.pas | 166 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 312 insertions(+), 7 deletions(-) create mode 100644 src/gui/fpg_trayicon.pas 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 @@ - + @@ -417,6 +417,10 @@ + + + + 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 @@ - @@ -30,7 +29,7 @@ - + @@ -419,6 +418,10 @@ + + + + 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. + -- cgit v1.2.3-70-g09d2