1 unit Gtk2Themes;
2
3 {$mode objfpc}{$H+}
4
5 interface
6
7 uses
8 // rtl
9 Types, Classes, SysUtils,
10 // os
11 glib2, gdk2, gtk2, gdk2pixbuf,
12 // lcl
13 LCLType, LCLProc, LCLIntf, Graphics, Themes, TmSchema, Forms,
14 // widgetset
15 Gtk2Def, Gtk2Int, Gtk2Proc, Gtk2Globals;
16
17 type
18 TGtkPainterType =
19 (
20 gptNone,
21 gptDefault,
22 gptHLine,
23 gptVLine,
24 gptShadow,
25 gptBox,
26 gptBoxGap,
27 gptFlatBox,
28 gptCheck,
29 gptOption,
30 gptTab,
31 gptSlider,
32 gptHandle,
33 gptExpander,
34 gptResizeGrip,
35 gptFocus,
36 gptArrow,
37 gptPixmap,
38 gptComboBox
39 );
40
41 TGtkStyleParams = record
42 Style : PGtkStyle; // paint style
43 Painter : TGtkPainterType; // type of paint handler
44 Widget : PGtkWidget; // widget
45 Window : PGdkWindow; // paint window
46 Origin : TPoint; // offset
47 State : TGtkStateType; // Style state
48 Shadow : TGtkShadowType; // Shadow
49 Detail : String; // Detail (button, checkbox, ...)
50 Orientation: TGtkOrientation; // Orientation (horizontal/vertical)
51 ArrowType : TGtkArrowType; // type of arrow
52 Fill : Boolean; // fill inside area
53 GapSide : TGtkPositionType;//
54 GapX : gint;
55 GapWidth : gint;
56 MaxWidth : gint; // max area width
57 Expander : TGtkExpanderStyle; // treeview expander
58 ExpanderSize: Integer;
59 Edge : TGdkWindowEdge;
60 IsHot : Boolean;
61 end;
62
63 type
64 { TGtk2ThemeServices }
65
66 TGtk2ThemeServices = class(TThemeServices)
67 protected
68 procedure DrawPixmap(DC: HDC; Area: PGdkRectangle; PixmapIndex: Byte); virtual;
69
InitThemesnull70 function InitThemes: Boolean; override;
UseThemesnull71 function UseThemes: Boolean; override;
ThemedControlsEnablednull72 function ThemedControlsEnabled: Boolean; override;
73
74 procedure InternalDrawParentBackground({%H-}Window: HWND; {%H-}Target: HDC; {%H-}Bounds: PRect); override;
GetBaseDetailsSizenull75 function GetBaseDetailsSize(Details: TThemedElementDetails): TSize;
76
GetParamsCountnull77 function GetParamsCount(Details: TThemedElementDetails): Integer; virtual;
78
79 procedure GtkDrawElement(DC: HDC; Details: TThemedElementDetails;
80 const R: TRect; ClipRect: PRect);
GetGtkStyleParamsnull81 function GetGtkStyleParams(DC: HDC; Details: TThemedElementDetails;
82 AIndex: Integer): TGtkStyleParams;
83 public
GetDetailSizenull84 function GetDetailSize(Details: TThemedElementDetails): TSize; override;
GetStockImagenull85 function GetStockImage(StockID: LongInt; out Image, Mask: HBitmap): Boolean; override;
GetOptionnull86 function GetOption(AOption: TThemeOption): Integer; override;
87 procedure DrawElement(DC: HDC; Details: TThemedElementDetails;
88 const R: TRect; ClipRect: PRect); override;
89
90 procedure DrawText(DC: HDC; Details: TThemedElementDetails; const S: String; R: TRect; Flags, {%H-}Flags2: Cardinal); override;
91 procedure DrawText(ACanvas: TPersistent; Details: TThemedElementDetails; const S: String; R: TRect; Flags, Flags2: Cardinal); virtual; overload; reintroduce;
92
ContentRectnull93 function ContentRect(DC: HDC; Details: TThemedElementDetails; BoundingRect: TRect): TRect; override;
HasTransparentPartsnull94 function HasTransparentParts({%H-}Details: TThemedElementDetails): Boolean; override;
95 end;
96
97 const
98 // most common maps
99 GtkButtonMap: array[0..6] of TGtkStateType =
100 (
101 { filter ? } GTK_STATE_NORMAL,
102 { normal } GTK_STATE_NORMAL,
103 { hot } GTK_STATE_PRELIGHT,
104 { pressed } GTK_STATE_ACTIVE,
105 { disabled } GTK_STATE_INSENSITIVE,
106 { defaulted/checked } GTK_STATE_ACTIVE,
107 { hot + checked } GTK_STATE_ACTIVE
108 );
109 GtkRadioCheckBoxMap: array[0..12] of TGtkStateType =
110 (
111 { Filler } GTK_STATE_NORMAL,
112 { UNCHECKEDNORMAL } GTK_STATE_NORMAL,
113 { UNCHECKEDHOT } GTK_STATE_PRELIGHT,
114 { UNCHECKEDPRESSED } GTK_STATE_ACTIVE,
115 { UNCHECKEDDISABLED } GTK_STATE_INSENSITIVE,
116 { CHECKEDNORMAL } GTK_STATE_NORMAL,
117 { CHECKEDHOT } GTK_STATE_PRELIGHT,
118 { CHECKEDPRESSED } GTK_STATE_ACTIVE,
119 { CHECKEDDISABLED } GTK_STATE_INSENSITIVE,
120 { MIXEDNORMAL } GTK_STATE_NORMAL,
121 { MIXEDHOT } GTK_STATE_PRELIGHT,
122 { MIXEDPRESSED } GTK_STATE_ACTIVE,
123 { MIXEDDISABLED } GTK_STATE_INSENSITIVE
124 );
125 GtkTitleButtonMap: array[0..5] of TGtkStateType =
126 (
127 { filter ? } GTK_STATE_NORMAL,
128 { normal } GTK_STATE_NORMAL,
129 { hot } GTK_STATE_PRELIGHT,
130 { pressed } GTK_STATE_ACTIVE,
131 { disabled } GTK_STATE_INSENSITIVE,
132 { inactive } GTK_STATE_INSENSITIVE
133 );
134
135 implementation
136 uses math;
137
138 {$I gtk2stdpixmaps.inc}
139
GetColumnButtonFromTreeViewnull140 function GetColumnButtonFromTreeView(AWidget: PGtkWidget; Part: Integer): PGtkWidget;
141 var
142 AColumn: PGtkTreeViewColumn;
143 AIndex: Integer;
144 begin
145 Result := nil;
146 if not GTK_IS_TREE_VIEW(AWidget) then
147 exit;
148
149 if Part = HP_HEADERITEMLEFT then
150 AIndex := 0
151 else if Part = HP_HEADERITEMRIGHT then
152 AIndex := 2
153 else
154 AIndex := 1;
155
156 AColumn := gtk_tree_view_get_column(PGtkTreeView(AWidget), AIndex);
157 if AColumn = nil then
158 Exit;
159 Result := AColumn^.button;
160 end;
161
162 { TGtk2ThemeServices }
163
164 procedure TGtk2ThemeServices.DrawPixmap(DC: HDC; Area: PGdkRectangle;
165 PixmapIndex: Byte);
166 var
167 APixmap, APixmapMask: PGdkPixmap;
168 DevCtx: TGtkDeviceContext absolute DC;
169 w, h: gint;
170 begin
171 if (PixmapIndex >= Low(PixmapArray)) and (PixmapIndex <= High(PixmapArray)) then
172 begin
173 APixmapMask := nil;
174 APixmap := gdk_pixmap_create_from_xpm_d(DevCtx.Drawable,
175 APixmapMask, nil, PixmapArray[PixmapIndex]);
176 if APixmap <> nil then
177 begin
178 gdk_drawable_get_size(APixmap, @w, @h);
179 w := (Area^.Width - w) div 2;
180 if w < 0 then
181 w := 0;
182 h := (Area^.Height - h) div 2;
183 if h < 0 then
184 h := 0;
185 if APixmapMask <> nil then
186 begin
187 gdk_gc_set_clip_mask(DevCtx.GC, APixmapMask);
188 gdk_gc_set_clip_origin(DevCtx.GC, Area^.x + w, Area^.y + h);
189 end;
190 gdk_draw_pixmap(DevCtx.Drawable, DevCtx.GC, APixmap, 0, 0, Area^.x + w, Area^.y + h,
191 Area^.Width, Area^.Height);
192 if APixmapMask <> nil then
193 DevCtx.ResetGCClipping;
194 gdk_pixmap_unref(APixmap);
195 end;
196 if APixmapMask <> nil then
197 gdk_pixmap_unref(APixmapMask);
198 end;
199 end;
200
TGtk2ThemeServices.InitThemesnull201 function TGtk2ThemeServices.InitThemes: Boolean;
202 begin
203 Result:=True;
204 end;
205
TGtk2ThemeServices.UseThemesnull206 function TGtk2ThemeServices.UseThemes: Boolean;
207 begin
208 Result:=True;
209 end;
210
TGtk2ThemeServices.ThemedControlsEnablednull211 function TGtk2ThemeServices.ThemedControlsEnabled: Boolean;
212 begin
213 Result:=True;
214 end;
215
216 procedure TGtk2ThemeServices.InternalDrawParentBackground(Window: HWND;
217 Target: HDC; Bounds: PRect);
218 begin
219 // ToDo: TGtk2ThemeServices.InternalDrawParentBackground: What to do?
220 end;
221
TGtk2ThemeServices.GetBaseDetailsSizenull222 function TGtk2ThemeServices.GetBaseDetailsSize(Details: TThemedElementDetails): TSize;
223 begin
224 Result := inherited GetDetailSize(Details);
225 end;
226
227
GetGtkStyleParamsnull228 function TGtk2ThemeServices.GetGtkStyleParams(DC: HDC;
229 Details: TThemedElementDetails; AIndex: Integer): TGtkStyleParams;
230 var
231 DevCtx: TGtkDeviceContext absolute DC;
232 ClientWidget: PGtkWidget;
233 begin
234 FillByte(Result{%H-}, SizeOf(Result), 0);
235 if not Gtk2WidgetSet.IsValidDC(DC) then Exit;
236
237 Result.Widget := DevCtx.Widget;
238 if Result.Widget <> nil then
239 begin
240 ClientWidget := GetFixedWidget(Result.Widget);
241 if ClientWidget <> nil then
242 Result.Widget := ClientWidget;
243 Result.Style := gtk_widget_get_style(Result.Widget);
244 end;
245 Result.Window := DevCtx.Drawable;
246 Result.Origin := DevCtx.Offset;
247
248 Result.Painter := gptDefault;
249 Result.State := GTK_STATE_NORMAL;
250 Result.Detail := '';
251 Result.Shadow := GTK_SHADOW_NONE;
252 Result.ArrowType := GTK_ARROW_UP;
253 Result.Fill := False;
254 Result.IsHot := False;
255 Result.GapSide := GTK_POS_LEFT;
256 Result.GapWidth := 0;
257 Result.GapX := 0;
258 Result.MaxWidth := 0;
259
260 case Details.Element of
261 teButton:
262 begin
263 case Details.Part of
264 BP_PUSHBUTTON:
265 begin
266 Result.Widget := GetStyleWidget(lgsButton);
267 Result.Style := GetStyle(lgsButton);
268 Result.State := GtkButtonMap[Details.State];
269 if Details.State = PBS_PRESSED then
270 Result.Shadow := GTK_SHADOW_IN
271 else
272 Result.Shadow := GTK_SHADOW_OUT;
273
274 Result.IsHot:= Result.State = GTK_STATE_PRELIGHT;
275 Result.Detail := 'button';
276 Result.Painter := gptBox;
277 end;
278 BP_RADIOBUTTON:
279 begin
280 Result.Widget := GetStyleWidget(lgsRadiobutton);
281 if Result.Style = nil then
282 Result.Style := GetStyle(lgsRadiobutton);
283 Result.State := GtkRadioCheckBoxMap[Details.State];
284 if Details.State >= RBS_CHECKEDNORMAL then
285 Result.Shadow := GTK_SHADOW_IN
286 else
287 Result.Shadow := GTK_SHADOW_OUT;
288 Result.Detail := 'radiobutton';
289 Result.Painter := gptOption;
290 end;
291 BP_CHECKBOX:
292 begin
293 Result.Widget := GetStyleWidget(lgsCheckbox);
294 if Result.Style = nil then
295 Result.Style := GetStyle(lgsCheckbox);
296 Result.State := GtkRadioCheckBoxMap[Details.State];
297 Result.Detail := 'checkbutton';
298 if Details.State >= CBS_MIXEDNORMAL then
299 result.Shadow := GTK_SHADOW_ETCHED_IN
300 else
301 if Details.State >= CBS_CHECKEDNORMAL then
302 Result.Shadow := GTK_SHADOW_IN
303 else
304 Result.Shadow := GTK_SHADOW_OUT;
305 Result.Painter := gptCheck;
306 end;
307 end;
308 end;
309 teComboBox:
310 begin
311 Result.Widget := GetStyleWidget(lgsComboBox);
312 if Result.Style = nil then
313 Result.Style := GetStyle(lgsComboBox);
314
315 Result.Detail := 'button';
316 Result.State := GTK_STATE_NORMAL;
317 if Details.State = CBXS_DISABLED then
318 Result.State := GTK_STATE_INSENSITIVE
319 else
320 if Details.State = CBXS_HOT then
321 Result.State := GTK_STATE_PRELIGHT
322 else
323 if Details.State = CBXS_PRESSED then
324 Result.State := GTK_STATE_ACTIVE;
325
326 Result.Painter := gptComboBox;
327 end;
328 teHeader:
329 begin
330 Result.Widget := GetColumnButtonFromTreeView(GetStyleWidget(lgsTreeView), Details.Part);
331 if Result.Widget = nil then
332 Result.Widget := GetStyleWidget(lgsTreeView);
333 Result.Style := gtk_widget_get_style(Result.Widget);
334 Result.State := GtkButtonMap[Details.State];
335 if Details.State = PBS_PRESSED then
336 Result.Shadow := GTK_SHADOW_IN
337 else
338 Result.Shadow := GTK_SHADOW_OUT;
339
340 Result.IsHot:= Result.State = GTK_STATE_PRELIGHT;
341
342 Result.Detail := 'button';
343 Result.Painter := gptBox;
344 end;
345 teStatus:
346 begin
347 Result.Widget := GetStyleWidget(lgsStatusBar);
348 if Result.Style = nil then
349 Result.Style := GetStyle(lgsStatusBar);
350 Result.Detail := 'statubar';
351 Result.State := GTK_STATE_NORMAL;
352 case Details.Part of
353 SP_PANE:
354 begin
355 Result.Painter := gptShadow;
356 Result.Shadow := GTK_SHADOW_OUT;
357 end;
358 SP_GRIPPER:
359 begin
360 Result.Painter := gptResizeGrip;
361 Result.Edge := GDK_WINDOW_EDGE_SOUTH_EAST;
362 end;
363 end;
364 end;
365 teToolBar:
366 begin
367 case Details.Part of
368 TP_BUTTON,
369 TP_DROPDOWNBUTTON,
370 TP_SPLITBUTTON,
371 TP_SPLITBUTTONDROPDOWN:
372 begin
373 Result.Widget := GetStyleWidget(lgsToolButton);
374 case Details.State of
375 TS_PRESSED, TS_CHECKED, TS_HOTCHECKED:
376 Result.Shadow := GTK_SHADOW_IN;
377 TS_HOT:
378 Result.Shadow := GTK_SHADOW_ETCHED_OUT;
379 else
380 Result.Shadow := GTK_SHADOW_NONE;
381 end;
382 if Details.Part = TP_SPLITBUTTONDROPDOWN then
383 begin
384 case Details.State of
385 TS_DISABLED: Result.State := GTK_STATE_INSENSITIVE;
386 else
387 Result.State := GTK_STATE_NORMAL;
388 end;
389 end else
390 Result.State := GtkButtonMap[Details.State];
391
392 Result.IsHot := Details.State in [TS_HOT, TS_HOTCHECKED];
393 if Result.Style = nil then
394 Result.Style := GetStyle(lgsToolButton);
395 if (Details.Part = TP_SPLITBUTTONDROPDOWN) then
396 begin
397 Result.Detail := 'arrow';
398 Result.ArrowType := GTK_ARROW_DOWN;
399 Result.Fill := True;
400 Result.Painter := gptArrow;
401 Result.MaxWidth := 10;
402 end
403 else
404 begin
405 Result.Detail := 'button';
406 if Result.Shadow = GTK_SHADOW_NONE then
407 Result.Painter := gptNone
408 else
409 Result.Painter := gptBox;
410 end;
411 end;
412 TP_SEPARATOR,
413 TP_SEPARATORVERT:
414 begin
415 Result.State := GTK_STATE_NORMAL;
416 Result.Shadow := GTK_SHADOW_NONE;
417 Result.Detail := 'toolbar';
418 if Details.Part = TP_SEPARATOR then
419 Result.Painter := gptVLine
420 else
421 Result.Painter := gptHLine;
422 end;
423 end;
424 end;
425 teRebar:
426 begin
427 case Details.Part of
428 RP_GRIPPER, RP_GRIPPERVERT:
429 begin
430 Result.State := GTK_STATE_NORMAL;
431 Result.Shadow := GTK_SHADOW_NONE;
432 Result.Detail := 'paned';
433 Result.Painter := gptHandle;
434 if Details.Part = RP_GRIPPER then
435 begin
436 Result.Orientation := GTK_ORIENTATION_VERTICAL;
437 Result.Widget := GetStyleWidget(lgsVerticalPaned);
438 end
439 else
440 begin
441 Result.Orientation := GTK_ORIENTATION_HORIZONTAL;
442 Result.Widget := GetStyleWidget(lgsHorizontalPaned);
443 end;
444 end;
445 RP_BAND:
446 begin
447 Result.Widget := GetStyleWidget(lgsVerticalPaned);
448 Result.State := GtkButtonMap[Details.State];
449 Result.Shadow := GTK_SHADOW_NONE;
450 Result.Detail := 'paned';
451 Result.Painter := gptFlatBox;
452 end;
453 end;
454 end;
455 teWindow:
456 begin
457 if Details.Part in [WP_SMALLCLOSEBUTTON, WP_MDIMINBUTTON, WP_MDIRESTOREBUTTON, WP_MDICLOSEBUTTON] then
458 begin
459 Result.State := GtkTitleButtonMap[Details.State];
460 Result.Shadow := GTK_SHADOW_NONE;
461 case Details.Part of
462 WP_MDIMINBUTTON: Result.Detail := #1;
463 WP_MDIRESTOREBUTTON: Result.Detail := #2;
464 WP_SMALLCLOSEBUTTON,
465 WP_MDICLOSEBUTTON: Result.Detail := #3;
466 end;
467 Result.Painter := gptPixmap;
468 end;
469 end;
470 teTab:
471 begin
472 Result.Widget := GetStyleWidget(lgsNotebook);
473 if Result.Style = nil then
474 Result.Style := GetStyle(lgsNotebook);
475 Result.State := GTK_STATE_NORMAL;
476 Result.Shadow := GTK_SHADOW_OUT;
477 Result.Detail := 'notebook';
478 if Details.Part = TABP_PANE then
479 Result.Painter := gptShadow
480 else
481 if Details.Part = TABP_BODY then
482 Result.Painter := gptBox;
483 end;
484 teToolTip:
485 begin
486 Result.Style := GetStyle(lgsTooltip);
487 Result.Widget := GetStyleWidget(lgsTooltip);
488 Result.State := GTK_STATE_NORMAL;
489 Result.Shadow := GTK_SHADOW_OUT;
490 Result.Detail := 'tooltip';
491 if Details.Part = TTP_STANDARD then
492 Result.Painter := gptFlatBox;
493 end;
494 teTreeview:
495 begin
496 if Details.Part in [TVP_GLYPH, TVP_HOTGLYPH] then
497 begin
498 Result.Painter := gptExpander;
499 Result.Shadow := GTK_SHADOW_NONE;
500 if Details.Part = TVP_GLYPH then
501 Result.State := GTK_STATE_NORMAL
502 else
503 Result.State := GTK_STATE_PRELIGHT;
504 Result.Widget := GetStyleWidget(lgsTreeView);
505 Result.Detail := 'treeview';
506 if Details.State = GLPS_CLOSED then
507 Result.Expander := GTK_EXPANDER_COLLAPSED
508 else
509 Result.Expander := GTK_EXPANDER_EXPANDED;
510
511 Result.ExpanderSize := GetDetailSize(Details).cx;
512 end
513 else
514 if Details.Part = TVP_TREEITEM then
515 begin
516 Result.Widget := GetStyleWidget(lgsTreeView);
517 Result.Shadow := GTK_SHADOW_NONE;
518 if AIndex = 0 then
519 begin
520 Result.Painter := gptFlatBox;
521 case Details.State of
522 TREIS_SELECTED,
523 TREIS_HOTSELECTED: Result.State := GTK_STATE_SELECTED;
524 TREIS_SELECTEDNOTFOCUS: Result.State := GTK_STATE_SELECTED; //Was: GTK_STATE_ACTIVE;
525 TREIS_HOT: Result.State := GTK_STATE_PRELIGHT;
526 TREIS_DISABLED: Result.State := GTK_STATE_INSENSITIVE;
527 else
528 Result.State := GTK_STATE_NORMAL;
529 end;
530 Result.Detail := 'cell_even';
531 end
532 else
533 if AIndex = 1 then
534 begin
535 Result.Detail := 'treeview';
536 if Details.State = TREIS_SELECTED then
537 begin
538 Result.State := GTK_STATE_SELECTED;
539 Result.Painter := gptFocus
540 end
541 else
542 begin
543 Result.State := GTK_STATE_NORMAL;
544 Result.Painter := gptNone;
545 end;
546 end;
547 end;
548 end;
549 end;
550 if Result.Style = nil then
551 Result.Style := gtk_widget_get_default_style();
552 end;
553
554
GetParamsCountnull555 function TGtk2ThemeServices.GetParamsCount(Details: TThemedElementDetails): Integer;
556 begin
557 if (Details.Element = teTreeview) and (Details.Part = TVP_TREEITEM) then
558 Result := 2
559 else
560 begin
561 Result := 1;
562 end;
563 end;
564
GetDetailSizenull565 function TGtk2ThemeServices.GetDetailSize(Details: TThemedElementDetails): TSize;
566 var
567 AValue: TGValue;
568 begin
569 case Details.Element of
570 teTreeView:
571 if (Byte(Details.Part) in [TVP_GLYPH, TVP_HOTGLYPH]) then
572 begin
573 FillChar(AValue{%H-}, SizeOf(AValue), 0);
574 g_value_init(@AValue, G_TYPE_INT);
575 gtk_widget_style_get_property(GetStyleWidget(lgsTreeView), 'expander-size', @AValue);
576 Result := Size(AValue.data[0].v_int, AValue.data[0].v_int);
577 end else
578 Result := GetBaseDetailsSize(Details);
579 teButton:
580 if (Byte(Details.Part) in [BP_CHECKBOX, BP_RADIOBUTTON]) then
581 begin
582 FillChar(AValue{%H-}, SizeOf(AValue), 0);
583 g_value_init(@AValue, G_TYPE_INT);
584 if Details.Part = BP_CHECKBOX then
585 gtk_widget_style_get_property(GetStyleWidget(lgsCheckbox),'indicator-size', @AValue)
586 else
587 gtk_widget_style_get_property(GetStyleWidget(lgsRadioButton),'indicator-size', @AValue);
588 Result := Size(AValue.data[0].v_int, AValue.data[0].v_int);
589 end else
590 Result := GetBaseDetailsSize(Details);
591 {$IFDEF LINUX} // fix tbsButtonDrop arrow outside button bounds
592 teToolBar:
593 if (Details.Part = TP_DROPDOWNBUTTON) then
594 begin
595 Result.cy := -1;
596 Result.cx := 15;
597 end else
598 Result := GetBaseDetailsSize(Details);
599 {$ENDIF}
600 teHeader:
601 if Details.Part = HP_HEADERSORTARROW then
602 Result := Size(-1, -1) // not yet supported
603 else
604 Result := GetBaseDetailsSize(Details);
605 else
606 Result := GetBaseDetailsSize(Details);
607 end;
608 end;
609
TGtk2ThemeServices.GetStockImagenull610 function TGtk2ThemeServices.GetStockImage(StockID: LongInt; out Image, Mask: HBitmap): Boolean;
611 var
612 GDIObj: PGDIObject;
613 StockName: PChar;
614 Style: PGtkStyle;
615 IconSet: PGtkIconSet;
616 Pixbuf: PGDKPixbuf;
617 begin
618 case StockID of
619 idButtonOk: StockName := GTK_STOCK_OK;
620 idButtonCancel: StockName := GTK_STOCK_CANCEL;
621 idButtonYes: StockName := GTK_STOCK_YES;
622 idButtonYesToAll: StockName := GTK_STOCK_YES;
623 idButtonNo: StockName := GTK_STOCK_NO;
624 idButtonNoToAll: StockName := GTK_STOCK_NO;
625 idButtonHelp: StockName := GTK_STOCK_HELP;
626 idButtonAbort: StockName := GTK_STOCK_STOP;
627 idButtonClose: StockName := GTK_STOCK_CLOSE;
628 // this is disputable but anyway stock icons looks like our own
629 idButtonAll: StockName := GTK_STOCK_APPLY;
630 idButtonIgnore: StockName := GTK_STOCK_DELETE;
631 idButtonRetry: StockName := GTK_STOCK_REFRESH;
632 idButtonOpen: StockName := GTK_STOCK_OPEN;
633 idButtonSave: StockName := GTK_STOCK_SAVE;
634 idButtonShield: StockName := GTK_STOCK_DIALOG_AUTHENTICATION;
635
636 idDialogWarning : StockName := GTK_STOCK_DIALOG_WARNING;
637 idDialogError: StockName := GTK_STOCK_DIALOG_ERROR;
638 idDialogInfo: StockName := GTK_STOCK_DIALOG_INFO;
639 idDialogConfirm: StockName := GTK_STOCK_DIALOG_QUESTION;
640 idDialogShield: StockName := GTK_STOCK_DIALOG_AUTHENTICATION;
641 else
642 begin
643 Result := inherited GetStockImage(StockID, Image, Mask);
644 Exit;
645 end;
646 end;
647
648 if (StockID >= idButtonBase) and (StockID <= idDialogBase) then
649 Style := GetStyle(lgsButton)
650 else
651 Style := GetStyle(lgsWindow);
652
653 if (Style = nil) or (not GTK_IS_STYLE(Style)) then
654 begin
655 Result := inherited GetStockImage(StockID, Image, Mask);
656 Exit;
657 end;
658
659 IconSet := gtk_style_lookup_icon_set(Style, StockName);
660
661 if (IconSet = nil) then
662 begin
663 Result := inherited GetStockImage(StockID, Image, Mask);
664 Exit;
665 end;
666
667 if (StockID >= idButtonBase) and (StockID <= idDialogBase) then
668 Pixbuf := gtk_icon_set_render_icon(IconSet, Style,
669 GTK_TEXT_DIR_NONE, GTK_STATE_NORMAL, GTK_ICON_SIZE_BUTTON, GetStyleWidget(lgsbutton), nil)
670 else
671 Pixbuf := gtk_icon_set_render_icon(IconSet, Style,
672 GTK_TEXT_DIR_NONE, GTK_STATE_NORMAL, GTK_ICON_SIZE_DIALOG, GetStyleWidget(lgswindow), nil);
673
674 GDIObj := Gtk2Widgetset.NewGDIObject(gdiBitmap);
675 with GDIObj^ do
676 begin
677 GDIBitmapType := gbPixbuf;
678 visual := gdk_visual_get_system();
679 gdk_visual_ref(visual);
680 colormap := gdk_colormap_get_system();
681 gdk_colormap_ref(colormap);
682 GDIPixbufObject := Pixbuf;
683 end;
684
685 Image := HBitmap({%H-}PtrUInt(GDIObj));
686 Mask := 0;
687 Result := True;
688 end;
689
690 procedure ButtonImagesChange({%H-}ASettings: PGtkSettings; {%H-}pspec: PGParamSpec; Services: TGtk2ThemeServices); cdecl;
691 begin
692 Application.IntfThemeOptionChange(Services, toShowButtonImages);
693 Services.IntfDoOnThemeChange;
694 end;
695
696 procedure MenuImagesChange({%H-}ASettings: PGtkSettings; {%H-}pspec: PGParamSpec; Services: TGtk2ThemeServices); cdecl;
697 begin
698 Application.IntfThemeOptionChange(Services, toShowMenuImages);
699 Services.IntfDoOnThemeChange;
700 end;
701
GetOptionnull702 function TGtk2ThemeServices.GetOption(AOption: TThemeOption): Integer;
703 var
704 ASettings: PGtkSettings;
705 BoolSetting: gboolean;
706 Widget: PGtkWidget;
707 Signal: guint;
708 begin
709 case AOption of
710 toShowButtonImages:
711 begin
712 Widget := GetStyleWidget(lgsButton);
713 ASettings := gtk_widget_get_settings(Widget);
714 BoolSetting := True; // default
715 g_object_get(ASettings, 'gtk-button-images', @BoolSetting, nil);
716 Result := Ord(BoolSetting = True);
717 if g_object_get_data(PGObject(Widget), 'lcl-images-change-callback') = nil then
718 begin
719 Signal := g_signal_connect(ASettings, 'notify::gtk-button-images', TGCallback(@ButtonImagesChange), Self);
720 g_object_set_data(PGObject(Widget), 'lcl-images-change-callback', {%H-}Pointer(PtrUInt(Signal)))
721 end;
722 end;
723 toShowMenuImages:
724 begin
725 Widget := GetStyleWidget(lgsMenuitem);
726 ASettings := gtk_widget_get_settings(Widget);
727 BoolSetting := False; // default
728 g_object_get(ASettings, 'gtk-menu-images', @BoolSetting, nil);
729 Result := Ord(BoolSetting = True);
730 if g_object_get_data(PGObject(Widget), 'lcl-images-change-callback') = nil then
731 begin
732 Signal := g_signal_connect(ASettings, 'notify::gtk-menu-images', TGCallback(@MenuImagesChange), Self);
733 g_object_set_data(PGObject(Widget), 'lcl-images-change-callback', {%H-}Pointer(PtrUInt(Signal)))
734 end;
735 end;
736 else
737 Result := inherited GetOption(AOption);
738 end;
739 end;
740
741 procedure TGtk2ThemeServices.GtkDrawElement(DC: HDC;
742 Details: TThemedElementDetails; const R: TRect; ClipRect: PRect);
743 var
744 DevCtx: TGtkDeviceContext absolute DC;
745 Area: TGdkRectangle;
746 ClipArea: TGdkRectangle;
747 StyleParams: TGtkStyleParams;
748 i: integer;
749 RDest: TRect;
750 ComboBoxHeight: gint;
751 ComboBoxWidth: gint;
752 begin
753 if IsRectEmpty(R) then
754 Exit;
755 for i := 0 to GetParamsCount(Details) - 1 do
756 begin
757 StyleParams := GetGtkStyleParams(DC, Details, i);
758 if StyleParams.Style <> nil then
759 begin
760 if DevCtx.HasTransf then
761 begin
762 if ClipRect <> nil then RDest := ClipRect^ else RDest := R;
763 RDest := DevCtx.TransfRectIndirect(R);
764 DevCtx.TransfNormalize(RDest.Left, RDest.Right);
765 DevCtx.TransfNormalize(RDest.Top, RDest.Bottom);
766 Area := GdkRectFromRect(RDest);
767 end else
768 Area := GdkRectFromRect(R);
769
770 ClipArea := DevCtx.ClipRect;
771
772 // move to origin
773 inc(Area.x, StyleParams.Origin.x);
774 inc(Area.y, StyleParams.Origin.y);
775
776 with StyleParams do
777 begin
778 if Painter = gptExpander then
779 begin
780 // Better to draw expander with the ExpanderSize, but sometimes it
781 // will not look very well. The best can we do is to use the same odd/even
782 // amount of pixels => expand/shrink area.width and area.height a bit
783
784 // Area.width := ExpanderSize;
785 if Odd(Area.width) <> Odd(ExpanderSize) then
786 if Area.width < ExpanderSize then
787 inc(Area.width)
788 else
789 dec(Area.width);
790 // Area.height := ExpanderSize;
791 if Odd(Area.height) <> Odd(ExpanderSize) then
792 if Area.height < ExpanderSize then
793 inc(Area.height)
794 else
795 dec(Area.height);
796 end;
797 if (MaxWidth <> 0) then
798 begin
799 if Area.width > MaxWidth then
800 begin
801 inc(Area.x, (Area.width - MaxWidth) div 2);
802 Area.width := MaxWidth;
803 end;
804 end;
805 case Painter of
806 gptDefault: inherited DrawElement(DC, Details, R, ClipRect);
807
808 gptComboBox:
809 begin
810 {this is hack to paint combobox under gtk2}
811 if Details.State = CBXS_PRESSED then
812 gtk_paint_focus(Style, Window, GTK_STATE_ACTIVE, @ClipArea, Widget, 'button', Area.X + 2, Area.y + 2, Area.Width - 4, Area.Height - 4);
813
814 if not (Byte(Details.Part) in [CP_DROPDOWNBUTTON, CP_DROPDOWNBUTTONRIGHT, CP_DROPDOWNBUTTONLEFT]) then
815 begin
816 gtk_paint_box(
817 Style, Window,
818 State, Shadow,
819 @ClipArea, Widget, PChar(Detail),
820 Area.x, Area.y,
821 Area.Width, Area.Height);
822
823 // now we draw box with arrows
824 RDest := RectFromGdkRect(Area);
825 if Area.width > 17 then
826 RDest.Left := RDest.Right - 16
827 else
828 RDest.Left := RDest.Right - (Area.Width div 2);
829 ComboBoxHeight := Area.Height;
830 ComboBoxWidth := Area.Width;
831 if RDest.Left < 0 then
832 RDest.Left := 0;
833
834 gtk_paint_vline(Style, Window, State, @ClipArea, Widget,'', Area.y + (ComboBoxHeight div 10), Area.Y + Area.Height - (ComboBoxHeight div 10), (RDest.Right - Min(23, (ComboBoxWidth div 2) + 1)) + 1);
835 end;
836
837 if Byte(Details.Part) in [CP_DROPDOWNBUTTON, CP_DROPDOWNBUTTONRIGHT, CP_DROPDOWNBUTTONLEFT] then
838 begin
839 RDest := RectFromGdkRect(Area);
840 ComboBoxHeight := (RDest.Right - Min(23, (ComboBoxWidth div 2) + 1)) + 2;
841 if RDest.Right - ComboBoxHeight < 8 then
842 ComboBoxHeight := Area.X + (Area.Width div 4);
843 gtk_paint_arrow(Style, Window, State, Shadow, @ClipArea, Widget,
844 PChar(Detail), GTK_ARROW_UP, True, RDest.Left + ((RDest.Right - RDest.Left) div 4), RDest.Top + ((RDest.Bottom - RDest.Top) div 2) - 5, Min(8, RDest.Right - RDest.Left), Min(8, RDest.Bottom - RDest.Top));
845 gtk_paint_arrow(Style, Window, State, Shadow, @ClipArea, Widget,
846 PChar(Detail), GTK_ARROW_DOWN, True, RDest.Left + ((RDest.Right - RDest.Left) div 4), RDest.Top + ((RDest.Bottom - RDest.Top) div 2) + 1, Min(8, RDest.Right - RDest.Left), Min(8, RDest.Bottom - RDest.Top));
847 end;
848 end;
849 gptBox:
850 gtk_paint_box(
851 Style, Window,
852 State, Shadow,
853 @ClipArea, Widget, PChar(Detail),
854 Area.x, Area.y,
855 Area.Width, Area.Height);
856 gptBoxGap:
857 gtk_paint_box_gap(
858 Style, Window,
859 State, Shadow,
860 @ClipArea, Widget, PChar(Detail),
861 Area.x, Area.y,
862 Area.Width, Area.Height,
863 GapSide, GapX, GapWidth);
864 gptHLine : gtk_paint_hline(
865 Style, Window,
866 State, @ClipArea,
867 Widget, PChar(Detail),
868 Area.x, Area.x + Area.Width, Area.y);
869 gptVLine : gtk_paint_vline(
870 Style, Window,
871 State, @ClipArea,
872 Widget, PChar(Detail),
873 Area.y, Area.y + Area.Height, Area.x);
874 gptShadow : gtk_paint_shadow(
875 Style, Window,
876 State, Shadow,
877 @ClipArea, Widget, PChar(Detail),
878 Area.x, Area.y,
879 Area.Width, Area.Height);
880 gptFlatBox: gtk_paint_flat_box(
881 Style, Window,
882 State, Shadow,
883 @ClipArea, Widget, PChar(Detail),
884 Area.x, Area.y,
885 Area.Width, Area.Height);
886 gptCheck : gtk_paint_check(
887 Style, Window,
888 State, Shadow,
889 @ClipArea, Widget, PChar(Detail),
890 Area.x, Area.y,
891 Area.Width, Area.Height);
892 gptOption : gtk_paint_option(
893 Style, Window,
894 State, Shadow,
895 @ClipArea, Widget, PChar(Detail),
896 Area.x, Area.y,
897 Area.Width, Area.Height);
898 gptTab : gtk_paint_tab(
899 Style, Window,
900 State, Shadow,
901 @ClipArea, Widget, PChar(Detail),
902 Area.x, Area.y,
903 Area.Width, Area.Height);
904 gptSlider : gtk_paint_slider(
905 Style, Window,
906 State, Shadow,
907 @ClipArea, Widget, PChar(Detail),
908 Area.x, Area.y,
909 Area.Width, Area.Height,
910 Orientation);
911 gptHandle : gtk_paint_handle(
912 Style, Window,
913 State, Shadow,
914 @ClipArea, Widget, PChar(Detail),
915 Area.x, Area.y,
916 Area.Width, Area.Height,
917 Orientation);
918
919 gptExpander: gtk_paint_expander(
920 Style, Window, State,
921 @ClipArea, Widget, PChar(Detail),
922 Area.x + Area.width shr 1, Area.y + Area.height shr 1,
923 Expander);
924 gptResizeGrip: gtk_paint_resize_grip(
925 Style, Window, State,
926 @ClipArea, Widget,
927 PChar(Detail), Edge,
928 Area.x, Area.y,
929 Area.Width, Area.Height);
930
931 gptFocus : gtk_paint_focus(
932 Style, Window, State,
933 @ClipArea, Widget, PChar(Detail),
934 Area.x, Area.y,
935 Area.Width, Area.Height);
936 gptArrow: gtk_paint_arrow(
937 Style, Window,
938 State, Shadow,
939 @ClipArea, Widget, PChar(Detail),
940 ArrowType, Fill,
941 Area.x, Area.y, Area.width, Area.height
942 );
943 gptPixmap: DrawPixmap(DC, @Area, Ord(Detail[1]));
944 end;
945 end;
946 end;
947 end;
948 end;
949
950 procedure TGtk2ThemeServices.DrawElement(DC: HDC;
951 Details: TThemedElementDetails; const R: TRect; ClipRect: PRect);
952 var
953 Widget: PGtkWidget;
954 begin
955 if (Details.Element = teTreeview) and (Details.Part = TVP_TREEITEM) and
956 (Details.State = TREIS_SELECTED) then
957 begin
958 // lie to cleanlooks theme
959 Widget := GetStyleWidget(lgsTreeView);
960 GTK_WIDGET_SET_FLAGS(Widget, GTK_HAS_FOCUS);
961 GtkDrawElement(DC, Details, R, ClipRect);
962 GTK_WIDGET_UNSET_FLAGS(Widget, GTK_HAS_FOCUS);
963 end
964 else
965 GtkDrawElement(DC, Details, R, ClipRect);
966 end;
967
968 procedure TGtk2ThemeServices.DrawText(ACanvas: TPersistent;
969 Details: TThemedElementDetails; const S: String; R: TRect; Flags,
970 Flags2: Cardinal);
971 begin
972 if ThemesEnabled then
973 DrawText(TCanvas(ACanvas).Handle, Details, S, R, Flags, Flags2)
974 else
975 inherited;
976 end;
977
978 procedure TGtk2ThemeServices.DrawText(DC: HDC; Details: TThemedElementDetails;
979 const S: String; R: TRect; Flags, Flags2: Cardinal);
980 var
981 StyleParams: TGtkStyleParams;
982 P: PChar;
983 tmpRect: TRect;
984 begin
985 StyleParams := GetGtkStyleParams(DC, Details, 0);
986 if StyleParams.Style <> nil then
987 with StyleParams do
988 begin
989 P := PChar(S);
990 tmpRect := R;
991 Gtk2Widgetset.DrawText(DC, P, Length(S), tmpRect, Flags);
992 // TODO: parse flags
993 //gtk_draw_string(Style, Window, State, R.Left + Origin.x, R.Top + Origin.y, P);
994 end;
995 end;
996
ContentRectnull997 function TGtk2ThemeServices.ContentRect(DC: HDC;
998 Details: TThemedElementDetails; BoundingRect: TRect): TRect;
999 var
1000 StyleParams: TGtkStyleParams;
1001 begin
1002 Result := BoundingRect;
1003 StyleParams := GetGtkStyleParams(DC, Details, 0);
1004 if StyleParams.Style <> nil then
1005 InflateRect(Result,
1006 -StyleParams.Style^.xthickness,
1007 -StyleParams.Style^.ythickness);
1008 end;
1009
HasTransparentPartsnull1010 function TGtk2ThemeServices.HasTransparentParts(Details: TThemedElementDetails): Boolean;
1011 begin
1012 Result := True; // ?
1013 end;
1014
1015 end.
1016
1017