Hello,

Now the TrayIcon component is fully working for Gtk2!!!

A patch is attached.

One little problem: The gtk2 tray icon requires gdk2x11, and this unit is on a package that needs to be installed. The Package cannot be added as a pre-requisite because the software is also used on Windows and without a package.

I recommend that gdk2x11 is put on Free Pascal Source. It worked without any problems for the tray icon.

thanks,

Felipe
Index: components/trayicon/wstrayicon.pas
===================================================================
--- components/trayicon/wstrayicon.pas	(revisão 8667)
+++ components/trayicon/wstrayicon.pas	(cópia de trabalho)
@@ -14,6 +14,8 @@
 
  Authors: Felipe Monteiro de Carvalho and Andrew Haines
 
+ Special thanks for: Danny Milosavljevic and the Lazarus Team
+
  This unit calls the appropriate widgetset code.
 }
 unit wstrayicon;
@@ -28,23 +30,33 @@
 *  Compatibility code for Delphi for Windows.
 *******************************************************************}
 {$ifndef FPC}
-  {$define LCLWin32}
+  {$define Win32}
 {$endif}
 
 
 uses
-{$ifdef LCLWin32}
+
+{$ifdef Win32}
+
   wswin32trayicon,
+  
 {$endif}
-{$ifdef LCLGtk}
-  wsgtktrayicon,
+{$ifdef UNIX}
+
+  {$ifdef LCLGtk}
+    wsgtktrayicon,
+  {$endif}
+  
+  {$ifdef LCLGnome}
+    wsgtktrayicon,
+  {$endif}
+  
+  {$ifdef LCLGtk2}
+    wsgtk2trayicon,
+  {$endif}
+  
 {$endif}
-{$ifdef LCLGnome}
-  wsgtktrayicon,
-{$endif}
-{$ifdef LCLGtk2}
-  wsgtk2trayicon,
-{$endif}
+
   Classes, SysUtils;
 
 type
@@ -76,3 +88,4 @@
 
 end.
 
+
Index: components/trayicon/wswin32trayicon.pas
===================================================================
--- components/trayicon/wswin32trayicon.pas	(revisão 8667)
+++ components/trayicon/wswin32trayicon.pas	(cópia de trabalho)
@@ -14,6 +14,8 @@
 
  Authors: Felipe Monteiro de Carvalho and Andrew Haines
 
+ Special thanks for: Danny Milosavljevic and the Lazarus Team
+
  Win32 specific code.
 }
 unit wswin32trayicon;
@@ -39,8 +41,8 @@
     public
       constructor Create; override;
       destructor Destroy; override;
-      function Hide: Boolean;
-      function Show: Boolean;
+      function Hide: Boolean; override;
+      function Show: Boolean; override;
       property Canvas: TCanvas read GetCanvas;
       procedure InternalUpdate; override;
     published
Index: components/trayicon/trayicon.pas
===================================================================
--- components/trayicon/trayicon.pas	(revisão 8667)
+++ components/trayicon/trayicon.pas	(cópia de trabalho)
@@ -14,6 +14,8 @@
 
  Authors: Felipe Monteiro de Carvalho and Andrew Haines
 
+ Special thanks for: Danny Milosavljevic and the Lazarus Team
+
  This unit contains the SystrayIcon object and the TTrayIcon visual component.
  
  Documentation for the component can be found here:
Index: components/trayicon/wsgtktrayicon.pas
===================================================================
--- components/trayicon/wsgtktrayicon.pas	(revisão 8667)
+++ components/trayicon/wsgtktrayicon.pas	(cópia de trabalho)
@@ -14,6 +14,8 @@
 
  Authors: Felipe Monteiro de Carvalho and Andrew Haines
 
+ Special thanks for: Danny Milosavljevic and the Lazarus Team
+
  Gtk1 specific code. Works on gnome also.
 }
 unit wsgtktrayicon;
@@ -52,8 +54,8 @@
       function GetCanvas: TCanvas;
     protected
     public
-      function Hide: Boolean;
-      function Show: Boolean;
+      function Hide: Boolean; override;
+      function Show: Boolean; override;
       property Canvas: TCanvas read GetCanvas;
       procedure InternalUpdate; override;
     published
@@ -127,6 +129,8 @@
   Ev: TXEvent;
   fmt: Integer;
 begin
