diff --git a/include/lgi/common/TimeZoneInfo.h b/include/lgi/common/TimeZoneInfo.h --- a/include/lgi/common/TimeZoneInfo.h +++ b/include/lgi/common/TimeZoneInfo.h @@ -1,522 +1,522 @@ // For reading timezone info files: // https://www.rfc-editor.org/rfc/rfc8536.html #pragma once #ifdef HAIKU #include #endif #include "lgi/common/DateTime.h" class LTimeZoneInfo { static int TimeToSeconds(LString t) { auto parts = t.SplitDelimit(":"); auto hrs = parts[0].Int(); auto min = parts.IdxCheck(1) ? parts[1].Int() : 0; auto sec = parts.IdxCheck(2) ? parts[2].Int() : 0; return (hrs * 3600) + (min * 60) + sec; } struct Header { char magic[4]; char version; char reserved[15]; uint32_t UtCount; uint32_t StdCount; uint32_t LeapCount; uint32_t TimeCount; uint32_t TypeCount; uint32_t CharCount; }; struct LocalTimeType { int32_t UtOffset; uint8_t IsDst; uint8_t Idx; }; struct LeapSecond1 { uint32_t occur; uint32_t corr; }; struct LeapSecond2 { uint64_t occur; uint32_t corr; }; struct PosixTransition { int JulianIdx = -1; // 0 - 365 int Month = 0; // 1 - 12 int Week = 0; // 1 - 5 int Day = 0; // 0 - 6 LString time; int OffsetSeconds(int StdOffsetSeconds) { int sec = StdOffsetSeconds; if (time) { auto TwoAm = 2 * 3600; auto TimeOff = TimeToSeconds(time); auto Offset = TimeOff - TwoAm; sec += Offset; } return sec; } LDateTime Get(int year) { LDateTime dt; if (JulianIdx >= 0) { dt.SetTimeZone(0, false); dt.Year(year); dt.Month(1); dt.Day(1); dt.AddDays(JulianIdx); } else if (Month) { dt.SetTimeZone(0, false); dt.Year(year); dt.Month(Month); dt.Day(1); if (Week == 5) { // Find last instance of 'Day' dt.Day(dt.DaysInMonth()); while (dt.Day() > 1 && dt.DayOfWeek() != Day) dt.AddDays(-1); } else { // Find 'Week'th instance of 'Day' int CurWk = Week; int MonthsDays = dt.DaysInMonth(); while (dt.Day() <= MonthsDays) { int CurDay = dt.DayOfWeek(); if (CurDay == Day) { if (--CurWk == 0) break; } dt.AddDays(1); } } } else LAssert(0); if (time) dt.Hours(time.Int()); else dt.Hours(2); return dt; } bool Set(LString spec) { auto parts = spec.SplitDelimit("/"); auto s = parts[0]; if (parts.Length() > 1) time = parts[1]; if (s(0) == 'J') { JulianIdx = s(1, -1).Int() - 1; return JulianIdx >= 0 && JulianIdx < 365; } else if (s(0) == 'M') { auto parts = s(1, -1).SplitDelimit("."); if (parts.Length() != 3) return false; Month = parts[0].Int(); Week = parts[1].Int(); Day = parts[2].Int(); return Month >= 1 && Month <= 12; } else { JulianIdx = s.Int(); return JulianIdx >= 0 && JulianIdx < 365; } } }; enum TVersion { Version1 = 0, Version2 = '2', Version3 = '3', }; LString data; LPointer ptr; char *end = NULL; bool Eof() { return ptr.c >= end; } ssize_t Remaining() { return end - ptr.c; } Header hdr; LArray TransitionTimes1; LArray TransitionTimes2; LArray TransitionTypes; LArray LocalTimeTypes; LString TimeZoneDesignations; LArray LeapSeconds1; LArray LeapSeconds2; LArray StandardWall; LArray UtLocal; // Format: // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 // Examples: // https://support.cyberdata.net/portal/en/kb/articles/010d63c0cfce3676151e1f2d5442e311 LString footer; bool Read(Header &h) { h.magic[0] = *ptr.c++; h.magic[1] = *ptr.c++; h.magic[2] = *ptr.c++; h.magic[3] = *ptr.c++; if (Strncmp(h.magic, "TZif", 4)) return OnError("bad magic value"); h.version = *ptr.u8++; ptr.c += 15; h.UtCount = ntohl(*ptr.u32++); h.StdCount = ntohl(*ptr.u32++); h.LeapCount = ntohl(*ptr.u32++); h.TimeCount = ntohl(*ptr.u32++); h.TypeCount = ntohl(*ptr.u32++); h.CharCount = ntohl(*ptr.u32++); return !Eof(); } bool Read(LocalTimeType &t) { if (Remaining() < 6) return false; t.UtOffset = ntohl(*ptr.s32++); t.IsDst = *ptr.u8++; t.Idx = *ptr.u8++; return true; } bool Read(LeapSecond1 &ls) { if (Remaining() < 8) return false; ls.occur = ntohl(*ptr.u32++); ls.corr = ntohl(*ptr.u32++); return true; } bool Read(LeapSecond2 &ls) { if (Remaining() < 12) return false; ls.occur = ntohll(*ptr.u64++); ls.corr = ntohl(*ptr.u32++); return true; } template bool Read(LArray &out, size_t elem) { if (elem == 0) return true; size_t bytes = sizeof(T) * elem; if (!out.Length(elem)) return OnError("alloc failed"); if (Remaining() < bytes) return OnError("not enough data left"); memcpy(out.AddressOf(), ptr.vp, bytes); ptr.c += bytes; return true; } bool Read(LString &s) { char *start = ptr.c; while (!Eof() && *ptr.c) ptr.c++; if (ptr.c > start) if (!s.Set(start, ptr.c - start)) return OnError("alloc failed"); ptr.c++; return true; } bool Read(TVersion ver) { if (ver == Version1) { if (!Read(TransitionTimes1, hdr.TimeCount)) return OnError("failed to read transitions"); } else if (ver == Version2) { if (!Read(TransitionTimes2, hdr.TimeCount)) return OnError("failed to read transitions"); for (auto &i: TransitionTimes2) i = ntohll(i); } else return OnError("unsupported version"); if (!Read(TransitionTypes, hdr.TimeCount)) return OnError("failed to read transition types"); if (!LocalTimeTypes.Length(hdr.TypeCount)) return OnError("alloc failed"); for (unsigned i=0; i &Info, LDateTime &Start, LDateTime *End) { auto InRange = [&](LDateTime &dt) { if (End) return dt >= Start && dt < *End; return dt.Year() == (Start.Year()-1) || dt.Year() == Start.Year(); }; for (unsigned i=0; iGet().Get() : NULL); #endif for (int year = Start.Year()-1; year <= (End ? End->Year() : Start.Year()); year++) { // Figure out the start and end DST for 'year' int DstOffset = endTrans.OffsetSeconds(StdOffsetSeconds) / 60; auto startDt = startTrans.Get(year); startDt.SetTimeZone(StdOffsetSeconds / 60, false); if (InRange(startDt)) { auto &s = Info.New(); - s.UtcTimeStamp = startDt.Ts(); + s.Utc = startDt.Ts(); s.Offset = DstOffset; } auto endDt = endTrans.Get(year); endDt.SetTimeZone(StdOffsetSeconds / 60, false); #if 0 printf("\tstart=%s %i, end=%s %i\n", startDt.Get().Get(), InRange(startDt), endDt.Get().Get(), InRange(endDt)); #endif if (InRange(endDt)) { auto &e = Info.New(); - e.UtcTimeStamp = endDt.Ts(); + e.Utc = endDt.Ts(); e.Offset = StdOffsetSeconds / 60; } } } return Info.Length() > 0; } }; diff --git a/src/common/Net/Uri.cpp b/src/common/Net/Uri.cpp --- a/src/common/Net/Uri.cpp +++ b/src/common/Net/Uri.cpp @@ -1,439 +1,439 @@ #include "lgi/common/Lgi.h" #include "lgi/common/Net.h" #include "lgi/common/RegKey.h" #include "lgi/common/Uri.h" ///////////////////////////////////////////////////////////////////////////////// static const char *Ws = " \t\r\n"; #define SkipWs(s) while (*s && strchr(Ws, *s)) s++; LUri::LUri(const char *uri) { if (uri) Set(uri); } LUri::LUri ( const char *proto, const char *user, const char *pass, const char *host, int port, const char *path, const char *anchor ) { sProtocol = proto; sUser = user; sPass = pass; sHost = host; Port = port; sPath = path; sAnchor = anchor; } LUri::~LUri() { Empty(); } LUri &LUri::operator +=(const char *s) { // Add segment to path if (!s) return *this; if (*s == '/') sPath.Empty(); // reset auto parts = sPath.SplitDelimit("/\\"); parts.SetFixedLength(false); for (auto p: LString(s).SplitDelimit("/\\")) { if (p.Equals("..")) parts.PopLast(); else if (p.Equals(".")) ; else parts.Add(p); } sPath = LString(IsFile() ? DIR_STR : "/").Join(parts); return *this; } LUri &LUri::operator =(const LUri &u) { Empty(); sProtocol = u.sProtocol; sUser = u.sUser; sPass = u.sPass; sHost = u.sHost; sPath = u.sPath; sAnchor = u.sAnchor; Port = u.Port; return *this; } void LUri::Empty() { Port = 0; sProtocol.Empty(); sUser.Empty(); sPass.Empty(); sHost.Empty(); sPath.Empty(); sAnchor.Empty(); } LUri::operator bool() { return IsFile() ? !sPath.IsEmpty() : !sHost.IsEmpty(); } LString LUri::LocalPath() { if (!IsFile()) - return NULL; + return LString(); #ifdef WINDOWS if (sPath.Length() > 0 && sPath(0) == '/') return sPath(1, -1).Replace("/", DIR_STR); #endif return sPath.Replace("/", DIR_STR); } LString LUri::ToString() { LStringPipe p; if (sProtocol) p.Print("%s://", sProtocol.Get()); if (sUser || sPass) { auto UserEnc = EncodeStr(sUser, "@:"); auto PassEnc = EncodeStr(sPass, "@:"); p.Print("%s:%s@", UserEnc?UserEnc.Get():"", PassEnc?PassEnc.Get():""); } if (sHost) p.Write(sHost); if (Port) p.Print(":%i", Port); if (sPath) { auto e = EncodeStr(sPath); char *s = e ? e : sPath; p.Print("%s%s", *s == '/' ? "" : "/", s); } if (sAnchor) p.Print("#%s", sAnchor.Get()); return p.NewLStr(); } bool LUri::Set(const char *uri) { if (!uri) return false; Empty(); const char *s = uri; SkipWs(s); // Scan ahead and check for protocol... const char *hasProto = NULL; const char *hasAt = NULL; const char *hasPath = NULL; const char *hasColon = NULL; for (auto c = s; *c; c++) { if (c[0] == ':' && c[1] == '/' && c[2] == '/') { if (!hasProto) { hasProto = c; c += 2; } } else if (c[0] == '@' && !hasAt) { hasAt = c; // keep the first '@' } else if (c[0] == ':') { hasColon = c; } else if ((c[0] == '/' || c[0] == '\\') && !hasPath) { hasPath = c; break; // anything after this is path... } } if (hasProto) { sProtocol.Set(s, hasProto - s); s = hasProto + 3; } if (hasAt) { if (hasAt >= s) { auto p = LString(s, hasAt - s).SplitDelimit(":", 1); if (p.Length() == 2) { sUser = DecodeStr(p[0]); sPass = DecodeStr(p[1]); } else if (p.Length() == 1) { sUser = DecodeStr(p[0]); } s = hasAt + 1; } else LAssert(!"hasAt should be > s"); } bool hasHost = hasProto || hasAt || hasColon || !hasPath; if (hasHost) { auto p = LString(s, hasPath ? hasPath - s : -1).SplitDelimit(":", 1); if (p.Length() == 2) { sHost = p[0]; Port = p[1].Int(); } else if (p.Length() == 1) { sHost = p[0]; } } else { hasPath = s; } if (hasPath) { sPath = hasPath; } if (sPath) { auto anchor = sPath.Find("#"); if (anchor >= 0) { sAnchor = sPath(anchor, -1); sPath.Length(anchor); } } return sHost || sPath; } LString LUri::EncodeStr(const char *s, const char *ExtraCharsToEncode) { LStringPipe p(256); if (s) { while (*s) { if (*s == ' ' || (ExtraCharsToEncode && strchr(ExtraCharsToEncode, *s))) { char h[4]; sprintf_s(h, sizeof(h), "%%%2.2X", (uint32_t)(uchar)*s++); p.Write(h, 3); } else { p.Write(s++, 1); } } } return p.NewLStr(); } LUri::StrMap LUri::Params() { StrMap m; if (sPath) { const char *q = strchr(sPath, '?'); if (q++) { auto Parts = LString(q).SplitDelimit("&"); for (auto p : Parts) { auto Var = p.Split("=", 1); if (Var.Length() == 2) m.Add(Var[0], Var[1]); } } } return m; } LString LUri::DecodeStr(const char *s) { LStringPipe p(256); if (s) { while (*s) { if (s[0] == '%' && s[1] && s[2]) { char h[3] = { s[1], s[2], 0 }; char c = htoi(h); p.Write(&c, 1); s += 3; } else { p.Write(s++, 1); } } } return p.NewLStr(); } struct UriUnitCase { const char *str; LUri uri; }; bool LUri::UnitTests() { UriUnitCase Parse[] = { {"http://user:pass@host:1234/somePath/seg/file.png", LUri("http", "user", "pass", "host", 1234, "somePath/seg/file.png")}, {"user:pass@host:1234/somePath/seg/file.png", LUri(NULL, "user", "pass", "host", 1234, "somePath/seg/file.png") }, {"user@host:1234/somePath/seg/file.png", LUri(NULL, "user", NULL, "host", 1234, "somePath/seg/file.png") }, {"user@host/somePath/seg/file.png", LUri(NULL, "user", NULL, "host", 0, "somePath/seg/file.png") }, {"user@host", LUri(NULL, "user", NULL, "host", 0, NULL) }, {"host", LUri(NULL, NULL, NULL, "host", 0, NULL) }, {"host:1234", LUri(NULL, NULL, NULL, "host", 1234, NULL) }, {"somePath/seg/file.png", LUri(NULL, NULL, NULL, NULL, 0, "somePath/seg/file.png") }, }; for (auto &test: Parse) { LUri u(test.str); if (u != test.uri) { LAssert(!"test failed"); return false; } } return true; } /////////////////////////////////////////////////////////////////////////////////////////////////////// #if defined LGI_CARBON int CFNumberRefToInt(CFNumberRef r, int Default = 0) { int i = Default; if (r && CFGetTypeID(r) == CFNumberGetTypeID()) { CFNumberGetValue(r, kCFNumberIntType, &r); } return i; } #endif LProxyUri::LProxyUri() { #if defined(WIN32) LRegKey k(false, "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); if (k.IsOk()) { uint32_t Enabled = 0; if (k.GetInt("ProxyEnable", Enabled) && Enabled) { char *p = k.GetStr("ProxyServer"); if (p) { Set(p); } } } #elif defined LINUX char *HttpProxy = getenv("http_proxy"); if (HttpProxy) { Set(HttpProxy); } #elif defined MAC // CFDictionaryRef Proxies = SCDynamicStoreCopyProxies(0); // if (!Proxies) // LgiTrace("%s:%i - SCDynamicStoreCopyProxies failed.\n", _FL); // else // { // int enable = CFNumberRefToInt((CFNumberRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPEnable)); // if (enable) // { // #ifdef LGI_COCOA // LAssert(!"Fixme"); // #else // Host = CFStringToUtf8((CFStringRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPProxy)); // #endif // Port = CFNumberRefToInt((CFNumberRef) CFDictionaryGetValue(Proxies, kSCPropNetProxiesHTTPPort)); // } // // CFRelease(Proxies); // } #elif defined(HAIKU) // There doesn't seem to be a system wide proxy setting, so for the time being // lets just put a setting in the Lgi config and use that: if (!LAppInst) { LgiTrace("%s:%i - No LApp instance yet?\n", _FL); } else { auto p = LAppInst->GetConfig(LApp::CfgNetworkHttpProxy); if (p) { Set(p); } else { static bool First = true; if (First) { First = false; LgiTrace("%s:%i No HTTP Proxy configured in '%s'.\n", _FL, LAppInst->GetConfigPath().Get()); } } } #else #warning "Impl getting OS proxy here." #endif } diff --git a/src/haiku/Window.cpp b/src/haiku/Window.cpp --- a/src/haiku/Window.cpp +++ b/src/haiku/Window.cpp @@ -1,1326 +1,1326 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/DragAndDrop.h" #include "lgi/common/Token.h" #include "lgi/common/Popup.h" #include "lgi/common/Panel.h" #include "lgi/common/Notifications.h" #include "lgi/common/Menu.h" #include "ViewPriv.h" #include "MenuBar.h" #define DEBUG_SETFOCUS 0 #define DEBUG_HANDLEVIEWKEY 0 #define DEBUG_WAIT_THREAD 0 #define DEBUG_SERIALIZE_STATE 0 #if DEBUG_WAIT_THREAD #define WAIT_LOG(...) LgiTrace(__VA_ARGS__) #else #define WAIT_LOG(...) #endif LString ToString(BRect &r) { LString s; s.Printf("%g,%g,%g,%g", r.left, r.top, r.right, r.bottom); return s; } /////////////////////////////////////////////////////////////////////// class HookInfo { public: LWindowHookType Flags; LView *Target; }; enum LAttachState { LUnattached, LAttaching, LAttached, LDetaching, }; class LWindowPrivate : public BWindow { public: LWindow *Wnd; bool SnapToEdge = false; LArray Hooks; LWindow *ModalParent = NULL; LWindow *ModalChild = NULL; bool ShowTitleBar = true; bool ThreadMsgDone = false; LString MakeName(LWindow *w) { LString s; s.Printf("%p::con::%s", w, w->GetClass()); // printf("%s\n", s.Get()); return s; } LWindowPrivate(LWindow *wnd) : BWindow(BRect(100,100,400,400), MakeName(wnd), B_DOCUMENT_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, 0), Wnd(wnd) { } ~LWindowPrivate() { #if 0 printf("%p::~LWindowPrivate start, locking=%i, cur=%i, thread=%i\n", this, LockingThread(), LCurrentThreadId(), Thread()); #endif LAssert(ModalChild == NULL); DeleteObj(Wnd->Menu); if (Thread() >= 0 && IsMinimized()) Wnd->_PrevZoom = LZoomMin; Wnd->d = NULL; Lock(); // printf("%p::~LWindowPrivate end\n", this); } window_look DefaultLook() { if (ShowTitleBar) return B_DOCUMENT_WINDOW_LOOK; else return B_NO_BORDER_WINDOW_LOOK; } int GetHookIndex(LView *Target, bool Create = false) { for (int i=0; iTarget = Target; n->Flags = LNoEvents; return Hooks.Length() - 1; } } return -1; } void FrameMoved(BPoint newPosition) { auto Pos = Frame(); if (Pos != Wnd->Pos) { Wnd->Pos = Pos; Wnd->OnPosChange(); } BWindow::FrameMoved(newPosition); } void FrameResized(float newWidth, float newHeight) { auto Pos = Frame(); if (Pos != Wnd->Pos) { Wnd->Pos.SetSize(newWidth, newHeight); Wnd->OnPosChange(); } BWindow::FrameResized(newWidth, newHeight); } bool QuitRequested() { /* printf("%p::QuitRequested() starting.. pos=%s cls=%s\n", Wnd, Wnd->Pos.GetStr(), 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", LCurrentThreadId()); Wnd->Handle()->RemoveSelf(); Quit(); // printf("Processed M_LWINDOW_DELETE\n"); } else { BWindow::MessageReceived(message); LView *view = NULL; auto r = message->FindPointer(LMessage::PropView, (void**)&view); if (r == B_OK) { if (!LView::RecentlyDeleted(view)) view->OnEvent((LMessage*)message); /* else printf("%s:%i - Dropping message to deleted view.\n", _FL); */ } else { Wnd->OnEvent((LMessage*)message); } } } void WindowActivated(bool focus) { // printf("%s::WindowActivated %i\n", Wnd->GetClass(), focus); if (!ThreadMsgDone) { ThreadMsgDone = true; LString n; n.Printf("%s/%s", Wnd->GetClass(), Wnd->Name()); LThread::RegisterThread(Thread(), n); } if (ModalChild && focus) { printf("%s:%i - %s dropping activate, has modal: %s.\n", _FL, Wnd->GetClass(), ModalChild->GetClass()); SendBehind(ModalChild->WindowHandle()); auto w = Wnd; while (auto p = w->GetModalParent()) { p->WindowHandle()->SendBehind(w->WindowHandle()); w = p; } } else { BWindow::WindowActivated(focus); } } }; /////////////////////////////////////////////////////////////////////// LWindow::LWindow() : LView(0) { d = new LWindowPrivate(this); _QuitOnClose = false; Menu = NULL; _Default = 0; _Window = this; ClearFlag(WndFlags, GWF_VISIBLE); } LWindow::~LWindow() { if (LAppInst->AppWnd == this) LAppInst->AppWnd = NULL; LAssert(!Menu); WaitThread(); } LWindow *LWindow::GetModalParent() { return d->ModalParent; } bool LWindow::SetModalParent(LWindow *p) { if (p) { if (d->ModalParent || p->GetModalChild()) { printf("%s:%i - this=%s p=%s d->ModalParent=%s p->GetModalChild()=%s\n", _FL, GetClass(), p?p->GetClass():NULL, d->ModalParent?d->ModalParent->GetClass():NULL, p->GetModalChild()?p->GetModalChild()->GetClass():NULL); LAssert(!"Already set!"); return false; } d->ModalParent = p; p->d->ModalChild = this; } else { if (d->ModalParent) { if (d->ModalParent->GetModalChild() != this) { LAssert(!"Wrong linkage."); return false; } d->ModalParent->d->ModalChild = NULL; d->ModalParent = NULL; } } return true; } LWindow *LWindow::GetModalChild() { return d->ModalChild; } bool LWindow::SetModalChild(LWindow *c) { if (c) { if (d->ModalChild || c->GetModalParent()) { LAssert(!"Already set."); return false; } d->ModalChild = c; c->d->ModalParent = this; } else { if (d->ModalChild) { if (d->ModalChild->GetModalParent() != this) { LAssert(!"Wrong linkage."); return false; } d->ModalChild->d->ModalParent = NULL; d->ModalChild = NULL; } } return true; } int LWindow::WaitThread() { if (!d) return -1; thread_id id = d->Thread(); bool thisThread = id == LCurrentThreadId(); WAIT_LOG("%s::~LWindow thread=%u lock=%u\n", Name(), LCurrentThreadId(), 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()); } DeleteObj(d); return 0; // If we're in thread... no need to wait. } status_t value = B_OK; if (d->Thread() >= 0) { // Post event to the window's thread to delete itself... WAIT_LOG("%s::~LWindow posting M_LWINDOW_DELETE from th=%u\n", Name(), LCurrentThreadId()); d->PostMessage(new BMessage(M_LWINDOW_DELETE)); WAIT_LOG("wait_for_thread(%u) start..\n", id); wait_for_thread(id, &value); WAIT_LOG("wait_for_thread(%u) end=%i\n", id, value); } else printf("%s:%i - No thread to wait for: %s\n", _FL, GetClass()); DeleteObj(d); return value; } OsWindow LWindow::WindowHandle() { return d; } bool LWindow::SetTitleBar(bool ShowTitleBar) { if (!d) return false; d->ShowTitleBar = ShowTitleBar; LLocker lck(d, _FL); auto r = d->SetLook(d->DefaultLook()); if (r) printf("%s:%i - SetFeel failed: %i\n", _FL, r); return r == B_OK; } 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.WaitForLock()) return false; return !d->IsHidden(); } void LWindow::Visible(bool i) { LLocker lck(d, _FL); if (!lck.WaitForLock()) { printf("%s:%i - Can't lock.\n", _FL); return; } if (i) { if (d->IsHidden()) { d->MoveTo(Pos.x1, Pos.y1); d->ResizeTo(Pos.X(), Pos.Y()); d->Show(); } else printf("%s already shown\n", GetClass()); } else { if (!d->IsHidden()) d->Hide(); } } bool LWindow::Obscured() { return false; } bool DndPointMap(LViewI *&v, LPoint &p, LDragDropTarget *&t, LWindow *Wnd, int x, int y) { LRect cli = Wnd->GetClient(); t = NULL; v = Wnd->WindowFromPoint(x - cli.x1, y - cli.y1, false); if (!v) { LgiTrace("%s:%i - @ %i,%i\n", _FL, x, y); return false; } v->WindowVirtualOffset(&p); p.x = x - p.x; p.y = y - p.y; for (LViewI *view = v; !t && view; view = view->GetParent()) t = view->DropTarget(); if (t) return true; LgiTrace("%s:%i - No target for %s\n", _FL, v->GetClass()); return false; } void LWindow::UpdateRootView() { auto rootView = Handle(); if (!rootView) { printf("%s:%i - Can't update root view: no view handle.\n", _FL); return; } auto wnd = WindowHandle(); auto looper = rootView->Looper(); auto isLocked = looper->IsLocked(); if (!isLocked) { printf("%s:%i - Can't update root view: not locked.\n", _FL); return; } if (rootView->IsHidden()) { printf("%s::UpdateRootView() curThread=%i/%s wndThread=%i/%s\n", GetClass(), LCurrentThreadId(), LThread::GetThreadName(LCurrentThreadId()), wnd->Thread(), LThread::GetThreadName(wnd->Thread()) ); 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); } 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); UpdateRootView(); } // Setup default button... if (!_Default) _Default = FindControl(IDOK); // Do a rough layout of child windows PourAll(); return true; } bool LWindow::OnRequestClose(bool OsShuttingDown) { if (GetQuitOnClose()) LCloseApp(); return LView::OnRequestClose(OsShuttingDown); } bool LWindow::HandleViewMouse(LView *v, LMouse &m) { if (d->ModalChild) { /* printf("%s:%i - %s ignoring mouse event while %s is shown.\n", _FL, GetClass(), d->ModalDlg->GetClass()); */ return false; } if (m.Down() && !m.IsMove()) { bool InPopup = false; for (LViewI *p = v; p; p = p->GetParent()) { if (dynamic_cast(p)) { InPopup = true; break; } } if (!InPopup && LPopup::CurrentPopups.Length()) { for (int i=0; iVisible()) p->Visible(false); } } } for (int i=0; iHooks.Length(); i++) { if (d->Hooks[i].Flags & LMouseEvents) { if (!d->Hooks[i].Target->OnViewMouse(v, m)) { return false; } } } return true; } bool LWindow::HandleViewKey(LView *v, LKey &k) { bool Status = false; LViewI *Ctrl = 0; #if DEBUG_HANDLEVIEWKEY bool Debug = 1; // k.vkey == LK_RETURN; char SafePrint = k.c16 < ' ' ? ' ' : k.c16; // if (Debug) { LgiTrace("%s/%p::HandleViewKey=%i ischar=%i %s%s%s%s\n", v->GetClass(), v, k.c16, k.IsChar, (char*)(k.Down()?" Down":" Up"), (char*)(k.Shift()?" Shift":""), (char*)(k.Alt()?" Alt":""), (char*)(k.Ctrl()?" Ctrl":"")); } #endif if (d->ModalChild) { /* printf("%s:%i - %s ignoring key event while %s is shown.\n", _FL, GetClass(), d->ModalChild->GetClass()); */ return false; } // Any window in a popup always gets the key... LViewI *p; for (p = v->GetParent(); p; p = p->GetParent()) { if (dynamic_cast(p)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tSending key to popup\n"); #endif return v->OnKey(k); } } // Give key to popups if (LAppInst && LAppInst->GetMouseHook() && LAppInst->GetMouseHook()->OnViewKey(v, k)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tMouseHook got key\n"); #endif goto AllDone; } // Allow any hooks to see the key... for (int i=0; iHooks.Length(); i++) { #if DEBUG_HANDLEVIEWKEY // if (Debug) LgiTrace("\tHook[%i]\n", i); #endif if (d->Hooks[i].Flags & LKeyEvents) { LView *Target = d->Hooks[i].Target; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tHook[%i].Target=%p %s\n", i, Target, Target->GetClass()); #endif if (Target->OnViewKey(v, k)) { Status = true; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tHook[%i] ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", i, SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } } } // Give the key to the window... if (v->OnKey(k)) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tView ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif Status = true; goto AllDone; } #if DEBUG_HANDLEVIEWKEY else if (Debug) LgiTrace("\t%s didn't eat '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", v->GetClass(), SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif // Window didn't want the key... switch (k.vkey) { case LK_RETURN: #ifdef LK_KEYPADENTER case LK_KEYPADENTER: #endif { Ctrl = _Default; break; } case LK_ESCAPE: { Ctrl = FindControl(IDCANCEL); break; } } // printf("Ctrl=%p\n", Ctrl); if (Ctrl) { if (Ctrl->Enabled()) { if (Ctrl->OnKey(k)) { Status = true; #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tDefault Button ate '%c'(%i) down=%i alt=%i ctrl=%i sh=%i\n", SafePrint, k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif goto AllDone; } // else printf("OnKey()=false\n"); } // else printf("Ctrl=disabled\n"); } #if DEBUG_HANDLEVIEWKEY else if (Debug) LgiTrace("\tNo default ctrl to handle key.\n"); #endif if (Menu) { Status = Menu->OnKey(v, k); if (Status) { #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tMenu ate '%c' down=%i alt=%i ctrl=%i sh=%i\n", k.c16, k.Down(), k.Alt(), k.Ctrl(), k.Shift()); #endif } } // Tab through controls if (k.vkey == LK_TAB && k.Down() && !k.IsChar) { LViewI *Wnd = GetNextTabStop(v, k.Shift()); #if DEBUG_HANDLEVIEWKEY if (Debug) LgiTrace("\tTab moving focus shift=%i Wnd=%p\n", k.Shift(), Wnd); #endif if (Wnd) Wnd->Focus(true); } // Control shortcut? if (k.Down() && k.Alt() && k.c16 > ' ') { ShortcutMap Map; BuildShortcuts(Map); LViewI *c = Map.Find(ToUpper(k.c16)); if (c) { c->OnNotify(c, LNotifyActivate); return true; } } AllDone: return Status; } void LWindow::Raise() { } LWindowZoom LWindow::GetZoom() { if (!d) return _PrevZoom; LLocker lck(d, _FL); if (!IsAttached() || lck.Lock()) { if (d->IsMinimized()) return LZoomMin; } return LZoomNormal; } void LWindow::SetZoom(LWindowZoom i) { LLocker lck(d, _FL); if (lck.Lock()) { d->Minimize(i == LZoomMin); } } LViewI *LWindow::GetDefault() { return _Default; } void LWindow::SetDefault(LViewI *v) { if (v && v->GetWindow() == this) { if (_Default != v) { LViewI *Old = _Default; _Default = v; if (Old) Old->Invalidate(); if (_Default) _Default->Invalidate(); } } else { _Default = 0; } } bool LWindow::Name(const char *n) { LLocker lck(d, _FL); if (lck.Lock()) d->SetTitle(n); return LBase::Name(n); } const char *LWindow::Name() { return LBase::Name(); } -LPoint LWindow::GetDpi() +LPoint LWindow::GetDpi() const { 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()) { LRect br = Handle()->Bounds(); if (br.Valid()) { r = br; auto frm = d->Frame(); frm.OffsetBy(-frm.left, -frm.top); // printf("%s:%i - LWindow::GetClient: Frame=%s Bounds=%s r=%s\n", _FL, ToString(frm).Get(), br.GetStr(), r.GetStr()); } // else printf("%s:%i - LWindow::GetClient: Bounds not valid.\n", _FL); lck.Unlock(); } // else printf("%s:%i - LWindow::GetClient: no lock.\n", _FL); return r; } #if DEBUG_SERIALIZE_STATE #define SERIALIZE_LOG(...) printf(__VA_ARGS__) #else #define SERIALIZE_LOG(...) #endif bool LWindow::SerializeState(LDom *Store, const char *FieldName, bool Load) { if (!Store || !FieldName) return false; if (Load) { ::LVariant v; if (Store->GetValue(FieldName, v) && v.Str()) { LRect Position(0, 0, -1, -1); LWindowZoom State = LZoomNormal; auto vars = LString(v.Str()).SplitDelimit(";"); SERIALIZE_LOG("SerializeState: %s=%s, vars=%i\n", FieldName, v.Str(), (int)vars.Length()); for (auto var: vars) { auto parts = var.SplitDelimit("=", 1); SERIALIZE_LOG("SerializeState: parts=%i\n", (int)parts.Length()); if (parts.Length() == 2) { if (parts[0].Equals("State")) { State = (LWindowZoom) parts[1].Int(); SERIALIZE_LOG("SerializeState: part=%s state=%i\n", parts[0].Get(), State); } else if (parts[0].Equals("Pos")) { Position.SetStr(parts[1]); SERIALIZE_LOG("SerializeState: part=%s pos=%s\n", parts[0].Get(), Position.GetStr()); } } } if (Position.Valid()) { SERIALIZE_LOG("SerializeState setpos %s\n", Position.GetStr()); SetPos(Position); } SetZoom(State); } else { SERIALIZE_LOG("SerializeState: no '%s' var\n", FieldName); return false; } } else { char s[256]; LWindowZoom State = GetZoom(); sprintf_s(s, sizeof(s), "State=%i;Pos=%s", State, GetPos().GetStr()); LVariant v = s; SERIALIZE_LOG("SerializeState: saving '%s' = '%s'\n", FieldName, s); if (!Store->SetValue(FieldName, v)) { SERIALIZE_LOG("SerializeState: SetValue failed\n"); return false; } } return true; } LRect &LWindow::GetPos() { return Pos; } bool LWindow::SetPos(LRect &p, bool Repaint) { Pos = p; LLocker lck(d, _FL); if (lck.Lock()) { d->MoveTo(Pos.x1, Pos.y1); d->ResizeTo(Pos.X(), Pos.Y()); } else printf("%s:%i - Failed to lock.\n", _FL); return true; } void LWindow::OnChildrenChanged(LViewI *Wnd, bool Attaching) { // Force repour Invalidate(); } void LWindow::OnCreate() { AttachChildren(); } void LWindow::OnPaint(LSurface *pDC) { pDC->Colour(L_MED); pDC->Rectangle(); } void LWindow::OnPosChange() { LLocker lck(WindowHandle(), _FL); if (lck.Lock()) { auto frame = WindowHandle()->Bounds(); auto menu = WindowHandle()->KeyMenuBar(); auto menuPos = menu ? menu->Frame() : BRect(0, 0, 0, 0); auto rootPos = Handle()->Frame(); if (menu) { if (menu->IsHidden()) // Why? { menu->Show(); if (menu->IsHidden()) { // printf("Can't show menu?\n"); for (auto p = menu->Parent(); p; p = p->Parent()) printf(" par=%s %i\n", p->Name(), p->IsHidden()); } } if (menuPos.Width() < 1) // Again... WHHHHY? FFS { float x = 0.0f, y = 0.0f; menu->GetPreferredSize(&x, &y); // printf("Pref=%g,%g\n", x, y); if (y > 0.0f) menu->ResizeTo(frame.Width(), y); } } #if 0 printf("frame=%s menu=%p,%i,%s rootpos=%s\n", ToString(frame).Get(), menu, menu?menu->IsHidden():0, ToString(menuPos).Get(), ToString(rootPos).Get()); #endif int rootTop = menu ? menuPos.bottom + 1 : 0; if (rootPos.top != rootTop) { Handle()->MoveTo(0, rootTop); Handle()->ResizeTo(rootPos.Width(), frame.Height() - menuPos.Height()); } lck.Unlock(); } LView::OnPosChange(); PourAll(); } #define IsTool(v) \ ( \ dynamic_cast(v) \ && \ dynamic_cast(v)->_IsToolBar \ ) void LWindow::PourAll() { LRect c = GetClient(); LRegion Client(c); LViewI *MenuView = 0; LRegion Update(Client); bool HasTools = false; LViewI *v; List::I Lst = Children.begin(); { LRegion Tools; for (v = *Lst; v; v = *++Lst) { bool IsMenu = MenuView == v; if (!IsMenu && IsTool(v)) { LRect OldPos = v->GetPos(); if (OldPos.Valid()) Update.Union(&OldPos); if (HasTools) { // 2nd and later toolbars if (v->Pour(Tools)) { if (!v->Visible()) { v->Visible(true); } auto vpos = v->GetPos(); if (OldPos != vpos) { // position has changed update... v->Invalidate(); } // Has it increased the size of the toolbar area? auto b = Tools.Bound(); if (vpos.y2 >= b.y2) { LRect Bar = Client; Bar.y2 = vpos.y2; Client.Subtract(&Bar); // LgiTrace("IncreaseToolbar=%s\n", Bar.GetStr()); } Tools.Subtract(&vpos); Update.Subtract(&vpos); // LgiTrace("vpos=%s\n", vpos.GetStr()); } } else { // First toolbar if (v->Pour(Client)) { HasTools = true; if (!v->Visible()) { v->Visible(true); } if (OldPos != v->GetPos()) { v->Invalidate(); } LRect Bar(v->GetPos()); // LgiTrace("%s = %s\n", v->GetClass(), Bar.GetStr()); Bar.x2 = GetClient().x2; Tools = Bar; Tools.Subtract(&v->GetPos()); Client.Subtract(&Bar); Update.Subtract(&Bar); } } } } } Lst = Children.begin(); for (LViewI *v = *Lst; v; v = *++Lst) { bool IsMenu = MenuView == v; if (!IsMenu && !IsTool(v)) { LRect OldPos = v->GetPos(); if (OldPos.Valid()) Update.Union(&OldPos); if (v->Pour(Client)) { // LRect p = v->GetPos(); // LgiTrace("%s = %s\n", v->GetClass(), p.GetStr()); if (!v->Visible()) v->Visible(true); v->Invalidate(); Client.Subtract(&v->GetPos()); Update.Subtract(&v->GetPos()); } else { // non-pourable } } } for (int i=0; iMsg()) { case M_CLOSE: { if (OnRequestClose(false)) { Quit(); return 0; } break; } } return LView::OnEvent(m); } bool LWindow::RegisterHook(LView *Target, LWindowHookType EventType, int Priority) { bool Status = false; if (Target && EventType) { int i = d->GetHookIndex(Target, true); if (i >= 0) { d->Hooks[i].Flags = EventType; Status = true; } } return Status; } bool LWindow::UnregisterHook(LView *Target) { int i = d->GetHookIndex(Target); if (i >= 0) { d->Hooks.DeleteAt(i); return true; } return false; } void LWindow::OnFrontSwitch(bool b) { } bool LWindow::SetWillFocus(bool f) { if (!d) return false; LLocker lck(d, _FL); auto flags = d->Flags(); if (f) flags &= ~B_AVOID_FOCUS; // clear flag else flags |= B_AVOID_FOCUS; // set flag auto r = d->SetFlags(flags); if (r) printf("%s:%i - SetFlags failed: %i\n", _FL, r); return true; } LViewI *LWindow::GetFocus() { if (!d) return NULL; LLocker lck(d, _FL); if (!lck.WaitForLock()) return NULL; auto f = d->CurrentFocus(); return LViewFromHandle(f); } void LWindow::SetFocus(LViewI *ctrl, FocusType type) { if (!ctrl) return; LLocker lck(d, _FL); if (lck.WaitForLock()) { auto h = ctrl->Handle(); h->MakeFocus(type == GainFocus ? true : false); } } void LWindow::SetDragHandlers(bool On) { } void LWindow::OnTrayClick(LMouse &m) { if (m.Down() || m.IsContextMenu()) { LSubMenu RClick; OnTrayMenu(RClick); if (GetMouse(m, true)) { int Result = RClick.Float(this, m.x, m.y); OnTrayMenuResult(Result); } } } void LWindow::SetAlwaysOnTop(bool b) { if (!d) return; LLocker lck(d, _FL); status_t r; if (b) r = d->SetFeel(B_FLOATING_APP_WINDOW_FEEL); else r = d->SetFeel(B_NORMAL_WINDOW_FEEL); if (r) printf("%s:%i - SetFeel failed: %i\n", _FL, r); }