1{%MainUnit gtk2def.pp}
2
3{******************************************************************************
4                                   TGtk2DeviceContext
5 ******************************************************************************
6
7 *****************************************************************************
8  This file is part of the Lazarus Component Library (LCL)
9
10  See the file COPYING.modifiedLGPL.txt, included in this distribution,
11  for details about the license.
12 *****************************************************************************
13}
14
15{$IFOPT C-}
16// Uncomment for local trace
17//  {$C+}
18//  {$DEFINE ASSERT_IS_ON}
19{$ENDIF}
20
21{ TDeviceContext }
22
23procedure TGtkDeviceContext.SetClipRegion(const AValue: PGdiObject);
24begin
25  ChangeGDIObject(fClipRegion, AValue);
26end;
27
28function TGtkDeviceContext.GetGDIObjects(ID: TGDIType): PGdiObject;
29begin
30  case ID of
31    gdiBitmap: Result:=CurrentBitmap;
32    gdiFont: Result:=CurrentFont;
33    gdiBrush: Result:=CurrentBrush;
34    gdiPen: Result:=CurrentPen;
35    gdiPalette: Result:=CurrentPalette;
36    gdiRegion: Result:=ClipRegion;
37  end;
38end;
39
40function TGtkDeviceContext.GetClipRectangle: TGdkRectangle;
41var
42  X,Y: gint;
43begin
44  if FClipRegion = nil then
45  begin
46    if (PaintRectangle.Left<>0) or (PaintRectangle.Top<>0) or
47      (PaintRectangle.Right<>0) or (PaintRectangle.Bottom<>0) then
48      Result := GdkRectFromRect(PaintRectangle)
49    else
50    begin
51      gdk_window_get_size(Drawable, @X, @Y);
52      Result := GdkRectFromRect(Rect(0,0, X, Y));
53    end;
54  end else
55    gdk_region_get_clipbox(FClipRegion^.GDIRegionObject, @Result);
56end;
57
58function TGtkDeviceContext.GetOffset: TPoint;
59var
60  Fixed: Pointer;
61  AChild: PGtkWidget;
62  AColumn: PGtkTreeViewColumn;
63  Area: TGdkRectangle;
64  h: gint;
65  w: gint;
66  yoffs: gint;
67  xoffs: gint;
68begin
69  Result := Point(0, 0);
70  if Assigned(FWidget) then
71  begin
72    Fixed := GetFixedWidget(FWidget);
73    if GTK_WIDGET_NO_WINDOW(FWidget) and
74       GTK_WIDGET_NO_WINDOW(Fixed) and
75       not GtkWidgetIsA(FWidget, GTKAPIWidget_GetType) then
76    begin
77      Inc(Result.X, FWidget^.Allocation.x);
78      Inc(Result.y, FWidget^.Allocation.y);
79    end;
80    if (GTK_IS_SCROLLED_WINDOW(FWidget) and GTK_IS_BIN(FWidget)) or (GTK_IS_TREE_VIEW(FWidget)) then
81    begin
82      if GTK_IS_TREE_VIEW(FWidget) then
83        AChild := FWidget
84      else
85        AChild := gtk_bin_get_child(PGtkBin(FWidget));
86      if GTK_IS_TREE_VIEW(AChild) and gtk_tree_view_get_headers_visible(PGtkTreeView(AChild)) then
87      begin
88        AColumn := gtk_tree_view_get_column(PGtkTreeView(AChild), 0);
89        gtk_tree_view_column_cell_get_size(AColumn, @Area, @xoffs, @yoffs, @w, @h);
90        // borders are 2px
91        dec(Result.y, h - 2);
92      end;
93    end;
94  end;
95end;
96
97function TGtkDeviceContext.GetOwnedGDIObjects(ID: TGDIType): PGdiObject;
98begin
99  Result:=fOwnedGDIObjects[ID];
100end;
101
102procedure TGtkDeviceContext.SetCurrentBitmap(const AValue: PGdiObject);
103begin
104  ChangeGDIObject(FCurrentBitmap,AValue);
105end;
106
107procedure TGtkDeviceContext.SetCurrentBrush(const AValue: PGdiObject);
108begin
109  ChangeGDIObject(FCurrentBrush,AValue);
110  if FSelectedColors = dcscBrush then
111    FSelectedColors := dcscCustom;
112end;
113
114procedure TGtkDeviceContext.SetCurrentFont(const AValue: PGdiObject);
115begin
116  ChangeGDIObject(FCurrentFont,AValue);
117  if FHasTransf then
118    TransfUpdateFont;
119end;
120
121procedure TGtkDeviceContext.SetCurrentPalette(const AValue: PGdiObject);
122begin
123  ChangeGDIObject(FCurrentPalette,AValue);
124end;
125
126procedure TGtkDeviceContext.SetCurrentPen(const AValue: PGdiObject);
127begin
128  ChangeGDIObject(FCurrentPen,AValue);
129  if FSelectedColors = dcscPen then
130    FSelectedColors := dcscCustom;
131  if FHasTransf then
132    TransfUpdatePen;
133end;
134
135procedure TGtkDeviceContext.ChangeGDIObject(var GDIObject: PGdiObject;
136  const NewValue: PGdiObject);
137begin
138  if GdiObject = NewValue then exit;
139  if GdiObject <> nil then
140  begin
141    dec(GdiObject^.DCCount);
142    if GdiObject^.DCCount < 0 then
143      RaiseGDBException('');
144    ReleaseGDIObject(GDIObject);
145  end;
146
147  GdiObject := NewValue;
148
149  if GdiObject <> nil then
150  begin
151    inc(GdiObject^.DCCount);
152    ReferenceGDIObject(GDIObject);
153  end;
154end;
155
156procedure TGtkDeviceContext.SetGDIObjects(ID: TGDIType; const AValue: PGdiObject);
157begin
158  case ID of
159    gdiBitmap:  ChangeGDIObject(fCurrentBitmap,AValue);
160    gdiFont:    ChangeGDIObject(fCurrentFont,AValue);
161    gdiBrush:   ChangeGDIObject(fCurrentBrush,AValue);
162    gdiPen:     ChangeGDIObject(fCurrentPen,AValue);
163    gdiPalette: ChangeGDIObject(fCurrentPalette,AValue);
164    gdiRegion:  ChangeGDIObject(fClipRegion,AValue);
165  end;
166end;
167
168procedure TGtkDeviceContext.SetMapMode(AValue: Integer);
169begin
170  if AValue <> FMapMode then
171  begin
172    case AValue of
173      MM_ANISOTROPIC:; // user's choice
174      MM_ISOTROPIC:; // adjusted after each SetViewPortExtEx call (see MSDN for details)
175      MM_HIENGLISH: FWindowExt := Point(1000, -1000);
176      MM_HIMETRIC: FWindowExt := Point(2540, -2540);
177      MM_LOENGLISH: FWindowExt := Point(100, -100);
178      MM_LOMETRIC: FWindowExt := Point(254, -254);
179      MM_TWIPS: FWindowExt := Point(1440, -1440);
180    else
181      AValue := MM_TEXT;
182      FWindowExt := Point(1, 1);
183      FViewPortExt := Point(1, 1);
184    end;
185    FMapMode := AValue;
186    // to do: combine with affine transformations here when they get implemented
187    FHasTransf :=
188      (FMapMode <> MM_TEXT) or
189      (FViewPortOrg.x <> 0) or
190      (FViewPortOrg.y <> 0) or
191      (FWindowOrg.x <> 0) or
192      (FWindowOrg.y <> 0);
193    if not (FMapMode in [MM_TEXT, MM_ANISOTROPIC, MM_ISOTROPIC]) then
194    begin
195      FViewPortExt.X := Gtk2WidgetSet.GetDeviceCaps(HDC(Self), LOGPIXELSX);
196      FViewPortExt.Y := Gtk2WidgetSet.GetDeviceCaps(HDC(Self), LOGPIXELSY);
197    end;
198    TransfUpdateFont;
199    TransfUpdatePen;
200  end;
201end;
202
203procedure TGtkDeviceContext.SetOwnedGDIObjects(ID: TGDIType;
204  const AValue: PGdiObject);
205begin
206//MWE: this is not right. all objects except bitmaps can be selected in more than one DC
207
208  if fOwnedGDIObjects[ID]=AValue then exit;
209  if fOwnedGDIObjects[ID]<>nil then
210    fOwnedGDIObjects[ID]^.Owner:=nil;
211  fOwnedGDIObjects[ID]:=AValue;
212  if fOwnedGDIObjects[ID]<>nil then
213    fOwnedGDIObjects[ID]^.Owner:=Self;
214end;
215
216procedure TGtkDeviceContext.SetROP2(AROP: Integer);
217var
218  Func: TGdkFunction;
219begin
220  case AROP of
221    R2_COPYPEN:     Func := GDK_COPY;
222    R2_NOT:         Func := GDK_INVERT;
223    R2_XORPEN:      Func := GDK_XOR;
224    R2_BLACK:       Func := GDK_CLEAR;
225    R2_MASKPEN:     Func := GDK_AND;
226    R2_MASKPENNOT:  Func := GDK_AND_REVERSE;
227    R2_MASKNOTPEN:  Func := GDK_AND_INVERT;
228    R2_NOP:         Func := GDK_NOOP;
229    R2_MERGEPEN:    Func := GDK_OR;
230    R2_NOTXORPEN:   Func := GDK_EQUIV;
231    R2_MERGEPENNOT: Func := GDK_OR_REVERSE;
232    R2_NOTCOPYPEN:  Func := GDK_COPY_INVERT;
233    R2_NOTMASKPEN:  Func := GDK_NAND;
234    //R2_NOTMERGEPEN: Func := GDK_NOR;
235    R2_WHITE:       Func := GDK_SET;
236  else
237    Func := GDK_COPY;
238  end;
239
240  gdk_gc_set_function(GC, Func);
241  gdk_gc_get_values(GC, @FGCValues);
242end;
243
244procedure TGtkDeviceContext.SetViewPortExt(const AValue: TPoint);
245var
246  Ratio: Single;
247begin
248  if (AValue.x <> FViewPortExt.x) or (AValue.y <> FViewPortExt.y) and
249    (FMapMode in [MM_ISOTROPIC, MM_ANISOTROPIC]) then
250  begin
251    if FMapMode = MM_ISOTROPIC then
252    begin
253      // TK: Is here also an adjustment on Windows if DPIX and DPIY are different?
254      Ratio := FWindowExt.x / FWindowExt.y; // no check, programmer cannot put nonsense
255      if AValue.y * Ratio > AValue.x then
256        FViewPortExt := Point(AValue.x, RoundToInt(AValue.x / Ratio))
257      else if AValue.y * Ratio < AValue.x then
258        FViewPortExt := Point(RoundToInt(AValue.y * Ratio), AValue.y)
259      else
260        FViewPortExt := AValue;
261    end else
262      FViewPortExt := AValue;
263    TransfUpdateFont;
264    TransfUpdatePen;
265  end;
266end;
267
268procedure TGtkDeviceContext.SetViewPortOrg(const AValue: TPoint);
269begin
270  if (FViewPortOrg.x <> AValue.x) or
271     (FViewPortOrg.y <> AValue.y) then
272  begin
273    FViewPortOrg := AValue;
274    FHasTransf := True;
275  end;
276end;
277
278procedure TGtkDeviceContext.SetWindowExt(const AValue: TPoint);
279begin
280  if (AValue.x <> FWindowExt.x) or (AValue.y <> FWindowExt.y) and
281    (FMapMode in [MM_ISOTROPIC, MM_ANISOTROPIC]) then
282  begin
283    FWindowExt := AValue;
284    if FMapMode = MM_ANISOTROPIC then
285    begin
286      TransfUpdateFont;
287      TransfUpdatePen;
288    end;
289  end;
290end;
291
292procedure TGtkDeviceContext.SetWindowOrg(AValue: TPoint);
293begin
294  if (FWindowOrg.x <> AValue.x) or
295     (FWindowOrg.y <> AValue.y) then
296  begin
297    FWindowOrg := AValue;
298    FHasTransf := True;
299  end;
300end;
301
302procedure TGtkDeviceContext.SetSelectedColors(AValue: TDevContextSelectedColorsType);
303begin
304  if FSelectedColors = AValue then Exit;
305  FSelectedColors := AValue;
306
307  case FSelectedColors of
308    dcscPen: SelectPenProps;
309    dcscBrush: SelectBrushProps;
310    dcscFont: SelectTextProps;
311  end;
312end;
313
314procedure TGtkDeviceContext.SetTextMetricsValid(AValid: Boolean);
315begin
316  if AValid then
317    Include(FFlags, dcfTextMetricsValid)
318  else
319    Exclude(FFlags, dcfTextMetricsValid);
320end;
321
322procedure TGtkDeviceContext.RemovePixbuf;
323begin
324  if Assigned(FPixbuf) then
325  begin
326    gdk_pixbuf_unref(FPixbuf);
327    FPixbuf := nil;
328  end;
329end;
330
331procedure TGtkDeviceContext.InvTransfPoint(var X1, Y1: Integer);
332begin
333  X1 := MulDiv(X1 + FWindowOrg.x - FViewPortOrg.x, FWindowExt.x, FViewPortExt.x);
334  Y1 := MulDiv(Y1 + FWindowOrg.y - FViewPortOrg.y, FWindowExt.y, FViewPortExt.y);
335  // to do: put affine inverse transformation here (for all Inv.. methods)
336end;
337
338function TGtkDeviceContext.InvTransfPointIndirect(const P: TPoint): TPoint;
339begin
340  Result.X := MulDiv(P.X + FWindowOrg.x - FViewPortOrg.x, FWindowExt.x, FViewPortExt.x);
341  Result.Y := MulDiv(P.Y + FWindowOrg.y - FViewPortOrg.y, FWindowExt.y, FViewPortExt.y);
342end;
343
344procedure TGtkDeviceContext.InvTransfRect(var X1, Y1, X2, Y2: Integer);
345begin
346  X1 := MulDiv(X1 + FWindowOrg.x - FViewPortOrg.x, FWindowExt.x, FViewPortExt.x);
347  Y1 := MulDiv(Y1 + FWindowOrg.y - FViewPortOrg.y, FWindowExt.y, FViewPortExt.y);
348  X2 := MulDiv(X2 + FWindowOrg.x - FViewPortOrg.x, FWindowExt.x, FViewPortExt.x);
349  Y2 := MulDiv(Y2 + FWindowOrg.y - FViewPortOrg.y, FWindowExt.y, FViewPortExt.y);
350end;
351
352function TGtkDeviceContext.InvTransfRectIndirect(const R: TRect): TRect;
353begin
354  Result.Left := MulDiv(R.Left + FWindowOrg.x - FViewPortOrg.x, FWindowExt.x, FViewPortExt.x);
355  Result.Top := MulDiv(R.Top + FWindowOrg.y - FViewPortOrg.y, FWindowExt.y, FViewPortExt.y);
356  Result.Right := MulDiv(R.Right + FWindowOrg.x - FViewPortOrg.x, FWindowExt.x, FViewPortExt.x);
357  Result.Bottom := MulDiv(R.Bottom + FWindowOrg.y - FViewPortOrg.y, FWindowExt.y, FViewPortExt.y);
358end;
359
360procedure TGtkDeviceContext.InvTransfExtent(var ExtX, ExtY: Integer);
361begin
362  ExtX := MulDiv(ExtX, FWindowExt.x, FViewPortExt.x);
363  ExtY := MulDiv(ExtY, FWindowExt.y, FViewPortExt.y);
364end;
365
366function TGtkDeviceContext.InvTransfExtentIndirect(const Extent: TPoint): TPoint;
367begin
368  Result.X := MulDiv(Extent.X, FWindowExt.x, FViewPortExt.x);
369  Result.Y := MulDiv(Extent.Y, FWindowExt.y, FViewPortExt.y);
370end;
371
372procedure TGtkDeviceContext.TransfAngles(var Angle1, Angle2: Integer);
373begin
374  if FWindowExt.x * FViewPortExt.x < 0 then
375  begin
376    // flip angles along 90-270 degree axis
377    Angle1 := 2880 - Angle1;
378    Angle2 := 2880 - Angle2;
379  end;
380  if FWindowExt.y * FViewPortExt.y < 0 then
381  begin
382    // flip angles along 0-180 degree axis
383    Angle1 := 5760 - Angle1;
384    Angle2 := 5760 - Angle2;
385  end;
386end;
387
388procedure TGtkDeviceContext.TransfNormalize(var Lower, Higher: Integer);
389var
390  Tmp: Integer;
391begin
392  if Lower > Higher then
393  begin
394    Tmp := Lower;
395    Lower := Higher;
396    Higher := Tmp;
397  end;
398end;
399
400procedure TGtkDeviceContext.TransfPoint(var X1, Y1: Integer);
401begin
402  // to do: put affine transformation here (for all Transf.. methods)
403  X1 := MulDiv(X1, FViewPortExt.x, FWindowExt.x) + FViewPortOrg.x - FWindowOrg.x;
404  Y1 := MulDiv(Y1, FViewPortExt.y, FWindowExt.y) + FViewPortOrg.y - FWindowOrg.y;
405end;
406
407function TGtkDeviceContext.TransfPointIndirect(const P: TPoint): TPoint;
408begin
409  Result.x := MulDiv(P.x, FViewPortExt.x, FWindowExt.x) + FViewPortOrg.x - FWindowOrg.x;
410  Result.Y := MulDiv(P.y, FViewPortExt.y, FWindowExt.y) + FViewPortOrg.y - FWindowOrg.y;
411end;
412
413procedure TGtkDeviceContext.TransfRect(var X1, Y1, X2, Y2: Integer);
414begin
415  X1 := MulDiv(X1, FViewPortExt.x, FWindowExt.x) + FViewPortOrg.x - FWindowOrg.x;
416  Y1 := MulDiv(Y1, FViewPortExt.y, FWindowExt.y) + FViewPortOrg.y - FWindowOrg.y;
417  X2 := MulDiv(X2, FViewPortExt.x, FWindowExt.x) + FViewPortOrg.x - FWindowOrg.x;
418  Y2 := MulDiv(Y2, FViewPortExt.y, FWindowExt.y) + FViewPortOrg.y - FWindowOrg.y;
419end;
420
421function TGtkDeviceContext.TransfRectIndirect(const R: TRect): TRect;
422begin
423  Result.Left := MulDiv(R.Left, FViewPortExt.x, FWindowExt.x) + FViewPortOrg.x - FWindowOrg.x;
424  Result.Top := MulDiv(R.Top, FViewPortExt.y, FWindowExt.y) + FViewPortOrg.y - FWindowOrg.y;
425  Result.Right := MulDiv(R.Right, FViewPortExt.x, FWindowExt.x) + FViewPortOrg.x - FWindowOrg.x;
426  Result.Bottom := MulDiv(R.Bottom, FViewPortExt.y, FWindowExt.y) + FViewPortOrg.y - FWindowOrg.y;
427end;
428
429procedure TGtkDeviceContext.TransfExtent(var ExtX, ExtY: Integer);
430begin
431  ExtX := MulDiv(ExtX, FViewPortExt.x, FWindowExt.x);
432  ExtY := MulDiv(ExtY, FViewPortExt.y, FWindowExt.y);
433end;
434
435function TGtkDeviceContext.TransfExtentIndirect(const Extent: TPoint): TPoint;
436begin
437  Result.X := MulDiv(Extent.X, FViewPortExt.x, FWindowExt.x);
438  Result.Y := MulDiv(Extent.Y, FViewPortExt.y, FWindowExt.y);
439end;
440
441procedure TGtkDeviceContext.TransfUpdateFont;
442var
443  AWidth, AHeight: Integer;
444  TmpObj: PGdiObject;
445begin
446  if (FCurrentFont <> nil) and (FCurrentFont^.GDIFontObject <> nil) and (FCurrentFont^.LogFont.lfFaceName[0] <> #0) then
447  begin
448    if FCurrentFont^.UntransfFontHeight = 0 then
449      FCurrentFont^.UntransfFontHeight := FCurrentFont^.LogFont.lfHeight;
450    AWidth := 0; AHeight := FCurrentFont^.UntransfFontHeight;
451    TransfExtent(AWidth, AHeight);
452    if FCurrentFont^.UntransfFontHeight > 0 then
453      AHeight := Abs(AHeight)
454    else
455      AHeight := -Abs(AHeight);
456    if AHeight = 0 then
457      if FCurrentFont^.LogFont.lfHeight > 0 then
458        AHeight := 1
459      else
460      if FCurrentFont^.LogFont.lfHeight < 0 then
461        AHeight := -1
462      else
463        AHeight := 0;
464    if FCurrentFont^.LogFont.lfHeight <> AHeight then
465    begin
466      FontCache.Unreference(FCurrentFont^.GDIFontObject);
467      FCurrentFont^.LogFont.lfHeight := AHeight;
468      TmpObj := {%H-}PGdiObject(PtrUInt(GTK2WidgetSet.CreateFontIndirect(FCurrentFont^.LogFont)));
469      FCurrentFont^.GDIFontObject := TmpObj^.GDIFontObject;
470      TmpObj^.GDIFontObject := nil;
471      TmpObj^.RefCount := 0;
472      GTK2WidgetSet.DisposeGDIObject(TmpObj);
473    end;
474  end;
475end;
476
477procedure TGtkDeviceContext.TransfUpdatePen;
478var
479  AWidth, AHeight: Integer;
480begin
481  if FCurrentPen <> nil then
482  begin
483    if FCurrentPen^.UnTransfPenWidth = 0 then
484      FCurrentPen^.UnTransfPenWidth := FCurrentPen^.GDIPenWidth;
485    AWidth := FCurrentPen^.UnTransfPenWidth;
486    AHeight := FCurrentPen^.UnTransfPenWidth;
487    TransfExtent(AWidth, AHeight);
488    AWidth := Abs(AWidth);
489    AHeight := Abs(AHeight);
490    if AWidth > AHeight then AWidth := AHeight;
491    if AWidth <= 0 then AWidth := 1;
492    if FCurrentPen^.GDIPenWidth <> DWord(AWidth) then
493    begin
494      FCurrentPen^.GDIPenWidth := AWidth;
495      Exclude(FFlags, dcfPenSelected);
496      SelectPenProps;
497    end;
498  end;
499end;
500
501procedure TGtkDeviceContext.SetWidget(AWidget: PGtkWidget; AWindow: PGdkWindow;
502                                      AWithChildWindows: Boolean; ADoubleBuffer: PGdkDrawable);
503
504  procedure RaiseWidgetWithoutClientArea;
505  begin
506    RaiseGDBException('TGtkDeviceContext.SetWidget: widget ' + DbgS(AWidget) + ' has no client area');
507  end;
508
509  procedure RaiseWidgetAlreadySet;
510  begin
511    RaiseGDBException('TGtkDeviceContext.SetWidget: widget already set');
512  end;
513
514  procedure RaiseUnableToRealize;
515  begin
516    RaiseGDBException('TGtkDeviceContext.SetWidget: Unable to realize GdkWindow');
517  end;
518
519var
520  ClientWidget: PGtkWidget;
521  W: PGtkWidget;
522begin
523  if FWidget <> nil then
524    RaiseWidgetAlreadySet;
525
526  FWithChildWindows := AWithChildWindows;
527  FWidget := AWidget;
528  FPixbuf := nil;
529
530  if (AWidget = nil) then
531  begin
532    // screen: ToDo: multiple desktops
533    FDrawable := gdk_screen_get_root_window(gdk_screen_get_default);
534  end else
535  begin
536    if ADoubleBuffer <> nil then
537    begin
538      Include(FFlags, dcfDoubleBuffer);
539      FOriginalDrawable := AWindow;
540      FDrawable := ADoubleBuffer;
541    end else
542    begin
543      // create a new devicecontext for this window
544      Exclude(FFlags, dcfDoubleBuffer);
545
546      if AWindow = nil then
547      begin
548        ClientWidget := GetFixedWidget(AWidget);
549        if ClientWidget = nil then RaiseWidgetWithoutClientArea;
550
551        AWindow := GetControlWindow(ClientWidget);
552        if AWindow = nil then
553        begin
554          W := gtk_widget_get_parent(AWidget);
555          //we are forcing window creation but not for GtkNotebook
556          //see issue #18754 and #20126
557          //Zeljko:This part should be NOT BE REMOVED since TToolbar, TFrame
558          //TGroupBox etc...depend on this. eg.TToolbar will lock
559          //mouse without realizing clientWidget.Also if THintWindow is
560          //visible it crashes sometimes. SO JUST NOTEBOOK !
561          if (W <> nil) and not GTK_IS_NOTEBOOK(W) then
562            gtk_widget_realize(ClientWidget);
563
564          AWindow := GetControlWindow(ClientWidget);
565          // Don't raise an exception. Not all operations needs drawable. For example font metrics:
566          // http://bugs.freepascal.org/view.php?id=14035
567          //if AWindow = nil then RaiseUnableToRealize;
568        end;
569      end else
570      begin
571        ClientWidget := AWidget;
572      end;
573
574      FDrawable := AWindow;
575      // GC is created on demand
576      if (FDrawable = nil) and not GTK_WIDGET_MAPPED(AWidget) then
577        FDrawable := gdk_screen_get_root_window(gdk_screen_get_default);
578    end;
579  end;
580
581  gdk_color_black(gdk_colormap_get_system, @CurrentTextColor.Color);
582  BuildColorRefFromGDKColor(CurrentTextColor);
583  gdk_color_white(gdk_colormap_get_system, @CurrentBackColor.Color);
584  BuildColorRefFromGDKColor(CurrentBackColor);
585  // font, brush, pen are created on demand
586end;
587
588procedure TGtkDeviceContext.Clear;
589var
590  g: TGDIType;
591
592  procedure WarnOwnedGDIObject;
593  begin
594    DebugLn(['TDeviceContext.Clear ',dbghex(PtrInt(Self)),' OwnedGDIObjects[',ord(g),']<>nil']);
595  end;
596
597begin
598  if Assigned(FPixbuf) then gdk_pixbuf_unref(FPixbuf);
599  FWidget := nil;
600  FDrawable := nil;
601  FPixbuf := nil;
602  FGC := nil;
603  FillChar(FGCValues, SizeOf(FGCValues), 0);
604
605  FViewPortExt := Point(1, 1);
606  FViewPortOrg := Point(0, 0);
607  FWindowExt := Point(1, 1);
608  FWindowOrg := Point(0, 0);
609  FMapMode := MM_TEXT;
610  if FHasTransf then
611  begin
612    FHasTransf := False;
613    TransfUpdateFont;
614    TransfUpdatePen;
615  end;
616
617  PenPos := Point(0, 0);
618
619  CurrentBitmap:=nil;
620  CurrentFont:=nil;
621  CurrentPen:=nil;
622  CurrentBrush:=nil;
623  CurrentPalette:=nil;
624  ClipRegion:=nil;
625  FillChar(CurrentTextColor,SizeOf(CurrentTextColor),0);
626  FillChar(CurrentBackColor,SizeOf(CurrentBackColor),0);
627  FillChar(PaintRectangle, SizeOf(PaintRectangle), 0);
628
629  SelectedColors:=dcscCustom;
630  SavedContext:=nil;
631  FFlags := [];
632
633  for g:=Low(TGDIType) to high(TGDIType) do
634    if OwnedGDIObjects[g]<>nil then
635      WarnOwnedGDIObject;
636end;
637
638{------------------------------------------------------------------------------
639  Function: CopyData - used by RestoreDC and SaveDC
640  Params:  DestinationDC:  a dc to copy data to
641           ClearSource: set true to make a move operation
642           MoveGDIOwnerShip: set true to pass the ownership of the GDI objects
643                             to Destination
644  Returns: True if succesful
645
646  Creates a copy DC from the given DC
647 ------------------------------------------------------------------------------}
648function TGtkDeviceContext.CopyDataFrom(ASource: TGtkDeviceContext; AClearSource, AMoveGDIOwnerShip, ARestore: Boolean): Boolean;
649  procedure RaiseRestoreDifferentWidget;
650  begin
651    RaiseGDBException('TGtkDeviceContext.CopyDataFrom: restore widget differs');
652  end;
653
654  procedure RaiseWidgetAlreadySet;
655  begin
656    RaiseGDBException('TGtkDeviceContext.CopyDataFrom: widget already set');
657  end;
658
659var
660  g: TGDIType;
661  CurGDIObject: PGDIObject;
662begin
663  Result := Assigned(Self) and Assigned(ASource);
664  if not Result then Exit;
665
666  if ARestore then
667  begin
668    if FWidget <> ASource.FWidget then
669      RaiseRestoreDifferentWidget;
670  end else
671  begin
672    if Assigned(FWidget) then
673      RaiseWidgetAlreadySet;
674    FWidget := ASource.FWidget;
675  end;
676
677  FWithChildWindows := ASource.FWithChildWindows;
678  FDrawable := ASource.FDrawable;
679  FPixbuf := ASource.Pixbuf;
680  FOriginalDrawable := ASource.FOriginalDrawable;
681
682  if Assigned(FGC) then
683  begin
684    // free old GC
685    gdk_gc_unref(FGC);
686    FGC := nil;
687    Exclude(FFlags, dcfPenSelected);
688  end;
689
690  if Assigned(ASource.FGC) and Assigned(FDrawable) then
691  begin
692    gdk_gc_get_values(ASource.FGC, @FGCValues);
693    FGC := gdk_gc_new_with_values(FDrawable, @FGCValues,
694      GDK_GC_FOREGROUND or GDK_GC_BACKGROUND or GDK_GC_SUBWINDOW);
695    Exclude(FFlags, dcfPenSelected);
696  end;
697
698  if dcfTextMetricsValid in ASource.Flags then
699  begin
700    Include(FFlags, dcfTextMetricsValid);
701    DCTextMetric := ASource.DCTextMetric;
702  end
703  else
704    Exclude(FFlags, dcfTextMetricsValid);
705
706  for g := Low(TGDIType) to High(TGDIType) do
707  begin
708    GDIObjects[g] := ASource.GDIObjects[g];
709    if AClearSource then
710      ASource.GDIObjects[g] := nil;
711
712    if AMoveGDIOwnerShip then
713    begin
714      if Assigned(OwnedGDIObjects[g]) then
715        DeleteObject(HGDIOBJ({%H-}PtrUInt(OwnedGDIObjects[g])));
716
717      CurGDIObject := ASource.OwnedGDIObjects[g];
718
719      if Assigned(CurGDIObject) then
720      begin
721        ASource.OwnedGDIObjects[g] := nil;
722        OwnedGDIObjects[g] := CurGDIObject;
723      end;
724    end;
725  end;
726  CopyGDIColor(ASource.CurrentTextColor, CurrentTextColor);
727  CopyGDIColor(ASource.CurrentBackColor, CurrentBackColor);
728
729  SelectedColors := dcscCustom;
730  PenPos := ASource.PenPos;
731
732  if FHasTransf then
733  begin
734    FHasTransf := False;
735    FMapMode := MM_TEXT;
736    FViewPortExt := Point(1, 1);
737    FViewPortOrg := Point(0, 0);
738    FWindowExt := Point(1, 1);
739    FWindowOrg := Point(0, 0);
740    TransfUpdateFont;
741    TransfUpdatePen;
742  end;
743
744  FHasTransf := ASource.HasTransf;
745  if FHasTransf then
746  begin
747    FMapMode := ASource.MapMode;
748    FViewPortExt := ASource.ViewPortExt;
749    FViewPortOrg := ASource.ViewPortOrg;
750    FWindowExt := ASource.WindowExt;
751    FWindowOrg := ASource.WindowOrg;
752    TransfUpdateFont;
753    TransfUpdatePen;
754  end;
755
756  SavedContext := nil;
757end;
758
759function TGtkDeviceContext.FillRect(ARect: TRect; ABrush: HBrush; SkipRop: Boolean): Boolean;
760var
761  Width, Height: Integer;
762  OldCurrentBrush: PGdiObject;
763  DCOrigin: TPoint;
764  BrushChanged: Boolean;
765  ClipArea: TGdkRectangle;
766begin
767  BrushChanged := False;
768  if not IsNullBrush then
769  begin
770    if FHasTransf then
771    begin
772      ARect := TransfRectIndirect(ARect);
773      TransfNormalize(ARect.Left, ARect.Right);
774      TransfNormalize(ARect.Top, ARect.Bottom);
775    end;
776
777    Width := ARect.Right - ARect.Left;
778    Height := ARect.Bottom - ARect.Top;
779
780    // Temporary hold the old brush to replace it with the given brush
781    OldCurrentBrush := GetBrush;
782    if not CompareGDIBrushes({%H-}PGdiObject(ABrush), OldCurrentBrush) then
783    begin
784      BrushChanged := True;
785      CurrentBrush := {%H-}PGdiObject(ABrush);
786      SelectedColors := dcscCustom;
787    end;
788
789    SelectBrushProps;
790    if SkipRop then
791      gdk_gc_set_function(GC, GDK_COPY);
792
793    DCOrigin := Offset;
794    ClipArea := ClipRect;
795    RemovePixbuf;
796    if (CurrentBrush^.GDIBrushFill = GDK_SOLID) and
797       (IsBackgroundColor(TColor(CurrentBrush^.GDIBrushColor.ColorRef))) then
798      StyleFillRectangle(Drawable, GC,
799                         CurrentBrush^.GDIBrushColor.ColorRef,
800                         ARect.Left + DCOrigin.X, ARect.Top + DCOrigin.Y,
801                         Width, Height, @ClipArea)
802    else
803      gdk_draw_rectangle(Drawable, GC, 1,
804                         ARect.Left + DCOrigin.X, ARect.Top + DCOrigin.Y,
805                         Width, Height);
806
807    if SkipRop then
808      gdk_gc_set_function(GC, GetFunction);
809
810    // Restore current brush
811    if BrushChanged then
812    begin
813      SelectedColors := dcscCustom;
814      CurrentBrush := OldCurrentBrush;
815    end;
816  end;
817
818  Result := True;
819end;
820
821procedure TGtkDeviceContext.CreateBrush;
822begin
823  if FCurrentBrush <> nil then Exit;
824  CurrentBrush := Gtk2Widgetset.CreateDefaultBrush;
825  OwnedGDIObjects[gdiBrush] := FCurrentBrush;
826end;
827
828procedure TGtkDeviceContext.CreateFont;
829var
830  NewFont: PGDIObject;
831  ClientWidget: PGtkWidget;
832begin
833  if FCurrentFont <> nil then exit;
834
835  // create font
836  if FWidget <> nil then
837  begin
838    ClientWidget := GetFixedWidget(FWidget);
839
840    NewFont := Gtk2Widgetset.NewGDIObject(gdiFont);
841    NewFont^.UntransfFontHeight := 0;
842    CurrentFont := NewFont;
843    FCurrentFont^.GDIFontObject := gtk_widget_create_pango_layout(ClientWidget, nil);
844
845    {$ifdef fontconsistencychecks}
846    if FontCache.FindGTKFont(FCurrentFont^.GDIFontObject) <> nil then
847      RaiseGDBException('inconsistency: font already in cache, maybe freed, but not removed from cache');
848    {$endif}
849
850    FontCache.AddWithoutName(FCurrentFont^.GDIFontObject);
851
852    // the gtk internal reference count was increased by
853    // gtk_widget_create_pango_layout and by FontCache.AddWithoutName
854    // reduce it to one, because only this DC is using them at this point
855    UnreferenceGtkIntfFont(FCurrentFont^.GDIFontObject);
856
857    {$ifdef fontconsistencychecks}
858    // MWE: are we paranoid or so ? (if you can't trust the cache, don't use it or stop coding)
859    // MG: some people are coding without knowing about the cache
860    if FontCache.FindGTKFont(FCurrentFont^.GDIFontObject) = nil then
861      RaiseGDBException('inconsistency: font added to cache, but can not be found');
862    {$endif}
863  end
864  else
865    CurrentFont := Gtk2Widgetset.CreateDefaultFont;
866  OwnedGDIObjects[gdiFont] := FCurrentFont;
867end;
868
869function TGtkDeviceContext.CreateGC: PGdkGC;
870begin
871  // create GC
872
873  if Drawable <> nil then
874  begin
875    if FWithChildWindows then
876    begin
877      FillChar(FGCValues, SizeOf(FGCValues), 0);
878      FGCValues.subwindow_mode := GDK_INCLUDE_INFERIORS;
879      Result := gdk_gc_new_with_values(Drawable, @FGCValues, GDK_GC_FUNCTION or GDK_GC_SUBWINDOW);
880    end else
881    begin
882      Result := gdk_gc_new(Drawable);
883    end;
884  end else
885  begin
886    // create default GC
887    Result := gdk_gc_new(gdk_screen_get_root_window(gdk_screen_get_default));
888  end;
889  if Result = nil then Exit;
890
891  gdk_gc_set_function(Result, GDK_COPY);
892  gdk_gc_get_values(Result, @FGCValues);
893end;
894
895procedure TGtkDeviceContext.CreateBitmap;
896begin
897  if FCurrentBitmap <> nil then Exit;
898  CurrentBitmap := GTK2Widgetset.CreateDefaultGDIBitmap;
899  OwnedGDIObjects[gdiBitmap] := FCurrentBitmap;
900end;
901
902procedure TGtkDeviceContext.CreateGDIObject(AGDIType: TGDIType);
903begin
904  case AGDIType of
905    gdiFont: CreateFont;
906    gdiBrush: CreateBrush;
907    gdiPen: CreatePen;
908    gdiBitmap: CreateBitmap;
909  else
910    RaiseGDBException('TGtkDeviceContext.CreateGDIObject');
911  end;
912end;
913
914procedure TGtkDeviceContext.CreatePen;
915begin
916  if FCurrentPen <> nil then exit;
917  CurrentPen := Gtk2WidgetSet.CreateDefaultPen;
918  OwnedGDIObjects[gdiPen] := FCurrentPen;
919end;
920
921
922function TGtkDeviceContext.GetGC: pgdkGC;
923begin
924  if FGC = nil then
925    FGC := CreateGC;
926  Result := FGC;
927end;
928
929function TGtkDeviceContext.GetFont: PGdiObject;
930begin
931  if FCurrentFont = nil then
932    CreateFont;
933
934  Result := FCurrentFont;
935end;
936
937function TGtkDeviceContext.GetBrush: PGdiObject;
938begin
939  if FCurrentBrush = nil then
940    CreateBrush;
941
942  Result := FCurrentBrush;
943end;
944
945function TGtkDeviceContext.GetPen: PGdiObject;
946begin
947  if FCurrentPen = nil then
948    CreatePen;
949
950  Result := FCurrentPen;
951end;
952
953function TGtkDeviceContext.GetROP2: Integer;
954begin
955  case GetFunction of
956    GDK_COPY:         result := R2_COPYPEN;
957    GDK_INVERT:       result := R2_NOT;
958    GDK_XOR:          result := R2_XORPEN;
959    GDK_CLEAR:        result := R2_BLACK;
960    GDK_AND:          result := R2_MASKPEN;
961    GDK_AND_REVERSE:  result := R2_MASKPENNOT;
962    GDK_AND_INVERT:   result := R2_MASKNOTPEN;
963    GDK_NOOP:         result := R2_NOP;
964    GDK_OR:           result := R2_MERGEPEN;
965    GDK_EQUIV:        result := R2_NOTXORPEN;
966    GDK_OR_REVERSE:   result := R2_MERGEPENNOT;
967    GDK_COPY_INVERT:  result := R2_NOTCOPYPEN;
968    GDK_NAND:         result := R2_NOTMASKPEN;
969    //GDK_NOR:          result := R2_NOTMERGEPEN;
970    GDK_SET:          result := R2_WHITE;
971  else
972    result := R2_COPYPEN;
973  end;
974end;
975
976function TGtkDeviceContext.HasGC: Boolean;
977begin
978  Result := FGC <> nil;
979end;
980
981function TGtkDeviceContext.IsNullBrush: boolean;
982begin
983  Result := (FCurrentBrush <> nil) and (FCurrentBrush^.IsNullBrush);
984end;
985
986
987function TGtkDeviceContext.IsNullPen: boolean;
988begin
989  Result := (FCurrentPen <> nil) and (FCurrentPen^.IsNullPen);
990end;
991
992procedure TGtkDeviceContext.ResetGCClipping;
993begin
994  if FGC = nil then Exit;
995
996  {$IFDEF DebugGDK}BeginGDKErrorTrap;{$endif}
997  gdk_gc_set_clip_mask(FGC, nil);
998  gdk_gc_set_clip_origin (FGC, 0,0);
999  {$IFDEF DebugGDK}EndGDKErrorTrap;{$endif}
1000
1001  SelectRegion;
1002end;
1003
1004function TGtkDeviceContext.SelectBitmap(AGdiObject: PGdiObject): PGdiObject;
1005var
1006  NewPixbuf: PGdkPixbuf;
1007  NewDrawable: PGdkPixmap;
1008  Mask: PGdkBitmap;
1009begin
1010  // always create, because a valid GDIObject is needed to restore
1011  Result := GetBitmap;
1012  if CurrentBitmap = AGDIObject then Exit;
1013
1014  NewPixbuf := nil;
1015  CurrentBitmap := AGDIObject;
1016  with FCurrentBitmap^ do
1017    case GDIBitmapType of
1018      gbPixmap: NewDrawable := GDIPixmapObject.Image;
1019      gbBitmap: NewDrawable := GDIBitmapObject;
1020      gbPixbuf:
1021        begin
1022          NewDrawable := nil;
1023          Mask := nil;
1024          NewPixbuf := GDIPixbufObject;
1025          gdk_pixbuf_render_pixmap_and_mask(GDIPixbufObject, NewDrawable, Mask, $80);
1026          GDIBitmapType := gbPixmap;
1027          GDIPixmapObject.Image := NewDrawable;
1028          GDIPixmapObject.Mask := Mask;
1029          if Visual <> nil then
1030            gdk_visual_unref(Visual);
1031          Visual := gdk_window_get_visual(NewDrawable);
1032          gdk_visual_ref(Visual);
1033        end;
1034    else
1035      DebugLn('[TGtkDeviceContext.SelectBitmap] - Unknown bitmaptype, DC=0x%p', [Pointer(Self)]);
1036      Exit;
1037    end;
1038
1039  // no drawable: this is normal, when restoring the default bitmap (FreeDC)
1040  if NewDrawable = nil then Exit;
1041
1042  if FGC <> nil then
1043    gdk_gc_unref(FGC);
1044  FDrawable := NewDrawable;
1045  FPixbuf := NewPixbuf;
1046  FGC := gdk_gc_new(FDrawable);
1047  gdk_gc_set_function(FGC, GDK_COPY);
1048  SelectedColors := dcscCustom;
1049end;
1050
1051{------------------------------------------------------------------------------
1052  Procedure: TGtkDeviceContext.SelectBrushProps
1053  Params:
1054  Returns: Nothing
1055
1056  Sets the forecolor and fill according to the brush
1057 ------------------------------------------------------------------------------}
1058procedure TGtkDeviceContext.SelectBrushProps;
1059begin
1060  if IsNullBrush then Exit;
1061
1062  // Force brush
1063  GetBrush;
1064
1065  EnsureGCColor(HDC(Self), dccCurrentBackColor, True, True);//BKColor
1066  EnsureGCColor(HDC(Self), dccGDIBrushColor, CurrentBrush^.GDIBrushFill = GDK_Solid, False);//Brush Color
1067
1068  if CurrentBrush^.GDIBrushFill = GDK_Solid then Exit;
1069  if CurrentBrush^.GDIBrushPixmap = nil then Exit;
1070
1071  if CurrentBrush^.GDIBrushFill = GDK_STIPPLED then
1072  begin
1073    //invert background / foreground colors to match Windows.FillRect behavior
1074    //with a 1bit bitmap pattern brush (bit set -> back color, bit unset -> text color)
1075    EnsureGCColor(HDC(Self), dccCurrentTextColor, False, True);
1076    EnsureGCColor(HDC(Self), dccCurrentBackColor, True, True);
1077    gdk_gc_set_stipple(GC, CurrentBrush^.GDIBrushPixmap);
1078    //use GDK_OPAQUE_STIPPLED to draw both background and foreground color
1079    gdk_gc_set_fill(GC, GDK_OPAQUE_STIPPLED);
1080  end
1081  else
1082  begin
1083    gdk_gc_set_tile(GC, CurrentBrush^.GDIBrushPixmap);
1084    gdk_gc_set_fill(GC, GDK_TILED);
1085  end;
1086
1087  gdk_gc_get_values(GC, @FGCValues);
1088end;
1089
1090function TGtkDeviceContext.SelectObject(AGdiObject: PGdiObject): PGdiObject;
1091begin
1092  case AGdiObject^.GDIType of
1093    gdiBitmap: Result := SelectBitmap(AGdiObject);
1094    gdiPen:    Result := SelectPen(AGdiObject);
1095  else
1096    // we only handle bitmaps here atm
1097    Result := {%H-}PGdiObject(GTK2WidgetSet.SelectObject(HDC(Self), {%H-}HGDIOBJ(AGdiObject)));
1098  end;
1099end;
1100
1101function TGtkDeviceContext.SelectPen(AGdiObject: PGdiObject): PGdiObject;
1102begin
1103  Result := GetPen;// always create, because a valid GDIObject is needed to restore
1104  if CurrentPen = AGDIObject then Exit;
1105
1106  CurrentPen := AGDIObject;
1107  Exclude(FFlags, dcfPenSelected);
1108  if FGC <> nil then
1109    SelectPenProps;
1110  SelectedColors := dcscCustom;
1111end;
1112
1113constructor TGtkDeviceContext.Create;
1114begin
1115  Clear;
1116  BkMode := OPAQUE;
1117end;
1118
1119destructor TGtkDeviceContext.Destroy;
1120begin
1121  if Assigned(FPixbuf) then gdk_pixbuf_unref(FPixbuf);
1122  inherited Destroy;
1123end;
1124
1125{------------------------------------------------------------------------------
1126  Procedure: TGtkDeviceContext.SelectPenProps
1127  Params:  DC: a (LCL)devicecontext
1128  Returns: Nothing
1129
1130  Sets the forecolor and fill according to the pen
1131 ------------------------------------------------------------------------------}
1132procedure TGtkDeviceContext.SelectPenProps;
1133var
1134  PenStyle: DWord;
1135  LineStyle: TGdkLineStyle;
1136  JoinStyle: TGdkJoinStyle;
1137  CapStyle: TGdkCapStyle;
1138  IsGeometric, IsExtPen: Boolean;
1139  PenWidth: gint;
1140
1141  procedure SetDashes(ADashes: array of gint8);
1142  var
1143    Multiplier: gint;
1144    i: integer;
1145  begin
1146    Multiplier := PenWidth;
1147    if Multiplier = 0 then
1148      Multiplier := 1;
1149
1150    // this works very well for geometric pens
1151    for i := Low(ADashes) to High(ADashes) do
1152      ADashes[i] := ADashes[i] * Multiplier;
1153
1154    laz_gdk_gc_set_dashes(GC, 0, @ADashes[0], Length(ADashes));
1155  end;
1156
1157begin
1158//  if IsNullPen then Exit;
1159
1160  EnsureGCColor(HDC(Self), dccCurrentBackColor, True, True); // BKColor
1161  EnsureGCColor(HDC(Self), dccGDIPenColor, False, False);    // Pen Color
1162
1163  if dcfPenSelected in FFlags then Exit;
1164  Exclude(FFlags, dcfPenInvalid);
1165  if GC = nil then Exit;
1166
1167  // force pen
1168  GetPen;
1169
1170  PenStyle := CurrentPen^.GDIPenStyle and PS_STYLE_MASK;
1171  IsExtPen := CurrentPen^.IsExtPen;
1172  PenWidth := CurrentPen^.GDIPenWidth;
1173
1174  if IsExtPen then
1175    IsGeometric := (CurrentPen^.GDIPenStyle and PS_TYPE_MASK) = PS_GEOMETRIC
1176  else
1177    IsGeometric := PenWidth > 1;
1178
1179  if not IsGeometric then
1180    PenWidth := 0;
1181
1182  CurrentPen^.IsNullPen := PenStyle = PS_NULL;
1183
1184  if IsExtPen and IsGeometric then
1185  begin
1186    case CurrentPen^.GDIPenStyle and PS_JOIN_MASK of
1187      PS_JOIN_ROUND: JoinStyle := GDK_JOIN_ROUND;
1188      PS_JOIN_BEVEL: JoinStyle := GDK_JOIN_BEVEL;
1189      PS_JOIN_MITER: JoinStyle := GDK_JOIN_MITER;
1190    end;
1191
1192    case CurrentPen^.GDIPenStyle and PS_ENDCAP_MASK of
1193      PS_ENDCAP_ROUND: CapStyle := GDK_CAP_ROUND;
1194      PS_ENDCAP_SQUARE: CapStyle := GDK_CAP_PROJECTING;
1195      PS_ENDCAP_FLAT: CapStyle := GDK_CAP_NOT_LAST;
1196    end;
1197  end
1198  else
1199  begin
1200    JoinStyle := GDK_JOIN_ROUND;
1201    if IsGeometric then
1202      CapStyle := GDK_CAP_ROUND
1203    else
1204      CapStyle := GDK_CAP_NOT_LAST;
1205  end;
1206
1207  if (PenStyle = PS_USERSTYLE) and (not IsExtPen or (CurrentPen^.GDIPenDashesCount = 0)) then
1208    PenStyle := PS_SOLID;
1209
1210  if (PenStyle = PS_SOLID) or (PenStyle = PS_INSIDEFRAME) then
1211    LineStyle := GDK_LINE_SOLID
1212  else
1213    LineStyle := GDK_LINE_ON_OFF_DASH;
1214
1215  {$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
1216  gdk_gc_set_line_attributes(GC, PenWidth, LineStyle, CapStyle, JoinStyle);
1217  {$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF}
1218
1219  // Paul Ishenin: I compared patterns with windows
1220  case PenStyle of
1221    PS_DASH:       SetDashes([3,1]);
1222    PS_DOT:        SetDashes([1,1]);
1223    PS_DASHDOT:    SetDashes([3,1,1,1]);
1224    PS_DASHDOTDOT: SetDashes([3,1,1,1,1,1]);
1225    PS_USERSTYLE:  laz_gdk_gc_set_dashes(GC, 0, CurrentPen^.GDIPenDashes, CurrentPen^.GDIPenDashesCount);
1226  end;
1227  {$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF}
1228  gdk_gc_get_values(GC, @FGCValues);
1229  Include(FFlags, dcfPenSelected);
1230end;
1231
1232{------------------------------------------------------------------------------
1233  procedure SelectRegion
1234
1235  Applies the current clipping region of the DC (DeviceContext) to the
1236  gc (GDK Graphic context - pgdkGC)
1237 ------------------------------------------------------------------------------}
1238procedure TGtkDeviceContext.SelectRegion;
1239var
1240  RGNType : Longint;
1241begin
1242  {$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF}
1243
1244  // force GC
1245  GetGC;
1246
1247  // Clear
1248  gdk_gc_set_clip_region(FGC,  nil);
1249  gdk_gc_set_clip_rectangle(FGC,  nil);
1250
1251  if ClipRegion <> nil then
1252  begin
1253    RGNType := RegionType(ClipRegion^.GDIRegionObject);
1254    if (RGNType <> ERROR) and (RGNType <> NULLREGION) then
1255      gdk_gc_set_clip_region(FGC,  ClipRegion^.GDIRegionObject);
1256  end;
1257
1258  {$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF}
1259end;
1260
1261{------------------------------------------------------------------------------
1262  Procedure: TGtkDeviceContext.SelectTextProps
1263  Params:
1264  Returns: Nothing
1265
1266  Sets the forecolor and fill according to the Textcolor
1267 ------------------------------------------------------------------------------}
1268procedure TGtkDeviceContext.SelectTextProps;
1269begin
1270  EnsureGCColor(HDC(Self), dccCurrentBackColor, True, True);//BKColor
1271  EnsureGCColor(HDC(Self), dccCurrentTextColor, False, False);//Font Color
1272end;
1273
1274function TGtkDeviceContext.GetBitmap: PGdiObject;
1275begin
1276  if FCurrentBitmap = nil then
1277    CreateBitmap;
1278
1279  Result := FCurrentBitmap;
1280end;
1281
1282function TGtkDeviceContext.GetFunction: TGdkFunction;
1283begin
1284  Result := GCValues._function;
1285end;
1286
1287
1288procedure SetLayoutText(ALayout: PPangoLayout; AText: PChar; ALength: PtrInt);
1289var
1290  OldStr: PChar;
1291begin
1292  OldStr := pango_layout_get_text(ALayout);
1293  if (strlen(OldStr)<>ALength) or (strlcomp(AText, OldStr, ALength) <> 0) then
1294    pango_layout_set_text(ALayout, AText, ALength);
1295end;
1296
1297procedure TGtkDeviceContext.DrawTextWithColors(AText: PChar; ALength: LongInt;
1298  X, Y: Integer; FGColor, BGColor: PGdkColor);
1299var
1300  WidgetCont: PPangoContext;
1301  NewMatrix: TPangoMatrix;
1302  OldMatrix: PPangoMatrix;
1303  renderer: PGdkPangoRenderer;
1304  Direction : TPangoDirection;
1305  AFont: PGdiObject;
1306
1307  procedure SetColors(AFGColor, ABGColor: PGdkColor); inline;
1308  begin
1309    gdk_pango_renderer_set_override_color(renderer, PANGO_RENDER_PART_FOREGROUND, AFGColor);
1310    gdk_pango_renderer_set_override_color(renderer, PANGO_RENDER_PART_UNDERLINE, AFGColor);
1311    gdk_pango_renderer_set_override_color(renderer, PANGO_RENDER_PART_STRIKETHROUGH, AFGColor);
1312    gdk_pango_renderer_set_override_color(renderer, PANGO_RENDER_PART_BACKGROUND, ABGColor);
1313  end;
1314
1315begin
1316  AFont := GetFont;
1317  SetLayoutText(AFont^.GDIFontObject, AText, ALength);
1318
1319  WidgetCont := pango_layout_get_context(AFont^.GDIFontObject);
1320  Direction := pango_find_base_dir(AText, ALength);
1321  pango_context_set_base_dir(WidgetCont, Direction);
1322
1323  if AFont^.LogFont.lfEscapement <> 0 then
1324  begin
1325    if Widget <> nil then
1326      renderer := gdk_pango_renderer_get_default(gtk_widget_get_screen(Widget))
1327    else
1328      renderer := gdk_pango_renderer_get_default(gdk_screen_get_default);
1329    RemovePixbuf;
1330    gdk_pango_renderer_set_drawable(renderer, drawable);
1331    gdk_pango_renderer_set_gc(renderer, GC);
1332    SetColors(FGColor, BGColor);
1333
1334    OldMatrix := pango_context_get_matrix(WidgetCont);
1335    NewMatrix.xx := 1.0;
1336    NewMatrix.xy := 0.0;
1337    NewMatrix.yx := 0.0;
1338    NewMatrix.yy := 1.0;
1339    NewMatrix.x0 := 0.0;
1340    NewMatrix.y0 := 0.0;
1341    pango_matrix_translate(@NewMatrix, X, Y);
1342    pango_matrix_rotate(@NewMatrix, AFont^.LogFont.lfEscapement div 10);
1343
1344    pango_context_set_matrix(WidgetCont, @NewMatrix);
1345    pango_layout_context_changed(AFont^.GDIFontObject);
1346    pango_renderer_draw_layout(PPangoRenderer(renderer), AFont^.GDIFontObject, X, Y);
1347
1348    //now reset
1349    pango_context_set_matrix(WidgetCont, OldMatrix);
1350    pango_layout_context_changed(AFont^.GDIFontObject);
1351
1352    SetColors(nil, nil);
1353    gdk_pango_renderer_set_drawable(renderer, nil);
1354    gdk_pango_renderer_set_gc(renderer, nil);
1355  end
1356  else
1357    gdk_draw_layout_with_colors(drawable, GC, X, Y, AFont^.GDIFontObject, FGColor, BGColor);
1358end;
1359
1360