+  FillChar(Ev, SizeOf(TXEvent), $0);
+
   ev.xclient._type := ClientMessage;
   ev.xclient.window := window;
   ev.xclient.message_type := XInternAtom (fDisplay, '_NET_SYSTEM_TRAY_OPCODE', False );
@@ -136,6 +140,7 @@
   ev.xclient.data.l[2] := data1;
   ev.xclient.data.l[3] := data2;
   ev.xclient.data.l[4] := data3;
+
   XSendEvent(fDisplay, fTrayParent, False, NoEventMask, @ev);
   XSync(fDisplay, False);
   Result := false;//(untrap_errors() = 0);
@@ -172,7 +177,6 @@
   Application.ProcessMessages;
 
   fDisplay := GDK_WINDOW_XDISPLAY(Pointer(PGtkWidget(GtkForm.Handle)^.window));
-//  SHowMessage(IntToStr(Integer(fDisplay)));
   fWindow := GDK_WINDOW_XWINDOW (Pointer(PGtkWidget(GtkForm.Handle)^.window));
   fScreen := XDefaultScreenOfDisplay(fDisplay); // get the screen
   fScreenID := XScreenNumberOfScreen(fScreen); // and it's number
@@ -287,11 +291,14 @@
 var
   size_hints: TXSizeHints;
 begin
+  FillChar(size_hints, SizeOf(TXSizeHints), $0);
+
   size_hints.flags := PSize or PMinSize or PMaxSize;
   size_hints.min_width := AWidth;
   size_hints.max_width := 100;
   size_hints.min_height := AHeight;
   size_hints.max_height := 100;
+
   XSetStandardProperties(fDisplay, fWindow, nil, nil, None, nil, 0, @size_hints);
 end;
 
@@ -330,3 +337,4 @@
 
 end.
 
+
Index: components/trayicon/wsgtk2trayicon.pas
===================================================================
--- components/trayicon/wsgtk2trayicon.pas	(revisão 8667)
+++ components/trayicon/wsgtk2trayicon.pas	(cópia de trabalho)
@@ -14,6 +14,8 @@
 
  Authors: Felipe Monteiro de Carvalho and Andrew Haines
 
+ Special thanks for: Danny Milosavljevic and the Lazarus Team
+
  Gtk2 specific code.
 }
 unit wsgtk2trayicon;
@@ -28,7 +30,7 @@
 
 uses
   Graphics, Classes, ExtCtrls, SysUtils, Forms, Controls, Dialogs,
-  Menus, wscommontrayicon, x, xlib, xutil, gtk2, gdk2, gtkproc;
+  Menus, wscommontrayicon, x, xlib, xutil, gtk2, gdk2, gdk2x, glib2, gtkdef;
 
 type
 
@@ -36,28 +38,20 @@
 
   TWidgetTrayIcon = class(TCustomWidgetTrayIcon)
     private
-      fDisplay: PDisplay;
-      fWindow: TWindow;
-      fScreen: PScreen;
-      fScreenID: longint;
-      fTrayParent: TWindow;
       fOwner: TComponent;
-      GtkForm: TForm;
       fEmbedded: Boolean;
       fMsgCount: Integer;
-      procedure SetEmbedded;
-      function Send_Message(window: TWindow; msg: Integer; data1, data2, data3: Integer): boolean;
-      procedure SetMinSize(AWidth, AHeight: Integer);
-      procedure PaintForm(Sender: TObject);
+      Tips: PGtkTooltips;
       procedure CreateForm(id: Integer);
       procedure RemoveForm(id: Integer);
       function GetCanvas: TCanvas;
     protected
     public
-      function Hide: Boolean;
-      function Show: Boolean;
+      function Hide: Boolean; override;
+      function Show: Boolean; override;
       property Canvas: TCanvas read GetCanvas;
       procedure InternalUpdate; override;
+      procedure PaintForm(Sender: TObject);
     published
   end;
 
@@ -68,15 +62,15 @@
 
 implementation
 
-type
-  PX11GdkDrawable = ^TX11GdkDrawable;
+uses WSTrayIcon;
 
