diff --git a/Lgi_vs2019.vcxproj b/Lgi_vs2019.vcxproj
--- a/Lgi_vs2019.vcxproj
+++ b/Lgi_vs2019.vcxproj
@@ -1,689 +1,689 @@
Debug
Win32
Debug
x64
ReleaseNoOptimize
Win32
ReleaseNoOptimize
x64
Release
Win32
Release
x64
Lgi
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}
10.0
DynamicLibrary
v142
false
Unicode
DynamicLibrary
v142
false
Unicode
DynamicLibrary
v142
false
Unicode
DynamicLibrary
v142
false
Unicode
DynamicLibrary
v142
false
Unicode
DynamicLibrary
v142
false
Unicode
<_ProjectFileVersion>12.0.30501.0
.\Lib\
$(Platform)$(Configuration)19\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x32
.\Lib\
$(Platform)$(Configuration)19\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x64
.\Lib\
$(Platform)$(Configuration)19\
true
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x32d
.\Lib\
$(Platform)$(Configuration)19\
true
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x64d
.\Lib\
$(Platform)$(Configuration)19\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x32nop
.\Lib\
$(Platform)$(Configuration)19\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi19x64nop
NDEBUG;%(PreprocessorDefinitions)
true
true
Win32
.\Release/Lgi.tlb
MinSpace
OnlyExplicitInline
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;%(AdditionalIncludeDirectories)
NDEBUG;WIN32;WINDOWS;LGI_RES;LGI_LIBRARY;%(PreprocessorDefinitions)
true
MultiThreadedDLL
true
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level2
true
ProgramDatabase
Default
NDEBUG;%(PreprocessorDefinitions)
0x0c09
/MACHINE:I386 %(AdditionalOptions)
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
true
false
$(OutDir)$(TargetName).lib
NDEBUG;%(PreprocessorDefinitions)
true
true
X64
.\Release/Lgi.tlb
MinSpace
OnlyExplicitInline
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories)
WIN64;NDEBUG;WINDOWS;LGI_RES;LGI_LIBRARY;%(PreprocessorDefinitions)
true
MultiThreadedDLL
true
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level2
true
ProgramDatabase
Default
NDEBUG;%(PreprocessorDefinitions)
0x0c09
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
true
false
$(OutDir)$(TargetName).lib
MachineX64
_DEBUG;%(PreprocessorDefinitions)
true
true
Win32
.\Debug/Lgi.tlb
Disabled
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;%(AdditionalIncludeDirectories)
LGI_LIBRARY;_DEBUG;WIN32;WINDOWS;LGI_RES;%(PreprocessorDefinitions)
EnableFastChecks
MultiThreadedDebugDLL
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level2
true
ProgramDatabase
Default
_DEBUG;%(PreprocessorDefinitions)
0x0c09
/MACHINE:I386 %(AdditionalOptions)
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
false
$(OutDir)$(TargetName).lib
_DEBUG;%(PreprocessorDefinitions)
true
true
X64
.\Debug/Lgi.tlb
Disabled
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories)
WIN64;LGI_LIBRARY;_DEBUG;WINDOWS;LGI_RES;%(PreprocessorDefinitions)
EnableFastChecks
MultiThreadedDebugDLL
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level3
true
ProgramDatabase
Default
_DEBUG;%(PreprocessorDefinitions)
0x0c09
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
NotSet
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
false
$(OutDir)$(TargetName).lib
MachineX64
NDEBUG;%(PreprocessorDefinitions)
true
true
Win32
.\Release/Lgi.tlb
Disabled
OnlyExplicitInline
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;%(AdditionalIncludeDirectories)
NDEBUG;WIN32;WINDOWS;LGI_RES;LGI_LIBRARY;%(PreprocessorDefinitions)
true
MultiThreadedDLL
true
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level2
true
ProgramDatabase
Default
NDEBUG;%(PreprocessorDefinitions)
0x0c09
/MACHINE:I386 %(AdditionalOptions)
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
true
false
$(OutDir)$(TargetName).lib
NDEBUG;%(PreprocessorDefinitions)
true
true
X64
.\Release/Lgi.tlb
MinSpace
OnlyExplicitInline
include;include\lgi\win;private\common;private\win;..\..\..\CodeLib\libiconv\include;..\..\scribe\libs\build-x64\libiconv-1.17\include;%(AdditionalIncludeDirectories)
WIN64;NDEBUG;WINDOWS;LGI_RES;LGI_LIBRARY;%(PreprocessorDefinitions)
true
MultiThreadedDLL
true
true
$(IntDir)$(TargetName).pch
$(IntDir)
$(IntDir)
$(IntDir)
Level2
true
ProgramDatabase
Default
NDEBUG;%(PreprocessorDefinitions)
0x0c09
ComCtl32.lib;Ws2_32.lib;UxTheme.lib;imm32.lib;%(AdditionalDependencies)
$(OutDir)$(TargetFileName)
true
true
$(OutDir)$(TargetName).pdb
Windows
true
false
$(OutDir)$(TargetName).lib
MachineX64
true
false
true
true
true
true
false
false
false
false
false
false
true
true
true
true
true
true
true
true
true
true
true
true
false
false
false
false
false
false
true
true
true
true
true
true
true
true
true
true
true
true
-
+
\ No newline at end of file
diff --git a/Lgi_vs2019.vcxproj.filters b/Lgi_vs2019.vcxproj.filters
--- a/Lgi_vs2019.vcxproj.filters
+++ b/Lgi_vs2019.vcxproj.filters
@@ -1,779 +1,779 @@
{afe8cb77-9ad1-4536-bbdd-3c127e7ed08c}
cpp;c;cxx;rc;def;r;odl;idl;hpj;bat
{66a64573-871b-4499-ae26-c19e9e2a514a}
{3fc23ef0-f144-4f1f-a9b4-18d3392bb63d}
{c6cd6d73-d33c-4413-ade1-9dad78e2dc9c}
{c8684fc7-2e3c-4f15-8284-9d44b044f6c6}
{87b1c801-b9ce-4f6c-97ab-a8f89aee9594}
{c06c25f2-2c07-4900-a517-4a6a324069e9}
{2c01a737-36cf-4197-bfa1-20395060263f}
{1e4cd802-8b94-4328-930e-37bfbfbedff5}
{01075698-dde2-4ed0-808f-7dd54414b597}
{4a6845a8-e6ec-47d5-8f6c-aa0cfdbc68df}
{f567a76b-edd5-434d-b0d9-d51481f34845}
{3dee0237-b01c-44e8-a730-08dc661df82f}
{bbaaace6-0ac7-4e76-90ae-9e5d5a0cb899}
{71e7b970-a070-40a7-a99f-88c215e14e44}
{6e115ea1-09fb-492b-82b6-428afe17fed9}
{719ef36f-419f-46f9-aef9-2f8158e4d106}
{fb221556-3700-4dd8-ba9a-10b5d66bfe54}
{8e9f0321-56ae-4485-a338-e87d714c7f50}
{c6050f41-574b-4a92-9ce5-860be2719ecf}
{a07cd969-801e-4ec9-9394-e34912a3271d}
{e067fae0-ef98-4e7a-8434-6f36590ba0e6}
{0a3e2151-b13a-407a-8cd9-ccb20f15cacf}
{c72248a4-c2f0-43b9-950d-89d09bfddbb3}
{dad60202-c763-4f32-9dfb-fe7def4637ee}
{532dfa4a-58d3-4133-9ed6-a9fbf7f1556e}
{5409aca4-2a55-4b2f-a719-d3db4e3cd7e4}
{e3a3aadd-47ef-4723-9bcc-7db1f75a18b0}
{c4327acf-78c3-4ef1-b8bc-3aac9ea52b41}
{b3c906b8-595e-4641-8eec-8ad03ab13f6f}
{baf0a65b-4a9c-4b11-850d-a957c19a22bf}
{6e349f5b-36a8-4821-9574-4040a3784204}
{ddfdebae-8bcf-4224-8938-2716aba03822}
{a126c55a-edee-489f-a852-25cbd1b433b2}
{ab5fd4a0-3862-42fd-b4ff-d5d8b0443f53}
{048d5e0a-220f-4911-999d-96965eb53691}
{258aef64-8cd0-4838-8131-147196656a99}
{814a5d81-3fd5-461b-a4a3-cda593ea404b}
{5fe450b8-5fa9-440e-9fb0-03860b3548d0}
h;hpp;hxx;hm;inl
Source Files\Core\Resources
Source Files\Core\Resources
Source Files\Core\Skin
Source Files\General\Hash
Source Files\General\Hash
Source Files\Graphics
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Graphics\Applicators
Source Files\Dialogs
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Dialogs
Source Files\Dialogs
Source Files\Text
Source Files\Widgets\Native Windows
Source Files\Widgets\Native Windows
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Graphics\Font
Source Files\General\Clipboard
Source Files\Graphics
Source Files\Graphics\Colour Reduction
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\Core\Memory Subsystem
Source Files\Text
Source Files\Text
Source Files\Core\DateTime
Source Files\Widgets\Native Windows
Source Files\Graphics\Font
Source Files\Dialogs
Source Files\Core\Drag and Drop
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\General
Source Files\Core\File
Source Files\Core\File
Source Files\Graphics\Filters
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics
Source Files\Graphics\Gdi Leak
Source Files\General
Source Files\Graphics
Source Files\Dialogs
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Core\Libraries
Source Files\General
Source Files\General
Source Files\General
Source Files\Widgets\Xp
Source Files\Dialogs
Source Files\Text
Source Files\Core\Memory Subsystem
Source Files\Graphics\Surfaces
Source Files\Core\Memory Subsystem
Source Files\Core\Menus
Source Files\Core\Menus
Source Files\General
Source Files\General\Mru
Source Files\Core\Mutex
Source Files\Network
Source Files\Network
Source Files\General
Source Files\Core\File
Source Files\Widgets\Xp
Source Files\General
Source Files\Graphics
Source Files\Widgets\Xp
Source Files\Graphics\Surfaces
Source Files\Graphics
Source Files\Widgets\Native Windows
Source Files\Dialogs
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\General
Source Files\Graphics
Source Files\Graphics\Surfaces
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\Widgets\Xp
Source Files\Widgets\Native Windows
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Core\Memory Subsystem
Source Files\Text
Source Files\Widgets\Xp
Source Files\Core\Process
Source Files\Graphics\Surfaces
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Core\Threads
Source Files\Core\Threads
Source Files\Core\Threads
Source Files\Text
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Graphics\Font
Source Files\Text
Source Files\Text
Source Files\Network
Source Files\Text
Source Files\Core\Variant
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Text
Source Files\Widgets\Xp
Source Files\Core\Resources
Source Files\Core\Resources
Source Files\General\Hash
Source Files\General\Hash
Source Files\Graphics
Source Files\Graphics\Gdi Leak
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Source Files\Graphics\Font
Source Files\Interface
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
-
- Header Files
-
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/include/lgi/common/Message.h b/include/lgi/common/Message.h
--- a/include/lgi/common/Message.h
+++ b/include/lgi/common/Message.h
@@ -1,300 +1,297 @@
#ifndef _GMESSAGE_H_
#define _GMESSAGE_H_
enum LgiMessages
{
#if defined(__GTK_H__)
/// Base point for system messages.
M_SYSTEM = 900,
/// Message that indicates the user is trying to close a top level window.
M_CLOSE,
/// Implemented to handle paint requests in the GUI thread.
M_X11_REPARENT,
/// \brief Mouse enter event
///
/// a = bool Inside; // is the mouse inside the client area?\n
/// b = MAKELONG(x, y); // mouse location
M_MOUSEENTER,
/// \brief Mouse exit event
///
/// a = bool Inside; // is the mouse inside the client area?\n
/// b = MAKELONG(x, y); // mouse location
M_MOUSEEXIT,
// return (bool)
M_WANT_DIALOG_PROC,
M_MENU,
M_COMMAND,
M_DRAG_DROP,
M_TRAY_NOTIFY,
M_CUT,
M_COPY,
M_PASTE,
M_GTHREADWORK_COMPELTE,
/// Implemented to handle timer events in the GUI thread.
M_PULSE,
M_SET_VISIBLE,
M_CAPTURE_PULSE,
/// Sent from a worker thread when calling LTextLabel::Name
M_TEXT_UPDATE_NAME,
- M_SET_CTRL_NAME,
- M_SET_CTRL_ENABLE,
- M_SET_CTRL_VISIBLE,
#elif defined(WINNATIVE)
// [WM_APP:WM_APP+200] is reserved for LGI itself.
// [WM_APP+200:0xBFFF] is reserved for LGI applications.
M_CUT = WM_CUT,
M_COPY = WM_COPY,
M_PASTE = WM_PASTE,
M_COMMAND = WM_COMMAND,
M_CLOSE = WM_CLOSE,
// wParam = bool Inside; // is the mouse inside the client area?
// lParam = MAKELONG(x, y); // mouse location
M_MOUSEENTER = WM_APP,
M_MOUSEEXIT,
// return (bool)
M_WANT_DIALOG_PROC,
// wParam = void
// lParam = (MSG*) Msg;
M_TRANSLATE_ACCELERATOR,
// None
M_TRAY_NOTIFY,
// lParam = Style
M_SET_WND_STYLE,
// lParam = LScrollBar *Obj
M_SCROLL_BAR_CHANGED,
// lParam = HWND of window under mouse
// This is only sent for non-LGI window in our process
// because we'd get WM_MOUSEMOVE's for our own windows
M_HANDLEMOUSEMOVE,
// Calls SetWindowPlacement...
M_SET_WINDOW_PLACEMENT,
// Set the visibility of the window
M_SET_VISIBLE,
/// Sent from a worker thread when calling LTextLabel::Name
M_TEXT_UPDATE_NAME,
/// Send when a window is losing it's mouse capture. Usually
// because something else has requested it.
M_LOSING_CAPTURE,
#elif defined(LGI_SDL) || defined(HAIKU)
/// Minimum value for application defined message ID's
M_MOUSEENTER = 900,
M_MOUSEEXIT,
M_COMMAND,
M_CUT,
M_COPY,
M_PASTE,
M_PULSE,
M_SET_VISIBLE,
M_MOUSE_CAPTURE_POLL,
M_TEXT_UPDATE_NAME,
M_ASSERT_DLG,
M_CLOSE,
#elif defined(MAC)
/// Base point for system messages.
M_SYSTEM = 0,
/// Message that indicates the user is trying to close a top level window.
M_CLOSE = (M_SYSTEM+92),
/// \brief Mouse enter event
///
/// a = bool Inside; // is the mouse inside the client area?\n
/// b = MAKELONG(x, y); // mouse location
M_MOUSEENTER = (M_SYSTEM+900),
/// \brief Mouse exit event
///
/// a = bool Inside; // is the mouse inside the client area?\n
/// b = MAKELONG(x, y); // mouse location
M_MOUSEEXIT,
// return (bool)
M_WANT_DIALOG_PROC,
M_MENU,
M_COMMAND,
M_DRAG_DROP,
M_TRAY_NOTIFY,
M_CUT,
M_COPY,
M_PASTE,
M_PULSE,
M_MOUSE_TRACK_UP,
M_GTHREADWORK_COMPELTE,
M_TEXT_UPDATE_NAME,
M_SET_VISIBLE,
M_SETPOS, // A=(LRect*)Rectangle, B=(LView*)this
M_ASSERT_DLG,
M_QUIT,
M_ABOUT,
M_PERFERENCES,
M_HIDE,
M_DESTROY,
- #else
-
-
#endif
M_DESCRIBE,
M_CHANGE,
M_DELETE,
M_TABLE_LAYOUT,
M_URL,
M_LOG_TEXT,
M_ASSERT_UI,
- M_INVALIDATE, // A=(LRect*)Rectangle, B=(LView*)this
- M_RESIZE_TO_CONTENT, // LItemContainer
- M_SCROLL_TO, // LTreeItem->LTree
- M_JOBS_LOADED, // LHtml
- M_THREAD_COMPLETED, // A=(LThread*)Thread
+ M_INVALIDATE, // A=(LRect*)Rectangle, B=(LView*)this
+ M_RESIZE_TO_CONTENT, // A=(int)Border (used by LItemContainer)
+ M_SCROLL_TO, // Sent from LTreeItem to LTree
+ M_JOBS_LOADED, // LHtml
+ M_THREAD_COMPLETED, // A=(LThread*)Thread
+ M_SET_CTRL_NAME, // A=(int)CtrlId, B=(LString*)Name
+ M_SET_CTRL_ENABLE, // A=(int)CtrlId, B=(bool)Enabled
+ M_SET_CTRL_VISIBLE, // A=(int)CtrlId, B=(bool)Visible
#ifdef WINDOWS
M_USER = WM_APP + 200
#else
M_USER = 1000
#endif
};
class LgiClass LMessage
#ifdef HAIKU
: public BMessage
#endif
{
public:
#if defined(WINNATIVE)
typedef LPARAM Param;
typedef LRESULT Result;
#elif defined(LGI_SDL)
typedef void *Param;
typedef NativeInt Result;
#else
typedef NativeInt Param;
typedef NativeInt Result;
#endif
#if !defined(LGI_SDL) && !defined(HAIKU)
int m;
#endif
#if defined(LGI_SDL)
SDL_Event Event;
struct EventParams
{
Param a, b;
EventParams(Param A, Param B)
{
a = A;
b = B;
}
};
#elif defined(WINNATIVE)
HWND hWnd;
WPARAM a;
LPARAM b;
#elif !defined(HAIKU)
Param a;
Param b;
#endif
#if LGI_COCOA
#if __OBJC__
NSEvent *event;
#else
void *event;
#endif
#endif
#if defined(HAIKU)
static constexpr char *PropA = "lgiA";
static constexpr char *PropB = "lgiB";
static constexpr char *PropView = "lgiView";
static constexpr char *PropCallback = "lgiCallback";
typedef std::function InThreadCb;
#endif
LMessage()
{
#if defined(LGI_SDL)
Set(0, 0, 0);
#else
#if defined(WINNATIVE)
hWnd = 0;
#endif
#if !defined(LGI_SDL) && !defined(HAIKU)
m = 0;
a = 0;
b = 0;
#endif
#endif
#if LGI_COCOA
event = NULL;
#endif
}
LMessage
(
int M,
#if defined(WINNATIVE)
WPARAM A = 0, LPARAM B = 0
#else
Param A = 0, Param B = 0
#endif
)
{
#if defined(WINNATIVE)
hWnd = 0;
#endif
#if LGI_COCOA
event = NULL;
#endif
Set(M, A, B);
}
#if defined(LGI_SDL) || defined(HAIKU)
int Msg();
Param A();
Param B();
#else
int Msg() { return m; }
Param A() { return a; }
Param B() { return b; }
#endif
void Set(int m, Param a, Param b);
bool Send(class LViewI *View);
};
#ifdef LINUX
extern LMessage CreateMsg(int m, int a = 0, int b = 0);
#else
#define CreateMsg(m, a, b) LMessage(m, a, b)
#endif
#endif
diff --git a/src/common/Lgi/ViewCommon.cpp b/src/common/Lgi/ViewCommon.cpp
--- a/src/common/Lgi/ViewCommon.cpp
+++ b/src/common/Lgi/ViewCommon.cpp
@@ -1,2553 +1,2537 @@
/// \file
/// \author Matthew Allen
#ifdef LINUX
#include
#endif
#include "lgi/common/Lgi.h"
#include "lgi/common/DragAndDrop.h"
#include "lgi/common/TableLayout.h"
#include "lgi/common/Button.h"
#include "lgi/common/Css.h"
#include "lgi/common/LgiRes.h"
#include "lgi/common/EventTargetThread.h"
#include "lgi/common/Popup.h"
#include "lgi/common/CssTools.h"
#include "ViewPriv.h"
#if 0
#define DEBUG_CAPTURE(...) printf(__VA_ARGS__)
#else
#define DEBUG_CAPTURE(...)
#endif
//////////////////////////////////////////////////////////////////////////////////////
// Helper
LPoint lgi_view_offset(LViewI *v, bool Debug = false)
{
LPoint Offset;
for (LViewI *p = v; p; p = p->GetParent())
{
if (dynamic_cast(p))
break;
LRect pos = p->GetPos();
LRect cli = p->GetClient(false);
if (Debug)
{
const char *cls = p->GetClass();
LgiTrace(" Off[%s] += %i,%i = %i,%i (%s)\n",
cls,
pos.x1, pos.y1,
Offset.x + pos.x1, Offset.y + pos.y1,
cli.GetStr());
}
Offset.x += pos.x1 + cli.x1;
Offset.y += pos.y1 + cli.y1;
}
return Offset;
}
LMouse &lgi_adjust_click(LMouse &Info, LViewI *Wnd, bool Capturing, bool Debug)
{
static LMouse Temp;
Temp = Info;
if (Wnd)
{
if (Debug
#if 0
|| Capturing
#endif
)
LgiTrace("AdjustClick Target=%s -> Wnd=%s, Info=%i,%i\n",
Info.Target?Info.Target->GetClass():"",
Wnd?Wnd->GetClass():"",
Info.x, Info.y);
if (Temp.Target != Wnd)
{
if (Temp.Target)
{
auto *TargetWnd = Temp.Target->GetWindow();
auto *WndWnd = Wnd->GetWindow();
if (TargetWnd == WndWnd)
{
LPoint TargetOff = lgi_view_offset(Temp.Target, Debug);
if (Debug)
LgiTrace(" WndOffset:\n");
LPoint WndOffset = lgi_view_offset(Wnd, Debug);
Temp.x += TargetOff.x - WndOffset.x;
Temp.y += TargetOff.y - WndOffset.y;
#if 0
LRect c = Wnd->GetClient(false);
Temp.x -= c.x1;
Temp.y -= c.y1;
if (Debug)
LgiTrace(" CliOff -= %i,%i\n", c.x1, c.y1);
#endif
Temp.Target = Wnd;
}
}
else
{
LPoint Offset;
Temp.Target = Wnd;
if (Wnd->WindowVirtualOffset(&Offset))
{
LRect c = Wnd->GetClient(false);
Temp.x -= Offset.x + c.x1;
Temp.y -= Offset.y + c.y1;
}
}
}
}
LAssert(Temp.Target != NULL);
return Temp;
}
//////////////////////////////////////////////////////////////////////////////////////
// LView class methods
LViewI *LView::_Capturing = 0;
LViewI *LView::_Over = 0;
#if LGI_VIEW_HASH
struct ViewTbl : public LMutex
{
typedef LHashTbl, int> T;
private:
T Map;
public:
ViewTbl() : Map(2000), LMutex("ViewTbl")
{
}
T *Lock()
{
if (!LMutex::Lock(_FL))
return NULL;
return ⤅
}
} ViewTblInst;
bool LView::LockHandler(LViewI *v, LView::LockOp Op)
{
ViewTbl::T *m = ViewTblInst.Lock();
if (!m)
return false;
int Ref = m->Find(v);
bool Status = false;
switch (Op)
{
case OpCreate:
{
if (Ref == 0)
Status = m->Add(v, 1);
else
LAssert(!"Already exists?");
break;
}
case OpDelete:
{
if (Ref == 1)
Status = m->Delete(v);
else
LAssert(!"Either locked or missing.");
break;
}
case OpExists:
{
Status = Ref > 0;
break;
}
}
ViewTblInst.Unlock();
return Status;
}
#endif
LView::LView(OsView view)
{
#ifdef _DEBUG
_Debug = false;
#endif
d = new LViewPrivate(this);
#ifdef LGI_SDL
_View = this;
#elif LGI_VIEW_HANDLE && !defined(HAIKU)
_View = view;
#endif
_Window = 0;
_Lock = 0;
_InLock = 0;
_BorderSize = 0;
_IsToolBar = false;
Pos.ZOff(-1, -1);
WndFlags = GWF_VISIBLE;
#ifndef LGI_VIEW_HASH
#error "LGI_VIEW_HASH needs to be defined"
#elif LGI_VIEW_HASH
LockHandler(this, OpCreate);
// printf("Adding %p to hash\n", (LViewI*)this);
#endif
}
LView::~LView()
{
if (d->SinkHnd >= 0)
{
LEventSinkMap::Dispatch.RemoveSink(this);
d->SinkHnd = -1;
}
#if LGI_VIEW_HASH
LockHandler(this, OpDelete);
#endif
for (unsigned i=0; iOwner == this)
{
// printf("%s:%i - ~%s setting %s->Owner to NULL\n", _FL, GetClass(), pu->GetClass());
pu->Owner = NULL;
}
}
_Delete();
DeleteObj(d);
}
int LView::AddDispatch()
{
if (d->SinkHnd < 0)
d->SinkHnd = LEventSinkMap::Dispatch.AddSink(this);
return d->SinkHnd;
}
LString LView::CssStyles(const char *Set)
{
if (Set)
{
d->Styles = Set;
d->CssDirty = true;
}
return d->Styles;
}
LString::Array *LView::CssClasses()
{
return &d->Classes;
}
LArray LView::IterateViews()
{
LArray a;
for (auto c: Children)
a.Add(c);
return a;
}
bool LView::AddView(LViewI *v, int Where)
{
LAssert(!Children.HasItem(v));
bool Add = Children.Insert(v, Where);
if (Add)
{
LView *gv = v->GetGView();
if (gv && gv->_Window != _Window)
{
LAssert(!_InLock);
gv->_Window = _Window;
}
v->SetParent(this);
v->OnAttach();
OnChildrenChanged(v, true);
}
return Add;
}
bool LView::DelView(LViewI *v)
{
bool Has = Children.HasItem(v);
bool b = Children.Delete(v);
if (Has)
OnChildrenChanged(v, false);
Has = Children.HasItem(v);
LAssert(!Has);
return b;
}
bool LView::HasView(LViewI *v)
{
return Children.HasItem(v);
}
OsWindow LView::WindowHandle()
{
auto *w = GetWindow();
return (w) ? w->WindowHandle() : (OsWindow)NULL;
}
LWindow *LView::GetWindow()
{
if (!_Window)
{
// Walk up parent list and find someone who has a window
auto *w = d->GetParent();
for (; w; w = w->d ? w->d->GetParent() : NULL)
{
if (w->_Window)
{
LAssert(!_InLock);
_Window = w->_Window;
break;
}
}
}
return dynamic_cast(_Window);
}
bool LView::Lock(const char *file, int line, int TimeOut)
{
if (!_Window)
GetWindow();
_InLock++;
// LgiTrace("%s::%p Lock._InLock=%i %s:%i\n", GetClass(), this, _InLock, file, line);
if (_Window && _Window->_Lock)
{
if (TimeOut < 0)
{
return _Window->_Lock->Lock(file, line);
}
else
{
return _Window->_Lock->LockWithTimeout(TimeOut, file, line);
}
}
return true;
}
void LView::Unlock()
{
#ifdef HAIKU
if (!d || !d->Hnd)
{
printf("%s:%i - Unlock() error, no hnd.\n", _FL);
return;
}
if (!d->Hnd->Parent())
{
// printf("%s:%p - Unlock() no parent.\n", GetClass(), this);
return;
}
if (_InLock > 0)
{
// printf("%s:%p - Calling UnlockLooper: %i.\n", GetClass(), this, _InLock);
d->Hnd->UnlockLooper();
_InLock--;
// printf("%s:%p - UnlockLooper done: %i.\n", GetClass(), this, _InLock);
}
else
{
printf("%s:%i - Unlock() without lock.\n", _FL);
}
#else
if (_Window &&
_Window->_Lock)
{
_Window->_Lock->Unlock();
}
_InLock--;
// LgiTrace("%s::%p Unlock._InLock=%i\n", GetClass(), this, _InLock);
#endif
}
void LView::OnMouseClick(LMouse &m)
{
}
void LView::OnMouseEnter(LMouse &m)
{
}
void LView::OnMouseExit(LMouse &m)
{
}
void LView::OnMouseMove(LMouse &m)
{
}
bool LView::OnMouseWheel(double Lines)
{
return false;
}
bool LView::OnKey(LKey &k)
{
return false;
}
void LView::OnAttach()
{
List::I it = Children.begin();
for (LViewI *v = *it; v; v = *++it)
{
if (!v->GetParent())
v->SetParent(this);
}
#if 0 // defined __GTK_H__
if (_View && !DropTarget())
{
// If one of our parents is drop capable we need to set a dest here
LViewI *p;
for (p = GetParent(); p; p = p->GetParent())
{
if (p->DropTarget())
{
break;
}
}
if (p)
{
Gtk::gtk_drag_dest_set( _View,
(Gtk::GtkDestDefaults)0,
NULL,
0,
Gtk::GDK_ACTION_DEFAULT);
// printf("%s:%i - Drop dest for '%s'\n", _FL, GetClass());
}
else
{
Gtk::gtk_drag_dest_unset(_View);
// printf("%s:%i - Not setting drop dest '%s'\n", _FL, GetClass());
}
}
#endif
}
void LView::OnCreate()
{
}
void LView::OnDestroy()
{
}
void LView::OnFocus(bool f)
{
// printf("%s::OnFocus(%i)\n", GetClass(), f);
}
void LView::OnPulse()
{
}
void LView::OnPosChange()
{
}
bool LView::OnRequestClose(bool OsShuttingDown)
{
return true;
}
int LView::OnHitTest(int x, int y)
{
return -1;
}
void LView::OnChildrenChanged(LViewI *Wnd, bool Attaching)
{
}
void LView::OnPaint(LSurface *pDC)
{
auto c = GetClient();
LCssTools Tools(this);
Tools.PaintContent(pDC, c);
}
int LView::OnNotify(LViewI *Ctrl, LNotification Data)
{
if (!Ctrl)
return 0;
if (Ctrl == (LViewI*)this && Data.Type == LNotifyActivate)
{
// Default activation is to focus the current control.
Focus(true);
}
else if (d && d->Parent)
{
// default behaviour is just to pass the
// notification up to the parent
// FIXME: eventually we need to call the 'LNotification' parent fn...
return d->Parent->OnNotify(Ctrl, Data);
}
return 0;
}
int LView::OnCommand(int Cmd, int Event, OsView Wnd)
{
return 0;
}
void LView::OnNcPaint(LSurface *pDC, LRect &r)
{
int Border = Sunken() || Raised() ? _BorderSize : 0;
if (Border == 2)
{
LEdge e;
if (Sunken())
e = Focus() ? EdgeWin7FocusSunken : DefaultSunkenEdge;
else
e = DefaultRaisedEdge;
#if WINNATIVE
if (d->IsThemed)
DrawThemeBorder(pDC, r);
else
#endif
LWideBorder(pDC, r, e);
}
else if (Border == 1)
{
LThinBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge);
}
}
#if LGI_COCOA || defined(__GTK_H__)
/*
uint64 nPaint = 0;
uint64 PaintTime = 0;
*/
void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update)
{
/*
uint64 StartTs = Update ? LCurrentTime() : 0;
d->InPaint = true;
*/
// Create temp DC if needed...
LAutoPtr Local;
if (!pDC)
{
if (!Local.Reset(new LScreenDC(this)))
return;
pDC = Local;
}
#if 0
// This is useful for coverage checking
pDC->Colour(LColour(255, 0, 255));
pDC->Rectangle();
#endif
// Non-Client drawing
LRect r;
if (Offset)
{
r = Pos;
r.Offset(Offset);
}
else
{
r = GetClient().ZeroTranslate();
}
pDC->SetClient(&r);
LRect zr1 = r.ZeroTranslate(), zr2 = zr1;
OnNcPaint(pDC, zr1);
pDC->SetClient(NULL);
if (zr2 != zr1)
{
r.x1 -= zr2.x1 - zr1.x1;
r.y1 -= zr2.y1 - zr1.y1;
r.x2 -= zr2.x2 - zr1.x2;
r.y2 -= zr2.y2 - zr1.y2;
}
LPoint o(r.x1, r.y1); // Origin of client
// Paint this view's contents...
pDC->SetClient(&r);
#if 0
if (_Debug)
{
#if defined(__GTK_H__)
Gtk::cairo_matrix_t matrix;
cairo_get_matrix(pDC->Handle(), &matrix);
double ex[4];
cairo_clip_extents(pDC->Handle(), ex+0, ex+1, ex+2, ex+3);
ex[0] += matrix.x0; ex[1] += matrix.y0; ex[2] += matrix.x0; ex[3] += matrix.y0;
LgiTrace("%s::_Paint, r=%s, clip=%g,%g,%g,%g - %g,%g\n",
GetClass(), r.GetStr(),
ex[0], ex[1], ex[2], ex[3],
matrix.x0, matrix.y0);
#elif LGI_COCOA
auto Ctx = pDC->Handle();
CGAffineTransform t = CGContextGetCTM(Ctx);
LRect cr = CGContextGetClipBoundingBox(Ctx);
printf("%s::_Paint() pos=%s transform=%g,%g,%g,%g-%g,%g clip=%s r=%s\n",
GetClass(),
GetPos().GetStr(),
t.a, t.b, t.c, t.d, t.tx, t.ty,
cr.GetStr(),
r.GetStr());
#endif
}
#endif
OnPaint(pDC);
pDC->SetClient(NULL);
// Paint all the children...
for (auto i : Children)
{
LView *w = i->GetGView();
if (w && w->Visible())
{
if (!w->Pos.Valid())
continue;
#if 0
if (w->_Debug)
LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), o.x, o.y);
#endif
w->_Paint(pDC, &o);
}
}
}
#else
void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update)
{
// Create temp DC if needed...
LAutoPtr Local;
if (!pDC)
{
Local.Reset(new LScreenDC(this));
pDC = Local;
}
if (!pDC)
{
printf("%s:%i - No context to draw in.\n", _FL);
return;
}
#if 0
// This is useful for coverage checking
pDC->Colour(LColour(255, 0, 255));
pDC->Rectangle();
#endif
bool HasClient = false;
LRect r(0, 0, Pos.X()-1, Pos.Y()-1), Client;
LPoint o;
if (Offset)
o = *Offset;
#if WINNATIVE
if (!_View)
#endif
{
// Non-Client drawing
Client = r;
OnNcPaint(pDC, Client);
HasClient = GetParent() && (Client != r);
if (HasClient)
{
Client.Offset(o.x, o.y);
pDC->SetClient(&Client);
}
}
r.Offset(o.x, o.y);
// Paint this view's contents
if (Update)
{
LRect OldClip = pDC->ClipRgn();
pDC->ClipRgn(Update);
OnPaint(pDC);
pDC->ClipRgn(OldClip.Valid() ? &OldClip : NULL);
}
else
{
OnPaint(pDC);
}
#if PAINT_VIRTUAL_CHILDREN
// Paint any virtual children
for (auto i : Children)
{
LView *w = i->GetGView();
if (w && w->Visible())
{
#if LGI_VIEW_HANDLE
if (!w->Handle())
#endif
{
LRect p = w->GetPos();
p.Offset(o.x, o.y);
if (HasClient)
p.Offset(Client.x1 - r.x1, Client.y1 - r.y1);
LPoint co(p.x1, p.y1);
// LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), p.x1, p.y1);
pDC->SetClient(&p);
w->_Paint(pDC, &co);
pDC->SetClient(NULL);
}
}
}
#endif
if (HasClient)
pDC->SetClient(0);
}
#endif
LViewI *LView::GetParent()
{
ThreadCheck();
return d ? d->Parent : NULL;
}
void LView::SetParent(LViewI *p)
{
ThreadCheck();
d->Parent = p ? p->GetGView() : NULL;
d->ParentI = p;
}
void LView::SendNotify(LNotification note)
{
LViewI *n = d->Notify ? d->Notify : d->Parent;
if (n)
{
if (
#if LGI_VIEW_HANDLE && !defined(HAIKU)
!_View ||
#endif
InThread())
{
n->OnNotify(this, note);
}
else
{
// We are not in the same thread as the target window. So we post a message
// across to the view.
if (GetId() <= 0)
{
// We are going to generate a control Id to help the receiver of the
// M_CHANGE message find out view later on. The reason for doing this
// instead of sending a pointer to the object, is that the object
// _could_ be deleted between the message being sent and being received.
// Which would result in an invalid memory access on that object.
LViewI *p = GetWindow();
if (!p)
{
// No window? Find the top most parent we can...
p = this;
while (p->GetParent())
p = p->GetParent();
}
if (p)
{
// Give the control a valid ID
int i;
for (i=10; i<1000; i++)
{
if (!p->FindControl(i))
{
printf("Giving the ctrl '%s' the id '%i' for SendNotify\n",
GetClass(),
i);
SetId(i);
break;
}
}
}
else
{
// Ok this is really bad... go random (better than nothing)
SetId(5000 + LRand(2000));
}
}
LAssert(GetId() > 0); // We must have a valid ctrl ID at this point, otherwise
// the receiver will never be able to find our object.
// printf("Post M_CHANGE %i %i\n", GetId(), Data);
n->PostEvent(M_CHANGE, (LMessage::Param) GetId(), (LMessage::Param) new LNotification(note));
}
}
}
LViewI *LView::GetNotify()
{
ThreadCheck();
return d->Notify;
}
void LView::SetNotify(LViewI *p)
{
ThreadCheck();
d->Notify = p;
}
#define ADJ_LEFT 1
#define ADJ_RIGHT 2
#define ADJ_UP 3
#define ADJ_DOWN 4
int IsAdjacent(LRect &a, LRect &b)
{
if ( (a.x1 == b.x2 + 1) &&
!(a.y1 > b.y2 || a.y2 < b.y1))
{
return ADJ_LEFT;
}
if ( (a.x2 == b.x1 - 1) &&
!(a.y1 > b.y2 || a.y2 < b.y1))
{
return ADJ_RIGHT;
}
if ( (a.y1 == b.y2 + 1) &&
!(a.x1 > b.x2 || a.x2 < b.x1))
{
return ADJ_UP;
}
if ( (a.y2 == b.y1 - 1) &&
!(a.x1 > b.x2 || a.x2 < b.x1))
{
return ADJ_DOWN;
}
return 0;
}
LRect JoinAdjacent(LRect &a, LRect &b, int Adj)
{
LRect t;
switch (Adj)
{
case ADJ_LEFT:
case ADJ_RIGHT:
{
t.y1 = MAX(a.y1, b.y1);
t.y2 = MIN(a.y2, b.y2);
t.x1 = MIN(a.x1, b.x1);
t.x2 = MAX(a.x2, b.x2);
break;
}
case ADJ_UP:
case ADJ_DOWN:
{
t.y1 = MIN(a.y1, b.y1);
t.y2 = MAX(a.y2, b.y2);
t.x1 = MAX(a.x1, b.x1);
t.x2 = MIN(a.x2, b.x2);
break;
}
}
return t;
}
LRect *LView::FindLargest(LRegion &r)
{
ThreadCheck();
int Pixels = 0;
LRect *Best = 0;
static LRect Final;
// do initial run through the list to find largest single region
for (LRect *i = r.First(); i; i = r.Next())
{
int Pix = i->X() * i->Y();
if (Pix > Pixels)
{
Pixels = Pix;
Best = i;
}
}
if (Best)
{
Final = *Best;
Pixels = Final.X() * Final.Y();
int LastPixels = Pixels;
LRect LastRgn = Final;
int ThisPixels = Pixels;
LRect ThisRgn = Final;
LRegion TempList;
for (LRect *i = r.First(); i; i = r.Next())
{
TempList.Union(i);
}
TempList.Subtract(Best);
do
{
LastPixels = ThisPixels;
LastRgn = ThisRgn;
// search for adjoining rectangles that maybe we can add
for (LRect *i = TempList.First(); i; i = TempList.Next())
{
int Adj = IsAdjacent(ThisRgn, *i);
if (Adj)
{
LRect t = JoinAdjacent(ThisRgn, *i, Adj);
int Pix = t.X() * t.Y();
if (Pix > ThisPixels)
{
ThisPixels = Pix;
ThisRgn = t;
TempList.Subtract(i);
}
}
}
} while (LastPixels < ThisPixels);
Final = ThisRgn;
}
else return 0;
return &Final;
}
LRect *LView::FindSmallestFit(LRegion &r, int Sx, int Sy)
{
ThreadCheck();
int X = 1000000;
int Y = 1000000;
LRect *Best = 0;
for (LRect *i = r.First(); i; i = r.Next())
{
if ((i->X() >= Sx && i->Y() >= Sy) &&
(i->X() < X || i->Y() < Y))
{
X = i->X();
Y = i->Y();
Best = i;
}
}
return Best;
}
LRect *LView::FindLargestEdge(LRegion &r, int Edge)
{
LRect *Best = 0;
ThreadCheck();
for (LRect *i = r.First(); i; i = r.Next())
{
if (!Best)
{
Best = i;
}
if
(
((Edge & GV_EDGE_TOP) && (i->y1 < Best->y1))
||
((Edge & GV_EDGE_RIGHT) && (i->x2 > Best->x2))
||
((Edge & GV_EDGE_BOTTOM) && (i->y2 > Best->y2))
||
((Edge & GV_EDGE_LEFT) && (i->x1 < Best->x1))
)
{
Best = i;
}
if
(
(
((Edge & GV_EDGE_TOP) && (i->y1 == Best->y1))
||
((Edge & GV_EDGE_BOTTOM) && (i->y2 == Best->y2))
)
&&
(
i->X() > Best->X()
)
)
{
Best = i;
}
if
(
(
((Edge & GV_EDGE_RIGHT) && (i->x2 == Best->x2))
||
((Edge & GV_EDGE_LEFT) && (i->x1 == Best->x1))
)
&&
(
i->Y() > Best->Y()
)
)
{
Best = i;
}
}
return Best;
}
LViewI *LView::FindReal(LPoint *Offset)
{
ThreadCheck();
if (Offset)
{
Offset->x = 0;
Offset->y = 0;
}
#if !LGI_VIEW_HANDLE
LViewI *w = GetWindow();
#endif
LViewI *p = d->Parent;
while (p &&
#if !LGI_VIEW_HANDLE
p != w
#else
!p->Handle()
#endif
)
{
if (Offset)
{
Offset->x += Pos.x1;
Offset->y += Pos.y1;
}
p = p->GetParent();
}
if (p &&
#if !LGI_VIEW_HANDLE
p == w
#else
p->Handle()
#endif
)
{
return p;
}
return NULL;
}
bool LView::HandleCapture(LView *Wnd, bool c)
{
ThreadCheck();
DEBUG_CAPTURE("%s::HandleCapture(%i)=%i\n", GetClass(), c, (int)(_Capturing == Wnd));
if (c)
{
if (_Capturing == Wnd)
{
DEBUG_CAPTURE(" %s already has capture\n", _Capturing?_Capturing->GetClass():0);
}
else
{
DEBUG_CAPTURE(" _Capturing=%s -> %s\n", _Capturing?_Capturing->GetClass():0, Wnd?Wnd->GetClass():0);
_Capturing = Wnd;
#if defined(__GTK_H__)
if (d->CaptureThread)
d->CaptureThread->Cancel();
d->CaptureThread = new LCaptureThread(this);
#elif WINNATIVE
LPoint Offset;
LViewI *v = _Capturing->Handle() ? _Capturing : FindReal(&Offset);
HWND h = v ? v->Handle() : NULL;
if (h)
SetCapture(h);
else
LAssert(0);
#elif defined(LGI_SDL)
#if SDL_VERSION_ATLEAST(2, 0, 4)
SDL_CaptureMouse(SDL_TRUE);
#else
LAppInst->CaptureMouse(true);
#endif
#endif
}
}
else if (_Capturing)
{
DEBUG_CAPTURE(" _Capturing=%s -> NULL\n", _Capturing?_Capturing->GetClass():0);
_Capturing = NULL;
#if defined(__GTK_H__)
if (d->CaptureThread)
{
d->CaptureThread->Cancel();
d->CaptureThread = NULL; // It'll delete itself...
}
#elif WINNATIVE
ReleaseCapture();
#elif defined(LGI_SDL)
#if SDL_VERSION_ATLEAST(2, 0, 4)
SDL_CaptureMouse(SDL_FALSE);
#else
LAppInst->CaptureMouse(false);
#endif
#endif
}
return true;
}
bool LView::IsCapturing()
{
ThreadCheck();
DEBUG_CAPTURE("%s::IsCapturing()=%i\n", GetClass(), (int)(_Capturing == this));
return _Capturing == this;
}
bool LView::Capture(bool c)
{
ThreadCheck();
DEBUG_CAPTURE("%s::Capture(%i)\n", GetClass());
return HandleCapture(this, c);
}
bool LView::Enabled()
{
ThreadCheck();
#if WINNATIVE
if (_View)
return IsWindowEnabled(_View) != 0;
#endif
return !TestFlag(GViewFlags, GWF_DISABLED);
}
void LView::Enabled(bool i)
{
ThreadCheck();
if (!i) SetFlag(GViewFlags, GWF_DISABLED);
else ClearFlag(GViewFlags, GWF_DISABLED);
#if LGI_VIEW_HANDLE && !defined(HAIKU)
if (_View)
{
#if WINNATIVE
EnableWindow(_View, i);
#elif defined LGI_CARBON
if (i)
{
OSStatus e = EnableControl(_View);
if (e) printf("%s:%i - Error enabling control (%i)\n", _FL, (int)e);
}
else
{
OSStatus e = DisableControl(_View);
if (e) printf("%s:%i - Error disabling control (%i)\n", _FL, (int)e);
}
#endif
}
#endif
Invalidate();
}
bool LView::Visible()
{
// This is a read only operation... which is kinda thread-safe...
// ThreadCheck();
#if WINNATIVE
if (_View)
/* This takes into account all the parent windows as well...
Which is kinda not what I want. I want this to reflect just
this window.
return IsWindowVisible(_View);
*/
return (GetWindowLong(_View, GWL_STYLE) & WS_VISIBLE) != 0;
#endif
return TestFlag(GViewFlags, GWF_VISIBLE);
}
void LView::Visible(bool v)
{
ThreadCheck();
if (v) SetFlag(GViewFlags, GWF_VISIBLE);
else ClearFlag(GViewFlags, GWF_VISIBLE);
#if defined(HAIKU)
LLocker lck(d->Hnd, _FL);
if (!IsAttached() || lck.Lock())
{
if (v)
d->Hnd->Show();
else
d->Hnd->Hide();
}
else LgiTrace("%s:%i - Can't lock.\n", _FL);
#elif LGI_VIEW_HANDLE
if (_View)
{
#if WINNATIVE
ShowWindow(_View, (v) ? SW_SHOWNORMAL : SW_HIDE);
#elif LGI_COCOA
LAutoPool Pool;
[_View.p setHidden:!v];
#elif LGI_CARBON
Boolean is = HIViewIsVisible(_View);
if (v != is)
{
OSErr e = HIViewSetVisible(_View, v);
if (e) printf("%s:%i - HIViewSetVisible(%p,%i) failed with %i (class=%s)\n",
_FL, _View, v, e, GetClass());
}
#endif
}
else
#endif
{
Invalidate();
}
}
bool LView::Focus()
{
ThreadCheck();
bool Has = false;
#if defined(__GTK_H__)
LWindow *w = GetWindow();
if (w)
{
bool Active = w->IsActive();
if (Active)
Has = w->GetFocus() == static_cast(this);
}
#elif defined(WINNATIVE)
if (_View)
{
HWND hFocus = GetFocus();
Has = hFocus == _View;
}
#elif LGI_COCOA
Has = TestFlag(WndFlags, GWF_FOCUS);
#elif LGI_CARBON
LWindow *w = GetWindow();
if (w)
{
ControlRef Cur;
OSErr e = GetKeyboardFocus(w->WindowHandle(), &Cur);
if (e)
LgiTrace("%s:%i - GetKeyboardFocus failed with %i\n", _FL, e);
else
Has = (Cur == _View);
}
#endif
#if !LGI_CARBON
if (Has)
SetFlag(WndFlags, GWF_FOCUS);
else
ClearFlag(WndFlags, GWF_FOCUS);
#endif
return Has;
}
void LView::Focus(bool i)
{
ThreadCheck();
if (i)
SetFlag(WndFlags, GWF_FOCUS);
else
ClearFlag(WndFlags, GWF_FOCUS);
auto *Wnd = GetWindow();
if (Wnd)
Wnd->SetFocus(this, i ? LWindow::GainFocus : LWindow::LoseFocus);
#if LGI_VIEW_HANDLE && !defined(HAIKU)
if (_View)
#endif
{
#if defined(LGI_SDL) || defined(__GTK_H__)
// Nop: Focus is all handled by Lgi's LWindow class.
#elif WINNATIVE
if (i)
{
HWND hCur = GetFocus();
if (hCur != _View)
{
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus %p (%-30s)\n", _FL, Handle(), Name());
}
SetFocus(_View);
}
}
else
{
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus(%p)\n", _FL, GetDesktopWindow());
}
SetFocus(GetDesktopWindow());
}
#elif defined LGI_CARBON
LViewI *Wnd = GetWindow();
if (Wnd && i)
{
OSErr e = SetKeyboardFocus(Wnd->WindowHandle(), _View, 1);
if (e)
{
// e = SetKeyboardFocus(Wnd->WindowHandle(), _View, kControlFocusNextPart);
// if (e)
{
HIViewRef p = HIViewGetSuperview(_View);
// errCouldntSetFocus
printf("%s:%i - SetKeyboardFocus failed: %i (%s, %p)\n", _FL, e, GetClass(), p);
}
}
// else printf("%s:%i - SetFocus v=%p(%s)\n", _FL, _View, GetClass());
}
else printf("%s:%i - no window?\n", _FL);
#endif
}
}
LDragDropSource *LView::DropSource(LDragDropSource *Set)
{
if (Set)
d->DropSource = Set;
return d->DropSource;
}
LDragDropTarget *LView::DropTarget(LDragDropTarget *Set)
{
if (Set)
d->DropTarget = Set;
return d->DropTarget;
}
#if defined LGI_CARBON
extern pascal OSStatus LgiViewDndHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData);
#endif
#if defined __GTK_H__
// Recursively add drag dest to all view and all children
bool GtkAddDragDest(LViewI *v, bool IsTarget)
{
if (!v) return false;
LWindow *w = v->GetWindow();
if (!w) return false;
auto wid = GtkCast(w->WindowHandle(), gtk_widget, GtkWidget);
if (IsTarget)
{
Gtk::gtk_drag_dest_set( wid,
(Gtk::GtkDestDefaults)0,
NULL,
0,
Gtk::GDK_ACTION_DEFAULT);
}
else
{
Gtk::gtk_drag_dest_unset(wid);
}
for (LViewI *c: v->IterateViews())
GtkAddDragDest(c, IsTarget);
return true;
}
#endif
bool LView::DropTarget(bool t)
{
ThreadCheck();
bool Status = false;
if (t) SetFlag(GViewFlags, GWF_DROP_TARGET);
else ClearFlag(GViewFlags, GWF_DROP_TARGET);
#if WINNATIVE
if (_View)
{
if (t)
{
if (!d->DropTarget)
DragAcceptFiles(_View, t);
else
Status = RegisterDragDrop(_View, (IDropTarget*) d->DropTarget) == S_OK;
}
else
{
if (_View && d->DropTarget)
Status = RevokeDragDrop(_View) == S_OK;
}
}
#elif defined MAC && !defined(LGI_SDL)
LWindow *Wnd = dynamic_cast(GetWindow());
if (Wnd)
{
Wnd->SetDragHandlers(t);
if (!d->DropTarget)
d->DropTarget = t ? Wnd : 0;
}
#if LGI_COCOA
LWindow *w = GetWindow();
if (w)
{
OsWindow h = w->WindowHandle();
if (h)
{
NSMutableArray *a = [[NSMutableArray alloc] init];
if (a)
{
[a addObject:(NSString*)kUTTypeItem];
for (id item in NSFilePromiseReceiver.readableDraggedTypes)
[a addObject:item];
[h.p.contentView registerForDraggedTypes:a];
[a release];
}
}
}
#elif LGI_CARBON
if (t)
{
static EventTypeSpec DragEvents[] =
{
{ kEventClassControl, kEventControlDragEnter },
{ kEventClassControl, kEventControlDragWithin },
{ kEventClassControl, kEventControlDragLeave },
{ kEventClassControl, kEventControlDragReceive },
};
if (!d->DndHandler)
{
OSStatus e = ::InstallControlEventHandler( _View,
NewEventHandlerUPP(LgiViewDndHandler),
GetEventTypeCount(DragEvents),
DragEvents,
(void*)this,
&d->DndHandler);
if (e) LgiTrace("%s:%i - InstallEventHandler failed (%i)\n", _FL, e);
}
SetControlDragTrackingEnabled(_View, true);
}
else
{
SetControlDragTrackingEnabled(_View, false);
}
#endif
#elif defined __GTK_H__
Status = GtkAddDragDest(this, t);
if (Status && !d->DropTarget)
d->DropTarget = t ? GetWindow() : 0;
#endif
return Status;
}
bool LView::Sunken()
{
// ThreadCheck();
#if WINNATIVE
return TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE);
#else
return TestFlag(GViewFlags, GWF_SUNKEN);
#endif
}
void LView::Sunken(bool i)
{
ThreadCheck();
#if WINNATIVE
if (i) SetFlag(d->WndExStyle, WS_EX_CLIENTEDGE);
else ClearFlag(d->WndExStyle, WS_EX_CLIENTEDGE);
if (_View)
SetWindowLong(_View, GWL_EXSTYLE, d->WndExStyle);
#else
if (i) SetFlag(GViewFlags, GWF_SUNKEN);
else ClearFlag(GViewFlags, GWF_SUNKEN);
#endif
if (i)
{
if (!_BorderSize)
_BorderSize = 2;
}
else _BorderSize = 0;
}
bool LView::Flat()
{
// ThreadCheck();
#if WINNATIVE
return !TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE) &&
!TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE);
#else
return !TestFlag(GViewFlags, GWF_SUNKEN) &&
!TestFlag(GViewFlags, GWF_RAISED);
#endif
}
void LView::Flat(bool i)
{
ThreadCheck();
#if WINNATIVE
ClearFlag(d->WndExStyle, (WS_EX_CLIENTEDGE|WS_EX_WINDOWEDGE));
#else
ClearFlag(GViewFlags, (GWF_RAISED|GWF_SUNKEN));
#endif
}
bool LView::Raised()
{
// ThreadCheck();
#if WINNATIVE
return TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE);
#else
return TestFlag(GViewFlags, GWF_RAISED);
#endif
}
void LView::Raised(bool i)
{
ThreadCheck();
#if WINNATIVE
if (i) SetFlag(d->WndExStyle, WS_EX_WINDOWEDGE);
else ClearFlag(d->WndExStyle, WS_EX_WINDOWEDGE);
#else
if (i) SetFlag(GViewFlags, GWF_RAISED);
else ClearFlag(GViewFlags, GWF_RAISED);
#endif
if (i)
{
if (!!_BorderSize)
_BorderSize = 2;
}
else _BorderSize = 0;
}
int LView::GetId()
{
// This is needed by SendNotify function which is thread safe.
// So no thread safety check here.
return d->CtrlId;
}
void LView::SetId(int i)
{
// This is needed by SendNotify function which is thread safe.
// So no thread safety check here.
d->CtrlId = i;
#if WINNATIVE
if (_View)
SetWindowLong(_View, GWL_ID, d->CtrlId);
#elif defined __GTK_H__
#elif defined MAC
#endif
}
bool LView::GetTabStop()
{
ThreadCheck();
#if WINNATIVE
return TestFlag(d->WndStyle, WS_TABSTOP);
#else
return d->TabStop;
#endif
}
void LView::SetTabStop(bool b)
{
ThreadCheck();
#if WINNATIVE
- if (b)
- SetFlag(d->WndStyle, WS_TABSTOP);
- else
- ClearFlag(d->WndStyle, WS_TABSTOP);
+ if (b)
+ SetFlag(d->WndStyle, WS_TABSTOP);
+ else
+ ClearFlag(d->WndStyle, WS_TABSTOP);
#else
- d->TabStop = b;
- #endif
- #if 0 // def __GTK_H__
- if (_View)
- {
- #if GtkVer(2, 18)
- ThreadCheck();
- gtk_widget_set_can_focus(_View, b);
- #else
- LgiTrace("Error: no api to set tab stop.\n");
- #endif
- }
+ d->TabStop = b;
#endif
}
int64 LView::GetCtrlValue(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
- if (!w)
- printf("%s:%i - Ctrl %i not found.\n", _FL, Id);
return (w) ? w->Value() : 0;
}
void LView::SetCtrlValue(int Id, int64 i)
{
ThreadCheck();
LViewI *w = FindControl(Id);
- // if (!w) printf("%s:%i - Ctrl %i not found.\n", _FL, Id);
if (w) w->Value(i);
}
const char *LView::GetCtrlName(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
- // if (!w) printf("%s:%i - Ctrl %i not found.\n", _FL, Id);
return (w) ? w->Name() : 0;
}
void LView::SetCtrlName(int Id, const char *s)
{
if (!IsAttached() || InThread())
{
if (auto w = FindControl(Id))
w->Name(s);
}
else
{
PostEvent( M_SET_CTRL_NAME,
(LMessage::Param)Id,
(LMessage::Param)new LString(s));
}
}
bool LView::GetCtrlEnabled(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
- // if (!w) printf("%s:%i - Ctrl %i not found.\n", _FL, Id);
return (w) ? w->Enabled() : 0;
}
void LView::SetCtrlEnabled(int Id, bool Enabled)
{
if (!IsAttached() || InThread())
{
if (auto w = FindControl(Id))
w->Enabled(Enabled);
}
else
{
PostEvent( M_SET_CTRL_ENABLE,
(LMessage::Param)Id,
(LMessage::Param)Enabled);
}
}
bool LView::GetCtrlVisible(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
if (!w)
LgiTrace("%s:%i - Ctrl %i not found.\n", _FL, Id);
return (w) ? w->Visible() : 0;
}
void LView::SetCtrlVisible(int Id, bool v)
{
if (!IsAttached() || InThread())
{
if (auto w = FindControl(Id))
w->Visible(v);
}
else
{
PostEvent( M_SET_CTRL_VISIBLE,
(LMessage::Param)Id,
(LMessage::Param)v);
}
}
bool LView::AttachChildren()
{
for (auto c: Children)
{
bool a = c->IsAttached();
if (!a)
{
if (!c->Attach(this))
{
LgiTrace("%s:%i - failed to attach %s\n", _FL, c->GetClass());
return false;
}
}
}
return true;
}
LFont *LView::GetFont()
{
if (!d->Font &&
d->Css &&
LResources::GetLoadStyles())
{
LFontCache *fc = LAppInst->GetFontCache();
if (fc)
{
LFont *f = fc->GetFont(d->Css);
if (f)
{
if (d->FontOwnType == GV_FontOwned)
DeleteObj(d->Font);
d->Font = f;
d->FontOwnType = GV_FontCached;
}
}
}
return d->Font ? d->Font : LSysFont;
}
void LView::SetFont(LFont *Font, bool OwnIt)
{
bool Change = d->Font != Font;
if (Change)
{
if (d->FontOwnType == GV_FontOwned)
{
LAssert(d->Font != LSysFont);
DeleteObj(d->Font);
}
d->FontOwnType = OwnIt ? GV_FontOwned : GV_FontPtr;
d->Font = Font;
#if WINNATIVE
if (_View)
SendMessage(_View, WM_SETFONT, (WPARAM) (Font ? Font->Handle() : 0), 0);
#endif
for (LViewI *p = GetParent(); p; p = p->GetParent())
{
LTableLayout *Tl = dynamic_cast(p);
if (Tl)
{
Tl->InvalidateLayout();
break;
}
}
Invalidate();
}
}
bool LView::IsOver(LMouse &m)
{
return (m.x >= 0) &&
(m.y >= 0) &&
(m.x < Pos.X()) &&
(m.y < Pos.Y());
}
bool LView::WindowVirtualOffset(LPoint *Offset)
{
bool Status = false;
if (Offset)
{
Offset->x = 0;
Offset->y = 0;
for (LViewI *Wnd = this; Wnd; Wnd = Wnd->GetParent())
{
#if !LGI_VIEW_HANDLE
auto IsWnd = dynamic_cast(Wnd);
if (!IsWnd)
#else
if (!Wnd->Handle())
#endif
{
LRect r = Wnd->GetPos();
LViewI *Par = Wnd->GetParent();
if (Par)
{
LRect c = Par->GetClient(false);
Offset->x += r.x1 + c.x1;
Offset->y += r.y1 + c.y1;
}
else
{
Offset->x += r.x1;
Offset->y += r.y1;
}
Status = true;
}
else break;
}
}
return Status;
}
LString _ViewDesc(LViewI *v)
{
LString s;
s.Printf("%s/%s/%i", v->GetClass(), v->Name(), v->GetId());
return s;
}
LViewI *LView::WindowFromPoint(int x, int y, int DebugDepth)
{
char Tabs[64];
if (DebugDepth)
{
memset(Tabs, 9, DebugDepth);
Tabs[DebugDepth] = 0;
LgiTrace("%s%s %i\n", Tabs, _ViewDesc(this).Get(), Children.Length());
}
// We iterate over the child in reverse order because if they overlap the
// end of the list is on "top". So they should get the click or whatever
// before the the lower windows.
auto it = Children.rbegin();
int n = (int)Children.Length() - 1;
for (LViewI *c = *it; c; c = *--it)
{
LRect CPos = c->GetPos();
if (CPos.Overlap(x, y) && c->Visible())
{
LRect CClient;
CClient = c->GetClient(false);
int Ox = CPos.x1 + CClient.x1;
int Oy = CPos.y1 + CClient.y1;
if (DebugDepth)
{
LgiTrace("%s[%i] %s Pos=%s Client=%s m(%i,%i)->(%i,%i)\n",
Tabs, n--,
_ViewDesc(c).Get(),
CPos.GetStr(),
CClient.GetStr(),
x, y,
x - Ox, y - Oy);
}
LViewI *Child = c->WindowFromPoint(x - Ox, y - Oy, DebugDepth ? DebugDepth + 1 : 0);
if (Child)
return Child;
}
else if (DebugDepth)
{
LgiTrace("%s[%i] MISSED %s Pos=%s m(%i,%i)\n", Tabs, n--, _ViewDesc(c).Get(), CPos.GetStr(), x, y);
}
}
if (x >= 0 && y >= 0 && x < Pos.X() && y < Pos.Y())
{
return this;
}
return NULL;
}
LColour LView::StyleColour(int CssPropType, LColour Default, int Depth)
{
LColour c = Default;
if ((CssPropType >> 8) == LCss::TypeColor)
{
LViewI *v = this;
for (int i=0; v && iGetParent())
{
auto Style = v->GetCss();
if (Style)
{
auto Colour = (LCss::ColorDef*) Style->PropAddress((LCss::PropType)CssPropType);
if (Colour)
{
if (Colour->Type == LCss::ColorRgb)
{
c.Set(Colour->Rgb32, 32);
break;
}
else if (Colour->Type == LCss::ColorTransparent)
{
c.Empty();
break;
}
}
}
if (dynamic_cast(v) ||
dynamic_cast(v))
break;
}
}
return c;
}
bool LView::InThread()
{
#if WINNATIVE
HWND Hnd = _View;
for (LViewI *p = GetParent(); p && !Hnd; p = p->GetParent())
{
Hnd = p->Handle();
}
auto CurThreadId = GetCurrentThreadId();
auto GuiThreadId = LAppInst->GetGuiThreadId();
DWORD ViewThread = Hnd ? GetWindowThreadProcessId(Hnd, NULL) : GuiThreadId;
return CurThreadId == ViewThread;
#elif defined(HAIKU)
return true;
#else
OsThreadId Me = GetCurrentThreadId();
OsThreadId Gui = LAppInst ? LAppInst->GetGuiThreadId() : 0;
#if 0
if (Gui != Me)
LgiTrace("%s:%i - Out of thread:"
#ifdef LGI_COCOA
"%llx, %llx"
#else
"%x, %x"
#endif
"\n", _FL, Gui, Me);
#endif
return Gui == Me;
#endif
}
bool LView::PostEvent(int Cmd, LMessage::Param a, LMessage::Param b)
{
#ifdef LGI_SDL
return LPostEvent(this, Cmd, a, b);
#elif defined(HAIKU)
if (!d || !d->Hnd || !d->Hnd->LockLooper())
return false;
BMessage *m = new BMessage(Cmd);
if (!m)
return false;
m->AddUInt64(LMessage::PropNames[0], a);
m->AddUInt64(LMessage::PropNames[1], b);
auto w = d && d->Hnd ? d->Hnd->Window() : NULL;
status_t r = w->PostMessage(m);
d->Hnd->UnlockLooper();
return r == B_OK;
#elif WINNATIVE
if (!_View)
return false;
BOOL Res = ::PostMessage(_View, Cmd, a, b);
if (!Res)
{
auto Err = GetLastError();
int asd=0;
}
return Res != 0;
#elif !LGI_VIEW_HANDLE
return LAppInst->PostEvent(this, Cmd, a, b);
#else
if (_View)
return LPostEvent(_View, Cmd, a, b);
LAssert(0);
return false;
#endif
}
bool LView::Invalidate(LRegion *r, bool Repaint, bool NonClient)
{
if (r)
{
for (int i=0; iLength(); i++)
{
bool Last = i == r->Length()-1;
Invalidate((*r)[i], Last ? Repaint : false, NonClient);
}
return true;
}
return false;
}
LButton *FindDefault(LViewI *w)
{
LButton *But = 0;
for (auto c: w->IterateViews())
{
But = dynamic_cast(c);
if (But && But->Default())
{
break;
}
But = FindDefault(c);
if (But)
{
break;
}
}
return But;
}
bool LView::Name(const char *n)
{
LBase::Name(n);
#if LGI_VIEW_HANDLE && !defined(HAIKU)
if (_View)
{
#if WINNATIVE
auto Temp = LBase::NameW();
SetWindowTextW(_View, Temp ? Temp : L"");
#endif
}
#endif
Invalidate();
return true;
}
const char *LView::Name()
{
#if WINNATIVE
if (_View)
{
LView::NameW();
}
#endif
return LBase::Name();
}
bool LView::NameW(const char16 *n)
{
LBase::NameW(n);
#if WINNATIVE
if (_View && n)
{
auto Txt = LBase::NameW();
SetWindowTextW(_View, Txt);
}
#endif
Invalidate();
return true;
}
const char16 *LView::NameW()
{
#if WINNATIVE
if (_View)
{
int Length = GetWindowTextLengthW(_View);
if (Length > 0)
{
char16 *Buf = new char16[Length+1];
if (Buf)
{
Buf[0] = 0;
int Chars = GetWindowTextW(_View, Buf, Length+1);
Buf[Chars] = 0;
LBase::NameW(Buf);
}
DeleteArray(Buf);
}
else
{
LBase::NameW(0);
}
}
#endif
return LBase::NameW();
}
LViewI *LView::FindControl(int Id)
{
LAssert(Id != -1);
if (GetId() == Id)
{
return this;
}
for (auto c : Children)
{
LViewI *Ctrl = c->FindControl(Id);
if (Ctrl)
{
return Ctrl;
}
}
return 0;
}
LPoint LView::GetMinimumSize()
{
return d->MinimumSize;
}
void LView::SetMinimumSize(LPoint Size)
{
d->MinimumSize = Size;
bool Change = false;
LRect p = Pos;
if (X() < d->MinimumSize.x)
{
p.x2 = p.x1 + d->MinimumSize.x - 1;
Change = true;
}
if (Y() < d->MinimumSize.y)
{
p.y2 = p.y1 + d->MinimumSize.y - 1;
Change = true;
}
if (Change)
{
SetPos(p);
}
}
bool LView::SetColour(LColour &c, bool Fore)
{
LCss *css = GetCss(true);
if (!css)
return false;
if (Fore)
{
if (c.IsValid())
css->Color(LCss::ColorDef(LCss::ColorRgb, c.c32()));
else
css->DeleteProp(LCss::PropColor);
}
else
{
if (c.IsValid())
css->BackgroundColor(LCss::ColorDef(LCss::ColorRgb, c.c32()));
else
css->DeleteProp(LCss::PropBackgroundColor);
}
return true;
}
/*
bool LView::SetCssStyle(const char *CssStyle)
{
if (!d->Css && !d->Css.Reset(new LCss))
return false;
const char *Defs = CssStyle;
bool b = d->Css->Parse(Defs, LCss::ParseRelaxed);
if (b && d->FontOwnType == GV_FontCached)
{
d->Font = NULL;
d->FontOwnType = GV_FontPtr;
}
return b;
}
*/
void LView::SetCss(LCss *css)
{
d->Css.Reset(css);
}
LCss *LView::GetCss(bool Create)
{
if (Create && !d->Css)
d->Css.Reset(new LCss);
if (d->CssDirty && d->Css)
{
const char *Defs = d->Styles;
if (d->Css->Parse(Defs, LCss::ParseRelaxed))
d->CssDirty = false;
}
return d->Css;
}
LPoint &LView::GetWindowBorderSize()
{
static LPoint s;
ZeroObj(s);
#if WINNATIVE
if (_View)
{
RECT Wnd, Client;
GetWindowRect(Handle(), &Wnd);
GetClientRect(Handle(), &Client);
s.x = (Wnd.right-Wnd.left) - (Client.right-Client.left);
s.y = (Wnd.bottom-Wnd.top) - (Client.bottom-Client.top);
}
#elif defined __GTK_H__
#elif defined MAC
s.x = 0;
s.y = 22;
#endif
return s;
}
#ifdef _DEBUG
#if defined(LGI_CARBON)
void DumpHiview(HIViewRef v, int Depth = 0)
{
char Sp[256];
memset(Sp, ' ', Depth << 2);
Sp[Depth<<2] = 0;
printf("%sHIView=%p", Sp, v);
if (v)
{
Boolean vis = HIViewIsVisible(v);
Boolean en = HIViewIsEnabled(v, NULL);
HIRect pos;
HIViewGetFrame(v, &pos);
char cls[128];
ZeroObj(cls);
GetControlProperty(v, 'meme', 'clas', sizeof(cls), NULL, cls);
printf(" vis=%i en=%i pos=%g,%g-%g,%g cls=%s",
vis, en,
pos.origin.x, pos.origin.y, pos.size.width, pos.size.height,
cls);
}
printf("\n");
for (HIViewRef c = HIViewGetFirstSubview(v); c; c = HIViewGetNextView(c))
{
DumpHiview(c, Depth + 1);
}
}
#elif defined(__GTK_H__)
void DumpGtk(Gtk::GtkWidget *w, Gtk::gpointer Depth = NULL)
{
using namespace Gtk;
if (!w)
return;
char Sp[65] = {0};
if (Depth)
memset(Sp, ' ', *((int*)Depth)*2);
auto *Obj = G_OBJECT(w);
LViewI *View = (LViewI*) g_object_get_data(Obj, "LViewI");
GtkAllocation a;
gtk_widget_get_allocation(w, &a);
LgiTrace("%s%p(%s) = %i,%i-%i,%i\n", Sp, w, View?View->GetClass():G_OBJECT_TYPE_NAME(Obj), a.x, a.y, a.width, a.height);
if (GTK_IS_CONTAINER(w))
{
auto *c = GTK_CONTAINER(w);
if (c)
{
int Next = Depth ? *((int*)Depth)+1 : 1;
gtk_container_foreach(c, DumpGtk, &Next);
}
}
}
#elif defined(HAIKU)
template
void _Dump(int Depth, T *v)
{
LString Sp, Name;
Sp.Length(Depth<<1);
memset(Sp.Get(), ' ', Depth<<1);
LRect Frame = v->Frame();
LViewPrivate *Priv = dynamic_cast(v);
if (Priv)
Name = Priv->View->GetClass();
printf("%s### %p::%s frame=%s vis=%i\n",
Sp.Get(), v, Name.Get(), Frame.GetStr(), !v->IsHidden());
for (int32 i=0; iCountChildren(); i++)
{
_Dump(Depth + 1, v->ChildAt(i));
}
}
#endif
void LView::_Dump(int Depth)
{
char Sp[65] = {0};
memset(Sp, ' ', Depth*2);
#if 0
char s[256];
sprintf_s(s, sizeof(s), "%s%p::%s %s (_View=%p)\n", Sp, this, GetClass(), GetPos().GetStr(), _View);
LgiTrace(s);
List::I i = Children.Start();
for (LViewI *c = *i; c; c = *++i)
{
LView *v = c->GetGView();
if (v)
v->_Dump(Depth+1);
}
#elif defined(LGI_CARBON)
DumpHiview(_View);
#elif defined(__GTK_H__)
// DumpGtk(_View);
#elif defined(HAIKU)
LLocker lck(WindowHandle(), _FL);
if (lck.Lock())
::_Dump(0, WindowHandle());
#endif
}
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
static LArray *AllFactories = NULL;
#if defined(WIN32)
static HANDLE FactoryEvent;
#else
static pthread_once_t FactoryOnce = PTHREAD_ONCE_INIT;
static void GFactoryInitFactories()
{
AllFactories = new LArray;
}
#endif
LViewFactory::LViewFactory()
{
#if defined(WIN32)
char16 Name[64];
swprintf_s(Name, CountOf(Name), L"LgiFactoryEvent.%i", GetCurrentProcessId());
HANDLE h = CreateEventW(NULL, false, false, Name);
DWORD err = GetLastError();
if (err != ERROR_ALREADY_EXISTS)
{
FactoryEvent = h;
AllFactories = new LArray;
}
else
{
LAssert(AllFactories != NULL);
}
#else
pthread_once(&FactoryOnce, GFactoryInitFactories);
#endif
if (AllFactories)
AllFactories->Add(this);
}
LViewFactory::~LViewFactory()
{
if (AllFactories)
{
AllFactories->Delete(this);
if (AllFactories->Length() == 0)
{
DeleteObj(AllFactories);
#if defined(WIN32)
CloseHandle(FactoryEvent);
#endif
}
}
}
LView *LViewFactory::Create(const char *Class, LRect *Pos, const char *Text)
{
if (ValidStr(Class) && AllFactories)
{
for (int i=0; iLength(); i++)
{
LView *v = (*AllFactories)[i]->NewView(Class, Pos, Text);
if (v)
{
return v;
}
}
}
return 0;
}
#ifdef _DEBUG
#if defined(__GTK_H__)
using namespace Gtk;
#include "LgiWidget.h"
#endif
void LView::Debug()
{
_Debug = true;
#if defined LGI_COCOA
d->ClassName = GetClass();
#endif
}
#endif