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,307 +1,308 @@ #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; #elif !defined(HAIKU) 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; /// The menu on the window LMenu *Menu; void SetChildDialog(LDialog *Dlg); void SetDragHandlers(bool On); 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; bool HandleViewMouse(LView *v, LMouse &m); 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) 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) 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/src/common/Lgi/GuiUtils.cpp b/src/common/Lgi/GuiUtils.cpp --- a/src/common/Lgi/GuiUtils.cpp +++ b/src/common/Lgi/GuiUtils.cpp @@ -1,284 +1,284 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/SkinEngine.h" #if defined(LINUX) && !defined(LGI_SDL) #include "LgiWinManGlue.h" #endif ////////////////////////////////////////////////////////////////////////////////// #if !defined(LK_CONTEXTKEY) #if defined(WINDOWS) #define LK_CONTEXTKEY 0x5d #elif defined(MAC) #define LK_CONTEXTKEY VK_APPS #else #define LK_CONTEXTKEY 0x5d #warning "Check local platform def for app menu key." #endif #endif bool LKey::IsContextMenu() const { #if WINNATIVE || defined(LINUX) return !IsChar && vkey == LK_CONTEXTKEY; #else return false; #endif } ////////////////////////////////////////////////////////////////////////////////// LString LMouse::ToString() const { LString s; s.Printf("LMouse(pos=%i,%i view=%p/%s btns=%i/%i/%i/%i/%i dwn=%i dbl=%i " "ctrl=%i alt=%i sh=%i sys=%i)", x, y, // pos Target, Target?Target->GetClass():NULL, // view Left(), Middle(), Right(), Button1(), Button2(), // btns Down(), Double(), // dwn Ctrl(), Alt(), Shift(), System()); // mod keys return s; } bool LMouse::IsContextMenu() const { if (Right()) return true; - #if defined(MAC) || defined(LINUX) + #if defined(MAC) if (Left() && Ctrl()) return true; #endif return false; } bool LMouse::ToScreen() { if (ViewCoords) { if (!Target) { printf("%s:%i - ToScreen Error: Target=%p ViewCoords=%i\n", _FL, Target, ViewCoords); return false; } LPoint p(x, y); Target->PointToScreen(p); x = p.x; y = p.y; ViewCoords = false; } return true; } bool LMouse::ToView() { if (!ViewCoords) { if (!Target) { printf("%s:%i - ToView Error: Target=%p ViewCoords=%i\n", _FL, Target, ViewCoords); return false; } LPoint p(x, y); Target->PointToView(p); x = p.x; y = p.y; ViewCoords = true; } return true; } #if WINNATIVE #if !defined(DM_POSITION) #define DM_POSITION 0x00000020L #endif typedef WINUSERAPI BOOL (WINAPI *pEnumDisplayDevicesA)(PVOID, DWORD, PDISPLAY_DEVICEA, DWORD); typedef WINUSERAPI BOOL (WINAPI *pEnumDisplayDevicesW)(PVOID, DWORD, PDISPLAY_DEVICEW, DWORD); typedef WINUSERAPI BOOL (WINAPI *pEnumDisplaySettingsA)(LPCSTR lpszDeviceName, DWORD iModeNum, LPDEVMODEA lpDevMode); typedef WINUSERAPI BOOL (WINAPI *pEnumDisplaySettingsW)(LPCWSTR lpszDeviceName, DWORD iModeNum, LPDEVMODEW lpDevMode); #endif LPointF GDisplayInfo::Scale() { LPointF p((double)Dpi.x / 96.0, (double)Dpi.y / 96.0); return p; } bool LGetDisplays(::LArray &Displays, LRect *AllDisplays) { #if WINNATIVE if (AllDisplays) AllDisplays->ZOff(-1, -1); LLibrary User32("User32"); DISPLAY_DEVICEW disp; ZeroObj(disp); disp.cb = sizeof(disp); pEnumDisplayDevicesW EnumDisplayDevicesW = (pEnumDisplayDevicesW) User32.GetAddress("EnumDisplayDevicesW"); pEnumDisplaySettingsW EnumDisplaySettingsW = (pEnumDisplaySettingsW) User32.GetAddress("EnumDisplaySettingsW"); for (int i=0; EnumDisplayDevicesW(0, i, &disp, 0); i++) { DEVMODEW mode; ZeroObj(mode); mode.dmSize = sizeof(mode); mode.dmDriverExtra = sizeof(mode); if (EnumDisplaySettingsW(disp.DeviceName, ENUM_CURRENT_SETTINGS, &mode)) { GDisplayInfo *Dsp = new GDisplayInfo; if (Dsp) { Dsp->r.ZOff(mode.dmPelsWidth-1, mode.dmPelsHeight-1); if (mode.dmFields & DM_POSITION) { Dsp->r.Offset(mode.dmPosition.x, mode.dmPosition.y); } if (AllDisplays) { if (AllDisplays->Valid()) AllDisplays->Union(&Dsp->r); else *AllDisplays = Dsp->r; } disp.cb = sizeof(disp); Dsp->BitDepth = mode.dmBitsPerPel; Dsp->Refresh = mode.dmDisplayFrequency; Dsp->Name = disp.DeviceString; Dsp->Device = disp.DeviceName; Dsp->Dpi.x = Dsp->Dpi.y = mode.dmLogPixels; DISPLAY_DEVICEW temp = disp; if (EnumDisplayDevicesW(temp.DeviceName, 0, &disp, 0)) Dsp->Monitor = disp.DeviceString; Displays.Add(Dsp); } } disp.cb = sizeof(disp); } #elif defined __GTK_H__ Gtk::GdkDisplay *Dsp = Gtk::gdk_display_get_default(); #if GtkVer(3, 22) int monitors = Gtk::gdk_display_get_n_monitors(Dsp); for (int i=0; ir = geometry; di->Device = Gtk::gdk_monitor_get_manufacturer(m); di->Name = Gtk::gdk_monitor_get_model(m); di->Refresh = Gtk::gdk_monitor_get_refresh_rate(m); Displays.Add(di); } #endif #elif LGI_COCOA for (NSScreen *s in [NSScreen screens]) { GDisplayInfo *di = new GDisplayInfo; di->r = s.frame; Displays.Add(di); } #endif return Displays.Length() > 0; } void GetChildrenList(LViewI *w, List &l) { if (!w) return; for (auto v: w->IterateViews()) { #if WINNATIVE int Style = GetWindowLong(v->Handle(), GWL_STYLE); if (TestFlag(Style, WS_VISIBLE) && !TestFlag(Style, WS_DISABLED)) { if (TestFlag(Style, WS_TABSTOP)) { l.Insert(v); } GetChildrenList(v, l); } #else if (v->Visible() && v->Enabled()) { if (v->GetTabStop()) { l.Insert(v); } GetChildrenList(v, l); } #endif } } LViewI *GetNextTabStop(LViewI *v, bool Back) { if (v) { LWindow *Wnd = v->GetWindow(); if (Wnd) { List All; GetChildrenList(Wnd, All); ssize_t MyIndex = All.IndexOf(v); if (MyIndex >= 0) { int Inc = Back ? -1 : 1; size_t NewIndex = (MyIndex + All.Length() + Inc) % All.Length(); return All.ItemAt(NewIndex); } else { return All[0]; } } } 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,2537 +1,2535 @@ /// \file /// \author Matthew Allen #ifdef LINUX #include #endif #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/TableLayout.h" #include "lgi/common/Button.h" #include "lgi/common/Css.h" #include "lgi/common/LgiRes.h" #include "lgi/common/EventTargetThread.h" #include "lgi/common/Popup.h" #include "lgi/common/CssTools.h" #include "ViewPriv.h" #if 0 #define DEBUG_CAPTURE(...) printf(__VA_ARGS__) #else #define DEBUG_CAPTURE(...) #endif ////////////////////////////////////////////////////////////////////////////////////// // Helper LPoint lgi_view_offset(LViewI *v, bool Debug = false) { LPoint Offset; for (LViewI *p = v; p; p = p->GetParent()) { if (dynamic_cast(p)) break; LRect pos = p->GetPos(); LRect cli = p->GetClient(false); if (Debug) { const char *cls = p->GetClass(); LgiTrace(" Off[%s] += %i,%i = %i,%i (%s)\n", cls, pos.x1, pos.y1, Offset.x + pos.x1, Offset.y + pos.y1, cli.GetStr()); } Offset.x += pos.x1 + cli.x1; Offset.y += pos.y1 + cli.y1; } return Offset; } LMouse &lgi_adjust_click(LMouse &Info, LViewI *Wnd, bool Capturing, bool Debug) { static LMouse Temp; Temp = Info; if (Wnd) { if (Debug #if 0 || Capturing #endif ) LgiTrace("AdjustClick Target=%s -> Wnd=%s, Info=%i,%i\n", Info.Target?Info.Target->GetClass():"", Wnd?Wnd->GetClass():"", Info.x, Info.y); if (Temp.Target != Wnd) { if (Temp.Target) { auto *TargetWnd = Temp.Target->GetWindow(); auto *WndWnd = Wnd->GetWindow(); if (TargetWnd == WndWnd) { LPoint TargetOff = lgi_view_offset(Temp.Target, Debug); if (Debug) LgiTrace(" WndOffset:\n"); LPoint WndOffset = lgi_view_offset(Wnd, Debug); Temp.x += TargetOff.x - WndOffset.x; Temp.y += TargetOff.y - WndOffset.y; #if 0 LRect c = Wnd->GetClient(false); Temp.x -= c.x1; Temp.y -= c.y1; if (Debug) LgiTrace(" CliOff -= %i,%i\n", c.x1, c.y1); #endif Temp.Target = Wnd; } } else { LPoint Offset; Temp.Target = Wnd; if (Wnd->WindowVirtualOffset(&Offset)) { LRect c = Wnd->GetClient(false); Temp.x -= Offset.x + c.x1; Temp.y -= Offset.y + c.y1; } } } } LAssert(Temp.Target != NULL); return Temp; } ////////////////////////////////////////////////////////////////////////////////////// // LView class methods LViewI *LView::_Capturing = 0; LViewI *LView::_Over = 0; #if LGI_VIEW_HASH struct ViewTbl : public LMutex { typedef LHashTbl, int> T; private: T Map; public: ViewTbl() : Map(2000), LMutex("ViewTbl") { } T *Lock() { if (!LMutex::Lock(_FL)) return NULL; return ⤅ } } ViewTblInst; bool LView::LockHandler(LViewI *v, LView::LockOp Op) { ViewTbl::T *m = ViewTblInst.Lock(); if (!m) return false; int Ref = m->Find(v); bool Status = false; switch (Op) { case OpCreate: { if (Ref == 0) Status = m->Add(v, 1); else LAssert(!"Already exists?"); break; } case OpDelete: { if (Ref == 1) Status = m->Delete(v); else LAssert(!"Either locked or missing."); break; } case OpExists: { Status = Ref > 0; break; } } ViewTblInst.Unlock(); return Status; } #endif LView::LView(OsView view) { #ifdef _DEBUG _Debug = false; #endif d = new LViewPrivate(this); #ifdef LGI_SDL _View = this; #elif LGI_VIEW_HANDLE && !defined(HAIKU) _View = view; #endif _Window = 0; _Lock = 0; _InLock = 0; _BorderSize = 0; _IsToolBar = false; Pos.ZOff(-1, -1); WndFlags = GWF_VISIBLE; #ifndef LGI_VIEW_HASH #error "LGI_VIEW_HASH needs to be defined" #elif LGI_VIEW_HASH LockHandler(this, OpCreate); // printf("Adding %p to hash\n", (LViewI*)this); #endif } LView::~LView() { if (d->SinkHnd >= 0) { LEventSinkMap::Dispatch.RemoveSink(this); d->SinkHnd = -1; } #if LGI_VIEW_HASH LockHandler(this, OpDelete); #endif for (unsigned i=0; iOwner == this) { // printf("%s:%i - ~%s setting %s->Owner to NULL\n", _FL, GetClass(), pu->GetClass()); pu->Owner = NULL; } } _Delete(); DeleteObj(d); } int LView::AddDispatch() { if (d->SinkHnd < 0) d->SinkHnd = LEventSinkMap::Dispatch.AddSink(this); return d->SinkHnd; } LString LView::CssStyles(const char *Set) { if (Set) { d->Styles = Set; d->CssDirty = true; } return d->Styles; } LString::Array *LView::CssClasses() { return &d->Classes; } LArray LView::IterateViews() { LArray a; for (auto c: Children) a.Add(c); return a; } bool LView::AddView(LViewI *v, int Where) { LAssert(!Children.HasItem(v)); bool Add = Children.Insert(v, Where); if (Add) { LView *gv = v->GetGView(); if (gv && gv->_Window != _Window) { LAssert(!_InLock); gv->_Window = _Window; } v->SetParent(this); v->OnAttach(); OnChildrenChanged(v, true); } return Add; } bool LView::DelView(LViewI *v) { bool Has = Children.HasItem(v); bool b = Children.Delete(v); if (Has) OnChildrenChanged(v, false); Has = Children.HasItem(v); LAssert(!Has); return b; } bool LView::HasView(LViewI *v) { return Children.HasItem(v); } OsWindow LView::WindowHandle() { auto *w = GetWindow(); return (w) ? w->WindowHandle() : (OsWindow)NULL; } LWindow *LView::GetWindow() { if (!_Window) { // Walk up parent list and find someone who has a window auto *w = d->GetParent(); for (; w; w = w->d ? w->d->GetParent() : NULL) { if (w->_Window) { LAssert(!_InLock); _Window = w->_Window; break; } } } return dynamic_cast(_Window); } bool LView::Lock(const char *file, int line, int TimeOut) { if (!_Window) GetWindow(); _InLock++; // LgiTrace("%s::%p Lock._InLock=%i %s:%i\n", GetClass(), this, _InLock, file, line); if (_Window && _Window->_Lock) { if (TimeOut < 0) { return _Window->_Lock->Lock(file, line); } else { return _Window->_Lock->LockWithTimeout(TimeOut, file, line); } } return true; } void LView::Unlock() { #ifdef HAIKU if (!d || !d->Hnd) { printf("%s:%i - Unlock() error, no hnd.\n", _FL); return; } if (!d->Hnd->Parent()) { // printf("%s:%p - Unlock() no parent.\n", GetClass(), this); return; } if (_InLock > 0) { // printf("%s:%p - Calling UnlockLooper: %i.\n", GetClass(), this, _InLock); d->Hnd->UnlockLooper(); _InLock--; // printf("%s:%p - UnlockLooper done: %i.\n", GetClass(), this, _InLock); } else { printf("%s:%i - Unlock() without lock.\n", _FL); } #else if (_Window && _Window->_Lock) { _Window->_Lock->Unlock(); } _InLock--; // LgiTrace("%s::%p Unlock._InLock=%i\n", GetClass(), this, _InLock); #endif } void LView::OnMouseClick(LMouse &m) { } void LView::OnMouseEnter(LMouse &m) { } void LView::OnMouseExit(LMouse &m) { } void LView::OnMouseMove(LMouse &m) { } bool LView::OnMouseWheel(double Lines) { return false; } bool LView::OnKey(LKey &k) { return false; } void LView::OnAttach() { List::I it = Children.begin(); for (LViewI *v = *it; v; v = *++it) { if (!v->GetParent()) v->SetParent(this); } #if 0 // defined __GTK_H__ if (_View && !DropTarget()) { // If one of our parents is drop capable we need to set a dest here LViewI *p; for (p = GetParent(); p; p = p->GetParent()) { if (p->DropTarget()) { break; } } if (p) { Gtk::gtk_drag_dest_set( _View, (Gtk::GtkDestDefaults)0, NULL, 0, Gtk::GDK_ACTION_DEFAULT); // printf("%s:%i - Drop dest for '%s'\n", _FL, GetClass()); } else { Gtk::gtk_drag_dest_unset(_View); // printf("%s:%i - Not setting drop dest '%s'\n", _FL, GetClass()); } } #endif } void LView::OnCreate() { } void LView::OnDestroy() { } void LView::OnFocus(bool f) { // printf("%s::OnFocus(%i)\n", GetClass(), f); } void LView::OnPulse() { } void LView::OnPosChange() { } bool LView::OnRequestClose(bool OsShuttingDown) { return true; } int LView::OnHitTest(int x, int y) { return -1; } void LView::OnChildrenChanged(LViewI *Wnd, bool Attaching) { } void LView::OnPaint(LSurface *pDC) { auto c = GetClient(); LCssTools Tools(this); Tools.PaintContent(pDC, c); } int LView::OnNotify(LViewI *Ctrl, LNotification Data) { if (!Ctrl) return 0; if (Ctrl == (LViewI*)this && Data.Type == LNotifyActivate) { // Default activation is to focus the current control. Focus(true); } else if (d && d->Parent) { // default behaviour is just to pass the // notification up to the parent // FIXME: eventually we need to call the 'LNotification' parent fn... return d->Parent->OnNotify(Ctrl, Data); } return 0; } int LView::OnCommand(int Cmd, int Event, OsView Wnd) { return 0; } void LView::OnNcPaint(LSurface *pDC, LRect &r) { int Border = Sunken() || Raised() ? _BorderSize : 0; if (Border == 2) { LEdge e; if (Sunken()) e = Focus() ? EdgeWin7FocusSunken : DefaultSunkenEdge; else e = DefaultRaisedEdge; #if WINNATIVE if (d->IsThemed) DrawThemeBorder(pDC, r); else #endif LWideBorder(pDC, r, e); } else if (Border == 1) { LThinBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge); } } #if LGI_COCOA || defined(__GTK_H__) /* uint64 nPaint = 0; uint64 PaintTime = 0; */ void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { /* uint64 StartTs = Update ? LCurrentTime() : 0; d->InPaint = true; */ // Create temp DC if needed... LAutoPtr Local; if (!pDC) { if (!Local.Reset(new LScreenDC(this))) return; pDC = Local; } #if 0 // This is useful for coverage checking pDC->Colour(LColour(255, 0, 255)); pDC->Rectangle(); #endif // Non-Client drawing LRect r; if (Offset) { r = Pos; r.Offset(Offset); } else { r = GetClient().ZeroTranslate(); } pDC->SetClient(&r); LRect zr1 = r.ZeroTranslate(), zr2 = zr1; OnNcPaint(pDC, zr1); pDC->SetClient(NULL); if (zr2 != zr1) { r.x1 -= zr2.x1 - zr1.x1; r.y1 -= zr2.y1 - zr1.y1; r.x2 -= zr2.x2 - zr1.x2; r.y2 -= zr2.y2 - zr1.y2; } LPoint o(r.x1, r.y1); // Origin of client // Paint this view's contents... pDC->SetClient(&r); #if 0 if (_Debug) { #if defined(__GTK_H__) Gtk::cairo_matrix_t matrix; cairo_get_matrix(pDC->Handle(), &matrix); double ex[4]; cairo_clip_extents(pDC->Handle(), ex+0, ex+1, ex+2, ex+3); ex[0] += matrix.x0; ex[1] += matrix.y0; ex[2] += matrix.x0; ex[3] += matrix.y0; LgiTrace("%s::_Paint, r=%s, clip=%g,%g,%g,%g - %g,%g\n", GetClass(), r.GetStr(), ex[0], ex[1], ex[2], ex[3], matrix.x0, matrix.y0); #elif LGI_COCOA auto Ctx = pDC->Handle(); CGAffineTransform t = CGContextGetCTM(Ctx); LRect cr = CGContextGetClipBoundingBox(Ctx); printf("%s::_Paint() pos=%s transform=%g,%g,%g,%g-%g,%g clip=%s r=%s\n", GetClass(), GetPos().GetStr(), t.a, t.b, t.c, t.d, t.tx, t.ty, cr.GetStr(), r.GetStr()); #endif } #endif OnPaint(pDC); pDC->SetClient(NULL); // Paint all the children... for (auto i : Children) { LView *w = i->GetGView(); if (w && w->Visible()) { if (!w->Pos.Valid()) continue; #if 0 if (w->_Debug) LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), o.x, o.y); #endif w->_Paint(pDC, &o); } } } #else void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { // Create temp DC if needed... LAutoPtr Local; if (!pDC) { Local.Reset(new LScreenDC(this)); pDC = Local; } if (!pDC) { printf("%s:%i - No context to draw in.\n", _FL); return; } #if 0 // This is useful for coverage checking pDC->Colour(LColour(255, 0, 255)); pDC->Rectangle(); #endif bool HasClient = false; LRect r(0, 0, Pos.X()-1, Pos.Y()-1), Client; LPoint o; if (Offset) o = *Offset; #if WINNATIVE if (!_View) #endif { // Non-Client drawing Client = r; OnNcPaint(pDC, Client); HasClient = GetParent() && (Client != r); if (HasClient) { Client.Offset(o.x, o.y); pDC->SetClient(&Client); } } r.Offset(o.x, o.y); // Paint this view's contents if (Update) { LRect OldClip = pDC->ClipRgn(); pDC->ClipRgn(Update); OnPaint(pDC); pDC->ClipRgn(OldClip.Valid() ? &OldClip : NULL); } else { OnPaint(pDC); } #if PAINT_VIRTUAL_CHILDREN // Paint any virtual children for (auto i : Children) { LView *w = i->GetGView(); if (w && w->Visible()) { #if LGI_VIEW_HANDLE if (!w->Handle()) #endif { LRect p = w->GetPos(); p.Offset(o.x, o.y); if (HasClient) p.Offset(Client.x1 - r.x1, Client.y1 - r.y1); LPoint co(p.x1, p.y1); // LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), p.x1, p.y1); pDC->SetClient(&p); w->_Paint(pDC, &co); pDC->SetClient(NULL); } } } #endif if (HasClient) pDC->SetClient(0); } #endif LViewI *LView::GetParent() { ThreadCheck(); return d ? d->Parent : NULL; } void LView::SetParent(LViewI *p) { ThreadCheck(); d->Parent = p ? p->GetGView() : NULL; d->ParentI = p; } void LView::SendNotify(LNotification note) { LViewI *n = d->Notify ? d->Notify : d->Parent; if (n) { if ( #if LGI_VIEW_HANDLE && !defined(HAIKU) !_View || #endif InThread()) { n->OnNotify(this, note); } else { // We are not in the same thread as the target window. So we post a message // across to the view. if (GetId() <= 0) { // We are going to generate a control Id to help the receiver of the // M_CHANGE message find out view later on. The reason for doing this // instead of sending a pointer to the object, is that the object // _could_ be deleted between the message being sent and being received. // Which would result in an invalid memory access on that object. LViewI *p = GetWindow(); if (!p) { // No window? Find the top most parent we can... p = this; while (p->GetParent()) p = p->GetParent(); } if (p) { // Give the control a valid ID int i; for (i=10; i<1000; i++) { if (!p->FindControl(i)) { printf("Giving the ctrl '%s' the id '%i' for SendNotify\n", GetClass(), i); SetId(i); break; } } } else { // Ok this is really bad... go random (better than nothing) SetId(5000 + LRand(2000)); } } LAssert(GetId() > 0); // We must have a valid ctrl ID at this point, otherwise // the receiver will never be able to find our object. // printf("Post M_CHANGE %i %i\n", GetId(), Data); n->PostEvent(M_CHANGE, (LMessage::Param) GetId(), (LMessage::Param) new LNotification(note)); } } } LViewI *LView::GetNotify() { ThreadCheck(); return d->Notify; } void LView::SetNotify(LViewI *p) { ThreadCheck(); d->Notify = p; } #define ADJ_LEFT 1 #define ADJ_RIGHT 2 #define ADJ_UP 3 #define ADJ_DOWN 4 int IsAdjacent(LRect &a, LRect &b) { if ( (a.x1 == b.x2 + 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_LEFT; } if ( (a.x2 == b.x1 - 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_RIGHT; } if ( (a.y1 == b.y2 + 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_UP; } if ( (a.y2 == b.y1 - 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_DOWN; } return 0; } LRect JoinAdjacent(LRect &a, LRect &b, int Adj) { LRect t; switch (Adj) { case ADJ_LEFT: case ADJ_RIGHT: { t.y1 = MAX(a.y1, b.y1); t.y2 = MIN(a.y2, b.y2); t.x1 = MIN(a.x1, b.x1); t.x2 = MAX(a.x2, b.x2); break; } case ADJ_UP: case ADJ_DOWN: { t.y1 = MIN(a.y1, b.y1); t.y2 = MAX(a.y2, b.y2); t.x1 = MAX(a.x1, b.x1); t.x2 = MIN(a.x2, b.x2); break; } } return t; } LRect *LView::FindLargest(LRegion &r) { ThreadCheck(); int Pixels = 0; LRect *Best = 0; static LRect Final; // do initial run through the list to find largest single region for (LRect *i = r.First(); i; i = r.Next()) { int Pix = i->X() * i->Y(); if (Pix > Pixels) { Pixels = Pix; Best = i; } } if (Best) { Final = *Best; Pixels = Final.X() * Final.Y(); int LastPixels = Pixels; LRect LastRgn = Final; int ThisPixels = Pixels; LRect ThisRgn = Final; LRegion TempList; for (LRect *i = r.First(); i; i = r.Next()) { TempList.Union(i); } TempList.Subtract(Best); do { LastPixels = ThisPixels; LastRgn = ThisRgn; // search for adjoining rectangles that maybe we can add for (LRect *i = TempList.First(); i; i = TempList.Next()) { int Adj = IsAdjacent(ThisRgn, *i); if (Adj) { LRect t = JoinAdjacent(ThisRgn, *i, Adj); int Pix = t.X() * t.Y(); if (Pix > ThisPixels) { ThisPixels = Pix; ThisRgn = t; TempList.Subtract(i); } } } } while (LastPixels < ThisPixels); Final = ThisRgn; } else return 0; return &Final; } LRect *LView::FindSmallestFit(LRegion &r, int Sx, int Sy) { ThreadCheck(); int X = 1000000; int Y = 1000000; LRect *Best = 0; for (LRect *i = r.First(); i; i = r.Next()) { if ((i->X() >= Sx && i->Y() >= Sy) && (i->X() < X || i->Y() < Y)) { X = i->X(); Y = i->Y(); Best = i; } } return Best; } LRect *LView::FindLargestEdge(LRegion &r, int Edge) { LRect *Best = 0; ThreadCheck(); for (LRect *i = r.First(); i; i = r.Next()) { if (!Best) { Best = i; } if ( ((Edge & GV_EDGE_TOP) && (i->y1 < Best->y1)) || ((Edge & GV_EDGE_RIGHT) && (i->x2 > Best->x2)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 > Best->y2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 < Best->x1)) ) { Best = i; } if ( ( ((Edge & GV_EDGE_TOP) && (i->y1 == Best->y1)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 == Best->y2)) ) && ( i->X() > Best->X() ) ) { Best = i; } if ( ( ((Edge & GV_EDGE_RIGHT) && (i->x2 == Best->x2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 == Best->x1)) ) && ( i->Y() > Best->Y() ) ) { Best = i; } } return Best; } LViewI *LView::FindReal(LPoint *Offset) { ThreadCheck(); if (Offset) { Offset->x = 0; Offset->y = 0; } #if !LGI_VIEW_HANDLE LViewI *w = GetWindow(); #endif LViewI *p = d->Parent; while (p && #if !LGI_VIEW_HANDLE p != w #else !p->Handle() #endif ) { if (Offset) { Offset->x += Pos.x1; Offset->y += Pos.y1; } p = p->GetParent(); } if (p && #if !LGI_VIEW_HANDLE p == w #else p->Handle() #endif ) { return p; } return NULL; } bool LView::HandleCapture(LView *Wnd, bool c) { ThreadCheck(); DEBUG_CAPTURE("%s::HandleCapture(%i)=%i\n", GetClass(), c, (int)(_Capturing == Wnd)); if (c) { if (_Capturing == Wnd) { DEBUG_CAPTURE(" %s already has capture\n", _Capturing?_Capturing->GetClass():0); } else { DEBUG_CAPTURE(" _Capturing=%s -> %s\n", _Capturing?_Capturing->GetClass():0, Wnd?Wnd->GetClass():0); _Capturing = Wnd; #if defined(__GTK_H__) if (d->CaptureThread) d->CaptureThread->Cancel(); d->CaptureThread = new LCaptureThread(this); #elif WINNATIVE LPoint Offset; LViewI *v = _Capturing->Handle() ? _Capturing : FindReal(&Offset); HWND h = v ? v->Handle() : NULL; if (h) SetCapture(h); else LAssert(0); #elif defined(LGI_SDL) #if SDL_VERSION_ATLEAST(2, 0, 4) SDL_CaptureMouse(SDL_TRUE); #else LAppInst->CaptureMouse(true); #endif #endif } } else if (_Capturing) { DEBUG_CAPTURE(" _Capturing=%s -> NULL\n", _Capturing?_Capturing->GetClass():0); _Capturing = NULL; #if defined(__GTK_H__) if (d->CaptureThread) { d->CaptureThread->Cancel(); d->CaptureThread = NULL; // It'll delete itself... } #elif WINNATIVE ReleaseCapture(); #elif defined(LGI_SDL) #if SDL_VERSION_ATLEAST(2, 0, 4) SDL_CaptureMouse(SDL_FALSE); #else LAppInst->CaptureMouse(false); #endif #endif } return true; } bool LView::IsCapturing() { - ThreadCheck(); - DEBUG_CAPTURE("%s::IsCapturing()=%i\n", GetClass(), (int)(_Capturing == this)); return _Capturing == this; } bool LView::Capture(bool c) { ThreadCheck(); DEBUG_CAPTURE("%s::Capture(%i)\n", GetClass()); return HandleCapture(this, c); } bool LView::Enabled() { ThreadCheck(); #if WINNATIVE if (_View) return IsWindowEnabled(_View) != 0; #endif return !TestFlag(GViewFlags, GWF_DISABLED); } void LView::Enabled(bool i) { ThreadCheck(); if (!i) SetFlag(GViewFlags, GWF_DISABLED); else ClearFlag(GViewFlags, GWF_DISABLED); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) { #if WINNATIVE EnableWindow(_View, i); #elif defined LGI_CARBON if (i) { OSStatus e = EnableControl(_View); if (e) printf("%s:%i - Error enabling control (%i)\n", _FL, (int)e); } else { OSStatus e = DisableControl(_View); if (e) printf("%s:%i - Error disabling control (%i)\n", _FL, (int)e); } #endif } #endif Invalidate(); } bool LView::Visible() { // This is a read only operation... which is kinda thread-safe... // ThreadCheck(); #if WINNATIVE if (_View) /* This takes into account all the parent windows as well... Which is kinda not what I want. I want this to reflect just this window. return IsWindowVisible(_View); */ return (GetWindowLong(_View, GWL_STYLE) & WS_VISIBLE) != 0; #endif return TestFlag(GViewFlags, GWF_VISIBLE); } void LView::Visible(bool v) { ThreadCheck(); if (v) SetFlag(GViewFlags, GWF_VISIBLE); else ClearFlag(GViewFlags, GWF_VISIBLE); #if defined(HAIKU) LLocker lck(d->Hnd, _FL); if (!IsAttached() || lck.Lock()) { if (v) d->Hnd->Show(); else d->Hnd->Hide(); } else LgiTrace("%s:%i - Can't lock.\n", _FL); #elif LGI_VIEW_HANDLE if (_View) { #if WINNATIVE ShowWindow(_View, (v) ? SW_SHOWNORMAL : SW_HIDE); #elif LGI_COCOA LAutoPool Pool; [_View.p setHidden:!v]; #elif LGI_CARBON Boolean is = HIViewIsVisible(_View); if (v != is) { OSErr e = HIViewSetVisible(_View, v); if (e) printf("%s:%i - HIViewSetVisible(%p,%i) failed with %i (class=%s)\n", _FL, _View, v, e, GetClass()); } #endif } else #endif { Invalidate(); } } bool LView::Focus() { ThreadCheck(); bool Has = false; #if defined(__GTK_H__) LWindow *w = GetWindow(); if (w) { bool Active = w->IsActive(); if (Active) Has = w->GetFocus() == static_cast(this); } #elif defined(WINNATIVE) if (_View) { HWND hFocus = GetFocus(); Has = hFocus == _View; } #elif LGI_COCOA Has = TestFlag(WndFlags, GWF_FOCUS); #elif LGI_CARBON LWindow *w = GetWindow(); if (w) { ControlRef Cur; OSErr e = GetKeyboardFocus(w->WindowHandle(), &Cur); if (e) LgiTrace("%s:%i - GetKeyboardFocus failed with %i\n", _FL, e); else Has = (Cur == _View); } #endif #if !LGI_CARBON if (Has) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); #endif return Has; } void LView::Focus(bool i) { ThreadCheck(); if (i) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); auto *Wnd = GetWindow(); if (Wnd) Wnd->SetFocus(this, i ? LWindow::GainFocus : LWindow::LoseFocus); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) #endif { #if defined(LGI_SDL) || defined(__GTK_H__) // Nop: Focus is all handled by Lgi's LWindow class. #elif WINNATIVE if (i) { HWND hCur = GetFocus(); if (hCur != _View) { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus %p (%-30s)\n", _FL, Handle(), Name()); } SetFocus(_View); } } else { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus(%p)\n", _FL, GetDesktopWindow()); } SetFocus(GetDesktopWindow()); } #elif defined LGI_CARBON LViewI *Wnd = GetWindow(); if (Wnd && i) { OSErr e = SetKeyboardFocus(Wnd->WindowHandle(), _View, 1); if (e) { // e = SetKeyboardFocus(Wnd->WindowHandle(), _View, kControlFocusNextPart); // if (e) { HIViewRef p = HIViewGetSuperview(_View); // errCouldntSetFocus printf("%s:%i - SetKeyboardFocus failed: %i (%s, %p)\n", _FL, e, GetClass(), p); } } // else printf("%s:%i - SetFocus v=%p(%s)\n", _FL, _View, GetClass()); } else printf("%s:%i - no window?\n", _FL); #endif } } LDragDropSource *LView::DropSource(LDragDropSource *Set) { if (Set) d->DropSource = Set; return d->DropSource; } LDragDropTarget *LView::DropTarget(LDragDropTarget *Set) { if (Set) d->DropTarget = Set; return d->DropTarget; } #if defined LGI_CARBON extern pascal OSStatus LgiViewDndHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); #endif #if defined __GTK_H__ // Recursively add drag dest to all view and all children bool GtkAddDragDest(LViewI *v, bool IsTarget) { if (!v) return false; LWindow *w = v->GetWindow(); if (!w) return false; auto wid = GtkCast(w->WindowHandle(), gtk_widget, GtkWidget); if (IsTarget) { Gtk::gtk_drag_dest_set( wid, (Gtk::GtkDestDefaults)0, NULL, 0, Gtk::GDK_ACTION_DEFAULT); } else { Gtk::gtk_drag_dest_unset(wid); } for (LViewI *c: v->IterateViews()) GtkAddDragDest(c, IsTarget); return true; } #endif bool LView::DropTarget(bool t) { ThreadCheck(); bool Status = false; if (t) SetFlag(GViewFlags, GWF_DROP_TARGET); else ClearFlag(GViewFlags, GWF_DROP_TARGET); #if WINNATIVE if (_View) { if (t) { if (!d->DropTarget) DragAcceptFiles(_View, t); else Status = RegisterDragDrop(_View, (IDropTarget*) d->DropTarget) == S_OK; } else { if (_View && d->DropTarget) Status = RevokeDragDrop(_View) == S_OK; } } #elif defined MAC && !defined(LGI_SDL) LWindow *Wnd = dynamic_cast(GetWindow()); if (Wnd) { Wnd->SetDragHandlers(t); if (!d->DropTarget) d->DropTarget = t ? Wnd : 0; } #if LGI_COCOA LWindow *w = GetWindow(); if (w) { OsWindow h = w->WindowHandle(); if (h) { NSMutableArray *a = [[NSMutableArray alloc] init]; if (a) { [a addObject:(NSString*)kUTTypeItem]; for (id item in NSFilePromiseReceiver.readableDraggedTypes) [a addObject:item]; [h.p.contentView registerForDraggedTypes:a]; [a release]; } } } #elif LGI_CARBON if (t) { static EventTypeSpec DragEvents[] = { { kEventClassControl, kEventControlDragEnter }, { kEventClassControl, kEventControlDragWithin }, { kEventClassControl, kEventControlDragLeave }, { kEventClassControl, kEventControlDragReceive }, }; if (!d->DndHandler) { OSStatus e = ::InstallControlEventHandler( _View, NewEventHandlerUPP(LgiViewDndHandler), GetEventTypeCount(DragEvents), DragEvents, (void*)this, &d->DndHandler); if (e) LgiTrace("%s:%i - InstallEventHandler failed (%i)\n", _FL, e); } SetControlDragTrackingEnabled(_View, true); } else { SetControlDragTrackingEnabled(_View, false); } #endif #elif defined __GTK_H__ Status = GtkAddDragDest(this, t); if (Status && !d->DropTarget) d->DropTarget = t ? GetWindow() : 0; #endif return Status; } bool LView::Sunken() { // ThreadCheck(); #if WINNATIVE return TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE); #else return TestFlag(GViewFlags, GWF_SUNKEN); #endif } void LView::Sunken(bool i) { ThreadCheck(); #if WINNATIVE if (i) SetFlag(d->WndExStyle, WS_EX_CLIENTEDGE); else ClearFlag(d->WndExStyle, WS_EX_CLIENTEDGE); if (_View) SetWindowLong(_View, GWL_EXSTYLE, d->WndExStyle); #else if (i) SetFlag(GViewFlags, GWF_SUNKEN); else ClearFlag(GViewFlags, GWF_SUNKEN); #endif if (i) { if (!_BorderSize) _BorderSize = 2; } else _BorderSize = 0; } bool LView::Flat() { // ThreadCheck(); #if WINNATIVE return !TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE) && !TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return !TestFlag(GViewFlags, GWF_SUNKEN) && !TestFlag(GViewFlags, GWF_RAISED); #endif } void LView::Flat(bool i) { ThreadCheck(); #if WINNATIVE ClearFlag(d->WndExStyle, (WS_EX_CLIENTEDGE|WS_EX_WINDOWEDGE)); #else ClearFlag(GViewFlags, (GWF_RAISED|GWF_SUNKEN)); #endif } bool LView::Raised() { // ThreadCheck(); #if WINNATIVE return TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return TestFlag(GViewFlags, GWF_RAISED); #endif } void LView::Raised(bool i) { ThreadCheck(); #if WINNATIVE if (i) SetFlag(d->WndExStyle, WS_EX_WINDOWEDGE); else ClearFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else if (i) SetFlag(GViewFlags, GWF_RAISED); else ClearFlag(GViewFlags, GWF_RAISED); #endif if (i) { if (!!_BorderSize) _BorderSize = 2; } else _BorderSize = 0; } int LView::GetId() { // This is needed by SendNotify function which is thread safe. // So no thread safety check here. return d->CtrlId; } void LView::SetId(int i) { // This is needed by SendNotify function which is thread safe. // So no thread safety check here. d->CtrlId = i; #if WINNATIVE if (_View) SetWindowLong(_View, GWL_ID, d->CtrlId); #elif defined __GTK_H__ #elif defined MAC #endif } bool LView::GetTabStop() { ThreadCheck(); #if WINNATIVE return TestFlag(d->WndStyle, WS_TABSTOP); #else return d->TabStop; #endif } void LView::SetTabStop(bool b) { ThreadCheck(); #if WINNATIVE if (b) SetFlag(d->WndStyle, WS_TABSTOP); else ClearFlag(d->WndStyle, WS_TABSTOP); #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) { #ifdef LGI_SDL return LPostEvent(this, Cmd, a, b); #elif defined(HAIKU) if (!d || !d->Hnd || !d->Hnd->LockLooper()) return false; BMessage *m = new BMessage(Cmd); if (!m) return false; m->AddUInt64(LMessage::PropNames[0], a); m->AddUInt64(LMessage::PropNames[1], b); auto w = d && d->Hnd ? d->Hnd->Window() : NULL; status_t r = w->PostMessage(m); d->Hnd->UnlockLooper(); return r == B_OK; #elif WINNATIVE if (!_View) return false; BOOL Res = ::PostMessage(_View, Cmd, a, b); if (!Res) { auto Err = GetLastError(); int asd=0; } return Res != 0; #elif !LGI_VIEW_HANDLE return LAppInst->PostEvent(this, Cmd, a, b); #else if (_View) return LPostEvent(_View, Cmd, a, b); LAssert(0); return false; #endif } bool LView::Invalidate(LRegion *r, bool Repaint, bool NonClient) { if (r) { for (int i=0; iLength(); i++) { bool Last = i == r->Length()-1; Invalidate((*r)[i], Last ? Repaint : false, NonClient); } return true; } return false; } LButton *FindDefault(LViewI *w) { LButton *But = 0; for (auto c: w->IterateViews()) { But = dynamic_cast(c); if (But && But->Default()) { break; } But = FindDefault(c); if (But) { break; } } return But; } bool LView::Name(const char *n) { LBase::Name(n); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) { #if WINNATIVE auto Temp = LBase::NameW(); SetWindowTextW(_View, Temp ? Temp : L""); #endif } #endif Invalidate(); return true; } const char *LView::Name() { #if WINNATIVE if (_View) { LView::NameW(); } #endif return LBase::Name(); } bool LView::NameW(const char16 *n) { LBase::NameW(n); #if WINNATIVE if (_View && n) { auto Txt = LBase::NameW(); SetWindowTextW(_View, Txt); } #endif Invalidate(); return true; } const char16 *LView::NameW() { #if WINNATIVE if (_View) { int Length = GetWindowTextLengthW(_View); if (Length > 0) { char16 *Buf = new char16[Length+1]; if (Buf) { Buf[0] = 0; int Chars = GetWindowTextW(_View, Buf, Length+1); Buf[Chars] = 0; LBase::NameW(Buf); } DeleteArray(Buf); } else { LBase::NameW(0); } } #endif return LBase::NameW(); } LViewI *LView::FindControl(int Id) { LAssert(Id != -1); if (GetId() == Id) { return this; } for (auto c : Children) { LViewI *Ctrl = c->FindControl(Id); if (Ctrl) { return Ctrl; } } return 0; } LPoint LView::GetMinimumSize() { return d->MinimumSize; } void LView::SetMinimumSize(LPoint Size) { d->MinimumSize = Size; bool Change = false; LRect p = Pos; if (X() < d->MinimumSize.x) { p.x2 = p.x1 + d->MinimumSize.x - 1; Change = true; } if (Y() < d->MinimumSize.y) { p.y2 = p.y1 + d->MinimumSize.y - 1; Change = true; } if (Change) { SetPos(p); } } bool LView::SetColour(LColour &c, bool Fore) { LCss *css = GetCss(true); if (!css) return false; if (Fore) { if (c.IsValid()) css->Color(LCss::ColorDef(LCss::ColorRgb, c.c32())); else css->DeleteProp(LCss::PropColor); } else { if (c.IsValid()) css->BackgroundColor(LCss::ColorDef(LCss::ColorRgb, c.c32())); else css->DeleteProp(LCss::PropBackgroundColor); } return true; } /* bool LView::SetCssStyle(const char *CssStyle) { if (!d->Css && !d->Css.Reset(new LCss)) return false; const char *Defs = CssStyle; bool b = d->Css->Parse(Defs, LCss::ParseRelaxed); if (b && d->FontOwnType == GV_FontCached) { d->Font = NULL; d->FontOwnType = GV_FontPtr; } return b; } */ void LView::SetCss(LCss *css) { d->Css.Reset(css); } LCss *LView::GetCss(bool Create) { if (Create && !d->Css) d->Css.Reset(new LCss); if (d->CssDirty && d->Css) { const char *Defs = d->Styles; if (d->Css->Parse(Defs, LCss::ParseRelaxed)) d->CssDirty = false; } return d->Css; } LPoint &LView::GetWindowBorderSize() { static LPoint s; ZeroObj(s); #if WINNATIVE if (_View) { RECT Wnd, Client; GetWindowRect(Handle(), &Wnd); GetClientRect(Handle(), &Client); s.x = (Wnd.right-Wnd.left) - (Client.right-Client.left); s.y = (Wnd.bottom-Wnd.top) - (Client.bottom-Client.top); } #elif defined __GTK_H__ #elif defined MAC s.x = 0; s.y = 22; #endif return s; } #ifdef _DEBUG #if defined(LGI_CARBON) void DumpHiview(HIViewRef v, int Depth = 0) { char Sp[256]; memset(Sp, ' ', Depth << 2); Sp[Depth<<2] = 0; printf("%sHIView=%p", Sp, v); if (v) { Boolean vis = HIViewIsVisible(v); Boolean en = HIViewIsEnabled(v, NULL); HIRect pos; HIViewGetFrame(v, &pos); char cls[128]; ZeroObj(cls); GetControlProperty(v, 'meme', 'clas', sizeof(cls), NULL, cls); printf(" vis=%i en=%i pos=%g,%g-%g,%g cls=%s", vis, en, pos.origin.x, pos.origin.y, pos.size.width, pos.size.height, cls); } printf("\n"); for (HIViewRef c = HIViewGetFirstSubview(v); c; c = HIViewGetNextView(c)) { DumpHiview(c, Depth + 1); } } #elif defined(__GTK_H__) void DumpGtk(Gtk::GtkWidget *w, Gtk::gpointer Depth = NULL) { using namespace Gtk; if (!w) return; char Sp[65] = {0}; if (Depth) memset(Sp, ' ', *((int*)Depth)*2); auto *Obj = G_OBJECT(w); LViewI *View = (LViewI*) g_object_get_data(Obj, "LViewI"); GtkAllocation a; gtk_widget_get_allocation(w, &a); LgiTrace("%s%p(%s) = %i,%i-%i,%i\n", Sp, w, View?View->GetClass():G_OBJECT_TYPE_NAME(Obj), a.x, a.y, a.width, a.height); if (GTK_IS_CONTAINER(w)) { auto *c = GTK_CONTAINER(w); if (c) { int Next = Depth ? *((int*)Depth)+1 : 1; gtk_container_foreach(c, DumpGtk, &Next); } } } #elif defined(HAIKU) template void _Dump(int Depth, T *v) { LString Sp, Name; Sp.Length(Depth<<1); memset(Sp.Get(), ' ', Depth<<1); LRect Frame = v->Frame(); LViewPrivate *Priv = dynamic_cast(v); if (Priv) Name = Priv->View->GetClass(); printf("%s### %p::%s frame=%s vis=%i\n", Sp.Get(), v, Name.Get(), Frame.GetStr(), !v->IsHidden()); for (int32 i=0; iCountChildren(); i++) { _Dump(Depth + 1, v->ChildAt(i)); } } #endif void LView::_Dump(int Depth) { char Sp[65] = {0}; memset(Sp, ' ', Depth*2); #if 0 char s[256]; sprintf_s(s, sizeof(s), "%s%p::%s %s (_View=%p)\n", Sp, this, GetClass(), GetPos().GetStr(), _View); LgiTrace(s); List::I i = Children.Start(); for (LViewI *c = *i; c; c = *++i) { LView *v = c->GetGView(); if (v) v->_Dump(Depth+1); } #elif defined(LGI_CARBON) DumpHiview(_View); #elif defined(__GTK_H__) // DumpGtk(_View); #elif defined(HAIKU) LLocker lck(WindowHandle(), _FL); if (lck.Lock()) ::_Dump(0, WindowHandle()); #endif } #endif //////////////////////////////////////////////////////////////////////////////////////////////////// static LArray *AllFactories = NULL; #if defined(WIN32) static HANDLE FactoryEvent; #else static pthread_once_t FactoryOnce = PTHREAD_ONCE_INIT; static void GFactoryInitFactories() { AllFactories = new LArray; } #endif LViewFactory::LViewFactory() { #if defined(WIN32) char16 Name[64]; swprintf_s(Name, CountOf(Name), L"LgiFactoryEvent.%i", GetCurrentProcessId()); HANDLE h = CreateEventW(NULL, false, false, Name); DWORD err = GetLastError(); if (err != ERROR_ALREADY_EXISTS) { FactoryEvent = h; AllFactories = new LArray; } else { LAssert(AllFactories != NULL); } #else pthread_once(&FactoryOnce, GFactoryInitFactories); #endif if (AllFactories) AllFactories->Add(this); } LViewFactory::~LViewFactory() { if (AllFactories) { AllFactories->Delete(this); if (AllFactories->Length() == 0) { DeleteObj(AllFactories); #if defined(WIN32) CloseHandle(FactoryEvent); #endif } } } LView *LViewFactory::Create(const char *Class, LRect *Pos, const char *Text) { if (ValidStr(Class) && AllFactories) { for (int i=0; iLength(); i++) { LView *v = (*AllFactories)[i]->NewView(Class, Pos, Text); if (v) { return v; } } } return 0; } #ifdef _DEBUG #if defined(__GTK_H__) using namespace Gtk; #include "LgiWidget.h" #endif void LView::Debug() { _Debug = true; #if defined LGI_COCOA d->ClassName = GetClass(); #endif } #endif diff --git a/src/linux/Lgi/Window.cpp b/src/linux/Lgi/Window.cpp --- a/src/linux/Lgi/Window.cpp +++ b/src/linux/Lgi/Window.cpp @@ -1,1856 +1,1881 @@ #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" using namespace Gtk; #undef Status #include "LgiWidget.h" #define DEBUG_SETFOCUS 0 #define DEBUG_HANDLEVIEWKEY 0 extern Gtk::GdkDragAction EffectToDragAction(int Effect); /////////////////////////////////////////////////////////////////////// class HookInfo { public: LWindowHookType Flags; LView *Target; }; enum LAttachState { LUnattached, LAttaching, LAttached, LDetaching, }; class LWindowPrivate { public: int Sx, Sy; bool Dynamic; LKey LastKey; ::LArray Hooks; bool SnapToEdge; ::LString Icon; LRect Decor; gulong DestroySig; LAutoPtr IconImg; LAttachState AttachState; // State GdkWindowState State; bool HadCreateEvent; // Focus stuff OsView FirstFocus; LViewI *Focus; bool Active; LWindowPrivate() { AttachState = LUnattached; DestroySig = 0; Decor.ZOff(-1, -1); FirstFocus = NULL; Focus = NULL; Active = false; State = (Gtk::GdkWindowState)0; HadCreateEvent = false; Sx = Sy = 0; Dynamic = true; SnapToEdge = false; LastKey.vkey = 0; LastKey.c16 = 0; LastKey.Data = 0; LastKey.IsChar = 0; } int GetHookIndex(LView *Target, bool Create = false) { for (int i=0; iTarget = Target; n->Flags = LNoEvents; return Hooks.Length() - 1; } } return -1; } }; /////////////////////////////////////////////////////////////////////// #define GWND_CREATE 0x0010000 LWindow::LWindow(GtkWidget *w) : LView(0) { d = new LWindowPrivate; _QuitOnClose = false; Menu = NULL; Wnd = GTK_WINDOW(w); if (Wnd) g_object_set_data(G_OBJECT(Wnd), "LViewI", (LViewI*)this); _Root = NULL; _MenuBar = NULL; _VBox = NULL; _Default = 0; _Window = this; WndFlags |= GWND_CREATE; ClearFlag(WndFlags, GWF_VISIBLE); _Lock = new ::LMutex("LWindow"); } LWindow::~LWindow() { d->AttachState = LDetaching; if (Wnd && d->DestroySig > 0) { // As we are already in the destructor, we don't want // GtkWindowDestroy to try and delete the object again. g_signal_handler_disconnect(Wnd, d->DestroySig); } if (LAppInst->AppWnd == this) LAppInst->AppWnd = NULL; if (_Root) { lgi_widget_detach(_Root); _Root = NULL; } if (Wnd) { gtk_widget_destroy(GTK_WIDGET(Wnd)); Wnd = NULL; } d->AttachState = LUnattached; DeleteObj(Menu); DeleteObj(d); DeleteObj(_Lock); } /* static void PixbufDestroyNotify(guchar *pixels, LSurface *data) { delete data; } */ bool LWindow::SetIcon(const char *FileName) { LString a; if (Wnd) { if (!LFileExists(FileName)) { if (a = LFindFile(FileName)) FileName = a; } if (!LFileExists(FileName)) { LgiTrace("%s:%i - SetIcon failed to find '%s'\n", _FL, FileName); return false; } else { #if defined(LINUX) LAppInst->SetApplicationIcon(FileName); #endif #if _MSC_VER GError *error = NULL; if (gtk_window_set_icon_from_file(Wnd, FileName, &error)) return true; #else // On windows this is giving a red for blue channel swap error... if (d->IconImg.Reset(GdcD->Load(a))) gtk_window_set_icon(Wnd, d->IconImg->CreatePixBuf()); #endif } } if (FileName != d->Icon.Get()) d->Icon = FileName; return d->Icon != NULL; } bool LWindow::GetSnapToEdge() { return d->SnapToEdge; } void LWindow::SetSnapToEdge(bool s) { d->SnapToEdge = s; } bool LWindow::IsActive() { return d->Active; } bool LWindow::SetActive() { if (!Wnd) return false; gtk_window_present(Wnd); return true; } bool LWindow::Visible() { return LView::Visible(); } void LWindow::Visible(bool i) { ThreadCheck(); auto w = GTK_WIDGET(Wnd); if (i) gtk_widget_show(w); else gtk_widget_hide(w); } bool LWindow::Obscured() { return d->State == GDK_WINDOW_STATE_WITHDRAWN || d->State == GDK_WINDOW_STATE_ICONIFIED; } void LWindow::_SetDynamic(bool i) { d->Dynamic = i; } void LWindow::_OnViewDelete() { if (d->Dynamic) { delete this; } } void LWindow::OnGtkRealize() { d->AttachState = LAttached; LView::OnGtkRealize(); } void LWindow::OnGtkDelete() { // Delete everything we own... // DeleteObj(Menu); #if 0 while (Children.Length()) { LViewI *c = Children.First(); c->Detach(); } #else for (unsigned i=0; iGetGView(); if (v) v->OnGtkDelete(); } #endif // These will be destroyed by GTK after returning from LWindowCallback Wnd = NULL; #ifndef __GTK_H__ _View = NULL; #endif } LRect *LWindow::GetDecorSize() { return d->Decor.x2 >= 0 ? &d->Decor : NULL; } void LWindow::SetDecor(bool Visible) { if (Wnd) gtk_window_set_decorated (Wnd, Visible); else LgiTrace("%s:%i - No window to set decor.\n", _FL); } LViewI *LWindow::WindowFromPoint(int x, int y, bool Debug) { if (!_Root) return NULL; auto rpos = GtkGetPos(_Root).ZeroTranslate(); if (!rpos.Overlap(x, y)) return NULL; return LView::WindowFromPoint(x - rpos.x1, y - rpos.y1, Debug); } bool LWindow::TranslateMouse(LMouse &m) { m.Target = WindowFromPoint(m.x, m.y, false); if (!m.Target) return false; LViewI *w = this; for (auto p = m.Target; p; p = p->GetParent()) { if (p == w) { auto ppos = GtkGetPos(GTK_WIDGET(WindowHandle())); m.x -= ppos.x1; m.y -= ppos.y1; break; } else { auto pos = p->GetPos(); m.x -= pos.x1; m.y -= pos.y1; } } return true; } gboolean LWindow::OnGtkEvent(GtkWidget *widget, GdkEvent *event) { if (!event) { printf("%s:%i - No event.\n", _FL); return FALSE; } #if 0 if (event->type != 28) LgiTrace("%s::OnGtkEvent(%i) name=%s\n", GetClass(), event->type, Name()); #endif switch (event->type) { case GDK_DELETE: { bool Close = OnRequestClose(false); if (Close) OnGtkDelete(); return !Close; } case GDK_DESTROY: { delete this; return true; } case GDK_KEY_PRESS: case GDK_KEY_RELEASE: { auto ModFlags = LAppInst->GetKeyModFlags(); auto e = &event->key; #define KEY(name) GDK_KEY_##name LKey k; k.Down(e->type == GDK_KEY_PRESS); k.c16 = k.vkey = e->keyval; k.Shift((e->state & ModFlags->Shift) != 0); k.Ctrl((e->state & ModFlags->Ctrl) != 0); k.Alt((e->state & ModFlags->Alt) != 0); k.System((e->state & ModFlags->System) != 0); #if 0//def _DEBUG if (k.vkey == GDK_KEY_Meta_L || k.vkey == GDK_KEY_Meta_R) break; #endif k.IsChar = !k.Ctrl() && !k.Alt() && !k.System() && (k.c16 >= ' ') && (k.c16 >> 8 != 0xff); if (e->keyval > 0xff && e->string != NULL) { // Convert string to unicode char auto *i = e->string; ptrdiff_t len = strlen(i); k.c16 = LgiUtf8To32((uint8_t *&) i, len); } switch (k.vkey) { case GDK_KEY_ISO_Left_Tab: case KEY(Tab): k.IsChar = true; k.c16 = k.vkey = LK_TAB; break; case KEY(Return): case KEY(KP_Enter): k.IsChar = true; k.c16 = k.vkey = LK_RETURN; break; case GDK_KEY_BackSpace: k.c16 = k.vkey = LK_BACKSPACE; k.IsChar = !k.Ctrl() && !k.Alt() && !k.System(); break; case KEY(Left): k.vkey = k.c16 = LK_LEFT; break; case KEY(Right): k.vkey = k.c16 = LK_RIGHT; break; case KEY(Up): k.vkey = k.c16 = LK_UP; break; case KEY(Down): k.vkey = k.c16 = LK_DOWN; break; case KEY(Page_Up): k.vkey = k.c16 = LK_PAGEUP; break; case KEY(Page_Down): k.vkey = k.c16 = LK_PAGEDOWN; break; case KEY(Home): k.vkey = k.c16 = LK_HOME; break; case KEY(End): k.vkey = k.c16 = LK_END; break; case KEY(Delete): k.vkey = k.c16 = LK_DELETE; break; #define KeyPadMap(gdksym, ch, is) \ case gdksym: k.c16 = ch; k.IsChar = is; break; KeyPadMap(KEY(KP_0), '0', true) KeyPadMap(KEY(KP_1), '1', true) KeyPadMap(KEY(KP_2), '2', true) KeyPadMap(KEY(KP_3), '3', true) KeyPadMap(KEY(KP_4), '4', true) KeyPadMap(KEY(KP_5), '5', true) KeyPadMap(KEY(KP_6), '6', true) KeyPadMap(KEY(KP_7), '7', true) KeyPadMap(KEY(KP_8), '8', true) KeyPadMap(KEY(KP_9), '9', true) KeyPadMap(KEY(KP_Space), ' ', true) KeyPadMap(KEY(KP_Tab), '\t', true) KeyPadMap(KEY(KP_F1), LK_F1, false) KeyPadMap(KEY(KP_F2), LK_F2, false) KeyPadMap(KEY(KP_F3), LK_F3, false) KeyPadMap(KEY(KP_F4), LK_F4, false) KeyPadMap(KEY(KP_Home), LK_HOME, false) KeyPadMap(KEY(KP_Left), LK_LEFT, false) KeyPadMap(KEY(KP_Up), LK_UP, false) KeyPadMap(KEY(KP_Right), LK_RIGHT, false) KeyPadMap(KEY(KP_Down), LK_DOWN, false) KeyPadMap(KEY(KP_Page_Up), LK_PAGEUP, false) KeyPadMap(KEY(KP_Page_Down), LK_PAGEDOWN, false) KeyPadMap(KEY(KP_End), LK_END, false) KeyPadMap(KEY(KP_Begin), LK_HOME, false) KeyPadMap(KEY(KP_Insert), LK_INSERT, false) KeyPadMap(KEY(KP_Delete), LK_DELETE, false) KeyPadMap(KEY(KP_Equal), '=', true) KeyPadMap(KEY(KP_Multiply), '*', true) KeyPadMap(KEY(KP_Add), '+', true) KeyPadMap(KEY(KP_Separator), '|', true) // is this right? KeyPadMap(KEY(KP_Subtract), '-', true) KeyPadMap(KEY(KP_Decimal), '.', true) KeyPadMap(KEY(KP_Divide), '/', true) } if (ModFlags->Debug) { :: LString Msg; Msg.Printf("e->state=%x %s", e->state, ModFlags->FlagsToString(e->state).Get()); k.Trace(Msg); } auto v = d->Focus ? d->Focus : this; if (!HandleViewKey(v->GetGView(), k)) { if (!k.Down()) return false; if (k.vkey == LK_TAB || k.vkey == KEY(ISO_Left_Tab)) { // Do tab between controls ::LArray a; BuildTabStops(this, a); int idx = a.IndexOf((LViewI*)v); if (idx >= 0) { idx += k.Shift() ? -1 : 1; int next_idx = idx == 0 ? a.Length() -1 : idx % a.Length(); LViewI *next = a[next_idx]; if (next) { // LgiTrace("Setting focus to %i of %i: %s, %s, %i\n", next_idx, a.Length(), next->GetClass(), next->GetPos().GetStr(), next->GetId()); next->Focus(true); } } } else if (k.System()) { if (ToLower(k.c16) == 'q') { auto AppWnd = LAppInst->AppWnd; auto Wnd = AppWnd ? AppWnd : this; if (Wnd->OnRequestClose(false)) { Wnd->Quit(); return true; } } } else return false; } break; } case GDK_CONFIGURE: { GdkEventConfigure *c = &event->configure; Pos.Set(c->x, c->y, c->x+c->width-1, c->y+c->height-1); // printf("%s::GDK_CONFIGURE %s\n", GetClass(), Pos.GetStr()); OnPosChange(); return FALSE; break; } case GDK_FOCUS_CHANGE: { d->Active = event->focus_change.in; #if 0 printf("%s/%s::GDK_FOCUS_CHANGE(%i)\n", GetClass(), Name(), event->focus_change.in); #endif break; } case GDK_WINDOW_STATE: { d->State = event->window_state.new_window_state; break; } case GDK_PROPERTY_NOTIFY: { gchar *Name = gdk_atom_name (event->property.atom); if (!Name) break; if (!_stricmp(Name, "_NET_FRAME_EXTENTS")) { // printf("PropChange: %i - %s\n", event->property.atom, Name); unsigned long *extents = NULL; if (gdk_property_get(event->property.window, gdk_atom_intern ("_NET_FRAME_EXTENTS", FALSE), gdk_atom_intern ("CARDINAL", FALSE), 0, sizeof (unsigned long) * 4, FALSE, NULL, NULL, NULL, (guchar **)&extents)) { d->Decor.Set(extents[0], extents[2], extents[1], extents[3]); g_free(extents); } else printf("%s:%i - Error: gdk_property_get failed.\n", _FL); } g_free(Name); break; } case GDK_UNMAP: { // LgiTrace("%s:%i - Unmap %s\n", _FL, GetClass()); break; } case GDK_VISIBILITY_NOTIFY: { // LgiTrace("%s:%i - Visible %s\n", _FL, GetClass()); break; } case GDK_DRAG_ENTER: { LgiTrace("%s:%i - GDK_DRAG_ENTER\n", _FL); break; } case GDK_DRAG_LEAVE: { LgiTrace("%s:%i - GDK_DRAG_LEAVE\n", _FL); break; } case GDK_DRAG_MOTION: { LgiTrace("%s:%i - GDK_DRAG_MOTION\n", _FL); break; } case GDK_DRAG_STATUS: { LgiTrace("%s:%i - GDK_DRAG_STATUS\n", _FL); break; } case GDK_DROP_START: { LgiTrace("%s:%i - GDK_DROP_START\n", _FL); break; } case GDK_DROP_FINISHED: { LgiTrace("%s:%i - GDK_DROP_FINISHED\n", _FL); break; } default: { printf("%s:%i - Unknown event %i\n", _FL, event->type); return false; } } return true; } static gboolean GtkWindowDestroy(GtkWidget *widget, LWindow *This) { delete This; return true; } static void GtkWindowRealize(GtkWidget *widget, LWindow *This) { #if 0 LgiTrace("GtkWindowRealize, This=%p(%s\"%s\")\n", This, (NativeInt)This > 0x1000 ? This->GetClass() : 0, (NativeInt)This > 0x1000 ? This->Name() : 0); #endif This->OnGtkRealize(); } static void GtkRootResize(GtkWidget *widget, GdkRectangle *alloc, LView *This) { LWindow *w = This->GetWindow(); if (w) w->PourAll(); } void LWindowUnrealize(GtkWidget *widget, LWindow *wnd) { // printf("%s:%i - LWindowUnrealize %s\n", _FL, wnd->GetClass()); } 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; } void LWindowDragBegin(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataDelete(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataGet(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *data, guint info, guint time, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } void LWindowDragDataReceived(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, guint info, guint time, LWindow *Wnd) { LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return; for (auto &d: t->Data) { auto type = gdk_atom_name(gtk_selection_data_get_data_type(data)); if (d.Format.Equals(type)) { gint length = 0; auto ptr = gtk_selection_data_get_data_with_length(data, &length); if (ptr) { d.Data[0].SetBinary(length, (void*)ptr, false); } break; } } } int GetAcceptFmts(::LString::Array &Formats, GdkDragContext *context, LDragDropTarget *t, LPoint &p) { int KeyState = 0; LDragFormats Fmts(true); int Flags = DROPEFFECT_NONE; GList *targets = gdk_drag_context_list_targets(context); Gtk::GList *i = targets; while (i) { auto a = gdk_atom_name((GdkAtom)i->data); if (a) Fmts.Supports(a); i = i->next; } Fmts.SetSource(false); Flags = t->WillAccept(Fmts, p, KeyState); auto Sup = Fmts.GetSupported(); for (auto &s: Sup) Formats.New() = s; return Flags; } gboolean LWindowDragDataDrop(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, LWindow *Wnd) { // Map the point to a view... LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return false; t->Data.Length(0); // Request the data... ::LArray Data; ::LString::Array Formats; int KeyState = 0; int Flags = GetAcceptFmts(Formats, context, t, p); for (auto f: Formats) { t->Data.New().Format = f; gtk_drag_get_data(widget, context, gdk_atom_intern(f, true), time); } // Wait for the data to arrive... uint64_t Start = LCurrentTime(); while (LCurrentTime()-Start < 2000) { int HasData = 0; for (auto d: t->Data) if (d.Data.Length() > 0) HasData++; if (HasData >= Formats.Length()) break; LYield(); } auto Result = t->OnDrop(t->Data, p, KeyState); if (Flags != DROPEFFECT_NONE) gdk_drag_status(context, EffectToDragAction(Flags), time); return Result != DROPEFFECT_NONE; } void LWindowDragEnd(GtkWidget *widget, GdkDragContext *context, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } gboolean LWindowDragFailed(GtkWidget *widget, GdkDragContext *context, GtkDragResult result, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); return false; } void LWindowDragLeave(GtkWidget *widget, GdkDragContext *context, guint time, LWindow *Wnd) { LgiTrace("%s:%i - %s %s\n", _FL, Wnd->GetClass(), __func__); } gboolean LWindowDragMotion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, LWindow *Wnd) { LPoint p; LViewI *v; LDragDropTarget *t; if (!DndPointMap(v, p, t, Wnd, x, y)) return false; ::LString::Array Formats; int Flags = GetAcceptFmts(Formats, context, t, p); if (Flags != DROPEFFECT_NONE) gdk_drag_status(context, EffectToDragAction(Flags), time); return Flags != DROPEFFECT_NONE; } bool LWindow::Attach(LViewI *p) { bool Status = false; ThreadCheck(); // Setup default button... if (!_Default) _Default = FindControl(IDOK); // Create a window if needed.. if (!Wnd) Wnd = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); if (Wnd) { auto Widget = GTK_WIDGET(Wnd); LView *i = this; if (Pos.X() > 0 && Pos.Y() > 0) gtk_window_resize(Wnd, Pos.X(), Pos.Y()); gtk_window_move(Wnd, Pos.x1, Pos.y1); auto Obj = G_OBJECT(Wnd); g_object_set_data(Obj, "LViewI", (LViewI*)this); d->DestroySig = g_signal_connect(Obj, "destroy", G_CALLBACK(GtkWindowDestroy), this); g_signal_connect(Obj, "delete_event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "key-press-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "key-release-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "focus-in-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "focus-out-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "window-state-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "property-notify-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "configure-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "unmap-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "visibility-notify-event",G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "realize", G_CALLBACK(GtkWindowRealize), i); g_signal_connect(Obj, "unrealize", G_CALLBACK(LWindowUnrealize), i); g_signal_connect(Obj, "drag-begin", G_CALLBACK(LWindowDragBegin), i); g_signal_connect(Obj, "drag-data-delete", G_CALLBACK(LWindowDragDataDelete), i); g_signal_connect(Obj, "drag-data-get", G_CALLBACK(LWindowDragDataGet), i); g_signal_connect(Obj, "drag-data-received", G_CALLBACK(LWindowDragDataReceived), i); g_signal_connect(Obj, "drag-drop", G_CALLBACK(LWindowDragDataDrop), i); g_signal_connect(Obj, "drag-end", G_CALLBACK(LWindowDragEnd), i); g_signal_connect(Obj, "drag-failed", G_CALLBACK(LWindowDragFailed), i); g_signal_connect(Obj, "drag-leave", G_CALLBACK(LWindowDragLeave), i); g_signal_connect(Obj, "drag-motion", G_CALLBACK(LWindowDragMotion), i); #if 0 g_signal_connect(Obj, "button-press-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "button-release-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "motion-notify-event", G_CALLBACK(GtkViewCallback), i); g_signal_connect(Obj, "scroll-event", G_CALLBACK(GtkViewCallback), i); #endif gtk_widget_add_events( Widget, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_VISIBILITY_NOTIFY_MASK); gtk_window_set_title(Wnd, LBase::Name()); d->AttachState = LAttaching; // g_action_map_add_action_entries (G_ACTION_MAP(Wnd), app_entries, G_N_ELEMENTS (app_entries), Wnd); if ((_Root = lgi_widget_new(this, true))) { g_signal_connect(_Root, "size-allocate", G_CALLBACK(GtkRootResize), i); GtkContainer *AttachPoint = NULL; if (GTK_IS_DIALOG(Wnd)) { auto content = gtk_dialog_get_content_area(GTK_DIALOG(Wnd)); if (!content) { LAssert(!"No content area"); return false; } AttachPoint = GTK_CONTAINER(content); } else { AttachPoint = GTK_CONTAINER(Wnd); } LAssert(AttachPoint != NULL); gtk_container_add(AttachPoint, _Root); // Check it actually worked... (would a return value kill you GTK? no it would not) auto p = gtk_widget_get_parent(_Root); if (!p) { LAssert(!"Add failed"); return false; } gtk_widget_show(_Root); } // This call sets up the GdkWindow handle gtk_widget_realize(Widget); // Do a rough layout of child windows PourAll(); // Add icon if (d->Icon) { SetIcon(d->Icon); d->Icon.Empty(); } + + auto p = GetParent(); + if (p) + { + auto pHnd = p->WindowHandle(); + if (!pHnd) + LgiTrace("%s:%i - SetParent() - no pHnd from %s.\n", _FL, p->GetClass()); + else + gtk_window_set_transient_for(GTK_WINDOW(Wnd), pHnd); + } Status = true; } return Status; } bool LWindow::OnRequestClose(bool OsShuttingDown) { if (GetQuitOnClose()) { LCloseApp(); } return LView::OnRequestClose(OsShuttingDown); } bool LWindow::HandleViewMouse(LView *v, LMouse &m) { 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 (d->Focus=%s/%p)\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":""), d->Focus?d->Focus->GetClass():0, d->Focus); } #endif // 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: if (d) d->LastKey = k; return Status; } void LWindow::Raise() { if (Wnd) gtk_window_present(Wnd); } LWindowZoom LWindow::GetZoom() { switch (d->State) { case GDK_WINDOW_STATE_ICONIFIED: return LZoomMin; case GDK_WINDOW_STATE_MAXIMIZED: return LZoomMax; default: break; } return LZoomNormal; } void LWindow::SetZoom(LWindowZoom i) { if (!Wnd) { // LgiTrace("%s:%i - No window.\n", _FL); return; } ThreadCheck(); switch (i) { case LZoomMin: { gtk_window_iconify(Wnd); break; } case LZoomNormal: { gtk_window_deiconify(Wnd); gtk_window_unmaximize(Wnd); break; } case LZoomMax: { gtk_window_maximize(Wnd); break; } default: { LgiTrace("%s:%i - Error: unsupported zoom.\n", _FL); break; } } } 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) { if (Wnd) { ThreadCheck(); gtk_window_set_title(Wnd, n); } return LBase::Name(n); } const char *LWindow::Name() { return LBase::Name(); } struct CallbackParams { LRect Menu; int Depth; CallbackParams() { Menu.ZOff(-1, -1); Depth = 0; } }; void ClientCallback(GtkWidget *w, CallbackParams *p) { const char *Name = gtk_widget_get_name(w); if (Name && !_stricmp(Name, "GtkMenuBar")) { GtkAllocation alloc = {0}; gtk_widget_get_allocation(w, &alloc); p->Menu.ZOff(alloc.width-1, alloc.height-1); // LgiTrace("GtkMenuBar = %s\n", p->Menu.GetStr()); } if (!p->Menu.Valid()) { p->Depth++; if (GTK_IS_CONTAINER(w)) gtk_container_forall(GTK_CONTAINER(w), (GtkCallback)ClientCallback, p); p->Depth--; } } LPoint LWindow::GetDpi() { return LScreenDpi(); } 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 = LView::GetClient(ClientSpace); if (Wnd) { CallbackParams p; gtk_container_forall(GTK_CONTAINER(Wnd), (GtkCallback)ClientCallback, &p); if (p.Menu.Valid()) { if (ClientSpace) r.y2 -= p.Menu.Y(); else r.y1 += p.Menu.Y(); } } return r; } 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; // printf("SerializeState load %s\n", v.Str()); LToken t(v.Str(), ";"); for (int i=0; iName()); v = p[n]; ch += sprintf_s(s + ch, sizeof(s) - ch, "%s>%s", Buf, v->GetClass()); } return LAutoString(NewStr(s)); } #endif void LWindow::SetFocus(LViewI *ctrl, FocusType type) { #if DEBUG_SETFOCUS const char *TypeName = NULL; switch (type) { case GainFocus: TypeName = "Gain"; break; case LoseFocus: TypeName = "Lose"; break; case ViewDelete: TypeName = "Delete"; break; } #endif switch (type) { case GainFocus: { if (d->Focus == ctrl) { #if DEBUG_SETFOCUS LAutoString _ctrl = DescribeView(ctrl); LgiTrace("SetFocus(%s, %s) already has focus.\n", _ctrl.Get(), TypeName); #endif return; } if (d->Focus) { LView *gv = d->Focus->GetGView(); if (gv) { #if DEBUG_SETFOCUS LAutoString _foc = DescribeView(d->Focus); LgiTrace(".....defocus LView: %s\n", _foc.Get()); #endif gv->_Focus(false); } else if (IsActive()) { #if DEBUG_SETFOCUS LAutoString _foc = DescribeView(d->Focus); LgiTrace(".....defocus view: %s (active=%i)\n", _foc.Get(), IsActive()); #endif d->Focus->OnFocus(false); d->Focus->Invalidate(); } } d->Focus = ctrl; if (d->Focus) { #if DEBUG_SETFOCUS static int Count = 0; #endif LView *gv = d->Focus->GetGView(); if (gv) { #if DEBUG_SETFOCUS LAutoString _set = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) %i focusing LView\n", _set.Get(), TypeName, Count++); #endif gv->_Focus(true); } else if (IsActive()) { #if DEBUG_SETFOCUS LAutoString _set = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) %i focusing nonGView (active=%i)\n", _set.Get(), TypeName, Count++, IsActive()); #endif d->Focus->OnFocus(true); d->Focus->Invalidate(); } } break; } case LoseFocus: { #if DEBUG_SETFOCUS LAutoString _Ctrl = DescribeView(d->Focus); LAutoString _Focus = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) d->Focus=%s\n", _Ctrl.Get(), TypeName, _Focus.Get()); #endif if (ctrl == d->Focus) { d->Focus = NULL; } break; } case ViewDelete: { if (ctrl == d->Focus) { #if DEBUG_SETFOCUS LAutoString _Ctrl = DescribeView(d->Focus); LgiTrace("LWindow::SetFocus(%s, %s) on delete\n", _Ctrl.Get(), TypeName); #endif d->Focus = NULL; } break; } } } void LWindow::SetDragHandlers(bool On) { } +void LWindow::SetParent(LViewI *p) +{ + LView::SetParent(p); + if (p && Wnd) + { + auto pHnd = p->WindowHandle(); + if (!pHnd) + LgiTrace("%s:%i - SetParent() - no pHnd from %s.\n", _FL, p->GetClass()); + else if (!GTK_IS_WINDOW(Wnd)) + LgiTrace("%s:%i - SetParent() - GTK_IS_WINDOW failed.\n", _FL); + else + gtk_window_set_transient_for(GTK_WINDOW(Wnd), pHnd); + } +} + bool LWindow::IsAttached() { return d->AttachState == LAttaching || d->AttachState == LAttached; } 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.x, m.y); #if WINNATIVE PostMessage(Handle(), WM_NULL, 0, 0); #endif OnTrayMenuResult(Result); } } } void LWindow::Quit(bool DontDelete) { ThreadCheck(); if (Wnd) { d->AttachState = LDetaching; auto wnd = Wnd; Wnd = NULL; gtk_widget_destroy(GTK_WIDGET(wnd)); } } void LWindow::SetAlwaysOnTop(bool b) { }