diff --git a/lvc/src/BrowseUi.cpp b/lvc/src/BrowseUi.cpp --- a/lvc/src/BrowseUi.cpp +++ b/lvc/src/BrowseUi.cpp @@ -1,333 +1,386 @@ #include "lgi/common/Lgi.h" #include "lgi/common/TabView.h" #include "lgi/common/TableLayout.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Edit.h" #include "lgi/common/List.h" #include "lgi/common/TextLog.h" +#include "lgi/common/ClipBoard.h" #include "Lvc.h" #include "VcFolder.h" #define OPT_WND_STATE "BrowseUiState" #define USE_RELATIVE_TIMES 1 -enum Ctrls { +enum Ids { IDC_STATIC = -1, IDC_FILTER = 100, IDC_TABLE, + + IDM_COPY_USER, + IDM_COPY_REF, + IDM_GOTO_LINE }; struct BrowseUiPriv { LList *Blame = NULL; LList *Log = NULL; LTextLog *Raw = NULL; LTabView *Tabs = NULL; AppPriv *Priv = NULL; VcFolder *Folder = NULL; LTableLayout *Tbl = NULL; BrowseUi::TMode Mode; LString Output; LString Path; LString UserHilight; LFont Mono; LArray Commits; LHashTbl, LColour*> Colours; int NextHue = 0; int NextLum = 140; LColour *NewColour() { LColour *hls; if (hls = new LColour) hls->SetHLS(NextHue, NextLum, 128); NextHue += 30; if (NextHue >= 360) { NextHue = 0; NextLum -= 32; } return hls; } BrowseUiPriv(BrowseUi::TMode mode, LString path) : Mode(mode), Path(path) { LCss css; css.FontFamily(LCss::FontFamilyMonospace); auto status = Mono.CreateFromCss(&css); LAssert(status); LDisplayString ds(&Mono, "1234"); Mono.TabSize(ds.X()); } ~BrowseUiPriv() { Colours.DeleteObjects(); Commits.DeleteObjects(); } void GotoRef(LString ref) { auto wnd = Blame->GetWindow(); Folder->SelectCommit(wnd, ref, Path); } - void GotoSource(const char *line) - { - } + void GotoLine(const char *line); }; enum LineIdx { TUser, TRef, TDate, TLine, TSrc }; struct BrowseItem : public LListItem { BrowseUiPriv *d = NULL; LColour *refColour = NULL; BrowseItem(BrowseUiPriv *priv, BlameLine &ln, LDateTime &now, LColour *refCol, int &lineNo) : refColour(refCol) { d = priv; SetText(ln.user, TUser); SetText(ln.ref, TRef); #if USE_RELATIVE_TIMES LDateTime dt; if (ln.date && dt.Set(ln.date)) SetText(dt.DescribePeriod(now), TDate); else #endif SetText(ln.date, TDate); if (ln.line) SetText(ln.line, TLine); else SetText(LString::Fmt("%i", lineNo), TLine); SetText(ln.src, TSrc); lineNo++; } void OnPaintColumn(LItem::ItemPaintCtx &Ctx, int i, LItemColumn *c) override { LColour old = Ctx.Fore; if (!Select()) { if (i == TUser) Ctx.Fore = LColour::Blue; else if (i == TRef && refColour) Ctx.Fore = *refColour; else if (i == TDate || i == TLine) Ctx.Fore.Rgb(192, 192, 192); } if (i == TLine) Ctx.Align = LCss::AlignRight; const char *src; if (i == TSrc && (src = GetText(TSrc)) ) { d->Mono.Transparent(false); d->Mono.Colour(Ctx.Fore, Ctx.Back); LDisplayString ds(&d->Mono, src); ds.Draw(Ctx.pDC, Ctx.x1, Ctx.y1, &Ctx); } else { LListItem::OnPaintColumn(Ctx, i, c); } Ctx.Fore = old; } void OnMouseClick(LMouse &m) override { int Col = -1; GetList()->GetColumnClickInfo(Col, m); - if (m.Down() && - m.Double()) + if (m.IsContextMenu()) + { + LSubMenu sub; + sub.AppendItem("Copy user", IDM_COPY_USER); + sub.AppendItem("Copy ref", IDM_COPY_REF); + sub.AppendItem("Goto line..", IDM_GOTO_LINE); + + switch (sub.Float(GetList(), m)) + { + case IDM_COPY_USER: + { + LClipBoard c(GetList()); + c.Text(GetText(TUser)); + break; + } + case IDM_COPY_REF: + { + LClipBoard c(GetList()); + c.Text(GetText(TRef)); + break; + } + case IDM_GOTO_LINE: + { + auto input = new LInput(GetList(), "", "Goto line:", AppName); + input->DoModal([this, input](auto dlg, auto code) + { + if (code) + d->GotoLine(input->GetStr()); + delete dlg; + }); + break; + } + } + } + else if (m.Down() && + m.Double()) { LAssert(d->Folder); switch (Col) { case TUser: { // Highlight all by that user? d->UserHilight = GetText(TUser); GetList()->UpdateAllItems(); break; } case TRef: { d->GotoRef(GetText(TRef)); break; } case TSrc: { - d->GotoSource(GetText(TLine)); + d->GotoLine(GetText(TLine)); break; } } } LListItem::OnMouseClick(m); } }; +void BrowseUiPriv::GotoLine(const char *line) +{ + LArray all; + if (Blame->GetAll(all)) + { + for (auto b: all) + { + auto ln = b->GetText(TLine); + auto match = !Stricmp(ln, line); + b->Select(match); + if (match) + b->ScrollTo(); + } + } +} + BrowseUi::BrowseUi(TMode mode, AppPriv *priv, VcFolder *folder, LString path) { d = new BrowseUiPriv(mode, path); d->Folder = folder; d->Priv = priv; Name("Lvc Browse"); if (!SerializeState(&d->Priv->Opts, OPT_WND_STATE, true)) { LRect r(0, 0, 800, 500); SetPos(r); MoveToCenter(); } if (Attach(0)) { AddView(d->Tabs = new LTabView(IDC_TABS)); auto BlameTab = d->Tabs->Append("Blame"); BlameTab->Append(d->Tbl = new LTableLayout(IDC_TABLE)); auto c = d->Tbl->GetCell(0, 0); c->Add(new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Filter:")); c->VerticalAlign(LCss::VerticalMiddle); c = d->Tbl->GetCell(1, 0); c->Add(new LEdit(IDC_FILTER, 0, 0, -1, -1)); c = d->Tbl->GetCell(0, 1, true, 2); c->Add(d->Blame = new LList(IDC_BLAME)); d->Blame->AddColumn("Ref", 100); d->Blame->AddColumn("User", 100); d->Blame->AddColumn("Date", 100); d->Blame->AddColumn("Line", 100); d->Blame->AddColumn("Src", 1000); d->Blame->SetPourLargest(true); auto LogTab = d->Tabs->Append("Log"); LogTab->Append(d->Log = new LList(IDC_LOG)); folder->UpdateColumns(d->Log); d->Log->SetPourLargest(true); auto RawTab = d->Tabs->Append("Raw"); RawTab->Append(d->Raw = new LTextLog(IDC_RAW)); AttachChildren(); Visible(true); LView *e; if (GetViewById(IDC_FILTER, e)) e->Focus(true); } } BrowseUi::~BrowseUi() { SerializeState(&d->Priv->Opts, OPT_WND_STATE, false); DeleteObj(d); } void BrowseUi::ParseBlame(LArray &lines, LString raw) { d->Colours.DeleteObjects(); d->Blame->Empty(); d->Raw->Name(d->Output = raw); LDateTime now; now.SetNow(); List items; int lineNo = 1; for (auto ln: lines) { auto col = d->Colours.Find(ln.ref); if (!col) d->Colours.Add(ln.ref, col = d->NewColour()); items.Insert(new BrowseItem(d, ln, now, col, lineNo)); } d->Blame->Insert(items); d->Blame->ResizeColumnsToContent(16); } void BrowseUi::ParseLog(LArray &commits, LString raw) { d->Commits.Swap(commits); d->Log->Empty(); d->Raw->Name(d->Output = raw); d->Tabs->Value(1); LDateTime now; now.SetNow(); for (auto commit: d->Commits) d->Log->Insert(commit); d->Log->ResizeColumnsToContent(); } int BrowseUi::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_FILTER: { auto f = Ctrl->Name(); LArray items; if (!d->Blame->GetAll(items)) break; auto line = Atoi(f); if (line > 0 && line <= (ssize_t)items.Length()) { for (auto i: items) { auto lineTxt = i->GetText(TLine); auto n = Atoi(lineTxt); bool match = n > 0 && n == line; i->Select(match); if (match) i->ScrollTo(); } } else { bool first = true; for (auto i: items) { auto src = i->GetText(TSrc); auto match = Stristr(src, f) != NULL; i->Select(match); if (match && first) { first = false; i->ScrollTo(); } } } break; } } return LWindow::OnNotify(Ctrl, n); } 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 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(); + Port = (int)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/win/Lgi/General.cpp b/src/win/Lgi/General.cpp --- a/src/win/Lgi/General.cpp +++ b/src/win/Lgi/General.cpp @@ -1,1169 +1,1169 @@ #define _WIN32_WINNT 0x500 // Win32 Implementation of General LGI functions #include #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/RegKey.h" #define DEBUG_LOG_WRITES 1 #if DEBUG_LOG_WRITES #define LOG_WRITE(...) LgiTrace(__VA_ARGS__) #else #define LOG_WRITE(...) #endif //////////////////////////////////////////////////////////////// // Implementations void LSleep(DWORD i) { ::Sleep(i); } LString LCurrentUserName() { TCHAR username[256]; DWORD username_len = sizeof(username); GetUserName(username, &username_len); return username; } bool LGetMimeTypeExtensions(const char *Mime, LArray &Ext) { auto Start = Ext.Length(); char *e; LRegKey t(false, "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\MIME\\Database\\Content Type\\%s", Mime); if (t.IsOk() && (e = t.GetStr("Extension"))) { if (*e == '.') e++; Ext.Add(e); } else { #define HardCodeExtention(mime, Ext1, Ext2) \ else if (!stricmp(Mime, mime)) \ { if (Ext1) Ext.Add(Ext1); \ if (Ext2) Ext.Add(Ext2); } const char *Null = NULL; if (!Mime); HardCodeExtention("text/calendar", "ics", Null) HardCodeExtention("text/x-vcard", "vcf", Null) HardCodeExtention("text/mbox", "mbx", "mbox") HardCodeExtention("text/html", "html", Null) HardCodeExtention("text/plain", "txt", Null) HardCodeExtention("message/rfc822", "eml", Null) HardCodeExtention("audio/mpeg", "mp3", Null) HardCodeExtention("application/msword", "doc", Null) HardCodeExtention("application/pdf", "pdf", Null) } return Ext.Length() > Start; } LString LGetFileMimeType(const char *File) { if (File) { char *Dot = strrchr((char*)File, '.'); if (Dot) { bool AssertOnError = LRegKey::AssertOnError; LRegKey::AssertOnError = false; LRegKey Key(false, "HKEY_CLASSES_ROOT\\%s", Dot); if (Key.IsOk()) { char *Ct = Key.GetStr("Content Type"); if (Ct && !stricmp(Dot, ".dsw") == 0 && !stricmp(Dot, ".dsp") == 0) { return Ct; } else { // Search mime type DB. LRegKey Db(false, "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\MIME\\Database\\Content Type"); List Sub; Db.GetKeyNames(Sub); for (auto k: Sub) { LRegKey Type(false, "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\MIME\\Database\\Content Type\\%s", k); char *Ext = Type.GetStr("Extension"); if (Ext && stricmp(Ext, Dot) == 0) { return k; } } Sub.DeleteArrays(); // This is a hack to get around file types without a MIME database entry // but do have a .ext entry. LGetAppsForMimeType knows about the hack too. LString MimeType; - MimeType.Printf("application/%s", Dot); + MimeType.Printf("application/%s", Dot+1); return MimeType; } } LRegKey::AssertOnError = AssertOnError; } // no extension? // no registry entry for file type? return "application/octet-stream"; } return LString(); } bool _GetApps_Add(LArray &Apps, char *In) { LAutoString Path; if (!In) return false; while (*In && strchr(LWhiteSpace, *In)) In++; if (!*In) return false; for (char *i = In; true; i++) { if (*i == '\'' || *i == '\"') { char delim = *i++; char *end = strchr(i, delim); if (!end) end = i + strlen(i); Path.Reset(NewStr(i, end-i)); In = end + (*end != 0); break; } else if (!*i || strchr(LWhiteSpace, *i)) { char old = *i; *i = 0; if (LFileExists(In)) { Path.Reset(NewStr(In)); } *i = old; if (Path) { In = i + (*i != 0); break; } } if (!*i) break; } if (Path) { LStringPipe p; char *RootVar = "%SystemRoot%"; char *SysRoot = stristr(Path, RootVar); if (SysRoot) { // correct path for variables TCHAR Temp[256]; UINT Ch = GetWindowsDirectory(Temp, CountOf(Temp)); LString Tmp = Temp; if (Tmp(-1) != DIR_CHAR) Tmp += DIR_STR; p.Push(Tmp); char *End = SysRoot + strlen(RootVar); p.Push(*End == DIR_CHAR ? End + 1 : End); } else { p.Push(Path); } auto &a = Apps.New(); a.Params = LString(In).Strip(); a.Path = p.NewLStr(); if (a.Path) { char e[MAX_PATH_LEN]; char *d = strrchr(a.Path, DIR_CHAR); if (d) strcpy_s(e, sizeof(e), d + 1); else strcpy_s(e, sizeof(e), a.Path); d = strchr(e, '.'); if (d) *d = 0; e[0] = toupper(e[0]); a.Name = e; if (ValidStr(a.Name)) { bool AllCaps = true; for (char *s=a.Name; *s; s++) { if (islower(*s)) { AllCaps = false; break; } } if (AllCaps) { Strlwr(a.Name.Get() + 1); } } } return true; } return false; } bool LGetAppsForMimeType(const char *Mime, LArray &Apps, int Limit) { bool Status = false; if (Mime) { if (stricmp(Mime, "application/email") == 0) { // get email app LRegKey Key(false, "HKEY_CLASSES_ROOT\\mailto\\shell\\open\\command"); if (Key.IsOk()) { // get app path char *Str = Key.GetStr(); // if (RegQueryValueEx(hKey, 0, 0, &Type, (uchar*)Str, &StrLen) == ERROR_SUCCESS) if (Str) { Status = _GetApps_Add(Apps, Str); } } } else if (!stricmp(Mime, "application/browser")) { // get default browser char *Keys[] = { "HKCU", "HKLM" }; char Base[] = "SOFTWARE\\Clients\\StartMenuInternet"; for (int i=0; i Keys; if (Shell.GetKeyNames(Keys)) { LRegKey First(false, "HKEY_CLASSES_ROOT\\Applications\\%s\\shell\\%s\\command", Application, Keys[0]); char *Path; if (Path = First.GetStr()) { Status |= _GetApps_Add(Apps, Path); } } Keys.DeleteArrays(); } } DeleteArray(Mru); } if (!Status) { // Explorers file extensions LRegKey FileExt(false, "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%s", Ext); char *Application; if (Application = FileExt.GetStr("Application")) { LRegKey App(false, "HKEY_CLASSES_ROOT\\Applications\\%s\\shell\\open\\command", Application); char *Path; if (Path = App.GetStr()) { Status = _GetApps_Add(Apps, Path); } } } if (!Status) { // get classes location LRegKey ExtEntry(false, "HKEY_CLASSES_ROOT\\%s", Ext); LRegKey TypeEntry(false, "HKEY_CLASSES_ROOT\\%s\\shell\\open\\command", ExtEntry.GetStr()); if (TypeEntry.IsOk()) { auto Path = TypeEntry.GetStr(); if (Path) { const char *c = Path; char *Part = LTokStr(c); if (Part) { char AppPath[256]; _snprintf_s(AppPath, sizeof(AppPath), "\"%s\"", Part); Status = _GetApps_Add(Apps, AppPath); DeleteArray(Part); } else { Status = _GetApps_Add(Apps, Path); } } } } } } } } return Status; } LString LGetAppForMimeType(const char *Mime) { LString App; LArray Apps; if (LGetAppsForMimeType(Mime, Apps, 1)) App = Apps[0].Path.Get(); return App; } int LRand(int i) { return (rand() % i); } bool LPlaySound(const char *FileName, int Flags) { bool Status = false; HMODULE hDll = LoadLibrary(_T("winmm.dll")); if (hDll) { typedef BOOL (__stdcall *Proc_sndPlaySound)(LPCSTR pszSound, UINT fuSound); Proc_sndPlaySound psndPlaySound = (Proc_sndPlaySound)GetProcAddress(hDll, "sndPlaySoundA"); if (psndPlaySound) { if (LGetOs() == LGI_OS_WIN9X) { // async broken on 98? Flags = 0; } Status = psndPlaySound(FileName, Flags) != 0; } FreeLibrary(hDll); } return Status; } #include LString LErrorCodeToString(uint32_t ErrorCode) { LString Str; HMODULE hModule = NULL; LPSTR MessageBuffer = NULL; DWORD dwBufferLength; DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM ; if (ErrorCode >= NERR_BASE && ErrorCode <= MAX_NERR) { hModule = LoadLibraryEx( TEXT("netmsg.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE); if (hModule != NULL) dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE; } if (dwBufferLength = FormatMessageA(dwFormatFlags, hModule, // module to get message from (NULL == system) ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language (LPSTR) &MessageBuffer, 0, NULL)) { Str.Set(MessageBuffer, dwBufferLength); LocalFree(MessageBuffer); } if (hModule != NULL) FreeLibrary(hModule); return Str; } bool LExecute(const char *File, const char *Arguments, const char *Dir, LString *ErrorMsg) { int Error = 0; HINSTANCE Status = NULL; if (!File) return false; uint64 Now = LCurrentTime(); if (LGetOs() == LGI_OS_WIN9X) { auto f = LToNativeCp(File); auto a = LToNativeCp(Arguments); auto d = LToNativeCp(Dir); if (f) { Status = ShellExecuteA(NULL, "open", f, a, d, 5); if ((size_t)Status <= 32) Error = GetLastError(); } } else { LAutoWString f(Utf8ToWide(File)); LAutoWString a(Utf8ToWide(Arguments)); LAutoWString d(Utf8ToWide(Dir)); if (f) { Status = ShellExecuteW(NULL, L"open", f, a, d, 5); if ((size_t)Status <= 32) Error = GetLastError(); } } #ifdef _DEBUG if ((size_t)Status <= 32) LgiTrace("ShellExecuteW failed with %p (LastErr=0x%x)\n", Status, Error); if (LCurrentTime() - Now > 1000) LgiTrace("ShellExecuteW took %I64i\n", LCurrentTime() - Now); #endif if (ErrorMsg) *ErrorMsg = LErrorCodeToString(Error); return (size_t)Status > 32; } //////////////////////////////////////////////////////////////////////////////////// HKEY GetRootKey(const char *s) { HKEY Root = 0; #define TestKey(Short, Name) \ if (!strncmp(s, #Short, strlen(#Short)) || \ !strncmp(s, #Name, strlen(#Name))) \ { \ Root = Name; \ } TestKey(HKCR, HKEY_CLASSES_ROOT) else TestKey(HKCC, HKEY_CURRENT_CONFIG) else TestKey(HKCU, HKEY_CURRENT_USER) else TestKey(HKLM, HKEY_LOCAL_MACHINE) else TestKey(HKU, HKEY_USERS) #undef TestKey LAssert(Root); return Root; } bool LRegKey::AssertOnError = true; LRegKey::LRegKey(bool WriteAccess, const char *Key, ...) { char Buffer[1025]; Root = (HKEY)-1; va_list Arg; va_start(Arg, Key); vsprintf_s(Buffer, Key, Arg); va_end(Arg); KeyName = Buffer; if (KeyName) { size_t Len = 0; char *SubKey = 0; #define TestKey(Long, Short) \ if (!strnicmp(KeyName, #Long, Len = strlen(#Long)) || \ !strnicmp(KeyName, #Short, Len = strlen(#Short))) \ { \ Root = Long; \ SubKey = KeyName.Get()[Len] ? KeyName.Get() + Len + 1 : 0; \ } TestKey(HKEY_CLASSES_ROOT, HKCR) else TestKey(HKEY_CURRENT_CONFIG, HKCC) else TestKey(HKEY_CURRENT_USER, HKCU) else TestKey(HKEY_LOCAL_MACHINE, HKLM) else TestKey(HKEY_USERS, HKU) else return; result = RegOpenKeyExA(Root, SubKey, 0, WriteAccess ? KEY_ALL_ACCESS : KEY_READ, &k); if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { DWORD err = GetLastError(); if (AssertOnError) LAssert(!"RegOpenKeyEx failed"); } } } LRegKey::~LRegKey() { if (k) RegCloseKey(k); } LString LRegKey::GetErrorName() { LError err(result); return err.GetMsg(); } bool LRegKey::IsOk() { return k != NULL; } bool LRegKey::Create() { bool Status = false; if (!k && KeyName) { char *Sub = strchr(KeyName, '\\'); if (Sub) { result = RegCreateKeyA(Root, Sub+1, &k); if (result == ERROR_SUCCESS) { Status = IsOk(); } else { DWORD err = GetLastError(); if (AssertOnError) LAssert(!"RegCreateKey failed"); } } } return Status; } const char *LRegKey::Name() { return KeyName; } bool LRegKey::DeleteValue(const char *Name) { if (k) { if ((result = RegDeleteValueA(k, Name)) == ERROR_SUCCESS) { return true; } else { DWORD Err = GetLastError(); LAssert(!"RegDeleteValue failed"); } } return false; } bool LRegKey::DeleteKey() { auto p = KeyName.SplitDelimit("\\", 1); if (p.Length() != 2) return false; if (k) { RegCloseKey(k); k = NULL; } HKEY Root = GetRootKey(p[0]); result = RegDeleteKeyA(Root, p[1]); if (result != ERROR_SUCCESS) { LgiTrace("RegDeleteKeyA(%p, %s) == %i\n", Root, p[1].Get(), result); if (AssertOnError) LAssert(!"RegDeleteKey failed."); return false; } KeyName.Empty(); return true; } char *LRegKey::GetStr(const char *Name) { if (!k) { LAssert(!"No key to read from."); return NULL; } DWORD Size = sizeof(s), Type; result = RegQueryValueExA(k, Name, 0, &Type, (uchar*)s, &Size); if (result != ERROR_SUCCESS) { if (AssertOnError) LAssert(!"RegQueryValueEx failed."); return NULL; } return s; } bool LRegKey::GetStr(const char *Name, LString &Str) { if (!k) { if (AssertOnError) LAssert(!"No key to read from."); return false; } DWORD Size = 0, Type; result = RegQueryValueExA(k, Name, 0, &Type, NULL, &Size); if (result != ERROR_SUCCESS) goto OnError; { LString Tmp((char*)NULL, Size); result = RegQueryValueExA(k, Name, 0, &Type, (LPBYTE)Tmp.Get(), &Size); if (result != ERROR_SUCCESS) goto OnError; Str = Tmp; return true; } OnError: if (AssertOnError) LAssert(!"RegQueryValueEx failed."); return false; } bool LRegKey::SetStr(const char *Name, const char *Value) { if (!k) { LAssert(!"No key open."); return false; } result = RegSetValueExA(k, Name, 0, REG_SZ, (uchar*)Value, Value ? (DWORD)strlen(Value) : 0); LOG_WRITE("RegSetValueExA(%s,%s,'%s')=%i\n", KeyName.Get(), Name, Value, result); if (result != ERROR_SUCCESS) { if (AssertOnError) LAssert(!"RegSetValueEx failed."); return false; } return true; } bool LRegKey::GetInt(const char *Name, uint32_t &Value) { if (!k) return false; DWORD Size = sizeof(Value), Type; result = RegQueryValueExA(k, Name, 0, &Type, (uchar*)&Value, &Size); return result == ERROR_SUCCESS; } bool LRegKey::SetInt(const char *Name, uint32_t Value) { if (!k) { LgiTrace("%s:%i - No key name.\n", _FL); return false; } result = RegSetValueExA(k, Name, 0, REG_DWORD, (uchar*)&Value, sizeof(Value)); LOG_WRITE("RegSetValueExA(%s,%s,%i)=%i\n", KeyName.Get(), Name, Value, result); if (result == ERROR_SUCCESS) return true; LgiTrace("%s:%i - RegSetValueExA(%s) returned error: %x.\n", _FL, Name, result); return false; } bool LRegKey::GetBinary(const char *Name, void *&Ptr, int &Len) { DWORD Size = 0, Type; if (k && (result = RegQueryValueExA(k, Name, 0, &Type, 0, &Size)) == ERROR_SUCCESS) { Len = Size; Ptr = new uchar[Len]; result = RegQueryValueExA(k, Name, 0, &Type, (uchar*)Ptr, &Size); return result == ERROR_SUCCESS; } return false; } bool LRegKey::SetBinary(const char *Name, void *Ptr, int Len) { LAssert(!"Not impl."); return false; } bool LRegKey::GetKeyNames(List &n) { FILETIME t; TCHAR Buf[256]; DWORD Size = CountOf(Buf), i = 0; while ((result = RegEnumKeyEx(k, i++, Buf, &Size, 0, 0, 0, &t)) == ERROR_SUCCESS) { n.Insert(WideToUtf8(Buf)); Size = sizeof(Buf); } return n.Length() > 0; } bool LRegKey::GetValueNames(List &n) { TCHAR Buf[256]; DWORD Type, Size = CountOf(Buf), i = 0; while ((result = RegEnumValue(k, i++, Buf, &Size, 0, &Type, 0, 0)) == ERROR_SUCCESS) { n.Insert(WideToUtf8(Buf)); Size = sizeof(Buf); } return n.Length() > 0; } ////////////////////////////////////////////////////////////////////////////////////// LString WinGetSpecialFolderPath(int Id) { LLibrary Shell("Shell32"); LString s; char16 wp[MAX_PATH_LEN] = { 0 }; pSHGetSpecialFolderPathW w = (pSHGetSpecialFolderPathW) Shell.GetAddress("SHGetSpecialFolderPathW"); if (w) { BOOL result = w(0, wp, Id, false); if (result && ValidStrW(wp)) { LAutoString Tmp(WideToUtf8(wp)); s = Tmp; } else { DWORD e = GetLastError(); LAssert(!"Error getting system folder."); } } return s; } ////////////////////////////////////////////////////////////////////// #ifndef LGI_STATIC static bool AssertUiOpen = false; void LAssertDlg(LString Msg, std::function Callback) { AssertUiOpen = true; auto a = new LAlert(LAppInst ? LAppInst->AppWnd : NULL, "Assert Failed", Msg, "Abort", "Debug", "Ignore"); a->SetAppModal(); a->DoModal([Callback](auto d, auto code) { if (Callback) Callback(code); delete d; AssertUiOpen = false; }); } #endif void _lgi_assert(bool b, const char *test, const char *file, int line) { if (!b) { #ifdef LGI_STATIC assert(b); #else if (AssertUiOpen || !LAppInst || !LSysFont) { // Woah boy... LgiTrace("%s:%i - Assert: %s failed.\n", file, line, test); } else { LgiTrace("%s:%i - Assert failed:\n%s\n", file, line, test); #ifdef _DEBUG LString Msg; Msg.Printf("Assert failed, file: %s, line: %i\n%s", file, line, test); int Result = 0; if (LAppInst->InThread()) { // We are in the GUI thread, show the dialog inline LAssertDlg(Msg, [](auto Result) { switch (Result) { case 1: { exit(-1); break; } case 2: { // Bring up the debugger... #if defined(_WIN64) || !defined(_MSC_VER) assert(0); #else _asm int 3 #endif break; } default: case 3: { break; } } }); } else { // Fall back to windows UI assert(0); } #endif } #endif } } ////////////////////////////////////////////////////////////////////// // The following code is from: // Web: http://www.codeproject.com/Articles/28071/Toggle-hardware-data-read-execute-breakpoints-prog // License: http://www.codeproject.com/info/cpol10.aspx struct HWBRK { void *a; HANDLE hT; HWBRK_TYPE Type; HWBRK_SIZE Size; HANDLE hEv; int iReg; int Opr; bool SUCC; HWBRK() { Opr = 0; a = 0; hT = 0; hEv = 0; iReg = 0; SUCC = false; } }; static void SetBits(DWORD_PTR& dw, int lowBit, int bits, int newValue) { DWORD_PTR mask = (1 << bits) - 1; dw = (dw & ~(mask << lowBit)) | (newValue << lowBit); } static DWORD WINAPI BreakpointThread(LPVOID lpParameter) { HWBRK* h = (HWBRK*)lpParameter; int j = 0; int y = 0; j = SuspendThread(h->hT); y = GetLastError(); CONTEXT ct = {0}; ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; j = GetThreadContext(h->hT,&ct); y = GetLastError(); int FlagBit = 0; bool Dr0Busy = false; bool Dr1Busy = false; bool Dr2Busy = false; bool Dr3Busy = false; if (ct.Dr7 & 1) Dr0Busy = true; if (ct.Dr7 & 4) Dr1Busy = true; if (ct.Dr7 & 16) Dr2Busy = true; if (ct.Dr7 & 64) Dr3Busy = true; if (h->Opr == 1) { // Remove if (h->iReg == 0) { FlagBit = 0; ct.Dr0 = 0; Dr0Busy = false; } if (h->iReg == 1) { FlagBit = 2; ct.Dr1 = 0; Dr1Busy = false; } if (h->iReg == 2) { FlagBit = 4; ct.Dr2 = 0; Dr2Busy = false; } if (h->iReg == 3) { FlagBit = 6; ct.Dr3 = 0; Dr3Busy = false; } ct.Dr7 &= ~(1 << FlagBit); } else { if (!Dr0Busy) { h->iReg = 0; ct.Dr0 = (DWORD_PTR)h->a; Dr0Busy = true; } else if (!Dr1Busy) { h->iReg = 1; ct.Dr1 = (DWORD_PTR)h->a; Dr1Busy = true; } else if (!Dr2Busy) { h->iReg = 2; ct.Dr2 = (DWORD_PTR)h->a; Dr2Busy = true; } else if (!Dr3Busy) { h->iReg = 3; ct.Dr3 = (DWORD_PTR)h->a; Dr3Busy = true; } else { h->SUCC = false; j = ResumeThread(h->hT); y = GetLastError(); SetEvent(h->hEv); return 0; } ct.Dr6 = 0; int st = 0; if (h->Type == HWBRK_TYPE_CODE) st = 0; if (h->Type == HWBRK_TYPE_READWRITE) st = 3; if (h->Type == HWBRK_TYPE_WRITE) st = 1; int le = 0; if (h->Size == HWBRK_SIZE_1) le = 0; if (h->Size == HWBRK_SIZE_2) le = 1; if (h->Size == HWBRK_SIZE_4) le = 3; if (h->Size == HWBRK_SIZE_8) le = 2; SetBits(ct.Dr7, 16 + h->iReg*4, 2, st); SetBits(ct.Dr7, 18 + h->iReg*4, 2, le); SetBits(ct.Dr7, h->iReg*2,1,1); } ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; j = SetThreadContext(h->hT,&ct); y = GetLastError(); ct.ContextFlags = CONTEXT_DEBUG_REGISTERS; j = GetThreadContext(h->hT,&ct); y = GetLastError(); j = ResumeThread(h->hT); y = GetLastError(); h->SUCC = true; SetEvent(h->hEv); return 0; } HANDLE SetHardwareBreakpoint(HANDLE hThread, HWBRK_TYPE Type, HWBRK_SIZE Size, void *s) { HWBRK *h = new HWBRK; h->a = s; h->Size = Size; h->Type = Type; h->hT = hThread; if (hThread == GetCurrentThread()) { DWORD pid = LCurrentThreadId(); h->hT = OpenThread(THREAD_ALL_ACCESS,0,pid); } h->hEv = CreateEvent(0, 0, 0, 0); h->Opr = 0; // Set Break HANDLE hY = CreateThread(0, 0, BreakpointThread, (LPVOID)h, 0, 0); WaitForSingleObject(h->hEv, INFINITE); CloseHandle(h->hEv); h->hEv = 0; if (hThread == GetCurrentThread()) { CloseHandle(h->hT); } h->hT = hThread; if (!h->SUCC) { delete h; return 0; } return (HANDLE)h; } bool RemoveHardwareBreakpoint(HANDLE hBrk) { HWBRK* h = (HWBRK*)hBrk; if (!h) return false; bool C = false; if (h->hT == GetCurrentThread()) { DWORD pid = LCurrentThreadId(); h->hT = OpenThread(THREAD_ALL_ACCESS,0,pid); C = true; } h->hEv = CreateEvent(0,0,0,0); h->Opr = 1; // Remove Break HANDLE hY = CreateThread(0,0,BreakpointThread,(LPVOID)h,0,0); WaitForSingleObject(h->hEv,INFINITE); CloseHandle(h->hEv); h->hEv = 0; if (C) { CloseHandle(h->hT); } delete h; return true; }