1{%mainunit customdrawnwsextctrls.pas}
2
3type
4
5  { TX11TrayIconHandle }
6
7  TX11TrayIconHandle = class(TObject)
8  private
9    //plug: PGtkWidget;
10    //drawingarea: PGtkWidget;
11    fDisplay: PDisplay;
12    fcanvas: TCanvas;
13    fWindow: TWindow;
14    fScreenID: longint;
15    fTrayParent: TWindow;
16    //fOwner: TComponent;
17    fEmbedded: Boolean;
18    //fMsgCount: Integer;
19    fTrayIcon: TCustomTrayIcon;
20    function GetCanvas: TCanvas;
21    procedure PaintForm(Sender: TObject);
22    function  Send_Message(window: TWindow; msg: Integer;data1, data2,data3: Integer): boolean;
23  public
24    constructor create;
25    destructor Destroy; override;
26    procedure CreateForm(id: Integer);
27    function  GetPosition: TPoint;
28    procedure Hide;
29    procedure SetEmbedded;
30    function  TrayParent(UseCachedValue: Boolean = True): TWindow;
31  public
32    property Canvas: TCanvas read GetCanvas;
33  end;
34
35const
36  SYSTEM_TRAY_REQUEST_DOCK   = 0;
37  SYSTEM_TRAY_BEGIN_MESSAGE  = 1;
38  SYSTEM_TRAY_CANCEL_MESSAGE = 2;
39
40// Temp ErrorHandler
41function TempX11ErrorHandler(Display:PDisplay; ErrorEv:PXErrorEvent):longint;cdecl;
42begin
43  DebugLn('[TX11TrayIconHandle] Error: ' + IntToStr(ErrorEv^.error_code));
44  Result:=0;
45end;
46
47function TX11TrayIconHandle.GetCanvas: TCanvas;
48begin
49  Result := nil;
50{  if Assigned(FCanvas) then Exit(FCanvas);
51
52  Result := TCanvas.Create;
53  Result.Handle:= GetDC(HWND(drawingarea));
54  FCanvas := Result;}
55end;
56
57{function TX11TrayIconHandle.NotifyExpose(Event: PGdkEventExpose;
58  Widget: PGtkWidget): Boolean; cdecl;
59begin
60  Result := False;
61
62  PaintForm(fTrayIcon);
63end;
64
65function TGtk1TrayIconHandle.NotifyMouseMove(Event: PGdkEventMotion;
66  Widget: PGtkWidget): Boolean; cdecl;
67begin
68  Result := False;
69
70  if Assigned(fTrayIcon.OnMouseMove) then
71     fTrayIcon.OnMouseMove(fTrayIcon, [], Trunc(Event^.x), Trunc(Event^.y));
72end;
73
74function TGtk1TrayIconHandle.NotifyMouseDown(Event: PGdkEventButton;
75  Widget: PGtkWidget): Boolean; cdecl;
76var
77  Button: TMouseButton;
78begin
79  Result := False;
80
81  case  Event^.button of
82    GDK_RIGHTBUTTON: Button := mbRight;
83    GDK_MIDDLEBUTTON: Button := mbMiddle;
84    GDK_LEFTBUTTON: Button := mbLeft;
85  end;
86
87  if Assigned(fTrayIcon.OnMouseDown) then
88    fTrayIcon.OnMouseDown(fTrayIcon, Button, [], Trunc(Event^.x), Trunc(Event^.y));
89end;
90
91function TGtk1TrayIconHandle.NotifyMouseUp(Event: PGdkEventButton;
92  Widget: PGtkWidget): Boolean; cdecl;
93var
94  Button: TMouseButton;
95begin
96  Result := False;
97
98  case  Event^.button of
99    3: Button := mbRight;
100    2: Button := mbMiddle;
101    1: Button := mbLeft;
102  end;
103
104  if Button = mbLeft then
105    case gdk_event_get_type(Event) of
106       GDK_BUTTON_PRESS:
107         if Assigned(fTrayIcon.OnClick) then
108           fTrayIcon.OnClick(fTrayIcon);
109       GDK_2BUTTON_PRESS:
110         if Assigned(fTrayIcon.OnDblClick) then
111           fTrayIcon.OnDblClick(fTrayIcon);
112    end;
113
114  { Just using GetPosition to get the screen position and then add
115    Event^.x and Event^.y to it won't work. It seams that this will
116    cause a small difference with Mouse.CursorPos, and using
117    TPopupMenu.PopUp will result in a wrong position for the menu }
118  if (Button = mbRight) and (fTrayIcon.PopUpMenu <> nil) then
119    fTrayIcon.PopUpMenu.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
120
121  if Assigned(fTrayIcon.OnMouseUp) then
122    fTrayIcon.OnMouseUp(fTrayIcon, Button, [], Trunc(Event^.x), Trunc(Event^.y));
123end;       }
124
125{*******************************************************************
126*  TX11TrayIconHandle.Send_Message ()
127*
128*  DESCRIPTION:    Sends a message to the X client
129*
130*******************************************************************}
131function TX11TrayIconHandle.Send_Message(window: TWindow; msg: Integer;data1, data2,data3: Integer): boolean;
132var
133  Ev: TXEvent;
134//  fmt: Integer;
135begin
136  FillChar(Ev, SizeOf(TXEvent), $0);
137
138  ev.xclient._type := ClientMessage;
139  ev.xclient.window := window;
140  ev.xclient.message_type := XInternAtom (fDisplay, '_NET_SYSTEM_TRAY_OPCODE', False );
141  ev.xclient.format := 32;
142  ev.xclient.data.l[0] := CurrentTime;
143  ev.xclient.data.l[1] := msg;
144  ev.xclient.data.l[2] := data1;
145  ev.xclient.data.l[3] := data2;
146  ev.xclient.data.l[4] := data3;
147
148  XSendEvent(fDisplay, fTrayParent, False, NoEventMask, @ev);
149  XSync(fDisplay, False);
150  Result := false;//(untrap_errors() = 0);
151end;
152
153constructor TX11TrayIconHandle.create;
154begin
155  inherited create;
156end;
157
158function TX11TrayIconHandle.TrayParent(UseCachedValue: Boolean = True): TWindow;
159var
160  buf: array[0..32] of char;
161  selection_atom: TAtom;
162begin
163  if (fTrayParent <> 0) and UseCachedValue then
164    Exit(fTrayParent);
165  fDisplay := CDWidgetset.FDisplay;
166  fScreenID := CDWidgetSet.FScreen;
167
168  buf :=  PChar('_NET_SYSTEM_TRAY_S' + IntToStr(fScreenID));
169  selection_atom := XInternAtom(fDisplay, buf, false);
170  fTrayParent := XGetSelectionOwner(fDisplay, selection_atom);
171
172  Result := fTrayParent;
173end;
174
175destructor TX11TrayIconHandle.Destroy;
176begin
177  if Assigned(FCanvas) and FCanvas.HandleAllocated then
178  begin
179    //ReleaseDC(HWND(drawingarea), fcanvas.Handle);
180    FCanvas.Free;
181  end;
182{  if Assigned(drawingarea) then
183  begin
184    g_signal_handlers_destroy(G_OBJECT(drawingarea));
185    gtk_widget_destroy(drawingarea);
186  end;
187  if Assigned(plug) then
188    gtk_widget_destroy(plug);}
189
190  inherited Destroy;
191end;
192
193{*******************************************************************
194*  TX11TrayIconHandle.SetEmbedded ()
195*
196*  DESCRIPTION:
197*
198*******************************************************************}
199procedure TX11TrayIconHandle.SetEmbedded;
200var
201  old_error: TXErrorHandler;
202begin
203  fEmbedded := False;
204  if TrayParent = None then
205    Exit;
206
207{  // so we have a TWindow
208  gtk_widget_realize(plug);}
209
210  old_error := XSetErrorHandler(@TempX11ErrorHandler);
211  Sleep(80);
212  xsync(fdisplay,true);
213
214  XSelectInput(fDisplay, TrayParent, StructureNotifyMask);
215
216  //fWindow := GDK_WINDOW_XWINDOW (Pointer(PGtkWidget(plug)^.window));
217
218  Send_Message(TrayParent, SYSTEM_TRAY_REQUEST_DOCK, fWindow, 0, 0);
219
220  //GTK_WIDGET_SET_FLAGS(plug,GTK_VISIBLE);
221  //GTK_WIDGET_SET_FLAGS(plug,GTK_MAPPED);
222
223  //gtk_widget_show_all(plug);
224
225  XSetErrorHandler(old_error);
226  fEmbedded:=True;
227end;
228
229procedure TX11TrayIconHandle.Hide;
230begin
231  //gtk_widget_hide_all(drawingarea);
232  fEmbedded := False;
233end;
234
235{*******************************************************************
236*  TX11TrayIconHandle.CreateForm ()
237*
238*  DESCRIPTION:
239*
240*******************************************************************}
241procedure TX11TrayIconHandle.CreateForm(id: Integer);
242begin
243{  plug := gtk_plug_new(0);
244  drawingarea := gtk_event_box_new;
245
246  gtk_container_add(GTK_CONTAINER(plug), drawingarea);
247
248  //gtk_widget_add_events(drawingarea, GDK_MOTION_NOTIFY);
249
250  gtk_signal_connect_object_after(G_OBJECT(drawingarea), 'expose-event', TGtkSignalFunc(@TGtk1TrayIconHandle.NotifyExpose), G_OBJECT(Self));
251  gtk_signal_connect_object(G_OBJECT(drawingarea), 'motion-notify-event', TGtkSignalFunc(@TGtk1TrayIconHandle.NotifyMouseMove), G_OBJECT(Self));
252  gtk_signal_connect_object(G_OBJECT(drawingarea), 'button-press-event', TGtkSignalFunc(@TGtk1TrayIconHandle.NotifyMouseDown), G_OBJECT(Self));
253  gtk_signal_connect_object(G_OBJECT(drawingarea), 'button-release-event', TGtkSignalFunc(@TGtk1TrayIconHandle.NotifyMouseUp), G_OBJECT(Self));}
254
255  fEmbedded := False;
256  GetCanvas;
257end;
258
259{*******************************************************************
260*  TX11TrayIconHandle.GetPosition ()
261*
262*  DESCRIPTION:    Returns the (x, y) position of the icon on the screen
263*
264*******************************************************************}
265function TX11TrayIconHandle.GetPosition: TPoint;
266{var
267  WindowHandle: PGDKWindow;}
268begin
269  Result := Point(0, 0);
270
271{  if not Assigned(plug) then Exit;
272
273  WindowHandle := plug^.window;
274
275  if not Assigned(WindowHandle) then Exit;
276
277  gdk_window_get_origin(WindowHandle, @Result.X, @Result.Y);}
278end;
279
280{*******************************************************************
281*  TX11TrayIconHandle.PaintForm ()
282*
283*  DESCRIPTION:    Paint method of the Icon Window
284*
285*******************************************************************}
286procedure TX11TrayIconHandle.PaintForm(Sender: TObject);
287begin
288  if fTrayIcon.ShowIcon then Canvas.Draw(0, 0, fTrayIcon.Icon);
289
290  if Assigned(fTrayIcon.OnPaint) then fTrayIcon.OnPaint(Self);
291end;
292
293{*******************************************************************
294*  TCDWSCustomTrayIcon.Hide ()
295*
296*  DESCRIPTION:    Hides the main tray icon of the program
297*
298*  PARAMETERS:     None
299*
300*  RETURNS:        True if sucessfull, otherwise False
301*
302*******************************************************************}
303class function TCDWSCustomTrayIcon.Hide(const ATrayIcon: TCustomTrayIcon): Boolean;
304var
305  TrayIconHandle: TX11TrayIconHandle;
306begin
307  Result := False;
308
309  TrayIconHandle := TX11TrayIconHandle(ATrayIcon.Handle);
310  TrayIconHandle.Free;
311
312  ATrayIcon.Handle := 0;
313
314  Result := True;
315end;
316
317{*******************************************************************
318*  TCDWSCustomTrayIcon.Show ()
319*
320*  DESCRIPTION:    Shows the main tray icon of the program
321*
322*  PARAMETERS:     None
323*
324*  RETURNS:        True if sucessfull, otherwise False
325*
326*******************************************************************}
327class function TCDWSCustomTrayIcon.Show(const ATrayIcon: TCustomTrayIcon): Boolean;
328var
329  TrayIconHandle: TX11TrayIconHandle;
330begin
331  Result := False;
332
333  TrayIconHandle := TX11TrayIconHandle.Create;
334  TrayIconHandle.fTrayIcon := ATrayIcon;
335
336  ATrayIcon.Handle := PtrInt(TrayIconHandle);
337
338  TrayIconHandle.CreateForm(0);
339  TrayIconHandle.SetEmbedded;
340
341  Result := True;
342end;
343
344{*******************************************************************
345*  TCDWSCustomTrayIcon.InternalUpdate ()
346*
347*  DESCRIPTION:    Makes modifications to the Icon while running
348*                  i.e. without hiding it and showing again
349*******************************************************************}
350class procedure TCDWSCustomTrayIcon.InternalUpdate(const ATrayIcon: TCustomTrayIcon);
351{var
352  TrayIconHandle: TGtk1TrayIconHandle;}
353begin
354{  TrayIconHandle := TGtk1TrayIconHandle(ATrayIcon.Handle);
355
356  if not Assigned(TrayIconHandle) then Exit;}
357end;
358
359class function TCDWSCustomTrayIcon.ShowBalloonHint(
360  const ATrayIcon: TCustomTrayIcon): Boolean;
361begin
362  Result := False;
363end;
364
365{*******************************************************************
366*  TCDWSCustomTrayIcon.GetPosition ()
367*
368*  DESCRIPTION:    Returns the position of the tray icon on the display.
369*                  This function is utilized to show message boxes near
370*                  the icon
371*
372*******************************************************************}
373class function TCDWSCustomTrayIcon.GetPosition(const ATrayIcon: TCustomTrayIcon): TPoint;
374var
375  TrayIconHandle: TX11TrayIconHandle;
376begin
377  Result := Point(0, 0);
378
379  TrayIconHandle := TX11TrayIconHandle(ATrayIcon.Handle);
380
381  if not Assigned(TrayIconHandle) then Exit;
382
383  Result := TrayIconHandle.GetPosition;
384end;
385
386{class function TGtkWSCustomTrayIcon.GetCanvas(const ATrayIcon: TCustomTrayIcon): TCanvas;
387var
388  TrayIconHandle: TX11TrayIconHandle;
389begin
390  TrayIconHandle := TX11TrayIconHandle(ATrayIcon.Handle);
391
392  if not Assigned(TrayIconHandle) then Exit(ATrayIcon.Icon.Canvas);
393
394  Result := TrayIconHandle.Canvas;
395end;     }
396
397