1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
|
{
/***************************************************************************
qtint.pp - Qt5 Interface Object
-------------------
Initial Revision : Fri Sep 16 2016
***************************************************************************/
*****************************************************************************
This file is part of the Lazarus Component Library (LCL)
See the file COPYING.modifiedLGPL.txt, included in this distribution,
for details about the license.
*****************************************************************************
}
unit qtint;
{$mode objfpc}{$H+}
interface
{$I qtdefines.inc}
{$ifdef Trace}
{$ASSERTIONS ON}
{$endif}
uses
{$IFDEF MSWINDOWS}
Windows, // used to retrieve correct caption color values
{$ENDIF}
// Bindings - qt5 must come first to avoid type redefinition problems
qt5,
// RTL
Classes, SysUtils, Math, Types, ctypes,
// LCL
InterfaceBase, LCLPlatformDef, LCLProc, LCLType, LMessages,
LCLMessageGlue, LCLStrConsts, Controls, ExtCtrls, Forms,
Dialogs, StdCtrls, LCLIntf, GraphUtil, Themes,
// LazUtils
GraphType, LazStringUtils, LazUtilities, LazLoggerBase, LazUTF8, Maps,
// WS
{$IFDEF HASX11}
qtx11dummywidget,
{$ENDIF}
qtproc;
type
{ TQtWidgetSet }
TQtWidgetSet = Class(TWidgetSet)
private
App: QApplicationH;
{$IFDEF QtUseNativeEventLoop}
FMainTimerID: integer;
{$ENDIF}
FIsLibraryInstance: Boolean;
// cache for WindowFromPoint
FLastWFPMousePos: TPoint;
FLastWFPResult: HWND;
// global actions
FGlobalActions: TFPList;
FAppActive: Boolean;
FOverrideCursor: TObject;
SavedDCList: TFPList;
CriticalSection: TRTLCriticalSection;
SavedHandlesList: TMap;
FSocketEventMap: TMap;
StayOnTopList: TMap;
SysTrayIconsList: TFPList;
// global hooks
FAppEvenFilterHook: QObject_hookH;
{$IFDEF QTUSEFOCUSCHANGEDHOOK}
FAppFocusChangedHook: QApplication_hookH;
{$ENDIF}
FAppSessionQuit: QGUIApplication_hookH;
FAppSaveSessionRequest: QGUIApplication_hookH;
// default application font name (FamilyName for "default" font)
FDefaultAppFontName: WideString;
FDockImage: QRubberBandH;
FDragImageList: QWidgetH;
FDragHotSpot: TPoint;
FDragImageLock: Boolean;
FCachedColors: array[0..MAX_SYS_COLORS] of PLongWord;
FSysColorBrushes: array[0..MAX_SYS_COLORS] of HBrush;
{$IFDEF HASX11}
SavedHintHandlesList: TFPList;
FWindowManagerName: String; // Track various incompatibilities between WM. Initialized at WS start.
{$ENDIF}
// qt style does not have pixel metric for themed menubar (menu) height
// so we must calculate it somehow.
FCachedMenuBarHeight: Integer;
function GetMenuHeight: Integer;
procedure ClearCachedColors;
function GetStyleName: String;
procedure SetOverrideCursor(const AValue: TObject);
procedure QtRemoveStayOnTop(const ASystemTopAlso: Boolean = False);
procedure QtRestoreStayOnTop(const ASystemTopAlso: Boolean = False);
procedure SetDefaultAppFontName;
protected
FPenForSetPixel: QPenH;
FInGetPixel: boolean;
FStockNullBrush: HBRUSH;
FStockBlackBrush: HBRUSH;
FStockLtGrayBrush: HBRUSH;
FStockGrayBrush: HBRUSH;
FStockDkGrayBrush: HBRUSH;
FStockWhiteBrush: HBRUSH;
FStockNullPen: HPEN;
FStockBlackPen: HPEN;
FStockWhitePen: HPEN;
FStockSystemFont: HFONT;
FStockDefaultDC: HDC;
{$IFDEF HASX11}
FWSFrameRect: TRect;
{$ENDIF}
function CreateThemeServices: TThemeServices; override;
function EventFilter(Sender: QObjectH; Event: QEventH): Boolean; cdecl;
{$IFDEF QTUSEFOCUSCHANGEDHOOK}
procedure FocusChanged(aold: QWidgetH; anew: QWidgetH); cdecl;
{$ENDIF}
procedure OnWakeMainThread(Sender: TObject);
{$ifndef QT_NO_SESSIONMANAGER}
procedure SlotCommitDataRequest(sessionManager: QSessionManagerH); cdecl;
procedure SlotSaveDataRequest(sessionManager: QSessionManagerH); cdecl;
{$endif}
public
function LCLPlatform: TLCLPlatform; override;
function GetLCLCapability(ACapability: TLCLCapability): PtrUInt; override;
// Application
procedure AppInit(var ScreenInfo: TScreenInfo); override;
procedure AppRun(const ALoop: TApplicationMainLoop); override;
procedure AppWaitMessage; override;
procedure AppProcessMessages; override;
procedure AppTerminate; override;
procedure AppMinimize; override;
procedure AppRestore; override;
procedure AppBringToFront; override;
procedure AppSetIcon(const Small, Big: HICON); override;
procedure AppSetTitle(const ATitle: string); override;
function AppRemoveStayOnTopFlags(const ASystemTopAlso: Boolean = False): Boolean; override;
function AppRestoreStayOnTopFlags(const ASystemTopAlso: Boolean = False): Boolean; override;
{$IFDEF HASX11}
function CreateDummyWidgetFrame(const ALeft, ATop, AWidth, AHeight: integer): boolean;
function GetDummyWidgetFrame: TRect;
{$ENDIF}
public
constructor Create; override;
destructor Destroy; override;
function DCGetPixel(CanvasHandle: HDC; X, Y: integer): TGraphicsColor; override;
procedure DCSetPixel(CanvasHandle: HDC; X, Y: integer; AColor: TGraphicsColor); override;
procedure DCRedraw(CanvasHandle: HDC); override;
procedure DCSetAntialiasing(CanvasHandle: HDC; AEnabled: Boolean); override;
procedure SetDesigning(AComponent: TComponent); override;
// create and destroy
function CreateTimer(Interval: integer; TimerFunc: TWSTimerProc): TLCLHandle; override;
function DestroyTimer(TimerHandle: TLCLHandle): boolean; override;
// device contexts
function IsValidDC(const DC: HDC): Boolean; virtual;
function IsValidGDIObject(const GDIObject: HGDIOBJ): Boolean; virtual;
// qt object handles map
procedure AddHandle(AHandle: TObject);
procedure RemoveHandle(AHandle: TObject);
function IsValidHandle(AHandle: HWND): Boolean;
// qt systray icons map
procedure RegisterSysTrayIcon(AHandle: TObject);
procedure UnRegisterSysTrayIcon(AHandle: TObject);
function IsValidSysTrayIcon(AHandle: HWND): Boolean;
{$IFDEF HASX11}
// qt hints handles map (needed on X11 only)
procedure AddHintHandle(AHandle: TObject);
procedure RemoveHintHandle(AHandle: TObject);
procedure RemoveAllHintsHandles;
function IsValidHintHandle(AHandle: TObject): Boolean;
procedure HideAllHints;
procedure RestoreAllHints;
{$ENDIF}
// application global actions (mainform mainmenu mnemonics Alt+XX)
procedure ClearGlobalActions;
procedure AddGlobalAction(AnAction: QActionH);
function ShortcutInGlobalActions(const AMnemonicText: WideString;
out AGlobalActionIndex: Integer): Boolean;
procedure TriggerGlobalAction(const ActionIndex: Integer);
// cache for WindowFromPoint to reduce very expensive calls
// of QApplication_widgetAt() inside WindowFromPoint().
function IsWidgetAtCache(AHandle: HWND): Boolean;
procedure InvalidateWidgetAtCache;
function IsValidWidgetAtCachePointer: Boolean;
function GetWidgetAtCachePoint: TPoint;
// drag image list
function DragImageList_BeginDrag(AImage: QImageH; AHotSpot: TPoint): Boolean;
procedure DragImageList_EndDrag;
function DragImageList_DragMove(X, Y: Integer): Boolean;
function DragImageList_SetVisible(NewVisible: Boolean): Boolean;
public
{$IFDEF HASX11}
FLastMinimizeEvent: DWord; // track mainform minimize events -> TQtMainWindow.EventFilter
FMinimizedByPager: Boolean; // track if app is minimized via desktop pager or by us.
{$ENDIF}
{$IFDEF MSWINDOWS}
function GetWinKeyState(AKeyState: LongInt): SHORT;
{$ENDIF}
function CreateDefaultFont: HFONT; virtual;
function GetDefaultAppFontName: WideString;
function GetQtDefaultDC: HDC; virtual;
procedure DeleteDefaultDC; virtual;
procedure SetQtDefaultDC(Handle: HDC); virtual;
procedure InitStockItems;
procedure FreeStockItems;
procedure FreeSysColorBrushes(const AInvalidateHandlesOnly: Boolean = False);
property AppActive: Boolean read FAppActive;
property DragImageLock: Boolean read FDragImageLock write FDragImageLock;
{do not create new QApplication object if we are called from library }
property IsLibraryInstance: Boolean read FIsLibraryInstance;
property OverrideCursor: TObject read FOverrideCursor write SetOverrideCursor;
property StyleName: String read GetStyleName;
{$IFDEF HASX11}
property WindowManagerName: String read FWindowManagerName;
{$ENDIF}
{$I qtwinapih.inc}
{$I qtlclintfh.inc}
end;
type
TEventProc = record
Name : String[25];
CallBack : procedure(Data : TObject);
Data : Pointer;
end;
CallbackProcedure = procedure (Data : Pointer);
pTRect = ^TRect;
function HwndFromWidgetH(const WidgetH: QWidgetH): HWND;
function DTFlagsToQtFlags(const Flags: Cardinal): Integer;
function GetPixelMetric(AMetric: QStylePixelMetric; AOption: QStyleOptionH;
AWidget: QWidgetH): Integer;
function GetQtVersion: String;
function QtVersionCheck(const AMajor, AMinor, AMicro: Integer): Boolean;
{$IFDEF HASX11}
function IsWayland: Boolean; {this is not X11 but wayland !}
function IsCurrentDesktop(AWidget: QWidgetH): Boolean;
function X11Raise(AHandle: PtrUInt): boolean;
function X11GetActiveWindow: QWidgetH;
function GetWindowManager: String;
function SetTransientForHint(Widget: QWidgetH; ATransientWin: QWidgetH): boolean;
procedure SetSkipX11Taskbar(Widget: QWidgetH; const ASkipTaskBar: Boolean);
{check if XWindow have _NET_WM_STATE_ABOVE and our form doesn''t know anything about it}
function GetAlwaysOnTopX11(Widget: QWidgetH): boolean;
{check if we are running under kde3 installation}
function IsOldKDEInstallation: Boolean;
{check KDE session version. Possible results are > 2, -1 means not running under KDE}
function GetKdeSessionVersion: integer;
{force mapping}
procedure MapX11Window(AWinID: PtrUInt);
{$IFDEF QtUseX11Extras}
// do not remove those
function GetX11WindowRealized(AWinID: PtrUInt): boolean;
function GetX11WindowAttributes(AWinID: PtrUInt; out ALeft, ATop, AWidth, AHeight, ABorder: integer): boolean;
function GetX11SupportedAtoms(AWinID: PtrUInt; AList: TStrings): boolean;
{Ask for _NET_FRAME_EXTENTS,_KDE_NET_WM_SHADOW,_GTK_NET_FRAME_EXTENTS}
function GetX11RectForAtom(AWinID: PtrUInt; const AAtomName: string; out ARect: TRect): boolean;
function GetX11WindowPos(AWinID: PtrUInt; out ALeft, ATop: integer): boolean;
function SetX11WindowPos(AWinID: PtrUInt; const ALeft, ATop: integer): boolean;
function GetX11WindowGeometry(AWinID: PtrUInt; out ARect: TRect): boolean;
{check if wm supports request for frame extents}
function AskX11_NET_REQUEST_FRAME_EXTENTS(AWinID: PtrUInt; out AMargins: TRect): boolean;
{$ENDIF}
{$ENDIF}
const
QtVersionMajor: Integer = 0;
QtVersionMinor: Integer = 0;
QtVersionMicro: Integer = 0;
QtMinimumWidgetSize = 0;
QtMaximumWidgetSize = 16777215;
TargetEntrys = 3;
QEventLCLMessage = QEventUser;
// QEventType(Ord(QEventUser) + $1000) is reserved by
// LCLQt_Destroy (qtobjects) to reduce includes !
LCLQt_CheckSynchronize = QEventType(Ord(QEventUser) + $1001);
LCLQt_PopupMenuClose = QEventType(Ord(QEventUser) + $1002);
LCLQt_PopupMenuTriggered = QEventType(Ord(QEventUser) + $1003);
// QEventType(Ord(QEventUser) + $1004 is reserved by
// LCLQt_ClipboardPrimarySelection (qtobjects) to reduce includes !
LCLQt_ApplicationActivate = QEventType(Ord(QEventUser) + $1005);
// deactivate sent from qt
LCLQt_ApplicationDeactivate = QEventType(Ord(QEventUser) + $1006);
// deactivate sent from LCLQt_ApplicationDeactivate to check it twice
// instead of using timer.
LCLQt_ApplicationDeactivate_Check = QEventType(Ord(QEventUser) + $1007);
// needed by itemviews (TQtListWidget, TQtTreeWidget)
LCLQt_ItemViewAfterMouseRelease = QEventType(Ord(QEventUser) + $1008);
// used by TQtTabWidget
LCLQt_DelayLayoutRequest = QEventType(Ord(QEventUser) + $1009);
// delayed resize event if wincontrol is computing bounds
LCLQt_DelayResizeEvent = QEventType(Ord(QEventUser) + $1010);
// systemtrayicon event, used to find and register private QWidget of QSystemTrayIcon
LCLQt_RegisterSystemTrayIcon = QEventType(Ord(QEventUser) + $1011);
// combobox OnCloseUp should be in order OnChange->OnSelect->OnCloseUp
LCLQt_ComboBoxCloseUp = QEventType(Ord(QEventUser) + $1012);
QtTextSingleLine = $0100;
QtTextDontClip = $0200;
QtTextExpandTabs = $0400;
QtTextShowMnemonic = $0800;
QtTextWordWrap = $1000;
QtTextWrapAnywhere = $2000;
QtTextHideMnemonic = $8000;
QtTextDontPrint = $4000;
QtTextIncludeTrailingSpaces = $08000000;
QtTextJustificationForced = $10000;
var
QtWidgetSet: TQtWidgetSet;
{$IFDEF HASX11}
function QX11Info_isCompositingManagerRunning: Boolean; {does not exist in Qt5}
{$ENDIF}
implementation
uses
////////////////////////////////////////////////////
// I M P O R T A N T
////////////////////////////////////////////////////
// To get as little as possible circles,
// uncomment only those units with implementation
////////////////////////////////////////////////////
{$IFDEF HASX11}
XAtom, X, XLib, XKB, xkblib,
{$ENDIF}
QtCaret,
QtThemes,
////////////////////////////////////////////////////
Graphics, buttons, Menus,
// Bindings
QtWSFactory, qtwidgets, qtobjects, qtsystemtrayicon;
function DTFlagsToQtFlags(const Flags: Cardinal): Integer;
begin
Result := 0;
// horizontal alignment
if Flags and DT_CENTER <> 0 then
Result := Result or QtAlignHCenter
else
if Flags and DT_RIGHT <> 0 then
Result := Result or QtAlignRight
else
Result := Result or QtAlignLeft;
// vertical alignment
if Flags and DT_VCENTER <> 0 then
Result := Result or QtAlignVCenter
else
if Flags and DT_BOTTOM <> 0 then
Result := Result or QtAlignBottom
else
Result := Result or QtAlignTop;
// mutually exclusive wordbreak and singleline
if Flags and DT_WORDBREAK <> 0 then
Result := Result or QtTextWordWrap
else
if Flags and DT_SINGLELINE <> 0 then
Result := Result or QtTextSingleLine;
if Flags and DT_NOPREFIX = 0 then
Result := Result or QtTextShowMnemonic;
if Flags and DT_NOCLIP <> 0 then
Result := Result or QtTextDontClip;
if Flags and DT_EXPANDTABS <> 0 then
Result := Result or QtTextExpandTabs;
end;
function GetPixelMetric(AMetric: QStylePixelMetric; AOption: QStyleOptionH;
AWidget: QWidgetH): Integer;
begin
Result := QStyle_pixelMetric(QApplication_style(),
AMetric, AOption, AWidget);
end;
function QtObjectFromWidgetH(const WidgetH: QWidgetH): TQtWidget;
var
V: QVariantH;
Ok: Boolean;
Obj: TObject;
QtWg: TQtWidget;
begin
Result := nil;
if WidgetH = nil then
exit;
V := QVariant_Create();
try
QObject_property(QObjectH(WidgetH), V, 'lclwidget');
if not QVariant_IsNull(v) and QVariant_isValid(V) then
begin
//Write('Got a valid variant .. ');
{$IFDEF CPU32}
Obj := TObject(QVariant_toUint(V, @Ok));
{$ENDIF}
{$IFDEF CPU64}
Obj := TObject(QVariant_toULongLong(V, @Ok));
{$ENDIF}
if OK and QtWidgetset.IsValidHandle(HWND(Obj)) then
begin
if not (Obj is TQtWidget) then
raise Exception.Create('QtObjectFromWidgetH: QObject_property returned '
+ 'a variant which is not TQtWidget ' + dbgHex(PtrUInt(Obj)));
QtWg := TQtWidget(Obj);
//Write('Converted successfully, Control=');
if QtWg<>nil then
begin
Result := QtWg;
//WriteLn(Result.LCLObject.Name);
end else
;//WriteLn('nil');
end else
;//WriteLn('Can''t convert to UINT');
end else
;//Writeln('GetFocus: Variant is NULL or INVALID');
finally
QVariant_Destroy(V);
end;
end;
function HwndFromWidgetH(const WidgetH: QWidgetH): HWND;
begin
Result := 0;
if WidgetH = nil then
exit;
Result := HWND(QtObjectFromWidgetH(WidgetH));
end;
function GetFirstQtObjectFromWidgetH(WidgetH: QWidgetH): TQtWidget;
begin
Result := nil;
if WidgetH = nil then
Exit;
repeat
Result := QtObjectFromWidgetH(WidgetH);
if Result = nil then
begin
WidgetH := QWidget_parentWidget(WidgetH);
if WidgetH = nil then
break;
end;
until Result <> nil;
end;
function ConvertFontWeightToQtConst(Value: Integer): Integer;
begin
case Value of
0: Result := FW_THIN;
12: Result := FW_EXTRALIGHT;
25: Result := FW_LIGHT;
50: Result := FW_NORMAL;
57: Result := FW_MEDIUM;
63: Result := FW_SEMIBOLD;
75: Result := FW_BOLD;
81: Result := FW_EXTRABOLD;
87: Result := FW_HEAVY;
else
Result := Round(Value * 9.5);
end;
end;
{------------------------------------------------------------------------------
Method: GetQtVersion
Params: none
Returns: String
Returns current Qt lib version used by application.
------------------------------------------------------------------------------}
function GetQtVersion: String;
begin
Result := QtVersion;
end;
procedure QtVersionInt(out AMajor, AMinor, AMicro: integer);
var
S: String;
AList: TStringList;
begin
AMajor := 0;
AMinor := 0;
AMicro := 0;
S := GetQtVersion;
// 5 is usual length of qt5 version eg. 5.6.1
if length(S) < 5 then
exit;
AList := TStringList.Create;
try
AList.Delimiter := '.';
AList.DelimitedText := S;
TryStrToInt(AList[0], AMajor);
TryStrToInt(AList[1], AMinor);
TryStrToInt(AList[2], AMicro);
finally
AList.Free;
end;
end;
{------------------------------------------------------------------------------
Method: QtVersionCheck
Params: AMajor, AMinor, AMicro: Integer
Returns: Boolean
Function checks if qt lib version satisfies our function params values.
Returns TRUE if successfull.
It is possible to check Major and/or Minor version only (or any of those
3 params) by setting it's param to -1.
eg. QtVersionCheck(4, 5, -1) checks only major and minor version and will
not process micro version check.
NOTE: It checks qt lib version used by application.
------------------------------------------------------------------------------}
function QtVersionCheck(const AMajor, AMinor, AMicro: Integer): Boolean;
begin
Result := False;
if AMajor > 0 then
Result := AMajor = QtVersionMajor;
if (AMajor > 0) and not Result then
exit;
if AMinor >= 0 then
Result := AMinor = QtVersionMinor;
if (AMinor >= 0) and not Result then
exit;
if AMicro >= 0 then
Result := AMicro = QtVersionMicro;
end;
{$IFDEF HASX11}
{$I qtx11.inc}
{$ENDIF}
{$I qtobject.inc}
{$I qtwinapi.inc}
{$I qtlclintf.inc}
end.
|