{ ***************************************************************************** * * * This file is part of the Lazarus Component Library (LCL) * * * * See the file COPYING.LCL, included in this distribution, * * for details about the copyright. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * ***************************************************************************** } {$IFOPT C-} // Uncomment for local trace // {$C+} // {$DEFINE ASSERT_IS_ON} {$ENDIF} {$DEFINE ASSERT_IS_ON} function DeliverPaintMessage(const Target: Pointer; var TheMessage): GBoolean; var PaintMsg: TLMPaint; begin if TLMessage(TheMessage).Msg=LM_GtkPAINT then PaintMsg:= GtkPaintMessageToPaintMessage(TLMGtkPaint(TheMessage),true) else PaintMsg:=TLMPaint(TheMessage); Result := DeliverMessage(Target,PaintMsg) = 0; FinalizePaintMessage(@PaintMsg); end; {------------------------------------------------------------------------------- function DeliverPostMessage(const Target: Pointer; var TheMessage): GBoolean; 'TheMessage' is in TLMessage format. Don't confuse this with tagMsg. --------------------------------------------------------------------------------} function DeliverPostMessage(const Target: Pointer; var TheMessage): GBoolean; begin if TObject(Target) is TWinControl then begin // convert TLMessage into a tagMsg and push on the message queue Result := PostMessage(TWinControl(Target).Handle, TLMessage(TheMessage).Msg, TLMessage(TheMessage).WParam, TLMessage(TheMessage).LParam ); end else begin if TLMessage(TheMessage).Msg<>LM_GtkPAINT then Result := DeliverMessage(Target, TheMessage) = 0 else begin Result := DeliverPaintMessage(Target,TheMessage); end; end; end; function DeliverGtkPaintMessage(Target: Pointer; Widget: PGtkWidget; Area: PGDKRectangle; RepaintAll: boolean): GBoolean; var MSG: TLMGtkPaint; {$IFDEF DirectPaintMsg} PaintMsg: TLMPaint; {$ENDIF} begin if (not RepaintAll) and ((Area^.Width<1) or (Area^.Width<1)) then exit; MSG.Msg := LM_GtkPAINT; MSG.Data:=TLMGtkPaintData.Create; MSG.Data.Widget := Widget; MSG.Data.State := GtkPaint_LCLWidget; Msg.Data.Rect:=Bounds(Area^.x,Area^.y,Area^.Width,Area^.Height); Msg.Data.RepaintAll:=RepaintAll; {$IFDEF DirectPaintMsg} PaintMsg:= GtkPaintMessageToPaintMessage(Msg,true); Result := DeliverMessage(Target,PaintMsg) = 0; FinalizePaintMessage(@PaintMsg); {$ELSE} Result:=DeliverPostMessage(Target,Msg); {$ENDIF} end; procedure EventTrace(const TheMessage : string; data : pointer); begin if Data = nil then Assert(False, Format('Trace:Event [%s] fired',[Themessage])) else Assert(False, Format('Trace:Event [%s] fired for %s', [TheMessage, TObject(data).Classname])); end; {*************************************************************} { callback routines } {*************************************************************} {------------------------------------------------------------------------------- function gtkNoteBookCloseBtnClicked Params: Widget: PGtkWidget; Data: Pointer Result: GBoolean gtkNoteBookCloseBtnClicked is called by the gtk, whenever a close button in the tab of a notebook page is clicked. -------------------------------------------------------------------------------} function gtkNoteBookCloseBtnClicked(Widget: PGtkWidget; Data: Pointer): GBoolean; cdecl; var APage: TCustomPage; begin Result:=true; // handled = true if Widget=nil then ; if ComponentIsDestroyingHandle(TWinControl(Data)) then exit; APage:=TCustomPage(Data); TCustomNoteBook(APage.Parent).DoCloseTabClicked(APage); end; {------------------------------------------------------------------------------- function GTKRealizeCB Params: Widget: PGtkWidget; Data: Pointer Result: GBoolean GTKRealizeCB is called by the gtk, whenever a widget is realized (ie mapped), but before the widget itself gets the realize signal. That means that the gdk window on the xserver has been created. -------------------------------------------------------------------------------} function gtkRealizeCB(Widget: PGtkWidget; Data: Pointer): GBoolean; cdecl; var decor,Func : Longint; TheWindow: PGdkWindow; TheForm: TCustomForm; begin Result := CallBackDefaultReturn; EventTrace('realize', nil); if (Data<>nil) then begin if TObject(Data) is TCustomForm then begin TheForm:=TCustomForm(Data); if TheForm.Parent=nil then begin TheWindow:=GetControlWindow(Widget); //apart from none and sizeable, this will //only work if WM supports motif flags //properly, which very few actually do. Decor := GetWindowDecorations(TheForm); Func := GetWindowFunction(TheForm); gdk_window_set_decorations(TheWindow, decor); gdk_window_set_functions(TheWindow, func); end; end; if (TObject(Data) is TWinControl) then UpdateWidgetStyleOfControl(TWinControl(Data)); if not (csDesigning in TComponent(Data).ComponentState) then RealizeAccelerator(TComponent(Data),Widget); end; end; {------------------------------------------------------------------------------- function GTKRealizeAfterCB Params: Widget: PGtkWidget; Data: Pointer Result: GBoolean GTKRealizeAfterCB is called by the gtk, whenever a widget is realized (ie mapped), and after the widget itself got the realize signal. That means that the gdk window on the xserver has been created and the widget initialized the gdkwindow. This function is used for the second part of the initialization of a widget. -------------------------------------------------------------------------------} function gtkRealizeAfterCB(Widget: PGtkWidget; Data: Pointer): GBoolean; cdecl; var WinWidgetInfo: PWinWidgetInfo; HiddenLCLObject, LCLObject: TObject; NewEventMask: TGdkEventMask; TheWinControl: TWinControl; ClientWidget: PGtkWidget; MainWidget: PGtkWidget; begin Result := CallBackDefaultReturn; if Data=nil then ; EventTrace('realizeafter', nil); HiddenLCLObject:=GetHiddenLCLObject(Widget); if HiddenLCLObject=nil then begin // this is a normal lcl wigdet MainWidget:=GetMainWidget(Widget); if MainWidget=nil then MainWidget:=Widget; WinWidgetInfo:=GetWidgetInfo(MainWidget,true); LCLObject:=GetLCLObject(MainWidget); if (LCLObject<>nil) and (WinWidgetInfo<>nil) then begin ClientWidget:=GetFixedWidget(Widget); if (LCLObject is TWinControl) then TheWinControl:=TWinControl(LCLObject) else TheWinControl:=nil; // set extra signal masks after the widget window is created // define extra events we're interrested in //write('GTKRealizeAfterCB '); //if TheWinControl<>nil then DbgOut(' ',TheWinControl.Name,':',TheWinControl.ClassName,' ',HexStr(Cardinal(TheWinControl.Handle),8)); //DebugLn(' Widget=',HexStr(Cardinal(Widget),8),' Fixed=',HexStr(Cardinal(GetFixedWidget(Widget)),8),' Main=',HexStr(Cardinal(GetMainWidget(Widget)),8)); if (TheWinControl<>nil) then begin {$IFDEF DebugGDK}BeginGDKErrorTrap;{$ENDIF} NewEventMask:=gdk_window_get_events(GetControlWindow(Widget)) or WinWidgetInfo^.EventMask; gdk_window_set_events(GetControlWindow(Widget),NewEventMask); if (ClientWidget<>nil) and (GetControlWindow(ClientWidget)<>nil) and (ClientWidget^.Window<>Widget^.Window) then begin NewEventMask:=gdk_window_get_events(GetControlWindow(ClientWidget)) or WinWidgetInfo^.EventMask; gdk_window_set_events(GetControlWindow(ClientWidget),NewEventMask); end; //DebugLn('BBB1 ',HexStr(Cardinal(NewEventMask),8),' ',HexStr(Cardinal(gdk_window_get_events(Widget^.Window)),8)); {$IFDEF DebugGDK}EndGDKErrorTrap;{$ENDIF} end; if TheWinControl<>nil then begin TheWinControl.InvalidatePreferredSize; SetCursor(TheWinControl, crDefault); ConnectInternalWidgetsSignals(MainWidget,TheWinControl); UpdateWidgetStyleOfControl(TheWinControl); if TheWinControl is TCustomPage then UpdateNotebookPageTab(nil,TheWinControl); end; end; end else begin // this is a hidden child widget of a lcl widget if HiddenLCLObject is TWinControl then ConnectInternalWidgetsSignals(Widget,TWinControl(HiddenLCLObject)); end; end; function gtkshowCB( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMShowWindow; begin Result := True; EventTrace('show', data); if Widget=nil then ; FillChar(Mess,SizeOf(Mess),0); Mess.Msg := LM_SHOWWINDOW; Mess.Show := True; Result := DeliverMessage(Data, Mess) = 0; end; function gtkHideCB( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMShowWindow; begin Result := True; EventTrace('hide', data); if Widget=nil then ; FillChar(Mess,SizeOf(Mess),0); Mess.Msg := LM_SHOWWINDOW; Mess.Show := False; Result := DeliverMessage(Data, Mess) = 0; end; function gtkactivateCB(widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess: TLMActivate; begin Result:= True; EventTrace('activate', data); if LockOnChange(PgtkObject(Widget),0) > 0 then Exit; FillChar(Mess,SizeOf(Mess),#0); Mess.Msg := LM_ACTIVATE; Mess.Active:=true; Mess.Minimized:=false; Mess.ActiveWindow:=0; if GtkWidgetIsA(Widget, gtk_window_get_type) then Mess.ActiveWindow:=HWnd(PGTKWindow(Widget)^.focus_widget); Mess.Result := 0; DeliverMessage(Data, Mess); //DebugLn('gtkactivateCB ',TWinControl(Data).Name,':',TWinControl(Data).ClassName); Result := CallBackDefaultReturn; end; function GTKCheckMenuToggeledCB(AMenuItem: PGTKCheckMenuItem; AData: gPointer): GBoolean; cdecl; // AData --> LCLMenuItem var LCLMenuItem: TMenuItem; begin Result := CallBackDefaultReturn; EventTrace('toggled', AData); LCLMenuItem := TMenuItem(AData); // some sanity checks if LCLMenuItem = nil then Exit; if not LCLMenuItem.IsCheckItem then Exit; // ??? // the gtk always toggles the check flag // -> restore 'checked' flag if needed if gtk_check_menu_item_get_active(AMenuItem) = LCLMenuItem.Checked then Exit; if LCLMenuItem.AutoCheck then Exit; // restore it LockOnChange(PgtkObject(AMenuItem), +1); gtk_check_menu_item_set_active(AMenuItem, LCLMenuItem.Checked); LockOnChange(PgtkObject(AMenuItem), -1); end; function gtkchangedCB( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin if ComponentIsDestroyingHandle(TWinControl(Data)) or (LockOnChange(PgtkObject(Widget),0)>0) then exit; EventTrace('changed', data); Mess.Msg := LM_CHANGED; DeliverMessage(Data, Mess); Result := CallBackDefaultReturn; end; function gtkchanged_editbox( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; Status : GBoolean; begin Result := CallBackDefaultReturn; if LockOnChange(PgtkObject(Widget),0)>0 then exit; EventTrace('changed_editbox', data); Mess.Msg := CM_TEXTCHANGED; Status := DeliverMessage(Data, Mess) = 0; {$ifdef GTK2} Result := CallBackDefaultReturn; {$Else} Result := Status; {$endif} end; function gtkdaychanged(Widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var MSG: TLMessage; Status : GBoolean; begin Result := CallBackDefaultReturn; if LockOnChange(PgtkObject(Widget),0)>0 then exit; EventTrace('day changed', data); MSG.Msg := LM_DAYCHANGED; Status := DeliverPostMessage(Data, MSG); {$ifdef GTK2} Result := CallBackDefaultReturn; {$Else} Result := Status; {$endif} end; function gtktoggledCB( widget: PGtkWidget; data: gPointer): GBoolean; cdecl; var Mess : TLMessage; begin Result:= True; EventTrace('toggled', data); if LockOnChange(PgtkObject(Widget),0) > 0 then Exit; if GtkWidgetIsA(Widget,GTK_TOGGLE_BUTTON_TYPE) then begin gtk_object_set_data(PgtkObject(Widget), 'Grayed', nil); end; Mess.Msg := LM_CHANGED; Mess.Result := 0; DeliverMessage(Data, Mess); //DebugLn('gtktoggledCB ',TWinControl(Data).Name,':',TWinControl(Data).ClassName); Result := CallBackDefaultReturn; end; {$Ifdef GTK1} function gtkDrawAfter(Widget: PGtkWidget; area: PGDKRectangle; data: gPointer) : GBoolean; cdecl; var DesignOnlySignal: boolean; begin Result := CallBackDefaultReturn; EventTrace('DrawAfter', data); if not (csDesigning in TComponent(Data).ComponentState) then begin DesignOnlySignal:=GetDesignOnlySignalFlag(Widget,dstDrawAfter); if DesignOnlySignal then exit; end else begin {$IFDEF VerboseDesignerDraw} DebugLn('gtkDrawAfter', ' Widget=',HexStr(Cardinal(Widget),8),'=',GetWidgetClassName(Widget), ' ',TComponent(Data).Name, ' ',area^.x,',',area^.y,',',area^.width,',',area^.height, ''); {$ENDIF} end; DeliverGtkPaintMessage(Data,Widget,Area,false); end; {$ENDIF} function gtkExposeEventAfter(Widget: PGtkWidget; Event : PGDKEventExpose; Data: gPointer): GBoolean; cdecl; var DesignOnlySignal: boolean; begin Result := CallBackDefaultReturn; EventTrace('ExposeAfter', data); if (Event^.Count > 0) then exit; if not (csDesigning in TComponent(Data).ComponentState) then begin DesignOnlySignal:=GetDesignOnlySignalFlag(Widget,dstExposeAfter); if DesignOnlySignal then exit; end else begin {$IFDEF VerboseDesignerDraw} DebugLn('gtkExposeAfter', ' Widget=',HexStr(Cardinal(Widget),8),'=',GetWidgetClassName(Widget), ' ',TComponent(Data).Name, ' ',Event^.area.x,',',Event^.area.y,',',Event^.area.width,',',Event^.area.height, ''); {$ENDIF} end; // the expose area is ok, but some gtk widgets repaints everything on expose // -> maximize the area DeliverGtkPaintMessage(Data,Widget,@Event^.Area,true); end; function gtkfrmactivateAfter(widget: PGtkWidget; Event : PgdkEventFocus; data: gPointer) : GBoolean; cdecl; var Mess : TLMActivate; {$IFDEF VerboseFocus} LCLObject: TObject; CurFocusWidget: PGtkWidget; {$ENDIF} begin EventTrace('activate after', data); if (Widget=nil) or (Event=nil) then ; FillChar(Mess,SizeOf(Mess),#0); {$IFDEF VerboseFocus} write('gtkfrmactivateAfter Widget=',HexStr(Cardinal(Widget),8),' Event^.theIn=',Event^.theIn); LCLObject:=TObject(data); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' LCLObject=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' LCLObject=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); DebugLn(''); DbgOut(' '); CurFocusWidget:=PGtkWidget(GetFocus); if CurFocusWidget<>nil then begin write(' GetFocus=',HexStr(Cardinal(CurFocusWidget),8)); LCLObject:=GetNearestLCLObject(CurFocusWidget); if LCLObject<>nil then begin if LCLObject is TComponent then begin DbgOut(' ParentLCLFocus=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin DbgOut(' ParentLCLFocus=',LCLObject.ClassName) end; end else DbgOut(' LCLObject=nil'); end else begin DbgOut(' GetFocus=nil'); end; DebugLn(''); {$ENDIF} UpdateMouseCaptureControl; Mess.Msg := LM_ACTIVATE; Mess.Active:=true; Mess.Minimized:=false; Mess.ActiveWindow:=0; if GtkWidgetIsA(Widget, gtk_window_get_type) then Mess.ActiveWindow:=HWnd(PGTKWindow(Widget)^.focus_widget); Mess.Result := 0; DeliverPostMessage(Data, Mess); Result := CallBackDefaultReturn; end; function gtkfrmdeactivateAfter( widget: PGtkWidget; Event : PgdkEventFocus; data: gPointer) : GBoolean; cdecl; var Mess : TLMActivate; Status : GBoolean; {$IFDEF VerboseFocus} LCLObject: TControl; {$ENDIF} begin EventTrace('deactivate after', data); if (Widget=nil) or (Event=nil) then ; {$IFDEF VerboseFocus} write('gtkfrmdeactivate Widget=',HexStr(Cardinal(Widget),8),' ',Event^.theIn, ' GetFocus=',HexStr(Cardinal(Widget),8)); LCLObject:=TControl(GetLCLObject(Widget)); if LCLObject<>nil then DebugLn(' LCLObject=',LCLObject.Name,':',LCLObject.ClassName) else DebugLn(' LCLObject=nil'); {$ENDIF} UpdateMouseCaptureControl; Mess.Msg := LM_DEACTIVATE; Status := DeliverPostMessage(Data, Mess); {$ifdef GTK2} Result := CallBackDefaultReturn; {$Else} Result := Status; {$endif} end; function GTKMap(Widget: PGTKWidget; Data: gPointer): GBoolean; cdecl; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; EventTrace('map', data); end; function GTKKeyUpDown(Widget: PGtkWidget; Event: PGdkEventKey; Data: gPointer) : GBoolean; cdecl; begin Result:=HandleGtkKeyUpDown(Widget,Event,Data,true); end; function GTKKeyUpDownAfter(Widget: PGtkWidget; Event: pgdkeventkey; Data: gPointer): GBoolean; cdecl; begin Result:=HandleGtkKeyUpDown(Widget,Event,Data,false); end; function GTKFocusCB( widget: PGtkWidget; event:PGdkEventFocus; data: gPointer) : GBoolean; cdecl; {$IFDEF VerboseFocus} var LCLObject: TObject; CurFocusWidget: PGtkWidget; {$ENDIF} begin EventTrace('focus', data); if (Widget=nil) or (Event=nil) then ; {$IFDEF VerboseFocus} write('GTKFocusCB Widget=',HexStr(Cardinal(Widget),8),' Event^.theIn=',Event^.theIn); LCLObject:=TObject(data); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' LCLObject=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' LCLObject=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); DebugLn(''); DbgOut(' '); CurFocusWidget:=PGtkWidget(GetFocus); if CurFocusWidget<>nil then begin write(' GetFocus=',HexStr(Cardinal(CurFocusWidget),8)); LCLObject:=GetNearestLCLObject(CurFocusWidget); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' ParentLCLFocus=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' ParentLCLFocus=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); end else begin write(' GetFocus=nil'); end; DebugLn(''); {$ENDIF} Result:=true; end; function GTKFocusCBAfter(widget: PGtkWidget; event:PGdkEventFocus; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; {$IFDEF VerboseFocus} LCLObject: TObject; CurFocusWidget: PGtkWidget; {$ENDIF} begin EventTrace('focus', data); if (Widget=nil) or (Event=nil) then ; {$IFDEF VerboseFocus} write('GTKFocusCBAfter Widget=',HexStr(Cardinal(Widget),8),' Event^.theIn=',Event^.theIn); LCLObject:=TObject(data); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' LCLObject=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' LCLObject=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); DebugLn(''); DbgOut(' '); CurFocusWidget:=PGtkWidget(GetFocus); if CurFocusWidget<>nil then begin write(' GetFocus=',HexStr(Cardinal(CurFocusWidget),8)); LCLObject:=GetNearestLCLObject(CurFocusWidget); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' ParentLCLFocus=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' ParentLCLFocus=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); end else begin write(' GetFocus=nil'); end; DebugLn(''); {$ENDIF} UpdateMouseCaptureControl; //TODO: fill in old focus FillChar(Mess,SizeOf(Mess),0); Mess.msg := LM_SETFOCUS; DeliverMessage(Data, Mess); Result:=true; end; function GTKKillFocusCB(widget: PGtkWidget; event:PGdkEventFocus; data: gPointer) : GBoolean; cdecl; {$IFDEF VerboseFocus} var LCLObject: TObject; CurFocusWidget: PGtkWidget; {$ENDIF} begin EventTrace('killfocus', data); if (Widget=nil) or (Event=nil) then ; {$IFDEF VerboseFocus} write('GTKillFocusCB Widget=',HexStr(Cardinal(Widget),8),' Event^.theIn=',Event^.theIn); LCLObject:=TObject(data); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' LCLObject=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' LCLObject=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); DebugLn(''); DbgOut(' '); CurFocusWidget:=PGtkWidget(GetFocus); if CurFocusWidget<>nil then begin write(' GetFocus=',HexStr(Cardinal(CurFocusWidget),8)); LCLObject:=GetNearestLCLObject(CurFocusWidget); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' ParentLCLFocus=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' ParentLCLFocus=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); end else begin write(' GetFocus=nil'); end; DebugLn(''); {$ENDIF} Result:=true; end; function GTKKillFocusCBAfter(widget: PGtkWidget; event:PGdkEventFocus; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; {$IFDEF VerboseFocus} LCLObject: TObject; CurFocusWidget: PGtkWidget; {$ENDIF} begin if (Widget=nil) or (Event=nil) then ; EventTrace('killfocus', data); {$IFDEF VerboseFocus} write('GTKillFocusCBAfter Widget=',HexStr(Cardinal(Widget),8),' Event^.theIn=',Event^.theIn); LCLObject:=TObject(data); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' LCLObject=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' LCLObject=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); DebugLn(''); DbgOut(' '); CurFocusWidget:=PGtkWidget(GetFocus); if CurFocusWidget<>nil then begin write(' GetFocus=',HexStr(Cardinal(CurFocusWidget),8)); LCLObject:=GetNearestLCLObject(CurFocusWidget); if LCLObject<>nil then begin if LCLObject is TComponent then begin write(' ParentLCLFocus=',TComponent(LCLObject).Name,':',LCLObject.ClassName) end else begin write(' ParentLCLFocus=',LCLObject.ClassName) end; end else write(' LCLObject=nil'); end else begin write(' GetFocus=nil'); end; DebugLn(''); {$ENDIF} UpdateMouseCaptureControl; FillChar(Mess,SizeOf(Mess),0); Mess.msg := LM_KILLFOCUS; //TODO: fill in new focus Assert(False, Format('Trace:TODO: [gtkkillfocusCB] %s finish', [TObject(Data).ClassName])); DeliverMessage(Data, Mess); Result:=true; end; function gtkdestroyCB(widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess: TLMessage; Status : GBoolean; Info: PWinWidgetInfo; begin Result := CallBackDefaultReturn; EventTrace('destroy', data); FillChar(Mess,SizeOf(Mess),0); Mess.msg := LM_DESTROY; Status := DeliverMessage(Data, Mess) = 0; {$ifdef GTK2} Result := CallBackDefaultReturn; {$Else} Result := Status; {$endif} // NOTE: if the destroy message is posted // we should post an info destroy message as well Info := GetWidgetInfo(widget, False); if Info <> nil then Dispose(Info); end; function gtkdeleteCB( widget : PGtkWidget; event : PGdkEvent; data : gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin FillChar(Mess,SizeOf(Mess),0); if (Widget=nil) or (Event=nil) then ; Mess.Msg:= LM_CLOSEQUERY; { Message results : True - do nothing, False - destroy or hide window } Result:= DeliverMessage(Data, Mess) = 0; end; function gtkresizeCB( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; //var // Mess : TLMessage; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; EventTrace('resize', data); // Mess.msg := LM_RESIZE; // TControl(data).WindowProc(TLMessage(Mess)); Assert(False, 'Trace:TODO: [gtkresizeCB] fix (or remove) to new LM_SIZE'); //TObject(data).Dispatch(Mess); end; function gtkMonthChanged(Widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess: TLMessage; Status : GBoolean; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; EventTrace('month changed', data); FillChar(Mess,SizeOf(Mess),0); Mess.Msg := LM_MONTHCHANGED; Status := DeliverPostMessage(Data, Mess); {$ifdef GTK2} Result := CallBackDefaultReturn; {$Else} Result := Status; {$endif} end; {------------------------------------------------------------------------------- procedure DeliverMouseMoveMessage(Widget:PGTKWidget; Event: PGDKEventMotion; AWinControl: TWinControl); Translate a gdk mouse motion event into a LCL mouse move message and send it. Mouse coordinate mapping: Why mapping: An lcl control can consists of several gtk widgets, and any message to them is send to the lcl control. The gtk sends the coordinates relative to the emitting gdkwindow (not relative to the gtkwidget). And the area of a lcl control can belong to several gdkwindows. Therefore the mouse coordinates must be mapped. What the lcl expects: For Delphi compatibility the mouse coordinates must be relative to the client area of the control. That means for example if the mouse is over the top-left pixel of the client widget (mostly a gtkfixed widget), then 0,0 is send. If the mouse is on the top-left pixel of the container widget then the coordinates can be negative, if there is frame around the client area. -------------------------------------------------------------------------------} procedure DeliverMouseMoveMessage(Widget:PGTKWidget; Event: PGDKEventMotion; AWinControl: TWinControl); var Msg: TLMMouseMove; ShiftState: TShiftState; MappedXY: TPoint; begin if (Widget=nil) then ; MappedXY:=TranslateGdkPointToClientArea(Event^.Window, Point(TruncToInt(Event^.X),TruncToInt(Event^.Y)), PGtkWidget(AWinControl.Handle)); ShiftState := GTKEventState2ShiftState(Event^.State); with Msg do begin Msg := LM_MouseMove; XPos := MappedXY.X; YPos := MappedXY.Y; Keys := 0; if ssShift in ShiftState then Keys := Keys or MK_SHIFT; if ssCtrl in ShiftState then Keys := Keys or MK_CONTROL; if ssLeft in ShiftState then Keys := Keys or MK_LBUTTON; if ssRight in ShiftState then Keys := Keys or MK_RBUTTON; if ssMiddle in ShiftState then Keys := Keys or MK_MBUTTON; Result := 0; end; // send the message directly to the LCL // (Posting the message via queue // has the risk of getting out of sync with the gtk) NotifyApplicationUserInput(Msg.Msg); DeliverMessage(AWinControl, Msg); end; {------------------------------------------------------------------------------- function ControlGetsMouseMoveBefore(AControl: TControl): boolean; Returns true, if mouse move event should be sent before the widget istelf reacts. -------------------------------------------------------------------------------} function ControlGetsMouseMoveBefore(AControl: TControl): boolean; begin if (AControl=nil) then ; // currently there are no controls, that need after events. Result:=true; end; {------------------------------------------------------------------------------- GTKMotionNotify Params: widget: PGTKWidget; event: PGDKEventMotion; data: gPointer Returns: GBoolean Called whenever the mouse is moved over a widget. The gtk event is translated into a lcl MouseMove message. -------------------------------------------------------------------------------} function gtkMotionNotify(Widget:PGTKWidget; Event: PGDKEventMotion; Data: gPointer): GBoolean; cdecl; var DesignOnlySignal: boolean; begin Result := CallBackDefaultReturn; {$IFDEF VerboseMouseBugfix} DesignOnlySignal:=GetDesignOnlySignalFlag(Widget,dstMouseMotion); DebugLn('[GTKMotionNotify] ', TControl(Data).Name,':',TControl(Data).ClassName, ' Widget=',HexStr(Cardinal(Widget),8), ' DSO=',DesignOnlySignal, ' Event^.X=',TruncToInt(Event^.X),' Event^.Y=',TruncToInt(Event^.Y) ); {$ENDIF} UpdateMouseCaptureControl; if not (csDesigning in TComponent(Data).ComponentState) then begin DesignOnlySignal:=GetDesignOnlySignalFlag(Widget,dstMouseMotion); if DesignOnlySignal then exit; if not ControlGetsMouseMoveBefore(TControl(Data)) then exit; end else begin // stop the signal, so that the widget does not auto react g_signal_stop_emission_by_name(PGTKObject(Widget),'motion-notify-event'); end; DeliverMouseMoveMessage(Widget,Event,TWinControl(Data)); end; {------------------------------------------------------------------------------- GTKMotionNotifyAfter Params: widget: PGTKWidget; event: PGDKEventMotion; data: gPointer Returns: GBoolean Called whenever the mouse is moved over a widget as last handler. -------------------------------------------------------------------------------} function GTKMotionNotifyAfter(widget:PGTKWidget; event: PGDKEventMotion; data: gPointer): GBoolean; cdecl; begin Result := CallBackDefaultReturn; {$IFDEF VerboseMouseBugfix} DebugLn('[GTKMotionNotifyAfter] ', TControl(Data).Name,':',TControl(Data).ClassName); {$ENDIF} // stop the signal, so that it is not sent to the parent widgets g_signal_stop_emission_by_name(PGTKObject(Widget),'motion-notify-event'); UpdateMouseCaptureControl; if (csDesigning in TComponent(Data).ComponentState) then exit; if ControlGetsMouseMoveBefore(TControl(Data)) then exit; DeliverMouseMoveMessage(Widget,Event,TWinControl(Data)); end; {------------------------------------------------------------------------------- function ControlGetsMouseDownBefore(AControl: TControl): boolean; Returns true, if mouse down event should be sent before the widget istelf reacts. -------------------------------------------------------------------------------} function ControlGetsMouseDownBefore(AControl: TControl): boolean; begin case AControl.fCompStyle of csCheckBox, csToggleBox: Result:=false; else Result:=true; end; end; {------------------------------------------------------------------------------- procedure DeliverMouseDownMessage(widget: PGtkWidget; event : pgdkEventButton; AWinControl: TWinControl); Translate a gdk mouse press event into a LCL mouse down message and send it. -------------------------------------------------------------------------------} procedure DeliverMouseDownMessage(widget: PGtkWidget; event : pgdkEventButton; AWinControl: TWinControl); const WHEEL_DELTA : array[Boolean] of Integer = (-1, 1); var MessI : TLMMouse; MessE : TLMMouseEvent; ShiftState: TShiftState; MappedXY: TPoint; EventXY: TPoint; { $DEFINE VerboseMouseBugfix} function CheckMouseButtonDown(var LastMouse: TLastMouseClick; BtnKey, MsgNormal, MsgDouble, MsgTriple, MsgQuad: longint): boolean; function LastClickInSameGdkWindow: boolean; begin Result:=(LastMouse.Window<>nil) and (LastMouse.Window=Event^.Window); end; function LastClickAtSamePosition: boolean; begin Result:= (Abs(EventXY.X-LastMouse.WindowPoint.X)<=DblClickThreshold) and (Abs(EventXY.Y-LastMouse.WindowPoint.Y)<=DblClickThreshold); end; function LastClickInTime: boolean; begin Result:=((now - LastMouse.TheTime) <= ((1/86400)*(DblClickTime/1000))); end; function TestIfMultiClick: boolean; begin Result:=LastClickInSameGdkWindow and LastClickAtSamePosition and LastClickInTime; end; var IsMultiClick: boolean; begin Result:=false; if (LastMouse.Down) and (not (gdk_event_get_type(Event) in [gdk_2button_press,gdk_3button_press])) then begin {$IFDEF VerboseMouseBugfix} DebugLn(' NO CLICK: LastMouse.Down=',LastMouse.Down, ' Event^.theType=',gdk_event_get_type(Event)); {$ENDIF} Exit; end; MessI.Keys := MessI.Keys or BtnKey; IsMultiClick:=TestIfMultiClick; case gdk_event_get_type(Event) of gdk_2button_press: // the gtk itself has detected a double click if (LastMouse.ClickCount>=2) and IsMultiClick then begin // the double click was already detected and sent to the LCL // -> skip this message exit; end else begin LastMouse.ClickCount:=2; end; gdk_3button_press: // the gtk itself has detected a triple click if (LastMouse.ClickCount>=3) and IsMultiClick then begin // the triple click was already detected and sent to the LCL // -> skip this message exit; end else begin LastMouse.ClickCount:=3; end; else begin inc(LastMouse.ClickCount); if (LastMouse.ClickCount<=4) and IsMultiClick then begin // multi click {$IFDEF VerboseMouseBugfix} DebugLn(' MULTI CLICK: ',now,'-',LastMouse.TheTime,'<= ', ((1/86400)*(DblClickTime/1000))); {$ENDIF} end else begin // normal click LastMouse.ClickCount:=1; end; end; end; {$IFDEF VerboseMouseBugfix} DebugLn(' ClickCount=',LastMouse.ClickCount); {$ENDIF} LastMouse.TheTime := Now; LastMouse.Window := Event^.Window; LastMouse.WindowPoint := EventXY; LastMouse.Down := True; LastMouse.Component:=AWinControl; //DebugLn('DeliverMouseDownMessage ',AWinControl.Name,':',AWinControl.ClassName,' Mapped=',dbgs(MappedXY.X),',',dbgs(MappedXY.Y),' Event=',dbgs(EventXY.X),',',dbgs(EventXY.Y),' ',dbgs(LastMouse.ClickCount)); case LastMouse.ClickCount of 1: MessI.Msg := MsgNormal; 2: MessI.Msg := MsgDouble; 3: MessI.Msg := MsgTriple; 4: MessI.Msg := MsgQuad; else MessI.Msg := LM_NULL; end; Result:=true; end; begin if (Widget=nil) then ; EventXY:=Point(TruncToInt(Event^.X),TruncToInt(Event^.Y)); ShiftState := GTKEventState2ShiftState(Event^.State); MappedXY:=TranslateGdkPointToClientArea(Event^.Window,EventXY, PGtkWidget(AWinControl.Handle)); //DebugLn('DeliverMouseDownMessage ',AWinControl.Name,':',AWinControl.ClassName,' Mapped=',dbgs(MappedXY.X),',',dbgs(MappedXY.Y),' Event=',dbgs(EventXY.X),',',dbgs(EventXY.Y)); if event^.Button in [4,5] then begin // this is a mouse wheel event MessE.Msg := LM_MOUSEWHEEL; MessE.WheelDelta := WHEEL_DELTA[event^.Button = 4]; MessE.X := MappedXY.X; MessE.Y := MappedXY.Y; MessE.State := ShiftState; MessE.UserData := AWinControl; MessE.Button := 0; // send the message directly to the LCL NotifyApplicationUserInput(MessE.Msg); DeliverMessage(AWinControl, MessE); end else begin // a normal mouse button is pressed MessI.Keys := 0; case event^.Button of 1: if not CheckMouseButtonDown(LastLeft, MK_LBUTTON, LM_LBUTTONDOWN, LM_LBUTTONDBLCLK, LM_LBUTTONTRIPLECLK, LM_LBUTTONQUADCLK) then exit; 2: if not CheckMouseButtonDown(LastMiddle, MK_MBUTTON, LM_MBUTTONDOWN, LM_MBUTTONDBLCLK, LM_MBUTTONTRIPLECLK, LM_MBUTTONQUADCLK) then exit; 3: if not CheckMouseButtonDown(LastRight, MK_RBUTTON, LM_RBUTTONDOWN, LM_RBUTTONDBLCLK, LM_RBUTTONTRIPLECLK, LM_RBUTTONQUADCLK) then exit; else begin MessI.Msg := LM_NULL; exit; end; end; // case MessI.XPos := MappedXY.X; MessI.YPos := MappedXY.Y; if ssShift in ShiftState then MessI.Keys := MessI.Keys or MK_SHIFT; if ssCtrl in ShiftState then MessI.Keys := MessI.Keys or MK_CONTROL; if ssLeft in ShiftState then MessI.Keys := MessI.Keys or MK_LBUTTON; if ssRight in ShiftState then MessI.Keys := MessI.Keys or MK_RBUTTON; if ssMiddle in ShiftState then MessI.Keys := MessI.Keys or MK_MBUTTON; MessI.Result:=0; // send the message directly to the LCL NotifyApplicationUserInput(MessI.Msg); DeliverMessage(AWinControl, MessI); end; end; {------------------------------------------------------------------------------- gtkMouseBtnPress Params: widget: PGTKWidget; event: PGDKEventMotion; data: gPointer Returns: GBoolean Called whenever the mouse is over a widget and a mouse button is pressed. -------------------------------------------------------------------------------} function gtkMouseBtnPress(widget: PGtkWidget; event: pgdkEventButton; data: gPointer) : GBoolean; cdecl; var DesignOnlySignal: boolean; CaptureWidget: PGtkWidget; begin Result := true; {$IFDEF VerboseMouseBugfix} DebugLn(''); DesignOnlySignal:=GetDesignOnlySignalFlag(Widget,dstMousePress); WriteLn('[gtkMouseBtnPress] ', TComponent(Data).Name,':',TObject(Data).ClassName, ' Widget=',HexStr(Cardinal(Widget),8), ' ControlWidget=',HexStr(Cardinal(TWinControl(Data).Handle),8), ' DSO=',DesignOnlySignal, ' ',TruncToInt(Event^.X),',',TruncToInt(Event^.Y), ' Type=',Event^.{$IFDEF GTK2}_type{$ELSE}theType{$ENDIF}); {$ENDIF} //DebugLn('DDD1 MousePress Widget=',HexStr(Cardinal(Widget),8), //' ClientWidget=',HexStr(Cardinal(GetFixedWidget(Widget)),8), //' EventMask=',HexStr(Cardinal(gdk_window_get_events(Widget^.Window)),8), //' GDK_BUTTON_RELEASE_MASK=',HexStr(Cardinal(GDK_BUTTON_RELEASE_MASK),8), //' Window=',HexStr(Cardinal(Widget^.Window),8) //); //if GetFixedWidget(Widget)<>nil then // DebugLn('DDD2 ClientWindow=',HexStr(Cardinal(PGtkWidget(GetFixedWidget(Widget))^.Window),8)); EventTrace('Mouse button Press', data); Assert(False, Format('Trace:[gtkMouseBtnPress] ', [])); UpdateMouseCaptureControl; if not (csDesigning in TComponent(Data).ComponentState) then begin DesignOnlySignal:=GetDesignOnlySignalFlag(Widget,dstMousePress); if DesignOnlySignal then exit; if not ControlGetsMouseDownBefore(TControl(Data)) then exit; CaptureWidget:=PGtkWidget(TWinControl(Data).Handle); if Event^.button=1 then begin CaptureMouseForWidget(CaptureWidget,mctGTKIntf); Result := false; end; end else begin // stop the signal, so that the widget does not auto react if (not (TControl(Data) is TCustomNoteBook)) or (event^.Button<>1) then begin g_signal_stop_emission_by_name(PGTKObject(Widget),'button-press-event'); result := false; end; end; //debugln('[gtkMouseBtnPress] calling DeliverMouseDownMessage'); DeliverMouseDownMessage(Widget,Event,TWinControl(Data)); end; {------------------------------------------------------------------------------- gtkMouseBtnPressAfter Params: widget: PGTKWidget; event: PGDKEventMotion; data: gPointer Returns: GBoolean Called whenever the mouse is over a widget and a mouse button is pressed. This is the last handler. -------------------------------------------------------------------------------} function gtkMouseBtnPressAfter(widget: PGtkWidget; event : pgdkEventButton; data: gPointer) : GBoolean; cdecl; begin Result := CallBackDefaultReturn; {$IFDEF VerboseMouseBugfix} WriteLn('[gtkMouseBtnPressAfter] ', TControl(Data).Name,':',TObject(Data).ClassName, ' Widget=',HexStr(Cardinal(Widget),8), ' ',TruncToInt(Event^.X),',',TruncToInt(Event^.Y)); {$ENDIF} UpdateMouseCaptureControl; // stop the signal, so that it is not sent to the parent widgets g_signal_stop_emission_by_name(PGTKObject(Widget),'button-press-event'); if (csDesigning in TComponent(Data).ComponentState) then exit; if ControlGetsMouseDownBefore(TControl(Data)) then exit; DeliverMouseDownMessage(Widget,Event,TWinControl(Data)); end; {------------------------------------------------------------------------------- function ControlGetsMouseUpBefore(AControl: TControl): boolean; Returns true, if mouse up event should be sent before the widget istelf reacts. -------------------------------------------------------------------------------} function ControlGetsMouseUpBefore(AControl: TControl): boolean; begin case AControl.fCompStyle of csCheckBox, csRadioButton, csToggleBox: Result:=false; else Result:=true; end; end; {------------------------------------------------------------------------------- procedure DeliverMouseUpMessage(widget: PGtkWidget; event : pgdkEventButton; AWinControl: TWinControl); Translate a gdk mouse release event into a LCL mouse up message and send it. -------------------------------------------------------------------------------} procedure DeliverMouseUpMessage(widget: PGtkWidget; event : pgdkEventButton; AWinControl: TWinControl); var MessI : TLMMouse; ShiftState: TShiftState; MappedXY: TPoint; function CheckMouseButtonUp(var LastMouse: TLastMouseClick; MsgUp: longint): boolean; begin MessI.Msg := MsgUp; LastMouse.Down := False; Result:=true; end; begin if (Widget=nil) then ; MappedXY:=TranslateGdkPointToClientArea(Event^.Window, Point(TruncToInt(Event^.X),TruncToInt(Event^.Y)), PGtkWidget(AWinControl.Handle)); case event^.Button of 1: if not CheckMouseButtonUp(LastLeft, LM_LBUTTONUP) then exit; 2: if not CheckMouseButtonUp(LastMiddle, LM_MBUTTONUP) then exit; 3: if not CheckMouseButtonUp(LastRight, LM_RBUTTONUP) then exit; else begin MessI.Msg := LM_NULL; exit; end; end; // case MessI.XPos := MappedXY.X; MessI.YPos := MappedXY.Y; ShiftState := gtkeventstate2shiftstate(Event^.State); MessI.Keys := 0; if ssShift in ShiftState then MessI.Keys := MessI.Keys or MK_SHIFT; if ssCtrl in ShiftState then MessI.Keys := MessI.Keys or MK_CONTROL; if ssLeft in ShiftState then MessI.Keys := MessI.Keys or MK_LBUTTON; if ssRight in ShiftState then MessI.Keys := MessI.Keys or MK_RBUTTON; if ssMiddle in ShiftState then MessI.Keys := MessI.Keys or MK_MBUTTON; if MessI.Msg <> LM_NULL then begin // send the message directly to the LCL // (Posting the message via queue // has the risk of getting out of sync with the gtk) MessI.Result := 0; NotifyApplicationUserInput(MessI.Msg); DeliverMessage(AWinControl, MessI); end; end; {------------------------------------------------------------------------------- gtkMouseBtnRelease Params: widget: PGTKWidget; event: PGDKEventMotion; data: gPointer Returns: GBoolean Called whenever the mouse is over a widget and a mouse button is released. -------------------------------------------------------------------------------} function gtkMouseBtnRelease(widget: PGtkWidget; event : pgdkEventButton; data: gPointer) : GBoolean; cdecl; var DesignOnlySignal: boolean; begin Result := CallBackDefaultReturn; {$IFDEF VerboseMouseBugfix} DesignOnlySignal:=GetDesignOnlySignalFlag(Widget,dstMouseRelease); DebugLn('[gtkMouseBtnRelease] A ', TComponent(Data).Name,':',TObject(Data).ClassName,' ', ' Widget=',HexStr(Cardinal(Widget),8), ' DSO=',DesignOnlySignal, ' ',TruncToInt(Event^.X),',',TruncToInt(Event^.Y),' Btn=',event^.Button); {$ENDIF} //DebugLn('EEE1 MouseRelease Widget=',HexStr(Cardinal(Widget),8), //' EventMask=',HexStr(Cardinal(gdk_window_get_events(Widget^.Window)),8), //' GDK_BUTTON_RELEASE_MASK=',HexStr(Cardinal(GDK_BUTTON_RELEASE_MASK),8)); UpdateMouseCaptureControl; if not (csDesigning in TComponent(Data).ComponentState) then begin DesignOnlySignal:=GetDesignOnlySignalFlag(Widget,dstMouseRelease); ReleaseMouseCapture; if DesignOnlySignal or (not ControlGetsMouseUpBefore(TControl(Data))) then begin exit; end; end else begin // stop the signal, so that the widget does not auto react if not (TControl(Data) is TCustomNoteBook) then g_signal_stop_emission_by_name(PGTKObject(Widget),'button-release-event'); end; DeliverMouseUpMessage(Widget,Event,TWinControl(Data)); end; {------------------------------------------------------------------------------- gtkMouseBtnReleaseAfter Params: widget: PGTKWidget; event: PGDKEventMotion; data: gPointer Returns: GBoolean Called whenever the mouse is over a widget and a mouse button is released. This is the last handler. -------------------------------------------------------------------------------} function gtkMouseBtnReleaseAfter(widget: PGtkWidget; event : pgdkEventButton; data: gPointer) : GBoolean; cdecl; begin Result := CallBackDefaultReturn; {$IFDEF VerboseMouseBugfix} {DebugLn('[gtkMouseBtnReleaseAfter] ', TControl(Data).Name,':',TObject(Data).ClassName,' ', TruncToInt(Event^.X),',',TruncToInt(Event^.Y));} {$ENDIF} // stop the signal, so that it is not sent to the parent widgets g_signal_stop_emission_by_name(PGTKObject(Widget),'button-release-event'); UpdateMouseCaptureControl; if (csDesigning in TComponent(Data).ComponentState) then exit; if ControlGetsMouseUpBefore(TControl(Data)) then exit; DeliverMouseUpMessage(Widget,Event,TWinControl(Data)); end; function gtkclickedCB(widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin Result := CallBackDefaultReturn; //DebugLn('[gtkclickedCB] ',TObject(Data).ClassName); EventTrace('clicked', data); if (LockOnChange(PgtkObject(Widget),0)>0) then exit; Mess.Msg := LM_CLICKED; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkOpenDialogRowSelectCB(widget : PGtkWidget; row : gint; column : gint; event : pgdkEventButton; data : gPointer ) : GBoolean; cdecl; var ShiftState: TShiftState; loop : gint; startRow : gint; endRow : gint; begin Result := CallBackDefaultReturn; if (Data=nil) then ; // only process the callback if there is event data. If there isn't any // event data that means it was called due to a direct function call of the // widget and not an actual mouse click on the widget. if event <> nil then begin ShiftState := GTKEventState2ShiftState(Event^.State); if ssShift in ShiftState then begin if LastFileSelectRow <> -1 then begin startRow := LastFileSelectRow; endRow := row; if LastFileSelectRow > row then begin startRow := row; endRow := LastFileSelectRow; end; for loop := startRow to endRow do begin gtk_clist_select_row(PGtkCList(widget), loop, column); end; end; end else if not (ssCtrl in ShiftState) then begin gtk_clist_unselect_all(PGtkCList(widget)); gtk_clist_select_row(PGtkCList(widget), row, column); end; LastFileSelectRow := row; end; end; function gtkDialogOKclickedCB( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var theDialog : TCommonDialog; Fpointer : Pointer; // colordialog colorsel : PGtkColorSelection; newColor : TGdkColor; // fontdialog FontName : String; ALogFont : TLogFont; // filedialog rowNum : gint; fileInfo : PGChar; {$IfDef GTK2} fileList : PPgchar; {$else} cListRow : PGList; fileList : PGTKCList; {$EndIf} DirName : string; FileName : string; Files: TStringList; CurFilename: string; function CheckOpenedFilename(const AFilename: string): boolean; begin Result:=true; if (ofOverwritePrompt in TOpenDialog(theDialog).Options) and FileExists(AFilename) then begin Result:=MessageDlg(rsfdOverwriteFile, Format(rsfdFileAlreadyExists,[AFileName]), mtConfirmation,[mbOk,mbCancel],0)=mrOk; if not Result then exit; end; end; procedure AddFile(List: TStrings; const NewFile: string); var i: Integer; begin for i:=0 to List.Count-1 do if List[i]=NewFile then exit; List.Add(NewFile); end; begin Result := True; if (Widget=nil) then ; theDialog := TCommonDialog(data); FPointer := Pointer(theDialog.Handle); if theDialog is TFileDialog then begin if theDialog is TOpenDialog then begin // check extra options if ofAllowMultiSelect in TOpenDialog(theDialog).Options then begin FileName:=gtk_file_selection_get_filename( PGtkFileSelection(FPointer)); DirName:=ExtractFilePath(FileName); TFileDialog(data).FileName := ''; Files:=TStringList(TFileDialog(theDialog).Files); Files.Clear; if (Filename<>'') then begin Result:=CheckOpenedFilename(Filename); if not Result then exit; AddFile(Files,FileName); end; {$IfDef GTK2} fileList := gtk_file_selection_get_selections(PGtkFileSelection(FPointer)); rowNum := 0; While FileList^ <> nil do begin fileInfo := FileList^; CurFilename:=AnsiString(fileInfo); if (CurFilename<>'') and (Files.IndexOf(CurFilename)<0) then begin Result:=CheckOpenedFilename(CurFilename); if not Result then exit; Files.Add(CurFilename); end; inc(FileList); inc(rowNum); end; Dec(FileList, rowNum); g_strfreev(fileList); {$Else} fileList := PGtkCList(PGtkFileSelection(FPointer)^.file_list); rowNum := 0; cListRow := fileList^.row_list; while cListRow <> nil do begin if PGtkCListRow(cListRow^.data)^.state = GTK_STATE_SELECTED then begin if gtk_clist_get_cell_type(fileList, rowNum, 0) = GTK_CELL_TEXT then begin gtk_clist_get_text(fileList, rowNum, 0, @fileInfo); CurFilename:=DirName+fileInfo; Result:=CheckOpenedFilename(CurFilename); if not Result then exit; AddFile(Files,CurFilename); end; end; // get next row from list rowNum := rowNum + 1; cListRow := g_list_next(cListRow); end; {$EndIf} end else begin CurFilename:= gtk_file_selection_get_filename(PGtkFileSelection(FPointer)); Result:=CheckOpenedFilename(CurFilename); if not Result then exit; TFileDialog(data).FileName := CurFilename; end; end else begin TFileDialog(data).FileName := gtk_file_selection_get_filename(PGtkFileSelection(FPointer)); end; end else if theDialog is TColorDialog then begin colorSel := PGtkColorSelection(PGtkColorSelectionDialog(FPointer)^.colorsel); gtk_color_selection_get_current_color(colorsel, @newColor); TColorDialog(theDialog).Color := TGDKColorToTColor(newcolor); {$IFDEF VerboseColorDialog} DebugLn('gtkDialogOKclickedCB ',HexStr(Cardinal(TColorDialog(theDialog).Color),8)); {$ENDIF} end else if theDialog is TFontDialog then begin Assert(False, 'Trace:Pressed OK in FontDialog'); FontName := gtk_font_selection_dialog_get_font_name( pgtkfontselectiondialog(FPointer)); // extract basic font attributes from the font name in XLFD format ALogFont:=XLFDNameToLogFont(FontName); TFontDialog(theDialog).Font.Assign(ALogFont); // set the font name in XLFD format // a font name in XLFD format overrides in the gtk interface all other font // settings. TFontDialog(theDialog).Font.Name := FontName; Assert(False, 'Trace:-----'+TFontDialog(theDialog).Font.Name+'----'); end; StoreCommonDialogSetup(theDialog); theDialog.UserChoice := mrOK; end; {------------------------------------------------------------------------------- function gtkDialogCancelclickedCB Params: widget: PGtkWidget; data: gPointer Result: GBoolean This function is called, whenever the user clicks the cancel button in a commondialog -------------------------------------------------------------------------------} function gtkDialogCancelclickedCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl; var theDialog : TCommonDialog; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; theDialog := TCommonDialog(data); if theDialog is TFileDialog then begin TFileDialog(data).FileName := ''; end; StoreCommonDialogSetup(theDialog); theDialog.UserChoice := mrCancel; end; {------------------------------------------------------------------------------- function gtkDialogHelpclickedCB Params: widget: PGtkWidget; data: gPointer Result: GBoolean This function is called, whenever the user clicks the help button in a commondialog -------------------------------------------------------------------------------} function gtkDialogHelpclickedCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl; var theDialog : TCommonDialog; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; theDialog := TCommonDialog(data); if theDialog is TOpenDialog then begin if TOpenDialog(theDialog).OnHelpClicked<>nil then TOpenDialog(theDialog).OnHelpClicked(theDialog); end; end; {------------------------------------------------------------------------------- function gtkDialogApplyclickedCB Params: widget: PGtkWidget; data: gPointer Result: GBoolean This function is called, whenever the user clicks the Apply button in a commondialog -------------------------------------------------------------------------------} function gtkDialogApplyclickedCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl; var theDialog : TCommonDialog; FontName: string; ALogFont: TLogFont; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; theDialog := TCommonDialog(data); if (theDialog is TFontDialog) and (fdApplyButton in TFontDialog(theDialog).Options) and (Assigned(TFontDialog(theDialog).OnApplyClicked)) then begin // extract basic font attributes from the font name in XLFD format FontName := gtk_font_selection_dialog_get_font_name( pgtkfontselectiondialog(theDialog.Handle)); ALogFont:=XLFDNameToLogFont(FontName); TFontDialog(theDialog).Font.Assign(ALogFont); // set the font name in XLFD format // a font name in XLFD format overrides in the gtk interface all other font // settings. TFontDialog(theDialog).Font.Name := FontName; TFontDialog(theDialog).OnApplyClicked(theDialog); end; end; {------------------------------------------------------------------------------- function gtkDialogCloseQueryCB Params: widget: PGtkWidget; data: gPointer Result: GBoolean This function is called, before a commondialog is destroyed -------------------------------------------------------------------------------} function gtkDialogCloseQueryCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl; var theDialog : TCommonDialog; CanClose: boolean; begin Result := False; // true = do nothing, false = destroy or hide window if (Data=nil) then ; // data is not the commondialog. Get it manually. theDialog := TCommonDialog(GetLCLObject(Widget)); if theDialog=nil then exit; if theDialog.OnCanClose<>nil then begin CanClose:=True; theDialog.OnCanClose(theDialog,CanClose); Result:=not CanClose; end; if not Result then begin StoreCommonDialogSetup(theDialog); DestroyCommonDialogAddOns(theDialog); end; end; {------------------------------------------------------------------------------- procedure UpdateDetailView Params: OpenDialog: TOpenDialog Result: none Shows some OS dependent information about the current file -------------------------------------------------------------------------------} procedure UpdateDetailView(OpenDialog: TOpenDialog); var FileDetailLabel: PGtkWidget; Filename, OldFilename, Details: string; begin Filename:= gtk_file_selection_get_filename(PGtkFileSelection(OpenDialog.Handle)); OldFilename:=OpenDialog.Filename; if Filename=OldFilename then exit; OpenDialog.Filename:=Filename; // tell application, that selection has changed OpenDialog.DoSelectionChange; if (OpenDialog.OnFolderChange<>nil) and (ExtractFilePath(Filename)<>ExtractFilePath(OldFilename)) then OpenDialog.DoFolderChange; // show some information FileDetailLabel:=gtk_object_get_data(PGtkObject(OpenDialog.Handle), 'FileDetailLabel'); if FileDetailLabel=nil then exit; if FileExists(Filename) then begin Details:=GetFileDescription(Filename); end else begin Details:=Format(rsFileInfoFileNotFound, [Filename]); end; gtk_label_set_text(PGtkLabel(FileDetailLabel),PChar(Details)); end; {------------------------------------------------------------------------------- function GTKDialogKeyUpDownCB Params: Widget: PGtkWidget; Event : pgdkeventkey; Data: gPointer Result: GBoolean This function is called, whenever a key is pressed or released in a common dialog window -------------------------------------------------------------------------------} function GTKDialogKeyUpDownCB(Widget: PGtkWidget; Event : pgdkeventkey; Data: gPointer) : GBoolean; cdecl; begin Result:=false; if (Widget=nil) then ; case gdk_event_get_type(Event) of GDK_KEY_RELEASE, GDK_KEY_PRESS: begin if Event^.KeyVal = GDK_KEY_Escape then begin StoreCommonDialogSetup(TCommonDialog(data)); TCommonDialog(data).UserChoice:=mrCancel; end; if (TCommonDialog(data) is TOpenDialog) then begin UpdateDetailView(TOpenDialog(data)); end; end; end; end; {------------------------------------------------------------------------------- function GTKDialogRealizeCB Params: Widget: PGtkWidget; Data: Pointer Result: GBoolean This function is called, whenever a commondialog window is realized -------------------------------------------------------------------------------} function GTKDialogRealizeCB(Widget: PGtkWidget; Data: Pointer): GBoolean; cdecl; var LCLComponent: TObject; begin if (Data=nil) then ; gdk_window_set_events(GetControlWindow(Widget), gdk_window_get_events(GetControlWindow(Widget)) or GDK_KEY_RELEASE_MASK or GDK_KEY_PRESS_MASK); LCLComponent:=GetLCLObject(Widget); if LCLComponent is TCommonDialog then TCommonDialog(LCLComponent).DoShow; Result:=true; end; {------------------------------------------------------------------------------- function GTKDialogFocusInCB Params: widget: PGtkWidget; data: gPointer Result: GBoolean This function is called, when a widget of a commondialog gets focus -------------------------------------------------------------------------------} function GTKDialogFocusInCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl; var theDialog: TCommonDialog; begin Result:=false; if (Data=nil) then ; theDialog:=TCommonDialog(GetLCLObject(Widget)); if (theDialog is TOpenDialog) then begin UpdateDetailView(TOpenDialog(theDialog)); end; end; {------------------------------------------------------------------------------- function GTKDialogSelectRowCB Params: widget: PGtkWidget; data: gPointer Result: GBoolean This function is called, whenever a row is selected in a commondialog -------------------------------------------------------------------------------} function GTKDialogSelectRowCB(widget: PGtkWidget; Row, Column: gInt; bevent: pgdkEventButton; data: gPointer): GBoolean; cdecl; var theDialog: TCommonDialog; begin Result:=false; if (Data=nil) or (BEvent=nil) or (Column=0) or (Row=0) then ; theDialog:=TCommonDialog(GetLCLObject(Widget)); if (theDialog is TOpenDialog) then begin UpdateDetailView(TOpenDialog(theDialog)); end; end; {------------------------------------------------------------------------------- function GTKDialogMenuActivateCB Params: widget: PGtkWidget; data: gPointer Result: GBoolean This function is called, whenever a menu of a commondialog is activated -------------------------------------------------------------------------------} function GTKDialogMenuActivateCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl; var theDialog: TCommonDialog; procedure CheckFilterActivated(FilterWidget: PGtkWidget); var AFilterEntry: PFileSelFilterEntry; begin if FilterWidget=nil then exit; AFilterEntry:=gtk_object_get_data(PGtkObject(FilterWidget), 'LCLIsFilterMenuItem'); if (AFilterEntry<>nil) and (AFilterEntry^.Mask<>nil) then begin gtk_file_selection_complete(PGtkFileSelection(theDialog.Handle), AFilterEntry^.Mask); UpdateDetailView(TOpenDialog(theDialog)); end; end; var AHistoryEntry: PFileSelHistoryEntry; FilterMenu, ActiveFilterMenuItem: PGtkWidget; begin Result:=false; if (Data=nil) then ; theDialog:=TCommonDialog(GetNearestLCLObject(Widget)); if (theDialog is TOpenDialog) then begin // check if history activated AHistoryEntry:=gtk_object_get_data(PGtkObject(Widget), 'LCLIsHistoryMenuItem'); if (AHistoryEntry<>nil) and (AHistoryEntry^.Filename<>nil) then begin // user has choosen a history file // -> select it in the filedialog gtk_file_selection_complete(PGtkFileSelection(theDialog.Handle), AHistoryEntry^.Filename); // restore filter if DirPathExists(AHistoryEntry^.Filename) then begin FilterMenu:=gtk_object_get_data(PGtkObject(theDialog.Handle), 'LCLFilterMenu'); if FilterMenu<>nil then begin ActiveFilterMenuItem:=gtk_menu_get_active(GTK_MENU(FilterMenu)); CheckFilterActivated(ActiveFilterMenuItem); end; end; UpdateDetailView(TOpenDialog(theDialog)); end; // check if filter activated CheckFilterActivated(Widget); end; end; {------------------------------------------------------------------------------- function gtkDialogDestroyCB Params: widget: PGtkWidget; data: gPointer Result: GBoolean This function is called, when a commondialog is destroyed -------------------------------------------------------------------------------} function gtkDialogDestroyCB(widget: PGtkWidget; data: gPointer): GBoolean; cdecl; begin Result := True; if (Widget=nil) then ; TCommonDialog(data).UserChoice := mrAbort; TCommonDialog(data).Close; end; function gtkPressedCB( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; Status : GBoolean; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; EventTrace('pressed', data); Mess.msg := LM_PRESSED; Status := DeliverMessage(Data, Mess) = 0; {$ifdef GTK2} Result := False; {$Else} Result := Status; {$endif} end; function gtkEnterCB(widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; Status : GBoolean; begin Result := CallBackDefaultReturn; EventTrace('enter', data); if csDesigning in TControl(Data).ComponentState then begin // stop the signal, so that the widget does not auto react g_signal_stop_emission_by_name(PGTKObject(Widget),'enter'); end; Mess.msg := CM_MOUSEENTER; Status := DeliverMessage(Data, Mess) = 0; {$ifdef GTK2} Result := CallBackDefaultReturn; {$Else} Result := Status; {$endif} end; function gtkLeaveCB(widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; Status : GBoolean; begin Result := CallBackDefaultReturn; EventTrace('leave', data); if csDesigning in TControl(Data).ComponentState then begin // stop the signal, so that the widget does not auto react g_signal_stop_emission_by_name(PGTKObject(Widget),'leave'); end; Mess.msg := CM_MOUSELEAVE; Status := DeliverMessage(Data, Mess) = 0; {$ifdef GTK2} Result := CallBackDefaultReturn; {$Else} Result := Status; {$endif} end; function gtkMoveCursorCB(widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; EventTrace('move-cursor', data); Mess.msg := LM_MOVECURSOR; DeliverMessage(Data, Mess); end; function gtksize_allocateCB(widget: PGtkWidget; size: pGtkAllocation; data: gPointer) : GBoolean; cdecl; begin Result := CallBackDefaultReturn; EventTrace('size-allocate', data); with Size^ do Assert(False, Format('Trace:[gtksize_allocateCB] %s --> X: %d, Y: %d, Width: %d, Height: %d', [TObject(data).ClassName, X, Y, Width, Height])); if not (TObject(Data) is TControl) then begin // owner is not TControl -> ignore DebugLn('WARNING: gtksize_allocateCB: Data is not TControl. Data=', HexStr(Cardinal(Data),8),' ',GetWidgetClassName(Widget)); if Data<>nil then DebugLn(' Data=',TObject(Data).ClassName); RaiseException(''); exit; end; { The gtk sends the size messages after the resizing. Therefore the parent widget is already resized, but the parent resize message will be emitted after all its childs. So, the gtk resizes in top-bottom order, just like the LCL. But it sends size messages in bottom-top order, which results in many resizes in the LCL. Therefore all resize messages between lcl and gtk are cached. } {$IFDEF VerboseSizeMsg} DebugLn('gtksize_allocateCB: ', TControl(Data).Name+':'+TControl(Data).ClassName, ' widget='+HexStr(Cardinal(Widget),8)+WidgetFlagsToString(widget)+ ' fixwidget=',HexStr(Cardinal(GetFixedWidget(Widget)),8), ' GtkPos=',dbgs(Widget^.allocation.x)+','+dbgs(Widget^.allocation.y), ','+dbgs(Widget^.allocation.width)+'x'+dbgs(Widget^.allocation.width)+ ' LCLPos='+dbgs(TControl(Data).Left)+','+dbgs(TControl(Data).Top), ','+dbgs(TControl(Data).Width)+'x'+dbgs(TControl(Data).Height)); {$ENDIF} {$IFDEF VerboseFormPositioning} if TControl(Data) is TCustomForm then DebugLn('VFP gtksize_allocateCB: ',TControl(Data).ClassName,' ',dbgs(Size^.X),',',dbgs(Size^.Y)); {$ENDIF} if GTK_WIDGET_REALIZED(Widget) then SaveSizeNotification(Widget); end; function gtksize_allocate_client(widget: PGtkWidget; size: pGtkAllocation; data: gPointer): GBoolean; cdecl; var MainWidget: PGtkWidget; ClientWidget: PGtkWidget; begin Result := CallBackDefaultReturn; if (Widget=nil) or (Size=nil) then ; if (TObject(Data) is TWinControl) then begin {$IFDEF VerboseSizeMsg} DebugLn('gtksize_allocate_client: ', TControl(Data).Name,':',TControl(Data).ClassName, ' widget=',HexStr(Cardinal(Widget),8), ' NewSize=',dbgs(Size^.Width),',',dbgs(Size^.Height), ' Allocation='+dbgs(widget^.Allocation.Width)+'x'+dbgs(Widget^.Allocation.Height), ' Requisiton='+dbgs(widget^.Requisition.Width)+'x'+dbgs(Widget^.Requisition.Height) ); {$ENDIF} if not TWinControl(Data).HandleAllocated then begin exit; end; MainWidget:=PGtkWidget(TWinControl(Data).Handle); ClientWidget:=GetFixedWidget(MainWidget); if GTK_WIDGET_REALIZED(ClientWidget) then SaveClientSizeNotification(ClientWidget); end else begin // owner is not TWinControl -> ignore DebugLn('WARNING: gtksize_allocate_client: Data is not TWinControl. Data=', HexStr(Cardinal(Data),8)); exit; end; end; function gtkswitchpage(widget: PGtkWidget; page: Pgtkwidget; pagenum: integer; data: gPointer): GBoolean; cdecl; var Mess: TLMNotify; NMHdr: tagNMHDR; SwitchAllowed: Boolean; begin Result := CallBackDefaultReturn; if (Widget=nil) or (Page=nil) then ; EventTrace('switch-page', data); UpdateNoteBookClientWidget(TObject(Data)); // gtkswitchpage is called before the switch // send first the TCN_SELCHANGING to ask if switch is allowed FillChar(Mess,SizeOf(Mess),0); Mess.Msg := LM_NOTIFY; FillChar(NMHdr,SizeOf(NMHdr),0); NMHdr.code := TCN_SELCHANGING; NMHdr.hwndfrom := longint(widget); NMHdr.idfrom := pagenum; //use this to set pageindex to the correct page. Mess.NMHdr := @NMHdr; Mess.Result := 0; DeliverMessage(Data, Mess); SwitchAllowed:=Mess.Result=0; if not SwitchAllowed then begin debugln('gtkswitchpage A SwitchAllowed=false not yet implemented'); end; // then send the new page FillChar(Mess,SizeOf(Mess),0); Mess.Msg := LM_NOTIFY; FillChar(NMHdr,SizeOf(NMHdr),0); NMHdr.code := TCN_SELCHANGE; NMHdr.hwndfrom := longint(widget); NMHdr.idfrom := pagenum; //use this to set pageindex to the correct page. Mess.NMHdr := @NMHdr; DeliverMessage(Data, Mess); end; function gtkconfigureevent( widget: PGtkWidget; event : PgdkEventConfigure; data: gPointer) : GBoolean; cdecl; var Allocation : PGtkAllocation; begin { This signal is emitted for top level controls only, i.e. only controls that are not children. Thus, we register this event only for forms. This event is fired when the form is sized, moved or changes Z order. } New(Allocation); try with Allocation^ do begin X:= Event^.X; Y:= Event^.Y; Width:= Event^.Width; Height:= Event^.Height; end; Result:= gtksize_allocateCB( Widget, Allocation, Data); finally Dispose(Allocation); end; end; function gtkreleasedCB( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMEssage; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; EventTrace('released', data); Mess.msg := LM_RELEASED; DeliverMessage(Data, Mess); end; function gtkInsertText( widget: PGtkWidget; char : pChar; NewTextLength : Integer; Position : pgint; data: gPointer) : GBoolean; cdecl; var Memo: TCustomMemo; CurrLength, CutLength: integer; begin Result := CallBackDefaultReturn; { GTK does not provide its own max. length for memos we have to do our own. } if TControl(Data) is TCustomMemo then begin Memo:= TCustomMemo(Data); if Memo.MaxLength < 0 then Exit; CurrLength:= gtk_text_get_length(PGtkText(widget)); if CurrLength + NewTextLength <= Memo.MaxLength then Exit; CutLength:= CurrLength + NewTextLength - Memo.MaxLength; if NewTextLength - CutLength > 0 then begin gtk_editable_insert_text(PGtkEditable(widget), char, NewTextLength - CutLength, Position); end; g_signal_stop_emission_by_name(PGtkObject(widget), 'insert_text'); end; end; function gtkDeleteText( widget: PGtkWidget; Startpos, EndPos : Integer; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Delete Text', data); if (StartPos=0) or (EndPos=0) or (Widget=nil) then ; Mess.msg := LM_DELETETEXT; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkSetEditable( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Set Editable', data); if (Widget=nil) then ; Mess.msg := LM_SETEDITABLE; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkMoveWord( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Move Word', data); if (Widget=nil) then ; Mess.msg := LM_MOVEWORD; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkMovePage( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Move Page', data); if (Widget=nil) then ; Mess.msg := LM_MOVEPAGE; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkMoveToRow( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Move To Row!!', data); if (Widget=nil) then ; Mess.msg := LM_MOVETOROW; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkMoveToColumn( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('MoveToColumn', data); if (Widget=nil) then ; Mess.msg := LM_MOVETOCOLUMN; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkKillChar( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Kill Char', data); if (Widget=nil) then ; Mess.msg := LM_KILLCHAR; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkKillWord( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Kill Word', data); if (Widget=nil) then ; Mess.msg := LM_KILLWORD; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkKillLine( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Kill Line', data); if (Widget=nil) then ; Mess.msg := LM_KILLLINE; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkCutToClip( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Cut to clip', data); if (Widget=nil) then ; Mess.msg := LM_CUTTOCLIP; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkCopyToClip( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Copy to Clip', data); if (Widget=nil) then ; Mess.msg := LM_COPYTOCLIP; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkPasteFromClip( widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin EventTrace('Paste from clip', data); if (Widget=nil) then ; Mess.msg := LM_PASTEFROMCLIP; Result:= DeliverMessage(Data, Mess) = 0; end; function gtkValueChanged(widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var Mess : TLMessage; begin Result := CallBackDefaultReturn; EventTrace('Value changed', data); if (Widget=nil) then ; Mess.msg := LM_CHANGED; DeliverMessage(Data, Mess); end; {------------------------------------------------------------------------------ Method: gtkTimerCB Params: Data - pointer TGtkITimerInfo structure Returns: 1 - 1 tells gtk to restart the timer 0 - 0 will stop the gtk timer Callback for gtk timer. WARNING: There seems to be a bug in gtk-1.2.x which breaks gtk_timeout_remove so we have to dispose data here & return 0 (s.a. KillTimer). ------------------------------------------------------------------------------} function gtkTimerCB(Data: gPointer): {$IFDEF Gtk2}gBoolean{$ELSE}gint{$ENDIF}; cdecl; var TimerInfo: PGtkITimerinfo; begin EventTrace ('TimerCB', nil); Result := GdkFalse; // assume: timer will stop TimerInfo:=PGtkITimerinfo(Data); if (FTimerData=nil) or (FTimerData.IndexOf(Data)<0) then begin {$IFDEF VerboseTimer} DebugLn('gtkTimerCB Timer was killed: TimerInfo=',HexStr(Cardinal(TimerInfo),8)); {$ENDIF} // timer was killed Result:=GdkFalse; // stop timer end else begin {$IFDEF VerboseTimer} DebugLn('gtkTimerCB Timer Event: TimerInfo=',HexStr(Cardinal(TimerInfo),8)); {$ENDIF} if TimerInfo^.TimerFunc <> nil then begin // Call users timer function TimerInfo^.TimerFunc; Result:=GdkTrue; // timer will go on end else begin Result := GdkFalse; // stop timer end; end; if (Result<>GdkFalse) and (FTimerData.IndexOf(Data)<0) then begin // timer was killed // -> stop timer Result:=GdkFalse; end; if Result=GdkFalse then begin {$IFDEF VerboseTimer} DebugLn('gtkTimerCB Timer was STOPPED: TimerInfo=',HexStr(Cardinal(TimerInfo),8)); {$ENDIF} // timer will be stopped // -> free timer data, if not already done if (FTimerData.IndexOf(Data)>=0) then begin FTimerData.Remove(Data); Dispose (TimerInfo); // free memory with timer data end; end; end; function gtkFocusInNotifyCB (widget : PGtkWidget; event : PGdkEvent; data : gpointer) : GBoolean; cdecl; var MessI : TLMEnter; begin Result := CallBackDefaultReturn; //DebugLn('[gtkFocusInNotifyCB] ',TControl(data).Name,':',TObject(data).ClassName); EventTrace ('FocusInNotify (alias Enter)', data); if (Event=nil) then ; if csDesigning in TControl(Data).ComponentState then begin // stop the signal, so that the widget does not auto react g_signal_stop_emission_by_name(PGTKObject(Widget),'focus-in-event'); end; MessI.msg := LM_Enter; DeliverMessage(Data, MessI); end; function gtkFocusOutNotifyCB (widget : PGtkWidget; event : PGdkEvent; data : gpointer) : GBoolean; cdecl; var MessI : TLMExit; begin Result := CallBackDefaultReturn; //DebugLn('[gtkFocusOutNotifyCB] ',TControl(data).Name,':',TObject(data).ClassName); EventTrace ('FocusOutNotify (alias Exit)', data); if (Event=nil) then ; if csDesigning in TControl(Data).ComponentState then begin // stop the signal, so that the widget does not auto react g_signal_stop_emission_by_name(PGTKObject(Widget),'focus-out-event'); end; MessI.msg := LM_Exit; DeliverMessage(Data, MessI); end; {$IFNDEF GTK2} function gtk_range_get_update_policy(range: PGTKRange): TGtkUpdateType; begin result := policy(Range^) end; {$ENDIF} {$IFDEF VerboseGtkScrollbars} procedure DebugScrollEvent(Range: PgtkRange); begin DbgOut('BUTTON='); case Range^.Button of 1: DbgOut('LEFT '); 2: DbgOut('CENTER '); 3: DbgOut('RIGHT '); else DbgOut(IntToStr(Range^.Button), ' -> ? '); end; DbgOut('CLICK_CHILD='); case click_child(Range^) of 1: DbgOut('TROUGH '); 2: DbgOut('SLIDER '); 3: DbgOut('STEP_FORW '); 4: DbgOut('STEP_BACK '); else DbgOut(IntToStr(click_child(range^)), ' -> ? '); end; DbgOut('IN_CHILD='); case in_child(range^) of 1: DbgOut('TROUGH '); 2: DbgOut('SLIDER '); 3: DbgOut('STEP_FORW '); 4: DbgOut('STEP_BACK '); else DbgOut(IntToStr(in_child(Range^)), ' -> ? '); end; DbgOut('TYPE='); case Scroll_Type(Range^) of GTK_SCROLL_NONE: DbgOut('GTK_SCROLL_NONE '); GTK_SCROLL_STEP_BACKWARD: DbgOut('GTK_SCROLL_STEP_BACKWARD '); GTK_SCROLL_STEP_FORWARD: DbgOut('GTK_SCROLL_STEP_FORWARD '); GTK_SCROLL_PAGE_BACKWARD: DbgOut('GTK_SCROLL_PAGE_BACKWARD '); GTK_SCROLL_PAGE_FORWARD: DbgOut('GTK_SCROLL_PAGE_FORWARD '); GTK_SCROLL_JUMP: DbgOut('GTK_SCROLL_JUMP '); else DbgOut(IntToStr(Scroll_Type(Range^)), '->?'); end; DbgOut('OLD_VALUE=', IntToStr(Round(Range^.old_value)),' '); Debugln; end; procedure DebugPolicy(Policy: TGtkUpdateType); begin DbgOut('POLICY='); case policy of GTK_UPDATE_CONTINUOUS: DbgOut('GTK_UPDATE_CONTINUOUS'); GTK_UPDATE_DISCONTINUOUS: DbgOut('GTK_UPDATE_DISCONTINUOUS'); GTK_UPDATE_DELAYED: DbgOut('GTK_UPDATE_DELAYED'); end; DebugLn; end; {$ENDIF VerboseGtkScrollbars} function GTKHScrollCB(Adjustment: PGTKAdjustment; data: GPointer): GBoolean; cdecl; var Msg: TLMHScroll; Scroll: PGtkRange; UpdatePolicy: TGtkUpdateType; RangeClass: PgtkRangeClass; begin Result := CallBackDefaultReturn; Assert(False, Format('Trace:[GTKHScrollCB] Value: %d', [RoundToInt(Adjustment^.Value)])); Scroll := PgtkRange(gtk_object_get_data(PGTKObject(Adjustment), 'ScrollBar')); if Scroll=nil then exit; RangeClass := PgtkRangeClass(PgtkObject(Scroll)^.klass); UpdatePolicy := gtk_range_get_update_policy(Scroll); //X := integer(gtk_object_get_data(PGtkObject(Scroll), 'FinalEvent')); //WriteLn('FINAL EVENT: ', X); //BeginGDKErrorTrap; //gdk_window_get_pointer(GetControlWindow(Scroll), @X, @Y, @Mask); //EndGDKErrorTrap; {$IFDEF VerboseGtkScrollbars} DebugScrollEvent(Scroll); DebugPolicy(UpdatePolicy); {$ENDIF} Msg.Msg := LM_HSCROLL; with Msg do begin pos := Round(Adjustment^.Value); ScrollBar := HWND(Scroll); case Scroll_type(Scroll^) of GTK_SCROLL_NONE: begin ScrollCode := SB_THUMBTRACK; if click_child(scroll^) = RangeClass^.Slider then if UpdatePolicy <> GTK_UPDATE_CONTINUOUS then ScrollCode := SB_THUMBPOSITION; end; GTK_SCROLL_STEP_BACKWARD: ScrollCode := SB_LINELEFT; GTK_SCROLL_STEP_FORWARD: ScrollCode := SB_LINERIGHT; GTK_SCROLL_PAGE_BACKWARD: ScrollCode := SB_PAGELEFT; GTK_SCROLL_PAGE_FORWARD: ScrollCode := SB_PAGERIGHT; else begin // GTK_SCROLL_JUMP and others not known? {$IFDEF VerboseGtkScrollbars} debugln('GTKVScrollCB: Scroll_type=', IntToStr(Scroll_type(Scroll^))); {$Endif} if UpdatePolicy=GTK_UPDATE_CONTINUOUS then ScrollCode := SB_THUMBTRACK else ScrollCode := SB_THUMBPOSITION; end; end; end; DeliverMessage(Data, Msg); end; function GTKVScrollCB(Adjustment: PGTKAdjustment; data: GPointer): GBoolean; cdecl; var Msg: TLMVScroll; Scroll: PGtkRange; UpdatePolicy: TGtkUpdateType; RangeClass: PgtkRangeClass; begin Result := CallBackDefaultReturn; Assert(False, Format('Trace:[GTKVScrollCB] Value: %d', [RoundToInt(Adjustment^.Value)])); Scroll := PgtkRange(gtk_object_get_data(PGTKObject(Adjustment), 'ScrollBar')); if (Scroll=nil) then exit; RangeClass := PgtkRangeClass(PgtkObject(Scroll)^.klass); UpdatePolicy := gtk_range_get_update_policy(Scroll); //UpdatePolicy := TGtkUpdateType( gtk_object_get_data(PgtkObject(Scroll), 'UpdatePolicy')); //WriteLn('FINAL EVENT: ', integer(gtk_object_get_data(PGtkObject(Scroll), 'FinalEvent'))); //BeginGDKErrorTrap; //gdk_window_get_pointer(GetControlWindow(Scroll), @X, @Y, @Mask); //EndGDKErrorTrap; {$IFDEF VerboseGtkScrollbars} DebugScrollEvent(Scroll); DebugPolicy(UpdatePolicy); {$ENDIF} Msg.Msg := LM_VSCROLL; with Msg do begin pos := Round(Adjustment^.Value); ScrollBar := HWND(Scroll); case Scroll_type(Scroll^) of GTK_SCROLL_NONE: begin ScrollCode := SB_THUMBTRACK; if click_child(scroll^) = RangeClass^.Slider then if UpdatePolicy <> GTK_UPDATE_CONTINUOUS then ScrollCode := SB_THUMBPOSITION; end; GTK_SCROLL_STEP_BACKWARD: ScrollCode := SB_LINEUP; GTK_SCROLL_STEP_FORWARD: ScrollCode := SB_LINEDOWN; GTK_SCROLL_PAGE_BACKWARD: ScrollCode := SB_PAGEUP; GTK_SCROLL_PAGE_FORWARD: ScrollCode := SB_PAGEDOWN; else begin // GTK_SCROLL_JUMP and others not known? {$IFDEF VerboseGtkScrollbars} debugln('GTKVScrollCB: Scroll_type=', IntToStr(Scroll_type(Scroll^))); {$ENDIF} if UpdatePolicy=GTK_UPDATE_CONTINUOUS then ScrollCode := SB_THUMBTRACK else ScrollCode := SB_THUMBPOSITION; end; end; end; DeliverMessage(Data, Msg); end; {------------------------------------------------------------------------------ Function: GTKKeySnooper Params: Widget: The widget for which this event is fired Event: The keyevent data FuncData: the user parameter passed when the snooper was installed Returns: True if other snoopers shouldn't handled Keeps track of which keys are pressed. The keycode is casted to a pointer and if it exists in the KeyStateList, it is pressed. ------------------------------------------------------------------------------} function GTKKeySnooper(Widget: PGtkWidget; Event: PGdkEventKey; FuncData: gPointer): gInt; cdecl; var KeyStateList: TList; procedure UpdateToggleList(const AVKeyCode: Integer); begin // Check for a toggle // If the remove was successfull, the key was on // else it was off so we should set the toggle flag if KeyStateList.Remove(Pointer(AVKeyCode or KEYMAP_TOGGLE)) < 0 then KeyStateList.Add(Pointer(AVKeyCode or KEYMAP_TOGGLE)); end; procedure UpdateList(const AVKeyCode: Integer; const APressed: Boolean); begin if AVKeyCode = 0 then Exit; if APressed then begin if KeyStateList.IndexOf(Pointer(AVKeyCode)) < 0 then KeyStateList.Add(Pointer(AVKeyCode)); end else begin KeyStateList.Remove(Pointer(AVKeyCode)); end; end; const STATE_MAP: array[0..3] of Byte = ( GDK_SHIFT_MASK, // shift GDK_CONTROL_MASK, // control GDK_MOD1_MASK, // alt GDK_MOD4_MASK // win ); VK_MAP: array[0..3] of array[0..2] of Byte = ( // (Main key, alt key 1, alt key 2) to check (VK_SHIFT, VK_LSHIFT, VK_RSHIFT), (VK_CONTROL, VK_LCONTROL, VK_RCONTROL), (VK_MENU, VK_LMENU, VK_RMENU), (VK_LWIN, VK_RWIN, 0) ); var VKey: TVKeyRecord; Pressed: Boolean; n: Integer; begin Result := 0; // TODO: Remove when KeyStateList is obsolete case gdk_event_get_type(Event) of GDK_KEY_PRESS: Pressed := True; GDK_KEY_RELEASE: Pressed := False; else // not interested Exit; end; VKey := KeySymToVKey(Event^.keyval); if VKey.VKey = $FF then begin if Pressed then DebugLn(Format('[WARNING] Key pressed without VKey: K=0x%x S="%s"', [ Event^.KeyVal, {$IFDEF GTK2} Event^._String {$ELSE} Event^.theString {$ENDIF} ])); Exit; end; KeyStateList := TList(FuncData); if KeyStateList = nil then Exit; UpdateList(Vkey.VKey, Pressed); if IsToggleKey(Vkey.VKey) then UpdateToggleList(Vkey.VKey); // Add special left and right codes case Event^.KeyVal of GDK_Key_Shift_L: UpdateList(VK_LSHIFT, Pressed); GDK_Key_Shift_R: UpdateList(VK_RSHIFT, Pressed); GDK_Key_Control_L: UpdateList(VK_LCONTROL, Pressed); GDK_Key_Control_R: UpdateList(VK_RCONTROL, Pressed); GDK_Key_Alt_L: UpdateList(VK_LMENU, Pressed); GDK_Key_Alt_R: UpdateList(VK_RMENU, Pressed); end; // Recheck the list against the modifiers for n := 0 to High(STATE_MAP) do begin // Skip our current key, since the state is updated after the event if VKey.Vkey = VK_MAP[n][0] then Continue; if VKey.Vkey = VK_MAP[n][1] then Continue; if VKey.Vkey = VK_MAP[n][2] then Continue; UpdateList(VK_MAP[n][0], (STATE_MAP[n] and Event^.State) <> 0); UpdateList(VK_MAP[n][1], (STATE_MAP[n] and Event^.State) <> 0); UpdateList(VK_MAP[n][2], (STATE_MAP[n] and Event^.State) <> 0); end; // if the VKey has multiple VK_codes then SHIFT distinguishes between them // In that case SHIFT is not pressed // On the next event the shift flag will be restored based on modifiers if Pressed and ((VKey.Flags and VKEY_FLAG_MULTI_VK) <> 0) then begin UpdateList(VK_SHIFT, False); UpdateList(VK_LSHIFT, False); UpdateList(VK_RSHIFT, False); end; end; function gtkYearChanged(Widget: PGtkWidget; data: gPointer) : GBoolean; cdecl; var MSG: TLMessage; begin Result := CallBackDefaultReturn; if (Widget=nil) then ; EventTrace('year changed', data); MSG.Msg := LM_YEARCHANGED; DeliverPostMessage(Data, MSG); end; {------------------------------------------------------------------------------ ClipboardSelectionReceivedHandler copy the received selection data record and buffer to internal record and buffer (ClipboardSelectionData) ------------------------------------------------------------------------------} procedure ClipboardSelectionReceivedHandler(TargetWidget: PGtkWidget; SelectionData: PGtkSelectionData; TimeID: cardinal; Data: Pointer); cdecl; var TempBuf: Pointer; c: PClipboardEventData; i: integer; begin // at any time there can be several requests // find the request with the correct TimeID if (Data=nil) or (TargetWidget=nil) then ; i:=ClipboardSelectionData.Count-1; while (i>=0) do begin c:=PClipboardEventData(ClipboardSelectionData[i]); if c^.TimeID=TimeID then break; dec(i); end; {$IFDEF DEBUG_CLIPBOARD} DebugLn('[ClipboardSelectionReceivedHandler] A TimeID=',TimeID,' RequestIndex=',i, ' selection=',SelectionData^.selection, ' target=',SelectionData^.Target, ' theType=',SelectionData^.theType, ' format=',SelectionData^.format, ' len=',SelectionData^.length ); {$ENDIF} if i<0 then exit; // free old data if (c^.Data.Data<>nil) then FreeMem(c^.Data.Data); // copy the information c^.Data:=SelectionData^; // copy the raw data to an internal buffer (the gtk buffer will be destroyed // right after this event) {$IFDEF DEBUG_CLIPBOARD} DebugLn('[ClipboardSelectionReceivedHandler] B DataLen=',c^.Data.Length); {$ENDIF} if (c^.Data.Data<>nil) and (c^.Data.Length>0) then begin GetMem(TempBuf,c^.Data.Length); Move(c^.Data.Data^,TempBuf^,c^.Data.Length); c^.Data.Data:=TempBuf; {$IFDEF DEBUG_CLIPBOARD} DebugLn('[ClipboardSelectionReceivedHandler] C FirstCharacter=',ord(PChar(c^.Data.Data)[0])); {$ENDIF} end else c^.Data.Data:=nil; end; {------------------------------------------------------------------------------ ClipboardSelectionRequestHandler This signal is emitted if someone requests the clipboard data. Since the lcl clipboard caches all requests this will typically be another application. ------------------------------------------------------------------------------} procedure ClipboardSelectionRequestHandler(TargetWidget: PGtkWidget; SelectionData: PGtkSelectionData; Info: cardinal; TimeID: cardinal; Data: Pointer); cdecl; var ClipboardType: TClipboardType; MemStream: TMemoryStream; FormatID: cardinal; Buffer: Pointer; BufLength: integer; P: PChar; BitCount: integer; begin {$IFDEF DEBUG_CLIPBOARD} DebugLn('*** [ClipboardSelectionRequestHandler] START'); {$ENDIF} if (Data=nil) or (TimeID=0) or (Info=0) or (TargetWidget=nil) then ; if SelectionData^.Target=0 then exit; for ClipboardType:=Low(TClipboardType) to High(TClipboardType) do if SelectionData^.Selection=ClipboardTypeAtoms[ClipboardType] then begin if Assigned(ClipboardHandler[ClipboardType]) then begin // handler found for this of clipboard // now create a stream and find a supported format {$IFDEF DEBUG_CLIPBOARD} p:=gdk_atom_name(SelectionData^.Target); DebugLn('[ClipboardSelectionRequestHandler] ',ClipboardTypeName[ClipboardType],' Format=',p,' ID=',SelectionData^.Target); g_free(p); {$ENDIF} MemStream:=TMemoryStream.Create; try // the gtk-interface provides automatically some formats, that the lcl // does not know. Wrapping them to lcl formats ... FormatID:=SelectionData^.Target; if ((FormatID=gdk_atom_intern('COMPOUND_TEXT',GdkTrue)) and (ClipboardExtraGtkFormats[ClipboardType][gfCOMPOUND_TEXT])) or ((FormatID=gdk_atom_intern('STRING',GdkTrue)) and (ClipboardExtraGtkFormats[ClipboardType][gfSTRING])) or ((FormatID=gdk_atom_intern('TEXT',GdkTrue)) and (ClipboardExtraGtkFormats[ClipboardType][gfTEXT])) then FormatID:=gdk_atom_intern('text/plain',GdkFalse); {$IFDEF DEBUG_CLIPBOARD} DebugLn('[ClipboardSelectionRequestHandler] FormatID=',FormatID,' CompoundText=',gdk_atom_intern('COMPOUND_TEXT',1),' ',ClipboardExtraGtkFormats[ClipboardType][gfCOMPOUND_TEXT]); {$ENDIF} // get the requested data by calling the handler for this clipboard type ClipboardHandler[ClipboardType](FormatID,MemStream); MemStream.Position:=0; // build clipboard data for gtk Buffer:=nil; BufLength:=0; BitCount:=8; // if the format was wrapped, transform it back if (FormatID=gdk_atom_intern('text/plain',GdkTrue)) then begin if (SelectionData^.Target=gdk_atom_intern('COMPOUND_TEXT',GdkTrue)) then begin // transform text/plain to COMPOUND_TEXT BufLength:=integer(MemStream.Size); P:=StrAlloc(BufLength+1); MemStream.Read(P^,BufLength); P[BufLength]:=#0; BufLength:=0; gdk_string_to_compound_text(P, @SelectionData^.{$ifdef GTK2}_Type{$ELSE}theType{$ENDIF}, @SelectionData^.Format,@Buffer,@BufLength); StrDispose(P); gtk_selection_data_set(SelectionData,SelectionData^.Target, SelectionData^.Format,Buffer,BufLength); gdk_free_compound_text(Buffer); exit; end; end; if Buffer=nil then begin {$IFDEF DEBUG_CLIPBOARD} DebugLn('[ClipboardSelectionRequestHandler] Default MemStream.Size=',MemStream.Size); {$ENDIF} BufLength:=integer(MemStream.Size); if BufLength>0 then begin GetMem(Buffer,BufLength); MemStream.Read(Buffer^,BufLength); {SetLength(s,MemStream.Size); MemStream.Position:=0; MemStream.Read(s[1],MemStream.Size); DebugLn(' >>> "',s,'"');} end; end; {$IFDEF DEBUG_CLIPBOARD} DebugLn('[ClipboardSelectionRequestHandler] Len=',BufLength); {$ENDIF} gtk_selection_data_set(SelectionData,SelectionData^.Target,BitCount, Buffer,BufLength); if Buffer<>nil then FreeMem(Buffer); finally MemStream.Free; end; end; break; end; end; {------------------------------------------------------------------------------ ClipboardSelectionLostOwnershipHandler This signal is emitted if another application gets the clipboard ownership. ------------------------------------------------------------------------------} function ClipboardSelectionLostOwnershipHandler(TargetWidget: PGtkWidget; EventSelection: PGdkEventSelection; Data: Pointer): cardinal; cdecl; var ClipboardType: TClipboardType; begin if (Data=nil) or (TargetWidget=nil) then ; //DebugLn('*** [ClipboardSelectionLostOwnershipHandler] ',hexstr(cardinal(targetwidget),8)); for ClipboardType:=Low(TClipboardType) to High(TClipboardType) do if EventSelection^.Selection=ClipboardTypeAtoms[ClipboardType] then begin {$IFDEF DEBUG_CLIPBOARD} DebugLn('*** [ClipboardSelectionLostOwnershipHandler] ',ClipboardTypeName[ClipboardType]); {$ENDIF} if (ClipboardWidget<>nil) and (gdk_selection_owner_get(ClipboardTypeAtoms[ClipboardType]) <> GetControlWindow(ClipboardWidget)) and Assigned(ClipboardHandler[ClipboardType]) then begin // handler found for this type of clipboard {$IFDEF DEBUG_CLIPBOARD} DebugLn('[ClipboardSelectionLostOwnershipHandler] ',ClipboardTypeName[ClipboardType]); {$ENDIF} ClipboardHandler[ClipboardType](0,nil); ClipboardHandler[ClipboardType]:=nil; end; break; end; Result:=1; end; {------------------------------------------------------------------------------- Procedure GTKStyleChanged(Widget: PGtkWidget; previous_style : PGTKStyle; Data: Pointer); cdecl; Handler for style changes. For example the user changes the theme. But also called on every widget realize, so it should not release all styles everytime. -------------------------------------------------------------------------------} Procedure GTKStyleChanged(Widget: PGtkWidget; previous_style : PGTKStyle; Data: Pointer); cdecl; begin if (Widget=nil) or (Data=nil) or (previous_style=nil) then ; EventTrace('style-set', nil); //ReleaseAllStyles; end; function gtkListBoxSelectionChangedCB(widget: PGtkWidget; data: gPointer ): GBoolean; cdecl; var Mess: TLMessage; begin //debugln('gtkListBoxSelectionChangedCB ',GetWidgetDebugReport(Widget)); Result := CallBackDefaultReturn; EventTrace('selection_changed', data); FillChar(Mess,SizeOf(Mess),0); Mess.msg := LM_SelChange; DeliverMessage(Data, Mess); end; {$I gtkDragCallback.inc} {$I gtkListViewCallback.inc} {$I gtkComboBoxCallback.inc} {$I gtkPageCallback.inc} {$IFDEF ASSERT_IS_ON} {$UNDEF ASSERT_IS_ON} {$C-} {$ENDIF} { ============================================================================= $Log$ Revision 1.258 2005/01/11 19:01:51 mattias fixed adding main file in gtk filediaog twice Revision 1.257 2005/01/07 01:31:44 mattias implemented TCheckBox.State=cbGrayed for gtk intf without visual representation Revision 1.256 2005/01/05 14:46:35 mattias started anchor editor and moved OverWritePromp from lcl to gtk intf Revision 1.255 2005/01/01 18:56:47 mattias implemented TTIProgressBar Revision 1.254 2004/12/21 22:49:29 mattias implemented scrollbar codes for gtk intf from Jesus Revision 1.253 2004/12/11 02:21:00 mattias fixed showing all keymappings and missing text selection keys Revision 1.252 2004/12/10 19:22:28 mattias implemented auto add on double click on component palette Revision 1.251 2004/11/10 18:23:56 mattias impementing changing a TLabel.Font properties Size, Height, Name, Style - set only at Handle creation time Revision 1.250 2004/11/03 14:18:36 mattias implemented preferred size for controls for theme depending AutoSizing Revision 1.249 2004/10/15 12:04:09 mattias calling updating notebook tab after realize, needed for close btns Revision 1.248 2004/09/21 10:05:26 mattias fixed disable at designtime and bounding TCustomProgressBar position Revision 1.247 2004/09/10 16:28:50 mattias implemented very rudimentary TTabControl Revision 1.246 2004/08/28 10:22:13 mattias added hints for long props in OI from Andrew Haines Revision 1.245 2004/08/18 20:49:02 mattias simple forms can now be child controls Revision 1.244 2004/08/17 19:01:36 mattias gtk intf now ignores size notifications of unrealized widgets Revision 1.243 2004/08/04 10:51:13 mazen * fix left mouse button click reaction Revision 1.242 2004/08/03 17:18:15 mazen * fix right mouse button down event Revision 1.241 2004/07/30 14:26:11 mazen * move HandleGtkKeyUpDown to gtkProc.inc make it visible to gtk2 this allow saving a call in a hevely called callback Revision 1.240 2004/07/16 21:49:00 mattias added RTTI controls Revision 1.239 2004/07/15 10:43:38 mattias added TCustomButton, TCustomBitBtn, TCustomSpeedButton Revision 1.238 2004/07/10 18:17:30 mattias added Delphi ToDo support, Application.WndProc, small bugfixes from Colin Revision 1.237 2004/07/07 17:10:02 mattias added hint for unimplemented IDE directives for non pascal sources Revision 1.236 2004/06/29 21:25:52 marc * Fixed compilation for gtk2 Revision 1.235 2004/06/28 15:45:48 mattias fixed a mem violation in gtk intf paint msg conversion Revision 1.234 2004/06/24 20:49:10 marc * Applied patch from Ido Revision 1.233 2004/05/30 14:02:30 mattias implemented OnChange for TRadioButton, TCheckBox, TToggleBox and some more docking stuff Revision 1.232 2004/05/22 14:35:32 mattias fixed button return key Revision 1.231 2004/05/14 12:53:25 mattias improved grids e.g. OnPrepareCanvas patch from Jesus Revision 1.230 2004/05/11 11:42:27 mattias replaced writeln by debugln Revision 1.229 2004/05/11 09:49:46 mattias started sending CN_KEYUP Revision 1.228 2004/04/23 11:18:28 mattias fixed unsetting csFocusing Revision 1.227 2004/04/19 10:06:56 mattias fixed illegal ancestor search Revision 1.226 2004/04/11 18:58:25 micha fix (lm_)setcursor changes for gtk target Revision 1.225 2004/04/09 11:25:20 mattias changed OnKeyPress keys are not delegated back to the gtk Revision 1.224 2004/04/02 14:28:44 vincents Fixed compilation with -dVerboseFocus Revision 1.223 2004/03/22 19:10:04 mattias implemented icons for TPage in gtk, mask for TCustomImageList Revision 1.222 2004/03/18 00:55:56 mattias fixed memleak in gtk opendlg Revision 1.221 2004/02/23 18:24:38 mattias completed new TToolBar Revision 1.220 2004/02/22 15:39:44 mattias fixed error handling on saving lpi file Revision 1.219 2004/02/13 15:49:54 mattias started advanced LCL auto sizing Revision 1.218 2004/02/07 18:04:14 mattias fixed grids OnDrawCells Revision 1.217 2004/02/02 15:46:19 mattias implemented basic TSplitter, still many ToDos Revision 1.216 2004/01/27 21:32:11 mattias improved changing style of controls Revision 1.215 2004/01/23 13:55:30 mattias style widgets are now realized, so all values are initialized Revision 1.214 2004/01/22 11:23:36 mattias started MaskBlt for gtkIF and applied patch for dir dlg in env opts from Vincent Revision 1.213 2004/01/14 20:09:49 mattias added TColorDialog debugging Revision 1.212 2004/01/13 10:41:40 mattias fixed statusbar updating all panels Revision 1.211 2004/01/09 20:03:13 mattias implemented new statusbar methods in gtk intf Revision 1.210 2004/01/09 13:49:43 mattias improved gtk intf key fetching and OI keyboard navigation Revision 1.209 2003/12/25 14:17:07 mattias fixed many range check warnings Revision 1.208 2003/12/21 15:36:47 mattias workaround for inherited bug in fpc 1.9 Revision 1.207 2003/12/21 13:58:06 mattias renamed DirectoryExists to DirPathExists to reduce ambigiousity Revision 1.206 2003/11/26 21:30:19 mattias reduced unit circles, fixed fpImage streaming Revision 1.205 2003/11/25 08:59:01 mattias fixed a few more black colors Revision 1.204 2003/10/19 16:33:10 marc * Fixed VKey keypad handling Revision 1.203 2003/10/17 03:21:21 ajgenius fix GTK2 compiling for new Keyboard changes Revision 1.202 2003/10/16 23:54:27 marc Implemented new gtk keyevent handling Revision 1.201 2003/10/03 01:25:01 ajgenius add more gtk1i<->gtk2 key & event wrappers, move more GTK2 workarounds from gtk to gtk2 interface, start GTK2 interface SetCallback Revision 1.200 2003/10/02 03:35:29 ajgenius more fixes for GTK2, synedit now mostly-useable Revision 1.199 2003/10/02 01:18:38 ajgenius more callbacks fixes for gtk2, partly fix gtk2 CheckListBox Revision 1.198 2003/10/01 20:51:09 ajgenius partly fix focus callbacks for GTK2 Revision 1.197 2003/10/01 15:57:37 ajgenius undo accidental mouse callback changes, partly fix key events for gtk2 Revision 1.196 2003/09/26 00:24:22 ajgenius partly cleanup gtk2 $ifdef's Revision 1.195 2003/09/25 16:02:16 ajgenius try to catch GDK/X drawable errors and raise an AV to stop killing App Revision 1.194 2003/09/23 17:52:04 mattias added SetAnchors Revision 1.193 2003/09/20 13:27:49 mattias varois improvements for ParentColor from Micha Revision 1.192 2003/09/19 00:41:51 ajgenius remove USE_PANGO define since pango now apears to work properly. Revision 1.191 2003/09/17 15:26:41 mattias fixed removing TCustomPage Revision 1.190 2003/09/13 16:43:01 mattias fixed PerformTab call Revision 1.189 2003/09/12 17:40:45 ajgenius fixes for GTK2(accel groups, menu accel, 'draw'), more work toward Pango(DrawText now works, UpdateDCTextMetric mostly works) Revision 1.188 2003/09/10 02:33:41 ajgenius fixed TColotDialog for GTK2 Revision 1.187 2003/09/09 04:15:08 ajgenius more updates for GTK2, more GTK1 wrappers, removal of more ifdef's, partly fixed signals Revision 1.186 2003/09/02 21:32:56 mattias implemented TOpenPictureDialog Revision 1.185 2003/08/30 18:53:07 mattias using default colors, when theme does not define them Revision 1.184 2003/08/29 21:21:07 mattias fixes for gtk2 Revision 1.183 2003/07/21 23:43:32 marc * Fixed radiogroup menuitems Revision 1.182 2002/08/17 23:41:34 mattias many clipping fixes Revision 1.181 2003/06/13 14:26:17 ajgenius some fixes toward gtk2 Revision 1.180 2003/06/10 17:23:35 mattias implemented tabstop Revision 1.179 2003/06/09 14:39:52 mattias implemented setting working directory for debugger Revision 1.178 2003/04/26 10:45:34 mattias fixed right control release Revision 1.177 2003/04/16 22:11:35 mattias fixed codetools Makefile, fixed default prop not found error Revision 1.176 2003/04/16 17:20:24 mattias implemented package check broken dependency on compile Revision 1.175 2003/04/10 09:22:42 mattias implemented changing dependency version Revision 1.174 2003/03/26 00:21:25 mattias implemented build lazarus extra options -d Revision 1.173 2003/03/25 13:00:39 mattias implemented TMemo.SelLength, improved OI hints Revision 1.172 2003/03/25 10:45:41 mattias reduced focus handling and improved focus setting Revision 1.171 2003/03/18 13:04:25 mattias improved focus debugging output Revision 1.170 2003/03/16 09:41:06 mattias fixed checking menuitems Revision 1.169 2003/03/09 21:13:32 mattias localized gtk interface Revision 1.168 2003/03/09 17:44:12 mattias finshed Make Resourcestring dialog and implemented TToggleBox Revision 1.167 2003/02/18 22:56:23 mattias fixed key grabbing Revision 1.166 2003/02/04 11:44:13 mattias fixed modified and loading xpms for button glyphs Revision 1.165 2003/02/03 22:28:08 mattias small bugfixes and fixed non checked menu items activate Revision 1.164 2003/01/27 13:49:16 mattias reduced speedbutton invalidates, added TCanvas.Frame Revision 1.163 2003/01/24 11:58:00 mattias fixed clipboard waiting and kwrite targets Revision 1.162 2003/01/06 10:51:41 mattias freeing stopped external tools Revision 1.161 2002/12/28 12:42:38 mattias focus fixes, reduced lpi size Revision 1.160 2002/11/23 13:48:44 mattias added Timer patch from Vincent Snijders Revision 1.159 2002/11/21 18:49:53 mattias started OnMouseEnter and OnMouseLeave Revision 1.158 2002/11/16 11:22:57 mbukovjan Fixes to MaxLength. TCustomMemo now has MaxLength, too. Revision 1.157 2002/11/05 20:03:42 lazarus MG: implemented hints Revision 1.156 2002/11/02 22:25:36 lazarus MG: implemented TMethodList and Application Idle handlers Revision 1.155 2002/10/23 15:59:25 lazarus MG: fixed radiobutton mousedown after Revision 1.154 2002/10/22 12:12:08 lazarus MG: accelerators are now shared between non modal forms Revision 1.153 2002/10/21 22:12:47 lazarus MG: fixed frmactivate Revision 1.152 2002/10/20 21:49:09 lazarus MG: fixes for fpc1.1 Revision 1.151 2002/10/20 19:03:56 lazarus AJ: minor fixes for FPC 1.1 Revision 1.150 2002/10/17 21:00:17 lazarus MG: fixed uncapturing of mouse Revision 1.149 2002/10/17 15:09:31 lazarus MG: made mouse capturing more strict Revision 1.148 2002/10/15 16:01:36 lazarus MG: fixed timers Revision 1.147 2002/10/15 07:01:29 lazarus MG: fixed timer checking Revision 1.146 2002/10/14 19:00:49 lazarus MG: fixed zombie timers Revision 1.145 2002/10/11 07:28:03 lazarus MG: gtk interface now sends keyboard events via DeliverMessage Revision 1.144 2002/10/10 08:51:13 lazarus MG: added paint messages for some gtk internal widgets Revision 1.143 2002/10/09 10:22:54 lazarus MG: fixed client origin coordinates Revision 1.142 2002/10/07 07:00:03 lazarus MG: fixed stopping keypress event if handled by LCL Revision 1.141 2002/10/06 20:24:27 lazarus MG: fixed stopping keypress event if handled by LCL Revision 1.140 2002/10/04 20:46:52 lazarus MG: improved TComboBox.SetItemIndex Revision 1.139 2002/10/04 16:38:15 lazarus MG: no OnChange event when app sets Text of TComboBox Revision 1.138 2002/10/03 14:47:31 lazarus MG: added TComboBox.OnPopup+OnCloseUp+ItemWidth Revision 1.137 2002/09/30 22:42:54 lazarus MG: deactivated transient modal forms Revision 1.136 2002/09/30 20:37:09 lazarus MG: fixed transient of modal forms Revision 1.135 2002/09/30 20:19:12 lazarus MG: fixed flickering of modal forms Revision 1.134 2002/09/30 09:26:42 lazarus MG: added DoSaveAll before CloseAll Revision 1.133 2002/09/29 15:08:39 lazarus MWE: Applied patch from "Andrew Johnson" Patch includes: -fixes Problems with hiding modal forms -temporarily fixes TCustomForm.BorderStyle in bsNone -temporarily fixes problems with improper tabbing in TSynEdit Revision 1.132 2002/09/27 20:52:23 lazarus MWE: Applied patch from "Andrew Johnson" Here is the run down of what it includes - -Vasily Volchenko's Updated Russian Localizations -improvements to GTK Styles/SysColors -initial GTK Palette code - (untested, and for now useless) -Hint Windows and Modal dialogs now try to stay transient to the main program form, aka they stay on top of the main form and usually minimize/maximize with it. -fixes to Form BorderStyle code(tool windows needed a border) -fixes DrawFrameControl DFCS_BUTTONPUSH to match Win32 better when flat -fixes DrawFrameControl DFCS_BUTTONCHECK to match Win32 better and to match GTK theme better. It works most of the time now, but some themes, noteably Default, don't work. -fixes bug in Bitmap code which broke compiling in NoGDKPixbuf mode. -misc other cleanups/ fixes in gtk interface -speedbutton's should now draw correctly when flat in Win32 -I have included an experimental new CheckBox(disabled by default) which has initial support for cbGrayed(Tri-State), and WordWrap, and misc other improvements. It is not done, it is mostly a quick hack to test DrawFrameControl DFCS_BUTTONCHECK, however it offers many improvements which can be seen in cbsCheck/cbsCrissCross (aka non-themed) state. -fixes Message Dialogs to more accurately determine button Spacing/Size, and Label Spacing/Size based on current System font. -fixes MessageDlgPos, & ShowMessagePos in Dialogs -adds InputQuery & InputBox to Dialogs -re-arranges & somewhat re-designs Control Tabbing, it now partially works - wrapping around doesn't work, and subcontrols(Panels & Children, etc) don't work. TabOrder now works to an extent. I am not sure what is wrong with my code, based on my other tests at least wrapping and TabOrder SHOULD work properly, but.. Anyone want to try and fix? -SynEdit(Code Editor) now changes mouse cursor to match position(aka over scrollbar/gutter vs over text edit) -adds a TRegion property to Graphics.pp, and Canvas. Once I figure out how to handle complex regions(aka polygons) data properly I will add Region functions to the canvas itself (SetClipRect, intersectClipRect etc.) -BitBtn now has a Stored flag on Glyph so it doesn't store to lfm/lrs if Glyph is Empty, or if Glyph is not bkCustom(aka bkOk, bkCancel, etc.) This should fix most crashes with older GDKPixbuf libs. Revision 1.131 2002/09/18 17:07:27 lazarus MG: added patch from Andrew Revision 1.130 2002/09/16 15:56:01 lazarus Resize cursors in designer. Revision 1.129 2002/09/16 14:46:08 lazarus MG: renamed designerstr.pas to objinspstrconsts.pas Revision 1.128 2002/09/16 08:54:03 lazarus MG: gtk mlouse events can now be fetched before or after Revision 1.127 2002/09/10 06:49:19 lazarus MG: scrollingwincontrol from Andrew Revision 1.126 2002/09/09 17:41:19 lazarus MG: added multiselection to TTreeView Revision 1.125 2002/09/07 12:14:50 lazarus EchoMode for TCustomEdit. emNone not implemented for GTK+, falls back to emPassword behaviour. Revision 1.124 2002/09/06 19:45:10 lazarus Cleanups plus a fix to TPanel parent/drawing problem. Revision 1.123 2002/09/06 15:57:34 lazarus MG: fixed notebook client area, send messages and minor bugs Revision 1.122 2002/09/05 12:11:43 lazarus MG: TNotebook is now streamable Revision 1.121 2002/09/05 10:12:07 lazarus New dialog for multiline caption of TCustomLabel. Prettified TStrings property editor. Memo now has automatic scrollbars (not fully working), WordWrap and Scrollbars property Removed saving of old combo text (it broke things and is not needed). Cleanups. Revision 1.120 2002/09/03 08:07:20 lazarus MG: image support, TScrollBox, and many other things from Andrew Revision 1.119 2002/09/01 16:11:22 lazarus MG: double, triple and quad clicks now works Revision 1.118 2002/08/31 11:37:10 lazarus MG: fixed destroying combobox Revision 1.117 2002/08/31 07:58:21 lazarus MG: fixed resetting comobobox text Revision 1.116 2002/08/30 12:32:22 lazarus MG: MoveWindowOrgEx, Splitted FWinControls/FControls, TControl drawing, Better DesignerDrawing, ... Revision 1.115 2002/08/29 00:07:02 lazarus MG: fixed TComboBox and InvalidateControl Revision 1.114 2002/08/28 09:40:49 lazarus MG: reduced paint messages and DC getting/releasing Revision 1.113 2002/08/27 18:45:13 lazarus MG: propedits text improvements from Andrew, uncapturing, improved comobobox Revision 1.112 2002/08/25 13:45:58 lazarus MG: ignoring double clicks for components that dont want them Revision 1.111 2002/08/24 12:54:59 lazarus MG: fixed mouse capturing, OI edit focus Revision 1.110 2002/08/24 08:07:14 lazarus MG: fixed double click recognition Revision 1.109 2002/08/24 07:11:56 lazarus MG: reduced output Revision 1.108 2002/08/24 07:09:04 lazarus MG: fixed bracket hilighting Revision 1.107 2002/08/24 06:51:22 lazarus MG: from Andrew: style list fixes, autosize for radio/checkbtns Revision 1.106 2002/08/22 16:43:35 lazarus MG: improved theme support from Andrew Revision 1.105 2002/08/22 13:45:58 lazarus MG: fixed non AutoCheck menuitems and editor bookmark popupmenu Revision 1.104 2002/08/22 12:30:36 lazarus MG: fixed key events and changed pixmap loading Revision 1.103 2002/08/22 12:25:00 lazarus MG: fixed mouse events Revision 1.102 2002/08/22 12:13:01 lazarus MG: changed user input events from queue to direct Revision 1.101 2002/08/19 18:00:02 lazarus MG: design signals for gtk internal widgets Revision 1.100 2002/08/19 08:50:28 lazarus MG: fixed parser for Clx enums and empty param lists Revision 1.99 2002/08/17 15:45:33 lazarus MG: removed ClientRectBugfix defines Revision 1.98 2002/08/17 07:57:05 lazarus MG: added TPopupMenu.OnPopup and SourceEditor PopupMenu checks Revision 1.97 2002/08/15 13:37:57 lazarus MG: started menuitem icon, checked, radio and groupindex Revision 1.96 2002/08/04 08:17:30 lazarus MG: fixed normal events in design mode Revision 1.95 2002/08/04 07:44:44 lazarus MG: fixed xml reading writing of special chars Revision 1.94 2002/08/04 07:09:27 lazarus MG: fixed client events Revision 1.93 2002/07/29 13:26:57 lazarus MG: source notebook pagenames are now updated more often Revision 1.92 2002/07/23 07:40:51 lazarus MG: fixed get widget position for inherited gdkwindows Revision 1.91 2002/07/22 18:25:12 lazarus MG: reduced output Revision 1.90 2002/07/20 13:47:03 lazarus MG: fixed eventmask for realized windows Revision 1.89 2002/06/26 16:15:56 lazarus MG: fixed missing declaration Revision 1.88 2002/06/26 15:11:09 lazarus MG: added new tool: Guess misplaced $IFDEF/$ENDIF Revision 1.87 2002/06/21 17:54:23 lazarus MG: in design mode the mouse cursor is now also set for hidden gdkwindows Revision 1.86 2002/06/21 16:59:15 lazarus MG: TControl.Cursor is now set, reduced auto reaction of widgets in design mode Revision 1.85 2002/06/19 19:46:09 lazarus MG: Form Editing: snapping, guidelines, modified on move/resize, creating components in csDesigning, ... Revision 1.84 2002/06/11 13:41:09 lazarus MG: fixed mouse coords and fixed mouse clicked thru bug Revision 1.83 2002/06/09 14:00:41 lazarus MG: fixed persistent caret and implemented Form.BorderStyle=bsNone Revision 1.82 2002/06/09 07:08:43 lazarus MG: fixed window jumping Revision 1.81 2002/06/08 17:16:02 lazarus MG: added close buttons and images to TNoteBook and close buttons to source editor Revision 1.80 2002/06/06 14:41:29 lazarus MG: if completion form visible it will now get all synedit keys Revision 1.79 2002/06/05 12:33:57 lazarus MG: fixed fonts in XLFD format and styles Revision 1.78 2002/06/04 15:17:22 lazarus MG: improved TFont for XLFD font names Revision 1.77 2002/05/30 14:11:12 lazarus MG: added filters and history to TOpenDialog Revision 1.76 2002/05/29 21:44:38 lazarus MG: improved TCommon/File/OpenDialog, fixed TListView scrolling and broder Revision 1.75 2002/05/24 07:16:32 lazarus MG: started mouse bugfix and completed Makefile.fpc Revision 1.74 2002/05/13 14:47:01 lazarus MG: fixed client rectangles, TRadioGroup, RecreateWnd Revision 1.73 2002/05/10 06:05:56 lazarus MG: changed license to LGPL Revision 1.72 2002/05/09 12:41:29 lazarus MG: further clientrect bugfixes Revision 1.71 2002/05/06 08:50:36 lazarus MG: replaced logo, increased version to 0.8.3a and some clientrectbugfix Revision 1.70 2002/04/27 15:35:51 lazarus MG: fixed window shrinking Revision 1.69 2002/04/22 13:07:45 lazarus MG: fixed AdjustClientRect of TGroupBox Revision 1.68 2002/04/04 12:25:02 lazarus MG: changed except statements to more verbosity Revision 1.67 2002/03/31 22:01:37 lazarus MG: fixed unreleased/unpressed Ctrl/Alt/Shift Revision 1.66 2002/03/29 19:11:38 lazarus Added Triple Click Shane Revision 1.65 2002/03/27 00:33:54 lazarus MWE: * Cleanup in lmessages * Added Listview selection and notification events + introduced commctrl Revision 1.64 2002/03/25 17:59:20 lazarus GTK Cleanup Shane Revision 1.63 2002/03/23 19:05:52 lazarus MG: pascal lowercase for open new unit Revision 1.62 2002/03/16 21:40:55 lazarus MG: reduced size+move messages between lcl and interface Revision 1.61 2002/03/14 18:12:46 lazarus Mouse events fixes. Revision 1.60 2002/03/13 22:48:16 lazarus Constraints implementation (first cut) and sizig - moving system rework to better match Delphi/Kylix way of doing things (the existing implementation worked by acident IMHO :-) Revision 1.59 2002/01/01 18:38:36 lazarus MG: more wmsize messages :( Revision 1.58 2002/01/01 15:50:16 lazarus MG: fixed initial component aligning Revision 1.57 2001/12/28 15:12:02 lazarus MG: LM_SIZE and LM_MOVE messages are now send directly, not queued Revision 1.56 2001/12/17 11:09:48 lazarus MG: fixed typed but not selected filename in TOpenDialog Revision 1.55 2001/12/12 20:45:30 lazarus MG: added non existing filename to multiselection in TOpenDialog Revision 1.54 2001/12/12 20:19:19 lazarus Modified the the GTKFileSelection so that it will handle and use CTRL and SHIFT keys in a fashion similar to Windows. Revision 1.53 2001/12/12 15:12:31 lazarus MG: added file path to files in TOpenDialog Revision 1.52 2001/12/12 08:29:21 lazarus Add code to allow TOpenDialog to do multiple line selects. MAH Revision 1.51 2001/12/07 20:12:15 lazarus Added a watch dialog. Shane Revision 1.50 2001/12/05 18:23:47 lazarus Added events to Calendar Shane Revision 1.49 2001/11/30 16:41:59 lazarus Improved hints with overlapping windows. Shane Revision 1.48 2001/11/29 18:41:27 lazarus Improved the double click. Shane Revision 1.47 2001/11/21 19:32:32 lazarus TComboBox can now be moved in FormEditor Shane Revision 1.46 2001/11/21 14:55:31 lazarus Changes for combobox to receive butondown and up events DblClick events now working. Shane Revision 1.45 2001/11/20 17:20:45 lazarus Fixed designer problem with moving controls. Can no longer drag controls off the form. Shane Revision 1.44 2001/11/14 17:46:58 lazarus Changes to make toggling between form and unit work. Added BringWindowToTop Shane Revision 1.43 2001/11/12 19:32:23 lazarus MG: fixed empty clipboard stream crashing bug Revision 1.42 2001/11/12 16:56:08 lazarus MG: CLIPBOARD Revision 1.41 2001/11/09 19:14:24 lazarus HintWindow changes Shane Revision 1.40 2001/11/01 21:30:35 lazarus Changes to Messagebox. Added line to CodeTools to prevent duplicate USES entries. Revision 1.39 2001/10/31 16:29:22 lazarus Fixed the gtk mousemove bug where the control gets the coord's based on it's parent instead of itself. Shane Revision 1.38 2001/10/16 14:19:13 lazarus MG: added nvidia opengl support and a new opengl example from satan Revision 1.35 2001/10/09 09:46:58 lazarus MG: added codetools, fixed synedit unindent, fixed MCatureHandle Revision 1.34 2001/10/03 21:03:02 lazarus MG: reduced repaints Revision 1.32 2001/06/16 09:14:38 lazarus MG: added lazqueue and used it for the messagequeue Revision 1.31 2001/06/14 14:57:59 lazarus MG: small bugfixes and less notes Revision 1.30 2001/04/06 22:25:14 lazarus * TTimer uses winapi-interface now instead of sendmessage-interface, stoppok Revision 1.29 2001/03/27 11:11:13 lazarus MG: fixed mouse msg, added filedialog initialdir Revision 1.28 2001/03/26 14:58:31 lazarus MG: setwindowpos + bugfixes Revision 1.26 2001/03/19 14:44:22 lazarus MG: fixed many unreleased DC and GDIObj bugs Revision 1.23 2001/02/28 13:17:34 lazarus Added some debug code for the top,left reporting problem. Shane Revision 1.22 2001/02/20 16:53:27 lazarus Changes for wordcompletion and many other things from Mattias. Shane Revision 1.21 2001/01/31 21:16:45 lazarus Changed to TCOmboBox focusing. Shane Revision 1.20 2001/01/30 18:15:02 lazarus Added code for TStatusBar I'm now capturing WMPainT and doing the drawing myself. Shane Revision 1.19 2001/01/28 03:51:42 lazarus Fixed the problem with Changed for ComboBoxs Shane Revision 1.18 2001/01/24 23:26:40 lazarus MWE: = moved some types to gtkdef + added WinWidgetInfo + added some initialization to Application.Create Revision 1.17 2001/01/24 03:21:03 lazarus Removed gtkDrawDefualt signal function from gtkcallback.inc It was no longer used. Shane Revision 1.16 2001/01/23 23:33:55 lazarus MWE: - Removed old LM_InvalidateRect - did some cleanup in old code + added some comments on gtkobject data (gtkproc) Revision 1.15 2001/01/12 18:10:54 lazarus Changes for keyevents in the editor. Shane Revision 1.14 2001/01/11 20:16:47 lazarus Added some TImageList code. Added a bookmark resource with 10 resource images. Removed some of the IFDEF's in mwCustomEdit around the inherited code. Shane Revision 1.13 2001/01/10 23:53:30 lazarus MWE: ~ minor change Revision 1.12 2001/01/10 20:12:29 lazarus Added the Nudge feature to the IDE. Shane Revision 1.11 2001/01/09 18:23:21 lazarus Worked on moving controls. It's just not working with the X and Y coord's I'm getting. Shane Revision 1.10 2001/01/04 15:09:05 lazarus Tested TCustomEdit.Readonly, MaxLength and CharCase. Shane Revision 1.9 2000/12/19 18:43:13 lazarus Removed IDEEDITOR. This causes the PROJECT class to not function. Saving projects no longer works. I added TSourceNotebook and TSourceEditor. They do all the work for saving/closing/opening units. Somethings work but they are in early development. Shane Revision 1.8 2000/11/29 21:22:35 lazarus New Object Inspector code Shane Revision 1.7 2000/10/09 22:50:32 lazarus MWE: * fixed some selection code + Added selection sample Revision 1.6 2000/09/10 23:08:31 lazarus MWE: + Added CreateCompatibeleBitamp function + Updated TWinControl.WMPaint + Added some checks to avoid gtk/gdk errors - Removed no fixed warning from GetDC - Removed some output Revision 1.5 2000/08/28 14:23:49 lazarus Added a few files for the start of creating classes for the editor. [SHANE] Revision 1.4 2000/08/11 14:59:09 lazarus Adding all the Synedit files. Changed the GDK_KEY_PRESS and GDK_KEY_RELEASE stuff to fix the problem in the editor with the shift key being ignored. Shane Revision 1.3 2000/08/10 10:55:45 lazarus Changed TCustomDialog to TCommonDialog Shane Revision 1.2 2000/07/30 21:48:33 lazarus MWE: = Moved ObjectToGTKObject to GTKProc unit * Fixed array checking in LoadPixmap = Moved LM_SETENABLED to API func EnableWindow and EnableMenuItem ~ Some cleanup Revision 1.1 2000/07/13 10:28:29 michael + Initial import }