diff --git a/Ide/Code/FindSymbol.cpp b/Ide/Code/FindSymbol.cpp --- a/Ide/Code/FindSymbol.cpp +++ b/Ide/Code/FindSymbol.cpp @@ -1,681 +1,680 @@ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/List.h" #include "lgi/common/Token.h" #include "lgi/common/EventTargetThread.h" #include "lgi/common/TextFile.h" #include "LgiIde.h" #include "FindSymbol.h" #include "ParserCommon.h" #if 1 #include "lgi/common/ParseCpp.h" #endif #include "resdefs.h" #define MSG_TIME_MS 1000 #define DEBUG_FIND_SYMBOL 0 #define DEBUG_NO_THREAD 1 // #define DEBUG_FILE "FileSelect.cpp" int SYM_FILE_SENT = 0; class FindSymbolDlg : public LDialog { // AppWnd *App; LList *Lst; FindSymbolSystem *Sys; public: FindSymResult Result; FindSymbolDlg(LViewI *parent, FindSymbolSystem *sys); ~FindSymbolDlg(); int OnNotify(LViewI *v, LNotification n); void OnCreate(); bool OnViewKey(LView *v, LKey &k); LMessage::Result OnEvent(LMessage *m); }; int ScoreCmp(FindSymResult **a, FindSymResult **b) { return (*b)->Score - (*a)->Score; } #define USE_HASH 1 struct FindSymbolSystemPriv : public LEventTargetThread { struct FileSyms { LString Path; int Platforms; LString::Array *Inc; LArray Defs; bool IsSource; bool IsHeader; bool IsPython; bool IsJavascript; bool Parse(LAutoWString Source, bool Debug) { IsSource = false; IsHeader = false; IsPython = false; IsJavascript = false; Defs.Length(0); bool Status = false; char *Ext = LGetExtension(Path); if (Ext) { IsSource = !_stricmp(Ext, "c") || !_stricmp(Ext, "cpp") || !_stricmp(Ext, "cxx"); IsHeader = !_stricmp(Ext, "h") || !_stricmp(Ext, "hpp") || !_stricmp(Ext, "hxx"); IsPython = !_stricmp(Ext, "py"); IsJavascript = !_stricmp(Ext, "js"); if (IsSource || IsHeader) Status = BuildCppDefnList(Path, Source, Defs, DefnNone, Debug); else if (IsJavascript) Status = BuildJsDefnList(Path, Source, Defs, DefnNone, Debug); else if (IsPython) Status = BuildPyDefnList(Path, Source, Defs, DefnNone, Debug); } return Status; } }; int hApp; int MissingFiles = 0; LArray IncPaths; #if USE_HASH LHashTbl, FileSyms*> Files; #else LArray Files; #endif int Tasks = 0; uint64 MsgTs = 0; bool DoingProgress = false; FindSymbolSystemPriv(int appSinkHnd) : LEventTargetThread("FindSymbolSystemPriv"), hApp(appSinkHnd) { } ~FindSymbolSystemPriv() { // End the thread... EndThread(); // Clean up mem Files.DeleteObjects(); } void Log(const char *Fmt, ...) { va_list Arg; va_start(Arg, Fmt); LString s; s.Printf(Arg, Fmt); va_end(Arg); if (s.Length()) PostThreadEvent(hApp, M_APPEND_TEXT, (LMessage::Param)NewStr(s), AppWnd::BuildTab); } #if !USE_HASH int GetFileIndex(LString &Path) { for (unsigned i=0; iPath) return i; } return -1; } #endif bool AddFile(LString Path, int Platforms) { bool Debug = false; #ifdef DEBUG_FILE if ((Debug = Path.Find(DEBUG_FILE) >= 0)) printf("%s:%i - AddFile(%s)\n", _FL, Path.Get()); #endif // Already added? #if USE_HASH FileSyms *f = Files.Find(Path); if (f) { if (Platforms && f->Platforms == 0) f->Platforms = Platforms; return true; } #else int Idx = GetFileIndex(Path); if (Idx >= 0) { if (Platforms && Files[Idx]->Platforms == 0) Files[Idx]->Platforms = Platforms; return true; } FileSyms *f; #endif if (!LFileExists(Path)) { Log("Missing '%s'\n", Path.Get()); MissingFiles++; return false; } LAssert(!LIsRelativePath(Path)); // Setup the file sym data... f = new FileSyms; if (!f) return false; f->Path = Path; f->Platforms = Platforms; f->Inc = IncPaths.Length() ? IncPaths.Last() : NULL; #if USE_HASH Files.Add(Path, f); #else Files.Add(f); #endif // Parse for headers... LTextFile Tf; if (!Tf.Open(Path, O_READ) || Tf.GetSize() < 4) { LgiTrace("%s:%i - Error: LTextFile.Open(%s) failed.\n", _FL, Path.Get()); return false; } LAutoString Source = Tf.Read(); LArray Headers; LArray EmptyInc; if (BuildHeaderList(Source, Headers, f->Inc ? *f->Inc : EmptyInc, false)) { for (auto h: Headers) AddFile(h, 0); } Headers.DeleteArrays(); // Parse for symbols... #ifdef DEBUG_FILE if (Debug) printf("%s:%i - About to parse '%s'.\n", _FL, f->Path.Get()); #endif return f->Parse(LAutoWString(Utf8ToWide(Source)), Debug); } bool ReparseFile(LString Path) { #if USE_HASH FileSyms *f = Files.Find(Path); int Platform = f ? f->Platforms : 0; #else int Idx = GetFileIndex(Path); int Platform = Idx >= 0 ? Files[Idx]->Platforms : 0; #endif if (!RemoveFile(Path)) return false; return AddFile(Path, Platform); } bool RemoveFile(LString Path) { #if USE_HASH FileSyms *f = Files.Find(Path); if (!f) return false; Files.Delete(Path); delete f; #else int Idx = GetFileIndex(Path); if (Idx < 0) return false; delete Files[Idx]; Files.DeleteAt(Idx); #endif return true; } LMessage::Result OnEvent(LMessage *Msg) { if (IsCancelled()) return -1; switch (Msg->Msg()) { case M_FIND_SYM_REQUEST: { LAutoPtr Req((FindSymRequest*)Msg->A()); - bool AllPlatforms = (bool)Msg->B(); + int Platforms = Msg->B(); if (Req && Req->SinkHnd >= 0) { LString::Array p = Req->Str.SplitDelimit(" \t"); if (p.Length() == 0) break; LArray ClassMatches; LArray HdrMatches; LArray SrcMatches; // For each file... #if USE_HASH for (auto it : Files) { FileSyms *fs = it.value; #else for (unsigned f=0; fPath.Find(DEBUG_FILE) >= 0; if (Debug) printf("%s:%i - Searching '%s' with %i syms...\n", _FL, fs->Path.Get(), (int)fs->Defs.Length()); #endif // Check platforms... - if (!AllPlatforms && - (fs->Platforms & PLATFORM_CURRENT) == 0) + if ((fs->Platforms & Platforms) == 0) { continue; } // For each symbol... for (unsigned i=0; iDefs.Length(); i++) { DefnInfo &Def = fs->Defs[i]; #ifdef DEBUG_FILE if (Debug) printf("%s:%i - '%s'\n", _FL, Def.Name.Get()); #endif // For each search term... bool Match = true; int ScoreSum = 0; for (unsigned n=0; nScore = ScoreSum; r->File = Def.File.Get(); r->Symbol = Def.Name.Get(); r->Line = Def.Line; if (Def.Type == DefnClass) ClassMatches.Add(r); else if (fs->IsHeader) HdrMatches.Add(r); else SrcMatches.Add(r); } } } } } ClassMatches.Sort(ScoreCmp); Req->Results.Add(ClassMatches); Req->Results.Add(HdrMatches); Req->Results.Add(SrcMatches); int Hnd = Req->SinkHnd; PostObject(Hnd, M_FIND_SYM_REQUEST, Req); } break; } case M_FIND_SYM_FILE: { LAutoPtr Params((FindSymbolSystem::SymFileParams*)Msg->A()); if (Params) { LString::Array Mime = LGetFileMimeType(Params->File).Split("/"); if (!Mime[0].Equals("image")) { if (Params->Action == FindSymbolSystem::FileAdd) AddFile(Params->File, Params->Platforms); else if (Params->Action == FindSymbolSystem::FileRemove) RemoveFile(Params->File); else if (Params->Action == FindSymbolSystem::FileReparse) ReparseFile(Params->File); } } SYM_FILE_SENT--; break; } case M_FIND_SYM_INC_PATHS: { LAutoPtr Paths((LString::Array*)Msg->A()); if (Paths) IncPaths.Add(Paths.Release()); break; } default: { LAssert(!"Implement handler for message."); break; } } auto Now = LCurrentTime(); // printf("Msg->Msg()=%i " LPrintfInt64 " %i\n", Msg->Msg(), MsgTs, (int)GetQueueSize()); if (Now - MsgTs > MSG_TIME_MS) { MsgTs = Now; DoingProgress = true; int Remaining = (int)(Tasks - GetQueueSize()); if (Remaining > 0) Log("FindSym: %i of %i (%.1f%%)\n", Remaining, Tasks, (double)Remaining * 100.0 / MAX(Tasks, 1)); } else if (GetQueueSize() == 0 && MsgTs) { if (DoingProgress) { Log("FindSym: Done.\n"); DoingProgress = false; } if (MissingFiles > 0) { Log("(%i files are missing)\n", MissingFiles); } MsgTs = 0; Tasks = 0; } return 0; } }; int AlphaCmp(LListItem *a, LListItem *b, NativeInt d) { return stricmp(a->GetText(0), b->GetText(0)); } FindSymbolDlg::FindSymbolDlg(LViewI *parent, FindSymbolSystem *sys) { Lst = NULL; Sys = sys; SetParent(parent); if (LoadFromResource(IDD_FIND_SYMBOL)) { MoveToCenter(); LViewI *f; if (GetViewById(IDC_STR, f)) f->Focus(true); GetViewById(IDC_RESULTS, Lst); } } FindSymbolDlg::~FindSymbolDlg() { } void FindSymbolDlg::OnCreate() { RegisterHook(this, LKeyEvents); } bool FindSymbolDlg::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; } } return false; } LMessage::Result FindSymbolDlg::OnEvent(LMessage *m) { switch (m->Msg()) { case M_FIND_SYM_REQUEST: { LAutoPtr Req((FindSymRequest*)m->A()); if (Req) { LString Str = GetCtrlName(IDC_STR); if (Str == Req->Str) { Lst->Empty(); List Ls; LString s; int CommonPathLen = 0; for (unsigned i=0; iResults.Length(); i++) { FindSymResult *r = Req->Results[i]; if (i) { char *a = s.Get(); char *a_end = strrchr(a, DIR_CHAR); char *b = r->File.Get(); char *b_end = strrchr(b, DIR_CHAR); int Common = 0; while ( *a && a <= a_end && *b && b <= b_end && ToLower(*a) == ToLower(*b)) { Common++; a++; b++; } if (i == 1) CommonPathLen = Common; else CommonPathLen = MIN(CommonPathLen, Common); } else s = r->File; } for (unsigned i=0; iResults.Length(); i++) { FindSymResult *r = Req->Results[i]; LListItem *it = new LListItem; if (it) { LString Ln; Ln.Printf("%i", r->Line); it->SetText(r->File.Get() + CommonPathLen, 0); it->SetText(Ln, 1); it->SetText(r->Symbol, 2); Ls.Insert(it); } } Lst->Insert(Ls); Lst->ResizeColumnsToContent(); } } break; } } return LDialog::OnEvent(m); } int FindSymbolDlg::OnNotify(LViewI *v, LNotification n) { switch (v->GetId()) { case IDC_STR: { if (n.Type != LNotifyReturnKey) { auto Str = v->Name(); if (Str && strlen(Str) > 2) { // Create a search Sys->Search(AddDispatch(), Str, true); } } break; } case IDC_RESULTS: { if (n.Type == LNotifyItemDoubleClick) { // Fall throu } else break; } case IDOK: { if (Lst) { LListItem *i = Lst->GetSelected(); if (i) { Result.File = i->GetText(0); Result.Line = atoi(i->GetText(1)); } } EndModal(true); break; } case IDCANCEL: { EndModal(false); break; } } return LDialog::OnNotify(v, n); } /////////////////////////////////////////////////////////////////////////// FindSymbolSystem::FindSymbolSystem(int AppHnd) { d = new FindSymbolSystemPriv(AppHnd); } FindSymbolSystem::~FindSymbolSystem() { delete d; } void FindSymbolSystem::OpenSearchDlg(LViewI *Parent, std::function Callback) { FindSymbolDlg *Dlg = new FindSymbolDlg(Parent, this); Dlg->DoModal([Dlg, Callback](auto d, auto code) { if (Callback) Callback(Dlg->Result); delete Dlg; }); } bool FindSymbolSystem::SetIncludePaths(LString::Array &Paths) { LString::Array *a = new LString::Array; if (!a) return false; *a = Paths; return d->PostEvent(M_FIND_SYM_INC_PATHS, (LMessage::Param)a); } bool FindSymbolSystem::OnFile(const char *Path, SymAction Action, int Platforms) { if (d->Tasks == 0) d->MsgTs = LCurrentTime(); d->Tasks++; LAutoPtr Params(new FindSymbolSystem::SymFileParams); Params->File = Path; Params->Action = Action; Params->Platforms = Platforms; if (d->PostObject(d->GetHandle(), M_FIND_SYM_FILE, Params)) { SYM_FILE_SENT++; } return false; } -void FindSymbolSystem::Search(int ResultsSinkHnd, const char *SearchStr, bool AllPlat) +void FindSymbolSystem::Search(int ResultsSinkHnd, const char *SearchStr, int Platforms) { FindSymRequest *Req = new FindSymRequest(ResultsSinkHnd); if (Req) { Req->Str = SearchStr; - d->PostEvent(M_FIND_SYM_REQUEST, (LMessage::Param)Req, (LMessage::Param)AllPlat); + d->PostEvent(M_FIND_SYM_REQUEST, (LMessage::Param)Req, (LMessage::Param)Platforms); } } diff --git a/Ide/Code/FindSymbol.h b/Ide/Code/FindSymbol.h --- a/Ide/Code/FindSymbol.h +++ b/Ide/Code/FindSymbol.h @@ -1,122 +1,122 @@ #ifndef _FIND_SYMBOL_H_ #define _FIND_SYMBOL_H_ #include "lgi/common/EventTargetThread.h" struct FindSymResult { LString Symbol, File; int Line; int Score; FindSymResult(const FindSymResult &c) { *this = c; } FindSymResult(const char *f = NULL, int line = 0) { Score = 0; File = f; Line = line; } FindSymResult &operator =(const FindSymResult &c) { Score = c.Score; Symbol = c.Symbol; File = c.File; Line = c.Line; return *this; } int Class() { if (Score > 0) return 0; char *e = LGetExtension(File); if (!e) return 3; if (!_stricmp(e, "h") || !_stricmp(e, "hpp") || !_stricmp(e, "hxx")) return 1; return 2; } // Sort symbol results by: int Compare(FindSymResult *r) { // Score first... if (Score != r->Score) return r->Score - Score; // Then headers... int c1 = Class(); int c2 = r->Class(); if (c1 != c2) return c1 - c2; // Then by file name... int c = _stricmp(File, r->File); if (c) return c; // Then by line number... return Line - r->Line; } }; struct FindSymRequest { int SinkHnd; LString Str; LArray Results; FindSymRequest(int sinkhnd) { SinkHnd = sinkhnd; } ~FindSymRequest() { Results.DeleteObjects(); } }; class FindSymbolSystem { struct FindSymbolSystemPriv *d; public: enum SymAction { FileAdd, FileRemove, FileReparse, FileRemoveAll, }; struct SymFileParams { SymAction Action; LString File; int Platforms; }; FindSymbolSystem(int AppHnd); ~FindSymbolSystem(); bool SetIncludePaths(LString::Array &Paths); bool OnFile(const char *Path, SymAction Action, int Platforms); void OpenSearchDlg(LViewI *Parent, std::function Callback); /// This function searches the database for symbols and returns /// the results as a M_FIND_SYM_REQUEST message. - void Search(int ResultsSinkHnd, const char *SearchStr, bool AllPlat); + void Search(int ResultsSinkHnd, const char *SearchStr, int Platforms); }; #endif \ No newline at end of file diff --git a/Ide/Code/IdeDoc.cpp b/Ide/Code/IdeDoc.cpp --- a/Ide/Code/IdeDoc.cpp +++ b/Ide/Code/IdeDoc.cpp @@ -1,2132 +1,2127 @@ #include #include #include "lgi/common/Lgi.h" #include "lgi/common/Token.h" #include "lgi/common/Net.h" #include "lgi/common/ClipBoard.h" #include "lgi/common/DisplayString.h" #include "lgi/common/ScrollBar.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Edit.h" #include "lgi/common/List.h" #include "lgi/common/PopupList.h" #include "lgi/common/TableLayout.h" #include "lgi/common/EventTargetThread.h" #include "lgi/common/CheckBox.h" #include "lgi/common/Http.h" #include "lgi/common/Menu.h" #include "lgi/common/FileSelect.h" #include "LgiIde.h" #include "ProjectNode.h" #include "SpaceTabConv.h" #include "DocEdit.h" #include "IdeDocPrivate.h" const char *Untitled = "[untitled]"; // static const char *White = " \r\t\n"; #define USE_OLD_FIND_DEFN 1 #define POPUP_WIDTH 700 // px #define POPUP_HEIGHT 350 // px enum { IDM_COPY_FILE = 1100, IDM_COPY_PATH, IDM_BROWSE }; int FileNameSorter(char **a, char **b) { char *A = strrchr(*a, DIR_CHAR); char *B = strrchr(*b, DIR_CHAR); return stricmp(A?A:*a, B?B:*b); } EditTray::EditTray(LTextView3 *ctrl, IdeDoc *doc) { Ctrl = ctrl; Doc = doc; Line = Col = 0; FuncBtn.ZOff(-1, -1); SymBtn.ZOff(-1, -1); int Ht = LSysFont->GetHeight() + 6; AddView(FileSearch = new LEdit(IDC_FILE_SEARCH, 0, 0, EDIT_CTRL_WIDTH, Ht)); AddView(FuncSearch = new LEdit(IDC_METHOD_SEARCH, 0, 0, EDIT_CTRL_WIDTH, Ht)); AddView(SymSearch = new LEdit(IDC_SYMBOL_SEARCH, 0, 0, EDIT_CTRL_WIDTH, Ht)); - AddView(AllPlatforms = new LCheckBox(IDC_ALL_PLATFORMS, 0, 0, 20, Ht, "All Platforms")); } EditTray::~EditTray() { } void EditTray::GotoSearch(int CtrlId, char *InitialText) { if (FileSearch && FileSearch->GetId() == CtrlId) { FileSearch->Name(InitialText); FileSearch->Focus(true); if (InitialText) FileSearch->SendNotify(LNotifyDocChanged); } if (FuncSearch && FuncSearch->GetId() == CtrlId) { FuncSearch->Name(InitialText); FuncSearch->Focus(true); if (InitialText) FuncSearch->SendNotify(LNotifyDocChanged); } if (SymSearch && SymSearch->GetId() == CtrlId) { SymSearch->Name(InitialText); SymSearch->Focus(true); if (InitialText) SymSearch->SendNotify(LNotifyDocChanged); } } void EditTray::OnCreate() { AttachChildren(); } int MeasureText(const char *s) { LDisplayString Ds(LSysFont, s); return Ds.X(); } #define HEADER_BTN_LABEL "h" #define FUNCTION_BTN_LABEL "{ }" #define SYMBOL_BTN_LABEL "s" void EditTray::OnPosChange() { LLayoutRect c(this, 2); c.Left(FileBtn, MeasureText(HEADER_BTN_LABEL)+10); if (FileSearch) c.Left(FileSearch, EDIT_CTRL_WIDTH); c.x1 += 8; c.Left(FuncBtn, MeasureText(FUNCTION_BTN_LABEL)+10); if (FuncSearch) c.Left(FuncSearch, EDIT_CTRL_WIDTH); c.x1 += 8; c.Left(SymBtn, MeasureText(SYMBOL_BTN_LABEL)+10); if (SymSearch) c.Left(SymSearch, EDIT_CTRL_WIDTH); c.x1 += 8; - if (AllPlatforms) - { - LViewLayoutInfo Inf; - AllPlatforms->OnLayout(Inf); - c.Left(AllPlatforms, Inf.Width.Max); - } - c.Remaining(TextMsg); } void EditTray::OnPaint(LSurface *pDC) { LRect c = GetClient(); pDC->Colour(L_MED); pDC->Rectangle(); LSysFont->Colour(L_TEXT, L_MED); LSysFont->Transparent(true); LString s; s.Printf("Cursor: %i,%i", Col, Line + 1); { LDisplayString ds(LSysFont, s); ds.Draw(pDC, TextMsg.x1, TextMsg.y1 + ((c.Y()-TextMsg.Y())/2), &TextMsg); } LRect f = FileBtn; LThinBorder(pDC, f, DefaultRaisedEdge); { LDisplayString ds(LSysFont, HEADER_BTN_LABEL); ds.Draw(pDC, f.x1 + 4, f.y1); } f = FuncBtn; LThinBorder(pDC, f, DefaultRaisedEdge); { LDisplayString ds(LSysFont, FUNCTION_BTN_LABEL); ds.Draw(pDC, f.x1 + 4, f.y1); } f = SymBtn; LThinBorder(pDC, f, DefaultRaisedEdge); { LDisplayString ds(LSysFont, SYMBOL_BTN_LABEL); ds.Draw(pDC, f.x1 + 4, f.y1); } } bool EditTray::Pour(LRegion &r) { LRect *c = FindLargest(r); if (c) { LRect n = *c; SetPos(n); return true; } return false; } void EditTray::OnHeaderList(LMouse &m) { // Header list button LArray Paths; if (Doc->BuildIncludePaths(Paths, PlatformCurrent, false)) { LArray Headers; if (Doc->BuildHeaderList(Ctrl->NameW(), Headers, Paths)) { // Sort them.. Headers.Sort(FileNameSorter); LSubMenu *s = new LSubMenu; if (s) { // Construct the menu LHashTbl, int> Map; int DisplayLines = GdcD->Y() / LSysFont->GetHeight(); if (Headers.Length() > (0.9 * DisplayLines)) { LArray Letters[26]; LArray Other; for (int i=0; i 1) { char *First = LGetLeaf(Letters[i][0]); char *Last = LGetLeaf(Letters[i].Last()); char Title[256]; sprintf_s(Title, sizeof(Title), "%s - %s", First, Last); LSubMenu *sub = s->AppendSub(Title); if (sub) { for (int n=0; n 0); sub->AppendItem(LGetLeaf(h), Id, true); } } } else if (Letters[i].Length() == 1) { char *h = Letters[i][0]; int Id = Map.Find(h); LAssert(Id > 0); s->AppendItem(LGetLeaf(h), Id, true); } } if (Other.Length() > 0) { for (int n=0; n 0); s->AppendItem(LGetLeaf(h), Id, true); } } } else { for (int i=0; i 0) s->AppendItem(LGetLeaf(h), Id, true); else LgiTrace("%s:%i - Failed to get id for '%s' (map.len=%i)\n", _FL, h, Map.Length()); } if (!Headers.Length()) { s->AppendItem("(none)", 0, false); } } // Show the menu LPoint p(m.x, m.y); PointToScreen(p); int Goto = s->Float(this, p.x, p.y, true); if (Goto > 0) { char *File = Headers[Goto-1]; if (File) { // Open the selected file Doc->GetProject()->GetApp()->OpenFile(File); } } DeleteObj(s); } } // Clean up memory Headers.DeleteArrays(); } else { LgiTrace("%s:%i - No include paths set.\n", _FL); } } void EditTray::OnFunctionList(LMouse &m) { LArray Funcs; if (BuildDefnList(Doc->GetFileName(), (char16*)Ctrl->NameW(), Funcs, DefnNone /*DefnFunc | DefnClass*/)) { LSubMenu s; LArray a; int ScreenHt = GdcD->Y(); int ScreenLines = ScreenHt / LSysFont->GetHeight(); float Ratio = ScreenHt ? (float)(LSysFont->GetHeight() * Funcs.Length()) / ScreenHt : 0.0f; bool UseSubMenus = Ratio > 0.9f; int Buckets = UseSubMenus ? (int)(ScreenLines * 0.9) : 1; int BucketSize = MAX(2, (int)Funcs.Length() / Buckets); LSubMenu *Cur = NULL; for (unsigned n=0; nType != DefnEnumValue) { for (char *k = i->Name; *k && o < Buf+sizeof(Buf)-8; k++) { if (*k == '&') { *o++ = '&'; *o++ = '&'; } else if (*k == '\t') { *o++ = ' '; } else { *o++ = *k; } } *o++ = 0; a[n] = i; if (UseSubMenus) { if (!Cur || n % BucketSize == 0) { LString SubMsg; SubMsg.Printf("%s...", Buf); Cur = s.AppendSub(SubMsg); } if (Cur) Cur->AppendItem(Buf, n+1, true); } else { s.AppendItem(Buf, n+1, true); } } } LPoint p(m.x, m.y); PointToScreen(p); int Goto = s.Float(this, p.x, p.y, true); if (Goto) { DefnInfo *Info = a[Goto-1]; if (Info) { Ctrl->SetLine(Info->Line); } } } else { LgiTrace("%s:%i - No functions in input.\n", _FL); } } void EditTray::OnSymbolList(LMouse &m) { LAutoString s(Ctrl->GetSelection()); if (s) { LAutoWString sw(Utf8ToWide(s)); if (sw) { #if USE_OLD_FIND_DEFN List Matches; Doc->FindDefn(sw, Ctrl->NameW(), Matches); #else LArray Matches; Doc->GetApp()->FindSymbol(s, Matches); #endif LSubMenu *s = new LSubMenu; if (s) { // Construct the menu int n=1; #if USE_OLD_FIND_DEFN for (auto Def: Matches) { char m[512]; char *d = strrchr(Def->File, DIR_CHAR); sprintf(m, "%s (%s:%i)", Def->Name.Get(), d ? d + 1 : Def->File.Get(), Def->Line); s->AppendItem(m, n++, true); } #else for (int i=0; iAppendItem(m, n++, true); } #endif if (!Matches.Length()) { s->AppendItem("(none)", 0, false); } // Show the menu LPoint p(m.x, m.y); PointToScreen(p); int Goto = s->Float(this, p.x, p.y, true); if (Goto) { #if USE_OLD_FIND_DEFN DefnInfo *Def = Matches[Goto-1]; #else FindSymResult *Def = &Matches[Goto-1]; #endif { // Open the selected symbol if (Doc->GetProject() && Doc->GetProject()->GetApp()) { AppWnd *App = Doc->GetProject()->GetApp(); IdeDoc *Doc = App->OpenFile(Def->File); if (Doc) { Doc->SetLine(Def->Line, false); } else { char *f = Def->File; LgiTrace("%s:%i - Couldn't open doc '%s'\n", _FL, f); } } else { LgiTrace("%s:%i - No project / app ptr.\n", _FL); } } } DeleteObj(s); } } } else { LSubMenu *s = new LSubMenu; if (s) { s->AppendItem("(No symbol currently selected)", 0, false); LPoint p(m.x, m.y); PointToScreen(p); s->Float(this, p.x, p.y, true); DeleteObj(s); } } } void EditTray::OnMouseClick(LMouse &m) { if (m.Left() && m.Down()) { if (FileBtn.Overlap(m.x, m.y)) { OnHeaderList(m); } else if (FuncBtn.Overlap(m.x, m.y)) { OnFunctionList(m); } else if (SymBtn.Overlap(m.x, m.y)) { OnSymbolList(m); } } } class ProjMethodPopup : public LPopupList { AppWnd *App; public: LArray All; ProjMethodPopup(AppWnd *app, LViewI *target) : LPopupList(target, PopupAbove, POPUP_WIDTH) { App = app; } LString ToString(DefnInfo *Obj) { return Obj->Name; } void OnSelect(DefnInfo *Obj) { App->GotoReference(Obj->File, Obj->Line, false); } bool Name(const char *s) { LString InputStr = s; LString::Array p = InputStr.SplitDelimit(" \t"); LArray Matching; for (unsigned i=0; iName, p[n])) { Match = false; break; } } if (Match) Matching.Add(Def); } return SetItems(Matching); } int OnNotify(LViewI *Ctrl, LNotification n) { if (Lst && Ctrl == Edit && (n.Type == LNotifyValueChanged || n.Type == LNotifyDocChanged)) { Name(Edit->Name()); } return LPopupList::OnNotify(Ctrl, n); } }; class ProjSymPopup : public LPopupList { AppWnd *App; IdeDoc *Doc; int CommonPathLen; public: LArray All; ProjSymPopup(AppWnd *app, IdeDoc *doc, LViewI *target) : LPopupList(target, PopupAbove, POPUP_WIDTH) { App = app; Doc = doc; CommonPathLen = 0; } void FindCommonPathLength() { LString s; for (unsigned i=0; iFile.Get(); char *b_end = strrchr(b, DIR_CHAR); int Common = 0; while ( *a && a <= a_end && *b && b <= b_end && ToLower(*a) == ToLower(*b)) { Common++; a++; b++; } if (i == 1) CommonPathLen = Common; else CommonPathLen = MIN(CommonPathLen, Common); } else s = All[i]->File; } } LString ToString(FindSymResult *Obj) { LString s; s.Printf("%s:%i - %s", CommonPathLen < Obj->File.Length() ? Obj->File.Get() + CommonPathLen : Obj->File.Get(), Obj->Line, Obj->Symbol.Get()); return s; } void OnSelect(FindSymResult *Obj) { App->GotoReference(Obj->File, Obj->Line, false); } int OnNotify(LViewI *Ctrl, LNotification n) { if (Lst && Ctrl == Edit && (n.Type == LNotifyValueChanged || n.Type == LNotifyDocChanged)) { // Kick off search... LString s = Ctrl->Name(); - int64 AllPlatforms = Ctrl->GetParent()->GetCtrlValue(IDC_ALL_PLATFORMS); s = s.Strip(); if (s.Length() > 2) - App->FindSymbol(Doc->AddDispatch(), s, AllPlatforms != 0); + App->FindSymbol(Doc->AddDispatch(), s); } return LPopupList::OnNotify(Ctrl, n); } }; -void FilterFiles(LArray &Perfect, LArray &Nodes, LString InputStr) +void FilterFiles(LArray &Perfect, LArray &Nodes, LString InputStr, int Platforms) { LString::Array p = InputStr.SplitDelimit(" \t"); auto InputLen = InputStr.RFind("."); if (InputLen < 0) InputLen = InputStr.Length(); LArray Partial; auto Start = LCurrentTime(); for (unsigned i=0; i 400) { break; } + ProjectNode *Pn = Nodes[i]; + if (! (Pn->GetPlatforms() & Platforms) ) + continue; + auto Fn = Pn->GetFileName(); if (Fn) { char *Dir = strchr(Fn, '/'); if (!Dir) Dir = strchr(Fn, '\\'); auto Leaf = Dir ? strrchr(Fn, *Dir) : Fn; bool Match = true; for (unsigned n=0; n { AppWnd *App; public: LArray Nodes; ProjFilePopup(AppWnd *app, LViewI *target) : LPopupList(target, PopupAbove, POPUP_WIDTH) { App = app; } LString ToString(ProjectNode *Obj) { return LString(Obj->GetFileName()); } void OnSelect(ProjectNode *Obj) { auto Fn = Obj->GetFileName(); if (LIsRelativePath(Fn)) { IdeProject *Proj = Obj->GetProject(); LAutoString Base = Proj->GetBasePath(); LFile::Path p(Base); p += Fn; App->GotoReference(p, 1, false); } else { App->GotoReference(Fn, 1, false); } } void Update(LString InputStr) { LArray Matches; - FilterFiles(Matches, Nodes, InputStr); + FilterFiles(Matches, Nodes, InputStr, App->GetPlatform()); SetItems(Matches); } int OnNotify(LViewI *Ctrl, LNotification n) { if (Lst && Ctrl == Edit && (n.Type == LNotifyValueChanged || n.Type == LNotifyDocChanged)) { auto s = Ctrl->Name(); if (ValidStr(s)) Update(s); } return LPopupList::OnNotify(Ctrl, n); } }; class LStyleThread : public LEventTargetThread { public: LStyleThread() : LEventTargetThread("StyleThread") { } LMessage::Result OnEvent(LMessage *Msg) { switch (Msg->Msg()) { } return 0; } } StyleThread; //////////////////////////////////////////////////////////////////////////////////////////// IdeDocPrivate::IdeDocPrivate(IdeDoc *d, AppWnd *a, NodeSource *src, const char *file) : NodeView(src), LMutex("IdeDocPrivate.Lock") { FilePopup = NULL; MethodPopup = NULL; SymPopup = NULL; App = a; Doc = d; Project = 0; FileName = file; LFontType Font, *Use = 0; if (Font.Serialize(App->GetOptions(), OPT_EditorFont, false)) { Use = &Font; } Doc->AddView(Edit = new DocEdit(Doc, Use)); Doc->AddView(Tray = new EditTray(Edit, Doc)); } void IdeDocPrivate::OnDelete() { IdeDoc *Temp = Doc; DeleteObj(Temp); } void IdeDocPrivate::UpdateName() { char n[MAX_PATH_LEN+30]; LString Dsp = GetDisplayName(); char *File = Dsp; #if MDI_TAB_STYLE char *Dir = File ? strrchr(File, DIR_CHAR) : NULL; if (Dir) File = Dir + 1; #endif strcpy_s(n, sizeof(n), File ? File : Untitled); if (Edit->IsDirty()) { strcat(n, " (changed)"); } Doc->Name(n); } LString IdeDocPrivate::GetDisplayName() { if (nSrc) { auto Fn = nSrc->GetFileName(); if (Fn) { if (stristr(Fn, "://")) { LUri u(nSrc->GetFileName()); if (u.sPass) { u.sPass = "******"; } return u.ToString(); } else if (*Fn == '.') { return nSrc->GetFullPath(); } } return LString(Fn); } return LString(FileName); } bool IdeDocPrivate::IsFile(const char *File) { LString Mem; char *f = NULL; if (nSrc) { Mem = nSrc->GetFullPath(); f = Mem; } else { f = FileName; } if (!f) return false; LToken doc(f, DIR_STR); LToken in(File, DIR_STR); ssize_t in_pos = (ssize_t)in.Length() - 1; ssize_t doc_pos = (ssize_t)doc.Length() - 1; while (in_pos >= 0 && doc_pos >= 0) { char *i = in[in_pos--]; char *d = doc[doc_pos--]; if (!i || !d) { return false; } if (!strcmp(i, ".") || !strcmp(i, "..")) { continue; } if (stricmp(i, d)) { return false; } } return true; } const char *IdeDocPrivate::GetLocalFile() { if (nSrc) { if (nSrc->IsWeb()) return nSrc->GetLocalCache(); auto fp = nSrc->GetFullPath(); if (_stricmp(fp.Get()?fp.Get():"", Buffer.Get()?Buffer.Get():"")) Buffer = fp; return Buffer; } return FileName; } void IdeDocPrivate::SetFileName(const char *f) { nSrc = NULL; FileName = f; Edit->IsDirty(true); } bool IdeDocPrivate::Load() { bool Status = false; if (nSrc) { Status = nSrc->Load(Edit, this); } else if (FileName) { if (LFileExists(FileName)) { Status = Edit->Open(FileName); } else LgiTrace("%s:%i - '%s' doesn't exist.\n", _FL, FileName.Get()); } if (Status) ModTs = GetModTime(); return Status; } bool IdeDocPrivate::Save() { bool Status = false; if (nSrc) { Status = nSrc->Save(Edit, this); } else if (FileName) { Status = Edit->Save(FileName); if (!Status) { const char *Err = Edit->GetLastError(); LgiMsg(App, "%s", AppName, MB_OK, Err ? Err : "$unknown_error"); } OnSaveComplete(Status); } else { Edit->IsDirty(false); } if (Status) ModTs = GetModTime(); return Status; } void IdeDocPrivate::OnSaveComplete(bool Status) { if (Status) Edit->IsDirty(false); UpdateName(); ProjectNode *Node = dynamic_cast(nSrc); if (Node) { auto Full = nSrc->GetFullPath(); App->OnNode(Full, Node, FindSymbolSystem::FileReparse); } } void IdeDocPrivate::CheckModTime() { if (!ModTs.IsValid()) return; LDateTime Ts = GetModTime(); if (Ts.IsValid() && Ts > ModTs) { static bool InCheckModTime = false; if (!InCheckModTime) { InCheckModTime = true; if (!Edit->IsDirty() || LgiMsg(Doc, "Do you want to reload modified file from\ndisk and lose your changes?", AppName, MB_YESNO) == IDYES) { auto Ln = Edit->GetLine(); Load(); Edit->IsDirty(false); UpdateName(); Edit->SetLine((int)Ln); } InCheckModTime = false; } } } /////////////////////////////////////////////////////////////////////////////////////////// LString IdeDoc::CurIpDoc; int IdeDoc::CurIpLine = -1; IdeDoc::IdeDoc(AppWnd *a, NodeSource *src, const char *file) { d = new IdeDocPrivate(this, a, src, file); d->UpdateName(); /* if (src || file) d->Load(); */ } IdeDoc::~IdeDoc() { d->App->OnDocDestroy(this); DeleteObj(d); } class WebBuild : public LThread { IdeDocPrivate *d; LString Uri; int64 SleepMs; LStream *Log; LCancel Cancel; public: WebBuild(IdeDocPrivate *priv, LString uri, int64 sleepMs) : LThread("WebBuild"), d(priv), Uri(uri), SleepMs(sleepMs) { Log = d->App->GetBuildLog(); Run(); } ~WebBuild() { Cancel.Cancel(); while (!IsExited()) { LSleep(1); } } int Main() { if (SleepMs > 0) { // Sleep for a number of milliseconds to allow the file to upload/save to the website uint64 Ts = LCurrentTime(); while (!Cancel.IsCancelled() && (LCurrentTime()-Ts) < SleepMs) LSleep(1); } // Download the file... LStringPipe Out; LString Error; bool r = LgiGetUri(&Cancel, &Out, &Error, Uri, NULL/*InHdrs*/, NULL/*Proxy*/); if (r) { // Parse through it and extract any errors... } else { // Show the download error in the build log... Log->Print("%s:%i - Web build download failed: %s\n", _FL, Error.Get()); } return 0; } }; bool IdeDoc::Build() { if (!d->Edit) return false; int64 SleepMs = -1; LString s = d->Edit->Name(), Uri; LString::Array Lines = s.Split("\n"); for (auto Ln : Lines) { s = Ln.Strip(); if (s.Find("//") == 0) { LString::Array p = s(2,-1).Strip().Split(":", 1); if (p.Length() == 2) { if (p[0].Equals("build-sleep")) { SleepMs = p[1].Strip().Int(); } else if (p[0].Equals("build-uri")) { Uri = p[1].Strip(); break; } } } } if (Uri) { if (d->Build && !d->Build->IsExited()) { // Already building... LStream *Log = d->App->GetBuildLog(); if (Log) Log->Print("%s:%i - Already building...\n"); return false; } return d->Build.Reset(new WebBuild(d, Uri, SleepMs)); } return false; } void IdeDoc::OnLineChange(int Line) { d->App->OnLocationChange(d->GetLocalFile(), Line); } void IdeDoc::OnMarginClick(int Line) { LString Dn = d->GetDisplayName(); d->App->ToggleBreakpoint(Dn, Line); } void IdeDoc::OnTitleClick(LMouse &m) { LMdiChild::OnTitleClick(m); if (m.IsContextMenu()) { char Full[MAX_PATH_LEN] = "", sFile[MAX_PATH_LEN] = "", sFull[MAX_PATH_LEN] = "", sBrowse[MAX_PATH_LEN] = ""; const char *Fn = GetFileName(), *Dir = NULL; IdeProject *p = GetProject(); if (Fn) { strcpy_s(Full, sizeof(Full), Fn); if (LIsRelativePath(Fn) && p) { LAutoString Base = p->GetBasePath(); if (Base) LMakePath(Full, sizeof(Full), Base, Fn); } Dir = strrchr(Full, DIR_CHAR); if (Dir) sprintf_s(sFile, sizeof(sFile), "Copy '%s'", Dir + 1); sprintf_s(sFull, sizeof(sFull), "Copy '%s'", Full); sprintf_s(sBrowse, sizeof(sBrowse), "Browse to '%s'", Dir ? Dir + 1 : Full); } LSubMenu s; s.AppendItem("Save", IDM_SAVE, d->Edit->IsDirty()); s.AppendItem("Close", IDM_CLOSE, true); if (Fn) { s.AppendSeparator(); if (Dir) s.AppendItem(sFile, IDM_COPY_FILE, true); s.AppendItem(sFull, IDM_COPY_PATH, true); s.AppendItem(sBrowse, IDM_BROWSE, true); s.AppendItem("Show In Project", IDM_SHOW_IN_PROJECT, true); } if (p) { s.AppendSeparator(); s.AppendItem("Properties", IDM_PROPERTIES, true); } m.ToScreen(); int Cmd = s.Float(this, m.x, m.y, m.Left()); switch (Cmd) { case IDM_SAVE: { SetClean(NULL); break; } case IDM_CLOSE: { if (OnRequestClose(false)) Quit(); break; } case IDM_COPY_FILE: { if (Dir) { LClipBoard c(this); c.Text(Dir + 1); } break; } case IDM_COPY_PATH: { LClipBoard c(this); c.Text(Full); break; } case IDM_BROWSE: { LBrowseToFile(Full); break; } case IDM_SHOW_IN_PROJECT: { d->App->ShowInProject(Fn); break; } case IDM_PROPERTIES: { p->ShowFileProperties(Full); break; } } } } AppWnd *IdeDoc::GetApp() { return d->App; } bool IdeDoc::IsFile(const char *File) { return File ? d->IsFile(File) : false; } bool IdeDoc::AddBreakPoint(ssize_t Line, bool Add) { if (Add) d->BreakPoints.Add(Line, true); else d->BreakPoints.Delete(Line); if (d->Edit) d->Edit->Invalidate(); return true; } void IdeDoc::GotoSearch(int CtrlId, char *InitialText) { LString File; if (CtrlId == IDC_SYMBOL_SEARCH) { // Check if the cursor is on a #include line... in which case we // should look up the header and go to that instead of looking for // a symbol in the code. if (d->Edit) { // Get current line LString Ln = (*d->Edit)[d->Edit->GetLine()]; if (Ln.Find("#include") >= 0) { LString::Array a = Ln.SplitDelimit(" \t", 1); if (a.Length() == 2) { File = a[1].Strip("\'\"<>"); InitialText = File; CtrlId = IDC_FILE_SEARCH; } } } } if (d->Tray) d->Tray->GotoSearch(CtrlId, InitialText); } #define IsVariableChar(ch) \ ( \ IsAlpha(ch) \ || \ IsDigit(ch) \ || \ strchr("-_~", ch) != NULL \ ) void IdeDoc::SearchSymbol() { if (!d->Edit || !d->Tray) { LAssert(0); return; } ssize_t Cur = d->Edit->GetCaret(); auto Txt = d->Edit->NameW(); if (Cur >= 0 && Txt != NULL) { ssize_t Start = Cur; while ( Start > 0 && IsVariableChar(Txt[Start-1])) Start--; ssize_t End = Cur; while ( Txt[End] && IsVariableChar(Txt[End])) End++; LString Word(Txt + Start, End - Start); GotoSearch(IDC_SYMBOL_SEARCH, Word); } } void IdeDoc::UpdateControl() { if (d->Edit) d->Edit->Invalidate(); } void IdeDoc::SearchFile() { GotoSearch(IDC_FILE_SEARCH, NULL); } bool IdeDoc::IsCurrentIp() { auto Fn = GetFileName(); bool DocMatch = CurIpDoc && Fn && !_stricmp(Fn, CurIpDoc); return DocMatch; } void IdeDoc::ClearCurrentIp() { CurIpDoc.Empty(); CurIpLine = -1; } void IdeDoc::SetCrLf(bool CrLf) { if (d->Edit) d->Edit->SetCrLf(CrLf); } bool IdeDoc::OpenFile(const char *File) { if (!d->Edit) return false; auto Cs = d->GetSrc() ? d->GetSrc()->GetCharset() : NULL; return d->Edit->Open(File, Cs); } void IdeDoc::SetEditorParams(int IndentSize, int TabSize, bool HardTabs, bool ShowWhiteSpace) { if (d->Edit) { d->Edit->SetIndentSize(IndentSize > 0 ? IndentSize : 4); d->Edit->SetTabSize(TabSize > 0 ? TabSize : 4); d->Edit->SetHardTabs(HardTabs); d->Edit->SetShowWhiteSpace(ShowWhiteSpace); d->Edit->Invalidate(); } } bool IdeDoc::HasFocus(int Set) { if (!d->Edit) return false; if (Set) d->Edit->Focus(Set); return d->Edit->Focus(); } void IdeDoc::SplitSelection(LString Sep) { if (!d->Edit) return; auto r = d->Edit->GetSelectionRange(); LString s = d->Edit->GetSelection(); if (!s) return; auto parts = s.SplitDelimit(Sep); auto joined = LString("\n").Join(parts); LAutoWString w(Utf8ToWide(joined)); d->Edit->DeleteSelection(); d->Edit->Insert(r.Start, w, StrlenW(w)); } void IdeDoc::JoinSelection(LString Sep) { if (!d->Edit) return; auto r = d->Edit->GetSelectionRange(); LString s = d->Edit->GetSelection(); if (!s) return; LAutoWString w(Utf8ToWide(s.Replace("\n", Sep))); d->Edit->DeleteSelection(); d->Edit->Insert(r.Start, w, StrlenW(w)); } void IdeDoc::EscapeSelection(bool ToEscaped) { if (!d->Edit) return; LString s = d->Edit->GetSelection(); if (!s) return; auto ReplaceSelection = [this](LString s) { auto r = d->Edit->GetSelectionRange(); LAutoWString w(Utf8ToWide(s)); d->Edit->DeleteSelection(); d->Edit->Insert(r.Start, w, StrlenW(w)); }; if (ToEscaped) { LMouse m; GetMouse(m); LString Delim = "\r\n\\"; if (m.Ctrl()) { auto Inp = new LInput(this, LString::Escape(Delim, -1, "\\"), "Delimiter chars:", "Escape"); Inp->DoModal([this, ReplaceSelection, s, Inp](auto d, auto code) { if (code) { auto Delim = LString::UnEscape(Inp->GetStr().Get(), -1); auto str = LString::Escape(s, -1, Delim); ReplaceSelection(str); } delete d; }); return; } s = LString::Escape(s, -1, Delim); } else { s = LString::UnEscape(s.Get(), -1); } ReplaceSelection(s); } void IdeDoc::ConvertWhiteSpace(bool ToTabs) { if (!d->Edit) return; LAutoString Sp( ToTabs ? SpacesToTabs(d->Edit->Name(), d->Edit->GetTabSize()) : TabsToSpaces(d->Edit->Name(), d->Edit->GetTabSize()) ); if (Sp) { d->Edit->Name(Sp); SetDirty(); } } ssize_t IdeDoc::GetLine() { return d->Edit ? d->Edit->GetLine() : -1; } void IdeDoc::SetLine(int Line, bool CurIp) { if (CurIp) { LString CurDoc = GetFileName(); if (ValidStr(CurIpDoc) ^ ValidStr(CurDoc) || (CurIpDoc && CurDoc && strcmp(CurDoc, CurIpDoc) != 0) || Line != CurIpLine) { bool Cur = IsCurrentIp(); if (d->Edit && Cur && CurIpLine >= 0) { // Invalidate the old IP location d->Edit->InvalidateLine(CurIpLine - 1); } CurIpLine = Line; CurIpDoc = CurDoc; // LgiTrace("%s:%i - CurIpLine=%i\n", _FL, CurIpLine); d->Edit->InvalidateLine(CurIpLine - 1); } } if (d->Edit) { d->Edit->SetLine(Line); } } IdeProject *IdeDoc::GetProject() { return d->Project; } void IdeDoc::SetProject(IdeProject *p) { d->Project = p; if (d->Project->GetApp() && d->BreakPoints.Length() == 0) d->Project->GetApp()->LoadBreakPoints(this); } const char *IdeDoc::GetFileName() { return d->GetLocalFile(); } void IdeDoc::SetFileName(const char *f, bool Write) { d->SetFileName(f); if (Write) d->Edit->Save(d->GetLocalFile()); } void IdeDoc::Focus(bool f) { d->Edit->Focus(f); } LMessage::Result IdeDoc::OnEvent(LMessage *Msg) { switch (Msg->Msg()) { case M_FIND_SYM_REQUEST: { LAutoPtr Resp((FindSymRequest*)Msg->A()); if (Resp && d->SymPopup) { LViewI *SymEd; if (GetViewById(IDC_SYMBOL_SEARCH, SymEd)) { LString Input = SymEd->Name(); if (Input == Resp->Str) // Is the input string still the same? { /* The problem with this is that the user it still typing something and the cursor / focus jumps to the document now they are typing a search string into the document, which is not what they intended. if (Resp->Results.Length() == 1) { FindSymResult *r = Resp->Results[0]; d->SymPopup->Visible(false); d->App->GotoReference(r->File, r->Line, false); } else */ { d->SymPopup->All = Resp->Results; Resp->Results.Length(0); d->SymPopup->FindCommonPathLength(); d->SymPopup->SetItems(d->SymPopup->All); d->SymPopup->Visible(true); } } } } break; } } return LMdiChild::OnEvent(Msg); } void IdeDoc::OnPulse() { d->CheckModTime(); if (d->Lock(_FL)) { if (d->WriteBuf.Length()) { bool Pour = d->Edit->SetPourEnabled(false); for (auto s: d->WriteBuf) { LAutoWString w(Utf8ToWide(s, s.Length())); d->Edit->Insert(d->Edit->Length(), w, Strlen(w.Get())); } d->Edit->SetPourEnabled(Pour); d->WriteBuf.Empty(); d->Edit->Invalidate(); } d->Unlock(); } } LString IdeDoc::Read() { return d->Edit->Name(); } ssize_t IdeDoc::Write(const void *Ptr, ssize_t Size, int Flags) { if (d->Lock(_FL)) { d->WriteBuf.New().Set((char*)Ptr, Size); d->Unlock(); } return 0; } void IdeDoc::OnProjectChange() { DeleteObj(d->FilePopup); DeleteObj(d->MethodPopup); DeleteObj(d->SymPopup); } int IdeDoc::OnNotify(LViewI *v, LNotification n) { // printf("IdeDoc::OnNotify(%i, %i)\n", v->GetId(), f); switch (v->GetId()) { case IDC_EDIT: { switch (n.Type) { case LNotifyDocChanged: { d->UpdateName(); break; } case LNotifyCursorChanged: { if (d->Tray) { LPoint Pt; if (d->Edit->GetLineColumnAtIndex(Pt, d->Edit->GetCaret())) { d->Tray->Col = Pt.x; d->Tray->Line = Pt.y; d->Tray->Invalidate(); } } break; } default: break; } break; } case IDC_FILE_SEARCH: { if (n.Type == LNotifyEscapeKey) { d->Edit->Focus(true); break; } auto SearchStr = v->Name(); if (ValidStr(SearchStr)) { if (!d->FilePopup) { if ((d->FilePopup = new ProjFilePopup(d->App, v))) { // Populate with files from the project... // Find the root project... IdeProject *p = d->Project; while (p && p->GetParentProject()) p = p->GetParentProject(); if (p) { // Get all the nodes List All; p->GetChildProjects(All); All.Insert(p); for (auto p: All) { p->GetAllNodes(d->FilePopup->Nodes); } } } } if (d->FilePopup) { // Update list elements... d->FilePopup->OnNotify(v, n); } } else if (d->FilePopup) { DeleteObj(d->FilePopup); } break; } case IDC_METHOD_SEARCH: { if (n.Type == LNotifyEscapeKey) { printf("%s:%i Got LNotifyEscapeKey\n", _FL); d->Edit->Focus(true); break; } auto SearchStr = v->Name(); if (ValidStr(SearchStr)) { if (!d->MethodPopup) d->MethodPopup = new ProjMethodPopup(d->App, v); if (d->MethodPopup) { // Populate with symbols d->MethodPopup->All.Length(0); BuildDefnList(GetFileName(), (char16*)d->Edit->NameW(), d->MethodPopup->All, DefnFunc); // Update list elements... d->MethodPopup->OnNotify(v, n); } } else if (d->MethodPopup) { DeleteObj(d->MethodPopup); } break; } case IDC_SYMBOL_SEARCH: { if (n.Type == LNotifyEscapeKey) { printf("%s:%i Got LNotifyEscapeKey\n", _FL); d->Edit->Focus(true); break; } auto SearchStr = v->Name(); if (ValidStr(SearchStr)) { if (!d->SymPopup) d->SymPopup = new ProjSymPopup(d->App, this, v); if (d->SymPopup) d->SymPopup->OnNotify(v, n); } else if (d->SymPopup) { DeleteObj(d->SymPopup); } break; } } return 0; } void IdeDoc::SetDirty() { d->Edit->IsDirty(true); d->UpdateName(); } bool IdeDoc::GetClean() { return !d->Edit->IsDirty(); } LString IdeDoc::GetFullPath() { LAutoString Base; if (GetProject()) Base = GetProject()->GetBasePath(); LString LocalPath; if (d->GetLocalFile() && LIsRelativePath(LocalPath) && Base) { char p[MAX_PATH_LEN]; LMakePath(p, sizeof(p), Base, d->GetLocalFile()); LocalPath = p; } else { LocalPath = d->GetLocalFile(); } if (d->Project) d->Project->CheckExists(LocalPath); return LocalPath; } void IdeDoc::SetClean(std::function Callback) { static bool Processing = false; bool Status = false; if (!Processing) { Processing = true; LAutoString Base; if (GetProject()) Base = GetProject()->GetBasePath(); LString LocalPath = GetFullPath(); // printf("OnSave setup d=%p\n", d); auto OnSave = [this, Callback](bool ok) { // printf("OnSave %i d=%p\n", ok, d); if (ok) d->Save(); // printf("OnSave cb\n"); if (Callback) Callback(ok); // printf("OnSave done\n"); }; if (d->Edit->IsDirty() && !LFileExists(LocalPath)) { // We need a valid filename to save to... auto s = new LFileSelect; s->Parent(this); if (Base) s->InitialDir(Base); s->Save([this, OnSave](auto fileSel, auto ok) { // printf("Doc.Save.Cb ok=%i d=%p\n", ok, d); if (ok) d->SetFileName(fileSel->Name()); // printf("Doc.Save.Cb onsave\n", ok); OnSave(ok); // printf("Doc.Save.Cb del\n", ok); delete fileSel; }); } else OnSave(true); Processing = false; } } void IdeDoc::OnPaint(LSurface *pDC) { LMdiChild::OnPaint(pDC); #if !MDI_TAB_STYLE LRect c = GetClient(); LWideBorder(pDC, c, SUNKEN); #endif } void IdeDoc::OnPosChange() { LMdiChild::OnPosChange(); } bool IdeDoc::OnRequestClose(bool OsShuttingDown) { if (d->Edit->IsDirty()) { LString Dsp = d->GetDisplayName(); int a = LgiMsg(this, "Save '%s'?", AppName, MB_YESNOCANCEL, Dsp ? Dsp.Get() : Untitled); switch (a) { case IDYES: { SetClean([](bool ok) { if (ok) { LAssert(!"Impl close doc."); } }); // This is returned immediately... before the user has decided to save or not return false; } case IDNO: { break; } default: case IDCANCEL: { return false; } } } return true; } /* LTextView3 *IdeDoc::GetEdit() { return d->Edit; } */ bool IdeDoc::BuildIncludePaths(LArray &Paths, IdePlatform Platform, bool IncludeSysPaths) { if (!GetProject()) { LgiTrace("%s:%i - GetProject failed.\n", _FL); return false; } bool Status = GetProject()->BuildIncludePaths(Paths, true, IncludeSysPaths, Platform); if (Status) { if (IncludeSysPaths) GetApp()->GetSystemIncludePaths(Paths); } else { LgiTrace("%s:%i - GetProject()->BuildIncludePaths failed.\n", _FL); } return Status; } bool IdeDoc::BuildHeaderList(const char16 *Cpp, LArray &Headers, LArray &IncPaths) { LAutoString c8(WideToUtf8(Cpp)); if (!c8) return false; return ::BuildHeaderList(c8, Headers, IncPaths, true); } bool MatchSymbol(DefnInfo *Def, char16 *Symbol) { static char16 Dots[] = {':', ':', 0}; LBase o; o.Name(Def->Name); auto Name = o.NameW(); auto Sep = StristrW((char16*)Name, Dots); auto Start = Sep ? Sep : Name; // char16 *End = StrchrW(Start, '('); ssize_t Len = StrlenW(Symbol); char16 *Match = StristrW((char16*)Start, Symbol); if (Match) // && Match + Len <= End) { if (Match > Start && isword(Match[-1])) { return false; } char16 *e = Match + Len; if (*e && isword(*e)) { return false; } return true; } return false; } bool IdeDoc::FindDefn(char16 *Symbol, const char16 *Source, List &Matches) { if (!Symbol || !Source) { LgiTrace("%s:%i - Arg error.\n", _FL); return false; } #if DEBUG_FIND_DEFN LStringPipe Dbg; LgiTrace("FindDefn(%S)\n", Symbol); #endif LString::Array Paths; LArray Headers; if (!BuildIncludePaths(Paths, PlatformCurrent, true)) { LgiTrace("%s:%i - BuildIncludePaths failed.\n", _FL); // return false; } char Local[MAX_PATH_LEN]; strcpy_s(Local, sizeof(Local), GetFileName()); LTrimDir(Local); Paths.New() = Local; if (!BuildHeaderList(Source, Headers, Paths)) { LgiTrace("%s:%i - BuildHeaderList failed.\n", _FL); // return false; } { LArray Defns; for (int i=0; i Defns; if (BuildDefnList(h, c16, Defns, DefnNone, false )) { bool Found = false; for (unsigned n=0; n 0; } diff --git a/Ide/Code/IdeDoc.h b/Ide/Code/IdeDoc.h --- a/Ide/Code/IdeDoc.h +++ b/Ide/Code/IdeDoc.h @@ -1,79 +1,79 @@ #ifndef _IDE_DOC_H_ #define _IDE_DOC_H_ #include #include "lgi/common/Mdi.h" #include "lgi/common/TextView3.h" #include "ParserCommon.h" -extern void FilterFiles(LArray &Perfect, LArray &Nodes, LString InputStr); +extern void FilterFiles(LArray &Perfect, LArray &Nodes, LString InputStr, int Platforms); class IdeDoc : public LMdiChild, public LStream { friend class DocEdit; class IdeDocPrivate *d; static LString CurIpDoc; static int CurIpLine; public: IdeDoc(class AppWnd *a, NodeSource *src, const char *file); ~IdeDoc(); AppWnd *GetApp(); void SetProject(IdeProject *p); IdeProject *GetProject(); const char *GetFileName(); LString GetFullPath(); void SetFileName(const char *f, bool Write); void Focus(bool f) override; bool GetClean(); void SetClean(std::function Callback); void SetDirty(); bool OnRequestClose(bool OsShuttingDown) override; void OnPosChange() override; void OnPaint(LSurface *pDC) override; bool IsFile(const char *File); bool AddBreakPoint(ssize_t Line, bool Add); bool OpenFile(const char *File); void SetEditorParams(int IndentSize, int TabSize, bool HardTabs, bool ShowWhiteSpace); bool HasFocus(int Set = -1); void ConvertWhiteSpace(bool ToTabs); void EscapeSelection(bool ToEscaped); void SplitSelection(LString s); void JoinSelection(LString s); void SetCrLf(bool CrLf); ssize_t GetLine(); void SetLine(int Line, bool CurIp); static void ClearCurrentIp(); bool IsCurrentIp(); void GotoSearch(int CtrlId, char *InitialText = NULL); void SearchSymbol(); void SearchFile(); void UpdateControl(); bool Build(); // Source tools bool BuildIncludePaths(LArray &Paths, IdePlatform Platform, bool IncludeSysPaths); bool BuildHeaderList(const char16 *Cpp, LArray &Headers, LArray &IncPaths); bool FindDefn(char16 *Def, const char16 *Source, List &Matches); // Events void OnLineChange(int Line); void OnMarginClick(int Line); void OnProjectChange(); // Impl void OnTitleClick(LMouse &m) override; LMessage::Result OnEvent(LMessage *Msg) override; int OnNotify(LViewI *v, LNotification n) override; void OnPulse() override; LString Read(); ssize_t Read(void *Ptr, ssize_t Size, int Flags = 0) override { return 0; } ssize_t Write(const void *Ptr, ssize_t Size, int Flags = 0) override; }; #endif diff --git a/Ide/Code/IdeDocPrivate.h b/Ide/Code/IdeDocPrivate.h --- a/Ide/Code/IdeDocPrivate.h +++ b/Ide/Code/IdeDocPrivate.h @@ -1,92 +1,90 @@ #ifndef _IdeDocPrivate_h_ #define _IdeDocPrivate_h_ #include "lgi/common/CheckBox.h" #define EDIT_CTRL_WIDTH 200 class EditTray : public LLayout { LRect FileBtn; LEdit *FileSearch; LRect FuncBtn; LEdit *FuncSearch; LRect SymBtn; LEdit *SymSearch; - LCheckBox *AllPlatforms; - LRect TextMsg; LTextView3 *Ctrl; IdeDoc *Doc; public: int Line, Col; EditTray(LTextView3 *ctrl, IdeDoc *doc); ~EditTray(); const char *GetClass() override { return "EditTray"; } void GotoSearch(int CtrlId, char *InitialText = NULL); void OnCreate() override; void OnPosChange() override; void OnPaint(LSurface *pDC) override; bool Pour(LRegion &r) override; void OnMouseClick(LMouse &m) override; void OnHeaderList(LMouse &m); void OnFunctionList(LMouse &m); void OnSymbolList(LMouse &m); }; class IdeDocPrivate : public NodeView, public LMutex { LString FileName; LString Buffer; public: IdeDoc *Doc; AppWnd *App; IdeProject *Project; LDateTime ModTs; class DocEdit *Edit; EditTray *Tray; LHashTbl, bool> BreakPoints; class ProjFilePopup *FilePopup; class ProjMethodPopup *MethodPopup; class ProjSymPopup *SymPopup; LString::Array WriteBuf; LAutoPtr Build; IdeDocPrivate(IdeDoc *d, AppWnd *a, NodeSource *src, const char *file); void OnDelete(); void UpdateName(); LString GetDisplayName(); bool IsFile(const char *File); const char *GetLocalFile(); void SetFileName(const char *f); bool Load(); bool Save(); void OnSaveComplete(bool Status); LDateTime GetModTime() { LDateTime Ts; auto Full = nSrc ? nSrc->GetFullPath() : FileName; if (Full) { LDirectory Dir; if (Dir.First(Full, NULL)) Ts.Set(Dir.GetLastWriteTime()); } return Ts; } void CheckModTime(); }; #endif diff --git a/Ide/Code/LgiIde.cpp b/Ide/Code/LgiIde.cpp --- a/Ide/Code/LgiIde.cpp +++ b/Ide/Code/LgiIde.cpp @@ -1,4683 +1,4742 @@ #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 OPT_PLATFORM "Platform" #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); + FilterFiles(Matches, Nodes, s, App->GetPlatform()); 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; } }; ////////////////////////////////////////////////////////////////////////////////////////// const 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; + int Platform = 0; 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); + LVariant SplitPx = 270, OutPx = 250, v; + d->Options.GetValue(OPT_SPLIT_PX, SplitPx); d->Options.GetValue(OPT_OUTPUT_PX, OutPx); + if (d->Options.GetValue(OPT_PLATFORM, v)) + SetPlatform(v.CastInt32()); + else + SetPlatform(PLATFORM_CURRENT); + 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)); + d->HBox->Value(MAX(SplitPx.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()); #ifdef HAIKU WaitThread(); #endif + LVariant v; if (d->HBox) - { - LVariant v = d->HBox->Value(); - d->Options.SetValue(OPT_SPLIT_PX, v); - } + d->Options.SetValue(OPT_SPLIT_PX, v = d->HBox->Value()); if (d->Output) - { - LVariant v = d->Output->Y(); - d->Options.SetValue(OPT_OUTPUT_PX, v); - } + d->Options.SetValue(OPT_OUTPUT_PX, v = d->Output->Y()); + d->Options.SetValue(OPT_PLATFORM, v = d->Platform); 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(); } for (auto n: NeedsPulse) n->OnPulse(); } 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(""); } } +int AppWnd::GetPlatform() +{ + return d->Platform; +} + +bool AppWnd::SetPlatform(int p) +{ + if (p == 0) + p = PLATFORM_CURRENT; + + if (d->Platform == p) + return false; + + d->Platform = p; + + if (auto m = GetMenu()) + { + bool all = d->Platform == PLATFORM_ALL; + #define SET_CHK(id, flag) \ + { auto mi = m->FindItem(id); \ + if (mi) mi->Checked((d->Platform & flag) && !all); } + + SET_CHK(IDM_WIN, PLATFORM_WIN32); + SET_CHK(IDM_LINUX, PLATFORM_LINUX); + SET_CHK(IDM_MAC, PLATFORM_MAC); + SET_CHK(IDM_HAIKU, PLATFORM_HAIKU); + auto mi = m->FindItem(IDM_ALL_OS); + if (mi) mi->Checked(all); + } + + return true; +} + 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: { auto dlg = new LAbout( this, AppName, APP_VER, "\nLGI Integrated Development Environment", "icon128.png", APP_URL, "fret@memecode.com"); dlg->DoModal(NULL); 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([Inp,this](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 dlg; }); } 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; } auto Dlg = new FindInFiles(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([this, Dlg, p](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(); if (d->FindParameters->Text) d->Finder->PostEvent(FindInFilesThread::M_START_SEARCH, (LMessage::Param) new FindParams(d->FindParameters)); delete Dlg; }); } 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; } + + // + // OS Platform + // + #define HANDLE_PLATFORM_ITEM(id, flag) \ + case id: \ + { \ + auto mi = GetMenu()->FindItem(id); \ + if (!mi) break; \ + if (d->Platform == PLATFORM_ALL) d->Platform = 0; \ + if (mi->Checked()) SetPlatform(d->Platform & ~flag); \ + else SetPlatform(d->Platform | flag); \ + break; \ + } + HANDLE_PLATFORM_ITEM(IDM_WIN, PLATFORM_WIN32) + HANDLE_PLATFORM_ITEM(IDM_LINUX, PLATFORM_LINUX) + HANDLE_PLATFORM_ITEM(IDM_MAC, PLATFORM_MAC) + HANDLE_PLATFORM_ITEM(IDM_HAIKU, PLATFORM_HAIKU) + case IDM_ALL_OS: + { + SetPlatform(PLATFORM_ALL); + 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::GetOutputLog() { return d->Output->Txt[AppWnd::OutputTab]; } 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) +void AppWnd::FindSymbol(int ResultsSinkHnd, const char *Sym) { - d->FindSym->Search(ResultsSinkHnd, Sym, AllPlatforms); + d->FindSym->Search(ResultsSinkHnd, Sym, d->Platform); } 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()) { LPoint dpi = LScreenDpi(); a.AppWnd = new AppWnd; // a.AppWnd->_Dump(); a.Run(); } return 0; } diff --git a/Ide/Code/LgiIde.h b/Ide/Code/LgiIde.h --- a/Ide/Code/LgiIde.h +++ b/Ide/Code/LgiIde.h @@ -1,346 +1,347 @@ #ifndef _LGI_IDE_H_ #define _LGI_IDE_H_ // // Compiler specific macros: // // Microsoft Visual C++: _MSC_VER // // GCC: __GNUC__ // #include "lgi/common/DocView.h" #include "lgi/common/OptionsFile.h" #include "lgi/common/StringClass.h" #include "lgi/common/TextView3.h" #include "lgi/common/List.h" #include "lgi/common/Tree.h" #include "resdefs.h" #include "FindSymbol.h" #include "Debugger.h" #ifdef WIN32 #include "resource.h" #endif #define APP_VER "1.0" #define APP_URL "http://www.memecode.com/lgi/ide/" #define DEBUG_FIND_DEFN 0 #define OptFileSeparator "\n" enum IdeMessages { M_APPEND_TEXT = M_USER+200, // A=(char*)heapStr, B=(int)tabIndex M_APPEND_STR, M_START_BUILD, M_BUILD_ERR, M_BUILD_DONE, M_DEBUG_ON_STATE, M_MAKEFILES_CREATED, M_LAST_MAKEFILE_CREATED, M_SELECT_TAB, // A=(int)tabIndex /// Find symbol results message: /// LAutoPtr Req((FindSymRequest*)Msg->A()); M_FIND_SYM_REQUEST, /// Send a file to the worker thread... /// FindSymbolSystem::SymFileParams *Params = (FindSymbolSystem::SymFileParams*)Msg->A(); M_FIND_SYM_FILE, /// Send a file to the worker thread... /// LAutoPtr Paths((LString::Array*)Msg->A()); M_FIND_SYM_INC_PATHS, /// Styling is finished M_STYLING_DONE, }; #define ICON_PROJECT 0 #define ICON_DEPENDANCY 1 #define ICON_FOLDER 2 #define ICON_SOURCE 3 #define ICON_HEADER 4 #define ICON_RESOURCE 5 #define ICON_GRAPHIC 6 #define ICON_WEB 7 enum IdeIcon { CMD_NEW, CMD_OPEN, CMD_SAVE, CMD_CUT, CMD_COPY, CMD_PASTE, CMD_COMPILE, CMD_BUILD, CMD_STOP_BUILD, CMD_EXECUTE, CMD_DEBUG, CMD_PAUSE, CMD_KILL, CMD_RESTART, CMD_RUN_TO, CMD_STEP_INTO, CMD_STEP_OVER, CMD_STEP_OUT, CMD_FIND_IN_FILES, CMD_SEARCH, CMD_SAVE_ALL, }; enum IdeControls { IDC_STATIC = -1, IDC_WATCH_LIST = 700, IDC_LOCALS_LIST, IDC_DEBUG_EDIT, IDC_DEBUGGER_LOG, IDC_BUILD_LOG, IDC_OUTPUT_LOG, IDC_FIND_LOG, IDC_CALL_STACK, IDC_DEBUG_TAB, IDC_OBJECT_DUMP, IDC_MEMORY_DUMP, IDC_MEMORY_TABLE, IDC_MEM_ADDR, IDC_MEM_SIZE, IDC_MEM_ROW_LEN, IDC_MEM_HEX, IDC_REGISTERS, IDC_THREADS, IDC_EDIT, IDC_FILE_SEARCH, IDC_METHOD_SEARCH, IDC_SYMBOL_SEARCH, IDC_PROJECT_TREE, - IDC_ALL_PLATFORMS, }; enum IdeMenuCmds { IDM_CONTINUE = 900 }; enum BuildConfig { BuildDebug, BuildRelease, BuildMax }; inline const char *toString(BuildConfig c) { switch (c) { default: case BuildDebug: return "Debug"; case BuildRelease: return "Release"; } return "#err"; } #define OPT_EditorFont "EdFont" #define OPT_Jobs "Jobs" ////////////////////////////////////////////////////////////////////// // Platform stuff enum IdePlatform { PlatformCurrent = -1, PlatformWin = 0, // 0x1 PlatformLinux, // 0x2 PlatformMac, // 0x4 PlatformHaiku, // 0x8 PlatformMax, }; #define PLATFORM_WIN32 (1 << PlatformWin) #define PLATFORM_LINUX (1 << PlatformLinux) #define PLATFORM_MAC (1 << PlatformMac) #define PLATFORM_HAIKU (1 << PlatformHaiku) #define PLATFORM_ALL (PLATFORM_WIN32|PLATFORM_LINUX|PLATFORM_MAC|PLATFORM_HAIKU) #if defined(_WIN32) #define PLATFORM_CURRENT PLATFORM_WIN32 #elif defined(MAC) #define PLATFORM_CURRENT PLATFORM_MAC #elif defined(LINUX) #define PLATFORM_CURRENT PLATFORM_LINUX #elif defined(HAIKU) #define PLATFORM_CURRENT PLATFORM_HAIKU #endif extern const char *PlatformNames[]; extern const char sCurrentPlatform[]; extern const char *Untitled; extern const char SourcePatterns[]; ////////////////////////////////////////////////////////////////////// class IdeDoc; class IdeProject; extern const char *AppName; extern char *FindHeader(char *Short, LArray &Paths); extern bool BuildHeaderList(char *Cpp, LArray &Headers, LArray &IncPaths, bool Recurse); class NodeView; class NodeSource { friend class NodeView; protected: NodeView *nView; public: NodeSource() { nView = 0; } virtual ~NodeSource(); virtual LString GetFullPath() = 0; virtual bool IsWeb() = 0; virtual const char *GetFileName() = 0; virtual const char *GetLocalCache() = 0; virtual const char *GetCharset() = 0; virtual bool Load(LDocView *Edit, NodeView *Callback) = 0; virtual bool Save(LDocView *Edit, NodeView *Callback) = 0; virtual IdeProject *GetProject() = 0; }; class NodeView { friend class NodeSource; protected: NodeSource *nSrc; public: NodeView(NodeSource *s) { if ((nSrc = s)) { nSrc->nView = this; } } virtual ~NodeView(); virtual void OnDelete() = 0; virtual void OnSaveComplete(bool Status) = 0; NodeSource *GetSrc() { return nSrc; } }; class AppWnd : public LWindow { class AppWndPrivate *d; friend class AppWndPrivate; void UpdateMemoryDump(); void DumpHistory(); public: enum Channels { BuildTab, OutputTab, FindTab, FtpTab, DebugTab, ChannelMax, }; enum DebugTabs { LocalsTab, ObjectTab, WatchTab, MemoryTab, ThreadsTab, CallStackTab, RegistersTab }; LArray NeedsPulse; AppWnd(); ~AppWnd(); const char *GetClass() override { return "AppWnd"; } + int GetPlatform(); + bool SetPlatform(int p); bool IsClean(); void SaveAll(std::function Callback, bool CloseDirty = false); void CloseAll(); IdeDoc *OpenFile(const char *FileName, NodeSource *Src = 0); IdeDoc *NewDocWnd(const char *FileName, NodeSource *Src); IdeDoc *GetCurrentDoc(); IdeProject *OpenProject(const char *FileName, IdeProject *ParentProj, bool Create = false, bool Dep = false); IdeProject *RootProject(); IdeDoc *TopDoc(); IdeDoc *FocusDoc(); LTextView3 *FocusEdit(); void AppendOutput(char *Txt, Channels Channel); int OnFixBuildErrors(); void OnBuildStateChanged(bool NewState); void UpdateState(int Debugging = -1, int Building = -1); void OnReceiveFiles(LArray &Files) override; BuildConfig GetBuildMode(); LTree *GetTree(); LOptionsFile *GetOptions(); LList *GetFtpLog(); LStream *GetOutputLog(); LStream *GetBuildLog(); LStream *GetDebugLog(); IdeDoc *FindOpenFile(char *FileName); IdeDoc *GotoReference(const char *File, int Line, bool CurIp, bool WithHistory = true); - void FindSymbol(int ResultsSinkHnd, const char *Sym, bool AllPlatforms); + void FindSymbol(int ResultsSinkHnd, const char *Sym); bool GetSystemIncludePaths(LArray &Paths); bool ShowInProject(const char *Fn); bool Build(); void SeekHistory(int Offset); // Events void OnLocationChange(const char *File, int Line); int OnCommand(int Cmd, int Event, OsView Wnd) override; void OnDocDestroy(IdeDoc *Doc); void OnProjectDestroy(IdeProject *Proj); void OnProjectChange(); void OnFile(char *File, bool IsProject = false); bool OnRequestClose(bool IsClose) override; int OnNotify(LViewI *Ctrl, LNotification n) override; LMessage::Result OnEvent(LMessage *m) override; bool OnNode(const char *Path, class ProjectNode *Node, FindSymbolSystem::SymAction Action); void OnPulse() override; // Debugging support class LDebugContext *GetDebugContext(); bool ToggleBreakpoint(const char *File, ssize_t Line); bool OnBreakPoint(LDebugger::BreakPoint &b, bool Add); bool LoadBreakPoints(IdeDoc *doc); bool LoadBreakPoints(LDebugger *db); void OnDebugState(bool Debugging, bool Running); }; #include "IdeDoc.h" #include "IdeProject.h" #include "FindInFiles.h" extern void NewMemDumpViewer(AppWnd *App, const char *file = 0); extern void NewProjectFromTemplate(LViewI *parent); class SysCharSupport : public LWindow { class SysCharSupportPriv *d; public: SysCharSupport(AppWnd *app); ~SysCharSupport(); int OnNotify(LViewI *v, LNotification n); void OnPosChange(); }; #endif diff --git a/Ide/Resources/LgiIde.lr8 b/Ide/Resources/LgiIde.lr8 --- a/Ide/Resources/LgiIde.lr8 +++ b/Ide/Resources/LgiIde.lr8 @@ -1,830 +1,843 @@ + + + + + + + + + + + + + diff --git a/Ide/Resources/resdefs.h b/Ide/Resources/resdefs.h --- a/Ide/Resources/resdefs.h +++ b/Ide/Resources/resdefs.h @@ -1,172 +1,180 @@ +// Generated by LgiRes + // This file generated by LgiRes #define IDC_APPLY 1 #define IDC_CLOSE 2 #define IDD_MISSING_FILES 7 #define IDC_TABLE 8 #define IDC_MISSING 10 #define IDC_RESULTS 12 #define IDC_BROWSE 14 #define IDC_DELETE 16 #define IDC_17 17 #define IDD_FIND_IN_FILES 19 #define IDC_FILE_TYPES 23 #define IDC_DIR 25 #define IDC_SET_DIR 26 #define IDC_WHOLE_WORD 29 #define IDC_CASE 30 #define IDC_SUB_DIRS 31 #define IDC_COPY_PATH 34 #define IDC_MSG 36 #define IDC_TYPE 37 #define IDC_RENAME 43 #define IDC_44 44 #define IDC_SYM 45 #define IDC_FIX_RENAMED 48 #define IDC_CHARSET 54 #define IDD_NEW_PROJ_FROM_TEMPLATE 56 #define IDC_57 57 #define IDC_PROJ_NAME 59 #define IDC_TEMPLATES 61 #define IDD_OPTIONS 63 #define IDC_FONT 65 #define IDC_SET_FONT 66 #define IDC_FOLDER_HISTORY 69 #define IDC_TYPE_HISTORY 70 #define IDC_FOLDER 71 #define IDC_BROWSE_OUTPUT 72 #define IDC_DEFAULTS 76 #define IDD_DEP_NODE 77 #define IDC_WIN32 79 #define IDC_LINUX 80 #define IDC_MAC 81 #define IDD_CHAR 82 #define IDC_CHAR 83 #define IDS_87 87 #define IDS_88 88 #define IDC_VALUE 96 #define IDC_PATH 97 #define IDD_WEB_FOLDER 98 #define IDS_99 99 #define IDC_HOST 101 #define IDC_USERNAME 104 #define IDC_PASSWORD 105 #define IDC_WWW 107 #define IDC_NAME 111 #define IDD_FTP_FILE 112 #define IDC_FILES 113 #define IDS_114 114 #define IDS_115 115 #define IDS_116 116 #define IDC_LOG 117 #define IDS_118 118 #define IDS_121 121 #define IDD_FIND_SYMBOL 179 #define IDC_182 182 #define IDC_183 183 #define IDC_184 184 #define IDC_LINK_AGAINST 188 #define IDC_STR 192 #define IDC_SHOW_CURRENT_ONLY 195 #define IDD_PROJECT_SETTINGS 199 #define IDC_HAIKU 200 #define IDC_SEARCH 202 #define IDC_CLEAR_SEARCH 203 #define IDC_SUB_TABLE 206 #define IDC_JOBS 213 #define IDC_233 233 #define IDC_ENTIRE_SOLUTION 234 #define IDC_FIND_PROJECT_FILE 239 #define IDC_240 240 #define IDC_242 242 #define IDC_TEXT 243 #define IDM_EOL_LF 500 #define IDM_MENU_501 501 #define IDM_OPEN 502 #define IDM_SAVE_ALL 503 #define IDM_NEW 504 #define IDM_CLOSE 505 #define IDM_CLOSE_ALL 506 #define IDM_OPTIONS 507 #define IDM_RECENT_FILES 508 #define IDM_RECENT_PROJECTS 509 #define IDM_EXIT 510 #define IDM_UNDO 511 #define IDM_REDO 512 #define IDM_CUT 513 #define IDM_COPY 514 #define IDM_PASTE 515 #define IDM_GOTO 516 #define IDM_FIND 517 #define IDM_FIND_NEXT 518 #define IDM_REPLACE 519 #define IDM_NEW_PROJECT 520 #define IDM_OPEN_PROJECT 521 #define IDM_IMPORT_DSP 522 #define IDM_PROJECT_SETTINGS 523 #define IDM_CLEAN 524 #define IDM_BUILD 525 #define IDM_BUILD_ALL 526 #define IDM_NEXT_MSG 527 #define IDM_DEBUG_MODE 528 #define IDM_RUN 529 #define IDM_DEPENDS 530 #define IDM_TAB_TO_SP 531 #define IDM_SP_TO_TAB 532 #define IDM_ABOUT 533 #define IDM_HELP 534 #define IDM_SAVEAS 535 #define IDM_WINDOW_LST 536 #define IDM_COMPILE 537 #define IDM_STOP_BUILD 538 #define IDM_FIND_IN_FILES 539 #define IDM_CREATE_MAKEFILE 540 #define IDM_RELEASE_MODE 541 #define IDC_SEARCH_DIR 542 #define IDM_VALGRIND 543 #define IDC_PLATFORMS 544 #define IDM_EOL_CRLF 545 #define IDM_FIX_MISSING_FILES 546 #define IDC_LOOK_FOR 547 #define IDM_FIND_DUPE_SYM 548 #define IDM_LOOKUP_SYMBOLS 549 #define IDM_MENU_550 550 #define IDD_FILE_PROPS 551 #define IDM_LOAD_MEMDUMP 552 #define IDM_SYS_CHAR_SUPPORT 553 #define IDC_NOT_MATCH 554 #define IDC_MATCH 555 #define IDM_ESCAPE 556 #define IDM_DESCAPE 557 #define IDC_75 558 #define IDM_FIND_SYMBOL 559 #define IDM_FIND_REFERENCES 560 #define IDM_MENU_561 561 #define IDM_GOTO_SYMBOL 562 #define IDC_SETTINGS 563 #define IDC_DETAIL 564 #define IDM_MAKEFILE_PLATFORMS 565 #define IDM_MENU_566 566 #define IDM_PREV_LOCATION 567 #define IDM_NEXT_LOCATION 568 #define IDM_SAVE 569 #define IDM_START_DEBUG 570 #define IDM_PAUSE_DEBUG 571 #define IDM_STOP_DEBUG 572 #define IDM_MENU_573 573 #define IDM_ATTACH_TO_PROCESS 574 #define IDM_STEP_INTO 575 #define IDM_STEP_OVER 576 #define IDM_STEP_OUT 577 #define IDM_MENU_578 578 #define IDM_TOGGLE_BREAKPOINT 579 #define IDM_MENU_580 580 #define IDM_RESTART_DEBUGGING 581 #define IDM_RUN_TO 582 #define IDM_PREV_MSG 583 #define IDM_FIND_PROJECT_FILE 584 #define IDM_RENAME_SYM 585 #define IDM_MENU_586 586 #define IDM_SPLIT 587 #define IDM_JOIN 588 #define IDM_NEW_PROJECT_TEMPLATE 589 +#define IDM_MENU_OS 590 +#define IDM_WIN 591 +#define IDM_LINUX 592 +#define IDM_MAC 593 +#define IDM_HAIKU 594 +#define IDM_ALL_OS 595 diff --git a/ResourceEditor/Code/LgiRes_TableLayout.cpp b/ResourceEditor/Code/LgiRes_TableLayout.cpp --- a/ResourceEditor/Code/LgiRes_TableLayout.cpp +++ b/ResourceEditor/Code/LgiRes_TableLayout.cpp @@ -1,1738 +1,1739 @@ #include #include "LgiResEdit.h" #include "LgiRes_Dialog.h" #include "lgi/common/Button.h" #include "lgi/common/Variant.h" #include "lgi/common/Token.h" #include "lgi/common/TableLayout.h" #include "lgi/common/LgiRes.h" #include "lgi/common/Menu.h" #define DRAW_CELL_INDEX 0 #define DRAW_TABLE_SIZE 0 enum Cmds { IDM_ALIGN_X_MIN = 100, IDM_ALIGN_X_CTR, IDM_ALIGN_X_MAX, IDM_ALIGN_Y_MIN, IDM_ALIGN_Y_CTR, IDM_ALIGN_Y_MAX, IDM_UNMERGE, IDM_FIX_TABLE, IDM_INSERT_ROW, IDM_INSERT_COL, }; static LColour Blue(0, 30, 222); ///////////////////////////////////////////////////////////////////// struct Pair { int Pos, Size; }; void CalcCell(LArray &p, LArray &s, int Total) { int CurI = 0; for (int i=0; i T SumElements(LArray &a, ssize_t start, ssize_t end) { T Sum = 0; for (auto i=start; i<=end; i++) { Sum += a[i]; } return Sum; } void MakeSumUnity(LArray &a) { double s = SumElements(a, 0, a.Length()-1); double Diff = s - 1.0; if (Diff < 0) Diff *= -1.0; if (Diff > 0.001) { for (int i=0; i Ctrls; TableAlign AlignX; TableAlign AlignY; LAutoString Class; // CSS class for styling LAutoString Style; // CSS styles ResTableCell(CtrlTable *table, ssize_t cx, ssize_t cy) { AlignX = AlignY = AlignMin; Table = table; Selected = false; Cell.ZOff(0, 0); Cell.Offset((int)cx, (int)cy); Pos.ZOff(-1, -1); } ResTableCell() { Ctrls.DeleteObjects(); } void MoveCtrls() { for (int i=0; iView()->GetPos(); if (r.X() > Pos.X()) { r.x2 = r.x1 + Pos.X() - 1; } if (r.Y() > Pos.Y()) { r.y2 = r.y1 + Pos.Y() - 1; } if (r.x2 > Pos.x2) { r.Offset(Pos.x2 - r.x2, 0); } if (r.y2 > Pos.y2) { r.Offset(0, Pos.y2 - r.y2); } c->SetPos(r); } } } void SetPos(LRect &p) { int Dx = p.x1 - Pos.x1; int Dy = p.y1 - Pos.y1; for (int i=0; iView()->GetPos(); r.Offset(Dx, Dy); c->SetPos(r); } } Pos = p; MoveCtrls(); } bool GetVariant(const char *Name, LVariant &Value, const char *Array) { if (stricmp(Name, VAL_Span) == 0) { Value = Cell.Describe(); } else if (stricmp(Name, VAL_Children) == 0) { List c; for (int i=0; iType == GV_VOID_PTR); ResDialogCtrl *Obj = dynamic_cast((ResObject*) v->Value.Ptr); if (Obj) { Table->SetAttachCell(this); LRect r = Obj->View()->GetPos(); Table->AttachCtrl(Obj, &r); Table->SetAttachCell(0); } } } else if (stricmp(Name, VAL_HorizontalAlign) == 0) { if (Value.Str()) { for (int i=0; iAppendSub("Horizontal Align"); if (s) { s->AppendItem("Left", IDM_ALIGN_X_MIN, AlignX != AlignMin); s->AppendItem("Center", IDM_ALIGN_X_CTR, AlignX != AlignCenter); s->AppendItem("Right", IDM_ALIGN_X_MAX, AlignX != AlignMax); } s = RClick->AppendSub("Vertical Align"); if (s) { s->AppendItem("Top", IDM_ALIGN_Y_MIN, AlignY != AlignMin); s->AppendItem("Center", IDM_ALIGN_Y_CTR, AlignY != AlignCenter); s->AppendItem("Bottom", IDM_ALIGN_Y_MAX, AlignY != AlignMax); } RClick->AppendItem("Unmerge", IDM_UNMERGE, Cell.X() > 1 || Cell.Y() > 1); RClick->AppendItem("Fix Missing Cells", IDM_FIX_TABLE, true); RClick->AppendItem("Insert Row", IDM_INSERT_ROW, true); RClick->AppendItem("Insert Column", IDM_INSERT_COL, true); m.ToScreen(); switch (RClick->Float(Table, m.x, m.y)) { case IDM_ALIGN_X_MIN: AlignX = AlignMin; break; case IDM_ALIGN_X_CTR: AlignX = AlignCenter; break; case IDM_ALIGN_X_MAX: AlignX = AlignMax; break; case IDM_ALIGN_Y_MIN: AlignY = AlignMin; break; case IDM_ALIGN_Y_CTR: AlignY = AlignCenter; break; case IDM_ALIGN_Y_MAX: AlignY = AlignMax; break; case IDM_UNMERGE: Table->UnMerge(this); break; case IDM_FIX_TABLE: Table->Fix(); break; case IDM_INSERT_ROW: Table->InsertRow(Cell.y1); break; case IDM_INSERT_COL: Table->InsertCol(Cell.x1); break; } DeleteObj(RClick); } } } }; enum HandleTypes { LAddCol, LAddRow, LDeleteCol, LDeleteRow, LSizeCol, LSizeRow, LJoinCells, }; struct OpHandle : public LRect { bool Over; HandleTypes Type; int Index; ResTableCell *a, *b; OpHandle &Set(HandleTypes type, int x = -1, int y = -1) { Type = type; Over = false; Index = -1; a = b = NULL; if (x > 0 && y > 0) ZOff(x, y); return *this; } void OnPaint(LSurface *pDC) { #define OFF 1 int cx = X() >> 1; int cy = Y() >> 1; pDC->Colour(Over ? LColour::Red : Blue); pDC->Rectangle(this); pDC->Colour(LColour::White); switch (Type) { case LSizeRow: { pDC->Line(x1 + cx, y1 + OFF, x1 + cx, y2 - OFF); pDC->Line(x1 + cx - 1, y1 + OFF + 1, x1 + cx + 1, y1 + OFF + 1); pDC->Line(x1 + cx - 1, y2 - OFF - 1, x1 + cx + 1, y2 - OFF - 1); break; } case LSizeCol: { pDC->Line(x1 + OFF, y1 + cy, x2 - OFF, y1 + cy); pDC->Line(x1 + OFF + 1, y1 + cy - 1, x1 + OFF + 1, y1 + cy + 1); pDC->Line(x2 - OFF - 1, y1 + cy - 1, x2 - OFF - 1, y1 + cy + 1); break; } case LAddCol: case LAddRow: case LJoinCells: { pDC->Line(x1 + cx, y1 + OFF, x1 + cx, y2 - OFF); pDC->Line(x1 + OFF, y1 + cy, x2 - OFF, y1 + cy); break; } case LDeleteCol: case LDeleteRow: { pDC->Line(x1 + OFF, y1 + cy, x2 - OFF, y1 + cy); break; } default: LAssert(!"Impl me."); } } }; class CtrlTablePrivate { public: bool InLayout, LayoutDirty; // The cell container CtrlTable *Table; ssize_t CellX, CellY; LArray Cells; ResTableCell *AttachTo; // Column + Row sizes LArray ColSize; LArray RowSize; // Goobers for editing the cell layout LArray Handles; int DragRowSize; int DragColSize; // Methods CtrlTablePrivate(CtrlTable *t) { InLayout = false; LayoutDirty = false; Table = t; CellX = CellY = 2; AttachTo = 0; Cells[0] = new ResTableCell(t, 0, 0); Cells[1] = new ResTableCell(t, 1, 0); Cells[2] = new ResTableCell(t, 0, 1); Cells[3] = new ResTableCell(t, 1, 1); ColSize[0] = 0.5; ColSize[1] = 0.5; RowSize[0] = 0.5; RowSize[1] = 0.5; DragRowSize = DragColSize = -1; } ~CtrlTablePrivate() { Cells.DeleteObjects(); } bool GetSelected(LArray &s) { for (int i=0; iSelected) s.Add(Cells[i]); } return s.Length(); } ResTableCell *GetCellAt(int cx, int cy) { for (int i=0; iCell.Overlap(cx, cy)) { return Cells[i]; } } return 0; } void Layout(LRect c) { - auto scale = Table->GetWindow()->GetDpiScale(); + LAssert(LAppInst->AppWnd); + auto scale = LAppInst->AppWnd->GetDpiScale(); int ADD_BORDER = (int) (10.0 * scale.x + 0.5); #define CELL_SPACING 1 if (InLayout) return; InLayout = true; LayoutDirty = false; Handles.Length(0); int x, y; int BtnSize = ADD_BORDER * 2 / 3; auto &AddX = Handles.New().Set(LAddCol, BtnSize, BtnSize); auto &AddY = Handles.New().Set(LAddRow, BtnSize, BtnSize); int BtnX = c.X() - AddX.X() - 2; int BtnY = c.Y() - AddY.Y() - 2; AddX.Offset(BtnX, 0); AddY.Offset(0, BtnY); c.x2 = AddX.x2 - ADD_BORDER; c.y2 = AddY.y2 - ADD_BORDER; if (c.Valid()) { int AvailX = c.X(); int AvailY = c.Y(); LArray XPair, YPair; CalcCell(XPair, ColSize, AvailX); CalcCell(YPair, RowSize, AvailY); XPair[CellX].Pos = AvailX; for (x=0; xCell.x1 == x && Cell->Cell.y1 == y) { LRect Pos = Cell->Pos; // Set cells pixel position, with spanning int Ex = XPair[Cell->Cell.x2 + 1].Pos; int Ey = YPair[Cell->Cell.y2 + 1].Pos; Pos.ZOff(Ex - Px - 1 - CELL_SPACING, Ey - Py - 1 - CELL_SPACING); // Offset the cell into place Pos.Offset(Px, Py); Cell->SetPos(Pos); if (Cell->Selected) { // Add cell joining goobers... ResTableCell *c = GetCellAt(x + Cell->Cell.X(), y); if (c && c->Selected) { auto &j = Handles.New().Set(LJoinCells, BtnSize, BtnSize); j.a = Cell; j.b = c; j.Offset(Cell->Pos.x2 - (j.X()>>1), Cell->Pos.y1 + ((Cell->Pos.Y()-j.Y()) >> 1)); int asd=0; } c = GetCellAt(x, y + Cell->Cell.Y()); // LgiTrace("%s %i,%i+%i = %p\n", Cell->Cell.GetStr(), x, y, Cell->Cell.Y(), c); if (c && c->Selected) { auto &j = Handles.New().Set(LJoinCells, BtnSize, BtnSize); j.a = Cell; j.b = c; j.Offset(Cell->Pos.x1 + ((Cell->Pos.X()-j.X()) >> 1), Cell->Pos.y2 - (j.Y()>>1)); int asd=0; } } } LAssert(Cell->Cell.X()); x += Cell->Cell.X(); Px += Cell->Pos.X() + CELL_SPACING; } else break; } } } InLayout = false; } bool DeleteCol(int x) { bool Status = false; int OldCellX = CellX; // Delete column 'x' for (int y=0; yCell.y2 + 1; if (c->Cell.X() == 1) { Cells.Delete(c); DeleteObj(c); } else { c->Cell.x2--; } Status = true; } else break; } CellX--; double Last = ColSize[x]; ColSize.DeleteAt(x, true); for (int i=0; iCell.x1 == x && c->Cell.y1 == y) { c->Cell.Offset(-1, 0); } } } } Table->Layout(); return Status; } bool DeleteRow(int y) { bool Status = false; int x; int OldCellY = CellY; // Delete row 'y' for (x=0; xCell.x2 + 1; if (c->Cell.Y() == 1) { Cells.Delete(c); DeleteObj(c); } else { c->Cell.y2--; } Status = true; } else break; } CellY--; double Last = RowSize[y]; RowSize.DeleteAt(y, true); for (int i=0; iCell.x1 == x && c->Cell.y1 == y) { c->Cell.Offset(0, -1); } } } } Table->Layout(); return Status; } ResTableCell *GetCell(ResDialogCtrl *Ctrl) { for (int i=0; iCtrls.HasItem(Ctrl)) { return Cells[i]; } } return 0; } }; CtrlTable::CtrlTable(ResDialog *dlg, LXmlTag *load) : ResDialogCtrl(dlg, Res_Table, load) { d = new CtrlTablePrivate(this); AcceptChildren = true; } CtrlTable::~CtrlTable() { DeleteObj(d); } bool CtrlTable::GetFields(FieldTree &Fields) { bool Status = ResDialogCtrl::GetFields(Fields); LArray s; if (d->GetSelected(s) == 1) { int Id = 150; Fields.Insert(this, DATA_STR, Id++, VAL_CellClass, "Cell Class"); Fields.Insert(this, DATA_STR, Id++, VAL_CellStyle, "Cell Style", -1, true); } return Status; } bool CtrlTable::Serialize(FieldTree &Fields) { bool Status = ResDialogCtrl::Serialize(Fields); LArray s; ResTableCell *c; if (d->GetSelected(s) == 1 && ((c = s[0])) != NULL) { Fields.Serialize(this, VAL_CellClass, c->Class); Fields.Serialize(this, VAL_CellStyle, c->Style); } return Status; } void CtrlTable::EnumCtrls(List &Ctrls) { // LgiTrace("Tbl Ref=%i\n", Str->GetRef()); for (int i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; for (int n=0; nCtrls.Length(); n++) { // LgiTrace(" Ref=%i\n", c->Ctrls[n]->Str->GetRef()); c->Ctrls[n]->EnumCtrls(Ctrls); } } } bool CtrlTable::GetVariant(const char *Name, LVariant &Value, const char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case TableLayoutCols: { LStringPipe p; for (int i=0; iColSize.Length(); i++) { if (i) p.Push(","); p.Print("%.3f", d->ColSize[i]); } Value.OwnStr(p.NewStr()); break; } case TableLayoutRows: { LStringPipe p; for (int i=0; iRowSize.Length(); i++) { if (i) p.Push(","); p.Print("%.3f", d->RowSize[i]); } Value.OwnStr(p.NewStr()); break; } case TableLayoutCell: { auto Coords = LString(Array).SplitDelimit(","); if (Coords.Length() != 2) return false; Value = d->GetCellAt(Coords[0].Int(), Coords[1].Int()); break; } default: { LAssert(!"Invalid property."); return false; } } return true; } bool CtrlTable::SetVariant(const char *Name, LVariant &Value, const char *Array) { LDomProperty p = LStringToDomProp(Name); switch (p) { case TableLayoutCols: { d->Cells.DeleteObjects(); LToken t(Value.Str(), ","); d->ColSize.Length(0); for (int i=0; iColSize.Add(atof(t[i])); } MakeSumUnity(d->ColSize); d->CellX = d->ColSize.Length(); break; } case TableLayoutRows: { d->Cells.DeleteObjects(); LToken t(Value.Str(), ","); d->RowSize.Length(0); for (int i=0; iRowSize.Add(atof(t[i])); } MakeSumUnity(d->RowSize); d->CellY = d->RowSize.Length(); break; } case TableLayoutCell: { auto Coords = LString(Array).SplitDelimit(","); if (Coords.Length() != 2) return false; auto Cx = Coords[0].Int(); auto Cy = Coords[1].Int(); ResTableCell *c = new ResTableCell(this, Cx, Cy); if (!c) return false; d->Cells.Add(c); LDom **Ptr = (LDom**)Value.Value.Ptr; if (!Ptr) return false; *Ptr = c; // LgiTrace("Create cell %i,%i = %p\n", (int)Cx, (int)Cy, c); d->LayoutDirty = true; break; } case ObjStyle: { const char *s = Value.Str(); GetCss(true)->Parse(s); break; } default: { LAssert(!"Invalid property."); return false; } } return true; } LRect *CtrlTable::GetPasteArea() { for (int i=0; iCells.Length(); i++) { if (d->Cells[i]->Selected) return &d->Cells[i]->Pos; } return 0; } LRect *CtrlTable::GetChildArea(ResDialogCtrl *Ctrl) { ResTableCell *c = d->GetCell(Ctrl); if (c) return &c->Pos; return NULL; } void CtrlTable::OnChildrenChanged(LViewI *Wnd, bool Attaching) { if (Attaching) return; ResDialogCtrl *Rc = dynamic_cast(Wnd); if (Rc) { ResTableCell *c = d->GetCell(Rc); if (c) c->Ctrls.Delete(Rc); } } void CtrlTable::SetAttachCell(ResTableCell *c) { d->AttachTo = c; } bool CtrlTable::AttachCtrl(ResDialogCtrl *Ctrl, LRect *r) { if (d->AttachTo) { Layout(); if (!d->AttachTo->Ctrls.HasItem(Ctrl)) { d->AttachTo->Ctrls.Add(Ctrl); } LRect b = d->AttachTo->Pos; if (r->X() > b.X()) { r->x1 = b.x1; r->x2 = b.x2; } else if (r->x2 > b.x2) { r->Offset(b.x2 - r->x2, 0); } if (r->Y() > b.Y()) { r->y1 = b.y1; r->y2 = b.y2; } else if (r->y2 > b.y2) { r->Offset(b.y2 - r->y2, 0); } Ctrl->SetPos(*r); } else { for (int i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c) { if (c->Pos.Overlap(r->x1, r->y1)) { if (!c->Ctrls.HasItem(Ctrl)) { c->Ctrls.Add(Ctrl); } Ctrl->SetPos(*r); c->MoveCtrls(); *r = Ctrl->View()->GetPos(); break; } } } } return ResDialogCtrl::AttachCtrl(Ctrl, r); } void CtrlTable::Layout() { d->Layout(GetClient()); } void CtrlTable::OnPosChange() { Layout(); } void CtrlTable::OnPaint(LSurface *pDC) { int i; Client.Set(0, 0, X()-1, Y()-1); if (d->LayoutDirty || d->Handles.Length() == 0) d->Layout(GetClient()); pDC->Colour(Blue); for (i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c) { pDC->Box(&c->Pos); if (c->Selected) { int Op = pDC->Op(GDC_ALPHA); if (pDC->Applicator()) { pDC->Colour(Blue); pDC->Applicator()->SetVar(GAPP_ALPHA_A, 0x20); } else pDC->Colour(LColour(Blue.r(), Blue.g(), Blue.b(), 0x20)); pDC->Rectangle(c->Pos.x1 + 1, c->Pos.y1 + 1, c->Pos.x2 - 1, c->Pos.y2 - 1); pDC->Op(Op); pDC->Colour(Blue); } #if DRAW_CELL_INDEX LSysFont->Colour(Blue, 24); LSysFont->Transparent(true); char s[256]; sprintf(s, "%i,%i-%i,%i", c->Cell.x1, c->Cell.y1, c->Cell.X(), c->Cell.Y()); LSysFont->Text(pDC, c->Pos.x1 + 3, c->Pos.y1 + 1, s); #endif // LgiTrace("Drawing %i,%i = %p @ %s\n", c->Cell.x1, c->Cell.y1, c, c->Pos.GetStr()); } } for (auto &h: d->Handles) h.OnPaint(pDC); #if DRAW_TABLE_SIZE LSysFont->Colour(Blue, 24); LSysFont->Transparent(true); char s[256]; sprintf(s, "Cells: %i,%i", d->CellX, d->CellY); LSysFont->Text(pDC, 3, 24, s); #endif ResDialogCtrl::OnPaint(pDC); } void CtrlTable::OnMouseMove(LMouse &m) { bool Change = false; for (auto &h: d->Handles) { bool Over = h.Overlap(m.x, m.y); if (Over != h.Over) { h.Over = Over; Change = true; } } if (Change) Invalidate(); if (IsCapturing()) { LArray p; int Fudge = 6; if (d->DragRowSize >= 0) { // Adjust RowSize[d->DragRowSize] and RowSize[d->DragRowSize+1] to // center on the mouse y location int AvailY = GetClient().Y(); CalcCell(p, d->RowSize, AvailY); int BothPx = p[d->DragRowSize].Size + p[d->DragRowSize+1].Size; int PxOffset = m.y - p[d->DragRowSize].Pos + Fudge; PxOffset = limit(PxOffset, 2, BothPx - 2); double Frac = (double) PxOffset / BothPx; double Total = d->RowSize[d->DragRowSize] + d->RowSize[d->DragRowSize+1]; d->RowSize[d->DragRowSize] = Frac * Total; d->RowSize[d->DragRowSize+1] = (1.0 - Frac) * Total; // LgiTrace("Int: %i/%i, Frac: %f,%f\n", PxOffset, BothPx, d->RowSize[d->DragRowSize], d->RowSize[d->DragRowSize+1]); Layout(); Invalidate(); return; } else if (d->DragColSize >= 0) { // Adjust ColSize[d->DragColSize] and ColSize[d->DragColSize+1] to // center on the mouse x location int AvailX = GetClient().X(); CalcCell(p, d->ColSize, AvailX); int BothPx = p[d->DragColSize].Size + p[d->DragColSize+1].Size; int PxOffset = m.x - p[d->DragColSize].Pos + Fudge; PxOffset = limit(PxOffset, 2, BothPx - 2); double Frac = (double) PxOffset / BothPx; double Total = d->ColSize[d->DragColSize] + d->ColSize[d->DragColSize+1]; d->ColSize[d->DragColSize] = Frac * Total; d->ColSize[d->DragColSize+1] = (1.0 - Frac) * Total; Layout(); Invalidate(); return; } } ResDialogCtrl::OnMouseMove(m); } void CtrlTable::Fix() { // Fix missing cells for (int y=0; yCellY; y++) { for (int x=0; xCellX; ) { ResTableCell *c = d->GetCellAt(x, y); // LgiTrace("[%i][%i] = %p (%ix%i, %s)\n", x, y, c, c ? c->Cell.X() : -1, c ? c->Cell.Y() : -1, c ? c->Pos.GetStr() : NULL); if (c) { x += c->Cell.X(); } else { c = new ResTableCell(this, x, y); if (c) { d->Cells.Add(c); x++; } else break; } } } // Fix rows/cols size #define absf(i) ((i) < 0.0 ? -(i) : (i)) double Sum = 0.0; int n; for (n=0; nRowSize.Length(); n++) { Sum += d->RowSize[n]; } if (absf(Sum - 1.0) > 0.001) { for (n=0; nRowSize.Length(); n++) { double k = d->RowSize[n]; k /= Sum; d->RowSize[n] = k; } } Sum = 0.0; for (n=0; nRowSize.Length(); n++) { Sum += d->RowSize[n]; } if (absf(Sum - 1.0) > 0.001) { for (n=0; nRowSize.Length(); n++) { double k = d->RowSize[n]; k /= Sum; d->RowSize[n] = k; } } Layout(); Invalidate(); } void CtrlTable::InsertRow(int y) { // Shift existing cells down int i; for (i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c) { if (c->Cell.y1 >= y) c->Cell.Offset(0, 1); else if (c->Cell.y2 >= y) // Make spanned cells taller... c->Cell.y2++; } } // Add new row of 1x1 cells for (i=0; iCellX; i++) { d->Cells.Add(new ResTableCell(this, i, y)); } d->CellY++; // Update rows double Last = d->RowSize[d->RowSize.Length()-1]; d->RowSize.Add(Last); for (i=0; iRowSize.Length(); i++) { d->RowSize[i] = d->RowSize[i] / (1.0 + Last); } // Refresh the screen Layout(); Invalidate(); } void CtrlTable::InsertCol(int x) { auto Par = GetParent(); // Shift existing cells down int i; for (i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c) { if (c->Cell.x1 >= x) c->Cell.Offset(1, 0); else if (c->Cell.x2 >= x) // Make spanned cells wider... c->Cell.x2++; } } // Add new row of 1x1 cells for (i=0; iCellY; i++) { d->Cells.Add(new ResTableCell(this, x, i)); } d->CellX++; // Update rows double Last = d->ColSize.Last(); d->ColSize.Add(Last); for (i=0; iColSize.Length(); i++) { d->ColSize[i] = d->ColSize[i] / (1.0 + Last); } // Refresh the screen Layout(); Invalidate(); } void CtrlTable::UnMerge(ResTableCell *Cell) { if (Cell && (Cell->Cell.X() > 1 || Cell->Cell.Y() > 1)) { for (int y=Cell->Cell.x1; y<=Cell->Cell.y2; y++) { for (int x=Cell->Cell.x1; x<=Cell->Cell.x2; x++) { if (x > Cell->Cell.x1 || y > Cell->Cell.y1) { ResTableCell *n = new ResTableCell(this, x, y); if (n) { d->Cells.Add(n); } } } } Cell->Cell.x2 = Cell->Cell.x1; Cell->Cell.y2 = Cell->Cell.y1; Layout(); Invalidate(); } } void CtrlTable::OnMouseClick(LMouse &m) { if (m.Down()) { if (m.Left()) { OpHandle *h = NULL; for (auto &i: d->Handles) { if (i.Overlap(m.x, m.y)) { h = &i; break; } } if (h && h->Type == LAddCol) { for (int i=0; iCellY; i++) d->Cells.Add(new ResTableCell(this, d->CellX, i)); d->CellX++; double Last = d->ColSize[d->ColSize.Length()-1]; d->ColSize.Add(Last); for (int i=0; iColSize.Length(); i++) d->ColSize[i] = d->ColSize[i] / (1.0 + Last); Layout(); Invalidate(); return; } else if (h && h->Type == LAddRow) { for (int i=0; iCellX; i++) d->Cells.Add(new ResTableCell(this, i, d->CellY)); d->CellY++; double Total = 0; double Last = d->RowSize[d->RowSize.Length()-1]; d->RowSize.Add(Last); for (int i=0; iRowSize.Length(); i++) { d->RowSize[i] = d->RowSize[i] / (1.0 + Last); Total += d->RowSize[i]; } Layout(); Invalidate(); return; } else { bool Dirty = false; bool EatClick = true; int i; ResTableCell *Over = 0; // Look at cell joins if (h && h->Type == LJoinCells) { // Do a cell merge LRect u = h->a->Cell; u.Union(&h->b->Cell); d->Cells.Delete(h->a); for (int y=u.y1; y<=u.y2; y++) { for (int x=u.x1; x<=u.x2; x++) { ResTableCell *c = d->GetCellAt(x, y); if (c) { d->Cells.Delete(c); DeleteObj(c); } } } h->a->Cell = u; d->Cells.Add(h->a); Dirty = true; } if (!Dirty) { // Select a cell? for (i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c->Pos.Overlap(m.x, m.y)) { Over = c; if (!c->Selected || m.Ctrl()) { Dirty = true; EatClick = false; c->Selected = !c->Selected; } } else if (!m.Ctrl()) { if (c->Selected) { Dirty = true; EatClick = false; c->Selected = false; } } } } // Delete column goobers if (h && h->Type == LDeleteCol) Dirty = d->DeleteCol(h->Index); // Delete row goobers if (!Dirty && h && h->Type == LDeleteRow) Dirty = d->DeleteRow(h->Index); // Size row goobs if (!Dirty && h && h->Type == LSizeRow) { Dirty = true; d->DragRowSize = h->Index; Capture(true); } // Size col goobs if (!Dirty && h && h->Type == LSizeCol) { Dirty = true; d->DragColSize = h->Index; Capture(true); } if (Dirty) { Layout(); Invalidate(); if (EatClick) return; } } } else if (m.IsContextMenu()) { for (int i=0; iCells.Length(); i++) { ResTableCell *c = d->Cells[i]; if (c->Pos.Overlap(m.x, m.y)) { c->OnMouseClick(m); return; } } } } else { Capture(false); d->DragRowSize = -1; d->DragColSize = -1; } ResDialogCtrl::OnMouseClick(m); } //////////////////////////////////////////////////////////////////////////////////////////////// enum { M_FINISHED = M_USER + 1000, }; class TableLayoutTest : public LDialog { LTableLayout *Tbl; LView *Msg; LTree *Tree; class DlgContainer *View; LAutoPtr Worker; LAutoString Base; public: TableLayoutTest(LViewI *par); ~TableLayoutTest(); void OnDialog(LDialogRes *Dlg); int OnNotify(LViewI *Ctrl, LNotification n); LMessage::Param OnEvent(LMessage *m); }; class DlgItem : public LTreeItem { TableLayoutTest *Wnd; LDialogRes *Dlg; public: DlgItem(TableLayoutTest *w, LDialogRes *dlg) { Wnd = w; Dlg = dlg; } const char *GetText(int i) { return Dlg->Str->Str; } void OnSelect() { Wnd->OnDialog(Dlg); } }; static bool HasTableLayout(LXmlTag *t) { if (t->IsTag("TableLayout")) return true; for (auto c: t->Children) { if (HasTableLayout(c)) return true; } return false; } class Lr8Item : public LTreeItem { TableLayoutTest *Wnd; LString File; LAutoPtr Res; public: Lr8Item(TableLayoutTest *w, LAutoPtr res, char *file) { Wnd = w; Res = res; File = file; List::I d = Res->GetDialogs(); for (LDialogRes *dlg = *d; dlg; dlg = *++d) { if (dlg->Str && HasTableLayout(dlg->Dialog)) { Insert(new DlgItem(Wnd, dlg)); } } if (stristr(File, "Scribe-Branches\\v2.00")) { Expanded(true); } } const char *GetText(int i) { return File ? File.Get() : "#error"; } }; class Lr8Search : public LThread { TableLayoutTest *Wnd; char *Base; LTree *Tree; public: Lr8Search(TableLayoutTest *w, char *base, LTree *tree) : LThread("Lr8Search") { Wnd = w; Base = base; Tree = tree; Run(); } ~Lr8Search() { while (!IsExited()) { LSleep(1); } } int Main() { LArray Ext; LArray Files; Ext.Add("*.lr8"); if (LRecursiveFileSearch(Base, &Ext, &Files)) { for (int i=0; i Res; if (Res.Reset(new LResources(Files[i]))) { List::I d = Res->GetDialogs(); bool HasTl = false; for (LDialogRes *dlg = *d; dlg; dlg = *++d) { if (dlg->Str && HasTableLayout(dlg->Dialog)) { HasTl = true; break; } } if (HasTl) { auto r = LMakeRelativePath(Base, Files[i]); Tree->Insert(new Lr8Item(Wnd, Res, r)); } } } } Files.DeleteArrays(); Wnd->PostEvent(M_FINISHED); return 0; } }; class DlgContainer : public LLayout { LDialogRes *Dlg; LRect Size; public: DlgContainer(int id) { Dlg = 0; Sunken(true); SetId(id); Size.Set(0, 0, 100, 100); SetPos(Size); } void OnPaint(LSurface *pDC) { pDC->Colour(L_WORKSPACE); pDC->Rectangle(); } void OnDialog(LDialogRes *d) { while (Children.Length()) delete Children[0]; if ((Dlg = d)) { if (Dlg->GetRes()->LoadDialog(Dlg->Str->Id, this, &Size)) { LRect r = GetPos(); r.SetSize(Size.X(), Size.Y()); SetPos(r); AttachChildren(); SendNotify(LNotifyTableLayoutRefresh); } } } }; TableLayoutTest::TableLayoutTest(LViewI *par) { LRect r(0, 0, 1000, 800); SetPos(r); SetParent(par); AddView(Tbl = new LTableLayout); auto *c = Tbl->GetCell(0, 0, true, 2); c->Add(Msg = new LTextLabel(100, 0, 0, -1, -1, "Searching for files...")); c = Tbl->GetCell(0, 1); c->Add(Tree = new LTree(101, 0, 0, 100, 100)); c = Tbl->GetCell(1, 1); c->Add(View = new DlgContainer(102)); c = Tbl->GetCell(0, 2, true, 2); c->TextAlign(LCss::AlignRight); c->Add(new LButton(IDOK, 0, 0, -1, -1, "Close")); char e[MAX_PATH_LEN]; LGetSystemPath(LSP_APP_INSTALL, e, sizeof(e)); LMakePath(e, sizeof(e), e, "../../.."); Base.Reset(NewStr(e)); Worker.Reset(new Lr8Search(this, Base, Tree)); } TableLayoutTest::~TableLayoutTest() { Worker.Reset(); } void TableLayoutTest::OnDialog(LDialogRes *Dlg) { if (View) View->OnDialog(Dlg); } int TableLayoutTest::OnNotify(LViewI *Ctrl, LNotification n) { switch (Ctrl->GetId()) { case IDOK: EndModal(1); break; } return LDialog::OnNotify(Ctrl, n); } LMessage::Param TableLayoutTest::OnEvent(LMessage *m) { if (m->Msg() == M_FINISHED) { Tree->Invalidate(); Msg->Name("Finished."); Worker.Reset(); } return LDialog::OnEvent(m); } void OpenTableLayoutTest(LViewI *p) { auto Dlg = new TableLayoutTest(p); Dlg->DoModal([](auto dlg, auto id) { delete dlg; }); }