{
 *****************************************************************************
 *                               gtk3procs.pas                               *
 *                               -------------                               *
 *                                                                           *
 *                                                                           *
 *****************************************************************************

 *****************************************************************************
  This file is part of the Lazarus Component Library (LCL)

  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************
}
unit gtk3procs;
{$i gtk3defines.inc}
{$mode objfpc}{$H+}

interface

uses
  {$IFDEF UNIX}
  BaseUnix, Unix,
  {$ENDIF}
  Classes, SysUtils, Controls, StdCtrls, Graphics,
  LazGtk3, LazGdk3, LazGLib2, LazGObject2, LazGdkPixbuf2, LazPango1,
  LCLType, InterfaceBase;

type
  GType = TGType;
{$IFDEF UNIX}
  PPChildSignalEventHandler = ^PChildSignalEventHandler;
  PChildSignalEventHandler = ^TChildSignalEventHandler;
  TChildSignalEventHandler = record
    PID: TPid;
    UserData: PtrInt;
    OnEvent: TChildExitEvent;
    PrevHandler: PChildSignalEventHandler;
    NextHandler: PChildSignalEventHandler;
  end;
{$ENDIF}

  // styles -------------------------------------------------------------------

  TLazGtkStyle = (
    lgsGTK_Default, // without anything
    lgsDefault,     // with rc file
    lgsButton,
    lgsLabel,
    lgsWindow,
    lgsCheckbox,
    lgsRadiobutton,
    lgsMenu,
    lgsMenuBar,
    lgsMenuitem,
    lgsList,
    lgsVerticalScrollbar,
    lgsHorizontalScrollbar,
    lgsTooltip,
    lgsVerticalPaned,
    lgsHorizontalPaned,
    lgsNotebook,
    lgsStatusBar,
    lgsHScale,
    lgsVScale,
    lgsGroupBox,
    lgsTreeView,      // for gtk3
    lgsToolBar,       // toolbar
    lgsToolButton,    // button placed on toolbar
    lgsCalendar,      // button placed on toolbar
    lgsScrolledWindow,
    lgsMemo, // memo
    lgsFrame,
    // user defined
    lgsUserDefined
    );


  PStyleObject = ^TStyleObject;
  TStyleObject = record
    Style: PGTKStyle;
    Owner: PGtkWidget;  // The widget that we hold a reference to.
    Widget: PGTKWidget; // This is the style widget.
    FrameBordersValid: boolean;
    FrameBorders: TRect;
  end;

const
  SysColorMap: array [0..MAX_SYS_COLORS] of DWORD = (
    $C0C0C0,     {COLOR_SCROLLBAR}
    $808000,     {COLOR_BACKGROUND}
    $800000,     {COLOR_ACTIVECAPTION}
    $808080,     {COLOR_INACTIVECAPTION}
    $C0C0C0,     {COLOR_MENU}
    $FFFFFF,     {COLOR_WINDOW}
    $000000,     {COLOR_WINDOWFRAME}
    $000000,     {COLOR_MENUTEXT}
    $000000,     {COLOR_WINDOWTEXT}
    $FFFFFF,     {COLOR_CAPTIONTEXT}
    $C0C0C0,     {COLOR_ACTIVEBORDER}
    $C0C0C0,     {COLOR_INACTIVEBORDER}
    $808080,     {COLOR_APPWORKSPACE}
    $800000,     {COLOR_HIGHLIGHT}
    $FFFFFF,     {COLOR_HIGHLIGHTTEXT}
    $D0D0D0,     {COLOR_BTNFACE}
    $808080,     {COLOR_BTNSHADOW}
    $808080,     {COLOR_GRAYTEXT}
    $000000,     {COLOR_BTNTEXT}
    $C0C0C0,     {COLOR_INACTIVECAPTIONTEXT}
    $F0F0F0,     {COLOR_BTNHIGHLIGHT}
    $000000,     {COLOR_3DDKSHADOW}
    $C0C0C0,     {COLOR_3DLIGHT}
    $000000,     {COLOR_INFOTEXT}
    $AEF3F3,     {COLOR_INFOBK}
    $000000,     {unassigned}
    $000000,     {COLOR_HOTLIGHT}
    $800000,     {COLOR_GRADIENTACTIVECAPTION}
    $808080,     {COLOR_GRADIENTINACTIVECAPTION}
    $800000,     {COLOR_MENUHILIGHT}
    $D0D0D0,     {COLOR_MENUBAR}
    $D0D0D0      {COLOR_FORM}
  ); {end _SysColors}

  LazGtkStyleNames: array[TLazGtkStyle] of string = (
    'gtk_default',
    'default',
    'button',
    'label',
    'window',
    'checkbox',
    'radiobutton',
    'menu',
    'menubar',
    'menuitem',
    'list',
    'vertical scrollbar',
    'horizontal scrollbar',
    'tooltip',
    'vertical paned',
    'horizontal paned',
    'notebook',
    'statusbar',
    'hscale',
    'vscale',
    'groupbox',
    'treeview',
    'toolbar',
    'toolbutton',
    'calendar',
    'scrolled window',
    'memo',
    'frame',
    ''
    );

  NO_PROPAGATION_TO_PARENT = 127;
  GTK3_LEFT_BUTTON = 1;
  GTK3_MIDDLE_BUTTON = 2;
  GTK3_RIGHT_BUTTON = 3;

  G_TYPE_FUNDAMENTAL_SHIFT = 2;
  G_TYPE_FUNDAMENTAL_MAX = 255 shl G_TYPE_FUNDAMENTAL_SHIFT;

