diff --git a/Ide/Code/LgiIde.cpp b/Ide/Code/LgiIde.cpp --- a/Ide/Code/LgiIde.cpp +++ b/Ide/Code/LgiIde.cpp @@ -1,4669 +1,4674 @@ #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Mdi.h" #include "lgi/common/Token.h" #include "lgi/common/XmlTree.h" #include "lgi/common/Panel.h" #include "lgi/common/Button.h" #include "lgi/common/TabView.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/Box.h" #include "lgi/common/TextLog.h" #include "lgi/common/Edit.h" #include "lgi/common/TableLayout.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Combo.h" #include "lgi/common/CheckBox.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Box.h" #include "lgi/common/SubProcess.h" #include "lgi/common/About.h" #include "lgi/common/Menu.h" #include "lgi/common/ToolBar.h" #include "lgi/common/FileSelect.h" #include "lgi/common/SubProcess.h" #include "LgiIde.h" #include "FtpThread.h" #include "FindSymbol.h" #include "Debugger.h" #include "ProjectNode.h" #define IDM_RECENT_FILE 1000 #define IDM_RECENT_PROJECT 1100 #define IDM_WINDOWS 1200 #define IDM_MAKEFILE_BASE 1300 #define USE_HAIKU_PULSE_HACK 0 #define OPT_ENTIRE_SOLUTION "SearchSolution" #define OPT_SPLIT_PX "SplitPos" #define OPT_OUTPUT_PX "OutputPx" #define OPT_FIX_RENAMED "FixRenamed" #define OPT_RENAMED_SYM "RenamedSym" #define IsSymbolChar(c) ( IsDigit(c) || IsAlpha(c) || strchr("-_", c) ) ////////////////////////////////////////////////////////////////////////////////////////// class FindInProject : public LDialog { AppWnd *App; LList *Lst; public: FindInProject(AppWnd *app) { Lst = NULL; App = app; if (LoadFromResource(IDC_FIND_PROJECT_FILE)) { MoveSameScreen(App); LViewI *v; if (GetViewById(IDC_TEXT, v)) v->Focus(true); if (!GetViewById(IDC_FILES, Lst)) return; RegisterHook(this, LKeyEvents, 0); } } bool OnViewKey(LView *v, LKey &k) { switch (k.vkey) { case LK_UP: case LK_DOWN: case LK_PAGEDOWN: case LK_PAGEUP: { return Lst->OnKey(k); break; } case LK_RETURN: { if (k.Down()) { LListItem *i = Lst->GetSelected(); if (i) { const char *Ref = i->GetText(0); App->GotoReference(Ref, 1, false); } EndModal(1); return true; } break; } case LK_ESCAPE: { if (k.Down()) { EndModal(0); return true; } break; } } return false; } void Search(const char *s) { IdeProject *p = App->RootProject(); if (!p || !s) return; LArray Matches, Nodes; List All; p->GetChildProjects(All); All.Insert(p); for (auto p: All) { p->GetAllNodes(Nodes); } FilterFiles(Matches, Nodes, s); Lst->Empty(); for (auto m: Matches) { LListItem *li = new LListItem; LString Fn = m->GetFileName(); #ifdef WINDOWS Fn = Fn.Replace("/","\\"); #else Fn = Fn.Replace("\\","/"); #endif m->GetProject()->CheckExists(Fn); li->SetText(Fn); Lst->Insert(li); } Lst->ResizeColumnsToContent(); } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_FILES: if (n.Type == LNotifyItemDoubleClick) { LListItem *i = Lst->GetSelected(); if (i) { App->GotoReference(i->GetText(0), 1, false); EndModal(1); } } break; case IDC_TEXT: if (n.Type != LNotifyReturnKey) Search(c->Name()); break; case IDCANCEL: EndModal(0); break; } return 0; } }; ////////////////////////////////////////////////////////////////////////////////////////// char AppName[] = "LgiIde"; char *dirchar(char *s, bool rev = false) { if (rev) { char *last = 0; while (s && *s) { if (*s == '/' || *s == '\\') last = s; s++; } return last; } else { while (s && *s) { if (*s == '/' || *s == '\\') return s; s++; } } return 0; } ////////////////////////////////////////////////////////////////////////////////////////// class AppDependency : public LTreeItem { char *File; bool Loaded; LTreeItem *Fake; public: AppDependency(const char *file) { File = NewStr(file); char *d = strrchr(File, DIR_CHAR); Loaded = false; Insert(Fake = new LTreeItem); if (LFileExists(File)) { SetText(d?d+1:File); } else { char s[256]; sprintf(s, "%s (missing)", d?d+1:File); SetText(s); } } ~AppDependency() { DeleteArray(File); } char *GetFile() { return File; } void Copy(LStringPipe &p, int Depth = 0) { { char s[1024]; ZeroObj(s); memset(s, ' ', Depth * 4); sprintf(s+(Depth*4), "[%c] %s\n", Expanded() ? '-' : '+', GetText(0)); p.Push(s); } if (Loaded) { for (LTreeItem *i=GetChild(); i; i=i->GetNext()) { ((AppDependency*)i)->Copy(p, Depth+1); } } } char *Find(const char *Paths, char *e) { LToken Path(Paths, LGI_PATH_SEPARATOR); for (int p=0; pSunken(true); Root = new AppDependency(File); if (Root) { t->Insert(Root); Root->Expanded(true); } Children.Insert(t); Children.Insert(new LButton(IDC_COPY, 10, t->LView::GetPos().y2 + 10, 60, 20, "Copy")); Children.Insert(new LButton(IDOK, 80, t->LView::GetPos().y2 + 10, 60, 20, "Ok")); } DoModal(NULL); } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_COPY: { if (Root) { LStringPipe p; Root->Copy(p); char *s = p.NewStr(); if (s) { LClipBoard c(this); c.Text(s); DeleteArray(s); } break; } break; } case IDOK: { EndModal(0); break; } } return 0; } }; ////////////////////////////////////////////////////////////////////////////////////////// class DebugTextLog : public LTextLog { public: DebugTextLog(int id) : LTextLog(id) { } void PourText(size_t Start, ssize_t Len) override { auto Ts = LCurrentTime(); LTextView3::PourText(Start, Len); auto Dur = LCurrentTime() - Ts; if (Dur > 1500) { // Yo homes, too much text bro... Name(NULL); } else { for (auto l: Line) { char16 *t = Text + l->Start; if (l->Len > 5 && !StrnicmpW(t, L"(gdb)", 5)) { l->c.Rgb(0, 160, 0); } else if (l->Len > 1 && t[0] == '[') { l->c.Rgb(192, 192, 192); } } } } }; WatchItem::WatchItem(IdeOutput *out, const char *Init) { Out = out; Expanded(false); if (Init) SetText(Init); Insert(PlaceHolder = new LTreeItem); } WatchItem::~WatchItem() { } bool WatchItem::SetValue(LVariant &v) { char *Str = v.CastString(); if (ValidStr(Str)) SetText(Str, 2); else LTreeItem::SetText(NULL, 2); return true; } bool WatchItem::SetText(const char *s, int i) { if (ValidStr(s)) { LTreeItem::SetText(s, i); if (i == 0 && Tree && Tree->GetWindow()) { LViewI *Tabs = Tree->GetWindow()->FindControl(IDC_DEBUG_TAB); if (Tabs) Tabs->SendNotify(LNotifyValueChanged); } return true; } if (i == 0) delete this; return false; } void WatchItem::OnExpand(bool b) { if (b && PlaceHolder) { // Do something } } class BuildLog : public LTextLog { public: BuildLog(int id) : LTextLog(id) { } void PourStyle(size_t Start, ssize_t Length) { List::I it = LTextView3::Line.begin(); for (LTextLine *ln = *it; ln; ln = *++it) { if (!ln->c.IsValid()) { char16 *t = Text + ln->Start; char16 *Err = Strnistr(t, L"error", ln->Len); char16 *Undef = Strnistr(t, L"undefined reference", ln->Len); char16 *Warn = Strnistr(t, L"warning", ln->Len); if ( (Err && strchr(":[", Err[5])) || (Undef != NULL) ) ln->c.Rgb(222, 0, 0); else if (Warn && strchr(":[", Warn[7])) ln->c.Rgb(255, 128, 0); else ln->c = LColour(L_TEXT); } } } }; class IdeOutput : public LTabView { public: AppWnd *App; LTabPage *Build; LTabPage *Output; LTabPage *Debug; LTabPage *Find; LTabPage *Ftp; LList *FtpLog; LTextLog *Txt[AppWnd::Channels::ChannelMax]; LArray Buf[AppWnd::Channels::ChannelMax]; LFont Small; LFont Fixed; LTabView *DebugTab; LBox *DebugBox; LBox *DebugLog; LList *Locals, *CallStack, *Threads; LTree *Watch; LTextLog *ObjectDump, *MemoryDump, *Registers; LTableLayout *MemTable; LEdit *DebugEdit; LTextLog *DebuggerLog; IdeOutput(AppWnd *app) { ZeroObj(Txt); App = app; Build = Output = Debug = Find = Ftp = 0; FtpLog = 0; DebugBox = NULL; Locals = NULL; Watch = NULL; DebugLog = NULL; DebugEdit = NULL; DebuggerLog = NULL; CallStack = NULL; ObjectDump = NULL; MemoryDump = NULL; MemTable = NULL; Threads = NULL; Registers = NULL; Small = *LSysFont; Small.PointSize(Small.PointSize()-1); Small.Create(); LAssert(Small.Handle()); LFontType Type; if (Type.GetSystemFont("Fixed")) { Type.SetPointSize(LSysFont->PointSize()-1); Fixed.Create(&Type); } else { Fixed.PointSize(LSysFont->PointSize()-1); Fixed.Face("Courier"); Fixed.Create(); } GetCss(true)->MinHeight("60px"); Build = Append("Build"); Output = Append("Output"); Find = Append("Find"); Ftp = Append("Ftp"); Debug = Append("Debug"); SetFont(&Small); Build->SetFont(&Small); Output->SetFont(&Small); Find->SetFont(&Small); Ftp->SetFont(&Small); Debug->SetFont(&Small); if (Build) Build->Append(Txt[AppWnd::BuildTab] = new BuildLog(IDC_BUILD_LOG)); if (Output) Output->Append(Txt[AppWnd::OutputTab] = new LTextLog(IDC_OUTPUT_LOG)); if (Find) Find->Append(Txt[AppWnd::FindTab] = new LTextLog(IDC_FIND_LOG)); if (Ftp) Ftp->Append(FtpLog = new LList(104, 0, 0, 100, 100)); if (Debug) { Debug->Append(DebugBox = new LBox); if (DebugBox) { DebugBox->SetVertical(false); if ((DebugTab = new LTabView(IDC_DEBUG_TAB))) { DebugTab->GetCss(true)->Padding("0px"); DebugTab->SetFont(&Small); DebugBox->AddView(DebugTab); LTabPage *Page; if ((Page = DebugTab->Append("Locals"))) { Page->SetFont(&Small); if ((Locals = new LList(IDC_LOCALS_LIST, 0, 0, 100, 100, "Locals List"))) { Locals->SetFont(&Small); Locals->AddColumn("", 30); Locals->AddColumn("Type", 50); Locals->AddColumn("Name", 50); Locals->AddColumn("Value", 1000); Locals->SetPourLargest(true); Page->Append(Locals); } } if ((Page = DebugTab->Append("Object"))) { Page->SetFont(&Small); if ((ObjectDump = new LTextLog(IDC_OBJECT_DUMP))) { ObjectDump->SetFont(&Fixed); ObjectDump->SetPourLargest(true); Page->Append(ObjectDump); } } if ((Page = DebugTab->Append("Watch"))) { Page->SetFont(&Small); if ((Watch = new LTree(IDC_WATCH_LIST, 0, 0, 100, 100, "Watch List"))) { Watch->SetFont(&Small); Watch->ShowColumnHeader(true); Watch->AddColumn("Watch", 80); Watch->AddColumn("Type", 100); Watch->AddColumn("Value", 600); Watch->SetPourLargest(true); Page->Append(Watch); LXmlTag *w = App->GetOptions()->LockTag("watches", _FL); if (!w) { App->GetOptions()->CreateTag("watches"); w = App->GetOptions()->LockTag("watches", _FL); } if (w) { for (auto c: w->Children) { if (c->IsTag("watch")) { Watch->Insert(new WatchItem(this, c->GetContent())); } } App->GetOptions()->Unlock(); } } } if ((Page = DebugTab->Append("Memory"))) { Page->SetFont(&Small); if ((MemTable = new LTableLayout(IDC_MEMORY_TABLE))) { LCombo *cbo; LCheckBox *chk; LTextLabel *txt; LEdit *ed; MemTable->SetFont(&Small); int x = 0, y = 0; auto *c = MemTable->GetCell(x++, y); if (c) { c->VerticalAlign(LCss::VerticalMiddle); c->Add(txt = new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Address:")); txt->SetFont(&Small); } c = MemTable->GetCell(x++, y); if (c) { c->PaddingRight(LCss::Len("1em")); c->Add(ed = new LEdit(IDC_MEM_ADDR, 0, 0, 60, 20)); ed->SetFont(&Small); } c = MemTable->GetCell(x++, y); if (c) { c->PaddingRight(LCss::Len("1em")); c->Add(cbo = new LCombo(IDC_MEM_SIZE, 0, 0, 60, 20)); cbo->SetFont(&Small); cbo->Insert("1 byte"); cbo->Insert("2 bytes"); cbo->Insert("4 bytes"); cbo->Insert("8 bytes"); } c = MemTable->GetCell(x++, y); if (c) { c->VerticalAlign(LCss::VerticalMiddle); c->Add(txt = new LTextLabel(IDC_STATIC, 0, 0, -1, -1, "Page width:")); txt->SetFont(&Small); } c = MemTable->GetCell(x++, y); if (c) { c->PaddingRight(LCss::Len("1em")); c->Add(ed = new LEdit(IDC_MEM_ROW_LEN, 0, 0, 60, 20)); ed->SetFont(&Small); } c = MemTable->GetCell(x++, y); if (c) { c->VerticalAlign(LCss::VerticalMiddle); c->Add(chk = new LCheckBox(IDC_MEM_HEX, 0, 0, -1, -1, "Show Hex")); chk->SetFont(&Small); chk->Value(true); } int cols = x; x = 0; c = MemTable->GetCell(x++, ++y, true, cols); if ((MemoryDump = new LTextLog(IDC_MEMORY_DUMP))) { MemoryDump->SetFont(&Fixed); MemoryDump->SetPourLargest(true); c->Add(MemoryDump); } Page->Append(MemTable); } } if ((Page = DebugTab->Append("Threads"))) { Page->SetFont(&Small); if ((Threads = new LList(IDC_THREADS, 0, 0, 100, 100, "Threads"))) { Threads->SetFont(&Small); Threads->AddColumn("", 20); Threads->AddColumn("Thread", 1000); Threads->SetPourLargest(true); Threads->MultiSelect(false); Page->Append(Threads); } } if ((Page = DebugTab->Append("Call Stack"))) { Page->SetFont(&Small); if ((CallStack = new LList(IDC_CALL_STACK, 0, 0, 100, 100, "Call Stack"))) { CallStack->SetFont(&Small); CallStack->AddColumn("", 20); CallStack->AddColumn("Call Stack", 1000); CallStack->SetPourLargest(true); CallStack->MultiSelect(false); Page->Append(CallStack); } } if ((Page = DebugTab->Append("Registers"))) { Page->SetFont(&Small); if ((Registers = new LTextLog(IDC_REGISTERS))) { Registers->SetFont(&Small); Registers->SetPourLargest(true); Page->Append(Registers); } } } if ((DebugLog = new LBox)) { DebugLog->SetVertical(true); DebugBox->AddView(DebugLog); DebugLog->AddView(DebuggerLog = new DebugTextLog(IDC_DEBUGGER_LOG)); DebuggerLog->SetFont(&Small); DebugLog->AddView(DebugEdit = new LEdit(IDC_DEBUG_EDIT, 0, 0, 60, 20)); DebugEdit->GetCss(true)->Height(LCss::Len(LCss::LenPx, (float)(LSysFont->GetHeight() + 8))); } } } if (FtpLog) { FtpLog->SetPourLargest(true); FtpLog->Sunken(true); FtpLog->AddColumn("Entry", 1000); FtpLog->ShowColumnHeader(false); } for (int n=0; nSetTabSize(8); Txt[n]->Sunken(true); } } ~IdeOutput() { } const char *GetClass() { return "IdeOutput"; } void Save() { if (Watch) { LXmlTag *w = App->GetOptions()->LockTag("watches", _FL); if (!w) { App->GetOptions()->CreateTag("watches"); w = App->GetOptions()->LockTag("watches", _FL); } if (w) { w->EmptyChildren(); for (LTreeItem *ti = Watch->GetChild(); ti; ti = ti->GetNext()) { LXmlTag *t = new LXmlTag("watch"); if (t) { t->SetContent(ti->GetText(0)); w->InsertTag(t); } } App->GetOptions()->Unlock(); } } } void OnCreate() { SetPulse(1000); AttachChildren(); } void RemoveAnsi(LArray &a) { char *s = a.AddressOf(); char *e = s + a.Length(); while (s < e) { if (*s == 0x7) { a.DeleteAt(s - a.AddressOf(), true); s--; } else if ( *s == 0x1b && s[1] >= 0x40 && s[1] <= 0x5f ) { // ANSI seq char *end; if (s[1] == '[' && s[2] == '0' && s[3] == ';') end = s + 4; else { end = s + 2; while (end < e && !IsAlpha(*end)) { end++; } if (*end) end++; } auto len = end - s; memmove(s, end, e - end); a.Length(a.Length() - len); s--; } s++; } } void OnPulse() { int Changed = -1; for (int Channel = 0; Channel w; if (!LIsUtf8(Utf, (ssize_t)Size)) { LgiTrace("Ch %i not utf len=" LPrintfInt64 "\n", Channel, Size); // Clear out the invalid UTF? uint8_t *u = (uint8_t*) Utf, *e = u + Size; ssize_t len = Size; LArray out; while (u < e) { int32 u32 = LgiUtf8To32(u, len); if (u32) { out.Add(u32); } else { out.Add(0xFFFD); u++; } } out.Add(0); w.Reset(out.Release()); } else { RemoveAnsi(Buf[Channel]); w.Reset(Utf8ToWide(Utf, (ssize_t)Size)); } // auto OldText = Txt[Channel]->NameW(); ssize_t OldLen = Txt[Channel]->Length(); auto Cur = Txt[Channel]->GetCaret(); Txt[Channel]->Insert(OldLen, w, StrlenW(w)); if (Cur > OldLen - 1) Txt[Channel]->SetCaret(OldLen + StrlenW(w), false); else printf("Caret move: %i, %i = %i\n", (int)Cur, (int)OldLen, Cur > OldLen - 1); Changed = Channel; Buf[Channel].Length(0); Txt[Channel]->Invalidate(); } } /* if (Changed >= 0) Value(Changed); */ } }; int DocSorter(IdeDoc *a, IdeDoc *b, NativeInt d) { auto A = a->GetFileName(); auto B = b->GetFileName(); if (A && B) { auto Af = strrchr(A, DIR_CHAR); auto Bf = strrchr(B, DIR_CHAR); return stricmp(Af?Af+1:A, Bf?Bf+1:B); } return 0; } struct FileLoc { LAutoString File; int Line; void Set(const char *f, int l) { File.Reset(NewStr(f)); Line = l; } }; class AppWndPrivate { public: AppWnd *App; LMdiParent *Mdi; LOptionsFile Options; LBox *HBox, *VBox; List Docs; List Projects; LImageList *Icons; LTree *Tree; IdeOutput *Output; bool Debugging; bool Running; bool Building; bool FixBuildWait = false; int RebuildWait = 0; LSubMenu *WindowsMenu; LSubMenu *CreateMakefileMenu; LAutoPtr FindSym; LArray SystemIncludePaths; LArray BreakPoints; // Debugging LDebugContext *DbgContext; // Cursor history tracking int HistoryLoc; LArray CursorHistory; bool InHistorySeek; void SeekHistory(int Direction) { if (CursorHistory.Length()) { int Loc = HistoryLoc + Direction; if (Loc >= 0 && Loc < CursorHistory.Length()) { HistoryLoc = Loc; FileLoc &Loc = CursorHistory[HistoryLoc]; App->GotoReference(Loc.File, Loc.Line, false, false); App->DumpHistory(); } } } // Find in files LAutoPtr FindParameters; LAutoPtr Finder; int AppHnd; // Mru LString::Array RecentFiles; LSubMenu *RecentFilesMenu = NULL; LString::Array RecentProjects; LSubMenu *RecentProjectsMenu = NULL; // Object AppWndPrivate(AppWnd *a) : Options(LOptionsFile::DesktopMode, AppName), AppHnd(LEventSinkMap::Dispatch.AddSink(a)) { FindSym.Reset(new FindSymbolSystem(AppHnd)); HistoryLoc = 0; InHistorySeek = false; WindowsMenu = 0; App = a; HBox = VBox = NULL; Tree = 0; Mdi = NULL; DbgContext = NULL; Output = 0; Debugging = false; Running = false; Building = false; Icons = LLoadImageList("icons.png", 16, 16); Options.SerializeFile(false); App->SerializeState(&Options, "WndPos", true); SerializeStringList("RecentFiles", &RecentFiles, false); SerializeStringList("RecentProjects", &RecentProjects, false); } ~AppWndPrivate() { FindSym.Reset(); Finder.Reset(); if (Output) Output->Save(); App->SerializeState(&Options, "WndPos", false); SerializeStringList("RecentFiles", &RecentFiles, true); SerializeStringList("RecentProjects", &RecentProjects, true); Options.SerializeFile(true); while (Docs.Length()) { auto len = Docs.Length(); delete Docs[0]; LAssert(Docs.Length() != len); // doc must delete itself... } auto root = App->RootProject(); if (root) { // printf("Deleting proj %s\n", root->GetFileName()); delete root; } LAssert(!Projects.Length()); DeleteObj(Icons); } bool FindSource(LAutoString &Full, char *File, char *Context) { if (!LIsRelativePath(File)) { Full.Reset(NewStr(File)); } char *ContextPath = 0; if (Context && !Full) { char *Dir = strrchr(Context, DIR_CHAR); for (auto p: Projects) { ContextPath = p->FindFullPath(Dir?Dir+1:Context); if (ContextPath) break; } if (ContextPath) { LTrimDir(ContextPath); char p[300]; LMakePath(p, sizeof(p), ContextPath, File); if (LFileExists(p)) { Full.Reset(NewStr(p)); } } else { LgiTrace("%s:%i - Context '%s' not found in project.\n", _FL, Context); } } if (!Full) { List::I Projs = Projects.begin(); for (IdeProject *p=*Projs; p; p=*++Projs) { LAutoString Base = p->GetBasePath(); if (Base) { char Path[MAX_PATH_LEN]; LMakePath(Path, sizeof(Path), Base, File); if (LFileExists(Path)) { Full.Reset(NewStr(Path)); break; } } } } if (!Full) { char *Dir = dirchar(File, true); for (auto p: Projects) { if (Full.Reset(p->FindFullPath(Dir?Dir+1:File))) break; } if (!Full) { if (LFileExists(File)) { Full.Reset(NewStr(File)); } } } return ValidStr(Full); } void ViewMsg(char *File, int Line, char *Context) { LAutoString Full; if (FindSource(Full, File, Context)) { App->GotoReference(Full, Line, false); } } #if 1 #define LOG_SEEK_MSG(...) printf(__VA_ARGS__) #else #define LOG_SEEK_MSG(...) #endif void GetContext(const char16 *Txt, ssize_t &i, char16 *&Context) { static char16 NMsg[] = L"In file included "; static char16 FromMsg[] = L"from "; auto NMsgLen = StrlenW(NMsg); if (Txt[i] != '\n') return; if (StrncmpW(Txt + i + 1, NMsg, NMsgLen)) return; i += NMsgLen + 1; while (Txt[i]) { // Skip whitespace while (Txt[i] && strchr(" \t\r\n", Txt[i])) i++; // Check for 'from' if (StrncmpW(FromMsg, Txt + i, 5)) break; i += 5; auto Start = Txt + i; // Skip to end of doc or line const char16 *Colon = 0; while (Txt[i] && Txt[i] != '\n') { if (Txt[i] == ':' && Txt[i+1] != '\n') { Colon = Txt + i; } i++; } if (Colon) { DeleteArray(Context); Context = NewStrW(Start, Colon-Start); } } } template bool IsTimeStamp(T *s, ssize_t i) { while (i > 0 && s[i-1] != '\n') i--; auto start = i; while (s[i] && (IsDigit(s[i]) || strchr(" :-", s[i]))) i++; LString txt(s + start, i - start); auto parts = txt.SplitDelimit(" :-"); return parts.Length() == 6 && parts[0].Length() == 4; } template bool IsContext(T *s, ssize_t i) { auto key = L"In file included"; auto end = i; while (i > 0 && s[i-1] != '\n') i--; if (Strnistr(s + i, key, end - i)) return true; return false; } #define PossibleLineSep(ch) \ ( (ch) == ':' || (ch) == '(' ) void SeekMsg(int Direction) { LString Comp; IdeProject *p = App->RootProject(); if (p) p ->GetSettings()->GetStr(ProjCompiler); // bool IsIAR = Comp.Equals("IAR"); if (!Output) return; int64 Current = Output->Value(); LTextView3 *o = Current < CountOf(Output->Txt) ? Output->Txt[Current] : 0; if (!o) return; auto Txt = o->NameW(); if (!Txt) return; ssize_t Cur = o->GetCaret(); char16 *Context = NULL; // Scan forward to the end of file for the next filename/line number separator. ssize_t i; for (i=Cur; Txt[i]; i++) { GetContext(Txt, i, Context); if ( PossibleLineSep(Txt[i]) && isdigit(Txt[i+1]) && !IsTimeStamp(Txt, i) && !IsContext(Txt, i) ) { break; } } // If not found then scan from the start of the file for the next filename/line number separator. if (!PossibleLineSep(Txt[i])) { for (i=0; i 0 && !strchr("\n>", Txt[Line-1])) { Line--; } // Store the filename LString File(Txt+Line, i-Line); if (!File) return; #if DIR_CHAR == '\\' File = File.Replace("/", "\\"); #else File = File.Replace("\\", "/"); #endif // Scan over the line number.. auto NumIndex = ++i; while (isdigit(Txt[NumIndex])) NumIndex++; // Store the line number LString NumStr(Txt + i, NumIndex - i); if (!NumStr) return; // Convert it to an integer auto LineNumber = (int)NumStr.Int(); o->SetCaret(Line, false); o->SetCaret(NumIndex + 1, true); LString Context8 = Context; ViewMsg(File, LineNumber, Context8); } void UpdateMenus() { static const char *None = "(none)"; if (!App->GetMenu()) return; // This happens in GTK during window destruction if (RecentFilesMenu) { RecentFilesMenu->Empty(); if (RecentFiles.Length() == 0) RecentFilesMenu->AppendItem(None, 0, false); else { int n=0; char *f; for (auto It = RecentFiles.begin(); (f = *It); f=*(++It)) { for (; f; f=*(++It)) { if (LIsUtf8(f)) RecentFilesMenu->AppendItem(f, IDM_RECENT_FILE+n++, true); else RecentFiles.Delete(It); } } } } if (RecentProjectsMenu) { RecentProjectsMenu->Empty(); if (RecentProjects.Length() == 0) RecentProjectsMenu->AppendItem(None, 0, false); else { int n=0; char *f; for (auto It = RecentProjects.begin(); (f = *It); f=*(++It)) { if (LIsUtf8(f)) RecentProjectsMenu->AppendItem(f, IDM_RECENT_PROJECT+n++, true); else RecentProjects.Delete(It); } } } if (WindowsMenu) { WindowsMenu->Empty(); Docs.Sort(DocSorter); int n=0; for (auto d: Docs) { const char *File = d->GetFileName(); if (!File) File = "(untitled)"; char *Dir = strrchr((char*)File, DIR_CHAR); WindowsMenu->AppendItem(Dir?Dir+1:File, IDM_WINDOWS+n++, true); } if (!Docs.Length()) { WindowsMenu->AppendItem(None, 0, false); } } } void Dump(LString::Array &a) { for (auto i: a) printf(" %s\n", i.Get()); } void OnFile(const char *File, bool IsProject = false) { if (!File) return; auto *Recent = IsProject ? &RecentProjects : &RecentFiles; for (auto &f: *Recent) { if (f && LFileCompare(f, File) == 0) { f = File; UpdateMenus(); return; } } Recent->AddAt(0, File); if (Recent->Length() > 10) Recent->Length(10); UpdateMenus(); } void RemoveRecent(const char *File) { if (File) { LString::Array *Recent[3] = { &RecentProjects, &RecentFiles, 0 }; for (int i=0; Recent[i]; i++) { auto &a = *Recent[i]; for (size_t n=0; nIsFile(File)) { return Doc; } } // LgiTrace("%s:%i - '%s' not found in %i docs.\n", _FL, File, Docs.Length()); return 0; } IdeProject *IsProjectOpen(const char *File) { if (File) { for (auto p: Projects) { if (p->GetFileName() && stricmp(p->GetFileName(), File) == 0) { return p; } } } return 0; } void SerializeStringList(const char *Opt, LString::Array *Lst, bool Write) { LVariant v; LString Sep = OptFileSeparator; if (Write) { if (Lst->Length() > 0) { auto s = Sep.Join(*Lst); Options.SetValue(Opt, v = s.Get()); // printf("Saving '%s' to %s\n", s.Get(), Opt); } else Options.DeleteValue(Opt); } else if (Options.GetValue(Opt, v)) { auto files = LString(v.Str()).Split(Sep); Lst->Length(0); for (auto f: files) if (f.Length() > 0) Lst->Add(f); // printf("Reading '%s' to %s, file.len=%i %s\n", v.Str(), Opt, (int)files.Length(), v.Str()); } // else printf("%s:%i - No option '%s' to read.\n", _FL, Opt); } }; AppWnd::AppWnd() { #ifdef __GTK_H__ LgiGetResObj(true, AppName); #endif LRect r(0, 0, 1000, 760); SetPos(r); MoveToCenter(); d = new AppWndPrivate(this); Name(AppName); SetQuitOnClose(true); #if WINNATIVE SetIcon((char*)MAKEINTRESOURCE(IDI_APP)); #else SetIcon("icon64.png"); #endif if (!Attach(0)) { LgiTrace("%s:%i - Attach failed.\n", _FL); return; } if ((Menu = new LMenu)) { Menu->Attach(this); bool Loaded = Menu->Load(this, "IDM_MENU"); LAssert(Loaded); if (Loaded) { Menu->SetPrefAndAboutItems(IDM_OPTIONS, IDM_ABOUT); d->RecentFilesMenu = Menu->FindSubMenu(IDM_RECENT_FILES); d->RecentProjectsMenu = Menu->FindSubMenu(IDM_RECENT_PROJECTS); d->WindowsMenu = Menu->FindSubMenu(IDM_WINDOW_LST); d->CreateMakefileMenu = Menu->FindSubMenu(IDM_CREATE_MAKEFILE); if (d->CreateMakefileMenu) { d->CreateMakefileMenu->Empty(); for (int i=0; PlatformNames[i]; i++) { d->CreateMakefileMenu->AppendItem(PlatformNames[i], IDM_MAKEFILE_BASE + i); } } else LgiTrace("%s:%i - FindSubMenu failed.\n", _FL); LMenuItem *Debug = GetMenu()->FindItem(IDM_DEBUG_MODE); if (Debug) Debug->Checked(true); else LgiTrace("%s:%i - FindSubMenu failed.\n", _FL); d->UpdateMenus(); } } LToolBar *Tools = NULL; if (GdcD->Y() > 1200) Tools = LgiLoadToolbar(this, "cmds-32px.png", 32, 32); else Tools = LgiLoadToolbar(this, "cmds-16px.png", 16, 16); if (Tools) { Tools->AppendButton("New", IDM_NEW, TBT_PUSH, true, CMD_NEW); Tools->AppendButton("Open", IDM_OPEN, TBT_PUSH, true, CMD_OPEN); Tools->AppendButton("Save", IDM_SAVE_ALL, TBT_PUSH, true, CMD_SAVE_ALL); Tools->AppendSeparator(); Tools->AppendButton("Cut", IDM_CUT, TBT_PUSH, true, CMD_CUT); Tools->AppendButton("Copy", IDM_COPY, TBT_PUSH, true, CMD_COPY); Tools->AppendButton("Paste", IDM_PASTE, TBT_PUSH, true, CMD_PASTE); Tools->AppendSeparator(); Tools->AppendButton("Compile", IDM_COMPILE, TBT_PUSH, true, CMD_COMPILE); Tools->AppendButton("Build", IDM_BUILD, TBT_PUSH, true, CMD_BUILD); Tools->AppendButton("Stop", IDM_STOP_BUILD, TBT_PUSH, true, CMD_STOP_BUILD); // Tools->AppendButton("Execute", IDM_EXECUTE, TBT_PUSH, true, CMD_EXECUTE); Tools->AppendSeparator(); Tools->AppendButton("Debug", IDM_START_DEBUG, TBT_PUSH, true, CMD_DEBUG); Tools->AppendButton("Pause", IDM_PAUSE_DEBUG, TBT_PUSH, true, CMD_PAUSE); Tools->AppendButton("Restart", IDM_RESTART_DEBUGGING, TBT_PUSH, true, CMD_RESTART); Tools->AppendButton("Kill", IDM_STOP_DEBUG, TBT_PUSH, true, CMD_KILL); Tools->AppendButton("Step Into", IDM_STEP_INTO, TBT_PUSH, true, CMD_STEP_INTO); Tools->AppendButton("Step Over", IDM_STEP_OVER, TBT_PUSH, true, CMD_STEP_OVER); Tools->AppendButton("Step Out", IDM_STEP_OUT, TBT_PUSH, true, CMD_STEP_OUT); Tools->AppendButton("Run To", IDM_RUN_TO, TBT_PUSH, true, CMD_RUN_TO); Tools->AppendSeparator(); Tools->AppendButton("Find In Files", IDM_FIND_IN_FILES, TBT_PUSH, true, CMD_FIND_IN_FILES); Tools->GetCss(true)->Padding("4px"); Tools->Attach(this); } else LgiTrace("%s:%i - No tools obj?", _FL); LVariant v = 270, OutPx = 250; d->Options.GetValue(OPT_SPLIT_PX, v); d->Options.GetValue(OPT_OUTPUT_PX, OutPx); AddView(d->VBox = new LBox); d->VBox->SetVertical(true); d->HBox = new LBox; d->VBox->AddView(d->HBox); d->VBox->AddView(d->Output = new IdeOutput(this)); d->HBox->AddView(d->Tree = new IdeTree); if (d->Tree) { d->Tree->SetImageList(d->Icons, false); d->Tree->Sunken(false); } d->HBox->AddView(d->Mdi = new LMdiParent); if (d->Mdi) { d->Mdi->HasButton(true); } d->HBox->Value(MAX(v.CastInt32(), 20)); LRect c = GetClient(); if (c.Y() > OutPx.CastInt32()) { auto Px = OutPx.CastInt32(); LCss::Len y(LCss::LenPx, (float)MAX(Px, 120)); d->Output->GetCss(true)->Height(y); } AttachChildren(); OnPosChange(); UpdateState(); Visible(true); DropTarget(true); SetPulse(1000); #ifdef LINUX LFinishXWindowsStartup(this); #endif #if USE_HAIKU_PULSE_HACK if (d->Output) d->Output->SetPulse(1000); #endif OnCommand(IDM_NEW, 0, NULL); } AppWnd::~AppWnd() { LAssert(IsClean()); WaitThread(); if (d->HBox) { LVariant v = d->HBox->Value(); d->Options.SetValue(OPT_SPLIT_PX, v); } if (d->Output) { LVariant v = d->Output->Y(); d->Options.SetValue(OPT_OUTPUT_PX, v); } ShutdownFtpThread(); LAppInst->AppWnd = NULL; DeleteObj(d); } void AppWnd::OnPulse() { IdeDoc *Top = TopDoc(); if (Top) Top->OnPulse(); if (d->FixBuildWait) { d->FixBuildWait = false; if (OnFixBuildErrors() > 0) d->RebuildWait = 3; } else if (d->RebuildWait > 0) { if (--d->RebuildWait == 0) Build(); } } LDebugContext *AppWnd::GetDebugContext() { return d->DbgContext; } struct DumpBinThread : public LThread { LStream *Out; LString InFile; bool IsLib; public: DumpBinThread(LStream *out, LString file) : LThread("DumpBin.Thread") { Out = out; InFile = file; DeleteOnExit = true; auto Ext = LGetExtension(InFile); IsLib = Ext && !stricmp(Ext, "lib"); Run(); } bool DumpBin(LString Args, LStream *Str) { char Buf[256]; ssize_t Rd; const char *Prog = "c:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\bin\\dumpbin.exe"; LSubProcess s(Prog, Args); if (!s.Start(true, false)) { Out->Print("%s:%i - '%s' doesn't exist.\n", _FL, Prog); return false; } while ((Rd = s.Read(Buf, sizeof(Buf))) > 0) Str->Write(Buf, Rd); return true; } LString::Array Dependencies(const char *Executable, int Depth = 0) { LString Args; LStringPipe p; Args.Printf("/dependents \"%s\"", Executable); DumpBin(Args, &p); char Spaces[256]; int Len = Depth * 2; memset(Spaces, ' ', Len); Spaces[Len] = 0; LString::Array Files; auto Parts = p.NewGStr().Replace("\r", "").Split("\n\n"); if (Parts.Length() > 0) { Files = Parts[4].Strip().Split("\n"); auto Path = LGetPath(); for (size_t i=0; i= 0) { auto p = Ln.Strip().Split(Key); if (p.Length() == 2) { Arch = p[1].Strip("()"); Machine = p[0].Int(16); } } } if (Machine == 0x14c) Arch += " 32bit"; else if (Machine == 0x200) Arch += " 64bit Itanium"; else if (Machine == 0x8664) Arch += " 64bit"; return Arch; } LString GetExports() { LString Args; LStringPipe p; /* if (IsLib) Args.Printf("/symbols \"%s\"", InFile.Get()); else */ Args.Printf("/exports \"%s\"", InFile.Get()); DumpBin(Args, &p); LString Exp; auto Raw = p.NewGStr().Replace("\r", ""); auto Sect = Raw.Split("\n\n"); #if 0 // Debug output Exp = Raw; #else if (IsLib) { for (auto &s : Sect) { if (s.Find("COFF/PE Dumper") >= 0) continue; auto ln = s.Split("\n"); if (ln.Length() == 1) continue; for (auto &l: ln) l = l.LStrip(); Exp = LString("\n").Join(ln); break; } } else { bool Ord = false; for (auto &s : Sect) { if (s.Strip().Find("ordinal") == 0) Ord = true; else if (Ord) { Exp = s; break; } else Ord = false; } } #endif return Exp; } int Main() { if (!IsLib) { auto Deps = Dependencies(InFile); if (Deps.Length()) Out->Print("Dependencies:\n\t%s\n\n", LString("\n\t").Join(Deps).Get()); } auto Arch = GetArch(); if (Arch) Out->Print("Arch: %s\n\n", Arch.Get()); auto Exp = GetExports(); if (Arch) Out->Print("Exports:\n%s\n\n", Exp.Get()); return 0; } }; void AppWnd::OnReceiveFiles(LArray &Files) { for (int i=0; iSetFileName(Docs, false); new DumpBinThread(Doc, Files[i]); } } else { OpenFile(f); } } Raise(); if (LAppInst->GetOption("createMakeFiles")) { IdeProject *p = RootProject(); if (p) { p->CreateMakefile(PlatformCurrent, false); } } } void AppWnd::OnDebugState(bool Debugging, bool Running) { // Make sure this event is processed in the GUI thread. #if DEBUG_SESSION_LOGGING LgiTrace("AppWnd::OnDebugState(%i,%i) InThread=%i\n", Debugging, Running, InThread()); #endif PostEvent(M_DEBUG_ON_STATE, Debugging, Running); } bool IsVarChar(LString &s, ssize_t pos) { if (pos < 0) return false; if (pos >= s.Length()) return false; char i = s[pos]; return IsAlpha(i) || IsDigit(i) || i == '_'; } bool ReplaceWholeWord(LString &Ln, LString Word, LString NewWord) { ssize_t Pos = 0; bool Status = false; while (Pos >= 0) { Pos = Ln.Find(Word, Pos); if (Pos < 0) return Status; ssize_t End = Pos + Word.Length(); if (!IsVarChar(Ln, Pos-1) && !IsVarChar(Ln, End)) { LString NewLn = Ln(0,Pos) + NewWord + Ln(End,-1); Ln = NewLn; Status = true; } Pos++; } return Status; } struct LFileInfo { LString Path; LString::Array Lines; bool Dirty; LFileInfo() { Dirty = false; } bool Save() { LFile f; if (!f.Open(Path, O_WRITE)) return false; LString NewFile = LString("\n").Join(Lines); f.SetSize(0); f.Write(NewFile); f.Close(); return true; } }; int AppWnd::OnFixBuildErrors() { LHashTbl, LString> Map; LVariant v; if (GetOptions()->GetValue(OPT_RENAMED_SYM, v)) { auto Lines = LString(v.Str()).Split("\n"); for (auto Ln: Lines) { auto p = Ln.SplitDelimit(); if (p.Length() == 2) Map.Add(p[0], p[1]); } } LString Raw = d->Output->Txt[AppWnd::BuildTab]->Name(); LString::Array Lines = Raw.Split("\n"); auto *Log = d->Output->Txt[AppWnd::OutputTab]; Log->Name(NULL); Log->Print("Parsing errors...\n"); int Replacements = 0; LArray Files; LHashTbl,bool> FixHistory; for (int Idx=0; Idx= 0) { #ifdef WINDOWS LString::Array p = Ln.SplitDelimit(">()"); #else LString::Array p = Ln(0, ErrPos).Strip().SplitDelimit(":"); #endif if (p.Length() <= 2) { Log->Print("Error: Only %i parts? '%s'\n", (int)p.Length(), Ln.Get()); } else { #ifdef WINDOWS int Base = p[0].IsNumeric() ? 1 : 0; LString Fn = p[Base]; if (Fn.Find("Program Files") >= 0) { Log->Print("Is prog file\n"); continue; } auto LineNo = p[Base+1].Int(); bool FileNotFound = Ln.Find("Cannot open include file:") > 0; #else LString Fn = p[0]; auto LineNo = p[1].Int(); bool FileNotFound = false; // fixme #endif LAutoString Full; if (!d->FindSource(Full, Fn, NULL)) { Log->Print("Error: Can't find Fn='%s' Line=%i\n", Fn.Get(), (int)LineNo); continue; } LFileInfo *Fi = NULL; for (auto &i: Files) { if (i.Path.Equals(Full)) { Fi = &i; break; } } if (!Fi) { LFile f(Full, O_READ); if (f.IsOpen()) { Fi = &Files.New(); Fi->Path = Full.Get(); auto OldFile = f.Read(); Fi->Lines = OldFile.SplitDelimit("\n", -1, false); } else { Log->Print("Error: Can't open '%s'\n", Full.Get()); } } if (Fi) { LString Loc; Loc.Printf("%s:%i", Full.Get(), (int)LineNo); if (FixHistory.Find(Loc)) { // Log->Print("Already fixed %s\n", Loc.Get()); } else if (LineNo <= Fi->Lines.Length()) { FixHistory.Add(Loc, true); if (FileNotFound) { auto n = p.Last().SplitDelimit("\'"); auto wrongName = n[1]; LFile f(Full, O_READ); auto Lines = f.Read().SplitDelimit("\n", -1, false); f.Close(); if (LineNo <= Lines.Length()) { auto &errLine = Lines[LineNo-1]; auto Pos = errLine.Find(wrongName); /* if (Pos < 0) { for (int i=0; iPrint("[%i]=%s\n", i, Lines[i].Get()); } */ if (Pos > 0) { // Find where it went... LString newPath; for (auto p: d->Projects) { const char *SubStr[] = { ".", "lgi/common" }; LArray IncPaths; if (p->BuildIncludePaths(IncPaths, true, false, PlatformCurrent)) { for (auto &inc: IncPaths) { for (int sub=0; !newPath && subPrint("Already changed '%s'.\n", wrongName.Get()); } else { LString backup = LString(Full.Get()) + ".orig"; if (LFileExists(backup)) FileDev->Delete(backup); LError Err; if (FileDev->Move(Full, backup, &Err)) { errLine = newLine; LString newLines = LString("\n").Join(Lines); LFile out(Full, O_WRITE); out.Write(newLines); Log->Print("Fixed '%s'->'%s' on ln %i in %s\n", wrongName.Get(), newPath.Get(), (int)LineNo, Full.Get()); Replacements++; } else Log->Print("Error: moving '%s' to backup (%s).\n", Full.Get(), Err.GetMsg().Get()); } } else Log->Print("Error: Missing header '%s'.\n", wrongName.Get()); } else { Log->Print("Error: '%s' not found in line %i of '%s' -> '%s'\n", wrongName.Get(), (int)LineNo, Fn.Get(), Full.Get()); // return; } } else Log->Print("Error: Line %i is beyond file lines: %i\n", (int)LineNo, (int)Lines.Length()); } else { auto OldReplacements = Replacements; for (auto i: Map) { for (int Offset = 0; (LineNo + Offset >= 1) && Offset >= -1; Offset--) { LString &s = Fi->Lines[LineNo+Offset-1]; if (ReplaceWholeWord(s, i.key, i.value)) { Log->Print("Renamed '%s' -> '%s' at %s:%i\n", i.key, i.value.Get(), Full.Get(), LineNo+Offset); Fi->Dirty = true; Replacements++; Offset = -2; } } } if (OldReplacements == Replacements && Ln.Find("syntax error: id") > 0) { Log->Print("Unhandled: %s\n", Ln.Get()); } } } else { Log->Print("Error: Invalid line %i\n", (int)LineNo); } } else { Log->Print("Error: Fi is NULL\n"); } } } } for (auto &Fi : Files) { if (Fi.Dirty) Fi.Save(); } Log->Print("%i replacements made.\n", Replacements); if (Replacements > 0) d->Output->Value(AppWnd::OutputTab); return Replacements; } void AppWnd::OnBuildStateChanged(bool NewState) { LVariant v; if (!NewState && GetOptions()->GetValue(OPT_FIX_RENAMED, v) && v.CastInt32()) { d->FixBuildWait = true; } } void AppWnd::UpdateState(int Debugging, int Building) { // printf("UpdateState %i %i\n", Debugging, Building); if (Debugging >= 0) d->Debugging = Debugging; if (Building >= 0) { if (d->Building != (Building != 0)) OnBuildStateChanged(Building); d->Building = Building; } SetCtrlEnabled(IDM_COMPILE, !d->Building); SetCtrlEnabled(IDM_BUILD, !d->Building); SetCtrlEnabled(IDM_STOP_BUILD, d->Building); // SetCtrlEnabled(IDM_RUN, !d->Building); // SetCtrlEnabled(IDM_TOGGLE_BREAKPOINT, !d->Building); SetCtrlEnabled(IDM_START_DEBUG, !d->Debugging && !d->Building); SetCtrlEnabled(IDM_PAUSE_DEBUG, d->Debugging); SetCtrlEnabled(IDM_RESTART_DEBUGGING, d->Debugging); SetCtrlEnabled(IDM_STOP_DEBUG, d->Debugging); SetCtrlEnabled(IDM_STEP_INTO, d->Debugging); SetCtrlEnabled(IDM_STEP_OVER, d->Debugging); SetCtrlEnabled(IDM_STEP_OUT, d->Debugging); SetCtrlEnabled(IDM_RUN_TO, d->Debugging); } void AppWnd::AppendOutput(char *Txt, AppWnd::Channels Channel) { if (!d->Output) { LgiTrace("%s:%i - No output panel.\n", _FL); return; } if (Channel < 0 || Channel >= CountOf(d->Output->Txt)) { LgiTrace("%s:%i - Channel range: %i, %i.\n", _FL, Channel, CountOf(d->Output->Txt)); return; } if (!d->Output->Txt[Channel]) { LgiTrace("%s:%i - No log for channel %i.\n", _FL, Channel); return; } if (Txt) { d->Output->Buf[Channel].Add(Txt, strlen(Txt)); } else { auto Ctrl = d->Output->Txt[Channel]; Ctrl->UnSelectAll(); Ctrl->Name(""); } } bool AppWnd::IsClean() { for (auto Doc: d->Docs) { if (!Doc->GetClean()) return false; } for (auto Proj: d->Projects) { if (!Proj->GetClean()) return false; } return true; } struct SaveState { AppWndPrivate *d = NULL; LArray Docs; LArray Projects; std::function Callback; bool Status = true; bool CloseDirty = false; void Iterate() { if (Docs.Length()) { auto doc = Docs[0]; Docs.DeleteAt(0); // printf("Saving doc...\n"); doc->SetClean([this, doc](bool ok) { // printf("SetClean cb ok=%i\n", ok); if (ok) d->OnFile(doc->GetFileName()); else { if (CloseDirty) delete doc; Status = false; } // printf("SetClean cb iter\n", ok); Iterate(); }); } else if (Projects.Length()) { auto proj = Projects[0]; Projects.DeleteAt(0); // printf("Saving proj...\n"); proj->SetClean([this, proj](bool ok) { if (ok) d->OnFile(proj->GetFileName(), true); else { if (CloseDirty) delete proj; Status = false; } Iterate(); }); } else { // printf("Doing callback...\n"); if (Callback) Callback(Status); // printf("Deleting...\n"); delete this; } } }; void AppWnd::SaveAll(std::function Callback, bool CloseDirty) { auto ss = new SaveState; ss->d = d; ss->Callback = Callback; ss->CloseDirty = CloseDirty; for (auto Doc: d->Docs) { if (!Doc->GetClean()) ss->Docs.Add(Doc); } for (auto Proj: d->Projects) { if (!Proj->GetClean()) ss->Projects.Add(Proj); } ss->Iterate(); } void AppWnd::CloseAll() { SaveAll([&](auto status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } while (d->Docs[0]) delete d->Docs[0]; IdeProject *p = RootProject(); if (p) DeleteObj(p); while (d->Projects[0]) delete d->Projects[0]; DeleteObj(d->DbgContext); }); } bool AppWnd::OnRequestClose(bool IsOsQuit) { if (!IsClean()) { SaveAll([](bool status) { LCloseApp(); }, true); return false; } else { return LWindow::OnRequestClose(IsOsQuit); } } bool AppWnd::OnBreakPoint(LDebugger::BreakPoint &b, bool Add) { List::I it = d->Docs.begin(); for (IdeDoc *doc = *it; doc; doc = *++it) { auto fn = doc->GetFileName(); bool Match = !Stricmp(fn, b.File.Get()); if (Match) doc->AddBreakPoint(b.Line, Add); } if (d->DbgContext) d->DbgContext->OnBreakPoint(b, Add); return true; } bool AppWnd::LoadBreakPoints(IdeDoc *doc) { if (!doc) return false; auto fn = doc->GetFileName(); for (int i=0; iBreakPoints.Length(); i++) { LDebugger::BreakPoint &b = d->BreakPoints[i]; if (!_stricmp(fn, b.File)) { doc->AddBreakPoint(b.Line, true); } } return true; } bool AppWnd::LoadBreakPoints(LDebugger *db) { if (!db) return false; for (int i=0; iBreakPoints.Length(); i++) { LDebugger::BreakPoint &bp = d->BreakPoints[i]; db->SetBreakPoint(&bp); } return true; } bool AppWnd::ToggleBreakpoint(const char *File, ssize_t Line) { bool DeleteBp = false; for (int i=0; iBreakPoints.Length(); i++) { LDebugger::BreakPoint &b = d->BreakPoints[i]; if (!_stricmp(File, b.File) && b.Line == Line) { OnBreakPoint(b, false); d->BreakPoints.DeleteAt(i); DeleteBp = true; break; } } if (!DeleteBp) { LDebugger::BreakPoint &b = d->BreakPoints.New(); b.File = File; b.Line = Line; OnBreakPoint(b, true); } return true; } void AppWnd::DumpHistory() { #if 0 LgiTrace("History %i of %i\n", d->HistoryLoc, d->CursorHistory.Length()); for (int i=0; iCursorHistory.Length(); i++) { FileLoc &p = d->CursorHistory[i]; LgiTrace(" [%i] = %s, %i %s\n", i, p.File.Get(), p.Line, d->HistoryLoc == i ? "<-----":""); } #endif } /* void CheckHistory(LArray &CursorHistory) { if (CursorHistory.Length() > 0) { FileLoc *loc = &CursorHistory[0]; for (unsigned i=CursorHistory.Length(); iInHistorySeek) { if (d->CursorHistory.Length() > 0) { FileLoc &Last = d->CursorHistory.Last(); if (_stricmp(File, Last.File) == 0 && abs(Last.Line - Line) <= 1) { // Previous or next line... just update line number Last.Line = Line; DumpHistory(); return; } // Add new entry d->HistoryLoc++; FileLoc &loc = d->CursorHistory[d->HistoryLoc]; #ifdef WIN64 if ((NativeInt)loc.File.Get() == 0xcdcdcdcdcdcdcdcd) LAssert(0); // wtf? else #endif loc.Set(File, Line); } else { // Add new entry d->CursorHistory[0].Set(File, Line); } // Destroy any history after the current... d->CursorHistory.Length(d->HistoryLoc+1); DumpHistory(); } } void AppWnd::OnFile(char *File, bool IsProject) { d->OnFile(File, IsProject); } IdeDoc *AppWnd::NewDocWnd(const char *FileName, NodeSource *Src) { IdeDoc *Doc = new IdeDoc(this, Src, 0); if (Doc) { d->Docs.Insert(Doc); LRect p = d->Mdi->NewPos(); Doc->LView::SetPos(p); Doc->Attach(d->Mdi); Doc->Focus(true); Doc->Raise(); if (FileName) d->OnFile(FileName); } return Doc; } IdeDoc *AppWnd::GetCurrentDoc() { if (d->Mdi) return dynamic_cast(d->Mdi->GetTop()); return NULL; } IdeDoc *AppWnd::GotoReference(const char *File, int Line, bool CurIp, bool WithHistory) { if (!WithHistory) d->InHistorySeek = true; IdeDoc *Doc = File ? OpenFile(File) : GetCurrentDoc(); if (Doc) { Doc->SetLine(Line, CurIp); Doc->Focus(true); } if (!WithHistory) d->InHistorySeek = false; return Doc; } IdeDoc *AppWnd::FindOpenFile(char *FileName) { List::I it = d->Docs.begin(); for (IdeDoc *i=*it; i; i=*++it) { auto f = i->GetFileName(); if (f) { IdeProject *p = i->GetProject(); if (p) { LAutoString Base = p->GetBasePath(); if (Base) { char Path[MAX_PATH_LEN]; if (*f == '.') LMakePath(Path, sizeof(Path), Base, f); else strcpy_s(Path, sizeof(Path), f); if (stricmp(Path, FileName) == 0) return i; } } else { if (stricmp(f, FileName) == 0) return i; } } } return 0; } IdeDoc *AppWnd::OpenFile(const char *FileName, NodeSource *Src) { static bool DoingProjectFind = false; IdeDoc *Doc = 0; const char *File = Src ? Src->GetFileName() : FileName; if (!Src && !ValidStr(File)) { LgiTrace("%s:%i - No source or file?\n", _FL); return NULL; } LString FullPath; if (LIsRelativePath(File)) { IdeProject *Proj = Src && Src->GetProject() ? Src->GetProject() : RootProject(); if (Proj) { List Projs; Projs.Insert(Proj); Proj->CollectAllSubProjects(Projs); for (auto p: Projs) { auto ProjPath = p->GetBasePath(); char s[MAX_PATH_LEN]; LMakePath(s, sizeof(s), ProjPath, File); LString Path = s; if (p->CheckExists(Path)) { FullPath = Path; File = FullPath; break; } } } } // Sniff type... bool probablyLgiProj = false; if (!Stricmp(LGetExtension(File), "xml")) { LFile f(File, O_READ); if (f) { char buf[256]; auto rd = f.Read(buf, sizeof(buf)); if (rd > 0) probablyLgiProj = Strnistr(buf, "IsFileOpen(File); if (!Doc) { if (Src) { Doc = NewDocWnd(File, Src); } else if (!DoingProjectFind) { DoingProjectFind = true; List::I Proj = d->Projects.begin(); for (IdeProject *p=*Proj; p && !Doc; p=*++Proj) { p->InProject(LIsRelativePath(File), File, true, &Doc); } DoingProjectFind = false; d->OnFile(File); } } if (!Doc && LFileExists(File)) { Doc = new IdeDoc(this, 0, File); if (Doc) { Doc->OpenFile(File); LRect p = d->Mdi->NewPos(); Doc->LView::SetPos(p); d->Docs.Insert(Doc); d->OnFile(File); } } if (Doc) { Doc->SetEditorParams(4, 4, true, false); if (!Doc->IsAttached()) { Doc->Attach(d->Mdi); } Doc->Focus(true); Doc->Raise(); } return Doc; } IdeProject *AppWnd::RootProject() { for (auto p: d->Projects) if (!p->GetParentProject()) return p; return NULL; } IdeProject *AppWnd::OpenProject(const char *FileName, IdeProject *ParentProj, bool Create, bool Dep) { if (!FileName) { LgiTrace("%s:%i - Error: No filename.\n", _FL); return NULL; } if (d->IsProjectOpen(FileName)) { LgiTrace("%s:%i - Warning: Project already open.\n", _FL); return NULL; } IdeProject *p = new IdeProject(this); if (!p) { LgiTrace("%s:%i - Error: mem alloc.\n", _FL); return NULL; } LString::Array Inc; p->BuildIncludePaths(Inc, false, false, PlatformCurrent); d->FindSym->SetIncludePaths(Inc); p->SetParentProject(ParentProj); ProjectStatus Status = p->OpenFile(FileName); if (Status == OpenOk) { d->Projects.Insert(p); d->OnFile(FileName, true); if (!Dep) { auto d = strrchr(FileName, DIR_CHAR); if (d++) { char n[256]; sprintf(n, "%s [%s]", AppName, d); Name(n); } } } else { LgiTrace("%s:%i - Failed to open '%s'\n", _FL, FileName); DeleteObj(p); if (Status == OpenError) d->RemoveRecent(FileName); } if (!GetTree()->Selection()) { GetTree()->Select(GetTree()->GetChild()); } GetTree()->Focus(true); return p; } LMessage::Result AppWnd::OnEvent(LMessage *m) { switch (m->Msg()) { case M_MAKEFILES_CREATED: { IdeProject *p = (IdeProject*)m->A(); if (p) p->OnMakefileCreated(); break; } case M_LAST_MAKEFILE_CREATED: { if (LAppInst->GetOption("exit")) LCloseApp(); break; } case M_START_BUILD: { IdeProject *p = RootProject(); if (p) p->Build(true, GetBuildMode()); else printf("%s:%i - No root project.\n", _FL); break; } case M_BUILD_DONE: { UpdateState(-1, false); IdeProject *p = RootProject(); if (p) p->StopBuild(); break; } case M_BUILD_ERR: { char *Msg = (char*)m->B(); if (Msg) { d->Output->Txt[AppWnd::BuildTab]->Print("Build Error: %s\n", Msg); DeleteArray(Msg); } break; } case M_APPEND_TEXT: { LAutoString Text((char*) m->A()); Channels Ch = (Channels) m->B(); AppendOutput(Text, Ch); break; } case M_SELECT_TAB: { if (!d->Output) break; d->Output->Value(m->A()); break; } case M_DEBUG_ON_STATE: { bool Debugging = m->A(); bool Running = m->B(); if (d->Running != Running) { bool RunToNotRun = d->Running && !Running; d->Running = Running; if (RunToNotRun && d->Output && d->Output->DebugTab) { d->Output->DebugTab->SendNotify(LNotifyValueChanged); } } if (d->Debugging != Debugging) { d->Debugging = Debugging; if (!Debugging) { IdeDoc::ClearCurrentIp(); IdeDoc *c = GetCurrentDoc(); if (c) c->UpdateControl(); // Shutdown the debug context and free the memory DeleteObj(d->DbgContext); } } SetCtrlEnabled(IDM_START_DEBUG, !Debugging || !Running); SetCtrlEnabled(IDM_PAUSE_DEBUG, Debugging && Running); SetCtrlEnabled(IDM_RESTART_DEBUGGING, Debugging); SetCtrlEnabled(IDM_STOP_DEBUG, Debugging); SetCtrlEnabled(IDM_STEP_INTO, Debugging && !Running); SetCtrlEnabled(IDM_STEP_OVER, Debugging && !Running); SetCtrlEnabled(IDM_STEP_OUT, Debugging && !Running); SetCtrlEnabled(IDM_RUN_TO, Debugging && !Running); break; } default: { if (d->DbgContext) d->DbgContext->OnEvent(m); break; } } return LWindow::OnEvent(m); } bool AppWnd::OnNode(const char *Path, ProjectNode *Node, FindSymbolSystem::SymAction Action) { // This takes care of adding/removing files from the symbol search engine. if (!Path || !Node) return false; if (d->FindSym) d->FindSym->OnFile(Path, Action, Node->GetPlatforms()); return true; } LOptionsFile *AppWnd::GetOptions() { return &d->Options; } class Options : public LDialog { AppWnd *App; LFontType Font; public: Options(AppWnd *a) { SetParent(App = a); if (LoadFromResource(IDD_OPTIONS)) { SetCtrlEnabled(IDC_FONT, false); MoveToCenter(); if (!Font.Serialize(App->GetOptions(), OPT_EditorFont, false)) { Font.GetSystemFont("Fixed"); } char s[256]; if (Font.GetDescription(s, sizeof(s))) { SetCtrlName(IDC_FONT, s); } LVariant v; if (App->GetOptions()->GetValue(OPT_Jobs, v)) SetCtrlValue(IDC_JOBS, v.CastInt32()); else SetCtrlValue(IDC_JOBS, 2); } } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDOK: { LVariant v; Font.Serialize(App->GetOptions(), OPT_EditorFont, true); App->GetOptions()->SetValue(OPT_Jobs, v = GetCtrlValue(IDC_JOBS)); // Fall through.. } case IDCANCEL: { EndModal(c->GetId()); break; } case IDC_SET_FONT: { Font.DoUI(this, [&](auto ui) { char s[256]; if (Font.GetDescription(s, sizeof(s))) { SetCtrlName(IDC_FONT, s); } }); break; } } return 0; } }; void AppWnd::UpdateMemoryDump() { if (d->DbgContext) { const char *sWord = GetCtrlName(IDC_MEM_SIZE); int iWord = sWord ? atoi(sWord) : 1; int64 RowLen = GetCtrlValue(IDC_MEM_ROW_LEN); bool InHex = GetCtrlValue(IDC_MEM_HEX) != 0; d->DbgContext->FormatMemoryDump(iWord, (int)RowLen, InHex); } } int AppWnd::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDC_PROJECT_TREE: { if (n.Type == LNotifyDeleteKey) { ProjectNode *n = dynamic_cast(d->Tree->Selection()); if (n) n->Delete(); } break; } case IDC_DEBUG_EDIT: { if (n.Type == LNotifyReturnKey && d->DbgContext) { const char *Cmd = Ctrl->Name(); if (Cmd) { d->DbgContext->OnUserCommand(Cmd); Ctrl->Name(NULL); } } break; } case IDC_MEM_ADDR: { if (n.Type == LNotifyReturnKey) { if (d->DbgContext) { const char *s = Ctrl->Name(); if (s) { auto sWord = GetCtrlName(IDC_MEM_SIZE); int iWord = sWord ? atoi(sWord) : 1; d->DbgContext->OnMemoryDump(s, iWord, (int)GetCtrlValue(IDC_MEM_ROW_LEN), GetCtrlValue(IDC_MEM_HEX) != 0); } else if (d->DbgContext->MemoryDump) { d->DbgContext->MemoryDump->Print("No address specified."); } else { LAssert(!"No MemoryDump."); } } else LAssert(!"No debug context."); } break; } case IDC_MEM_ROW_LEN: { if (n.Type == LNotifyReturnKey) UpdateMemoryDump(); break; } case IDC_MEM_HEX: case IDC_MEM_SIZE: { UpdateMemoryDump(); break; } case IDC_DEBUG_TAB: { if (d->DbgContext && n.Type == LNotifyValueChanged) { switch (Ctrl->Value()) { case AppWnd::LocalsTab: { d->DbgContext->UpdateLocals(); break; } case AppWnd::WatchTab: { d->DbgContext->UpdateWatches(); break; } case AppWnd::RegistersTab: { d->DbgContext->UpdateRegisters(); break; } case AppWnd::CallStackTab: { d->DbgContext->UpdateCallStack(); break; } case AppWnd::ThreadsTab: { d->DbgContext->UpdateThreads(); break; } default: break; } } break; } case IDC_LOCALS_LIST: { if (d->Output->Locals && n.Type == LNotifyItemDoubleClick && d->DbgContext) { LListItem *it = d->Output->Locals->GetSelected(); if (it) { const char *Var = it->GetText(2); const char *Val = it->GetText(3); if (Var) { if (d->Output->DebugTab) d->Output->DebugTab->Value(AppWnd::ObjectTab); d->DbgContext->DumpObject(Var, Val); } } } break; } case IDC_CALL_STACK: { if (n.Type == LNotifyValueChanged) { if (d->Output->DebugTab) d->Output->DebugTab->Value(AppWnd::CallStackTab); } else if (n.Type == LNotifyItemSelect) { // This takes the user to a given call stack reference if (d->Output->CallStack && d->DbgContext) { LListItem *item = d->Output->CallStack->GetSelected(); if (item) { LAutoString File; int Line; if (d->DbgContext->ParseFrameReference(item->GetText(1), File, Line)) { LAutoString Full; if (d->FindSource(Full, File, NULL)) { GotoReference(Full, Line, false); const char *sFrame = item->GetText(0); if (sFrame && IsDigit(*sFrame)) d->DbgContext->SetFrame(atoi(sFrame)); } } } } } break; } case IDC_WATCH_LIST: { WatchItem *Edit = NULL; switch (n.Type) { case LNotifyDeleteKey: { LArray Sel; for (LTreeItem *c = d->Output->Watch->GetChild(); c; c = c->GetNext()) { if (c->Select()) Sel.Add(c); } Sel.DeleteObjects(); break; } case LNotifyItemClick: { Edit = dynamic_cast(d->Output->Watch->Selection()); break; } case LNotifyContainerClick: { // Create new watch. Edit = new WatchItem(d->Output); if (Edit) d->Output->Watch->Insert(Edit); break; } default: break; } if (Edit) Edit->EditLabel(0); break; } case IDC_THREADS: { if (n.Type == LNotifyItemSelect) { // This takes the user to a given thread if (d->Output->Threads && d->DbgContext) { LListItem *item = d->Output->Threads->GetSelected(); if (item) { LString sId = item->GetText(0); int ThreadId = (int)sId.Int(); if (ThreadId > 0) { d->DbgContext->SelectThread(ThreadId); } } } } break; } } return 0; } bool AppWnd::Build() { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL); return; } IdeDoc *Top; IdeProject *p = RootProject(); if (p) { UpdateState(-1, true); p->Build(false, GetBuildMode()); } else if ((Top = TopDoc())) { Top->Build(); } }); return false; } class RenameDlg : public LDialog { AppWnd *App; public: static RenameDlg *Inst; RenameDlg(AppWnd *a) { Inst = this; SetParent(App = a); MoveSameScreen(a); if (LoadFromResource(IDC_RENAME)) { LVariant v; if (App->GetOptions()->GetValue(OPT_FIX_RENAMED, v)) SetCtrlValue(IDC_FIX_RENAMED, v.CastInt32()); if (App->GetOptions()->GetValue(OPT_RENAMED_SYM, v)) SetCtrlName(IDC_SYM, v.Str()); SetAlwaysOnTop(true); DoModeless(); } } ~RenameDlg() { Inst = NULL; } int OnNotify(LViewI *c, LNotification n) { switch (c->GetId()) { case IDC_APPLY: { LVariant v; App->GetOptions()->SetValue(OPT_RENAMED_SYM, v = GetCtrlName(IDC_SYM)); App->GetOptions()->SetValue(OPT_FIX_RENAMED, v = GetCtrlValue(IDC_FIX_RENAMED)); App->GetOptions()->SerializeFile(true); break; } case IDC_CLOSE: { EndModeless(); break; } } return 0; } }; void AppWnd::SeekHistory(int Offset) { d->SeekHistory(Offset); } RenameDlg *RenameDlg::Inst = NULL; bool AppWnd::ShowInProject(const char *Fn) { if (!Fn) return false; for (auto p: d->Projects) { ProjectNode *Node = NULL; if (p->FindFullPath(Fn, &Node)) { for (LTreeItem *i = Node->GetParent(); i; i = i->GetParent()) { i->Expanded(true); } Node->Select(true); Node->ScrollTo(); return true; } } return false; } int AppWnd::OnCommand(int Cmd, int Event, OsView Wnd) { switch (Cmd) { case IDM_EXIT: { LCloseApp(); break; } case IDM_OPTIONS: { auto dlg = new Options(this); dlg->DoModal(NULL); break; } case IDM_HELP: { LExecute(APP_URL); break; } case IDM_ABOUT: { LAbout a(this, AppName, APP_VER, "\nLGI Integrated Development Environment", "icon128.png", APP_URL, "fret@memecode.com"); break; } case IDM_NEW: { IdeDoc *Doc; d->Docs.Insert(Doc = new IdeDoc(this, 0, 0)); if (Doc && d->Mdi) { LRect p = d->Mdi->NewPos(); Doc->LView::SetPos(p); Doc->Attach(d->Mdi); Doc->Focus(true); } break; } case IDM_OPEN: { LFileSelect *s = new LFileSelect; s->Parent(this); // printf("File open dlg from thread=%u\n", GetCurrentThreadId()); s->Open([&](auto s, auto ok) { // printf("open handler start... ok=%i thread=%u\n", ok, GetCurrentThreadId()); if (ok) OpenFile(s->Name()); // printf("open handler deleting...\n"); delete s; // printf("open handler deleted...\n"); }); break; } case IDM_SAVE_ALL: { SaveAll(NULL); break; } case IDM_SAVE: { IdeDoc *Top = TopDoc(); if (Top) Top->SetClean(NULL); break; } case IDM_SAVEAS: { IdeDoc *Top = TopDoc(); if (Top) { LFileSelect *s = new LFileSelect; s->Parent(this); s->Save([&](auto s, auto ok) { Top->SetFileName(s->Name(), true); d->OnFile(s->Name()); delete s; }); } break; } case IDM_CLOSE: { IdeDoc *Top = TopDoc(); if (Top) { if (Top->OnRequestClose(false)) { Top->Quit(); } } DeleteObj(d->DbgContext); break; } case IDM_CLOSE_ALL: { CloseAll(); Name(AppName); break; } // // Editor // case IDM_UNDO: { LTextView3 *Doc = FocusEdit(); if (Doc) { Doc->Undo(); } else LgiTrace("%s:%i - No focus doc.\n", _FL); break; } case IDM_REDO: { LTextView3 *Doc = FocusEdit(); if (Doc) { Doc->Redo(); } else LgiTrace("%s:%i - No focus doc.\n", _FL); break; } case IDM_FIND: { LTextView3 *Doc = FocusEdit(); if (Doc) { Doc->DoFind(NULL); } else LgiTrace("%s:%i - No focus doc.\n", _FL); break; } case IDM_FIND_NEXT: { LTextView3 *Doc = FocusEdit(); if (Doc) { Doc->DoFindNext(NULL); } else LgiTrace("%s:%i - No focus doc.\n", _FL); break; } case IDM_REPLACE: { LTextView3 *Doc = FocusEdit(); if (Doc) { Doc->DoReplace(NULL); } else LgiTrace("%s:%i - No focus doc.\n", _FL); break; } case IDM_GOTO: { LTextView3 *Doc = FocusEdit(); if (Doc) Doc->DoGoto(NULL); else { LInput *Inp = new LInput(this, NULL, LLoadString(L_TEXTCTRL_GOTO_LINE, "Goto [file:]line:"), "Goto"); Inp->DoModal([&](auto dlg, auto code) { LString s = Inp->GetStr(); LString::Array p = s.SplitDelimit(":,"); if (p.Length() == 2) { LString file = p[0]; int line = (int)p[1].Int(); GotoReference(file, line, false, true); } else LgiMsg(this, "Error: Needs a file name as well.", AppName); delete Inp; }); } break; } case IDM_CUT: { LTextView3 *Doc = FocusEdit(); if (Doc) Doc->PostEvent(M_CUT); break; } case IDM_COPY: { LTextView3 *Doc = FocusEdit(); if (Doc) Doc->PostEvent(M_COPY); break; } case IDM_PASTE: { LTextView3 *Doc = FocusEdit(); if (Doc) Doc->PostEvent(M_PASTE); break; } case IDM_FIND_IN_FILES: { if (!d->Finder) { d->Finder.Reset(new FindInFilesThread(d->AppHnd)); } if (d->Finder) { if (!d->FindParameters && d->FindParameters.Reset(new FindParams)) { LVariant var; if (GetOptions()->GetValue(OPT_ENTIRE_SOLUTION, var)) d->FindParameters->Type = var.CastInt32() ? FifSearchSolution : FifSearchDirectory; } FindInFiles Dlg(this, d->FindParameters); LViewI *Focus = GetFocus(); if (Focus) { LTextView3 *Edit = dynamic_cast(Focus); if (Edit && Edit->HasSelection()) { LAutoString a(Edit->GetSelection()); Dlg.Params->Text = a; } } IdeProject *p = RootProject(); if (p) { LAutoString Base = p->GetBasePath(); if (Base) Dlg.Params->Dir = Base; } Dlg.DoModal([&](auto dlg, auto code) { if (p && Dlg.Params->Type == FifSearchSolution) { Dlg.Params->ProjectFiles.Length(0); List Projects; Projects.Insert(p); p->GetChildProjects(Projects); LArray Nodes; for (auto p: Projects) p->GetAllNodes(Nodes); for (unsigned i=0; iGetFullPath(); if (s) Dlg.Params->ProjectFiles.Add(s); } } LVariant var = d->FindParameters->Type == FifSearchSolution; GetOptions()->SetValue(OPT_ENTIRE_SOLUTION, var); d->Finder->Stop(); d->Finder->PostEvent(FindInFilesThread::M_START_SEARCH, (LMessage::Param) new FindParams(d->FindParameters)); }); } break; } case IDM_FIND_SYMBOL: { IdeDoc *Doc = FocusDoc(); if (Doc) { Doc->GotoSearch(IDC_SYMBOL_SEARCH); } else { d->FindSym->OpenSearchDlg(this, [&](auto r) { if (r.File) GotoReference(r.File, r.Line, false); }); } break; } case IDM_GOTO_SYMBOL: { IdeDoc *Doc = FocusDoc(); if (Doc) { Doc->SearchSymbol(); } break; } case IDM_FIND_PROJECT_FILE: { IdeDoc *Doc = FocusDoc(); if (Doc) { Doc->SearchFile(); } else { auto d = new FindInProject(this); d->DoModal([](auto dlg, auto ctrlId){ delete dlg; }); } break; } case IDM_FIND_REFERENCES: { LViewI *f = LAppInst->GetFocus(); LDocView *doc = dynamic_cast(f); if (!doc) break; ssize_t c = doc->GetCaret(); if (c < 0) break; LString Txt = doc->Name(); char *s = Txt.Get() + c; char *e = s; while ( s > Txt.Get() && IsSymbolChar(s[-1])) s--; while (*e && IsSymbolChar(*e)) e++; if (e <= s) break; LString Word(s, e - s); if (!d->Finder) d->Finder.Reset(new FindInFilesThread(d->AppHnd)); if (!d->Finder) break; IdeProject *p = RootProject(); if (!p) break; List Projects; Projects.Insert(p); p->GetChildProjects(Projects); LArray Nodes; for (auto p: Projects) p->GetAllNodes(Nodes); LAutoPtr Params(new FindParams); Params->Type = FifSearchSolution; Params->MatchWord = true; Params->Text = Word; for (unsigned i = 0; i < Nodes.Length(); i++) { Params->ProjectFiles.New() = Nodes[i]->GetFullPath(); } d->Finder->Stop(); d->Finder->PostEvent(FindInFilesThread::M_START_SEARCH, (LMessage::Param) Params.Release()); break; } case IDM_PREV_LOCATION: { d->SeekHistory(-1); break; } case IDM_NEXT_LOCATION: { d->SeekHistory(1); break; } // // Project // case IDM_NEW_PROJECT: { CloseAll(); IdeProject *p; d->Projects.Insert(p = new IdeProject(this)); if (p) { p->CreateProject(); } break; } case IDM_NEW_PROJECT_TEMPLATE: { NewProjectFromTemplate(this); break; } case IDM_OPEN_PROJECT: { LFileSelect *s = new LFileSelect; s->Parent(this); s->Type("Projects", "*.xml"); s->Open([&](auto s, auto ok) { if (ok) { CloseAll(); OpenProject(s->Name(), NULL, Cmd == IDM_NEW_PROJECT); if (d->Tree) { d->Tree->Focus(true); } } delete s; }); break; } case IDM_IMPORT_DSP: { IdeProject *p = RootProject(); if (p) { LFileSelect *s = new LFileSelect; s->Parent(this); s->Type("Developer Studio Project", "*.dsp"); s->Open([&](auto s, auto ok) { if (ok) p->ImportDsp(s->Name()); delete s; }); } break; } case IDM_RUN: { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } IdeProject *p = RootProject(); if (p) p->Execute(); }); break; } case IDM_VALGRIND: { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } IdeProject *p = RootProject(); if (p) p->Execute(ExeValgrind); }); break; } case IDM_FIX_MISSING_FILES: { IdeProject *p = RootProject(); if (p) p->FixMissingFiles(); else LgiMsg(this, "No project loaded.", AppName); break; } case IDM_FIND_DUPE_SYM: { IdeProject *p = RootProject(); if (p) p->FindDuplicateSymbols(); else LgiMsg(this, "No project loaded.", AppName); break; } case IDM_RENAME_SYM: { if (!RenameDlg::Inst) new RenameDlg(this); break; } case IDM_START_DEBUG: { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } IdeProject *p = RootProject(); if (!p) { LgiMsg(this, "No project loaded.", "Error"); return; } LString ErrMsg; if (d->DbgContext) { d->DbgContext->OnCommand(IDM_CONTINUE); } else if ((d->DbgContext = p->Execute(ExeDebug, &ErrMsg))) { d->DbgContext->DebuggerLog = d->Output->DebuggerLog; d->DbgContext->Watch = d->Output->Watch; d->DbgContext->Locals = d->Output->Locals; d->DbgContext->CallStack = d->Output->CallStack; d->DbgContext->Threads = d->Output->Threads; d->DbgContext->ObjectDump = d->Output->ObjectDump; d->DbgContext->Registers = d->Output->Registers; d->DbgContext->MemoryDump = d->Output->MemoryDump; d->DbgContext->OnCommand(IDM_START_DEBUG); d->Output->Value(AppWnd::DebugTab); d->Output->DebugEdit->Focus(true); } else if (ErrMsg) { LgiMsg(this, "Error: %s", AppName, MB_OK, ErrMsg.Get()); } }); break; } case IDM_TOGGLE_BREAKPOINT: { IdeDoc *Cur = GetCurrentDoc(); if (Cur) ToggleBreakpoint(Cur->GetFileName(), Cur->GetLine()); break; } case IDM_ATTACH_TO_PROCESS: case IDM_PAUSE_DEBUG: case IDM_RESTART_DEBUGGING: case IDM_RUN_TO: case IDM_STEP_INTO: case IDM_STEP_OVER: case IDM_STEP_OUT: { if (d->DbgContext) d->DbgContext->OnCommand(Cmd); break; } case IDM_STOP_DEBUG: { if (d->DbgContext && d->DbgContext->OnCommand(Cmd)) { DeleteObj(d->DbgContext); } break; } case IDM_BUILD: { Build(); break; } case IDM_STOP_BUILD: { IdeProject *p = RootProject(); if (p) p->StopBuild(); break; } case IDM_CLEAN: { SaveAll([&](bool status) { if (!status) { LgiTrace("%s:%i - status=%i\n", _FL, status); return; } IdeProject *p = RootProject(); if (p) p->Clean(true, GetBuildMode()); }); break; } case IDM_NEXT_MSG: { d->SeekMsg(1); break; } case IDM_PREV_MSG: { d->SeekMsg(-1); break; } case IDM_DEBUG_MODE: { LMenuItem *Debug = GetMenu()->FindItem(IDM_DEBUG_MODE); LMenuItem *Release = GetMenu()->FindItem(IDM_RELEASE_MODE); if (Debug && Release) { Debug->Checked(true); Release->Checked(false); } break; } case IDM_RELEASE_MODE: { LMenuItem *Debug = GetMenu()->FindItem(IDM_DEBUG_MODE); LMenuItem *Release = GetMenu()->FindItem(IDM_RELEASE_MODE); if (Debug && Release) { Debug->Checked(false); Release->Checked(true); } break; } // // Other // case IDM_LOOKUP_SYMBOLS: { IdeDoc *Cur = GetCurrentDoc(); if (Cur) { // LookupSymbols(Cur->Read()); } break; } case IDM_DEPENDS: { IdeProject *p = RootProject(); if (p) { LString Exe = p->GetExecutable(GetCurrentPlatform()); if (LFileExists(Exe)) { Depends Dlg(this, Exe); } else { LgiMsg(this, "Couldn't find '%s'\n", AppName, MB_OK, Exe ? Exe.Get() : ""); } } break; } case IDM_SP_TO_TAB: { IdeDoc *Doc = FocusDoc(); if (Doc) Doc->ConvertWhiteSpace(true); break; } case IDM_TAB_TO_SP: { IdeDoc *Doc = FocusDoc(); if (Doc) Doc->ConvertWhiteSpace(false); break; } case IDM_ESCAPE: { IdeDoc *Doc = FocusDoc(); if (Doc) Doc->EscapeSelection(true); break; } case IDM_DESCAPE: { IdeDoc *Doc = FocusDoc(); if (Doc) Doc->EscapeSelection(false); break; } case IDM_SPLIT: { IdeDoc *Doc = FocusDoc(); if (!Doc) break; LInput *i = new LInput(this, "", "Separator:", AppName); i->DoModal([&](auto dlg, auto ok) { if (ok) Doc->SplitSelection(i->GetStr()); delete i; }); break; } case IDM_JOIN: { IdeDoc *Doc = FocusDoc(); if (!Doc) break; LInput *i = new LInput(this, "", "Separator:", AppName); i->DoModal([&](auto dlg, auto ok) { if (ok) Doc->JoinSelection(i->GetStr()); delete i; }); break; } case IDM_EOL_LF: { IdeDoc *Doc = FocusDoc(); if (!Doc) break; Doc->SetCrLf(false); break; } case IDM_EOL_CRLF: { IdeDoc *Doc = FocusDoc(); if (!Doc) break; Doc->SetCrLf(true); break; } case IDM_LOAD_MEMDUMP: { NewMemDumpViewer(this); break; } case IDM_SYS_CHAR_SUPPORT: { new SysCharSupport(this); break; } default: { int index = Cmd - IDM_RECENT_FILE; auto r = d->RecentFiles.IdxCheck(index)? d->RecentFiles[index] : LString(); if (r) { auto idx = Cmd - IDM_RECENT_FILE; if (idx > 0) { d->RecentFiles.DeleteAt(idx, true); d->RecentFiles.AddAt(0, r); } IdeDoc *f = d->IsFileOpen(r); if (f) f->Raise(); else OpenFile(r); } index = Cmd - IDM_RECENT_PROJECT; auto p = d->RecentProjects.IdxCheck(index) ? d->RecentProjects[index] : LString(); if (p) { auto idx = Cmd - IDM_RECENT_PROJECT; if (idx > 0) { d->RecentProjects.DeleteAt(idx, true); d->RecentProjects.AddAt(0, p); } CloseAll(); OpenProject(p, NULL, false); if (d->Tree) { d->Tree->Focus(true); } } IdeDoc *Doc = d->Docs[Cmd - IDM_WINDOWS]; if (Doc) { Doc->Raise(); } IdePlatform PlatIdx = (IdePlatform) (Cmd - IDM_MAKEFILE_BASE); const char *Platform = PlatIdx >= 0 && PlatIdx < PlatformMax ? PlatformNames[Cmd - IDM_MAKEFILE_BASE] : NULL; if (Platform) { IdeProject *p = RootProject(); if (p) { p->CreateMakefile(PlatIdx, false); } } break; } } return 0; } LTree *AppWnd::GetTree() { return d->Tree; } IdeDoc *AppWnd::TopDoc() { return d->Mdi ? dynamic_cast(d->Mdi->GetTop()) : NULL; } LTextView3 *AppWnd::FocusEdit() { return dynamic_cast(GetWindow()->GetFocus()); } IdeDoc *AppWnd::FocusDoc() { IdeDoc *Doc = TopDoc(); if (Doc) { if (Doc->HasFocus()) { return Doc; } else { LViewI *f = GetFocus(); LgiTrace("%s:%i - Edit doesn't have focus, f=%p %s doc.edit=%s\n", _FL, f, f ? f->GetClass() : 0, Doc->Name()); } } return 0; } void AppWnd::OnProjectDestroy(IdeProject *Proj) { if (d) { auto locked = Lock(_FL); // printf("OnProjectDestroy(%s) %i\n", Proj->GetFileName(), locked); d->Projects.Delete(Proj); if (locked) Unlock(); } else LAssert(!"No priv"); } void AppWnd::OnProjectChange() { LArray Views; if (d->Mdi->GetChildren(Views)) { for (unsigned i=0; i(Views[i]); if (Doc) Doc->OnProjectChange(); } } } void AppWnd::OnDocDestroy(IdeDoc *Doc) { if (d) { auto locked = Lock(_FL); d->Docs.Delete(Doc); d->UpdateMenus(); if (locked) Unlock(); } else { LAssert(!"OnDocDestroy no priv...\n"); } } BuildConfig AppWnd::GetBuildMode() { LMenuItem *Release = GetMenu()->FindItem(IDM_RELEASE_MODE); if (Release && Release->Checked()) { return BuildRelease; } return BuildDebug; } LList *AppWnd::GetFtpLog() { return d->Output->FtpLog; } LStream *AppWnd::GetBuildLog() { return d->Output->Txt[AppWnd::BuildTab]; } LStream *AppWnd::GetDebugLog() { return d->Output->Txt[AppWnd::DebugTab]; } void AppWnd::FindSymbol(int ResultsSinkHnd, const char *Sym, bool AllPlatforms) { d->FindSym->Search(ResultsSinkHnd, Sym, AllPlatforms); } bool AppWnd::GetSystemIncludePaths(::LArray &Paths) { if (d->SystemIncludePaths.Length() == 0) { #if !defined(WINNATIVE) // echo | gcc -v -x c++ -E - LSubProcess sp1("echo"); LSubProcess sp2("gcc", "-v -x c++ -E -"); sp1.Connect(&sp2); sp1.Start(true, false); char Buf[256]; ssize_t r; LStringPipe p; while ((r = sp1.Read(Buf, sizeof(Buf))) > 0) { p.Write(Buf, r); } bool InIncludeList = false; while (p.Pop(Buf, sizeof(Buf))) { if (stristr(Buf, "#include")) { InIncludeList = true; } else if (stristr(Buf, "End of search")) { InIncludeList = false; } else if (InIncludeList) { LAutoString a(TrimStr(Buf)); d->SystemIncludePaths.New() = a; } } #else char p[MAX_PATH_LEN]; LGetSystemPath(LSP_USER_DOCUMENTS, p, sizeof(p)); LMakePath(p, sizeof(p), p, "Visual Studio 2008\\Settings\\CurrentSettings.xml"); if (LFileExists(p)) { LFile f; if (f.Open(p, O_READ)) { LXmlTree t; LXmlTag r; if (t.Read(&r, &f)) { LXmlTag *Opts = r.GetChildTag("ToolsOptions"); if (Opts) { LXmlTag *Projects = NULL; char *Name; for (auto c: Opts->Children) { if (c->IsTag("ToolsOptionsCategory") && (Name = c->GetAttr("Name")) && !stricmp(Name, "Projects")) { Projects = c; break; } } LXmlTag *VCDirectories = NULL; if (Projects) for (auto c: Projects->Children) { if (c->IsTag("ToolsOptionsSubCategory") && (Name = c->GetAttr("Name")) && !stricmp(Name, "VCDirectories")) { VCDirectories = c; break; } } if (VCDirectories) for (auto prop: VCDirectories->Children) { if (prop->IsTag("PropertyValue") && (Name = prop->GetAttr("Name")) && !stricmp(Name, "IncludeDirectories")) { char *Bar = strchr(prop->GetContent(), '|'); LToken t(Bar ? Bar + 1 : prop->GetContent(), ";"); for (int i=0; iSystemIncludePaths.New().Reset(NewStr(s)); } } } } } } } #endif } for (int i=0; iSystemIncludePaths.Length(); i++) { Paths.Add(NewStr(d->SystemIncludePaths[i])); } return true; } /* class SocketTest : public LWindow, public LThread { LTextLog *Log; public: SocketTest() : LThread("SocketTest") { Log = new LTextLog(100); SetPos(LRect(200, 200, 900, 800)); Attach(0); Visible(true); Log->Attach(this); Run(); } int Main() { LSocket s; s.SetTimeout(15000); Log->Print("Starting...\n"); auto r = s.Open("192.168.1.30", 7000); Log->Print("Open =%i\n", r); return 0; } }; */ class TestView : public LView { public: TestView() { LRect r(10, 10, 110, 210); SetPos(r); Sunken(true); printf("_BorderSize=%i\n", _BorderSize); } void OnPaint(LSurface *pdc) { auto c = GetClient(); pdc->Colour(LColour::Red); pdc->Line(c.x1, c.y1, c.x2, c.y2); pdc->Ellipse(c.x1+(c.X()/2)+10, c.y1+(c.Y()/2), c.X()/2, c.Y()/2); } }; #include "lgi/common/Tree.h" #include "lgi/common/List.h" class Test : public LWindow { public: Test() { LRect r(100, 100, 800, 700); SetPos(r); Name("Test"); SetQuitOnClose(true); if (Attach(0)) { // AddView(new TestView); // auto t = new LTree(10, 10, 10, 100, 200); auto t = new LTextLabel(10, 10, 10, 100, 35, "Text"); AddView(t); AttachChildren(); Visible(true); } } }; int LgiMain(OsAppArguments &AppArgs) { printf("LgiIde v%s\n", APP_VER); LApp a(AppArgs, "LgiIde"); if (a.IsOk()) { + LArray Apps; + LGetAppsForMimeType("text/html", Apps); + + auto app = LGetAppForMimeType("text/html"); + a.AppWnd = new AppWnd; // a.AppWnd->_Dump(); a.Run(); } return 0; } diff --git a/include/lgi/common/App.h b/include/lgi/common/App.h --- a/include/lgi/common/App.h +++ b/include/lgi/common/App.h @@ -1,435 +1,435 @@ #ifndef _GAPP_H_ #define _GAPP_H_ ///////////////////////////////////////////////////////////////////////////////// #if WINNATIVE typedef DWORD OsProcessId; #else typedef int OsProcessId; #endif /// Returns the current process ID #define LProcessId() (LAppInst->GetProcessId()) /// Returns a pointer to the LApp object. /// /// \warning Don't use this before you have created your LApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define LAppInst (LApp::ObjInstance()) /// Process any pending messages in the applications message que and then return. #define LYield() LAppInst->Yield() /// Returns a system font pointer. /// /// \warning Don't use this before you have created your LApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define LSysFont (LAppInst->SystemNormal) /// Returns a bold system font pointer. /// /// \warning Don't use this before you have created your LApp object. i.e. in a constructor /// of a global static class which is initialized before the main begins executing. #define LSysBold (LAppInst->SystemBold) /// Exits the application right now! /// /// \warning This will cause data loss if you have any unsaved data. Equivilant to exit(0). LgiFunc void LExitApp(); /// Closes the application gracefully. /// /// This actually causes LApp::Run() to stop processing message and return. #define LCloseApp() LAppInst->Exit(false) #if defined(LINUX) && !defined(LGI_SDL) #define ThreadCheck() LAssert(InThread()) #else #define ThreadCheck() #endif /// Optional arguments to the LApp object struct LAppArguments { /// Don't initialize the skinning engine. bool NoSkin = false; /// Don't do crash handling bool NoCrashHandler = false; LAppArguments(const char *init = NULL) { auto a = LString(init).SplitDelimit(","); for (auto o: a) if (o.Equals("NoCrashHandler")) NoCrashHandler = true; else if (o.Equals("NoSkin")) NoSkin = true; } }; class LAppPrivate; #if LGI_COCOA && defined __OBJC__ #include "LCocoaView.h" @interface LNsApplication : NSApplication { } @property LAppPrivate *d; - (id)init; - (void)setPriv:(nonnull LAppPrivate*)priv; - (void)terminate:(nullable id)sender; - (void)dealloc; - (void)assert:(nonnull LCocoaAssert*)ca; - (void)onUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply; @end ObjCWrapper(LNsApplication, OsApp) #endif /// \brief Singleton class for handling application wide settings and methods /// /// This should be the first class you create, passing in the arguments from the /// operating system. And once your initialization is complete the 'Run' method /// is called to enter the main application loop that processes messages for the /// life time of the application. class LgiClass LApp : virtual public LAppI, public LBase, public OsApplication { friend class LView; friend class LWindow; public: typedef LHashTbl, LWindowsClass*> ClassContainer; protected: // private member vars class LAppPrivate *d = NULL; #if defined LGI_SDL void OnSDLEvent(LMessage *m); #elif defined LGI_COCOA LAutoPtr Default; #elif defined WIN32 CRITICAL_SECTION StackTraceSync; friend LONG __stdcall _ExceptionFilter_Redir(LPEXCEPTION_POINTERS e); LONG __stdcall _ExceptionFilter(LPEXCEPTION_POINTERS e, char *ProductId); friend class LWindowsClass; ClassContainer *GetClasses(); #elif defined LINUX friend class LClipBoard; // virtual void OnEvents(); void DeleteMeLater(LViewI *v); void SetClipBoardContent(OsView Hnd, LVariant &v); bool GetClipBoardContent(OsView Hnd, LVariant &v, LArray &Types); #endif friend class LMouseHook; static LMouseHook *MouseHook; public: // Static publics #ifdef LINUX constexpr static const char *CfgLinuxKeysShift = "Linux.Keys.Shift"; constexpr static const char *CfgLinuxKeysCtrl = "Linux.Keys.Ctrl"; constexpr static const char *CfgLinuxKeysAlt = "Linux.Keys.Alt"; constexpr static const char *CfgLinuxKeysSystem = "Linux.Keys.System"; constexpr static const char *CfgLinuxMouseLeft = "Linux.Mouse.Left"; constexpr static const char *CfgLinuxMouseMiddle = "Linux.Mouse.Middle"; constexpr static const char *CfgLinuxMouseRight = "Linux.Mouse.Right"; constexpr static const char *CfgLinuxMouseBack = "Linux.Mouse.Back"; constexpr static const char *CfgLinuxMouseForward = "Linux.Mouse.Forward"; #endif constexpr static const char *CfgFontsGlyphSub = "Fonts.GlyphSub"; constexpr static const char *CfgFontsPointSizeOffset = "Fonts.PointSizeOffset"; constexpr static const char *CfgFontsSystemFont = "Fonts.SystemFont"; constexpr static const char *CfgFontsBoldFont = "Fonts.BoldFont"; constexpr static const char *CfgFontsMonoFont = "Fonts.MonoFont"; constexpr static const char *CfgFontsSmallFont = "Fonts.SmallFont"; constexpr static const char *CfgFontsCaptionFont = "Fonts.CaptionFont"; constexpr static const char *CfgFontsMenuFont = "Fonts.MenuFont"; /// Use 'LAppInst' to return a pointer to the LApp object static LApp *ObjInstance(); static class LSkinEngine *SkinEngine; // public member vars /// The system font LFont *SystemNormal = NULL; /// The system font in bold LFont *SystemBold = NULL; /// Pointer to the applications main window LWindow *AppWnd = NULL; /// Returns true if the LApp object initialized correctly bool IsOk(); /// Returns this processes ID OsProcessId GetProcessId(); /// Returns the thread currently running the active message loop OsThread _GetGuiThread(); OsThreadId GetGuiThreadId(); bool InThread(); /// Returns the number of CPU cores the machine has int GetCpuCount(); /// Construct the object LApp ( /// The arguments passed in by the OS. OsAppArguments &AppArgs, /// The application's name. const char *AppName, /// Optional args LAppArguments *ObjArgs = 0 ); /// Destroys the object virtual ~LApp(); /// Resets the arguments void SetAppArgs(OsAppArguments &AppArgs); /// Returns the arguemnts OsAppArguments *GetAppArgs(); /// Returns the n'th argument as a heap string. Free with DeleteArray(...). const char *GetArgumentAt(int n); /// Enters the message loop. bool Run ( /// Idle callback OnIdleProc IdleCallback = NULL, /// Param for IdleCallback void *IdleParam = NULL ); /// Processes any messages in the queue and then returns. [[deprecated]] bool Yield(); /// Event called to process the command line void OnCommandLine(); /// Event called to process files dropped on the application void OnReceiveFiles(LArray &Files); /// Event called to process URLs given to the application void OnUrl(const char *Url); /// Exits the event loop with the code specified void Exit ( /// The application exit code. int Code = 0 ); /// \brief Parses the command line for a switch /// \return true if the option exists. bool GetOption ( /// The option to look for. const char *Option, /// String to receive the value (if any) of the option LString &Value ); /// \brief Parses the command line for a switch /// \return true if the option exists. bool GetOption ( /// The option to look for. const char *Option, /// The buffer to receive the value of the command line parameter or NULL if you don't care. char *Dst = 0, /// The buffer size in bytes int DstSize = 0 ); /// Return the path to the Lgi config file... (not the same as the application options, more global Lgi apps settings) ::LString GetConfigPath(); /// Gets the application conf stored in lgi.conf ::LString GetConfig(const char *Variable); /// Sets a single tag in the config. (Not written to disk) void SetConfig(const char *Variable, const char *Value); /// Gets the control with the keyboard focus LViewI *GetFocus(); /// Gets the MIME type of a file /// \returns the mime type or NULL if unknown. LString GetFileMimeType ( /// The file to identify const char *File ); /// Gets the applications that can handle a file of a certain mime type - bool GetAppsForMimeType(char *Mime, LArray &Apps); + bool GetAppsForMimeType(char *Mime, LArray &Apps); /// Get a system metric int32 GetMetric ( /// One of #LGI_MET_DECOR_X, #LGI_MET_DECOR_Y LSystemMetric Metric ); /// Get the mouse hook instance LMouseHook *GetMouseHook(); /// Gets the singleton symbol lookup class class LSymLookup *GetSymLookup(); /// \returns true if the process is running with elevated permissions bool IsElevated(); /// Gets the font cache class LFontCache *GetFontCache(); // OS Specific #if defined(LGI_SDL) /// This keeps track of the dirty rectangle and issues a M_INVALIDATE /// event when needed to keep the screen up to date. bool InvalidateRect(LRect &r); /// Push a new window to the top of the window stack bool PushWindow(LWindow *w); /// Remove the top most window LWindow *PopWindow(); /// Sets up mouse tracking beyond the current window... void CaptureMouse(bool capture); /// Returns the freetype version as a string. LString GetFreetypeVersion(); #elif defined(WIN32) HINSTANCE GetInstance(); int GetShow(); /// \returns true if the application is running under Wine on Linux. This is useful to know /// if you need to work around missing functionality in the Wine implementation. bool IsWine(); #elif defined(LINUX) class LLibrary *GetWindowManagerLib(); class DesktopInfo { friend class LApp; LString File; bool Dirty; struct KeyPair { LString Key, Value; }; struct Section { LString Name; LArray Values; KeyPair *Get(const char *Name, bool Create, bool &Dirty) { for (unsigned i=0; iKey.Equals(Name)) return kp; } if (Create) { KeyPair *kp = &Values.New(); kp->Key = Name; Dirty = true; return kp; } return NULL; } }; LArray
Data; bool Serialize(bool Write); Section *GetSection(const char *Name, bool Create); public: DesktopInfo(const char *file); LString Get(const char *Field, const char *Section = NULL); bool Set(const char *Field, const char *Value, const char *Section = NULL); bool Update() { return Dirty ? Serialize(true) : true; } const char *GetFile() { return File; } }; DesktopInfo *GetDesktopInfo(); bool SetApplicationIcon(const char *FileName); #elif LGI_COCOA && defined(__OBJC__) OsApp &Handle(); #endif #ifdef __GTK_H__ struct KeyModFlags { int Shift, Alt, Ctrl, System; bool Debug; KeyModFlags() { Shift = 0; Alt = 0; Ctrl = 0; System = 0; Debug = false; } const char *FlagName(int Flag); // Single flag to string int FlagValue(const char *Name); // Single name to bitmask ::LString FlagsToString(int Flags); // Turn multiple flags to string }; KeyModFlags *GetKeyModFlags(); void OnDetach(LViewI *View); #endif #if !LGI_VIEW_HANDLE bool PostEvent(LViewI *View, int Msg, LMessage::Param a = 0, LMessage::Param b = 0); #endif }; #endif diff --git a/include/lgi/common/LgiCommon.h b/include/lgi/common/LgiCommon.h --- a/include/lgi/common/LgiCommon.h +++ b/include/lgi/common/LgiCommon.h @@ -1,420 +1,420 @@ /** \file \author Matthew Allen \date 27/3/2000 \brief Common LGI include Copyright (C) 2000-2004, Matthew Allen */ /** * \defgroup Base Foundation tools * \ingroup Lgi */ /** * \defgroup Text Text handling * \ingroup Lgi */ #ifndef _LGI_COMMON_H #define _LGI_COMMON_H #if defined LINUX #include #endif #include "lgi/common/Mem.h" #include "lgi/common/Array.h" #include "lgi/common/LgiString.h" #include "lgi/common/StringClass.h" #include "lgi/common/CurrentTime.h" #include "lgi/common/LgiUiBase.h" /// Returns the system path specified /// \ingroup Base LgiExtern LString LGetSystemPath( /// Which path to retreive LSystemPath Which, int WordSize = 0 ); /// Gets the path of the currently running executable /// \ingroup Base LgiExtern LString LGetExePath(); /// Gets the file of the currently running executable /// \ingroup Base LgiExtern LString LGetExeFile(); /// Returns the mime type of the file /// \ingroup Mime LgiExtern LString LGetFileMimeType ( /// File to find mime type for const char *File ); /// Finds a file in the applications directory or nearby /// \ingroup Base LgiExtern LString LFindFile(const char *Name); /// Returns the application associated with the mime type /// \ingroup Mime LgiExtern LString LGetAppForMimeType ( /// Type of the file to find and app for const char *Mime ); /// \return a formatted file size LgiExtern LString LFormatSize(int64_t Size); /// URL encode a string LgiExtern LString LUrlEncode(const char *s, const char *delim); /// URL decode a string LgiExtern LString LUrlDecode(const char *s); /// Gets the current user LgiExtern LString LCurrentUserName(); /// Returns an environment variable. LgiExtern LString LGetEnv(const char *Var); /// Gets the system path.. LgiExtern LString::Array LGetPath(); /// Check for a valid email string LgiExtern bool LIsValidEmail(LString Email); /// Finds an application to handle a protocol request (e.g. 'mailto', 'http' etc) LgiExtern LString LGetAppForProtocol(const char *Protocol); /// Converts a string to the native 8bit charset of the OS from utf-8 /// \ingroup Text LgiExtern LString LToNativeCp(const char *In, ssize_t InLen = -1); /// Converts a string from the native 8bit charset of the OS to utf-8 /// \ingroup Text LgiExtern LString LFromNativeCp(const char *In, ssize_t InLen = -1); LgiExtern LString LStrConvertCp ( /// Output charset const char *OutCp, /// Input buffer const void *In, /// The input data's charset const char *InCp, /// Bytes of valid data in the input ssize_t InLen = -1 ); /// Converts an OS error code into a text string LgiExtern LString LErrorCodeToString(uint32_t ErrorCode); #ifdef __cplusplus extern "C" { #endif ///////////////////////////////////////////////////////////// // Externs // Codepages /// Converts a buffer of text to a different charset /// \ingroup Text /// \returns the bytes written to the location pointed to by 'Out' LgiFunc ssize_t LBufConvertCp(void *Out, const char *OutCp, ssize_t OutLen, const void *&In, const char *InCp, ssize_t &InLen); /// \brief Converts a string to a new charset /// \return A dynamically allocate, null terminated string in the new charset /// \ingroup Text LgiFunc void *LNewConvertCp ( /// Output charset const char *OutCp, /// Input buffer const void *In, /// The input data's charset const char *InCp, /// Bytes of valid data in the input ssize_t InLen = -1 ); /// Return true if Lgi support the charset /// \ingroup Text LgiFunc bool LIsCpImplemented(const char *Cp); /// Converts the ANSI code page to a charset name /// \ingroup Text LgiFunc const char *LAnsiToLgiCp(int AnsiCodePage = -1); /// Calculate the number of characters in a string /// \ingroup Text LgiFunc int LCharLen(const void *Str, const char *Cp, int Bytes = -1); /// Move a pointer along a utf-8 string by characters /// \ingroup Text LgiFunc char *LSeekUtf8 ( /// Pointer to the current character const char *Ptr, /// The number of characters to move forward or back ssize_t D, /// The start of the memory buffer if you known char *Start = 0 ); /// Return true if the string is valid utf-8 /// \ingroup Text LgiFunc bool LIsUtf8(const char *s, ssize_t len = -1); /// Returns the next token in a string, leaving the argument pointing to the end of the token /// \ingroup Text LgiFunc char *LTokStr(const char *&s); /// Formats a data size into appropriate units /// \ingroup Base LgiFunc void LFormatSize ( /// Output string char *Str, /// Output string buffer length int SLen, /// Input size in bytes int64_t Size ); /// \returns true if the path is a volume root. LgiFunc bool LIsVolumeRoot(const char *Path); /// Converts a string from URI encoding (ala %20 -> ' ') /// \returns a dynamically allocated string or NULL on error /// \ingroup Text LgiFunc char *LDecodeUri ( /// The URI const char *uri, /// The length or -1 if NULL terminated int len = -1 ); /// Converts a string to URI encoding (ala %20 -> ' ') /// \returns a dynamically allocated string or NULL on error /// \ingroup Text LgiFunc char *LEncodeUri ( /// The URI const char *uri, /// The length or -1 if NULL terminated int len = -1 ); // Path #if LGI_COCOA || defined(__GTK_H__) || defined(HAIKU) LgiExtern LString LgiArgsAppPath; #endif /// Returns the system path specified /// \ingroup Base LgiFunc bool LGetSystemPath ( /// Which path to retreive LSystemPath Which, /// The buffer to receive the path into char *Dst, /// The size of the receive buffer in bytes ssize_t DstSize ); /// \brief Recursively search for files /// \return Non zero if something was found /// \ingroup Base LgiFunc bool LRecursiveFileSearch ( /// Start search in this dir const char *Root, /// Extensions to match LArray *Ext = NULL, /// [optional] Output filenames LArray *Files = NULL, /// [optional] Output total size uint64 *Size = NULL, /// [optional] File count uint64 *Count = NULL, /// [optional] Callback for match std::function Callback = NULL, /// [optional] Cancel object LCancel *Cancel = NULL ); // Resources /// Gets the currently selected language /// \ingroup Resources LgiFunc struct LLanguage *LGetLanguageId(); // Os version functions /// Gets the current operating system and optionally it's version. /// \returns One of the defines starting with #LGI_OS_UNKNOWN in LgiDefs.h /// \ingroup Base LgiFunc int LGetOs(LArray *Ver = 0); /// Gets the current operation systems name. /// \ingroup Base LgiFunc const char *LGetOsName(); // System /// \brief Opens a file or directory. /// /// If the input is an executable then it is run. If the input file /// is a document then an appropriate application is found to open the /// file and the file is passed to that application. If the input is /// a directory then the OS's file manager is openned to browse the /// directory. /// /// \ingroup Base LgiFunc bool LExecute ( /// The file to open const char *File, /// The arguments to pass to the program const char *Arguments="", /// The directory to run in const char *Dir = 0, /// An error message LString *ErrorMsg = NULL ); /// Initializes the random number generator /// \ingroup Base LgiFunc void LRandomize(uint Seed); /// Returns a random number between 0 and Max-1 /// \ingroup Base LgiFunc uint LRand(uint Max = 0); LgiFunc bool _lgi_read_colour_config(const char *Tag, uint32_t *c); #ifndef SND_ASYNC #define SND_ASYNC 0x0001 #endif /// Plays a sound /// \ingroup Base LgiFunc bool LPlaySound ( /// File name of the sound to play const char *FileName, /// 0 or SND_ASYNC. If 0 the function blocks till the sound finishes. int Flags ); /** * \defgroup Mime Mime handling support. * \ingroup Lgi */ /// Returns the file extensions associated with the mimetype /// \ingroup Mime LgiExtern bool LGetMimeTypeExtensions ( /// The returned mime type const char *Mime, /// The extensions LArray &Ext ); inline bool LGetAppForMimeType(const char *Mime, char *AppPath, int BufSize) { LString p = LGetAppForMimeType(Mime); if (AppPath && p) strcpy_s(AppPath, BufSize, p); return p.Length() > 0; } /// Returns the all applications that can open a given mime type. /// \ingroup Mime LgiFunc bool LGetAppsForMimeType ( /// The type of files to match apps to. /// /// Two special cases exist: /// - application/email gets the default email client /// - application/browser get the default web browser const char *Mime, /// The applications that can handle the - LArray &Apps, + LArray &Apps, /// Limit the length of the results, i.e. stop looking after 'Limit' matches. /// -1 means return all matches. int Limit = -1 ); // Debug /// Returns true if the build is for release. /// \ingroup Base LgiFunc int LIsReleaseBuild(); #if defined WIN32 /// Registers an active x control LgiFunc bool RegisterActiveXControl(const char *Dll); enum HWBRK_TYPE { HWBRK_TYPE_CODE, HWBRK_TYPE_READWRITE, HWBRK_TYPE_WRITE, }; enum HWBRK_SIZE { HWBRK_SIZE_1, HWBRK_SIZE_2, HWBRK_SIZE_4, HWBRK_SIZE_8, }; /// Set a hardware breakpoint. LgiFunc HANDLE SetHardwareBreakpoint ( /// Use GetCurrentThread() HANDLE hThread, /// Type of breakpoint HWBRK_TYPE Type, /// Size of breakpoint HWBRK_SIZE Size, /// The pointer to the data to break on void *s ); /// Deletes a hardware breakpoint LgiFunc bool RemoveHardwareBreakpoint(HANDLE hBrk); #elif defined LINUX /// Window managers enum WindowManager { WM_Unknown, WM_Kde, WM_Gnome }; /// Returns the currently running window manager WindowManager LGetWindowManager(); #elif defined(__OBJC__) LgiFunc NSCursor *LCocoaCursor(LCursor lc); #endif #ifdef __cplusplus } #endif #endif diff --git a/include/lgi/common/LgiUiBase.h b/include/lgi/common/LgiUiBase.h --- a/include/lgi/common/LgiUiBase.h +++ b/include/lgi/common/LgiUiBase.h @@ -1,419 +1,421 @@ #ifndef _LGI_CLASS_H_ #define _LGI_CLASS_H_ #include #include "lgi/common/LgiInc.h" #include "lgi/common/LgiDefs.h" #include "lgi/common/AutoPtr.h" #include "lgi/common/StringClass.h" #include "lgi/common/Point.h" #if defined(__OBJC__) && !defined(__GTK_H__) #include #endif #include // Virtual input classes class LKey; class LMouse; // General GUI classes class LApp; class LWindow; class LWindowsClass; class LView; class LLayout; class LFileSelect; class LSubMenu; class LMenuItem; class LMenu; class LToolBar; class LToolButton; class LSplitter; class LStatusPane; class LStatusBar; class LScrollBar; class LImageList; class LDialog; // Tracing: /// Sets the output stream for the LgiTrace statement. By default the stream output /// is to .txt in the executables folder or $LSP_APP_ROOT\.txt if /// that is not writable. If the stream is set to something then normal file output is /// directed to the specified stream instead. LgiFunc void LgiTraceSetStream(class LStreamI *stream); /// Gets the log file path LgiFunc bool LgiTraceGetFilePath(char *LogPath, int BufLen); /// Writes a debug statement to a output stream, or if not defined with LgiTraceSetStream /// then to a log file (see LgiTraceSetStream for details) /// /// Default path is ./.txt relative to the executable. /// Fall back path is LgiGetSystemPath(LSP_APP_ROOT). LgiFunc void LgiTrace(const char *Format, ...); #ifndef LGI_STATIC /// Same as LgiTrace but writes a stack trace as well. LgiFunc void LStackTrace(const char *Format, ...); #endif // Template hash function: template RESULT LHash(const CHAR *v, ssize_t l, bool Case) { RESULT h = 0; if (Case) { // case sensitive if (l > 0) { while (l--) h = (h << 5) - h + *v++; } else { for (; *v; v ++) h = (h << 5) - h + *v; } } else { // case insensitive CHAR c; if (l > 0) { while (l--) { c = tolower(*v); v++; h = (h << 5) - h + c; } } else { for (; *v; v++) { c = tolower(*v); h = (h << 5) - h + c; } } } return h; } // Template sort function: template void LSort(T *v, ssize_t left, ssize_t right, std::function comp) { ssize_t i, last; if (left >= right) return; LSwap(v[left], v[(left + right)>>1]); last = left; for (i = left+1; i <= right; i++) if (comp(v[i], v[left]) < 0) /* Here's the function call */ LSwap(v[++last], v[i]); LSwap(v[left], v[last]); LSort(v, left, last-1, comp); LSort(v, last+1, right, comp); } #define AssignFlag(f, bit, to) if (to) f |= bit; else f &= ~(bit) // OsEvent is defined here because the LUiEvent is the primary user. // And this header can be included independently of LgiOsDefs.h where // this would otherwise live. #if defined __GTK_H__ typedef Gtk::GdkEvent *OsEvent; #elif defined _SDL_H typedef SDL_Event *OsEvent; #elif defined _WIN32 // No native type here, but LMessage can encapsulate the message code, WPARAM and LPARAM. typedef class LMessage *OsEvent; #elif LGI_COCOA #include "ObjCWrapper.h" ObjCWrapper(NSEvent, OsEvent); #elif LGI_CARBON typedef EventRef OsEvent; #elif LGI_HAIKU typedef BMessage *OsEvent; #else #error "Impl me." #endif /// General user interface event class LgiClass LUiEvent { public: int Flags; OsEvent Event; LUiEvent() { Flags = 0; } virtual ~LUiEvent() {} virtual void Trace(const char *Msg) const {} /// The key or mouse button was being pressed. false on the up-click. bool Down() const { return TestFlag(Flags, LGI_EF_DOWN); } /// The mouse button was double clicked. bool Double() const { return TestFlag(Flags, LGI_EF_DOUBLE); } /// A ctrl button was held down during the event bool Ctrl() const { return TestFlag(Flags, LGI_EF_CTRL); } /// A alt button was held down during the event bool Alt() const { return TestFlag(Flags, LGI_EF_ALT); } /// A shift button was held down during the event bool Shift() const { return TestFlag(Flags, LGI_EF_SHIFT); } /// The system key was held down (windows key / apple key etc) bool System() const { return TestFlag(Flags, LGI_EF_SYSTEM); } // Set void Down(bool i) { AssignFlag(Flags, LGI_EF_DOWN, i); } void Double(bool i) { AssignFlag(Flags, LGI_EF_DOUBLE, i); } void Ctrl(bool i) { AssignFlag(Flags, LGI_EF_CTRL, i); } void Alt(bool i) { AssignFlag(Flags, LGI_EF_ALT, i); } void Shift(bool i) { AssignFlag(Flags, LGI_EF_SHIFT, i); } void System(bool i) { AssignFlag(Flags, LGI_EF_SYSTEM, i); } #if defined(MAC) bool CtrlCmd() const { return System(); } static const char *CtrlCmdName() { return "\xE2\x8C\x98"; } bool AltCmd() const { return System(); } static const char *AltCmdName() { return "\xE2\x8C\x98"; } #elif defined(HAIKU) bool CtrlCmd() const { return System(); } static const char *CtrlCmdName() { return "System"; } bool AltCmd() const { return Ctrl(); } static const char *AltCmdName() { return "Ctrl"; } #else // win32 and linux bool CtrlCmd() const { return Ctrl(); } static const char *CtrlCmdName() { return "Ctrl"; } bool AltCmd() const { return Alt(); } static const char *AltCmdName() { return "Alt"; } #endif #if LGI_COCOA void SetModifer(uint32_t modifierKeys); #else void SetModifer(uint32_t modifierKeys) { #if defined(__GTK_H__) System((modifierKeys & Gtk::GDK_MOD4_MASK) != 0); Shift((modifierKeys & Gtk::GDK_SHIFT_MASK) != 0); Alt((modifierKeys & Gtk::GDK_MOD1_MASK) != 0); Ctrl((modifierKeys & Gtk::GDK_CONTROL_MASK) != 0); #elif LGI_CARBON System(modifierKeys & cmdKey); Shift(modifierKeys & shiftKey); Alt(modifierKeys & optionKey); Ctrl(modifierKeys & controlKey); #endif } #endif }; #ifdef WINDOWS struct LKeyWinBits { unsigned Repeat : 16; unsigned Scan : 8; unsigned Extended : 1; unsigned Reserved : 4; unsigned Context : 1; unsigned Previous : 1; unsigned Pressed : 1; }; #endif /// All the information related to a keyboard event class LgiClass LKey : public LUiEvent { public: /// The virtual code for key /// Rule: Only compare with LK_??? symbols char16 vkey = 0; /// The unicode character for the key /// Rule: Never compare with LK_??? symbols char16 c16 = 0; /// OS Specific #ifdef WINDOWS union { #endif uint32_t Data = 0; #ifdef WINDOWS LKeyWinBits WinBits; }; #endif /// True if this is a standard character (ie not a control key) bool IsChar = false; LKey() {} LKey(int vkey, uint32_t flags); void Trace(const char *Msg) const { LgiTrace("%s LKey vkey=%i(0x%x) c16=%i(%c) IsChar=%i down=%i ctrl=%i alt=%i sh=%i sys=%i\n", Msg ? Msg : (char*)"", vkey, vkey, c16, c16 >= ' ' && c16 < 127 ? c16 : '.', IsChar, Down(), Ctrl(), Alt(), Shift(), System()); } bool CapsLock() const { return TestFlag(Flags, LGI_EF_CAPS_LOCK); } /// Returns the character in the right case... char16 GetChar() const { if (Shift() ^ CapsLock()) { return (c16 >= 'a' && c16 <= 'z') ? c16 - 'a' + 'A' : c16; } else { return (c16 >= 'A' && c16 <= 'Z') ? c16 - 'A' + 'a' : c16; } } /// \returns true if this event should show a context menu bool IsContextMenu() const; }; /// \brief All the parameters of a mouse click event /// /// The parent class LUiEvent keeps information about whether it was a Down() /// or Double() click. You can also query whether the Alt(), Ctrl() or Shift() /// keys were pressed at the time the event occurred. /// /// To get the position of the mouse in screen co-ordinates you can either use /// LView::GetMouse() and pass true in the 'ScreenCoords' parameter. Or you can /// construct a LPoint out of the x,y fields of this class and use LView::PointToScreen() /// to map the point to screen co-ordinates. class LgiClass LMouse : public LUiEvent, public LPoint { public: /// Receiving view class LViewI *Target; /// True if specified in view coordinates, false if in screen coords bool ViewCoords; LMouse(LViewI *target = NULL) { Target = target; ViewCoords = true; } LMouse operator -(LPoint p) { LMouse m = *this; m.x -= p.x; m.y -= p.y; return m; } LMouse operator +(LPoint p) { LMouse m = *this; m.x += p.x; m.y += p.y; return m; } bool operator !=(const LMouse &m) { return x != m.x || y != m.y || Flags != m.Flags; } LString ToString() const; void Trace(const char *Msg) const { LgiTrace("%s %s\n", Msg ? Msg : (char*)"", ToString().Get()); } bool Left() const { return TestFlag(Flags, LGI_EF_LEFT); } bool Middle() const { return TestFlag(Flags, LGI_EF_MIDDLE); } bool Right() const { return TestFlag(Flags, LGI_EF_RIGHT); } bool Button1() const { return TestFlag(Flags, LGI_EF_XBTN1); } bool Button2() const { return TestFlag(Flags, LGI_EF_XBTN2); } bool IsMove() const { return TestFlag(Flags, LGI_EF_MOVE); } void Left(bool i) { AssignFlag(Flags, LGI_EF_LEFT, i); } void Middle(bool i) { AssignFlag(Flags, LGI_EF_MIDDLE, i); } void Right(bool i) { AssignFlag(Flags, LGI_EF_RIGHT, i); } void Button1(bool i) { AssignFlag(Flags, LGI_EF_XBTN1, i); } void Button2(bool i) { AssignFlag(Flags, LGI_EF_XBTN2, i); } void IsMove(bool i) { AssignFlag(Flags, LGI_EF_MOVE, i); } /// Converts to screen coordinates bool ToScreen(); /// Converts to local coordinates bool ToView(); /// \returns true if this event should show a context menu bool IsContextMenu() const; #if defined(__OBJC__) && !defined(__GTK_H__) void SetFromEvent(NSEvent *ev, NSView *view); #else void SetButton(uint32_t Btn) { #if defined(MAC) && defined(__CARBONEVENTS__) Left(Btn == kEventMouseButtonPrimary); Right(Btn == kEventMouseButtonSecondary); Middle(Btn == kEventMouseButtonTertiary); #elif defined(__GTK_H__) if (Btn == 1) Left(true); else if (Btn == 2) Middle(true); else if (Btn == 3) Right(true); #endif } #endif }; /// Holds information pertaining to an application class LAppInfo { public: + /// Mime type for the app + LString MimeType; /// The path to the executable for the app LString Path; /// Plain text name for the app LString Name; /// A path to an icon to display for the app LString Icon; /// The params to call the app with LString Params; }; // Base class for GUI objects class LgiClass LBase { char *_Name8; char16 *_Name16; public: LBase(); virtual ~LBase(); virtual const char *Name(); virtual bool Name(const char *n); virtual const char16 *NameW(); virtual bool NameW(const char16 *n); }; #endif diff --git a/src/common/Gdc2/GdcCommon.cpp b/src/common/Gdc2/GdcCommon.cpp --- a/src/common/Gdc2/GdcCommon.cpp +++ b/src/common/Gdc2/GdcCommon.cpp @@ -1,1214 +1,1214 @@ #include "lgi/common/Lgi.h" #include "lgi/common/Palette.h" #include "ControlLook.h" ///////////////////////////////////////////////////////////////////////////// // Mem ops void Add64(ulong *DestH, ulong *DestL, ulong SrcH, ulong SrcL) { *DestH += SrcH + (*DestL & SrcL & 0x80000000) ? 1 : 0; *DestL += SrcL; } void MemSet(void *d, int c, uint s) { if (d && s > 0) memset(d, c, s); } void MemCpy(void *d, void *s, uint l) { if (d && s && l > 0) memcpy(d, s, l); } void MemAnd(void *d, void *s, uint l) { uchar *D = (uchar*) d; uchar *S = (uchar*) s; if (D && S) { for (; l > 0; l--) { *D++ &= *S++; } } } void MemXor(void *d, void *s, uint l) { uchar *D = (uchar*) d; uchar *S = (uchar*) s; if (D && S) { for (; l > 0; l--) { *D++ ^= *S++; } } } void MemOr(void *d, void *s, uint l) { uchar *D = (uchar*) d; uchar *S = (uchar*) s; if (D && S) { for (; l > 0; l--) { *D++ |= *S++; } } } ////////////////////////////////////////////////////////////////////// bool LFindBounds(LSurface *pDC, LRect *rc) { if (!pDC || ! rc) return false; LAssert(pDC->GetColourSpace() == System32BitColourSpace); // Move top border down to image while (rc->y1 < rc->y2) { System32BitPixel *p = (System32BitPixel*)(*pDC)[rc->y1]; if (!p) return false; p += rc->x1; System32BitPixel *e = p + rc->X(); bool IsTrans = true; while (p < e) { if (p->a != 0) { IsTrans = false; break; } p++; } if (IsTrans) rc->y1++; else break; } // Move bottom border up to image while (rc->y2 >= rc->y1) { System32BitPixel *p = (System32BitPixel*)(*pDC)[rc->y2]; if (!p) return false; p += rc->x1; System32BitPixel *e = p + rc->X(); bool IsTrans = true; while (p < e) { if (p->a != 0) { IsTrans = false; break; } p++; } if (IsTrans) rc->y2--; else break; } // Do the left and right edges too int x1 = rc->x2; int x2 = rc->x1; for (int y=rc->y1; y<=rc->y2; y++) { System32BitPixel *p = (System32BitPixel*)(*pDC)[y]; if (!p) return false; System32BitPixel *px1 = p + rc->x1; System32BitPixel *px2 = p + rc->x2; while (px1 < px2 && px1->a == 0) px1++; x1 = MIN(x1, (int) (px1 - p)); while (px2 >= px1 && px2->a == 0) px2--; x2 = MAX(x2, (int) (px2 - p)); } rc->x1 = x1; rc->x2 = x2; if (rc->Valid()) return true; // No data? rc->ZOff(-1, -1); return false; } ////////////////////////////////////////////////////////////////////// // Drawing functions void LDrawBox(LSurface *pDC, LRect &r, bool Sunken, bool Fill) { if (Fill) { pDC->Colour(LColour(L_MED)); pDC->Rectangle(r.x1+1, r.y1+1, r.x2-1, r.y2-1); } pDC->Colour((Sunken) ? LColour(L_LIGHT) : LColour(L_LOW)); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); pDC->Colour((Sunken) ? LColour(L_LOW) : LColour(L_LIGHT)); pDC->Line(r.x1, r.y1, r.x1, r.y2); pDC->Line(r.x1, r.y1, r.x2, r.y1); } void LWideBorder(LSurface *pDC, LRect &r, LEdge Type) { if (!pDC) return; COLOUR Old = pDC->Colour(); LColour VLow = LColour(L_SHADOW); LColour Low = LColour(L_LOW); LColour High = LColour(L_HIGH); LColour VHigh = LColour(L_LIGHT); switch (Type) { case EdgeXpSunken: { // XP theme pDC->Colour(Low); pDC->Line(r.x1, r.y1, r.x2-1, r.y1); pDC->Line(r.x1, r.y1, r.x1, r.y2-1); pDC->Colour(VLow); pDC->Line(r.x1+1, r.y1+1, r.x2-2, r.y1+1); pDC->Line(r.x1+1, r.y1+1, r.x1+1, r.y2-2); pDC->Colour(High); pDC->Line(r.x2-1, r.y2-1, r.x2-1, r.y1+1); pDC->Line(r.x2-1, r.y2-1, r.x1+1, r.y2-1); pDC->Colour(VHigh); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); break; } case EdgeXpRaised: { pDC->Colour(VHigh); pDC->Line(r.x1, r.y1, r.x2-1, r.y1); pDC->Line(r.x1, r.y1, r.x1, r.y2-1); pDC->Colour(High); pDC->Line(r.x1+1, r.y1+1, r.x2-1, r.y1+1); pDC->Line(r.x1+1, r.y1+1, r.x1+1, r.y2-1); pDC->Colour(Low); pDC->Line(r.x2-1, r.y2-1, r.x2-1, r.y1+1); pDC->Line(r.x2-1, r.y2-1, r.x1+1, r.y2-1); pDC->Colour(VLow); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); break; } case EdgeXpChisel: { pDC->Colour(Low); pDC->Line(r.x1, r.y1, r.x2-1, r.y1); pDC->Line(r.x1, r.y1, r.x1, r.y2-1); pDC->Colour(VHigh); pDC->Line(r.x1+1, r.y1+1, r.x2-2, r.y1+1); pDC->Line(r.x1+1, r.y1+1, r.x1+1, r.y2-2); pDC->Colour(Low); pDC->Line(r.x2-1, r.y2-1, r.x2-1, r.y1+1); pDC->Line(r.x2-1, r.y2-1, r.x1+1, r.y2-1); pDC->Colour(VHigh); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); break; } case EdgeWin7Sunken: case EdgeWin7FocusSunken: { bool Focus = Type == EdgeWin7FocusSunken; // Win7 theme LColour Med(L_MED), Ws(L_WORKSPACE); LColour Mixer = Med.GetGray() >= 128 ? LColour::Black : LColour::White; LColour TopLeft, RightBottom; if (Focus) { LColour Focus(L_FOCUS_SEL_BACK); TopLeft = Med.Mix(Focus, 0.4f); RightBottom = Ws.Mix(Focus, 0.2f); } else { TopLeft = Med.Mix(Mixer, 0.4f); RightBottom = Ws.Mix(Mixer, 0.2f); } // Top corners pDC->Colour(Med); pDC->Set(r.x1, r.y1); pDC->Set(r.x2, r.y1); pDC->Colour(Ws.Mix(TopLeft)); pDC->Set(r.x1+1, r.y1+1); pDC->Set(r.x2-1, r.y1+1); pDC->Set(r.x1+1, r.y2-1); // Edges pDC->Colour(TopLeft); pDC->Line(r.x1, r.y1+1, r.x1, r.y2-1); // left pDC->Line(r.x1+1, r.y1, r.x2-1, r.y1); // top // Bottom edge pDC->Colour(RightBottom); pDC->Line(r.x1+1, r.y2, r.x2-1, r.y2); // bottom pDC->Line(r.x2, r.y1+1, r.x2, r.y2-1); // right // Inner workspace rect pDC->Colour(Ws); pDC->Line(r.x1+2, r.y1+1, r.x2-2, r.y1+1); // top pDC->Line(r.x1+1, r.y1+2, r.x1+1, r.y2-2); // left pDC->Line(r.x2-1, r.y1+2, r.x2-1, r.y2-2); // right pDC->Line(r.x1+2, r.y2-1, r.x2-1, r.y2-1); // bottom break; } default: { return; } } r.Inset(2, 2); pDC->Colour(Old); } void LThinBorder(LSurface *pDC, LRect &r, LEdge Type) { if (!pDC) return; COLOUR Old = pDC->Colour(); switch (Type) { case EdgeXpSunken: case EdgeWin7FocusSunken: case EdgeWin7Sunken: { pDC->Colour(LColour(L_LIGHT)); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); pDC->Colour(LColour(L_LOW)); pDC->Line(r.x1, r.y1, r.x1, r.y2); pDC->Line(r.x1, r.y1, r.x2, r.y1); r.Inset(1, 1); break; } case EdgeXpRaised: { pDC->Colour(LColour(L_LOW)); pDC->Line(r.x2, r.y2, r.x2, r.y1); pDC->Line(r.x2, r.y2, r.x1, r.y2); pDC->Colour(LColour(L_LIGHT)); pDC->Line(r.x1, r.y1, r.x1, r.y2); pDC->Line(r.x1, r.y1, r.x2, r.y1); r.Inset(1, 1); break; } default: { LAssert(0); break; } } pDC->Colour(Old); } void LFlatBorder(LSurface *pDC, LRect &r, int Width) { pDC->Colour(LColour(L_MED)); if (Width < 1 || r.X() < (2 * Width) || r.Y() < (2 * Width)) { pDC->Rectangle(&r); r.ZOff(-1, -1); } else { pDC->Rectangle(r.x1, r.y1, r.x2, r.y1+Width-1); pDC->Rectangle(r.x1, r.y2-Width+1, r.x2, r.y2); pDC->Rectangle(r.x1, r.y1+Width, r.x1+Width-1, r.y2-Width); pDC->Rectangle(r.x2-Width+1, r.y1+Width, r.x2, r.y2-Width); r.Inset(Width, Width); } } void LFillGradient(LSurface *pDC, LRect &r, bool Vert, LArray &Stops) { int CurStop = 0; LColourStop *This = Stops.Length() > CurStop ? &Stops[CurStop] : 0; CurStop++; LColourStop *Next = Stops.Length() > CurStop ? &Stops[CurStop] : 0; int Limit = Vert ? r.Y() : r.X(); for (int n=0; nPos) { // Just this c = This->Colour; } else if (p > This->Pos && p < Next->Pos) { // Interpolate between this and next float d = Next->Pos - This->Pos; float t = (Next->Pos - p) / d; float n = (p - This->Pos) / d; uint8_t r = (uint8_t) ((This->Colour.r() * t) + (Next->Colour.r() * n)); uint8_t g = (uint8_t) ((This->Colour.g() * t) + (Next->Colour.g() * n)); uint8_t b = (uint8_t) ((This->Colour.b() * t) + (Next->Colour.b() * n)); uint8_t a = (uint8_t) ((This->Colour.a() * t) + (Next->Colour.a() * n)); c.Rgb(r, g, b, a); } else if (p >= Next->Pos) { // Get next colour stops This = Stops.Length() > CurStop ? &Stops[CurStop] : 0; CurStop++; Next = Stops.Length() > CurStop ? &Stops[CurStop] : 0; goto DoStop; } pDC->Colour(c); if (Vert) pDC->Line(r.x1, r.y1 + n, r.x2, r.y1 + n); else pDC->Line(r.x1 + n, r.y1, r.x1 + n, r.y2); } } } ////////////////////////////////////////////////////////////////////////////////// // Other Gdc Stuff LSurface *ConvertDC(LSurface *pDC, int Bits) { LSurface *pNew = new LMemDC; if (pNew && pNew->Create(pDC->X(), pDC->Y(), LBitsToColourSpace(Bits))) { pNew->Blt(0, 0, pDC); DeleteObj(pDC); return pNew; } return pDC; } LColour GdcMixColour(LColour c1, LColour c2, float HowMuchC1) { float HowMuchC2 = 1.0f - HowMuchC1; uint8_t r = (uint8_t) ((c1.r()*HowMuchC1) + (c2.r()*HowMuchC2)); uint8_t g = (uint8_t) ((c1.g()*HowMuchC1) + (c2.g()*HowMuchC2)); uint8_t b = (uint8_t) ((c1.b()*HowMuchC1) + (c2.b()*HowMuchC2)); uint8_t a = (uint8_t) ((c1.a()*HowMuchC1) + (c2.a()*HowMuchC2)); return LColour(r, g, b, a); } COLOUR CBit(int DstBits, COLOUR c, int SrcBits, LPalette *Pal) { if (SrcBits == DstBits) { return c; } else { switch (SrcBits) { case 8: { GdcRGB Grey, *p = 0; if (!Pal || !(p = (*Pal)[c])) { Grey.r = Grey.g = Grey.b = c & 0xFF; p = &Grey; } switch (DstBits) { case 16: { return Rgb16(p->r, p->g, p->b); } case 24: { return Rgb24(p->r, p->g, p->b); } case 32: { return Rgb32(p->r, p->g, p->b); } } break; } case 15: { int R = R15(c); int G = G15(c); int B = B15(c); R = R | (R >> 5); G = G | (G >> 5); B = B | (B >> 5); switch (DstBits) { case 8: { if (Pal) { return Pal->MatchRgb(Rgb24(R, G, B)); } break; } case 16: { return Rgb16(R, G, B); } case 24: { return Rgb24(R, G, B); } case 32: { return Rgb32(R, G, B); } } break; } case 16: { /* int R = ((c>>8)&0xF8) | ((c>>13)&0x7); int G = ((c>>3)&0xFC) | ((c>>9)&0x3); int B = ((c<<3)&0xF8) | ((c>>2)&0x7); */ int R = (c >> 8) & 0xF8; int G = (c >> 3) & 0xFC; int B = (c << 3) & 0xF8; switch (DstBits) { case 8: { if (Pal) { return Pal->MatchRgb(Rgb24(R, G, B)); } break; } case 15: { return Rgb16To15(c); } case 24: { return Rgb24(R, G, B); } case 32: { return Rgb32(R, G, B); } } break; } case 24: { switch (DstBits) { case 8: { if (Pal) { return Pal->MatchRgb(c); } break; } case 15: { return Rgb24To15(c); } case 16: { if (LGetOs() == LGI_OS_WIN32 || LGetOs() == LGI_OS_WIN64) { return Rgb24To16(c); } int r = MAX(R24(c) - 1, 0) >> 3; int g = MAX(G24(c) - 1, 0) >> 2; int b = MAX(B24(c) - 1, 0) >> 3; return (r << 11) | (g << 5) | (b); } case 24: case 48: { return c; } case 32: case 64: { return Rgb24To32(c); } default: LAssert(0); break; } break; } case 32: { switch (DstBits) { case 8: { if (Pal) { return Pal->MatchRgb(Rgb32To24(c)); } break; } case 15: { return Rgb32To15(c); } case 16: { return Rgb32To16(c); } case 24: { return Rgb32To24(c); } } break; } } } return c; } ///////////////////////////////////////////////////////////////////////////// const char *LColourSpaceToString(LColourSpace cs) { #define CS_STR_BUF 4 static int Cur = 0; static char Buf[CS_STR_BUF][16]; static const char *CompTypes[] = { "N", // None "I", // Index "R", // Red "G", // Green "B", // Blue "A", // Alpha "X", // Pad "H", // Hue "S", // Sat "L", // Lum "C", // Cyan "M", // Magenta "Y", // Yellow "B", // Black "?", "?", }; char *start = Buf[Cur++], *s = start; int total = 0; bool first = true; if (Cur >= CS_STR_BUF) Cur = 0; *s++ = 'C'; *s++ = 's'; // printf("Converting Cs to String: 0x%x\n", cs); for (int i=3; i>=0; i--) { int c = (((uint32_t)cs) >> (i << 3)) & 0xff; // printf(" c[%i] = 0x%x\n", i, c); if (c) { LComponentType type = (LComponentType)(c >> 4); int size = c & 0xf; if (first) { *s++ = CompTypes[type][0]; first = false; } else { *s++ = tolower(CompTypes[type][0]); } total += size ? size : 16; } } s += sprintf_s(s, 4, "%i", total); return start; } int LColourSpaceChannels(LColourSpace Cs) { int Channels = 0; while (Cs) { uint8_t c = Cs & 0xff; if (!c) break; Channels++; ((int&)Cs) >>= 8; } return Channels; } bool LColourSpaceHasAlpha(LColourSpace Cs) { while (Cs) { uint8_t c = Cs & 0xff; LComponentType type = (LComponentType)(c >> 4); if (type == CtAlpha) return true; ((int&)Cs) >>= 8; } return false; } int LColourSpaceToBits(LColourSpace ColourSpace) { uint32_t c = ColourSpace; int bits = 0; while (c) { if (c & 0xf0) { int n = c & 0xf; bits += n ? n : 16; } c >>= 8; } return bits; } LColourSpace LStringToColourSpace(const char *c) { if (!c) return CsNone; if (!_strnicmp(c, "Cs", 2)) { // "Cs" style colour space c += 2; const char *n = c; while (*n && !IsDigit(*n)) n++; int Depth = ::atoi(n); if (!_strnicmp(c, "Index", 5)) { if (Depth == 8) return CsIndex8; } else { LArray Comp; while (*c) { switch (tolower(*c)) { case 'r': Comp.Add(CtRed); break; case 'g': Comp.Add(CtGreen); break; case 'b': Comp.Add(CtBlue); break; case 'a': Comp.Add(CtAlpha); break; case 'x': Comp.Add(CtPad); break; case 'c': Comp.Add(CtCyan); break; case 'm': Comp.Add(CtMagenta); break; case 'y': Comp.Add(CtYellow); break; case 'k': Comp.Add(CtBlack); break; case 'h': Comp.Add(CtHue); break; case 'l': Comp.Add(CtLuminance); break; case 's': Comp.Add(CtSaturation); break; } c++; } LColourSpaceBits a; ZeroObj(a); if (Comp.Length() == 3) { a[0].Type(Comp[0]); a[1].Type(Comp[1]); a[2].Type(Comp[2]); if (Depth == 24) { a[0].Size(8); a[1].Size(8); a[2].Size(8); } else if (Depth == 16) { a[0].Size(5); a[1].Size(6); a[2].Size(5); } else if (Depth == 15) { a[0].Size(5); a[0].Size(5); a[0].Size(5); } else if (Depth == 48) { a[0].Size(0); a[1].Size(0); a[2].Size(0); } else return CsNone; } else if (Comp.Length() == 4) { a[0].Type(Comp[0]); a[1].Type(Comp[1]); a[2].Type(Comp[2]); a[3].Type(Comp[3]); if (Depth == 32) { a[0].Size(8); a[1].Size(8); a[2].Size(8); a[3].Size(8); } else if (Depth == 64) { a[0].Size(0); a[1].Size(0); a[2].Size(0); a[3].Size(0); } else return CsNone; } LColourSpace Cs = (LColourSpace)a.All; return Cs; } } else if (!_strnicmp(c, "System", 6)) { // "System" colour space c += 6; int Depth = ::atoi(c); if (Depth) return LBitsToColourSpace(Depth); } return CsNone; } LColourSpace LBitsToColourSpace(int Bits) { switch (Bits) { case 8: return CsIndex8; case 15: return System15BitColourSpace; case 16: return System16BitColourSpace; case 24: return System24BitColourSpace; case 32: return System32BitColourSpace; default: LAssert(!"Unknown colour space."); break; } return CsNone; } bool LColourSpaceTest() { union { uint8_t b4[4]; uint32_t u32; LBgrx32 bgrx32; LXrgb32 xrgb32; LRgba32 rgba32; }; b4[0] = 1; b4[1] = 2; b4[2] = 3; b4[3] = 4; #if 0 // def LGI_SDL printf("LeastSigBit=%i, LeastSigByte=%i, u32=%08x\n", LeastSigBit, LeastSigByte, u32); printf("bgrx32=%i,%i,%i,%i\n", bgrx32.b, bgrx32.g, bgrx32.r, bgrx32.pad); printf("xrgb32=%i,%i,%i,%i\n", xrgb32.pad, xrgb32.r, xrgb32.g, xrgb32.b); #endif if (bgrx32.b != 1 || bgrx32.pad != 4 || xrgb32.pad != 1 || xrgb32.b != 4) { LAssert(!"32bit colour space byte ordering wrong. Flip the value of LEAST_SIG_BYTE_FIRST"); return false; } union { uint16 u16; LRgb16 rgb16; }; rgba32.r = 0xff; rgba32.g = 0; rgba32.b = 0; rgba32.a = 0; u16 = 0xf800; #if 0 // def LGI_SDL printf("rgba32=%i,%i,%i,%i or %08x\n", rgba32.r, rgba32.g, rgba32.b, rgba32.a, u32); printf("rgb16=%i,%i,%i or %04x\n", rgb16.r, rgb16.g, rgb16.b, u16); #endif if (rgb16.r != 0x1f || rgb16.b != 0x0) { LAssert(!"16bit colour space bit ordering wrong. Flip the value of LEAST_SIG_BIT_FIRST"); return false; } return true; } //////////////////////////////////////////////////////////////////////// LSurface *LInlineBmp::Create(uint32_t TransparentPx) { LSurface *pDC = new LMemDC; if (pDC->Create(X, Y, System32BitColourSpace, LSurface::SurfaceRequireExactCs)) { LBmpMem Src, Dst; Src.Base = (uint8_t*)Data; Src.Line = X * Bits >> 3; Src.x = X; Src.y = Y; switch (Bits) { case 8: Src.Cs = CsIndex8; break; case 15: Src.Cs = CsRgb15; break; case 16: Src.Cs = CsRgb16; break; case 24: Src.Cs = CsRgb24; break; case 32: Src.Cs = CsRgba32; break; default: Src.Cs = CsNone; break; } Dst.Base = (*pDC)[0]; Dst.Line = pDC->GetRowStep(); Dst.x = pDC->X(); Dst.y = pDC->Y(); Dst.Cs = pDC->GetColourSpace(); LRopUniversal(&Dst, &Src, false); if (TransparentPx != 0xffffffff) { for (int y=0; y> 3) { case 1: { for (int x=0; xr == r && px->g == g && px->b == b) { *d = 0; } d++; } break; } case 4: { for (int x=0; x %s\n", _FL, LColourSpaceToString(SrcCs), LColourSpaceToString(DstCs)); LAssert(!"Unsupported pixel conversion."); return false; } } return true; } /// Universal bit blt method bool LRopUniversal(LBmpMem *Dst, LBmpMem *Src, bool Composite) { if (!Dst || !Src) return false; // Work out conversion area... int Cx = MIN(Dst->x, Src->x); int Cy = MIN(Dst->y, Src->y); // Size of src and dst pixels: int SrcBits = LColourSpaceToBits(Src->Cs); int DstBits = LColourSpaceToBits(Dst->Cs); if (Dst->Cs == Src->Cs && !Composite) { // No conversion idiom uint8_t *d = Dst->Base; uint8_t *s = Src->Base; int BytesPerPx = (SrcBits + 7) / 8; int Bytes = BytesPerPx * Cx; for (int y=0; yLine; s += Src->Line; } return true; } if (SrcBits > 8 && DstBits > 8) { uint8_t *d = Dst->Base; uint8_t *s = Src->Base; for (int y=0; yCs, s, Src->Cs, Cx, Composite)) return false; d += Dst->Line; s += Src->Line; } return true; } else { } // LAssert(!"Unsupported pixel conversion."); return false; } LPoint LScreenDpi() { static LPoint Dpi; #if LGI_COCOA if (!Dpi.x) { NSScreen *screen = [NSScreen mainScreen]; NSDictionary *description = [screen deviceDescription]; NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; CGSize displayPhysicalSize = CGDisplayScreenSize( [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); Dpi.x = (int) ((displayPixelSize.width / displayPhysicalSize.width) * 25.4f); Dpi.y = (int) ((displayPixelSize.height / displayPhysicalSize.height) * 25.4f); } #elif defined(__GTK_H__) if (!Dpi.x) { auto dpi = Gtk::gdk_screen_get_resolution(Gtk::gdk_screen_get_default()); if (dpi > 0) Dpi.Set((int)dpi, (int)dpi); } #elif defined(HAIKU) if (!Dpi.x) { double scaling = std::max(1.0f, be_plain_font->Size() / 12.0f); Dpi.x = (int)(96.0 * scaling); Dpi.y = Dpi.x; - printf("scaling=%g dpi=%i\n", scaling, Dpi.x); + // printf("scaling=%g dpi=%i\n", scaling, Dpi.x); } #elif defined(WINDOWS) if (!Dpi.x) { auto dpi = GetDpiForWindow(GetDesktopWindow()); Dpi.Set(dpi, dpi); } #endif if (Dpi.x <= 0) Dpi.Set(96, 96); return Dpi; } bool LMemDC::SwapRedAndBlue() { switch (GetColourSpace()) { #define ROP(cs) \ case Cs##cs: \ { \ for (int y=0; yr, p->b); \ p++; \ } \ break; \ } \ break; \ } ROP(Rgb24) ROP(Bgr24) ROP(Rgbx32) ROP(Bgrx32) ROP(Xrgb32) ROP(Xbgr32) ROP(Argb32) ROP(Abgr32) ROP(Rgba32) ROP(Bgra32) default: return false; } return true; } diff --git a/src/common/Text/DocView.cpp b/src/common/Text/DocView.cpp --- a/src/common/Text/DocView.cpp +++ b/src/common/Text/DocView.cpp @@ -1,224 +1,224 @@ #include #include "lgi/common/Lgi.h" #include "lgi/common/DocView.h" #define SubtractPtr(a, b) ((a)-(b)) const char *LDocView::WhiteSpace = " \t\r\n"; const char *LDocView::Delimiters = "!@#$%^&*()'\":;,.<>/?[]{}-=+\\|`~"; const char *LDocView::UrlDelim = "!~/:%+-?@&$#._=,;*()|"; LDocumentEnv::LDocumentEnv(LDocView *v) { if (v) { Viewers.Add(v); } } LDocumentEnv::~LDocumentEnv() { for (uint32_t i=0; iEnvironment = 0; } } int LDocumentEnv::NextUid() { if (!Lock(_FL)) return -1; int Uid = 0; for (auto v : Viewers) Uid = MAX(Uid, v->GetDocumentUid() + 1); Unlock(); return Uid; } void LDocumentEnv::OnDone(LAutoPtr j) { LoadJob *ld = dynamic_cast(j.Get()); if (ld) { if (Lock(_FL)) { LDocView *View = NULL; for (unsigned i=0; iGetDocumentUid(); if (Uid == ld->UserUid) { View = Viewers[i]; break; } } if (View) { View->OnContent(ld); j.Release(); } Unlock(); } } else LAssert(!"RTTI failed."); } ////////////////////////////////////////////////////////////////////////////////// char16 *ConvertToCrLf(char16 *Text) { if (Text) { // add '\r's int Lfs = 0; int Len = 0; char16 *s=Text; for (; *s; s++) { if (*s == '\n') Lfs++; Len++; } char16 *Temp = new char16[Len+Lfs+1]; if (Temp) { char16 *d=Temp; s = Text; for (; *s; s++) { if (*s == '\n') { *d++ = 0x0d; *d++ = 0x0a; } else if (*s == '\r') { // ignore } else { *d++ = *s; } } *d++ = 0; DeleteObj(Text); return Temp; } } return Text; } ////////////////////////////////////////////////////////////////////////////////// #include "lgi/common/Net.h" LDocumentEnv::LoadType LDefaultDocumentEnv::GetContent(LoadJob *&j) { if (!j || !ValidStr(j->Uri)) return LoadError; char p[MAX_PATH_LEN]; char *FullPath = NULL; char *FileName = NULL; LUri u(j->Uri); if (u.sProtocol && !_stricmp(u.sProtocol, "file")) FileName = u.sPath; else FileName = j->Uri; if (FileName) { LGetSystemPath(LSP_APP_INSTALL, p, sizeof(p)); LMakePath(p, sizeof(p), p, FileName); if (LFileExists(p)) { FullPath = p; } else { auto f = LFindFile(FileName); if (f) strcpy_s(FullPath = p, sizeof(p), f); } } if (LFileExists(FullPath)) { LString Mt = LGetFileMimeType(FullPath); if (Mt.Find("image/") == 0) { j->pDC.Reset(GdcD->Load(p)); return LoadImmediate; } j->Filename = FullPath; return LoadImmediate; } return LoadError; } bool LDefaultDocumentEnv::OnNavigate(LDocView *Parent, const char *Uri) { if (Uri) { if ( _strnicmp(Uri, "mailto:", 7) == 0 || ( strchr(Uri, '@') != 0 && strchr(Uri, '/') == 0 ) ) { // email - LArray Apps; + LArray Apps; if (LGetAppsForMimeType("application/email", Apps)) { - LAppInfo *First = Apps[0]; + LAppInfo *First = &Apps[0]; LStringPipe a; char *Arg = First->Params ? strstr(First->Params, "%1") : 0; if (Arg) { // change '%1' into the email address in question a.Write(First->Params, (int) (Arg-First->Params)); a.Print("%s%s", Uri, Arg + 2); } else { // no argument place holder... just pass as a normal arg a.Print(" %s", Uri); } LAutoString Exe(TrimStr(First->Path, "\"\'")); LAutoString Args(a.NewStr()); LString ErrorMsg; if (LExecute(Exe, Args, ".", &ErrorMsg)) return true; LgiMsg(Parent, "Failed to open '%s':\n%s", LAppInst->LBase::Name(), MB_OK, Exe.Get(), ErrorMsg.Get()); } else { LgiMsg(Parent, "Couldn't get app to handle email.", LAppInst->LBase::Name(), MB_OK); } } else { // webpage LString ErrorMsg; if (LExecute(Uri, NULL, NULL, &ErrorMsg)) return true; LgiMsg(Parent, "Failed to open '%s':\n%s", LAppInst->LBase::Name(), MB_OK, Uri, ErrorMsg.Get()); } } return false; } diff --git a/src/common/Widgets/Button.cpp b/src/common/Widgets/Button.cpp --- a/src/common/Widgets/Button.cpp +++ b/src/common/Widgets/Button.cpp @@ -1,560 +1,565 @@ #if !defined(_WIN32) || (XP_BUTTON != 0) #include #include #include "lgi/common/Lgi.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/Button.h" #include "lgi/common/DisplayString.h" #include "lgi/common/TableLayout.h" #include "lgi/common/LgiRes.h" #include "lgi/common/StringLayout.h" #include "lgi/common/CssTools.h" #define DOWN_MOUSE 0x1 #define DOWN_KEY 0x2 #if 0 #define DEBUG_LOG(...) printf(__VA_ARGS__) #else #define DEBUG_LOG(...) #endif // Size of extra pixels, beyond the size of the text itself. LPoint LButton::Overhead = LPoint( // Extra width needed #if defined(MAC) && !defined(LGI_SDL) 24, #else 16, #endif // Extra height needed 6 ); class LButtonPrivate : public LStringLayout { public: int Pressed; bool KeyDown; bool Over; bool WantsDefault; bool Toggle; LRect TxtSz; LSurface *Image; bool OwnImage; LButtonPrivate() : LStringLayout(LAppInst->GetFontCache()) { AmpersandToUnderline = true; Pressed = 0; KeyDown = false; Toggle = false; Over = 0; WantsDefault = false; Image = NULL; OwnImage = false; SetWrap(false); } ~LButtonPrivate() { if (OwnImage) DeleteObj(Image); } void Layout(LCss *css, const char *s) { Empty(); LCss c(*css); c.FontWeight(LCss::FontWeightBold); Add(s, &c); int32 MinX, MaxX; DoPreLayout(MinX, MaxX); DoLayout(MaxX); TxtSz = GetBounds(); } }; LButton::LButton(int id, int x, int y, int cx, int cy, const char *name) : ResObject(Res_Button) { d = new LButtonPrivate; Name(name); LRect r(x, y, x + (cx <= 0 ? d->TxtSz.X() + Overhead.x : cx) - 1, y + (cy <= 0 ? d->TxtSz.Y() + Overhead.y : cy) - 1); LAssert(r.Valid()); SetPos(r); SetId(id); SetTabStop(true); } LButton::~LButton() { if (GetWindow() && GetWindow()->_Default == this) { GetWindow()->_Default = 0; } DeleteObj(d); } int LButton::OnNotify(LViewI *Ctrl, LNotification n) { if (Ctrl == (LViewI*)this && n.Type == LNotifyActivate) { LMouse m; if (n.IsMouseEvent()) m = n.GetMouseEvent(); else GetMouse(m); OnClick(m); } return 0; } bool LButton::Default() { if (GetWindow()) return GetWindow()->_Default == this; return false; } void LButton::Default(bool b) { if (GetWindow()) { GetWindow()->_Default = b ? this : 0; if (IsAttached()) { Invalidate(); } } else { d->WantsDefault = b; } } bool LButton::GetIsToggle() { return d->Toggle; } void LButton::SetIsToggle(bool toggle) { d->Toggle = toggle; } LSurface *LButton::GetImage() { return d->Image; } bool LButton::SetImage(const char *FileName) { if (d->OwnImage) DeleteObj(d->Image); d->Image = GdcD->Load(FileName); Invalidate(); return d->OwnImage = d->Image != NULL; } bool LButton::SetImage(LSurface *Img, bool OwnIt) { if (d->OwnImage) DeleteObj(d->Image); d->Image = Img; d->OwnImage = d->Image != NULL && OwnIt; Invalidate(); return d->OwnImage; } void LButton::OnStyleChange() { d->Layout(GetCss(true), LBase::Name()); } bool LButton::Name(const char *n) { bool Status = LView::Name(n); OnStyleChange(); return Status; } bool LButton::NameW(const char16 *n) { bool Status = LView::NameW(n); OnStyleChange(); return Status; } void LButton::SetFont(LFont *Fnt, bool OwnIt) { LAssert(Fnt && Fnt->Handle()); LView::SetFont(Fnt, OwnIt); OnStyleChange(); Invalidate(); } LMessage::Result LButton::OnEvent(LMessage *Msg) { return LView::OnEvent(Msg); } void LButton::OnMouseClick(LMouse &m) { if (!Enabled()) { DEBUG_LOG("Not enabled\n"); return; } if (d->Toggle) { DEBUG_LOG("OnMouseClick: Toggle=true, m.Down=%i\n", m.Down()); if (m.Down()) { Value(!Value()); OnClick(m); } } else { bool Click = IsCapturing(); DEBUG_LOG("OnMouseClick: Toggle=false, Click=%i, m.Down()=%i\n", Click, m.Down()); Capture(m.Down()); if (Click ^ m.Down()) { DEBUG_LOG("d->Over=%i\n", d->Over); if (d->Over) { if (m.Down()) { d->Pressed++; Focus(true); } else { d->Pressed--; } Invalidate(); DEBUG_LOG("m.Down()=%i d->Pressed=%i\n", m.Down(), d->Pressed); if (!m.Down() && d->Pressed == 0) { // This may delete ourself, so do it last. OnClick(m); } } } } } void LButton::OnMouseEnter(LMouse &m) { DEBUG_LOG("OnMouseEnter\n"); d->Over = true; if (IsCapturing()) { Value(d->Pressed + 1); } else if (Enabled()) { if (!LAppInst->SkinEngine) Invalidate(); } } void LButton::OnMouseExit(LMouse &m) { DEBUG_LOG("OnMouseExit\n"); d->Over = false; if (IsCapturing()) { Value(d->Pressed - 1); } else if (Enabled()) { if (!LAppInst->SkinEngine) Invalidate(); } } bool LButton::OnKey(LKey &k) { if ( #ifdef WINNATIVE k.IsChar || #endif !Enabled()) { return false; } switch (k.vkey) { case LK_ESCAPE: { if (GetId() != IDCANCEL) { break; } // else fall thru } case LK_SPACE: case LK_RETURN: #ifndef WINDOWS case LK_KEYPADENTER: #endif { if (d->KeyDown ^ k.Down()) { d->KeyDown = k.Down(); if (k.Down()) { d->Pressed++; } else { d->Pressed--; } Invalidate(); if (!k.Down() && d->Pressed == 0) { LMouse m; GetMouse(m); OnClick(m); } } return true; break; } } return false; } void LButton::OnClick(const LMouse &m) { SendNotify(LNotification(m)); } void LButton::OnFocus(bool f) { Invalidate(); } void LButton::OnPaint(LSurface *pDC) { #if defined LGI_CARBON LColour NoPaintColour = StyleColour(LCss::PropBackgroundColor, LColour(L_MED)); if (!NoPaintColour.IsTransparent()) { pDC->Colour(NoPaintColour); pDC->Rectangle(); } LRect rc = GetClient(); rc.x1 += 2; rc.y2 -= 1; rc.x2 -= 1; HIRect Bounds = rc; HIThemeButtonDrawInfo Info; HIRect LabelRect; Info.version = 0; Info.state = d->Pressed ? kThemeStatePressed : (Enabled() ? kThemeStateActive : kThemeStateInactive); Info.kind = kThemePushButton; Info.value = /*Default() ? kThemeButtonOn :*/ kThemeButtonOff; Info.adornment = Focus() ? kThemeAdornmentFocus : kThemeAdornmentNone; OSStatus e = HIThemeDrawButton( &Bounds, &Info, pDC->Handle(), kHIThemeOrientationNormal, &LabelRect); if (e) printf("%s:%i - HIThemeDrawButton failed %li\n", _FL, e); else { LPoint pt; LRect r = GetClient(); pt.x = r.x1 + ((r.X()-d->TxtSz.X())/2) + (d->Pressed != 0); pt.y = r.y1 + ((r.Y()-d->TxtSz.Y())/2) + (d->Pressed != 0); d->Paint(pDC, pt, LColour(), r, Enabled(), Info.state == kThemeStatePressed); } + + // #elif defined(HAIKU) + + // FIXME: + // Use BControlLook to do the drawing... #else if (LApp::SkinEngine && TestFlag(LApp::SkinEngine->GetFeatures(), GSKIN_BUTTON)) { LSkinState State; State.pScreen = pDC; State.MouseOver = d->Over; State.Image = d->Image; if (X() < GdcD->X() && Y() < GdcD->Y()) LApp::SkinEngine->OnPaint_LButton(this, &State); LPoint pt; LRect r = GetClient(); pt.x = r.x1 + ((r.X()-d->TxtSz.X())/2) + (d->Pressed != 0); pt.y = r.y1 + ((r.Y()-d->TxtSz.Y())/2) + (d->Pressed != 0); d->Paint(pDC, pt, LColour(), r, Enabled(), false); if (Focus()) { LRect r = GetClient(); r.Inset(5, 3); pDC->Colour(LColour(180, 180, 180)); pDC->LineStyle(LSurface::LineAlternate); pDC->Box(&r); } } else { LColour Back(d->Over ? L_HIGH : L_MED); LRect r(0, 0, X()-1, Y()-1); if (Default()) { pDC->Colour(L_BLACK); pDC->Box(&r); r.Inset(1, 1); } LWideBorder(pDC, r, d->Pressed ? DefaultSunkenEdge : DefaultRaisedEdge); LPoint pt; pt.x = r.x1 + ((r.X()-d->TxtSz.X())/2) + (d->Pressed != 0); pt.y = r.y1 + ((r.Y()-d->TxtSz.Y())/2) + (d->Pressed != 0); d->Paint(pDC, pt, Back, r, Enabled(), false); } #endif } int64 LButton::Value() { return d->Pressed != 0; } void LButton::Value(int64 i) { d->Pressed = (int)i; Invalidate(); } void LButton::OnCreate() { } void LButton::OnAttach() { LResources::StyleElement(this); OnStyleChange(); LView::OnAttach(); if (d->WantsDefault) { d->WantsDefault = false; if (GetWindow()) GetWindow()->_Default = this; } } void LButton::SetPreferredSize(int x, int y) { LRect r = GetPos(); int Ix = d->Image ? d->Image->X() : 0; int Iy = d->Image ? d->Image->Y() : 0; int Cx = d->TxtSz.X() + Ix + (d->TxtSz.X() && d->Image ? LTableLayout::CellSpacing : 0); int Cy = MAX(d->TxtSz.Y(), Iy); r.SetSize((x > 0 ? x : Cx + Overhead.x), (y > 0 ? y : Cy + Overhead.y)); SetPos(r); } bool LButton::OnLayout(LViewLayoutInfo &Inf) { LPoint Dpi(96, 96); auto Css = GetCss(); auto Font = GetFont(); LCssTools Tools(Css, Font); auto c = GetClient(); auto TxtMin = d->GetMin(); auto TxtMax = d->GetMax(); auto Wnd = GetWindow(); if (Wnd) Dpi = Wnd->GetDpi(); double Scale = (double)Dpi.x / 96.0; LRect DefaultPad(Scale*Overhead.x/2, Scale*Overhead.y/2, Scale*Overhead.x/2, Scale*Overhead.y/2); LRect Pad = Tools.GetPadding(c, &DefaultPad), Border = Tools.GetBorder(c); if (!Inf.Width.Min) { int BaseX = Pad.x1 + Pad.x2 + Border.x1 + Border.x2; int ImgX = d->Image ? d->Image->X() + 4/*img->text spacer*/ : 0; LCss::Len MinX, MaxX; if (Css) { MinX = Css->MinWidth(); MaxX = Css->MaxWidth(); } Inf.Width.Min = MinX.IsValid() ? MinX.ToPx(c.X(), Font) : BaseX + ImgX + TxtMin.x; Inf.Width.Max = MaxX.IsValid() ? MaxX.ToPx(c.X(), Font) : BaseX + ImgX + TxtMax.x; /* LgiTrace("%i.Layout.Btn.x = %i, %i valid=%i,%i c=%s, base=%i, img=%i\n", GetId(), Inf.Width.Min, Inf.Width.Max, MinX.IsValid(), MaxX.IsValid(), c.GetStr(), BaseX, ImgX); */ } else { int BaseY = Pad.y1 + Pad.y2 + Border.y1 + Border.y2; int ImgY = d->Image ? d->Image->Y() : 0; LCss::Len MinY, MaxY; if (Css) { MinY = Css->MinHeight(); MaxY = Css->MaxHeight(); } Inf.Height.Min = MinY.IsValid() ? MinY.ToPx(c.Y(), Font) : BaseY + MAX(ImgY, TxtMin.y); Inf.Height.Max = MaxY.IsValid() ? MaxY.ToPx(c.Y(), Font) : BaseY + MAX(ImgY, TxtMax.y); // LgiTrace("%i.Layout.Btn.y = %i, %i\n", GetId(), Inf.Height.Min, Inf.Height.Max); } return true; } #endif diff --git a/src/haiku/App.cpp b/src/haiku/App.cpp --- a/src/haiku/App.cpp +++ b/src/haiku/App.cpp @@ -1,1095 +1,1095 @@ #include #include #include #include #include #include #include #include "lgi/common/Lgi.h" #include "lgi/common/SkinEngine.h" #include "lgi/common/Array.h" #include "lgi/common/Variant.h" #include "lgi/common/Token.h" #include "lgi/common/FontCache.h" #include "AppPriv.h" #define DEBUG_MSG_TYPES 0 #define DEBUG_HND_WARNINGS 0 #define IDLE_ALWAYS 0 LString LgiArgsAppPath; //////////////////////////////////////////////////////////////// struct OsAppArgumentsPriv { ::LArray Ptr; ~OsAppArgumentsPriv() { Ptr.DeleteArrays(); } }; OsAppArguments::OsAppArguments(int args, const char **arg) { d = new OsAppArgumentsPriv; for (int i=0; iPtr.Add(NewStr(arg[i])); } Args = d->Ptr.Length(); Arg = &d->Ptr[0]; } OsAppArguments::~OsAppArguments() { DeleteObj(d); } bool OsAppArguments::Get(const char *Name, const char **Val) { if (!Name) return false; for (int i=0; iPtr.DeleteArrays(); if (!CmdLine) return; for (char *s = CmdLine; *s; ) { while (*s && strchr(WhiteSpace, *s)) s++; if (*s == '\'' || *s == '\"') { char delim = *s++; char *e = strchr(s, delim); if (e) d->Ptr.Add(NewStr(s, e - s)); else break; s = e + 1; } else { char *e = s; while (*e && !strchr(WhiteSpace, *e)) e++; d->Ptr.Add(NewStr(s, e-s)); s = e; } } Args = d->Ptr.Length(); Arg = &d->Ptr[0]; } OsAppArguments &OsAppArguments::operator =(OsAppArguments &a) { d->Ptr.DeleteArrays(); for (int i=0; iPtr.Add(NewStr(a.Arg[i])); } Args = d->Ptr.Length(); Arg = &d->Ptr[0]; return *this; } //////////////////////////////////////////////////////////////// #if HAS_SHARED_MIME #include "GFilterUtils.h" #include "mime-types.h" class LSharedMime : public LLibrary { public: LSharedMime() : #ifdef _DEBUG LLibrary("libsharedmime1d") #else LLibrary("libsharedmime1") #endif { } DynFunc0(int, mimetypes_init); DynFunc1(const char*, mimetypes_set_default_type, const char *, default_type); DynFunc2(const char*, mimetypes_get_file_type, const char*, pathname, mimetypes_flags, flags); DynFunc2(const char*, mimetypes_get_data_type, const void*, data, int, length); DynFunc3(bool, mimetypes_decode, const char *, type, char **, media_type, char **, sub_type); DynFunc2(char *, mimetypes_convert_filename, const char *, pathname, const char *, mime_type); DynFunc3(bool, mimetypes_add_mime_dir, const char *, path, bool, high_priority, bool, rescan); DynFunc2(const char *, mimetypes_get_type_info, const char *, mime_type, const char *, lang); }; #endif #if 1 ///////////////////////////////////////////////////////////////////////////// // // Attempts to cleanup and call drkonqi to process the crash // void LgiCrashHandler(int Sig) { // Don't get into an infinite loop signal(SIGSEGV, SIG_DFL); #ifndef _MSC_VER // Our pid int MyPid = getpid(); printf("LgiCrashHandler trigger MyPid=%i\n", MyPid); #endif exit(-1); } #endif ///////////////////////////////////////////////////////////////////////////// #ifndef XK_Num_Lock #define XK_Num_Lock 0xff7f #endif #ifndef XK_Shift_Lock #define XK_Shift_Lock 0xffe6 #endif #ifndef XK_Caps_Lock #define XK_Caps_Lock 0xffe5 #endif struct Msg { LViewI *v; int m; LMessage::Param a, b; void Set(LViewI *V, int M, LMessage::Param A, LMessage::Param B) { v = V; m = M; a = A; b = B; } }; // Out of thread messages... must lock before access. class LMessageQue : public LMutex { public: typedef ::LArray MsgArray; LMessageQue() : LMutex("LMessageQue") { } MsgArray *Lock(const char *file, int line) { if (!LMutex::Lock(file, line)) return NULL; return &q; } operator bool() { return q.Length() > 0; } private: MsgArray q; } MsgQue; ///////////////////////////////////////////////////////////////////////////// LApp *TheApp = NULL; LSkinEngine *LApp::SkinEngine; LMouseHook *LApp::MouseHook; LApp::LApp(OsAppArguments &AppArgs, const char *name, LAppArguments *Args) : OsApplication(AppArgs.Args, AppArgs.Arg) { TheApp = this; LgiArgsAppPath = AppArgs.Arg[0]; Name(name); d = new LAppPrivate(this); if (LIsRelativePath(LgiArgsAppPath)) { char Cwd[MAX_PATH_LEN]; getcwd(Cwd, sizeof(Cwd)); LMakePath(Cwd, sizeof(Cwd), Cwd, LgiArgsAppPath); LgiArgsAppPath = Cwd; } int WCharSz = sizeof(wchar_t); #if defined(_MSC_VER) LAssert(WCharSz == 2); ::LFile::Path Dlls(LgiArgsAppPath); Dlls--; SetDllDirectoryA(Dlls); #else LAssert(WCharSz == 4); #endif #ifdef _MSC_VER SetEnvironmentVariable(_T("GTK_CSD"), _T("0")); #else setenv("GTK_CSD", "0", true); #endif // We want our printf's NOW! setvbuf(stdout,(char *)NULL,_IONBF,0); // print mesgs immediately. // Setup the file and graphics sub-systems d->FileSystem = new LFileSystem; d->GdcSystem = new GdcDevice; SetAppArgs(AppArgs); srand(LCurrentTime()); LColour::OnChange(); MouseHook = new LMouseHook; d->GetConfig(); // System font setup LFontType SysFontType; if (SysFontType.GetSystemFont("System")) { SystemNormal = SysFontType.Create(); if (SystemNormal) SystemNormal->Transparent(true); SystemBold = SysFontType.Create(); if (SystemBold) { SystemBold->Bold(true); SystemBold->Transparent(true); SystemBold->Create(); } } else { printf("%s:%i - Couldn't get system font setting.\n", __FILE__, __LINE__); } if (!SystemNormal) { LgiMsg(0, "Error: Couldn't create system font.", "Lgi Error: LApp::LApp", MB_OK); LExitApp(); } if (!GetOption("noskin")) { extern LSkinEngine *CreateSkinEngine(LApp *App); SkinEngine = CreateSkinEngine(this); } } LApp::~LApp() { DeleteObj(AppWnd); DeleteObj(SystemNormal); DeleteObj(SystemBold); DeleteObj(SkinEngine); DeleteObj(MouseHook); DeleteObj(d->FileSystem); DeleteObj(d->GdcSystem); DeleteObj(LFontSystem::Me); DeleteObj(d); TheApp = 0; } LApp *LApp::ObjInstance() { return TheApp; } bool LApp::IsOk() { bool Status = #ifndef __clang__ (this != 0) && #endif (d != 0); LAssert(Status); return Status; } LMouseHook *LApp::GetMouseHook() { return MouseHook; } int LApp::GetMetric(LSystemMetric Metric) { switch (Metric) { case LGI_MET_DECOR_X: return 8; case LGI_MET_DECOR_Y: return 8 + 19; case LGI_MET_DECOR_CAPTION: return 19; default: break; } return 0; } LViewI *LApp::GetFocus() { // GtkWidget *w = gtk_window_get_focus(GtkWindow *window); return NULL; } OsThread LApp::_GetGuiThread() { return d->GuiThread; } OsThreadId LApp::GetGuiThreadId() { return d->GuiThreadId; } OsProcessId LApp::GetProcessId() { #ifdef WIN32 return GetCurrentProcessId(); #else return getpid(); #endif } OsAppArguments *LApp::GetAppArgs() { return IsOk() ? &d->Args : 0; } void LApp::SetAppArgs(OsAppArguments &AppArgs) { if (IsOk()) { d->Args = AppArgs; } } bool LApp::InThread() { OsThreadId Me = GetCurrentThreadId(); OsThreadId Gui = GetGuiThreadId(); // printf("Me=%i Gui=%i\n", Me, Gui); return Gui == Me; } bool LApp::Run(OnIdleProc IdleCallback, void *IdleParam) { if (!InThread()) { printf("%s:%i - Error: Out of thread.\n", _FL); return false; } printf("Running main loop...\n"); d->Run(); printf("Main loop finished.\n"); return true; } bool LApp::Yield() { return false; } void LApp::Exit(int Code) { if (Code) { // hard exit ::exit(Code); } else { // soft exit printf("Quitting main loop...\n"); if (d->Lock()) { d->Quit(); d->Unlock(); } } } void LApp::OnUrl(const char *Url) { if (AppWnd) AppWnd->OnUrl(Url); } void LApp::OnReceiveFiles(::LArray &Files) { if (AppWnd) AppWnd->OnReceiveFiles(Files); else LAssert(!"You probably want to set 'AppWnd' before calling LApp::Run... maybe."); } const char *LApp::GetArgumentAt(int n) { return n >= 0 && n < d->Args.Args ? NewStr(d->Args.Arg[n]) : 0; } bool LApp::GetOption(const char *Option, char *Dest, int DestLen) { ::LString Buf; if (GetOption(Option, Buf)) { if (Dest) { if (DestLen > 0) { strcpy_s(Dest, DestLen, Buf); } else return false; } return true; } return false; } bool LApp::GetOption(const char *Option, ::LString &Buf) { if (IsOk() && Option) { int OptLen = strlen(Option); for (int i=1; iArgs.Args; i++) { auto *a = d->Args.Arg[i]; if (!a) continue; if (strchr("-/\\", a[0])) { if (strnicmp(a+1, Option, OptLen) == 0) { const char *Arg = NULL; if (strlen(a+1+OptLen) > 0) { Arg = a + 1 + OptLen; } else if (i < d->Args.Args - 1) { Arg = d->Args.Arg[i + 1]; } if (Arg) { if (strchr("\'\"", *Arg)) { char Delim = *Arg++; char *End = strchr(Arg, Delim); if (End) { auto Len = End-Arg; if (Len > 0) Buf.Set(Arg, Len); else return false; } else return false; } else { Buf = Arg; } } return true; } } } } return false; } void LApp::OnCommandLine() { ::LArray Files; for (int i=1; iArgs; i++) { auto a = GetAppArgs()->Arg[i]; if (LFileExists(a)) { Files.Add(NewStr(a)); } } // call app if (Files.Length() > 0) { OnReceiveFiles(Files); } // clear up Files.DeleteArrays(); } ::LString LApp::GetFileMimeType(const char *File) { ::LString Status; char Full[MAX_PATH_LEN] = ""; if (!LFileExists(File)) { // Look in the path LToken p(getenv("PATH"), LGI_PATH_SEPARATOR); for (int i=0; i 0) Status = s.SplitDelimit(":").Last().Strip(); } return Status; } } #endif return Status; } -bool LApp::GetAppsForMimeType(char *Mime, ::LArray<::LAppInfo*> &Apps) +bool LApp::GetAppsForMimeType(char *Mime, LArray<::LAppInfo> &Apps) { // Find alternative version of the MIME type (e.g. x-type and type). char AltMime[256]; strcpy(AltMime, Mime); char *s = strchr(AltMime, '/'); if (s) { s++; int Len = strlen(s) + 1; if (strnicmp(s, "x-", 2) == 0) { memmove(s, s+2, Len - 2); } else { memmove(s+2, s, Len); s[0] = 'x'; s[1] = '-'; } } LGetAppsForMimeType(Mime, Apps); LGetAppsForMimeType(AltMime, Apps); return Apps.Length() > 0; } #if defined(LINUX) LLibrary *LApp::GetWindowManagerLib() { if (this != NULL && !d->WmLib) { char Lib[32]; WindowManager Wm = LGetWindowManager(); switch (Wm) { case WM_Kde: strcpy(Lib, "liblgikde3"); break; case WM_Gnome: strcpy(Lib, "liblgignome2"); break; default: strcpy(Lib, "liblgiother"); break; } #ifdef _DEBUG strcat(Lib, "d"); #endif d->WmLib = new LLibrary(Lib, true); if (d->WmLib) { if (d->WmLib->IsLoaded()) { Proc_LgiWmInit WmInit = (Proc_LgiWmInit) d->WmLib->GetAddress("LgiWmInit"); if (WmInit) { WmInitParams Params; // Params.Dsp = XObject::XDisplay(); Params.Args = d->Args.Args; Params.Arg = d->Args.Arg; WmInit(&Params); } // else printf("%s:%i - Failed to find method 'LgiWmInit' in WmLib.\n", __FILE__, __LINE__); } // else printf("%s:%i - couldn't load '%s.so'\n", __FILE__, __LINE__, Lib); } // else printf("%s:%i - alloc error\n", __FILE__, __LINE__); } return d->WmLib && d->WmLib->IsLoaded() ? d->WmLib : 0; } void LApp::DeleteMeLater(LViewI *v) { d->DeleteLater.Add(v); } void LApp::SetClipBoardContent(OsView Hnd, ::LVariant &v) { // Store the clipboard data we will serve d->ClipData = v; } bool LApp::GetClipBoardContent(OsView Hnd, ::LVariant &v, ::LArray &Types) { return false; } #endif LSymLookup *LApp::GetSymLookup() { return NULL; } bool LApp::IsElevated() { #ifdef WIN32 LAssert(!"What API works here?"); return false; #else return geteuid() == 0; #endif } int LApp::GetCpuCount() { return 1; } LFontCache *LApp::GetFontCache() { if (!d->FontCache) d->FontCache.Reset(new LFontCache(SystemNormal)); return d->FontCache; } #ifdef LINUX LApp::DesktopInfo::DesktopInfo(const char *file) { File = file; Dirty = false; if (File) Serialize(false); } bool LApp::DesktopInfo::Serialize(bool Write) { ::LFile f; if (Write) { ::LFile::Path p(File); p--; if (!p.Exists()) return false; } else if (!LFileExists(File)) return false; if (!f.Open(File, Write?O_WRITE:O_READ)) { LgiTrace("%s:%i - Failed to open '%s'\n", _FL, File.Get()); return false; } if (Write) { f.SetSize(0); for (unsigned i=0; i= 0) { int e = l.Find("]", ++s); if (e >= 0) { Cur = &Data.New(); Cur->Name = l(s, e - s + 1); } } else if ((s = l.Find("=")) >= 0) { if (!Cur) Cur = &Data.New(); KeyPair &kp = Cur->Values.New(); kp.Key = l(0, s).Strip(); kp.Value = l(++s, -1).Strip(); // printf("Read '%s': '%s'='%s'\n", Cur->Name.Get(), kp.Key.Get(), kp.Value.Get()); } } } return true; } LApp::DesktopInfo::Section *LApp::DesktopInfo::GetSection(const char *Name, bool Create) { for (unsigned i=0; iGet(Field, false, Dirty); if (kp) { return kp->Value; } } } return ::LString(); } bool LApp::DesktopInfo::Set(const char *Field, const char *Value, const char *Sect) { if (!Field) return false; Section *s = GetSection(Sect ? Sect : DefaultSection, true); if (!s) return false; KeyPair *kp = s->Get(Field, true, Dirty); if (!kp) return false; if (kp->Value != Value) { kp->Value = Value; Dirty = true; } return true; } LApp::DesktopInfo *LApp::GetDesktopInfo() { auto sExe = LGetExeFile(); ::LFile::Path Exe(sExe); ::LFile::Path Desktop(LSP_HOME); ::LString Leaf; Leaf.Printf("%s.desktop", Exe.Last().Get()); Desktop += ".local/share/applications"; Desktop += Leaf; const char *Ex = Exe; const char *Fn = Desktop; if (d->DesktopInfo.Reset(new DesktopInfo(Desktop))) { // Do a sanity check... ::LString s = d->DesktopInfo->Get("Name"); if (!s && Name()) d->DesktopInfo->Set("Name", Name()); s = d->DesktopInfo->Get("Exec"); if (!s || s != (const char*)sExe) d->DesktopInfo->Set("Exec", sExe); s = d->DesktopInfo->Get("Type"); if (!s) d->DesktopInfo->Set("Type", "Application"); s = d->DesktopInfo->Get("Categories"); if (!s) d->DesktopInfo->Set("Categories", "Application;"); s = d->DesktopInfo->Get("Terminal"); if (!s) d->DesktopInfo->Set("Terminal", "false"); d->DesktopInfo->Update(); } return d->DesktopInfo; } bool LApp::SetApplicationIcon(const char *FileName) { DesktopInfo *di = GetDesktopInfo(); if (!di) return false; ::LString IcoPath = di->Get("Icon"); if (IcoPath == FileName) return true; di->Set("Icon", FileName); return di->Update(); } #endif //////////////////////////////////////////////////////////////// OsApplication *OsApplication::Inst = 0; class OsApplicationPriv { public: OsApplicationPriv() { } }; OsApplication::OsApplication(int Args, const char **Arg) { Inst = this; d = new OsApplicationPriv; } OsApplication::~OsApplication() { DeleteObj(d); Inst = 0; } //////////////////////////////////////////////////////////////// int LMessage::Msg() { return what; } LMessage::Param LMessage::A() { int64 a = 0; if (FindInt64(PropA, &a) != B_OK) { int32 c = CountNames(B_ANY_TYPE); printf("%s:%i - Failed to find PropA (%i names)\n", _FL, c); for (int32 i=0; iPostEvent(what, A(), B()); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// LLocker::LLocker(BHandler *h, const char *File, int Line) { hnd = h; file = File; line = Line; } LLocker::~LLocker() { Unlock(); } bool LLocker::Lock() { if (locked) { printf("%s:%i - Locker already locked.\n", file, line); LAssert(!"Locker already locked."); return false; } if (!hnd) { // printf("%s:%i - Locker hnd is NULL.\n", file, line); return false; } while (!locked) { status_t result = hnd->LockLooperWithTimeout(1000 * 1000); if (result == B_OK) { locked = true; break; } else if (result == B_TIMED_OUT) { // Warn about failure to lock... thread_id Thread = hnd->Looper()->LockingThread(); printf("%s:%i - Warning: can't lock. Myself=%i\n", _FL, LGetCurrentThread(), Thread); } else if (result == B_BAD_VALUE) { break; } else { // Warn about error printf("%s:%i - Error from LockLooperWithTimeout = 0x%x\n", _FL, result); } } return locked; } status_t LLocker::LockWithTimeout(int64 time) { LAssert(!locked); LAssert(hnd != NULL); status_t result = hnd->LockLooperWithTimeout(time); if (result == B_OK) locked = true; return result; } void LLocker::Unlock() { if (locked) { hnd->UnlockLooper(); locked = false; } } diff --git a/src/haiku/General.cpp b/src/haiku/General.cpp --- a/src/haiku/General.cpp +++ b/src/haiku/General.cpp @@ -1,451 +1,374 @@ // Linux Implementation of General LGI functions #include #include #include #include #include +#include "MimeType.h" +#include "StringList.h" +#include "Path.h" + #define _POSIX_TIMERS #include #include "lgi/common/Lgi.h" #include "lgi/common/SubProcess.h" #include "lgi/common/TextLabel.h" #include "lgi/common/Button.h" #include "lgi/common/Token.h" #include #include #include #define DEBUG_GET_APPS_FOR_MIMETYPE 0 //////////////////////////////////////////////////////////////// -// Local helper functions -bool _lgi_check_file(char *Path) -{ - if (Path) - { - if (LFileExists(Path)) - { - // file is there - return true; - } - else - { - // shortcut? - char *e = Path + strlen(Path); - strcpy(e, ".lnk"); - if (LFileExists(Path)) - { - // resolve shortcut - char Link[256]; - if (LResolveShortcut(Path, Link, sizeof(Link))) - { - // check destination of link - if (LFileExists(Link)) - { - strcpy(Path, Link); - return true; - } - } - } - *e = 0; - } - } - - return false; -} - LString LCurrentUserName() { struct passwd *pw = getpwuid(geteuid()); if (pw) return pw->pw_name; - return ""; + return LString(); } - -void LSleep(uint32_t i) +void LSleep(uint32_t ms) { struct timespec request, remain; ZeroObj(request); ZeroObj(remain); - request.tv_sec = i / 1000; - request.tv_nsec = (i % 1000) * 1000000; + request.tv_sec = ms / 1000; + request.tv_nsec = (ms % 1000) * 1000000; - //printf("%i LSleep(%i)\n", LGetCurrentThread(), i); while (nanosleep(&request, &remain) == -1) - { request = remain; - //printf("\t%i Resleeping=%i\n", LGetCurrentThread(), request.tv_sec*1000 + request.tv_nsec/1000); - } } void _lgi_assert(bool b, const char *test, const char *file, int line) { static bool Asserting = false; if (!b && !Asserting) { Asserting = true; printf("%s:%i - Assert failed:\n%s\n", file, line, test); LString msg; msg.Printf("Assert failed: %s\n%s:%i", test, file, line); BAlert *alert = new BAlert("Assert", msg, "Abort", "Debug", "Ignore"); auto result = alert->Go(); printf("alert result=%i\n", result); switch (result) { case 0: // Abort { exit(-1); break; } case 1: // Debug { int *i = 0; *i = 0; break; } // default: Ignore.. } Asserting = false; } } //////////////////////////////////////////////////////////////////////// // Implementations LMessage CreateMsg(int m, int a, int b) { return LMessage(m, a, b); } bool LGetMimeTypeExtensions(const char *Mime, LArray &Ext) { int Start = Ext.Length(); char *e; #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"); return Ext.Length() > Start; } LString LGetFileMimeType(const char *File) { return LAppInst->GetFileMimeType(File); } bool _GetSystemFont(char *FontType, char *Font, int FontBufSize, int &PointSize) { bool Status = false; - LAssert(!"Impl me."); - return Status; } -bool LGetAppsForMimeType(const char *Mime, LArray &Apps, int Limit) +static bool MimeTypeToAppInfo(LAppInfo &app, LString MimeType) { - bool Status = false; - - char Args[MAX_PATH_LEN]; - sprintf(Args, "query default %s", Mime); - LStringPipe Output; - - LLanguage *CurLang = LGetLanguageId(); - char LangName[64]; - sprintf_s(LangName, sizeof(LangName), "Name[%s]", CurLang ? CurLang->Id : "en"); - - #if DEBUG_GET_APPS_FOR_MIMETYPE - printf("LGetAppsForMimeType('%s', ..., %i)\nRunning 'xdg-mime %s'\n", - Mime, Limit, Args); - #endif - - LSubProcess p("xdg-mime", Args); - if (p.Start()) + BMimeType appType; + auto r = appType.SetTo(MimeType); + if (r != B_OK) { - p.Communicate(&Output); - LAutoString o(Output.NewStr()); + LgiTrace("%s:%i - appType.SetTo(%s) failed: %i.\n", _FL, MimeType.Get(), r); + return false; + } - #if DEBUG_GET_APPS_FOR_MIMETYPE - printf("Output:\n%s\n", o.Get()); - #endif - if (o) - { - char *e = o + strlen(o); - while (e > o && strchr(" \t\r\n", e[-1])) - *(--e) = 0; - - char p[MAX_PATH_LEN]; - if (LMakePath(p, sizeof(p), "/usr/share/applications", o)) - { - if (LFileExists(p)) - { - LAutoString txt(LReadTextFile(p)); - LAutoString Section; + entry_ref ref; + r = appType.GetAppHint(&ref); + if (r != B_OK) + { + LgiTrace("%s:%i - GetAppHint failed: %i\n", _FL, r); + return false; + } - #if DEBUG_GET_APPS_FOR_MIMETYPE - printf("Reading '%s', got %i bytes\n", p, strlen(txt)); - #endif + app.MimeType = MimeType; + app.Name = ref.name; + + BEntry entry(&ref); + BPath path; + if (entry.GetPath(&path) == B_OK) + app.Path = path.Path(); - if (txt) - { - LAppInfo *ai = new LAppInfo; - Apps.Add(ai); - - LToken t(txt, "\n"); - for (int i=0; iPath.Set(exe, sp-exe); - else - ai->Path = exe; - - Status = true; - } - else if (!stricmp(Var, "Name") || - !stricmp(Var, LangName)) - { - ai->Name = Value; - } - } - } - } +bool LGetAppsForMimeType(const char *Mime, LArray &Apps, int Limit) +{ + BMimeType mt(Mime); + + BMessage msg; + auto r = mt.GetSupportingApps(&msg); + if (r != B_OK) + { + printf("%s:%i - GetSupportingApps for %s failed: %i.\n", _FL, Mime, r); + return false; + } + + BStringList lst; + r = msg.FindStrings("applications", &lst); + if (r != B_OK) + { + printf("%s:%i - No apps string list: %i.\n", _FL, r); + return false; + } + + for (int i=0; iName.Get(), ai->Path.Get()); - #endif - } - else LgiTrace("%s:%i - Can't read from '%s'\n", _FL, p); - } - else LgiTrace("%s:%i - '%s' doesn't exist.", _FL, p); - } - else LgiTrace("%s:%i - Failed to create path.\n", _FL); - } - else LgiTrace("%s:%i - No output from xdg-mime\n", _FL); + auto &app = Apps.New(); + if (!MimeTypeToAppInfo(app, s.String())) + Apps.PopLast(); } - else LgiTrace("%s:%i - Failed to execute xdg-mime\n", _FL); - return Status; + return true; } LString LGetAppForMimeType(const char *Mime) { - LString App; - LArray Apps; - if (LGetAppsForMimeType(Mime, Apps, 1)) - App = Apps[0]->Path.Get(); - Apps.DeleteObjects(); - return App; + BMimeType mt(Mime); + + char app[B_MIME_TYPE_LENGTH+1] = {}; + auto r = mt.GetPreferredApp(app); + if (r != B_OK) + { + printf("%s:%i - GetPreferredApp for %s failed: %i.\n", _FL, Mime, r); + return false; + } + + LAppInfo info; + if (!MimeTypeToAppInfo(info, app)) + return false; + + // printf("LGetAppForMimeType(%s)=%s\n", Mime, info.Path.Get()); + return info.Path; } int LRand(int Limit) { return rand() % Limit; } LString LErrorCodeToString(uint32_t ErrorCode) { LString e = strerror(ErrorCode); if (!e) e.Printf("UnknownError(%i)", ErrorCode); return e; } bool LExecute(const char *File, const char *Args, const char *Dir, LString *Error) { if (File) { bool IsUrl = false; char App[MAX_PATH_LEN] = ""; if (strnicmp(File, "http://", 7) == 0 || strnicmp(File, "https://", 8) == 0) { IsUrl = true; LGetAppForMimeType("text/html", App, sizeof(App)); } else { struct stat f; char Path[MAX_PATH_LEN]; // see if the file is executable bool InPath = false; bool Ok = stat(File, &f) == 0; if (Ok) { strcpy_s(Path, sizeof(Path), File); } else { // look in the path InPath = true; LToken p(getenv("PATH"), LGI_PATH_SEPARATOR); for (int i=0; iPrintf("'%s' doesn't exist.\n", File); } if (App[0]) { bool FileAdded = false; LString AppPath; LString EscFile = LString::Escape(File, -1, " &"); LString a = App; int Pos; while ((Pos = a.Find("%")) >= 0) { char *s = a.Get() + Pos; printf("a='%s'\n", a.Get()); switch (s[1]) { case 'f': case 'F': { a = a(0,Pos) + EscFile + a(Pos+2,-1); FileAdded = true; break; } case 'u': case 'U': { if (IsUrl) a = a(0,Pos) + EscFile + a(Pos+2,-1); else a = a(0,Pos) + LString("file:") + EscFile + a(Pos+2,-1); FileAdded = true; break; } default: { // we don't understand this command a = a(0,Pos) + a(Pos+2,-1); break; } } printf("a='%s'\n", a.Get()); } if (!FileAdded) { a += " "; a += EscFile; } a += " > /dev/null 2> /dev/null &"; int e; if (Dir) chdir(Dir); printf("a=\n%s\n", a.Get()); if (e = system(a)) { if (Error) *Error = LErrorCodeToString(errno); return false; } return true; } } return false; } bool LPlaySound(const char *FileName, int ASync) { LAssert(!"Impl me."); return false; } 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,1164 +1,1164 @@ #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); 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(WhiteSpace, *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(WhiteSpace, *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); } LAppInfo *a = new LAppInfo; if (a) { Apps[Apps.Length()] = a; a->Params = LString(In).Strip(); a->Path = p.NewGStr(); 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 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()); char *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(); Apps.DeleteObjects(); 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(char *s) { HKEY Root = 0; #define TestKey(Name) \ if (strncmp(s, #Name, strlen(#Name)) == 0) \ { \ Root = Name; \ } TestKey(HKEY_CLASSES_ROOT) else TestKey(HKEY_CURRENT_CONFIG) else TestKey(HKEY_CURRENT_USER) else TestKey(HKEY_LOCAL_MACHINE) else TestKey(HKEY_USERS) #undef TestKey return Root; } bool LRegKey::AssertOnError = true; LRegKey::LRegKey(bool WriteAccess, char *Key, ...) { char Buffer[1025]; k = 0; s[0] = 0; 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; LONG ret = RegOpenKeyExA(Root, SubKey, 0, WriteAccess ? KEY_ALL_ACCESS : KEY_READ, &k); if (ret != ERROR_SUCCESS && ret != ERROR_FILE_NOT_FOUND) { DWORD err = GetLastError(); if (AssertOnError) LAssert(!"RegOpenKeyEx failed"); } } } LRegKey::~LRegKey() { if (k) RegCloseKey(k); } bool LRegKey::IsOk() { return k != NULL; } bool LRegKey::Create() { bool Status = false; if (!k && KeyName) { char *Sub = strchr(KeyName, '\\'); if (Sub) { LONG Ret = RegCreateKeyA(Root, Sub+1, &k); if (Ret == ERROR_SUCCESS) { Status = IsOk(); } else { DWORD err = GetLastError(); if (AssertOnError) LAssert(!"RegCreateKey failed"); } } } return Status; } char *LRegKey::Name() { return KeyName; } bool LRegKey::DeleteValue(char *Name) { if (k) { if (RegDeleteValueA(k, Name) == ERROR_SUCCESS) { return true; } else { DWORD Err = GetLastError(); LAssert(!"RegDeleteValue failed"); } } return false; } bool LRegKey::DeleteKey() { bool Status = false; if (k) { char *n = strchr(KeyName, '\\'); if (n++) { RegCloseKey(k); k = 0; HKEY Root = GetRootKey(KeyName); int Ret = RegDeleteKeyA(Root, n); Status = Ret == ERROR_SUCCESS; if (!Status) { if (AssertOnError) LAssert(!"RegDeleteKey failed."); } KeyName.Empty(); } } return false; } char *LRegKey::GetStr(const char *Name) { if (!k) { LAssert(!"No key to read from."); return NULL; } DWORD Size = sizeof(s), Type; LONG Ret = RegQueryValueExA(k, Name, 0, &Type, (uchar*)s, &Size); if (Ret != 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; LONG Ret = RegQueryValueExA(k, Name, 0, &Type, NULL, &Size); if (Ret != ERROR_SUCCESS) goto OnError; { LString Tmp((char*)NULL, Size); Ret = RegQueryValueExA(k, Name, 0, &Type, (LPBYTE)Tmp.Get(), &Size); if (Ret != 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; } auto r = 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, r); if (r != 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; LSTATUS r = RegQueryValueExA(k, Name, 0, &Type, (uchar*)&Value, &Size); return r == ERROR_SUCCESS; } bool LRegKey::SetInt(const char *Name, uint32_t Value) { if (!k) { LgiTrace("%s:%i - No key name.\n", _FL); return false; } auto r = RegSetValueExA(k, Name, 0, REG_DWORD, (uchar*)&Value, sizeof(Value)); LOG_WRITE("RegSetValueExA(%s,%s,%i)=%i\n", KeyName.Get(), Name, Value, r); if (r == ERROR_SUCCESS) return true; LgiTrace("%s:%i - RegSetValueExA(%s) returned error: %x.\n", _FL, Name, r); return false; } bool LRegKey::GetBinary(char *Name, void *&Ptr, int &Len) { DWORD Size = 0, Type; if (k && RegQueryValueExA(k, Name, 0, &Type, 0, &Size) == ERROR_SUCCESS) { Len = Size; Ptr = new uchar[Len]; return RegQueryValueExA(k, Name, 0, &Type, (uchar*)Ptr, &Size) == ERROR_SUCCESS; } return false; } bool LRegKey::SetBinary(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; LSTATUS Status; while ((Status = 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 (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 void LAssertDlg(LString Msg, std::function Callback) { LAlert a(LAppInst ? LAppInst->AppWnd : NULL, "Assert Failed", Msg, "Abort", "Debug", "Ignore"); a.SetAppModal(); a.DoModal([Callback](auto d, auto code) { if (Callback) Callback(code); }); } #endif void _lgi_assert(bool b, const char *test, const char *file, int line) { static bool Asserting = false; if (!b) { #ifdef LGI_STATIC assert(b); #else if (Asserting || !LAppInst || !LSysFont) { // Woah boy... LgiTrace("%s:%i - Assert: %s failed.\n", file, line, test); } else { Asserting = true; printf("%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 Asserting = false; } #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 = GetCurrentThreadId(); 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 = GetCurrentThreadId(); 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; }