-  TX11GdkDrawable = record
-    parent_instance: TGdkWindow;
-    wrapper: PGdkDrawable;
-    colormap: PGdkColorMap;
-    xid:x.TWindow;
-  end;
+var
+  fDisplay: PDisplay;
+  fWindow: TWindow;
+  fScreen: PScreen;
+  fScreenID: longint;
+  GtkForm: PGtkWidget;
+  fTrayParent: TWindow;
 
 {*******************************************************************
 *  TempX11ErrorHandler ()
@@ -93,75 +87,213 @@
   WriteLn('Error: ' + IntToStr(ErrorEv^.error_code));
 end;
 
+{*******************************************************************
+*  Send_Message ()
+*
+*  DESCRIPTION:    Sends a message to the X client
+*
+*  PARAMETERS:     None
+*
+*  RETURNS:        Nothing
+*
+*******************************************************************}
+function SendMessage(window: TWindow; msg: Integer; data1, data2, data3: Integer): boolean;
+var
+  Ev: TXEvent;
+  fmt: Integer;
+begin
+  FillChar(Ev, SizeOf(TXEvent), $0);
 
-{ TWidgetTrayIcon }
+  ev.xclient._type := ClientMessage;
+  ev.xclient.window := window;
+  ev.xclient.message_type := XInternAtom (fDisplay, '_NET_SYSTEM_TRAY_OPCODE', False );
+  ev.xclient.format := 32;
+  ev.xclient.data.l[0] := CurrentTime;
+  ev.xclient.data.l[1] := msg;
+  ev.xclient.data.l[2] := data1;
+  ev.xclient.data.l[3] := data2;
+  ev.xclient.data.l[4] := data3;
 
+  XSendEvent(fDisplay, fTrayParent, False, NoEventMask, @ev);
+  XSync(fDisplay, False);
+  Result := false;//(untrap_errors() = 0);
+end;
+
 {*******************************************************************
-*  TWidgetTrayIcon.SetEmbedded ()
+*  SetEmbedded ()
 *
-*  DESCRIPTION:
+*  DESCRIPTION:    Docks the GtkPlug into the system tray
 *
 *  PARAMETERS:     None
 *
 *  RETURNS:        Nothing
 *
 *******************************************************************}
-procedure TWidgetTrayIcon.SetEmbedded;
+procedure SetEmbedded;
 var
   old_error: TXErrorHandler;
   buf: array [0..32] of char;
   selection_atom : TAtom;
 begin
   old_error := XSetErrorHandler(@TempX11ErrorHandler);
-  Sleep(80);
+
   xsync(fdisplay,true);
   buf :=  PChar('_NET_SYSTEM_TRAY_S' + IntToStr(fScreenID));
   selection_atom := XInternAtom(fDisplay, buf, false);
   XGrabServer(fDisplay);
+
   fTrayParent := XGetSelectionOwner(fDisplay, selection_atom);
   if fTrayParent <> None then
   begin
     XSelectInput(fDisplay, fTrayParent, StructureNotifyMask);
   end;
+
   XUngrabServer(fDisplay);
   XFlush(fDisplay);
 
   if fTrayParent <> None then
-    Send_Message(fTrayParent, SYSTEM_TRAY_REQUEST_DOCK, fWindow, 0, 0);
+    SendMessage(fTrayParent, SYSTEM_TRAY_REQUEST_DOCK, fWindow, 0, 0);
 
   XSetErrorHandler(old_error);
 end;
 
 {*******************************************************************
-*  TWidgetTrayIcon.Send_Message ()
+*  realize_cb ()
 *
-*  DESCRIPTION:    Sends a message to the X client
+*  DESCRIPTION:    Callback function for the realize signal
+*                  Sets the systray icon after the widget is realized
 *
 *  PARAMETERS:     None
 *
 *  RETURNS:        Nothing
 *
 *******************************************************************}
-function TWidgetTrayIcon.Send_Message(window: TWindow; msg: Integer; data1, data2, data3: Integer): boolean;
+procedure realize_cb(widget: PGtkWidget; user_data: gpointer); cdecl;
 var
-  Ev: TXEvent;
-  fmt: Integer;
+  gdk_screen: PGdkScreen;
 begin
-  ev.xclient._type := ClientMessage;
-  ev.xclient.window := window;
-  ev.xclient.message_type := XInternAtom (fDisplay, '_NET_SYSTEM_TRAY_OPCODE', False );
-  ev.xclient.format := 32;
-  ev.xclient.data.l[0] := CurrentTime;
-  ev.xclient.data.l[1] := msg;
-  ev.xclient.data.l[2] := data1;
-  ev.xclient.data.l[3] := data2;
-  ev.xclient.data.l[4] := data3;
-  XSendEvent(fDisplay, fTrayParent, False, NoEventMask, @ev);
-  XSync(fDisplay, False);
-  Result := false;//(untrap_errors() = 0);
+  fDisplay := GDK_WINDOW_XDISPLAY(GtkForm^.window);
+  fWindow := GDK_WINDOW_XWINDOW(GtkForm^.window);
+
+{  Doesn´t work
+
+  gdk_screen := gtk_widget_get_screen(GtkForm);
+  fScreen := GDK_SCREEN_XSCREEN(gdk_screen); // get the real screen}
+
+  fScreen := XDefaultScreenOfDisplay(fDisplay);
+  fScreenID := XScreenNumberOfScreen(fScreen); // and it's number
+  
+  SetEmbedded;
 end;
 
 {*******************************************************************
+*  button_release_cb ()
+*
+*  DESCRIPTION:    Callback function for Mouse Click
+*
+*  PARAMETERS:     None
+*
+*  RETURNS:        Nothing
+*
+*******************************************************************}
+function button_release_cb(widget: PGtkWidget; event: PGdkEventButton;
+ user_data: gpointer): gboolean; cdecl;
+begin
+  Result := False;
+  
+  case event^.button of
+    1:
+    begin
+      if Assigned(vwsTrayIcon.OnClick) then vwsTrayIcon.OnClick(vwsTrayIcon);
+      if Assigned(vwsTrayIcon.OnMouseUp) then
+       vwsTrayIcon.OnMouseUp(vwsTrayIcon, mbLeft, [], Round(event^.X), Round(event^.Y));
+    end;
+    
+    2: if Assigned(vwsTrayIcon.OnMouseUp) then
+        vwsTrayIcon.OnMouseUp(vwsTrayIcon, mbRight, [], Round(event^.X), Round(event^.Y));
+
+    3:
+    begin
+      if Assigned(vwsTrayIcon.OnMouseUp) then
+       vwsTrayIcon.OnMouseUp(vwsTrayIcon, mbRight, [], Round(event^.X), Round(event^.Y));
+      if Assigned(vwsTrayIcon.PopUpMenu) then
+       vwsTrayIcon.PopUpMenu.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
+    end;
+  end;
+end;
+
+{*******************************************************************
+*  button_press_cb ()
+*
+*  DESCRIPTION:    Callback function for Mouse Click
+*
+*  PARAMETERS:     None
+*
+*  RETURNS:        Nothing
+*
+*******************************************************************}
+function button_press_cb(widget: PGtkWidget; event: PGdkEventButton;
+ user_data: gpointer): gboolean; cdecl;
+begin
+  Result := False;
+
+  if (event^._type = GDK_2BUTTON_PRESS) and Assigned(vwsTrayIcon.OnDblClick) then
+   vwsTrayIcon.OnDblClick(vwsTrayIcon)
+  else
+  begin
+    case event^.button of
+      1: if Assigned(vwsTrayIcon.OnMouseUp) then
+          vwsTrayIcon.OnMouseDown(vwsTrayIcon, mbLeft, [], Round(event^.X), Round(event^.Y));
+
+      2: if Assigned(vwsTrayIcon.OnMouseUp) then
+         vwsTrayIcon.OnMouseDown(vwsTrayIcon, mbRight, [], Round(event^.X), Round(event^.Y));
+
+      3: if Assigned(vwsTrayIcon.OnMouseUp) then
+          vwsTrayIcon.OnMouseDown(vwsTrayIcon, mbRight, [], Round(event^.X), Round(event^.Y));
+    end;
+  end;
+end;
+
+{*******************************************************************
+*  popup_cb ()
+*
+*  DESCRIPTION:    Callback function for the popup menu
+*
+*  PARAMETERS:     None
+*
+*  RETURNS:        Nothing
+*
+*******************************************************************}
+function popup_cb(widget: PGtkWidget; user_data: gpointer): Boolean; cdecl;
+begin
+  Result := True;
+
+  if Assigned(vwsTrayIcon.PopUpMenu) then
+   vwsTrayIcon.PopUpMenu.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
+end;
+
+{*******************************************************************
+*  motion_cb ()
+*
+*  DESCRIPTION:    Callback function for the realize signal
+*                  Sets the systray icon after the widget is realized
+*
+*  PARAMETERS:     None
+*
+*  RETURNS:        Nothing
+*
+*******************************************************************}
+function motion_cb(widget: PGtkWidget; event: PGdkEventMotion; user_data: gpointer): Boolean; cdecl;
+begin
+  Result := False;
+
+  if Assigned(vwsTrayIcon.OnMouseMove) then
+   vwsTrayIcon.OnMouseMove(vwsTrayIcon, [], Round(event^.X), Round(event^.Y));
+end;
+
+{ TWidgetTrayIcon }
+
+{*******************************************************************
 *  TWidgetTrayIcon.CreateForm ()
 *
 *  DESCRIPTION:
@@ -173,33 +305,55 @@
 *******************************************************************}
 procedure TWidgetTrayIcon.CreateForm(id: Integer);
 var
-  Widget: PGtkWidget;
+  AImage: PGtkWidget;
+  GDIObject: PgdiObject;
 begin
-  GtkForm := TForm.Create(nil);
+  {*******************************************************************
+  *  Creates the GtkPlug
+  *******************************************************************}
+  
   fEmbedded := False;
-  //fWindow := GDK_WINDOW_XWINDOW (Pointer(PGtkWidget(GtkForm.Handle)^.window));
-  //SHowMessage(IntToStr(Integer(fWindow)));
-  //GtkForm.Parent := TWinConTrol(fOwner);
-  GtkForm.WindowState := wsMinimized;
-  GtkForm.BorderStyle := bsNone; //without this gnome will make a 1 pixel wide window!
-  //GtkForm.Canvas.AutoRedraw := True; //not working :(
 
-  // needed because some things aparently don't get fully initialized until
-  // visible at least once!  This is Gtk related NOT LCL related.
-  GtkForm.Visible :=True;
-  GtkForm.Width := 22;
-  GtkForm.Height := 22;
-  GtkForm.Visible := False;
+  GtkForm := gtk_plug_new(0);
 
-  Application.ProcessMessages;
+  Tips := gtk_tooltips_new;
+  
+  gtk_tooltips_set_tip(GTK_TOOLTIPS(Tips), GtkForm, PChar(Hint), '');
 
-  fDisplay := GDK_DISPLAY;
-//  SHowMessage(IntToStr(Integer(fDisplay)));
-  Widget := PGtkWidget(GtkForm.Handle);
-  fWindow := PX11GdkDrawable(PGdkWindowObject(Widget^.window)^.impl)^.xid;
+  {*******************************************************************
+  *  Connects the signals
+  *******************************************************************}
+  
+  gtk_widget_add_events(GtkForm, GDK_ALL_EVENTS_MASK);
 
-  fScreen := XDefaultScreenOfDisplay(fDisplay); // get the screen
-  fScreenID := XScreenNumberOfScreen(fScreen); // and it's number
+  g_signal_connect(GtkForm, 'realize', TGCallback(@realize_cb), nil);
+
+  g_signal_connect(GtkForm, 'popup-menu', TGCallback(@popup_cb), nil);
+
+  g_signal_connect(GtkForm, 'motion-notify-event', TGCallback(@motion_cb), nil);
+
+  g_signal_connect(GtkForm, 'button-press-event', TGCallback(@button_press_cb), nil);
+
+  g_signal_connect(GtkForm, 'button-release-event', TGCallback(@button_release_cb), nil);
+
+  {*******************************************************************
+  *  Draws the icon
+  *******************************************************************}
+
+  GDIObject := PgdiObject(Icon.Handle);
+
+  AImage := gtk_image_new_from_pixmap(GDIObject^.GDIPixmapObject,
+   GDIObject^.GDIBitmapMaskObject);
+
+  gtk_widget_show(AImage);
+
+  gtk_container_add(GTK_CONTAINER(GtkForm), AImage);
+
+  {*******************************************************************
+  *  Now shows the GtkPlug
+  *******************************************************************}
+
+  gtk_widget_show(GtkForm);
 end;
 
 {*******************************************************************
@@ -214,7 +368,11 @@
 *******************************************************************}
 procedure TWidgetTrayIcon.RemoveForm(id: Integer);
 begin
-  GtkForm.Free;
+  gtk_widget_destroy(GtkForm);
+  
+  GtkForm := nil;
+
+  Tips := nil;
 end;
 
 {*******************************************************************
@@ -229,7 +387,7 @@
 *******************************************************************}
 function TWidgetTrayIcon.GetCanvas: TCanvas;
 begin
-  Result := GtkForm.Canvas;
+  Result := Icon.Canvas;
 end;
 
 {*******************************************************************
@@ -273,22 +431,6 @@
   
   CreateForm(0);
 
-  SetEmbedded;
-
-  GTK_WIDGET_SET_FLAGS(PGtkWidget(GtkForm.Handle),GTK_VISIBLE);
-  GTK_WIDGET_SET_FLAGS(PGtkWidget(GtkForm.Handle),GTK_MAPPED);
-
-  GtkForm.Width := 22; //needed for gnome
-  GtkForm.Height := 22;
-  SetMinSize(Icon.Width, Icon.Height);
-
-  GtkForm.OnMouseDown := Self.OnMouseDown;
-  GtkForm.OnMouseMove := Self.OnMouseMove;
-  GtkForm.OnMouseUp := Self.OnMouseUp;
-  GtkForm.OnClick := Self.OnClick;
-  GtkForm.OnPaint := PaintForm;
-  GtkForm.PopupMenu := Self.PopUpMenu;
-
   fEmbedded := True;
 
   vVisible := True;
@@ -297,28 +439,6 @@
 end;
 
 {*******************************************************************
-*  TWidgetTrayIcon.SetMinSize ()
-*
-*  DESCRIPTION:    Attemps to avoid problems on Gnome
-*
-*  PARAMETERS:
-*
-*  RETURNS:        Nothing
-*
-*******************************************************************}
-procedure TWidgetTrayIcon.SetMinSize(AWidth, AHeight: Integer);
-var
-  size_hints: TXSizeHints;
-begin
-  size_hints.flags := PSize or PMinSize or PMaxSize;
-  size_hints.min_width := AWidth;
-  size_hints.max_width := 100;
-  size_hints.min_height := AHeight;
-  size_hints.max_height := 100;
-  XSetStandardProperties(fDisplay, fWindow, nil, nil, None, nil, 0, @size_hints);
-end;
-
-{*******************************************************************
 *  TWidgetTrayIcon.PaintForm ()
 *
 *  DESCRIPTION:    Paint method of the Icon Window
@@ -330,7 +450,7 @@
 *******************************************************************}
 procedure TWidgetTrayIcon.PaintForm(Sender: TObject);
 begin
-  if ShowIcon then GtkForm.Canvas.Draw(0, 0, Icon);
+//  if ShowIcon then GtkForm.Canvas.Draw(0, 0, Icon);
 
   if Assigned(OnPaint) then OnPaint(Self);
 end;
@@ -348,7 +468,7 @@
 *******************************************************************}
 procedure TWidgetTrayIcon.InternalUpdate;
 begin
-  if Assigned(GtkForm) then GtkForm.PopupMenu := Self.PopUpMenu;
+  gtk_tooltips_set_tip(GTK_TOOLTIPS(Tips), GtkForm, PChar(Hint), '');
 end;
 
 end.
Index: components/trayicon/wscommontrayicon.pas
===================================================================
--- components/trayicon/wscommontrayicon.pas	(revisão 8667)
+++ components/trayicon/wscommontrayicon.pas	(cópia de trabalho)
@@ -47,6 +47,8 @@
       constructor Create;  virtual;
       destructor Destroy; override;
       procedure InternalUpdate; virtual; abstract;
+      function Hide: Boolean; virtual; abstract;
+      function Show: Boolean; virtual; abstract;
     published
   end;
 
@@ -85,6 +87,8 @@
 *******************************************************************}
 destructor TCustomWidgetTrayIcon.Destroy;
 begin
+  Hide;
+
   Icon.Free;
 
   inherited Destroy;
@@ -92,3 +96,4 @@
 
 end.
 
+

Reply via email to