{ Constant fundamental types,
  introduced by g_type_init(). }
  G_TYPE_INVALID = GType(0 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_NONE = GType(1 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_INTERFACE = GType(2 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_CHAR = GType(3 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_UCHAR = GType(4 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_BOOLEAN = GType(5 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_INT = GType(6 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_UINT = GType(7 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_LONG = GType(8 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_ULONG = GType(9 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_INT64 = GType(10 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_UINT64 = GType(11 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_ENUM = GType(12 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_FLAGS = GType(13 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_FLOAT = GType(14 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_DOUBLE = GType(15 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_STRING = GType(16 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_POINTER = GType(17 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_BOXED = GType(18 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_PARAM = GType(19 shl G_TYPE_FUNDAMENTAL_SHIFT);
  G_TYPE_OBJECT = GType(20 shl G_TYPE_FUNDAMENTAL_SHIFT);


  GtkListItemGtkListTag = 'GtkList';
  GtkListItemLCLListTag = 'LCLList';

  AGtkJustification: array[TAlignment] of TGTKJustification =
  (
    0, {GTK_JUSTIFY_LEFT  taLeftJustify}
    1, {GTK_JUSTIFY_RIGHT taRightJustify}
    2 {GTK_JUSTIFY_CENTER taCenter}
  );

  AGtkJustificationF: array[TAlignment] of gfloat =
  (
    0.0, {GTK_JUSTIFY_LEFT  taLeftJustify}
    1.0, {GTK_JUSTIFY_RIGHT taRightJustify}
    0.5 {GTK_JUSTIFY_CENTER taCenter}
  );

  BorderStyleShadowMap: array[TBorderStyle] of TGtkShadowType =
  (
   0, {GTK_SHADOW_NONE, bsNone   }
   3 {GTK_SHADOW_ETCHED_IN bsSingle }
  );

  StaticBorderShadowMap: array[TStaticBorderStyle] of TGtkShadowType =
  (
    0, {GTK_SHADOW_NONE, sbsNone   }
    3, {GTK_SHADOW_ETCHED_IN sbsSingle }
    1  {GTK_SHADOW_IN sbsSunken}
  );

  MenuDirection : array[Boolean] of Longint = (
    0, {GTK_PACK_DIRECTION_LTR}
    1 {GTK_PACK_DIRECTION_RTL}
    );



  odnScrollArea = 'scroll_area'; // the gtk_scrolled_window of a widget
                                 // used by TCustomForm and TScrollbox
  odnScrollBar = 'ScrollBar'; // Gives the scrollbar the tgtkrange is belonging to
                              // Used by TScrollbar, TScrollbox and TWinApiWidget
  odnScrollBarLastPos = 'ScrollBarLastPos';

  // checklistbox states
  gtk3CLBState = 0; // byte
  gtk3CLBText = 1; // PGChar
  gtk3CLBDisabled = 3; // gboolean




function Gtk3IsObject(AWidget: PGObject): GBoolean;
function Gtk3IsButton(AWidget: PGObject): GBoolean;

function Gtk3IsCellView(AWidget: PGObject): GBoolean;
function Gtk3IsComboBox(AWidget: PGObject): GBoolean;
function Gtk3IsContainer(AWidget: PGObject): GBoolean;
function Gtk3IsEditable(AWidget: PGObject): GBoolean;
function Gtk3IsEntry(AWidget: PGObject): GBoolean;
function Gtk3IsTextView(AWidget: PGObject): GBoolean;

function Gtk3IsBox(AWidget: PGObject): GBoolean;
function Gtk3IsEventBox(AWidget: PGObject): GBoolean;
function Gtk3IsFixed(AWidget: PGObject): GBoolean;
function Gtk3IsLayout(AWidget: PGObject): GBoolean;

function Gtk3IsMenu(AWidget: PGObject): GBoolean;
function Gtk3IsMenuBar(AWidget: PGObject): GBoolean;
function Gtk3IsMenuItem(AWidget: PGObject): GBoolean;

function Gtk3IsNoteBook(AWidget: PGObject): GBoolean;
function Gtk3IsRadioMenuItem(AWidget: PGObject): GBoolean;

function Gtk3IsHScrollbar(AWidget: PGObject): GBoolean;
function Gtk3IsVScrollbar(AWidget: PGObject): GBoolean;

function Gtk3IsScrolledWindow(AWidget: PGObject): GBoolean;
function Gtk3IsSpinButton(AWidget: PGObject): GBoolean;
function Gtk3IsViewPort(AWidget: PGObject): GBoolean;
function Gtk3IsWidget(AWidget: PGObject): GBoolean;
function Gtk3IsGtkWindow(AWidget: PGObject): GBoolean;
function Gtk3IsGdkWindow(AWidget: PGObject): GBoolean;
function Gtk3IsGdkPixbuf(AWidget: PGObject): GBoolean;
function Gtk3IsGdkVisual(AVisual: PGObject): GBoolean;

function Gtk3IsPangoContext(APangoContext: PGObject): GBoolean;
function Gtk3IsPangoFontMetrics(APangoFontMetrics: PGObject): GBoolean;

function Gtk3TranslateScrollStyle(const SS: TScrollStyle): TPoint;
function Gtk3ScrollTypeToScrollCode(ScrollType: TGtkScrollType): LongWord;

function TGDKColorToTColor(const value : TGDKColor) : TColor;
function TColortoTGDKColor(const value : TColor) : TGDKColor;
function TGdkRGBAToTColor(const value : TGdkRGBA) : TColor;
function TColortoTGdkRGBA(const value : TColor; IgnoreAlpha: Boolean = True) : TGdkRGBA;
function ColorToCairoRGB(AColor: TColor; out ARed, AGreen, ABlue: Double): Boolean;
function RectFromGtkAllocation(AGtkAllocation: TGtkAllocation): TRect;
function RectFromGdkRect(AGdkRect: TGdkRectangle): TRect;
function RectFromPangoRect(APangoRect: TPangoRectangle): TRect;
function GdkRectFromRect(R: TRect): TGdkRectangle;
function GtkAllocationFromRect(R: TRect): TGtkAllocation;

function GdkKeyToLCLKey(AValue: Word): Word;
function GdkModifierStateToLCL(AState: TGdkModifierType; const AIsKeyEvent: Boolean): PtrInt;

procedure SetWindowCursor(AWindow: PGdkWindow; ACursor: HCursor;
  ARecursive: Boolean; ASetDefault: Boolean);
procedure SetGlobalCursor(Cursor: HCURSOR);

type
  Charsetstr = string[15];
  PCharSetEncodingRec=^TCharSetEncodingRec;
  TCharSetEncodingRec=record
    CharSet: byte;              // winapi charset value
    CharSetReg:CharSetStr;      // Charset Registry Pattern
    CharSetCod:CharSetStr;      // Charset Encoding Pattern
    EnumMap: boolean;           // this mapping is meanful when enumerating fonts?
    CharsetRegPart: boolean;    // is CharsetReg a partial pattern?
    CharsetCodPart: boolean;    // is CharsetCod a partial pattern?
  end;

var
  CharSetEncodingList: TList;
  StandardStyles: array[TLazGtkStyle] of PStyleObject;
  Styles: TStrings;



  procedure AddCharsetEncoding(CharSet: Byte; CharSetReg, CharSetCod: CharSetStr;
    ToEnum:boolean=true; CrPart:boolean=false; CcPart:boolean=false);
  procedure ClearCharsetEncodings;
  procedure CreateDefaultCharsetEncodings;

function GetStyleWidget(aStyle: TLazGtkStyle): PGtkWidget;
procedure ReleaseAllStyles;

implementation
uses LCLProc;

function TGdkRGBAToTColor(const value: TGdkRGBA): TColor;
begin
  Result := Trunc(value.red * $FF)
         or (Trunc(value.green * $FF) shl  8)
         or (Trunc(value.blue * $FF) shl  16)
         or (Trunc(value.alpha * $FF) shl  24);
end;

function TColortoTGdkRGBA(const value: TColor; IgnoreAlpha: Boolean = True): TGdkRGBA;
begin
  Result.red := (value and $FF) / 255;
  Result.green := ((value shr 8) and $FF) / 255;
  Result.blue := ((value shr 16) and $FF) / 255;
  if not IgnoreAlpha then
    Result.alpha := ((value shr 24) and $FF) / 255
  else
    Result.alpha:=1;
end;

function ColorToCairoRGB(AColor: TColor; out ARed, AGreen, ABlue: Double): Boolean;
begin
  Result := True;
  ARed := (AColor and $FF) / 255;
  AGreen := ((AColor shr 8) and $FF) / 255;
  ABlue := ((AColor shr 16) and $FF) / 255;
end;

function RectFromGtkAllocation(AGtkAllocation: TGtkAllocation): TRect;
begin
  with AGtkAllocation do
  begin
    Result.Left := x;
    Result.Top := y;
    Result.Right := Width + x;
    Result.Bottom := Height + y;
  end;
end;

function RectFromGdkRect(AGdkRect: TGdkRectangle): TRect;
begin
  with AGdkRect do
  begin
    Result.Left := x;
    Result.Top := y;
    Result.Right := Width + x;
    Result.Bottom := Height + y;
  end;
end;

function RectFromPangoRect(APangoRect: TPangoRectangle): TRect;
begin
  with APangoRect do
  begin
    Result.Left := x div PANGO_SCALE;
    Result.Top := y div PANGO_SCALE;
    Result.Right := (Width div PANGO_SCALE) + (x div PANGO_SCALE);
    Result.Bottom := (Height div PANGO_SCALE) + (y div PANGO_SCALE);
  end;
end;

function GdkRectFromRect(R: TRect): TGdkRectangle;
begin
  with Result do
  begin
    x := R.Left;
    y := R.Top;
    width := R.Right-R.Left;
    height := R.Bottom-R.Top;
  end;
end;

function GtkAllocationFromRect(R: TRect): TGtkAllocation;
begin
  with Result do
  begin
    x := R.Left;
    y := R.Top;
    width := R.Right-R.Left;
    height := R.Bottom-R.Top;
  end;
end;

function Gtk3IsObject(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and g_type_check_instance_is_a(PGTypeInstance(AWidget), g_object_get_type);
end;

function Gtk3IsButton(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_button_get_type);
end;

function Gtk3IsCellView(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_cell_view_get_type);
end;

function Gtk3IsComboBox(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_combo_box_get_type);
end;

function Gtk3IsEntry(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_entry_get_type);
end;

function Gtk3IsContainer(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_container_get_type);
end;

function Gtk3IsEditable(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_editable_get_type);
end;

function Gtk3IsTextView(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_text_view_get_type);
end;

function Gtk3IsBox(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_box_get_type);
end;

function Gtk3IsEventBox(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_event_box_get_type);
end;

function Gtk3IsFixed(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_fixed_get_type);
end;

function Gtk3IsLayout(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_layout_get_type);
end;

function Gtk3IsNoteBook(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_notebook_get_type);
end;

function Gtk3IsMenu(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_menu_get_type);
end;

function Gtk3IsMenuBar(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_menu_bar_get_type);
end;

function Gtk3IsMenuItem(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_menu_item_get_type);
end;

function Gtk3IsRadioMenuItem(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_radio_menu_item_get_type);
end;

function Gtk3IsHScrollbar(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_hscrollbar_get_type);
end;

function Gtk3IsVScrollbar(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_vscrollbar_get_type);
end;

function Gtk3IsScrolledWindow(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_scrolled_window_get_type);
end;

function Gtk3IsSpinButton(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_spin_button_get_type);
end;

function Gtk3IsViewPort(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_viewport_get_type);
end;

function Gtk3IsWidget(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_widget_get_type);
end;

function Gtk3IsGtkWindow(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gtk_window_get_type);
end;

function Gtk3IsGdkWindow(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gdk_window_get_type);
end;

function Gtk3IsGdkPixbuf(AWidget: PGObject): GBoolean;
begin
  Result := (AWidget <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AWidget), gdk_pixbuf_get_type);
end;

function Gtk3IsGdkVisual(AVisual: PGObject): GBoolean;
begin
  Result := (AVisual <> nil) and  g_type_check_instance_is_a(PGTypeInstance(AVisual), gdk_visual_get_type);
end;

function Gtk3IsPangoContext(APangoContext: PGObject): GBoolean;
begin
  Result := (APangoContext <> nil) and  g_type_check_instance_is_a(PGTypeInstance(APangoContext), pango_context_get_type);
end;

function Gtk3IsPangoFontMetrics(APangoFontMetrics: PGObject): GBoolean;
begin
  Result := (APangoFontMetrics <> nil);//  and  g_type_check_instance_is_a(PGTypeInstance(APangoFontMetrics), pango_font_metrics_get_type);
end;

function Gtk3TranslateScrollStyle(const SS: TScrollStyle): TPoint;
begin
  case SS of
    ssAutoBoth: Result:=Point(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    ssAutoHorizontal: Result:=Point(GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
    ssAutoVertical: Result:=Point(GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
    ssBoth: Result:=Point(GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
    ssHorizontal: Result:=Point(GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
    ssNone: Result:=Point(GTK_POLICY_NEVER, GTK_POLICY_NEVER);
    ssVertical: Result:=Point(GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
  end;
end;

function Gtk3ScrollTypeToScrollCode(ScrollType: TGtkScrollType): LongWord;
begin
  (*
  GTK_SCROLL_NONE: TGtkScrollType = 0;
  GTK_SCROLL_JUMP: TGtkScrollType = 1;
  GTK_SCROLL_STEP_BACKWARD: TGtkScrollType = 2;
  GTK_SCROLL_STEP_FORWARD: TGtkScrollType = 3;
  GTK_SCROLL_PAGE_BACKWARD: TGtkScrollType = 4;
  GTK_SCROLL_PAGE_FORWARD: TGtkScrollType = 5;
  GTK_SCROLL_STEP_UP: TGtkScrollType = 6;
  GTK_SCROLL_STEP_DOWN: TGtkScrollType = 7;
  GTK_SCROLL_PAGE_UP: TGtkScrollType = 8;
  GTK_SCROLL_PAGE_DOWN: TGtkScrollType = 9;
  GTK_SCROLL_STEP_LEFT: TGtkScrollType = 10;
  GTK_SCROLL_STEP_RIGHT: TGtkScrollType = 11;
  GTK_SCROLL_PAGE_LEFT: TGtkScrollType = 12;
  GTK_SCROLL_PAGE_RIGHT: TGtkScrollType = 13;
  GTK_SCROLL_START: TGtkScrollType = 14;
  GTK_SCROLL_END: TGtkScrollType = 15;
  *)
  case ScrollType of
    0{GTK_SCROLL_NONE}          : Result := SB_ENDSCROLL;
    1{GTK_SCROLL_JUMP}          : Result := SB_THUMBTRACK;
    2{GTK_SCROLL_STEP_BACKWARD} : Result := SB_LINELEFT;
    3{GTK_SCROLL_STEP_FORWARD}  : Result := SB_LINERIGHT;
    4{GTK_SCROLL_PAGE_BACKWARD} : Result := SB_PAGELEFT;
    5{GTK_SCROLL_PAGE_FORWARD}  : Result := SB_PAGERIGHT;
    6{GTK_SCROLL_STEP_UP}       : Result := SB_LINEUP;
    7{GTK_SCROLL_STEP_DOWN}     : Result := SB_LINEDOWN;
    8{GTK_SCROLL_PAGE_UP}       : Result := SB_PAGEUP;
    9{GTK_SCROLL_PAGE_DOWN}     : Result := SB_PAGEDOWN;
    10{GTK_SCROLL_STEP_LEFT}     : Result := SB_LINELEFT;
    11{GTK_SCROLL_STEP_RIGHT}    : Result := SB_LINERIGHT;
    12{GTK_SCROLL_PAGE_LEFT}     : Result := SB_PAGELEFT;
    13{GTK_SCROLL_PAGE_RIGHT}    : Result := SB_PAGERIGHT;
    14{GTK_SCROLL_START}         : Result := SB_TOP;
    15{GTK_SCROLL_END}           : Result := SB_BOTTOM;
  end;
end;

function TGDKColorToTColor(const value : TGDKColor) : TColor;
begin
  Result := ((Value.Blue shr 8) shl 16) + ((Value.Green shr 8) shl 8)
           + (Value.Red shr 8);
end;

function TColortoTGDKColor(const value : TColor) : TGDKColor;
begin
  if Value<0 then
  begin
    Result.blue := $FF;
    Result.red := $FF;
    Result.green := $FF;
    Result.pixel := 0;
    exit;
  end;
  with Result do
  begin
    pixel := 0;
    red   := (value and $ff) * 257;
    green := ((value shr 8) and $ff) * 257;
    blue  := ((value shr 16) and $ff) * 257;
  end;
end;

function GdkKeyToLCLKey(AValue: Word): Word;
begin
  if AValue <= $FF then
    exit(AValue);
  Result := VK_UNKNOWN;
  case AValue of
    GDK_KEY_Return,
    GDK_KEY_KP_Enter, GDK_KEY_3270_Enter: Result := VK_RETURN;
    GDK_KEY_Insert: Result := VK_INSERT;
    GDK_KEY_Delete: Result := VK_DELETE;
    GDK_KEY_BackSpace: Result := VK_BACK;
    GDK_KEY_Home: Result := VK_HOME;
    GDK_KEY_End: Result := VK_END;
    GDK_KEY_Page_Up: Result := VK_PRIOR;
    GDK_KEY_Page_Down: Result := VK_NEXT;
    GDK_KEY_Left: Result := VK_LEFT;
    GDK_KEY_Up: Result := VK_UP;
    GDK_KEY_Right: Result := VK_RIGHT;
    GDK_KEY_Down: Result := VK_DOWN;
    GDK_KEY_Menu: Result := VK_APPS;
    GDK_KEY_Tab, GDK_KEY_3270_BackTab, GDK_KEY_ISO_Left_Tab: Result := VK_TAB;
    GDK_KEY_Shift_L, GDK_KEY_Shift_R: Result := VK_SHIFT;
    GDK_KEY_Control_L, GDK_KEY_Control_R: Result := VK_CONTROL;
  end;
end;

function GdkModifierStateToLCL(AState: TGdkModifierType; const AIsKeyEvent: Boolean): PtrInt;
begin
  Result := 0;
  if AState and GDK_BUTTON1_MASK <> 0 then
    Result := Result or MK_LBUTTON;

  if AState and GDK_BUTTON2_MASK <> 0 then
    Result := Result or MK_RBUTTON;

  if AState and GDK_BUTTON3_MASK <> 0 then
    Result := Result or MK_MBUTTON;

  if AState and GDK_BUTTON4_MASK <> 0 then
    Result := Result or MK_XBUTTON1;

  if AState and GDK_BUTTON5_MASK <> 0 then
    Result := Result or MK_XBUTTON2;

  if AState and GDK_SHIFT_MASK <> 0 then
    Result := Result or MK_SHIFT;

  if AState and GDK_CONTROL_MASK <> 0 then
    Result := Result or MK_CONTROL;
end;

procedure AddCharsetEncoding(CharSet: Byte; CharSetReg, CharSetCod: CharSetStr;
  ToEnum:boolean=true; CrPart:boolean=false; CcPart:boolean=false);
var
  Rec: PCharsetEncodingRec;
begin
   New(Rec);
   Rec^.Charset := CharSet;
   Rec^.CharsetReg := CharSetReg;
   Rec^.CharsetCod := CharSetCod;
   Rec^.EnumMap := ToEnum;
   Rec^.CharsetRegPart := CrPart;
   Rec^.CharsetCodPart := CcPart;
   CharSetEncodingList.Add(Rec);
end;

procedure ClearCharsetEncodings;
var
  Rec: PCharsetEncodingRec;
  i: Integer;
begin
  for i:=0 to CharsetEncodingList.Count-1 do
  begin
    Rec := CharsetEncodingList[i];
    if Rec<>nil then
      Dispose(Rec);
  end;
  CharsetEncodingList.Clear;
end;

procedure CreateDefaultCharsetEncodings;
begin
  ClearCharsetEncodings;

  AddCharsetEncoding(ANSI_CHARSET,        'iso8859',  '1',    false);
  AddCharsetEncoding(ANSI_CHARSET,        'iso8859',  '3',    false);
  AddCharsetEncoding(ANSI_CHARSET,        'iso8859',  '15',   false);
  AddCharsetEncoding(ANSI_CHARSET,        'ansi',     '0');
  AddCharsetEncoding(ANSI_CHARSET,        '*',        'cp1252');
  AddCharsetEncoding(ANSI_CHARSET,        'iso8859',  '*');
  AddCharsetEncoding(DEFAULT_CHARSET,     '*',        '*');
  AddCharsetEncoding(SYMBOL_CHARSET,      '*',        'fontspecific');
  AddCharsetEncoding(MAC_CHARSET,         '*',        'cpxxxx'); // todo
  AddCharsetEncoding(SHIFTJIS_CHARSET,    'jis',      '0',    true, true);
  AddCharsetEncoding(SHIFTJIS_CHARSET,    '*',        'cp932');
  AddCharsetEncoding(HANGEUL_CHARSET,     '*',        'cp949');
  AddCharsetEncoding(JOHAB_CHARSET,       '*',        'cp1361');
  AddCharsetEncoding(GB2312_CHARSET,      'gb2312',   '0',    true, true);
  AddCharsetEncoding(CHINESEBIG5_CHARSET, 'big5',     '0',    true, true);
  AddCharsetEncoding(CHINESEBIG5_CHARSET, '*',        'cp950');
  AddCharsetEncoding(GREEK_CHARSET,       'iso8859',  '7');
  AddCharsetEncoding(GREEK_CHARSET,       '*',        'cp1253');
  AddCharsetEncoding(TURKISH_CHARSET,     'iso8859',  '9');
  AddCharsetEncoding(TURKISH_CHARSET,     '*',        'cp1254');
  AddCharsetEncoding(VIETNAMESE_CHARSET,  '*',        'cp1258');
  AddCharsetEncoding(HEBREW_CHARSET,      'iso8859',  '8');
  AddCharsetEncoding(HEBREW_CHARSET,      '*',        'cp1255');
  AddCharsetEncoding(ARABIC_CHARSET,      'iso8859',  '6');
  AddCharsetEncoding(ARABIC_CHARSET,      '*',        'cp1256');
  AddCharsetEncoding(BALTIC_CHARSET,      'iso8859',  '13');
  AddCharsetEncoding(BALTIC_CHARSET,      'iso8859',  '4');  // northern europe
  AddCharsetEncoding(BALTIC_CHARSET,      'iso8859',  '14'); // CELTIC_CHARSET
  AddCharsetEncoding(BALTIC_CHARSET,      '*',        'cp1257');
  AddCharsetEncoding(RUSSIAN_CHARSET,     'iso8859',  '5');
  AddCharsetEncoding(RUSSIAN_CHARSET,     'koi8',     '*');
  AddCharsetEncoding(RUSSIAN_CHARSET,     '*',        'cp1251');
  AddCharsetEncoding(THAI_CHARSET,        'iso8859',  '11');
  AddCharsetEncoding(THAI_CHARSET,        'tis620',   '*',  true, true);
  AddCharsetEncoding(THAI_CHARSET,        '*',        'cp874');
  AddCharsetEncoding(EASTEUROPE_CHARSET,  'iso8859',  '2');
  AddCharsetEncoding(EASTEUROPE_CHARSET,  '*',        'cp1250');
  AddCharsetEncoding(OEM_CHARSET,         'ascii',    '0');
  AddCharsetEncoding(OEM_CHARSET,         'iso646',   '*',  true, true);
  AddCharsetEncoding(FCS_ISO_10646_1,     'iso10646', '1');
  AddCharsetEncoding(FCS_ISO_8859_1,      'iso8859',  '1');
  AddCharsetEncoding(FCS_ISO_8859_2,      'iso8859',  '2');
  AddCharsetEncoding(FCS_ISO_8859_3,      'iso8859',  '3');
  AddCharsetEncoding(FCS_ISO_8859_4,      'iso8859',  '4');
  AddCharsetEncoding(FCS_ISO_8859_5,      'iso8859',  '5');
  AddCharsetEncoding(FCS_ISO_8859_6,      'iso8859',  '6');
  AddCharsetEncoding(FCS_ISO_8859_7,      'iso8859',  '7');
  AddCharsetEncoding(FCS_ISO_8859_8,      'iso8859',  '8');
  AddCharsetEncoding(FCS_ISO_8859_9,      'iso8859',  '9');
  AddCharsetEncoding(FCS_ISO_8859_10,     'iso8859',  '10');
  AddCharsetEncoding(FCS_ISO_8859_15,     'iso8859',  '15');
end;

function IndexOfStyleWithName(const WName : String): integer;
begin
  if Styles<>nil then
  begin
    for Result := 0 to Styles.Count-1 do
      if CompareText(WName, Styles[Result]) = 0 then
        exit;
  end;
  Result:=-1;
end;

function NewStyleObject: PStyleObject;
begin
  New(Result);
  FillChar(Result^, SizeOf(TStyleObject), 0);
end;

{.-$DEFINE VerboseUpdateSysColorMap}
procedure UpdateSysColorMap(Widget: PGtkWidget; Lgs: TLazGtkStyle);
{$IFDEF VerboseUpdateSysColorMap}
  function GdkColorAsString(c: TgdkColor): string;
  begin
    Result:='LCL='+DbgS(TGDKColorToTColor(c))
             +' Pixel='+DbgS(c.Pixel)
             +' Red='+DbgS(c.Red)
             +' Green='+DbgS(c.Green)
             +' Blue='+DbgS(c.Blue)
             ;
  end;
{$ENDIF}
var
  MainStyle: PGtkStyle;
begin
  if Widget = nil then exit;
  if not (Lgs in [lgsButton, lgsWindow, lgsMenuBar, lgsMenuitem,
    lgsVerticalScrollbar, lgsHorizontalScrollbar, lgsTooltip, lgsMemo, lgsFrame]) then exit;

  {$IFDEF NoStyle}
  exit;
  {$ENDIF}
  //debugln('UpdateSysColorMap ',GetWidgetDebugReport(Widget));
  // gtk_widget_set_rc_style(Widget);
  MainStyle := Widget^.get_style;
  if MainStyle = nil then exit;
  with MainStyle^ do
  begin
    {$IFDEF VerboseUpdateSysColorMap}
    if rc_style<>nil then
    begin
      with rc_style^ do
      begin
        DebugLn('rc_style:');
        DebugLn(' FG GTK_STATE_NORMAL ',GdkColorAsString(fg[GTK_STATE_NORMAL]));
        DebugLn(' FG GTK_STATE_ACTIVE ',GdkColorAsString(fg[GTK_STATE_ACTIVE]));
        DebugLn(' FG GTK_STATE_PRELIGHT ',GdkColorAsString(fg[GTK_STATE_PRELIGHT]));
        DebugLn(' FG GTK_STATE_SELECTED ',GdkColorAsString(fg[GTK_STATE_SELECTED]));
        DebugLn(' FG GTK_STATE_INSENSITIVE ',GdkColorAsString(fg[GTK_STATE_INSENSITIVE]));
        DebugLn('');
        DebugLn(' BG GTK_STATE_NORMAL ',GdkColorAsString(bg[GTK_STATE_NORMAL]));
        DebugLn(' BG GTK_STATE_ACTIVE ',GdkColorAsString(bg[GTK_STATE_ACTIVE]));
        DebugLn(' BG GTK_STATE_PRELIGHT ',GdkColorAsString(bg[GTK_STATE_PRELIGHT]));
        DebugLn(' BG GTK_STATE_SELECTED ',GdkColorAsString(bg[GTK_STATE_SELECTED]));
        DebugLn(' BG GTK_STATE_INSENSITIVE ',GdkColorAsString(bg[GTK_STATE_INSENSITIVE]));
        DebugLn('');
        DebugLn(' TEXT GTK_STATE_NORMAL ',GdkColorAsString(text[GTK_STATE_NORMAL]));
        DebugLn(' TEXT GTK_STATE_ACTIVE ',GdkColorAsString(text[GTK_STATE_ACTIVE]));
        DebugLn(' TEXT GTK_STATE_PRELIGHT ',GdkColorAsString(text[GTK_STATE_PRELIGHT]));
        DebugLn(' TEXT GTK_STATE_SELECTED ',GdkColorAsString(text[GTK_STATE_SELECTED]));
        DebugLn(' TEXT GTK_STATE_INSENSITIVE ',GdkColorAsString(text[GTK_STATE_INSENSITIVE]));
        DebugLn('');
      end;
    end;

    DebugLn('MainStyle:');
    DebugLn(' FG GTK_STATE_NORMAL ',GdkColorAsString(fg[GTK_STATE_NORMAL]));
    DebugLn(' FG GTK_STATE_ACTIVE ',GdkColorAsString(fg[GTK_STATE_ACTIVE]));
    DebugLn(' FG GTK_STATE_PRELIGHT ',GdkColorAsString(fg[GTK_STATE_PRELIGHT]));
    DebugLn(' FG GTK_STATE_SELECTED ',GdkColorAsString(fg[GTK_STATE_SELECTED]));
    DebugLn(' FG GTK_STATE_INSENSITIVE ',GdkColorAsString(fg[GTK_STATE_INSENSITIVE]));
    DebugLn('');
    DebugLn(' BG GTK_STATE_NORMAL ',GdkColorAsString(bg[GTK_STATE_NORMAL]));
    DebugLn(' BG GTK_STATE_ACTIVE ',GdkColorAsString(bg[GTK_STATE_ACTIVE]));
    DebugLn(' BG GTK_STATE_PRELIGHT ',GdkColorAsString(bg[GTK_STATE_PRELIGHT]));
    DebugLn(' BG GTK_STATE_SELECTED ',GdkColorAsString(bg[GTK_STATE_SELECTED]));
    DebugLn(' BG GTK_STATE_INSENSITIVE ',GdkColorAsString(bg[GTK_STATE_INSENSITIVE]));
    DebugLn('');
    DebugLn(' TEXT GTK_STATE_NORMAL ',GdkColorAsString(text[GTK_STATE_NORMAL]));
    DebugLn(' TEXT GTK_STATE_ACTIVE ',GdkColorAsString(text[GTK_STATE_ACTIVE]));
    DebugLn(' TEXT GTK_STATE_PRELIGHT ',GdkColorAsString(text[GTK_STATE_PRELIGHT]));
    DebugLn(' TEXT GTK_STATE_SELECTED ',GdkColorAsString(text[GTK_STATE_SELECTED]));
    DebugLn(' TEXT GTK_STATE_INSENSITIVE ',GdkColorAsString(text[GTK_STATE_INSENSITIVE]));
    DebugLn('');
    DebugLn(' LIGHT GTK_STATE_NORMAL ',GdkColorAsString(light[GTK_STATE_NORMAL]));
    DebugLn(' LIGHT GTK_STATE_ACTIVE ',GdkColorAsString(light[GTK_STATE_ACTIVE]));
    DebugLn(' LIGHT GTK_STATE_PRELIGHT ',GdkColorAsString(light[GTK_STATE_PRELIGHT]));
    DebugLn(' LIGHT GTK_STATE_SELECTED ',GdkColorAsString(light[GTK_STATE_SELECTED]));
    DebugLn(' LIGHT GTK_STATE_INSENSITIVE ',GdkColorAsString(light[GTK_STATE_INSENSITIVE]));
    DebugLn('');
    DebugLn(' DARK GTK_STATE_NORMAL ',GdkColorAsString(dark[GTK_STATE_NORMAL]));
    DebugLn(' DARK GTK_STATE_ACTIVE ',GdkColorAsString(dark[GTK_STATE_ACTIVE]));
    DebugLn(' DARK GTK_STATE_PRELIGHT ',GdkColorAsString(dark[GTK_STATE_PRELIGHT]));
    DebugLn(' DARK GTK_STATE_SELECTED ',GdkColorAsString(dark[GTK_STATE_SELECTED]));
    DebugLn(' DARK GTK_STATE_INSENSITIVE ',GdkColorAsString(dark[GTK_STATE_INSENSITIVE]));
    DebugLn('');
    DebugLn(' MID GTK_STATE_NORMAL ',GdkColorAsString(mid[GTK_STATE_NORMAL]));
    DebugLn(' MID GTK_STATE_ACTIVE ',GdkColorAsString(mid[GTK_STATE_ACTIVE]));
    DebugLn(' MID GTK_STATE_PRELIGHT ',GdkColorAsString(mid[GTK_STATE_PRELIGHT]));
    DebugLn(' MID GTK_STATE_SELECTED ',GdkColorAsString(mid[GTK_STATE_SELECTED]));
    DebugLn(' MID GTK_STATE_INSENSITIVE ',GdkColorAsString(mid[GTK_STATE_INSENSITIVE]));
    DebugLn('');
    DebugLn(' BASE GTK_STATE_NORMAL ',GdkColorAsString(base[GTK_STATE_NORMAL]));
    DebugLn(' BASE GTK_STATE_ACTIVE ',GdkColorAsString(base[GTK_STATE_ACTIVE]));
    DebugLn(' BASE GTK_STATE_PRELIGHT ',GdkColorAsString(base[GTK_STATE_PRELIGHT]));
    DebugLn(' BASE GTK_STATE_SELECTED ',GdkColorAsString(base[GTK_STATE_SELECTED]));
    DebugLn(' BASE GTK_STATE_INSENSITIVE ',GdkColorAsString(base[GTK_STATE_INSENSITIVE]));
    DebugLn('');
    DebugLn(' BLACK ',GdkColorAsString(black));
    DebugLn(' WHITE ',GdkColorAsString(white));
    {$ENDIF}

    {$IFNDEF DisableGtkSysColors}
    // this map is taken from this research:
    // http://www.endolith.com/wordpress/2008/08/03/wine-colors/
    case Lgs of
      lgsButton:
        begin
          SysColorMap[COLOR_ACTIVEBORDER] := TGDKColorToTColor(bg[GTK_STATE_INSENSITIVE]);
          SysColorMap[COLOR_INACTIVEBORDER] := TGDKColorToTColor(bg[GTK_STATE_INSENSITIVE]);
          SysColorMap[COLOR_WINDOWFRAME] := TGDKColorToTColor(mid[GTK_STATE_SELECTED]);

          SysColorMap[COLOR_BTNFACE] := TGDKColorToTColor(bg[GTK_STATE_INSENSITIVE]);
          SysColorMap[COLOR_BTNSHADOW] := TGDKColorToTColor(dark[GTK_STATE_INSENSITIVE]);
          SysColorMap[COLOR_BTNTEXT] := TGDKColorToTColor(fg[GTK_STATE_NORMAL]);
          SysColorMap[COLOR_BTNHIGHLIGHT] := TGDKColorToTColor(light[GTK_STATE_INSENSITIVE]);
          SysColorMap[COLOR_3DDKSHADOW] := TGDKColorToTColor(black);
          SysColorMap[COLOR_3DLIGHT] := TGDKColorToTColor(bg[GTK_STATE_INSENSITIVE]);
        end;
      lgsMemo:
        begin
          SysColorMap[COLOR_HIGHLIGHT] := TGDKColorToTColor(base[GTK_STATE_SELECTED]);
          SysColorMap[COLOR_HIGHLIGHTTEXT] := TGDKColorToTColor(fg[GTK_STATE_SELECTED]);
          SysColorMap[COLOR_WINDOW] := TGDKColorToTColor(base[GTK_STATE_NORMAL]);
          SysColorMap[COLOR_WINDOWTEXT] := TGDKColorToTColor(text[GTK_STATE_NORMAL]);
        end;
      lgsFrame:
        begin
          SysColorMap[COLOR_BACKGROUND] := TGDKColorToTColor(bg[GTK_STATE_NORMAL]);
        end;
      lgsWindow:
        begin
          // colors which can be only retrieved from the window manager (metacity)
          SysColorMap[COLOR_ACTIVECAPTION] := TGDKColorToTColor(dark[GTK_STATE_SELECTED]);
          SysColorMap[COLOR_INACTIVECAPTION] := TGDKColorToTColor(dark[GTK_STATE_NORMAL]);
          SysColorMap[COLOR_GRADIENTACTIVECAPTION] := TGDKColorToTColor(light[GTK_STATE_SELECTED]);
          SysColorMap[COLOR_GRADIENTINACTIVECAPTION] := TGDKColorToTColor(base[GTK_STATE_NORMAL]);
          SysColorMap[COLOR_CAPTIONTEXT] := TGDKColorToTColor(white);
          SysColorMap[COLOR_INACTIVECAPTIONTEXT] := TGDKColorToTColor(white);
          // others
          SysColorMap[COLOR_APPWORKSPACE] := TGDKColorToTColor(base[GTK_STATE_NORMAL]);
          SysColorMap[COLOR_GRAYTEXT] := TGDKColorToTColor(fg[GTK_STATE_INSENSITIVE]);
          (*
          SysColorMap[COLOR_HIGHLIGHT] := TGDKColorToTColor(base[GTK_STATE_SELECTED]);
          SysColorMap[COLOR_HIGHLIGHTTEXT] := TGDKColorToTColor(fg[GTK_STATE_SELECTED]);
          SysColorMap[COLOR_WINDOW] := TGDKColorToTColor(base[GTK_STATE_NORMAL]);
          SysColorMap[COLOR_WINDOWTEXT] := TGDKColorToTColor(text[GTK_STATE_NORMAL]);
          *)
          SysColorMap[COLOR_HOTLIGHT] := TGDKColorToTColor(light[GTK_STATE_NORMAL]);
          // SysColorMap[COLOR_BACKGROUND] := TGDKColorToTColor(bg[GTK_STATE_PRELIGHT]);
          SysColorMap[COLOR_FORM] := TGDKColorToTColor(bg[GTK_STATE_NORMAL]);
        end;
      lgsMenuBar:
        begin
          SysColorMap[COLOR_MENUBAR] := TGDKColorToTColor(bg[GTK_STATE_NORMAL]);
        end;
      lgsMenuitem:
        begin
          SysColorMap[COLOR_MENU] := TGDKColorToTColor(light[GTK_STATE_ACTIVE]);
          SysColorMap[COLOR_MENUTEXT] := TGDKColorToTColor(fg[GTK_STATE_NORMAL]);
          SysColorMap[COLOR_MENUHILIGHT] := TGDKColorToTColor(bg[GTK_STATE_PRELIGHT]);
        end;
      lgsVerticalScrollbar,
      lgsHorizontalScrollbar:
        begin
          SysColorMap[COLOR_SCROLLBAR] := TGDKColorToTColor(bg[GTK_STATE_ACTIVE]);
        end;
      lgsTooltip:
        begin
          SysColorMap[COLOR_INFOTEXT] := TGDKColorToTColor(fg[GTK_STATE_NORMAL]);
          SysColorMap[COLOR_INFOBK] := TGDKColorToTColor(bg[GTK_STATE_NORMAL]);
        end;
    end;
    {$ENDIF}
  end;
end;

function GetStyleWithName(const WName: String): PGtkWidget;
var
  StyleObject : PStyleObject;
  AIndex: Integer;
  lgs: TLazGtkStyle;
  WidgetName: String;
begin
  Result := nil;
  if (WName='') then exit;
  AIndex := IndexOfStyleWithName(WName);
  if AIndex >= 0 then
  begin
    StyleObject := PStyleObject(Styles.Objects[AIndex]);
    Result := StyleObject^.Widget;
  end else
  begin
    StyleObject := NewStyleObject;
    lgs := lgsUserDefined;
    DebugLn('GetStyleWithName creating style widget ',WName);
    WidgetName := 'LazStyle' + WName;
    if CompareText(WName, LazGtkStyleNames[lgsButton]) = 0 then
    begin
      StyleObject^.Widget := TGtkButton.new;
      lgs := lgsButton;
    end else
    if CompareText(WName, LazGtkStyleNames[lgsNotebook]) = 0 then
    begin
      StyleObject^.Widget := TGtkNoteBook.new;
      lgs := lgsNotebook;
    end else
    if CompareText(WName, LazGtkStyleNames[lgsWindow]) = 0 then
    begin
      StyleObject^.Widget := TGtkWindow.new(GTK_WINDOW_TOPLEVEL);
      lgs := lgsWindow;
    end else
    if CompareText(WName, LazGtkStyleNames[lgsTreeView]) = 0 then
    begin
      StyleObject^.Widget := TGtkTreeView.new;
      lgs := lgsTreeView;
    end else
    if CompareText(WName, LazGtkStyleNames[lgsMemo]) = 0 then
    begin
      StyleObject^.Widget := TGtkTextView.new;
      lgs := lgsMemo;
    end else
    if CompareText(WName, LazGtkStyleNames[lgsFrame]) = 0 then
    begin
      StyleObject^.Widget := TGtkFixed.new;
      lgs := lgsFrame;
    end else
    begin
    end;
    if Gtk3IsWidget(StyleObject^.Widget) then
    begin
      StyleObject^.Widget^.set_name(PgChar(WidgetName));
      StyleObject^.Widget^.show_all;
      StyleObject^.Widget^.ensure_style;
      Styles.AddObject(WName, TObject(StyleObject));
      if lgs <> lgsUserDefined then
        StandardStyles[lgs] := StyleObject;
      StyleObject^.Widget^.hide;

      //TODO: copy stuff from gtk2proc
      UpdateSysColorMap(StyleObject^.Widget, lgs);
      Result := StyleObject^.Widget;
    end;
  end;
end;

function GetStyleWidgetWithName(const WName : String) : PGtkWidget;
var
  l : Longint;
begin
  Result := nil;
  // init style
  GetStyleWithName(WName);
  // return widget
  l := IndexOfStyleWithName(WName);
  if l>=0 then
    Result := PStyleObject(Styles.Objects[l])^.Widget;
end;

function GetStyleWidget(aStyle: TLazGtkStyle) : PGtkWidget;
begin
  if aStyle in [lgsUserDefined] then
    raise Exception.Create('Gtk3: user styles are defined by name');

  if StandardStyles[aStyle]<>nil then
    // already created
    Result := StandardStyles[aStyle]^.Widget
  else
    // create it
    Result := GetStyleWidgetWithName(LazGtkStyleNames[aStyle]);
end;

procedure FreeStyleObject(var StyleObject : PStyleObject);
// internal function to dispose a styleobject
// it does *not* remove it from the style lists
begin
  if StyleObject <> nil then
  begin
    if StyleObject^.Owner <> nil then
    begin
      // GTK owns the reference to top level widgets created by application,
      // so they cannot be destroyed by unreferencing.
      if gtk_widget_is_toplevel(StyleObject^.Owner) then
        gtk_widget_destroy(StyleObject^.Owner)
      else
        g_object_unref(StyleObject^.Owner);
    end;
    if StyleObject^.Style <> nil then
      if StyleObject^.Style^.attach_count > 0 then
        g_object_unref(StyleObject^.Style);
    Dispose(StyleObject);
    StyleObject := nil;
  end;
end;

procedure ReleaseAllStyles;
var
  StyleObject: PStyleObject;
  lgs: TLazGtkStyle;
  i: Integer;
begin
  if Styles = nil then
    exit;
  for i:=Styles.Count-1 downto 0 do
  begin
    StyleObject := PStyleObject(Styles.Objects[i]);
    FreeStyleObject(StyleObject);
  end;
  Styles.Clear;
  for lgs:=Low(TLazGtkStyle) to High(TLazGtkStyle) do
    StandardStyles[lgs]:=nil;
end;

{------------------------------------------------------------------------------
  procedure: SetWindowCursor
  Params:  AWindow : PGDkWindow, ACursor: PGdkCursor, ASetDefault: Boolean
  Returns: Nothing

  Sets the cursor for a window.
  Tries to avoid messing with the cursors of implicitly created
  child windows (e.g. headers in TListView) with the following logic:
  - If Cursor <> nil, saves the old cursor (if not already done or ASetDefault = true)
    before setting the new one.
  - If Cursor = nil, restores the old cursor (if not already done).
  ------------------------------------------------------------------------------}
procedure SetWindowCursor(AWindow: PGdkWindow; Cursor: PGdkCursor; ASetDefault: Boolean);
var
  OldCursor: PGdkCursor;
  Data: gpointer;
begin
  if ASetDefault then //and ((Cursor <> nil) or ( <> nil)) then
  begin
    // Override any old default cursor
    g_object_steal_data(PGObject(AWindow), 'havesavedcursor'); // OK?
    g_object_steal_data(PGObject(AWindow), 'savedcursor');
    gdk_window_set_cursor(AWindow, Cursor);
    Exit;
  end;
  if Cursor <> nil then
  begin
    OldCursor := gdk_window_get_cursor(AWindow);
    if ASetDefault or (g_object_get_data(PGObject(AWindow), 'havesavedcursor') = nil) then
    begin
      g_object_set_data(PGObject(AWindow), 'havesavedcursor', gpointer(1));
      g_object_set_data(PGObject(AWindow), 'savedcursor', gpointer(OldCursor));
    end;
    gdk_pointer_grab(AWindow, False, 0, AWindow, Cursor, 1);
    try
      gdk_window_set_cursor(AWindow, Cursor);
    finally
      gdk_pointer_ungrab(0);
    end;
  end else
  begin
    if g_object_steal_data(PGObject(AWindow), 'havesavedcursor') <> nil then
    begin
      Cursor := g_object_steal_data(PGObject(AWindow), 'savedcursor');
      gdk_window_set_cursor(AWindow, Cursor);
    end;
  end;
end;

{------------------------------------------------------------------------------
  procedure: SetWindowCursor
  Params:  AWindow : PGDkWindow, ACursor: HCursor, ARecursive: Boolean
  Returns: Nothing

  Sets the cursor for a window (or recursively for window with children)
 ------------------------------------------------------------------------------}
procedure SetWindowCursor(AWindow: PGdkWindow; ACursor: HCursor;
  ARecursive: Boolean; ASetDefault: Boolean);
var
  Cursor: PGdkCursor;

  procedure SetCursorRecursive(AWindow: PGdkWindow);
  var
    ChildWindows, ListEntry: PGList;
  begin
    SetWindowCursor(AWindow, Cursor, ASetDefault);

    ChildWindows := gdk_window_get_children(AWindow);

    ListEntry := ChildWindows;
    while ListEntry <> nil do
    begin
      SetCursorRecursive(PGdkWindow(ListEntry^.Data));
      ListEntry := ListEntry^.Next;
    end;
    g_list_free(ChildWindows);
  end;
begin
  Cursor := {%H-}PGdkCursor(ACursor);
  if ARecursive then
    SetCursorRecursive(AWindow)
  else
    SetWindowCursor(AWindow, Cursor, ASetDefault);
end;

{------------------------------------------------------------------------------
  procedure: SetGlobalCursor
  Params:  ACursor: HCursor
  Returns: Nothing

  Sets the cursor for all toplevel windows. Also sets the cursor for all child
  windows recursively provided gdk_get_window_cursor is available.
 ------------------------------------------------------------------------------}
procedure SetGlobalCursor(Cursor: HCURSOR);
var
  TopList: PGList;
  i: Integer;
begin
  TopList := gdk_screen_get_toplevel_windows(gdk_screen_get_default);
  if TopList = nil then
    exit;
  for i := 0 to g_list_length(TopList) - 1 do
  begin
    if (TopList^.Data <> nil) then
      SetWindowCursor(PGDKWindow(TopList^.Data), Cursor,
        True, False);
  end;
  g_list_free(TopList);
end;

end.
