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