diff --git a/ide/resources/LgiIde.lr8 b/ide/resources/LgiIde.lr8
--- a/ide/resources/LgiIde.lr8
+++ b/ide/resources/LgiIde.lr8
@@ -1,851 +1,851 @@
-
diff --git a/ide/src/LgiIde.cpp b/ide/src/LgiIde.cpp
--- a/ide/src/LgiIde.cpp
+++ b/ide/src/LgiIde.cpp
@@ -1,5043 +1,5035 @@
#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/TextView4.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 "lgi/common/PopupNotification.h"
#include "LgiIde.h"
#include "FtpThread.h"
#include "FindSymbol.h"
#include "Debugger.h"
#include "ProjectNode.h"
#include "IdeFindInFiles.h"
#define IDM_RECENT_FILE 1000
#define IDM_RECENT_PROJECT 1100
#define IDM_WINDOWS 1200
#define IDM_MAKEFILE_BASE 1300
#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) )
////////////////////////////////////////////////////////////////////////
SysIncThread::SysIncThread( AppWnd* app,
IdeProject* proj,
std::function callback) :
LThread("SysIncThread"),
App(app),
Callback(callback)
{
auto Platform = PlatformFlagsToEnum(App->GetPlatform());
App->GetOptions()->GetValue(OPT_SearchSysInc, SearchSysInc);
if (SearchSysInc.CastInt32())
{
IdeProject* p = proj;
while (proj && proj->GetParentProject())
proj = proj->GetParentProject();
if (proj)
{
// Get all the nodes
List All;
proj->GetChildProjects(All);
All.Insert(proj);
for (auto Cur : All)
{
if (LString Lst = Cur->GetSettings()->GetStr(ProjSystemIncludes, NULL, Platform))
{
auto Lines = Lst.Strip().SplitDelimit("\r\n");
for (auto path : Lines)
{
if (LIsRelativePath(path))
{
char full[MAX_PATH_LEN];
LMakePath(full, sizeof(full), Cur->GetFileName(), "..");
LMakePath(full, sizeof(full), full, path);
Paths.Add(full);
}
else Paths.Add(path);
}
}
}
}
}
Run();
}
SysIncThread::~SysIncThread()
{
Cancel();
WaitForExit();
}
void SysIncThread::Scan(LString p)
{
if (Map.Find(p))
return;
Map.Add(p, true);
LDirectory d;
for (auto b = d.First(p); !IsCancelled() && b; b = d.Next())
{
if (d.IsDir())
{
Scan(d.FullPath());
}
else
{
Headers.New() = d.FullPath();
}
}
}
int SysIncThread::Main()
{
for (unsigned i = 0; !IsCancelled() && i < Paths.Length(); i++)
{
LString p = Paths[i];
if (p[0] == '`')
{
Paths.DeleteAt(i--, true);
auto a = p.Strip("`").SplitDelimit(" \t\r\n", 1);
LSubProcess sub(a[0], a[1]);
if (sub.Start())
{
LStringPipe out;
sub.Communicate(&out);
auto parts = out.NewLStr().SplitDelimit();
for (auto part : parts)
{
if (part.Find("-I") == 0)
Paths.Add(part(2, -1));
}
}
else printf("%s:%i - Error starting %s\n", _FL, p.Get());
continue;
}
// printf("%s:%i SysIncThread: '%s'\n", _FL, p.Get());
}
for (auto &p: Paths)
Scan(p);
// Can't use the async callback mode here, otherwise this thread will exit
// before the callback can be processed. Use the blocking form.
App->RunCallback([this]()
{
if (Callback)
Callback(&Headers);
return 0;
},
-1,
this);
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////
class FindInProject : public LDialog
{
AppWnd *App = NULL;
LList *Lst = NULL;
bool SearchSysInc = false;
static LString::Array SysHeaders;
LAutoPtr Thread;
public:
constexpr static int SYS_HEADER_SEARCH_TIME = 500; //ms
FindInProject(AppWnd *app)
{
Lst = NULL;
App = app;
if (LoadFromResource(IDD_FIND_PROJECT_FILE))
{
MoveSameScreen(App);
LViewI *v;
if (GetViewById(IDC_TEXT, v))
v->Focus(true);
if (!GetViewById(IDC_FILES, Lst))
return;
LVariant s = false;
App->GetOptions()->GetValue(OPT_SearchSysInc, s);
SearchSysInc = s.CastInt32() != 0;
SetCtrlValue(IDC_SEARCH_SYS_INCLUDES, SearchSysInc);
RegisterHook(this, LKeyEvents, 0);
}
}
~FindInProject()
{
Thread.Reset();
}
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, true, NULL);
}
EndModal(1);
return true;
}
break;
}
case LK_ESCAPE:
{
if (k.Down())
{
EndModal(0);
return true;
}
break;
}
}
return false;
}
void Search(const char *s)
{
auto p = App->RootProject();
if (!p || !s)
return;
LArray Matches, Nodes;
auto Platforms = App->GetPlatform();
List All;
p->GetChildProjects(All);
All.Insert(p);
for (auto p: All)
{
p->GetAllNodes(Nodes);
}
FilterFiles(Matches, Nodes, s, Platforms);
Lst->Empty();
for (auto m: Matches)
{
auto 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);
}
if (SysHeaders.Length() == 0)
{
if (!Thread)
{
Thread.Reset(new SysIncThread(App, App->RootProject(), [this](auto hdrs)
{
SysHeaders.Swap(*hdrs);
Search(GetCtrlName(IDC_TEXT));
}));
}
}
else
{
auto start = LCurrentTime();
for (auto h: SysHeaders)
{
if (Stristr(h.Get(), s))
Lst->Insert(new LListItem(h));
if (LCurrentTime() - start >= SYS_HEADER_SEARCH_TIME)
break;
}
}
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, true, NULL);
EndModal(1);
}
}
break;
}
case IDC_TEXT:
{
if (n.Type != LNotifyReturnKey)
Search(c->Name());
break;
}
case IDCANCEL:
{
EndModal(0);
break;
}
case IDC_SEARCH_SYS_INCLUDES:
{
bool s = c->Value() != 0;
if (s != SearchSysInc)
{
LVariant v = SearchSysInc = s;
App->GetOptions()->SetValue(OPT_SearchSysInc, v);
Search(GetCtrlName(IDC_TEXT));
}
break;
}
}
return 0;
}
};
LString::Array FindInProject::SysHeaders;
//////////////////////////////////////////////////////////////////////////////////////////
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];
snprintf(s, sizeof(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);
int ch = Depth * 4;
memset(s, ' ', ch);
snprintf(s+ch, sizeof(s)-ch, "[%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;
}
};
//////////////////////////////////////////////////////////////////////////////////////////
#define TextLogCls LTextLog4
class DebugTextLog : public TextLogCls
{
public:
DebugTextLog(int id) : TextLogCls(id)
{
}
void PourText(size_t Start, ssize_t Len) override
{
auto Ts = LCurrentTime();
TextLogCls::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 = NULL;
LTabPage *Build = NULL;
LTabPage *Output = NULL;
LTabPage *Debug = NULL;
LTabPage *Find = NULL;
LTabPage *Ftp = NULL;
LList *FtpLog = NULL;
LTextLog *Txt[AppWnd::Channels::ChannelMax] = {};
LArray Buf[AppWnd::Channels::ChannelMax];
LFont Small;
LFont Fixed;
LTabView *DebugTab = NULL;
LBox *DebugBox = NULL;
LBox *DebugLog = NULL;
LList *Locals = NULL, *CallStack = NULL, *Threads = NULL;
LTree *Watch = NULL;
LTextLog *ObjectDump = NULL, *MemoryDump = NULL, *Registers = NULL;
LTableLayout *MemTable = NULL;
LEdit *DebugEdit = NULL;
DebugTextLog *DebuggerLog = NULL;
IdeOutput(AppWnd *app)
{
App = app;
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->ColumnHeaders(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->ColumnHeaders(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, NULL);
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);
// printf("Options.GetFile()='%s'\n", Options.GetFile());
}
~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, true, NULL);
}
}
#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()
{
LgiGetResObj(true, AppName);
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 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(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);
}
#if 0
else
{
printf("load outPx: not enough space %i > %i, cli=%s, pos=%s\n",
c.Y(), OutPx.CastInt32(),
c.GetStr(), GetPos().GetStr());
}
#endif
AttachChildren();
OnPosChange();
UpdateState();
Visible(true);
DropTarget(true);
SetPulse(1000);
#ifdef LINUX
LFinishXWindowsStartup(this);
#endif
}
AppWnd::~AppWnd()
{
// Everything needs to be clean BEFORE we get here... because we can't show
// any async UI to select save locations or anything.
LAssert(IsClean());
// Haiku: wait for the window thread to shutdown cleanly.
WaitThread();
LVariant v;
if (d->HBox)
d->Options.SetValue(OPT_SPLIT_PX, v = d->HBox->Value());
if (d->Output)
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\\2019\\Community\\VC\\Tools\\MSVC\\14.29.30133\\bin\\Hostx64\\x64\\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.NewLStr().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.NewLStr().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" };
LString::Array IncPaths;
if (p->BuildIncludePaths(IncPaths, NULL, 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)
{
THREAD_WARNING
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()
{
THREAD_WARNING
SaveAll([this](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)
{
THREAD_WARNING
d->OnFile(File, IsProject);
}
IdeDoc *AppWnd::NewDocWnd(const char *FileName, NodeSource *Src)
{
THREAD_WARNING
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;
}
void AppWnd::GotoReference(const char *File, int Line, bool CurIp, bool WithHistory, std::function Callback)
{
if (!InThread())
{
printf("\tGotoReference(%s,%i,%i,%i) out of thread\n", File, Line, CurIp, WithHistory);
RunCallback
(
[this, File=LString(File), Line, CurIp, WithHistory, Callback]()
{
GotoReference(File, Line, CurIp, WithHistory, Callback);
}
);
return;
}
printf("\tGotoReference(%s,%i,%i,%i) in thread\n", File, Line, CurIp, WithHistory);
if (!WithHistory)
d->InHistorySeek = true;
IdeDoc *Doc = File ? OpenFile(File) : GetCurrentDoc();
if (Doc)
{
Doc->SetLine(Line, CurIp);
Doc->Focus(true);
}
else
{
LString Msg;
Msg.Printf("Failed to find '%s'", File);
LPopupNotification::Message(this, Msg);
}
if (!WithHistory)
d->InHistorySeek = false;
if (Callback)
Callback(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 = NULL;
THREAD_WARNING
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;
}
FindSymbolSystem *AppWnd::GetFindSym()
{
return d->FindSym;
}
IdePlatform PlatformFlagsToEnum(int flags)
{
if (flags == PLATFORM_WIN32)
return PlatformWin;
if (flags == PLATFORM_LINUX)
return PlatformLinux;
if (flags == PLATFORM_MAC)
return PlatformMac;
if (flags == PLATFORM_HAIKU)
return PlatformHaiku;
return PlatformCurrent;
}
LString PlatformFlagsToStr(int flags)
{
LString::Array a;
if (flags & PLATFORM_WIN32) a.New() = PlatformNames[PlatformWin];
if (flags & PLATFORM_LINUX) a.New() = PlatformNames[PlatformLinux];
if (flags & PLATFORM_MAC) a.New() = PlatformNames[PlatformMac];
if (flags & PLATFORM_HAIKU) a.New() = PlatformNames[PlatformHaiku];
return a.Length() ? LString(",").Join(a) : "EmptyFlags";
}
IdeProject *AppWnd::OpenProject(const char *FileName, IdeProject *ParentProj, bool Create, ProjectNode *DepParent)
{
THREAD_WARNING
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, DepParent);
if (!p)
{
LgiTrace("%s:%i - Error: mem alloc.\n", _FL);
return NULL;
}
p->SetParentProject(ParentProj);
auto Status = p->OpenFile(FileName);
if (Status == OpenOk)
{
d->Projects.Insert(p);
d->OnFile(FileName, true);
if (!DepParent)
{
auto d = strrchr(FileName, DIR_CHAR);
if (d++)
{
LString n;
n.Printf("%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_GET_PLATFORM_FLAGS:
{
PostThreadEvent(m->A(), M_GET_PLATFORM_FLAGS, 0, GetPlatform());
break;
}
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 Breaking = d->Running && !Running;
d->Running = Running;
if (Breaking &&
d->Output &&
d->Output->DebugTab)
{
d->Output->DebugTab->SendNotify(LNotification(LNotifyValueChanged, _FL));
}
}
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");
SetCtrlName(IDC_FONT, Font.GetDescription());
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, [this](auto ui)
{
SetCtrlName(IDC_FONT, Font.GetDescription());
});
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)
{
THREAD_WARNING
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)
{
printf("IDC_DEBUG_TAB notification: %s\n", n.Sender.Get());
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, true, NULL);
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([this](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(IDD_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);
s->Open([this](auto s, auto ok)
{
if (ok)
OpenFile(s->Name());
delete s;
});
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([this, Top](auto s, auto ok)
{
if (ok)
{
Top->SetFileName(s->Name(), true);
d->OnFile(s->Name());
}
delete s;
});
}
break;
}
case IDM_CLOSE:
{
IdeDoc *Top = TopDoc();
if (Top && Top->OnRequestClose(false))
Top->Quit();
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)
{
if (code == IDOK)
{
LString s = Inp->GetStr();
LString::Array p = s.SplitDelimit(":,");
if (p.Length() == 1)
{
GotoReference(p[0], 1, false, true, NULL);
}
else if (p.Length() > 1)
{
LString file = p[0];
int line = (int)p[1].Int();
GotoReference(file, line, false, true, NULL);
}
else LgiMsg(this, "Error: No filename.", 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)
+ if (auto Doc = FocusEdit())
Doc->PostEvent(M_COPY);
break;
}
case IDM_PASTE:
{
- LTextView3 *Doc = FocusEdit();
- if (Doc)
+ if (auto Doc = FocusEdit())
{
#if 0
LClipBoard c(this);
LArray Formats;
auto r = c.EnumFormats(Formats);
GetBuildLog()->Print("r=%i\n", r);
for (auto f: Formats)
{
GetBuildLog()->Print("f=%s\n", LClipBoard::FmtToStr(f).Get());
}
auto html = c.Html();
GetBuildLog()->Print("html='%s'\n", html.Get());
#else
Doc->PostEvent(M_PASTE);
#endif
}
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();
+ auto 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)
+ if (auto Doc = FocusDoc())
{
Doc->GotoSearch(IDC_SYMBOL_SEARCH);
}
else
{
d->FindSym->OpenSearchDlg(this, [this](auto r)
{
if (r.File)
GotoReference(r.File, r.Line, false, true, NULL);
});
}
break;
}
case IDM_GOTO_SYMBOL:
{
- IdeDoc *Doc = FocusDoc();
- if (Doc)
- {
+ if (auto Doc = FocusDoc())
Doc->SearchSymbol();
- }
break;
}
case IDM_FIND_PROJECT_FILE:
{
- IdeDoc *Doc = FocusDoc();
- if (Doc)
+ if (auto Doc = FocusDoc())
{
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);
+ auto doc = dynamic_cast(LAppInst->GetFocus());
if (!doc)
break;
- ssize_t c = doc->GetCaret();
+ auto 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([this, Cmd](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([this, p](auto s, auto ok)
{
if (ok)
p->ImportDsp(s->Name());
delete s;
});
}
break;
}
case IDM_RUN:
{
SaveAll([this](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([this](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([this](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([this](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([i, Doc](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([i, Doc](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)
{
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);
}
}
};
void LDirTest()
{
LDirectory dir;
for (auto b = dir.First(LGetSystemPath(LSP_HOME)); b; b = dir.Next())
{
bool debug = Stricmp(dir.GetName(), "restored.mp4") == 0;
auto modTime = dir.GetLastWriteTime();
if (debug)
{
int asd=0;
}
auto dt = LDirectory::TsToDateTime(modTime);
LgiTrace("%-36sunix=" LPrintfInt64 ", dt=%s %g\n",
dir.GetName(),
LDirectory::TsToUnix(modTime),
dt.Get().Get(),
(double) dt.GetTimeZone() / 60.0);
}
}
/*
#include "lgi/common/TimeZoneInfo.h"
void TimeZoneInfoTest()
{
LTimeZoneInfo tzInfo;
tzInfo.Read("c:\\code\\Sydney");
LArray info;
LDateTime start, end;
start.SetNow();
start.Day(1);
start.Month(1);
end = start;
end.Year(start.Year()+1);
start.Year(2000);
if (tzInfo.GetDaylightSavingsInfo(info, start, &end))
{
for (auto &i: info)
{
LDateTime dt(i.UtcTimeStamp);
LgiTrace("%s = %i\n", dt.Get().Get(), i.Offset);
}
}
}
*/
int LgiMain(OsAppArguments &AppArgs)
{
printf("LgiIde v%s\n", APP_VER);
LApp a(AppArgs, "LgiIde");
if (a.IsOk())
{
a.AppWnd = new AppWnd;
// LPlaySound("~/code/mixkit-happy-bells-notification-937.wav");
LArray ver;
LGetOs(&ver);
// auto testFile = "/boot/home/code/lgi/trunk/CMakeLists.txt";
// LShowFileProperties(a.AppWnd->Handle(), testFile);
// LBrowseToFile(testFile);
// LDirTest();
// TimeZoneInfoTest();
a.Run();
}
return 0;
}
diff --git a/ide/src/NewProjectFromTemplate.cpp b/ide/src/NewProjectFromTemplate.cpp
--- a/ide/src/NewProjectFromTemplate.cpp
+++ b/ide/src/NewProjectFromTemplate.cpp
@@ -1,184 +1,188 @@
#include "lgi/common/Lgi.h"
#include "lgi/common/Tree.h"
#include "lgi/common/SubProcess.h"
#include "lgi/common/FileSelect.h"
#include "LgiIde.h"
#include "resdefs.h"
static LString TemplatesPath;
class NewProjFromTemplate : public LDialog
{
public:
NewProjFromTemplate(LViewI *parent)
{
SetParent(parent);
if (LoadFromResource(IDD_NEW_PROJ_FROM_TEMPLATE))
{
MoveSameScreen(parent);
LTree *t;
if (TemplatesPath && GetViewById(IDC_TEMPLATES, t))
Add(t, TemplatesPath);
}
}
void Add(LTreeNode *t, const char *path)
{
LDirectory d;
for (auto b=d.First(path); b; b=d.Next())
{
auto Full = d.FullPath();
if (d.IsDir())
{
LTreeItem *c = new LTreeItem;
c->SetText(d.GetName());
c->SetText(Full, 1);
t->Insert(c);
Add(c, Full);
}
}
}
int OnNotify(LViewI *c, LNotification n)
{
switch (c->GetId())
{
case IDC_BROWSE_OUTPUT:
{
- LFileSelect *s = new LFileSelect;
+ auto s = new LFileSelect;
s->Parent(this);
s->OpenFolder([this](auto s, auto ok)
{
if (ok)
SetCtrlName(IDC_FOLDER, s->Name());
delete s;
});
break;
}
case IDOK:
case IDCANCEL:
EndModal(c->GetId() == IDOK);
break;
}
return 0;
}
};
LString GetPython3()
{
auto Path = LGetPath();
for (auto i: Path)
{
LFile::Path p(i);
p +=
#ifdef MAC
"python3"
#else
"python"
#endif
LGI_EXECUTABLE_EXT;
if (p.Exists())
{
printf("Got python '%s'\n", p.GetFull().Get());
// But what version is it?
LSubProcess sp(p, "--version");
if (sp.Start())
{
LStringPipe out;
sp.Communicate(&out);
auto s = out.NewLStr();
// printf("out=%s\n", s.Get());
if (s.Find(" 3.") >= 0)
return p.GetFull();
}
}
}
return LString();
}
bool CreateProject(const char *Name, const char *Template, const char *Folder)
{
auto Py3 = GetPython3();
if (!Py3)
{
LgiTrace("%s:%i - Can't find python3.\n", _FL);
return false;
}
// Make sure output folder exists?
if (!LDirExists(Folder))
{
if (!FileDev->CreateFolder(Folder))
{
LgiTrace("%s:%i - Create folder '%s' failed.\n", _FL, Folder);
return false;
}
}
// Copy in script...
auto CreateProjectPy = "create_project.py";
LFile::Path ScriptIn(TemplatesPath);
ScriptIn += CreateProjectPy;
LFile::Path ScriptOut(Folder);
ScriptOut += CreateProjectPy;
if (!FileDev->Copy(ScriptIn, ScriptOut))
{
LgiTrace("%s:%i - Copy '%s' to '%s' failed.\n", _FL, ScriptIn.GetFull().Get(), ScriptOut.GetFull().Get());
return false;
}
// Call the script
LString Args;
Args.Printf("\"%s\" \"%s\" \"%s\"", ScriptOut.GetFull().Get(), Template, Name);
LSubProcess p(Py3, Args);
if (!p.Start())
{
LgiTrace("%s:%i - Start process failed.\n", _FL);
return false;
}
LStringPipe Out;
p.Communicate(&Out);
LgiTrace("Out=%s\n", Out.NewLStr().Get());
FileDev->Delete(ScriptOut);
return true;
}
void NewProjectFromTemplate(LViewI *parent)
{
LFile::Path p(LSP_APP_INSTALL);
p +=
#ifdef MAC
"../../../../"
#endif
- "../../templates";
+ "../templates";
if (!p.Exists())
{
LgiMsg(parent, "The path '%s' doesn't exist.", AppName, MB_OK, p.GetFull().Get());
return;
}
TemplatesPath = p;
auto Dlg = new NewProjFromTemplate(parent);
Dlg->DoModal([Dlg](auto dlg, auto code)
{
- LTree *t;
- if (!Dlg->GetViewById(IDC_TEMPLATES, t))
+ if (code)
{
- LgiTrace("%s:%i - No tree.\n", _FL);
- return;
+ LTree *t;
+ if (!Dlg->GetViewById(IDC_TEMPLATES, t))
+ {
+ LgiTrace("%s:%i - No tree.\n", _FL);
+ return;
+ }
+
+ auto sel = t->Selection();
+ if (!sel)
+ {
+ LgiTrace("%s:%i - No selection.\n", _FL);
+ return;
+ }
+
+ CreateProject(Dlg->GetCtrlName(IDC_PROJ_NAME), sel->GetText(1), Dlg->GetCtrlName(IDC_FOLDER));
}
-
- auto sel = t->Selection();
- if (!sel)
- {
- LgiTrace("%s:%i - No selection.\n", _FL);
- return;
- }
-
- CreateProject(Dlg->GetCtrlName(IDC_PROJ_NAME), sel->GetText(1), Dlg->GetCtrlName(IDC_FOLDER));
+
delete Dlg;
});
}