diff --git a/Lgi_vs2015.vcxproj b/Lgi_vs2015.vcxproj
--- a/Lgi_vs2015.vcxproj
+++ b/Lgi_vs2015.vcxproj
@@ -1,694 +1,693 @@
Debug
Win32
Debug
x64
ReleaseNoOptimize
Win32
ReleaseNoOptimize
x64
Release
Win32
Release
x64
Lgi
{95DF9CA4-6D37-4A85-A648-80C2712E0DA1}
10.0.19041.0
DynamicLibrary
v140
false
Unicode
DynamicLibrary
v140
false
Unicode
DynamicLibrary
v140
false
Unicode
DynamicLibrary
v140
false
Unicode
DynamicLibrary
v140
false
Unicode
DynamicLibrary
v140
false
Unicode
<_ProjectFileVersion>12.0.30501.0
.\Lib\
$(Platform)$(Configuration)14\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi14x32
.\Lib\
$(Platform)$(Configuration)14\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi14x64
.\Lib\
$(Platform)$(Configuration)14\
true
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi14x32d
.\Lib\
$(Platform)$(Configuration)14\
true
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi14x64d
.\Lib\
$(Platform)$(Configuration)14\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi14x32nop
.\Lib\
$(Platform)$(Configuration)14\
false
$(VC_IncludePath);$(WindowsSDK_IncludePath);..\..\..\CodeLib\libiconv-1.14\include
Lgi14x64nop
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;%(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;%(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;%(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_vs2015.vcxproj.filters b/Lgi_vs2015.vcxproj.filters
--- a/Lgi_vs2015.vcxproj.filters
+++ b/Lgi_vs2015.vcxproj.filters
@@ -1,806 +1,803 @@
{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
{4472f483-982a-4fb1-8995-6dc204cd81ae}
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\Interface
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Core\Threads
Source Files\Core\Variant
Source Files\Graphics
Source Files\Dialogs
Source Files\Interface
Source Files\Interface
Source Files\Network
Source Files\Widgets\Native Windows
Source Files\Widgets\Native Windows
Source Files\Text
Source Files\Graphics
Source Files\Graphics
Source Files\Widgets\Native Windows
Source Files\Text
Source Files\Text
Source Files\Core\DateTime
Source Files\Widgets\Native Windows
Source Files\Graphics\Font
Source Files\Interface
Source Files\Interface
Source Files\Interface
Source Files\Widgets\Native Windows
Source Files\General
Source Files\Core\File
Source Files\Core\File
Source Files\Dialogs
Source Files\Graphics\Filters
Source Files\Dialogs
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics\Font
Source Files\Graphics
Source Files\Core
Source Files\Interface
Source Files\Graphics
Source Files\Dialogs
Source Files\General
Source Files\General
Source Files\Core\Libraries
Source Files\Dialogs
Source Files\Network
Source Files\Core\Memory Subsystem
Source Files\Core\Memory Subsystem
Source Files\Core\Memory Subsystem
Source Files\General
Source Files\Interface
Source Files\Core\Mutex
Source Files\Network
Source Files\Network
Source Files\Interface
Source Files\General
Source Files\General
Source Files\Graphics
Source Files\Interface
Source Files\Graphics\Surfaces
Source Files\Widgets\Native Windows
Source Files\Widgets\Native Windows
Source Files\General
Source Files\Graphics
Source Files\Widgets\Native Windows
Source Files\Core\Memory Subsystem
Source Files\Text
Source Files\Interface
Source Files\Core\Process
Source Files\Graphics\Surfaces
Source Files\Core\Threads
Source Files\Core\Threads
Source Files\Core\Memory Subsystem
Source Files\Graphics\Font
Source Files\Text
Source Files\Text
Source Files\Network
Source Files\Text
Source Files\Text
Source Files\Widgets\TextViews
Source Files\Widgets\TextViews
Source Files
Source Files
Source Files
Source Files\Graphics\Font
Source Files\Graphics\Surfaces
Source Files\Graphics\Surfaces
Source Files\Graphics\Surfaces
Source Files\Widgets\Container
Source Files\Widgets\Container
Source Files\Widgets\Container
Source Files\Widgets\Layout
Source Files\Widgets\Layout
Source Files\Widgets\Layout
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Interface
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Dialogs
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\Widgets\Layout
Source Files\Widgets\Xp
Source Files\Widgets\Container
Source Files\Widgets\Xp
Source Files\Widgets\Container
Source Files\Widgets\Xp
Source Files\Widgets\Xp
Source Files\General\Hash
Source Files\General\Hash
-
- 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
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Source Files\Core\Memory Subsystem
Source Files\Widgets\TextViews
Source Files\Widgets\TextViews
Header Files
Header Files
Source Files\Graphics\Font
+
+ Source Files\Interface
+
\ No newline at end of file
diff --git a/include/lgi/common/View.h b/include/lgi/common/View.h
--- a/include/lgi/common/View.h
+++ b/include/lgi/common/View.h
@@ -1,796 +1,822 @@
#pragma once
#if defined(LGI_CARBON)
LgiFunc void DumpHnd(HIViewRef v, int depth = 0);
#elif LGI_COCOA && defined(__OBJC__)
#include "LCocoaView.h"
#endif
/// \brief The base class for all windows in the GUI.
///
/// This is the core object that all on screen windows inherit from. It encapsulates
/// a HWND on Win32, a GtkWidget on Linux, and a NSView for Mac OS X. Used by
/// itself it's not a top level window, for that see the LWindow class.
///
/// To create a top level window see LWindow or LDialog.
///
/// For a LView with scroll bars use LLayout.
///
class LgiClass LView : virtual public LViewI, virtual public LBase
{
friend class LWindow;
friend class LLayout;
friend class LControl;
friend class LMenu;
friend class LSubMenu;
friend class LScrollBar;
friend class LDialog;
friend class LDragDropTarget;
friend class LPopup;
friend class LWindowPrivate;
friend class LViewPrivate;
friend bool SysOnKey(LView *w, LMessage *m);
#if defined(__GTK_H__)
friend Gtk::gboolean lgi_widget_draw(Gtk::GtkWidget *widget, Gtk::cairo_t *cr);
friend Gtk::gboolean lgi_widget_click(Gtk::GtkWidget *widget, Gtk::GdkEventButton *ev);
friend Gtk::gboolean lgi_widget_motion(Gtk::GtkWidget *widget, Gtk::GdkEventMotion *ev);
friend Gtk::gboolean GViewCallback(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, LView *view);
friend Gtk::gboolean PopupEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, class LPopup *This);
friend Gtk::gboolean GtkViewCallback(Gtk::GtkWidget *widget, Gtk::GdkEvent *event, LView *This);
virtual Gtk::gboolean OnGtkEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event);
public:
virtual void OnGtkRealize();
virtual void OnGtkDelete();
private:
#endif
#if defined WIN32
friend class LWindowsClass;
friend class LCombo;
friend LRESULT CALLBACK DlgRedir(OsView hWnd, UINT m, WPARAM a, LPARAM b);
static void CALLBACK TimerProc(OsView hwnd, UINT uMsg, UINT_PTR idEvent, uint32_t dwTime);
#elif defined MAC
#if LGI_COCOA
#elif LGI_CARBON
friend OSStatus LgiWindowProc(EventHandlerCallRef, EventRef, void *);
friend OSStatus LgiRootCtrlProc(EventHandlerCallRef, EventRef, void *);
friend OSStatus CarbonControlProc(EventHandlerCallRef, EventRef, void *);
friend OSStatus GViewProc(EventHandlerCallRef, EventRef, void *);
friend OSStatus LgiViewDndHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData);
#endif
#elif defined HAIKU
template friend class LBView;
#endif
#if defined(LGI_SDL)
friend Uint32 SDL_PulseCallback(Uint32 interval, LView *v);
friend class LApp;
#endif
LRect Pos;
int _InLock = 0;
protected:
class LViewPrivate *d = NULL;
#if LGI_VIEW_HANDLE && !defined(HAIKU)
OsView _View; // OS specific handle to view object
#endif
LView *_Window = NULL;
#ifndef HAIKU
LMutex *_Lock = NULL;
#endif
uint16 _BorderSize = 0;
uint16 _IsToolBar = 0;
int WndFlags = 0;
static LViewI *_Capturing;
static LViewI *_Over;
#ifndef LGI_VIEW_HASH
#error "Define LGI_VIEW_HASH to 0 or 1"
#endif
#if LGI_VIEW_HASH
public:
enum LockOp
{
OpCreate,
OpDelete,
OpExists,
};
static bool LockHandler(LViewI *v, LockOp Op);
#endif
protected:
#if defined WINNATIVE
uint32_t GetStyle();
void SetStyle(uint32_t i);
uint32_t GetExStyle();
void SetExStyle(uint32_t i);
uint32_t GetDlgCode();
void SetDlgCode(uint32_t i);
/// \brief Gets the win32 class passed to CreateWindowEx()
const char *GetClassW32();
/// \brief Sets the win32 class passed to CreateWindowEx()
void SetClassW32(const char *s);
/// \brief Creates a class to pass to CreateWindowEx(). If this methed is not
/// explicitly called then the string from GetClass() is used to create a class,
/// which is usually the name of the object.
LWindowsClass *CreateClassW32(const char *Class = 0, HICON Icon = 0, int AddStyles = 0);
virtual int SysOnNotify(int Msg, int Code) { return 0; }
#elif defined MAC
bool _Attach(LViewI *parent);
#if LGI_COCOA
public:
LPoint Flip(LPoint p);
LRect Flip(LRect p);
virtual void OnDealloc();
#elif LGI_CARBON
OsView _CreateCustomView();
virtual bool _OnGetInfo(HISize &size, HISize &line, HIRect &bounds, HIPoint &origin) { return false; }
virtual void _OnScroll(HIPoint &origin) {}
#endif
#endif
#if !WINNATIVE
LView *&PopupChild();
virtual bool _Mouse(LMouse &m, bool Move);
void _Focus(bool f);
#endif
#if LGI_COCOA
protected:
#endif
// Complex Region searches
/// Finds the largest rectangle in the region
LRect *FindLargest(LRegion &r);
/// Finds the smallest rectangle that would fit a window 'Sx' by 'Sy'
LRect *FindSmallestFit(LRegion &r, int Sx, int Sy);
/// Finds the largest rectangle on the specified
LRect *FindLargestEdge
(
/// The region to search
LRegion &r,
/// The edge to look at:
/// \sa GV_EDGE_TOP, GV_EDGE_RIGHT, GV_EDGE_BOTTOM or GV_EDGE_LEFT
int Edge
);
virtual void _Delete();
LViewI *FindReal(LPoint *Offset = 0);
bool HandleCapture(LView *Wnd, bool c);
virtual bool OnViewMouse(LView *v, LMouse &m) override { return true; }
virtual bool OnViewKey(LView *v, LKey &k) override { return false; }
virtual void OnNcPaint(LSurface *pDC, LRect &r);
/// List of children views.
List Children;
#if defined(LGI_SDL) || defined(LGI_COCOA)
public:
#endif
virtual void _Paint(LSurface *pDC = NULL, LPoint *Offset = NULL, LRect *Update = NULL);
public:
/// \brief Creates a view/window.
///
/// On non-Win32 platforms the default argument is the class that redirects the
/// C++ virtual event handlers to the LView handlers. Which is usually the
/// 'DefaultOsView' class. If you pass NULL in a DefaultOsView will be created to
/// do the job.
LView
(
/// The handle that the OS knows the window by
OsView wnd = NULL
);
/// Destructor
virtual ~LView();
/// Returns the OS handle of the view
#if defined(HAIKU)
OsView Handle() const;
#elif LGI_VIEW_HANDLE
OsView Handle() const { return _View; }
#endif
/// Returns the ptr to a LView
LView *GetGView() override { return this; }
/// Returns the OS handle of the top level window
OsWindow WindowHandle() override;
// Attaching windows / heirarchy
bool AddView(LViewI *v, int Where = -1) override;
bool DelView(LViewI *v) override;
bool HasView(LViewI *v) override;
LArray IterateViews() override;
/// \brief Attaches the view to a parent view.
///
/// Each LView starts in an un-attached state. When you attach it to a Parent LView
/// the view gains a OS-specific handle and becomes visible on the screen (if the
/// Visible() property is TRUE). However if a view is inserted into the Children list
/// of a LView and it's parent pointer is set correctly it will still paint on the
/// screen without the OS knowing about it. This is known in Lgi as a "virtual window"
/// and is primarily used to cut down on windowing resources. Mouse clicks are handled
/// by the parent window and passed down to the virtual children. Virtual children
/// are somewhat limited. They can't receive focus, or participate in drag and drop
/// operations. If you want to see an example have a look at the LToolBar code.
virtual bool Attach
(
/// The parent view or NULL for a top level window
LViewI *p
) override;
/// Attachs all the views in the Children list if not already attached.
virtual bool AttachChildren() override;
/// Detachs a window from it's parent.
virtual bool Detach() override;
/// Returns true if the window is attached
virtual bool IsAttached() override;
/// Destroys the window async
virtual void Quit(bool DontDelete = false) override;
// Properties
/// Gets the top level window that this view belongs to
LWindow *GetWindow() override;
/// Gets the parent view.
LViewI *GetParent() override;
/// \brief Sets the parent view.
///
/// This doesn't attach the window so that it will display. You should use LView::Attach for that.
virtual void SetParent(LViewI *p) override;
/// Sends a notification to the notify target or the parent chain
void SendNotify(LNotification n = LNotifyValueChanged) override;
/// Gets the window that receives event notifications
LViewI *GetNotify() override;
/// \brief Sets the view to receive event notifications.
///
/// The notify window will receive events when this view changes. By
/// default the parent view receives the events.
virtual void SetNotify(LViewI *n) override;
/// \brief Each top level window (LWindow) has a lock. By calling this function
/// you lock the whole LWindow and all it's children.
bool Lock
(
/// The file name of the caller
const char *file,
/// The line number of the caller
int line,
/// The timeout in milli-seconds or -1 to block until locked.
int TimeOut = -1
) override;
/// Unlocks the LWindow and that this view belongs to.
void Unlock() override;
/// Add this view to the event target sink dispatch hash table.
/// This allows you to use PostThreadEvent with a handle. Which
/// is safe even if the object is deleted (unlike the PostEvent
/// member function).
///
/// Calling this multiple times only adds the view once, but it
/// returns the same handle each time.
/// The view is automatically removed from the dispatch on
/// deletion.
///
/// \returns the handle for PostThreadEvent.
int AddDispatch() override;
/// Called to process every message received by this window.
LMessage::Result OnEvent(LMessage *Msg) override;
/// true if the view is enabled
bool Enabled() override;
/// Sets the enabled state
void Enabled(bool e) override;
/// true if the view is visible
bool Visible() override;
/// Hides/Shows the view
void Visible
(
/// True if you want to show the view, False to hide the view/
bool v
) override;
/// true if the view has keyboard focus
bool Focus() override;
/// Sets the keyboard focus state on the view.
void Focus(bool f) override;
/// Get/Set the drop source
LDragDropSource *DropSource(LDragDropSource *Set = NULL) override;
/// Get/Set the drop target
LDragDropTarget *DropTarget(LDragDropTarget *Set = NULL) override;
/// Sets the drop target state of this view
bool DropTarget(bool t) override;
/// \brief Gives this view a 1 or 2 px sunken border.
///
/// The size is set by the _BorderSize member variable. This border is
/// not considered part of the client area. Mouse and drawing coordinates
/// do not take it into account.
bool Sunken() override;
/// Sets a sunken border around the control
void Sunken(bool i) override;
/// true if the view has a flat border
bool Flat() override;
/// Sets the flat border state
void Flat(bool i) override;
/// \brief true if the view has a raised border
///
/// The size is set by the _BorderSize member variable. This border is
/// not considered part of the client area. Mouse and drawing coordinates
/// do not take it into account.
bool Raised() override;
/// Sets the raised border state
void Raised(bool i) override;
/// Draws an OS themed border
void DrawThemeBorder(LSurface *pDC, LRect &r);
/// \brief true if the control is currently executing in the GUI thread
///
/// Some OS functions are not thread safe, and can only be called in the GUI
/// thread. In the Linux implementation the GUI thread can change from time
/// to time. On Win32 it stays the same. In any case if this function returns
/// true it's safe to do just about anything.
bool InThread() override;
/// \brief Asyncronously posts an event to be received by this view
virtual bool PostEvent
(
/// The command ID.
/// \sa Should be M_USER or higher for custom events.
int Cmd,
/// The first 32-bits of data. Equivalent to wParam on Win32.
LMessage::Param a = 0,
/// The second 32-bits of data. Equivalent to lParam on Win32.
LMessage::Param b = 0,
/// Optional timeout in milliseconds
int64_t TimeoutMs = -1
) override;
template
bool PostEvent(int Cmd, T *Ptr)
{
return PostEvent(Cmd, (LMessage::Param)Ptr, 0);
}
/// \brief Sets the utf-8 text associated with this view
///
/// Name and NameW are interchangable. Using them in any order will convert the
/// text between utf-8 and wide to satify any requirement. Generally once the opposing
/// version of the string is required both the utf-8 and wide copies of the string
/// remain cached in RAM until the Name is changed.
bool Name(const char *n) override;
/// Returns the utf-8 text associated with this view
const char *Name() override;
/// Sets the wide char text associated with this view
bool NameW(const char16 *n) override;
/// \brief Returns the wide char text associated with this view
///
/// On Win32 the wide characters are 16 bits, on unix systems they are 32-bit
/// characters.
const char16 *NameW() override;
/// \brief Gets the font this control should draw with.
///
/// The default font is the system font, owned by the LApp object.
virtual LFont *GetFont() override;
/// \brief Sets the font for this control
///
/// The lifetime of the font passed in is the responsibility of the caller.
/// The LView object assumes the pointer will be valid at all times.
virtual void SetFont(LFont *Fnt, bool OwnIt = false) override;
/// Returns the cursor that should be displayed for the given location
/// \returns a cursor type. i.e. LCUR_Normal from LgiDefs.h
LCursor GetCursor(int x, int y) override;
/// \brief Get the position of the view relitive to it's parent.
virtual LRect &GetPos() override { return Pos; }
/// Get the client region of the window relitive to itself (ie always 0,0-x,y)
virtual LRect &GetClient(bool InClientSpace = true) override;
/// Set the position of the view in terms of it's parent
virtual bool SetPos(LRect &p, bool Repaint = false) override;
/// Gets the width of the view in pixels
int X() override { return Pos.X(); }
/// Gets the height of the view in pixels.
int Y() override { return Pos.Y(); }
/// Gets the minimum size of the view
LPoint GetMinimumSize() override;
/// \brief Set the minimum size of the view.
///
/// Only works for top level windows.
void SetMinimumSize(LPoint Size) override;
/// Gets the style of the control
class LCss *GetCss(bool Create = false) override;
/// Resolve a CSS colour, e.g.:
/// auto Back = StyleColour(LCss::PropBackgroundColor, LColour(L_MED));
LColour StyleColour(int CssPropType, LColour Default, int Depth = 5);
/// Sets the style of the control (will take ownership of 'css')
void SetCss(LCss *css) override;
/// Sets the CSS foreground or background colour
bool SetColour(LColour &c, bool Fore) override;
/// The class' name. Should be overriden in child classes to return the
/// right class name. Mostly used for debugging, but in the win32 port it
/// is also the default WIN32 class name passed to RegisterClass() in
/// LView::CreateClass().
///
/// \returns the Class' name for debugging
const char *GetClass() override;
/// The array of CSS class names.
LString::Array *CssClasses() override;
/// Any element level styles
LString CssStyles(const char *Set = NULL) override;
/// \brief Captures all mouse events to this view
///
/// Once you have mouse capture all mouse events will be passed to this
/// view. i.e. during a mouse click.
bool Capture(bool c) override;
/// true if this view is capturing mouse events.
bool IsCapturing() override;
/// \brief Gets the current mouse location
/// \return true on success
bool GetMouse
(
/// The mouse location information returned
LMouse &m,
/// Get the location in screen coordinates
bool ScreenCoords = false
) override;
/// \brief Gets the ID associated with the view
///
/// The ID of a view is designed to associate controls defined in resource
/// files with a object at runtime via a C header file define.
int GetId() override;
/// Sets the view's ID.
void SetId(int i) override;
/// true if this control is a tab stop.
bool GetTabStop() override;
/// \brief Sets whether this control is a tab stop.
///
/// A top stop is a control that receives focus if the user scrolls through the controls
/// with the tab key.
void SetTabStop(bool b) override;
/// Gets the integer representation of the view's contents
virtual int64 Value() override { return 0; }
/// Sets the integer representation of the view's contents
virtual void Value(int64 i) override {}
#if LGI_VIEW_HANDLE
/// Find a view by it's os handle
virtual LViewI *FindControl(OsView hnd);
#endif
/// Returns the view by it's ID
virtual LViewI *FindControl
(
// The ID to look for
int Id
) override;
/// Gets the value of the control identified by the ID
int64 GetCtrlValue(int Id) override;
/// Sets the value of the control identified by the ID
void SetCtrlValue(int Id, int64 i) override;
/// Gets the name (text) of the control identified by the ID
const char *GetCtrlName(int Id) override;
/// Sets the name (text) of the control identified by the ID
void SetCtrlName(int Id, const char *s) override;
/// Gets the enabled state of the control identified by the ID
bool GetCtrlEnabled(int Id) override;
/// Sets the enabled state of the control identified by the ID
void SetCtrlEnabled(int Id, bool Enabled) override;
/// Gets the visible state of the control identified by the ID
bool GetCtrlVisible(int Id) override;
/// Sets the visible state of the control identified by the ID
void SetCtrlVisible(int Id, bool Visible) override;
/// Causes the given area of the view to be repainted to update the screen
bool Invalidate
(
/// The rectangle of the view to repaint, or NULL for the entire view
LRect *r = NULL,
/// true if you want to wait for the update to happen
bool Repaint = false,
/// false to update in client coordinates, true to update the non client region
bool NonClient = false
) override;
/// Causes the given area of the view to be repainted to update the screen
bool Invalidate
(
/// The region of the view to repaint
LRegion *r,
/// true if you want to wait for the update to happen
bool Repaint = false,
/// false to update in client coordinates, true to update the non client region
bool NonClient = false
) override;
/// true if the mouse event is over the view
bool IsOver(LMouse &m) override;
/// returns the sub window located at the point x,y
LViewI *WindowFromPoint(int x, int y, int DebugDepth = 0) override;
/// Sets a timer to call the OnPulse() event
void SetPulse
(
/// The milliseconds between calls to OnPulse() or -1 to disable
int Ms = -1
) override;
/// Convert a point form view coordinates to screen coordinates
bool PointToScreen(LPoint &p) override;
/// Convert a point form screen coordinates to view coordinates
bool PointToView(LPoint &p) override;
/// Get the x,y offset from the virtual window to the first real view in the parent chain
bool WindowVirtualOffset(LPoint *Offset) override;
/// Get the size of the window borders
LPoint &GetWindowBorderSize() override;
/// Layout all the child views
virtual bool Pour
(
/// The available space to lay out the views into
LRegion &r
) override { return false; }
/// The mouse was clicked over this view
void OnMouseClick
(
/// The event parameters
LMouse &m
) override;
/// Mouse moves into the area over the control
void OnMouseEnter
(
/// The event parameters
LMouse &m
) override;
/// Mouse leaves the area over the control
void OnMouseExit
(
/// The event parameters
LMouse &m
) override;
/// The mouse moves over the control
void OnMouseMove
(
/// The event parameters
LMouse &m
) override;
/// The mouse wheel was scrolled.
bool OnMouseWheel
(
/// The amount scrolled
double Lines
) override;
/// A key was pressed while this view has focus
bool OnKey(LKey &k) override;
/// The view is attached
void OnCreate() override;
/// The view is detached
void OnDestroy() override;
/// The view gains or loses the keyboard focus
void OnFocus
(
/// True if the control is receiving focus
bool f
) override;
/// \brief Called every so often by the timer system.
/// \sa SetPulse()
void OnPulse() override;
/// Called when the view position changes
void OnPosChange() override;
/// Called on a top level window when something requests to close the window
/// \returns true if it's ok to continue shutting down.
bool OnRequestClose
(
/// True if the operating system is shutting down.
bool OsShuttingDown
) override;
/// Return the type of cursor that should be visible when the mouse is at x,y
/// e.g. #LCUR_Normal
int OnHitTest
(
/// The x coordinate in view coordinates
int x,
/// The y coordinate in view coordinates
int y
) override;
/// Called when the contents of the Children list have changed.
void OnChildrenChanged(LViewI *Wnd, bool Attaching) override;
/// Called to paint the on screen representation of the view
void OnPaint(LSurface *pDC) override;
/// \brief Called when a child view or view with it's SetNotify() set to this window changes.
///
/// The event by default will bubble up to the LWindow at the top of the window hierarchy visiting
/// each LView on the way. If it reaches a LView that processes it then the event stops propagating
/// up the hierarchy.
int OnNotify(LViewI *Ctrl, LNotification n) override;
/// Called when a menu command is activated by the user.
int OnCommand(int Cmd, int Event, OsView Wnd) override;
/// Called after the view is attached to a new parent
void OnAttach() override;
/// Called to get layout information for the control. It's called
/// up to 3 times to collect various dimensions:
/// 1) PreLayout: Get the maximum width, and optionally the minimum width.
/// Called with both widths set to zero.
/// Must fill out Inf.Width.Max. Use -1 to fill all available space.
/// Optionally fill out the min width.
/// 2) Layout: Called to work out row height.
/// Called with:
/// - Width.Min unchanged from previous call.
/// - Width.Max is limited to Cell size.
/// Must fill out Inf.Height.Max.
/// Min height currently not used.
/// 3) PostLayout: Called to position view in cell.
/// Not called.
bool OnLayout(LViewLayoutInfo &Inf) override { return false; }
+ /// This class allows some task to tap into the events the view receives.
+ class LgiClass ViewEventTarget :
+ public LEventSinkI,
+ public LEventTargetI
+ {
+ friend class LView;
+
+ protected:
+ LView *view = NULL;
+ LHashTbl,bool> Msgs;
+
+ public:
+ ViewEventTarget
+ (
+ /// The view to attach to.
+ LView *View,
+ /// The message to handle... more can be added to 'Msgs'.
+ /// Pass 0 to get all messages, at the cost of slowing the
+ /// App down a bunch. Not recommended.
+ int Msg
+ );
+ ~ViewEventTarget();
+
+ // Post events to the view, which will return to the OnEvent handler..
+ bool PostEvent(int Cmd, LMessage::Param a = 0, LMessage::Param b = 0, int64_t TimeoutMs = -1);
+
+ // Impl LMessage::Result OnEvent(LMessage *Msg) in your subclass
+ };
+
#if defined(_DEBUG)
bool _Debug;
void Debug();
void _Dump(int Depth = 0);
#endif
};
LgiFunc LView *LViewFromHandle(OsView hWnd);
/// \brief Factory for creating view's by name.
///
/// Inherit from this to add a new factory to create objects. Override
/// NewView() to create your control.
class LgiClass LViewFactory
{
/** \brief Create a view by name
\code
if (strcmp(Class, "MyControl") == 0)
{
return new MyControl;
}
\endcode
*/
virtual LView *NewView
(
/// The name of the class to create
const char *Class,
/// The initial position of the view
LRect *Pos,
/// The initial text of the view
const char *Text
) = 0;
public:
LViewFactory();
virtual ~LViewFactory();
/// Create a view by name.
static LView *Create(const char *Class, LRect *Pos = 0, const char *Text = 0);
};
#define DeclFactory(CLS) \
class CLS ## Factory : public LViewFactory \
{ \
LView *NewView(const char *Name, LRect *Pos, const char *Text) \
{ \
if (!_stricmp(Name, #CLS)) return new CLS; \
return NULL; \
} \
} CLS ## FactoryInst;
#define DeclFactoryParam1(CLS, Param1) \
class CLS ## Factory : public LViewFactory \
{ \
LView *NewView(const char *Name, LRect *Pos, const char *Text) \
{ \
if (!_stricmp(Name, #CLS)) return new CLS(Param1); \
return NULL; \
} \
} CLS ## FactoryInst;
/// Control widget base class
class LgiClass LControl :
public LView
{
friend class LDialog;
protected:
- #if defined BEOS
- bigtime_t Sys_LastClick;
- void MouseClickEvent(bool Down);
- #elif WINNATIVE
- bool *SetOnDelete;
- LWindowsClass *SubClass;
+ #if WINNATIVE
+ bool *SetOnDelete = NULL;
+ LWindowsClass *SubClass = NULL;
#endif
LPoint SizeOfStr(const char *Str);
public:
#if WINNATIVE
- LControl(char *SubClassName = 0);
+ LControl(const char *SubClassName = NULL);
#else
LControl(OsView view = NULL);
#endif
~LControl();
LMessage::Result OnEvent(LMessage *Msg);
};
diff --git a/private/common/ViewPriv.h b/private/common/ViewPriv.h
--- a/private/common/ViewPriv.h
+++ b/private/common/ViewPriv.h
@@ -1,240 +1,241 @@
// Private LView definations
#pragma once
#if WINNATIVE
#if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x501
#endif
#include "commctrl.h"
#include "Uxtheme.h"
#define GViewFlags d->WndStyle
#else
#define GViewFlags WndFlags
#endif
#if defined(__GTK_H__)
struct LCaptureThread : public LThread, public LCancel
{
int view = -1;
LString name;
public:
constexpr static int EventMs = 150;
LCaptureThread(LView *v);
~LCaptureThread();
int Main();
};
#elif defined(MAC)
extern OsThread LgiThreadInPaint;
#elif defined(HAIKU)
#include
extern void *IsAttached(BView *v);
#endif
#define PAINT_VIRTUAL_CHILDREN 0
extern bool In_SetWindowPos;
extern LMouse &lgi_adjust_click(LMouse &Info,
LViewI *Wnd,
bool Capturing = false,
bool Debug = false);
#ifdef __GTK_H__
extern LPoint GtkAbsPos(Gtk::GtkWidget *w);
extern LRect GtkGetPos(Gtk::GtkWidget *w);
#endif
#if !WINNATIVE
#include "lgi/common/ThreadEvent.h"
class LPulseThread : public LThread, public LCancel
{
LView *View = NULL;
LString ViewClass;
int Length = 0;
LThreadEvent Event;
uint64_t WarnTs = 0;
LString MakeName(LView *v, const char *Type)
{
LString s;
s.Printf("LPulseThread.%s.%s", v->GetClass(), Type);
return s;
}
public:
LPulseThread(LView *view, int len) :
View(view),
LThread(MakeName(view, "Thread")),
Event(MakeName(view, "Event"))
{
LAssert(View);
Length = len;
ViewClass = View->GetClass();
Run();
}
~LPulseThread()
{
View = NULL;
Cancel();
Event.Signal();
WaitForExit();
}
int Main()
{
while (!IsCancelled() && LAppInst)
{
auto s = Event.Wait(Length);
if (!View || IsCancelled() || s == LThreadEvent::WaitError)
break;
if (!View->PostEvent(M_PULSE, 0, 0, 50/*milliseconds*/))
{
auto now = LCurrentTime();
if (now - WarnTs >= 5000)
{
WarnTs = now;
printf("%s:%i - PulseThread::PostEvent failed for %p/%s.\n", _FL, View, ViewClass.Get());
}
}
}
return 0;
}
};
#endif
enum LViewFontType
{
/// The LView has a pointer to an externally owned font.
GV_FontPtr,
/// The LView owns the font object, and must free it.
GV_FontOwned,
/// The LApp's font cache owns the object. In this case,
/// calling GetCssStyle on the LView will invalidate the
/// font ptr causing it to be re-calculated.
GV_FontCached,
};
class LViewPrivate
{
public:
// General
LView *View = NULL; // Owning view
LDragDropSource *DropSource = NULL;
LDragDropTarget *DropTarget = NULL;
bool IsThemed = false;
int CtrlId = -1;
int WantsPulse = -1;
// Hierarchy
LViewI *ParentI = NULL;
LView *Parent = NULL;
LViewI *Notify = NULL;
// Size
LPoint MinimumSize;
// Font
LFont *Font = NULL;
LViewFontType FontOwnType = GV_FontPtr;
// Style
LAutoPtr Css;
bool CssDirty = false; // This is set when 'Styles' changes, the next call to GetCss(...) parses
// the styles into the 'Css' object.
LString Styles; // Somewhat temporary object to store unparsed styles particular to
// this view until runtime, where the view heirarchy is known.
LString::Array Classes;
- // Event dispatch handle
+ // Event handling
int SinkHnd = -1;
+ LArray EventTargets;
// OS Specific
#if WINNATIVE
static OsView hPrevCapture;
int WndStyle = 0; // Windows hWnd Style
int WndExStyle = 0; // Windows hWnd ExStyle
int WndDlgCode = 0; // Windows Dialog Code (WM_GETDLGCODE)
LString WndClass;
UINT_PTR TimerId = 0;
HTHEME hTheme = NULL;
#else
// Cursor
LPulseThread *PulseThread = NULL;
LView *Popup = NULL;
bool TabStop = false;
bool WantsFocus = false;
#if defined __GTK_H__
bool InPaint = false;
bool GotOnCreate = false;
#elif defined(MAC) && !defined(LGI_COCOA)
static HIObjectClassRef BaseClass;
#endif
#endif
#if defined(__GTK_H__)
static LCaptureThread *CaptureThread;
LMouse PrevMouse;
#elif defined(MAC)
#ifdef LGI_COCOA
LString ClassName;
bool AttachEvent;
#elif defined LGI_CARBON
EventHandlerRef DndHandler;
LAutoString AcceptedDropFormat;
#endif
#elif defined(LGI_SDL)
SDL_TimerID PulseId;
int PulseLength = -1;
#elif defined(HAIKU)
BView *Hnd = NULL;
LArray MsgQue; // For before the window is attached...
#endif
LViewPrivate(LView *view);
~LViewPrivate();
LView *GetParent()
{
if (Parent)
return Parent;
if (ParentI)
return ParentI->GetGView();
return 0;
}
};
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,2709 +1,2735 @@
/// \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
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)
pu->Owner = NULL;
}
_Delete();
// printf("%p::~LView delete %p th=%u\n", this, d, GetCurrentThreadId());
DeleteObj(d);
// printf("%p::~LView\n", this);
}
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();
OsWindow h;
if (w)
h = w->WindowHandle();
return h;
}
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)
{
#ifdef HAIKU
bool Debug = !Stricmp("LList", GetClass());
if (!d || !d->Hnd)
{
if (Debug)
printf("%s:%i - no handle %p %p\n", _FL, d, d ? d->Hnd : NULL);
return false;
}
if (d->Hnd->Parent() == NULL)
{
if (Debug)
printf("%s:%p - Lock() no parent.\n", GetClass(), this);
return true;
}
if (TimeOut >= 0)
{
auto r = d->Hnd->LockLooperWithTimeout(TimeOut * 1000);
if (r == B_OK)
{
_InLock++;
if (Debug)
printf("%s:%p - Lock() cnt=%i par=%p.\n", GetClass(), this, _InLock, d->Hnd->Parent());
return true;
}
printf("%s:%i - Lock(%i) failed with %x.\n", _FL, TimeOut, r);
return false;
}
auto r = d->Hnd->LockLooper();
if (r)
{
_InLock++;
if (Debug)
{
auto w = WindowHandle();
printf("%s:%p - Lock() cnt=%i myThread=%i wndThread=%i.\n",
GetClass(),
this,
_InLock,
GetCurrentThreadId(),
w ? w->Thread() : -1);
}
return true;
}
if (Debug)
printf("%s:%i - Lock(%s:%i) failed.\n", _FL, file, line);
return false;
#else
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;
#endif
}
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()
{
// DEBUG_CAPTURE("%s::IsCapturing()=%p==%p==%i\n", GetClass(), _Capturing, this, (int)(_Capturing == this));
return _Capturing == this;
}
bool LView::Capture(bool c)
{
ThreadCheck();
DEBUG_CAPTURE("%s::Capture(%i)\n", GetClass(), c);
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())
{
const int attempts = 3;
// printf("%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden());
if (v)
{
bool parentHidden = false;
for (auto p = d->Hnd->Parent(); p; p = p->Parent())
{
if (p->IsHidden())
{
parentHidden = true;
break;
}
}
if (!parentHidden) // Don't try and show if one of the parent's is hidden.
{
for (int i=0; iHnd->IsHidden(); i++)
{
// printf("\t%p Show\n", this);
d->Hnd->Show();
}
if (d->Hnd->IsHidden())
{
printf("%s:%i - Failed to show %s.\n", _FL, GetClass());
for (auto p = d->Hnd->Parent(); p; p = p->Parent())
printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden());
}
}
}
else
{
for (int i=0; iHnd->IsHidden(); i++)
{
// printf("\t%p Hide\n", this);
d->Hnd->Hide();
}
if (!d->Hnd->IsHidden())
{
printf("%s:%i - Failed to hide %s.\n", _FL, GetClass());
for (auto p = d->Hnd->Parent(); p; p = p->Parent())
printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden());
}
}
// printf("\t%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden());
}
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(HAIKU)
LLocker lck(d->Hnd, _FL);
if (lck.Lock())
{
Has = d->Hnd->IsFocus();
lck.Unlock();
}
#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(HAIKU)
_Focus(i);
#elif 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);
#else
d->TabStop = b;
#endif
}
int64 LView::GetCtrlValue(int Id)
{
ThreadCheck();
LViewI *w = FindControl(Id);
return (w) ? w->Value() : 0;
}
void LView::SetCtrlValue(int Id, int64 i)
{
ThreadCheck();
LViewI *w = FindControl(Id);
if (w) w->Value(i);
}
const char *LView::GetCtrlName(int Id)
{
ThreadCheck();
LViewI *w = FindControl(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);
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, int64_t timeoutMs)
{
#ifdef LGI_SDL
return LPostEvent(this, Cmd, a, b);
#elif defined(HAIKU)
if (!d || !d->Hnd)
{
// printf("%s:%i - Bad pointers %p %p\n", _FL, d, d ? d->Hnd : NULL);
return false;
}
BWindow *bWnd = NULL;
LWindow *wnd = dynamic_cast(this);
if (wnd)
{
bWnd = wnd->WindowHandle();
}
else
{
// Look for an attached view to lock...
for (LViewI *v = this; v; v = v->GetParent())
{
auto vhnd = v->Handle();
if (vhnd && ::IsAttached(vhnd))
{
bWnd = vhnd->Window();
break;
}
}
}
BMessage m(Cmd);
auto r = m.AddInt64(LMessage::PropA, a);
if (r != B_OK)
printf("%s:%i - AddUInt64 failed.\n", _FL);
r = m.AddInt64(LMessage::PropB, b);
if (r != B_OK)
printf("%s:%i - AddUInt64 failed.\n", _FL);
r = m.AddPointer(LMessage::PropView, this);
if (r != B_OK)
printf("%s:%i - AddPointer failed.\n", _FL);
if (bWnd)
{
r = bWnd->PostMessage(&m);
if (r != B_OK)
printf("%s:%i - PostMessage failed.\n", _FL);
return r == B_OK;
}
// Not attached yet...
d->MsgQue.Add(new BMessage(m));
// printf("%s:%i - PostEvent.MsgQue=%i\n", _FL, (int)d->MsgQue.Length());
return true;
#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) || defined(WINDOWS)
template
void _Dump(int Depth, T v)
{
LString Sp, Name;
Sp.Length(Depth<<1);
memset(Sp.Get(), ' ', Depth<<1);
Sp.Get()[Depth<<1] = 0;
#if defined(HAIKU)
LRect Frame = v->Frame();
Name = v->Name();
bool IsHidden = v->IsHidden();
#else
RECT rc; GetWindowRect(v, &rc);
LRect Frame = rc;
wchar_t txt[256];
GetWindowTextW(v, txt, CountOf(txt));
Name = txt;
bool IsHidden = !(GetWindowLong(v, GWL_STYLE) & WS_VISIBLE);
#endif
LgiTrace("%s%p::%s frame=%s vis=%i\n",
Sp.Get(), v, Name.Get(), Frame.GetStr(), !IsHidden);
#if defined(HAIKU)
for (int32 i=0; iCountChildren(); i++)
_Dump(Depth + 1, v->ChildAt(i));
#else
for (auto h = GetWindow(v, GW_CHILD); h; h = GetWindow(h, GW_HWNDNEXT))
_Dump(Depth + 1, h);
#endif
}
#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(MAC)
#if defined(HAIKU)
LLocker lck(WindowHandle(), _FL);
if (lck.Lock())
#endif
::_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;
}
+///////////////////////////////////////////////////////////////////////////////////////////
+LView::ViewEventTarget::ViewEventTarget(LView *View, int Msg)
+{
+ LAssert(View != NULL);
+ view = View;
+ if (Msg)
+ Msgs.Add(Msg, true);
+ if (view)
+ view->d->EventTargets.Add(this);
+}
+
+LView::ViewEventTarget::~ViewEventTarget()
+{
+ if (view)
+ view->d->EventTargets.Delete(this);
+}
+
+bool LView::ViewEventTarget::PostEvent(int Cmd, LMessage::Param a, LMessage::Param b, int64_t TimeoutMs)
+{
+ if (view)
+ return view->PostEvent(Cmd, a, b, TimeoutMs);
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
#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
diff --git a/src/win/Lgi/View.cpp b/src/win/Lgi/View.cpp
--- a/src/win/Lgi/View.cpp
+++ b/src/win/Lgi/View.cpp
@@ -1,2159 +1,2167 @@
/*hdr
** FILE: LView.cpp
** AUTHOR: Matthew Allen
** DATE: 23/4/98
** DESCRIPTION: Win32 LView Implementation
**
** Copyright (C) 1998-2003, Matthew Allen
** fret@memecode.com
*/
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Base64.h"
#include "lgi/common/Com.h"
#include "lgi/common/DragAndDrop.h"
#include "lgi/common/DropFiles.h"
#include "lgi/common/GdiLeak.h"
#include "lgi/common/Css.h"
#include "lgi/common/Edit.h"
#include "lgi/common/LgiRes.h"
#include "lgi/common/Menu.h"
#include "lgi/common/Thread.h"
#include "ViewPriv.h"
#define DEBUG_MOUSE_CLICKS 0
#define DEBUG_OVER 0
#define OLD_WM_CHAR_MODE 1
////////////////////////////////////////////////////////////////////////////////////////////////////
bool In_SetWindowPos = false;
HWND LViewPrivate::hPrevCapture = 0;
LViewPrivate::LViewPrivate(LView *view) : View(view)
{
WndStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN;
- WndExStyle = 0;
- WndDlgCode = 0;
IsThemed = LResources::DefaultColours;
}
LViewPrivate::~LViewPrivate()
{
+ while (EventTargets.Length())
+ delete EventTargets[0];
+
if (hTheme)
{
CloseThemeData(hTheme);
hTheme = NULL;
}
if (FontOwnType == GV_FontOwned)
{
DeleteObj(Font);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Helper Stuff
#include "zmouse.h"
int MouseRollMsg = 0;
#ifdef __GNUC__
#define MSH_WHEELMODULE_CLASS "MouseZ"
#define MSH_WHEELMODULE_TITLE "Magellan MSWHEEL"
#define MSH_SCROLL_LINES "MSH_SCROLL_LINES_MSG"
#endif
int _lgi_mouse_wheel_lines()
{
UINT nScrollLines;
if (SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, (PVOID) &nScrollLines, 0))
return nScrollLines;
return 3;
}
#define SetKeyFlag(v, k, f) if (GetKeyState(k)&0xFF00) { v |= f; }
int _lgi_get_key_flags()
{
int Flags = 0;
if (LGetOs() == LGI_OS_WIN9X)
{
SetKeyFlag(Flags, VK_MENU, LGI_EF_ALT);
SetKeyFlag(Flags, VK_SHIFT, LGI_EF_SHIFT);
SetKeyFlag(Flags, VK_CONTROL, LGI_EF_CTRL);
}
else // is NT/2K/XP
{
SetKeyFlag(Flags, VK_LMENU, LGI_EF_LALT);
SetKeyFlag(Flags, VK_RMENU, LGI_EF_RALT);
SetKeyFlag(Flags, VK_LSHIFT, LGI_EF_LSHIFT);
SetKeyFlag(Flags, VK_RSHIFT, LGI_EF_RSHIFT);
SetKeyFlag(Flags, VK_LCONTROL, LGI_EF_LCTRL);
SetKeyFlag(Flags, VK_RCONTROL, LGI_EF_RCTRL);
}
if (GetKeyState(VK_CAPITAL))
SetFlag(Flags, LGI_EF_CAPS_LOCK);
return Flags;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
int GetInputACP()
{
char16 Str[16];
LCID Lcid = (NativeInt)GetKeyboardLayout(GetCurrentThreadId()) & 0xffff;
GetLocaleInfo(Lcid, LOCALE_IDEFAULTANSICODEPAGE, Str, sizeof(Str));
return _wtoi(Str);
}
LKey::LKey(int v, uint32_t flags)
{
const char *Cp = 0;
vkey = v;
Data = flags;
c16 = 0;
#if OLD_WM_CHAR_MODE
c16 = vkey;
#else
typedef int (WINAPI *p_ToUnicode)(UINT, UINT, PBYTE, LPWSTR, int, UINT);
static bool First = true;
static p_ToUnicode ToUnicode = 0;
if (First)
{
ToUnicode = (p_ToUnicode) GetProcAddress(LoadLibrary("User32.dll"), "ToUnicode");
First = false;
}
if (ToUnicode)
{
BYTE state[256];
GetKeyboardState(state);
char16 w[4];
int r = ToUnicode(vkey, flags & 0x7f, state, w, CountOf(w), 0);
if (r == 1)
{
c16 = w[0];
}
}
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////////
template
bool CastHwnd(T *&Ptr, HWND hWnd)
{
#if _MSC_VER >= _MSC_VER_VS2005
LONG_PTR user = GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
LONG user = GetWindowLong(hWnd, GWL_USERDATA);
#endif
LONG magic = GetWindowLong(hWnd, GWL_LGI_MAGIC);
if (magic != LGI_GViewMagic)
{
TCHAR ClsName[256] = {0};
int Ch = GetClassName(hWnd, ClsName, CountOf(ClsName));
LString Cls = ClsName;
// LgiTrace("%s:%i - Error: hWnd=%p/%s, GWL_LGI_MAGIC=%i\n", _FL, hWnd, Cls.Get(), magic);
return false;
}
Ptr = dynamic_cast((LViewI*)user);
return Ptr != NULL;
}
bool SetLgiMagic(HWND hWnd)
{
SetLastError(0);
LONG res = SetWindowLong(hWnd, GWL_LGI_MAGIC, LGI_GViewMagic);
bool Status = res != 0;
if (!Status)
{
DWORD err = GetLastError();
Status = err == 0;
}
LONG v = GetWindowLong(hWnd, GWL_LGI_MAGIC);
// LgiTrace("set LGI_GViewMagic for %p, %i, %i\n", hWnd, Status, v);
return Status;
}
LRESULT CALLBACK LWindowsClass::Redir(HWND hWnd, UINT m, WPARAM a, LPARAM b)
{
if (m == WM_NCCREATE)
{
LPCREATESTRUCT Info = (LPCREATESTRUCT) b;
LViewI *ViewI = (LViewI*) Info->lpCreateParams;
if (ViewI)
{
LView *View = ViewI->GetGView();
if (View) View->_View = hWnd;
#if _MSC_VER >= _MSC_VER_VS2005
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)ViewI);
#else
SetWindowLong(hWnd, GWL_USERDATA, (LONG)ViewI);
#endif
SetLgiMagic(hWnd);
}
}
LViewI *Wnd = (LViewI*)
#if _MSC_VER >= _MSC_VER_VS2005
GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
GetWindowLong(hWnd, GWL_USERDATA);
#endif
if (Wnd)
{
LMessage Msg(m, a, b);
Msg.hWnd = hWnd;
return Wnd->OnEvent(&Msg);
}
return DefWindowProcW(hWnd, m, a, b);
}
LRESULT CALLBACK LWindowsClass::SubClassRedir(HWND hWnd, UINT m, WPARAM a, LPARAM b)
{
if (m == WM_NCCREATE)
{
LPCREATESTRUCT Info = (LPCREATESTRUCT) b;
LViewI *ViewI = 0;
if (Info->lpCreateParams)
{
if (ViewI = (LViewI*) Info->lpCreateParams)
{
LView *View = ViewI->GetGView();
if (View)
View->_View = hWnd;
}
}
#if _MSC_VER >= _MSC_VER_VS2005
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) ViewI);
#else
SetWindowLong(hWnd, GWL_USERDATA, (LONG) ViewI);
#endif
SetLgiMagic(hWnd);
}
LViewI *Wnd = (LViewI*)
#if _MSC_VER >= _MSC_VER_VS2005
GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
GetWindowLong(hWnd, GWL_USERDATA);
#endif
if (Wnd)
{
LMessage Msg(m, a, b);
Msg.hWnd = hWnd;
LMessage::Result Status = Wnd->OnEvent(&Msg);
return Status;
}
return DefWindowProcW(hWnd, m, a, b);
}
LWindowsClass::LWindowsClass(const char *name)
{
Name(name);
ZeroObj(Class);
Class.lpfnWndProc = (WNDPROC) Redir;
Class.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
Class.cbWndExtra = GWL_EXTRA_BYTES;
Class.cbSize = sizeof(Class);
ParentProc = 0;
}
LWindowsClass::~LWindowsClass()
{
UnregisterClassW(NameW(), LProcessInst());
Class.lpszClassName = NULL;
}
LWindowsClass *LWindowsClass::Create(const char *ClassName)
{
if (!LAppInst)
return NULL;
LApp::ClassContainer *Classes = LAppInst->GetClasses();
if (!Classes)
return NULL;
LWindowsClass *c = Classes->Find(ClassName);
if (!c)
{
c = new LWindowsClass(ClassName);
if (c)
Classes->Add(ClassName, c);
}
return c;
}
bool LWindowsClass::IsSystem(const char *Cls)
{
if (!_stricmp(Cls, WC_BUTTONA) ||
!_stricmp(Cls, WC_COMBOBOXA) ||
!_stricmp(Cls, WC_STATICA)||
!_stricmp(Cls, WC_LISTBOXA)||
!_stricmp(Cls, WC_SCROLLBARA)||
!_stricmp(Cls, WC_HEADERA)||
!_stricmp(Cls, WC_LISTVIEWA)||
!_stricmp(Cls, WC_TREEVIEWA)||
!_stricmp(Cls, WC_COMBOBOXEXA)||
!_stricmp(Cls, WC_TABCONTROLA)||
!_stricmp(Cls, WC_IPADDRESSA)||
!_stricmp(Cls, WC_EDITA))
{
return true;
}
return false;
}
bool LWindowsClass::Register()
{
bool Status = false;
if (IsSystem(Name()))
{
ZeroObj(Class);
Class.cbSize = sizeof(Class);
Status = GetClassInfoExW(LProcessInst(), NameW(), &Class) != 0;
LAssert(Status);
}
else // if (!Class.lpszClassName)
{
Class.hInstance = LProcessInst();
if (!Class.lpszClassName)
Class.lpszClassName = NameW();
Status = RegisterClassExW(&Class) != 0;
if (!Status)
{
auto err = GetLastError();
if (err == 1410)
Status = true;
else
LAssert(Status);
}
}
return Status;
}
bool LWindowsClass::SubClass(char *Parent)
{
bool Status = false;
if (!Class.lpszClassName)
{
HBRUSH hBr = Class.hbrBackground;
LAutoWString p(Utf8ToWide(Parent));
if (p)
{
if (GetClassInfoExW(LProcessInst(), p, &Class))
{
ParentProc = Class.lpfnWndProc;
if (hBr)
{
Class.hbrBackground = hBr;
}
Class.cbWndExtra = max(Class.cbWndExtra, GWL_EXTRA_BYTES);
Class.hInstance = LProcessInst();
Class.lpfnWndProc = (WNDPROC) SubClassRedir;
Class.lpszClassName = NameW();
Status = RegisterClassExW(&Class) != 0;
LAssert(Status);
}
}
}
else Status = true;
return Status;
}
LRESULT CALLBACK LWindowsClass::CallParent(HWND hWnd, UINT m, WPARAM a, LPARAM b)
{
if (!ParentProc)
return 0;
if (IsWindowUnicode(hWnd))
{
return CallWindowProcW(ParentProc, hWnd, m, a, b);
}
else
{
return CallWindowProcA(ParentProc, hWnd, m, a, b);
}
}
//////////////////////////////////////////////////////////////////////////////
LViewI *LWindowFromHandle(HWND hWnd)
{
if (hWnd)
{
SetLastError(0);
int32 m = GetWindowLong(hWnd, GWL_LGI_MAGIC);
#if 0 //def _DEBUG
DWORD err = GetLastError();
if (err == 1413)
{
TCHAR name[256];
if (GetClassName(hWnd, name, sizeof(name)))
{
WNDCLASSEX cls;
ZeroObj(cls);
cls.cbSize = sizeof(WNDCLASSEX);
if (GetClassInfoEx(LAppInst->GetInstance(), name, &cls))
{
if (cls.cbWndExtra >= 8)
{
LAssert(!"Really?");
}
}
}
}
#endif
if (m == LGI_GViewMagic)
{
return (LViewI*)
#if _MSC_VER >= _MSC_VER_VS2005
GetWindowLongPtr(hWnd, GWLP_USERDATA);
#else
GetWindowLong(hWnd, GWL_USERDATA);
#endif
}
}
return 0;
}
//////////////////////////////////////////////////////////////////////////////
const char *LView::GetClass()
{
return "LView";
}
void LView::_Delete()
{
if (_View && d->DropTarget)
{
RevokeDragDrop(_View);
}
#ifdef _DEBUG
// Sanity check..
// LArray HasView;
for (auto c: Children)
{
auto par = c->GetParent();
bool ok = ((LViewI*)par) == this || par == NULL;
if (!ok)
LAssert(!"heirachy error");
}
#endif
// Delete myself out of my parent's list
if (d->Parent)
{
d->Parent->OnChildrenChanged(this, false);
d->Parent->DelView(this);
d->Parent = 0;
d->ParentI = 0;
}
// Delete all children
LViewI *c;
while (c = Children[0])
{
// If it has no parent, remove the pointer from the child list,
// Because the child isn't going to do it...
if (c->GetParent() == 0)
Children.Delete(c);
// Delete the child view
DeleteObj(c);
}
// Delete the OS representation of myself
if (_View && IsWindow(_View))
{
WndFlags |= GWF_DESTRUCTOR;
BOOL Status = DestroyWindow(_View);
LAssert(Status != 0);
}
// NULL my handles and flags
_View = 0;
WndFlags = 0;
// Remove static references to myself
if (_Over == this) _Over = 0;
if (_Capturing == this)
{
#if DEBUG_CAPTURE
LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n",
_FL, this, GetClass());
#endif
_Capturing = 0;
}
LWindow *Wnd = GetWindow();
if (Wnd)
Wnd->SetFocus(this, LWindow::ViewDelete);
// this should only exist in an ex-LWindow, due to the way
// C++ deletes objects it needs to be here.
DeleteObj(_Lock);
}
void LView::Quit(bool DontDelete)
{
if (_View)
{
if (!DontDelete)
{
WndFlags |= GWF_QUIT_WND;
}
DestroyWindow(_View);
}
}
uint32_t LView::GetDlgCode()
{
return d->WndDlgCode;
}
void LView::SetDlgCode(uint32_t i)
{
d->WndDlgCode = i;
}
uint32_t LView::GetStyle()
{
return d->WndStyle;
}
void LView::SetStyle(uint32_t i)
{
d->WndStyle = i;
}
uint32_t LView::GetExStyle()
{
return d->WndExStyle;
}
void LView::SetExStyle(uint32_t i)
{
d->WndExStyle = i;
}
const char *LView::GetClassW32()
{
return d->WndClass;
}
void LView::SetClassW32(const char *c)
{
d->WndClass = c;
}
LWindowsClass *LView::CreateClassW32(const char *Class, HICON Icon, int AddStyles)
{
if (Class)
{
SetClassW32(Class);
}
if (GetClassW32())
{
LWindowsClass *c = LWindowsClass::Create(GetClassW32());
if (c)
{
if (Icon)
{
c->Class.hIcon = Icon;
}
if (AddStyles)
{
c->Class.style |= AddStyles;
}
c->Register();
return c;
}
}
return 0;
}
bool LView::IsAttached()
{
return _View && IsWindow(_View);
}
bool LView::Attach(LViewI *p)
{
bool Status = false;
SetParent(p);
LView *Parent = d->GetParent();
if (Parent && !_Window)
_Window = Parent->_Window;
const char *ClsName = GetClassW32();
if (!ClsName)
ClsName = GetClass();
if (ClsName)
{
// Real window with HWND
bool Enab = Enabled();
// Check the class is created
bool IsSystemClass = LWindowsClass::IsSystem(ClsName);
LWindowsClass *Cls = LWindowsClass::Create(ClsName);
if (Cls)
{
auto r = Cls->Register();
if (!r)
{
LAssert(0);
}
}
else if (!IsSystemClass)
return false;
LAssert(!Parent || Parent->Handle() != 0);
DWORD Style = GetStyle();
DWORD ExStyle = GetExStyle() & ~WS_EX_CONTROLPARENT;
if (!TestFlag(WndFlags, GWF_SYS_BORDER))
ExStyle &= ~(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE);
auto Text = LBase::NameW();
LAutoWString WCls(Utf8ToWide(ClsName));
_View = CreateWindowExW(ExStyle,
WCls,
Text,
Style,
Pos.x1, Pos.y1,
Pos.X(), Pos.Y(),
Parent ? Parent->Handle() : 0,
NULL,
LProcessInst(),
(LViewI*) this);
#ifdef _DEBUG
if (!_View)
{
DWORD e = GetLastError();
LgiTrace("%s:%i - CreateWindowExW failed with 0x%x\n", _FL, e);
LAssert(!"CreateWindowEx failed");
}
#endif
if (_View)
{
Status = (_View != NULL);
if (d->Font)
SendMessage(_View, WM_SETFONT, (WPARAM) d->Font->Handle(), 0);
if (d->DropTarget)
RegisterDragDrop(_View, d->DropTarget);
if (TestFlag(WndFlags, GWF_FOCUS))
SetFocus(_View);
if (d->WantsPulse > 0)
{
SetPulse(d->WantsPulse);
d->WantsPulse = -1;
}
}
OnAttach();
}
else
{
// Virtual window (no HWND)
Status = true;
}
if (Status && d->Parent)
{
if (!d->Parent->HasView(this))
{
d->Parent->AddView(this);
}
d->Parent->OnChildrenChanged(this, true);
}
return Status;
}
bool LView::Detach()
{
bool Status = false;
if (_Window)
{
LWindow *Wnd = dynamic_cast(_Window);
if (Wnd)
Wnd->SetFocus(this, LWindow::ViewDelete);
_Window = NULL;
}
if (d->Parent)
{
d->Parent->DelView(this);
d->Parent->OnChildrenChanged(this, false);
d->Parent = 0;
d->ParentI = 0;
Status = true;
WndFlags &= ~GWF_FOCUS;
if (_Capturing == this)
{
if (_View)
ReleaseCapture();
#if DEBUG_CAPTURE
LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n",
_FL, this, GetClass());
#endif
_Capturing = 0;
}
if (_View)
{
WndFlags &= ~GWF_QUIT_WND;
BOOL Status = DestroyWindow(_View);
DWORD Err = GetLastError();
LAssert(Status != 0);
}
}
return Status;
}
LRect &LView::GetClient(bool InClientSpace)
{
static LRect Client;
if (_View)
{
RECT rc;
GetClientRect(_View, &rc);
Client = rc;
}
else
{
Client.Set(0, 0, Pos.X()-1, Pos.Y()-1);
if (dynamic_cast(this) ||
dynamic_cast(this))
{
Client.x1 += GetSystemMetrics(SM_CXFRAME);
Client.x2 -= GetSystemMetrics(SM_CXFRAME);
Client.y1 += GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION);
Client.y2 -= GetSystemMetrics(SM_CYFRAME);
}
else if (Sunken() || Raised())
{
Client.Inset(_BorderSize, _BorderSize);
}
}
if (InClientSpace)
Client.Offset(-Client.x1, -Client.y1);
return Client;
}
LCursor LView::GetCursor(int x, int y)
{
return LCUR_Normal;
}
#ifndef GCL_HCURSOR
#define GCL_HCURSOR -12
#endif
bool LgiToWindowsCursor(OsView Hnd, LCursor Cursor)
{
char16 *Set = 0;
switch (Cursor)
{
case LCUR_UpArrow:
Set = IDC_UPARROW;
break;
case LCUR_Cross:
Set = IDC_CROSS;
break;
case LCUR_Wait:
Set = IDC_WAIT;
break;
case LCUR_Ibeam:
Set = IDC_IBEAM;
break;
case LCUR_SizeVer:
Set = IDC_SIZENS;
break;
case LCUR_SizeHor:
Set = IDC_SIZEWE;
break;
case LCUR_SizeBDiag:
Set = IDC_SIZENESW;
break;
case LCUR_SizeFDiag:
Set = IDC_SIZENWSE;
break;
case LCUR_SizeAll:
Set = IDC_SIZEALL;
break;
case LCUR_PointingHand:
{
LArray Ver;
int Os = LGetOs(&Ver);
if
(
(
Os == LGI_OS_WIN32
||
Os == LGI_OS_WIN64
)
&&
Ver[0] >= 5)
{
#ifndef IDC_HAND
#define IDC_HAND MAKEINTRESOURCE(32649)
#endif
Set = IDC_HAND;
}
// else not supported
break;
}
case LCUR_Forbidden:
Set = IDC_NO;
break;
// Not impl
case LCUR_SplitV:
break;
case LCUR_SplitH:
break;
case LCUR_Blank:
break;
}
HCURSOR cur = LoadCursor(0, Set ? Set : IDC_ARROW);
SetCursor(cur);
if (Hnd)
SetWindowLongPtr(Hnd, GCL_HCURSOR, (LONG_PTR)cur);
return true;
}
bool LView::PointToScreen(LPoint &p)
{
POINT pt = {p.x, p.y};
LViewI *t = this;
while ( t &&
t->GetParent() &&
!t->Handle())
{
pt.x += t->GetPos().x1;
pt.y += t->GetPos().y1;
t = t->GetParent();
}
ClientToScreen(t->Handle(), &pt);
p.x = pt.x;
p.y = pt.y;
return true;
}
bool LView::PointToView(LPoint &p)
{
POINT pt = {p.x, p.y};
LViewI *t = this;
while ( t &&
t->GetParent() &&
!t->Handle())
{
pt.x -= t->GetPos().x1;
pt.y -= t->GetPos().y1;
t = t->GetParent();
}
ScreenToClient(t->Handle(), &pt);
p.x = pt.x;
p.y = pt.y;
return true;
}
bool LView::GetMouse(LMouse &m, bool ScreenCoords)
{
// position
POINT p;
GetCursorPos(&p);
if (!ScreenCoords)
{
ScreenToClient(_View, &p);
}
m.x = p.x;
m.y = p.y;
m.Target = this;
// buttons
m.Flags = ((GetAsyncKeyState(VK_LBUTTON)&0x8000) ? LGI_EF_LEFT : 0) |
((GetAsyncKeyState(VK_MBUTTON)&0x8000) ? LGI_EF_MIDDLE : 0) |
((GetAsyncKeyState(VK_RBUTTON)&0x8000) ? LGI_EF_RIGHT : 0) |
((GetAsyncKeyState(VK_CONTROL)&0x8000) ? LGI_EF_CTRL : 0) |
((GetAsyncKeyState(VK_MENU) &0x8000) ? LGI_EF_ALT : 0) |
((GetAsyncKeyState(VK_LWIN) &0x8000) ? LGI_EF_SYSTEM : 0) |
((GetAsyncKeyState(VK_RWIN) &0x8000) ? LGI_EF_SYSTEM : 0) |
((GetAsyncKeyState(VK_SHIFT) &0x8000) ? LGI_EF_SHIFT : 0);
if (m.Flags & (LGI_EF_LEFT | LGI_EF_MIDDLE | LGI_EF_RIGHT))
{
m.Flags |= LGI_EF_DOWN;
}
return true;
}
bool LView::SetPos(LRect &p, bool Repaint)
{
bool Status = true;
LRect OldPos = Pos;
if (Pos != p)
{
Pos = p;
if (_View)
{
HWND hOld = GetFocus();
bool WasVis = IsWindowVisible(_View) != 0;
In_SetWindowPos = true;
Status = SetWindowPos( _View,
NULL,
Pos.x1,
Pos.y1,
Pos.X(),
Pos.Y(),
// ((Repaint) ? 0 : SWP_NOREDRAW) |
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER) != 0;
In_SetWindowPos = false;
}
else if (GetParent())
{
OnPosChange();
}
if (Repaint)
{
Invalidate();
}
}
return Status;
}
bool LView::Invalidate(LRect *r, bool Repaint, bool Frame)
{
if (_View)
{
bool Status = false;
if (Frame)
{
RedrawWindow( _View,
NULL,
NULL,
RDW_FRAME |
RDW_INVALIDATE |
RDW_ALLCHILDREN |
((Repaint) ? RDW_UPDATENOW : 0));
}
else
{
if (r)
{
Status = InvalidateRect(_View, &((RECT)*r), false) != 0;
}
else
{
RECT c = GetClient();
Status = InvalidateRect(_View, &c, false) != 0;
}
}
if (Repaint)
{
UpdateWindow(_View);
}
return Status;
}
else
{
LRect Up;
LViewI *p = this;
if (r)
{
Up = *r;
}
else
{
Up.Set(0, 0, Pos.X()-1, Pos.Y()-1);
}
if (dynamic_cast(this))
return true;
while (p && !p->Handle())
{
LViewI *Par = p->GetParent();
LView *VPar = Par?Par->GetGView():0;
LRect w = p->GetPos();
LRect c = p->GetClient(false);
if (Frame && p == this)
Up.Offset(w.x1, w.y1);
else
Up.Offset(w.x1 + c.x1, w.y1 + c.y1);
p = Par;
}
if (p && p->Handle())
{
return p->Invalidate(&Up, Repaint);
}
}
return false;
}
void
CALLBACK
LView::TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, uint32_t dwTime)
{
LView *View = (LView*) idEvent;
if (View)
{
View->OnPulse();
}
}
void LView::SetPulse(int Length)
{
if (_View)
{
if (Length > 0)
{
d->TimerId = SetTimer(_View, (UINT_PTR) this, Length, (TIMERPROC) TimerProc);
}
else
{
KillTimer(_View, d->TimerId);
d->TimerId = 0;
}
}
else
{
d->WantsPulse = Length;
}
}
static int ConsumeTabKey = 0;
bool SysOnKey(LView *w, LMessage *m)
{
if (m->a == VK_TAB &&
(m->m == WM_KEYDOWN ||
m->m == WM_SYSKEYDOWN) )
{
if (!TestFlag(w->d->WndDlgCode, DLGC_WANTTAB) &&
!TestFlag(w->d->WndDlgCode, DLGC_WANTALLKEYS))
{
// push the focus to the next control
bool Shifted = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
LViewI *Wnd = GetNextTabStop(w, Shifted);
if (Wnd)
{
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus(%p)\\n", _FL, Wnd->Handle());
}
ConsumeTabKey = 2;
::SetFocus(Wnd->Handle());
return true;
}
}
}
return false;
}
#ifdef _MSC_VER
#include "vsstyle.h"
void LView::DrawThemeBorder(LSurface *pDC, LRect &r)
{
if (!d->hTheme)
d->hTheme = OpenThemeData(_View, VSCLASS_EDIT);
if (d->hTheme)
{
RECT rc = r;
int StateId;
if (!Enabled())
StateId = EPSN_DISABLED;
else if (GetFocus() == _View)
StateId = EPSN_FOCUSED;
else
StateId = EPSN_NORMAL;
// LgiTrace("ThemeDraw %s: %i\n", GetClass(), StateId);
RECT clip[4];
clip[0] = LRect(r.x1, r.y1, r.x1 + 1, r.y2); // left
clip[1] = LRect(r.x1 + 2, r.y1, r.x2 - 2, r.y1 + 1); // top
clip[2] = LRect(r.x2 - 1, r.y1, r.x2, r.y2); // right
clip[3] = LRect(r.x1 + 2, r.y2 - 1, r.x2 - 2, r.y2); // bottom
LColour cols[4] =
{
LColour(255, 0, 0),
LColour(0, 255, 0),
LColour(0, 0, 255),
LColour(255, 255, 0)
};
for (int i=0; iColour(cols[i]);
pDC->Rectangle(&tmp);
#else
DrawThemeBackground(d->hTheme,
pDC->Handle(),
EP_EDITBORDER_NOSCROLL,
StateId,
&rc,
&clip[i]);
#endif
}
pDC->Colour(L_MED);
pDC->Set(r.x1, r.y1);
pDC->Set(r.x2, r.y1);
pDC->Set(r.x1, r.y2);
pDC->Set(r.x2, r.y2);
r.Inset(2, 2);
}
else
{
LWideBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge);
d->IsThemed = false;
}
}
#else
void LView::DrawThemeBorder(LSurface *pDC, LRect &r)
{
LWideBorder(pDC, r, DefaultSunkenEdge);
}
#endif
bool IsKeyChar(LKey &k, int vk)
{
if (k.Ctrl() || k.Alt() || k.System())
return false;
switch (vk)
{
case VK_BACK:
case VK_TAB:
case VK_RETURN:
case VK_SPACE:
case 0xba: // ;
case 0xbb: // =
case 0xbc: // ,
case 0xbd: // -
case 0xbe: // .
case 0xbf: // /
case 0xc0: // `
case 0xdb: // [
case 0xdc: // |
case 0xdd: // ]
case 0xde: // '
return true;
}
if (vk >= VK_NUMPAD0 && vk <= VK_DIVIDE)
return true;
if (vk >= '0' && vk <= '9')
return true;
if (vk >= 'A' && vk <= 'Z')
return true;
return false;
}
#define KEY_FLAGS (~(MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))
LMessage::Result LView::OnEvent(LMessage *Msg)
{
int Status = 0;
if (Msg->Msg() == MouseRollMsg)
{
HWND hFocus = GetFocus();
if (_View)
{
int Flags = ((GetKeyState(VK_SHIFT)&0xF000) ? VK_SHIFT : 0) |
((GetKeyState(VK_CONTROL)&0xF000) ? VK_CONTROL : 0);
PostMessage(hFocus, WM_MOUSEWHEEL, MAKELONG(Flags, (short)Msg->a), Msg->b);
}
return 0;
}
+ for (auto target: d->EventTargets)
+ {
+ if (target->Msgs.Length() == 0 ||
+ target->Msgs.Find(Msg->Msg()))
+ target->OnEvent(Msg);
+ }
+
if (_View)
{
switch (Msg->m)
{
#if 1
case WM_CTLCOLORBTN:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORSTATIC:
{
HDC hdc = (HDC)Msg->A();
HWND hwnd = (HWND)Msg->B();
LViewI *v = FindControl(hwnd);
LView *gv = v ? v->GetGView() : NULL;
if (gv)
{
int Depth = dynamic_cast(gv) ? 1 : 10;
LColour Fore = gv->StyleColour(LCss::PropColor, LColour(), Depth);
LColour Back = gv->StyleColour(LCss::PropBackgroundColor, LColour(), Depth);
if (Fore.IsValid())
{
COLORREF c = RGB(Fore.r(), Fore.g(), Fore.b());
SetTextColor(hdc, c);
}
if (Back.IsValid())
{
COLORREF c = RGB(Back.r(), Back.g(), Back.b());
SetBkColor(hdc, c);
SetDCBrushColor(hdc, c);
}
if (Fore.IsValid() || Back.IsValid())
{
#if !defined(DC_BRUSH)
#define DC_BRUSH 18
#endif
return (LRESULT) GetStockObject(DC_BRUSH);
}
}
goto ReturnDefaultProc;
return 0;
}
#endif
case 5700:
{
// I forget what this is for...
break;
}
case WM_ERASEBKGND:
{
return 1;
}
case WM_GETFONT:
{
LFont *f = GetFont();
if (!f || f == LSysFont)
return (LMessage::Result) LSysFont->Handle();
return (LMessage::Result) f->Handle();
break;
}
case WM_MENUCHAR:
case WM_MEASUREITEM:
{
return LMenu::_OnEvent(Msg);
break;
}
case WM_DRAWITEM:
{
DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT*)Msg->B();
if (di)
{
if (di->CtlType == ODT_MENU)
{
return LMenu::_OnEvent(Msg);
}
/*
else if (di->CtlType == ODT_BUTTON)
{
LView *b;
if (CastHwnd(b, di->hwndItem) &&
b->GetCss())
{
LScreenDC dc(di->hDC, di->hwndItem);
switch (di->itemAction)
{
case ODA_DRAWENTIRE:
{
LRect c = di->rcItem;
LMemDC m(c.X(), c.Y(), GdcD->GetColourSpace());
HDC hdc = m.StartDC();
m.Colour(LColour(255, 0, 255));
m.Line(0, 0, m.X()-1, m.Y()-1);
LONG s = GetWindowLong(_View, GWL_STYLE);
SetWindowLong(_View, GWL_STYLE, (s & ~BS_TYPEMASK) | BS_PUSHBUTTON);
SendMessage(_View, WM_PRINT, (WPARAM)hdc, PRF_ERASEBKGND|PRF_CLIENT);
SetWindowLong(_View, GWL_STYLE, (s & ~BS_TYPEMASK) | BS_OWNERDRAW);
m.EndDC();
dc.Blt(0, 0, &m);
break;
}
case ODA_FOCUS:
{
break;
}
case ODA_SELECT:
{
break;
}
}
return true;
}
}
*/
}
if (!(WndFlags & GWF_DIALOG))
goto ReturnDefaultProc;
break;
}
case WM_ENABLE:
{
Invalidate(&Pos);
break;
}
case WM_HSCROLL:
case WM_VSCROLL:
{
LViewI *Wnd = FindControl((HWND) Msg->b);
if (Wnd)
{
Wnd->OnEvent(Msg);
}
break;
}
case WM_GETDLGCODE:
{
// we handle all tab control stuff
return DLGC_WANTALLKEYS; // d->WndDlgCode | DLGC_WANTTAB;
}
case WM_MOUSEWHEEL:
{
// short fwKeys = LOWORD(Msg->a); // key flags
short zDelta = (short) HIWORD(Msg->a); // wheel rotation
int nScrollLines = - _lgi_mouse_wheel_lines();
double Lines = ((double)zDelta * (double)nScrollLines) / WHEEL_DELTA;
if (ABS(Lines) < 1.0)
Lines *= 1.0 / ABS(Lines);
// LgiTrace("Lines = %g, zDelta = %i, nScrollLines = %i\n", Lines, zDelta, nScrollLines);
// Try giving the event to the current window...
if (!OnMouseWheel(Lines))
{
// Find the window under the cursor... and try giving it the mouse wheel event
short xPos = (short) LOWORD(Msg->b); // horizontal position of pointer
short yPos = (short) HIWORD(Msg->b); // vertical position of pointer
POINT Point = {xPos, yPos};
HWND hUnder = ::WindowFromPoint(Point);
HWND hParent = ::GetParent(hUnder);
if (hUnder &&
hUnder != _View && // Don't want to send ourselves a message...
hParent != _View) // WM_MOUSEWHEEL will propagate back up to us and cause an infinite loop
{
// Do a post event in case the window is deleting... at least it won't crash.
PostMessage(hUnder, Msg->m, Msg->a, Msg->b);
}
}
return 0;
}
case M_CHANGE:
{
LWindow *w = GetWindow();
LAutoPtr note((LNotification*)Msg->B());
LViewI *Ctrl = w ? w->FindControl((int)Msg->a) : 0;
if (Ctrl)
{
LAssert(note.Get() != NULL);
return OnNotify(Ctrl, note ? *note : LNotifyNull);
}
else
{
LgiTrace("Ctrl %i not found.\n", Msg->a);
}
break;
}
case M_COMMAND:
{
// LViewI *Ci = FindControl((HWND) Msg->b);
// LView *Ctrl = Ci ? Ci->GetGView() : 0;
LView *Ctrl;
if (Msg->b &&
CastHwnd(Ctrl, (HWND)Msg->b))
{
short Code = HIWORD(Msg->a);
switch (Code)
{
case CBN_CLOSEUP:
{
PostMessage(_View, WM_COMMAND, MAKELONG(Ctrl->GetId(), CBN_EDITCHANGE), Msg->b);
break;
}
case CBN_EDITCHANGE: // COMBO
{
Ctrl->SysOnNotify(Msg->Msg(), Code);
OnNotify(Ctrl, LNotifyValueChanged);
break;
}
/*
case BN_CLICKED: // BUTTON
case EN_CHANGE: // EDIT
*/
default:
{
Ctrl->SysOnNotify(Msg->Msg(), Code);
break;
}
}
}
break;
}
case WM_NCDESTROY:
{
#if _MSC_VER >= _MSC_VER_VS2005
SetWindowLongPtr(_View, GWLP_USERDATA, 0);
#else
SetWindowLong(_View, GWL_USERDATA, 0);
#endif
_View = NULL;
if (WndFlags & GWF_QUIT_WND)
{
delete this;
}
break;
}
case WM_CLOSE:
{
if (OnRequestClose(false))
{
Quit();
}
break;
}
case WM_DESTROY:
{
OnDestroy();
break;
}
case WM_CREATE:
{
SetId(d->CtrlId);
LWindow *w = GetWindow();
if (w && w->GetFocus() == this)
{
HWND hCur = GetFocus();
if (hCur != _View)
{
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus(%p) (%s)\\n", __FILE__, __LINE__, Handle(), GetClass());
}
SetFocus(_View);
}
}
if (TestFlag(GViewFlags, GWF_DROP_TARGET))
{
DropTarget(true);
}
OnCreate();
break;
}
case WM_SETFOCUS:
{
LWindow *w = GetWindow();
if (w)
{
w->SetFocus(this, LWindow::GainFocus);
}
else
{
// This can happen in popup sub-trees of views. Where the focus
// is tracked separately from the main LWindow.
OnFocus(true);
Invalidate((LRect*)NULL, false, true);
}
break;
}
case WM_KILLFOCUS:
{
LWindow *w = GetWindow();
if (w)
{
w->SetFocus(this, LWindow::LoseFocus);
}
else
{
// This can happen when the LWindow is being destroyed
Invalidate((LRect*)NULL, false, true);
OnFocus(false);
}
break;
}
case WM_WINDOWPOSCHANGED:
{
if (!IsIconic(_View))
{
WINDOWPOS *Info = (LPWINDOWPOS) Msg->b;
if (Info)
{
if (Info->x == -32000 &&
Info->y == -32000)
{
#if 0
LgiTrace("WM_WINDOWPOSCHANGED %i,%i,%i,%i (icon=%i)\\n",
Info->x,
Info->y,
Info->cx,
Info->cy,
IsIconic(Handle()));
#endif
}
else
{
LRect r;
r.ZOff(Info->cx-1, Info->cy-1);
r.Offset(Info->x, Info->y);
if (r.Valid() && r != Pos)
{
Pos = r;
}
}
}
OnPosChange();
}
if (!(WndFlags & GWF_DIALOG))
{
goto ReturnDefaultProc;
}
break;
}
case WM_CAPTURECHANGED:
{
LViewI *Wnd;
if (Msg->B() &&
CastHwnd(Wnd, (HWND)Msg->B()))
{
if (Wnd != _Capturing)
{
#if DEBUG_CAPTURE
LgiTrace("%s:%i - _Capturing %p/%s -> %p/%s\n",
_FL,
_Capturing, _Capturing?_Capturing->GetClass():0,
Wnd, Wnd?Wnd->GetClass() : 0);
#endif
_Capturing = Wnd;
}
}
else if (_Capturing)
{
#if DEBUG_CAPTURE
LgiTrace("%s:%i - _Capturing %p/%s -> NULL\n",
_FL,
_Capturing, _Capturing?_Capturing->GetClass():0);
#endif
_Capturing = NULL;
}
break;
}
case M_MOUSEENTER:
{
LMouse Ms;
Ms.Target = this;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = 0;
LViewI *MouseOver = WindowFromPoint(Ms.x, Ms.y);
if (MouseOver &&
_Over != MouseOver &&
!(MouseOver == this || MouseOver->Handle() == 0))
{
if (_Capturing)
{
if (MouseOver == _Capturing)
{
Ms = lgi_adjust_click(Ms, _Capturing);
_Capturing->OnMouseEnter(Ms);
}
}
else
{
if (_Over)
{
LMouse m = lgi_adjust_click(Ms, _Over);
_Over->OnMouseExit(m);
#if DEBUG_OVER
LgiTrace("Enter.LoseOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name());
#endif
}
_Over = MouseOver;
if (_Over)
{
#if DEBUG_OVER
LgiTrace("Enter.GetOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name());
#endif
LMouse m = lgi_adjust_click(Ms, _Over);
_Over->OnMouseEnter(m);
}
}
}
break;
}
case M_MOUSEEXIT:
{
if (_Over)
{
LMouse Ms;
Ms.Target = this;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = 0;
bool Mine = false;
if (_Over->Handle())
{
Mine = _Over == this;
}
else
{
for (LViewI *o = _Capturing ? _Capturing : _Over; o; o = o->GetParent())
{
if (o == this)
{
Mine = true;
break;
}
}
}
if (Mine)
{
if (_Capturing)
{
LMouse m = lgi_adjust_click(Ms, _Capturing);
_Capturing->OnMouseExit(m);
}
else
{
#if DEBUG_OVER
LgiTrace("Exit.LoseOver=%p '%-20s'\n", _Over, _Over->Name());
#endif
_Over->OnMouseExit(Ms);
_Over = 0;
}
}
}
break;
}
case WM_MOUSEMOVE:
{
LMouse Ms;
Ms.Target = this;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags();
Ms.IsMove(true);
if (TestFlag(Msg->a, MK_LBUTTON)) SetFlag(Ms.Flags, LGI_EF_LEFT);
if (TestFlag(Msg->a, MK_RBUTTON)) SetFlag(Ms.Flags, LGI_EF_RIGHT);
if (TestFlag(Msg->a, MK_MBUTTON)) SetFlag(Ms.Flags, LGI_EF_MIDDLE);
SetKeyFlag(Ms.Flags, VK_MENU, MK_ALT);
Ms.Down((Msg->a & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) != 0);
LViewI *MouseOver = WindowFromPoint(Ms.x, Ms.y);
if (_Over != MouseOver)
{
if (_Over)
{
#if DEBUG_OVER
LgiTrace("Move.LoseOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name());
#endif
LMouse m = lgi_adjust_click(Ms, _Over);
_Over->OnMouseExit(m);
}
_Over = MouseOver;
if (_Over)
{
LMouse m = lgi_adjust_click(Ms, _Over);
_Over->OnMouseEnter(m);
#if DEBUG_OVER
LgiTrace("Move.GetOver=%p/%s '%-20s'\n", _Over, _Over->GetClass(), _Over->Name());
#endif
}
}
// int CurX = Ms.x, CurY = Ms.y;
LCursor Cursor = (_Over ? _Over : this)->GetCursor(Ms.x, Ms.y);
LgiToWindowsCursor(_View, Cursor);
#if 0
LgiTrace("WM_MOUSEMOVE %i,%i target=%p/%s, over=%p/%s, cap=%p/%s\n",
Ms.x, Ms.y,
Ms.Target, Ms.Target?Ms.Target->GetClass():0,
_Over, _Over?_Over->GetClass():0,
_Capturing, _Capturing?_Capturing->GetClass():0);
#endif
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
return 0;
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
{
Ms.Target->OnMouseMove(Ms);
}
break;
}
case WM_NCHITTEST:
{
POINT Pt = { LOWORD(Msg->b), HIWORD(Msg->b) };
ScreenToClient(_View, &Pt);
int Hit = OnHitTest(Pt.x, Pt.y);
if (Hit >= 0)
{
// LgiTrace("%I64i Hit=%i\n", LCurrentTime(), Hit);
return Hit;
}
if (!(WndFlags & GWF_DIALOG))
{
goto ReturnDefaultProc;
}
break;
}
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
{
LMouse Ms;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags() | LGI_EF_LEFT;
Ms.Down(Msg->m != WM_LBUTTONUP);
Ms.Double(Msg->m == WM_LBUTTONDBLCLK);
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
Ms.Target = this;
#if DEBUG_MOUSE_CLICKS
LString Msg;
Msg.Printf("%s.Click", Ms.Target->GetClass());
Ms.Trace(Msg);
#endif
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
Ms.Target->OnMouseClick(Ms);
break;
}
case WM_RBUTTONDBLCLK:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
{
LMouse Ms;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags() | LGI_EF_RIGHT;
Ms.Down(Msg->m != WM_RBUTTONUP);
Ms.Double(Msg->m == WM_RBUTTONDBLCLK);
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
Ms.Target = this;
#if DEBUG_MOUSE_CLICKS
LString Msg;
Msg.Printf("%s.Click", Ms.Target->GetClass());
Ms.Trace(Msg);
#endif
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
Ms.Target->OnMouseClick(Ms);
break;
}
case WM_MBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
{
LMouse Ms;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags() | LGI_EF_MIDDLE;
Ms.Down(Msg->m != WM_MBUTTONUP);
Ms.Double(Msg->m == WM_MBUTTONDBLCLK);
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
Ms.Target = this;
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
Ms.Target->OnMouseClick(Ms);
break;
}
case WM_XBUTTONDBLCLK:
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
{
LMouse Ms;
int Clicked = (Msg->a >> 16) & 0xffff;
Ms.x = (short) (Msg->b&0xFFFF);
Ms.y = (short) (Msg->b>>16);
Ms.Flags = _lgi_get_key_flags() | LGI_EF_MIDDLE;
Ms.Button1(TestFlag(Clicked, XBUTTON1));
Ms.Button2(TestFlag(Clicked, XBUTTON2));
Ms.Down(Msg->m != WM_XBUTTONUP);
Ms.Double(Msg->m == WM_XBUTTONDBLCLK);
if (_Capturing)
Ms = lgi_adjust_click(Ms, _Capturing, true);
else if (_Over)
Ms = lgi_adjust_click(Ms, _Over);
else
Ms.Target = this;
LWindow *Wnd = GetWindow();
if (!Wnd || Wnd->HandleViewMouse(dynamic_cast(Ms.Target), Ms))
Ms.Target->OnMouseClick(Ms);
break;
}
case WM_SYSKEYUP:
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
case WM_KEYUP:
{
static char AltCode[32];
bool IsDialog = TestFlag(WndFlags, GWF_DIALOG);
bool IsDown = Msg->m == WM_KEYDOWN || Msg->m == WM_SYSKEYDOWN;
int KeyFlags = _lgi_get_key_flags();
HWND hwnd = _View;
if (SysOnKey(this, Msg))
{
// LgiTrace("SysOnKey true, Msg=0x%x %x,%x\n", Msg->m, Msg->a, Msg->b);
return 0;
}
else
{
// Key
LKey Key((int)Msg->a, (int)Msg->b);
Key.Flags = KeyFlags;
Key.Down(IsDown);
Key.IsChar = false;
if (Key.Ctrl())
{
Key.c16 = (char16)Msg->a;
}
if (Key.c16 == VK_TAB && ConsumeTabKey)
{
ConsumeTabKey--;
}
else
{
LWindow *Wnd = GetWindow();
if (Wnd)
{
if (Key.Alt() ||
Key.Ctrl() ||
(Key.c16 < 'A' || Key.c16 > 'Z'))
{
Wnd->HandleViewKey(this, Key);
}
}
else
{
OnKey(Key);
}
}
if (Msg->m == WM_SYSKEYUP || Msg->m == WM_SYSKEYDOWN)
{
if (Key.vkey >= VK_F1 &&
Key.vkey <= VK_F12 &&
Key.Alt() == false)
{
// So in LgiIde if you press F10 (debug next) you get a hang
// sometimes in DefWindowProc. Until I figure out what's going
// on this code exits before calling DefWindowProc without
// breaking other WM_SYSKEY* functionality (esp Alt+F4).
return 0;
}
}
}
if (!IsDialog)
{
// required for Alt-Key function (eg Alt-F4 closes window)
goto ReturnDefaultProc;
}
break;
}
#if OLD_WM_CHAR_MODE
case WM_CHAR:
{
LKey Key((int)Msg->a, (int)Msg->b);
Key.Flags = _lgi_get_key_flags();
Key.Down(true);
Key.IsChar = true;
bool Shift = Key.Shift();
bool Caps = TestFlag(Key.Flags, LGI_EF_CAPS_LOCK);
if (!(Shift ^ Caps))
{
Key.c16 = ToLower(Key.c16);
}
else
{
Key.c16 = ToUpper(Key.c16);
}
if (Key.c16 == LK_TAB && ConsumeTabKey)
{
ConsumeTabKey--;
}
else
{
LWindow *Wnd = GetWindow();
if (Wnd)
{
Wnd->HandleViewKey(this, Key);
}
else
{
OnKey(Key);
}
}
break;
}
#endif
case M_SET_WND_STYLE:
{
SetWindowLong(Handle(), GWL_STYLE, (LONG)Msg->b);
SetWindowPos( Handle(),
0, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOSIZE | SWP_FRAMECHANGED);
break;
}
case WM_PAINT:
{
_Paint();
break;
}
case WM_NCPAINT:
{
if (GetWindow() != this &&
!TestFlag(WndFlags, GWF_SYS_BORDER))
{
HDC hDC = GetWindowDC(_View);
LScreenDC Dc(hDC, _View, true);
LRect p(0, 0, Dc.X()-1, Dc.Y()-1);
OnNcPaint(&Dc, p);
}
goto ReturnDefaultProc;
break;
}
case WM_NCCALCSIZE:
{
LMessage::Param Status = 0;
int Edge = (Sunken() || Raised()) ? _BorderSize : 0;
RECT *rc = NULL;
if (Msg->a)
{
NCCALCSIZE_PARAMS *p = (NCCALCSIZE_PARAMS*) Msg->b;
rc = p->rgrc;
}
else
{
rc = (RECT*)Msg->b;
}
if (!(WndFlags & GWF_DIALOG))
{
Status = DefWindowProcW(_View, Msg->m, Msg->a, Msg->b);
}
if (Edge && rc && !TestFlag(WndFlags, GWF_SYS_BORDER))
{
rc->left += Edge;
rc->top += Edge;
rc->right -= Edge;
rc->bottom -= Edge;
return 0;
}
return Status;
}
case WM_NOTIFY:
{
NMHDR *Hdr = (NMHDR*)Msg->B();
if (Hdr)
{
LView *Wnd;
if (CastHwnd(Wnd, Hdr->hwndFrom))
Wnd->SysOnNotify(Msg->Msg(), Hdr->code);
}
break;
}
case M_THREAD_COMPLETED:
{
auto Th = (LThread*)Msg->A();
if (!Th)
break;
Th->OnComplete();
if (Th->GetDeleteOnExit())
delete Th;
return true;
}
default:
{
if (!(WndFlags & GWF_DIALOG))
goto ReturnDefaultProc;
break;
}
}
}
return 0;
ReturnDefaultProc:
#ifdef _DEBUG
uint64 start = LCurrentTime();
#endif
LRESULT r = DefWindowProcW(_View, Msg->m, Msg->a, Msg->b);
#ifdef _DEBUG
uint64 now = LCurrentTime();
if (now - start > 1000)
{
LgiTrace("DefWindowProc(0x%.4x, %i, %i) took %ims\n",
Msg->m, Msg->a, Msg->b, (int)(now - start));
}
#endif
return r;
}
LViewI *LView::FindControl(OsView hCtrl)
{
if (_View == hCtrl)
{
return this;
}
for (List::I i = Children.begin(); i.In(); i++)
{
LViewI *Ctrl = (*i)->FindControl(hCtrl);
if (Ctrl)
return Ctrl;
}
return 0;
}
diff --git a/src/win/Widgets/Dialog_Win.cpp b/src/win/Widgets/Dialog_Win.cpp
--- a/src/win/Widgets/Dialog_Win.cpp
+++ b/src/win/Widgets/Dialog_Win.cpp
@@ -1,419 +1,417 @@
/*hdr
** FILE: GWidgets.cpp
** AUTHOR: Matthew Allen
** DATE: 8/9/1998
** DESCRIPTION: Dialog components
**
** Copyright (C) 1998-2001 Matthew Allen
** fret@memecode.com
*/
#include
#include
#include "lgi/common/Lgi.h"
#include
#include "lgi/common/TableLayout.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/Button.h"
#include "lgi/common/LgiRes.h"
struct LDialogPriv
{
bool IsModal = false, IsModeless = false, _Resizable = true;
int ModalStatus = -1;
int BtnId = -1;
int ModalResult = -1;
// Modal state
OsView ParentHnd = NULL;
LWindow *ParentWnd = NULL;
LDialog::OnClose Callback;
};
///////////////////////////////////////////////////////////////////////////////////////////
LDialog::LDialog(LViewI *parent)
: ResObject(Res_Dialog)
{
d = new LDialogPriv;
_Window = this;
Name("Dialog");
if (parent)
SetParent(parent);
SetStyle(GetStyle() & ~(WS_MINIMIZEBOX | WS_MAXIMIZEBOX));
SetStyle(GetStyle() | WS_DLGFRAME);
}
LDialog::~LDialog()
{
LAssert(!d->IsModal && !d->IsModeless); // Can't delete while still active...
DeleteObj(d);
}
bool LDialog::LoadFromResource(int Resource, char *TagList)
{
LAutoString n;
bool Status = LResourceLoad::LoadFromResource(Resource, this, &Pos, &n, TagList);
if (Status && n)
Name(n);
return Status;
}
LRESULT CALLBACK DlgRedir(HWND hWnd, UINT m, WPARAM a, LPARAM b)
{
if (m == WM_INITDIALOG)
{
LDialog *NewWnd = (LDialog*) b;
NewWnd->_View = hWnd;
#if _MSC_VER >= _MSC_VER_VS2005
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)(LViewI*)NewWnd);
#else
SetWindowLong(hWnd, GWL_USERDATA, (LONG)(LViewI*)NewWnd);
#endif
}
LViewI *Wnd = (LViewI*)
#if _MSC_VER >= _MSC_VER_VS2005
#pragma warning(disable : 4312)
GetWindowLongPtr(hWnd, GWLP_USERDATA);
#pragma warning(default : 4312)
#else
GetWindowLong(hWnd, GWL_USERDATA);
#endif
if (Wnd)
{
LMessage Msg(m, a, b);
return Wnd->OnEvent(&Msg);
}
return 0;
}
bool LDialog::OnRequestClose(bool OsClose)
{
return true;
}
bool LDialog::IsModal()
{
return d->IsModal;
}
void LDialog::DoModal(OnClose Callback, OsView ParentHnd)
{
d->IsModal = true;
d->Callback = Callback;
LViewI *p = GetParent();
if (p && p->GetWindow() != p)
p = p->GetWindow();
if (Attach(0))
{
AttachChildren();
d->ParentWnd = dynamic_cast(p);
if (d->ParentWnd)
d->ParentWnd->_Dialog = this;
if (p)
{
LRect pp = p->GetPos();
if (pp.Valid())
{
int cx = pp.x1 + (pp.X() >> 1);
int cy = pp.y1 + (pp.Y() >> 1);
LRect np = GetPos();
np.Offset( cx - (np.X() >> 1) - np.x1,
cy - (np.Y() >> 1) - np.y1);
SetPos(np);
MoveOnScreen();
}
}
Visible(true);
d->ParentHnd = d->ParentHnd ? d->ParentHnd : (p ? p->Handle() : NULL);
if (d->ParentHnd)
EnableWindow(d->ParentHnd, false);
}
else
{
LAssert(!"Attach failed.");
}
}
void LDialog::EndModal(int Code)
{
if (!d->IsModal)
{
LAssert(!"Not a modal dialog.");
return;
}
// This is so the calling code can unwind all it's stack frames without
// worrying about accessing things that have been deleted.
PostEvent(M_DIALOG_END_MODAL, (LMessage::Param)Code);
}
static char *BaseStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
int LDialog::DoModeless()
{
int Status = -1;
LAssert(!_View);
if (_View)
return Status;
d->IsModeless = true;
d->IsModal = false;
LViewI *p = GetParent();
if (p && p->GetWindow() != p)
p = p->GetWindow();
if (Attach(0))
{
AttachChildren();
if (p && Handle() && p->Handle())
{
#ifdef _WIN64
SetWindowLongPtr(Handle(), GWLP_HWNDPARENT, (LONG_PTR)p->Handle());
#else
SetWindowLong(Handle(), GWL_HWNDPARENT, (LONG)p->Handle());
#endif
}
if (p)
{
LRect pp = p->GetPos();
int cx = pp.x1 + (pp.X() >> 1);
int cy = pp.y1 + (pp.Y() >> 1);
LRect np = GetPos();
np.Offset( cx - (np.X() >> 1) - np.x1,
cy - (np.Y() >> 1) - np.y1);
SetPos(np);
MoveOnScreen();
}
Visible(true);
Status = true;
}
else
{
LAssert(!"Attach failed.");
}
return Status;
}
LMessage::Result LDialog::OnEvent(LMessage *Msg)
{
switch (Msg->m)
{
case WM_CREATE:
{
LRect r = Pos;
Pos.ZOff(-1, -1);
SetPos(r); // resets the dialog to the correct
// size when large fonts are used
if (GetAlwaysOnTop())
SetAlwaysOnTop(true);
AttachChildren();
if (!_Default)
SetDefault(FindControl(IDOK));
LResources::StyleElement(this);
// This was commented out. I've re-introduced it until such time
// as there is a good reason not to have it enabled. If such a reason
// arises, update this comment to reflect that.
OnCreate();
// If we don't return true here the LWindow::OnEvent handler for
// WM_CREATE will call OnCreate again.
return true;
}
case M_DIALOG_END_MODAL:
{
// See ::EndModal for comment on why this is here.
d->ModalResult = max((int)Msg->A(), 0);
if (d->ParentHnd)
EnableWindow(d->ParentHnd, true);
if (d->ParentWnd)
d->ParentWnd->_Dialog = NULL;
Visible(false);
d->IsModal = false;
if (d->Callback)
d->Callback(this, d->ModalResult);
else
delete this; // default action is to delete the dialog
return 1;
}
}
return LWindow::OnEvent(Msg);
}
int LDialog::GetButtonId()
{
return d->BtnId;
}
int LDialog::OnNotify(LViewI *Ctrl, LNotification n)
{
LButton *b = dynamic_cast(Ctrl);
if (b)
{
d->BtnId = b->GetId();
if (d->IsModal)
EndModal(d->BtnId);
else if (d->IsModeless)
EndModeless();
}
return 0;
}
void LDialog::Quit(bool DontDelete)
{
if (d->IsModal)
EndModal(0);
else
LView::Quit(DontDelete);
}
void LDialog::OnPosChange()
{
if (Children.Length() == 1)
{
List::I it = Children.begin();
LLayout *t = dynamic_cast((LViewI*)it);
if (t)
{
LRect r = GetClient();
r.Inset(LTableLayout::CellSpacing, LTableLayout::CellSpacing);
t->SetPos(r);
}
}
}
void LDialog::EndModeless(int Code)
{
if (d->IsModeless)
Quit(Code != 0);
}
///////////////////////////////////////////////////////////////////////////////////////////
-LControl::LControl(char *SubClassName) : LView(0)
+LControl::LControl(const char *SubClassName) : LView(0)
{
- SubClass = 0;
- SetOnDelete = NULL;
if (SubClassName)
{
SetClassW32(SubClassName);
SubClass = LWindowsClass::Create(SubClassName); // Owned by the LWindowsClass object
}
Pos.ZOff(10, 10);
}
LControl::~LControl()
{
if (SetOnDelete)
*SetOnDelete = true;
}
LMessage::Result LControl::OnEvent(LMessage *Msg)
{
LMessage::Result Status = 0;
// Pre-OS event handler
switch (Msg->m)
{
case WM_CREATE:
{
SetId(GetId());
OnCreate();
break;
}
case WM_SETTEXT:
{
if (IsWindowUnicode(_View))
LBase::NameW((char16*)Msg->b);
else
LBase::Name((char*)Msg->b);
break;
}
case WM_GETDLGCODE:
case WM_NOTIFY:
case WM_COMMAND:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_KEYDOWN:
case WM_KEYUP:
{
bool Deleted = false;
SetOnDelete = &Deleted;
Status = LView::OnEvent(Msg);
if (Deleted)
return Status;
SetOnDelete = NULL;
break;
}
case WM_CTLCOLOREDIT:
case WM_CTLCOLORSTATIC:
{
// These should never be called.. but just in case.
return LView::OnEvent(Msg);
break;
}
case WM_NCDESTROY:
{
Status = LView::OnEvent(Msg);
break;
}
}
// OS event handler
if (SubClass)
Status = SubClass->CallParent(Handle(), Msg->m, Msg->a, Msg->b);
return Status;
}
LPoint LControl::SizeOfStr(const char *Str)
{
LPoint Pt(0, 0);
if (Str)
{
for (const char *s=Str; s && *s; )
{
const char *e = strchr(s, '\n');
if (!e) e = s + strlen(s);
LDisplayString ds(LSysFont, (char*)s, e - s);
Pt.y += ds.Y();
Pt.x = max(Pt.x, ds.X());
s = (*e=='\n') ? e + 1 : 0;
}
}
return Pt;
}