1{%MainUnit gtkproc.pp}
2
3{******************************************************************************
4                        Misc Support Functs
5 ******************************************************************************
6   used by:
7     GTKObject
8     GTKWinAPI
9     GTKCallback
10 ******************************************************************************
11 *****************************************************************************
12  This file is part of the Lazarus Component Library (LCL)
13
14  See the file COPYING.modifiedLGPL.txt, included in this distribution,
15  for details about the license.
16 *****************************************************************************
17}
18
19{off $DEFINE VerboseAccelerator}
20{off $DEFINE VerboseUpdateSysColorMap}
21
22{$IFOPT C-}
23// Uncomment for local trace
24  //{$C+}
25  //{$DEFINE ASSERT_IS_ON}
26{$ENDIF}
27
28function gtk_widget_get_xthickness(Style : PGTKStyle) : gint;
29begin
30  If (Style <> nil) then begin
31    {$IfNDef GTK2}
32      If (Style^.klass = nil) then
33        result := 0
34      else
35    {$EndIf}
36        result := Style^.{$IfNDef GTK2}klass^.{$EndIF}xthickness
37  end else
38    result := 0;
39end;
40
41function gtk_widget_get_ythickness(Style : PGTKStyle) : gint;
42begin
43  If (Style <> nil) then begin
44    {$IfNDef GTK2}
45      If (Style^.klass = nil) then
46        result := 0
47      else
48    {$EndIf}
49        result := Style^.{$IfNDef GTK2}klass^.{$EndIF}ythickness
50  end else
51    result := 0;
52end;
53
54function gtk_widget_get_xthickness(Widget : PGTKWidget) : gint; overload;
55begin
56  result := gtk_widget_get_xthickness(gtk_widget_get_style(Widget));
57end;
58
59function gtk_widget_get_ythickness(Widget : PGTKWidget) : gint; overload;
60begin
61  result := gtk_widget_get_ythickness(gtk_widget_get_style(Widget));
62end;
63
64function GetGtkContainerBorderWidth(Widget: PGtkContainer): gint;
65begin
66  Result:=(Widget^.flag0 and bm_TGtkContainer_border_width)
67          shr bp_TGtkContainer_border_width;
68end;
69
70procedure gdk_event_key_get_string(Event : PGDKEventKey; var theString : Pointer);
71begin
72  {$IfDef GTK2}
73  theString := Pointer(Event^._String);
74  {$Else}
75  theString := Pointer(Event^.TheString);
76  {$EndIF}
77end;
78
79procedure gdk_event_key_set_string(Event: PGDKEventKey; const NewString: PChar);
80var
81  OldString: PChar;
82begin
83  {$IfDef GTK2}
84  OldString := Pointer(Event^._String);
85  {$Else}
86  OldString := Pointer(Event^.TheString);
87  {$EndIF}
88  // MG: should we set Event^.length := 0; or is this used for mem allocation?
89  if (OldString<>nil) then begin
90    if (NewString<>nil) then
91      OldString[0]:=NewString[0]
92    else
93      OldString[0]:=#0;
94  end;
95end;
96
97function gdk_event_get_type(Event : Pointer) : TGdkEventType;
98begin
99  {$IfDef GTK2}
100  result := PGdkEvent(Event)^._type;
101  {$Else}
102  result := PGdkEvent(Event)^.TheType;
103  {$EndIF}
104end;
105
106procedure RememberKeyEventWasHandledByLCL(Event: PGdkEventKey;
107  BeforeEvent: boolean);
108var
109  HandledEvent: TLCLHandledKeyEvent;
110  EventList: TFPList;
111begin
112  if KeyEventWasHandledByLCL(Event,BeforeEvent) then exit;
113  if BeforeEvent then begin
114    if LCLHandledKeyEvents=nil then
115      LCLHandledKeyEvents:=TFPList.Create;
116    EventList:=LCLHandledKeyEvents;
117  end else begin
118    if LCLHandledKeyAfterEvents=nil then
119      LCLHandledKeyAfterEvents:=TFPList.Create;
120    EventList:=LCLHandledKeyAfterEvents;
121  end;
122  HandledEvent:=TLCLHandledKeyEvent.Create(Event);
123  EventList.Add(HandledEvent);
124  while EventList.Count>10 do begin
125    HandledEvent:=TLCLHandledKeyEvent(EventList[0]);
126    HandledEvent.Free;
127    EventList.Delete(0);
128  end;
129end;
130
131function KeyEventWasHandledByLCL(Event: PGdkEventKey; BeforeEvent: boolean
132  ): boolean;
133var
134  i: Integer;
135  HandledEvent: TLCLHandledKeyEvent;
136  EventList: TFPList;
137begin
138  Result:=false;
139  if BeforeEvent then
140    EventList:=LCLHandledKeyEvents
141  else
142    EventList:=LCLHandledKeyAfterEvents;
143  if EventList=nil then exit;
144  for i:=0 to EventList.Count-1 do begin
145    HandledEvent:=TLCLHandledKeyEvent(EventList[i]);
146    if HandledEvent.IsEqual(Event) then begin
147      Result:=true;
148      exit;
149    end;
150  end;
151end;
152
153
154{$Ifdef GTK2}
155function gtk_class_get_type(aclass : Pointer) : TGtkType;
156begin
157  If (aclass <> nil) then
158    result := PGtkTypeClass(aclass)^.g_Type
159  else
160    result := 0;
161end;
162
163function gtk_object_get_class(anobject : Pointer) : Pointer;
164begin
165  If (anobject <> nil) then
166    result := PGtkTypeObject(anobject)^.g_Class
167  else
168    result := nil;
169end;
170
171function gtk_window_get_modal(window:PGtkWindow):gboolean;
172begin
173  if assigned(Window) then
174    result := GTK2.gtk_window_get_modal(window)
175  else
176    result := False;
177end;
178
179function gdk_region_union_with_rect(region:PGdkRegion; rect:PGdkRectangle) : PGdkRegion;
180begin
181  result := gdk_region_copy(region);
182  GDK2.gdk_region_union_with_rect(result, rect);
183end;
184
185function gdk_region_intersect(source1:PGdkRegion; source2:PGdkRegion) : PGdkRegion;
186begin
187  result := gdk_region_copy(source1);
188  GDK2.gdk_region_intersect(result, source2);
189end;
190
191function gdk_region_union(source1:PGdkRegion; source2:PGdkRegion) : PGdkRegion;
192begin
193  result := gdk_region_copy(source1);
194  GDK2.gdk_region_union(result, source2);
195end;
196
197function gdk_region_subtract(source1:PGdkRegion; source2:PGdkRegion) : PGdkRegion;
198begin
199  result := gdk_region_copy(source1);
200  GDK2.gdk_region_subtract(result, source2);
201end;
202
203function gdk_region_xor(source1:PGdkRegion; source2:PGdkRegion) : PGdkRegion;
204begin
205  result := gdk_region_copy(source1);
206  GDK2.gdk_region_xor(result, source2);
207end;
208
209Procedure gdk_text_extents(TheFont: TGtkIntfFont;
210  Str: PChar; StrLength: integer;
211  lbearing, rbearing, width, ascent, descent: Pgint);
212var
213  Layout : PPangoLayout;
214  Extents : TPangoRectangle;
215begin
216  //DebugLn(['gdk_text_extents Str="',Str,'" StrLength=',StrLength,' lbearing=',lbearing<>nil,' rbearing=',rbearing<>Nil,' width=',width<>nil,' ascent=',ascent<>nil,' descent=',descent<>Nil,' ',TheFont<>Nil]);
217  Layout:=TheFont;
218  pango_layout_set_single_paragraph_mode(Layout, TRUE);
219  pango_layout_set_width(Layout, -1);
220  pango_layout_set_text(Layout, Str, StrLength);
221  if Assigned(width) then
222    pango_layout_get_pixel_size(Layout, width, nil);
223  if Assigned(lbearing) or Assigned(rbearing)
224  or Assigned(ascent) or Assigned(descent) then begin
225    pango_layout_get_extents(Layout, nil, @Extents);
226
227    if Assigned(lbearing) then
228      lbearing^ := PANGO_LBEARING(extents) div PANGO_SCALE;
229
230    if Assigned(rbearing) then
231      rBearing^ := PANGO_RBEARING(extents) div PANGO_SCALE;
232
233    if Assigned(ascent) then
234      ascent^ := PANGO_ASCENT(extents) div PANGO_SCALE;
235
236    if Assigned(descent) then
237      descent^ := PANGO_DESCENT(extents) div PANGO_SCALE;
238  end;
239end;
240
241{$EndIf Gtk2}
242
243procedure BeginGDKErrorTrap;
244begin
245  Inc(GdkTrapCalls);
246  if GdkTrapIsSet then
247    exit;
248
249  gdk_error_trap_push; //try to prevent GDK Bad Drawable/X Windows Errors
250                         // from killing us...
251
252  {$IfDef GDK_ERROR_TRAP_FLUSH}
253  gdk_flush; //only for debugging purposes DO NOT enable by default.
254               // slows things down intolerably for actual use, if we ever
255               // have a real need for it, it should be called from that
256               // specific function, since this gets called constantly during
257               // drawing.
258  {$EndIf}
259
260  GdkTrapIsSet:=true;
261end;
262
263procedure EndGDKErrorTrap;
264var
265  Xerror : gint;
266begin
267  Dec(GdkTrapCalls);
268  if (not GdkTrapIsSet) then
269    RaiseGDBException('EndGDKErrorTrap without BeginGDKErrorTrap');
270  if (GdkTrapCalls > 0) then
271    exit;
272
273  Xerror := gdk_error_trap_pop;
274
275  GdkTrapIsSet:=false;
276
277  {$IFDEF VerboseGtkToDos}{$note TODO: enable standard error_log handling}{$ENDIF}
278  {$IfDef REPORT_GDK_ERRORS}
279  If (Xerror<>0) then
280    RaiseGDBException('A GDK/X Error occurred, this is normally fatal. The error code was: ' + IntToStr(Xerror));
281  {$EndIf}
282end;
283
284function dbgGRect(const ARect: PGDKRectangle): string;
285begin
286  if ARect=nil then begin
287    Result:='nil';
288  end else begin
289    Result:='x='+dbgs(ARect^.x)+',y='+dbgs(ARect^.y)
290           +',w='+dbgs(ARect^.width)+',h='+dbgs(ARect^.height);
291  end;
292end;
293
294
295{------------------------------------------------------------------------------
296  Allocates a new PChar
297 ------------------------------------------------------------------------------
298function CreatePChar(const s: string): PChar;
299begin
300  Result:=StrAlloc(length(s) + 1);
301  StrPCopy(Result, s);
302end;
303}
304function FindChar(c: char; p:PChar; Max: integer): integer;
305begin
306  Result:=0;
307  while (Result<Max) do begin
308    if p[Result]<>c then
309      inc(Result)
310    else
311      exit;
312  end;
313  Result:=-1;
314end;
315
316{------------------------------------------------------------------------------
317  function FindLineLen(p: PChar; Max: integer): integer;
318
319  Find line end
320 ------------------------------------------------------------------------------}
321function FindLineLen(p: PChar; Max: integer): integer;
322begin
323  Result:=0;
324  while (Result<Max) do begin
325    if not (p[Result] in [#10,#13]) then
326      inc(Result)
327    else
328      exit;
329  end;
330  Result:=-1;
331end;
332
333function RectFromGdkRect(const AGdkRect: TGdkRectangle): TRect;
334begin
335  with Result do
336  begin
337    Left := AGdkRect.x;
338    Top := AGdkRect.y;
339    Right := AGdkRect.Width + AGdkRect.x;
340    Bottom := AGdkRect.Height + AGdkRect.y;
341  end;
342end;
343
344function GdkRectFromRect(const R: TRect): TGdkRectangle;
345begin
346  with Result do
347  begin
348    x := R.Left;
349    y := R.Top;
350    width := R.Right-R.Left;
351    height := R.Bottom-R.Top;
352  end;
353end;
354
355function AlignToGtkAlign(Align: TAlignment): gfloat;
356begin
357  case Align of
358    taLeftJustify : AlignToGtkAlign := 0.0;
359    taCenter      : AlignToGtkAlign := 0.5;
360    taRightJustify: AlignToGtkAlign := 1.0;
361  end;
362end;
363
364{$ifdef gtk2}
365function GtkScrollTypeToScrollCode(ScrollType: TGtkScrollType): LongWord;
366begin
367  case ScrollType of
368      GTK_SCROLL_NONE          : Result := SB_ENDSCROLL;
369      GTK_SCROLL_JUMP          : Result := SB_THUMBPOSITION;
370      GTK_SCROLL_STEP_BACKWARD : Result := SB_LINELEFT;
371      GTK_SCROLL_STEP_FORWARD  : Result := SB_LINERIGHT;
372      GTK_SCROLL_PAGE_BACKWARD : Result := SB_PAGELEFT;
373      GTK_SCROLL_PAGE_FORWARD  : Result := SB_PAGERIGHT;
374      GTK_SCROLL_STEP_UP       : Result := SB_LINEUP;
375      GTK_SCROLL_STEP_DOWN     : Result := SB_LINEDOWN;
376      GTK_SCROLL_PAGE_UP       : Result := SB_PAGEUP;
377      GTK_SCROLL_PAGE_DOWN     : Result := SB_PAGEDOWN;
378      GTK_SCROLL_STEP_LEFT     : Result := SB_LINELEFT;
379      GTK_SCROLL_STEP_RIGHT    : Result := SB_LINERIGHT;
380      GTK_SCROLL_PAGE_LEFT     : Result := SB_PAGELEFT;
381      GTK_SCROLL_PAGE_RIGHT    : Result := SB_PAGERIGHT;
382      GTK_SCROLL_START         : Result := SB_TOP;
383      GTK_SCROLL_END           : Result := SB_BOTTOM;
384    end;
385end;
386{$endif}
387
388{------------------------------------------------------------------------------
389  function GtkWidgetIsA(Widget: PGtkWidget; AType: TGtkType): boolean;
390
391  The GTK_IS_XXX macro functions in the fpc gtk1.x bindings are not correct.
392  They just test the highest level.
393  This function checks as the real C macros.
394 ------------------------------------------------------------------------------}
395function GtkWidgetIsA(Widget: PGtkWidget; AType: TGtkType): boolean;
396begin
397  Result:=(Widget<>nil)
398    and (gtk_object_get_class(Widget)<>nil)
399    and gtk_type_is_a(gtk_class_get_type(gtk_object_get_class(Widget)), AType);
400end;
401
402{------------------------------------------------------------------------------
403  function GetWidgetClassName(Widget: PGtkWidget): string;
404
405  Returns the gtk class name of Widget.
406 ------------------------------------------------------------------------------}
407function GetWidgetClassName(Widget: PGtkWidget): string;
408var
409  AType: TGtkType;
410  ClassPGChar: Pgchar;
411  ClassLen: Integer;
412begin
413  Result:='';
414  if Widget=nil then begin
415    Result:='nil';
416    exit;
417  end;
418  if (gtk_object_get_class(Widget)=nil) then begin
419    Result:='<Widget without class>';
420    exit;
421  end;
422  AType:=gtk_class_get_type(gtk_object_get_class(Widget));
423  ClassPGChar:=gtk_type_name(AType);
424  if ClassPGChar=nil then begin
425    Result:='<Widget without classname>';
426    exit;
427  end;
428  ClassLen:=strlen(ClassPGChar);
429  SetLength(Result,ClassLen);
430  if ClassLen>0 then
431    Move(ClassPGChar[0],Result[1],ClassLen);
432end;
433
434function GetWidgetDebugReport(Widget: PGtkWidget): string;
435var
436  LCLObject: TObject;
437  AWinControl: TWinControl;
438  MainWidget: PGtkWidget;
439  WinWidgetInfo: PWinWidgetInfo;
440  FixedWidget: PGTKWidget;
441begin
442  if Widget = nil
443  then begin
444    Result := 'nil';
445    exit;
446  end;
447  Result := Format('%p=%s %s', [Pointer(Widget), GetWidgetClassName(Widget), WidgetFlagsToString(Widget)]);
448  LCLObject:=GetNearestLCLObject(Widget);
449  Result := Result + Format(' LCLObject=%p', [Pointer(LCLObject)]);
450  if LCLObject=nil then exit;
451  if LCLObject is TControl then
452    Result:=Result+'='+TControl(LCLObject).Name+':'+LCLObject.ClassName
453  else
454    Result:=Result+'='+LCLObject.ClassName;
455  if LCLObject is TWinControl then begin
456    AWinControl:=TWinControl(LCLObject);
457    if AWinControl.HandleAllocated then begin
458      MainWidget:=PGTKWidget(AWinControl.Handle);
459      if MainWidget=Widget
460      then Result:=Result+'<Is MainWidget>'
461      else Result:=Result+Format('<MainWidget=%p=%s>', [Pointer(MainWidget), GetWidgetClassName(MainWidget)]);
462      FixedWidget:=GetFixedWidget(MainWidget);
463      if FixedWidget=Widget then
464        Result:=Result+'<Is FixedWidget>';
465      WinWidgetInfo:=GetWidgetInfo(MainWidget,false);
466      if WinWidgetInfo<>nil then begin
467        if WinWidgetInfo^.CoreWidget = Widget then
468          Result:=Result+'<Is CoreWidget>';
469      end;
470    end
471    else begin
472      Result:=Result+'<NOT HandleAllocated>'
473    end;
474  end;
475end;
476
477function GetWindowDebugReport(AWindow: PGDKWindow): string;
478var
479  p: gpointer;
480  Widget: PGtkWidget;
481  WindowType: TGdkWindowType;
482  Width: Integer;
483  Height: Integer;
484  {$ifdef gtk1}
485  Visual: PGdkVisual;
486  {$endif}
487  TypeAsStr: String;
488begin
489  Result := DbgS(AWindow);
490  if AWindow = nil then Exit;
491
492  // window type
493  WindowType := gdk_window_get_type(AWindow);
494  case WindowType of
495    GDK_WINDOW_ROOT: TypeAsStr := 'Root';
496    GDK_WINDOW_TOPLEVEL: TypeAsStr := 'TopLvl';
497    GDK_WINDOW_CHILD: TypeAsStr := 'Child';
498    GDK_WINDOW_DIALOG: TypeAsStr := 'Dialog';
499    GDK_WINDOW_TEMP: TypeAsStr := 'Temp';
500    {$ifdef gtk1}
501    GDK_WINDOW_PIXMAP: TypeAsStr := 'Pixmap';
502    {$endif gtk1}
503    GDK_WINDOW_FOREIGN: TypeAsStr := 'Foreign';
504  else
505    TypeAsStr := 'Unknown';
506  end;
507  Result:=Result + ' Type=' + TypeAsStr;
508
509  DebugLn(Result);
510  // user data
511  if WindowType in [GDK_WINDOW_ROOT,GDK_WINDOW_TOPLEVEL,GDK_WINDOW_CHILD, GDK_WINDOW_DIALOG] then
512  begin
513    p := nil;
514    gdk_window_get_user_data(AWindow, @p);
515    if GtkWidgetIsA(PGTKWidget(p), gtk_widget_get_type) then
516    begin
517      Widget := PGTKWidget(p);
518      Result := Result + '<Widget[' + GetWidgetDebugReport(Widget) + ']>';
519    end
520    else
521      Result := Result + '<UserData=' + DbgS(p) + ']>';
522  end;
523
524  // size
525  gdk_window_get_size(AWindow, @Width, @Height);
526  Result := Result + ' Size=' + IntToStr(Width) + 'x' + IntToStr(Height);
527
528  {$ifdef gtk1}
529  // visual
530  Visual := gdk_window_get_visual(AWindow);
531  if Visual <> nil then
532    if WindowType in [GDK_WINDOW_PIXMAP] then
533      Result := Result + ' Depth=' + IntToStr(Visual^.bits_per_rgb);
534  {$endif gtk1}
535end;
536
537function GetStyleDebugReport(AStyle: PGTKStyle): string;
538begin
539  Result:='[';
540  if AStyle=nil then
541    Result:=Result+'nil'
542  else begin
543    Result:=Result+'FG[N]:='+GdkColorToStr(@AStyle^.fg[GTK_STATE_NORMAL])+' ';
544    Result:=Result+'BG[N]:='+GdkColorToStr(@AStyle^.bg[GTK_STATE_NORMAL])+' ';
545    Result:=Result+'Base[N]:='+GdkColorToStr(@AStyle^.base[GTK_STATE_NORMAL])+' ';
546    Result:=Result+'BG_Pixmap[N]:='+DbgS(AStyle^.bg_pixmap[GTK_STATE_NORMAL])+' ';
547    Result:=Result+'rc_style='+GetRCStyleDebugReport(AStyle^.rc_style);
548  end;
549  Result:=Result+']';
550end;
551
552function GetRCStyleDebugReport(AStyle: PGtkRcStyle): string;
553begin
554  Result:='[';
555  if AStyle=nil then
556    Result:=Result+'nil'
557  else begin
558    Result:=Result+'name="'+AStyle^.name+'" ';
559{$IFDEF GTK1}
560    Result:=Result+'font_name="'+AStyle^.font_name+'" ';
561    Result:=Result+'fontset_name="'+AStyle^.fontset_name+'" ';
562    Result:=Result+'FG[N]='+GdkColorToStr(@AStyle^.fg[GTK_STATE_NORMAL])+' ';
563    Result:=Result+'BG[N]='+GdkColorToStr(@AStyle^.bg[GTK_STATE_NORMAL])+' ';
564    Result:=Result+'Base[N]='+GdkColorToStr(@AStyle^.base[GTK_STATE_NORMAL])+' ';
565    Result:=Result+'flagi='+intTostr(AStyle^.color_flags[GTK_STATE_NORMAL])+' ';
566{$ELSE GTK2}
567    Result:=Result+'font_desc=['+GetPangoDescriptionReport(AStyle^.font_desc)+'] ';
568{$ENDIF GTK2}
569    Result:=Result+'bg_pixmap_name[N]="'+AStyle^.bg_pixmap_name[GTK_STATE_NORMAL]+'" ';
570{$IFDEF GTK1}
571    Result:=Result+'engine='+DbgS(AStyle^.engine);
572{$ENDIF GTK1}
573  end;
574  Result:=Result+']';
575end;
576
577{$IFDEF Gtk2}
578function GetPangoDescriptionReport(Desc: PPangoFontDescription): string;
579begin
580  if Desc=nil then begin
581    Result:='nil';
582  end else begin
583    Result:='family='+pango_font_description_get_family(Desc);
584    Result:=Result+' size='+IntToStr(pango_font_description_get_size(Desc));
585    Result:=Result+' weight='+IntToStr(pango_font_description_get_weight(Desc));
586    Result:=Result+' variant='+IntToStr(pango_font_description_get_variant(Desc));
587    Result:=Result+' style='+IntToStr(pango_font_description_get_style(Desc));
588    Result:=Result+' stretch='+IntToStr(pango_font_description_get_stretch(Desc));
589  end;
590end;
591{$ENDIF}
592
593function WidgetFlagsToString(Widget: PGtkWidget): string;
594begin
595  Result:='[';
596  if Widget=nil then
597    Result:=Result+'nil'
598  else begin
599    if GTK_WIDGET_REALIZED(Widget) then
600      Result:=Result+'R';
601    if GTK_WIDGET_MAPPED(Widget) then
602      Result:=Result+'M';
603    if GTK_WIDGET_VISIBLE(Widget) then
604      Result:=Result+'V';
605    if GTK_WIDGET_DRAWABLE(Widget) then
606      Result:=Result+'D';
607    if GTK_WIDGET_CAN_FOCUS(Widget) then
608      Result:=Result+'F';
609    if GTK_WIDGET_RC_STYLE(Widget) then
610      Result:=Result+'St';
611    if GTK_WIDGET_PARENT_SENSITIVE(Widget) then
612      Result:=Result+'Pr';
613    {$IFDEF Gtk2}
614    if GTK_WIDGET_NO_WINDOW(Widget) then
615      Result:=Result+'Nw';
616    if GTK_WIDGET_COMPOSITE_CHILD(Widget) then
617      Result:=Result+'Cc';
618    if GTK_WIDGET_APP_PAINTABLE(Widget) then
619      Result:=Result+'Ap';
620    if GTK_WIDGET_DOUBLE_BUFFERED(Widget) then
621      Result:=Result+'Db';
622    {$ENDIF}
623  end;
624  Result:=Result+']';
625end;
626
627function GdkColorToStr(Color: PGDKColor): string;
628begin
629  if Color=nil then
630    Result:='nil'
631  else
632    Result:='R'+HexStr(Color^.Red,4)+'G'+HexStr(Color^.Green,4)
633           +'B'+HexStr(Color^.Blue,4);
634end;
635
636function GetWidgetStyleReport(Widget: PGtkWidget): string;
637var
638  AStyle: PGtkStyle;
639  ARCStyle: PGtkRcStyle;
640begin
641  Result:='';
642  if Widget=nil then exit;
643  AStyle:=gtk_widget_get_style(Widget);
644  if AStyle=nil then begin
645    Result:='nil';
646    exit;
647  end;
648  Result:=Result+'attach_count='+dbgs(AStyle^.attach_count);
649  ARCStyle:=AStyle^.rc_style;
650  if ARCStyle=nil then begin
651    Result:=Result+' rc_style=nil';
652  end else begin
653    Result:=Result+' rc_style=[';
654{$IFDEF GTK1}
655    Result:=Result+ARCStyle^.font_name+',';
656    Result:=Result+ARCStyle^.fontset_name+',';
657{$ELSE GTK1}
658    Result:=Result+GetPangoDescriptionReport(AStyle^.font_desc);
659{$ENDIF GTK1}
660    Result:=Result+']';
661  end;
662end;
663
664{------------------------------------------------------------------------------
665  function WidgetIsDestroyingHandle(Widget: PGtkWidget): boolean;
666
667  Tests if Destruction Mark is set.
668 ------------------------------------------------------------------------------}
669function WidgetIsDestroyingHandle(Widget: PGtkWidget): boolean;
670begin
671  Result:=gtk_object_get_data(PGtkObject(Widget),'LCLDestroyingHandle')<>nil;
672end;
673
674{------------------------------------------------------------------------------
675  procedure SetWidgetIsDestroyingHandle(Widget: PGtkWidget);
676
677  Marks widget for destruction.
678 ------------------------------------------------------------------------------}
679procedure SetWidgetIsDestroyingHandle(Widget: PGtkWidget);
680begin
681  gtk_object_set_data(PGtkObject(Widget),'LCLDestroyingHandle',Widget);
682end;
683
684{------------------------------------------------------------------------------
685  function ComponentIsDestroyingHandle(AWinControl: TWinControl): boolean;
686
687  Tests if Destruction Mark is set.
688 ------------------------------------------------------------------------------}
689function ComponentIsDestroyingHandle(AWinControl: TWinControl): boolean;
690begin
691  Result:=
692    (AWinControl<>nil) and (AWinControl is TWinControl)
693    and (AWinControl.HandleAllocated)
694    and WidgetIsDestroyingHandle(PGtkWidget(AWinControl.Handle));
695end;
696
697{------------------------------------------------------------------------------
698  function LockOnChange(GtkObject: PGtkObject; LockOffset: integer): integer;
699
700  Adds LockOffset to the OnChangeLock and returns the result.
701 ------------------------------------------------------------------------------}
702function LockOnChange(GtkObject: PGtkObject; LockOffset: integer): integer;
703var
704  Info: PWidgetInfo;
705begin
706  Info := GetWidgetInfo(GtkObject, True);
707  if Info = nil
708  then begin
709    Result := 0;
710    Exit;
711  end;
712
713  Inc(Info^.ChangeLock, LockOffset);
714  Result := Info^.ChangeLock;
715end;
716
717procedure SetFormShowInTaskbar(AForm: TCustomForm;
718  const AValue: TShowInTaskbar);
719var
720  Enable: boolean;
721  Widget: PGtkWidget;
722begin
723  if (AForm.Parent <> nil) or
724     (AForm.ParentWindow <> 0) or
725     not (AForm.HandleAllocated) then Exit;
726
727  Widget := PGtkWidget(AForm.Handle);
728  // if widget not yet realized then exit
729  if Widget^.Window = nil then
730    Exit;
731
732  Enable := AValue <> stNever;
733  {if (AValue = stDefault)
734  and (Application<>nil) and (Application.MainForm <> nil)
735  and (Application.MainForm <> AForm) then
736    Enable := false;}
737
738  //debugln('SetGtkWindowShowInTaskbar ',DbgSName(AForm),' ',dbgs(Enable));
739  // The button reappears in some (still unknown) situations, but has the
740  //'skip-taskbar-hint' property still set to True, so invoking the function
741  //doesn't have an effect. Resetting the property makes it work.
742  {$IFNDEF GTK1}
743  if (not Enable) and gtk_window_get_skip_taskbar_hint(PGtkWindow(Widget)) then
744    gtk_window_set_skip_taskbar_hint(PGtkWindow(Widget), False);
745  {$ENDIF}
746  SetGtkWindowShowInTaskbar(PGtkWindow(Widget), Enable);
747end;
748
749procedure SetGtkWindowShowInTaskbar(AGtkWindow: PGtkWindow; Value: boolean);
750begin
751  {$IFDEF GTK1}
752  if PgtkWidget(AGtkWindow)^.Window=nil then begin
753    // widget not yet realized
754    exit;
755  end;
756  GDK_WINDOW_SHOW_IN_TASKBAR(PGdkWindowPrivate(PGtkWidget(AGtkWindow)^.Window),
757                             Value);
758  {$ELSE}
759  //DebugLn(['SetGtkWindowShowInTaskbar ',GetWidgetDebugReport(PGtkWidget(AGtkWindow)),' ',Value]);
760  gtk_window_set_skip_taskbar_hint(AGtkWindow, not Value);
761  {$ENDIF}
762end;
763
764procedure SetWindowFullScreen(AForm: TCustomForm; const AValue: Boolean);
765{$IFDEF GTK1}
766var
767  XDisplay: PDisplay;
768  XScreen: PScreen;
769  XRootWindow,
770  XWindow: TWindow;
771  XEvent: TXClientMessageEvent;
772  _NET_WM_STATE: Integer;
773  //_NET_WM_STATE_MODAL: Integer;
774  //_NET_WM_STATE_ABOVE: Integer;
775  //_NET_WM_STATE_FULLSCREEN: Integer;
776  _NET_WM_STATE_ATOMS: array [0..2] of Integer;
777  I: Integer;
778{$ENDIF}
779begin
780  {$IFDEF GTK2}
781  If AValue then
782    GTK_Window_FullScreen(PGTKWindow(AForm.Handle))
783  else
784    GTK_Window_UnFullScreen(PGTKWindow(AForm.Handle));
785  {$ENDIF}
786  {$IFDEF GTK1}
787  XDisplay := gdk_display;
788  XScreen := XDefaultScreenOfDisplay(xdisplay);
789  XRootWindow := XRootWindowOfScreen(xscreen);
790  XWindow := FormToX11Window(AForm);
791
792  _NET_WM_STATE := XInternAtom(xdisplay, '_NET_WM_STATE', false);
793  //_NET_WM_STATE_MODAL := XInternAtom(xdisplay, '_NET_WM_STATE_MODAL', false);
794  //_NET_WM_STATE_ABOVE := XInternAtom(xdisplay, '_NET_WM_STATE_ABOVE', false);
795  //_NET_WM_STATE_FULLSCREEN := XInternAtom(xdisplay, '_NET_WM_STATE_FULLSCREEN', false);
796  _NET_WM_STATE_ATOMS[0] := XInternAtom(xdisplay, '_NET_WM_STATE_MODAL', false);
797  _NET_WM_STATE_ATOMS[1] := XInternAtom(xdisplay, '_NET_WM_STATE_ABOVE', false);
798  _NET_WM_STATE_ATOMS[2] := XInternAtom(xdisplay, '_NET_WM_STATE_FULLSCREEN', false);
799
800  for I := 0 to 2 do begin
801    XEvent._type := ClientMessage;
802    XEvent.window := XWindow;
803    XEvent.message_type := _NET_WM_STATE;
804    XEvent.format := 32;
805    XEvent.data.l[0] := Ord(AValue);// 0=Remove 1=Add 2=Toggle
806    XEvent.data.l[1] := _NET_WM_STATE_ATOMS[I];
807
808    XSendEvent(XDisplay, XRootWindow, False, SubstructureNotifyMask, PXEvent(@XEvent));
809  end;
810   {$ENDIF}
811end;
812
813procedure GrabKeyBoardToForm(AForm: TCustomForm);
814begin
815  {$IFDEF HasX}
816  XGrabKeyboard(gdk_display, FormToX11Window(AForm), true, GrabModeASync,
817                GrabModeASync, CurrentTime);
818  {$ENDIF}
819end;
820
821procedure ReleaseKeyBoardFromForm(AForm: TCustomForm);
822begin
823  {$IFDEF HasX}
824  XUngrabKeyboard(gdk_display, CurrentTime);
825  {$ENDIF}
826end;
827
828procedure GrabMouseToForm(AForm: TCustomForm);
829{$IFDEF HasX}
830var
831  eventMask: LongInt;
832begin
833  eventMask := ButtonPressMask or ButtonReleaseMask
834               or PointerMotionMask or PointerMotionHintMask;
835
836  XGrabPointer(gdk_display, FormToX11Window(AForm), true,
837               eventMask, GrabModeASync, GrabModeAsync,  FormToX11Window(AForm),
838               None, CurrentTime);
839end;
840{$ELSE}
841begin
842end;
843{$ENDIF}
844
845procedure ReleaseMouseFromForm(AForm: TCustomForm);
846begin
847  {$IFDEF HasX}
848  XUngrabPointer(gdk_display, CurrentTime);
849  {$ENDIF}
850end;
851
852{$IFDEF HasX}
853function FormToX11Window(const AForm: TCustomForm): X.TWindow;
854var
855  Widget: PGtkWidget;
856begin
857  Result:=0;
858  if (AForm=nil) or (not AForm.HandleAllocated) then exit;
859  Widget:=PGtkWidget(AForm.Handle);
860  if Widget^.window = nil then exit;
861  {$ifdef gtk1}
862  Result := PGdkWindowPrivate(Widget^.window)^.xwindow;
863  {$else}
864  Result := gdk_window_xwindow(Widget^.window);
865  {$endif}
866end;
867{$ENDIF}
868
869procedure SetLabelAlignment(LabelWidget: PGtkLabel;
870  const NewAlignment: TAlignment);
871const
872  cLabelAlignX : array[TAlignment] of gfloat = (0.0, 1.0, 0.5);
873  cLabelAlignY : array[TTextLayout] of gfloat = (0.0, 0.5, 1.0);
874  cLabelAlign : array[TAlignment] of TGtkJustification =
875    (GTK_JUSTIFY_LEFT, GTK_JUSTIFY_RIGHT, GTK_JUSTIFY_CENTER);
876begin
877  gtk_label_set_justify(LabelWidget, cLabelAlign[NewAlignment]);
878  gtk_misc_set_alignment(GTK_MISC(LabelWidget), cLabelAlignX[NewAlignment],
879                        cLabelAlignY[tlTop]);
880end;
881
882{------------------------------------------------------------------------------
883  function GtkPaintMessageToPaintMessage(var GtkPaintMsg: TLMGtkPaint;
884    FreeGtkPaintMsg: boolean): TLMPaint;
885
886  Converts a LM_GTKPAINT message to a LM_PAINT message
887 ------------------------------------------------------------------------------}
888function GtkPaintMessageToPaintMessage(var GtkPaintMsg: TLMGtkPaint;
889  FreeGtkPaintMsg: boolean): TLMPaint;
890var
891  PS : PPaintStruct;
892  Widget: PGtkWidget;
893begin
894  FillByte(Result,SizeOf(Result),0);
895  Result.Msg := LM_PAINT;
896  New(PS);
897  FillChar(PS^, SizeOf(TPaintStruct), 0);
898  Widget := GtkPaintMsg.Data.Widget;
899  If GtkPaintMsg.Data.RepaintAll then
900    PS^.rcPaint := Rect(0, 0, Widget^.Allocation.Width, Widget^.Allocation.Height)
901  else
902    PS^.rcPaint := GtkPaintMsg.Data.Rect;
903
904  Result.DC := BeginPaint(THandle(PtrUInt(Widget)), PS^);
905  Result.PaintStruct := PS;
906  Result.Result := 0;
907  if FreeGtkPaintMsg then
908    FreeThenNil(GtkPaintMsg.Data);
909end;
910
911procedure FinalizePaintMessage(Msg: PLMessage);
912var
913  PS: PPaintStruct;
914  DC: TGtkDeviceContext;
915begin
916  if (Msg^.Msg = LM_PAINT) then
917  begin
918    if Msg^.LParam <> 0 then
919    begin
920      PS := PPaintStruct(Msg^.LParam);
921      if Msg^.WParam <> 0 then
922        DC := TGtkDeviceContext(Msg^.WParam)
923      else
924        DC := TGtkDeviceContext(PS^.hdc);
925      EndPaint(THandle(PtrUInt(DC.Widget)), PS^);
926      Dispose(PS);
927      Msg^.LParam:=0;
928      Msg^.WParam:=0;
929    end
930    else
931    if Msg^.WParam<>0 then
932    begin
933      ReleaseDC(0, Msg^.WParam);
934      Msg^.WParam := 0;
935    end;
936  end else
937  if Msg^.Msg = LM_GTKPAINT then
938    FreeThenNil(TLMGtkPaintData(Msg^.WParam));
939end;
940
941procedure FinalizePaintTagMsg(Msg: PMsg);
942var
943  PS: PPaintStruct;
944  DC: TGtkDeviceContext;
945begin
946  if (Msg^.Message = LM_PAINT) then
947  begin
948    if Msg^.LParam <> 0 then
949    begin
950      PS := PPaintStruct(Msg^.LParam);
951      if Msg^.WParam<>0 then
952        DC := TGtkDeviceContext(Msg^.WParam)
953      else
954        DC := TGtkDeviceContext(PS^.hdc);
955      EndPaint(THandle(PtrUInt(DC.Widget)), PS^);
956      Dispose(PS);
957      Msg^.LParam:=0;
958      Msg^.WParam:=0;
959    end else
960    if Msg^.WParam<>0 then
961    begin
962      ReleaseDC(0, Msg^.WParam);
963      Msg^.WParam:=0;
964    end;
965  end else
966  if Msg^.Message = LM_GTKPAINT then
967    FreeThenNil(TObject(Msg^.WParam));
968end;
969
970procedure SetGCRasterOperation(TheGC: PGDKGC; Rop: Cardinal);
971begin
972  case ROP of
973    WHITENESS,
974    BLACKNESS,
975    SRCCOPY :
976      gdk_gc_set_function(TheGC, GDK_Copy);
977    SRCPAINT :
978      gdk_gc_set_function(TheGC, GDK_NOOP);
979    SRCAND :
980      gdk_gc_set_function(TheGC, GDK_Clear);
981    SRCINVERT :
982      gdk_gc_set_function(TheGC, GDK_XOR);
983    SRCERASE :
984      gdk_gc_set_function(TheGC, GDK_AND);
985    NOTSRCCOPY :
986      gdk_gc_set_function(TheGC, GDK_OR_REVERSE);
987    NOTSRCERASE :
988      gdk_gc_set_function(TheGC, GDK_AND);
989    MERGEPAINT :
990      gdk_gc_set_function(TheGC, GDK_Copy_Invert);
991    DSTINVERT :
992      gdk_gc_set_function(TheGC, GDK_INVERT);
993    else begin
994      gdk_gc_set_function(TheGC, GDK_COPY);
995      DebugLn('WARNING: [SetRasterOperation] Got unknown/unsupported CopyMode!!');
996    end;
997  end;
998end;
999
1000procedure MergeClipping(DestinationDC: TGtkDeviceContext; DestinationGC: PGDKGC;
1001  X,Y,Width,Height: integer; ClipMergeMask: PGdkBitmap;
1002  ClipMergeMaskX, ClipMergeMaskY: integer;
1003  var NewClipMask: PGdkBitmap);
1004// merge ClipMergeMask into the destination clipping mask at the
1005// destination rectangle
1006var
1007  temp_gc : PGDKGC;
1008  temp_color : TGDKColor;
1009  RGNType : Longint;
1010  OffsetXY: TPoint;
1011  //ClipMergeMaskWidth, ClipMergeMaskHeight: integer;
1012begin
1013  {$IFDEF VerboseStretchCopyArea}
1014  DebugLn('MergeClipping START DestinationDC=',DbgS(DestinationDC),
1015    ' DestinationGC=',DbgS(DestinationGC),
1016    ' X='+dbgs(X),' Y='+dbgs(Y),' Width='+dbgs(Width),' Height='+dbgs(Height),
1017    ' ClipMergeMask=',DbgS(ClipMergeMask),
1018    ' ClipMergeMaskX=',dbgs(ClipMergeMaskX),' ClipMergeMaskY=',dbgs(ClipMergeMaskY));
1019  {$ENDIF}
1020
1021  // activate clipping region of destination
1022  DestinationDC.SelectRegion;
1023  NewClipMask := nil;
1024  if (ClipMergeMask = nil) then exit;
1025
1026  BeginGDKErrorTrap;
1027  // create temporary mask with the size of the destination rectangle
1028  NewClipMask := PGdkBitmap(gdk_pixmap_new(nil, width, height, 1));
1029  // create temporary GC for combination mask
1030  temp_gc := gdk_gc_new(NewClipMask);
1031  gdk_gc_set_clip_region(temp_gc, nil); // no default clipping
1032  gdk_gc_set_clip_rectangle(temp_gc, nil);
1033
1034  // clear mask
1035  temp_color.pixel := 0;
1036  gdk_gc_set_foreground(temp_gc, @temp_color);
1037  gdk_draw_rectangle(NewClipMask, temp_gc, 1, 0, 0, width+1, height+1);
1038
1039  // copy the destination clipping mask into the temporary mask
1040  with DestinationDC do begin
1041    If (ClipRegion <> nil) then begin
1042      RGNType := RegionType(ClipRegion^.GDIRegionObject);
1043      If (RGNType <> ERROR) and (RGNType <> NULLREGION) then begin
1044        // destination has a clipping mask
1045        {$IFDEF VerboseStretchCopyArea}
1046        DebugLn('MergeClipping Destination has clipping mask -> apply to temp GC');
1047        {$ENDIF}
1048        // -> copy the destination clipping mask to the temporary mask
1049        //    The X,Y coordinate in the destination relates to
1050        //    0,0 in the temporary mask.
1051        //    The clip region of dest is always at 0,0 in dest
1052        OffsetXY:=Point(-X,-Y);
1053        // 1. Move the region
1054        gdk_region_offset(ClipRegion^.GDIRegionObject,OffsetXY.X,OffsetXY.Y);
1055        // 2. Apply region to temporary mask
1056        gdk_gc_set_clip_region(temp_gc, ClipRegion^.GDIRegionObject);
1057        // 3. Undo moving the region
1058        gdk_region_offset(ClipRegion^.GDIRegionObject,-OffsetXY.X,-OffsetXY.Y);
1059      end;
1060    end;
1061  end;
1062
1063  // merge the source clipping mask into the temporary mask
1064  //gdk_window_get_size(ClipMergeMask,@ClipMergeMaskWidth,@ClipMergeMaskHeight);
1065  //DebugLn('MergeClipping A MergeMask Size=',ClipMergeMaskWidth,',',ClipMergeMaskHeight);
1066  gdk_draw_pixmap(NewClipMask, temp_gc,
1067                  ClipMergeMask, ClipMergeMaskX, ClipMergeMaskY, 0, 0, -1, -1);
1068
1069  // free the temporary GC
1070  gdk_gc_destroy(temp_gc);
1071
1072  // apply the new mask to the destination GC
1073  // The new mask has only the size of the destination rectangle, not of
1074  // the whole destination. Apply it to destination and move it to the right
1075  // position.
1076  gdk_gc_set_clip_mask(DestinationGC, NewClipMask);
1077  gdk_gc_set_clip_origin(DestinationGC, x, y);
1078  EndGDKErrorTrap;
1079end;
1080
1081function CreatePixbufFromImageAndMask(ASrc: PGdkDrawable; ASrcX, ASrcY, ASrcWidth,
1082  ASrcHeight: integer; ASrcColorMap: PGdkColormap; ASrcMask: PGdkBitmap): PGdkPixbuf;
1083
1084  procedure Warn(const AText: String);
1085  begin
1086    DebugLn('[WARNING] ScalePixmapAndMask: ' + AText);
1087  end;
1088
1089  procedure ApplyMask(APixels, AMask: pguchar);
1090  type
1091    TPixbufPixel = record
1092      R,G,B,A: Byte;
1093    end;
1094  var
1095    RGBA: ^TPixbufPixel absolute APixels;
1096    Mask: ^TPixbufPixel absolute AMask;
1097    n: Integer;
1098  begin
1099    for n := 0 to (ASrcHeight * ASrcWidth) - 1 do
1100    begin
1101      if (Mask^.B = 0) and (Mask^.G = 0) and (Mask^.R = 0)
1102      then RGBA^.A := 0;
1103      inc(RGBA);
1104      inc(Mask);
1105    end;
1106  end;
1107
1108var
1109  Msk: PGdkPixbuf;
1110  FullSrcWidth, FullSrcHeight: integer;
1111begin
1112  Result := nil;
1113  if ASrc = nil then Exit;
1114
1115  gdk_window_get_size(PGDKWindow(ASrc), @FullSrcWidth, @FullSrcHeight);
1116  if ASrcX + ASrcWidth > FullSrcWidth
1117  then begin
1118    Warn('ASrcX+ASrcWidth>FullSrcWidth');
1119  end;
1120  if ASrcY + ASrcHeight > FullSrcHeight
1121  then begin
1122    Warn('ASrcY+ASrcHeight>FullSrcHeight');
1123  end;
1124
1125  // Creating PixBuf from pixmap
1126  Result := CreatePixbufFromDrawable(ASrc, ASrcColorMap, ASrcMask <> nil, ASrcX, ASrcY, 0, 0, ASrcWidth, ASrcHeight);
1127  if Result = nil
1128  then begin
1129    Warn('Result=nil');
1130    Exit;
1131  end;
1132  //DbgDumpPixbuf(Result, 'Pixbuf from Source');
1133
1134  // Apply mask if present
1135  if ASrcMask <> nil
1136  then begin
1137    if gdk_pixbuf_get_rowstride(Result) <> ASrcWidth shl 2
1138    then begin
1139      Warn('rowstride <> 4*width');
1140      gdk_pixbuf_unref(Result);
1141      Result := nil;
1142      Exit;
1143    end;
1144
1145    Msk := CreatePixbufFromDrawable(ASrcMask, nil, True, ASrcX, ASrcY, 0, 0, ASrcWidth, ASrcHeight);
1146    ApplyMask(gdk_pixbuf_get_pixels(Result), gdk_pixbuf_get_pixels(Msk));
1147    gdk_pixbuf_unref(Msk);
1148  end;
1149end;
1150
1151function ScalePixmapAndMask(AScaleGC: PGDKGC; AScaleMethod: TGdkInterpType;
1152  ASrc: PGdkPixmap; ASrcX, ASrcY, ASrcWidth, ASrcHeight: integer;
1153  ASrcColorMap: PGdkColormap; ASrcMask: PGdkBitmap;
1154  ADstWidth, ADstHeight: Integer; FlipHorz, FlipVert: Boolean;
1155  out ADst, ADstMask: PGdkPixmap) : Boolean;
1156
1157  procedure Warn(const AText: String);
1158  begin
1159    DebugLn('[WARNING] ScalePixmapAndMask: ' + AText);
1160  end;
1161
1162var
1163  ScaleSrc, ScaleDst: PGdkPixbuf;
1164begin
1165  Result := False;
1166  ADst:=nil;
1167  ADstMask:=nil;
1168
1169  // Creating PixBuf from pixmap
1170  ScaleSrc := CreatePixbufFromImageAndMask(ASrc, ASrcX, ASrcY, ASrcWidth, ASrcHeight,
1171    ASrcColorMap, ASrcMask);
1172
1173  // Scaling PixBuf
1174  ScaleDst := gdk_pixbuf_scale_simple(ScaleSrc, ADstWidth, ADstHeight, AScaleMethod);
1175  gdk_pixbuf_unref(ScaleSrc);
1176  if ScaleDst = nil
1177  then begin
1178    Warn('ScaleDst=nil');
1179    exit;
1180  end;
1181
1182  // flip if needed
1183  if FlipHorz then
1184  begin
1185    {$IFNDEF GTK1}
1186    ScaleSrc := ScaleDst;
1187    ScaleDst := gdk_pixbuf_flip(ScaleSrc, True);
1188    gdk_pixbuf_unref(ScaleSrc);
1189    if ScaleDst = nil
1190    then begin
1191      Warn('ScaleDst=nil');
1192      exit;
1193    end;
1194    {$ELSE}
1195    // TODO: implement flipping for gtk1
1196    {$ENDIF}
1197  end;
1198
1199  if FlipVert then
1200  begin
1201    {$IFNDEF GTK1}
1202    ScaleSrc := ScaleDst;
1203    ScaleDst := gdk_pixbuf_flip(ScaleSrc, False);
1204    gdk_pixbuf_unref(ScaleSrc);
1205    if ScaleDst = nil
1206    then begin
1207      Warn('ScaleDst=nil');
1208      exit;
1209    end;
1210    {$ELSE}
1211    // TODO: implement flipping for gtk1
1212    {$ENDIF}
1213  end;
1214
1215//  BeginGDKErrorTrap;
1216
1217  // Creating pixmap from scaled pixbuf
1218  gdk_pixbuf_render_pixmap_and_mask(ScaleDst, ADst, ADstMask, $80);
1219
1220//  EndGDKErrorTrap;
1221  gdk_pixbuf_unref(ScaleDst);
1222  Result := True;
1223end;
1224
1225{$IFDEF VerboseGtkToDos}{$note remove when gtk native imagelist will be ready}{$ENDIF}
1226procedure DrawImageListIconOnWidget(ImgList: TCustomImageList;
1227  Index: integer; AEffect: TGraphicsDrawEffect; DestWidget: PGTKWidget;
1228  CenterHorizontally, CenterVertically: boolean;
1229  DestLeft, DestTop: integer);
1230// draw icon of imagelist centered on gdkwindow
1231var
1232  Bitmap: TBitmap;
1233  ImageWidth: Integer;
1234  ImageHeight: Integer;
1235  WindowWidth, WindowHeight: integer;
1236  DestDC: HDC;
1237  Offset: TPoint;
1238  {$ifdef gtk2}
1239  FixedWidget: PGtkWidget;
1240  {$ENDIF}
1241begin
1242  if ImgList=nil then exit;
1243  if (Index<0) or (Index>=ImgList.Count) then exit;
1244  if (DestWidget=nil) then exit;
1245  ImageWidth:=ImgList.Width;
1246  ImageHeight:=ImgList.Height;
1247  Bitmap := TBitmap.Create;
1248  ImgList.GetBitmap(Index, Bitmap, AEffect);
1249  if (ImageWidth<1) or (ImageHeight<1) then exit;
1250
1251  WindowWidth := DestWidget^.allocation.width;
1252  WindowHeight := DestWidget^.allocation.height;
1253
1254  Offset := Point(0, 0);
1255  {$ifdef gtk2}
1256  // if our widget is placed on non-window fixed then we should substract its allocation here
1257  // since in GetDC we will get this difference in offset
1258  FixedWidget := GetFixedWidget(DestWidget);
1259  if (FixedWidget <> nil) and GTK_WIDGET_NO_WINDOW(FixedWidget) then
1260    Offset := Point(FixedWidget^.allocation.x, FixedWidget^.allocation.y);
1261  {$endif}
1262
1263  if CenterHorizontally then
1264    DestLeft := DestWidget^.allocation.x - Offset.x + ((WindowWidth-ImageWidth) div 2);
1265  if CenterVertically then
1266    DestTop := DestWidget^.allocation.y - Offset.y +  ((WindowHeight-ImageHeight) div 2);
1267  DestDC := GetDC(HDC(PtrUInt(DestWidget)));
1268
1269  //DebugLn('DrawImageListIconOnWidget B DestXY=',DestLeft,',',DestTop,
1270  //  ' DestWindowSize=',WindowWidth,',',WindowWidth,
1271  //  ' SrcRect=',ImageRect.Left,',',ImageRect.Top,',',ImageWidth,'x',ImageHeight);
1272  StretchBlt(DestDC, DestLeft, DestTop, ImageWidth, ImageHeight,
1273    Bitmap.Canvas.Handle, 0, 0, ImageWidth, ImageHeight,
1274    SRCCOPY);
1275  ReleaseDC(HDC(PtrUInt(DestWidget)),DestDC);
1276  Bitmap.Free;
1277end;
1278
1279procedure DrawImageListIconOnWidget(ImgList: TCustomImageList;
1280  Index: integer; DestWidget: PGTKWidget);
1281begin
1282  DrawImageListIconOnWidget(ImgList, Index, gdeNormal, DestWidget, true, true, 0, 0);
1283end;
1284
1285function GetGdkImageBitsPerPixel(Image: PGdkImage): cardinal;
1286begin
1287  Result:=Image^.bpp;
1288  if Result<Image^.Depth then
1289    Result:=Result*8;
1290end;
1291
1292{------------------------------------------------------------------------------
1293  Function: CreateGtkBitmapMask
1294  Params:  AImageMask: Then internal gtkBitmap for imagemask
1295           AMask: External gtkbitmap
1296  Returns: A GdkBitmap
1297
1298  This function returns a bitmap based on the internal alpha bitmap and the
1299  maskhandle passed.
1300  If both internal mask and the given mask is valid, then a new bitmap is created
1301  else either internal mask or given mask (with increased reference)
1302 ------------------------------------------------------------------------------}
1303function CreateGdkMaskBitmap(AImageMask, AMask: PGdkBitmap): PGdkBitmap;
1304var
1305  W, H: Integer;
1306  GC: PGdkGc;
1307begin
1308  Result := nil;
1309  if (AImageMask = nil) and (AMask = nil) then Exit;
1310
1311  if AMask = nil
1312  then begin
1313    Result := AImageMask;
1314    gdk_pixmap_ref(Result);
1315    Exit;
1316  end;
1317
1318  if AImageMask = nil
1319  then begin
1320    Result := AMask;
1321    gdk_pixmap_ref(Result);
1322    Exit;
1323  end;
1324
1325  // if we are here we need a combination (=AND) of both masks
1326  gdk_window_get_size(AImageMask, @W, @H);
1327  Result := gdk_pixmap_new(nil, W, H, 1);
1328  GC := gdk_gc_new(Result);
1329  // copy image mask
1330  gdk_draw_pixmap(Result, GC, AImageMask, 0, 0, 0, 0, -1, -1);
1331  // and with mask
1332  gdk_gc_set_function(GC, GDK_AND);
1333  gdk_draw_pixmap(Result, GC, AMask, 0, 0, 0, 0, -1, -1);
1334  gdk_gc_unref(GC);
1335end;
1336
1337{------------------------------------------------------------------------------
1338  Function: CreateGdkMaskBitmap
1339  Params:  AImage: Handle to the (LCL) bitmap image
1340           AMask: Handle to the (LCL) bitmap mask
1341  Returns: A GdkBitmap
1342
1343  This function returns a bitmap based on the internal alpha bitmap of the
1344  image handle and the maskhandle passed.
1345  If only internal mask is valid, then that one is returned (with increased reference)
1346  Otherwise a new bitmap is created.
1347 ------------------------------------------------------------------------------}
1348function CreateGdkMaskBitmap(AImage, AMask: HBITMAP): PGdkBitmap;
1349var
1350  GdiImage: PGdiObject absolute AImage;
1351  GdiMask: PGdiObject absolute AMask;
1352  W, H: Integer;
1353  GC: PGdkGc;
1354begin
1355  Result := nil;
1356  if (AImage = 0) and (AMask = 0) then Exit;
1357
1358  if GdiMask = nil
1359  then begin
1360    if GdiImage^.GDIBitmapType = gbPixmap
1361    then Result := GdiImage^.GDIPixmapObject.Mask;
1362    if Result <> nil
1363    then gdk_pixmap_ref(Result);
1364//    DbgDumpBitmap(Result, 'CreateGdkMaskBitmap - Internal mask');
1365    Exit;
1366  end;
1367
1368  if GdiMask^.GDIBitmapType <> gbBitmap
1369  then begin
1370    DebugLN('[WARNING] CreateGtkBitmapMask: GDIBitmapType <> dbBitmap');
1371    Exit;
1372  end;
1373
1374  if (GdiImage = nil)
1375  or (GdiImage^.GDIBitmapType <> gbPixmap)
1376  or (GdiImage^.GDIPixmapObject.Mask = nil)
1377  then begin
1378    gdk_window_get_size(GdiMask^.GDIBitmapObject, @W, @H);
1379    Result := gdk_pixmap_new(nil, W, H, 1);
1380    GC := gdk_gc_new(Result);
1381    gdk_gc_set_function(GC, {$ifdef gtk1}11{$else}GDK_COPY_INVERT{$endif});
1382    gdk_draw_pixmap(Result, GC, GdiMask^.GDIBitmapObject, 0, 0, 0, 0, -1, -1);
1383    gdk_gc_unref(GC);
1384
1385    //DbgDumpBitmap(Result, 'CreateGdkMaskBitmap - Mask');
1386    Exit;
1387  end;
1388
1389  // if we are here we need a combination (=AND) of both masks
1390  gdk_window_get_size(GdiImage^.GDIPixmapObject.Mask, @W, @H);
1391  Result := gdk_pixmap_new(nil, W, H, 1);
1392  GC := gdk_gc_new(Result);
1393  // copy image mask
1394  gdk_draw_pixmap(Result, GC, GdiImage^.GDIPixmapObject.Mask, 0, 0, 0, 0, -1, -1);
1395  // and with mask
1396  gdk_gc_set_function(GC, {$ifdef gtk1}6{$else}GDK_AND_INVERT{$endif});
1397  gdk_draw_pixmap(Result, GC, GdiMask^.GDIBitmapObject, 0, 0, 0, 0, -1, -1);
1398  gdk_gc_unref(GC);
1399
1400//  DbgDumpBitmap(Result, 'CreateGdkMaskBitmap - Combi');
1401end;
1402
1403function ExtractGdkBitmap(Bitmap: PGdkBitmap; const SrcRect: TRect): PGdkBitmap;
1404var
1405  MaxRect: TRect;
1406  SourceRect: TRect;
1407  SrcWidth: Integer;
1408  SrcHeight: Integer;
1409  GC: PGdkGC;
1410begin
1411  Result:=nil;
1412  if Bitmap=nil then exit;
1413  MaxRect:=Rect(0,0,0,0);
1414  gdk_window_get_size(Bitmap,@MaxRect.Right,@MaxRect.Bottom);
1415  IntersectRect(SourceRect,SrcRect,MaxRect);
1416  SrcWidth:=SourceRect.Right-SourceRect.Left;
1417  SrcHeight:=SourceRect.Bottom-SourceRect.Top;
1418  DebugLn('ExtractGdkBitmap SourceRect=',dbgs(SourceRect));
1419  if (SrcWidth<1) or (SrcHeight<1) then exit;
1420  Result:= gdk_pixmap_new(nil, SrcWidth, SrcHeight, 1);
1421  GC := GDK_GC_New(Result);
1422  gdk_window_copy_area(Result,GC,0,0,Bitmap,
1423                       SourceRect.Left,SourceRect.Top,SrcWidth,SrcHeight);
1424  GDK_GC_Unref(GC);
1425end;
1426
1427procedure CheckGdkImageBitOrder(AImage: PGdkImage; AData: PByte; ADataCount: Integer);
1428var
1429  b, count: Byte;
1430  c: Cardinal;
1431
1432{$ifdef hasx}
1433  XImage: XLib.PXimage;
1434{$endif}
1435begin
1436{$ifdef hasx}
1437  if AImage = nil then Exit;
1438
1439  XImage := gdk_x11_image_get_ximage(AImage);
1440  if XImage^.bitmap_bit_order = LSBFirst then Exit;
1441{$endif}
1442
1443  // on windows or bigendian servers the bits need to be swapped
1444
1445  // align dataptr first
1446  count := PtrUint(AData) and 3;
1447  if count > ADataCount then count := ADataCount;
1448  Dec(ADataCount, Count);
1449
1450  while (Count > 0) do
1451  begin
1452    // reduce dereferences
1453    b      := AData^;
1454    b      := ((b shr 4) and $0F) or ((b shl 4) and $F0);
1455    b      := ((b shr 2) and $33) or ((b shl 2) and $CC);
1456    AData^ := ((b shr 1) and $55) or ((b shl 1) and $AA);
1457
1458    Dec(Count);
1459    Inc(AData);
1460  end;
1461
1462  // get remainder
1463  Count := ADataCount and 3;
1464
1465  // now swap bits with 4 in a row
1466  ADataCount := ADataCount shr 2;
1467  while (ADataCount > 0) do
1468  begin
1469    // reduce dereferences
1470    c                 := PCardinal(AData)^;
1471    c                 := ((c shr 4) and $0F0F0F0F) or ((c shl 4) and $F0F0F0F0);
1472    c                 := ((c shr 2) and $33333333) or ((c shl 2) and $CCCCCCCC);
1473    PCardinal(AData)^ := ((c shr 1) and $55555555) or ((c shl 1) and $AAAAAAAA);
1474
1475    Dec(ADataCount);
1476    Inc(AData, 4);
1477  end;
1478
1479  // process remainder
1480  while (Count > 0) do
1481  begin
1482    // reduce dereferences
1483    b      := AData^;
1484    b      := ((b shr 4) and $0F) or ((b shl 4) and $F0);
1485    b      := ((b shr 2) and $33) or ((b shl 2) and $CC);
1486    AData^ := ((b shr 1) and $55) or ((b shl 1) and $AA);
1487
1488    Dec(Count);
1489    Inc(AData);
1490  end;
1491
1492end;
1493
1494
1495{------------------------------------------------------------------------------
1496  Function: AllocGDKColor
1497  Params:  AColor: A RGB color (TColor)
1498  Returns: an Allocated GDKColor
1499
1500  Allocated a GDKColor from a winapi color
1501 ------------------------------------------------------------------------------}
1502function AllocGDKColor(const AColor: TColorRef): TGDKColor;
1503begin
1504  with Result do
1505  begin
1506    Red :=   ((AColor shl 8) and $00FF00) or ((AColor       ) and $0000FF);
1507    Green := ((AColor      ) and $00FF00) or ((AColor shr 8 ) and $0000FF);
1508    Blue :=  ((AColor shr 8) and $00FF00) or ((AColor shr 16) and $0000FF);
1509  end;
1510  {$IFDEF DebugGDK}
1511  BeginGDKErrorTrap;
1512  {$ENDIF}
1513  gdk_colormap_alloc_color(gdk_colormap_get_system, @Result, False, True);
1514  {$IFDEF DebugGDK}
1515  EndGDKErrorTrap;
1516  {$ENDIF}
1517end;
1518
1519
1520function RegionType(RGN: PGDKRegion) : Longint;
1521var
1522  aRect : TGDKRectangle;
1523  SimpleRGN: PGdkRegion;
1524begin
1525  {$IFDEF DebugGDK}
1526  BeginGDKErrorTrap;
1527  {$ENDIF}
1528  If RGN = nil then
1529    Result := ERROR
1530  else
1531    If gdk_region_empty(RGN) then
1532      Result := NULLREGION
1533  else begin
1534    gdk_region_get_clipbox(RGN,@aRect);
1535    SimpleRGN := gdk_region_rectangle(@aRect);
1536    if gdk_region_equal(SimpleRGN, RGN) then
1537      Result := SIMPLEREGION
1538    else
1539      Result := COMPLEXREGION;
1540    gdk_region_destroy(SimpleRGN);
1541  end;
1542  {$IFDEF DebugGDK}
1543  EndGDKErrorTrap;
1544  {$ENDIF}
1545end;
1546
1547
1548function GDKRegionAsString(RGN: PGDKRegion): string;
1549var
1550  aRect: TGDKRectangle;
1551begin
1552  Result:=DbgS(RGN);
1553  BeginGDKErrorTrap;
1554  gdk_region_get_clipbox(RGN,@aRect);
1555  EndGDKErrorTrap;
1556  Result:=Result+'(x='+IntToStr(Integer(aRect.x))+',y='+IntToStr(Integer(aRect.y))+',w='
1557                    +IntToStr(aRect.Width)+',h='+IntToStr(aRect.Height)+' '
1558                    +'Type='+IntToStr(RegionType(RGN))+')';
1559end;
1560
1561function CreateRectGDKRegion(const ARect: TRect): PGDKRegion;
1562var
1563  GDkRect: TGDKRectangle;
1564begin
1565  GDkRect.x:=ARect.Left;
1566  GDkRect.y:=ARect.Top;
1567  GDkRect.Width:=ARect.Right-ARect.Left;
1568  GDkRect.Height:=ARect.Bottom-ARect.Top;
1569  {$IFDEF DebugGDK}
1570  BeginGDKErrorTrap;
1571  {$ENDIF}
1572  Result:=gdk_region_rectangle(@GDKRect);
1573  {$IFDEF DebugGDK}
1574  EndGDKErrorTrap;
1575  {$ENDIF}
1576end;
1577
1578Procedure FreeGDIColor(GDIColor: PGDIColor);
1579begin
1580  if (cfColorAllocated in GDIColor^.ColorFlags) then begin
1581    if (GDIColor^.Colormap <> nil) then begin
1582      BeginGDKErrorTrap;
1583      gdk_colormap_free_colors(GDIColor^.Colormap,@(GDIColor^.Color), 1);
1584      EndGDKErrorTrap;
1585    end;
1586    //GDIColor.Color.Pixel := -1;
1587    Exclude(GDIColor^.ColorFlags,cfColorAllocated);
1588  end;
1589end;
1590
1591procedure SetGDIColorRef(var GDIColor: TGDIColor; NewColorRef: TColorRef);
1592begin
1593  if GDIColor.ColorRef=NewColorRef then exit;
1594  FreeGDIColor(@GDIColor);
1595  GDIColor.ColorRef:=NewColorRef;
1596end;
1597
1598Procedure AllocGDIColor(DC: hDC; GDIColor: PGDIColor);
1599var
1600  RGBColor : TColorRef;
1601begin
1602  if DC=0 then ;
1603  if not (cfColorAllocated in GDIColor^.ColorFlags) then begin
1604    RGBColor := ColorToRGB(GDIColor^.ColorRef);
1605
1606    With GDIColor^.Color do begin
1607      Red := gushort(GetRValue(RGBColor)) shl 8;
1608      Green := gushort(GetGValue(RGBColor)) shl 8;
1609      Blue := gushort(GetBValue(RGBColor)) shl 8;
1610      Pixel := 0;
1611    end;
1612
1613    {with TGtkDeviceContext(DC) do
1614      If CurrentPalette <> nil then
1615        GDIColor.Colormap := CurrentPalette^.PaletteColormap
1616      else}
1617        GDIColor^.Colormap := GDK_Colormap_get_system;
1618
1619    gdk_colormap_alloc_color(GDIColor^.Colormap, @(GDIColor^.Color),True,True);
1620
1621    Include(GDIColor^.ColorFlags,cfColorAllocated);
1622  end;
1623end;
1624
1625procedure BuildColorRefFromGDKColor(var GDIColor: TGDIColor);
1626begin
1627  GDIColor.ColorRef:=TGDKColorToTColor(GDIColor.Color);
1628  Include(GDIColor.ColorFlags,cfColorAllocated);
1629end;
1630
1631procedure EnsureGCColor(DC: hDC; ColorType: TDevContextsColorType;
1632  IsSolidBrush, AsBackground: Boolean);
1633var
1634  GC: PGDKGC;
1635  GDIColor: PGDIColor;
1636
1637  procedure WarnAllocFailed(const foreground : TGdkColor);
1638  begin
1639    DebugLn('NOTE: EnsureGCColor.EnsureAsGCValues gdk_colormap_alloc_color failed ',
1640      ' Foreground=',
1641      DbgS(Foreground.red),',',
1642      DbgS(Foreground.green),',',
1643      DbgS(Foreground.blue),
1644      ' GDIColor^.ColorRef=',DbgS(GDIColor^.ColorRef)
1645      );
1646  end;
1647
1648  procedure EnsureAsGCValues;
1649  var
1650    AllocFG : Boolean;
1651    SysGCValues: TGdkGCValues;
1652  begin
1653    FreeGDIColor(GDIColor);
1654    SysGCValues:=GetSysGCValues(GDIColor^.ColorRef,
1655                                TGtkDeviceContext(DC).Widget);
1656    {$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
1657    with SysGCValues do
1658    begin
1659      AllocFG := Foreground.Pixel = 0;
1660      if AllocFG then
1661        if not gdk_colormap_alloc_color(GDK_Colormap_get_system, @Foreground,
1662                                        True, True) then
1663          WarnAllocFailed(Foreground);
1664      gdk_gc_set_fill(GC, fill);
1665      if AsBackground then
1666        gdk_gc_set_background(GC, @foreground)
1667      else
1668        gdk_gc_set_foreground(GC, @foreground);
1669      case Fill of
1670        GDK_TILED :
1671          if Tile <> nil then
1672          begin
1673            gdk_gc_set_ts_origin(GC, ts_x_origin, ts_y_origin);
1674            gdk_gc_set_tile(GC, Tile);
1675          end;
1676        GDK_STIPPLED,
1677        GDK_OPAQUE_STIPPLED:
1678          if stipple <> nil then
1679          begin
1680            gdk_gc_set_background(GC, @background);
1681            gdk_gc_set_ts_origin(GC, ts_x_origin, ts_y_origin);
1682            gdk_gc_set_stipple(GC, stipple);
1683          end;
1684      end;
1685      if AllocFG then
1686        gdk_colormap_free_colors(GDK_Colormap_get_system, @Foreground,1);
1687    end;
1688    {$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF}
1689  end;
1690
1691  procedure EnsureAsColor;
1692  begin
1693    AllocGDIColor(DC, GDIColor);
1694    //DebugLn('EnsureAsColor ',DbgS(GDIColor^.ColorRef),' AsBackground=',AsBackground);
1695    {$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
1696    if AsBackground then
1697      gdk_gc_set_background(GC, @(GDIColor^.Color))
1698    else
1699    begin
1700      gdk_gc_set_fill(GC, GDK_SOLID);
1701      gdk_gc_set_foreground(GC, @(GDIColor^.Color));
1702    end;
1703    {$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF}
1704  end;
1705
1706begin
1707  GC:=TGtkDeviceContext(DC).GC;
1708  GDIColor:=nil;
1709  with TGtkDeviceContext(DC) do
1710  begin
1711    case ColorType of
1712      dccCurrentBackColor: GDIColor:=@CurrentBackColor;
1713      dccCurrentTextColor: GDIColor:=@CurrentTextColor;
1714      dccGDIBrushColor   : GDIColor:=@(GetBrush^.GDIBrushColor);
1715      dccGDIPenColor     : GDIColor:=@(GetPen^.GDIPenColor);
1716    end;
1717  end;
1718  if GDIColor=nil then exit;
1719
1720  // FPC bug workaround:
1721  // clScrollbar = $80000000 can't be used in case statements
1722  if TColor(GDIColor^.ColorRef)=clScrollbar then
1723  begin
1724    //often have a BK Pixmap
1725    if IsSolidBrush then
1726      EnsureAsGCValues
1727    else
1728      EnsureAsColor;//GC's with Pixmaps can't work w/Hatch's (yet)
1729    exit;
1730  end;
1731
1732  case TColor(GDIColor^.ColorRef) of
1733    //clScrollbar: see above
1734    clInfoBk,
1735    clMenu,
1736    clHighlight,
1737    clBtnFace,
1738    clWindow,
1739    clForm:
1740      //often have a BK Pixmap
1741      if IsSolidBrush then
1742        EnsureAsGCValues
1743      else
1744        EnsureAsColor;//GC's with Pixmaps can't work w/Hatch's (yet)
1745
1746    clHighlightText,
1747    clBtnShadow,
1748    clBtnHighlight,
1749    clBtnText,
1750    clInfoText,
1751    clWindowText,
1752    clMenuText,
1753    clGrayText:
1754      //should never have a BK Pixmap
1755      EnsureAsGCValues;
1756    else
1757      EnsureAsColor;
1758  end;
1759end;
1760
1761procedure CopyGDIColor(var SourceGDIColor, DestGDIColor: TGDIColor);
1762begin
1763  SetGDIColorRef(DestGDIColor,SourceGDIColor.ColorRef);
1764end;
1765
1766function IsBackgroundColor(Color: TColor): boolean;
1767begin
1768  Result := (Color = clForm) or
1769            (Color = clInfoBk) or
1770            (Color = clBackground);
1771end;
1772
1773function CompareGDIColor(const Color1, Color2: TGDIColor): boolean;
1774begin
1775  Result:=Color1.ColorRef=Color2.ColorRef;
1776end;
1777
1778function CompareGDIFill(const Fill1, Fill2: TGdkFill): boolean;
1779begin
1780  Result:=Fill1=Fill2;
1781end;
1782
1783function CompareGDIBrushes(Brush1, Brush2: PGdiObject): boolean;
1784begin
1785  Result:=Brush1^.IsNullBrush=Brush2^.IsNullBrush;
1786  if Result then begin
1787    Result:=CompareGDIColor(Brush1^.GDIBrushColor,Brush2^.GDIBrushColor);
1788    if Result then begin
1789      Result:=CompareGDIFill(Brush1^.GDIBrushFill,Brush2^.GDIBrushFill);
1790      if Result then begin
1791        Result:=Brush1^.GDIBrushPixMap=Brush2^.GDIBrushPixMap;
1792      end;
1793    end;
1794  end;
1795end;
1796
1797//-----------------------------------------------------------------------------
1798
1799{ Palette Index<->RGB Hash Functions }
1800
1801type
1802  TIndexRGB = record
1803    Index: longint;
1804    RGB: longint;
1805  end;
1806  PIndexRGB = ^TIndexRGB;
1807
1808function GetIndexAsKey(p: pointer): pointer;
1809begin
1810  Result:=Pointer(PIndexRGB(p)^.Index + 1);
1811end;
1812
1813function GetRGBAsKey(p: pointer): pointer;
1814begin
1815  Result:=Pointer(PIndexRGB(p)^.RGB + 1);
1816end;
1817
1818function PaletteIndexToIndexRGB(Pal : PGDIObject; I : longint): PIndexRGB;
1819var
1820  HashItem: PDynHashArrayItem;
1821begin
1822  Result := nil;
1823  HashItem:=Pal^.IndexTable.FindHashItemWithKey(Pointer(I + 1));
1824  if HashItem<>nil then
1825    Result:=PIndexRGB(HashItem^.Item);
1826end;
1827
1828function PaletteRGBToIndexRGB(Pal : PGDIObject; RGB : longint): PIndexRGB;
1829var
1830  HashItem: PDynHashArrayItem;
1831begin
1832  Result := nil;
1833  HashItem:=Pal^.RGBTable.FindHashItemWithKey(Pointer(RGB + 1));
1834  if HashItem<>nil then
1835    Result:=PIndexRGB(HashItem^.Item);
1836end;
1837
1838{ Palette Index<->RGB lookup Functions }
1839
1840function PaletteIndexExists(Pal : PGDIObject; I : longint): Boolean;
1841begin
1842  Result := Pal^.IndexTable.ContainsKey(Pointer(I + 1));
1843end;
1844
1845function PaletteRGBExists(Pal : PGDIObject; RGB : longint): Boolean;
1846begin
1847  Result := Pal^.RGBTable.ContainsKey(Pointer(RGB + 1));
1848end;
1849
1850function PaletteAddIndex(Pal : PGDIObject; I, RGB : Longint): Boolean;
1851var
1852  IndexRGB: PIndexRGB;
1853begin
1854  New(IndexRGB);
1855  IndexRGB^.Index:=I;
1856  IndexRGB^.RGB:=RGB;
1857  Pal^.IndexTable.Add(IndexRGB);
1858  Result := PaletteIndexExists(Pal, I);
1859  If Not Result then
1860    Dispose(IndexRGB)
1861  else begin
1862    Pal^.RGBTable.Add(IndexRGB);
1863    Result := PaletteRGBExists(Pal, RGB);
1864    If not Result then begin
1865      Pal^.IndexTable.Remove(IndexRGB);
1866      Dispose(IndexRGB);
1867    end;
1868  end;
1869end;
1870
1871function PaletteDeleteIndex(Pal : PGDIObject; I : Longint): Boolean;
1872var
1873  RGBIndex : PIndexRGB;
1874begin
1875  RGBIndex := PaletteIndextoIndexRGB(Pal,I);
1876  Result := RGBIndex = nil;
1877  If not Result then begin
1878    Pal^.IndexTable.Remove(RGBIndex);
1879    If PaletteRGBExists(Pal, RGBIndex^.RGB) then
1880      Pal^.RGBTable.Remove(RGBIndex);
1881    Dispose(RGBIndex);
1882  end;
1883end;
1884
1885function PaletteIndexToRGB(Pal : PGDIObject; I : longint): longint;
1886var
1887  RGBIndex : PIndexRGB;
1888begin
1889  RGBIndex := PaletteIndextoIndexRGB(Pal,I);
1890  if RGBIndex = nil then
1891    Result := -1//InvalidRGB
1892  else
1893    Result := RGBIndex^.RGB;
1894end;
1895
1896function PaletteRGBToIndex(Pal : PGDIObject; RGB : longint): longint;
1897var
1898  RGBIndex : PIndexRGB;
1899begin
1900  RGBIndex := PaletteRGBtoIndexRGB(Pal,RGB);
1901  if RGBIndex = nil then
1902    Result:=-1//InvalidIndex
1903  else
1904    Result := RGBIndex^.Index;
1905end;
1906
1907procedure InitializePalette(const Pal: PGDIObject; const Entries: PPaletteEntry; const RGBCount: Longint);
1908var
1909  I: Integer;
1910  RGBValue: Longint;
1911begin
1912  for I := 0 to RGBCount - 1 do
1913  begin
1914    if PaletteIndexExists(Pal, I) then
1915      PaletteDeleteIndex(Pal, I);
1916    with Entries[I] do
1917      RGBValue := RGB(peRed, peGreen, peBlue) {or (peFlags shl 32)??};
1918    if not PaletteRGBExists(Pal, RGBValue) then
1919      PaletteAddIndex(Pal, I, RGBValue);
1920  end;
1921end;
1922
1923function HandleGTKKeyUpDown(AWidget: PGtkWidget; AEvent: PGdkEventKey;
1924  AData: gPointer; ABeforeEvent, AHandleDown: Boolean;
1925  const AEventName: PGChar) : GBoolean;
1926// returns CallBackDefaultReturn if event can continue in gtk's message system
1927{off $DEFINE VerboseKeyboard}
1928const
1929  KEYUP_MAP: array[Boolean {syskey}, Boolean {before}] of Cardinal = (
1930    (LM_KEYUP, CN_KEYUP),
1931    (LM_SYSKEYUP, CN_SYSKEYUP)
1932  );
1933
1934  KEYDOWN_MAP: array[Boolean {syskey}, Boolean {before}] of Cardinal = (
1935    (LM_KEYDOWN, CN_KEYDOWN),
1936    (LM_SYSKEYDOWN, CN_SYSKEYDOWN)
1937  );
1938
1939  CHAR_MAP: array[Boolean {syskey}, Boolean {before}] of Cardinal = (
1940    (LM_CHAR, CN_CHAR),
1941    (LM_SYSCHAR, CN_SYSCHAR)
1942  );
1943var
1944  Msg: TLMKey;
1945  EventStopped: Boolean;
1946  EventString: PChar; // GTK1 and GTK2 workaround
1947                      // (and easy access to bytes)
1948  KeyCode: Word;
1949  KCInfo: TKeyCodeInfo;
1950  VKey: Byte;
1951  ShiftState: TShiftState;
1952
1953  Character: TUTF8Char;
1954  SysKey: Boolean;
1955
1956  CommonKeyData: Integer;
1957  Flags: Integer;
1958  FocusedWidget: PGtkWidget;
1959  LCLObject: TObject;
1960  FocusedWinControl: TWinControl;
1961  HandledByLCL: Boolean;
1962  TargetWidget: PGtkWidget;
1963  TargetObj: gPointer;
1964  KeyPressesChar: char;
1965
1966  procedure StopKeyEvent;
1967  begin
1968    {$IFDEF VerboseKeyboard}
1969    DebugLn('StopKeyEvent AEventName="',AEventName,'" ABeforeEvent=',dbgs(ABeforeEvent));
1970    {$ENDIF}
1971    if not EventStopped
1972    then begin
1973      g_signal_stop_emission_by_name(PGtkObject(AWidget), AEventName);
1974      EventStopped := True;
1975    end;
1976
1977    //MWE: still need to skip on win32 ?
1978    {MWE:.$IfNDef Win32}
1979    if EventString <> nil
1980    then begin
1981      gdk_event_key_set_string(AEvent, #0);
1982      AEvent^.length := 0;
1983    end;
1984    {MWE:.$EndIf}
1985    ResetDefaultIMContext;
1986
1987    AEvent^.KeyVal := 0;
1988  end;
1989
1990  function DeliverKeyMessage(const Target: Pointer; var AMessage): boolean;
1991  begin
1992    Result:=DeliverMessage(Target,AMessage)=0;
1993    if not Result then StopKeyEvent;
1994  end;
1995
1996  function GetSpecialChar: Char;
1997  begin
1998    if (AEvent^.keyval > $FF00) and (AEvent^.keyval < $FF20) and
1999       (AEvent^.keyval <> GDK_KEY_Tab) then
2000      Result := Chr(AEvent^.keyval xor $FF00)
2001    else
2002      Result := #0;
2003  end;
2004
2005  function CanSendChar: Boolean;
2006  begin
2007    Result := False;
2008    if AEvent^.Length > 1 then Exit;
2009
2010    // to be delphi compatible we should not send a space here
2011    if AEvent^.KeyVal = GDK_KEY_KP_SPACE then Exit;
2012
2013    // Check if CTRL is pressed
2014    if ssCtrl in ShiftState
2015    then begin
2016      // Check if we pressed ^@
2017      if  (AEvent^.Length = 0)
2018      and (AEvent^.KeyVal = GDK_KEY_AT)
2019      then begin
2020        Result := True;
2021        Exit;
2022      end;
2023      // check if we send the ^Char subset
2024      if (AEvent^.Length = 1) and (EventString <> nil)
2025      then begin
2026        Result := (EventString^ > #0) and (EventString^ < ' ');
2027      end;
2028      Exit;
2029    end;
2030    Result := (AEvent^.Length > 0) or (GetSpecialChar <> #0);
2031  end;
2032
2033  function KeyAlreadyHandledByGtk: boolean;
2034  begin
2035    Result := false;
2036    if AWidget = nil then exit;
2037
2038    if GtkWidgetIsA(AWidget, gtk_entry_get_type)
2039    then begin
2040      // the gtk_entry handles the following keys
2041      case Aevent^.keyval of
2042        GDK_Key_Return,
2043        GDK_Key_Escape,
2044        GDK_Key_Tab: Exit;
2045      end;
2046
2047      Result := AEvent^.length > 0;
2048      if Result then Exit;
2049
2050      case AEvent^.keyval of
2051        GDK_Key_BackSpace,
2052        GDK_Key_Clear,
2053        GDK_Key_Insert,
2054        GDK_Key_Delete,
2055        GDK_Key_Home,
2056        GDK_Key_End,
2057        GDK_Key_Left,
2058        GDK_Key_Right,
2059        $20..$FF: Result := True;
2060      end;
2061      exit;
2062    end;
2063
2064    if GtkWidgetIsA(AWidget, gtk_text_get_type)
2065    then begin
2066      // the gtk_text handles the following keys
2067      case AEvent^.keyval of
2068        GDK_Key_Escape: Exit;
2069      end;
2070
2071      Result := AEvent^.length > 0;
2072      if Result then Exit;
2073
2074      case AEvent^.keyval of
2075        GDK_Key_Return,
2076        GDK_Key_Tab,
2077        GDK_Key_BackSpace,
2078        GDK_Key_Clear,
2079        GDK_Key_Insert,
2080        GDK_Key_Delete,
2081        GDK_Key_Home,
2082        GDK_Key_End,
2083        GDK_Key_Left,
2084        GDK_Key_Right,
2085        GDK_Key_Up,
2086        GDK_Key_Down,
2087        $20..$FF: Result := True;
2088      end;
2089      exit;
2090    end;
2091  end;
2092
2093  procedure CharToKeyVal(C: Char; out KeyVal: guint; out Length: gint);
2094  begin
2095    Length := 1;
2096    {$ifndef gtk1}
2097    if C in [#$01..#$1B] then
2098    begin
2099      KeyVal := $FF00 or Ord(C);
2100      if KeyVal = GDK_KEY_BackSpace then
2101        Length := 0;
2102    end
2103    else
2104    {$endif}
2105      KeyVal := Ord(C);
2106  end;
2107
2108  function KeyActivatedAccelerator: boolean;
2109
2110    function CheckMenuChilds(AMenuItem: TMenuItem): boolean;
2111    var
2112      i: Integer;
2113      Item: TMenuItem;
2114      MenuItemWidget: PGtkWidget;
2115    begin
2116      Result:=false;
2117      if (AMenuItem=nil) or (not AMenuItem.HandleAllocated) then exit;
2118      for i:=0 to AMenuItem.Count-1 do begin
2119        Item:=AMenuItem[i];
2120        if not Item.HandleAllocated then continue;
2121        if not GTK_WIDGET_SENSITIVE(PGTKWidget(Item.Handle)) then continue;
2122        if IsAccel(Msg.CharCode,Item.Caption) then begin
2123          // found
2124          Result:=true;
2125          MenuItemWidget:=PGTKWidget(Item.Handle);
2126          if GtkWidgetIsA(MenuItemWidget,gtk_menu_item_get_type) then begin
2127            //DebugLn(['CheckMenuChilds popup: ',dbgsName(Item)]);
2128            // popup the submenu
2129            gtk_signal_emit_by_name(PGtkObject(MenuItemWidget),'activate-item');
2130          end;
2131          exit;
2132        end;
2133      end;
2134    end;
2135
2136  var
2137    AComponent: TComponent;
2138    AControl: TControl;
2139    AForm: TCustomForm;
2140  begin
2141    Result:=false;
2142    //debugln('KeyActivatedAccelerator A');
2143    if not SysKey then exit;
2144    // it is a system key -> try menus
2145    if (Msg.CharCode in [VK_A..VK_Z]) then begin
2146      if (TObject(TargetObj) is TComponent) then begin
2147        AComponent:=TComponent(TargetObj);
2148        //DebugLn(['KeyActivatedAccelerator ',dbgsName(AComponent)]);
2149        if AComponent is TControl then begin
2150          AControl:=TControl(AComponent);
2151          repeat
2152            AForm:=GetFirstParentForm(AControl);
2153            if AForm<>nil then begin
2154              if AForm.Menu<>nil then begin
2155                Result:=CheckMenuChilds(AForm.Menu.Items);
2156                if Result then exit;
2157              end;
2158            end;
2159            AControl:=AForm.Parent;
2160          until AControl=nil;
2161
2162          {$IFDEF Gtk2}
2163          // check main menu of MainForm
2164          if (Application.MainForm<>nil) then begin
2165            AControl:=TControl(AComponent);
2166            AForm:=GetParentForm(AControl);
2167            if (AForm<>nil)
2168            and (not (fsModal in AForm.FormState))
2169            and (not Application.MainForm.IsParentOf(AControl))
2170            and (Application.MainForm.Menu<>nil) then begin
2171              Result:=CheckMenuChilds(Application.MainForm.Menu.Items);
2172              if Result then exit;
2173            end;
2174          end;
2175          {$ENDIF}
2176        end;
2177      end;
2178    end;
2179  end;
2180
2181  procedure EmulateEatenKeys;
2182  begin
2183    // some widgets eats keys, but do not do anything useful for the LCL
2184    // emulate the keys
2185    if not ABeforeEvent then Exit;
2186    if EventStopped then Exit;
2187
2188    //DebugLn(['EmulateEatenKeys TargetWidget=',dbghex(PtrInt(TargetWidget))]);
2189    //DebugLn(['EmulateEatenKeys ',GetWidgetDebugReport(TargetWidget),' gdk_event_get_type(AEvent)=',gdk_event_get_type(AEvent),' GDK_KEY_PRESS=',GDK_KEY_PRESS,' VKey=',VKey]);
2190    {$IFDEF Gtk2}
2191    // the gtk2 gtkentry handles the return key and emits an activate signal
2192    // The LCL does not use that and needs the return key event
2193    // => emulate it
2194    if GtkWidgetIsA(TargetWidget, gtk_type_entry)
2195    and (gdk_event_get_type(AEvent) = GDK_KEY_PRESS)
2196    and (VKey=13)
2197    then begin
2198      //DebugLn(['EmulateKeysEatenByGtk ']);
2199      FillChar(Msg, SizeOf(Msg), 0);
2200      Msg.CharCode := VKey;
2201      if SysKey then
2202        Msg.msg := LM_SYSKEYDOWN
2203      else
2204        Msg.msg := LM_KEYDOWN;
2205      Msg.KeyData := CommonKeyData or (Flags shl 16) or $0001 {TODO:  repeatcount};
2206
2207      // send the (Sys)KeyDown message directly to the LCL
2208      NotifyApplicationUserInput(TControl(TargetObj), Msg.Msg);
2209      DeliverKeyMessage(TargetObj, Msg);
2210    end;
2211    {$ENDIF}
2212  end;
2213
2214  procedure CheckDeadKey;
2215  begin
2216    if ABeforeEvent then begin
2217      {$IFDEF Gtk2}
2218      if im_context_widget<>TargetWidget then begin
2219        //DebugLn(['CheckDeadKey init im_context ',GetWidgetDebugReport(TargetWidget)]);
2220        ResetDefaultIMContext;
2221        im_context_widget:=TargetWidget;
2222        gtk_im_context_set_client_window(im_context,GetControlWindow(TargetWidget));
2223        //DebugLn(['CheckDeadKey im_context initialized']);
2224      end;
2225      // Note: gtk_im_context_filter_keypress understands keypress and keyrelease
2226      gtk_im_context_filter_keypress (im_context, AEvent);
2227      //DebugLn(['CheckDeadKey DeadKey=',DeadKey,' str="',im_context_string,'"']);
2228      {$ENDIF}
2229    end;
2230  end;
2231
2232begin
2233  Result := CallBackDefaultReturn;
2234
2235  EventStopped := False;
2236  HandledByLCL := KeyEventWasHandledByLCL(AEvent, ABeforeEvent);
2237
2238  {$IFDEF VerboseKeyboard}
2239  DebugLn(['[HandleGTKKeyUpDown] ',DbgSName(TControl(AData)),
2240    ' ',(AEvent^.{$IFDEF GTK1}theType{$ELSE}_Type{$ENDIF}),' Widget=',GetWidgetClassName(AWidget),
2241    ' Before=',ABeforeEvent,' Down=',AHandleDown,' HandledByLCL=',HandledByLCL]);
2242  {$ENDIF}
2243
2244  // handle every key event only once
2245  if HandledByLCL then Exit;
2246
2247  TargetWidget := AWidget;
2248  TargetObj := AData;
2249  FocusedWinControl := nil;
2250  FocusedWidget := nil;
2251
2252  // The gtk sends keys first to the gtkwindow and then to the focused control.
2253  // The LCL expects only once to the focused control.
2254  // And some gtk widgets (combo) eats keys, so that the LCL has no chance to
2255  // handle it. Therefore keys to the form are immediately redirected to the
2256  // focused control without changing the normal gtk event path.
2257  if GtkWidgetIsA(AWidget, gtk_window_get_type)
2258  then begin
2259    FocusedWidget := PGtkWindow(AWidget)^.focus_widget;
2260    if FocusedWidget <> nil
2261    then begin
2262      LCLObject := GetNearestLCLObject(FocusedWidget);
2263      if LCLObject is TWinControl
2264      then begin
2265        FocusedWinControl := TWinControl(LCLObject);
2266        if FocusedWidget <> AWidget
2267        then begin
2268          {$IFDEF VerboseKeyboard}
2269          DebugLn('[HandleGTKKeyUpDown] REDIRECTING ',
2270            ' FocusedWidget=',GetWidgetClassName(FocusedWidget),
2271            ' Control=',FocusedWinControl.Name,':',FocusedWinControl.ClassName);
2272          {$ENDIF}
2273          // redirect key to lcl control
2274          TargetWidget := FocusedWidget;
2275          TargetObj := FocusedWinControl;
2276        end;
2277      end;
2278    end;
2279  end;
2280
2281  // remember this event
2282  RememberKeyEventWasHandledByLCL(AEvent, ABeforeEvent);
2283
2284  if TargetWidget = nil then Exit;
2285
2286  //DebugLn(['HandleGTKKeyUpDown TargetWidget=',GetWidgetDebugReport(TargetWidget)]);
2287
2288
2289  //DebugLn(['HandleGTKKeyUpDown TargetWidget=',GetWidgetDebugReport(TargetWidget),' ',DbgStr(EventString),' state=',AEvent^.state,' keyval=',AEvent^.keyval]);
2290  FillChar(Msg, SizeOf(Msg), 0);
2291
2292  gdk_event_key_get_string(AEvent, EventString);
2293  //DebugLn(['HandleGTKKeyUpDown TargetWidget=',GetWidgetDebugReport(TargetWidget),' ',DbgStr(EventString),' state=',AEvent^.state,' keyval=',AEvent^.keyval]);
2294  CheckDeadKey;
2295  Flags := 0;
2296  SysKey := False;
2297  ShiftState := GTKEventStateToShiftState(AEvent^.state);
2298  {$ifdef gtk1}
2299    KeyCode := XKeysymToKeycode(gdk_display, AEvent^.keyval);
2300  {$else}
2301    KeyCode := AEvent^.hardware_keycode;
2302  {$endif}
2303
2304  if (KeyCode = 0)
2305  or (KeyCode > High(MKeyCodeInfo))
2306  or (MKeyCodeInfo[KeyCode].VKey1 = 0)
2307  then begin
2308    // no VKey defined, maybe composed char ?
2309    CommonKeyData := 0;
2310  end
2311  else begin
2312    KCInfo := MKeyCodeInfo[KeyCode];
2313
2314    if (KCInfo.Flags and KCINFO_FLAG_SHIFT_XOR_NUM <> 0)
2315    and ((ssShift in ShiftState) xor (ssNum in ShiftState))
2316    then VKey := KCInfo.VKey2
2317    else VKey := KCInfo.VKey1;
2318
2319    if (KCInfo.Flags and KCINFO_FLAG_EXT) <> 0
2320    then Flags := KF_EXTENDED;
2321
2322
2323    // ssAlt + a key pressed is always a syskey
2324    // ssAltGr + a key is only a syskey when the key pressed has no levelshift or when ssHift is pressed to0
2325    SysKey := (ssAlt in ShiftState);
2326    if not SysKey
2327    then begin
2328      // Check ssAltGr
2329      if (KCInfo.Flags and KCINFO_FLAG_ALTGR) = 0
2330      then begin
2331        // VKey has no levelshift char so AltGr is syskey
2332        SysKey := ssAltGr in ShiftState;
2333      end
2334      else begin
2335        // VKey has levelshift char so AltGr + Shift is syskey
2336        SysKey := ShiftState * [ssShift, ssAltGr] = [ssShift, ssAltGr]
2337      end;
2338    end;
2339    if SysKey
2340    then Flags := Flags or KF_ALTDOWN;
2341
2342    CommonKeyData := KeyCode shl 16; // Not really scancode, but will do
2343
2344    if AHandleDown
2345    then begin
2346      {$IFDEF VerboseKeyboard}
2347      DebugLn('[HandleGTKKeyUpDown] GDK_KEY_PRESS VKey=',dbgs(VKey),' SysKey=',dbgs(SysKey));
2348      {$ENDIF}
2349
2350      Msg.CharCode := VKey;
2351      Msg.Msg := KEYDOWN_MAP[SysKey, ABeforeEvent];
2352
2353      // todo  repeat
2354      // Flags := Flags or KF_REPEAT;
2355
2356      Msg.KeyData := CommonKeyData or (Flags shl 16) or $0001 {TODO:  repeatcount};
2357
2358      if not KeyAlreadyHandledByGtk
2359      then begin
2360        // send the (Sys)KeyDown message directly to the LCL
2361        NotifyApplicationUserInput(TControl(TargetObj), Msg.Msg);
2362        if DeliverKeyMessage(TargetObj, Msg)
2363        and (Msg.CharCode <> Vkey) then
2364          StopKeyEvent;
2365      end;
2366
2367      if (not EventStopped) and ABeforeEvent
2368      then begin
2369        if KeyActivatedAccelerator then exit;
2370      end;
2371    end
2372    else begin
2373      {$IFDEF VerboseKeyboard}
2374      DebugLn('[HandleGTKKeyUpDown] GDK_KEY_RELEASE VKey=',dbgs(VKey));
2375      {$ENDIF}
2376
2377      Msg.CharCode := VKey;
2378      Msg.Msg := KEYUP_MAP[SysKey, ABeforeEvent];
2379      Flags := Flags or KF_UP or KF_REPEAT;
2380      Msg.KeyData := CommonKeyData or (Flags shl 16) or $0001 {always};
2381
2382      // send the message directly to the LCL
2383      Msg.Result:=0;
2384      NotifyApplicationUserInput(TControl(TargetObj), Msg.Msg);
2385
2386      if DeliverKeyMessage(TargetObj, Msg)
2387      and (Msg.CharCode <> VKey)
2388      then begin
2389        // key was handled by LCL
2390        StopKeyEvent;
2391      end;
2392    end;
2393  end;
2394
2395  // send keypresses
2396  if not EventStopped and AHandleDown then begin
2397
2398    // send the UTF8 keypress
2399    if ABeforeEvent then begin
2400      // try to get the UTF8 representation of the key
2401      {$IFDEF GTK1}
2402        Character := '';
2403        if (AEvent^.length > 0) and (AEvent^.length <= 8) //max composed UTF8 char has lenght 8
2404        then begin
2405          SetLength(Character, AEvent^.length);
2406          System.Move(AEvent^.thestring^, Character[1], length(Character));
2407        end;
2408      {$ELSE GTK2}
2409        if im_context_string <> '' then
2410        begin
2411          Character := UTF8Copy(im_context_string,1,1);
2412          im_context_string:='';// clear, to avoid sending again
2413        end
2414        else
2415        begin
2416          KeyPressesChar := GetSpecialChar;
2417          if KeyPressesChar <> #0 then
2418            Character := KeyPressesChar
2419          else
2420            Character := '';
2421        end;
2422      {$ENDIF GTK2}
2423
2424      {$IFDEF VerboseKeyboard}
2425      debugln('[HandleGTKKeyUpDown] GDK_KEY_PRESS UTF8="',DbgStr(Character),'"');
2426      {$ENDIF}
2427
2428      if Character <> ''
2429      then begin
2430        LCLObject := GetNearestLCLObject(TargetWidget);
2431        if LCLObject is TWinControl
2432        then begin
2433          // send the key after navigation keys were handled
2434          Result := TWinControl(LCLObject).IntfUTF8KeyPress(Character, 1, SysKey);
2435          if Result or (Character = '')
2436          then StopKeyEvent
2437          else if (Length(Character) = 1)
2438          {$IFDEF Gtk1}
2439          // GTK1 only supports normal ASCII characters (Note: #127 is delete)
2440          and (Character[1] in [#32..#126])
2441          {$ENDIF}
2442          then begin
2443            CharToKeyVal(Character[1], AEvent^.KeyVal, AEvent^.length);
2444            if AEvent^.length = 1 then
2445            begin
2446              EventString^ := Character[1];
2447              EventString[1] := #0;
2448            end
2449            else
2450              EventString^ := #0;
2451          end;
2452        end;
2453      end;
2454    end;
2455
2456    //  send a normal KeyPress Event for Delphi compatibility
2457    if not EventStopped and CanSendChar
2458    then begin
2459      {$IFDEF EventTrace}
2460      EventTrace('char', data);
2461      {$ENDIF}
2462
2463      KeyPressesChar := #0;
2464      if AEvent^.Length = 1
2465      then begin
2466        // ASCII key was pressed
2467        KeyPressesChar := EventString^;
2468      end
2469      else
2470        KeyPressesChar := GetSpecialChar;
2471
2472      if KeyPressesChar <> #0
2473      then begin
2474        FillChar(Msg, SizeOf(Msg), 0);
2475
2476        Msg.KeyData := CommonKeyData;
2477        Msg.Msg := CHAR_MAP[SysKey, ABeforeEvent];
2478
2479        // send the (Sys)Char message directly (not queued) to the LCL
2480        Msg.Result:=0;
2481        Msg.CharCode := Ord(KeyPressesChar);
2482        if DeliverKeyMessage(TargetObj, Msg)
2483        and (Ord(KeyPressesChar) <> Msg.CharCode)
2484        then begin
2485          // key was changed by lcl
2486          if (Msg.CharCode=0) or (Msg.CharCode>=128)
2487          then begin
2488            // key set to invalid => just stop
2489            StopKeyEvent;
2490          end
2491          else begin
2492            // try to change the key
2493            CharToKeyVal(chr(Msg.CharCode), AEvent^.KeyVal, AEvent^.length);
2494            if AEvent^.length = 1 then
2495            begin
2496              EventString^ := Character[1];
2497              EventString[1] := #0;
2498            end
2499            else
2500              EventString^ := #0;
2501            gdk_event_key_set_string(AEvent, EventString);
2502          end;
2503        end;
2504      end;
2505    end;
2506  end;
2507
2508  EmulateEatenKeys;
2509
2510  {$IFDEF Gtk1}
2511  Result:=true;
2512  {$ELSE}
2513  Result:=EventStopped;
2514  {$ENDIF}
2515end;
2516
2517{------------------------------------------------------------------------------
2518  Procedure: InitKeyboardTables
2519  Params:    none
2520  Returns:   none
2521
2522  Initializes the CharToVK and CKeyToVK tables
2523 ------------------------------------------------------------------------------}
2524procedure InitKeyboardTables;
2525
2526  procedure FindVKeyInfo(const AKeySym: Cardinal; var AVKey: Byte;
2527    var AExtended, AHasMultiVK, ASecondKey: Boolean);
2528  var
2529    ByteKey: Byte;
2530  begin
2531    AExtended := False;
2532    AHasMultiVK := False;
2533    AVKey := VK_UNDEFINED;
2534    ASecondKey := False;
2535
2536    case AKeySym of
2537      32..255: begin
2538        ByteKey:=Byte(AKeySym);
2539        case Chr(ByteKey) of // Normal ASCII chars
2540          //only unshifted values are checked
2541          //'A'..'Z',
2542          '0'..'9',
2543          ' ':      AVKey := ByteKey;
2544          'a'..'z': AVKey := ByteKey - Ord('a') + Ord('A');
2545          '+': AVKey := VK_OEM_PLUS;
2546          ',': AVKey := VK_OEM_COMMA;
2547          '-': AVKey := VK_OEM_MINUS;
2548          '.': AVKey := VK_OEM_PERIOD;
2549
2550          // try the US keycodes first
2551          ';': AVKey := VK_OEM_1;
2552          '/': AVKey := VK_OEM_2;
2553          '`': AVKey := VK_OEM_3;
2554          '[': AVKey := VK_OEM_4;
2555          '\': AVKey := VK_OEM_5;
2556          ']': AVKey := VK_OEM_6;
2557          '''': AVKey := VK_OEM_7;
2558        end;
2559      end;
2560
2561      GDK_KEY_Tab,
2562      GDK_KEY_ISO_Left_Tab: AVKey := VK_TAB;
2563      GDK_KEY_RETURN:       AVKey := VK_RETURN;
2564  //    GDK_KEY_LINEFEED;     AVKey := $0A;
2565
2566      // Cursor block / keypad
2567      GDK_KEY_INSERT:
2568      begin
2569        AExtended := True;
2570        AVKey := VK_INSERT;
2571      end;
2572      GDK_KEY_DELETE:
2573      begin
2574        AExtended := True;
2575        AVKey := VK_DELETE;
2576      end;
2577      GDK_KEY_HOME:
2578      begin
2579        AExtended := True;
2580        AVKey := VK_HOME;
2581      end;
2582      GDK_KEY_LEFT:
2583      begin
2584        AExtended := True;
2585        AVKey := VK_LEFT;
2586      end;
2587      GDK_KEY_UP:
2588      begin
2589        AExtended := True;
2590        AVKey := VK_UP;
2591      end;
2592      GDK_KEY_RIGHT:
2593      begin
2594        AExtended := True;
2595        AVKey := VK_RIGHT;
2596      end;
2597      GDK_KEY_DOWN:
2598      begin
2599        AExtended := True;
2600        AVKey := VK_DOWN;
2601      end;
2602      GDK_KEY_PAGE_UP:
2603      begin
2604        AExtended := True;
2605        AVKey := VK_PRIOR;
2606      end;
2607      GDK_KEY_PAGE_DOWN:
2608      begin
2609        AExtended := True;
2610        AVKey := VK_NEXT;
2611      end;
2612      GDK_KEY_END:
2613      begin
2614        AExtended := True;
2615        AVKey := VK_END;
2616      end;
2617
2618      // Keypad
2619      GDK_KEY_KP_ENTER:
2620      begin
2621        AExtended := True;
2622        AVKey := VK_Return;
2623      end;
2624      GDK_KEY_KP_Space, GDK_KEY_KP_Begin:
2625      begin
2626        AVKey := VK_CLEAR;
2627        AHasMultiVK := True;
2628      end;
2629      GDK_KEY_KP_INSERT:
2630      begin
2631        // Keypad key is not extended
2632        AVKey := VK_INSERT;
2633        AHasMultiVK := True;
2634      end;
2635      GDK_KEY_KP_HOME:
2636      begin
2637        // Keypad key is not extended
2638        AVKey := VK_HOME;
2639        AHasMultiVK := True;
2640      end;
2641      GDK_KEY_KP_LEFT:
2642      begin
2643        // Keypad key is not extended
2644        AVKey := VK_LEFT;
2645        AHasMultiVK := True;
2646      end;
2647      GDK_KEY_KP_UP:
2648      begin
2649        // Keypad key is not extended
2650        AVKey := VK_UP;
2651        AHasMultiVK := True;
2652      end;
2653      GDK_KEY_KP_RIGHT:
2654      begin
2655        // Keypad key is not extended
2656        AVKey := VK_RIGHT;
2657        AHasMultiVK := True;
2658      end;
2659      GDK_KEY_KP_DOWN:
2660      begin
2661        // Keypad key is not extended
2662        AVKey := VK_DOWN;
2663        AHasMultiVK := True;
2664      end;
2665      GDK_KEY_KP_PAGE_UP:
2666      begin
2667        // Keypad key is not extended
2668        AVKey := VK_PRIOR;
2669        AHasMultiVK := True;
2670      end;
2671      GDK_KEY_KP_PAGE_DOWN:
2672      begin
2673        // Keypad key is not extended
2674        AVKey := VK_NEXT;
2675        AHasMultiVK := True;
2676      end;
2677      GDK_KEY_KP_END:
2678      begin
2679        // Keypad key is not extended
2680        AVKey := VK_END;
2681        AHasMultiVK := True;
2682      end;
2683      GDK_KEY_Num_Lock:
2684      begin
2685        AExtended := True;
2686        AVKey := VK_NUMLOCK;
2687      end;
2688      GDK_KEY_KP_F1..GDK_KEY_KP_F4:
2689      begin
2690        // not on "normal" keyboard so defined extended to differentiate between normal Fn
2691        AExtended := True;
2692        AVKey := VK_F1 + AKeySym - GDK_KEY_KP_F1;
2693      end;
2694      GDK_KEY_KP_TAB:
2695      begin
2696        // not on "normal" keyboard so defined extended to differentiate between normal TAB
2697        AExtended := True;
2698        AVKey := VK_TAB;
2699      end;
2700      GDK_KEY_KP_Multiply:
2701      begin
2702        AVKey := VK_MULTIPLY;
2703      end;
2704      GDK_KEY_KP_Add:
2705      begin
2706        AVKey := VK_ADD;
2707      end;
2708      GDK_KEY_KP_Separator:
2709      begin
2710        // Keypad key is not extended
2711        AVKey := VK_SEPARATOR;
2712        AHasMultiVK := True;
2713      end;
2714      GDK_KEY_KP_Subtract:
2715      begin
2716        AVKey := VK_SUBTRACT;
2717      end;
2718      GDK_KEY_KP_Decimal:
2719      begin
2720        // Keypad key is not extended
2721        AVKey := VK_DECIMAL;
2722        AHasMultiVK := True;
2723      end;
2724      GDK_KEY_KP_Delete:
2725      begin
2726        // Keypad key is not extended
2727        AVKey := VK_DELETE;
2728        AHasMultiVK := True;
2729      end;
2730      GDK_KEY_KP_Divide:
2731      begin
2732        AExtended := True;
2733        AVKey := VK_DIVIDE;
2734      end;
2735      GDK_KEY_KP_0..GDK_KEY_KP_9:
2736      begin
2737        // Keypad key is not extended, it is identified by VK
2738        AVKey := VK_NUMPAD0 + AKeySym - GDK_KEY_KP_0;
2739        AHasMultiVK := True;
2740      end;
2741
2742      GDK_KEY_BackSpace:    AVKey := VK_BACK;
2743      GDK_KEY_Clear:        AVKey := VK_CLEAR;
2744      GDK_KEY_Pause:        AVKey := VK_PAUSE;
2745      GDK_KEY_Scroll_Lock:  AVKey := VK_SCROLL;
2746      GDK_KEY_Sys_Req:      AVKey := VK_SNAPSHOT;
2747      GDK_KEY_Escape:       AVKey := VK_ESCAPE;
2748
2749      GDK_KEY_Kanji:        AVKey := VK_KANJI;
2750
2751      GDK_Key_Select:       AVKey := VK_SELECT;
2752      GDK_Key_Print:        AVKey := VK_PRINT;
2753      GDK_Key_Execute:      AVKey := VK_EXECUTE;
2754      GDK_Key_Cancel:       AVKey := VK_CANCEL;
2755      GDK_Key_Help:         AVKey := VK_HELP;
2756      GDK_Key_Break:        AVKey := VK_CANCEL;
2757      GDK_Key_Mode_switch:  AVKey := VK_MODECHANGE;
2758      GDK_Key_Caps_Lock:    AVKey := VK_CAPITAL;
2759      GDK_Key_Shift_L:      AVKey := VK_SHIFT;
2760      GDK_Key_Shift_R:
2761      begin
2762        AVKey := VK_SHIFT;
2763        ASecondKey := True;
2764      end;
2765      GDK_Key_Control_L:    AVKey := VK_CONTROL;
2766      GDK_Key_Control_R:
2767      begin
2768        AVKey := VK_CONTROL;
2769        ASecondKey := True;
2770      end;
2771  //      GDK_Key_Meta_L:       AVKey := VK_MENU;  //shifted alt, so it is found by alt
2772  //      GDK_Key_Meta_R:       AVKey := VK_MENU;
2773      GDK_Key_Alt_L:        AVKey := VK_MENU;
2774      GDK_Key_Alt_R:
2775      begin
2776        AVKey := VK_MENU;
2777        ASecondKey := True;
2778      end;
2779      GDK_Key_Super_L:      AVKey := VK_LWIN;
2780      GDK_Key_Super_R: begin
2781        AVKey := VK_RWIN;
2782        ASecondKey := True;
2783      end;
2784      GDK_Key_Menu:         AVKey := VK_APPS;
2785
2786      // function keys
2787      GDK_KEY_F1..GDK_KEY_F24:  AVKey := VK_F1 + AKeySym - GDK_Key_F1;
2788
2789      // Extra keys on a "internet" keyboard
2790      GDKX_KEY_Sleep:
2791      begin
2792        AExtended := True;
2793        AVKey := VK_SLEEP;
2794      end;
2795      GDKX_KEY_AudioLowerVolume:
2796      begin
2797        AExtended := True;
2798        AVKey := VK_VOLUME_DOWN;
2799      end;
2800      GDKX_KEY_AudioMute:
2801      begin
2802        AExtended := True;
2803        AVKey := VK_VOLUME_MUTE;
2804      end;
2805      GDKX_KEY_AudioRaiseVolume:
2806      begin
2807        AExtended := True;
2808        AVKey := VK_VOLUME_UP;
2809      end;
2810      GDKX_KEY_AudioPlay:
2811      begin
2812        AExtended := True;
2813        AVKey := VK_MEDIA_PLAY_PAUSE;
2814      end;
2815      GDKX_KEY_AudioStop:
2816      begin
2817        AExtended := True;
2818        AVKey := VK_MEDIA_STOP;
2819      end;
2820      GDKX_KEY_AudioPrev:
2821      begin
2822        AExtended := True;
2823        AVKey := VK_MEDIA_PREV_TRACK;
2824      end;
2825      GDKX_KEY_AudioNext:
2826      begin
2827        AExtended := True;
2828        AVKey := VK_MEDIA_NEXT_TRACK;
2829      end;
2830      GDKX_KEY_Mail:
2831      begin
2832        AExtended := True;
2833        AVKey := VK_LAUNCH_MAIL;
2834      end;
2835      GDKX_KEY_HomePage:
2836      begin
2837        AExtended := True;
2838        AVKey := VK_BROWSER_HOME;
2839      end;
2840      GDKX_KEY_Back:
2841      begin
2842        AExtended := True;
2843        AVKey := VK_BROWSER_BACK;
2844      end;
2845      GDKX_KEY_Forward:
2846      begin
2847        AExtended := True;
2848        AVKey := VK_BROWSER_FORWARD;
2849      end;
2850      GDKX_KEY_Stop:
2851      begin
2852        AExtended := True;
2853        AVKey := VK_BROWSER_STOP;
2854      end;
2855      GDKX_KEY_Refresh:
2856      begin
2857        AExtended := True;
2858        AVKey := VK_BROWSER_REFRESH;
2859      end;
2860      GDKX_KEY_WWW:
2861      begin
2862        AExtended := True;
2863        AVKey := VK_BROWSER_HOME;
2864      end;
2865      GDKX_KEY_Favorites:
2866      begin
2867        AExtended := True;
2868        AVKey := VK_BROWSER_FAVORITES;
2869      end;
2870      GDKX_KEY_AudioMedia:
2871      begin
2872        AExtended := True;
2873        AVKey := VK_LAUNCH_MEDIA_SELECT;
2874      end;
2875      GDKX_KEY_MyComputer:
2876      begin
2877        AExtended := True;
2878        AVKey := VK_LAUNCH_APP1;
2879      end;
2880      GDKX_KEY_Calculator:
2881      begin
2882        AExtended := True;
2883        AVKey := VK_LAUNCH_APP2;
2884      end;
2885
2886      // For faster cases, group by families
2887      $400..$4FF: begin
2888        // Katakana
2889      end;
2890
2891      $500..$5FF: begin
2892        // Arabic
2893        case AKeySym of
2894          GDK_KEY_arabic_hamza:                  AVKey := VK_X;
2895          GDK_KEY_arabic_hamzaonwaw:             AVKey := VK_C;
2896          GDK_KEY_arabic_hamzaonyeh:             AVKey := VK_Z;
2897          GDK_KEY_arabic_alef:                   AVKey := VK_H;
2898          GDK_KEY_arabic_beh:                    AVKey := VK_F;
2899          GDK_KEY_arabic_tehmarbuta:             AVKey := VK_M;
2900          GDK_KEY_arabic_teh:                    AVKey := VK_J;
2901          GDK_KEY_arabic_theh:                   AVKey := VK_E;
2902          GDK_KEY_arabic_jeem:                   AVKey := VK_OEM_4;
2903          GDK_KEY_arabic_hah:                    AVKey := VK_P;
2904          GDK_KEY_arabic_khah:                   AVKey := VK_O;
2905          GDK_KEY_arabic_dal:                    AVKey := VK_OEM_6;
2906          GDK_KEY_arabic_thal:                   AVKey := VK_OEM_3;
2907          GDK_KEY_arabic_ra:                     AVKey := VK_V;
2908          GDK_KEY_arabic_zain:                   AVKey := VK_OEM_PERIOD;
2909          GDK_KEY_arabic_seen:                   AVKey := VK_S;
2910          GDK_KEY_arabic_sheen:                  AVKey := VK_A;
2911          GDK_KEY_arabic_sad:                    AVKey := VK_W;
2912          GDK_KEY_arabic_dad:                    AVKey := VK_Q;
2913          GDK_KEY_arabic_tah:                    AVKey := VK_OEM_7;
2914          GDK_KEY_arabic_zah:                    AVKey := VK_OEM_2;
2915          GDK_KEY_arabic_ain:                    AVKey := VK_U;
2916          GDK_KEY_arabic_ghain:                  AVKey := VK_Y;
2917          GDK_KEY_arabic_feh:                    AVKey := VK_T;
2918          GDK_KEY_arabic_qaf:                    AVKey := VK_R;
2919          GDK_KEY_arabic_kaf:                    AVKey := VK_OEM_1;
2920          GDK_KEY_arabic_lam:                    AVKey := VK_G;
2921          GDK_KEY_arabic_meem:                   AVKey := VK_L;
2922          GDK_KEY_arabic_noon:                   AVKey := VK_K;
2923          GDK_KEY_arabic_heh:                    AVKey := VK_I;
2924          GDK_KEY_arabic_waw:                    AVKey := VK_OEM_COMMA;
2925          GDK_KEY_arabic_alefmaksura:            AVKey := VK_N;
2926          GDK_KEY_arabic_yeh:                    AVKey := VK_D;
2927        end;
2928      end;
2929
2930      $600..$6FF: begin
2931        // Cyrillic
2932
2933        // MWE:
2934        // These VK codes are not compatible with all cyrillic KBlayouts
2935        // Example:
2936        // VK_A on a russian layout generates a cyrillic_EF
2937        // VK_A on a serbian layout generates a cyrillic_A
2938        //
2939        // Mapping cyrillic_A to VK_A is easier so that encoding is used.
2940        // Maybe in future we can take the KBLayout into account
2941        case AKeySym of
2942          GDK_KEY_cyrillic_a..GDK_KEY_cyrillic_ze:
2943          begin
2944            AVKey := VK_A + AKeySym - GDK_KEY_cyrillic_a;
2945          end;
2946          // Capital is not needed, the lower will match
2947          //GDK_KEY_cyrillic_A..GDK_KEY_cyrillic_ZE:
2948          //begin
2949          //  AVKey := VK_A + AKeySym - GDK_KEY_cyrillic_A;
2950          //end;
2951        end;
2952      end;
2953
2954      $700..$7FF: begin
2955        // Greek
2956        case AKeySym of
2957          // Capital is not needed, the lower will match
2958          GDK_KEY_greek_alpha:           AVKey := VK_A;
2959          GDK_KEY_greek_beta:            AVKey := VK_B;
2960          GDK_KEY_greek_gamma:           AVKey := VK_G;
2961          GDK_KEY_greek_delta:           AVKey := VK_D;
2962          GDK_KEY_greek_epsilon:         AVKey := VK_E;
2963          GDK_KEY_greek_zeta:            AVKey := VK_Z;
2964          GDK_KEY_greek_eta:             AVKey := VK_H;
2965          GDK_KEY_greek_theta:           AVKey := VK_U;
2966          GDK_KEY_greek_iota:            AVKey := VK_I;
2967          GDK_KEY_greek_kappa:           AVKey := VK_K;
2968          GDK_KEY_greek_lamda:           AVKey := VK_L;
2969          GDK_KEY_greek_mu:              AVKey := VK_M;
2970          GDK_KEY_greek_nu:              AVKey := VK_N;
2971          GDK_KEY_greek_xi:              AVKey := VK_J;
2972          GDK_KEY_greek_omicron:         AVKey := VK_O;
2973          GDK_KEY_greek_pi:              AVKey := VK_P;
2974          GDK_KEY_greek_rho:             AVKey := VK_R;
2975          GDK_KEY_greek_sigma:           AVKey := VK_S;
2976          GDK_KEY_greek_finalsmallsigma: AVKey := VK_W;
2977          GDK_KEY_greek_tau:             AVKey := VK_T;
2978          GDK_KEY_greek_upsilon:         AVKey := VK_Y;
2979          GDK_KEY_greek_phi:             AVKey := VK_F;
2980          GDK_KEY_greek_chi:             AVKey := VK_X;
2981          GDK_KEY_greek_psi:             AVKey := VK_C;
2982          GDK_KEY_greek_omega:           AVKey := VK_V;
2983        end;
2984      end;
2985
2986      $C00..$CFF: begin
2987        // Hebrew
2988        // Shifted keys will produce A..Z so the VK codes will be assigned there
2989      end;
2990
2991      $D00..$DFF: begin
2992        // Thai
2993        // To many differences to assign VK codes through lookup
2994        // Thai Kedmanee and Thai Pattachote are complete different layouts
2995      end;
2996
2997      $E00..$EFF: begin
2998        // Korean
2999      end;
3000    end;
3001  end;
3002
3003  function IgnoreShifted(const AUnshiftKeySym: Cardinal): Boolean;
3004  begin
3005    case AUnshiftKeySym of
3006      GDK_KEY_END,
3007      GDK_KEY_HOME,
3008      GDK_KEY_LEFT,
3009      GDK_KEY_RIGHT,
3010      GDK_KEY_UP,
3011      GDK_KEY_DOWN,
3012      GDK_KEY_PAGE_UP,
3013      GDK_KEY_PAGE_DOWN: Result := True;
3014    else
3015      Result := False;
3016    end;
3017  end;
3018
3019  procedure NextFreeVK(var AFreeVK: Byte);
3020  begin
3021    case AFreeVK of
3022      $96: AFreeVK := $E1;
3023      $E1: AFreeVK := $E3;
3024      $E4: AFreeVK := $E6;
3025      $E6: AFreeVK := $E9;
3026      $F5: begin
3027        {$ifndef HideKeyTableWarnings}
3028        DebugLn('[WARNING] Out of OEM specific VK codes, changing to unassigned');
3029        {$endif}
3030        AFreeVK := $88;
3031      end;
3032      $8F: AFreeVK := $97;
3033      $9F: AFreeVK := $D8;
3034      $DA: AFreeVK := $E5;
3035      $E5: AFreeVK := $E8;
3036      $E8: begin
3037        {$ifndef HideKeyTableWarnings}
3038        DebugLn('[WARNING] Out of unassigned VK codes, assigning $FF');
3039        {$endif}
3040        AFreeVK := $FF;
3041      end;
3042      $FF: AFreeVK := $FF; // stay there
3043    else
3044      Inc(AFreeVK);
3045    end;
3046  end;
3047
3048
3049const
3050  KEYFLAGS: array[0..3] of Byte = (
3051    $00,
3052    KCINFO_FLAG_SHIFT,
3053    KCINFO_FLAG_ALTGR,
3054    KCINFO_FLAG_ALTGR or KCINFO_FLAG_SHIFT
3055  );
3056  EXTFLAG: array[Boolean] of Byte = (
3057    $00,
3058    KCINFO_FLAG_EXT
3059  );
3060  MULTIFLAG: array[Boolean] of Byte = (
3061    $00,
3062    KCINFO_FLAG_SHIFT_XOR_NUM
3063  );
3064
3065{$ifdef HasX}
3066{
3067 Starting gdk 2.10 Alt, meta, hyper are reported by a own mask. Since we support
3068 older versions, we need to create the modifiermap ourselves for X and we cannot
3069 ise them
3070}
3071type
3072  TModMap = array[Byte] of Cardinal;
3073
3074  procedure SetupModifiers(ADisplay: Pointer; var AModMap: TModMap);
3075  const
3076    MODIFIERS: array[0..7] of Cardinal = (
3077      GDK_SHIFT_MASK,
3078      GDK_LOCK_MASK,
3079      GDK_CONTROL_MASK,
3080      GDK_MOD1_MASK,
3081      GDK_MOD2_MASK,
3082      GDK_MOD3_MASK,
3083      GDK_MOD4_MASK,
3084      GDK_MOD5_MASK
3085    );
3086  var
3087    Map: PXModifierKeymap;
3088    KeyCode: PKeyCode;
3089    Modifier, n: Integer;
3090  begin
3091    FillByte(AModMap, SizeOf(AModMap), 0);
3092
3093    Map := XGetModifierMapping(ADisplay);
3094    KeyCode := Map^.modifiermap;
3095
3096    for Modifier := Low(MODIFIERS) to High(MODIFIERS) do
3097    begin
3098      for n := 1 to Map^.max_keypermod do
3099      begin
3100        if KeyCode^ <> 0
3101        then begin
3102          AModMap[KeyCode^] := MODIFIERS[Modifier];
3103          {$ifdef VerboseModifiermap}
3104          DebugLn('Mapped keycode=%u to modifier=$%2.2x', [KeyCode^, MODIFIERS[Modifier]]);
3105          {$endif}
3106        end;
3107        Inc(KeyCode);
3108      end;
3109    end;
3110
3111    XFreeModifiermap(Map);
3112  end;
3113
3114  procedure UpdateModifierMap(const AModMap: TModMap; AKeyCode: Byte; AKeySym: Cardinal);
3115  var
3116  {$ifdef VerboseModifiermap}
3117    s: string;
3118  {$endif}
3119    ShiftState: TShiftStateEnum;
3120  begin
3121    if AModMap[AKeyCode] = 0 then Exit;
3122
3123    case AKeySym of
3124      GDK_KEY_Caps_Lock,
3125      GDK_KEY_Shift_Lock: ShiftState := ssCaps;
3126      GDK_KEY_Num_Lock: ShiftState := ssNum;
3127      GDK_KEY_Scroll_Lock: ShiftState := ssScroll;
3128      GDK_Key_Shift_L,
3129      GDK_Key_Shift_R: ShiftState := ssShift;
3130      GDK_KEY_Control_L,
3131      GDK_KEY_Control_R: ShiftState := ssCtrl;
3132      {$ifndef UseOwnShiftState}
3133      // UseOwnShiftState will track these, so we don't have to put them in the modmap
3134      GDK_KEY_Meta_L,
3135      GDK_KEY_Meta_R: ShiftState := ssMeta;
3136      GDK_KEY_Alt_L,
3137      GDK_KEY_Alt_R: ShiftState := ssAlt;
3138      GDK_KEY_Super_L,
3139      GDK_KEY_Super_R: ShiftState := ssSuper;
3140      GDK_KEY_Hyper_L,
3141      GDK_KEY_Hyper_R: ShiftState := ssHyper;
3142      GDK_KEY_ISO_Level3_Shift{,
3143      GDK_KEY_Mode_switch}: ShiftState := ssAltGr;
3144      {$endif}
3145    else
3146      Exit;
3147    end;
3148
3149    MModifiers[ShiftState].Mask := AModMap[AKeyCode];
3150    MModifiers[ShiftState].UseValue := False;
3151
3152    {$ifdef VerboseModifiermap}
3153    WriteStr(s, ShiftState);
3154    DebugLn('Mapped keycode=%u, keysym=$%x, modifier=$%2.2x to shiftstate %s',
3155            [AKeyCode, AKeySym, AModMap[AKeyCode], s]);
3156    {$endif}
3157
3158  end;
3159
3160  {$ifdef UseOwnShiftState}
3161  procedure UpdateKeyStateMap(var AIndex: integer; AKeyCode: Byte; AKeySym: Cardinal);
3162  var
3163    Enum: TShiftStateEnum;
3164  begin
3165    case AKeySym of
3166      GDK_KEY_Alt_L, GDK_KEY_Alt_R:     Enum := ssAlt;
3167      GDK_KEY_Meta_L, GDK_KEY_Meta_R:   Enum := ssMeta;
3168      GDK_KEY_Super_L, GDK_KEY_Super_R: Enum := ssSuper;
3169      GDK_KEY_Hyper_L, GDK_KEY_Hyper_R: Enum := ssHyper;
3170      GDK_KEY_ISO_Level3_Shift:         Enum := ssAltGr;
3171    else
3172      Exit;
3173    end;
3174
3175    if High(MKeyStateMap) < AIndex
3176    then SetLength(MKeyStateMap, AIndex + 8);
3177
3178    MKeyStateMap[AIndex].Index := AKeyCode shr 3;
3179    MKeyStateMap[AIndex].Mask := 1 shl (AKeyCode and 7);
3180    MKeyStateMap[AIndex].Enum := Enum;
3181    Inc(AIndex)
3182  end;
3183  {$endif UseOwnShiftState}
3184
3185{$endif HasX}
3186
3187const
3188  // first OEM specific VK
3189  VK_FIRST_OEM = $92;
3190
3191var
3192{$ifdef gtk1}
3193  XKeyEvent: TXKeyEvent;
3194  KeySymStart, KeySymNext: PKeySym;
3195  UpKeySym, LoKeySym: TKeySym;
3196  KeySyms: array of TKeySym;
3197{$else}
3198  KeySyms: array of guint;
3199  KeyVals: Pguint;
3200  KeymapKeys: PGdkKeymapKey;
3201  UniChar: gunichar;
3202{$endif}
3203  KeySymCount: Integer;
3204  KeySymChars: array[0..16] of Char;
3205  KeySymCharLen: Integer;
3206
3207{$ifdef HasX}
3208  XDisplay: Pointer;
3209  ModMap: TModMap;
3210{$endif}
3211{$ifdef UseOwnShiftState}
3212  KeyStateMapIndex: Integer;
3213{$endif}
3214
3215  KeyCode: Byte;
3216  m: Integer;
3217  LoKey, HiKey: Integer;
3218
3219  VKey, FreeVK: Byte;
3220  HasMultiVK, DummyBool, Extended, SecondKey, HasKey, ComputeVK: Boolean;
3221begin
3222{$ifdef HasX}
3223  XDisplay := gdk_display;
3224  if XDisplay = nil then Exit;
3225
3226  FillByte(MKeyStateMap, SizeOF(MKeyStateMap), 0);
3227  SetupModifiers(XDisplay, ModMap);
3228{$endif}
3229
3230{$ifdef gtk1}
3231  // Init dummy XEvent to retrieve the char corresponding to a key
3232  FillChar(XKeyEvent, SizeOf(XKeyEvent), 0);
3233  XKeyEvent._Type := GDK_KEY_PRESS;
3234  XKeyEvent.Display := XDisplay;
3235  XKeyEvent.Same_Screen := 1;
3236
3237  // Retrieve the KeyCode bounds
3238  XDisplayKeyCodes(XDisplay, @LoKey, @HiKey);
3239  if LoKey < 0
3240  then begin
3241    DebugLn('[WARNING] Low keycode (%d) negative, adjusting to 0', [LoKey]);
3242    LoKey := 0;
3243  end;
3244  if HiKey > 255
3245  then begin
3246    DebugLn('[WARNING] High keycode (%d) larget than 255, adjusting to 255', [HiKey]);
3247    HiKey := 255;
3248  end;
3249
3250  KeySymCount := 0;
3251  KeySymStart := XGetKeyboardMapping(XDisplay, LoKey, HiKey - LoKey + 1, @KeySymCount);
3252  KeySymNext := KeySymStart;
3253
3254  if (KeySymCount = 0) or (KeySymStart = nil)
3255  then begin
3256    DebugLn('[WARNING] failed to retrieve keyboardmapping');
3257    if KeySymStart <> nil
3258    then XFree(KeySymStart);
3259    Exit;
3260  end;
3261  if KeySymCount > Length(MVKeyInfo[0].KeySym)
3262  then DebugLn('[WARNING] keysymcount=%u larger than expected=%u', [KeySymCount, Length(MVKeyInfo[0].KeySym)]);
3263  SetLength(KeySyms, KeySymCount);
3264{$else gtk1}
3265  LoKey := 0;
3266  HiKey := 255;
3267{$endif}
3268
3269{$ifdef UseOwnShiftState}
3270  KeyStateMapIndex := 0;
3271{$endif}
3272  FreeVK := VK_FIRST_OEM;
3273  for KeyCode := LoKey to HiKey do
3274  begin
3275  {$ifdef gtk1}
3276    Move(KeySymNext^, KeySyms[0], SizeOf(KeySyms[0]) * KeySymCount);
3277    Inc(KeySymNext, KeySymCount);
3278
3279    HasKey := False;
3280    m := 0;
3281    while m < KeySymCount do
3282    begin
3283      // there might be only uppercase chars are in the map,
3284      // so we have to add the lowercase ourselves
3285      // when a group consists of one char(next =0)
3286      if KeySyms[m] <> 0
3287      then begin
3288        HasKey := True;
3289        if KeySyms[m+1] = 0
3290        then begin
3291          XConvertCase(KeySyms[m], @LoKeySym, @UpKeySym);
3292          if LoKeySym <> UpKeySym
3293          then begin
3294            KeySyms[m] := LoKeySym;
3295            KeySyms[m+1] := UpKeySym;
3296          end;
3297        end;
3298      end;
3299      Inc(m, 2);
3300    end;
3301
3302  {$else}
3303    if not gdk_keymap_get_entries_for_keycode(nil, KeyCode, KeymapKeys, KeyVals, @KeySymCount) then Continue;
3304    SetLength(KeySyms, KeySymCount);
3305    Move(KeyVals^, KeySyms[0], SizeOf(KeySyms[0]) * KeySymCount);
3306    g_free(KeymapKeys); // unused but we cannot pass a nil as param
3307    g_free(KeyVals);
3308    HasKey := KeySyms[0] <> 0;
3309    //DebugLn(['InitKeyboardTables ',KeyCode,' ',HasKey,' ',KeySyms[0]]);
3310  {$endif}
3311
3312  {$ifdef HasX}
3313    // Check if this keycode is in the modifiers map
3314    // loop through all keysyms till one found.
3315    // Some maps have a modifier with an undefined first keysym. It is checked for
3316    // modifiers, but not for vkeys
3317    for m := 0 to KeySymCount - 1 do
3318    begin
3319      if KeySyms[m] = 0 then Continue;
3320      UpdateModifierMap(ModMap, KeyCode, KeySyms[m]);
3321      {$ifdef UseOwnShiftState}
3322      UpdateKeyStateMap(KeyStateMapIndex, KeyCode, KeySyms[m]);
3323      {$endif}
3324      Break;
3325    end;
3326  {$endif}
3327
3328    // Continue if there is no keysym found
3329    if not HasKey then Continue;
3330
3331    // Start looking for a VKcode
3332    VKey := VK_UNDEFINED;
3333    for m := 0 to KeySymCount - 1 do
3334    begin
3335      if KeySyms[m] = 0 then Continue;
3336      FindVKeyInfo(KeySyms[m], VKey, Extended, HasMultiVK, SecondKey);
3337    {$ifdef Windows}
3338      // on windows, the keycode is perdef the VK,
3339      // we only enter this loop to set the correct flags
3340      VKey := KeyCode;
3341      Break;
3342    {$else}
3343      if HasMultiVK then Break; // has VK per def
3344      if VKey = VK_UNDEFINED then Continue;
3345      if MVKeyInfo[VKey].KeyCode[SecondKey or Extended] = 0 then Break; // found unused VK
3346
3347      // already in use
3348      VKey := VK_UNDEFINED;
3349    {$endif}
3350    end;
3351
3352    ComputeVK := VKey = VK_UNDEFINED;
3353    if ComputeVK and not HasMultiVK
3354    then begin
3355      VKey := FreeVK;
3356      NextFreeVK(FreeVK);
3357    end;
3358
3359    if VKey = VK_UNDEFINED
3360    then begin
3361      MKeyCodeInfo[KeyCode].Flags := $FF
3362    end
3363    else begin
3364      MKeyCodeInfo[KeyCode].Flags := EXTFLAG[Extended] or MULTIFLAG[HasMultiVK];
3365      MVKeyInfo[VKey].KeyCode[SecondKey] := KeyCode;
3366    end;
3367    MKeyCodeInfo[KeyCode].VKey1 := VKey;
3368
3369    for m := 0 to Min(High(MVKeyInfo[0].KeyChar), KeySymCount - 1) do
3370    begin
3371      if KeySyms[m] = 0 then Continue;
3372      if (m >= 2) and (KeySyms[m] = KeySyms[m - 2]) then Continue;
3373
3374      if HasMultiVK
3375      then begin
3376        if m >= 2 then Break; // Only process shift
3377
3378        // The keypadkeys have 2 VK_keycodes :(
3379        // In that case we have to FIndKeyInfo for every keysym
3380        if m = 1
3381        then begin
3382          FindVKeyInfo(KeySyms[m], VKey, Extended, DummyBool, DummyBool);
3383          MKeyCodeInfo[KeyCode].VKey2 := VKey;
3384        end;
3385      end;
3386      if VKey = VK_UNDEFINED then Continue;
3387
3388      MKeyCodeInfo[KeyCode].Flags := MKeyCodeInfo[KeyCode].Flags or KEYFLAGS[m];
3389
3390      FillByte(KeySymChars, SizeOf(KeySymChars), 0);
3391    {$ifdef gtk1}
3392      // Retrieve the chars for this KeySym
3393      XKeyEvent.KeyCode := KeyCode;
3394      case m of
3395        0: XKeyEvent.State := 0;
3396        1: XKeyEvent.State := MModifiers[ssShift].Mask;
3397        2: XKeyEvent.State := MModifiers[ssAltGr].Mask;
3398        3: XKeyEvent.State := MModifiers[ssAltGr].Mask or MModifiers[ssShift].Mask;
3399      else
3400        // TODO: m > 3 ??
3401        Continue;
3402      end;
3403
3404      KeySymCharLen := XLookupString(@XKeyEvent, KeySymChars, SizeOf(KeySymChars), nil, nil);
3405      if (KeySymCharLen > 0) and (KeySymChars[KeySymCharLen - 1] = #0)
3406      then Dec(KeySymCharLen);
3407      if (KeySymCharLen <= 0) then Continue;
3408    {$else gtk1}
3409      UniChar := gdk_keyval_to_unicode(KeySyms[m]);
3410      if UniChar = 0 then Continue;
3411      KeySymCharLen := g_unichar_to_utf8(UniChar, @KeySymChars[0]);
3412    {$endif}
3413      if (KeySymCharLen > SizeOf(TVKeyUTF8Char))
3414      then DebugLn('[WARNING] InitKeyboardTables - Keysymstring for keycode=%u longer than %u bytes: %s', [KeyCode, SizeOf(TVKeyUTF8Char), KeySymChars]);
3415      Move(KeySymChars[0], MVKeyInfo[VKey].KeyChar[m], SizeOf(TVKeyUTF8Char));
3416    end;
3417  end;
3418{$ifdef UseOwnShiftState}
3419  SetLength(MKeyStateMap, KeyStateMapIndex);
3420{$endif}
3421
3422{$ifdef gtk1}
3423  XFree(KeySymStart);
3424{$endif}
3425end;
3426
3427{------------------------------------------------------------------------------
3428  Procedure: DoneKeyboardTables
3429  Params:    none
3430  Returns:   none
3431
3432  Frees the dynamic keyboard tables
3433 ------------------------------------------------------------------------------}
3434procedure DoneKeyboardTables;
3435var
3436  i: Integer;
3437begin
3438  if LCLHandledKeyEvents<>nil then begin
3439    for i:=0 to LCLHandledKeyEvents.Count-1 do
3440      TObject(LCLHandledKeyEvents[i]).Free;
3441    LCLHandledKeyEvents.Free;
3442    LCLHandledKeyEvents:=nil;
3443  end;
3444  if LCLHandledKeyAfterEvents<>nil then begin
3445    for i:=0 to LCLHandledKeyAfterEvents.Count-1 do
3446      TObject(LCLHandledKeyAfterEvents[i]).Free;
3447    LCLHandledKeyAfterEvents.Free;
3448    LCLHandledKeyAfterEvents:=nil;
3449  end;
3450end;
3451
3452{------------------------------------------------------------------------------
3453  Function:  GetVKeyInfo
3454  Params:    AVKey: A virtual key to get the info for
3455  Returns:   A Info record
3456
3457  This function is more a safety to make sure MVkeyInfo isn't accessed out of
3458  it's bounds
3459 ------------------------------------------------------------------------------}
3460function GetVKeyInfo(const AVKey: Byte): TVKeyInfo;
3461begin
3462  Result := MVKeyInfo[AVKey];
3463end;
3464
3465{------------------------------------------------------------------------------
3466  Procedure: GTKEventState2ShiftState
3467  Params:    KeyState: The gtk keystate
3468  Returns:   the TShiftState for the given KeyState
3469
3470  GTKEventStateToShiftState converts a GTK event state to a LCL/Delphi TShiftState
3471 ------------------------------------------------------------------------------}
3472function GTKEventStateToShiftState(KeyState: Word): TShiftState;
3473  {$ifdef HasX}
3474  function GetState: TShiftState;
3475  var
3476    Keys: chararr32;
3477    n: Integer;
3478  begin
3479    Result := [];
3480    keys:='';
3481    XQueryKeyMap(gdk_display, Keys);
3482    for n := Low(MKeyStateMap) to High(MKeyStateMap) do
3483    begin
3484      if Ord(Keys[MKeyStateMap[n].Index]) and MKeyStateMap[n].Mask = 0 then Continue;
3485      Include(Result, MKeyStateMap[n].Enum);
3486      Break;
3487    end;
3488  end;
3489  {$else}
3490  {$ifdef windows}
3491  function GetState: TShiftState;
3492  begin
3493    Result := [];
3494    if GetKeyState(VK_MENU) < 0 then Include(Result, ssAlt);
3495    if (GetKeyState(VK_LWIN) < 0) or (GetKeyState(VK_RWIN) < 0) then Include(Result, ssMeta);
3496  end;
3497  {$else}
3498  function GetState: TShiftState;
3499  begin
3500    Result := [];
3501  end;
3502  {$endif}
3503  {$endif}
3504
3505var
3506  State: TShiftStateEnum;
3507begin
3508  {$ifdef UseOwnShiftState}
3509  Result := GetState;
3510  {$else}
3511  Result := [];
3512  {$endif}
3513  for State := Low(State) to High(State) do
3514  begin
3515    if MModifiers[State].Mask = 0 then Continue;
3516    if MModifiers[State].UseValue
3517    then begin
3518      if KeyState and MModifiers[State].Mask = MModifiers[State].Value
3519      then Include(Result, State);
3520    end
3521    else begin
3522      if KeyState and MModifiers[State].Mask <> 0
3523      then Include(Result, State);
3524    end;
3525  end;
3526end;
3527
3528{------------------------------------------------------------------------------
3529  Procedure: StoreCommonDialogSetup
3530  Params:    ADialog: TCommonDialog
3531  Returns:   none
3532
3533  Stores the size of a TCommonDialog.
3534 ------------------------------------------------------------------------------}
3535procedure StoreCommonDialogSetup(ADialog: TCommonDialog);
3536var DlgWindow: PGtkWidget;
3537begin
3538  if (ADialog=nil) or (ADialog.Handle=0) then exit;
3539  DlgWindow:=PGtkWidget(ADialog.Handle);
3540  if DlgWindow^.Allocation.Width>0 then
3541    ADialog.Width:=DlgWindow^.Allocation.Width;
3542  if DlgWindow^.Allocation.Height>0 then
3543    ADialog.Height:=DlgWindow^.Allocation.Height;
3544end;
3545
3546{------------------------------------------------------------------------------
3547  Procedure: DestroyCommonDialogAddOns
3548  Params:    ADialog: TCommonDialog
3549  Returns:   none
3550
3551  Free the memory of additional data of a TCommonDialog
3552 ------------------------------------------------------------------------------}
3553procedure DestroyCommonDialogAddOns(ADialog: TCommonDialog);
3554var
3555  DlgWindow: PGtkWidget;
3556  HistoryList: TFPList; // list of TFileSelHistoryListEntry
3557  AHistoryEntry: PFileSelHistoryEntry;
3558  i: integer;
3559  FileSelWidget: PGtkFileSelection;
3560  LCLHistoryMenu: PGTKWidget;
3561  {$IFDEF Gtk1}
3562  //AFilterEntry: TFileSelFilterEntry;
3563  FilterList: TFPList; // list of TFileSelFilterListEntry
3564  LCLFilterMenu: PGTKWidget;
3565  {$ENDIF}
3566begin
3567  if (ADialog=nil) or (not ADialog.HandleAllocated) then exit;
3568  DlgWindow:=PGtkWidget(ADialog.Handle);
3569  {$IFDEF VerboseTransient}
3570  DebugLn('DestroyCommonDialogAddOns ',ADialog.Name,':',ADialog.ClassName);
3571  {$ENDIF}
3572  gtk_window_set_transient_for(PGtkWindow(DlgWindow),nil);
3573  if ADialog is TOpenDialog then begin
3574    {$IFDEF GTK2}
3575    FileSelWidget:=GTK_FILE_CHOOSER(DlgWindow);
3576    {$ELSE}
3577    FileSelWidget:=GTK_FILE_SELECTION(DlgWindow);
3578    FreeWidgetInfo(FileSelWidget^.selection_entry);
3579    FreeWidgetInfo(FileSelWidget^.dir_list);
3580    FreeWidgetInfo(FileSelWidget^.file_list);
3581    LCLFilterMenu:=PGTKWidget(gtk_object_get_data(PGtkObject(FileSelWidget),
3582                                       'LCLFilterMenu'));
3583    if LCLFilterMenu<>nil then FreeWidgetInfo(LCLFilterMenu);
3584    {$ENDIF}
3585    LCLHistoryMenu:=PGTKWidget(gtk_object_get_data(PGtkObject(FileSelWidget),
3586                                       'LCLHistoryMenu'));
3587    if LCLHistoryMenu<>nil then FreeWidgetInfo(LCLHistoryMenu);
3588
3589    // free history
3590    HistoryList:=TFPList(gtk_object_get_data(PGtkObject(DlgWindow),
3591                                             'LCLHistoryList'));
3592    if HistoryList<>nil then begin
3593      for i:=0 to HistoryList.Count-1 do begin
3594        AHistoryEntry:=PFileSelHistoryEntry(HistoryList[i]);
3595        StrDispose(AHistoryEntry^.Filename);
3596        AHistoryEntry^.Filename:=nil;
3597        Dispose(AHistoryEntry);
3598      end;
3599      HistoryList.Free;
3600      gtk_object_set_data(PGtkObject(DlgWindow),'LCLHistoryList',nil);
3601    end;
3602
3603    {$IFDEF GTK1}
3604    // free filter
3605    FilterList:=TFPList(gtk_object_get_data(PGtkObject(DlgWindow),
3606                                            'LCLFilterList'));
3607    if FilterList<>nil then begin
3608      for i:=0 to FilterList.Count-1 do
3609        TObject(FilterList[i]).Free;
3610      FilterList.Free;
3611      gtk_object_set_data(PGtkObject(DlgWindow),'LCLFilterList',nil);
3612    end;
3613    {$ENDIF}
3614
3615    // free preview handle
3616    if ADialog is TPreviewFileDialog then begin
3617      if TPreviewFileDialog(ADialog).PreviewFileControl<>nil then
3618        TPreviewFileDialog(ADialog).PreviewFileControl.Handle:=0;
3619    end;
3620  end;
3621end;
3622
3623{------------------------------------------------------------------------------
3624  Procedure: PopulateFileAndDirectoryLists
3625  Params:    FileSelection: PGtkFileSelection;
3626              Mask: string (File mask, such as *.txt)
3627  Returns:   none
3628
3629  Populate the directory and file lists according to the given mask
3630 ------------------------------------------------------------------------------}
3631procedure PopulateFileAndDirectoryLists(FileSelection: PGtkFileSelection;
3632  const Mask: string);
3633var
3634  Dirs, Files: PGtkCList;
3635  Text: array [0..1] of Pgchar;
3636  Info: TSearchRec;
3637  DirName: PChar;
3638  Dir: string;
3639  StrList: TStringList;
3640  CurFileMask: String;
3641
3642  procedure Add(List: PGtkCList; const s: string);
3643  begin
3644    Text[0] := PChar(s);
3645    gtk_clist_append(List, Text);
3646  end;
3647
3648  procedure AddList(List: PGtkCList);
3649  var
3650    i: integer;
3651  begin
3652    StrList.Sorted := True;
3653    //DebugLn(['AddList ',StrList.Text]);
3654    for i:=0 to StrList.Count-1 do
3655      Add(List, StrList[i]);
3656    StrList.Sorted := False;
3657  end;
3658
3659begin
3660  StrList := TStringList.Create;
3661  dirs := PGtkCList(FileSelection^.dir_list);
3662  files := PGtkCList(FileSelection^.file_list);
3663  DirName := gtk_file_selection_get_filename(FileSelection);
3664  if DirName <> nil then begin
3665    SetString(Dir, DirName, strlen(DirName));
3666    SetLength(Dir, LastDelimiter(PathDelim,Dir));
3667  end else
3668    Dir := '';
3669  //DebugLn(['PopulateFileAndDirectoryLists ',Dir]);
3670  Text[1] := nil;
3671  gtk_clist_freeze(Dirs);
3672  gtk_clist_clear(Dirs);
3673  gtk_clist_freeze(Files);
3674  gtk_clist_clear(Files);
3675  { Add all directories }
3676  Strlist.Add('..'+PathDelim);
3677  if FindFirstUTF8(AppendPathDelim(Dir)+GetAllFilesMask, faAnyFile and faDirectory,
3678    Info) = 0
3679  then begin
3680    repeat
3681      if ((Info.Attr and faDirectory) = faDirectory) and (Info.Name <> '.')
3682      and (Info.Name <> '..') and (Info.Name<>'') then
3683        StrList.Add(AppendPathDelim(Info.Name));
3684    until FindNextUTF8(Info) <> 0;
3685  end;
3686  FindCloseUTF8(Info);
3687  AddList(Dirs);
3688  // add required files
3689  StrList.Clear;
3690  CurFileMask:=Mask;
3691  if CurFileMask='' then CurFileMask:=GetAllFilesMask;
3692  if FindFirstUTF8(AppendPathDelim(Dir)+GetAllFilesMask, faAnyFile, Info) = 0 then
3693  begin
3694    repeat
3695      if ((Info.Attr and faDirectory) <> faDirectory) then begin
3696        //debugln('PopulateFileAndDirectoryLists CurFileMask="',CurFileMask,'" Info.Name="',Info.Name,'" ',dbgs(MatchesMaskList(Info.Name,CurFileMask)));
3697        if (CurFileMask='') or (MatchesMaskList(Info.Name,CurFileMask)) then
3698        begin
3699          Strlist.Add(Info.Name);
3700        end;
3701      end;
3702    until FindNextUTF8(Info) <> 0;
3703  end;
3704  FindCloseUTF8(Info);
3705  AddList(Files);
3706  StrList.Free;
3707  gtk_clist_thaw(Dirs);
3708  gtk_clist_thaw(Files);
3709end;
3710
3711{------------------------------------------------------------------------------
3712  Procedure: DeliverMessage
3713  Params:    Message: the message to process
3714  Returns:   True if handled
3715
3716  Generic function which calls the WindowProc if defined, otherwise the
3717  dispatcher
3718 ------------------------------------------------------------------------------}
3719function DeliverMessage(const Target: Pointer; var AMessage): PtrInt;
3720begin
3721  if (TLMessage(AMessage).Msg = LM_PAINT) or
3722     (TLMessage(AMessage).Msg = LM_GTKPAINT) then
3723    CurrentSentPaintMessageTarget := TObject(Target);
3724
3725  Result := LCLMessageGlue.DeliverMessage(TObject(Target), AMessage);
3726
3727  CurrentSentPaintMessageTarget := nil;
3728end;
3729
3730{------------------------------------------------------------------------------
3731  Function: ObjectToGTKObject
3732  Params: AnObject: A LCL Object
3733  Returns:  The GTKObject of the given object
3734
3735  Returns the GTKObject of the given object, nil if no object available
3736 ------------------------------------------------------------------------------}
3737function ObjectToGTKObject(const AnObject: TObject): PGtkObject;
3738var
3739  handle : HWND;
3740begin
3741  Handle := 0;
3742  if not assigned(AnObject) then
3743  begin
3744    assert (false, 'TRACE:  [ObjectToGtkObject] Object not assigned');
3745  end
3746  else if (AnObject is TWinControl) then
3747  begin
3748    if TWinControl(AnObject).HandleAllocated then
3749      handle := TWinControl(AnObject).Handle;
3750  end
3751  else if (AnObject is TMenuItem) then
3752  begin
3753    if TMenuItem(AnObject).HandleAllocated then
3754      handle := TMenuItem(AnObject).Handle;
3755  end
3756  else if (AnObject is TMenu) then
3757  begin
3758    if TMenu(AnObject).HandleAllocated then
3759      handle := TMenu(AnObject).Items.Handle;
3760  end
3761  else if (AnObject is TCommonDialog) then
3762  begin
3763    {if TCommonDialog(AObject).HandleAllocated then }
3764    handle := TCommonDialog(AnObject).Handle;
3765  end
3766  else begin
3767    //DebugLn(Format('Trace:  [ObjectToGtkObject] Message received with unhandled class-type <%s>', [AnObject.ClassName]));
3768  end;
3769  Result := PGTKObject(handle);
3770  if handle = 0 then
3771    Assert (false, 'Trace:  [ObjectToGtkObject]****** Warning: handle = 0 *******');
3772end;
3773
3774
3775(***********************************************************************
3776  Widget member functions
3777************************************************************************)
3778
3779// ----------------------------------------------------------------------
3780// the main widget is the widget passed as handle to the winAPI
3781// main data is stored in the fixed form to get a reference to its parent
3782// ----------------------------------------------------------------------
3783function GetMainWidget(const Widget: Pointer): Pointer;
3784begin
3785  if Widget = nil
3786  then raise EInterfaceException.Create('GetMainWidget Widget=nil');
3787
3788  Result := gtk_object_get_data(Widget, 'Main');
3789  if Result = nil then Result := Widget; // the widget is the main widget itself.
3790end;
3791
3792procedure SetMainWidget(const ParentWidget, ChildWidget: Pointer);
3793begin
3794  if ParentWidget = nil
3795  then raise EInterfaceException.Create('SetMainWidget ParentWidget=nil');
3796  if ChildWidget = nil
3797  then raise EInterfaceException.Create('SetMainWidget ChildWidget=nil');
3798  if ParentWidget = ChildWidget
3799  then raise EInterfaceException.Create('SetMainWidget ParentWidget=ChildWidget');
3800  {$IFDEF Gtk2}
3801  if PGtkWidget(ParentWidget)^.parent=ChildWidget
3802  then raise EInterfaceException.Create('SetMainWidget Parent^.Parent=ChildWidget');
3803  {$ENDIF}
3804
3805  gtk_object_set_data(ChildWidget, 'Main', ParentWidget)
3806end;
3807
3808{ ------------------------------------------------------------------------------
3809 Get the fixed widget of a widget.
3810 Every LCL control with a clientarea, has at least a main widget for the control
3811 and a fixed widget for the client area. If the Fixed widget is not set, use
3812 try to get it trough WinWidgetInfo
3813------------------------------------------------------------------------------ }
3814//TODO: remove when WinWidgetInfo implementation is complete
3815function GetFixedWidget(const Widget: Pointer): Pointer;
3816var
3817  WidgetInfo: PWinWidgetInfo;
3818begin
3819  if Widget = nil
3820  then raise EInterfaceException.Create('GetFixedWidget Widget=nil');
3821
3822  WidgetInfo := GetWidgetInfo(Widget, False);
3823  if WidgetInfo <> nil
3824  then Result := WidgetInfo^.ClientWidget
3825  else Result := nil;
3826  if Result <> nil then Exit;
3827
3828  Result := gtk_object_get_data(Widget, 'Fixed');
3829  // A last resort
3830  if Result = nil then Result := Widget;
3831end;
3832
3833{ ------------------------------------------------------------------------------
3834 Set the fixed widget of a widget.
3835 Every LCL control with a clientarea, has at least a main widget for the control
3836 and a fixed widget for the client area.
3837------------------------------------------------------------------------------ }
3838procedure SetFixedWidget(const ParentWidget, FixedWidget: Pointer);
3839var
3840  WidgetInfo: PWinWidgetInfo;
3841begin
3842  if ParentWidget = nil
3843  then raise EInterfaceException.Create('SetFixedWidget ParentWidget=nil');
3844
3845  WidgetInfo := GetWidgetInfo(ParentWidget, True);
3846  WidgetInfo^.ClientWidget := FixedWidget;
3847  //TODO: remove old compatebility
3848  gtk_object_set_data(ParentWidget, 'Fixed', FixedWidget)
3849end;
3850
3851{-------------------------------------------------------------------------------
3852  Set the LCLobject which created this widget.
3853
3854-------------------------------------------------------------------------------}
3855procedure SetLCLObject(const Widget: Pointer; const AnObject: TObject);
3856var
3857  WidgetInfo: PWinWidgetInfo;
3858begin
3859  if Widget = nil
3860  then raise EInterfaceException.Create('SetLCLObject Widget=nil');
3861  if AnObject = nil
3862  then raise EInterfaceException.Create('SetLCLObject AnObject=nil');
3863
3864  WidgetInfo := GetWidgetInfo(Widget, True);
3865  WidgetInfo^.LCLObject := AnObject;
3866end;
3867
3868function GetLCLObject(const Widget: Pointer): TObject;
3869var
3870  WidgetInfo: PWinWidgetInfo;
3871begin
3872  if Widget = nil
3873  then raise EInterfaceException.Create('GetLCLObject Widget=nil');
3874
3875  WidgetInfo := GetWidgetInfo(Widget);
3876  if WidgetInfo <> nil
3877  then Result := WidgetInfo^.LCLObject
3878  else Result := nil;
3879end;
3880
3881{-------------------------------------------------------------------------------
3882 Some need the HiddenLCLobject which created a parent of this widget.
3883
3884 MWE: is this obsolete ?
3885-------------------------------------------------------------------------------}
3886procedure SetHiddenLCLObject(const Widget: Pointer; const AnObject: TObject);
3887begin
3888  if (Widget <> nil) then
3889    gtk_object_set_data(Widget, 'LCLHiddenClass', Pointer(AnObject));
3890end;
3891
3892function GetHiddenLCLObject(const Widget: Pointer): TObject;
3893begin
3894  Result := TObject(gtk_object_get_data(Widget, 'LCLHiddenClass'));
3895end;
3896
3897{-------------------------------------------------------------------------------
3898  function GetNearestLCLObject(Widget: PGtkWidget): TObject;
3899
3900  Retrieves the LCLObject belonging to the widget. If the widget is created as
3901  child of a main widget, the parent is queried.
3902
3903  This function probably obsoletes Get/SetMainWidget
3904-------------------------------------------------------------------------------}
3905//TODO: check if Get/SetMainWidget is still required
3906function GetNearestLCLObject(Widget: PGtkWidget): TObject;
3907begin
3908  while (Widget<>nil) do begin
3909    Result:=GetLCLObject(Widget);
3910    if Result<>nil then exit;
3911    Widget:=Widget^.Parent;
3912  end;
3913  Result:=nil;
3914end;
3915
3916function CreateFixedClientWidget(WithWindow: Boolean = True): PGTKWidget;
3917begin
3918  Result := gtk_fixed_new();
3919  {$IFDEF GTK2}
3920  if WithWindow then
3921    gtk_fixed_set_has_window(PGtkFixed(Result), true);
3922  {$ENDIF}
3923end;
3924
3925{------------------------------------------------------------------------------
3926  procedure FixedMoveControl(Parent, Child : PGTKWidget; Left, Top : Longint);
3927
3928  Move a childwidget on a client area (fixed or layout widget).
3929------------------------------------------------------------------------------}
3930procedure FixedMoveControl(Parent, Child : PGTKWidget; Left, Top : Longint);
3931begin
3932  If GTKWidgetIsA(Parent, GTK_Layout_Get_Type) then begin
3933    // parent is layout
3934    gtk_Layout_move(PGtkLayout(Parent), Child, Left, Top)
3935  end else If GTKWidgetIsA(Parent, GTK_Fixed_Get_Type) then begin
3936    // parent is fixed
3937    gtk_fixed_move(PGtkFixed(Parent), Child, gint16(Left), gint16(Top));
3938  end else begin
3939    // parent is invalid
3940    DebugLn('[FixedMoveControl] WARNING: Invalid Fixed Widget');
3941  end;
3942end;
3943
3944{------------------------------------------------------------------------------
3945  procedure FixedPutControl(Parent, Child : PGTKWIdget; Left, Top : Longint);
3946
3947  Add a childwidget onto a client area (fixed or layout widget).
3948------------------------------------------------------------------------------}
3949procedure FixedPutControl(Parent, Child: PGTKWidget; Left, Top: Longint);
3950
3951  procedure RaiseInvalidFixedWidget;
3952  begin
3953    // this is in a separate procedure for optimisation
3954    DebugLn('[FixedPutControl] WARNING: Invalid Fixed Widget.',
3955      ' Parent=',DbgS(Parent),
3956      ' Child=',DbgS(Child)
3957      );
3958  end;
3959
3960begin
3961  if GtkWidgetIsA(Parent, gtk_fixed_get_type) then
3962    gtk_fixed_put(PGtkFixed(Parent), Child, gint16(Left), gint16(Top))
3963  else
3964  if GtkWidgetIsA(Parent, gtk_layout_get_type) then
3965    gtk_layout_put(PGtkLayout(Parent), Child, Left, Top)
3966  else
3967    RaiseInvalidFixedWidget;
3968end;
3969
3970function GetWinControlWidget(Child: PGtkWidget): PGtkWidget;
3971// return the first widget, which is associated with a TWinControl handle
3972var
3973  LCLParent: TObject;
3974begin
3975  Result:=nil;
3976  LCLParent:=GetNearestLCLObject(Child);
3977  if (LCLParent=nil) or (not (LCLParent is TWinControl))
3978  or (not TWinControl(LCLParent).HandleAllocated)
3979  then exit;
3980  Result:=PGtkWidget(TWinControl(LCLParent).Handle);
3981end;
3982
3983function GetWinControlFixedWidget(Child: PGtkWidget): PGtkWidget;
3984begin
3985  Result:=GetWinControlWidget(Child);
3986  if Result=nil then exit;
3987  Result:=GetFixedWidget(Result);
3988end;
3989
3990function FindFixedChildListItem(ParentFixed: PGtkFixed; Child: PGtkWidget): PGList;
3991begin
3992  Result:=ParentFixed^.children;
3993  while (Result<>nil) do begin
3994    if (Result^.Data<>nil) and (PGtkFixedChild(Result^.Data)^.Widget=Child) then
3995      exit;
3996    Result:=Result^.Next;
3997  end;
3998end;
3999
4000function FindFixedLastChildListItem(ParentFixed: PGtkFixed): PGList;
4001begin
4002  Result:=g_list_last(ParentFixed^.children);
4003end;
4004
4005function GetFixedChildListWidget(Item: PGList): PGtkWidget;
4006begin
4007  Result:=PGtkFixedChild(Item^.Data)^.Widget;
4008end;
4009
4010{------------------------------------------------------------------------------
4011  procedure MoveGListLinkBehind(First, Item, After: PGList);
4012
4013  Move the list item 'Item' behind the list item 'After'.
4014  If After=nil then insert as first item.
4015------------------------------------------------------------------------------}
4016procedure MoveGListLinkBehind(First, Item, After: PGList);
4017var
4018  Data: Pointer;
4019  NewPos: Integer;
4020begin
4021  if (Item=After) or (Item^.Next=After) then exit;
4022  if (g_list_position(First,Item)<0) then
4023    RaiseGDBException('MoveGListLinkBehind Item not found');
4024  if (After<>nil) and (g_list_position(First,After)<0) then
4025    RaiseGDBException('MoveGListLinkBehind After not found');
4026  Data:=Item^.Data;
4027  g_list_remove_link(First,Item);
4028  if After<>nil then begin
4029    NewPos:=g_list_position(First,After)+1;
4030  end else begin
4031    NewPos:=0;
4032  end;
4033  g_list_insert(First,Data,NewPos);
4034end;
4035
4036procedure MoveGListLink(First: PGList; FromIndex, ToIndex: integer);
4037var
4038  Item: PGList;
4039  InsertAfter: PGList;
4040  i: Integer;
4041begin
4042  if (FromIndex=ToIndex) then exit;
4043  Item:=First;
4044  i:=0;
4045  while (i<FromIndex) do begin
4046    Item:=Item^.next;
4047    inc(i);
4048  end;
4049  // unbind
4050  if Item^.next<>nil then Item^.next^.prev:=Item^.prev;
4051  if Item^.prev<>nil then Item^.prev^.next:=Item^.next;
4052  Item^.next:=nil;
4053  Item^.prev:=nil;
4054  // insert
4055  if ToIndex=0 then begin
4056    Item^.next:=First;
4057    First^.prev:=Item;
4058  end else begin
4059    i:=0;
4060    InsertAfter:=First;
4061    while (i<ToIndex-1) do begin
4062      if InsertAfter^.next=nil then break;
4063      InsertAfter:=InsertAfter^.next;
4064      inc(i);
4065    end;
4066    Item^.prev:=InsertAfter;
4067    Item^.next:=InsertAfter^.next;
4068    InsertAfter^.next:=Item;
4069    if Item^.next<>nil then Item^.next^.prev:=Item;
4070  end;
4071end;
4072
4073{------------------------------------------------------------------------------
4074  function GetControlWindow(Widget: Pointer) : PGDKWindow;
4075
4076  Get the gdkwindow of a widget.
4077------------------------------------------------------------------------------}
4078function GetControlWindow(Widget: Pointer) : PGDKWindow;
4079begin
4080  if Widget <> nil then
4081  begin
4082    If not GTKWidgetIsA(PGTKWidget(Widget), GTK_Layout_Get_Type) then
4083      Result := PGTKWidget(Widget)^.Window
4084    else
4085      Result := PGtkLayout(Widget)^.bin_window;
4086    {$IFDEF Gtk2}
4087    if (Result=nil) and (GTK_WIDGET_NO_WINDOW(Widget)) then
4088      Result:=gtk_widget_get_parent_window(Widget);
4089    {$ENDIF}
4090  end else
4091    RaiseGDBException('GetControlWindow Widget=nil');
4092end;
4093
4094
4095{------------------------------------------------------------------------------
4096  function CreateWidgetInfo(const AWidget: Pointer): PWidgetInfo;
4097
4098 Creates a WidgetInfo structure for the given widget
4099 Info needed by the API of a HWND (=Widget)
4100
4101 This structure obsoletes all other object data, like
4102   "core-child", "fixed", "class"
4103 ------------------------------------------------------------------------------}
4104function CreateWidgetInfo(const AWidget: Pointer): PWidgetInfo;
4105begin
4106  if AWidget = nil then Result:= nil
4107  else begin
4108    New(Result);
4109    FillChar(Result^, SizeOf(Result^), 0);
4110    gtk_object_set_data(AWidget, 'widgetinfo', Result);
4111    Result^.DefaultCursor := HCursor(-1);
4112  end;
4113end;
4114
4115function CreateWidgetInfo(const AWidget: Pointer; const AObject: TObject;
4116  const AParams: TCreateParams): PWidgetInfo;
4117begin
4118  Result := CreateWidgetInfo(AWidget);
4119  if Result = nil then Exit;
4120
4121  Result^.LCLObject := AObject;
4122  // in most cases the created widget is the core widget
4123  // so default to it
4124  Result^.CoreWidget := AWidget;
4125  Result^.Style := AParams.Style;
4126  Result^.ExStyle := AParams.ExStyle;
4127  Result^.WndProc := PtrUInt(AParams.WindowClass.lpfnWndProc);
4128end;
4129
4130function GetWidgetInfo(const AWidget: Pointer {; const Create: Boolean = False}): PWidgetInfo;
4131begin
4132  Result := GetWidgetInfo(AWidget, False);
4133end;
4134
4135function GetWidgetInfo(const AWidget: Pointer;
4136  const ACreate: Boolean): PWidgetInfo;
4137var
4138  MainWidget: PGtkObject;
4139begin
4140  if AWidget <> nil then
4141  begin
4142    MainWidget := GetMainWidget(AWidget);
4143    Result := gtk_object_get_data(MainWidget, 'widgetinfo');
4144    if (Result = nil) and ACreate then
4145    begin
4146      Result := CreateWidgetInfo(MainWidget);
4147      // use the main widget as default
4148      Result^.CoreWidget := PGtkWidget(MainWidget);
4149    end;
4150  end
4151  else Result := nil;
4152end;
4153
4154procedure FreeWidgetInfo(AWidget: Pointer);
4155var
4156  Info: PWidgetInfo;
4157begin
4158  if AWidget = nil then Exit;
4159  //DebugLn(['FreeWidgetInfo ',GetWidgetDebugReport(AWidget)]);
4160  Info := gtk_object_get_data(AWidget, 'widgetinfo');
4161  if Info = nil then Exit;
4162
4163  if Info^.DoubleBuffer <> nil then
4164    gdk_pixmap_unref(Info^.DoubleBuffer);
4165
4166  if (Info^.UserData <> nil) and (Info^.DataOwner) then begin
4167    FreeMem(Info^.UserData);
4168    //Info^.UserData := nil; // see below the whole memory is cleared by Fillchar
4169  end;
4170  gtk_object_set_data(AWidget,'widgetinfo',nil);
4171
4172  // Set WidgetInfo memory to nil. This will expose bugs that use widgetinfo after
4173  // it has been freed and is still referenced by something!
4174  FillChar(Info^, SizeOf(TWidgetInfo), 0);
4175
4176  Dispose(Info);
4177  //DebugLn(['FreeWidgetInfo END']);
4178end;
4179
4180{-------------------------------------------------------------------------------
4181  procedure DestroyWidget(Widget: PGtkWidget);
4182
4183  - sends LM_DESTROY
4184  - frees the WidgetInfo
4185  - destroys the widget in the gtk
4186
4187  IMPORTANT:
4188    The above order must be kept, to avoid callbacks working with dangling
4189    pointers.
4190
4191  Some widgets have a LM_DESTROY set, so if the gtk or some other code
4192  destroys those widget, the above is done in gtkdestroyCB.
4193-------------------------------------------------------------------------------}
4194procedure DestroyWidget(Widget: PGtkWidget);
4195var
4196  Info: PWidgetInfo;
4197  AWinControl: TWinControl;
4198  Mess: TLMessage;
4199begin
4200  //DebugLn(['DestroyWidget A ',GetWidgetDebugReport(Widget)]);
4201  {$IFDEF DebugLCLComponents}
4202  if DebugGtkWidgets.FindInfo(Widget)=nil then
4203    DebugLn(['DestroyWidget ',GetWidgetDebugReport(Widget)]);
4204  {$ENDIF}
4205  Info:=GetWidgetInfo(Widget);
4206  if Info<>nil then begin
4207    if (Info^.LCLObject is TWinControl) then begin
4208      AWinControl:=TWinControl(Info^.LCLObject);
4209      if AWinControl.HandleAllocated
4210      and (PGtkWidget(AWinControl.Handle)=Widget) then begin
4211        // send the LM_DESTROY message before destroying the widget
4212        FillChar(Mess,SizeOf(Mess),0);
4213        Mess.msg := LM_DESTROY;
4214        DeliverMessage(Info^.LCLObject, Mess);
4215      end;
4216    end;
4217    FreeWidgetInfo(Widget);
4218  end;
4219  {$IFDEF DebugLCLComponents}
4220  DebugGtkWidgets.MarkDestroyed(Widget);
4221  {$ENDIF}
4222  gtk_widget_destroy(Widget);
4223  //DebugLn(['DestroyWidget B']);
4224end;
4225
4226{-------------------------------------------------------------------------------
4227  function GetGtkNoteBookDummyPage(ANoteBookWidget: PGtkNoteBook): PGtkWidget;
4228
4229  Retrieves the DummyWidget associated with the ANoteBookWidget
4230-------------------------------------------------------------------------------}
4231function GetGtkNoteBookDummyPage(ANoteBookWidget: PGtkNoteBook): PGtkWidget;
4232begin
4233  Result:=gtk_object_get_data(PGtkObject(ANoteBookWidget),'LCLDummyPage');
4234end;
4235
4236{-------------------------------------------------------------------------------
4237  procedure SetGtkNoteBookDummyPage(ANoteBookWidget: PGtkNoteBook;
4238    DummyWidget: PGtkWidget): PGtkWidget;
4239
4240  Associates the DummyWidget with the ANoteBookWidget
4241-------------------------------------------------------------------------------}
4242procedure SetGtkNoteBookDummyPage(ANoteBookWidget: PGtkNoteBook;
4243  DummyWidget: PGtkWidget);
4244begin
4245  gtk_object_set_data(PGtkObject(ANoteBookWidget),'LCLDummyPage',DummyWidget);
4246end;
4247
4248{------------------------------------------------------------------------------
4249  UpdateNoteBookClientWidget
4250  Params: ANoteBook: TObject
4251
4252  This procedure updates the 'Fixed' object data.
4253  * obsolete *
4254------------------------------------------------------------------------------}
4255procedure UpdateNoteBookClientWidget(ANoteBook: TObject);
4256var
4257  ClientWidget: PGtkWidget;
4258  NoteBookWidget: PGtkNotebook;
4259begin
4260  if not TCustomTabControl(ANoteBook).HandleAllocated then exit;
4261  NoteBookWidget := PGtkNotebook(TCustomTabControl(ANoteBook).Handle);
4262  ClientWidget := nil;
4263  SetFixedWidget(NoteBookWidget, ClientWidget);
4264end;
4265
4266{-------------------------------------------------------------------------------
4267  function GetGtkNoteBookPageCount(ANoteBookWidget: PGtkNoteBook): integer;
4268
4269  Returns the number of pages in a PGtkNotebook
4270-------------------------------------------------------------------------------}
4271function GetGtkNoteBookPageCount(ANoteBookWidget: PGtkNoteBook): integer;
4272var
4273  AListItem: PGList;
4274begin
4275  Result:=0;
4276  if ANoteBookWidget=nil then exit;
4277  AListItem:=ANoteBookWidget^.children;
4278  while AListItem<>nil do begin
4279    inc(Result);
4280    AListItem:=AListItem^.Next;
4281  end;
4282end;
4283
4284{$IFDef GTK1}
4285var
4286  NoteBookCloseBtnPixmapImg: PGdkPixmap = nil;
4287  NoteBookCloseBtnPixmapMask: PGdkPixmap = nil;
4288{$EndIf}
4289
4290{-------------------------------------------------------------------------------
4291  procedure RemoveDummyNoteBookPage(NoteBookWidget: PGtkNotebook);
4292
4293  Removes the dummy page.
4294  See also AddDummyNoteBookPage
4295-------------------------------------------------------------------------------}
4296procedure RemoveDummyNoteBookPage(NoteBookWidget: PGtkNotebook);
4297var
4298  DummyWidget: PGtkWidget;
4299begin
4300  DummyWidget:=GetGtkNoteBookDummyPage(NoteBookWidget);
4301  if DummyWidget=nil then exit;
4302  gtk_notebook_remove_page(NoteBookWidget,
4303                           gtk_notebook_page_num(NoteBookWidget,DummyWidget));
4304  DummyWidget:=nil;
4305  SetGtkNoteBookDummyPage(NoteBookWidget,DummyWidget);
4306end;
4307
4308{-------------------------------------------------------------------------------
4309  method GetNoteBookCloseBtnImage
4310  Params:
4311  Result: none
4312
4313  Loads the image for the close button in the tabs of the TCustomTabControl(s).
4314-------------------------------------------------------------------------------}
4315{$IfDef GTK1}
4316procedure GetNoteBookCloseBtnImage(Window: PGdkWindow;
4317  var Img, Mask: PGdkPixmap);
4318begin
4319  if (NoteBookCloseBtnPixmapImg=nil)
4320  and (Window<>nil) then begin
4321    LoadXPMFromLazResource('tnotebook_close_tab',Window,
4322                        NoteBookCloseBtnPixmapImg,NoteBookCloseBtnPixmapMask);
4323  end;
4324  Img:=NoteBookCloseBtnPixmapImg;
4325  Mask:=NoteBookCloseBtnPixmapMask;
4326end;
4327{$EndIF}
4328
4329{-------------------------------------------------------------------------------
4330  method UpdateNotebookPageTab
4331  Params: ANoteBook: TCustomTabControl; APage: TCustomPage
4332  Result: none
4333
4334  Updates the tab of a page of a notebook. This contains the image to the left
4335  side, the label, the close button, the menu image and the menu label.
4336-------------------------------------------------------------------------------}
4337procedure UpdateNotebookPageTab(ANoteBook, APage: TObject);
4338var
4339  TheNoteBook: TCustomTabControl;
4340  ThePage: TCustomPage;
4341
4342  NoteBookWidget: PGtkWidget;  // the notebook
4343  PageWidget: PGtkWidget;      // the page (content widget)
4344  TabWidget: PGtkWidget;       // the tab (hbox containing a pixmap, a label
4345                               //          and a close button)
4346  TabImageWidget: PGtkWidget;  // the icon widget in the tab (a fixed widget)
4347  TabLabelWidget: PGtkWidget;  // the label in the tab
4348  TabCloseBtnWidget: PGtkWidget;// the close button in the tab
4349  TabCloseBtnImageWidget: PGtkWidget; // the pixmap in the close button
4350  MenuWidget: PGtkWidget;      // the popup menu (hbox containing a pixmap and
4351                               // a label)
4352  MenuImageWidget: PGtkWidget; // the icon widget in the popup menu item (a fixed widget)
4353  MenuLabelWidget: PGtkWidget; // the label in the popup menu item
4354
4355  procedure UpdateTabImage;
4356  var
4357    HasIcon: Boolean;
4358    IconSize: TPoint;
4359    ImageIndex: Integer;
4360  begin
4361    HasIcon:=false;
4362    IconSize:=Point(0,0);
4363    ImageIndex := TheNoteBook.GetImageIndex(ThePage.PageIndex);
4364    if (TheNoteBook.Images<>nil)
4365    and (ImageIndex >= 0)
4366    and (ImageIndex < TheNoteBook.Images.Count) then
4367    begin
4368      // page has valid image
4369      IconSize := Point(TheNoteBook.Images.Width, TheNoteBook.Images.Height);
4370      HasIcon := (IconSize.X>0) and (IconSize.Y>0);
4371    end;
4372
4373    if HasIcon then
4374    begin
4375      // page has an image
4376      if TabImageWidget <> nil then
4377      begin
4378        // there is already an icon widget for the image in the tab
4379        // -> resize the icon widget
4380        gtk_widget_set_usize(TabImageWidget,IconSize.X,IconSize.Y);
4381      end else
4382      begin
4383        // there is no pixmap for the image in the tab
4384        // -> insert one ot the left side of the label
4385        TabImageWidget := gtk_label_new(#0);
4386        g_signal_connect(PgtkObject(TabImageWidget), 'expose_event',
4387                           TGTKSignalFunc(@PageIconWidgetExposeAfter), ThePage);
4388        {$IFNDEF GTK2}
4389        g_signal_connect(PgtkObject(TabImageWidget), 'draw',
4390                             TGTKSignalFunc(@PageIconWidgetDrawAfter), ThePage);
4391        {$ENDIF}
4392        gtk_object_set_data(PGtkObject(TabWidget), 'TabImage', TabImageWidget);
4393        gtk_widget_set_usize(TabImageWidget, IconSize.X, IconSize.Y);
4394        gtk_widget_show(TabImageWidget);
4395        gtk_box_pack_start_defaults(PGtkBox(TabWidget), TabImageWidget);
4396        gtk_box_reorder_child(PGtkBox(TabWidget), TabImageWidget, 0);
4397      end;
4398      if MenuImageWidget<>nil then
4399      begin
4400        // there is already an icon widget for the image in the menu
4401        // -> resize the icon widget
4402        gtk_widget_set_usize(MenuImageWidget, IconSize.X, IconSize.Y);
4403      end else
4404      begin
4405        // there is no icon widget for the image in the menu
4406        // -> insert one at the left side of the label
4407        MenuImageWidget:=gtk_label_new(#0);
4408        g_signal_connect_after(PgtkObject(MenuImageWidget), 'expose_event',
4409                          TGTKSignalFunc(@PageIconWidgetExposeAfter), ThePage);
4410        {$IFNDEF GTK2}
4411        g_signal_connect_after(PgtkObject(MenuImageWidget), 'draw',
4412                             TGTKSignalFunc(@PageIconWidgetDrawAfter), ThePage);
4413        {$ENDIF}
4414        gtk_widget_set_usize(MenuImageWidget,IconSize.X,IconSize.Y);
4415        gtk_object_set_data(PGtkObject(MenuWidget),'TabImage',MenuImageWidget);
4416        gtk_widget_show(MenuImageWidget);
4417        gtk_box_pack_start_defaults(PGtkBox(MenuWidget),MenuImageWidget);
4418        gtk_box_reorder_child(PGtkBox(MenuWidget),MenuImageWidget,0);
4419      end;
4420    end else
4421    begin
4422      // page does not have an image
4423      if TabImageWidget<>nil then
4424      begin
4425        // there is a pixmap for an old image in the tab
4426        // -> remove the icon widget
4427        DestroyWidget(TabImageWidget);
4428        gtk_object_set_data(PGtkObject(TabWidget), 'TabImage', nil);
4429        TabImageWidget:=nil;
4430      end;
4431      if MenuImageWidget<>nil then
4432      begin
4433        // there is a pixmap for an old image in the menu
4434        // -> remove the icon widget
4435        DestroyWidget(MenuImageWidget);
4436        gtk_object_set_data(PGtkObject(MenuWidget), 'TabImage', nil);
4437        MenuImageWidget:=nil;
4438      end;
4439    end;
4440  end;
4441
4442  procedure UpdateTabLabel;
4443  var
4444    ACaption: String;
4445  begin
4446    ACaption := ThePage.Caption;
4447    GTKWidgetSet.SetLabelCaption(PGtkLabel(TabLabelWidget), ACaption);
4448
4449    if MenuLabelWidget <> nil then
4450      GTKWidgetSet.SetLabelCaption(PGtkLabel(MenuLabelWidget), ACaption);
4451  end;
4452
4453  procedure UpdateTabCloseBtn;
4454  var
4455    {$IfDef GTK1}
4456    Img: PGdkPixmap;
4457    Mask: PGdkBitmap;
4458    {$Else}
4459    style: PGtkRcStyle;
4460    {$EndIf}
4461  begin
4462    {$IfDef GTK1}
4463    //debugln('UpdateTabCloseBtn ',GetWidgetDebugReport(NoteBookWidget));
4464    Img:=nil;
4465    Mask:=nil;
4466    GetNoteBookCloseBtnImage(GetControlWindow(NoteBookWidget), Img, Mask);
4467    {$EndIf}
4468    //debugln('UpdateTabCloseBtn ',dbgs(nboShowCloseButtons in TheNotebook.Options),' ',dbgs(Img<>nil));
4469    if (nboShowCloseButtons in TheNotebook.Options)
4470       {$ifdef GTK1}and (Img <> nil){$ENDIF} then
4471    begin
4472      // close buttons enabled
4473      if TabCloseBtnWidget = nil then
4474      begin
4475        // there is no close button yet
4476        // -> add one to the right side of the label in the tab
4477        TabCloseBtnWidget := gtk_button_new;
4478        gtk_button_set_relief(PGtkButton(TabCloseBtnWidget), GTK_RELIEF_NONE);
4479        {$ifdef gtk2}
4480        gtk_button_set_focus_on_click(PGtkButton(TabCloseBtnWidget), False);
4481        style := gtk_widget_get_modifier_style(TabCloseBtnWidget);
4482        style^.xthickness := 0;
4483        style^.ythickness := 0;
4484        gtk_widget_modify_style(TabCloseBtnWidget, style);
4485        {$endif}
4486        gtk_object_set_data(PGtkObject(TabWidget), 'TabCloseBtn',
4487                            TabCloseBtnWidget);
4488        // put a pixmap into the button
4489       {$IfDef GTK1}
4490        TabCloseBtnImageWidget:=gtk_pixmap_new(Img,Mask);
4491       {$Else}
4492        TabCloseBtnImageWidget:=gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
4493       {$EndIf}
4494        gtk_object_set_data(PGtkObject(TabCloseBtnWidget),'TabCloseBtnImage',
4495                            TabCloseBtnImageWidget);
4496        gtk_widget_show(TabCloseBtnImageWidget);
4497        gtk_container_add(PGtkContainer(TabCloseBtnWidget),
4498                          TabCloseBtnImageWidget);
4499        gtk_widget_show(TabCloseBtnWidget);
4500        g_signal_connect(PGtkObject(TabCloseBtnWidget), 'clicked',
4501          TGTKSignalFunc(@gtkNoteBookCloseBtnClicked), APage);
4502        gtk_box_pack_start(PGtkBox(TabWidget), TabCloseBtnWidget, False, False, 0);
4503      end;
4504    end else begin
4505      // close buttons disabled
4506      if TabCloseBtnWidget<>nil then begin
4507        // there is a close button
4508        // -> remove it
4509        gtk_object_set_data(PGtkObject(TabWidget), 'TabCloseBtn',
4510                            nil);
4511        DestroyWidget(TabCloseBtnWidget);
4512        TabCloseBtnWidget:=nil;
4513      end;
4514    end;
4515  end;
4516
4517begin
4518  ThePage := TCustomPage(APage);
4519  TheNoteBook := TCustomTabControl(ANoteBook);
4520  if (APage=nil) or (not ThePage.HandleAllocated) then exit;
4521  if TheNoteBook=nil then begin
4522    TheNoteBook:=TCustomTabControl(ThePage.Parent);
4523    if TheNoteBook=nil then exit;
4524  end;
4525  NoteBookWidget:=PGtkWidget(TWinControl(TheNoteBook).Handle);
4526  PageWidget:=PGtkWidget(TWinControl(ThePage).Handle);
4527
4528  // get the tab container and the tab components: pixmap, label and closebtn
4529  TabWidget:=gtk_notebook_get_tab_label(PGtkNoteBook(NotebookWidget),
4530                                        PageWidget);
4531  if TabWidget<>nil then begin
4532    TabImageWidget:=gtk_object_get_data(PGtkObject(TabWidget), 'TabImage');
4533    TabLabelWidget:=gtk_object_get_data(PGtkObject(TabWidget), 'TabLabel');
4534    TabCloseBtnWidget:=gtk_object_get_data(PGtkObject(TabWidget),'TabCloseBtn');
4535  end else begin
4536    TabImageWidget:=nil;
4537    TabLabelWidget:=nil;
4538    TabCloseBtnWidget:=nil;
4539  end;
4540
4541  // get the menu container and its components: pixmap and label
4542  MenuWidget:=gtk_notebook_get_menu_label(PGtkNoteBook(NotebookWidget),
4543                                          PageWidget);
4544  if MenuWidget<>nil then begin
4545    MenuImageWidget:=gtk_object_get_data(PGtkObject(MenuWidget), 'TabImage');
4546    MenuLabelWidget:=gtk_object_get_data(PGtkObject(MenuWidget), 'TabLabel');
4547  end else begin
4548    MenuImageWidget:=nil;
4549    MenuLabelWidget:=nil;
4550  end;
4551
4552  UpdateTabImage;
4553  UpdateTabLabel;
4554  UpdateTabCloseBtn;
4555end;
4556
4557
4558{-------------------------------------------------------------------------------
4559  GetWidgetScreenPos
4560
4561  Returns the absolute left top position of a widget on the screen.
4562-------------------------------------------------------------------------------}
4563function GetWidgetOrigin(TheWidget: PGtkWidget): TPoint;
4564var
4565  TheWindow: PGdkWindow;
4566  {$IFDEF RaiseExceptionOnNilPointers}
4567  LCLObject: TObject;
4568  {$ENDIF}
4569begin
4570  TheWindow:=GetControlWindow(TheWidget);
4571  if TheWindow<>nil then begin
4572    BeginGDKErrorTrap;
4573    gdk_window_get_origin(TheWindow,@Result.X,@Result.Y);
4574    EndGDKErrorTrap;
4575  end else begin
4576    {$IFDEF RaiseExceptionOnNilPointers}
4577    LCLobject:=GetLCLObject(TheWidget);
4578    DbgOut('GetWidgetOrigin ');
4579    if LCLObject=nil then
4580      DbgOut(' LCLObject=nil')
4581    else if LCLObject is TControl then
4582      DbgOut(' LCLObject=',TControl(LCLObject).Name,':',TControl(LCLObject).ClassName)
4583    else
4584      DbgOut(' LCLObject=',TControl(LCLObject).ClassName);
4585    DebugLn('');
4586    RaiseException('GetWidgetOrigin Window=nil');
4587    {$ENDIF}
4588    Result.X:=0;
4589    Result.Y:=0;
4590  end;
4591  // check if the gdkwindow is the clientwindow of the parent
4592  if gtk_widget_get_parent_window(TheWidget)=TheWindow then begin
4593    // the widget is using its parent window
4594    // -> adjust the coordinates
4595    inc(Result.X,TheWidget^.Allocation.X);
4596    inc(Result.Y,TheWidget^.Allocation.Y);
4597  end;
4598end;
4599
4600{-------------------------------------------------------------------------------
4601  GetWidgetClientScreenPos
4602
4603  Returns the absolute left top position of a widget's client area
4604  on the screen.
4605-------------------------------------------------------------------------------}
4606function GetWidgetClientOrigin(TheWidget: PGtkWidget): TPoint;
4607
4608  {$IFDEF Gtk2}
4609  procedure GetNoteBookClientOrigin(NBWidget: PGtkNotebook);
4610  var
4611    PageIndex: LongInt;
4612    PageWidget: PGtkWidget;
4613    ClientWidget: PGTKWidget;
4614    FrameBorders: TRect;
4615  begin
4616    // get current page
4617    PageIndex:=gtk_notebook_get_current_page(NBWidget);
4618    if PageIndex>=0 then
4619      PageWidget:=gtk_notebook_get_nth_page(NBWidget,PageIndex)
4620    else
4621      PageWidget:=nil;
4622
4623    // get client widget of page
4624    if (PageWidget<>nil) then
4625      ClientWidget:=GetFixedWidget(PageWidget)
4626    else
4627      ClientWidget:=nil;
4628
4629    // Be careful while using ClientWidget here, it may be nil
4630    if (ClientWidget<>nil) and (ClientWidget^.window<>nil) then
4631    begin
4632      // get the position of the current page
4633      gdk_window_get_origin(ClientWidget^.window,@Result.X,@Result.Y);
4634      if GTK_WIDGET_NO_WINDOW(ClientWidget)
4635      then begin
4636        Inc(Result.X, ClientWidget^.Allocation.X);
4637        Inc(Result.Y, ClientWidget^.Allocation.Y);
4638      end;
4639    end
4640    else
4641    begin
4642      // use defaults
4643      Result:=GetWidgetOrigin(TheWidget);
4644      FrameBorders:=GetStyleNotebookFrameBorders;
4645      GetWidgetClientOrigin.x:=Result.x+FrameBorders.Left;
4646      GetWidgetClientOrigin.y:=Result.y+FrameBorders.Top;
4647    end;
4648  end;
4649  {$ENDIF}
4650
4651var
4652  ClientWidget: PGtkWidget;
4653  ClientWindow: PGdkWindow;
4654begin
4655  ClientWidget := GetFixedWidget(TheWidget);
4656  if ClientWidget <> TheWidget then
4657  begin
4658    ClientWindow := GetControlWindow(ClientWidget);
4659    if ClientWindow <> nil then
4660    begin
4661      {$IFDEF DebugGDK}
4662      BeginGDKErrorTrap;
4663      {$ENDIF}
4664      gdk_window_get_origin(ClientWindow, @Result.X, @Result.Y);
4665      {$Ifdef GTK2}
4666      if GTK_WIDGET_NO_WINDOW(ClientWidget) then
4667      begin
4668        Inc(Result.X, ClientWidget^.Allocation.X);
4669        Inc(Result.Y, ClientWidget^.Allocation.Y);
4670      end;
4671      {$EndIf}
4672      {$IFDEF DebugGDK}
4673      EndGDKErrorTrap;
4674      {$ENDIF}
4675      exit;
4676    end;
4677  {$IFDEF Gtk2}
4678  end
4679  else
4680  if GtkWidgetIsA(TheWidget,GTK_TYPE_NOTEBOOK) then
4681  begin
4682    GetNoteBookClientOrigin(PGtkNoteBook(TheWidget));
4683    Exit;
4684  {$ENDIF}
4685  end;
4686  Result := GetWidgetOrigin(TheWidget);
4687end;
4688
4689{-------------------------------------------------------------------------------
4690  TranslateGdkPointToClientArea
4691
4692  Translates SourcePos relative to SourceWindow to a coordinate relative to the
4693  client area of the LCL WinControl.
4694-------------------------------------------------------------------------------}
4695function TranslateGdkPointToClientArea(SourceWindow: PGdkWindow;
4696  SourcePos: TPoint; DestinationWidget: PGtkWidget): TPoint;
4697var
4698  SrcWindowOrigin: TPoint;
4699  ClientAreaWindowOrigin: TPoint;
4700  Src2ClientAreaVector: TPoint;
4701begin
4702  if SourceWindow = nil then
4703  begin
4704    {$IFDEF RaiseExceptionOnNilPointers}
4705    RaiseException('TranslateGdkPointToClientArea Window=nil');
4706    {$ENDIF}
4707    DebugLn('WARNING: TranslateGdkPointToClientArea SourceWindow=nil');
4708  end;
4709  gdk_window_get_origin(SourceWindow, @SrcWindowOrigin.X, @SrcWindowOrigin.Y);
4710
4711  ClientAreaWindowOrigin := GetWidgetClientOrigin(DestinationWidget);
4712  Src2ClientAreaVector.X := ClientAreaWindowOrigin.X - SrcWindowOrigin.X;
4713  Src2ClientAreaVector.Y := ClientAreaWindowOrigin.Y - SrcWindowOrigin.Y;
4714  Result.X := SourcePos.X - Src2ClientAreaVector.X;
4715  Result.Y := SourcePos.Y - Src2ClientAreaVector.Y;
4716end;
4717
4718function SubtractScoll(AWidget: PGtkWidget; APosition: TPoint): TPoint;
4719begin
4720  Result := APosition;
4721  AWidget := gtk_object_get_data(PGTKObject(AWidget), odnScrollArea);
4722  if GTK_IS_SCROLLED_WINDOW(AWidget) then
4723  begin
4724    with gtk_scrolled_window_get_hadjustment(PGtkScrolledWindow(AWidget))^ do
4725      dec(Result.x, Trunc(value - lower));
4726    with gtk_scrolled_window_get_vadjustment(PGtkScrolledWindow(AWidget))^ do
4727      dec(Result.y, Trunc(value - lower));
4728  end;
4729end;
4730
4731{------------------------------------------------------------------------------
4732  Function: UpdateMouseCaptureControl
4733  Params: none
4734  Returns:  none
4735
4736  Sets MouseCaptureWidget to the current capturing widget.
4737 ------------------------------------------------------------------------------}
4738procedure UpdateMouseCaptureControl;
4739var
4740  OldMouseCaptureWidget,
4741  CurMouseCaptureWidget: PGtkWidget;
4742begin
4743  OldMouseCaptureWidget:=MouseCaptureWidget;
4744  CurMouseCaptureWidget:=gtk_grab_get_current;
4745
4746  if OldMouseCaptureWidget<>CurMouseCaptureWidget then begin
4747    // the mouse grab changed
4748    // -> this means the gtk itself has changed the mouse grab
4749    {$IFDEF VerboseMouseCapture}
4750    DebugLn('UpdateMouseCaptureControl Capture changed from ',
4751      '[',GetWidgetDebugReport(OldMouseCaptureWidget),' type=',MouseCaptureTypeNames[MouseCaptureType],']',
4752      ' to [',GetWidgetDebugReport(CurMouseCaptureWidget),' type=GTK]');
4753    if CurMouseCaptureWidget<>nil then
4754    DebugLn('parent ',    GetWidgetDebugReport(CurMouseCaptureWidget^.Parent));
4755    {$ENDIF}
4756
4757    // notify the new capture control
4758    MouseCaptureWidget:=CurMouseCaptureWidget;
4759    MouseCaptureType:=mctGTK;
4760    if MouseCaptureWidget<>nil then begin
4761      // the MouseCaptureWidget is probably not a main widget
4762      SendMessage(HWnd(PtrUInt(MouseCaptureWidget)), LM_CAPTURECHANGED, 0,
4763        HWnd(PtrUInt(OldMouseCaptureWidget)));
4764    end;
4765  end;
4766end;
4767
4768procedure IncreaseMouseCaptureIndex;
4769begin
4770  if MouseCaptureIndex<$ffffffff then
4771    inc(MouseCaptureIndex)
4772  else
4773    MouseCaptureIndex:=0;
4774end;
4775
4776procedure CaptureMouseForWidget(Widget: PGtkWidget; Owner: TMouseCaptureType);
4777var
4778  CaptureWidget: PGtkWidget;
4779  NowIndex: Cardinal;
4780begin
4781  {$IFDEF VerboseMouseCapture}
4782  DebugLn('CaptureMouseForWidget START ',GetWidgetDebugReport(Widget));
4783  {$ENDIF}
4784  if not (Owner in [mctGTKIntf,mctLCL]) then exit;
4785  // not every widget can capture the mouse
4786  CaptureWidget:=GetDefaultMouseCaptureWidget(Widget);
4787  if CaptureWidget=nil then exit;
4788
4789  UpdateMouseCaptureControl;
4790  if (MouseCaptureType<>mctGTK) then begin
4791    // we are capturing
4792    if (MouseCaptureWidget=CaptureWidget) then begin
4793      // we are already capturing this widget
4794      exit;
4795    end;
4796    // release old capture
4797    ReleaseMouseCapture;
4798  end;
4799
4800  {$IFDEF VerboseMouseCapture}
4801  DebugLn('CaptureMouseForWidget Start Capturing for ',GetWidgetDebugReport(CaptureWidget));
4802  {$ENDIF}
4803  IncreaseMouseCaptureIndex;
4804  NowIndex:=MouseCaptureIndex;
4805  if not gtk_widget_has_focus(CaptureWidget) then
4806    gtk_widget_grab_focus(CaptureWidget);
4807  if NowIndex=MouseCaptureIndex then begin
4808    {$IFDEF VerboseMouseCapture}
4809    DebugLn('CaptureMouseForWidget Commit Capturing for ',GetWidgetDebugReport(CaptureWidget));
4810    {$ENDIF}
4811    MouseCaptureWidget:=CaptureWidget;
4812    MouseCaptureType:=Owner;
4813    gtk_grab_add(CaptureWidget);
4814  end;
4815end;
4816
4817function GetDefaultMouseCaptureWidget(Widget: PGtkWidget
4818  ): PGtkWidget;
4819var
4820  WidgetInfo: PWinWidgetInfo;
4821  LCLObject: TObject;
4822begin
4823  Result:=nil;
4824  if Widget=nil then exit;
4825  if GtkWidgetIsA(Widget,GTKAPIWidget_Type) then begin
4826    WidgetInfo:=GetWidgetInfo(Widget,false);
4827    if WidgetInfo<>nil then
4828      Result:=WidgetInfo^.CoreWidget;
4829    exit;
4830  end;
4831  LCLObject:=GetNearestLCLObject(Widget);
4832  if LCLObject=nil then exit;
4833  if (TWinControl(LCLObject) is TCustomSplitter) and (TWinControl(LCLObject).HandleAllocated)
4834  then begin
4835    WidgetInfo:=GetWidgetInfo(PGtkWidget(TWinControl(LCLObject).Handle),false);
4836    if WidgetInfo<>nil then
4837      Result:=WidgetInfo^.CoreWidget;
4838  end;
4839end;
4840
4841{------------------------------------------------------------------------------
4842  procedure ReleaseMouseCapture;
4843
4844  If the current mouse capture was captured by the LCL or the gtk intf, release
4845  the capture. Don't release mouse captures of the gtk, because captures must
4846  be balanced and this is already done by the gtk.
4847 ------------------------------------------------------------------------------}
4848procedure ReleaseMouseCapture;
4849var
4850  OldMouseCaptureWidget: PGtkWidget;
4851  Info: PWidgetInfo;
4852begin
4853  {$IFDEF VerboseMouseCapture}
4854  DebugLn('ReleaseMouseCapture ',dbgs(ord(MouseCaptureType)),' MouseCaptureWidget=[',GetWidgetDebugReport(MouseCaptureWidget),']');
4855  {$ENDIF}
4856  if MouseCaptureType=mctGTK then
4857  begin
4858    Info := GetWidgetInfo(gtk_grab_get_current, false);
4859    if (Info <> nil) and (Info^.CoreWidget <> nil) then
4860    begin
4861      if GtkWidgetIsA(Info^.CoreWidget, gtk_list_get_type) then
4862      begin
4863        // Paul Ishenin:
4864        // listbox grabs pointer and other control for itself, when we click on listbox item
4865        // also it changes its state to drag_selection
4866        // this is not expected in LCL and as result cause bugs, such as 7892
4867        // so we need end drag selection manually
4868        OldMouseCaptureWidget := Info^.CoreWidget;
4869        gtk_list_end_drag_selection(PGtkList(OldMouseCaptureWidget));
4870      end;
4871    end;
4872    exit;
4873  end;
4874  OldMouseCaptureWidget:=MouseCaptureWidget;
4875  MouseCaptureWidget:=nil;
4876  MouseCaptureType:=mctGTK;
4877  if OldMouseCaptureWidget<>nil then
4878    gtk_grab_remove(OldMouseCaptureWidget);
4879  // tell the LCL
4880  SetCaptureControl(nil);
4881end;
4882
4883procedure ReleaseCaptureWidget(Widget : PGtkWidget);
4884begin
4885  if (Widget=nil)
4886  or ((MouseCaptureWidget<>Widget) and (MouseCaptureWidget<>Widget^.parent))
4887  then
4888    exit;
4889  DebugLn('ReleaseCaptureWidget ',GetWidgetDebugReport(Widget));
4890  ReleaseMouseCapture;
4891end;
4892
4893{-------------------------------------------------------------------------------
4894  procedure: SignalConnect
4895  Params:  AWidget: PGTKWidget
4896           ASignal: PChar
4897           AProc:   Pointer
4898           AInfo:   PWidgetInfo
4899  Returns: Nothing
4900
4901  Connects a gtk signal handler.
4902  This is a wrapper to get around gtk casting
4903-------------------------------------------------------------------------------}
4904procedure SignalConnect(const AWidget: PGTKWidget; const ASignal: PChar;
4905  const AProc: Pointer; const AInfo: PWidgetInfo);
4906begin
4907  g_signal_connect(PGtkObject(AWidget), ASignal, TGTKSignalFunc(AProc), AInfo);
4908end;
4909
4910{-------------------------------------------------------------------------------
4911  procedure: SignalConnectAfter
4912  Params:  AWidget: PGTKWidget
4913           ASignal: PChar
4914           AProc:   Pointer
4915           AInfo:   PGtkWSWidgetInfo
4916  Returns: Nothing
4917
4918  Connects a gtk signal after handler.
4919  This is a wrapper to get around gtk casting
4920-------------------------------------------------------------------------------}
4921procedure SignalConnectAfter(const AWidget:PGTKWidget; const ASignal: PChar;
4922  const AProc: Pointer; const AInfo: PWidgetInfo);
4923begin
4924  g_signal_connect_after(PGTKObject(AWidget), ASignal, TGTKSignalFunc(AProc), AInfo);
4925end;
4926
4927{-------------------------------------------------------------------------------
4928  procedure ConnectSignal(const AnObject:PGTKObject; const ASignal: PChar;
4929    const ACallBackProc: Pointer; const ReqSignalMask: TGdkEventMask;
4930    Flags: TConnectSignalFlags);
4931
4932  Connects a gtk signal handler.
4933-------------------------------------------------------------------------------}
4934procedure InitDesignSignalMasks;
4935var
4936  SignalType: TDesignSignalType;
4937begin
4938  DesignSignalMasks[dstUnknown]:=0;
4939  for SignalType:=Low(TDesignSignalType) to High(TDesignSignalType) do
4940    DesignSignalMasks[SignalType]:=1 shl ord(SignalType);
4941end;
4942
4943function DesignSignalNameToType(Name: PChar; After: boolean): TDesignSignalType;
4944begin
4945  for Result:=Low(TDesignSignalType) to High(TDesignSignalType) do
4946    if SamePChar(DesignSignalNames[Result],Name)
4947    and (DesignSignalAfter[Result]=After) then exit;
4948  Result:=dstUnknown;
4949end;
4950
4951function GetDesignSignalMask(Widget: PGtkWidget): TDesignSignalMask;
4952begin
4953  Result:=TDesignSignalMask(PtrUInt(gtk_object_get_data(PGtkObject(Widget),
4954                                                'LCLDesignMask')));
4955end;
4956
4957procedure SetDesignSignalMask(Widget: PGtkWidget; NewMask: TDesignSignalMask);
4958begin
4959  gtk_object_set_data(PGtkObject(Widget),'LCLDesignMask',Pointer(PtrInt(NewMask)));
4960end;
4961
4962function GetDesignOnlySignalFlag(Widget: PGtkWidget;
4963  DesignSignalType: TDesignSignalType): boolean;
4964begin
4965  Result:=(GetDesignSignalMask(Widget)
4966          and DesignSignalMasks[DesignSignalType])<>0;
4967end;
4968
4969function SignalConnected(const AnObject:PGTKObject; const ASignal: PChar;
4970  const ACallBackProc: Pointer; const ALCLObject: TObject;
4971  const ASFlags: TConnectSignalFlags): boolean;
4972{$IFDEF Gtk1}
4973var
4974  Handler: PGTKHandler;
4975  SignalID: guint;
4976begin
4977  Handler := gtk_object_get_data_by_id (AnObject, gtk_handler_quark);
4978  SignalID := g_signal_lookup(ASignal, GTK_OBJECT_TYPE(AnObject));
4979  if SignalID>$ffffff then
4980    RaiseGDBException('SignalConnected');
4981
4982  while (Handler <> nil) do begin
4983    with Handler^ do
4984    begin
4985      // check if signal is already connected
4986      //debugln('ConnectSignal Id=',dbgs(Id));
4987      if  (Id > 0)
4988      and (Signal_ID = SignalID)
4989      and (Func = TGTKSignalFunc(ACallBackProc))
4990      and (func_data = Pointer(ALCLObject))
4991      and (((flags and bmSignalAfter)<>0)=(csfAfter in ASFlags))
4992      then begin
4993        // signal is already connected
4994        Result:=true;
4995        Exit;
4996      end;
4997
4998      Handler := Next;
4999    end;
5000  end;
5001  Result:=false;
5002end;
5003{$ELSE}
5004begin
5005  Result:=g_signal_handler_find(AnObject,
5006    G_SIGNAL_MATCH_FUNC or G_SIGNAL_MATCH_DATA,
5007    0,0,nil,ACallBackProc,ALCLObject)<>0;
5008end;
5009{$ENDIF}
5010
5011procedure ConnectSignal(const AnObject:PGTKObject; const ASignal: PChar;
5012  const ACallBackProc: Pointer; const ALCLObject: TObject;
5013  const AReqSignalMask: TGdkEventMask; const ASFlags: TConnectSignalFlags);
5014var
5015  WinWidgetInfo: PWinWidgetInfo;
5016  MainWidget: PGtkWidget;
5017  OldDesignMask, NewDesignMask: TDesignSignalMask;
5018  DesignSignalType: TDesignSignalType;
5019  RealizeConnected: Boolean;
5020  HasRealizeSignal: Boolean;
5021begin
5022  if ACallBackProc = nil then
5023    RaiseGDBException('ConnectSignal');
5024
5025  // first loop through the handlers to:
5026  // - check if a handler already exists
5027  // - Find the realize handler to change data
5028  DesignSignalType:=DesignSignalNameToType(ASignal,csfAfter in ASFlags);
5029  if SignalConnected(AnObject,ASignal,ACallBackProc,ALCLObject,ASFlags) then
5030  begin
5031    // signal is already connected
5032    // update the DesignSignalMask
5033    if (DesignSignalType <> dstUnknown)
5034    and (not (csfDesignOnly in ASFlags))
5035    then begin
5036      OldDesignMask := GetDesignSignalMask(PGtkWidget(AnObject));
5037      NewDesignMask :=OldDesignMask and not DesignSignalMasks[DesignSignalType];
5038      if OldDesignMask <> NewDesignMask
5039      then SetDesignSignalMask(PGtkWidget(AnObject),NewDesignMask);
5040    end;
5041    Exit;
5042  end;
5043
5044  // if we are here, then no handler was defined yet
5045  // -> register handler
5046  //if (Msg=LM_LBUTTONUP) then DebugLn('CONNECT ',ReqSignalMask,' Widget=',DbgS(AnObject));
5047  //debugln('ConnectSignal ',DbgSName(ALCLObject),' ',ASignal,' After=',dbgs(csfAfter in ASFlags));
5048  if csfAfter in ASFlags then
5049    g_signal_connect_after(AnObject, ASignal,
5050                           TGTKSignalFunc(ACallBackProc), ALCLObject)
5051  else
5052    g_signal_connect      (AnObject, ASignal,
5053                           TGTKSignalFunc(ACallBackProc), ALCLObject);
5054
5055  // update signal mask which will be set in the realize handler
5056  if (csfUpdateSignalMask in ASFlags) and (AReqSignalMask <> 0)
5057  then begin
5058    MainWidget := GetMainWidget(PGtkWidget(AnObject));
5059    if MainWidget=nil
5060    then MainWidget := PGtkWidget(AnObject);
5061    WinWidgetInfo := GetWidgetInfo(MainWidget,true);
5062    WinWidgetInfo^.EventMask := WinWidgetInfo^.EventMask or AReqSignalMask;
5063  end;
5064
5065  // -> register realize handler
5066  if (csfConnectRealize in ASFlags) then begin
5067    HasRealizeSignal:=g_signal_lookup('realize', GTK_OBJECT_TYPE(AnObject))>0;
5068    if HasRealizeSignal then begin
5069      RealizeConnected:=SignalConnected(AnObject,'realize',@GTKRealizeCB,
5070                                        ALCLObject,[]);
5071      if not RealizeConnected then begin
5072        g_signal_connect(AnObject, 'realize',
5073          TGTKSignalFunc(@GTKRealizeCB), ALCLObject);
5074        g_signal_connect_after(AnObject, 'realize',
5075          TGTKSignalFunc(@GTKRealizeAfterCB), ALCLObject);
5076      end;
5077    end;
5078  end;
5079
5080  // update the DesignSignalMask
5081  if (DesignSignalType <> dstUnknown)
5082  then begin
5083    OldDesignMask:=GetDesignSignalMask(PGtkWidget(AnObject));
5084    if csfDesignOnly in ASFlags then
5085      NewDesignMask:=OldDesignMask or DesignSignalMasks[DesignSignalType]
5086    else
5087      NewDesignMask:=OldDesignMask and not DesignSignalMasks[DesignSignalType];
5088    if OldDesignMask<>NewDesignMask then
5089      SetDesignSignalMask(PGtkWidget(AnObject),NewDesignMask);
5090  end;
5091end;
5092
5093procedure ConnectSignal(const AnObject:PGTKObject; const ASignal: PChar;
5094  const ACallBackProc: Pointer; const ALCLObject: TObject;
5095  const AReqSignalMask: TGdkEventMask);
5096begin
5097  ConnectSignal(AnObject,ASignal,ACallBackProc, ALCLObject, AReqSignalMask,
5098                [csfConnectRealize,csfUpdateSignalMask]);
5099end;
5100
5101procedure ConnectSignalAfter(const AnObject:PGTKObject; const ASignal: PChar;
5102  const ACallBackProc: Pointer; const ALCLObject: TObject;
5103  const AReqSignalMask: TGdkEventMask);
5104begin
5105  ConnectSignal(AnObject,ASignal,ACallBackProc, ALCLObject, AReqSignalMask,
5106                [csfConnectRealize,csfUpdateSignalMask,csfAfter]);
5107end;
5108
5109procedure ConnectSignal(const AnObject:PGTKObject; const ASignal: PChar;
5110  const ACallBackProc: Pointer; const ALCLObject: TObject);
5111begin
5112  ConnectSignal(AnObject,ASignal,ACallBackProc, ALCLObject, 0);
5113end;
5114
5115procedure ConnectSignalAfter(const AnObject:PGTKObject; const ASignal: PChar;
5116  const ACallBackProc: Pointer; const ALCLObject: TObject);
5117begin
5118  ConnectSignalAfter(AnObject,ASignal,ACallBackProc, ALCLObject, 0);
5119end;
5120
5121{------------------------------------------------------------------------------
5122  procedure: ConnectInternalWidgetsSignals
5123  Params:  AWidget: PGtkWidget; AWinControl: TWinControl
5124  Returns: Nothing
5125
5126  Connects hidden child widgets signals.
5127  Many gtk widgets create internally child widgets (e.g. scrollbars). In
5128  Design mode these widgets should not auto react themselves, but instead send
5129  messages to the lcl. Therefore these widgets are connected also to our
5130  signal handlers.
5131  This procedure is called by the realize-after handler of all LCL widgets
5132  and each time the design mode of a LCL control changes.
5133 ------------------------------------------------------------------------------}
5134procedure ConnectInternalWidgetsSignals(AWidget: PGtkWidget;
5135  AWinControl: TWinControl);
5136
5137  function WidgetIsInternal(TheWidget: PGtkWidget): boolean;
5138  begin
5139    Result:=(TheWidget<>nil)
5140      and (PGtkWidget(AWinControl.Handle)<>TheWidget)
5141      and (GetMainWidget(TheWidget)=nil);
5142  end;
5143
5144  procedure ConnectSignals(TheWidget: PGtkWidget); forward;
5145
5146  procedure ConnectChilds(TheWidget: PGtkWidget);
5147  var
5148    ScrolledWindow: PGtkScrolledWindow;
5149    BinWidget: PGtkBin;
5150    {$IFDEF Gtk2}
5151    ChildEntry2: PGList;
5152    {$ELSE}
5153    ChildEntry: PGSList;
5154    {$ENDIF}
5155    ChildWidget: PGtkWidget;
5156  begin
5157    //if AWinControl is TListView then DebugLn('ConnectChilds A ',DbgS(TheWidget));
5158    if GtkWidgetIsA(TheWidget,GTK_TYPE_CONTAINER) then begin
5159      //if AWinControl is TListView then DebugLn('ConnectChilds B ');
5160      // this is a container widget -> connect all children
5161      {$IFDEF Gtk2}
5162      ChildEntry2:=gtk_container_get_children(PGtkContainer(TheWidget));
5163      while ChildEntry2<>nil do begin
5164        ChildWidget:=PGtkWidget(ChildEntry2^.Data);
5165        if ChildWidget<>TheWidget then
5166          ConnectSignals(ChildWidget);
5167        ChildEntry2:=ChildEntry2^.Next;
5168      end;
5169      {$ELSE}
5170      ChildEntry:=PGtkContainer(TheWidget)^.resize_widgets;
5171      while ChildEntry<>nil do begin
5172        ChildWidget:=PGtkWidget(ChildEntry^.Data);
5173        ConnectSignals(ChildWidget);
5174        ChildEntry:=ChildEntry^.Next;
5175      end;
5176      {$endif}
5177    end;
5178    if GtkWidgetIsA(TheWidget,GTK_TYPE_BIN) then begin
5179      //if AWinControl is TListView then DebugLn('ConnectChilds C ');
5180      BinWidget:=PGtkBin(TheWidget);
5181      ConnectSignals(BinWidget^.child);
5182    end;
5183    if GtkWidgetIsA(TheWidget,GTK_TYPE_SCROLLED_WINDOW) then begin
5184      //if AWinControl is TListView then DebugLn('ConnectChilds D ');
5185      ScrolledWindow:=PGtkScrolledWindow(TheWidget);
5186      ConnectSignals(ScrolledWindow^.hscrollbar);
5187      ConnectSignals(ScrolledWindow^.vscrollbar);
5188    end;
5189    if GtkWidgetIsA(TheWidget,GTK_TYPE_COMBO) then begin
5190      //if AWinControl is TListView then DebugLn('ConnectChilds E ');
5191      ConnectSignals(PGtkCombo(TheWidget)^.entry);
5192      ConnectSignals(PGtkCombo(TheWidget)^.button);
5193    end;
5194  end;
5195
5196  procedure ConnectSignals(TheWidget: PGtkWidget);
5197  var
5198    LCLObject, HiddenLCLObject: TObject;
5199    DesignSignalType: TDesignSignalType;
5200    DesignFlags: TConnectSignalFlags;
5201  begin
5202    //if AWinControl is TListView then DebugLn('ConnectSignals A ',DbgS(TheWidget));
5203    if TheWidget=nil then exit;
5204
5205    // check if TheWidget belongs to another LCL object
5206    LCLObject:=GetLCLObject(TheWidget);
5207    HiddenLCLObject:=GetHiddenLCLObject(TheWidget);
5208    if (LCLObject<>nil) and (LCLObject<>AWinControl) then begin
5209      exit;
5210    end;
5211    if (HiddenLCLObject<>nil) and (HiddenLCLObject<>AWinControl) then begin
5212      exit;
5213    end;
5214
5215    //if AWinControl is TListView then DebugLn('ConnectSignals B ',DbgS(TheWidget));
5216    // connect signals needed for design mode:
5217    for DesignSignalType:=Low(TDesignSignalType) to High(TDesignSignalType) do
5218    begin
5219      if DesignSignalType=dstUnknown then continue;
5220      if (not DesignSignalBefore[DesignSignalType])
5221      and (not DesignSignalAfter[DesignSignalType]) then
5222        continue;
5223
5224      DesignFlags:=[csfDesignOnly];
5225      if DesignSignalAfter[DesignSignalType] then
5226        Include(DesignFlags,csfAfter);
5227      ConnectSignal(PGtkObject(TheWidget),DesignSignalNames[DesignSignalType],
5228                    DesignSignalFuncs[DesignSignalType],AWinControl,0,
5229                    DesignFlags);
5230    end;
5231
5232    if WidgetIsInternal(TheWidget) then
5233      // mark widget as 'hidden' connected
5234      SetHiddenLCLObject(TheWidget,AWinControl);
5235
5236    // connect recursively ...
5237    ConnectChilds(TheWidget);
5238  end;
5239
5240begin
5241  if (AWinControl=nil) or (AWidget=nil)
5242  or (not (csDesigning in AWinControl.ComponentState)) then exit;
5243  ConnectSignals(AWidget);
5244end;
5245
5246// ----------------------------------------------------------------------
5247// The Accelgroup and AccelKey is needed by menus
5248// ----------------------------------------------------------------------
5249function GetAccelGroup(const Widget: PGtkWidget;
5250  CreateIfNotExists: boolean): PGTKAccelGroup;
5251begin
5252  Result := PGTKAccelGroup(gtk_object_get_data(PGtkObject(Widget),'AccelGroup'));
5253  if (Result=nil) and CreateIfNotExists then begin
5254    {$IFDEF VerboseAccelerator}
5255    DebugLn('GetAccelGroup CREATING  Widget=',DbgS(Widget),' CreateIfNotExists=',dbgs(CreateIfNotExists));
5256    {$ENDIF}
5257    Result:=gtk_accel_group_new;
5258    SetAccelGroup(Widget,Result);
5259    if GtkWidgetIsA(Widget,GTK_TYPE_WINDOW) then
5260      ShareWindowAccelGroups(Widget);
5261  end;
5262end;
5263
5264procedure SetAccelGroup(const Widget: PGtkWidget;
5265  const AnAccelGroup: PGTKAccelGroup);
5266begin
5267  if (Widget = nil) then exit;
5268  gtk_object_set_data(PGtkObject(Widget), 'AccelGroup', AnAccelGroup);
5269  if AnAccelGroup<>nil then begin
5270    // attach group to widget
5271    {$IFDEF VerboseAccelerator}
5272    DebugLn(['SetAccelGroup AnAccelGroup=',DbgS(AnAccelGroup),' IsMenu=',GtkWidgetIsA(Widget,GTK_TYPE_MENU)]);
5273    {$ENDIF}
5274    if GtkWidgetIsA(Widget,GTK_TYPE_MENU) then
5275      gtk_menu_set_accel_group(PGtkMenu(Widget), AnAccelGroup)
5276    else begin
5277      {$IfDef GTK2}
5278      Assert(GtkWidgetIsA(Widget,GTK_TYPE_WINDOW));
5279      gtk_window_add_accel_group(GTK_WINDOW(widget), AnAccelGroup);
5280      {$else}
5281      gtk_accel_group_attach(AnAccelGroup, PGtkObject(Widget));
5282      {$endif}
5283    end;
5284  end;
5285end;
5286
5287procedure FreeAccelGroup(const Widget: PGtkWidget);
5288var
5289  AccelGroup: PGTKAccelGroup;
5290begin
5291  AccelGroup:=GetAccelGroup(Widget,false);
5292  if AccelGroup<>nil then begin
5293    {$IFDEF VerboseAccelerator}
5294    DebugLn('FreeAccelGroup  AccelGroup=',DbgS(AccelGroup));
5295    {$ENDIF}
5296    gtk_accel_group_unref(AccelGroup);
5297    SetAccelGroup(Widget,nil);
5298  end;
5299end;
5300
5301procedure ShareWindowAccelGroups(AWindow: PGtkWidget);
5302
5303  procedure AttachUnique(TheWindow: PGtkWidget; TheAccelGroup: PGTKAccelGroup);
5304  begin
5305    {$IfDef GTK2}
5306    if (TheWindow=nil) or (TheAccelGroup=nil)
5307      or (TheAccelGroup^.acceleratables=nil)
5308      or (g_slist_find(TheAccelGroup^.acceleratables, TheWindow)=nil)
5309    then
5310      exit;
5311    gtk_window_add_accel_group(GTK_WINDOW(TheWindow), TheAccelGroup);
5312    {$else}
5313    if (TheAccelGroup=nil)
5314    or ((TheAccelGroup^.attach_objects<>nil)
5315      and (g_slist_find(TheAccelGroup^.attach_objects, TheWindow)<>nil))
5316    then
5317      exit;
5318    gtk_accel_group_attach(TheAccelGroup, PGtkObject(TheWindow));
5319    {$endif}
5320  end;
5321
5322var
5323  TheForm, CurForm: TCustomForm;
5324  i: integer;
5325  TheAccelGroup, CurAccelGroup: PGTKAccelGroup;
5326  CurWindow: PGtkWidget;
5327begin
5328  TheForm:=TCustomForm(GetLCLObject(AWindow));
5329
5330  // check if visible TCustomForm (not frame)
5331  if (TheForm=nil) or (not (TheForm is TCustomForm))
5332  or (not TheForm.Visible) or (TheForm.Parent<>nil)
5333  or (csDesigning in TheForm.ComponentState)
5334  then
5335    exit;
5336
5337  // check if modal form
5338  if fsModal in TheForm.FormState then begin
5339    // a modal form does not share accelerators
5340    exit;
5341  end;
5342
5343  // check if there is an accelerator group
5344  TheAccelGroup:=GetAccelGroup(AWindow,false);
5345
5346  // this is a normal form
5347  // -> share accelerators with all other visible normal forms
5348  for i:=0 to Screen.FormCount-1 do begin
5349    CurForm:=Screen.Forms[i];
5350    if (CurForm=TheForm)
5351    or (not CurForm.HandleAllocated)
5352    or (not CurForm.Visible)
5353    or (fsModal in CurForm.FormState)
5354    or (CurForm.Parent<>nil)
5355    or (csDesigning in CurForm.ComponentState)
5356    then continue;
5357
5358    CurWindow:=PGtkWidget(CurForm.Handle);
5359    CurAccelGroup:=GetAccelGroup(CurWindow,false);
5360    {$IFDEF VerboseAccelerator}
5361    DebugLn('ShareWindowAccelGroups ',TheForm.Name,':',TheForm.ClassName,
5362            ' <-> ',CurForm.Name,':',CurForm.ClassName);
5363    {$ENDIF}
5364
5365    // cross connect
5366    AttachUnique(CurWindow,TheAccelGroup);
5367    AttachUnique(AWindow,CurAccelGroup);
5368  end;
5369end;
5370
5371procedure UnshareWindowAccelGroups(AWindow: PGtkWidget);
5372
5373  procedure Detach(TheWindow: PGtkWidget; TheAccelGroup: PGTKAccelGroup);
5374  begin
5375    {$IfDef GTK2}
5376    if (TheWindow=nil) or (TheAccelGroup=nil)
5377      or (TheAccelGroup^.acceleratables=nil)
5378      or (g_slist_find(TheAccelGroup^.acceleratables, TheWindow)=nil)
5379    then
5380      exit;
5381    gtk_window_remove_accel_group(GTK_WINDOW(TheWindow), TheAccelGroup);
5382    {$else}
5383    if (TheAccelGroup=nil)
5384    or (TheAccelGroup^.attach_objects=nil)
5385    or (g_slist_find(TheAccelGroup^.attach_objects, TheWindow)=nil)
5386    then
5387      exit;
5388    gtk_accel_group_detach(TheAccelGroup, PGtkObject(TheWindow));
5389    {$endif}
5390  end;
5391
5392var
5393  TheForm, CurForm: TCustomForm;
5394  i: integer;
5395  TheAccelGroup, CurAccelGroup: PGTKAccelGroup;
5396  CurWindow: PGtkWidget;
5397begin
5398  TheForm:=TCustomForm(GetLCLObject(AWindow));
5399
5400  // check if TCustomForm
5401  if (TheForm=nil) or (not (TheForm is TCustomForm))
5402  then exit;
5403
5404  TheAccelGroup:=GetAccelGroup(AWindow,false);
5405
5406  // -> unshare accelerators with all other forms
5407  for i:=0 to Screen.FormCount-1 do begin
5408    CurForm:=Screen.Forms[i];
5409    if (CurForm=TheForm)
5410    or (not CurForm.HandleAllocated)
5411    then continue;
5412
5413    CurWindow:=PGtkWidget(CurForm.Handle);
5414    CurAccelGroup:=GetAccelGroup(CurWindow,false);
5415    {$IFDEF VerboseAccelerator}
5416    DebugLn('UnshareWindowAccelGroups ',TheForm.Name,':',TheForm.ClassName,
5417            ' <-> ',CurForm.Name,':',CurForm.ClassName);
5418    {$ENDIF}
5419
5420    // unlink
5421    Detach(CurWindow,TheAccelGroup);
5422    Detach(AWindow,CurAccelGroup);
5423  end;
5424end;
5425
5426function GetAccelGroupForComponent(Component: TComponent;
5427  CreateIfNotExists: boolean): PGTKAccelGroup;
5428var
5429  Control: TControl;
5430  MenuItem: TMenuItem;
5431  Form: TCustomForm;
5432  Menu: TMenu;
5433begin
5434  Result:=nil;
5435  if Component=nil then exit;
5436
5437  if Component is TMenuItem then begin
5438    MenuItem:=TMenuItem(Component);
5439    Menu:=MenuItem.GetParentMenu;
5440    if (Menu=nil) or (Menu.Parent=nil) then exit;
5441    {$IFDEF VerboseAccelerator}
5442    DebugLn('GetAccelGroupForComponent A ',Component.Name,':',Component.ClassName);
5443    {$ENDIF}
5444    Result:=GetAccelGroupForComponent(Menu.Parent,CreateIfNotExists);
5445  end else if Component is TControl then begin
5446    Control:=TControl(Component);
5447    while Control.Parent<>nil do Control:=Control.Parent;
5448    if Control is TCustomForm then begin
5449      Form:=TCustomForm(Control);
5450      if Form.HandleAllocated then begin
5451        Result:=GetAccelGroup(PGtkWidget(Form.Handle),CreateIfNotExists);
5452        {$IFDEF VerboseAccelerator}
5453        DebugLn('GetAccelGroupForComponent C ',Component.Name,':',Component.ClassName);
5454        {$ENDIF}
5455      end;
5456    end;
5457  end;
5458  {$IFDEF VerboseAccelerator}
5459  DebugLn('GetAccelGroupForComponent END ',Component.Name,':',Component.ClassName,' Result=',DbgS(Result));
5460  {$ENDIF}
5461end;
5462
5463function GetAccelKey(Widget: PGtkWidget): PAcceleratorKey;
5464begin
5465  Result := PAcceleratorKey(gtk_object_get_data(PGtkObject(Widget),'AccelKey'));
5466end;
5467
5468function SetAccelKey(const Widget: PGtkWidget;
5469  Key: guint; Mods: TGdkModifierType; const Signal: string): PAcceleratorKey;
5470begin
5471  if (Widget = nil) then exit(nil);
5472  Result:=GetAccelKey(Widget);
5473  if Result=nil then begin
5474    if Key>0 then begin
5475      New(Result);
5476      FillChar(Result^,SizeOf(Result),0);
5477    end;
5478  end else begin
5479    if Key=0 then begin
5480      Dispose(Result);
5481      Result:=nil;
5482    end;
5483  end;
5484  if (Result<>nil) then begin
5485    Result^.Key:=Key;
5486    Result^.Mods:=Mods;
5487    Result^.Signal:=Signal;
5488    Result^.Realized:=false;
5489  end;
5490  {$IFDEF VerboseAccelerator}
5491  DebugLn('SetAccelKey Widget=',DbgS(Widget),
5492    ' Key=',dbgs(Key),' Mods=',DbgS(Mods),
5493    ' Signal="',Signal,'" Result=',DbgS(Result));
5494  {$ENDIF}
5495  gtk_object_set_data(PGtkObject(Widget), 'AccelKey', Result);
5496end;
5497
5498procedure ClearAccelKey(Widget: PGtkWidget);
5499begin
5500  SetAccelKey(Widget,0,0,'');
5501end;
5502
5503procedure RealizeAccelerator(Component: TComponent; Widget : PGtkWidget);
5504var
5505  AccelKey: PAcceleratorKey;
5506  AccelGroup: PGTKAccelGroup;
5507begin
5508  if (Component=nil) or (Widget=nil) then
5509    RaiseGDBException('RealizeAccelerate: invalid input');
5510
5511  // Set the accelerator
5512  AccelKey:=GetAccelKey(Widget);
5513  if (AccelKey=nil) or (AccelKey^.Realized) then exit;
5514
5515  if AccelKey^.Key>0 then begin
5516    AccelGroup:=GetAccelGroupForComponent(Component,true);
5517    if AccelGroup<>nil then begin
5518      {$IFDEF VerboseAccelerator}
5519      DebugLn('RealizeAccelerator Add Accelerator ',
5520        Component.Name,':',Component.ClassName,
5521        ' Widget=',DbgS(Widget),
5522        ' Signal=',AccelKey^.Signal,
5523        ' Key=',dbgs(AccelKey^.Key),' Mods=',dbgs(AccelKey^.Mods),
5524        '');
5525      {$ENDIF}
5526      gtk_widget_add_accelerator(Widget, PChar(AccelKey^.Signal),
5527        AccelGroup, AccelKey^.Key, AccelKey^.Mods, GTK_ACCEL_VISIBLE);
5528      AccelKey^.Realized:=true;
5529    end else begin
5530      AccelKey^.Realized:=false;
5531    end;
5532  end else begin
5533    AccelKey^.Realized:=true;
5534  end;
5535end;
5536
5537procedure UnrealizeAccelerator(Widget : PGtkWidget);
5538var
5539  AccelKey: PAcceleratorKey;
5540begin
5541  if (Widget=nil) then
5542    RaiseGDBException('UnrealizeAccelerate: invalid input');
5543
5544  AccelKey:=GetAccelKey(Widget);
5545  if (AccelKey=nil) or (not AccelKey^.Realized) then exit;
5546
5547  if AccelKey^.Signal<>'' then begin
5548    {$IFDEF VerboseAccelerator}
5549    DebugLn('UnrealizeAccelerator  ',
5550      ' Widget=',DbgS(Widget),
5551      ' Signal=',AccelKey^.Signal,
5552      ' Key=',dbgs(AccelKey^.Key),' Mods=',dbgs(AccelKey^.Mods),
5553      '');
5554    {$ENDIF}
5555    {$Ifdef GTK2}
5556       DebugLn('ToDo: gtkproc.inc UnrealizeAccelerator');
5557    {$else}
5558    gtk_widget_remove_accelerators(Widget, PChar(AccelKey^.Signal), false);
5559    {$EndIf}
5560  end;
5561  AccelKey^.Realized:=false;
5562end;
5563
5564procedure RegroupAccelerator(Widget: PGtkWidget);
5565begin
5566  UnrealizeAccelerator(Widget);
5567  RealizeAccelerator(TComponent(GetLCLObject(Widget)),Widget);
5568end;
5569
5570procedure Accelerate(Component: TComponent; const Widget : PGtkWidget;
5571  const Key: guint; Mods: TGdkModifierType; const Signal : string);
5572var
5573  OldAccelKey: PAcceleratorKey;
5574begin
5575  if (Component=nil) or (Widget=nil) or (Signal='') then
5576    RaiseGDBException('Accelerate: invalid input');
5577  {$IFDEF VerboseAccelerator}
5578  DebugLn('Accelerate ',DbgSName(Component),' Key=',dbgs(Key),' Mods=',DbgS(Mods),' Signal=',Signal);
5579  {$ENDIF}
5580
5581  // delete old accelerator key
5582  OldAccelKey:=GetAccelKey(Widget);
5583  if (OldAccelKey <> nil) then begin
5584    if (OldAccelKey^.Key=Key) and (OldAccelKey^.Mods=Mods)
5585    and (OldAccelKey^.Signal=Signal)
5586    then begin
5587      // no change
5588      exit;
5589    end;
5590
5591    UnrealizeAccelerator(Widget);
5592  end;
5593
5594  // Set the accelerator
5595  SetAccelKey(Widget,Key,Mods,Signal);
5596  if (Key>0) and (not (csDesigning in Component.ComponentState))
5597  then
5598    RealizeAccelerator(Component,Widget);
5599end;
5600
5601procedure Accelerate(Component: TComponent; const Widget : PGtkWidget;
5602  const NewShortCut: TShortCut; const Signal : string);
5603var
5604  GDKModifier: TGdkModifierType;
5605  GDKKey: guint;
5606  NewKey: word;
5607  NewModifier: TShiftState;
5608  Shift: TShiftStateEnum;
5609begin
5610  { Map the shift states }
5611  GDKModifier := 0;
5612  ShortCutToKey(NewShortCut, NewKey, NewModifier);
5613  for Shift := Low(Shift) to High(Shift) do
5614  begin
5615    if Shift in NewModifier
5616    then GDKModifier := GDKModifier or MModifiers[Shift].Mask;
5617  end;
5618
5619  // Send the unmodified keysym ?
5620  if (ssShift in NewModifier)
5621  and ((NewKey < VK_F1) or (NewKey > VK_F24))
5622  then GDKKey := GetVKeyInfo(NewKey).KeySym[1]
5623  else GDKKey := GetVKeyInfo(NewKey).KeySym[0];
5624
5625  Accelerate(Component,Widget,GDKKey,GDKModifier,Signal);
5626end;
5627
5628{-------------------------------------------------------------------------------
5629  method TGtkWidgetSet LoadPixbufFromLazResource
5630  Params: const ResourceName: string;
5631          var Pixbuf: PGdkPixbuf
5632  Result: none
5633
5634  Loads a pixbuf from a lazarus resource. The resource must be a XPM file.
5635-------------------------------------------------------------------------------}
5636procedure LoadPixbufFromLazResource(const ResourceName: string;
5637  var Pixbuf: PGdkPixbuf);
5638var
5639  ImgData: PPChar;
5640begin
5641  Pixbuf:=nil;
5642  try
5643    ImgData:=LazResourceXPMToPPChar(ResourceName);
5644  except
5645    on e: Exception do
5646      DebugLn('WARNING: TGtkWidgetSet.LoadXPMFromLazResource: '+e.Message);
5647  end;
5648  {$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
5649  {$IFDEF VerboseGdkPixbuf}
5650  debugln('LoadPixbufFromLazResource A1');
5651  {$ENDIF}
5652  pixbuf:=gdk_pixbuf_new_from_xpm_data(ImgData);
5653  {$IFDEF VerboseGdkPixbuf}
5654  debugln('LoadPixbufFromLazResource A2');
5655  {$ENDIF}
5656  {$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF}
5657  FreeMem(ImgData);
5658end;
5659
5660{-------------------------------------------------------------------------------
5661  method CreatePixbufFromDrawable
5662  Params: ASource: The source drawable
5663          AColorMap: The colormap to use, when nil a matching colormap is passed
5664          AIncludeAplha: If set, the resulting pixmap has an alpha channel
5665          ASrcX, ASrcY: Offset within the source
5666          ADstX, ADstY: Offset within destination
5667          AWidth, AHeight: Size of the new image
5668  Result: New Pixbuf with refcount = 1
5669
5670  Replaces the gdk_pixbuf_get_from_drawable function which is buggy on big endian
5671  X servers when an alpha channel is requested.
5672-------------------------------------------------------------------------------}
5673function CreatePixbufFromDrawable(ASource: PGdkDrawable; AColorMap:PGdkColormap; AIncludeAplha: Boolean; ASrcX, ASrcY, ADstX, ADstY, AWidth, AHeight: longint): PGdkPixbuf;
5674{$ifndef HasX}
5675const
5676  CanRequestAlpha: Boolean = True;
5677var
5678{$else}
5679var
5680  CanRequestAlpha: Boolean;
5681{$endif}
5682  PixBuf: PGdkPixBuf;
5683{$ifdef Windows}
5684  Image: PGdkImage;
5685{$endif}
5686begin
5687  {$ifdef HasX}
5688  CanRequestAlpha := BitmapBitOrder(gdk_display) = LSBFirst;
5689  {$endif}
5690
5691  // If Source is GdkBitmap then gdk_pixbuf_get_from_drawable will get
5692  // pixbuf with 2 colors: transparent and white, but we need only Black and White.
5693  // If we all alpha at the end then problem is gone.
5694  CanRequestAlpha := CanRequestAlpha and (gdk_drawable_get_depth(ASource) > 1);
5695
5696  if CanRequestAlpha and AIncludeAplha
5697  then Pixbuf := gdk_pixbuf_new(GDK_COLORSPACE_RGB, True, 8, AWidth, AHeight)
5698  else Pixbuf := nil;
5699
5700  // gtk1 requires always a colormap and fails when none passed
5701  // gtk2 fails when the colormap depth is different than the drawable depth.
5702  //      It wil use the correct system map when none passed.
5703  //      Bitmaps (depth = 1) don't need a colormap
5704  {$ifdef gtk1}
5705  if AColormap = nil
5706  then AColorMap := gdk_colormap_get_system;
5707  {$else}
5708  if  (AColorMap = nil)
5709  and (gdk_drawable_get_depth(ASource) > 1)
5710  and (gdk_drawable_get_colormap(ASource) = nil)
5711  then AColorMap := gdk_colormap_get_system;
5712  {$endif}
5713  {$ifdef Windows}
5714  if gdk_drawable_get_depth(ASource) = 1 then
5715  begin
5716    // Fix gdk error in converter. For 1 bit Byte order is not significant
5717    Image := gdk_drawable_get_image(ASource, ASrcX, ASrcY, AWidth, AHeight);
5718    Image^.byte_order := GDK_MSB_FIRST;
5719    Result := gdk_pixbuf_get_from_image(Pixbuf, Image, nil, ASrcX, ASrcY, ADstX, ADstY, AWidth, AHeight);
5720    gdk_image_unref(Image);
5721  end
5722  else
5723  {$endif}
5724  Result := gdk_pixbuf_get_from_drawable(Pixbuf, ASource, AColorMap, ASrcX, ASrcY, ADstX, ADstY, AWidth, AHeight);
5725  //DbgDumpPixbuf(Result, '');
5726
5727  if CanRequestAlpha then Exit; // we're done
5728  if not AIncludeAplha then Exit;
5729
5730  pixbuf := gdk_pixbuf_add_alpha(Result, false, guchar(0),guchar(0),guchar(0));
5731  gdk_pixbuf_unref(Result);
5732  Result := pixbuf;
5733end;
5734
5735{-------------------------------------------------------------------------------
5736  method LoadXPMFromLazResource
5737  Params: const ResourceName: string;
5738          Window: PGdkWindow;
5739          var PixmapImg, PixmapMask: PGdkPixmap
5740  Result: none
5741
5742  Loads a pixmap from a lazarus resource. The resource must be a XPM file.
5743-------------------------------------------------------------------------------}
5744procedure LoadXPMFromLazResource(const ResourceName: string;
5745  Window: PGdkWindow; var PixmapImg, PixmapMask: PGdkPixmap);
5746var
5747  ImgData: PPGChar;
5748begin
5749  PixmapImg:=nil;
5750  PixmapMask:=nil;
5751  try
5752    ImgData:=PPGChar(LazResourceXPMToPPChar(ResourceName));
5753  except
5754    on e: Exception do
5755      DebugLn('WARNING: TGtkWidgetSet.LoadXPMFromLazResource: '+e.Message);
5756  end;
5757  {$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
5758  PixmapImg:=gdk_pixmap_create_from_xpm_d(Window,PixmapMask,nil,ImgData);
5759  {$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF}
5760  FreeMem(ImgData);
5761end;
5762
5763{------------------------------------------------------------------------------
5764  function MENU_ITEM_CLASS(widget: PGtkWidget): PGtkMenuItemClass;
5765
5766  Returns the gtk klass of a menuitem widget.
5767 ------------------------------------------------------------------------------}
5768function MENU_ITEM_CLASS(widget: PGtkWidget): PGtkMenuItemClass;
5769begin
5770  Result:=GTK_MENU_ITEM_CLASS(gtk_object_get_class(widget));
5771end;
5772
5773{------------------------------------------------------------------------------
5774  function CHECK_MENU_ITEM_CLASS(widget: PGtkWidget): PGtkCheckMenuItemClass;
5775
5776  Returns the gtk klass of a checkmenuitem widget.
5777 ------------------------------------------------------------------------------}
5778function CHECK_MENU_ITEM_CLASS(widget: PGtkWidget): PGtkCheckMenuItemClass;
5779begin
5780  Result:=GTK_CHECK_MENU_ITEM_CLASS(gtk_object_get_class(widget));
5781end;
5782
5783{------------------------------------------------------------------------------
5784  procedure LockRadioGroupOnChange(RadioGroup: PGSList; const ADelta: Integer);
5785
5786  Calls LockOnChange for all groupmembers
5787 ------------------------------------------------------------------------------}
5788procedure LockRadioGroupOnChange(RadioGroup: PGSList; const ADelta: Integer);
5789begin
5790  while RadioGroup <> nil do
5791  begin
5792    if RadioGroup^.Data <> nil
5793    then LockOnChange(PgtkObject(RadioGroup^.Data), ADelta);
5794    RadioGroup := RadioGroup^.Next;
5795  end;
5796end;
5797
5798{------------------------------------------------------------------------------
5799  procedure UpdateRadioGroupChecks(RadioGroup: PGSList);
5800
5801  Set 'checked' for all menuitems in the group
5802 ------------------------------------------------------------------------------}
5803procedure UpdateRadioGroupChecks(RadioGroup: PGSList);
5804var
5805  CurListItem: PGSList;
5806  MenuItem: PGtkCheckMenuItem;
5807  LCLMenuItem: TMenuItem;
5808begin
5809  // Check if it is a single entry
5810  if (RadioGroup = nil) or (RadioGroup^.Next = nil)
5811  then Exit;
5812
5813  // Lock whole group for update
5814  LockRadioGroupOnChange(RadioGroup, +1);
5815  CurListItem := RadioGroup;
5816  try
5817    // set active radiomenuitem
5818    while CurListItem <> nil do
5819    begin
5820      MenuItem := PGtkCheckMenuItem(CurListItem^.Data);
5821      if MenuItem<>nil
5822      then begin
5823        LCLMenuItem := TMenuItem(GetLCLObject(MenuItem));
5824        if  (LCLMenuItem <> nil)
5825        and (gtk_check_menu_item_get_active(MenuItem) <> LCLMenuItem.Checked)
5826        then gtk_check_menu_item_set_active(MenuItem, LCLMenuItem.Checked);
5827      end;
5828      CurListItem := CurListItem^.Next;
5829    end;
5830  finally
5831    // Unlock whole group for update
5832    LockRadioGroupOnChange(RadioGroup, -1);
5833  end;
5834end;
5835
5836{------------------------------------------------------------------------------
5837  procedure DrawMenuItemIcon(MenuItem: PGtkCheckMenuItem;
5838    area: PGdkRectangle); cdecl;
5839
5840  Handler for drawing the icon of a menuitem.
5841 ------------------------------------------------------------------------------}
5842procedure DrawMenuItemIcon(MenuItem: PGtkCheckMenuItem;
5843  Area: PGdkRectangle); cdecl;
5844var
5845  Widget: PGtkWidget;
5846  Container: PgtkContainer;
5847  ALeft, ATop, BorderWidth: gint;
5848  LCLMenuItem: TMenuItem;
5849  AWindow: PGdkWindow;
5850  IconWidth, IconHeight: integer;
5851  IconSize: TPoint;
5852  {$IFDEF Gtk2}
5853  HorizPadding, ToggleSpacing: Integer;
5854  {$ENDIF}
5855
5856  AEffect: TGraphicsDrawEffect;
5857  AImageList: TCustomImageList;
5858  FreeImageList: Boolean;
5859  AImageIndex: Integer;
5860  ItemBmp: TBitmap;
5861begin
5862  if (MenuItem=nil) then
5863    exit;
5864  if not (GTK_WIDGET_DRAWABLE (PGtkWidget(MenuItem))) then
5865    exit;
5866
5867  // get icon
5868  LCLMenuItem:=TMenuItem(GetLCLObject(MenuItem));
5869  if LCLMenuItem=nil then begin // needed for gtk2 dialog
5870    if GtkWidgetIsA(PGtkWidget(MenuItem), gtk_check_menu_item_get_type) then
5871      OldCheckMenuItemDrawProc(MenuItem, Area);
5872    Exit;
5873  end;
5874  if not LCLMenuItem.HasIcon then
5875  begin
5876    // call default draw function
5877    OldCheckMenuItemDrawProc(MenuItem,Area);
5878    exit;
5879  end;
5880  IconSize:=LCLMenuItem.GetIconSize(0);
5881  IconWidth:=IconSize.X;
5882  IconHeight:=IconSize.Y;
5883
5884  // calculate left and top
5885  Widget := PGtkWidget(MenuItem);
5886  AWindow:=GetControlWindow(Widget);
5887  if AWindow = nil then
5888    exit;
5889  Container := GTK_CONTAINER (MenuItem);
5890  BorderWidth := Container^.flag0 and bm_TGtkContainer_border_width;
5891
5892  {$IFDEF Gtk2}
5893  gtk_widget_style_get(PGtkWidget(MenuItem),
5894                       'horizontal-padding', @HorizPadding,
5895                       'toggle-spacing', @ToggleSpacing,
5896                       nil);
5897
5898  ALeft := BorderWidth +
5899           gtk_widget_get_xthickness(gtk_widget_get_style(Widget)) +
5900           HorizPadding +
5901           ((PGtkMenuItem(MenuItem)^.toggle_size-ToggleSpacing-IconWidth) div 2);
5902
5903  if gtk_widget_get_direction(Widget) = GTK_TEXT_DIR_RTL then
5904    ALeft := Widget^.Allocation.width - IconWidth - ALeft; //not sure it is the correct Width
5905  {$ELSE}
5906  ALeft := (BorderWidth + gtk_widget_get_xthickness(gtk_widget_get_style(Widget)) + 2)
5907           +((PGtkMenuItem(MenuItem)^.toggle_size-IconWidth) div 2);
5908  {$ENDIF}
5909
5910  ATop := (Widget^.Allocation.Height - IconHeight) div 2;
5911
5912  // draw icon
5913  AImageList := LCLMenuItem.GetImageList;
5914  if AImageList = nil then
5915  begin
5916    AImageList := TImageList.Create(nil);
5917    // prevent multiple calls to GetBitmap;
5918    ItemBmp := LCLMenuItem.Bitmap;
5919    AImageList.Width := ItemBmp.Width; // maybe height to prevent too wide bitmaps?
5920    AImageList.Height := ItemBmp.Height;
5921    if ItemBmp.Masked
5922    then AImageIndex := AImageList.AddMasked(ItemBmp, ItemBmp.TransparentColor)
5923    else AImageIndex := AImageList.Add(ItemBmp, nil);
5924    FreeImageList := True;
5925  end
5926  else
5927  begin
5928    FreeImageList := False;
5929    AImageIndex := LCLMenuItem.ImageIndex;
5930  end;
5931
5932  if not LCLMenuItem.Enabled then
5933    AEffect := gdeDisabled
5934  else
5935    AEffect := gdeNormal;
5936
5937  if AImageIndex < AImageList.Count then
5938    {$IFDEF VerboseGtkToDos}{$note reimplement}{$ENDIF}
5939    DrawImageListIconOnWidget(AImageList, AImageIndex, AEffect,
5940      Widget, false, false, ALeft, ATop);
5941
5942  if FreeImageList then
5943    AImageList.Free;
5944end;
5945
5946{------------------------------------------------------------------------------
5947  procedure MenuSizeRequest(widget:PGtkWidget;
5948    requisition:PGtkRequisition); cdecl;
5949
5950  SizeAllocate Handler for check menuitem widgets.
5951 ------------------------------------------------------------------------------}
5952procedure MenuSizeRequest(widget:PGtkWidget; requisition:PGtkRequisition); cdecl;
5953var
5954  CurToggleSize, MaxToggleSize: integer;
5955  MenuShell: PGtkMenuShell;
5956  ListItem: PGList;
5957  MenuItem: PGtkMenuItem;
5958  CheckMenuItem: PGtkMenuItem;
5959  LCLMenuItem: TMenuItem;
5960  IconSize: TPoint;
5961begin
5962  MaxToggleSize:=0;
5963  MenuShell:=GTK_MENU_SHELL(widget);
5964  ListItem:=MenuShell^.Children;
5965  CheckMenuItem:=nil;
5966  while ListItem<>nil do begin
5967    MenuItem:=PGtkMenuItem(ListItem^.Data);
5968    if GTK_IS_CHECK_MENU_ITEM(PGtkWidget(MenuItem)) then begin
5969      CheckMenuItem:=MenuItem;
5970      CurToggleSize:=OldCheckMenuItemToggleSize;
5971      LCLMenuItem:=TMenuItem(GetLCLObject(MenuItem));
5972      if LCLMenuItem<>nil then begin
5973        IconSize:=LCLMenuItem.GetIconSize(0);
5974        {if IconSize.X>100 then
5975          debugln('MenuSizeRequest LCLMenuItem=',LCLMenuItem.Name,' ',LCLMenuItem.Caption,
5976            ' ');}
5977        if CurToggleSize<IconSize.X then
5978          CurToggleSize:=IconSize.X;
5979      end;
5980      if MaxToggleSize<CurToggleSize then
5981        MaxToggleSize:=CurToggleSize;
5982    end;
5983    ListItem:=ListItem^.Next;
5984  end;
5985  //DebugLn('MenuSizeRequest A MaxToggleSize=',MaxToggleSize);
5986  {$IFDEF Gtk2}
5987  // Gtk2ToDo
5988  if CheckMenuItem<>nil then begin
5989    GTK_MENU_ITEM(CheckMenuItem)^.toggle_size := 0;
5990    gtk_menu_item_toggle_size_allocate(GTK_MENU_ITEM(CheckMenuItem),MaxToggleSize);
5991    GTK_MENU_ITEM(CheckMenuItem)^.toggle_size := MaxToggleSize;
5992  end;
5993  {$ELSE}
5994  if CheckMenuItem<>nil then
5995    MENU_ITEM_CLASS(PGtkWidget(CheckMenuItem))^.toggle_size:=MaxToggleSize;
5996  {$ENDIF}
5997  //DebugLn('MenuSizeRequest B ',GetWidgetDebugReport(widget),' ',requisition^.width,',',requisition^.height);
5998  OldMenuSizeRequestProc(Widget,requisition);
5999  //DebugLn('MenuSizeRequest C ',GetWidgetDebugReport(widget),' ',requisition^.width,',',requisition^.height);
6000end;
6001
6002procedure UpdateInnerMenuItem(LCLMenuItem: TMenuItem; MenuItemWidget: PGtkWidget);
6003begin
6004  UpdateInnerMenuItem(LCLMenuItem, MenuItemWidget, LCLMenuItem.ShortCut, LCLMenuItem.ShortCutKey2);
6005end;
6006
6007{------------------------------------------------------------------------------
6008  Update the inner widgets of a menuitem widget.
6009 ------------------------------------------------------------------------------}
6010procedure UpdateInnerMenuItem(LCLMenuItem: TMenuItem; MenuItemWidget: PGtkWidget;
6011  NewShortCut, ShortCutKey2: TShortCut);
6012{$ifdef GTK2}
6013const
6014  WidgetDirection : array[boolean] of longint = (GTK_TEXT_DIR_LTR, GTK_TEXT_DIR_RTL);
6015  {$endif}
6016  function UseRTL: Boolean;
6017  begin
6018    Result := LCLMenuItem.GetIsRightToLeft;
6019  end;
6020var
6021  HBoxWidget: PGtkWidget;
6022
6023  procedure SetMenuItemLabelText(LCLMenuItem: TMenuItem;
6024    MenuItemWidget: PGtkWidget);
6025  var
6026    LabelWidget: PGtkLabel;
6027  begin
6028    if (MenuItemWidget = nil) or (LCLMenuItem = nil) then
6029      Exit;
6030    LabelWidget := gtk_object_get_data(PGtkObject(MenuItemWidget), 'LCLLabel');
6031    GtkWidgetset.SetLabelCaption(LabelWidget, LCLMenuItem.Caption);
6032    {$ifdef GTK2}
6033    gtk_widget_set_direction(PGtkWidget(LabelWidget), WidgetDirection[UseRTL]);
6034    {$endif}
6035  end;
6036
6037  procedure UpdateShortCutLabel;
6038  var
6039    LabelWidget: PGtkLabel;
6040    NeedShortCut: Boolean;
6041    Key, Key2: Word;
6042    Shift, Shift2: TShiftState;
6043    s: String;
6044  begin
6045    //DebugLn(['UpdateShortCutLabel ',dbgsName(LCLMenuItem),' ',ShortCutToText(NewShortCut)]);
6046    ShortCutToKey(NewShortCut, Key, Shift);
6047    ShortCutToKey(ShortCutKey2, Key2, Shift2);
6048
6049    // Check if shortcut is needed. No shortcut captions for items in menubar
6050    NeedShortCut := (Key <> 0) and
6051       not ( (LCLMenuItem.Parent <> nil) and LCLMenuItem.Parent.HandleAllocated and
6052       GtkWidgetIsA(PGtkWidget(LCLMenuItem.Parent.Handle), GTK_TYPE_MENU_BAR) );
6053
6054    LabelWidget := PGtkLabel(gtk_object_get_data(PGtkObject(MenuItemWidget),'LCLShortCutLabel'));
6055    if NeedShortCut then
6056    begin
6057      s := GetAcceleratorString(Key, Shift);
6058      if Key2 <> 0 then
6059        s := s + ', ' + GetAcceleratorString(Key2, Shift2);
6060      //  ShortCutToText(NewShortCut);
6061      if LabelWidget = nil then
6062      begin
6063        // create a label for the ShortCut
6064        LabelWidget := PGtkLabel(gtk_label_new(PChar(Pointer(s))));
6065        gtk_object_set_data(PGtkObject(MenuItemWidget), 'LCLShortCutLabel', LabelWidget);
6066        gtk_container_add(GTK_CONTAINER(HBoxWidget), PGtkWidget(LabelWidget));
6067        gtk_widget_show(PGtkWidget(LabelWidget));
6068      end
6069      else
6070      begin
6071        gtk_label_set_text(LabelWidget, PChar(Pointer(s)));
6072      end;
6073      {$ifdef GTK2}
6074      gtk_widget_set_direction(PGtkWidget(LabelWidget), GTK_TEXT_DIR_LTR); //Shortcut always LTR
6075      {$endif}
6076      if UseRTL then
6077        gtk_misc_set_alignment(GTK_MISC(LabelWidget), 0.0, 0.5)
6078      else
6079        gtk_misc_set_alignment(GTK_MISC (LabelWidget), 1.0, 0.5);
6080    end else
6081    if LabelWidget <> nil then
6082      gtk_widget_destroy(PGtkWidget(LabelWidget));
6083  end;
6084
6085  procedure CreateIcon;
6086  var
6087    {$IFNDEF Gtk2}
6088    IconWidth, IconHeight: integer;
6089    IconSize: TPoint;
6090    {$ENDIF}
6091    MinHeightWidget: PGtkWidget;
6092  begin
6093    // the icon will be painted instead of the toggle
6094    // of a normal gtkcheckmenuitem
6095
6096    if LCLMenuItem.HasIcon then
6097    begin
6098      {$IFNDEF Gtk2}
6099      IconSize := LCLMenuItem.GetIconSize(0);
6100      IconWidth := IconSize.X;
6101      IconHeight := IconSize.Y;
6102      // set the toggle width
6103      GTK_MENU_ITEM(MenuItemWidget)^.toggle_size := guint16(IconWidth);
6104      {$ENDIF}
6105
6106      GTK_MENU_ITEM(MenuItemWidget)^.flag0:=
6107        PGtkMenuItem(MenuItemWidget)^.flag0 or
6108          {$IFDEF Gtk2}
6109          bm_TGtkCheckMenuItem_always_show_toggle;
6110          {$ELSE}
6111          bm_show_toggle_indicator;
6112          {$ENDIF}
6113
6114      // set our own draw handler
6115      if OldCheckMenuItemDrawProc = nil then
6116        OldCheckMenuItemDrawProc := CHECK_MENU_ITEM_CLASS(MenuItemWidget)^.draw_indicator;
6117      CHECK_MENU_ITEM_CLASS(MenuItemWidget)^.draw_indicator := @DrawMenuItemIcon;
6118
6119      {$IFNDEF Gtk2}
6120      // add a dummy widget for the icon height
6121      MinHeightWidget := gtk_label_new('');
6122      gtk_widget_show(MinHeightWidget);
6123      gtk_widget_set_usize(MinHeightWidget, 1, IconHeight);
6124      gtk_box_pack_start(GTK_BOX(HBoxWidget), MinHeightWidget, False, False, 0);
6125      {$ENDIF}
6126    end
6127    else
6128      MinHeightWidget := nil;
6129    gtk_object_set_data(PGtkObject(MenuItemWidget),
6130                        'LCLMinHeight', MinHeightWidget);
6131  end;
6132
6133  procedure CreateLabel;
6134  var
6135    LabelWidget: PGtkLabel;
6136  begin
6137    // create a label for the Caption
6138    LabelWidget := PGtkLabel(gtk_label_new(''));
6139    gtk_misc_set_alignment(GTK_MISC (LabelWidget), 0.0, 0.5);
6140    gtk_object_set_data(PGtkObject(MenuItemWidget), 'LCLLabel', LabelWidget);
6141    gtk_container_add(GTK_CONTAINER(HBoxWidget), PGtkWidget(LabelWidget));
6142    SetMenuItemLabelText(LCLMenuItem, MenuItemWidget);
6143    //gtk_accel_label_set_accel_widget(GTK_ACCEL_LABEL(LabelWidget), MenuItemWidget);
6144    gtk_widget_show(PGtkWidget(LabelWidget));
6145  end;
6146
6147begin
6148  HBoxWidget := gtk_object_get_data(PGtkObject(MenuItemWidget), 'LCLHBox');
6149  if HBoxWidget = nil then
6150  begin
6151    // create inner widgets
6152    if LCLMenuItem.Caption = cLineCaption then
6153    begin
6154      // a separator is an empty gtkmenuitem
6155      exit;
6156    end;
6157    HBoxWidget := gtk_hbox_new(false, 20);
6158    {$ifdef GTK2}
6159    gtk_widget_set_direction(PGtkWidget(HBoxWidget), WidgetDirection[UseRTL]);
6160    {$endif}
6161    gtk_object_set_data(PGtkObject(MenuItemWidget), 'LCLHBox', HBoxWidget);
6162    CreateIcon;
6163    CreateLabel;
6164    UpdateShortCutLabel;
6165    gtk_container_add(GTK_CONTAINER(MenuItemWidget), HBoxWidget);
6166    gtk_widget_show(HBoxWidget);
6167  end else
6168  begin
6169    // there are already inner widgets
6170    if LCLMenuItem.Caption = cLineCaption then
6171    begin
6172      // a separator is an empty gtkmenuitem -> delete the inner widgets
6173      DestroyWidget(HBoxWidget);
6174      gtk_object_set_data(PGtkObject(MenuItemWidget), 'LCLHBox', nil);
6175    end else
6176    begin
6177      // just update the content
6178      {$ifdef GTK2}
6179      gtk_widget_set_direction(PGtkWidget(HBoxWidget), WidgetDirection[UseRTL]);
6180      {$endif}
6181      SetMenuItemLabelText(LCLMenuItem, MenuItemWidget);
6182      UpdateShortCutLabel;
6183    end;
6184  end;
6185end;
6186
6187function CreateStatusBarPanel(StatusBar: TObject; Index: integer): PGtkWidget;
6188begin
6189  Result := gtk_statusbar_new;
6190  gtk_widget_show(Result);
6191  // other properties are set in UpdateStatusBarPanels
6192end;
6193
6194procedure UpdateStatusBarPanels(StatusBar: TObject; StatusBarWidget: PGtkWidget);
6195var
6196  AStatusBar: TStatusBar;
6197  HBox: PGtkWidget;
6198  CurPanelCount: integer;
6199  NewPanelCount: Integer;
6200  CurStatusPanelWidget: PGtkWidget;
6201  ListItem: PGList;
6202  i: Integer;
6203  ExpandItem: boolean;
6204{$IFNDEF GTK1}
6205  ShowSizeGrip: Boolean;
6206{$ENDIF}
6207begin
6208  AStatusBar := StatusBar as TStatusBar;
6209  HBox := PGtkWidget(StatusBarWidget);
6210  if (not GtkWidgetIsA(StatusBarWidget, GTK_HBOX_GET_TYPE)) then
6211    RaiseGDBException('');
6212
6213  // create needed panels
6214  CurPanelCount := integer(g_list_length(PGtkBox(HBox)^.children));
6215  if AStatusBar.SimplePanel or (AStatusBar.Panels.Count < 1) then
6216    NewPanelCount := 1
6217  else
6218    NewPanelCount := AStatusBar.Panels.Count;
6219
6220  while CurPanelCount < NewPanelCount do
6221  begin
6222    CurStatusPanelWidget := CreateStatusBarPanel(StatusBar, CurPanelCount);
6223    ExpandItem := (CurPanelCount = NewPanelCount - 1);
6224    gtk_box_pack_start(PGtkBox(HBox), CurStatusPanelWidget,
6225                       ExpandItem, ExpandItem, 0);
6226    inc(CurPanelCount);
6227  end;
6228
6229  // remove unneeded panels
6230  while CurPanelCount > NewPanelCount do
6231  begin
6232    CurStatusPanelWidget := PGtkBoxChild(
6233              g_list_nth_data(PGtkBox(HBox)^.children, CurPanelCount - 1))^.Widget;
6234    {$IFDEF GTK2}
6235    gtk_object_remove_data(PGtkObject(CurStatusPanelWidget),'lcl_statusbar_id');
6236    {$ENDIF}
6237    DestroyConnectedWidgetCB(CurStatusPanelWidget, True);
6238    dec(CurPanelCount);
6239  end;
6240
6241  // check new panel count
6242  CurPanelCount := integer(g_list_length(PGtkBox(HBox)^.children));
6243  //DebugLn('TGtkWidgetSet.UpdateStatusBarPanels B ',Dbgs(StatusBar),' NewPanelCount=',dbgs(NewPanelCount),' CurPanelCount=',dbgs(CurPanelCount));
6244  if CurPanelCount <> NewPanelCount then
6245    RaiseGDBException('');
6246
6247  // set panel properties
6248  {$IFNDEF GTK1}
6249  ShowSizeGrip := AStatusBar.SizeGrip and AStatusBar.SizeGripEnabled;
6250  {$ENDIF}
6251  ListItem := PGTKBox(HBox)^.children;
6252  i := 0;
6253  while ListItem <> nil do
6254  begin
6255    CurStatusPanelWidget := PGtkBoxChild(PGTKWidget(ListItem^.data))^.widget;
6256    ExpandItem := (ListItem^.next = nil);
6257    gtk_box_set_child_packing(PGtkBox(HBox), CurStatusPanelWidget,
6258      ExpandItem, ExpandItem, 0, GTK_PACK_START);
6259    UpdateStatusBarPanel(StatusBar, i, CurStatusPanelWidget);
6260    inc(i);
6261    ListItem := ListItem^.next;
6262    {$IFNDEF GTK1}
6263    gtk_statusbar_set_has_resize_grip(PGtkStatusBar(CurStatusPanelWidget),
6264      (ListItem = nil) and ShowSizeGrip);
6265    {$ENDIF}
6266  end;
6267end;
6268
6269{$IFDEF GTK2}
6270function gtk2PaintStatusBarWidget(Widget: PGtkWidget; Event : PGDKEventExpose;
6271  Data: gPointer): GBoolean; cdecl;
6272var
6273  Msg: TLMDrawItems;
6274  PS : TPaintStruct;
6275  ItemStruct: PDrawItemStruct;
6276  ItemID: Integer;
6277begin
6278  Result := CallBackDefaultReturn;
6279  if (Event^.Count > 0) then exit;
6280
6281  if (csDesigning in TComponent(Data).ComponentState) then
6282    exit;
6283
6284  if TStatusBar(Data).SimplePanel then
6285    exit;
6286
6287  ItemId := PtrInt(gtk_object_get_data(PGtkObject(Widget), 'lcl_statusbar_id')^);
6288
6289  if not ((ItemId >= 0) and (ItemId < TStatusBar(Data).Panels.Count)) then
6290    exit;
6291
6292  if TStatusBar(Data).Panels[ItemId].Style <> psOwnerDraw then
6293    exit;
6294
6295  FillChar(Msg, SizeOf(Msg), #0);
6296  FillChar(PS, SizeOf(PS), #0);
6297  FillChar(ItemStruct, SizeOf(ItemStruct), #0);
6298  New(ItemStruct);
6299  // we must fill up complete area otherwise gtk2 will do
6300  // strange paints when item is not fully exposed.
6301  ItemStruct^.rcItem := Rect(Widget^.allocation.x,
6302    Widget^.allocation.y,
6303    Widget^.allocation.width + Widget^.allocation.x,
6304    Widget^.allocation.height + Widget^.allocation.y);
6305
6306  OffsetRect(ItemStruct^.rcItem, -ItemStruct^.rcItem.Left, -ItemStruct^.rcItem.Top);
6307
6308  // take frame borders into account
6309  with ItemStruct^.rcItem do
6310  begin
6311    Left := Left + Widget^.style^.xthickness;
6312    Top := Top + Widget^.style^.ythickness;
6313    Right := Right - Widget^.style^.xthickness;
6314    Bottom := Bottom - Widget^.style^.ythickness;
6315  end;
6316
6317  ItemStruct^.itemID := ItemID;
6318  PS.rcPaint := ItemStruct^.rcItem;
6319  ItemStruct^._hDC := BeginPaint(THandle(PtrUInt(Widget)), PS);
6320  Msg.Ctl := TStatusBar(Data).Handle;
6321  Msg.DrawItemStruct := ItemStruct;
6322  Msg.Msg := LM_DRAWITEM;
6323  try
6324    DeliverMessage(TStatusBar(Data), Msg);
6325    Result := not CallBackDefaultReturn;
6326  finally
6327    PS.hdc := ItemStruct^._hDC;
6328    EndPaint(THandle(PtrUInt(TGtkDeviceContext(PS.hdc).Widget)), PS);
6329    Dispose(ItemStruct);
6330  end;
6331end;
6332{$ENDIF}
6333
6334procedure UpdateStatusBarPanel(StatusBar: TObject; Index: integer;
6335  StatusPanelWidget: PGtkWidget);
6336var
6337  AStatusBar: TStatusBar;
6338  CurPanel: TStatusPanel;
6339  FrameWidget: PGtkWidget;
6340  LabelWidget: PGtkLabel;
6341  PanelText: String;
6342  ContextID: LongWord;
6343  NewShadowType: TGtkShadowType;
6344  NewJustification: TGtkJustification;
6345  {$ifndef gtk1}
6346  xalign, yalign: gfloat;
6347  {$endif}
6348begin
6349  //DebugLn('UpdateStatusBarPanel ',DbgS(StatusBar),' Index=',dbgs(Index));
6350  AStatusBar := StatusBar as TStatusBar;
6351
6352  CurPanel := nil;
6353  if (not AStatusBar.SimplePanel) and (AStatusBar.Panels.Count > Index) then
6354    CurPanel := AStatusBar.Panels[Index];
6355  //DebugLn('Panel ',Index,' ',GetWidgetClassName(StatusPanelWidget),
6356  //  ' frame=',GetWidgetClassName(PGTKStatusBar(StatusPanelWidget)^.frame),
6357  //  ' thelabel=',GetWidgetClassName(PGTKStatusBar(StatusPanelWidget)^.thelabel),
6358  // '');
6359  FrameWidget := PGTKStatusBar(StatusPanelWidget)^.frame;
6360  LabelWidget := PGtkLabel(
6361    {$ifndef gtk1}
6362      PGTKStatusBar(StatusPanelWidget)^._label
6363    {$else}
6364      PGTKStatusBar(StatusPanelWidget)^.thelabel
6365    {$endif});
6366
6367  // Text
6368  if AStatusBar.SimplePanel then
6369    PanelText := AStatusBar.SimpleText
6370  else
6371  if CurPanel <> nil then
6372    PanelText := CurPanel.Text
6373  else
6374    PanelText := '';
6375
6376  ContextID := gtk_statusbar_get_context_id(PGTKStatusBar(StatusPanelWidget),
6377                                            'state');
6378  //DebugLn('  PanelText="',PanelText,'"');
6379  if PanelText <> '' then
6380    gtk_statusbar_push(PGTKStatusBar(StatusPanelWidget), ContextID, PGChar(PanelText))
6381  else
6382    gtk_statusbar_push(PGTKStatusBar(StatusPanelWidget), ContextID, '');
6383
6384
6385  if CurPanel <> nil then
6386  begin
6387    //DebugLn('  Alignment="',ord(CurPanel.Alignment),'"');
6388    // Alignment
6389    NewJustification := aGtkJustification[CurPanel.Alignment];
6390    if GTK_IS_LABEL(LabelWidget) then
6391    begin
6392      {$ifndef gtk1}
6393        if GTK_IS_MISC(LabelWidget) then
6394        begin
6395        {gtk_label_set_justify() has no effect on labels containing
6396         only a single line !}
6397        gtk_misc_get_alignment(GTK_MISC(LabelWidget), @xalign, @yalign);
6398        xalign := AlignToGtkAlign(CurPanel.Alignment);
6399        gtk_misc_set_alignment(GTK_MISC(LabelWidget), xalign, yalign);
6400        end else
6401          gtk_label_set_justify(LabelWidget, NewJustification);
6402      {$else}
6403      gtk_label_set_justify(LabelWidget, NewJustification);
6404      {$endif}
6405    end;
6406
6407    // Bevel
6408
6409    // Paul: this call will not modify frame on gtk2. GtkStatusBar resets frame
6410    // shadow on every size request. I have tried to modify rcStyle and tried to
6411    // hook property change event. Both ways are 1) not valid 2) does not give me
6412    // any result.
6413    // As a possible solution we can subclass PGtkStatusBar but if gtk developers
6414    // decided that stausbar should work so whether we need to override that?
6415    NewShadowType := aGtkShadowFromBevel[CurPanel.Bevel];
6416    if GTK_IS_FRAME(FrameWidget) then
6417      gtk_frame_set_shadow_type(PGtkFrame(FrameWidget), NewShadowType);
6418
6419    // Width
6420    //DebugLn('  CurPanel.Width="',CurPanel.Width,'"');
6421    gtk_widget_set_usize(StatusPanelWidget, CurPanel.Width,
6422      StatusPanelWidget^.allocation.height);
6423    {$IFDEF GTK2}
6424    gtk_object_set_data(PGtkObject(StatusPanelWidget),'lcl_statusbar_id',
6425      @AStatusBar.Panels[Index].ID);
6426    g_signal_connect_after(StatusPanelWidget, 'expose-event',
6427      TGtkSignalFunc(@gtk2PaintStatusBarWidget), AStatusBar);
6428    {$ENDIF}
6429  end;
6430end;
6431
6432function gtkListGetSelectionMode(list: PGtkList): TGtkSelectionMode; cdecl;
6433begin
6434  Result:=TGtkSelectionMode(
6435       (list^.flag0 and bm_TGtkList_selection_mode) shr bp_TGtkList_selection_mode);
6436end;
6437
6438{------------------------------------------------------------------------------
6439  SaveSizeNotification
6440  Params: Widget: PGtkWidget  A widget that is the handle of a lcl control.
6441
6442  When the gtk sends a size signal, it is not send directly to the LCL. All gtk
6443  size/move messages are collected and only the last one for each widget is sent
6444  to the LCL.
6445  This is neccessary, because the gtk sends size messages several times and
6446  it replays resizes. Since the LCL reacts to every size notification and
6447  resizes child controls, this results in a perpetuum mobile.
6448 ------------------------------------------------------------------------------}
6449procedure SaveSizeNotification(Widget: PGtkWidget);
6450{$IFDEF VerboseSizeMsg}
6451var
6452  LCLControl: TWinControl;
6453{$ENDIF}
6454begin
6455  {$IFDEF VerboseSizeMsg}
6456  DbgOut('SaveSizeNotification Widget=',DbgS(Widget));
6457  LCLControl:=TWinControl(GetLCLObject(Widget));
6458  if (LCLControl<>nil) then begin
6459    if LCLControl is TWinControl then
6460      DebugLn(' ',LCLControl.Name,':',LCLControl.ClassName)
6461    else
6462      DebugLn(' ERROR: ',LCLControl.ClassName);
6463  end else begin
6464    DebugLn(' ERROR: LCLControl=nil');
6465  end;
6466  {$ENDIF}
6467  if not FWidgetsResized.Contains(Widget) then
6468    FWidgetsResized.Add(Widget);
6469end;
6470
6471{------------------------------------------------------------------------------
6472  SaveClientSizeNotification
6473  Params: FixWidget: PGtkWidget  A widget that is the fixed widget
6474                                 of a lcl control.
6475
6476  When the gtk sends a size signal, it is not sent directly to the LCL. All gtk
6477  size/move messages are collected and only the last one for each widget is sent
6478  to the LCL.
6479  This is neccessary, because the gtk sends size messages several times and
6480  it replays resizes. Since the LCL reacts to every size notification and
6481  resizes child controls, this results in a perpetuum mobile.
6482 ------------------------------------------------------------------------------}
6483procedure SaveClientSizeNotification(FixWidget: PGtkWidget);
6484{$IFDEF VerboseSizeMsg}
6485var
6486  LCLControl: TWinControl;
6487  MainWidget: PGtkWidget;
6488{$ENDIF}
6489begin
6490  {$IFDEF VerboseSizeMsg}
6491  MainWidget:=GetMainWidget(FixWidget);
6492  //write('SaveClientSizeNotification',
6493  //  ' FixWidget=',DbgS(FixWidget),
6494  //  ' MainWIdget=',DbgS(MainWidget));
6495  LCLControl:=TWinControl(GetLCLObject(MainWidget));
6496  if (LCLControl<>nil) then begin
6497    if LCLControl is TWinControl then begin
6498      //DebugLn('SaveClientSizeNotification ',LCLControl.Name,':',LCLControl.ClassName,
6499      //  ' FixWidget=',DbgS(FixWidget),
6500      //  ' MainWidget=',DbgS(MainWidget));
6501    end else begin
6502      DbgOut('ERROR: SaveClientSizeNotification ',
6503        ' LCLControl=',LCLControl.ClassName,
6504        ' FixWidget=',DbgS(FixWidget),
6505        ' MainWidget=',DbgS(MainWidget));
6506      RaiseGDBException('SaveClientSizeNotification');
6507    end;
6508  end else begin
6509    DbgOut('ERROR: SaveClientSizeNotification LCLControl=nil',
6510      ' FixWidget=',DbgS(FixWidget),
6511      ' MainWIdget=',DbgS(MainWidget));
6512    RaiseGDBException('SaveClientSizeNotification');
6513  end;
6514  {$ENDIF}
6515  if not FFixWidgetsResized.Contains(FixWidget) then
6516    FFixWidgetsResized.Add(FixWidget);
6517end;
6518
6519{-------------------------------------------------------------------------------
6520  CreateTopologicalSortedWidgets
6521  Params: HashArray: TDynHashArray  of PGtkWidget
6522
6523  Creates a topologically sorted TFPList of PGtkWidget.
6524-------------------------------------------------------------------------------}
6525function CreateTopologicalSortedWidgets(HashArray: TDynHashArray): TFPList;
6526type
6527  PTopologicalEntry = ^TTopologicalEntry;
6528  TTopologicalEntry = record
6529      Widget: PGtkWidget;
6530      ParentLevel: integer;
6531    end;
6532
6533  function GetParentLevel(AControl: TControl): integer;
6534  // nil has lvl -1
6535  // a control without parent has lvl 0
6536  begin
6537    Result:=-1;
6538    while AControl<>nil do begin
6539      inc(Result);
6540      AControl:=AControl.Parent;
6541    end;
6542  end;
6543
6544var
6545  TopologicalList: PTopologicalEntry;
6546  HashItem: PDynHashArrayItem;
6547  i, Lvl, MaxLevel: integer;
6548  LCLControl: TControl;
6549  LevelCounts: PInteger;
6550begin
6551  Result:=TFPList.Create;
6552  if HashArray.Count=0 then exit;
6553
6554  // put all widgets into an array and calculate their parent levels
6555  GetMem(TopologicalList,SizeOf(TTopologicalEntry)*HashArray.Count);
6556  HashItem:=HashArray.FirstHashItem;
6557  i:=0;
6558  MaxLevel:=0;
6559  //DebugLn('CreateTopologicalSortedWidgets HashArray.Count=',HashArray.Count);
6560  while HashItem<>nil do begin
6561    TopologicalList[i].Widget:=HashItem^.Item;
6562    //DebugLn('CreateTopologicalSortedWidgets i=',i,' Widget=',DbgS(TopologicalList[i].Widget));
6563    LCLControl:=TControl(GetLCLObject(TopologicalList[i].Widget));
6564    if (LCLControl=nil) or (not (LCLControl is TControl)) then
6565      RaiseGDBException('CreateTopologicalSortedWidgets: '
6566                             +'Widget without LCL control');
6567    Lvl:=GetParentLevel(LCLControl);
6568    TopologicalList[i].ParentLevel:=Lvl;
6569    if MaxLevel<Lvl then
6570      MaxLevel:=Lvl;
6571    //DebugLn('CreateTopologicalSortedWidgets i=',i,' Lvl=',Lvl,' MaxLvl=',MaxLevel,' LCLControl=',LCLControl.Name,':',LCLControl.ClassName);
6572    inc(i);
6573    HashItem:=HashItem^.Next;
6574  end;
6575  inc(MaxLevel);
6576
6577  // bucket sort the widgets
6578
6579  // count each number of levels (= bucketsizes)
6580  GetMem(LevelCounts,SizeOf(Integer)*MaxLevel);
6581  FillChar(LevelCounts^,SizeOf(Integer)*MaxLevel,0);
6582  for i:=0 to HashArray.Count-1 do
6583    inc(LevelCounts[TopologicalList[i].ParentLevel]);
6584
6585  // calculate bucketends
6586  for i:=1 to MaxLevel-1 do
6587    inc(LevelCounts[i],LevelCounts[i-1]);
6588
6589  // bucket sort the widgets in Result
6590  Result.Count:=HashArray.Count;
6591  for i:=0 to HashArray.Count-1 do
6592    Result[i]:=nil;
6593  for i:=0 to HashArray.Count-1 do begin
6594    Lvl:=TopologicalList[i].ParentLevel;
6595    dec(LevelCounts[Lvl]);
6596    //DebugLn('CreateTopologicalSortedWidgets bucket sort i=',i,' Lvl=',Lvl,' LevelCounts[Lvl]=',LevelCounts[Lvl],
6597    //  ' Widget=',DbgS(TopologicalList[i].Widget));
6598    Result[LevelCounts[Lvl]]:=TopologicalList[i].Widget;
6599  end;
6600
6601  FreeMem(LevelCounts);
6602  FreeMem(TopologicalList);
6603end;
6604
6605procedure GetGTKDefaultWidgetSize(AWinControl: TWinControl;
6606  var PreferredWidth, PreferredHeight: integer; WithThemeSpace: Boolean);
6607var
6608  Widget: PGtkWidget;
6609  Requisition: TGtkRequisition;
6610begin
6611  Widget := PGtkWidget(AWinControl.Handle);
6612  // set size to default
6613  //DebugLn(['GetGTKDefaultWidgetSize ',GetWidgetDebugReport(Widget)]);
6614  {$IFDEF GTK1}
6615  gtk_widget_set_usize(Widget, -1, -1); // deprecated in gtk2
6616  {$ELSE}
6617  gtk_widget_set_size_request(Widget, -1, -1);
6618  {$ENDIF}
6619  // ask default size
6620  gtk_widget_size_request(Widget,@Requisition);
6621  PreferredWidth:=Requisition.width;
6622  PreferredHeight:=Requisition.height;
6623  if WithThemeSpace then begin
6624    {$IFDEF Gtk1}
6625    //DebugLn(['GetGTKDefaultWidgetSize WithThemeSpace ',DbgSName(AWinControl),' ',GtkWidgetIsA(Widget,GTK_BUTTON_TYPE),' ',GetWidgetDebugReport(Widget),' ',2*gtk_widget_get_ythickness(Widget)]);
6626    if gtk_class_get_type(gtk_object_get_class(Widget))=GTK_BUTTON_TYPE then
6627      inc(PreferredHeight,2*gtk_widget_get_ythickness(Widget))
6628    else if not GtkWidgetIsA(Widget,GTK_ENTRY_TYPE) then
6629      dec(PreferredHeight,2*gtk_widget_get_ythickness(Widget));
6630    {$ENDIF}
6631  end else begin
6632    //debugLn('GetGTKDefaultWidgetSize ',DbgSName(AWinControl),' ',dbgs(gtk_widget_get_xthickness(Widget)),' ythickness=',dbgs(gtk_widget_get_ythickness(Widget)));
6633    //debugLn(['GetGTKDefaultWidgetSize ',GetWidgetDebugReport(Widget)]);
6634    //dec(PreferredWidth,gtk_widget_get_xthickness(Widget));
6635    {$IFDEF Gtk1}
6636    //if not GtkWidgetIsA(Widget,GTK_ENTRY_TYPE) then
6637    //  dec(PreferredHeight,2*gtk_widget_get_ythickness(Widget));
6638    {$ELSE}
6639    //if gtk_class_get_type(gtk_object_get_class(Widget))=GTK_TYPE_BUTTON then
6640    //  dec(PreferredHeight,2*gtk_widget_get_ythickness(Widget));
6641    {$ENDIF}
6642  end;
6643  {DebugLn(['GetGTKDefaultWidgetSize Allocation=',Widget^.allocation.x,',',Widget^.allocation.y,',',Widget^.allocation.width,',',Widget^.allocation.height,
6644   ' requisition=',Widget^.requisition.width,',',Widget^.requisition.height,
6645   ' PreferredWidth=',PreferredWidth,' PreferredHeight=',PreferredHeight,
6646   ' WithThemeSpace=',WithThemeSpace]);}
6647  // set new size
6648  {$IFDEF GTK1}
6649  gtk_widget_set_usize(Widget, AWinControl.Width, AWinControl.Height);
6650  {$ELSE}
6651  gtk_widget_set_size_request(Widget, AWinControl.Width, AWinControl.Height);
6652  {$ENDIF}
6653  //debugln('GetGTKDefaultSize PreferredWidth=',dbgs(PreferredWidth),' PreferredHeight=',dbgs(PreferredHeight));
6654end;
6655
6656procedure SendSizeNotificationToLCL(aWidget: PGtkWidget);
6657var
6658  LCLControl: TWinControl;
6659  LCLLeft, LCLTop, LCLWidth, LCLHeight: integer;
6660  GtkLeft, GtkTop, GtkWidth, GtkHeight: integer;
6661  TopLeftChanged, WidthHeightChanged, IsTopLevelWidget: boolean;
6662  MessageDelivered: boolean;
6663  SizeMsg: TLMSize;
6664  MoveMsg: TLMMove;
6665  PosMsg : TLMWindowPosChanged;
6666  MainWidget: PGtkWidget;
6667  FixedWidget: PGtkWidget;
6668
6669  procedure UpdateLCLPos;
6670  begin
6671    LCLLeft:=LCLControl.Left;
6672    LCLTop:=LCLControl.Top;
6673    TopLeftChanged:=(LCLLeft<>GtkLeft) or (LCLTop<>GtkTop);
6674  end;
6675
6676  procedure UpdateLCLSize;
6677  begin
6678    LCLWidth:=LCLControl.Width;
6679    LCLHeight:=LCLControl.Height;
6680    WidthHeightChanged:=(LCLWidth<>GtkWidth) or (LCLHeight<>GtkHeight);
6681    if LCLControl.ClientRectNeedsInterfaceUpdate then begin
6682      WidthHeightChanged:=true;
6683      //DebugLn(['UpdateLCLSize InvalidateClientRectCache ',DbgSName(LCLControl)]);
6684      LCLControl.InvalidateClientRectCache(false);
6685    end;
6686  end;
6687
6688begin
6689  LCLControl:=TWinControl(GetLCLObject(aWidget));
6690  if LCLControl=nil then exit;
6691  {$IFDEF VerboseSizeMsg}
6692  DebugLn('SendSizeNotificationToLCL checking ... ',DbgSName(LCLControl),' Widget=',WidgetFlagsToString(aWidget));
6693  {$ENDIF}
6694  MainWidget:=PGtkWidget(LCLControl.Handle);
6695  FixedWidget:=PGtkWidget(GetFixedWidget(MainWidget));
6696
6697  FWidgetsResized.Remove(MainWidget);
6698  FFixWidgetsResized.Remove(FixedWidget);
6699
6700  {$IF defined(Gtk1)}
6701  if not GTK_WIDGET_REALIZED(aWidget) then begin
6702    // the widget is not yet realized, so this GTK resize was not a user change.
6703    // => ignore
6704    {$IFDEF VerboseSizeMsg}
6705    LCLControl:=TWinControl(GetLCLObject(aWidget));
6706    DebugLn('SendSizeNotificationToLCL ',DbgSName(LCLControl),' aWidget=',WidgetFlagsToString(aWidget),' Ignored, because not realized ');
6707    {$ENDIF}
6708    exit;
6709  end;
6710  {$ENDIF}
6711
6712  GetWidgetRelativePosition(MainWidget,GtkLeft,GtkTop);
6713
6714  {$ifdef gtk2}
6715  gtk_widget_get_size_request(MainWidget, @GtkWidth, @GtkHeight);
6716
6717  if GtkWidth < 0 then
6718    GtkWidth:=MainWidget^.Allocation.Width
6719  else
6720    MainWidget^.Allocation.Width:=GtkWidth;
6721  if GtkHeight < 0 then
6722    GtkHeight:=MainWidget^.Allocation.Height
6723  else
6724    MainWidget^.Allocation.Height:=GtkHeight;
6725  //DebugLn(['SendSizeNotificationToLCL ',DbgSName(LCLControl),' gtk=',GtkLeft,',',GtkTop,',',GtkWidth,'x',GtkHeight,' Allocation=',MainWidget^.Allocation.Width,'x',MainWidget^.Allocation.Height]);
6726  {$else}
6727  GtkWidth:=MainWidget^.Allocation.Width;
6728  GtkHeight:=MainWidget^.Allocation.Height;
6729  {$endif}
6730
6731  if GtkWidth<0 then GtkWidth:=0;
6732  if GtkHeight<0 then GtkHeight:=0;
6733
6734  IsTopLevelWidget:=(LCLControl is TCustomForm) and (LCLControl.Parent=nil);
6735  if IsTopLevelWidget then begin
6736    if not GTK_WIDGET_VISIBLE(MainWidget) then begin
6737      // size/move messages of invisible windows are not reliable
6738      // -> ignore
6739      exit;
6740    end;
6741    if (GtkWidth=1) and (GtkHeight=1) then begin
6742      // this is default size of the gtk. Ignore.
6743      exit;
6744    end;
6745    //DebugLn(['SendSizeNotificationToLCL FORM ',GetWidgetDebugReport(MainWidget)]);
6746
6747    {$IFDEF VerboseFormPositioning}
6748    DebugLn(['VFP SendSizeNotificationToLCL ',DbgSName(LCLControl),' ',
6749      GtkLeft,',',GtkTop,',',GtkWidth,'x',GtkHeight,' ',GetWidgetDebugReport(MainWidget)]);
6750    {$ENDIF}
6751  end;
6752
6753  UpdateLCLPos;
6754  UpdateLCLSize;
6755
6756  // first send a LM_WINDOWPOSCHANGED message
6757  if TopLeftChanged or WidthHeightChanged then begin
6758    {$IFDEF VerboseSizeMsg}
6759    DebugLn('SendSizeNotificationToLCL ',DbgSName(LCLControl),
6760      ' GTK=',dbgs(GtkLeft)+','+dbgs(GtkTop)+','+dbgs(GtkWidth)+'x'+dbgs(GtkHeight),
6761      ' LCL=',dbgs(LCLLeft)+','+dbgs(LCLTop)+','+dbgs(LCLWidth)+'x'+dbgs(LCLHeight)
6762      );
6763    {$ENDIF}
6764    PosMsg.Msg := LM_WINDOWPOSCHANGED; //LM_SIZEALLOCATE;
6765    PosMsg.Result := 0;
6766    New(PosMsg.WindowPos);
6767    try
6768      with PosMsg.WindowPos^ do begin
6769        hWndInsertAfter := 0;
6770        x := GtkLeft;
6771        y := GtkTop;
6772        cx := GtkWidth;
6773        cy := GtkHeight;
6774        flags:=0;
6775        // flags := SWP_SourceIsInterface;
6776      end;
6777      MessageDelivered := DeliverMessage(LCLControl, PosMsg) = 0;
6778    finally
6779      Dispose(PosMsg.WindowPos);
6780    end;
6781    if (not MessageDelivered) then exit;
6782    if FWidgetsWithResizeRequest.Contains(MainWidget) then exit;
6783    UpdateLCLPos;
6784    UpdateLCLSize;
6785  end;
6786
6787  // then send a LM_SIZE message
6788  if WidthHeightChanged then begin
6789    {$IFDEF VerboseSizeMsg}
6790    DebugLn('Send LM_SIZE To LCL ',LCLControl.Name,':',LCLControl.ClassName);
6791    {$ENDIF}
6792    with SizeMsg do
6793    begin
6794      Result := 0;
6795      Msg := LM_SIZE;
6796      {$IFDEF GTK1}
6797      if GDK_WINDOW_GET_MAXIMIZED(PGdkWindowPrivate(MainWidget^.window)) then
6798        SizeType := SIZEFULLSCREEN
6799      else
6800        SizeType := SIZENORMAL;
6801      {$ELSE}
6802      if LCLControl is TCustomForm then begin
6803        // if the LCL gets an event without a State it resets it to SIZENORMAL
6804        // so we send it the state it already is
6805        case TCustomForm(LCLControl).WindowState of
6806          wsNormal: SizeType := SIZENORMAL;
6807          wsMinimized: SizeType := SIZEICONIC;
6808          wsMaximized: SizeType := SIZEFULLSCREEN;
6809        end;
6810      end
6811      else
6812        SizeType := 0;
6813      {$ENDIF}
6814      SizeType := SizeType or Size_SourceIsInterface;
6815      Width := SmallInt(GtkWidth);
6816      Height := SmallInt(GtkHeight);
6817    end;
6818    MessageDelivered := (DeliverMessage(LCLControl, SizeMsg) = 0);
6819    if not MessageDelivered then exit;
6820    if FWidgetsWithResizeRequest.Contains(MainWidget) then exit;
6821    UpdateLCLPos;
6822  end;
6823
6824  // then send a LM_MOVE message
6825  if TopLeftChanged then begin
6826    {$IFDEF VerboseSizeMsg}
6827    DebugLn('Send LM_MOVE To LCL ',LCLControl.Name,':',LCLControl.ClassName);
6828    {$ENDIF}
6829    with MoveMsg do
6830    begin
6831      Result := 0;
6832      Msg := LM_MOVE;
6833      MoveType := Move_SourceIsInterface;
6834      XPos := SmallInt(GtkLeft);
6835      YPos := SmallInt(GtkTop);
6836    end;
6837    MessageDelivered := (DeliverMessage(LCLControl,  MoveMsg) = 0);
6838    if not MessageDelivered then exit;
6839  end;
6840
6841  {$ifndef gtk1}
6842  if GtkWidgetIsA(aWidget, GTKAPIWidget_Type) and
6843     not (wwiNoEraseBkgnd in GetWidgetInfo(aWidget)^.Flags) then
6844    gtk_widget_queue_draw(aWidget);
6845  {$endif}
6846end;
6847
6848procedure SendCachedGtkResizeNotifications;
6849{ This proc sends all cached size messages from the gtk to lcl but in an
6850  optimized order.
6851  When sending the LCL a size/move/windowposchanged messages the LCL will
6852  automatically realign all child controls. This realigning is based on the
6853  clientrect.
6854  Therefore, before a size message is sent to the lcl, all clientrect must be
6855  updated.
6856  If a size message results in resizing a widget that was also resized, then
6857  the message for the dependent widget is not sent to the lcl, because the lcl
6858  resize was after the gtk resize.
6859}
6860var
6861  FixWidget, MainWidget: PGtkWidget;
6862  LCLControl: TWinControl;
6863  List: TFPList;
6864  i: integer;
6865
6866  procedure RaiseInvalidLCLControl;
6867  begin
6868    RaiseGDBException(Format('SendCachedGtkResizeNotifications FixWidget=%p MainWidget=%p LCLControl=%p',
6869                  [FixWidget, MainWidget, Pointer(LCLControl)]));
6870  end;
6871
6872begin
6873  if (FWidgetsResized.Count=0) and (FFixWidgetsResized.Count=0) then exit;
6874
6875  List:=TFPList.Create;
6876
6877  { if any fixed widget was resized then a client area of a LCL control was
6878    resized
6879    -> invalidate client rectangles
6880  }
6881  {$IFDEF VerboseSizeMsg}
6882  DebugLn('HHH1 SendCachedGtkClientResizeNotifications Invalidating ClientRects ... '
6883  ,' FixSizeMsgCount=',dbgs(FFixWidgetsResized.Count));
6884  {$ENDIF}
6885  FFixWidgetsResized.AssignTo(List);
6886  for i:=0 to List.Count-1 do begin
6887    FixWidget:=List[i];
6888    MainWidget:=GetMainWidget(FixWidget);
6889    LCLControl:=TWinControl(GetLCLObject(MainWidget));
6890    if (LCLControl=nil) or (not (LCLControl is TWinControl)) then
6891      RaiseInvalidLCLControl;
6892    LCLControl.InvalidateClientRectCache(false);
6893  end;
6894
6895  { if any main widget (= not fixed widget) was resized
6896    then a LCL control was resized
6897    -> send WMSize, WMMove, and WMWindowPosChanged messages
6898  }
6899  {$IFDEF VerboseSizeMsg}
6900  if FWidgetsResized.First<>nil then
6901    DebugLn('HHH2 SendCachedGtkClientResizeNotifications SizeMsgCount=',dbgs(FWidgetsResized.Count));
6902  {$ENDIF}
6903  repeat
6904    MainWidget:=FWidgetsResized.First;
6905    if MainWidget<>nil then begin
6906      FWidgetsResized.Remove(MainWidget);
6907      if not FWidgetsWithResizeRequest.Contains(MainWidget) then begin
6908        SendSizeNotificationToLCL(MainWidget);
6909      end;
6910    end else break;
6911  until Application.Terminated;
6912
6913  { if any client area was resized, which MainWidget Size was already in sync
6914    with the LCL, no message was sent. So, tell each changed client area to
6915    check its size.
6916  }
6917  {$IFDEF VerboseSizeMsg}
6918  if FFixWidgetsResized.First<>nil then
6919    DebugLn('HHH3 SendCachedGtkClientResizeNotifications Updating ClientRects ...');
6920  {$ENDIF}
6921  repeat
6922    FixWidget:=FFixWidgetsResized.First;
6923    if FixWidget<>nil then begin
6924      FFixWidgetsResized.Remove(FixWidget);
6925      MainWidget:=GetMainWidget(FixWidget);
6926      LCLControl:=TWinControl(GetLCLObject(MainWidget));
6927      LCLControl.DoAdjustClientRectChange(False);
6928    end else begin
6929      break;
6930    end;
6931  until Application.Terminated;
6932
6933  List.Free;
6934  {$IFDEF VerboseSizeMsg}
6935  DebugLn('HHH4 SendCachedGtkClientResizeNotifications  completed.');
6936  {$ENDIF}
6937end;
6938
6939procedure ResizeHandle(LCLControl: TWinControl);
6940var
6941  Widget: PGtkWidget;
6942  Later: Boolean;
6943  {$IFDEF Gtk2}
6944  IsTopLevelWidget: Boolean;
6945  {$ENDIF}
6946begin
6947  Widget := PGtkWidget(LCLControl.Handle);
6948  if not WidgetSizeIsEditable(Widget) then
6949    Exit;
6950  Later := true;
6951  {$IFDEF Gtk2}
6952  // add resize request immediately
6953  IsTopLevelWidget:= (LCLControl is TCustomForm) and
6954                     (LCLControl.Parent = nil) and
6955                     (LCLControl.ParentWindow = 0);
6956  if not IsTopLevelWidget then
6957  begin
6958    SetWidgetSizeAndPosition(LCLControl);
6959    Later := false;
6960  end;
6961  {$ENDIF}
6962  if Later then
6963    SetResizeRequest(Widget);
6964end;
6965
6966procedure SetWidgetSizeAndPosition(LCLControl: TWinControl);
6967var
6968  Requisition: TGtkRequisition;
6969  FixedWidget: PGtkWidget;
6970  {$IFDEF Gtk2}
6971  allocation: TGtkAllocation;
6972  {$ENDIF}
6973  LCLLeft: LongInt;
6974  LCLTop: LongInt;
6975  LCLWidth: LongInt;
6976  LCLHeight: LongInt;
6977  Widget: PGtkWidget;
6978  ParentWidget: PGtkWidget;
6979  ParentFixed: PGtkWidget;
6980  WinWidgetInfo: PWidgetInfo;
6981  {$IFDEF VerboseSizeMsg}
6982  LCLObject: TObject;
6983  {$ENDIF}
6984
6985  procedure WriteBigWarning;
6986  begin
6987    DebugLn('WARNING: SetWidgetSizeAndPosition: resizing BIG ',
6988      ' Control=',LCLControl.Name,':',LCLControl.ClassName,
6989      ' NewSize=',dbgs(LCLWidth),',',dbgs(LCLHeight));
6990    //RaiseException('');
6991  end;
6992
6993  procedure WriteWarningParentWidgetNotFound;
6994  begin
6995    DebugLn('WARNING: SetWidgetSizeAndPosition - '
6996            ,'Parent''s Fixed Widget not found');
6997    DebugLn('  Control=',LCLControl.Name,':',LCLControl.ClassName,
6998      ' Parent=',LCLControl.Parent.Name,':',LCLControl.Parent.ClassName,
6999      ' ParentWidget=',DbgS(ParentWidget),
7000      '');
7001  end;
7002
7003begin
7004  {$IFDEF VerboseSizeMsg}
7005  DebugLn(['SetWidgetSizeAndPosition ',DbgSName(LCLControl)]);
7006  {$ENDIF}
7007  Widget:=PGtkWidget(LCLControl.Handle);
7008
7009  LCLLeft := LCLControl.Left;
7010  LCLTop := LCLControl.Top;
7011
7012  // move widget on the fixed widget of parent control
7013  if ((LCLControl.Parent <> nil) and (LCLControl.Parent.HandleAllocated)) or
7014     ((LCLControl.Parent = nil) and (LCLControl.ParentWindow <> 0)) then
7015  begin
7016    if LCLControl.Parent <> nil then
7017      ParentWidget := PGtkWidget(LCLControl.Parent.Handle)
7018    else
7019      ParentWidget := PGtkWidget(LCLControl.ParentWindow);
7020    ParentFixed := GetFixedWidget(ParentWidget);
7021    if GtkWidgetIsA(ParentFixed,GTK_FIXED_GET_TYPE) or
7022       GtkWidgetIsA(ParentFixed,GTK_LAYOUT_GET_TYPE) then
7023    begin
7024      //DebugLn(['SetWidgetSizeAndPosition ',DbgSName(LCLControl),' Widget=[',GetWidgetDebugReport(Widget),'] ParentFixed=[',GetWidgetDebugReport(ParentFixed),']']);
7025      FixedMoveControl(ParentFixed, Widget, LCLLeft, LCLTop);
7026    end
7027    else
7028    begin
7029      WinWidgetInfo := GetWidgetInfo(Widget, False);
7030      if (WinWidgetInfo = nil) or (not (wwiNotOnParentsClientArea in WinWidgetInfo^.Flags)) then
7031        WriteWarningParentWidgetNotFound;
7032    end;
7033  end;
7034
7035  // resize widget
7036  LCLWidth := LCLControl.Width;
7037  if LCLWidth <= 0 then
7038    LCLWidth := 1;
7039  LCLHeight := LCLControl.Height;
7040  if LCLHeight <= 0 then
7041    LCLHeight := 1;
7042  if (LCLWidth > 10000) or (LCLHeight > 10000) then
7043  begin
7044    WriteBigWarning;
7045    if LCLWidth > 10000 then
7046      LCLWidth := 10000;
7047    if LCLHeight > 10000 then
7048      LCLHeight := 10000;
7049  end;
7050
7051  {$IFDEF VerboseSizeMsg}
7052  LCLObject:=GetNearestLCLObject(Widget);
7053  DbgOut('TGtkWidgetSet.SetWidgetSizeAndPosition Widget='+DbgS(Widget)+WidgetFlagsToString(Widget)+
7054    ' New='+dbgs(LCLWidth)+','+dbgs(LCLHeight));
7055  if (LCLObject<>nil) and (LCLObject is TControl) then begin
7056    with TControl(LCLObject) do
7057      DebugLn(' LCL=',Name,':',ClassName,' ',dbgs(Left),',',dbgs(Top),',',dbgs(Width),',',dbgs(Height));
7058  end else begin
7059    DebugLn(' LCL=',DbgS(LCLObject));
7060  end;
7061  {$ENDIF}
7062
7063  if GtkWidgetIsA(Widget,GTK_TYPE_SCROLLBAR) then
7064  begin
7065    // the width of a scrollbar is fixed and depends only on the theme
7066    gtk_widget_size_request(widget, @Requisition);
7067    if GtkWidgetIsA(Widget, GTK_TYPE_HSCROLLBAR) then
7068    begin
7069      LCLHeight:=Requisition.height;
7070    end else begin
7071      LCLWidth:=Requisition.width;
7072    end;
7073    //DebugLn('TGtkWidgetSet.SetWidgetSizeAndPosition A ',LCLwidth,',',LCLheight);
7074  end;
7075
7076  gtk_widget_set_usize(Widget, LCLWidth, LCLHeight);
7077  //DebugLn(['TGtkWidgetSet.SetWidgetSizeAndPosition ',GetWidgetDebugReport(Widget),' LCLWidth=',LCLWidth,' LCLHeight=',LCLHeight]);
7078
7079  {$IFDEF Gtk1}
7080  if GtkWidgetIsA(Widget, GTK_TYPE_COMBO) then
7081  begin
7082    // the combobox has an entry, which height is not resized
7083    // automatically. Do it manually.
7084    gtk_widget_set_usize(PGtkCombo(Widget)^.entry,
7085      PGtkCombo(Widget)^.entry^.allocation.width, LCLHeight);
7086  end;
7087  {$ENDIF}
7088
7089  if GtkWidgetIsA(Widget,gtk_toolbar_get_type) then begin
7090    FixedWidget:=GetFixedWidget(Widget);
7091    if (FixedWidget<>nil) and (FixedWidget<>Widget) then begin
7092      //DebugLn('WARNING: ToDo TGtkWidgetSet.SetWidgetSizeAndPosition for TToolBar ',LCLWidth,',',LCLHeight);
7093      gtk_widget_set_usize(FixedWidget,LCLWidth,LCLHeight);
7094    end;
7095  end;
7096
7097  {$IFDEF Gtk2}
7098  if (Widget^.parent<>nil)
7099  and GtkWidgetIsA(Widget^.parent,GTK_TYPE_FIXED)
7100  and GTK_WIDGET_NO_WINDOW(Widget^.parent)
7101  then begin
7102    inc(LCLLeft, Widget^.parent^.allocation.x);
7103    inc(LCLTop, Widget^.parent^.allocation.y);
7104  end;
7105
7106  // commit size and position
7107  allocation:=Widget^.allocation;
7108  allocation.x:=LCLLeft;
7109  allocation.y:=LCLTop;
7110  allocation.width:=LCLWidth;
7111  allocation.height:=LCLHeight;
7112  //DebugLn(['SetWidgetSizeAndPosition ',DbgSName(LCLControl),' LCL=',dbgs(LCLControl.BoundsRect),' allocation=',dbgs(allocation),' ',GetWidgetDebugReport(Widget)]);
7113  gtk_widget_size_allocate(Widget,@allocation);// Beware: this triggers callbacks
7114  {$ENDIF}
7115end;
7116
7117{------------------------------------------------------------------------------
7118  Method: SetWindowSizeAndPosition
7119  Params:  Widget: PGtkWidget; AWinControl: TWinControl
7120  Returns: Nothing
7121
7122  Set the size and position of a top level window.
7123 ------------------------------------------------------------------------------}
7124procedure SetWindowSizeAndPosition(Window: PGtkWindow;
7125  AWinControl: TWinControl);
7126var
7127  Width, Height: integer;
7128  {$IFDEF Gtk2}
7129  allocation: TGtkAllocation;
7130  {$ENDIF}
7131  //Info: PGtkWindowGeometryInfo;
7132begin
7133  Width:=AWinControl.Width;
7134  // 0 and negative values have a special meaning, so don't use them
7135  if Width<=0 then Width:=1;
7136  Height:=AWinControl.Height;
7137  if Height<=0 then Height:=1;
7138
7139  {$IFDEF VerboseSizeMsg}
7140  DebugLn(['TGtkWidgetSet.SetWindowSizeAndPosition START ',DbgSName(AWinControl),' ',AWinControl.Visible,' Old=',PGtkWidget(Window)^.allocation.Width,',',PGtkWidget(Window)^.allocation.Width,' New=',Width,',',Height]);
7141  {$ENDIF}
7142  // set geometry default size
7143  //Info:=gtk_window_get_geometry_info(Window, TRUE);
7144  //if (Info^.default_width<>Width) or (Info^.default_height<>Height) then
7145    gtk_window_set_default_size(Window, Width, Height);
7146
7147  {$IFDEF Gtk2}
7148  // resize
7149  gtk_window_resize(Window, Width, Height);
7150  // reposition
7151  gtk_window_move(Window, AWinControl.Left, AWinControl.Top);
7152  // force early resize
7153  allocation := PGtkWidget(Window)^.allocation;
7154  allocation.width := Width;
7155  allocation.height := Height;
7156  //DebugLn(['SetWindowSizeAndPosition ',DbgSName(AWinControl),' ',dbgs(allocation)]);
7157  gtk_widget_size_allocate(PGtkWidget(Window), @allocation);// Beware: this triggers callbacks
7158
7159  if (PGtkWidget(Window)^.Window <> nil) then
7160  begin
7161    // resize gdkwindow directly (sometimes the gtk forgets this)
7162    gdk_window_move_resize(PGtkWidget(Window)^.Window,AWinControl.Left,
7163      AWinControl.Top,Width,Height)
7164  end;
7165  {$ELSE}
7166  // resize
7167  if assigned(PGtkWidget(Window)^.Window) then
7168    // widget is realized, resize gdkwindow directly
7169    gdk_window_move_resize(PGtkWidget(Window)^.Window,AWinControl.Left,
7170      AWinControl.Top,Width,Height)
7171  else begin
7172    // widget is not yet realized, force resize needed for shrinking under gtk1
7173    gtk_widget_set_usize(PGtkWidget(Window), -1,-1);
7174  end;
7175  // reposition
7176  gtk_widget_set_usize(PGtkWidget(Window),Width,Height);
7177  gtk_widget_set_uposition(PGtkWidget(Window),AWinControl.Left,AWinControl.Top);
7178  {$ENDIF}
7179
7180  {$IFDEF VerboseSizeMsg}
7181  DebugLn(['SetWindowSizeAndPosition B ',DbgSName(AWinControl),
7182    ' Visible=',AWinControl.Visible,
7183    ' Cur=',PGtkWidget(Window)^.allocation.X,',',PGtkWidget(Window)^.allocation.Y,
7184    ' New=',AWinControl.Left,',',AWinControl.Top,',',Width,'x',Height]);
7185  {$ENDIF}
7186end;
7187
7188{-------------------------------------------------------------------------------
7189  GetWidgetRelativePosition
7190
7191  Returns the Left, Top, relative to the client origin of its parent
7192-------------------------------------------------------------------------------}
7193procedure GetWidgetRelativePosition(aWidget: PGtkWidget; var Left, Top: integer);
7194var
7195  GdkWindow: PGdkWindow;
7196  LCLControl: TWinControl;
7197  GtkLeft, GtkTop: GInt;
7198begin
7199  Left:=aWidget^.allocation.X;
7200  Top:=aWidget^.allocation.Y;
7201  {$IFDEF Gtk2}
7202  if (aWidget^.parent<>nil)
7203  and (not GtkWidgetIsA(aWidget^.parent,GTK_TYPE_FIXED))
7204  and (not GtkWidgetIsA(aWidget^.parent,GTK_TYPE_LAYOUT))
7205  then begin
7206    // widget is not on a normal client area. e.g. TPage
7207    Left:=0;
7208    Top:=0;
7209  end
7210  else
7211  if (aWidget^.parent<>nil)
7212  and GtkWidgetIsA(aWidget^.parent,GTK_TYPE_FIXED)
7213  and GTK_WIDGET_NO_WINDOW(aWidget^.parent)
7214  then begin
7215    // widget on a fixed, but fixed w/o window
7216    Dec(Left, PGtkWidget(aWidget^.parent)^.allocation.x);
7217    Dec(Top, PGtkWidget(aWidget^.parent)^.allocation.y);
7218  end;
7219  {$ENDIF}
7220  if GtkWidgetIsA(aWidget,GTK_TYPE_WINDOW) then begin
7221    GdkWindow:=GetControlWindow(aWidget);
7222    if (GdkWindow<>nil) and (GTK_WIDGET_MAPPED(aWidget)) then begin
7223      // window is mapped = window manager has put the window somewhere
7224      gdk_window_get_root_origin(GdkWindow, @GtkLeft, @GtkTop);
7225      Left := GtkLeft;
7226      Top := GtkTop;
7227    end else begin
7228      // the gtk has not yet put the window to the final position
7229      // => the gtk/gdk position is not reliable
7230      // => use the LCL coords
7231      LCLControl:=GetLCLObject(aWidget) as TWinControl;
7232      Left:=LCLControl.Left;
7233      Top:=LCLControl.Top;
7234    end;
7235    //DebugLn(['TGtkWidgetSet.GetWindowRelativePosition ',GetWidgetDebugReport(aWidget),' Left=',Left,' Top=',Top,' GdkWindow=',GdkWindow<>nil]);
7236  end;
7237  //DebugLn(['TGtkWidgetSet.GetWindowRelativePosition ',GetWidgetDebugReport(aWidget),' Left=',Left,' Top=',Top]);
7238end;
7239
7240{------------------------------------------------------------------------------
7241  UnsetResizeRequest
7242  Params: Widget: PGtkWidget
7243
7244  Unset the mark for the Widget to send a ResizeRequest to the gtk.
7245  LCL size requests for a widget are cached and only the last one is sent. Some
7246  widgets like forms send a resize request immediately. To avoid sending resize
7247  requests multiple times they can unset the mark with this procedure.
7248 ------------------------------------------------------------------------------}
7249procedure UnsetResizeRequest(Widget: PGtkWidget);
7250begin
7251  {$IFDEF VerboseSizeMsg}
7252  if FWidgetsWithResizeRequest.Contains(Widget) then begin
7253    DebugLn(['UnsetResizeRequest ',GetWidgetDebugReport(Widget)]);
7254  end;
7255  {$ENDIF}
7256  FWidgetsWithResizeRequest.Remove(Widget);
7257end;
7258
7259{------------------------------------------------------------------------------
7260  TGtkWidgetSet SetResizeRequest
7261  Params: Widget: PGtkWidget
7262
7263  Marks the widget to send a ResizeRequest to the gtk.
7264  When the LCL resizes a control the new bounds will not be set directly, but
7265  cached. This is needed, because it is common behaviour to set the bounds step
7266  by step. For example: Left:=10; Top:=10; Width:=100; Height:=50; results in
7267  SetBounds(10,0,0,0);
7268  SetBounds(10,10,0,0);
7269  SetBounds(10,10,100,0);
7270  SetBounds(10,10,100,50);
7271  Because the gtk puts all size requests into a queue, it will process the
7272  requests not immediately, but _after_ all requests. This results in changing
7273  the widget size four times and everytime the LCL gets a message. If the
7274  control has children, this will resize the children four times.
7275  Therefore LCL size requests for a widget are cached and only the final one is
7276  sent in: TGtkWidgetSet.SendCachedLCLMessages.
7277 ------------------------------------------------------------------------------}
7278procedure SetResizeRequest(Widget: PGtkWidget);
7279{$IFDEF VerboseSizeMsg}
7280var
7281  LCLControl: TWinControl;
7282{$ENDIF}
7283begin
7284  {$IFDEF Gtk2}
7285  if not WidgetSizeIsEditable(Widget) then exit;
7286  {$ENDIF}
7287  {$IFDEF VerboseSizeMsg}
7288  LCLControl:=TWinControl(GetLCLObject(Widget));
7289  DbgOut('SetResizeRequest Widget=',DbgS(Widget));
7290  if LCLControl is TWinControl then
7291    DebugLn(' ',DbgSName(LCLControl),' LCLBounds=',dbgs(LCLControl.BoundsRect))
7292  else
7293    DebugLn(' ERROR: ',DbgSName(LCLControl));
7294  {$ENDIF}
7295  if not FWidgetsWithResizeRequest.Contains(Widget) then
7296    FWidgetsWithResizeRequest.Add(Widget);
7297end;
7298
7299{------------------------------------------------------------------------------
7300  function WidgetSizeIsEditable(Widget: PGtkWidget): boolean;
7301
7302  True if the widget can be resized.
7303  False if the size is under complete control of the gtk.
7304------------------------------------------------------------------------------}
7305function WidgetSizeIsEditable(Widget: PGtkWidget): boolean;
7306begin
7307  if Widget=nil then exit(false);
7308  if (GtkWidgetIsA(Widget,GTK_TYPE_WINDOW))
7309  or (GtkWidgetIsA(Widget^.Parent,gtk_fixed_get_type))
7310  or (GtkWidgetIsA(Widget^.Parent,gtk_layout_get_type))
7311  then
7312    Result:=true
7313  else
7314    Result:=false;
7315end;
7316
7317procedure ReportNotObsolete(const Texts : String);
7318Begin
7319  DebugLn('*********************************************');
7320  DebugLn('*********************************************');
7321  DebugLn('*************Non-Obsolete report*************');
7322  DebugLn('*********************************************');
7323  DebugLn('*************'+Texts+'*is being used yet.****');
7324  DebugLn('*******Please remove this function from******');
7325  DebugLn('*******the obsolete section in gtkproc.inc***');
7326  DebugLn('*********************************************');
7327  DebugLn('*********************************************');
7328  DebugLn('*********************************************');
7329  DebugLn('*********************************************');
7330end;
7331
7332function TGDKColorToTColor(const value : TGDKColor) : TColor;
7333begin
7334  Result := ((Value.Blue shr 8) shl 16) + ((Value.Green shr 8) shl 8)
7335           + (Value.Red shr 8);
7336end;
7337
7338function TColortoTGDKColor(const value : TColor) : TGDKColor;
7339var
7340  newColor : TGDKColor;
7341begin
7342  if Value<0 then begin
7343    FillChar(Result,SizeOf(Result),0);
7344    exit;
7345  end;
7346
7347  newColor.pixel := 0;
7348  newColor.red   := (value and $ff) * 257;
7349  newColor.green := ((value shr 8) and $ff) * 257;
7350  newColor.blue  := ((value shr 16) and $ff) * 257;
7351
7352  Result := newColor;
7353end;
7354
7355{------------------------------------------------------------------------------
7356  Function: UpdateSysColorMap
7357  Params:  none
7358  Returns: none
7359
7360  Reads the system colors.
7361 ------------------------------------------------------------------------------}
7362procedure UpdateSysColorMap(Widget: PGtkWidget; Lgs: TLazGtkStyle);
7363{$IFDEF VerboseUpdateSysColorMap}
7364  function GdkColorAsString(c: TgdkColor): string;
7365  begin
7366    Result:='LCL='+DbgS(TGDKColorToTColor(c))
7367             +' Pixel='+DbgS(c.Pixel)
7368             +' Red='+DbgS(c.Red)
7369             +' Green='+DbgS(c.Green)
7370             +' Blue='+DbgS(c.Blue)
7371             ;
7372  end;
7373{$ENDIF}
7374var
7375  MainStyle: PGtkStyle;
7376begin
7377  if Widget=nil then exit;
7378  if not (Lgs in [lgsButton, lgsWindow, lgsMenuBar, lgsMenuitem,
7379    lgsVerticalScrollbar, lgsHorizontalScrollbar, lgsTooltip]) then exit;
7380
7381  {$IFDEF NoStyle}
7382  exit;
7383  {$ENDIF}
7384  //debugln('UpdateSysColorMap ',GetWidgetDebugReport(Widget));
7385  gtk_widget_set_rc_style(Widget);
7386  MainStyle := gtk_widget_get_style(Widget);
7387  if MainStyle = nil then exit;
7388  with MainStyle^ do
7389  begin
7390    {$IFDEF VerboseUpdateSysColorMap}
7391    if rc_style<>nil then
7392    begin
7393      with rc_style^ do
7394      begin
7395        DebugLn('rc_style:');
7396        DebugLn(' FG GTK_STATE_NORMAL ',GdkColorAsString(fg[GTK_STATE_NORMAL]));
7397        DebugLn(' FG GTK_STATE_ACTIVE ',GdkColorAsString(fg[GTK_STATE_ACTIVE]));
7398        DebugLn(' FG GTK_STATE_PRELIGHT ',GdkColorAsString(fg[GTK_STATE_PRELIGHT]));
7399        DebugLn(' FG GTK_STATE_SELECTED ',GdkColorAsString(fg[GTK_STATE_SELECTED]));
7400        DebugLn(' FG GTK_STATE_INSENSITIVE ',GdkColorAsString(fg[GTK_STATE_INSENSITIVE]));
7401        DebugLn('');
7402        DebugLn(' BG GTK_STATE_NORMAL ',GdkColorAsString(bg[GTK_STATE_NORMAL]));
7403        DebugLn(' BG GTK_STATE_ACTIVE ',GdkColorAsString(bg[GTK_STATE_ACTIVE]));
7404        DebugLn(' BG GTK_STATE_PRELIGHT ',GdkColorAsString(bg[GTK_STATE_PRELIGHT]));
7405        DebugLn(' BG GTK_STATE_SELECTED ',GdkColorAsString(bg[GTK_STATE_SELECTED]));
7406        DebugLn(' BG GTK_STATE_INSENSITIVE ',GdkColorAsString(bg[GTK_STATE_INSENSITIVE]));
7407        DebugLn('');
7408        DebugLn(' TEXT GTK_STATE_NORMAL ',GdkColorAsString(text[GTK_STATE_NORMAL]));
7409        DebugLn(' TEXT GTK_STATE_ACTIVE ',GdkColorAsString(text[GTK_STATE_ACTIVE]));
7410        DebugLn(' TEXT GTK_STATE_PRELIGHT ',GdkColorAsString(text[GTK_STATE_PRELIGHT]));
7411        DebugLn(' TEXT GTK_STATE_SELECTED ',GdkColorAsString(text[GTK_STATE_SELECTED]));
7412        DebugLn(' TEXT GTK_STATE_INSENSITIVE ',GdkColorAsString(text[GTK_STATE_INSENSITIVE]));
7413        DebugLn('');
7414      end;
7415    end;
7416
7417    DebugLn('MainStyle:');
7418    DebugLn(' FG GTK_STATE_NORMAL ',GdkColorAsString(fg[GTK_STATE_NORMAL]));
7419    DebugLn(' FG GTK_STATE_ACTIVE ',GdkColorAsString(fg[GTK_STATE_ACTIVE]));
7420    DebugLn(' FG GTK_STATE_PRELIGHT ',GdkColorAsString(fg[GTK_STATE_PRELIGHT]));
7421    DebugLn(' FG GTK_STATE_SELECTED ',GdkColorAsString(fg[GTK_STATE_SELECTED]));
7422    DebugLn(' FG GTK_STATE_INSENSITIVE ',GdkColorAsString(fg[GTK_STATE_INSENSITIVE]));
7423    DebugLn('');
7424    DebugLn(' BG GTK_STATE_NORMAL ',GdkColorAsString(bg[GTK_STATE_NORMAL]));
7425    DebugLn(' BG GTK_STATE_ACTIVE ',GdkColorAsString(bg[GTK_STATE_ACTIVE]));
7426    DebugLn(' BG GTK_STATE_PRELIGHT ',GdkColorAsString(bg[GTK_STATE_PRELIGHT]));
7427    DebugLn(' BG GTK_STATE_SELECTED ',GdkColorAsString(bg[GTK_STATE_SELECTED]));
7428    DebugLn(' BG GTK_STATE_INSENSITIVE ',GdkColorAsString(bg[GTK_STATE_INSENSITIVE]));
7429    DebugLn('');
7430    DebugLn(' TEXT GTK_STATE_NORMAL ',GdkColorAsString(text[GTK_STATE_NORMAL]));
7431    DebugLn(' TEXT GTK_STATE_ACTIVE ',GdkColorAsString(text[GTK_STATE_ACTIVE]));
7432    DebugLn(' TEXT GTK_STATE_PRELIGHT ',GdkColorAsString(text[GTK_STATE_PRELIGHT]));
7433    DebugLn(' TEXT GTK_STATE_SELECTED ',GdkColorAsString(text[GTK_STATE_SELECTED]));
7434    DebugLn(' TEXT GTK_STATE_INSENSITIVE ',GdkColorAsString(text[GTK_STATE_INSENSITIVE]));
7435    DebugLn('');
7436    DebugLn(' LIGHT GTK_STATE_NORMAL ',GdkColorAsString(light[GTK_STATE_NORMAL]));
7437    DebugLn(' LIGHT GTK_STATE_ACTIVE ',GdkColorAsString(light[GTK_STATE_ACTIVE]));
7438    DebugLn(' LIGHT GTK_STATE_PRELIGHT ',GdkColorAsString(light[GTK_STATE_PRELIGHT]));
7439    DebugLn(' LIGHT GTK_STATE_SELECTED ',GdkColorAsString(light[GTK_STATE_SELECTED]));
7440    DebugLn(' LIGHT GTK_STATE_INSENSITIVE ',GdkColorAsString(light[GTK_STATE_INSENSITIVE]));
7441    DebugLn('');
7442    DebugLn(' DARK GTK_STATE_NORMAL ',GdkColorAsString(dark[GTK_STATE_NORMAL]));
7443    DebugLn(' DARK GTK_STATE_ACTIVE ',GdkColorAsString(dark[GTK_STATE_ACTIVE]));
7444    DebugLn(' DARK GTK_STATE_PRELIGHT ',GdkColorAsString(dark[GTK_STATE_PRELIGHT]));
7445    DebugLn(' DARK GTK_STATE_SELECTED ',GdkColorAsString(dark[GTK_STATE_SELECTED]));
7446    DebugLn(' DARK GTK_STATE_INSENSITIVE ',GdkColorAsString(dark[GTK_STATE_INSENSITIVE]));
7447    DebugLn('');
7448    DebugLn(' MID GTK_STATE_NORMAL ',GdkColorAsString(mid[GTK_STATE_NORMAL]));
7449    DebugLn(' MID GTK_STATE_ACTIVE ',GdkColorAsString(mid[GTK_STATE_ACTIVE]));
7450    DebugLn(' MID GTK_STATE_PRELIGHT ',GdkColorAsString(mid[GTK_STATE_PRELIGHT]));
7451    DebugLn(' MID GTK_STATE_SELECTED ',GdkColorAsString(mid[GTK_STATE_SELECTED]));
7452    DebugLn(' MID GTK_STATE_INSENSITIVE ',GdkColorAsString(mid[GTK_STATE_INSENSITIVE]));
7453    DebugLn('');
7454    DebugLn(' BASE GTK_STATE_NORMAL ',GdkColorAsString(base[GTK_STATE_NORMAL]));
7455    DebugLn(' BASE GTK_STATE_ACTIVE ',GdkColorAsString(base[GTK_STATE_ACTIVE]));
7456    DebugLn(' BASE GTK_STATE_PRELIGHT ',GdkColorAsString(base[GTK_STATE_PRELIGHT]));
7457    DebugLn(' BASE GTK_STATE_SELECTED ',GdkColorAsString(base[GTK_STATE_SELECTED]));
7458    DebugLn(' BASE GTK_STATE_INSENSITIVE ',GdkColorAsString(base[GTK_STATE_INSENSITIVE]));
7459    DebugLn('');
7460    DebugLn(' BLACK ',GdkColorAsString(black));
7461    DebugLn(' WHITE ',GdkColorAsString(white));
7462    {$ENDIF}
7463
7464    {$IFNDEF DisableGtkSysColors}
7465    // this map is taken from this research:
7466    // http://www.endolith.com/wordpress/2008/08/03/wine-colors/
7467    case Lgs of
7468      lgsButton:
7469        begin
7470          SysColorMap[COLOR_ACTIVEBORDER] := TGDKColorToTColor(bg[GTK_STATE_INSENSITIVE]);
7471          SysColorMap[COLOR_INACTIVEBORDER] := TGDKColorToTColor(bg[GTK_STATE_INSENSITIVE]);
7472          SysColorMap[COLOR_WINDOWFRAME] := TGDKColorToTColor(mid[GTK_STATE_SELECTED]);
7473
7474          SysColorMap[COLOR_BTNFACE] := TGDKColorToTColor(bg[GTK_STATE_INSENSITIVE]);
7475          SysColorMap[COLOR_BTNSHADOW] := TGDKColorToTColor(dark[GTK_STATE_INSENSITIVE]);
7476          SysColorMap[COLOR_BTNTEXT] := TGDKColorToTColor(fg[GTK_STATE_NORMAL]);
7477          SysColorMap[COLOR_BTNHIGHLIGHT] := TGDKColorToTColor(light[GTK_STATE_INSENSITIVE]);
7478          SysColorMap[COLOR_3DDKSHADOW] := TGDKColorToTColor(black);
7479          SysColorMap[COLOR_3DLIGHT] := TGDKColorToTColor(bg[GTK_STATE_INSENSITIVE]);
7480        end;
7481      lgsWindow:
7482        begin
7483          // colors which can be only retrieved from the window manager (metacity)
7484          SysColorMap[COLOR_ACTIVECAPTION] := TGDKColorToTColor(dark[GTK_STATE_SELECTED]);
7485          SysColorMap[COLOR_INACTIVECAPTION] := TGDKColorToTColor(dark[GTK_STATE_NORMAL]);
7486          SysColorMap[COLOR_GRADIENTACTIVECAPTION] := TGDKColorToTColor(light[GTK_STATE_SELECTED]);
7487          SysColorMap[COLOR_GRADIENTINACTIVECAPTION] := TGDKColorToTColor(base[GTK_STATE_NORMAL]);
7488          SysColorMap[COLOR_CAPTIONTEXT] := TGDKColorToTColor(white);
7489          SysColorMap[COLOR_INACTIVECAPTIONTEXT] := TGDKColorToTColor(white);
7490          // others
7491          SysColorMap[COLOR_APPWORKSPACE] := TGDKColorToTColor(base[GTK_STATE_NORMAL]);
7492          SysColorMap[COLOR_GRAYTEXT] := TGDKColorToTColor(fg[GTK_STATE_INSENSITIVE]);
7493          SysColorMap[COLOR_HIGHLIGHT] := TGDKColorToTColor(base[GTK_STATE_SELECTED]);
7494          SysColorMap[COLOR_HIGHLIGHTTEXT] := TGDKColorToTColor(fg[GTK_STATE_SELECTED]);
7495          SysColorMap[COLOR_WINDOW] := TGDKColorToTColor(base[GTK_STATE_NORMAL]);
7496          SysColorMap[COLOR_WINDOWTEXT] := TGDKColorToTColor(text[GTK_STATE_NORMAL]);
7497          SysColorMap[COLOR_HOTLIGHT] := TGDKColorToTColor(light[GTK_STATE_NORMAL]);
7498          SysColorMap[COLOR_BACKGROUND] := TGDKColorToTColor(bg[GTK_STATE_PRELIGHT]);
7499          SysColorMap[COLOR_FORM] := TGDKColorToTColor(bg[GTK_STATE_NORMAL]);
7500        end;
7501      lgsMenuBar:
7502        begin
7503          SysColorMap[COLOR_MENUBAR] := TGDKColorToTColor(bg[GTK_STATE_NORMAL]);
7504        end;
7505      lgsMenuitem:
7506        begin
7507          SysColorMap[COLOR_MENU] := TGDKColorToTColor(light[GTK_STATE_ACTIVE]);
7508          SysColorMap[COLOR_MENUTEXT] := TGDKColorToTColor(fg[GTK_STATE_NORMAL]);
7509          SysColorMap[COLOR_MENUHILIGHT] := TGDKColorToTColor(bg[GTK_STATE_PRELIGHT]);
7510        end;
7511      lgsVerticalScrollbar,
7512      lgsHorizontalScrollbar:
7513        begin
7514          SysColorMap[COLOR_SCROLLBAR] := TGDKColorToTColor(bg[GTK_STATE_ACTIVE]);
7515        end;
7516      lgsTooltip:
7517        begin
7518          SysColorMap[COLOR_INFOTEXT] := TGDKColorToTColor(fg[GTK_STATE_NORMAL]);
7519          SysColorMap[COLOR_INFOBK] := TGDKColorToTColor(bg[GTK_STATE_NORMAL]);
7520        end;
7521    end;
7522    {$ENDIF}
7523  end;
7524end;
7525
7526
7527{------------------------------------------------------------------------------
7528  Function: WaitForClipbrdAnswerDummyTimer
7529
7530  this is a helper function for WaitForClipboardAnswer
7531 ------------------------------------------------------------------------------}
7532function WaitForClipbrdAnswerDummyTimer(Client: Pointer): {$IFDEF Gtk2}gboolean{$ELSE}gint{$ENDIF}; cdecl;
7533begin
7534  if CLient=nil then ;
7535  Result:=GdkTrue; // go on, make sure getting a message at least every second
7536end;
7537
7538function GetScreenWidthMM(GdkValue: boolean): integer;
7539begin
7540  Result:=gdk_screen_width_mm;
7541  if (Result<=0) and not GdkValue then
7542    Result:=300; // some TV-out screens don't know there size
7543end;
7544
7545function GetScreenHeightMM(GdkValue: boolean): integer;
7546begin
7547  Result:=gdk_screen_height_mm;
7548  if (Result<=0) and not GdkValue then
7549    Result:=300; // some TV-out screens don't know there size
7550end;
7551
7552{------------------------------------------------------------------------------
7553  Function: WaitForClipboardAnswer
7554  Params: none
7555  Returns: true, if clipboard data arrived
7556
7557  waits til clipboard/selection answer arrived (max 1 second)
7558  ! While waiting the messagequeue will be processed !
7559 ------------------------------------------------------------------------------}
7560function WaitForClipboardAnswer(c: PClipboardEventData): boolean;
7561var
7562  StartTime, CurTime: TSystemTime;
7563  Timer: cardinal;
7564
7565  function ValidDateSelection : boolean;
7566  begin
7567    result := c^.Data.Selection<>0;
7568  end;
7569
7570begin
7571  Result:=false;
7572  {$IFDEF DEBUG_CLIPBOARD}
7573  DebugLn('[WaitForClipboardAnswer] A');
7574  {$ENDIF}
7575  if (ValidDateSelection) or (c^.Waiting) or (c^.Stopping) then begin
7576    {$IFDEF DEBUG_CLIPBOARD}
7577    DebugLn('[WaitForClipboardAnswer] ValidDateSelection=',dbgs(ValidDateSelection),' Waiting=',dbgs(c^.Waiting),' Stopping=',dbgs(c^.Stopping));
7578    {$ENDIF}
7579    Result:=(ValidDateSelection);
7580    exit;
7581  end;
7582  c^.Waiting:=true;
7583  DateTimeToSystemTime(Time,StartTime);
7584  //DebugLn('[WaitForClipboardAnswer] C');
7585  Application.ProcessMessages;
7586  //DebugLn('[WaitForClipboardAnswer] D');
7587  if (ValidDateSelection) or (c^.Stopping) then begin
7588    {$IFDEF DEBUG_CLIPBOARD}
7589    DebugLn('[WaitForClipboardAnswer] E  Yeah, Response received after processing messages');
7590    {$ENDIF}
7591    Result:=(ValidDateSelection);
7592    exit;
7593  end;
7594  //DebugLn('[WaitForClipboardAnswer] F');
7595  // start a timer to make sure not waiting forever
7596  Timer := gtk_timeout_add(500, @WaitForClipbrdAnswerDummyTimer, nil);
7597  try
7598    repeat
7599      // just wait ...
7600      {$IFDEF DEBUG_CLIPBOARD}
7601      DebugLn('[WaitForClipboardAnswer] G');
7602      {$ENDIF}
7603      Application.ProcessMessages;
7604      if (ValidDateSelection) or (c^.Stopping) then begin
7605        {$IFDEF DEBUG_CLIPBOARD}
7606        DebugLn('[WaitForClipboardAnswer] H  Yeah, Response received after waiting with timer');
7607        {$ENDIF}
7608        Result:=(ValidDateSelection);
7609        exit;
7610      end;
7611      DateTimeToSystemTime(Time,CurTime);
7612    until (CurTime.Second*1000+CurTime.MilliSecond
7613           -StartTime.Second*1000-StartTime.MilliSecond
7614           >1000);
7615  finally
7616    {$IFDEF DEBUG_CLIPBOARD}
7617    DebugLn('[WaitForClipboardAnswer] H');
7618    {$ENDIF}
7619    // stop the timer
7620    gtk_timeout_remove(Timer);
7621//DebugLn('[WaitForClipboardAnswer] END');
7622  end;
7623  { $IFDEF DEBUG_CLIPBOARD}
7624  DebugLn('[WaitForClipboardAnswer] WARNING: no answer received in time');
7625  { $ENDIF}
7626end;
7627
7628{------------------------------------------------------------------------------
7629  Function: RequestSelectionData
7630  Params: ClipboardWidget - widget with connected signals 'selection_get'
7631                            and 'selection_clear_event'
7632          ClipboardType
7633          FormatID - the selection target format wanted
7634  Returns: the TGtkSelectionData record
7635
7636  requests the format FormatID of clipboard of type ClipboardType and
7637  waits til clipboard/selection answer arrived (max 1 second)
7638  ! While waiting the messagequeue will be processed !
7639 ------------------------------------------------------------------------------}
7640function RequestSelectionData(ClipboardWidget: PGtkWidget;
7641  ClipboardType: TClipboardType; FormatID: PtrUInt): TGtkSelectionData;
7642
7643  function TimeIDExists(TimeID: guint32): boolean;
7644  var
7645    i: Integer;
7646  begin
7647    i:=ClipboardSelectionData.Count-1;
7648    while (i>=0) do begin
7649      if (PClipboardEventData(ClipboardSelectionData[i])^.TimeID=TimeID) then
7650        exit(true);
7651      dec(i);
7652    end;
7653    Result:=false;
7654  end;
7655
7656var
7657  TimeID: cardinal;
7658  c: PClipboardEventData;
7659  sanity: Integer = 0;
7660begin
7661  {$IFDEF DEBUG_CLIPBOARD}
7662  DebugLn('[RequestSelectionData] FormatID=',dbgs(FormatID));
7663  {$ENDIF}
7664  FillChar(Result,SizeOf(TGtkSelectionData),0);
7665  if (ClipboardWidget=nil) or (FormatID=0)
7666  or (ClipboardTypeAtoms[ClipboardType]=0) then exit;
7667
7668  TimeID:= gdk_event_get_time(gtk_get_current_event);
7669             // IMPORTANT: To retrieve data from xterm or kde applications
7670             //            the time id must be 0 or event^.time
7671  repeat
7672    while TimeIDExists(TimeID) do begin
7673      inc(TimeID);
7674      if TimeID>1010 then exit;
7675    end;
7676    New(c);
7677    FillChar(c^,SizeOf(TClipboardEventData),0);
7678    c^.TimeID:=TimeID;
7679    ClipboardSelectionData.Add(c);
7680    try
7681      {$IFDEF DEBUG_CLIPBOARD}
7682      DebugLn('[RequestSelectionData] TimeID=',dbgs(TimeID),' Type=',GdkAtomToStr(ClipboardTypeAtoms[ClipboardType]),' FormatID=',GdkAtomToStr(FormatID), ' Sanity=', IntToStr(Sanity));
7683      {$ENDIF}
7684      if gtk_selection_convert(ClipboardWidget, ClipboardTypeAtoms[ClipboardType],
7685                               FormatID, TimeID)<>GdkFalse
7686      then begin
7687        if not WaitForClipboardAnswer(c) then exit;
7688        Result:=c^.Data;
7689        break;
7690      end;
7691    finally
7692      ClipboardSelectionData.Remove(c);
7693      Dispose(c);
7694    end;
7695    Inc(sanity);
7696    sleep(100);
7697  until false or (sanity > 10);
7698end;
7699
7700{------------------------------------------------------------------------------
7701  Function: FreeClipboardTargetEntries
7702  Params: ClipboardType
7703  Returns: -
7704
7705  frees the memory of a ClipboardTargetEntries list
7706 ------------------------------------------------------------------------------}
7707procedure FreeClipboardTargetEntries(ClipboardType: TClipboardType);
7708var i: integer;
7709begin
7710  if ClipboardTargetEntries[ClipboardType]<>nil then begin
7711    for i:=0 to ClipboardTargetEntryCnt[ClipboardType]-1 do
7712      StrDispose(ClipboardTargetEntries[ClipboardType][i].Target);
7713    FreeMem(ClipboardTargetEntries[ClipboardType]);
7714  end;
7715end;
7716
7717
7718{------------------------------------------------------------------------------
7719  function GdkAtomToStr(const Atom: TGdkAtom): string;
7720
7721  Returns the associated string
7722 ------------------------------------------------------------------------------}
7723function GdkAtomToStr(const Atom: TGdkAtom): string;
7724var
7725  p: Pgchar;
7726begin
7727  p:=gdk_atom_name(Atom);
7728  Result:=p;
7729  if p<>nil then g_free(p);
7730end;
7731
7732{-------------------------------------------------------------------------------
7733  function CreateFormContents(AForm: TCustomForm;
7734    var FormWidget: Pointer): Pointer;
7735
7736  Creates the contents for the form (normally a hbox plus a client area.
7737  The hbox is needed for the menu.) The FormWidget is the main widget, for which
7738  the client area is associated. If FormWidget=nil then the hbox will be used
7739  as main widget.
7740-------------------------------------------------------------------------------}
7741function CreateFormContents(AForm: TCustomForm;
7742  var FormWidget: Pointer; AWidgetInfo: PWidgetInfo = nil): Pointer;
7743var
7744  ScrolledWidget, ClientAreaWidget: PGtkWidget;
7745  WindowStyle: PGtkStyle;
7746  Adjustment: PGtkAdjustment;
7747begin
7748  // Create the VBox. We need that to place controls outside
7749  // the client area (like menu)
7750  Result := gtk_vbox_new(False, 0);
7751
7752  if FormWidget = nil then
7753    FormWidget := Result;
7754
7755  // Create the form client area (a scrolled window with a gtklayout
7756  // with the style of a window)
7757  ScrolledWidget := gtk_scrolled_window_new(nil, nil);
7758  gtk_box_pack_end(Result, ScrolledWidget, True, True, 0);
7759  gtk_widget_show(ScrolledWidget);
7760
7761  ClientAreaWidget := gtk_layout_new(nil, nil);
7762  WindowStyle := GetStyle(lgsWindow);
7763  gtk_widget_set_style(ClientAreaWidget, WindowStyle);
7764  //debugln('CreateFormContents Style=',GetStyleDebugReport(WindowStyle));
7765  gtk_container_add(PGtkContainer(ScrolledWidget), ClientAreaWidget);
7766
7767  gtk_object_set_data(FormWidget, odnScrollArea, ScrolledWidget);
7768
7769  gtk_widget_show(ClientAreaWidget);
7770  SetFixedWidget(FormWidget, ClientAreaWidget);
7771  SetMainWidget(FormWidget, ClientAreaWidget);
7772
7773  if ScrolledWidget <> nil then
7774  begin
7775    GTK_WIDGET_UNSET_FLAGS(PGtkScrolledWindow(ScrolledWidget)^.hscrollbar,
7776                           GTK_CAN_FOCUS);
7777    GTK_WIDGET_UNSET_FLAGS(PGtkScrolledWindow(ScrolledWidget)^.vscrollbar,
7778                           GTK_CAN_FOCUS);
7779    gtk_scrolled_window_set_policy(PGtkScrolledWindow(ScrolledWidget),
7780                                   GTK_POLICY_NEVER,GTK_POLICY_NEVER);
7781
7782    Adjustment := gtk_scrolled_window_get_vadjustment(PGTKScrolledWindow(ScrolledWidget));
7783    if Adjustment <> nil then
7784      gtk_object_set_data(PGTKObject(Adjustment), odnScrollBar,
7785        PGTKScrolledWindow(ScrolledWidget)^.vscrollbar);
7786
7787    Adjustment := gtk_scrolled_window_get_hadjustment(PGTKScrolledWindow(ScrolledWidget));
7788    if Adjustment <> nil then
7789      gtk_object_set_data(PGTKObject(Adjustment), odnScrollBar,
7790        PGTKScrolledWindow(ScrolledWidget)^.hscrollbar);
7791    {$ifdef gtk2}
7792    if (AWidgetInfo <> nil) and
7793      (gtk_major_version >= 2) and (gtk_minor_version > 8) then
7794    begin
7795      g_signal_connect(PGTKScrolledWindow(ScrolledWidget)^.hscrollbar, 'change-value',
7796        TGCallback(@Gtk2RangeScrollCB), AWidgetInfo);
7797      g_signal_connect(PGTKScrolledWindow(ScrolledWidget)^.vscrollbar, 'change-value',
7798        TGCallback(@Gtk2RangeScrollCB), AWidgetInfo);
7799    end;
7800    {$endif}
7801  end;
7802end;
7803
7804function IndexOfStyle(aStyle: TLazGtkStyle): integer;
7805begin
7806  Result:=IndexOfStyleWithName(LazGtkStyleNames[aStyle]);
7807end;
7808
7809{------------------------------------------------------------------------------
7810  Function: IndexOfWithNameStyle
7811  Params: WName
7812  Returns: Index of Style
7813
7814  Returns the Index within the Styles property of WNAME
7815 ------------------------------------------------------------------------------}
7816function IndexOfStyleWithName(const WName : String): integer;
7817begin
7818  if Styles<>nil then begin
7819    for Result:=0 to Styles.Count-1 do
7820      if CompareText(WName,Styles[Result])=0 then exit;
7821  end;
7822  Result:=-1;
7823end;
7824
7825{------------------------------------------------------------------------------
7826  Function: ReleaseStyle
7827  Params: WName
7828  Returns: nothing
7829
7830  Tries to release a Style corresponding to the Widget Name passed, aka 'button',
7831  'default', checkbox', etc. This should only be called on theme change or on
7832  application terminate.
7833 ------------------------------------------------------------------------------}
7834function NewStyleObject : PStyleObject;
7835begin
7836  New(Result);
7837  FillChar(Result^, SizeOf(TStyleObject), 0);
7838end;
7839
7840procedure FreeStyleObject(var StyleObject : PStyleObject);
7841// internal function to dispose a styleobject
7842// it does *not* remove it from the style lists
7843begin
7844  if StyleObject <> nil then
7845  begin
7846    if StyleObject^.Obj <> nil then
7847      gtk_object_destroy(StyleObject^.Obj);
7848    if StyleObject^.Widget <> nil then
7849    begin
7850      // first unref
7851      gtk_widget_unref(StyleObject^.Widget);
7852      // then destroy
7853      gtk_widget_destroy(StyleObject^.Widget);
7854    end;
7855    if StyleObject^.Style <> nil then
7856      if StyleObject^.Style^.{$IFDEF Gtk2}attach_count{$ELSE}Ref_Count{$ENDIF} > 0 then
7857        gtk_style_unref(StyleObject^.Style);
7858    Dispose(StyleObject);
7859    StyleObject := nil;
7860  end;
7861end;
7862
7863procedure ReleaseAllStyles;
7864var
7865  StyleObject: PStyleObject;
7866  lgs: TLazGtkStyle;
7867  i: Integer;
7868begin
7869  if Styles=nil then exit;
7870  {$IFDEF Gtk2}
7871  if DefaultPangoLayout<>nil then begin
7872    g_object_unref(DefaultPangoLayout);
7873    DefaultPangoLayout:=nil;
7874  end;
7875  {$ENDIF}
7876  for i:=Styles.Count-1 downto 0 do begin
7877    StyleObject:=PStyleObject(Styles.Objects[i]);
7878    FreeStyleObject(StyleObject);
7879  end;
7880  Styles.Clear;
7881  for lgs:=Low(TLazGtkStyle) to High(TLazGtkStyle) do
7882    StandardStyles[lgs]:=nil;
7883end;
7884
7885procedure ReleaseStyle(aStyle: TLazGtkStyle);
7886var
7887  StyleObject: PStyleObject;
7888  l: Integer;
7889begin
7890  if Styles=nil then exit;
7891  if aStyle in [lgsUserDefined] then
7892    RaiseGDBException('');// user styles are defined by name
7893  StyleObject:=StandardStyles[aStyle];
7894  if StyleObject<>nil then begin
7895    l:=IndexOfStyle(aStyle);
7896    Styles.Delete(l);
7897    StandardStyles[aStyle]:=nil;
7898    FreeStyleObject(StyleObject);
7899  end;
7900end;
7901
7902procedure ReleaseStyleWithName(const WName : String);
7903var
7904  l : Longint;
7905  s : PStyleObject;
7906begin
7907  if Styles=nil then exit;
7908  l := IndexOfStyleWithName(WName);
7909  If l >= 0 then begin
7910    If Styles.Objects[l] <> nil then
7911      Try
7912        s := PStyleObject(Styles.Objects[l]);
7913        FreeStyleObject(S);
7914      Except
7915        DebugLn('[ReleaseStyle] : Unable To Unreference Style');
7916      end;
7917    Styles.Delete(l);
7918  end;
7919end;
7920
7921function GetStyle(aStyle: TLazGtkStyle): PGTKStyle;
7922begin
7923  if Styles = nil then Exit(nil);
7924  if aStyle in [lgsUserDefined] then
7925    RaiseGDBException(''); // user styles are defined by name
7926  if StandardStyles[aStyle] <> nil then // already created
7927    Result := StandardStyles[aStyle]^.Style
7928  else // create it
7929    Result := GetStyleWithName(LazGtkStyleNames[aStyle]);
7930end;
7931
7932procedure tooltip_window_style_set(Widget: PGtkWidget; PreviousStyle: PGtkStyle;
7933          StyleObject: PStyleObject); cdecl;
7934begin
7935  StyleObject^.Style := gtk_widget_get_style(Widget);
7936  UpdateSysColorMap(Widget, lgsToolTip);
7937end;
7938
7939{------------------------------------------------------------------------------
7940  Function: GetStyleWithName
7941  Params: none
7942  Returns: Returns a Corresponding Style
7943
7944  Tries to get the Style corresponding to the Widget Name passed, aka 'button',
7945  'default', checkbox', etc. for use within such routines as DrawFrameControl
7946  to attempt to supply theme dependent drawing. Styles are stored in a TStrings
7947  list which is only updated on theme change, to ensure fast efficient retrieval
7948  of Styles.
7949 ------------------------------------------------------------------------------}
7950function GetStyleWithName(const WName: String) : PGTKStyle;
7951var
7952  StyleObject : PStyleObject;
7953
7954  function CreateStyleNotebook: PGTKWidget;
7955  var
7956    NoteBookWidget: PGtkNotebook;
7957    //NoteBookPageWidget: PGtkWidget;
7958    NoteBookPageClientAreaWidget: PGtkWidget;
7959    NoteBookTabLabel: PGtkWidget;
7960    NoteBookTabMenuLabel: PGtkWidget;
7961  begin
7962    Result:=gtk_notebook_new;
7963    NoteBookWidget := PGtkNoteBook(Result);
7964    //NoteBookPageWidget := gtk_hbox_new(false, 0);
7965    NoteBookPageClientAreaWidget := CreateFixedClientWidget{$IFNDEF GtkFixedWithWindow}(false){$ENDIF};
7966    gtk_widget_show(NoteBookPageClientAreaWidget);
7967    //gtk_container_add(GTK_CONTAINER(NoteBookPageWidget),
7968    //                  NoteBookPageClientAreaWidget);
7969    //gtk_widget_show(NoteBookPageWidget);
7970    NoteBookTabLabel:=gtk_label_new('Lazarus');
7971    gtk_widget_show(NoteBookTabLabel);
7972    NoteBookTabMenuLabel:=gtk_label_new('Lazarus');
7973    gtk_widget_show(NoteBookTabMenuLabel);
7974    gtk_notebook_append_page_menu(NoteBookWidget,NoteBookPageClientAreaWidget,
7975                                  NoteBookTabLabel,NoteBookTabMenuLabel);
7976    gtk_widget_set_usize(Result,400,400);
7977  end;
7978
7979  procedure ResizeWidget(CurWidget: PGTKWidget; NewWidth, NewHeight: integer);
7980  {$IFDEF Gtk1}
7981  begin
7982    gtk_widget_set_usize(StyleObject^.Widget,NewWidth,NewHeight);
7983  end;
7984  {$ELSE}
7985  var
7986    allocation: TGtkAllocation;
7987  begin
7988    allocation.x:=0;
7989    allocation.y:=0;
7990    allocation.width:=NewWidth;
7991    allocation.height:=NewHeight;
7992    //gtk_widget_set_usize(StyleObject^.Widget,NewWidth,NewHeight);
7993    gtk_widget_size_allocate(CurWidget,@allocation);
7994    StyleObject^.FrameBordersValid:=false;
7995  end;
7996  {$ENDIF}
7997
7998var
7999  Tp : Pointer;
8000  l : Longint;
8001  NoName: PGChar;
8002  lgs: TLazGtkStyle;
8003  WidgetName: String;
8004  //VBox: PGtkWidget;
8005  AddToStyleWindow: Boolean;
8006  StyleWindowWidget: PGtkWidget;
8007  Requisition: TGtkRequisition;
8008  WindowFixedWidget: PGtkWidget;
8009  VBox: PGtkWidget;
8010begin
8011  Result := nil;
8012  if Styles=nil then exit;
8013  {$IFDEF NoStyle}
8014  exit;
8015  {$ENDIF}
8016
8017  if (WName='') then exit;
8018  l:=IndexOfStyleWithName(WName);
8019  //DebugLn(['GetStyleWithName START ',WName,' ',l]);
8020
8021  if l >= 0 then
8022  begin
8023    StyleObject:=PStyleObject(Styles.Objects[l]);
8024    Result := StyleObject^.Style;
8025  end else
8026  begin
8027    // create a new style object
8028    StyleObject := NewStyleObject;
8029    lgs := lgsUserDefined;
8030    Tp := nil;
8031    AddToStyleWindow := True;
8032    WidgetName := 'LazStyle' + WName;
8033    // create a style widget
8034    If CompareText(WName,LazGtkStyleNames[lgsButton])=0 then begin
8035        StyleObject^.Widget := GTK_BUTTON_NEW;
8036        lgs:=lgsButton;
8037      end
8038    else
8039      If CompareText(WName,LazGtkStyleNames[lgsLabel])=0 then begin
8040        StyleObject^.Widget := GTK_LABEL_NEW('StyleLabel');
8041        lgs:=lgsLabel;
8042      end
8043    else
8044      If CompareText(WName,LazGtkStyleNames[lgsDefault])=0 then begin
8045        lgs:=lgsDefault;
8046        AddToStyleWindow:=false;
8047        NoName:=nil;
8048        StyleObject^.Widget :=
8049          // GTK2 does not allow to instantiate the abstract base Widget
8050          // so we use the "invisible" widget, which should never be defined
8051          // by the theme
8052          GTK_WIDGET_NEW(
8053            {$IFDEF Gtk2}GTK_TYPE_INVISIBLE{$ELSE}GTK_WIDGET_TYPE{$ENDIF},
8054            NoName,[]);
8055      end
8056    else
8057      If CompareText(WName,LazGtkStyleNames[lgsWindow])=0 then begin
8058        lgs:=lgsWindow;
8059        StyleObject^.Widget := GTK_WINDOW_NEW(GTK_WINDOW_TOPLEVEL);
8060        AddToStyleWindow:=false;
8061        gtk_widget_hide(StyleObject^.Widget);
8062        // create the fixed widget
8063        // (where to put all style widgets, that need a parent for realize)
8064        VBox:=gtk_vbox_new(false,0);// vbox is needed for menu above and fixed widget below
8065        gtk_widget_show(VBox);
8066        gtk_container_add(PGtkContainer(StyleObject^.Widget), VBox);
8067        gtk_object_set_data(PGtkObject(StyleObject^.Widget),'vbox',VBox);
8068        WindowFixedWidget:=CreateFixedClientWidget;
8069        gtk_widget_show(WindowFixedWidget);
8070        gtk_container_add(PGtkContainer(VBox), WindowFixedWidget);
8071        gtk_object_set_data(PGtkObject(StyleObject^.Widget),'fixedwidget',WindowFixedWidget);
8072        gtk_widget_realize(StyleObject^.Widget);
8073      end
8074    else
8075      If CompareText(WName,LazGtkStyleNames[lgsCheckbox])=0 then begin
8076        lgs:=lgsCheckbox;
8077        StyleObject^.Widget := GTK_CHECK_BUTTON_NEW;
8078      end
8079    else
8080      If CompareText(WName,LazGtkStyleNames[lgsRadiobutton])=0 then begin
8081        lgs:=lgsRadiobutton;
8082        StyleObject^.Widget := GTK_RADIO_BUTTON_NEW(nil);
8083      end
8084    else
8085      If CompareText(WName,LazGtkStyleNames[lgsMenu])=0 then begin
8086        lgs:=lgsMenu;
8087        {$IFDEF Gtk1}
8088        AddToStyleWindow:=false;
8089        {$ENDIF}
8090        StyleObject^.Widget := gtk_menu_new;
8091      end
8092    else
8093      If CompareText(WName,LazGtkStyleNames[lgsMenuBar])=0 then begin
8094        lgs:=lgsMenuBar;
8095        {$IFDEF Gtk1}
8096        AddToStyleWindow:=false;
8097        {$ENDIF}
8098        StyleObject^.Widget := gtk_menu_bar_new;
8099      end
8100    else
8101      If CompareText(WName,LazGtkStyleNames[lgsMenuitem])=0 then begin
8102        lgs:=lgsMenuitem;
8103        {$IFDEF Gtk1}
8104        AddToStyleWindow:=false;
8105        StyleObject^.Widget := gtk_menu_item_new;
8106        {$ELSE}
8107        // image menu item is needed to correctly return theme options
8108        StyleObject^.Widget := gtk_image_menu_item_new;
8109        {$ENDIF}
8110      end
8111    else
8112      If CompareText(WName,LazGtkStyleNames[lgsStatusBar])=0 then begin
8113        lgs:=lgsStatusBar;
8114        AddToStyleWindow:=true;
8115        StyleObject^.Widget := gtk_statusbar_new;
8116      end
8117    else
8118      If CompareText(WName,LazGtkStyleNames[lgsCalendar])=0 then begin
8119        lgs:=lgsCalendar;
8120        AddToStyleWindow:=true;
8121        StyleObject^.Widget := gtk_calendar_new;
8122      end
8123    else
8124      If CompareText(WName,LazGtkStyleNames[lgsList])=0 then begin
8125        lgs:=lgsList;
8126        StyleObject^.Widget := gtk_list_new;
8127      end
8128    else
8129      If CompareText(WName,LazGtkStyleNames[lgsVerticalScrollbar])=0 then begin
8130        lgs:=lgsVerticalScrollbar;
8131        StyleObject^.Widget := gtk_vscrollbar_new(nil);
8132      end
8133    else
8134      If CompareText(WName,LazGtkStyleNames[lgsHorizontalScrollbar])=0 then begin
8135        lgs:=lgsHorizontalScrollbar;
8136        StyleObject^.Widget := gtk_hscrollbar_new(nil);
8137      end
8138    else
8139      If CompareText(WName,LazGtkStyleNames[lgsVerticalPaned])=0 then begin
8140        lgs:=lgsVerticalPaned;
8141        StyleObject^.Widget := gtk_vpaned_new;
8142      end
8143    else
8144      If CompareText(WName,LazGtkStyleNames[lgsHorizontalPaned])=0 then begin
8145        lgs:=lgsHorizontalPaned;
8146        StyleObject^.Widget := gtk_hpaned_new;
8147      end
8148    else
8149      If CompareText(WName,LazGtkStyleNames[lgsNotebook])=0 then begin
8150        lgs:=lgsNotebook;
8151        StyleObject^.Widget := CreateStyleNotebook;
8152      end
8153    else
8154      if CompareText(WName,LazGtkStyleNames[lgsTooltip])=0 then
8155      begin
8156        lgs := lgsTooltip;
8157        Tp := gtk_tooltips_new;
8158        gtk_tooltips_force_window(Tp);
8159        StyleObject^.Widget := PGTKTooltips(Tp)^.Tip_Window;
8160        gtk_widget_ref(StyleObject^.Widget);// MG: why is this needed?
8161        {$IFNDEF GTK1}
8162        g_signal_connect(StyleObject^.Widget, 'style-set',
8163			 TGCallback(@tooltip_window_style_set), StyleObject);
8164        {$ENDIF}
8165        WidgetName := 'gtk-tooltip-lcl';
8166        StyleObject^.Obj := Tp;
8167        Tp := nil;
8168        {$IFDEF GTK1}
8169        AddToStyleWindow := False;
8170        {$ENDIF}
8171      end
8172    else
8173      If CompareText(WName,LazGtkStyleNames[lgsHScale])=0 then begin
8174        lgs:=lgsHScale;
8175        TP := PGtkWidget( gtk_adjustment_new (0, 0, 100, 1, 10, 0));
8176        StyleObject^.Widget := gtk_hscale_new (PGTKADJUSTMENT (TP));
8177      end
8178    else
8179      If CompareText(WName,LazGtkStyleNames[lgsVScale])=0 then begin
8180        lgs:=lgsVScale;
8181        TP := PGtkWidget( gtk_adjustment_new (0, 0, 100, 1, 10, 0));
8182        StyleObject^.Widget := gtk_vscale_new (PGTKADJUSTMENT (TP));
8183      end
8184    else
8185      If CompareText(WName,LazGtkStyleNames[lgsGroupBox])=0 then begin
8186        lgs:=lgsGroupBox;
8187        StyleObject^.Widget := gtk_frame_new('GroupBox');
8188        WindowFixedWidget:=CreateFixedClientWidget{$IFNDEF GtkFixedWithWindow}(false){$ENDIF};
8189        gtk_widget_show(WindowFixedWidget);
8190        gtk_container_add(PGtkContainer(StyleObject^.Widget), WindowFixedWidget);
8191        gtk_object_set_data(PGtkObject(StyleObject^.Widget),'fixedwidget',WindowFixedWidget);
8192      end
8193{$ifdef gtk2}
8194    else
8195      If CompareText(WName,LazGtkStyleNames[lgsTreeView])=0 then begin
8196        lgs:=lgsTreeView;
8197        StyleObject^.Widget := gtk_tree_view_new;
8198        gtk_tree_view_append_column(PGtkTreeView(StyleObject^.Widget), gtk_tree_view_column_new);
8199      end
8200{$endif}
8201    else
8202      If CompareText(WName,LazGtkStyleNames[lgsToolBar])=0 then begin
8203        lgs:=lgsToolBar;
8204        StyleObject^.Widget := gtk_toolbar_new;
8205      end
8206    else
8207      If CompareText(WName,LazGtkStyleNames[lgsToolButton])=0 then begin
8208        lgs:=lgsToolButton;
8209        StyleObject^.Widget := gtk_toolbar_append_item(PGtkToolBar(GetStyleWidget(lgsToolBar)), 'B', nil, nil, nil, nil, nil);
8210      end
8211    else
8212      if CompareText(WName,LazGtkStyleNames[lgsScrolledWindow])=0 then begin
8213        lgs:=lgsScrolledWindow;
8214        StyleObject^.Widget := gtk_scrolled_window_new(nil, nil);
8215      end
8216    else
8217      If CompareText(WName,LazGtkStyleNames[lgsGTK_Default])=0 then begin
8218        lgs:=lgsGTK_Default;
8219        AddToStyleWindow:=false;
8220        StyleObject^.Widget := nil;
8221        StyleObject^.Style := gtk_style_new;
8222      end
8223    else begin
8224      // unknown style name -> bug
8225      FreeStyleObject(StyleObject);
8226      AddToStyleWindow:=false;
8227      RaiseGDBException('');
8228    end;
8229
8230    if (lgs<>lgsUserDefined) and (StandardStyles[lgs]<>nil) then begin
8231      // consistency error
8232      RaiseGDBException('');
8233    end;
8234
8235    // ensure style of the widget
8236    If (StyleObject^.Widget <> nil) then begin
8237      gtk_widget_ref(StyleObject^.Widget);
8238
8239      // put style widget on style window, so that it can be realized
8240      if AddToStyleWindow then
8241      begin
8242        gtk_widget_show_all(StyleObject^.Widget);
8243        if GtkWidgetIsA(StyleObject^.Widget,GTK_TYPE_MENU) then
8244        begin
8245          // attach menu to window
8246          gtk_menu_attach_to_widget(PGtkMenu(StyleObject^.Widget),
8247            GetStyleWidget(lgsWindow), nil);
8248        end
8249        else
8250        if GtkWidgetIsA(StyleObject^.Widget,GTK_TYPE_MENU_BAR) then
8251        begin
8252          StyleWindowWidget:=GetStyleWidget(lgsWindow);
8253          // add menu above the forms client area (fixed widget)
8254          VBox:=PGTKWidget(
8255                     gtk_object_get_data(PGtkObject(StyleWindowWidget),'vbox'));
8256          gtk_box_pack_start(PGTKBox(VBox), StyleObject^.Widget, False, False, 0);
8257        end
8258        else
8259        if GtkWidgetIsA(StyleObject^.Widget,GTK_TYPE_MENU_ITEM) then
8260        begin
8261          gtk_menu_bar_append({$IFDEF Gtk1}
8262                              PGtkMenuBar(GetStyleWidget(lgsMenuBar)),
8263                              {$ELSE}
8264                              GetStyleWidget(lgsMenuBar),
8265                              {$ENDIF}
8266                              StyleObject^.Widget);
8267        end
8268        else
8269{$ifdef gtk2}
8270        if GtkWidgetIsA(StyleObject^.Widget, GTK_TYPE_TOOL_BUTTON) then
8271        begin
8272          //gtk_toolbar_insert();
8273          gtk_toolbar_append_widget(GTK_TOOLBAR(GetStyleWidget(lgsToolBar)),
8274            StyleObject^.Widget, nil, nil);
8275        end
8276        else
8277{$endif}
8278        if (lgs = lgsToolButton) or
8279           (lgs = lgsTooltip) then
8280        begin
8281          // already on a parent => nothing to do
8282        end
8283        else
8284        begin
8285          StyleWindowWidget:=GetStyleWidget(lgsWindow);
8286          // add widget on client area of form
8287          WindowFixedWidget:=PGTKWidget(
8288                 gtk_object_get_data(PGtkObject(StyleWindowWidget),'fixedwidget'));
8289          //DebugLn('GetStyleWithName adding on hidden stylewindow ',WName,' ',GetWidgetDebugReport(StyleObject^.Widget));
8290          if WindowFixedWidget <> nil then
8291            gtk_fixed_put(PGtkFixed(WindowFixedWidget),StyleObject^.Widget,10,10);
8292        end;
8293      end;
8294
8295      gtk_widget_set_name(StyleObject^.Widget,PChar(WidgetName));
8296      gtk_widget_ensure_style(StyleObject^.Widget);
8297
8298      // request default sizing
8299      FillChar(Requisition,SizeOf(Requisition),0);
8300      gtk_widget_size_request(StyleObject^.Widget, @Requisition);
8301
8302      StyleObject^.Style:=gtk_widget_get_style(StyleObject^.Widget);
8303      // ToDo: find out, why sometimes the style is not initialized.
8304      // for example: why the following occurs:
8305      if CompareText(WName,'button')=0 then begin
8306        if StyleObject^.Style^.light_gc[GTK_STATE_NORMAL]=nil then begin
8307          //DebugLn('GetStyleWithName ',WName);
8308        end;
8309      end;
8310      if AddToStyleWindow then begin
8311        if not GtkWidgetIsA(StyleObject^.Widget,GTK_WINDOW_GET_TYPE) then begin
8312          //DebugLn(['GetStyleWithName realizing ...']);
8313          gtk_widget_realize(StyleObject^.Widget);
8314          //DebugLn('AddToStyleWindow realized: ',WName,' ',GetWidgetDebugReport(StyleObject^.Widget));
8315        end;
8316        ResizeWidget(StyleObject^.Widget,200,200);
8317      end;
8318    end;
8319
8320    // increase refcount of style
8321    if StyleObject^.Style <> nil then
8322      if CompareText(WName,LazGtkStyleNames[lgsGTK_Default])<>0 then
8323        StyleObject^.Style := GTK_Style_Ref(StyleObject^.Style);
8324
8325    // if successful add to style objects list
8326    if StyleObject^.Style <> nil then
8327    begin
8328      Styles.AddObject(WName, TObject(StyleObject));
8329      if lgs <> lgsUserDefined then
8330        StandardStyles[lgs] := StyleObject;
8331      Result := StyleObject^.Style;
8332      UpdateSysColorMap(StyleObject^.Widget, lgs);
8333
8334      // ToDo: create all gc of the style
8335      //gtk_widget_set_rc_style(StyleObject^.Widget);
8336
8337      if lgs = lgsTooltip then
8338        gtk_widget_hide_all(StyleObject^.Widget);
8339    end
8340    else begin
8341      // no success, clean up
8342      FreeStyleObject(StyleObject);
8343      DebugLn('WARNING: GetStyleWithName ',WName,' failed');
8344    end;
8345
8346    // clean up
8347    if Tp <> nil then
8348      gtk_object_destroy(Tp);
8349  end;
8350end;
8351
8352function GetStyleWidget(aStyle: TLazGtkStyle) : PGTKWidget;
8353begin
8354  if aStyle in [lgsUserDefined] then
8355    RaiseGDBException('');// user styles are defined by name
8356  if StandardStyles[aStyle]<>nil then
8357    // already created
8358    Result:=StandardStyles[aStyle]^.Widget
8359  else
8360    // create it
8361    Result:=GetStyleWidgetWithName(LazGtkStyleNames[aStyle]);
8362end;
8363
8364function GetStyleWidgetWithName(const WName : String) : PGTKWidget;
8365var
8366  l : Longint;
8367begin
8368  Result := nil;
8369  // init style
8370  GetStyleWithName(WName);
8371  // return widget
8372  l:=IndexOfStyleWithName(WName);
8373  if l>=0 then
8374    Result := PStyleObject(Styles.Objects[l])^.Widget;
8375end;
8376
8377{------------------------------------------------------------------------------
8378  Function: LoadDefaultFont(Desc)
8379  Params: none
8380  Returns: Returns the default Font
8381
8382  For Text/Font Routines: if the Font is invalid, this can be used instead, or
8383  if the DT_internal flag is used(aka use system font) this is used. This is
8384  also the font returned by GetStockObject(SYSTEM_FONT).
8385
8386  It attempts to get the font from the default Style, or if none is available,
8387  a new style(aka try and get GTK builtin values), if that fails tries to get
8388  a generic fixed font, if THAT fails, it gets whatever font is available.
8389  If the result is not nil it MUST be GDK_FONT_UNREF'd when done.
8390 ------------------------------------------------------------------------------}
8391function LoadDefaultFont: TGtkIntfFont;
8392{$IFDEF Gtk1}
8393var
8394  Style : PGTKStyle;
8395{$ENDIF}
8396begin
8397  {$IFDEF Gtk2}
8398  Result:=gtk_widget_create_pango_layout(GetStyleWidget(lgsdefault), nil);
8399  {$ELSE Gtk1}
8400  Result := nil;
8401  Style := GetStyle(lgsDefault);
8402  if Style = nil then
8403    Style := GetStyle(lgsGTK_Default);
8404  if Style <> nil then begin
8405    Result := Style^.Font;
8406    if Result = nil then
8407      {$IFNDEF NoStyle}
8408      if (Style^.RC_Style <> nil) then begin
8409        if (Style^.RC_Style^.font_name <> nil) then
8410          Result := gdk_font_load(Style^.RC_Style^.font_name);
8411      end;
8412      {$ENDIF}
8413  end;
8414
8415  If Result = nil then
8416    Result := gdk_fontset_load('-*-fixed-*-*-*-*-*-120-*-*-*-*-*-*');
8417  if Result = nil then
8418    Result := gdk_fontset_load('-*-*-*-*-*-*-*-*-*-*-*-*-*-*');
8419  {$ENDIF}
8420
8421  If Result <> nil then
8422    ReferenceGtkIntfFont(Result);
8423end;
8424
8425{$Ifdef GTK2}
8426function LoadDefaultFontDesc: PPangoFontDescription;
8427var
8428  Style : PGTKStyle;
8429begin
8430  Result := nil;
8431
8432  {$IFDEF VerboseGtkToDos}{$WARNING ToDo LoadDefaultFontDesc: get a working default pango font description}{$ENDIF}
8433  Result := pango_font_description_from_string('sans 12');
8434
8435  exit;
8436
8437  Style := GetStyle(lgsLabel);
8438  if Style = nil then
8439    Style := GetStyle(lgsDefault);
8440  if Style = nil then
8441    Style := GetStyle(lgsGTK_Default);
8442
8443  If (Style <> nil) then begin
8444    Result := pango_font_description_copy(Style^.font_desc);
8445  end;
8446
8447  If Result = nil then
8448    Result := pango_font_description_from_string('sans 12');
8449
8450  if Result = nil then
8451    Result := pango_font_description_from_string('12');
8452end;
8453{$ENDIF}
8454
8455function GetDefaultFontName: string;
8456var
8457  Style: PGtkStyle;
8458  {$IFDEF GTK2}
8459  PangoFontDesc: PPangoFontDescription;
8460  {$ELSE}
8461  p,t: pchar;
8462  AFont: PGdkFont;
8463  {$ENDIF}
8464begin
8465  Result:='';
8466  Style := GetStyle(lgsDefault);
8467  if Style = nil then
8468    Style := GetStyle(lgsGTK_Default);
8469
8470  If Style <> nil then begin
8471    {$IFDEF GTK1}
8472      {$IFNDEF NoStyle}
8473      if (Style^.RC_Style <> nil) then
8474        with style^.RC_Style^ do begin
8475          if (font_name <> nil) then
8476            Result := font_name;
8477          if (Result='') and (fontset_name<>nil) then
8478          begin
8479            // fontset_name it's usually a comma separated list of font names
8480            // try to get the first valid font.
8481            p := fontset_name;
8482            while p<>nil do begin
8483              t := strscan(p, ',');
8484              if t=nil then
8485                result := p
8486              else begin
8487                result := copy(p, 1, t-p);
8488                while (t<>nil) and (t^ in [',',' ',#9,#10,#13]) do
8489                  inc(t);
8490              end;
8491              AFont := gdk_font_load(pchar(result));
8492              if AFont<>nil then begin
8493                gdk_font_unref(AFont);
8494                {$IFDEF VerboseFonts}
8495                debugln('DefaultFont found in fontset: ',result);
8496                {$ENDIF}
8497                break;
8498              end;
8499              p := t;
8500            end;
8501          end;
8502        end;
8503      {$ENDIF}
8504    {$ENDIF}
8505    {$IFDEF GTK2}
8506    If (Style <> nil) then begin
8507      PangoFontDesc := Style^.font_desc;
8508      if PangoFontDesc<>nil then begin
8509        Result:=pango_font_description_get_family(PangoFontDesc);
8510      end;
8511    end;
8512    {$ENDIF}
8513  end;
8514  {$IFDEF VerboseFonts}
8515  DebugLn('GetDefaultFontName: DefaultFont=',result);
8516  {$ENDIF}
8517end;
8518
8519procedure RealizeGDKColor(ColorMap: PGdkColormap; Color: PGDKColor);
8520var
8521  AllocResult: gboolean;
8522begin
8523  if ColorMap=nil then ColorMap:=gdk_colormap_get_system;
8524  if (Color^.pixel = 0)
8525  and ((Color^.red<>0) or (Color^.blue<>0) or (Color^.green<>0)) then
8526    gdk_colormap_alloc_colors(ColorMap, Color, 1, false, true, @AllocResult)
8527  else
8528    gdk_colormap_query_color(ColorMap, Color^.pixel, Color);
8529end;
8530
8531procedure RealizeGtkStyleColor(Style: PGTKStyle; Color: PGDKColor);
8532begin
8533  if (Style<>nil) then
8534    RealizeGDKColor(Style^.ColorMap,Color)
8535  else
8536    RealizeGDKColor(nil,Color);
8537end;
8538
8539function GetSysGCValues(Color: TColorRef;
8540  ThemeWidget: PGtkWidget): TGDKGCValues;
8541// ThemeWidget can be nil
8542
8543  function GetWidgetWithBackgroundWindow(Widget: PGtkWidget): PGtkWidget;
8544  // returns the gtk widget which has the background gdk window
8545  var
8546    WindowOwnerWidget: PGtkWidget;
8547  begin
8548    Result:=Widget;
8549    if Result=nil then exit;
8550    if Result^.window=nil then exit;
8551    gdk_window_get_user_data(Result^.window,PGPointer(@WindowOwnerWidget));
8552    Result:=WindowOwnerWidget;
8553    if Result=nil then exit;
8554  end;
8555
8556var
8557  Style: PGTKStyle;
8558  GC: PGDKGC;
8559  Pixmap: PGDKPixmap;
8560  SysColor: TColorRef;
8561  BaseColor: TColorRef;
8562  Red, Green, Blue: byte;
8563begin
8564  // Set defaults in case something goes wrong
8565  FillChar(Result, SizeOf(Result), 0);
8566  Style := nil;
8567  GC := nil;
8568  Pixmap := nil;
8569
8570  SysColor := ColorToRGB(Color);
8571  Result.Fill := GDK_Solid;
8572  RedGreenBlue(TColor(SysColor), Red, Green, Blue);
8573  Result.foreground.Red:=gushort(Red) shl 8 + Red;
8574  Result.foreground.Green:=gushort(Green) shl 8 + Green;
8575  Result.foreground.Blue:=gushort(Blue) shl 8 + Blue;
8576
8577  {$IfDef Disable_GC_SysColors}
8578  exit;
8579  {$EndIf}
8580  BaseColor := Color and $FF;
8581  case BaseColor of
8582    {These are WM/X defined, but might be possible to get
8583
8584    COLOR_CAPTIONTEXT
8585    COLOR_INACTIVECAPTIONTEXT}
8586
8587    {These Are incompatible or WM defined
8588
8589    COLOR_ACTIVECAPTION
8590    COLOR_INACTIVECAPTION
8591    COLOR_GRADIENTACTIVECAPTION
8592    COLOR_GRADIENTINACTIVECAPTION
8593    COLOR_WINDOWFRAME
8594    COLOR_ACTIVEBORDER
8595    COLOR_INACTIVEBORDER}
8596
8597    COLOR_BACKGROUND:
8598      begin
8599        Style := GetStyle(lgsDefault);
8600        if Style = nil then
8601          Style := GetStyle(lgsWindow);
8602        if Style = nil then
8603          exit;
8604        Pixmap := Style^.bg_pixmap[GTK_STATE_NORMAL];
8605        if Pixmap <> nil then
8606        begin
8607          Result.Fill := GDK_Tiled;
8608          Result.Tile := Pixmap;
8609        end
8610        else
8611        begin
8612          GC := Style^.bg_gc[GTK_STATE_NORMAL];
8613          if GC = nil then
8614          begin
8615            Result.Fill := GDK_Solid;
8616            Result.foreground := Style^.bg[GTK_STATE_PRELIGHT];
8617          end
8618          else
8619            GDK_GC_Get_Values(GC, @Result);
8620        end;
8621      end;
8622
8623    COLOR_INFOBK :
8624      begin
8625        Style := GetStyle(lgsTooltip);
8626        if Style = nil then
8627          Style := GetStyle(lgsWindow);
8628        if Style = nil then
8629          exit;
8630
8631        Pixmap := Style^.bg_pixmap[GTK_STATE_NORMAL];
8632        if Pixmap <> nil then
8633        begin
8634          Result.Fill := GDK_Tiled;
8635          Result.Tile := Pixmap;
8636        end
8637        else
8638        begin
8639          GC := Style^.bg_gc[GTK_STATE_NORMAL];
8640          if GC = nil then
8641          begin
8642            Result.Fill := GDK_Solid;
8643            {$IFDEF Gtk1}
8644            Result.foreground := Style^.bg[GTK_STATE_PRELIGHT];
8645            {$ELSE}
8646            Result.foreground := Style^.bg[GTK_STATE_NORMAL];
8647            {$ENDIF}
8648          end
8649          else
8650            GDK_GC_Get_Values(GC, @Result);
8651        end;
8652      end;
8653
8654    COLOR_INFOTEXT :
8655      begin
8656        Style := GetStyle(lgsTooltip);
8657
8658        if Style = nil then
8659          Style := GetStyle(lgsWindow);
8660
8661        if Style = nil then
8662          exit;
8663
8664        GC := Style^.fg_gc[GTK_STATE_NORMAL];
8665        if GC = nil then
8666        begin
8667          Result.Fill := GDK_Solid;
8668          Result.foreground := Style^.fg[GTK_STATE_NORMAL];
8669        end
8670        else
8671          GDK_GC_Get_Values(GC, @Result);
8672      end;
8673
8674    COLOR_FORM,
8675    COLOR_MENU,
8676    COLOR_SCROLLBAR,
8677    COLOR_BTNFACE :
8678      begin
8679        case BaseColor of
8680          COLOR_FORM: Style := GetStyle(lgsWindow);
8681          COLOR_BTNFACE: Style := GetStyle(lgsButton);
8682          COLOR_MENU: Style := GetStyle(lgsMenu);
8683          COLOR_SCROLLBAR: Style := GetStyle(lgsHorizontalScrollbar);
8684        end;
8685        if Style = nil then
8686          exit;
8687        Pixmap := Style^.bg_pixmap[GTK_STATE_NORMAL];
8688        if Pixmap <> nil then
8689        begin
8690          Result.Fill := GDK_Tiled;
8691          Result.Tile := Pixmap;
8692        end else
8693        begin
8694          GC := Style^.bg_gc[GTK_STATE_NORMAL];
8695          if GC = nil then
8696          begin
8697            Result.Fill := GDK_Solid;
8698            Result.foreground := Style^.bg[GTK_STATE_NORMAL];
8699          end
8700          else
8701            GDK_GC_Get_Values(GC, @Result);
8702        end;
8703      end;
8704
8705    COLOR_3DDKSHADOW,
8706    COLOR_BTNSHADOW :
8707      begin
8708        Style := GetStyle(lgsButton);
8709        if Style = nil then
8710          exit;
8711        GC := Style^.dark_gc[GTK_STATE_NORMAL];
8712        if GC = nil then
8713        begin
8714          Result.Fill := GDK_Solid;
8715          Result.foreground := Style^.dark[GTK_STATE_NORMAL];
8716        end
8717        else
8718          GDK_GC_Get_Values(GC, @Result);
8719      end;
8720
8721    COLOR_GRAYTEXT :
8722      begin
8723        Style := GetStyle(lgsDefault);
8724        if Style = nil then
8725          exit;
8726        GC := Style^.text_gc[GTK_STATE_INSENSITIVE];
8727        if GC = nil then
8728        begin
8729          Result.Fill := GDK_Solid;
8730          Result.foreground := Style^.text[GTK_STATE_NORMAL];
8731        end else
8732          GDK_GC_Get_Values(GC, @Result);
8733      end;
8734
8735    COLOR_MENUTEXT,
8736    COLOR_BTNTEXT :
8737      begin
8738        case BaseColor of
8739          COLOR_BTNTEXT : Style := GetStyle(lgsButton);
8740          COLOR_MENUTEXT : Style := GetStyle(lgsMenuitem);
8741        end;
8742        if Style = nil then
8743          exit;
8744        GC := Style^.fg_gc[GTK_STATE_NORMAL];
8745        if GC = nil then
8746        begin
8747          Result.Fill := GDK_Solid;
8748          Result.foreground := Style^.fg[GTK_STATE_NORMAL];
8749        end
8750        else
8751          GDK_GC_Get_Values(GC, @Result);
8752      end;
8753
8754    COLOR_WINDOWTEXT:
8755      begin
8756        Style := GetStyle(lgsDefault);
8757        if Style = nil then
8758          exit;
8759        GC := Style^.text_gc[GTK_STATE_NORMAL];
8760        if GC = nil then
8761        begin
8762          Result.Fill := GDK_Solid;
8763          Result.foreground := Style^.text[GTK_STATE_NORMAL];
8764        end
8765        else
8766          GDK_GC_Get_Values(GC, @Result);
8767      end;
8768
8769    COLOR_3DLIGHT,
8770    COLOR_BTNHIGHLIGHT :
8771      begin
8772        Style := GetStyle(lgsButton);
8773        if Style = nil then
8774          exit;
8775        GC := Style^.light_gc[GTK_STATE_NORMAL];
8776        if GC = nil then
8777        begin
8778          Result.Fill := GDK_Solid;
8779          Result.foreground := Style^.light[GTK_STATE_NORMAL];
8780        end
8781        else
8782          GDK_GC_Get_Values(GC, @Result);
8783      end;
8784
8785    COLOR_WINDOW :
8786      begin
8787        Style := GetStyle(lgsList);
8788        if Style = nil then
8789          exit;
8790        GC := Style^.base_gc[GTK_STATE_NORMAL];
8791        if (GC = nil) then
8792        begin
8793          Result.Fill := GDK_Solid;
8794          if Style^.base[GTK_STATE_NORMAL].Pixel<>0 then
8795          begin
8796            Result.foreground := Style^.base[GTK_STATE_NORMAL];
8797            Result.background := Style^.base[GTK_STATE_NORMAL];
8798          end;
8799        end
8800        else
8801          GDK_GC_Get_Values(GC, @Result);
8802      end;
8803
8804    COLOR_HIGHLIGHT :
8805      begin
8806        Style := GetStyle(lgsDefault);
8807        if Style = nil then
8808          exit;
8809        GC := Style^.bg_gc[GTK_STATE_SELECTED];
8810        if GC = nil then
8811        begin
8812          Result.Fill := GDK_Solid;
8813          Result.foreground := Style^.bg[GTK_STATE_SELECTED];
8814        end
8815        else
8816          GDK_GC_Get_Values(GC, @Result);
8817      end;
8818
8819    COLOR_HIGHLIGHTTEXT :
8820      begin
8821        Style := GetStyle(lgsDefault);
8822        if Style = nil then
8823          exit;
8824        {$IFDEF Gtk1}
8825        GC := Style^.bg_gc[GTK_STATE_PRELIGHT];
8826        {$ELSE}
8827        GC := Style^.text_gc[GTK_STATE_SELECTED];
8828        {$ENDIF}
8829        if GC = nil then
8830        begin
8831          Result.Fill := GDK_Solid;
8832          Result.foreground := Style^.bg[GTK_STATE_PRELIGHT];
8833        end
8834        else
8835          GDK_GC_Get_Values(GC, @Result);
8836      end;
8837
8838    {?????????????
8839    COLOR_HOTLIGHT :
8840      begin
8841      end;
8842    ?????????????}
8843
8844    {?????????????????
8845    COLOR_APPWORKSPACE :
8846      begin
8847      end;
8848    ?????????????????}
8849  end;
8850
8851  RealizeGtkStyleColor(Style, @Result.foreground);
8852end;
8853
8854function StyleForegroundColor(Color: TColorRef;
8855  DefaultColor: PGDKColor): PGDKColor;
8856var
8857  style : PGTKStyle;
8858begin
8859  style := nil;
8860  Result := DefaultColor;
8861
8862  Case TColor(Color) of
8863    clINFOTEXT :
8864      begin
8865        Style := GetStyle(lgsTooltip);
8866
8867        If Style = nil then
8868          exit;
8869
8870        Result := @Style^.fg[GTK_STATE_NORMAL];
8871      end;
8872
8873    cl3DDKSHADOW,
8874    clBTNSHADOW :
8875      begin
8876        Style := GetStyle(lgsButton);
8877        If Style = nil then
8878          exit;
8879        Result := @Style^.dark[GTK_STATE_NORMAL];
8880      end;
8881
8882    clGRAYTEXT :
8883      begin
8884        Style := GetStyle(lgsDefault);
8885        If Style = nil then
8886          exit;
8887        Result := @Style^.text[GTK_STATE_INSENSITIVE];
8888      end;
8889
8890    clMENUTEXT,
8891    clBTNTEXT :
8892      begin
8893        Case TColor(Color) of
8894          clBTNTEXT : Style := GetStyle(lgsButton);
8895          clMENUTEXT : Style := GetStyle(lgsMenuitem);
8896        end;
8897        If Style = nil then
8898          exit;
8899        Result := @Style^.fg[GTK_STATE_NORMAL];
8900      end;
8901
8902    clWINDOWTEXT:
8903      begin
8904        Style := GetStyle(lgsDefault);
8905        If Style = nil then
8906          exit;
8907        Result := @Style^.text[GTK_STATE_NORMAL];
8908      end;
8909
8910    cl3DLIGHT,
8911    clBTNHIGHLIGHT :
8912      begin
8913        Style := GetStyle(lgsButton);
8914        If Style = nil then
8915          exit;
8916        Result := @Style^.light[GTK_STATE_NORMAL];
8917      end;
8918
8919    clHIGHLIGHTTEXT :
8920      begin
8921        DebugLn(['StyleForegroundColor clHIGHLIGHTTEXT']);
8922        Style := GetStyle(lgsDefault);
8923        If Style = nil then
8924          exit;
8925        Result := @Style^.text[GTK_STATE_PRELIGHT];
8926        DebugLn(['StyleForegroundColor clHIGHLIGHTTEXT 2 ',Result<>nil]);
8927      end;
8928  end;
8929
8930  If Result = nil then
8931    Result := DefaultColor;
8932
8933  if (Result <> nil) and (Result <> DefaultColor) then
8934    RealizeGtkStyleColor(Style,Result);
8935end;
8936
8937function GetStyleGroupboxFrameBorders: TRect;
8938const s = 200;
8939var
8940  StyleObject: PStyleObject;
8941  allocation: TGtkAllocation;
8942  FrameWidget: PGtkFrame;
8943  f: TRect;
8944begin
8945  GetStyleWidget(lgsGroupBox);
8946  StyleObject:=StandardStyles[lgsGroupBox];
8947  if not StyleObject^.FrameBordersValid then begin
8948    allocation.x:=0;
8949    allocation.y:=0;
8950    allocation.width:=s;
8951    allocation.height:=s;
8952    gtk_widget_size_allocate(StyleObject^.Widget,@allocation);
8953    FrameWidget:=pGtkFrame(StyleObject^.Widget);
8954    {$IFDEF Gtk1}
8955    allocation:=FrameWidget^.bin.child^.allocation;
8956    {$ELSE}
8957    GTK_FRAME_GET_CLASS(FrameWidget)^.compute_child_allocation(
8958      FrameWidget,@allocation);
8959    {$ENDIF}
8960    //DebugLn(['GetStyleGroupboxFrame BBB2 ',dbgs(allocation)]);
8961    f.Left:=Min(s,Max(0,allocation.x));
8962    f.Top:=Min(s,Max(0,allocation.y));
8963    f.Right:=Max(0,Min(s-f.Left,s-allocation.x-allocation.width));
8964    f.Bottom:=Max(0,Min(s-f.Top,s-allocation.x-allocation.width));
8965    StyleObject^.FrameBorders:=f;
8966    //DebugLn(['GetStyleGroupboxFrame FrameBorders=',dbgs(StyleObject^.FrameBorders)]);
8967    StyleObject^.FrameBordersValid:=true;
8968  end;
8969  Result:=StyleObject^.FrameBorders;
8970end;
8971
8972function GetStyleNotebookFrameBorders: TRect;
8973const s = 400;
8974var
8975  StyleObject: PStyleObject;
8976  allocation: TGtkAllocation;
8977  f: TRect;
8978  PageWidget: PGtkWidget;
8979begin
8980  GetStyleWidget(lgsNotebook);
8981  StyleObject:=StandardStyles[lgsNotebook];
8982  if not StyleObject^.FrameBordersValid then begin
8983    allocation.x:=0;
8984    allocation.y:=0;
8985    allocation.width:=s;
8986    allocation.height:=s;
8987    gtk_widget_size_allocate(StyleObject^.Widget,@allocation);
8988    PageWidget:=gtk_notebook_get_nth_page(PGtkNoteBook(StyleObject^.Widget),0);
8989    //DebugLn(['GetStyleNotebookFrameBorders BBB2 ',dbgs(allocation)]);
8990    allocation:=PageWidget^.allocation;
8991    f.Left:=Min(s,Max(0,allocation.x));
8992    f.Top:=Min(s,Max(0,allocation.y));
8993    f.Right:=Max(0,Min(s-f.Left,s-allocation.x-allocation.width));
8994    f.Bottom:=Max(0,Min(s-f.Top,s-allocation.x-allocation.width));
8995    StyleObject^.FrameBorders:=f;
8996    //DebugLn(['GetStyleNotebookFrameBorders FrameBorders=',dbgs(StyleObject^.FrameBorders)]);
8997    StyleObject^.FrameBordersValid:=true;
8998  end;
8999  Result:=StyleObject^.FrameBorders;
9000end;
9001
9002{$IFDEF Gtk2}
9003function GetStyleFormFrameBorders(WithMenu: boolean): TRect;
9004const s = 400;
9005var
9006  StyleObject: PStyleObject;
9007  allocation: TGtkAllocation;
9008  f: TRect;
9009  InnerWidget: PGtkWidget;
9010  Outer: TGdkRectangle;
9011  Inner: TGdkRectangle;
9012begin
9013  GetStyleWidget(lgsMenu);
9014  StyleObject:=StandardStyles[lgsWindow];
9015  if not StyleObject^.FrameBordersValid then begin
9016    allocation.x:=0;
9017    allocation.y:=0;
9018    allocation.width:=s;
9019    allocation.height:=s;
9020    gtk_widget_size_allocate(StyleObject^.Widget,@allocation);
9021    InnerWidget:=PGTKWidget(
9022          gtk_object_get_data(PGtkObject(StyleObject^.Widget),'fixedwidget'));
9023    allocation:=InnerWidget^.allocation;
9024    //DebugLn(['GetStyleFormFrameBorders BBB2 ',dbgs(allocation),' WithMenu=',WithMenu,' ClientWidget=',GetWidgetDebugReport(InnerWidget)]);
9025    f.Left:=Min(s,Max(0,allocation.x));
9026    f.Top:=Min(s,Max(0,allocation.y));
9027    f.Right:=Max(0,Min(s-f.Left,s-allocation.x-allocation.width));
9028    f.Bottom:=Max(0,Min(s-f.Top,s-allocation.x-allocation.width));
9029    StyleObject^.FrameBorders:=f;
9030    //DebugLn(['GetStyleFormFrameBorders FrameBorders=',dbgs(StyleObject^.FrameBorders)]);
9031    StyleObject^.FrameBordersValid:=true;
9032  end;
9033
9034  if WithMenu then begin
9035    InnerWidget:=PGTKWidget(
9036                 gtk_object_get_data(PGtkObject(StyleObject^.Widget),'vbox'));
9037  end else begin
9038    InnerWidget:=PGTKWidget(
9039          gtk_object_get_data(PGtkObject(StyleObject^.Widget),'fixedwidget'));
9040  end;
9041  Outer:=StyleObject^.Widget^.allocation;
9042  Inner:=InnerWidget^.allocation;
9043  Result.Left:=Min(Outer.width,Max(0,Inner.x));
9044  Result.Top:=Min(Outer.height,Max(0,Inner.y));
9045  Result.Right:=Max(0,Min(Outer.width-f.Left,Outer.width-Inner.x-Inner.width));
9046  Result.Bottom:=Max(0,Min(Outer.height-f.Top,Outer.height-Inner.x-Inner.width));
9047  //DebugLn(['GetStyleFormFrameBorders BBB3 Inner=',dbgs(Inner),' Outer=',dbgs(Outer),' WithMenu=',WithMenu,' InnerWidget=',GetWidgetDebugReport(InnerWidget),' Result=',dbgs(Result)]);
9048end;
9049{$ENDIF}
9050
9051procedure StyleFillRectangle(drawable : PGDKDrawable; GC : PGDKGC;
9052  Color : TColorRef; x, y, width, height : gint);
9053var
9054  style: PGTKStyle;
9055  widget: PGTKWidget;
9056  state: TGTKStateType;
9057  shadow: TGtkShadowType;
9058  detail: pgchar;
9059begin
9060  style := nil;
9061  shadow := GTK_SHADOW_NONE;
9062  state := GTK_STATE_NORMAL;
9063
9064  case TColor(Color) of
9065   { clMenu:
9066      begin
9067        Style := GetStyle('menuitem');
9068        widget := GetStyleWidget('menuitem');
9069        detail := 'menuitem';
9070      end;
9071
9072    clBtnFace :
9073      begin
9074        Style := GetStyle('button');
9075        widget := GetStyleWidget('button');
9076        detail := 'button';
9077      end;
9078
9079    clWindow :
9080      begin
9081        Style := GetStyle('default');
9082        widget := GetStyleWidget('default');
9083        detail := 'list';
9084      end;   }
9085
9086    clBackground:
9087      begin
9088        Style := GetStyle(lgsWindow);
9089        widget := GetStyleWidget(lgsWindow);
9090        detail := 'window';
9091      end;
9092
9093    clInfoBk :
9094      begin
9095        Style := GetStyle(lgsToolTip);
9096        Widget := GetStyleWidget(lgsToolTip);
9097        shadow := GTK_SHADOW_OUT;
9098        detail := 'tooltip';
9099      end;
9100
9101    clForm :
9102      begin
9103        Style := GetStyle(lgsWindow);
9104        widget := GetStyleWidget(lgsWindow);
9105        detail := 'window';
9106      end;
9107  end;
9108
9109  if Assigned(Style) then
9110    gtk_paint_flat_box(style, drawable, state, shadow, nil, widget,
9111                       detail, x, y, width, height)
9112  else
9113    gdk_draw_rectangle(drawable, GC, 1, x, y, width, height);
9114end;
9115
9116procedure UpdateWidgetStyleOfControl(AWinControl: TWinControl);
9117var
9118  RCStyle : PGtkRCStyle;
9119  Widget, FixWidget : PGTKWidget;
9120  MainWidget: PGtkWidget;
9121  FreeFontName: boolean;
9122  FreeFontSetName: boolean;
9123
9124  procedure CreateRCStyle;
9125  begin
9126    if RCStyle=nil then
9127      RCStyle:=gtk_rc_style_new;
9128  end;
9129
9130  procedure SetRCFont(FontGdiObject: PGdiObject);
9131  {$IFDEF GTK1}
9132  var
9133    FontDesc: TGtkFontCacheDescriptor;
9134  {$ENDIF}
9135  begin
9136    {$IFDEF GTK1}
9137    CreateRCStyle;
9138    FontDesc:=FontCache.FindADescriptor(FontGdiObject^.GDIFontObject);
9139    if (FontDesc<>nil) and (FontDesc.xlfd<>'') then begin
9140      RCStyle:=gtk_rc_style_new;
9141      g_free(RCStyle^.font_name);
9142      RCStyle^.font_name:=g_strdup(PChar(FontDesc.xlfd));
9143      g_free(RCStyle^.fontset_name);
9144      RCStyle^.fontset_name:=g_strdup(PChar(FontDesc.xlfd));
9145      FreeFontName:=true;
9146
9147      //DebugLn('UpdateWidgetStyleOfControl.SetRCFont ',DbgSName(AWinControl),' Widget=',GetWidgetDebugReport(Widget),' Style=',GetWidgetStyleReport(Widget));
9148    end;
9149    {$ENDIF}
9150  end;
9151
9152begin
9153  {$IFDEF NoStyle}
9154  exit;
9155  {$ENDIF}
9156
9157  if not AWinControl.HandleAllocated then exit;
9158
9159  MainWidget:=PGtkWidget(AWinControl.Handle);
9160  FixWidget:=GetFixedWidget(MainWidget);
9161  if (FixWidget <> nil) and (FixWidget <> MainWidget) then
9162    Widget := FixWidget
9163  else
9164    Widget := MainWidget;
9165
9166  RCStyle:=nil;
9167  FreeFontName:=false;
9168  FreeFontSetName:=false;
9169  try
9170    // set default background
9171    if (AWinControl.Color=clNone) then
9172    begin
9173      // clNone => remove default background
9174      if (FixWidget<>nil) and (FixWidget^.Window<>nil) then
9175      begin
9176        gdk_window_set_back_pixmap(FixWidget^.Window, nil, GdkFalse);
9177      end;
9178    end
9179    else
9180    if not IsColorDefault(AWinControl) and ((AWinControl.Color and SYS_COLOR_BASE)=0) then
9181    begin
9182      // set background to user defined color
9183
9184      // don't set background for custom controls, which paint themselves
9185      // (this prevents flickering)
9186      if (csOpaque in AWinControl.ControlStyle)
9187      and GtkWidgetIsA(MainWidget,GTKAPIWidget_Type) then exit;
9188
9189      {for i:=0 to 4 do begin
9190        RCStyle^.bg[i]:=NewColor;
9191
9192        // Indicate which colors the GtkRcStyle will affect;
9193        // unflagged colors will follow the theme
9194        RCStyle^.color_flags[i]:=
9195          RCStyle^.color_flags[i] or GTK_RC_BG;
9196      end;}
9197
9198      //DebugLn('UpdateWidgetStyleOfControl ',DbgSName(AWinControl),' Color=',DbgS(AWinControl.Color));
9199    end;
9200
9201    {if (AWinControl is TCustomForm) then begin
9202      gdk_window_set_back_pixmap(FixWidget^.Window,nil,GdkFalse);
9203
9204      NewColor:=TColorToTGDKColor(clRed);
9205
9206      CreateRCStyle;
9207      for i:=0 to 4 do begin
9208        debugln('UpdateWidgetStyleOfControl i=',dbgs(i),' ',RCStyle^.bg_pixmap_name[i],' ',RCStyle^.Name);
9209        RCStyle^.bg[i]:=NewColor;
9210
9211        // Indicate which colors the GtkRcStyle will affect;
9212        // unflagged colors will follow the theme
9213        RCStyle^.color_flags[i]:=
9214          RCStyle^.color_flags[i] or GTK_RC_BG;
9215      end;
9216    end;}
9217
9218    // set font color
9219
9220    // set font (currently only TCustomLabel)
9221    if (GtkWidgetIsA(Widget,gtk_label_get_type)
9222    or GtkWidgetIsA(Widget,gtk_editable_get_type)
9223    or GtkWidgetIsA(Widget,gtk_check_button_get_type))
9224    and (not AWinControl.Font.IsDefault)
9225    then begin
9226       // allocate font (just read it)
9227       if AWinControl.Font.Reference.Handle=0 then ;
9228    end;
9229
9230  finally
9231    if RCStyle<>nil then begin
9232      //DebugLn('UpdateWidgetStyleOfControl Apply Modifications ',AWinControl.Name,' ',GetWidgetClassName(Widget));
9233      gtk_widget_modify_style(Widget,RCStyle);
9234
9235      if FreeFontName then begin
9236        {$ifdef gtk1}
9237        g_free(RCStyle^.font_name);
9238        RCStyle^.font_name:=nil;
9239        {$else}
9240        pango_font_description_free(RCStyle^.font_desc);
9241        RCStyle^.font_desc:=nil;
9242        {$endif}
9243      end;
9244      if FreeFontSetName then begin
9245        {$ifdef gtk1}
9246        g_free(RCStyle^.fontset_name);
9247        RCStyle^.fontset_name:=nil;
9248        {$endif}
9249      end;
9250      //DebugLn('UpdateWidgetStyleOfControl END ',DbgSName(AWinControl),' Widget=',GetWidgetDebugReport(Widget),' Style=',GetWidgetStyleReport(Widget));
9251      gtk_rc_style_unref(RCStyle);
9252    end;
9253  end;
9254end;
9255
9256{-------------------------------------------------------------------------------
9257  Creates a new PChar. Deletes escaping ampersands, replaces the first single
9258  ampersand with an underscore and deletes all other single ampersands.
9259-------------------------------------------------------------------------------}
9260function Ampersands2Underscore(Src: PChar) : PChar;
9261var
9262  s: String;
9263begin
9264  s := StrPas(Src);
9265  s := Ampersands2Underscore(s);
9266  Result := StrAlloc(Length(s)+1); // +1 for #0 char at end
9267  strcopy(Result, PChar(s));
9268end;
9269
9270{-------------------------------------------------------------------------------
9271  Deletes escaping ampersands, replaces the first single
9272  ampersand with an underscore and deletes all other single ampersands.
9273-------------------------------------------------------------------------------}
9274function Ampersands2Underscore(const ASource: String): String;
9275var
9276  n: Integer;
9277  FirstFound: Boolean;
9278begin
9279  //TODO: escape underscores
9280  FirstFound := False;
9281  Result := ASource;
9282  n := 1;
9283  while n <= Length(Result) do
9284  begin
9285    if Result[n] = '&' then
9286    begin
9287      if FirstFound
9288      or ( (n < Length(Result)) and (Result[n+1] = '&') ) // got &&
9289      then begin
9290        Delete(Result, n, 1);
9291        if not FirstFound then
9292          Inc(n);               // Skip the second & of &&
9293      end
9294      else begin
9295        FirstFound := True;
9296        Result[n] := '_';
9297      end;
9298    end;
9299    Inc(n);
9300  end;
9301end;
9302
9303{-------------------------------------------------------------------------------
9304  function RemoveAmpersands(Src: PChar; LineLength : Longint) : PChar;
9305
9306  Creates a new PChar removing all escaping ampersands.
9307-------------------------------------------------------------------------------}
9308function RemoveAmpersands(Src: PChar; LineLength : Longint) : PChar;
9309var
9310  i, j: Longint;
9311  ShortenChars, NewLength, SrcLength: integer;
9312begin
9313  // count ampersands and find first ampersand
9314  ShortenChars:= 0;  // chars to delete
9315  SrcLength:= LineLength;
9316
9317  { Look for amperands. If found, check if it is an escaped ampersand.
9318    If it is, don't count it in. }
9319  i:=0;
9320  while i<SrcLength do begin
9321    if Src[i] = '&' then begin
9322      if (i < SrcLength - 1) and (Src[i+1] = '&') then begin
9323        // escaping ampersand found
9324        inc(ShortenChars);
9325        inc(i,2);
9326        Continue;
9327      end
9328      else
9329        inc(ShortenChars);
9330    end;
9331    inc(i);
9332  end;
9333  // create new PChar
9334  NewLength:= SrcLength - ShortenChars;
9335
9336  Result:=StrAlloc(NewLength+1); // +1 for #0 char at end
9337
9338  // copy string without ampersands
9339  i:=0;
9340  j:=0;
9341  while (j < NewLength) do begin
9342    if Src[i] <> '&' then begin
9343      // copy normal char
9344      Result[j]:= Src[i];
9345    end else begin
9346      // ampersand
9347      if (i < (SrcLength - 1)) and (Src[i+1] = '&') then begin
9348        // escaping ampersand found
9349        inc(i);
9350        Result[j]:='&';
9351      end else
9352        // delete single ampersand
9353        dec(j);
9354    end;
9355    Inc(i);
9356    Inc(j);
9357  end;
9358  Result[NewLength]:=#0;
9359end;
9360
9361{-------------------------------------------------------------------------------
9362  function RemoveAmpersands(const ASource: String): String;
9363
9364  Removing all escaping ampersands.
9365-------------------------------------------------------------------------------}
9366function RemoveAmpersands(const ASource: String): String;
9367var
9368  n: Integer;
9369begin
9370  Result := ASource;
9371  n := 1;
9372  while n <= Length(Result) do
9373  begin
9374    if Result[n] = '&'
9375    then begin
9376      if (n < Length(Result))
9377      and (Result[n + 1] = '&')
9378      then begin
9379        // we got a &&, remove the first
9380        Delete(Result, n, 1);
9381        Inc(n);
9382        Continue;
9383      end;
9384      // simply remove it
9385      Delete(Result, n, 1);
9386      Continue;
9387    end;
9388    Inc(n);
9389  end;
9390end;
9391
9392{-------------------------------------------------------------------------------
9393  procedure LabelFromAmpersands(var AText, APattern: String; var AAccelChar: Char)
9394
9395  Removes all escaping ampersands &&, creates an underscore pattern and returns
9396  the first ampersand char as accelerator char
9397-------------------------------------------------------------------------------}
9398procedure LabelFromAmpersands(var AText, APattern: String; var AAccelChar: Char);
9399var
9400  n: Integer;
9401  FirstFound: Boolean;
9402begin
9403  FirstFound := False;
9404  APattern := StringOfChar(' ', Length(AText));
9405  AAccelChar := #0;
9406  n := 1;
9407  while n <= Length(AText) do
9408  begin
9409    case AText[n] of
9410      '&': begin
9411        if (n < Length(AText))
9412        and (AText[n + 1] = '&')
9413        then begin
9414          // we got a &&, remove the first
9415          Delete(AText, n, 1);
9416          Delete(APattern, n, 1);
9417          Inc(n);
9418        end else begin
9419          Delete(AText, n, 1);
9420          Delete(APattern, n, 1);
9421          if FirstFound
9422          then Continue; // simply remove it
9423
9424          // if we are here it's our first
9425          FirstFound := True;
9426          AAccelChar := System.lowerCase(AText[n]);
9427          // is there a next char we can underline ?
9428          if n <= Length(APattern)
9429          then APattern[n] := '_';
9430        end;
9431      end;
9432      '_': begin
9433        AText[n] := ' ';
9434        APattern[n] := '_';
9435      end;
9436    end;
9437    Inc(n);
9438  end;
9439end;
9440
9441
9442{-------------------------------------------------------------------------------
9443  function GetTextExtentIgnoringAmpersands(TheFont: PGDKFont;
9444    Str : PChar; StrLength: integer;
9445    MaxWidth: Longint; lbearing, rbearing, width, ascent, descent : Pgint);
9446
9447  Gets text extent of a string, ignoring escaped Ampersands.
9448  That means, ampersands are not counted.
9449  Negative MaxWidth means no limit.
9450-------------------------------------------------------------------------------}
9451procedure GetTextExtentIgnoringAmpersands(TheFont: TGtkIntfFont;
9452  Str : PChar; StrLength: integer;
9453  lbearing, rbearing, width, ascent, descent : Pgint);
9454var
9455  NewStr : PChar;
9456  i: integer;
9457begin
9458  NewStr:=Str;
9459  // first check if Str contains an ampersand:
9460  if (Str<>nil) then begin
9461    i:=0;
9462    while (Str[i]<>'&') and (i<StrLength) do inc(i);
9463    if i<StrLength then begin
9464      NewStr := RemoveAmpersands(Str, StrLength);
9465      StrLength:=StrLen(NewStr);
9466    end;
9467  end;
9468  gdk_text_extents(TheFont, NewStr, StrLength,
9469                   lbearing, rBearing, width, ascent, descent);
9470  if NewStr<>Str then
9471    StrDispose(NewStr);
9472end;
9473
9474{------------------------------------------------------------------------------
9475  function FontIsDoubleByteCharsFont(TheFont: TGtkIntfFont): boolean;
9476
9477  This is only a heuristic
9478 ------------------------------------------------------------------------------}
9479function FontIsDoubleByteCharsFont(TheFont: TGtkIntfFont): boolean;
9480var
9481  SingleCharLen, DoubleCharLen: integer;
9482begin
9483  {$IFDEF Gtk1}
9484  SingleCharLen:=gdk_text_width(TheFont, 'A', 1);
9485  DoubleCharLen:=gdk_text_width(TheFont, #0'A', 2);
9486  {$ELSE}
9487  pango_layout_set_single_paragraph_mode(TheFont, TRUE);
9488  pango_layout_set_width(TheFont, -1);
9489  pango_layout_set_text(TheFont, 'A', 1);
9490  pango_layout_get_pixel_size(TheFont, @SingleCharLen, nil);
9491  pango_layout_set_text(TheFont, #0'A', 2);
9492  pango_layout_get_pixel_size(TheFont, @DoubleCharLen, nil);
9493  {$ENDIF}
9494  Result:=(SingleCharLen=0) and (DoubleCharLen>0);
9495end;
9496
9497{------------------------------------------------------------------------------
9498  function FontIsMonoSpaceFont(TheFont: TGtkIntfFont): boolean;
9499
9500  This is only a heuristic
9501 ------------------------------------------------------------------------------}
9502function FontIsMonoSpaceFont(TheFont: TGtkIntfFont): boolean;
9503var
9504  {$IFDEF Gtk1}
9505  SingleCharLen: LongInt;
9506  {$ENDIF}
9507  MWidth: LongInt;
9508  IWidth: LongInt;
9509begin
9510  {$IFDEF Gtk1}
9511  SingleCharLen:=gdk_text_width(TheFont, 'A', 1);
9512  if SingleCharLen=0 then begin
9513    // assume a double byte character font
9514    MWidth:=gdk_text_width(TheFont, '#0m', 2);
9515    IWidth:=gdk_text_width(TheFont, '#0i', 2);
9516  end else begin
9517    // assume a single byte character font
9518    MWidth:=gdk_text_width(TheFont, 'm', 1);
9519    IWidth:=gdk_text_width(TheFont, 'i', 1);
9520  end;
9521  {$ELSE}
9522  pango_layout_set_single_paragraph_mode(TheFont, TRUE);
9523  pango_layout_set_width(TheFont, -1);
9524  pango_layout_set_text(TheFont, 'm', 1);
9525  pango_layout_get_pixel_size(TheFont, @MWidth, nil);
9526  pango_layout_set_text(TheFont, 'i', 1);
9527  pango_layout_get_pixel_size(TheFont, @IWidth, nil);
9528  {$ENDIF}
9529  Result:=MWidth=IWidth;
9530end;
9531
9532{------------------------------------------------------------------------------
9533  Method:   GDKPixel2GDIRGB
9534  Params:
9535            Pixel - a GDK Pixel, refers to Index in Colormap/Visual
9536            Visual - a GDK Visual, if nil, the System Default is used
9537            Colormap - a GDK Colormap, if nil, the System Default is used
9538  Returns:  TGDIRGB
9539
9540  A convenience function for use with GDK Image's. It takes a pixel value
9541  retrieved from gdk_image_get_pixel, and uses the passed Visual and Colormap
9542  to try and look up actual RGB values.
9543 ------------------------------------------------------------------------------}
9544function GDKPixel2GDIRGB(Pixel: Longint; Visual: PGDKVisual;
9545  Colormap: PGDKColormap) : TGDIRGB;
9546var
9547  Color: TGDKColor;
9548begin
9549  FillChar(Result, SizeOf(TGDIRGB),0);
9550
9551  If (Visual = nil) or (Colormap = nil) then begin
9552    Visual := GDK_Visual_Get_System;
9553    Colormap := GDK_Colormap_Get_System;
9554  end;
9555
9556  gdk_colormap_query_color(colormap, pixel, @color);
9557
9558  Result.Red := Color.Red shr 8;
9559  Result.Green := Color.Green shr 8;
9560  Result.Blue := Color.Blue shr 8;
9561end;
9562
9563{------------------------------------------------------------------------------
9564  function GetWindowDecorations(AForm : TCustomForm) : Longint;
9565
9566 ------------------------------------------------------------------------------}
9567function GetWindowDecorations(AForm : TCustomForm) : Longint;
9568var
9569  ABorderStyle: TFormBorderStyle;
9570begin
9571  Result := 0;
9572
9573  if not (csDesigning in AForm.ComponentState) then
9574    ABorderStyle:=AForm.BorderStyle
9575  else
9576    ABorderStyle:=bsSizeable;
9577
9578  {$IFDEF Gtk2}
9579
9580  case ABorderStyle of
9581    bsNone: Result := 0;
9582
9583    bsSingle: Result := GDK_DECOR_TITLE or
9584                 GDK_DECOR_MENU or GDK_DECOR_MINIMIZE or
9585                 GDK_DECOR_MAXIMIZE;
9586
9587    bsSizeable: Result := GDK_DECOR_BORDER or GDK_DECOR_TITLE or
9588                 GDK_DECOR_MENU or GDK_DECOR_MINIMIZE or GDK_DECOR_MAXIMIZE
9589                 or GDK_DECOR_RESIZEH;
9590
9591    bsDialog: Result := GDK_DECOR_BORDER or GDK_DECOR_TITLE or
9592                 GDK_DECOR_MENU or GDK_DECOR_MINIMIZE;
9593
9594    bsToolWindow: Result := GDK_DECOR_TITLE or GDK_DECOR_MENU;
9595
9596    bsSizeToolWin: Result := GDK_DECOR_BORDER or GDK_DECOR_TITLE or
9597                   GDK_DECOR_MENU or GDK_DECOR_RESIZEH;
9598  end;
9599
9600  if not (csDesigning in AForm.ComponentState) then
9601  begin
9602    if not (biMinimize in AForm.BorderIcons) then
9603      Result := Result and not GDK_DECOR_MINIMIZE;
9604    if not (biMaximize in AForm.BorderIcons) then
9605      Result := Result and not GDK_DECOR_MAXIMIZE;
9606    if not (biSystemMenu in AForm.BorderIcons) then
9607      Result := Result and not GDK_DECOR_MENU;
9608  end;
9609
9610  {$ELSE}
9611  case ABorderStyle of
9612    bsNone : Result := 0;
9613
9614    bsSingle : Result := GDK_DECOR_TITLE or
9615                 GDK_DECOR_MENU or GDK_DECOR_MINIMIZE or
9616                 GDK_DECOR_MAXIMIZE;
9617
9618    bsSizeable : Result := GDK_DECOR_BORDER or GDK_DECOR_TITLE or
9619                 GDK_DECOR_MENU or GDK_DECOR_MINIMIZE or GDK_DECOR_MAXIMIZE
9620                 or GDK_DECOR_RESIZEH;
9621
9622    bsDialog : Result := GDK_DECOR_BORDER or GDK_DECOR_TITLE or
9623                 GDK_DECOR_MENU or GDK_DECOR_MINIMIZE;
9624
9625    bsToolWindow : Result := GDK_DECOR_TITLE or GDK_DECOR_MENU;
9626
9627    bsSizeToolWin :Result := GDK_DECOR_BORDER or GDK_DECOR_TITLE or
9628                   GDK_DECOR_MENU or GDK_DECOR_RESIZEH;
9629  end;
9630  {$ENDIF}
9631
9632  //DebugLn('GetWindowDecorations ',DbgSName(AForm),' ',dbgs(ord(ABorderStyle)),' ',binStr(Result,8));
9633end;
9634
9635{------------------------------------------------------------------------------
9636  function GetWindowFunction(AForm : TCustomForm) : Longint;
9637
9638 ------------------------------------------------------------------------------}
9639function GetWindowFunction(AForm : TCustomForm) : Longint;
9640var
9641  ABorderStyle: TFormBorderStyle;
9642begin
9643  Result:=0;
9644  if not (csDesigning in AForm.ComponentState) then
9645    ABorderStyle:=AForm.BorderStyle
9646  else
9647    ABorderStyle:=bsSizeable;
9648
9649  {$IFDEF Gtk2}
9650  case ABorderStyle of
9651    bsNone : Result := GDK_FUNC_RESIZE or GDK_FUNC_CLOSE {$ifndef windows}or GDK_FUNC_MOVE{$endif};
9652
9653    bsSingle : Result := GDK_FUNC_MOVE or GDK_FUNC_MINIMIZE or GDK_FUNC_CLOSE;
9654
9655    bsSizeable : Result := GDK_FUNC_RESIZE or GDK_FUNC_MOVE or
9656                GDK_FUNC_MINIMIZE or GDK_FUNC_CLOSE or GDK_FUNC_MAXIMIZE;
9657
9658    bsDialog : Result := GDK_FUNC_CLOSE or GDK_FUNC_MINIMIZE
9659                or GDK_FUNC_MOVE;
9660
9661    bsToolWindow : Result := GDK_FUNC_MOVE or GDK_FUNC_CLOSE;
9662
9663    bsSizeToolWin : Result := GDK_FUNC_RESIZE or GDK_FUNC_MOVE or GDK_FUNC_CLOSE;
9664  end;
9665
9666  // X warns if marking a fixed size window resizeable:
9667  if ((AForm.Constraints.MinWidth>0)
9668  and (AForm.Constraints.MinWidth=AForm.Constraints.MaxWidth))
9669  or ((AForm.Constraints.MinHeight>0)
9670  and (AForm.Constraints.MinHeight=AForm.Constraints.MaxHeight)) then
9671    Result:=Result-GDK_FUNC_RESIZE;
9672
9673  if (not (csDesigning in AForm.ComponentState)) then
9674  begin
9675    if not (biMinimize in AForm.BorderIcons) then
9676      Result:=Result and not GDK_FUNC_MINIMIZE;
9677    if not (biMaximize in AForm.BorderIcons) then
9678      Result:=Result and not GDK_FUNC_MAXIMIZE;
9679  end;
9680  {$ELSE}
9681  case ABorderStyle of
9682    bsNone : Result := GDK_FUNC_RESIZE or GDK_FUNC_CLOSE;
9683
9684    bsSingle : Result := GDK_FUNC_MOVE or GDK_FUNC_MINIMIZE or GDK_FUNC_CLOSE;
9685
9686    bsSizeable : Result := GDK_FUNC_RESIZE or GDK_FUNC_MOVE or
9687                GDK_FUNC_MINIMIZE or GDK_FUNC_CLOSE or GDK_FUNC_MAXIMIZE;
9688
9689    bsDialog : Result := GDK_FUNC_CLOSE or GDK_FUNC_MINIMIZE
9690                or GDK_FUNC_MOVE;
9691
9692    bsToolWindow : Result := GDK_FUNC_MOVE or GDK_FUNC_CLOSE;
9693
9694    bsSizeToolWin : Result := GDK_FUNC_RESIZE or GDK_FUNC_MOVE or GDK_FUNC_CLOSE;
9695  end;
9696
9697  // X warns if marking a fixed size window resizeable:
9698  if ((AForm.Constraints.MinWidth>0)
9699  and (AForm.Constraints.MinWidth=AForm.Constraints.MaxWidth))
9700  or ((AForm.Constraints.MinHeight>0)
9701  and (AForm.Constraints.MinHeight=AForm.Constraints.MaxHeight)) then
9702    Result:=Result-GDK_FUNC_RESIZE;
9703  {$ENDIF}
9704
9705  //DebugLn('GetWindowFunction ',DbgSName(AForm),' ',dbgs(ord(ABorderStyle)),' ',binStr(Result,8));
9706end;
9707
9708procedure FillScreenFonts(ScreenFonts : TStrings);
9709var
9710  {$ifdef gtk1}
9711  theFonts : PPChar;
9712  {$else}
9713  Widget : PGTKWidget;
9714  Context : PPangoContext;
9715  families : PPPangoFontFamily;
9716  {$endif}
9717  Tmp: AnsiString;
9718  I, N: Integer;
9719begin
9720  ScreenFonts.Clear;
9721  {$ifdef gtk1}
9722  theFonts := XListFonts(gdk_display,PChar('-*-*-*-*-*-*-*-*-*-*-*-*-*-*'), 10000, @N);
9723  debugln('FillScreenFonts N=',dbgs(N));
9724  for I := 0 to N - 1 do
9725    if theFonts[I] <> nil then begin
9726      Tmp := ExtractFamilyFromXLFDName(theFonts[I]);
9727      if Tmp <> '' then
9728        if ScreenFonts.IndexOf(Tmp) < 0 then
9729          ScreenFonts.Append(Tmp);
9730    end;
9731   XFreeFontNames(theFonts);
9732  {$else}
9733   Widget := GetStyleWidget(lgsDefault);
9734   if Widget = nil then begin
9735     exit;//raise an error here I guess
9736   end;
9737   Context := gtk_widget_get_pango_context(Widget);
9738   if Context = nil then begin
9739     exit;//raise an error here I guess
9740   end;
9741   families := nil;
9742   pango_context_list_families(Context, @families, @n);
9743
9744   for I := 0 to N - 1 do
9745     if families[I] <> nil then begin
9746       Tmp := StrPas(pango_font_family_get_name(families[I]));
9747       if Tmp <> '' then
9748         if ScreenFonts.IndexOf(Tmp) < 0 then
9749           ScreenFonts.Append(Tmp);
9750    end;
9751   if (families <> nil) then
9752     g_free(families);
9753  {$endif gtk2}
9754end;
9755
9756function GetTextHeight(DCTextMetric: TDevContextTextMetric): integer;
9757// IMPORTANT: Before this call:  UpdateDCTextMetric(TGtkDeviceContext(DC));
9758begin
9759  {$IfDef Win32}
9760  Result := DCTextMetric.TextMetric.tmHeight div 2;
9761  {$Else}
9762  Result := DCTextMetric.TextMetric.tmAscent;
9763  {$EndIf}
9764end;
9765
9766{$IFDEF GTK1}
9767{ Compile with UseXinerama defined to use the Xinerama extension to avoid dialog
9768  boxes straddling two monitors. This is only required for GTK1, as it is built
9769  into GTK2. The Xinerama library is not always available, so the libraries will
9770  be dynamically loaded. (A single monitor is assumed if the load fails.) On
9771  some systems only a static Xinerama library is available, so define
9772  StaticXinerama also. MAC OSX is in this latter category, but it crashed the
9773  X server when I tried it on a real two monitor display.
9774}
9775{$IFDEF UseXinerama}
9776{$IFDEF StaticXinerama}
9777{$LINKLIB Xinerama}
9778{$ENDIF}
9779var
9780  FirstScreenCalled: Boolean = False;
9781  FirstScreenResult: Boolean = False;
9782{ Copy record definition from Xinerama unit.
9783  Can't use the unit itself, as it forces the executable to
9784  refer to the libraray }
9785type
9786   TXineramaScreenInfo = record
9787        screen_number : cint;
9788        x_org         : cshort;
9789        y_org         : cshort;
9790        width         : cshort;
9791        height        : cshort;
9792     end;
9793   PXineramaScreenInfo = ^TXineramaScreenInfo;
9794
9795function GetFirstScreen: Boolean;
9796var
9797  nMonitors: cint;
9798  XineramaScreenInfo: PXineramaScreenInfo;
9799  opcode, firstevent, firsterror: cint;
9800  XineramaLib: TLibHandle;
9801  pXineramaIsActive: function (dpy: PDisplay):TBool;cdecl;
9802  pXineramaQueryScreens: function (dpy: PDisplay;
9803    number: Pcint): PXineramaScreenInfo;cdecl;
9804begin
9805  if not FirstScreenCalled then begin
9806    if XQueryExtension(gdk_display, 'XINERAMA', @opcode, @firstevent,
9807      @firsterror)
9808    then begin
9809      XineramaLib := {$IFDEF StaticXinerama} 1 {Flag present} {$ELSE} LoadLibrary('libXinerama.so') {$ENDIF};
9810      if XineramaLib <> 0 then begin
9811        {$IFDEF StaticXinerama}
9812          Pointer(pXineramaIsActive) := @XineramaIsActive;
9813          Pointer(pXineramaQueryScreens) := @XineramaQueryScreens;
9814        {$ELSE}
9815          Pointer(pXineramaIsActive) :=
9816                            GetProcAddress(XineramaLib, 'XineramaIsActive');
9817          Pointer(pXineramaQueryScreens) :=
9818                            GetProcAddress(XineramaLib, 'XineramaQueryScreens');
9819        {$ENDIF}
9820        if (pXineramaIsActive <> nil) and (pXineramaQueryScreens <> nil) and
9821          pXineramaIsActive(gdk_display)
9822        then begin
9823          XineramaScreenInfo := pXineramaQueryScreens(gdk_display, @nMonitors);
9824          if XineramaScreenInfo <> nil then begin
9825            if (nMonitors > 0) and (nMonitors < 10) then begin
9826              FirstScreen.x := XineramaScreenInfo^.width;
9827              FirstScreen.y := XineramaScreenInfo^.height;
9828              FirstScreenResult := True;
9829            end;
9830            XFree(XineramaScreenInfo);
9831          end;
9832        end;
9833        // Do not FreeLibrary(XineramaLib) because it causes the X11 library to
9834        // crash on exit
9835      end;
9836    end;
9837    FirstScreenCalled := True;
9838  end;
9839  Result := FirstScreenResult;
9840end;
9841{$ENDIF UseXinerama}
9842{$ENDIF Gtk1}
9843
9844{$IFDEF HasX}
9845function  XGetWorkarea(var ax,ay,awidth,aheight:gint): gint;
9846
9847var
9848  XDisplay: PDisplay;
9849  XScreen: PScreen;
9850  XWindow: TWindow;
9851  AtomType: x.TAtom;
9852  Format: gint;
9853  nitems: gulong;
9854  bytes_after: gulong;
9855  current_desktop: pguint;
9856  res   : Integer;
9857begin
9858  Result := -1;
9859  xdisplay := gdk_display;
9860  xscreen := XDefaultScreenOfDisplay(xdisplay);
9861  xwindow := XRootWindowOfScreen(xscreen);
9862  res:=XGetWindowProperty (xdisplay, xwindow,
9863             XInternAtom(xdisplay, '_NET_WORKAREA', false),
9864             0, MaxInt, False, XA_CARDINAL, @atomtype, @format, @nitems,
9865             @bytes_after, gpointer(@current_desktop));
9866  if (atomtype = XA_CARDINAL) and (format = 32) and  (nitems > 0) then begin
9867    result:=res;
9868    ax:=current_desktop[0];
9869    ay:=current_desktop[1];
9870    awidth:=current_desktop[2];
9871    aheight:=current_desktop[3];
9872  end;
9873  if current_desktop <> nil then
9874    XFree (current_desktop);
9875end;
9876{$ENDIF}
9877
9878function FindFocusWidget(AWidget: PGtkWidget): PGtkWidget;
9879var
9880  WinWidgetInfo: PWinWidgetInfo;
9881  ImplWidget: PGtkWidget;
9882  GList: PGlist;
9883  LastFocusWidget: PGtkWidget;
9884begin
9885  // Default to the widget, try to find other
9886  Result := AWidget;
9887
9888  // Combo
9889  if GtkWidgetIsA(AWidget, gtk_combo_get_type)
9890  then begin
9891    // handle is a gtk combo
9892    {$IfDef VerboseFocus}
9893    DebugLn('  D taking gtkcombo entry');
9894    {$EndIf}
9895    Result := PgtkWidget(PGtkCombo(AWidget)^.entry);
9896    Exit;
9897  end;
9898
9899  // check if widget has a WinWidgetInfo record
9900  WinWidgetInfo := GetWidgetInfo(AWidget, false);
9901  if WinWidgetInfo = nil then Exit;
9902
9903  ImplWidget:= WinWidgetInfo^.CoreWidget;
9904  if ImplWidget = nil then Exit;
9905  // set default to the implementation widget
9906  Result := ImplWidget;
9907
9908  // handle has an ImplementationWidget
9909  if GtkWidgetIsA(ImplWidget, gtk_list_get_type)
9910  then begin
9911    {$IfDef VerboseFocus}
9912    DebugLn('  E using list');
9913    {$EndIf}
9914    // Try the last added selected
9915    if not (selection_mode(PGtkList(ImplWidget)^) in [GTK_SELECTION_SINGLE, GTK_SELECTION_BROWSE])
9916    and (PGtkList(ImplWidget)^.last_focus_child <> nil)
9917    then begin
9918      LastFocusWidget:=PGtkList(ImplWidget)^.last_focus_child;
9919      if g_list_find(PGtkList(ImplWidget)^.selection,LastFocusWidget)<>nil
9920      then begin
9921        Result := PGtkList(ImplWidget)^.last_focus_child;
9922        {$IfDef VerboseFocus}
9923        DebugLn('  E.1 using last_focus_child');
9924        {$EndIf}
9925        Exit;
9926      end;
9927    end;
9928
9929    // If there is a selection, try the first
9930    GList := PGtkList(ImplWidget)^.selection;
9931    if (GList <> nil) and (GList^.data <> nil)
9932    then begin
9933      Result := GList^.data;
9934      {$IfDef VerboseFocus}
9935      DebugLn('  E.2 using 1st selection');
9936      {$EndIf}
9937      Exit;
9938    end;
9939
9940    // If not in browse mode, set focus to the first child
9941    // in browsemode, the focused item cannot be selected by mouse
9942//      if selection_mode(PGtkList(ImplWidget)^) = GTK_SELECTION_BROWSE
9943//      then begin
9944//        {$IfDef VerboseFocus}
9945//        DebugLn('  E.3 Browse mode -> using ImplWidget');
9946//        {$EndIf}
9947//        Exit;
9948//      end;
9949
9950    GList := PGtkList(ImplWidget)^.children;
9951    if GList = nil then Exit;
9952    if GList^.Data = nil then Exit;
9953    Result := GList^.Data;
9954    {$IfDef VerboseFocus}
9955    DebugLn('  E.4 using 1st child');
9956    {$EndIf}
9957
9958    Exit;
9959  end;
9960
9961  {$IfDef VerboseFocus}
9962  DebugLn('  E taking ImplementationWidget');
9963  {$EndIf}
9964end;
9965
9966
9967{$IFDEF ASSERT_IS_ON}
9968  {$UNDEF ASSERT_IS_ON}
9969  {$C-}
9970{$ENDIF}
9971
9972// included by gtkproc.pp
9973