diff --git a/Lgi.xml b/Lgi.xml
--- a/Lgi.xml
+++ b/Lgi.xml
@@ -1,597 +1,598 @@
-
+
+
+
-
+
-
Makefile.linux
Makefile.win64
Makefile.macosx
gcc
0
./include
./private/common
./include
./private/common
./include/lgi/linux
./include/lgi/linux/Gtk
./private/linux
./include/lgi/linux
./include/lgi/linux/Gtk
./private/linux
./include/lgi/win
./private/win
./include/lgi/win
./private/win
./include/lgi/haiku
./private/haiku
./include/lgi/haiku
./private/haiku
/usr/include/libappindicator3-0.1
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gstreamer-1.0`
/usr/include/libappindicator3-0.1
`pkg-config --cflags gtk+-3.0`
`pkg-config --cflags gstreamer-1.0`
magic
appindicator3
crypt
-static-libgcc
`pkg-config --libs gtk+-3.0`
magic
appindicator3
crypt
-static-libgcc
`pkg-config --libs gtk+-3.0`
-static-libgcc
gnu
network
be
-static-libgcc
gnu
network
be
lgi-gtk3
lgi-gtk3
DynamicLibrary
LGI_LIBRARY
LGI_LIBRARY
POSIX
_GNU_SOURCE
POSIX
_GNU_SOURCE
diff --git a/include/lgi/common/Window.h b/include/lgi/common/Window.h
--- a/include/lgi/common/Window.h
+++ b/include/lgi/common/Window.h
@@ -1,315 +1,327 @@
#ifndef _LWINDOW_H_
#define _LWINDOW_H_
#include "lgi/common/View.h"
/// The available states for a top level window
enum LWindowZoom
{
/// Minimized
LZoomMin,
/// Restored/Normal
LZoomNormal,
/// Maximized
LZoomMax
};
enum LWindowHookType
{
LNoEvents = 0,
/// \sa LWindow::RegisterHook()
LMouseEvents = 1,
/// \sa LWindow::RegisterHook()
LKeyEvents = 2,
/// \sa LWindow::RegisterHook()
LKeyAndMouseEvents = LMouseEvents | LKeyEvents,
};
/// A top level window.
class LgiClass LWindow :
public LView,
// This needs to be second otherwise is causes v-table problems.
#ifndef LGI_SDL
virtual
#endif
public LDragDropTarget
{
friend class BViewRedir;
friend class LApp;
friend class LView;
friend class LButton;
friend class LDialog;
friend class LWindowPrivate;
friend struct LDialogPriv;
bool _QuitOnClose;
protected:
class LWindowPrivate *d;
#if WINNATIVE
LRect OldPos;
LWindow *_Dialog = NULL;
#elif defined(HAIKU)
LWindowZoom _PrevZoom = LZoomNormal;
#else
OsWindow Wnd;
void SetDeleteOnClose(bool i);
#endif
#if defined __GTK_H__
friend class LMenu;
friend void lgi_widget_size_allocate(Gtk::GtkWidget *widget, Gtk::GtkAllocation *allocation);
Gtk::GtkWidget *_Root, *_VBox, *_MenuBar;
void OnGtkDelete();
Gtk::gboolean OnGtkEvent(Gtk::GtkWidget *widget, Gtk::GdkEvent *event);
#elif defined(LGI_CARBON)
friend pascal OSStatus LgiWindowProc(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData);
void _Delete();
bool _RequestClose(bool os);
#elif defined(__OBJC__)
public:
// This returns the root level content NSView
NSView *Handle();
protected:
#endif
/// The default button
LViewI *_Default = NULL;
/// The menu on the window
LMenu *Menu = NULL;
void SetChildDialog(LDialog *Dlg);
void SetDragHandlers(bool On);
/// Haiku: This shuts down the window's thread cleanly.
int WaitThread();
public:
#ifdef _DEBUG
LMemDC DebugDC;
#endif
#ifdef __GTK_H__
LWindow(Gtk::GtkWidget *w = NULL);
#elif LGI_CARBON
LWindow(WindowRef wr = NULL);
#elif LGI_COCOA
LWindow(OsWindow wnd = NULL);
#else
LWindow();
#endif
~LWindow();
const char *GetClass() override { return "LWindow"; }
/// Lays out the child views into the client area.
virtual void PourAll();
/// Returns the current menu object
LMenu *GetMenu() { return Menu; }
/// Set the menu object.
void SetMenu(LMenu *m) { Menu = m; }
/// Set the window's icon
bool SetIcon(const char *FileName);
/// Gets the "quit on close" setting.
bool GetQuitOnClose() { return _QuitOnClose; }
/// \brief Sets the "quit on close" setting.
///
/// When this is switched on the application will quit the main message
/// loop when this LWindow is closed. This is really useful for your
/// main application window. Otherwise the UI will disappear but the
/// application is still running.
void SetQuitOnClose(bool i) { _QuitOnClose = i; }
bool GetSnapToEdge();
void SetSnapToEdge(bool b);
bool GetAlwaysOnTop();
void SetAlwaysOnTop(bool b);
/// Gets the current zoom setting
LWindowZoom GetZoom();
/// Sets the current zoom
void SetZoom(LWindowZoom i);
/// Raises the window to the top of the stack.
void Raise();
/// Moves a top level window on screen.
void MoveOnScreen();
/// Moves a top level to the center of the screen
void MoveToCenter();
/// Moves a top level window to where the mouse is
void MoveToMouse();
/// Moves the window to somewhere on the same screen as 'wnd'
bool MoveSameScreen(LViewI *wnd);
// Focus setting
LViewI *GetFocus();
enum FocusType
{
GainFocus,
LoseFocus,
ViewDelete
};
void SetFocus(LViewI *ctrl, FocusType type);
/// Registers a watcher to receive OnView... messages before they
/// are passed through to the intended recipient.
bool RegisterHook
(
/// The target view.
LView *Target,
/// Combination of:
/// #LMouseEvents - Where Target->OnViewMouse(...) is called for each click.
/// and
/// #LKeyEvents - Where Target->OnViewKey(...) is called for each key.
/// OR'd together.
LWindowHookType EventType,
/// Not implemented
int Priority = 0
);
/// Unregisters a hook target
bool UnregisterHook(LView *Target);
/// Gets the default view
LViewI *GetDefault();
/// Sets the default view
void SetDefault(LViewI *v);
/// Saves/loads the window's state, e.g. position, minimized/maximized etc
bool SerializeState
(
/// The data store for reading/writing
LDom *Store,
/// The field name to use for storing settings under
const char *FieldName,
/// TRUE if loading the settings into the window, FALSE if saving to the store.
bool Load
);
/// Builds a map of keyboard short cuts.
typedef LHashTbl,LViewI*> ShortcutMap;
void BuildShortcuts(ShortcutMap &Map, LViewI *v = NULL);
////////////////////// Events ///////////////////////////////
/// Called when the window zoom state changes.
virtual void OnZoom(LWindowZoom Action) {}
/// Called when the tray icon is clicked. (if present)
virtual void OnTrayClick(LMouse &m);
/// Called when the tray icon menu is about to be displayed.
virtual void OnTrayMenu(LSubMenu &m) {}
/// Called when the tray icon menu item has been selected.
virtual void OnTrayMenuResult(int MenuId) {}
/// Called when files are dropped on the window.
virtual void OnReceiveFiles(LArray &Files) {}
/// Called when a URL is sent to the window
virtual void OnUrl(const char *Url) {};
///////////////// Implementation ////////////////////////////
void OnPosChange() override;
LMessage::Result OnEvent(LMessage *Msg) override;
void OnPaint(LSurface *pDC) override;
+
+ /// Allow the window to filter mouse events:
+ /// \returns false if the Window consumed the event.
bool HandleViewMouse(LView *v, LMouse &m);
+
+ /// Allow the window to filter key events:
+ /// \returns false if the Window consumed the event.
bool HandleViewKey(LView *v, LKey &k);
+
/// Return true to accept application quit
bool OnRequestClose(bool OsShuttingDown) override;
+
bool Obscured();
bool Visible() override;
void Visible(bool i) override;
bool IsActive();
bool SetActive();
LRect &GetPos() override;
void SetDecor(bool Visible);
LPoint GetDpi();
LPointF GetDpiScale();
void ScaleSizeToDpi();
// D'n'd
int WillAccept(LDragFormats &Formats, LPoint Pt, int KeyState) override;
int OnDrop(LArray &Data, LPoint Pt, int KeyState) override;
#if !WINNATIVE
bool Attach(LViewI *p) override;
// Props
#if defined(HAIKU)
OsWindow WindowHandle() override;
#else
OsWindow WindowHandle() override { return Wnd; }
#endif
bool Name(const char *n) override;
const char *Name() override;
bool SetPos(LRect &p, bool Repaint = false) override;
LRect &GetClient(bool InClientSpace = true) override;
// Events
void OnChildrenChanged(LViewI *Wnd, bool Attaching) override;
void OnCreate() override;
virtual void OnFrontSwitch(bool b);
#else
OsWindow WindowHandle() override { return _View; }
#endif
- #if defined(LGI_SDL)
+ #if HAIKU
+
+ void SetModalDialog(LWindow *dlg);
+
+ #elif defined(LGI_SDL)
virtual bool PushWindow(LWindow *v);
virtual LWindow *PopWindow();
#elif defined __GTK_H__
void OnGtkRealize();
bool IsAttached();
void Quit(bool DontDelete = false);
LRect *GetDecorSize();
bool TranslateMouse(LMouse &m);
LViewI *WindowFromPoint(int x, int y, bool Debug = false);
void _SetDynamic(bool b);
void _OnViewDelete();
void SetParent(LViewI *p) override;
#elif defined(MAC)
bool PostEvent(int Cmd, LMessage::Param a = 0, LMessage::Param b = 0, int64_t TimeoutMs = -1) override;
void Quit(bool DontDelete = false) override;
int OnCommand(int Cmd, int Event, OsView Wnd) override;
LViewI *WindowFromPoint(int x, int y, int DebugDebug = 0) override;
#if defined(LGI_CARBON)
OSErr HandlerCallback(DragTrackingMessage *tracking, DragRef theDrag);
#endif
#endif
};
#endif
diff --git a/include/lgi/haiku/LgiOsClasses.h b/include/lgi/haiku/LgiOsClasses.h
--- a/include/lgi/haiku/LgiOsClasses.h
+++ b/include/lgi/haiku/LgiOsClasses.h
@@ -1,21 +1,22 @@
#ifndef __OS_CLASS_H
#define __OS_CLASS_H
class LLocker
{
protected:
BHandler *hnd = NULL;
const char *file = NULL;
int line = 0;
bool locked = false;
+ bool noThread = false;
public:
LLocker(BHandler *h, const char *File, int Line);
~LLocker();
bool Lock();
status_t LockWithTimeout(int64 time);
void Unlock();
};
#endif
diff --git a/private/haiku/AppPriv.h b/private/haiku/AppPriv.h
--- a/private/haiku/AppPriv.h
+++ b/private/haiku/AppPriv.h
@@ -1,82 +1,75 @@
#pragma once
#ifdef HAIKU
#include
#endif
#include "lgi/common/Json.h"
-typedef LArray AppArray;
+typedef LArray AppArray;
-class LAppPrivate
- #ifdef HAIKU
- : public BApplication
- #endif
+class LAppPrivate : public BApplication
{
public:
// Common
LApp *Owner;
LAutoPtr Config;
LFileSystem *FileSystem;
GdcDevice *GdcSystem;
OsAppArguments Args;
LLibrary *SkinLib;
LHashTbl,AppArray*> MimeToApp;
LHashTbl, LView*> Handles;
OsThread GuiThread;
OsThreadId GuiThreadId;
int MessageLoopDepth;
int CurEvent;
#if DEBUG_MSG_TYPES
LArray Types;
#endif
::LArray DeleteLater;
LMouse LastMove;
LAutoString Name;
/// Any fonts needed for styling the elements
LAutoPtr FontCache;
// Clipboard handling
int Clipboard, Utf8, Utf8String;
::LVariant ClipData;
LHashTbl, ::LVariant*> ClipNotify;
// Mouse click info
uint64 LastClickTime;
OsView LastClickWnd;
int LastClickX;
int LastClickY;
LAppPrivate(LApp *a) : Args(0, 0), Owner(a)
#ifdef HAIKU
, BApplication(LString("application/") + a->Name())
#endif
{
CurEvent = 0;
GuiThread = LGetCurrentThread();
GuiThreadId = GetCurrentThreadId();
FileSystem = 0;
GdcSystem = 0;
SkinLib = 0;
MessageLoopDepth = 0;
LastClickTime = 0;
LastClickWnd = 0;
LastClickX = 0;
LastClickY = 0;
}
~LAppPrivate()
{
- for (auto p : MimeToApp)
- {
- p.value->DeleteObjects();
- DeleteObj(p.value);
- }
+ MimeToApp.DeleteObjects();
}
LJson *GetConfig();
bool SaveConfig();
};
diff --git a/src/haiku/App.cpp b/src/haiku/App.cpp
--- a/src/haiku/App.cpp
+++ b/src/haiku/App.cpp
@@ -1,1062 +1,1103 @@
#include
#include
#include
#include
#include
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/SkinEngine.h"
#include "lgi/common/Array.h"
#include "lgi/common/Variant.h"
#include "lgi/common/Token.h"
#include "lgi/common/FontCache.h"
#include "AppPriv.h"
#include "MimeType.h"
#define DEBUG_MSG_TYPES 0
#define DEBUG_HND_WARNINGS 0
#define IDLE_ALWAYS 0
LString LgiArgsAppPath;
////////////////////////////////////////////////////////////////
struct OsAppArgumentsPriv
{
::LArray Ptr;
~OsAppArgumentsPriv()
{
Ptr.DeleteArrays();
}
};
OsAppArguments::OsAppArguments(int args, const char **arg)
{
d = new OsAppArgumentsPriv;
for (int i=0; iPtr.Add(NewStr(arg[i]));
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
}
OsAppArguments::~OsAppArguments()
{
DeleteObj(d);
}
bool OsAppArguments::Get(const char *Name, const char **Val)
{
if (!Name)
return false;
for (int i=0; iPtr.DeleteArrays();
if (!CmdLine)
return;
for (char *s = CmdLine; *s; )
{
while (*s && strchr(WhiteSpace, *s)) s++;
if (*s == '\'' || *s == '\"')
{
char delim = *s++;
char *e = strchr(s, delim);
if (e)
d->Ptr.Add(NewStr(s, e - s));
else
break;
s = e + 1;
}
else
{
char *e = s;
while (*e && !strchr(WhiteSpace, *e))
e++;
d->Ptr.Add(NewStr(s, e-s));
s = e;
}
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
}
OsAppArguments &OsAppArguments::operator =(OsAppArguments &a)
{
d->Ptr.DeleteArrays();
for (int i=0; iPtr.Add(NewStr(a.Arg[i]));
}
Args = d->Ptr.Length();
Arg = &d->Ptr[0];
return *this;
}
////////////////////////////////////////////////////////////////
#if HAS_SHARED_MIME
#include "GFilterUtils.h"
#include "mime-types.h"
class LSharedMime : public LLibrary
{
public:
LSharedMime() :
#ifdef _DEBUG
LLibrary("libsharedmime1d")
#else
LLibrary("libsharedmime1")
#endif
{
}
DynFunc0(int, mimetypes_init);
DynFunc1(const char*, mimetypes_set_default_type, const char *, default_type);
DynFunc2(const char*, mimetypes_get_file_type, const char*, pathname, mimetypes_flags, flags);
DynFunc2(const char*, mimetypes_get_data_type, const void*, data, int, length);
DynFunc3(bool, mimetypes_decode, const char *, type, char **, media_type, char **, sub_type);
DynFunc2(char *, mimetypes_convert_filename, const char *, pathname, const char *, mime_type);
DynFunc3(bool, mimetypes_add_mime_dir, const char *, path, bool, high_priority, bool, rescan);
DynFunc2(const char *, mimetypes_get_type_info, const char *, mime_type, const char *, lang);
};
#endif
#if 1
/////////////////////////////////////////////////////////////////////////////
//
// Attempts to cleanup and call drkonqi to process the crash
//
void LgiCrashHandler(int Sig)
{
// Don't get into an infinite loop
signal(SIGSEGV, SIG_DFL);
#ifndef _MSC_VER
// Our pid
int MyPid = getpid();
printf("LgiCrashHandler trigger MyPid=%i\n", MyPid);
#endif
exit(-1);
}
#endif
/////////////////////////////////////////////////////////////////////////////
#ifndef XK_Num_Lock
-#define XK_Num_Lock 0xff7f
+#define XK_Num_Lock 0xff7f
#endif
#ifndef XK_Shift_Lock
-#define XK_Shift_Lock 0xffe6
+#define XK_Shift_Lock 0xffe6
#endif
#ifndef XK_Caps_Lock
-#define XK_Caps_Lock 0xffe5
+#define XK_Caps_Lock 0xffe5
#endif
struct Msg
{
LViewI *v;
int m;
LMessage::Param a, b;
void Set(LViewI *V, int M, LMessage::Param A, LMessage::Param B)
{
v = V;
m = M;
a = A;
b = B;
}
};
// Out of thread messages... must lock before access.
class LMessageQue : public LMutex
{
public:
typedef ::LArray MsgArray;
LMessageQue() : LMutex("LMessageQue")
{
}
MsgArray *Lock(const char *file, int line)
{
if (!LMutex::Lock(file, line))
return NULL;
return &q;
}
operator bool()
{
return q.Length() > 0;
}
private:
MsgArray q;
} MsgQue;
/////////////////////////////////////////////////////////////////////////////
LApp *TheApp = NULL;
LSkinEngine *LApp::SkinEngine;
LMouseHook *LApp::MouseHook;
LApp::LApp(OsAppArguments &AppArgs, const char *name, LAppArguments *Args) :
OsApplication(AppArgs.Args, AppArgs.Arg)
{
TheApp = this;
LgiArgsAppPath = AppArgs.Arg[0];
Name(name);
d = new LAppPrivate(this);
if (LIsRelativePath(LgiArgsAppPath))
{
char Cwd[MAX_PATH_LEN];
getcwd(Cwd, sizeof(Cwd));
LMakePath(Cwd, sizeof(Cwd), Cwd, LgiArgsAppPath);
LgiArgsAppPath = Cwd;
}
char AppPathLnk[MAX_PATH_LEN];
if (LResolveShortcut(LgiArgsAppPath, AppPathLnk, sizeof(AppPathLnk)))
LgiArgsAppPath = AppPathLnk;
int WCharSz = sizeof(wchar_t);
#if defined(_MSC_VER)
LAssert(WCharSz == 2);
::LFile::Path Dlls(LgiArgsAppPath);
Dlls--;
SetDllDirectoryA(Dlls);
#else
LAssert(WCharSz == 4);
#endif
#ifdef _MSC_VER
SetEnvironmentVariable(_T("GTK_CSD"), _T("0"));
#else
setenv("GTK_CSD", "0", true);
#endif
// We want our printf's NOW!
setvbuf(stdout,(char *)NULL,_IONBF,0); // print mesgs immediately.
// Setup the file and graphics sub-systems
d->FileSystem = new LFileSystem;
d->GdcSystem = new GdcDevice;
SetAppArgs(AppArgs);
srand(LCurrentTime());
LColour::OnChange();
MouseHook = new LMouseHook;
d->GetConfig();
// System font setup
LFontType SysFontType;
if (SysFontType.GetSystemFont("System"))
{
SystemNormal = SysFontType.Create();
if (SystemNormal)
SystemNormal->Transparent(true);
SystemBold = SysFontType.Create();
if (SystemBold)
{
SystemBold->Bold(true);
SystemBold->Transparent(true);
SystemBold->Create();
}
}
else
{
printf("%s:%i - Couldn't get system font setting.\n", __FILE__, __LINE__);
}
if (!SystemNormal)
{
LgiMsg(0, "Error: Couldn't create system font.", "Lgi Error: LApp::LApp", MB_OK);
LExitApp();
}
if (!GetOption("noskin"))
{
extern LSkinEngine *CreateSkinEngine(LApp *App);
SkinEngine = CreateSkinEngine(this);
}
}
LApp::~LApp()
{
DeleteObj(AppWnd);
DeleteObj(SystemNormal);
DeleteObj(SystemBold);
DeleteObj(SkinEngine);
DeleteObj(MouseHook);
DeleteObj(d->FileSystem);
DeleteObj(d->GdcSystem);
DeleteObj(LFontSystem::Me);
DeleteObj(d);
TheApp = 0;
}
LApp *LApp::ObjInstance()
{
return TheApp;
}
bool LApp::IsOk()
{
bool Status =
#ifndef __clang__
(this != 0) &&
#endif
(d != 0);
LAssert(Status);
return Status;
}
LMouseHook *LApp::GetMouseHook()
{
return MouseHook;
}
int LApp::GetMetric(LSystemMetric Metric)
{
switch (Metric)
{
case LGI_MET_DECOR_X:
return 8;
case LGI_MET_DECOR_Y:
return 8 + 19;
case LGI_MET_DECOR_CAPTION:
return 19;
default:
break;
}
return 0;
}
LViewI *LApp::GetFocus()
{
// GtkWidget *w = gtk_window_get_focus(GtkWindow *window);
return NULL;
}
OsThread LApp::_GetGuiThread()
{
return d->GuiThread;
}
OsThreadId LApp::GetGuiThreadId()
{
return d->GuiThreadId;
}
OsProcessId LApp::GetProcessId()
{
- #ifdef WIN32
+ #ifdef WIN32
return GetCurrentProcessId();
- #else
+ #else
return getpid();
#endif
}
OsAppArguments *LApp::GetAppArgs()
{
return IsOk() ? &d->Args : 0;
}
void LApp::SetAppArgs(OsAppArguments &AppArgs)
{
if (IsOk())
{
d->Args = AppArgs;
}
}
bool LApp::InThread()
{
OsThreadId Me = GetCurrentThreadId();
OsThreadId Gui = GetGuiThreadId();
// printf("Me=%i Gui=%i\n", Me, Gui);
return Gui == Me;
}
bool LApp::Run(OnIdleProc IdleCallback, void *IdleParam)
{
if (!InThread())
{
printf("%s:%i - Error: Out of thread.\n", _FL);
return false;
}
printf("Running main loop...\n");
d->Run();
printf("Main loop finished.\n");
return true;
}
bool LApp::Yield()
{
return false;
}
void LApp::Exit(int Code)
{
if (Code)
{
// hard exit
::exit(Code);
}
else
{
// soft exit
printf("Quitting main loop...\n");
if (d->Lock())
{
d->Quit();
d->Unlock();
}
}
}
void LApp::OnUrl(const char *Url)
{
if (AppWnd)
AppWnd->OnUrl(Url);
}
void LApp::OnReceiveFiles(::LArray &Files)
{
if (AppWnd)
AppWnd->OnReceiveFiles(Files);
else
LAssert(!"You probably want to set 'AppWnd' before calling LApp::Run... maybe.");
}
const char *LApp::GetArgumentAt(int n)
{
return n >= 0 && n < d->Args.Args ? NewStr(d->Args.Arg[n]) : 0;
}
bool LApp::GetOption(const char *Option, char *Dest, int DestLen)
{
::LString Buf;
if (GetOption(Option, Buf))
{
if (Dest)
{
if (DestLen > 0)
{
strcpy_s(Dest, DestLen, Buf);
}
else return false;
}
return true;
}
return false;
}
bool LApp::GetOption(const char *Option, ::LString &Buf)
{
if (IsOk() && Option)
{
int OptLen = strlen(Option);
for (int i=1; iArgs.Args; i++)
{
auto *a = d->Args.Arg[i];
if (!a)
continue;
if (strchr("-/\\", a[0]))
{
if (strnicmp(a+1, Option, OptLen) == 0)
{
const char *Arg = NULL;
if (strlen(a+1+OptLen) > 0)
{
Arg = a + 1 + OptLen;
}
else if (i < d->Args.Args - 1)
{
Arg = d->Args.Arg[i + 1];
}
if (Arg)
{
if (strchr("\'\"", *Arg))
{
char Delim = *Arg++;
char *End = strchr(Arg, Delim);
if (End)
{
auto Len = End-Arg;
if (Len > 0)
- Buf.Set(Arg, Len);
+ Buf.Set(Arg, Len);
else
- return false;
+ return false;
}
else return false;
}
else
{
- Buf = Arg;
+ Buf = Arg;
}
}
return true;
}
}
}
}
return false;
}
void LApp::OnCommandLine()
{
::LArray Files;
for (int i=1; iArgs; i++)
{
auto a = GetAppArgs()->Arg[i];
if (LFileExists(a))
{
Files.Add(NewStr(a));
}
}
// call app
if (Files.Length() > 0)
{
OnReceiveFiles(Files);
}
// clear up
Files.DeleteArrays();
}
LString LApp::GetFileMimeType(const char *File)
{
BMimeType mt;
auto r = BMimeType::GuessMimeType(File, &mt);
if (r != B_OK)
{
LgiTrace("%s:%i - GuessMimeType(%s) failed: %i\n", _FL, File, r);
return LString();
}
return mt.Type();
}
bool LApp::GetAppsForMimeType(const char *Mime, LArray &Apps)
{
// Find alternative version of the MIME type (e.g. x-type and type).
char AltMime[256];
strcpy(AltMime, Mime);
char *s = strchr(AltMime, '/');
if (s)
{
s++;
int Len = strlen(s) + 1;
if (strnicmp(s, "x-", 2) == 0)
{
memmove(s, s+2, Len - 2);
}
else
{
memmove(s+2, s, Len);
s[0] = 'x';
s[1] = '-';
}
}
LGetAppsForMimeType(Mime, Apps);
LGetAppsForMimeType(AltMime, Apps);
return Apps.Length() > 0;
}
#if defined(LINUX)
LLibrary *LApp::GetWindowManagerLib()
{
if (this != NULL && !d->WmLib)
{
char Lib[32];
WindowManager Wm = LGetWindowManager();
switch (Wm)
{
case WM_Kde:
strcpy(Lib, "liblgikde3");
break;
case WM_Gnome:
strcpy(Lib, "liblgignome2");
break;
default:
strcpy(Lib, "liblgiother");
break;
}
#ifdef _DEBUG
strcat(Lib, "d");
#endif
d->WmLib = new LLibrary(Lib, true);
if (d->WmLib)
{
if (d->WmLib->IsLoaded())
{
Proc_LgiWmInit WmInit = (Proc_LgiWmInit) d->WmLib->GetAddress("LgiWmInit");
if (WmInit)
{
WmInitParams Params;
// Params.Dsp = XObject::XDisplay();
Params.Args = d->Args.Args;
Params.Arg = d->Args.Arg;
WmInit(&Params);
}
// else printf("%s:%i - Failed to find method 'LgiWmInit' in WmLib.\n", __FILE__, __LINE__);
}
// else printf("%s:%i - couldn't load '%s.so'\n", __FILE__, __LINE__, Lib);
}
// else printf("%s:%i - alloc error\n", __FILE__, __LINE__);
}
return d->WmLib && d->WmLib->IsLoaded() ? d->WmLib : 0;
}
void LApp::DeleteMeLater(LViewI *v)
{
d->DeleteLater.Add(v);
}
void LApp::SetClipBoardContent(OsView Hnd, ::LVariant &v)
{
// Store the clipboard data we will serve
d->ClipData = v;
}
bool LApp::GetClipBoardContent(OsView Hnd, ::LVariant &v, ::LArray &Types)
{
return false;
}
#endif
LSymLookup *LApp::GetSymLookup()
{
return NULL;
}
bool LApp::IsElevated()
{
#ifdef WIN32
LAssert(!"What API works here?");
return false;
#else
return geteuid() == 0;
#endif
}
int LApp::GetCpuCount()
{
return 1;
}
LFontCache *LApp::GetFontCache()
{
if (!d->FontCache)
d->FontCache.Reset(new LFontCache(SystemNormal));
return d->FontCache;
}
#ifdef LINUX
LApp::DesktopInfo::DesktopInfo(const char *file)
{
File = file;
Dirty = false;
if (File)
Serialize(false);
}
bool LApp::DesktopInfo::Serialize(bool Write)
{
::LFile f;
if (Write)
{
::LFile::Path p(File);
p--;
if (!p.Exists())
return false;
}
else if (!LFileExists(File))
return false;
if (!f.Open(File, Write?O_WRITE:O_READ))
{
LgiTrace("%s:%i - Failed to open '%s'\n", _FL, File.Get());
return false;
}
if (Write)
{
f.SetSize(0);
for (unsigned i=0; i= 0)
{
int e = l.Find("]", ++s);
if (e >= 0)
{
Cur = &Data.New();
Cur->Name = l(s, e - s + 1);
}
}
else if ((s = l.Find("=")) >= 0)
{
if (!Cur)
Cur = &Data.New();
KeyPair &kp = Cur->Values.New();
kp.Key = l(0, s).Strip();
kp.Value = l(++s, -1).Strip();
// printf("Read '%s': '%s'='%s'\n", Cur->Name.Get(), kp.Key.Get(), kp.Value.Get());
}
}
}
return true;
}
LApp::DesktopInfo::Section *LApp::DesktopInfo::GetSection(const char *Name, bool Create)
{
for (unsigned i=0; iGet(Field, false, Dirty);
if (kp)
{
return kp->Value;
}
}
}
return ::LString();
}
bool LApp::DesktopInfo::Set(const char *Field, const char *Value, const char *Sect)
{
if (!Field)
return false;
Section *s = GetSection(Sect ? Sect : DefaultSection, true);
if (!s)
return false;
KeyPair *kp = s->Get(Field, true, Dirty);
if (!kp)
return false;
if (kp->Value != Value)
{
kp->Value = Value;
Dirty = true;
}
return true;
}
LApp::DesktopInfo *LApp::GetDesktopInfo()
{
auto sExe = LGetExeFile();
::LFile::Path Exe(sExe);
::LFile::Path Desktop(LSP_HOME);
::LString Leaf;
Leaf.Printf("%s.desktop", Exe.Last().Get());
Desktop += ".local/share/applications";
Desktop += Leaf;
const char *Ex = Exe;
const char *Fn = Desktop;
if (d->DesktopInfo.Reset(new DesktopInfo(Desktop)))
{
// Do a sanity check...
::LString s = d->DesktopInfo->Get("Name");
if (!s && Name())
d->DesktopInfo->Set("Name", Name());
s = d->DesktopInfo->Get("Exec");
if (!s || s != (const char*)sExe)
d->DesktopInfo->Set("Exec", sExe);
s = d->DesktopInfo->Get("Type");
if (!s) d->DesktopInfo->Set("Type", "Application");
s = d->DesktopInfo->Get("Categories");
if (!s) d->DesktopInfo->Set("Categories", "Application;");
s = d->DesktopInfo->Get("Terminal");
if (!s) d->DesktopInfo->Set("Terminal", "false");
d->DesktopInfo->Update();
}
return d->DesktopInfo;
}
bool LApp::SetApplicationIcon(const char *FileName)
{
DesktopInfo *di = GetDesktopInfo();
if (!di)
return false;
::LString IcoPath = di->Get("Icon");
if (IcoPath == FileName)
return true;
di->Set("Icon", FileName);
return di->Update();
}
#endif
////////////////////////////////////////////////////////////////
OsApplication *OsApplication::Inst = 0;
class OsApplicationPriv
{
public:
OsApplicationPriv()
{
}
};
OsApplication::OsApplication(int Args, const char **Arg)
{
- Inst = this;
- d = new OsApplicationPriv;
+ Inst = this;
+ d = new OsApplicationPriv;
}
OsApplication::~OsApplication()
{
DeleteObj(d);
Inst = 0;
}
////////////////////////////////////////////////////////////////
int LMessage::Msg()
{
return what;
}
LMessage::Param LMessage::A()
{
int64 a = 0;
if (FindInt64(PropA, &a) != B_OK)
{
int32 c = CountNames(B_ANY_TYPE);
printf("%s:%i - Failed to find PropA (%i names)\n", _FL, c);
for (int32 i=0; iPostEvent(what, A(), B());
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
LLocker::LLocker(BHandler *h, const char *File, int Line)
{
- hnd = h;
- file = File;
- line = Line;
+ hnd = h;
+ file = File;
+ line = Line;
}
LLocker::~LLocker()
{
- Unlock();
+ Unlock();
}
bool LLocker::Lock()
{
if (locked)
{
printf("%s:%i - Locker already locked.\n", file, line);
- LAssert(!"Locker already locked.");
- return false;
- }
- if (!hnd)
- {
- // printf("%s:%i - Locker hnd is NULL.\n", file, line);
- return false;
- }
+ LAssert(!"Locker already locked.");
+ return false;
+ }
+ if (!hnd)
+ {
+ // printf("%s:%i - Locker hnd is NULL.\n", file, line);
+ return false;
+ }
+
+ auto looper = hnd->Looper();
+ if (!looper)
+ {
+ // printf("%s:%i - Locker looper is NULL %i.\n", file, line, count);
+ return false;
+ }
+
+ thread_id threadId = looper->Thread();
+ if (threadId <= 0)
+ {
+ // printf("%s:%i - Looper has no thread?!?!\n", file, line);
+ noThread = true;
+ return locked = true;
+ }
- while (!locked)
- {
- status_t result = hnd->LockLooperWithTimeout(1000 * 1000);
- if (result == B_OK)
- {
- locked = true;
- break;
- }
- else if (result == B_TIMED_OUT)
- {
- // Warn about failure to lock...
- auto cur = GetCurrentThreadId();
- auto locking = hnd->Looper()->LockingThread();
-
- printf("%s:%i - Warning: can't lock. cur=%i locking=%i\n",
- _FL,
- cur,
- locking);
- }
- else if (result == B_BAD_VALUE)
- {
- break;
- }
- else
- {
- // Warn about error
- printf("%s:%i - Error from LockLooperWithTimeout = 0x%x\n", _FL, result);
- }
- }
+ while (!locked)
+ {
+ status_t result = hnd->LockLooperWithTimeout(1000 * 1000);
+ if (result == B_OK)
+ {
+ locked = true;
+ break;
+ }
+ else if (result == B_TIMED_OUT)
+ {
+ // Warn about failure to lock...
+ auto cur = GetCurrentThreadId();
+ auto locking = hnd->Looper()->LockingThread();
+
+ printf("%s:%i - Warning: can't lock. cur=%i locking=%i\n",
+ _FL,
+ cur,
+ locking);
+ }
+ else if (result == B_BAD_VALUE)
+ {
+ break;
+ }
+ else
+ {
+ // Warn about error
+ printf("%s:%i - Error from LockLooperWithTimeout = 0x%x\n", _FL, result);
+ }
+ }
- return locked;
+ return locked;
}
status_t LLocker::LockWithTimeout(int64 time)
{
- LAssert(!locked);
- LAssert(hnd != NULL);
-
- status_t result = hnd->LockLooperWithTimeout(time);
- if (result == B_OK)
- locked = true;
-
- return result;
+ LAssert(!locked);
+
+ if (!hnd)
+ {
+ // printf("%s:%i - Locker hnd is NULL.\n", file, line);
+ return false;
+ }
+
+ auto looper = hnd->Looper();
+ if (!looper)
+ {
+ // printf("%s:%i - Locker looper is NULL %i.\n", file, line, count);
+ return false;
+ }
+
+ thread_id threadId = looper->Thread();
+ if (threadId <= 0)
+ {
+ // printf("%s:%i - Looper has no thread?!?!\n", file, line);
+ noThread = true;
+ return locked = true;
+ }
+
+ status_t result = hnd->LockLooperWithTimeout(time);
+ if (result == B_OK)
+ locked = true;
+
+ return result;
}
void LLocker::Unlock()
{
- if (locked)
- {
- hnd->UnlockLooper();
- locked = false;
- }
+ if (noThread)
+ {
+ locked = false;
+ return;
+ }
+
+ if (locked)
+ {
+ hnd->UnlockLooper();
+ locked = false;
+ }
}
diff --git a/src/haiku/Widgets.cpp b/src/haiku/Widgets.cpp
--- a/src/haiku/Widgets.cpp
+++ b/src/haiku/Widgets.cpp
@@ -1,279 +1,304 @@
/*hdr
** FILE: GuiDlg.cpp
** AUTHOR: Matthew Allen
** DATE: 8/9/1998
** DESCRIPTION: Dialog components
**
** Copyright (C) 1998 Matthew Allen
** fret@memecode.com
*/
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Slider.h"
#include "lgi/common/Bitmap.h"
#include "lgi/common/TableLayout.h"
#include "lgi/common/DisplayString.h"
#include "lgi/common/Button.h"
///////////////////////////////////////////////////////////////////////////////////////////
#define GreyBackground()
struct LDialogPriv
{
- int ModalStatus;
- int BtnId;
- bool IsModal, IsModeless;
- bool Resizable;
- LDialog::OnClose ModalCb;
+ int ModalStatus = 0;
+ int BtnId = -1;
+ bool IsModal = false;
+ bool IsModeless = false;
+ bool Resizable = true;
thread_id CallingThread = NULL;
-
- LDialogPriv()
- {
- IsModal = false;
- IsModeless = false;
- Resizable = true;
- ModalStatus = 0;
- BtnId = -1;
- }
+
+ /// The callback for the modal dialog:
+ LDialog::OnClose ModalCb;
+
+ /// This is set when the parent window has a pointer
+ /// to this dialog. We mustn't delete or change parent
+ /// while they have that.
+ bool ParentModal = false;
};
///////////////////////////////////////////////////////////////////////////////////////////
LDialog::LDialog(LViewI *parent)
:
#ifdef __GTK_H__
// , LWindow(gtk_dialog_new())
LWindow(gtk_window_new(GTK_WINDOW_TOPLEVEL)),
#endif
ResObject(Res_Dialog)
{
d = new LDialogPriv();
Name("Dialog");
if (parent)
SetParent(parent);
}
LDialog::~LDialog()
{
+ LAssert(!d->ParentModal);
DeleteObj(d);
}
bool LDialog::IsModal()
{
return d->IsModal;
}
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();
else if (d->IsModeless)
EndModeless();
}
return 0;
}
void LDialog::Quit(bool DontDelete)
{
if (d->IsModal)
EndModal(0);
else
LView::Quit(DontDelete);
}
void LDialog::OnPosChange()
{
LWindow::OnPosChange();
if (Children.Length() == 1)
{
List::I it = Children.begin();
LTableLayout *t = dynamic_cast((LViewI*)it);
if (t)
{
LRect r = GetClient();
r.Inset(LTableLayout::CellSpacing, LTableLayout::CellSpacing);
t->SetPos(r);
// _Dump();
}
}
}
bool LDialog::LoadFromResource(int Resource, char *TagList)
{
LAutoString n;
LRect p;
LProfile Prof("LDialog::LoadFromResource");
bool Status = LResourceLoad::LoadFromResource(Resource, this, &p, &n, TagList);
if (Status)
{
Prof.Add("Name.");
Name(n);
SetPos(p);
}
return Status;
}
bool LDialog::OnRequestClose(bool OsClose)
{
if (d->IsModal)
{
EndModal(0);
return false;
}
return true;
}
void LDialog::DoModal(OnClose Cb, OsView OverrideParent)
{
d->ModalStatus = -1;
auto Parent = GetParent();
if (Parent)
MoveSameScreen(Parent);
d->IsModal = true;
d->IsModeless = false;
d->ModalCb = Cb;
d->CallingThread = find_thread(NULL);
if (WindowHandle() &&
Parent &&
Parent->WindowHandle())
{
+ #if 0
+
// Keep this dialog above the parent window...
WindowHandle()->SetFeel(B_MODAL_SUBSET_WINDOW_FEEL);
WindowHandle()->AddToSubset(Parent->WindowHandle());
+
+ #else
+
+ auto Wnd = dynamic_cast(Parent);
+ LAssert(Wnd);
+ if (Wnd)
+ {
+ d->ParentModal = true;
+ Wnd->SetModalDialog(this);
+ }
+
+ #endif
}
else LgiTrace("%s:%i - Can't set parent for modal.\n", _FL);
BLooper *looper = BLooper::LooperForThread(d->CallingThread);
if (!looper)
printf("%s:%i - no looper for domodal thread.\n",_FL);
if (Attach(0))
{
AttachChildren();
Visible(true);
}
else printf("%s:%i - attach failed..\n", _FL);
}
void LDialog::EndModal(int Code)
{
if (!d->IsModal)
{
LgiTrace("%s:%i - EndModal error: LDialog is not model.\n", _FL);
return;
}
+ if (d->ParentModal)
+ {
+ auto Wnd = dynamic_cast(GetParent());
+ LAssert(Wnd);
+ if (Wnd)
+ {
+ Wnd->SetModalDialog(NULL);
+ d->ParentModal = false;
+ }
+ }
+
d->IsModal = false;
if (!d->ModalCb)
{
// If no callback is supplied, the default option is to just delete the
// dialog, closing it.
delete this;
return;
}
BLooper *looper = BLooper::LooperForThread(d->CallingThread);
if (!looper)
{
LgiTrace("%s:%i - Failed to get looper for %p\n", _FL, d->CallingThread);
delete this;
return;
}
BMessage *m = new BMessage(M_HANDLE_IN_THREAD);
m->AddPointer
(
LMessage::PropCallback,
new LMessage::InThreadCb
(
[dlg=this,cb=d->ModalCb,code=Code]()
{
// printf("%s:%i - Calling LDialog callback.. in original thread\n", _FL);
cb(dlg, code);
// printf("%s:%i - Calling LDialog callback.. done\n", _FL);
}
)
);
looper->PostMessage(m);
}
int LDialog::DoModeless()
{
d->IsModal = false;
d->IsModeless = true;
Visible(true);
return 0;
}
void LDialog::EndModeless(int Code)
{
Quit(Code);
}
extern LButton *FindDefault(LView *w);
LMessage::Param LDialog::OnEvent(LMessage *Msg)
{
return LView::OnEvent(Msg);
}
///////////////////////////////////////////////////////////////////////////////////////////
LControl::LControl(OsView view) : LView(view)
{
Pos.ZOff(10, 10);
}
LControl::~LControl()
{
}
LMessage::Param LControl::OnEvent(LMessage *Msg)
{
return 0;
}
LPoint LControl::SizeOfStr(const char *Str)
{
int y = LSysFont->GetHeight();
LPoint Pt(0, 0);
if (Str)
{
const char *e = 0;
for (const char *s = Str; s && *s; s = e?e+1:0)
{
e = strchr(s, '\n');
auto Len = e ? e - s : strlen(s);
LDisplayString ds(LSysFont, s, Len);
Pt.x = MAX(Pt.x, ds.X());
Pt.y += y;
}
}
return Pt;
}
diff --git a/src/haiku/Window.cpp b/src/haiku/Window.cpp
--- a/src/haiku/Window.cpp
+++ b/src/haiku/Window.cpp
@@ -1,1044 +1,1068 @@
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/DragAndDrop.h"
#include "lgi/common/Token.h"
#include "lgi/common/Popup.h"
#include "lgi/common/Panel.h"
#include "lgi/common/Notifications.h"
#include "lgi/common/Menu.h"
#include "ViewPriv.h"
#include "MenuBar.h"
#define DEBUG_SETFOCUS 0
#define DEBUG_HANDLEVIEWKEY 0
#define DEBUG_WAIT_THREAD 1
#define DEBUG_SERIALIZE_STATE 0
#if DEBUG_WAIT_THREAD
#define WAIT_LOG(...) LgiTrace(__VA_ARGS__)
#else
#define WAIT_LOG(...)
#endif
LString ToString(BRect &r)
{
LString s;
s.Printf("%g,%g,%g,%g", r.left, r.top, r.right, r.bottom);
return s;
}
///////////////////////////////////////////////////////////////////////
class HookInfo
{
public:
LWindowHookType Flags;
LView *Target;
};
enum LAttachState
{
LUnattached,
LAttaching,
LAttached,
LDetaching,
};
class LWindowPrivate : public BWindow
{
public:
LWindow *Wnd;
bool SnapToEdge = false;
LArray Hooks;
+ LWindow *ModalDlg = NULL;
LWindowPrivate(LWindow *wnd) :
Wnd(wnd),
BWindow(BRect(100,100,400,400),
"...loading...",
B_DOCUMENT_WINDOW_LOOK,
B_NORMAL_WINDOW_FEEL,
0)
{
}
~LWindowPrivate()
{
+ LAssert(ModalDlg == NULL);
DeleteObj(Wnd->Menu);
if (IsMinimized())
Wnd->_PrevZoom = LZoomMin;
Wnd->d = NULL;
}
int GetHookIndex(LView *Target, bool Create = false)
{
for (int i=0; iTarget = Target;
n->Flags = LNoEvents;
return Hooks.Length() - 1;
}
}
return -1;
}
void FrameMoved(BPoint newPosition)
{
auto Pos = Frame();
if (Pos != Wnd->Pos)
{
Wnd->Pos = Pos;
Wnd->OnPosChange();
}
BWindow::FrameMoved(newPosition);
}
void FrameResized(float newWidth, float newHeight)
{
auto Pos = Frame();
if (Pos != Wnd->Pos)
{
Wnd->Pos.SetSize(newWidth, newHeight);
Wnd->OnPosChange();
}
BWindow::FrameResized(newWidth, newHeight);
}
bool QuitRequested()
{
printf("%s::QuitRequested() starting.. %s\n", Wnd->GetClass(), Wnd->Pos.GetStr());
auto r = Wnd->OnRequestClose(false);
// printf("%s::QuitRequested()=%i\n", Wnd->GetClass(), r);
return r;
}
void MessageReceived(BMessage *message)
{
if (message->what == M_LWINDOW_DELETE)
{
// printf("Processing M_LWINDOW_DELETE th=%u\n", GetCurrentThreadId());
Wnd->Handle()->RemoveSelf();
Quit();
// printf("Processed M_LWINDOW_DELETE\n");
}
else
{
BWindow::MessageReceived(message);
LView *view = NULL;
auto r = message->FindPointer(LMessage::PropView, &view);
if (r == B_OK)
view->OnEvent((LMessage*)message);
else
Wnd->OnEvent((LMessage*)message);
}
}
};
///////////////////////////////////////////////////////////////////////
LWindow::LWindow() : LView(0)
{
d = new LWindowPrivate(this);
_QuitOnClose = false;
Menu = NULL;
_Default = 0;
_Window = this;
ClearFlag(WndFlags, GWF_VISIBLE);
}
LWindow::~LWindow()
{
if (LAppInst->AppWnd == this)
LAppInst->AppWnd = NULL;
LAssert(!Menu);
WaitThread();
}
+void LWindow::SetModalDialog(LWindow *dlg)
+{
+ d->ModalDlg = dlg;
+}
+
int LWindow::WaitThread()
{
if (!d)
return -1;
thread_id id = d->Thread();
bool thisThread = id == GetCurrentThreadId();
WAIT_LOG("%s::~LWindow thread=%u lock=%u\n", Name(), GetCurrentThreadId(), d->LockingThread());
if (thisThread)
{
// We are in thread... can delete easily.
if (d->Lock())
{
// printf("%s::~LWindow Quiting\n", Name());
Handle()->RemoveSelf();
d->Quit();
// printf("%s::~LWindow Quit finished\n", Name());
}
d = NULL;
return 0; // If we're in thread... no need to wait.
}
// Post event to the window's thread to delete itself...
WAIT_LOG("%s::~LWindow posting M_LWINDOW_DELETE from th=%u\n", Name(), GetCurrentThreadId());
d->PostMessage(new BMessage(M_LWINDOW_DELETE));
status_t value = 0;
WAIT_LOG("wait_for_thread(%u) start..\n", id);
wait_for_thread(id, &value);
WAIT_LOG("wait_for_thread(%u) end=%i\n", id, value);
d = NULL;
return value;
}
OsWindow LWindow::WindowHandle()
{
return d;
}
bool LWindow::SetIcon(const char *FileName)
{
LString a;
if (!LFileExists(FileName))
{
if (a = LFindFile(FileName))
FileName = a;
}
return false;
}
bool LWindow::GetSnapToEdge()
{
return d->SnapToEdge;
}
void LWindow::SetSnapToEdge(bool s)
{
d->SnapToEdge = s;
}
bool LWindow::IsActive()
{
LLocker lck(d, _FL);
if (!lck.Lock())
return false;
return d->IsActive();
}
bool LWindow::SetActive()
{
LLocker lck(d, _FL);
if (!lck.Lock())
return false;
d->Activate();
return true;
}
bool LWindow::Visible()
{
LLocker lck(d, _FL);
if (!lck.Lock())
return false;
return !d->IsHidden();
}
void LWindow::Visible(bool i)
{
LLocker lck(d, _FL);
if (!lck.Lock())
{
printf("%s:%i - Can't lock.\n", _FL);
return;
}
if (i)
{
if (d->IsHidden())
{
printf("%s show %s\n", GetClass(), GetPos().GetStr());
d->MoveTo(Pos.x1, Pos.y1);
d->ResizeTo(Pos.X(), Pos.Y());
d->Show();
}
else
printf("%s already shown\n", GetClass());
}
else
{
if (!d->IsHidden())
d->Hide();
}
}
bool LWindow::Obscured()
{
return false;
}
bool DndPointMap(LViewI *&v, LPoint &p, LDragDropTarget *&t, LWindow *Wnd, int x, int y)
{
LRect cli = Wnd->GetClient();
t = NULL;
v = Wnd->WindowFromPoint(x - cli.x1, y - cli.y1, false);
if (!v)
{
LgiTrace("%s:%i - @ %i,%i\n", _FL, x, y);
return false;
}
v->WindowVirtualOffset(&p);
p.x = x - p.x;
p.y = y - p.y;
for (LViewI *view = v; !t && view; view = view->GetParent())
t = view->DropTarget();
if (t)
return true;
LgiTrace("%s:%i - No target for %s\n", _FL, v->GetClass());
return false;
}
bool LWindow::Attach(LViewI *p)
{
LLocker lck(d, _FL);
if (!lck.Lock())
return false;
auto rootView = Handle();
auto wnd = WindowHandle();
// printf("%s:%i attach %p to %p\n", _FL, Handle(), WindowHandle());
if (rootView && wnd)
{
wnd->AddChild(rootView);
if (rootView->IsHidden())
rootView->Show();
auto menu = wnd->KeyMenuBar();
BRect menuPos = menu ? menu->Frame() : BRect(0, 0, 0, 0);
auto f = wnd->Frame();
rootView->ResizeTo(f.Width(), f.Height() - menuPos.Height());
if (menu)
rootView->MoveTo(0, menuPos.Height());
rootView->SetResizingMode(B_FOLLOW_ALL_SIDES);
}
// Setup default button...
if (!_Default)
_Default = FindControl(IDOK);
// Do a rough layout of child windows
PourAll();
return true;
}
bool LWindow::OnRequestClose(bool OsShuttingDown)
{
if (GetQuitOnClose())
LCloseApp();
return LView::OnRequestClose(OsShuttingDown);
}
bool LWindow::HandleViewMouse(LView *v, LMouse &m)
{
+ if (d->ModalDlg)
+ {
+ printf("%s:%i - %s ignoring mouse event while %s is shown.\n",
+ _FL,
+ GetClass(),
+ d->ModalDlg->GetClass());
+ return false;
+ }
+
if (m.Down() && !m.IsMove())
{
bool InPopup = false;
for (LViewI *p = v; p; p = p->GetParent())
{
if (dynamic_cast(p))
{
InPopup = true;
break;
}
}
if (!InPopup && LPopup::CurrentPopups.Length())
{
for (int i=0; iVisible())
p->Visible(false);
}
}
}
for (int i=0; iHooks.Length(); i++)
{
if (d->Hooks[i].Flags & LMouseEvents)
{
if (!d->Hooks[i].Target->OnViewMouse(v, m))
{
return false;
}
}
}
return true;
}
bool LWindow::HandleViewKey(LView *v, LKey &k)
{
bool Status = false;
LViewI *Ctrl = 0;
#if DEBUG_HANDLEVIEWKEY
bool Debug = 1; // k.vkey == LK_RETURN;
char SafePrint = k.c16 < ' ' ? ' ' : k.c16;
// if (Debug)
{
LgiTrace("%s/%p::HandleViewKey=%i ischar=%i %s%s%s%s\n",
v->GetClass(), v,
k.c16,
k.IsChar,
(char*)(k.Down()?" Down":" Up"),
(char*)(k.Shift()?" Shift":""),
(char*)(k.Alt()?" Alt":""),
(char*)(k.Ctrl()?" Ctrl":""));
}
#endif
+ if (d->ModalDlg)
+ {
+ printf("%s:%i - %s ignoring key event while %s is shown.\n",
+ _FL,
+ GetClass(),
+ d->ModalDlg->GetClass());
+ return false;
+ }
+
// Any window in a popup always gets the key...
LViewI *p;
for (p = v->GetParent(); p; p = p->GetParent())
{
if (dynamic_cast(p))
{
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tSending key to popup\n");
#endif
return v->OnKey(k);
}
}
// Give key to popups
if (LAppInst &&
LAppInst->GetMouseHook() &&
LAppInst->GetMouseHook()->OnViewKey(v, k))
{
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tMouseHook got key\n");
#endif
goto AllDone;
}
// Allow any hooks to see the key...
for (int i=0; iHooks.Length(); i++)
{
#if DEBUG_HANDLEVIEWKEY
// if (Debug) LgiTrace("\tHook[%i]\n", i);
#endif
if (d->Hooks[i].Flags & LKeyEvents)
{
LView *Target = d->Hooks[i].Target;
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tHook[%i].Target=%p %s\n", i, Target, Target->GetClass());
#endif
if (Target->OnViewKey(v, k))
{
Status = true;
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tHook[%i] ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", i, SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
goto AllDone;
}
}
}
// Give the key to the window...
if (v->OnKey(k))
{
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tView ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n",
SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
Status = true;
goto AllDone;
}
#if DEBUG_HANDLEVIEWKEY
else if (Debug)
LgiTrace("\t%s didn't eat '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n",
v->GetClass(), SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
// Window didn't want the key...
switch (k.vkey)
{
case LK_RETURN:
#ifdef LK_KEYPADENTER
case LK_KEYPADENTER:
#endif
{
Ctrl = _Default;
break;
}
case LK_ESCAPE:
{
Ctrl = FindControl(IDCANCEL);
break;
}
}
// printf("Ctrl=%p\n", Ctrl);
if (Ctrl)
{
if (Ctrl->Enabled())
{
if (Ctrl->OnKey(k))
{
Status = true;
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tDefault Button ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n",
SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
goto AllDone;
}
// else printf("OnKey()=false\n");
}
// else printf("Ctrl=disabled\n");
}
#if DEBUG_HANDLEVIEWKEY
else if (Debug)
LgiTrace("\tNo default ctrl to handle key.\n");
#endif
if (Menu)
{
Status = Menu->OnKey(v, k);
if (Status)
{
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tMenu ate '%c' down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift());
#endif
}
}
// Tab through controls
if (k.vkey == LK_TAB && k.Down() && !k.IsChar)
{
LViewI *Wnd = GetNextTabStop(v, k.Shift());
#if DEBUG_HANDLEVIEWKEY
if (Debug)
LgiTrace("\tTab moving focus shift=%i Wnd=%p\n", k.Shift(), Wnd);
#endif
if (Wnd)
Wnd->Focus(true);
}
// Control shortcut?
if (k.Down() && k.Alt() && k.c16 > ' ')
{
ShortcutMap Map;
BuildShortcuts(Map);
LViewI *c = Map.Find(ToUpper(k.c16));
if (c)
{
c->OnNotify(c, LNotifyActivate);
return true;
}
}
AllDone:
return Status;
}
void LWindow::Raise()
{
}
LWindowZoom LWindow::GetZoom()
{
if (!d)
return _PrevZoom;
LLocker lck(d, _FL);
if (!IsAttached() || lck.Lock())
{
if (d->IsMinimized())
return LZoomMin;
}
return LZoomNormal;
}
void LWindow::SetZoom(LWindowZoom i)
{
LLocker lck(d, _FL);
if (lck.Lock())
{
d->Minimize(i == LZoomMin);
}
}
LViewI *LWindow::GetDefault()
{
return _Default;
}
void LWindow::SetDefault(LViewI *v)
{
if (v &&
v->GetWindow() == this)
{
if (_Default != v)
{
LViewI *Old = _Default;
_Default = v;
if (Old) Old->Invalidate();
if (_Default) _Default->Invalidate();
}
}
else
{
_Default = 0;
}
}
bool LWindow::Name(const char *n)
{
LLocker lck(d, _FL);
if (lck.Lock())
d->SetTitle(n);
return LBase::Name(n);
}
const char *LWindow::Name()
{
return LBase::Name();
}
LPoint LWindow::GetDpi()
{
return LPoint(96, 96);
}
LPointF LWindow::GetDpiScale()
{
auto Dpi = GetDpi();
return LPointF((double)Dpi.x/96.0, (double)Dpi.y/96.0);
}
LRect &LWindow::GetClient(bool ClientSpace)
{
static LRect r;
r = Pos.ZeroTranslate();
LLocker lck(WindowHandle(), _FL);
if (lck.Lock())
{
auto br = Handle()->Bounds();
r = br;
auto frm = d->Frame();
frm.OffsetBy(-frm.left, -frm.top);
// printf("Frame=%s Bounds=%s r=%s\n", ToString(frm).Get(), ToString(br).Get(), r.GetStr());
lck.Unlock();
}
return r;
}
#if DEBUG_SERIALIZE_STATE
#define SERIALIZE_LOG(...) printf(__VA_ARGS__)
#else
#define SERIALIZE_LOG(...)
#endif
bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load)
{
if (!Store || !FieldName)
return false;
if (Load)
{
::LVariant v;
if (Store->GetValue(FieldName, v) && v.Str())
{
LRect Position(0, 0, -1, -1);
LWindowZoom State = LZoomNormal;
auto vars = LString(v.Str()).SplitDelimit(";");
SERIALIZE_LOG("SerializeState: %s=%s, vars=%i\n", FieldName, v.Str(), (int)vars.Length());
for (auto var: vars)
{
auto parts = var.SplitDelimit("=", 1);
SERIALIZE_LOG("SerializeState: parts=%i\n", (int)parts.Length());
if (parts.Length() == 2)
{
if (parts[0].Equals("State"))
{
State = (LWindowZoom) parts[1].Int();
SERIALIZE_LOG("SerializeState: part=%s state=%i\n", parts[0].Get(), State);
}
else if (parts[0].Equals("Pos"))
{
Position.SetStr(parts[1]);
SERIALIZE_LOG("SerializeState: part=%s pos=%s\n", parts[0].Get(), Position.GetStr());
}
}
}
if (Position.Valid())
{
SERIALIZE_LOG("SerializeState setpos %s\n", Position.GetStr());
SetPos(Position);
}
SetZoom(State);
}
else
{
SERIALIZE_LOG("SerializeState: no '%s' var\n", FieldName);
return false;
}
}
else
{
char s[256];
LWindowZoom State = GetZoom();
sprintf_s(s, sizeof(s), "State=%i;Pos=%s", State, GetPos().GetStr());
LVariant v = s;
SERIALIZE_LOG("SerializeState: saving '%s' = '%s'\n", FieldName, s);
if (!Store->SetValue(FieldName, v))
{
SERIALIZE_LOG("SerializeState: SetValue failed\n");
return false;
}
}
return true;
}
LRect &LWindow::GetPos()
{
return Pos;
}
bool LWindow::SetPos(LRect &p, bool Repaint)
{
- printf("SetPos %s -> %s\n", Pos.GetStr(), p.GetStr());
Pos = p;
LLocker lck(d, _FL);
if (lck.Lock())
{
d->MoveTo(Pos.x1, Pos.y1);
d->ResizeTo(Pos.X(), Pos.Y());
}
else printf("%s:%i - Failed to lock.\n", _FL);
return true;
}
void LWindow::OnChildrenChanged(LViewI *Wnd, bool Attaching)
{
// Force repour
}
void LWindow::OnCreate()
{
AttachChildren();
}
void LWindow::OnPaint(LSurface *pDC)
{
pDC->Colour(L_MED);
pDC->Rectangle();
}
void LWindow::OnPosChange()
{
LLocker lck(WindowHandle(), _FL);
if (lck.Lock())
{
auto frame = WindowHandle()->Bounds();
auto menu = WindowHandle()->KeyMenuBar();
auto menuPos = menu ? menu->Frame() : BRect(0, 0, 0, 0);
auto rootPos = Handle()->Frame();
if (menu)
{
if (menu->IsHidden()) // Why?
{
menu->Show();
if (menu->IsHidden())
{
// printf("Can't show menu?\n");
for (auto p = menu->Parent(); p; p = p->Parent())
printf(" par=%s %i\n", p->Name(), p->IsHidden());
}
}
if (menuPos.Width() < 1) // Again... WHHHHY? FFS
{
float x = 0.0f, y = 0.0f;
menu->GetPreferredSize(&x, &y);
// printf("Pref=%g,%g\n", x, y);
if (y > 0.0f)
menu->ResizeTo(frame.Width(), y);
}
}
#if 0
printf("frame=%s menu=%p,%i,%s rootpos=%s\n",
ToString(frame).Get(), menu, menu?menu->IsHidden():0, ToString(menuPos).Get(), ToString(rootPos).Get());
#endif
int rootTop = menu ? menuPos.bottom + 1 : 0;
if (rootPos.top != rootTop)
{
Handle()->MoveTo(0, rootTop);
Handle()->ResizeTo(rootPos.Width(), frame.Height() - menuPos.Height());
}
lck.Unlock();
}
LView::OnPosChange();
PourAll();
}
#define IsTool(v) \
( \
dynamic_cast(v) \
&& \
dynamic_cast(v)->_IsToolBar \
)
void LWindow::PourAll()
{
LRect c = GetClient();
LRegion Client(c);
LViewI *MenuView = 0;
LRegion Update(Client);
bool HasTools = false;
LViewI *v;
List::I Lst = Children.begin();
{
LRegion Tools;
for (v = *Lst; v; v = *++Lst)
{
bool IsMenu = MenuView == v;
if (!IsMenu && IsTool(v))
{
LRect OldPos = v->GetPos();
if (OldPos.Valid())
Update.Union(&OldPos);
if (HasTools)
{
// 2nd and later toolbars
if (v->Pour(Tools))
{
if (!v->Visible())
{
v->Visible(true);
}
auto vpos = v->GetPos();
if (OldPos != vpos)
{
// position has changed update...
v->Invalidate();
}
// Has it increased the size of the toolbar area?
auto b = Tools.Bound();
if (vpos.y2 >= b.y2)
{
LRect Bar = Client;
Bar.y2 = vpos.y2;
Client.Subtract(&Bar);
// LgiTrace("IncreaseToolbar=%s\n", Bar.GetStr());
}
Tools.Subtract(&vpos);
Update.Subtract(&vpos);
// LgiTrace("vpos=%s\n", vpos.GetStr());
}
}
else
{
// First toolbar
if (v->Pour(Client))
{
HasTools = true;
if (!v->Visible())
{
v->Visible(true);
}
if (OldPos != v->GetPos())
{
v->Invalidate();
}
LRect Bar(v->GetPos());
// LgiTrace("%s = %s\n", v->GetClass(), Bar.GetStr());
Bar.x2 = GetClient().x2;
Tools = Bar;
Tools.Subtract(&v->GetPos());
Client.Subtract(&Bar);
Update.Subtract(&Bar);
}
}
}
}
}
Lst = Children.begin();
for (LViewI *v = *Lst; v; v = *++Lst)
{
bool IsMenu = MenuView == v;
if (!IsMenu && !IsTool(v))
{
LRect OldPos = v->GetPos();
if (OldPos.Valid())
Update.Union(&OldPos);
if (v->Pour(Client))
{
// LRect p = v->GetPos();
// LgiTrace("%s = %s\n", v->GetClass(), p.GetStr());
if (!v->Visible())
v->Visible(true);
v->Invalidate();
Client.Subtract(&v->GetPos());
Update.Subtract(&v->GetPos());
}
else
{
// non-pourable
}
}
}
for (int i=0; iMsg())
{
case M_CLOSE:
{
if (OnRequestClose(false))
{
Quit();
return 0;
}
break;
}
}
return LView::OnEvent(m);
}
bool LWindow::RegisterHook(LView *Target, LWindowHookType EventType, int Priority)
{
bool Status = false;
if (Target && EventType)
{
int i = d->GetHookIndex(Target, true);
if (i >= 0)
{
d->Hooks[i].Flags = EventType;
Status = true;
}
}
return Status;
}
bool LWindow::UnregisterHook(LView *Target)
{
int i = d->GetHookIndex(Target);
if (i >= 0)
{
d->Hooks.DeleteAt(i);
return true;
}
return false;
}
void LWindow::OnFrontSwitch(bool b)
{
}
LViewI *LWindow::GetFocus()
{
return NULL;
}
void LWindow::SetFocus(LViewI *ctrl, FocusType type)
{
}
void LWindow::SetDragHandlers(bool On)
{
}
void LWindow::OnTrayClick(LMouse &m)
{
if (m.Down() || m.IsContextMenu())
{
LSubMenu RClick;
OnTrayMenu(RClick);
if (GetMouse(m, true))
{
int Result = RClick.Float(this, m.x, m.y);
OnTrayMenuResult(Result);
}
}
}
void LWindow::SetAlwaysOnTop(bool b)
{
}
diff --git a/src/win/Lgi/Window.cpp b/src/win/Lgi/Window.cpp
--- a/src/win/Lgi/Window.cpp
+++ b/src/win/Lgi/Window.cpp
@@ -1,1391 +1,1392 @@
#include
#include
#include "lgi/common/Lgi.h"
#include "lgi/common/Edit.h"
#include "lgi/common/Popup.h"
#include "lgi/common/ToolBar.h"
#include "lgi/common/Panel.h"
#include "lgi/common/Variant.h"
#include "lgi/common/Token.h"
#include "lgi/common/Button.h"
#include "lgi/common/Notifications.h"
#include "lgi/common/CssTools.h"
#include "lgi/common/Menu.h"
#define DEBUG_WINDOW_PLACEMENT 0
#define DEBUG_HANDLE_VIEW_KEY 0
#define DEBUG_HANDLE_VIEW_MOUSE 0
#define DEBUG_SERIALIZE_STATE 0
#define DEBUG_SETFOCUS 0
extern bool In_SetWindowPos;
typedef UINT (WINAPI *ProcGetDpiForWindow)(_In_ HWND hwnd);
typedef UINT (WINAPI *ProcGetDpiForSystem)(VOID);
LLibrary User32("User32");
LPoint LGetDpiForWindow(HWND hwnd)
{
static bool init = false;
static ProcGetDpiForWindow pGetDpiForWindow = NULL;
static ProcGetDpiForSystem pGetDpiForSystem = NULL;
if (!init)
{
init = true;
pGetDpiForWindow = (ProcGetDpiForWindow) User32.GetAddress("GetDpiForWindow");
pGetDpiForSystem = (ProcGetDpiForSystem) User32.GetAddress("GetDpiForSystem");
}
if (pGetDpiForWindow && pGetDpiForSystem)
{
auto Dpi = hwnd ? pGetDpiForWindow(hwnd) : pGetDpiForSystem();
return LPoint(Dpi, Dpi);
}
return LScreenDpi();
}
///////////////////////////////////////////////////////////////////////////////////////////////
class HookInfo
{
public:
int Flags;
LView *Target;
};
class LWindowPrivate
{
public:
LArray Hooks;
bool SnapToEdge;
bool AlwaysOnTop;
LWindowZoom Show;
bool InCreate;
LAutoPtr Wp;
LPoint Dpi;
// Focus stuff
LViewI *Focus;
LWindowPrivate()
{
Focus = NULL;
InCreate = true;
Show = LZoomNormal;
SnapToEdge = false;
AlwaysOnTop = false;
}
~LWindowPrivate()
{
}
ssize_t GetHookIndex(LView *Target, bool Create = false)
{
for (int i=0; iTarget = Target;
n->Flags = 0;
return (ssize_t)Hooks.Length() - 1;
}
}
return -1;
}
};
///////////////////////////////////////////////////////////////////////////////////////////////
LWindow::LWindow() : LView(0)
{
_Window = this;
d = new LWindowPrivate;
SetStyle(GetStyle() | WS_TILEDWINDOW | WS_CLIPCHILDREN);
SetStyle(GetStyle() & ~WS_CHILD);
SetExStyle(GetExStyle() | WS_EX_CONTROLPARENT);
LWindowsClass *c = LWindowsClass::Create(GetClass());
if (c)
c->Register();
Visible(false);
_Default = 0;
_Lock = new LMutex("LWindow");
_QuitOnClose = false;
}
LWindow::~LWindow()
{
if (LAppInst && LAppInst->AppWnd == this)
LAppInst->AppWnd = NULL;
if (Menu)
{
Menu->Detach();
DeleteObj(Menu);
}
DeleteObj(_Lock);
DeleteObj(d);
}
int LWindow::WaitThread()
{
// No thread to wait on...
return 0;
}
bool LWindow::SetIcon(const char *Icon)
{
return CreateClassW32(LAppInst->Name(), LoadIcon(LProcessInst(), (LPCWSTR)Icon)) != 0;
}
LViewI *LWindow::GetFocus()
{
return d->Focus;
}
static LAutoString DescribeView(LViewI *v)
{
if (!v)
return LAutoString(NewStr("NULL"));
char s[512];
int ch = 0;
::LArray p;
for (LViewI *i = v; i; i = i->GetParent())
{
p.Add(i);
}
for (auto n=MIN(3, (ssize_t)p.Length()-1); n>=0; n--)
{
v = p[n];
ch += sprintf_s(s + ch, sizeof(s) - ch, ">%s", v->GetClass());
}
return LAutoString(NewStr(s));
}
static bool HasParentPopup(LViewI *v)
{
for (; v; v = v->GetParent())
{
if (dynamic_cast(v))
return true;
}
return false;
}
void LWindow::SetFocus(LViewI *ctrl, FocusType type)
{
const char *TypeName = NULL;
switch (type)
{
case GainFocus: TypeName = "Gain"; break;
case LoseFocus: TypeName = "Lose"; break;
case ViewDelete: TypeName = "Delete"; break;
}
switch (type)
{
case GainFocus:
{
LViewI *This = this;
if (ctrl == This && d->Focus)
{
// The main LWindow is getting focus.
// Check if we can re-focus the previous child focus...
LView *v = d->Focus->GetGView();
if (v && !HasParentPopup(v))
{
// We should never return focus to a popup, or it's child.
if (!(v->WndFlags & GWF_FOCUS))
{
// Yes, the child view doesn't think it has focus...
// So re-focus it...
if (v->Handle())
{
// Non-virtual window...
::SetFocus(v->Handle());
}
v->WndFlags |= GWF_FOCUS;
v->OnFocus(true);
v->Invalidate();
#if DEBUG_SETFOCUS
LAutoString _set = DescribeView(ctrl);
LAutoString _foc = DescribeView(d->Focus);
LgiTrace("LWindow::SetFocus(%s, %s) refocusing: %s\n",
_set.Get(),
TypeName,
_foc.Get());
#endif
return;
}
}
}
// Check if the control already has focus
if (d->Focus == ctrl)
return;
if (d->Focus)
{
LView *v = d->Focus->GetGView();
if (v) v->WndFlags &= ~GWF_FOCUS;
d->Focus->OnFocus(false);
d->Focus->Invalidate();
#if DEBUG_SETFOCUS
LAutoString _foc = DescribeView(d->Focus);
LgiTrace(".....defocus: %s\n",
_foc.Get());
#endif
}
d->Focus = ctrl;
if (d->Focus)
{
LView *v = d->Focus->GetGView();
if (v) v->WndFlags |= GWF_FOCUS;
d->Focus->OnFocus(true);
d->Focus->Invalidate();
#if DEBUG_SETFOCUS
LAutoString _set = DescribeView(d->Focus);
LgiTrace("LWindow::SetFocus(%s, %s) focusing\n",
_set.Get(),
TypeName);
#endif
}
break;
}
case LoseFocus:
{
if (ctrl == d->Focus)
{
LView *v = d->Focus->GetGView();
if (v)
{
if (v->WndFlags & GWF_FOCUS)
{
// View thinks it has focus
v->WndFlags &= ~GWF_FOCUS;
d->Focus->OnFocus(false);
// keep d->Focus pointer, as we want to be able to re-focus the child
// view when we get focus again
#if DEBUG_SETFOCUS
LAutoString _ctrl = DescribeView(ctrl);
LAutoString _foc = DescribeView(d->Focus);
LgiTrace("LWindow::SetFocus(%s, %s) keep_focus: %s\n",
_ctrl.Get(),
TypeName,
_foc.Get());
#endif
}
// else view doesn't think it has focus anyway...
}
else
{
// Non LView handler
d->Focus->OnFocus(false);
d->Focus->Invalidate();
d->Focus = NULL;
}
}
else
{
/*
LgiTrace("LWindow::SetFocus(%p.%s, %s) error on losefocus: %p(%s)\n",
ctrl,
ctrl ? ctrl->GetClass() : NULL,
TypeName,
d->Focus,
d->Focus ? d->Focus->GetClass() : NULL);
*/
}
break;
}
case ViewDelete:
{
if (ctrl == d->Focus)
{
#if DEBUG_SETFOCUS
LgiTrace("LWindow::SetFocus(%p.%s, %s) delete_focus: %p(%s)\n",
ctrl,
ctrl ? ctrl->GetClass() : NULL,
TypeName,
d->Focus,
d->Focus ? d->Focus->GetClass() : NULL);
#endif
d->Focus = NULL;
}
break;
}
}
}
bool LWindow::GetSnapToEdge()
{
return d->SnapToEdge;
}
void LWindow::SetSnapToEdge(bool b)
{
d->SnapToEdge = b;
}
bool LWindow::GetAlwaysOnTop()
{
return d->AlwaysOnTop;
}
void LWindow::SetAlwaysOnTop(bool b)
{
d->AlwaysOnTop = b;
if (_View)
SetWindowPos(_View, b ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
}
void LWindow::Raise()
{
if (_View)
{
DWORD dwFGProcessId;
DWORD dwFGThreadId = GetWindowThreadProcessId(_View, &dwFGProcessId);
DWORD dwThisThreadId = GetCurrentThreadId();
AttachThreadInput(dwThisThreadId, dwFGThreadId, true);
SetForegroundWindow(_View);
BringWindowToTop(_View);
if (In_SetWindowPos)
{
assert(0);
LgiTrace("%s:%i - SetFocus(%p)\n", __FILE__, __LINE__, _View);
}
::SetFocus(_View);
AttachThreadInput(dwThisThreadId, dwFGThreadId, false);
}
}
LWindowZoom LWindow::GetZoom()
{
if (IsZoomed(Handle()))
{
return LZoomMax;
}
else if (IsIconic(Handle()))
{
return LZoomMin;
}
return LZoomNormal;
}
void LWindow::SetZoom(LWindowZoom i)
{
if (_View && IsWindowVisible(_View))
{
switch (i)
{
case LZoomMax:
{
ShowWindow(Handle(), SW_MAXIMIZE);
break;
}
case LZoomMin:
{
ShowWindow(Handle(), SW_MINIMIZE);
break;
}
case LZoomNormal:
{
if (!Visible())
{
Visible(true);
}
if (IsIconic(Handle()) || IsZoomed(Handle()))
{
ShowWindow(Handle(), SW_NORMAL);
}
LYield();
RECT r;
GetWindowRect(Handle(), &r);
if (r.left != Pos.x1 ||
r.top != Pos.y1)
{
SetWindowPos(Handle(), 0, Pos.x1, Pos.y1, Pos.X(), Pos.Y(), SWP_NOZORDER);
}
break;
}
}
}
d->Show = i;
}
bool LWindow::OnRequestClose(bool OsShuttingDown)
{
if (GetQuitOnClose())
{
LCloseApp();
}
return true;
}
bool LWindow::HandleViewMouse(LView *v, LMouse &m)
{
#if DEBUG_HANDLE_VIEW_MOUSE
m.Trace("HandleViewMouse");
#endif
-
+
for (int i=0; iHooks.Length(); i++)
{
if (d->Hooks[i].Flags & LMouseEvents)
{
LView *t = d->Hooks[i].Target;
if (!t->OnViewMouse(v, m))
{
#if DEBUG_HANDLE_VIEW_MOUSE
if (m.IsMove())
LgiTrace(" Hook %i of %i ate mouse event: '%s'\n", i, d->Hooks.Length(), d->Hooks[i].Target->GetClass());
#endif
return false;
}
}
}
#if DEBUG_HANDLE_VIEW_MOUSE
if (!m.IsMove())
LgiTrace(" Passing mouse event to '%s'\n", v->GetClass());
#endif
+
return true;
}
bool LWindow::HandleViewKey(LView *v, LKey &k)
{
#if DEBUG_HANDLE_VIEW_KEY
char msg[256];
sprintf_s(msg, sizeof(msg), "HandleViewKey, v=%s", v ? v->GetClass() : "NULL");
k.Trace(msg);
#endif
// Any window in a pop up always gets the key...
LViewI *p;
for (p = v->GetParent(); p; p = p->GetParent())
{
if (dynamic_cast(p))
{
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" Popup %s handling key.\n", p->GetClass());
#endif
return v->OnKey(k);
}
}
// Allow any hooks to see the key...
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" d->Hooks.Length()=%i.\n", (int)d->Hooks.Length());
#endif
for (int i=0; iHooks.Length(); i++)
{
if (d->Hooks[i].Flags & LKeyEvents)
{
if (d->Hooks[i].Target->OnViewKey(v, k))
{
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" Hook[%i] %s handling key.\n", i, d->Hooks[i].Target->GetClass());
#endif
return true;
}
}
}
// Give the key to the focused window...
if (d->Focus && d->Focus->OnKey(k))
{
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" d->Focus %s handling key.\n", d->Focus->GetClass());
#endif
return true;
}
// Check default controls
p = 0;
if (k.c16 == VK_RETURN)
{
if (!_Default)
p = _Default = FindControl(IDOK);
else
p = _Default;
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" Using _Default ctrl (%s).\n", p ? p->GetClass() : "NULL");
#endif
}
else if (k.c16 == VK_ESCAPE)
{
p = FindControl(IDCANCEL);
if (p)
{
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" Using IDCANCEL ctrl (%s).\n", p->GetClass());
#endif
}
}
if (p && p->OnKey(k))
{
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" Default control %s handled key.\n", p->GetClass());
#endif
return true;
}
// Menu shortcut?
if (Menu && Menu->OnKey(v, k))
{
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" Menu handled key.\n");
#endif
return true;
}
// Control shortcut?
if (k.Down() && k.Alt() && k.c16 > ' ')
{
ShortcutMap Map;
BuildShortcuts(Map);
LViewI *c = Map.Find(ToUpper(k.c16));
if (c)
{
c->OnNotify(c, LNotifyActivate);
return true;
}
}
#if DEBUG_HANDLE_VIEW_KEY
LgiTrace(" No one handled key.\n");
#endif
return false;
}
void LWindow::OnPaint(LSurface *pDC)
{
auto c = GetClient();
LCssTools Tools(this);
Tools.PaintContent(pDC, c);
}
bool LWindow::Obscured()
{
RECT tRect;
bool isObscured = false;
if (GetWindowRect(_View, &tRect))
{
RECT nRect;
HWND walker = _View;
while (walker = ::GetNextWindow(walker, GW_HWNDPREV))
{
if (IsWindowVisible(walker))
{
if ((::GetWindowRect(walker, &nRect)))
{
RECT iRect;
IntersectRect(&iRect, &tRect, &nRect);
if (iRect.bottom || iRect.top || iRect.left || iRect.right)
{
isObscured = true;
break;
}
}
}
}
}
return isObscured;
}
bool LWindow::Visible()
{
return LView::Visible();
}
void LWindow::Visible(bool v)
{
if (v)
PourAll();
if (v)
{
SetStyle(GetStyle() | WS_VISIBLE);
if (_View)
{
LWindowZoom z = d->Show;
char *Cmd = 0;
LAutoPtr Wp(new WINDOWPLACEMENT);
if (Wp)
{
ZeroObj(*Wp.Get());
Wp->length = sizeof(*Wp);
Wp->flags = 2;
Wp->ptMaxPosition.x = -1;
Wp->ptMaxPosition.y = -1;
if (d->Show == LZoomMax)
{
Wp->showCmd = SW_MAXIMIZE;
Cmd = "SW_MAXIMIZE";
}
else if (d->Show == LZoomMin)
{
Wp->showCmd = SW_MINIMIZE;
Cmd = "SW_MINIMIZE";
}
else
{
Wp->showCmd = SW_NORMAL;
Cmd = "SW_NORMAL";
}
Wp->rcNormalPosition = Pos;
#if DEBUG_WINDOW_PLACEMENT
LgiTrace("%s:%i - SetWindowPlacement, pos=%s, show=%i\n", __FILE__, __LINE__, Pos.GetStr(), Wp->showCmd);
#endif
SetWindowPlacement(_View, Wp);
if (d->InCreate)
d->Wp = Wp;
}
}
}
else
{
#if DEBUG_WINDOW_PLACEMENT
LgiTrace("%s:%i - Visible(%i)\n", __FILE__, __LINE__, v);
#endif
LView::Visible(v);
}
if (v)
{
OnZoom(d->Show);
}
}
static bool IsAppWnd(HWND h)
{
if (!IsWindowVisible(h))
return false;
auto flags = GetWindowLong(h, GWL_STYLE);
if (flags & WS_POPUP)
return false;
return true;
}
bool LWindow::IsActive()
{
auto top = GetTopWindow(GetDesktopWindow());
while (top && !IsAppWnd(top))
top = ::GetWindow(top, GW_HWNDNEXT);
return top == _View;
}
bool LWindow::SetActive()
{
if (!_View)
return false;
return SetWindowPos(_View, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE) != 0;
}
void LWindow::PourAll()
{
LRegion Client(GetClient());
LRegion Update;
bool HasTools = false;
{
LRegion Tools;
for (auto v: Children)
{
LView *k = dynamic_cast(v);
if (k && k->_IsToolBar)
{
LRect OldPos = v->GetPos();
Update.Union(&OldPos);
if (HasTools)
{
// 2nd and later toolbars
if (v->Pour(Tools))
{
if (!v->Visible())
{
v->Visible(true);
}
auto vpos = v->GetPos();
if (OldPos != vpos)
{
// position has changed update...
v->Invalidate();
}
// Has it increased the size of the toolbar area?
auto b = Tools.Bound();
if (vpos.y2 >= b.y2)
{
LRect Bar = Client;
Bar.y2 = vpos.y2;
Client.Subtract(&Bar);
// LgiTrace("IncreaseToolbar=%s\n", Bar.GetStr());
}
Tools.Subtract(&vpos);
Update.Subtract(&vpos);
// LgiTrace("vpos=%s\n", vpos.GetStr());
}
}
else
{
// First toolbar
if (v->Pour(Client))
{
HasTools = true;
if (!v->Visible())
{
v->Visible(true);
}
if (OldPos != v->GetPos())
{
v->Invalidate();
}
LRect Bar(v->GetPos());
Bar.x2 = GetClient().x2;
Tools = Bar;
Tools.Subtract(&v->GetPos());
Client.Subtract(&Bar);
Update.Subtract(&Bar);
}
}
}
}
}
// LgiTrace("Client=%s\n", Client.Bound().GetStr());
for (auto v: Children)
{
LView *k = dynamic_cast(v);
if (!(k && k->_IsToolBar))
{
LRect OldPos = v->GetPos();
Update.Union(&OldPos);
if (v->Pour(Client))
{
if (!v->Visible())
{
v->Visible(true);
}
if (OldPos != v->GetPos())
{
// position has changed update...
v->Invalidate();
}
Client.Subtract(&v->GetPos());
Update.Subtract(&v->GetPos());
}
else
{
// make the view not visible
// v->Visible(FALSE);
}
}
}
for (int i=0; iMsg())
{
case WM_DPICHANGED:
{
d->Dpi.x = HIWORD(Msg->A());
d->Dpi.y = LOWORD(Msg->A());
OnPosChange();
break;
}
case M_ASSERT_UI:
{
LAutoPtr Str((LString*)Msg->A());
extern void LAssertDlg(LString Msg, std::function Callback);
if (Str)
LAssertDlg(Str ? *Str : "Error: no msg.", NULL);
break;
}
case M_SET_WINDOW_PLACEMENT:
{
/* Apparently if you use SetWindowPlacement inside the WM_CREATE handler,
then the restored rect doesn't "stick", it gets stomped on by windows.
So this code... RESETS it to be what we set earlier. Windows sucks. */
if (!d->Wp || !_View)
break;
LRect r = d->Wp->rcNormalPosition;
if (!LView::Visible())
d->Wp->showCmd = SW_HIDE;
#if DEBUG_WINDOW_PLACEMENT
LgiTrace("%s:%i - SetWindowPlacement, pos=%s, show=%i\n", __FILE__, __LINE__, r.GetStr(), d->Wp->showCmd);
#endif
SetWindowPlacement(_View, d->Wp);
d->Wp.Reset();
break;
}
case WM_SYSCOLORCHANGE:
{
LColour::OnChange();
break;
}
case WM_WINDOWPOSCHANGING:
{
bool Icon = IsIconic(Handle()) != 0;
bool Zoom = IsZoomed(Handle()) != 0;
if (!Icon && (_Dialog || !Zoom))
{
WINDOWPOS *Info = (LPWINDOWPOS) Msg->b;
if (!Info)
break;
if (Info->flags == (SWP_NOSIZE | SWP_NOMOVE) && _Dialog)
{
// Info->flags |= SWP_NOZORDER;
Info->hwndInsertAfter = _Dialog->Handle();
}
if (GetMinimumSize().x &&
GetMinimumSize().x > Info->cx)
{
Info->cx = GetMinimumSize().x;
}
if (GetMinimumSize().y &&
GetMinimumSize().y > Info->cy)
{
Info->cy = GetMinimumSize().y;
}
/* This is broken on windows 10... windows get stuck on the edge of the desktop.
RECT Rc;
if (d->SnapToEdge &&
SystemParametersInfo(SPI_GETWORKAREA, 0, &Rc, SPIF_SENDCHANGE))
{
LRect r = Rc;
LRect p(Info->x,
Info->y,
Info->x + Info->cx - 1,
Info->y + Info->cy - 1);
if (r.Valid() && p.Valid())
{
int Snap = 12;
if (abs(p.x1 - r.x1) <= Snap)
{
// Snap left edge
Info->x = r.x1;
}
else if (abs(p.x2 - r.x2) <= Snap)
{
// Snap right edge
Info->x = r.x2 - Info->cx + 1;
}
if (abs(p.y1 - r.y1) <= Snap)
{
// Snap top edge
Info->y = r.y1;
}
else if (abs(p.y2 - r.y2) <= Snap)
{
// Snap bottom edge
Info->y = r.y2 - Info->cy + 1;
}
}
}
*/
}
break;
}
case WM_SIZE:
{
if (Visible())
{
LWindowZoom z = d->Show;
switch (Msg->a)
{
case SIZE_MINIMIZED:
{
z = LZoomMin;
break;
}
case SIZE_MAXIMIZED:
{
z = LZoomMax;
break;
}
case SIZE_RESTORED:
{
z = LZoomNormal;
break;
}
}
if (z != d->Show)
{
OnZoom(d->Show = z);
}
}
Status = LView::OnEvent(Msg) != 0;
break;
}
case WM_CREATE:
{
if (d->AlwaysOnTop)
SetAlwaysOnTop(true);
PourAll();
OnCreate();
if (!_Default)
{
_Default = FindControl(IDOK);
if (_Default)
_Default->Invalidate();
}
d->InCreate = false;
if (d->Wp)
{
PostEvent(M_SET_WINDOW_PLACEMENT);
}
break;
}
case WM_WINDOWPOSCHANGED:
{
d->Wp.Reset();
Status = LView::OnEvent(Msg) != 0;
break;
}
case WM_QUERYENDSESSION:
case WM_CLOSE:
{
bool QuitApp;
bool OsShuttingDown = Msg->Msg() == WM_QUERYENDSESSION;
if (QuitApp = OnRequestClose(OsShuttingDown))
{
Quit();
}
if (Msg->Msg() == WM_CLOSE)
{
return 0;
}
else
{
return QuitApp;
}
break;
}
case WM_SYSCOMMAND:
{
if (Msg->a == SC_CLOSE)
{
if (OnRequestClose(false))
{
Quit();
}
return 0;
}
else
{
Status = LView::OnEvent(Msg) != 0;
}
break;
}
case WM_DROPFILES:
{
HDROP hDrop = (HDROP) Msg->a;
if (hDrop)
{
LArray FileNames;
int Count = 0;
Count = DragQueryFileW(hDrop, -1, NULL, 0);
for (int i=0; i 0)
{
FileNames.Add(WideToUtf8(FileName));
}
}
OnReceiveFiles(FileNames);
FileNames.DeleteArrays();
}
break;
}
case M_HANDLEMOUSEMOVE:
{
// This receives events fired from the LMouseHookPrivate class so that
// non-LGI windows create mouse hook events as well.
LTempView v((OsView)Msg->B());
LMouse m;
m.x = LOWORD(Msg->A());
m.y = HIWORD(Msg->A());
HandleViewMouse(&v, m);
break;
}
case M_COMMAND:
{
HWND OurWnd = Handle(); // copy onto the stack, because
// we might lose the 'this' object in the
// OnCommand handler which would delete
// the memory containing the handle.
Status = OnCommand((int) Msg->a, 0, (OsView) Msg->b);
if (!IsWindow(OurWnd))
{
// The window was deleted so break out now
break;
}
// otherwise fall thru to the LView handler
}
default:
{
Status = (int) LView::OnEvent(Msg);
break;
}
}
return Status;
}
LPoint LWindow::GetDpi()
{
if (!d->Dpi.x)
d->Dpi = LGetDpiForWindow(_View);
return d->Dpi;
}
LPointF LWindow::GetDpiScale()
{
auto Dpi = GetDpi();
LPointF r( Dpi.x / 96.0, Dpi.y / 96.0 );
return r;
}
LRect &LWindow::GetPos()
{
if (_View && IsZoomed(_View))
{
static LRect r;
RECT rc;
GetWindowRect(_View, &rc);
r = rc;
return r;
}
return Pos;
}
void LWindow::OnPosChange()
{
PourAll();
}
bool LWindow::RegisterHook(LView *Target, LWindowHookType EventType, int Priority)
{
bool Status = false;
if (Target && EventType)
{
auto i = d->GetHookIndex(Target, true);
if (i >= 0)
{
d->Hooks[i].Flags = EventType;
Status = true;
}
}
return Status;
}
LViewI *LWindow::GetDefault()
{
return _Default;
}
void LWindow::SetDefault(LViewI *v)
{
#if WINNATIVE
LButton *Btn;
if (Btn = dynamic_cast(_Default))
Btn->Default(false);
#endif
_Default = v;
#if WINNATIVE
if (Btn = dynamic_cast(_Default))
Btn->Default(true);
#endif
}
bool LWindow::UnregisterHook(LView *Target)
{
auto i = d->GetHookIndex(Target);
if (i >= 0)
{
d->Hooks.DeleteAt(i);
return true;
}
return false;
}
bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load)
{
if (!Store || !FieldName)
return false;
#if DEBUG_SERIALIZE_STATE
LgiTrace("LWindow::SerializeState(%p, %s, %i)\n", Store, FieldName, Load);
#endif
if (Load)
{
LVariant v;
if (Store->GetValue(FieldName, v) && v.Str())
{
LRect Position(0, 0, -1, -1);
LWindowZoom State = LZoomNormal;
#if DEBUG_SERIALIZE_STATE
LgiTrace("\t::SerializeState:%i v=%s\n", __LINE__, v.Str());
#endif
LToken t(v.Str(), ";");
for (int i=0; iGetShow();
if (Show == SW_SHOWMINIMIZED ||
Show == SW_SHOWMINNOACTIVE ||
Show == SW_MINIMIZE)
{
State = LZoomMin;
}
else if (Show == SW_SHOWMAXIMIZED ||
Show == SW_MAXIMIZE)
{
State = LZoomMax;
}
LAutoPtr Wp(new WINDOWPLACEMENT);
if (Wp)
{
ZeroObj(*Wp.Get());
Wp->length = sizeof(WINDOWPLACEMENT);
if (Visible())
{
if (State == LZoomMax)
{
Wp->showCmd = SW_SHOWMAXIMIZED;
}
else if (State == LZoomMin)
{
Wp->showCmd = SW_MINIMIZE;
}
else
{
Wp->showCmd = SW_NORMAL;
}
}
else
{
Wp->showCmd = SW_HIDE;
d->Show = State;
}
LRect DefaultPos(100, 100, 900, 700);
if (Position.Valid())
{
LArray Displays;
LRect AllDisplays;
bool PosOk = true;
if (LGetDisplays(Displays, &AllDisplays))
{
// Check that the position is on one of the screens
PosOk = false;
for (unsigned i=0; ir;
Int.Intersection(&Position);
if (Int.Valid() &&
Int.X() > 20 &&
Int.Y() > 20)
{
PosOk = true;
break;
}
}
Displays.DeleteObjects();
}
if (PosOk)
Pos = Position;
else
Pos = DefaultPos;
}
else Pos = DefaultPos;
Wp->rcNormalPosition = Pos;
#if DEBUG_SERIALIZE_STATE
LgiTrace("%s:%i - SetWindowPlacement, pos=%s, show=%i\n", _FL, Pos.GetStr(), Wp->showCmd);
#endif
SetWindowPlacement(Handle(), Wp);
if (d->InCreate)
d->Wp = Wp;
}
}
else return false;
}
else
{
char s[256];
LWindowZoom State = GetZoom();
LRect Position;
if (Handle())
{
WINDOWPLACEMENT Wp;
ZeroObj(Wp);
Wp.length = sizeof(Wp);
GetWindowPlacement(Handle(), &Wp);
Position = Wp.rcNormalPosition;
}
else
{
// A reasonable fall back if we don't have a window...
Position = GetPos();
}
sprintf_s(s, sizeof(s), "State=%i;Pos=%s", State, Position.GetStr());
#if DEBUG_SERIALIZE_STATE
LgiTrace("\t::SerializeState:%i s='%s'\n", __LINE__, s);
#endif
LVariant v = s;
if (!Store->SetValue(FieldName, v))
return false;
}
return true;
}
void LWindow::OnTrayClick(LMouse &m)
{
if (m.Down() || m.IsContextMenu())
{
LSubMenu RClick;
OnTrayMenu(RClick);
if (GetMouse(m, true))
{
#if WINNATIVE
SetForegroundWindow(Handle());
#endif
int Result = RClick.Float(this, m);
#if WINNATIVE
PostMessage(Handle(), WM_NULL, 0, 0);
#endif
OnTrayMenuResult(Result);
}
}
}