diff --git a/Ide/Code/DocEdit.cpp b/Ide/Code/DocEdit.cpp --- a/Ide/Code/DocEdit.cpp +++ b/Ide/Code/DocEdit.cpp @@ -1,532 +1,530 @@ #include "lgi/common/Lgi.h" #include "lgi/common/LgiRes.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/Menu.h" #include "LgiIde.h" #include "DocEdit.h" #include "IdeDocPrivate.h" #define EDIT_TRAY_HEIGHT (LSysFont->GetHeight() + 10) #define EDIT_LEFT_MARGIN 16 // gutter for debug break points int DocEdit::LeftMarginPx = EDIT_LEFT_MARGIN; LAutoPtr GlobalFindReplace; DocEdit::DocEdit(IdeDoc *d, LFontType *f) : LTextView3(IDC_EDIT, 0, 0, 100, 100, f), DocEditStyling(this) { RefreshSize = 0; RefreshEdges = NULL; FileType = SrcUnknown; Doc = d; CurLine = -1; if (!GlobalFindReplace) { GlobalFindReplace.Reset(CreateFindReplaceParams()); } SetFindReplaceParams(GlobalFindReplace); CanScrollX = true; GetCss(true)->PaddingLeft(LCss::Len(LCss::LenPx, (float)(LeftMarginPx + 2))); if (!f) { LFontType Type; if (Type.GetSystemFont("Fixed")) { LFont *f = Type.Create(); if (f) { #if defined LINUX f->PointSize(9); #elif defined WIN32 f->PointSize(8); #endif SetFont(f); } } } SetWrapType(TEXTED_WRAP_NONE); SetEnv(this); } DocEdit::~DocEdit() { ParentState = KExiting; Event.Signal(); while (!IsExited()) LSleep(1); SetEnv(0); } void DocEdit::OnCreate() { LTextView3::OnCreate(); Run(); } bool DocEdit::AppendItems(LSubMenu *Menu, const char *Param, int Base) { LSubMenu *Insert = Menu->AppendSub("Insert..."); if (Insert) { Insert->AppendItem("File Comment", IDM_FILE_COMMENT, Doc->GetProject() != 0); Insert->AppendItem("Function Comment", IDM_FUNC_COMMENT, Doc->GetProject() != 0); } return true; } void DocEdit::DoGoto(std::function Callback) { LInput *Dlg = new LInput(this, "", LLoadString(L_TEXTCTRL_GOTO_LINE, "Goto [file:]line:"), "Goto"); Dlg->DoModal([&, App=Doc->GetApp()](auto d, auto code) { bool status = code && ValidStr(Dlg->GetStr()); if (status) { auto s = Dlg->GetStr(); LString::Array p = s.SplitDelimit(":,"); if (p.Length() == 2) { LString file = p[0]; int line = (int)p[1].Int(); App->GotoReference(file, line, false, true); } else if (p.Length() == 1) { int line = (int)p[0].Int(); if (line > 0) SetLine(line); else // Probably a filename with no line number.. App->GotoReference(p[0], 1, false, true); } } if (Callback) Callback(status); delete Dlg; }); } bool DocEdit::SetPourEnabled(bool b) { bool e = PourEnabled; PourEnabled = b; if (PourEnabled) { PourText(0, Size); PourStyle(0, Size); } return e; } int DocEdit::GetTopPaddingPx() { return GetCss(true)->PaddingTop().ToPx(GetClient().Y(), GetFont()); } void DocEdit::InvalidateLine(int Idx) { LTextLine *Ln = LTextView3::Line[Idx]; if (Ln) { int PadPx = GetTopPaddingPx(); LRect r = Ln->r; r.Offset(0, -ScrollYPixel() + PadPx); // LgiTrace("%s:%i - r=%s\n", _FL, r.GetStr()); Invalidate(&r); } } void DocEdit::OnPaint(LSurface *pDC) { LTextView3::OnPaint(pDC); #if 0 LRect cli = GetClient(); pDC->Colour(LColour::Red); pDC->Box(&cli); for (LViewI *p = GetParent(); p; p = p->GetParent()) { if (p == (LViewI*)GetWindow()) break; auto pos = p->GetPos(); cli.Offset(pos.x1, pos.y1); } LgiTrace("cli=%s\n", cli.GetStr()); #endif } void DocEdit::OnPaintLeftMargin(LSurface *pDC, LRect &r, LColour &colour) { LColour GutterColour(0xfa, 0xfa, 0xfa); LTextView3::OnPaintLeftMargin(pDC, r, GutterColour); int Y = ScrollYLine(); int TopPaddingPx = GetTopPaddingPx(); pDC->Colour(LColour(200, 0, 0)); List::I it = LTextView3::Line.begin(Y); int DocOffset = (*it)->r.y1; for (LTextLine *l = *it; l; l = *++it, Y++) { if (Doc->d->BreakPoints.Find(Y+1)) { int r = l->r.Y() >> 1; pDC->FilledCircle(8, l->r.y1 + r + TopPaddingPx - DocOffset, r - 1); } } bool DocMatch = Doc->IsCurrentIp(); { // We have the current IP location it = LTextView3::Line.begin(); int Idx = 1; for (LTextLine *ln = *it; ln; ln = *++it, Idx++) { if (DocMatch && Idx == IdeDoc::CurIpLine) { ln->Back = LColour(L_DEBUG_CURRENT_LINE); } else { ln->Back.Empty(); } } } } void DocEdit::OnMouseClick(LMouse &m) { if (m.Button1()) { if (m.Down()) Doc->GetApp()->SeekHistory(-1); return; } else if (m.Button2()) { if (m.Down()) Doc->GetApp()->SeekHistory(1); return; } if (m.Down()) { if (HasSelection()) { MsClick.x = -100; MsClick.y = -100; } else { MsClick.x = m.x; MsClick.y = m.y; } } else if ( m.x < LeftMarginPx && abs(m.x - MsClick.x) < 5 && abs(m.y - MsClick.y) < 5 ) { // Margin click... work out the line int Y = (VScroll) ? (int)VScroll->Value() : 0; LFont *f = GetFont(); if (!f) return; LCss::Len PaddingTop = GetCss(true)->PaddingTop(); int TopPx = PaddingTop.ToPx(GetClient().Y(), f); int Idx = ((m.y - TopPx) / f->GetHeight()) + Y + 1; if (Idx > 0 && Idx <= LTextView3::Line.Length()) { Doc->OnMarginClick(Idx); } } LTextView3::OnMouseClick(m); } void DocEdit::SetCaret(size_t i, bool Select, bool ForceFullUpdate) { LTextView3::SetCaret(i, Select, ForceFullUpdate); if (IsAttached()) { auto Line = (int)GetLine(); if (Line != CurLine) { Doc->OnLineChange(CurLine = Line); } } } char *DocEdit::TemplateMerge(const char *Template, const char *Name, List *Params) { // Parse template and insert into doc LStringPipe T; for (const char *t = Template; *t; ) { char *e = strstr((char*) t, "<%"); if (e) { // Push text before tag T.Push(t, e-t); char *TagStart = e; e += 2; skipws(e); char *Start = e; while (*e && isalpha(*e)) e++; // Get tag char *Tag = NewStr(Start, e-Start); if (Tag) { // Process tag if (Name && stricmp(Tag, "name") == 0) { T.Push(Name); } else if (Params && stricmp(Tag, "params") == 0) { char *Line = TagStart; while (Line > Template && Line[-1] != '\n') Line--; int i = 0; for (auto p: *Params) { if (i) T.Push(Line, TagStart-Line); T.Push(p); if (i < Params->Length()-1) T.Push("\n"); i++; } } DeleteArray(Tag); } e = strstr(e, "%>"); if (e) { t = e + 2; } else break; } else { T.Push(t); break; } } T.Push("\n"); return T.NewStr(); } bool DocEdit::GetVisible(LStyle &s) { LRect c = GetClient(); auto a = HitText(c.x1, c.y1, false); auto b = HitText(c.x2, c.y2, false); s.Start = a; s.Len = b - a + 1; return true; } bool DocEdit::Pour(LRegion &r) { LRect c = r.Bound(); c.y2 -= EDIT_TRAY_HEIGHT; - printf("%s\n", c.GetStr()); - SetPos(c); return true; } bool DocEdit::Insert(size_t At, const char16 *Data, ssize_t Len) { int Old = PourEnabled ? CountRefreshEdges(At, 0) : 0; bool Status = LTextView3::Insert(At, Data, Len); int New = PourEnabled ? CountRefreshEdges(At, Len) : 0; if (Old != New) Invalidate(); return Status; } bool DocEdit::Delete(size_t At, ssize_t Len) { int Old = CountRefreshEdges(At, Len); bool Status = LTextView3::Delete(At, Len); int New = CountRefreshEdges(At, 0); if (Old != New) Invalidate(); return Status; } bool DocEdit::OnKey(LKey &k) { #ifdef MAC if (k.Ctrl()) #else if (k.Alt()) #endif { // This allows the Alt+Left/Right to be processed by the prev/next navigator menu. if (k.vkey == LK_LEFT || k.vkey == LK_RIGHT) { return false; } } if (k.AltCmd()) { if (ToLower(k.c16) == 'm') { if (k.Down()) Doc->GotoSearch(IDC_METHOD_SEARCH); return true; } else if (ToLower(k.c16) == 'o' && k.Shift()) { if (k.Down()) Doc->GotoSearch(IDC_FILE_SEARCH); return true; } } return LTextView3::OnKey(k); } LMessage::Result DocEdit::OnEvent(LMessage *m) { switch (m->Msg()) { case M_STYLING_DONE: OnApplyStyles(); break; } return LTextView3::OnEvent(m); } bool DocEdit::OnMenu(LDocView *View, int Id, void *Context) { if (View) { switch (Id) { case IDM_FILE_COMMENT: { const char *Template = Doc->GetProject()->GetFileComment(); if (Template) { auto File = strrchr(Doc->GetFileName(), DIR_CHAR); if (File) { auto Comment = TemplateMerge(Template, File + 1, 0); if (Comment) { char16 *C16 = Utf8ToWide(Comment); DeleteArray(Comment); if (C16) { Insert(Cursor, C16, StrlenW(C16)); DeleteArray(C16); Invalidate(); } } } } break; } case IDM_FUNC_COMMENT: { const char *Template = Doc->GetProject()->GetFunctionComment(); if (ValidStr(Template)) { const char16 *n = NameW(); if (n) { List Tokens; char16 *s; char16 *p = (char16*)n + GetCaret(); char16 OpenBrac[] = { '(', 0 }; char16 CloseBrac[] = { ')', 0 }; ssize_t OpenBracketIndex = -1; // Parse from cursor to the end of the function defn while ((s = LexCpp(p, LexStrdup))) { if (StricmpW(s, OpenBrac) == 0) { OpenBracketIndex = Tokens.Length(); } Tokens.Insert(s); if (StricmpW(s, CloseBrac) == 0) { break; } } if (OpenBracketIndex > 0) { char *FuncName = WideToUtf8(Tokens[OpenBracketIndex-1]); if (FuncName) { // Get a list of parameter names List Params; for (auto i = OpenBracketIndex+1; (p = Tokens[i]); i++) { char16 Comma[] = { ',', 0 }; if (StricmpW(p, Comma) == 0 || StricmpW(p, CloseBrac) == 0) { char16 *Param = Tokens[i-1]; if (Param) { Params.Insert(WideToUtf8(Param)); } } } // Do insertion char *Comment = TemplateMerge(Template, FuncName, &Params); if (Comment) { char16 *C16 = Utf8ToWide(Comment); DeleteArray(Comment); if (C16) { Insert(Cursor, C16, StrlenW(C16)); DeleteArray(C16); Invalidate(); } } // Clean up DeleteArray(FuncName); Params.DeleteArrays(); } else LgiTrace("%s:%i - No function name.\n", _FL); } else LgiTrace("%s:%i - OpenBracketIndex not found.\n", _FL); Tokens.DeleteArrays(); } else LgiTrace("%s:%i - No input text.\n", _FL); } else LgiTrace("%s:%i - No template.\n", _FL); break; } } } return true; } diff --git a/Lgi.xml b/Lgi.xml --- a/Lgi.xml +++ b/Lgi.xml @@ -1,596 +1,599 @@ - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - + + 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/private/common/ViewPriv.h b/private/common/ViewPriv.h --- a/private/common/ViewPriv.h +++ b/private/common/ViewPriv.h @@ -1,246 +1,247 @@ // Private LView definations #pragma once #if WINNATIVE #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x501 #undef _WIN32_WINNT #define _WIN32_WINNT 0x501 #endif #include "commctrl.h" #include "Uxtheme.h" #define GViewFlags d->WndStyle #else #define GViewFlags WndFlags #endif #if defined(__GTK_H__) struct LCaptureThread : public LThread, public LCancel { int view = -1; public: constexpr static int EventMs = 150; LCaptureThread(LView *v); ~LCaptureThread(); int Main(); }; #elif defined(MAC) extern OsThread LgiThreadInPaint; #elif defined(HAIKU) #include extern void *IsAttached(BView *v); #endif #define PAINT_VIRTUAL_CHILDREN 1 extern bool In_SetWindowPos; extern LMouse &lgi_adjust_click(LMouse &Info, LViewI *Wnd, bool Capturing = false, bool Debug = false); #ifdef __GTK_H__ extern LPoint GtkAbsPos(Gtk::GtkWidget *w); extern LRect GtkGetPos(Gtk::GtkWidget *w); #endif #if !WINNATIVE #include "lgi/common/ThreadEvent.h" class LPulseThread : public LThread, public LCancel { LView *View = NULL; LString ViewClass; int Length = 0; LThreadEvent Event; uint64_t WarnTs = 0; LString MakeName(LView *v, const char *Type) { LString s; s.Printf("LPulseThread.%s.%s", v->GetClass(), Type); return s; } public: LPulseThread(LView *view, int len) : View(view), LThread(MakeName(view, "Thread")), Event(MakeName(view, "Event")) { LAssert(View); Length = len; ViewClass = View->GetClass(); Run(); } ~LPulseThread() { Cancel(); View = NULL; Event.Signal(); while (!IsExited()) LSleep(1); } int Main() { while (!IsCancelled() && LAppInst) { auto s = Event.Wait(Length); if (IsCancelled() || s == LThreadEvent::WaitError) break; if (View) { auto r = View->PostEvent(M_PULSE, 0, 0, 50/*milliseconds*/); #if 0 if (!r) { auto now = LCurrentTime(); if (now - WarnTs >= 5000) { WarnTs = now; printf("%s:%i - PulseThread::PostEvent failed for %p/%s.\n", _FL, View, ViewClass.Get()); } } #endif } } return 0; } }; #endif enum LViewFontType { /// The LView has a pointer to an externally owned font. GV_FontPtr, /// The LView owns the font object, and must free it. GV_FontOwned, /// The LApp's font cache owns the object. In this case, /// calling GetCssStyle on the LView will invalidate the /// font ptr causing it to be re-calculated. GV_FontCached, }; class LViewPrivate { public: // General LView *View = NULL; // Owning view LDragDropSource *DropSource = NULL; LDragDropTarget *DropTarget = NULL; bool IsThemed = false; int CtrlId = -1; int WantsPulse = -1; // Hierarchy LViewI *ParentI = NULL; LView *Parent = NULL; LViewI *Notify = NULL; // Size LPoint MinimumSize; // Font LFont *Font = NULL; LViewFontType FontOwnType = GV_FontPtr; // Style LAutoPtr Css; bool CssDirty = false; // This is set when 'Styles' changes, the next call to GetCss(...) parses // the styles into the 'Css' object. LString Styles; // Somewhat temporary object to store unparsed styles particular to // this view until runtime, where the view heirarchy is known. LString::Array Classes; // Event dispatch handle int SinkHnd = -1; // OS Specific #if WINNATIVE static OsView hPrevCapture; int WndStyle = 0; // Windows hWnd Style int WndExStyle = 0; // Windows hWnd ExStyle int WndDlgCode = 0; // Windows Dialog Code (WM_GETDLGCODE) LString WndClass; UINT_PTR TimerId = 0; HTHEME hTheme = NULL; #else // Cursor LPulseThread *PulseThread = NULL; LView *Popup = NULL; bool TabStop = false; bool WantsFocus = false; #if defined __GTK_H__ bool InPaint = false; bool GotOnCreate = false; #elif defined(MAC) && !defined(LGI_COCOA) static HIObjectClassRef BaseClass; #endif #endif #if defined(__GTK_H__) LCaptureThread *CaptureThread = NULL; LMouse PrevMouse; #elif defined(MAC) #ifdef LGI_COCOA LString ClassName; bool AttachEvent; #elif defined LGI_CARBON EventHandlerRef DndHandler; LAutoString AcceptedDropFormat; #endif #elif defined(LGI_SDL) SDL_TimerID PulseId; int PulseLength = -1; #elif defined(HAIKU) BView *Hnd = NULL; + LArray MsgQue; // For before the window is attached... #endif LViewPrivate(LView *view); ~LViewPrivate(); LView *GetParent() { if (Parent) return Parent; if (ParentI) return ParentI->GetGView(); return 0; } }; diff --git a/src/common/Lgi/ViewCommon.cpp b/src/common/Lgi/ViewCommon.cpp --- a/src/common/Lgi/ViewCommon.cpp +++ b/src/common/Lgi/ViewCommon.cpp @@ -1,2773 +1,2686 @@ /// \file /// \author Matthew Allen #ifdef LINUX #include #endif #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/TableLayout.h" #include "lgi/common/Button.h" #include "lgi/common/Css.h" #include "lgi/common/LgiRes.h" #include "lgi/common/EventTargetThread.h" #include "lgi/common/Popup.h" #include "lgi/common/CssTools.h" #include "ViewPriv.h" #if 0 #define DEBUG_CAPTURE(...) printf(__VA_ARGS__) #else #define DEBUG_CAPTURE(...) #endif ////////////////////////////////////////////////////////////////////////////////////// // Helper LPoint lgi_view_offset(LViewI *v, bool Debug = false) { LPoint Offset; for (LViewI *p = v; p; p = p->GetParent()) { if (dynamic_cast(p)) break; LRect pos = p->GetPos(); LRect cli = p->GetClient(false); if (Debug) { const char *cls = p->GetClass(); LgiTrace(" Off[%s] += %i,%i = %i,%i (%s)\n", cls, pos.x1, pos.y1, Offset.x + pos.x1, Offset.y + pos.y1, cli.GetStr()); } Offset.x += pos.x1 + cli.x1; Offset.y += pos.y1 + cli.y1; } return Offset; } LMouse &lgi_adjust_click(LMouse &Info, LViewI *Wnd, bool Capturing, bool Debug) { static LMouse Temp; Temp = Info; if (Wnd) { if (Debug #if 0 || Capturing #endif ) LgiTrace("AdjustClick Target=%s -> Wnd=%s, Info=%i,%i\n", Info.Target?Info.Target->GetClass():"", Wnd?Wnd->GetClass():"", Info.x, Info.y); if (Temp.Target != Wnd) { if (Temp.Target) { auto *TargetWnd = Temp.Target->GetWindow(); auto *WndWnd = Wnd->GetWindow(); if (TargetWnd == WndWnd) { LPoint TargetOff = lgi_view_offset(Temp.Target, Debug); if (Debug) LgiTrace(" WndOffset:\n"); LPoint WndOffset = lgi_view_offset(Wnd, Debug); Temp.x += TargetOff.x - WndOffset.x; Temp.y += TargetOff.y - WndOffset.y; #if 0 LRect c = Wnd->GetClient(false); Temp.x -= c.x1; Temp.y -= c.y1; if (Debug) LgiTrace(" CliOff -= %i,%i\n", c.x1, c.y1); #endif Temp.Target = Wnd; } } else { LPoint Offset; Temp.Target = Wnd; if (Wnd->WindowVirtualOffset(&Offset)) { LRect c = Wnd->GetClient(false); Temp.x -= Offset.x + c.x1; Temp.y -= Offset.y + c.y1; } } } } LAssert(Temp.Target != NULL); return Temp; } ////////////////////////////////////////////////////////////////////////////////////// // LView class methods LViewI *LView::_Capturing = 0; LViewI *LView::_Over = 0; #if LGI_VIEW_HASH struct ViewTbl : public LMutex { typedef LHashTbl, int> T; private: T Map; public: ViewTbl() : Map(2000), LMutex("ViewTbl") { } T *Lock() { if (!LMutex::Lock(_FL)) return NULL; return ⤅ } } ViewTblInst; bool LView::LockHandler(LViewI *v, LView::LockOp Op) { ViewTbl::T *m = ViewTblInst.Lock(); if (!m) return false; int Ref = m->Find(v); bool Status = false; switch (Op) { case OpCreate: { if (Ref == 0) Status = m->Add(v, 1); else LAssert(!"Already exists?"); break; } case OpDelete: { if (Ref == 1) Status = m->Delete(v); else LAssert(!"Either locked or missing."); break; } case OpExists: { Status = Ref > 0; break; } } ViewTblInst.Unlock(); return Status; } #endif LView::LView(OsView view) { #ifdef _DEBUG _Debug = false; #endif d = new LViewPrivate(this); #ifdef LGI_SDL _View = this; #elif LGI_VIEW_HANDLE && !defined(HAIKU) _View = view; #endif Pos.ZOff(-1, -1); WndFlags = GWF_VISIBLE; #ifndef LGI_VIEW_HASH #error "LGI_VIEW_HASH needs to be defined" #elif LGI_VIEW_HASH LockHandler(this, OpCreate); // printf("Adding %p to hash\n", (LViewI*)this); #endif } LView::~LView() { if (d->SinkHnd >= 0) { LEventSinkMap::Dispatch.RemoveSink(this); d->SinkHnd = -1; } #if LGI_VIEW_HASH LockHandler(this, OpDelete); #endif for (unsigned i=0; iOwner == this) pu->Owner = NULL; } _Delete(); // printf("%p::~LView delete %p th=%u\n", this, d, GetCurrentThreadId()); DeleteObj(d); // printf("%p::~LView\n", this); } int LView::AddDispatch() { if (d->SinkHnd < 0) d->SinkHnd = LEventSinkMap::Dispatch.AddSink(this); return d->SinkHnd; } LString LView::CssStyles(const char *Set) { if (Set) { d->Styles = Set; d->CssDirty = true; } return d->Styles; } LString::Array *LView::CssClasses() { return &d->Classes; } LArray LView::IterateViews() { LArray a; for (auto c: Children) a.Add(c); return a; } bool LView::AddView(LViewI *v, int Where) { LAssert(!Children.HasItem(v)); bool Add = Children.Insert(v, Where); if (Add) { LView *gv = v->GetGView(); if (gv && gv->_Window != _Window) { LAssert(!_InLock); gv->_Window = _Window; } v->SetParent(this); v->OnAttach(); OnChildrenChanged(v, true); } return Add; } bool LView::DelView(LViewI *v) { bool Has = Children.HasItem(v); bool b = Children.Delete(v); if (Has) OnChildrenChanged(v, false); Has = Children.HasItem(v); LAssert(!Has); return b; } bool LView::HasView(LViewI *v) { return Children.HasItem(v); } OsWindow LView::WindowHandle() { auto w = GetWindow(); auto h = w ? w->WindowHandle() : NULL; return h; } LWindow *LView::GetWindow() { if (!_Window) { // Walk up parent list and find someone who has a window auto *w = d->GetParent(); for (; w; w = w->d ? w->d->GetParent() : NULL) { if (w->_Window) { LAssert(!_InLock); _Window = w->_Window; break; } } } return dynamic_cast(_Window); } bool LView::Lock(const char *file, int line, int TimeOut) { #ifdef HAIKU if (!d || !d->Hnd) { // printf("%s:%i - no handle %p %p\n", _FL, d, d ? d->Hnd : NULL); return false; } if (d->Hnd->Parent() == NULL) { // printf("%s:%p - Lock() no parent.\n", GetClass(), this); return true; } if (TimeOut >= 0) { auto r = d->Hnd->LockLooperWithTimeout(TimeOut * 1000); if (r == B_OK) { _InLock++; // printf("%s:%p - Lock() cnt=%i par=%p.\n", GetClass(), this, _InLock, d->Hnd->Parent()); return true; } printf("%s:%i - Lock(%i) failed with %x.\n", _FL, TimeOut, r); return false; } auto r = d->Hnd->LockLooper(); if (r) { _InLock++; // printf("%s:%p - Lock() cnt=%i par=%p.\n", GetClass(), this, _InLock, d->Hnd->Parent()); return true; } printf("%s:%i - Lock(%s:%i) failed.\n", _FL, file, line); return false; #else if (!_Window) GetWindow(); _InLock++; // LgiTrace("%s::%p Lock._InLock=%i %s:%i\n", GetClass(), this, _InLock, file, line); if (_Window && _Window->_Lock) { if (TimeOut < 0) { return _Window->_Lock->Lock(file, line); } else { return _Window->_Lock->LockWithTimeout(TimeOut, file, line); } } return true; #endif } void LView::Unlock() { #ifdef HAIKU if (!d || !d->Hnd) { printf("%s:%i - Unlock() error, no hnd.\n", _FL); return; } if (!d->Hnd->Parent()) { // printf("%s:%p - Unlock() no parent.\n", GetClass(), this); return; } if (_InLock > 0) { // printf("%s:%p - Calling UnlockLooper: %i.\n", GetClass(), this, _InLock); d->Hnd->UnlockLooper(); _InLock--; // printf("%s:%p - UnlockLooper done: %i.\n", GetClass(), this, _InLock); } else { printf("%s:%i - Unlock() without lock.\n", _FL); } #else if (_Window && _Window->_Lock) { _Window->_Lock->Unlock(); } _InLock--; // LgiTrace("%s::%p Unlock._InLock=%i\n", GetClass(), this, _InLock); #endif } void LView::OnMouseClick(LMouse &m) { } void LView::OnMouseEnter(LMouse &m) { } void LView::OnMouseExit(LMouse &m) { } void LView::OnMouseMove(LMouse &m) { } bool LView::OnMouseWheel(double Lines) { return false; } bool LView::OnKey(LKey &k) { return false; } void LView::OnAttach() { List::I it = Children.begin(); for (LViewI *v = *it; v; v = *++it) { if (!v->GetParent()) v->SetParent(this); } #if 0 // defined __GTK_H__ if (_View && !DropTarget()) { // If one of our parents is drop capable we need to set a dest here LViewI *p; for (p = GetParent(); p; p = p->GetParent()) { if (p->DropTarget()) { break; } } if (p) { Gtk::gtk_drag_dest_set( _View, (Gtk::GtkDestDefaults)0, NULL, 0, Gtk::GDK_ACTION_DEFAULT); // printf("%s:%i - Drop dest for '%s'\n", _FL, GetClass()); } else { Gtk::gtk_drag_dest_unset(_View); // printf("%s:%i - Not setting drop dest '%s'\n", _FL, GetClass()); } } #endif } void LView::OnCreate() { } void LView::OnDestroy() { } void LView::OnFocus(bool f) { // printf("%s::OnFocus(%i)\n", GetClass(), f); } void LView::OnPulse() { } void LView::OnPosChange() { } bool LView::OnRequestClose(bool OsShuttingDown) { return true; } int LView::OnHitTest(int x, int y) { return -1; } void LView::OnChildrenChanged(LViewI *Wnd, bool Attaching) { } void LView::OnPaint(LSurface *pDC) { auto c = GetClient(); LCssTools Tools(this); Tools.PaintContent(pDC, c); } int LView::OnNotify(LViewI *Ctrl, LNotification Data) { if (!Ctrl) return 0; if (Ctrl == (LViewI*)this && Data.Type == LNotifyActivate) { // Default activation is to focus the current control. Focus(true); } else if (d && d->Parent) { // default behaviour is just to pass the // notification up to the parent // FIXME: eventually we need to call the 'LNotification' parent fn... return d->Parent->OnNotify(Ctrl, Data); } return 0; } int LView::OnCommand(int Cmd, int Event, OsView Wnd) { return 0; } void LView::OnNcPaint(LSurface *pDC, LRect &r) { int Border = Sunken() || Raised() ? _BorderSize : 0; if (Border == 2) { LEdge e; if (Sunken()) e = Focus() ? EdgeWin7FocusSunken : DefaultSunkenEdge; else e = DefaultRaisedEdge; #if WINNATIVE if (d->IsThemed) DrawThemeBorder(pDC, r); else #endif LWideBorder(pDC, r, e); } else if (Border == 1) { LThinBorder(pDC, r, Sunken() ? DefaultSunkenEdge : DefaultRaisedEdge); } } #if LGI_COCOA || defined(__GTK_H__) /* uint64 nPaint = 0; uint64 PaintTime = 0; */ void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { /* uint64 StartTs = Update ? LCurrentTime() : 0; d->InPaint = true; */ // Create temp DC if needed... LAutoPtr Local; if (!pDC) { if (!Local.Reset(new LScreenDC(this))) return; pDC = Local; } #if 0 // This is useful for coverage checking pDC->Colour(LColour(255, 0, 255)); pDC->Rectangle(); #endif // Non-Client drawing LRect r; if (Offset) { r = Pos; r.Offset(Offset); } else { r = GetClient().ZeroTranslate(); } pDC->SetClient(&r); LRect zr1 = r.ZeroTranslate(), zr2 = zr1; OnNcPaint(pDC, zr1); pDC->SetClient(NULL); if (zr2 != zr1) { r.x1 -= zr2.x1 - zr1.x1; r.y1 -= zr2.y1 - zr1.y1; r.x2 -= zr2.x2 - zr1.x2; r.y2 -= zr2.y2 - zr1.y2; } LPoint o(r.x1, r.y1); // Origin of client // Paint this view's contents... pDC->SetClient(&r); #if 0 if (_Debug) { #if defined(__GTK_H__) Gtk::cairo_matrix_t matrix; cairo_get_matrix(pDC->Handle(), &matrix); double ex[4]; cairo_clip_extents(pDC->Handle(), ex+0, ex+1, ex+2, ex+3); ex[0] += matrix.x0; ex[1] += matrix.y0; ex[2] += matrix.x0; ex[3] += matrix.y0; LgiTrace("%s::_Paint, r=%s, clip=%g,%g,%g,%g - %g,%g\n", GetClass(), r.GetStr(), ex[0], ex[1], ex[2], ex[3], matrix.x0, matrix.y0); #elif LGI_COCOA auto Ctx = pDC->Handle(); CGAffineTransform t = CGContextGetCTM(Ctx); LRect cr = CGContextGetClipBoundingBox(Ctx); printf("%s::_Paint() pos=%s transform=%g,%g,%g,%g-%g,%g clip=%s r=%s\n", GetClass(), GetPos().GetStr(), t.a, t.b, t.c, t.d, t.tx, t.ty, cr.GetStr(), r.GetStr()); #endif } #endif OnPaint(pDC); pDC->SetClient(NULL); // Paint all the children... for (auto i : Children) { LView *w = i->GetGView(); if (w && w->Visible()) { if (!w->Pos.Valid()) continue; #if 0 if (w->_Debug) LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), o.x, o.y); #endif w->_Paint(pDC, &o); } } } #else void LView::_Paint(LSurface *pDC, LPoint *Offset, LRect *Update) { // Create temp DC if needed... LAutoPtr Local; if (!pDC) { Local.Reset(new LScreenDC(this)); pDC = Local; } if (!pDC) { printf("%s:%i - No context to draw in.\n", _FL); return; } #if 0 // This is useful for coverage checking pDC->Colour(LColour(255, 0, 255)); pDC->Rectangle(); #endif bool HasClient = false; LRect r(0, 0, Pos.X()-1, Pos.Y()-1), Client; LPoint o; if (Offset) o = *Offset; #if WINNATIVE if (!_View) #endif { // Non-Client drawing Client = r; OnNcPaint(pDC, Client); HasClient = GetParent() && (Client != r); if (HasClient) { Client.Offset(o.x, o.y); pDC->SetClient(&Client); } } r.Offset(o.x, o.y); // Paint this view's contents if (Update) { LRect OldClip = pDC->ClipRgn(); pDC->ClipRgn(Update); OnPaint(pDC); pDC->ClipRgn(OldClip.Valid() ? &OldClip : NULL); } else { OnPaint(pDC); } #if PAINT_VIRTUAL_CHILDREN // Paint any virtual children for (auto i : Children) { LView *w = i->GetGView(); if (w && w->Visible()) { #if LGI_VIEW_HANDLE if (!w->Handle()) #endif { LRect p = w->GetPos(); p.Offset(o.x, o.y); if (HasClient) p.Offset(Client.x1 - r.x1, Client.y1 - r.y1); LPoint co(p.x1, p.y1); // LgiTrace("%s::_Paint %i,%i\n", w->GetClass(), p.x1, p.y1); pDC->SetClient(&p); w->_Paint(pDC, &co); pDC->SetClient(NULL); } } } #endif if (HasClient) pDC->SetClient(0); } #endif LViewI *LView::GetParent() { ThreadCheck(); return d ? d->Parent : NULL; } void LView::SetParent(LViewI *p) { ThreadCheck(); d->Parent = p ? p->GetGView() : NULL; d->ParentI = p; } void LView::SendNotify(LNotification note) { LViewI *n = d->Notify ? d->Notify : d->Parent; if (n) { if ( #if LGI_VIEW_HANDLE && !defined(HAIKU) !_View || #endif InThread()) { n->OnNotify(this, note); } else { // We are not in the same thread as the target window. So we post a message // across to the view. if (GetId() <= 0) { // We are going to generate a control Id to help the receiver of the // M_CHANGE message find out view later on. The reason for doing this // instead of sending a pointer to the object, is that the object // _could_ be deleted between the message being sent and being received. // Which would result in an invalid memory access on that object. LViewI *p = GetWindow(); if (!p) { // No window? Find the top most parent we can... p = this; while (p->GetParent()) p = p->GetParent(); } if (p) { // Give the control a valid ID int i; for (i=10; i<1000; i++) { if (!p->FindControl(i)) { printf("Giving the ctrl '%s' the id '%i' for SendNotify\n", GetClass(), i); SetId(i); break; } } } else { // Ok this is really bad... go random (better than nothing) SetId(5000 + LRand(2000)); } } LAssert(GetId() > 0); // We must have a valid ctrl ID at this point, otherwise // the receiver will never be able to find our object. // printf("Post M_CHANGE %i %i\n", GetId(), Data); n->PostEvent(M_CHANGE, (LMessage::Param) GetId(), (LMessage::Param) new LNotification(note)); } } } LViewI *LView::GetNotify() { ThreadCheck(); return d->Notify; } void LView::SetNotify(LViewI *p) { ThreadCheck(); d->Notify = p; } #define ADJ_LEFT 1 #define ADJ_RIGHT 2 #define ADJ_UP 3 #define ADJ_DOWN 4 int IsAdjacent(LRect &a, LRect &b) { if ( (a.x1 == b.x2 + 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_LEFT; } if ( (a.x2 == b.x1 - 1) && !(a.y1 > b.y2 || a.y2 < b.y1)) { return ADJ_RIGHT; } if ( (a.y1 == b.y2 + 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_UP; } if ( (a.y2 == b.y1 - 1) && !(a.x1 > b.x2 || a.x2 < b.x1)) { return ADJ_DOWN; } return 0; } LRect JoinAdjacent(LRect &a, LRect &b, int Adj) { LRect t; switch (Adj) { case ADJ_LEFT: case ADJ_RIGHT: { t.y1 = MAX(a.y1, b.y1); t.y2 = MIN(a.y2, b.y2); t.x1 = MIN(a.x1, b.x1); t.x2 = MAX(a.x2, b.x2); break; } case ADJ_UP: case ADJ_DOWN: { t.y1 = MIN(a.y1, b.y1); t.y2 = MAX(a.y2, b.y2); t.x1 = MAX(a.x1, b.x1); t.x2 = MIN(a.x2, b.x2); break; } } return t; } LRect *LView::FindLargest(LRegion &r) { ThreadCheck(); int Pixels = 0; LRect *Best = 0; static LRect Final; // do initial run through the list to find largest single region for (LRect *i = r.First(); i; i = r.Next()) { int Pix = i->X() * i->Y(); if (Pix > Pixels) { Pixels = Pix; Best = i; } } if (Best) { Final = *Best; Pixels = Final.X() * Final.Y(); int LastPixels = Pixels; LRect LastRgn = Final; int ThisPixels = Pixels; LRect ThisRgn = Final; LRegion TempList; for (LRect *i = r.First(); i; i = r.Next()) { TempList.Union(i); } TempList.Subtract(Best); do { LastPixels = ThisPixels; LastRgn = ThisRgn; // search for adjoining rectangles that maybe we can add for (LRect *i = TempList.First(); i; i = TempList.Next()) { int Adj = IsAdjacent(ThisRgn, *i); if (Adj) { LRect t = JoinAdjacent(ThisRgn, *i, Adj); int Pix = t.X() * t.Y(); if (Pix > ThisPixels) { ThisPixels = Pix; ThisRgn = t; TempList.Subtract(i); } } } } while (LastPixels < ThisPixels); Final = ThisRgn; } else return 0; return &Final; } LRect *LView::FindSmallestFit(LRegion &r, int Sx, int Sy) { ThreadCheck(); int X = 1000000; int Y = 1000000; LRect *Best = 0; for (LRect *i = r.First(); i; i = r.Next()) { if ((i->X() >= Sx && i->Y() >= Sy) && (i->X() < X || i->Y() < Y)) { X = i->X(); Y = i->Y(); Best = i; } } return Best; } LRect *LView::FindLargestEdge(LRegion &r, int Edge) { LRect *Best = 0; ThreadCheck(); for (LRect *i = r.First(); i; i = r.Next()) { if (!Best) { Best = i; } if ( ((Edge & GV_EDGE_TOP) && (i->y1 < Best->y1)) || ((Edge & GV_EDGE_RIGHT) && (i->x2 > Best->x2)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 > Best->y2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 < Best->x1)) ) { Best = i; } if ( ( ((Edge & GV_EDGE_TOP) && (i->y1 == Best->y1)) || ((Edge & GV_EDGE_BOTTOM) && (i->y2 == Best->y2)) ) && ( i->X() > Best->X() ) ) { Best = i; } if ( ( ((Edge & GV_EDGE_RIGHT) && (i->x2 == Best->x2)) || ((Edge & GV_EDGE_LEFT) && (i->x1 == Best->x1)) ) && ( i->Y() > Best->Y() ) ) { Best = i; } } return Best; } LViewI *LView::FindReal(LPoint *Offset) { ThreadCheck(); if (Offset) { Offset->x = 0; Offset->y = 0; } #if !LGI_VIEW_HANDLE LViewI *w = GetWindow(); #endif LViewI *p = d->Parent; while (p && #if !LGI_VIEW_HANDLE p != w #else !p->Handle() #endif ) { if (Offset) { Offset->x += Pos.x1; Offset->y += Pos.y1; } p = p->GetParent(); } if (p && #if !LGI_VIEW_HANDLE p == w #else p->Handle() #endif ) { return p; } return NULL; } bool LView::HandleCapture(LView *Wnd, bool c) { ThreadCheck(); DEBUG_CAPTURE("%s::HandleCapture(%i)=%i\n", GetClass(), c, (int)(_Capturing == Wnd)); if (c) { if (_Capturing == Wnd) { DEBUG_CAPTURE(" %s already has capture\n", _Capturing?_Capturing->GetClass():0); } else { DEBUG_CAPTURE(" _Capturing=%s -> %s\n", _Capturing?_Capturing->GetClass():0, Wnd?Wnd->GetClass():0); _Capturing = Wnd; #if defined(__GTK_H__) if (d->CaptureThread) d->CaptureThread->Cancel(); d->CaptureThread = new LCaptureThread(this); #elif WINNATIVE LPoint Offset; LViewI *v = _Capturing->Handle() ? _Capturing : FindReal(&Offset); HWND h = v ? v->Handle() : NULL; if (h) SetCapture(h); else LAssert(0); #elif defined(LGI_SDL) #if SDL_VERSION_ATLEAST(2, 0, 4) SDL_CaptureMouse(SDL_TRUE); #else LAppInst->CaptureMouse(true); #endif #endif } } else if (_Capturing) { DEBUG_CAPTURE(" _Capturing=%s -> NULL\n", _Capturing?_Capturing->GetClass():0); _Capturing = NULL; #if defined(__GTK_H__) if (d->CaptureThread) { d->CaptureThread->Cancel(); d->CaptureThread = NULL; // It'll delete itself... } #elif WINNATIVE ReleaseCapture(); #elif defined(LGI_SDL) #if SDL_VERSION_ATLEAST(2, 0, 4) SDL_CaptureMouse(SDL_FALSE); #else LAppInst->CaptureMouse(false); #endif #endif } return true; } bool LView::IsCapturing() { // DEBUG_CAPTURE("%s::IsCapturing()=%p==%p==%i\n", GetClass(), _Capturing, this, (int)(_Capturing == this)); return _Capturing == this; } bool LView::Capture(bool c) { ThreadCheck(); DEBUG_CAPTURE("%s::Capture(%i)\n", GetClass(), c); return HandleCapture(this, c); } bool LView::Enabled() { ThreadCheck(); #if WINNATIVE if (_View) return IsWindowEnabled(_View) != 0; #endif return !TestFlag(GViewFlags, GWF_DISABLED); } void LView::Enabled(bool i) { ThreadCheck(); if (!i) SetFlag(GViewFlags, GWF_DISABLED); else ClearFlag(GViewFlags, GWF_DISABLED); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) { #if WINNATIVE EnableWindow(_View, i); #elif defined LGI_CARBON if (i) { OSStatus e = EnableControl(_View); if (e) printf("%s:%i - Error enabling control (%i)\n", _FL, (int)e); } else { OSStatus e = DisableControl(_View); if (e) printf("%s:%i - Error disabling control (%i)\n", _FL, (int)e); } #endif } #endif Invalidate(); } bool LView::Visible() { // This is a read only operation... which is kinda thread-safe... // ThreadCheck(); #if WINNATIVE if (_View) /* This takes into account all the parent windows as well... Which is kinda not what I want. I want this to reflect just this window. return IsWindowVisible(_View); */ return (GetWindowLong(_View, GWL_STYLE) & WS_VISIBLE) != 0; #endif return TestFlag(GViewFlags, GWF_VISIBLE); } void LView::Visible(bool v) { ThreadCheck(); if (v) SetFlag(GViewFlags, GWF_VISIBLE); else ClearFlag(GViewFlags, GWF_VISIBLE); #if defined(HAIKU) LLocker lck(d->Hnd, _FL); if (!IsAttached() || lck.Lock()) { const int attempts = 3; // printf("%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden()); if (v) { bool parentHidden = false; for (auto p = d->Hnd->Parent(); p; p = p->Parent()) { if (p->IsHidden()) { parentHidden = true; break; } } if (!parentHidden) // Don't try and show if one of the parent's is hidden. { for (int i=0; iHnd->IsHidden(); i++) { // printf("\t%p Show\n", this); d->Hnd->Show(); } if (d->Hnd->IsHidden()) { printf("%s:%i - Failed to show %s.\n", _FL, GetClass()); for (auto p = d->Hnd->Parent(); p; p = p->Parent()) printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden()); } } } else { for (int i=0; iHnd->IsHidden(); i++) { // printf("\t%p Hide\n", this); d->Hnd->Hide(); } if (!d->Hnd->IsHidden()) { printf("%s:%i - Failed to hide %s.\n", _FL, GetClass()); for (auto p = d->Hnd->Parent(); p; p = p->Parent()) printf("\tparent: %s/%p ishidden=%i\n", p->Name(), p, p->IsHidden()); } } // printf("\t%s/%p:Visible(%i) hidden=%i\n", GetClass(), this, v, d->Hnd->IsHidden()); } else LgiTrace("%s:%i - Can't lock.\n", _FL); #elif LGI_VIEW_HANDLE if (_View) { #if WINNATIVE ShowWindow(_View, (v) ? SW_SHOWNORMAL : SW_HIDE); #elif LGI_COCOA LAutoPool Pool; [_View.p setHidden:!v]; #elif LGI_CARBON Boolean is = HIViewIsVisible(_View); if (v != is) { OSErr e = HIViewSetVisible(_View, v); if (e) printf("%s:%i - HIViewSetVisible(%p,%i) failed with %i (class=%s)\n", _FL, _View, v, e, GetClass()); } #endif } else #endif { Invalidate(); } } bool LView::Focus() { ThreadCheck(); bool Has = false; #if defined(__GTK_H__) LWindow *w = GetWindow(); if (w) { bool Active = w->IsActive(); if (Active) Has = w->GetFocus() == static_cast(this); } #elif defined(HAIKU) LLocker lck(d->Hnd, _FL); if (lck.Lock()) { Has = d->Hnd->IsFocus(); lck.Unlock(); } #elif defined(WINNATIVE) if (_View) { HWND hFocus = GetFocus(); Has = hFocus == _View; } #elif LGI_COCOA Has = TestFlag(WndFlags, GWF_FOCUS); #elif LGI_CARBON LWindow *w = GetWindow(); if (w) { ControlRef Cur; OSErr e = GetKeyboardFocus(w->WindowHandle(), &Cur); if (e) LgiTrace("%s:%i - GetKeyboardFocus failed with %i\n", _FL, e); else Has = (Cur == _View); } #endif #if !LGI_CARBON if (Has) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); #endif return Has; } void LView::Focus(bool i) { ThreadCheck(); if (i) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); auto *Wnd = GetWindow(); if (Wnd) Wnd->SetFocus(this, i ? LWindow::GainFocus : LWindow::LoseFocus); #if LGI_VIEW_HANDLE && !defined(HAIKU) if (_View) #endif { #if defined(HAIKU) _Focus(i); #elif defined(LGI_SDL) || defined(__GTK_H__) // Nop: Focus is all handled by Lgi's LWindow class. #elif WINNATIVE if (i) { HWND hCur = GetFocus(); if (hCur != _View) { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus %p (%-30s)\n", _FL, Handle(), Name()); } SetFocus(_View); } } else { if (In_SetWindowPos) { assert(0); LgiTrace("%s:%i - SetFocus(%p)\n", _FL, GetDesktopWindow()); } SetFocus(GetDesktopWindow()); } #elif defined LGI_CARBON LViewI *Wnd = GetWindow(); if (Wnd && i) { OSErr e = SetKeyboardFocus(Wnd->WindowHandle(), _View, 1); if (e) { // e = SetKeyboardFocus(Wnd->WindowHandle(), _View, kControlFocusNextPart); // if (e) { HIViewRef p = HIViewGetSuperview(_View); // errCouldntSetFocus printf("%s:%i - SetKeyboardFocus failed: %i (%s, %p)\n", _FL, e, GetClass(), p); } } // else printf("%s:%i - SetFocus v=%p(%s)\n", _FL, _View, GetClass()); } else printf("%s:%i - no window?\n", _FL); #endif } } LDragDropSource *LView::DropSource(LDragDropSource *Set) { if (Set) d->DropSource = Set; return d->DropSource; } LDragDropTarget *LView::DropTarget(LDragDropTarget *Set) { if (Set) d->DropTarget = Set; return d->DropTarget; } #if defined LGI_CARBON extern pascal OSStatus LgiViewDndHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData); #endif #if defined __GTK_H__ // Recursively add drag dest to all view and all children bool GtkAddDragDest(LViewI *v, bool IsTarget) { if (!v) return false; LWindow *w = v->GetWindow(); if (!w) return false; auto wid = GtkCast(w->WindowHandle(), gtk_widget, GtkWidget); if (IsTarget) { Gtk::gtk_drag_dest_set( wid, (Gtk::GtkDestDefaults)0, NULL, 0, Gtk::GDK_ACTION_DEFAULT); } else { Gtk::gtk_drag_dest_unset(wid); } for (LViewI *c: v->IterateViews()) GtkAddDragDest(c, IsTarget); return true; } #endif bool LView::DropTarget(bool t) { ThreadCheck(); bool Status = false; if (t) SetFlag(GViewFlags, GWF_DROP_TARGET); else ClearFlag(GViewFlags, GWF_DROP_TARGET); #if WINNATIVE if (_View) { if (t) { if (!d->DropTarget) DragAcceptFiles(_View, t); else Status = RegisterDragDrop(_View, (IDropTarget*) d->DropTarget) == S_OK; } else { if (_View && d->DropTarget) Status = RevokeDragDrop(_View) == S_OK; } } #elif defined MAC && !defined(LGI_SDL) LWindow *Wnd = dynamic_cast(GetWindow()); if (Wnd) { Wnd->SetDragHandlers(t); if (!d->DropTarget) d->DropTarget = t ? Wnd : 0; } #if LGI_COCOA LWindow *w = GetWindow(); if (w) { OsWindow h = w->WindowHandle(); if (h) { NSMutableArray *a = [[NSMutableArray alloc] init]; if (a) { [a addObject:(NSString*)kUTTypeItem]; for (id item in NSFilePromiseReceiver.readableDraggedTypes) [a addObject:item]; [h.p.contentView registerForDraggedTypes:a]; [a release]; } } } #elif LGI_CARBON if (t) { static EventTypeSpec DragEvents[] = { { kEventClassControl, kEventControlDragEnter }, { kEventClassControl, kEventControlDragWithin }, { kEventClassControl, kEventControlDragLeave }, { kEventClassControl, kEventControlDragReceive }, }; if (!d->DndHandler) { OSStatus e = ::InstallControlEventHandler( _View, NewEventHandlerUPP(LgiViewDndHandler), GetEventTypeCount(DragEvents), DragEvents, (void*)this, &d->DndHandler); if (e) LgiTrace("%s:%i - InstallEventHandler failed (%i)\n", _FL, e); } SetControlDragTrackingEnabled(_View, true); } else { SetControlDragTrackingEnabled(_View, false); } #endif #elif defined __GTK_H__ Status = GtkAddDragDest(this, t); if (Status && !d->DropTarget) d->DropTarget = t ? GetWindow() : 0; #endif return Status; } bool LView::Sunken() { // ThreadCheck(); #if WINNATIVE return TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE); #else return TestFlag(GViewFlags, GWF_SUNKEN); #endif } void LView::Sunken(bool i) { ThreadCheck(); #if WINNATIVE if (i) SetFlag(d->WndExStyle, WS_EX_CLIENTEDGE); else ClearFlag(d->WndExStyle, WS_EX_CLIENTEDGE); if (_View) SetWindowLong(_View, GWL_EXSTYLE, d->WndExStyle); #else if (i) SetFlag(GViewFlags, GWF_SUNKEN); else ClearFlag(GViewFlags, GWF_SUNKEN); #endif if (i) { if (!_BorderSize) _BorderSize = 2; } else _BorderSize = 0; } bool LView::Flat() { // ThreadCheck(); #if WINNATIVE return !TestFlag(d->WndExStyle, WS_EX_CLIENTEDGE) && !TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return !TestFlag(GViewFlags, GWF_SUNKEN) && !TestFlag(GViewFlags, GWF_RAISED); #endif } void LView::Flat(bool i) { ThreadCheck(); #if WINNATIVE ClearFlag(d->WndExStyle, (WS_EX_CLIENTEDGE|WS_EX_WINDOWEDGE)); #else ClearFlag(GViewFlags, (GWF_RAISED|GWF_SUNKEN)); #endif } bool LView::Raised() { // ThreadCheck(); #if WINNATIVE return TestFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else return TestFlag(GViewFlags, GWF_RAISED); #endif } void LView::Raised(bool i) { ThreadCheck(); #if WINNATIVE if (i) SetFlag(d->WndExStyle, WS_EX_WINDOWEDGE); else ClearFlag(d->WndExStyle, WS_EX_WINDOWEDGE); #else if (i) SetFlag(GViewFlags, GWF_RAISED); else ClearFlag(GViewFlags, GWF_RAISED); #endif if (i) { if (!!_BorderSize) _BorderSize = 2; } else _BorderSize = 0; } int LView::GetId() { // This is needed by SendNotify function which is thread safe. // So no thread safety check here. return d->CtrlId; } void LView::SetId(int i) { // This is needed by SendNotify function which is thread safe. // So no thread safety check here. d->CtrlId = i; #if WINNATIVE if (_View) SetWindowLong(_View, GWL_ID, d->CtrlId); #elif defined __GTK_H__ #elif defined MAC #endif } bool LView::GetTabStop() { ThreadCheck(); #if WINNATIVE return TestFlag(d->WndStyle, WS_TABSTOP); #else return d->TabStop; #endif } void LView::SetTabStop(bool b) { ThreadCheck(); #if WINNATIVE if (b) SetFlag(d->WndStyle, WS_TABSTOP); else ClearFlag(d->WndStyle, WS_TABSTOP); #else d->TabStop = b; #endif } int64 LView::GetCtrlValue(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Value() : 0; } void LView::SetCtrlValue(int Id, int64 i) { ThreadCheck(); LViewI *w = FindControl(Id); if (w) w->Value(i); } const char *LView::GetCtrlName(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Name() : 0; } void LView::SetCtrlName(int Id, const char *s) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Name(s); } else { PostEvent( M_SET_CTRL_NAME, (LMessage::Param)Id, (LMessage::Param)new LString(s)); } } bool LView::GetCtrlEnabled(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); return (w) ? w->Enabled() : 0; } void LView::SetCtrlEnabled(int Id, bool Enabled) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Enabled(Enabled); } else { PostEvent( M_SET_CTRL_ENABLE, (LMessage::Param)Id, (LMessage::Param)Enabled); } } bool LView::GetCtrlVisible(int Id) { ThreadCheck(); LViewI *w = FindControl(Id); if (!w) LgiTrace("%s:%i - Ctrl %i not found.\n", _FL, Id); return (w) ? w->Visible() : 0; } void LView::SetCtrlVisible(int Id, bool v) { if (!IsAttached() || InThread()) { if (auto w = FindControl(Id)) w->Visible(v); } else { PostEvent( M_SET_CTRL_VISIBLE, (LMessage::Param)Id, (LMessage::Param)v); } } bool LView::AttachChildren() { for (auto c: Children) { bool a = c->IsAttached(); if (!a) { if (!c->Attach(this)) { LgiTrace("%s:%i - failed to attach %s\n", _FL, c->GetClass()); return false; } } } return true; } LFont *LView::GetFont() { if (!d->Font && d->Css && LResources::GetLoadStyles()) { LFontCache *fc = LAppInst->GetFontCache(); if (fc) { LFont *f = fc->GetFont(d->Css); if (f) { if (d->FontOwnType == GV_FontOwned) DeleteObj(d->Font); d->Font = f; d->FontOwnType = GV_FontCached; } } } return d->Font ? d->Font : LSysFont; } void LView::SetFont(LFont *Font, bool OwnIt) { bool Change = d->Font != Font; if (Change) { if (d->FontOwnType == GV_FontOwned) { LAssert(d->Font != LSysFont); DeleteObj(d->Font); } d->FontOwnType = OwnIt ? GV_FontOwned : GV_FontPtr; d->Font = Font; #if WINNATIVE if (_View) SendMessage(_View, WM_SETFONT, (WPARAM) (Font ? Font->Handle() : 0), 0); #endif for (LViewI *p = GetParent(); p; p = p->GetParent()) { LTableLayout *Tl = dynamic_cast(p); if (Tl) { Tl->InvalidateLayout(); break; } } Invalidate(); } } bool LView::IsOver(LMouse &m) { return (m.x >= 0) && (m.y >= 0) && (m.x < Pos.X()) && (m.y < Pos.Y()); } bool LView::WindowVirtualOffset(LPoint *Offset) { bool Status = false; if (Offset) { Offset->x = 0; Offset->y = 0; for (LViewI *Wnd = this; Wnd; Wnd = Wnd->GetParent()) { #if !LGI_VIEW_HANDLE auto IsWnd = dynamic_cast(Wnd); if (!IsWnd) #else if (!Wnd->Handle()) #endif { LRect r = Wnd->GetPos(); LViewI *Par = Wnd->GetParent(); if (Par) { LRect c = Par->GetClient(false); Offset->x += r.x1 + c.x1; Offset->y += r.y1 + c.y1; } else { Offset->x += r.x1; Offset->y += r.y1; } Status = true; } else break; } } return Status; } LString _ViewDesc(LViewI *v) { LString s; s.Printf("%s/%s/%i", v->GetClass(), v->Name(), v->GetId()); return s; } LViewI *LView::WindowFromPoint(int x, int y, int DebugDepth) { char Tabs[64]; if (DebugDepth) { memset(Tabs, 9, DebugDepth); Tabs[DebugDepth] = 0; LgiTrace("%s%s %i\n", Tabs, _ViewDesc(this).Get(), Children.Length()); } // We iterate over the child in reverse order because if they overlap the // end of the list is on "top". So they should get the click or whatever // before the the lower windows. auto it = Children.rbegin(); int n = (int)Children.Length() - 1; for (LViewI *c = *it; c; c = *--it) { LRect CPos = c->GetPos(); if (CPos.Overlap(x, y) && c->Visible()) { LRect CClient; CClient = c->GetClient(false); int Ox = CPos.x1 + CClient.x1; int Oy = CPos.y1 + CClient.y1; if (DebugDepth) { LgiTrace("%s[%i] %s Pos=%s Client=%s m(%i,%i)->(%i,%i)\n", Tabs, n--, _ViewDesc(c).Get(), CPos.GetStr(), CClient.GetStr(), x, y, x - Ox, y - Oy); } LViewI *Child = c->WindowFromPoint(x - Ox, y - Oy, DebugDepth ? DebugDepth + 1 : 0); if (Child) return Child; } else if (DebugDepth) { LgiTrace("%s[%i] MISSED %s Pos=%s m(%i,%i)\n", Tabs, n--, _ViewDesc(c).Get(), CPos.GetStr(), x, y); } } if (x >= 0 && y >= 0 && x < Pos.X() && y < Pos.Y()) { return this; } return NULL; } LColour LView::StyleColour(int CssPropType, LColour Default, int Depth) { LColour c = Default; if ((CssPropType >> 8) == LCss::TypeColor) { LViewI *v = this; for (int i=0; v && iGetParent()) { auto Style = v->GetCss(); if (Style) { auto Colour = (LCss::ColorDef*) Style->PropAddress((LCss::PropType)CssPropType); if (Colour) { if (Colour->Type == LCss::ColorRgb) { c.Set(Colour->Rgb32, 32); break; } else if (Colour->Type == LCss::ColorTransparent) { c.Empty(); break; } } } if (dynamic_cast(v) || dynamic_cast(v)) break; } } return c; } bool LView::InThread() { #if WINNATIVE HWND Hnd = _View; for (LViewI *p = GetParent(); p && !Hnd; p = p->GetParent()) { Hnd = p->Handle(); } auto CurThreadId = GetCurrentThreadId(); auto GuiThreadId = LAppInst->GetGuiThreadId(); DWORD ViewThread = Hnd ? GetWindowThreadProcessId(Hnd, NULL) : GuiThreadId; return CurThreadId == ViewThread; #elif defined(HAIKU) return true; #else OsThreadId Me = GetCurrentThreadId(); OsThreadId Gui = LAppInst ? LAppInst->GetGuiThreadId() : 0; #if 0 if (Gui != Me) LgiTrace("%s:%i - Out of thread:" #ifdef LGI_COCOA "%llx, %llx" #else "%x, %x" #endif "\n", _FL, Gui, Me); #endif return Gui == Me; #endif } bool LView::PostEvent(int Cmd, LMessage::Param a, LMessage::Param b, int64_t timeoutMs) { #ifdef LGI_SDL + return LPostEvent(this, Cmd, a, b); + #elif defined(HAIKU) + if (!d || !d->Hnd) { // printf("%s:%i - Bad pointers %p %p\n", _FL, d, d ? d->Hnd : NULL); return false; } - auto start = LCurrentTime(); - bool locked = false; - BView *lockView = NULL; - BWindow *lockWindow = NULL; + BWindow *bWnd = NULL; LWindow *wnd = dynamic_cast(this); if (wnd) { - lockWindow = wnd->WindowHandle(); - if (!lockWindow) - { - printf("%s:%i - No window to lock (%s)\n", _FL, GetClass()); - return false; - } - - do - { - int64_t elapsedMs = LCurrentTime() - start; - int64_t remainingMs = timeoutMs >= 0 ? timeoutMs - elapsedMs : 2000; - - auto r = lockWindow->LockWithTimeout(remainingMs * 1000); - if (r == B_OK) - { - locked = true; - break; - } - else if (r == B_BAD_VALUE) - { - printf("%s:%i - Lock destroyed.\n", _FL); - return false; - } - else - { - if (timeoutMs >= 0) - return false; - - printf("%s waiting on lock for %gs (r=%x, me=%i, locker=%i)\n", - GetClass(), - (double)(LCurrentTime()-start)/1000.0, - r, - GetCurrentThreadId(), - lockWindow ? lockWindow->LockingThread() : 0); - } - } - while (true); + bWnd = wnd->WindowHandle(); } else { // Look for an attached view to lock... for (LViewI *v = this; v; v = v->GetParent()) { auto vhnd = v->Handle(); if (vhnd && ::IsAttached(vhnd)) { - lockView = vhnd; + bWnd = vhnd->Window(); break; } } - - // Try and lock the looper... - if (!lockView) - { - #if 0 - auto wnd = lockView ? lockView->Window() : NULL; - auto par = lockView ? lockView->Parent() : NULL; - printf("%s:%i - Failed to locklooper: %p %i %p %p cls=%s\n", - _FL, lockView, locked, wnd, par, GetClass()); - #endif - return false; - } - - lockWindow = lockView->Window(); - - do - { - int64_t elapsedMs = LCurrentTime() - start; - int64_t remainingMs = timeoutMs >= 0 ? timeoutMs - elapsedMs : 2000; - - auto r = lockView->LockLooperWithTimeout(remainingMs * 1000); - if (r == B_OK) - { - locked = true; - break; - } - else if (r == B_BAD_VALUE) - { - printf("%s:%i - Lock destroyed.\n", _FL); - return false; - } - else - { - if (timeoutMs >= 0) - return false; - - printf("%s waiting on lock for %gs (r=%x, me=%i, locker=%i)\n", - GetClass(), - (double)(LCurrentTime()-start)/1000.0, - r, - GetCurrentThreadId(), - lockWindow ? lockWindow->LockingThread() : 0); - } - } - while (true); } - BMessage *m = new BMessage(Cmd); - if (!m) - { - printf("%s:%i - alloc failed.\n", _FL); - return false; - } + BMessage m(Cmd); - auto r = m->AddInt64(LMessage::PropA, a); - if (r != B_OK) printf("%s:%i - AddUInt64 failed.\n", _FL); - r = m->AddInt64(LMessage::PropB, b); - if (r != B_OK) printf("%s:%i - AddUInt64 failed.\n", _FL); - if (lockView != d->Hnd) + auto r = m.AddInt64(LMessage::PropA, a); + if (r != B_OK) + printf("%s:%i - AddUInt64 failed.\n", _FL); + r = m.AddInt64(LMessage::PropB, b); + if (r != B_OK) + printf("%s:%i - AddUInt64 failed.\n", _FL); + r = m.AddPointer(LMessage::PropView, this); + if (r != B_OK) + printf("%s:%i - AddPointer failed.\n", _FL); + + if (bWnd) { - r = m->AddPointer(LMessage::PropView, this); + r = bWnd->PostMessage(&m); if (r != B_OK) - printf("%s:%i - AddPointer failed.\n", _FL); + printf("%s:%i - PostMessage failed.\n", _FL); + + return r == B_OK; } - if (lockView) - { - r = d->Hnd->Window()->PostMessage(m, lockView); - if (r != B_OK) - printf("%s:%i - PostMessage failed.\n", _FL); - } - else if (lockWindow) - { - r = lockWindow->PostMessage(m); - if (r != B_OK) - printf("%s:%i - PostMessage failed.\n", _FL); - } - else - { - r = B_ERROR; - printf("%s:%i - No window?\n", _FL); - } + // Not attached yet... + d->MsgQue.Add(new BMessage(m)); + // printf("%s:%i - PostEvent.MsgQue=%i\n", _FL, (int)d->MsgQue.Length()); + + return true; - if (lockView) - lockView->UnlockLooper(); - else if (lockWindow) - lockWindow->UnlockLooper(); + #elif WINNATIVE - 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) || defined(WINDOWS) template void _Dump(int Depth, T v) { LString Sp, Name; Sp.Length(Depth<<1); memset(Sp.Get(), ' ', Depth<<1); Sp.Get()[Depth<<1] = 0; #if defined(HAIKU) LRect Frame = v->Frame(); Name = v->Name(); bool IsHidden = v->IsHidden(); #else RECT rc; GetWindowRect(v, &rc); LRect Frame = rc; wchar_t txt[256]; GetWindowTextW(v, txt, CountOf(txt)); Name = txt; bool IsHidden = !(GetWindowLong(v, GWL_STYLE) & WS_VISIBLE); #endif LgiTrace("%s%p::%s frame=%s vis=%i\n", Sp.Get(), v, Name.Get(), Frame.GetStr(), !IsHidden); #if defined(HAIKU) for (int32 i=0; iCountChildren(); i++) _Dump(Depth + 1, v->ChildAt(i)); #else for (auto h = GetWindow(v, GW_CHILD); h; h = GetWindow(h, GW_HWNDNEXT)) _Dump(Depth + 1, h); #endif } #endif void LView::_Dump(int Depth) { char Sp[65] = {0}; memset(Sp, ' ', Depth*2); #if 0 char s[256]; sprintf_s(s, sizeof(s), "%s%p::%s %s (_View=%p)\n", Sp, this, GetClass(), GetPos().GetStr(), _View); LgiTrace(s); List::I i = Children.Start(); for (LViewI *c = *i; c; c = *++i) { LView *v = c->GetGView(); if (v) v->_Dump(Depth+1); } #elif defined(LGI_CARBON) DumpHiview(_View); #elif defined(__GTK_H__) // DumpGtk(_View); #else #if defined(HAIKU) LLocker lck(WindowHandle(), _FL); if (lck.Lock()) #endif ::_Dump(0, WindowHandle()); #endif } #endif //////////////////////////////////////////////////////////////////////////////////////////////////// static LArray *AllFactories = NULL; #if defined(WIN32) static HANDLE FactoryEvent; #else static pthread_once_t FactoryOnce = PTHREAD_ONCE_INIT; static void GFactoryInitFactories() { AllFactories = new LArray; } #endif LViewFactory::LViewFactory() { #if defined(WIN32) char16 Name[64]; swprintf_s(Name, CountOf(Name), L"LgiFactoryEvent.%i", GetCurrentProcessId()); HANDLE h = CreateEventW(NULL, false, false, Name); DWORD err = GetLastError(); if (err != ERROR_ALREADY_EXISTS) { FactoryEvent = h; AllFactories = new LArray; } else { LAssert(AllFactories != NULL); } #else pthread_once(&FactoryOnce, GFactoryInitFactories); #endif if (AllFactories) AllFactories->Add(this); } LViewFactory::~LViewFactory() { if (AllFactories) { AllFactories->Delete(this); if (AllFactories->Length() == 0) { DeleteObj(AllFactories); #if defined(WIN32) CloseHandle(FactoryEvent); #endif } } } LView *LViewFactory::Create(const char *Class, LRect *Pos, const char *Text) { if (ValidStr(Class) && AllFactories) { for (int i=0; iLength(); i++) { LView *v = (*AllFactories)[i]->NewView(Class, Pos, Text); if (v) { return v; } } } return 0; } #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/common/Widgets/ScrollBar.cpp b/src/common/Widgets/ScrollBar.cpp --- a/src/common/Widgets/ScrollBar.cpp +++ b/src/common/Widgets/ScrollBar.cpp @@ -1,741 +1,733 @@ #include "lgi/common/Lgi.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/LgiRes.h" #define DrawBorder(dc, r, edge) LThinBorder(dc, r, edge) #if defined(LGI_CARBON) #define MAC_SKIN 1 #elif defined(LGI_COCOA) #define MAC_LOOK 1 #else #define WINXP_LOOK 1 #endif enum ScrollZone { BTN_NONE, BTN_SUB, BTN_SLIDE, BTN_ADD, BTN_PAGE_SUB, BTN_PAGE_ADD, }; class LScrollBarPrivate { public: LScrollBar *Widget; bool Vertical; int64 Value, Min, Max, Page; LRect Sub, Add, Slide, PageSub, PageAdd; int Clicked; bool Over; int SlideOffset; int Ignore; LScrollBarPrivate(LScrollBar *w) { Ignore = 0; Widget = w; Vertical = true; Value = Min = 0; Max = -1; Page = 1; Clicked = BTN_NONE; Over = false; } bool IsVertical() { return Vertical; } int IsOver() { return Over ? Clicked : BTN_NONE; } void DrawIcon(LSurface *pDC, LRect &r, bool Add, LSystemColour c) { pDC->Colour(c); int IconSize = MAX(r.X(), r.Y()) * 2 / 6; int Cx = r.x1 + (r.X() >> 1); int Cy = r.y1 + (r.Y() >> 1); int Off = (IconSize >> 1) * (Add ? 1 : -1); int x = Cx + (IsVertical() ? 0 : Off); int y = Cy + (IsVertical() ? Off : 0); if (Add) { if (IsOver() == BTN_ADD) { x++; y++; } if (IsVertical()) { // down for (int i=0; iLine(x-i, y, x+i, y); - } } else { // right for (int i=0; iLine(x, y-i, x, y+i); - } } } else { if (IsOver() == BTN_SUB) { x++; y++; } if (IsVertical()) { // up for (int i=0; iLine(x-i, y, x+i, y); - } } else { // left for (int i=0; iLine(x, y-i, x, y+i); - } } } } void OnPaint(LSurface *pDC) { LColour SlideCol(L_MED); SlideCol.Rgb( (255 + SlideCol.r()) >> 1, (255 + SlideCol.g()) >> 1, (255 + SlideCol.b()) >> 1); #if MAC_LOOK || MAC_SKIN pDC->Colour(SlideCol); pDC->Rectangle(); if (IsValid()) { LRect r = Slide; r.Inset(3, 3); pDC->Colour(L_LOW); // pDC->Rectangle(&r); double rad = (IsVertical() ? (double)r.X() : (double)r.Y()) / 2; double cx = (double)r.x1 + rad; pDC->FilledArc(cx, r.y1 + rad, rad, 0, 180); pDC->Rectangle(r.x1, r.y1 + rad, r.x2, r.y2 - rad); pDC->FilledArc(cx, r.y2 - rad, rad, 180, 0); } #elif WINXP_LOOK // left/up button LRect r = Sub; DrawBorder(pDC, r, IsOver() == BTN_SUB ? DefaultSunkenEdge : DefaultRaisedEdge); pDC->Colour(L_MED); pDC->Rectangle(&r); DrawIcon(pDC, r, false, IsValid() ? L_BLACK : L_LOW); // right/down r = Add; DrawBorder(pDC, r, IsOver() == BTN_ADD ? DefaultSunkenEdge : DefaultRaisedEdge); pDC->Colour(L_MED); pDC->Rectangle(&r); DrawIcon(pDC, r, true, IsValid() ? L_BLACK : L_LOW); // printf("Paint %ix%i, %s\n", pDC->X(), pDC->Y(), Widget->GetPos().GetStr()); if (IsValid()) { // slide space pDC->Colour(SlideCol); pDC->Rectangle(&PageSub); pDC->Rectangle(&PageAdd); // slide button r = Slide; DrawBorder(pDC, r, DefaultRaisedEdge); // IsOver() == BTN_SLIDE ? SUNKEN : RAISED); pDC->Colour(L_MED); if (r.Valid()) pDC->Rectangle(&r); } else { pDC->Colour(SlideCol); pDC->Rectangle(&Slide); } #else #error "No look and feel defined." #endif } int GetWidth() { return IsVertical() ? Widget->X() : Widget->Y(); } int GetLength() { return (IsVertical() ? Widget->Y() : Widget->X()) #if !MAC_LOOK - (GetWidth() * 2) #endif ; } int64 GetRange() { return Max >= Min ? Max - Min + 1 : 0; } bool IsValid() { return Max >= Min; } void CalcRegions() { LRect r = Widget->GetPos(); Vertical = r.Y() > r.X(); int w = GetWidth(); int len = GetLength(); // Button sizes #if MAC_LOOK Sub.ZOff(-1, -1); Add.ZOff(-1, -1); #else Sub.ZOff(w-1, w-1); Add.ZOff(w-1, w-1); // Button positions if (IsVertical()) Add.Offset(0, Widget->GetPos().Y()-w); else Add.Offset(Widget->GetPos().X()-w, 0); #endif // Slider int64 Start, End; #if LGI_SDL int MinSize = w; // Touch UI needs large slide.... #else int MinSize = 18; #endif // printf("Calc %i, " LPrintfInt64 ", " LPrintfInt64 "\n", IsValid(), Min, Max); if (IsValid()) { int64 Range = GetRange(); int64 Size = Range ? MIN((int)Page, Range) * len / Range : len; if (Size < MinSize) Size = MinSize; Start = Range > Page ? Value * (len - Size) / (Range - (int)Page) : 0; End = Start + Size; /* printf("Range=%i Page=%i Size=%i Start=%i End=%i\n", (int)Range, (int)Page, (int)Size, (int)Start, (int)End); */ if (IsVertical()) { Slide.ZOff(w-1, (int) (End-Start-1)); #if MAC_LOOK Slide.Offset(0, (int) (r.y1+Start)); #else Slide.Offset(0, (int) (Sub.y2+1+Start)); #endif if (Start > 1) { PageSub.x1 = Slide.x1; #if MAC_LOOK PageSub.y1 = 0; #else PageSub.y1 = Sub.y2 + 1; #endif PageSub.x2 = Slide.x2; PageSub.y2 = Slide.y1 - 1; } else { PageSub.ZOff(-1, -1); } if (End < Add.y1 - 2) { PageAdd.x1 = Slide.x1; PageAdd.x2 = Slide.x2; PageAdd.y1 = Slide.y2 + 1; #if MAC_LOOK PageAdd.y2 = r.Y()-1; #else PageAdd.y2 = Add.y1 - 1; #endif } else { PageAdd.ZOff(-1, -1); } } else { Slide.ZOff((int)(End-Start-1), w-1); Slide.Offset((int)(Sub.x2+1+Start), 0); if (Start > 1) { PageSub.y1 = Slide.y1; #if MAC_LOOK PageSub.x1 = 0; #else PageSub.x1 = Sub.x2 + 1; #endif PageSub.y2 = Slide.y2; PageSub.x2 = Slide.x1 - 1; } else { PageSub.ZOff(-1, -1); } if (End < Add.x1 - 2) { PageAdd.y1 = Slide.y1; PageAdd.y2 = Slide.y2; PageAdd.x1 = Slide.x2 + 1; #if MAC_LOOK PageAdd.x2 = r.X() - 1; #else PageAdd.x2 = Add.x1 - 1; #endif } else { PageAdd.ZOff(-1, -1); } } } else { PageAdd.ZOff(-1, -1); PageSub.ZOff(-1, -1); Slide = Widget->GetClient(); if (IsVertical()) { Slide.Inset(0, Sub.y2 + 1); } else { Slide.Inset(Sub.x2 + 1, 0); } } } int OnHit(int x, int y) { #if MAC_SKIN HIThemeTrackDrawInfo Info; LRect Client = Widget->GetClient(); HIRect Rc = Client; Info.version = 0; Info.kind = kThemeScrollBarMedium; Info.bounds = Rc; Info.min = Min; Info.max = Max - Page; Info.value = Value; Info.reserved = 0; Info.attributes = (Widget->Vertical() ? 0 : kThemeTrackHorizontal) | (Widget->Focus() ? kThemeTrackHasFocus : 0) | kThemeTrackShowThumb; Info.enableState = Widget->Enabled() ? kThemeTrackActive : kThemeTrackDisabled; Info.filler1 = 0; Info.trackInfo.scrollbar.viewsize = Page; Info.trackInfo.scrollbar.pressState = false; HIPoint pt = {(CGFloat)x, (CGFloat)y}; ControlPartCode part; Boolean b = HIThemeHitTestTrack(&Info, &pt, &part); if (b) { switch (part) { case kAppearancePartUpButton: return BTN_SUB; case kAppearancePartDownButton: return BTN_ADD; case 129: return BTN_SLIDE; case kControlPageUpPart: return BTN_PAGE_SUB; case kControlPageDownPart: return BTN_PAGE_ADD; default: printf("%s:%i - Unknown scroll bar hittest value: %i\n", _FL, part); break; } } #else if (Sub.Overlap(x, y)) return BTN_SUB; if (Slide.Overlap(x, y)) return BTN_SLIDE; if (Add.Overlap(x, y)) return BTN_ADD; if (PageSub.Overlap(x, y)) return BTN_PAGE_SUB; if (PageAdd.Overlap(x, y)) return BTN_PAGE_ADD; #endif return BTN_NONE; } int OnClick(int Btn, int x, int y) { if (IsValid()) { switch (Btn) { case BTN_SUB: { SetValue(Value-1); break; } case BTN_ADD: { SetValue(Value+1); break; } case BTN_PAGE_SUB: { SetValue(Value-Page); break; } case BTN_PAGE_ADD: { SetValue(Value+Page); break; } case BTN_SLIDE: { SlideOffset = IsVertical() ? y - Slide.y1 : x - Slide.x1; break; } } } return false; } void SetValue(int64 i) { if (i < Min) { i = Min; } if (IsValid() && i > Max - Page + 1) { i = MAX(Min, Max - Page + 1); } if (Value != i) { Value = i; CalcRegions(); Widget->Invalidate(); LViewI *n = Widget->GetNotify() ? Widget->GetNotify() : Widget->GetParent(); if (n) n->OnNotify(Widget, LNotifyItemChange); } } }; ///////////////////////////////////////////////////////////////////////////////////// LScrollBar::LScrollBar() : ResObject(Res_ScrollBar) { d = new LScrollBarPrivate(this); } LScrollBar::LScrollBar(int id, int x, int y, int cx, int cy, const char *name) : ResObject(Res_ScrollBar) { d = new LScrollBarPrivate(this); SetId(id); if (name) Name(name); if (cx > cy) { SetVertical(false); } LResources::StyleElement(this); } LScrollBar::~LScrollBar() { DeleteObj(d); } bool LScrollBar::Valid() { return d->Max > d->Min; } int LScrollBar::SCROLL_BAR_SIZE = 0; int LScrollBar::GetScrollSize() { if (!SCROLL_BAR_SIZE) SCROLL_BAR_SIZE = std::max(15, LScreenDpi().x / 6); return SCROLL_BAR_SIZE; } bool LScrollBar::Attach(LViewI *p) { bool Status = LControl::Attach(p); #if 0 printf("%p::Attach scroll bar to %s, Status=%i, Vis=%i\n", this, p->GetClass(), Status, Visible()); #endif return Status; } void LScrollBar::OnPaint(LSurface *pDC) { #if MAC_SKIN #if 0 pDC->Colour(LColour(255, 0, 255)); pDC->Rectangle(); #endif HIThemeTrackDrawInfo Info; LRect Client = GetClient(); HIRect Rc = Client; Info.version = 0; Info.kind = kThemeScrollBarMedium; Info.bounds = Rc; Info.min = d->Min; Info.max = d->Max - d->Page + 1; Info.value = d->Value; Info.reserved = 0; Info.attributes = (Vertical() ? 0 : kThemeTrackHorizontal) | (Focus() ? kThemeTrackHasFocus : 0) | kThemeTrackShowThumb; Info.enableState = Enabled() ? kThemeTrackActive : kThemeTrackDisabled; Info.filler1 = 0; Info.trackInfo.scrollbar.viewsize = d->Page; Info.trackInfo.scrollbar.pressState = false; CGContextRef Cr = pDC->Handle(); OSStatus e = HIThemeDrawTrack(&Info, NULL, Cr, kHIThemeOrientationNormal); if (e) printf("%s:%i - HIThemeDrawTrack failed with %li\n", _FL, e); #else d->OnPaint(pDC); #endif } void LScrollBar::OnPosChange() { d->CalcRegions(); } void LScrollBar::OnMouseClick(LMouse &m) { if (d->Max >= d->Min) { int Hit = d->OnHit(m.x, m.y); Capture(m.Down()); if (m.Down()) { if (Hit != d->Clicked) { d->Clicked = Hit; d->Over = true; Invalidate(); d->OnClick(Hit, m.x, m.y); if (Hit != BTN_SLIDE) { d->Ignore = 2; SetPulse(50); } } } else { if (d->Clicked) { d->Clicked = BTN_NONE; d->Over = false; Invalidate(); } } } } void LScrollBar::OnMouseMove(LMouse &m) { if (IsCapturing()) { if (d->Clicked == BTN_SLIDE) { if (d->GetLength()) { int64 Range = d->GetRange(); int Len = d->GetLength(); int Size = d->IsVertical() ? d->Slide.Y() : d->Slide.X(); int Px = (d->IsVertical() ? m.y : m.x) - d->GetWidth() - d->SlideOffset; int64 Value = Px * (Range - d->Page) / (Len - Size); d->SetValue(Value); } } else { int Hit = d->OnHit(m.x, m.y); bool Over = Hit == d->Clicked; if (Over != d->Over) { d->Over = Over; Invalidate(); } } } } bool LScrollBar::OnKey(LKey &k) { return false; } bool LScrollBar::OnMouseWheel(double Lines) { return false; } bool LScrollBar::Vertical() { return d->Vertical; } void LScrollBar::SetVertical(bool v) { d->Vertical = v; d->CalcRegions(); Invalidate(); } int64 LScrollBar::Value() { return d->Value; } void LScrollBar::Value(int64 v) { d->SetValue(v); } LRange LScrollBar::GetRange() const { return LRange(d->Min, d->Max - d->Min + 1); } void LScrollBar::Limits(int64 &Low, int64 &High) { Low = d->Min; High = d->Max; } bool LScrollBar::SetRange(const LRange &r) { if (d->Min != r.Start || d->Max != r.End() - 1) { d->Min = r.Start; d->Max = r.End() - 1; d->Page = MIN(d->Page, d->GetRange()); d->CalcRegions(); Invalidate(); OnConfigure(); } return true; } void LScrollBar::SetLimits(int64 Low, int64 High) { SetRange(LRange(Low, High-Low+1)); } int64 LScrollBar::Page() { return d->Page; } void LScrollBar::SetPage(int64 i) { if (d->Page != i) { d->Page = MAX(i, 1); d->CalcRegions(); Invalidate(); OnConfigure(); } } LMessage::Result LScrollBar::OnEvent(LMessage *Msg) { return LView::OnEvent(Msg); } void LScrollBar::OnPulse() { if (d->Ignore > 0) { d->Ignore--; } else { LMouse m; if (GetMouse(m)) { int Hit = d->OnHit(m.x, m.y); if (Hit == d->Clicked) { d->OnClick(d->Clicked, m.x, m.y); } } } } diff --git a/src/common/Widgets/Tree.cpp b/src/common/Widgets/Tree.cpp --- a/src/common/Widgets/Tree.cpp +++ b/src/common/Widgets/Tree.cpp @@ -1,2250 +1,2251 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/Tree.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/DisplayString.h" #include "lgi/common/Palette.h" #include "lgi/common/LgiRes.h" #include "lgi/common/CssTools.h" +#include #define TREE_BLOCK 16 #define DRAG_THRESHOLD 4 #define DRAG_SCROLL_EDGE 20 #define DRAG_SCROLL_X 8 #define DRAG_SCROLL_Y 1 #define TreeUpdateNow false #define TREELOCK LMutex::Auto Lck(d, _FL); #define ForAll(Items) for (auto c : Items) ////////////////////////////////////////////////////////////////////////////// // Private class definitions for binary compatibility class LTreePrivate : public LMutex { public: // Private data int LineFlags[4]; bool LayoutDirty; LPoint Limit; LPoint LastClick; LPoint DragStart; int DragData; LMemDC *IconCache; bool InPour; int64 DropSelectTime; int8 IconTextGap; int LastLayoutPx; LMouse *CurrentClick; LTreeItem *ScrollTo; // Visual style LTree::ThumbStyle Btns; bool JoiningLines; // Pointers into items... be careful to clear when deleting items... LTreeItem *LastHit; List Selection; LTreeItem *DropTarget; LTreePrivate() : LMutex("LTreePrivate") { CurrentClick = NULL; LastLayoutPx = -1; DropSelectTime = 0; InPour = false; LastHit = 0; DropTarget = 0; IconCache = 0; LayoutDirty = true; IconTextGap = 0; ScrollTo = NULL; Btns = LTree::TreeTriangle; JoiningLines = false; } ~LTreePrivate() { DeleteObj(IconCache); } }; class LTreeItemPrivate { LArray Ds; LArray ColPx; public: LTreeItem *Item; LRect Pos; LRect Thumb; LRect Text; LRect Icon; bool Open; bool Selected; bool Visible; bool Last; int Depth; LTreeItemPrivate(LTreeItem *it) { Item = it; Ds = NULL; Pos.ZOff(-1, -1); Open = false; Selected = false; Visible = false; Last = false; Depth = 0; Text.ZOff(-1, -1); Icon.ZOff(-1, -1); } ~LTreeItemPrivate() { Ds.DeleteObjects(); } LDisplayString *GetDs(int Col, int FixPx) { if (!Ds[Col]) { LFont *f = Item->GetTree() ? Item->GetTree()->GetFont() : LSysFont; Ds[Col] = new LDisplayString(f, Item->GetText(Col)); if (Ds[Col]) { ColPx[Col] = Ds[Col]->X(); if (FixPx > 0) { Ds[Col]->TruncateWithDots(FixPx); } } } return Ds[Col]; } void ClearDs(int Col = -1) { if (Col >= 0) { delete Ds[Col]; Ds[Col] = NULL; } else { Ds.DeleteObjects(); } } int GetColumnPx(int Col) { int BasePx = 0; GetDs(Col, 0); if (Col == 0) { BasePx = (Depth + 1) * TREE_BLOCK; } return ColPx[Col] + BasePx; } }; ////////////////////////////////////////////////////////////////////////////// LTreeNode::LTreeNode() { Parent = NULL; Tree = NULL; } LTreeNode::~LTreeNode() { } void LTreeNode::SetLayoutDirty() { Tree->d->LayoutDirty = true; } void LTreeNode::_Visible(bool v) { for (LTreeItem *i=GetChild(); i; i=i->GetNext()) { LAssert(i != this); i->OnVisible(v); i->_Visible(v); } } void LTreeNode::_ClearDs(int Col) { List::I it = Items.begin(); for (LTreeItem *c = *it; c; c = *++it) { c->_ClearDs(Col); } } LItemContainer *LTreeItem::GetContainer() { return Tree; } LTreeItem *LTreeNode::Insert(LTreeItem *Obj, ssize_t Idx) { LAssert(Obj != this); if (Obj && Obj->Tree) Obj->Remove(); LTreeItem *NewObj = Obj ? Obj : new LTreeItem; if (NewObj) { NewObj->Parent = Item(); NewObj->_SetTreePtr(Tree); Items.Delete(NewObj); Items.Insert(NewObj, Idx); if (Tree) { Tree->d->LayoutDirty = true; if (Pos() && Pos()->Y() > 0) Tree->_UpdateBelow(Pos()->y1); else Tree->Invalidate(); } } return NewObj; } void LTreeNode::Detach() { if (Parent) { LTreeItem *It = Item(); if (It) { LAssert(Parent->Items.HasItem(It)); Parent->Items.Delete(It); } Parent = 0; } if (Tree) { Tree->d->LayoutDirty = true; Tree->Invalidate(); } if (Item()) Item()->_SetTreePtr(0); } void LTreeNode::Remove() { int y = 0; if (Parent) { LTreeItem *i = Item(); if (i && i->IsRoot()) { LRect *p = Pos(); LTreeItem *Prev = GetPrev(); if (Prev) { y = Prev->d->Pos.y1; } else { y = p->y1; } } else { y = Parent->d->Pos.y1; } } LTree *t = Tree; if (Item()) Item()->_Remove(); if (t) { t->_UpdateBelow(y); } } bool LTreeNode::IsRoot() { return Parent == 0 || (LTreeNode*)Parent == (LTreeNode*)Tree; } size_t LTreeNode::Length() { return Items.Length(); } bool LTreeNode::HasItem(LTreeItem *obj, bool recurse) { if (!obj) return false; if (this == (LTreeNode*)obj) return true; for (auto i: Items) { if (i == obj) return true; if (recurse && i->HasItem(obj, recurse)) return true; } return false; } int LTreeNode::ForEach(std::function Fn) { int Count = 0; for (auto t : Items) { Fn(t); Count += t->ForEach(Fn); } return Count + 1; } ssize_t LTreeNode::IndexOf() { if (Parent) { return Parent->Items.IndexOf(Item()); } else if (Tree) { return Tree->Items.IndexOf(Item()); } return -1; } LTreeItem *LTreeNode::GetChild() { return Items.Length() ? Items[0] : NULL; } LTreeItem *LTreeNode::GetPrev() { List *l = (Parent) ? &Parent->Items : (Tree) ? &Tree->Items : 0; if (l) { ssize_t Index = l->IndexOf(Item()); if (Index >= 0) { return l->ItemAt(Index-1); } } return 0; } LTreeItem *LTreeNode::GetNext() { List *l = (Parent) ? &Parent->Items : (Tree) ? &Tree->Items : 0; if (l) { ssize_t Index = l->IndexOf(Item()); if (Index >= 0) { return l->ItemAt(Index+1); } } return 0; } ////////////////////////////////////////////////////////////////////////////// LTreeItem::LTreeItem() { d = new LTreeItemPrivate(this); } LTreeItem::~LTreeItem() { if (Tree) { if (Tree->d->DropTarget == this) Tree->d->DropTarget = 0; if (Tree->d->LastHit == this) Tree->d->LastHit = 0; if (Tree->IsCapturing()) Tree->Capture(false); } int y = 0; LTree *t = 0; if (Parent && (LTreeNode*)Parent != (LTreeNode*)Tree) { t = Tree; y = Parent->d->Pos.y1; } else if ((LTreeNode*)this != (LTreeNode*)Tree) { t = Tree; LTreeItem *p = GetPrev(); if (p) y = p->d->Pos.y1; else y = d->Pos.y1; } _Remove(); while (Items.Length()) { auto It = Items.begin(); delete *It; } DeleteObj(d); if (t) t->_UpdateBelow(y); } int LTreeItem::GetColumnSize(int Col) { int Px = d->GetColumnPx(Col); if (Expanded()) { ForAll(Items) { int ChildPx = c->GetColumnSize(Col); Px = MAX(ChildPx, Px); } } return Px; } LRect *LTreeItem::Pos() { return &d->Pos; } LPoint LTreeItem::_ScrollPos() { LPoint p; if (Tree) p = Tree->_ScrollPos(); return p; } LRect *LTreeItem::_GetRect(LTreeItemRect Which) { switch (Which) { case TreeItemPos: return &d->Pos; case TreeItemThumb: return &d->Thumb; case TreeItemText: return &d->Text; case TreeItemIcon: return &d->Icon; } return 0; } bool LTreeItem::IsDropTarget() { LTree *t = GetTree(); if (t && t->d && t->d->DropTarget == this) return true; return false; } LRect *LTreeItem::GetPos(int Col) { if (!d->Pos.Valid() && Tree) Tree->_Pour(); static LRect r; r = d->Pos; if (Col >= 0) { LItemColumn *Column = 0; int Cx = Tree->GetImageList() ? 16 : 0; for (int c=0; cColumnAt(c); if (Column) { Cx += Column->Width(); } } Column = Tree->ColumnAt(Col); if (Column) { r.x1 = Cx; r.x2 = Cx + Column->Width() - 1; } } return &r; } void LTreeItem::_RePour() { if (Tree) Tree->_Pour(); } void LTreeItem::ScrollTo() { if (!Tree) return; if (Tree->VScroll) { LRect c = Tree->GetClient(); LRect p = d->Pos; int y = d->Pos.Y() ? d->Pos.Y() : 16; p.Offset(0, (int) (-Tree->VScroll->Value() * y)); if (p.y1 < c.y1) { int Lines = (c.y1 - p.y1 + y - 1) / y; Tree->VScroll->Value(Tree->VScroll->Value() - Lines); } else if (p.y2 > c.y2) { int Lines = (p.y2 - c.y2 + y - 1) / y; Tree->VScroll->Value(Tree->VScroll->Value() + Lines); } } else { Tree->d->ScrollTo = this; if (Tree->IsAttached()) Tree->PostEvent(M_SCROLL_TO); } } void LTreeItem::_SetTreePtr(LTree *t) { if (Tree && !t) { // Clearing tree pointer, must remove all references to this item that // the tree might still have. if (d->Selected) { Tree->d->Selection.Delete(this); d->Selected = false; } if (Tree->d->LastHit == this) { Tree->d->LastHit = 0; } if (Tree->d->DropTarget == this) { Tree->d->DropTarget = 0; } } Tree = t; List::I it = Items.begin(); for (LTreeItem *i=*it; i; i=*++it) { i->_SetTreePtr(t); } } void LTreeItem::_Remove() { if ((LTreeNode*)this != (LTreeNode*)Tree) { if (Parent) { LAssert(Parent->Items.HasItem(this)); Parent->Items.Delete(this); } else if (Tree) { LAssert(Tree->Items.HasItem(this)); Tree->Items.Delete(this); } if (Tree) { LAssert(Tree->d != NULL); Tree->d->LayoutDirty = true; if (Tree->IsCapturing()) Tree->Capture(false); } } Parent = 0; _SetTreePtr(0); } void LTreeItem::_PourText(LPoint &Size) { LFont *f = Tree ? Tree->GetFont() : LSysFont; auto *Txt = GetText(); #if defined(_WIN64) && defined(_DEBUG) if ((void*)Txt == (void*)0xfeeefeeefeeefeee || (void*)Txt == (void*)0xcdcdcdcdcdcdcdcd) { LAssert(!"Yeah nah..."); } #endif LDisplayString ds(f, Txt); Size.x = ds.X() + 4; Size.y = 0; } void LTreeItem::_PaintText(LItem::ItemPaintCtx &Ctx) { const char *Text = GetText(); if (Text) { LDisplayString *Ds = d->GetDs(0, d->Text.X()); LFont *f = Tree ? Tree->GetFont() : LSysFont; int Tab = f->TabSize(); f->TabSize(0); f->Transparent(false); f->Colour(Ctx.Fore, Ctx.TxtBack); if (Ds) { Ds->Draw(Ctx.pDC, d->Text.x1 + 2, d->Text.y1 + 1, &d->Text); if (Ctx.x2 > d->Text.x2) { LRect r = Ctx; r.x1 = d->Text.x2 + 1; Ctx.pDC->Colour(Ctx.Back); Ctx.pDC->Rectangle(&r); } } f->TabSize(Tab); } else { Ctx.pDC->Colour(Ctx.Back); Ctx.pDC->Rectangle(&Ctx); } } void LTreeItem::_Pour(LPoint *Limit, int ColumnPx, int Depth, bool Visible) { auto css = GetCss(false); auto display = css ? css->Display() != LCss::DispNone : true; d->Visible = display && Visible; d->Depth = Depth; if (d->Visible) { LPoint TextSize; _PourText(TextSize); LImageList *ImgLst = Tree->GetImageList(); // int IconX = (ImgLst && GetImage() >= 0) ? ImgLst->TileX() + Tree->d->IconTextGap : 0; int IconY = (ImgLst && GetImage() >= 0) ? ImgLst->TileY() : 0; int Height = MAX(TextSize.y, IconY); if (!Height) Height = 16; LDisplayString *Ds = d->GetDs(0, 0); d->Pos.ZOff(ColumnPx - 1, (Ds ? MAX(Height, Ds->Y()) : Height) - 1); d->Pos.Offset(0, Limit->y); if (!d->Pos.Valid()) { printf("Invalid pos: %s, ColumnPx=%i\n", d->Pos.GetStr(), ColumnPx); } Limit->x = MAX(Limit->x, d->Pos.x2 + 1); Limit->y = MAX(Limit->y, d->Pos.y2 + 1); } else { d->Pos.ZOff(-1, -1); } LTreeItem *n; List::I it = Items.begin(); for (LTreeItem *i=*it; i; i=n) { n = *++it; i->d->Last = n == 0; i->_Pour(Limit, ColumnPx, Depth+1, d->Open && d->Visible); } } void LTreeItem::_ClearDs(int Col) { d->ClearDs(Col); LTreeNode::_ClearDs(Col); } const char *LTreeItem::GetText(int i) { return Str[i]; } bool LTreeItem::SetText(const char *s, int i) { LAutoPtr Lck; if (Tree) Lck.Reset(new LMutex::Auto(Tree->d, -1, _FL)); Str[i] = s; if (Tree) Update(); return true; } int LTreeItem::GetImage(int Flags) { return Sys_Image; } void LTreeItem::SetImage(int i) { Sys_Image = i; } void LTreeItem::Update() { if (Tree) { LRect p = d->Pos; p.x2 = 10000; d->ClearDs(); Tree->_Update(&p, TreeUpdateNow); } } bool LTreeItem::Select() { return d->Selected; } void LTreeItem::Select(bool b) { if (d->Selected != b) { d->Selected = b; if (b) { LTreeItem *p = this; while ((p = p->GetParent())) { p->Expanded(true); } } Update(); if (b && Tree) { Tree->_OnSelect(this); Tree->OnItemSelect(this); } } } bool LTreeItem::Expanded() { return d->Open; } void LTreeItem::Expanded(bool b) { if (d->Open != b) { d->Open = b; if (Items.Length() > 0) { if (Tree) { Tree->d->LayoutDirty = true; Tree->_UpdateBelow(d->Pos.y1); } OnExpand(b); } } } void LTreeItem::OnExpand(bool b) { _Visible(b); } LTreeItem *LTreeItem::_HitTest(int x, int y, bool Debug) { LTreeItem *Status = 0; if (d->Pos.Overlap(x, y) && x > (d->Depth*TREE_BLOCK)) { Status = this; } if (d->Open) { List::I it = Items.begin(); for (LTreeItem *i=*it; i && !Status; i=*++it) { Status = i->_HitTest(x, y, Debug); } } return Status; } void LTreeItem::_MouseClick(LMouse &m) { if (m.Down()) { if ((Items.Length() > 0 && d->Thumb.Overlap(m.x, m.y)) || m.Double()) { Expanded(!Expanded()); } LRect rText = d->Text; if (Tree && Tree->Columns.Length() > 0) rText.x2 = Tree->X(); if (rText.Overlap(m.x, m.y) || d->Icon.Overlap(m.x, m.y)) { Select(true); if (Tree) Tree->OnItemClick(this, m); } } } void LTreeItem::OnPaint(ItemPaintCtx &Ctx) { LAssert(Tree != NULL); if (!d->Visible) return; // background up to text LSurface *&pDC = Ctx.pDC; pDC->Colour(Ctx.Back); pDC->Rectangle(0, d->Pos.y1, (d->Depth*TREE_BLOCK)+TREE_BLOCK, d->Pos.y2); // draw trunk LRect Pos = d->Pos; Pos.x2 = Pos.x1 + Ctx.ColPx[0] - 1; int x = 0; LColour Ws(L_WORKSPACE); LColour Lines = Ws.Invert().Mix(Ws); pDC->Colour(Lines); if (Tree->d->JoiningLines) { for (int i=0; iDepth; i++) { if (Tree->d->LineFlags[0] & (1 << i)) pDC->Line(x + 8, Pos.y1, x + 8, Pos.y2); x += TREE_BLOCK; } } else { x += TREE_BLOCK * d->Depth; } // draw node int cy = Pos.y1 + (Pos.Y() >> 1); if (Items.Length() > 0) { d->Thumb.ZOff(8, 8); d->Thumb.Offset(x + 4, cy - 4); switch (Tree->d->Btns) { case LTree::TreePlus: { // plus/minus symbol pDC->Colour(L_LOW); pDC->Box(&d->Thumb); pDC->Colour(L_WHITE); pDC->Rectangle(d->Thumb.x1+1, d->Thumb.y1+1, d->Thumb.x2-1, d->Thumb.y2-1); pDC->Colour(L_SHADOW); pDC->Line( d->Thumb.x1+2, d->Thumb.y1+4, d->Thumb.x1+6, d->Thumb.y1+4); if (!d->Open) { // not open, so draw the cross bar making the '-' into a '+' pDC->Colour(L_SHADOW); pDC->Line( d->Thumb.x1+4, d->Thumb.y1+2, d->Thumb.x1+4, d->Thumb.y1+6); } break; } case LTree::TreeTriangle: { // Triangle style expander pDC->Colour(Lines); int Off = 2; if (d->Open) { for (int y=0; yThumb.Y(); y++) { int x1 = d->Thumb.x1 + y; int x2 = d->Thumb.x2 - y; if (x2 < x1) break; pDC->HLine(x1, x2, d->Thumb.y1 + y + Off); } } else { for (int x=0; xThumb.X(); x++) { int y1 = d->Thumb.y1 + x; int y2 = d->Thumb.y2 - x; if (y2 < y1) break; pDC->VLine(d->Thumb.x1 + x + Off, y1, y2); } } break; } } pDC->Colour(Lines); if (Tree->d->JoiningLines) { if (Parent || IndexOf() > 0) // draw line to item above pDC->Line(x + 8, Pos.y1, x + 8, d->Thumb.y1-1); // draw line to leaf beside pDC->Line(d->Thumb.x2+1, cy, x + (TREE_BLOCK-1), cy); if (!d->Last) // draw line to item below pDC->Line(x + 8, d->Thumb.y2+1, x + 8, Pos.y2); } } else if (Tree->d->JoiningLines) { // leaf node pDC->Colour(L_MED); if (d->Last) pDC->Rectangle(x + 8, Pos.y1, x + 8, cy); else pDC->Rectangle(x + 8, Pos.y1, x + 8, Pos.y2); pDC->Rectangle(x + 8, cy, x + (TREE_BLOCK-1), cy); } x += TREE_BLOCK; // draw icon int Image = GetImage(Select()); LImageList *Lst = Tree->GetImageList(); if (Image >= 0 && Lst) { d->Icon.ZOff(Lst->TileX() + Tree->d->IconTextGap - 1, Pos.Y() - 1); d->Icon.Offset(x, Pos.y1); pDC->Colour(Ctx.Back); if (Tree->d->IconCache) { // no flicker LRect From; From.ZOff(Lst->TileX()-1, Tree->d->IconCache->Y()-1); From.Offset(Lst->TileX()*Image, 0); pDC->Blt(d->Icon.x1, d->Icon.y1, Tree->d->IconCache, &From); pDC->Rectangle(d->Icon.x1 + Lst->TileX(), d->Icon.y1, d->Icon.x2, d->Icon.y2); } else { // flickers... int Px = d->Icon.y1 + ((Lst->TileY()-Pos.Y()) >> 1); pDC->Rectangle(&d->Icon); Tree->GetImageList()->Draw(pDC, d->Icon.x1, Px, Image, Ctx.Back); } x += d->Icon.X(); } LColour SelFore(Tree->Focus() ? L_FOCUS_SEL_FORE : L_NON_FOCUS_SEL_FORE); LColour SelBack(Tree->Focus() ? L_FOCUS_SEL_BACK : L_NON_FOCUS_SEL_BACK); bool IsSelected = (Tree->d->DropTarget == this) || (Tree->d->DropTarget == NULL && Select()); LColour Fore = Ctx.Fore; LColour TxtBack = Ctx.TxtBack; auto Css = GetCss(); LCss::ColorDef f, b; if (Css) { f = Css->Color(); b = Css->BackgroundColor(); } // text: first column Ctx.Fore = f.Type == LCss::ColorRgb ? (LColour)f : (IsSelected ? SelFore : Fore); Ctx.TxtBack = b.Type == LCss::ColorRgb ? (LColour)b : (IsSelected ? SelBack : Ctx.Back); auto ColourDiff = abs(Ctx.Fore.GetGray() - Ctx.TxtBack.GetGray()); if (ColourDiff < 32) // Check if the colours are too similar and then disambiguate... { // LgiTrace("%s %s are too similar %i\n", Ctx.Fore.GetStr(), Ctx.TxtBack.GetStr(), (int)ColourDiff); Ctx.TxtBack = Ctx.TxtBack.Mix(L_WORKSPACE); } LPoint TextSize; _PourText(TextSize); d->Text.ZOff(TextSize.x-1, Pos.Y()-1); d->Text.Offset(x, Pos.y1); (LRect&)Ctx = d->Text; Ctx.x2 = Ctx.ColPx[0] - 1; _PaintText(Ctx); x = Pos.x2 + 1; // text: other columns Ctx.Fore = f.Type == LCss::ColorRgb ? (LColour)f : Fore; Ctx.TxtBack = b.Type == LCss::ColorRgb ? (LColour)b : Ctx.Back; for (int i=1; iColumns[i]); x = Ctx.x2 + 1; } Ctx.Fore = Fore; Ctx.TxtBack = TxtBack; // background after text pDC->Colour(Ctx.Back); pDC->Rectangle(x, Pos.y1, MAX(Tree->X(), Tree->d->Limit.x), Pos.y2); // children if (d->Open) { if (!d->Last) Tree->d->LineFlags[0] |= 1 << d->Depth; List::I it = Items.begin(); for (LTreeItem *i=*it; i; i=*++it) i->OnPaint(Ctx); Tree->d->LineFlags[0] &= ~(1 << d->Depth); } } void LTreeItem::OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c) { LDisplayString *ds = d->GetDs(i, Ctx.ColPx[i]); if (ds) { LFont *f = ds->GetFont(); f->Colour(Ctx.Fore, Ctx.TxtBack); ds->Draw(Ctx.pDC, Ctx.x1 + 2, Ctx.y1 + 1, &Ctx); } } ////////////////////////////////////////////////////////////////////////////// LTree::LTree(int id, int x, int y, int cx, int cy, const char *name) : ResObject(Res_TreeView) { d = new LTreePrivate; SetId(id); LRect e(x, y, x+cx, y+cy); SetPos(e); if (name) Name(name); else Name("LGI.LTree"); Sunken(true); Tree = this; Lines = true; Buttons = true; LinesAtRoot = true; EditLabels = false; ColumnHeaders = false; rItems.ZOff(-1, -1); #if WINNATIVE SetStyle(GetStyle() | WS_CHILD | WS_VISIBLE | WS_TABSTOP); #endif SetTabStop(true); LResources::StyleElement(this); } LTree::~LTree() { Empty(); DeleteObj(d); } bool LTree::Lock(const char *file, int line, int TimeOut) { if (TimeOut > 0) return d->LockWithTimeout(TimeOut, file, line); return d->Lock(file, line); } void LTree::Unlock() { return d->Unlock(); } // Internal tree methods List *LTree::GetSelLst() { return &d->Selection; } void LTree::_Update(LRect *r, bool Now) { TREELOCK if (r) { LRect u = *r; LPoint s = _ScrollPos(); LRect c = GetClient(); u.Offset(c.x1-s.x, c.y1-s.y); Invalidate(&u, Now && !d->InPour); } else { Invalidate((LRect*)0, Now && !d->InPour); } } void LTree::_UpdateBelow(int y, bool Now) { TREELOCK LPoint s = _ScrollPos(); LRect c = GetClient(); LRect u(c.x1, y - s.y + c.y1, X()-1, Y()-1); Invalidate(&u, Now); } void LTree::ClearDs(int Col) { TREELOCK List::I it = Items.begin(); for (LTreeItem *i=*it; i; i=*++it) i->_ClearDs(Col); } LPoint LTree::_ScrollPos() { TREELOCK LPoint Status; Status.x = (HScroll) ? (int)HScroll->Value() : 0; Status.y = (VScroll) ? (int)VScroll->Value() * TREE_BLOCK : 0; return Status; } void LTree::_UpdateScrollBars() { static bool Processing = false; if (!Processing) { Processing = true; { TREELOCK LPoint Old = _ScrollPos(); LRect Client = GetClient(); bool x = d->Limit.x > Client.X(); bool y = d->Limit.y > Client.Y(); SetScrollBars(x, y); Client = GetClient(); // x scroll... in pixels if (HScroll) { HScroll->SetRange(d->Limit.x); HScroll->SetPage(Client.X()); int Max = d->Limit.x - Client.X(); if (HScroll->Value() > Max) { HScroll->Value(Max+1); } } // y scroll... in items if (VScroll) { int All = (d->Limit.y + TREE_BLOCK - 1) / TREE_BLOCK; int Visible = Client.Y() / TREE_BLOCK; VScroll->SetRange(All); VScroll->SetPage(Visible); /* Why is this commented out? -fret Dec2018 int Max = All - Visible + 1; if (VScroll->Value() > Max) VScroll->Value(Max); */ } LPoint New = _ScrollPos(); if (Old.x != New.x || Old.y != New.y) { Invalidate(); } } Processing = false; } } void LTree::_OnSelect(LTreeItem *Item) { TREELOCK if ( !MultiSelect() || !d->CurrentClick || ( d->CurrentClick && !d->CurrentClick->Ctrl() ) ) { for (auto i: d->Selection) { if (i != Item) i->Select(false); } d->Selection.Empty(); } else { d->Selection.Delete(Item); } d->Selection.Insert(Item); } void LTree::_Pour() { TREELOCK d->InPour = true; d->Limit.x = rItems.x1; d->Limit.y = rItems.y1; int ColumnPx = 0; if (Columns.Length()) { for (int i=0; iWidth(); } } else { ColumnPx = d->LastLayoutPx = GetClient().X(); if (ColumnPx < 16) ColumnPx = 16; } LTreeItem *n; List::I it = Items.begin(); for (LTreeItem *i=*it; i; i=n) { n = *++it; i->d->Last = n == 0; i->_Pour(&d->Limit, ColumnPx, 0, true); } _UpdateScrollBars(); d->LayoutDirty = false; d->InPour = false; } // External methods and events void LTree::OnItemSelect(LTreeItem *Item) { if (!Item) return; TREELOCK Item->OnSelect(); SendNotify(LNotifyItemSelect); } void LTree::OnItemExpand(LTreeItem *Item, bool Expand) { TREELOCK if (Item) Item->OnExpand(Expand); } LTreeItem *LTree::GetAdjacent(LTreeItem *i, bool Down) { TREELOCK LTreeItem *Ret = NULL; if (i) { if (Down) { LTreeItem *n = i->GetChild(); if (!n || !n->d->Visible) { for (n = i; n; ) { LTreeItem *p = n->GetParent(); if (p) { ssize_t Index = n->IndexOf(); if (Index < (ssize_t)p->Items.Length()-1) { n = n->GetNext(); break; } else { n = p; } } else { n = n->GetNext(); break; } } } Ret = n; } else { LTreeItem *p = i->GetParent() ? i->GetParent() : 0; ssize_t Index = i->IndexOf(); if (p) { LTreeItem *n = p; if (Index > 0) { n = i->GetPrev(); while ( n->GetChild() && n->GetChild()->d->Visible) { n = n->Items.ItemAt(n->Items.Length()-1); } } Ret = n; } else if (Index > 0) { p = i->GetTree()->ItemAt(Index - 1); while (p->GetChild() && p->GetChild()->d->Visible) { if (p->Items.Length()) { p = p->Items.ItemAt(p->Items.Length()-1); } else break; } Ret = p; } } } return Ret; } bool LTree::OnKey(LKey &k) { if (!Lock(_FL)) return false; bool Status = false; LTreeItem *i = d->Selection[0]; if (!i) { i = Items[0]; if (i) i->Select(); } if (k.Down()) { switch (k.vkey) { case LK_PAGEUP: case LK_PAGEDOWN: { if (i && i->d->Pos.Y() > 0) { int Page = GetClient().Y() / i->d->Pos.Y(); for (int j=0; jSelect(true); i->ScrollTo(); } } Status = true; break; } case LK_HOME: { LTreeItem *i; if ((i = Items[0])) { i->Select(true); i->ScrollTo(); } Status = true; break; } case LK_END: { LTreeItem *n = i, *p = 0; while ((n = GetAdjacent(n, true))) { p = n; } if (p) { p->Select(true); p->ScrollTo(); } Status = true; break; } case LK_LEFT: { if (i) { if (i->Items.Length() && i->Expanded()) { i->Expanded(false); break; } else { LTreeItem *p = i->GetParent(); if (p) { p->Select(true); p->Expanded(false); _Pour(); break; } } } // fall thru } case LK_UP: { LTreeItem *n = GetAdjacent(i, false); if (n) { n->Select(true); n->ScrollTo(); } Status = true; break; } case LK_RIGHT: { if (i) { i->Expanded(true); if (d->LayoutDirty) { _Pour(); break; } } // fall thru } case LK_DOWN: { LTreeItem *n = GetAdjacent(i, true); if (n) { n->Select(true); n->ScrollTo(); } Status = true; break; } case LK_DELETE: { if (k.Down()) { Unlock(); // before potentially being deleted...? SendNotify(LNotification(k)); // This might delete the item... so just return here. return true; } break; } #ifdef VK_APPS case VK_APPS: { LTreeItem *s = Selection(); if (s) { LRect *r = &s->d->Text; if (r) { LMouse m; m.x = r->x1 + (r->X() >> 1); m.y = r->y1 + (r->Y() >> 1); m.Target = this; m.ViewCoords = true; m.Down(true); m.Right(true); s->OnMouseClick(m); } } break; } #endif default: { switch (k.c16) { case 'F': case 'f': { if (k.Ctrl()) SendNotify(LNotifyContainerFind); break; } } break; } } } if (i && i != (LTreeItem*)this) { i->OnKey(k); } Unlock(); return Status; } LTreeItem *LTree::ItemAtPoint(int x, int y, bool Debug) { TREELOCK LPoint s = _ScrollPos(); List::I it = Items.begin(); LTreeItem *Hit = NULL; for (LTreeItem *i = *it; i; i=*++it) { Hit = i->_HitTest(s.x + x, s.y + y, Debug); if (Hit) break; } return Hit; } bool LTree::OnMouseWheel(double Lines) { TREELOCK if (VScroll) VScroll->Value(VScroll->Value() + (int)Lines); return true; } void LTree::OnMouseClick(LMouse &m) { TREELOCK d->CurrentClick = &m; if (m.Down()) { DragMode = DRAG_NONE; if (ColumnHeaders && ColumnHeader.Overlap(m.x, m.y)) { d->DragStart.x = m.x; d->DragStart.y = m.y; // Clicked on a column heading LItemColumn *Resize; LItemColumn *Over = NULL; HitColumn(m.x, m.y, Resize, Over); if (Resize) { if (m.Double()) { Resize->Width(Resize->GetContentSize() + DEFAULT_COLUMN_SPACING); Invalidate(); } else { DragMode = RESIZE_COLUMN; d->DragData = (int)Columns.IndexOf(Resize); Capture(true); } } /* else { DragMode = CLICK_COLUMN; d->DragData = Columns.IndexOf(Over); if (Over) { Over->Value(true); LRect r = Over->GetPos(); Invalidate(&r); Capture(true); } } */ } else if (rItems.Overlap(m.x, m.y)) { Focus(true); Capture(true); d->LastClick.x = m.x; d->LastClick.y = m.y; d->LastHit = ItemAtPoint(m.x, m.y, true); if (d->LastHit) { LPoint c = _ScrollPos(); m.x += c.x; m.y += c.y; d->LastHit->_MouseClick(m); } else { SendNotify(LNotification(m, LNotifyContainerClick)); } } } else if (IsCapturing()) { Capture(false); if (rItems.Overlap(m.x, m.y)) { d->LastClick.x = m.x; d->LastClick.y = m.y; d->LastHit = ItemAtPoint(m.x, m.y); if (d->LastHit) { LPoint c = _ScrollPos(); m.x += c.x; m.y += c.y; d->LastHit->_MouseClick(m); } } } d->CurrentClick = NULL; } void LTree::OnMouseMove(LMouse &m) { if (!IsCapturing()) return; TREELOCK switch (DragMode) { /* case DRAG_COLUMN: { if (DragCol) { LPoint p; PointToScreen(p); LRect r = DragCol->GetPos(); r.Offset(-p.x, -p.y); // to view co-ord r.Offset(m.x - DragCol->GetOffset() - r.x1, 0); if (r.x1 < 0) r.Offset(-r.x1, 0); if (r.x2 > X()-1) r.Offset((X()-1)-r.x2, 0); r.Offset(p.x, p.y); // back to screen co-ord DragCol->SetPos(r, true); r = DragCol->GetPos(); } break; } */ case RESIZE_COLUMN: { LItemColumn *c = Columns[d->DragData]; if (c) { // int OldWidth = c->Width(); int NewWidth = m.x - c->GetPos().x1; c->Width(MAX(NewWidth, 4)); _ClearDs(d->DragData); Invalidate(); } break; } default: { if (rItems.Overlap(m.x, m.y)) { if (abs(d->LastClick.x - m.x) > DRAG_THRESHOLD || abs(d->LastClick.y - m.y) > DRAG_THRESHOLD) { OnItemBeginDrag(d->LastHit, m); Capture(false); } } break; } } } void LTree::OnPosChange() { TREELOCK if (Columns.Length() == 0 && d->LastLayoutPx != GetClient().X()) d->LayoutDirty = true; LLayout::OnPosChange(); _UpdateScrollBars(); } void LTree::OnPaint(LSurface *pDC) { TREELOCK LCssTools Tools(this); #if 0 // coverage testing... pDC->Colour(LColour(255, 0, 255)); pDC->Rectangle(); #endif rItems = GetClient(); LFont *f = GetFont(); if (ShowColumnHeader()) { ColumnHeader.ZOff(rItems.X()-1, f->GetHeight() + 4); PaintColumnHeadings(pDC); rItems.y1 = ColumnHeader.y2 + 1; } else { ColumnHeader.ZOff(-1, -1); } d->IconTextGap = GetFont()->GetHeight() / 6; auto cText = LColour(L_TEXT); auto cWs = LColour(L_WORKSPACE); LColour Fore = Tools.GetFore(&cText); LColour Background = Tools.GetBack(&cWs, 0); // icon cache if (GetImageList() && !d->IconCache) { int CacheHeight = MAX(LSysFont->GetHeight(), GetImageList()->Y()); d->IconCache = new LMemDC; if (d->IconCache && d->IconCache->Create(GetImageList()->X(), CacheHeight, GdcD->GetColourSpace())) { if (d->IconCache->GetColourSpace() == CsIndex8) { d->IconCache->Palette(new LPalette(GdcD->GetGlobalColour()->GetPalette())); } d->IconCache->Colour(Background); d->IconCache->Rectangle(); d->IconCache->Op(GDC_ALPHA); GetImageList()->Lock(); int DrawY = (CacheHeight - GetImageList()->TileY()) >> 1; LAssert(DrawY >= 0); for (int i=0; iGetItems(); i++) { GetImageList()->Draw(d->IconCache, i * GetImageList()->TileX(), DrawY, i, Background); } GetImageList()->Unlock(); d->IconCache->Unlock(); } } // scroll LPoint s = _ScrollPos(); int Ox, Oy; pDC->GetOrigin(Ox, Oy); pDC->SetOrigin(Ox + s.x, Oy + s.y); // selection colour LArray ColPx; LItem::ItemPaintCtx Ctx; Ctx.pDC = pDC; if (Columns.Length() > 0) { Ctx.Columns = (int)Columns.Length(); for (int i=0; iWidth(); } else { Ctx.Columns = 1; ColPx[0] = rItems.X(); } Ctx.ColPx = &ColPx[0]; Ctx.Fore = Fore; Ctx.Back = Background; Ctx.TxtBack = Background; LColour SelFore(Focus() ? L_FOCUS_SEL_FORE : L_NON_FOCUS_SEL_FORE); LColour SelBack(Focus() ? L_FOCUS_SEL_BACK : L_NON_FOCUS_SEL_BACK); // layout items if (d->LayoutDirty) { _Pour(); } // paint items ZeroObj(d->LineFlags); List::I it = Items.begin(); for (LTreeItem *i = *it; i; i=*++it) i->OnPaint(Ctx); pDC->SetOrigin(Ox, Oy); if (d->Limit.y-s.y < rItems.Y()) { // paint after items pDC->Colour(Background); pDC->Rectangle(rItems.x1, d->Limit.y - s.y, rItems.x2, rItems.y2); } } int LTree::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_HSCROLL: case IDC_VSCROLL: { TREELOCK - if (Flags == LNotifyScrollBarCreate) + if (n.Type == LNotifyScrollBarCreate) { _UpdateScrollBars(); if (VScroll) { if (HasItem(d->ScrollTo)) d->ScrollTo->ScrollTo(); d->ScrollTo = NULL; } } Invalidate(); break; } } return LLayout::OnNotify(Ctrl, n); } LMessage::Result LTree::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_SCROLL_TO: { LTreeItem *Item = (LTreeItem*)Msg->A(); if (!HasItem(Item)) break; if (VScroll) Item->ScrollTo(); break; } } return LItemContainer::OnEvent(Msg); } LTreeItem *LTree::Insert(LTreeItem *Obj, ssize_t Pos) { TREELOCK LTreeItem *NewObj = LTreeNode::Insert(Obj, Pos); if (NewObj) NewObj->_SetTreePtr(this); return NewObj; } bool LTree::HasItem(LTreeItem *Obj, bool Recurse) { TREELOCK if (!Obj) return false; return LTreeNode::HasItem(Obj, Recurse); } bool LTree::Remove(LTreeItem *Obj) { TREELOCK bool Status = false; if (Obj && Obj->Tree == this) { Obj->Remove(); Status = true; } return Status; } void LTree::RemoveAll() { TREELOCK List::I it = Items.begin(); for (LTreeItem *i=*it; i; i=*++it) i->_Remove(); Invalidate(); } void LTree::Empty() { TREELOCK LTreeItem *i; while ((i = Items[0])) Delete(i); } bool LTree::Delete(LTreeItem *Obj) { bool Status = false; TREELOCK if (Obj) { LTreeItem *i; while ((i = Obj->Items[0])) { Delete(i); } Obj->Remove(); DeleteObj(Obj); Status = true; } return Status; } void LTree::OnPulse() { TREELOCK if (d->DropTarget) { int64 p = LCurrentTime() - d->DropSelectTime; if (p >= 1000) { SetPulse(); if (!d->DropTarget->Expanded() && d->DropTarget->GetChild()) { d->DropTarget->Expanded(true); } } } if (InsideDragOp()) { LMouse m; if (GetMouse(m)) { if (!m.Left() && !m.Right() && !m.Middle()) { // Be robust against missing drag exit events (Mac specific?) InsideDragOp(false); } else { LRect c = GetClient(); if (VScroll) { if (m.y < DRAG_SCROLL_EDGE) { // Scroll up... VScroll->Value(VScroll->Value() - DRAG_SCROLL_Y); } else if (m.y > c.Y() - DRAG_SCROLL_EDGE) { // Scroll down... VScroll->Value(VScroll->Value() + DRAG_SCROLL_Y); } } if (HScroll) { if (m.x < DRAG_SCROLL_EDGE) { // Scroll left... HScroll->Value(HScroll->Value() - DRAG_SCROLL_X); } else if (m.x > c.X() - DRAG_SCROLL_EDGE) { // Scroll right... HScroll->Value(HScroll->Value() + DRAG_SCROLL_X); } } } } } } int LTree::GetContentSize(int ColumnIdx) { TREELOCK int MaxPx = 0; List::I it = Items.begin(); for (LTreeItem *i = *it; i; i=*++it) { int ItemPx = i->GetColumnSize(ColumnIdx); MaxPx = MAX(ItemPx, MaxPx); } return MaxPx; } LCursor LTree::GetCursor(int x, int y) { TREELOCK LItemColumn *Resize = NULL, *Over = NULL; HitColumn(x, y, Resize, Over); return (Resize) ? LCUR_SizeHor : LCUR_Normal; } void LTree::OnDragEnter() { TREELOCK InsideDragOp(true); SetPulse(120); } void LTree::OnDragExit() { TREELOCK InsideDragOp(false); SetPulse(); SelectDropTarget(0); } void LTree::SelectDropTarget(LTreeItem *Item) { TREELOCK if (Item != d->DropTarget) { bool Update = (d->DropTarget != 0) ^ (Item != 0); LTreeItem *Old = d->DropTarget; d->DropTarget = Item; if (Old) { Old->Update(); } if (d->DropTarget) { d->DropTarget->Update(); d->DropSelectTime = LCurrentTime(); } if (Update) { OnFocus(true); } } } bool LTree::Select(LTreeItem *Obj) { TREELOCK bool Status = false; if (Obj && IsAttached()) { Obj->Select(true); Status = true; } else if (d->Selection.Length()) { d->Selection.Empty(); OnItemSelect(0); Status = true; } return Status; } LTreeItem *LTree::Selection() { TREELOCK return d->Selection[0]; } bool LTree::ForAllItems(std::function Callback) { TREELOCK return ForEach(Callback) > 0; } void LTree::OnItemClick(LTreeItem *Item, LMouse &m) { if (!Item) return; TREELOCK Item->OnMouseClick(m); if (!m.Ctrl() && !m.Shift()) SendNotify(LNotification(m)); } void LTree::OnItemBeginDrag(LTreeItem *Item, LMouse &m) { if (!Item) return; TREELOCK Item->OnBeginDrag(m); } void LTree::OnFocus(bool b) { TREELOCK // errors during deletion of the control can cause // this to be called after the destructor if (d) { List::I it = d->Selection.begin(); for (LTreeItem *i=*it; i; i=*++it) i->Update(); } } static void LTreeItemUpdateAll(LTreeNode *n) { for (LTreeItem *i=n->GetChild(); i; i=i->GetNext()) { i->Update(); LTreeItemUpdateAll(i); } } void LTree::UpdateAllItems() { TREELOCK d->LayoutDirty = true; LTreeItemUpdateAll(this); } void LTree::SetVisualStyle(ThumbStyle Btns, bool JoiningLines) { TREELOCK d->Btns = Btns; d->JoiningLines = JoiningLines; Invalidate(); } diff --git a/src/haiku/Layout.cpp b/src/haiku/Layout.cpp --- a/src/haiku/Layout.cpp +++ b/src/haiku/Layout.cpp @@ -1,312 +1,303 @@ /* ** FILE: Layout.cpp ** AUTHOR: Matthew Allen ** DATE: 1/12/2021 ** DESCRIPTION: Standard Views ** ** Copyright (C) 2021, Matthew Allen ** fret@memecode.com */ #include #include "lgi/common/Lgi.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/Notifications.h" ////////////////////////////////////////////////////////////////////////////// LLayout::LLayout() { _PourLargest = false; VScroll = 0; HScroll = 0; } LLayout::~LLayout() { DeleteObj(HScroll); DeleteObj(VScroll); } LViewI *LLayout::FindControl(int Id) { if (VScroll && VScroll->GetId() == Id) return VScroll; if (HScroll && HScroll->GetId() == Id) return HScroll; return LView::FindControl(Id); } bool LLayout::GetPourLargest() { return _PourLargest; } void LLayout::SetPourLargest(bool i) { _PourLargest = i; } bool LLayout::Pour(LRegion &r) { if (_PourLargest) { LRect *Best = FindLargest(r); if (Best) { SetPos(*Best); return true; } } return false; } void LLayout::GetScrollPos(int64 &x, int64 &y) { - if (HScroll) - { - x = HScroll->Value(); - } - else - { - x = 0; - } - - if (VScroll) - { - y = VScroll->Value(); - } - else - { - y = 0; - } + x = HScroll ? HScroll->Value() : 0; + y = VScroll ? VScroll->Value() : 0; + + printf("GetScrollPos=%i,%i\n", (int)x, (int)y); } void LLayout::SetScrollPos(int64 x, int64 y) { if (HScroll) HScroll->Value(x); if (VScroll) VScroll->Value(y); } bool LLayout::Attach(LViewI *p) { bool Status = LView::Attach(p); AttachScrollBars(); return Status; } bool LLayout::Detach() { return LView::Detach(); } void LLayout::OnCreate() { AttachScrollBars(); OnPosChange(); } void LLayout::AttachScrollBars() { if (HScroll && !HScroll->IsAttached()) { HScroll->Visible(true); HScroll->Attach(this); HScroll->SetNotify(this); } if (VScroll && !VScroll->IsAttached()) { VScroll->Visible(true); VScroll->Attach(this); VScroll->SetNotify(this); } } bool LLayout::SetScrollBars(bool x, bool y) { if (x ^ (HScroll != NULL) || y ^ (VScroll != NULL)) { + // printf("%s:%i - setScroll %i,%i attached=%i\n", _FL, x, y, IsAttached()); + if (!IsAttached()) { _SetScrollBars(x, y); } else if (_SetScroll.x != x || _SetScroll.y != y || !_SetScroll.SentMsg) { // This is to filter out masses of duplicate messages // before they have a chance to be processed. Esp important on // GTK systems where the message handling isn't very fast. _SetScroll.x = x; _SetScroll.y = y; _SetScroll.SentMsg = true; auto r = PostEvent(M_SET_SCROLL, x, y); if (!r) printf("%s:%i - sending M_SET_SCROLL(%i,%i) to myself=%s, r=%i, attached=%i.\n", _FL, x, y, GetClass(), r, IsAttached()); return r; } // Duplicate... ignore... return true; } return true; } bool LLayout::_SetScrollBars(bool x, bool y) { static bool Processing = false; + // printf("%s:%i - _setScroll %i,%i %i\n", _FL, x, y, Processing); + if (!Processing && (((HScroll!=0) ^ x ) || ((VScroll!=0) ^ y )) ) { Processing = true; if (x) { if (!HScroll) { HScroll = new LScrollBar(IDC_HSCROLL, 0, 0, 100, 10, "LLayout->HScroll"); if (HScroll) { HScroll->SetVertical(false); HScroll->Visible(false); } } } else { DeleteObj(HScroll); } if (y) { if (!VScroll) { VScroll = new LScrollBar(IDC_VSCROLL, 0, 0, 10, 100, "LLayout->VScroll"); if (VScroll) { VScroll->Visible(false); } } } else if (VScroll) { DeleteObj(VScroll); } AttachScrollBars(); OnPosChange(); Invalidate(); Processing = false; } return true; } int LLayout::OnNotify(LViewI *c, LNotification n) { return LView::OnNotify(c, n.Type); } void LLayout::OnPosChange() { LRect r = LView::GetClient(); auto Px = LScrollBar::GetScrollSize(); LRect v(r.x2-Px+1, r.y1, r.x2, r.y2); LRect h(r.x1, r.y2-Px+1, r.x2, r.y2); if (VScroll && HScroll) { h.x2 = v.x1 - 1; v.y2 = h.y1 - 1; } if (VScroll) { VScroll->Visible(true); VScroll->SetPos(v, true); } if (HScroll) { HScroll->Visible(true); HScroll->SetPos(h, true); } } void LLayout::OnNcPaint(LSurface *pDC, LRect &r) { LView::OnNcPaint(pDC, r); if (VScroll && VScroll->Visible()) { r.x2 -= VScroll->X(); } if (HScroll && HScroll->Visible()) { r.y2 -= HScroll->Y(); } if (VScroll && VScroll->Visible() && HScroll && HScroll->Visible()) { // Draw square at the end of each scroll bar LRect s( VScroll->GetPos().x1, HScroll->GetPos().y1, VScroll->GetPos().x2, HScroll->GetPos().y2); pDC->Colour(L_MED); pDC->Rectangle(&s); } } LRect &LLayout::GetClient(bool ClientSpace) { static LRect r; r = LView::GetClient(ClientSpace); if (VScroll && VScroll->Visible()) { // printf("\tLayout.GetCli r=%s -> ", r.GetStr()); r.x2 = VScroll->GetPos().x1 - 1; // printf("%s\n", r.GetStr()); } if (HScroll && HScroll->Visible()) r.y2 = HScroll->GetPos().y1 - 1; return r; } LMessage::Param LLayout::OnEvent(LMessage *Msg) { if (Msg->Msg() == M_SET_SCROLL) { _SetScroll.SentMsg = false; // printf("%s:%i - receiving M_SET_SCROLL myself=%s.\n", _FL, GetClass()); _SetScrollBars(Msg->A(), Msg->B()); if (HScroll) HScroll->SendNotify(LNotifyScrollBarCreate); if (VScroll) VScroll->SendNotify(LNotifyScrollBarCreate); return 0; } int Status = LView::OnEvent(Msg); if (Msg->Msg() == M_CHANGE && Status == -1 && GetParent()) { Status = GetParent()->OnEvent(Msg); } return Status; } diff --git a/src/haiku/ScreenDC.cpp b/src/haiku/ScreenDC.cpp --- a/src/haiku/ScreenDC.cpp +++ b/src/haiku/ScreenDC.cpp @@ -1,443 +1,471 @@ /*hdr ** FILE: LScreenDC.cpp ** AUTHOR: Matthew Allen ** DATE: 29/11/2021 ** DESCRIPTION: Haiku screen DC ** ** Copyright (C) 2021, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include +#include #define VIEW_CHECK(...) if (!d->v) return __VA_ARGS__; class LScreenPrivate { public: int x = 0, y = 0, Bits = 32; bool Own = false; LColour Col; LRect Client; LView *View = NULL; OsView v = NULL; LScreenPrivate() { Client.ZOff(-1, -1); } ~LScreenPrivate() { } }; // Translates are cumulative... so we undo the last one before resetting it. ///////////////////////////////////////////////////////////////////////////////////////////////////// LScreenDC::LScreenDC() { d = new LScreenPrivate; d->x = GdcD->X(); d->y = GdcD->Y(); d->Bits = GdcD->GetBits(); } LScreenDC::LScreenDC(LView *view, void *param) { d = new LScreenPrivate; if (d->View = view) d->v = view->Handle(); d->Bits = GdcD->GetBits(); + /* if (d->v) d->Client = d->v->Frame(); else LgiTrace("%s:%i - LScreenDC::LScreenDC - No view?\n", _FL); + */ } LScreenDC::~LScreenDC() { DeleteObj(d); } OsPainter LScreenDC::Handle() { return d->v; } ::LString LScreenDC::Dump() { ::LString s; s.Printf("LScreenDC size=%i,%i\n", d->x, d->y); return s; } bool LScreenDC::SupportsAlphaCompositing() { - // GTK/X11 doesn't seem to support alpha compositing. - return false; + return true; } LPoint LScreenDC::GetDpi() { return LScreenDpi(); } bool LScreenDC::GetClient(LRect *c) { if (!c) return false; *c = d->Client; return true; } void LScreenDC::GetOrigin(int &x, int &y) { if (d->Client.Valid()) { x = OriginX + d->Client.x1; y = OriginY + d->Client.y1; } else { x = OriginX; y = OriginY; } } +LString GetClip(BView *v) +{ + BRegion r; + v->GetClippingRegion(&r); + LRect lr = r.Frame(); + return lr.GetStr(); +} + void LScreenDC::SetOrigin(int x, int y) { VIEW_CHECK() if (d->Client.Valid()) { OriginX = x - d->Client.x1; OriginY = y - d->Client.y1; + + // The clipping region is relative to the offset. + // Remove it here and reinstate it after setting the origin. + d->v->ConstrainClippingRegion(NULL); } else { OriginX = x; OriginY = y; } - // printf("setori %i,%i\n", OriginX, OriginY); - d->v->SetOrigin(OriginX, OriginX); + d->v->SetOrigin(-OriginX, -OriginY); + + if (d->Client.Valid()) + { + // Reset the clipping region related to the origin. + auto clp = d->Client.ZeroTranslate(); + clp.Offset(OriginX, OriginY); + d->v->ClipToRect(clp); + } } void LScreenDC::SetClient(LRect *c) { VIEW_CHECK() if (c) { d->Client = *c; OriginX = c->x1; OriginY = c->y1; - LRect clp(0, 0, c->X()-2, c->Y()-2); - // printf("clp=%s ori=%i,%i\n", clp.GetStr(), OriginX, OriginY); - d->v->SetOrigin(OriginX, OriginY); + d->v->SetOrigin(-OriginX, -OriginY); + + auto clp = d->Client.ZeroTranslate(); + clp.Offset(OriginX, OriginY); d->v->ClipToRect(clp); + + /* + BRegion r; + d->v->GetClippingRegion(&r); + LRect lr = r.Frame(); + printf("SetClient %s = %s (%i,%i)\n", c->GetStr(), lr.GetStr(), OriginX, OriginY); + */ } else { - if (Clip.Valid()) - ClipRgn(NULL); - + d->v->ConstrainClippingRegion(NULL); d->v->SetOrigin(OriginX = 0, OriginX = 0); + d->Client.ZOff(-1, -1); } } LRect *LScreenDC::GetClient() { return &d->Client; } uint LScreenDC::LineStyle() { return LSurface::LineStyle(); } uint LScreenDC::LineStyle(uint32_t Bits, uint32_t Reset) { return LSurface::LineStyle(Bits); } int LScreenDC::GetFlags() { return 0; } LRect LScreenDC::ClipRgn() { return Clip; } LRect LScreenDC::ClipRgn(LRect *c) { LRect Prev = Clip; if (c) { Clip = *c; d->v->ClipToRect(Clip); } else { Clip.ZOff(-1, -1); d->v->ConstrainClippingRegion(NULL); } return Prev; } COLOUR LScreenDC::Colour() { return d->Col.Get(GetBits()); } COLOUR LScreenDC::Colour(COLOUR c, int Bits) { auto b = Bits ? Bits : GetBits(); d->Col.Set(c, b); return Colour(d->Col).Get(b); } LColour LScreenDC::Colour(LColour c) { LColour Prev = d->Col; d->Col = c; if (d->v) { d->v->SetLowColor(c); d->v->SetHighColor(c); } else LgiTrace("%s:%i - No view.\n", _FL); return Prev; } int LScreenDC::Op(int ROP, NativeInt Param) { return GDC_SET; // not supported.. } int LScreenDC::Op() { return GDC_SET; // not supported.. } int LScreenDC::X() { return d->Client.Valid() ? d->Client.X() : d->x; } int LScreenDC::Y() { return d->Client.Valid() ? d->Client.Y() : d->y; } int LScreenDC::GetBits() { return d->Bits; } LPalette *LScreenDC::Palette() { return NULL; // not supported. } void LScreenDC::Palette(LPalette *pPal, bool bOwnIt) { } void LScreenDC::Set(int x, int y) { VIEW_CHECK() d->v->StrokeLine(BPoint(x, y), BPoint(x, y)); } COLOUR LScreenDC::Get(int x, int y) { return 0; } void LScreenDC::HLine(int x1, int x2, int y) { VIEW_CHECK() d->v->StrokeLine(BPoint(x1, y), BPoint(x2, y)); } void LScreenDC::VLine(int x, int y1, int y2) { VIEW_CHECK() d->v->StrokeLine(BPoint(x, y1), BPoint(x, y2)); } void LScreenDC::Line(int x1, int y1, int x2, int y2) { VIEW_CHECK() d->v->StrokeLine(BPoint(x1, y1), BPoint(x2, y2)); } void LScreenDC::Circle(double cx, double cy, double radius) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), radius, radius, 0, 360); } void LScreenDC::FilledCircle(double cx, double cy, double radius) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), radius, radius, 0, 360); } void LScreenDC::Arc(double cx, double cy, double radius, double start, double end) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), radius, radius, start, end); } void LScreenDC::FilledArc(double cx, double cy, double radius, double start, double end) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), radius, radius, start, end); } void LScreenDC::Ellipse(double cx, double cy, double x, double y) { VIEW_CHECK() d->v->StrokeArc(BPoint(cx, cy), x, y, 0, 360); } void LScreenDC::FilledEllipse(double cx, double cy, double x, double y) { VIEW_CHECK() d->v->FillArc(BPoint(cx, cy), x, y, 0, 360); } void LScreenDC::Box(int x1, int y1, int x2, int y2) { VIEW_CHECK() d->v->StrokeRect(LRect(x1, y1, x2, y2)); } void LScreenDC::Box(LRect *a) { VIEW_CHECK() if (a) d->v->StrokeRect(*a); else Box(0, 0, X()-1, Y()-1); } void LScreenDC::Rectangle(int x1, int y1, int x2, int y2) { VIEW_CHECK() if (x2 >= x1 && y2 >= y1) { // auto o = d->v->Origin(); // printf("Rect %i,%i,%i,%i %g,%g\n", x1, y1, x2, y2, o.x, o.y); d->v->FillRect(LRect(x1, y1, x2, y2)); } } void LScreenDC::Rectangle(LRect *a) { VIEW_CHECK() LRect r = a ? *a : Bounds(); BRect br(r.x1, r.y1, r.x2, r.y2); d->v->FillRect(br); } void LScreenDC::Polygon(int Points, LPoint *Data) { VIEW_CHECK() if (!Data || Points <= 0) return; } void LScreenDC::Blt(int x, int y, LSurface *Src, LRect *a) { VIEW_CHECK() if (!Src) { LgiTrace("%s:%i - No source.\n", _FL); return; } if (Src->IsScreen()) { LgiTrace("%s:%i - Can't do screen->screen blt.\n", _FL); return; } BBitmap *bmp = Src->GetBitmap(); if (!bmp) { LAssert(!"no bmp."); LgiTrace("%s:%i - No bitmap.\n", _FL); return; } d->v->SetDrawingMode(B_OP_ALPHA); d->v->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); if (a) { BRect bitmapRect = *a; BRect viewRect(x, y, x + a->X() - 1, y + a->Y() - 1); d->v->DrawBitmap(bmp, bitmapRect, viewRect); #if 0 printf("ScreenDC::Blt %g,%g/%gx%g -> %g,%g/%gx%g %i,%i\n", bitmapRect.left, bitmapRect.top, bitmapRect.Width(), bitmapRect.Height(), viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height(), a->X(), a->Y()); #endif } else { BRect bitmapRect = bmp->Bounds(); BRect viewRect(x, y, x + Src->X() - 1, y + Src->Y() - 1); d->v->DrawBitmap(bmp, bitmapRect, viewRect); #if 0 printf("ScreenDC::Blt %g,%g/%gx%g -> %g,%g/%gx%g %i,%i\n", bitmapRect.left, bitmapRect.top, bitmapRect.Width(), bitmapRect.Height(), viewRect.left, viewRect.top, viewRect.Width(), viewRect.Height(), x, y); #endif } d->v->SetDrawingMode(B_OP_COPY); } void LScreenDC::StretchBlt(LRect *Dest, LSurface *Src, LRect *s) { VIEW_CHECK() LAssert(0); } void LScreenDC::Bezier(int Threshold, LPoint *Pt) { VIEW_CHECK() LAssert(0); } void LScreenDC::FloodFill(int x, int y, int Mode, COLOUR Border, LRect *Bounds) { VIEW_CHECK() LAssert(0); } diff --git a/src/haiku/View.cpp b/src/haiku/View.cpp --- a/src/haiku/View.cpp +++ b/src/haiku/View.cpp @@ -1,1119 +1,1129 @@ /*hdr ** FILE: LView.cpp ** AUTHOR: Matthew Allen ** DATE: 29/11/2021 ** DESCRIPTION: Haiku LView Implementation ** ** Copyright (C) 2021, Matthew Allen ** fret@memecode.com */ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Edit.h" #include "lgi/common/Popup.h" #include "lgi/common/Css.h" #include "ViewPriv.h" #include #define DEBUG_MOUSE_EVENTS 0 #if 0 #define DEBUG_INVALIDATE(...) printf(__VA_ARGS__) #else #define DEBUG_INVALIDATE(...) #endif struct CursorInfo { public: LRect Pos; LPoint HotSpot; } CursorMetrics[] = { // up arrow { LRect(0, 0, 8, 15), LPoint(4, 0) }, // cross hair { LRect(20, 0, 38, 18), LPoint(29, 9) }, // hourglass { LRect(40, 0, 51, 15), LPoint(45, 8) }, // I beam { LRect(60, 0, 66, 17), LPoint(63, 8) }, // N-S arrow { LRect(80, 0, 91, 16), LPoint(85, 8) }, // E-W arrow { LRect(100, 0, 116, 11), LPoint(108, 5) }, // NW-SE arrow { LRect(120, 0, 132, 12), LPoint(126, 6) }, // NE-SW arrow { LRect(140, 0, 152, 12), LPoint(146, 6) }, // 4 way arrow { LRect(160, 0, 178, 18), LPoint(169, 9) }, // Blank { LRect(0, 0, 0, 0), LPoint(0, 0) }, // Vertical split { LRect(180, 0, 197, 16), LPoint(188, 8) }, // Horizontal split { LRect(200, 0, 216, 17), LPoint(208, 8) }, // Hand { LRect(220, 0, 233, 13), LPoint(225, 0) }, // No drop { LRect(240, 0, 258, 18), LPoint(249, 9) }, // Copy drop { LRect(260, 0, 279, 19), LPoint(260, 0) }, // Move drop { LRect(280, 0, 299, 19), LPoint(280, 0) }, }; // CursorData is a bitmap in an array of uint32's. This is generated from a graphics file: // ./Code/cursors.png // // The pixel values are turned into C code by a program called i.Mage: // http://www.memecode.com/image.php // // Load the graphic into i.Mage and then go Edit->CopyAsCode // Then paste the text into the CursorData variable at the bottom of this file. // // This saves a lot of time finding and loading an external resouce, and even having to // bundle extra files with your application. Which have a tendancy to get lost along the // way etc. extern uint32_t CursorData[]; LInlineBmp Cursors = { 300, 20, 8, CursorData }; //////////////////////////////////////////////////////////////////////////// void _lgi_yield() { LAppInst->Yield(); } void *IsAttached(BView *v) { auto pview = v->Parent(); auto pwnd = v->Window(); return pwnd ? (void*)pwnd : (void*)pview; } bool LgiIsKeyDown(int Key) { LAssert(0); return false; } LKey::LKey(int Vkey, uint32_t flags) { vkey = Vkey; Flags = flags; IsChar = false; } //////////////////////////////////////////////////////////////////////////////////////////////////// template struct LBView : public Parent { LViewPrivate *d = NULL; static uint32 MouseButtons; LBView(LViewPrivate *priv) : d(priv), Parent ( "", B_FULL_UPDATE_ON_RESIZE | B_WILL_DRAW | B_NAVIGABLE | B_FRAME_EVENTS ) { Parent::SetName(d->View->GetClass()); } ~LBView() { if (d) d->Hnd = NULL; } void AttachedToWindow() { if (d) d->View->OnCreate(); } LKey ConvertKey(const char *bytes, int32 numBytes) { LKey k; uint8_t *utf = (uint8_t*)bytes; ssize_t len = numBytes; auto w = LgiUtf8To32(utf, len); key_info KeyInfo; if (get_key_info(&KeyInfo) == B_OK) { k.Ctrl(TestFlag(KeyInfo.modifiers, B_CONTROL_KEY)); k.Alt(TestFlag(KeyInfo.modifiers, B_MENU_KEY)); k.Shift(TestFlag(KeyInfo.modifiers, B_SHIFT_KEY)); k.System(TestFlag(KeyInfo.modifiers, B_COMMAND_KEY)); } #if 0 LString::Array a; for (int i=0; iCurrentMessage(); if (bmsg) { int32 key = 0; if (bmsg->FindInt32("key", &key) == B_OK) { // Translate the function keys into the LGI address space... switch (key) { case B_F1_KEY: w = LK_F1; break; case B_F2_KEY: w = LK_F2; break; case B_F3_KEY: w = LK_F3; break; case B_F4_KEY: w = LK_F4; break; case B_F5_KEY: w = LK_F5; break; case B_F6_KEY: w = LK_F6; break; case B_F7_KEY: w = LK_F7; break; case B_F8_KEY: w = LK_F8; break; case B_F9_KEY: w = LK_F9; break; case B_F10_KEY: w = LK_F10; break; case B_F11_KEY: w = LK_F11; break; case B_F12_KEY: w = LK_F12; break; default: printf("%s:%i - Upsupported key %i.\n", _FL, key); break; } } else printf("%s:%i - No 'key' in BMessage.\n", _FL); } else printf("%s:%i - No BMessage.\n", _FL); } k.c16 = k.vkey = w; } k.IsChar = !( k.System() || k.Alt() ) && ( (k.c16 >= ' ' && k.c16 < LK_DELETE) || k.c16 == LK_BACKSPACE || k.c16 == LK_TAB || k.c16 == LK_RETURN ); return k; } void KeyDown(const char *bytes, int32 numBytes) { if (!d) return; auto k = ConvertKey(bytes, numBytes); k.Down(true); auto wnd = d->View->GetWindow(); if (wnd) wnd->HandleViewKey(d->View, k); else d->View->OnKey(k); } void KeyUp(const char *bytes, int32 numBytes) { if (!d) return; auto k = ConvertKey(bytes, numBytes); auto wnd = d->View->GetWindow(); if (wnd) wnd->HandleViewKey(d->View, k); else d->View->OnKey(k); } void FrameMoved(BPoint newPosition) { if (!d) return; d->View->Pos = Parent::Frame(); d->View->OnPosChange(); } void FrameResized(float newWidth, float newHeight) { if (!d) return; d->View->Pos = Parent::Frame(); d->View->OnPosChange(); } void MessageReceived(BMessage *message) { if (!d) return; void *v = NULL; if (message->FindPointer(LMessage::PropView, &v) == B_OK) { // Proxy'd event for child view... ((LView*)v)->OnEvent((LMessage*)message); return; } else d->View->OnEvent((LMessage*)message); if (message->what == B_MOUSE_WHEEL_CHANGED) { float x = 0.0f, y = 0.0f; message->FindFloat("be:wheel_delta_x", &x); message->FindFloat("be:wheel_delta_y", &y); d->View->OnMouseWheel(y * 3.0f); } else if (message->what == M_SET_SCROLL) { return; } Parent::MessageReceived(message); } void Draw(BRect updateRect) { if (!d) return; LScreenDC dc(d->View); LPoint off(0,0); d->View->_Paint(&dc, &off, NULL); } LMouse ConvertMouse(BPoint where, bool down = false) { LMouse m; BPoint loc; uint32 buttons = 0; m.Target = d->View; m.x = where.x; m.y = where.y; if (down) { m.Down(true); Parent::GetMouse(&loc, &buttons, false); MouseButtons = buttons; // save for up click } else { buttons = MouseButtons; } if (buttons & B_PRIMARY_MOUSE_BUTTON) m.Left(true); if (buttons & B_TERTIARY_MOUSE_BUTTON) m.Middle(true); if (buttons & B_SECONDARY_MOUSE_BUTTON) m.Right(true); uint32 mod = modifiers(); if (mod & B_SHIFT_KEY) m.Shift(true); if (mod & B_OPTION_KEY) m.Alt(true); if (mod & B_CONTROL_KEY) m.Ctrl(true); if (mod & B_COMMAND_KEY) m.System(true); return m; } void MouseDown(BPoint where) { if (!d) return; static uint64_t lastClick = 0; bigtime_t interval = 0; status_t r = get_click_speed(&interval); auto now = LCurrentTime(); bool doubleClick = now-lastClick < (interval/1000); lastClick = now; LMouse m = ConvertMouse(where, true); m.Double(doubleClick); d->View->_Mouse(m, false); } void MouseUp(BPoint where) { if (!d) return; LMouse m = ConvertMouse(where); m.Down(false); d->View->_Mouse(m, false); } void MouseMoved(BPoint where, uint32 code, const BMessage *dragMessage) { if (!d) return; LMouse m = ConvertMouse(where); m.Down( m.Left() || m.Middle() || m.Right()); m.IsMove(true); d->View->_Mouse(m, true); } void MakeFocus(bool focus=true) { if (!d) return; Parent::MakeFocus(focus); d->View->OnFocus(focus); } }; template uint32 LBView::MouseButtons = 0; //////////////////////////////////////////////////////////////////////////////////////////////////// LViewPrivate::LViewPrivate(LView *view) : View(view), Hnd(new LBView(this)) { } LViewPrivate::~LViewPrivate() { View->d = NULL; + MsgQue.DeleteObjects(); + if (Font && FontOwnType == GV_FontOwned) DeleteObj(Font); if (Hnd) { auto *bv = dynamic_cast*>(Hnd); // printf("%p::~LViewPrivate View=%p bv=%p th=%u\n", this, View, bv, GetCurrentThreadId()); if (bv) bv->d = NULL; auto Wnd = Hnd->Window(); if (Wnd) Wnd->LockLooper(); if (Hnd->Parent()) Hnd->RemoveSelf(); DeleteObj(Hnd); if (Wnd) Wnd->UnlockLooper(); } } void LView::_Focus(bool f) { ThreadCheck(); if (f) SetFlag(WndFlags, GWF_FOCUS); else ClearFlag(WndFlags, GWF_FOCUS); LLocker lck(d->Hnd, _FL); if (lck.Lock()) { d->Hnd->MakeFocus(f); lck.Unlock(); } // OnFocus will be called by the LBview handler... Invalidate(); } void LView::_Delete() { SetPulse(); // Remove static references to myself if (_Over == this) _Over = 0; if (_Capturing == this) _Capturing = 0; auto *Wnd = GetWindow(); if (Wnd && Wnd->GetFocus() == static_cast(this)) Wnd->SetFocus(this, LWindow::ViewDelete); if (LAppInst && LAppInst->AppWnd == this) { LAppInst->AppWnd = 0; } // Hierarchy LViewI *c; while ((c = Children[0])) { if (c->GetParent() != (LViewI*)this) { printf("%s:%i - ~LView error, %s not attached correctly: %p(%s) Parent: %p(%s)\n", _FL, c->GetClass(), c, c->Name(), c->GetParent(), c->GetParent() ? c->GetParent()->Name() : ""); Children.Delete(c); } DeleteObj(c); } Detach(); // Misc Pos.ZOff(-1, -1); } LView *&LView::PopupChild() { return d->Popup; } BCursorID LgiToHaiku(LCursor c) { switch (c) { #define _(l,h) case l: return h; _(LCUR_Blank, B_CURSOR_ID_NO_CURSOR) _(LCUR_Normal, B_CURSOR_ID_SYSTEM_DEFAULT) _(LCUR_UpArrow, B_CURSOR_ID_RESIZE_NORTH) _(LCUR_DownArrow, B_CURSOR_ID_RESIZE_SOUTH) _(LCUR_LeftArrow, B_CURSOR_ID_RESIZE_WEST) _(LCUR_RightArrow, B_CURSOR_ID_RESIZE_EAST) _(LCUR_Cross, B_CURSOR_ID_CROSS_HAIR) _(LCUR_Wait, B_CURSOR_ID_PROGRESS) _(LCUR_Ibeam, B_CURSOR_ID_I_BEAM) _(LCUR_SizeVer, B_CURSOR_ID_RESIZE_NORTH_SOUTH) _(LCUR_SizeHor, B_CURSOR_ID_RESIZE_EAST_WEST) _(LCUR_SizeBDiag, B_CURSOR_ID_RESIZE_NORTH_WEST_SOUTH_EAST) _(LCUR_SizeFDiag, B_CURSOR_ID_RESIZE_NORTH_EAST_SOUTH_WEST) _(LCUR_PointingHand, B_CURSOR_ID_GRAB) _(LCUR_Forbidden, B_CURSOR_ID_NOT_ALLOWED) _(LCUR_DropCopy, B_CURSOR_ID_COPY) _(LCUR_DropMove, B_CURSOR_ID_MOVE) // _(LCUR_SizeAll, // _(LCUR_SplitV, // _(LCUR_SplitH, #undef _ } return B_CURSOR_ID_SYSTEM_DEFAULT; } bool LView::_Mouse(LMouse &m, bool Move) { ThreadCheck(); #if DEBUG_MOUSE_EVENTS if (!Move) LgiTrace("%s:%i - %s\n", _FL, m.ToString().Get()); #endif if ( GetWindow() && !GetWindow()->HandleViewMouse(this, m) ) { #if DEBUG_MOUSE_EVENTS LgiTrace("%s:%i - HandleViewMouse consumed event, cls=%s\n", _FL, GetClass()); #endif return false; } #if 0 //DEBUG_MOUSE_EVENTS if (!Move) LgiTrace("%s:%i - _Capturing=%p/%s\n", _FL, _Capturing, _Capturing ? _Capturing->GetClass() : NULL); #endif if (Move) { auto *o = m.Target; if (_Over != o) { #if DEBUG_MOUSE_EVENTS // if (!o) WindowFromPoint(m.x, m.y, true); LgiTrace("%s:%i - _Over changing from %p/%s to %p/%s\n", _FL, _Over, _Over ? _Over->GetClass() : NULL, o, o ? o->GetClass() : NULL); #endif if (_Over) _Over->OnMouseExit(lgi_adjust_click(m, _Over)); _Over = o; if (_Over) _Over->OnMouseEnter(lgi_adjust_click(m, _Over)); } int cursor = GetCursor(m.x, m.y); if (cursor >= 0) { BCursorID haikuId = LgiToHaiku((LCursor)cursor); static BCursorID curId = B_CURSOR_ID_SYSTEM_DEFAULT; if (curId != haikuId) { curId = haikuId; LLocker lck(Handle(), _FL); if (lck.Lock()) { Handle()->SetViewCursor(new BCursor(curId)); lck.Unlock(); } } } } LView *Target = NULL; if (_Capturing) Target = dynamic_cast(_Capturing); else Target = dynamic_cast(_Over ? _Over : this); if (!Target) return false; LRect Client = Target->LView::GetClient(false); m = lgi_adjust_click(m, Target, !Move); if (!Client.Valid() || Client.Overlap(m.x, m.y) || _Capturing) { if (Move) Target->OnMouseMove(m); else Target->OnMouseClick(m); } else if (!Move) { #if DEBUG_MOUSE_EVENTS LgiTrace("%s:%i - Click outside %s %s %i,%i\n", _FL, Target->GetClass(), Client.GetStr(), m.x, m.y); #endif } return true; } LRect &LView::GetClient(bool ClientSpace) { int Edge = (Sunken() || Raised()) ? _BorderSize : 0; static LRect c; if (ClientSpace) { c.ZOff(Pos.X() - 1 - (Edge<<1), Pos.Y() - 1 - (Edge<<1)); } else { c.ZOff(Pos.X()-1, Pos.Y()-1); c.Inset(Edge, Edge); } return c; } LViewI *LView::FindControl(OsView hCtrl) { if (d->Hnd == hCtrl) { return this; } for (auto i : Children) { LViewI *Ctrl = i->FindControl(hCtrl); if (Ctrl) { return Ctrl; } } return 0; } void LView::Quit(bool DontDelete) { ThreadCheck(); if (DontDelete) { Visible(false); } else { delete this; } } bool LView::SetPos(LRect &p, bool Repaint) { if (Pos != p) { Pos = p; LLocker lck(d->Hnd, _FL); if (lck.Lock()) { d->Hnd->ResizeTo(Pos.X(), Pos.Y()); d->Hnd->MoveTo(Pos.x1, Pos.y1); lck.Unlock(); } OnPosChange(); } return true; } bool LView::Invalidate(LRect *rc, bool Repaint, bool Frame) { auto *ParWnd = GetWindow(); if (!ParWnd) return false; // Nothing we can do till we attach if (!InThread()) { DEBUG_INVALIDATE("%s::Invalidate out of thread\n", GetClass()); return PostEvent(M_INVALIDATE, NULL, (LMessage::Param)this); } LRect r; if (rc) { r = *rc; } else { if (Frame) r = Pos.ZeroTranslate(); else r = GetClient().ZeroTranslate(); } DEBUG_INVALIDATE("%s::Invalidate r=%s frame=%i\n", GetClass(), r.GetStr(), Frame); if (!Frame) r.Offset(_BorderSize, _BorderSize); LPoint Offset; WindowVirtualOffset(&Offset); r.Offset(Offset.x, Offset.y); DEBUG_INVALIDATE(" voffset=%i,%i = %s\n", Offset.x, Offset.y, r.GetStr()); if (!r.Valid()) { DEBUG_INVALIDATE(" error: invalid\n"); return false; } static bool Repainting = false; if (!Repainting) { Repainting = true; if (d->Hnd) { LLocker lck(d->Hnd, _FL); if (lck.Lock()) d->Hnd->Invalidate(); } Repainting = false; } else { DEBUG_INVALIDATE(" error: repainting\n"); } return true; } void LView::SetPulse(int Length) { DeleteObj(d->PulseThread); if (Length > 0) d->PulseThread = new LPulseThread(this, Length); } LMessage::Param LView::OnEvent(LMessage *Msg) { ThreadCheck(); int Id; switch (Id = Msg->Msg()) { case M_HANDLE_IN_THREAD: { LMessage::InThreadCb *Cb = NULL; if (Msg->FindPointer(LMessage::PropCallback, (void**)&Cb) == B_OK) { // printf("M_HANDLE_IN_THREAD before call..\n"); (*Cb)(); // printf("M_HANDLE_IN_THREAD after call..\n"); delete Cb; // printf("M_HANDLE_IN_THREAD after delete..\n"); } else printf("%s:%i - No Callback.\n", _FL); break; } case M_INVALIDATE: { if ((LView*)this == (LView*)Msg->B()) { LAutoPtr r((LRect*)Msg->A()); Invalidate(r); } break; } case M_PULSE: { OnPulse(); break; } case M_CHANGE: { LViewI *Ctrl = NULL; if (GetViewById(Msg->A(), Ctrl)) { LNotification n((LNotifyType)Msg->B()); return OnNotify(Ctrl, n); } break; } case M_COMMAND: { // printf("M_COMMAND %i\n", (int)Msg->A()); return OnCommand(Msg->A(), 0, 0); } case M_THREAD_COMPLETED: { auto Th = (LThread*)Msg->A(); if (!Th) break; Th->OnComplete(); if (Th->GetDeleteOnExit()) delete Th; return true; } } return 0; } OsView LView::Handle() const { if (!d) { printf("%s:%i - No priv?\n", _FL); return NULL; } return d->Hnd; } bool LView::PointToScreen(LPoint &p) { if (!Handle()) { LgiTrace("%s:%i - No handle.\n", _FL); return false; } LPoint Offset; WindowVirtualOffset(&Offset); // printf("p=%i,%i offset=%i,%i\n", p.x, p.y, Offset.x, Offset.y); p += Offset; // printf("p=%i,%i\n", p.x, p.y); LLocker lck(Handle(), _FL); if (!lck.Lock()) { LgiTrace("%s:%i - Can't lock.\n", _FL); return false; } BPoint pt = Handle()->ConvertToScreen(BPoint(p.x, p.y)); // printf("pt=%g,%g\n", pt.x, pt.y); p.x = pt.x; p.y = pt.y; // printf("p=%i,%i\n\n", p.x, p.y); return true; } bool LView::PointToView(LPoint &p) { if (!Handle()) { LgiTrace("%s:%i - No handle.\n", _FL); return false; } LPoint Offset; WindowVirtualOffset(&Offset); p -= Offset; LLocker lck(Handle(), _FL); if (!lck.Lock()) { LgiTrace("%s:%i - Can't lock.\n", _FL); return false; } BPoint pt = Handle()->ConvertFromScreen(BPoint(Offset.x, Offset.y)); Offset.x = pt.x; Offset.y = pt.y; return true; } bool LView::GetMouse(LMouse &m, bool ScreenCoords) { LLocker Locker(d->Hnd, _FL); if (!Locker.Lock()) return false; // get mouse state BPoint Cursor; uint32 Buttons; d->Hnd->GetMouse(&Cursor, &Buttons); if (ScreenCoords) d->Hnd->ConvertToScreen(&Cursor); // position m.x = Cursor.x; m.y = Cursor.y; // buttons m.Left(TestFlag(Buttons, B_PRIMARY_MOUSE_BUTTON)); m.Middle(TestFlag(Buttons, B_TERTIARY_MOUSE_BUTTON)); m.Right(TestFlag(Buttons, B_SECONDARY_MOUSE_BUTTON)); // key states key_info KeyInfo; if (get_key_info(&KeyInfo) == B_OK) { m.Ctrl(TestFlag(KeyInfo.modifiers, B_CONTROL_KEY)); m.Alt(TestFlag(KeyInfo.modifiers, B_MENU_KEY)); m.Shift(TestFlag(KeyInfo.modifiers, B_SHIFT_KEY)); } return true; } bool LView::IsAttached() { bool attached = false; LLocker lck(d->Hnd, _FL); if (lck.Lock()) { auto pview = d->Hnd->Parent(); auto pwnd = d->Hnd->Window(); attached = pview != NULL || pwnd != NULL; } return attached; } const char *LView::GetClass() { return "LView"; } bool LView::Attach(LViewI *parent) { bool Status = false; bool Debug = false; // !Stricmp(GetClass(), "LScrollBar"); LView *Parent = d->GetParent(); LAssert(Parent == NULL || Parent == parent); SetParent(parent); Parent = d->GetParent(); auto WndNull = _Window == NULL; _Window = Parent ? Parent->_Window : this; if (!parent) { LgiTrace("%s:%i - No parent window.\n", _FL); } else { auto w = GetWindow(); if (w && TestFlag(WndFlags, GWF_FOCUS)) w->SetFocus(this, LWindow::GainFocus); auto bview = parent->Handle(); if (bview) { LLocker lck(bview, _FL); if (lck.Lock()) { if (Debug) LgiTrace("%s:%i - Attaching %s to view %s\n", _FL, GetClass(), parent->GetClass()); d->Hnd->SetName(GetClass()); if (::IsAttached(d->Hnd)) d->Hnd->RemoveSelf(); bview->AddChild(d->Hnd); d->Hnd->ResizeTo(Pos.X(), Pos.Y()); d->Hnd->MoveTo(Pos.x1, Pos.y1); bool ShowView = TestFlag(GViewFlags, GWF_VISIBLE) && d->Hnd->IsHidden(); if (Debug) LgiTrace("%s:%i - IsHidden=%i ShowView=%i\n", _FL, d->Hnd->IsHidden(), ShowView); if (ShowView) d->Hnd->Show(); Status = true; + + if (d->MsgQue.Length()) + { + printf("%s:%i - %s.Attach msgQue=%i\n", _FL, GetClass(), (int)d->MsgQue.Length()); + for (auto bmsg: d->MsgQue) + d->Hnd->Window()->PostMessage(bmsg); + d->MsgQue.DeleteObjects(); + } if (Debug) LgiTrace("%s:%i - Attached %s/%p to view %s/%p, success\n", _FL, GetClass(), d->Hnd, parent->GetClass(), bview); } else { if (Debug) LgiTrace("%s:%i - Error attaching %s to view %s, can't lock.\n", _FL, GetClass(), parent->GetClass()); } } if (!Parent->HasView(this)) { OnAttach(); if (!d->Parent->HasView(this)) d->Parent->AddView(this); d->Parent->OnChildrenChanged(this, true); } } return Status; } bool LView::Detach() { ThreadCheck(); // Detach view if (_Window) { auto *Wnd = dynamic_cast(_Window); if (Wnd) Wnd->SetFocus(this, LWindow::ViewDelete); _Window = NULL; } LViewI *Par = GetParent(); if (Par) { // Events Par->DelView(this); Par->OnChildrenChanged(this, false); Par->Invalidate(&Pos); } d->Parent = 0; d->ParentI = 0; #if 0 // Windows is not doing a deep detach... so we shouldn't either? { int Count = Children.Length(); if (Count) { int Detached = 0; LViewI *c, *prev = NULL; while ((c = Children[0])) { LAssert(!prev || c != prev); if (c->GetParent()) c->Detach(); else Children.Delete(c); Detached++; prev = c; } LAssert(Count == Detached); } } #endif return true; } LCursor LView::GetCursor(int x, int y) { return LCUR_Normal; } /////////////////////////////////////////////////////////////////// bool LgiIsMounted(char *Name) { return false; } bool LgiMountVolume(char *Name) { return false; } //////////////////////////////// uint32_t CursorData[] = { 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x01010101, 0x02020202, 0x02020202, 0x02010101, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x01020202, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x02020202, 0x02020202, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x01000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x01020202, 0x02020201, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010102, 0x00000000, 0x02020101, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010001, 0x00010001, 0x01000001, 0x02020202, 0x02020202, 0x00010102, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01000102, 0x00000100, 0x02010000, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020100, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00010101, 0x01000000, 0x02020202, 0x00000001, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00000102, 0x01010100, 0x01010101, 0x01000000, 0x02020202, 0x02020201, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x01010102, 0x01010001, 0x02020101, 0x02020202, 0x02020202, 0x01020201, 0x02010000, 0x02020102, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x01000001, 0x02020101, 0x02020202, 0x02020202, 0x00010202, 0x01010000, 0x01020202, 0x00000001, 0x02020201, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01000001, 0x01010101, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x02020101, 0x00000102, 0x00000100, 0x02020201, 0x02020202, 0x01000001, 0x01000000, 0x01020202, 0x02020201, 0x02020202, 0x02020202, 0x02020201, 0x02010001, 0x02010202, 0x02020202, 0x01020202, 0x01020201, 0x02010000, 0x02010102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x01010000, 0x02020101, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x00000102, 0x02020100, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x02010001, 0x00000001, 0x00010201, 0x02020201, 0x02020202, 0x02010001, 0x00000001, 0x00010201, 0x02020201, 0x02020202, 0x01020202, 0x02020201, 0x02010001, 0x01010202, 0x02020202, 0x00010202, 0x01020201, 0x02010000, 0x01000102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02010102, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x00000102, 0x00000001, 0x02020201, 0x00010202, 0x02020100, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01010100, 0x01010101, 0x01000000, 0x02020202, 0x01000001, 0x01000000, 0x01020202, 0x02020201, 0x02020202, 0x02020101, 0x00000102, 0x00000100, 0x02020201, 0x02020202, 0x00010202, 0x02020201, 0x02010001, 0x00010202, 0x02020201, 0x00000102, 0x01010101, 0x01010000, 0x00000101, 0x02020201, 0x01010101, 0x01010101, 0x01010100, 0x01010101, 0x02020201, 0x01000001, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x00000001, 0x00000101, 0x02020100, 0x00010202, 0x02010000, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01010102, 0x01010101, 0x01010001, 0x01010101, 0x02020101, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020100, 0x01020202, 0x02010000, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020201, 0x02020202, 0x02020201, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x01010101, 0x01010001, 0x00010101, 0x02020100, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020100, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x00000001, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x00010202, 0x02010000, 0x01020202, 0x02010000, 0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x01020202, 0x02020100, 0x02020202, 0x00000001, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02010000, 0x00000102, 0x01010101, 0x01010000, 0x00000101, 0x02020201, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x02020201, 0x00000102, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x01020202, 0x01000000, 0x01020202, 0x02010000, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x01010102, 0x01010101, 0x01010001, 0x01010101, 0x02020101, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020101, 0x01020202, 0x02020201, 0x02020202, 0x00000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x01010101, 0x01010001, 0x00010101, 0x02020100, 0x00010202, 0x01020201, 0x02010000, 0x01000102, 0x02020202, 0x01010101, 0x01010101, 0x01010100, 0x01010101, 0x02020201, 0x00010202, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x01000001, 0x02020202, 0x00000001, 0x01020201, 0x02010000, 0x00000001, 0x01000000, 0x02010101, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02010101, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x01000001, 0x02020100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01000001, 0x01010101, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x01020202, 0x02020202, 0x02020202, 0x00000001, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00010202, 0x02020201, 0x02010001, 0x00010202, 0x02020201, 0x01020202, 0x01020201, 0x02010000, 0x02010102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00000000, 0x02010000, 0x02020202, 0x00000001, 0x02020201, 0x00000102, 0x00010100, 0x02010000, 0x00000001, 0x01000001, 0x01020202, 0x01010101, 0x01010101, 0x00000001, 0x01000001, 0x01020202, 0x01010101, 0x01010101, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01000102, 0x01000001, 0x02010001, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x02020201, 0x02020202, 0x01020202, 0x02020201, 0x02010001, 0x01010202, 0x02020202, 0x02020202, 0x01020201, 0x02010000, 0x02020102, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02020100, 0x02020202, 0x00000102, 0x02020201, 0x00010202, 0x00010000, 0x02020100, 0x01000001, 0x01000001, 0x01020202, 0x00000000, 0x01000000, 0x01000001, 0x01000001, 0x01020202, 0x00000000, 0x01000000, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00010001, 0x00000000, 0x01000100, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02010001, 0x02010202, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010100, 0x02020201, 0x02020202, 0x02020202, 0x00000102, 0x00000000, 0x02020201, 0x02020202, 0x00000102, 0x02020100, 0x01020202, 0x00000000, 0x02020100, 0x02010001, 0x00000102, 0x01020201, 0x01010000, 0x01000001, 0x02010001, 0x00000102, 0x01020201, 0x01010100, 0x01000001, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x00010202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01010102, 0x01010001, 0x02020101, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00000102, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01010202, 0x01010101, 0x02020202, 0x02020202, 0x00010202, 0x01010000, 0x01020202, 0x00000001, 0x02020201, 0x02020101, 0x00000102, 0x01020201, 0x00000100, 0x01000100, 0x02020101, 0x00000102, 0x01020201, 0x01000100, 0x01000100, 0x01020202, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x01010101, 0x02020202, 0x02020202, 0x00010102, 0x02020101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x00000000, 0x02020201, 0x02020202, 0x02020202, 0x01020202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x00010202, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x00010101, 0x01000000, 0x02020202, 0x02020202, 0x00010202, 0x01020100, 0x00000100, 0x01000000, 0x02020202, 0x00010202, 0x01020100, 0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01000001, 0x02010000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x02020100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00000001, 0x00000000, 0x02010000, 0x02020202, 0x02020202, 0x00010202, 0x01020100, 0x00000100, 0x01000100, 0x02020202, 0x00010202, 0x01020100, 0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010101, 0x02010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02010001, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020201, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x00010102, 0x00000000, 0x02020101, 0x02020202, 0x02020202, 0x01020202, 0x01020201, 0x01010000, 0x01000001, 0x02020202, 0x01020202, 0x01020201, 0x01000100, 0x01000100, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020102, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x01020202, 0x00000000, 0x01000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x01010101, 0x02020202, 0x02020202, 0x01020202, 0x01010101, 0x01010101, }; diff --git a/src/haiku/Window.cpp b/src/haiku/Window.cpp --- a/src/haiku/Window.cpp +++ b/src/haiku/Window.cpp @@ -1,1155 +1,1010 @@ #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 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; LWindowPrivate(LWindow *wnd) : Wnd(wnd), BWindow(BRect(100,100,400,400), "...loading...", B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 0) { } ~LWindowPrivate() { 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); // printf("Wnd->Pos=%s %i,%i %g,%g\n", Wnd->Pos.GetStr(), Wnd->Pos.X(), Wnd->Pos.Y(), newWidth, newHeight); Wnd->OnPosChange(); } BWindow::FrameResized(newWidth, newHeight); } bool QuitRequested() { // printf("%s::QuitRequested() starting..\n", Wnd->GetClass()); 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); - Wnd->OnEvent((LMessage*)message); + + LView *view = NULL; + auto r = message->FindPointer(LMessage::PropView, &view); + if (r == B_OK) + view->OnEvent((LMessage*)message); + else + Wnd->OnEvent((LMessage*)message); } } }; /////////////////////////////////////////////////////////////////////// -#define GWND_CREATE 0x0010000 - LWindow::LWindow() : LView(0) { d = new LWindowPrivate(this); _QuitOnClose = false; Menu = NULL; _Default = 0; _Window = this; - WndFlags |= GWND_CREATE; ClearFlag(WndFlags, GWF_VISIBLE); } LWindow::~LWindow() { if (LAppInst->AppWnd == this) LAppInst->AppWnd = NULL; LAssert(!Menu); WaitThread(); } int LWindow::WaitThread() { if (!d) return -1; thread_id id = d->Thread(); bool thisThread = id == GetCurrentThreadId(); // printf("%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... // printf("%s::~LWindow posting M_LWINDOW_DELETE from th=%u\n", Name(), GetCurrentThreadId()); d->PostMessage(new BMessage(M_LWINDOW_DELETE)); status_t value = 0; // printf("wait_for_thread(%u) start..\n", id); wait_for_thread(id, &value); // printf("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()) d->Show(); } 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 (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 // 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; } 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; iParent(); 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; } -#if DEBUG_SETFOCUS -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 (int n=MIN(3, p.Length()-1); n>=0; n--) - { - char Buf[256] = ""; - if (!stricmp(v->GetClass(), "LMdiChild")) - sprintf(Buf, "'%s'", v->Name()); - 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::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::SetAlwaysOnTop(bool b) { } diff --git a/src/win/Lgi/Layout.cpp b/src/win/Lgi/Layout.cpp --- a/src/win/Lgi/Layout.cpp +++ b/src/win/Lgi/Layout.cpp @@ -1,218 +1,206 @@ /* ** FILE: GuiViews.cpp ** AUTHOR: Matthew Allen ** DATE: 18/7/98 ** DESCRIPTION: Standard Views ** ** Copyright (C) 1998, Matthew Allen ** fret@memecode.com */ #include #include "lgi/common/Lgi.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/Css.h" #include "lgi/common/Layout.h" ////////////////////////////////////////////////////////////////////////////// LLayout::LLayout() : LView(0) { _SettingScrollBars = 0; _PourLargest = 0; VScroll = 0; HScroll = 0; #if WINNATIVE SetExStyle(GetExStyle() | WS_EX_CONTROLPARENT); #endif } LLayout::~LLayout() { DeleteObj(VScroll); DeleteObj(HScroll); } LViewI *LLayout::FindControl(int Id) { if (VScroll && VScroll->GetId() == Id) return VScroll; if (HScroll && HScroll->GetId() == Id) return HScroll; return LView::FindControl(Id); } void LLayout::GetScrollPos(int64 &x, int64 &y) { if (HScroll) - { x = (int)HScroll->Value(); - } else - { x = 0; - } if (VScroll) - { y = (int)VScroll->Value(); - } else - { y = 0; - } } void LLayout::SetScrollPos(int64 x, int64 y) { if (HScroll) - { HScroll->Value(x); - } if (VScroll) - { VScroll->Value(y); - } } bool LLayout::SetScrollBars(bool x, bool y) { bool Status = false; if (!_SettingScrollBars) { _SettingScrollBars = true; if (y) { if (!VScroll) { VScroll = new LScrollBar; if (VScroll) { VScroll->SetVertical(true); VScroll->SetParent(this); VScroll->SetId(IDC_VSCROLL); Status = true; } } } else { if (VScroll) { DeleteObj(VScroll); Status = true; } } if (x) { if (!HScroll) { HScroll = new LScrollBar; if (HScroll) { HScroll->SetVertical(false); HScroll->SetParent(this); HScroll->SetId(IDC_HSCROLL); Status = true; } } } else { if (HScroll) { DeleteObj(HScroll); Status = true; } } _SettingScrollBars = false; } return Status; } bool LLayout::GetPourLargest() { return _PourLargest; } void LLayout::SetPourLargest(bool i) { _PourLargest = i; } LCss::Len &SelectValid(LCss::Len &a, LCss::Len &b, LCss::Len &c) { if (a.IsValid()) return a; if (b.IsValid()) return b; return c; } bool LLayout::Pour(LRegion &r) { if (!_PourLargest) return false; LRect *Largest = FindLargest(r); if (!Largest) return false; LRect p = *Largest; LCss *css = GetCss(); if (css) { LCss::Len margin = css->Margin(); LCss::Len s; LCss::Len zero; LFont *f = GetFont(); s = css->MarginTop(); p.x1 += SelectValid(s, margin, zero).ToPx(r.X(), f); s = css->MarginTop(); p.y1 += SelectValid(s, margin, zero).ToPx(r.Y(), f); s = css->MarginRight(); p.x2 -= SelectValid(s, margin, zero).ToPx(r.X(), f); s = css->MarginBottom(); p.y2 -= SelectValid(s, margin, zero).ToPx(r.Y(), f); if ((s = css->Width()).IsValid()) p.x2 = p.x1 + s.ToPx(r.X(), f) - 1; if ((s = css->Height()).IsValid()) p.y2 = p.y1 + s.ToPx(r.Y(), f) - 1; } if (p.Valid()) { SetPos(p, true); } else { LAssert(0); return false; } return true; } LMessage::Result LLayout::OnEvent(LMessage *Msg) { if (VScroll) VScroll->OnEvent(Msg); if (HScroll) HScroll->OnEvent(Msg); LMessage::Result Status = LView::OnEvent(Msg); if (Msg->Msg() == M_CHANGE && Status == -1 && GetParent()) { Status = GetParent()->OnEvent(Msg); } return Status; }